|
@@ -9,7 +9,11 @@ use rand;
|
|
use rand::seq::SliceRandom;
|
|
use rand::seq::SliceRandom;
|
|
use serde_json;
|
|
use serde_json;
|
|
|
|
|
|
-use context::StationContext;
|
|
|
|
|
|
+use crate::context::StationContext;
|
|
|
|
+use crate::playback::mixer::Mixer;
|
|
|
|
+use crate::playback::player::Player;
|
|
|
|
+use crate::protocol;
|
|
|
|
+use crate::protocol::spirc::{DeviceState, Frame, MessageType, PlayStatus, State, TrackRef};
|
|
use librespot_core::config::ConnectConfig;
|
|
use librespot_core::config::ConnectConfig;
|
|
use librespot_core::mercury::MercuryError;
|
|
use librespot_core::mercury::MercuryError;
|
|
use librespot_core::session::Session;
|
|
use librespot_core::session::Session;
|
|
@@ -17,14 +21,10 @@ use librespot_core::spotify_id::{SpotifyAudioType, SpotifyId, SpotifyIdError};
|
|
use librespot_core::util::SeqGenerator;
|
|
use librespot_core::util::SeqGenerator;
|
|
use librespot_core::version;
|
|
use librespot_core::version;
|
|
use librespot_core::volume::Volume;
|
|
use librespot_core::volume::Volume;
|
|
-use playback::mixer::Mixer;
|
|
|
|
-use playback::player::Player;
|
|
|
|
-use protocol;
|
|
|
|
-use protocol::spirc::{DeviceState, Frame, MessageType, PlayStatus, State, TrackRef};
|
|
|
|
|
|
|
|
pub struct SpircTask {
|
|
pub struct SpircTask {
|
|
player: Player,
|
|
player: Player,
|
|
- mixer: Box<Mixer>,
|
|
|
|
|
|
+ mixer: Box<dyn Mixer>,
|
|
config: SpircTaskConfig,
|
|
config: SpircTaskConfig,
|
|
|
|
|
|
sequence: SeqGenerator<u32>,
|
|
sequence: SeqGenerator<u32>,
|
|
@@ -33,15 +33,15 @@ pub struct SpircTask {
|
|
device: DeviceState,
|
|
device: DeviceState,
|
|
state: State,
|
|
state: State,
|
|
|
|
|
|
- subscription: Box<Stream<Item = Frame, Error = MercuryError>>,
|
|
|
|
- sender: Box<Sink<SinkItem = Frame, SinkError = MercuryError>>,
|
|
|
|
|
|
+ subscription: Box<dyn Stream<Item = Frame, Error = MercuryError>>,
|
|
|
|
+ sender: Box<dyn Sink<SinkItem = Frame, SinkError = MercuryError>>,
|
|
commands: mpsc::UnboundedReceiver<SpircCommand>,
|
|
commands: mpsc::UnboundedReceiver<SpircCommand>,
|
|
- end_of_track: Box<Future<Item = (), Error = oneshot::Canceled>>,
|
|
|
|
|
|
+ end_of_track: Box<dyn Future<Item = (), Error = oneshot::Canceled>>,
|
|
|
|
|
|
shutdown: bool,
|
|
shutdown: bool,
|
|
session: Session,
|
|
session: Session,
|
|
- context_fut: Box<Future<Item = serde_json::Value, Error = MercuryError>>,
|
|
|
|
- autoplay_fut: Box<Future<Item = String, Error = MercuryError>>,
|
|
|
|
|
|
+ context_fut: Box<dyn Future<Item = serde_json::Value, Error = MercuryError>>,
|
|
|
|
+ autoplay_fut: Box<dyn Future<Item = String, Error = MercuryError>>,
|
|
context: Option<StationContext>,
|
|
context: Option<StationContext>,
|
|
}
|
|
}
|
|
|
|
|
|
@@ -221,7 +221,7 @@ impl Spirc {
|
|
config: ConnectConfig,
|
|
config: ConnectConfig,
|
|
session: Session,
|
|
session: Session,
|
|
player: Player,
|
|
player: Player,
|
|
- mixer: Box<Mixer>,
|
|
|
|
|
|
+ mixer: Box<dyn Mixer>,
|
|
) -> (Spirc, SpircTask) {
|
|
) -> (Spirc, SpircTask) {
|
|
debug!("new Spirc[{}]", session.session_id());
|
|
debug!("new Spirc[{}]", session.session_id());
|
|
|
|
|
|
@@ -526,7 +526,8 @@ impl SpircTask {
|
|
|
|
|
|
if self.state.get_track().len() > 0 {
|
|
if self.state.get_track().len() > 0 {
|
|
let now = self.now_ms();
|
|
let now = self.now_ms();
|
|
- self.state.set_position_ms(frame.get_state().get_position_ms());
|
|
|
|
|
|
+ self.state
|
|
|
|
+ .set_position_ms(frame.get_state().get_position_ms());
|
|
self.state.set_position_measured_at(now as u64);
|
|
self.state.set_position_measured_at(now as u64);
|
|
|
|
|
|
let play = frame.get_state().get_status() == PlayStatus::kPlayStatusPlay;
|
|
let play = frame.get_state().get_status() == PlayStatus::kPlayStatusPlay;
|
|
@@ -689,7 +690,8 @@ impl SpircTask {
|
|
tracks_len - new_index < CONTEXT_FETCH_THRESHOLD
|
|
tracks_len - new_index < CONTEXT_FETCH_THRESHOLD
|
|
);
|
|
);
|
|
let context_uri = self.state.get_context_uri().to_owned();
|
|
let context_uri = self.state.get_context_uri().to_owned();
|
|
- if (context_uri.starts_with("spotify:station:") || context_uri.starts_with("spotify:dailymix:"))
|
|
|
|
|
|
+ if (context_uri.starts_with("spotify:station:")
|
|
|
|
+ || context_uri.starts_with("spotify:dailymix:"))
|
|
&& ((self.state.get_track().len() as u32) - new_index) < CONTEXT_FETCH_THRESHOLD
|
|
&& ((self.state.get_track().len() as u32) - new_index) < CONTEXT_FETCH_THRESHOLD
|
|
{
|
|
{
|
|
self.context_fut = self.resolve_station(&context_uri);
|
|
self.context_fut = self.resolve_station(&context_uri);
|
|
@@ -785,18 +787,28 @@ impl SpircTask {
|
|
self.state.get_position_ms() + diff as u32
|
|
self.state.get_position_ms() + diff as u32
|
|
}
|
|
}
|
|
|
|
|
|
- fn resolve_station(&self, uri: &str) -> Box<Future<Item = serde_json::Value, Error = MercuryError>> {
|
|
|
|
|
|
+ fn resolve_station(
|
|
|
|
+ &self,
|
|
|
|
+ uri: &str,
|
|
|
|
+ ) -> Box<dyn Future<Item = serde_json::Value, Error = MercuryError>> {
|
|
let radio_uri = format!("hm://radio-apollo/v3/stations/{}", uri);
|
|
let radio_uri = format!("hm://radio-apollo/v3/stations/{}", uri);
|
|
|
|
|
|
self.resolve_uri(&radio_uri)
|
|
self.resolve_uri(&radio_uri)
|
|
}
|
|
}
|
|
|
|
|
|
- fn resolve_autoplay_uri(&self, uri: &str) -> Box<Future<Item = String, Error = MercuryError>> {
|
|
|
|
|
|
+ fn resolve_autoplay_uri(
|
|
|
|
+ &self,
|
|
|
|
+ uri: &str,
|
|
|
|
+ ) -> Box<dyn Future<Item = String, Error = MercuryError>> {
|
|
let query_uri = format!("hm://autoplay-enabled/query?uri={}", uri);
|
|
let query_uri = format!("hm://autoplay-enabled/query?uri={}", uri);
|
|
let request = self.session.mercury().get(query_uri);
|
|
let request = self.session.mercury().get(query_uri);
|
|
Box::new(request.and_then(move |response| {
|
|
Box::new(request.and_then(move |response| {
|
|
if response.status_code == 200 {
|
|
if response.status_code == 200 {
|
|
- let data = response.payload.first().expect("Empty autoplay uri").to_vec();
|
|
|
|
|
|
+ let data = response
|
|
|
|
+ .payload
|
|
|
|
+ .first()
|
|
|
|
+ .expect("Empty autoplay uri")
|
|
|
|
+ .to_vec();
|
|
let autoplay_uri = String::from_utf8(data).unwrap();
|
|
let autoplay_uri = String::from_utf8(data).unwrap();
|
|
Ok(autoplay_uri)
|
|
Ok(autoplay_uri)
|
|
} else {
|
|
} else {
|
|
@@ -806,11 +818,17 @@ impl SpircTask {
|
|
}))
|
|
}))
|
|
}
|
|
}
|
|
|
|
|
|
- fn resolve_uri(&self, uri: &str) -> Box<Future<Item = serde_json::Value, Error = MercuryError>> {
|
|
|
|
|
|
+ fn resolve_uri(
|
|
|
|
+ &self,
|
|
|
|
+ uri: &str,
|
|
|
|
+ ) -> Box<dyn Future<Item = serde_json::Value, Error = MercuryError>> {
|
|
let request = self.session.mercury().get(uri);
|
|
let request = self.session.mercury().get(uri);
|
|
|
|
|
|
Box::new(request.and_then(move |response| {
|
|
Box::new(request.and_then(move |response| {
|
|
- let data = response.payload.first().expect("Empty payload on context uri");
|
|
|
|
|
|
+ let data = response
|
|
|
|
+ .payload
|
|
|
|
+ .first()
|
|
|
|
+ .expect("Empty payload on context uri");
|
|
let response: serde_json::Value = serde_json::from_slice(&data).unwrap();
|
|
let response: serde_json::Value = serde_json::from_slice(&data).unwrap();
|
|
|
|
|
|
Ok(response)
|
|
Ok(response)
|
|
@@ -828,7 +846,8 @@ impl SpircTask {
|
|
track_vec.drain(0..head);
|
|
track_vec.drain(0..head);
|
|
}
|
|
}
|
|
track_vec.extend_from_slice(&new_tracks);
|
|
track_vec.extend_from_slice(&new_tracks);
|
|
- self.state.set_track(protobuf::RepeatedField::from_vec(track_vec));
|
|
|
|
|
|
+ self.state
|
|
|
|
+ .set_track(protobuf::RepeatedField::from_vec(track_vec));
|
|
|
|
|
|
// Update playing index
|
|
// Update playing index
|
|
if let Some(new_index) = self
|
|
if let Some(new_index) = self
|
|
@@ -849,7 +868,9 @@ impl SpircTask {
|
|
let context_uri = frame.get_state().get_context_uri().to_owned();
|
|
let context_uri = frame.get_state().get_context_uri().to_owned();
|
|
let tracks = frame.get_state().get_track();
|
|
let tracks = frame.get_state().get_track();
|
|
debug!("Frame has {:?} tracks", tracks.len());
|
|
debug!("Frame has {:?} tracks", tracks.len());
|
|
- if context_uri.starts_with("spotify:station:") || context_uri.starts_with("spotify:dailymix:") {
|
|
|
|
|
|
+ if context_uri.starts_with("spotify:station:")
|
|
|
|
+ || context_uri.starts_with("spotify:dailymix:")
|
|
|
|
+ {
|
|
self.context_fut = self.resolve_station(&context_uri);
|
|
self.context_fut = self.resolve_station(&context_uri);
|
|
} else if self.config.autoplay {
|
|
} else if self.config.autoplay {
|
|
info!("Fetching autoplay context uri");
|
|
info!("Fetching autoplay context uri");
|
|
@@ -889,7 +910,8 @@ impl SpircTask {
|
|
let track = {
|
|
let track = {
|
|
let mut track_ref = self.state.get_track()[index as usize].clone();
|
|
let mut track_ref = self.state.get_track()[index as usize].clone();
|
|
let mut track_id = self.get_spotify_id_for_track(&track_ref);
|
|
let mut track_id = self.get_spotify_id_for_track(&track_ref);
|
|
- while track_id.is_err() || track_id.unwrap().audio_type == SpotifyAudioType::NonPlayable {
|
|
|
|
|
|
+ while track_id.is_err() || track_id.unwrap().audio_type == SpotifyAudioType::NonPlayable
|
|
|
|
+ {
|
|
warn!(
|
|
warn!(
|
|
"Skipping track <{:?}> at position [{}] of {}",
|
|
"Skipping track <{:?}> at position [{}] of {}",
|
|
track_ref.get_uri(),
|
|
track_ref.get_uri(),
|