diff --git a/gui/slick/interfaces/default/config_providers.tmpl b/gui/slick/interfaces/default/config_providers.tmpl index 88aa3e084d452733b35f9da3223c7cc4f23abdd5..72b5b27b0d4f9e7e11562fd97230ec91a8c5e013 100644 --- a/gui/slick/interfaces/default/config_providers.tmpl +++ b/gui/slick/interfaces/default/config_providers.tmpl @@ -420,6 +420,45 @@ var show_nzb_providers = #if $sickbeard.USE_NZBS then "true" else "false"#; </div> #end if + #if $hasattr($curTorrentProvider, 'confirmed'): + <div class="field-pair"> + <label for="${curTorrentProvider.getID()}_confirmed"> + <span class="component-title">Confirmed download</span> + <span class="component-desc"> + <input type="checkbox" name="${curTorrentProvider.getID()}_confirmed" id="${curTorrentProvider.getID()}_confirmed" #if $curTorrentProvider.confirmed then "checked=\"checked\"" else ""#/> + <p>only download torrents from trusted or verified uploaders ?</p> + </span> + </label> + </div> + #end if + + #if $hasattr($curTorrentProvider, 'ranked'): + <div class="field-pair"> + <label for="${curTorrentProvider.getID()}_ranked"> + <span class="component-title">Ranked torrents</span> + <span class="component-desc"> + <input type="checkbox" name="${curTorrentProvider.getID()}_ranked" id="${curTorrentProvider.getID()}_ranked" #if $curTorrentProvider.ranked then "checked=\"checked\"" else ""#/> + <p>only download ranked torrents (internal releases)</p> + </span> + </label> + </div> + #end if + + #if $hasattr($curTorrentProvider, 'sorting'): + <div class="field-pair"> + <label for="${curTorrentProvider.getID()}_sorting"> + <span class="component-title">Sorting results by</span> + <span class="component-desc"> + <select name="${curTorrentProvider.getID()}_sorting" id="${curTorrentProvider.getID()}_sorting" class="form-control input-sm"> +#for $curAction in ('last', 'seeders', 'leechers'): + <option value="$curAction" #if $curAction == $curTorrentProvider.sorting then ' selected="selected"' else ''#>$curAction</option> +#end for + </select> + </span> + </label> + </div> + #end if + #if $hasattr($curTorrentProvider, 'proxy'): <div class="field-pair"> <label for="${curTorrentProvider.getID()}_proxy"> @@ -447,18 +486,6 @@ var show_nzb_providers = #if $sickbeard.USE_NZBS then "true" else "false"#; #end if #end if - #if $hasattr($curTorrentProvider, 'confirmed'): - <div class="field-pair"> - <label for="${curTorrentProvider.getID()}_confirmed"> - <span class="component-title">Confirmed download</span> - <span class="component-desc"> - <input type="checkbox" name="${curTorrentProvider.getID()}_confirmed" id="${curTorrentProvider.getID()}_confirmed" #if $curTorrentProvider.confirmed then "checked=\"checked\"" else ""#/> - <p>only download torrents from trusted or verified uploaders ?</p> - </span> - </label> - </div> - #end if - #if $hasattr($curTorrentProvider, 'freeleech'): <div class="field-pair"> <label for="${curTorrentProvider.getID()}_freeleech"> diff --git a/gui/slick/interfaces/default/editShow.tmpl b/gui/slick/interfaces/default/editShow.tmpl index 6925945ce7adb14cfd49e37248e815c728f61610..678dbaef803b3733ff1b5909733f2f6a42db0d8b 100644 --- a/gui/slick/interfaces/default/editShow.tmpl +++ b/gui/slick/interfaces/default/editShow.tmpl @@ -36,14 +36,10 @@ <b>Scene Exception:</b><br /> <input type="text" id="SceneName" class="form-control form-control-inline input-sm input200"> <input class="btn btn-inline" type="button" value="Add" id="addSceneName"><br /> - -<div id="SceneException" > - <div> - <p>This will <b>affect the episode show search</b> on nzb and torrent provider.<br /> +This will <b>affect the episode show search</b> on nzb and torrent provider.<br /> This list overrides the original name, it doesn't append to it.<br /> - </p> - </div> +<div id="SceneException" > <div class="pull-left" style="text-align:center;"> <h4>Exceptions List</h4> <select id="exceptions_list" name="exceptions_list" multiple="multiple" style="min-width:10em;" > @@ -51,11 +47,11 @@ <option value="$cur_exception">$cur_exception</option> #end for </select> - <div> - <input id="removeSceneName" value="Remove" class="btn float-left" type="button" style="margin-top: 10px;"/> + <div> + <input id="removeSceneName" value="Remove" class="btn float-left" type="button" style="margin-top: 10px;"/> + </div> + <br /> </div> -<br /> -</div> </div> <div class="clearfix"></div> <br /> diff --git a/sickbeard/__init__.py b/sickbeard/__init__.py index 6f37d89a2b2522c24358e092e8d1270570be96b3..36b84eddbb66ba84d4aa27f3abad10608b5fd1d0 100755 --- a/sickbeard/__init__.py +++ b/sickbeard/__init__.py @@ -1191,6 +1191,12 @@ def initialize(consoleLogging=True): if hasattr(curTorrentProvider, 'confirmed'): curTorrentProvider.confirmed = bool(check_setting_int(CFG, curTorrentProvider.getID().upper(), curTorrentProvider.getID() + '_confirmed', 0)) + if hasattr(curTorrentProvider, 'ranked'): + curTorrentProvider.ranked = bool(check_setting_int(CFG, curTorrentProvider.getID().upper(), + curTorrentProvider.getID() + '_ranked', 0)) + if hasattr(curTorrentProvider, 'sorting'): + curTorrentProvider.sorting = check_setting_str(CFG, curTorrentProvider.getID().upper(), + curTorrentProvider.getID() + '_sorting','seeders') if hasattr(curTorrentProvider, 'options'): curTorrentProvider.options = check_setting_str(CFG, curTorrentProvider.getID().upper(), curTorrentProvider.getID() + '_options', '') @@ -1759,6 +1765,11 @@ def save_config(): if hasattr(curTorrentProvider, 'confirmed'): new_config[curTorrentProvider.getID().upper()][curTorrentProvider.getID() + '_confirmed'] = int( curTorrentProvider.confirmed) + if hasattr(curTorrentProvider, 'ranked'): + new_config[curTorrentProvider.getID().upper()][curTorrentProvider.getID() + '_ranked'] = int( + curTorrentProvider.ranked) + if hasattr(curTorrentProvider, 'sorting'): + new_config[curTorrentProvider.getID().upper()][curTorrentProvider.getID() + '_sorting'] = curTorrentProvider.sorting if hasattr(curTorrentProvider, 'ratio'): new_config[curTorrentProvider.getID().upper()][ curTorrentProvider.getID() + '_ratio'] = curTorrentProvider.ratio diff --git a/sickbeard/helpers.py b/sickbeard/helpers.py index ef313702db25493dacca2fea805e621f32a32c88..81c5473ff15e409b157bd08a0dda82a0bdbf69da 100644 --- a/sickbeard/helpers.py +++ b/sickbeard/helpers.py @@ -1519,7 +1519,7 @@ def verify_freespace(src, dest, oldfile=None): if not ek.ek(os.path.isfile, src): logger.log("A path to a file is required for the source. " + src + " is not a file.", logger.WARNING) - return False + return True try: diskfree = disk_usage(dest) @@ -1555,3 +1555,36 @@ def pretty_time_delta(seconds): return '%s%02dm%02ds' % (sign_string, minutes, seconds) else: return '%s%02ds' % (sign_string, seconds) + +def isFileLocked(file, writeLockCheck=False): + ''' + Checks to see if a file is locked. Performs three checks + 1. Checks if the file even exists + 2. Attempts to open the file for reading. This will determine if the file has a write lock. + Write locks occur when the file is being edited or copied to, e.g. a file copy destination + 3. If the readLockCheck parameter is True, attempts to rename the file. If this fails the + file is open by some other process for reading. The file can be read, but not written to + or deleted. + @param file: the file being checked + @param writeLockCheck: when true will check if the file is locked for writing (prevents move operations) + ''' + if(not(os.path.exists(file))): + return True + try: + f = open(file, 'r') + f.close() + except IOError: + return True + + if(writeLockCheck): + lockFile = file + ".lckchk" + if(os.path.exists(lockFile)): + os.remove(lockFile) + try: + os.rename(file, lockFile) + time.sleep(1) + os.rename(lockFile, file) + except WindowsError: + return True + + return False diff --git a/sickbeard/name_parser/parser.py b/sickbeard/name_parser/parser.py index b68756c55d4b8860fdeeb04bad6e0f9af6c2028b..e2ad166259743d8ff969ae83784cfd97e325d9cb 100644 --- a/sickbeard/name_parser/parser.py +++ b/sickbeard/name_parser/parser.py @@ -78,13 +78,13 @@ class NameParser(object): def _compile_regexes(self, regexMode): if regexMode == self.ANIME_REGEX: - logger.log(u"Using ANIME regexs", logger.DEBUG) + dbg_str = u"ANIME" uncompiled_regex = [regexes.anime_regexes, regexes.normal_regexes] elif regexMode == self.NORMAL_REGEX: - logger.log(u"Using NORMAL regexs", logger.DEBUG) + dbg_str = u"NORMAL" uncompiled_regex = [regexes.normal_regexes] else: - logger.log(u"Using ALL regexes", logger.DEBUG) + dbg_str = u"ALL" uncompiled_regex = [regexes.normal_regexes, regexes.anime_regexes] self.compiled_regexes = [] @@ -93,7 +93,7 @@ class NameParser(object): try: cur_regex = re.compile(cur_pattern, re.VERBOSE | re.IGNORECASE) except re.error, errormsg: - logger.log(u"WARNING: Invalid episode_pattern, %s. %s" % (errormsg, cur_pattern)) + logger.log(u"WARNING: Invalid episode_pattern using %s regexs, %s. %s" % (dbg_str, errormsg, cur_pattern)) else: self.compiled_regexes.append((cur_pattern_num, cur_pattern_name, cur_regex)) @@ -607,4 +607,4 @@ class InvalidNameException(Exception): class InvalidShowException(Exception): - "The given show name is not valid" \ No newline at end of file + "The given show name is not valid" diff --git a/sickbeard/postProcessor.py b/sickbeard/postProcessor.py index 83c6119b0dad905eb0cfdda171cc3bd1101e88a5..fc5df375813ebd4f1e9efcb50c5043d3d61c1ef0 100644 --- a/sickbeard/postProcessor.py +++ b/sickbeard/postProcessor.py @@ -910,9 +910,13 @@ class PostProcessor(object): logger.DEBUG) # try to find out if we have enough space to perform the copy or move action. - if not verify_freespace(self.file_path, ek.ek(os.path.dirname, ep_obj.show._location), [ep_obj] + ep_obj.relatedEps): - self._log("Not enough space to continue PP, exiting") - return False + if not helpers.isFileLocked(self.file_path, False): + if not verify_freespace(self.file_path, ek.ek(os.path.dirname, ep_obj.show._location), [ep_obj] + ep_obj.relatedEps): + self._log("Not enough space to continue PP, exiting") + return False + else: + self._log("Unable to determine needed filespace as the source file is locked for access") + # delete the existing file (and company) for cur_ep in [ep_obj] + ep_obj.relatedEps: @@ -1019,15 +1023,21 @@ class PostProcessor(object): try: # move the episode and associated files to the show dir if self.process_method == "copy": + if helpers.isFileLocked(self.file_path, False): + raise exceptions.PostProcessingFailed("File is locked for reading") self._copy(self.file_path, dest_path, new_base_name, sickbeard.MOVE_ASSOCIATED_FILES, sickbeard.USE_SUBTITLES and ep_obj.show.subtitles) elif self.process_method == "move": + if helpers.isFileLocked(self.file_path, True): + raise exceptions.PostProcessingFailed("File is locked for reading/writing") self._move(self.file_path, dest_path, new_base_name, sickbeard.MOVE_ASSOCIATED_FILES, sickbeard.USE_SUBTITLES and ep_obj.show.subtitles) elif self.process_method == "hardlink": self._hardlink(self.file_path, dest_path, new_base_name, sickbeard.MOVE_ASSOCIATED_FILES, sickbeard.USE_SUBTITLES and ep_obj.show.subtitles) elif self.process_method == "symlink": + if helpers.isFileLocked(self.file_path, True): + raise exceptions.PostProcessingFailed("File is locked for reading/writing") self._moveAndSymlink(self.file_path, dest_path, new_base_name, sickbeard.MOVE_ASSOCIATED_FILES, sickbeard.USE_SUBTITLES and ep_obj.show.subtitles) else: diff --git a/sickbeard/processTV.py b/sickbeard/processTV.py index 5fb4192903abda28e527cd623699c93cc04cb008..68c1c99779b667fec3f8234231d398187e61a177 100644 --- a/sickbeard/processTV.py +++ b/sickbeard/processTV.py @@ -444,8 +444,7 @@ def already_postprocessed(dirName, videofile, force, result): myDB = db.DBConnection() sqlResult = myDB.select("SELECT * FROM tv_episodes WHERE release_name = ?", [dirName]) if sqlResult: - result.output += logHelper(u"You're trying to post process a dir that's already been processed, skipping", - logger.DEBUG) + #result.output += logHelper(u"You're trying to post process a dir that's already been processed, skipping", logger.DEBUG) return True else: @@ -455,8 +454,7 @@ def already_postprocessed(dirName, videofile, force, result): sqlResult = myDB.select("SELECT * FROM tv_episodes WHERE release_name = ?", [videofile.rpartition('.')[0]]) if sqlResult: - result.output += logHelper(u"You're trying to post process a video that's already been processed, skipping", - logger.DEBUG) + #result.output += logHelper(u"You're trying to post process a video that's already been processed, skipping", logger.DEBUG) return True #Needed if we have downloaded the same episode @ different quality @@ -479,8 +477,7 @@ def already_postprocessed(dirName, videofile, force, result): search_sql += " and history.resource LIKE ?" sqlResult = myDB.select(search_sql, [u'%' + videofile]) if sqlResult: - result.output += logHelper(u"You're trying to post process a video that's already been processed, skipping", - logger.DEBUG) + #result.output += logHelper(u"You're trying to post process a video that's already been processed, skipping", logger.DEBUG) return True return False diff --git a/sickbeard/providers/generic.py b/sickbeard/providers/generic.py index 47f6ef084d336bc805feff58f01a9bf0e0044360..4f35c7b490ac7c636435af196ebffa6cf24ca0a9 100644 --- a/sickbeard/providers/generic.py +++ b/sickbeard/providers/generic.py @@ -146,6 +146,7 @@ class GenericProvider: if self.providerType == GenericProvider.TORRENT: try: torrent_hash = re.findall('urn:btih:([\w]{32,40})', result.url)[0].upper() + torrent_name = re.findall('dn=([^&]+)', result.url)[0] if len(torrent_hash) == 32: torrent_hash = b16encode(b32decode(torrent_hash)).upper() @@ -156,9 +157,8 @@ class GenericProvider: urls = [ 'http://torcache.net/torrent/' + torrent_hash + '.torrent', - #zoink.ch misconfigured, torrage.com domain expired. - #'http://zoink.ch/torrent/' + torrent_hash + '.torrent', - #'http://torrage.com/torrent/' + torrent_hash.lower() + '.torrent', + 'http://zoink.ch/torrent/' + torrent_name + '.torrent', + 'http://torrage.com/torrent/' + torrent_hash + '.torrent', ] except: urls = [result.url] @@ -184,6 +184,8 @@ class GenericProvider: if self._verify_download(filename): return True + else: + helpers._remove_file_failed(filename) logger.log(u"Failed to download result", logger.WARNING) return False @@ -208,7 +210,7 @@ class GenericProvider: except Exception as e: logger.log(u"Failed to validate torrent file: " + ex(e), logger.DEBUG) - logger.log(u"Result is not a valid torrent file", logger.WARNING) + logger.log(u"Result is not a valid torrent file", logger.DEBUG) return False return True diff --git a/sickbeard/providers/rarbg.py b/sickbeard/providers/rarbg.py index c56a3ccc672cdc691632c35a66fd7bb5312fc01c..7c2814905b09a0f03c9340f4f6baaf6fa35a296f 100644 --- a/sickbeard/providers/rarbg.py +++ b/sickbeard/providers/rarbg.py @@ -58,6 +58,8 @@ class RarbgProvider(generic.TorrentProvider): self.supportsBacklog = True self.ratio = None self.minseed = None + self.ranked = None + self.sorting = None self.minleech = None self.token = None self.tokenExpireDate = None @@ -84,10 +86,8 @@ class RarbgProvider(generic.TorrentProvider): } self.defaultOptions = self.urlOptions['categories'].format(categories='18;41') + \ - self.urlOptions['sorting'].format(sorting='last') + \ self.urlOptions['limit'].format(limit='100') + \ - self.urlOptions['format'].format(format='json') + \ - self.urlOptions['ranked'].format(ranked='1') + self.urlOptions['format'].format(format='json') self.next_request = datetime.datetime.now() @@ -226,6 +226,12 @@ class RarbgProvider(generic.TorrentProvider): if self.minseed: searchURL += self.urlOptions['seeders'].format(min_seeders=int(self.minseed)) + + if self.sorting: + searchURL += self.urlOptions['sorting'].format(sorting=self.sorting) + + if self.ranked: + searchURL += self.urlOptions['ranked'].format(ranked=int(self.ranked)) logger.log(u'{name} search page URL: {url}'.format(name=self.name, url=searchURL), logger.DEBUG) @@ -274,6 +280,11 @@ class RarbgProvider(generic.TorrentProvider): return results logger.log(u'{name} Using new token'.format(name=self.name), logger.DEBUG) continue + if re.search('<div id="error">.*</div>', data): + logger.log(u'{name} {proxy} does not support https.'.format(name=self.name, proxy=self.proxy.getProxyURL()), logger.DEBUG) + searchURL = searchURL.replace(u'https', 'http') + continue + #No error found break break else: @@ -283,7 +294,11 @@ class RarbgProvider(generic.TorrentProvider): continue try: - data_json = json.loads(data) + data = re.search('\[\{\"f\".*\}\]', data) + if data is not None: + data_json = json.loads(data.group()) + else: + data_json = {} except Exception as e: logger.log(u'{name} json load failed: {traceback_info}'.format(name=self.name, traceback_info=traceback.format_exc()), logger.DEBUG) logger.log(u'{name} json load failed. Data dump = {data}'.format(name=self.name, data=data), logger.DEBUG) diff --git a/sickbeard/providers/thepiratebay.py b/sickbeard/providers/thepiratebay.py index 2a395bbab035892b1771cf4f2f779e38ec0faaf9..08cbe2fbebaeef57f7854045e6664765b8767913 100644 --- a/sickbeard/providers/thepiratebay.py +++ b/sickbeard/providers/thepiratebay.py @@ -59,7 +59,7 @@ class ThePirateBayProvider(generic.TorrentProvider): self.cache = ThePirateBayCache(self) - self.urls = {'base_url': 'https://thepiratebay.se/'} + self.urls = {'base_url': 'https://thepiratebay.gd/'} self.url = self.urls['base_url'] diff --git a/sickbeard/webserve.py b/sickbeard/webserve.py index 817fecc29f5cbafcefe6481a271c65441d47cf63..0a66efbb090f07fab0515421f6ababa1778ff8d9 100644 --- a/sickbeard/webserve.py +++ b/sickbeard/webserve.py @@ -4478,6 +4478,19 @@ class ConfigProviders(Config): except: curTorrentProvider.confirmed = 0 + if hasattr(curTorrentProvider, 'ranked'): + try: + curTorrentProvider.ranked = config.checkbox_to_value( + kwargs[curTorrentProvider.getID() + '_ranked']) + except: + curTorrentProvider.ranked = 0 + + if hasattr(curTorrentProvider, 'sorting'): + try: + curTorrentProvider.sorting = str(kwargs[curTorrentProvider.getID() + '_sorting']).strip() + except: + curTorrentProvider.sorting = 'seeders' + if hasattr(curTorrentProvider, 'proxy'): try: curTorrentProvider.proxy.enabled = config.checkbox_to_value(