|
@@ -2,7 +2,7 @@ use std;
|
|
use std::time::{SystemTime, UNIX_EPOCH};
|
|
use std::time::{SystemTime, UNIX_EPOCH};
|
|
|
|
|
|
use futures::future;
|
|
use futures::future;
|
|
-use futures::sync::{mpsc, oneshot};
|
|
|
|
|
|
+use futures::sync::mpsc;
|
|
use futures::{Async, Future, Poll, Sink, Stream};
|
|
use futures::{Async, Future, Poll, Sink, Stream};
|
|
use protobuf::{self, Message};
|
|
use protobuf::{self, Message};
|
|
use rand;
|
|
use rand;
|
|
@@ -11,7 +11,7 @@ use serde_json;
|
|
|
|
|
|
use crate::context::StationContext;
|
|
use crate::context::StationContext;
|
|
use crate::playback::mixer::Mixer;
|
|
use crate::playback::mixer::Mixer;
|
|
-use crate::playback::player::Player;
|
|
|
|
|
|
+use crate::playback::player::{Player, PlayerEvent, PlayerEventChannel};
|
|
use crate::protocol;
|
|
use crate::protocol;
|
|
use crate::protocol::spirc::{DeviceState, Frame, MessageType, PlayStatus, State, TrackRef};
|
|
use crate::protocol::spirc::{DeviceState, Frame, MessageType, PlayStatus, State, TrackRef};
|
|
use librespot_core::config::ConnectConfig;
|
|
use librespot_core::config::ConnectConfig;
|
|
@@ -22,6 +22,14 @@ use librespot_core::util::SeqGenerator;
|
|
use librespot_core::version;
|
|
use librespot_core::version;
|
|
use librespot_core::volume::Volume;
|
|
use librespot_core::volume::Volume;
|
|
|
|
|
|
|
|
+enum SpircPlayStatus {
|
|
|
|
+ Stopped,
|
|
|
|
+ LoadingPlay { position_ms: u32 },
|
|
|
|
+ LoadingPause { position_ms: u32 },
|
|
|
|
+ Playing { nominal_start_time: i64 },
|
|
|
|
+ Paused { position_ms: u32 },
|
|
|
|
+}
|
|
|
|
+
|
|
pub struct SpircTask {
|
|
pub struct SpircTask {
|
|
player: Player,
|
|
player: Player,
|
|
mixer: Box<dyn Mixer>,
|
|
mixer: Box<dyn Mixer>,
|
|
@@ -32,11 +40,14 @@ pub struct SpircTask {
|
|
ident: String,
|
|
ident: String,
|
|
device: DeviceState,
|
|
device: DeviceState,
|
|
state: State,
|
|
state: State,
|
|
|
|
+ play_request_id: Option<u64>,
|
|
|
|
+ mixer_started: bool,
|
|
|
|
+ play_status: SpircPlayStatus,
|
|
|
|
|
|
subscription: Box<dyn Stream<Item = Frame, Error = MercuryError>>,
|
|
subscription: Box<dyn Stream<Item = Frame, Error = MercuryError>>,
|
|
sender: Box<dyn Sink<SinkItem = Frame, SinkError = MercuryError>>,
|
|
sender: Box<dyn Sink<SinkItem = Frame, SinkError = MercuryError>>,
|
|
commands: mpsc::UnboundedReceiver<SpircCommand>,
|
|
commands: mpsc::UnboundedReceiver<SpircCommand>,
|
|
- end_of_track: Box<dyn Future<Item = (), Error = oneshot::Canceled>>,
|
|
|
|
|
|
+ player_events: PlayerEventChannel,
|
|
|
|
|
|
shutdown: bool,
|
|
shutdown: bool,
|
|
session: Session,
|
|
session: Session,
|
|
@@ -255,6 +266,8 @@ impl Spirc {
|
|
};
|
|
};
|
|
let device = initial_device_state(config);
|
|
let device = initial_device_state(config);
|
|
|
|
|
|
|
|
+ let player_events = player.get_player_event_channel();
|
|
|
|
+
|
|
let mut task = SpircTask {
|
|
let mut task = SpircTask {
|
|
player: player,
|
|
player: player,
|
|
mixer: mixer,
|
|
mixer: mixer,
|
|
@@ -266,11 +279,14 @@ impl Spirc {
|
|
|
|
|
|
device: device,
|
|
device: device,
|
|
state: initial_state(),
|
|
state: initial_state(),
|
|
|
|
+ play_request_id: None,
|
|
|
|
+ mixer_started: false,
|
|
|
|
+ play_status: SpircPlayStatus::Stopped,
|
|
|
|
|
|
subscription: subscription,
|
|
subscription: subscription,
|
|
sender: sender,
|
|
sender: sender,
|
|
commands: cmd_rx,
|
|
commands: cmd_rx,
|
|
- end_of_track: Box::new(future::empty()),
|
|
|
|
|
|
+ player_events: player_events,
|
|
|
|
|
|
shutdown: false,
|
|
shutdown: false,
|
|
session: session.clone(),
|
|
session: session.clone(),
|
|
@@ -350,13 +366,14 @@ impl Future for SpircTask {
|
|
Async::NotReady => (),
|
|
Async::NotReady => (),
|
|
}
|
|
}
|
|
|
|
|
|
- match self.end_of_track.poll() {
|
|
|
|
- Ok(Async::Ready(())) => {
|
|
|
|
|
|
+ match self.player_events.poll() {
|
|
|
|
+ Ok(Async::NotReady) => (),
|
|
|
|
+ Ok(Async::Ready(None)) => (),
|
|
|
|
+ Err(_) => (),
|
|
|
|
+ Ok(Async::Ready(Some(event))) => {
|
|
progress = true;
|
|
progress = true;
|
|
- self.handle_end_of_track();
|
|
|
|
|
|
+ self.handle_player_event(event);
|
|
}
|
|
}
|
|
- Ok(Async::NotReady) => (),
|
|
|
|
- Err(oneshot::Canceled) => self.end_of_track = Box::new(future::empty()),
|
|
|
|
}
|
|
}
|
|
// TODO: Refactor
|
|
// TODO: Refactor
|
|
match self.context_fut.poll() {
|
|
match self.context_fut.poll() {
|
|
@@ -431,6 +448,26 @@ impl SpircTask {
|
|
+ (dur.subsec_nanos() / 1000_000) as i64)
|
|
+ (dur.subsec_nanos() / 1000_000) as i64)
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ fn ensure_mixer_started(&mut self) {
|
|
|
|
+ if !self.mixer_started {
|
|
|
|
+ self.mixer.start();
|
|
|
|
+ self.mixer_started = true;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ fn ensure_mixer_stopped(&mut self) {
|
|
|
|
+ if self.mixer_started {
|
|
|
|
+ self.mixer.stop();
|
|
|
|
+ self.mixer_started = false;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ fn update_state_position(&mut self, position_ms: u32) {
|
|
|
|
+ let now = self.now_ms();
|
|
|
|
+ self.state.set_position_measured_at(now as u64);
|
|
|
|
+ self.state.set_position_ms(position_ms);
|
|
|
|
+ }
|
|
|
|
+
|
|
fn handle_command(&mut self, cmd: SpircCommand) {
|
|
fn handle_command(&mut self, cmd: SpircCommand) {
|
|
let active = self.device.get_is_active();
|
|
let active = self.device.get_is_active();
|
|
match cmd {
|
|
match cmd {
|
|
@@ -498,14 +535,112 @@ impl SpircTask {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ fn handle_player_event(&mut self, event: PlayerEvent) {
|
|
|
|
+ // we only process events if the play_request_id matches. If it doesn't, it is
|
|
|
|
+ // an event that belongs to a previous track and only arrives now due to a race
|
|
|
|
+ // condition. In this case we have updated the state already and don't want to
|
|
|
|
+ // mess with it.
|
|
|
|
+ if let Some(play_request_id) = event.get_play_request_id() {
|
|
|
|
+ if Some(play_request_id) == self.play_request_id {
|
|
|
|
+ match event {
|
|
|
|
+ PlayerEvent::EndOfTrack { .. } => self.handle_end_of_track(),
|
|
|
|
+ PlayerEvent::Loading { .. } => (),
|
|
|
|
+ PlayerEvent::Playing { position_ms, .. } => {
|
|
|
|
+ let new_nominal_start_time =
|
|
|
|
+ self.now_ms() - position_ms as i64;
|
|
|
|
+ match self.play_status {
|
|
|
|
+ SpircPlayStatus::Playing { nominal_start_time } => {
|
|
|
|
+ if (nominal_start_time - new_nominal_start_time)
|
|
|
|
+ .abs()
|
|
|
|
+ > 100
|
|
|
|
+ {
|
|
|
|
+ self.update_state_position(position_ms);
|
|
|
|
+ self.notify(None);
|
|
|
|
+ self.play_status = SpircPlayStatus::Playing {
|
|
|
|
+ nominal_start_time: new_nominal_start_time,
|
|
|
|
+ };
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ SpircPlayStatus::LoadingPlay { .. }
|
|
|
|
+ | SpircPlayStatus::LoadingPause { .. } => {
|
|
|
|
+ self.state.set_status(PlayStatus::kPlayStatusPlay);
|
|
|
|
+ self.update_state_position(position_ms);
|
|
|
|
+ self.notify(None);
|
|
|
|
+ self.play_status = SpircPlayStatus::Playing {
|
|
|
|
+ nominal_start_time: new_nominal_start_time,
|
|
|
|
+ };
|
|
|
|
+ }
|
|
|
|
+ _ => (),
|
|
|
|
+ };
|
|
|
|
+ trace!("==> kPlayStatusPlay");
|
|
|
|
+ }
|
|
|
|
+ PlayerEvent::Paused {
|
|
|
|
+ position_ms: new_position_ms,
|
|
|
|
+ ..
|
|
|
|
+ } => {
|
|
|
|
+ match self.play_status {
|
|
|
|
+ SpircPlayStatus::Paused {
|
|
|
|
+ ref mut position_ms,
|
|
|
|
+ } => {
|
|
|
|
+ if *position_ms != new_position_ms {
|
|
|
|
+ *position_ms = new_position_ms;
|
|
|
|
+ self.update_state_position(new_position_ms);
|
|
|
|
+ self.notify(None);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ SpircPlayStatus::LoadingPlay { .. }
|
|
|
|
+ | SpircPlayStatus::LoadingPause { .. } => {
|
|
|
|
+ self.state.set_status(PlayStatus::kPlayStatusPause);
|
|
|
|
+ self.update_state_position(new_position_ms);
|
|
|
|
+ self.notify(None);
|
|
|
|
+ self.play_status = SpircPlayStatus::Paused {
|
|
|
|
+ position_ms: new_position_ms,
|
|
|
|
+ };
|
|
|
|
+ }
|
|
|
|
+ _ => (),
|
|
|
|
+ }
|
|
|
|
+ trace!("==> kPlayStatusPause");
|
|
|
|
+ }
|
|
|
|
+ PlayerEvent::Stopped { .. } => match self.play_status {
|
|
|
|
+ SpircPlayStatus::Stopped => (),
|
|
|
|
+ _ => {
|
|
|
|
+ warn!("The player has stopped unexpentedly.");
|
|
|
|
+ self.state.set_status(PlayStatus::kPlayStatusStop);
|
|
|
|
+ self.ensure_mixer_stopped();
|
|
|
|
+ self.notify(None);
|
|
|
|
+ self.play_status = SpircPlayStatus::Stopped;
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ PlayerEvent::TimeToPreloadNextTrack {..} => match self.play_status {
|
|
|
|
+ SpircPlayStatus::Paused {..}|SpircPlayStatus::Playing {..}| SpircPlayStatus::LoadingPause{..}|SpircPlayStatus::LoadingPlay {..} => {
|
|
|
|
+ if let Some(track_id) = self.preview_next_track() {
|
|
|
|
+ self.player.preload(track_id);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ SpircPlayStatus::Stopped => (),
|
|
|
|
+ }
|
|
|
|
+ _ => (),
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
fn handle_frame(&mut self, frame: Frame) {
|
|
fn handle_frame(&mut self, frame: Frame) {
|
|
|
|
+ let state_string = match frame.get_state().get_status() {
|
|
|
|
+ PlayStatus::kPlayStatusLoading => "kPlayStatusLoading",
|
|
|
|
+ PlayStatus::kPlayStatusPause => "kPlayStatusPause",
|
|
|
|
+ PlayStatus::kPlayStatusStop => "kPlayStatusStop",
|
|
|
|
+ PlayStatus::kPlayStatusPlay => "kPlayStatusPlay",
|
|
|
|
+ };
|
|
|
|
+
|
|
debug!(
|
|
debug!(
|
|
- "{:?} {:?} {} {} {}",
|
|
|
|
|
|
+ "{:?} {:?} {} {} {} {}",
|
|
frame.get_typ(),
|
|
frame.get_typ(),
|
|
frame.get_device_state().get_name(),
|
|
frame.get_device_state().get_name(),
|
|
frame.get_ident(),
|
|
frame.get_ident(),
|
|
frame.get_seq_nr(),
|
|
frame.get_seq_nr(),
|
|
- frame.get_state_update_id()
|
|
|
|
|
|
+ frame.get_state_update_id(),
|
|
|
|
+ state_string,
|
|
);
|
|
);
|
|
|
|
|
|
if frame.get_ident() == self.ident
|
|
if frame.get_ident() == self.ident
|
|
@@ -529,18 +664,15 @@ impl SpircTask {
|
|
self.update_tracks(&frame);
|
|
self.update_tracks(&frame);
|
|
|
|
|
|
if self.state.get_track().len() > 0 {
|
|
if self.state.get_track().len() > 0 {
|
|
- let now = self.now_ms();
|
|
|
|
- self.state
|
|
|
|
- .set_position_ms(frame.get_state().get_position_ms());
|
|
|
|
- self.state.set_position_measured_at(now as u64);
|
|
|
|
-
|
|
|
|
- let play = frame.get_state().get_status() == PlayStatus::kPlayStatusPlay;
|
|
|
|
- self.load_track(play);
|
|
|
|
|
|
+ let start_playing =
|
|
|
|
+ frame.get_state().get_status() == PlayStatus::kPlayStatusPlay;
|
|
|
|
+ self.load_track(start_playing, frame.get_state().get_position_ms());
|
|
} else {
|
|
} else {
|
|
info!("No more tracks left in queue");
|
|
info!("No more tracks left in queue");
|
|
self.state.set_status(PlayStatus::kPlayStatusStop);
|
|
self.state.set_status(PlayStatus::kPlayStatusStop);
|
|
self.player.stop();
|
|
self.player.stop();
|
|
self.mixer.stop();
|
|
self.mixer.stop();
|
|
|
|
+ self.play_status = SpircPlayStatus::Stopped;
|
|
}
|
|
}
|
|
|
|
|
|
self.notify(None);
|
|
self.notify(None);
|
|
@@ -607,12 +739,7 @@ impl SpircTask {
|
|
}
|
|
}
|
|
|
|
|
|
MessageType::kMessageTypeSeek => {
|
|
MessageType::kMessageTypeSeek => {
|
|
- let position = frame.get_position();
|
|
|
|
-
|
|
|
|
- let now = self.now_ms();
|
|
|
|
- self.state.set_position_ms(position);
|
|
|
|
- self.state.set_position_measured_at(now as u64);
|
|
|
|
- self.player.seek(position);
|
|
|
|
|
|
+ self.handle_seek(frame.get_position());
|
|
self.notify(None);
|
|
self.notify(None);
|
|
}
|
|
}
|
|
|
|
|
|
@@ -631,7 +758,8 @@ impl SpircTask {
|
|
self.device.set_is_active(false);
|
|
self.device.set_is_active(false);
|
|
self.state.set_status(PlayStatus::kPlayStatusStop);
|
|
self.state.set_status(PlayStatus::kPlayStatusStop);
|
|
self.player.stop();
|
|
self.player.stop();
|
|
- self.mixer.stop();
|
|
|
|
|
|
+ self.ensure_mixer_stopped();
|
|
|
|
+ self.play_status = SpircPlayStatus::Stopped;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -640,39 +768,75 @@ impl SpircTask {
|
|
}
|
|
}
|
|
|
|
|
|
fn handle_play(&mut self) {
|
|
fn handle_play(&mut self) {
|
|
- if self.state.get_status() == PlayStatus::kPlayStatusPause {
|
|
|
|
- self.mixer.start();
|
|
|
|
- self.player.play();
|
|
|
|
- self.state.set_status(PlayStatus::kPlayStatusPlay);
|
|
|
|
- let now = self.now_ms();
|
|
|
|
- self.state.set_position_measured_at(now as u64);
|
|
|
|
|
|
+ match self.play_status {
|
|
|
|
+ SpircPlayStatus::Paused { position_ms } => {
|
|
|
|
+ self.ensure_mixer_started();
|
|
|
|
+ self.player.play();
|
|
|
|
+ self.state.set_status(PlayStatus::kPlayStatusPlay);
|
|
|
|
+ self.update_state_position(position_ms);
|
|
|
|
+ self.play_status = SpircPlayStatus::Playing {
|
|
|
|
+ nominal_start_time: self.now_ms() as i64 - position_ms as i64,
|
|
|
|
+ };
|
|
|
|
+ }
|
|
|
|
+ SpircPlayStatus::LoadingPause { position_ms } => {
|
|
|
|
+ self.ensure_mixer_started();
|
|
|
|
+ self.player.play();
|
|
|
|
+ self.play_status = SpircPlayStatus::LoadingPlay { position_ms };
|
|
|
|
+ }
|
|
|
|
+ _ => (),
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
fn handle_play_pause(&mut self) {
|
|
fn handle_play_pause(&mut self) {
|
|
- match self.state.get_status() {
|
|
|
|
- PlayStatus::kPlayStatusPlay => self.handle_pause(),
|
|
|
|
- PlayStatus::kPlayStatusPause => self.handle_play(),
|
|
|
|
|
|
+ match self.play_status {
|
|
|
|
+ SpircPlayStatus::Paused { .. } | SpircPlayStatus::LoadingPause { .. } => {
|
|
|
|
+ self.handle_play()
|
|
|
|
+ }
|
|
|
|
+ SpircPlayStatus::Playing { .. } | SpircPlayStatus::LoadingPlay { .. } => {
|
|
|
|
+ self.handle_play()
|
|
|
|
+ }
|
|
_ => (),
|
|
_ => (),
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
fn handle_pause(&mut self) {
|
|
fn handle_pause(&mut self) {
|
|
- if self.state.get_status() == PlayStatus::kPlayStatusPlay {
|
|
|
|
- self.player.pause();
|
|
|
|
- self.mixer.stop();
|
|
|
|
- self.state.set_status(PlayStatus::kPlayStatusPause);
|
|
|
|
-
|
|
|
|
- let now = self.now_ms() as u64;
|
|
|
|
- let position = self.state.get_position_ms();
|
|
|
|
-
|
|
|
|
- let diff = now - self.state.get_position_measured_at();
|
|
|
|
-
|
|
|
|
- self.state.set_position_ms(position + diff as u32);
|
|
|
|
- self.state.set_position_measured_at(now);
|
|
|
|
|
|
+ match self.play_status {
|
|
|
|
+ SpircPlayStatus::Playing { nominal_start_time } => {
|
|
|
|
+ self.player.pause();
|
|
|
|
+ self.state.set_status(PlayStatus::kPlayStatusPause);
|
|
|
|
+ let position_ms = (self.now_ms() - nominal_start_time) as u32;
|
|
|
|
+ self.update_state_position(position_ms);
|
|
|
|
+ self.play_status = SpircPlayStatus::Paused { position_ms };
|
|
|
|
+ }
|
|
|
|
+ SpircPlayStatus::LoadingPlay { position_ms } => {
|
|
|
|
+ self.player.pause();
|
|
|
|
+ self.play_status = SpircPlayStatus::LoadingPause { position_ms };
|
|
|
|
+ }
|
|
|
|
+ _ => (),
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ fn handle_seek(&mut self, position_ms: u32) {
|
|
|
|
+ self.update_state_position(position_ms);
|
|
|
|
+ self.player.seek(position_ms);
|
|
|
|
+ let now = self.now_ms();
|
|
|
|
+ match self.play_status {
|
|
|
|
+ SpircPlayStatus::Stopped => (),
|
|
|
|
+ SpircPlayStatus::LoadingPause {
|
|
|
|
+ position_ms: ref mut position,
|
|
|
|
+ }
|
|
|
|
+ | SpircPlayStatus::LoadingPlay {
|
|
|
|
+ position_ms: ref mut position,
|
|
|
|
+ }
|
|
|
|
+ | SpircPlayStatus::Paused {
|
|
|
|
+ position_ms: ref mut position,
|
|
|
|
+ } => *position = position_ms,
|
|
|
|
+ SpircPlayStatus::Playing {
|
|
|
|
+ ref mut nominal_start_time,
|
|
|
|
+ } => *nominal_start_time = now - position_ms as i64,
|
|
|
|
+ };
|
|
|
|
+ }
|
|
|
|
+
|
|
fn consume_queued_track(&mut self) -> usize {
|
|
fn consume_queued_track(&mut self) -> usize {
|
|
// Removes current track if it is queued
|
|
// Removes current track if it is queued
|
|
// Returns the index of the next track
|
|
// Returns the index of the next track
|
|
@@ -687,6 +851,10 @@ impl SpircTask {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ fn preview_next_track(&mut self) -> Option<SpotifyId> {
|
|
|
|
+ None
|
|
|
|
+ }
|
|
|
|
+
|
|
fn handle_next(&mut self) {
|
|
fn handle_next(&mut self) {
|
|
let mut new_index = self.consume_queued_track() as u32;
|
|
let mut new_index = self.consume_queued_track() as u32;
|
|
let mut continue_playing = true;
|
|
let mut continue_playing = true;
|
|
@@ -720,17 +888,14 @@ impl SpircTask {
|
|
|
|
|
|
if tracks_len > 0 {
|
|
if tracks_len > 0 {
|
|
self.state.set_playing_track_index(new_index);
|
|
self.state.set_playing_track_index(new_index);
|
|
- self.state.set_position_ms(0);
|
|
|
|
- let now = self.now_ms();
|
|
|
|
- self.state.set_position_measured_at(now as u64);
|
|
|
|
-
|
|
|
|
- self.load_track(continue_playing);
|
|
|
|
|
|
+ self.load_track(continue_playing, 0);
|
|
} else {
|
|
} else {
|
|
info!("Not playing next track because there are no more tracks left in queue.");
|
|
info!("Not playing next track because there are no more tracks left in queue.");
|
|
self.state.set_playing_track_index(0);
|
|
self.state.set_playing_track_index(0);
|
|
self.state.set_status(PlayStatus::kPlayStatusStop);
|
|
self.state.set_status(PlayStatus::kPlayStatusStop);
|
|
self.player.stop();
|
|
self.player.stop();
|
|
- self.mixer.stop();
|
|
|
|
|
|
+ self.ensure_mixer_stopped();
|
|
|
|
+ self.play_status = SpircPlayStatus::Stopped;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -765,17 +930,11 @@ impl SpircTask {
|
|
pos += 1;
|
|
pos += 1;
|
|
}
|
|
}
|
|
|
|
|
|
- let now = self.now_ms();
|
|
|
|
self.state.set_playing_track_index(new_index);
|
|
self.state.set_playing_track_index(new_index);
|
|
- self.state.set_position_ms(0);
|
|
|
|
- self.state.set_position_measured_at(now as u64);
|
|
|
|
|
|
|
|
- self.load_track(true);
|
|
|
|
|
|
+ self.load_track(true, 0);
|
|
} else {
|
|
} else {
|
|
- let now = self.now_ms();
|
|
|
|
- self.state.set_position_ms(0);
|
|
|
|
- self.state.set_position_measured_at(now as u64);
|
|
|
|
- self.player.seek(0);
|
|
|
|
|
|
+ self.handle_seek(0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -801,8 +960,15 @@ impl SpircTask {
|
|
}
|
|
}
|
|
|
|
|
|
fn position(&mut self) -> u32 {
|
|
fn position(&mut self) -> u32 {
|
|
- let diff = self.now_ms() as u64 - self.state.get_position_measured_at();
|
|
|
|
- self.state.get_position_ms() + diff as u32
|
|
|
|
|
|
+ match self.play_status {
|
|
|
|
+ SpircPlayStatus::Stopped => 0,
|
|
|
|
+ SpircPlayStatus::LoadingPlay { position_ms }
|
|
|
|
+ | SpircPlayStatus::LoadingPause { position_ms }
|
|
|
|
+ | SpircPlayStatus::Paused { position_ms } => position_ms,
|
|
|
|
+ SpircPlayStatus::Playing { nominal_start_time } => {
|
|
|
|
+ (self.now_ms() - nominal_start_time) as u32
|
|
|
|
+ }
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
fn resolve_station(
|
|
fn resolve_station(
|
|
@@ -912,7 +1078,7 @@ impl SpircTask {
|
|
})
|
|
})
|
|
}
|
|
}
|
|
|
|
|
|
- fn load_track(&mut self, play: bool) {
|
|
|
|
|
|
+ fn load_track(&mut self, start_playing: bool, position_ms: u32) {
|
|
let context_uri = self.state.get_context_uri().to_owned();
|
|
let context_uri = self.state.get_context_uri().to_owned();
|
|
let mut index = self.state.get_playing_track_index();
|
|
let mut index = self.state.get_playing_track_index();
|
|
let start_index = index;
|
|
let start_index = index;
|
|
@@ -949,16 +1115,15 @@ impl SpircTask {
|
|
}
|
|
}
|
|
.expect("Invalid SpotifyId");
|
|
.expect("Invalid SpotifyId");
|
|
|
|
|
|
- let position = self.state.get_position_ms();
|
|
|
|
- let end_of_track = self.player.load(track, play, position);
|
|
|
|
|
|
+ self.play_request_id = Some(self.player.load(track, start_playing, position_ms));
|
|
|
|
|
|
- if play {
|
|
|
|
- self.state.set_status(PlayStatus::kPlayStatusPlay);
|
|
|
|
|
|
+ self.update_state_position(position_ms);
|
|
|
|
+ self.state.set_status(PlayStatus::kPlayStatusLoading);
|
|
|
|
+ if start_playing {
|
|
|
|
+ self.play_status = SpircPlayStatus::LoadingPlay { position_ms };
|
|
} else {
|
|
} else {
|
|
- self.state.set_status(PlayStatus::kPlayStatusPause);
|
|
|
|
|
|
+ self.play_status = SpircPlayStatus::LoadingPause { position_ms };
|
|
}
|
|
}
|
|
-
|
|
|
|
- self.end_of_track = Box::new(end_of_track);
|
|
|
|
}
|
|
}
|
|
|
|
|
|
fn hello(&mut self) {
|
|
fn hello(&mut self) {
|
|
@@ -966,6 +1131,13 @@ impl SpircTask {
|
|
}
|
|
}
|
|
|
|
|
|
fn notify(&mut self, recipient: Option<&str>) {
|
|
fn notify(&mut self, recipient: Option<&str>) {
|
|
|
|
+ let status_string = match self.state.get_status() {
|
|
|
|
+ PlayStatus::kPlayStatusLoading => "kPlayStatusLoading",
|
|
|
|
+ PlayStatus::kPlayStatusPause => "kPlayStatusPause",
|
|
|
|
+ PlayStatus::kPlayStatusStop => "kPlayStatusStop",
|
|
|
|
+ PlayStatus::kPlayStatusPlay => "kPlayStatusPlay",
|
|
|
|
+ };
|
|
|
|
+ trace!("Sending status to server: [{}]", status_string);
|
|
let mut cs = CommandSender::new(self, MessageType::kMessageTypeNotify);
|
|
let mut cs = CommandSender::new(self, MessageType::kMessageTypeNotify);
|
|
if let Some(s) = recipient {
|
|
if let Some(s) = recipient {
|
|
cs = cs.recipient(&s);
|
|
cs = cs.recipient(&s);
|
|
@@ -980,6 +1152,7 @@ impl SpircTask {
|
|
if let Some(cache) = self.session.cache() {
|
|
if let Some(cache) = self.session.cache() {
|
|
cache.save_volume(Volume { volume })
|
|
cache.save_volume(Volume { volume })
|
|
}
|
|
}
|
|
|
|
+ self.player.emit_volume_set_event(volume);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|