Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ability to add to queue #471

Merged
merged 8 commits into from
Jun 17, 2024
Merged
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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ cargo bundle --release
- Reorder tracks
- Rename playlist
- Playlist folders
- [ ] Playback queue
- [x] Playback queue
- [ ] React to audio output device events
- Pause after disconnecting headphones
- Transfer playback after connecting headphones
Expand Down
4 changes: 4 additions & 0 deletions psst-core/src/player/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ impl Player {
PlayerCommand::Seek { position } => self.seek(position),
PlayerCommand::Configure { config } => self.configure(config),
PlayerCommand::SetQueueBehavior { behavior } => self.queue.set_behaviour(behavior),
PlayerCommand::AddToQueue { item } => self.queue.add(item),
PlayerCommand::SetVolume { volume } => self.set_volume(volume),
}
}
Expand Down Expand Up @@ -422,6 +423,9 @@ pub enum PlayerCommand {
SetQueueBehavior {
behavior: QueueBehavior,
},
AddToQueue {
item: PlaybackItem,
},
/// Change playback volume to a value in 0.0..=1.0 range.
SetVolume {
volume: f64,
Expand Down
32 changes: 30 additions & 2 deletions psst-core/src/player/queue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ impl Default for QueueBehavior {

pub struct Queue {
items: Vec<PlaybackItem>,
user_items: Vec<PlaybackItem>,
position: usize,
user_items_position: usize,
positions: Vec<usize>,
behavior: QueueBehavior,
}
Expand All @@ -27,7 +29,9 @@ impl Queue {
pub fn new() -> Self {
Self {
items: Vec::new(),
user_items: Vec::new(),
position: 0,
user_items_position: 0,
positions: Vec::new(),
behavior: QueueBehavior::default(),
}
Expand All @@ -46,6 +50,22 @@ impl Queue {
self.compute_positions();
}

pub fn add(&mut self, item: PlaybackItem) {
self.user_items.push(item);
}

fn handle_added_queue(&mut self) {
if self.user_items.len() > self.user_items_position {
self.items.insert(
self.positions.len(),
self.user_items[self.user_items_position],
);
self.positions
.insert(self.position + 1, self.positions.len());
self.user_items_position += 1;
}
}

pub fn set_behaviour(&mut self, behavior: QueueBehavior) {
self.behavior = behavior;
self.compute_positions();
Expand Down Expand Up @@ -81,10 +101,12 @@ impl Queue {
}

pub fn skip_to_next(&mut self) {
self.handle_added_queue();
self.position = self.next_position();
}

pub fn skip_to_following(&mut self) {
self.handle_added_queue();
self.position = self.following_position();
}

Expand All @@ -94,8 +116,14 @@ impl Queue {
}

pub fn get_following(&self) -> Option<&PlaybackItem> {
let position = self.positions.get(self.following_position()).copied()?;
self.items.get(position)
if let Some(position) = self.positions.get(self.position).copied() {
if let Some(item) = self.items.get(position) {
return Some(item);
}
} else {
return self.user_items.first();
}
None
}

fn previous_position(&self) -> usize {
Expand Down
5 changes: 3 additions & 2 deletions psst-gui/src/cmd.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use std::time::Duration;

use druid::{Selector, WidgetId};
use psst_core::item_id::ItemId;
use psst_core::{item_id::ItemId, player::item::PlaybackItem};

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

Expand Down Expand Up @@ -56,6 +56,7 @@ pub const PLAY_PAUSE: Selector = Selector::new("app.play-pause");
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_SEEK: Selector<f64> = Selector::new("app.play-seek");

Expand Down
14 changes: 14 additions & 0 deletions psst-gui/src/controller/playback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,12 @@ impl PlaybackController {
self.send(PlayerEvent::Command(PlayerCommand::SetVolume { volume }));
}

fn add_to_queue(&mut self, item: &PlaybackItem) {
self.send(PlayerEvent::Command(PlayerCommand::AddToQueue {
item: *item,
}));
}

fn set_queue_behavior(&mut self, behavior: QueueBehavior) {
self.send(PlayerEvent::Command(PlayerCommand::SetQueueBehavior {
behavior: match behavior {
Expand Down Expand Up @@ -406,6 +412,14 @@ where
self.stop();
ctx.set_handled();
}
Event::Command(cmd) if cmd.is(cmd::ADD_TO_QUEUE) => {
log::info!("adding to queue");
let (entry, item) = cmd.get_unchecked(cmd::ADD_TO_QUEUE);

self.add_to_queue(item);
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());
Expand Down
21 changes: 20 additions & 1 deletion psst-gui/src/data/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ pub struct AppState {
pub personalized: Personalized,
pub alerts: Vector<Alert>,
pub finder: Finder,
pub added_queue: Vector<QueueEntry>,
}

impl AppState {
Expand Down Expand Up @@ -118,6 +119,7 @@ impl AppState {
cache_size: Promise::Empty,
},
playback,
added_queue: Vector::new(),
search: Search {
input: "".into(),
results: Promise::Empty,
Expand Down Expand Up @@ -180,11 +182,28 @@ impl AppState {

impl AppState {
pub fn queued_entry(&self, item_id: ItemId) -> Option<QueueEntry> {
self.playback
if let Some(queued) = self
.playback
.queue
.iter()
.find(|queued| queued.item.id() == item_id)
.cloned()
{
Some(queued)
} else if let Some(queued) = self
.added_queue
.iter()
.find(|queued| queued.item.id() == item_id)
.cloned()
{
return Some(queued);
} else {
None
}
}

pub fn add_queued_entry(&mut self, queue_entry: QueueEntry) {
self.added_queue.push_back(queue_entry);
}

pub fn loading_playback(&mut self, item: Playable, origin: PlaybackOrigin) {
Expand Down
24 changes: 23 additions & 1 deletion psst-gui/src/ui/track.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,16 @@ use druid::{
widget::{CrossAxisAlignment, Either, Flex, Label, ViewSwitcher},
LensExt, LocalizedString, Menu, MenuItem, Size, TextAlignment, Widget, WidgetExt,
};
use psst_core::{
audio::normalize::NormalizationLevel,
item_id::{ItemId, ItemIdType},
player::item::PlaybackItem,
};

use crate::{
cmd,
data::{
AppState, Library, Nav, PlaybackOrigin, PlaylistAddTrack, PlaylistRemoveTrack,
AppState, Library, Nav, PlaybackOrigin, PlaylistAddTrack, PlaylistRemoveTrack, QueueEntry,
RecommendationsRequest, Track,
},
ui::playlist,
Expand Down Expand Up @@ -323,6 +328,23 @@ pub fn track_menu(
}
}

menu = menu.entry(
MenuItem::new(
LocalizedString::new("menu-item-add-to-queue").with_placeholder("Add Track To Queue"),
)
//PlayerCommand
.command(cmd::ADD_TO_QUEUE.with((
QueueEntry {
item: crate::ui::Playable::Track(track.clone()),
origin: origin.clone(),
},
PlaybackItem {
item_id: ItemId::from_base62(&String::from(track.id), ItemIdType::Track).unwrap(),
norm_level: NormalizationLevel::Track,
},
))),
);

let mut playlist_menu = Menu::new(
LocalizedString::new("menu-item-add-to-playlist").with_placeholder("Add to Playlist"),
);
Expand Down
Loading