#!/usr/bin/python # Copyright Rob Fell, 2007. import sys import string import os import os.path import string import shutil import tempfile from subprocess import * best = False def GetFlacInfo( meta, f ): cmd = "metaflac --show-sample-rate --show-tag=artist --show-tag=album --show-tag=title --show-tag=tracknumber --show-tag=date %s" p = Popen( cmd % f, shell=True, stdout=PIPE, stderr=PIPE ) artist = album = year = song = tracknum = rate = None for field in p.communicate()[0].split('\n'): sep = field.find('=') if sep > 0: key = field[0:sep].lower().lstrip() value = field[sep+1:] if key == 'artist': artist = value if key == 'album': album = value if key == 'title': song = value if key == 'tracknumber': tracknum = int( value ) if key == 'date': year = value else: str = "" for ch in field.lstrip(): if ch.isdigit(): str += ch else: break if str: rate = int( str ) if song: meta['song'].append( song ) if artist: meta['artist'].append( artist ) if album: meta['album'].append( album ) if tracknum: meta['tracknum'].append( tracknum ) if year: meta['date'].append( year ) if rate: meta['rate'].append( rate ) def GetOggInfo( meta, f ): p = Popen( "ogginfo %s" % f, shell=True, stdout=PIPE ) artist = album = year = song = tracknum = rate = None for field in p.communicate()[0].split('\n'): sep = field.find('=') if sep > 0: key = field[0:sep].lower().lstrip() value = field[sep+1:] if key == 'artist': artist = value if key == 'album': album = value if key == 'title': song = value if key == 'tracknumber': tracknum = int( value ) if key == 'date': year = value else: if 'Rate:' in field: rate = int( field.split(' ')[1] ) if song: meta['song'].append( song ) if artist: meta['artist'].append( artist ) if album: meta['album'].append( album ) if tracknum: meta['tracknum'].append( tracknum ) if year: meta['date'].append( year ) if rate: meta['rate'].append( rate ) def GetID3Info( meta, f ): p = Popen( "id3info %s" % f, shell=True, stdout=PIPE ) artist = album = year = song = tracknum = rate = None for field in p.communicate()[0].split('\n'): if 'Frequency' in field: freq = field.split(':')[-1].lstrip().lower() if '44khz' == freq: rate = 44100 elif '48khz' == freq: rate = 48000 if not field[0:3] == '===': continue key = field[4:8] if key == 'TIT2': song = field.split(':')[-1].lstrip() elif key == 'TPE1': artist = field.split(':')[-1].lstrip() elif key == 'TALB': album = field.split(':')[-1].lstrip() elif key == 'TRCK': tracknum = int( field.split(':')[-1] ) elif key == 'TYER': year = int( field.split(':')[-1] ) if song: meta['song'].append( song ) if artist: meta['artist'].append( artist ) if album: meta['album'].append( album ) if tracknum: meta['tracknum'].append( tracknum ) if year: meta['date'].append( year ) if rate: meta['rate'].append( rate ) def GetMetadata( ftype, f ): meta = { 'artist' : [], 'album' : [], 'date' : [], 'tracknum' : [], 'song' : [], 'rate' : [] } if ftype == 'riff': return None if ftype == 'flac': GetFlacInfo( meta, f ) if ftype == 'ogg': GetOggInfo( meta, f ) GetID3Info( meta, f ) if not meta['tracknum']: tracknum = "" for ch in os.path.basename( f ): if ch.isdigit(): tracknum += ch else: break if tracknum: meta['tracknum'].append( int( tracknum )) return meta def DetermineFileType( f ): idtable = [ (('MPEG', 'layer III'), ('mp3')), (('RIFF', 'PCM', 'Microsoft'), ('riff')), (('Ogg', 'Vorbis'), ('ogg')) ] # 'file' misidentifies flac files as MP3's, so we use metaflac # to see if it IS a flac file first # is_flac = Popen( "metaflac --list %s" % f, shell=True, stdout=PIPE, stderr=PIPE ).wait() == 0 if is_flac: ftype = 'flac' else: # now fall back to normal 'file' identification # p = Popen( "file %s" % f, shell=True, stdout=PIPE ) info = p.communicate()[0] ftype = None for rule, tag in idtable: ftype = tag for str in rule: if str not in info: ftype = None break if ftype: break return ftype def Flac2Wav( src, dst ): return Popen( "flac123 --wav=%s %s" % (dst, src), shell=True, stdout=PIPE ).wait() def Wav2Flac( src, dst, meta ): cmd = "flac" args = "" if meta: if meta['song']: args += ' -T "title=%s"' % meta['song'][0] if meta['artist']: args += ' -T "artist=%s"' % meta['artist'][0] if meta['album']: args += ' -T "album=%s"' % meta['album'][0] if meta['date']: args += ' -T "date=%s"' % meta['date'][0] if meta['tracknum']: args += ' -T "tracknumber=%d"' % meta['tracknum'][0] if args: cmd += " %s %s %s" % (args, src, dst) else: cmd += " %s %s" % (src, dst) return Popen( cmd , shell=True, stdout=PIPE ).wait() def Ogg2Wav( src, dst ): return Popen( "oggdec -o %s %s" % (dst, src), shell=True, stdout=PIPE ).wait() def Wav2Ogg( src, dst, meta ): if best: cmd = "oggenc -q 10" else: cmd = "oggenc -q 7" args = "" if meta: if meta['song']: args += ' -t "%s"' % meta['song'][0] if meta['artist']: args += ' -a "%s"' % meta['artist'][0] if meta['album']: args += ' -l "%s"' % meta['album'][0] if meta['date']: args += ' -d "%s"' % meta['date'][0] if meta['tracknum']: args += ' -N %d' % meta['tracknum'][0] if args: cmd += " %s -o %s %s" % (args, dst, src) else: cmd += " -o %s %s" % (dst, src) print cmd return Popen( cmd , shell=True, stdout=PIPE ).wait() def Mp32Wav( src, dst ): return Popen( "mpg123 -w %s %s" % (dst, src), shell=True, stdout=PIPE ).wait() def Wav2Mp3( src, dst, meta ): if best: cmd = "lame -V 0 -h " else: cmd = "lame -V 4 -h" args = "" if meta: if meta['song']: args += ' --tt "%s"' % meta['song'][0] if meta['artist']: args += ' --ta "%s"' % meta['artist'][0] if meta['album']: args += ' --tl "%s"' % meta['album'][0] if meta['date']: args += ' --ty "%s"' % meta['date'][0] if meta['tracknum']: args += ' --tn %d' % meta['tracknum'][0] if args: cmd += " %s %s %s" % (args, src, dst) else: cmd += " %s %s" % (src, dst) return Popen( cmd , shell=True, stdout=PIPE ).wait() def NewName( f, meta, type ): types = { 'riff':'wav', 'flac':'flac', 'ogg':'ogg', 'mp3':'mp3' } if type not in types.keys(): return None if meta and meta['song']: if meta['tracknum']: song = "".join( [ x for x in meta['song'][0].split(' ')] ) name = "%02d%s" % (meta['tracknum'][0], song) trans = string.maketrans( '', '' ) return ".".join( [string.translate( name, trans, ':,\\/.!\"$%^&*()+=-\'_ ' ), types[type]] ) else: name = os.path.splitext( os.path.basename( f ))[0] trans = string.maketrans( '', '' ) return ".".join( [string.translate( name, trans, ':,\\/.!\"$%^&*()+=\' ' ), types[type]] ) def Transcode( src, dst, meta ): srcFmt, srcFile = src dstFmt, dstFile = dst decoders = { 'flac': Flac2Wav, 'ogg' : Ogg2Wav, 'mp3' : Mp32Wav, 'riff': None } encoders = { 'flac': Wav2Flac, 'ogg' : Wav2Ogg, 'mp3' : Wav2Mp3, 'riff': None } if os.path.isfile( dstFile ): raise "refusing to overwrite %s" % dstFile if not os.path.isdir( os.path.dirname( dstFile )): raise "directory %s does not exist" % os.path.dirname( dstFile ) if srcFmt == dstFmt: # only a copy required # print "Copying %s to %s" % ( srcFile, dstFile ) shutil.copyfile( srcFile, dstFile ) else: handle, tempname = tempfile.mkstemp( '.wav', 'pyxcode', '/tmp' ) os.close(handle) decode = decoders[srcFmt] encode = encoders[dstFmt] if not decode: shutil.copyfile( srcFile, tempname ) else: decode( srcFile, tempname ) if not encode: shutil.copyfile( tempname, dstFile ) else: encode( tempname, dstFile, meta ) os.remove(tempname) print "Converted %s" % os.path.basename( srcFile ) def Usage(): print """ exec.py --odir=path --fmt=[flac|wav|riff|ogg|mp3] [--best] file [file...] """ sys.exit(1) if __name__ == "__main__": files = [] odir = None fmt = None args = sys.argv[1:] for arg in args: if arg[0:2] == '--': opt = arg[2:] sep = opt.find('=') if sep > 0: key = opt[0:sep] value = opt[sep+1:] if key == 'odir': odir = value elif key == 'fmt': if value == 'wav': value = 'riff' fmt = value else: Usage() else: if opt == 'best': best = True else: Usage() else: files.append( arg ) if not odir or not fmt: Usage() for f in files: if os.path.isfile( f ): ftype = DetermineFileType( f ) if ftype: meta = GetMetadata( ftype, f ) src = ( ftype, f ) dst = ( fmt, os.path.join( odir, NewName( f, meta, fmt ) ) ) ret = Transcode( src, dst, meta ) if ret: print ret