diff --git a/gui/slick/images/providers/sick_beard_index.png b/gui/slick/images/providers/sick_beard_index.png new file mode 100644 index 0000000000000000000000000000000000000000..2ba4d236a4bc8196a9574bb4282530793ddbab4a Binary files /dev/null and b/gui/slick/images/providers/sick_beard_index.png differ diff --git a/gui/slick/images/providers/sickbeard_index.png b/gui/slick/images/providers/sickbeard_index.png new file mode 100644 index 0000000000000000000000000000000000000000..2ba4d236a4bc8196a9574bb4282530793ddbab4a Binary files /dev/null and b/gui/slick/images/providers/sickbeard_index.png differ diff --git a/gui/slick/js/core.js b/gui/slick/js/core.js index e1cb8c23f8c927a803edaac5059793fd0c72ce70..c4cb8db07296728bce0703b45c6b19b26fb3d8cb 100644 --- a/gui/slick/js/core.js +++ b/gui/slick/js/core.js @@ -385,7 +385,7 @@ var SICKRAGE = { plex.client.host = $.trim($('#plex_host').val()); plex.client.username = $.trim($('#plex_client_username').val()); plex.client.password = $.trim($('#plex_client_password').val()); - if (!plex.host) { + if (!plex.client.host) { $('#testPMC-result').html('Please fill out the necessary fields above.'); $('#plex_host').addClass('warning'); return; @@ -1626,9 +1626,9 @@ var SICKRAGE = { $('#torrent_auth_type_option').show(); } else if (selectedProvider.toLowerCase() === 'qbittorrent'){ client = 'qbittorrent'; + $('#torrent_path_option').hide(); $('#label_warning_qbittorrent').show(); - $('#label_anime_warning_qbittorrrent').show(); - $('#torrent_label_anime_option').hide(); + $('#label_anime_warning_qbittorrent').show(); $('#host_desc_torrent').text('URL to your qbittorrent client (e.g. http://localhost:8080)'); } else if (selectedProvider.toLowerCase() === 'mlnet'){ client = 'mlnet'; diff --git a/gui/slick/js/core.min.js b/gui/slick/js/core.min.js index 8c06f8ad2a4bae5d25b12933e3defc490d19df09..1036343312e0c8d5b50291c3921a2d96bca097cc 100644 Binary files a/gui/slick/js/core.min.js and b/gui/slick/js/core.min.js differ diff --git a/gui/slick/views/config.mako b/gui/slick/views/config.mako index a6208e93f7d37651849365107b30018ae46a52f4..f11755430437d292bb43c2c126a209b4b216480d 100644 --- a/gui/slick/views/config.mako +++ b/gui/slick/views/config.mako @@ -3,7 +3,8 @@ import sickbeard from sickbeard import db from sickbeard.helpers import anon_url - import sys, os + from sickbeard.versionChecker import CheckVersion + import sys %> <%block name="content"> % if not header is UNDEFINED: @@ -25,9 +26,22 @@ ##disk_percent_used = ${disk.percent} <div id="config-content"> <table class="infoTable" cellspacing="1" border="0" cellpadding="0" width="100%"> - <tr><td class="infoTableHeader">SR Version: </td><td class="infoTableCell"> + <tr><td class="infoTableHeader" style="vertical-align: top;">Version:</td><td class="infoTableCell"> % if sickbeard.VERSION_NOTIFY: - BRANCH: (${sickbeard.BRANCH}) / COMMIT: (${sickbeard.CUR_COMMIT_HASH}) <!-- – build.date //--><br> + Branch: <a href="${anon_url('https://github.com/SickRage/SickRage/tree/%s' % sickbeard.BRANCH)}">${sickbeard.BRANCH}</a><br> + Commit: <a href="${anon_url('https://github.com/SickRage/SickRage/commit/%s' % sickbeard.CUR_COMMIT_HASH)}">${sickbeard.CUR_COMMIT_HASH}</a><br> + <% + updater = CheckVersion().updater + %> + + % if updater is not None: + <% + updater.need_update() + %> + + Version: <a href="${anon_url('https://github.com/SickRage/SickRage/releases/tag/%s' % updater.get_cur_version())}">${updater.get_cur_version()}</a> + % endif + <!-- – build.date //--> % else: You don't have version checking turned on. Please turn on "Check for Update" in Config > General.<br> % endif @@ -36,37 +50,40 @@ <% sr_user = None try: - import pwd + import os, pwd sr_user = pwd.getpwuid(os.getuid()).pw_name except ImportError: import getpass sr_user = getpass.getuser() %> % if sr_user: - <tr><td class="infoTableHeader">SR User:</td><td class="infoTableCell">${sr_user}</td></tr> + <tr><td class="infoTableHeader">User:</td><td class="infoTableCell">${sr_user}</td></tr> % endif % try: <% import locale %> <% sr_locale = locale.getdefaultlocale() %> - <tr><td class="infoTableHeader">SR Locale:</td><td class="infoTableCell">${sr_locale}</td></tr> + <tr><td class="infoTableHeader" style="vertical-align: top;">Locale:</td><td class="infoTableCell"> + Language: ${sr_locale[0]}<br> + Encoding: ${sr_locale[1]} + </td></tr> % except: "" % endtry - <tr><td class="infoTableHeader">SR Config:</td><td class="infoTableCell">${sickbeard.CONFIG_FILE}</td></tr> - <tr><td class="infoTableHeader">SR Database:</td><td class="infoTableCell">${db.dbFilename()}</td></tr> - <tr><td class="infoTableHeader">SR Cache Dir:</td><td class="infoTableCell">${sickbeard.CACHE_DIR}</td></tr> - <tr><td class="infoTableHeader">SR Log Dir:</td><td class="infoTableCell">${sickbeard.LOG_DIR}</td></tr> - <tr><td class="infoTableHeader">SR Arguments:</td><td class="infoTableCell">${sickbeard.MY_ARGS}</td></tr> + <tr><td class="infoTableHeader">Configuration File:</td><td class="infoTableCell">${sickbeard.CONFIG_FILE}</td></tr> + <tr><td class="infoTableHeader">Database:</td><td class="infoTableCell">${db.dbFilename()}</td></tr> + <tr><td class="infoTableHeader">Cache Directory:</td><td class="infoTableCell">${sickbeard.CACHE_DIR}</td></tr> + <tr><td class="infoTableHeader">Log Directory:</td><td class="infoTableCell">${sickbeard.LOG_DIR}</td></tr> + <tr><td class="infoTableHeader">Arguments:</td><td class="infoTableCell">${sickbeard.MY_ARGS}</td></tr> % if sickbeard.WEB_ROOT: - <tr><td class="infoTableHeader">SR Web Root:</td><td class="infoTableCell">${sickbeard.WEB_ROOT}</td></tr> + <tr><td class="infoTableHeader">Web Root:</td><td class="infoTableCell">${sickbeard.WEB_ROOT}</td></tr> % endif <tr><td class="infoTableHeader">Python Version:</td><td class="infoTableCell">${sys.version[:120]}</td></tr> - <tr class="infoTableSeperator"><td class="infoTableHeader"><i class="icon16-sb"></i> Website</td><td class="infoTableCell"><a href="${anon_url('http://sickrage.github.io/')}" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">http://sickrage.github.io/</a></td></tr> - <tr><td class="infoTableHeader"><i class="icon16-WiKi"></i> WiKi</td><td class="infoTableCell"><a href="${anon_url('https://github.com/SickRage/sickrage-issues/wiki')}" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">https://github.com/SickRage/sickrage-issues/wiki</a></td></tr> - <tr><td class="infoTableHeader"><i class="icon16-github"></i> Source</td><td class="infoTableCell"><a href="${anon_url('https://github.com/SickRage/SickRage/')}" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">https://github.com/SickRage/SickRage/</a></td></tr> - <tr><td class="infoTableHeader"><i class="icon16-mirc"></i> IRChat</td><td class="infoTableCell"><a href="irc://irc.freenode.net/#sickrage-issues" rel="noreferrer"><i>#sickrage-issues</i> on <i>irc.freenode.net</i></a></td></tr> + <tr class="infoTableSeperator"><td class="infoTableHeader"><i class="icon16-sb"></i> Website:</td><td class="infoTableCell"><a href="${anon_url('http://sickrage.github.io/')}" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">http://sickrage.github.io/</a></td></tr> + <tr><td class="infoTableHeader"><i class="icon16-WiKi"></i> Wiki:</td><td class="infoTableCell"><a href="${anon_url('https://github.com/SickRage/sickrage-issues/wiki')}" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">https://github.com/SickRage/sickrage-issues/wiki</a></td></tr> + <tr><td class="infoTableHeader"><i class="icon16-github"></i> Source:</td><td class="infoTableCell"><a href="${anon_url('https://github.com/SickRage/SickRage/')}" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">https://github.com/SickRage/SickRage/</a></td></tr> + <tr><td class="infoTableHeader"><i class="icon16-mirc"></i> IRChat:</td><td class="infoTableCell"><a href="irc://irc.freenode.net/#sickrage-issues" rel="noreferrer"><i>#sickrage-issues</i> on <i>irc.freenode.net</i></a></td></tr> </table> </div> </%block> diff --git a/gui/slick/views/manage_backlogOverview.mako b/gui/slick/views/manage_backlogOverview.mako index 5484580449cbd194a3ea4239ce56735f2967c57c..9b97601276c4d3fe2e0ae0f35829ca8e0ed7bdb0 100644 --- a/gui/slick/views/manage_backlogOverview.mako +++ b/gui/slick/views/manage_backlogOverview.mako @@ -32,13 +32,21 @@ %> <div class="h2footer pull-right"> + % if totalWanted > 0: <span class="listing-key wanted">Wanted: <b>${totalWanted}</b></span> + % endif + + % if totalQualSnatched > 0: <span class="listing-key snatched">Snatched (Low Quality): <b>${totalQualSnatched}</b></span> + % endif + + % if totalQual > 0: <span class="listing-key qual">Low Quality: <b>${totalQual}</b></span> + % endif </div><br> <div class="float-left"> -Jump to Show +Jump to Show: <select id="pickShow" class="form-control form-control-inline input-sm"> % for curShow in backLogShows: <option value="${curShow.indexerid}">${curShow.name}</option> @@ -52,14 +60,21 @@ Jump to Show <% continue %> % endif <tr class="seasonheader" id="show-${curShow.indexerid}"> - <td colspan="3" class="align-left"> - <br><h2><a href="${srRoot}/home/displayShow?show=${curShow.indexerid}">${curShow.name}</a></h2> - <div class="pull-right"> + <td colspan="3" class="align-left" style="position: relative;"> + <h2 style="display: inline-block;"><a href="${srRoot}/home/displayShow?show=${curShow.indexerid}">${curShow.name}</a></h2> + <div style="position: absolute; bottom: 10px; right: 0;"> + % if showCounts[curShow.indexerid][Overview.WANTED] > 0: <span class="listing-key wanted">Wanted: <b>${showCounts[curShow.indexerid][Overview.WANTED]}</b></span> - % if showQualSnatched(curShow): + % endif + + % if showQualSnatched(curShow) and showCounts[curShow.indexerid][Overview.SNATCHED] > 0: <span class="listing-key snatched">Snatched (Low Quality): <b>${showCounts[curShow.indexerid][Overview.SNATCHED]}</b></span> % endif + + % if showCounts[curShow.indexerid][Overview.QUAL] > 0: <span class="listing-key qual">Low Quality: <b>${showCounts[curShow.indexerid][Overview.QUAL]}</b></span> + % endif + <a class="btn btn-inline forceBacklog" href="${srRoot}/manage/backlogShow?indexer_id=${curShow.indexerid}"><i class="icon-play-circle icon-white"></i> Force Backlog</a> </div> </td> diff --git a/lib/subliminal/__init__.py b/lib/subliminal/__init__.py index 23dd54d0ecd1c0414a204f199b6d1670da5a38eb..4c0631efd474acb08cb4e46c56fc6a4968914597 100644 --- a/lib/subliminal/__init__.py +++ b/lib/subliminal/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- __title__ = 'subliminal' -__version__ = '1.1.0.dev0' +__version__ = '1.2.dev0' __author__ = 'Antoine Bertin' __license__ = 'MIT' __copyright__ = 'Copyright 2015, Antoine Bertin' diff --git a/lib/subliminal/api.py b/lib/subliminal/api.py index ee51940a91bdc79bfec04dcfe8b6d8989be6443d..3af8809a7d89644a099971b1839dc4bc131f94dd 100644 --- a/lib/subliminal/api.py +++ b/lib/subliminal/api.py @@ -37,6 +37,7 @@ provider_manager = InternalExtensionManager('subliminal.providers', [EntryPoint. 'napiprojekt = subliminal.providers.napiprojekt:NapiProjektProvider', 'opensubtitles = subliminal.providers.opensubtitles:OpenSubtitlesProvider', 'podnapisi = subliminal.providers.podnapisi:PodnapisiProvider', + 'subscenter = subliminal.providers.subscenter:SubsCenterProvider', 'thesubdb = subliminal.providers.thesubdb:TheSubDBProvider', 'tvsubtitles = subliminal.providers.tvsubtitles:TVsubtitlesProvider' )]) diff --git a/lib/subliminal/cli.py b/lib/subliminal/cli.py index 2c4f4c14f84b7b6713b4ecd8fbc70feaaa5067c0..7f44263a6c714d4d9667c5034ad8bfed97adf470 100644 --- a/lib/subliminal/cli.py +++ b/lib/subliminal/cli.py @@ -206,12 +206,13 @@ config_file = 'config.ini' @click.option('--legendastv', type=click.STRING, nargs=2, metavar='USERNAME PASSWORD', help='LegendasTV configuration.') @click.option('--opensubtitles', type=click.STRING, nargs=2, metavar='USERNAME PASSWORD', help='OpenSubtitles configuration.') +@click.option('--subscenter', type=click.STRING, nargs=2, metavar='USERNAME PASSWORD', help='SubsCenter configuration.') @click.option('--cache-dir', type=click.Path(writable=True, resolve_path=True, file_okay=False), default=app_dir, show_default=True, expose_value=True, help='Path to the cache directory.') @click.option('--debug', is_flag=True, help='Print useful information for debugging subliminal and for reporting bugs.') @click.version_option(__version__) @click.pass_context -def subliminal(ctx, addic7ed, legendastv, opensubtitles, cache_dir, debug): +def subliminal(ctx, addic7ed, legendastv, opensubtitles, subscenter, cache_dir, debug): """Subtitles, faster than your thoughts.""" # create cache directory try: @@ -239,6 +240,8 @@ def subliminal(ctx, addic7ed, legendastv, opensubtitles, cache_dir, debug): ctx.obj['provider_configs']['legendastv'] = {'username': legendastv[0], 'password': legendastv[1]} if opensubtitles: ctx.obj['provider_configs']['opensubtitles'] = {'username': opensubtitles[0], 'password': opensubtitles[1]} + if subscenter: + ctx.obj['provider_configs']['subscenter'] = {'username': subscenter[0], 'password': subscenter[1]} @subliminal.command() diff --git a/lib/subliminal/providers/addic7ed.py b/lib/subliminal/providers/addic7ed.py index c8b9ed2bc351e29980380fb4aeda167ab4bdb63c..6061319cb0f55c7c8286ceca7bdc173841bb5177 100644 --- a/lib/subliminal/providers/addic7ed.py +++ b/lib/subliminal/providers/addic7ed.py @@ -140,7 +140,7 @@ class Addic7edProvider(Provider): def _search_show_id(self, series, year=None): """Search the show id from the `series` and `year`. - :param string series: series of the episode. + :param str series: series of the episode. :param year: year of the series, if any. :type year: int or None :return: the show id, if found. diff --git a/lib/subliminal/providers/podnapisi.py b/lib/subliminal/providers/podnapisi.py index 3a1455714550a729cdeb5f73c04712e390851c4b..ca7923d9b79c87de7382bff2757ea496184eb556 100644 --- a/lib/subliminal/providers/podnapisi.py +++ b/lib/subliminal/providers/podnapisi.py @@ -76,7 +76,6 @@ class PodnapisiSubtitle(Subtitle): class PodnapisiProvider(Provider): languages = ({Language('por', 'BR'), Language('srp', script='Latn')} | {Language.fromalpha2(l) for l in language_converters['alpha2'].codes}) - video_types = (Episode, Movie) server_url = 'http://podnapisi.net/subtitles/' def initialize(self): diff --git a/lib/subliminal/providers/subscenter.py b/lib/subliminal/providers/subscenter.py new file mode 100644 index 0000000000000000000000000000000000000000..27e8ae56d083bb35570edb1eb24126c0d10f3f18 --- /dev/null +++ b/lib/subliminal/providers/subscenter.py @@ -0,0 +1,228 @@ +# -*- coding: utf-8 -*- +import bisect +import io +import json +import logging +import zipfile + +from babelfish import Language +from guessit import guess_episode_info, guess_movie_info +from requests import Session + +from . import ParserBeautifulSoup, Provider, get_version +from .. import __version__ +from ..cache import SHOW_EXPIRATION_TIME, region +from ..exceptions import AuthenticationError, ConfigurationError, ProviderError +from ..subtitle import Subtitle, fix_line_ending, guess_matches, sanitized_string_equal +from ..video import Episode, Movie + +logger = logging.getLogger(__name__) + + +class SubsCenterSubtitle(Subtitle): + provider_name = 'subscenter' + + def __init__(self, language, hearing_impaired, page_link, series, season, episode, title, subtitle_id, subtitle_key, + downloaded, releases): + super(SubsCenterSubtitle, self).__init__(language, hearing_impaired, page_link) + self.series = series + self.season = season + self.episode = episode + self.title = title + self.subtitle_id = subtitle_id + self.subtitle_key = subtitle_key + self.downloaded = downloaded + self.releases = releases + + @property + def id(self): + return str(self.subtitle_id) + + def get_matches(self, video, hearing_impaired=False): + matches = super(SubsCenterSubtitle, self).get_matches(video, hearing_impaired=hearing_impaired) + + # episode + if isinstance(video, Episode): + # series + if video.series and sanitized_string_equal(self.series, video.series): + matches.add('series') + # season + if video.season and self.season == video.season: + matches.add('season') + # episode + if video.episode and self.episode == video.episode: + matches.add('episode') + # guess + for release in self.releases: + matches |= guess_matches(video, guess_episode_info(release + '.mkv')) + # movie + elif isinstance(video, Movie): + # guess + for release in self.releases: + matches |= guess_matches(video, guess_movie_info(release + '.mkv')) + + # title + if video.title and sanitized_string_equal(self.title, video.title): + matches.add('title') + + return matches + + +class SubsCenterProvider(Provider): + languages = {Language.fromalpha2(l) for l in ['he']} + server = 'http://subscenter.cinemast.com/he/' + + def __init__(self, username=None, password=None): + if username is not None and password is None or username is None and password is not None: + raise ConfigurationError('Username and password must be specified') + + self.username = username + self.password = password + self.logged_in = False + + def initialize(self): + self.session = Session() + self.session.headers = {'User-Agent': 'Subliminal/%s' % get_version(__version__)} + + # login + if self.username is not None and self.password is not None: + logger.debug('Logging in') + url = self.server + 'subscenter/accounts/login/' + + # retrieve CSRF token + self.session.get(url) + csrf_token = self.session.cookies['csrftoken'] + + # actual login + data = {'username': self.username, 'password': self.password, 'csrfmiddlewaretoken': csrf_token} + r = self.session.post(url, data, allow_redirects=False, timeout=10) + + if r.status_code != 302: + raise AuthenticationError(self.username) + + logger.info('Logged in') + self.logged_in = True + + def terminate(self): + # logout + if self.logged_in: + logger.info('Logging out') + r = self.session.get(self.server + 'subscenter/accounts/logout/', timeout=10) + r.raise_for_status() + logger.info('Logged out') + self.logged_in = False + + self.session.close() + + @region.cache_on_arguments(expiration_time=SHOW_EXPIRATION_TIME) + def _search_url_title(self, title, kind): + """Search the URL title for the given `title`. + + :param str title: title to search for. + :param str kind: kind of the title, ``movie`` or ``series``. + :return: the URL version of the title. + :rtype: str or None + + """ + # make the search + logger.info('Searching title name for %r', title) + r = self.session.get(self.server + 'subtitle/search/', params={'q': title}, allow_redirects=False, timeout=10) + r.raise_for_status() + + # if redirected, get the url title from the Location header + if r.is_redirect: + parts = r.headers['Location'].split('/') + + # check kind + if parts[-3] == kind: + return parts[-2] + + return None + + # otherwise, get the first valid suggestion + soup = ParserBeautifulSoup(r.content, ['lxml', 'html.parser']) + suggestions = soup.select('#processes div.generalWindowTop a') + logger.debug('Found %d suggestions', len(suggestions)) + for suggestion in suggestions: + parts = suggestion.attrs['href'].split('/') + + # check kind + if parts[-3] == kind: + return parts[-2] + + def query(self, series=None, season=None, episode=None, title=None): + # set the correct parameters depending on the kind + if series and season and episode: + url_series = self._search_url_title(series, 'series') + url = self.server + 'cinemast/data/series/sb/{}/{}/{}/'.format(url_series, season, episode) + page_link = self.server + 'subtitle/series/{}/{}/{}/'.format(url_series, season, episode) + elif title: + url_title = self._search_url_title(title, 'movie') + url = self.server + 'cinemast/data/movie/sb/{}/'.format(url_title) + page_link = self.server + 'subtitle/movie/{}/'.format(url_title) + else: + raise ValueError('One or more parameters are missing') + + # get the list of subtitles + logger.debug('Getting the list of subtitles') + r = self.session.get(url) + r.raise_for_status() + results = json.loads(r.text) + + # loop over results + subtitles = {} + for language_code, language_data in results.items(): + for quality_data in language_data.values(): + for quality, subtitles_data in quality_data.items(): + for subtitle_item in subtitles_data.values(): + # read the item + language = Language.fromalpha2(language_code) + hearing_impaired = bool(subtitle_item['hearing_impaired']) + subtitle_id = subtitle_item['id'] + subtitle_key = subtitle_item['key'] + downloaded = subtitle_item['downloaded'] + release = subtitle_item['subtitle_version'] + + # add the release and increment downloaded count if we already have the subtitle + if subtitle_id in subtitles: + logger.debug('Found additional release %r for subtitle %d', release, subtitle_id) + bisect.insort_left(subtitles[subtitle_id].releases, release) # deterministic order + subtitles[subtitle_id].downloaded += downloaded + continue + + # otherwise create it + subtitle = SubsCenterSubtitle(language, hearing_impaired, page_link, series, season, episode, + title, subtitle_id, subtitle_key, downloaded, [release]) + logger.debug('Found subtitle %r', subtitle) + subtitles[subtitle_id] = subtitle + + return subtitles.values() + + def list_subtitles(self, video, languages): + series = None + season = None + episode = None + title = video.title + + if isinstance(video, Episode): + series = video.series + season = video.season + episode = video.episode + + return [s for s in self.query(series, season, episode, title) if s.language in languages] + + def download_subtitle(self, subtitle): + # download + url = self.server + 'subtitle/download/{}/{}/'.format(subtitle.language.alpha2, subtitle.subtitle_id) + params = {'v': subtitle.releases[0], 'key': subtitle.subtitle_key} + r = self.session.get(url, params=params, headers={'Referer': subtitle.page_link}, timeout=10) + r.raise_for_status() + + # open the zip + with zipfile.ZipFile(io.BytesIO(r.content)) as zf: + # remove some filenames from the namelist + namelist = [n for n in zf.namelist() if not n.endswith('.txt')] + if len(namelist) > 1: + raise ProviderError('More than one file to unzip') + + subtitle.content = fix_line_ending(zf.read(namelist[0])) diff --git a/lib/subliminal/providers/tvsubtitles.py b/lib/subliminal/providers/tvsubtitles.py index 6df1ad22705e9812a7406153b9b90a7eae7b61aa..8c4433fc966bd5b0b54eba9b99dfab2291e6db9b 100644 --- a/lib/subliminal/providers/tvsubtitles.py +++ b/lib/subliminal/providers/tvsubtitles.py @@ -84,7 +84,7 @@ class TVsubtitlesProvider(Provider): def search_show_id(self, series, year=None): """Search the show id from the `series` and `year`. - :param string series: series of the episode. + :param str series: series of the episode. :param year: year of the series, if any. :type year: int or None :return: the show id, if any. diff --git a/lib/subliminal/subtitle.py b/lib/subliminal/subtitle.py index 5694af7419ae98461df3e46c237430d1b472b8cb..d764b8901db3c0cfa20b1a0df2baa8b3883cb29d 100644 --- a/lib/subliminal/subtitle.py +++ b/lib/subliminal/subtitle.py @@ -22,6 +22,8 @@ class Subtitle(object): :param bool hearing_impaired: whether or not the subtitle is hearing impaired. :param page_link: URL of the web page from which the subtitle can be downloaded. :type page_link: str + :param encoding: Text encoding of the subtitle + :type encoding: str """ #: Name of the provider that returns that class of subtitle @@ -254,9 +256,7 @@ def sanitized_string_equal(string1, string2): :rtype: bool """ - valid_pattern = '[^a-zA-Z0-9]' - - return sanitize_string(string1).lower() == sanitize_string(string2).lower() + return string1 and string2 and sanitize_string(string1).lower() == sanitize_string(string2).lower() def guess_matches(video, guess, partial=False): diff --git a/sickbeard/common.py b/sickbeard/common.py index bea39f1475958b9d62123168d078ea9be7a9e350..e7d34ee3737b42df0db71a5a9a7d5425ff81de68 100644 --- a/sickbeard/common.py +++ b/sickbeard/common.py @@ -225,17 +225,17 @@ class Quality(object): def splitQuality(quality): if quality is None: quality = Quality.NONE - any_qualities = [] - best_qualities = [] + allowed_qualities = [] + prefferred_qualities = [] for cur_qual in Quality.qualityStrings: if cur_qual is None: cur_qual = Quality.NONE if cur_qual & quality: - any_qualities.append(cur_qual) + allowed_qualities.append(cur_qual) if cur_qual << 16 & quality: - best_qualities.append(cur_qual) + prefferred_qualities.append(cur_qual) - return sorted(any_qualities), sorted(best_qualities) + return sorted(allowed_qualities), sorted(prefferred_qualities) @staticmethod def nameQuality(name, anime=False): diff --git a/sickbeard/postProcessor.py b/sickbeard/postProcessor.py index de35e84fb695967d506eeeb33d9c20c788c4b3f2..a1e8275b8ea73c3e8ea03173aecef23013aae45e 100644 --- a/sickbeard/postProcessor.py +++ b/sickbeard/postProcessor.py @@ -516,7 +516,8 @@ class PostProcessor(object): to_return = (show, season, [], quality, version) qual_str = common.Quality.qualityStrings[quality] if quality is not None else quality - self._log("Found result in history for %s - Season: %s - Quality: %s - Version: %s" % (show.name, season, qual_str, version), logger.DEBUG) + self._log("Found result in history for %s - Season: %s - Quality: %s - Version: %s" + % (show.name if show else "UNDEFINED", season, qual_str, version), logger.DEBUG) return to_return @@ -681,7 +682,14 @@ class PostProcessor(object): self._log( u"Looks like this is an air-by-date or sports show, attempting to convert the date to season/episode", logger.DEBUG) - airdate = episodes[0].toordinal() + + try: + airdate = episodes[0].toordinal() + except AttributeError: + self._log(u"Could not convert to a valid airdate: %s" % episodes[0], logger.DEBUG) + episodes = [] + continue + myDB = db.DBConnection() # Ignore season 0 when searching for episode(Conflict between special and regular episode, same air date) sql_result = myDB.select( diff --git a/sickbeard/providers/cpasbien.py b/sickbeard/providers/cpasbien.py index dd23b79963057ae7e33687172432d7dce62b1f58..83ed1e9349a8fb4cdef3740266ff228e2316b337 100644 --- a/sickbeard/providers/cpasbien.py +++ b/sickbeard/providers/cpasbien.py @@ -18,10 +18,10 @@ # along with Sick Beard. If not, see <http://www.gnu.org/licenses/>. import traceback -import re from sickbeard import logger from sickbeard import tvcache +from sickrage.helper.common import try_int from sickbeard.bs4_parser import BS4Parser from sickrage.providers.torrent.TorrentProvider import TorrentProvider @@ -42,7 +42,7 @@ class CpasbienProvider(TorrentProvider): self.cache = CpasbienCache(self) - def search(self, search_params, age=0, ep_obj=None): + def search(self, search_params, age=0, ep_obj=None): # pylint: disable=too-many-locals, too-many-statements, too-many-branches results = [] items = {'Season': [], 'Episode': [], 'RSS': []} @@ -53,10 +53,9 @@ class CpasbienProvider(TorrentProvider): if mode != 'RSS': logger.log(u"Search string: %s " % search_string, logger.DEBUG) - if mode != 'RSS': - searchURL = self.url + '/recherche/' + search_string.replace('.', '-') + '.html' + searchURL = self.url + '/recherche/' + search_string.replace('.', '-').replace(' ', '-') + '.html' else: - searchURL = self.url + '/view_cat.php?categorie=series' + searchURL = self.url + '/view_cat.php?categorie=series&trie=date-d' logger.log(u"Search URL: %s" % searchURL, logger.DEBUG) data = self.get_url(searchURL) @@ -66,35 +65,25 @@ class CpasbienProvider(TorrentProvider): try: with BS4Parser(data, 'html5lib') as html: - lin = erlin = 0 - resultdiv = [] - while erlin == 0: - try: - classlin = 'ligne' + str(lin) - resultlin = html.findAll(attrs={'class': [classlin]}) - if resultlin: - for ele in resultlin: - resultdiv.append(ele) - lin += 1 - else: - erlin = 1 - except Exception: - erlin = 1 - - for torrent in resultdiv: + line = 0 + torrents = [] + while True: + resultlin = html.findAll(class_='ligne%i' % line) + if not resultlin: + break + + torrents += resultlin + line += 1 + + for torrent in torrents: try: - title = torrent.findAll(attrs={'class': ["titre"]})[0].text.replace("HDTV", "HDTV x264-CPasBien") - detail_url = torrent.find("a")['href'] - tmp = detail_url.split('/')[-1].replace('.html', '.torrent') + title = torrent.find(class_="titre").get_text(strip=True).replace("HDTV", "HDTV x264-CPasBien") + tmp = torrent.find("a")['href'].split('/')[-1].replace('.html', '.torrent').strip() download_url = (self.url + '/telechargement/%s' % tmp) - torrent_size = (str(torrent.findAll(attrs={'class': ["poid"]})[0].text).rstrip(' ')).rstrip() - size = -1 - if re.match(r"\d+([,\.]\d+)?\s*[KkMmGgTt]?[Oo]", torrent_size): - size = self._convertSize(torrent_size.rstrip()) - seeders = torrent.findAll(attrs={'class': ["seed_ok"]})[0].text - leechers = torrent.findAll(attrs={'class': ["down"]})[0].text - - except (AttributeError, TypeError): + size = self._convertSize(torrent.find(class_="poid").get_text(strip=True)) + seeders = try_int(torrent.find(class_="up").get_text(strip=True)) + leechers = try_int(torrent.find(class_="down").get_text(strip=True)) + except (AttributeError, TypeError, KeyError, IndexError): continue if not all([title, download_url]): @@ -125,7 +114,8 @@ class CpasbienProvider(TorrentProvider): def seed_ratio(self): return self.ratio - def _convertSize(self, sizeString): + @staticmethod + def _convertSize(sizeString): size = sizeString[:-2].strip() modifier = sizeString[-2:].upper() try: @@ -138,8 +128,11 @@ class CpasbienProvider(TorrentProvider): size *= 1024 ** 3 elif modifier in 'TO': size *= 1024 ** 4 + else: + raise except Exception: size = -1 + return long(size) diff --git a/sickbeard/providers/extratorrent.py b/sickbeard/providers/extratorrent.py index 9445bdcdcee39cf2348035ab99a6d1f2f5b60bdf..73bade79050e843b7be96989e2b5ed81a846e2fc 100644 --- a/sickbeard/providers/extratorrent.py +++ b/sickbeard/providers/extratorrent.py @@ -19,6 +19,7 @@ import re import traceback +import sickbeard from sickbeard import logger from sickbeard import tvcache from sickbeard.common import USER_AGENT @@ -27,7 +28,7 @@ from sickbeard.bs4_parser import BS4Parser from sickrage.providers.torrent.TorrentProvider import TorrentProvider -class ExtraTorrentProvider(TorrentProvider): +class ExtraTorrentProvider(TorrentProvider): # pylint: disable=too-many-instance-attributes def __init__(self): TorrentProvider.__init__(self, "ExtraTorrent") @@ -48,7 +49,7 @@ class ExtraTorrentProvider(TorrentProvider): self.headers.update({'User-Agent': USER_AGENT}) self.search_params = {'cid': 8} - def search(self, search_strings, age=0, ep_obj=None): + def search(self, search_strings, age=0, ep_obj=None): # pylint: disable=too-many-locals, too-many-branches results = [] items = {'Season': [], 'Episode': [], 'RSS': []} @@ -59,29 +60,32 @@ class ExtraTorrentProvider(TorrentProvider): if mode != 'RSS': logger.log(u"Search string: %s " % search_string, logger.DEBUG) - try: - self.search_params.update({'type': ('search', 'rss')[mode == 'RSS'], 'search': search_string}) - - url = self.urls['rss'] if not self.custom_url else self.urls['rss'].replace(self.urls['index'], self.custom_url) - - data = self.get_url(url, params=self.search_params) - if not data: - logger.log(u"No data returned from provider", logger.DEBUG) - continue - - if not data.startswith('<?xml'): - logger.log(u'Expected xml but got something else, is your mirror failing?', logger.INFO) - continue - - with BS4Parser(data, 'html5lib') as parser: - for item in parser.findAll('item'): - title = re.sub(r'^<!\[CDATA\[|\]\]>$', '', item.find('title').text) - # info_hash = item.get('info_hash', '') - size = try_int(item.find('size').text, -1) - seeders = try_int(item.find('seeders').text) - leechers = try_int(item.find('leechers').text) - enclosure = item.find('enclosure') - download_url = enclosure['url'] if enclosure else self._magnet_from_details(item.find('link').text) + self.search_params.update({'type': ('search', 'rss')[mode == 'RSS'], 'search': search_string}) + url = self.urls['rss'] if not self.custom_url else self.urls['rss'].replace(self.urls['index'], self.custom_url) + data = self.get_url(url, params=self.search_params) + if not data: + logger.log(u"No data returned from provider", logger.DEBUG) + continue + + if not data.startswith('<?xml'): + logger.log(u'Expected xml but got something else, is your mirror failing?', logger.INFO) + continue + + with BS4Parser(data, 'html5lib') as parser: + for item in parser.findAll('item'): + try: + title = re.sub(r'^<!\[CDATA\[|\]\]>$', '', item.find('title').get_text(strip=True)) + size = try_int(item.find('size').get_text(strip=True), -1) if item.find('size') else -1 + seeders = try_int(item.find('seeders').get_text(strip=True)) if item.find('seeders') else 0 + leechers = try_int(item.find('leechers').get_text(strip=True)) if item.find('leechers') else 0 + + if sickbeard.TORRENT_METHOD == 'blackhole': + enclosure = item.find('enclosure') # Backlog doesnt have enclosure + download_url = enclosure['url'] if enclosure else item.find('link').next.strip() + download_url = re.sub(r'(.*)/torrent/(.*).html', r'\1/download/\2.torrent', download_url) + else: + info_hash = item.find('info_hash').get_text(strip=True) + download_url = "magnet:?xt=urn:btih:" + info_hash + "&dn=" + title + self._custom_trackers if not all([title, download_url]): continue @@ -98,8 +102,8 @@ class ExtraTorrentProvider(TorrentProvider): items[mode].append(item) - except (AttributeError, TypeError, KeyError, ValueError): - logger.log(u"Failed parsing provider. Traceback: %r" % traceback.format_exc(), logger.WARNING) + except (AttributeError, TypeError, KeyError, ValueError): + logger.log(u"Failed parsing provider. Traceback: %r" % traceback.format_exc(), logger.WARNING) # For each search mode sort all the items by seeders if available items[mode].sort(key=lambda tup: tup[3], reverse=True) @@ -108,17 +112,6 @@ class ExtraTorrentProvider(TorrentProvider): return results - def _magnet_from_details(self, link): - details = self.get_url(link) - if not details: - return '' - - match = re.search(r'href="(magnet.*?)"', details) - if not match: - return '' - - return match.group(1) - def seed_ratio(self): return self.ratio diff --git a/sickbeard/providers/limetorrents.py b/sickbeard/providers/limetorrents.py index 048bf6913501fa11da2dd4dfe665613f25a6a23b..6a73e2d846a531f689098b9b59a8e9c465088dcf 100644 --- a/sickbeard/providers/limetorrents.py +++ b/sickbeard/providers/limetorrents.py @@ -90,13 +90,13 @@ class LimeTorrentsProvider(TorrentProvider): # pylint: disable=too-many-instance # Category: <a href="http://www.limetorrents.cc/browse-torrents/TV-shows/">TV shows</a><br /> Seeds: 1<br />Leechers: 0<br />Size: 7.71 GB<br /><br /><a href="http://www.limetorrents.cc/Owen-Hart-of-Gold-Djon91-torrent-7180661.html">More @ limetorrents.cc</a><br /> # ]]> description = item.find('description') - seeders = description.find_all('br')[0].next_sibling.strip().lstrip('Seeds: ') - leechers = description.find_all('br')[1].next_sibling.strip().lstrip('Leechers: ') + seeders = try_int(description.find_all('br')[0].next_sibling.strip().lstrip('Seeds: ')) + leechers = try_int(description.find_all('br')[1].next_sibling.strip().lstrip('Leechers: ')) else: #<description>Seeds: 6982 , Leechers 734</description> description = item.find('description').text.partition(',') - seeders = description[0].lstrip('Seeds: ').strip() - leechers = description[2].lstrip('Leechers ').strip() + seeders = try_int(description[0].lstrip('Seeds: ').strip()) + leechers = try_int(description[2].lstrip('Leechers ').strip()) size = try_int(item.find('size').text, -1) except (AttributeError, TypeError, KeyError, ValueError): diff --git a/sickbeard/providers/newznab.py b/sickbeard/providers/newznab.py index 723bdffaa5cd0cba1ac8fa51760a6237969388ab..9c458647dfa999ed6a58f5543b19cb66ed3297a8 100644 --- a/sickbeard/providers/newznab.py +++ b/sickbeard/providers/newznab.py @@ -38,7 +38,7 @@ from sickbeard.common import Quality from sickrage.helper.encoding import ek, ss from sickrage.show.Show import Show from sickrage.helper.common import try_int -from sickbeard.common import USER_AGENT +# from sickbeard.common import USER_AGENT from sickrage.providers.nzb.NZBProvider import NZBProvider @@ -53,7 +53,7 @@ class NewznabProvider(NZBProvider): NZBProvider.__init__(self, name) - self.headers.update({'User-Agent': USER_AGENT}) + # self.headers.update({'User-Agent': USER_AGENT}) self.urls = {'base_url': url} self.url = self.urls['base_url'] @@ -173,9 +173,10 @@ class NewznabProvider(NZBProvider): def _get_default_providers(): # name|url|key|catIDs|enabled|search_mode|search_fallback|enable_daily|enable_backlog return 'NZB.Cat|https://nzb.cat/||5030,5040,5010|0|eponly|1|1|1!!!' + \ - 'NZBGeek|https://api.nzbgeek.info/||5030,5040|0|eponly|0|0|0!!!' + \ - 'NZBs.org|https://nzbs.org/||5030,5040|0|eponly|0|0|0!!!' + \ - 'Usenet-Crawler|https://www.usenet-crawler.com/||5030,5040|0|eponly|0|0|0' + 'NZBGeek|https://api.nzbgeek.info/||5030,5040|0|eponly|0|0|0!!!' + \ + 'NZBs.org|https://nzbs.org/||5030,5040|0|eponly|0|0|0!!!' + \ + 'Usenet-Crawler|https://www.usenet-crawler.com/||5030,5040|0|eponly|0|0|0!!!' + \ + 'DOGnzb|https://api.dognzb.cr/||5030,5040,5060,5070|0|eponly|0|1|1' def _get_season_search_strings(self, ep_obj): """ diff --git a/sickbeard/providers/torrentz.py b/sickbeard/providers/torrentz.py index 817c6bf933ca8c3de3da53fbc88013abb881ce4e..3916ccdc0a4ddc8febdb45df5b5f78c8caca946f 100644 --- a/sickbeard/providers/torrentz.py +++ b/sickbeard/providers/torrentz.py @@ -20,7 +20,6 @@ import re import traceback from six.moves import urllib - from sickbeard import logger from sickbeard import tvcache from sickbeard.common import USER_AGENT @@ -63,14 +62,13 @@ class TORRENTZProvider(TorrentProvider): if mode != 'RSS': search_url += '?q=' + urllib.parse.quote_plus(search_string) - logger.log(search_url) data = self.get_url(search_url) if not data: - logger.log(u'Seems to be down right now!') + logger.log(u"No data returned from provider", logger.DEBUG) continue if not data.startswith("<?xml"): - logger.log(u'Wrong data returned from: ' + search_url, logger.DEBUG) + logger.log(u"Expected xml but got something else, is your mirror failing?", logger.INFO) continue try: @@ -98,7 +96,7 @@ class TORRENTZProvider(TorrentProvider): items[mode].append((title, download_url, size, seeders, leechers)) except (AttributeError, TypeError, KeyError, ValueError): - logger.log(u"Failed parsing provider. Traceback: %r" % traceback.format_exc(), logger.WARNING) + logger.log(u"Failed parsing provider. Traceback: %r" % traceback.format_exc(), logger.ERROR) # For each search mode sort all the items by seeders if available items[mode].sort(key=lambda tup: tup[3], reverse=True) diff --git a/sickbeard/subtitles.py b/sickbeard/subtitles.py index d84b0b1a9ed6cc28302af32d2850531cfed88471..7b20b57330223bc0ed47829f0ca785c6e0539bc9 100644 --- a/sickbeard/subtitles.py +++ b/sickbeard/subtitles.py @@ -50,6 +50,7 @@ ENTRY_POINTS = { 'napiprojekt = subliminal.providers.napiprojekt:NapiProjektProvider', 'opensubtitles = subliminal.providers.opensubtitles:OpenSubtitlesProvider', 'podnapisi = subliminal.providers.podnapisi:PodnapisiProvider', + 'subscenter = subliminal.providers.subscenter:SubsCenterProvider', 'thesubdb = subliminal.providers.thesubdb:TheSubDBProvider', 'tvsubtitles = subliminal.providers.tvsubtitles:TVsubtitlesProvider' ], @@ -74,6 +75,7 @@ PROVIDER_URLS = { 'napiprojekt': 'http://www.napiprojekt.pl', 'opensubtitles': 'http://www.opensubtitles.org', 'podnapisi': 'http://www.podnapisi.net', + 'subscenter': 'http://www.subscenter.org', 'thesubdb': 'http://www.thesubdb.com', 'tvsubtitles': 'http://www.tvsubtitles.net' } @@ -196,7 +198,7 @@ def download_subtitles(subtitles_info): # pylint: disable=too-many-locals, too- return existing_subtitles, None for subtitle in subtitles_list: - matches = subtitle.get_matches(video, hearing_impaired=False) + matches = subtitle.get_matches(video, hearing_impaired=sickbeard.SUBTITLES_HEARING_IMPAIRED) score = subliminal.subtitle.compute_score(matches, video) logger.log(u"[%s] Subtitle score for %s is: %s (min=%s)" % (subtitle.provider_name, subtitle.id, score, user_score), logger.DEBUG) @@ -372,7 +374,7 @@ class SubtitlesFinder(object): only_one=not sickbeard.SUBTITLES_MULTI) for subtitle in subtitles_list: - matches = subtitle.get_matches(video, hearing_impaired=False) + matches = subtitle.get_matches(video, hearing_impaired=sickbeard.SUBTITLES_HEARING_IMPAIRED) score = subliminal.subtitle.compute_score(matches, video) logger.log(u"[%s] Subtitle score for %s is: %s (min=%s)" % (subtitle.provider_name, subtitle.id, score, user_score), logger.DEBUG) diff --git a/sickbeard/tv.py b/sickbeard/tv.py index eed757342723fc6d7c330def5cf4685029fb58e7..2146c5471f586e1c0399c077d580b339c7b5302a 100644 --- a/sickbeard/tv.py +++ b/sickbeard/tv.py @@ -149,24 +149,15 @@ class TVShow(object): @property def is_anime(self): - if int(self.anime) > 0: - return True - else: - return False + return int(self.anime) > 0 @property def is_sports(self): - if int(self.sports) > 0: - return True - else: - return False + return int(self.sports) > 0 @property def is_scene(self): - if int(self.scene) > 0: - return True - else: - return False + return int(self.scene) > 0 @property def network_logo_name(self): @@ -174,13 +165,10 @@ class TVShow(object): def _getLocation(self): # no dir check needed if missing show dirs are created during post-processing - if sickbeard.CREATE_MISSING_SHOW_DIRS: + if sickbeard.CREATE_MISSING_SHOW_DIRS or ek(os.path.isdir, self._location): return self._location - if ek(os.path.isdir, self._location): - return self._location - else: - raise ShowDirectoryNotFoundException("Show folder doesn't exist, you shouldn't be using it") + raise ShowDirectoryNotFoundException("Show folder doesn't exist, you shouldn't be using it") def _setLocation(self, newLocation): logger.log(u"Setter sets location to " + newLocation, logger.DEBUG) @@ -1212,8 +1200,6 @@ class TVShow(object): def wantEpisode(self, season, episode, quality, manualSearch=False, downCurQuality=False): - logger.log(u"Checking if found episode %s S%02dE%02d is wanted at quality %s" % (self.name, season or 0, episode or 0, Quality.qualityStrings[quality]), logger.DEBUG) - # if the quality isn't one we want under any circumstances then just say no allowed_qualities, preferred_qualities = Quality.splitQuality(self.quality) logger.log(u"Any,Best = [ %s ] [ %s ] Found = [ %s ]" % @@ -1222,8 +1208,8 @@ class TVShow(object): self.qualitiesToString([quality])), logger.DEBUG) if quality not in allowed_qualities + preferred_qualities or quality is UNKNOWN: - logger.log(u"Skipping %s (S%02dE%02d, %s): Don't want this quality, ignoring found episode" % - (self.name, season or 0, episode or 0, Quality.qualityStrings[quality]), logger.INFO) + logger.log(u"Don't want this quality, ignoring found result for %s S%02dE%02d with quality %s" % + (self.name, season or 0, episode or 0, Quality.qualityStrings[quality]), logger.DEBUG) return False myDB = db.DBConnection() @@ -1231,72 +1217,76 @@ class TVShow(object): [self.indexerid, season, episode]) if not sqlResults or not len(sqlResults): - logger.log(u"Skipping %s (S%02dE%02d, %s): Unable to find a matching episode in database, ignoring found episode" % - (self.name, season or 0, episode or 0, Quality.qualityStrings[quality]), logger.INFO) + logger.log(u"Unable to find a matching episode in database, ignoring found result for %s S%02dE%02d with quality %s" % + (self.name, season or 0, episode or 0, Quality.qualityStrings[quality]), logger.DEBUG) return False epStatus = int(sqlResults[0]["status"]) epStatus_text = statusStrings[epStatus] - logger.log(u"Existing episode status: " + str(epStatus) + " (" + epStatus_text + ")", logger.DEBUG) - # if we know we don't want it then just say no if epStatus in Quality.ARCHIVED + [UNAIRED, SKIPPED, IGNORED] and not manualSearch: - logger.log(u"Skipping %s (S%02dE%02d, %s): Existing episode status is '%s', ignoring found episode" % - (self.name, season or 0, episode or 0, Quality.qualityStrings[quality], epStatus_text), logger.INFO) + logger.log(u"Existing episode status is '%s', ignoring found result for %s S%02dE%02d with quality %s" % + (epStatus_text, self.name, season or 0, episode or 0, Quality.qualityStrings[quality]), logger.DEBUG) return False curStatus, curQuality = Quality.splitCompositeStatus(epStatus) # if it's one of these then we want it as long as it's in our allowed initial qualities if epStatus in (WANTED, SKIPPED, UNKNOWN): - logger.log(u"Existing episode status is '%s', getting found episode" % epStatus_text, logger.DEBUG) + logger.log(u"Existing episode status is '%s', getting found result for %s S%02dE%02d with quality %s" % + (epStatus_text, self.name, season or 0, episode or 0, Quality.qualityStrings[quality]), logger.DEBUG) return True elif manualSearch: if (downCurQuality and quality >= curQuality) or (not downCurQuality and quality > curQuality): logger.log( - u"Usually ignoring found episode, but forced search allows the quality, getting found episode", - logger.DEBUG) + u"Usually ignoring found result, but forced search allows the quality, getting found result for %s S%02dE%02d with quality %s" % + (self.name, season or 0, episode or 0, Quality.qualityStrings[quality]), logger.DEBUG) return True # if we are re-downloading then we only want it if it's in our preferred_qualities list and better than what we have, or we only have one bestQuality and we do not have that quality yet if epStatus in Quality.DOWNLOADED + Quality.SNATCHED + Quality.SNATCHED_PROPER and quality in preferred_qualities and (quality > curQuality or curQuality not in preferred_qualities): - logger.log(u"Episode already exists but the found episode quality is wanted more, getting found episode", - logger.DEBUG) + logger.log(u"Episode already exists with quality %s but the found result quality %s is wanted more, getting found result for %s S%02dE%02d" % + (Quality.qualityStrings[curQuality], Quality.qualityStrings[quality], self.name, season or 0, episode or 0), logger.DEBUG) return True elif curQuality == Quality.UNKNOWN and manualSearch: - logger.log(u"Episode already exists but quality is Unknown, getting found episode", - logger.DEBUG) + logger.log(u"Episode already exists but quality is Unknown, getting found result for %s S%02dE%02d with quality %s" % + (self.name, season or 0, episode or 0, Quality.qualityStrings[quality]), logger.DEBUG) return True else: - logger.log(u"Episode already exists and the found episode has same/lower quality, ignoring found episode", - logger.DEBUG) - - logger.log(u"Skipping %s (S%02dE%02d, %s): None of the conditions were met, ignoring found episode" % - (self.name, season or 0, episode or 0, Quality.qualityStrings[quality]), logger.INFO) + logger.log(u"Episode already exists with quality %s and the found result has same/lower quality, ignoring found result for %s S%02dE%02d with quality %s" % + (Quality.qualityStrings[curQuality], self.name, season or 0, episode or 0, Quality.qualityStrings[quality]), logger.DEBUG) return False def getOverview(self, epStatus): + """ + Get the Overview status from the Episode status + + :param epStatus: an Episode status + :return: an Overview status + """ - if epStatus == WANTED: + ep_status = try_int(epStatus) or UNKNOWN + + if ep_status == WANTED: return Overview.WANTED - elif epStatus in (UNAIRED, UNKNOWN): + elif ep_status in (UNAIRED, UNKNOWN): return Overview.UNAIRED - elif epStatus in (SKIPPED, IGNORED): + elif ep_status in (SKIPPED, IGNORED): return Overview.SKIPPED - elif epStatus in Quality.ARCHIVED: + elif ep_status in Quality.ARCHIVED: return Overview.GOOD - elif epStatus in Quality.FAILED: + elif ep_status in Quality.FAILED: return Overview.WANTED - elif epStatus in Quality.SNATCHED: + elif ep_status in Quality.SNATCHED: return Overview.SNATCHED - elif epStatus in Quality.SNATCHED_PROPER: + elif ep_status in Quality.SNATCHED_PROPER: return Overview.SNATCHED_PROPER - elif epStatus in Quality.SNATCHED_BEST: + elif ep_status in Quality.SNATCHED_BEST: return Overview.SNATCHED_BEST - elif epStatus in Quality.DOWNLOADED: - allowed_qualities, preferred_qualities = Quality.splitQuality(self.quality) # @UnusedVariable - epStatus, cur_quality = Quality.splitCompositeStatus(epStatus) + elif ep_status in Quality.DOWNLOADED: + allowed_qualities, preferred_qualities = Quality.splitQuality(self.quality) + ep_status, cur_quality = Quality.splitCompositeStatus(ep_status) if cur_quality not in allowed_qualities + preferred_qualities: if cur_quality != Quality.UNKNOWN and ( @@ -1310,6 +1300,9 @@ class TVShow(object): return Overview.QUAL else: return Overview.GOOD + else: + logger.log(u'Could not parse episode status into a valid overview status: %s' % epStatus, logger.ERROR) + def __getstate__(self): d = dict(self.__dict__) diff --git a/sickbeard/tvcache.py b/sickbeard/tvcache.py index 62585bbfa6ee7e342c5f75d2c25f5a63f8aa4dc5..17b8c6e799d889ab24a2e01aa0c72923fbd4d8ae 100644 --- a/sickbeard/tvcache.py +++ b/sickbeard/tvcache.py @@ -165,7 +165,7 @@ class TVCache(object): title = self._translateTitle(title) url = self._translateLinkURL(url) - logger.log(u"Attempting to add item to cache: " + title, logger.DEBUG) + #logger.log(u"Attempting to add item to cache: " + title, logger.DEBUG) return self._addCacheEntry(title, url) else: @@ -356,7 +356,7 @@ class TVCache(object): # if the show says we want that episode then add it to the list if not showObj.wantEpisode(curSeason, curEp, curQuality, manualSearch, downCurQuality): - logger.log(u"Skipping " + curResult["name"], logger.DEBUG) + logger.log(u"Ignoring " + curResult["name"], logger.DEBUG) continue epObj = showObj.getEpisode(curSeason, curEp) diff --git a/sickbeard/webapi.py b/sickbeard/webapi.py index 81d1a2ab028e0a10e6c0b54be871bd9b35a81a9f..0403c3234b25dcb2f958007337621f0b4feac1ae 100644 --- a/sickbeard/webapi.py +++ b/sickbeard/webapi.py @@ -1159,7 +1159,7 @@ class CMD_Backlog(ApiCall): for curResult in sql_results: - cur_ep_cat = curShow.getOverview(int(curResult["status"] or -1)) + cur_ep_cat = curShow.getOverview(curResult["status"]) if cur_ep_cat and cur_ep_cat in (Overview.WANTED, Overview.QUAL): show_eps.append(curResult) diff --git a/sickbeard/webserve.py b/sickbeard/webserve.py index 841fca286dc73d8f7cb70d8406327bcfb587d9c8..d160599662f044d4ab976191427316c251697533 100644 --- a/sickbeard/webserve.py +++ b/sickbeard/webserve.py @@ -1278,7 +1278,7 @@ class Home(WebRoot): epCats = {} for curResult in sqlResults: - curEpCat = showObj.getOverview(int(curResult["status"] or -1)) + curEpCat = showObj.getOverview(curResult["status"]) if curEpCat: epCats[str(curResult["season"]) + "x" + str(curResult["episode"])] = curEpCat epCounts[curEpCat] += 1 @@ -1965,7 +1965,8 @@ class Home(WebRoot): 'searchstatus': searchstatus, 'status': statusStrings[searchThread.segment.status], 'quality': self.getQualityClass(searchThread.segment), - 'overview': Overview.overviewStrings[showObj.getOverview(int(searchThread.segment.status or -1))]}) + 'overview': Overview.overviewStrings[showObj.getOverview(searchThread.segment.status)] + }) else: for epObj in searchThread.segment: results.append({'show': epObj.show.indexerid, @@ -1975,7 +1976,8 @@ class Home(WebRoot): 'searchstatus': searchstatus, 'status': statusStrings[epObj.status], 'quality': self.getQualityClass(epObj), - 'overview': Overview.overviewStrings[showObj.getOverview(int(epObj.status or -1))]}) + 'overview': Overview.overviewStrings[showObj.getOverview(epObj.status)] + }) return results @@ -3112,7 +3114,7 @@ class Manage(Home, WebRoot): [curShow.indexerid]) for curResult in sqlResults: - curEpCat = curShow.getOverview(int(curResult["status"] or -1)) + curEpCat = curShow.getOverview(curResult["status"]) if curEpCat: epCats['S%02dE%02d' % (curResult['season'], curResult['episode'])] = curEpCat epCounts[curEpCat] += 1 @@ -3692,7 +3694,7 @@ class Config(WebRoot): def index(self): t = PageTemplate(rh=self, filename="config.mako") - return t.render(submenu=self.ConfigMenu(), title='Configuration', header='Configuration', topmenu="config") + return t.render(submenu=self.ConfigMenu(), title='SickRage Configuration', header='SickRage Configuration', topmenu="config") @route('/config/general(/?.*)')