Handle missing translations in scanner (#493)

This commit is contained in:
Zoe Roux 2024-05-15 00:10:10 +02:00 committed by GitHub
commit 465562b563
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 96 additions and 61 deletions

View File

@ -87,6 +87,7 @@ class Matcher:
async def search_movie(self, title: str, year: Optional[int], path: str): async def search_movie(self, title: str, year: Optional[int], path: str):
movie = await self._provider.search_movie(title, year) movie = await self._provider.search_movie(title, year)
movie.file_title = title
movie.path = path movie.path = path
logger.debug("Got movie: %s", movie) logger.debug("Got movie: %s", movie)
movie_id = await self._client.post("movies", data=movie.to_kyoo()) movie_id = await self._client.post("movies", data=movie.to_kyoo())
@ -116,7 +117,7 @@ class Matcher:
) )
episode.path = path episode.path = path
logger.debug("Got episode: %s", episode) logger.debug("Got episode: %s", episode)
episode.show_id = await self.create_or_get_show(episode) episode.show_id = await self.create_or_get_show(episode, title)
if episode.season_number is not None: if episode.season_number is not None:
episode.season_id = await self.register_seasons( episode.season_id = await self.register_seasons(
@ -140,7 +141,7 @@ class Matcher:
provider_id = collection.external_id[self._provider.name].data_id provider_id = collection.external_id[self._provider.name].data_id
return await create_collection(provider_id) return await create_collection(provider_id)
async def create_or_get_show(self, episode: Episode) -> str: async def create_or_get_show(self, episode: Episode, fallback_name: str) -> str:
@cache(ttl=timedelta(days=1), cache=self._show_cache) @cache(ttl=timedelta(days=1), cache=self._show_cache)
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.
@ -151,6 +152,7 @@ class Matcher:
if isinstance(episode.show, PartialShow) if isinstance(episode.show, PartialShow)
else episode.show else episode.show
) )
show.file_title = fallback_name
# TODO: collections # TODO: collections
logger.debug("Got show: %s", episode) logger.debug("Got show: %s", episode)
ret = await self._client.post("show", data=show.to_kyoo()) ret = await self._client.post("show", data=show.to_kyoo())

View File

@ -1,7 +1,8 @@
import os
from dataclasses import asdict, dataclass, field from dataclasses import asdict, dataclass, field
from typing import Optional from typing import Optional
from providers.utils import ProviderError, select_translation, select_image
from .metadataid import MetadataID from .metadataid import MetadataID
@ -20,14 +21,15 @@ class Collection:
translations: dict[str, CollectionTranslation] = field(default_factory=dict) translations: dict[str, CollectionTranslation] = field(default_factory=dict)
def to_kyoo(self): def to_kyoo(self):
# For now, the API of kyoo only support one language so we remove the others. trans = select_translation(self)
default_language = os.environ["LIBRARY_LANGUAGES"].split(",")[0] if trans is None:
raise ProviderError(
"Could not find translations for the collection. Aborting"
)
return { return {
**asdict(self), **asdict(self),
**asdict(self.translations[default_language]), **asdict(trans),
"poster": next(iter(self.translations[default_language].posters), None), "poster": select_image(self, "posters"),
"thumbnail": next( "thumbnail": select_image(self, "thumbnails"),
iter(self.translations[default_language].thumbnails), None "logo": select_image(self, "logos"),
),
"logo": next(iter(self.translations[default_language].logos), None),
} }

View File

@ -1,8 +1,9 @@
import os
from datetime import date from datetime import date
from dataclasses import dataclass, field, asdict from dataclasses import dataclass, field, asdict
from typing import Optional from typing import Optional
from providers.utils import select_translation
from .show import Show from .show import Show
from .metadataid import MetadataID from .metadataid import MetadataID
@ -24,8 +25,8 @@ class EpisodeID:
@dataclass @dataclass
class EpisodeTranslation: class EpisodeTranslation:
name: str name: Optional[str]
overview: Optional[str] overview: Optional[str] = None
@dataclass @dataclass
@ -45,10 +46,9 @@ class Episode:
translations: dict[str, EpisodeTranslation] = field(default_factory=dict) translations: dict[str, EpisodeTranslation] = field(default_factory=dict)
def to_kyoo(self): def to_kyoo(self):
# For now, the API of kyoo only support one language so we remove the others. trans = select_translation(self) or EpisodeTranslation("")
default_language = os.environ["LIBRARY_LANGUAGES"].split(",")[0]
return { return {
**asdict(self), **asdict(self),
**asdict(self.translations[default_language]), **asdict(trans),
"show": None, "show": None,
} }

View File

@ -1,9 +1,10 @@
import os
from dataclasses import asdict, dataclass, field from dataclasses import asdict, dataclass, field
from datetime import date from datetime import date
from typing import Optional from typing import Optional
from enum import Enum from enum import Enum
from providers.utils import select_translation, select_image
from .collection import Collection from .collection import Collection
from .genre import Genre from .genre import Genre
from .studio import Studio from .studio import Studio
@ -44,22 +45,22 @@ class Movie:
external_id: dict[str, MetadataID] external_id: dict[str, MetadataID]
path: Optional[str] = None path: Optional[str] = None
# The title of this show according to it's filename (None only for ease of use in providers)
file_title: Optional[str] = None
collections: list[Collection] = field(default_factory=list) collections: list[Collection] = field(default_factory=list)
translations: dict[str, MovieTranslation] = field(default_factory=dict) translations: dict[str, MovieTranslation] = field(default_factory=dict)
def to_kyoo(self): def to_kyoo(self):
from ..utils import select_image trans = select_translation(self) or MovieTranslation(name=self.file_title or "")
# For now, the API of kyoo only support one language so we remove the others.
default_language = os.environ["LIBRARY_LANGUAGES"].split(",")[0]
return { return {
**asdict(self), **asdict(self),
**asdict(self.translations[default_language]), **asdict(trans),
"poster": select_image(self, "posters"), "poster": select_image(self, "posters"),
"thumbnail": select_image(self, "thumbnails"), "thumbnail": select_image(self, "thumbnails"),
"logo": select_image(self, "logos"), "logo": select_image(self, "logos"),
"trailer": next(iter(self.translations[default_language].trailers), None), "trailer": next(iter(trans.trailers), None),
"studio": next((x.to_kyoo() for x in self.studios), None), "studio": next((x.to_kyoo() for x in self.studios), None),
"genres": [x.to_kyoo() for x in self.genres], "genres": [x.to_kyoo() for x in self.genres],
"collections": None, "collections": None,
"file_title": None,
} }

View File

@ -1,8 +1,9 @@
import os
from datetime import date from datetime import date
from dataclasses import dataclass, field, asdict from dataclasses import dataclass, field, asdict
from typing import Optional from typing import Optional
from providers.utils import select_translation, select_image
from .metadataid import MetadataID from .metadataid import MetadataID
@ -28,13 +29,10 @@ class Season:
translations: dict[str, SeasonTranslation] = field(default_factory=dict) translations: dict[str, SeasonTranslation] = field(default_factory=dict)
def to_kyoo(self): def to_kyoo(self):
# For now, the API of kyoo only support one language so we remove the others. trans = select_translation(self) or SeasonTranslation()
default_language = os.environ["LIBRARY_LANGUAGES"].split(",")[0]
return { return {
**asdict(self), **asdict(self),
**asdict(self.translations[default_language]), **asdict(trans),
"poster": next(iter(self.translations[default_language].posters), None), "poster": select_image(self, "posters"),
"thumbnail": next( "thumbnail": select_image(self, "thumbnails"),
iter(self.translations[default_language].thumbnails), None
),
} }

View File

@ -1,9 +1,10 @@
import os
from dataclasses import asdict, dataclass, field from dataclasses import asdict, dataclass, field
from datetime import date from datetime import date
from typing import Optional from typing import Optional
from enum import Enum from enum import Enum
from providers.utils import select_translation, select_image
from .genre import Genre from .genre import Genre
from .studio import Studio from .studio import Studio
from .season import Season from .season import Season
@ -20,14 +21,14 @@ class Status(str, Enum):
@dataclass @dataclass
class ShowTranslation: class ShowTranslation:
name: str name: str
tagline: Optional[str] tagline: Optional[str] = None
tags: list[str] tags: list[str] = field(default_factory=list)
overview: Optional[str] overview: Optional[str] = None
posters: list[str] posters: list[str] = field(default_factory=list)
logos: list[str] logos: list[str] = field(default_factory=list)
trailers: list[str] trailers: list[str] = field(default_factory=list)
thumbnails: list[str] thumbnails: list[str] = field(default_factory=list)
@dataclass @dataclass
@ -46,20 +47,21 @@ class Show:
external_id: dict[str, MetadataID] external_id: dict[str, MetadataID]
translations: dict[str, ShowTranslation] = field(default_factory=dict) translations: dict[str, ShowTranslation] = field(default_factory=dict)
# The title of this show according to it's filename (None only for ease of use in providers)
file_title: Optional[str] = None
def to_kyoo(self): def to_kyoo(self):
from providers.utils import select_image trans = select_translation(self) or ShowTranslation(name=self.file_title or "")
# For now, the API of kyoo only support one language so we remove the others.
default_language = os.environ["LIBRARY_LANGUAGES"].split(",")[0]
return { return {
**asdict(self), **asdict(self),
**asdict(self.translations[default_language]), **asdict(trans),
"rating": self.rating or 0,
"studio": next((x.to_kyoo() for x in self.studios), None), "studio": next((x.to_kyoo() for x in self.studios), None),
"seasons": None, "seasons": None,
"poster": select_image(self, "posters"), "poster": select_image(self, "posters"),
"thumbnail": select_image(self, "thumbnails"), "thumbnail": select_image(self, "thumbnails"),
"logo": select_image(self, "logos"), "logo": select_image(self, "logos"),
"trailer": next(iter(self.translations[default_language].trailers), None), "trailer": next(iter(trans.trailers), None),
"genres": [x.to_kyoo() for x in self.genres], "genres": [x.to_kyoo() for x in self.genres],
"file_title": None,
} }

View File

@ -1,11 +1,16 @@
from __future__ import annotations
import os import os
from datetime import date from datetime import date
from itertools import chain from itertools import chain
from typing import TYPE_CHECKING, Literal, Any, Optional
from typing import Literal if TYPE_CHECKING:
from providers.types.movie import Movie
from providers.types.movie import Movie from providers.types.show import Show
from providers.types.show import Show from providers.types.season import Season
from providers.types.episode import Episode
from providers.types.collection import Collection
def format_date(date: date | int | None) -> str | None: def format_date(date: date | int | None) -> str | None:
@ -16,21 +21,46 @@ def format_date(date: date | int | None) -> str | None:
return date.isoformat() return date.isoformat()
# For now, the API of kyoo only support one language so we remove the others.
default_languages = os.environ["LIBRARY_LANGUAGES"].split(",")
def sort_translations(
value: Movie | Show | Season | Episode | Collection,
*,
prefer_orginal=False,
):
from providers.types.movie import Movie
from providers.types.show import Show
if (
prefer_orginal
and (isinstance(value, Movie) or isinstance(value, Show))
and value.original_language
and value.original_language in value.translations
):
yield value.translations[value.original_language]
for lang in default_languages:
if lang in value.translations:
yield value.translations[lang]
def select_translation(
value: Movie | Show | Season | Episode | Collection, *, prefer_orginal=False
) -> Optional[Any]:
return next(sort_translations(value, prefer_orginal=prefer_orginal), None)
def select_image( def select_image(
self: Movie | Show, value: Movie | Show | Season | Collection,
type: Literal["posters"] | Literal["thumbnails"] | Literal["logos"], kind: Literal["posters"] | Literal["thumbnails"] | Literal["logos"],
) -> str | None: ) -> str | None:
# For now, the API of kyoo only support one language so we remove the others.
default_language = os.environ["LIBRARY_LANGUAGES"].split(",")[0]
return next( return next(
chain( chain(
( *(
getattr(self.translations[self.original_language], type) getattr(trans, kind)
if self.original_language for trans in sort_translations(value, prefer_orginal=True)
else [] )
),
getattr(self.translations[default_language], type),
*(getattr(x, type) for x in self.translations.values()),
), ),
None, None,
) )