diff --git a/Cargo.lock b/Cargo.lock index c061fe4..e8c96a7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4141,6 +4141,7 @@ dependencies = [ "prost", "spoticord_api_grpc", "spoticord_database", + "spoticord_player", "spoticord_session", "thiserror", "tokio", @@ -4198,6 +4199,7 @@ dependencies = [ "hex", "librespot", "log", + "rand", "songbird", "spoticord_audio", "spoticord_utils", diff --git a/spoticord_api/Cargo.toml b/spoticord_api/Cargo.toml index 95a341e..dfaf5af 100644 --- a/spoticord_api/Cargo.toml +++ b/spoticord_api/Cargo.toml @@ -7,10 +7,11 @@ edition = "2021" spoticord_database = { path = "../spoticord_database" } spoticord_session = { path = "../spoticord_session" } spoticord_api_grpc = { path = "../spoticord_api_grpc"} +spoticord_player = { path = "../spoticord_player"} prost = "0.13.3" tokio = {version = "1.38.0", features = ["io-util", "net", "rt-multi-thread"]} axum = "0.7.5" env_logger = "0.11.3" log = "0.4.21" thiserror = "1.0.61" -tonic = "0.12.3" +tonic = "0.12.3" \ No newline at end of file diff --git a/spoticord_api/src/lib.rs b/spoticord_api/src/lib.rs index fb25578..800897b 100644 --- a/spoticord_api/src/lib.rs +++ b/spoticord_api/src/lib.rs @@ -1,5 +1,6 @@ use log::info; -use spoticord_api_grpc::spoticord_api::service::PlayPlaylistRequest; +use spoticord_api_grpc::spoticord_api::service::{PlayModes, PlayPlaylistRequest}; +use spoticord_player::playback_control::{PlayPlaylist, StartAt}; use spoticord_session::manager::{SessionManager, SessionQuery}; use tonic::{Request, Response, Status}; @@ -26,7 +27,19 @@ impl spoticord_api_grpc::spoticord_api::service::spoticord_api_server::Spoticord .get_session(SessionQuery::Owner(playlist.discord_user_id.into())) .unwrap(); - session.queue_playlist(playlist.playlist_uri).await.unwrap(); + let shuffle = match playlist.order() { + PlayModes::Shuffle => true, + PlayModes::InOrder => false, + }; + + let play_playlist = PlayPlaylist { + uri: playlist.playlist_uri, + shuffle, + repeat: true, + start_at: StartAt::FirstSong, + }; + + session.queue_playlist(play_playlist).await.unwrap(); let response = spoticord_api_grpc::spoticord_api::service::Response { resp: Option::from( diff --git a/spoticord_player/Cargo.toml b/spoticord_player/Cargo.toml index ac0b178..b15c5ee 100644 --- a/spoticord_player/Cargo.toml +++ b/spoticord_player/Cargo.toml @@ -16,3 +16,4 @@ anyhow = "1.0.86" log = "0.4.22" symphonia = { version = "0.5.4", default-features = false, features = ["pcm"] } hex = "0.4.3" +rand = "0.8.5" diff --git a/spoticord_player/src/lib.rs b/spoticord_player/src/lib.rs index f927f76..b5e567d 100644 --- a/spoticord_player/src/lib.rs +++ b/spoticord_player/src/lib.rs @@ -1,5 +1,7 @@ pub mod info; +pub mod playback_control; +use crate::playback_control::{PlayPlaylist, StartAt}; use anyhow::Result; use info::PlaybackInfo; use librespot::connect::spirc::SpircLoadCommand; @@ -18,6 +20,7 @@ use librespot::{ }, }; use log::{error, trace}; +use rand::{thread_rng, Rng}; use songbird::{input::RawAdapter, tracks::TrackHandle, Call}; use spoticord_audio::{ sink::{SinkEvent, StreamSink}, @@ -38,7 +41,7 @@ enum PlayerCommand { GetPlaybackInfo(oneshot::Sender>), GetLyrics(oneshot::Sender>), - QueuePlaylist { uri: String }, + QueuePlaylist(PlayPlaylist), Shutdown, } @@ -218,7 +221,7 @@ impl Player { PlayerCommand::GetLyrics(tx) => self.get_lyrics(tx).await, PlayerCommand::Shutdown => self.commands.close(), - PlayerCommand::QueuePlaylist { uri } => self.queue_playlist(uri).await, + PlayerCommand::QueuePlaylist(play_playlist) => self.queue_playlist(play_playlist).await, }; } @@ -307,12 +310,15 @@ impl Player { _ = tx.send(Some(lyrics)); } - async fn queue_playlist(&self, playlist_uri: String) { - let playlist = Playlist::get(&self.session, &SpotifyId::from_uri(&playlist_uri).unwrap()) - .await - .unwrap(); + async fn queue_playlist(&self, playlist_control: PlayPlaylist) { + let playlist = Playlist::get( + &self.session, + &SpotifyId::from_uri(&playlist_control.uri).unwrap(), + ) + .await + .unwrap(); - let tracks = playlist + let tracks: Vec<_> = playlist .tracks() .map(|track_id| { let mut track = TrackRef::new(); @@ -323,13 +329,18 @@ impl Player { self.spirc.activate().unwrap(); + let start_ndx = match playlist_control.start_at { + StartAt::FirstSong => 0u32, + StartAt::Random => thread_rng().gen_range(0..tracks.len()) as u32, + }; + self.spirc .load(SpircLoadCommand { - context_uri: playlist_uri, + context_uri: playlist_control.uri, start_playing: true, - shuffle: true, - repeat: true, - playing_track_index: 0, + shuffle: playlist_control.shuffle, + repeat: playlist_control.repeat, + playing_track_index: start_ndx, tracks, }) .unwrap(); @@ -389,9 +400,9 @@ impl PlayerHandle { _ = self.commands.send(PlayerCommand::Shutdown).await; } - pub async fn queue_playlist(&self, playlist_uri: String) { + pub async fn queue_playlist(&self, play_playlist: PlayPlaylist) { self.commands - .send(PlayerCommand::QueuePlaylist { uri: playlist_uri }) + .send(PlayerCommand::QueuePlaylist(play_playlist)) .await .unwrap() } diff --git a/spoticord_player/src/playback_control.rs b/spoticord_player/src/playback_control.rs new file mode 100644 index 0000000..97c43ab --- /dev/null +++ b/spoticord_player/src/playback_control.rs @@ -0,0 +1,13 @@ +#[derive(Debug, Clone)] +pub enum StartAt { + FirstSong, + Random, +} + +#[derive(Debug, Clone)] +pub struct PlayPlaylist { + pub uri: String, + pub shuffle: bool, + pub repeat: bool, + pub start_at: StartAt, +} diff --git a/spoticord_session/src/lib.rs b/spoticord_session/src/lib.rs index 010dea5..8926e68 100644 --- a/spoticord_session/src/lib.rs +++ b/spoticord_session/src/lib.rs @@ -17,6 +17,7 @@ use serenity::{ }; use songbird::{model::payload::ClientDisconnect, Call, CoreEvent, Event, EventContext}; use spoticord_database::Database; +use spoticord_player::playback_control::PlayPlaylist; use spoticord_player::{Player, PlayerEvent, PlayerHandle}; use spoticord_utils::{discord::Colors, spotify}; use std::{ops::ControlFlow, sync::Arc, time::Duration}; @@ -42,9 +43,7 @@ pub enum SessionCommand { ShutdownPlayer, Disconnect, DisconnectTimedOut, - QueuePlaylist { - playlist_uri: String, - }, + QueuePlaylist(PlayPlaylist), } pub struct Session { @@ -292,8 +291,8 @@ impl Session { return ControlFlow::Break(()); } - SessionCommand::QueuePlaylist { playlist_uri } => { - self.player.queue_playlist(playlist_uri).await; + SessionCommand::QueuePlaylist(play_playlist) => { + self.player.queue_playlist(play_playlist).await; } }; @@ -557,9 +556,9 @@ impl SessionHandle { } } - pub async fn queue_playlist(&self, playlist_uri: String) -> Result<()> { + pub async fn queue_playlist(&self, play_playlist: PlayPlaylist) -> Result<()> { self.commands - .send(SessionCommand::QueuePlaylist { playlist_uri }) + .send(SessionCommand::QueuePlaylist(play_playlist)) .await?; Ok(())