2022-05-20 01:58:51 +00:00
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
|
|
import logging
|
2022-05-24 03:27:04 +00:00
|
|
|
import time
|
2022-05-20 01:58:51 +00:00
|
|
|
|
|
|
|
from requests import HTTPError
|
|
|
|
from requests import Session
|
|
|
|
from subliminal_patch.core import Episode
|
|
|
|
from subliminal_patch.language import PatchedAddic7edConverter
|
|
|
|
from subliminal_patch.providers import Provider
|
|
|
|
from subliminal_patch.providers.utils import update_matches
|
|
|
|
from subliminal_patch.subtitle import Subtitle
|
|
|
|
from subzero.language import Language
|
|
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
_BASE_URL = "https://api.gestdown.info"
|
|
|
|
|
|
|
|
|
|
|
|
class GestdownSubtitle(Subtitle):
|
|
|
|
provider_name = "gestdown"
|
|
|
|
hash_verifiable = False
|
2023-11-10 18:59:38 +00:00
|
|
|
hearing_impaired_verifiable = True
|
2022-05-20 01:58:51 +00:00
|
|
|
|
|
|
|
def __init__(self, language, data: dict):
|
|
|
|
super().__init__(language, hearing_impaired=data["hearingImpaired"])
|
|
|
|
self.page_link = _BASE_URL + data["downloadUri"]
|
|
|
|
self._id = data["subtitleId"]
|
|
|
|
self.release_info = data["version"]
|
2023-03-02 03:11:50 +00:00
|
|
|
self._matches = {"title", "series", "season", "episode", "tvdb_id"}
|
2022-05-20 01:58:51 +00:00
|
|
|
|
|
|
|
def get_matches(self, video):
|
|
|
|
update_matches(self._matches, video, self.release_info)
|
|
|
|
|
|
|
|
return self._matches
|
|
|
|
|
|
|
|
@property
|
|
|
|
def id(self):
|
|
|
|
return self._id
|
|
|
|
|
|
|
|
|
2022-05-24 03:27:04 +00:00
|
|
|
def _retry_on_423(method):
|
|
|
|
def retry(self, *args, **kwargs):
|
|
|
|
retries = 0
|
2022-05-26 00:44:47 +00:00
|
|
|
while 3 > retries:
|
2022-05-24 03:27:04 +00:00
|
|
|
try:
|
2022-05-26 00:44:47 +00:00
|
|
|
return method(self, *args, **kwargs)
|
2022-05-24 03:27:04 +00:00
|
|
|
except HTTPError as error:
|
|
|
|
if error.response.status_code != 423:
|
|
|
|
raise
|
|
|
|
|
|
|
|
retries += 1
|
|
|
|
|
|
|
|
logger.debug("423 returned. Retrying in 30 seconds")
|
|
|
|
time.sleep(30)
|
|
|
|
|
|
|
|
logger.debug("Retries limit exceeded. Ignoring query")
|
2022-05-26 00:44:47 +00:00
|
|
|
return []
|
2022-05-24 03:27:04 +00:00
|
|
|
|
|
|
|
return retry
|
|
|
|
|
|
|
|
|
2022-05-20 01:58:51 +00:00
|
|
|
class GestdownProvider(Provider):
|
|
|
|
provider_name = "gestdown"
|
|
|
|
|
|
|
|
video_types = (Episode,)
|
|
|
|
subtitle_class = GestdownSubtitle
|
|
|
|
|
|
|
|
# fmt: off
|
|
|
|
languages = {Language('por', 'BR')} | {Language(l) for l in [
|
|
|
|
'ara', 'aze', 'ben', 'bos', 'bul', 'cat', 'ces', 'dan', 'deu', 'ell', 'eng', 'eus', 'fas', 'fin', 'fra', 'glg',
|
|
|
|
'heb', 'hrv', 'hun', 'hye', 'ind', 'ita', 'jpn', 'kor', 'mkd', 'msa', 'nld', 'nor', 'pol', 'por', 'ron', 'rus',
|
|
|
|
'slk', 'slv', 'spa', 'sqi', 'srp', 'swe', 'tha', 'tur', 'ukr', 'vie', 'zho'
|
|
|
|
]} | {Language.fromietf(l) for l in ["sr-Latn", "sr-Cyrl"]}
|
|
|
|
languages.update(set(Language.rebuild(l, hi=True) for l in languages))
|
|
|
|
# fmt: on
|
|
|
|
|
|
|
|
_converter = PatchedAddic7edConverter()
|
|
|
|
|
|
|
|
def initialize(self):
|
|
|
|
self._session = Session()
|
|
|
|
self._session.headers.update({"User-Agent": "Bazarr"})
|
|
|
|
|
|
|
|
def terminate(self):
|
|
|
|
self._session.close()
|
|
|
|
|
2022-10-29 22:13:56 +00:00
|
|
|
def _subtitles_search(self, video, language: Language, show_id):
|
2022-09-26 22:09:41 +00:00
|
|
|
lang = self._converter.convert(language.alpha3)
|
2022-10-29 22:13:56 +00:00
|
|
|
response = self._session.get(f"{_BASE_URL}/subtitles/get/{show_id}/{video.season}/{video.episode}/{lang}")
|
2022-05-20 01:58:51 +00:00
|
|
|
|
|
|
|
# TODO: implement rate limiting
|
|
|
|
response.raise_for_status()
|
|
|
|
|
|
|
|
matching_subtitles = response.json()["matchingSubtitles"]
|
|
|
|
|
|
|
|
if not matching_subtitles:
|
|
|
|
logger.debug("No episodes found for '%s' language", language)
|
|
|
|
return None
|
|
|
|
|
|
|
|
for subtitle_dict in matching_subtitles:
|
2022-05-24 03:28:26 +00:00
|
|
|
if not subtitle_dict["completed"]:
|
|
|
|
continue
|
|
|
|
|
2022-05-20 01:58:51 +00:00
|
|
|
sub = GestdownSubtitle(language, subtitle_dict)
|
|
|
|
logger.debug("Found subtitle: %s", sub)
|
|
|
|
yield sub
|
|
|
|
|
2022-10-29 22:13:56 +00:00
|
|
|
def _search_show(self, video):
|
2022-10-02 17:17:00 +00:00
|
|
|
try:
|
2023-03-02 03:11:50 +00:00
|
|
|
response = self._session.get(f"{_BASE_URL}/shows/external/tvdb/{video.series_tvdb_id}")
|
2022-10-02 17:17:00 +00:00
|
|
|
response.raise_for_status()
|
2023-03-02 03:11:50 +00:00
|
|
|
return response.json()["shows"]
|
2022-10-02 17:17:00 +00:00
|
|
|
except HTTPError as error:
|
|
|
|
if error.response.status_code == 404:
|
2022-10-29 22:13:56 +00:00
|
|
|
return None
|
2022-10-02 17:17:00 +00:00
|
|
|
raise
|
|
|
|
|
|
|
|
|
2022-05-26 00:44:47 +00:00
|
|
|
@_retry_on_423
|
2022-05-20 01:58:51 +00:00
|
|
|
def list_subtitles(self, video, languages):
|
|
|
|
subtitles = []
|
2023-03-02 03:11:50 +00:00
|
|
|
shows = self._search_show(video)
|
|
|
|
if shows is None:
|
2022-10-02 17:17:00 +00:00
|
|
|
logger.debug("Couldn't find the show")
|
|
|
|
return subtitles
|
|
|
|
|
2022-05-20 01:58:51 +00:00
|
|
|
for language in languages:
|
|
|
|
try:
|
2023-03-02 03:11:50 +00:00
|
|
|
for show in shows:
|
|
|
|
subs = list(self._subtitles_search(video, language, show["id"]))
|
|
|
|
if len(subs) > 0:
|
|
|
|
subtitles += subs
|
|
|
|
continue
|
2022-05-20 01:58:51 +00:00
|
|
|
except HTTPError as error:
|
|
|
|
if error.response.status_code == 404:
|
|
|
|
logger.debug("Couldn't find the show or its season/episode")
|
|
|
|
return []
|
|
|
|
raise
|
|
|
|
|
|
|
|
return subtitles
|
|
|
|
|
|
|
|
def download_subtitle(self, subtitle: GestdownSubtitle):
|
|
|
|
response = self._session.get(subtitle.page_link, allow_redirects=True)
|
|
|
|
response.raise_for_status()
|
|
|
|
subtitle.content = response.content
|