2 iPod Shuffle database access
4 File format documentation:
5 - http://ipodlinux.org/ITunesDB#iTunesSD_file and further
7 Author: Artem Baguinski
10 from __future__
import with_statement
17 iTunesSD_track
= Record([
18 Field(Uint24(), '%reclen%', check
= True),
20 Field(Uint24(), 'starttime', default
= 0),
22 Field(Uint24(), 'stoptime', default
= 0),
24 Field(Uint24(), 'volume', default
= 0x64),
25 # for the following fileds there is no sensible default
26 Field(Uint24(), 'file_type'),
28 Field(ZeroPaddedString(522, 'UTF-16-LE'), 'filename'),
29 Field(Bool8(), 'shuffleflag'),
30 Field(Bool8(), 'bookmarkflag'),
35 Field(Uint24(), 'len(tracks)'),
36 Field(Uint24(), const
= 0x010800),
37 Field(Uint24(), '%reclen%', check
= True),
39 ListField('tracks', iTunesSD_track
)],
42 iTunesStats_track
= Record([
43 Field( Uint24(), '%reclen%', check
= True),
44 Field( Int24(), 'bookmarktime', default
= -1),
46 Field( Uint24(), 'playcount', default
= 0),
47 Field( Uint24(), 'skippedcount', default
= 0)])
49 iTunesStats
= Record([
51 Field( Uint24(), 'len(tracks)'),
53 ListField('tracks', iTunesStats_track
)],
56 # Here I disagree with the format from URL, but I'm not sure about nothing
57 iTunesPState
= Record([
58 Field( Uint8(), 'volume', default
= 29 ),
59 Field( Uint24(), 'shufflepos', default
= 0 ),
60 Field( Uint24(), 'trackno', default
= 0 ),
61 Field( Bool24(), 'shuffleflag', default
= False),
62 Field( Uint24(), 'trackpos', default
= 0),
66 class ShuffleException(Exception):
70 supported_file_types
= (".mp3", ".aa", ".m4a", ".m4b", ".m4p", ".wav")
72 def write_iTunesSD(self
, tracks
):
73 with
open('iTunesSD', WRITE
) as file:
74 iTunesSD
.write(file, {'tracks':tracks
})
77 def read_iTunesSD(self
):
78 with
open('iTunesSD', READ
) as file:
79 return iTunesSD
.read(file)['tracks']
81 def write_iTunesStats(self
, tracks
):
82 with
open('iTunesStats', WRITE
) as file:
83 iTunesStats
.write(file, {'tracks':tracks
})
86 def read_iTunesStats(self
, tracks
):
87 with
open('iTunesStats', READ
) as file:
88 iTunesStats
.read(file,{'tracks':tracks
})['tracks']
90 def write_iTunesPState(self
, pstate
):
91 with
open('iTunesPState', WRITE
) as file:
92 iTunesPState
.write(file, pstate
)
95 def read_iTunesPState(self
):
96 with
open('iTunesPState', READ
) as file:
97 return iTunesPState
.read(file)
99 def read_all(self
, control_dir
):
101 os
.chdir(control_dir
)
103 tracks
= self
.read_iTunesSD()
104 self
.read_iTunesStats(tracks
)
105 pstate
= self
.read_iTunesPState()
106 return (tracks
, pstate
)
108 return ([], iTunesPState
.make_default())
112 def write_all(self
, control_dir
, tracks
, pstate
):
114 os
.chdir(control_dir
)
116 self
.write_iTunesSD(tracks
)
117 self
.write_iTunesStats(tracks
)
118 self
.write_iTunesPState(pstate
)
119 if os
.path
.exists("iTunesShuffle"):
120 os
.remove("iTunesShuffle") # let the player recreate it
124 def set_old_tracks(self
, lst
):
126 for i
in xrange(len(lst
)):
128 self
.old_tracks
[t
['filename']] = t
129 self
.old_tracks
[i
] = t
131 def make_track_record(self
, filename
):
132 if self
.old_tracks
.has_key(filename
):
133 return self
.old_tracks
[filename
]
136 iTunesSD_track
.make_default(dict)
137 iTunesStats_track
.make_default(dict)
139 dict['filename'] = filename
140 if filename
.endswith((".mp3",".aa")):
141 dict['file_type'] = 1
142 elif filename
.endswith((".m4a", ".m4b", ".m4p")):
143 dict['file_type'] = 2
144 elif filename
.endswith(".wav"):
145 dict['file_type'] = 4
147 raise ShuffleException(
148 "%s: unsupported file type" % (filename
))
149 dict['bookmarkflag'] = filename
.endswith((".aa",".m4b"))
150 dict['shuffleflag'] = not dict['bookmarkflag']
153 #####################################################################
154 if __name__
== '__main__':
156 logging
.basicConfig(level
=logging
.DEBUG
,filename
='shuffle-debug.log')
162 if len(sys
.argv
) > 1:
165 tracks
, pstate
= db
.read_all(sys
.argv
[1])
169 #if len(sys.argv) > 2:
171 #os.chdir(sys.argv[2])
172 #db.write_all(tracks, pstate)