1
0
Fork 0
mirror of https://github.com/librespot-org/librespot.git synced 2025-10-03 17:59:24 +02:00

add initial MPRIS support using zbus

- following the spec at https://specifications.freedesktop.org/mpris-spec/latest/

- some properties/commands are not fully supported, yet
This commit is contained in:
wisp3rwind 2024-10-01 11:36:41 +02:00 committed by Paul Fariello
parent feb2dc6b1b
commit 01ac2e3b60
4 changed files with 1507 additions and 13 deletions

271
Cargo.lock generated
View file

@ -160,6 +160,65 @@ dependencies = [
"pin-project-lite", "pin-project-lite",
] ]
[[package]]
name = "async-channel"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2"
dependencies = [
"concurrent-queue",
"event-listener-strategy",
"futures-core",
"pin-project-lite",
]
[[package]]
name = "async-io"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc"
dependencies = [
"autocfg",
"cfg-if",
"concurrent-queue",
"futures-io",
"futures-lite",
"parking",
"polling",
"rustix 1.1.2",
"slab",
"windows-sys 0.61.0",
]
[[package]]
name = "async-lock"
version = "3.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fd03604047cee9b6ce9de9f70c6cd540a0520c813cbd49bae61f33ab80ed1dc"
dependencies = [
"event-listener",
"event-listener-strategy",
"pin-project-lite",
]
[[package]]
name = "async-process"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc50921ec0055cdd8a16de48773bfeec5c972598674347252c0399676be7da75"
dependencies = [
"async-channel",
"async-io",
"async-lock",
"async-signal",
"async-task",
"blocking",
"cfg-if",
"event-listener",
"futures-lite",
"rustix 1.1.2",
]
[[package]] [[package]]
name = "async-recursion" name = "async-recursion"
version = "1.1.1" version = "1.1.1"
@ -171,6 +230,30 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "async-signal"
version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43c070bbf59cd3570b6b2dd54cd772527c7c3620fce8be898406dd3ed6adc64c"
dependencies = [
"async-io",
"async-lock",
"atomic-waker",
"cfg-if",
"futures-core",
"futures-io",
"rustix 1.1.2",
"signal-hook-registry",
"slab",
"windows-sys 0.61.0",
]
[[package]]
name = "async-task"
version = "4.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de"
[[package]] [[package]]
name = "async-trait" name = "async-trait"
version = "0.1.89" version = "0.1.89"
@ -248,6 +331,19 @@ dependencies = [
"generic-array", "generic-array",
] ]
[[package]]
name = "blocking"
version = "1.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21"
dependencies = [
"async-channel",
"async-task",
"futures-io",
"futures-lite",
"piper",
]
[[package]] [[package]]
name = "bumpalo" name = "bumpalo"
version = "3.19.0" version = "3.19.0"
@ -1210,6 +1306,12 @@ version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "hermit-abi"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c"
[[package]] [[package]]
name = "hex" name = "hex"
version = "0.4.3" version = "0.4.3"
@ -1861,8 +1963,11 @@ dependencies = [
"sha1", "sha1",
"sysinfo", "sysinfo",
"thiserror 2.0.16", "thiserror 2.0.16",
"time",
"tokio", "tokio",
"url", "url",
"zbus 4.4.0",
"zvariant 4.2.0",
] ]
[[package]] [[package]]
@ -1985,7 +2090,7 @@ dependencies = [
"sha1", "sha1",
"thiserror 2.0.16", "thiserror 2.0.16",
"tokio", "tokio",
"zbus", "zbus 5.11.0",
] ]
[[package]] [[package]]
@ -2205,6 +2310,19 @@ dependencies = [
"jni-sys", "jni-sys",
] ]
[[package]]
name = "nix"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
dependencies = [
"bitflags 2.9.4",
"cfg-if",
"cfg_aliases",
"libc",
"memoffset",
]
[[package]] [[package]]
name = "nix" name = "nix"
version = "0.30.1" version = "0.30.1"
@ -2633,6 +2751,17 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "piper"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066"
dependencies = [
"atomic-waker",
"fastrand",
"futures-io",
]
[[package]] [[package]]
name = "pkcs1" name = "pkcs1"
version = "0.7.5" version = "0.7.5"
@ -2660,6 +2789,20 @@ version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
[[package]]
name = "polling"
version = "3.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218"
dependencies = [
"cfg-if",
"concurrent-queue",
"hermit-abi",
"pin-project-lite",
"rustix 1.1.2",
"windows-sys 0.61.0",
]
[[package]] [[package]]
name = "portable-atomic" name = "portable-atomic"
version = "1.11.1" version = "1.11.1"
@ -4865,6 +5008,16 @@ version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb"
[[package]]
name = "xdg-home"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec1cdab258fb55c0da61328dc52c8764709b249011b2cad0454c72f0bf10a1f6"
dependencies = [
"libc",
"windows-sys 0.59.0",
]
[[package]] [[package]]
name = "yoke" name = "yoke"
version = "0.8.0" version = "0.8.0"
@ -4889,6 +5042,39 @@ dependencies = [
"synstructure", "synstructure",
] ]
[[package]]
name = "zbus"
version = "4.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb97012beadd29e654708a0fdb4c84bc046f537aecfde2c3ee0a9e4b4d48c725"
dependencies = [
"async-broadcast",
"async-process",
"async-recursion",
"async-trait",
"enumflags2",
"event-listener",
"futures-core",
"futures-sink",
"futures-util",
"hex",
"nix 0.29.0",
"ordered-stream",
"rand 0.8.5",
"serde",
"serde_repr",
"sha1",
"static_assertions",
"tokio",
"tracing",
"uds_windows",
"windows-sys 0.52.0",
"xdg-home",
"zbus_macros 4.4.0",
"zbus_names 3.0.0",
"zvariant 4.2.0",
]
[[package]] [[package]]
name = "zbus" name = "zbus"
version = "5.11.0" version = "5.11.0"
@ -4903,7 +5089,7 @@ dependencies = [
"futures-core", "futures-core",
"futures-lite", "futures-lite",
"hex", "hex",
"nix", "nix 0.30.1",
"ordered-stream", "ordered-stream",
"serde", "serde",
"serde_repr", "serde_repr",
@ -4912,9 +5098,22 @@ dependencies = [
"uds_windows", "uds_windows",
"windows-sys 0.60.2", "windows-sys 0.60.2",
"winnow", "winnow",
"zbus_macros", "zbus_macros 5.11.0",
"zbus_names", "zbus_names 4.2.0",
"zvariant", "zvariant 5.7.0",
]
[[package]]
name = "zbus_macros"
version = "4.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "267db9407081e90bbfa46d841d3cbc60f59c0351838c4bc65199ecd79ab1983e"
dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
"syn",
"zvariant_utils 2.1.0",
] ]
[[package]] [[package]]
@ -4927,9 +5126,20 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn",
"zbus_names", "zbus_names 4.2.0",
"zvariant", "zvariant 5.7.0",
"zvariant_utils", "zvariant_utils 3.2.1",
]
[[package]]
name = "zbus_names"
version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b9b1fef7d021261cc16cba64c351d291b715febe0fa10dc3a443ac5a5022e6c"
dependencies = [
"serde",
"static_assertions",
"zvariant 4.2.0",
] ]
[[package]] [[package]]
@ -4941,7 +5151,7 @@ dependencies = [
"serde", "serde",
"static_assertions", "static_assertions",
"winnow", "winnow",
"zvariant", "zvariant 5.7.0",
] ]
[[package]] [[package]]
@ -5024,6 +5234,19 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "zvariant"
version = "4.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2084290ab9a1c471c38fc524945837734fbf124487e105daec2bb57fd48c81fe"
dependencies = [
"endi",
"enumflags2",
"serde",
"static_assertions",
"zvariant_derive 4.2.0",
]
[[package]] [[package]]
name = "zvariant" name = "zvariant"
version = "5.7.0" version = "5.7.0"
@ -5034,8 +5257,21 @@ dependencies = [
"enumflags2", "enumflags2",
"serde", "serde",
"winnow", "winnow",
"zvariant_derive", "zvariant_derive 5.7.0",
"zvariant_utils", "zvariant_utils 3.2.1",
]
[[package]]
name = "zvariant_derive"
version = "4.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73e2ba546bda683a90652bac4a279bc146adad1386f25379cf73200d2002c449"
dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
"syn",
"zvariant_utils 2.1.0",
] ]
[[package]] [[package]]
@ -5048,7 +5284,18 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn",
"zvariant_utils", "zvariant_utils 3.2.1",
]
[[package]]
name = "zvariant_utils"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c51bcff7cc3dbb5055396bcf774748c3dab426b4b8659046963523cee4808340"
dependencies = [
"proc-macro2",
"quote",
"syn",
] ]
[[package]] [[package]]

View file

@ -37,7 +37,7 @@ repository = "https://github.com/librespot-org/librespot"
edition = "2024" edition = "2024"
[features] [features]
default = ["native-tls", "rodio-backend", "with-libmdns"] default = ["native-tls", "rodio-backend", "with-libmdns", "with-mpris"]
# TLS backends (mutually exclusive - compile-time checks in oauth/src/lib.rs) # TLS backends (mutually exclusive - compile-time checks in oauth/src/lib.rs)
# Note: Feature validation is in oauth crate since it's compiled first in the dependency tree. # Note: Feature validation is in oauth crate since it's compiled first in the dependency tree.
@ -133,6 +133,10 @@ with-dns-sd = ["librespot-discovery/with-dns-sd"]
# data. # data.
passthrough-decoder = ["librespot-playback/passthrough-decoder"] passthrough-decoder = ["librespot-playback/passthrough-decoder"]
# MPRIS: Allow external tool to have access to playback
# status, metadata and to control the player.
with-mpris = ["dep:zbus", "dep:zvariant"]
[lib] [lib]
name = "librespot" name = "librespot"
path = "src/lib.rs" path = "src/lib.rs"
@ -181,7 +185,10 @@ tokio = { version = "1", features = [
"sync", "sync",
"process", "process",
] } ] }
time = { version = "0.3", features = ["formatting"] }
url = "2.2" url = "2.2"
zbus = { version = "4", default-features = false, features = ["tokio"], optional = true }
zvariant = { version = "4", default-features = false, optional = true }
[package.metadata.deb] [package.metadata.deb]
maintainer = "Librespot Organization <noreply@github.com>" maintainer = "Librespot Organization <noreply@github.com>"

View file

@ -41,6 +41,11 @@ use url::Url;
mod player_event_handler; mod player_event_handler;
use player_event_handler::{EventHandler, run_program_on_sink_events}; use player_event_handler::{EventHandler, run_program_on_sink_events};
#[cfg(feature = "with-mpris")]
mod mpris_event_handler;
#[cfg(feature = "with-mpris")]
use mpris_event_handler::MprisEventHandler;
fn device_id(name: &str) -> String { fn device_id(name: &str) -> String {
HEXLOWER.encode(&Sha1::digest(name.as_bytes())) HEXLOWER.encode(&Sha1::digest(name.as_bytes()))
} }
@ -1991,6 +1996,14 @@ async fn main() {
} }
} }
#[cfg(feature = "with-mpris")]
let mpris = MprisEventHandler::spawn(player.clone())
.await
.unwrap_or_else(|e| {
error!("could not initialize MPRIS: {e}");
exit(1);
});
loop { loop {
tokio::select! { tokio::select! {
credentials = async { credentials = async {
@ -2044,6 +2057,10 @@ async fn main() {
exit(1); exit(1);
} }
}; };
#[cfg(feature = "with-mpris")]
mpris.set_spirc(spirc_.clone());
spirc = Some(spirc_); spirc = Some(spirc_);
spirc_task = Some(Box::pin(spirc_task_)); spirc_task = Some(Box::pin(spirc_task_));
@ -2089,6 +2106,9 @@ async fn main() {
let mut shutdown_tasks = tokio::task::JoinSet::new(); let mut shutdown_tasks = tokio::task::JoinSet::new();
#[cfg(feature = "with-mpris")]
shutdown_tasks.spawn(mpris.quit_and_join());
// Shutdown spirc if necessary // Shutdown spirc if necessary
if let Some(spirc) = spirc { if let Some(spirc) = spirc {
if let Err(e) = spirc.shutdown() { if let Err(e) = spirc.shutdown() {

1220
src/mpris_event_handler.rs Normal file

File diff suppressed because it is too large Load diff