finish updating to python 3

This commit is contained in:
lynxize 2025-06-20 15:16:26 -06:00
parent d0d8588e5f
commit 503dd069c8
Signed by: lynxize
GPG key ID: 8615849B8532CD77

View file

@ -1,5 +1,5 @@
#!/usr/bin/env python #!/usr/bin/env python
import functools
# This program is free software; you can redistribute it and/or modify # This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or # the Free Software Foundation; either version 2 of the License, or
@ -14,18 +14,11 @@
# 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 division
from __future__ import print_function
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 functools import reduce from functools import reduce
from past.builtins import cmp
from past.utils import old_div
__title__ = "KeyJ's iPod shuffle Database Builder" __title__ = "KeyJ's iPod shuffle Database Builder"
__version__ = "1.0" __version__ = "1.0"
__author__ = "Martin Fiedler" __author__ = "Martin Fiedler"
@ -112,7 +105,7 @@ def open_log():
global logfile global logfile
if Options['logging']: if Options['logging']:
try: try:
logfile = file(Options['logfile'], "w") logfile = open(Options['logfile'], "w")
except IOError: except IOError:
logfile = None logfile = None
else: else:
@ -166,11 +159,11 @@ def MatchRule(props, rule):
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 prop == ref
elif rule[1] == '>': elif rule[1] == '>':
return cmp(prop, ref) > 0 return prop > ref
elif rule[1] == '<': elif rule[1] == '<':
return cmp(prop, ref) < 0 return prop < ref
else: else:
return False return False
@ -193,7 +186,7 @@ def ParseRule(rule):
def ParseAction(action): def ParseAction(action):
prop, value = list(map(string.strip, action.split('=', 1))) prop, value = list(map(str.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))
@ -201,12 +194,12 @@ def ParseAction(action):
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(str.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)
@ -215,7 +208,6 @@ def ParseRuleLine(line):
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
################################################################################ ################################################################################
@ -269,12 +261,12 @@ def write_to_db(filename):
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.tobytes() + \
"".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))).encode()
# 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] + bytes([props['shuffle']]) + bytes([props['bookmark']]) + bytes([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
@ -294,7 +286,7 @@ def make_key(s):
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 b"%s%d%s" % (x[0], x[1], key_repr(x[2]))
else: else:
return x return x
@ -305,9 +297,14 @@ def cmp_key(a, b):
else: else:
return cmp(key_repr(a), key_repr(b)) return cmp(key_repr(a), key_repr(b))
def cmp(a, b):
if a < b: return -1
elif a > b: return 1
else: return 0
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:
@ -315,10 +312,10 @@ def file_entry(path, name, prefix=""):
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
@ -362,7 +359,7 @@ def browse(path, interactive):
except OSError: except OSError:
pass pass
files.sort(cmp_key) files.sort(key = functools.cmp_to_key(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([])
@ -385,7 +382,7 @@ def browse(path, interactive):
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 b"%c%c%c" % (i & 0xFF, (i >> 8) & 0xFF, (i >> 16) & 0xFF)
def listval(i): def listval(i):
@ -399,20 +396,21 @@ def make_playback_state(volume=None):
log("Setting playback state ...", False) log("Setting playback state ...", False)
PState = [] PState = []
try: try:
f = file("iPod_Control/iTunes/iTunesPState", "rb") f = open("iPod_Control/iTunes/iTunesPState", "rb")
a = array.array('B') a = array.array('B')
a.fromstring(f.read()) a.frombytes(f.read())
PState = a.tolist() PState = a.tolist()
f.close() f.close()
except IOError as EOFError: except IOError:
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 # print("catstare")
# 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 = open("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:
@ -425,8 +423,9 @@ 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 = open("iPod_Control/iTunes/iTunesStats", "wb")
stringval(count) + "\0" * 3 + (stringval(18) + "\xff" * 3 + "\0" * 12) * count) file.write(stringval(count) + b"\0" * 3 + (stringval(18) + b"\xff" * 3 + b"\0" * 12) * count)
file.close()
except IOError: except IOError:
log("FAILED.") log("FAILED.")
return 0 return 0
@ -453,11 +452,11 @@ def smart_shuffle():
metric = [ metric = [
min([slice_count] + [min(abs(s - u), abs(s - u + slice_count), abs(s - u - slice_count)) for u in used]) 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)] for s in range(slice_count)]
thresh = old_div((max(metric) + 1), 2) thresh = (max(metric) + 1) // 2
farthest = [s for s in range(slice_count) if metric[s] >= thresh] 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 = (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
@ -488,7 +487,8 @@ def make_shuffle(count):
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))) with open("iPod_Control/iTunes/iTunesShuffle", "wb") as file:
file.write(b"".join(map(stringval, seq)))
except IOError: except IOError:
log("FAILED.") log("FAILED.")
return 0 return 0
@ -505,7 +505,7 @@ def main(dirs):
log() log()
try: try:
f = file("rebuild_db.rules", "r") f = open("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:
@ -521,13 +521,13 @@ Please make sure that:
header = array.array('B') header = array.array('B')
iTunesSD = None iTunesSD = None
try: try:
iTunesSD = file("iPod_Control/iTunes/iTunesSD", "rb") iTunesSD = open("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(b"\0", 1)[0]
KnownEntries[filename] = entry KnownEntries[filename] = entry
entry = iTunesSD.read(558) entry = iTunesSD.read(558)
except (IOError, EOFError): except (IOError, EOFError):
@ -551,7 +551,7 @@ Please make sure that:
log() log()
try: try:
iTunesSD = file("iPod_Control/iTunes/iTunesSD", "wb") iTunesSD = open("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)!
@ -572,7 +572,7 @@ Please make sure that:
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(b"\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.")
@ -625,6 +625,8 @@ def parse_options():
"logfile=", "rename"]) "logfile=", "rename"])
except getopt.GetoptError as message: except getopt.GetoptError as message:
opterr(message) opterr(message)
sys.exit(1)
for opt, arg in opts: for opt, arg in opts:
if opt in ("-h", "--help"): if opt in ("-h", "--help"):
help() help()