From cc69dc35f67f6ef5c36616a323641d4d8b0855e6 Mon Sep 17 00:00:00 2001 From: s-weigand Date: Sun, 17 Aug 2025 12:34:25 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=91=8C=20Make=20finding=20best=5Fmatch=5F?= =?UTF-8?q?title=20more=20robust?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../menu/media/download_episodes.py | 10 ++---- .../interactive/menu/media/provider_search.py | 10 ++---- viu_cli/cli/service/download/service.py | 13 +++---- viu_cli/cli/utils/search.py | 36 +++++++++++++++++++ 4 files changed, 46 insertions(+), 23 deletions(-) create mode 100644 viu_cli/cli/utils/search.py diff --git a/viu_cli/cli/interactive/menu/media/download_episodes.py b/viu_cli/cli/interactive/menu/media/download_episodes.py index 72ebdf2..2027b14 100644 --- a/viu_cli/cli/interactive/menu/media/download_episodes.py +++ b/viu_cli/cli/interactive/menu/media/download_episodes.py @@ -6,7 +6,7 @@ from ...state import InternalDirective, State @session.menu def download_episodes(ctx: Context, state: State) -> State | InternalDirective: """Menu to select and download episodes synchronously.""" - from .....core.utils.fuzzy import fuzz + from viu_cli.cli.utils.search import find_best_match_title from .....core.utils.normalizer import normalize_title from ....service.download.service import DownloadService @@ -40,12 +40,8 @@ def download_episodes(ctx: Context, state: State) -> State | InternalDirective: return InternalDirective.BACK provider_results_map = {res.title: res for res in provider_search_results.results} - best_match_title = max( - provider_results_map.keys(), - key=lambda p_title: fuzz.ratio( - normalize_title(p_title, config.general.provider.value).lower(), - media_title.lower(), - ), + best_match_title = find_best_match_title( + provider_results_map, config.general.provider, media_item ) selected_provider_anime_ref = provider_results_map[best_match_title] diff --git a/viu_cli/cli/interactive/menu/media/provider_search.py b/viu_cli/cli/interactive/menu/media/provider_search.py index d3f0da1..f5ce13d 100644 --- a/viu_cli/cli/interactive/menu/media/provider_search.py +++ b/viu_cli/cli/interactive/menu/media/provider_search.py @@ -6,7 +6,7 @@ from ...state import InternalDirective, MenuName, ProviderState, State @session.menu def provider_search(ctx: Context, state: State) -> State | InternalDirective: - from .....core.utils.fuzzy import fuzz + from viu_cli.cli.utils.search import find_best_match_title from .....core.utils.normalizer import normalize_title, update_user_normalizer_json feedback = ctx.feedback @@ -51,12 +51,8 @@ def provider_search(ctx: Context, state: State) -> State | InternalDirective: # --- Auto-Select or Prompt --- if config.general.auto_select_anime_result: # Use fuzzy matching to find the best title - best_match_title = max( - provider_results_map.keys(), - key=lambda p_title: fuzz.ratio( - normalize_title(p_title, config.general.provider.value).lower(), - media_title.lower(), - ), + best_match_title = find_best_match_title( + provider_results_map, config.general.provider, media_item ) feedback.info(f"Auto-selecting best match: {best_match_title}") selected_provider_anime = provider_results_map[best_match_title] diff --git a/viu_cli/cli/service/download/service.py b/viu_cli/cli/service/download/service.py index dfbbe63..bff69f1 100644 --- a/viu_cli/cli/service/download/service.py +++ b/viu_cli/cli/service/download/service.py @@ -2,11 +2,12 @@ import logging from pathlib import Path from typing import TYPE_CHECKING, List +from viu_cli.cli.utils.search import find_best_match_title + from ....core.config.model import AppConfig from ....core.constants import APP_CACHE_DIR from ....core.downloader import DownloadParams, create_downloader from ....core.utils.concurrency import ManagedBackgroundWorker, thread_manager -from ....core.utils.fuzzy import fuzz from ....core.utils.normalizer import normalize_title from ....libs.media_api.types import MediaItem from ....libs.provider.anime.params import ( @@ -204,14 +205,8 @@ class DownloadService: provider_results_map = { result.title: result for result in provider_search_results.results } - best_match_title = max( - provider_results_map.keys(), - key=lambda p_title: fuzz.ratio( - normalize_title( - p_title, self.app_config.general.provider.value - ).lower(), - media_title.lower(), - ), + best_match_title = find_best_match_title( + provider_results_map, self.app_config.general.provider, media_item ) provider_anime_ref = provider_results_map[best_match_title] diff --git a/viu_cli/cli/utils/search.py b/viu_cli/cli/utils/search.py new file mode 100644 index 0000000..c32c506 --- /dev/null +++ b/viu_cli/cli/utils/search.py @@ -0,0 +1,36 @@ +"""Search functionality.""" + +from viu_cli.core.utils.fuzzy import fuzz +from viu_cli.core.utils.normalizer import normalize_title +from viu_cli.libs.provider.anime.types import SearchResult, ProviderName +from viu_cli.libs.media_api.types import MediaItem + + +def find_best_match_title( + provider_results_map: dict[str, SearchResult], + provider: ProviderName, + media_item: MediaItem, +) -> str: + """Find the best match title using fuzzy matching for both the english AND romaji title. + + Parameters: + provider_results_map (dict[str, SearchResult]): The map of provider results. + provider (ProviderName): The provider name from the config. + media_item (MediaItem): The media item to match. + + Returns: + str: The best match title. + """ + return max( + provider_results_map.keys(), + key=lambda p_title: max( + fuzz.ratio( + normalize_title(p_title, provider.value).lower(), + (media_item.title.romaji or "").lower(), + ), + fuzz.ratio( + normalize_title(p_title, provider.value).lower(), + (media_item.title.english or "").lower(), + ), + ), + )