FLAC to MP3 Conversion
Here is my custom Python script to convert FLAC files to MP3 files.
In addition to the obligatory audio conversion, it also handles tag conversion. Because of this, the external eyeD3 module is required.
Take a look:
#!/usr/bin/python
import subprocess
import eyed3
import os
import os.path
import re
import struct
import getopt
temp_tags = r"/tmp/___TAGS___.utf8"
temp_cover = r"/tmp/___COVER___.jpg"
def apic_from_unicode_to_latin1(filename):
data = open(filename, "rb").read()
match = re.search('APIC(......)\x01image/jpeg\x00\x03\xff\xfe\x00\x00', data)
if match:
header = match.group(1)
# Just inject FIX as picture description to keep same frame size!
old = 'APIC' + header + '\x01image/jpeg\x00\x03\xff\xfe\x00\x00'
new = 'APIC' + header + '\x00image/jpeg\x00\x03FIX\x00'
data = data.replace(old, new)
fh = open(filename, "wb")
fh.write(data)
fh.close()
class TagData(object):
def __init__(self):
self.title = None
self.album = None
self.track_num = None
self.artist = None
self.genre = None
self.date = None
self.album_artist = None
def read_flac_tags(self, filename):
subprocess.call(["metaflac", "--no-utf8-convert", "--export-tags-to=" + temp_tags, filename])
fh = open(temp_tags, "rb")
for line in fh:
match = re.match(r'^TITLE=(.*)', line.decode('UTF-8'), re.IGNORECASE)
if match:
self.title = match.group(1)
match = re.match(r'^ALBUM=(.*)', line.decode('UTF-8'), re.IGNORECASE)
if match:
self.album = match.group(1)
match = re.match(r'^TRACKNUMBER=(\d*)', line.decode('UTF-8'), re.IGNORECASE)
if match:
self.track_num = int(match.group(1))
match = re.match(r'^ARTIST=(.*)', line.decode('UTF-8'), re.IGNORECASE)
if match:
self.artist = match.group(1)
match = re.match(r'^GENRE=(.*)', line.decode('UTF-8'), re.IGNORECASE)
if match:
self.genre = match.group(1)
match = re.match(r'^DATE=(\d*)', line.decode('UTF-8'), re.IGNORECASE)
if match:
self.date = int(match.group(1))
fh.close()
def write_mp3_tags(self, filename):
audiofile = eyed3.load(filename)
audiofile.tag = eyed3.id3.Tag() # Create NEW!
if self.title:
audiofile.tag.title = self.title
if self.album:
audiofile.tag.album = self.album
if self.track_num:
audiofile.tag.track_num = self.track_num
if self.artist:
audiofile.tag.artist = self.artist
if self.genre:
audiofile.tag.genre = self.genre
if self.date:
audiofile.tag.recording_date = self.date # Works if version 2.3
if self.album_artist:
audiofile.tag.album_artist = self.album_artist
audiofile.tag.save(filename, eyed3.id3.ID3_V2_3)
def print_usage(progname):
print "Usage: %s " % (progname)
print "-f <FLAC File>"
print "-a [AlbumArtist]"
print "-c [Cover Image Path]"
print "-n [Album Name Override]"
if __name__ == "__main__":
import sys
tagdata = TagData()
# NOTE: Need to filter out empty strings from sys.argv.
# If not, the parser thinks parsing finishes early.
try:
opts, args = getopt.getopt(filter(None, sys.argv[1:]), "hf:a:c:n:", ["help", "flacfile=", "albumartist=", "cover=", "albumname="])
except getopt.GetoptError as err:
print str(err)
print_usage(sys.argv[0])
sys.exit(1)
print "Opts:", opts
print "Args:", args
filename = None
album_artist = None
cover_override = None
album_name_override = None
for o, a in opts:
if o in ("-h", "--help"):
print_usage(sys.argv[0])
sys.exit(1)
elif o in ("-f", "--flacfile"):
filename = a
print "FLAC File:", filename
elif o in ("-a", "--albumartist"):
album_artist = a
print "AlbumArtist:", album_artist
elif o in ("-c", "--cover"):
cover_override = a
print "Cover Image Path:", cover_override
elif o in ("-n", "--albumname"):
album_name_override = a
print "Album Name Override:", album_name_override
if filename == None:
print_usage(sys.argv[0])
sys.exit(1)
# Set optional AlbumArtist.
if album_artist:
tagdata.album_artist = unicode(album_artist)
# Read old tags from FLAC.
tagdata.read_flac_tags(filename)
# Set optional album name override, AFTER reading default tags.
if album_name_override:
tagdata.album = unicode(album_name_override)
# Decode FLAC, encode MP3.
subprocess.call(["flac", "-d", filename, "-o", filename + ".wav"])
subprocess.call(["lame", "-b", "320", "-h", filename + ".wav", filename + ".mp3"])
# Write new tags to MP3.
tagdata.write_mp3_tags(filename + ".mp3")
# Get cover image from argument, or...
if cover_override:
cover_file = cover_override
else:
# ...attempt to extract cover image from FLAC file...
subprocess.call(["metaflac", "--export-picture-to=" + temp_cover, filename])
cover_file = temp_cover
# ...then apply it.
if os.path.isfile(cover_file):
imagedata = open(cover_file, "rb").read()
if imagedata.startswith("\xff\xd8\xff"):
audiofile = eyed3.load(filename + ".mp3")
audiofile.tag.images.set(3, imagedata, "image/jpeg")
audiofile.tag.save()
else:
print "Warning: Image data is not JPEG."
# iTunes APIC fix, encoding must be changed to latin1.
apic_from_unicode_to_latin1(filename + ".mp3")
# Remove ".flac" part of final MP3 file.
if os.path.isfile(filename + ".mp3"):
os.rename(filename + ".mp3", filename.replace(".flac", "") + ".mp3")
# Remove temporary files.
if os.path.isfile(filename + ".wav"):
os.remove(filename + ".wav")
if os.path.isfile(temp_tags):
os.remove(temp_tags)
if os.path.isfile(temp_cover):
os.remove(temp_cover)