diff --git a/.beads/interactions.jsonl b/.beads/interactions.jsonl index 2fabcc5..f1377d2 100644 --- a/.beads/interactions.jsonl +++ b/.beads/interactions.jsonl @@ -45,3 +45,4 @@ {"id":"int-6ca684f7","kind":"field_change","created_at":"2026-05-26T04:00:46.072019Z","actor":"dirtydishes","issue_id":"dreamio-42s","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Fixed seek-time range-cache prefetching to prioritize the post-seek byte offset and avoid cancelling active prefetch work inside the same window; added focused coverage and validated with StreamResolverTests plus xcodebuild."}} {"id":"int-176e3ad2","kind":"field_change","created_at":"2026-05-26T04:14:19.812849Z","actor":"dirtydishes","issue_id":"dreamio-meh","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Fixed range-cache prefetch reprioritization so foreground VLC misses cancel stale speculative work and restart around VLC's actual requested byte range; added regression coverage for the observed jump mismatch."}} {"id":"int-56a87fde","kind":"field_change","created_at":"2026-05-26T04:35:35.693504Z","actor":"dirtydishes","issue_id":"dreamio-3pn","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Implemented backward-biased seek priming, global 1 MB range-cache chunk alignment, bounded protected eviction, partial foreground miss fetching/logging, main-actor VLC delegate handling, tests, and turn documentation."}} +{"id":"int-91b3db21","kind":"field_change","created_at":"2026-05-26T04:40:10.299245Z","actor":"dirtydishes","issue_id":"dreamio-mi1","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Used actual foreground VLC reads as prefetch follow signals on hits and changed foreground misses to fetch aligned chunks; added regression tests and updated the turn document."}} diff --git a/.beads/issues.jsonl b/.beads/issues.jsonl index a549252..45b452b 100644 --- a/.beads/issues.jsonl +++ b/.beads/issues.jsonl @@ -1,5 +1,6 @@ {"_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} +{"_type":"issue","id":"dreamio-mi1","title":"adapt vlc prefetch to actual post-seek reads","description":"Use real foreground VLC reads after a seek as a prefetch signal even when they are cache hits, and fetch aligned chunks for partial foreground misses so the cache warms ahead before VLC reaches the edge of retained data.","status":"closed","priority":1,"issue_type":"bug","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-26T04:38:14Z","created_by":"dirtydishes","updated_at":"2026-05-26T04:40:10Z","started_at":"2026-05-26T04:38:16Z","closed_at":"2026-05-26T04:40:10Z","close_reason":"Used actual foreground VLC reads as prefetch follow signals on hits and changed foreground misses to fetch aligned chunks; added regression tests and updated the turn document.","dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"dreamio-3pn","title":"reduce vlc seek buffering with range cache priming","description":"Improve VLC local range cache behavior after seek/jump by priming bytes behind the target, using stable global chunk boundaries, retaining useful cached ranges under a byte budget, and adding tests for the observed post-seek request pattern.","status":"closed","priority":1,"issue_type":"task","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-26T04:31:46Z","created_by":"dirtydishes","updated_at":"2026-05-26T04:35:36Z","started_at":"2026-05-26T04:31:51Z","closed_at":"2026-05-26T04:35:36Z","close_reason":"Implemented backward-biased seek priming, global 1 MB range-cache chunk alignment, bounded protected eviction, partial foreground miss fetching/logging, main-actor VLC delegate handling, tests, and turn documentation.","dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"dreamio-meh","title":"Use VLC range requests to reprioritize seek prefetch","description":"Jump logs show duration-based byteOffset estimates can be far behind VLC's actual post-seek range requests, so prefetch keeps warming stale bytes while VLC buffers on higher cache misses.","status":"closed","priority":1,"issue_type":"bug","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-26T04:11:38Z","created_by":"dirtydishes","updated_at":"2026-05-26T04:14:20Z","started_at":"2026-05-26T04:11:40Z","closed_at":"2026-05-26T04:14:20Z","close_reason":"Fixed range-cache prefetch reprioritization so foreground VLC misses cancel stale speculative work and restart around VLC's actual requested byte range; added regression coverage for the observed jump mismatch.","dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"dreamio-42s","title":"Reduce VLC range-cache buffering after seeks","description":"Logs show repeated local-cache misses and cancelled prefetch tasks after VLC jumps backward, causing buffering while the cache restarts speculative requests instead of preserving useful adjacent downloads.","status":"closed","priority":1,"issue_type":"bug","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-26T03:58:03Z","created_by":"dirtydishes","updated_at":"2026-05-26T04:00:46Z","started_at":"2026-05-26T03:58:10Z","closed_at":"2026-05-26T04:00:46Z","close_reason":"Fixed seek-time range-cache prefetching to prioritize the post-seek byte offset and avoid cancelling active prefetch work inside the same window; added focused coverage and validated with StreamResolverTests plus xcodebuild.","dependency_count":0,"dependent_count":0,"comment_count":0} diff --git a/Dreamio/ProgressiveHTTPRangeCache.swift b/Dreamio/ProgressiveHTTPRangeCache.swift index 708080f..31d711e 100644 --- a/Dreamio/ProgressiveHTTPRangeCache.swift +++ b/Dreamio/ProgressiveHTTPRangeCache.swift @@ -376,6 +376,7 @@ final class ProgressiveHTTPRangeCacheSession { #if DEBUG print("[DreamioRangeCache] cache=hit range=\(bounded.start)-\(bounded.end)") #endif + prefetchAheadIfForegroundMoved(to: bounded) return data } @@ -386,8 +387,13 @@ final class ProgressiveHTTPRangeCacheSession { #endif cancelPrefetchIfNeeded(forForegroundRange: bounded) for missingRange in missingRanges { - let data = try await fetcher.fetch(range: missingRange) - store.insert(data: data, at: missingRange.start) + for fetchRange in alignedChunks(covering: missingRange) where !store.hasData(for: fetchRange) { + let data = try await fetcher.fetch(range: fetchRange) + store.insert(data: data, at: fetchRange.start) +#if DEBUG + print("[DreamioRangeCache] foreground fetched range=\(fetchRange.start)-\(fetchRange.end) bytes=\(data.count)") +#endif + } } prefetch(aroundByteOffset: bounded.end + 1, forceRestart: true) return store.data(for: bounded) ?? Data() @@ -500,6 +506,18 @@ final class ProgressiveHTTPRangeCacheSession { cancelPrefetch() } + private func prefetchAheadIfForegroundMoved(to range: HTTPByteRange) { + guard activePrefetchWindow?.contains(range.start) == true, + let preferredOffset = activePrefetchPreferredOffset, + abs(range.start - preferredOffset) >= responseChunkSize else { + return + } +#if DEBUG + print("[DreamioRangeCache] prefetch follow-foreground from=\(preferredOffset) to=\(range.end + 1)") +#endif + prefetch(aroundByteOffset: range.end + 1, forceRestart: true) + } + private func targetWindow(aroundByteOffset offset: Int64) -> HTTPByteRange { targetWindow(aroundByteOffset: offset, minimumBehind: prefetchChunkSize) } @@ -539,6 +557,18 @@ final class ProgressiveHTTPRangeCacheSession { return chunks } + func alignedChunks(covering range: HTTPByteRange) -> [HTTPByteRange] { + let bounded = clamp(range) + var chunks: [HTTPByteRange] = [] + var cursor = alignedChunkStart(for: bounded.start) + while cursor <= bounded.end { + let chunk = HTTPByteRange(start: cursor, end: min(contentLength - 1, cursor + prefetchChunkSize - 1)) + chunks.append(chunk) + cursor = chunk.end + 1 + } + return chunks + } + private func evictOverBudget(protecting range: HTTPByteRange) { let headerRange = HTTPByteRange(start: 0, end: min(contentLength - 1, prefetchChunkSize - 1)) let tailStart = max(0, contentLength - (4 * prefetchChunkSize)) diff --git a/Tests/StreamResolverTests.swift b/Tests/StreamResolverTests.swift index 679675a..368e877 100644 --- a/Tests/StreamResolverTests.swift +++ b/Tests/StreamResolverTests.swift @@ -40,7 +40,9 @@ struct StreamResolverTests { testRangeCachePrefetchPrioritizesSeekOffset() testRangeCacheSeekPrimingIncludesObservedVLCStart() testRangeCachePrefetchUsesGlobalChunkBoundaries() + testRangeCacheForegroundMissFetchesAlignedChunks() await testRangeCacheForegroundMissReprioritizesPrefetch() + await testRangeCacheHitFollowsActualPostSeekReadArea() await testRangeProbeFallsBackWhenServerIgnoresRange() await testRangeFetcherPreservesHeaders() print("StreamResolverTests passed") @@ -437,6 +439,18 @@ struct StreamResolverTests { assertEqual(chunks[0], HTTPByteRange(start: 212_860_928, end: 213_909_503)) } + private static func testRangeCacheForegroundMissFetchesAlignedChunks() { + let session = ProgressiveHTTPRangeCacheSession( + fetcher: HTTPRangeRemoteFetcher(url: URL(string: "https://example.test/video.mkv")!, headers: [:]), + contentLength: 711_080_522, + durationProvider: { 0 } + ) + + let chunks = session.alignedChunks(covering: HTTPByteRange(start: 48_234_649, end: 49_185_907)) + + assertEqual(chunks, [HTTPByteRange(start: 48_234_496, end: 49_283_071)]) + } + private static func testRangeCacheForegroundMissReprioritizesPrefetch() async { let queue = DispatchQueue(label: "dreamio.range-cache-test") var requestedRanges: [String] = [] @@ -473,7 +487,7 @@ struct StreamResolverTests { try? await Task.sleep(nanoseconds: 50_000_000) let ranges = queue.sync { requestedRanges } - assert(ranges.contains("bytes=51818977-52867552"), "Expected foreground VLC range to be fetched") + assert(ranges.contains("bytes=51380224-52428799"), "Expected foreground VLC miss to fetch aligned cache chunks") assert(ranges.contains { range in range.hasPrefix("bytes=52428800-") }, "Expected prefetch to restart on a global chunk boundary near VLC's foreground range, got \(ranges)") @@ -482,6 +496,50 @@ struct StreamResolverTests { try? await Task.sleep(nanoseconds: 50_000_000) } + private static func testRangeCacheHitFollowsActualPostSeekReadArea() async { + let queue = DispatchQueue(label: "dreamio.range-cache-hit-follow-test") + var requestedRanges: [String] = [] + MockURLProtocol.handler = { request in + let range = request.value(forHTTPHeaderField: "Range") ?? "" + queue.sync { + requestedRanges.append(range) + } + let byteRange = byteRange(fromHeader: range, contentLength: 80_000_000) + let response = HTTPURLResponse( + url: request.url!, + statusCode: 206, + httpVersion: nil, + headerFields: ["Content-Range": "bytes \(byteRange.start)-\(byteRange.end)/80000000"] + )! + return (Data(repeating: 1, count: Int(byteRange.length)), response) + } + + let session = ProgressiveHTTPRangeCacheSession( + fetcher: HTTPRangeRemoteFetcher( + url: URL(string: "https://cdn.example.test/movie.mp4")!, + headers: [:], + session: mockSession() + ), + contentLength: 80_000_000, + durationProvider: { 100 } + ) + defer { + session.cancelPrefetch() + } + + session.store.insert(data: Data(repeating: 7, count: 1_048_576), at: 27_165_812) + session.prefetchForSeek(aroundByteOffset: 15_936_567) + _ = try? await session.data(for: HTTPByteRange(start: 27_165_812, end: 28_214_387)) + try? await Task.sleep(nanoseconds: 100_000_000) + + let ranges = queue.sync { requestedRanges } + assert(ranges.contains { range in + range.hasPrefix("bytes=27262976-") + }, "Expected a cache hit far from the seek estimate to restart prefetch near VLC's real read area, got \(ranges)") + MockURLProtocol.handler = nil + try? await Task.sleep(nanoseconds: 50_000_000) + } + private static func byteRange(fromHeader header: String, contentLength: Int64) -> HTTPByteRange { let value = header.replacingOccurrences(of: "bytes=", with: "") let pieces = value.split(separator: "-", maxSplits: 1).map(String.init) diff --git a/docs/turns/2026-05-26-reduce-vlc-seek-buffering.html b/docs/turns/2026-05-26-reduce-vlc-seek-buffering.html index 5d03339..8c8b0e5 100644 --- a/docs/turns/2026-05-26-reduce-vlc-seek-buffering.html +++ b/docs/turns/2026-05-26-reduce-vlc-seek-buffering.html @@ -293,6 +293,152 @@ code { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; fo

Validation

Issues, Limitations, and Mitigations

Follow-up Work

+

New Changes as of 2026-05-26 00:40 EDT

Summary of changes

Adjusted the cache to follow VLC's actual foreground read area even when those reads are hits, and changed foreground partial misses to fetch stable aligned chunks synchronously.

Why this change was made

Device logs showed a backward jump estimating byte 15936567, while VLC continued reading from about 27165812 through the warmed cache. Because those reads were hits, prefetch stayed near the estimate until VLC reached the cache edge and began partial misses.

Code diffs

Dreamio/ProgressiveHTTPRangeCache.swift

Dreamio/ProgressiveHTTPRangeCache.swift
-2+32
375 unmodified lines
376
377
378
379
380
381
4 unmodified lines
386
387
388
389
390
391
392
393
106 unmodified lines
500
501
502
503
504
505
33 unmodified lines
539
540
541
542
543
544
375 unmodified lines
#if DEBUG
print("[DreamioRangeCache] cache=hit range=\(bounded.start)-\(bounded.end)")
#endif
return data
}
+
4 unmodified lines
#endif
cancelPrefetchIfNeeded(forForegroundRange: bounded)
for missingRange in missingRanges {
let data = try await fetcher.fetch(range: missingRange)
store.insert(data: data, at: missingRange.start)
}
prefetch(aroundByteOffset: bounded.end + 1, forceRestart: true)
return store.data(for: bounded) ?? Data()
106 unmodified lines
cancelPrefetch()
}
+
private func targetWindow(aroundByteOffset offset: Int64) -> HTTPByteRange {
targetWindow(aroundByteOffset: offset, minimumBehind: prefetchChunkSize)
}
33 unmodified lines
return chunks
}
+
private func evictOverBudget(protecting range: HTTPByteRange) {
let headerRange = HTTPByteRange(start: 0, end: min(contentLength - 1, prefetchChunkSize - 1))
let tailStart = max(0, contentLength - (4 * prefetchChunkSize))
375 unmodified lines
376
377
378
379
380
381
382
4 unmodified lines
387
388
389
390
391
392
393
394
395
396
397
398
399
106 unmodified lines
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
33 unmodified lines
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
375 unmodified lines
#if DEBUG
print("[DreamioRangeCache] cache=hit range=\(bounded.start)-\(bounded.end)")
#endif
prefetchAheadIfForegroundMoved(to: bounded)
return data
}
+
4 unmodified lines
#endif
cancelPrefetchIfNeeded(forForegroundRange: bounded)
for missingRange in missingRanges {
for fetchRange in alignedChunks(covering: missingRange) where !store.hasData(for: fetchRange) {
let data = try await fetcher.fetch(range: fetchRange)
store.insert(data: data, at: fetchRange.start)
#if DEBUG
print("[DreamioRangeCache] foreground fetched range=\(fetchRange.start)-\(fetchRange.end) bytes=\(data.count)")
#endif
}
}
prefetch(aroundByteOffset: bounded.end + 1, forceRestart: true)
return store.data(for: bounded) ?? Data()
106 unmodified lines
cancelPrefetch()
}
+
private func prefetchAheadIfForegroundMoved(to range: HTTPByteRange) {
guard activePrefetchWindow?.contains(range.start) == true,
let preferredOffset = activePrefetchPreferredOffset,
abs(range.start - preferredOffset) >= responseChunkSize else {
return
}
#if DEBUG
print("[DreamioRangeCache] prefetch follow-foreground from=\(preferredOffset) to=\(range.end + 1)")
#endif
prefetch(aroundByteOffset: range.end + 1, forceRestart: true)
}
+
private func targetWindow(aroundByteOffset offset: Int64) -> HTTPByteRange {
targetWindow(aroundByteOffset: offset, minimumBehind: prefetchChunkSize)
}
33 unmodified lines
return chunks
}
+
func alignedChunks(covering range: HTTPByteRange) -> [HTTPByteRange] {
let bounded = clamp(range)
var chunks: [HTTPByteRange] = []
var cursor = alignedChunkStart(for: bounded.start)
while cursor <= bounded.end {
let chunk = HTTPByteRange(start: cursor, end: min(contentLength - 1, cursor + prefetchChunkSize - 1))
chunks.append(chunk)
cursor = chunk.end + 1
}
return chunks
}
+
private func evictOverBudget(protecting range: HTTPByteRange) {
let headerRange = HTTPByteRange(start: 0, end: min(contentLength - 1, prefetchChunkSize - 1))
let tailStart = max(0, contentLength - (4 * prefetchChunkSize))

Tests/StreamResolverTests.swift

Tests/StreamResolverTests.swift
-1+59
39 unmodified lines
40
41
42
43
44
45
46
390 unmodified lines
437
438
439
440
441
442
30 unmodified lines
473
474
475
476
477
478
479
2 unmodified lines
482
483
484
485
486
487
39 unmodified lines
testRangeCachePrefetchPrioritizesSeekOffset()
testRangeCacheSeekPrimingIncludesObservedVLCStart()
testRangeCachePrefetchUsesGlobalChunkBoundaries()
await testRangeCacheForegroundMissReprioritizesPrefetch()
await testRangeProbeFallsBackWhenServerIgnoresRange()
await testRangeFetcherPreservesHeaders()
print("StreamResolverTests passed")
390 unmodified lines
assertEqual(chunks[0], HTTPByteRange(start: 212_860_928, end: 213_909_503))
}
+
private static func testRangeCacheForegroundMissReprioritizesPrefetch() async {
let queue = DispatchQueue(label: "dreamio.range-cache-test")
var requestedRanges: [String] = []
30 unmodified lines
try? await Task.sleep(nanoseconds: 50_000_000)
+
let ranges = queue.sync { requestedRanges }
assert(ranges.contains("bytes=51818977-52867552"), "Expected foreground VLC range to be fetched")
assert(ranges.contains { range in
range.hasPrefix("bytes=52428800-")
}, "Expected prefetch to restart on a global chunk boundary near VLC's foreground range, got \(ranges)")
2 unmodified lines
try? await Task.sleep(nanoseconds: 50_000_000)
}
+
private static func byteRange(fromHeader header: String, contentLength: Int64) -> HTTPByteRange {
let value = header.replacingOccurrences(of: "bytes=", with: "")
let pieces = value.split(separator: "-", maxSplits: 1).map(String.init)
39 unmodified lines
40
41
42
43
44
45
46
47
48
390 unmodified lines
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
30 unmodified lines
487
488
489
490
491
492
493
2 unmodified lines
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
39 unmodified lines
testRangeCachePrefetchPrioritizesSeekOffset()
testRangeCacheSeekPrimingIncludesObservedVLCStart()
testRangeCachePrefetchUsesGlobalChunkBoundaries()
testRangeCacheForegroundMissFetchesAlignedChunks()
await testRangeCacheForegroundMissReprioritizesPrefetch()
await testRangeCacheHitFollowsActualPostSeekReadArea()
await testRangeProbeFallsBackWhenServerIgnoresRange()
await testRangeFetcherPreservesHeaders()
print("StreamResolverTests passed")
390 unmodified lines
assertEqual(chunks[0], HTTPByteRange(start: 212_860_928, end: 213_909_503))
}
+
private static func testRangeCacheForegroundMissFetchesAlignedChunks() {
let session = ProgressiveHTTPRangeCacheSession(
fetcher: HTTPRangeRemoteFetcher(url: URL(string: "https://example.test/video.mkv")!, headers: [:]),
contentLength: 711_080_522,
durationProvider: { 0 }
)
+
let chunks = session.alignedChunks(covering: HTTPByteRange(start: 48_234_649, end: 49_185_907))
+
assertEqual(chunks, [HTTPByteRange(start: 48_234_496, end: 49_283_071)])
}
+
private static func testRangeCacheForegroundMissReprioritizesPrefetch() async {
let queue = DispatchQueue(label: "dreamio.range-cache-test")
var requestedRanges: [String] = []
30 unmodified lines
try? await Task.sleep(nanoseconds: 50_000_000)
+
let ranges = queue.sync { requestedRanges }
assert(ranges.contains("bytes=51380224-52428799"), "Expected foreground VLC miss to fetch aligned cache chunks")
assert(ranges.contains { range in
range.hasPrefix("bytes=52428800-")
}, "Expected prefetch to restart on a global chunk boundary near VLC's foreground range, got \(ranges)")
2 unmodified lines
try? await Task.sleep(nanoseconds: 50_000_000)
}
+
private static func testRangeCacheHitFollowsActualPostSeekReadArea() async {
let queue = DispatchQueue(label: "dreamio.range-cache-hit-follow-test")
var requestedRanges: [String] = []
MockURLProtocol.handler = { request in
let range = request.value(forHTTPHeaderField: "Range") ?? ""
queue.sync {
requestedRanges.append(range)
}
let byteRange = byteRange(fromHeader: range, contentLength: 80_000_000)
let response = HTTPURLResponse(
url: request.url!,
statusCode: 206,
httpVersion: nil,
headerFields: ["Content-Range": "bytes \(byteRange.start)-\(byteRange.end)/80000000"]
)!
return (Data(repeating: 1, count: Int(byteRange.length)), response)
}
+
let session = ProgressiveHTTPRangeCacheSession(
fetcher: HTTPRangeRemoteFetcher(
url: URL(string: "https://cdn.example.test/movie.mp4")!,
headers: [:],
session: mockSession()
),
contentLength: 80_000_000,
durationProvider: { 100 }
)
defer {
session.cancelPrefetch()
}
+
session.store.insert(data: Data(repeating: 7, count: 1_048_576), at: 27_165_812)
session.prefetchForSeek(aroundByteOffset: 15_936_567)
_ = try? await session.data(for: HTTPByteRange(start: 27_165_812, end: 28_214_387))
try? await Task.sleep(nanoseconds: 100_000_000)
+
let ranges = queue.sync { requestedRanges }
assert(ranges.contains { range in
range.hasPrefix("bytes=27262976-")
}, "Expected a cache hit far from the seek estimate to restart prefetch near VLC's real read area, got \(ranges)")
MockURLProtocol.handler = nil
try? await Task.sleep(nanoseconds: 50_000_000)
}
+
private static func byteRange(fromHeader header: String, contentLength: Int64) -> HTTPByteRange {
let value = header.replacingOccurrences(of: "bytes=", with: "")
let pieces = value.split(separator: "-", maxSplits: 1).map(String.init)

Related issues or PRs

Beads issue dreamio-mi1.

\ No newline at end of file