XLD encoding

+ bonus post processing xld cue splitting.

Currently uses Advanced Encoding Options to determine xld and the xld
profile to use, e.g xld Apple Lossless. Would like to use congig
instead but can't figure out how to do it
This commit is contained in:
Ade
2012-10-25 11:19:18 +13:00
parent 3e2bab2383
commit 608ccb2c0a
3 changed files with 314 additions and 9 deletions

181
headphones/getXldProfile.py Executable file
View File

@@ -0,0 +1,181 @@
import os.path
import plistlib
import sys
import xml.parsers.expat as expat
import commands
from headphones import logger
def getXldProfile(xldProfile):
xldProfileNotFound = xldProfile
expandedPath = os.path.expanduser('~/Library/Preferences/jp.tmkk.XLD.plist')
try:
preferences = plistlib.Plist.fromFile(expandedPath)
except (expat.ExpatError):
os.system("/usr/bin/plutil -convert xml1 %s" % expandedPath )
try:
preferences = plistlib.Plist.fromFile(expandedPath)
except (ImportError):
os.system("/usr/bin/plutil -convert binary1 %s" % expandedPath )
logger.info('The plist at "%s" has a date in it, and therefore is not useable.' % expandedPath)
return(xldProfileNotFound, None, None)
except (ImportError):
logger.info('The plist at "%s" has a date in it, and therefore is not useable.' % expandedPath)
except:
logger.info('Unexpected error:', sys.exc_info()[0])
return(xldProfileNotFound, None, None)
xldProfile = xldProfile.lower()
profiles = preferences.get('Profiles')
for profile in profiles:
profilename = profile.get('XLDProfileManager_ProfileName')
xldProfileForCmd = profilename
profilename = profilename.lower()
xldFormat = None
xldBitrate = None
if profilename == xldProfile:
OutputFormatName = profile.get('OutputFormatName')
ShortDesc = profile.get('ShortDesc')
# Determine format and bitrate
if OutputFormatName == 'WAV':
xldFormat = 'wav'
elif OutputFormatName == 'AIFF':
xldFormat = 'aiff'
elif 'PCM' in OutputFormatName:
xldFormat = 'pcm'
elif OutputFormatName == 'Wave64':
xldFormat = 'w64'
elif OutputFormatName == 'MPEG-4 AAC':
xldFormat = 'm4a'
if 'CBR' in ShortDesc or 'ABR' in ShortDesc or 'CVBR' in ShortDesc:
xldBitrate = int(profile.get('XLDAacOutput2_Bitrate'))
elif 'TVBR' in ShortDesc:
XLDAacOutput2_VBRQuality = int(profile.get('XLDAacOutput2_VBRQuality'))
if XLDAacOutput2_VBRQuality > 122:
xldBitrate = 320
elif XLDAacOutput2_VBRQuality > 113 and XLDAacOutput2_VBRQuality <= 122:
xldBitrate = 285
elif XLDAacOutput2_VBRQuality > 104 and XLDAacOutput2_VBRQuality <= 113:
xldBitrate = 255
elif XLDAacOutput2_VBRQuality > 95 and XLDAacOutput2_VBRQuality <= 104:
xldBitrate = 225
elif XLDAacOutput2_VBRQuality > 86 and XLDAacOutput2_VBRQuality <= 95:
xldBitrate = 195
elif XLDAacOutput2_VBRQuality > 77 and XLDAacOutput2_VBRQuality <= 86:
xldBitrate = 165
elif XLDAacOutput2_VBRQuality > 68 and XLDAacOutput2_VBRQuality <= 77:
xldBitrate = 150
elif XLDAacOutput2_VBRQuality > 58 and XLDAacOutput2_VBRQuality <= 68:
xldBitrate = 135
elif XLDAacOutput2_VBRQuality > 49 and XLDAacOutput2_VBRQuality <= 58:
xldBitrate = 115
elif XLDAacOutput2_VBRQuality > 40 and XLDAacOutput2_VBRQuality <= 49:
xldBitrate = 105
elif XLDAacOutput2_VBRQuality > 31 and XLDAacOutput2_VBRQuality <= 40:
xldBitrate = 95
elif XLDAacOutput2_VBRQuality > 22 and XLDAacOutput2_VBRQuality <= 31:
xldBitrate = 80
elif XLDAacOutput2_VBRQuality > 13 and XLDAacOutput2_VBRQuality <= 22:
xldBitrate = 75
elif XLDAacOutput2_VBRQuality > 4 and XLDAacOutput2_VBRQuality <= 13:
xldBitrate = 45
elif XLDAacOutput2_VBRQuality >= 0 and XLDAacOutput2_VBRQuality <= 4:
xldBitrate = 40
elif OutputFormatName == 'Apple Lossless':
xldFormat = 'm4a'
elif OutputFormatName == 'FLAC':
if 'ogg' in ShortDesc:
xldFormat = 'oga'
else:
xldFormat = 'flac'
elif OutputFormatName == 'MPEG-4 HE-AAC':
xldFormat = 'm4a'
xldBitrate = int(profile.get('Bitrate'))
elif OutputFormatName == 'LAME MP3':
xldFormat = 'mp3'
if 'VBR' in ShortDesc:
VbrQuality = float(profile.get('VbrQuality'))
if VbrQuality < 1:
xldBitrate = 260
elif VbrQuality >= 1 and VbrQuality < 2:
xldBitrate = 250
elif VbrQuality >= 2 and VbrQuality < 3:
xldBitrate = 210
elif VbrQuality >= 3 and VbrQuality < 4:
xldBitrate = 195
elif VbrQuality >= 4 and VbrQuality < 5:
xldBitrate = 185
elif VbrQuality >= 5 and VbrQuality < 6:
xldBitrate = 150
elif VbrQuality >= 6 and VbrQuality < 7:
xldBitrate = 130
elif VbrQuality >= 7 and VbrQuality < 8:
xldBitrate = 120
elif VbrQuality >= 8 and VbrQuality < 9:
xldBitrate = 105
elif VbrQuality >= 9:
xldBitrate = 85
elif 'CBR' in ShortDesc:
xldBitrate = int(profile.get('Bitrate'))
elif 'ABR' in ShortDesc:
xldBitrate = int(profile.get('AbrBitrate'))
elif OutputFormatName == 'Opus':
xldFormat = 'opus'
xldBitrate = int(profile.get('XLDOpusOutput_Bitrate'))
elif OutputFormatName == 'Ogg Vorbis':
xldFormat = 'ogg'
XLDVorbisOutput_Quality = float(profile.get('XLDVorbisOutput_Quality'))
if XLDVorbisOutput_Quality <= -2:
xldBitrate = 32
elif XLDVorbisOutput_Quality > -2 and XLDVorbisOutput_Quality <= -1:
xldBitrate = 48
elif XLDVorbisOutput_Quality > -1 and XLDVorbisOutput_Quality <= 0:
xldBitrate = 64
elif XLDVorbisOutput_Quality > 0 and XLDVorbisOutput_Quality <= 1:
xldBitrate = 80
elif XLDVorbisOutput_Quality > 1 and XLDVorbisOutput_Quality <= 2:
xldBitrate = 96
elif XLDVorbisOutput_Quality > 2 and XLDVorbisOutput_Quality <= 3:
xldBitrate = 112
elif XLDVorbisOutput_Quality > 3 and XLDVorbisOutput_Quality <= 4:
xldBitrate = 128
elif XLDVorbisOutput_Quality > 4 and XLDVorbisOutput_Quality <= 5:
xldBitrate = 160
elif XLDVorbisOutput_Quality > 5 and XLDVorbisOutput_Quality <= 6:
xldBitrate = 192
elif XLDVorbisOutput_Quality > 6 and XLDVorbisOutput_Quality <= 7:
xldBitrate = 224
elif XLDVorbisOutput_Quality > 7 and XLDVorbisOutput_Quality <= 8:
xldBitrate = 256
elif XLDVorbisOutput_Quality > 8 and XLDVorbisOutput_Quality <= 9:
xldBitrate = 320
elif XLDVorbisOutput_Quality > 9:
xldBitrate = 500
elif OutputFormatName == 'WavPack':
xldFormat = 'wv'
if ShortDesc != 'normal':
xldBitrate = int(profile.get('XLDWavpackOutput_BitRate'))
# Lossless
if xldFormat and not xldBitrate:
xldBitrate = 500
return(xldProfileForCmd, xldFormat, xldBitrate)
return(xldProfileNotFound, None, None)

View File

@@ -27,7 +27,26 @@ try:
except ImportError:
import lib.argparse as argparse
# xld
if headphones.ADVANCEDENCODER.lower().startswith('xld'):
XLDPROFILE = headphones.ADVANCEDENCODER[4:]
import getXldProfile
XLD = True
else:
XLD = False
def encode(albumPath):
# Return if xld details not found
if XLD:
global xldProfile
(xldProfile, xldFormat, xldBitrate) = getXldProfile.getXldProfile(XLDPROFILE)
if not xldFormat:
logger.error(u'Details for xld profile "%s" not found, will not be reencoded' % (xldProfile))
return None
tempDirEncode=os.path.join(albumPath,"temp")
musicFiles=[]
musicFinalFiles=[]
@@ -46,26 +65,48 @@ def encode(albumPath):
for r,d,f in os.walk(albumPath):
for music in f:
if any(music.lower().endswith('.' + x.lower()) for x in headphones.MEDIA_FORMATS):
if not XLD:
encoderFormat = headphones.ENCODEROUTPUTFORMAT.encode(headphones.SYS_ENCODING)
else:
xldMusicFile = os.path.join(r, music)
xldInfoMusic = MediaFile(xldMusicFile)
encoderFormat = xldFormat
if (headphones.ENCODERLOSSLESS):
if (music.lower().endswith('.flac')):
ext = os.path.normpath(os.path.splitext(music)[1].lstrip(".")).lower()
if not XLD and ext == 'flac' or XLD and (ext != xldFormat and (xldInfoMusic.bitrate / 1000 > 500)):
musicFiles.append(os.path.join(r, music))
musicTemp = os.path.normpath(os.path.splitext(music)[0]+'.'+headphones.ENCODEROUTPUTFORMAT.encode(headphones.SYS_ENCODING))
musicTemp = os.path.normpath(os.path.splitext(music)[0] + '.' + encoderFormat)
musicTempFiles.append(os.path.join(tempDirEncode, musicTemp))
else:
logger.debug('Music "%s" is already encoded' % (music))
else:
musicFiles.append(os.path.join(r, music))
musicTemp = os.path.normpath(os.path.splitext(music)[0]+'.'+headphones.ENCODEROUTPUTFORMAT.encode(headphones.SYS_ENCODING))
musicTemp = os.path.normpath(os.path.splitext(music)[0] + '.' + encoderFormat)
musicTempFiles.append(os.path.join(tempDirEncode, musicTemp))
if headphones.ENCODER=='lame':
if XLD:
if headphones.ENCODERFOLDER:
encoder = os.path.join(headphones.ENCODERFOLDER.encode(headphones.SYS_ENCODING), 'xld')
else:
encoder = os.path.join('/Applications', 'xld')
elif headphones.ENCODER=='lame':
encoder=os.path.join(headphones.ENCODERFOLDER.encode(headphones.SYS_ENCODING),'lame')
elif headphones.ENCODER=='ffmpeg':
encoder=os.path.join(headphones.ENCODERFOLDER.encode(headphones.SYS_ENCODING),'ffmpeg')
i=0
for music in musicFiles:
infoMusic=MediaFile(music)
if headphones.ENCODER == 'lame':
if XLD:
if xldBitrate and (infoMusic.bitrate / 1000 <= xldBitrate):
logger.info('Music "%s" has bitrate <= "%skbit", will not be reencoded' % (music.decode(headphones.SYS_ENCODING, 'replace'), xldBitrate))
else:
command(encoder,music,musicTempFiles[i],albumPath)
ifencoded=1
elif headphones.ENCODER == 'lame':
if not any(music.decode(headphones.SYS_ENCODING, 'replace').lower().endswith('.' + x) for x in ["mp3", "wav"]):
logger.warn(u'Lame cant encode "%s" format for "%s", use ffmpeg' % (os.path.splitext(music)[1].decode(headphones.SYS_ENCODING, 'replace'),music.decode(headphones.SYS_ENCODING, 'replace')))
else:
@@ -106,7 +147,17 @@ def command(encoder,musicSource,musicDest,albumPath):
return_code=1
cmd=''
startMusicTime=time.time()
if headphones.ENCODER == 'lame':
if XLD:
xldDestDir = os.path.split(musicDest)[0]
cmd = encoder
cmd = cmd + ' "' + musicSource + '"'
cmd = cmd + ' --profile'
cmd = cmd + ' "' + xldProfile + '"'
cmd = cmd + ' -o'
cmd = cmd + ' "' + xldDestDir + '"'
elif headphones.ENCODER == 'lame':
if headphones.ADVANCEDENCODER =='':
cmd=encoder + ' -h'
if headphones.ENCODERVBRCBR=='cbr':

View File

@@ -28,6 +28,15 @@ from lib.beets.mediafile import MediaFile
import headphones
from headphones import db, albumart, librarysync, lyrics, logger, helpers
# xld
if headphones.ADVANCEDENCODER.lower().startswith('xld'):
XLDPROFILE = headphones.ADVANCEDENCODER[4:]
import getXldProfile
XLD = True
else:
XLD = False
postprocessor_lock = threading.Lock()
def checkFolder():
@@ -147,12 +156,73 @@ def verify(albumid, albumpath):
tracks = myDB.select('SELECT * from tracks WHERE AlbumID=?', [albumid])
downloaded_track_list = []
downloaded_cuecount = 0
for r,d,f in os.walk(albumpath):
for files in f:
if any(files.lower().endswith('.' + x.lower()) for x in headphones.MEDIA_FORMATS):
downloaded_track_list.append(os.path.join(r, files))
elif files.lower().endswith('.cue'):
downloaded_cuecount += 1
# use xld to split cue
if XLD and headphones.MUSIC_ENCODER and downloaded_cuecount and downloaded_cuecount >= len(downloaded_track_list):
(xldProfile, xldFormat, xldBitrate) = getXldProfile.getXldProfile(XLDPROFILE)
if not xldFormat:
logger.info(u'Details for xld profile "%s" not found, cannot split cue' % (xldProfile))
else:
if headphones.ENCODERFOLDER:
xldencoder = os.path.join(headphones.ENCODERFOLDER, 'xld')
else:
xldencoder = os.path.join('/Applications','xld')
for r,d,f in os.walk(albumpath):
xldfolder = r
xldfile = ''
xldcue = ''
for file in f:
if any(file.lower().endswith('.' + x.lower()) for x in headphones.MEDIA_FORMATS) and not xldfile:
xldfile = os.path.join(r, file)
elif file.lower().endswith('.cue') and not xldcue:
xldcue = os.path.join(r, file)
if xldfile and xldcue and xldfolder:
xldcmd = xldencoder
xldcmd = xldcmd + ' "' + xldfile + '"'
xldcmd = xldcmd + ' -c'
xldcmd = xldcmd + ' "' + xldcue + '"'
xldcmd = xldcmd + ' --profile'
xldcmd = xldcmd + ' "' + xldProfile + '"'
xldcmd = xldcmd + ' -o'
xldcmd = xldcmd + ' "' + xldfolder + '"'
logger.info(u"Cue found, splitting file " + xldfile.decode(headphones.SYS_ENCODING, 'replace'))
logger.debug(xldcmd)
os.system(xldcmd)
# count files, should now be more than original if xld successfully split
new_downloaded_track_list_count = 0
for r,d,f in os.walk(albumpath):
for file in f:
if any(file.lower().endswith('.' + x.lower()) for x in headphones.MEDIA_FORMATS):
new_downloaded_track_list_count += 1
if new_downloaded_track_list_count > len(downloaded_track_list):
# rename original unsplit files
for downloaded_track in downloaded_track_list:
os.rename(downloaded_track, downloaded_track + '.original')
#reload
downloaded_track_list = []
for r,d,f in os.walk(albumpath):
for file in f:
if any(file.lower().endswith('.' + x.lower()) for x in headphones.MEDIA_FORMATS):
downloaded_track_list.append(os.path.join(r, file))
# test #1: metadata - usually works
logger.debug('Verifying metadata...')
@@ -233,9 +303,12 @@ def verify(albumid, albumpath):
def doPostProcessing(albumid, albumpath, release, tracks, downloaded_track_list):
logger.info('Starting post-processing for: %s - %s' % (release['ArtistName'], release['AlbumTitle']))
#start enconding
#start encoding
if headphones.MUSIC_ENCODER:
downloaded_track_list=music_encoder.encode(albumpath)
if not downloaded_track_list:
return
album_art_path = albumart.getAlbumArt(albumid)