Add tvdb episode identify

This commit is contained in:
Zoe Roux 2024-05-08 15:37:49 +02:00
parent 1c483fa14f
commit a6b6067b0b
No known key found for this signature in database
2 changed files with 111 additions and 6 deletions

View File

@ -111,7 +111,7 @@ class Matcher:
episode = await self._provider.search_episode( episode = await self._provider.search_episode(
title, title,
season=season, season=season,
episode_nbr=episode_nbr, episode_nbr=episode_nbr if season is not None else None,
absolute=episode_nbr if season is None else None, absolute=episode_nbr if season is None else None,
year=year, year=year,
) )

View File

@ -1,7 +1,8 @@
from datetime import timedelta import asyncio
from datetime import timedelta, datetime
from aiohttp import ClientSession from aiohttp import ClientSession
from logging import getLogger from logging import getLogger
from typing import Optional, Any from typing import Optional, Any, Literal
from matcher.cache import cache from matcher.cache import cache
@ -33,6 +34,12 @@ class TVDB(Provider):
self._pin = pin self._pin = pin
self._languages = languages self._languages = languages
def two_to_three_lang(self, lang: str) -> str:
return lang
def three_to_two_lang(self, lang: str) -> str:
return lang
@cache(ttl=timedelta(days=30)) @cache(ttl=timedelta(days=30))
async def login(self) -> str: async def login(self) -> str:
async with self._client.post( async with self._client.post(
@ -45,15 +52,16 @@ class TVDB(Provider):
async def get( async def get(
self, self,
path: str, path: Optional[str] = None,
*, *,
fullPath: Optional[str] = None,
params: dict[str, Any] = {}, params: dict[str, Any] = {},
not_found_fail: Optional[str] = None, not_found_fail: Optional[str] = None,
): ):
token = await self.login() token = await self.login()
params = {k: v for k, v in params.items() if v is not None} params = {k: v for k, v in params.items() if v is not None}
async with self._client.get( async with self._client.get(
f"{self.base}/{path}", fullPath or f"{self.base}/{path}",
params={"api_key": self._api_key, **params}, params={"api_key": self._api_key, **params},
headers={"Authorization": f"Bearer {token}"}, headers={"Authorization": f"Bearer {token}"},
) as r: ) as r:
@ -66,6 +74,30 @@ class TVDB(Provider):
def name(self) -> str: def name(self) -> str:
return "tvdb" return "tvdb"
async def search_show(self, name: str, year: Optional[int]) -> Show:
pass
@cache(ttl=timedelta(days=1))
async def get_episodes(
self,
show_id: str,
order: Literal["default", "absolute"],
language: Optional[str] = None,
):
path = f"/series/{show_id}/episodes/{order}"
if language is not None:
path += f"/{language}"
ret = await self.get(
path, not_found_fail=f"Could not find show with id {show_id}"
)
episodes = ret["data"]["episodes"]
next = ret["links"]["next"]
while next != None:
ret = await self.get(fullPath=next)
next = ret["links"]["next"]
episodes += ret["data"]
return episodes
async def search_episode( async def search_episode(
self, self,
name: str, name: str,
@ -85,4 +117,77 @@ class TVDB(Provider):
episode_nbr: Optional[int], episode_nbr: Optional[int],
absolute: Optional[int], absolute: Optional[int],
) -> Episode: ) -> Episode:
return await self.get(f"") flang, slang, *olang = [*self._languages, None]
episodes = await self.get_episodes(show_id, order="default", language=flang)
show = episodes["data"]
ret = next(
filter(
(lambda x: x["seasonNumber"] == 1 and x["number"] == absolute)
if absolute is not None
else (
lambda x: x["seasonNumber"] == season and x["number"] == episode_nbr
),
episodes["episodes"],
),
None,
)
if ret == None:
raise ProviderError(
f"Could not retrive episode {show['name']} s{season}e{episode_nbr}, absolute {absolute}"
)
absolutes = await self.get_episodes(
show_id, order="absolute", language=slang or flang
)
abs = next(filter(lambda x: x["id"] == ret["id"], absolutes["episodes"]))
otrans = await asyncio.gather(
*(
self.get_episodes(show_id, order="default", language=lang)
for lang in olang
if lang is not None
)
)
translations = {
lang: EpisodeTranslation(
name=val["name"],
overview=val["overview"],
)
for (lang, val) in zip(
self._languages,
[
ret,
abs,
*(
next(x for x in e["episodes"] if x["id"] == ret["id"])
for e in otrans
),
],
)
}
return Episode(
show=PartialShow(
name=show["name"],
original_language=self.three_to_two_lang(show["originalLanguage"]),
external_id={
self.name: MetadataID(
show_id, f"https://thetvdb.com/series/{show['slug']}"
),
},
),
season_number=ret["seasonNumber"],
episode_number=ret["number"],
absolute_number=abs["number"],
runtime=ret["runtime"],
release_date=datetime.strptime(ret["aired"], "%Y-%m-%d").date(),
thumbnail=f"https://artworks.thetvdb.com{ret['image']}",
external_id={
self.name: EpisodeID(
show_id,
ret["seasonNumber"],
ret["number"],
f"https://thetvdb.com/series/{show_id}/episodes/{ret['id']}",
),
},
translations=translations,
)