Skip to content

Fix playback always starts from the first track of a playlist when using external device#177

Merged
LargeModGames merged 5 commits intomainfrom
fix--Playback-always-starts-from-the-first-track-of-a-playlist-when-using-external-device
Mar 20, 2026
Merged

Fix playback always starts from the first track of a playlist when using external device#177
LargeModGames merged 5 commits intomainfrom
fix--Playback-always-starts-from-the-first-track-of-a-playlist-when-using-external-device

Conversation

@LargeModGames
Copy link
Owner

This pull request refactors how the StreamingPlayer is accessed throughout the codebase, consolidating its state within the App struct rather than duplicating it in the Network struct. It updates all related logic and function signatures to fetch the streaming player from the App state, 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

  • The streaming_player field is removed from the Network struct, and all access to the streaming player now goes through the App state (app.streaming_player). Helper functions like current_streaming_player are introduced to facilitate this access throughout the codebase. [1] [2] [3] [4]

Playback Logic Updates

  • All playback-related methods in PlaybackNetwork are updated to use the new current_streaming_player helper, ensuring they operate on the player instance held by App. 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

  • Introduces the api_playback_offset utility 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

  • Adds new unit tests for the api_playback_offset function to ensure correct behavior for various playback scenarios, increasing test coverage and reliability.

General Code Cleanup

  • Removes unused imports, updates test module import order, and makes minor improvements to code clarity and documentation. [1] [2] [3]

Overall, these changes centralize the streaming player state, streamline playback logic, and improve both testability and maintainability.

Copilot AI review requested due to automatic review settings March 19, 2026 14:19
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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_player and introduce helper accessors.
  • Add native streaming recovery flow in main.rs and support cache-only re-auth in the streaming player implementation.
  • Add api_playback_offset helper 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
Copilot AI review requested due to automatic review settings March 20, 2026 11:48
@LargeModGames LargeModGames merged commit 0789e6b into main Mar 20, 2026
9 of 10 checks passed
@LargeModGames LargeModGames deleted the fix--Playback-always-starts-from-the-first-track-of-a-playlist-when-using-external-device branch March 20, 2026 11:49
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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_player and route all streaming-player access through app.streaming_player (with helper accessors).
  • Refactor playback start logic to build Spotify API offsets via a new api_playback_offset utility 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.

Comment on lines +78 to +79
if let Some(first_uri) = context_uris.and_then(|uris| uris.first()) {
return Some(Offset::Uri(first_uri.uri()));
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
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()));
}

Copilot uses AI. Check for mistakes.
Comment on lines +971 to +984
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()
))
);
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants