gists/unsync.py

140 lines
3.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
import mutagen
import mutagen.id3
import mutagen.easyid3
def popm_any(id3):
for k, v in id3.iteritems():
if k.startswith('POPM'):
return v
def byte2rating(b):
if b >= 224: return 5
if b >= 160: return 4
if b >= 96: return 3
if b >= 32: return 2
if b >= 1: return 1
return 0
def rating_get(id3, key):
try:
return list(id3['TXXX:RATING'])
except KeyError:
try:
return list(unicode(byte2rating(popm_any(id3).rating)))
except AttributeError:
return
def rating_set(id3, key, val):
# TODO: test
try:
frame = id3["TXXX:RATING"]
except KeyError:
id3.add(mutagen.id3.TXXX(encoding=3, desc=u'RATING', text=val))
else:
frame.encoding = 3
frame.text = val
def rating_delete(id3, key):
# TODO: delete POPM too?
del(id3["TXXX:RATING"])
mutagen.easyid3.EasyID3.RegisterTXXXKey('sync', 'SYNC')
mutagen.easyid3.EasyID3.RegisterKey('rating', rating_get, rating_set, rating_delete)
def G(d, k):
try:
return d[k]
except KeyError:
return None
2014-04-26 14:34:13 -07:00
2014-04-27 06:46:24 -07:00
def walkfiles(walker):
for root, _, files in walker:
for f in files:
yield os.path.join(root, f)
def filterext(paths, exts):
for p in paths:
ext = os.path.splitext(p)[1].lower()
if ext in exts:
yield p
2014-04-26 14:34:13 -07:00
def shouldsync(md):
rating = G(md, 'rating')
sync = G(md, 'sync')
2014-04-27 07:05:51 -07:00
if rating != None:
rating = int(rating[0])
if sync:
sync = sync[0].lower()
else:
sync = u''
2014-04-26 14:34:13 -07:00
if sync == u'no' or sync == u'space':
return False
if sync == u'yes' or sync == u'share':
return True
2014-04-27 07:05:51 -07:00
if rating != None and rating >= 3:
2014-04-26 14:34:13 -07:00
return True
return False
def findmatching(haystack, needle):
artist = G(needle, 'artist')
album = G(needle, 'album')
title = G(needle, 'title')
2014-04-26 14:34:13 -07:00
for match in haystack:
if artist == G(match, 'artist') \
and album == G(match, 'album') \
and title == G(match, 'title'):
2014-04-26 14:34:13 -07:00
return match
def run(args):
if not len(args) in (2, 3):
print("I need a path or two!", file=sys.stderr)
2014-04-27 07:05:51 -07:00
return 1
2014-04-26 14:34:13 -07:00
inonly = len(args) == 2
# BUG: doesn't work with my .m4a files?
goodexts = ('.mp3', '.m4a', '.flac', '.ogg')
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):
if inonly: print(p)
else: tosync.append(md)
2014-04-26 14:34:13 -07:00
2014-04-27 07:05:51 -07:00
if inonly: return 0
2014-04-26 14:34:13 -07:00
print("Beginning matching...", file=sys.stderr)
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)
# TODO: don't print anything
# TODO: don't match mismatched lengths (Xing?)
# TODO: update important tags on mostly matching files
# TODO: don't sync files that wouldn't match!
# TODO: convert files in loop that works on altered tosync
if match:
print("MATCHING", p)
else:
# TODO: just delete missing ones here
print("MISSING", p)
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)