Match guessed's entries to tmdb's entries

This commit is contained in:
Zoe Roux 2025-05-09 02:20:06 +02:00
parent 47f6c66435
commit f26dce64ef
No known key found for this signature in database
3 changed files with 67 additions and 97 deletions

View File

@ -3,6 +3,8 @@ from logging import getLogger
from aiohttp import ClientSession
from .models.movie import Movie
from .models.serie import Serie
from .models.videos import Video, VideoCreated, VideoInfo
logger = getLogger(__name__)
@ -10,33 +12,32 @@ logger = getLogger(__name__)
class KyooClient:
def __init__(self) -> None:
self._api_key: str = os.environ.get("KYOO_APIKEY") # type: ignore
if not self._api_key:
api_key = os.environ.get("KYOO_APIKEY")
if not api_key:
print("Missing environment variable 'KYOO_APIKEY'.")
exit(2)
self._url = os.environ.get("KYOO_URL", "http://api:3567/api")
async def __aenter__(self):
self._client = ClientSession(
base_url=os.environ.get("KYOO_URL", "http://api:3567/api"),
headers={
"User-Agent": "kyoo",
"User-Agent": "kyoo scanner v5",
"X-API-KEY": api_key,
},
)
async def __aenter__(self):
return self
async def __aexit__(self):
await self._client.close()
async def get_videos_info(self) -> VideoInfo:
async with self._client.get(
f"{self._url}/videos",
) as r:
async with self._client.get("/videos") as r:
r.raise_for_status()
return VideoInfo(**await r.json())
async def create_videos(self, videos: list[Video]) -> list[VideoCreated]:
async with self._client.post(
f"{self._url}/videos",
"videos",
json=[x.model_dump_json() for x in videos],
) as r:
r.raise_for_status()
@ -44,85 +45,21 @@ class KyooClient:
async def delete_videos(self, videos: list[str] | set[str]):
async with self._client.delete(
f"{self._url}/videos",
"videos",
json=videos,
) as r:
r.raise_for_status()
# async def link_collection(
# self, collection: str, type: Literal["movie"] | Literal["show"], id: str
# ):
# async with self.client.put(
# f"{self._url}/collections/{collection}/{type}/{id}",
# headers={"X-API-Key": self._api_key},
# ) as r:
# # Allow 409 and continue as if it worked.
# if not r.ok and r.status != 409:
# logger.error(f"Request error: {await r.text()}")
# r.raise_for_status()
#
# async def post(self, path: str, *, data: dict[str, Any]) -> str:
# logger.debug(
# "Sending %s: %s",
# path,
# jsons.dumps(
# data,
# key_transformer=jsons.KEY_TRANSFORMER_CAMELCASE,
# jdkwargs={"indent": 4},
# ),
# )
# async with self.client.post(
# f"{self._url}/{path}",
# json=data,
# headers={"X-API-Key": self._api_key},
# ) as r:
# # Allow 409 and continue as if it worked.
# if not r.ok and r.status != 409:
# logger.error(f"Request error: {await r.text()}")
# r.raise_for_status()
# ret = await r.json()
# return ret["id"]
#
# async def delete(
# self,
# path: str,
# ):
# logger.info("Deleting %s", path)
#
# async with self.client.delete(
# f"{self._url}/paths?recursive=true&path={quote(path)}",
# headers={"X-API-Key": self._api_key},
# ) as r:
# if not r.ok:
# logger.error(f"Request error: {await r.text()}")
# r.raise_for_status()
#
# async def get(self, path: str):
# async with self.client.get(
# f"{self._url}/{path}",
# headers={"X-API-Key": self._api_key},
# ) as r:
# if not r.ok:
# logger.error(f"Request error: {await r.text()}")
# r.raise_for_status()
# return await r.json()
#
# async def put(self, path: str, *, data: dict[str, Any]):
# logger.debug(
# "Sending %s: %s",
# path,
# jsons.dumps(
# data,
# key_transformer=jsons.KEY_TRANSFORMER_CAMELCASE,
# jdkwargs={"indent": 4},
# ),
# )
# async with self.client.put(
# f"{self._url}/{path}",
# json=data,
# headers={"X-API-Key": self._api_key},
# ) as r:
# # Allow 409 and continue as if it worked.
# if not r.ok and r.status != 409:
# logger.error(f"Request error: {await r.text()}")
# r.raise_for_status()
async def create_movie(self, movie: Movie):
async with self._client.post(
"movies",
json=movie.model_dump_json(),
) as r:
r.raise_for_status()
async def create_serie(self, serie: Serie):
async with self._client.post(
"series",
json=serie.model_dump_json(),
) as r:
r.raise_for_status()

View File

@ -36,7 +36,10 @@ class Provider(ABC):
raise NotImplementedError
async def find_movie(
self, title: str, year: int | None, external_id: dict[str, str]
self,
title: str,
year: int | None,
external_id: dict[str, str],
) -> Movie:
ret = await self.get_movie(external_id)
if ret is not None:
@ -54,7 +57,10 @@ class Provider(ABC):
return ret
async def find_serie(
self, title: str, year: int | None, external_id: dict[str, str]
self,
title: str,
year: int | None,
external_id: dict[str, str],
) -> Serie:
ret = await self.get_serie(external_id)
if ret is not None:

View File

@ -1,5 +1,6 @@
from __future__ import annotations
from logging import getLogger
from typing import Literal
from .client import KyooClient
@ -7,6 +8,8 @@ from .models.videos import Guess
from .providers.composite import CompositeProvider
from .utils import Model
logger = getLogger(__name__)
class Request(Model):
kind: Literal["episode", "movie"]
@ -33,21 +36,45 @@ async def enqueue(requests: list[Request]):
class RequestProcessor:
def __init__(self, client: KyooClient, providers: CompositeProvider):
self._client = client
self._providers = providers
async def process_scan_requests(self):
# select for update skip_locked limit 1
request: Request = ...
if request.kind == "movie":
movie = await providers.get_movie(
request.title, request.year, request.external_id
movie = await self._providers.find_movie(
request.title,
request.year,
request.external_id,
)
movie.videos = request.videos
movie.videos = [x.id for x in request.videos]
await self._client.create_movie(movie)
else:
serie = await providers.get_serie(request.title, request.year)
# for vid in request.videos:
# for ep in vid.episodes:
# entry = next(x for x in series.entries if (ep.season is None or x.season == ep.season), None)
serie = await self._providers.find_serie(
request.title,
request.year,
request.external_id,
)
for vid in request.videos:
for ep in vid.episodes:
entry = next(
(
x
for x in serie.entries
if (ep.season is None and x.order == ep.episode)
or (
x.season_number == ep.season
and x.episode_number == ep.episode
)
),
None,
)
if entry is None:
logger.warning(
f"Couldn't match entry for {serie.slug} {ep.season or 'abs'}-e{ep.episode}."
)
continue
entry.videos.append(vid.id)
await self._client.create_serie(serie)
# delete request