1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
|
"""Read and write Gtkpod extended info files."""
import os
import types
# The hashlib module is only available in python >= 2.5,
# while the sha module is deprecated in 2.6.
try:
import hashlib
sha1 = hashlib.sha1
except ImportError:
import sha
sha1 = sha.sha
# This file is originally stolen from pypod-0.5.0
# http://superduper.net/index.py?page=pypod
# and reworked significantly since then.
class ParseError(Exception):
"""Exception for parse errors."""
pass
class SyncError(Exception):
"""Exception for sync errors."""
pass
def sha1_hash(filename):
"""Return an SHA1 hash on the first 16k of a file."""
import struct
# only hash the first 16k
hash_len = 4*4096
hash = sha1()
size = os.path.getsize(filename)
hash.update(struct.pack("<L", size))
hash.update(open(filename).read(hash_len))
return hash.hexdigest()
def write(filename, db, itunesdb_file):
"""Save extended info to a file.
db is a gpod.Database instance
Extended info is written for the iTunesDB specified in
itunesdb_file
"""
file = open(filename, "w")
def write_pair(name, value):
if type(value) not in (types.StringType, types.UnicodeType):
# e.g., an integer
value = str(value)
file.write("=".join([name, value]))
file.write('\n')
write_pair("itunesdb_hash", sha1_hash(itunesdb_file))
write_pair("version", "0.99.9")
for track in db:
write_pair("id", track['id'])
if not track['userdata']:
track['userdata'] = {}
[write_pair(i[0],i[1]) for i in track['userdata'].items()]
write_pair("id", "xxx")
def parse(filename, db, itunesdb_file=None):
"""Load extended info from a file.
db is a gpod.Database instance
If itunesdb_file is set and it's hash is valid some expensive
checks are skipped.
"""
ext_hash_valid = False
ext_data = {}
for line in open(filename).readlines():
parts = line.strip().split("=", 1)
if len(parts) != 2:
print parts
name, value = parts
if name == "id":
if value == 'xxx':
break
id = int(value)
ext_data[id] = {}
elif name == "version":
pass
elif name == "itunesdb_hash":
if itunesdb_file and sha1_hash(itunesdb_file) == value:
ext_hash_valid = True
else:
# value is a string of undetermined encoding at the moment
ext_data[id][name] = value
# now add each extended info block to the track it goes with
# equiv. of fill_in_extended_info()
if ext_hash_valid:
# the normal case
for track in db:
try:
track['userdata'] = ext_data[track['id']]
except KeyError:
# no userdata available...
track['userdata'] = {}
else:
# the iTunesDB was changed, so id's will be wrong.
# match up using hash instead
tracks_by_sha = {}
for track in db:
# make a dict to allow us to find each track by the sha1_hash
tracks_by_sha[sha1_hash(track.ipod_filename())] = track
for ext_block in ext_data.values():
try:
if ext_block.has_key('sha1_hash'):
track = tracks_by_sha[ext_block['sha1_hash']]
elif ext_block.has_key('md5_hash'):
# recent gpod uses sha1_hash, older uses md5_hash
track = tracks_by_sha[ext_block['md5_hash']]
except KeyError:
# what should we do about this?
print "Failed to match hash from extended information file with one that we just calculated:"
print ext_block
continue
track['userdata'] = ext_block
|