Skip to content

fix(subtitles): match preferred language against track labels for untagged tracks#1324

Open
anikettuli wants to merge 2 commits into
NuvioMedia:cmp-rewritefrom
anikettuli:fix/subtitle-preferred-language
Open

fix(subtitles): match preferred language against track labels for untagged tracks#1324
anikettuli wants to merge 2 commits into
NuvioMedia:cmp-rewritefrom
anikettuli:fix/subtitle-preferred-language

Conversation

@anikettuli

Copy link
Copy Markdown
Contributor

Summary

Embedded subtitle tracks in MKV files frequently carry no ISO language tag — format.language arrives as null or "und" with the language only present in the track title (e.g. "English", "English [SDH]"). The preferred-language auto-selection matched exclusively on the language tag, so for these files the lookup always failed, ExoPlayer's container-default track stayed active, and users saw Arabic/Italian/Spanish/Portuguese subtitles even though an English track existed.

PR type

  • Behavior bug/regression fix

Why

The root cause was reproduced with a failing unit test before writing the fix: findPreferredSubtitleTrackIndex in PlayerTrackSelection.kt handles ISO 639-1/639-2 tags, regional variants, and even full language names in the language field — but returns -1 for tracks whose tag is null or "und", which is exactly how MKV muxers commonly ship embedded tracks.

Two changes:

  • Label fallback. When no track matches a target by language tag, the track label is matched through the same normalizeLanguageCode / languageMatchesPreference path. The fallback is restricted to tracks whose normalized tag is null or "und", so a track with a usable foreign tag can never be hijacked by a coincidental label.
  • No wrong-language default. When neither pass matches anything (and the preferred-target list is non-empty), the text track is explicitly disabled via selectSubtitleTrack(-1) instead of leaving the container-default selection showing a language the user never asked for. This branch previously only fired for the FORCED preference.

Interactions verified during review: persisted per-show track choices run first (restorePersistedTrackPreferenceIfNeeded sets the applied flag before this code executes) so they cannot be clobbered, and there is no automatic addon-subtitle apply at startup (both setSubtitleUri sites are explicit user actions) so the disable path cannot interfere with addon subtitles. One accepted edge: an untagged track labeled "Non-English parts only" would label-match an English preference — by convention that is an English forced-narrative track, so selecting it over nothing is reasonable.

Issue or approval

Fixes #1197

UI / behavior impact

  • No UI change
  • Behavior changed only to fix a documented bug/regression

Policy check

  • I have read and understood CONTRIBUTING.md.
  • This PR is small, focused, and limited to one problem.
  • This PR is not cosmetic-only.
  • Any UI change fixes a linked glitch/bug and includes visual proof, or this PR has no UI change.
  • Any behavior change fixes a linked bug/regression or has explicit approval, or this PR has no behavior change.
  • This PR does not bundle unrelated refactors, cleanups, formatting, or drive-by changes.
  • This PR does not add dependencies, architecture changes, migrations, or product-direction changes without explicit approval.
  • I listed the testing performed below.

Scope boundaries

  • The audio-track preference path has the same theoretical gap but no bug report; it is intentionally untouched.
  • languageMatchesPreference itself is unchanged — the fallback lives only in subtitle track selection.
  • No changes to addon subtitle fetching, filtering, or the FORCED-subtitle flow.

Testing

./gradlew :composeApp:compileFullDebugKotlinAndroid passes clean. ./gradlew :composeApp:testFullDebugUnitTest passes.

New unit test PlayerTrackSelectionTest (10 cases): ISO 639-1/639-2/regional tag matching as regression guards, the null and "und" label-fallback cases (these fail against the previous implementation), suffixed labels ("English [SDH]"), tagged-track-wins-over-label-only precedence, foreign-tag protection against coincidental labels, no-match returns -1, and secondary-target fallback.

Manual (please verify during review — needs a device):

  • Play an MKV whose subtitle tracks have no language tags, only titles ("Arabic", "English"): English is auto-selected with preferred language = English.
  • Play a file whose tracks include a tagged preferred-language track: that track is still selected as before.
  • Play a file with NO preferred-language track at all: no subtitle is shown (previously an unrelated container-default track displayed).
  • FORCED subtitle preference: behavior unchanged.

Screenshots / Video (UI changes only)

Not a UI change.

Breaking changes

None.

Linked issues

Fixes #1197

…agged tracks

Embedded subtitle tracks in MKV files frequently carry no ISO language
tag — format.language arrives as null or "und" with the language only
present in the track title (e.g. "English", "English [SDH]").
findPreferredSubtitleTrackIndex matched exclusively on the language
tag, so for these files the preferred-language lookup always failed,
the auto-selection left ExoPlayer's container-default track active,
and users saw Arabic/Italian/Spanish/Portuguese subtitles even though
an English track existed.

Two changes:
- When no track matches a target by language tag, fall back to
  matching the track label through the same normalizeLanguageCode
  path, restricted to tracks whose tag is null/blank/"und" so a
  usable foreign tag can never be hijacked by a coincidental label.
- When neither pass matches anything, explicitly disable the text
  track instead of leaving the container-default selection showing a
  language the user never asked for.

New unit test PlayerTrackSelectionTest covers tag matching (639-1,
639-2, regional), the null/"und" label fallback, suffixed labels,
tag-over-label precedence, foreign-tag protection, and secondary
target fallback. The null/"und" cases fail against the previous
implementation.

Fixes NuvioMedia#1197

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 11, 2026 23:44

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Adds label-based subtitle track matching when language tags are missing/unknown, plus adjusts runtime subtitle deselection behavior and introduces targeted tests.

Changes:

  • Add label fallback matching in findPreferredSubtitleTrackIndex when a track has no usable language tag (null / und).
  • Add common tests covering ISO tags, regional variants, and label fallback behavior.
  • Simplify runtime deselection logic when no preferred subtitle track is found.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
composeApp/src/commonTest/kotlin/com/nuvio/app/features/player/PlayerTrackSelectionTest.kt Adds test coverage for subtitle preference matching, including label fallback cases.
composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerTrackSelection.kt Implements label fallback selection for tracks without a usable language tag.
composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerScreenRuntimeTrackActions.kt Changes when subtitles are deselected if no preferred track is found.

@skoruppa

Copy link
Copy Markdown
Member

Hi :) If you are going to change things here, pleace check how we detect brazilian and latin american subtitles in the TV version. This is still missing in mobile and would be good to have it here

…419 tracks

Embedded tracks tagged with a bare "pt" or "es" often carry the
regional variant only in their name or track id ("Português (Brasil)",
"Español (Latinoamérica)"). Subtitle selection now derives an
effective variant from those fields using the same tag lists as the
TV app, so pt-BR and es-419 preferences can find their tracks.

Regional pairs are matched exactly, also mirroring TV: a "pt"
preference no longer selects a Brazilian track and "es" no longer
selects a Latin American one, nor the reverse.

normalizeLanguageCode additionally recognizes the accented
"português" / "español" spellings that real-world track labels use;
the unaccented checks missed them, which also broke the label
fallback for untagged Brazilian tracks.

Requested by review on the PR for cross-app consistency with NuvioTV.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@anikettuli

anikettuli commented Jun 12, 2026

Copy link
Copy Markdown
Contributor Author

@skoruppa Done in e71e337 — ported the TV detection into this PR. Tracks tagged with a bare pt/es now derive their regional variant from the track name/id using the same tag lists as PlayerSubtitleUtils.detectTrackLanguageVariant on TV (so "Português (Brasil)" with a pt tag matches a pt-BR preference), and the regional pairs are matched exactly like on TV — pt no longer selects Brazilian tracks, es no longer selects Latin American ones, nor the reverse. Also fixed mobile's normalizeLanguageCode missing the accented "português"/"español" spellings that real track labels use. Covered by 8 new unit tests including the untagged-Brazilian-label case.

On a separate note — I know it's unrelated, but I've been mistagged as @atuli instead of @anikettuli across multiple Nuvio repos in release notes and the Reddit posts, so the credit ended up pointing at the wrong account each time. Would appreciate getting that fixed going forward (and corrected in the old notes if possible).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: subtitles selecting every language other than preferred

3 participants