diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index a82d04cd8..226209c40 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -146,6 +146,17 @@ dependencies = [ "slab", ] +[[package]] +name = "async-fs" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8034a681df4aed8b8edbd7fbe472401ecf009251c8b40556b304567052e294c5" +dependencies = [ + "async-lock", + "blocking", + "futures-lite", +] + [[package]] name = "async-io" version = "2.6.0" @@ -2745,6 +2756,19 @@ dependencies = [ "pxfm", ] +[[package]] +name = "mpris-server" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "058bc2227727af394f34aa51da3e36aeecf2c808f39315d35f754872660750ae" +dependencies = [ + "async-channel", + "futures-channel", + "serde", + "trait-variant", + "zbus 4.4.0", +] + [[package]] name = "muda" version = "0.17.1" @@ -2782,6 +2806,7 @@ dependencies = [ "log", "m3u", "memoize", + "mpris-server", "nosleep", "pathdiff", "rayon", @@ -2847,6 +2872,19 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" +[[package]] +name = "nix" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +dependencies = [ + "bitflags 2.11.0", + "cfg-if", + "cfg_aliases", + "libc", + "memoffset", +] + [[package]] name = "nix" version = "0.30.1" @@ -2941,7 +2979,7 @@ dependencies = [ "mac-notification-sys", "serde", "tauri-winrt-notification", - "zbus", + "zbus 5.14.0", ] [[package]] @@ -3304,7 +3342,7 @@ checksum = "e4022a17595a00d6a369236fdae483f0de7f0a339960a53118b818238e132224" dependencies = [ "android_system_properties", "log", - "nix", + "nix 0.30.1", "objc2", "objc2-foundation", "objc2-ui-kit", @@ -4932,6 +4970,12 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "string_cache" version = "0.8.9" @@ -5410,7 +5454,7 @@ dependencies = [ "thiserror 2.0.18", "url 2.5.8", "windows 0.61.3", - "zbus", + "zbus 5.14.0", ] [[package]] @@ -5488,7 +5532,7 @@ dependencies = [ "thiserror 2.0.18", "tracing", "windows-sys 0.60.2", - "zbus", + "zbus 5.14.0", ] [[package]] @@ -5989,6 +6033,17 @@ dependencies = [ "once_cell", ] +[[package]] +name = "trait-variant" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70977707304198400eb4835a78f6a9f928bf41bba420deb8fdb175cd965d77a7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "tray-icon" version = "0.21.3" @@ -6818,6 +6873,15 @@ dependencies = [ "windows-targets 0.48.5", ] +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows-sys" version = "0.59.0" @@ -7368,6 +7432,16 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd" +[[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]] name = "yoke" version = "0.8.1" @@ -7391,6 +7465,44 @@ dependencies = [ "synstructure", ] +[[package]] +name = "zbus" +version = "4.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb97012beadd29e654708a0fdb4c84bc046f537aecfde2c3ee0a9e4b4d48c725" +dependencies = [ + "async-broadcast", + "async-executor", + "async-fs", + "async-io", + "async-lock", + "async-process", + "async-recursion", + "async-task", + "async-trait", + "blocking", + "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", + "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]] name = "zbus" version = "5.14.0" @@ -7421,9 +7533,22 @@ dependencies = [ "uuid", "windows-sys 0.61.2", "winnow 0.7.14", - "zbus_macros", - "zbus_names", - "zvariant", + "zbus_macros 5.14.0", + "zbus_names 4.3.1", + "zvariant 5.10.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 3.4.0", + "proc-macro2", + "quote", + "syn 2.0.117", + "zvariant_utils 2.1.0", ] [[package]] @@ -7436,9 +7561,20 @@ dependencies = [ "proc-macro2", "quote", "syn 2.0.117", - "zbus_names", - "zvariant", - "zvariant_utils", + "zbus_names 4.3.1", + "zvariant 5.10.0", + "zvariant_utils 3.3.0", +] + +[[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]] @@ -7449,7 +7585,7 @@ checksum = "ffd8af6d5b78619bab301ff3c560a5bd22426150253db278f164d6cf3b72c50f" dependencies = [ "serde", "winnow 0.7.14", - "zvariant", + "zvariant 5.10.0", ] [[package]] @@ -7553,6 +7689,19 @@ dependencies = [ "zune-core", ] +[[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]] name = "zvariant" version = "5.10.0" @@ -7563,8 +7712,21 @@ dependencies = [ "enumflags2", "serde", "winnow 0.7.14", - "zvariant_derive", - "zvariant_utils", + "zvariant_derive 5.10.0", + "zvariant_utils 3.3.0", +] + +[[package]] +name = "zvariant_derive" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73e2ba546bda683a90652bac4a279bc146adad1386f25379cf73200d2002c449" +dependencies = [ + "proc-macro-crate 3.4.0", + "proc-macro2", + "quote", + "syn 2.0.117", + "zvariant_utils 2.1.0", ] [[package]] @@ -7577,7 +7739,18 @@ dependencies = [ "proc-macro2", "quote", "syn 2.0.117", - "zvariant_utils", + "zvariant_utils 3.3.0", +] + +[[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 2.0.117", ] [[package]] diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 7cd585ade..9b9e83b7e 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -58,6 +58,9 @@ ts-rs = "12.0.1" uuid = { version = "1.22.0", features = ["v3", "v4", "fast-rng"] } walkdir = "2.5.0" +[target.'cfg(target_os = "linux")'.dependencies] +mpris-server = "0.8" + [profile.dev] incremental = true # Compile your binary in smaller steps. diff --git a/src-tauri/build.rs b/src-tauri/build.rs index 8fc268210..e378fe883 100644 --- a/src-tauri/build.rs +++ b/src-tauri/build.rs @@ -49,6 +49,18 @@ fn main() { .plugin( "sleepblocker", tauri_build::InlinedPlugin::new().commands(&["enable", "disable"]), + ) + .plugin( + "mpris", + tauri_build::InlinedPlugin::new().commands(&[ + "update_playback_status", + "update_track_metadata", + "update_volume", + "update_shuffle", + "update_repeat", + "update_position", + "clear_metadata", + ]), ), ) .expect("Failed to run tauri-build"); diff --git a/src-tauri/capabilities/main.json b/src-tauri/capabilities/main.json index 8a140149a..c12ffce42 100644 --- a/src-tauri/capabilities/main.json +++ b/src-tauri/capabilities/main.json @@ -51,7 +51,14 @@ "database:allow-reset", "default-view:allow-set", "sleepblocker:allow-enable", - "sleepblocker:allow-disable" + "sleepblocker:allow-disable", + "mpris:allow-update-playback-status", + "mpris:allow-update-track-metadata", + "mpris:allow-update-volume", + "mpris:allow-update-shuffle", + "mpris:allow-update-repeat", + "mpris:allow-update-position", + "mpris:allow-clear-metadata" ], "platforms": ["linux", "macOS", "windows"] } diff --git a/src-tauri/src/libs/events.rs b/src-tauri/src/libs/events.rs index 0199df869..3c1c2c8f1 100644 --- a/src-tauri/src/libs/events.rs +++ b/src-tauri/src/libs/events.rs @@ -13,6 +13,12 @@ pub enum IPCEvent<'a> { PlaybackPrevious, PlaybackNext, PlaybackStart, + // MPRIS-related playback events + PlaybackSeekTo, + PlaybackSeekBy, + PlaybackSetVolume, + PlaybackSetShuffle, + PlaybackSetRepeat, // Scan-related events LibraryScanProgress, // Menu-related events diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index ece0d0e20..06188865d 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -98,6 +98,10 @@ fn main() { .build(), ); + // Native MPRIS integration for Linux + #[cfg(target_os = "linux")] + let builder = builder.plugin(plugins::mpris::init()); + // Experimental: local HTTP server for audio streaming // (WebKitGTK's asset protocol doesn't support media streaming) let builder = if enable_stream_server { diff --git a/src-tauri/src/plugins/mod.rs b/src-tauri/src/plugins/mod.rs index 0c2f90b52..327f28678 100644 --- a/src-tauri/src/plugins/mod.rs +++ b/src-tauri/src/plugins/mod.rs @@ -24,5 +24,7 @@ pub mod db; * Settings-related plugins */ pub mod default_view; +#[cfg(target_os = "linux")] +pub mod mpris; pub mod sleepblocker; pub mod stream_server; diff --git a/src-tauri/src/plugins/mpris.rs b/src-tauri/src/plugins/mpris.rs new file mode 100644 index 000000000..25bde4715 --- /dev/null +++ b/src-tauri/src/plugins/mpris.rs @@ -0,0 +1,554 @@ +use std::sync::{Arc, Mutex}; + +use log::{error, info}; +use mpris_server::{ + zbus::{self, fdo}, + LoopStatus, Metadata, PlaybackStatus, PlayerInterface, Property, RootInterface, Server, Signal, + Time, TrackId, +}; +use tauri::plugin::{Builder, TauriPlugin}; +use tauri::{Emitter, Manager, Runtime, State}; + +use crate::libs::error::AnyResult; +use crate::libs::events::IPCEvent; + +/// Internal state shared between the MPRIS server and Tauri commands. +struct MprisInner { + playback_status: Mutex, + metadata: Mutex, + volume: Mutex, + shuffle: Mutex, + loop_status: Mutex, + position: Mutex