From 38ee0e959bce3811aa623f346da8a146198c4a9f Mon Sep 17 00:00:00 2001 From: barbequesauce Date: Tue, 14 May 2019 10:02:21 -0400 Subject: [PATCH 01/13] FIX: notify on some cases of bad import data --- mylar/webserve.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mylar/webserve.py b/mylar/webserve.py index 7654458d..5ee44406 100644 --- a/mylar/webserve.py +++ b/mylar/webserve.py @@ -4465,7 +4465,8 @@ class WebInterface(object): movedata = [] for result in results: - if result is None: + if result is None or result == 'None': + logger.info('[IMPORT] Ultron gave me bad information, this issue wont import correctly: %s' & DynamicName) break if result['WatchMatch']: From 6550611e020af63a67878d538676378b1398a6bb Mon Sep 17 00:00:00 2001 From: doucheymcdoucherson Date: Sun, 16 Jun 2019 02:00:09 -0700 Subject: [PATCH 02/13] deluge options update --- data/interfaces/default/config.html | 16 ++++++++++++++++ mylar/config.py | 3 +++ mylar/torrent/clients/deluge.py | 18 ++++++++++++++++-- mylar/webserve.py | 5 ++++- 4 files changed, 39 insertions(+), 3 deletions(-) diff --git a/data/interfaces/default/config.html b/data/interfaces/default/config.html index 0722a42e..3c98d5c3 100644 --- a/data/interfaces/default/config.html +++ b/data/interfaces/default/config.html @@ -673,6 +673,22 @@
Label to be used on the torrents +
+ +
+ Where the torrent will be downloaded to +
+
+ +
+ Path that files will be moved to after download is complete +
+
+
+ + Add the torrent in a paused state +
+

diff --git a/mylar/config.py b/mylar/config.py index dd8c8f7f..63e45bb7 100644 --- a/mylar/config.py +++ b/mylar/config.py @@ -352,6 +352,9 @@ _CONFIG_DEFINITIONS = OrderedDict({ 'DELUGE_USERNAME': (str, 'Deluge', None), 'DELUGE_PASSWORD': (str, 'Deluge', None), 'DELUGE_LABEL': (str, 'Deluge', None), + 'DELUGE_PAUSE': (bool, 'Deluge', False), + 'DELUGE_DOWNLOAD_DIRECTORY': (str, 'Deluge', ""), + 'DELUGE_DONE_DIRECTORY': (str, 'Deluge', ""), 'QBITTORRENT_HOST': (str, 'qBittorrent', None), 'QBITTORRENT_USERNAME': (str, 'qBittorrent', None), diff --git a/mylar/torrent/clients/deluge.py b/mylar/torrent/clients/deluge.py index 15451b85..2f98b726 100644 --- a/mylar/torrent/clients/deluge.py +++ b/mylar/torrent/clients/deluge.py @@ -99,6 +99,18 @@ class TorrentClient(object): def load_torrent(self, filepath): + options = {} + + if mylar.CONFIG.DELUGE_DOWNLOAD_DIRECTORY: + options['download_location'] = mylar.CONFIG.DELUGE_DOWNLOAD_DIRECTORY + + if mylar.CONFIG.DELUGE_DONE_DIRECTORY: + options['move_completed'] = 1 + options['move_completed_path'] = mylar.CONFIG.DELUGE_DONE_DIRECTORY + + if mylar.CONFIG.DELUGE_PAUSE: + options['add_paused'] = int(mylar.CONFIG.DELUGE_PAUSE) + logger.info('filepath to torrent file set to : ' + filepath) torrent_id = False @@ -121,13 +133,13 @@ class TorrentClient(object): else: logger.info('Torrent not added yet, trying to add it now!') try: - torrent_id = self.client.call('core.add_torrent_file', str(os.path.basename(filepath)), base64.encodestring(torrentcontent), '') + torrent_id = self.client.call('core.add_torrent_file', str(os.path.basename(filepath)), base64.encodestring(torrentcontent), options) except Exception as e: logger.debug('Torrent not added') return False else: try: - torrent_id = self.client.call('core.add_torrent_magnet', str(filepath), {}) + torrent_id = self.client.call('core.add_torrent_magnet', str(filepath), options) except Exception as e: logger.debug('Torrent not added') return False @@ -158,10 +170,12 @@ class TorrentClient(object): return {'hash': torrent_info['hash'], 'label': mylar.CONFIG.DELUGE_LABEL, 'folder': torrent_info['save_path'], + 'move path': torrent_info['move_completed_path'], 'total_filesize': torrent_info['total_size'], 'name': torrent_info['name'], 'files': torrent_info['files'], 'time_started': torrent_info['active_time'], + 'pause': torrent_info['paused'], 'completed': torrent_info['is_finished']} diff --git a/mylar/webserve.py b/mylar/webserve.py index 5ee44406..ac9a0ec5 100644 --- a/mylar/webserve.py +++ b/mylar/webserve.py @@ -4986,6 +4986,9 @@ class WebInterface(object): "deluge_username": mylar.CONFIG.DELUGE_USERNAME, "deluge_password": mylar.CONFIG.DELUGE_PASSWORD, "deluge_label": mylar.CONFIG.DELUGE_LABEL, + "deluge_pause": helpers.checked(mylar.CONFIG.DELUGE_PAUSE), + "deluge_download_directory": mylar.CONFIG.DELUGE_DOWNLOAD_DIRECTORY, + "deluge_done_directory": mylar.CONFIG.DELUGE_DONE_DIRECTORY, "qbittorrent_host": mylar.CONFIG.QBITTORRENT_HOST, "qbittorrent_username": mylar.CONFIG.QBITTORRENT_USERNAME, "qbittorrent_password": mylar.CONFIG.QBITTORRENT_PASSWORD, @@ -5372,7 +5375,7 @@ class WebInterface(object): 'lowercase_filenames', 'autowant_upcoming', 'autowant_all', 'comic_cover_local', 'alternate_latest_series_covers', 'cvinfo', 'snatchedtorrent_notify', 'prowl_enabled', 'prowl_onsnatch', 'pushover_enabled', 'pushover_onsnatch', 'boxcar_enabled', 'boxcar_onsnatch', 'pushbullet_enabled', 'pushbullet_onsnatch', 'telegram_enabled', 'telegram_onsnatch', 'slack_enabled', 'slack_onsnatch', - 'email_enabled', 'email_enc', 'email_ongrab', 'email_onpost', 'opds_enable', 'opds_authentication', 'opds_metainfo', 'enable_ddl'] + 'email_enabled', 'email_enc', 'email_ongrab', 'email_onpost', 'opds_enable', 'opds_authentication', 'opds_metainfo', 'enable_ddl', 'deluge_pause'] for checked_config in checked_configs: if checked_config not in kwargs: From 9542c47fc2a04eb16083a9147d60c2ae20bd8ad2 Mon Sep 17 00:00:00 2001 From: Roberto Pastor Date: Mon, 17 Jun 2019 19:32:51 +0200 Subject: [PATCH 03/13] Reduced `getIndex` response data to only expose minimal and necessary data to the API. --- mylar/api.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/mylar/api.py b/mylar/api.py index 2e438731..d357a8e1 100644 --- a/mylar/api.py +++ b/mylar/api.py @@ -183,7 +183,18 @@ class Api(object): self.data = self._error_with_message('Incorrect username or password.') def _getIndex(self, **kwargs): - self.data = self._dic_from_query('SELECT * from comics order by ComicSortName COLLATE NOCASE') + self.data = self._dic_from_query( + "SELECT ComicID as id,\ + ComicName as name,\ + ComicImageURL as imageURL,\ + Status as status,\ + ComicPublisher as publisher,\ + ComicYear as year,\ + LatestIssue as latestIssue,\ + Total as totalIssues,\ + DetailURL as detailsURL\ + FROM comics \ + ORDER BY ComicSortName COLLATE NOCASE") return def _getReadList(self, **kwargs): From ae093da9e5274dc621c99efa489d141173e343cb Mon Sep 17 00:00:00 2001 From: Roberto Pastor Date: Mon, 24 Jun 2019 13:32:32 +0200 Subject: [PATCH 04/13] - Revamped 'getIndex' and 'getComic' commands --- mylar/api.py | 81 +++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 65 insertions(+), 16 deletions(-) diff --git a/mylar/api.py b/mylar/api.py index d357a8e1..3977a9ee 100644 --- a/mylar/api.py +++ b/mylar/api.py @@ -134,6 +134,40 @@ class Api(object): else: return self.data + def _selectForComics(self): + return 'SELECT \ + ComicID as id,\ + ComicName as name,\ + ComicImageURL as imageURL,\ + Status as status,\ + ComicPublisher as publisher,\ + ComicYear as year,\ + LatestIssue as latestIssue,\ + Total as totalIssues,\ + DetailURL as detailsURL\ + FROM comics' + + def _selectForIssues(self): + return 'SELECT \ + IssueID as id,\ + IssueName as name,\ + ImageURL as imageURL,\ + Issue_Number as number,\ + ReleaseDate as releaseDate,\ + IssueDate as issueDate,\ + Status as status\ + FROM issues' + + def _selectForAnnuals(self): + return 'SELECT \ + IssueID as id,\ + IssueName as name,\ + Issue_Number as number,\ + ReleaseDate as releaseDate,\ + IssueDate as issueDate,\ + Status as status\ + FROM annuals' + def _dic_from_query(self, query): myDB = db.DBConnection() rows = myDB.select(query) @@ -183,18 +217,14 @@ class Api(object): self.data = self._error_with_message('Incorrect username or password.') def _getIndex(self, **kwargs): - self.data = self._dic_from_query( - "SELECT ComicID as id,\ - ComicName as name,\ - ComicImageURL as imageURL,\ - Status as status,\ - ComicPublisher as publisher,\ - ComicYear as year,\ - LatestIssue as latestIssue,\ - Total as totalIssues,\ - DetailURL as detailsURL\ - FROM comics \ - ORDER BY ComicSortName COLLATE NOCASE") + + query = '{select} FROM comics ORDER BY ComicSortName COLLATE NOCASE'.format( + select = self._selectForComics(), + id = self.id + ) + + self.data = self._dic_from_query(query) + return def _getReadList(self, **kwargs): @@ -208,14 +238,33 @@ class Api(object): else: self.id = kwargs['id'] - comic = self._dic_from_query('SELECT * from comics WHERE ComicID="' + self.id + '"') - issues = self._dic_from_query('SELECT * from issues WHERE ComicID="' + self.id + '"order by Int_IssueNumber DESC') + comicQuery = '{select} WHERE ComicID="{id}" ORDER BY ComicSortName COLLATE NOCASE'.format( + select = self._selectForComics(), + id = self.id + ) + comic = self._dic_from_query(comicQuery) + + issuesQuery = '{select} WHERE ComicID="{id}" ORDER BY Int_IssueNumber DESC'.format( + select = self._selectForIssues(), + id = self.id + ) + issues = self._dic_from_query(issuesQuery) + if mylar.CONFIG.ANNUALS_ON: - annuals = self._dic_from_query('SELECT * FROM annuals WHERE ComicID="' + self.id + '"') + annualsQuery = '{select} WHERE ComicID="{id}"'.format( + select = self._selectForAnnuals(), + id = self.id + ) + annuals = self._dic_from_query(annualsQuery) else: annuals = [] - self.data = {'comic': comic, 'issues': issues, 'annuals': annuals} + self.data = { + 'comic': comic, + 'issues': issues, + 'annuals': annuals + } + return def _getHistory(self, **kwargs): From 33112c75f44f16c3bde67b9f7dff0f4631913b34 Mon Sep 17 00:00:00 2001 From: Roberto Pastor Date: Mon, 24 Jun 2019 13:49:50 +0200 Subject: [PATCH 05/13] - Fixed 'getIndex' command --- mylar/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mylar/api.py b/mylar/api.py index 3977a9ee..26c22abd 100644 --- a/mylar/api.py +++ b/mylar/api.py @@ -218,7 +218,7 @@ class Api(object): def _getIndex(self, **kwargs): - query = '{select} FROM comics ORDER BY ComicSortName COLLATE NOCASE'.format( + query = '{select} ORDER BY ComicSortName COLLATE NOCASE'.format( select = self._selectForComics(), id = self.id ) From e2f2a8f03b9daaae052da50c25c1e17342e851de Mon Sep 17 00:00:00 2001 From: Roberto Pastor Date: Mon, 24 Jun 2019 13:54:47 +0200 Subject: [PATCH 06/13] - Removed unnecessary parameter in format --- mylar/api.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mylar/api.py b/mylar/api.py index 26c22abd..9b393b05 100644 --- a/mylar/api.py +++ b/mylar/api.py @@ -219,8 +219,7 @@ class Api(object): def _getIndex(self, **kwargs): query = '{select} ORDER BY ComicSortName COLLATE NOCASE'.format( - select = self._selectForComics(), - id = self.id + select = self._selectForComics() ) self.data = self._dic_from_query(query) From 61632d67a0610a24ff15f1554caa53f7a2417ba5 Mon Sep 17 00:00:00 2001 From: Roberto Pastor Date: Mon, 24 Jun 2019 14:12:49 +0200 Subject: [PATCH 07/13] - Added ComicName to issues and annuals in 'getComic' command - Revamped 'getReadList' command --- mylar/api.py | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/mylar/api.py b/mylar/api.py index 9b393b05..e8e534a5 100644 --- a/mylar/api.py +++ b/mylar/api.py @@ -155,7 +155,8 @@ class Api(object): Issue_Number as number,\ ReleaseDate as releaseDate,\ IssueDate as issueDate,\ - Status as status\ + Status as status,\ + ComicName as comicName\ FROM issues' def _selectForAnnuals(self): @@ -165,9 +166,19 @@ class Api(object): Issue_Number as number,\ ReleaseDate as releaseDate,\ IssueDate as issueDate,\ - Status as status\ + Status as status,\ + ComicName as comicName\ FROM annuals' - + + def _selectForReadList(self): + return 'SELECT \ + IssueID as id,\ + Issue_Number as number,\ + IssueDate as issueDate,\ + Status as status,\ + ComicName as comicName\ + FROM readlist' + def _dic_from_query(self, query): myDB = db.DBConnection() rows = myDB.select(query) @@ -227,7 +238,13 @@ class Api(object): return def _getReadList(self, **kwargs): - self.data = self._dic_from_query('SELECT * from readlist order by IssueDate ASC') + + readListQuery ='{select} ORDER BY IssueDate ASC'.format( + select = self._selectForReadList() + ) + + self.data = self._dic_from_query(readListQuery) + return def _getComic(self, **kwargs): From 6a1f96167cf9cad6bf2e0ef7881b398e75b98169 Mon Sep 17 00:00:00 2001 From: Roberto Pastor Date: Tue, 25 Jun 2019 21:55:59 +0200 Subject: [PATCH 08/13] - Revamped getAPI, getVersion, getReadList, getIndex and getComic endpoints. - Normalized error responses - Normalized success responses for getVersion, getReadList, getIndex and getComic. --- mylar/api.py | 191 +++++++++++++++++++++++++++++---------------------- 1 file changed, 108 insertions(+), 83 deletions(-) diff --git a/mylar/api.py b/mylar/api.py index e8e534a5..58b45fec 100644 --- a/mylar/api.py +++ b/mylar/api.py @@ -38,9 +38,10 @@ cmd_list = ['getIndex', 'getComic', 'getUpcoming', 'getWanted', 'getHistory', 'getComicInfo', 'getIssueInfo', 'getArt', 'downloadIssue', 'downloadNZB', 'getReadList', 'getStoryArc', 'addStoryArc'] - class Api(object): + API_ERROR_CODE_DEFAULT = 460 + def __init__(self): self.apikey = None self.cmd = None @@ -54,25 +55,55 @@ class Api(object): self.apitype = None self.comicrn = False + def _failureResponse(self, errorMessage, code = API_ERROR_CODE_DEFAULT): + response = { + 'success': False, + 'error': { + 'code': code, + 'message': errorMessage + } + } + cherrypy.response.headers['Content-Type'] = "application/json" + return simplejson.dumps(response) + + def _successResponse(self, results): + response = { + 'success': True, + 'data': results + } + cherrypy.response.headers['Content-Type'] = "application/json" + return simplejson.dumps(response) + + def _resultsFromQuery(self, query): + myDB = db.DBConnection() + rows = myDB.select(query) + + results = [] + + for row in rows: + results.append(dict(zip(row.keys(), row))) + + return results + def checkParams(self, *args, **kwargs): if 'cmd' not in kwargs: - self.data = self._error_with_message('Missing parameter: cmd') + self.data = self._failureResponse('Missing parameter: cmd') return if 'apikey' not in kwargs and ('apikey' not in kwargs and kwargs['cmd'] != 'getAPI'): - self.data = self._error_with_message('Missing api key') + self.data = self._failureResponse('Missing API key') return elif kwargs['cmd'] == 'getAPI': self.apitype = 'normal' else: if not mylar.CONFIG.API_ENABLED: if kwargs['apikey'] != mylar.DOWNLOAD_APIKEY: - self.data = self._error_with_message('API not enabled') + self.data = self._failureResponse('API not enabled') return if kwargs['apikey'] != mylar.CONFIG.API_KEY and all([kwargs['apikey'] != mylar.DOWNLOAD_APIKEY, mylar.DOWNLOAD_APIKEY != None]): - self.data = self._error_with_message('Incorrect API key') + self.data = self._failureResponse('Incorrect API key') return else: if kwargs['apikey'] == mylar.CONFIG.API_KEY: @@ -83,22 +114,22 @@ class Api(object): self.apikey = kwargs.pop('apikey') if not([mylar.CONFIG.API_KEY, mylar.DOWNLOAD_APIKEY]): - self.data = self._error_with_message('API key not generated') + self.data = self._failureResponse('API key not generated') return if self.apitype: if self.apitype == 'normal' and len(mylar.CONFIG.API_KEY) != 32: - self.data = self._error_with_message('API key not generated correctly') + self.data = self._failureResponse('API key not generated correctly') return if self.apitype == 'download' and len(mylar.DOWNLOAD_APIKEY) != 32: - self.data = self._error_with_message('Download API key not generated correctly') + self.data = self._failureResponse('Download API key not generated correctly') return else: - self.data = self._error_with_message('API key not generated correctly') + self.data = self._failureResponse('API key not generated correctly') return if kwargs['cmd'] not in cmd_list: - self.data = self._error_with_message('Unknown command: %s' % kwargs['cmd']) + self.data = self._failureResponse('Unknown command: %s' % kwargs['cmd']) return else: self.cmd = kwargs.pop('cmd') @@ -179,38 +210,21 @@ class Api(object): ComicName as comicName\ FROM readlist' - 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 _error_with_message(self, message): - error = {'error': {'message': message} } - cherrypy.response.headers['Content-Type'] = "application/json" - return simplejson.dumps(error) - def _getAPI(self, **kwargs): if 'username' not in kwargs: - self.data = self._error_with_message('Missing parameter: username') + self.data = self._failureResponse('Missing parameter: username') return else: username = kwargs['username'] if 'password' not in kwargs: - self.data = self._error_with_message('Missing parameter: password') + self.data = self._failureResponse('Missing parameter: password') return else: password = kwargs['password'] if any([mylar.CONFIG.HTTP_USERNAME is None, mylar.CONFIG.HTTP_PASSWORD is None]): - self.data = self._error_with_message('Unable to use this command - username & password MUST be enabled.') + self.data = self._failureResponse('Unable to use this command - username & password MUST be enabled.') return ht_user = mylar.CONFIG.HTTP_USERNAME @@ -218,38 +232,47 @@ class Api(object): ed_chk = edc.decrypt_it() if mylar.CONFIG.ENCRYPT_PASSWORDS is True: if username == ht_user and all([ed_chk['status'] is True, ed_chk['password'] == password]): - self.data = {'apikey': mylar.CONFIG.API_KEY} + self.data = self._successResponse( + {'apikey': mylar.CONFIG.API_KEY} + ) else: - self.data = self._error_with_message('Incorrect username or password.') + self.data = self._failureResponse('Incorrect username or password.') else: if username == ht_user and password == mylar.CONFIG.HTTP_PASSWORD: - self.data = {'apikey': mylar.CONFIG.API_KEY} + self.data = self._successResponse( + {'apikey': mylar.CONFIG.API_KEY} + ) else: - self.data = self._error_with_message('Incorrect username or password.') + self.data = self._failureResponse('Incorrect username or password.') def _getIndex(self, **kwargs): query = '{select} ORDER BY ComicSortName COLLATE NOCASE'.format( select = self._selectForComics() ) - - self.data = self._dic_from_query(query) + + self.data = self._successResponse( + self._resultsFromQuery(query) + ) return def _getReadList(self, **kwargs): - readListQuery ='{select} ORDER BY IssueDate ASC'.format( + readListQuery = '{select} ORDER BY IssueDate ASC'.format( select = self._selectForReadList() ) - self.data = self._dic_from_query(readListQuery) - + self.data = self._successResponse( + self._resultsFromQuery(readListQuery) + ) + return def _getComic(self, **kwargs): + if 'id' not in kwargs: - self.data = self._error_with_message('Missing parameter: id') + self.data = self._failureResponse('Missing parameter: id') return else: self.id = kwargs['id'] @@ -258,33 +281,35 @@ class Api(object): select = self._selectForComics(), id = self.id ) - comic = self._dic_from_query(comicQuery) + comic = self._resultsFromQuery(comicQuery) issuesQuery = '{select} WHERE ComicID="{id}" ORDER BY Int_IssueNumber DESC'.format( select = self._selectForIssues(), id = self.id ) - issues = self._dic_from_query(issuesQuery) + issues = self._resultsFromQuery(issuesQuery) if mylar.CONFIG.ANNUALS_ON: annualsQuery = '{select} WHERE ComicID="{id}"'.format( select = self._selectForAnnuals(), id = self.id ) - annuals = self._dic_from_query(annualsQuery) + annuals = self._resultsFromQuery(annualsQuery) else: annuals = [] - self.data = { + self.data = self._successResponse({ 'comic': comic, 'issues': issues, 'annuals': annuals - } + }) return def _getHistory(self, **kwargs): - self.data = self._dic_from_query('SELECT * from snatched order by DateAdded DESC') + self.data = self._successResponse( + self._resultsFromQuery('SELECT * from snatched order by DateAdded DESC') + ) return def _getUpcoming(self, **kwargs): @@ -304,14 +329,14 @@ class Api(object): week = today.strftime('%U') year = today.strftime('%Y') - self.data = self._dic_from_query( + self.data = self._resultsFromQuery( "SELECT w.COMIC AS ComicName, w.ISSUE AS IssueNumber, w.ComicID, w.IssueID, w.SHIPDATE AS IssueDate, w.STATUS AS Status, c.ComicName AS DisplayComicName \ FROM weekly w JOIN comics c ON w.ComicID = c.ComicID WHERE w.COMIC IS NOT NULL AND w.ISSUE IS NOT NULL AND \ SUBSTR('0' || w.weeknumber, -2) = '" + week + "' AND w.year = '" + year + "' AND " + select_status_clause + " ORDER BY c.ComicSortName") return def _getWanted(self, **kwargs): - self.data = self._dic_from_query("SELECT * from issues WHERE Status='Wanted'") + self.data = self._resultsFromQuery("SELECT * from issues WHERE Status='Wanted'") return def _getLogs(self, **kwargs): @@ -325,7 +350,7 @@ class Api(object): def _delComic(self, **kwargs): if 'id' not in kwargs: - self.data = self._error_with_message('Missing parameter: id') + self.data = self._failureResponse('Missing parameter: id') return else: self.id = kwargs['id'] @@ -337,7 +362,7 @@ class Api(object): def _pauseComic(self, **kwargs): if 'id' not in kwargs: - self.data = self._error_with_message('Missing parameter: id') + self.data = self._failureResponse('Missing parameter: id') return else: self.id = kwargs['id'] @@ -349,7 +374,7 @@ class Api(object): def _resumeComic(self, **kwargs): if 'id' not in kwargs: - self.data = self._error_with_message('Missing parameter: id') + self.data = self._failureResponse('Missing parameter: id') return else: self.id = kwargs['id'] @@ -361,7 +386,7 @@ class Api(object): def _refreshComic(self, **kwargs): if 'id' not in kwargs: - self.data = self._error_with_message('Missing parameter: id') + self.data = self._failureResponse('Missing parameter: id') return else: self.id = kwargs['id'] @@ -375,7 +400,7 @@ class Api(object): def _addComic(self, **kwargs): if 'id' not in kwargs: - self.data = self._error_with_message('Missing parameter: id') + self.data = self._failureResponse('Missing parameter: id') return else: self.id = kwargs['id'] @@ -389,7 +414,7 @@ class Api(object): def _queueIssue(self, **kwargs): if 'id' not in kwargs: - self.data = self._error_with_message('Missing parameter: id') + self.data = self._failureResponse('Missing parameter: id') return else: self.id = kwargs['id'] @@ -402,7 +427,7 @@ class Api(object): def _unqueueIssue(self, **kwargs): if 'id' not in kwargs: - self.data = self._error_with_message('Missing parameter: id') + self.data = self._failureResponse('Missing parameter: id') return else: self.id = kwargs['id'] @@ -417,7 +442,7 @@ class Api(object): def _issueProcess(self, **kwargs): if 'comicid' not in kwargs: - self.data = self._error_with_message('Missing parameter: comicid') + self.data = self._failureResponse('Missing parameter: comicid') return else: self.comicid = kwargs['comicid'] @@ -428,7 +453,7 @@ class Api(object): self.issueid = kwargs['issueid'] if 'folder' not in kwargs: - self.data = self._error_with_message('Missing parameter: folder') + self.data = self._failureResponse('Missing parameter: folder') return else: self.folder = kwargs['folder'] @@ -441,13 +466,13 @@ class Api(object): def _forceProcess(self, **kwargs): if 'nzb_name' not in kwargs: - self.data = self._error_with_message('Missing parameter: nzb_name') + self.data = self._failureResponse('Missing parameter: nzb_name') return else: self.nzb_name = kwargs['nzb_name'] if 'nzb_folder' not in kwargs: - self.data = self._error_with_message('Missing parameter: nzb_folder') + self.data = self._failureResponse('Missing parameter: nzb_folder') return else: self.nzb_folder = kwargs['nzb_folder'] @@ -492,13 +517,13 @@ class Api(object): return def _getVersion(self, **kwargs): - self.data = { + self.data = self._successResponse({ 'git_path': mylar.CONFIG.GIT_PATH, 'install_type': mylar.INSTALL_TYPE, 'current_version': mylar.CURRENT_VERSION, 'latest_version': mylar.LATEST_VERSION, 'commits_behind': mylar.COMMITS_BEHIND, - } + }) def _checkGithub(self, **kwargs): versioncheck.checkGithub() @@ -515,7 +540,7 @@ class Api(object): def _getArtistArt(self, **kwargs): if 'id' not in kwargs: - self.data = self._error_with_message('Missing parameter: id') + self.data = self._failureResponse('Missing parameter: id') return else: self.id = kwargs['id'] @@ -524,7 +549,7 @@ class Api(object): def _getIssueArt(self, **kwargs): if 'id' not in kwargs: - self.data = self._error_with_message('Missing parameter: id') + self.data = self._failureResponse('Missing parameter: id') return else: self.id = kwargs['id'] @@ -533,7 +558,7 @@ class Api(object): def _getComicInfo(self, **kwargs): if 'id' not in kwargs: - self.data = self._error_with_message('Missing parameter: id') + self.data = self._failureResponse('Missing parameter: id') return else: self.id = kwargs['id'] @@ -542,7 +567,7 @@ class Api(object): def _getIssueInfo(self, **kwargs): if 'id' not in kwargs: - self.data = self._error_with_message('Missing parameter: id') + self.data = self._failureResponse('Missing parameter: id') return else: self.id = kwargs['id'] @@ -551,7 +576,7 @@ class Api(object): def _getArt(self, **kwargs): if 'id' not in kwargs: - self.data = self._error_with_message('Missing parameter: id') + self.data = self._failureResponse('Missing parameter: id') return else: self.id = kwargs['id'] @@ -567,7 +592,7 @@ class Api(object): return else: # If we cant find the image, lets check the db for a url. - comic = self._dic_from_query('SELECT * from comics WHERE ComicID="' + self.id + '"') + comic = self._resultsFromQuery('SELECT * from comics WHERE ComicID="' + self.id + '"') # Try every img url in the db try: @@ -586,9 +611,9 @@ class Api(object): self.img = image_path return else: - self.data = self._error_with_message('Failed return a image') + self.data = self._failureResponse('Failed return a image') else: - self.data = self._error_with_message('Failed to return a image') + self.data = self._failureResponse('Failed to return a image') def _findComic(self, name, issue=None, type_=None, mode=None, explisit=None, serinfo=None): # set defaults @@ -599,7 +624,7 @@ class Api(object): # Dont do shit if name is missing if len(name) == 0: - self.data = self._error_with_message('Missing a Comic name') + self.data = self._failureResponse('Missing a Comic name') return if type_ == 'comic' and mode == 'series': @@ -616,15 +641,15 @@ class Api(object): def _downloadIssue(self, id): if not id: - self.data = self._error_with_message('You need to provide a issueid') + self.data = self._failureResponse('You need to provide a issueid') return self.id = id # Fetch a list of dicts from issues table - i = self._dic_from_query('SELECT * from issues WHERE issueID="' + self.id + '"') + i = self._resultsFromQuery('SELECT * from issues WHERE issueID="' + self.id + '"') if not len(i): - self.data = self._error_with_message('Couldnt find a issue with issueID %s' % self.id) + self.data = self._failureResponse('Couldnt find a issue with issueID %s' % self.id) return # issueid is unique so it should one dict in the list @@ -635,7 +660,7 @@ class Api(object): # Check the issue is downloaded if issuelocation is not None: # Find the comic location - comic = self._dic_from_query('SELECT * from comics WHERE comicID="' + issue['ComicID'] + '"')[0] + comic = self._resultsFromQuery('SELECT * from comics WHERE comicID="' + issue['ComicID'] + '"')[0] comiclocation = comic.get('ComicLocation') f = os.path.join(comiclocation, issuelocation) if not os.path.isfile(f): @@ -648,12 +673,12 @@ class Api(object): self.file = f self.filename = issuelocation else: - self.data = self._error_with_message('You need to download that issue first') + self.data = self._failureResponse('You need to download that issue first') return def _downloadNZB(self, nzbname): if not nzbname: - self.data = self._error_with_message('You need to provide a nzbname') + self.data = self._failureResponse('You need to provide a nzbname') return self.nzbname = nzbname @@ -662,18 +687,18 @@ class Api(object): self.file = f self.filename = nzbname else: - self.data = self._error_with_message('NZBname does not exist within the cache directory. Unable to retrieve.') + self.data = self._failureResponse('NZBname does not exist within the cache directory. Unable to retrieve.') return def _getStoryArc(self, **kwargs): if not 'id' in kwargs: if 'customOnly' in kwargs and kwargs['customOnly']: - self.data = self._dic_from_query('SELECT StoryArcID, StoryArc, MAX(ReadingOrder) AS HighestOrder from storyarcs WHERE StoryArcID LIKE "C%" GROUP BY StoryArcID ORDER BY StoryArc') + self.data = self._resultsFromQuery('SELECT StoryArcID, StoryArc, MAX(ReadingOrder) AS HighestOrder from storyarcs WHERE StoryArcID LIKE "C%" GROUP BY StoryArcID ORDER BY StoryArc') else: - self.data = self._dic_from_query('SELECT StoryArcID, StoryArc, MAX(ReadingOrder) AS HighestOrder from storyarcs GROUP BY StoryArcID ORDER BY StoryArc') + self.data = self._resultsFromQuery('SELECT StoryArcID, StoryArc, MAX(ReadingOrder) AS HighestOrder from storyarcs GROUP BY StoryArcID ORDER BY StoryArc') else: self.id = kwargs['id'] - self.data = self._dic_from_query('SELECT StoryArc, ReadingOrder, ComicID, ComicName, IssueNumber, IssueID, \ + self.data = self._resultsFromQuery('SELECT StoryArc, ReadingOrder, ComicID, ComicName, IssueNumber, IssueID, \ IssueDate, IssueName, IssuePublisher from storyarcs WHERE StoryArcID="' + self.id + '" ORDER BY ReadingOrder') return @@ -682,17 +707,17 @@ class Api(object): if not 'id' in kwargs: self.id = 'C%04d' % random.randint(1, 9999) if not 'storyarcname' in kwargs: - self.data = self._error_with_message('You need to provide either id or storyarcname') + self.data = self._failureResponse('You need to provide either id or storyarcname') return else: storyarcname = kwargs.pop('storyarcname') else: self.id = kwargs.pop('id') - arc = self._dic_from_query('SELECT * from storyarcs WHERE StoryArcID="' + self.id + '" ORDER by ReadingOrder') + arc = self._resultsFromQuery('SELECT * from storyarcs WHERE StoryArcID="' + self.id + '" ORDER by ReadingOrder') storyarcname = arc[0]['StoryArc'] issuecount = len(arc) if not 'issues' in kwargs and not 'arclist' in kwargs: - self.data = self._error_with_message('No issues specified') + self.data = self._failureResponse('No issues specified') return else: arclist = "" From 15aadaaa23ba8c48aa34b5e3bc9d7a9287d00ce5 Mon Sep 17 00:00:00 2001 From: evilhero Date: Thu, 25 Jul 2019 20:36:44 -0400 Subject: [PATCH 09/13] FIX:(#2313) One-shot file name parsing would not properly detect details in some instances --- mylar/updater.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mylar/updater.py b/mylar/updater.py index 91ec96d7..c807345c 100755 --- a/mylar/updater.py +++ b/mylar/updater.py @@ -995,7 +995,7 @@ def forceRescan(ComicID, archive=None, module=None, recheck=False): break try: - if all([booktype == 'TPB', iscnt > 1]) or all([booktype == 'One-Shot', iscnt == 1]): + if all([booktype == 'TPB', iscnt > 1]) or all([booktype == 'One-Shot', iscnt == 1, cla['JusttheDigits'] is None]): if cla['SeriesVolume'] is not None: just_the_digits = re.sub('[^0-9]', '', cla['SeriesVolume']).strip() else: From d51ad541d15d081fc68732be9a3d4f809b4389c4 Mon Sep 17 00:00:00 2001 From: evilhero Date: Mon, 5 Aug 2019 14:50:45 -0400 Subject: [PATCH 10/13] FIX: fix to allow for the file parser to adjust for filenames where the date is prior to the issue number --- mylar/filechecker.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mylar/filechecker.py b/mylar/filechecker.py index f3ba9308..ccff49ef 100755 --- a/mylar/filechecker.py +++ b/mylar/filechecker.py @@ -800,7 +800,8 @@ class FileChecker(object): yearposition = x['yearposition'] yearmodposition = x['yearmodposition'] - if highest_series_pos > yearposition: highest_series_pos = yearposition #dc['position']: highest_series_pos = dc['position'] + if yearposition is not None and highest_series_pos > yearposition: + highest_series_pos = yearposition #dc['position']: highest_series_pos = dc['position'] else: issue_year = None yearposition = None From f4ea76345aca7294c536c783314eb838150645df Mon Sep 17 00:00:00 2001 From: Bart274 Date: Tue, 9 Jul 2019 14:18:19 +0200 Subject: [PATCH 11/13] Send snatched_name in Telegram messages on snatch The current snatch message is always just `Issue snatched!` --- mylar/search.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mylar/search.py b/mylar/search.py index d3f0a7a0..b516c101 100755 --- a/mylar/search.py +++ b/mylar/search.py @@ -2744,7 +2744,7 @@ def notify_snatch(sent_to, comicname, comyear, IssueNumber, nzbprov, pack): if mylar.CONFIG.TELEGRAM_ENABLED and mylar.CONFIG.TELEGRAM_ONSNATCH: logger.info(u"Sending Telegram notification") telegram = notifiers.TELEGRAM() - telegram.notify(snline) + telegram.notify(snline + " - " + snatched_name) if mylar.CONFIG.SLACK_ENABLED and mylar.CONFIG.SLACK_ONSNATCH: logger.info(u"Sending Slack notification") slack = notifiers.SLACK() From 9fb9ab4d732fad1c181df54e419975e5ec823d2a Mon Sep 17 00:00:00 2001 From: Barbeque Sauce Date: Tue, 30 Jul 2019 12:43:30 -0400 Subject: [PATCH 12/13] FIX: Progress Bar colors match index FIX: Progress Bar colors match index, #2282 --- data/interfaces/default/managecomics.html | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/data/interfaces/default/managecomics.html b/data/interfaces/default/managecomics.html index 0b0f8066..36261ca3 100755 --- a/data/interfaces/default/managecomics.html +++ b/data/interfaces/default/managecomics.html @@ -51,7 +51,15 @@ %for comic in comics: <% - if comic['Status'] == 'Paused': + + if comic['percent'] == 101: + css = '
' + if comic['percent'] == 100: + css = '
' + if comic['percent'] < 100: + css = '
' + + if comic['Status'] == 'Paused': grade = 'X' elif comic['Status'] == 'Loading': grade = 'L' @@ -79,7 +87,7 @@ ${comic['Status']} ${comic['LatestIssue']} (${comic['LatestDate']}) ${comic['ComicPublisher']} -
${comic['haveissues']}/${comic['totalissues']}
+ ${css}
${comic['haveissues']}/${comic['totalissues']}
${comic['DateAdded']} %endfor From 8d2e0ae1fe9086bd9511cf589673c74bfb88e91a Mon Sep 17 00:00:00 2001 From: evilhero Date: Fri, 4 Oct 2019 12:48:02 -0400 Subject: [PATCH 13/13] FIX:(#2347) module error when attempting to add storyarc --- mylar/webserve.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mylar/webserve.py b/mylar/webserve.py index ac9a0ec5..74cf4cfa 100644 --- a/mylar/webserve.py +++ b/mylar/webserve.py @@ -522,6 +522,8 @@ class WebInterface(object): logger.warn('Unable to download image from CV URL link: %s [Status Code returned: %s]' % (imageurl, r.status_code)) else: if r.headers.get('Content-Encoding') == 'gzip': + import gzip + from StringIO import StringIO buf = StringIO(r.content) f = gzip.GzipFile(fileobj=buf)