Sinono3 / souvlaki

A cross-platform library for handling OS media controls and metadata.
MIT License
79 stars 15 forks source link
cross-platform mediakeys mpris rust

Souvlaki Crates.io Docs CI

A cross-platform library for handling OS media controls and metadata. One abstraction for Linux, MacOS and Windows.

Supported platforms

Windows

Linux

In another shell

$ playerctl metadata my_player xesam:artist Slowdive my_player xesam:album Souvlaki my_player mpris:artUrl https://c.pxhere.com/photos/34/c1/souvlaki_authentic_greek_greek_food_mezes-497780.jpg!d my_player mpris:trackid '/' my_player mpris:length 290000000 my_player xesam:title When The Sun Hits


## MacOS

- Control Center:\
![Control Center](https://user-images.githubusercontent.com/434125/171526539-ecb07a74-5dc5-4f4b-8305-4a99d4d5c31c.png)
- Now Playing:\
![Now Playing](https://user-images.githubusercontent.com/434125/171526759-9232be58-63ed-4eea-ac15-aa50258d8254.png)

## Requirements

Minimum supported Rust version is 1.67.

## Usage

The main struct is `MediaControls`. In order to create this struct you need a `PlatformConfig`. This struct contains all of the platform-specific requirements for spawning media controls. Here are the differences between the platforms:

- MacOS: No config needed, but requires a window to be open ([#23](https://github.com/Sinono3/souvlaki/issues/23))
- Linux: 
    - `dbus_name`: The way your player will appear on D-Bus. It should follow [the D-Bus specification](https://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-names-bus). 
    - `display_name`: This could be however you want. It's the name that will be shown to the users.
- Windows: 
    - `hwnd`: In this platform, a window needs to be opened to create media controls. The argument required is an `HWND`, a value of type `*mut c_void`. This value can be extracted when you open a window in your program, for example using the `raw_window_handle` in winit.

### Linux backends: D-Bus and `zbus`

When using the library on Linux, the default backend is `dbus-crossroads`. This backend has some issues with consistency in general, but is more stable and uses the native D-Bus library behind the scenes. The zbus backend however, is more modern and is written in pure Rust. It spawns another thread and stars an async `pollster` runtime, handling the incoming MPRIS messages. 

To enable the zbus backend, in your Cargo.toml, set `default-features` to false and enable the `use_zbus` feature:

```toml
souvlaki = { version = "<version>", default-features = false, features = ["use_zbus"] }

Note: If you think there's a better way of using the zbus library regarding the async runtime in another thread, feel free to leave a PR or issue.

Example

use souvlaki::{MediaControlEvent, MediaControls, MediaMetadata, PlatformConfig};

fn main() {
    #[cfg(not(target_os = "windows"))]
    let hwnd = None;

    #[cfg(target_os = "windows")]
    let hwnd = {
        use raw_window_handle::windows::WindowsHandle;

        let handle: WindowsHandle = unimplemented!();
        Some(handle.hwnd)
    };

    let config = PlatformConfig {
        dbus_name: "my_player",
        display_name: "My Player",
        hwnd,
    };

    let mut controls = MediaControls::new(config).unwrap();

    // The closure must be Send and have a static lifetime.
    controls
        .attach(|event: MediaControlEvent| println!("Event received: {:?}", event))
        .unwrap();

    // Update the media metadata.
    controls
        .set_metadata(MediaMetadata {
            title: Some("Souvlaki Space Station"),
            artist: Some("Slowdive"),
            album: Some("Souvlaki"),
            ..Default::default()
        })
        .unwrap();

    // Your actual logic goes here.
    loop {
        std::thread::sleep(std::time::Duration::from_secs(1));
    }

    // The controls automatically detach on drop.
}

Check out this example here.

Thanks 💗