Fix panic when retrying a track that already failed

This commit is contained in:
Roderick van Domburg 2022-01-13 21:05:17 +01:00
parent ab67370dc8
commit e627cb4b35
No known key found for this signature in database
GPG key ID: A9EF5222A26F0451

View file

@ -14,7 +14,10 @@ use std::{
}; };
use byteorder::{LittleEndian, ReadBytesExt}; use byteorder::{LittleEndian, ReadBytesExt};
use futures_util::{future, stream::futures_unordered::FuturesUnordered, StreamExt, TryFutureExt}; use futures_util::{
future, future::FusedFuture, stream::futures_unordered::FuturesUnordered, StreamExt,
TryFutureExt,
};
use parking_lot::Mutex; use parking_lot::Mutex;
use symphonia::core::io::MediaSource; use symphonia::core::io::MediaSource;
use tokio::sync::{mpsc, oneshot}; use tokio::sync::{mpsc, oneshot};
@ -499,7 +502,7 @@ enum PlayerPreload {
None, None,
Loading { Loading {
track_id: SpotifyId, track_id: SpotifyId,
loader: Pin<Box<dyn Future<Output = Result<PlayerLoadedTrackData, ()>> + Send>>, loader: Pin<Box<dyn FusedFuture<Output = Result<PlayerLoadedTrackData, ()>> + Send>>,
}, },
Ready { Ready {
track_id: SpotifyId, track_id: SpotifyId,
@ -515,7 +518,7 @@ enum PlayerState {
track_id: SpotifyId, track_id: SpotifyId,
play_request_id: u64, play_request_id: u64,
start_playback: bool, start_playback: bool,
loader: Pin<Box<dyn Future<Output = Result<PlayerLoadedTrackData, ()>> + Send>>, loader: Pin<Box<dyn FusedFuture<Output = Result<PlayerLoadedTrackData, ()>> + Send>>,
}, },
Paused { Paused {
track_id: SpotifyId, track_id: SpotifyId,
@ -571,6 +574,7 @@ impl PlayerState {
matches!(self, Stopped) matches!(self, Stopped)
} }
#[allow(dead_code)]
fn is_loading(&self) -> bool { fn is_loading(&self) -> bool {
use self::PlayerState::*; use self::PlayerState::*;
matches!(self, Loading { .. }) matches!(self, Loading { .. })
@ -1026,31 +1030,34 @@ impl Future for PlayerInternal {
play_request_id, play_request_id,
} = self.state } = self.state
{ {
match loader.as_mut().poll(cx) { // The loader may be terminated if we are trying to load the same track
Poll::Ready(Ok(loaded_track)) => { // as before, and that track failed to open before.
self.start_playback( if !loader.as_mut().is_terminated() {
track_id, match loader.as_mut().poll(cx) {
play_request_id, Poll::Ready(Ok(loaded_track)) => {
loaded_track, self.start_playback(
start_playback, track_id,
); play_request_id,
if let PlayerState::Loading { .. } = self.state { loaded_track,
error!("The state wasn't changed by start_playback()"); start_playback,
exit(1); );
if let PlayerState::Loading { .. } = self.state {
error!("The state wasn't changed by start_playback()");
exit(1);
}
} }
Poll::Ready(Err(e)) => {
error!(
"Skipping to next track, unable to load track <{:?}>: {:?}",
track_id, e
);
self.send_event(PlayerEvent::Unavailable {
track_id,
play_request_id,
})
}
Poll::Pending => (),
} }
Poll::Ready(Err(e)) => {
error!(
"Skipping to next track, unable to load track <{:?}>: {:?}",
track_id, e
);
debug_assert!(self.state.is_loading());
self.send_event(PlayerEvent::Unavailable {
track_id,
play_request_id,
})
}
Poll::Pending => (),
} }
} }
@ -2000,7 +2007,7 @@ impl PlayerInternal {
&mut self, &mut self,
spotify_id: SpotifyId, spotify_id: SpotifyId,
position_ms: u32, position_ms: u32,
) -> impl Future<Output = Result<PlayerLoadedTrackData, ()>> + Send + 'static { ) -> impl FusedFuture<Output = Result<PlayerLoadedTrackData, ()>> + Send + 'static {
// This method creates a future that returns the loaded stream and associated info. // This method creates a future that returns the loaded stream and associated info.
// Ideally all work should be done using asynchronous code. However, seek() on the // Ideally all work should be done using asynchronous code. However, seek() on the
// audio stream is implemented in a blocking fashion. Thus, we can't turn it into future // audio stream is implemented in a blocking fashion. Thus, we can't turn it into future