add reshuffling and update queue ui in client

This commit is contained in:
Mark 2025-03-01 12:06:45 +01:00
parent f1dd980b52
commit b8e729d81c
7 changed files with 195 additions and 100 deletions

View File

@ -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() {

View File

@ -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);

View File

@ -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!(

View File

@ -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

View File

@ -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),

View File

@ -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 {

View File

@ -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,
], ],
) )