mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-05-24 02:02:36 -04:00
Add kyoo requests for episodes
This commit is contained in:
parent
2334afb3eb
commit
c3b8595cd7
@ -41,6 +41,10 @@ class TheMovieDatabase(Provider):
|
||||
37: Genre.WESTERN,
|
||||
}
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
return "themoviedatabase"
|
||||
|
||||
async def get(self, path: str, *, params: dict[str, Any] = {}):
|
||||
params = {k: v for k, v in params.items() if v is not None}
|
||||
async with self._client.get(
|
||||
@ -84,7 +88,7 @@ class TheMovieDatabase(Provider):
|
||||
if "logo_path" in company
|
||||
else [],
|
||||
external_id={
|
||||
"themoviedatabase": MetadataID(
|
||||
self.name: MetadataID(
|
||||
company["id"], f"https://www.themoviedb.org/company/{company['id']}"
|
||||
)
|
||||
},
|
||||
@ -127,7 +131,7 @@ class TheMovieDatabase(Provider):
|
||||
if x["id"] in self.genre_map
|
||||
],
|
||||
external_id={
|
||||
"themoviedatabase": MetadataID(
|
||||
self.name: MetadataID(
|
||||
movie["id"], f"https://www.themoviedb.org/movie/{movie['id']}"
|
||||
),
|
||||
"imdb": MetadataID(
|
||||
@ -162,7 +166,7 @@ class TheMovieDatabase(Provider):
|
||||
*,
|
||||
language: list[str],
|
||||
) -> Show:
|
||||
show_id = show.external_id["themoviedatabase"].id
|
||||
show_id = show.external_id[self.name].id
|
||||
if show.original_language not in language:
|
||||
language.append(show.original_language)
|
||||
|
||||
@ -194,7 +198,7 @@ class TheMovieDatabase(Provider):
|
||||
if x["id"] in self.genre_map
|
||||
],
|
||||
external_id={
|
||||
"themoviedatabase": MetadataID(
|
||||
self.name: MetadataID(
|
||||
show["id"], f"https://www.themoviedb.org/tv/{show['id']}"
|
||||
),
|
||||
"imdb": MetadataID(
|
||||
@ -256,7 +260,7 @@ class TheMovieDatabase(Provider):
|
||||
start_date=datetime.strptime(season["air_date"], "%Y-%m-%d").date(),
|
||||
end_date=None,
|
||||
external_id={
|
||||
"themoviedatabase": MetadataID(
|
||||
self.name: MetadataID(
|
||||
season["id"],
|
||||
f"https://www.themoviedb.org/tv/{show_id}/season/{season['season_number']}",
|
||||
)
|
||||
@ -290,6 +294,8 @@ class TheMovieDatabase(Provider):
|
||||
language.append(search["original_language"])
|
||||
|
||||
# TODO: Handle absolute episodes
|
||||
if not season or not episode_nbr:
|
||||
raise NotImplementedError("Absolute order episodes not implemented for the movie database")
|
||||
|
||||
async def for_language(lng: str) -> Episode:
|
||||
episode = await self.get(
|
||||
@ -305,7 +311,7 @@ class TheMovieDatabase(Provider):
|
||||
name=search["name"],
|
||||
original_language=search["original_language"],
|
||||
external_id={
|
||||
"themoviedatabase": MetadataID(
|
||||
self.name: MetadataID(
|
||||
show_id, f"https://www.themoviedb.org/tv/{show_id}"
|
||||
)
|
||||
},
|
||||
@ -319,7 +325,7 @@ class TheMovieDatabase(Provider):
|
||||
if "poster_path" in episode
|
||||
else None,
|
||||
external_id={
|
||||
"themoviedatabase": MetadataID(
|
||||
self.name: MetadataID(
|
||||
episode["id"],
|
||||
f"https://www.themoviedb.org/movie/{episode['id']}",
|
||||
),
|
||||
|
@ -1,9 +1,10 @@
|
||||
import os
|
||||
from aiohttp import ClientSession
|
||||
from abc import abstractmethod
|
||||
from abc import abstractmethod, abstractproperty
|
||||
from typing import Optional, TypeVar
|
||||
|
||||
from .types.episode import Episode
|
||||
from .types.episode import Episode, PartialShow
|
||||
from .types.show import Show
|
||||
from .types.movie import Movie
|
||||
|
||||
|
||||
@ -23,18 +24,26 @@ class Provider:
|
||||
|
||||
return providers
|
||||
|
||||
@abstractproperty
|
||||
def name(self) -> str:
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
async def identify_movie(
|
||||
self, name: str, year: Optional[int], *, language: list[str]
|
||||
) -> Movie:
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
async def identify_show(self, show: PartialShow, *, language: list[str]) -> Show:
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
async def identify_episode(
|
||||
self,
|
||||
name: str,
|
||||
season: Optional[int],
|
||||
episode: Optional[int],
|
||||
episode_nbr: Optional[int],
|
||||
absolute: Optional[int],
|
||||
*,
|
||||
language: list[str]
|
||||
|
@ -1,7 +1,9 @@
|
||||
import os
|
||||
from datetime import date
|
||||
from dataclasses import dataclass, field
|
||||
from dataclasses import dataclass, field, asdict
|
||||
from typing import Optional
|
||||
|
||||
from ..utils import format_date
|
||||
from .show import Show
|
||||
from .season import Season
|
||||
from .metadataid import MetadataID
|
||||
@ -26,9 +28,20 @@ class Episode:
|
||||
season_number: Optional[int]
|
||||
episode_number: Optional[int]
|
||||
absolute_number: Optional[int]
|
||||
release_date: Optional[date | int]
|
||||
release_date: Optional[date]
|
||||
thumbnail: Optional[str]
|
||||
external_id: dict[str, MetadataID]
|
||||
|
||||
path: Optional[str] = None
|
||||
show_id: Optional[str] = None
|
||||
translations: dict[str, EpisodeTranslation] = field(default_factory=dict)
|
||||
|
||||
|
||||
def to_kyoo(self):
|
||||
# For now, the API of kyoo only support one language so we remove the others.
|
||||
default_language = os.environ["LIBRARY_LANGUAGES"].split(",")[0]
|
||||
return {
|
||||
**asdict(self),
|
||||
**asdict(self.translations[default_language]),
|
||||
"release_date": format_date(self.release_date),
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ from enum import Enum
|
||||
from .genre import Genre
|
||||
from .studio import Studio
|
||||
from .metadataid import MetadataID
|
||||
from ..utils import format_date
|
||||
|
||||
|
||||
class Status(str, Enum):
|
||||
@ -43,13 +44,6 @@ class Movie:
|
||||
|
||||
translations: dict[str, MovieTranslation] = field(default_factory=dict)
|
||||
|
||||
def format_date(self, date: date | int | None) -> str | None:
|
||||
if date is None:
|
||||
return None
|
||||
if isinstance(date, int):
|
||||
return f"{date}-01-01T00:00:00Z"
|
||||
return date.isoformat()
|
||||
|
||||
def to_kyoo(self):
|
||||
# For now, the API of kyoo only support one language so we remove the others.
|
||||
default_language = os.environ["LIBRARY_LANGUAGES"].split(",")[0]
|
||||
@ -64,7 +58,7 @@ class Movie:
|
||||
"trailer": next(iter(self.translations[default_language].trailers), None),
|
||||
"studio": next(iter(x.to_kyoo() for x in self.studios), None),
|
||||
"release_date": None,
|
||||
"startAir": self.format_date(self.release_date),
|
||||
"startAir": format_date(self.release_date),
|
||||
"title": self.translations[default_language].name,
|
||||
"genres": [x.to_kyoo() for x in self.genres],
|
||||
"isMovie": True,
|
||||
|
@ -8,6 +8,7 @@ from .genre import Genre
|
||||
from .studio import Studio
|
||||
from .season import Season
|
||||
from .metadataid import MetadataID
|
||||
from ..utils import format_date
|
||||
|
||||
class Status(str, Enum):
|
||||
UNKNOWN = "unknown"
|
||||
@ -45,13 +46,6 @@ class Show:
|
||||
|
||||
translations: dict[str, ShowTranslation] = field(default_factory=dict)
|
||||
|
||||
def format_date(self, date: date | int | None) -> str | None:
|
||||
if date is None:
|
||||
return None
|
||||
if isinstance(date, int):
|
||||
return f"{date}-01-01T00:00:00Z"
|
||||
return date.isoformat()
|
||||
|
||||
def to_kyoo(self):
|
||||
# For now, the API of kyoo only support one language so we remove the others.
|
||||
default_language = os.environ["LIBRARY_LANGUAGES"].split(",")[0]
|
||||
@ -65,8 +59,8 @@ class Show:
|
||||
"logo": next(iter(self.translations[default_language].logos), None),
|
||||
"trailer": next(iter(self.translations[default_language].trailers), None),
|
||||
"studio": next(iter(x.to_kyoo() for x in self.studios), None),
|
||||
"startAir": self.format_date(self.start_air),
|
||||
"endAir": self.format_date(self.end_air),
|
||||
"startAir": format_date(self.start_air),
|
||||
"endAir": format_date(self.end_air),
|
||||
"title": self.translations[default_language].name,
|
||||
"genres": [x.to_kyoo() for x in self.genres],
|
||||
}
|
||||
|
8
scanner/providers/utils.py
Normal file
8
scanner/providers/utils.py
Normal file
@ -0,0 +1,8 @@
|
||||
from datetime import date
|
||||
|
||||
def format_date(date: date | int | None) -> str | None:
|
||||
if date is None:
|
||||
return None
|
||||
if isinstance(date, int):
|
||||
return f"{date}-01-01T00:00:00Z"
|
||||
return date.isoformat()
|
@ -7,6 +7,7 @@ from aiohttp import ClientSession
|
||||
from pathlib import Path
|
||||
from guessit import guessit
|
||||
from providers.provider import Provider
|
||||
from providers.types.episode import PartialShow
|
||||
|
||||
|
||||
def log_errors(f):
|
||||
@ -27,6 +28,7 @@ class Scanner:
|
||||
self._client = client
|
||||
self._api_key = api_key
|
||||
self.provider = Provider.get_all(client)[0]
|
||||
self.cache = {"shows": {}}
|
||||
self.languages = languages
|
||||
|
||||
async def scan(self, path: str):
|
||||
@ -44,6 +46,7 @@ class Scanner:
|
||||
|
||||
# TODO: Add collections support
|
||||
if raw["type"] == "movie":
|
||||
return
|
||||
movie = await self.provider.identify_movie(
|
||||
raw["title"], raw.get("year"), language=self.languages
|
||||
)
|
||||
@ -51,21 +54,32 @@ class Scanner:
|
||||
logging.debug("Got movie: %s", movie)
|
||||
await self.post("movies", data=movie.to_kyoo())
|
||||
elif raw["type"] == "episode":
|
||||
# TODO: Identify shows & seasons too.
|
||||
episode = await self.provider.identify_episode(
|
||||
raw["title"],
|
||||
season=raw.get("season"),
|
||||
episode=raw.get("episode"),
|
||||
episode_nbr=raw.get("episode"),
|
||||
absolute=raw.get("episode") if "season" not in raw else None,
|
||||
language=self.languages,
|
||||
)
|
||||
episode.path = str(path)
|
||||
logging.debug("Got episode: %s", episode)
|
||||
|
||||
show_provider_id = episode.show.external_id[self.provider.name].id
|
||||
if (
|
||||
isinstance(episode.show, PartialShow)
|
||||
and show_provider_id not in self.cache["shows"]
|
||||
):
|
||||
show = await self.provider.identify_show(
|
||||
episode.show, language=self.languages
|
||||
)
|
||||
logging.debug("Got show: %s", episode)
|
||||
self.cache["shows"][show_provider_id] = await self.post("show", data=show.to_kyoo())
|
||||
episode.show_id = self.cache["shows"][show_provider_id]
|
||||
await self.post("episodes", data=episode.to_kyoo())
|
||||
else:
|
||||
logging.warn("Unknown video file type: %s", raw["type"])
|
||||
|
||||
async def post(self, path: str, *, data: object):
|
||||
async def post(self, path: str, *, data: object) -> str:
|
||||
url = os.environ.get("KYOO_URL", "http://back:5000")
|
||||
print(json.dumps(data, indent=4))
|
||||
async with self._client.post(
|
||||
@ -74,3 +88,6 @@ class Scanner:
|
||||
if not r.ok:
|
||||
print(await r.text())
|
||||
r.raise_for_status()
|
||||
ret = await r.json()
|
||||
return ret["id"]
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user