diff options
Diffstat (limited to 'src/component/title.rs')
-rw-r--r-- | src/component/title.rs | 116 |
1 files changed, 116 insertions, 0 deletions
diff --git a/src/component/title.rs b/src/component/title.rs new file mode 100644 index 0000000..092550e --- /dev/null +++ b/src/component/title.rs @@ -0,0 +1,116 @@ +use std::{collections::HashMap, sync::Arc, time::Duration}; + +use tokio::{ + sync::{mpsc::Sender, Mutex}, + task::{AbortHandle, JoinSet}, +}; +use zbus::zvariant::OwnedValue; + +use crate::{ + dbus::player::{PlaybackStatus, PlayerProxy}, + i3bar::Block, + Error, +}; + +use super::{Component, Update}; + +const TICK_RATE: Duration = Duration::from_millis(500); + +pub struct Title; + +impl Component for Title { + const NAME: &'static str = "title"; + type Updater = Self; + type Colorer = PlaybackStatus; + type Handler = (); +} + +impl Update for Title { + type Value = String; + + async fn listen(tx: Sender<Self::Value>, proxy: PlayerProxy<'_>) -> Result<(), Error> { + use futures_util::StreamExt; + + let mut join_set = JoinSet::new(); + let mut rotator = None; + let mut old_title = None; + + Self::handle_metadata( + tx.clone(), + proxy.metadata().await?, + &mut old_title, + &mut rotator, + &mut join_set, + ) + .await?; + + let mut stream = proxy.receive_metadata_changed().await; + while let Some(signal) = stream.next().await { + if let Ok(metadata) = signal.get().await { + Self::handle_metadata( + tx.clone(), + metadata, + &mut old_title, + &mut rotator, + &mut join_set, + ) + .await?; + } + } + Ok(()) + } + + async fn update(mut value: Self::Value, block: Arc<Mutex<Block>>) -> Result<bool, Error> { + value.push(' '); + let mut block = block.lock().await; + block.full_text = value; + + Ok(true) + } +} + +impl Title { + async fn handle_metadata( + tx: Sender<<<Self as Component>::Updater as Update>::Value>, + metadata: HashMap<String, OwnedValue>, + old_title: &mut Option<String>, + rotator: &mut Option<AbortHandle>, + join_set: &mut JoinSet<Result<(), Error>>, + ) -> Result<(), Error> { + let Some(owned_value) = metadata.get("xesam:title") else { + return Ok(()); + }; + + let title: String = owned_value.try_to_owned()?.try_into()?; + + if old_title.as_ref().is_some_and(|s| *s == title) { + return Ok(()); + } + + if let Some(h) = rotator.take() { + h.abort(); + }; + + *old_title = Some(title.clone()); + + if title.len() >= 10 { + let tx = tx.clone(); + let mut chars = title.clone().chars().collect::<Vec<char>>(); + chars.push(' '); + *rotator = Some(join_set.spawn(async move { + let mut interval = tokio::time::interval(TICK_RATE); + loop { + interval.tick().await; + tx.send(String::from_iter(chars[0..10].iter())) + .await + .unwrap(); + chars.rotate_left(1); + } + })); + } else { + tx.send(title).await?; + } + + Ok(()) + } +} |