This commit is contained in:
Ultradesu
2026-08-13 11:55:35 +01:00
parent e26159f85f
commit c2ca09aecf
12 changed files with 534 additions and 38 deletions
+57 -7
View File
@@ -265,8 +265,8 @@ impl Actor {
.then_some(unix_time_ms()),
position_secs: self.state.playback.position_seconds,
volume: volume_percent(self.state.playback.volume),
shuffle: false,
repeat: music_dht::device_sync::PlaybackRepeat::Off,
shuffle: self.state.playback.shuffle,
repeat: repeat_to_wire(self.state.playback.repeat),
}
}
@@ -311,16 +311,39 @@ impl Actor {
start_audio: bool,
seek: bool,
) {
let previous_status = self.state.playback.status;
let previous_index = self.state.queue.current_index();
let previous_track = self
.state
.queue
.current()
.map(|item| item.track.key.clone());
let tracks = wire
.queue
.iter()
.filter_map(|track| self.resolve_playback_track(track))
.collect::<Vec<_>>();
if !tracks.is_empty() {
self.state.queue.replace_context(tracks, wire.queue_pos);
if wire.shuffle {
if !self.state.playback.shuffle {
self.state.queue.remember_order();
}
self.state
.queue
.replace_shuffled_context(tracks, wire.queue_pos);
} else {
self.state.queue.replace_context(tracks, wire.queue_pos);
}
self.resolve_queue_artwork();
}
let current_changed = previous_index != self.state.queue.current_index()
|| previous_track
.as_ref()
.zip(self.state.queue.current().map(|item| &item.track.key))
.is_none_or(|(previous, current)| !previous.matches(current));
self.state.playback.volume = f32::from(wire.volume.min(100)) / 100.0;
self.state.playback.shuffle = wire.shuffle;
self.state.playback.repeat = repeat_from_wire(wire.repeat);
self.state.playback.position_seconds = wire.position_secs.max(0.0);
self.state.playback.duration_seconds = self
.state
@@ -338,9 +361,11 @@ impl Actor {
if !start_audio {
return;
}
if wire.playing {
if !wire.playing {
self.audio.stop();
} else if previous_status == PlaybackStatus::Stopped || current_changed {
self.play_current();
if seek || wire.position_secs > 0.0 {
if seek {
self.audio
.seek(Duration::from_secs_f64(wire.position_secs.max(0.0)));
self.state.playback.position_seconds = wire.position_secs.max(0.0);
@@ -349,8 +374,13 @@ impl Actor {
self.audio.pause();
self.state.playback.status = PlaybackStatus::Paused;
}
} else {
self.audio.stop();
} else if wire.paused {
self.audio.pause();
} else if previous_status == PlaybackStatus::Paused {
self.audio.resume();
} else if seek {
self.audio
.seek(Duration::from_secs_f64(wire.position_secs.max(0.0)));
}
}
@@ -479,3 +509,23 @@ impl Actor {
})
}
}
fn repeat_to_wire(
repeat: furumi_backend_api::PlaybackRepeat,
) -> music_dht::device_sync::PlaybackRepeat {
match repeat {
furumi_backend_api::PlaybackRepeat::Off => music_dht::device_sync::PlaybackRepeat::Off,
furumi_backend_api::PlaybackRepeat::One => music_dht::device_sync::PlaybackRepeat::One,
furumi_backend_api::PlaybackRepeat::All => music_dht::device_sync::PlaybackRepeat::All,
}
}
fn repeat_from_wire(
repeat: music_dht::device_sync::PlaybackRepeat,
) -> furumi_backend_api::PlaybackRepeat {
match repeat {
music_dht::device_sync::PlaybackRepeat::Off => furumi_backend_api::PlaybackRepeat::Off,
music_dht::device_sync::PlaybackRepeat::One => furumi_backend_api::PlaybackRepeat::One,
music_dht::device_sync::PlaybackRepeat::All => furumi_backend_api::PlaybackRepeat::All,
}
}
+40 -4
View File
@@ -11,8 +11,9 @@ use furumi_backend_api::{
BackendCommand, BackendSnapshot, BuildInfoSnapshot, ConnectedDeviceSnapshot,
ConnectedDevicesSnapshot, DevicePlaybackRole, DevicePresence, DeviceTrust,
FederationActivitySnapshot, FederationDebugSnapshot, FederationOperation, LibrarySnapshot,
PendingPairingSnapshot, PlaybackStatus, PlaylistSnapshot, RemoteData, RequestId, SearchResults,
SearchSnapshot, SearchStats, SendCommandError, SettingsSnapshot, VersionEntrySnapshot,
PendingPairingSnapshot, PlaybackRepeat, PlaybackStatus, PlaylistSnapshot, RemoteData,
RequestId, SearchResults, SearchSnapshot, SearchStats, SendCommandError, SettingsSnapshot,
VersionEntrySnapshot,
};
use furumi_domain::{
Artist, ArtistId, ArtistKey, ArtistRef, Artwork, AudioSource, CatalogSource, ContentId,
@@ -688,9 +689,25 @@ impl Actor {
}
self.publish();
}
BackendCommand::ToggleShuffle => {
self.state.playback.shuffle = !self.state.playback.shuffle;
if self.state.playback.shuffle {
self.state.queue.shuffle_upcoming();
} else {
self.state.queue.restore_upcoming_order();
}
self.send_control_state(false);
self.publish();
}
BackendCommand::CycleRepeat => {
self.state.playback.repeat = self.state.playback.repeat.next();
self.send_control_state(false);
self.publish();
}
BackendCommand::PlayRelease { release_id, start } => {
if let Some(release) = self.release(&release_id).cloned() {
self.state.queue.replace_context(release.tracks, start);
self.shuffle_new_context();
self.resolve_queue_artwork();
self.play_current();
}
@@ -698,6 +715,7 @@ impl Actor {
BackendCommand::PlayTrack { track } => {
if let Some(track) = self.track(&track).cloned() {
self.state.queue.replace_context(vec![track], 0);
self.shuffle_new_context();
self.resolve_queue_artwork();
self.play_current();
}
@@ -712,6 +730,7 @@ impl Actor {
if !tracks.is_empty() {
let start = selected_track_position(&tracks, &selected);
self.state.queue.replace_context(tracks, start);
self.shuffle_new_context();
self.resolve_queue_artwork();
self.play_current();
}
@@ -808,7 +827,7 @@ impl Actor {
self.select_playback_device(&device_id);
}
BackendCommand::Next => {
if self.state.queue.advance().is_some() {
if self.advance_queue(false) {
self.play_current();
}
}
@@ -1427,7 +1446,7 @@ impl Actor {
}
audio::Event::Finished => {
self.remove_ephemeral_audio();
if self.state.queue.advance().is_some() {
if self.advance_queue(true) {
self.play_current();
} else {
self.state.playback.status = PlaybackStatus::Stopped;
@@ -1476,6 +1495,23 @@ impl Actor {
self.publish();
}
fn shuffle_new_context(&mut self) {
if self.state.playback.shuffle {
self.state.queue.shuffle_upcoming();
}
}
fn advance_queue(&mut self, natural_finish: bool) -> bool {
if natural_finish && self.state.playback.repeat == PlaybackRepeat::One {
return self.state.queue.current().is_some();
}
if self.state.queue.advance().is_some() {
return true;
}
self.state.playback.repeat == PlaybackRepeat::All
&& self.state.queue.select_index(0).is_some()
}
fn start_federated_playback(&mut self, track: Track, peer_id: String, content_id: ContentId) {
let Some(client) = self.federation.clone() else {
self.state.playback_error = Some("federation is still starting".into());