From 8a816b587fb9097cfba876957f5f70aa111246ab Mon Sep 17 00:00:00 2001 From: Zoe Roux Date: Wed, 17 Apr 2024 23:01:36 +0200 Subject: [PATCH] Add refresh message handler --- scanner/matcher/matcher.py | 28 ++++++++++++++++++++++++++++ scanner/matcher/subscriber.py | 13 +++++++------ scanner/providers/kyoo_client.py | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+), 6 deletions(-) diff --git a/scanner/matcher/matcher.py b/scanner/matcher/matcher.py index bc3277c8..b0b76932 100644 --- a/scanner/matcher/matcher.py +++ b/scanner/matcher/matcher.py @@ -1,4 +1,5 @@ from datetime import timedelta +from typing import Literal import asyncio from logging import getLogger from providers.provider import Provider, ProviderError @@ -165,3 +166,30 @@ class Matcher: return await self._client.post("seasons", data=season.to_kyoo()) return await create_season(show_id, season_number) + + async def refresh( + self, + kind: Literal["collection", "movie", "episode", "show", "season"], + kyoo_id: str, + ): + identify_table = { + "collection": lambda _, id: self._provider.identify_collection( + id["dataId"] + ), + "movie": lambda _, id: self._provider.identify_movie(id["dataId"]), + "show": lambda _, id: self._provider.identify_show(id["dataId"]), + "season": lambda season, id: self._provider.identify_season( + id["dataId"], season["seasonNumber"] + ), + "episode": lambda episode, id: self._provider.identify_episode( + id["showId"], id["season"], id["episode"], episode["absoluteNumber"] + ), + } + current = await self._client.get(kind, kyoo_id) + if self._provider.name not in current["externalId"]: + logger.error(f"Could not refresh metadata of {kind}/{kyoo_id}. Missisg provider id.") + return False + provider_id = current["externalId"][self._provider.name] + new_value = await identify_table[kind](current, provider_id) + await self._client.put(f"{kind}/{kyoo_id}", data=new_value.to_kyoo()) + return True diff --git a/scanner/matcher/subscriber.py b/scanner/matcher/subscriber.py index b8b7bac9..103b55bd 100644 --- a/scanner/matcher/subscriber.py +++ b/scanner/matcher/subscriber.py @@ -1,5 +1,5 @@ import asyncio -from typing import Union +from typing import Union, Literal from msgspec import Struct, json import os import logging @@ -19,10 +19,11 @@ class Scan(Message): class Delete(Message): path: str -class Identify(Message): - pass +class Refresh(Message): + kind: Literal["collection", "show", "movie", "season", "episode"] + id: str -decoder = json.Decoder(Union[Scan, Delete, Identify]) +decoder = json.Decoder(Union[Scan, Delete, Refresh]) class Subscriber: QUEUE = "scanner" @@ -49,8 +50,8 @@ class Subscriber: ack = await scanner.identify(path) case Delete(path): ack = await scanner.delete(path) - # case Identify(): - # ack = await scanner.delete(msg.path) + case Refresh(kind, id): + ack = await scanner.refresh(kind, id) case _: logger.error(f"Invalid action: {msg.action}") if ack: diff --git a/scanner/providers/kyoo_client.py b/scanner/providers/kyoo_client.py index 5dfbdc7b..a8ef8ccd 100644 --- a/scanner/providers/kyoo_client.py +++ b/scanner/providers/kyoo_client.py @@ -154,3 +154,35 @@ class KyooClient: r.raise_for_status() await self.delete_issue(path) + + async def get( + self, kind: Literal["movie", "show", "season", "episode", "collection"], id: str + ): + async with self.client.get( + f"{self._url}/{kind}/{id}", + 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.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()