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 sha
import os
import socket
# This file is originally stolen from pypod-0.5.0
# http://superduper.net/index.py?page=pypod
# I hope that's ok, both works are GPL.
hostname = socket.gethostname()
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 = sha.sha()
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):
value = unicode(value).encode("utf-8")
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'] = {}
track['userdata']['filename_ipod'] = track['ipod_path']
try:
hash_name = 'sha1_hash'
del track['userdata'][hash_name]
except KeyError:
# recent gpod uses sha1_hash, older uses md5_hash
hash_name = 'md5_hash'
del track['userdata'][hash_name]
if track['userdata'].has_key('filename_locale') and not track['userdata'].has_key(hash_name):
if os.path.exists(track['userdata']['filename_locale']):
track['userdata'][hash_name] = sha1_hash(
track['userdata']['filename_locale'])
[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.
"""
tracks_by_id = {}
tracks_by_sha = {}
id = 0
ext_hash_valid = True
for track in db:
track['userdata'] = {}
for track in db:
tracks_by_id[track['id']] = track
track = None
file = open(filename)
ext_data = {}
ext_block = None
for line in file:
parts = line.strip().split("=", 1)
if len(parts) != 2:
print parts
name, value = parts
if name == "id":
if ext_block:
ext_data[id] = ext_block
if value != 'xxx':
id = int(value)
ext_block = {}
elif name == "version":
pass
elif name == "itunesdb_hash":
if itunesdb_file and sha1_hash(itunesdb_file) != value:
ext_hash_valid = False
else:
ext_block[name] = value
if ext_hash_valid:
for id,ext_block in ext_data.items():
tracks_by_id[id]['userdata'] = ext_block
else:
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:
track = tracks_by_sha[ext_block['sha1_hash']]
except KeyError:
# recent gpod uses sha1_hash, older uses md5_hash
track = tracks_by_sha[ext_block['md5_hash']]
track['userdata'] = ext_block
|