use std::{path::PathBuf, fs, sync::{Arc, Mutex}, time::Duration}; use clap::Parser; use mpris::{PlayerFinder, PlaybackStatus}; use serde::{Serialize, Deserialize}; use tokio::{net::{UnixListener, UnixStream}, time, task}; use tokio_util::codec::{LengthDelimitedCodec, Framed}; use futures::{prelude::stream::StreamExt, SinkExt}; #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub enum Request { GetLastActive, } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub enum Response { None, Players(Vec), } #[derive(Debug, Parser)] pub struct Cli { #[arg(short = 'c', long, default_value = r"config.json", value_hint = clap::ValueHint::FilePath)] pub config: PathBuf, #[arg(short = 's', long, default_value = r"/tmp/mpris-ctl.sock", value_hint = clap::ValueHint::FilePath)] pub socket: PathBuf } type SharedData = Arc>>; #[tokio::main] async fn main() { let args = Cli::parse(); if args.socket.exists() { fs::remove_file(&args.socket).unwrap(); } let last_active_players = Arc::new(Mutex::new(Vec::::new())); let last_active_mpris = last_active_players.clone(); let _mpris_handle = tokio::spawn(async move { handle_mpris_daemon(last_active_mpris).await; }); let listener = UnixListener::bind(&args.socket).expect("failed to bind socket"); loop { let last_active_listener = last_active_players.clone(); match listener.accept().await { Ok((stream, _addr)) => { tokio::spawn(async move { process(stream, last_active_listener).await; }); }, Err(e) => { eprintln!("{:?}", e); } } } } async fn handle_mpris_daemon(shared_data: SharedData) { let mut interval = time::interval(Duration::from_millis(250)); loop { interval.tick().await; let player_finder = PlayerFinder::new().expect("Could not connect to D-Bus"); // get active players let active_players: Vec = player_finder .iter_players() .expect("Could not iterate players") .map(|x| x.expect("Could not get one of the players")) .filter(|x| x.get_playback_status().expect("Cannot get playback status") == PlaybackStatus::Playing) .map(|x| String::from(x.identity())) .collect(); if active_players.len() == 0 { continue; // there is nothing to do } // change active players let mut guard = shared_data.lock().expect("Could not lock the mutex"); *guard = active_players; } } async fn process(stream: UnixStream, shared_data: SharedData) { let mut framed = Framed::new(stream, LengthDelimitedCodec::new()); while let Some(frame) = framed.next().await { match frame { Ok(data) => { let request: serde_json::Result = serde_json::from_slice(&data); if request.is_err() { eprintln!("Could not parse the frame from client: {:?}", request.err()); break; } let request = request.unwrap(); let response = handle_request(request, shared_data.clone()).await; let buffer = serde_json::to_vec(&response).unwrap(); let send = framed.send(buffer.into()).await; if send.is_err() { eprintln!("Could not send to the client: {:?}", send.err()); break; } }, Err(err) => { eprintln!("Could not read from client: {:?}", err); break; } } } task::yield_now().await; } async fn handle_request(request: Request, shared_data: SharedData) -> Response { match request { Request::GetLastActive => { let guard = shared_data.lock().expect("Could not lock the mutex."); Response::Players(guard.clone()) }, _ => Response::None } }