2014-04-26 14:34:13 -07:00
|
|
|
#!/bin/python2
|
|
|
|
|
|
|
|
# only using python2 because mutagen
|
|
|
|
from __future__ import print_function
|
|
|
|
|
|
|
|
import os
|
|
|
|
import os.path
|
|
|
|
import sys
|
2014-04-27 15:34:09 -07:00
|
|
|
import shutil
|
2014-04-28 19:31:24 -07:00
|
|
|
import tempfile
|
2014-04-27 15:34:09 -07:00
|
|
|
|
2014-04-27 12:03:52 -07:00
|
|
|
import mutaext
|
2014-04-27 15:34:09 -07:00
|
|
|
import convert
|
2014-04-30 17:04:02 -07:00
|
|
|
from mutaext import SyncFile
|
2014-04-26 14:34:13 -07:00
|
|
|
|
2014-04-29 03:32:23 -07:00
|
|
|
# BUG: doesn't work with my .m4a files?
|
|
|
|
goodexts = ('.mp3', '.m4a', '.flac', '.ogg')
|
|
|
|
|
|
|
|
matchtags = ['artist', 'album', 'title']
|
2014-04-27 13:47:08 -07:00
|
|
|
updatabletags = [\
|
|
|
|
'albumartist', 'composer', 'comment' \
|
|
|
|
'tracknumber', 'discnumber', \
|
|
|
|
'genre', 'date', \
|
|
|
|
]
|
|
|
|
updatabletags.extend(mutaext.replaygain_tags)
|
|
|
|
updatabletags.extend(mutaext.extra_tags)
|
2014-04-27 15:34:09 -07:00
|
|
|
alltags = list(updatabletags)
|
2014-04-29 03:32:23 -07:00
|
|
|
alltags.extend(matchtags)
|
2014-04-27 06:46:24 -07:00
|
|
|
|
2014-04-29 03:32:23 -07:00
|
|
|
lament = lambda *args, **kwargs: print(*args, file=sys.stderr, **kwargs)
|
|
|
|
walkfiles = lambda w: (os.path.join(r, f) for r, _, fs in w for f in fs)
|
|
|
|
extof = lambda p: os.path.splitext(p)[1].lower()
|
|
|
|
filterext = lambda ps, es: (p for p in ps if extof(p) in es)
|
2014-04-27 06:46:24 -07:00
|
|
|
|
2014-04-26 14:34:13 -07:00
|
|
|
def shouldsync(md):
|
2014-04-30 17:04:02 -07:00
|
|
|
rating = md.get('rating', u'')
|
2014-04-28 19:31:24 -07:00
|
|
|
sync = md.get('sync', u'')
|
2014-04-27 13:47:08 -07:00
|
|
|
try:
|
2014-04-30 17:04:02 -07:00
|
|
|
rating = int(rating)
|
|
|
|
except ValueError:
|
2014-04-27 13:47:08 -07:00
|
|
|
pass
|
2014-04-27 07:05:51 -07:00
|
|
|
if sync:
|
2014-04-30 17:04:02 -07:00
|
|
|
sync = sync.lower()
|
2014-04-26 14:34:13 -07:00
|
|
|
|
2014-04-29 08:52:55 -07:00
|
|
|
return sync == 'yes' or type(rating) == int and rating >= 3 and sync != 'no' and sync != 'space'
|
2014-04-26 14:34:13 -07:00
|
|
|
|
2014-04-27 09:36:14 -07:00
|
|
|
def fixmetadata(md):
|
2014-04-29 03:32:23 -07:00
|
|
|
md['artist'] = md.get('artist', u"Unknown Artist")
|
|
|
|
md['album'] = md.get('album', u"Unknown Album")
|
2014-04-27 09:36:14 -07:00
|
|
|
if 'title' not in md:
|
|
|
|
fn = os.path.basename(md.path)
|
|
|
|
fn = os.path.splitext(fn)[0]
|
2014-04-29 03:32:23 -07:00
|
|
|
md['title'] = unicode(fn)
|
2014-04-27 09:36:14 -07:00
|
|
|
|
2014-04-26 14:34:13 -07:00
|
|
|
def findmatching(haystack, needle):
|
2014-04-29 03:32:23 -07:00
|
|
|
matchme = [needle[t] for t in matchtags]
|
|
|
|
ismatch = lambda hay: [hay[t] for t in matchtags] == matchme
|
|
|
|
for match in (hay for hay in haystack if ismatch(hay)):
|
|
|
|
if match.seen:
|
2014-04-30 17:04:02 -07:00
|
|
|
# TODO: check other tags too?
|
2014-04-29 03:32:23 -07:00
|
|
|
lament("Warning: duplicate match found:")
|
|
|
|
lament(u"%(title)s by %(artist)s from %(album)s" % locals())
|
|
|
|
match.seen = True
|
|
|
|
return match
|
2014-04-26 14:34:13 -07:00
|
|
|
|
2014-04-27 12:03:52 -07:00
|
|
|
def updatemetadata(mdold, mdnew):
|
|
|
|
modified = False
|
|
|
|
for tag in updatabletags:
|
2014-04-30 17:04:02 -07:00
|
|
|
if tag in mdnew:
|
|
|
|
if mdnew[tag] != mdold[tag]:
|
2014-04-27 12:03:52 -07:00
|
|
|
mdold[tag] = mdnew[tag]
|
|
|
|
modified = True
|
|
|
|
elif tag in mdold:
|
|
|
|
del mdold[tag]
|
|
|
|
modified = True
|
|
|
|
return modified
|
|
|
|
|
2014-04-27 15:34:09 -07:00
|
|
|
def makefilename(md):
|
2014-04-30 17:04:02 -07:00
|
|
|
title = md['title']
|
|
|
|
artist = md['artist']
|
|
|
|
album = md['album']
|
|
|
|
|
2014-04-29 03:32:23 -07:00
|
|
|
return u"%(artist)s - %(album)s - %(title)s.ogg" % locals()
|
2014-04-27 15:34:09 -07:00
|
|
|
|
2014-04-26 14:34:13 -07:00
|
|
|
def run(args):
|
|
|
|
if not len(args) in (2, 3):
|
2014-04-29 03:32:23 -07:00
|
|
|
lament("I need a path or two!")
|
2014-04-27 07:05:51 -07:00
|
|
|
return 1
|
2014-04-26 14:34:13 -07:00
|
|
|
inonly = len(args) == 2
|
|
|
|
|
|
|
|
tosync = []
|
|
|
|
indir = args[1]
|
2014-04-27 06:46:24 -07:00
|
|
|
paths = lambda dir: filterext(walkfiles(os.walk(dir)), goodexts)
|
2014-04-26 14:34:13 -07:00
|
|
|
|
2014-04-27 06:46:24 -07:00
|
|
|
for p in paths(indir):
|
2014-04-30 17:04:02 -07:00
|
|
|
md = SyncFile(p)
|
2014-04-27 06:46:24 -07:00
|
|
|
if shouldsync(md):
|
2014-04-27 09:36:14 -07:00
|
|
|
if inonly:
|
|
|
|
print(p)
|
|
|
|
else:
|
|
|
|
fixmetadata(md)
|
|
|
|
tosync.append(md)
|
|
|
|
|
|
|
|
if inonly:
|
|
|
|
return 0
|
2014-04-27 15:34:09 -07:00
|
|
|
|
2014-04-29 03:32:23 -07:00
|
|
|
lament("Matching...")
|
2014-04-26 14:34:13 -07:00
|
|
|
|
|
|
|
outdir = args[2]
|
2014-04-27 06:46:24 -07:00
|
|
|
for p in paths(outdir):
|
2014-04-30 17:04:02 -07:00
|
|
|
md = SyncFile(p)
|
2014-04-27 06:46:24 -07:00
|
|
|
match = findmatching(tosync, md)
|
2014-04-30 17:04:02 -07:00
|
|
|
if match == None:
|
2014-04-27 15:34:09 -07:00
|
|
|
print("DEL", p)
|
|
|
|
os.remove(p)
|
2014-04-29 03:32:23 -07:00
|
|
|
elif updatemetadata(md, match):
|
|
|
|
print("UPD", p)
|
2014-04-30 17:04:02 -07:00
|
|
|
md.md.save()
|
2014-04-27 09:36:14 -07:00
|
|
|
|
|
|
|
for md in tosync:
|
2014-04-28 19:31:24 -07:00
|
|
|
if md.seen:
|
|
|
|
continue
|
|
|
|
print("ADD", md.path)
|
|
|
|
|
|
|
|
fout = os.path.join(outdir, makefilename(md))
|
|
|
|
_, ftemp = tempfile.mkstemp()
|
|
|
|
try:
|
|
|
|
convert.ogg(md.path, ftemp)
|
2014-04-30 17:04:02 -07:00
|
|
|
mdnew = SyncFile(ftemp)
|
2014-04-27 15:34:09 -07:00
|
|
|
for tag in alltags:
|
|
|
|
if tag in md:
|
|
|
|
mdnew[tag] = md[tag]
|
2014-04-30 17:04:02 -07:00
|
|
|
mdnew.md.save()
|
2014-04-28 19:31:24 -07:00
|
|
|
shutil.copy2(ftemp, fout)
|
|
|
|
finally:
|
|
|
|
os.remove(ftemp)
|
2014-04-26 14:34:13 -07:00
|
|
|
|
2014-04-27 07:05:51 -07:00
|
|
|
return 0
|
|
|
|
|
|
|
|
ret = 0
|
2014-04-26 14:34:13 -07:00
|
|
|
try:
|
2014-04-27 07:05:51 -07:00
|
|
|
ret = run(sys.argv)
|
2014-04-26 14:34:13 -07:00
|
|
|
except KeyboardInterrupt:
|
|
|
|
sys.exit(1)
|
2014-04-27 07:05:51 -07:00
|
|
|
sys.exit(ret)
|