2022-11-15 00:34:27 +00:00
|
|
|
// Copyright (c) 2022 Marceline Cramer
|
|
|
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
|
|
|
2022-11-17 22:03:12 +00:00
|
|
|
use std::{os::unix::net::UnixStream, path::Path};
|
|
|
|
|
|
|
|
use canary_magpie::protocol::{ClientMessenger, CreatePanel, MagpieServerMsg, MAGPIE_SOCK};
|
2022-11-16 04:41:13 +00:00
|
|
|
use canary_music_player::*;
|
2022-10-31 05:31:54 +00:00
|
|
|
use mpris::PlayerFinder;
|
2022-09-24 18:54:55 +00:00
|
|
|
|
2022-11-17 22:03:12 +00:00
|
|
|
pub type MagpieClient<'a> = ClientMessenger<&'a UnixStream>;
|
|
|
|
|
2022-10-31 05:31:54 +00:00
|
|
|
pub struct MetadataTracker {
|
|
|
|
pub album: AlbumInfo,
|
|
|
|
pub track: TrackInfo,
|
|
|
|
}
|
|
|
|
|
2022-11-16 04:41:13 +00:00
|
|
|
impl From<&mpris::Metadata> for MetadataTracker {
|
|
|
|
fn from(metadata: &mpris::Metadata) -> Self {
|
2022-10-31 05:31:54 +00:00
|
|
|
let album = AlbumInfo {
|
|
|
|
title: metadata.album_name().map(ToString::to_string),
|
|
|
|
artists: metadata
|
|
|
|
.album_artists()
|
|
|
|
.unwrap_or(Vec::new())
|
|
|
|
.iter()
|
|
|
|
.map(ToString::to_string)
|
|
|
|
.collect(),
|
|
|
|
};
|
|
|
|
|
|
|
|
let track = TrackInfo {
|
|
|
|
title: metadata.title().map(ToString::to_string),
|
|
|
|
artists: metadata
|
|
|
|
.artists()
|
|
|
|
.unwrap_or(Vec::new())
|
|
|
|
.iter()
|
|
|
|
.map(ToString::to_string)
|
|
|
|
.collect(),
|
|
|
|
track_number: metadata.track_number(),
|
|
|
|
};
|
2022-09-24 18:54:55 +00:00
|
|
|
|
2022-10-31 05:31:54 +00:00
|
|
|
Self { album, track }
|
|
|
|
}
|
2022-09-24 18:54:55 +00:00
|
|
|
}
|
|
|
|
|
2022-10-31 05:31:54 +00:00
|
|
|
impl MetadataTracker {
|
2022-11-16 04:41:13 +00:00
|
|
|
pub fn new(magpie: &mut MagpieClient, metadata: &mpris::Metadata) -> Self {
|
2022-10-31 05:31:54 +00:00
|
|
|
let new: Self = metadata.into();
|
2022-11-17 22:03:12 +00:00
|
|
|
magpie.send_panel_json(0, &InMsg::AlbumChanged(new.album.clone()));
|
|
|
|
magpie.send_panel_json(0, &InMsg::TrackChanged(new.track.clone()));
|
|
|
|
magpie.send_panel_json(
|
2022-11-16 04:41:13 +00:00
|
|
|
0,
|
|
|
|
&InMsg::ProgressChanged(ProgressChanged {
|
|
|
|
position: 0.0,
|
|
|
|
length: metadata.length().map(|l| l.as_secs_f32()),
|
|
|
|
}),
|
|
|
|
);
|
2022-10-31 05:31:54 +00:00
|
|
|
new
|
2022-09-24 18:54:55 +00:00
|
|
|
}
|
|
|
|
|
2022-11-16 04:41:13 +00:00
|
|
|
pub fn update(&mut self, messenger: &mut MagpieClient, metadata: &mpris::Metadata) {
|
2022-10-31 05:31:54 +00:00
|
|
|
let new: Self = metadata.into();
|
2022-09-24 18:54:55 +00:00
|
|
|
|
2022-10-31 05:31:54 +00:00
|
|
|
if self.album != new.album {
|
2022-11-17 22:03:12 +00:00
|
|
|
messenger.send_panel_json(0, &InMsg::AlbumChanged(new.album.clone()));
|
2022-10-31 05:31:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if self.track != new.track {
|
2022-11-17 22:03:12 +00:00
|
|
|
messenger.send_panel_json(0, &InMsg::TrackChanged(new.track.clone()));
|
|
|
|
messenger.send_panel_json(
|
2022-11-16 04:41:13 +00:00
|
|
|
0,
|
|
|
|
&InMsg::ProgressChanged(ProgressChanged {
|
|
|
|
position: 0.0,
|
|
|
|
length: metadata.length().map(|l| l.as_secs_f32()),
|
|
|
|
}),
|
|
|
|
);
|
2022-10-31 05:31:54 +00:00
|
|
|
}
|
2022-09-24 18:54:55 +00:00
|
|
|
|
2022-10-31 05:31:54 +00:00
|
|
|
*self = new;
|
2022-09-24 18:54:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
let args: Vec<String> = std::env::args().collect();
|
|
|
|
let module_path = args
|
|
|
|
.get(1)
|
|
|
|
.expect("Please pass a path to a Canary script!")
|
|
|
|
.to_owned();
|
|
|
|
|
2022-10-30 05:26:02 +00:00
|
|
|
let player_finder = PlayerFinder::new().expect("Could not connect to D-Bus");
|
2022-09-24 18:54:55 +00:00
|
|
|
|
2022-11-17 22:03:12 +00:00
|
|
|
let sock_dir = std::env::var("XDG_RUNTIME_DIR").expect("XDG_RUNTIME_DIR not set");
|
|
|
|
let sock_dir = Path::new(&sock_dir);
|
|
|
|
let sock_path = sock_dir.join(MAGPIE_SOCK);
|
|
|
|
let socket = UnixStream::connect(sock_path).unwrap();
|
|
|
|
let mut magpie = MagpieClient::new(&socket);
|
2022-11-16 06:52:11 +00:00
|
|
|
let protocol = "tebibyte-media.desktop.music-player-controller".to_string();
|
2022-10-30 05:26:02 +00:00
|
|
|
let script = std::path::PathBuf::from(&module_path);
|
2022-11-17 22:03:12 +00:00
|
|
|
let msg = CreatePanel {
|
|
|
|
id: 0,
|
|
|
|
protocol,
|
|
|
|
script,
|
|
|
|
};
|
|
|
|
|
2022-10-30 05:26:02 +00:00
|
|
|
let msg = MagpieServerMsg::CreatePanel(msg);
|
2022-11-17 22:03:12 +00:00
|
|
|
magpie.send(&msg).unwrap();
|
2022-09-24 18:54:55 +00:00
|
|
|
|
2022-10-31 05:31:54 +00:00
|
|
|
let mut first_loop = true;
|
|
|
|
let mut connected = false;
|
|
|
|
|
2022-10-30 05:26:02 +00:00
|
|
|
loop {
|
2022-10-31 05:31:54 +00:00
|
|
|
if !first_loop {
|
|
|
|
let wait = std::time::Duration::from_secs(1);
|
|
|
|
std::thread::sleep(wait);
|
|
|
|
}
|
|
|
|
|
|
|
|
first_loop = false;
|
2022-10-30 05:26:02 +00:00
|
|
|
|
2022-10-31 05:31:54 +00:00
|
|
|
if connected {
|
|
|
|
println!("Disconnected from MPRIS");
|
|
|
|
let msg = InMsg::Disconnected;
|
2022-11-17 22:03:12 +00:00
|
|
|
magpie.send_panel_json(0, &msg);
|
2022-10-31 05:31:54 +00:00
|
|
|
connected = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
println!("Connecting to MPRIS...");
|
|
|
|
|
|
|
|
let player = match player_finder.find_active() {
|
|
|
|
Ok(player) => player,
|
2022-10-30 05:26:02 +00:00
|
|
|
Err(err) => {
|
|
|
|
eprintln!("Couldn't find player: {:?}", err);
|
|
|
|
continue;
|
2022-09-24 18:54:55 +00:00
|
|
|
}
|
2022-10-30 05:26:02 +00:00
|
|
|
};
|
|
|
|
|
2022-10-31 05:31:54 +00:00
|
|
|
println!(
|
|
|
|
"Connected to \"{}\" ({})",
|
|
|
|
player.identity(),
|
|
|
|
player.bus_name()
|
|
|
|
);
|
|
|
|
connected = true;
|
2022-11-17 22:03:12 +00:00
|
|
|
magpie.send_panel_json(0, &InMsg::Connected);
|
2022-10-31 05:31:54 +00:00
|
|
|
|
|
|
|
let metadata = player.get_metadata().unwrap();
|
2022-11-16 04:41:13 +00:00
|
|
|
let mut metadata_tracker = MetadataTracker::new(&mut magpie, &metadata);
|
2022-10-31 05:31:54 +00:00
|
|
|
|
|
|
|
let mut events = match player.events() {
|
|
|
|
Ok(events) => events,
|
|
|
|
Err(err) => {
|
|
|
|
eprintln!("Player events D-Bus error: {:?}", err);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
loop {
|
|
|
|
let event = match events.next() {
|
|
|
|
None => break,
|
|
|
|
Some(Ok(e)) => e,
|
|
|
|
Some(Err(err)) => {
|
|
|
|
eprintln!("D-Bus error while reading player events: {:?}", err);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
use mpris::Event::*;
|
|
|
|
let in_msg = match event {
|
|
|
|
Playing => Some(InMsg::PlaybackStatusChanged(PlaybackStatus::Playing)),
|
|
|
|
Paused => Some(InMsg::PlaybackStatusChanged(PlaybackStatus::Paused)),
|
|
|
|
Stopped => Some(InMsg::PlaybackStatusChanged(PlaybackStatus::Stopped)),
|
|
|
|
LoopingChanged(status) => {
|
|
|
|
use mpris::LoopStatus::*;
|
|
|
|
let status = match status {
|
|
|
|
None => LoopStatus::None,
|
|
|
|
Track => LoopStatus::Track,
|
|
|
|
Playlist => LoopStatus::Playlist,
|
|
|
|
};
|
2022-10-30 05:26:02 +00:00
|
|
|
|
2022-10-31 05:31:54 +00:00
|
|
|
Some(InMsg::LoopingChanged(status))
|
|
|
|
}
|
|
|
|
ShuffleToggled(shuffle) => Some(InMsg::ShuffleChanged { shuffle }),
|
|
|
|
VolumeChanged(volume) => Some(InMsg::VolumeChanged {
|
|
|
|
volume: volume as f32,
|
|
|
|
}),
|
|
|
|
PlayerShutDown => None,
|
2022-11-16 04:41:13 +00:00
|
|
|
TrackChanged(ref metadata) => {
|
2022-10-31 05:31:54 +00:00
|
|
|
metadata_tracker.update(&mut magpie, metadata);
|
|
|
|
None
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
eprintln!("Unhandled MPRIS message: {:?}", event);
|
|
|
|
None
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
if let Some(msg) = in_msg {
|
2022-11-17 22:03:12 +00:00
|
|
|
magpie.send_panel_json(0, &msg);
|
2022-10-31 05:31:54 +00:00
|
|
|
}
|
|
|
|
}
|
2022-10-30 05:26:02 +00:00
|
|
|
}
|
2022-09-24 18:54:55 +00:00
|
|
|
}
|