gists/music_sync/unsync.py

173 lines
4.5 KiB
Python
Raw Permalink Normal View History

#!/bin/python
2014-04-26 14:34:13 -07:00
2014-11-02 13:29:44 -08:00
import os, os.path
2014-04-26 14:34:13 -07:00
import sys
2014-11-02 13:29:44 -08:00
from shutil import copy2
from tempfile import mkstemp
2014-05-25 16:34:58 -07:00
from zlib import crc32
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-11-02 10:12:52 -08:00
goodexts = ('.mp3', '.flac', '.ogg')
2014-04-29 03:32:23 -07:00
2015-03-24 20:25:59 -07:00
matchtags = ['artist', 'album', 'title', 'tracknumber', 'discnumber']
2014-11-02 13:29:44 -08:00
alltags = [
'albumartist', 'composer', 'comment',
'genre', 'date',
2014-04-27 13:47:08 -07:00
]
2014-05-03 19:33:38 -07:00
alltags.extend(mutaext.replaygain_tags)
alltags.extend(mutaext.extra_tags)
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):
rating = md.get('rating', '')
sync = md.get('sync', '')
2014-11-02 13:29:44 -08:00
if rating.isnumeric():
2014-04-30 17:04:02 -07:00
rating = int(rating)
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
2015-03-24 20:25:59 -07:00
import re
re_digits = re.compile(r'\d+')
def tonumber(crap):
if crap is None or len(crap) == 0:
return 0
nums = re_digits.findall(crap)
if len(nums) == 0:
return 0
return int(nums[0])
2014-04-27 09:36:14 -07:00
def fixmetadata(md):
md['artist'] = md.get('artist', "Unknown Artist")
md['album'] = md.get('album', "Unknown Album")
2015-03-24 20:25:59 -07:00
md['discnumber'] = str(tonumber(md.get('discnumber', '0')))
md['tracknumber'] = str(tonumber(md.get('tracknumber', '0')))
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]
md['title'] = str(fn)
2014-04-27 09:36:14 -07:00
2014-04-26 14:34:13 -07:00
def findmatching(haystack, needle):
2015-03-24 20:25:59 -07:00
#matchme = [needle[t].lower() for t in matchtags]
#ismatch = lambda hay: [hay[t].lower() for t in matchtags] == matchme
matchme = [needle[t] for t in matchtags]
ismatch = lambda hay: [hay[t] for t in matchtags] == matchme
2014-04-29 03:32:23 -07:00
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-05-03 19:33:38 -07:00
lament("Duplicate")
return None
match.seen = needle.path
2014-04-29 03:32:23 -07:00
return match
2014-04-26 14:34:13 -07:00
2014-04-27 12:03:52 -07:00
def updatemetadata(mdold, mdnew):
modified = False
2014-05-03 19:33:38 -07:00
for tag in alltags:
2014-04-30 17:04:02 -07:00
if tag in mdnew:
2014-05-03 19:33:38 -07:00
if tag not in mdold or 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']
2015-03-24 20:25:59 -07:00
track = md['tracknumber']
disc = md['discnumber']
2014-04-30 17:04:02 -07:00
2015-03-24 20:25:59 -07:00
fn = "%(disc)s-%(track)s - %(artist)s - %(album)s - %(title)s" % locals()
2014-05-25 16:34:58 -07:00
# FAT is a pain to deal with so just use nondescript filenames
2014-11-02 13:29:44 -08:00
crc = crc32(fn.encode('utf-8')) & 0xFFFFFFFF
2014-05-25 16:34:58 -07:00
fn = '{:08X}.ogg'.format(crc)
return fn
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]
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-11-02 13:29:44 -08:00
lament("Matching tags...")
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)
2015-03-24 20:25:59 -07:00
fixmetadata(md)
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)
2015-03-24 20:25:59 -07:00
print('was', md['title'], 'by', md['artist'])
2014-04-27 15:34:09 -07:00
os.remove(p)
2014-04-29 03:32:23 -07:00
elif updatemetadata(md, match):
print("UPD", p)
2014-11-02 13:29:44 -08:00
md.save()
lament("Syncing files...")
2014-04-27 09:36:14 -07:00
for md in tosync:
2014-05-03 19:33:38 -07:00
fn = makefilename(md)
fout = os.path.join(outdir, fn)
2014-04-28 19:31:24 -07:00
if md.seen:
2014-11-02 13:29:44 -08:00
_from = md.seen
_to = fout
if _from != _to:
print("MOV", _from)
os.rename(_from, _to)
continue
2014-05-03 19:33:38 -07:00
2014-04-28 19:31:24 -07:00
print("ADD", md.path)
2014-11-02 13:29:44 -08:00
_, ftemp = mkstemp()
2014-04-28 19:31:24 -07:00
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]
2015-03-24 20:25:59 -07:00
fixmetadata(mdnew) # redundant?
2014-11-02 13:29:44 -08:00
mdnew.save()
copy2(ftemp, fout)
2014-04-28 19:31:24 -07:00
finally:
os.remove(ftemp)
2014-04-26 14:34:13 -07:00
2014-04-27 07:05:51 -07:00
return 0
2014-11-02 10:12:52 -08:00
if __name__ == '__main__':
ret = 0
try:
ret = run(sys.argv)
except KeyboardInterrupt:
sys.exit(1)
sys.exit(ret)