summaryrefslogtreecommitdiffstats
path: root/src/lib.rs
blob: d369be574d255f580d7c67d0e2df9c20cb61b33f (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
use std::sync::Arc;

use futures_util::StreamExt;
use tokio::{sync::Mutex, task::JoinSet};
use zbus::Connection;

pub use crate::{
    component::{Button, Component, Update},
    error::{Error, Result},
};
use crate::{
    dbus::{player::PlayerProxy, playerctld::PlayerctldProxy},
    i3bar::{Block, Click},
};

pub mod component;
pub mod dbus;
pub mod error;
pub mod i3bar;

const IGNORED: [&str; 2] = ["playerctld", "kdeconnect"];

pub async fn run<C>(mut block: Block) -> Result<(), Error>
where
    C: Component,
{
    let conn = Connection::session().await?;

    block.name = Some(format!("mpris-{}", C::NAME));
    tokio::spawn(listeners::<C>(conn.clone(), block));

    for click in std::io::stdin()
        .lines()
        .map_while(Result::ok)
        .flat_map(|s| serde_json::from_str::<Click>(&s))
    {
        <<C as Component>::Handler as Button>::handle(conn.clone(), click).await?;
    }

    Ok(())
}

async fn listeners<C>(conn: Connection, block: Block) -> Result<(), Error>
where
    C: Component,
{
    let mut join_set = JoinSet::new();

    let (tx_player, mut rx_player) = tokio::sync::mpsc::channel(128);
    let (tx_status, mut rx_status) = tokio::sync::mpsc::channel(128);
    let (tx_value, mut rx_value) = tokio::sync::mpsc::channel(128);

    let proxy = PlayerctldProxy::builder(&conn).build().await?;

    tokio::spawn(async move {
        let mut last = proxy
            .player_names()
            .await?
            .into_iter()
            .find(|s| s.split('.').nth(3).is_some_and(|s| !IGNORED.contains(&s)))
            .unwrap_or_default();
        tx_player.send(last.clone()).await?;
        let mut stream = proxy.receive_active_player_change_end().await?;
        while let Some(signal) = stream.next().await {
            let name = signal.args()?.name.to_owned();
            if name != last
                && name
                    .split('.')
                    .nth(3)
                    .is_some_and(|s| !IGNORED.contains(&s))
            {
                last.clone_from(&name);
                tx_player.send(name).await?;
            }
        }
        Result::<_, Error>::Ok(())
    });

    let block = Arc::new(Mutex::new(block));

    loop {
        let updated = tokio::select! {
            Some(name) = rx_player.recv() => {
                join_set.shutdown().await;
                if name.is_empty() {
                    let mut block = block.lock().await;
                    block.full_text = String::new();
                    block.instance = None;
                    true
                } else {
                    let mut block = block.lock().await;
                    block.instance.clone_from(&Some(name.clone()));
                    let proxy = PlayerProxy::builder(&conn)
                        .destination(name)?
                        .build()
                        .await?;
                    join_set.spawn(<<C as Component>::Colorer as Update>::listen(tx_status.clone(), proxy.clone()));
                    join_set.spawn(<<C as Component>::Updater as Update>::listen(tx_value.clone(), proxy));
                    false
                }
            }
            Some(color) = rx_status.recv() => <<C as Component>::Colorer as Update>::update(color, block.clone()).await?,
            Some(value) = rx_value.recv() => <<C as Component>::Updater as Update>::update(value, block.clone()).await?
        };

        if updated {
            let s = block.lock().await;
            s.write_stdout()?;
        }
    }
}