diff --git a/mutaext.py b/mutaext.py index bea3556..149828d 100644 --- a/mutaext.py +++ b/mutaext.py @@ -1,3 +1,4 @@ +from collections import MutableMapping import mutagen import mutagen.id3 from mutagen.easyid3 import EasyID3 @@ -74,3 +75,37 @@ for tag in extra_tags: EasyID3.RegisterTextKey('albumartist', 'TPE2') EasyID3.RegisterKey('rating', rating_get, rating_set, rating_delete) + +class SyncFile(MutableMapping): + + def __init__(self, path): + self.md = mutagen.File(path, easy=True) + self.path = path + self.seen = False + + def __getitem__(self, key): + d = self.md[key] + try: + return d[0] + except IndexError: + raise KeyError(key) + + def __setitem__(self, key, value): + if type(value) != unicode: + raise ValueError + self.md[key] = [value] + + def __delitem__(self, key): + del(self.md[key]) + + def __iter__(self): + for k in self.md: + try: + self.__getitem__(k) + except KeyError: + pass + else: + yield k + + def __len__(self): + return len([k for k in self.__iter__()]) diff --git a/unsync.py b/unsync.py index 808d2bc..a5bfafb 100755 --- a/unsync.py +++ b/unsync.py @@ -8,10 +8,10 @@ import os.path import sys import shutil import tempfile -import mutagen import mutaext import convert +from mutaext import SyncFile # BUG: doesn't work with my .m4a files? goodexts = ('.mp3', '.m4a', '.flac', '.ogg') @@ -33,14 +33,14 @@ extof = lambda p: os.path.splitext(p)[1].lower() filterext = lambda ps, es: (p for p in ps if extof(p) in es) def shouldsync(md): - rating = md.get('rating') + rating = md.get('rating', u'') sync = md.get('sync', u'') try: - rating = int(rating[0]) - except (IndexError, ValueError): + rating = int(rating) + except ValueError: pass if sync: - sync = sync[0].lower() + sync = sync.lower() return sync == 'yes' or type(rating) == int and rating >= 3 and sync != 'no' and sync != 'space' @@ -50,7 +50,6 @@ def fixmetadata(md): if 'title' not in md: fn = os.path.basename(md.path) fn = os.path.splitext(fn)[0] - # TODO: attempt to infer trackNum/discNum from fn md['title'] = unicode(fn) def findmatching(haystack, needle): @@ -58,7 +57,7 @@ def findmatching(haystack, needle): 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? + # TODO: check other tags too? lament("Warning: duplicate match found:") lament(u"%(title)s by %(artist)s from %(album)s" % locals()) match.seen = True @@ -67,9 +66,8 @@ def findmatching(haystack, needle): def updatemetadata(mdold, mdnew): modified = False for tag in updatabletags: - # checking for length b/c sometimes (ID3 genre) exists but empty - if tag in mdnew and len(mdnew[tag]): - if not tag in mdold or mdnew[tag][0] != mdold[tag][0]: + if tag in mdnew: + if mdnew[tag] != mdold[tag]: mdold[tag] = mdnew[tag] modified = True elif tag in mdold: @@ -78,9 +76,10 @@ def updatemetadata(mdold, mdnew): return modified def makefilename(md): - title = md['title'][0] - artist = md['artist'][0] - album = md['album'][0] + title = md['title'] + artist = md['artist'] + album = md['album'] + return u"%(artist)s - %(album)s - %(title)s.ogg" % locals() def run(args): @@ -94,14 +93,11 @@ def run(args): paths = lambda dir: filterext(walkfiles(os.walk(dir)), goodexts) for p in paths(indir): - md = mutagen.File(p, easy=True) + md = SyncFile(p) if shouldsync(md): 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) @@ -112,14 +108,14 @@ def run(args): outdir = args[2] for p in paths(outdir): - md = mutagen.File(p, easy=True) + md = SyncFile(p) match = findmatching(tosync, md) - if not match: + if match == None: print("DEL", p) os.remove(p) elif updatemetadata(md, match): print("UPD", p) - md.save() + md.md.save() for md in tosync: if md.seen: @@ -130,11 +126,11 @@ def run(args): _, ftemp = tempfile.mkstemp() try: convert.ogg(md.path, ftemp) - mdnew = mutagen.File(ftemp, easy=True) + mdnew = SyncFile(ftemp) for tag in alltags: if tag in md: mdnew[tag] = md[tag] - mdnew.save() + mdnew.md.save() shutil.copy2(ftemp, fout) finally: os.remove(ftemp)