Add a basic id mapper to retrive tvdb id from a tmdb id

This commit is contained in:
Zoe Roux 2024-01-05 12:40:28 +01:00
parent 91c88e7f43
commit b3ddac60f6
5 changed files with 99 additions and 28 deletions

View 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

View File

@ -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,

View File

@ -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

View File

@ -9,7 +9,6 @@ from .metadataid import MetadataID
@dataclass
class PartialShow:
name: str
original_language: str
external_id: dict[str, MetadataID]

View File

@ -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
)