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.push((
|
||||
udepid,
|
||||
musicdb_lib::data::database::UpdateEndpoint::Custom(Box::new(
|
||||
move |cmd| match &cmd.action {
|
||||
Action::Resume
|
||||
| Action::Pause
|
||||
| Action::Stop
|
||||
| Action::Save
|
||||
| Action::InitComplete => {}
|
||||
Action::NextSong
|
||||
| Action::QueueUpdate(..)
|
||||
| Action::QueueAdd(..)
|
||||
| Action::QueueInsert(..)
|
||||
| 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);
|
||||
musicdb_lib::data::database::UpdateEndpoint::Custom(Box::new(move |cmd| {
|
||||
the_impl(&cmd.action, &event_sender_arc, ¬if_sender_two);
|
||||
fn the_impl(
|
||||
action: &Action,
|
||||
event_sender_arc: &Arc<Mutex<Option<UserEventSender<GuiEvent>>>>,
|
||||
notif_sender_two: &Sender<
|
||||
Box<dyn FnOnce(&NotifOverlay) -> (Box<dyn GuiElem>, NotifInfo) + Send>,
|
||||
>,
|
||||
) {
|
||||
match action {
|
||||
Action::Resume
|
||||
| Action::Pause
|
||||
| Action::Stop
|
||||
| Action::Save
|
||||
| Action::InitComplete => {}
|
||||
Action::NextSong
|
||||
| Action::QueueUpdate(..)
|
||||
| Action::QueueAdd(..)
|
||||
| Action::QueueInsert(..)
|
||||
| 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::AddSong(_, _)
|
||||
| Action::AddAlbum(_, _)
|
||||
| Action::AddArtist(_, _)
|
||||
| Action::AddCover(_, _)
|
||||
| Action::ModifySong(_, _)
|
||||
| Action::ModifyAlbum(_, _)
|
||||
| Action::ModifyArtist(_, _)
|
||||
| Action::RemoveSong(_)
|
||||
| Action::RemoveAlbum(_)
|
||||
| Action::RemoveArtist(_)
|
||||
| Action::TagSongFlagSet(..)
|
||||
| Action::TagSongFlagUnset(..)
|
||||
| Action::TagAlbumFlagSet(..)
|
||||
| Action::TagAlbumFlagUnset(..)
|
||||
| Action::TagArtistFlagSet(..)
|
||||
| Action::TagArtistFlagUnset(..)
|
||||
| Action::TagSongPropertySet(..)
|
||||
| Action::TagSongPropertyUnset(..)
|
||||
| Action::TagAlbumPropertySet(..)
|
||||
| Action::TagAlbumPropertyUnset(..)
|
||||
| Action::TagArtistPropertySet(..)
|
||||
| Action::TagArtistPropertyUnset(..)
|
||||
| Action::SetSongDuration(..) => {
|
||||
if let Some(s) = &*event_sender_arc.lock().unwrap() {
|
||||
_ = s.send_event(GuiEvent::UpdatedLibrary);
|
||||
Action::SyncDatabase(..)
|
||||
| Action::AddSong(_, _)
|
||||
| Action::AddAlbum(_, _)
|
||||
| Action::AddArtist(_, _)
|
||||
| Action::AddCover(_, _)
|
||||
| Action::ModifySong(_, _)
|
||||
| Action::ModifyAlbum(_, _)
|
||||
| Action::ModifyArtist(_, _)
|
||||
| Action::RemoveSong(_)
|
||||
| Action::RemoveAlbum(_)
|
||||
| Action::RemoveArtist(_)
|
||||
| Action::TagSongFlagSet(..)
|
||||
| Action::TagSongFlagUnset(..)
|
||||
| Action::TagAlbumFlagSet(..)
|
||||
| Action::TagAlbumFlagUnset(..)
|
||||
| Action::TagArtistFlagSet(..)
|
||||
| Action::TagArtistFlagUnset(..)
|
||||
| Action::TagSongPropertySet(..)
|
||||
| Action::TagSongPropertyUnset(..)
|
||||
| Action::TagAlbumPropertySet(..)
|
||||
| Action::TagAlbumPropertyUnset(..)
|
||||
| Action::TagArtistPropertySet(..)
|
||||
| Action::TagArtistPropertyUnset(..)
|
||||
| Action::SetSongDuration(..) => {
|
||||
if let Some(s) = &*event_sender_arc.lock().unwrap() {
|
||||
_ = s.send_event(GuiEvent::UpdatedLibrary);
|
||||
}
|
||||
}
|
||||
}
|
||||
Action::ErrorInfo(t, d) => {
|
||||
let (t, d) = (t.clone(), d.clone());
|
||||
notif_sender_two
|
||||
.send(Box::new(move |_| {
|
||||
(
|
||||
Box::new(Panel::with_background(
|
||||
GuiElemCfg::default(),
|
||||
[Label::new(
|
||||
Action::Multiple(actions) => {
|
||||
for action in actions {
|
||||
the_impl(action, event_sender_arc, notif_sender_two);
|
||||
}
|
||||
}
|
||||
Action::ErrorInfo(t, d) => {
|
||||
let (t, d) = (t.clone(), d.clone());
|
||||
notif_sender_two
|
||||
.send(Box::new(move |_| {
|
||||
(
|
||||
Box::new(Panel::with_background(
|
||||
GuiElemCfg::default(),
|
||||
if t.is_empty() {
|
||||
format!("Server message\n{d}")
|
||||
} else {
|
||||
format!("Server error ({t})\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"
|
||||
[Label::new(
|
||||
GuiElemCfg::default(),
|
||||
if t.is_empty() {
|
||||
format!("Server message\n{d}")
|
||||
} else {
|
||||
"action, likely desynced"
|
||||
format!("Server error ({t})\n{d}")
|
||||
},
|
||||
),
|
||||
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();
|
||||
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 {
|
||||
"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;
|
||||
|
@ -527,19 +527,23 @@ impl Database {
|
||||
if let Some(client) = client {
|
||||
for (udepid, udep) in &mut self.update_endpoints {
|
||||
if client == *udepid {
|
||||
let denied =
|
||||
Action::Denied(command.action.get_req().unwrap_or_else(Req::none))
|
||||
.cmd(0xFFu8);
|
||||
match udep {
|
||||
UpdateEndpoint::Bytes(w) => {
|
||||
let _ = w.write(&denied.to_bytes_vec());
|
||||
let mut reqs = command.action.get_req_if_some();
|
||||
if reqs.is_empty() {
|
||||
reqs.push(Req::none());
|
||||
}
|
||||
for req in reqs {
|
||||
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;
|
||||
}
|
||||
@ -864,6 +868,11 @@ impl Database {
|
||||
song.duration_millis = duration;
|
||||
}
|
||||
}
|
||||
Action::Multiple(actions) => {
|
||||
for action in actions {
|
||||
self.apply_action_unchecked_seq(action, client);
|
||||
}
|
||||
}
|
||||
Action::InitComplete => {
|
||||
self.client_is_init = true;
|
||||
}
|
||||
@ -1008,13 +1017,13 @@ impl Database {
|
||||
self.seq.inc();
|
||||
}
|
||||
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 bytes = None;
|
||||
let mut arc = None;
|
||||
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) {
|
||||
update.action.put_req(req.unwrap());
|
||||
if reqs.iter().any(|r| r.is_some()) && client.is_some_and(|v| *udepid == v) {
|
||||
update.action.put_req_all(reqs.clone());
|
||||
match udep {
|
||||
UpdateEndpoint::Bytes(writer) => {
|
||||
if writer.write_all(&update.to_bytes_vec()).is_err() {
|
||||
@ -1035,7 +1044,7 @@ impl Database {
|
||||
func(bytes.as_ref().unwrap())
|
||||
}
|
||||
}
|
||||
update.action.take_req();
|
||||
update.action.take_req_all();
|
||||
}
|
||||
match udep {
|
||||
UpdateEndpoint::Bytes(writer) => {
|
||||
@ -1079,9 +1088,7 @@ impl Database {
|
||||
self.update_endpoints.remove(i);
|
||||
}
|
||||
}
|
||||
if let Some(req) = req {
|
||||
update.action.put_req(req);
|
||||
}
|
||||
update.action.put_req_all(reqs);
|
||||
update.action
|
||||
}
|
||||
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 {
|
||||
Command::new(seq, self)
|
||||
}
|
||||
pub fn take_req(&mut self) -> Option<Req> {
|
||||
pub fn take_req_all(&mut self) -> Vec<Req> {
|
||||
self.req_mut()
|
||||
.into_iter()
|
||||
.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())
|
||||
.collect()
|
||||
}
|
||||
pub fn get_req(&mut self) -> Option<Req> {
|
||||
self.req_mut().map(|r| *r).filter(|r| r.is_some())
|
||||
}
|
||||
pub fn put_req(&mut self, req: Req) {
|
||||
if let Some(r) = self.req_mut() {
|
||||
*r = req;
|
||||
pub fn put_req_all(&mut self, reqs: Vec<Req>) {
|
||||
for (o, n) in self.req_mut().into_iter().zip(reqs) {
|
||||
*o = n;
|
||||
}
|
||||
}
|
||||
fn req_mut(&mut self) -> Option<&mut Req> {
|
||||
fn req_mut(&mut self) -> Vec<&mut Req> {
|
||||
match self {
|
||||
Self::QueueUpdate(_, _, req)
|
||||
| Self::QueueAdd(_, _, req)
|
||||
@ -68,7 +76,7 @@ impl Action {
|
||||
| Self::ModifySong(_, req)
|
||||
| Self::ModifyAlbum(_, req)
|
||||
| Self::ModifyArtist(_, req)
|
||||
| Self::Denied(req) => Some(req),
|
||||
| Self::Denied(req) => vec![req],
|
||||
Self::Resume
|
||||
| Self::Pause
|
||||
| Self::Stop
|
||||
@ -99,7 +107,8 @@ impl Action {
|
||||
| Self::TagArtistPropertyUnset(_, _)
|
||||
| Self::InitComplete
|
||||
| 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),
|
||||
TagArtistPropertyUnset(ArtistId, String),
|
||||
|
||||
Multiple(Vec<Self>),
|
||||
|
||||
InitComplete,
|
||||
Save,
|
||||
ErrorInfo(String, String),
|
||||
@ -471,6 +482,7 @@ const BYTE_PAUSE: u8 = 0b01_000_001;
|
||||
const BYTE_STOP: u8 = 0b01_000_010;
|
||||
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_SET_SONG_DURATION: u8 = 0b01_010_001;
|
||||
const BYTE_SAVE: u8 = 0b01_010_010;
|
||||
@ -546,8 +558,7 @@ impl ToFromBytes for Req {
|
||||
Ok(Self(ToFromBytes::from_bytes(s)?))
|
||||
}
|
||||
}
|
||||
// impl ToFromBytes for Action {
|
||||
impl Action {
|
||||
impl ToFromBytes for Action {
|
||||
fn to_bytes<T>(&self, s: &mut T) -> Result<(), std::io::Error>
|
||||
where
|
||||
T: Write,
|
||||
@ -753,6 +764,10 @@ impl Action {
|
||||
i.to_bytes(s)?;
|
||||
d.to_bytes(s)?;
|
||||
}
|
||||
Self::Multiple(actions) => {
|
||||
s.write_all(&[BYTE_MULTIPLE])?;
|
||||
actions.to_bytes(s)?;
|
||||
}
|
||||
Self::InitComplete => {
|
||||
s.write_all(&[BYTE_INIT_COMPLETE])?;
|
||||
}
|
||||
@ -880,6 +895,7 @@ impl Action {
|
||||
}
|
||||
},
|
||||
BYTE_SET_SONG_DURATION => Self::SetSongDuration(from_bytes!(), from_bytes!()),
|
||||
BYTE_MULTIPLE => Self::Multiple(from_bytes!()),
|
||||
BYTE_INIT_COMPLETE => Self::InitComplete,
|
||||
BYTE_SAVE => Self::Save,
|
||||
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();
|
||||
loop {
|
||||
let mut cmd = musicdb_lib::server::Command::from_bytes(&mut con).unwrap();
|
||||
use musicdb_lib::server::Action::*;
|
||||
match &cmd.action {
|
||||
// ignore playback and queue commands, and denials
|
||||
Resume | Pause | Stop | NextSong | QueueUpdate(..) | QueueAdd(..)
|
||||
| QueueInsert(..) | QueueRemove(..) | QueueMove(..) | QueueMoveInto(..)
|
||||
| QueueGoto(..) | QueueShuffle(..) | QueueSetShuffle(..)
|
||||
| QueueUnshuffle(..) | Denied(..) => continue,
|
||||
SyncDatabase(..)
|
||||
| AddSong(..)
|
||||
| AddAlbum(..)
|
||||
| AddArtist(..)
|
||||
| AddCover(..)
|
||||
| ModifySong(..)
|
||||
| ModifyAlbum(..)
|
||||
| RemoveSong(..)
|
||||
| RemoveAlbum(..)
|
||||
| RemoveArtist(..)
|
||||
| ModifyArtist(..)
|
||||
| SetSongDuration(..)
|
||||
| TagSongFlagSet(..)
|
||||
| TagSongFlagUnset(..)
|
||||
| TagAlbumFlagSet(..)
|
||||
| TagAlbumFlagUnset(..)
|
||||
| TagArtistFlagSet(..)
|
||||
| TagArtistFlagUnset(..)
|
||||
| TagSongPropertySet(..)
|
||||
| TagSongPropertyUnset(..)
|
||||
| TagAlbumPropertySet(..)
|
||||
| TagAlbumPropertyUnset(..)
|
||||
| TagArtistPropertySet(..)
|
||||
| TagArtistPropertyUnset(..)
|
||||
| InitComplete
|
||||
| Save
|
||||
| ErrorInfo(..) => (),
|
||||
use musicdb_lib::server::Action::{self, *};
|
||||
fn sanitize_actions(action: Action) -> Option<Action> {
|
||||
match action {
|
||||
// ignore playback and queue commands, and denials
|
||||
Resume | Pause | Stop | NextSong | QueueUpdate(..) | QueueAdd(..)
|
||||
| QueueInsert(..) | QueueRemove(..) | QueueMove(..) | QueueMoveInto(..)
|
||||
| QueueGoto(..) | QueueShuffle(..) | QueueSetShuffle(..)
|
||||
| QueueUnshuffle(..) | Denied(..) => None,
|
||||
SyncDatabase(..)
|
||||
| AddSong(..)
|
||||
| AddAlbum(..)
|
||||
| AddArtist(..)
|
||||
| AddCover(..)
|
||||
| ModifySong(..)
|
||||
| ModifyAlbum(..)
|
||||
| RemoveSong(..)
|
||||
| RemoveAlbum(..)
|
||||
| RemoveArtist(..)
|
||||
| ModifyArtist(..)
|
||||
| SetSongDuration(..)
|
||||
| TagSongFlagSet(..)
|
||||
| TagSongFlagUnset(..)
|
||||
| TagAlbumFlagSet(..)
|
||||
| TagAlbumFlagUnset(..)
|
||||
| TagArtistFlagSet(..)
|
||||
| TagArtistFlagUnset(..)
|
||||
| TagSongPropertySet(..)
|
||||
| TagSongPropertyUnset(..)
|
||||
| TagAlbumPropertySet(..)
|
||||
| TagAlbumPropertyUnset(..)
|
||||
| TagArtistPropertySet(..)
|
||||
| TagArtistPropertyUnset(..)
|
||||
| InitComplete
|
||||
| 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