diff --git a/.beads/interactions.jsonl b/.beads/interactions.jsonl index 80ecff8..1802479 100644 --- a/.beads/interactions.jsonl +++ b/.beads/interactions.jsonl @@ -50,3 +50,4 @@ {"id":"int-204223f5","kind":"field_change","created_at":"2026-05-26T04:56:13.920284Z","actor":"dirtydishes","issue_id":"dreamio-816","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Bypassed the local range cache for Matroska-family tail-index containers and added a regression test confirming MKV probes fall back to direct VLC playback without issuing cache probe requests."}} {"id":"int-b6f641ed","kind":"field_change","created_at":"2026-05-26T12:10:16.392655Z","actor":"dirtydishes","issue_id":"dreamio-3sw","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Removed the Matroska/WebM extension-level range-cache bypass and added a regression test proving MKV URLs use the cache when the origin advertises byte-range support."}} {"id":"int-2b073805","kind":"field_change","created_at":"2026-05-26T12:16:53.567972Z","actor":"dirtydishes","issue_id":"dreamio-btc","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Added a short timeout to range-cache probe requests so slow MKV HEAD/range probes fall back to direct VLC startup instead of tripping the native-player startup timeout."}} +{"id":"int-1ed0a18a","kind":"field_change","created_at":"2026-05-26T13:01:27.690486Z","actor":"dirtydishes","issue_id":"dreamio-dd7","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Changed VLC startup to open direct playback immediately instead of waiting for slow range-cache probes, restoring reliable native-player startup for MKV streams."}} diff --git a/.beads/issues.jsonl b/.beads/issues.jsonl index e1289f0..b125b9d 100644 --- a/.beads/issues.jsonl +++ b/.beads/issues.jsonl @@ -1,3 +1,4 @@ +{"_type":"issue","id":"dreamio-dd7","title":"Start VLC before slow range-cache probes","description":"Current MKV cache probing can still block native VLC startup because HEAD and tiny range timeout sequentially before any media is opened. Start direct playback immediately or otherwise ensure VLC media opens before probing completes, while preserving range-cache support when it is ready quickly.","status":"closed","priority":0,"issue_type":"bug","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-26T13:00:37Z","created_by":"dirtydishes","updated_at":"2026-05-26T13:01:28Z","started_at":"2026-05-26T13:00:43Z","closed_at":"2026-05-26T13:01:28Z","close_reason":"Changed VLC startup to open direct playback immediately instead of waiting for slow range-cache probes, restoring reliable native-player startup for MKV streams.","dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"dreamio-btc","title":"Bound VLC range cache probe startup latency","description":"After enabling MKV range cache probing, some Torrentio/Real-Debrid MKV streams log cache-probe but never reach opening mode before the native-player startup timeout. Add a bounded probe/local-cache startup path that falls back to direct playback when the range probe is slow or inconclusive.","status":"closed","priority":0,"issue_type":"bug","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-26T12:14:02Z","created_by":"dirtydishes","updated_at":"2026-05-26T12:16:53Z","started_at":"2026-05-26T12:14:11Z","closed_at":"2026-05-26T12:16:53Z","close_reason":"Added a short timeout to range-cache probe requests so slow MKV HEAD/range probes fall back to direct VLC startup instead of tripping the native-player startup timeout.","dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"dreamio-mun","title":"fix vlc cache loopback port startup","description":"Device logs showed local-cache playback opening http://127.0.0.1:0, because the NWListener ephemeral port was read before the listener reached ready. Wait for the real assigned port before returning the local cache URL.","status":"closed","priority":0,"issue_type":"bug","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-25T22:32:41Z","created_by":"dirtydishes","updated_at":"2026-05-25T22:33:15Z","started_at":"2026-05-25T22:33:14Z","closed_at":"2026-05-25T22:33:15Z","close_reason":"Wait for NWListener ready state before returning the local cache URL; verified tests and simulator build.","dependency_count":0,"dependent_count":0,"comment_count":0} {"_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} diff --git a/Dreamio/VLCNativePlaybackBackend.swift b/Dreamio/VLCNativePlaybackBackend.swift index d9ee3cf..5a0ee11 100644 --- a/Dreamio/VLCNativePlaybackBackend.swift +++ b/Dreamio/VLCNativePlaybackBackend.swift @@ -71,59 +71,15 @@ final class VLCNativePlaybackBackend: NSObject, NativePlaybackBackend { lastLoggedState = nil lastBufferingLogTime = nil #if DEBUG - print("[DreamioVLC] cache-probe url=\(URLRedactor.redactedURLString(request.playbackURL.absoluteString))") + print("[DreamioVLC] cache fallback reason=startup-direct-preferred url=\(URLRedactor.redactedURLString(request.playbackURL.absoluteString))") #endif - playbackStartupTask = Task { [weak self] in - guard let self else { - return - } - let fetcher = HTTPRangeRemoteFetcher(url: request.playbackURL, headers: request.headers) - let probe = await fetcher.probe(timeoutInterval: 1.5) - guard !Task.isCancelled else { - return - } - - if probe.isCacheable, let contentLength = probe.contentLength, contentLength > 0 { - do { - let session = ProgressiveHTTPRangeCacheSession( - fetcher: fetcher, - contentLength: contentLength, - durationProvider: { [weak self] in self?.duration ?? 0 } - ) - let localURL = try await ProgressiveHTTPRangeCacheServer.shared.localURL(for: session) - await MainActor.run { - self.rangeCacheSession = session - session.prefetch(aroundByteOffset: 0) - self.startVLCMedia( - url: localURL, - request: request, - playbackMode: "local-cache", - cachingMilliseconds: 500, - includeRemoteHTTPOptions: false - ) - } - return - } catch { -#if DEBUG - print("[DreamioVLC] cache fallback reason=local-server-error-\(error)") -#endif - } - } else { -#if DEBUG - print("[DreamioVLC] cache fallback reason=\(probe.fallbackReason ?? "unknown")") -#endif - } - - await MainActor.run { - self.startVLCMedia( - url: request.playbackURL, - request: request, - playbackMode: "direct", - cachingMilliseconds: 2500, - includeRemoteHTTPOptions: true - ) - } - } + startVLCMedia( + url: request.playbackURL, + request: request, + playbackMode: "direct", + cachingMilliseconds: 2500, + includeRemoteHTTPOptions: true + ) #else onFailure?(NativePlaybackError.backendUnavailable) #endif diff --git a/docs/turns/2026-05-26-fix-vlc-range-cache-mkv.html b/docs/turns/2026-05-26-fix-vlc-range-cache-mkv.html index 5d48b06..5e6db74 100644 --- a/docs/turns/2026-05-26-fix-vlc-range-cache-mkv.html +++ b/docs/turns/2026-05-26-fix-vlc-range-cache-mkv.html @@ -463,6 +463,87 @@ code { font-family: "SF Mono", Menlo, Consolas, monospace; font-size: 0.92em; ba +
+

New Changes as of May 26, 2026 at 9:01 AM

+

Summary of changes

+

Changed VLC startup to open direct playback immediately instead of waiting for the range-cache probe.

+

Why this change was made

+

Device logs showed the probe request timing out and the native player still failing to start before the startup watchdog. The reliable behavior is to start VLC first, then revisit cache probing as a non-blocking optimization later.

+

Code diffs

+

Dreamio/VLCNativePlaybackBackend.swift

Dreamio/VLCNativePlaybackBackend.swift
-53+9
70 unmodified lines
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
70 unmodified lines
lastLoggedState = nil
lastBufferingLogTime = nil
#if DEBUG
print("[DreamioVLC] cache-probe url=\(URLRedactor.redactedURLString(request.playbackURL.absoluteString))")
#endif
playbackStartupTask = Task { [weak self] in
guard let self else {
return
}
let fetcher = HTTPRangeRemoteFetcher(url: request.playbackURL, headers: request.headers)
let probe = await fetcher.probe(timeoutInterval: 1.5)
guard !Task.isCancelled else {
return
}
+
if probe.isCacheable, let contentLength = probe.contentLength, contentLength > 0 {
do {
let session = ProgressiveHTTPRangeCacheSession(
fetcher: fetcher,
contentLength: contentLength,
durationProvider: { [weak self] in self?.duration ?? 0 }
)
let localURL = try await ProgressiveHTTPRangeCacheServer.shared.localURL(for: session)
await MainActor.run {
self.rangeCacheSession = session
session.prefetch(aroundByteOffset: 0)
self.startVLCMedia(
url: localURL,
request: request,
playbackMode: "local-cache",
cachingMilliseconds: 500,
includeRemoteHTTPOptions: false
)
}
return
} catch {
#if DEBUG
print("[DreamioVLC] cache fallback reason=local-server-error-\(error)")
#endif
}
} else {
#if DEBUG
print("[DreamioVLC] cache fallback reason=\(probe.fallbackReason ?? "unknown")")
#endif
}
+
await MainActor.run {
self.startVLCMedia(
url: request.playbackURL,
request: request,
playbackMode: "direct",
cachingMilliseconds: 2500,
includeRemoteHTTPOptions: true
)
}
}
#else
onFailure?(NativePlaybackError.backendUnavailable)
#endif
70 unmodified lines
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
70 unmodified lines
lastLoggedState = nil
lastBufferingLogTime = nil
#if DEBUG
print("[DreamioVLC] cache fallback reason=startup-direct-preferred url=\(URLRedactor.redactedURLString(request.playbackURL.absoluteString))")
#endif
startVLCMedia(
url: request.playbackURL,
request: request,
playbackMode: "direct",
cachingMilliseconds: 2500,
includeRemoteHTTPOptions: true
)
#else
onFailure?(NativePlaybackError.backendUnavailable)
#endif
+

Related issues or PRs

+

Beads issue: dreamio-dd7. This supersedes the blocking startup probe behavior from the earlier MKV range-cache experiment.

+

Validation

+ +
+