fix(seek): debounce seeks that restart a decoder - #331
Conversation
Seeking a buffered network stream ran Player.Seek inline on the UI goroutine, and each seek restarts ffmpeg and waits for PCM -- so a burst of arrow presses froze the UI while they ran one after another. Route them through the accumulate-and-debounce path yt-dlp already used, so presses sum into one seek fired from a command goroutine. Affects the providers sharing that pipeline (Navidrome, Plex, Jellyfin, Emby, Qobuz, Audiobookshelf); local files and non-seekable streams are unchanged.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
Included review availability: Your plan provides up to 8 included reviews per hour; 7 remain after this review. 📝 WalkthroughWalkthroughSeeking now debounces yt-dlp decoder-restart seeks and seekable stream seeks. Shared pending targets handle relative, absolute, and timer-triggered seeks. Immediate seeking remains for non-debounced playback. ChangesSeek debouncing and dispatch
Estimated code review effort: 3 (Moderate) | ~20 minutes Merge Risk: ⚪ Minimal · up to The change debounces decoder-restarting stream seeks while preserving immediate seeking for other sources. No actionable merge-blocking risk remains after normal checks and review. Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Summary
Holding or tapping the seek keys on a buffered network stream freezes the UI. Each press runs
Player.Seekinline on the UI goroutine, and for these streams a seek kills ffmpeg and restarts it with-ss, then waits for PCM — so three quick presses serialise three restarts and the interface stops responding until they finish.seekRelativealready had the fix for yt-dlp: presses accumulate intoseek.targetPosand one seek fires ~800ms after the last, from a command goroutine. This routes decoder-restarting stream seeks through that same path rather than adding a second mechanism.needsDebouncedSeekgates it ontrack.Stream && Seekable(), which covers the providers sharing the nav/ffmpeg pipeline — Navidrome, Plex, Jellyfin, Emby, Qobuz, Audiobookshelf. Local files, radio (not seekable), Spotify (Stream: false, and its decoder repositions in place), and NetEase keep seeking inline.Worth noting this mitigates rather than removes the cost: ffmpeg reads these streams through
pipe:0, so it cannot seek and has to restart, even thoughnavBufferalready has the bytes on disk. Feeding it the temp file instead would make seeks cheap and this debounce unnecessary — a larger change, and one I would rather raise separately than bundle here.How to test
shift+arrow for the larger step, and the left arrow to confirm backward seeks sum the same way.cliamp "https://www.youtube.com/watch?v=<id>") and confirm its existing debounce still works — this PR renames the functions it uses.Checklist
make checkpassesdocs/andsite/index.htmlupdated for user-facing changesNo docs change: nothing in
docs/orsite/index.htmldescribes seek timing, so there is no stale claim to correct.I tested Audiobookshelf, yt-dlp, Spotify, radio and local files by hand. Navidrome, Plex, Jellyfin, Emby and Qobuz share the same decoder and predicate so should behave identically, but I have no server for those and have not verified them directly.
Summary by CodeRabbit