Skip to content

Instantly share code, notes, and snippets.

@jlnwlf
Last active July 16, 2018 12:41
Show Gist options
  • Save jlnwlf/f4a511a16db411c533fef0e9c4307d39 to your computer and use it in GitHub Desktop.
Save jlnwlf/f4a511a16db411c533fef0e9c4307d39 to your computer and use it in GitHub Desktop.
Lower FLAC, MP3 -> MP3 to a target bitrate, and replace the files in dir(s) (requires ffmpeg, multiprocess, Python3, *nix only for now)
import argparse
import hashlib
import logging
import subprocess
from multiprocessing import Pool, log_to_stderr
from os import path, remove, replace, walk
from mutagen.mp3 import MP3
parser = argparse.ArgumentParser(description="Floor bitrate of mp3 (and flac) to mp3 to gain storage space, recursively on DIR")
parser.add_argument('DIR',
nargs='+')
parser.add_argument('--target',
dest="bitrate",
type=int,
default=192,
choices=[64, 128, 192, 256, 320],
help="target kbits/s")
EXTS = ["mp3", "flac", "wav", "m4a"]
logger = log_to_stderr()
logger.setLevel(logging.ERROR)
def work(filepath):
if filepath.lower().endswith("mp3"):
f = MP3(filepath)
bitrate = int(f.info.bitrate / 1000)
if bitrate <= THRESHOLD:
logger.info("%s left untouched (%d kbps)", filepath, bitrate)
return None
ext = "mp3"
else:
ext = filepath.lower().split(".")[-1]
h = hashlib.sha256(filepath.encode("utf-8")).hexdigest()
tmp_filepath = "/tmp/%s.mp3" % h
command = ["ffmpeg",
"-i",
filepath,
"-ab",
"%dk" % THRESHOLD,
"-map_metadata",
"0",
"-id3v2_version",
"3",
tmp_filepath,
"-y"]
output = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
if ext == "mp3":
replace(tmp_filepath, filepath)
logger.info("Converted %s !", filepath)
else:
new_filepath = path.splitext(filepath)[0] + ".mp3"
replace(tmp_filepath, new_filepath)
remove(filepath) # Remove the original flac file
logger.info("Converted %s to %s and removed the original !", filepath, new_filepath)
return (filepath, output)
if __name__ == '__main__':
args = vars(parser.parse_args())
THRESHOLD = args['bitrate']
files_input = []
for folder in args['DIR']:
for r, _, files in walk(folder):
for filename in files:
if any([filename.lower().endswith(ext) for ext in EXTS]):
full_path = path.join(r, filename)
files_input.append(full_path)
original_size = len(files_input)
i = original_size
with Pool() as p:
for result in p.imap_unordered(work, files_input):
if result:
filename, result = result
if result.returncode != 0:
logger.error(result.decode("utf-8"))
else:
logger.info("%s treated...", filename)
i -= 1
if i > 0:
logger.info(">> Left to treat %d / %d" % (i, original_size))
logger.info("Done")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment