Handle crew & fix some themoviedb bugs

This commit is contained in:
Zoe Roux 2025-05-20 12:18:23 +02:00
parent 9ef53d06bd
commit d9a1fd00ed
No known key found for this signature in database
11 changed files with 1930 additions and 35 deletions

View File

@ -0,0 +1 @@
ALTER TYPE "kyoo"."role_kind" ADD VALUE 'crew' BEFORE 'other';

File diff suppressed because it is too large Load Diff

View File

@ -148,6 +148,13 @@
"when": 1746198322219,
"tag": "0020_video_unique",
"breakpoints": true
},
{
"idx": 21,
"version": "7",
"when": 1747727831649,
"tag": "0021_crew",
"breakpoints": true
}
]
}

View File

@ -19,6 +19,7 @@ export const roleKind = schema.enum("role_kind", [
"writter",
"producer",
"music",
"crew",
"other",
]);

View File

@ -16,6 +16,7 @@ export const Role = t.Object({
"writter",
"producer",
"music",
"crew",
"other",
]),
character: t.Nullable(Character),

View File

@ -124,7 +124,7 @@ services:
- "traefik.http.middlewares.phantom-token.forwardauth.address=http://auth:4568/auth/jwt"
- "traefik.http.middlewares.phantom-token.forwardauth.authRequestHeaders=Authorization,X-Api-Key"
- "traefik.http.middlewares.phantom-token.forwardauth.authResponseHeaders=Authorization"
command: fastapi dev scanner --host 0.0.0.0 --port 3489
command: fastapi dev scanner --host 0.0.0.0 --port 4389
develop:
watch:
- action: sync

View File

@ -7,6 +7,7 @@ from typing import Optional, Any, Callable, OrderedDict
from langcodes import Language
from matcher.cache import cache
from scanner.models.staff import Role
from ..provider import Provider, ProviderError
from ..utils import normalize_lang
@ -75,6 +76,19 @@ class TVDB(Provider):
"martial-arts": None,
"awards-show": None,
}
self._roles_map = {
"Actor": Role.ACTOR,
"Creator": Role.OTHER,
"Crew": Role.CREW,
"Director": Role.DIRECTOR,
"Executive Producer": Role.OTHER,
"Guest Star": Role.OTHER,
"Host": Role.OTHER,
"Musical Guest": Role.MUSIC,
"Producer": Role.PRODUCER,
"Showrunner": Role.OTHER,
"Writer": Role.WRITTER,
}
@cache(ttl=timedelta(days=30))
async def login(self) -> str:

View File

@ -58,6 +58,7 @@ class KyooClient(metaclass=Singleton):
r.raise_for_status()
async def create_movie(self, movie: Movie) -> Resource:
logger.debug("sending movie %s", movie.model_dump_json())
async with self._client.post(
"movies",
json=movie.model_dump_json(),
@ -66,6 +67,7 @@ class KyooClient(metaclass=Singleton):
return Resource(**await r.json())
async def create_serie(self, serie: Serie) -> Resource:
logger.debug("sending serie %s", serie.model_dump_json())
async with self._client.post(
"series",
json=serie.model_dump_json(),

View File

@ -12,6 +12,7 @@ class Role(StrEnum):
WRITTER = "writter"
PRODUCER = "producer"
MUSIC = "music"
CREW = "crew"
OTHER = "other"

View File

@ -69,6 +69,20 @@ class TheMovieDatabase(Provider):
10767: Genre.TALK,
10768: [Genre.WAR, Genre.POLITICS],
}
self._roles_map = {
"Camera": Role.OTHER,
"Costume & Make-Up": Role.OTHER,
"Lighting": Role.OTHER,
"Art": Role.OTHER,
"Visual Effects": Role.OTHER,
"Crew": Role.CREW,
"Writing": Role.WRITTER,
"Production": Role.PRODUCER,
"Editing": Role.OTHER,
"Directing": Role.DIRECTOR,
"Sound": Role.MUSIC,
"Actors": Role.ACTOR,
}
async def __aenter__(self):
return self
@ -169,7 +183,7 @@ class TheMovieDatabase(Provider):
Language.get(
f"{trans['iso_639_1']}-{trans['iso_3166_1']}"
): MovieTranslation(
name=clean(trans["data"]["title"])
name=clean(trans["data"]["name"])
or (
clean(movie["original_title"])
if movie["original_language"] == trans["iso_639_1"]
@ -314,17 +328,17 @@ class TheMovieDatabase(Provider):
Language.get(
f"{trans['iso_639_1']}-{trans['iso_3166_1']}"
): SerieTranslation(
name=clean(trans["data"]["title"])
name=clean(trans["data"]["name"])
or (
clean(serie["original_title"])
clean(serie["original_name"])
if serie["original_language"] == trans["iso_639_1"]
else None
)
or serie["title"],
or serie["name"],
latin_name=next(
(
x["title"]
for x in serie["alternative_titles"]["titles"]
for x in serie["alternative_titles"]["results"]
if x["iso_3166_1"] == trans["iso_3166_1"]
and x["type"] == "Romaji"
),
@ -334,10 +348,10 @@ class TheMovieDatabase(Provider):
tagline=clean(trans["data"]["tagline"]),
aliases=[
x["title"]
for x in serie["alternative_titles"]["titles"]
for x in serie["alternative_titles"]["results"]
if x["iso_3166_1"] == trans["iso_3166_1"]
],
tags=[x["name"] for x in serie["keywords"]["keywords"]],
tags=[x["name"] for x in serie["keywords"]["results"]],
poster=self._pick_image(serie, trans["iso_639_1"], "posters"),
logo=self._pick_image(serie, trans["iso_639_1"], "logos"),
banner=None,
@ -366,7 +380,7 @@ class TheMovieDatabase(Provider):
staff=[self._map_staff(x) for x in serie["credits"]["cast"]],
)
async def _get_season(self, serie_id: str, season_number: int) -> Season:
async def _get_season(self, serie_id: str | int, season_number: int) -> Season:
season = await self._get(
f"tv/{serie_id}/season/{season_number}",
params={
@ -383,7 +397,7 @@ class TheMovieDatabase(Provider):
end_air=None,
external_id={
self.name: SeasonId(
serie_id=serie_id,
serie_id=str(serie_id),
season=season["season_number"],
link=f"https://www.themoviedb.org/tv/{serie_id}/season/{season['season_number']}",
)
@ -403,7 +417,7 @@ class TheMovieDatabase(Provider):
)
async def _get_all_entries(
self, serie_id: str, seasons: list[dict[str, Any]]
self, serie_id: str | int, seasons: list[dict[str, Any]]
) -> list[Entry]:
# TODO: batch those
ret = await asyncio.gather(
@ -496,7 +510,7 @@ class TheMovieDatabase(Provider):
return ret
async def _get_entry(self, serie_id: str, season: int, episode_nbr: int) -> Entry:
async def _get_entry(self, serie_id: str | int, season: int, episode_nbr: int) -> Entry:
episode = await self._get(
f"tv/{serie_id}/season/{season}/episode/{episode_nbr}",
params={
@ -519,7 +533,7 @@ class TheMovieDatabase(Provider):
number=episode["episode_number"],
external_id={
self.name: EpisodeId(
serie_id=serie_id,
serie_id=str(serie_id),
season=episode["season_number"],
episode=episode["episode_number"],
link=f"https://www.themoviedb.org/tv/{serie_id}/season/{episode['season_number']}/episode/{episode['episode_number']}",
@ -538,7 +552,7 @@ class TheMovieDatabase(Provider):
},
)
async def _get_collection(self, provider_id: str) -> Collection:
async def _get_collection(self, provider_id: str | int) -> Collection:
collection = await self._get(
f"collection/{provider_id}",
params={
@ -567,7 +581,7 @@ class TheMovieDatabase(Provider):
Language.get(
f"{trans['iso_639_1']}-{trans['iso_3166_1']}"
): CollectionTranslation(
name=clean(trans["data"]["title"]) or collection["title"],
name=clean(trans["data"]["name"]) or collection["name"],
latin_name=None,
description=trans["overview"],
tagline=None,
@ -663,8 +677,7 @@ class TheMovieDatabase(Provider):
def _map_staff(self, person: dict[str, Any]) -> Staff:
return Staff(
# TODO: map those to Role (see https://developer.themoviedb.org/reference/configuration-jobs for list)
kind=person["known_for_department"],
kind=self._roles_map.get(person["known_for_department"], Role.OTHER),
character=Character(
name=person["character"],
latin_name=None,

View File

@ -77,13 +77,11 @@ class RequestProcessor:
_ = tg.create_task(self.process_all())
def terminated(*_):
logger.info("terminated")
closed.set()
while True:
closed.clear()
logger.info("aquire")
try:
# TODO: unsure if timeout actually work, i think not...
async with self._pool.acquire(timeout=10) as db:
try:
self._database = cast(Connection, db)
@ -98,8 +96,6 @@ class RequestProcessor:
await self._database.remove_listener("scanner_requests", process)
self._database.remove_termination_listener(terminated)
raise
except TimeoutError:
logger.info("temiout")
async def process_all(self):
found = True