From c9d63f717f581661fa9df6436a77c75b6feca92e Mon Sep 17 00:00:00 2001 From: Vitiko Date: Thu, 19 May 2022 21:58:51 -0400 Subject: [PATCH] Add Gestdown.info Provider --- README.md | 115 +++++++++++--------- libs/subliminal_patch/providers/gestdown.py | 103 ++++++++++++++++++ tests/subliminal_patch/test_gestdown.py | 77 +++++++++++++ 3 files changed, 241 insertions(+), 54 deletions(-) create mode 100644 libs/subliminal_patch/providers/gestdown.py create mode 100644 tests/subliminal_patch/test_gestdown.py diff --git a/README.md b/README.md index b1a1c24bd..e64258428 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ # bazarr + Bazarr is a companion application to Sonarr and Radarr. It manages and downloads subtitles based on your requirements. You define your preferences by TV show or movie and Bazarr takes care of everything for you. Be aware that Bazarr doesn't scan disk to detect series and movies: It only takes care of the series and movies that are indexed in Sonarr and Radarr. @@ -6,10 +7,12 @@ Be aware that Bazarr doesn't scan disk to detect series and movies: It only take Thanks to the folks at OpenSubtitles for their logo that was an inspiration for ours. ## Support on Paypal + At the request of some, here is a way to demonstrate your appreciation for the efforts made in the development of Bazarr: [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=XHHRWXT9YB7WE&source=url) # Status + [![GitHub issues](https://img.shields.io/github/issues/morpheus65535/bazarr.svg?style=flat-square)](https://github.com/morpheus65535/bazarr/issues) [![GitHub stars](https://img.shields.io/github/stars/morpheus65535/bazarr.svg?style=flat-square)](https://github.com/morpheus65535/bazarr/stargazers) [![Docker Pulls](https://img.shields.io/docker/pulls/linuxserver/bazarr.svg?style=flat-square)](https://hub.docker.com/r/linuxserver/bazarr/) @@ -17,6 +20,7 @@ At the request of some, here is a way to demonstrate your appreciation for the e [![Discord](https://img.shields.io/badge/discord-chat-MH2e2eb.svg?style=flat-square)](https://discord.gg/MH2e2eb) # Support + For installation and configuration instructions, see [wiki](https://wiki.bazarr.media). You can reach us for support on [Discord](https://discord.gg/MH2e2eb). @@ -24,64 +28,67 @@ You can reach us for support on [Discord](https://discord.gg/MH2e2eb). If you find a bug, please open an issue on [Github](https://github.com/morpheus65535/bazarr/issues). # Feature Requests + If you need something that is not already part of Bazarr, feel free to create a feature request on [Feature Upvote](http://features.bazarr.media). ## Major Features Include: -* Support for major platforms: Windows, Linux, macOS, Raspberry Pi, etc. -* Automatically add new series and episodes from Sonarr -* Automatically add new movies from Radarr -* Series or movies based configuration for subtitles languages -* Scan your existing library for internal and external subtitles and download any missing -* Keep history of what was downloaded from where and when -* Manual search so you can download subtitles on demand -* Upgrade subtitles previously downloaded when a better one is found -* Ability to delete external subtitles from disk -* Currently support 184 subtitles languages with support for forced/foreign subtitles (depending of providers) -* And a beautiful UI based on Sonarr +- Support for major platforms: Windows, Linux, macOS, Raspberry Pi, etc. +- Automatically add new series and episodes from Sonarr +- Automatically add new movies from Radarr +- Series or movies based configuration for subtitles languages +- Scan your existing library for internal and external subtitles and download any missing +- Keep history of what was downloaded from where and when +- Manual search so you can download subtitles on demand +- Upgrade subtitles previously downloaded when a better one is found +- Ability to delete external subtitles from disk +- Currently support 184 subtitles languages with support for forced/foreign subtitles (depending of providers) +- And a beautiful UI based on Sonarr ## Supported subtitles providers: -* Addic7ed -* Argenteam -* Assrt -* BetaSeries -* BSplayer -* Embedded Subtitles -* GreekSubtitles -* Hosszupuska -* LegendasDivx -* LegendasTV -* Ktuvit (Get `hashed_password` using method described [here](https://github.com/XBMCil/service.subtitles.ktuvit)) -* Napiprojekt -* Napisy24 -* Nekur -* OpenSubtitles.org -* Podnapisi -* RegieLive -* Sous-Titres.eu -* Subdivx -* Subs.sab.bz -* Subs4Free -* Subs4Series -* Subscene -* Subscenter -* Subsunacs.net -* SubSynchro -* Subtitrari-noi.ro -* subtitri.id.lv -* Subtitulamos.tv -* Sucha -* Supersubtitles -* Titlovi -* Titrari.ro -* Titulky.com -* TuSubtitulo -* TVSubtitles -* Wizdom -* XSubs -* Yavka.net -* YIFY Subtitles -* Zimuku + +- Addic7ed +- Argenteam +- Assrt +- BetaSeries +- BSplayer +- Embedded Subtitles +- Gestdown.info +- GreekSubtitles +- Hosszupuska +- LegendasDivx +- LegendasTV +- Ktuvit (Get `hashed_password` using method described [here](https://github.com/XBMCil/service.subtitles.ktuvit)) +- Napiprojekt +- Napisy24 +- Nekur +- OpenSubtitles.org +- Podnapisi +- RegieLive +- Sous-Titres.eu +- Subdivx +- Subs.sab.bz +- Subs4Free +- Subs4Series +- Subscene +- Subscenter +- Subsunacs.net +- SubSynchro +- Subtitrari-noi.ro +- subtitri.id.lv +- Subtitulamos.tv +- Sucha +- Supersubtitles +- Titlovi +- Titrari.ro +- Titulky.com +- TuSubtitulo +- TVSubtitles +- Wizdom +- XSubs +- Yavka.net +- YIFY Subtitles +- Zimuku ## Screenshot @@ -89,5 +96,5 @@ If you need something that is not already part of Bazarr, feel free to create a ### License -* [GNU GPL v3](http://www.gnu.org/licenses/gpl.html) -* Copyright 2010-2019 +- [GNU GPL v3](http://www.gnu.org/licenses/gpl.html) +- Copyright 2010-2019 diff --git a/libs/subliminal_patch/providers/gestdown.py b/libs/subliminal_patch/providers/gestdown.py new file mode 100644 index 000000000..56b922b6d --- /dev/null +++ b/libs/subliminal_patch/providers/gestdown.py @@ -0,0 +1,103 @@ +# -*- coding: utf-8 -*- + +import logging + +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 + + 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"] + self._matches = {"title", "series", "season", "episode"} + + def get_matches(self, video): + update_matches(self._matches, video, self.release_info) + + return self._matches + + @property + def id(self): + return self._id + + +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() + + def _subtitles_search(self, video, language: Language): + json_data = { + "search": f"{video.series} S{video.season:02}E{video.episode:02}", + "language": self._converter.convert(language.alpha3), + } + + logger.debug("Post data: %s", json_data) + response = self._session.post(f"{_BASE_URL}/subtitles/search", json=json_data) + + # 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: + sub = GestdownSubtitle(language, subtitle_dict) + logger.debug("Found subtitle: %s", sub) + yield sub + + def list_subtitles(self, video, languages): + subtitles = [] + for language in languages: + try: + subtitles += self._subtitles_search(video, language) + 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 diff --git a/tests/subliminal_patch/test_gestdown.py b/tests/subliminal_patch/test_gestdown.py new file mode 100644 index 000000000..4a67304d9 --- /dev/null +++ b/tests/subliminal_patch/test_gestdown.py @@ -0,0 +1,77 @@ +import pytest + +from subliminal_patch.providers.gestdown import GestdownProvider +from subliminal_patch.providers.gestdown import GestdownSubtitle +from subzero.language import Language + +from subliminal_patch.language import PatchedAddic7edConverter + + +def test_language_list_is_convertible(): + converter = PatchedAddic7edConverter() + for language in GestdownProvider.languages: + converter.convert(language.alpha3) + + +@pytest.mark.parametrize( + "episode_key,language,expected_any_release_info", + [ + ("breaking_bad_s01e01", Language.fromietf("en"), "BluRayREWARD"), + ("better_call_saul_s06e04", Language.fromietf("fr"), "AMZN-NTb"), + ], +) +def test_list_subtitles(episodes, episode_key, language, expected_any_release_info): + with GestdownProvider() as provider: + subtitles = provider.list_subtitles(episodes[episode_key], {language}) + assert any( + subtitle.release_info == expected_any_release_info for subtitle in subtitles + ) + + +def test_list_subtitles_hearing_impaired(episodes): + with GestdownProvider() as provider: + subtitles = provider.list_subtitles( + episodes["better_call_saul_s06e04"], {Language.fromietf("en")} + ) + assert not all(subtitle.hearing_impaired for subtitle in subtitles) + assert any(subtitle.hearing_impaired for subtitle in subtitles) + + +def test_list_subtitles_inexistent(episodes): + with GestdownProvider() as provider: + assert not provider.list_subtitles( + episodes["inexistent"], {Language.fromietf("en")} + ) + + +@pytest.fixture +def subtitle(): + return GestdownSubtitle( + Language.fromietf("fr"), + { + "subtitleId": "d28b4d5b-7dcc-47b3-8232-fb02f081d135", + "version": "480p.AMZN.WEB-DL.NTb", + "hearingImpaired": False, + "downloadUri": "/subtitles/download/d28b4d5b-7dcc-47b3-8232-fb02f081d135", + }, + ) + + +def test_subtitle(subtitle): + assert subtitle.language == Language.fromietf("fr") + assert subtitle.id == "d28b4d5b-7dcc-47b3-8232-fb02f081d135" + assert subtitle.hearing_impaired == False + + +def test_subtitle_get_matches(subtitle, episodes): + matches = subtitle.get_matches(episodes["better_call_saul_s06e04"]) + + assert matches.issuperset(("series", "title", "season", "episode", "source")) + assert "resolution" not in matches + + +def test_subtitle_download(subtitle): + with GestdownProvider() as provider: + provider.download_subtitle(subtitle) + assert subtitle.content is not None + assert subtitle.is_valid()