#!/usr/bin/python # Transcode my music collection into an ogg version suitable for portable # devices. import os, sys, re, subprocess, getopt music = "/n/stuff/music" source_dirs = [ music + "/cds", music + "/lps", music + "/tapes", "/home/ats/Work/music/recorded", ] exclude_res = [ re.compile(r'/raw/'), ] dest_dir = music + "/ogg" transcode_exts = [".flac", ".wav"] lossy_exts = [".mp3", ".ogg"] oggenc_args = ["-q5"] dryrun = False lint = False def run_command(cmd): if dryrun: print "Would run: %s" % (" ".join(cmd)) return rc = subprocess.call(cmd) if rc != 0: print "Command failed with rc %d: %s" % (rc, " ".join(cmd)) sys.exit(rc) def clean_fn(fn): """Replace characters that aren't legal in VFAT long filenames.""" fn = fn.replace(":", " -") fn = re.sub(r'["*<>?\\|\000-\037\177-\377]', '_', fn) return fn def sync_dir(src, base, files, seen): dest = dest_dir + clean_fn(base) has_lossy = False has_lossless = False for fn in files: if any([fn.endswith(ext) for ext in lossy_exts]): has_lossy = True continue base_fn = None for ext in transcode_exts: if fn.endswith(ext): base_fn = fn[:-len(ext)] if base_fn is None: continue has_lossless = True src_fn = src + "/" + fn excluded = False for exp in exclude_res: if exp.search(src_fn) is not None: excluded = True if excluded: continue dest_fn = dest + "/" + clean_fn(base_fn) + ".ogg" seen.add(dest_fn) if os.access(dest_fn, os.F_OK): continue print "Transcode %s to %s" % (src_fn, dest_fn) try: if not dryrun: os.makedirs(dest) except OSError: pass run_command(["oggenc", "-Q", "-o", dest_fn] + oggenc_args + [src_fn]) if lint and has_lossy: if has_lossless: print "Contains both lossy and lossless files: %s" % src else: print "Contains lossy files: %s" % src def clean_dir(dest, files, seen): for fn in files: dest_fn = dest + "/" + fn if not dest_fn in seen: print "Remove %s" % dest_fn if not dryrun: os.unlink(dest_fn) if os.listdir(dest) == []: print "Remove dir %s" % dest if not dryrun: os.rmdir(dest) def main(args): try: opts, args = getopt.getopt(args, "nl", []) except getopt.GetoptError: print "Usage: transcode-music [-n] [-l]" sys.exit(1) global dryrun, lint for o, a in opts: if o == "-n": dryrun = True elif o == "-l": lint = True seen = set() for dir in source_dirs: for src, dirs, files in os.walk(dir, topdown = True): sync_dir(src, src[len(dir):], files, seen) for dest, dirs, files in os.walk(dest_dir, topdown = False): clean_dir(dest, files, seen) if __name__ == "__main__": main(sys.argv[1:])