mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-07-09 03:04:20 -04:00
Handle missing translations in scanner (#493)
This commit is contained in:
commit
465562b563
@ -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())
|
||||||
|
@ -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),
|
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
|
@ -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
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
)
|
)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user