Skip to content

Instantly share code, notes, and snippets.

@egordm
Created April 13, 2018 21:03
Show Gist options
  • Save egordm/0345045c15a722493c85fb5217a80d8f to your computer and use it in GitHub Desktop.
Save egordm/0345045c15a722493c85fb5217a80d8f to your computer and use it in GitHub Desktop.
Converting all flacs and copying all meta. All folders & files are mirrored including images. Run like this `python drop-the-bytes-music.py "D:\Library\Music" "D:\Library\PhoneMusic" -v -j 4`
import argparse
from mutagen import File
from mutagen.flac import FLAC
from mutagen.easyid3 import EasyID3
import os
import shutil
from subprocess import call
from multiprocessing import Pool
from mutagen.id3 import ID3NoHeaderError
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('source_dir', metavar='source_dir', type=str, help='Path to a directory full of flacs.')
parser.add_argument('dest_dir', metavar='dest_dir', type=str, help='Path where you want your mp3\'s to be dropped.')
parser.add_argument('-j', '--threads', type=int, default=os.cpu_count(), help='How much threads do you want to run.')
parser.add_argument('-v', '--verbose', action="store_true", help='Display the resulting set of tags when done.')
args = parser.parse_args()
sync_folder(args.source_dir, args.dest_dir, args.threads, args.verbose)
def sync_folder(source, destination, verbose=False, threads=os.cpu_count(), level=0):
if not os.path.exists(destination): os.makedirs(destination, exist_ok=True)
files = os.listdir(source)
convert_queue = []
for file in files:
file_source = os.path.join(source, file)
file_dest = os.path.join(destination, file)
if os.path.exists(file_dest) and not os.path.isdir(file_source):
if verbose: print(('\t' * level) + f'File: {file} already exists. Skipping')
continue
if os.path.isdir(file_source):
if verbose: print(('\t' * level) + f'Syncing folder: {file}')
sync_folder(file_source, file_dest, verbose, threads, level + 1)
elif file_source.endswith('.flac'):
pre, ext = os.path.splitext(file_dest)
file_dest = pre + '.mp3'
if os.path.exists(file_dest): continue
if verbose: print(('\t' * level) + f'Converting file: {file}')
convert_queue.append(tuple([file_source, file_dest]))
else:
if verbose: print(('\t' * level) + f'Copying file: {file}')
shutil.copy2(file_source, file_dest)
if verbose: print(('\t' * level) + f'Starting convert jobs:')
pool = Pool(threads)
pool.starmap(convert_flac, convert_queue)
def convert_flac(source, destination):
try:
call(f'flac --silent --decode --stdout "{source}" | lame --quiet --preset extreme - "{destination}"', shell=True)
copy_id3(source, destination)
except Exception as e: print(f'Failed converting: {source}')
def copy_id3(srcname, destname, verbose=False):
try:
meta = EasyID3(destname)
except ID3NoHeaderError:
meta = File(destname, easy=True)
meta.add_tags()
meta.save()
src = FLAC(srcname)
dest = EasyID3(destname)
for tag in src:
if tag in EasyID3.valid_keys.keys(): dest[tag] = src[tag]
dest.save()
if verbose:
print("Copied ID3 tags from %(srcname)r to %(destname)r--result:" % locals())
print(dest.pprint())
if __name__ == "__main__":
main()
@egordm
Copy link
Author

egordm commented Apr 13, 2018

python drop-the-bytes-music.py "D:\Library\Music" "D:\Library\PhoneMusic" -v -j 4
Source Destination Verbose ThreadCount

@egordm
Copy link
Author

egordm commented Apr 13, 2018

FLAC and LAME are required btw

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment