diff --git a/.pullapprove.yml b/.pullapprove.yml index f4e7046f97..4265923062 100644 --- a/.pullapprove.yml +++ b/.pullapprove.yml @@ -4,40 +4,14 @@ reject_regex: (^Rejected|^Fix it) reset_on_push: false reviewers: - - name: backend-devs + name: devs required: 1 members: - duramato - fernandog - labrys - medariox + - ratoaq2 - p0psicles - - adaur -# # CONDITIONS REQUIRE PRO ACCOUNT -# conditions: -# branches: -# - master -# - beta -# - develop -# files: -# - "*.py" - - - name: gui-devs - required: 1 - members: - - Labrys - OmgImAlexis - - p0psicles -# # CONDITIONS REQUIRE PRO ACCOUNT -# conditions: -# branches: -# - master -# - beta -# - develop -# files: -# - "gui/" - - - name: support - required: 0 - members: - - neoatomic + - adaur diff --git a/gui/slick/images/providers/xspeeds.png b/gui/slick/images/providers/xspeeds.png new file mode 100644 index 0000000000..13ca7fdbf8 Binary files /dev/null and b/gui/slick/images/providers/xspeeds.png differ diff --git a/gui/slick/images/providers/xspeeds_eu.png b/gui/slick/images/providers/xspeeds_eu.png new file mode 100644 index 0000000000..13ca7fdbf8 Binary files /dev/null and b/gui/slick/images/providers/xspeeds_eu.png differ diff --git a/lib/subliminal/__init__.py b/lib/subliminal/__init__.py index 0b94f73b10..187e618b31 100644 --- a/lib/subliminal/__init__.py +++ b/lib/subliminal/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- __title__ = 'subliminal' -__version__ = '2.0.rc1' +__version__ = '2.0.3' __short_version__ = '.'.join(__version__.split('.')[:2]) __author__ = 'Antoine Bertin' __license__ = 'MIT' diff --git a/lib/subliminal/cli.py b/lib/subliminal/cli.py index f6c5425a84..e2a78cf153 100644 --- a/lib/subliminal/cli.py +++ b/lib/subliminal/cli.py @@ -6,6 +6,7 @@ from __future__ import division from collections import defaultdict from datetime import timedelta +import glob import json import logging import os @@ -15,7 +16,7 @@ from babelfish import Error as BabelfishError, Language import click from dogpile.cache.backends.file import AbstractFileLock -from dogpile.core import ReadWriteMutex +from dogpile.util.readwrite_lock import ReadWriteMutex from six.moves import configparser from subliminal import (AsyncProviderPool, Episode, Movie, Video, __version__, check_video, compute_score, get_scores, @@ -266,7 +267,8 @@ def subliminal(ctx, addic7ed, itasa, legendastv, opensubtitles, subscenter, cach def cache(ctx, clear_subliminal): """Cache management.""" if clear_subliminal: - os.remove(os.path.join(ctx.parent.params['cache_dir'], cache_file)) + for file in glob.glob(os.path.join(ctx.parent.params['cache_dir'], cache_file) + '*'): + os.remove(file) click.echo('Subliminal\'s cache cleared.') else: click.echo('Nothing done.') diff --git a/lib/subliminal/providers/itasa.py b/lib/subliminal/providers/itasa.py index 0e828801b8..f4478113ff 100644 --- a/lib/subliminal/providers/itasa.py +++ b/lib/subliminal/providers/itasa.py @@ -2,12 +2,13 @@ import copy import io import logging +import re from babelfish import Language from guessit import guessit try: from lxml import etree -except ImportError: +except ImportError: # pragma: no cover try: import xml.etree.cElementTree as etree except ImportError: @@ -17,7 +18,7 @@ from . import Provider from .. import __version__ -from .. cache import SHOW_EXPIRATION_TIME, region +from .. cache import EPISODE_EXPIRATION_TIME, SHOW_EXPIRATION_TIME, region from .. exceptions import AuthenticationError, ConfigurationError, TooManyRequests from .. subtitle import (Subtitle, fix_line_ending, guess_matches, sanitize) from .. video import Episode @@ -28,18 +29,19 @@ class ItaSASubtitle(Subtitle): provider_name = 'itasa' - def __init__(self, sub_id, series, season, episode, format, full_data, hash=None): + def __init__(self, sub_id, series, season, episode, video_format, year, tvdb_id, full_data): super(ItaSASubtitle, self).__init__(Language('ita')) self.sub_id = sub_id self.series = series self.season = season self.episode = episode - self.format = format + self.format = video_format + self.year = year + self.tvdb_id = tvdb_id self.full_data = full_data - self.hash = hash @property - def id(self): + def id(self): # pragma: no cover return self.sub_id def get_matches(self, video, hearing_impaired=False): @@ -57,13 +59,10 @@ def get_matches(self, video, hearing_impaired=False): # format if video.format and video.format.lower() in self.format.lower(): matches.add('format') - if not video.format and not self.format: - matches.add('format') - # hash - if 'itasa' in video.hashes and self.hash == video.hashes['itasa']: - print('Hash %s' % video.hashes['itasa']) - if 'series' in matches and 'season' in matches and 'episode' in matches: - matches.add('hash') + if video.year and self.year == video.year: + matches.add('year') + if video.series_tvdb_id and self.tvdb_id == video.series_tvdb_id: + matches.add('tvdb_id') # other properties matches |= guess_matches(video, guessit(self.full_data), partial=True) @@ -76,8 +75,6 @@ class ItaSAProvider(Provider): video_types = (Episode,) - required_hash = 'itasa' - server_url = 'https://api.italiansubs.net/api/rest/' apikey = 'd86ad6ec041b334fac1e512174ee04d5' @@ -90,10 +87,12 @@ def __init__(self, username=None, password=None): self.password = password self.logged_in = False self.login_itasa = False + self.session = None + self.auth_code = None def initialize(self): self.session = Session() - self.session.headers = {'User-Agent': 'Subliminal/%s' % __version__} + self.session.headers['User-Agent'] = 'Subliminal/%s' % __version__ # login if self.username is not None and self.password is not None: @@ -110,7 +109,6 @@ def initialize(self): if root.find('status').text == 'fail': raise AuthenticationError(root.find('error/message').text) - # logger.debug('Logged in: \n' + etree.tostring(root)) self.auth_code = root.find('data/user/authcode').text data = { @@ -148,7 +146,7 @@ def _get_show_ids(self): # populate the show ids show_ids = {} for show in root.findall('data/shows/show'): - if show.find('name').text is None: + if show.find('name').text is None: # pragma: no cover continue show_ids[sanitize(show.find('name').text).lower()] = int(show.find('id').text) logger.debug('Found %d show ids', len(show_ids)) @@ -186,14 +184,14 @@ def _search_show_id(self, series): return show_id # Not in the first page of result try next (if any) - next = root.find('data/next') - while next.text is not None: + next_page = root.find('data/next') + while next_page.text is not None: # pragma: no cover - r = self.session.get(next.text, timeout=10) + r = self.session.get(next_page.text, timeout=10) r.raise_for_status() root = etree.fromstring(r.content) - logger.info('Loading suggestion page %s', root.find('data/page').text) + logger.info('Loading suggestion page %r', root.find('data/page').text) # Looking for show in following pages for show in root.findall('data/shows/show'): @@ -203,7 +201,7 @@ def _search_show_id(self, series): return show_id - next = root.find('data/next') + next_page = root.find('data/next') # No matches found logger.warning('Show id not found: suggestions does not match') @@ -216,6 +214,7 @@ def get_show_id(self, series, country_code=None): First search in the result of :meth:`_get_show_ids` and fallback on a search with :meth:`_search_show_id` :param str series: series of the episode. + :param str country_code: the country in which teh show is aired. :return: the show id, if found. :rtype: int or None @@ -241,6 +240,7 @@ def get_show_id(self, series, country_code=None): return show_id + @region.cache_on_arguments(expiration_time=EPISODE_EXPIRATION_TIME) def _download_zip(self, sub_id): # download the subtitle logger.info('Downloading subtitle %r', sub_id) @@ -256,10 +256,69 @@ def _download_zip(self, sub_id): return r.content - def query(self, series, season, episode, format, country=None, hash=None): + def _get_season_subtitles(self, show_id, season, sub_format): + params = { + 'apikey': self.apikey, + 'show_id': show_id, + 'q': 'Stagione %d' % season, + 'version': sub_format + } + r = self.session.get(self.server_url + 'subtitles/search', params=params, timeout=30) + r.raise_for_status() + root = etree.fromstring(r.content) + + if int(root.find('data/count').text) == 0: + logger.warning('Subtitles for season not found, try with rip suffix') + + params['version'] = sub_format + 'rip' + r = self.session.get(self.server_url + 'subtitles/search', params=params, timeout=30) + r.raise_for_status() + root = etree.fromstring(r.content) + if int(root.find('data/count').text) == 0: + logger.warning('Subtitles for season not found') + return [] + + subs = [] + # Looking for subtitles in first page + for subtitle in root.findall('data/subtitles/subtitle'): + if 'stagione %d' % season in subtitle.find('name').text.lower(): + logger.debug('Found season zip id %d - %r - %r', + int(subtitle.find('id').text), + subtitle.find('name').text, + subtitle.find('version').text) + + content = self._download_zip(int(subtitle.find('id').text)) + if not is_zipfile(io.BytesIO(content)): # pragma: no cover + if 'limite di download' in content: + raise TooManyRequests() + else: + raise ConfigurationError('Not a zip file: %r' % content) + + with ZipFile(io.BytesIO(content)) as zf: + episode_re = re.compile('s(\d{1,2})e(\d{1,2})') + for index, name in enumerate(zf.namelist()): + match = episode_re.search(name) + if not match: # pragma: no cover + logger.debug('Cannot decode subtitle %r', name) + else: + sub = ItaSASubtitle( + int(subtitle.find('id').text), + subtitle.find('show_name').text, + int(match.group(1)), + int(match.group(2)), + None, + None, + None, + name) + sub.content = fix_line_ending(zf.read(name)) + subs.append(sub) + + return subs + + def query(self, series, season, episode, video_format, resolution, country=None): # To make queries you need to be logged in - if not self.logged_in: + if not self.logged_in: # pragma: no cover raise ConfigurationError('Cannot query if not logged in') # get the show id @@ -269,16 +328,33 @@ def query(self, series, season, episode, format, country=None, hash=None): return [] # get the page of the season of the show - logger.info('Getting the subtitle of show id %d, season %d episode %d, format %s', show_id, - season, episode, format) + logger.info('Getting the subtitle of show id %d, season %d episode %d, format %r', show_id, + season, episode, video_format) subtitles = [] - # Default format is HDTV - sub_format = '' - if format is None or format.lower() == 'hdtv': - sub_format = 'normale' + # Default format is SDTV + if not video_format or video_format.lower() == 'hdtv': + if resolution in ('1080i', '1080p', '720p'): + sub_format = resolution + else: + sub_format = 'normale' else: - sub_format = format.lower() + sub_format = video_format.lower() + + # Look for year + params = { + 'apikey': self.apikey + } + r = self.session.get(self.server_url + 'shows/' + str(show_id), params=params, timeout=30) + r.raise_for_status() + root = etree.fromstring(r.content) + + year = root.find('data/show/started').text + if year: + year = int(year.split('-', 1)[0]) + tvdb_id = root.find('data/show/id_tvdb').text + if tvdb_id: + tvdb_id = int(tvdb_id) params = { 'apikey': self.apikey, @@ -286,20 +362,37 @@ def query(self, series, season, episode, format, country=None, hash=None): 'q': '%dx%02d' % (season, episode), 'version': sub_format } - logger.debug(params) r = self.session.get(self.server_url + 'subtitles/search', params=params, timeout=30) r.raise_for_status() root = etree.fromstring(r.content) if int(root.find('data/count').text) == 0: - logger.warning('Subtitles not found') - return [] + logger.warning('Subtitles not found, try with rip suffix') + + params['version'] = sub_format + 'rip' + r = self.session.get(self.server_url + 'subtitles/search', params=params, timeout=30) + r.raise_for_status() + root = etree.fromstring(r.content) + if int(root.find('data/count').text) == 0: + logger.warning('Subtitles not found, go season mode') + + # If no subtitle are found for single episode try to download all season zip + subs = self._get_season_subtitles(show_id, season, sub_format) + if subs: + for subtitle in subs: + subtitle.format = video_format + subtitle.year = year + subtitle.tvdb_id = tvdb_id + + return subs + else: + return [] - # Looking for subtitlles in first page + # Looking for subtitles in first page for subtitle in root.findall('data/subtitles/subtitle'): if '%dx%02d' % (season, episode) in subtitle.find('name').text.lower(): - logger.debug('Found subtitle id %d - %s - %s', + logger.debug('Found subtitle id %d - %r - %r', int(subtitle.find('id').text), subtitle.find('name').text, subtitle.find('version').text) @@ -309,27 +402,28 @@ def query(self, series, season, episode, format, country=None, hash=None): subtitle.find('show_name').text, season, episode, - format, - subtitle.find('name').text, - hash) + video_format, + year, + tvdb_id, + subtitle.find('name').text) subtitles.append(sub) # Not in the first page of result try next (if any) - next = root.find('data/next') - while next.text is not None: + next_page = root.find('data/next') + while next_page.text is not None: # pragma: no cover - r = self.session.get(next.text, timeout=30) + r = self.session.get(next_page.text, timeout=30) r.raise_for_status() root = etree.fromstring(r.content) - logger.info('Loading subtitles page %s', root.data.page.text) + logger.info('Loading subtitles page %r', root.data.page.text) # Looking for show in following pages for subtitle in root.findall('data/subtitles/subtitle'): if '%dx%02d' % (season, episode) in subtitle.find('name').text.lower(): - logger.debug('Found subtitle id %d - %s - %s', + logger.debug('Found subtitle id %d - %r - %r', int(subtitle.find('id').text), subtitle.find('name').text, subtitle.find('version').text) @@ -339,39 +433,40 @@ def query(self, series, season, episode, format, country=None, hash=None): subtitle.find('show_name').text, season, episode, - format, - subtitle.find('name').text, - hash) + video_format, + year, + tvdb_id, + subtitle.find('name').text) subtitles.append(sub) - next = root.find('data/next') + next_page = root.find('data/next') - # Dowload the subs found, can be more than one in zip + # Download the subs found, can be more than one in zip additional_subs = [] for sub in subtitles: # open the zip content = self._download_zip(sub.sub_id) - if not is_zipfile(io.BytesIO(content)): + if not is_zipfile(io.BytesIO(content)): # pragma: no cover if 'limite di download' in content: raise TooManyRequests() else: raise ConfigurationError('Not a zip file: %r' % content) with ZipFile(io.BytesIO(content)) as zf: - if len(zf.namelist()) > 1: + if len(zf.namelist()) > 1: # pragma: no cover - for name in enumerate(zf.namelist()): + for index, name in enumerate(zf.namelist()): - if name[0] == 0: - # First elemnent - sub.content = fix_line_ending(zf.read(name[1])) - sub.full_data = name[1] + if index == 0: + # First element + sub.content = fix_line_ending(zf.read(name)) + sub.full_data = name else: add_sub = copy.deepcopy(sub) - add_sub.content = fix_line_ending(zf.read(name[1])) - add_sub.full_data = name[1] + add_sub.content = fix_line_ending(zf.read(name)) + add_sub.full_data = name additional_subs.append(add_sub) else: sub.content = fix_line_ending(zf.read(zf.namelist()[0])) @@ -380,7 +475,7 @@ def query(self, series, season, episode, format, country=None, hash=None): return subtitles + additional_subs def list_subtitles(self, video, languages): - return self.query(video.series, video.season, video.episode, video.format, hash=video.hashes.get('itasa')) + return self.query(video.series, video.season, video.episode, video.format, video.resolution) - def download_subtitle(self, subtitle): + def download_subtitle(self, subtitle): # pragma: no cover pass diff --git a/lib/subliminal/providers/legendastv.py b/lib/subliminal/providers/legendastv.py index 7c3cc74d13..cdd16aca25 100644 --- a/lib/subliminal/providers/legendastv.py +++ b/lib/subliminal/providers/legendastv.py @@ -303,7 +303,7 @@ def get_archives(self, title_id, language_code): archives.append(archive) # stop on last page - if soup.find('a', attrs={'class': 'load_more'}, text='carregar mais') is None: + if soup.find('a', attrs={'class': 'load_more'}, string='carregar mais') is None: break # increment page count diff --git a/lib/subliminal/score.py b/lib/subliminal/score.py index b4d30e249c..6646441714 100755 --- a/lib/subliminal/score.py +++ b/lib/subliminal/score.py @@ -36,12 +36,12 @@ #: Scores for episodes -episode_scores = {'hash': 215, 'series': 108, 'year': 54, 'season': 18, 'episode': 18, 'release_group': 9, - 'format': 4, 'audio_codec': 2, 'resolution': 1, 'hearing_impaired': 1, 'video_codec': 1} +episode_scores = {'hash': 359, 'series': 180, 'year': 90, 'season': 30, 'episode': 30, 'release_group': 15, + 'format': 7, 'audio_codec': 3, 'resolution': 2, 'video_codec': 2, 'hearing_impaired': 1} #: Scores for movies -movie_scores = {'hash': 71, 'title': 36, 'year': 18, 'release_group': 9, - 'format': 4, 'audio_codec': 2, 'resolution': 1, 'hearing_impaired': 1, 'video_codec': 1} +movie_scores = {'hash': 119, 'title': 60, 'year': 30, 'release_group': 15, + 'format': 7, 'audio_codec': 3, 'resolution': 2, 'video_codec': 2, 'hearing_impaired': 1} def get_scores(video): @@ -160,13 +160,13 @@ def solve_episode_equations(): Eq(audio_codec, video_codec + 1), # resolution counts as much as video_codec - Eq(resolution, video_codec), + Eq(resolution, video_codec), - # hearing impaired is as much as resolution - Eq(hearing_impaired, resolution), + # video_codec is the least valuable match but counts more than the sum of all scoring increasing matches + Eq(video_codec, hearing_impaired + 1), - # video_codec is the least valuable match - Eq(video_codec, 1), + # hearing impaired is only used for score increasing, so put it to 1 + Eq(hearing_impaired, 1), ] return solve(equations, [hash, series, year, season, episode, release_group, format, audio_codec, resolution, @@ -200,13 +200,13 @@ def solve_movie_equations(): Eq(audio_codec, video_codec + 1), # resolution counts as much as video_codec - Eq(resolution, video_codec), + Eq(resolution, video_codec), - # hearing impaired is as much as resolution - Eq(hearing_impaired, resolution), + # video_codec is the least valuable match but counts more than the sum of all scoring increasing matches + Eq(video_codec, hearing_impaired + 1), - # video_codec is the least valuable match - Eq(video_codec, 1), + # hearing impaired is only used for score increasing, so put it to 1 + Eq(hearing_impaired, 1), ] return solve(equations, [hash, title, year, release_group, format, audio_codec, resolution, hearing_impaired, diff --git a/sickbeard/search_queue.py b/sickbeard/search_queue.py index 533bd0c9b5..e4e6f7c810 100644 --- a/sickbeard/search_queue.py +++ b/sickbeard/search_queue.py @@ -331,10 +331,10 @@ def run(self): self.results = search_result self.success = True if self.manual_search_type == 'season': - ui.notifications.message("We have found season pack results for {0}".format(self.show.name), + ui.notifications.message("We have found season packs for {0}".format(self.show.name), "These should become visible in the manual select page.") else: - ui.notifications.message("We have found single results for {0}".format(self.segment[0].prettyName()), + ui.notifications.message("We have found results for {0}".format(self.segment[0].prettyName()), "These should become visible in the manual select page.") else: ui.notifications.message('No results were found') diff --git a/sickbeard/server/api/core.py b/sickbeard/server/api/core.py index ee9a15738c..f4da89be53 100644 --- a/sickbeard/server/api/core.py +++ b/sickbeard/server/api/core.py @@ -1280,6 +1280,7 @@ class CMD_PostProcess(ApiCall): "return_data": {"desc": "Returns the result of the post-process"}, "process_method": {"desc": "How should valid post-processed files be handled"}, "is_priority": {"desc": "Replace the file even if it exists in a higher quality"}, + "delete_files": {"desc": "Delete files and folders like auto processing"}, "failed": {"desc": "Mark download as failed"}, "type": {"desc": "The type of post-process being requested"}, } @@ -1294,6 +1295,7 @@ def __init__(self, args, kwargs): self.process_method, args = self.check_params(args, kwargs, "process_method", False, False, "string", ["copy", "symlink", "hardlink", "move"]) self.is_priority, args = self.check_params(args, kwargs, "is_priority", False, False, "bool", []) + self.delete_files, args = self.check_params(args, kwargs, "delete_files", False, False, "bool", []) self.failed, args = self.check_params(args, kwargs, "failed", False, False, "bool", []) self.type, args = self.check_params(args, kwargs, "type", "auto", None, "string", ["auto", "manual"]) # super, missing, help @@ -1311,7 +1313,8 @@ def run(self): self.type = "manual" data = processTV.processDir(self.path, process_method=self.process_method, force=self.force_replace, - is_priority=self.is_priority, failed=self.failed, proc_type=self.type) + is_priority=self.is_priority, delete_on=self.delete_files, failed=self.failed, + proc_type=self.type) if not self.return_data: data = "" diff --git a/sickbeard/server/web/__init__.py b/sickbeard/server/web/__init__.py index ece587b3af..31c0914462 100644 --- a/sickbeard/server/web/__init__.py +++ b/sickbeard/server/web/__init__.py @@ -36,12 +36,6 @@ HomeChangeLog, HomePostProcess, HomeAddShows, - Home, - HomeIRC, - HomeNews, - HomeChangeLog, - HomePostProcess, - HomeAddShows, ) from sickbeard.server.web.manage import ( Manage, diff --git a/sickbeard/server/web/core/base.py b/sickbeard/server/web/core/base.py index e997bb2cf4..20c4d7a477 100644 --- a/sickbeard/server/web/core/base.py +++ b/sickbeard/server/web/core/base.py @@ -472,9 +472,9 @@ def get_messages(self): cur_notification_num = 1 for cur_notification in ui.notifications.get_notifications(self.request.remote_ip): messages['notification-{number}'.format(number=cur_notification_num)] = { - 'title': cur_notification.title, - 'message': cur_notification.message, - 'type': cur_notification.type, + 'title': '{0}'.format(cur_notification.title), + 'message': '{0}'.format(cur_notification.message), + 'type': '{0}'.format(cur_notification.type), } cur_notification_num += 1 diff --git a/sickbeard/server/web/home/add_shows.py b/sickbeard/server/web/home/add_shows.py index 6bbe2ea9ec..d651b7a823 100644 --- a/sickbeard/server/web/home/add_shows.py +++ b/sickbeard/server/web/home/add_shows.py @@ -149,7 +149,7 @@ def massAddTable(self, rootDir=None): dir_results = main_db_con.select( b'SELECT indexer_id ' b'FROM tv_shows ' - b'WHERE location = ? LIMIT 1', + b'WHERE location = ? LIMIT 1', [cur_path] ) @@ -534,7 +534,7 @@ def finishAddShow(): series_pieces = whichSeries.split('|') if (whichSeries and rootDir) or (whichSeries and fullShowPath and len(series_pieces) > 1): if len(series_pieces) < 6: - logger.log(u'Unable to add show due to show selection. Not anough arguments: %s' % (repr(series_pieces)), + logger.log(u'Unable to add show due to show selection. Not enough arguments: %s' % (repr(series_pieces)), logger.ERROR) ui.notifications.error('Unknown error. Unable to add show due to problem with show selection.') return self.redirect('/addShows/existingShows/') diff --git a/sickbeard/server/web/home/handler.py b/sickbeard/server/web/home/handler.py index fdfc074cac..1812c19628 100644 --- a/sickbeard/server/web/home/handler.py +++ b/sickbeard/server/web/home/handler.py @@ -1369,7 +1369,6 @@ def editShow(self, show=None, location=None, anyQualities=[], bestQualities=[], show_obj.rls_ignore_words = rls_ignore_words.strip() show_obj.rls_require_words = rls_require_words.strip() - location = location.decode('UTF-8') # if we change location clear the db of episodes, change it, write to db, and rescan old_location = ek(os.path.normpath, show_obj._location) new_location = ek(os.path.normpath, location) @@ -1388,7 +1387,7 @@ def editShow(self, show=None, location=None, anyQualities=[], bestQualities=[], except CantRefreshShowException as msg: errors.append('Unable to refresh this show:{error}'.format(error=msg)) # grab updated info from TVDB - # showObj.loadEpisodesFromIndexer() + # show_obj.loadEpisodesFromIndexer() # rescan the episodes in the new folder except NoNFOException: errors.append('The folder at {location} doesn\'t contain a tvshow.nfo - ' @@ -1434,7 +1433,7 @@ def editShow(self, show=None, location=None, anyQualities=[], bestQualities=[], return self.redirect('/home/displayShow?show={show}'.format(show=show)) - def erase_cache(self, showObj): + def erase_cache(self, show_obj): try: main_db_con = db.DBConnection('cache.db') @@ -1452,36 +1451,36 @@ def erase_cache(self, showObj): main_db_con.action( b'DELETE FROM \'{provider}\' ' b'WHERE indexerid = ?'.format(provider=cur_provider.get_id()), - [showObj.indexerid] + [show_obj.indexerid] ) except Exception: logger.log(u'Unable to delete cached results for provider {provider} for show: {show}'.format - (provider=cur_provider, show=showObj.name), logger.DEBUG) + (provider=cur_provider, show=show_obj.name), logger.DEBUG) except Exception: logger.log(u'Unable to delete cached results for show: {show}'.format - (show=showObj.name), logger.DEBUG) + (show=show_obj.name), logger.DEBUG) def togglePause(self, show=None): - error, show = Show.pause(show) + error, show_obj = Show.pause(show) if error is not None: return self._genericMessage('Error', error) ui.notifications.message('{show} has been {state}'.format - (show=show.name, state='paused' if show.paused else 'resumed')) + (show=show_obj.name, state='paused' if show_obj.paused else 'resumed')) - return self.redirect('/home/displayShow?show={show}'.format(show=show.indexerid)) + return self.redirect('/home/displayShow?show={show}'.format(show=show_obj.indexerid)) def deleteShow(self, show=None, full=0): if show: - error, show = Show.delete(show, full) + error, show_obj = Show.delete(show, full) if error is not None: return self._genericMessage('Error', error) ui.notifications.message('{show} has been {state} {details}'.format( - show=show.name, + show=show_obj.name, state='trashed' if sickbeard.TRASH_REMOVE_SHOW else 'deleted', details='(with all related media)' if full else '(media untouched)', )) @@ -1489,16 +1488,16 @@ def deleteShow(self, show=None, full=0): time.sleep(cpu_presets[sickbeard.CPU_PRESET]) # Remove show from 'RECENT SHOWS' in 'Shows' menu - sickbeard.SHOWS_RECENT = [x for x in sickbeard.SHOWS_RECENT if x['indexerid'] != show.indexerid] + sickbeard.SHOWS_RECENT = [x for x in sickbeard.SHOWS_RECENT if x['indexerid'] != show_obj.indexerid] # Don't redirect to the default page, so the user can confirm that the show was deleted return self.redirect('/home/') def refreshShow(self, show=None): - error, show = Show.refresh(show) + error, show_obj = Show.refresh(show) # This is a show validation error - if error is not None and show is None: + if error is not None and show_obj is None: return self._genericMessage('Error', error) # This is a refresh error @@ -1507,7 +1506,7 @@ def refreshShow(self, show=None): time.sleep(cpu_presets[sickbeard.CPU_PRESET]) - return self.redirect('/home/displayShow?show={show}'.format(show=show.indexerid)) + return self.redirect('/home/displayShow?show={show}'.format(show=show_obj.indexerid)) def updateShow(self, show=None, force=0): @@ -1528,7 +1527,7 @@ def updateShow(self, show=None, force=0): # just give it some time time.sleep(cpu_presets[sickbeard.CPU_PRESET]) - return self.redirect('/home/displayShow?show={show}'.format(show=show.indexerid)) + return self.redirect('/home/displayShow?show={show}'.format(show=show_obj.indexerid)) def subtitleShow(self, show=None, force=0): @@ -1545,7 +1544,7 @@ def subtitleShow(self, show=None, force=0): time.sleep(cpu_presets[sickbeard.CPU_PRESET]) - return self.redirect('/home/displayShow?show={show}'.format(show=show.indexerid)) + return self.redirect('/home/displayShow?show={show}'.format(show=show_obj.indexerid)) def updateKODI(self, show=None): show_name = None @@ -1567,7 +1566,7 @@ def updateKODI(self, show=None): ui.notifications.error('Unable to contact one or more KODI host(s): {host}'.format(host=host)) if show_obj: - return self.redirect('/home/displayShow?show={show}'.format(show=show.indexerid)) + return self.redirect('/home/displayShow?show={show}'.format(show=show_obj.indexerid)) else: return self.redirect('/home/') @@ -1592,7 +1591,7 @@ def updateEMBY(self, show=None): ui.notifications.error('Unable to contact Emby host: {host}'.format(host=sickbeard.EMBY_HOST)) if show_obj: - return self.redirect('/home/displayShow?show={show}'.format(show=show.indexerid)) + return self.redirect('/home/displayShow?show={show}'.format(show=show_obj.indexerid)) else: return self.redirect('/home/') @@ -1796,7 +1795,7 @@ def testRename(self, show=None): t = PageTemplate(rh=self, filename='testRename.mako') submenu = [{ 'title': 'Edit', - 'path': 'home/editShow?show={show}'.format(show=show.indexerid), + 'path': 'home/editShow?show={show}'.format(show=show_obj.indexerid), 'icon': 'ui-icon ui-icon-pencil' }] diff --git a/sickbeard/server/web/manage/handler.py b/sickbeard/server/web/manage/handler.py index 9d4cbce014..1470e1f1fd 100644 --- a/sickbeard/server/web/manage/handler.py +++ b/sickbeard/server/web/manage/handler.py @@ -696,7 +696,7 @@ def manageTorrents(self): def failedDownloads(self, limit=100, toRemove=None): failed_db_con = db.DBConnection('failed.db') - if limit: + if int(limit): sql_results = failed_db_con.select( b'SELECT * ' b'FROM failed '