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