diff --git a/src/spotify.rs b/src/spotify.rs index 88ac916e..0c100f4b 100644 --- a/src/spotify.rs +++ b/src/spotify.rs @@ -2,7 +2,6 @@ use librespot_core::authentication::Credentials; use librespot_core::cache::Cache; use librespot_core::config::SessionConfig; use librespot_core::session::Session; -use librespot_core::session::SessionError; use librespot_playback::audio_backend::SinkBuilder; use librespot_playback::config::PlayerConfig; use librespot_playback::mixer::softmixer::SoftMixer; @@ -112,7 +111,10 @@ impl Spotify { } pub fn session_config() -> SessionConfig { - let mut session_config = SessionConfig::default(); + let mut session_config = librespot_core::SessionConfig { + client_id: config::CLIENT_ID.to_string(), + ..Default::default() + }; match env::var("http_proxy") { Ok(proxy) => { info!("Setting HTTP proxy {}", proxy); @@ -123,17 +125,19 @@ impl Spotify { session_config } - pub fn test_credentials(credentials: Credentials) -> Result { + pub fn test_credentials(credentials: Credentials) -> Result { let config = Self::session_config(); + let _guard = ASYNC_RUNTIME.enter(); + let session = Session::new(config, None); ASYNC_RUNTIME - .block_on(Session::connect(config, credentials, None, true)) - .map(|r| r.0) + .block_on(session.connect(credentials, true)) + .map(|_| session) } async fn create_session( cfg: &config::Config, credentials: Credentials, - ) -> Result { + ) -> Result { let librespot_cache_path = config::cache_path("librespot"); let audio_cache_path = match cfg.values().audio_cache.unwrap_or(true) { true => Some(librespot_cache_path.join("files")), @@ -150,9 +154,8 @@ impl Spotify { .expect("Could not create cache"); debug!("opening spotify session"); let session_config = Self::session_config(); - Session::connect(session_config, credentials, Some(cache), true) - .await - .map(|r| r.0) + let session = Session::new(session_config, Some(cache)); + session.connect(credentials, true).await.map(|_| session) } fn init_backend(desired_backend: Option) -> Option { @@ -213,12 +216,13 @@ impl Spotify { let backend = Self::init_backend(backend_name).expect("Could not find an audio playback backend"); let audio_format: librespot_playback::config::AudioFormat = Default::default(); - let (player, player_events) = Player::new( + let player = Player::new( player_config, session.clone(), mixer.get_soft_volume(), move || (backend)(cfg.values().backend_device.clone(), audio_format), ); + let player_events = player.get_player_event_channel(); let mut worker = Worker::new( events.clone(), diff --git a/src/spotify_api.rs b/src/spotify_api.rs index 1248e7ac..7e525631 100644 --- a/src/spotify_api.rs +++ b/src/spotify_api.rs @@ -89,8 +89,8 @@ impl WebApi { if let Some(token) = token_option { *self.api.token.lock().expect("can't writelock api token") = Some(Token { access_token: token.access_token, - expires_in: chrono::Duration::seconds(token.expires_in.into()), - scopes: HashSet::from_iter(token.scope), + expires_in: chrono::Duration::from_std(token.expires_in).unwrap(), + scopes: HashSet::from_iter(token.scopes), expires_at: None, refresh_token: None, }); @@ -98,7 +98,7 @@ impl WebApi { .token_expiration .write() .expect("could not writelock token") = - Utc::now() + ChronoDuration::seconds(token.expires_in.into()); + Utc::now() + ChronoDuration::from_std(token.expires_in).unwrap(); } else { error!("Failed to update token"); } diff --git a/src/spotify_worker.rs b/src/spotify_worker.rs index e24677b0..d88bc467 100644 --- a/src/spotify_worker.rs +++ b/src/spotify_worker.rs @@ -1,16 +1,17 @@ -use crate::config; use crate::events::{Event, EventManager}; use crate::model::playable::Playable; use crate::queue::QueueEvent; use crate::spotify::PlayerEvent; use futures::channel::oneshot; -use futures::{Future, FutureExt}; -use librespot_core::keymaster::Token; +use futures::Future; +use futures::FutureExt; use librespot_core::session::Session; -use librespot_core::spotify_id::{SpotifyAudioType, SpotifyId}; +use librespot_core::spotify_id::SpotifyId; +use librespot_core::token::Token; use librespot_playback::mixer::Mixer; use librespot_playback::player::{Player, PlayerEvent as LibrespotPlayerEvent}; use log::{debug, error, info, warn}; +use std::sync::Arc; use std::time::Duration; use std::{pin::Pin, time::SystemTime}; use tokio::sync::mpsc; @@ -36,10 +37,10 @@ pub struct Worker { player_events: UnboundedReceiverStream, commands: UnboundedReceiverStream, session: Session, - player: Player, + player: Arc, token_task: Pin + Send>>, active: bool, - mixer: Box, + mixer: Arc, } impl Worker { @@ -48,8 +49,8 @@ impl Worker { player_events: mpsc::UnboundedReceiver, commands: mpsc::UnboundedReceiver, session: Session, - player: Player, - mixer: Box, + player: Arc, + mixer: Arc, ) -> Worker { Worker { events, @@ -72,30 +73,13 @@ impl Drop for Worker { } impl Worker { - fn get_token( - &self, - sender: oneshot::Sender>, - ) -> Pin + Send>> { - let client_id = config::CLIENT_ID; + async fn get_token(session: Session, sender: oneshot::Sender>) { let scopes = "user-read-private,playlist-read-private,playlist-read-collaborative,playlist-modify-public,playlist-modify-private,user-follow-modify,user-follow-read,user-library-read,user-library-modify,user-top-read,user-read-recently-played"; - let url = - format!("hm://keymaster/token/authenticated?client_id={client_id}&scope={scopes}"); - Box::pin( - self.session - .mercury() - .get(url) - .map(move |response| { - response.ok().and_then(move |response| { - let payload = response.payload.first()?; - - let data = String::from_utf8(payload.clone()).ok()?; - let token: Token = serde_json::from_str(&data).ok()?; - info!("new token received: {:?}", token); - Some(token) - }) - }) - .map(|result| sender.send(result).unwrap()), - ) + session + .token_provider() + .get_token(scopes) + .map(|response| sender.send(response.ok()).expect("token channel is closed")) + .await; } pub async fn run_loop(&mut self) { @@ -114,7 +98,7 @@ impl Worker { match SpotifyId::from_uri(&playable.uri()) { Ok(id) => { info!("player loading track: {:?}", id); - if id.audio_type == SpotifyAudioType::NonPlayable { + if !id.is_playable() { warn!("track is not playable"); self.events.send(Event::Player(PlayerEvent::FinishedTrack)); } else { @@ -143,7 +127,7 @@ impl Worker { self.mixer.set_volume(volume); } Some(WorkerCommand::RequestToken(sender)) => { - self.token_task = self.get_token(sender); + self.token_task = Box::pin(Self::get_token(self.session.clone(), sender)); } Some(WorkerCommand::Preload(playable)) => { if let Ok(id) = SpotifyId::from_uri(&playable.uri()) { @@ -162,7 +146,6 @@ impl Worker { play_request_id: _, track_id: _, position_ms, - duration_ms: _, }) => { let position = Duration::from_millis(position_ms as u64); let playback_start = SystemTime::now() - position; @@ -174,7 +157,6 @@ impl Worker { play_request_id: _, track_id: _, position_ms, - duration_ms: _, }) => { let position = Duration::from_millis(position_ms as u64); self.events