diff --git a/src/rust/amadeus-rs/Cargo.lock b/src/rust/amadeus-rs/Cargo.lock index 25f8651df2a9485136e29cf28054c8cbe4666cd5..dbf3c1923497294c560885781ebc59fa9cc3e650 100644 --- a/src/rust/amadeus-rs/Cargo.lock +++ b/src/rust/amadeus-rs/Cargo.lock @@ -639,6 +639,15 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "fastrand" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" +dependencies = [ + "instant", +] + [[package]] name = "flate2" version = "1.0.24" @@ -970,6 +979,7 @@ dependencies = [ "amadeus_macro", "lazy_static", "log", + "no-panic", "regex", "serde", "serde_json", @@ -1174,6 +1184,16 @@ dependencies = [ "memoffset", ] +[[package]] +name = "no-panic" +version = "0.1.15" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "tempfile", +] + [[package]] name = "nohash-hasher" version = "0.2.0" @@ -1431,6 +1451,15 @@ version = "0.6.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + [[package]] name = "ron" version = "0.7.1" @@ -1630,6 +1659,20 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tempfile" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +dependencies = [ + "cfg-if", + "fastrand", + "libc", + "redox_syscall", + "remove_dir_all", + "winapi", +] + [[package]] name = "thiserror" version = "1.0.34" diff --git a/src/rust/amadeus-rs/Cargo.toml b/src/rust/amadeus-rs/Cargo.toml index 23f29b1a3bcb4712d8013e31d21dffaf7faa6542..f3ba4ed4f9e99abc508c057c42da17ac6027ff60 100644 --- a/src/rust/amadeus-rs/Cargo.toml +++ b/src/rust/amadeus-rs/Cargo.toml @@ -4,5 +4,6 @@ members = [ "amadeus-lib", "amadeus-macro", "lkt-rs", - "lkt-lib" -] \ No newline at end of file + "lkt-lib", + "no-panic" +] diff --git a/src/rust/amadeus-rs/amadeus-lib/src/deamon.rs b/src/rust/amadeus-rs/amadeus-lib/src/deamon.rs index 7d59b01ff19a7e19ae1a12220b8ff1aba124ad4e..a2f321ee858b6b3aaf6ed6b5cf0820b71cee00ea 100644 --- a/src/rust/amadeus-rs/amadeus-lib/src/deamon.rs +++ b/src/rust/amadeus-rs/amadeus-lib/src/deamon.rs @@ -1,5 +1,5 @@ use lkt_lib::*; -use log::error; +use log::*; use std::{ cell::Cell, io, @@ -179,6 +179,7 @@ impl Deamon for StatusDeamon { } }; + info!("send {status:?} and {current:?}"); if let Err(e) = responses_send.send((status, current)) { error!("Failed to send a status response to amadeus: {e}"); } diff --git a/src/rust/amadeus-rs/amadeus/src/amadeus.rs b/src/rust/amadeus-rs/amadeus/src/amadeus.rs index e0660113d33d6f93d774dd9052769f475269549b..279803f8c30c30e07431c9f771f4028233d0428b 100644 --- a/src/rust/amadeus-rs/amadeus/src/amadeus.rs +++ b/src/rust/amadeus-rs/amadeus/src/amadeus.rs @@ -3,9 +3,12 @@ use amadeus_lib::{ actions, deamon::{self, CommandDeamon, Deamon, StatusDeamon}, }; -use amadeus_macro::either; +use amadeus_macro::{either, then_some}; use eframe::{egui, App}; -use lkt_lib::{Kara, LektorQuery, LektorResponse, LektorState, Playlist}; +use lkt_lib::{ + Kara, LektorCurrentKaraInnerResponse, LektorPlaybackStatusResponse, LektorQuery, + LektorResponse, LektorState, +}; use log::debug; use std::{ sync::mpsc::{Receiver, Sender}, @@ -35,7 +38,9 @@ pub struct Amadeus<'a> { deamon: Option<CommandDeamonData>, status_deamon: Option<StatusDeamonData>, - lektord_current_kara: Option<KaraCard>, + lektord_status: Option<LektorPlaybackStatusResponse>, + lektord_current: Option<LektorCurrentKaraInnerResponse>, + lektord_queue: KaraCardCollection<'a>, lektord_historic: KaraCardCollection<'a>, @@ -80,10 +85,12 @@ impl Default for Amadeus<'_> { need_settings_window: Default::default(), deamon: Default::default(), status_deamon: Default::default(), - lektord_current_kara: Default::default(), lektord_updated_query: Default::default(), lektord_state: Default::default(), amadeus_logo_texture: Default::default(), + + lektord_status: None, + lektord_current: None, } } } @@ -104,7 +111,6 @@ impl Amadeus<'_> { ctx.set_style(style); ret.apply_settings(); - ret.fill_debug_values(); Box::new(ret) } @@ -192,35 +198,47 @@ impl Amadeus<'_> { }); } - fn render_bottom_panel(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { - let seconds = time::SystemTime::now() - .duration_since(time::SystemTime::UNIX_EPOCH) - .unwrap() - .as_secs() - % 90; - let progress = seconds as f32 / 90_f32; - let text_duration = { - let duration = time::Duration::from_secs(seconds); - let mins = duration.as_secs() / 60; - let secs = duration.as_secs() % 60; - format!("{:02}:{:02}", mins, secs) - }; + fn render_bottom_panel(&mut self, ctx: &egui::Context, _: &mut eframe::Frame) { + if !(self.lektord_status.is_some() && self.lektord_current.is_some()) { + return; + } + + let status = self.lektord_status.as_ref().unwrap(); + let current = self.lektord_current.as_ref().unwrap(); + + let progress = (status.elapsed() as f64 / status.duration() as f64) as f32; + let text_duration = format!( + "{:02}:{:02}", + status.duration() / 60, + status.duration() % 60 + ); egui::TopBottomPanel::bottom("FOOTER") .max_height(constants::BOTTOM_PANEL_MAX_SIZE) .min_height(constants::BOTTOM_PANEL_MAX_SIZE) .resizable(false) .show(ctx, |ui| { + // The text... ui.horizontal(|ui| { - if let Some(current_kara) = &self.lektord_current_kara { - current_kara.render_compact(ui, self.config.dark_mode); - } + KaraCard::new(Kara { + id: status.songid().unwrap() as u32, + source_name: current.source().to_string(), + song_type: current.song_type().to_string(), + language: current.language().to_string(), + category: current.category().to_string(), + title: current.title().to_string(), + song_number: current.song_number().map(|x| x as u32), + author: current.author().to_string(), + is_available: true, + }) + .render_compact(ui, self.config.dark_mode); ui.with_layout(egui::Layout::right_to_left(egui::Align::Min), |ui| { ui.style_mut().override_text_style = Some(utils::font::heading3()); ui.add_space(constants::PADDING * 2.); ui.label(text_duration); }); }); + // The progress bar... let style = ui.style().clone(); ui.style_mut().override_text_style = Some(utils::font::small()); ui.style_mut().visuals.override_text_color = @@ -334,6 +352,34 @@ impl Amadeus<'_> { ctx.set_visuals(visuals); } + /// Handle the events recieved from the deamons that connect to lektord. + fn handle_deamons_events(&mut self) { + if let Some(((_, deamon), _)) = self.deamon.as_mut() { + while let Ok(event) = deamon.try_recv() { + use LektorResponse::*; + match event { + PlaybackStatus(_) => todo!(), + CurrentKara(_) => todo!(), + PlaylistSet(_) => todo!(), + EmptyResponse(_) => todo!(), + KaraSet(_) => todo!(), + } + } + } + + if let Some(((deamon,), _)) = self.status_deamon.as_mut() { + while let Ok((status, current)) = deamon.try_recv() { + // Sanitize some values, if the time/elapsed/duration is not + // valid, we set everything to None/zero. + self.lektord_status = then_some!(status.verify() => status); + self.lektord_current = + then_some!(current.is_some() => current.unwrap().maybe_into_inner()).flatten(); + } + } + } + + /// Handle actions recieved from the user. May communicate to the deamons + /// and send infos/commands to lektord if they are available. fn handle_action(&mut self) { use actions::Action::*; @@ -342,7 +388,25 @@ impl Amadeus<'_> { match act { OpenPlaylist => self.playlist_store.show_playlist(id), ClearPlaylistContent => self.playlist_store.clear_playlist(id), - _ => debug!("Execute action {act:?} on lektor item with id {id}"), + PlayFromKara + | DeleteKaraFromQueue + | InsertKaraInQueue + | AddKaraToQueue + | AddKaraToPlaylist + | DeleteKaraFromPlaylist(_) + | AddPlaylistToQueue + | InsertPlaylistToQueue => { + debug!("Execute action {act:?} on lektor item with id {id}") + } + + // This should not occure on items because they are global + // actions. + PlaybackPrevious + | PlaybackPlay + | PlaybackPause + | PlaybackNext + | ConnectToLektord + | DisconnectFromLektord => unreachable!(), } } @@ -377,7 +441,12 @@ impl Amadeus<'_> { deamon.quit(); } } - _ => debug!("Execute action {act:?} on lektor"), + + PlaybackPrevious | PlaybackPlay | PlaybackPause | PlaybackNext => { + debug!("Execute action {act:?} on lektor") + } + + _ => unreachable!(), } } @@ -404,56 +473,6 @@ impl Amadeus<'_> { self.lektord_search_results .with_max_content(self.config.ui_max_search_items.as_integer()); } - - fn fill_debug_values(&mut self) { - let default_kara_card = KaraCard::new(Kara { - id: 1000, - source_name: "Totoro".to_owned(), - song_type: "OP".to_owned(), - language: "jp".to_owned(), - category: "vo".to_owned(), - title: "Totoro no Sampo".to_owned(), - song_number: Some(1), - author: "Geralt".to_owned(), - is_available: false, - }); - - self.lektord_current_kara = Some(KaraCard::new(Kara { - id: 3000, - source_name: "Umineko no Naku Koro ni Saku".to_owned(), - song_type: "ED".to_owned(), - language: "jp".to_owned(), - category: "cdg".to_owned(), - title: "Birth of a New Witdh".to_owned(), - song_number: Some(6), - author: "Kubat".to_owned(), - is_available: true, - })); - - self.lektord_queue - .add_card(default_kara_card.clone()) - .add_card(default_kara_card.clone()); - for _i in 1..=50 { - self.lektord_queue.add_card(default_kara_card.clone()); - } - - let mut some_playlist = KaraCardCollection::new("@Kubat".to_string()); - some_playlist.add_card(default_kara_card.clone()); - - self.playlist_store.create(Playlist { - id: 1, - name: "@Kubat".to_owned(), - }); - self.playlist_store.create(Playlist { - id: 2, - name: "@Geralt".to_owned(), - }); - self.playlist_store.create(Playlist { - id: 3, - name: "@Elliu".to_owned(), - }); - self.playlist_store.add("@Kubat", default_kara_card.inner); - } } impl App for Amadeus<'_> { @@ -484,6 +503,7 @@ impl App for Amadeus<'_> { self.last_render_instant = self.begin_render_instant; self.handle_action(); + self.handle_deamons_events(); if self.has_config_changed { self.apply_settings(); diff --git a/src/rust/amadeus-rs/lkt-lib/Cargo.toml b/src/rust/amadeus-rs/lkt-lib/Cargo.toml index d28a58181390b0bbe7207150be48431dfcef0619..3c5214492cb00977ade570dc0786d566eade8144 100644 --- a/src/rust/amadeus-rs/lkt-lib/Cargo.toml +++ b/src/rust/amadeus-rs/lkt-lib/Cargo.toml @@ -6,6 +6,7 @@ license = "MIT" [dependencies] amadeus_macro = { path = "../amadeus-macro" } +no-panic = { path = "../no-panic" } serde = { version = "1", default-features = false, features = [ "derive", "std" ] } serde_json = { version = "1", default-features = false, features = [ "std" ] } log = { version = "0.4" } diff --git a/src/rust/amadeus-rs/lkt-lib/src/connexion.rs b/src/rust/amadeus-rs/lkt-lib/src/connexion.rs index 3d8549bf3a2b572564372aa15b2c5b9a1e20eee6..e148df5d8ffd52fb8521a0111ddd2840f215b4b8 100644 --- a/src/rust/amadeus-rs/lkt-lib/src/connexion.rs +++ b/src/rust/amadeus-rs/lkt-lib/src/connexion.rs @@ -23,8 +23,8 @@ impl std::fmt::Display for LektorQueryError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { use LektorQueryError::*; match self { - Query(err) => write!(f, "lektor query logic error: {err}"), IO(io) => write!(f, "lektor query error io: {io}"), + Query(err) => write!(f, "lektor query logic error: {err}"), Other(other) => write!(f, "lektor query error: {other}"), ToTyped(msg) => write!(f, "lektor formated to typed error: {msg}"), } @@ -32,9 +32,8 @@ impl std::fmt::Display for LektorQueryError { } pub struct LektorConnexion { - stream: TcpStream, pub version: String, - + stream: TcpStream, reader: BufReader<TcpStream>, } @@ -119,7 +118,10 @@ impl LektorConnexion { Err(e) => return io::Result::Err(e), Ok(size) => { if size == 0 { - panic!("Got nothing in the line... consider this to be an error") + return io::Result::Err(io::Error::new( + io::ErrorKind::Other, + "recieved empty line", + )); } let msg = reply_line.trim(); match LektorQueryLineType::from_str(msg) { @@ -144,18 +146,17 @@ impl LektorConnexion { fn write_string(&mut self, buffer: String) -> io::Result<()> { if buffer.len() >= constants::LKT_MESSAGE_MAX { - panic!( - "Try to write a string that is too long for MPD! String length is {}, max is {}", - buffer.len(), - constants::LKT_MESSAGE_MAX - ); - } - if !buffer.ends_with('\n') { - panic!( - "A line to be send must end with a newline (\\n), it was not the case for: {}", - buffer - ); + Err(io::Error::new( + io::ErrorKind::InvalidData, + "buffer to send is to big!", + )) + } else if !buffer.ends_with('\n') { + Err(io::Error::new( + io::ErrorKind::InvalidData, + "buffer dosen't end with a new line", + )) + } else { + self.stream.write_all(buffer.as_bytes()) } - self.stream.write_all(buffer.as_bytes()) } } diff --git a/src/rust/amadeus-rs/lkt-lib/src/response.rs b/src/rust/amadeus-rs/lkt-lib/src/response.rs index fe9a9e89f30e376da6aecbc5cc0fb6207b763067..5f97291ef18d6f3e08e0397837e82d6332251586 100644 --- a/src/rust/amadeus-rs/lkt-lib/src/response.rs +++ b/src/rust/amadeus-rs/lkt-lib/src/response.rs @@ -170,6 +170,14 @@ pub struct LektorEmptyResponse; impl LektorCurrentKaraResponse { getter!(content: ref Option<LektorCurrentKaraInnerResponse>); + + pub fn maybe_into_inner(self) -> Option<LektorCurrentKaraInnerResponse> { + self.content + } + + pub fn into_inner(self) -> LektorCurrentKaraInnerResponse { + self.content.unwrap() + } } impl LektorCurrentKaraInnerResponse { @@ -195,6 +203,10 @@ impl LektorPlaybackStatusResponse { getter!(consume: bool); getter!(single: bool); getter!(repeat: bool); + + pub fn verify(&self) -> bool { + self.elapsed() <= self.duration() + } } impl LektorKaraSetResponse { diff --git a/src/rust/amadeus-rs/no-panic b/src/rust/amadeus-rs/no-panic new file mode 160000 index 0000000000000000000000000000000000000000..32558fbd00c01981c64e4742724342a9c5f0203b --- /dev/null +++ b/src/rust/amadeus-rs/no-panic @@ -0,0 +1 @@ +Subproject commit 32558fbd00c01981c64e4742724342a9c5f0203b