cmus_notify/
notification.rs

1#[cfg(feature = "debug")]
2use log::info;
3use notify_rust::Notification;
4
5use crate::{CompleteStr, track_cover, TrackCover};
6use crate::cmus::{TemplateProcessor, Track};
7use crate::cmus::events::CmusEvent;
8use crate::cmus::player_settings::PlayerSettings;
9use crate::cmus::query::CmusQueryResponse;
10use crate::settings::Settings;
11
12pub enum Action {
13    Show {
14        body: CompleteStr,
15        summary:CompleteStr,
16        timeout: i32,
17        save: bool,
18    },
19    None,
20}
21
22pub struct NotificationsHandler {
23    cover_set: bool,
24    notification: Notification,
25    notifications: Vec<CmusNotification>,
26    settings: Settings,
27}
28
29struct CmusNotification {
30    body_template: String,
31    summary_template: String,
32    visible: bool,
33    handle: notify_rust::NotificationHandle
34}
35
36impl CmusNotification {
37    #[inline(always)]
38    fn update(&mut self, track: &Track, player_settings: &PlayerSettings) {
39        use crate::process_template_placeholders;
40        self.handle.summary(&process_template_placeholders(self.summary_template.clone(), track, player_settings))
41            .body(&process_template_placeholders(self.body_template.clone(), track, player_settings));
42        self.handle.update();
43    }
44}
45
46impl NotificationsHandler {
47    pub fn new(settings: Settings) -> Self {
48        Self {
49            cover_set: false,
50            notification: Notification::new(),
51            notifications: Vec::with_capacity(2),
52            settings,
53        }
54    }
55
56    #[inline]
57    pub fn show_notification(
58        &mut self,
59        events: Vec<CmusEvent>,
60        response: &CmusQueryResponse,
61    ) -> Result<(), notify_rust::error::Error> {
62        for event in events {
63            #[cfg(feature = "debug")]
64            info!("event: {:?}", event);
65
66            if let CmusEvent::PositionChanged(track, player_settings) = &event {
67                for notification in &mut self.notifications {
68                    if notification.visible {
69                        notification.update(track, player_settings);
70                    }
71                }
72                continue;
73            } else if let CmusEvent::TrackChanged(_, _) = &event {
74                for notification in &mut self.notifications {
75                    notification.handle.timeout = 2.into(); // Hide the notification after 2 millisecond
76                    notification.handle.update();
77                }
78                // Clean the notifications vec
79                self.notifications.clear();
80            }
81
82            match event.build_notification(&self.settings) {
83                Action::Show { body, summary, timeout, save } => {
84                    // Setup the notification cover
85                    if self.settings.show_track_cover {
86                        self.update_cover(&event, response);
87                    } else if self.settings.notification_static_cover.is_some() && !self.cover_set {
88                        self.setup_the_notification();
89                        self.notification
90                            .image_path(self.settings.notification_static_cover.as_ref().unwrap());
91                        self.cover_set = true;
92                    }
93
94                    self.notification.timeout(timeout).summary(&summary.str).body(&body.str);
95
96                    // Show the notification
97                    let mut handle = self.notification.show()?;
98                    if save {
99                        // Add the close handler
100                        /*handle.on_close(|reason| {
101
102                        });*/
103
104                        self.notifications.push(
105                            CmusNotification {
106                                body_template: body.template,
107                                summary_template: summary.template,
108                                visible: true,
109                                handle
110                            }
111                        )
112                    }
113                }
114                Action::None => {}
115            };
116        }
117
118        Ok(())
119    }
120
121    #[inline(always)]
122    fn update_cover(&mut self, event: &CmusEvent, response: &CmusQueryResponse) {
123        // If the track is changed, we need to update the cover.
124        match event {
125            CmusEvent::TrackChanged(track, _) => {
126                // Reset the notification
127                self.setup_the_notification();
128                self.set_cover(track);
129            }
130            _ => {
131                if !self.cover_set {
132                    // If the cover is not found, we need to update it.
133                    if let Ok(track) = response.track() {
134                        self.set_cover(&track);
135                    }
136                }
137            }
138        };
139    }
140
141    #[inline]
142    fn set_cover(&mut self, track: &Track) {
143        let path = match &self.settings.cover_path_template {
144            Some(template) => track.process(template.clone()),
145            None => track.path.clone(),
146        };
147        // Get the track cover and set it to notification
148        let track_cover = track_cover(
149            path,
150            track.get_name(),
151            self.settings.depth(),
152            self.settings.force_use_external_cover,
153            self.settings.no_use_external_cover,
154        );
155
156        if track_cover != TrackCover::None {
157            track_cover.set_notification_image(&mut self.notification);
158        } else if self.settings.notification_static_cover.is_some() {
159            self.notification
160                .image_path(self.settings.notification_static_cover.as_ref().unwrap());
161        }
162
163        // Flip the change flag
164        self.cover_set = true;
165    }
166
167    #[inline(always)]
168    fn setup_the_notification(&mut self) {
169        self.notification = Notification::new();
170        self.notification
171            .appname(self.settings.app_name().as_str())
172            .hint(notify_rust::Hint::Category("music".to_string()))
173            .hint(notify_rust::Hint::DesktopEntry("cmus.desktop".to_string()))
174            .hint(notify_rust::Hint::Resident(true));
175    }
176}