manga matching and WebApp
Build and Deploy / build (push) Successful in 32s
Build and Deploy / deploy (push) Successful in 25s

This commit is contained in:
2026-05-26 20:20:24 +02:00
parent 12edb8a5d7
commit 615bd1b468
9 changed files with 665 additions and 56 deletions
+35 -3
View File
@@ -48,6 +48,7 @@ from MangadexVolumeResolver import MangaDexVolumeResolver
from MangaBakaWorksResolver import MangaBakaWorksResolver
from MALResolver import MALResolver
from AniListResolver import AniListResolver
from MatchesCache import MatchesCache
try:
from PIL import Image
@@ -170,7 +171,8 @@ class ComicInfoBuilder:
volume_resolver: "MangaDexVolumeResolver | None" = None,
works_resolver: "MangaBakaWorksResolver | None" = None,
mal_resolver: "MALResolver | None" = None,
al_resolver: "AniListResolver | None" = None):
al_resolver: "AniListResolver | None" = None,
matches_cache: "MatchesCache | None" = None):
if not manga_title or not str(manga_title).strip():
raise ValueError("manga_title must not be empty.")
@@ -197,6 +199,7 @@ class ComicInfoBuilder:
request_timeout=request_timeout)
self._al_resolver = al_resolver or AniListResolver(
request_timeout=request_timeout)
self._matches_cache = matches_cache
self._metadata: "dict | None" = None
self._pages: list[dict] = []
@@ -353,14 +356,43 @@ class ComicInfoBuilder:
return series
def _search_best_series(self, title: str):
"""Searches for `title` and returns the best matching series entry."""
"""
Resolves `title` to a MangaBaka series.
Lookup order:
1. matches.json cache (if attached) — uses the stored series ID
to fetch the full series, skipping the search step entirely.
2. Fresh MangaBaka search — top hit. The match is persisted to
matches.json before being returned so it survives a crash.
"""
if self._matches_cache is not None:
cached = self._matches_cache.get(title)
if cached and cached.get("mangabakaId"):
try:
return self._fetch_series_by_id(cached["mangabakaId"])
except Exception as exc:
print(f"[ComicInfoBuilder] cached id "
f"{cached['mangabakaId']} for {title!r} failed "
f"({exc}); falling back to fresh search",
flush=True)
url = f"{self.api_base_url}/series/search"
resp = self._session.get(
url, params={"q": title, "page": 1, "limit": 1},
timeout=self.request_timeout)
resp.raise_for_status()
data = resp.json().get("data") or []
return data[0] if data else None
series = data[0] if data else None
if series and self._matches_cache is not None:
self._matches_cache.add(
title,
mangabaka_id=series.get("id"),
mangabaka_name=series.get("title") or "",
image_url=_pick_cover_url(series.get("cover")),
)
return series
def _fetch_series_by_id(self, series_id) -> dict:
url = f"{self.api_base_url}/series/{series_id}"