Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions psst-core/src/player/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use crate::{
use self::{
file::MediaPath,
item::{LoadedPlaybackItem, PlaybackItem},
queue::{Queue, QueueBehavior},
queue::{LoopBehavior, Queue, ShuffleBehavior},
worker::PlaybackManager,
};

Expand Down Expand Up @@ -118,7 +118,12 @@ impl Player {
PlayerCommand::Stop => self.stop(),
PlayerCommand::Seek { position } => self.seek(position),
PlayerCommand::Configure { config } => self.configure(config),
PlayerCommand::SetQueueBehavior { behavior } => self.queue.set_behaviour(behavior),
PlayerCommand::SetShuffleBehavior { shuffle_behavior } => {
self.queue.set_shuffle_behaviour(shuffle_behavior)
}
PlayerCommand::SetLoopBehavior { loop_behavior } => {
self.queue.set_loop_behaviour(loop_behavior)
}
PlayerCommand::AddToQueue { item } => self.queue.add(item),
PlayerCommand::SetVolume { volume } => self.set_volume(volume),
}
Expand Down Expand Up @@ -420,8 +425,11 @@ pub enum PlayerCommand {
Configure {
config: PlaybackConfig,
},
SetQueueBehavior {
behavior: QueueBehavior,
SetShuffleBehavior {
shuffle_behavior: ShuffleBehavior,
},
SetLoopBehavior {
loop_behavior: LoopBehavior,
},
AddToQueue {
item: PlaybackItem,
Expand Down
59 changes: 35 additions & 24 deletions psst-core/src/player/queue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,38 @@ use rand::prelude::SliceRandom;
use super::PlaybackItem;

#[derive(Debug)]
pub enum QueueBehavior {
pub enum ShuffleBehavior {
Sequential,
Random,
LoopTrack,
LoopAll,
}

impl Default for QueueBehavior {
#[derive(Debug)]
pub enum LoopBehavior {
Track,
All,
None,
}

impl Default for ShuffleBehavior {
fn default() -> Self {
Self::Sequential
}
}

impl Default for LoopBehavior {
fn default() -> Self {
Self::None
}
}

pub struct Queue {
items: Vec<PlaybackItem>,
user_items: Vec<PlaybackItem>,
position: usize,
user_items_position: usize,
positions: Vec<usize>,
behavior: QueueBehavior,
shuffle_behavior: ShuffleBehavior,
loop_behavior: LoopBehavior,
}

impl Queue {
Expand All @@ -33,7 +45,8 @@ impl Queue {
position: 0,
user_items_position: 0,
positions: Vec::new(),
behavior: QueueBehavior::default(),
shuffle_behavior: ShuffleBehavior::default(),
loop_behavior: LoopBehavior::default(),
}
}

Expand Down Expand Up @@ -66,8 +79,13 @@ impl Queue {
}
}

pub fn set_behaviour(&mut self, behavior: QueueBehavior) {
self.behavior = behavior;
pub fn set_shuffle_behaviour(&mut self, shuffle_behavior: ShuffleBehavior) {
self.shuffle_behavior = shuffle_behavior;
self.compute_positions();
}

pub fn set_loop_behaviour(&mut self, loop_behavior: LoopBehavior) {
self.loop_behavior = loop_behavior;
self.compute_positions();
}

Expand All @@ -82,7 +100,7 @@ impl Queue {
// Start with an ordered 1:1 mapping.
self.positions = (0..self.items.len()).collect();

if let QueueBehavior::Random = self.behavior {
if let ShuffleBehavior::Random = self.shuffle_behavior {
// Swap the current position with the first item, so we will start from the
// beginning, with the full queue ahead of us. Then shuffle the rest of the
// items and set the position to 0.
Expand Down Expand Up @@ -127,28 +145,21 @@ impl Queue {
}

fn previous_position(&self) -> usize {
match self.behavior {
QueueBehavior::Sequential
| QueueBehavior::Random
| QueueBehavior::LoopTrack
| QueueBehavior::LoopAll => self.position.saturating_sub(1),
}
self.position.saturating_sub(1)
}

fn next_position(&self) -> usize {
match self.behavior {
QueueBehavior::Sequential | QueueBehavior::Random | QueueBehavior::LoopTrack => {
self.position + 1
}
QueueBehavior::LoopAll => (self.position + 1) % self.items.len(),
match self.loop_behavior {
LoopBehavior::Track | LoopBehavior::None => self.position + 1,
LoopBehavior::All => (self.position + 1) % self.items.len(),
}
}

fn following_position(&self) -> usize {
match self.behavior {
QueueBehavior::Sequential | QueueBehavior::Random => self.position + 1,
QueueBehavior::LoopTrack => self.position,
QueueBehavior::LoopAll => (self.position + 1) % self.items.len(),
match self.loop_behavior {
LoopBehavior::None => self.position + 1,
LoopBehavior::Track => self.position,
LoopBehavior::All => (self.position + 1) % self.items.len(),
}
}
}
6 changes: 4 additions & 2 deletions psst-gui/src/cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use std::sync::Arc;
use std::time::Duration;

use crate::{
data::{Nav, PlaybackPayload, QueueBehavior, QueueEntry},
data::{LoopBehavior, Nav, PlaybackPayload, QueueEntry, ShuffleBehavior},
ui::find::Find,
};

Expand Down Expand Up @@ -54,7 +54,9 @@ pub const PLAY_RESUME: Selector = Selector::new("app.play-resume");
pub const PLAY_NEXT: Selector = Selector::new("app.play-next");
pub const PLAY_STOP: Selector = Selector::new("app.play-stop");
pub const ADD_TO_QUEUE: Selector<(QueueEntry, PlaybackItem)> = Selector::new("app.add-to-queue");
pub const PLAY_QUEUE_BEHAVIOR: Selector<QueueBehavior> = Selector::new("app.play-queue-behavior");
pub const PLAY_SHUFFLE_BEHAVIOR: Selector<ShuffleBehavior> =
Selector::new("app.play-shuffle-behavior");
pub const PLAY_LOOP_BEHAVIOR: Selector<LoopBehavior> = Selector::new("app.play-loop-behavior");
pub const PLAY_SEEK: Selector<f64> = Selector::new("app.play-seek");
pub const SKIP_TO_POSITION: Selector<u64> = Selector::new("app.skip-to-position");

Expand Down
46 changes: 32 additions & 14 deletions psst-gui/src/controller/playback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,13 @@ use souvlaki::{
};
use std::time::{SystemTime, UNIX_EPOCH};

use crate::data::ShuffleBehavior;
use crate::{
cmd,
data::Nav,
data::{
AppState, Config, NowPlaying, Playable, Playback, PlaybackOrigin, PlaybackState,
QueueBehavior, QueueEntry,
AppState, Config, LoopBehavior, NowPlaying, Playable, Playback, PlaybackOrigin,
PlaybackState, QueueEntry,
},
ui::lyrics,
};
Expand Down Expand Up @@ -392,13 +393,23 @@ impl PlaybackController {
}));
}

fn set_queue_behavior(&mut self, behavior: QueueBehavior) {
self.send(PlayerEvent::Command(PlayerCommand::SetQueueBehavior {
behavior: match behavior {
QueueBehavior::Sequential => psst_core::player::queue::QueueBehavior::Sequential,
QueueBehavior::Random => psst_core::player::queue::QueueBehavior::Random,
QueueBehavior::LoopTrack => psst_core::player::queue::QueueBehavior::LoopTrack,
QueueBehavior::LoopAll => psst_core::player::queue::QueueBehavior::LoopAll,
fn set_shuffle_behavior(&mut self, shuffle_behavior: ShuffleBehavior) {
self.send(PlayerEvent::Command(PlayerCommand::SetShuffleBehavior {
shuffle_behavior: match shuffle_behavior {
ShuffleBehavior::Sequential => {
psst_core::player::queue::ShuffleBehavior::Sequential
}
ShuffleBehavior::Random => psst_core::player::queue::ShuffleBehavior::Random,
},
}));
}

fn set_loop_behavior(&mut self, loop_behavior: LoopBehavior) {
self.send(PlayerEvent::Command(PlayerCommand::SetLoopBehavior {
loop_behavior: match loop_behavior {
LoopBehavior::Track => psst_core::player::queue::LoopBehavior::Track,
LoopBehavior::All => psst_core::player::queue::LoopBehavior::All,
LoopBehavior::None => psst_core::player::queue::LoopBehavior::None,
},
}));
}
Expand Down Expand Up @@ -526,10 +537,16 @@ where
data.add_queued_entry(entry.clone());
ctx.set_handled();
}
Event::Command(cmd) if cmd.is(cmd::PLAY_QUEUE_BEHAVIOR) => {
let behavior = cmd.get_unchecked(cmd::PLAY_QUEUE_BEHAVIOR);
data.set_queue_behavior(behavior.to_owned());
self.set_queue_behavior(behavior.to_owned());
Event::Command(cmd) if cmd.is(cmd::PLAY_SHUFFLE_BEHAVIOR) => {
let behavior = cmd.get_unchecked(cmd::PLAY_SHUFFLE_BEHAVIOR);
data.set_shuffle_behavior(behavior.to_owned());
self.set_shuffle_behavior(behavior.to_owned());
ctx.set_handled();
}
Event::Command(cmd) if cmd.is(cmd::PLAY_LOOP_BEHAVIOR) => {
let behavior = cmd.get_unchecked(cmd::PLAY_LOOP_BEHAVIOR);
data.set_loop_behavior(behavior.to_owned());
self.set_loop_behavior(behavior.to_owned());
ctx.set_handled();
}
Event::Command(cmd) if cmd.is(cmd::PLAY_SEEK) => {
Expand Down Expand Up @@ -600,7 +617,8 @@ where

// Initialize values loaded from the config.
self.set_volume(data.playback.volume);
self.set_queue_behavior(data.playback.queue_behavior);
self.set_shuffle_behavior(data.playback.shuffle_behavior);
self.set_loop_behavior(data.playback.loop_behavior);

// Request focus so we can receive keyboard events.
ctx.submit_command(cmd::SET_FOCUS.to(ctx.widget_id()));
Expand Down
8 changes: 5 additions & 3 deletions psst-gui/src/data/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use psst_core::{
};
use serde::{Deserialize, Serialize};

use super::{Nav, Promise, QueueBehavior, SliderScrollScale};
use super::{LoopBehavior, Nav, Promise, ShuffleBehavior, SliderScrollScale};
use crate::ui::theme;

#[derive(Clone, Debug, Data, Lens)]
Expand Down Expand Up @@ -114,7 +114,8 @@ pub struct Config {
pub theme: Theme,
pub volume: f64,
pub last_route: Option<Nav>,
pub queue_behavior: QueueBehavior,
pub shuffle_behavior: ShuffleBehavior,
pub loop_behavior: LoopBehavior,
pub show_track_cover: bool,
pub window_size: Size,
pub slider_scroll_scale: SliderScrollScale,
Expand All @@ -136,7 +137,8 @@ impl Default for Config {
theme: Default::default(),
volume: 1.0,
last_route: Default::default(),
queue_behavior: Default::default(),
shuffle_behavior: Default::default(),
loop_behavior: Default::default(),
show_track_cover: Default::default(),
window_size: Size::new(theme::grid(80.0), theme::grid(100.0)),
slider_scroll_scale: Default::default(),
Expand Down
19 changes: 13 additions & 6 deletions psst-gui/src/data/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ pub use crate::data::{
find::{FindQuery, Finder, MatchFindQuery},
nav::{Nav, Route, SpotifyUrl},
playback::{
NowPlaying, Playable, PlayableMatcher, Playback, PlaybackOrigin, PlaybackPayload,
PlaybackState, QueueBehavior, QueueEntry,
LoopBehavior, NowPlaying, Playable, PlayableMatcher, Playback, PlaybackOrigin,
PlaybackPayload, PlaybackState, QueueEntry, ShuffleBehavior,
},
playlist::{
Playlist, PlaylistAddTrack, PlaylistDetail, PlaylistLink, PlaylistRemoveTrack,
Expand Down Expand Up @@ -108,7 +108,8 @@ impl AppState {
let playback = Playback {
state: PlaybackState::Stopped,
now_playing: None,
queue_behavior: config.queue_behavior,
shuffle_behavior: config.shuffle_behavior,
loop_behavior: config.loop_behavior,
queue: Vector::new(),
volume: config.volume,
};
Expand Down Expand Up @@ -280,9 +281,15 @@ impl AppState {
self.common_ctx_mut().now_playing.take();
}

pub fn set_queue_behavior(&mut self, queue_behavior: QueueBehavior) {
self.playback.queue_behavior = queue_behavior;
self.config.queue_behavior = queue_behavior;
pub fn set_shuffle_behavior(&mut self, shuffle_behavior: ShuffleBehavior) {
self.playback.shuffle_behavior = shuffle_behavior;
self.config.shuffle_behavior = shuffle_behavior;
self.config.save();
}

pub fn set_loop_behavior(&mut self, loop_behavior: LoopBehavior) {
self.playback.loop_behavior = loop_behavior;
self.config.loop_behavior = loop_behavior;
self.config.save();
}
}
Expand Down
15 changes: 11 additions & 4 deletions psst-gui/src/data/playback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ use super::{
pub struct Playback {
pub state: PlaybackState,
pub now_playing: Option<NowPlaying>,
pub queue_behavior: QueueBehavior,
pub shuffle_behavior: ShuffleBehavior,
pub loop_behavior: LoopBehavior,
pub queue: Vector<QueueEntry>,
pub volume: f64,
}
Expand Down Expand Up @@ -73,12 +74,18 @@ impl Data for Playable {
}

#[derive(Default, Copy, Clone, Debug, Data, Eq, PartialEq, Serialize, Deserialize)]
pub enum QueueBehavior {
pub enum ShuffleBehavior {
#[default]
Sequential,
Random,
LoopTrack,
LoopAll,
}

#[derive(Default, Copy, Clone, Debug, Data, Eq, PartialEq, Serialize, Deserialize)]
pub enum LoopBehavior {
#[default]
Track,
All,
None,
}

#[derive(Copy, Clone, Debug, Data, Eq, PartialEq)]
Expand Down
2 changes: 1 addition & 1 deletion psst-gui/src/ui/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ pub const DOWNLOAD_ARTWORK: Selector<(String, String)> = Selector::new("app.artw
pub fn main_window(config: &Config) -> WindowDesc<AppState> {
let win = WindowDesc::new(root_widget())
.title(compute_main_window_title)
.with_min_size((theme::grid(65.0), theme::grid(50.0)))
.with_min_size((theme::grid(70.0), theme::grid(50.0)))
.window_size(config.window_size)
.show_title(false)
.transparent_titlebar(true);
Expand Down
Loading