Fix panic when retrying a track that already failed
@@ -14,7 +14,10 @@ use std::{
};
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 symphonia::core::io::MediaSource;
use tokio::sync::{mpsc, oneshot};
@@ -499,7 +502,7 @@ enum PlayerPreload {
None,
Loading {
track_id: SpotifyId,
- loader: Pin<Box<dyn Future<Output = Result<PlayerLoadedTrackData, ()>> + Send>>,
+ loader: Pin<Box<dyn FusedFuture<Output = Result<PlayerLoadedTrackData, ()>> + Send>>,
},
Ready {
track_id: SpotifyId,
@@ -515,7 +518,7 @@ enum PlayerState {
track_id: SpotifyId,
play_request_id: u64,
start_playback: bool,
- loader: Pin<Box<dyn Future<Output = Result<PlayerLoadedTrackData, ()>> + Send>>,
+ loader: Pin<Box<dyn FusedFuture<Output = Result<PlayerLoadedTrackData, ()>> + Send>>,
},
Paused {
track_id: SpotifyId,
@@ -571,6 +574,7 @@ impl PlayerState {
matches!(self, Stopped)
}
+ #[allow(dead_code)]
fn is_loading(&self) -> bool {
use self::PlayerState::*;
matches!(self, Loading { .. })
@@ -1026,6 +1030,9 @@ impl Future for PlayerInternal {
play_request_id,
} = self.state
{
+ // The loader may be terminated if we are trying to load the same track
+ // as before, and that track failed to open before.
+ if !loader.as_mut().is_terminated() {
match loader.as_mut().poll(cx) {
Poll::Ready(Ok(loaded_track)) => {
self.start_playback(
@@ -1044,7 +1051,6 @@ impl Future for PlayerInternal {
"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,
@@ -1053,6 +1059,7 @@ impl Future for PlayerInternal {
Poll::Pending => (),
}
}
+ }
// handle pending preload requests.
if let PlayerPreload::Loading {
@@ -2000,7 +2007,7 @@ impl PlayerInternal {
&mut self,
spotify_id: SpotifyId,
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.
// 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
