mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-05-24 02:02:36 -04:00
Add a basic id mapper to retrive tvdb id from a tmdb id
This commit is contained in:
parent
91c88e7f43
commit
b3ddac60f6
36
scanner/providers/idmapper.py
Normal file
36
scanner/providers/idmapper.py
Normal file
@ -0,0 +1,36 @@
|
||||
from __future__ import annotations
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from providers.implementations.themoviedatabase import TheMovieDatabase
|
||||
|
||||
from typing import List, Optional
|
||||
from providers.types.metadataid import MetadataID
|
||||
|
||||
|
||||
class IdMapper:
|
||||
def init(self, *, language: str, tmdb: Optional[TheMovieDatabase]):
|
||||
self.language = language
|
||||
self._tmdb = tmdb
|
||||
|
||||
async def get_show(
|
||||
self, show: dict[str, MetadataID], *, required: Optional[List[str]] = None
|
||||
):
|
||||
ids = show
|
||||
|
||||
# Only fetch using tmdb if one of the required ids is not already known.
|
||||
should_fetch = required is not None and any((x not in ids for x in required))
|
||||
if self._tmdb and self._tmdb.name in ids and should_fetch:
|
||||
tmdb_info = await self._tmdb.identify_show(
|
||||
ids[self._tmdb.name].data_id,
|
||||
original_language=None,
|
||||
language=[self.language],
|
||||
)
|
||||
return {**ids, **tmdb_info.external_id}
|
||||
return ids
|
||||
|
||||
async def get_movie(
|
||||
self, movie: dict[str, MetadataID], *, required: Optional[List[str]] = None
|
||||
):
|
||||
# TODO: actually do something here
|
||||
return movie
|
@ -3,6 +3,7 @@ import logging
|
||||
from aiohttp import ClientSession
|
||||
from datetime import datetime
|
||||
from typing import Awaitable, Callable, Dict, List, Optional, Any, TypeVar
|
||||
from providers.idmapper import IdMapper
|
||||
from providers.implementations.thexem import TheXem
|
||||
|
||||
from providers.utils import ProviderError
|
||||
@ -19,10 +20,13 @@ from ..types.collection import Collection, CollectionTranslation
|
||||
|
||||
|
||||
class TheMovieDatabase(Provider):
|
||||
def __init__(self, client: ClientSession, api_key: str, xem: TheXem) -> None:
|
||||
def __init__(
|
||||
self, client: ClientSession, api_key: str, xem: TheXem, idmapper: IdMapper
|
||||
) -> None:
|
||||
super().__init__()
|
||||
self._client = client
|
||||
self._xem = xem
|
||||
self._idmapper = idmapper
|
||||
self.base = "https://api.themoviedb.org/3"
|
||||
self.api_key = api_key
|
||||
self.genre_map = {
|
||||
@ -212,17 +216,20 @@ class TheMovieDatabase(Provider):
|
||||
ret.translations = {lng: translation}
|
||||
return ret
|
||||
|
||||
return await self.process_translations(for_language, language)
|
||||
ret = await self.process_translations(for_language, language)
|
||||
# If we have more external_ids freely available, add them.
|
||||
ret.external_id = await self._idmapper.get_movie(ret.external_id)
|
||||
return ret
|
||||
|
||||
async def identify_show(
|
||||
self,
|
||||
pshow: PartialShow,
|
||||
show_id: str,
|
||||
*,
|
||||
original_language: Optional[str],
|
||||
language: list[str],
|
||||
) -> Show:
|
||||
show_id = pshow.external_id[self.name].data_id
|
||||
if pshow.original_language not in language:
|
||||
language.append(pshow.original_language)
|
||||
if original_language and original_language not in language:
|
||||
language.append(original_language)
|
||||
|
||||
async def for_language(lng: str) -> Show:
|
||||
show = await self.get(
|
||||
@ -290,7 +297,7 @@ class TheMovieDatabase(Provider):
|
||||
show["images"]["posters"]
|
||||
+ (
|
||||
[{"file_path": show["poster_path"]}]
|
||||
if lng == pshow.original_language
|
||||
if lng == show["original_language"]
|
||||
else []
|
||||
)
|
||||
),
|
||||
@ -299,7 +306,7 @@ class TheMovieDatabase(Provider):
|
||||
show["images"]["backdrops"]
|
||||
+ (
|
||||
[{"file_path": show["backdrop_path"]}]
|
||||
if lng == pshow.original_language
|
||||
if lng == show["original_language"]
|
||||
else []
|
||||
)
|
||||
),
|
||||
@ -333,6 +340,8 @@ class TheMovieDatabase(Provider):
|
||||
ret = await self.process_translations(
|
||||
for_language, language, merge_seasons_translations
|
||||
)
|
||||
# If we have more external_ids freely available, add them.
|
||||
ret.external_id = await self._idmapper.get_show(ret.external_id)
|
||||
return ret
|
||||
|
||||
def to_season(
|
||||
@ -381,6 +390,14 @@ class TheMovieDatabase(Provider):
|
||||
raise ProviderError(f"No result for a tv show named: {name}")
|
||||
search = self.get_best_result(search_results, name, year)
|
||||
show_id = search["id"]
|
||||
show = PartialShow(
|
||||
original_language=search["original_language"],
|
||||
external_id={
|
||||
self.name: MetadataID(
|
||||
show_id, f"https://www.themoviedb.org/tv/{show_id}"
|
||||
)
|
||||
},
|
||||
)
|
||||
if search["original_language"] not in language:
|
||||
language.append(search["original_language"])
|
||||
|
||||
@ -388,10 +405,18 @@ class TheMovieDatabase(Provider):
|
||||
# For example when name is "Jojo's bizzare adventure - Stone Ocean", with season None,
|
||||
# We want something like season 6 ep 3.
|
||||
if season is None and absolute is not None:
|
||||
(tvdb_season, tvdb_episode, absolute) = await self._xem.get_episode_override("tvdb", tvdbid, name, absolute)
|
||||
# Most of the time, tvdb absolute and tmdb absolute are in think so we use that as our souce of truth.
|
||||
# tvdb_season/episode are not in sync with tmdb so we discard those and use our usual absolute order fetching.
|
||||
(_, _) = tvdb_season, tvdb_episode
|
||||
ids = await self._idmapper.get_show(show.external_id, required=["tmdbid"])
|
||||
if ids["tvdb"] is not None:
|
||||
(
|
||||
tvdb_season,
|
||||
tvdb_episode,
|
||||
absolute,
|
||||
) = await self._xem.get_episode_override(
|
||||
"tvdb", ids["tvdb"].data_id, name, absolute
|
||||
)
|
||||
# Most of the time, tvdb absolute and tmdb absolute are in think so we use that as our souce of truth.
|
||||
# tvdb_season/episode are not in sync with tmdb so we discard those and use our usual absolute order fetching.
|
||||
(_, _) = tvdb_season, tvdb_episode
|
||||
|
||||
if not show_id in self.absolute_episode_cache:
|
||||
await self.get_absolute_order(show_id)
|
||||
@ -441,15 +466,7 @@ class TheMovieDatabase(Provider):
|
||||
logging.debug("TMDb responded: %s", episode)
|
||||
|
||||
ret = Episode(
|
||||
show=PartialShow(
|
||||
name=search["name"],
|
||||
original_language=search["original_language"],
|
||||
external_id={
|
||||
self.name: MetadataID(
|
||||
show_id, f"https://www.themoviedb.org/tv/{show_id}"
|
||||
)
|
||||
},
|
||||
),
|
||||
show=show,
|
||||
season_number=episode["season_number"],
|
||||
episode_number=episode["episode_number"],
|
||||
absolute_number=absolute,
|
||||
|
@ -5,7 +5,7 @@ from typing import Optional, TypeVar
|
||||
|
||||
from providers.utils import ProviderError
|
||||
|
||||
from .types.episode import Episode, PartialShow
|
||||
from .types.episode import Episode
|
||||
from .types.show import Show
|
||||
from .types.movie import Movie
|
||||
from .types.collection import Collection
|
||||
@ -16,22 +16,35 @@ Self = TypeVar("Self", bound="Provider")
|
||||
|
||||
class Provider:
|
||||
@classmethod
|
||||
def get_all(cls: type[Self], client: ClientSession) -> list[Self]:
|
||||
def get_all(
|
||||
cls: type[Self], client: ClientSession, languages: list[str]
|
||||
) -> list[Self]:
|
||||
providers = []
|
||||
|
||||
from providers.idmapper import IdMapper
|
||||
|
||||
idmapper = IdMapper()
|
||||
|
||||
from providers.implementations.thexem import TheXem
|
||||
|
||||
xem = TheXem(client)
|
||||
|
||||
from providers.implementations.themoviedatabase import TheMovieDatabase
|
||||
|
||||
tmdb = os.environ.get("THEMOVIEDB_APIKEY")
|
||||
if tmdb:
|
||||
providers.append(TheMovieDatabase(client, tmdb, xem))
|
||||
tmdb = TheMovieDatabase(client, tmdb, xem, idmapper)
|
||||
providers.append(tmdb)
|
||||
else:
|
||||
tmdb = None
|
||||
|
||||
if not any(providers):
|
||||
raise ProviderError(
|
||||
"No provider configured. You probably forgot to specify an API Key"
|
||||
)
|
||||
|
||||
idmapper.init(tmdb=tmdb, language=languages[0])
|
||||
|
||||
return providers
|
||||
|
||||
@abstractproperty
|
||||
@ -45,7 +58,9 @@ class Provider:
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
async def identify_show(self, show: PartialShow, *, language: list[str]) -> Show:
|
||||
async def identify_show(
|
||||
self, show_id: str, *, original_language: Optional[str], language: list[str]
|
||||
) -> Show:
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
|
@ -9,7 +9,6 @@ from .metadataid import MetadataID
|
||||
|
||||
@dataclass
|
||||
class PartialShow:
|
||||
name: str
|
||||
original_language: str
|
||||
external_id: dict[str, MetadataID]
|
||||
|
||||
|
@ -28,7 +28,7 @@ class Scanner:
|
||||
except Exception as e:
|
||||
self._ignore_pattern = re.compile("")
|
||||
logging.error(f"Invalid ignore pattern. Ignoring. Error: {e}")
|
||||
self.provider = Provider.get_all(client)[0]
|
||||
self.provider = Provider.get_all(client, languages)[0]
|
||||
self.cache = {"shows": {}, "seasons": {}, "collections": {}}
|
||||
self.languages = languages
|
||||
|
||||
@ -156,7 +156,11 @@ class Scanner:
|
||||
async def create_show(_: str):
|
||||
# TODO: Check if a show with the same metadata id exists already on kyoo.
|
||||
show = (
|
||||
await self.provider.identify_show(episode.show, language=self.languages)
|
||||
await self.provider.identify_show(
|
||||
episode.show.external_id[self.provider.name].data_id,
|
||||
original_language=episode.show.original_language,
|
||||
language=self.languages,
|
||||
)
|
||||
if isinstance(episode.show, PartialShow)
|
||||
else episode.show
|
||||
)
|
||||
|
Loading…
x
Reference in New Issue
Block a user