summaryrefslogtreecommitdiffstats
path: root/src/component/title.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/component/title.rs')
-rw-r--r--src/component/title.rs116
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(())
+ }
+}