diff --git a/crabidy-server/src/playback.rs b/crabidy-server/src/playback.rs index 6be80cb..0081b5b 100644 --- a/crabidy-server/src/playback.rs +++ b/crabidy-server/src/playback.rs @@ -331,12 +331,25 @@ impl Playback { *state }; debug!(?state, "toggling play"); - if state == PlayState::Playing { - if let Err(err) = self.player.pause().await { - warn!("pause failed: {err:?}"); + match state { + PlayState::Playing => { + if let Err(err) = self.player.pause().await { + warn!("pause failed: {err:?}"); + } + } + PlayState::Paused => { + if let Err(err) = self.player.unpause().await { + warn!("unpause failed: {err:?}"); + } + } + // Stopped/idle: nothing is loaded in the player (e.g. right + // after a restart restored the queue without autoplay), so + // there is nothing to unpause. Load and play the current + // queue track, exactly as `SetCurrent` does — a no-op when + // the queue is empty. + _ => { + self.play(self.current_track()).await; } - } else if let Err(err) = self.player.unpause().await { - warn!("unpause failed: {err:?}"); } } @@ -410,8 +423,23 @@ impl Playback { PlaybackCommand::RestartTrack => { debug!("restarting current track"); - if let Err(err) = self.player.restart().await { - warn!("restart failed: {err:?}"); + let state = { + let Ok(state) = self.state.lock() else { + error!("play state lock poisoned"); + return; + }; + *state + }; + // With a track loaded (playing or paused) restart it from the + // top. When nothing is loaded (queue restored without autoplay) + // there is no source to restart, so start the current queue + // track instead. + if matches!(state, PlayState::Playing | PlayState::Paused) { + if let Err(err) = self.player.restart().await { + warn!("restart failed: {err:?}"); + } + } else { + self.play(self.current_track()).await; } } @@ -701,6 +729,19 @@ impl Playback { } } + /// The queue's current track, or `None` when the queue is empty (or its + /// lock is poisoned). The resume-style controls (`TogglePlay`, + /// `RestartTrack`) use it to load what a restored/idle queue points at. + fn current_track(&self) -> Option { + match self.queue.lock() { + Ok(queue) => queue.current_track(), + Err(_) => { + error!("queue lock poisoned"); + None + } + } + } + /// Plays the given track if there is one, otherwise stops the player. #[instrument(skip(self, track), fields(track = track.as_ref().map(|t| t.path.as_str())))] async fn play_or_stop(&self, track: Option) {