From 2ef67be5c6d020e08a8277353221edbccc0d6184 Mon Sep 17 00:00:00 2001
From: Kubat <mael.martin31@gmail.com>
Date: Sat, 4 Feb 2023 16:52:43 +0100
Subject: [PATCH] MPRIS: Add the default implementations for the MPRIS server

---
 src/rust/mpris/src/error.rs  |  12 ++
 src/rust/mpris/src/lib.rs    |   1 +
 src/rust/mpris/src/server.rs | 242 +++++++++++++++++++----------------
 src/rust/mpris/src/types.rs  |  81 ++++++++++--
 4 files changed, 219 insertions(+), 117 deletions(-)
 create mode 100644 src/rust/mpris/src/error.rs

diff --git a/src/rust/mpris/src/error.rs b/src/rust/mpris/src/error.rs
new file mode 100644
index 00000000..cb9f10ed
--- /dev/null
+++ b/src/rust/mpris/src/error.rs
@@ -0,0 +1,12 @@
+use zbus::DBusError;
+
+#[derive(DBusError, Debug)]
+#[dbus_error(prefix = "org.mpris.MediaPlayer2")]
+pub enum Error {
+    #[dbus_error(zbus_error)]
+    ZBus(zbus::Error),
+
+    NoFile(String),
+}
+
+pub type MPRISResult<T> = std::result::Result<T, Error>;
diff --git a/src/rust/mpris/src/lib.rs b/src/rust/mpris/src/lib.rs
index e728031c..5449773c 100644
--- a/src/rust/mpris/src/lib.rs
+++ b/src/rust/mpris/src/lib.rs
@@ -1,2 +1,3 @@
+pub mod error;
 pub mod server;
 pub mod types;
diff --git a/src/rust/mpris/src/server.rs b/src/rust/mpris/src/server.rs
index 993d31ca..93be0933 100644
--- a/src/rust/mpris/src/server.rs
+++ b/src/rust/mpris/src/server.rs
@@ -1,18 +1,26 @@
 //! # DBus interface for: `org.mpris.MediaPlayer2`, `org.mpris.MediaPlayer2.Player`
 
 // use crate::types::*;
+use crate::types::*;
+use commons::log;
 use zbus::dbus_interface;
 
+/// Should be served at "/org/mpris/MediaPlayer2"
 pub struct MediaPlayer2 {
     identity: String,
 }
 
+/// Should be served at "/org/mpris/MediaPlayer2"
+pub struct MediaPlayer2Player {}
+
 #[dbus_interface(name = "org.mpris.MediaPlayer2")]
 impl MediaPlayer2 {
     /// Quit method
+    #[dbus_interface(name = "Quit")]
     fn quit(&self) {}
 
     /// Raise method
+    #[dbus_interface(name = "Raise")]
     fn raise(&self) {}
 
     /// CanQuit property
@@ -45,7 +53,7 @@ impl MediaPlayer2 {
         false
     }
     #[dbus_interface(property, name = "Fullscreen")]
-    fn set_fullscreen(&mut self, _value: bool) {}
+    fn set_fullscreen(&self, _value: bool) {}
 
     /// HasTrackList property
     #[dbus_interface(property, name = "HasTrackList")]
@@ -75,110 +83,128 @@ impl MediaPlayer2 {
     }
 }
 
-// #[dbus_interface(
-//     interface = "org.mpris.MediaPlayer2.Player",
-//     default_path = "/org/mpris/MediaPlayer2",
-//     assume_defaults = true
-// )]
-// trait Player {
-//     /// Next method
-//     fn next(&self) -> zbus::Result<()>;
-//
-//     /// OpenUri method
-//     fn open_uri(&self, id_uri: &str) -> zbus::Result<()>;
-//
-//     /// Pause method
-//     fn pause(&self) -> zbus::Result<()>;
-//
-//     /// Play method
-//     fn play(&self) -> zbus::Result<()>;
-//
-//     /// PlayPause method
-//     fn play_pause(&self) -> zbus::Result<()>;
-//
-//     /// Previous method
-//     fn previous(&self) -> zbus::Result<()>;
-//
-//     /// Seek method
-//     fn seek(&self, time: TimeMicroSec) -> zbus::Result<()>;
-//
-//     /// SetPosition method
-//     fn set_position(
-//         &self,
-//         track_id: &MediaPlayerObjectPath,
-//         time: TimeMicroSec,
-//     ) -> zbus::Result<()>;
-//
-//     /// Stop method
-//     fn stop(&self) -> zbus::Result<()>;
-//
-//     /// CanControl property
-//     #[dbus_interface(property)]
-//     fn can_control(&self) -> zbus::Result<bool>;
-//
-//     /// CanPause property
-//     #[dbus_interface(property)]
-//     fn can_pause(&self) -> zbus::Result<bool>;
-//
-//     /// CanPlay property
-//     #[dbus_interface(property)]
-//     fn can_play(&self) -> zbus::Result<bool>;
-//
-//     /// CanSeek property
-//     #[dbus_interface(property)]
-//     fn can_seek(&self) -> zbus::Result<bool>;
-//
-//     /// LoopStatus property
-//     #[dbus_interface(property)]
-//     fn loop_status(&self) -> zbus::Result<MediaPlayerObjectPath>;
-//     fn set_loop_status(&self, value: &MediaPlayerObjectPath) -> zbus::Result<()>;
-//
-//     /// MaximumRate property. We will only set it to `1.0` here...
-//     #[dbus_interface(property)]
-//     fn maximum_rate(&self) -> zbus::Result<f64> {
-//         Ok(1.0)
-//     }
-//     fn set_maximum_rate(&self, _: f64) -> zbus::Result<()> {
-//         Ok(())
-//     }
-//
-//     /// Metadata property. If there is a current kara playing, there must be at
-//     /// least a `mpris:trackid` value of type `o` which is the object path of
-//     /// the current kara id.
-//     #[dbus_interface(property)]
-//     fn metadata(
-//         &self,
-//     ) -> zbus::Result<std::collections::HashMap<String, zbus::zvariant::OwnedValue>>;
-//
-//     /// MinimumRate property. We will only set it to `1.0` here...
-//     #[dbus_interface(property)]
-//     fn minimum_rate(&self) -> zbus::Result<f64> {
-//         Ok(1.0)
-//     }
-//     fn set_minimum_rate(&self, _: f64) -> zbus::Result<()> {
-//         Ok(())
-//     }
-//
-//     /// PlaybackStatus property
-//     #[dbus_interface(property)]
-//     fn playback_status(&self) -> zbus::Result<MediaPlayerPlaybackStatus>;
-//
-//     /// Position property
-//     #[dbus_interface(property)]
-//     fn position(&self) -> zbus::Result<TimeMicroSec>;
-//
-//     /// Rate property
-//     #[dbus_interface(property)]
-//     fn rate(&self) -> zbus::Result<f64>;
-//     fn set_rate(&self, value: f64) -> zbus::Result<()>;
-//
-//     /// Shuffle property
-//     #[dbus_interface(property)]
-//     fn shuffle(&self) -> zbus::Result<bool>;
-//     fn set_shuffle(&self, value: bool) -> zbus::Result<()>;
-//
-//     /// Volume property
-//     #[dbus_interface(property)]
-//     fn volume(&self) -> zbus::Result<f64>;
-//     fn set_volume(&self, value: f64) -> zbus::Result<()>;
-// }
+#[dbus_interface(name = "org.mpris.MediaPlayer2.Player")]
+impl MediaPlayer2Player {
+    /// Next method
+    fn next(&self) {}
+
+    /// Pause method
+    fn pause(&self) {
+        todo!()
+    }
+
+    /// Play method
+    fn play(&self) {
+        todo!()
+    }
+
+    /// OpenUri method
+    fn open_uri(&self, uri: &str) {
+        todo!("handle open uri {uri}")
+    }
+
+    fn play_pause(&self) {
+        todo!()
+    }
+
+    /// Previous method
+    fn previous(&self) {
+        todo!()
+    }
+
+    /// Seek method
+    fn seek(&self, offset: TimeMicroSec) {
+        todo!("handle seek offset {offset:?}")
+    }
+
+    /// SetPosition method
+    fn set_position(&self, track_id: MediaPlayerObjectPath, position: TimeMicroSec) {
+        todo!("handle set position {track_id:?} {position:?}")
+    }
+
+    /// Stop method
+    fn stop(&self) {
+        todo!()
+    }
+
+    /// CanControl property
+    #[dbus_interface(property)]
+    fn can_control(&self) -> bool {
+        true
+    }
+
+    /// CanPause property
+    #[dbus_interface(property)]
+    fn can_pause(&self) -> bool {
+        todo!()
+    }
+
+    /// CanPlay property
+    #[dbus_interface(property)]
+    fn can_play(&self) -> bool {
+        todo!()
+    }
+
+    /// CanSeek property
+    #[dbus_interface(property)]
+    fn can_seek(&self) -> bool {
+        false
+    }
+
+    /// LoopStatus property
+    #[dbus_interface(property, name = "LoopStatus")]
+    fn loop_status(&self) -> MediaPlayerObjectPath {
+        todo!()
+    }
+    #[dbus_interface(property, name = "LoopStatus")]
+    fn set_loop_status(&self, loop_status: MediaPlayerObjectPath) {
+        todo!("handle loop status {loop_status:?}")
+    }
+
+    #[rustfmt::skip] #[dbus_interface(property, name = "MaximumRate")]    fn maximum_rate(&self) -> f64 { 1.0 }
+    #[rustfmt::skip] #[dbus_interface(property, name = "MinimumRate")]    fn minimum_rate(&self) -> f64 { 1.0 }
+    #[rustfmt::skip] #[dbus_interface(property, name = "MaximumRate")]    fn set_maximum_rate(&self, rate: f64) { log::warn!("ignore rate {rate}") }
+    #[rustfmt::skip] #[dbus_interface(property, name = "MinimumRate")]    fn set_minimum_rate(&self, rate: f64) { log::warn!("ignore rate {rate}") }
+    #[rustfmt::skip] #[dbus_interface(property, name = "Rate"       )]    fn rate(&self) -> f64 { 1.0 }
+    #[rustfmt::skip] #[dbus_interface(property, name = "Rate"       )]    fn set_rate(&self, rate: f64) { log::warn!("ignore rate {rate}") }
+
+    /// Shuffle property
+    #[dbus_interface(property, name = "Shuffle")]
+    fn shuffle(&self) -> bool {
+        todo!()
+    }
+    #[dbus_interface(property, name = "Shuffle")]
+    fn set_shuffle(&self, shuffle: bool) {
+        todo!("handle shuffle {shuffle}")
+    }
+
+    /// Volume property
+    #[dbus_interface(property, name = "Volume")]
+    fn volume(&self) -> f64 {
+        todo!()
+    }
+    #[dbus_interface(property, name = "Volume")]
+    fn set_volume(&self, volume: f64) {
+        todo!("handle volume {volume}")
+    }
+
+    /// PlaybackStatus property
+    #[dbus_interface(property, name = "PlaybackStatus")]
+    fn playback_status(&self) -> MediaPlayerPlaybackStatus {
+        todo!()
+    }
+
+    /// Position property
+    #[dbus_interface(property, name = "Position")]
+    fn position(&self) -> TimeMicroSec {
+        todo!()
+    }
+
+    /// Metadata property. If there is a current kara playing, there must be at
+    /// least a `mpris:trackid` value of type `o` which is the object path of
+    /// the current kara id.
+    #[dbus_interface(property, name = "Metadata")]
+    fn metadata(&self) -> MediaPlayerMetadata {
+        todo!()
+    }
+}
diff --git a/src/rust/mpris/src/types.rs b/src/rust/mpris/src/types.rs
index 07af4a2b..7e017d11 100644
--- a/src/rust/mpris/src/types.rs
+++ b/src/rust/mpris/src/types.rs
@@ -1,5 +1,6 @@
+use hashbrown::HashMap;
 use serde::{Deserialize, Serialize};
-use zbus::zvariant::{ObjectPath as ZObjectPath, OwnedValue as ZOwnedValue, Type as ZType};
+use zbus::zvariant::{Dict as ZDict, ObjectPath as ZObjectPath, Type as ZType, Value as ZValue};
 
 /// The loop status.
 #[derive(Debug, Deserialize, Serialize, ZType, Clone, Copy, PartialEq, Eq)]
@@ -25,6 +26,10 @@ pub enum MediaPlayerPlaybackStatus {
 #[zvariant(signature = "x")]
 pub struct TimeMicroSec(i64);
 
+#[derive(Debug, Serialize, Deserialize, ZType, Clone, PartialEq, Eq)]
+#[zvariant(signature = "a{sv}")]
+pub struct MediaPlayerMetadata(HashMap<String, String>);
+
 /// A valid path must be prefiexed by `/org/mpris/MediaPlayer2`. After that we
 /// have the variant name, then a `/`, then the ascii only value. For example
 /// we can have:
@@ -53,15 +58,15 @@ impl From<i64> for TimeMicroSec {
     }
 }
 
-impl From<ZOwnedValue> for TimeMicroSec {
-    fn from(value: ZOwnedValue) -> Self {
+impl From<ZValue<'_>> for TimeMicroSec {
+    fn from(value: ZValue<'_>) -> Self {
         let value: i64 = value.try_into().unwrap_or_default();
         Self(value)
     }
 }
 
-impl From<ZOwnedValue> for MediaPlayerLoopStatus {
-    fn from(value: ZOwnedValue) -> Self {
+impl From<ZValue<'_>> for MediaPlayerLoopStatus {
+    fn from(value: ZValue<'_>) -> Self {
         let value: String = value.try_into().unwrap_or_default();
         match &value[..] {
             "None" => Self::None,
@@ -72,8 +77,8 @@ impl From<ZOwnedValue> for MediaPlayerLoopStatus {
     }
 }
 
-impl From<ZOwnedValue> for MediaPlayerPlaybackStatus {
-    fn from(value: ZOwnedValue) -> Self {
+impl From<ZValue<'_>> for MediaPlayerPlaybackStatus {
+    fn from(value: ZValue<'_>) -> Self {
         let value: String = value.try_into().unwrap_or_default();
         match &value[..] {
             "Paused" => Self::Paused,
@@ -84,8 +89,8 @@ impl From<ZOwnedValue> for MediaPlayerPlaybackStatus {
     }
 }
 
-impl<'a> From<ZOwnedValue> for MediaPlayerObjectPath {
-    fn from(value: ZOwnedValue) -> Self {
+impl From<ZValue<'_>> for MediaPlayerObjectPath {
+    fn from(value: ZValue<'_>) -> Self {
         let value: ZObjectPath = value.try_into().unwrap_or_default();
         let value = value.trim_start_matches('/');
         match value.split('/').collect::<Vec<_>>()[..] {
@@ -98,3 +103,61 @@ impl<'a> From<ZOwnedValue> for MediaPlayerObjectPath {
         }
     }
 }
+
+impl From<MediaPlayerObjectPath> for ZValue<'_> {
+    fn from(value: MediaPlayerObjectPath) -> Self {
+        use MediaPlayerObjectPath::*;
+        ZValue::ObjectPath(
+            match value {
+                None => ZObjectPath::try_from("/org/mpris/MediaPlayer2/None"),
+                NoTrack => ZObjectPath::try_from("/org/mpris/MediaPlayer2/TrackList/NoTrack"),
+                Id(id) => ZObjectPath::try_from(format!("/org/mpris/MediaPlayer2/Id/{id}")),
+            }
+            .expect("incorrect serialization"),
+        )
+    }
+}
+
+impl From<MediaPlayerLoopStatus> for ZValue<'_> {
+    fn from(value: MediaPlayerLoopStatus) -> Self {
+        use MediaPlayerLoopStatus::*;
+        match value {
+            None => "None",
+            Track => "Track",
+            Playlist => "Playlist",
+        }
+        .into()
+    }
+}
+
+impl From<MediaPlayerPlaybackStatus> for ZValue<'_> {
+    fn from(value: MediaPlayerPlaybackStatus) -> Self {
+        use MediaPlayerPlaybackStatus::*;
+        match value {
+            Paused => "Paused",
+            Playing => "Playing",
+            Stopped => "Stopped",
+        }
+        .into()
+    }
+}
+
+impl From<TimeMicroSec> for ZValue<'_> {
+    fn from(TimeMicroSec(value): TimeMicroSec) -> Self {
+        value.into()
+    }
+}
+
+impl From<MediaPlayerMetadata> for ZValue<'_> {
+    fn from(MediaPlayerMetadata(value): MediaPlayerMetadata) -> Self {
+        let mut dict = ZDict::new(
+            "s".try_into().expect("invalid signature"),
+            "v".try_into().expect("invalid signature"),
+        );
+        for (key, value) in value {
+            dict.append(key.into(), value.into())
+                .expect("invalid entry found")
+        }
+        todo!()
+    }
+}
-- 
GitLab