Add AniList resolver as MAL fallback; fix SeriesGroup, tag formatting, empty-cache bug
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
"""
|
||||
media_resolver.py
|
||||
=================
|
||||
|
||||
Abstract base class for tracker-specific manga metadata resolvers.
|
||||
|
||||
Concrete implementations (MALResolver, AniListResolver) must implement
|
||||
every abstract method, ensuring a uniform interface regardless of the
|
||||
underlying data source (Jikan/MAL, AniList GraphQL, …).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
|
||||
|
||||
class MediaResolver(ABC):
|
||||
"""
|
||||
Abstract base for tracker-specific manga metadata resolvers.
|
||||
|
||||
Subclasses connect to a specific tracker API and expose a common
|
||||
interface for:
|
||||
- Searching a manga by title → tracker-specific numeric ID
|
||||
- Fetching summary statistics (score, rank, popularity, …)
|
||||
- Listing characters and staff (name-only and detailed forms)
|
||||
- Fetching full details for a single character or person
|
||||
|
||||
Methods that accept a tracker ID treat None as "unknown" and return
|
||||
a safe empty value rather than raising.
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def find_id(self, title: str) -> "int | None":
|
||||
"""
|
||||
Searches the tracker for a manga by title.
|
||||
Returns the best-matching tracker ID, or None on failure.
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def get_stats(self, tracker_id: "int | None") -> "dict | None":
|
||||
"""
|
||||
Returns a statistics dict for the given tracker ID:
|
||||
|
||||
{score, rank, scored_by, popularity, members, favorites,
|
||||
url, title, as_of (DD-MM-YYYY)}
|
||||
|
||||
Returns None if tracker_id is None or on network failure.
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def get_characters(self, tracker_id: "int | None") -> "list[str]":
|
||||
"""
|
||||
Returns a flat list of character name strings for the manga.
|
||||
Used to populate the ComicInfo <Characters> XML element.
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def get_characters_detailed(self, tracker_id: "int | None") -> "list[dict]":
|
||||
"""
|
||||
Returns detailed character entries for a manga:
|
||||
[{id, name, image_url, role, about=None, ...}, ...]
|
||||
|
||||
'about' is not populated here; call get_character_details() lazily.
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def get_staff_detailed(self, tracker_id: "int | None") -> "list[dict]":
|
||||
"""
|
||||
Returns detailed staff/author entries for a manga:
|
||||
[{id, name, image_url, positions, about=None, ...}, ...]
|
||||
|
||||
'about' is not populated here; call get_person_details() lazily.
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def get_character_details(self, char_id: "int | None") -> "dict | None":
|
||||
"""
|
||||
Returns full details for a single character, including description.
|
||||
Implementations should cache the result.
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def get_person_details(self, person_id: "int | None") -> "dict | None":
|
||||
"""
|
||||
Returns full details for a single person (staff), including description.
|
||||
Implementations should cache the result.
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def clear_cache(self) -> None:
|
||||
"""Clears all internal caches."""
|
||||
Reference in New Issue
Block a user