Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 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
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- [connect] Add method `add_to_queue` to `Spirc` to add tracks, episodes, albums, playlists, artists, and shows to the queue
- [playback] Add `AddedToQueue` player event, emitting when a track was added to the queue with `Spirc::add_to_queue`

### Changed

- [core] Made `SpotifyId::to_base62`, `SpotifyId::to_base16`, `FileId::to_base16`, `SpotifyUri::to_id`, `SpotifyUri::to_uri` infallible (breaking)
Expand Down
66 changes: 65 additions & 1 deletion connect/src/spirc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use crate::{
connect::{Cluster, ClusterUpdate, LogoutCommand, SetVolumeCommand},
context::Context,
explicit_content_pubsub::UserAttributesUpdate,
player::ProvidedTrack,
playlist4_external::PlaylistModificationInfo,
social_connect_v2::SessionUpdate,
transfer_state::TransferState,
Expand Down Expand Up @@ -132,6 +133,7 @@ enum SpircCommand {
Activate,
Transfer(Option<TransferRequest>),
Load(LoadRequest),
AddToQueue(SpotifyUri),
}

const CONTEXT_FETCH_THRESHOLD: usize = 2;
Expand Down Expand Up @@ -388,6 +390,25 @@ impl Spirc {
Ok(self.commands.send(SpircCommand::Load(command))?)
}

/// Adds a track, episode, album, playlist, artist, or show to the queue.
///
/// Does nothing if we are not the active device.
///
/// For albums, playlists, artists, and shows, all tracks/episodes are resolved
/// and added to the queue.
pub fn add_to_queue(&self, uri: SpotifyUri) -> Result<(), Error> {
if !matches!(
uri,
SpotifyUri::Track { .. }
| SpotifyUri::Episode { .. }
| SpotifyUri::Album { .. }
| SpotifyUri::Playlist { .. }
) {
return Err(Error::invalid_argument("uri"));
}
Ok(self.commands.send(SpircCommand::AddToQueue(uri))?)
}

/// Disconnects the current device and pauses the playback according the value.
///
/// Does nothing if we are not the active device.
Expand Down Expand Up @@ -679,6 +700,7 @@ impl SpircTask {
SpircCommand::SetPosition(position) => self.handle_seek(position),
SpircCommand::SetVolume(volume) => self.set_volume(volume),
SpircCommand::Load(command) => self.handle_load(command, None, None).await?,
SpircCommand::AddToQueue(uri) => self.handle_add_to_queue(uri).await,
};

self.notify().await
Expand Down Expand Up @@ -1062,7 +1084,13 @@ impl SpircTask {
self.handle_repeat_context(repeat_context.value)?
}
SetRepeatingTrack(repeat_track) => self.handle_repeat_track(repeat_track.value),
AddToQueue(add_to_queue) => self.connect_state.add_to_queue(add_to_queue.track, true),
AddToQueue(add_to_queue) => {
let track = add_to_queue.track.clone();
self.connect_state.add_to_queue(add_to_queue.track, true);
if let Ok(uri) = SpotifyUri::from_uri(&track.uri) {
self.player.emit_added_to_queue_event(uri);
}
}
SetQueue(set_queue) => self.connect_state.handle_set_queue(set_queue),
SetOptions(set_options) => {
if let Some(repeat_context) = set_options.repeating_context {
Expand Down Expand Up @@ -1545,6 +1573,42 @@ impl SpircTask {
self.connect_state.set_repeat_track(repeat);
}

async fn handle_add_to_queue(&mut self, uri: SpotifyUri) {
let track_uris: Vec<String> = match uri {
SpotifyUri::Track { .. } | SpotifyUri::Episode { .. } => vec![uri.to_uri()],
SpotifyUri::Album { .. }
| SpotifyUri::Playlist { .. }
| SpotifyUri::Artist { .. }
| SpotifyUri::Show { .. } => {
Comment thread
photovoltex marked this conversation as resolved.
Outdated
match self.session.spclient().get_context(&uri.to_uri()).await {
Ok(context) => context
.pages
.iter()
.flat_map(|page| page.tracks.iter())
.filter_map(|track| track.uri.clone())
.collect(),
Err(e) => {
error!("failed to resolve context for {}: {e}", uri.item_type());
return;
}
}
}
_ => return,
};

for track_uri in track_uris {
let track = ProvidedTrack {
uri: track_uri.clone(),
..Default::default()
};
self.connect_state.add_to_queue(track, true);

if let Ok(uri) = SpotifyUri::from_uri(&track_uri) {
self.player.emit_added_to_queue_event(uri);
}
}
}

fn handle_preload_next_track(&mut self) {
// Requests the player thread to preload the next track
match self.play_status {
Expand Down
16 changes: 16 additions & 0 deletions playback/src/player.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ enum PlayerCommand {
track: bool,
},
EmitAutoPlayChangedEvent(bool),
EmitAddedToQueueEvent(SpotifyUri),
}

#[derive(Debug, Clone)]
Expand All @@ -146,6 +147,9 @@ pub enum PlayerEvent {
PlayRequestIdChanged {
play_request_id: u64,
},
AddedToQueue {
track_id: SpotifyUri,
},
// Fired when the player is stopped (e.g. by issuing a "stop" command to the player).
Stopped {
play_request_id: u64,
Expand Down Expand Up @@ -647,6 +651,10 @@ impl Player {
pub fn emit_auto_play_changed_event(&self, auto_play: bool) {
self.command(PlayerCommand::EmitAutoPlayChangedEvent(auto_play));
}

pub fn emit_added_to_queue_event(&self, track_id: SpotifyUri) {
self.command(PlayerCommand::EmitAddedToQueueEvent(track_id));
}
}

impl Drop for Player {
Expand Down Expand Up @@ -2335,6 +2343,10 @@ impl PlayerInternal {
self.auto_normalise_as_album = setting
}

PlayerCommand::EmitAddedToQueueEvent(track_id) => {
self.send_event(PlayerEvent::AddedToQueue { track_id })
}

PlayerCommand::EmitFilterExplicitContentChangedEvent(filter) => {
self.send_event(PlayerEvent::FilterExplicitContentChanged { filter });

Expand Down Expand Up @@ -2536,6 +2548,10 @@ impl fmt::Debug for PlayerCommand {
.debug_tuple("EmitAutoPlayChangedEvent")
.field(&auto_play)
.finish(),
PlayerCommand::EmitAddedToQueueEvent(track_id) => f
.debug_tuple("EmitAddedToQueueEvent")
.field(&track_id)
.finish(),
}
}
}
Expand Down
4 changes: 4 additions & 0 deletions src/player_event_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ impl EventHandler {
.insert("PLAYER_EVENT", "play_request_id_changed".to_string());
env_vars.insert("PLAY_REQUEST_ID", play_request_id.to_string());
}
PlayerEvent::AddedToQueue { track_id } => {
env_vars.insert("PLAYER_EVENT", "added_to_queue".to_string());
env_vars.insert("TRACK_ID", track_id.to_id());
}
PlayerEvent::TrackChanged { audio_item } => {
let id = audio_item.track_id.to_id();
env_vars.insert("PLAYER_EVENT", "track_changed".to_string());
Expand Down