diff --git a/gui/slick/images/providers/hd4free.png b/gui/slick/images/providers/hd4free.png new file mode 100644 index 0000000000000000000000000000000000000000..6cf5435727f8fd916e5e68a54a53b6bd650f9954 Binary files /dev/null and b/gui/slick/images/providers/hd4free.png differ diff --git a/gui/slick/js/parsers.js b/gui/slick/js/parsers.js index b7be826ea345a3ba6d6aa7340bdc2e3773a68d2b..1635746cbe2ddfa31128466ef1871bb87ff55c31 100644 --- a/gui/slick/js/parsers.js +++ b/gui/slick/js/parsers.js @@ -18,7 +18,21 @@ $.tablesorter.addParser({ return false; }, format: function(s) { - return s.replace('hd1080p', 5).replace('hd720p', 4).replace('hd', 3).replace('sd', 2).replace('any', 1).replace('best', 0).replace('custom', 7); + var replacements = { + 'custom': 11, + 'bluray': 10, // Custom: Only bluray + 'hd1080p': 9, + '1080p': 8, // Custom: Only 1080p + 'hdtv': 7, // Custom: 1080p and 720p (only HDTV) + 'web-dl': 6, // Custom: 1080p and 720p (only WEB-DL) + 'hd720p': 5, + '720p': 4, // Custom: Only 720p + 'hd': 3, + 'sd': 2, + 'any': 1, + 'best': 0 + }; + return replacements[s.toLowerCase()]; }, type: 'numeric' }); diff --git a/gui/slick/js/restart.js b/gui/slick/js/restart.js index 9c7ec6f0703d5fee800dec7855e7dd0408cb463a..1dc8af1412ed805a29100e2a3f4eba6539b828ab 100644 --- a/gui/slick/js/restart.js +++ b/gui/slick/js/restart.js @@ -5,7 +5,7 @@ $(document).ready(function() { var isAliveUrl = srRoot + '/home/is_alive/'; - var checkIsAlive = setInterval(isAlive, 1000); + var checkIsAlive = setInterval(isAlive, 100); function isAlive() { // jshint ignore:line // Setup error detection diff --git a/gui/slick/views/config_subtitles.mako b/gui/slick/views/config_subtitles.mako index 720b10cc23e5274acc53a981bf0a75dfaa3651bc..d1cee5a2085150a8e5cef6f898ccb450f176f00d 100644 --- a/gui/slick/views/config_subtitles.mako +++ b/gui/slick/views/config_subtitles.mako @@ -83,6 +83,16 @@ $('#subtitles_dir').fileBrowser({ title: 'Select Subtitles Download Directory' } <span class="component-desc">time in hours between scans (default: 1)</span> </label> </div> + <div class="field-pair"> + <label for="subtitles_perfect_match" class="clearfix"> + <span class="component-title">Perfect matches</span> + <span class="component-desc"> + <input type="checkbox" class="enabler" ${('', ' checked="checked"')[bool(sickbeard.SUBTITLES_PERFECT_MATCH)]} id="subtitles_perfect_match" name="subtitles_perfect_match"> + <p>Only download subtitles that match: release group, video codec, audio codec and resolution</p> + <p>If disabled you may get out of sync subtitles</p> + </span> + </label> + </div> <div class="field-pair"> <label class="clearfix" for="subtitles_history"> <span class="component-title">Subtitles History</span> diff --git a/gui/slick/views/displayShow.mako b/gui/slick/views/displayShow.mako index 9b7515d7ff7681da6e9bb316a590fcb9c70c674d..c071de73206444bf3871d404defdf3a90232274a 100644 --- a/gui/slick/views/displayShow.mako +++ b/gui/slick/views/displayShow.mako @@ -126,9 +126,12 @@ <img src="${srRoot}/images/blank.png" class="country-flag flag-${country}" width="16" height="11" style="margin-left: 3px; vertical-align:middle;" /> % endfor % endif - % if 'year' in show.imdb_info: - <span>(${show.imdb_info['year']}) - ${show.imdb_info['runtimes']} minutes - </span> + <span> + % if 'year' in show.imdb_info and show.imdb_info['year']: + (${show.imdb_info['year']}) - % endif + ${show.imdb_info['runtimes']} minutes</span> + <a href="${anon_url('http://www.imdb.com/title/', _show.imdbid)}" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;" title="http://www.imdb.com/title/${show.imdbid}"><img alt="[imdb]" height="16" width="16" src="${srRoot}/images/imdb.png" style="margin-top: -1px; vertical-align:middle;"/></a> % endif <a href="${anon_url(sickbeard.indexerApi(_show.indexer).config['show_url'], _show.indexerid)}" onclick="window.open(this.href, '_blank'); return false;" title="${sickbeard.indexerApi(show.indexer).config["show_url"] + str(show.indexerid)}"><img alt="${sickbeard.indexerApi(show.indexer).name}" height="16" width="16" src="${srRoot}/images/${sickbeard.indexerApi(show.indexer).config["icon"]}" style="margin-top: -1px; vertical-align:middle;"/></a> @@ -139,14 +142,13 @@ <div id="tags"> <ul class="tags"> - % if not show.imdbid and show.genre: + % if ('genres' not in show.imdb_info or not show.imdb_info['genres']) and show.genre: % for genre in show.genre[1:-1].split('|'): <a href="${anon_url('http://trakt.tv/shows/popular/?genres=', genre.lower())}" target="_blank" title="View other popular ${genre} shows on trakt.tv."><li>${genre}</li></a> % endfor - % endif - % if 'year' in show.imdb_info: + % elif 'genres' in show.imdb_info and show.imdb_info['genres']: % for imdbgenre in show.imdb_info['genres'].replace('Sci-Fi','Science-Fiction').split('|'): - <a href="${anon_url('http://trakt.tv/shows/popular/?genres=', imdbgenre.lower())}" target="_blank" title="View other popular ${imdbgenre} shows on trakt.tv."><li>${imdbgenre}</li></a> + <a href="${anon_url('http://www.imdb.com/search/title?count=100&title_type=tv_series&genres=', imdbgenre.lower())}" target="_blank" title="View other popular ${imdbgenre} shows on IMDB."><li>${imdbgenre}</li></a> % endfor % endif </ul> @@ -519,10 +521,8 @@ <a class="epSearch" id="${str(show.indexerid)}x${str(epResult["season"])}x${str(epResult["episode"])}" name="${str(show.indexerid)}x${str(epResult["season"])}x${str(epResult["episode"])}" href="searchEpisode?show=${show.indexerid}&season=${epResult["season"]}&episode=${epResult["episode"]}"><img src="${srRoot}/images/search16.png" width="16" height="16" alt="search" title="Manual Search" /></a> % endif % endif - % if sickbeard.USE_SUBTITLES and show.subtitles and epResult["location"]: - % if (sickbeard.SUBTITLES_MULTI and subtitles.needs_subtitles(epResult["subtitles"])) or (not sickbeard.SUBTITLES_MULTI and len(subtitles.wanted_languages()) > 0 and "und" not in epResult["subtitles"] and list(subtitles.wanted_languages())[0] not in epResult["subtitles"]): - <a class="epSubtitlesSearch" href="searchEpisodeSubtitles?show=${show.indexerid}&season=${epResult["season"]}&episode=${epResult["episode"]}"><img src="${srRoot}/images/closed_captioning.png" height="16" alt="search subtitles" title="Search Subtitles" /></a> - % endif + % if sickbeard.USE_SUBTITLES and show.subtitles and epResult["location"] and subtitles.needs_subtitles(epResult['subtitles']): + <a class="epSubtitlesSearch" href="searchEpisodeSubtitles?show=${show.indexerid}&season=${epResult["season"]}&episode=${epResult["episode"]}"><img src="${srRoot}/images/closed_captioning.png" height="16" alt="search subtitles" title="Search Subtitles" /></a> % endif </td> </tr> diff --git a/gui/slick/views/inc_defs.mako b/gui/slick/views/inc_defs.mako index f8ff29646e906f4cc13b9d163d4f77cec5711fe6..94200437394fe008ff15336a0870be664dfa9128 100644 --- a/gui/slick/views/inc_defs.mako +++ b/gui/slick/views/inc_defs.mako @@ -5,16 +5,16 @@ <%def name="renderQualityPill(quality, showTitle=False, overrideClass=None)"><% # Build a string of quality names to use as title attribute if showTitle: - iQuality, pQuality = Quality.splitQuality(quality) + iquality, pquality = Quality.splitQuality(quality) title = 'Initial Quality:\n' - if iQuality: - for curQual in iQuality: + if iquality: + for curQual in iquality: title += " " + Quality.qualityStrings[curQual] + "\n" else: title += " None\n" title += "\nPreferred Quality:\n" - if pQuality: - for curQual in pQuality: + if pquality: + for curQual in pquality: title += " " + Quality.qualityStrings[curQual] + "\n" else: title += " None\n" @@ -22,12 +22,17 @@ else: title = "" - iQuality = quality & 0xFFFF - pQuality = quality >> 16 + sum_iquality = quality & 0xFFFF + sum_pquality = quality >> 16 + set_hdtv = set([Quality.HDTV, Quality.RAWHDTV, Quality.FULLHDTV]) + set_webdl = set([Quality.HDWEBDL, Quality.FULLHDWEBDL]) + set_bluray = set([Quality.HDBLURAY, Quality.FULLHDBLURAY]) + set_1080p = set([Quality.FULLHDTV, Quality.FULLHDWEBDL, Quality.FULLHDBLURAY]) + set_720p = set([Quality.HDTV, Quality.RAWHDTV, Quality.HDWEBDL, Quality.HDBLURAY]) # If initial and preferred qualities are the same, show pill as initial quality - if iQuality == pQuality: - quality = iQuality + if sum_iquality == sum_pquality: + quality = sum_iquality if quality in qualityPresets: cssClass = qualityPresetStrings[quality] @@ -38,6 +43,26 @@ elif quality in Quality.qualityStrings: cssClass = Quality.cssClassStrings[quality] qualityString = Quality.qualityStrings[quality] + # Check if all sources are HDTV + elif set(iquality).issubset(set_hdtv)and set(pquality).issubset(set_hdtv): + cssClass = Quality.cssClassStrings[Quality.ANYHDTV] + qualityString = 'HDTV' + # Check if all sources are WEB-DL + elif set(iquality).issubset(set_webdl)and set(pquality).issubset(set_webdl): + cssClass = Quality.cssClassStrings[Quality.ANYWEBDL] + qualityString = 'WEB-DL' + # Check if all sources are BLURAY + elif set(iquality).issubset(set_bluray)and set(pquality).issubset(set_bluray): + cssClass = Quality.cssClassStrings[Quality.ANYBLURAY] + qualityString = 'BLURAY' + # Check if all resolutions are 1080p + elif set(iquality).issubset(set_1080p)and set(pquality).issubset(set_1080p): + cssClass = Quality.cssClassStrings[Quality.FULLHDBLURAY] + qualityString = '1080p' + # Check if all resolutions are 720p + elif set(iquality).issubset(set_720p)and set(pquality).issubset(set_720p): + cssClass = Quality.cssClassStrings[Quality.HDBLURAY] + qualityString = '720p' else: cssClass = "Custom" qualityString = "Custom" diff --git a/sickbeard/__init__.py b/sickbeard/__init__.py index 8cd96b162a23a70ae3ea6a43ee265096bdce6767..75508c6998827408151750b16811abc869ae1849 100644 --- a/sickbeard/__init__.py +++ b/sickbeard/__init__.py @@ -519,6 +519,7 @@ SUBTITLES_DIR = '' SUBTITLES_SERVICES_LIST = [] SUBTITLES_SERVICES_ENABLED = [] SUBTITLES_HISTORY = False +SUBTITLES_PERFECT_MATCH = False EMBEDDED_SUBTITLES_ALL = False SUBTITLES_HEARING_IMPAIRED = False SUBTITLES_FINDER_FREQUENCY = 1 @@ -612,7 +613,7 @@ def initialize(consoleLogging=True): GUI_NAME, HOME_LAYOUT, HISTORY_LAYOUT, DISPLAY_SHOW_SPECIALS, COMING_EPS_LAYOUT, COMING_EPS_SORT, COMING_EPS_DISPLAY_PAUSED, COMING_EPS_MISSED_RANGE, FUZZY_DATING, TRIM_ZERO, DATE_PRESET, TIME_PRESET, TIME_PRESET_W_SECONDS, THEME_NAME, \ POSTER_SORTBY, POSTER_SORTDIR, HISTORY_LIMIT, CREATE_MISSING_SHOW_DIRS, ADD_SHOWS_WO_DIR, \ METADATA_WDTV, METADATA_TIVO, METADATA_MEDE8ER, IGNORE_WORDS, TRACKERS_LIST, IGNORED_SUBS_LIST, REQUIRE_WORDS, CALENDAR_UNPROTECTED, CALENDAR_ICONS, NO_RESTART, \ - USE_SUBTITLES, SUBTITLES_LANGUAGES, SUBTITLES_DIR, SUBTITLES_SERVICES_LIST, SUBTITLES_SERVICES_ENABLED, SUBTITLES_HISTORY, SUBTITLES_FINDER_FREQUENCY, SUBTITLES_MULTI, SUBTITLES_DOWNLOAD_IN_PP, EMBEDDED_SUBTITLES_ALL, SUBTITLES_EXTRA_SCRIPTS, subtitlesFinderScheduler, \ + USE_SUBTITLES, SUBTITLES_LANGUAGES, SUBTITLES_DIR, SUBTITLES_SERVICES_LIST, SUBTITLES_SERVICES_ENABLED, SUBTITLES_HISTORY, SUBTITLES_FINDER_FREQUENCY, SUBTITLES_MULTI, SUBTITLES_DOWNLOAD_IN_PP, EMBEDDED_SUBTITLES_ALL, SUBTITLES_EXTRA_SCRIPTS, SUBTITLES_PERFECT_MATCH, subtitlesFinderScheduler, \ SUBTITLES_HEARING_IMPAIRED, ADDIC7ED_USER, ADDIC7ED_PASS, LEGENDASTV_USER, LEGENDASTV_PASS, OPENSUBTITLES_USER, OPENSUBTITLES_PASS, \ USE_FAILED_DOWNLOADS, DELETE_FAILED, ANON_REDIRECT, LOCALHOST_IP, DEBUG, DEFAULT_PAGE, PROXY_SETTING, PROXY_INDEXERS, \ AUTOPOSTPROCESSER_FREQUENCY, SHOWUPDATE_HOUR, \ @@ -1148,6 +1149,7 @@ def initialize(consoleLogging=True): if x] SUBTITLES_DEFAULT = bool(check_setting_int(CFG, 'Subtitles', 'subtitles_default', 0)) SUBTITLES_HISTORY = bool(check_setting_int(CFG, 'Subtitles', 'subtitles_history', 0)) + SUBTITLES_PERFECT_MATCH = bool(check_setting_int(CFG, 'Subtitles', 'subtitles_perfect_match', 1)) EMBEDDED_SUBTITLES_ALL = bool(check_setting_int(CFG, 'Subtitles', 'embedded_subtitles_all', 0)) SUBTITLES_HEARING_IMPAIRED = bool(check_setting_int(CFG, 'Subtitles', 'subtitles_hearing_impaired', 0)) SUBTITLES_FINDER_FREQUENCY = check_setting_int(CFG, 'Subtitles', 'subtitles_finder_frequency', 1) @@ -1534,82 +1536,30 @@ def halt(): logger.log(u"Aborting all threads") - events.stop.set() - logger.log(u"Waiting for the EVENTS thread to exit") - try: - events.join(10) - except Exception: - pass - - dailySearchScheduler.stop.set() - logger.log(u"Waiting for the DAILYSEARCH thread to exit") - try: - dailySearchScheduler.join(10) - except Exception: - pass - - backlogSearchScheduler.stop.set() - logger.log(u"Waiting for the BACKLOG thread to exit") - try: - backlogSearchScheduler.join(10) - except Exception: - pass - - showUpdateScheduler.stop.set() - logger.log(u"Waiting for the SHOWUPDATER thread to exit") - try: - showUpdateScheduler.join(10) - except Exception: - pass - - versionCheckScheduler.stop.set() - logger.log(u"Waiting for the VERSIONCHECKER thread to exit") - try: - versionCheckScheduler.join(10) - except Exception: - pass - - showQueueScheduler.stop.set() - logger.log(u"Waiting for the SHOWQUEUE thread to exit") - try: - showQueueScheduler.join(10) - except Exception: - pass - - searchQueueScheduler.stop.set() - logger.log(u"Waiting for the SEARCHQUEUE thread to exit") - try: - searchQueueScheduler.join(10) - except Exception: - pass - - autoPostProcesserScheduler.stop.set() - logger.log(u"Waiting for the POSTPROCESSER thread to exit") - try: - autoPostProcesserScheduler.join(10) - except Exception: - pass - - traktCheckerScheduler.stop.set() - logger.log(u"Waiting for the TRAKTCHECKER thread to exit") - try: - traktCheckerScheduler.join(10) - except Exception: - pass - - properFinderScheduler.stop.set() - logger.log(u"Waiting for the PROPERFINDER thread to exit") - try: - properFinderScheduler.join(10) - except Exception: - pass - - subtitlesFinderScheduler.stop.set() - logger.log(u"Waiting for the SUBTITLESFINDER thread to exit") - try: - subtitlesFinderScheduler.join(10) - except Exception: - pass + threads = [ + dailySearchScheduler, + backlogSearchScheduler, + showUpdateScheduler, + versionCheckScheduler, + showQueueScheduler, + searchQueueScheduler, + autoPostProcesserScheduler, + traktCheckerScheduler, + properFinderScheduler, + subtitlesFinderScheduler, + events + ] + + # set them all to stop at the same time + for t in threads: + t.stop.set() + + for t in threads: + logger.log(u"Waiting for the %s thread to exit" % t.name) + try: + t.join(10) + except Exception: + pass if ADBA_CONNECTION: ADBA_CONNECTION.logout() @@ -1624,6 +1574,7 @@ def halt(): def sig_handler(signum=None, frame=None): + _ = frame if not isinstance(signum, type(None)): logger.log(u"Signal %i caught, saving and exiting..." % int(signum)) Shutdown.stop(PID) @@ -1640,16 +1591,6 @@ def saveAll(): save_config() -def restart(soft=True): - if soft: - halt() - saveAll() - logger.log(u"Re-initializing all data") - initialize() - else: - events.put(events.SystemEvent.RESTART) - - def save_config(): new_config = ConfigObj() new_config.filename = CONFIG_FILE @@ -2148,6 +2089,7 @@ def save_config(): new_config['Subtitles']['subtitles_dir'] = SUBTITLES_DIR new_config['Subtitles']['subtitles_default'] = int(SUBTITLES_DEFAULT) new_config['Subtitles']['subtitles_history'] = int(SUBTITLES_HISTORY) + new_config['Subtitles']['subtitles_perfect_match'] = int(SUBTITLES_PERFECT_MATCH) new_config['Subtitles']['embedded_subtitles_all'] = int(EMBEDDED_SUBTITLES_ALL) new_config['Subtitles']['subtitles_hearing_impaired'] = int(SUBTITLES_HEARING_IMPAIRED) new_config['Subtitles']['subtitles_finder_frequency'] = int(SUBTITLES_FINDER_FREQUENCY) diff --git a/sickbeard/helpers.py b/sickbeard/helpers.py index e1e191302e6455060090e11b006be55d29df5fa2..d5dd70ae0c6b10901f3046cf721de0636c20c654 100644 --- a/sickbeard/helpers.py +++ b/sickbeard/helpers.py @@ -1285,7 +1285,7 @@ def mapIndexersToShow(showObj): if len(nlist) >= 4: logger.log(u"Found indexer mapping in cache for show: " + showObj.name, logger.DEBUG) mapped[int(curResult['mindexer'])] = int(curResult['mindexer_id']) - return mapped + break else: sql_l = [] for indexer in sickbeard.indexerApi().indexers: diff --git a/sickbeard/providers/__init__.py b/sickbeard/providers/__init__.py index 1b447b6d896116b49e3c7102071382662b077e71..3923308c2fcc82f84895f726ec921227d28d906d 100644 --- a/sickbeard/providers/__init__.py +++ b/sickbeard/providers/__init__.py @@ -23,10 +23,10 @@ from random import shuffle import sickbeard from sickbeard import logger -from sickbeard.providers import btn, newznab, womble, thepiratebay, torrentleech, kat, iptorrents, torrentz, \ +from sickbeard.providers import btn, newznab, rsstorrent, womble, thepiratebay, torrentleech, kat, iptorrents, torrentz, \ omgwtfnzbs, scc, hdtorrents, torrentday, hdbits, hounddawgs, speedcd, nyaatorrents, animenzb, bluetigers, cpasbien, fnt, xthor, torrentbytes, \ freshontv, titansoftv, morethantv, bitsoup, t411, tokyotoshokan, shazbat, rarbg, alpharatio, tntvillage, binsearch, torrentproject, extratorrent, \ - scenetime, btdigg, strike, transmitthenet, tvchaosuk, bitcannon, pretome, gftracker, hdspace, newpct, elitetorrent, bitsnoop, danishbits + scenetime, btdigg, strike, transmitthenet, tvchaosuk, bitcannon, pretome, gftracker, hdspace, newpct, elitetorrent, bitsnoop, danishbits, hd4free __all__ = [ 'womble', 'btn', 'thepiratebay', 'kat', 'torrentleech', 'scc', 'hdtorrents', @@ -36,7 +36,7 @@ __all__ = [ 'shazbat', 'rarbg', 'tntvillage', 'binsearch', 'bluetigers', 'cpasbien', 'fnt', 'xthor', 'scenetime', 'btdigg', 'strike', 'transmitthenet', 'tvchaosuk', 'torrentproject', 'extratorrent', 'bitcannon', 'torrentz', 'pretome', 'gftracker', - 'hdspace', 'newpct', 'elitetorrent', 'bitsnoop', 'danishbits' + 'hdspace', 'newpct', 'elitetorrent', 'bitsnoop', 'danishbits', 'hd4free' ] @@ -73,7 +73,7 @@ def makeProviderList(): def getNewznabProviderList(data): defaultList = [makeNewznabProvider(x) for x in getDefaultNewznabProviders().split('!!!')] - providerList = filter(lambda x: x, [makeNewznabProvider(x) for x in data.split('!!!')]) + providerList = [x for x in [makeNewznabProvider(x) for x in data.split('!!!')] if x] seen_values = set() providerListDeduped = [] @@ -103,7 +103,7 @@ def getNewznabProviderList(data): providerDict[curDefault.name].enable_daily = curDefault.enable_daily providerDict[curDefault.name].enable_backlog = curDefault.enable_backlog - return filter(lambda x: x, providerList) + return [x for x in providerList if x] def makeNewznabProvider(configString): @@ -129,7 +129,7 @@ def makeNewznabProvider(configString): logger.log(u"Skipping Newznab provider string: '" + configString + "', incorrect format", logger.ERROR) return None - newznab = sys.modules['sickbeard.providers.newznab'] + # newznab = sys.modules['sickbeard.providers.newznab'] newProvider = newznab.NewznabProvider(name, url, key=key, catIDs=catIDs, search_mode=search_mode, search_fallback=search_fallback, enable_daily=enable_daily, @@ -140,7 +140,7 @@ def makeNewznabProvider(configString): def getTorrentRssProviderList(data): - providerList = filter(lambda x: x, [makeTorrentRssProvider(x) for x in data.split('!!!')]) + providerList = [x for x in [makeTorrentRssProvider(x) for x in data.split('!!!')] if x] seen_values = set() providerListDeduped = [] @@ -150,7 +150,7 @@ def getTorrentRssProviderList(data): providerListDeduped.append(d) seen_values.add(value) - return filter(lambda x: x, providerList) + return [x for x in providerList if x] def makeTorrentRssProvider(configString): @@ -179,12 +179,12 @@ def makeTorrentRssProvider(configString): logger.ERROR) return None - try: - torrentRss = sys.modules['sickbeard.providers.rsstorrent'] - except Exception: - return + # try: + # torrentRss = sys.modules['sickbeard.providers.rsstorrent'] + # except Exception: + # return - newProvider = torrentRss.TorrentRssProvider(name, url, cookies, titleTAG, search_mode, search_fallback, enable_daily, + newProvider = rsstorrent.TorrentRssProvider(name, url, cookies, titleTAG, search_mode, search_fallback, enable_daily, enable_backlog) newProvider.enabled = enabled == '1' diff --git a/sickbeard/providers/animenzb.py b/sickbeard/providers/animenzb.py index 57d23009692293c9dd8d8eb504dad521f648180c..93460b5185da94fddbf51cbd56c4a49752159db9 100644 --- a/sickbeard/providers/animenzb.py +++ b/sickbeard/providers/animenzb.py @@ -42,7 +42,7 @@ class animenzb(NZBProvider): self.supports_absolute_numbering = True self.anime_only = True - self.urls = {'base_url': 'http://animenzb.com//'} + self.urls = {'base_url': 'http://animenzb.com/'} self.url = self.urls['base_url'] self.cache = animenzbCache(self) diff --git a/sickbeard/providers/bitsoup.py b/sickbeard/providers/bitsoup.py index f8585c739dbd3d1904525f60756e23fe771bc0ab..5060f1504121bd742688825bb808a0a3112b38ed 100644 --- a/sickbeard/providers/bitsoup.py +++ b/sickbeard/providers/bitsoup.py @@ -115,8 +115,8 @@ class BitSoupProvider(TorrentProvider): try: title = link.getText() - seeders = int(cells[10].getText()) - leechers = int(cells[11].getText()) + seeders = int(cells[10].getText().replace(',', '')) + leechers = int(cells[11].getText().replace(',', '')) # FIXME size = -1 except (AttributeError, TypeError): @@ -131,6 +131,9 @@ class BitSoupProvider(TorrentProvider): logger.log(u"Discarding torrent because it doesn't meet the minimum seeders or leechers: {0} (S:{1} L:{2})".format(title, seeders, leechers), logger.DEBUG) continue + if seeders >= 32768 or leechers >= 32768: + continue + item = title, download_url, size, seeders, leechers if mode != 'RSS': logger.log(u"Found result: %s " % title, logger.DEBUG) diff --git a/sickbeard/providers/danishbits.py b/sickbeard/providers/danishbits.py index 51d466014a22fe575a7673f6224382e282e7e9ca..4f27fc0961fc90eeb14c8ef77cc27abe4d159cb0 100644 --- a/sickbeard/providers/danishbits.py +++ b/sickbeard/providers/danishbits.py @@ -58,7 +58,7 @@ class DanishbitsProvider(TorrentProvider): # pylint: disable=too-many-instance- @staticmethod def loginSuccess(output): - if "<title>Login :: Danishbits.org</title>" in output: + if not output or "<title>Login :: Danishbits.org</title>" in output: return False else: return True diff --git a/sickbeard/providers/hd4free.py b/sickbeard/providers/hd4free.py new file mode 100644 index 0000000000000000000000000000000000000000..f6b6e715e045fbf8a641c24b1dfcdcffc981a1d7 --- /dev/null +++ b/sickbeard/providers/hd4free.py @@ -0,0 +1,129 @@ +# coding=utf-8 +# Author: Gonçalo (aka duramato) <matigonkas@outlook.com> +# URL: https://github.com/SickRage/SickRage +# +# This file is part of SickRage. +# +# SickRage is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# SickRage is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with SickRage. If not, see <http://www.gnu.org/licenses/>. + +from urllib import urlencode +from sickbeard import logger +from sickbeard import tvcache +from sickrage.providers.TorrentProvider import TorrentProvider + + +class HD4FREEProvider(TorrentProvider): # pylint: disable=too-many-instance-attributes + + def __init__(self): + TorrentProvider.__init__(self, "HD4Free") + + self.public = True + self.url = 'https://hd4free.xyz' + self.ratio = 0 + self.cache = HD4FREECache(self) + self.minseed, self.minleech = 2 * [None] + self.username = None + self.api_key = None + self.freeleech = None + + def _check_auth(self): + if self.username and self.api_key: + return True + + logger.log('Your authentication credentials for %s are missing, check your config.' % self.name) + return False + + def search(self, search_strings, age=0, ep_obj=None): # pylint: disable=too-many-locals + + results = [] + items = {'Season': [], 'Episode': [], 'RSS': []} + + search_params = { + 'tv': 'true', + 'username': self.username, + 'apikey': self.api_key + } + + for mode in search_strings.keys(): # Mode = RSS, Season, Episode + logger.log(u"Search Mode: %s" % mode, logger.DEBUG) + for search_string in search_strings[mode]: + if mode != 'RSS': + search_params['search'] = search_string.encode('utf-8') + + search_params['fl'] = 'true' if self.freeleech else 'false' + + if mode != 'RSS': + logger.log(u"Search string: " + search_string.strip(), logger.DEBUG) + + searchURL = self.url + "/searchapi.php?" + urlencode(search_params) + logger.log(u"Search URL: %s" % searchURL, logger.DEBUG) + jdata = self.get_url(searchURL, json=True) + if not jdata: + logger.log(u"No data returned from provider", logger.DEBUG) + continue + + try: + if jdata['0']['total_results'] == 0: + logger.log(u"Provider has no results for this search", logger.DEBUG) + continue + except (ValueError, KeyError): + pass + + for i in jdata: + seeders = jdata[i]["seeders"] + leechers = jdata[i]["leechers"] + title = jdata[i]["release_name"] + size = jdata[i]["size"] + download_url = jdata[i]["download_url"] + + if not all([title, download_url]): + continue + + # Filter unseeded torrent + if seeders < self.minseed or leechers < self.minleech: + if mode != 'RSS': + logger.log(u"Discarding torrent because it doesn't meet the minimum seeders or leechers: {0} (S:{1} L:{2})".format(title, seeders, leechers), logger.DEBUG) + continue + + if mode != 'RSS': + logger.log(u"Found result: %s " % title, logger.DEBUG) + + item = title, download_url, size, seeders, leechers + items[mode].append(item) + + # For each search mode sort all the items by seeders if available + items[mode].sort(key=lambda tup: tup[3], reverse=True) + + results += items[mode] + + return results + + def seed_ratio(self): + return self.ratio + + +class HD4FREECache(tvcache.TVCache): + def __init__(self, provider_obj): + + tvcache.TVCache.__init__(self, provider_obj) + + # Cache results for 10 min + self.minTime = 10 + + def _getRSSData(self): + + search_params = {'RSS': ['']} + return {'entries': self.provider.search(search_params)} + +provider = HD4FREEProvider() diff --git a/sickbeard/providers/hdbits.py b/sickbeard/providers/hdbits.py index 72f069d6bc967d39ba16bb4e4d70186ff2a89f71..888e31649df057eed21ff9d8c93791b73bea9594 100644 --- a/sickbeard/providers/hdbits.py +++ b/sickbeard/providers/hdbits.py @@ -69,11 +69,7 @@ class HDBitsProvider(TorrentProvider): return episode_search_string def _get_title_and_url(self, item): - - title = item['name'] - if title: - title = self._clean_title(title) - + title = item.get('name', '').replace(' ', '.') url = self.urls['download'] + urllib.urlencode({'id': item['id'], 'passkey': self.passkey}) return title, url diff --git a/sickbeard/providers/hounddawgs.py b/sickbeard/providers/hounddawgs.py index c5f1fb6202a7b083b2e74c3ac2fc792a455ca6f5..2d2b4a77438ead5f2efebcb9889b5ab3ba162d95 100644 --- a/sickbeard/providers/hounddawgs.py +++ b/sickbeard/providers/hounddawgs.py @@ -190,7 +190,7 @@ class HoundDawgsProvider(TorrentProvider): # pylint: disable=too-many-instance- modifier = matches.group(2) mod = {'K': 1, 'M': 2, 'G': 3, 'T': 4} - return float(size) * 1024**mod[modifier] + return int(float(size) * 1024**mod[modifier]) def seed_ratio(self): diff --git a/sickbeard/providers/rsstorrent.py b/sickbeard/providers/rsstorrent.py index dd728c4e7def044e2c807ae79fa6b66fc7f2851d..bb3d72ed7317664fb0580b6ca9201d1ae2c5c0e9 100644 --- a/sickbeard/providers/rsstorrent.py +++ b/sickbeard/providers/rsstorrent.py @@ -71,9 +71,7 @@ class TorrentRssProvider(TorrentProvider): def _get_title_and_url(self, item): - title = item.get(self.titleTAG) - if title: - title = self._clean_title(title) + title = item.get(self.titleTAG, '').replace(' ', '.') attempt_list = [lambda: item.get('torrent_magneturi'), diff --git a/sickbeard/search.py b/sickbeard/search.py index f4b0deaf7e36b4dbbe264b92b70b625059d33896..ab4411f83bbfb87f9cd884402334c30448a692f1 100644 --- a/sickbeard/search.py +++ b/sickbeard/search.py @@ -367,7 +367,6 @@ def searchForNeededEpisodes(): didSearch = False origThreadName = threading.currentThread().name - threads = [] show_list = sickbeard.showList fromDate = datetime.date.fromordinal(1) @@ -375,19 +374,15 @@ def searchForNeededEpisodes(): for curShow in show_list: if not curShow.paused: + sickbeard.name_cache.buildNameCache(curShow) episodes.extend(wantedEpisodes(curShow, fromDate)) providers = [x for x in sickbeard.providers.sortedProviderList(sickbeard.RANDOMIZE_PROVIDERS) if x.is_active() and x.enable_daily] for curProvider in providers: - threads += [threading.Thread(target=curProvider.cache.updateCache, name=origThreadName + " :: [" + curProvider.name + "]")] - - # start the thread we just created - for t in threads: - t.start() + threading.currentThread().name = origThreadName + " :: [" + curProvider.name + "]" + curProvider.cache.updateCache() - # wait for all threads to finish - for t in threads: - t.join() + threading.currentThread().name = origThreadName for curProvider in providers: threading.currentThread().name = origThreadName + " :: [" + curProvider.name + "]" @@ -447,7 +442,6 @@ def searchProviders(show, episodes, manualSearch=False, downCurQuality=False): finalResults = [] didSearch = False - threads = [] # build name cache for show sickbeard.name_cache.buildNameCache(show) @@ -456,18 +450,12 @@ def searchProviders(show, episodes, manualSearch=False, downCurQuality=False): providers = [x for x in sickbeard.providers.sortedProviderList(sickbeard.RANDOMIZE_PROVIDERS) if x.is_active() and x.enable_backlog] for curProvider in providers: - threads += [threading.Thread(target=curProvider.cache.updateCache, - name=origThreadName + " :: [" + curProvider.name + "]")] - - # start the thread we just created - for t in threads: - t.start() + threading.currentThread().name = origThreadName + " :: [" + curProvider.name + "]" + curProvider.cache.updateCache() - # wait for all threads to finish - for t in threads: - t.join() + threading.currentThread().name = origThreadName - for providerNum, curProvider in enumerate(providers): + for curProvider in providers: if curProvider.anime_only and not show.is_anime: logger.log(u"" + str(show.name) + " is not an anime, skiping", logger.DEBUG) continue diff --git a/sickbeard/subtitles.py b/sickbeard/subtitles.py index a7fab4ccdc17f97f44658ff57e6286aac2444dc8..932caded5b61b2dd5d53a85f1add51836e67bba8 100644 --- a/sickbeard/subtitles.py +++ b/sickbeard/subtitles.py @@ -87,21 +87,19 @@ def sorted_service_list(): for current_service in sickbeard.SUBTITLES_SERVICES_LIST: if current_service in subliminal.provider_manager.names(): new_list.append({'name': current_service, - 'url': PROVIDER_URLS[current_service] if current_service in PROVIDER_URLS else - lmgtfy % current_service, + 'url': PROVIDER_URLS[current_service] if current_service in + PROVIDER_URLS else lmgtfy % current_service, 'image': current_service + '.png', - 'enabled': sickbeard.SUBTITLES_SERVICES_ENABLED[current_index] == 1 - }) + 'enabled': sickbeard.SUBTITLES_SERVICES_ENABLED[current_index] == 1}) current_index += 1 for current_service in subliminal.provider_manager.names(): if current_service not in [service['name'] for service in new_list]: new_list.append({'name': current_service, - 'url': PROVIDER_URLS[current_service] if current_service in PROVIDER_URLS else - lmgtfy % current_service, + 'url': PROVIDER_URLS[current_service] if current_service in + PROVIDER_URLS else lmgtfy % current_service, 'image': current_service + '.png', - 'enabled': False, - }) + 'enabled': False}) return new_list @@ -112,7 +110,7 @@ def enabled_service_list(): def wanted_languages(sql_like=None): wanted = frozenset(sickbeard.SUBTITLES_LANGUAGES).intersection(subtitle_code_filter()) - return (wanted, '%' + ','.join(wanted) + '%')[bool(sql_like)] + return (wanted, '%' + ','.join(sorted(wanted)) + '%')[bool(sql_like)] def get_needed_languages(subtitles): @@ -124,13 +122,16 @@ def subtitle_code_filter(): def needs_subtitles(subtitles): - if isinstance(subtitles, basestring) and sickbeard.SUBTITLES_MULTI: - subtitles = {subtitle.strip() for subtitle in subtitles.split(',')} + if not wanted_languages(): + return False + + if isinstance(subtitles, basestring): + subtitles = {subtitle.strip() for subtitle in subtitles.split(',') if subtitle.strip()} if sickbeard.SUBTITLES_MULTI: - return len(wanted_languages().difference(subtitles)) > 0 - elif 'und' not in subtitles: - return True + return wanted_languages().difference(subtitles) + + return 'und' not in subtitles # Hack around this for now. @@ -150,7 +151,7 @@ def code_from_code(code): return from_code(code).opensubtitles -def download_subtitles(subtitles_info): +def download_subtitles(subtitles_info): # pylint: disable=too-many-locals existing_subtitles = subtitles_info['subtitles'] if not needs_subtitles(existing_subtitles): @@ -168,6 +169,7 @@ def download_subtitles(subtitles_info): subtitles_path = get_subtitles_path(subtitles_info['location']).encode(sickbeard.SYS_ENCODING) video_path = subtitles_info['location'].encode(sickbeard.SYS_ENCODING) + user_score = 132 if sickbeard.SUBTITLES_PERFECT_MATCH else 111 video = get_video(video_path, subtitles_path=subtitles_path) if not video: @@ -195,13 +197,14 @@ def download_subtitles(subtitles_info): return (existing_subtitles, None) for sub in subtitles_list: - matches = sub.get_matches(video, hearing_impaired=False) - score = subliminal.subtitle.compute_score(matches, video) - logger.log(u"[%s] Subtitle score for %s is: %s (min=132)" % (sub.provider_name, sub.id, score), logger.DEBUG) + matches = sub.get_matches(video, hearing_impaired=False) + score = subliminal.subtitle.compute_score(matches, video) + logger.log(u"[%s] Subtitle score for %s is: %s (min=%s)" + % (sub.provider_name, sub.id, score, user_score), logger.DEBUG) - found_subtitles = pool.download_best_subtitles(subtitles_list, video, languages=languages, min_score=132, + found_subtitles = pool.download_best_subtitles(subtitles_list, video, languages=languages, hearing_impaired=sickbeard.SUBTITLES_HEARING_IMPAIRED, - only_one=not sickbeard.SUBTITLES_MULTI) + min_score=user_score, only_one=not sickbeard.SUBTITLES_MULTI) subliminal.save_subtitles(video, found_subtitles, directory=subtitles_path, single=not sickbeard.SUBTITLES_MULTI) @@ -230,8 +233,8 @@ def download_subtitles(subtitles_info): if sickbeard.SUBTITLES_HISTORY: for subtitle in found_subtitles: - logger.log(u'history.logSubtitle %s, %s' % - (subtitle.provider_name, subtitle.language.opensubtitles), logger.DEBUG) + logger.log(u'history.logSubtitle %s, %s' + % (subtitle.provider_name, subtitle.language.opensubtitles), logger.DEBUG) history.logSubtitle(subtitles_info['show_indexerid'], subtitles_info['season'], subtitles_info['episode'], subtitles_info['status'], subtitle) @@ -311,7 +314,7 @@ class SubtitlesFinder(object): self.amActive = False @staticmethod - def subtitles_download_in_pp(): # pylint: disable=too-many-locals + def subtitles_download_in_pp(): # pylint: disable=too-many-locals, too-many-branches logger.log(u'Checking for needed subtitles in Post-Process folder', logger.INFO) providers = enabled_service_list() @@ -340,8 +343,9 @@ class SubtitlesFinder(object): if new_video_filename != video_filename: os.rename(video_filename, new_video_filename) video_filename = new_video_filename - except Exception as e: - logger.log(u'Could not remove non release groups from video file. Error: %r' % ex(e), logger.DEBUG) + except Exception as error: + logger.log(u'Could not remove non release groups from video file. Error: %r' + % ex(error), logger.DEBUG) if video_filename.rsplit(".", 1)[1] in media_extensions: try: video = subliminal.scan_video(os.path.join(root, video_filename), @@ -354,14 +358,17 @@ class SubtitlesFinder(object): continue hearing_impaired = sickbeard.SUBTITLES_HEARING_IMPAIRED + user_score = 132 if sickbeard.SUBTITLES_PERFECT_MATCH else 111 found_subtitles = pool.download_best_subtitles(subtitles_list, video, languages=languages, - hearing_impaired=hearing_impaired, min_score=132, + hearing_impaired=hearing_impaired, + min_score=user_score, only_one=not sickbeard.SUBTITLES_MULTI) for sub in subtitles_list: - matches = sub.get_matches(video, hearing_impaired=False) - score = subliminal.subtitle.compute_score(matches, video) - logger.log(u"[%s] Subtitle score for %s is: %s (min=132)" % (sub.provider_name, sub.id, score), logger.DEBUG) + matches = sub.get_matches(video, hearing_impaired=False) + score = subliminal.subtitle.compute_score(matches, video) + logger.log(u"[%s] Subtitle score for %s is: %s (min=%s)" + % (sub.provider_name, sub.id, score, user_score), logger.DEBUG) downloaded_languages = set() for subtitle in found_subtitles: @@ -392,7 +399,7 @@ class SubtitlesFinder(object): logger.log(u"Starting post-process with default settings now that we found subtitles") processTV.processDir(sickbeard.TV_DOWNLOAD_DIR) - def run(self, force=False): # pylint: disable=unused-argument + def run(self, force=False): # pylint: disable=unused-argument,too-many-statements,too-many-branches if not sickbeard.USE_SUBTITLES: return @@ -416,24 +423,34 @@ class SubtitlesFinder(object): # - search count < 2 and diff(airdate, now) > 1 week : now -> 1d # - search count < 7 and diff(airdate, now) <= 1 week : now -> 4h -> 8h -> 16h -> 1d -> 1d -> 1d + """ + Defines the hours to wait between 2 subtitles search depending on: + - the episode: new or old + - the number of searches done so far (searchcount), represented by the index of the list + """ + rules = {'old': [0, 24], 'new': [0, 4, 8, 4, 16, 24, 24]} + + if sickbeard.SUBTITLES_MULTI: + query_languages = wanted_languages(True) + else: + query_languages = '%und%' + today = datetime.date.today().toordinal() database = db.DBConnection() - sql_results = database.select( - 'SELECT s.show_name, e.showid, e.season, e.episode, e.status, e.subtitles, ' + + 'SELECT s.show_name, e.showid, e.season, e.episode, e.status, e.subtitles, ' 'e.subtitles_searchcount AS searchcount, e.subtitles_lastsearch AS lastsearch, e.location, ' - '(? - e.airdate) AS airdate_daydiff ' + - 'FROM tv_episodes AS e INNER JOIN tv_shows AS s ON (e.showid = s.indexer_id) ' + - 'WHERE s.subtitles = 1 AND e.subtitles NOT LIKE (?) ' + - 'AND (e.subtitles_searchcount <= 2 OR (e.subtitles_searchcount <= 7 AND airdate_daydiff <= 7)) ' + - 'AND e.location != ""', [today, wanted_languages(True)]) + '(? - e.airdate) AS airdate_daydiff ' + 'FROM tv_episodes AS e INNER JOIN tv_shows AS s ON (e.showid = s.indexer_id) ' + 'WHERE s.subtitles = 1 AND e.subtitles NOT LIKE ? ' + 'AND (e.subtitles_searchcount <= 2 OR (e.subtitles_searchcount <= 7 AND airdate_daydiff <= 7)) ' + 'AND e.location != ""', [today, query_languages]) if len(sql_results) == 0: logger.log(u'No subtitles to download', logger.INFO) self.amActive = False return - rules = self._get_rules() now = datetime.datetime.now() for ep_to_sub in sql_results: @@ -447,58 +464,56 @@ class SubtitlesFinder(object): % (ep_to_sub['show_name'], ep_to_sub['season'], ep_to_sub['episode']), logger.DEBUG) continue - # http://bugs.python.org/issue7980#msg221094 - # I dont think this needs done here, but keeping to be safe (Recent shows rule) - datetime.datetime.strptime('20110101', '%Y%m%d') - if ((ep_to_sub['airdate_daydiff'] > 7 and ep_to_sub['searchcount'] < 2 and - now - datetime.datetime.strptime(ep_to_sub['lastsearch'], dateTimeFormat) > - datetime.timedelta(hours=rules['old'][ep_to_sub['searchcount']])) or - (ep_to_sub['airdate_daydiff'] <= 7 and ep_to_sub['searchcount'] < 7 and - now - datetime.datetime.strptime(ep_to_sub['lastsearch'], dateTimeFormat) > - datetime.timedelta(hours=rules['new'][ep_to_sub['searchcount']]))): - - logger.log(u'Downloading subtitles for %s S%02dE%02d' - % (ep_to_sub['show_name'], ep_to_sub['season'], ep_to_sub['episode']), logger.DEBUG) + logger.log(u"%s S%02dE%02d doesn't have all needed subtitles" + % (ep_to_sub['show_name'], ep_to_sub['season'], ep_to_sub['episode']), logger.DEBUG) + + try: + try: + lastsearched = datetime.datetime.strptime(ep_to_sub['lastsearch'], dateTimeFormat) + except ValueError: + lastsearched = datetime.datetime.min - show_object = Show.find(sickbeard.showList, int(ep_to_sub['showid'])) - if not show_object: - logger.log(u'Show with ID %s not found in the database' % ep_to_sub['showid'], logger.DEBUG) - self.amActive = False - return + if ((ep_to_sub['airdate_daydiff'] > 7 and ep_to_sub['searchcount'] < 2 and + now - lastsearched > datetime.timedelta(hours=rules['old'][ep_to_sub['searchcount']])) or + (ep_to_sub['airdate_daydiff'] <= 7 and ep_to_sub['searchcount'] < 7 and + now - lastsearched > datetime.timedelta(hours=rules['new'][ep_to_sub['searchcount']]))): - episode_object = show_object.getEpisode(int(ep_to_sub["season"]), int(ep_to_sub["episode"])) - if isinstance(episode_object, str): - logger.log(u'%s S%02dE%02d not found in the database' - % (ep_to_sub['show_name'], ep_to_sub['season'], ep_to_sub['episode']), logger.DEBUG) - self.amActive = False - return + logger.log(u'Started subtitles search for %s S%02dE%02d' + % (ep_to_sub['show_name'], ep_to_sub['season'], ep_to_sub['episode']), logger.INFO) - existing_subtitles = episode_object.subtitles + show_object = Show.find(sickbeard.showList, int(ep_to_sub['showid'])) + if not show_object: + logger.log(u'Show with ID %s not found in the database' % ep_to_sub['showid'], logger.DEBUG) + continue - try: - episode_object.download_subtitles() - except Exception as error: - logger.log(u'Unable to find subtitles for %s S%02dE%02d' - % (ep_to_sub['show_name'], ep_to_sub['season'], ep_to_sub['episode']), logger.DEBUG) - logger.log(str(error), logger.DEBUG) - self.amActive = False - return - - new_subtitles = frozenset(episode_object.subtitles).difference(existing_subtitles) - if new_subtitles: - logger.log(u'Downloaded %s subtitles for %s S%02dE%02d' - % (', '.join(new_subtitles), ep_to_sub['show_name'], ep_to_sub["season"], ep_to_sub["episode"])) + episode_object = show_object.getEpisode(int(ep_to_sub["season"]), int(ep_to_sub["episode"])) + if isinstance(episode_object, str): + logger.log(u'%s S%02dE%02d not found in the database' + % (ep_to_sub['show_name'], ep_to_sub['season'], ep_to_sub['episode']), logger.DEBUG) + continue - self.amActive = False + existing_subtitles = episode_object.subtitles - @staticmethod - def _get_rules(): - """ - Define the hours to wait between 2 subtitles search depending on: - - the episode: new or old - - the number of searches done so far (searchcount), represented by the index of the list - """ - return {'old': [0, 24], 'new': [0, 4, 8, 4, 16, 24, 24]} + try: + episode_object.download_subtitles() + except Exception as error: + logger.log(u'Unable to find subtitles for %s S%02dE%02d. Error: %r' + % (ep_to_sub['show_name'], ep_to_sub['season'], ep_to_sub['episode'], + ex(error)), logger.ERROR) + continue + + new_subtitles = frozenset(episode_object.subtitles).difference(existing_subtitles) + if new_subtitles: + logger.log(u'Downloaded %s subtitles for %s S%02dE%02d' + % (', '.join(new_subtitles), ep_to_sub['show_name'], + ep_to_sub["season"], ep_to_sub["episode"])) + except Exception as error: + logger.log(u'Error while searching subtitles for %s S%02dE%02d. Error: %r' + % (ep_to_sub['show_name'], ep_to_sub['season'], ep_to_sub['episode'], + ex(error)), logger.ERROR) + continue + + self.amActive = False def run_subs_extra_scripts(episode_object, found_subtitles, video, single=False): diff --git a/sickbeard/versionChecker.py b/sickbeard/versionChecker.py index 414fb08911a229a412aa716588301d882f67014a..40ee5ffe0bd8289ee7e7b21380e413f719f17f87 100644 --- a/sickbeard/versionChecker.py +++ b/sickbeard/versionChecker.py @@ -686,7 +686,7 @@ class GitUpdateManager(UpdateManager): def update_remote_origin(self): self._run_git(self._git_path, 'config remote.%s.url %s' % (sickbeard.GIT_REMOTE, sickbeard.GIT_REMOTE_URL)) if sickbeard.GIT_USERNAME: - self._run_git(self._git_path, 'config remote.%s.pushurl %s' % (sickbeard.GIT_REMOTE, sickbeard.GIT_REMOTE_URL.replace(sickbeard.GIT_ORG, sickbeard.GIT_USERNAME))) + self._run_git(self._git_path, 'config remote.%s.pushurl %s' % (sickbeard.GIT_REMOTE, sickbeard.GIT_REMOTE_URL.replace(sickbeard.GIT_ORG, sickbeard.GIT_USERNAME, 1))) class SourceUpdateManager(UpdateManager): def __init__(self): diff --git a/sickbeard/webserve.py b/sickbeard/webserve.py index 69e9ed15461e1b8dc2ded14b8d92ebb886e97db9..8f6d76f65bfe6a72895d4a93d5abbdc77041bfca 100644 --- a/sickbeard/webserve.py +++ b/sickbeard/webserve.py @@ -4091,9 +4091,12 @@ class ConfigPostProcessing(Config): sickbeard.FILE_TIMESTAMP_TIMEZONE = file_timestamp_timezone sickbeard.MOVE_ASSOCIATED_FILES = config.checkbox_to_value(move_associated_files) sickbeard.SYNC_FILES = sync_files - sickbeard.ALLOWED_EXTENSIONS = allowed_extensions sickbeard.POSTPONE_IF_SYNC_FILES = config.checkbox_to_value(postpone_if_sync_files) sickbeard.POSTPONE_IF_NO_SUBS = config.checkbox_to_value(postpone_if_no_subs) + # If 'postpone if no subs' is enabled, we must have SRT in allowed extensions list + if sickbeard.POSTPONE_IF_NO_SUBS: + allowed_extensions += ',srt' + sickbeard.ALLOWED_EXTENSIONS = ','.join({x.strip() for x in allowed_extensions.split(',') if x.strip()}) sickbeard.NAMING_CUSTOM_ABD = config.checkbox_to_value(naming_custom_abd) sickbeard.NAMING_CUSTOM_SPORTS = config.checkbox_to_value(naming_custom_sports) sickbeard.NAMING_CUSTOM_ANIME = config.checkbox_to_value(naming_custom_anime) @@ -4966,7 +4969,7 @@ class ConfigSubtitles(Config): header='Subtitles', topmenu='config', controller="config", action="subtitles") - def saveSubtitles(self, use_subtitles=None, subtitles_plugins=None, subtitles_languages=None, subtitles_dir=None, + def saveSubtitles(self, use_subtitles=None, subtitles_plugins=None, subtitles_languages=None, subtitles_dir=None, subtitles_perfect_match=None, service_order=None, subtitles_history=None, subtitles_finder_frequency=None, subtitles_download_in_pp=None, subtitles_multi=None, embedded_subtitles_all=None, subtitles_extra_scripts=None, subtitles_hearing_impaired=None, addic7ed_user=None, addic7ed_pass=None, legendastv_user=None, legendastv_pass=None, opensubtitles_user=None, opensubtitles_pass=None): @@ -4978,6 +4981,7 @@ class ConfigSubtitles(Config): sickbeard.SUBTITLES_LANGUAGES = [code.strip() for code in subtitles_languages.split(',') if code.strip() in subtitles.subtitle_code_filter()] if subtitles_languages else [] sickbeard.SUBTITLES_DIR = subtitles_dir + sickbeard.SUBTITLES_PERFECT_MATCH = config.checkbox_to_value(subtitles_perfect_match) sickbeard.SUBTITLES_HISTORY = config.checkbox_to_value(subtitles_history) sickbeard.EMBEDDED_SUBTITLES_ALL = config.checkbox_to_value(embedded_subtitles_all) sickbeard.SUBTITLES_HEARING_IMPAIRED = config.checkbox_to_value(subtitles_hearing_impaired) diff --git a/tests/api_v1_tests.py b/tests/api_v1_tests.py new file mode 100644 index 0000000000000000000000000000000000000000..271bdcd8153389dc09acf9e0b88487fe0fb4d4c2 --- /dev/null +++ b/tests/api_v1_tests.py @@ -0,0 +1,257 @@ +# coding=utf-8 + +""" +Test the SR API +""" + +import unittest + + +class APITestEpisodes(unittest.TestCase): + """ + Test episode commands for API + + This tests all episode related commands from the legacy api. + """ + @unittest.skip('Not yet implemented') + def test_episode(self): + """ + Test getting detailed information about an episode using the legacy API. + + :return: None + """ + pass + + @unittest.skip('Not yet implemented') + def test_episode_search(self): + """ + Test searching for an episode using the legacy API. + The response might take some time. + :return: None + """ + pass + + @unittest.skip('Not yet implemented') + def test_episode_set_status(self): + pass + + @unittest.skip('Not yet implemented') + def test_episode_subtitle_search(self): + pass + + +class APITestShows(unittest.TestCase): + """ + Test shows commands for API + """ + @unittest.skip('Not yet implemented') + def test_shows(self): + pass + + @unittest.skip('Not yet implemented') + def test_shows_stats(self): + pass + + +class APITestShow(unittest.TestCase): + """ + Test show commands for API + """ + @unittest.skip('Not yet implemented') + def test_show(self): + pass + + @unittest.skip('Not yet implemented') + def test_show_add_existing(self): + pass + + @unittest.skip('Not yet implemented') + def test_show_add_new(self): + pass + + @unittest.skip('Not yet implemented') + def test_show_cache(self): + pass + + @unittest.skip('Not yet implemented') + def test_show_delete(self): + pass + + @unittest.skip('Not yet implemented') + def test_show_get_banner(self): + pass + + @unittest.skip('Not yet implemented') + def test_show_get_fanart(self): + pass + + @unittest.skip('Not yet implemented') + def test_show_get_network_logo(self): + pass + + @unittest.skip('Not yet implemented') + def test_show_get_poster(self): + pass + + @unittest.skip('Not yet implemented') + def test_show_get_quality(self): + pass + + @unittest.skip('Not yet implemented') + def test_show_pause(self): + pass + + @unittest.skip('Not yet implemented') + def test_show_refresh(self): + pass + + @unittest.skip('Not yet implemented') + def test_show_season_list(self): + pass + + @unittest.skip('Not yet implemented') + def test_show_seasons(self): + pass + + @unittest.skip('Not yet implemented') + def test_show_set_quality(self): + pass + + @unittest.skip('Not yet implemented') + def test_show_stats(self): + pass + + @unittest.skip('Not yet implemented') + def test_show_update(self): + pass + + +class APITestSickBeard(unittest.TestCase): + @unittest.skip('Not yet implemented') + def test_sickbeard(self): + pass + + @unittest.skip('Not yet implemented') + def test_sb_add_root_dir(self): + pass + + @unittest.skip('Not yet implemented') + def test_sb_check_scheduler(self): + pass + + @unittest.skip('Not yet implemented') + def test_sb_check_version(self): + pass + + @unittest.skip('Not yet implemented') + def test_sb_delete_root_dir(self): + pass + + @unittest.skip('Not yet implemented') + def test_sb_get_defaults(self): + pass + + @unittest.skip('Not yet implemented') + def test_sb_get_messages(self): + pass + + @unittest.skip('Not yet implemented') + def test_sb_get_root_dirs(self): + pass + + @unittest.skip('Not yet implemented') + def test_sb_pause_backlog(self): + pass + + @unittest.skip('Not yet implemented') + def test_sb_ping(self): + pass + + @unittest.skip('Not yet implemented') + def test_sb_restart(self): + pass + + @unittest.skip('Not yet implemented') + def test_sb_search_indexers(self): + pass + + @unittest.skip('Not yet implemented') + def test_sb_search_tvdb(self): + pass + + @unittest.skip('Not yet implemented') + def test_sb_search_tvrage(self): + pass + + @unittest.skip('Not yet implemented') + def test_sb_set_defaults(self): + pass + + @unittest.skip('Not yet implemented') + def test_sb_shutdown(self): + pass + + @unittest.skip('Not yet implemented') + def test_sb_update(self): + pass + + +class APITestHistory(unittest.TestCase): + @unittest.skip('Not yet implemented') + def test_history(self): + pass + + @unittest.skip('Not yet implemented') + def test_history_clear(self): + pass + + @unittest.skip('Not yet implemented') + def test_history_trim(self): + pass + + +class APITestMisc(unittest.TestCase): + @unittest.skip('Not yet implemented') + def test_backlog(self): + pass + + @unittest.skip('Not yet implemented') + def test_exceptions(self): + pass + + @unittest.skip('Not yet implemented') + def test_failed(self): + pass + + @unittest.skip('Not yet implemented') + def test_future(self): + pass + + @unittest.skip('Not yet implemented') + def test_help(self): + pass + + @unittest.skip('Not yet implemented') + def test_logs(self): + pass + + @unittest.skip('Not yet implemented') + def test_post_process(self): + pass + + +TEST_CLASSES = { + APITestEpisodes, APITestHistory, APITestMisc, APITestShow, APITestShows, APITestSickBeard +} + + +def run_all(): + """ + Run all tests + :return: + """ + unittest.main(verbosity=2) + + +if __name__ == '__main__': + run_all() diff --git a/tests/ssl_sni_tests.py b/tests/ssl_sni_tests.py index 1d69b96314025c85b559a9ce04782ce6df0130c3..add4e704002c873d53c440416bd4c4e3a8138e53 100644 --- a/tests/ssl_sni_tests.py +++ b/tests/ssl_sni_tests.py @@ -36,34 +36,49 @@ import requests # pylint: disable=import-error import sickbeard.providers as providers -class SniTests(unittest.TestCase): - """ - Test SNI +def test_generator(_provider): """ - self_signed_cert_providers = ["Womble's Index", "Libertalia"] + Generate tests for each provider - def test_sni_urls(self): + :param test_strings: to generate tests from + :return: test + """ + def _connectivity_test(self): # pylint: disable=unused-argument """ - Test SNI urls - :return: + Generate tests + :param self: + :return: test to run """ - print '' - # Just checking all providers - we should make this error on non-existent urls. - for provider in [provider for provider in providers.makeProviderList() if provider.name not in self.self_signed_cert_providers]: - print 'Checking %s' % provider.name - try: - requests.head(provider.url, verify=certifi.old_where(), timeout=10) - except requests.exceptions.Timeout: - pass - except requests.exceptions.SSLError as error: - if u'SSL3_GET_SERVER_CERTIFICATE' not in ex(error): - print 'SSLError on %s: %s' % (provider.name, ex(error.message)) - raise - else: - print 'Cannot verify certificate for %s' % provider.name - except Exception: # pylint: disable=broad-except - pass + if not _provider.url: + print '%s has no url set, skipping' % _provider.name + return + + try: + requests.head(_provider.url, verify=certifi.old_where(), timeout=10) + except requests.exceptions.SSLError as error: + if 'certificate verify failed' in str(error): + print 'Cannot verify certificate for %s' % _provider.name + else: + print 'SSLError on %s: %s' % (_provider.name, ex(error.message)) + raise + except requests.exceptions.Timeout: + print 'Provider timed out' + + return _connectivity_test + +class SniTests(unittest.TestCase): + pass if __name__ == "__main__": + print("==================") + print("STARTING - Provider Connectivity TESTS and SSL/SNI") + print("==================") + print("######################################################################") + # Just checking all providers - we should make this error on non-existent urls. + for provider in [p for p in providers.makeProviderList()]: + test_name = 'test_%s' % provider.name + test = test_generator(provider) + setattr(SniTests, test_name, test) + SUITE = unittest.TestLoader().loadTestsFromTestCase(SniTests) unittest.TextTestRunner(verbosity=2).run(SUITE)