Skip to content

Instantly share code, notes, and snippets.

@sixohsix
Last active March 27, 2016 09:22
Show Gist options
  • Select an option

  • Save sixohsix/e1e9b3a7b51ae02b3579 to your computer and use it in GitHub Desktop.

Select an option

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
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