[implement] Implement the `{progress}` placeholderr

This commit is contained in:
Anas Elgarhy 2023-04-23 05:16:40 +02:00
parent 44bb297e78
commit 0eed8c5360
No known key found for this signature in database
GPG Key ID: 0501802A1D496528
4 changed files with 110 additions and 33 deletions

View File

@ -1,6 +1,6 @@
use crate::{process_template_placeholders}; use crate::{CompleteStr, process_template_placeholders};
use crate::cmus::{Track}; use crate::cmus::Track;
use crate::cmus::player_settings::{PlayerSettings}; use crate::cmus::player_settings::PlayerSettings;
use crate::notification::Action; use crate::notification::Action;
use crate::settings::Settings; 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), (settings.aaa_mode_notification_body(), settings.aaa_mode_notification_summary(), settings.aaa_mode_notification_timeout(), track, player_settings),
_ => { return Action::None }, _ => { return Action::None },
}; };
let persistent = is_mutable(&body_template) || is_mutable(&summary_template);
Action::Show { Action::Show {
body: process_template_placeholders( body: CompleteStr {
body_template, template: body_template.clone(),
track, str: process_template_placeholders(
player_settings, body_template,
), track,
summary: process_template_placeholders( player_settings,
summary_template, ),
track, },
player_settings, summary: CompleteStr {
), template: summary_template.clone(),
timeout: timeout * 1000 , str: process_template_placeholders(
save: false, 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
}

View File

@ -94,7 +94,7 @@ impl TemplateProcessor for Track {
/// Process the template with the track metadata. /// Process the template with the track metadata.
/// The template is a string with placeholders that will be replaced 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). /// 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 { fn process(&self, template: String) -> String {
#[cfg(feature = "debug")] #[cfg(feature = "debug")]
{ {
@ -107,14 +107,14 @@ impl TemplateProcessor for Track {
Self::get_keys(template.as_str()).iter().for_each(|key| { Self::get_keys(template.as_str()).iter().for_each(|key| {
#[cfg(feature = "debug")] #[cfg(feature = "debug")]
debug!("Replacing the placeholder {{{key}}} with its matching value."); 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. // Replace the key with their matching value if exists
let status = self.status.to_string();
if let Some(value) = match key.as_str() { if let Some(value) = match key.as_str() {
"status" => Some(status.as_str()), "status" => Some(self.status.to_string()),
"title" => Some(self.get_name()), "title" => Some(self.get_name().to_string()),
_ => self.metadata.get(key), "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());
} }
}); });

View File

@ -112,6 +112,11 @@ pub fn search_for(
} }
} }
pub struct CompleteStr {
pub template: String,
pub str: String
}
/// The cover of a track. /// The cover of a track.
#[derive(PartialEq)] #[derive(PartialEq)]
#[cfg_attr(any(feature = "debug", test), derive(Debug))] #[cfg_attr(any(feature = "debug", test), derive(Debug))]

View File

@ -1,18 +1,18 @@
#[cfg(feature = "debug")] #[cfg(feature = "debug")]
use log::{info}; use log::info;
use notify_rust::Notification; use notify_rust::Notification;
use crate::{track_cover, TrackCover}; use crate::{CompleteStr, track_cover, TrackCover};
use crate::cmus::{TemplateProcessor, Track}; use crate::cmus::{TemplateProcessor, Track};
use crate::cmus::events::CmusEvent; use crate::cmus::events::CmusEvent;
use crate::cmus::player_settings::PlayerSettings;
use crate::cmus::query::CmusQueryResponse; use crate::cmus::query::CmusQueryResponse;
use crate::settings::Settings; use crate::settings::Settings;
pub enum Action { pub enum Action {
Show { Show {
body: String, body: CompleteStr,
summary: String, summary:CompleteStr,
timeout: i32, timeout: i32,
save: bool, save: bool,
}, },
@ -22,16 +22,33 @@ pub enum Action {
pub struct NotificationsHandler { pub struct NotificationsHandler {
cover_set: bool, cover_set: bool,
notification: Notification, notification: Notification,
handlers: Vec<notify_rust::NotificationHandle>, notifications: Vec<CmusNotification>,
settings: Settings, 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 { impl NotificationsHandler {
pub fn new(settings: Settings) -> Self { pub fn new(settings: Settings) -> Self {
Self { Self {
cover_set: false, cover_set: false,
notification: Notification::new(), notification: Notification::new(),
handlers: Vec::with_capacity(2), notifications: Vec::with_capacity(2),
settings, settings,
} }
} }
@ -46,6 +63,22 @@ impl NotificationsHandler {
#[cfg(feature = "debug")] #[cfg(feature = "debug")]
info!("event: {:?}", event); 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) { match event.build_notification(&self.settings) {
Action::Show { body, summary, timeout, save } => { Action::Show { body, summary, timeout, save } => {
// Setup the notification cover // Setup the notification cover
@ -58,12 +91,24 @@ impl NotificationsHandler {
self.cover_set = true; 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 // Show the notification
let handle = self.notification.show()?; let mut handle = self.notification.show()?;
if save { 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 => {} Action::None => {}