summaryrefslogtreecommitdiffstats
path: root/src/component/title.rs
blob: 092550e2210b609e40e18b1114b965d897df2a03 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
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(())
    }
}