From c05301bae8d5065d301cbd7a3c761a5c58d774ee Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 17 Aug 2026 21:02:47 +0200 Subject: [PATCH] fix: group player subtitles by resolved language name - Group subtitle tracks by language name derived from IETF tag instead of raw originalName (e.g. "fr" and "French [SUB]" now land in the same "French" group) - Fall back to "Unknown" group when language cannot be resolved - Label tracks within a group by originalName instead of nameSuffix - Filter subtitles by IETF tag match instead of name substring, matching primary subtag so regional variants (pt-br) match (pt) - Keep subtitles with unresolvable language instead of dropping them - Only exempt DOWNLOADED_FILE from filtering so embedded tracks are also filtered across player reloads - Use getApiProviderLangSettings + fromCodeToLangTagIETF for proper tag normalization in the filter list --- .../cloudstream3/ui/player/GeneratorPlayer.kt | 30 +++++++++++-------- .../ui/player/PlayerGeneratorViewModel.kt | 12 ++++++-- app/src/main/res/values/strings.xml | 1 + 3 files changed, 28 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt index 4495a560262..7df9d814221 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt @@ -47,6 +47,7 @@ import androidx.preference.PreferenceManager import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull +import com.lagradost.cloudstream3.AllLanguagesName import com.lagradost.cloudstream3.CloudStreamApp import com.lagradost.cloudstream3.CloudStreamApp.Companion.setKey import com.lagradost.cloudstream3.CommonActivity.showToast @@ -101,6 +102,7 @@ import com.lagradost.cloudstream3.ui.settings.Globals.isLayout import com.lagradost.cloudstream3.ui.subtitles.SUBTITLE_AUTO_SELECT_KEY import com.lagradost.cloudstream3.ui.subtitles.SubtitlesFragment import com.lagradost.cloudstream3.ui.subtitles.SubtitlesFragment.Companion.getAutoSelectLanguageTagIETF +import com.lagradost.cloudstream3.utils.AppContextUtils.getApiProviderLangSettings import com.lagradost.cloudstream3.utils.AppContextUtils.getShortSeasonText import com.lagradost.cloudstream3.utils.AppContextUtils.html import com.lagradost.cloudstream3.utils.AppContextUtils.sortSubs @@ -112,7 +114,7 @@ import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.ExtractorLinkType import com.lagradost.cloudstream3.utils.Qualities import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showDialog -import com.lagradost.cloudstream3.utils.SubtitleHelper.fromTagToEnglishLanguageName +import com.lagradost.cloudstream3.utils.SubtitleHelper.fromCodeToLangTagIETF import com.lagradost.cloudstream3.utils.SubtitleHelper.fromTagToLanguageName import com.lagradost.cloudstream3.utils.SubtitleHelper.languages import com.lagradost.cloudstream3.utils.UIHelper.clipboardHelper @@ -1195,8 +1197,14 @@ class GeneratorPlayer : FullScreenPlayer() { ArrayAdapter(ctx, R.layout.sort_bottom_single_choice) subsArrayAdapter.add(ctx.getString(R.string.no_subtitles).html()) + val unknownGroupName = ctx.getString(R.string.subtitles_group_unknown) + fun groupName(sub: SubtitleData): String { + return fromTagToLanguageName(sub.getIETF_tag())?.takeIf { it.isNotBlank() } + ?: unknownGroupName + } + val subtitlesGrouped = - currentSubtitles.groupBy { it.originalName }.map { (key, value) -> + currentSubtitles.groupBy { groupName(it) }.map { (key, value) -> key to value.sortedBy { it.nameSuffix.toIntOrNull() ?: 0 } }.toMap() val subtitlesGroupedList = subtitlesGrouped.entries.toList() @@ -1204,11 +1212,11 @@ class GeneratorPlayer : FullScreenPlayer() { val subtitles = subtitlesGrouped.map { it.key.html() } val subtitleGroupIndexStart = - subtitlesGrouped.keys.indexOf(currentSelectedSubtitles?.originalName) + 1 + subtitlesGrouped.keys.indexOf(currentSelectedSubtitles?.let { groupName(it) }) + 1 var subtitleGroupIndex = subtitleGroupIndexStart val subtitleOptionIndexStart = - subtitlesGrouped[currentSelectedSubtitles?.originalName]?.indexOfFirst { it.nameSuffix == currentSelectedSubtitles?.nameSuffix } + subtitlesGrouped[currentSelectedSubtitles?.let { groupName(it) }]?.indexOfFirst { it.nameSuffix == currentSelectedSubtitles?.nameSuffix } ?: 0 var subtitleOptionIndex = subtitleOptionIndexStart @@ -1232,8 +1240,8 @@ class GeneratorPlayer : FullScreenPlayer() { val subtitleOptions = subtitlesGroupedList .getOrNull(subtitleGroupIndex - 1)?.value?.map { subtitle -> - val nameSuffix = subtitle.nameSuffix.html() - nameSuffix.ifBlank { + val label = subtitle.originalName.html() + label.ifBlank { when (subtitle.origin) { SubtitleOrigin.URL -> txt(R.string.subtitles_from_online) SubtitleOrigin.DOWNLOADED_FILE -> txt(R.string.downloaded) @@ -2264,12 +2272,10 @@ class GeneratorPlayer : FullScreenPlayer() { viewModel.filterSubByLang = settingsManager.getBoolean(getString(R.string.filter_sub_lang_key), false) if (viewModel.filterSubByLang) { - val langFromPrefMedia = settingsManager.getStringSet( - this.getString(R.string.provider_lang_key), mutableSetOf("en") - ) - viewModel.langFilterList = langFromPrefMedia?.mapNotNull { - fromTagToEnglishLanguageName(it)?.lowercase() ?: return@mapNotNull null - } ?: listOf() + viewModel.langFilterList = ctx.getApiProviderLangSettings().map { tag -> + if (tag == AllLanguagesName) tag + else fromCodeToLangTagIETF(tag)?.lowercase() ?: tag.lowercase() + } } // Set up TV clock visibility diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/PlayerGeneratorViewModel.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/PlayerGeneratorViewModel.kt index cb8cf8bfff5..0e1e381c88b 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/PlayerGeneratorViewModel.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/PlayerGeneratorViewModel.kt @@ -5,6 +5,7 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.lagradost.cloudstream3.AllLanguagesName import com.lagradost.cloudstream3.LoadResponse import com.lagradost.cloudstream3.mvvm.Resource import com.lagradost.cloudstream3.mvvm.launchSafe @@ -371,13 +372,18 @@ class PlayerGeneratorViewModel : ViewModel() { return true } - /** Only filter out subtitles fetched online */ - if (subtitle.origin != SubtitleOrigin.URL) { + if (langFilterList.contains(AllLanguagesName)) { return true } + if (subtitle.origin == SubtitleOrigin.DOWNLOADED_FILE) { + return true + } + + val subtitleTag = subtitle.getIETF_tag()?.lowercase() ?: return true + return langFilterList.any { lang -> - subtitle.originalName.contains(lang, ignoreCase = true) + subtitleTag == lang || subtitleTag.substringBefore('-') == lang.substringBefore('-') } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7d530b59f6e..3b90ebc5a61 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -487,6 +487,7 @@ Remove closed captions from subtitles Remove bloat from subtitles Filter by preferred media language + Unknown Extras Trailer https://example.com/example.mp4