From ec6539a6cf12fac752a9c69400e67e66ade131df Mon Sep 17 00:00:00 2001 From: Yovarni Yearwood Date: Wed, 18 Sep 2019 22:03:09 -0400 Subject: [PATCH 01/27] Added win32api module to allow for long path names Windows has a 260 character limit on directory paths. The win32api module was installed and imported to allow for computers running Windows OS to bypass that limitation, as ComicRN was not able to move or rename files with a long file path. --- post-processing/autoProcessComics.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/post-processing/autoProcessComics.py b/post-processing/autoProcessComics.py index 44f9a1d2..b23df8e8 100644 --- a/post-processing/autoProcessComics.py +++ b/post-processing/autoProcessComics.py @@ -3,14 +3,26 @@ import os.path import ConfigParser import urllib2 import urllib +import platform + try: import requests use_requests = True except ImportError: - print "Requests module not found on system. I'll revert so this will work, but you probably should install " - print "requests to bypass this in the future (ie. pip install requests)" + print '''Requests module not found on system. I'll revert so this will work, but you probably should install + requests to bypass this in the future (i.e. pip install requests)''' use_requests = False +if platform.system() == 'Windows': + try: + import win32api + use_win32api = True + except ImportError: + print '''The win32api module was not found on this system. While it's fine to run without it, you're + running a Windows-based OS, so it would benefit you to install it. It enables ComicRN to better + work with file paths beyond the 260 character limit. Run "pip install pypiwin32".''' + + apc_version = "2.04" def processEpisode(dirName, nzbName=None): @@ -18,6 +30,8 @@ def processEpisode(dirName, nzbName=None): return processIssue(dirName, nzbName) def processIssue(dirName, nzbName=None, failed=False, comicrn_version=None): + if use_win32api is True: + dirName = win32api.GetShortPathName(dirName) config = ConfigParser.ConfigParser() configFilename = os.path.join(os.path.dirname(sys.argv[0]), "autoProcessComics.cfg") From a756f637a4bea76577b200f6afe895d04c6f46a7 Mon Sep 17 00:00:00 2001 From: Yovarni Yearwood Date: Sat, 21 Sep 2019 10:17:23 -0400 Subject: [PATCH 02/27] Fixed one instance of an error locking the search queue UnboundLocalError: local variable 'issueid_info' referenced before assignment --- mylar/search.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/mylar/search.py b/mylar/search.py index b516c101..89f0687a 100755 --- a/mylar/search.py +++ b/mylar/search.py @@ -1529,7 +1529,12 @@ def NZB_SEARCH(ComicName, IssueNumber, ComicYear, SeriesYear, Publisher, IssueDa issinfo = mylar.COMICINFO['pack_issuelist'] if issinfo is not None: #we need to get EVERY issue ID within the pack and update the log to reflect that they're being downloaded via a pack. - logger.fdebug('Found matching comic within pack...preparing to send to Updater with IssueIDs: %s and nzbname of %s' % (issueid_info, nzbname)) + + try: + logger.fdebug('Found matching comic within pack...preparing to send to Updater with IssueIDs: %s and nzbname of %s' % (issueid_info, nzbname)) + except NameError: + logger.fdebug('Did not find issueid_info') + #because packs need to have every issue that's not already Downloaded in a Snatched status, throw it to the updater here as well. for isid in issinfo['issues']: updater.nzblog(isid['issueid'], nzbname, ComicName, SARC=SARC, IssueArcID=IssueArcID, id=nzbid, prov=tmpprov, oneoff=oneoff) From 22bf11ef13d5167de0adc88b0829890d6cbd50a1 Mon Sep 17 00:00:00 2001 From: Yovarni Yearwood Date: Sat, 21 Sep 2019 14:46:06 -0400 Subject: [PATCH 03/27] Fixed incorrect volume/issue parsing Some comics declare their volume/issue as v01.002 (volume #1, issue #2) but with the way the application parses this, it becomes "v01002" which can't be properly parsed. TypeError: expected string or buffer --- mylar/filechecker.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mylar/filechecker.py b/mylar/filechecker.py index ccff49ef..36c0167e 100755 --- a/mylar/filechecker.py +++ b/mylar/filechecker.py @@ -597,7 +597,10 @@ class FileChecker(object): #now we try to find the series title &/or volume lablel. if any( [sf.lower().startswith('v'), sf.lower().startswith('vol'), volumeprior == True, 'volume' in sf.lower(), 'vol' in sf.lower(), 'part' in sf.lower()] ) and sf.lower() not in {'one','two','three','four','five','six'}: if any([ split_file[split_file.index(sf)].isdigit(), split_file[split_file.index(sf)][3:].isdigit(), split_file[split_file.index(sf)][1:].isdigit() ]): - volume = re.sub("[^0-9]", "", sf) + if '.' in sf: + volume = sf.split('.')[0] + else: + volume = re.sub("[^0-9]", "", sf) if volumeprior: try: volume_found['position'] = split_file.index(volumeprior_label, current_pos -1) #if this passes, then we're ok, otherwise will try exception From 4ae28f6dbf355a8e53dbcaa7743ad46251be0d0a Mon Sep 17 00:00:00 2001 From: Yovarni Yearwood Date: Sat, 21 Sep 2019 14:48:03 -0400 Subject: [PATCH 04/27] Fixed sandwich variable not being assigned Added try-except for post-processing failed comics when sandwich was not defined by current parameters. UnboundLocalError: local variable 'sandwich' referenced before assignment --- mylar/Failed.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/mylar/Failed.py b/mylar/Failed.py index 66358c82..29e72706 100644 --- a/mylar/Failed.py +++ b/mylar/Failed.py @@ -170,15 +170,21 @@ class FailedProcessor(object): sandwich = issueid elif 'G' in issueid or '-' in issueid: sandwich = 1 - if helpers.is_number(sandwich): - if sandwich < 900000: + try: + if helpers.is_number(sandwich): + if sandwich < 900000: # if sandwich is less than 900000 it's a normal watchlist download. Bypass. - pass - else: - logger.info('Failed download handling for story-arcs and one-off\'s are not supported yet. Be patient!') - self._log(' Unable to locate downloaded file to rename. PostProcessing aborted.') + pass + else: + logger.info('Failed download handling for story-arcs and one-off\'s are not supported yet. Be patient!') + self._log(' Unable to locate downloaded file to rename. PostProcessing aborted.') + self.valreturn.append({"self.log": self.log, + "mode": 'stop'}) + return self.queue.put(self.valreturn) + except NameError: + logger.info('sandwich was not defined. Post-processing aborted...') self.valreturn.append({"self.log": self.log, - "mode": 'stop'}) + "mode": 'stop'}) return self.queue.put(self.valreturn) From 07750a32da226706f6813a88c5b5b00a7c9c01cb Mon Sep 17 00:00:00 2001 From: Yovarni Yearwood Date: Mon, 23 Sep 2019 10:28:17 -0400 Subject: [PATCH 05/27] Update for previous fix regarding volume parsing Narrowed the parameters in which a str.split() would be used to be more exclusive --- mylar/filechecker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mylar/filechecker.py b/mylar/filechecker.py index 36c0167e..541fd3c3 100755 --- a/mylar/filechecker.py +++ b/mylar/filechecker.py @@ -597,7 +597,7 @@ class FileChecker(object): #now we try to find the series title &/or volume lablel. if any( [sf.lower().startswith('v'), sf.lower().startswith('vol'), volumeprior == True, 'volume' in sf.lower(), 'vol' in sf.lower(), 'part' in sf.lower()] ) and sf.lower() not in {'one','two','three','four','five','six'}: if any([ split_file[split_file.index(sf)].isdigit(), split_file[split_file.index(sf)][3:].isdigit(), split_file[split_file.index(sf)][1:].isdigit() ]): - if '.' in sf: + if all(identifier in sf for identifier in ['.', 'v']): volume = sf.split('.')[0] else: volume = re.sub("[^0-9]", "", sf) From 46cd4d931718741f7dbb76facb8d4de475721270 Mon Sep 17 00:00:00 2001 From: Peluko Date: Thu, 19 Sep 2019 20:18:55 +0200 Subject: [PATCH 06/27] Fixed index out of range when parsing issue number --- mylar/filechecker.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mylar/filechecker.py b/mylar/filechecker.py index 541fd3c3..9fcda37f 100755 --- a/mylar/filechecker.py +++ b/mylar/filechecker.py @@ -516,9 +516,9 @@ class FileChecker(object): if all([lastissue_position == (split_file.index(sf) -1), lastissue_label is not None, '#' not in sf, sf != 'p']): #find it in the original file to see if there's a decimal between. findst = lastissue_mod_position+1 - if findst > len(modfilename): + if findst >= len(modfilename): findst = len(modfilename) -1 - + if modfilename[findst] != '.' or modfilename[findst] != '#': #findst != '.' and findst != '#': if sf.isdigit(): seper_num = False From 2776f47198def1a1b65959ffde446af457cd121f Mon Sep 17 00:00:00 2001 From: Peluko Date: Thu, 19 Sep 2019 20:47:27 +0200 Subject: [PATCH 07/27] Not integer exception when file is like 'Something 1-74+Annuals 1-3' --- mylar/helpers.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mylar/helpers.py b/mylar/helpers.py index 8c663646..0ee7df7b 100755 --- a/mylar/helpers.py +++ b/mylar/helpers.py @@ -2476,7 +2476,8 @@ def issue_find_ids(ComicName, ComicID, pack, IssueNumber): tmp_annuals = pack[pack.find('Annual'):] tmp_ann = re.sub('[annual/annuals/+]', '', tmp_annuals.lower()).strip() tmp_pack = re.sub('[annual/annuals/+]', '', pack.lower()).strip() - pack_issues = range(int(tmp_pack[:tmp_pack.find('-')]),int(tmp_pack[tmp_pack.find('-')+1:])+1) + pack_issues_numbers = re.findall(r'\d+', tmp_pack) + pack_issues = range(int(pack_issues_numbers[0]),int(pack_issues_numbers[1])+1) annualize = True issues = {} From 32bacb4e30d0b3a9bca65c168265891ff8bc2b25 Mon Sep 17 00:00:00 2001 From: evilhero Date: Fri, 4 Oct 2019 14:05:34 -0400 Subject: [PATCH 08/27] FIX: DDL Quietly Fails if /ddl_location is set to a location that doesn't exist (#2278) --- mylar/getcomics.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/mylar/getcomics.py b/mylar/getcomics.py index ede1909f..eb483c45 100644 --- a/mylar/getcomics.py +++ b/mylar/getcomics.py @@ -375,6 +375,14 @@ class GC(object): #write the filename to the db for tracking purposes... myDB.upsert('ddl_info', {'filename': filename, 'remote_filesize': remote_filesize}, {'id': id}) + if mylar.CONFIG.DDL_LOCATION is not None and not os.path.isdir(mylar.CONFIG.DDL_LOCATION): + checkdirectory = mylar.filechecker.validateAndCreateDirectory(mylar.CONFIG.DDL_LOCATION, True) + if not checkdirectory: + logger.warn('[ABORTING] Error trying to validate/create DDL download directory: %s.' % mylar.CONFIG.DDL_LOCATION) + return ({"success": False, + "filename": filename, + "path": None}) + path = os.path.join(mylar.CONFIG.DDL_LOCATION, filename) if t.headers.get('content-encoding') == 'gzip': #.get('Content-Encoding') == 'gzip': From 356ecf0dca2da86c1b3ccbfb62c7d8020cf9de26 Mon Sep 17 00:00:00 2001 From: evilhero Date: Fri, 4 Oct 2019 14:08:36 -0400 Subject: [PATCH 09/27] FIX:(#2355) Fix for incorrectly identifying some series as One-Shot instead of Print --- mylar/cv.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mylar/cv.py b/mylar/cv.py index c243249d..bd107a24 100755 --- a/mylar/cv.py +++ b/mylar/cv.py @@ -344,7 +344,7 @@ def GetComicInfo(comicid, dom, safechk=None): comic['Type'] = 'Print' if comic_desc != 'None' and comic['Type'] == 'None': - if 'print' in comic_desc[:60].lower() and all(['print edition can be found' not in comic_desc.lower(), 'reprints' not in comic_desc.lower()]): + if 'print' in comic_desc[:60].lower() and all(['for the printed edition' not in comic_desc.lower(), 'print edition can be found' not in comic_desc.lower(), 'reprints' not in comic_desc.lower()]): comic['Type'] = 'Print' elif 'digital' in comic_desc[:60].lower() and 'digital edition can be found' not in comic_desc.lower(): comic['Type'] = 'Digital' @@ -352,10 +352,10 @@ def GetComicInfo(comicid, dom, safechk=None): comic['Type'] = 'TPB' elif 'hardcover' in comic_desc[:60].lower() and 'hardcover can be found' not in comic_desc.lower(): comic['Type'] = 'HC' - elif any(['one-shot' in comic_desc[:60].lower(), 'one shot' in comic_desc[:60].lower()]) and any(['can be found' not in comic_desc.lower(), 'following the' not in comic_desc.lower()]): + elif any(['one-shot' in comic_desc[:60].lower(), 'one shot' in comic_desc[:60].lower()]) and any(['can be found' not in comic_desc.lower(), 'following the' not in comic_desc.lower(), 'after the' not in comic_desc.lower()]): i = 0 comic['Type'] = 'One-Shot' - avoidwords = ['preceding', 'after the special', 'following the'] + avoidwords = ['preceding', 'after the', 'following the'] while i < 2: if i == 0: cbd = 'one-shot' From 29f39e59ed8b53a6ff99431312358389d28ae861 Mon Sep 17 00:00:00 2001 From: evilhero Date: Sat, 5 Oct 2019 11:53:10 -0400 Subject: [PATCH 10/27] FIX: filenames having a decimal as a space delimiter and also a series titles containting a hyphen would not be able to have the issue number properly detected --- mylar/filechecker.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mylar/filechecker.py b/mylar/filechecker.py index 9fcda37f..a9421524 100755 --- a/mylar/filechecker.py +++ b/mylar/filechecker.py @@ -854,6 +854,11 @@ class FileChecker(object): 'number': pis['number'], 'position': pis['position']}) continue + #2019-10-05 fix - if decimal-spaced filename has a series title with a hyphen will include issue # as part of series title + elif yearposition == pis['position']: + logger.info('Already validated year, ignoring as possible issue number: ' + str(pis['number'])) + continue + #end 2019-10-05 elif yearposition == pis['position']: logger.fdebug('Already validated year, ignoring as possible issue number: ' + str(pis['number'])) continue From e350eca3ef1c28e1469e824c8b427999b9d7b743 Mon Sep 17 00:00:00 2001 From: evilhero Date: Mon, 7 Oct 2019 13:18:17 -0400 Subject: [PATCH 11/27] FIX:(#2359) Unicode logging error on status update during search --- mylar/updater.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/mylar/updater.py b/mylar/updater.py index c807345c..57413c8f 100755 --- a/mylar/updater.py +++ b/mylar/updater.py @@ -856,8 +856,7 @@ def foundsearch(ComicID, IssueID, mode=None, down=None, provider=None, SARC=None pass myDB.upsert("oneoffhistory", newValue, ctlVal) - - logger.info(module + ' Updated the status (Snatched) complete for ' + ComicName + ' Issue: ' + str(IssueNum)) + logger.info('%s Updated the status (Snatched) complete for %s Issue: %s' % (module, ComicName, IssueNum)) else: if down == 'PP': logger.info(module + ' Setting status to Post-Processed in history.') @@ -917,7 +916,7 @@ def foundsearch(ComicID, IssueID, mode=None, down=None, provider=None, SARC=None newVal['year'] = pullinfo['year'] myDB.upsert("oneoffhistory", newVal, ctlVal) - logger.info(module + ' Updating Status (' + downstatus + ') now complete for ' + ComicName + ' issue: ' + IssueNum) + logger.info('%s Updating Status (%s) now completed for %s issue: %s' (module, downstatus, ComicName, IssueNum)) return def forceRescan(ComicID, archive=None, module=None, recheck=False): From a1f844f5cdb6eff8d594ed6f4fbecc72c95dd373 Mon Sep 17 00:00:00 2001 From: evilhero Date: Wed, 9 Oct 2019 21:30:15 -0400 Subject: [PATCH 12/27] FIX: undeclared variable when using ComicRN script on non-windows OS --- post-processing/autoProcessComics.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/post-processing/autoProcessComics.py b/post-processing/autoProcessComics.py index b23df8e8..6b0c8cd3 100644 --- a/post-processing/autoProcessComics.py +++ b/post-processing/autoProcessComics.py @@ -13,6 +13,7 @@ except ImportError: requests to bypass this in the future (i.e. pip install requests)''' use_requests = False +use_win32api = False if platform.system() == 'Windows': try: import win32api @@ -21,8 +22,7 @@ if platform.system() == 'Windows': print '''The win32api module was not found on this system. While it's fine to run without it, you're running a Windows-based OS, so it would benefit you to install it. It enables ComicRN to better work with file paths beyond the 260 character limit. Run "pip install pypiwin32".''' - - + apc_version = "2.04" def processEpisode(dirName, nzbName=None): From 36f544b6d9455d0f2a4b2eb91ae3a762b558c3de Mon Sep 17 00:00:00 2001 From: evilhero Date: Fri, 11 Oct 2019 10:15:12 -0400 Subject: [PATCH 13/27] FIX: 'str' object error during post-processing runs --- mylar/updater.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mylar/updater.py b/mylar/updater.py index 57413c8f..75c2a1a8 100755 --- a/mylar/updater.py +++ b/mylar/updater.py @@ -916,7 +916,7 @@ def foundsearch(ComicID, IssueID, mode=None, down=None, provider=None, SARC=None newVal['year'] = pullinfo['year'] myDB.upsert("oneoffhistory", newVal, ctlVal) - logger.info('%s Updating Status (%s) now completed for %s issue: %s' (module, downstatus, ComicName, IssueNum)) + logger.info('%s Updating Status (%s) now completed for %s issue: %s' % (module, downstatus, ComicName, IssueNum)) return def forceRescan(ComicID, archive=None, module=None, recheck=False): From ffd597b2cf5a2ab7c5fb1a5cb9426243a43b30a7 Mon Sep 17 00:00:00 2001 From: evilhero Date: Fri, 8 Nov 2019 11:22:02 -0500 Subject: [PATCH 14/27] FIX: When doing search match comparisons would ignore the year matching criteria if the filename contained the issue year, but the Volume label in Mylar was V1 --- mylar/search.py | 48 +++++++++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/mylar/search.py b/mylar/search.py index 89f0687a..eb13a953 100755 --- a/mylar/search.py +++ b/mylar/search.py @@ -1122,6 +1122,9 @@ def NZB_SEARCH(ComicName, IssueNumber, ComicYear, SeriesYear, Publisher, IssueDa continue else: logger.fdebug('match_check: %s' % filecomic) + if filecomic['process_status'] == 'fail': + logger.fdebug('%s was not a match to %s (%s)' % (cleantitle, ComicName, SeriesYear)) + continue elif booktype != parsed_comic['booktype']: logger.fdebug('Booktypes do not match. Looking for %s, this is a %s. Ignoring this result.' % (booktype, parsed_comic['booktype'])) continue @@ -1148,39 +1151,38 @@ def NZB_SEARCH(ComicName, IssueNumber, ComicYear, SeriesYear, Publisher, IssueDa fndcomicversion = None if parsed_comic['series_volume'] is not None: - versionfound = "yes" - if len(parsed_comic['series_volume'][1:]) == 4 and parsed_comic['series_volume'][1:].isdigit(): #v2013 - logger.fdebug("[Vxxxx] Version detected as %s" % (parsed_comic['series_volume'])) - vers4year = "yes" #re.sub("[^0-9]", " ", str(ct)) #remove the v + versionfound = "yes" + if len(parsed_comic['series_volume'][1:]) == 4 and parsed_comic['series_volume'][1:].isdigit(): #v2013 + logger.fdebug("[Vxxxx] Version detected as %s" % (parsed_comic['series_volume'])) + vers4year = "yes" #re.sub("[^0-9]", " ", str(ct)) #remove the v + fndcomicversion = parsed_comic['series_volume'] + elif len(parsed_comic['series_volume'][1:]) == 1 and parsed_comic['series_volume'][1:].isdigit(): #v2 + logger.fdebug("[Vx] Version detected as %s" % parsed_comic['series_volume']) + vers4vol = parsed_comic['series_volume'] + fndcomicversion = parsed_comic['series_volume'] + elif parsed_comic['series_volume'][1:].isdigit() and len(parsed_comic['series_volume']) < 4: + logger.fdebug('[Vxxx] Version detected as %s' % parsed_comic['series_volume']) + vers4vol = parsed_comic['series_volume'] + fndcomicversion = parsed_comic['series_volume'] + elif parsed_comic['series_volume'].isdigit() and len(parsed_comic['series_volume']) <=4: + # this stuff is necessary for 32P volume manipulation + if len(parsed_comic['series_volume']) == 4: + vers4year = "yes" fndcomicversion = parsed_comic['series_volume'] - elif len(parsed_comic['series_volume'][1:]) == 1 and parsed_comic['series_volume'][1:].isdigit(): #v2 - logger.fdebug("[Vx] Version detected as %s" % parsed_comic['series_volume']) + elif len(parsed_comic['series_volume']) == 1: vers4vol = parsed_comic['series_volume'] fndcomicversion = parsed_comic['series_volume'] - elif parsed_comic['series_volume'][1:].isdigit() and len(parsed_comic['series_volume']) < 4: - logger.fdebug('[Vxxx] Version detected as %s' % parsed_comic['series_volume']) + elif len(parsed_comic['series_volume']) < 4: vers4vol = parsed_comic['series_volume'] fndcomicversion = parsed_comic['series_volume'] - elif parsed_comic['series_volume'].isdigit() and len(parsed_comic['series_volume']) <=4: - # this stuff is necessary for 32P volume manipulation - if len(parsed_comic['series_volume']) == 4: - vers4year = "yes" - fndcomicversion = parsed_comic['series_volume'] - elif len(parsed_comic['series_volume']) == 1: - vers4vol = parsed_comic['series_volume'] - fndcomicversion = parsed_comic['series_volume'] - elif len(parsed_comic['series_volume']) < 4: - vers4vol = parsed_comic['series_volume'] - fndcomicversion = parsed_comic['series_volume'] - else: - logger.fdebug("error - unknown length for : %s" % parsed_comic['series_volume']) - + else: + logger.fdebug("error - unknown length for : %s" % parsed_comic['series_volume']) yearmatch = "false" if vers4vol != "no" or vers4year != "no": logger.fdebug("Series Year not provided but Series Volume detected of %s. Bypassing Year Match." % fndcomicversion) yearmatch = "true" - elif ComVersChk == 0: + elif ComVersChk == 0 and parsed_comic['issue_year'] is None: logger.fdebug("Series version detected as V1 (only series in existance with that title). Bypassing Year/Volume check") yearmatch = "true" elif any([UseFuzzy == "0", UseFuzzy == "2", UseFuzzy is None, IssDateFix != "no"]) and parsed_comic['issue_year'] is not None: From f8a8cb2a6b904c93b61d0e34edd6a16a0641ab36 Mon Sep 17 00:00:00 2001 From: evilhero Date: Wed, 20 Nov 2019 10:27:55 -0500 Subject: [PATCH 15/27] FIX: Removed Public Torrents as an available search option (forced option to False for possible future additions), FIX: Updated folder format description to show different path variations based on OS --- data/interfaces/default/config.html | 10 ++++++++-- mylar/config.py | 3 +++ mylar/webserve.py | 4 ++-- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/data/interfaces/default/config.html b/data/interfaces/default/config.html index 3c98d5c3..6e467066 100644 --- a/data/interfaces/default/config.html +++ b/data/interfaces/default/config.html @@ -818,12 +818,14 @@ Torrents
+
@@ -1212,7 +1214,11 @@ %> Use: $Publisher, $Series, $Year
- E.g.: $Publisher/$Series ($Year) = DC Comics/Action Comics (2011)
+ %if 'windows' in mylar.OS_DETECT.lower(): + E.g.: $Publisher\$Series ($Year) = DC Comics\Action Comics (2011) + %else: + E.g.: $Publisher/$Series ($Year) = DC Comics/Action Comics (2011) + %endif
diff --git a/mylar/config.py b/mylar/config.py index 63e45bb7..41accffc 100644 --- a/mylar/config.py +++ b/mylar/config.py @@ -841,6 +841,9 @@ class Config(object): self.ALT_PULL = 2 config.set('Weekly', 'alt_pull', str(self.ALT_PULL)) + #force off public torrents usage as currently broken. + self.ENABLE_PUBLIC = False + try: if not any([self.SAB_HOST is None, self.SAB_HOST == '', 'http://' in self.SAB_HOST[:7], 'https://' in self.SAB_HOST[:8]]): self.SAB_HOST = 'http://' + self.SAB_HOST diff --git a/mylar/webserve.py b/mylar/webserve.py index 74cf4cfa..7db738de 100644 --- a/mylar/webserve.py +++ b/mylar/webserve.py @@ -5370,14 +5370,14 @@ class WebInterface(object): checked_configs = ['enable_https', 'launch_browser', 'syno_fix', 'auto_update', 'annuals_on', 'api_enabled', 'nzb_startup_search', 'enforce_perms', 'sab_to_mylar', 'torrent_local', 'torrent_seedbox', 'rtorrent_ssl', 'rtorrent_verify', 'rtorrent_startonload', 'enable_torrents', 'enable_rss', 'nzbsu', 'nzbsu_verify', - 'dognzb', 'dognzb_verify', 'experimental', 'enable_torrent_search', 'enable_public', 'enable_32p', 'enable_torznab', + 'dognzb', 'dognzb_verify', 'experimental', 'enable_torrent_search', 'enable_32p', 'enable_torznab', 'newznab', 'use_minsize', 'use_maxsize', 'ddump', 'failed_download_handling', 'sab_client_post_processing', 'nzbget_client_post_processing', 'failed_auto', 'post_processing', 'enable_check_folder', 'enable_pre_scripts', 'enable_snatch_script', 'enable_extra_scripts', 'enable_meta', 'cbr2cbz_only', 'ct_tag_cr', 'ct_tag_cbl', 'ct_cbz_overwrite', 'rename_files', 'replace_spaces', 'zero_level', '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', 'deluge_pause'] + 'email_enabled', 'email_enc', 'email_ongrab', 'email_onpost', 'opds_enable', 'opds_authentication', 'opds_metainfo', 'enable_ddl', 'deluge_pause'] #enable_public for checked_config in checked_configs: if checked_config not in kwargs: From 66897c15499bac615211cf7186a072e75336c0e6 Mon Sep 17 00:00:00 2001 From: Barbeque Sauce Date: Mon, 2 Dec 2019 13:30:27 -0500 Subject: [PATCH 16/27] IMP: Updated deluge_client for v2 compatibility --- lib/deluge_client/__init__.py | 2 +- lib/deluge_client/client.py | 252 ++++++++++++++++++++++----- lib/deluge_client/rencode.py | 310 +++++++++++++++++++--------------- lib/deluge_client/tests.py | 96 +++++++---- 4 files changed, 443 insertions(+), 217 deletions(-) diff --git a/lib/deluge_client/__init__.py b/lib/deluge_client/__init__.py index e70f458d..125e0d16 100644 --- a/lib/deluge_client/__init__.py +++ b/lib/deluge_client/__init__.py @@ -1 +1 @@ -from .client import DelugeRPCClient \ No newline at end of file +from .client import DelugeRPCClient, FailedToReconnectException \ No newline at end of file diff --git a/lib/deluge_client/client.py b/lib/deluge_client/client.py index 59695da2..827fcd7c 100644 --- a/lib/deluge_client/client.py +++ b/lib/deluge_client/client.py @@ -2,6 +2,7 @@ import logging import socket import ssl import struct +import warnings import zlib from .rencode import dumps, loads @@ -10,102 +11,265 @@ RPC_RESPONSE = 1 RPC_ERROR = 2 RPC_EVENT = 3 -#MESSAGE_HEADER_SIZE = 5 +MESSAGE_HEADER_SIZE = 5 READ_SIZE = 10 logger = logging.getLogger(__name__) -class ConnectionLostException(Exception): + +class DelugeClientException(Exception): + """Base exception for all deluge client exceptions""" + + +class ConnectionLostException(DelugeClientException): pass -class CallTimeoutException(Exception): + +class CallTimeoutException(DelugeClientException): pass + +class InvalidHeaderException(DelugeClientException): + pass + + +class FailedToReconnectException(DelugeClientException): + pass + + +class RemoteException(DelugeClientException): + pass + + class DelugeRPCClient(object): timeout = 20 - - def __init__(self, host, port, username, password): + + def __init__(self, host, port, username, password, decode_utf8=False, automatic_reconnect=True): self.host = host self.port = port self.username = username self.password = password - + self.deluge_version = None + # This is only applicable if deluge_version is 2 + self.deluge_protocol_version = None + + self.decode_utf8 = decode_utf8 + if not self.decode_utf8: + warnings.warn('Using `decode_utf8=False` is deprecated, please set it to True.' + 'The argument will be removed in a future release where it will be always True', DeprecationWarning) + + self.automatic_reconnect = automatic_reconnect + self.request_id = 1 self.connected = False self._create_socket() - + def _create_socket(self, ssl_version=None): if ssl_version is not None: self._socket = ssl.wrap_socket(socket.socket(socket.AF_INET, socket.SOCK_STREAM), ssl_version=ssl_version) else: self._socket = ssl.wrap_socket(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) self._socket.settimeout(self.timeout) - + def connect(self): """ Connects to the Deluge instance """ + self._connect() + logger.debug('Connected to Deluge, detecting daemon version') + self._detect_deluge_version() + logger.debug('Daemon version {} detected, logging in'.format(self.deluge_version)) + if self.deluge_version == 2: + result = self.call('daemon.login', self.username, self.password, client_version='deluge-client') + else: + result = self.call('daemon.login', self.username, self.password) + logger.debug('Logged in with value %r' % result) + self.connected = True + + def _connect(self): logger.info('Connecting to %s:%s' % (self.host, self.port)) try: self._socket.connect((self.host, self.port)) except ssl.SSLError as e: - if e.reason != 'UNSUPPORTED_PROTOCOL' or not hasattr(ssl, 'PROTOCOL_SSLv3'): + # Note: have not verified that we actually get errno 258 for this error + if (hasattr(ssl, 'PROTOCOL_SSLv3') and + (getattr(e, 'reason', None) == 'UNSUPPORTED_PROTOCOL' or e.errno == 258)): + logger.warning('Was unable to ssl handshake, trying to force SSLv3 (insecure)') + self._create_socket(ssl_version=ssl.PROTOCOL_SSLv3) + self._socket.connect((self.host, self.port)) + else: raise - - logger.warning('Was unable to ssl handshake, trying to force SSLv3 (insecure)') - self._create_socket(ssl_version=ssl.PROTOCOL_SSLv3) - self._socket.connect((self.host, self.port)) - - logger.debug('Connected to Deluge, logging in') - result = self.call('daemon.login', self.username, self.password) - logger.debug('Logged in with value %r' % result) - self.connected = True - + def disconnect(self): """ Disconnect from deluge """ if self.connected: self._socket.close() - - def call(self, method, *args, **kwargs): - """ - Calls an RPC function - """ + self._socket = None + self.connected = False + + def _detect_deluge_version(self): + if self.deluge_version is not None: + return + + self._send_call(1, None, 'daemon.info') + self._send_call(2, None, 'daemon.info') + self._send_call(2, 1, 'daemon.info') + result = self._socket.recv(1) + if result[:1] == b'D': + # This is a protocol deluge 2.0 was using before release + self.deluge_version = 2 + self.deluge_protocol_version = None + # If we need the specific version of deluge 2, this is it. + daemon_version = self._receive_response(2, None, partial_data=result) + elif ord(result[:1]) == 1: + self.deluge_version = 2 + self.deluge_protocol_version = 1 + # If we need the specific version of deluge 2, this is it. + daemon_version = self._receive_response(2, 1, partial_data=result) + else: + self.deluge_version = 1 + # Deluge 1 doesn't recover well from the bad request. Re-connect the socket. + self._socket.close() + self._create_socket() + self._connect() + + def _send_call(self, deluge_version, protocol_version, method, *args, **kwargs): self.request_id += 1 - logger.debug('Calling reqid %s method %r with args:%r kwargs:%r' % (self.request_id, method, args, kwargs)) - + if method == 'daemon.login': + debug_args = list(args) + if len(debug_args) >= 2: + debug_args[1] = '