Skip to main content

doobs_mpris/binding/
player.rs

1// SPDX-License-Identifier: MPL-2.0
2use std::future::Future;
3
4use zbus::interface;
5use zbus::object_server::SignalEmitter;
6
7use crate::types::{LoopStatus, Metadata, MprisDuration, PlaybackStatus, TrackId};
8
9/// Implement this trait to provide the functionality of [Player].
10///
11/// Most functions have a default implementation to make it easier to create a simple player that
12/// only exposes the current playback status.
13/// When implementing additional functions make sure read the MPRIS specification.
14#[allow(unused_variables)]
15pub trait PlayerProvider {
16    /// Skips to the next track in the tracklist.
17    ///
18    /// If there is no next track (and endless playback and track repeat are both off), stop playback.
19    ///
20    /// If playback is paused or stopped, it remains that way.
21    ///
22    /// If *CanGoNext* is `false`, attempting to call this method should have no effect.
23    fn next(&self) -> impl Future<Output = zbus::fdo::Result<()>> + Send {
24        async { Ok(()) }
25    }
26
27    /// Skips to the previous track in the tracklist.
28    ///
29    /// If there is no previous track (and endless playback and track repeat are both off), stop playback.
30    ///
31    /// If playback is paused or stopped, it remains that way.
32    ///
33    /// If *CanGoPrevious* is `false`, attempting to call this method should have no effect.
34    fn previous(&self) -> impl Future<Output = zbus::fdo::Result<()>> + Send {
35        async { Ok(()) }
36    }
37
38    /// Pauses playback.
39    ///
40    /// If playback is already paused, this has no effect.
41    ///
42    /// Calling *Play* after this should cause playback to start again from the same position.
43    ///
44    /// If *CanPause* is `false`, attempting to call this method should have no effect.
45    fn pause(&self) -> impl Future<Output = zbus::fdo::Result<()>> + Send {
46        async { Ok(()) }
47    }
48
49    /// Toggles the payback state.
50    ///
51    /// If playback is already paused, resumes playback.
52    ///
53    /// If playback is stopped, starts playback.
54    ///
55    /// If *CanPause* is `false`, attempting to call this method should have no effect and raise an error.
56    fn play_pause(&self) -> impl Future<Output = zbus::fdo::Result<()>> + Send {
57        async { Ok(()) }
58    }
59
60    /// Stops playback.
61    ///
62    /// If playback is already stopped, this has no effect.
63    ///
64    /// Calling *Play* after this should cause playback to start again from the beginning of the track.
65    ///
66    /// If *CanControl* is `false`, attempting to call this method should have no effect and raise an error.
67    fn stop(&self) -> impl Future<Output = zbus::fdo::Result<()>> + Send {
68        async { Ok(()) }
69    }
70
71    /// Starts or resumes playback.
72    ///
73    /// If already playing, this has no effect.
74    ///
75    /// If paused, playback resumes from the current position.
76    ///
77    /// If there is no track to play, this has no effect.
78    ///
79    /// If *CanPlay* is `false`, attempting to call this method should have no effect.
80    fn play(&self) -> impl Future<Output = zbus::fdo::Result<()>> + Send {
81        async { Ok(()) }
82    }
83
84    // Seeks forward in the current track by the specified duration.
85    //
86    // A negative value seeks back.
87    // If this would mean seeking back further than the start of the track, the position is set to `0`.
88    //
89    // If the value passed in would mean seeking beyond the end of the track, acts like a call to Next.
90    //
91    // If the *CanSeek* property is `false`, this has no effect.
92    fn seek(&self, offset: MprisDuration) -> impl Future<Output = zbus::fdo::Result<()>> + Send {
93        async { Ok(()) }
94    }
95
96    /// Sets the current track position.
97    ///
98    /// If the `position` argument is less than `0`, do nothing.
99    ///
100    /// If the `position` argument is greater than the track length, do nothing.
101    ///
102    /// If the *CanSeek* property is `false`, this has no effect.
103    fn set_position(
104        &self,
105        track_id: TrackId,
106        position: MprisDuration,
107    ) -> impl Future<Output = zbus::fdo::Result<()>> + Send {
108        async { Ok(()) }
109    }
110
111    /// Opens the Uri given as an argument.
112    ///
113    /// If the playback is stopped, starts playing
114    ///
115    /// If the uri scheme or the mime-type of the uri to open is not supported, this method does nothing and may raise an error.
116    /// In particular, if the list of available uri schemes is empty, this method may not be implemented.
117    ///
118    /// Clients should not assume that the Uri has been opened as soon as this method returns. They should wait until the `mpris:trackid` field in the *Metadata* property changes.
119    ///
120    /// If the media player implements the [TrackList][crate::binding::TrackList] interface, then the opened track should be made part of the tracklist, the *org.mpris.MediaPlayer2.TrackList.TrackAdded* or *org.mpris.MediaPlayer2.TrackList.TrackListReplaced* signal should be fired, as well as the *org.freedesktop.DBus.Properties.PropertiesChanged* signal on the tracklist interface.
121    fn open_uri(&self, uri: &str) -> impl Future<Output = zbus::fdo::Result<()>> + Send {
122        async {
123            Err(zbus::fdo::Error::NotSupported(
124                "not implemented: OpenUri".to_string(),
125            ))
126        }
127    }
128
129    /// The current playback status.
130    fn playback_status(&self) -> impl Future<Output = zbus::fdo::Result<PlaybackStatus>> + Send;
131
132    /// The current loop / repeat status
133    fn loop_status(&self) -> impl Future<Output = zbus::fdo::Result<LoopStatus>> + Send {
134        async { Ok(LoopStatus::None) }
135    }
136    /// Sets the current loop / repeat status.
137    ///
138    /// If *CanControl* is `false`, attempting to set this property should have no effect and raise an error.
139    fn set_loop_status(&self, value: LoopStatus) -> impl Future<Output = zbus::Result<()>> + Send {
140        async { Err(zbus::Error::Unsupported) }
141    }
142
143    /// The current playback rate.
144    ///
145    /// The value must fall in the range described by *MinimumRate* and *MaximumRate*, and must not be `0.0`.
146    /// If playback is paused, the *PlaybackStatus* property should be used to indicate this.
147    ///
148    /// If the media player has no ability to play at speeds other than the normal playback rate, this must still be implemented, and must return `1.0`.
149    /// The *MinimumRate* and *MaximumRate* properties must also be set to `1.0`.
150    fn rate(&self) -> impl Future<Output = zbus::fdo::Result<f64>> + Send {
151        async { Ok(1.0) }
152    }
153    // Sets the current playback rate.
154    //
155    /// A value of `0.0` should not be set by the client.
156    /// If it is, the media player should act as though *Pause* was called.
157    ///
158    /// Not all values may be accepted by the media player.
159    /// It is left to media player implementations to decide how to deal with values they cannot use; they may either ignore them or pick a "best fit" value.
160    /// Clients are recommended to only use sensible fractions or multiples of 1 (eg: `0.5`, `0.25`, `1.5`, `2.0`, etc).
161    fn set_rate(&self, value: f64) -> impl Future<Output = zbus::Result<()>> + Send {
162        async { Ok(()) }
163    }
164
165    /// Whether shuffle is enabled.
166    ///
167    /// A value of `false` indicates that playback is progressing linearly through a playlist, while `true` means playback is progressing through a playlist in some other order.
168    fn shuffle(&self) -> impl Future<Output = zbus::fdo::Result<bool>> + Send {
169        async { Ok(false) }
170    }
171    /// Set whether shuffle is enabled.
172    ///
173    /// If *CanControl* is `false`, attempting to set this property should have no effect and raise an error.
174    fn set_shuffle(&self, value: bool) -> impl Future<Output = zbus::Result<()>> + Send {
175        async { Err(zbus::Error::Unsupported) }
176    }
177
178    /// The metadata of the current element.
179    ///
180    /// If there is a current track, this must have a "mpris:trackid" entry at the very least, which contains a D-Bus path that uniquely identifies this track.
181    ///
182    /// See [Metadata] for more details.
183    fn metadata(&self) -> impl Future<Output = zbus::fdo::Result<Metadata>> + Send;
184
185    /// The volume level.
186    fn volume(&self) -> impl Future<Output = zbus::fdo::Result<f64>> + Send {
187        async { Ok(1.0) }
188    }
189    /// Set the volume level.
190    ///
191    /// When setting, if a negative value is passed, the volume should be set to `0.0`.
192    ///
193    /// If *CanControl* is `false`, attempting to set this property should have no effect and raise an error.
194    fn set_volume(&self, value: f64) -> impl Future<Output = zbus::Result<()>> + Send {
195        async { Err(zbus::Error::Unsupported) }
196    }
197
198    /// The current track position, between `0` and the 'mpris:length' metadata entry (see [Metadata]).
199    ///
200    /// Note: If the media player allows it, the current playback position can be changed either by the *SetPosition* method or the *Seek* method on this interface.
201    /// If this is not the case, the *CanSeek* property is `false`, and setting this property has no effect and can raise an error.
202    ///
203    /// If the playback progresses in a way that is inconsistent with the *Rate* property, the *Seeked* signal is emitted.
204    fn position(&self) -> impl Future<Output = zbus::fdo::Result<MprisDuration>> + Send;
205
206    /// The minimum value which the *Rate* property can take.
207    ///
208    /// Clients should not attempt to set the *Rate* property below this value.
209    ///
210    /// Note that even if this value is `0.0` or negative, clients should not attempt to set the *Rate* property to `0.0`.
211    ///
212    /// This value should always be `1.0` or less.
213    fn minimum_rate(&self) -> impl Future<Output = zbus::fdo::Result<f64>> + Send {
214        async { Ok(1.0) }
215    }
216
217    /// The maximum value which the *Rate* property can take.
218    ///
219    /// Clients should not attempt to set the Rate property above this value.
220    ///
221    /// This value should always be `1.0` or greater.
222    fn maximum_rate(&self) -> impl Future<Output = zbus::fdo::Result<f64>> + Send {
223        async { Ok(1.0) }
224    }
225
226    /// Whether the client can call the *Next* method on this interface and expect the current track to change.
227    ///
228    /// If it is unknown whether a call to *Next* will be successful (for example, when streaming tracks), this property should be set to `true`.
229    ///
230    /// If *CanControl* is `false`, this property should also be `false`.
231    fn can_go_next(&self) -> impl Future<Output = zbus::fdo::Result<bool>> + Send {
232        async { Ok(false) }
233    }
234
235    /// Whether the client can call the *Previous* method on this interface and expect the current track to change.
236    ///
237    /// If it is unknown whether a call to *Previous* will be successful (for example, when streaming tracks), this property should be set to `true`.
238    ///
239    /// If *CanControl* is `false`, this property should also be `false`.
240    fn can_go_previous(&self) -> impl Future<Output = zbus::fdo::Result<bool>> + Send {
241        async { Ok(false) }
242    }
243
244    /// Whether playback can be started using *Play* or *PlayPause*.
245    ///
246    /// Note that this is related to whether there is a "current track": the value should not depend on whether the track is currently paused or playing.
247    /// In fact, if a track is currently playing (and *CanControl* is `true`), this should be `true`.
248    ///
249    /// If *CanControl* is `false`, this property should also be `false`.
250    fn can_play(&self) -> impl Future<Output = zbus::fdo::Result<bool>> + Send {
251        async { Ok(false) }
252    }
253
254    /// Whether playback can be paused using *Pause* or *PlayPause*.
255    ///
256    /// Note that this is an intrinsic property of the current track: its value should not depend on whether the track is currently paused or playing.
257    /// In fact, if playback is currently paused (and *CanControl* is `true`), this should be `true`.
258    ///
259    /// If *CanControl* is `false`, this property should also be `false`.
260    fn can_pause(&self) -> impl Future<Output = zbus::fdo::Result<bool>> + Send {
261        async { Ok(false) }
262    }
263
264    /// Whether the client can control the playback position using *Seek* and *SetPosition*.
265    ///
266    /// This may be different for different tracks.
267    ///
268    /// If *CanControl* is `false`, this property should also be `false`.
269    fn can_seek(&self) -> impl Future<Output = zbus::fdo::Result<bool>> + Send {
270        async { Ok(false) }
271    }
272
273    /// Whether the media player may be controlled over this interface.
274    ///
275    /// This property is not expected to change, as it describes an intrinsic capability of the implementation.
276    ///
277    /// If this is `false`, clients should assume that all properties on this interface are read-only (and will raise errors if writing to them is attempted), no methods are implemented and all other properties starting with "*Can*" are also `false`.
278    fn can_control(&self) -> impl Future<Output = zbus::fdo::Result<bool>> + Send {
279        async { Ok(false) }
280    }
281}
282
283/// This interface implements the methods for querying and providing basic control over what is currently playing.
284///
285/// It delegates the D-Bus calls to its provider.
286pub struct Player<P>(P);
287
288impl<P> Player<P>
289where
290    P: PlayerProvider + Send + Sync + 'static,
291{
292    /// Creates a new MPRIS player that delegates to the given [PlayerProvider].
293    pub fn new(provider: P) -> Self {
294        Self(provider)
295    }
296
297    /// The reference to the underlying [PlayerProvider].
298    pub fn inner(&self) -> &P {
299        &self.0
300    }
301
302    /// The mutable reference to the underlying [PlayerProvider].
303    pub fn inner_mut(&mut self) -> &mut P {
304        &mut self.0
305    }
306}
307
308#[interface(
309    name = "org.mpris.MediaPlayer2.Player",
310    proxy(default_path = "/org/mpris/MediaPlayer2")
311)]
312impl<P> Player<P>
313where
314    P: PlayerProvider + Send + Sync + 'static,
315{
316    /// Next method
317    async fn next(&self) -> zbus::fdo::Result<()> {
318        self.0.next().await
319    }
320
321    /// Previous method
322    async fn previous(&self) -> zbus::fdo::Result<()> {
323        self.0.previous().await
324    }
325
326    /// Pause method
327    async fn pause(&self) -> zbus::fdo::Result<()> {
328        self.0.pause().await
329    }
330
331    /// PlayPause method
332    async fn play_pause(&self) -> zbus::fdo::Result<()> {
333        self.0.play_pause().await
334    }
335
336    /// Stop method
337    async fn stop(&self) -> zbus::fdo::Result<()> {
338        self.0.stop().await
339    }
340
341    /// Play method
342    async fn play(&self) -> zbus::fdo::Result<()> {
343        self.0.play().await
344    }
345
346    /// Seek method
347    async fn seek(&self, offset: MprisDuration) -> zbus::fdo::Result<()> {
348        self.0.seek(offset).await
349    }
350
351    /// SetPosition method
352    async fn set_position(
353        &self,
354        track_id: TrackId,
355        position: MprisDuration,
356    ) -> zbus::fdo::Result<()> {
357        self.0.set_position(track_id, position).await
358    }
359
360    /// OpenUri method
361    async fn open_uri(&self, uri: &str) -> zbus::fdo::Result<()> {
362        self.0.open_uri(uri).await
363    }
364
365    /// Seeked signal
366    ///
367    /// Indicates that the track position has changed in a way that is inconsistent with the current playing state.
368    ///
369    /// This signal does not need to be emitted when playback starts or when the track changes, unless the track is starting at an unexpected position.
370    /// An expected position would be the last known one when going from *Paused* to *Playing*, and `0` when going from *Stopped* to *Playing*.
371    #[zbus(signal)]
372    async fn seeked(emitter: &SignalEmitter<'_>, position: MprisDuration) -> zbus::Result<()>;
373
374    /// PlaybackStatus property
375    #[zbus(property)]
376    async fn playback_status(&self) -> zbus::fdo::Result<PlaybackStatus> {
377        self.0.playback_status().await
378    }
379
380    /// LoopStatus property
381    #[zbus(property)]
382    async fn loop_status(&self) -> zbus::fdo::Result<LoopStatus> {
383        self.0.loop_status().await
384    }
385    #[zbus(property)]
386    async fn set_loop_status(&self, value: LoopStatus) -> zbus::Result<()> {
387        self.0.set_loop_status(value).await
388    }
389
390    /// Rate property
391    #[zbus(property)]
392    async fn rate(&self) -> zbus::fdo::Result<f64> {
393        self.0.rate().await
394    }
395    #[zbus(property)]
396    async fn set_rate(&self, value: f64) -> zbus::Result<()> {
397        self.0.set_rate(value).await
398    }
399
400    /// Shuffle property
401    #[zbus(property)]
402    async fn shuffle(&self) -> zbus::fdo::Result<bool> {
403        self.0.shuffle().await
404    }
405    #[zbus(property)]
406    async fn set_shuffle(&self, value: bool) -> zbus::Result<()> {
407        self.0.set_shuffle(value).await
408    }
409
410    /// Metadata property
411    #[zbus(property)]
412    async fn metadata(&self) -> zbus::fdo::Result<Metadata> {
413        self.0.metadata().await
414    }
415
416    /// Volume property
417    #[zbus(property)]
418    async fn volume(&self) -> zbus::fdo::Result<f64> {
419        self.0.volume().await
420    }
421    #[zbus(property)]
422    async fn set_volume(&self, value: f64) -> zbus::Result<()> {
423        self.0.set_volume(value).await
424    }
425
426    /// Position property
427    #[zbus(property(emits_changed_signal = "false"))]
428    async fn position(&self) -> zbus::fdo::Result<MprisDuration> {
429        self.0.position().await
430    }
431
432    /// MinimumRate property
433    #[zbus(property)]
434    async fn minimum_rate(&self) -> zbus::fdo::Result<f64> {
435        self.0.minimum_rate().await
436    }
437
438    /// MaximumRate property
439    #[zbus(property)]
440    async fn maximum_rate(&self) -> zbus::fdo::Result<f64> {
441        self.0.maximum_rate().await
442    }
443
444    /// CanGoNext property
445    #[zbus(property)]
446    async fn can_go_next(&self) -> zbus::fdo::Result<bool> {
447        self.0.can_go_next().await
448    }
449
450    /// CanGoPrevious property
451    #[zbus(property)]
452    async fn can_go_previous(&self) -> zbus::fdo::Result<bool> {
453        self.0.can_go_previous().await
454    }
455
456    /// CanPlay property
457    #[zbus(property)]
458    async fn can_play(&self) -> zbus::fdo::Result<bool> {
459        self.0.can_play().await
460    }
461
462    /// CanPause property
463    #[zbus(property)]
464    async fn can_pause(&self) -> zbus::fdo::Result<bool> {
465        self.0.can_pause().await
466    }
467
468    /// CanSeek property
469    #[zbus(property)]
470    async fn can_seek(&self) -> zbus::fdo::Result<bool> {
471        self.0.can_seek().await
472    }
473
474    /// CanControl property
475    #[zbus(property(emits_changed_signal = "false"))]
476    async fn can_control(&self) -> zbus::fdo::Result<bool> {
477        self.0.can_control().await
478    }
479}