gists/unsync.py

150 lines
4 KiB
Python
Raw Normal View History

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-26 14:34:13 -07:00
import mutagen
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-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-28 19:31:24 -07:00
rating = md.get('rating')
sync = md.get('sync', u'')
2014-04-27 13:47:08 -07:00
try:
rating = int(rating[0])
2014-04-29 03:32:23 -07:00
except (IndexError, ValueError):
2014-04-27 13:47:08 -07:00
pass
2014-04-27 07:05:51 -07:00
if sync:
sync = sync[0].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-27 12:03:52 -07:00
# TODO: attempt to infer trackNum/discNum from fn
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:
# TODO: check other tags and filename and such?
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-29 03:32:23 -07:00
# checking for length b/c sometimes (ID3 genre) exists but empty
2014-04-28 19:31:24 -07:00
if tag in mdnew and len(mdnew[tag]):
if not tag in mdold or mdnew[tag][0] != mdold[tag][0]:
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):
title = md['title'][0]
artist = md['artist'][0]
album = md['album'][0]
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):
md = mutagen.File(p, easy=True)
if shouldsync(md):
2014-04-27 09:36:14 -07:00
if inonly:
print(p)
else:
# TODO: don't use custom members on external metadata class
md.path = p
md.seen = False
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):
md = mutagen.File(p, easy=True)
match = findmatching(tosync, md)
2014-04-29 03:32:23 -07:00
if not match:
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)
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)
mdnew = mutagen.File(ftemp, easy=True)
2014-04-27 15:34:09 -07:00
for tag in alltags:
if tag in md:
mdnew[tag] = md[tag]
mdnew.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)