Last active
March 27, 2016 09:22
-
-
Save sixohsix/e1e9b3a7b51ae02b3579 to your computer and use it in GitHub Desktop.
A script to move music from one dir to another, and reencode m4a files to mp3 in the process
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import contextlib | |
| import multiprocessing | |
| import os | |
| import shutil | |
| import subprocess | |
| import sys | |
| REALLY = True | |
| FFMPEG = r'c:\Users\Mike\Desktop\ffmpeg-20160325-git-585cfab-win64-static\bin\ffmpeg.exe' | |
| QUEUE_SIZE = 4 | |
| NUM_PROCESSES = 3 | |
| STOP_QUEUE = ('stop', None) | |
| class FfmpegRunner(object): | |
| def __init__(self, queue, proc_id): | |
| self.queue = queue | |
| self.proc_id = proc_id | |
| print "Created runner", proc_id | |
| def work(self): | |
| print "Started {}".format(self.proc_id) | |
| while True: | |
| req = self.queue.get() | |
| if req == STOP_QUEUE: | |
| print ' <{}> Stopping.'.format(self.proc_id) | |
| break | |
| else: | |
| path, dest_path = req | |
| print ' <{}> Encoding...'.format(self.proc_id) | |
| reencode(path, dest_path) | |
| print ' <{}> Done.'.format(self.proc_id) | |
| class FfmpegPool(object): | |
| def __init__(self): | |
| self.queue = multiprocessing.Queue(QUEUE_SIZE) | |
| self.procs = [ | |
| multiprocessing.Process(target=FfmpegRunner(self.queue, x).work) | |
| for x in range(NUM_PROCESSES)] | |
| [p.start() for p in self.procs] | |
| def reencode(self, path, dest_path): | |
| self.queue.put((path, dest_path)) | |
| def stop(self): | |
| for _ in range(len(self.procs)): | |
| self.queue.put(STOP_QUEUE) | |
| for proc in self.procs: | |
| proc.join() | |
| @contextlib.contextmanager | |
| def ffmpegPool(): | |
| pool = FfmpegPool() | |
| yield pool | |
| pool.stop() | |
| def mkdirp(path): | |
| if REALLY: | |
| if not os.path.exists(path): | |
| os.makedirs(path) | |
| else: | |
| print "os.makdirs", path | |
| def copy(path, dest_path): | |
| if REALLY: | |
| shutil.copyfile(path, dest_path) | |
| else: | |
| print "copyfile", path, dest_path | |
| def reencode(path, dest_path): | |
| if REALLY: | |
| subprocess.call([FFMPEG, '-i', path, '-q:a', '0', dest_path]) | |
| else: | |
| print "reencode", path, dest_path | |
| def get_files(src): | |
| all_files = [] | |
| for root, dirs, files in os.walk(src): | |
| all_files.extend([ | |
| os.path.join(root, f) | |
| for f in files | |
| if not "DS_Store" in f | |
| and not ':' in f | |
| and not f.endswith('.')]) | |
| return tuple(sorted(all_files)) | |
| def new_path(src, dest, path): | |
| rel = os.path.relpath(path, src) | |
| new = os.path.join(dest, rel) | |
| if new.endswith('.m4a'): | |
| new = new[:-3] + '.mp3' | |
| if len(new) > 4096: | |
| print "fail:", new_path(p) | |
| assert False | |
| left = new | |
| while True: | |
| left, right = os.path.split(left) | |
| if len(right) > 260: | |
| print "fail:", right, "in", p | |
| assert False | |
| if not left or not right: | |
| break | |
| return new | |
| def needs_reencode(path): | |
| return path.endswith('.m4a') | |
| def main(): | |
| dirs_made = set() | |
| src, dest = sys.argv[1:3] | |
| print "{} -> {}".format(src, dest) | |
| paths = get_files(src) | |
| num_paths = len(paths) | |
| mapped = [(p, new_path(src, dest, p)) for p in paths] | |
| with ffmpegPool() as pool: | |
| for i, (p, dest_path) in enumerate(mapped): | |
| if os.path.exists(dest_path): | |
| continue | |
| print "[{:4d}/{:4d}] {}\n -> {}".format(i + 1, num_paths, p, dest_path) | |
| dirpath, filename = os.path.split(dest_path) | |
| if dirpath not in dirs_made: | |
| mkdirp(dirpath) | |
| dirs_made.add(dirpath) | |
| if needs_reencode(p): | |
| pool.reencode(p, dest_path) | |
| else: | |
| copy(p, dest_path) | |
| if __name__=="__main__": | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment