diff --git a/src/ComicInfoBuilder.py b/src/ComicInfoBuilder.py index 70f6bd4..b93e969 100644 --- a/src/ComicInfoBuilder.py +++ b/src/ComicInfoBuilder.py @@ -439,8 +439,7 @@ class ComicInfoBuilder: # ----- Title / Series ----------------------------------------------- add("Title", sd.get("Title") or f"Chapter {self._chapter}") add("Series", md.get("title") or self._manga_title) - add("LocalizedSeries", - md.get("native_title") or md.get("romanized_title")) + add("LocalizedSeries", self._romanized_for_native(md)) add("SeriesSort", self._get_sort_title(md)) add("Number", sd.get("Number") or self._chapter) add("Count", md.get("total_chapters")) @@ -648,6 +647,70 @@ class ComicInfoBuilder: # ====================================================================== # Title helpers # ====================================================================== + # Mapping from series type to the matching romanized language code(s) + # in the MangaBaka titles array. Used to pick the correct romaji / + # romaja / pinyin for LocalizedSeries. + _ROMANIZED_LANG_BY_TYPE = { + "manga": ("ja-latn", "ja-romaji"), + "manhwa": ("ko-latn", "ko-romaji"), + "manhua": ("zh-latn",), + } + + @classmethod + def _romanized_for_native(cls, md: dict) -> "str | None": + """ + Picks the romanized title in the manga's original language from the + ``titles`` array. + + The series' original language is inferred from ``type``:: + + manga -> ja-Latn (Japanese romaji) + manhwa -> ko-Latn (Korean romaja) + manhua -> zh-Latn (Chinese pinyin) + + Among multiple entries for the matching language, the one with the + highest "quality score" wins (``official`` trait > ``is_primary`` > + first seen). + + The root-level ``romanized_title`` field is **deliberately not used + as a fallback** — MangaBaka frequently stores a different language's + romanization there (e.g. Korean romaja on a Japanese manga), which + is exactly what this function is meant to avoid. + + Returns ``None`` when no romanized title is available for the + inferred language. + """ + mtype = (md.get("type") or "").lower() + langs = cls._ROMANIZED_LANG_BY_TYPE.get(mtype) + if not langs: + return None + + titles = md.get("titles") or md.get("alt_titles") or [] + if not isinstance(titles, list): + return None + + best_score = -1 + best_title: "str | None" = None + for entry in titles: + if not isinstance(entry, dict): + continue + lang = (entry.get("language") or entry.get("lang") or "").lower() + if lang not in langs: + continue + title = entry.get("title") + if not title: + continue + traits = entry.get("traits") or [] + score = 0 + if "official" in traits: + score += 2 + if entry.get("is_primary"): + score += 1 + if score > best_score: + best_score = score + best_title = title + return best_title + def _get_sort_title(self, md: dict) -> "str | None": """ Returns the SeriesSort title in the configured language.