#[cfg(feature = "album-art")]
use crate::art::ArtFetcher;
use crate::mpris::MprisPropertiesChange;
use crate::mpris::PlayerMetadata;
use crate::mpris::PlayerStatus;
use crate::notifier::Notification;
use crate::DBusError;
use crate::{configuration::Configuration, dbus::DBusConnection, notifier::Notifier};
use rustbus::message_builder::MarshalledMessage;
use std::collections::HashMap;
use std::time::Duration;
use std::time::Instant;
use thiserror::Error;
const NOTIFICATION_DELAY: Duration = Duration::from_millis(250);
#[derive(Debug, Error)]
pub enum SignalHandlerError {
#[error("error handling D-Bus signal")]
DBus(#[from] DBusError),
}
pub struct SignalHandler {
configuration: Configuration,
notifier: Notifier,
art_fetcher: ArtFetcher,
metadata: HashMap<String, PlayerMetadata>,
pending_notification: Option<Notification>,
}
impl SignalHandler {
pub fn new(configuration: &Configuration) -> Self {
Self {
configuration: configuration.clone(),
notifier: Notifier::new(configuration),
art_fetcher: ArtFetcher::new(configuration),
metadata: HashMap::new(),
pending_notification: None,
}
}
pub fn handle_pending(&mut self, dbus: &mut DBusConnection) -> Result<(), SignalHandlerError> {
if let Some(pending) = &self.pending_notification {
let delta = Instant::now() - pending.last_touched();
if delta > NOTIFICATION_DELAY {
self.notifier
.send_notification(self.pending_notification.take().unwrap(), dbus)?;
}
}
Ok(())
}
pub fn handle_signal(&mut self, signal: MarshalledMessage) -> Result<(), SignalHandlerError> {
let sender = signal
.dynheader
.sender
.as_ref()
.ok_or_else(|| DBusError::Invalid("Missing sender header".to_string()))?
.clone();
let change = MprisPropertiesChange::try_from(signal).ok();
if change.is_none() {
return Ok(());
}
let change = change.unwrap();
let mut metadata: Option<&PlayerMetadata> = self.metadata.get(&sender);
if let Some(new_metadata) = change.metadata {
self.metadata
.insert(sender.to_string(), new_metadata.clone());
metadata = self.metadata.get(&sender);
let pending = self.pending_notification.as_mut();
if let Some(pending) = pending {
if pending.sender() == sender {
pending.update(&new_metadata, None);
}
} else {
self.pending_notification = Some(Notification::new(&sender, &new_metadata, None));
}
}
if metadata.is_none() {
return Ok(());
}
let metadata = metadata.unwrap();
if let Some(status) = change.status {
if status == PlayerStatus::Playing {
self.pending_notification = Some(Notification::new(&sender, metadata, None));
} else {
self.pending_notification = None;
return Ok(());
}
}
let pending = self.pending_notification.as_mut().unwrap();
#[cfg(feature = "album-art")]
if metadata.art_url.is_some() && self.configuration.enable_album_art {
let result = self
.art_fetcher
.get_album_art(metadata.art_url.as_ref().unwrap());
match result {
Ok(data) => {
pending.update(metadata, Some(data));
}
Err(err) => {
log::warn!("Error fetching album art for {:#?}: {}", &metadata, err);
}
}
}
Ok(())
}
}