Type series & all related types

This commit is contained in:
Zoe Roux 2025-05-08 01:59:21 +02:00
parent 356b4c0c33
commit 2f00e52857
No known key found for this signature in database
12 changed files with 167 additions and 179 deletions

View File

@ -56,7 +56,7 @@ export type MovieEntry = Prettify<typeof MovieEntry.static>;
export const SeedMovieEntry = t.Composite([
t.Omit(BaseMovieEntry, ["thumbnail", "nextRefresh"]),
t.Object({
slug: t.Optional(t.String({ format: "slug" })),
slug: t.Optional(t.Nullable(t.String({ format: "slug" }))),
thumbnail: t.Nullable(SeedImage),
translations: TranslationRecord(
t.Intersect([

View File

@ -41,7 +41,7 @@ async def identify(path: str) -> Video:
guess = Guess(
title=cast(str, title.value),
kind=cast(Literal["episode"] | Literal["movie"], kind.value),
kind=cast(Literal["episode", "movie"], kind.value),
extra_kind=None,
years=[cast(int, y.value) for y in years],
episodes=[

View File

@ -25,7 +25,7 @@ class CollectionTranslation(Model):
aliases: list[str]
tags: list[str]
posters: list[str]
thumbnails: list[str]
banner: list[str]
logos: list[str]
poster: str | None
thumbnail: str | None
banner: str | None
logo: str | None

View File

@ -0,0 +1,36 @@
from __future__ import annotations
from datetime import date
from typing import Literal
from ..utils import Model
from .metadataid import EpisodeId, MetadataId
class Entry(Model):
kind: Literal["episode", "movie", "special"]
order: float
runtime: int | None = None
air_date: date | None = None
thumbnail: str | None = None
# Movie-specific fields
slug: str | None = None
# Episode-specific fields
season_number: int | None = None
episode_number: int | None = None
# Special-specific fields
number: int | None = None
externalId: dict[str, MetadataId | EpisodeId]
translations: dict[str, EntryTranslation] = {}
videos: list[str] = []
class EntryTranslation(Model):
name: str | None = None
description: str | None = None
tagline: str | None = None
poster: str | None = None

View File

@ -1,54 +0,0 @@
from datetime import date
from dataclasses import dataclass, field, asdict
from typing import Optional
from providers.utils import select_translation
from .show import Show
from .metadataid import MetadataID
@dataclass
class PartialShow:
name: str
original_language: Optional[str]
external_id: dict[str, MetadataID]
@dataclass
class EpisodeID:
show_id: str
season: Optional[int]
episode: int
link: str
@dataclass
class EpisodeTranslation:
name: Optional[str]
overview: Optional[str] = None
@dataclass
class Episode:
show: Show | PartialShow
season_number: int
episode_number: int
absolute_number: int
runtime: Optional[int]
release_date: Optional[date | int]
thumbnail: Optional[str]
external_id: dict[str, EpisodeID]
path: Optional[str] = None
show_id: Optional[str] = None
season_id: Optional[str] = None
translations: dict[str, EpisodeTranslation] = field(default_factory=dict)
def to_kyoo(self):
trans = select_translation(self) or EpisodeTranslation("")
return {
**asdict(self),
**asdict(trans),
"show": None,
}

View File

@ -1,10 +1,21 @@
from typing import Literal
from enum import Enum
type ExtraKind = (
Literal["other"]
| Literal["trailer"]
| Literal["interview"]
| Literal["behind-the-scene"]
| Literal["deleted-scene"]
| Literal["blooper"]
)
from ..utils import Model
class ExtraKind(str, Enum):
OTHER = "other"
TRAILER = "trailer"
INTERVIEW = "interview"
BEHIND_THE_SCENE = "behind-the-scene"
DELETED_SCENE = "deleted-scene"
BLOOPER = "blooper"
class Extra(Model):
kind: ExtraKind
slug: str
name: str
runtime: int | None
thumbnail: str | None
video: str

View File

@ -13,7 +13,7 @@ from .staff import Staff
from .studio import Studio
class Status(str, Enum):
class MovieStatus(str, Enum):
UNKNOWN = "unknown"
FINISHED = "finished"
PLANNED = "planned"
@ -24,7 +24,7 @@ class Movie(Model):
original_language: Language | None
genres: list[Genre]
rating: int | None
status: Status
status: MovieStatus
runtime: int | None
air_date: date | None
@ -44,11 +44,11 @@ class MovieTranslation(Model):
aliases: list[str]
tags: list[str]
posters: list[str]
thumbnails: list[str]
banner: list[str]
logos: list[str]
trailers: list[str]
poster: str | None
thumbnail: str | None
banner: str | None
logo: str | None
trailer: str | None
class SearchMovie(Model):

View File

@ -1,38 +1,22 @@
from __future__ import annotations
from datetime import date
from dataclasses import dataclass, field, asdict
from typing import Optional
from providers.utils import select_translation, select_image
from .metadataid import MetadataID
from ..utils import Model
from .metadataid import MetadataId
@dataclass
class SeasonTranslation:
name: Optional[str] = None
overview: Optional[str] = None
posters: list[str] = field(default_factory=list)
thumbnails: list[str] = field(default_factory=list)
@dataclass
class Season:
class Season(Model):
season_number: int
# This is not used by kyoo, this is just used internaly by the TMDB provider.
# maybe this should be moved?
episodes_count: int
start_air: Optional[date | int] = None
end_air: Optional[date | int] = None
external_id: dict[str, MetadataID] = field(default_factory=dict)
start_air: date | None
end_air: date | None
external_id: dict[str, MetadataId]
translations: dict[str, SeasonTranslation] = {}
show_id: Optional[str] = None
translations: dict[str, SeasonTranslation] = field(default_factory=dict)
def to_kyoo(self):
trans = select_translation(self) or SeasonTranslation()
return {
**asdict(self),
**asdict(trans),
"poster": select_image(self, "posters"),
"thumbnail": select_image(self, "thumbnails"),
}
class SeasonTranslation(Model):
name: str | None
description: str | None
poster: str | None
thumbnail: str | None
banner: str | None

View File

@ -0,0 +1,69 @@
from __future__ import annotations
from datetime import date
from enum import Enum
from langcodes import Language
from ..utils import Model
from .collection import Collection
from .entry import Entry
from .extra import Extra
from .genre import Genre
from .metadataid import MetadataId
from .season import Season
from .staff import Staff
from .studio import Studio
class SerieStatus(str, Enum):
UNKNOWN = "unknown"
FINISHED = "finished"
AIRING = "airing"
PLANNED = "planned"
class Serie(Model):
slug: str
original_language: Language | None
genres: list[Genre]
rating: int | None
status: SerieStatus
runtime: int | None
start_air: date | None
end_air: date | None
external_id: dict[str, MetadataId]
translations: dict[str, SerieTranslation] = {}
seasons: list[Season] = []
entries: list[Entry] = []
extra: list[Extra] = []
collections: list[Collection] = []
studios: list[Studio] = []
staff: list[Staff] = []
class SerieTranslation(Model):
name: str
latin_name: str | None
description: str | None
tagline: str | None
aliases: list[str]
tags: list[str]
poster: str | None
thumbnail: str | None
banner: str | None
logo: str | None
trailer: str | None
class SearchSerie(Model):
slug: str
name: str
description: str | None
start_air: date | None
end_air: date | None
poster: str
original_language: Language | None
external_id: dict[str, MetadataId]

View File

@ -1,67 +0,0 @@
from dataclasses import asdict, dataclass, field
from datetime import date
from typing import Optional
from enum import Enum
from providers.utils import select_translation, select_image
from .genre import Genre
from .studio import Studio
from .season import Season
from .metadataid import MetadataID
class Status(str, Enum):
UNKNOWN = "unknown"
FINISHED = "finished"
AIRING = "airing"
PLANNED = "planned"
@dataclass
class ShowTranslation:
name: str
tagline: Optional[str] = None
tags: list[str] = field(default_factory=list)
overview: Optional[str] = None
posters: list[str] = field(default_factory=list)
logos: list[str] = field(default_factory=list)
trailers: list[str] = field(default_factory=list)
thumbnails: list[str] = field(default_factory=list)
@dataclass
class Show:
original_language: Optional[str]
aliases: list[str]
start_air: Optional[date | int]
end_air: Optional[date | int]
status: Status
rating: Optional[int]
studios: list[Studio]
genres: list[Genre]
seasons: list[Season]
# TODO: handle staff
# staff: list[Staff]
external_id: dict[str, MetadataID]
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):
trans = select_translation(self) or ShowTranslation(name=self.file_title or "")
return {
**asdict(self),
**asdict(trans),
"rating": self.rating or 0,
"studio": next((x.to_kyoo() for x in self.studios), None),
"seasons": None,
"poster": select_image(self, "posters"),
"thumbnail": select_image(self, "thumbnails"),
"logo": select_image(self, "logos"),
"trailer": select_image(self, "trailers"),
"genres": [x.to_kyoo() for x in self.genres],
"file_title": None,
}

View File

@ -38,7 +38,9 @@ class CompositeProvider(Provider):
raise ProviderError(
f"Couldn't find a movie with title {title}. (year: {year}"
)
ret = await self.get_movie(search[0].external_id)
ret = await self.get_movie(
{k: v.data_id for k, v in search[0].external_id.items()}
)
if not ret:
raise ValueError()
return ret
@ -68,4 +70,9 @@ class CompositeProvider(Provider):
raise ProviderError(
f"Couldn't find a serie with title {title}. (year: {year}"
)
return await self.get_serie(search[0].external_id)
ret = await self.get_serie(
{k: v.data_id for k, v in search[0].external_id.items()}
)
if not ret:
raise ValueError()
return ret

View File

@ -4,12 +4,12 @@ from typing import Literal
from .client import KyooClient
from .models.videos import Guess
from .utils import Model
from .providers.composite import CompositeProvider
from .utils import Model
class Request(Model):
kind: Literal["episode"] | Literal["movie"]
kind: Literal["episode", "movie"]
title: str
year: int | None
external_id: dict[str, str]
@ -39,7 +39,9 @@ class RequestProcessor:
request: Request = ...
if request.kind == "movie":
movie = await providers.get_movie(request.title, request.year, request.external_id)
movie = await providers.get_movie(
request.title, request.year, request.external_id
)
movie.videos = request.videos
await self._client.create_movie(movie)
else: