Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@

### Fixed

- **A Listening Party can no longer start Spotify on top of another source**: with Local Files, Subsonic, Internet Radio, YouTube or Qobuz playing, native Spotify streaming is only paused, and the party relay (a guest following the host's state, a host running a guest's command) still picked the Spotify player from its device identity. It transferred and activated the Connect device and loaded the host's track into it, or paused, skipped and seeked the paused player, so Spotify audio started on top of the track you were hearing. Every Spotify playback call now checks who owns the output first and does nothing while another source plays (switching Spotify to the spotatui device says so; switching it to another device still works), and the party ignores messages until Spotify plays again. The host publishes only a Spotify track it plays itself: while it plays another source or the cross-source queue it sends nothing instead of the suspended Spotify track, and a guest ignores a state it cannot play instead of pausing or resuming its own music to match. Party commands now run through the same path as a keypress.

- **The jump and add-to-playlist keys follow the track that plays**: with a Spotify track playing from the cross-source queue, jump to album (`a`), jump to artist (`A`) and add the playing track to a playlist (`w`/`W`) acted on the track of the *suspended* Spotify context, so they opened the album, the artist and the picker for a song you were not hearing; they now use the queued track itself, and jump to context (`o`) says the queue slot has no play context. Under Local Files, Subsonic, Internet Radio, YouTube or Qobuz those five keys (`a`, `A`, `o`, `w`, `W`) say they need a Spotify track playing instead of acting on the suspended one, and with nothing playing the three jump keys say so instead of doing nothing. Like (`F`) already followed the queued track and keeps doing so; it now shares the same resolution.

- **Spotify's rate limit no longer crashes spotatui at startup**: with a cached login, startup verifies the token with one `/me` request, and that request went straight through rspotify with none of the pacing and `Retry-After` retries every other Spotify call gets. A 429 there ended the process with `Error: http error: status code 429 Too Many Requests` before the UI existed, and relaunching only added more unpaced hits on a rate limit that is shared by everyone using the same client ID. The check now goes through the same paced, retrying request path as the rest of the app, and when Spotify still answers with anything other than a rejected token (a rate limit that outlasts the retries, an outage, no network) spotatui keeps the cached token and starts instead of quitting; the native-streaming account check asks again with its own retries ([#504](https://github.com/LargeModGames/spotatui/issues/504)).
Expand Down
22 changes: 22 additions & 0 deletions src/core/test_helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

use crate::core::app::UserInfo;
use crate::core::plugin_api::PlaylistInfo;
#[cfg(feature = "streaming")]
use crate::core::plugin_api::TrackInfo;
use chrono::Duration;
use rspotify::model::{
idtypes::{PlaylistId, UserId},
Expand All @@ -12,6 +14,26 @@ use rspotify::model::{
};
use std::collections::HashMap;

/// A queue-slot [`TrackInfo`] with only its `uri` and `name` set.
#[cfg(feature = "streaming")]
pub fn queued_track(uri: &str, name: &str) -> TrackInfo {
TrackInfo {
uri: Some(uri.to_string()),
name: name.to_string(),
artists: vec!["Artist".to_string()],
album: "Album".to_string(),
duration_ms: 180_000,
id: None,
album_id: None,
artist_refs: Vec::new(),
is_playable: true,
is_local: false,
track_number: 1,
explicit: false,
image_url: None,
}
}

/// Domain [`UserInfo`] for tests. `display_name` mirrors `private_user`.
pub fn user_info(id: &str) -> UserInfo {
UserInfo {
Expand Down
Loading
Loading