diff --git a/src/cmus/events.rs b/src/cmus/events.rs index c896582..977d282 100644 --- a/src/cmus/events.rs +++ b/src/cmus/events.rs @@ -1,6 +1,6 @@ -use crate::{process_template_placeholders}; -use crate::cmus::{Track}; -use crate::cmus::player_settings::{PlayerSettings}; +use crate::{CompleteStr, process_template_placeholders}; +use crate::cmus::Track; +use crate::cmus::player_settings::PlayerSettings; use crate::notification::Action; use crate::settings::Settings; @@ -37,20 +37,47 @@ impl CmusEvent { (settings.aaa_mode_notification_body(), settings.aaa_mode_notification_summary(), settings.aaa_mode_notification_timeout(), track, player_settings), _ => { return Action::None }, }; - + + let persistent = is_mutable(&body_template) || is_mutable(&summary_template); + Action::Show { - body: process_template_placeholders( - body_template, - track, - player_settings, - ), - summary: process_template_placeholders( - summary_template, - track, - player_settings, - ), - timeout: timeout * 1000 , - save: false, + body: CompleteStr { + template: body_template.clone(), + str: process_template_placeholders( + body_template, + track, + player_settings, + ), + }, + summary: CompleteStr { + template: summary_template.clone(), + str: process_template_placeholders( + summary_template, + track, + player_settings, + ), + }, + timeout: if persistent { 0 } else { timeout * 1000 }, + save: persistent, } } } + +fn is_mutable(template: &str) -> bool { + let mut key = String::new(); // Just a buffer to build the key. + + for c in template.chars() { + if c == '{' { + key = String::new(); + } else if c == '}' { + match key.as_str() { + "lyrics" | "progress" | "progress_bar" => return true, + _ => {} + } + } else { + key.push(c); + } + } + + false +} diff --git a/src/cmus/mod.rs b/src/cmus/mod.rs index 49be5e6..28236b8 100644 --- a/src/cmus/mod.rs +++ b/src/cmus/mod.rs @@ -94,7 +94,7 @@ impl TemplateProcessor for Track { /// Process the template with the track metadata. /// The template is a string with placeholders that will be replaced with the track metadata. /// The unknown placeholders will be skipped (don't replaced with anything, because they are maybe placeholders for player settings). - #[inline(always)] + #[inline] fn process(&self, template: String) -> String { #[cfg(feature = "debug")] { @@ -107,14 +107,14 @@ impl TemplateProcessor for Track { Self::get_keys(template.as_str()).iter().for_each(|key| { #[cfg(feature = "debug")] debug!("Replacing the placeholder {{{key}}} with its matching value."); - // Replace the key with their matching value if exists, if not replace with the empty string. - let status = self.status.to_string(); + // Replace the key with their matching value if exists if let Some(value) = match key.as_str() { - "status" => Some(status.as_str()), - "title" => Some(self.get_name()), - _ => self.metadata.get(key), + "status" => Some(self.status.to_string()), + "title" => Some(self.get_name().to_string()), + "progress" => Some(format!("{:.2}/{:.2}", self.duration as f32 / 60.0, self.position as f32 / 60.0)), + _ => self.metadata.get(key).map(|r| r.to_string()), } { - processed = processed.replace(&format!("{{{key}}}"), value); + processed = processed.replace(&format!("{{{key}}}"), value.as_str()); } }); diff --git a/src/lib.rs b/src/lib.rs index a65d460..98f2b4e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -112,6 +112,11 @@ pub fn search_for( } } +pub struct CompleteStr { + pub template: String, + pub str: String +} + /// The cover of a track. #[derive(PartialEq)] #[cfg_attr(any(feature = "debug", test), derive(Debug))] diff --git a/src/notification.rs b/src/notification.rs index b1afb45..d3e9d03 100644 --- a/src/notification.rs +++ b/src/notification.rs @@ -1,18 +1,18 @@ #[cfg(feature = "debug")] -use log::{info}; +use log::info; use notify_rust::Notification; -use crate::{track_cover, TrackCover}; +use crate::{CompleteStr, track_cover, TrackCover}; use crate::cmus::{TemplateProcessor, Track}; use crate::cmus::events::CmusEvent; - +use crate::cmus::player_settings::PlayerSettings; use crate::cmus::query::CmusQueryResponse; use crate::settings::Settings; pub enum Action { Show { - body: String, - summary: String, + body: CompleteStr, + summary:CompleteStr, timeout: i32, save: bool, }, @@ -22,16 +22,33 @@ pub enum Action { pub struct NotificationsHandler { cover_set: bool, notification: Notification, - handlers: Vec, + notifications: Vec, settings: Settings, } +struct CmusNotification { + body_template: String, + summary_template: String, + visible: bool, + handle: notify_rust::NotificationHandle +} + +impl CmusNotification { + #[inline(always)] + fn update(&mut self, track: &Track, player_settings: &PlayerSettings) { + use crate::process_template_placeholders; + self.handle.summary(&process_template_placeholders(self.summary_template.clone(), track, player_settings)) + .body(&process_template_placeholders(self.body_template.clone(), track, player_settings)); + self.handle.update(); + } +} + impl NotificationsHandler { pub fn new(settings: Settings) -> Self { Self { cover_set: false, notification: Notification::new(), - handlers: Vec::with_capacity(2), + notifications: Vec::with_capacity(2), settings, } } @@ -46,6 +63,22 @@ impl NotificationsHandler { #[cfg(feature = "debug")] info!("event: {:?}", event); + if let CmusEvent::PositionChanged(track, player_settings) = &event { + for notification in &mut self.notifications { + if notification.visible { + notification.update(track, player_settings); + } + } + continue; + } else if let CmusEvent::TrackChanged(_, _) = &event { + for notification in &mut self.notifications { + notification.handle.timeout = 2.into(); // Hide the notification after 2 millisecond + notification.handle.update(); + } + // Clean the notifications vec + self.notifications.clear(); + } + match event.build_notification(&self.settings) { Action::Show { body, summary, timeout, save } => { // Setup the notification cover @@ -58,12 +91,24 @@ impl NotificationsHandler { self.cover_set = true; } - self.notification.timeout(timeout).summary(&summary).body(&body); + self.notification.timeout(timeout).summary(&summary.str).body(&body.str); // Show the notification - let handle = self.notification.show()?; + let mut handle = self.notification.show()?; if save { - self.handlers.push(handle); + // Add the close handler + /*handle.on_close(|reason| { + + });*/ + + self.notifications.push( + CmusNotification { + body_template: body.template, + summary_template: summary.template, + visible: true, + handle + } + ) } } Action::None => {}