[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::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
}

View File

@ -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());
}
});

View File

@ -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))]

View File

@ -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<notify_rust::NotificationHandle>,
notifications: Vec<CmusNotification>,
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 => {}