mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-09 08:01:20 +02:00
Add "Only Forced Subtitles" option to subtitle selection (#589)
## Description Adds a client-side "Only Forced Subtitles" option when selecting subtitles, per the discussion in #571. ### Changes - **TrackIndex.ONLY_FORCED** (`-3`): New constant for the special case - **StreamChoiceService**: Intercepts `ONLY_FORCED` at the top of `chooseSubtitleStream()` before server-side `SubtitlePlaybackMode` logic (which remains untouched) - **UI**: Adds "Only Forced Subtitles" option to both: - In-player subtitle menu (`PlaybackDialog.kt`) - Pre-playback stream selection dialog (`Dialogs.kt`) ### Forced track detection Uses metadata `isForced` flag as primary check, with title-based fallback ("forced", "signs", "songs" patterns) when language matches audio. ### Related issues Closes #571 ### Screenshots N/A - subtitle selection menu only ### AI/LLM usage Used Claude Code for assistance and to draft PR description. --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> Co-authored-by: Damontecres <damontecres@gmail.com>
This commit is contained in:
parent
8bdc8a6f8f
commit
ed67e5575f
8 changed files with 279 additions and 5 deletions
|
|
@ -528,6 +528,16 @@ fun chooseStream(
|
|||
onClick = { onClick.invoke(TrackIndex.DISABLED) },
|
||||
),
|
||||
)
|
||||
add(
|
||||
DialogItem(
|
||||
headlineContent = {
|
||||
Text(text = stringResource(R.string.only_forced_subtitles))
|
||||
},
|
||||
supportingContent = {
|
||||
},
|
||||
onClick = { onClick.invoke(TrackIndex.ONLY_FORCED) },
|
||||
),
|
||||
)
|
||||
}
|
||||
addAll(
|
||||
streams.filter { it.type == type }.mapIndexed { index, stream ->
|
||||
|
|
|
|||
|
|
@ -106,6 +106,8 @@ fun PlaybackDialog(
|
|||
onPlaybackActionClick.invoke(PlaybackAction.ToggleCaptions(subtitleIndex))
|
||||
} else if (subtitleIndex == TrackIndex.DISABLED) {
|
||||
onPlaybackActionClick.invoke(PlaybackAction.ToggleCaptions(TrackIndex.DISABLED))
|
||||
} else if (subtitleIndex == TrackIndex.ONLY_FORCED) {
|
||||
onPlaybackActionClick.invoke(PlaybackAction.ToggleCaptions(TrackIndex.ONLY_FORCED))
|
||||
}
|
||||
},
|
||||
onSelectSearch = {
|
||||
|
|
@ -322,6 +324,23 @@ fun SubtitleChoiceBottomDialog(
|
|||
supportingContent = {},
|
||||
)
|
||||
}
|
||||
item {
|
||||
ListItem(
|
||||
selected = currentChoice == TrackIndex.ONLY_FORCED,
|
||||
onClick = {
|
||||
onSelectChoice(TrackIndex.ONLY_FORCED)
|
||||
},
|
||||
leadingContent = {
|
||||
SelectedLeadingContent(currentChoice == TrackIndex.ONLY_FORCED)
|
||||
},
|
||||
headlineContent = {
|
||||
Text(
|
||||
text = stringResource(R.string.only_forced_subtitles),
|
||||
)
|
||||
},
|
||||
supportingContent = {},
|
||||
)
|
||||
}
|
||||
itemsIndexed(choices) { index, choice ->
|
||||
val interactionSource = remember { MutableInteractionSource() }
|
||||
ListItem(
|
||||
|
|
|
|||
|
|
@ -523,7 +523,13 @@ class PlaybackViewModel
|
|||
currentItemPlayback.copy(
|
||||
sourceId = source.id?.toUUIDOrNull(),
|
||||
audioIndex = audioIndex ?: TrackIndex.UNSPECIFIED,
|
||||
subtitleIndex = subtitleIndex ?: TrackIndex.DISABLED,
|
||||
// Preserve special constants (ONLY_FORCED, DISABLED) instead of resolved index
|
||||
subtitleIndex =
|
||||
if (currentItemPlayback.subtitleIndex < 0) {
|
||||
currentItemPlayback.subtitleIndex
|
||||
} else {
|
||||
subtitleIndex ?: TrackIndex.DISABLED
|
||||
},
|
||||
)
|
||||
if (userInitiated) {
|
||||
viewModelScope.launchIO {
|
||||
|
|
@ -744,11 +750,27 @@ class PlaybackViewModel
|
|||
type = MediaStreamType.AUDIO,
|
||||
)
|
||||
this@PlaybackViewModel.currentItemPlayback.setValueOnMain(itemPlayback)
|
||||
|
||||
// Resolve ONLY_FORCED to actual track based on new audio language
|
||||
val source = currentPlayback.value?.mediaSourceInfo
|
||||
val resolvedSubtitleIndex =
|
||||
if (source != null) {
|
||||
streamChoiceService.resolveSubtitleIndex(
|
||||
source = source,
|
||||
audioStreamIndex = index,
|
||||
seriesId = item.data.seriesId,
|
||||
subtitleIndex = itemPlayback.subtitleIndex,
|
||||
prefs = preferences,
|
||||
)
|
||||
} else {
|
||||
itemPlayback.subtitleIndex.takeIf { it >= 0 }
|
||||
}
|
||||
|
||||
changeStreams(
|
||||
item,
|
||||
itemPlayback,
|
||||
index,
|
||||
itemPlayback.subtitleIndex,
|
||||
resolvedSubtitleIndex,
|
||||
onMain { player.currentPosition },
|
||||
true,
|
||||
)
|
||||
|
|
@ -766,11 +788,27 @@ class PlaybackViewModel
|
|||
type = MediaStreamType.SUBTITLE,
|
||||
)
|
||||
this@PlaybackViewModel.currentItemPlayback.setValueOnMain(itemPlayback)
|
||||
|
||||
// Resolve ONLY_FORCED to actual track index for playback
|
||||
val source = currentPlayback.value?.mediaSourceInfo
|
||||
val resolvedIndex =
|
||||
if (source != null) {
|
||||
streamChoiceService.resolveSubtitleIndex(
|
||||
source = source,
|
||||
audioStreamIndex = itemPlayback.audioIndex,
|
||||
seriesId = item.data.seriesId,
|
||||
subtitleIndex = index,
|
||||
prefs = preferences,
|
||||
)
|
||||
} else {
|
||||
index.takeIf { it >= 0 }
|
||||
}
|
||||
|
||||
changeStreams(
|
||||
item,
|
||||
itemPlayback,
|
||||
itemPlayback.audioIndex,
|
||||
index,
|
||||
resolvedIndex,
|
||||
onMain { player.currentPosition },
|
||||
true,
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue