refactor(tracks): honour the best token and share one match rule

sort_subtitles resolved only the all token, while the video and audio sorts
resolved all and best. by_language now asks matching_languages rather than
repeating the RFC 4647 preference, with no behaviour change.
This commit is contained in:
imSp4rky
2026-08-03 10:29:09 -06:00
parent a6997f6bca
commit a5f5e7d14d
2 changed files with 14 additions and 17 deletions
+9 -2
View File
@@ -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()
+5 -15
View File
@@ -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]: