Make preloading work.

This commit is contained in:
Konstantin Seiler 2020-02-03 09:15:15 +11:00
parent 349e182d41
commit 6fed8d0413
2 changed files with 181 additions and 60 deletions

View file

@ -24,10 +24,20 @@ use librespot_core::volume::Volume;
enum SpircPlayStatus { enum SpircPlayStatus {
Stopped, Stopped,
LoadingPlay { position_ms: u32 }, LoadingPlay {
LoadingPause { position_ms: u32 }, position_ms: u32,
Playing { nominal_start_time: i64 }, },
Paused { position_ms: u32 }, LoadingPause {
position_ms: u32,
},
Playing {
nominal_start_time: i64,
preloading_of_next_track_triggered: bool,
},
Paused {
position_ms: u32,
preloading_of_next_track_triggered: bool,
},
} }
pub struct SpircTask { pub struct SpircTask {
@ -548,13 +558,14 @@ impl SpircTask {
PlayerEvent::Playing { position_ms, .. } => { PlayerEvent::Playing { position_ms, .. } => {
let new_nominal_start_time = self.now_ms() - position_ms as i64; let new_nominal_start_time = self.now_ms() - position_ms as i64;
match self.play_status { match self.play_status {
SpircPlayStatus::Playing { nominal_start_time } => { SpircPlayStatus::Playing {
if (nominal_start_time - new_nominal_start_time).abs() > 100 { ref mut nominal_start_time,
..
} => {
if (*nominal_start_time - new_nominal_start_time).abs() > 100 {
*nominal_start_time = new_nominal_start_time;
self.update_state_position(position_ms); self.update_state_position(position_ms);
self.notify(None); self.notify(None);
self.play_status = SpircPlayStatus::Playing {
nominal_start_time: new_nominal_start_time,
};
} }
} }
SpircPlayStatus::LoadingPlay { .. } SpircPlayStatus::LoadingPlay { .. }
@ -564,6 +575,7 @@ impl SpircTask {
self.notify(None); self.notify(None);
self.play_status = SpircPlayStatus::Playing { self.play_status = SpircPlayStatus::Playing {
nominal_start_time: new_nominal_start_time, nominal_start_time: new_nominal_start_time,
preloading_of_next_track_triggered: false,
}; };
} }
_ => (), _ => (),
@ -577,6 +589,7 @@ impl SpircTask {
match self.play_status { match self.play_status {
SpircPlayStatus::Paused { SpircPlayStatus::Paused {
ref mut position_ms, ref mut position_ms,
..
} => { } => {
if *position_ms != new_position_ms { if *position_ms != new_position_ms {
*position_ms = new_position_ms; *position_ms = new_position_ms;
@ -591,6 +604,7 @@ impl SpircTask {
self.notify(None); self.notify(None);
self.play_status = SpircPlayStatus::Paused { self.play_status = SpircPlayStatus::Paused {
position_ms: new_position_ms, position_ms: new_position_ms,
preloading_of_next_track_triggered: false,
}; };
} }
_ => (), _ => (),
@ -608,15 +622,22 @@ impl SpircTask {
} }
}, },
PlayerEvent::TimeToPreloadNextTrack { .. } => match self.play_status { PlayerEvent::TimeToPreloadNextTrack { .. } => match self.play_status {
SpircPlayStatus::Paused { .. } SpircPlayStatus::Paused {
| SpircPlayStatus::Playing { .. } ref mut preloading_of_next_track_triggered,
| SpircPlayStatus::LoadingPause { .. } ..
| SpircPlayStatus::LoadingPlay { .. } => { }
| SpircPlayStatus::Playing {
ref mut preloading_of_next_track_triggered,
..
} => {
*preloading_of_next_track_triggered = true;
if let Some(track_id) = self.preview_next_track() { if let Some(track_id) = self.preview_next_track() {
self.player.preload(track_id); self.player.preload(track_id);
} }
} }
SpircPlayStatus::Stopped => (), SpircPlayStatus::LoadingPause { .. }
| SpircPlayStatus::LoadingPlay { .. }
| SpircPlayStatus::Stopped => (),
}, },
_ => (), _ => (),
} }
@ -745,6 +766,22 @@ impl SpircTask {
MessageType::kMessageTypeReplace => { MessageType::kMessageTypeReplace => {
self.update_tracks(&frame); self.update_tracks(&frame);
self.notify(None); self.notify(None);
if let SpircPlayStatus::Playing {
preloading_of_next_track_triggered,
..
}
| SpircPlayStatus::Paused {
preloading_of_next_track_triggered,
..
} = self.play_status
{
if preloading_of_next_track_triggered {
if let Some(track_id) = self.preview_next_track() {
self.player.preload(track_id);
}
}
}
} }
MessageType::kMessageTypeVolume => { MessageType::kMessageTypeVolume => {
@ -768,13 +805,17 @@ impl SpircTask {
fn handle_play(&mut self) { fn handle_play(&mut self) {
match self.play_status { match self.play_status {
SpircPlayStatus::Paused { position_ms } => { SpircPlayStatus::Paused {
position_ms,
preloading_of_next_track_triggered,
} => {
self.ensure_mixer_started(); self.ensure_mixer_started();
self.player.play(); self.player.play();
self.state.set_status(PlayStatus::kPlayStatusPlay); self.state.set_status(PlayStatus::kPlayStatusPlay);
self.update_state_position(position_ms); self.update_state_position(position_ms);
self.play_status = SpircPlayStatus::Playing { self.play_status = SpircPlayStatus::Playing {
nominal_start_time: self.now_ms() as i64 - position_ms as i64, nominal_start_time: self.now_ms() as i64 - position_ms as i64,
preloading_of_next_track_triggered,
}; };
} }
SpircPlayStatus::LoadingPause { position_ms } => { SpircPlayStatus::LoadingPause { position_ms } => {
@ -800,12 +841,18 @@ impl SpircTask {
fn handle_pause(&mut self) { fn handle_pause(&mut self) {
match self.play_status { match self.play_status {
SpircPlayStatus::Playing { nominal_start_time } => { SpircPlayStatus::Playing {
nominal_start_time,
preloading_of_next_track_triggered,
} => {
self.player.pause(); self.player.pause();
self.state.set_status(PlayStatus::kPlayStatusPause); self.state.set_status(PlayStatus::kPlayStatusPause);
let position_ms = (self.now_ms() - nominal_start_time) as u32; let position_ms = (self.now_ms() - nominal_start_time) as u32;
self.update_state_position(position_ms); self.update_state_position(position_ms);
self.play_status = SpircPlayStatus::Paused { position_ms }; self.play_status = SpircPlayStatus::Paused {
position_ms,
preloading_of_next_track_triggered,
};
} }
SpircPlayStatus::LoadingPlay { position_ms } => { SpircPlayStatus::LoadingPlay { position_ms } => {
self.player.pause(); self.player.pause();
@ -829,9 +876,11 @@ impl SpircTask {
} }
| SpircPlayStatus::Paused { | SpircPlayStatus::Paused {
position_ms: ref mut position, position_ms: ref mut position,
..
} => *position = position_ms, } => *position = position_ms,
SpircPlayStatus::Playing { SpircPlayStatus::Playing {
ref mut nominal_start_time, ref mut nominal_start_time,
..
} => *nominal_start_time = now - position_ms as i64, } => *nominal_start_time = now - position_ms as i64,
}; };
} }
@ -851,7 +900,8 @@ impl SpircTask {
} }
fn preview_next_track(&mut self) -> Option<SpotifyId> { fn preview_next_track(&mut self) -> Option<SpotifyId> {
None self.get_track_id_to_play_from_playlist(self.state.get_playing_track_index() + 1)
.and_then(|(track_id, _)| Some(track_id))
} }
fn handle_next(&mut self) { fn handle_next(&mut self) {
@ -963,10 +1013,10 @@ impl SpircTask {
SpircPlayStatus::Stopped => 0, SpircPlayStatus::Stopped => 0,
SpircPlayStatus::LoadingPlay { position_ms } SpircPlayStatus::LoadingPlay { position_ms }
| SpircPlayStatus::LoadingPause { position_ms } | SpircPlayStatus::LoadingPause { position_ms }
| SpircPlayStatus::Paused { position_ms } => position_ms, | SpircPlayStatus::Paused { position_ms, .. } => position_ms,
SpircPlayStatus::Playing { nominal_start_time } => { SpircPlayStatus::Playing {
(self.now_ms() - nominal_start_time) as u32 nominal_start_time, ..
} } => (self.now_ms() - nominal_start_time) as u32,
} }
} }
@ -1077,42 +1127,56 @@ impl SpircTask {
}) })
} }
fn load_track(&mut self, start_playing: bool, position_ms: u32) { fn get_track_id_to_play_from_playlist(&self, index: u32) -> Option<(SpotifyId, u32)> {
let context_uri = self.state.get_context_uri().to_owned();
let mut index = self.state.get_playing_track_index();
let start_index = index;
let tracks_len = self.state.get_track().len() as u32; let tracks_len = self.state.get_track().len() as u32;
debug!(
"Loading context: <{}> index: [{}] of {}", let mut new_playlist_index = index;
context_uri, index, tracks_len
); if new_playlist_index >= tracks_len {
new_playlist_index = 0;
}
let start_index = new_playlist_index;
// Cycle through all tracks, break if we don't find any playable tracks // Cycle through all tracks, break if we don't find any playable tracks
// TODO: This will panic if no playable tracks are found!
// tracks in each frame either have a gid or uri (that may or may not be a valid track) // tracks in each frame either have a gid or uri (that may or may not be a valid track)
// E.g - context based frames sometimes contain tracks with <spotify:meta:page:> // E.g - context based frames sometimes contain tracks with <spotify:meta:page:>
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(),
index, new_playlist_index,
tracks_len tracks_len
); );
index = if index + 1 < tracks_len { index + 1 } else { 0 };
self.state.set_playing_track_index(index); new_playlist_index += 1;
if index == start_index { if new_playlist_index >= tracks_len {
new_playlist_index = 0;
}
if new_playlist_index == start_index {
warn!("No playable track found in state: {:?}", self.state); warn!("No playable track found in state: {:?}", self.state);
break; return None;
} }
track_ref = self.state.get_track()[index as usize].clone(); track_ref = self.state.get_track()[index as usize].clone();
track_id = self.get_spotify_id_for_track(&track_ref); track_id = self.get_spotify_id_for_track(&track_ref);
} }
track_id
match track_id {
Ok(track_id) => Some((track_id, new_playlist_index)),
Err(_) => None,
} }
.expect("Invalid SpotifyId"); }
fn load_track(&mut self, start_playing: bool, position_ms: u32) {
let index = self.state.get_playing_track_index();
match self.get_track_id_to_play_from_playlist(index) {
Some((track, index)) => {
self.state.set_playing_track_index(index);
self.play_request_id = Some(self.player.load(track, start_playing, position_ms)); self.play_request_id = Some(self.player.load(track, start_playing, position_ms));
@ -1124,6 +1188,62 @@ impl SpircTask {
self.play_status = SpircPlayStatus::LoadingPause { position_ms }; self.play_status = SpircPlayStatus::LoadingPause { position_ms };
} }
} }
None => {
self.state.set_status(PlayStatus::kPlayStatusStop);
self.player.stop();
self.ensure_mixer_stopped();
self.play_status = SpircPlayStatus::Stopped;
}
}
}
// fn load_track(&mut self, start_playing: bool, position_ms: u32) {
// let context_uri = self.state.get_context_uri().to_owned();
// let mut index = self.state.get_playing_track_index();
// let start_index = index;
// let tracks_len = self.state.get_track().len() as u32;
// debug!(
// "Loading context: <{}> index: [{}] of {}",
// context_uri, index, tracks_len
// );
// // Cycle through all tracks, break if we don't find any playable tracks
// // TODO: This will panic if no playable tracks are found!
// // tracks in each frame either have a gid or uri (that may or may not be a valid track)
// // E.g - context based frames sometimes contain tracks with <spotify:meta:page:>
// let track = {
// let mut track_ref = self.state.get_track()[index as usize].clone();
// let mut track_id = self.get_spotify_id_for_track(&track_ref);
// while track_id.is_err() || track_id.unwrap().audio_type == SpotifyAudioType::NonPlayable
// {
// warn!(
// "Skipping track <{:?}> at position [{}] of {}",
// track_ref.get_uri(),
// index,
// tracks_len
// );
// index = if index + 1 < tracks_len { index + 1 } else { 0 };
// self.state.set_playing_track_index(index);
// if index == start_index {
// warn!("No playable track found in state: {:?}", self.state);
// break;
// }
// track_ref = self.state.get_track()[index as usize].clone();
// track_id = self.get_spotify_id_for_track(&track_ref);
// }
// track_id
// }
// .expect("Invalid SpotifyId");
//
// self.play_request_id = Some(self.player.load(track, start_playing, position_ms));
//
// self.update_state_position(position_ms);
// self.state.set_status(PlayStatus::kPlayStatusLoading);
// if start_playing {
// self.play_status = SpircPlayStatus::LoadingPlay { position_ms };
// } else {
// self.play_status = SpircPlayStatus::LoadingPause { position_ms };
// }
// }
fn hello(&mut self) { fn hello(&mut self) {
CommandSender::new(self, MessageType::kMessageTypeHello).send(); CommandSender::new(self, MessageType::kMessageTypeHello).send();

View file

@ -1270,6 +1270,7 @@ impl PlayerInternal {
} }
PlayerCommand::Preload { track_id } => { PlayerCommand::Preload { track_id } => {
debug!("Preloading track");
let mut preload_track = true; let mut preload_track = true;
if let PlayerPreload::Loading { if let PlayerPreload::Loading {