diff --git a/bazarr/get_providers.py b/bazarr/get_providers.py index 2a6290d0d..f0754c1b9 100644 --- a/bazarr/get_providers.py +++ b/bazarr/get_providers.py @@ -51,6 +51,10 @@ PROVIDER_THROTTLE_MAP = { DownloadLimitReached: (datetime.timedelta(hours=6), "6 hours"), APIThrottled: (datetime.timedelta(seconds=15), "15 seconds"), }, + "opensubtitlescom": { + TooManyRequests: (datetime.timedelta(minutes=1), "1 minute"), + DownloadLimitExceeded: (datetime.timedelta(hours=hours_until_end_of_day), "{} hours".format(str(hours_until_end_of_day))), + }, "addic7ed": { DownloadLimitExceeded: (datetime.timedelta(hours=3), "3 hours"), TooManyRequests: (datetime.timedelta(minutes=5), "5 minutes"), diff --git a/libs/subliminal_patch/providers/opensubtitlescom.py b/libs/subliminal_patch/providers/opensubtitlescom.py index 161bcbf5b..0ca62eed0 100644 --- a/libs/subliminal_patch/providers/opensubtitlescom.py +++ b/libs/subliminal_patch/providers/opensubtitlescom.py @@ -10,6 +10,7 @@ from babelfish import language_converters from subliminal import Episode, Movie from subliminal.score import get_equivalent_release_groups from subliminal.utils import sanitize_release_group, sanitize +from subliminal_patch.exceptions import TooManyRequests from subliminal.exceptions import DownloadLimitExceeded, AuthenticationError, ConfigurationError, ServiceUnavailable, \ ProviderError from .mixins import ProviderRetryMixin @@ -107,6 +108,9 @@ class OpenSubtitlesComProvider(ProviderRetryMixin, Provider): languages.update(set(Language.rebuild(l, forced=True) for l in languages)) def __init__(self, username=None, password=None, use_hash=True, api_key=None): + if not api_key: + raise ConfigurationError('Api_key must be specified') + if not all((username, password)): raise ConfigurationError('Username and password must be specified') @@ -150,36 +154,52 @@ class OpenSubtitlesComProvider(ProviderRetryMixin, Provider): region.set("oscom_token", self.token) return True elif r.status_code == 401: - raise AuthenticationError('Login failed: %s' % r.reason) + raise AuthenticationError('Login failed: {}'.format(r.reason)) + elif r.status_code == 429: + raise TooManyRequests() else: - raise ProviderError('Bad status code: ' + str(r.status_code)) + raise ProviderError('Bad status code: {}'.format(r.status_code)) finally: return False @region.cache_on_arguments(expiration_time=SHOW_EXPIRATION_TIME) def search_titles(self, title): title_id = None + imdb_id = None + + if isinstance(self.video, Episode) and self.video.series_imdb_id: + imdb_id = self.video.series_imdb_id + elif isinstance(self.video, Movie) and self.video.imdb_id: + imdb_id = self.video.imdb_id - if self.video.imdb_id: - parameters = {'imdb_id': self.video.imdb_id} + if imdb_id: + parameters = {'imdb_id': imdb_id} + logging.debug('Searching using this IMDB id: {}'.format(imdb_id)) else: parameters = {'query': title} + logging.debug('Searching using this title: {}'.format(title)) results = self.session.get(self.server_url + 'features', params=parameters, timeout=10) + results.raise_for_status() if results.status_code == 401: + logging.debug('Authentification failed: clearing cache and attempting to login.') region.delete("oscom_token") self.login() results = self.session.get(self.server_url + 'features', params=parameters, timeout=10) + results.raise_for_status() - results.raise_for_status() + if results.status_code == 429: + raise TooManyRequests() + elif results.status_code == 429: + raise TooManyRequests() # deserialize results try: results_dict = results.json()['data'] except ValueError: - logger.debug('Unable to parse returned json') + raise ProviderError('Invalid JSON returned by provider') else: # loop over results for result in results_dict: @@ -189,15 +209,17 @@ class OpenSubtitlesComProvider(ProviderRetryMixin, Provider): break if title_id: + logging.debug('Found this title ID: {}'.format(title_id)) return title_id finally: if not title_id: - logger.debug('No match found for "%s"' % title) + logger.debug('No match found for {}'.format(title)) def query(self, languages, video): self.video = video if self.use_hash: hash = self.video.hashes.get('opensubtitlescom') + logging.debug('Searching using this hash: {}'.format(hash)) else: hash = None @@ -211,6 +233,7 @@ class OpenSubtitlesComProvider(ProviderRetryMixin, Provider): return [] lang_strings = [str(lang) for lang in languages] langs = ','.join(lang_strings) + logging.debug('Searching for this languages: {}'.format(lang_strings)) # query the server if isinstance(self.video, Episode): @@ -228,41 +251,52 @@ class OpenSubtitlesComProvider(ProviderRetryMixin, Provider): 'moviehash': hash}, timeout=10) res.raise_for_status() - result = res.json() - subtitles = [] - if len(result['data']): - for item in result['data']: - if 'season_number' in item['attributes']['feature_details']: - season_number = item['attributes']['feature_details']['season_number'] - else: - season_number = None + if res.status_code == 429: + raise TooManyRequests() - if 'episode_number' in item['attributes']['feature_details']: - episode_number = item['attributes']['feature_details']['episode_number'] - else: - episode_number = None + subtitles = [] - if 'moviehash_match' in item['attributes']: - moviehash_match = item['attributes']['moviehash_match'] - else: - moviehash_match = False - - subtitle = OpenSubtitlesComSubtitle( - language=Language.fromietf(item['attributes']['language']), - hearing_impaired=item['attributes']['hearing_impaired'], - page_link=item['attributes']['url'], - file_id=item['attributes']['files'][0]['file_id'], - releases=item['attributes']['release'], - uploader=item['attributes']['uploader']['name'], - title=item['attributes']['feature_details']['movie_name'], - year=item['attributes']['feature_details']['year'], - season=season_number, - episode=episode_number, - hash_matched=moviehash_match - ) - subtitle.get_matches(self.video) - subtitles.append(subtitle) + try: + result = res.json() + except ValueError: + raise ProviderError('Invalid JSON returned by provider') + else: + logging.debug('Query returned {} subtitles'.format(len(result['data']))) + + if len(result['data']): + for item in result['data']: + if 'season_number' in item['attributes']['feature_details']: + season_number = item['attributes']['feature_details']['season_number'] + else: + season_number = None + + if 'episode_number' in item['attributes']['feature_details']: + episode_number = item['attributes']['feature_details']['episode_number'] + else: + episode_number = None + + if 'moviehash_match' in item['attributes']: + moviehash_match = item['attributes']['moviehash_match'] + else: + moviehash_match = False + + if len(item['attributes']['files']): + subtitle = OpenSubtitlesComSubtitle( + language=Language.fromietf(item['attributes']['language']), + hearing_impaired=item['attributes']['hearing_impaired'], + page_link=item['attributes']['url'], + file_id=item['attributes']['files'][0]['file_id'], + releases=item['attributes']['release'], + uploader=item['attributes']['uploader']['name'], + title=item['attributes']['feature_details']['movie_name'], + year=item['attributes']['feature_details']['year'], + season=season_number, + episode=episode_number, + hash_matched=moviehash_match + ) + subtitle.get_matches(self.video) + subtitles.append(subtitle) return subtitles @@ -278,14 +312,28 @@ class OpenSubtitlesComProvider(ProviderRetryMixin, Provider): headers=headers, timeout=10) res.raise_for_status() - subtitle.download_link = res.json()['link'] - r = self.session.get(subtitle.download_link, timeout=10) - r.raise_for_status() + if res.status_code == 429: + raise TooManyRequests() + elif res.status_code == 406: + raise DownloadLimitExceeded("Daily download limit reached") + else: + try: + subtitle.download_link = res.json()['link'] + except ValueError: + raise ProviderError('Invalid JSON returned by provider') + else: + r = self.session.get(subtitle.download_link, timeout=10) + r.raise_for_status() + + if res.status_code == 429: + raise TooManyRequests() + elif res.status_code == 406: + raise DownloadLimitExceeded("Daily download limit reached") - subtitle_content = r.content + subtitle_content = r.content - if subtitle_content: - subtitle.content = fix_line_ending(subtitle_content) - else: - logger.debug('Could not download subtitle from %s', subtitle.download_link) + if subtitle_content: + subtitle.content = fix_line_ending(subtitle_content) + else: + logger.debug('Could not download subtitle from {}'.format(subtitle.download_link))