You are not logged in.

#1 2018-10-04 05:48:48

dwidmann
Member
From: Gordonsville, Virginia, US
Registered: 2009-05-27
Posts: 50

transcode_music_collection

#!/usr/bin/env python

import glob
import multiprocessing
import argparse
import os
import subprocess
import taglib


srcpath=destpath=srcextlist=destext=codec=bitrate=force=''
def process_args():
    parser = argparse.ArgumentParser(description='Recompress music library files using ffmpeg. Copies directory tree, file names, most tags, etc. Useful for making a copy of your music library to put on a device.')
    parser.add_argument('--srcpath', type=str, dest='srcpath', default='/mnt/media/wutai/Music/archive/artists/', help="root directory of the original files.")
    parser.add_argument('--destpath',type=str, dest='destpath',default='/mnt/media/wutai/Music/opus_b64/artists/', help="destination directory for transcoded files.")
    parser.add_argument('--srcextlist',type=str, dest='srcextlist',  default='.flac,.opus.ogg,.mp4,.m4a,.mp3,.aac', help="comma separated list of file extensions that should be transcoded - Default is \".flac,.opus.ogg,.mp4,.m4a,.mp3,.aac\"")
    parser.add_argument('--destext', type=str, dest='destext', default='.opus.ogg', help="file extension to use for transcoded files. - Note: ffmpeg uses this to choose the container format, and it should be something that plays nice with the --codec you specify. e.g. Use .m4a or .aac for aac, .ogg for vorbis, .opus.ogg for libopus, etc.\nDefault: .opus.ogg")
    parser.add_argument('--force',   type=bool,dest='force',   default=False, help="pass this argument to force overwrite existing files - (existing files will be skipped (i.e. not transcoded)\nDefault: No. ")
    parser.add_argument('--codec',   type=str, dest='codec',   default='libopus', help="audio codec to use for transcoding. e.g. libopus, libfdk_aac, libvorbis. See man ffmpeg-codecs, section AUDIO ENCODERS, for a full list of possible options.\nDefault: libopus")
    parser.add_argument('--bitrate', type=str, dest='bitrate', default='64k', help="Nominal bitrate of transcoded file, e.g. 64k corresponds to 64kbps. Higher bitrates lead to better quality and larger file size. \nDefault: 64k.")
    parser.add_argument('--quality', type=str, dest='quality', default=-1, help="Quality factor of the file, for codecs that support it (such as vorbis). Else set to -1 and it won't be used. \nDefault: -1.")
    return parser.parse_args()


def compress(filepath):
    global donesize
    global totalsize
    oldpath = filepath
    newpath = oldpath.replace(srcpath,destpath)
    newpath = newpath.replace(os.path.splitext(filepath)[1],destext)
    dirpath = newpath.rsplit('/',1)[0]
    os.makedirs(dirpath,exist_ok=True)
    ot = taglib.File(oldpath).tags
    
    # Form metadata string
    metadata = ""
    for tag in ['GENRE','DATE','TRACKNUMBER','ALBUM','ARTIST','TITLE','LABEL','ALBUMARTIST','ARTISTSORT','COMPOSER','DISCNUMBER','ORIGINALDATE','PERFORMER','PERFORMERS']:
        if tag in ot:
            for item in ot[tag]:
                metadata = "{} -metadata {}=\"{}\"".format(metadata,tag.lower(),item)
    # If file doesn't already exist (or if it does and force overwrite is enabled)
    if not os.path.isfile(newpath) or force==True:
        print ('Converting {0}'.format(oldpath))
        buf = ''
        if quality > 0: # used by vorbis
            buf = '-qscale:a {}'.format(quality)
        else:
            buf = '-b:a {}'.format(bitrate)
        command = 'ffmpeg -loglevel quiet -i \"{src}\" {metadata}  -sn -vn -c:a {codec} {buf} \"{dest}\"'.format(src=oldpath,dest=newpath,codec=codec,metadata=metadata,buf=buf)
        subprocess.call(command,shell=True)
                
    else:
        #pass
        print ('Skipping {0}'.format(oldpath))
    size = os.path.getsize(oldpath)
    
    try:
        with donesize.get_lock():
            donesize.value = donesize.value + size
            print("Percent Complete: {percent:.4%}".format(percent=donesize.value/totalsize.value))
    except:
        print("An exception occurred instead.")
        raise

donesize = None
totalsize = None

def init(adonesize,atotalsize):
    ''' store the counter for later use '''
    global donesize
    global totalsize
    donesize = adonesize
    totalsize = atotalsize

if __name__ == '__main__':
    try:
        args     = process_args()
        srcpath  = args.srcpath
        srcextlist   = args.srcextlist
        destpath = args.destpath
        destext  = args.destext
        force    = args.force
        codec    = args.codec
        bitrate  = args.bitrate
        quality  = args.quality
        biglist = []
        donesize = multiprocessing.Value('L',0)
        totalsize = multiprocessing.Value('L',0)
        print('Creating list.')
        for srcext in srcextlist.split(','):
            biglist.extend(glob.iglob('{}/**/*{}'.format(srcpath,srcext),recursive=True))
        biglist.sort()
        totalsize.value = sum(os.path.getsize(name) for name in biglist)
        print('Getting Started.') 
        pool = multiprocessing.Pool(initializer=init, initargs=(donesize,totalsize,))
        pool.imap(compress,biglist)
        pool.close()
        pool.join()

    except (KeyboardInterrupt, SystemExit):
        raise
    except:
        raise
    finally:
        print("Bye.\n\n")

Something I wrote a while back. Maybe someone else will find it useful too.

Last edited by dwidmann (2018-10-04 06:37:13)


"I refuse to be part of a society that encourages the rampant abuse of its own language." ~ BM

Offline

#2 2018-10-04 05:54:29

ugjka
Member
From: Latvia
Registered: 2014-04-01
Posts: 1,169

Re: transcode_music_collection

that should go to this thread https://bbs.archlinux.org/viewtopic.php?id=56646

if you gonna post in Community Contributions at least have same basic documentation


Github | Soundcloud

"Once you hit 10 nested VMs you're basically brad pitt with women who matter" ~ echoline

Offline

Board footer

Powered by FluxBB