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,6 +1030,9 @@ impl Future for PlayerInternal {
play_request_id, play_request_id,
} = self.state } = 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) { match loader.as_mut().poll(cx) {
Poll::Ready(Ok(loaded_track)) => { Poll::Ready(Ok(loaded_track)) => {
self.start_playback( self.start_playback(
@ -1044,7 +1051,6 @@ impl Future for PlayerInternal {
"Skipping to next track, unable to load track <{:?}>: {:?}", "Skipping to next track, unable to load track <{:?}>: {:?}",
track_id, e track_id, e
); );
debug_assert!(self.state.is_loading());
self.send_event(PlayerEvent::Unavailable { self.send_event(PlayerEvent::Unavailable {
track_id, track_id,
play_request_id, play_request_id,
@ -1053,6 +1059,7 @@ impl Future for PlayerInternal {
Poll::Pending => (), Poll::Pending => (),
} }
} }
}
// handle pending preload requests. // handle pending preload requests.
if let PlayerPreload::Loading { if let PlayerPreload::Loading {
@ -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