Add feature-gated dual subtitle MVP with merged subtitle track#2549
Add feature-gated dual subtitle MVP with merged subtitle track#2549beng1z wants to merge 1 commit intorecloudstream:masterfrom
Conversation
fire-light42
left a comment
There was a problem hiding this comment.
First round of comments, for the easiest to spot issues.
I will give you more once I can properly test it as the app reliably freezes when testing it.
We will not merge anything half-finished, as to keep CloudStream maintainable. We will therefore not merge a MVP. This feature will need to be polished before getting accepted.
|
|
||
| \n%s</string> | ||
| <string name="delete_message_series_section" formatted="true">رح كمان تمحي ل الأبد كل الحلقات اللي ب هيدا المسلسل؟ | ||
| \n |
There was a problem hiding this comment.
Why all of these removals?
There was a problem hiding this comment.
Most of that diff was unintended newline and whitespace churn from touching the locale file. In the follow-up I trimmed the locale changes back and removed the AI-added dual subtitle strings from translated files; for now the new keys are only kept in the base strings file.
| return Pair(listOf(merged.first), listOf(merged.second)) | ||
| } | ||
|
|
||
| event(ErrorEvent(ErrorLoadingException(context.getString(R.string.subtitles_secondary_load_failed)))) |
There was a problem hiding this comment.
These events cause an error loop wherein the player goes to the next source and the subtitles are loaded again. This freezes the app.
There was a problem hiding this comment.
I removed the ErrorEvent path here and changed it to clear the secondary selection and fall through to the normal single subtitle load path instead. That was meant to avoid the reload loop you pointed out.
|
|
||
| event(ErrorEvent(ErrorLoadingException(context.getString(R.string.subtitles_secondary_load_failed)))) | ||
| dualMergedTrackId = null | ||
| return Pair(emptyList(), emptyList()) |
There was a problem hiding this comment.
You should fall back to single subtitles if an error occurs.
There was a problem hiding this comment.
Changed this in the follow-up. Unsupported or failed dual subtitle cases now drop back to the existing single subtitle path instead of returning empty sources or surfacing a dual subtitle load error from here.
| private fun cleanDualSubtitleCache(context: Context, exclude: File? = null) { | ||
| try { | ||
| context.cacheDir.listFiles { file -> | ||
| file.name.startsWith("dual_sub_") && file.name.endsWith(".vtt") && file != exclude |
There was a problem hiding this comment.
The name should be in a constant, as to prevent issues when someone in the future renames the subtitle file. The subtitle files should preferably also be in its own folder to ease future debugging.
There was a problem hiding this comment.
Adjusted this. The generated files now live under a dedicated dual_subtitles cache subdirectory, and the dir, prefix, and extension are pulled into constants instead of being inlined.
| return try { | ||
| when (subtitle.origin) { | ||
| SubtitleOrigin.URL -> { | ||
| val mergedHeaders = mutableMapOf<String, String>().apply { |
There was a problem hiding this comment.
Why merged headers? Other subtitles do not have merged headers.
There was a problem hiding this comment.
Removed that header merge in the follow-up and switched this back to subtitle.headers only. I left the broader header and referer handling out of this PR.
| } | ||
|
|
||
| private fun isDualSubtitleTrackSelectionEnabled(): Boolean { | ||
| val enabled = getKey<Boolean>(SUBTITLE_DUAL_ENABLED_KEY) ?: false |
There was a problem hiding this comment.
WET code, use the function you defined
There was a problem hiding this comment.
Changed this to use player.isDualSubtitleCombinationSupported(...) and moved the shared check onto IPlayer and CS3IPlayer, so GeneratorPlayer no longer duplicates the logic.
|
|
||
| <string name="subtitles_dual_enabled">ଡୁଆଲ୍ ସବ୍ଟାଇଟ୍ (ପରୀକ୍ଷାମୂଳକ)</string> | ||
| <string name="subtitles_edit_primary">ପ୍ରାଥମିକ</string> | ||
| <string name="subtitles_edit_secondary">ଦ୍ Secondary ିତୀୟ</string> |
There was a problem hiding this comment.
Do not translate strings using AI, let real people do the translations.
There was a problem hiding this comment.
Removed the AI-added dual subtitle strings from the translated locale files in the follow-up. For now the new subtitle keys are only kept in the base strings file.
Add opt-in dual-subtitle support (disabled by default) where the player merges a primary and secondary subtitle track into a single generated WebVTT file at parse time, rather than rendering two overlay tracks. - `IPlayer` gets `setSecondarySubtitles` / `getCurrentSecondarySubtitle` and `isDualSubtitleCombinationSupported`. - `CS3IPlayer` builds the merged track via `DualSubtitleComposer`, caches it under `cacheDir/dual_subtitles/dual_sub_<hash>.vtt` (constants for dir/prefix/extension), reuses the file on reload, and cleans stale entries on episode change. - Network / file I/O for the merge is wrapped in `runBlocking(Dispatchers.IO)` so it never runs on the main thread when `loadOnlinePlayer` is called from the UI thread. - On unsupported combinations (embedded secondary, secondary without primary, unsupported origin pair) the pipeline logs and falls back to the normal single-subtitle path instead of firing an `ErrorEvent` that previously triggered a next-source reload loop. - `GeneratorPlayer` adds a Primary/Secondary selection mode in the source/subtitle dialog (gated on the new preference), uses an Apply-based commit so selections are only sent to the player once, and blocks unsupported combinations with a localized toast. - New `Dual subtitles (experimental)` toggle in subtitle settings (`SUBTITLE_DUAL_ENABLED_KEY`). - Landscape and portrait subtitle dialog layouts get the new tab buttons with `contentDescription` for accessibility, plus a color selector for the active-tab text. - New base strings only; translated locales are left untouched. - `DualSubtitleComposer` + unit tests cover overlap merging, adjacent same-text coalescing, short cue boundaries, primary/secondary-only cases, negative durations, and WebVTT header/timestamp formatting.
ada76ed to
9ff0789
Compare
|
Follow-up update:
I understand the maintainability concern about merging an MVP — if there are specific polish items beyond what's above that need to land before this is acceptable, I'm happy to address them in-place rather than as a follow-up. |
Summary
offby default).What changed
IPlayerAPI expanded with secondary subtitle methods.CS3IPlayerimplements secondary state, dual merge pipeline, and merged track selection.GeneratorPlayerupdated for Primary/Secondary selection UX and Apply-based commit behavior.Dual subtitles (experimental)switch.DualSubtitleComposer+ unit tests.AI usage disclosure
Testing
./gradlew testStableDebugUnitTest assembleStableDebug --no-daemonpasses.Notes