Fix playback always starts from the first track of a playlist when using external device#177
Conversation
There was a problem hiding this comment.
Pull request overview
This PR centralizes native StreamingPlayer ownership in App (instead of duplicating it in Network) and updates playback/event-handling paths accordingly, with additional logic to improve reliability when the native streaming session disconnects.
Changes:
- Move streaming player access throughout networking/playback code to
app.streaming_playerand introduce helper accessors. - Add native streaming recovery flow in
main.rsand support cache-only re-auth in the streaming player implementation. - Add
api_playback_offsethelper and unit tests to standardize how playback offsets are constructed for Spotify Web API calls.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| src/main.rs | Refactors event handlers to pull the streaming player from App and adds a recovery loop for disconnected native sessions. |
| src/infra/player/streaming.rs | Adds cache-only auth mode and improves native session liveness detection (spirc_alive). |
| src/infra/network/playback.rs | Switches playback control to fetch the streaming player from App, introduces api_playback_offset, and adds tests. |
| src/infra/network/mod.rs | Removes streaming_player from Network but leaves a vestigial constructor argument. |
| src/infra/network/library.rs | Fetches streaming player from App for rootlist folder fetching; updates helper signature. |
| src/core/app.rs | Minor test import ordering cleanup. |
…starts-from-the-first-track-of-a-playlist-when-using-external-device # Conflicts: # src/infra/player/streaming.rs # src/main.rs
There was a problem hiding this comment.
Pull request overview
This PR centralizes StreamingPlayer ownership in App (removing duplicated state from Network) and adjusts playback/startup event handling to use the app-held player, aiming to fix incorrect playlist start offsets when controlling playback via external devices.
Changes:
- Remove
Network.streaming_playerand route all streaming-player access throughapp.streaming_player(with helper accessors). - Refactor playback start logic to build Spotify API offsets via a new
api_playback_offsetutility and add unit tests for it. - Add native streaming recovery plumbing and restructure player-event task spawning in
main.rs.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| src/main.rs | Refactors native player event handling, adds recovery channel/task, and switches macOS media handling to pull the player from App. |
| src/infra/player/streaming.rs | Adds cache-only auth mode + spirc liveness tracking to improve detection/recovery of dead streaming sessions. |
| src/infra/network/playback.rs | Introduces api_playback_offset, switches playback calls to use it, and updates streaming-player access to come from App. |
| src/infra/network/mod.rs | Removes streaming_player from Network and updates constructor signature accordingly. |
| src/infra/network/library.rs | Updates folder-fetch logic to retrieve StreamingPlayer from App and adjusts helper signature accordingly. |
| if let Some(first_uri) = context_uris.and_then(|uris| uris.first()) { | ||
| return Some(Offset::Uri(first_uri.uri())); |
There was a problem hiding this comment.
api_playback_offset ignores the provided offset whenever context_uris is non-empty (it always picks uris.first()). If a caller ever provides a context URI list with more than one element and an offset > 0, this will still start playback from the first item instead of the requested one. Consider selecting context_uris.get(offset.unwrap_or(0)) (with bounds fallback) and only falling back to Offset::Position(...) when no URI can be derived.
| if let Some(first_uri) = context_uris.and_then(|uris| uris.first()) { | |
| return Some(Offset::Uri(first_uri.uri())); | |
| if let Some(uris) = context_uris { | |
| let index = offset.unwrap_or(0); | |
| if let Some(selected_uri) = uris.get(index).or_else(|| uris.first()) { | |
| return Some(Offset::Uri(selected_uri.uri())); | |
| } |
| fn api_playback_offset_uses_track_uri_for_context_playback() { | ||
| let uris = vec![ | ||
| playable_track("0000000000000000000001"), | ||
| playable_track("0000000000000000000002"), | ||
| ]; | ||
|
|
||
| let offset = api_playback_offset(Some(&uris), Some(1)); | ||
|
|
||
| assert_eq!( | ||
| offset, | ||
| Some(Offset::Uri( | ||
| "spotify:track:0000000000000000000001".to_string() | ||
| )) | ||
| ); |
There was a problem hiding this comment.
The test api_playback_offset_uses_track_uri_for_context_playback passes offset = Some(1) but asserts that the first URI is used. If api_playback_offset is updated to honor offsets for multi-URI context playback (recommended), this test will start failing for the right reason. Adjust/add test cases to explicitly cover offset=0 vs offset=1 selection (and any bounds/fallback behavior) instead of encoding offset-ignoring behavior.
This pull request refactors how the
StreamingPlayeris accessed throughout the codebase, consolidating its state within theAppstruct rather than duplicating it in theNetworkstruct. It updates all related logic and function signatures to fetch the streaming player from theAppstate, reducing redundancy and improving thread safety. Additionally, it introduces a utility for constructing playback offsets and adds comprehensive tests for this logic.Refactoring: StreamingPlayer State Management
streaming_playerfield is removed from theNetworkstruct, and all access to the streaming player now goes through theAppstate (app.streaming_player). Helper functions likecurrent_streaming_playerare introduced to facilitate this access throughout the codebase. [1] [2] [3] [4]Playback Logic Updates
PlaybackNetworkare updated to use the newcurrent_streaming_playerhelper, ensuring they operate on the player instance held byApp. This affects methods for play, pause, next, previous, seek, shuffle, repeat, volume, and device transfer. [1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12] [13] [14] [15]Offset Handling Improvements
api_playback_offsetutility function to determine the correct playback offset for both context and URI list playback, aligning with Spotify API requirements. The playback logic is refactored to use this utility, improving correctness and maintainability. [1] [2] [3] [4]Testing Enhancements
api_playback_offsetfunction to ensure correct behavior for various playback scenarios, increasing test coverage and reliability.General Code Cleanup
Overall, these changes centralize the streaming player state, streamline playback logic, and improve both testability and maintainability.