merged ln metadata into manga mover
This commit is contained in:
+32
-17
@@ -32,27 +32,35 @@ Dependencies
|
||||
from __future__ import annotations
|
||||
|
||||
import datetime
|
||||
import difflib
|
||||
import time
|
||||
|
||||
import requests
|
||||
|
||||
from MediaResolver import MediaResolver
|
||||
from TextUtils import best_similarity
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# GraphQL query strings
|
||||
# --------------------------------------------------------------------------
|
||||
_SEARCH_MANGA = """
|
||||
# AniList models both manga and light novels as type MANGA; the format
|
||||
# clause decides which of the two a search returns. The placeholder is
|
||||
# substituted at construction time (see `media_format`).
|
||||
_SEARCH_MANGA_TEMPLATE = """
|
||||
query ($search: String) {
|
||||
Page(page: 1, perPage: 5) {
|
||||
media(search: $search, type: MANGA, format_not_in: [NOVEL]) {
|
||||
media(search: $search, type: MANGA, __FORMAT_CLAUSE__) {
|
||||
id title { romaji english native } siteUrl
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
_FORMAT_CLAUSES = {
|
||||
"manga": "format_not_in: [NOVEL]",
|
||||
"novel": "format_in: [NOVEL]",
|
||||
}
|
||||
|
||||
_MANGA_STATS = """
|
||||
query ($id: Int) {
|
||||
Media(id: $id, type: MANGA) {
|
||||
@@ -131,10 +139,24 @@ class AniListResolver(MediaResolver):
|
||||
cls._instance._initialized = False
|
||||
return cls._instance
|
||||
|
||||
def __init__(self, *, request_timeout: int = 30):
|
||||
def __init__(self, *, request_timeout: int = 30,
|
||||
media_format: str = "manga"):
|
||||
"""
|
||||
media_format : "manga" (excludes novels) or "novel" (novels only).
|
||||
Only the FIRST construction in the process sets it
|
||||
(singleton); construct the resolver with the correct
|
||||
format in the entry point / orchestrator.
|
||||
"""
|
||||
if self._initialized:
|
||||
return
|
||||
|
||||
if media_format not in _FORMAT_CLAUSES:
|
||||
raise ValueError(f"media_format must be one of "
|
||||
f"{sorted(_FORMAT_CLAUSES)}, got {media_format!r}")
|
||||
self.media_format = media_format
|
||||
self._search_query = _SEARCH_MANGA_TEMPLATE.replace(
|
||||
"__FORMAT_CLAUSE__", _FORMAT_CLAUSES[media_format])
|
||||
|
||||
self.request_timeout = request_timeout
|
||||
|
||||
self._session = requests.Session()
|
||||
@@ -178,7 +200,7 @@ class AniListResolver(MediaResolver):
|
||||
return self._id_cache[key]
|
||||
|
||||
try:
|
||||
data = self._gql(_SEARCH_MANGA, {"search": title})
|
||||
data = self._gql(self._search_query, {"search": title})
|
||||
results = ((data.get("data") or {})
|
||||
.get("Page", {})
|
||||
.get("media") or [])
|
||||
@@ -469,18 +491,11 @@ class AniListResolver(MediaResolver):
|
||||
def _score_title(query: str, entry: dict) -> float:
|
||||
"""Returns the best title-similarity score for an AniList media entry."""
|
||||
title_obj = entry.get("title") or {}
|
||||
candidates = [
|
||||
title_obj.get("romaji") or "",
|
||||
title_obj.get("english") or "",
|
||||
title_obj.get("native") or "",
|
||||
]
|
||||
best = 0.0
|
||||
q = query.lower()
|
||||
for t in candidates:
|
||||
if t:
|
||||
ratio = difflib.SequenceMatcher(None, q, t.lower()).ratio()
|
||||
best = max(best, ratio)
|
||||
return best
|
||||
return best_similarity(query, (
|
||||
title_obj.get("romaji"),
|
||||
title_obj.get("english"),
|
||||
title_obj.get("native"),
|
||||
))
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user