Torznab : recherche anime Sonarr (numéro absolu) + titre de release aligné sur la requête
docker / build-push (push) Successful in 27s

- t=search&q=Titre 02 : le numéro absolu suffixé par Sonarr est extrait et
  utilisé comme filtre épisode, au lieu de partir tel quel au scraper
  (chaîne introuvable → résultats parasites)
- le titre des releases reprend la requête Sonarr (titre/alias TVDB) plutôt
  que le titre du scraper : Sonarr reconnaît sa série au lieu de rejeter
  en « Unknown Series »
This commit is contained in:
Roman
2026-09-27 11:10:06 +00:00
parent 28877bda4f
commit 2581372af8
2 changed files with 54 additions and 2 deletions
+23 -2
View File
@@ -17,6 +17,7 @@ Formats :
import asyncio
import hashlib
import logging
import re
import time
from dataclasses import dataclass
from datetime import UTC, datetime
@@ -48,6 +49,11 @@ _MAX_RELEASES = 100 # borne du flux retourné
_SEARCH_TIMEOUT = 40.0 # scraping lent : garde-fou par source
_EPISODE_CACHE_TTL = 600.0 # listes d'épisodes re-scrapées au bout de 10 min
# Recherche anime Sonarr : t=search&q=Titre 02 — le numéro d'épisode ABSOLU
# est suffixé au titre (2 à 4 chiffres). Sans décodage, le scraper reçoit une
# chaîne introuvable (« Back Street Girls GOKUDOLLS 02 ») et renvoie n'importe quoi.
_ANIME_ABS_RE = re.compile(r"^(?P<title>.+?)\s+(?P<abs>\d{2,4})$")
@dataclass
class Release:
@@ -149,7 +155,13 @@ class TorznabService:
# ------------------------------------------------------------ recherche
async def tvsearch(self, q: str, season: int | None = None, ep: int | None = None) -> list[Release]:
"""Recherche type Sonarr : série (+ saison/épisode optionnels)."""
"""Recherche type Sonarr : série (+ saison/épisode optionnels).
Le titre publié reprend la requête Sonarr (titre TVDB ou alias) plutôt
que le titre du scraper : c'est ce titre-là que Sonarr reconnaît lors
du parsing de la release.
"""
q, ep = self._parse_sonarr_query(q, ep)
releases: list[Release] = []
for source in await self._enabled_sources():
outcomes = await self._search_source(source, q)
@@ -159,12 +171,21 @@ class TorznabService:
except (ScrapeError, TimeoutError):
continue
releases.extend(
self._releases_for(result.title, source, result.source_id, episodes, season, ep)
self._releases_for(q, source, result.source_id, episodes, season, ep)
)
if len(releases) >= _MAX_RELEASES:
return releases[:_MAX_RELEASES]
return releases
@staticmethod
def _parse_sonarr_query(q: str, ep: int | None) -> tuple[str, int | None]:
"""Extrait le numéro absolu suffixé par Sonarr (« Titre 02 » → ep=2)."""
if ep is None:
match = _ANIME_ABS_RE.match(q.strip())
if match:
return match.group("title").strip(), int(match.group("abs"))
return q, ep
async def search(self, q: str) -> list[Release]:
"""Recherche libre : tous les épisodes des fiches trouvées."""
return await self.tvsearch(q)