From a3f6d90f74c65f4061fc76c41f1218fc8b24be11 Mon Sep 17 00:00:00 2001 From: rembo10 Date: Tue, 19 Jun 2012 12:46:44 +0530 Subject: [PATCH] CoSome general cleanup. Converted all tabs to spaces for consistency, removed templates.py as it was no longer being used --- headphones/__init__.py | 74 +-- headphones/albumart.py | 12 +- headphones/api.py | 568 ++++++++-------- headphones/db.py | 126 ++-- headphones/helpers.py | 214 +++--- headphones/importer.py | 684 ++++++++++---------- headphones/lastfm.py | 326 +++++----- headphones/librarysync.py | 374 +++++------ headphones/logger.py | 112 ++-- headphones/lyrics.py | 92 +-- headphones/mb.py | 6 +- headphones/music_encoder.py | 238 +++---- headphones/notifiers.py | 202 +++--- headphones/postprocessor.py | 1218 +++++++++++++++++------------------ headphones/sab.py | 2 +- headphones/searcher.py | 505 ++++++++------- headphones/templates.py | 440 ------------- headphones/updater.py | 20 +- headphones/versioncheck.py | 390 +++++------ headphones/webserve.py | 1090 +++++++++++++++---------------- headphones/webstart.py | 68 +- 21 files changed, 3160 insertions(+), 3601 deletions(-) delete mode 100644 headphones/templates.py diff --git a/headphones/__init__.py b/headphones/__init__.py index 606f14f8..793b92c0 100644 --- a/headphones/__init__.py +++ b/headphones/__init__.py @@ -325,7 +325,7 @@ def initialize(): INTERFACE = check_setting_str(CFG, 'General', 'interface', 'default') FOLDER_PERMISSIONS = check_setting_str(CFG, 'General', 'folder_permissions', '0755') - + ENCODERFOLDER = check_setting_str(CFG, 'General', 'encoderfolder', '') ENCODER = check_setting_str(CFG, 'General', 'encoder', 'ffmpeg') BITRATE = check_setting_int(CFG, 'General', 'bitrate', 192) @@ -363,7 +363,7 @@ def initialize(): # update folder formats in the config & bump up config version if CONFIG_VERSION == '0': from headphones.helpers import replace_all - file_values = { 'tracknumber': 'Track', 'title': 'Title','artist' : 'Artist', 'album' : 'Album', 'year' : 'Year' } + file_values = { 'tracknumber': 'Track', 'title': 'Title','artist' : 'Artist', 'album' : 'Album', 'year' : 'Year' } folder_values = { 'artist' : 'Artist', 'album':'Album', 'year' : 'Year', 'releasetype' : 'Type', 'first' : 'First', 'lowerfirst' : 'first' } FILE_FORMAT = replace_all(FILE_FORMAT, file_values) FOLDER_FORMAT = replace_all(FOLDER_FORMAT, folder_values) @@ -372,34 +372,34 @@ def initialize(): if CONFIG_VERSION == '1': - from headphones.helpers import replace_all + from headphones.helpers import replace_all - file_values = { 'Track': '$Track', - 'Title': '$Title', - 'Artist': '$Artist', - 'Album': '$Album', - 'Year': '$Year', - 'track': '$track', - 'title': '$title', - 'artist': '$artist', - 'album': '$album', - 'year': '$year' - } - folder_values = { 'Artist': '$Artist', - 'Album': '$Album', - 'Year': '$Year', - 'Type': '$Type', - 'First': '$First', - 'artist': '$artist', - 'album': '$album', - 'year': '$year', - 'type': '$type', - 'first': '$first' - } - FILE_FORMAT = replace_all(FILE_FORMAT, file_values) - FOLDER_FORMAT = replace_all(FOLDER_FORMAT, folder_values) - - CONFIG_VERSION = '2' + file_values = { 'Track': '$Track', + 'Title': '$Title', + 'Artist': '$Artist', + 'Album': '$Album', + 'Year': '$Year', + 'track': '$track', + 'title': '$title', + 'artist': '$artist', + 'album': '$album', + 'year': '$year' + } + folder_values = { 'Artist': '$Artist', + 'Album': '$Album', + 'Year': '$Year', + 'Type': '$Type', + 'First': '$First', + 'artist': '$artist', + 'album': '$album', + 'year': '$year', + 'type': '$type', + 'first': '$first' + } + FILE_FORMAT = replace_all(FILE_FORMAT, file_values) + FOLDER_FORMAT = replace_all(FOLDER_FORMAT, folder_values) + + CONFIG_VERSION = '2' if not LOG_DIR: LOG_DIR = os.path.join(DATA_DIR, 'logs') @@ -435,12 +435,12 @@ def initialize(): # Check for new versions if CHECK_GITHUB_ON_STARTUP: - try: - LATEST_VERSION = versioncheck.checkGithub() - except: - LATEST_VERSION = CURRENT_VERSION + try: + LATEST_VERSION = versioncheck.checkGithub() + except: + LATEST_VERSION = CURRENT_VERSION else: - LATEST_VERSION = CURRENT_VERSION + LATEST_VERSION = CURRENT_VERSION __INITIALIZED__ = True return True @@ -766,9 +766,9 @@ def shutdown(restart=False, update=False): config_write() if not restart and not update: - logger.info('Headphones is shutting down...') + logger.info('Headphones is shutting down...') if update: - logger.info('Headphones is updating...') + logger.info('Headphones is updating...') try: versioncheck.update() except Exception, e: @@ -779,7 +779,7 @@ def shutdown(restart=False, update=False): os.remove(PIDFILE) if restart: - logger.info('Headphones is restarting...') + logger.info('Headphones is restarting...') popen_list = [sys.executable, FULL_PATH] popen_list += ARGS if '--nolaunch' not in popen_list: diff --git a/headphones/albumart.py b/headphones/albumart.py index ace34556..08773870 100644 --- a/headphones/albumart.py +++ b/headphones/albumart.py @@ -2,9 +2,9 @@ from headphones import db def getAlbumArt(albumid): - myDB = db.DBConnection() - asin = myDB.action('SELECT AlbumASIN from albums WHERE AlbumID=?', [albumid]).fetchone()[0] - - url = 'http://ec1.images-amazon.com/images/P/%s.01.LZZZZZZZ.jpg' % asin - - return url \ No newline at end of file + myDB = db.DBConnection() + asin = myDB.action('SELECT AlbumASIN from albums WHERE AlbumID=?', [albumid]).fetchone()[0] + + url = 'http://ec1.images-amazon.com/images/P/%s.01.LZZZZZZZ.jpg' % asin + + return url diff --git a/headphones/api.py b/headphones/api.py index c6f132ed..852249c9 100644 --- a/headphones/api.py +++ b/headphones/api.py @@ -7,295 +7,295 @@ from xml.dom.minidom import Document import copy cmd_list = [ 'getIndex', 'getArtist', 'getAlbum', 'getUpcoming', 'getWanted', 'getSimilar', 'getHistory', 'getLogs', - 'findArtist', 'findAlbum', 'addArtist', 'delArtist', 'pauseArtist', 'resumeArtist', 'refreshArtist', - 'queueAlbum', 'unqueueAlbum', 'forceSearch', 'forceProcess', 'getVersion', 'checkGithub', - 'shutdown', 'restart', 'update', ] + 'findArtist', 'findAlbum', 'addArtist', 'delArtist', 'pauseArtist', 'resumeArtist', 'refreshArtist', + 'queueAlbum', 'unqueueAlbum', 'forceSearch', 'forceProcess', 'getVersion', 'checkGithub', + 'shutdown', 'restart', 'update', ] class Api(object): - def __init__(self): - - self.apikey = None - self.cmd = None - self.id = None - - self.kwargs = None + def __init__(self): + + self.apikey = None + self.cmd = None + self.id = None + + self.kwargs = None - self.data = None + self.data = None - self.callback = None + self.callback = None - - def checkParams(self,*args,**kwargs): - - if not headphones.API_ENABLED: - self.data = 'API not enabled' - return - if not headphones.API_KEY: - self.data = 'API key not generated' - return - if len(headphones.API_KEY) != 32: - self.data = 'API key not generated correctly' - return - - if 'apikey' not in kwargs: - self.data = 'Missing api key' - return - - if kwargs['apikey'] != headphones.API_KEY: - self.data = 'Incorrect API key' - return - else: - self.apikey = kwargs.pop('apikey') - - if 'cmd' not in kwargs: - self.data = 'Missing parameter: cmd' - return - - if kwargs['cmd'] not in cmd_list: - self.data = 'Unknown command: %s' % kwargs['cmd'] - return - else: - self.cmd = kwargs.pop('cmd') - - self.kwargs = kwargs - self.data = 'OK' + + def checkParams(self,*args,**kwargs): + + if not headphones.API_ENABLED: + self.data = 'API not enabled' + return + if not headphones.API_KEY: + self.data = 'API key not generated' + return + if len(headphones.API_KEY) != 32: + self.data = 'API key not generated correctly' + return + + if 'apikey' not in kwargs: + self.data = 'Missing api key' + return + + if kwargs['apikey'] != headphones.API_KEY: + self.data = 'Incorrect API key' + return + else: + self.apikey = kwargs.pop('apikey') + + if 'cmd' not in kwargs: + self.data = 'Missing parameter: cmd' + return + + if kwargs['cmd'] not in cmd_list: + self.data = 'Unknown command: %s' % kwargs['cmd'] + return + else: + self.cmd = kwargs.pop('cmd') + + self.kwargs = kwargs + self.data = 'OK' - def fetchData(self): - - if self.data == 'OK': - logger.info('Recieved API command: ' + self.cmd) - methodToCall = getattr(self, "_" + self.cmd) - result = methodToCall(**self.kwargs) - if 'callback' not in self.kwargs: - if type(self.data) == type(''): - return self.data - else: - return simplejson.dumps(self.data) - else: - self.callback = self.kwargs['callback'] - self.data = simplejson.dumps(self.data) - self.data = self.callback + '(' + self.data + ');' - return self.data - else: - return self.data - - def _dic_from_query(self,query): - - myDB = db.DBConnection() - rows = myDB.select(query) - - rows_as_dic = [] - - for row in rows: - row_as_dic = dict(zip(row.keys(), row)) - rows_as_dic.append(row_as_dic) - - return rows_as_dic - - def _getIndex(self, **kwargs): - - self.data = self._dic_from_query('SELECT * from artists order by ArtistSortName COLLATE NOCASE') - return - - def _getArtist(self, **kwargs): - - if 'id' not in kwargs: - self.data = 'Missing parameter: id' - return - else: - self.id = kwargs['id'] - - artist = self._dic_from_query('SELECT * from artists WHERE ArtistID="' + self.id + '"') - albums = self._dic_from_query('SELECT * from albums WHERE ArtistID="' + self.id + '" order by ReleaseDate DESC') - - self.data = { 'artist': artist, 'albums': albums } - return - - def _getAlbum(self, **kwargs): - - if 'id' not in kwargs: - self.data = 'Missing parameter: id' - return - else: - self.id = kwargs['id'] - - album = self._dic_from_query('SELECT * from albums WHERE AlbumID="' + self.id + '"') - tracks = self._dic_from_query('SELECT * from tracks WHERE AlbumID="' + self.id + '"') - description = self._dic_from_query('SELECT * from descriptions WHERE ReleaseGroupID="' + self.id + '"') - - self.data = { 'album' : album, 'tracks' : tracks, 'description' : description } - return - - def _getHistory(self, **kwargs): - self.data = self._dic_from_query('SELECT * from snatched order by DateAdded DESC') - return - - def _getUpcoming(self, **kwargs): - self.data = self._dic_from_query("SELECT * from albums WHERE ReleaseDate > date('now') order by ReleaseDate DESC") - return - - def _getWanted(self, **kwargs): - self.data = self._dic_from_query("SELECT * from albums WHERE Status='Wanted'") - return - - def _getSimilar(self, **kwargs): - self.data = self._dic_from_query('SELECT * from lastfmcloud') - return - - def _getLogs(self, **kwargs): - pass - - def _findArtist(self, **kwargs): - if 'name' not in kwargs: - self.data = 'Missing parameter: name' - return - if 'limit' in kwargs: - limit = kwargs['limit'] - else: - limit=50 - - self.data = mb.findArtist(kwargs['name'], limit) + def fetchData(self): + + if self.data == 'OK': + logger.info('Recieved API command: ' + self.cmd) + methodToCall = getattr(self, "_" + self.cmd) + result = methodToCall(**self.kwargs) + if 'callback' not in self.kwargs: + if type(self.data) == type(''): + return self.data + else: + return simplejson.dumps(self.data) + else: + self.callback = self.kwargs['callback'] + self.data = simplejson.dumps(self.data) + self.data = self.callback + '(' + self.data + ');' + return self.data + else: + return self.data + + def _dic_from_query(self,query): + + myDB = db.DBConnection() + rows = myDB.select(query) + + rows_as_dic = [] + + for row in rows: + row_as_dic = dict(zip(row.keys(), row)) + rows_as_dic.append(row_as_dic) + + return rows_as_dic + + def _getIndex(self, **kwargs): + + self.data = self._dic_from_query('SELECT * from artists order by ArtistSortName COLLATE NOCASE') + return + + def _getArtist(self, **kwargs): + + if 'id' not in kwargs: + self.data = 'Missing parameter: id' + return + else: + self.id = kwargs['id'] + + artist = self._dic_from_query('SELECT * from artists WHERE ArtistID="' + self.id + '"') + albums = self._dic_from_query('SELECT * from albums WHERE ArtistID="' + self.id + '" order by ReleaseDate DESC') + + self.data = { 'artist': artist, 'albums': albums } + return + + def _getAlbum(self, **kwargs): + + if 'id' not in kwargs: + self.data = 'Missing parameter: id' + return + else: + self.id = kwargs['id'] + + album = self._dic_from_query('SELECT * from albums WHERE AlbumID="' + self.id + '"') + tracks = self._dic_from_query('SELECT * from tracks WHERE AlbumID="' + self.id + '"') + description = self._dic_from_query('SELECT * from descriptions WHERE ReleaseGroupID="' + self.id + '"') + + self.data = { 'album' : album, 'tracks' : tracks, 'description' : description } + return + + def _getHistory(self, **kwargs): + self.data = self._dic_from_query('SELECT * from snatched order by DateAdded DESC') + return + + def _getUpcoming(self, **kwargs): + self.data = self._dic_from_query("SELECT * from albums WHERE ReleaseDate > date('now') order by ReleaseDate DESC") + return + + def _getWanted(self, **kwargs): + self.data = self._dic_from_query("SELECT * from albums WHERE Status='Wanted'") + return + + def _getSimilar(self, **kwargs): + self.data = self._dic_from_query('SELECT * from lastfmcloud') + return + + def _getLogs(self, **kwargs): + pass + + def _findArtist(self, **kwargs): + if 'name' not in kwargs: + self.data = 'Missing parameter: name' + return + if 'limit' in kwargs: + limit = kwargs['limit'] + else: + limit=50 + + self.data = mb.findArtist(kwargs['name'], limit) - def _findAlbum(self, **kwargs): - if 'name' not in kwargs: - self.data = 'Missing parameter: name' - return - if 'limit' in kwargs: - limit = kwargs['limit'] - else: - limit=50 - - self.data = mb.findRelease(kwargs['name'], limit) - - def _addArtist(self, **kwargs): - if 'id' not in kwargs: - self.data = 'Missing parameter: id' - return - else: - self.id = kwargs['id'] - - try: - importer.addArtisttoDB(self.id) - except Exception, e: - self.data = e - - return - - def _delArtist(self, **kwargs): - if 'id' not in kwargs: - self.data = 'Missing parameter: id' - return - else: - self.id = kwargs['id'] - - myDB = db.DBConnection() - myDB.action('DELETE from artists WHERE ArtistID="' + self.id + '"') - myDB.action('DELETE from albums WHERE ArtistID="' + self.id + '"') - myDB.action('DELETE from tracks WHERE ArtistID="' + self.id + '"') - - def _pauseArtist(self, **kwargs): - if 'id' not in kwargs: - self.data = 'Missing parameter: id' - return - else: - self.id = kwargs['id'] - - myDB = db.DBConnection() - controlValueDict = {'ArtistID': self.id} - newValueDict = {'Status': 'Paused'} - myDB.upsert("artists", newValueDict, controlValueDict) - - def _resumeArtist(self, **kwargs): - if 'id' not in kwargs: - self.data = 'Missing parameter: id' - return - else: - self.id = kwargs['id'] - - myDB = db.DBConnection() - controlValueDict = {'ArtistID': self.id} - newValueDict = {'Status': 'Active'} - myDB.upsert("artists", newValueDict, controlValueDict) - - def _refreshArtist(self, **kwargs): - if 'id' not in kwargs: - self.data = 'Missing parameter: id' - return - else: - self.id = kwargs['id'] - - try: - importer.addArtisttoDB(self.id) - except Exception, e: - self.data = e - - return - - def _queueAlbum(self, **kwargs): - - if 'id' not in kwargs: - self.data = 'Missing parameter: id' - return - else: - self.id = kwargs['id'] - - if 'new' in kwargs: - new = kwargs['new'] - else: - new = False - - if 'lossless' in kwargs: - lossless = kwargs['lossless'] - else: - lossless = False - - myDB = db.DBConnection() - controlValueDict = {'AlbumID': self.id} - if lossless: - newValueDict = {'Status': 'Wanted Lossless'} - else: - newValueDict = {'Status': 'Wanted'} - myDB.upsert("albums", newValueDict, controlValueDict) - searcher.searchforalbum(self.id, new) - - def _unqueueAlbum(self, **kwargs): - - if 'id' not in kwargs: - self.data = 'Missing parameter: id' - return - else: - self.id = kwargs['id'] - - myDB = db.DBConnection() - controlValueDict = {'AlbumID': self.id} - newValueDict = {'Status': 'Skipped'} - myDB.upsert("albums", newValueDict, controlValueDict) - - def _forceSearch(self, **kwargs): - searcher.searchforalbum() - - def _forceProcess(self, **kwargs): - postprocessor.forcePostProcess() - - def _getVersion(self, **kwargs): - self.data = { - 'git_path' : headphones.GIT_PATH, - 'install_type' : headphones.INSTALL_TYPE, - 'current_version' : headphones.CURRENT_VERSION, - 'latest_version' : headphones.LATEST_VERSION, - 'commits_behind' : headphones.COMMITS_BEHIND, - } - - def _checkGithub(self, **kwargs): - versioncheck.checkGithub() - self._getVersion() - - def _shutdown(self, **kwargs): - headphones.SIGNAL = 'shutdown' - - def _restart(self, **kwargs): - headphones.SIGNAL = 'restart' - - def _update(self, **kwargs): - headphones.SIGNAL = 'update' \ No newline at end of file + def _findAlbum(self, **kwargs): + if 'name' not in kwargs: + self.data = 'Missing parameter: name' + return + if 'limit' in kwargs: + limit = kwargs['limit'] + else: + limit=50 + + self.data = mb.findRelease(kwargs['name'], limit) + + def _addArtist(self, **kwargs): + if 'id' not in kwargs: + self.data = 'Missing parameter: id' + return + else: + self.id = kwargs['id'] + + try: + importer.addArtisttoDB(self.id) + except Exception, e: + self.data = e + + return + + def _delArtist(self, **kwargs): + if 'id' not in kwargs: + self.data = 'Missing parameter: id' + return + else: + self.id = kwargs['id'] + + myDB = db.DBConnection() + myDB.action('DELETE from artists WHERE ArtistID="' + self.id + '"') + myDB.action('DELETE from albums WHERE ArtistID="' + self.id + '"') + myDB.action('DELETE from tracks WHERE ArtistID="' + self.id + '"') + + def _pauseArtist(self, **kwargs): + if 'id' not in kwargs: + self.data = 'Missing parameter: id' + return + else: + self.id = kwargs['id'] + + myDB = db.DBConnection() + controlValueDict = {'ArtistID': self.id} + newValueDict = {'Status': 'Paused'} + myDB.upsert("artists", newValueDict, controlValueDict) + + def _resumeArtist(self, **kwargs): + if 'id' not in kwargs: + self.data = 'Missing parameter: id' + return + else: + self.id = kwargs['id'] + + myDB = db.DBConnection() + controlValueDict = {'ArtistID': self.id} + newValueDict = {'Status': 'Active'} + myDB.upsert("artists", newValueDict, controlValueDict) + + def _refreshArtist(self, **kwargs): + if 'id' not in kwargs: + self.data = 'Missing parameter: id' + return + else: + self.id = kwargs['id'] + + try: + importer.addArtisttoDB(self.id) + except Exception, e: + self.data = e + + return + + def _queueAlbum(self, **kwargs): + + if 'id' not in kwargs: + self.data = 'Missing parameter: id' + return + else: + self.id = kwargs['id'] + + if 'new' in kwargs: + new = kwargs['new'] + else: + new = False + + if 'lossless' in kwargs: + lossless = kwargs['lossless'] + else: + lossless = False + + myDB = db.DBConnection() + controlValueDict = {'AlbumID': self.id} + if lossless: + newValueDict = {'Status': 'Wanted Lossless'} + else: + newValueDict = {'Status': 'Wanted'} + myDB.upsert("albums", newValueDict, controlValueDict) + searcher.searchforalbum(self.id, new) + + def _unqueueAlbum(self, **kwargs): + + if 'id' not in kwargs: + self.data = 'Missing parameter: id' + return + else: + self.id = kwargs['id'] + + myDB = db.DBConnection() + controlValueDict = {'AlbumID': self.id} + newValueDict = {'Status': 'Skipped'} + myDB.upsert("albums", newValueDict, controlValueDict) + + def _forceSearch(self, **kwargs): + searcher.searchforalbum() + + def _forceProcess(self, **kwargs): + postprocessor.forcePostProcess() + + def _getVersion(self, **kwargs): + self.data = { + 'git_path' : headphones.GIT_PATH, + 'install_type' : headphones.INSTALL_TYPE, + 'current_version' : headphones.CURRENT_VERSION, + 'latest_version' : headphones.LATEST_VERSION, + 'commits_behind' : headphones.COMMITS_BEHIND, + } + + def _checkGithub(self, **kwargs): + versioncheck.checkGithub() + self._getVersion() + + def _shutdown(self, **kwargs): + headphones.SIGNAL = 'shutdown' + + def _restart(self, **kwargs): + headphones.SIGNAL = 'restart' + + def _update(self, **kwargs): + headphones.SIGNAL = 'update' diff --git a/headphones/db.py b/headphones/db.py index 32de8a36..20113a1c 100644 --- a/headphones/db.py +++ b/headphones/db.py @@ -15,70 +15,70 @@ db_lock = threading.Lock() def dbFilename(filename="headphones.db"): - return os.path.join(headphones.DATA_DIR, filename) + return os.path.join(headphones.DATA_DIR, filename) class DBConnection: - def __init__(self, filename="headphones.db"): - - self.filename = filename - self.connection = sqlite3.connect(dbFilename(filename), timeout=20) - self.connection.row_factory = sqlite3.Row - - def action(self, query, args=None): - - with db_lock: + def __init__(self, filename="headphones.db"): + + self.filename = filename + self.connection = sqlite3.connect(dbFilename(filename), timeout=20) + self.connection.row_factory = sqlite3.Row + + def action(self, query, args=None): + + with db_lock: - if query == None: - return - - sqlResult = None - attempt = 0 - - while attempt < 5: - try: - if args == None: - #logger.debug(self.filename+": "+query) - sqlResult = self.connection.execute(query) - else: - #logger.debug(self.filename+": "+query+" with args "+str(args)) - sqlResult = self.connection.execute(query, args) - self.connection.commit() - break - except sqlite3.OperationalError, e: - if "unable to open database file" in e.message or "database is locked" in e.message: - logger.warn('Database Error: %s' % e) - attempt += 1 - time.sleep(1) - else: - logger.error('Database error: %s' % e) - raise - except sqlite3.DatabaseError, e: - logger.error('Fatal Error executing %s :: %s' % (query, e)) - raise - - return sqlResult - - def select(self, query, args=None): - - sqlResults = self.action(query, args).fetchall() - - if sqlResults == None: - return [] - - return sqlResults - - def upsert(self, tableName, valueDict, keyDict): - - changesBefore = self.connection.total_changes - - genParams = lambda myDict : [x + " = ?" for x in myDict.keys()] - - query = "UPDATE "+tableName+" SET " + ", ".join(genParams(valueDict)) + " WHERE " + " AND ".join(genParams(keyDict)) - - self.action(query, valueDict.values() + keyDict.values()) - - if self.connection.total_changes == changesBefore: - query = "INSERT INTO "+tableName+" (" + ", ".join(valueDict.keys() + keyDict.keys()) + ")" + \ - " VALUES (" + ", ".join(["?"] * len(valueDict.keys() + keyDict.keys())) + ")" - self.action(query, valueDict.values() + keyDict.values()) \ No newline at end of file + if query == None: + return + + sqlResult = None + attempt = 0 + + while attempt < 5: + try: + if args == None: + #logger.debug(self.filename+": "+query) + sqlResult = self.connection.execute(query) + else: + #logger.debug(self.filename+": "+query+" with args "+str(args)) + sqlResult = self.connection.execute(query, args) + self.connection.commit() + break + except sqlite3.OperationalError, e: + if "unable to open database file" in e.message or "database is locked" in e.message: + logger.warn('Database Error: %s' % e) + attempt += 1 + time.sleep(1) + else: + logger.error('Database error: %s' % e) + raise + except sqlite3.DatabaseError, e: + logger.error('Fatal Error executing %s :: %s' % (query, e)) + raise + + return sqlResult + + def select(self, query, args=None): + + sqlResults = self.action(query, args).fetchall() + + if sqlResults == None: + return [] + + return sqlResults + + def upsert(self, tableName, valueDict, keyDict): + + changesBefore = self.connection.total_changes + + genParams = lambda myDict : [x + " = ?" for x in myDict.keys()] + + query = "UPDATE "+tableName+" SET " + ", ".join(genParams(valueDict)) + " WHERE " + " AND ".join(genParams(keyDict)) + + self.action(query, valueDict.values() + keyDict.values()) + + if self.connection.total_changes == changesBefore: + query = "INSERT INTO "+tableName+" (" + ", ".join(valueDict.keys() + keyDict.keys()) + ")" + \ + " VALUES (" + ", ".join(["?"] * len(valueDict.keys() + keyDict.keys())) + ")" + self.action(query, valueDict.values() + keyDict.values()) diff --git a/headphones/helpers.py b/headphones/helpers.py index 73054bdb..a6d732f2 100644 --- a/headphones/helpers.py +++ b/headphones/helpers.py @@ -20,120 +20,120 @@ def multikeysort(items, columns): return sorted(items, cmp=comparer) def checked(variable): - if variable: - return 'Checked' - else: - return '' - + if variable: + return 'Checked' + else: + return '' + def radio(variable, pos): - if variable == pos: - return 'Checked' - else: - return '' - + if variable == pos: + return 'Checked' + else: + return '' + def latinToAscii(unicrap): - """ - From couch potato - """ - xlate = {0xc0:'A', 0xc1:'A', 0xc2:'A', 0xc3:'A', 0xc4:'A', 0xc5:'A', - 0xc6:'Ae', 0xc7:'C', - 0xc8:'E', 0xc9:'E', 0xca:'E', 0xcb:'E', 0x86:'e', - 0xcc:'I', 0xcd:'I', 0xce:'I', 0xcf:'I', - 0xd0:'Th', 0xd1:'N', - 0xd2:'O', 0xd3:'O', 0xd4:'O', 0xd5:'O', 0xd6:'O', 0xd8:'O', - 0xd9:'U', 0xda:'U', 0xdb:'U', 0xdc:'U', - 0xdd:'Y', 0xde:'th', 0xdf:'ss', - 0xe0:'a', 0xe1:'a', 0xe2:'a', 0xe3:'a', 0xe4:'a', 0xe5:'a', - 0xe6:'ae', 0xe7:'c', - 0xe8:'e', 0xe9:'e', 0xea:'e', 0xeb:'e', 0x0259:'e', - 0xec:'i', 0xed:'i', 0xee:'i', 0xef:'i', - 0xf0:'th', 0xf1:'n', - 0xf2:'o', 0xf3:'o', 0xf4:'o', 0xf5:'o', 0xf6:'o', 0xf8:'o', - 0xf9:'u', 0xfa:'u', 0xfb:'u', 0xfc:'u', - 0xfd:'y', 0xfe:'th', 0xff:'y', - 0xa1:'!', 0xa2:'{cent}', 0xa3:'{pound}', 0xa4:'{currency}', - 0xa5:'{yen}', 0xa6:'|', 0xa7:'{section}', 0xa8:'{umlaut}', - 0xa9:'{C}', 0xaa:'{^a}', 0xab:'<<', 0xac:'{not}', - 0xad:'-', 0xae:'{R}', 0xaf:'_', 0xb0:'{degrees}', - 0xb1:'{+/-}', 0xb2:'{^2}', 0xb3:'{^3}', 0xb4:"'", - 0xb5:'{micro}', 0xb6:'{paragraph}', 0xb7:'*', 0xb8:'{cedilla}', - 0xb9:'{^1}', 0xba:'{^o}', 0xbb:'>>', - 0xbc:'{1/4}', 0xbd:'{1/2}', 0xbe:'{3/4}', 0xbf:'?', - 0xd7:'*', 0xf7:'/' - } + """ + From couch potato + """ + xlate = {0xc0:'A', 0xc1:'A', 0xc2:'A', 0xc3:'A', 0xc4:'A', 0xc5:'A', + 0xc6:'Ae', 0xc7:'C', + 0xc8:'E', 0xc9:'E', 0xca:'E', 0xcb:'E', 0x86:'e', + 0xcc:'I', 0xcd:'I', 0xce:'I', 0xcf:'I', + 0xd0:'Th', 0xd1:'N', + 0xd2:'O', 0xd3:'O', 0xd4:'O', 0xd5:'O', 0xd6:'O', 0xd8:'O', + 0xd9:'U', 0xda:'U', 0xdb:'U', 0xdc:'U', + 0xdd:'Y', 0xde:'th', 0xdf:'ss', + 0xe0:'a', 0xe1:'a', 0xe2:'a', 0xe3:'a', 0xe4:'a', 0xe5:'a', + 0xe6:'ae', 0xe7:'c', + 0xe8:'e', 0xe9:'e', 0xea:'e', 0xeb:'e', 0x0259:'e', + 0xec:'i', 0xed:'i', 0xee:'i', 0xef:'i', + 0xf0:'th', 0xf1:'n', + 0xf2:'o', 0xf3:'o', 0xf4:'o', 0xf5:'o', 0xf6:'o', 0xf8:'o', + 0xf9:'u', 0xfa:'u', 0xfb:'u', 0xfc:'u', + 0xfd:'y', 0xfe:'th', 0xff:'y', + 0xa1:'!', 0xa2:'{cent}', 0xa3:'{pound}', 0xa4:'{currency}', + 0xa5:'{yen}', 0xa6:'|', 0xa7:'{section}', 0xa8:'{umlaut}', + 0xa9:'{C}', 0xaa:'{^a}', 0xab:'<<', 0xac:'{not}', + 0xad:'-', 0xae:'{R}', 0xaf:'_', 0xb0:'{degrees}', + 0xb1:'{+/-}', 0xb2:'{^2}', 0xb3:'{^3}', 0xb4:"'", + 0xb5:'{micro}', 0xb6:'{paragraph}', 0xb7:'*', 0xb8:'{cedilla}', + 0xb9:'{^1}', 0xba:'{^o}', 0xbb:'>>', + 0xbc:'{1/4}', 0xbd:'{1/2}', 0xbe:'{3/4}', 0xbf:'?', + 0xd7:'*', 0xf7:'/' + } - r = '' - for i in unicrap: - if xlate.has_key(ord(i)): - r += xlate[ord(i)] - elif ord(i) >= 0x80: - pass - else: - r += str(i) - return r - + r = '' + for i in unicrap: + if xlate.has_key(ord(i)): + r += xlate[ord(i)] + elif ord(i) >= 0x80: + pass + else: + r += str(i) + return r + def convert_milliseconds(ms): - seconds = ms/1000 - gmtime = time.gmtime(seconds) - if seconds > 3600: - minutes = time.strftime("%H:%M:%S", gmtime) - else: - minutes = time.strftime("%M:%S", gmtime) + seconds = ms/1000 + gmtime = time.gmtime(seconds) + if seconds > 3600: + minutes = time.strftime("%H:%M:%S", gmtime) + else: + minutes = time.strftime("%M:%S", gmtime) - return minutes - + return minutes + def convert_seconds(s): - gmtime = time.gmtime(s) - if s > 3600: - minutes = time.strftime("%H:%M:%S", gmtime) - else: - minutes = time.strftime("%M:%S", gmtime) + gmtime = time.gmtime(s) + if s > 3600: + minutes = time.strftime("%H:%M:%S", gmtime) + else: + minutes = time.strftime("%M:%S", gmtime) - return minutes - + return minutes + def today(): - today = datetime.date.today() - yyyymmdd = datetime.date.isoformat(today) - return yyyymmdd - + today = datetime.date.today() + yyyymmdd = datetime.date.isoformat(today) + return yyyymmdd + def now(): - now = datetime.datetime.now() - return now.strftime("%Y-%m-%d %H:%M:%S") - + now = datetime.datetime.now() + return now.strftime("%Y-%m-%d %H:%M:%S") + def bytes_to_mb(bytes): - mb = int(bytes)/1048576 - size = '%.1f MB' % mb - return size - + mb = int(bytes)/1048576 + size = '%.1f MB' % mb + return size + def replace_all(text, dic): - for i, j in dic.iteritems(): - text = text.replace(i, j) - return text - + for i, j in dic.iteritems(): + text = text.replace(i, j) + return text + def cleanName(string): - pass1 = latinToAscii(string).lower() - out_string = re.sub('[\.\-\/\!\@\#\$\%\^\&\*\(\)\+\-\"\'\,\;\:\[\]\{\}\<\>\=\_]', '', pass1).encode('utf-8') - - return out_string - + pass1 = latinToAscii(string).lower() + out_string = re.sub('[\.\-\/\!\@\#\$\%\^\&\*\(\)\+\-\"\'\,\;\:\[\]\{\}\<\>\=\_]', '', pass1).encode('utf-8') + + return out_string + def cleanTitle(title): - title = re.sub('[\.\-\/\_]', ' ', title).lower() - - # Strip out extra whitespace - title = ' '.join(title.split()) - - title = title.title() - - return 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 #headphones default format @@ -161,18 +161,18 @@ def extract_data(s): return (name, album, year) def extract_logline(s): - # Default log format - pattern = re.compile(r'(?P.*?)\s\-\s(?P.*?)\s*\:\:\s(?P.*?)\s\:\s(?P.*)', re.VERBOSE) - match = pattern.match(s) - if match: - timestamp = match.group("timestamp") - level = match.group("level") - thread = match.group("thread") - message = match.group("message") - return (timestamp, level, thread, message) - else: - return None - + # Default log format + pattern = re.compile(r'(?P.*?)\s\-\s(?P.*?)\s*\:\:\s(?P.*?)\s\:\s(?P.*)', re.VERBOSE) + match = pattern.match(s) + if match: + timestamp = match.group("timestamp") + level = match.group("level") + thread = match.group("thread") + message = match.group("message") + return (timestamp, level, thread, message) + else: + return None + def extract_song_data(s): #headphones default format @@ -202,4 +202,4 @@ def extract_song_data(s): return (name, album, year) else: logger.info("Couldn't parse " + s + " into a valid Newbin format") - return (name, album, year) \ No newline at end of file + return (name, album, year) diff --git a/headphones/importer.py b/headphones/importer.py index dd6a896a..575b5ca4 100644 --- a/headphones/importer.py +++ b/headphones/importer.py @@ -7,369 +7,369 @@ import headphones from headphones import logger, helpers, db, mb, albumart, lastfm various_artists_mbid = '89ad4ac3-39f7-470e-963a-56509c546377' - + def is_exists(artistid): - myDB = db.DBConnection() - - # See if the artist is already in the database - artistlist = myDB.select('SELECT ArtistID, ArtistName from artists WHERE ArtistID=?', [artistid]) + myDB = db.DBConnection() + + # See if the artist is already in the database + artistlist = myDB.select('SELECT ArtistID, ArtistName from artists WHERE ArtistID=?', [artistid]) - if any(artistid in x for x in artistlist): - logger.info(artistlist[0][1] + u" is already in the database. Updating 'have tracks', but not artist information") - return True - else: - return False + if any(artistid in x for x in artistlist): + logger.info(artistlist[0][1] + u" is already in the database. Updating 'have tracks', but not artist information") + return True + else: + return False def artistlist_to_mbids(artistlist, forced=False): - for artist in artistlist: - - if forced: - artist = unicode(artist, 'utf-8') - - results = mb.findArtist(artist, limit=1) - - if not results: - logger.info('No results found for: %s' % artist) - continue - - try: - artistid = results[0]['id'] - - except IndexError: - logger.info('MusicBrainz query turned up no matches for: %s' % artist) - continue - - # Add to database if it doesn't exist - if artistid != various_artists_mbid and not is_exists(artistid): - addArtisttoDB(artistid) - - # Just update the tracks if it does - else: - myDB = db.DBConnection() - havetracks = len(myDB.select('SELECT TrackTitle from tracks WHERE ArtistID=?', [artistid])) + len(myDB.select('SELECT TrackTitle from have WHERE ArtistName like ?', [artist])) - myDB.action('UPDATE artists SET HaveTracks=? WHERE ArtistID=?', [havetracks, artistid]) - - # Update the similar artist tag cloud: - logger.info('Updating artist information from Last.fm') - try: - lastfm.getSimilar() - except Exception, e: - logger.warn('Failed to update arist information from Last.fm: %s' % e) - + for artist in artistlist: + + if forced: + artist = unicode(artist, 'utf-8') + + results = mb.findArtist(artist, limit=1) + + if not results: + logger.info('No results found for: %s' % artist) + continue + + try: + artistid = results[0]['id'] + + except IndexError: + logger.info('MusicBrainz query turned up no matches for: %s' % artist) + continue + + # Add to database if it doesn't exist + if artistid != various_artists_mbid and not is_exists(artistid): + addArtisttoDB(artistid) + + # Just update the tracks if it does + else: + myDB = db.DBConnection() + havetracks = len(myDB.select('SELECT TrackTitle from tracks WHERE ArtistID=?', [artistid])) + len(myDB.select('SELECT TrackTitle from have WHERE ArtistName like ?', [artist])) + myDB.action('UPDATE artists SET HaveTracks=? WHERE ArtistID=?', [havetracks, artistid]) + + # Update the similar artist tag cloud: + logger.info('Updating artist information from Last.fm') + try: + lastfm.getSimilar() + except Exception, e: + logger.warn('Failed to update arist information from Last.fm: %s' % e) + def addArtistIDListToDB(artistidlist): - # Used to add a list of artist IDs to the database in a single thread - logger.debug("Importer: Adding artist ids %s" % artistidlist) - for artistid in artistidlist: - addArtisttoDB(artistid) + # Used to add a list of artist IDs to the database in a single thread + logger.debug("Importer: Adding artist ids %s" % artistidlist) + for artistid in artistidlist: + addArtisttoDB(artistid) def addArtisttoDB(artistid, extrasonly=False): - - # Can't add various artists - throws an error from MB - if artistid == various_artists_mbid: - logger.warn('Cannot import Various Artists.') - return - - myDB = db.DBConnection() + + # Can't add various artists - throws an error from MB + if artistid == various_artists_mbid: + logger.warn('Cannot import Various Artists.') + return + + myDB = db.DBConnection() - # We need the current minimal info in the database instantly - # so we don't throw a 500 error when we redirect to the artistPage + # We need the current minimal info in the database instantly + # so we don't throw a 500 error when we redirect to the artistPage - controlValueDict = {"ArtistID": artistid} + controlValueDict = {"ArtistID": artistid} - # Don't replace a known artist name with an "Artist ID" placeholder + # Don't replace a known artist name with an "Artist ID" placeholder - dbartist = myDB.action('SELECT * FROM artists WHERE ArtistID=?', [artistid]).fetchone() - if dbartist is None: - newValueDict = {"ArtistName": "Artist ID: %s" % (artistid), - "Status": "Loading"} - else: - newValueDict = {"Status": "Loading"} + dbartist = myDB.action('SELECT * FROM artists WHERE ArtistID=?', [artistid]).fetchone() + if dbartist is None: + newValueDict = {"ArtistName": "Artist ID: %s" % (artistid), + "Status": "Loading"} + else: + newValueDict = {"Status": "Loading"} - myDB.upsert("artists", newValueDict, controlValueDict) - - artist = mb.getArtist(artistid, extrasonly) - - if not artist: - logger.warn("Error fetching artist info. ID: " + artistid) - if dbartist is None: - newValueDict = {"ArtistName": "Fetch failed, try refreshing. (%s)" % (artistid), - "Status": "Active"} - else: - newValueDict = {"Status": "Active"} - myDB.upsert("artists", newValueDict, controlValueDict) - return - - if artist['artist_name'].startswith('The '): - sortname = artist['artist_name'][4:] - else: - sortname = artist['artist_name'] - + myDB.upsert("artists", newValueDict, controlValueDict) + + artist = mb.getArtist(artistid, extrasonly) + + if not artist: + logger.warn("Error fetching artist info. ID: " + artistid) + if dbartist is None: + newValueDict = {"ArtistName": "Fetch failed, try refreshing. (%s)" % (artistid), + "Status": "Active"} + else: + newValueDict = {"Status": "Active"} + myDB.upsert("artists", newValueDict, controlValueDict) + return + + if artist['artist_name'].startswith('The '): + sortname = artist['artist_name'][4:] + else: + sortname = artist['artist_name'] + - logger.info(u"Now adding/updating: " + artist['artist_name']) - controlValueDict = {"ArtistID": artistid} - newValueDict = {"ArtistName": artist['artist_name'], - "ArtistSortName": sortname, - "DateAdded": helpers.today(), - "Status": "Loading"} - - if headphones.INCLUDE_EXTRAS: - newValueDict['IncludeExtras'] = 1 - - myDB.upsert("artists", newValueDict, controlValueDict) + logger.info(u"Now adding/updating: " + artist['artist_name']) + controlValueDict = {"ArtistID": artistid} + newValueDict = {"ArtistName": artist['artist_name'], + "ArtistSortName": sortname, + "DateAdded": helpers.today(), + "Status": "Loading"} + + if headphones.INCLUDE_EXTRAS: + newValueDict['IncludeExtras'] = 1 + + myDB.upsert("artists", newValueDict, controlValueDict) - for rg in artist['releasegroups']: - - rgid = rg['id'] - - # check if the album already exists - rg_exists = myDB.select("SELECT * from albums WHERE AlbumID=?", [rg['id']]) - - try: - release_dict = mb.getReleaseGroup(rgid) - except Exception, e: - logger.info('Unable to get release information for %s - there may not be any official releases in this release group' % rg['title']) - continue - - if not release_dict: - continue - - logger.info(u"Now adding/updating album: " + rg['title']) - controlValueDict = {"AlbumID": rg['id']} - - if len(rg_exists): - - newValueDict = {"AlbumASIN": release_dict['asin'], - "ReleaseDate": release_dict['releasedate'], - } - - else: - - newValueDict = {"ArtistID": artistid, - "ArtistName": artist['artist_name'], - "AlbumTitle": rg['title'], - "AlbumASIN": release_dict['asin'], - "ReleaseDate": release_dict['releasedate'], - "DateAdded": helpers.today(), - "Type": rg['type'] - } - - if headphones.AUTOWANT_ALL: - newValueDict['Status'] = "Wanted" - elif release_dict['releasedate'] > helpers.today() and headphones.AUTOWANT_UPCOMING: - newValueDict['Status'] = "Wanted" - else: - newValueDict['Status'] = "Skipped" - - myDB.upsert("albums", newValueDict, controlValueDict) - - try: - lastfm.getAlbumDescription(rg['id'], artist['artist_name'], rg['title']) - except Exception, e: - logger.error('Attempt to retrieve album description from Last.fm failed: %s' % e) - - # I changed the albumid from releaseid -> rgid, so might need to delete albums that have a releaseid - for release in release_dict['releaselist']: - myDB.action('DELETE from albums WHERE AlbumID=?', [release['releaseid']]) - myDB.action('DELETE from tracks WHERE AlbumID=?', [release['releaseid']]) - - for track in release_dict['tracks']: - - cleanname = helpers.cleanName(artist['artist_name'] + ' ' + rg['title'] + ' ' + track['title']) - - controlValueDict = {"TrackID": track['id'], - "AlbumID": rg['id']} - newValueDict = {"ArtistID": artistid, - "ArtistName": artist['artist_name'], - "AlbumTitle": rg['title'], - "AlbumASIN": release_dict['asin'], - "TrackTitle": track['title'], - "TrackDuration": track['duration'], - "TrackNumber": track['number'], - "CleanName": cleanname - } - - match = myDB.action('SELECT Location, BitRate, Format from have WHERE CleanName=?', [cleanname]).fetchone() - - if not match: - match = myDB.action('SELECT Location, BitRate, Format from have WHERE ArtistName LIKE ? AND AlbumTitle LIKE ? AND TrackTitle LIKE ?', [artist['artist_name'], rg['title'], track['title']]).fetchone() - if not match: - match = myDB.action('SELECT Location, BitRate, Format from have WHERE TrackID=?', [track['id']]).fetchone() - if match: - newValueDict['Location'] = match['Location'] - newValueDict['BitRate'] = match['BitRate'] - newValueDict['Format'] = match['Format'] - myDB.action('DELETE from have WHERE Location=?', [match['Location']]) - - myDB.upsert("tracks", newValueDict, controlValueDict) - - latestalbum = myDB.action('SELECT AlbumTitle, ReleaseDate, AlbumID from albums WHERE ArtistID=? order by ReleaseDate DESC', [artistid]).fetchone() - totaltracks = len(myDB.select('SELECT TrackTitle from tracks WHERE ArtistID=?', [artistid])) - havetracks = len(myDB.select('SELECT TrackTitle from tracks WHERE ArtistID=? AND Location IS NOT NULL', [artistid])) + len(myDB.select('SELECT TrackTitle from have WHERE ArtistName like ?', [artist['artist_name']])) + for rg in artist['releasegroups']: + + rgid = rg['id'] + + # check if the album already exists + rg_exists = myDB.select("SELECT * from albums WHERE AlbumID=?", [rg['id']]) + + try: + release_dict = mb.getReleaseGroup(rgid) + except Exception, e: + logger.info('Unable to get release information for %s - there may not be any official releases in this release group' % rg['title']) + continue + + if not release_dict: + continue + + logger.info(u"Now adding/updating album: " + rg['title']) + controlValueDict = {"AlbumID": rg['id']} + + if len(rg_exists): + + newValueDict = {"AlbumASIN": release_dict['asin'], + "ReleaseDate": release_dict['releasedate'], + } + + else: + + newValueDict = {"ArtistID": artistid, + "ArtistName": artist['artist_name'], + "AlbumTitle": rg['title'], + "AlbumASIN": release_dict['asin'], + "ReleaseDate": release_dict['releasedate'], + "DateAdded": helpers.today(), + "Type": rg['type'] + } + + if headphones.AUTOWANT_ALL: + newValueDict['Status'] = "Wanted" + elif release_dict['releasedate'] > helpers.today() and headphones.AUTOWANT_UPCOMING: + newValueDict['Status'] = "Wanted" + else: + newValueDict['Status'] = "Skipped" + + myDB.upsert("albums", newValueDict, controlValueDict) + + try: + lastfm.getAlbumDescription(rg['id'], artist['artist_name'], rg['title']) + except Exception, e: + logger.error('Attempt to retrieve album description from Last.fm failed: %s' % e) + + # I changed the albumid from releaseid -> rgid, so might need to delete albums that have a releaseid + for release in release_dict['releaselist']: + myDB.action('DELETE from albums WHERE AlbumID=?', [release['releaseid']]) + myDB.action('DELETE from tracks WHERE AlbumID=?', [release['releaseid']]) + + for track in release_dict['tracks']: + + cleanname = helpers.cleanName(artist['artist_name'] + ' ' + rg['title'] + ' ' + track['title']) + + controlValueDict = {"TrackID": track['id'], + "AlbumID": rg['id']} + newValueDict = {"ArtistID": artistid, + "ArtistName": artist['artist_name'], + "AlbumTitle": rg['title'], + "AlbumASIN": release_dict['asin'], + "TrackTitle": track['title'], + "TrackDuration": track['duration'], + "TrackNumber": track['number'], + "CleanName": cleanname + } + + match = myDB.action('SELECT Location, BitRate, Format from have WHERE CleanName=?', [cleanname]).fetchone() + + if not match: + match = myDB.action('SELECT Location, BitRate, Format from have WHERE ArtistName LIKE ? AND AlbumTitle LIKE ? AND TrackTitle LIKE ?', [artist['artist_name'], rg['title'], track['title']]).fetchone() + if not match: + match = myDB.action('SELECT Location, BitRate, Format from have WHERE TrackID=?', [track['id']]).fetchone() + if match: + newValueDict['Location'] = match['Location'] + newValueDict['BitRate'] = match['BitRate'] + newValueDict['Format'] = match['Format'] + myDB.action('DELETE from have WHERE Location=?', [match['Location']]) + + myDB.upsert("tracks", newValueDict, controlValueDict) + + latestalbum = myDB.action('SELECT AlbumTitle, ReleaseDate, AlbumID from albums WHERE ArtistID=? order by ReleaseDate DESC', [artistid]).fetchone() + totaltracks = len(myDB.select('SELECT TrackTitle from tracks WHERE ArtistID=?', [artistid])) + havetracks = len(myDB.select('SELECT TrackTitle from tracks WHERE ArtistID=? AND Location IS NOT NULL', [artistid])) + len(myDB.select('SELECT TrackTitle from have WHERE ArtistName like ?', [artist['artist_name']])) - controlValueDict = {"ArtistID": artistid} - - if latestalbum: - newValueDict = {"Status": "Active", - "LatestAlbum": latestalbum['AlbumTitle'], - "ReleaseDate": latestalbum['ReleaseDate'], - "AlbumID": latestalbum['AlbumID'], - "TotalTracks": totaltracks, - "HaveTracks": havetracks} - else: - newValueDict = {"Status": "Active", - "TotalTracks": totaltracks, - "HaveTracks": havetracks} - - newValueDict['LastUpdated'] = helpers.now() - - myDB.upsert("artists", newValueDict, controlValueDict) - logger.info(u"Updating complete for: " + artist['artist_name']) - + controlValueDict = {"ArtistID": artistid} + + if latestalbum: + newValueDict = {"Status": "Active", + "LatestAlbum": latestalbum['AlbumTitle'], + "ReleaseDate": latestalbum['ReleaseDate'], + "AlbumID": latestalbum['AlbumID'], + "TotalTracks": totaltracks, + "HaveTracks": havetracks} + else: + newValueDict = {"Status": "Active", + "TotalTracks": totaltracks, + "HaveTracks": havetracks} + + newValueDict['LastUpdated'] = helpers.now() + + myDB.upsert("artists", newValueDict, controlValueDict) + logger.info(u"Updating complete for: " + artist['artist_name']) + def addReleaseById(rid): - - myDB = db.DBConnection() + + myDB = db.DBConnection() - rgid = None - artistid = None - release_dict = None - results = myDB.select("SELECT albums.ArtistID, releases.ReleaseGroupID from releases, albums WHERE releases.ReleaseID=? and releases.ReleaseGroupID=albums.AlbumID LIMIT 1", [rid]) - for result in results: - rgid = result['ReleaseGroupID'] - artistid = result['ArtistID'] - logger.debug("Found a cached releaseid : releasegroupid relationship: " + rid + " : " + rgid) - if not rgid: - #didn't find it in the cache, get the information from MB - logger.debug("Didn't find releaseID " + rid + " in the cache. Looking up its ReleaseGroupID") - try: - release_dict = mb.getRelease(rid) - except Exception, e: - logger.info('Unable to get release information for Release: ' + str(rid) + " " + str(e)) - return - if not release_dict: - logger.info('Unable to get release information for Release: ' + str(rid) + " no dict") - return - - rgid = release_dict['rgid'] - artistid = release_dict['artist_id'] - - #we don't want to make more calls to MB here unless we have to, could be happening quite a lot - rg_exists = myDB.select("SELECT * from albums WHERE AlbumID=?", [rgid]) - - #make sure the artist exists since I don't know what happens later if it doesn't - artist_exists = myDB.select("SELECT * from artists WHERE ArtistID=?", [artistid]) - - if not artist_exists and release_dict: - if release_dict['artist_name'].startswith('The '): - sortname = release_dict['artist_name'][4:] - else: - sortname = release_dict['artist_name'] - - - logger.info(u"Now manually adding: " + release_dict['artist_name'] + " - with status Paused") - controlValueDict = {"ArtistID": release_dict['artist_id']} - newValueDict = {"ArtistName": release_dict['artist_name'], - "ArtistSortName": sortname, - "DateAdded": helpers.today(), - "Status": "Paused"} - - if headphones.INCLUDE_EXTRAS: - newValueDict['IncludeExtras'] = 1 - - myDB.upsert("artists", newValueDict, controlValueDict) - - elif not artist_exists and not release_dict: - logger.error("Artist does not exist in the database and did not get a valid response from MB. Skipping release.") - return - - if not rg_exists and release_dict: #it should never be the case that we have an rg and not the artist - #but if it is this will fail - logger.info(u"Now adding-by-id album (" + release_dict['title'] + ") from id: " + rgid) - controlValueDict = {"AlbumID": rgid} + rgid = None + artistid = None + release_dict = None + results = myDB.select("SELECT albums.ArtistID, releases.ReleaseGroupID from releases, albums WHERE releases.ReleaseID=? and releases.ReleaseGroupID=albums.AlbumID LIMIT 1", [rid]) + for result in results: + rgid = result['ReleaseGroupID'] + artistid = result['ArtistID'] + logger.debug("Found a cached releaseid : releasegroupid relationship: " + rid + " : " + rgid) + if not rgid: + #didn't find it in the cache, get the information from MB + logger.debug("Didn't find releaseID " + rid + " in the cache. Looking up its ReleaseGroupID") + try: + release_dict = mb.getRelease(rid) + except Exception, e: + logger.info('Unable to get release information for Release: ' + str(rid) + " " + str(e)) + return + if not release_dict: + logger.info('Unable to get release information for Release: ' + str(rid) + " no dict") + return + + rgid = release_dict['rgid'] + artistid = release_dict['artist_id'] + + #we don't want to make more calls to MB here unless we have to, could be happening quite a lot + rg_exists = myDB.select("SELECT * from albums WHERE AlbumID=?", [rgid]) + + #make sure the artist exists since I don't know what happens later if it doesn't + artist_exists = myDB.select("SELECT * from artists WHERE ArtistID=?", [artistid]) + + if not artist_exists and release_dict: + if release_dict['artist_name'].startswith('The '): + sortname = release_dict['artist_name'][4:] + else: + sortname = release_dict['artist_name'] + + + logger.info(u"Now manually adding: " + release_dict['artist_name'] + " - with status Paused") + controlValueDict = {"ArtistID": release_dict['artist_id']} + newValueDict = {"ArtistName": release_dict['artist_name'], + "ArtistSortName": sortname, + "DateAdded": helpers.today(), + "Status": "Paused"} + + if headphones.INCLUDE_EXTRAS: + newValueDict['IncludeExtras'] = 1 + + myDB.upsert("artists", newValueDict, controlValueDict) + + elif not artist_exists and not release_dict: + logger.error("Artist does not exist in the database and did not get a valid response from MB. Skipping release.") + return + + if not rg_exists and release_dict: #it should never be the case that we have an rg and not the artist + #but if it is this will fail + logger.info(u"Now adding-by-id album (" + release_dict['title'] + ") from id: " + rgid) + controlValueDict = {"AlbumID": rgid} - newValueDict = {"ArtistID": release_dict['artist_id'], - "ArtistName": release_dict['artist_name'], - "AlbumTitle": release_dict['rg_title'], - "AlbumASIN": release_dict['asin'], - "ReleaseDate": release_dict['date'], - "DateAdded": helpers.today(), - "Status": 'Wanted', - "Type": release_dict['rg_type'] - } - - myDB.upsert("albums", newValueDict, controlValueDict) + newValueDict = {"ArtistID": release_dict['artist_id'], + "ArtistName": release_dict['artist_name'], + "AlbumTitle": release_dict['rg_title'], + "AlbumASIN": release_dict['asin'], + "ReleaseDate": release_dict['date'], + "DateAdded": helpers.today(), + "Status": 'Wanted', + "Type": release_dict['rg_type'] + } + + myDB.upsert("albums", newValueDict, controlValueDict) - #keep a local cache of these so that external programs that are adding releasesByID don't hammer MB - myDB.action('INSERT INTO releases VALUES( ?, ?)', [rid, release_dict['rgid']]) - - for track in release_dict['tracks']: - - cleanname = helpers.cleanName(release_dict['artist_name'] + ' ' + release_dict['rg_title'] + ' ' + track['title']) - - controlValueDict = {"TrackID": track['id'], - "AlbumID": rgid} - newValueDict = {"ArtistID": release_dict['artist_id'], - "ArtistName": release_dict['artist_name'], - "AlbumTitle": release_dict['rg_title'], - "AlbumASIN": release_dict['asin'], - "TrackTitle": track['title'], - "TrackDuration": track['duration'], - "TrackNumber": track['number'], - "CleanName": cleanname - } - - match = myDB.action('SELECT Location, BitRate, Format from have WHERE CleanName=?', [cleanname]).fetchone() - - if not match: - match = myDB.action('SELECT Location, BitRate, Format from have WHERE ArtistName LIKE ? AND AlbumTitle LIKE ? AND TrackTitle LIKE ?', [release_dict['artist_name'], release_dict['rg_title'], track['title']]).fetchone() - - if not match: - match = myDB.action('SELECT Location, BitRate, Format from have WHERE TrackID=?', [track['id']]).fetchone() - - if match: - newValueDict['Location'] = match['Location'] - newValueDict['BitRate'] = match['BitRate'] - newValueDict['Format'] = match['Format'] - myDB.action('DELETE from have WHERE Location=?', [match['Location']]) - - myDB.upsert("tracks", newValueDict, controlValueDict) - - #start a search for the album - import searcher - searcher.searchNZB(rgid, False) - elif not rg_exists and not release_dict: - logger.error("ReleaseGroup does not exist in the database and did not get a valid response from MB. Skipping release.") - return - else: - logger.info('Release ' + str(rid) + " already exists in the database!") + #keep a local cache of these so that external programs that are adding releasesByID don't hammer MB + myDB.action('INSERT INTO releases VALUES( ?, ?)', [rid, release_dict['rgid']]) + + for track in release_dict['tracks']: + + cleanname = helpers.cleanName(release_dict['artist_name'] + ' ' + release_dict['rg_title'] + ' ' + track['title']) + + controlValueDict = {"TrackID": track['id'], + "AlbumID": rgid} + newValueDict = {"ArtistID": release_dict['artist_id'], + "ArtistName": release_dict['artist_name'], + "AlbumTitle": release_dict['rg_title'], + "AlbumASIN": release_dict['asin'], + "TrackTitle": track['title'], + "TrackDuration": track['duration'], + "TrackNumber": track['number'], + "CleanName": cleanname + } + + match = myDB.action('SELECT Location, BitRate, Format from have WHERE CleanName=?', [cleanname]).fetchone() + + if not match: + match = myDB.action('SELECT Location, BitRate, Format from have WHERE ArtistName LIKE ? AND AlbumTitle LIKE ? AND TrackTitle LIKE ?', [release_dict['artist_name'], release_dict['rg_title'], track['title']]).fetchone() + + if not match: + match = myDB.action('SELECT Location, BitRate, Format from have WHERE TrackID=?', [track['id']]).fetchone() + + if match: + newValueDict['Location'] = match['Location'] + newValueDict['BitRate'] = match['BitRate'] + newValueDict['Format'] = match['Format'] + myDB.action('DELETE from have WHERE Location=?', [match['Location']]) + + myDB.upsert("tracks", newValueDict, controlValueDict) + + #start a search for the album + import searcher + searcher.searchNZB(rgid, False) + elif not rg_exists and not release_dict: + logger.error("ReleaseGroup does not exist in the database and did not get a valid response from MB. Skipping release.") + return + else: + logger.info('Release ' + str(rid) + " already exists in the database!") def updateFormat(): - myDB = db.DBConnection() - tracks = myDB.select('SELECT * from tracks WHERE Location IS NOT NULL and Format IS NULL') - if len(tracks) > 0: - logger.info('Finding media format for %s files' % len(tracks)) - for track in tracks: - try: - f = MediaFile(track['Location']) - except Exception, e: - logger.info("Exception from MediaFile for: " + track['Location'] + " : " + str(e)) - continue - controlValueDict = {"TrackID": track['TrackID']} - newValueDict = {"Format": f.format} - myDB.upsert("tracks", newValueDict, controlValueDict) - logger.info('Finished finding media format for %s files' % len(tracks)) - havetracks = myDB.select('SELECT * from have WHERE Location IS NOT NULL and Format IS NULL') - if len(havetracks) > 0: - logger.info('Finding media format for %s files' % len(havetracks)) - for track in havetracks: - try: - f = MediaFile(track['Location']) - except Exception, e: - logger.info("Exception from MediaFile for: " + track['Location'] + " : " + str(e)) - continue - controlValueDict = {"TrackID": track['TrackID']} - newValueDict = {"Format": f.format} - myDB.upsert("have", newValueDict, controlValueDict) - logger.info('Finished finding media format for %s files' % len(havetracks)) + myDB = db.DBConnection() + tracks = myDB.select('SELECT * from tracks WHERE Location IS NOT NULL and Format IS NULL') + if len(tracks) > 0: + logger.info('Finding media format for %s files' % len(tracks)) + for track in tracks: + try: + f = MediaFile(track['Location']) + except Exception, e: + logger.info("Exception from MediaFile for: " + track['Location'] + " : " + str(e)) + continue + controlValueDict = {"TrackID": track['TrackID']} + newValueDict = {"Format": f.format} + myDB.upsert("tracks", newValueDict, controlValueDict) + logger.info('Finished finding media format for %s files' % len(tracks)) + havetracks = myDB.select('SELECT * from have WHERE Location IS NOT NULL and Format IS NULL') + if len(havetracks) > 0: + logger.info('Finding media format for %s files' % len(havetracks)) + for track in havetracks: + try: + f = MediaFile(track['Location']) + except Exception, e: + logger.info("Exception from MediaFile for: " + track['Location'] + " : " + str(e)) + continue + controlValueDict = {"TrackID": track['TrackID']} + newValueDict = {"Format": f.format} + myDB.upsert("have", newValueDict, controlValueDict) + logger.info('Finished finding media format for %s files' % len(havetracks)) diff --git a/headphones/lastfm.py b/headphones/lastfm.py index f32f9963..84df9050 100644 --- a/headphones/lastfm.py +++ b/headphones/lastfm.py @@ -11,179 +11,179 @@ api_key = '395e6ec6bb557382fc41fde867bce66f' def getSimilar(): - - myDB = db.DBConnection() - results = myDB.select('SELECT ArtistID from artists ORDER BY HaveTracks DESC') - - artistlist = [] - - for result in results[:12]: - - url = 'http://ws.audioscrobbler.com/2.0/?method=artist.getsimilar&mbid=%s&api_key=%s' % (result['ArtistID'], api_key) - - try: - data = urllib.urlopen(url).read() - except: - time.sleep(1) - continue - - if len(data) < 200: - continue - - d = minidom.parseString(data) - node = d.documentElement - artists = d.getElementsByTagName("artist") - - for artist in artists: - namenode = artist.getElementsByTagName("name")[0].childNodes - mbidnode = artist.getElementsByTagName("mbid")[0].childNodes - - for node in namenode: - artist_name = node.data - for node in mbidnode: - artist_mbid = node.data - - try: - if not any(artist_mbid in x for x in results): - artistlist.append((artist_name, artist_mbid)) - except: - continue - - count = defaultdict(int) - - for artist, mbid in artistlist: - count[artist, mbid] += 1 - - items = count.items() - - top_list = sorted(items, key=lambda x: x[1], reverse=True)[:25] - - random.shuffle(top_list) - - myDB.action('''DELETE from lastfmcloud''') - for tuple in top_list: - artist_name, artist_mbid = tuple[0] - count = tuple[1] - myDB.action('INSERT INTO lastfmcloud VALUES( ?, ?, ?)', [artist_name, artist_mbid, count]) - + + myDB = db.DBConnection() + results = myDB.select('SELECT ArtistID from artists ORDER BY HaveTracks DESC') + + artistlist = [] + + for result in results[:12]: + + url = 'http://ws.audioscrobbler.com/2.0/?method=artist.getsimilar&mbid=%s&api_key=%s' % (result['ArtistID'], api_key) + + try: + data = urllib.urlopen(url).read() + except: + time.sleep(1) + continue + + if len(data) < 200: + continue + + d = minidom.parseString(data) + node = d.documentElement + artists = d.getElementsByTagName("artist") + + for artist in artists: + namenode = artist.getElementsByTagName("name")[0].childNodes + mbidnode = artist.getElementsByTagName("mbid")[0].childNodes + + for node in namenode: + artist_name = node.data + for node in mbidnode: + artist_mbid = node.data + + try: + if not any(artist_mbid in x for x in results): + artistlist.append((artist_name, artist_mbid)) + except: + continue + + count = defaultdict(int) + + for artist, mbid in artistlist: + count[artist, mbid] += 1 + + items = count.items() + + top_list = sorted(items, key=lambda x: x[1], reverse=True)[:25] + + random.shuffle(top_list) + + myDB.action('''DELETE from lastfmcloud''') + for tuple in top_list: + artist_name, artist_mbid = tuple[0] + count = tuple[1] + myDB.action('INSERT INTO lastfmcloud VALUES( ?, ?, ?)', [artist_name, artist_mbid, count]) + def getArtists(): - myDB = db.DBConnection() - results = myDB.select('SELECT ArtistID from artists') + myDB = db.DBConnection() + results = myDB.select('SELECT ArtistID from artists') - if not headphones.LASTFM_USERNAME: - return - - else: - username = headphones.LASTFM_USERNAME - - url = 'http://ws.audioscrobbler.com/2.0/?method=library.getartists&limit=10000&api_key=%s&user=%s' % (api_key, username) - data = urllib.urlopen(url).read() - d = minidom.parseString(data) - artists = d.getElementsByTagName("artist") - - artistlist = [] - - for artist in artists: - mbidnode = artist.getElementsByTagName("mbid")[0].childNodes + if not headphones.LASTFM_USERNAME: + return + + else: + username = headphones.LASTFM_USERNAME + + url = 'http://ws.audioscrobbler.com/2.0/?method=library.getartists&limit=10000&api_key=%s&user=%s' % (api_key, username) + data = urllib.urlopen(url).read() + d = minidom.parseString(data) + artists = d.getElementsByTagName("artist") + + artistlist = [] + + for artist in artists: + mbidnode = artist.getElementsByTagName("mbid")[0].childNodes - for node in mbidnode: - artist_mbid = node.data - - try: - if not any(artist_mbid in x for x in results): - artistlist.append(artist_mbid) - except: - continue - - from headphones import importer - - for artistid in artistlist: - importer.addArtisttoDB(artistid) - + for node in mbidnode: + artist_mbid = node.data + + try: + if not any(artist_mbid in x for x in results): + artistlist.append(artist_mbid) + except: + continue + + from headphones import importer + + for artistid in artistlist: + importer.addArtisttoDB(artistid) + def getAlbumDescription(rgid, artist, album): - - myDB = db.DBConnection() - result = myDB.select('SELECT Summary from descriptions WHERE ReleaseGroupID=?', [rgid]) - - if result: - return - - params = { "method": 'album.getInfo', - "api_key": api_key, + + myDB = db.DBConnection() + result = myDB.select('SELECT Summary from descriptions WHERE ReleaseGroupID=?', [rgid]) + + if result: + return + + params = { "method": 'album.getInfo', + "api_key": api_key, "artist": artist.encode('utf-8'), "album": album.encode('utf-8') } - searchURL = 'http://ws.audioscrobbler.com/2.0/?' + urllib.urlencode(params) - data = urllib.urlopen(searchURL).read() - - if data == 'Album not found': - return - - try: - d = minidom.parseString(data) + searchURL = 'http://ws.audioscrobbler.com/2.0/?' + urllib.urlencode(params) + data = urllib.urlopen(searchURL).read() + + if data == 'Album not found': + return + + try: + d = minidom.parseString(data) - albuminfo = d.getElementsByTagName("album") - - for item in albuminfo: - summarynode = item.getElementsByTagName("summary")[0].childNodes - contentnode = item.getElementsByTagName("content")[0].childNodes - for node in summarynode: - summary = node.data - for node in contentnode: - content = node.data - - controlValueDict = {'ReleaseGroupID': rgid} - newValueDict = {'Summary': summary, - 'Content': content} - myDB.upsert("descriptions", newValueDict, controlValueDict) - - except: - return + albuminfo = d.getElementsByTagName("album") + + for item in albuminfo: + summarynode = item.getElementsByTagName("summary")[0].childNodes + contentnode = item.getElementsByTagName("content")[0].childNodes + for node in summarynode: + summary = node.data + for node in contentnode: + content = node.data + + controlValueDict = {'ReleaseGroupID': rgid} + newValueDict = {'Summary': summary, + 'Content': content} + myDB.upsert("descriptions", newValueDict, controlValueDict) + + except: + return def getAlbumDescriptionOld(rgid, releaselist): - """ - This was a dumb way to do it - going to just use artist & album name but keeping this here - because I may use it to fetch and cache album art - """ + """ + This was a dumb way to do it - going to just use artist & album name but keeping this here + because I may use it to fetch and cache album art + """ - myDB = db.DBConnection() - result = myDB.select('SELECT Summary from descriptions WHERE ReleaseGroupID=?', [rgid]) - - if result: - return - - for release in releaselist: - - mbid = release['releaseid'] - url = 'http://ws.audioscrobbler.com/2.0/?method=album.getInfo&mbid=%s&api_key=%s' % (mbid, api_key) - data = urllib.urlopen(url).read() - - if data == 'Album not found': - continue - - try: - d = minidom.parseString(data) - - albuminfo = d.getElementsByTagName("album") - - for item in albuminfo: - summarynode = item.getElementsByTagName("summary")[0].childNodes - contentnode = item.getElementsByTagName("content")[0].childNodes - for node in summarynode: - summary = node.data - for node in contentnode: - content = node.data - - controlValueDict = {'ReleaseGroupID': rgid} - newValueDict = {'ReleaseID': mbid, - 'Summary': summary, - 'Content': content} - myDB.upsert("descriptions", newValueDict, controlValueDict) - break - - except: - continue - - \ No newline at end of file + myDB = db.DBConnection() + result = myDB.select('SELECT Summary from descriptions WHERE ReleaseGroupID=?', [rgid]) + + if result: + return + + for release in releaselist: + + mbid = release['releaseid'] + url = 'http://ws.audioscrobbler.com/2.0/?method=album.getInfo&mbid=%s&api_key=%s' % (mbid, api_key) + data = urllib.urlopen(url).read() + + if data == 'Album not found': + continue + + try: + d = minidom.parseString(data) + + albuminfo = d.getElementsByTagName("album") + + for item in albuminfo: + summarynode = item.getElementsByTagName("summary")[0].childNodes + contentnode = item.getElementsByTagName("content")[0].childNodes + for node in summarynode: + summary = node.data + for node in contentnode: + content = node.data + + controlValueDict = {'ReleaseGroupID': rgid} + newValueDict = {'ReleaseID': mbid, + 'Summary': summary, + 'Content': content} + myDB.upsert("descriptions", newValueDict, controlValueDict) + break + + except: + continue + + diff --git a/headphones/librarysync.py b/headphones/librarysync.py index eef8a90c..900cd405 100644 --- a/headphones/librarysync.py +++ b/headphones/librarysync.py @@ -8,212 +8,212 @@ from headphones import db, logger, helpers, importer def libraryScan(dir=None): - if not dir: - dir = headphones.MUSIC_DIR - - try: - 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 + if not dir: + dir = headphones.MUSIC_DIR + + try: + 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=?, Format=? WHERE TrackID=?', [None, None, None, track['TrackID']]) + 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=?, Format=? WHERE TrackID=?', [None, None, None, track['TrackID']]) - logger.info('Scanning music directory: %s' % dir) + logger.info('Scanning music directory: %s' % dir) - new_artists = [] - bitrates = [] + new_artists = [] + bitrates = [] - myDB.action('DELETE from have') - - for r,d,f in os.walk(dir): - for files in f: - # MEDIA_FORMATS = music file extensions, e.g. mp3, flac, etc - if any(files.lower().endswith('.' + x.lower()) for x in headphones.MEDIA_FORMATS): + myDB.action('DELETE from have') + + for r,d,f in os.walk(dir): + for files in f: + # MEDIA_FORMATS = music file extensions, e.g. mp3, flac, etc + if any(files.lower().endswith('.' + x.lower()) for x in headphones.MEDIA_FORMATS): - song = os.path.join(r, files) - file = unicode(os.path.join(r, files), headphones.SYS_ENCODING, errors='replace') + song = os.path.join(r, files) + file = unicode(os.path.join(r, files), headphones.SYS_ENCODING, errors='replace') - # Try to read the metadata - try: - f = MediaFile(song) + # Try to read the metadata + try: + f = MediaFile(song) - except: - logger.error('Cannot read file: ' + file) - continue - - # Grab the bitrates for the auto detect bit rate option - if f.bitrate: - bitrates.append(f.bitrate) - - # Try to find a match based on artist/album/tracktitle - if f.albumartist: - f_artist = f.albumartist - elif f.artist: - f_artist = f.artist - else: - continue - - if f_artist and f.album and f.title: + except: + logger.error('Cannot read file: ' + file) + continue + + # Grab the bitrates for the auto detect bit rate option + if f.bitrate: + bitrates.append(f.bitrate) + + # Try to find a match based on artist/album/tracktitle + if f.albumartist: + f_artist = f.albumartist + elif f.artist: + f_artist = f.artist + else: + continue + + if f_artist and f.album and f.title: - track = myDB.action('SELECT TrackID from tracks WHERE CleanName LIKE ?', [helpers.cleanName(f_artist +' '+f.album+' '+f.title)]).fetchone() - - if not track: - track = myDB.action('SELECT TrackID from tracks WHERE ArtistName LIKE ? AND AlbumTitle LIKE ? AND TrackTitle LIKE ?', [f_artist, f.album, f.title]).fetchone() - - if track: - myDB.action('UPDATE tracks SET Location=?, BitRate=?, Format=? WHERE TrackID=?', [file, f.bitrate, f.format, track['TrackID']]) - continue - - # Try to match on mbid if available and we couldn't find a match based on metadata - if f.mb_trackid: + track = myDB.action('SELECT TrackID from tracks WHERE CleanName LIKE ?', [helpers.cleanName(f_artist +' '+f.album+' '+f.title)]).fetchone() + + if not track: + track = myDB.action('SELECT TrackID from tracks WHERE ArtistName LIKE ? AND AlbumTitle LIKE ? AND TrackTitle LIKE ?', [f_artist, f.album, f.title]).fetchone() + + if track: + myDB.action('UPDATE tracks SET Location=?, BitRate=?, Format=? WHERE TrackID=?', [file, f.bitrate, f.format, track['TrackID']]) + continue + + # Try to match on mbid if available and we couldn't find a match based on metadata + if f.mb_trackid: - # Wondering if theres a better way to do this -> do one thing if the row exists, - # do something else if it doesn't - track = myDB.action('SELECT TrackID from tracks WHERE TrackID=?', [f.mb_trackid]).fetchone() - - if track: - myDB.action('UPDATE tracks SET Location=?, BitRate=?, Format=? WHERE TrackID=?', [file, f.bitrate, f.format, track['TrackID']]) - continue - - # if we can't find a match in the database on a track level, it might be a new artist or it might be on a non-mb release - new_artists.append(f_artist) - - # The have table will become the new database for unmatched tracks (i.e. tracks with no associated links in the database - myDB.action('INSERT INTO have (ArtistName, AlbumTitle, TrackNumber, TrackTitle, TrackLength, BitRate, Genre, Date, TrackID, Location, CleanName, Format) VALUES( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', [f_artist, f.album, f.track, f.title, f.length, f.bitrate, f.genre, f.date, f.mb_trackid, file, helpers.cleanName(f_artist+' '+f.album+' '+f.title), f.format]) + # Wondering if theres a better way to do this -> do one thing if the row exists, + # do something else if it doesn't + track = myDB.action('SELECT TrackID from tracks WHERE TrackID=?', [f.mb_trackid]).fetchone() + + if track: + myDB.action('UPDATE tracks SET Location=?, BitRate=?, Format=? WHERE TrackID=?', [file, f.bitrate, f.format, track['TrackID']]) + continue + + # if we can't find a match in the database on a track level, it might be a new artist or it might be on a non-mb release + new_artists.append(f_artist) + + # The have table will become the new database for unmatched tracks (i.e. tracks with no associated links in the database + myDB.action('INSERT INTO have (ArtistName, AlbumTitle, TrackNumber, TrackTitle, TrackLength, BitRate, Genre, Date, TrackID, Location, CleanName, Format) VALUES( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', [f_artist, f.album, f.track, f.title, f.length, f.bitrate, f.genre, f.date, f.mb_trackid, file, helpers.cleanName(f_artist+' '+f.album+' '+f.title), f.format]) - logger.info('Completed scanning of directory: %s' % dir) - logger.info('Checking filepaths to see if we can find any matches') + logger.info('Completed scanning of directory: %s' % dir) + logger.info('Checking filepaths to see if we can find any matches') - # Now check empty file paths to see if we can find a match based on their folder format - tracks = myDB.select('SELECT * from tracks WHERE Location IS NULL') - for track in tracks: - - release = myDB.action('SELECT * from albums WHERE AlbumID=?', [track['AlbumID']]).fetchone() + # Now check empty file paths to see if we can find a match based on their folder format + tracks = myDB.select('SELECT * from tracks WHERE Location IS NULL') + for track in tracks: + + release = myDB.action('SELECT * from albums WHERE AlbumID=?', [track['AlbumID']]).fetchone() - try: - year = release['ReleaseDate'][:4] - except TypeError: - year = '' - - artist = release['ArtistName'].replace('/', '_') - album = release['AlbumTitle'].replace('/', '_') - releasetype = release['Type'].replace('/', '_') - - if release['ArtistName'].startswith('The '): - sortname = release['ArtistName'][4:] - else: - sortname = release['ArtistName'] - - if sortname.isdigit(): - firstchar = '0-9' - else: - firstchar = sortname[0] + try: + year = release['ReleaseDate'][:4] + except TypeError: + year = '' + + artist = release['ArtistName'].replace('/', '_') + album = release['AlbumTitle'].replace('/', '_') + releasetype = release['Type'].replace('/', '_') + + if release['ArtistName'].startswith('The '): + sortname = release['ArtistName'][4:] + else: + sortname = release['ArtistName'] + + if sortname.isdigit(): + firstchar = '0-9' + else: + firstchar = sortname[0] - - albumvalues = { '$Artist': artist, - '$Album': album, - '$Year': year, - '$Type': releasetype, - '$First': firstchar, - '$artist': artist.lower(), - '$album': album.lower(), - '$year': year, - '$type': releasetype.lower(), - '$first': firstchar.lower() - } - - - folder = helpers.replace_all(headphones.FOLDER_FORMAT, albumvalues) - folder = folder.replace('./', '_/').replace(':','_').replace('?','_') - - if folder.endswith('.'): - folder = folder.replace(folder[len(folder)-1], '_') + + albumvalues = { '$Artist': artist, + '$Album': album, + '$Year': year, + '$Type': releasetype, + '$First': firstchar, + '$artist': artist.lower(), + '$album': album.lower(), + '$year': year, + '$type': releasetype.lower(), + '$first': firstchar.lower() + } + + + folder = helpers.replace_all(headphones.FOLDER_FORMAT, albumvalues) + folder = folder.replace('./', '_/').replace(':','_').replace('?','_') + + if folder.endswith('.'): + folder = folder.replace(folder[len(folder)-1], '_') - if not track['TrackNumber']: - tracknumber = '' - else: - tracknumber = '%02d' % track['TrackNumber'] - - title = track['TrackTitle'] - - trackvalues = { '$Track': tracknumber, - '$Title': title, - '$Artist': release['ArtistName'], - '$Album': release['AlbumTitle'], - '$Year': year, - '$track': tracknumber, - '$title': title.lower(), - '$artist': release['ArtistName'].lower(), - '$album': release['AlbumTitle'].lower(), - '$year': year - } - - new_file_name = helpers.replace_all(headphones.FILE_FORMAT, trackvalues).replace('/','_') + '.*' - - 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)).encode(headphones.SYS_ENCODING, 'replace') + if not track['TrackNumber']: + tracknumber = '' + else: + tracknumber = '%02d' % track['TrackNumber'] + + title = track['TrackTitle'] + + trackvalues = { '$Track': tracknumber, + '$Title': title, + '$Artist': release['ArtistName'], + '$Album': release['AlbumTitle'], + '$Year': year, + '$track': tracknumber, + '$title': title.lower(), + '$artist': release['ArtistName'].lower(), + '$album': release['AlbumTitle'].lower(), + '$year': year + } + + new_file_name = helpers.replace_all(headphones.FILE_FORMAT, trackvalues).replace('/','_') + '.*' + + 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)).encode(headphones.SYS_ENCODING, 'replace') - match = glob.glob(full_path_to_file) - - if match: + 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') + 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=?', [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: - f = MediaFile(match[0]) - f.mb_trackid = track['TrackID'] - f.save() - myDB.action('UPDATE tracks SET BitRate=?, Format=? WHERE TrackID=?', [f.bitrate, f.format, track['TrackID']]) + 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: + f = MediaFile(match[0]) + f.mb_trackid = track['TrackID'] + f.save() + myDB.action('UPDATE tracks SET BitRate=?, Format=? WHERE TrackID=?', [f.bitrate, f.format, track['TrackID']]) - logger.debug('Wrote mbid to track: %s' % match[0]) + logger.debug('Wrote mbid to track: %s' % match[0]) - except: - logger.error('Error embedding track id into: %s' % match[0]) - continue + except: + logger.error('Error embedding track id into: %s' % match[0]) + continue - logger.info('Done checking empty filepaths') - logger.info('Done syncing library with directory: %s' % dir) - - # Clean up the new artist list - unique_artists = {}.fromkeys(new_artists).keys() - current_artists = myDB.select('SELECT ArtistName, ArtistID from artists') - - artist_list = [f for f in unique_artists if f.lower() not in [x[0].lower() for x in current_artists]] - - # Update track counts - logger.info('Updating track counts') + logger.info('Done checking empty filepaths') + logger.info('Done syncing library with directory: %s' % dir) + + # Clean up the new artist list + unique_artists = {}.fromkeys(new_artists).keys() + current_artists = myDB.select('SELECT ArtistName, ArtistID from artists') + + artist_list = [f for f in unique_artists if f.lower() not in [x[0].lower() for x in current_artists]] + + # Update track counts + logger.info('Updating track counts') - for artist in current_artists: - havetracks = len(myDB.select('SELECT TrackTitle from tracks WHERE ArtistID like ? AND Location IS NOT NULL', [artist['ArtistID']])) + len(myDB.select('SELECT TrackTitle from have WHERE ArtistName like ?', [artist['ArtistName']])) - myDB.action('UPDATE artists SET HaveTracks=? WHERE ArtistID=?', [havetracks, artist['ArtistID']]) - - logger.info('Found %i new artists' % len(artist_list)) + for artist in current_artists: + havetracks = len(myDB.select('SELECT TrackTitle from tracks WHERE ArtistID like ? AND Location IS NOT NULL', [artist['ArtistID']])) + len(myDB.select('SELECT TrackTitle from have WHERE ArtistName like ?', [artist['ArtistName']])) + myDB.action('UPDATE artists SET HaveTracks=? WHERE ArtistID=?', [havetracks, artist['ArtistID']]) + + logger.info('Found %i new artists' % len(artist_list)) - if len(artist_list): - if headphones.ADD_ARTISTS: - logger.info('Importing %i new artists' % len(artist_list)) - importer.artistlist_to_mbids(artist_list) - else: - logger.info('To add these artists, go to Manage->Manage New Artists') - headphones.NEW_ARTISTS = artist_list - - if headphones.DETECT_BITRATE: - headphones.PREFERRED_BITRATE = sum(bitrates)/len(bitrates)/1000 + if len(artist_list): + if headphones.ADD_ARTISTS: + logger.info('Importing %i new artists' % len(artist_list)) + importer.artistlist_to_mbids(artist_list) + else: + logger.info('To add these artists, go to Manage->Manage New Artists') + headphones.NEW_ARTISTS = artist_list + + if headphones.DETECT_BITRATE: + headphones.PREFERRED_BITRATE = sum(bitrates)/len(bitrates)/1000 diff --git a/headphones/logger.py b/headphones/logger.py index 97c435bc..758d1233 100644 --- a/headphones/logger.py +++ b/headphones/logger.py @@ -13,69 +13,69 @@ MAX_FILES = 5 # Simple rotating log handler that uses RotatingFileHandler class RotatingLogger(object): - def __init__(self, filename, max_size, max_files): - - self.filename = filename - self.max_size = max_size - self.max_files = max_files - - - def initLogger(self, verbose=1): - - l = logging.getLogger('headphones') - l.setLevel(logging.DEBUG) - - self.filename = os.path.join(headphones.LOG_DIR, self.filename) - - filehandler = handlers.RotatingFileHandler(self.filename, maxBytes=self.max_size, backupCount=self.max_files) - filehandler.setLevel(logging.DEBUG) - - fileformatter = logging.Formatter('%(asctime)s - %(levelname)-7s :: %(message)s', '%d-%b-%Y %H:%M:%S') - - filehandler.setFormatter(fileformatter) - l.addHandler(filehandler) - - if verbose: - consolehandler = logging.StreamHandler() - if verbose == 1: - consolehandler.setLevel(logging.INFO) - if verbose == 2: - consolehandler.setLevel(logging.DEBUG) - consoleformatter = logging.Formatter('%(asctime)s - %(levelname)s :: %(message)s', '%d-%b-%Y %H:%M:%S') - consolehandler.setFormatter(consoleformatter) - l.addHandler(consolehandler) - - def log(self, message, level): + def __init__(self, filename, max_size, max_files): + + self.filename = filename + self.max_size = max_size + self.max_files = max_files + + + def initLogger(self, verbose=1): + + l = logging.getLogger('headphones') + l.setLevel(logging.DEBUG) + + self.filename = os.path.join(headphones.LOG_DIR, self.filename) + + filehandler = handlers.RotatingFileHandler(self.filename, maxBytes=self.max_size, backupCount=self.max_files) + filehandler.setLevel(logging.DEBUG) + + fileformatter = logging.Formatter('%(asctime)s - %(levelname)-7s :: %(message)s', '%d-%b-%Y %H:%M:%S') + + filehandler.setFormatter(fileformatter) + l.addHandler(filehandler) + + if verbose: + consolehandler = logging.StreamHandler() + if verbose == 1: + consolehandler.setLevel(logging.INFO) + if verbose == 2: + consolehandler.setLevel(logging.DEBUG) + consoleformatter = logging.Formatter('%(asctime)s - %(levelname)s :: %(message)s', '%d-%b-%Y %H:%M:%S') + consolehandler.setFormatter(consoleformatter) + l.addHandler(consolehandler) + + def log(self, message, level): - logger = logging.getLogger('headphones') - - threadname = threading.currentThread().getName() - - if level != 'DEBUG': - headphones.LOG_LIST.insert(0, (helpers.now(), message, level, threadname)) - - message = threadname + ' : ' + message + logger = logging.getLogger('headphones') + + threadname = threading.currentThread().getName() + + if level != 'DEBUG': + headphones.LOG_LIST.insert(0, (helpers.now(), message, level, threadname)) + + message = threadname + ' : ' + message - if level == 'DEBUG': - logger.debug(message) - elif level == 'INFO': - logger.info(message) - elif level == 'WARNING': - logger.warn(message) - else: - logger.error(message) + if level == 'DEBUG': + logger.debug(message) + elif level == 'INFO': + logger.info(message) + elif level == 'WARNING': + logger.warn(message) + else: + logger.error(message) headphones_log = RotatingLogger('headphones.log', MAX_SIZE, MAX_FILES) def debug(message): - headphones_log.log(message, level='DEBUG') + headphones_log.log(message, level='DEBUG') def info(message): - headphones_log.log(message, level='INFO') - + headphones_log.log(message, level='INFO') + def warn(message): - headphones_log.log(message, level='WARNING') - + headphones_log.log(message, level='WARNING') + def error(message): - headphones_log.log(message, level='ERROR') - + headphones_log.log(message, level='ERROR') + diff --git a/headphones/lyrics.py b/headphones/lyrics.py index 3c95676e..6ce42b57 100644 --- a/headphones/lyrics.py +++ b/headphones/lyrics.py @@ -7,53 +7,53 @@ from headphones import logger def getLyrics(artist, song): - params = { "artist": artist.encode('utf-8'), - "song": song.encode('utf-8'), - "fmt": 'xml' + params = { "artist": artist.encode('utf-8'), + "song": song.encode('utf-8'), + "fmt": 'xml' } - searchURL = 'http://lyrics.wikia.com/api.php?' + urllib.urlencode(params) - - try: - data = urllib.urlopen(searchURL).read() - except Exception, e: - logger.warn('Error opening: %s. Error: %s' % (searchURL, e)) - return - - try: - parseddata = minidom.parseString(data) - except Exception, e: - logger.warn('Error parsing data from url: %s. Error: %s' % (searchURL, e)) - return - - url = parseddata.getElementsByTagName("url") - - if url: - lyricsurl = url[0].firstChild.nodeValue - else: - logger.info('No lyrics found for %s - %s' % (artist, song)) - return - - try: - lyricspage = urllib.urlopen(lyricsurl).read() - except Exception, e: - logger.warn('Error fetching lyrics from: %s. Error: %s' % (lyricsurl, e)) - return - - m = re.compile('''
.*?
(.*?) %s' % (downloaded_track, new_file_name)) - try: - os.rename(downloaded_track, new_file) - except Exception, e: - logger.error('Error renaming file: %s. Error: %s' % (downloaded_track, e)) - continue - + logger.debug('Renaming %s ---> %s' % (downloaded_track, new_file_name)) + try: + os.rename(downloaded_track, new_file) + except Exception, e: + logger.error('Error renaming file: %s. Error: %s' % (downloaded_track, e)) + continue + def updateHave(albumpath): - results = [] - - 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): - results.append(os.path.join(r, files)) - - if results: - - myDB = db.DBConnection() - - for song in results: - try: - f = MediaFile(song) - #logger.debug('Reading: %s' % song.decode('UTF-8')) - except: - logger.warn('Could not read file: %s' % song) - continue - else: - if f.albumartist: - artist = f.albumartist - elif f.artist: - artist = f.artist - else: - continue - - myDB.action('UPDATE tracks SET Location=?, BitRate=?, Format=? WHERE ArtistName LIKE ? AND AlbumTitle LIKE ? AND TrackTitle LIKE ?', [unicode(song, headphones.SYS_ENCODING, errors="replace"), f.bitrate, f.format, artist, f.album, f.title]) - + results = [] + + 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): + results.append(os.path.join(r, files)) + + if results: + + myDB = db.DBConnection() + + for song in results: + try: + f = MediaFile(song) + #logger.debug('Reading: %s' % song.decode('UTF-8')) + except: + logger.warn('Could not read file: %s' % song) + continue + else: + if f.albumartist: + artist = f.albumartist + elif f.artist: + artist = f.artist + else: + continue + + myDB.action('UPDATE tracks SET Location=?, BitRate=?, Format=? WHERE ArtistName LIKE ? AND AlbumTitle LIKE ? AND TrackTitle LIKE ?', [unicode(song, headphones.SYS_ENCODING, errors="replace"), f.bitrate, f.format, artist, f.album, f.title]) + def renameUnprocessedFolder(albumpath): - - i = 0 - while True: - if i == 0: - new_folder_name = albumpath + ' (Unprocessed)' - else: - new_folder_name = albumpath + ' (Unprocessed)[%i]' % i - - if os.path.exists(new_folder_name): - i += 1 - - else: - os.rename(albumpath, new_folder_name) - return - + + i = 0 + while True: + if i == 0: + new_folder_name = albumpath + ' (Unprocessed)' + else: + new_folder_name = albumpath + ' (Unprocessed)[%i]' % i + + if os.path.exists(new_folder_name): + i += 1 + + else: + os.rename(albumpath, new_folder_name) + return + def forcePostProcess(): - - if not headphones.DOWNLOAD_DIR: - logger.error('No DOWNLOAD_DIR has been set. Set "Music Download Directory:" to your SAB download directory on the settings page.') - return - else: - processing = "nzb" - processing_next = "torrent" - while processing != "done": - if headphones.DOWNLOAD_DIR and processing == "nzb": - download_dir = headphones.DOWNLOAD_DIR.encode('utf-8') - if not headphones.DOWNLOAD_TORRENT_DIR: - processing_next = "done" - if headphones.DOWNLOAD_TORRENT_DIR and processing == "torrent": - download_dir = headphones.DOWNLOAD_TORRENT_DIR.encode('utf-8') - processing_next = "done" - if not headphones.DOWNLOAD_DIR and processing == "nzb": - download_dir = headphones.DOWNLOAD_TORRENT_DIR.encode('utf-8') - processing_next = "done" - - 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 to process' % len(folders)) - pass - else: - logger.info('Found no folders to process in: %s' % download_dir) - return - - # Parse the folder names to get artist album info - 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) - except: - logger.info("Couldn't parse " + folder + " into any valid format.") - continue - if name and album and year: - - myDB = db.DBConnection() - 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) - else: - logger.info('Querying MusicBrainz for the release group id for: %s - %s' % (name, album)) - from headphones import mb - try: - 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)) - processing = processing_next + + if not headphones.DOWNLOAD_DIR: + logger.error('No DOWNLOAD_DIR has been set. Set "Music Download Directory:" to your SAB download directory on the settings page.') + return + else: + processing = "nzb" + processing_next = "torrent" + while processing != "done": + if headphones.DOWNLOAD_DIR and processing == "nzb": + download_dir = headphones.DOWNLOAD_DIR.encode('utf-8') + if not headphones.DOWNLOAD_TORRENT_DIR: + processing_next = "done" + if headphones.DOWNLOAD_TORRENT_DIR and processing == "torrent": + download_dir = headphones.DOWNLOAD_TORRENT_DIR.encode('utf-8') + processing_next = "done" + if not headphones.DOWNLOAD_DIR and processing == "nzb": + download_dir = headphones.DOWNLOAD_TORRENT_DIR.encode('utf-8') + processing_next = "done" + + 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 to process' % len(folders)) + pass + else: + logger.info('Found no folders to process in: %s' % download_dir) + return + + # Parse the folder names to get artist album info + 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) + except: + logger.info("Couldn't parse " + folder + " into any valid format.") + continue + if name and album and year: + + myDB = db.DBConnection() + 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) + else: + logger.info('Querying MusicBrainz for the release group id for: %s - %s' % (name, album)) + from headphones import mb + try: + 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)) + processing = processing_next diff --git a/headphones/sab.py b/headphones/sab.py index 98085ba2..1f5a5889 100644 --- a/headphones/sab.py +++ b/headphones/sab.py @@ -66,7 +66,7 @@ def sendNZB(nzb): multiPartParams = {"nzbfile": (nzb.name+".nzb", nzb.extraInfo[0])} if not headphones.SAB_HOST.startswith('http'): - headphones.SAB_HOST = 'http://' + headphones.SAB_HOST + headphones.SAB_HOST = 'http://' + headphones.SAB_HOST if headphones.SAB_HOST.endswith('/'): headphones.SAB_HOST = headphones.SAB_HOST[0:len(headphones.SAB_HOST)-1] diff --git a/headphones/searcher.py b/headphones/searcher.py index 0ed33eaf..4f8166c9 100644 --- a/headphones/searcher.py +++ b/headphones/searcher.py @@ -126,9 +126,9 @@ def searchNZB(albumid=None, new=False, losslessOnly=False): if albums[0] in albums[1] or len(albums[0]) < 4 or len(albums[1]) < 4: term = cleanartist + ' ' + cleanalbum + ' ' + year elif albums[0] == 'Various Artists': - term = cleanalbum + ' ' + year + term = cleanalbum + ' ' + year else: - term = cleanartist + ' ' + cleanalbum + term = cleanartist + ' ' + cleanalbum # Replace bad characters in the term and unicode it term = re.sub('[\.\-\/]', ' ', term).encode('utf-8') @@ -152,7 +152,7 @@ def searchNZB(albumid=None, new=False, losslessOnly=False): # hopefully this will fix it for now. If you notice anything else it gets stuck on, please post it # on Github so it can be added if term.lower().startswith("the "): - term = term[4:] + term = term[4:] params = { "page": "download", @@ -169,26 +169,26 @@ def searchNZB(albumid=None, new=False, losslessOnly=False): searchURL = "http://rss.nzbmatrix.com/rss.php?" + urllib.urlencode(params) logger.info(u'Parsing results from NZBMatrix' % searchURL) try: - data = urllib2.urlopen(searchURL, timeout=20).read() + data = urllib2.urlopen(searchURL, timeout=20).read() except urllib2.URLError, e: - logger.warn('Error fetching data from NZBMatrix: %s' % e) - data = False - + logger.warn('Error fetching data from NZBMatrix: %s' % e) + data = False + if data: - d = feedparser.parse(data) - - for item in d.entries: - try: - url = item.link - title = item.title - size = int(item.links[1]['length']) - - resultlist.append((title, size, url, provider)) - logger.info('Found %s. Size: %s' % (title, helpers.bytes_to_mb(size))) - - except AttributeError, e: - logger.info(u"No results found from NZBMatrix for %s" % term) + d = feedparser.parse(data) + + for item in d.entries: + try: + url = item.link + title = item.title + size = int(item.links[1]['length']) + + resultlist.append((title, size, url, provider)) + logger.info('Found %s. Size: %s' % (title, helpers.bytes_to_mb(size))) + + except AttributeError, e: + logger.info(u"No results found from NZBMatrix for %s" % term) if headphones.NEWZNAB: provider = "newznab" @@ -211,31 +211,31 @@ def searchNZB(albumid=None, new=False, losslessOnly=False): logger.info(u'Parsing results from %s' % (searchURL, headphones.NEWZNAB_HOST)) try: - data = urllib2.urlopen(searchURL, timeout=20).read() + data = urllib2.urlopen(searchURL, timeout=20).read() except urllib2.URLError, e: - logger.warn('Error fetching data from %s: %s' % (headphones.NEWZNAB_HOST, e)) - data = False - + logger.warn('Error fetching data from %s: %s' % (headphones.NEWZNAB_HOST, e)) + data = False + if data: - - d = feedparser.parse(data) - - if not len(d.entries): - logger.info(u"No results found from %s for %s" % (headphones.NEWZNAB_HOST, term)) - pass - - else: - for item in d.entries: - try: - url = item.link - title = item.title - size = int(item.links[1]['length']) - - resultlist.append((title, size, url, provider)) - logger.info('Found %s. Size: %s' % (title, helpers.bytes_to_mb(size))) - - except Exception, e: - logger.error(u"An unknown error occured trying to parse the feed: %s" % e) + + d = feedparser.parse(data) + + if not len(d.entries): + logger.info(u"No results found from %s for %s" % (headphones.NEWZNAB_HOST, term)) + pass + + else: + for item in d.entries: + try: + url = item.link + title = item.title + size = int(item.links[1]['length']) + + resultlist.append((title, size, url, provider)) + logger.info('Found %s. Size: %s' % (title, helpers.bytes_to_mb(size))) + + except Exception, e: + logger.error(u"An unknown error occured trying to parse the feed: %s" % e) if headphones.NZBSORG: provider = "nzbsorg" @@ -258,31 +258,31 @@ def searchNZB(albumid=None, new=False, losslessOnly=False): logger.info(u'Parsing results from nzbs.org' % searchURL) try: - data = urllib2.urlopen(searchURL, timeout=20).read() + data = urllib2.urlopen(searchURL, timeout=20).read() except urllib2.URLError, e: - logger.warn('Error fetching data from nzbs.org: %s' % e) - data = False - + logger.warn('Error fetching data from nzbs.org: %s' % e) + data = False + if data: - - d = feedparser.parse(data) - - if not len(d.entries): - logger.info(u"No results found from nzbs.org for %s" % term) - pass - - else: - for item in d.entries: - try: - url = item.link - title = item.title - size = int(item.links[1]['length']) - - resultlist.append((title, size, url, provider)) - logger.info('Found %s. Size: %s' % (title, helpers.bytes_to_mb(size))) - - except Exception, e: - logger.error(u"An unknown error occured trying to parse the feed: %s" % e) + + d = feedparser.parse(data) + + if not len(d.entries): + logger.info(u"No results found from nzbs.org for %s" % term) + pass + + else: + for item in d.entries: + try: + url = item.link + title = item.title + size = int(item.links[1]['length']) + + resultlist.append((title, size, url, provider)) + logger.info('Found %s. Size: %s' % (title, helpers.bytes_to_mb(size))) + + except Exception, e: + logger.error(u"An unknown error occured trying to parse the feed: %s" % e) if headphones.NEWZBIN: provider = "newzbin" @@ -300,9 +300,9 @@ def searchNZB(albumid=None, new=False, losslessOnly=False): params = { "fpn": "p", 'u_nfo_posts_only': 0, - 'u_url_posts_only': 0, - 'u_comment_posts_only': 0, - 'u_show_passworded': 0, + 'u_url_posts_only': 0, + 'u_comment_posts_only': 0, + 'u_show_passworded': 0, "searchaction": "Search", #"dl": 1, "category": categories, @@ -403,28 +403,28 @@ def searchNZB(albumid=None, new=False, losslessOnly=False): if new: - while True: - - if len(nzblist): - - alreadydownloaded = myDB.select('SELECT * from snatched WHERE URL=?', [nzblist[0][2]]) - - if len(alreadydownloaded): - logger.info('%s has already been downloaded. Skipping.' % nzblist[0][0]) - nzblist.pop(0) - - else: - break - else: - logger.info('No more results found for %s' % term) - return "none" + while True: + + if len(nzblist): + + alreadydownloaded = myDB.select('SELECT * from snatched WHERE URL=?', [nzblist[0][2]]) + + if len(alreadydownloaded): + logger.info('%s has already been downloaded. Skipping.' % nzblist[0][0]) + nzblist.pop(0) + + else: + break + else: + logger.info('No more results found for %s' % term) + return "none" logger.info(u"Pre-processing result") (data, bestqual) = preprocess(nzblist) if data and bestqual: - logger.info(u'Found best result: %s - %s' % (bestqual[2], bestqual[0], helpers.bytes_to_mb(bestqual[1]))) + logger.info(u'Found best result: %s - %s' % (bestqual[2], bestqual[0], helpers.bytes_to_mb(bestqual[1]))) nzb_folder_name = '%s - %s [%s]' % (helpers.latinToAscii(albums[0]).encode('UTF-8').replace('/', '_'), helpers.latinToAscii(albums[1]).encode('UTF-8').replace('/', '_'), year) if headphones.SAB_HOST and not headphones.BLACKHOLE: @@ -455,9 +455,9 @@ def searchNZB(albumid=None, new=False, losslessOnly=False): def verifyresult(title, artistterm, term): - - title = re.sub('[\.\-\/\_]', ' ', title) - + + title = re.sub('[\.\-\/\_]', ' ', title) + #if artistterm != 'Various Artists': # # if not re.search('^' + re.escape(artistterm), title, re.IGNORECASE): @@ -472,23 +472,23 @@ def verifyresult(title, artistterm, term): #another attempt to weed out substrings. We don't want "Vol III" when we were looking for "Vol II" - tokens = re.split('\W', term, re.IGNORECASE | re.UNICODE) - for token in tokens: + tokens = re.split('\W', term, re.IGNORECASE | re.UNICODE) + for token in tokens: - if not token: - continue - if token == 'Various' or token == 'Artists' or token == 'VA': - continue - if not re.search('(?:\W|^)+' + token + '(?:\W|$)+', title, re.IGNORECASE | re.UNICODE): - cleantoken = ''.join(c for c in token if c not in string.punctuation) - if not not re.search('(?:\W|^)+' + cleantoken + '(?:\W|$)+', title, re.IGNORECASE | re.UNICODE): - dic = {'!':'i', '$':'s'} - dumbtoken = helpers.replace_all(token, dic) - if not not re.search('(?:\W|^)+' + dumbtoken + '(?:\W|$)+', title, re.IGNORECASE | re.UNICODE): - logger.info("Removed from results: " + title + " (missing tokens: " + token + " and " + cleantoken + ")") - return False - - return True + if not token: + continue + if token == 'Various' or token == 'Artists' or token == 'VA': + continue + if not re.search('(?:\W|^)+' + token + '(?:\W|$)+', title, re.IGNORECASE | re.UNICODE): + cleantoken = ''.join(c for c in token if c not in string.punctuation) + if not not re.search('(?:\W|^)+' + cleantoken + '(?:\W|$)+', title, re.IGNORECASE | re.UNICODE): + dic = {'!':'i', '$':'s'} + dumbtoken = helpers.replace_all(token, dic) + if not not re.search('(?:\W|^)+' + dumbtoken + '(?:\W|$)+', title, re.IGNORECASE | re.UNICODE): + logger.info("Removed from results: " + title + " (missing tokens: " + token + " and " + cleantoken + ")") + return False + + return True def getresultNZB(result): @@ -521,7 +521,7 @@ def preprocess(resultlist): usenet_retention = 2000 else: usenet_retention = int(headphones.USENET_RETENTION) - + for result in resultlist: nzb = getresultNZB(result) if nzb: @@ -581,9 +581,9 @@ def searchTorrent(albumid=None, new=False, losslessOnly=False): if albums[0] in albums[1] or len(albums[0]) < 4 or len(albums[1]) < 4: term = cleanartist + ' ' + cleanalbum + ' ' + year elif albums[0] == 'Various Artists': - term = cleanalbum + ' ' + year + term = cleanalbum + ' ' + year else: - term = cleanartist + ' ' + cleanalbum + term = cleanartist + ' ' + cleanalbum # Replace bad characters in the term and unicode it term = re.sub('[\.\-\/]', ' ', term).encode('utf-8') @@ -619,51 +619,50 @@ def searchTorrent(albumid=None, new=False, losslessOnly=False): searchURL = providerurl + "/?%s" % urllib.urlencode(params) try: - data = urllib2.urlopen(searchURL, timeout=20).read() + data = urllib2.urlopen(searchURL, timeout=20).read() except urllib2.URLError, e: - logger.warn('Error fetching data from %s: %s' % (provider, e)) - data = False + logger.warn('Error fetching data from %s: %s' % (provider, e)) + data = False if data: - - d = feedparser.parse(data) - if not len(d.entries): - logger.info(u"No results found from %s for %s" % (provider, term)) - pass - - else: - for item in d.entries: - try: - rightformat = True - title = item.title - seeders = item.seeds - url = item.links[1]['url'] - size = int(item.links[1]['length']) - try: - if format == "2": - request = urllib2.Request(url) - request.add_header('Accept-encoding', 'gzip') - response = urllib2.urlopen(request) - if response.info().get('Content-Encoding') == 'gzip': - buf = StringIO( response.read()) - f = gzip.GzipFile(fileobj=buf) - torrent = f.read() - else: - torrent = response.read() - if int(torrent.find(".mp3")) > 0 and int(torrent.find(".flac")) < 1: - rightformat = False - except Exception, e: - rightformat = False - if rightformat == True and size < maxsize and minimumseeders < int(seeders): - resultlist.append((title, size, url, provider)) - logger.info('Found %s. Size: %s' % (title, helpers.bytes_to_mb(size))) - else: - logger.info('%s is larger than the maxsize, the wrong format or has to little seeders for this category, skipping. (Size: %i bytes, Seeders: %i, Format: %s)' % (title, size, int(seeders), rightformat)) - - except Exception, e: - logger.error(u"An unknown error occured in the KAT parser: %s" % e) + + d = feedparser.parse(data) + if not len(d.entries): + logger.info(u"No results found from %s for %s" % (provider, term)) + pass + + else: + for item in d.entries: + try: + rightformat = True + title = item.title + seeders = item.seeds + url = item.links[1]['url'] + size = int(item.links[1]['length']) + try: + if format == "2": + request = urllib2.Request(url) + request.add_header('Accept-encoding', 'gzip') + response = urllib2.urlopen(request) + if response.info().get('Content-Encoding') == 'gzip': + buf = StringIO( response.read()) + f = gzip.GzipFile(fileobj=buf) + torrent = f.read() + else: + torrent = response.read() + if int(torrent.find(".mp3")) > 0 and int(torrent.find(".flac")) < 1: + rightformat = False + except Exception, e: + rightformat = False + if rightformat == True and size < maxsize and minimumseeders < int(seeders): + resultlist.append((title, size, url, provider)) + logger.info('Found %s. Size: %s' % (title, helpers.bytes_to_mb(size))) + else: + logger.info('%s is larger than the maxsize, the wrong format or has to little seeders for this category, skipping. (Size: %i bytes, Seeders: %i, Format: %s)' % (title, size, int(seeders), rightformat)) + + except Exception, e: + logger.error(u"An unknown error occured in the KAT parser: %s" % e) - if headphones.ISOHUNT: provider = "ISOhunt" providerurl = url_fix("http://isohunt.com/js/rss/" + term) @@ -687,54 +686,54 @@ def searchTorrent(albumid=None, new=False, losslessOnly=False): searchURL = providerurl + "?%s" % urllib.urlencode(params) try: - data = urllib2.urlopen(searchURL, timeout=20).read() + data = urllib2.urlopen(searchURL, timeout=20).read() except urllib2.URLError, e: - logger.warn('Error fetching data from %s: %s' % (provider, e)) - data = False + logger.warn('Error fetching data from %s: %s' % (provider, e)) + data = False if data: - - d = feedparser.parse(data) - if not len(d.entries): - logger.info(u"No results found from %s for %s" % (provider, term)) - pass - - else: - for item in d.entries: - try: - rightformat = True - title = re.sub(r"(?<= \[)(.+)(?=\])","",item.title) - title = title.replace("[]","") - sxstart = item.description.find("Seeds: ") + 7 - seeds = "" - while item.description[sxstart:sxstart + 1] != " ": - seeds = seeds + item.description[sxstart:sxstart + 1] - sxstart = sxstart + 1 - url = item.links[1]['url'] - size = int(item.links[1]['length']) - try: - if format == "2": - request = urllib2.Request(url) - request.add_header('Accept-encoding', 'gzip') - response = urllib2.urlopen(request) - if response.info().get('Content-Encoding') == 'gzip': - buf = StringIO( response.read()) - f = gzip.GzipFile(fileobj=buf) - torrent = f.read() - else: - torrent = response.read() - if int(torrent.find(".mp3")) > 0 and int(torrent.find(".flac")) < 1: - rightformat = False - except Exception, e: - rightformat = False - if rightformat == True and size < maxsize and minimumseeders < seeds: - resultlist.append((title, size, url, provider)) - logger.info('Found %s. Size: %s' % (title, helpers.bytes_to_mb(size))) - else: - logger.info('%s is larger than the maxsize, the wrong format or has to little seeders for this category, skipping. (Size: %i bytes, Seeders: %i, Format: %s)' % (title, size, int(seeds), rightformat)) - - except Exception, e: - logger.error(u"An unknown error occured in the ISOhunt parser: %s" % e) + + d = feedparser.parse(data) + if not len(d.entries): + logger.info(u"No results found from %s for %s" % (provider, term)) + pass + + else: + for item in d.entries: + try: + rightformat = True + title = re.sub(r"(?<= \[)(.+)(?=\])","",item.title) + title = title.replace("[]","") + sxstart = item.description.find("Seeds: ") + 7 + seeds = "" + while item.description[sxstart:sxstart + 1] != " ": + seeds = seeds + item.description[sxstart:sxstart + 1] + sxstart = sxstart + 1 + url = item.links[1]['url'] + size = int(item.links[1]['length']) + try: + if format == "2": + request = urllib2.Request(url) + request.add_header('Accept-encoding', 'gzip') + response = urllib2.urlopen(request) + if response.info().get('Content-Encoding') == 'gzip': + buf = StringIO( response.read()) + f = gzip.GzipFile(fileobj=buf) + torrent = f.read() + else: + torrent = response.read() + if int(torrent.find(".mp3")) > 0 and int(torrent.find(".flac")) < 1: + rightformat = False + except Exception, e: + rightformat = False + if rightformat == True and size < maxsize and minimumseeders < seeds: + resultlist.append((title, size, url, provider)) + logger.info('Found %s. Size: %s' % (title, helpers.bytes_to_mb(size))) + else: + logger.info('%s is larger than the maxsize, the wrong format or has to little seeders for this category, skipping. (Size: %i bytes, Seeders: %i, Format: %s)' % (title, size, int(seeds), rightformat)) + + except Exception, e: + logger.error(u"An unknown error occured in the ISOhunt parser: %s" % e) if headphones.MININOVA: provider = "Mininova" @@ -755,53 +754,53 @@ def searchTorrent(albumid=None, new=False, losslessOnly=False): searchURL = providerurl try: - data = urllib2.urlopen(searchURL, timeout=20).read() + data = urllib2.urlopen(searchURL, timeout=20).read() except urllib2.URLError, e: - logger.warn('Error fetching data from %s: %s' % (provider, e)) - data = False + logger.warn('Error fetching data from %s: %s' % (provider, e)) + data = False if data: - - d = feedparser.parse(data) - if not len(d.entries): - logger.info(u"No results found from %s for %s" % (provider, term)) - pass - - else: - for item in d.entries: - try: - rightformat = True - title = item.title - sxstart = item.description.find("Ratio: ") + 7 - seeds = "" - while item.description[sxstart:sxstart + 1] != " ": - seeds = seeds + item.description[sxstart:sxstart + 1] - sxstart = sxstart + 1 - url = item.links[1]['url'] - size = int(item.links[1]['length']) - try: - if format == "2": - request = urllib2.Request(url) - request.add_header('Accept-encoding', 'gzip') - response = urllib2.urlopen(request) - if response.info().get('Content-Encoding') == 'gzip': - buf = StringIO( response.read()) - f = gzip.GzipFile(fileobj=buf) - torrent = f.read() - else: - torrent = response.read() - if int(torrent.find(".mp3")) > 0 and int(torrent.find(".flac")) < 1: - rightformat = False - except Exception, e: - rightformat = False - if rightformat == True and size < maxsize and minimumseeders < seeds: - resultlist.append((title, size, url, provider)) - logger.info('Found %s. Size: %s' % (title, helpers.bytes_to_mb(size))) - else: - logger.info('%s is larger than the maxsize, the wrong format or has to little seeders for this category, skipping. (Size: %i bytes, Seeders: %i, Format: %s)' % (title, size, int(seeds), rightformat)) - - except Exception, e: - logger.error(u"An unknown error occured in the MiniNova Parser: %s" % e) + + d = feedparser.parse(data) + if not len(d.entries): + logger.info(u"No results found from %s for %s" % (provider, term)) + pass + + else: + for item in d.entries: + try: + rightformat = True + title = item.title + sxstart = item.description.find("Ratio: ") + 7 + seeds = "" + while item.description[sxstart:sxstart + 1] != " ": + seeds = seeds + item.description[sxstart:sxstart + 1] + sxstart = sxstart + 1 + url = item.links[1]['url'] + size = int(item.links[1]['length']) + try: + if format == "2": + request = urllib2.Request(url) + request.add_header('Accept-encoding', 'gzip') + response = urllib2.urlopen(request) + if response.info().get('Content-Encoding') == 'gzip': + buf = StringIO( response.read()) + f = gzip.GzipFile(fileobj=buf) + torrent = f.read() + else: + torrent = response.read() + if int(torrent.find(".mp3")) > 0 and int(torrent.find(".flac")) < 1: + rightformat = False + except Exception, e: + rightformat = False + if rightformat == True and size < maxsize and minimumseeders < seeds: + resultlist.append((title, size, url, provider)) + logger.info('Found %s. Size: %s' % (title, helpers.bytes_to_mb(size))) + else: + logger.info('%s is larger than the maxsize, the wrong format or has to little seeders for this category, skipping. (Size: %i bytes, Seeders: %i, Format: %s)' % (title, size, int(seeds), rightformat)) + + except Exception, e: + logger.error(u"An unknown error occured in the MiniNova Parser: %s" % e) @@ -847,28 +846,28 @@ def searchTorrent(albumid=None, new=False, losslessOnly=False): if new: - while True: - - if len(torrentlist): - - alreadydownloaded = myDB.select('SELECT * from snatched WHERE URL=?', [torrentlist[0][2]]) - - if len(alreadydownloaded): - logger.info('%s has already been downloaded. Skipping.' % torrentlist[0][0]) - torrentlist.pop(0) - - else: - break - else: - logger.info('No more results found for %s' % term) - return + while True: + + if len(torrentlist): + + alreadydownloaded = myDB.select('SELECT * from snatched WHERE URL=?', [torrentlist[0][2]]) + + if len(alreadydownloaded): + logger.info('%s has already been downloaded. Skipping.' % torrentlist[0][0]) + torrentlist.pop(0) + + else: + break + else: + logger.info('No more results found for %s' % term) + return logger.info(u"Pre-processing result") (data, bestqual) = preprocesstorrent(torrentlist) if data and bestqual: - logger.info(u'Found best result: %s - %s' % (bestqual[2], bestqual[0], helpers.bytes_to_mb(bestqual[1]))) + logger.info(u'Found best result: %s - %s' % (bestqual[2], bestqual[0], helpers.bytes_to_mb(bestqual[1]))) torrent_folder_name = '%s - %s [%s]' % (helpers.latinToAscii(albums[0]).encode('UTF-8').replace('/', '_'), helpers.latinToAscii(albums[1]).encode('UTF-8').replace('/', '_'), year) if headphones.TORRENTBLACKHOLE_DIR == "sendtracker": diff --git a/headphones/templates.py b/headphones/templates.py deleted file mode 100644 index 5e922bd7..00000000 --- a/headphones/templates.py +++ /dev/null @@ -1,440 +0,0 @@ -from headphones import db - -_header = ''' - - - Headphones - - - - - -
''' - -_shutdownheader = ''' - - - Headphones - - - - - - -
''' - -_logobar = ''' - -
- ''' - -_nav = '''''' - -_footer = ''' -
- - ''' - -configform = form = ''' -
-
- -
-
-
-
-

Web Interface

- - - - - - - - - - - - - - - - -
-

- HTTP Host:

-
- i.e. localhost or 0.0.0.0 -

-
-

- HTTP Username:

- -

-
-

- HTTP Port:

- -

-
-

- HTTP Password:

- -

-
-

Launch Browser on Startup:

-
- -

Download Settings

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-

SABnzbd Host:


- - usually localhost:8080 -
-

SABnzbd Username:

-
-
- -

SABnzbd API:

-
-
- -

SABnzbd Password:

-
-
- -

SABnzbd Category:

-
-
- -

Music Download Directory:


- - Full path to the directory where SAB downloads your music
- i.e. /Users/name/Downloads/music
-
-
- -

Use Black Hole:

-
-
- -

Black Hole Directory:


- - Folder your Download program watches for NZBs -
-
- -

Usenet Retention:

-
- -

Search Providers

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-

NZBMatrix:

-
-

- NZBMatrix Username:
- -

-
-

- NZBMatrix API:
- -

-
-
- -

Newznab:

-
-
- -

- Newznab Host:
-
- i.e. http://nzb.su -

-
-
- -

- Newznab API:
- -

-
-
- -

NZBs.org:

-
-
- -

- NZBs.org UID:
- -

-
-
- -

- NZBs.org Hash:
- -

-
-
- -

Newzbin:

-
-
- -

- Newzbin UID:
- -

-
-
- -

- Newzbin Password:
- -

-
- -

Quality & Post Processing

- - - - - - - - - - -
- Album Quality: -

- Highest Quality excluding Lossless

- Highest Quality including Lossless

- Lossless Only

- Preferred Bitrate: - kbps
- Auto-Detect Preferred Bitrate -

-
- Post-Processing: -

- Move downloads to Destination Folder
- Rename files
- Correct metadata
- Delete leftover files (.m3u, .nfo, .sfv, .nzb, etc.)
- Add album art as 'folder.jpg' to album folder
- Embed album art in each file -

-
-
- -

Path to Destination folder:
-
- i.e. /Users/name/Music/iTunes or /Volumes/share/music -

-
- -

Advanced Settings

- - - - - - - - - -
- Renaming Options: -
- -

Folder Format:
-
- Use: artist, album, year and first (first letter in artist name)
- E.g.: first/artist/album [year] = G/Girl Talk/All Day [2010]
-

- -

File Format:
-
- Use: tracknumber, title, artist, album and year -

-
- Miscellaneous: -

- Automatically Include Extras When Adding an Artist
- Extras includes: EPs, Compilations, Live Albums, Remix Albums and Singles -

-

Log Directory:
-

-
- Prowl Notification: -

- Enabled? -

-

- API key: - -

-

- Notify on snatch?

-

Priority (-2,-1,0,1 or 2): -

-
- -


- (Web Interface changes require a restart to take effect)

-
-
-
''' - - -def displayAlbums(ArtistID, Type=None): - - myDB = db.DBConnection() - - results = myDB.select('SELECT AlbumTitle, ReleaseDate, AlbumID, Status, ArtistName, AlbumASIN from albums WHERE ArtistID=? AND Type=? order by ReleaseDate DESC', [ArtistID, Type]) - - if not len(results): - return - - typeheadings = {'Album' : 'Official Albums', - 'Compilation': 'Compilations', - 'EP': 'EPs', - 'Live': 'Live Albums', - 'Remix': 'Remixes', - 'Single': 'Singles'} - - page = ['''

%s

- - - - - - - - ''' % typeheadings[Type]] - i = 0 - while i < len(results): - totaltracks = len(myDB.select('SELECT TrackTitle from tracks WHERE AlbumID=?', [results[i][2]])) - havetracks = len(myDB.select('SELECT TrackTitle from have WHERE ArtistName like ? AND AlbumTitle like ?', [results[i][4], results[i][0]])) - try: - percent = (havetracks*100)/totaltracks - if percent > 100: - percent = 100 - except ZeroDivisionError: - percent = 100 - if results[i][3] == 'Skipped': - newStatus = '''%s [want]''' % (results[i][3], results[i][2], ArtistID) - elif results[i][3] == 'Wanted': - newStatus = '''%s[skip]''' % (results[i][3], results[i][2], ArtistID) - elif results[i][3] == 'Downloaded': - newStatus = '''%s[retry][new]''' % (results[i][3], results[i][2], ArtistID, results[i][2], ArtistID) - elif results[i][3] == 'Snatched': - newStatus = '''%s[retry][new]''' % (results[i][3], results[i][2], ArtistID, results[i][2], ArtistID) - else: - newStatus = '%s' % (results[i][3]) - page.append(''' - - - - - ''' % (results[i][5], results[i][2], results[i][0], results[i][2], results[i][1], newStatus, percent, havetracks, totaltracks)) - i = i+1 - page.append('
Album NameRelease DateStatusHave
- %s - (link)%s%s
%s/%s

') - - return ''.join(page) diff --git a/headphones/updater.py b/headphones/updater.py index 1ea2974b..3aa5e847 100644 --- a/headphones/updater.py +++ b/headphones/updater.py @@ -4,15 +4,15 @@ from headphones import logger, db, importer def dbUpdate(): - myDB = db.DBConnection() + myDB = db.DBConnection() - activeartists = myDB.select('SELECT ArtistID, ArtistName from artists WHERE Status="Active" or Status="Loading" order by LastUpdated ASC') + activeartists = myDB.select('SELECT ArtistID, ArtistName from artists WHERE Status="Active" or Status="Loading" order by LastUpdated ASC') - logger.info('Starting update for %i active artists' % len(activeartists)) - - for artist in activeartists: - - artistid = artist[0] - importer.addArtisttoDB(artistid) - - logger.info('Update complete') + logger.info('Starting update for %i active artists' % len(activeartists)) + + for artist in activeartists: + + artistid = artist[0] + importer.addArtisttoDB(artistid) + + logger.info('Update complete') diff --git a/headphones/versioncheck.py b/headphones/versioncheck.py index 88c9a81f..669b2e5e 100644 --- a/headphones/versioncheck.py +++ b/headphones/versioncheck.py @@ -10,205 +10,205 @@ branch = "master" def runGit(args): - if headphones.GIT_PATH: - git_locations = ['"'+headphones.GIT_PATH+'"'] - else: - git_locations = ['git'] - - if platform.system().lower() == 'darwin': - git_locations.append('/usr/local/git/bin/git') - - - output = err = None - - for cur_git in git_locations: - - cmd = cur_git+' '+args - - try: - logger.debug('Trying to execute: "' + cmd + '" with shell in ' + headphones.PROG_DIR) - p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True, cwd=headphones.PROG_DIR) - output, err = p.communicate() - logger.debug('Git output: ' + output) - except OSError: - logger.debug('Command ' + cmd + ' didn\'t work, couldn\'t find git') - continue - - if 'not found' in output or "not recognized as an internal or external command" in output: - logger.debug('Unable to find git with command ' + cmd) - output = None - elif 'fatal:' in output or err: - logger.error('Git returned bad info. Are you sure this is a git installation?') - output = None - elif output: - break - - return (output, err) - + if headphones.GIT_PATH: + git_locations = ['"'+headphones.GIT_PATH+'"'] + else: + git_locations = ['git'] + + if platform.system().lower() == 'darwin': + git_locations.append('/usr/local/git/bin/git') + + + output = err = None + + for cur_git in git_locations: + + cmd = cur_git+' '+args + + try: + logger.debug('Trying to execute: "' + cmd + '" with shell in ' + headphones.PROG_DIR) + p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True, cwd=headphones.PROG_DIR) + output, err = p.communicate() + logger.debug('Git output: ' + output) + except OSError: + logger.debug('Command ' + cmd + ' didn\'t work, couldn\'t find git') + continue + + if 'not found' in output or "not recognized as an internal or external command" in output: + logger.debug('Unable to find git with command ' + cmd) + output = None + elif 'fatal:' in output or err: + logger.error('Git returned bad info. Are you sure this is a git installation?') + output = None + elif output: + break + + return (output, err) + def getVersion(): - if version.HEADPHONES_VERSION.startswith('build '): - - headphones.INSTALL_TYPE = 'win' - - # Don't have a way to update exe yet, but don't want to set VERSION to None - return 'Windows Install' - - elif os.path.isdir(os.path.join(headphones.PROG_DIR, '.git')): - - headphones.INSTALL_TYPE = 'git' - output, err = runGit('rev-parse HEAD') - - if not output: - logger.error('Couldn\'t find latest installed version.') - return None - - cur_commit_hash = output.strip() - - if not re.match('^[a-z0-9]+$', cur_commit_hash): - logger.error('Output doesn\'t look like a hash, not using it') - return None - - return cur_commit_hash - - else: - - headphones.INSTALL_TYPE = 'source' - - version_file = os.path.join(headphones.PROG_DIR, 'version.txt') - - if not os.path.isfile(version_file): - return None - - fp = open(version_file, 'r') - current_version = fp.read().strip(' \n\r') - fp.close() - - if current_version: - return current_version - else: - return None - + if version.HEADPHONES_VERSION.startswith('build '): + + headphones.INSTALL_TYPE = 'win' + + # Don't have a way to update exe yet, but don't want to set VERSION to None + return 'Windows Install' + + elif os.path.isdir(os.path.join(headphones.PROG_DIR, '.git')): + + headphones.INSTALL_TYPE = 'git' + output, err = runGit('rev-parse HEAD') + + if not output: + logger.error('Couldn\'t find latest installed version.') + return None + + cur_commit_hash = output.strip() + + if not re.match('^[a-z0-9]+$', cur_commit_hash): + logger.error('Output doesn\'t look like a hash, not using it') + return None + + return cur_commit_hash + + else: + + headphones.INSTALL_TYPE = 'source' + + version_file = os.path.join(headphones.PROG_DIR, 'version.txt') + + if not os.path.isfile(version_file): + return None + + fp = open(version_file, 'r') + current_version = fp.read().strip(' \n\r') + fp.close() + + if current_version: + return current_version + else: + return None + def checkGithub(): - # Get the latest commit available from github - url = 'https://api.github.com/repos/%s/headphones/commits/%s' % (user, branch) - logger.info ('Retrieving latest version information from github') - try: - result = urllib2.urlopen(url).read() - git = simplejson.JSONDecoder().decode(result) - headphones.LATEST_VERSION = git['sha'] - except: - logger.warn('Could not get the latest commit from github') - headphones.COMMITS_BEHIND = 0 - return headphones.CURRENT_VERSION - - # See how many commits behind we are - if headphones.CURRENT_VERSION: - logger.info('Comparing currently installed version with latest github version') - url = 'https://api.github.com/repos/%s/headphones/compare/%s...%s' % (user, headphones.CURRENT_VERSION, headphones.LATEST_VERSION) - - try: - result = urllib2.urlopen(url).read() - git = simplejson.JSONDecoder().decode(result) - headphones.COMMITS_BEHIND = git['total_commits'] - except: - logger.warn('Could not get commits behind from github') - headphones.COMMITS_BEHIND = 0 - return headphones.CURRENT_VERSION - - if headphones.COMMITS_BEHIND >= 1: - logger.info('New version is available. You are %s commits behind' % headphones.COMMITS_BEHIND) - elif headphones.COMMITS_BEHIND == 0: - logger.info('Headphones is up to date') - elif headphones.COMMITS_BEHIND == -1: - logger.info('You are running an unknown version of Headphones. Run the updater to identify your version') - - else: - logger.info('You are running an unknown version of Headphones. Run the updater to identify your version') - - return headphones.LATEST_VERSION - + # Get the latest commit available from github + url = 'https://api.github.com/repos/%s/headphones/commits/%s' % (user, branch) + logger.info ('Retrieving latest version information from github') + try: + result = urllib2.urlopen(url).read() + git = simplejson.JSONDecoder().decode(result) + headphones.LATEST_VERSION = git['sha'] + except: + logger.warn('Could not get the latest commit from github') + headphones.COMMITS_BEHIND = 0 + return headphones.CURRENT_VERSION + + # See how many commits behind we are + if headphones.CURRENT_VERSION: + logger.info('Comparing currently installed version with latest github version') + url = 'https://api.github.com/repos/%s/headphones/compare/%s...%s' % (user, headphones.CURRENT_VERSION, headphones.LATEST_VERSION) + + try: + result = urllib2.urlopen(url).read() + git = simplejson.JSONDecoder().decode(result) + headphones.COMMITS_BEHIND = git['total_commits'] + except: + logger.warn('Could not get commits behind from github') + headphones.COMMITS_BEHIND = 0 + return headphones.CURRENT_VERSION + + if headphones.COMMITS_BEHIND >= 1: + logger.info('New version is available. You are %s commits behind' % headphones.COMMITS_BEHIND) + elif headphones.COMMITS_BEHIND == 0: + logger.info('Headphones is up to date') + elif headphones.COMMITS_BEHIND == -1: + logger.info('You are running an unknown version of Headphones. Run the updater to identify your version') + + else: + logger.info('You are running an unknown version of Headphones. Run the updater to identify your version') + + return headphones.LATEST_VERSION + def update(): - - if headphones.INSTALL_TYPE == 'win': - - logger.info('Windows .exe updating not supported yet.') - pass - + + if headphones.INSTALL_TYPE == 'win': + + logger.info('Windows .exe updating not supported yet.') + pass + - elif headphones.INSTALL_TYPE == 'git': - - output, err = runGit('pull origin ' + version.HEADPHONES_VERSION) - - if not output: - logger.error('Couldn\'t download latest version') - - for line in output.split('\n'): - - if 'Already up-to-date.' in line: - logger.info('No update available, not updating') - logger.info('Output: ' + str(output)) - elif line.endswith('Aborting.'): - logger.error('Unable to update from git: '+line) - logger.info('Output: ' + str(output)) - - else: - - tar_download_url = 'https://github.com/%s/headphones/tarball/%s' % (user, branch) - update_dir = os.path.join(headphones.PROG_DIR, 'update') - version_path = os.path.join(headphones.PROG_DIR, 'version.txt') - - try: - logger.info('Downloading update from: '+tar_download_url) - data = urllib2.urlopen(tar_download_url) - except (IOError, URLError): - logger.error("Unable to retrieve new version from "+tar_download_url+", can't update") - return - - download_name = data.geturl().split('/')[-1] - - tar_download_path = os.path.join(headphones.PROG_DIR, download_name) - - # Save tar to disk - f = open(tar_download_path, 'wb') - f.write(data.read()) - f.close() - - # Extract the tar to update folder - logger.info('Extracing file' + tar_download_path) - tar = tarfile.open(tar_download_path) - tar.extractall(update_dir) - tar.close() - - # Delete the tar.gz - logger.info('Deleting file' + tar_download_path) - os.remove(tar_download_path) - - # Find update dir name - update_dir_contents = [x for x in os.listdir(update_dir) if os.path.isdir(os.path.join(update_dir, x))] - if len(update_dir_contents) != 1: - logger.error(u"Invalid update data, update failed: "+str(update_dir_contents)) - return - content_dir = os.path.join(update_dir, update_dir_contents[0]) - - # walk temp folder and move files to main folder - for dirname, dirnames, filenames in os.walk(content_dir): - dirname = dirname[len(content_dir)+1:] - for curfile in filenames: - old_path = os.path.join(content_dir, dirname, curfile) - new_path = os.path.join(headphones.PROG_DIR, dirname, curfile) - - if os.path.isfile(new_path): - os.remove(new_path) - os.renames(old_path, new_path) - - # Update version.txt - try: - ver_file = open(version_path, 'w') - ver_file.write(headphones.LATEST_VERSION) - ver_file.close() - except IOError, e: - logger.error(u"Unable to write current version to version.txt, update not complete: "+ex(e)) - return + elif headphones.INSTALL_TYPE == 'git': + + output, err = runGit('pull origin ' + version.HEADPHONES_VERSION) + + if not output: + logger.error('Couldn\'t download latest version') + + for line in output.split('\n'): + + if 'Already up-to-date.' in line: + logger.info('No update available, not updating') + logger.info('Output: ' + str(output)) + elif line.endswith('Aborting.'): + logger.error('Unable to update from git: '+line) + logger.info('Output: ' + str(output)) + + else: + + tar_download_url = 'https://github.com/%s/headphones/tarball/%s' % (user, branch) + update_dir = os.path.join(headphones.PROG_DIR, 'update') + version_path = os.path.join(headphones.PROG_DIR, 'version.txt') + + try: + logger.info('Downloading update from: '+tar_download_url) + data = urllib2.urlopen(tar_download_url) + except (IOError, URLError): + logger.error("Unable to retrieve new version from "+tar_download_url+", can't update") + return + + download_name = data.geturl().split('/')[-1] + + tar_download_path = os.path.join(headphones.PROG_DIR, download_name) + + # Save tar to disk + f = open(tar_download_path, 'wb') + f.write(data.read()) + f.close() + + # Extract the tar to update folder + logger.info('Extracing file' + tar_download_path) + tar = tarfile.open(tar_download_path) + tar.extractall(update_dir) + tar.close() + + # Delete the tar.gz + logger.info('Deleting file' + tar_download_path) + os.remove(tar_download_path) + + # Find update dir name + update_dir_contents = [x for x in os.listdir(update_dir) if os.path.isdir(os.path.join(update_dir, x))] + if len(update_dir_contents) != 1: + logger.error(u"Invalid update data, update failed: "+str(update_dir_contents)) + return + content_dir = os.path.join(update_dir, update_dir_contents[0]) + + # walk temp folder and move files to main folder + for dirname, dirnames, filenames in os.walk(content_dir): + dirname = dirname[len(content_dir)+1:] + for curfile in filenames: + old_path = os.path.join(content_dir, dirname, curfile) + new_path = os.path.join(headphones.PROG_DIR, dirname, curfile) + + if os.path.isfile(new_path): + os.remove(new_path) + os.renames(old_path, new_path) + + # Update version.txt + try: + ver_file = open(version_path, 'w') + ver_file.write(headphones.LATEST_VERSION) + ver_file.close() + except IOError, e: + logger.error(u"Unable to write current version to version.txt, update not complete: "+ex(e)) + return diff --git a/headphones/webserve.py b/headphones/webserve.py index 055d0964..13b9c5fa 100644 --- a/headphones/webserve.py +++ b/headphones/webserve.py @@ -17,562 +17,562 @@ from headphones.helpers import checked, radio def serve_template(templatename, **kwargs): - interface_dir = os.path.join(str(headphones.PROG_DIR), 'data/interfaces/') - template_dir = os.path.join(str(interface_dir), headphones.INTERFACE) - - _hplookup = TemplateLookup(directories=[template_dir]) - - try: - template = _hplookup.get_template(templatename) - return template.render(**kwargs) - except: - return exceptions.html_error_template().render() - + interface_dir = os.path.join(str(headphones.PROG_DIR), 'data/interfaces/') + template_dir = os.path.join(str(interface_dir), headphones.INTERFACE) + + _hplookup = TemplateLookup(directories=[template_dir]) + + try: + template = _hplookup.get_template(templatename) + return template.render(**kwargs) + except: + return exceptions.html_error_template().render() + class WebInterface(object): - - def index(self): - raise cherrypy.HTTPRedirect("home") - index.exposed=True + + def index(self): + raise cherrypy.HTTPRedirect("home") + index.exposed=True - def home(self): - myDB = db.DBConnection() - artists = myDB.select('SELECT * from artists order by ArtistSortName COLLATE NOCASE') - return serve_template(templatename="index.html", title="Home", artists=artists) - home.exposed = True + def home(self): + myDB = db.DBConnection() + artists = myDB.select('SELECT * from artists order by ArtistSortName COLLATE NOCASE') + return serve_template(templatename="index.html", title="Home", artists=artists) + home.exposed = True - def artistPage(self, ArtistID): - myDB = db.DBConnection() - artist = myDB.action('SELECT * FROM artists WHERE ArtistID=?', [ArtistID]).fetchone() - albums = myDB.select('SELECT * from albums WHERE ArtistID=? order by ReleaseDate DESC', [ArtistID]) - if artist is None: - raise cherrypy.HTTPRedirect("home") - return serve_template(templatename="artist.html", title=artist['ArtistName'], artist=artist, albums=albums) - artistPage.exposed = True - - - def albumPage(self, AlbumID): - myDB = db.DBConnection() - album = myDB.action('SELECT * from albums WHERE AlbumID=?', [AlbumID]).fetchone() - tracks = myDB.select('SELECT * from tracks WHERE AlbumID=?', [AlbumID]) - description = myDB.action('SELECT * from descriptions WHERE ReleaseGroupID=?', [AlbumID]).fetchone() - title = album['ArtistName'] + ' - ' + album['AlbumTitle'] - return serve_template(templatename="album.html", title=title, album=album, tracks=tracks, description=description) - albumPage.exposed = True - - - def search(self, name, type): - if len(name) == 0: - raise cherrypy.HTTPRedirect("home") - if type == 'artist': - searchresults = mb.findArtist(name, limit=100) - else: - searchresults = mb.findRelease(name, limit=100) - return serve_template(templatename="searchresults.html", title='Search Results for: "' + name + '"', searchresults=searchresults, type=type) - search.exposed = True + def artistPage(self, ArtistID): + myDB = db.DBConnection() + artist = myDB.action('SELECT * FROM artists WHERE ArtistID=?', [ArtistID]).fetchone() + albums = myDB.select('SELECT * from albums WHERE ArtistID=? order by ReleaseDate DESC', [ArtistID]) + if artist is None: + raise cherrypy.HTTPRedirect("home") + return serve_template(templatename="artist.html", title=artist['ArtistName'], artist=artist, albums=albums) + artistPage.exposed = True + + + def albumPage(self, AlbumID): + myDB = db.DBConnection() + album = myDB.action('SELECT * from albums WHERE AlbumID=?', [AlbumID]).fetchone() + tracks = myDB.select('SELECT * from tracks WHERE AlbumID=?', [AlbumID]) + description = myDB.action('SELECT * from descriptions WHERE ReleaseGroupID=?', [AlbumID]).fetchone() + title = album['ArtistName'] + ' - ' + album['AlbumTitle'] + return serve_template(templatename="album.html", title=title, album=album, tracks=tracks, description=description) + albumPage.exposed = True + + + def search(self, name, type): + if len(name) == 0: + raise cherrypy.HTTPRedirect("home") + if type == 'artist': + searchresults = mb.findArtist(name, limit=100) + else: + searchresults = mb.findRelease(name, limit=100) + return serve_template(templatename="searchresults.html", title='Search Results for: "' + name + '"', searchresults=searchresults, type=type) + search.exposed = True - def addArtist(self, artistid): - threading.Thread(target=importer.addArtisttoDB, args=[artistid]).start() - threading.Thread(target=lastfm.getSimilar).start() - raise cherrypy.HTTPRedirect("artistPage?ArtistID=%s" % artistid) - addArtist.exposed = True - - def getExtras(self, ArtistID): - myDB = db.DBConnection() - controlValueDict = {'ArtistID': ArtistID} - newValueDict = {'IncludeExtras': 1} - myDB.upsert("artists", newValueDict, controlValueDict) - threading.Thread(target=importer.addArtisttoDB, args=[ArtistID, True]).start() - raise cherrypy.HTTPRedirect("artistPage?ArtistID=%s" % ArtistID) - getExtras.exposed = True - - def removeExtras(self, ArtistID): - myDB = db.DBConnection() - controlValueDict = {'ArtistID': ArtistID} - newValueDict = {'IncludeExtras': 0} - myDB.upsert("artists", newValueDict, controlValueDict) - extraalbums = myDB.select('SELECT AlbumID from albums WHERE ArtistID=? AND Status="Skipped" AND Type!="Album"', [ArtistID]) - for album in extraalbums: - myDB.action('DELETE from tracks WHERE ArtistID=? AND AlbumID=?', [ArtistID, album['AlbumID']]) - myDB.action('DELETE from albums WHERE ArtistID=? AND AlbumID=?', [ArtistID, album['AlbumID']]) - raise cherrypy.HTTPRedirect("artistPage?ArtistID=%s" % ArtistID) - removeExtras.exposed = True - - def pauseArtist(self, ArtistID): - logger.info(u"Pausing artist: " + ArtistID) - myDB = db.DBConnection() - controlValueDict = {'ArtistID': ArtistID} - newValueDict = {'Status': 'Paused'} - myDB.upsert("artists", newValueDict, controlValueDict) - raise cherrypy.HTTPRedirect("artistPage?ArtistID=%s" % ArtistID) - pauseArtist.exposed = True - - def resumeArtist(self, ArtistID): - logger.info(u"Resuming artist: " + ArtistID) - myDB = db.DBConnection() - controlValueDict = {'ArtistID': ArtistID} - newValueDict = {'Status': 'Active'} - myDB.upsert("artists", newValueDict, controlValueDict) - raise cherrypy.HTTPRedirect("artistPage?ArtistID=%s" % ArtistID) - resumeArtist.exposed = True - - def deleteArtist(self, ArtistID): - logger.info(u"Deleting all traces of artist: " + ArtistID) - myDB = db.DBConnection() - myDB.action('DELETE from artists WHERE ArtistID=?', [ArtistID]) - myDB.action('DELETE from albums WHERE ArtistID=?', [ArtistID]) - myDB.action('DELETE from tracks WHERE ArtistID=?', [ArtistID]) - raise cherrypy.HTTPRedirect("home") - deleteArtist.exposed = True - - def refreshArtist(self, ArtistID): - importer.addArtisttoDB(ArtistID) - raise cherrypy.HTTPRedirect("artistPage?ArtistID=%s" % ArtistID) - refreshArtist.exposed=True - - def markAlbums(self, ArtistID=None, action=None, **args): - myDB = db.DBConnection() - if action == 'WantedNew': - newaction = 'Wanted' - else: - newaction = action - for mbid in args: - controlValueDict = {'AlbumID': mbid} - newValueDict = {'Status': newaction} - myDB.upsert("albums", newValueDict, controlValueDict) - if action == 'Wanted': - searcher.searchforalbum(mbid, new=False) - if action == 'WantedNew': - searcher.searchforalbum(mbid, new=True) - if ArtistID: - raise cherrypy.HTTPRedirect("artistPage?ArtistID=%s" % ArtistID) - else: - raise cherrypy.HTTPRedirect("upcoming") - markAlbums.exposed = True - - def addArtists(self, **args): - threading.Thread(target=importer.artistlist_to_mbids, args=[args, True]).start() - raise cherrypy.HTTPRedirect("home") - addArtists.exposed = True - - def queueAlbum(self, AlbumID, ArtistID=None, new=False, redirect=None, lossless=False): - logger.info(u"Marking album: " + AlbumID + " as wanted...") - myDB = db.DBConnection() - controlValueDict = {'AlbumID': AlbumID} - if lossless: - newValueDict = {'Status': 'Wanted Lossless'} - logger.info("...lossless only!") - else: - newValueDict = {'Status': 'Wanted'} - myDB.upsert("albums", newValueDict, controlValueDict) - searcher.searchforalbum(AlbumID, new) - if ArtistID: - raise cherrypy.HTTPRedirect("artistPage?ArtistID=%s" % ArtistID) - else: - raise cherrypy.HTTPRedirect(redirect) - queueAlbum.exposed = True + def addArtist(self, artistid): + threading.Thread(target=importer.addArtisttoDB, args=[artistid]).start() + threading.Thread(target=lastfm.getSimilar).start() + raise cherrypy.HTTPRedirect("artistPage?ArtistID=%s" % artistid) + addArtist.exposed = True + + def getExtras(self, ArtistID): + myDB = db.DBConnection() + controlValueDict = {'ArtistID': ArtistID} + newValueDict = {'IncludeExtras': 1} + myDB.upsert("artists", newValueDict, controlValueDict) + threading.Thread(target=importer.addArtisttoDB, args=[ArtistID, True]).start() + raise cherrypy.HTTPRedirect("artistPage?ArtistID=%s" % ArtistID) + getExtras.exposed = True + + def removeExtras(self, ArtistID): + myDB = db.DBConnection() + controlValueDict = {'ArtistID': ArtistID} + newValueDict = {'IncludeExtras': 0} + myDB.upsert("artists", newValueDict, controlValueDict) + extraalbums = myDB.select('SELECT AlbumID from albums WHERE ArtistID=? AND Status="Skipped" AND Type!="Album"', [ArtistID]) + for album in extraalbums: + myDB.action('DELETE from tracks WHERE ArtistID=? AND AlbumID=?', [ArtistID, album['AlbumID']]) + myDB.action('DELETE from albums WHERE ArtistID=? AND AlbumID=?', [ArtistID, album['AlbumID']]) + raise cherrypy.HTTPRedirect("artistPage?ArtistID=%s" % ArtistID) + removeExtras.exposed = True + + def pauseArtist(self, ArtistID): + logger.info(u"Pausing artist: " + ArtistID) + myDB = db.DBConnection() + controlValueDict = {'ArtistID': ArtistID} + newValueDict = {'Status': 'Paused'} + myDB.upsert("artists", newValueDict, controlValueDict) + raise cherrypy.HTTPRedirect("artistPage?ArtistID=%s" % ArtistID) + pauseArtist.exposed = True + + def resumeArtist(self, ArtistID): + logger.info(u"Resuming artist: " + ArtistID) + myDB = db.DBConnection() + controlValueDict = {'ArtistID': ArtistID} + newValueDict = {'Status': 'Active'} + myDB.upsert("artists", newValueDict, controlValueDict) + raise cherrypy.HTTPRedirect("artistPage?ArtistID=%s" % ArtistID) + resumeArtist.exposed = True + + def deleteArtist(self, ArtistID): + logger.info(u"Deleting all traces of artist: " + ArtistID) + myDB = db.DBConnection() + myDB.action('DELETE from artists WHERE ArtistID=?', [ArtistID]) + myDB.action('DELETE from albums WHERE ArtistID=?', [ArtistID]) + myDB.action('DELETE from tracks WHERE ArtistID=?', [ArtistID]) + raise cherrypy.HTTPRedirect("home") + deleteArtist.exposed = True + + def refreshArtist(self, ArtistID): + importer.addArtisttoDB(ArtistID) + raise cherrypy.HTTPRedirect("artistPage?ArtistID=%s" % ArtistID) + refreshArtist.exposed=True + + def markAlbums(self, ArtistID=None, action=None, **args): + myDB = db.DBConnection() + if action == 'WantedNew': + newaction = 'Wanted' + else: + newaction = action + for mbid in args: + controlValueDict = {'AlbumID': mbid} + newValueDict = {'Status': newaction} + myDB.upsert("albums", newValueDict, controlValueDict) + if action == 'Wanted': + searcher.searchforalbum(mbid, new=False) + if action == 'WantedNew': + searcher.searchforalbum(mbid, new=True) + if ArtistID: + raise cherrypy.HTTPRedirect("artistPage?ArtistID=%s" % ArtistID) + else: + raise cherrypy.HTTPRedirect("upcoming") + markAlbums.exposed = True + + def addArtists(self, **args): + threading.Thread(target=importer.artistlist_to_mbids, args=[args, True]).start() + raise cherrypy.HTTPRedirect("home") + addArtists.exposed = True + + def queueAlbum(self, AlbumID, ArtistID=None, new=False, redirect=None, lossless=False): + logger.info(u"Marking album: " + AlbumID + " as wanted...") + myDB = db.DBConnection() + controlValueDict = {'AlbumID': AlbumID} + if lossless: + newValueDict = {'Status': 'Wanted Lossless'} + logger.info("...lossless only!") + else: + newValueDict = {'Status': 'Wanted'} + myDB.upsert("albums", newValueDict, controlValueDict) + searcher.searchforalbum(AlbumID, new) + if ArtistID: + raise cherrypy.HTTPRedirect("artistPage?ArtistID=%s" % ArtistID) + else: + raise cherrypy.HTTPRedirect(redirect) + queueAlbum.exposed = True - def unqueueAlbum(self, AlbumID, ArtistID): - logger.info(u"Marking album: " + AlbumID + "as skipped...") - myDB = db.DBConnection() - controlValueDict = {'AlbumID': AlbumID} - newValueDict = {'Status': 'Skipped'} - myDB.upsert("albums", newValueDict, controlValueDict) - raise cherrypy.HTTPRedirect("artistPage?ArtistID=%s" % ArtistID) - unqueueAlbum.exposed = True - - def deleteAlbum(self, AlbumID, ArtistID=None): - logger.info(u"Deleting all traces of album: " + AlbumID) - myDB = db.DBConnection() - myDB.action('DELETE from albums WHERE AlbumID=?', [AlbumID]) - myDB.action('DELETE from tracks WHERE AlbumID=?', [AlbumID]) - if ArtistID: - raise cherrypy.HTTPRedirect("artistPage?ArtistID=%s" % ArtistID) - else: - raise cherrypy.HTTPRedirect("home") - deleteAlbum.exposed = True + def unqueueAlbum(self, AlbumID, ArtistID): + logger.info(u"Marking album: " + AlbumID + "as skipped...") + myDB = db.DBConnection() + controlValueDict = {'AlbumID': AlbumID} + newValueDict = {'Status': 'Skipped'} + myDB.upsert("albums", newValueDict, controlValueDict) + raise cherrypy.HTTPRedirect("artistPage?ArtistID=%s" % ArtistID) + unqueueAlbum.exposed = True + + def deleteAlbum(self, AlbumID, ArtistID=None): + logger.info(u"Deleting all traces of album: " + AlbumID) + myDB = db.DBConnection() + myDB.action('DELETE from albums WHERE AlbumID=?', [AlbumID]) + myDB.action('DELETE from tracks WHERE AlbumID=?', [AlbumID]) + if ArtistID: + raise cherrypy.HTTPRedirect("artistPage?ArtistID=%s" % ArtistID) + else: + raise cherrypy.HTTPRedirect("home") + deleteAlbum.exposed = True - def upcoming(self): - myDB = db.DBConnection() - upcoming = myDB.select("SELECT * from albums WHERE ReleaseDate > date('now') order by ReleaseDate DESC") - wanted = myDB.select("SELECT * from albums WHERE Status='Wanted'") - return serve_template(templatename="upcoming.html", title="Upcoming", upcoming=upcoming, wanted=wanted) - upcoming.exposed = True - - def manage(self): - return serve_template(templatename="manage.html", title="Manage") - manage.exposed = True - - def manageArtists(self): - myDB = db.DBConnection() - artists = myDB.select('SELECT * from artists order by ArtistSortName COLLATE NOCASE') - return serve_template(templatename="manageartists.html", title="Manage Artists", artists=artists) - manageArtists.exposed = True - - def manageNew(self): - return serve_template(templatename="managenew.html", title="Manage New Artists") - manageNew.exposed = True - - def markArtists(self, action=None, **args): - myDB = db.DBConnection() - artistsToAdd = [] - for ArtistID in args: - if action == 'delete': - myDB.action('DELETE from artists WHERE ArtistID=?', [ArtistID]) - myDB.action('DELETE from albums WHERE ArtistID=?', [ArtistID]) - myDB.action('DELETE from tracks WHERE ArtistID=?', [ArtistID]) - elif action == 'pause': - controlValueDict = {'ArtistID': ArtistID} - newValueDict = {'Status': 'Paused'} - myDB.upsert("artists", newValueDict, controlValueDict) - elif action == 'resume': - controlValueDict = {'ArtistID': ArtistID} - newValueDict = {'Status': 'Active'} - myDB.upsert("artists", newValueDict, controlValueDict) - else: - artistsToAdd.append(ArtistID) - if len(artistsToAdd) > 0: - logger.debug("Refreshing artists: %s" % artistsToAdd) - threading.Thread(target=importer.addArtistIDListToDB, args=[artistsToAdd]).start() - raise cherrypy.HTTPRedirect("home") - markArtists.exposed = True - - def importLastFM(self, username): - headphones.LASTFM_USERNAME = username - headphones.config_write() - threading.Thread(target=lastfm.getArtists).start() - time.sleep(10) - raise cherrypy.HTTPRedirect("home") - importLastFM.exposed = True - - def importItunes(self, path): - headphones.PATH_TO_XML = path - headphones.config_write() - threading.Thread(target=importer.itunesImport, args=[path]).start() - time.sleep(10) - raise cherrypy.HTTPRedirect("home") - importItunes.exposed = True - - def musicScan(self, path, redirect=None, autoadd=0): - headphones.ADD_ARTISTS = autoadd - headphones.MUSIC_DIR = path - headphones.config_write() - try: - threading.Thread(target=librarysync.libraryScan).start() - except Exception, e: - logger.error('Unable to complete the scan: %s' % e) - time.sleep(10) - if redirect: - raise cherrypy.HTTPRedirect(redirect) - else: - raise cherrypy.HTTPRedirect("home") - musicScan.exposed = True - - def forceUpdate(self): - from headphones import updater - threading.Thread(target=updater.dbUpdate).start() - raise cherrypy.HTTPRedirect("home") - forceUpdate.exposed = True - - def forceSearch(self): - from headphones import searcher - threading.Thread(target=searcher.searchforalbum).start() - raise cherrypy.HTTPRedirect("home") - forceSearch.exposed = True - - def forcePostProcess(self): - from headphones import postprocessor - threading.Thread(target=postprocessor.forcePostProcess).start() - raise cherrypy.HTTPRedirect("home") - forcePostProcess.exposed = True - - def checkGithub(self): - from headphones import versioncheck - versioncheck.checkGithub() - raise cherrypy.HTTPRedirect("home") - checkGithub.exposed = True - - def history(self): - myDB = db.DBConnection() - history = myDB.select('''SELECT * from snatched order by DateAdded DESC''') - return serve_template(templatename="history.html", title="History", history=history) - return page - history.exposed = True - - def logs(self): - return serve_template(templatename="logs.html", title="Log", lineList=headphones.LOG_LIST) - logs.exposed = True - - def clearhistory(self, type=None): - myDB = db.DBConnection() - if type == 'all': - logger.info(u"Clearing all history") - myDB.action('DELETE from snatched') - else: - logger.info(u"Clearing history where status is %s" % type) - myDB.action('DELETE from snatched WHERE Status=?', [type]) - raise cherrypy.HTTPRedirect("history") - clearhistory.exposed = True - - def generateAPI(self): + def upcoming(self): + myDB = db.DBConnection() + upcoming = myDB.select("SELECT * from albums WHERE ReleaseDate > date('now') order by ReleaseDate DESC") + wanted = myDB.select("SELECT * from albums WHERE Status='Wanted'") + return serve_template(templatename="upcoming.html", title="Upcoming", upcoming=upcoming, wanted=wanted) + upcoming.exposed = True + + def manage(self): + return serve_template(templatename="manage.html", title="Manage") + manage.exposed = True + + def manageArtists(self): + myDB = db.DBConnection() + artists = myDB.select('SELECT * from artists order by ArtistSortName COLLATE NOCASE') + return serve_template(templatename="manageartists.html", title="Manage Artists", artists=artists) + manageArtists.exposed = True + + def manageNew(self): + return serve_template(templatename="managenew.html", title="Manage New Artists") + manageNew.exposed = True + + def markArtists(self, action=None, **args): + myDB = db.DBConnection() + artistsToAdd = [] + for ArtistID in args: + if action == 'delete': + myDB.action('DELETE from artists WHERE ArtistID=?', [ArtistID]) + myDB.action('DELETE from albums WHERE ArtistID=?', [ArtistID]) + myDB.action('DELETE from tracks WHERE ArtistID=?', [ArtistID]) + elif action == 'pause': + controlValueDict = {'ArtistID': ArtistID} + newValueDict = {'Status': 'Paused'} + myDB.upsert("artists", newValueDict, controlValueDict) + elif action == 'resume': + controlValueDict = {'ArtistID': ArtistID} + newValueDict = {'Status': 'Active'} + myDB.upsert("artists", newValueDict, controlValueDict) + else: + artistsToAdd.append(ArtistID) + if len(artistsToAdd) > 0: + logger.debug("Refreshing artists: %s" % artistsToAdd) + threading.Thread(target=importer.addArtistIDListToDB, args=[artistsToAdd]).start() + raise cherrypy.HTTPRedirect("home") + markArtists.exposed = True + + def importLastFM(self, username): + headphones.LASTFM_USERNAME = username + headphones.config_write() + threading.Thread(target=lastfm.getArtists).start() + time.sleep(10) + raise cherrypy.HTTPRedirect("home") + importLastFM.exposed = True + + def importItunes(self, path): + headphones.PATH_TO_XML = path + headphones.config_write() + threading.Thread(target=importer.itunesImport, args=[path]).start() + time.sleep(10) + raise cherrypy.HTTPRedirect("home") + importItunes.exposed = True + + def musicScan(self, path, redirect=None, autoadd=0): + headphones.ADD_ARTISTS = autoadd + headphones.MUSIC_DIR = path + headphones.config_write() + try: + threading.Thread(target=librarysync.libraryScan).start() + except Exception, e: + logger.error('Unable to complete the scan: %s' % e) + time.sleep(10) + if redirect: + raise cherrypy.HTTPRedirect(redirect) + else: + raise cherrypy.HTTPRedirect("home") + musicScan.exposed = True + + def forceUpdate(self): + from headphones import updater + threading.Thread(target=updater.dbUpdate).start() + raise cherrypy.HTTPRedirect("home") + forceUpdate.exposed = True + + def forceSearch(self): + from headphones import searcher + threading.Thread(target=searcher.searchforalbum).start() + raise cherrypy.HTTPRedirect("home") + forceSearch.exposed = True + + def forcePostProcess(self): + from headphones import postprocessor + threading.Thread(target=postprocessor.forcePostProcess).start() + raise cherrypy.HTTPRedirect("home") + forcePostProcess.exposed = True + + def checkGithub(self): + from headphones import versioncheck + versioncheck.checkGithub() + raise cherrypy.HTTPRedirect("home") + checkGithub.exposed = True + + def history(self): + myDB = db.DBConnection() + history = myDB.select('''SELECT * from snatched order by DateAdded DESC''') + return serve_template(templatename="history.html", title="History", history=history) + return page + history.exposed = True + + def logs(self): + return serve_template(templatename="logs.html", title="Log", lineList=headphones.LOG_LIST) + logs.exposed = True + + def clearhistory(self, type=None): + myDB = db.DBConnection() + if type == 'all': + logger.info(u"Clearing all history") + myDB.action('DELETE from snatched') + else: + logger.info(u"Clearing history where status is %s" % type) + myDB.action('DELETE from snatched WHERE Status=?', [type]) + raise cherrypy.HTTPRedirect("history") + clearhistory.exposed = True + + def generateAPI(self): - import hashlib, random - - apikey = hashlib.sha224( str(random.getrandbits(256)) ).hexdigest()[0:32] - logger.info("New API generated") - return apikey - - generateAPI.exposed = True - - def config(self): - - interface_dir = os.path.join(headphones.PROG_DIR, 'data/interfaces/') - interface_list = [ name for name in os.listdir(interface_dir) if os.path.isdir(os.path.join(interface_dir, name)) ] + import hashlib, random + + apikey = hashlib.sha224( str(random.getrandbits(256)) ).hexdigest()[0:32] + logger.info("New API generated") + return apikey + + generateAPI.exposed = True + + def config(self): + + interface_dir = os.path.join(headphones.PROG_DIR, 'data/interfaces/') + interface_list = [ name for name in os.listdir(interface_dir) if os.path.isdir(os.path.join(interface_dir, name)) ] - config = { - "http_host" : headphones.HTTP_HOST, - "http_user" : headphones.HTTP_USERNAME, - "http_port" : headphones.HTTP_PORT, - "http_pass" : headphones.HTTP_PASSWORD, - "launch_browser" : checked(headphones.LAUNCH_BROWSER), - "api_enabled" : checked(headphones.API_ENABLED), - "api_key" : headphones.API_KEY, - "download_scan_interval" : headphones.DOWNLOAD_SCAN_INTERVAL, - "nzb_search_interval" : headphones.SEARCH_INTERVAL, - "libraryscan_interval" : headphones.LIBRARYSCAN_INTERVAL, - "sab_host" : headphones.SAB_HOST, - "sab_user" : headphones.SAB_USERNAME, - "sab_api" : headphones.SAB_APIKEY, - "sab_pass" : headphones.SAB_PASSWORD, - "sab_cat" : headphones.SAB_CATEGORY, - "download_dir" : headphones.DOWNLOAD_DIR, - "use_blackhole" : checked(headphones.BLACKHOLE), - "blackhole_dir" : headphones.BLACKHOLE_DIR, - "usenet_retention" : headphones.USENET_RETENTION, - "use_nzbmatrix" : checked(headphones.NZBMATRIX), - "nzbmatrix_user" : headphones.NZBMATRIX_USERNAME, - "nzbmatrix_api" : headphones.NZBMATRIX_APIKEY, - "use_newznab" : checked(headphones.NEWZNAB), - "newznab_host" : headphones.NEWZNAB_HOST, - "newznab_api" : headphones.NEWZNAB_APIKEY, - "use_nzbsorg" : checked(headphones.NZBSORG), - "nzbsorg_uid" : headphones.NZBSORG_UID, - "nzbsorg_hash" : headphones.NZBSORG_HASH, - "use_newzbin" : checked(headphones.NEWZBIN), - "newzbin_uid" : headphones.NEWZBIN_UID, - "newzbin_pass" : headphones.NEWZBIN_PASSWORD, - "torrentblackhole_dir" : headphones.TORRENTBLACKHOLE_DIR, - "download_torrent_dir" : headphones.DOWNLOAD_TORRENT_DIR, - "numberofseeders" : headphones.NUMBEROFSEEDERS, - "use_isohunt" : checked(headphones.ISOHUNT), - "use_kat" : checked(headphones.KAT), - "use_mininova" : checked(headphones.MININOVA), - "pref_qual_0" : radio(headphones.PREFERRED_QUALITY, 0), - "pref_qual_1" : radio(headphones.PREFERRED_QUALITY, 1), - "pref_qual_3" : radio(headphones.PREFERRED_QUALITY, 3), - "pref_qual_2" : radio(headphones.PREFERRED_QUALITY, 2), - "pref_bitrate" : headphones.PREFERRED_BITRATE, - "detect_bitrate" : checked(headphones.DETECT_BITRATE), - "move_files" : checked(headphones.MOVE_FILES), - "rename_files" : checked(headphones.RENAME_FILES), - "correct_metadata" : checked(headphones.CORRECT_METADATA), - "cleanup_files" : checked(headphones.CLEANUP_FILES), - "add_album_art" : checked(headphones.ADD_ALBUM_ART), - "embed_album_art" : checked(headphones.EMBED_ALBUM_ART), - "embed_lyrics" : checked(headphones.EMBED_LYRICS), - "dest_dir" : headphones.DESTINATION_DIR, - "folder_format" : headphones.FOLDER_FORMAT, - "file_format" : headphones.FILE_FORMAT, - "include_extras" : checked(headphones.INCLUDE_EXTRAS), - "autowant_upcoming" : checked(headphones.AUTOWANT_UPCOMING), - "autowant_all" : checked(headphones.AUTOWANT_ALL), - "log_dir" : headphones.LOG_DIR, - "interface_list" : interface_list, - "music_encoder": checked(headphones.MUSIC_ENCODER), - "encoder": headphones.ENCODER, - "bitrate": int(headphones.BITRATE), - "encoderfolder": headphones.ENCODERFOLDER, - "advancedencoder": headphones.ADVANCEDENCODER, - "encoderoutputformat": headphones.ENCODEROUTPUTFORMAT, - "samplingfrequency": headphones.SAMPLINGFREQUENCY, - "encodervbrcbr": headphones.ENCODERVBRCBR, - "encoderquality": headphones.ENCODERQUALITY, - "encoderlossless": checked(headphones.ENCODERLOSSLESS), - "prowl_enabled": checked(headphones.PROWL_ENABLED), - "prowl_onsnatch": checked(headphones.PROWL_ONSNATCH), - "prowl_keys": headphones.PROWL_KEYS, - "prowl_priority": headphones.PROWL_PRIORITY, - "xbmc_enabled": checked(headphones.XBMC_ENABLED), - "xbmc_host": headphones.XBMC_HOST, - "xbmc_username": headphones.XBMC_USERNAME, - "xbmc_password": headphones.XBMC_PASSWORD, - "xbmc_update": checked(headphones.XBMC_UPDATE), - "xbmc_notify": checked(headphones.XBMC_NOTIFY), - "nma_enabled": checked(headphones.NMA_ENABLED), - "nma_apikey": headphones.NMA_APIKEY, - "nma_priority": int(headphones.NMA_PRIORITY), - "mirror_list": headphones.MIRRORLIST, - "mirror": headphones.MIRROR, - "customhost": headphones.CUSTOMHOST, - "customport": headphones.CUSTOMPORT, - "customsleep": headphones.CUSTOMSLEEP, - "hpuser": headphones.HPUSER, - "hppass": headphones.HPPASS - } - return serve_template(templatename="config.html", title="Settings", config=config) - config.exposed = True - - - def configUpdate(self, http_host='0.0.0.0', http_username=None, http_port=8181, http_password=None, launch_browser=0, api_enabled=0, api_key=None, download_scan_interval=None, nzb_search_interval=None, libraryscan_interval=None, - sab_host=None, sab_username=None, sab_apikey=None, sab_password=None, sab_category=None, download_dir=None, blackhole=0, blackhole_dir=None, - usenet_retention=None, nzbmatrix=0, nzbmatrix_username=None, nzbmatrix_apikey=None, newznab=0, newznab_host=None, newznab_apikey=None, - nzbsorg=0, nzbsorg_uid=None, nzbsorg_hash=None, newzbin=0, newzbin_uid=None, newzbin_password=None, preferred_quality=0, preferred_bitrate=None, detect_bitrate=0, move_files=0, - torrentblackhole_dir=None, download_torrent_dir=None, numberofseeders=10, use_isohunt=0, use_kat=0, use_mininova=0, - rename_files=0, correct_metadata=0, cleanup_files=0, add_album_art=0, embed_album_art=0, embed_lyrics=0, destination_dir=None, folder_format=None, file_format=None, include_extras=0, autowant_upcoming=False, autowant_all=False, interface=None, log_dir=None, - music_encoder=0, encoder=None, bitrate=None, samplingfrequency=None, encoderfolder=None, advancedencoder=None, encoderoutputformat=None, encodervbrcbr=None, encoderquality=None, encoderlossless=0, - prowl_enabled=0, prowl_onsnatch=0, prowl_keys=None, prowl_priority=0, xbmc_enabled=0, xbmc_host=None, xbmc_username=None, xbmc_password=None, xbmc_update=0, xbmc_notify=0, - nma_enabled=False, nma_apikey=None, nma_priority=0, mirror=None, customhost=None, customport=None, customsleep=None, hpuser=None, hppass=None): + config = { + "http_host" : headphones.HTTP_HOST, + "http_user" : headphones.HTTP_USERNAME, + "http_port" : headphones.HTTP_PORT, + "http_pass" : headphones.HTTP_PASSWORD, + "launch_browser" : checked(headphones.LAUNCH_BROWSER), + "api_enabled" : checked(headphones.API_ENABLED), + "api_key" : headphones.API_KEY, + "download_scan_interval" : headphones.DOWNLOAD_SCAN_INTERVAL, + "nzb_search_interval" : headphones.SEARCH_INTERVAL, + "libraryscan_interval" : headphones.LIBRARYSCAN_INTERVAL, + "sab_host" : headphones.SAB_HOST, + "sab_user" : headphones.SAB_USERNAME, + "sab_api" : headphones.SAB_APIKEY, + "sab_pass" : headphones.SAB_PASSWORD, + "sab_cat" : headphones.SAB_CATEGORY, + "download_dir" : headphones.DOWNLOAD_DIR, + "use_blackhole" : checked(headphones.BLACKHOLE), + "blackhole_dir" : headphones.BLACKHOLE_DIR, + "usenet_retention" : headphones.USENET_RETENTION, + "use_nzbmatrix" : checked(headphones.NZBMATRIX), + "nzbmatrix_user" : headphones.NZBMATRIX_USERNAME, + "nzbmatrix_api" : headphones.NZBMATRIX_APIKEY, + "use_newznab" : checked(headphones.NEWZNAB), + "newznab_host" : headphones.NEWZNAB_HOST, + "newznab_api" : headphones.NEWZNAB_APIKEY, + "use_nzbsorg" : checked(headphones.NZBSORG), + "nzbsorg_uid" : headphones.NZBSORG_UID, + "nzbsorg_hash" : headphones.NZBSORG_HASH, + "use_newzbin" : checked(headphones.NEWZBIN), + "newzbin_uid" : headphones.NEWZBIN_UID, + "newzbin_pass" : headphones.NEWZBIN_PASSWORD, + "torrentblackhole_dir" : headphones.TORRENTBLACKHOLE_DIR, + "download_torrent_dir" : headphones.DOWNLOAD_TORRENT_DIR, + "numberofseeders" : headphones.NUMBEROFSEEDERS, + "use_isohunt" : checked(headphones.ISOHUNT), + "use_kat" : checked(headphones.KAT), + "use_mininova" : checked(headphones.MININOVA), + "pref_qual_0" : radio(headphones.PREFERRED_QUALITY, 0), + "pref_qual_1" : radio(headphones.PREFERRED_QUALITY, 1), + "pref_qual_3" : radio(headphones.PREFERRED_QUALITY, 3), + "pref_qual_2" : radio(headphones.PREFERRED_QUALITY, 2), + "pref_bitrate" : headphones.PREFERRED_BITRATE, + "detect_bitrate" : checked(headphones.DETECT_BITRATE), + "move_files" : checked(headphones.MOVE_FILES), + "rename_files" : checked(headphones.RENAME_FILES), + "correct_metadata" : checked(headphones.CORRECT_METADATA), + "cleanup_files" : checked(headphones.CLEANUP_FILES), + "add_album_art" : checked(headphones.ADD_ALBUM_ART), + "embed_album_art" : checked(headphones.EMBED_ALBUM_ART), + "embed_lyrics" : checked(headphones.EMBED_LYRICS), + "dest_dir" : headphones.DESTINATION_DIR, + "folder_format" : headphones.FOLDER_FORMAT, + "file_format" : headphones.FILE_FORMAT, + "include_extras" : checked(headphones.INCLUDE_EXTRAS), + "autowant_upcoming" : checked(headphones.AUTOWANT_UPCOMING), + "autowant_all" : checked(headphones.AUTOWANT_ALL), + "log_dir" : headphones.LOG_DIR, + "interface_list" : interface_list, + "music_encoder": checked(headphones.MUSIC_ENCODER), + "encoder": headphones.ENCODER, + "bitrate": int(headphones.BITRATE), + "encoderfolder": headphones.ENCODERFOLDER, + "advancedencoder": headphones.ADVANCEDENCODER, + "encoderoutputformat": headphones.ENCODEROUTPUTFORMAT, + "samplingfrequency": headphones.SAMPLINGFREQUENCY, + "encodervbrcbr": headphones.ENCODERVBRCBR, + "encoderquality": headphones.ENCODERQUALITY, + "encoderlossless": checked(headphones.ENCODERLOSSLESS), + "prowl_enabled": checked(headphones.PROWL_ENABLED), + "prowl_onsnatch": checked(headphones.PROWL_ONSNATCH), + "prowl_keys": headphones.PROWL_KEYS, + "prowl_priority": headphones.PROWL_PRIORITY, + "xbmc_enabled": checked(headphones.XBMC_ENABLED), + "xbmc_host": headphones.XBMC_HOST, + "xbmc_username": headphones.XBMC_USERNAME, + "xbmc_password": headphones.XBMC_PASSWORD, + "xbmc_update": checked(headphones.XBMC_UPDATE), + "xbmc_notify": checked(headphones.XBMC_NOTIFY), + "nma_enabled": checked(headphones.NMA_ENABLED), + "nma_apikey": headphones.NMA_APIKEY, + "nma_priority": int(headphones.NMA_PRIORITY), + "mirror_list": headphones.MIRRORLIST, + "mirror": headphones.MIRROR, + "customhost": headphones.CUSTOMHOST, + "customport": headphones.CUSTOMPORT, + "customsleep": headphones.CUSTOMSLEEP, + "hpuser": headphones.HPUSER, + "hppass": headphones.HPPASS + } + return serve_template(templatename="config.html", title="Settings", config=config) + config.exposed = True + + + def configUpdate(self, http_host='0.0.0.0', http_username=None, http_port=8181, http_password=None, launch_browser=0, api_enabled=0, api_key=None, download_scan_interval=None, nzb_search_interval=None, libraryscan_interval=None, + sab_host=None, sab_username=None, sab_apikey=None, sab_password=None, sab_category=None, download_dir=None, blackhole=0, blackhole_dir=None, + usenet_retention=None, nzbmatrix=0, nzbmatrix_username=None, nzbmatrix_apikey=None, newznab=0, newznab_host=None, newznab_apikey=None, + nzbsorg=0, nzbsorg_uid=None, nzbsorg_hash=None, newzbin=0, newzbin_uid=None, newzbin_password=None, preferred_quality=0, preferred_bitrate=None, detect_bitrate=0, move_files=0, + torrentblackhole_dir=None, download_torrent_dir=None, numberofseeders=10, use_isohunt=0, use_kat=0, use_mininova=0, + rename_files=0, correct_metadata=0, cleanup_files=0, add_album_art=0, embed_album_art=0, embed_lyrics=0, destination_dir=None, folder_format=None, file_format=None, include_extras=0, autowant_upcoming=False, autowant_all=False, interface=None, log_dir=None, + music_encoder=0, encoder=None, bitrate=None, samplingfrequency=None, encoderfolder=None, advancedencoder=None, encoderoutputformat=None, encodervbrcbr=None, encoderquality=None, encoderlossless=0, + prowl_enabled=0, prowl_onsnatch=0, prowl_keys=None, prowl_priority=0, xbmc_enabled=0, xbmc_host=None, xbmc_username=None, xbmc_password=None, xbmc_update=0, xbmc_notify=0, + nma_enabled=False, nma_apikey=None, nma_priority=0, mirror=None, customhost=None, customport=None, customsleep=None, hpuser=None, hppass=None): - headphones.HTTP_HOST = http_host - headphones.HTTP_PORT = http_port - headphones.HTTP_USERNAME = http_username - headphones.HTTP_PASSWORD = http_password - headphones.LAUNCH_BROWSER = launch_browser - headphones.API_ENABLED = api_enabled - headphones.API_KEY = api_key - headphones.DOWNLOAD_SCAN_INTERVAL = download_scan_interval - headphones.SEARCH_INTERVAL = nzb_search_interval - headphones.LIBRARYSCAN_INTERVAL = libraryscan_interval - headphones.SAB_HOST = sab_host - headphones.SAB_USERNAME = sab_username - headphones.SAB_PASSWORD = sab_password - headphones.SAB_APIKEY = sab_apikey - headphones.SAB_CATEGORY = sab_category - headphones.DOWNLOAD_DIR = download_dir - headphones.BLACKHOLE = blackhole - headphones.BLACKHOLE_DIR = blackhole_dir - headphones.USENET_RETENTION = usenet_retention - headphones.NZBMATRIX = nzbmatrix - headphones.NZBMATRIX_USERNAME = nzbmatrix_username - headphones.NZBMATRIX_APIKEY = nzbmatrix_apikey - headphones.NEWZNAB = newznab - headphones.NEWZNAB_HOST = newznab_host - headphones.NEWZNAB_APIKEY = newznab_apikey - headphones.NZBSORG = nzbsorg - headphones.NZBSORG_UID = nzbsorg_uid - headphones.NZBSORG_HASH = nzbsorg_hash - headphones.NEWZBIN = newzbin - headphones.NEWZBIN_UID = newzbin_uid - headphones.NEWZBIN_PASSWORD = newzbin_password - headphones.TORRENTBLACKHOLE_DIR = torrentblackhole_dir - headphones.NUMBEROFSEEDERS = numberofseeders - headphones.DOWNLOAD_TORRENT_DIR = download_torrent_dir - headphones.ISOHUNT = use_isohunt - headphones.KAT = use_kat - headphones.MININOVA = use_mininova - headphones.PREFERRED_QUALITY = int(preferred_quality) - headphones.PREFERRED_BITRATE = preferred_bitrate - headphones.DETECT_BITRATE = detect_bitrate - headphones.MOVE_FILES = move_files - headphones.CORRECT_METADATA = correct_metadata - headphones.RENAME_FILES = rename_files - headphones.CLEANUP_FILES = cleanup_files - headphones.ADD_ALBUM_ART = add_album_art - headphones.EMBED_ALBUM_ART = embed_album_art - headphones.EMBED_LYRICS = embed_lyrics - headphones.DESTINATION_DIR = destination_dir - headphones.FOLDER_FORMAT = folder_format - headphones.FILE_FORMAT = file_format - headphones.INCLUDE_EXTRAS = include_extras - headphones.AUTOWANT_UPCOMING = autowant_upcoming - headphones.AUTOWANT_ALL = autowant_all - headphones.INTERFACE = interface - headphones.LOG_DIR = log_dir - headphones.MUSIC_ENCODER = music_encoder - headphones.ENCODER = encoder - headphones.BITRATE = int(bitrate) - headphones.SAMPLINGFREQUENCY = int(samplingfrequency) - headphones.ENCODERFOLDER = encoderfolder - headphones.ADVANCEDENCODER = advancedencoder - headphones.ENCODEROUTPUTFORMAT = encoderoutputformat - headphones.ENCODERVBRCBR = encodervbrcbr - headphones.ENCODERQUALITY = int(encoderquality) - headphones.ENCODERLOSSLESS = encoderlossless - headphones.PROWL_ENABLED = prowl_enabled - headphones.PROWL_ONSNATCH = prowl_onsnatch - headphones.PROWL_KEYS = prowl_keys - headphones.PROWL_PRIORITY = prowl_priority - headphones.XBMC_ENABLED = xbmc_enabled - headphones.XBMC_HOST = xbmc_host - headphones.XBMC_USERNAME = xbmc_username - headphones.XBMC_PASSWORD = xbmc_password - headphones.XBMC_UPDATE = xbmc_update - headphones.XBMC_NOTIFY = xbmc_notify - headphones.NMA_ENABLED = nma_enabled - headphones.NMA_APIKEY = nma_apikey - headphones.NMA_PRIORITY = nma_priority - headphones.MIRROR = mirror - headphones.CUSTOMHOST = customhost - headphones.CUSTOMPORT = customport - headphones.CUSTOMSLEEP = customsleep - headphones.HPUSER = hpuser - headphones.HPPASS = hppass - - headphones.config_write() + headphones.HTTP_HOST = http_host + headphones.HTTP_PORT = http_port + headphones.HTTP_USERNAME = http_username + headphones.HTTP_PASSWORD = http_password + headphones.LAUNCH_BROWSER = launch_browser + headphones.API_ENABLED = api_enabled + headphones.API_KEY = api_key + headphones.DOWNLOAD_SCAN_INTERVAL = download_scan_interval + headphones.SEARCH_INTERVAL = nzb_search_interval + headphones.LIBRARYSCAN_INTERVAL = libraryscan_interval + headphones.SAB_HOST = sab_host + headphones.SAB_USERNAME = sab_username + headphones.SAB_PASSWORD = sab_password + headphones.SAB_APIKEY = sab_apikey + headphones.SAB_CATEGORY = sab_category + headphones.DOWNLOAD_DIR = download_dir + headphones.BLACKHOLE = blackhole + headphones.BLACKHOLE_DIR = blackhole_dir + headphones.USENET_RETENTION = usenet_retention + headphones.NZBMATRIX = nzbmatrix + headphones.NZBMATRIX_USERNAME = nzbmatrix_username + headphones.NZBMATRIX_APIKEY = nzbmatrix_apikey + headphones.NEWZNAB = newznab + headphones.NEWZNAB_HOST = newznab_host + headphones.NEWZNAB_APIKEY = newznab_apikey + headphones.NZBSORG = nzbsorg + headphones.NZBSORG_UID = nzbsorg_uid + headphones.NZBSORG_HASH = nzbsorg_hash + headphones.NEWZBIN = newzbin + headphones.NEWZBIN_UID = newzbin_uid + headphones.NEWZBIN_PASSWORD = newzbin_password + headphones.TORRENTBLACKHOLE_DIR = torrentblackhole_dir + headphones.NUMBEROFSEEDERS = numberofseeders + headphones.DOWNLOAD_TORRENT_DIR = download_torrent_dir + headphones.ISOHUNT = use_isohunt + headphones.KAT = use_kat + headphones.MININOVA = use_mininova + headphones.PREFERRED_QUALITY = int(preferred_quality) + headphones.PREFERRED_BITRATE = preferred_bitrate + headphones.DETECT_BITRATE = detect_bitrate + headphones.MOVE_FILES = move_files + headphones.CORRECT_METADATA = correct_metadata + headphones.RENAME_FILES = rename_files + headphones.CLEANUP_FILES = cleanup_files + headphones.ADD_ALBUM_ART = add_album_art + headphones.EMBED_ALBUM_ART = embed_album_art + headphones.EMBED_LYRICS = embed_lyrics + headphones.DESTINATION_DIR = destination_dir + headphones.FOLDER_FORMAT = folder_format + headphones.FILE_FORMAT = file_format + headphones.INCLUDE_EXTRAS = include_extras + headphones.AUTOWANT_UPCOMING = autowant_upcoming + headphones.AUTOWANT_ALL = autowant_all + headphones.INTERFACE = interface + headphones.LOG_DIR = log_dir + headphones.MUSIC_ENCODER = music_encoder + headphones.ENCODER = encoder + headphones.BITRATE = int(bitrate) + headphones.SAMPLINGFREQUENCY = int(samplingfrequency) + headphones.ENCODERFOLDER = encoderfolder + headphones.ADVANCEDENCODER = advancedencoder + headphones.ENCODEROUTPUTFORMAT = encoderoutputformat + headphones.ENCODERVBRCBR = encodervbrcbr + headphones.ENCODERQUALITY = int(encoderquality) + headphones.ENCODERLOSSLESS = encoderlossless + headphones.PROWL_ENABLED = prowl_enabled + headphones.PROWL_ONSNATCH = prowl_onsnatch + headphones.PROWL_KEYS = prowl_keys + headphones.PROWL_PRIORITY = prowl_priority + headphones.XBMC_ENABLED = xbmc_enabled + headphones.XBMC_HOST = xbmc_host + headphones.XBMC_USERNAME = xbmc_username + headphones.XBMC_PASSWORD = xbmc_password + headphones.XBMC_UPDATE = xbmc_update + headphones.XBMC_NOTIFY = xbmc_notify + headphones.NMA_ENABLED = nma_enabled + headphones.NMA_APIKEY = nma_apikey + headphones.NMA_PRIORITY = nma_priority + headphones.MIRROR = mirror + headphones.CUSTOMHOST = customhost + headphones.CUSTOMPORT = customport + headphones.CUSTOMSLEEP = customsleep + headphones.HPUSER = hpuser + headphones.HPPASS = hppass + + headphones.config_write() - raise cherrypy.HTTPRedirect("config") - - configUpdate.exposed = True + raise cherrypy.HTTPRedirect("config") + + configUpdate.exposed = True - def shutdown(self): - headphones.SIGNAL = 'shutdown' - message = 'Shutting Down...' - return serve_template(templatename="shutdown.html", title="Shutting Down", message=message, timer=15) - return page + def shutdown(self): + headphones.SIGNAL = 'shutdown' + message = 'Shutting Down...' + return serve_template(templatename="shutdown.html", title="Shutting Down", message=message, timer=15) + return page - shutdown.exposed = True + shutdown.exposed = True - def restart(self): - headphones.SIGNAL = 'restart' - message = 'Restarting...' - return serve_template(templatename="shutdown.html", title="Restarting", message=message, timer=30) - restart.exposed = True - - def update(self): - headphones.SIGNAL = 'update' - message = 'Updating...' - return serve_template(templatename="shutdown.html", title="Updating", message=message, timer=120) - return page - update.exposed = True - - def extras(self): - myDB = db.DBConnection() - cloudlist = myDB.select('SELECT * from lastfmcloud') - return serve_template(templatename="extras.html", title="Extras", cloudlist=cloudlist) - return page - extras.exposed = True + def restart(self): + headphones.SIGNAL = 'restart' + message = 'Restarting...' + return serve_template(templatename="shutdown.html", title="Restarting", message=message, timer=30) + restart.exposed = True + + def update(self): + headphones.SIGNAL = 'update' + message = 'Updating...' + return serve_template(templatename="shutdown.html", title="Updating", message=message, timer=120) + return page + update.exposed = True + + def extras(self): + myDB = db.DBConnection() + cloudlist = myDB.select('SELECT * from lastfmcloud') + return serve_template(templatename="extras.html", title="Extras", cloudlist=cloudlist) + return page + extras.exposed = True - def addReleaseById(self, rid): - threading.Thread(target=importer.addReleaseById, args=[rid]).start() - raise cherrypy.HTTPRedirect("home") - addReleaseById.exposed = True - - def updateCloud(self): - - lastfm.getSimilar() - raise cherrypy.HTTPRedirect("extras") - - updateCloud.exposed = True - - def api(self, *args, **kwargs): - - from headphones.api import Api - - a = Api() - - a.checkParams(*args, **kwargs) - - data = a.fetchData() - - return data - - api.exposed = True + def addReleaseById(self, rid): + threading.Thread(target=importer.addReleaseById, args=[rid]).start() + raise cherrypy.HTTPRedirect("home") + addReleaseById.exposed = True + + def updateCloud(self): + + lastfm.getSimilar() + raise cherrypy.HTTPRedirect("extras") + + updateCloud.exposed = True + + def api(self, *args, **kwargs): + + from headphones.api import Api + + a = Api() + + a.checkParams(*args, **kwargs) + + data = a.fetchData() + + return data + + api.exposed = True diff --git a/headphones/webstart.py b/headphones/webstart.py index 2c462c1c..7d0cfa73 100644 --- a/headphones/webstart.py +++ b/headphones/webstart.py @@ -10,16 +10,16 @@ from headphones.webserve import WebInterface def initialize(options={}): - cherrypy.config.update({ - 'log.screen': False, - 'server.thread_pool': 10, - 'server.socket_port': options['http_port'], - 'server.socket_host': options['http_host'], - 'engine.autoreload_on': False, - }) + cherrypy.config.update({ + 'log.screen': False, + 'server.thread_pool': 10, + 'server.socket_port': options['http_port'], + 'server.socket_host': options['http_host'], + 'engine.autoreload_on': False, + }) - conf = { - '/': { + conf = { + '/': { 'tools.staticdir.root': os.path.join(headphones.PROG_DIR, 'data') }, '/interfaces':{ @@ -39,32 +39,32 @@ def initialize(options={}): 'tools.staticdir.dir': "js" }, '/favicon.ico':{ - 'tools.staticfile.on': True, - 'tools.staticfile.filename': "images/favicon.ico" + 'tools.staticfile.on': True, + 'tools.staticfile.filename': "images/favicon.ico" } } - if options['http_password'] != "": - conf['/'].update({ - 'tools.auth_basic.on': True, - 'tools.auth_basic.realm': 'Headphones', - 'tools.auth_basic.checkpassword': cherrypy.lib.auth_basic.checkpassword_dict( - {options['http_username']:options['http_password']}) - }) - + if options['http_password'] != "": + conf['/'].update({ + 'tools.auth_basic.on': True, + 'tools.auth_basic.realm': 'Headphones', + 'tools.auth_basic.checkpassword': cherrypy.lib.auth_basic.checkpassword_dict( + {options['http_username']:options['http_password']}) + }) + - # Prevent time-outs - cherrypy.engine.timeout_monitor.unsubscribe() - - cherrypy.tree.mount(WebInterface(), options['http_root'], config = conf) - - try: - cherrypy.process.servers.check_port(options['http_host'], options['http_port']) - cherrypy.server.start() - except IOError: - print 'Failed to start on port: %i. Is something else running?' % (options['http_port']) - sys.exit(0) - - cherrypy.server.wait() - - \ No newline at end of file + # Prevent time-outs + cherrypy.engine.timeout_monitor.unsubscribe() + + cherrypy.tree.mount(WebInterface(), options['http_root'], config = conf) + + try: + cherrypy.process.servers.check_port(options['http_host'], options['http_port']) + cherrypy.server.start() + except IOError: + print 'Failed to start on port: %i. Is something else running?' % (options['http_port']) + sys.exit(0) + + cherrypy.server.wait() + +