summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authormorpheus65535 <[email protected]>2023-09-29 13:51:21 -0400
committermorpheus65535 <[email protected]>2023-09-29 13:51:21 -0400
commitbad77e2ce7aa61d8202d8b0bd505ab24610f12d5 (patch)
tree1155187ed2112161f8acffad0eef2fe2e6c8cf36
parent807621a612a0734e91b8271278099f7d950d01de (diff)
parent63335f40fcefd773405c11db8550988662ac88ae (diff)
downloadbazarr-bad77e2ce7aa61d8202d8b0bd505ab24610f12d5.tar.gz
bazarr-bad77e2ce7aa61d8202d8b0bd505ab24610f12d5.zip
Merge remote-tracking branch 'origin/development' into developmentv1.3.1-beta.5
-rw-r--r--README.md1
-rw-r--r--bazarr/app/config.py4
-rw-r--r--bazarr/app/get_providers.py4
-rw-r--r--frontend/src/pages/Settings/Providers/list.ts18
-rw-r--r--libs/subliminal_patch/providers/hdbits.py171
5 files changed, 198 insertions, 0 deletions
diff --git a/README.md b/README.md
index 1c92ead6c..a7c2aaebf 100644
--- a/README.md
+++ b/README.md
@@ -55,6 +55,7 @@ If you need something that is not already part of Bazarr, feel free to create a
- Embedded Subtitles
- Gestdown.info
- GreekSubtitles
+- HDBits.org
- Hosszupuska
- LegendasDivx
- Karagarga.in
diff --git a/bazarr/app/config.py b/bazarr/app/config.py
index 23a9cb528..6a759c89c 100644
--- a/bazarr/app/config.py
+++ b/bazarr/app/config.py
@@ -229,6 +229,10 @@ defaults = {
'timeout': '600',
'unknown_as_english': 'False',
},
+ 'hdbits': {
+ 'username': '',
+ 'passkey': '',
+ },
'karagarga': {
'username': '',
'password': '',
diff --git a/bazarr/app/get_providers.py b/bazarr/app/get_providers.py
index 2338fa753..7e36a3e9a 100644
--- a/bazarr/app/get_providers.py
+++ b/bazarr/app/get_providers.py
@@ -294,6 +294,10 @@ def get_providers_auth():
'f_username': settings.karagarga.f_username,
'f_password': settings.karagarga.f_password,
},
+ 'hdbits': {
+ 'username': settings.hdbits.username,
+ 'passkey': settings.hdbits.passkey,
+ },
'subf2m': {
'verify_ssl': settings.subf2m.getboolean('verify_ssl'),
'user_agent': settings.subf2m.user_agent,
diff --git a/frontend/src/pages/Settings/Providers/list.ts b/frontend/src/pages/Settings/Providers/list.ts
index 653d45380..0f1375756 100644
--- a/frontend/src/pages/Settings/Providers/list.ts
+++ b/frontend/src/pages/Settings/Providers/list.ts
@@ -127,6 +127,24 @@ export const ProviderList: Readonly<ProviderInfo[]> = [
name: "GreekSubtitles",
description: "Greek Subtitles Provider",
},
+ {
+ key: "hdbits",
+ name: "HDBits.org",
+ description: "Private Tracker Subtitles Provider",
+ message:
+ "You must have 2FA enabled and whitelist your IP if you are running from a server.",
+ inputs: [
+ {
+ type: "text",
+ key: "username",
+ },
+ {
+ type: "password",
+ key: "passkey",
+ name: "Your profile's passkey",
+ },
+ ],
+ },
{ key: "hosszupuska", description: "Hungarian Subtitles Provider" },
{
key: "legendasdivx",
diff --git a/libs/subliminal_patch/providers/hdbits.py b/libs/subliminal_patch/providers/hdbits.py
new file mode 100644
index 000000000..1d2fc87ce
--- /dev/null
+++ b/libs/subliminal_patch/providers/hdbits.py
@@ -0,0 +1,171 @@
+# -*- coding: utf-8 -*-
+import functools
+import logging
+import time
+
+from babelfish import language_converters
+from guessit import guessit
+from requests import Session
+from subliminal_patch.core import Episode
+from subliminal_patch.core import Movie
+from subliminal_patch.providers import Provider
+from subliminal_patch.providers.utils import get_archive_from_bytes
+from subliminal_patch.providers.utils import get_subtitle_from_archive
+from subliminal_patch.providers.utils import update_matches
+from subliminal_patch.subtitle import Subtitle
+from subzero.language import Language
+
+logger = logging.getLogger(__name__)
+
+
+class HDBitsSubtitle(Subtitle):
+ provider_name = "hdbits"
+ hash_verifiable = False
+
+ def __init__(self, language, id, name, filename, matches=None, episode=None):
+ super().__init__(language, hearing_impaired=language.hi)
+ self.item_id = id
+ self.release_info = name
+ self.filename = filename
+ self.episode = episode
+ self._matches = matches or set()
+
+ def get_matches(self, video):
+ update_matches(self._matches, video, self.release_info)
+ return self._matches
+
+ @property
+ def id(self):
+ return f"{self.provider_name}_{self.item_id}"
+
+
+_SPECIAL_LANG_MAP = {"uk": ("eng",), "br": ("por", "BR"), "gr": ("ell",)}
+_ALLOWED_EXTENSIONS = (".ass", ".srt", ".zip", ".rar")
+
+
+def _get_language(code):
+ special_args = _SPECIAL_LANG_MAP.get(code)
+ if special_args is None:
+ try:
+ return Language.fromietf(code)
+ except Exception as error:
+ logger.debug("Error [%s] loading language with '%s' code", error, code)
+ return None
+
+ return Language(*special_args)
+
+
+class HDBitsProvider(Provider):
+ provider_name = "hdbits"
+
+ video_types = (Movie, Episode)
+ subtitle_class = HDBitsSubtitle
+
+ languages = {Language("por", "BR")} | {
+ Language.fromalpha2(l) for l in language_converters["alpha2"].codes
+ }
+
+ def __init__(self, username, passkey) -> None:
+ self._session = Session()
+ self._def_params = {"username": username, "passkey": passkey}
+ self._session.headers.update({"User-Agent": "Bazarr"})
+
+ def initialize(self):
+ pass
+
+ def terminate(self):
+ self._session.close()
+
+ def list_subtitles(self, video, languages):
+ episode = None
+ if isinstance(video, Movie):
+ lookup = {"imdb": {"id": (video.imdb_id or "").lstrip("tt")}}
+ matches = {"imdb_id", "title", "year"}
+ else:
+ lookup = {"tvdb": {"id": video.series_tvdb_id, "season": video.season}}
+ matches = {"tvdb_id", "imdb_id", "series", "title", "episode", "season"}
+ episode = video.episode
+
+ logger.debug("ID lookup: %s", lookup)
+
+ response = self._session.post(
+ "https://hdbits.org/api/torrents", json={**self._def_params, **lookup}
+ )
+ response.raise_for_status()
+ ids = [item["id"] for item in response.json()["data"]]
+
+ subtitles = []
+ for torrent_id in ids:
+ subtitles.extend(
+ self._parse_subtitles(torrent_id, languages, episode, matches)
+ )
+ time.sleep(0.5)
+
+ return subtitles
+
+ def _parse_subtitles(self, torrent_id, languages, episode=None, matches=None):
+ response = self._session.post(
+ "https://hdbits.org/api/subtitles",
+ json={**self._def_params, **{"torrent_id": torrent_id}},
+ )
+ subtitles = response.json()["data"]
+ parsed_subs = []
+ for subtitle in subtitles:
+ if not subtitle["filename"].endswith(_ALLOWED_EXTENSIONS):
+ logger.debug("Extension not supported: %s", subtitle["filename"])
+ continue
+
+ language = _get_language(subtitle["language"])
+ if language is None:
+ continue
+
+ if language not in languages:
+ logger.debug("Ignoring language: %r !~ %r", language, languages)
+ continue
+
+ if episode is not None:
+ guessed = _memoized_episode_guess(subtitle["title"])
+ if guessed.get("episode") is not None and episode != guessed["episode"]:
+ logger.debug(
+ "Episode not matched: %s != %s", subtitle["title"], episode
+ )
+ continue
+
+ parsed = HDBitsSubtitle(
+ language,
+ subtitle["id"],
+ subtitle["title"],
+ subtitle["filename"],
+ matches,
+ episode,
+ )
+ parsed_subs.append(parsed)
+
+ return parsed_subs
+
+ def download_subtitle(self, subtitle):
+ response = self._session.get(
+ f"https://hdbits.org/getdox.php?id={subtitle.item_id}&passkey={self._def_params['passkey']}"
+ )
+ response.raise_for_status()
+ if subtitle.filename.endswith((".zip", ".rar")):
+ archive = get_archive_from_bytes(response.content)
+ subtitle.content = get_subtitle_from_archive(
+ archive, episode=subtitle.episode
+ )
+ else:
+ subtitle.content = response.content
+
+
[email protected]_cache(2048)
+def _memoized_episode_guess(content):
+ # Use include to save time from unnecessary checks
+ return guessit(
+ content,
+ {
+ "type": "episode",
+ # Add codec keys to avoid matching x264, 5.1, etc as episode info
+ "includes": ["season", "episode", "video_codec", "audio_codec"],
+ "enforce_list": True,
+ },
+ )