Repository navigation
fix(playback): identity-keyed stream resolve, probed URLs, and deferred metadata - #285
Merged
ajisth69 merged 2 commits intoOct 9, 2026
Merged
Conversation
…mmediately YouTube startup was re-extracting the same video, installing URLs that 403, and racing metadata against the first buffer. An identity-keyed cache, single-flight extraction, and a probed URL keep the tapped track playing while lyrics and artwork wait.
ajisth69
added a commit
that referenced
this pull request
Oct 9, 2026
Resolve conflicts in InnerTubeMusicApi.kt and MusicPlayer.kt. Main's playback resolver (#285) landed after this branch forked and deliberately supersedes its parallel fast-path race (single POST per client, sequential, with a whole-path budget and maxAttempts=1), the lossless head-start race in resolveRemoteTrackAudioStream (lossless now promotes via background upgrade), and focus-aware buffer durations. Keep main's implementation for those paths; retain this branch's non-conflicting changes: findBestMatch hybrid gate and stricter stream-upgrade/audition duration guards.
9 of 14 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Playback startup now treats
youtubeVideoIdas the only stream identity. A cached URL starts the player immediately, one extraction serves every waiter for that video, and a URL is installed only after a byte probe accepts it. Lyrics, artwork, Apple Music, Tidal, and recommendations wait until audio is already playing.Device log
lastwave-diagnostics-1791481694558.txt(build before the probe, lossless-on-cache-hit, and metadata-delay changes) already showed cache-hit startup at 176–179 ms and a joined in-flight extract at 817 ms. The 403 replay, lossless upgrade, and metadata-storm fixes are in this branch and covered by unit tests. They still need a fresh device log.Root cause
youtubeVideoIdcould start another extract or overwrite the URL the player was using.kd5KqlmcHNo) was extracted three times and took 11.8 s.prepareand never calledscheduleQualityUpgrade, so a cached song stayed on the YouTube codec.Changes
TrackIdentityistrackId+youtubeVideoId. Cache lookup and publish match onyoutubeVideoIdonly. A result is published only while that identity is still the requested one (StaleResolveExceptionotherwise).StreamExtractionkeeps oneDeferredperyoutubeVideoId. Later callers logEXTRACT_JOIN. Cancelling prefetch does not cancel the extract;putIfAbsentkeeps the URL.cacheDir/playback_stream_cache/streams.json(128 entries). Entries expire 2 minutes beforeexpiresAt. Prefetch cannot replace a fresh playback URL.PREFETCH_SKIPPED_ALREADY_ACTIVEandPREFETCH_SKIPPED_CACHEDreturn without cancelling. The next URL is byte-cached beforereplaceMediaItem. A 403 or 410 invalidates the cache and is not installed.Range: bytes=0-1probe is not returned.MetadataLog.SETTLE_MS). A skip cancels them (METADATA_SKIPPED). End-of-queue radio/discover still fetches immediately so the queue does not stop.TrafficPriorityInterceptorlogs each OkHttp call asLastWaveTrafficwith source, video id, priority, and elapsed time.LastWaveStartuprecordsTAP_TRACK, cache, video-id lookup, extract, prepare, and a[STARTUP]line withtotalMs.Acceptance criteria
youtubeVideoIdproduce one extraction (EXTRACT_JOINon the second).PREFETCH_SKIPPED_ALREADY_ACTIVEorPREFETCH_SKIPPED_CACHEDand does not start another extract.[STARTUP] totalMsstays under 200 ms.[STREAM UPGRADE] starting(or an explicitskipped/No upgrade stream foundwhen the setting or addon blocks it).METADATA_DELAYEDand start only after ~3 seconds of playback. A skip before that logsMETADATA_SKIPPED../gradlew :app:testDebugUnitTest --tests com.lastwave.app.playback.resolve.StreamResolveTestpasses.Performance validation
Measured on device in
log/lastwave-diagnostics-1791481694558.txt(Nothing A001, app 4.2.4, before probe / lossless-on-cache-hit / metadata delay):zAEA3h529ZUcacheHit=truejCEdTq3j-0UbufferedPos=57261AlvUuGJccKsextractMs=3112gWbNcgZFM0extractMs=2614,stage=newpipekPmAJPUVY8IextractMs=2759kd5KqlmcHNoUnit tests (
StreamResolveTest, 15 cases) passed on this branch: cache key, expiry, disk reload, stale token, mismatch retry, cache hit skips network, cancel drops an in-flight publish, prefetch cannot claim playback identity, one extraction per video, cancelled prefetch still caches, second prefetch does not extract again../gradlew :app:assembleDebugsucceeded. APK:app/build/outputs/apk/debug/app-debug.apk.Out of scope / remaining limits
visitor_idandyoutubei/playerduring a cold extract are the stream resolve itself. They are not deferred.preferLosslessStreaming, quality other than YouTube (-1), and a configured addon. It swaps after YouTube audio has started.Test plan
[STARTUP] cacheHit=trueandtotalMsunder 200.EXTRACT_JOINorPREFETCH_SKIPPED_CACHED, not a second extract.METADATA_SKIPPEDand no lyrics / Tidal / Apple Music burst for the skipped songs.[STREAM UPGRADE] startingand, when the addon is enabled, a later lossless swap at the same position.next-cachedoes not install it and the next resolve probes another itag../gradlew :app:testDebugUnitTest --tests com.lastwave.app.playback.resolve.StreamResolveTest