diff --git a/tests/tracks/test_subtitle_sort.py b/tests/tracks/test_subtitle_sort.py index c583b494..02170415 100644 --- a/tests/tracks/test_subtitle_sort.py +++ b/tests/tracks/test_subtitle_sort.py @@ -180,11 +180,18 @@ def test_no_priority_leaves_order_unchanged(many: Tracks) -> None: assert langs(many) == ["ar", "bg", "en", "es", "fr", "ja"] -def test_all_alone_still_floats_the_original_language(many: Tracks) -> None: - many.sort_subtitles(by_language=["all"], group_by="language") +@pytest.mark.parametrize("token", ["all", "best"]) +def test_magic_token_alone_floats_the_original_language(many: Tracks, token: str) -> None: + many.sort_subtitles(by_language=[token], group_by="language") assert langs(many) == ["ja", "ar", "bg", "en", "es", "fr"] +@pytest.mark.parametrize("token", ["all", "best"]) +def test_priority_outranks_either_magic_token(many: Tracks, token: str) -> None: + many.sort_subtitles(by_language=["en", "es", token], group_by="language") + assert langs(many) == ["en", "es", "ja", "ar", "bg", "fr"] + + @pytest.fixture def regionals() -> Tracks: t = Tracks() diff --git a/unshackle/core/tracks/tracks.py b/unshackle/core/tracks/tracks.py index 268fe3e3..faa5ce26 100644 --- a/unshackle/core/tracks/tracks.py +++ b/unshackle/core/tracks/tracks.py @@ -7,7 +7,7 @@ from functools import partial from pathlib import Path from typing import Any, Callable, Iterator, Optional, Sequence, Union -from langcodes import Language, closest_supported_match +from langcodes import Language from rich.progress import Progress, SpinnerColumn, TaskID, TextColumn, TimeRemainingColumn from rich.table import Table from rich.tree import Tree @@ -15,7 +15,7 @@ from rich.tree import Tree from unshackle.core import binaries from unshackle.core.config import config from unshackle.core.console import GradientPulseBarColumn, console -from unshackle.core.constants import LANGUAGE_EXACT_DISTANCE, LANGUAGE_MAX_DISTANCE, AnyTrack, TrackT +from unshackle.core.constants import AnyTrack, TrackT from unshackle.core.events import events from unshackle.core.tracks.attachment import Attachment from unshackle.core.tracks.audio import Audio @@ -351,7 +351,7 @@ class Tracks: by_type() # sections for language in reversed(by_language or []): - if str(language) == "all": + if str(language) in ("all", "best"): language = next((x.language for x in self.subtitles if x.is_original_lang), "") if not language: continue @@ -469,21 +469,11 @@ class Tracks: def by_language( tracks: list[TrackT], languages: list[str], per_language: int = 0, exact_match: bool = False ) -> list[TrackT]: - distance = LANGUAGE_EXACT_DISTANCE if exact_match else LANGUAGE_MAX_DISTANCE selected: list[TrackT] = [] seen_ids: set[str] = set() for language in languages: - matches = [x for x in tracks if closest_supported_match(str(x.language), [language], distance)] - if exact_match and len(matches) > 1: - # CLDR tag_distance measures intelligibility, not tag identity: it rates a base - # language and its "paradigm" regional variant as the same language (distance 0 - # for ar/ar-EG, en/en-US, pt/pt-BR), so exact mode alone cannot separate them. - # Follow RFC 4647 Lookup: prefer the most specific (string-equal) tag, and fall - # back to the fuzzy match only when none exists (e.g. zh matches cmn). - want = Language.get(language).to_tag().casefold() - exact_hits = [x for x in matches if Language.get(str(x.language)).to_tag().casefold() == want] - if exact_hits: - matches = exact_hits + wanted = matching_languages(language, [x.language for x in tracks], exact_match) + matches = [x for x in tracks if str(x.language) in wanted] # Overlapping tags can still resolve to the same physical track; dedupe by id so # callers never get duplicate tracks. for track in matches[: per_language or None]: