mirror of
https://github.com/rembo10/headphones.git
synced 2026-05-19 10:05:30 +01:00
Merge branch 'master' into misc
This commit is contained in:
@@ -25,6 +25,9 @@
|
||||
<%def name="body()">
|
||||
<div id="paddingheader">
|
||||
<h1>${artist['ArtistName']}<h1>
|
||||
%if artist['Status'] == 'Loading':
|
||||
<h3><i>(Album information for this artist is currently being loaded)</i></h3>
|
||||
%endif
|
||||
</div>
|
||||
<form action="markAlbums" method="get"><input type="hidden" name="ArtistID" value=${artist['ArtistID']}>
|
||||
<p class="indented">Mark selected albums as
|
||||
|
||||
@@ -25,6 +25,9 @@
|
||||
<%def name="body()">
|
||||
<div id="paddingheader">
|
||||
<h1>${artist['ArtistName']}<h1>
|
||||
%if artist['Status'] == 'Loading':
|
||||
<h3><i>(Album information for this artist is currently being loaded)</i></h3>
|
||||
%endif
|
||||
</div>
|
||||
<form action="markAlbums" method="get"><input type="hidden" name="ArtistID" value=${artist['ArtistID']}>
|
||||
<p class="indented">Mark selected albums as
|
||||
|
||||
@@ -120,7 +120,8 @@ BITRATE = None
|
||||
SAMPLINGFREQUENCY = None
|
||||
ADVANCEDENCODER = None
|
||||
ENCODEROUTPUTFORMAT = None
|
||||
VORBISQUALITY = None
|
||||
ENCODERQUALITY = None
|
||||
ENCODERVBRCBR = None
|
||||
|
||||
def CheckSection(sec):
|
||||
""" Check if INI section exists, if not create it """
|
||||
@@ -180,7 +181,7 @@ def initialize():
|
||||
LIBRARYSCAN_INTERVAL, DOWNLOAD_SCAN_INTERVAL, SAB_HOST, SAB_USERNAME, SAB_PASSWORD, SAB_APIKEY, SAB_CATEGORY, \
|
||||
NZBMATRIX, NZBMATRIX_USERNAME, NZBMATRIX_APIKEY, NEWZNAB, NEWZNAB_HOST, NEWZNAB_APIKEY, \
|
||||
NZBSORG, NZBSORG_UID, NZBSORG_HASH, NEWZBIN, NEWZBIN_UID, NEWZBIN_PASSWORD, LASTFM_USERNAME, INTERFACE, FOLDER_PERMISSIONS, \
|
||||
ENCODERFOLDER, ENCODER, BITRATE, SAMPLINGFREQUENCY, ENCODE, ADVANCEDENCODER, ENCODEROUTPUTFORMAT, VORBISQUALITY
|
||||
ENCODERFOLDER, ENCODER, BITRATE, SAMPLINGFREQUENCY, ENCODE, ADVANCEDENCODER, ENCODEROUTPUTFORMAT, ENCODERQUALITY, ENCODERVBRVBR
|
||||
|
||||
if __INITIALIZED__:
|
||||
return False
|
||||
@@ -269,7 +270,8 @@ def initialize():
|
||||
ENCODE = bool(check_setting_int(CFG, 'General', 'encode', 0))
|
||||
ADVANCEDENCODER = check_setting_str(CFG, 'General', 'advancedencoder', '')
|
||||
ENCODEROUTPUTFORMAT = check_setting_str(CFG, 'General', 'encoderoutputformat', 'mp3')
|
||||
VORBISQUALITY = check_setting_int(CFG, 'General', 'vorbisquality', 60)
|
||||
ENCODERQUALITY = check_setting_int(CFG, 'General', 'vorbisquality', 60)
|
||||
ENCODERVBRCBR = check_setting_str(CFG, 'General', 'encodervbrcbr', 'cbr')
|
||||
|
||||
if not LOG_DIR:
|
||||
LOG_DIR = os.path.join(DATA_DIR, 'logs')
|
||||
@@ -455,8 +457,9 @@ def config_write():
|
||||
new_config['General']['encoderfolder'] = ENCODERFOLDER
|
||||
new_config['General']['advancedencoder'] = ADVANCEDENCODER
|
||||
new_config['General']['encoderoutputformat'] = ENCODEROUTPUTFORMAT
|
||||
new_config['General']['vorbisquality'] = VORBISQUALITY
|
||||
|
||||
new_config['General']['encoderquality'] = ENCODERQUALITY
|
||||
new_config['General']['encodervbrcbr'] = ENCODERVBRCBR
|
||||
|
||||
new_config.write()
|
||||
|
||||
|
||||
|
||||
@@ -13,12 +13,14 @@ except ImportError:
|
||||
import lib.argparse as argparse
|
||||
|
||||
def encode(albumPath):
|
||||
|
||||
tempDirEncode=os.path.join(albumPath,"temp")
|
||||
musicFiles=[]
|
||||
musicFinalFiles=[]
|
||||
musicTempFiles=[]
|
||||
encoder =""
|
||||
startAlbumTime=time.clock()
|
||||
ifencoded=0
|
||||
|
||||
if not os.path.exists(tempDirEncode):
|
||||
os.mkdir(tempDirEncode)
|
||||
else:
|
||||
@@ -30,7 +32,7 @@ def encode(albumPath):
|
||||
for music in f:
|
||||
if any(music.endswith('.' + x) for x in headphones.MEDIA_FORMATS):
|
||||
musicFiles.append(os.path.join(r, music))
|
||||
musicTemp = os.path.splitext(music)[0]+'.'+headphones.ENCODEROUTPUTFORMAT
|
||||
musicTemp = os.path.normpath(os.path.splitext(music)[0]+'.'+headphones.ENCODEROUTPUTFORMAT).encode(headphones.SYS_ENCODING)
|
||||
musicTempFiles.append(os.path.join(tempDirEncode, musicTemp))
|
||||
|
||||
if headphones.ENCODER=='lame':
|
||||
@@ -49,33 +51,46 @@ def encode(albumPath):
|
||||
logger.warn('Music "%s" has bitrate<="%skbit" will not be reencoded' % (music,headphones.BITRATE))
|
||||
else:
|
||||
command(encoder,music,musicTempFiles[i],albumPath)
|
||||
ifencoded=1
|
||||
else:
|
||||
if headphones.ENCODEROUTPUTFORMAT=='ogg':
|
||||
if music.endswith('.ogg'):
|
||||
logger.warn('Can not reencode .ogg music "%s"' % (music))
|
||||
else:
|
||||
command(encoder,music,musicTempFiles[i],albumPath)
|
||||
ifencoded=1
|
||||
elif (headphones.ENCODEROUTPUTFORMAT=='mp3' or headphones.ENCODEROUTPUTFORMAT=='m4a'):
|
||||
if (music.endswith('.'+headphones.ENCODEROUTPUTFORMAT) and (infoMusic.bitrate/1000<=headphones.BITRATE)):
|
||||
logger.warn('Music "%s" has bitrate<="%skbit" will not be reencoded' % (music,headphones.BITRATE))
|
||||
else:
|
||||
command(encoder,music,musicTempFiles[i],albumPath)
|
||||
ifencoded=1
|
||||
i=i+1
|
||||
|
||||
shutil.rmtree(tempDirEncode)
|
||||
time.sleep(1)
|
||||
logger.info('Encoding for folder "%s" is completed' % (albumPath))
|
||||
time.sleep(1)
|
||||
for r,d,f in os.walk(albumPath):
|
||||
for music in f:
|
||||
if any(music.endswith('.' + x) for x in headphones.MEDIA_FORMATS):
|
||||
musicFinalFiles.append(os.path.join(r, music))
|
||||
|
||||
if ifencoded==0:
|
||||
logger.info('Encoding for folder "%s" is not needed' % (albumPath))
|
||||
else:
|
||||
logger.info('Encoding for folder "%s" is completed in %s' % (albumPath,getTimeEncode(startAlbumTime)))
|
||||
|
||||
return musicFinalFiles
|
||||
|
||||
def command(encoder,musicSource,musicDest,albumPath):
|
||||
return_code=1
|
||||
cmd=''
|
||||
startMusicTime=time.clock()
|
||||
if headphones.ENCODER == 'lame':
|
||||
cmd=encoder + ' -h --resample ' + str(headphones.SAMPLINGFREQUENCY) + ' -b ' + str(headphones.BITRATE)
|
||||
cmd=encoder + ' -h'
|
||||
if headphones.ENCODERVBRCBR=='cbr':
|
||||
cmd=cmd+ ' --resample ' + str(headphones.SAMPLINGFREQUENCY) + ' -b ' + str(headphones.BITRATE)
|
||||
elif headphones.ENCODERVBRCBR=='vbr':
|
||||
cmd=cmd+''
|
||||
cmd=cmd+ ' ' + headphones.ADVANCEDENCODER
|
||||
cmd=cmd+ ' "' + musicSource + '"'
|
||||
cmd=cmd+ ' "' + musicDest +'"'
|
||||
@@ -86,10 +101,23 @@ def command(encoder,musicSource,musicDest,albumPath):
|
||||
cmd=cmd+ ' -acodec libvorbis'
|
||||
if headphones.ENCODEROUTPUTFORMAT=='m4a':
|
||||
cmd=cmd+ ' -strict experimental'
|
||||
cmd=cmd+ ' -y -ac 2 -map_metadata 0:0,s0 -vn -ar ' + str(headphones.SAMPLINGFREQUENCY) + ' -ab ' + str(headphones.BITRATE) + 'k'
|
||||
if headphones.ENCODERVBRCBR=='cbr':
|
||||
cmd=cmd+ ' -ar ' + str(headphones.SAMPLINGFREQUENCY) + ' -ab ' + str(headphones.BITRATE) + 'k'
|
||||
elif headphones.ENCODERVBRCBR=='vbr':
|
||||
cmd=cmd+''
|
||||
cmd=cmd+ ' -y -ac 2 -map_metadata 0:0,s0 -vn'
|
||||
cmd=cmd+ ' ' + headphones.ADVANCEDENCODER
|
||||
cmd=cmd+ ' "' + musicDest + '"'
|
||||
return_code = call(cmd, shell=True)
|
||||
if (return_code==0) and (os.path.exists(musicDest)):
|
||||
os.remove(musicSource)
|
||||
shutil.move(musicDest,albumPath)
|
||||
shutil.move(musicDest,albumPath)
|
||||
logger.info('Music "%s" encoded in %s' % (musicSource,getTimeEncode(startMusicTime)))
|
||||
|
||||
def getTimeEncode(start):
|
||||
seconds =int(time.clock()-start)
|
||||
hours = seconds / 3600
|
||||
seconds -= 3600*hours
|
||||
minutes = seconds / 60
|
||||
seconds -= 60*minutes
|
||||
return "%02d:%02d:%02d" % (hours, minutes, seconds)
|
||||
@@ -121,6 +121,17 @@ def cleanName(string):
|
||||
|
||||
return out_string
|
||||
|
||||
def cleanTitle(title):
|
||||
|
||||
title = re.sub('[\.\-\/\_]', ' ', title).lower()
|
||||
|
||||
# Strip out extra whitespace
|
||||
title = ' '.join(title.split())
|
||||
|
||||
title = title.title()
|
||||
|
||||
return title
|
||||
|
||||
def extract_data(s):
|
||||
|
||||
from headphones import logger
|
||||
|
||||
@@ -8,15 +8,6 @@ from headphones import db, logger, helpers, importer
|
||||
|
||||
def libraryScan(dir=None):
|
||||
|
||||
myDB = db.DBConnection()
|
||||
|
||||
# Clean up bad filepaths
|
||||
tracks = myDB.select('SELECT Location, TrackID from tracks WHERE Location IS NOT NULL')
|
||||
|
||||
for track in tracks:
|
||||
if not os.path.isfile(track['Location'].encode(headphones.SYS_ENCODING)):
|
||||
myDB.action('UPDATE tracks SET Location=? WHERE TrackID=?', [None, track['TrackID']])
|
||||
|
||||
if not dir:
|
||||
dir = headphones.MUSIC_DIR
|
||||
|
||||
@@ -24,6 +15,19 @@ def libraryScan(dir=None):
|
||||
dir = str(dir)
|
||||
except UnicodeEncodeError:
|
||||
dir = unicode(dir).encode('unicode_escape')
|
||||
|
||||
if not os.path.isdir(dir):
|
||||
logger.warn('Cannot find directory: %s. Not scanning' % dir)
|
||||
return
|
||||
|
||||
myDB = db.DBConnection()
|
||||
|
||||
# Clean up bad filepaths
|
||||
tracks = myDB.select('SELECT Location, TrackID from tracks WHERE Location IS NOT NULL')
|
||||
|
||||
for track in tracks:
|
||||
if not os.path.isfile(track['Location'].encode(headphones.SYS_ENCODING)):
|
||||
myDB.action('UPDATE tracks SET Location=?, BitRate=? WHERE TrackID=?', [None, None, track['TrackID']])
|
||||
|
||||
logger.info('Scanning music directory: %s' % dir)
|
||||
|
||||
@@ -114,12 +118,14 @@ def libraryScan(dir=None):
|
||||
firstchar = '0-9'
|
||||
else:
|
||||
firstchar = sortname[0]
|
||||
|
||||
lowerfirst = firstchar.lower()
|
||||
|
||||
|
||||
albumvalues = { 'artist': artist,
|
||||
'album': album,
|
||||
'year': year,
|
||||
'first': firstchar,
|
||||
'lowerfirst': lowerfirst
|
||||
}
|
||||
|
||||
|
||||
@@ -145,16 +151,18 @@ def libraryScan(dir=None):
|
||||
|
||||
new_file_name = new_file_name.replace('?','_').replace(':', '_')
|
||||
|
||||
full_path_to_file = os.path.normpath(os.path.join(headphones.MUSIC_DIR, folder, new_file_name))
|
||||
full_path_to_file = os.path.normpath(os.path.join(headphones.MUSIC_DIR, folder, new_file_name)).encode(headphones.SYS_ENCODING, 'replace')
|
||||
|
||||
match = glob.glob(full_path_to_file)
|
||||
|
||||
if match:
|
||||
|
||||
logger.info('Found a match: %s. Writing MBID to metadata' % match[0])
|
||||
|
||||
unipath = unicode(match[0], headphones.SYS_ENCODING, errors='replace')
|
||||
|
||||
myDB.action('UPDATE tracks SET Location=? WHERE TrackID=?', [match[0], track['TrackID']])
|
||||
myDB.action('DELETE from have WHERE Location=?', [match[0]])
|
||||
myDB.action('UPDATE tracks SET Location=? WHERE TrackID=?', [unipath, track['TrackID']])
|
||||
myDB.action('DELETE from have WHERE Location=?', [unipath])
|
||||
|
||||
# Try to insert the appropriate track id so we don't have to keep doing this
|
||||
try:
|
||||
|
||||
@@ -7,8 +7,8 @@ from headphones import logger
|
||||
|
||||
def getLyrics(artist, song):
|
||||
|
||||
params = { "artist": artist,
|
||||
"song": song,
|
||||
params = { "artist": artist.encode('utf-8'),
|
||||
"song": song.encode('utf-8'),
|
||||
"fmt": 'xml'
|
||||
}
|
||||
|
||||
@@ -43,7 +43,12 @@ def getLyrics(artist, song):
|
||||
m = re.compile('''<div class='lyricbox'><div class='rtMatcher'>.*?</div>(.*?)<!--''').search(lyricspage)
|
||||
|
||||
if not m:
|
||||
return
|
||||
m = re.compile('''<div class='lyricbox'><span style="padding:1em"><a href="/Category:Instrumental" title="Instrumental">''').search(lyricspage)
|
||||
if m:
|
||||
return u'(Instrumental)'
|
||||
else:
|
||||
logger.warn('Cannot find lyrics on: %s' % lyricsurl)
|
||||
return
|
||||
|
||||
lyrics = convert_html_entities(m.group(1)).replace('<br />', '\n')
|
||||
lyrics = re.sub('<.*?>', '', lyrics)
|
||||
|
||||
@@ -453,7 +453,7 @@ def findAlbumID(artist=None, album=None):
|
||||
try:
|
||||
results = q.getReleaseGroups(f)
|
||||
break
|
||||
except WebServiceError, e:
|
||||
except Exception, e:
|
||||
logger.warn('Attempt to query MusicBrainz for %s - %s failed: %s. Sleeping 5 seconds.' % (artist, album, e))
|
||||
attempt += 1
|
||||
time.sleep(5)
|
||||
|
||||
@@ -18,7 +18,7 @@ def checkFolder():
|
||||
|
||||
if album['FolderName']:
|
||||
|
||||
album_path = os.path.join(headphones.DOWNLOAD_DIR, album['FolderName'])
|
||||
album_path = os.path.join(headphones.DOWNLOAD_DIR, album['FolderName']).encode(headphones.SYS_ENCODING)
|
||||
|
||||
if os.path.exists(album_path):
|
||||
logger.debug('Found %s. Verifying....' % album['FolderName'])
|
||||
@@ -36,13 +36,17 @@ def verify(albumid, albumpath):
|
||||
#TODO: This should be a call to a class method.. copied it out of importer with only minor changes
|
||||
#TODO: odd things can happen when there are diacritic characters in the folder name, need to translate them?
|
||||
import mb
|
||||
|
||||
release_dict = None
|
||||
|
||||
try:
|
||||
release_dict = mb.getReleaseGroup(albumid)
|
||||
except Exception, e:
|
||||
logger.info('Unable to get release information for manual album with rgid: ' + albumid)
|
||||
|
||||
logger.info('Unable to get release information for manual album with rgid: %s. Error: %s' % (albumid, e))
|
||||
return
|
||||
|
||||
if not release_dict:
|
||||
logger.warn('Unable to get release information for manual album with rgid: ' + albumid)
|
||||
logger.info('Unable to get release information for manual album with rgid: %s' % albumid)
|
||||
return
|
||||
|
||||
logger.info(u"Now adding/updating artist: " + release_dict['artist_name'])
|
||||
@@ -110,8 +114,6 @@ def verify(albumid, albumpath):
|
||||
release = myDB.action('SELECT * from albums WHERE AlbumID=?', [albumid]).fetchone()
|
||||
tracks = myDB.select('SELECT * from tracks WHERE AlbumID=?', [albumid])
|
||||
|
||||
albumpath = albumpath.encode(headphones.SYS_ENCODING)
|
||||
|
||||
downloaded_track_list = []
|
||||
|
||||
for r,d,f in os.walk(albumpath):
|
||||
@@ -302,12 +304,15 @@ def moveFiles(albumpath, release, tracks):
|
||||
firstchar = '0-9'
|
||||
else:
|
||||
firstchar = sortname[0]
|
||||
|
||||
lowerfirst = firstchar.lower()
|
||||
|
||||
|
||||
values = { 'artist': artist,
|
||||
'album': album,
|
||||
'year': year,
|
||||
'first': firstchar,
|
||||
'lowerfirst': lowerfirst
|
||||
}
|
||||
|
||||
|
||||
@@ -429,21 +434,27 @@ def renameFiles(albumpath, downloaded_track_list, release):
|
||||
tracknumber = '%02d' % f.track
|
||||
|
||||
if not f.title:
|
||||
basename = os.path.basename(downloaded_track)
|
||||
|
||||
basename = unicode(os.path.basename(downloaded_track), headphones.SYS_ENCODING, errors='replace')
|
||||
title = os.path.splitext(basename)[0]
|
||||
ext = os.path.splitext(basename)[1]
|
||||
|
||||
new_file_name = helpers.cleanTitle(title) + ext
|
||||
|
||||
else:
|
||||
title = f.title
|
||||
|
||||
values = { 'tracknumber': tracknumber,
|
||||
'title': title,
|
||||
'artist': release['ArtistName'],
|
||||
'album': release['AlbumTitle'],
|
||||
'year': year
|
||||
}
|
||||
|
||||
ext = os.path.splitext(downloaded_track)[1]
|
||||
values = { 'tracknumber': tracknumber,
|
||||
'title': title,
|
||||
'artist': release['ArtistName'],
|
||||
'album': release['AlbumTitle'],
|
||||
'year': year
|
||||
}
|
||||
|
||||
ext = os.path.splitext(downloaded_track)[1]
|
||||
|
||||
new_file_name = helpers.replace_all(headphones.FILE_FORMAT, values).replace('/','_') + ext
|
||||
|
||||
new_file_name = helpers.replace_all(headphones.FILE_FORMAT, values).replace('/','_') + ext
|
||||
|
||||
new_file_name = new_file_name.replace('?','_').replace(':', '_').encode(headphones.SYS_ENCODING)
|
||||
|
||||
@@ -508,14 +519,14 @@ def forcePostProcess():
|
||||
logger.error('No DOWNLOAD_DIR has been set. Set "Music Download Directory:" to your SAB download directory on the settings page.')
|
||||
return
|
||||
else:
|
||||
download_dir = headphones.DOWNLOAD_DIR
|
||||
download_dir = headphones.DOWNLOAD_DIR.encode('utf-8')
|
||||
|
||||
logger.info('Checking to see if there are any folders to process in download_dir: %s' % download_dir)
|
||||
# Get a list of folders in the download_dir
|
||||
folders = [d for d in os.listdir(download_dir) if os.path.isdir(os.path.join(download_dir, d))]
|
||||
|
||||
if len(folders):
|
||||
logger.info('Found %i folders: %s' % (len(folders), str(folders)))
|
||||
logger.info('Found %i folders to process' % len(folders))
|
||||
pass
|
||||
else:
|
||||
logger.info('Found no folders to process in: %s' % download_dir)
|
||||
@@ -525,6 +536,9 @@ def forcePostProcess():
|
||||
for folder in folders:
|
||||
|
||||
albumpath = os.path.join(download_dir, folder)
|
||||
folder = unicode(folder, headphones.SYS_ENCODING, errors='replace')
|
||||
|
||||
logger.info('Processing: %s' % folder)
|
||||
|
||||
try:
|
||||
name, album, year = helpers.extract_data(folder)
|
||||
@@ -534,7 +548,7 @@ def forcePostProcess():
|
||||
if name and album and year:
|
||||
|
||||
myDB = db.DBConnection()
|
||||
release = myDB.action('SELECT AlbumID, ArtistName, AlbumTitle from albums WHERE ArtistName=? and AlbumTitle=?', [name, album]).fetchone()
|
||||
release = myDB.action('SELECT AlbumID, ArtistName, AlbumTitle from albums WHERE ArtistName LIKE ? and AlbumTitle LIKE ?', [name, album]).fetchone()
|
||||
if release:
|
||||
logger.info('Found a match in the database: %s - %s. Verifying to make sure it is the correct album' % (release['ArtistName'], release['AlbumTitle']))
|
||||
verify(release['AlbumID'], albumpath)
|
||||
@@ -542,11 +556,11 @@ def forcePostProcess():
|
||||
logger.info('Querying MusicBrainz for the release group id for: %s - %s' % (name, album))
|
||||
from headphones import mb
|
||||
try:
|
||||
rgid = mb.findAlbumID(name, album)
|
||||
rgid = mb.findAlbumID(helpers.latinToAscii(name), helpers.latinToAscii(album))
|
||||
except:
|
||||
logger.error('Can not get release information for this album')
|
||||
continue
|
||||
if rgid:
|
||||
verify(rgid, albumpath)
|
||||
|
||||
|
||||
else:
|
||||
logger.info('No match found on MusicBrainz for: %s - %s' % (name, album))
|
||||
Reference in New Issue
Block a user