diff --git a/.beads/interactions.jsonl b/.beads/interactions.jsonl index ba85e4d..654e9c5 100644 --- a/.beads/interactions.jsonl +++ b/.beads/interactions.jsonl @@ -21,3 +21,4 @@ {"id":"int-6343b773","kind":"field_change","created_at":"2026-05-25T14:25:59.50764Z","actor":"dirtydishes","issue_id":"dreamio-bd9","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Stopped rebuilding the captions menu on every progress refresh and validated the build."}} {"id":"int-26b872a1","kind":"field_change","created_at":"2026-05-25T14:31:46.83464Z","actor":"dirtydishes","issue_id":"dreamio-ese","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Added subtitle-shaped fetch/XHR inspection diagnostics and validated the build."}} {"id":"int-4e095d3f","kind":"field_change","created_at":"2026-05-25T14:38:21.968713Z","actor":"dirtydishes","issue_id":"dreamio-djc","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Auto-select the first discovered VLC subtitle track when playback is still disabled, while preserving manual caption choices."}} +{"id":"int-96629c65","kind":"field_change","created_at":"2026-05-25T14:45:38.521113Z","actor":"dirtydishes","issue_id":"dreamio-ppj","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Re-applied the auto-selected VLC subtitle track after stream discovery and playback state changes to harden rendering timing."}} diff --git a/.beads/issues.jsonl b/.beads/issues.jsonl index 503714f..4986ec7 100644 --- a/.beads/issues.jsonl +++ b/.beads/issues.jsonl @@ -1,5 +1,5 @@ {"_type":"issue","id":"dreamio-8cz","title":"fix stremio external subtitle loading regression","description":"After adding late subtitle forwarding for native playback, Stremio external subtitle loading is failing. Investigate the injected bridge and native subtitle forwarding path, then adjust behavior so Stremio can still load external subtitles while native playback receives late candidates.","status":"closed","priority":0,"issue_type":"bug","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-25T11:05:42Z","created_by":"dirtydishes","updated_at":"2026-05-25T11:07:35Z","started_at":"2026-05-25T11:05:55Z","closed_at":"2026-05-25T11:07:35Z","close_reason":"Hardened subtitle bridge network observers so non-text Stremio subtitle loads are not touched, and made parser traversal deterministic for metadata preservation.","dependency_count":0,"dependent_count":0,"comment_count":0} -{"_type":"issue","id":"dreamio-ppj","title":"Reapply VLC embedded subtitle selection after track discovery","description":"Device logs show VLC eventually exposes and selects the embedded English SDH subtitle track, but subtitles still do not render. Investigate and harden the VLC selection timing so embedded tracks are selected after discovery is stable.","status":"open","priority":1,"issue_type":"bug","owner":"dishes@dpdrm.com","created_at":"2026-05-25T14:44:08Z","created_by":"dirtydishes","updated_at":"2026-05-25T14:44:08Z","dependency_count":0,"dependent_count":0,"comment_count":0} +{"_type":"issue","id":"dreamio-ppj","title":"Reapply VLC embedded subtitle selection after track discovery","description":"Device logs show VLC eventually exposes and selects the embedded English SDH subtitle track, but subtitles still do not render. Investigate and harden the VLC selection timing so embedded tracks are selected after discovery is stable.","status":"closed","priority":1,"issue_type":"bug","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-25T14:44:08Z","created_by":"dirtydishes","updated_at":"2026-05-25T14:45:38Z","started_at":"2026-05-25T14:44:18Z","closed_at":"2026-05-25T14:45:38Z","close_reason":"Re-applied the auto-selected VLC subtitle track after stream discovery and playback state changes to harden rendering timing.","dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"dreamio-djc","title":"Auto-select embedded VLC subtitle tracks","description":"VLC discovers embedded MKV subtitle tracks after playback starts, but Dreamio leaves subtitles disabled when no external candidates were provided. Add automatic selection for the first selectable VLC subtitle track while preserving manual caption choices.","status":"closed","priority":1,"issue_type":"bug","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-25T14:36:11Z","created_by":"dirtydishes","updated_at":"2026-05-25T14:38:22Z","started_at":"2026-05-25T14:36:17Z","closed_at":"2026-05-25T14:38:22Z","close_reason":"Auto-select the first discovered VLC subtitle track when playback is still disabled, while preserving manual caption choices.","dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"dreamio-ese","title":"Discover Stremio external subtitle payloads","description":"Extend and instrument the injected web subtitle discovery path so Stremio/OpenSubtitles addon responses can be captured when native playback only sees embedded VLC subtitle tracks.","status":"closed","priority":1,"issue_type":"bug","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-25T14:29:57Z","created_by":"dirtydishes","updated_at":"2026-05-25T14:31:47Z","started_at":"2026-05-25T14:30:03Z","closed_at":"2026-05-25T14:31:47Z","close_reason":"Added subtitle-shaped fetch/XHR inspection diagnostics and validated the build.","dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"dreamio-bd9","title":"Stabilize captions menu refresh","description":"Stop rebuilding the captions UIMenu on every playback progress refresh so embedded subtitle actions can remain stable long enough to fire, while keeping DEBUG logs for menu state and selection.","status":"closed","priority":1,"issue_type":"bug","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-25T14:24:45Z","created_by":"dirtydishes","updated_at":"2026-05-25T14:25:59Z","started_at":"2026-05-25T14:24:50Z","closed_at":"2026-05-25T14:25:59Z","close_reason":"Stopped rebuilding the captions menu on every progress refresh and validated the build.","dependency_count":0,"dependent_count":0,"comment_count":0} diff --git a/Dreamio/VLCNativePlaybackBackend.swift b/Dreamio/VLCNativePlaybackBackend.swift index 209128d..f23b8bc 100644 --- a/Dreamio/VLCNativePlaybackBackend.swift +++ b/Dreamio/VLCNativePlaybackBackend.swift @@ -25,6 +25,7 @@ final class VLCNativePlaybackBackend: NSObject, NativePlaybackBackend { private var attachedSubtitleURLs = Set() private var didAutoSelectSubtitleTrack = false private var didUserSelectSubtitleTrack = false + private var autoSelectedSubtitleTrackID: Int32? override init() { super.init() @@ -45,6 +46,7 @@ final class VLCNativePlaybackBackend: NSObject, NativePlaybackBackend { attachedSubtitleURLs.removeAll() didAutoSelectSubtitleTrack = false didUserSelectSubtitleTrack = false + autoSelectedSubtitleTrackID = nil let media = VLCMedia(url: request.playbackURL) let headerValue = request.headers .map { "\($0.key): \($0.value)" } @@ -105,6 +107,7 @@ final class VLCNativePlaybackBackend: NSObject, NativePlaybackBackend { func selectSubtitleTrack(id: Int32) { #if canImport(MobileVLCKit) didUserSelectSubtitleTrack = true + autoSelectedSubtitleTrackID = nil #if DEBUG logSubtitleTracks(reason: "before-select-\(id)") #endif @@ -270,10 +273,33 @@ final class VLCNativePlaybackBackend: NSObject, NativePlaybackBackend { } didAutoSelectSubtitleTrack = true + autoSelectedSubtitleTrackID = track.id #if DEBUG print("[DreamioVLC] auto-select subtitle id=\(track.id) name=\(track.name) reason=\(reason)") #endif mediaPlayer.currentVideoSubTitleIndex = track.id + scheduleAutoSubtitleSelectionReapply(trackID: track.id) + } + + private func scheduleAutoSubtitleSelectionReapply(trackID: Int32) { + [0.3, 1.0, 2.0, 4.0].forEach { delay in + DispatchQueue.main.asyncAfter(deadline: .now() + delay) { [weak self] in + self?.reapplyAutoSelectedSubtitleTrackIfNeeded(reason: "delayed-\(String(format: "%.1f", delay))") + } + } + } + + private func reapplyAutoSelectedSubtitleTrackIfNeeded(reason: String) { + guard !didUserSelectSubtitleTrack, + let trackID = autoSelectedSubtitleTrackID, + subtitleTracks.contains(where: { $0.id == trackID }) else { + return + } + + mediaPlayer.currentVideoSubTitleIndex = trackID +#if DEBUG + print("[DreamioVLC] reapply subtitle id=\(trackID) reason=\(reason) selected=\(mediaPlayer.currentVideoSubTitleIndex)") +#endif } #endif } @@ -286,6 +312,7 @@ extension VLCNativePlaybackBackend: VLCMediaPlayerDelegate { #endif switch mediaPlayer.state { case .buffering, .playing: + reapplyAutoSelectedSubtitleTrackIfNeeded(reason: stateName(mediaPlayer.state)) onReady?() onStateChange?() case .error: diff --git a/docs/turns/2026-05-25-auto-select-vlc-subtitle-tracks.html b/docs/turns/2026-05-25-auto-select-vlc-subtitle-tracks.html index 57bcb80..033eb0d 100644 --- a/docs/turns/2026-05-25-auto-select-vlc-subtitle-tracks.html +++ b/docs/turns/2026-05-25-auto-select-vlc-subtitle-tracks.html @@ -228,6 +228,84 @@
  • Consider a future setting for “auto-enable embedded subtitles” if users want control over the default behavior.
  • +
    +

    New Changes as of May 25, 2026 at 10:45 AM EDT

    +

    Summary of changes: Added a timed re-apply loop for the automatically selected VLC subtitle track. Dreamio now remembers the auto-selected track id and re-sends it after short delays and when VLC reports buffering or playing.

    +

    Why this change was made: Follow-up device logs showed VLC selected track 3, but subtitles still did not render. That points to a MobileVLCKit timing issue after elementary stream discovery, so the selected embedded track is re-applied after playback settles.

    +

    Code diffs:

    +
    Dreamio/VLCNativePlaybackBackend.swift
    +27
    24 unmodified lines
    25
    26
    27
    28
    29
    30
    14 unmodified lines
    45
    46
    47
    48
    49
    50
    54 unmodified lines
    105
    106
    107
    108
    109
    110
    159 unmodified lines
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    6 unmodified lines
    286
    287
    288
    289
    290
    291
    24 unmodified lines
    private var attachedSubtitleURLs = Set<URL>()
    private var didAutoSelectSubtitleTrack = false
    private var didUserSelectSubtitleTrack = false
    +
    override init() {
    super.init()
    14 unmodified lines
    attachedSubtitleURLs.removeAll()
    didAutoSelectSubtitleTrack = false
    didUserSelectSubtitleTrack = false
    let media = VLCMedia(url: request.playbackURL)
    let headerValue = request.headers
    .map { "\($0.key): \($0.value)" }
    54 unmodified lines
    func selectSubtitleTrack(id: Int32) {
    #if canImport(MobileVLCKit)
    didUserSelectSubtitleTrack = true
    #if DEBUG
    logSubtitleTracks(reason: "before-select-\(id)")
    #endif
    159 unmodified lines
    }
    +
    didAutoSelectSubtitleTrack = true
    #if DEBUG
    print("[DreamioVLC] auto-select subtitle id=\(track.id) name=\(track.name) reason=\(reason)")
    #endif
    mediaPlayer.currentVideoSubTitleIndex = track.id
    }
    #endif
    }
    6 unmodified lines
    #endif
    switch mediaPlayer.state {
    case .buffering, .playing:
    onReady?()
    onStateChange?()
    case .error:
    24 unmodified lines
    25
    26
    27
    28
    29
    30
    31
    14 unmodified lines
    46
    47
    48
    49
    50
    51
    52
    54 unmodified lines
    107
    108
    109
    110
    111
    112
    113
    159 unmodified lines
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    6 unmodified lines
    312
    313
    314
    315
    316
    317
    318
    24 unmodified lines
    private var attachedSubtitleURLs = Set<URL>()
    private var didAutoSelectSubtitleTrack = false
    private var didUserSelectSubtitleTrack = false
    private var autoSelectedSubtitleTrackID: Int32?
    +
    override init() {
    super.init()
    14 unmodified lines
    attachedSubtitleURLs.removeAll()
    didAutoSelectSubtitleTrack = false
    didUserSelectSubtitleTrack = false
    autoSelectedSubtitleTrackID = nil
    let media = VLCMedia(url: request.playbackURL)
    let headerValue = request.headers
    .map { "\($0.key): \($0.value)" }
    54 unmodified lines
    func selectSubtitleTrack(id: Int32) {
    #if canImport(MobileVLCKit)
    didUserSelectSubtitleTrack = true
    autoSelectedSubtitleTrackID = nil
    #if DEBUG
    logSubtitleTracks(reason: "before-select-\(id)")
    #endif
    159 unmodified lines
    }
    +
    didAutoSelectSubtitleTrack = true
    autoSelectedSubtitleTrackID = track.id
    #if DEBUG
    print("[DreamioVLC] auto-select subtitle id=\(track.id) name=\(track.name) reason=\(reason)")
    #endif
    mediaPlayer.currentVideoSubTitleIndex = track.id
    scheduleAutoSubtitleSelectionReapply(trackID: track.id)
    }
    +
    private func scheduleAutoSubtitleSelectionReapply(trackID: Int32) {
    [0.3, 1.0, 2.0, 4.0].forEach { delay in
    DispatchQueue.main.asyncAfter(deadline: .now() + delay) { [weak self] in
    self?.reapplyAutoSelectedSubtitleTrackIfNeeded(reason: "delayed-\(String(format: "%.1f", delay))")
    }
    }
    }
    +
    private func reapplyAutoSelectedSubtitleTrackIfNeeded(reason: String) {
    guard !didUserSelectSubtitleTrack,
    let trackID = autoSelectedSubtitleTrackID,
    subtitleTracks.contains(where: { $0.id == trackID }) else {
    return
    }
    +
    mediaPlayer.currentVideoSubTitleIndex = trackID
    #if DEBUG
    print("[DreamioVLC] reapply subtitle id=\(trackID) reason=\(reason) selected=\(mediaPlayer.currentVideoSubTitleIndex)")
    #endif
    }
    #endif
    }
    6 unmodified lines
    #endif
    switch mediaPlayer.state {
    case .buffering, .playing:
    reapplyAutoSelectedSubtitleTrackIfNeeded(reason: stateName(mediaPlayer.state))
    onReady?()
    onStateChange?()
    case .error:
    +

    Related issues or PRs: Beads issue dreamio-ppj.

    +
    + \ No newline at end of file