mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-05-31 04:04:21 -04:00
Type series & all related types
This commit is contained in:
parent
356b4c0c33
commit
2f00e52857
@ -56,7 +56,7 @@ export type MovieEntry = Prettify<typeof MovieEntry.static>;
|
|||||||
export const SeedMovieEntry = t.Composite([
|
export const SeedMovieEntry = t.Composite([
|
||||||
t.Omit(BaseMovieEntry, ["thumbnail", "nextRefresh"]),
|
t.Omit(BaseMovieEntry, ["thumbnail", "nextRefresh"]),
|
||||||
t.Object({
|
t.Object({
|
||||||
slug: t.Optional(t.String({ format: "slug" })),
|
slug: t.Optional(t.Nullable(t.String({ format: "slug" }))),
|
||||||
thumbnail: t.Nullable(SeedImage),
|
thumbnail: t.Nullable(SeedImage),
|
||||||
translations: TranslationRecord(
|
translations: TranslationRecord(
|
||||||
t.Intersect([
|
t.Intersect([
|
||||||
|
@ -41,7 +41,7 @@ async def identify(path: str) -> Video:
|
|||||||
|
|
||||||
guess = Guess(
|
guess = Guess(
|
||||||
title=cast(str, title.value),
|
title=cast(str, title.value),
|
||||||
kind=cast(Literal["episode"] | Literal["movie"], kind.value),
|
kind=cast(Literal["episode", "movie"], kind.value),
|
||||||
extra_kind=None,
|
extra_kind=None,
|
||||||
years=[cast(int, y.value) for y in years],
|
years=[cast(int, y.value) for y in years],
|
||||||
episodes=[
|
episodes=[
|
||||||
|
@ -25,7 +25,7 @@ class CollectionTranslation(Model):
|
|||||||
aliases: list[str]
|
aliases: list[str]
|
||||||
tags: list[str]
|
tags: list[str]
|
||||||
|
|
||||||
posters: list[str]
|
poster: str | None
|
||||||
thumbnails: list[str]
|
thumbnail: str | None
|
||||||
banner: list[str]
|
banner: str | None
|
||||||
logos: list[str]
|
logo: str | None
|
||||||
|
36
scanner/scanner/models/entry.py
Normal file
36
scanner/scanner/models/entry.py
Normal 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
|
@ -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,
|
|
||||||
}
|
|
@ -1,10 +1,21 @@
|
|||||||
from typing import Literal
|
from enum import Enum
|
||||||
|
|
||||||
type ExtraKind = (
|
from ..utils import Model
|
||||||
Literal["other"]
|
|
||||||
| Literal["trailer"]
|
|
||||||
| Literal["interview"]
|
class ExtraKind(str, Enum):
|
||||||
| Literal["behind-the-scene"]
|
OTHER = "other"
|
||||||
| Literal["deleted-scene"]
|
TRAILER = "trailer"
|
||||||
| Literal["blooper"]
|
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
|
||||||
|
@ -13,7 +13,7 @@ from .staff import Staff
|
|||||||
from .studio import Studio
|
from .studio import Studio
|
||||||
|
|
||||||
|
|
||||||
class Status(str, Enum):
|
class MovieStatus(str, Enum):
|
||||||
UNKNOWN = "unknown"
|
UNKNOWN = "unknown"
|
||||||
FINISHED = "finished"
|
FINISHED = "finished"
|
||||||
PLANNED = "planned"
|
PLANNED = "planned"
|
||||||
@ -24,7 +24,7 @@ class Movie(Model):
|
|||||||
original_language: Language | None
|
original_language: Language | None
|
||||||
genres: list[Genre]
|
genres: list[Genre]
|
||||||
rating: int | None
|
rating: int | None
|
||||||
status: Status
|
status: MovieStatus
|
||||||
runtime: int | None
|
runtime: int | None
|
||||||
air_date: date | None
|
air_date: date | None
|
||||||
|
|
||||||
@ -44,11 +44,11 @@ class MovieTranslation(Model):
|
|||||||
aliases: list[str]
|
aliases: list[str]
|
||||||
tags: list[str]
|
tags: list[str]
|
||||||
|
|
||||||
posters: list[str]
|
poster: str | None
|
||||||
thumbnails: list[str]
|
thumbnail: str | None
|
||||||
banner: list[str]
|
banner: str | None
|
||||||
logos: list[str]
|
logo: str | None
|
||||||
trailers: list[str]
|
trailer: str | None
|
||||||
|
|
||||||
|
|
||||||
class SearchMovie(Model):
|
class SearchMovie(Model):
|
||||||
|
@ -1,38 +1,22 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
from datetime import date
|
from datetime import date
|
||||||
from dataclasses import dataclass, field, asdict
|
|
||||||
from typing import Optional
|
|
||||||
|
|
||||||
from providers.utils import select_translation, select_image
|
from ..utils import Model
|
||||||
|
from .metadataid import MetadataId
|
||||||
from .metadataid import MetadataID
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
class Season(Model):
|
||||||
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:
|
|
||||||
season_number: int
|
season_number: int
|
||||||
# This is not used by kyoo, this is just used internaly by the TMDB provider.
|
start_air: date | None
|
||||||
# maybe this should be moved?
|
end_air: date | None
|
||||||
episodes_count: int
|
external_id: dict[str, MetadataId]
|
||||||
start_air: Optional[date | int] = None
|
translations: dict[str, SeasonTranslation] = {}
|
||||||
end_air: Optional[date | int] = None
|
|
||||||
external_id: dict[str, MetadataID] = field(default_factory=dict)
|
|
||||||
|
|
||||||
show_id: Optional[str] = None
|
|
||||||
translations: dict[str, SeasonTranslation] = field(default_factory=dict)
|
|
||||||
|
|
||||||
def to_kyoo(self):
|
class SeasonTranslation(Model):
|
||||||
trans = select_translation(self) or SeasonTranslation()
|
name: str | None
|
||||||
return {
|
description: str | None
|
||||||
**asdict(self),
|
poster: str | None
|
||||||
**asdict(trans),
|
thumbnail: str | None
|
||||||
"poster": select_image(self, "posters"),
|
banner: str | None
|
||||||
"thumbnail": select_image(self, "thumbnails"),
|
|
||||||
}
|
|
||||||
|
69
scanner/scanner/models/serie.py
Normal file
69
scanner/scanner/models/serie.py
Normal 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]
|
@ -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,
|
|
||||||
}
|
|
@ -38,7 +38,9 @@ class CompositeProvider(Provider):
|
|||||||
raise ProviderError(
|
raise ProviderError(
|
||||||
f"Couldn't find a movie with title {title}. (year: {year}"
|
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:
|
if not ret:
|
||||||
raise ValueError()
|
raise ValueError()
|
||||||
return ret
|
return ret
|
||||||
@ -68,4 +70,9 @@ class CompositeProvider(Provider):
|
|||||||
raise ProviderError(
|
raise ProviderError(
|
||||||
f"Couldn't find a serie with title {title}. (year: {year}"
|
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
|
||||||
|
@ -4,12 +4,12 @@ from typing import Literal
|
|||||||
|
|
||||||
from .client import KyooClient
|
from .client import KyooClient
|
||||||
from .models.videos import Guess
|
from .models.videos import Guess
|
||||||
from .utils import Model
|
|
||||||
from .providers.composite import CompositeProvider
|
from .providers.composite import CompositeProvider
|
||||||
|
from .utils import Model
|
||||||
|
|
||||||
|
|
||||||
class Request(Model):
|
class Request(Model):
|
||||||
kind: Literal["episode"] | Literal["movie"]
|
kind: Literal["episode", "movie"]
|
||||||
title: str
|
title: str
|
||||||
year: int | None
|
year: int | None
|
||||||
external_id: dict[str, str]
|
external_id: dict[str, str]
|
||||||
@ -39,7 +39,9 @@ class RequestProcessor:
|
|||||||
request: Request = ...
|
request: Request = ...
|
||||||
|
|
||||||
if request.kind == "movie":
|
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
|
movie.videos = request.videos
|
||||||
await self._client.create_movie(movie)
|
await self._client.create_movie(movie)
|
||||||
else:
|
else:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user