Playback: play the current track when toggling/restarting from idle
After a restart the queue is restored with the right current track, but playback does not autostart -- the audio engine has nothing loaded and the play state is Stopped. Space (TogglePlay) called unpause() and r (RestartTrack) called restart(), both of which error out with "not playing" because no source is loaded; only switching to the queue and pressing Enter (SetCurrent) actually started anything. Make the resume-style controls load the current queue track when the player is idle: TogglePlay now plays the current track on any non- playing/paused state, and RestartTrack starts the current track when nothing is loaded (and still restarts the loaded one otherwise). Both route through the same play() path SetCurrent uses, and are a no-op when the queue is empty. Paused/Playing behaviour is unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
28a4c155d0
commit
4e69261e02
|
|
@ -331,14 +331,27 @@ impl Playback {
|
||||||
*state
|
*state
|
||||||
};
|
};
|
||||||
debug!(?state, "toggling play");
|
debug!(?state, "toggling play");
|
||||||
if state == PlayState::Playing {
|
match state {
|
||||||
|
PlayState::Playing => {
|
||||||
if let Err(err) = self.player.pause().await {
|
if let Err(err) = self.player.pause().await {
|
||||||
warn!("pause failed: {err:?}");
|
warn!("pause failed: {err:?}");
|
||||||
}
|
}
|
||||||
} else if let Err(err) = self.player.unpause().await {
|
}
|
||||||
|
PlayState::Paused => {
|
||||||
|
if let Err(err) = self.player.unpause().await {
|
||||||
warn!("unpause failed: {err:?}");
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
PlaybackCommand::Stop => {
|
PlaybackCommand::Stop => {
|
||||||
debug!("stopping playback");
|
debug!("stopping playback");
|
||||||
|
|
@ -410,9 +423,24 @@ impl Playback {
|
||||||
|
|
||||||
PlaybackCommand::RestartTrack => {
|
PlaybackCommand::RestartTrack => {
|
||||||
debug!("restarting current track");
|
debug!("restarting current track");
|
||||||
|
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 {
|
if let Err(err) = self.player.restart().await {
|
||||||
warn!("restart failed: {err:?}");
|
warn!("restart failed: {err:?}");
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
self.play(self.current_track()).await;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PlaybackCommand::VolumeChanged { volume } => {
|
PlaybackCommand::VolumeChanged { volume } => {
|
||||||
|
|
@ -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<Track> {
|
||||||
|
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.
|
/// 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())))]
|
#[instrument(skip(self, track), fields(track = track.as_ref().map(|t| t.path.as_str())))]
|
||||||
async fn play_or_stop(&self, track: Option<Track>) {
|
async fn play_or_stop(&self, track: Option<Track>) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue