Skip to content

Commit

Permalink
Migrate ncspot to librespot 0.5 breaking changes
Browse files Browse the repository at this point in the history
- Set `client_id` via `SessionConfig`
- Use `TokenProvider` to obtain client token instead of custom Mercury call
- Other minor changes
  • Loading branch information
hrkfdn committed Jul 26, 2023
1 parent c491805 commit e972190
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 48 deletions.
24 changes: 14 additions & 10 deletions src/spotify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand All @@ -123,17 +125,19 @@ impl Spotify {
session_config
}

pub fn test_credentials(credentials: Credentials) -> Result<Session, SessionError> {
pub fn test_credentials(credentials: Credentials) -> Result<Session, librespot_core::Error> {
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<Session, SessionError> {
) -> Result<Session, librespot_core::Error> {
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")),
Expand All @@ -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<String>) -> Option<SinkBuilder> {
Expand Down Expand Up @@ -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(),
Expand Down
6 changes: 3 additions & 3 deletions src/spotify_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,16 +89,16 @@ 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,
});
*self
.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");
}
Expand Down
52 changes: 17 additions & 35 deletions src/spotify_worker.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -36,10 +37,10 @@ pub struct Worker {
player_events: UnboundedReceiverStream<LibrespotPlayerEvent>,
commands: UnboundedReceiverStream<WorkerCommand>,
session: Session,
player: Player,
player: Arc<Player>,
token_task: Pin<Box<dyn Future<Output = ()> + Send>>,
active: bool,
mixer: Box<dyn Mixer>,
mixer: Arc<dyn Mixer>,
}

impl Worker {
Expand All @@ -48,8 +49,8 @@ impl Worker {
player_events: mpsc::UnboundedReceiver<LibrespotPlayerEvent>,
commands: mpsc::UnboundedReceiver<WorkerCommand>,
session: Session,
player: Player,
mixer: Box<dyn Mixer>,
player: Arc<Player>,
mixer: Arc<dyn Mixer>,
) -> Worker {
Worker {
events,
Expand All @@ -72,30 +73,13 @@ impl Drop for Worker {
}

impl Worker {
fn get_token(
&self,
sender: oneshot::Sender<Option<Token>>,
) -> Pin<Box<dyn Future<Output = ()> + Send>> {
let client_id = config::CLIENT_ID;
async fn get_token(session: Session, sender: oneshot::Sender<Option<Token>>) {
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) {
Expand All @@ -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 {
Expand Down Expand Up @@ -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()) {
Expand All @@ -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;
Expand All @@ -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
Expand Down

0 comments on commit e972190

Please sign in to comment.