mirror of
https://github.com/Dummi26/musicdb.git
synced 2025-03-10 05:43:53 +01:00
add Multiple action and tool to remove queue-duplicates
This commit is contained in:
parent
a8e18abfd2
commit
b1c0925647
@ -368,116 +368,130 @@ impl Gui {
|
|||||||
db.update_endpoints_id += 1;
|
db.update_endpoints_id += 1;
|
||||||
db.update_endpoints.push((
|
db.update_endpoints.push((
|
||||||
udepid,
|
udepid,
|
||||||
musicdb_lib::data::database::UpdateEndpoint::Custom(Box::new(
|
musicdb_lib::data::database::UpdateEndpoint::Custom(Box::new(move |cmd| {
|
||||||
move |cmd| match &cmd.action {
|
the_impl(&cmd.action, &event_sender_arc, ¬if_sender_two);
|
||||||
Action::Resume
|
fn the_impl(
|
||||||
| Action::Pause
|
action: &Action,
|
||||||
| Action::Stop
|
event_sender_arc: &Arc<Mutex<Option<UserEventSender<GuiEvent>>>>,
|
||||||
| Action::Save
|
notif_sender_two: &Sender<
|
||||||
| Action::InitComplete => {}
|
Box<dyn FnOnce(&NotifOverlay) -> (Box<dyn GuiElem>, NotifInfo) + Send>,
|
||||||
Action::NextSong
|
>,
|
||||||
| Action::QueueUpdate(..)
|
) {
|
||||||
| Action::QueueAdd(..)
|
match action {
|
||||||
| Action::QueueInsert(..)
|
Action::Resume
|
||||||
| Action::QueueRemove(..)
|
| Action::Pause
|
||||||
| Action::QueueMove(..)
|
| Action::Stop
|
||||||
| Action::QueueMoveInto(..)
|
| Action::Save
|
||||||
| Action::QueueGoto(..)
|
| Action::InitComplete => {}
|
||||||
| Action::QueueShuffle(..)
|
Action::NextSong
|
||||||
| Action::QueueSetShuffle(..)
|
| Action::QueueUpdate(..)
|
||||||
| Action::QueueUnshuffle(..) => {
|
| Action::QueueAdd(..)
|
||||||
if let Some(s) = &*event_sender_arc.lock().unwrap() {
|
| Action::QueueInsert(..)
|
||||||
_ = s.send_event(GuiEvent::UpdatedQueue);
|
| Action::QueueRemove(..)
|
||||||
|
| Action::QueueMove(..)
|
||||||
|
| Action::QueueMoveInto(..)
|
||||||
|
| Action::QueueGoto(..)
|
||||||
|
| Action::QueueShuffle(..)
|
||||||
|
| Action::QueueSetShuffle(..)
|
||||||
|
| Action::QueueUnshuffle(..) => {
|
||||||
|
if let Some(s) = &*event_sender_arc.lock().unwrap() {
|
||||||
|
_ = s.send_event(GuiEvent::UpdatedQueue);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
Action::SyncDatabase(..)
|
||||||
Action::SyncDatabase(..)
|
| Action::AddSong(_, _)
|
||||||
| Action::AddSong(_, _)
|
| Action::AddAlbum(_, _)
|
||||||
| Action::AddAlbum(_, _)
|
| Action::AddArtist(_, _)
|
||||||
| Action::AddArtist(_, _)
|
| Action::AddCover(_, _)
|
||||||
| Action::AddCover(_, _)
|
| Action::ModifySong(_, _)
|
||||||
| Action::ModifySong(_, _)
|
| Action::ModifyAlbum(_, _)
|
||||||
| Action::ModifyAlbum(_, _)
|
| Action::ModifyArtist(_, _)
|
||||||
| Action::ModifyArtist(_, _)
|
| Action::RemoveSong(_)
|
||||||
| Action::RemoveSong(_)
|
| Action::RemoveAlbum(_)
|
||||||
| Action::RemoveAlbum(_)
|
| Action::RemoveArtist(_)
|
||||||
| Action::RemoveArtist(_)
|
| Action::TagSongFlagSet(..)
|
||||||
| Action::TagSongFlagSet(..)
|
| Action::TagSongFlagUnset(..)
|
||||||
| Action::TagSongFlagUnset(..)
|
| Action::TagAlbumFlagSet(..)
|
||||||
| Action::TagAlbumFlagSet(..)
|
| Action::TagAlbumFlagUnset(..)
|
||||||
| Action::TagAlbumFlagUnset(..)
|
| Action::TagArtistFlagSet(..)
|
||||||
| Action::TagArtistFlagSet(..)
|
| Action::TagArtistFlagUnset(..)
|
||||||
| Action::TagArtistFlagUnset(..)
|
| Action::TagSongPropertySet(..)
|
||||||
| Action::TagSongPropertySet(..)
|
| Action::TagSongPropertyUnset(..)
|
||||||
| Action::TagSongPropertyUnset(..)
|
| Action::TagAlbumPropertySet(..)
|
||||||
| Action::TagAlbumPropertySet(..)
|
| Action::TagAlbumPropertyUnset(..)
|
||||||
| Action::TagAlbumPropertyUnset(..)
|
| Action::TagArtistPropertySet(..)
|
||||||
| Action::TagArtistPropertySet(..)
|
| Action::TagArtistPropertyUnset(..)
|
||||||
| Action::TagArtistPropertyUnset(..)
|
| Action::SetSongDuration(..) => {
|
||||||
| Action::SetSongDuration(..) => {
|
if let Some(s) = &*event_sender_arc.lock().unwrap() {
|
||||||
if let Some(s) = &*event_sender_arc.lock().unwrap() {
|
_ = s.send_event(GuiEvent::UpdatedLibrary);
|
||||||
_ = s.send_event(GuiEvent::UpdatedLibrary);
|
}
|
||||||
}
|
}
|
||||||
}
|
Action::Multiple(actions) => {
|
||||||
Action::ErrorInfo(t, d) => {
|
for action in actions {
|
||||||
let (t, d) = (t.clone(), d.clone());
|
the_impl(action, event_sender_arc, notif_sender_two);
|
||||||
notif_sender_two
|
}
|
||||||
.send(Box::new(move |_| {
|
}
|
||||||
(
|
Action::ErrorInfo(t, d) => {
|
||||||
Box::new(Panel::with_background(
|
let (t, d) = (t.clone(), d.clone());
|
||||||
GuiElemCfg::default(),
|
notif_sender_two
|
||||||
[Label::new(
|
.send(Box::new(move |_| {
|
||||||
|
(
|
||||||
|
Box::new(Panel::with_background(
|
||||||
GuiElemCfg::default(),
|
GuiElemCfg::default(),
|
||||||
if t.is_empty() {
|
[Label::new(
|
||||||
format!("Server message\n{d}")
|
GuiElemCfg::default(),
|
||||||
} else {
|
if t.is_empty() {
|
||||||
format!("Server error ({t})\n{d}")
|
format!("Server message\n{d}")
|
||||||
},
|
|
||||||
Color::WHITE,
|
|
||||||
None,
|
|
||||||
Vec2::new(0.5, 0.5),
|
|
||||||
)],
|
|
||||||
Color::from_rgba(0.0, 0.0, 0.0, 0.8),
|
|
||||||
)),
|
|
||||||
if t.is_empty() {
|
|
||||||
NotifInfo::new(Duration::from_secs(2))
|
|
||||||
} else {
|
|
||||||
NotifInfo::new(Duration::from_secs(5))
|
|
||||||
.with_highlight(Color::RED)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}))
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
Action::Denied(req) => {
|
|
||||||
let req = *req;
|
|
||||||
notif_sender_two
|
|
||||||
.send(Box::new(move |_| {
|
|
||||||
(
|
|
||||||
Box::new(Panel::with_background(
|
|
||||||
GuiElemCfg::default(),
|
|
||||||
[Label::new(
|
|
||||||
GuiElemCfg::default(),
|
|
||||||
format!(
|
|
||||||
"server denied {}",
|
|
||||||
if req.is_some() {
|
|
||||||
"request, maybe desynced"
|
|
||||||
} else {
|
} else {
|
||||||
"action, likely desynced"
|
format!("Server error ({t})\n{d}")
|
||||||
},
|
},
|
||||||
),
|
Color::WHITE,
|
||||||
Color::WHITE,
|
None,
|
||||||
None,
|
Vec2::new(0.5, 0.5),
|
||||||
Vec2::new(0.5, 0.5),
|
)],
|
||||||
)],
|
Color::from_rgba(0.0, 0.0, 0.0, 0.8),
|
||||||
Color::from_rgba(0.0, 0.0, 0.0, 0.8),
|
)),
|
||||||
)),
|
if t.is_empty() {
|
||||||
NotifInfo::new(Duration::from_secs(1)),
|
NotifInfo::new(Duration::from_secs(2))
|
||||||
)
|
} else {
|
||||||
}))
|
NotifInfo::new(Duration::from_secs(5))
|
||||||
.unwrap();
|
.with_highlight(Color::RED)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}))
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
Action::Denied(req) => {
|
||||||
|
let req = *req;
|
||||||
|
notif_sender_two
|
||||||
|
.send(Box::new(move |_| {
|
||||||
|
(
|
||||||
|
Box::new(Panel::with_background(
|
||||||
|
GuiElemCfg::default(),
|
||||||
|
[Label::new(
|
||||||
|
GuiElemCfg::default(),
|
||||||
|
format!(
|
||||||
|
"server denied {}",
|
||||||
|
if req.is_some() {
|
||||||
|
"request, maybe desynced"
|
||||||
|
} else {
|
||||||
|
"action, likely desynced"
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Color::WHITE,
|
||||||
|
None,
|
||||||
|
Vec2::new(0.5, 0.5),
|
||||||
|
)],
|
||||||
|
Color::from_rgba(0.0, 0.0, 0.0, 0.8),
|
||||||
|
)),
|
||||||
|
NotifInfo::new(Duration::from_secs(1)),
|
||||||
|
)
|
||||||
|
}))
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
)),
|
})),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
let no_animations = false;
|
let no_animations = false;
|
||||||
|
@ -527,19 +527,23 @@ impl Database {
|
|||||||
if let Some(client) = client {
|
if let Some(client) = client {
|
||||||
for (udepid, udep) in &mut self.update_endpoints {
|
for (udepid, udep) in &mut self.update_endpoints {
|
||||||
if client == *udepid {
|
if client == *udepid {
|
||||||
let denied =
|
let mut reqs = command.action.get_req_if_some();
|
||||||
Action::Denied(command.action.get_req().unwrap_or_else(Req::none))
|
if reqs.is_empty() {
|
||||||
.cmd(0xFFu8);
|
reqs.push(Req::none());
|
||||||
match udep {
|
}
|
||||||
UpdateEndpoint::Bytes(w) => {
|
for req in reqs {
|
||||||
let _ = w.write(&denied.to_bytes_vec());
|
let denied = Action::Denied(req).cmd(0xFFu8);
|
||||||
|
match udep {
|
||||||
|
UpdateEndpoint::Bytes(w) => {
|
||||||
|
let _ = w.write(&denied.to_bytes_vec());
|
||||||
|
}
|
||||||
|
UpdateEndpoint::CmdChannel(w) => {
|
||||||
|
let _ = w.send(Arc::new(denied));
|
||||||
|
}
|
||||||
|
UpdateEndpoint::Custom(w) => w(&denied),
|
||||||
|
UpdateEndpoint::CustomArc(w) => w(Arc::new(denied)),
|
||||||
|
UpdateEndpoint::CustomBytes(w) => w(&denied.to_bytes_vec()),
|
||||||
}
|
}
|
||||||
UpdateEndpoint::CmdChannel(w) => {
|
|
||||||
let _ = w.send(Arc::new(denied));
|
|
||||||
}
|
|
||||||
UpdateEndpoint::Custom(w) => w(&denied),
|
|
||||||
UpdateEndpoint::CustomArc(w) => w(Arc::new(denied)),
|
|
||||||
UpdateEndpoint::CustomBytes(w) => w(&denied.to_bytes_vec()),
|
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -864,6 +868,11 @@ impl Database {
|
|||||||
song.duration_millis = duration;
|
song.duration_millis = duration;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Action::Multiple(actions) => {
|
||||||
|
for action in actions {
|
||||||
|
self.apply_action_unchecked_seq(action, client);
|
||||||
|
}
|
||||||
|
}
|
||||||
Action::InitComplete => {
|
Action::InitComplete => {
|
||||||
self.client_is_init = true;
|
self.client_is_init = true;
|
||||||
}
|
}
|
||||||
@ -1008,13 +1017,13 @@ impl Database {
|
|||||||
self.seq.inc();
|
self.seq.inc();
|
||||||
}
|
}
|
||||||
let mut update = self.seq.pack(update);
|
let mut update = self.seq.pack(update);
|
||||||
let req = update.action.take_req();
|
let reqs = update.action.take_req_all();
|
||||||
let mut remove = vec![];
|
let mut remove = vec![];
|
||||||
let mut bytes = None;
|
let mut bytes = None;
|
||||||
let mut arc = None;
|
let mut arc = None;
|
||||||
for (i, (udepid, udep)) in self.update_endpoints.iter_mut().enumerate() {
|
for (i, (udepid, udep)) in self.update_endpoints.iter_mut().enumerate() {
|
||||||
if req.is_some_and(|r| r.is_some()) && client.is_some_and(|v| *udepid == v) {
|
if reqs.iter().any(|r| r.is_some()) && client.is_some_and(|v| *udepid == v) {
|
||||||
update.action.put_req(req.unwrap());
|
update.action.put_req_all(reqs.clone());
|
||||||
match udep {
|
match udep {
|
||||||
UpdateEndpoint::Bytes(writer) => {
|
UpdateEndpoint::Bytes(writer) => {
|
||||||
if writer.write_all(&update.to_bytes_vec()).is_err() {
|
if writer.write_all(&update.to_bytes_vec()).is_err() {
|
||||||
@ -1035,7 +1044,7 @@ impl Database {
|
|||||||
func(bytes.as_ref().unwrap())
|
func(bytes.as_ref().unwrap())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
update.action.take_req();
|
update.action.take_req_all();
|
||||||
}
|
}
|
||||||
match udep {
|
match udep {
|
||||||
UpdateEndpoint::Bytes(writer) => {
|
UpdateEndpoint::Bytes(writer) => {
|
||||||
@ -1079,9 +1088,7 @@ impl Database {
|
|||||||
self.update_endpoints.remove(i);
|
self.update_endpoints.remove(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(req) = req {
|
update.action.put_req_all(reqs);
|
||||||
update.action.put_req(req);
|
|
||||||
}
|
|
||||||
update.action
|
update.action
|
||||||
}
|
}
|
||||||
pub fn sync(&mut self, artists: Vec<Artist>, albums: Vec<Album>, songs: Vec<Song>) {
|
pub fn sync(&mut self, artists: Vec<Artist>, albums: Vec<Album>, songs: Vec<Song>) {
|
||||||
|
@ -43,20 +43,28 @@ impl Action {
|
|||||||
pub fn cmd(self, seq: u8) -> Command {
|
pub fn cmd(self, seq: u8) -> Command {
|
||||||
Command::new(seq, self)
|
Command::new(seq, self)
|
||||||
}
|
}
|
||||||
pub fn take_req(&mut self) -> Option<Req> {
|
pub fn take_req_all(&mut self) -> Vec<Req> {
|
||||||
self.req_mut()
|
self.req_mut()
|
||||||
|
.into_iter()
|
||||||
.map(|r| std::mem::replace(r, Req::none()))
|
.map(|r| std::mem::replace(r, Req::none()))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
pub fn get_req_all(&mut self) -> Vec<Req> {
|
||||||
|
self.req_mut().into_iter().map(|r| *r).collect()
|
||||||
|
}
|
||||||
|
pub fn get_req_if_some(&mut self) -> Vec<Req> {
|
||||||
|
self.req_mut()
|
||||||
|
.into_iter()
|
||||||
|
.map(|r| *r)
|
||||||
.filter(|r| r.is_some())
|
.filter(|r| r.is_some())
|
||||||
|
.collect()
|
||||||
}
|
}
|
||||||
pub fn get_req(&mut self) -> Option<Req> {
|
pub fn put_req_all(&mut self, reqs: Vec<Req>) {
|
||||||
self.req_mut().map(|r| *r).filter(|r| r.is_some())
|
for (o, n) in self.req_mut().into_iter().zip(reqs) {
|
||||||
}
|
*o = n;
|
||||||
pub fn put_req(&mut self, req: Req) {
|
|
||||||
if let Some(r) = self.req_mut() {
|
|
||||||
*r = req;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn req_mut(&mut self) -> Option<&mut Req> {
|
fn req_mut(&mut self) -> Vec<&mut Req> {
|
||||||
match self {
|
match self {
|
||||||
Self::QueueUpdate(_, _, req)
|
Self::QueueUpdate(_, _, req)
|
||||||
| Self::QueueAdd(_, _, req)
|
| Self::QueueAdd(_, _, req)
|
||||||
@ -68,7 +76,7 @@ impl Action {
|
|||||||
| Self::ModifySong(_, req)
|
| Self::ModifySong(_, req)
|
||||||
| Self::ModifyAlbum(_, req)
|
| Self::ModifyAlbum(_, req)
|
||||||
| Self::ModifyArtist(_, req)
|
| Self::ModifyArtist(_, req)
|
||||||
| Self::Denied(req) => Some(req),
|
| Self::Denied(req) => vec![req],
|
||||||
Self::Resume
|
Self::Resume
|
||||||
| Self::Pause
|
| Self::Pause
|
||||||
| Self::Stop
|
| Self::Stop
|
||||||
@ -99,7 +107,8 @@ impl Action {
|
|||||||
| Self::TagArtistPropertyUnset(_, _)
|
| Self::TagArtistPropertyUnset(_, _)
|
||||||
| Self::InitComplete
|
| Self::InitComplete
|
||||||
| Self::Save
|
| Self::Save
|
||||||
| Self::ErrorInfo(_, _) => None,
|
| Self::ErrorInfo(_, _) => vec![],
|
||||||
|
Self::Multiple(actions) => actions.iter_mut().flat_map(|v| v.req_mut()).collect(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -215,6 +224,8 @@ pub enum Action {
|
|||||||
TagArtistPropertySet(ArtistId, String, String),
|
TagArtistPropertySet(ArtistId, String, String),
|
||||||
TagArtistPropertyUnset(ArtistId, String),
|
TagArtistPropertyUnset(ArtistId, String),
|
||||||
|
|
||||||
|
Multiple(Vec<Self>),
|
||||||
|
|
||||||
InitComplete,
|
InitComplete,
|
||||||
Save,
|
Save,
|
||||||
ErrorInfo(String, String),
|
ErrorInfo(String, String),
|
||||||
@ -471,6 +482,7 @@ const BYTE_PAUSE: u8 = 0b01_000_001;
|
|||||||
const BYTE_STOP: u8 = 0b01_000_010;
|
const BYTE_STOP: u8 = 0b01_000_010;
|
||||||
const BYTE_NEXT_SONG: u8 = 0b01_000_100;
|
const BYTE_NEXT_SONG: u8 = 0b01_000_100;
|
||||||
|
|
||||||
|
const BYTE_MULTIPLE: u8 = 0b01_010_100;
|
||||||
const BYTE_INIT_COMPLETE: u8 = 0b01_010_000;
|
const BYTE_INIT_COMPLETE: u8 = 0b01_010_000;
|
||||||
const BYTE_SET_SONG_DURATION: u8 = 0b01_010_001;
|
const BYTE_SET_SONG_DURATION: u8 = 0b01_010_001;
|
||||||
const BYTE_SAVE: u8 = 0b01_010_010;
|
const BYTE_SAVE: u8 = 0b01_010_010;
|
||||||
@ -546,8 +558,7 @@ impl ToFromBytes for Req {
|
|||||||
Ok(Self(ToFromBytes::from_bytes(s)?))
|
Ok(Self(ToFromBytes::from_bytes(s)?))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// impl ToFromBytes for Action {
|
impl ToFromBytes for Action {
|
||||||
impl Action {
|
|
||||||
fn to_bytes<T>(&self, s: &mut T) -> Result<(), std::io::Error>
|
fn to_bytes<T>(&self, s: &mut T) -> Result<(), std::io::Error>
|
||||||
where
|
where
|
||||||
T: Write,
|
T: Write,
|
||||||
@ -753,6 +764,10 @@ impl Action {
|
|||||||
i.to_bytes(s)?;
|
i.to_bytes(s)?;
|
||||||
d.to_bytes(s)?;
|
d.to_bytes(s)?;
|
||||||
}
|
}
|
||||||
|
Self::Multiple(actions) => {
|
||||||
|
s.write_all(&[BYTE_MULTIPLE])?;
|
||||||
|
actions.to_bytes(s)?;
|
||||||
|
}
|
||||||
Self::InitComplete => {
|
Self::InitComplete => {
|
||||||
s.write_all(&[BYTE_INIT_COMPLETE])?;
|
s.write_all(&[BYTE_INIT_COMPLETE])?;
|
||||||
}
|
}
|
||||||
@ -880,6 +895,7 @@ impl Action {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
BYTE_SET_SONG_DURATION => Self::SetSongDuration(from_bytes!(), from_bytes!()),
|
BYTE_SET_SONG_DURATION => Self::SetSongDuration(from_bytes!(), from_bytes!()),
|
||||||
|
BYTE_MULTIPLE => Self::Multiple(from_bytes!()),
|
||||||
BYTE_INIT_COMPLETE => Self::InitComplete,
|
BYTE_INIT_COMPLETE => Self::InitComplete,
|
||||||
BYTE_SAVE => Self::Save,
|
BYTE_SAVE => Self::Save,
|
||||||
BYTE_ERRORINFO => Self::ErrorInfo(from_bytes!(), from_bytes!()),
|
BYTE_ERRORINFO => Self::ErrorInfo(from_bytes!(), from_bytes!()),
|
||||||
|
1
musicdb-remove-duplicates-from-queue/.gitignore
vendored
Executable file
1
musicdb-remove-duplicates-from-queue/.gitignore
vendored
Executable file
@ -0,0 +1 @@
|
|||||||
|
/target
|
9
musicdb-remove-duplicates-from-queue/Cargo.toml
Normal file
9
musicdb-remove-duplicates-from-queue/Cargo.toml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
[package]
|
||||||
|
name = "musicdb-test"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
musicdb-lib = { path = "../musicdb-lib" }
|
61
musicdb-remove-duplicates-from-queue/src/main.rs
Executable file
61
musicdb-remove-duplicates-from-queue/src/main.rs
Executable file
@ -0,0 +1,61 @@
|
|||||||
|
use std::{collections::HashSet, io::Write, net::TcpStream};
|
||||||
|
|
||||||
|
use musicdb_lib::{
|
||||||
|
data::{
|
||||||
|
database::Database,
|
||||||
|
queue::{Queue, QueueContent},
|
||||||
|
SongId,
|
||||||
|
},
|
||||||
|
load::ToFromBytes,
|
||||||
|
server::{Action, Command},
|
||||||
|
};
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut con = TcpStream::connect(
|
||||||
|
std::env::args()
|
||||||
|
.nth(1)
|
||||||
|
.expect("required argument: server address and port"),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
writeln!(con, "main").unwrap();
|
||||||
|
let mut db = Database::new_clientside();
|
||||||
|
while !db.is_client_init() {
|
||||||
|
db.apply_action_unchecked_seq(Command::from_bytes(&mut con).unwrap().action, None);
|
||||||
|
}
|
||||||
|
let mut actions = vec![];
|
||||||
|
rev_actions(&mut actions, &db.queue, &mut vec![], &mut HashSet::new());
|
||||||
|
eprintln!("Removing {} queue elements", actions.len());
|
||||||
|
db.seq
|
||||||
|
.pack(Action::Multiple(actions))
|
||||||
|
.to_bytes(&mut con)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rev_actions(
|
||||||
|
actions: &mut Vec<Action>,
|
||||||
|
queue: &Queue,
|
||||||
|
path: &mut Vec<usize>,
|
||||||
|
seen: &mut HashSet<SongId>,
|
||||||
|
) {
|
||||||
|
match queue.content() {
|
||||||
|
QueueContent::Song(id) => {
|
||||||
|
if seen.contains(id) {
|
||||||
|
actions.push(Action::QueueRemove(path.clone()));
|
||||||
|
} else {
|
||||||
|
seen.insert(*id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
QueueContent::Folder(folder) => {
|
||||||
|
for (i, queue) in folder.iter().enumerate() {
|
||||||
|
path.push(i);
|
||||||
|
rev_actions(actions, queue, path, seen);
|
||||||
|
path.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
QueueContent::Loop(_, _, inner) => {
|
||||||
|
path.push(0);
|
||||||
|
rev_actions(actions, &*inner, path, seen);
|
||||||
|
path.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -149,43 +149,60 @@ fn main() {
|
|||||||
writeln!(con, "main").unwrap();
|
writeln!(con, "main").unwrap();
|
||||||
loop {
|
loop {
|
||||||
let mut cmd = musicdb_lib::server::Command::from_bytes(&mut con).unwrap();
|
let mut cmd = musicdb_lib::server::Command::from_bytes(&mut con).unwrap();
|
||||||
use musicdb_lib::server::Action::*;
|
use musicdb_lib::server::Action::{self, *};
|
||||||
match &cmd.action {
|
fn sanitize_actions(action: Action) -> Option<Action> {
|
||||||
// ignore playback and queue commands, and denials
|
match action {
|
||||||
Resume | Pause | Stop | NextSong | QueueUpdate(..) | QueueAdd(..)
|
// ignore playback and queue commands, and denials
|
||||||
| QueueInsert(..) | QueueRemove(..) | QueueMove(..) | QueueMoveInto(..)
|
Resume | Pause | Stop | NextSong | QueueUpdate(..) | QueueAdd(..)
|
||||||
| QueueGoto(..) | QueueShuffle(..) | QueueSetShuffle(..)
|
| QueueInsert(..) | QueueRemove(..) | QueueMove(..) | QueueMoveInto(..)
|
||||||
| QueueUnshuffle(..) | Denied(..) => continue,
|
| QueueGoto(..) | QueueShuffle(..) | QueueSetShuffle(..)
|
||||||
SyncDatabase(..)
|
| QueueUnshuffle(..) | Denied(..) => None,
|
||||||
| AddSong(..)
|
SyncDatabase(..)
|
||||||
| AddAlbum(..)
|
| AddSong(..)
|
||||||
| AddArtist(..)
|
| AddAlbum(..)
|
||||||
| AddCover(..)
|
| AddArtist(..)
|
||||||
| ModifySong(..)
|
| AddCover(..)
|
||||||
| ModifyAlbum(..)
|
| ModifySong(..)
|
||||||
| RemoveSong(..)
|
| ModifyAlbum(..)
|
||||||
| RemoveAlbum(..)
|
| RemoveSong(..)
|
||||||
| RemoveArtist(..)
|
| RemoveAlbum(..)
|
||||||
| ModifyArtist(..)
|
| RemoveArtist(..)
|
||||||
| SetSongDuration(..)
|
| ModifyArtist(..)
|
||||||
| TagSongFlagSet(..)
|
| SetSongDuration(..)
|
||||||
| TagSongFlagUnset(..)
|
| TagSongFlagSet(..)
|
||||||
| TagAlbumFlagSet(..)
|
| TagSongFlagUnset(..)
|
||||||
| TagAlbumFlagUnset(..)
|
| TagAlbumFlagSet(..)
|
||||||
| TagArtistFlagSet(..)
|
| TagAlbumFlagUnset(..)
|
||||||
| TagArtistFlagUnset(..)
|
| TagArtistFlagSet(..)
|
||||||
| TagSongPropertySet(..)
|
| TagArtistFlagUnset(..)
|
||||||
| TagSongPropertyUnset(..)
|
| TagSongPropertySet(..)
|
||||||
| TagAlbumPropertySet(..)
|
| TagSongPropertyUnset(..)
|
||||||
| TagAlbumPropertyUnset(..)
|
| TagAlbumPropertySet(..)
|
||||||
| TagArtistPropertySet(..)
|
| TagAlbumPropertyUnset(..)
|
||||||
| TagArtistPropertyUnset(..)
|
| TagArtistPropertySet(..)
|
||||||
| InitComplete
|
| TagArtistPropertyUnset(..)
|
||||||
| Save
|
| InitComplete
|
||||||
| ErrorInfo(..) => (),
|
| Save
|
||||||
|
| ErrorInfo(..) => Some(action),
|
||||||
|
Multiple(actions) => {
|
||||||
|
let actions = actions
|
||||||
|
.into_iter()
|
||||||
|
.flat_map(|action| sanitize_actions(action))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
if actions.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(Multiple(actions))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(action) = sanitize_actions(cmd.action) {
|
||||||
|
database
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.apply_action_unchecked_seq(action, None);
|
||||||
}
|
}
|
||||||
cmd.seq = 0xFF;
|
|
||||||
database.lock().unwrap().apply_command(cmd, None);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user