mirror of
https://github.com/Dummi26/musicdb.git
synced 2025-03-10 05:43:53 +01:00
add reshuffling and update queue ui in client
This commit is contained in:
parent
f1dd980b52
commit
b8e729d81c
@ -59,7 +59,7 @@ impl QueueViewer {
|
|||||||
QueueContent::Folder(musicdb_lib::data::queue::QueueFolder {
|
QueueContent::Folder(musicdb_lib::data::queue::QueueFolder {
|
||||||
index: 0,
|
index: 0,
|
||||||
content: vec![],
|
content: vec![],
|
||||||
name: "in loop".to_string(),
|
name: String::new(),
|
||||||
order: None,
|
order: None,
|
||||||
})
|
})
|
||||||
.into(),
|
.into(),
|
||||||
@ -79,7 +79,7 @@ impl QueueViewer {
|
|||||||
QueueContent::Folder(musicdb_lib::data::queue::QueueFolder {
|
QueueContent::Folder(musicdb_lib::data::queue::QueueFolder {
|
||||||
index: 0,
|
index: 0,
|
||||||
content: vec![],
|
content: vec![],
|
||||||
name: "in loop".to_string(),
|
name: String::new(),
|
||||||
order: None,
|
order: None,
|
||||||
})
|
})
|
||||||
.into(),
|
.into(),
|
||||||
@ -255,7 +255,7 @@ impl GuiElem for QueueViewer {
|
|||||||
&mut h,
|
&mut h,
|
||||||
vec![],
|
vec![],
|
||||||
true,
|
true,
|
||||||
true,
|
false,
|
||||||
);
|
);
|
||||||
let scroll_box = &mut self.c_scroll_box;
|
let scroll_box = &mut self.c_scroll_box;
|
||||||
scroll_box.children = c;
|
scroll_box.children = c;
|
||||||
@ -279,20 +279,32 @@ fn queue_gui(
|
|||||||
target_h: &mut Vec<f32>,
|
target_h: &mut Vec<f32>,
|
||||||
path: Vec<usize>,
|
path: Vec<usize>,
|
||||||
current: bool,
|
current: bool,
|
||||||
skip_folder: bool,
|
skip_first: bool,
|
||||||
) {
|
) -> Option<Box<dyn GuiElem>> {
|
||||||
|
let mut out = None;
|
||||||
|
let mut push = |target: &mut Vec<_>, e| {
|
||||||
|
if skip_first && out.is_none() {
|
||||||
|
out = Some(e);
|
||||||
|
} else {
|
||||||
|
target.push(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let is_root = path.is_empty();
|
||||||
let cfg = GuiElemCfg::at(Rectangle::from_tuples((depth, 0.0), (1.0, 1.0)));
|
let cfg = GuiElemCfg::at(Rectangle::from_tuples((depth, 0.0), (1.0, 1.0)));
|
||||||
match queue.content() {
|
match queue.content() {
|
||||||
QueueContent::Song(id) => {
|
QueueContent::Song(id) => {
|
||||||
if let Some(s) = db.songs().get(id) {
|
if let Some(s) = db.songs().get(id) {
|
||||||
target.push(Box::new(QueueSong::new(
|
push(
|
||||||
|
target,
|
||||||
|
Box::new(QueueSong::new(
|
||||||
cfg,
|
cfg,
|
||||||
path,
|
path,
|
||||||
s.clone(),
|
s.clone(),
|
||||||
current,
|
current,
|
||||||
db,
|
db,
|
||||||
depth_inc_by * 0.33,
|
depth_inc_by * 0.33,
|
||||||
)));
|
)),
|
||||||
|
);
|
||||||
target_h.push(line_height * 1.75);
|
target_h.push(line_height * 1.75);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -303,15 +315,12 @@ fn queue_gui(
|
|||||||
name: _,
|
name: _,
|
||||||
order: _,
|
order: _,
|
||||||
} = qf;
|
} = qf;
|
||||||
if !skip_folder {
|
let mut folder = QueueFolder::new(cfg.clone(), path.clone(), qf.clone(), current);
|
||||||
target.push(Box::new(QueueFolder::new(
|
if skip_first || is_root {
|
||||||
cfg.clone(),
|
folder.no_ins_before = true;
|
||||||
path.clone(),
|
|
||||||
qf.clone(),
|
|
||||||
current,
|
|
||||||
)));
|
|
||||||
target_h.push(line_height * 0.8);
|
|
||||||
}
|
}
|
||||||
|
push(target, Box::new(folder));
|
||||||
|
target_h.push(line_height * 0.8);
|
||||||
for (i, q) in qf.iter().enumerate() {
|
for (i, q) in qf.iter().enumerate() {
|
||||||
let mut p = path.clone();
|
let mut p = path.clone();
|
||||||
p.push(i);
|
p.push(i);
|
||||||
@ -328,26 +337,22 @@ fn queue_gui(
|
|||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if !skip_folder {
|
if !is_root {
|
||||||
let mut p1 = path;
|
let mut p1 = path;
|
||||||
let p2 = p1.pop().unwrap_or(0) + 1;
|
let p2 = p1.pop().unwrap_or(0) + 1;
|
||||||
target.push(Box::new(QueueIndentEnd::new(cfg, (p1, p2))));
|
push(target, Box::new(QueueIndentEnd::new(cfg, (p1, p2))));
|
||||||
target_h.push(line_height * 0.4);
|
target_h.push(line_height * 0.4);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
QueueContent::Loop(_, _, inner) => {
|
QueueContent::Loop(_, _, inner) => {
|
||||||
let mut p = path.clone();
|
let mut p = path.clone();
|
||||||
p.push(0);
|
p.push(0);
|
||||||
let mut p1 = path.clone();
|
let i = target.len();
|
||||||
let p2 = p1.pop().unwrap_or(0) + 1;
|
push(
|
||||||
target.push(Box::new(QueueLoop::new(
|
target,
|
||||||
cfg.clone(),
|
Box::new(QueueLoop::new(cfg.clone(), path, queue.clone(), current)),
|
||||||
path,
|
);
|
||||||
queue.clone(),
|
if let Some(mut inner) = queue_gui(
|
||||||
current,
|
|
||||||
)));
|
|
||||||
target_h.push(line_height * 0.8);
|
|
||||||
queue_gui(
|
|
||||||
&inner,
|
&inner,
|
||||||
db,
|
db,
|
||||||
depth,
|
depth,
|
||||||
@ -358,12 +363,18 @@ fn queue_gui(
|
|||||||
p,
|
p,
|
||||||
current,
|
current,
|
||||||
true,
|
true,
|
||||||
);
|
) {
|
||||||
target.push(Box::new(QueueIndentEnd::new(cfg, (p1, p2))));
|
inner.config_mut().pos = Rectangle::from_tuples((0.5, 0.0), (1.0, 1.0));
|
||||||
target_h.push(line_height * 0.4);
|
target[i]
|
||||||
|
.any_mut()
|
||||||
|
.downcast_mut::<QueueLoop>()
|
||||||
|
.unwrap()
|
||||||
|
.inner = Some(inner);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
out
|
||||||
|
}
|
||||||
|
|
||||||
struct QueueEmptySpaceDragHandler {
|
struct QueueEmptySpaceDragHandler {
|
||||||
config: GuiElemCfg,
|
config: GuiElemCfg,
|
||||||
@ -658,6 +669,7 @@ struct QueueFolder {
|
|||||||
queue: musicdb_lib::data::queue::QueueFolder,
|
queue: musicdb_lib::data::queue::QueueFolder,
|
||||||
current: bool,
|
current: bool,
|
||||||
insert_into: bool,
|
insert_into: bool,
|
||||||
|
no_ins_before: bool,
|
||||||
mouse: bool,
|
mouse: bool,
|
||||||
mouse_pos: Vec2,
|
mouse_pos: Vec2,
|
||||||
copy: bool,
|
copy: bool,
|
||||||
@ -678,12 +690,7 @@ impl QueueFolder {
|
|||||||
order,
|
order,
|
||||||
} = &queue;
|
} = &queue;
|
||||||
Self {
|
Self {
|
||||||
config: if path.is_empty() {
|
config: config.w_mouse().w_keyboard_watch().w_drag_target(),
|
||||||
config
|
|
||||||
} else {
|
|
||||||
config.w_mouse().w_keyboard_watch()
|
|
||||||
}
|
|
||||||
.w_drag_target(),
|
|
||||||
c_name: Label::new(
|
c_name: Label::new(
|
||||||
GuiElemCfg::default(),
|
GuiElemCfg::default(),
|
||||||
format!(
|
format!(
|
||||||
@ -704,6 +711,7 @@ impl QueueFolder {
|
|||||||
queue,
|
queue,
|
||||||
current,
|
current,
|
||||||
insert_into: false,
|
insert_into: false,
|
||||||
|
no_ins_before: false,
|
||||||
mouse: false,
|
mouse: false,
|
||||||
mouse_pos: Vec2::ZERO,
|
mouse_pos: Vec2::ZERO,
|
||||||
copy: false,
|
copy: false,
|
||||||
@ -741,7 +749,8 @@ impl GuiElem for QueueFolder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
fn draw(&mut self, info: &mut DrawInfo, g: &mut speedy2d::Graphics2D) {
|
fn draw(&mut self, info: &mut DrawInfo, g: &mut speedy2d::Graphics2D) {
|
||||||
self.insert_into = info.mouse_pos.y > info.pos.top_left().y + info.pos.height() * 0.5;
|
self.insert_into = self.no_ins_before
|
||||||
|
|| info.mouse_pos.y > info.pos.top_left().y + info.pos.height() * 0.5;
|
||||||
if !self.always_copy && info.dragging.is_some() && info.pos.contains(info.mouse_pos) {
|
if !self.always_copy && info.dragging.is_some() && info.pos.contains(info.mouse_pos) {
|
||||||
g.draw_rectangle(
|
g.draw_rectangle(
|
||||||
if self.insert_into {
|
if self.insert_into {
|
||||||
@ -789,7 +798,7 @@ impl GuiElem for QueueFolder {
|
|||||||
return vec![GuiAction::SendToServer(if self.queue.order.is_some() {
|
return vec![GuiAction::SendToServer(if self.queue.order.is_some() {
|
||||||
Action::QueueUnshuffle(self.path.clone())
|
Action::QueueUnshuffle(self.path.clone())
|
||||||
} else {
|
} else {
|
||||||
Action::QueueShuffle(self.path.clone())
|
Action::QueueShuffle(self.path.clone(), 1)
|
||||||
})];
|
})];
|
||||||
}
|
}
|
||||||
vec![]
|
vec![]
|
||||||
@ -816,7 +825,7 @@ impl GuiElem for QueueFolder {
|
|||||||
_key: Option<VirtualKeyCode>,
|
_key: Option<VirtualKeyCode>,
|
||||||
_scan: speedy2d::window::KeyScancode,
|
_scan: speedy2d::window::KeyScancode,
|
||||||
) -> Vec<GuiAction> {
|
) -> Vec<GuiAction> {
|
||||||
self.copy = modifiers.ctrl();
|
self.copy = self.always_copy || modifiers.ctrl();
|
||||||
vec![]
|
vec![]
|
||||||
}
|
}
|
||||||
fn dragged(&mut self, e: &mut EventInfo, dragged: Dragging) -> Vec<GuiAction> {
|
fn dragged(&mut self, e: &mut EventInfo, dragged: Dragging) -> Vec<GuiAction> {
|
||||||
@ -923,6 +932,7 @@ struct QueueLoop {
|
|||||||
copy: bool,
|
copy: bool,
|
||||||
always_copy: bool,
|
always_copy: bool,
|
||||||
copy_on_mouse_down: bool,
|
copy_on_mouse_down: bool,
|
||||||
|
inner: Option<Box<dyn GuiElem>>,
|
||||||
}
|
}
|
||||||
impl QueueLoop {
|
impl QueueLoop {
|
||||||
pub fn new(config: GuiElemCfg, path: Vec<usize>, queue: Queue, current: bool) -> Self {
|
pub fn new(config: GuiElemCfg, path: Vec<usize>, queue: Queue, current: bool) -> Self {
|
||||||
@ -948,6 +958,7 @@ impl QueueLoop {
|
|||||||
copy: false,
|
copy: false,
|
||||||
always_copy: false,
|
always_copy: false,
|
||||||
copy_on_mouse_down: false,
|
copy_on_mouse_down: false,
|
||||||
|
inner: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn alwayscopy(mut self) -> Self {
|
fn alwayscopy(mut self) -> Self {
|
||||||
@ -979,8 +990,16 @@ impl GuiElem for QueueLoop {
|
|||||||
&mut self.config
|
&mut self.config
|
||||||
}
|
}
|
||||||
fn children(&mut self) -> Box<dyn Iterator<Item = &mut dyn GuiElem> + '_> {
|
fn children(&mut self) -> Box<dyn Iterator<Item = &mut dyn GuiElem> + '_> {
|
||||||
|
if let Some(inner) = &mut self.inner {
|
||||||
|
Box::new(
|
||||||
|
[inner.elem_mut()]
|
||||||
|
.into_iter()
|
||||||
|
.chain(self.children.iter_mut().map(|v| v.elem_mut())),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
Box::new(self.children.iter_mut().map(|v| v.elem_mut()))
|
Box::new(self.children.iter_mut().map(|v| v.elem_mut()))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
fn any(&self) -> &dyn std::any::Any {
|
fn any(&self) -> &dyn std::any::Any {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@ -1018,6 +1037,14 @@ impl GuiElem for QueueLoop {
|
|||||||
info.mouse_pos.y - self.config.pixel_pos.top_left().y,
|
info.mouse_pos.y - self.config.pixel_pos.top_left().y,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
let pos = Rectangle::new(
|
||||||
|
*info.pos.top_left(),
|
||||||
|
Vec2::new(
|
||||||
|
(info.pos.top_left().x + info.pos.bottom_right().x) / 2.0,
|
||||||
|
info.pos.bottom_right().y,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
let ppos = std::mem::replace(&mut info.pos, pos);
|
||||||
generic_queue_draw(
|
generic_queue_draw(
|
||||||
info,
|
info,
|
||||||
&self.path,
|
&self.path,
|
||||||
@ -1025,6 +1052,7 @@ impl GuiElem for QueueLoop {
|
|||||||
&mut self.mouse,
|
&mut self.mouse,
|
||||||
self.copy_on_mouse_down,
|
self.copy_on_mouse_down,
|
||||||
);
|
);
|
||||||
|
info.pos = ppos;
|
||||||
}
|
}
|
||||||
fn mouse_down(&mut self, e: &mut EventInfo, button: MouseButton) -> Vec<GuiAction> {
|
fn mouse_down(&mut self, e: &mut EventInfo, button: MouseButton) -> Vec<GuiAction> {
|
||||||
if button == MouseButton::Left && e.take() {
|
if button == MouseButton::Left && e.take() {
|
||||||
|
@ -89,7 +89,7 @@ impl CacheManager {
|
|||||||
let mut queue = db.queue.clone();
|
let mut queue = db.queue.clone();
|
||||||
|
|
||||||
let queue_current_song = queue.get_current_song().copied();
|
let queue_current_song = queue.get_current_song().copied();
|
||||||
queue.advance_index_inner();
|
queue.advance_index_inner(&mut Vec::new(), &mut Vec::new());
|
||||||
let queue_next_song = queue.get_current_song().copied();
|
let queue_next_song = queue.get_current_song().copied();
|
||||||
|
|
||||||
let mut ids_to_cache = queue_current_song
|
let mut ids_to_cache = queue_current_song
|
||||||
@ -98,7 +98,7 @@ impl CacheManager {
|
|||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
for _ in 2..songs_to_cache {
|
for _ in 2..songs_to_cache {
|
||||||
queue.advance_index_inner();
|
queue.advance_index_inner(&mut Vec::new(), &mut Vec::new());
|
||||||
if let Some(id) = queue.get_current_song() {
|
if let Some(id) = queue.get_current_song() {
|
||||||
if !ids_to_cache.contains(id) {
|
if !ids_to_cache.contains(id) {
|
||||||
ids_to_cache.push(*id);
|
ids_to_cache.push(*id);
|
||||||
|
@ -569,7 +569,7 @@ impl Database {
|
|||||||
// some commands shouldn't be broadcast. these will broadcast a different command in their specific implementation.
|
// some commands shouldn't be broadcast. these will broadcast a different command in their specific implementation.
|
||||||
match &action {
|
match &action {
|
||||||
// Will broadcast `QueueSetShuffle`
|
// Will broadcast `QueueSetShuffle`
|
||||||
Action::QueueShuffle(_) => (),
|
Action::QueueShuffle(_, _) => (),
|
||||||
Action::NextSong if self.queue.is_almost_empty() => (),
|
Action::NextSong if self.queue.is_almost_empty() => (),
|
||||||
Action::Pause if !self.playing => (),
|
Action::Pause if !self.playing => (),
|
||||||
Action::Resume if self.playing => (),
|
Action::Resume if self.playing => (),
|
||||||
@ -679,7 +679,8 @@ impl Database {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Action::QueueGoto(index) => Queue::set_index_db(self, &index),
|
Action::QueueGoto(index) => Queue::set_index_db(self, &index),
|
||||||
Action::QueueShuffle(path) => {
|
Action::QueueShuffle(path, set_index) => {
|
||||||
|
if !self.is_client() {
|
||||||
if let Some(elem) = self.queue.get_item_at_index_mut(&path, 0) {
|
if let Some(elem) = self.queue.get_item_at_index_mut(&path, 0) {
|
||||||
if let QueueContent::Folder(QueueFolder {
|
if let QueueContent::Folder(QueueFolder {
|
||||||
index: _,
|
index: _,
|
||||||
@ -690,7 +691,10 @@ impl Database {
|
|||||||
{
|
{
|
||||||
let mut ord: Vec<usize> = (0..content.len()).collect();
|
let mut ord: Vec<usize> = (0..content.len()).collect();
|
||||||
ord.shuffle(&mut thread_rng());
|
ord.shuffle(&mut thread_rng());
|
||||||
self.apply_action_unchecked_seq(Action::QueueSetShuffle(path, ord), client);
|
self.apply_action_unchecked_seq(
|
||||||
|
Action::QueueSetShuffle(path, ord, set_index),
|
||||||
|
client,
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
eprintln!("(QueueShuffle) QueueElement at {path:?} not a folder!");
|
eprintln!("(QueueShuffle) QueueElement at {path:?} not a folder!");
|
||||||
}
|
}
|
||||||
@ -698,7 +702,8 @@ impl Database {
|
|||||||
eprintln!("(QueueShuffle) No QueueElement at {path:?}");
|
eprintln!("(QueueShuffle) No QueueElement at {path:?}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Action::QueueSetShuffle(path, ord) => {
|
}
|
||||||
|
Action::QueueSetShuffle(path, ord, set_index) => {
|
||||||
if let Some(elem) = self.queue.get_item_at_index_mut(&path, 0) {
|
if let Some(elem) = self.queue.get_item_at_index_mut(&path, 0) {
|
||||||
if let QueueContent::Folder(QueueFolder {
|
if let QueueContent::Folder(QueueFolder {
|
||||||
index,
|
index,
|
||||||
@ -708,9 +713,15 @@ impl Database {
|
|||||||
}) = elem.content_mut()
|
}) = elem.content_mut()
|
||||||
{
|
{
|
||||||
if ord.len() == content.len() {
|
if ord.len() == content.len() {
|
||||||
|
match set_index {
|
||||||
|
0 => {}
|
||||||
|
1 => {
|
||||||
if let Some(ni) = ord.iter().position(|v| *v == *index) {
|
if let Some(ni) = ord.iter().position(|v| *v == *index) {
|
||||||
*index = ni;
|
*index = ni;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
*order = Some(ord);
|
*order = Some(ord);
|
||||||
} else {
|
} else {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use std::ops::AddAssign;
|
use std::ops::AddAssign;
|
||||||
|
|
||||||
use crate::load::ToFromBytes;
|
use crate::{load::ToFromBytes, server::Action};
|
||||||
|
|
||||||
use super::{database::Database, SongId};
|
use super::{database::Database, SongId};
|
||||||
|
|
||||||
@ -210,7 +210,11 @@ impl Queue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn advance_index_db(db: &mut Database) -> bool {
|
pub fn advance_index_db(db: &mut Database) -> bool {
|
||||||
let o = db.queue.advance_index_inner();
|
let mut actions = Vec::new();
|
||||||
|
let o = db.queue.advance_index_inner(&mut Vec::new(), &mut actions);
|
||||||
|
for action in actions {
|
||||||
|
db.apply_action_unchecked_seq(action, None);
|
||||||
|
}
|
||||||
o
|
o
|
||||||
}
|
}
|
||||||
pub fn init(&mut self) {
|
pub fn init(&mut self) {
|
||||||
@ -225,20 +229,50 @@ impl Queue {
|
|||||||
QueueContent::Loop(_, _, inner) => inner.init(),
|
QueueContent::Loop(_, _, inner) => inner.init(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn advance_index_inner(&mut self) -> bool {
|
pub fn done(&mut self, path: &Vec<usize>, actions: &mut Vec<Action>) {
|
||||||
match &mut self.content {
|
match &mut self.content {
|
||||||
QueueContent::Song(_) => false,
|
QueueContent::Song(..) => {}
|
||||||
QueueContent::Folder(folder) => folder.advance_index_inner(),
|
QueueContent::Folder(folder) => {
|
||||||
QueueContent::Loop(total, current, inner) => {
|
if folder.order.is_some() {
|
||||||
if inner.advance_index_inner() {
|
actions.push(Action::QueueShuffle(path.clone(), 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
QueueContent::Loop(_, _, _) => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn advance_index_inner(
|
||||||
|
&mut self,
|
||||||
|
path: &mut Vec<usize>,
|
||||||
|
actions: &mut Vec<Action>,
|
||||||
|
) -> bool {
|
||||||
|
match &mut self.content {
|
||||||
|
QueueContent::Song(_) => {
|
||||||
|
self.done(path, actions);
|
||||||
|
false
|
||||||
|
}
|
||||||
|
QueueContent::Folder(folder) => {
|
||||||
|
if folder.advance_index_inner(path, actions) {
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
|
self.done(path, actions);
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
QueueContent::Loop(total, current, inner) => {
|
||||||
|
path.push(0);
|
||||||
|
if inner.advance_index_inner(path, actions) {
|
||||||
|
path.pop();
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
inner.done(path, actions);
|
||||||
|
path.pop();
|
||||||
*current += 1;
|
*current += 1;
|
||||||
if *total == 0 || *current < *total {
|
if *total == 0 || *current < *total {
|
||||||
inner.init();
|
inner.init();
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
*current = 0;
|
*current = 0;
|
||||||
|
self.done(path, actions);
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -481,12 +515,20 @@ impl QueueFolder {
|
|||||||
self.content.first()
|
self.content.first()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn advance_index_inner(&mut self) -> bool {
|
pub fn advance_index_inner(
|
||||||
|
&mut self,
|
||||||
|
path: &mut Vec<usize>,
|
||||||
|
actions: &mut Vec<Action>,
|
||||||
|
) -> bool {
|
||||||
|
let index = self.index;
|
||||||
if let Some(c) = self.get_current_mut() {
|
if let Some(c) = self.get_current_mut() {
|
||||||
if c.advance_index_inner() {
|
path.push(index);
|
||||||
|
if c.advance_index_inner(path, actions) {
|
||||||
|
path.pop();
|
||||||
// inner value could advance index, do nothing.
|
// inner value could advance index, do nothing.
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
|
path.pop();
|
||||||
loop {
|
loop {
|
||||||
if self.index + 1 < self.content.len() {
|
if self.index + 1 < self.content.len() {
|
||||||
// can advance
|
// can advance
|
||||||
|
@ -86,8 +86,8 @@ impl Action {
|
|||||||
| Self::QueueMove(_, _)
|
| Self::QueueMove(_, _)
|
||||||
| Self::QueueMoveInto(_, _)
|
| Self::QueueMoveInto(_, _)
|
||||||
| Self::QueueGoto(_)
|
| Self::QueueGoto(_)
|
||||||
| Self::QueueShuffle(_)
|
| Self::QueueShuffle(_, _)
|
||||||
| Self::QueueSetShuffle(_, _)
|
| Self::QueueSetShuffle(_, _, _)
|
||||||
| Self::QueueUnshuffle(_)
|
| Self::QueueUnshuffle(_)
|
||||||
| Self::RemoveSong(_)
|
| Self::RemoveSong(_)
|
||||||
| Self::RemoveAlbum(_)
|
| Self::RemoveAlbum(_)
|
||||||
@ -186,10 +186,12 @@ pub enum Action {
|
|||||||
/// Take an element from A and add it to the end of the folder B
|
/// Take an element from A and add it to the end of the folder B
|
||||||
QueueMoveInto(Vec<usize>, Vec<usize>),
|
QueueMoveInto(Vec<usize>, Vec<usize>),
|
||||||
QueueGoto(Vec<usize>),
|
QueueGoto(Vec<usize>),
|
||||||
// sent by clients when they want to shuffle a folder
|
/// sent by clients when they want to shuffle a folder
|
||||||
QueueShuffle(Vec<usize>),
|
/// last parameter: 0 = don't change index, 1 = set folder index to new index of previously active element
|
||||||
// sent by the server when the folder was shuffled
|
QueueShuffle(Vec<usize>, u8),
|
||||||
QueueSetShuffle(Vec<usize>, Vec<usize>),
|
/// sent by the server when the folder was shuffled
|
||||||
|
/// last parameter, see QueueShuffle
|
||||||
|
QueueSetShuffle(Vec<usize>, Vec<usize>, u8),
|
||||||
QueueUnshuffle(Vec<usize>),
|
QueueUnshuffle(Vec<usize>),
|
||||||
|
|
||||||
/// .id field is ignored!
|
/// .id field is ignored!
|
||||||
@ -615,16 +617,18 @@ impl ToFromBytes for Action {
|
|||||||
s.write_all(&[BYTE_QUEUE_GOTO])?;
|
s.write_all(&[BYTE_QUEUE_GOTO])?;
|
||||||
index.to_bytes(s)?;
|
index.to_bytes(s)?;
|
||||||
}
|
}
|
||||||
Self::QueueShuffle(path) => {
|
Self::QueueShuffle(path, set_index) => {
|
||||||
s.write_all(&[BYTE_QUEUE_ACTION])?;
|
s.write_all(&[BYTE_QUEUE_ACTION])?;
|
||||||
s.write_all(&[SUBBYTE_ACTION_SHUFFLE])?;
|
s.write_all(&[SUBBYTE_ACTION_SHUFFLE])?;
|
||||||
path.to_bytes(s)?;
|
path.to_bytes(s)?;
|
||||||
|
set_index.to_bytes(s)?;
|
||||||
}
|
}
|
||||||
Self::QueueSetShuffle(path, map) => {
|
Self::QueueSetShuffle(path, map, set_index) => {
|
||||||
s.write_all(&[BYTE_QUEUE_ACTION])?;
|
s.write_all(&[BYTE_QUEUE_ACTION])?;
|
||||||
s.write_all(&[SUBBYTE_ACTION_SET_SHUFFLE])?;
|
s.write_all(&[SUBBYTE_ACTION_SET_SHUFFLE])?;
|
||||||
path.to_bytes(s)?;
|
path.to_bytes(s)?;
|
||||||
map.to_bytes(s)?;
|
map.to_bytes(s)?;
|
||||||
|
set_index.to_bytes(s)?;
|
||||||
}
|
}
|
||||||
Self::QueueUnshuffle(path) => {
|
Self::QueueUnshuffle(path) => {
|
||||||
s.write_all(&[BYTE_QUEUE_ACTION])?;
|
s.write_all(&[BYTE_QUEUE_ACTION])?;
|
||||||
@ -813,8 +817,10 @@ impl ToFromBytes for Action {
|
|||||||
BYTE_QUEUE_MOVE_INTO => Self::QueueMoveInto(from_bytes!(), from_bytes!()),
|
BYTE_QUEUE_MOVE_INTO => Self::QueueMoveInto(from_bytes!(), from_bytes!()),
|
||||||
BYTE_QUEUE_GOTO => Self::QueueGoto(from_bytes!()),
|
BYTE_QUEUE_GOTO => Self::QueueGoto(from_bytes!()),
|
||||||
BYTE_QUEUE_ACTION => match s.read_byte()? {
|
BYTE_QUEUE_ACTION => match s.read_byte()? {
|
||||||
SUBBYTE_ACTION_SHUFFLE => Self::QueueShuffle(from_bytes!()),
|
SUBBYTE_ACTION_SHUFFLE => Self::QueueShuffle(from_bytes!(), from_bytes!()),
|
||||||
SUBBYTE_ACTION_SET_SHUFFLE => Self::QueueSetShuffle(from_bytes!(), from_bytes!()),
|
SUBBYTE_ACTION_SET_SHUFFLE => {
|
||||||
|
Self::QueueSetShuffle(from_bytes!(), from_bytes!(), from_bytes!())
|
||||||
|
}
|
||||||
SUBBYTE_ACTION_UNSHUFFLE => Self::QueueUnshuffle(from_bytes!()),
|
SUBBYTE_ACTION_UNSHUFFLE => Self::QueueUnshuffle(from_bytes!()),
|
||||||
_ => {
|
_ => {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
@ -943,8 +949,8 @@ fn test_to_from_bytes() {
|
|||||||
Action::QueueMove(vec![], vec![]),
|
Action::QueueMove(vec![], vec![]),
|
||||||
Action::QueueMoveInto(vec![], vec![]),
|
Action::QueueMoveInto(vec![], vec![]),
|
||||||
Action::QueueGoto(vec![]),
|
Action::QueueGoto(vec![]),
|
||||||
Action::QueueShuffle(vec![]),
|
Action::QueueShuffle(vec![], 1),
|
||||||
Action::QueueSetShuffle(vec![], vec![]),
|
Action::QueueSetShuffle(vec![], vec![], 0),
|
||||||
Action::QueueUnshuffle(vec![]),
|
Action::QueueUnshuffle(vec![]),
|
||||||
// Action::AddSong(Song, Req),
|
// Action::AddSong(Song, Req),
|
||||||
// Action::AddAlbum(Album, Req),
|
// Action::AddAlbum(Album, Req),
|
||||||
|
@ -148,7 +148,7 @@ fn main() {
|
|||||||
let mut con = TcpStream::connect(addr).unwrap();
|
let mut con = TcpStream::connect(addr).unwrap();
|
||||||
writeln!(con, "main").unwrap();
|
writeln!(con, "main").unwrap();
|
||||||
loop {
|
loop {
|
||||||
let mut cmd = musicdb_lib::server::Command::from_bytes(&mut con).unwrap();
|
let cmd = musicdb_lib::server::Command::from_bytes(&mut con).unwrap();
|
||||||
use musicdb_lib::server::Action::{self, *};
|
use musicdb_lib::server::Action::{self, *};
|
||||||
fn sanitize_actions(action: Action) -> Option<Action> {
|
fn sanitize_actions(action: Action) -> Option<Action> {
|
||||||
match action {
|
match action {
|
||||||
|
@ -9,10 +9,8 @@ use musicdb_lib::data::song::Song;
|
|||||||
use musicdb_lib::data::SongId;
|
use musicdb_lib::data::SongId;
|
||||||
use musicdb_lib::server::{Action, Command, Req};
|
use musicdb_lib::server::{Action, Command, Req};
|
||||||
use rocket::futures::{SinkExt, StreamExt};
|
use rocket::futures::{SinkExt, StreamExt};
|
||||||
use rocket::http::ContentType;
|
|
||||||
use rocket::response::content::RawHtml;
|
use rocket::response::content::RawHtml;
|
||||||
use rocket::response::Responder;
|
use rocket::{get, routes, Config, State};
|
||||||
use rocket::{get, routes, Config, Response, State};
|
|
||||||
use rocket_seek_stream::SeekStream;
|
use rocket_seek_stream::SeekStream;
|
||||||
use rocket_ws::{Message, WebSocket};
|
use rocket_ws::{Message, WebSocket};
|
||||||
use tokio::select;
|
use tokio::select;
|
||||||
@ -53,7 +51,6 @@ struct Data {
|
|||||||
|
|
||||||
#[get("/")]
|
#[get("/")]
|
||||||
fn index(data: &State<Data>) -> RawHtml<String> {
|
fn index(data: &State<Data>) -> RawHtml<String> {
|
||||||
dbg!(());
|
|
||||||
let script = r#"<script>
|
let script = r#"<script>
|
||||||
const sleep = ms => new Promise(r => setTimeout(r, ms));
|
const sleep = ms => new Promise(r => setTimeout(r, ms));
|
||||||
async function performSearch() {
|
async function performSearch() {
|
||||||
@ -90,7 +87,8 @@ async function performSearch() {
|
|||||||
async function addSong(id) {
|
async function addSong(id) {
|
||||||
await fetch("/add-song/" + id);
|
await fetch("/add-song/" + id);
|
||||||
}
|
}
|
||||||
</script>"#;
|
</script>
|
||||||
|
"#;
|
||||||
let script2 = r#"<script>
|
let script2 = r#"<script>
|
||||||
const searchDiv = document.getElementById("searchDiv");
|
const searchDiv = document.getElementById("searchDiv");
|
||||||
searchDiv.style.display = "";
|
searchDiv.style.display = "";
|
||||||
@ -263,7 +261,10 @@ async function runLoop() {
|
|||||||
document.getElementById("warnLag").innerText = "Average update time: " + Math.round(averageLoopTimeMs) + "ms";
|
document.getElementById("warnLag").innerText = "Average update time: " + Math.round(averageLoopTimeMs) + "ms";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
runLoop();</script>"#;
|
updateLivePlaybackIds();
|
||||||
|
runLoop();
|
||||||
|
</script>
|
||||||
|
"#;
|
||||||
let buttons = "<button onclick=\"fetch('/play')\">play</button><button onclick=\"fetch('/pause')\">pause</button><button onclick=\"fetch('/stop')\">stop</button><button onclick=\"fetch('/skip')\">skip</button><button onclick=\"fetch('/clear-queue')\">clear queue</button>";
|
let buttons = "<button onclick=\"fetch('/play')\">play</button><button onclick=\"fetch('/pause')\">pause</button><button onclick=\"fetch('/stop')\">stop</button><button onclick=\"fetch('/skip')\">skip</button><button onclick=\"fetch('/clear-queue')\">clear queue</button>";
|
||||||
let search = "<input id=\"searchFieldArtist\" placeholder=\"artist\"><input id=\"searchFieldAlbum\" placeholder=\"album\"><input id=\"searchFieldTitle\" placeholder=\"title\">
|
let search = "<input id=\"searchFieldArtist\" placeholder=\"artist\"><input id=\"searchFieldAlbum\" placeholder=\"album\"><input id=\"searchFieldTitle\" placeholder=\"title\">
|
||||||
<button onclick=\"performSearch()\">search</button><div id=\"searchResultDiv\"></div>";
|
<button onclick=\"performSearch()\">search</button><div id=\"searchResultDiv\"></div>";
|
||||||
@ -272,7 +273,6 @@ runLoop();</script>"#;
|
|||||||
let now_playing = gen_now_playing(&db);
|
let now_playing = gen_now_playing(&db);
|
||||||
let mut queue = String::new();
|
let mut queue = String::new();
|
||||||
gen_queue_html(&db.queue, &mut queue, &db);
|
gen_queue_html(&db.queue, &mut queue, &db);
|
||||||
dbg!(&queue);
|
|
||||||
drop(db);
|
drop(db);
|
||||||
RawHtml(format!(
|
RawHtml(format!(
|
||||||
"{HTML_START}<title>MusicDb</title>{script}{HTML_SEP}<small><small><div id=\"warnLag\">no javascript? reload to see updated information.</div></small></small><div id=\"nowPlayingDiv\">{now_playing}</div><div>{buttons}</div>{playback_live}<div id=\"searchDiv\" style=\"display:none;\">{search}</div><div id=\"queueDiv\">{queue}</div>{script2}{HTML_END}",
|
"{HTML_START}<title>MusicDb</title>{script}{HTML_SEP}<small><small><div id=\"warnLag\">no javascript? reload to see updated information.</div></small></small><div id=\"nowPlayingDiv\">{now_playing}</div><div>{buttons}</div>{playback_live}<div id=\"searchDiv\" style=\"display:none;\">{search}</div><div id=\"queueDiv\">{queue}</div>{script2}{HTML_END}",
|
||||||
@ -301,8 +301,15 @@ fn now_playing_ids(data: &State<Data>) -> String {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/song/<id>/<name>")]
|
#[get("/song/<id>")]
|
||||||
fn song(data: &State<Data>, id: SongId, name: String) -> Option<SeekStream> {
|
fn song1(data: &State<Data>, id: SongId) -> Option<SeekStream> {
|
||||||
|
song(data, id)
|
||||||
|
}
|
||||||
|
#[get("/song/<id>/<_>")]
|
||||||
|
fn song2(data: &State<Data>, id: SongId) -> Option<SeekStream> {
|
||||||
|
song(data, id)
|
||||||
|
}
|
||||||
|
fn song(data: &State<Data>, id: SongId) -> Option<SeekStream> {
|
||||||
let db = data.db.lock().unwrap();
|
let db = data.db.lock().unwrap();
|
||||||
if let Some(song) = db.get_song(&id) {
|
if let Some(song) = db.get_song(&id) {
|
||||||
song.cached_data().cache_data_start_thread(&*db, song);
|
song.cached_data().cache_data_start_thread(&*db, song);
|
||||||
@ -901,7 +908,7 @@ pub fn main(
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.block_on(async_main(data, addr));
|
.block_on(async_main(data, addr));
|
||||||
}
|
}
|
||||||
pub async fn async_main(data: Data, addr: SocketAddr) {
|
async fn async_main(data: Data, addr: SocketAddr) {
|
||||||
rocket::build()
|
rocket::build()
|
||||||
.configure(Config {
|
.configure(Config {
|
||||||
address: addr.ip(),
|
address: addr.ip(),
|
||||||
@ -925,7 +932,8 @@ pub async fn async_main(data: Data, addr: SocketAddr) {
|
|||||||
search,
|
search,
|
||||||
now_playing_html,
|
now_playing_html,
|
||||||
now_playing_ids,
|
now_playing_ids,
|
||||||
song,
|
song1,
|
||||||
|
song2,
|
||||||
queue_html,
|
queue_html,
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
Loading…
Reference in New Issue
Block a user