automated reformat pass

This commit is contained in:
lynxize 2025-06-20 08:58:24 -06:00
parent 66fe9bc514
commit d0d8588e5f
Signed by: lynxize
GPG key ID: 8615849B8532CD77

View file

@ -14,19 +14,22 @@
# along with this program; if not, write to the Free Software # along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
from __future__ import print_function
from __future__ import division from __future__ import division
from past.builtins import cmp from __future__ import print_function
from builtins import chr from builtins import chr
from builtins import input from builtins import input
from builtins import map from builtins import map
from builtins import range from builtins import range
from past.utils import old_div
from functools import reduce from functools import reduce
__title__="KeyJ's iPod shuffle Database Builder"
__version__="1.0" from past.builtins import cmp
__author__="Martin Fiedler" from past.utils import old_div
__email__="martin.fiedler@gmx.net"
__title__ = "KeyJ's iPod shuffle Database Builder"
__version__ = "1.0"
__author__ = "Martin Fiedler"
__email__ = "martin.fiedler@gmx.net"
""" VERSION HISTORY """ VERSION HISTORY
1.0-rc1 (2006-04-26) 1.0-rc1 (2006-04-26)
@ -71,34 +74,35 @@ __email__="martin.fiedler@gmx.net"
* initial public release, Win32 only * initial public release, Win32 only
""" """
import sys, os, os.path, array, getopt, random, fnmatch, operator, string
import sys,os,os.path,array,getopt,random,types,fnmatch,operator,string # @formatter:off
KnownProps = ('filename', 'size', 'ignore', 'type', 'shuffle', 'reuse', 'bookmark')
KnownProps=('filename','size','ignore','type','shuffle','reuse','bookmark') Rules = [
Rules=[ ([('filename', '~', '*.mp3')], {'type': 1, 'shuffle': 1, 'bookmark': 0}),
([('filename','~','*.mp3')], {'type':1, 'shuffle':1, 'bookmark':0}), ([('filename', '~', '*.m4?')], {'type': 2, 'shuffle': 1, 'bookmark': 0}),
([('filename','~','*.m4?')], {'type':2, 'shuffle':1, 'bookmark':0}), ([('filename', '~', '*.m4b')], { 'shuffle': 0, 'bookmark': 1}),
([('filename','~','*.m4b')], { 'shuffle':0, 'bookmark':1}), ([('filename', '~', '*.aa')], {'type': 1, 'shuffle': 0, 'bookmark': 1, 'reuse': 1}),
([('filename','~','*.aa')], {'type':1, 'shuffle':0, 'bookmark':1, 'reuse':1}), ([('filename', '~', '*.wav')], {'type': 4, 'shuffle': 0, 'bookmark': 0}),
([('filename','~','*.wav')], {'type':4, 'shuffle':0, 'bookmark':0}), ([('filename', '~', '*.book.???')], { 'shuffle': 0, 'bookmark': 1}),
([('filename','~','*.book.???')], { 'shuffle':0, 'bookmark':1}), ([('filename', '~', '*.announce.???')], { 'shuffle': 0, 'bookmark': 0}),
([('filename','~','*.announce.???')], { 'shuffle':0, 'bookmark':0}), ([('filename', '~', '/recycled/*')], {'ignore': 1}),
([('filename','~','/recycled/*')], {'ignore':1}),
] ]
# @formatter:on
Options={ Options = {
"volume":None, "volume": None,
"interactive":False, "interactive": False,
"smart":True, "smart": True,
"home":True, "home": True,
"logging":True, "logging": True,
"reuse":1, "reuse": 1,
"logfile":"rebuild_db.log.txt", "logfile": "rebuild_db.log.txt",
"rename":False "rename": False
} }
domains=[] domains = []
total_count=0 total_count = 0
KnownEntries={} KnownEntries = {}
################################################################################ ################################################################################
@ -108,21 +112,21 @@ def open_log():
global logfile global logfile
if Options['logging']: if Options['logging']:
try: try:
logfile=file(Options['logfile'],"w") logfile = file(Options['logfile'], "w")
except IOError: except IOError:
logfile=None logfile = None
else: else:
logfile=None logfile = None
def log(line="",newline=True): def log(line="", newline=True):
global logfile global logfile
if newline: if newline:
print(line) print(line)
line+="\n" line += "\n"
else: else:
print(line, end=' ') print(line, end=' ')
line+=" " line += " "
if logfile: if logfile:
try: try:
logfile.write(line) logfile.write(line)
@ -154,59 +158,62 @@ def filesize(filename):
################################################################################ ################################################################################
def MatchRule(props,rule): def MatchRule(props, rule):
try: try:
prop,op,ref=props[rule[0]],rule[1],rule[2] prop, op, ref = props[rule[0]], rule[1], rule[2]
except KeyError: except KeyError:
return False return False
if rule[1]=='~': if rule[1] == '~':
return fnmatch.fnmatchcase(prop.lower(),ref.lower()) return fnmatch.fnmatchcase(prop.lower(), ref.lower())
elif rule[1]=='=': elif rule[1] == '=':
return cmp(prop,ref)==0 return cmp(prop, ref) == 0
elif rule[1]=='>': elif rule[1] == '>':
return cmp(prop,ref)>0 return cmp(prop, ref) > 0
elif rule[1]=='<': elif rule[1] == '<':
return cmp(prop,ref)<0 return cmp(prop, ref) < 0
else: else:
return False return False
def ParseValue(val): def ParseValue(val):
if len(val)>=2 and ((val[0]=="'" and val[-1]=="'") or (val[0]=='"' and val[-1]=='"')): if len(val) >= 2 and ((val[0] == "'" and val[-1] == "'") or (val[0] == '"' and val[-1] == '"')):
return val[1:-1] return val[1:-1]
try: try:
return int(val) return int(val)
except ValueError: except ValueError:
return val return val
def ParseRule(rule): def ParseRule(rule):
sep_pos=min([rule.find(sep) for sep in "~=<>" if rule.find(sep)>0]) sep_pos = min([rule.find(sep) for sep in "~=<>" if rule.find(sep) > 0])
prop=rule[:sep_pos].strip() prop = rule[:sep_pos].strip()
if not prop in KnownProps: if not prop in KnownProps:
log("WARNING: unknown property `%s'"%prop) log("WARNING: unknown property `%s'" % prop)
return (prop,rule[sep_pos],ParseValue(rule[sep_pos+1:].strip())) return (prop, rule[sep_pos], ParseValue(rule[sep_pos + 1:].strip()))
def ParseAction(action): def ParseAction(action):
prop,value=list(map(string.strip,action.split('=',1))) prop, value = list(map(string.strip, action.split('=', 1)))
if not prop in KnownProps: if not prop in KnownProps:
log("WARNING: unknown property `%s'"%prop) log("WARNING: unknown property `%s'" % prop)
return (prop,ParseValue(value)) return (prop, ParseValue(value))
def ParseRuleLine(line): def ParseRuleLine(line):
line=line.strip() line = line.strip()
if not(line) or line[0]=="#": if not (line) or line[0] == "#":
return None return None
try: try:
# split line into "ruleset: action" # split line into "ruleset: action"
tmp=line.split(":") tmp = line.split(":")
ruleset=list(map(string.strip,":".join(tmp[:-1]).split(","))) ruleset = list(map(string.strip, ":".join(tmp[:-1]).split(",")))
actions=dict(list(map(ParseAction,tmp[-1].split(",")))) actions = dict(list(map(ParseAction, tmp[-1].split(","))))
if len(ruleset)==1 and not(ruleset[0]): if len(ruleset) == 1 and not (ruleset[0]):
return ([],actions) return ([], actions)
else: else:
return (list(map(ParseRule,ruleset)),actions) return (list(map(ParseRule, ruleset)), actions)
except OSError: #(ValueError,IndexError,KeyError): except OSError: # (ValueError,IndexError,KeyError):
log("WARNING: rule `%s' is malformed, ignoring"%line) log("WARNING: rule `%s' is malformed, ignoring" % line)
return None return None
return None return None
@ -219,29 +226,30 @@ def safe_char(c):
return c return c
return "_" return "_"
def rename_safely(path,name):
base,ext=os.path.splitext(name) def rename_safely(path, name):
newname=''.join(map(safe_char,base)) base, ext = os.path.splitext(name)
if name==newname+ext: newname = ''.join(map(safe_char, base))
if name == newname + ext:
return name return name
if os.path.exists("%s/%s%s"%(path,newname,ext)): if os.path.exists("%s/%s%s" % (path, newname, ext)):
i=0 i = 0
while os.path.exists("%s/%s_%d%s"%(path,newname,i,ext)): while os.path.exists("%s/%s_%d%s" % (path, newname, i, ext)):
i+=1 i += 1
newname+="_%d"%i newname += "_%d" % i
newname+=ext newname += ext
try: try:
os.rename("%s/%s"%(path,name),"%s/%s"%(path,newname)) os.rename("%s/%s" % (path, name), "%s/%s" % (path, newname))
except OSError: except OSError:
pass # don't fail if the rename didn't work pass # don't fail if the rename didn't work
return newname return newname
def write_to_db(filename): def write_to_db(filename):
global iTunesSD,domains,total_count,KnownEntries,Rules global iTunesSD, domains, total_count, KnownEntries, Rules
# set default properties # set default properties
props={ props = {
'filename': filename, 'filename': filename,
'size': filesize(filename[1:]), 'size': filesize(filename[1:]),
'ignore': 0, 'ignore': 0,
@ -252,63 +260,65 @@ def write_to_db(filename):
} }
# check and apply rules # check and apply rules
for ruleset,action in Rules: for ruleset, action in Rules:
if reduce(operator.__and__,[MatchRule(props,rule) for rule in ruleset],True): if reduce(operator.__and__, [MatchRule(props, rule) for rule in ruleset], True):
props.update(action) props.update(action)
if props['ignore']: return 0 if props['ignore']: return 0
# retrieve entry from known entries or rebuild it # retrieve entry from known entries or rebuild it
entry=props['reuse'] and (filename in KnownEntries) and KnownEntries[filename] entry = props['reuse'] and (filename in KnownEntries) and KnownEntries[filename]
if not entry: if not entry:
header[29]=props['type'] header[29] = props['type']
entry=header.tostring()+ \ entry = header.tostring() + \
"".join([c+"\0" for c in filename[:261]])+ \ "".join([c + "\0" for c in filename[:261]]) + \
"\0"*(525-2*len(filename)) "\0" * (525 - 2 * len(filename))
# write entry, modifying shuffleflag and bookmarkflag at least # write entry, modifying shuffleflag and bookmarkflag at least
iTunesSD.write(entry[:555]+chr(props['shuffle'])+chr(props['bookmark'])+entry[557]) iTunesSD.write(entry[:555] + chr(props['shuffle']) + chr(props['bookmark']) + entry[557])
if props['shuffle']: domains[-1].append(total_count) if props['shuffle']: domains[-1].append(total_count)
total_count+=1 total_count += 1
return 1 return 1
def make_key(s): def make_key(s):
if not s: return s if not s: return s
s=s.lower() s = s.lower()
for i in range(len(s)): for i in range(len(s)):
if s[i].isdigit(): break if s[i].isdigit(): break
if not s[i].isdigit(): return s if not s[i].isdigit(): return s
for j in range(i,len(s)): for j in range(i, len(s)):
if not s[j].isdigit(): break if not s[j].isdigit(): break
if s[j].isdigit(): j+=1 if s[j].isdigit(): j += 1
return (s[:i],int(s[i:j]),make_key(s[j:])) return (s[:i], int(s[i:j]), make_key(s[j:]))
def key_repr(x): def key_repr(x):
if type(x)==tuple: if type(x) == tuple:
return "%s%d%s"%(x[0],x[1],key_repr(x[2])) return "%s%d%s" % (x[0], x[1], key_repr(x[2]))
else: else:
return x return x
def cmp_key(a,b):
if type(a)==tuple and type(b)==tuple: def cmp_key(a, b):
return cmp(a[0],b[0]) or cmp(a[1],b[1]) or cmp_key(a[2],b[2]) if type(a) == tuple and type(b) == tuple:
return cmp(a[0], b[0]) or cmp(a[1], b[1]) or cmp_key(a[2], b[2])
else: else:
return cmp(key_repr(a),key_repr(b)) return cmp(key_repr(a), key_repr(b))
def file_entry(path,name,prefix=""): def file_entry(path, name, prefix=""):
if not(name) or name[0]==".": return None if not (name) or name[0] == ".": return None
fullname="%s/%s"%(path,name) fullname = "%s/%s" % (path, name)
may_rename=not(fullname.startswith("./iPod_Control")) and Options['rename'] may_rename = not (fullname.startswith("./iPod_Control")) and Options['rename']
try: try:
if os.path.islink(fullname): if os.path.islink(fullname):
return None return None
if os.path.isdir(fullname): if os.path.isdir(fullname):
if may_rename: name=rename_safely(path,name) if may_rename: name = rename_safely(path, name)
return (0,make_key(name),prefix+name) return (0, make_key(name), prefix + name)
if os.path.splitext(name)[1].lower() in (".mp3",".m4a",".m4b",".m4p",".aa",".wav"): if os.path.splitext(name)[1].lower() in (".mp3", ".m4a", ".m4b", ".m4p", ".aa", ".wav"):
if may_rename: name=rename_safely(path,name) if may_rename: name = rename_safely(path, name)
return (1,make_key(name),prefix+name) return (1, make_key(name), prefix + name)
except OSError: except OSError:
pass pass
return None return None
@ -317,19 +327,19 @@ def file_entry(path,name,prefix=""):
def browse(path, interactive): def browse(path, interactive):
global domains global domains
if path[-1]=="/": path=path[:-1] if path[-1] == "/": path = path[:-1]
displaypath=path[1:] displaypath = path[1:]
if not displaypath: displaypath="/" if not displaypath: displaypath = "/"
if interactive: if interactive:
while 1: while 1:
try: try:
choice=input("include `%s'? [(Y)es, (N)o, (A)ll] "%displaypath)[:1].lower() choice = input("include `%s'? [(Y)es, (N)o, (A)ll] " % displaypath)[:1].lower()
except EOFError: except EOFError:
raise KeyboardInterrupt raise KeyboardInterrupt
if not choice: continue if not choice: continue
if choice in "at": # all/alle/tous/<dontknow> if choice in "at": # all/alle/tous/<dontknow>
interactive=0 interactive = 0
break break
if choice in "yjos": # yes/ja/oui/si if choice in "yjos": # yes/ja/oui/si
break break
@ -337,71 +347,73 @@ def browse(path, interactive):
return 0 return 0
try: try:
files=[_f for _f in [file_entry(path,name) for name in os.listdir(path)] if _f] files = [_f for _f in [file_entry(path, name) for name in os.listdir(path)] if _f]
except OSError: except OSError:
return return
if path=="./iPod_Control/Music": if path == "./iPod_Control/Music":
subdirs=[x[2] for x in files if not x[0]] subdirs = [x[2] for x in files if not x[0]]
files=[x for x in files if x[0]] files = [x for x in files if x[0]]
for dir in subdirs: for dir in subdirs:
subpath="%s/%s"%(path,dir) subpath = "%s/%s" % (path, dir)
try: try:
files.extend([x for x in [file_entry(subpath,name,dir+"/") for name in os.listdir(subpath)] if x and x[0]]) files.extend(
[x for x in [file_entry(subpath, name, dir + "/") for name in os.listdir(subpath)] if x and x[0]])
except OSError: except OSError:
pass pass
files.sort(cmp_key) files.sort(cmp_key)
count=len([None for x in files if x[0]]) count = len([None for x in files if x[0]])
if count: domains.append([]) if count: domains.append([])
real_count=0 real_count = 0
for item in files: for item in files:
fullname="%s/%s"%(path,item[2]) fullname = "%s/%s" % (path, item[2])
if item[0]: if item[0]:
real_count+=write_to_db(fullname[1:]) real_count += write_to_db(fullname[1:])
else: else:
browse(fullname,interactive) browse(fullname, interactive)
if real_count==count: if real_count == count:
log("%s: %d files"%(displaypath,count)) log("%s: %d files" % (displaypath, count))
else: else:
log("%s: %d files (out of %d)"%(displaypath,real_count,count)) log("%s: %d files (out of %d)" % (displaypath, real_count, count))
################################################################################ ################################################################################
def stringval(i): def stringval(i):
if i<0: i+=0x1000000 if i < 0: i += 0x1000000
return "%c%c%c"%(i&0xFF,(i>>8)&0xFF,(i>>16)&0xFF) return "%c%c%c" % (i & 0xFF, (i >> 8) & 0xFF, (i >> 16) & 0xFF)
def listval(i): def listval(i):
if i<0: i+=0x1000000 if i < 0: i += 0x1000000
return [i&0xFF,(i>>8)&0xFF,(i>>16)&0xFF] return [i & 0xFF, (i >> 8) & 0xFF, (i >> 16) & 0xFF]
def make_playback_state(volume=None): def make_playback_state(volume=None):
# I'm not at all proud of this function. Why can't stupid Python make strings # I'm not at all proud of this function. Why can't stupid Python make strings
# mutable?! # mutable?!
log("Setting playback state ...",False) log("Setting playback state ...", False)
PState=[] PState = []
try: try:
f=file("iPod_Control/iTunes/iTunesPState","rb") f = file("iPod_Control/iTunes/iTunesPState", "rb")
a=array.array('B') a = array.array('B')
a.fromstring(f.read()) a.fromstring(f.read())
PState=a.tolist() PState = a.tolist()
f.close() f.close()
except IOError as EOFError: except IOError as EOFError:
del PState[:] del PState[:]
if len(PState)!=21: if len(PState) != 21:
PState=listval(29)+[0]*15+listval(1) # volume 29, FW ver 1.0 PState = listval(29) + [0] * 15 + listval(1) # volume 29, FW ver 1.0
PState[3:15]=[0]*6+[1]+[0]*5 # track 0, shuffle mode, start of track PState[3:15] = [0] * 6 + [1] + [0] * 5 # track 0, shuffle mode, start of track
if volume is not None: if volume is not None:
PState[:3]=listval(volume) PState[:3] = listval(volume)
try: try:
f=file("iPod_Control/iTunes/iTunesPState","wb") f = file("iPod_Control/iTunes/iTunesPState", "wb")
array.array('B',PState).tofile(f) array.array('B', PState).tofile(f)
f.close() f.close()
except IOError: except IOError:
log("FAILED.") log("FAILED.")
@ -411,10 +423,10 @@ def make_playback_state(volume=None):
def make_stats(count): def make_stats(count):
log("Creating statistics file ...",False) log("Creating statistics file ...", False)
try: try:
file("iPod_Control/iTunes/iTunesStats","wb").write(\ file("iPod_Control/iTunes/iTunesStats", "wb").write( \
stringval(count)+"\0"*3+(stringval(18)+"\xff"*3+"\0"*12)*count) stringval(count) + "\0" * 3 + (stringval(18) + "\xff" * 3 + "\0" * 12) * count)
except IOError: except IOError:
log("FAILED.") log("FAILED.")
return 0 return 0
@ -427,54 +439,56 @@ def make_stats(count):
def smart_shuffle(): def smart_shuffle():
try: try:
slice_count=max(list(map(len,domains))) slice_count = max(list(map(len, domains)))
except ValueError: except ValueError:
return [] return []
slices=[[] for x in range(slice_count)] slices = [[] for x in range(slice_count)]
slice_fill=[0]*slice_count slice_fill = [0] * slice_count
for d in range(len(domains)): for d in range(len(domains)):
used=[] used = []
if not domains[d]: continue if not domains[d]: continue
for n in domains[d]: for n in domains[d]:
# find slices where the nearest track of the same domain is far away # find slices where the nearest track of the same domain is far away
metric=[min([slice_count]+[min(abs(s-u),abs(s-u+slice_count),abs(s-u-slice_count)) for u in used]) for s in range(slice_count)] metric = [
thresh=old_div((max(metric)+1),2) min([slice_count] + [min(abs(s - u), abs(s - u + slice_count), abs(s - u - slice_count)) for u in used])
farthest=[s for s in range(slice_count) if metric[s]>=thresh] for s in range(slice_count)]
thresh = old_div((max(metric) + 1), 2)
farthest = [s for s in range(slice_count) if metric[s] >= thresh]
# find emptiest slices # find emptiest slices
thresh=old_div((min(slice_fill)+max(slice_fill)+1),2) thresh = old_div((min(slice_fill) + max(slice_fill) + 1), 2)
emptiest=[s for s in range(slice_count) if slice_fill[s]<=thresh if (s in farthest)] emptiest = [s for s in range(slice_count) if slice_fill[s] <= thresh if (s in farthest)]
# choose one of the remaining candidates and add the track to the chosen slice # choose one of the remaining candidates and add the track to the chosen slice
s=random.choice(emptiest or farthest) s = random.choice(emptiest or farthest)
slices[s].append((n,d)) slices[s].append((n, d))
slice_fill[s]+=1 slice_fill[s] += 1
used.append(s) used.append(s)
# shuffle slices and avoid adjacent tracks of the same domain at slice boundaries # shuffle slices and avoid adjacent tracks of the same domain at slice boundaries
seq=[] seq = []
last_domain=-1 last_domain = -1
for slice in slices: for slice in slices:
random.shuffle(slice) random.shuffle(slice)
if len(slice)>2 and slice[0][1]==last_domain: if len(slice) > 2 and slice[0][1] == last_domain:
slice.append(slice.pop(0)) slice.append(slice.pop(0))
seq+=[x[0] for x in slice] seq += [x[0] for x in slice]
last_domain=slice[-1][1] last_domain = slice[-1][1]
return seq return seq
def make_shuffle(count): def make_shuffle(count):
random.seed() random.seed()
if Options['smart']: if Options['smart']:
log("Generating smart shuffle sequence ...",False) log("Generating smart shuffle sequence ...", False)
seq=smart_shuffle() seq = smart_shuffle()
else: else:
log("Generating shuffle sequence ...",False) log("Generating shuffle sequence ...", False)
seq=list(range(count)) seq = list(range(count))
random.shuffle(seq) random.shuffle(seq)
try: try:
file("iPod_Control/iTunes/iTunesShuffle","wb").write("".join(map(stringval,seq))) file("iPod_Control/iTunes/iTunesShuffle", "wb").write("".join(map(stringval, seq)))
except IOError: except IOError:
log("FAILED.") log("FAILED.")
return 0 return 0
@ -486,13 +500,13 @@ def make_shuffle(count):
def main(dirs): def main(dirs):
global header,iTunesSD,total_count,KnownEntries,Rules global header, iTunesSD, total_count, KnownEntries, Rules
log("Welcome to %s, version %s"%(__title__,__version__)) log("Welcome to %s, version %s" % (__title__, __version__))
log() log()
try: try:
f=file("rebuild_db.rules","r") f = file("rebuild_db.rules", "r")
Rules+=[_f for _f in map(ParseRuleLine,f.read().split("\n")) if _f] Rules += [_f for _f in map(ParseRuleLine, f.read().split("\n")) if _f]
f.close() f.close()
except IOError: except IOError:
pass pass
@ -504,40 +518,40 @@ Please make sure that:
(*) the iPod was correctly initialized with iTunes""") (*) the iPod was correctly initialized with iTunes""")
sys.exit(1) sys.exit(1)
header=array.array('B') header = array.array('B')
iTunesSD=None iTunesSD = None
try: try:
iTunesSD=file("iPod_Control/iTunes/iTunesSD","rb") iTunesSD = file("iPod_Control/iTunes/iTunesSD", "rb")
header.fromfile(iTunesSD,51) header.fromfile(iTunesSD, 51)
if Options['reuse']: if Options['reuse']:
iTunesSD.seek(18) iTunesSD.seek(18)
entry=iTunesSD.read(558) entry = iTunesSD.read(558)
while len(entry)==558: while len(entry) == 558:
filename=entry[33::2].split("\0",1)[0] filename = entry[33::2].split("\0", 1)[0]
KnownEntries[filename]=entry KnownEntries[filename] = entry
entry=iTunesSD.read(558) entry = iTunesSD.read(558)
except (IOError,EOFError): except (IOError, EOFError):
pass pass
if iTunesSD: iTunesSD.close() if iTunesSD: iTunesSD.close()
if len(header)==51: if len(header) == 51:
log("Using iTunesSD headers from existing database.") log("Using iTunesSD headers from existing database.")
if KnownEntries: if KnownEntries:
log("Collected %d entries from existing database."%len(KnownEntries)) log("Collected %d entries from existing database." % len(KnownEntries))
else: else:
del header[18:] del header[18:]
if len(header)==18: if len(header) == 18:
log("Using iTunesSD main header from existing database.") log("Using iTunesSD main header from existing database.")
else: else:
del header[:] del header[:]
log("Rebuilding iTunesSD main header from scratch.") log("Rebuilding iTunesSD main header from scratch.")
header.fromlist([0,0,0,1,6,0,0,0,18]+[0]*9) header.fromlist([0, 0, 0, 1, 6, 0, 0, 0, 18] + [0] * 9)
log("Rebuilding iTunesSD entry header from scratch.") log("Rebuilding iTunesSD entry header from scratch.")
header.fromlist([0,2,46,90,165,1]+[0]*20+[100,0,0,1,0,2,0]) header.fromlist([0, 2, 46, 90, 165, 1] + [0] * 20 + [100, 0, 0, 1, 0, 2, 0])
log() log()
try: try:
iTunesSD=file("iPod_Control/iTunes/iTunesSD","wb") iTunesSD = file("iPod_Control/iTunes/iTunesSD", "wb")
header[:18].tofile(iTunesSD) header[:18].tofile(iTunesSD)
except IOError: except IOError:
log("""ERROR: Cannot write to the iPod database file (iTunesSD)! log("""ERROR: Cannot write to the iPod database file (iTunesSD)!
@ -551,22 +565,22 @@ Please make sure that:
try: try:
if dirs: if dirs:
for dir in dirs: for dir in dirs:
browse("./"+dir,Options['interactive']) browse("./" + dir, Options['interactive'])
else: else:
browse(".",Options['interactive']) browse(".", Options['interactive'])
log("%d playable files were found on your iPod."%total_count) log("%d playable files were found on your iPod." % total_count)
log() log()
log("Fixing iTunesSD header.") log("Fixing iTunesSD header.")
iTunesSD.seek(0) iTunesSD.seek(0)
iTunesSD.write("\0%c%c"%(total_count>>8,total_count&0xFF)) iTunesSD.write("\0%c%c" % (total_count >> 8, total_count & 0xFF))
iTunesSD.close() iTunesSD.close()
except IOError: except IOError:
log("ERROR: Some strange errors occured while writing iTunesSD.") log("ERROR: Some strange errors occured while writing iTunesSD.")
log(" You may have to re-initialize the iPod using iTunes.") log(" You may have to re-initialize the iPod using iTunes.")
sys.exit(1) sys.exit(1)
if make_playback_state(Options['volume'])* \ if make_playback_state(Options['volume']) * \
make_stats(total_count)* \ make_stats(total_count) * \
make_shuffle(total_count): make_shuffle(total_count):
log() log()
log("The iPod shuffle database was rebuilt successfully.") log("The iPod shuffle database was rebuilt successfully.")
@ -581,7 +595,7 @@ Please make sure that:
def help(): def help():
print("Usage: %s [OPTION]... [DIRECTORY]..."%sys.argv[0]) print("Usage: %s [OPTION]... [DIRECTORY]..." % sys.argv[0])
print("""Rebuild iPod shuffle database. print("""Rebuild iPod shuffle database.
Mandatory arguments to long options are mandatory for short options too. Mandatory arguments to long options are mandatory for short options too.
@ -599,47 +613,49 @@ searched for playable files, unless at least one DIRECTORY is specified.""")
def opterr(msg): def opterr(msg):
print("parse error:",msg) print("parse error:", msg)
print("use `%s -h' to get help"%sys.argv[0]) print("use `%s -h' to get help" % sys.argv[0])
sys.exit(1) sys.exit(1)
def parse_options(): def parse_options():
try: try:
opts,args=getopt.getopt(sys.argv[1:],"hiv:snlfL:r",\ opts, args = getopt.getopt(sys.argv[1:], "hiv:snlfL:r", \
["help","interactive","volume=","nosmart","nochdir","nolog","force","logfile=","rename"]) ["help", "interactive", "volume=", "nosmart", "nochdir", "nolog", "force",
"logfile=", "rename"])
except getopt.GetoptError as message: except getopt.GetoptError as message:
opterr(message) opterr(message)
for opt,arg in opts: for opt, arg in opts:
if opt in ("-h","--help"): if opt in ("-h", "--help"):
help() help()
sys.exit(0) sys.exit(0)
elif opt in ("-i","--interactive"): elif opt in ("-i", "--interactive"):
Options['interactive']=True Options['interactive'] = True
elif opt in ("-v","--volume"): elif opt in ("-v", "--volume"):
try: try:
Options['volume']=int(arg) Options['volume'] = int(arg)
except ValueError: except ValueError:
opterr("invalid volume") opterr("invalid volume")
elif opt in ("-s","--nosmart"): elif opt in ("-s", "--nosmart"):
Options['smart']=False Options['smart'] = False
elif opt in ("-n","--nochdir"): elif opt in ("-n", "--nochdir"):
Options['home']=False Options['home'] = False
elif opt in ("-l","--nolog"): elif opt in ("-l", "--nolog"):
Options['logging']=False Options['logging'] = False
elif opt in ("-f","--force"): elif opt in ("-f", "--force"):
Options['reuse']=0 Options['reuse'] = 0
elif opt in ("-L","--logfile"): elif opt in ("-L", "--logfile"):
Options['logfile']=arg Options['logfile'] = arg
elif opt in ("-r","--rename"): elif opt in ("-r", "--rename"):
Options['rename']=True Options['rename'] = True
return args return args
################################################################################ ################################################################################
if __name__=="__main__": if __name__ == "__main__":
args=parse_options() args = parse_options()
go_home() go_home()
open_log() open_log()
try: try: