Improved retry and exception handling in opensubtitles.com provider.

pull/1696/head v1.0.3-beta.18
morpheus65535 2 years ago
parent aa0c2ffca7
commit d879128dcd

@ -4,7 +4,7 @@ import os
import time import time
import datetime import datetime
from requests import Session, ConnectionError, Timeout, ReadTimeout from requests import Session, ConnectionError, Timeout, ReadTimeout, RequestException
from subzero.language import Language from subzero.language import Language
from babelfish import language_converters from babelfish import language_converters
@ -161,15 +161,15 @@ class OpenSubtitlesComProvider(ProviderRetryMixin, Provider):
return self._started and (time.time() - self._started) < TOKEN_EXPIRATION_TIME return self._started and (time.time() - self._started) < TOKEN_EXPIRATION_TIME
def login(self): def login(self):
try: r = self.retry(
r = self.session.post(self.server_url + 'login', lambda: checked(
lambda: self.session.post(self.server_url + 'login',
json={"username": self.username, "password": self.password}, json={"username": self.username, "password": self.password},
allow_redirects=False, allow_redirects=False,
timeout=30) timeout=30)
except (ConnectionError, Timeout, ReadTimeout): )
raise ServiceUnavailable('Unknown Error, empty response: %s: %r' % (r.status_code, r)) )
else:
if r.status_code == 200:
try: try:
self.token = r.json()['token'] self.token = r.json()['token']
except ValueError: except ValueError:
@ -177,14 +177,6 @@ class OpenSubtitlesComProvider(ProviderRetryMixin, Provider):
else: else:
region.set("oscom_token", self.token) region.set("oscom_token", self.token)
return return
elif r.status_code == 401:
raise AuthenticationError('Login failed: {}'.format(r.reason))
elif r.status_code == 429:
raise TooManyRequests()
elif 500 <= r.status_code <= 599:
raise ProviderError(r.reason)
else:
raise ProviderError('Bad status code: {}'.format(r.status_code))
@staticmethod @staticmethod
def sanitize_external_ids(external_id): def sanitize_external_ids(external_id):
@ -200,23 +192,23 @@ class OpenSubtitlesComProvider(ProviderRetryMixin, Provider):
parameters = {'query': title.lower()} parameters = {'query': title.lower()}
logging.debug('Searching using this title: {}'.format(title)) logging.debug('Searching using this title: {}'.format(title))
results = self.session.get(self.server_url + 'features', params=parameters, timeout=30) results = self.retry(
lambda: checked(
lambda: self.session.get(self.server_url + 'features', params=parameters, timeout=30),
validate_token=True
)
)
if results.status_code == 401: if results == 401:
logging.debug('Authentification failed: clearing cache and attempting to login.') logging.debug('Authentification failed: clearing cache and attempting to login.')
region.delete("oscom_token") region.delete("oscom_token")
self.login() self.login()
results = self.session.get(self.server_url + 'features', params=parameters, timeout=30) results = self.retry(
lambda: checked(
if results.status_code == 429: lambda: self.session.get(self.server_url + 'features', params=parameters, timeout=30)
raise TooManyRequests() )
elif 500 <= results.status_code <= 599: )
raise ProviderError(results.reason)
elif results.status_code == 429:
raise TooManyRequests()
elif 500 <= results.status_code <= 599:
raise ProviderError(results.reason)
# deserialize results # deserialize results
try: try:
@ -287,7 +279,9 @@ class OpenSubtitlesComProvider(ProviderRetryMixin, Provider):
# query the server # query the server
if isinstance(self.video, Episode): if isinstance(self.video, Episode):
res = self.session.get(self.server_url + 'subtitles', res = self.retry(
lambda: checked(
lambda: self.session.get(self.server_url + 'subtitles',
params=(('episode_number', self.video.episode), params=(('episode_number', self.video.episode),
('foreign_parts_only', forced), ('foreign_parts_only', forced),
('languages', langs.lower()), ('languages', langs.lower()),
@ -296,20 +290,20 @@ class OpenSubtitlesComProvider(ProviderRetryMixin, Provider):
('season_number', self.video.season), ('season_number', self.video.season),
('query', os.path.basename(self.video.name))), ('query', os.path.basename(self.video.name))),
timeout=30) timeout=30)
)
)
else: else:
res = self.session.get(self.server_url + 'subtitles', res = self.retry(
lambda: checked(
lambda: self.session.get(self.server_url + 'subtitles',
params=(('foreign_parts_only', forced), params=(('foreign_parts_only', forced),
('id', title_id) if title_id else ('imdb_id', imdb_id), ('id', title_id) if title_id else ('imdb_id', imdb_id),
('languages', langs.lower()), ('languages', langs.lower()),
('moviehash', file_hash), ('moviehash', file_hash),
('query', os.path.basename(self.video.name))), ('query', os.path.basename(self.video.name))),
timeout=30) timeout=30)
)
if res.status_code == 429: )
raise TooManyRequests()
elif 500 <= res.status_code <= 599:
raise ProviderError(res.reason)
subtitles = [] subtitles = []
@ -374,30 +368,30 @@ class OpenSubtitlesComProvider(ProviderRetryMixin, Provider):
headers = {'Accept': 'application/json', 'Content-Type': 'application/json', headers = {'Accept': 'application/json', 'Content-Type': 'application/json',
'Authorization': 'Beaker ' + self.token} 'Authorization': 'Beaker ' + self.token}
res = self.session.post(self.server_url + 'download', res = self.retry(
lambda: checked(
lambda: self.session.post(self.server_url + 'download',
json={'file_id': subtitle.file_id, 'sub_format': 'srt'}, json={'file_id': subtitle.file_id, 'sub_format': 'srt'},
headers=headers, headers=headers,
timeout=30) timeout=30)
if res.status_code == 429: )
raise TooManyRequests() )
elif res.status_code == 406:
raise DownloadLimitExceeded("Daily download limit reached")
elif 500 <= res.status_code <= 599:
raise ProviderError(res.reason)
else:
try: try:
subtitle.download_link = res.json()['link'] download_data = res.json()
except ValueError: except ValueError:
raise ProviderError('Invalid JSON returned by provider') raise ProviderError('Invalid JSON returned by provider')
else: else:
r = self.session.get(subtitle.download_link, timeout=30) if 'link' not in download_data:
return False
if res.status_code == 429: subtitle.download_link = download_data['link']
raise TooManyRequests()
elif res.status_code == 406: r = self.retry(
raise DownloadLimitExceeded("Daily download limit reached") lambda: checked(
elif 500 <= res.status_code <= 599: lambda: self.session.get(subtitle.download_link, timeout=30)
raise ProviderError(res.reason) )
)
subtitle_content = r.content subtitle_content = r.content
@ -405,3 +399,42 @@ class OpenSubtitlesComProvider(ProviderRetryMixin, Provider):
subtitle.content = fix_line_ending(subtitle_content) subtitle.content = fix_line_ending(subtitle_content)
else: else:
logger.debug('Could not download subtitle from {}'.format(subtitle.download_link)) logger.debug('Could not download subtitle from {}'.format(subtitle.download_link))
def checked(fn, validate_token=False):
"""Run :fn: and check the response status before returning it.
:param fn: the function to make an API call to OpenSubtitles.com.
:param validate_token: test if token is valid and return 401 if not.
:return: the response.
"""
response = None
try:
try:
response = fn()
except (ConnectionError, Timeout, ReadTimeout):
raise ServiceUnavailable('Unknown Error, empty response: %s: %r' % (response.status_code, response))
except RequestException as e:
status_code = e.response.status_code
else:
status_code = int(response['status'][:3])
except Exception:
status_code = None
else:
if status_code == 401:
if validate_token:
return 401
else:
raise AuthenticationError('Login failed: {}'.format(response.reason))
elif status_code == 406:
raise DownloadLimitExceeded("Daily download limit reached")
elif status_code == 429:
raise TooManyRequests()
elif 500 <= status_code <= 599:
raise ProviderError(response.reason)
if status_code != 200:
raise ProviderError('Bad status code: {}'.format(response.status_code))
return response

Loading…
Cancel
Save