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}