Bisschen länger.
Synct die Dateien einer .m3u Playlist in einen Ordner. Konvertiert dabei alac files in mp3 mit brauchbarer Qualität. Behält die Ordnerhierarchie bei.
Wenn es jemand benutzen will:
Es SYNCT die Playlist und den Ordner. Sprich, es löscht alles was da sonst noch im Zielordner und nicht in der m3u.
Legt einen Cache für checksums in ~/.pushm3ucache/checksums an, der Ordner sollte also existieren, sonst halt ändern.
Ist nützlich wenn man seinen bevorzugten Musikplayer irgendwie mounten kann und dann nen einfachen Sync-Mechanismus ohne nen ganzen Player will, so wie ich, weil beim Galaxy Nexus irgendwie jedes Programm das das theoretisch können sollte abschmiert.
Braucht audiotools (audiotools.sf.net). Code könnte crap sein, hab nicht so den Plan von Python, und womöglich kriegt man was besseres mit rsync hin.
Code:
#!/usr/bin/python
import sys
import os
import audiotools
import hashlib
import tempfile
import base64
import shutil
import string
sumsfile = "~/.pushm3ucache/checksums"
deviceFormats = ["mp3", "m4a"]
convertFormat = audiotools.MP3Audio
convertQuality = "extreme"
def pathize(path):
return os.path.abspath(os.path.expanduser(path))
def parsem3u(filename):
print "Reading m3u file..."
files = []
f = open(filename, 'r')
for line in f:
if (line[0] != '#'):
path = pathize(os.path.join(os.path.dirname(filename), line.strip()))
if (os.path.exists(path)):
files.append(path)
else:
print("What the hell is '" + line + "' doing in your .m3u file? "
+ "I don't know what that is.")
return files
def printAudiotoolsInfo():
print "pushm3u using audiotools " + audiotools.VERSION
print "supported types: "
for k, v in audiotools.TYPE_MAP.iteritems():
print k + " with supported compression modes " + str(v.COMPRESSION_MODES)
def getNewSums(files):
print "Getting new checksums..."
checksums = {}
for file in files:
md5 = hashlib.md5()
with open(file, 'r') as f:
for chunk in iter(lambda: f.read(128*md5.block_size), b''):
md5.update(chunk)
checksums[file] = md5.digest()
return checksums
def getOldSums():
checksums = {}
key = ""
if os.path.exists(sumsfile):
f = open(sumsfile, 'r')
for number, line in enumerate(f):
if (number % 2 == 0):
key = line.strip()
else:
checksums[key] = base64.b64decode(line.strip())
return checksums
def writeSums(checksums):
f = open(sumsfile, 'w')
for k, v in checksums.iteritems():
f.write(k + '\n' + base64.b64encode(v) + '\n')
def filterChanges(files, oldSums, newSums):
newFiles = []
for file in files:
if file not in oldSums or oldSums[file] != newSums[file]:
newFiles.append(file)
return newFiles
def convert(filename, audio):
print "Converting " + os.path.basename(filename) + "..."
newFile = tempfile.NamedTemporaryFile().name
audio.convert(newFile, convertFormat, convertQuality)
newAudio = audiotools.open(newFile)
newAudio.set_metadata(audio.get_metadata())
return newFile
def getCopyables(files, audios):
copyables = []
for file, audio in zip(files, audios):
if audio.NAME in deviceFormats:
copyables.append(file)
else:
copyables.append(convert(file, audio))
return copyables
def destMap(srcs, newSrcs, dest):
if (len(srcs) > 1):
prefix = os.path.commonprefix(srcs)
print "stripping " + prefix + " from the paths."
dsts = map(lambda x : os.path.join(dest, string.lstrip(newSrcs[x], prefix)), srcs)
destMap = dict(zip(srcs, dsts))
return destMap
def copyAll(origs, news, dests):
print "Copying..."
for orig, new in zip(origs, news):
dest = dests[orig]
dir = os.path.dirname(dest)
if not os.path.exists(dir):
os.makedirs(dir)
shutil.copy(new, dest)
def getNewSrcs(files, audios):
newSrcs = {}
for filename, audio in zip(files, audios):
if audio.NAME in deviceFormats:
newSrcs[filename] = filename
else:
base, ext = os.path.splitext(filename)
newFile = base + '.' + convertFormat.SUFFIX
newSrcs[filename] = newFile
return newSrcs
def delIfEmpty(dirname):
if os.listdir(dirname) == []:
print "removing empty directory " + path
os.removedir(dirname)
def visitCrap(dests, dirname, names):
for name in names:
path = os.path.join(dirname, name)
if os.path.isfile(path):
if path not in dests.values():
print "removing file " + path
os.remove(path)
elif os.path.isdir(path):
delIfEmpty(path)
delIfEmpty(dirname)
def deleteCrap(dest, dests):
os.path.walk(dest, visitCrap, dests)
def pushm3u(src, dest):
printAudiotoolsInfo()
srcs = parsem3u(src)
checksums = getNewSums(srcs)
newSrcs = getNewSrcs(srcs, audiotools.open_files(srcs, False))
copyFiles = filterChanges(srcs, getOldSums(), checksums)
copyables = getCopyables(copyFiles, audiotools.open_files(copyFiles, False))
dests = destMap(srcs, newSrcs, dest)
copyAll(copyFiles, copyables, dests)
writeSums(checksums)
deleteCrap(dest, dests)
# MAIN #
if (len(sys.argv) < 3):
print "usage: pushm3u source.m3u destination"
else:
src = pathize(sys.argv[1])
dest = pathize(sys.argv[2])
sumsfile = pathize(sumsfile)
pushm3u(src, dest)