mpris_server/lib.rs
1#![cfg_attr(docsrs, feature(doc_cfg))]
2#![warn(rust_2018_idioms, missing_debug_implementations)]
3#![deny(rustdoc::broken_intra_doc_links)]
4#![doc = include_str!("../README.md")]
5
6// TODO:
7// * Document the rest of public interface
8// * Access Server from interfaces
9// * Replace `DateTime` and `Uri` with proper types
10// * Add sensible default method impls on `*Interface` traits
11// * Profile if inlining is worth it
12// * Add public `test` method to check if interface is implemented correctly
13// * Avoid clones in `Metadata` getters
14
15mod local_server;
16mod loop_status;
17mod metadata;
18mod playback_status;
19mod player;
20mod playlist;
21mod playlist_ordering;
22mod property;
23mod server;
24mod signal;
25mod time;
26mod track_id;
27
28/// This contains the definitions of builder-pattern structs.
29///
30/// The `builder` methods on the objects must be used instead to construct
31/// these builder-pattern structs.
32pub mod builder {
33 pub use crate::{metadata::MetadataBuilder, player::PlayerBuilder};
34}
35
36pub use zbus;
37use zbus::{fdo, zvariant::OwnedObjectPath, Result};
38
39pub use crate::{
40 local_server::{LocalServer, LocalServerRunTask},
41 loop_status::LoopStatus,
42 metadata::{DateTime, Metadata},
43 playback_status::PlaybackStatus,
44 player::Player,
45 playlist::Playlist,
46 playlist_ordering::PlaylistOrdering,
47 property::{PlaylistsProperty, Property, TrackListProperty},
48 server::Server,
49 signal::{PlaylistsSignal, Signal, TrackListSignal},
50 time::Time,
51 track_id::TrackId,
52};
53
54macro_rules! define_iface {
55 (#[$attr:meta],
56 $root_iface_ident:ident extra_docs $extra_root_docs:literal,
57 $player_iface_ident:ident extra_docs $extra_player_docs:literal,
58 $track_list_iface_ident:ident extra_docs $extra_track_list_docs:literal,
59 $playlists_iface_ident:ident extra_docs $extra_playlists_docs:literal) => {
60 #[doc = $extra_root_docs]
61 #[doc = ""]
62 /// Used to implement [org.mpris.MediaPlayer2] interface.
63 ///
64 /// [org.mpris.MediaPlayer2]: https://specifications.freedesktop.org/mpris-spec/latest/Media_Player.html
65 #[$attr]
66 pub trait $root_iface_ident {
67 /// Brings the media player's user interface to the front using any
68 /// appropriate mechanism available.
69 ///
70 /// The media player may be unable to control how its user interface is
71 /// displayed, or it may not have a graphical user interface at all. In this
72 /// case, the [`CanRaise`] property is **false** and this method does
73 /// nothing.
74 ///
75 /// [`CanRaise`]: Self::can_raise
76 #[doc(alias = "Raise")]
77 async fn raise(&self) -> fdo::Result<()>;
78
79 /// Causes the media player to stop running.
80 ///
81 /// The media player may refuse to allow clients to shut it down. In this
82 /// case, the [`CanQuit`] property is **false** and this method does
83 /// nothing.
84 ///
85 /// **Note:** Media players which can be D-Bus activated, or for which there
86 /// is no sensibly easy way to terminate a running instance (via the
87 /// main interface or a notification area icon for example) should allow
88 /// clients to use this method. Otherwise, it should not be needed.
89 ///
90 /// If the media player does not have a UI, this should be implemented.
91 ///
92 /// [`CanQuit`]: Self::can_quit
93 #[doc(alias = "Quit")]
94 async fn quit(&self) -> fdo::Result<()>;
95
96 /// Whether the player may be asked to quit.
97 ///
98 /// When this property changes, the
99 /// `org.freedesktop.DBus.Properties.PropertiesChanged` signal via
100 /// [`properties_changed`] must be emitted with the new value.
101 ///
102 /// If **false**, calling [`Quit`] will have no effect, and may raise a
103 /// [`NotSupported`] error. If **true**, calling [`Quit`] will cause the media
104 /// application to attempt to quit (although it may still be prevented from
105 /// quitting by the user, for example).
106 ///
107 /// [`properties_changed`]: Server::properties_changed
108 /// [`Quit`]: Self::quit
109 /// [`NotSupported`]: fdo::Error::NotSupported
110 #[doc(alias = "CanQuit")]
111 async fn can_quit(&self) -> fdo::Result<bool>;
112
113 /// Whether the media player is occupying the fullscreen.
114 ///
115 /// This property is *optional*. Clients should handle its absence
116 /// gracefully.
117 ///
118 /// When this property changes, the
119 /// `org.freedesktop.DBus.Properties.PropertiesChanged` signal via
120 /// [`properties_changed`] must be emitted with the new value.
121 ///
122 /// This is typically used for videos. A value of **true** indicates that
123 /// the media player is taking up the full screen.
124 ///
125 /// Media centre software may well have this value fixed to **true**
126 ///
127 /// If [`CanSetFullscreen`] is **true**, clients may set this property to
128 /// **true** to tell the media player to enter fullscreen mode, or to
129 /// **false** to return to windowed mode.
130 ///
131 /// If [`CanSetFullscreen`] is **false**, then attempting to set this
132 /// property should have no effect, and may raise an error. However,
133 /// even if it is **true**, the media player may still be unable to
134 /// fulfil the request, in which case attempting to set this property
135 /// will have no effect (but should not raise an error).
136 ///
137 /// <details><summary>Rationale</summary>
138 ///
139 /// This allows remote control interfaces, such as LIRC or mobile devices
140 /// like phones, to control whether a video is shown in fullscreen.
141 ///
142 /// </details>
143 ///
144 /// [`properties_changed`]: Server::properties_changed
145 /// [`CanSetFullscreen`]: Self::can_set_fullscreen
146 #[doc(alias = "Fullscreen")]
147 async fn fullscreen(&self) -> fdo::Result<bool>;
148
149 /// Sets whether the media player is occupying the fullscreen.
150 ///
151 /// See [`Fullscreen`] for more details.
152 ///
153 /// [`Fullscreen`]: Self::fullscreen
154 #[doc(alias = "Fullscreen")]
155 async fn set_fullscreen(&self, fullscreen: bool) -> Result<()>;
156
157 /// Whether the player may be asked to enter or leave fullscreen.
158 ///
159 /// This property is *optional*. Clients should handle its absence
160 /// gracefully.
161 ///
162 /// When this property changes, the
163 /// `org.freedesktop.DBus.Properties.PropertiesChanged` signal via
164 /// [`properties_changed`] must be emitted with the new value.
165 ///
166 /// If **false**, attempting to set [`Fullscreen`] will have no effect, and
167 /// may raise an error. If **true**, attempting to set [`Fullscreen`]
168 /// will not raise an error, and (if it is different from the current
169 /// value) will cause the media player to attempt to enter or exit
170 /// fullscreen mode.
171 ///
172 /// Note that the media player may be unable to fulfil the request. In this
173 /// case, the value will not change. If the media player knows in advance
174 /// that it will not be able to fulfil the request, however, this property
175 /// should be **false**.
176 ///
177 /// <details><summary>Rationale</summary>
178 ///
179 /// This allows clients to choose whether to display controls for entering
180 /// or exiting fullscreen mode.
181 ///
182 /// </details>
183 ///
184 /// [`properties_changed`]: Server::properties_changed
185 /// [`Fullscreen`]: Self::fullscreen
186 #[doc(alias = "CanSetFullscreen")]
187 async fn can_set_fullscreen(&self) -> fdo::Result<bool>;
188
189 /// Whether the media player may be asked to be raised.
190 ///
191 /// When this property changes, the
192 /// `org.freedesktop.DBus.Properties.PropertiesChanged` signal via
193 /// [`properties_changed`] must be emitted with the new value.
194 ///
195 /// If **false**, calling [`Raise`] will have no effect, and may raise a
196 /// [`NotSupported`] error. If **true**, calling [`Raise`] will cause the
197 /// media application to attempt to bring its user interface to the
198 /// front, although it may be prevented from doing so (by the window
199 /// manager, for example).
200 ///
201 /// [`properties_changed`]: Server::properties_changed
202 /// [`Raise`]: Self::raise
203 /// [`NotSupported`]: fdo::Error::NotSupported
204 #[doc(alias = "CanRaise")]
205 async fn can_raise(&self) -> fdo::Result<bool>;
206
207 /// Indicates whether the `/org/mpris/MediaPlayer2` object implements the
208 /// [`TrackList interface`].
209 ///
210 /// When this property changes, the
211 /// `org.freedesktop.DBus.Properties.PropertiesChanged` signal via
212 /// [`properties_changed`] must be emitted with the new value.
213 ///
214 /// [`TrackList interface`]: TrackListInterface
215 /// [`properties_changed`]: Server::properties_changed
216 #[doc(alias = "HasTrackList")]
217 async fn has_track_list(&self) -> fdo::Result<bool>;
218
219 /// A friendly name to identify the media player to users (eg: "VLC media
220 /// player").
221 ///
222 /// When this property changes, the
223 /// `org.freedesktop.DBus.Properties.PropertiesChanged` signal via
224 /// [`properties_changed`] must be emitted with the new value.
225 ///
226 /// This should usually match the name found in .desktop files
227 ///
228 /// [`properties_changed`]: Server::properties_changed
229 #[doc(alias = "Identity")]
230 async fn identity(&self) -> fdo::Result<String>;
231
232 /// The basename of an installed .desktop file which complies with the
233 /// [Desktop entry specification], with the ".desktop" extension stripped.
234 ///
235 /// This property is *optional*. Clients should handle its absence
236 /// gracefully.
237 ///
238 /// When this property changes, the
239 /// `org.freedesktop.DBus.Properties.PropertiesChanged` signal via
240 /// [`properties_changed`] must be emitted with the new value.
241 ///
242 /// Example: The desktop entry file is
243 /// "/usr/share/applications/vlc.desktop", and this property contains "vlc"
244 ///
245 /// [Desktop entry specification]: https://specifications.freedesktop.org/desktop-entry-spec/latest/
246 /// [`properties_changed`]: Server::properties_changed
247 #[doc(alias = "DesktopEntry")]
248 async fn desktop_entry(&self) -> fdo::Result<String>;
249
250 /// The URI schemes supported by the media player.
251 ///
252 /// When this property changes, the
253 /// `org.freedesktop.DBus.Properties.PropertiesChanged` signal via
254 /// [`properties_changed`] must be emitted with the new value.
255 ///
256 /// This can be viewed as protocols supported by the player in almost all
257 /// cases. Almost every media player will include support for the "file"
258 /// scheme. Other common schemes are "http" and "rtsp".
259 ///
260 /// Note that URI schemes should be lower-case.
261 ///
262 /// <details><summary>Rationale</summary>
263 ///
264 /// This is important for clients to know when using the editing
265 /// capabilities of the [`Playlists interface`], for example.
266 ///
267 /// </details>
268 ///
269 /// [`properties_changed`]: Server::properties_changed
270 /// [`Playlists interface`]: PlaylistsInterface
271 #[doc(alias = "SupportedUriSchemes")]
272 async fn supported_uri_schemes(&self) -> fdo::Result<Vec<String>>;
273
274 /// The mime-types supported by the media player.
275 ///
276 /// When this property changes, the
277 /// `org.freedesktop.DBus.Properties.PropertiesChanged` signal via
278 /// [`properties_changed`] must be emitted with the new value.
279 ///
280 /// Mime-types should be in the standard format (eg: audio/mpeg or
281 /// application/ogg).
282 ///
283 /// <details><summary>Rationale</summary>
284 ///
285 /// This is important for clients to know when using the editing
286 /// capabilities of the [`Playlists interface`], for example.
287 ///
288 /// </details>
289 ///
290 /// [`properties_changed`]: Server::properties_changed
291 /// [`Playlists interface`]: PlaylistsInterface
292 #[doc(alias = "SupportedMimeTypes")]
293 async fn supported_mime_types(&self) -> fdo::Result<Vec<String>>;
294 }
295
296 #[doc = $extra_player_docs]
297 #[doc = ""]
298 /// Used to implement [org.mpris.MediaPlayer2.Player] interface, which
299 /// implements the methods for querying and providing basic control over what is
300 /// currently playing.
301 ///
302 /// [org.mpris.MediaPlayer2.Player]: https://specifications.freedesktop.org/mpris-spec/latest/Player_Interface.html
303 #[$attr]
304 #[doc(alias = "org.mpris.MediaPlayer2.Player")]
305 pub trait $player_iface_ident: $root_iface_ident {
306 /// Skips to the next track in the tracklist.
307 ///
308 /// If there is no next track (and endless playback and track repeat are
309 /// both off), stop playback.
310 ///
311 /// If playback is paused or stopped, it remains that way.
312 ///
313 /// If [`CanGoNext`] is **false**, attempting to call this method should
314 /// have no effect.
315 ///
316 /// [`CanGoNext`]: Self::can_go_next
317 #[doc(alias = "Next")]
318 async fn next(&self) -> fdo::Result<()>;
319
320 /// Skips to the previous track in the tracklist.
321 ///
322 /// If there is no previous track (and endless playback and track repeat are
323 /// both off), stop playback.
324 ///
325 /// If playback is paused or stopped, it remains that way.
326 ///
327 /// If [`CanGoPrevious`] is **false**, attempting to call this method should
328 /// have no effect.
329 ///
330 /// [`CanGoPrevious`]: Self::can_go_previous
331 #[doc(alias = "Previous")]
332 async fn previous(&self) -> fdo::Result<()>;
333
334 /// Pauses playback.
335 ///
336 /// If playback is already paused, this has no effect.
337 ///
338 /// Calling [`Play`] after this should cause playback to start again from
339 /// the same position.
340 ///
341 /// If [`CanPause`] is **false**, attempting to call this method should have
342 /// no effect.
343 ///
344 /// [`Play`]: Self::play
345 /// [`CanPause`]: Self::can_pause
346 #[doc(alias = "Pause")]
347 async fn pause(&self) -> fdo::Result<()>;
348
349 /// Pauses playback.
350 ///
351 /// If playback is already paused, resumes playback.
352 ///
353 /// If playback is stopped, starts playback.
354 ///
355 /// If [`CanPause`] is **false**, attempting to call this method should have
356 /// no effect and raise an error.
357 ///
358 /// [`CanPause`]: Self::can_pause
359 #[doc(alias = "PlayPause")]
360 async fn play_pause(&self) -> fdo::Result<()>;
361
362 /// Stops playback.
363 ///
364 /// If playback is already stopped, this has no effect.
365 ///
366 /// Calling Play after this should cause playback to start again from the
367 /// beginning of the track.
368 ///
369 /// If [`CanControl`] is **false**, attempting to call this method should
370 /// have no effect and raise an error.
371 ///
372 /// [`CanControl`]: Self::can_control
373 #[doc(alias = "Stop")]
374 async fn stop(&self) -> fdo::Result<()>;
375
376 /// Starts or resumes playback.
377 ///
378 /// If already playing, this has no effect.
379 ///
380 /// If paused, playback resumes from the current position.
381 ///
382 /// If there is no track to play, this has no effect.
383 ///
384 /// If [`CanPlay`] is **false**, attempting to call this method should have
385 /// no effect.
386 ///
387 /// [`CanPlay`]: Self::can_play
388 #[doc(alias = "Play")]
389 async fn play(&self) -> fdo::Result<()>;
390
391 /// Seeks forward in the current track by the specified offset in time.
392 ///
393 /// # Parameters
394 ///
395 /// * `offset` - The offset in time to seek forward.
396 ///
397 /// A negative value seeks back. If this would mean seeking back further
398 /// than the start of the track, the position is set to 0.
399 ///
400 /// If the value passed in would mean seeking beyond the end of the track,
401 /// acts like a call to Next.
402 ///
403 /// If the [`CanSeek`] property is **false**, this has no effect.
404 ///
405 /// [`CanSeek`]: Self::can_seek
406 #[doc(alias = "Seek")]
407 async fn seek(&self, offset: Time) -> fdo::Result<()>;
408
409 /// Sets the current track position.
410 ///
411 /// # Parameters
412 ///
413 /// * `track_id` - The currently playing track's identifier. If this does
414 /// not match the id of the currently-playing track, the call is ignored
415 /// as "stale". [`/org/mpris/MediaPlayer2/TrackList/NoTrack`] is not a
416 /// valid value for this argument.
417 /// * `position` - The track position. This must be between 0 and
418 /// <track_length>.
419 ///
420 /// If the Position argument is less than 0, do nothing.
421 ///
422 /// If the Position argument is greater than the track length, do nothing.
423 ///
424 /// If the [`CanSeek`] property is **false**, this has no effect.
425 ///
426 /// <details><summary>Rationale</summary>
427 ///
428 /// The reason for having this method, rather than making [`Position`]
429 /// writable, is to include the `track_id` argument to avoid race
430 /// conditions where a client tries to seek to a position when the track
431 /// has already changed.
432 ///
433 /// </details>
434 ///
435 /// [`/org/mpris/MediaPlayer2/TrackList/NoTrack`]: TrackId::NO_TRACK
436 /// [`CanSeek`]: Self::can_seek
437 /// [`Position`]: Self::position
438 #[doc(alias = "SetPosition")]
439 async fn set_position(&self, track_id: TrackId, position: Time) -> fdo::Result<()>;
440
441 /// Opens the `uri` given as an argument
442 ///
443 /// # Parameters
444 ///
445 /// * `uri` - Uri of the track to load. Its uri scheme should be an element
446 /// of the [`SupportedUriSchemes`] property and the mime-type should match
447 /// one of the elements of the [`SupportedMimeTypes`].
448 ///
449 /// If the playback is stopped, starts playing
450 ///
451 /// If the uri scheme or the mime-type of the uri to open is not supported,
452 /// this method does nothing and may raise an error. In particular, if the
453 /// list of available uri schemes is empty, this method may not be
454 /// implemented.
455 ///
456 /// Clients should not assume that the `uri` has been opened as soon as this
457 /// method returns. They should wait until the [`mpris:trackid`] field in
458 /// the [`Metadata`] property changes.
459 ///
460 /// If the media player implements the [`TrackList interface`], then the
461 /// opened track should be made part of the tracklist, the [`TrackAdded`] or
462 /// [`TrackListReplaced`] signal should be fired, as well as the
463 /// `org.freedesktop.DBus.Properties.PropertiesChanged` signal on the
464 /// [`TrackList interface`].
465 ///
466 /// [`SupportedUriSchemes`]: RootInterface::supported_uri_schemes
467 /// [`SupportedMimeTypes`]: RootInterface::supported_mime_types
468 /// [`mpris:trackid`]: Metadata::set_trackid
469 /// [`Metadata`]: Self::metadata
470 /// [`TrackList interface`]: TrackListInterface
471 /// [`TrackAdded`]: TrackListSignal::TrackAdded
472 /// [`TrackListReplaced`]: TrackListSignal::TrackListReplaced
473 #[doc(alias = "OpenUri")]
474 async fn open_uri(&self, uri: String) -> fdo::Result<()>;
475
476 /// The current playback status.
477 ///
478 /// When this property changes, the
479 /// `org.freedesktop.DBus.Properties.PropertiesChanged` signal via
480 /// [`properties_changed`] must be emitted with the new value.
481 ///
482 /// May be [`Playing`], [`Paused`] or [`Stopped`].
483 ///
484 /// [`properties_changed`]: Server::properties_changed
485 /// [`Playing`]: PlaybackStatus::Playing
486 /// [`Paused`]: PlaybackStatus::Paused
487 /// [`Stopped`]: PlaybackStatus::Stopped
488 #[doc(alias = "PlaybackStatus")]
489 async fn playback_status(&self) -> fdo::Result<PlaybackStatus>;
490
491 /// The current loop / repeat status
492 ///
493 /// This property is *optional*. Clients should handle its absence
494 /// gracefully.
495 ///
496 /// When this property changes, the
497 /// `org.freedesktop.DBus.Properties.PropertiesChanged` signal via
498 /// [`properties_changed`] must be emitted with the new value.
499 ///
500 /// May be:
501 ///
502 /// * [`None`] if the playback will stop when there are no more tracks to
503 /// play
504 /// * [`Track`] if the current track will start again from the beginning
505 /// once it has finished playing
506 /// * [`Playlist`] if the playback loops through a list of tracks
507 ///
508 /// If [`CanControl`] is **false**, attempting to set this property should
509 /// have no effect and raise an error.
510 ///
511 /// [`properties_changed`]: Server::properties_changed
512 /// [`None`]: LoopStatus::None
513 /// [`Track`]: LoopStatus::Track
514 /// [`Playlist`]: LoopStatus::Playlist
515 /// [`CanControl`]: Self::can_control
516 #[doc(alias = "LoopStatus")]
517 async fn loop_status(&self) -> fdo::Result<LoopStatus>;
518
519 /// Sets the current loop / repeat status
520 ///
521 /// See [`LoopStatus`] for more details.
522 ///
523 /// [`LoopStatus`]: Self::loop_status
524 #[doc(alias = "LoopStatus")]
525 async fn set_loop_status(&self, loop_status: LoopStatus) -> Result<()>;
526
527 /// The current playback rate.
528 ///
529 /// When this property changes, the
530 /// `org.freedesktop.DBus.Properties.PropertiesChanged` signal via
531 /// [`properties_changed`] must be emitted with the new value.
532 ///
533 /// The value must fall in the range described by [`MinimumRate`] and
534 /// [`MaximumRate`], and must not be 0.0. If playback is paused, the
535 /// [`PlaybackStatus`] property should be used to indicate this. A value of
536 /// 0.0 should not be set by the client. If it is, the media player
537 /// should act as though [`Pause`] was called.
538 ///
539 /// If the media player has no ability to play at speeds other than the
540 /// normal playback rate, this must still be implemented, and must return
541 /// 1.0. The [`MinimumRate`] and [`MaximumRate`] properties must also be set
542 /// to 1.0.
543 ///
544 /// Not all values may be accepted by the media player. It is left to media
545 /// player implementations to decide how to deal with values they cannot
546 /// use; they may either ignore them or pick a "best fit" value. Clients are
547 /// recommended to only use sensible fractions or multiples of 1 (eg: 0.5,
548 /// 0.25, 1.5, 2.0, etc).
549 ///
550 /// <details><summary>Rationale</summary>
551 ///
552 /// This allows clients to display (reasonably) accurate progress bars
553 /// without having to regularly query the media player for the current
554 /// position.
555 ///
556 /// </details>
557 ///
558 /// [`properties_changed`]: Server::properties_changed
559 /// [`MinimumRate`]: Self::minimum_rate
560 /// [`MaximumRate`]: Self::maximum_rate
561 /// [`PlaybackStatus`]: Self::playback_status
562 /// [`Pause`]: Self::pause
563 #[doc(alias = "Rate")]
564 async fn rate(&self) -> fdo::Result<PlaybackRate>;
565
566 /// Sets the current playback rate.
567 ///
568 /// See [`Rate`] for more details.
569 ///
570 /// [`Rate`]: Self::rate
571 #[doc(alias = "Rate")]
572 async fn set_rate(&self, rate: PlaybackRate) -> Result<()>;
573
574 /// Whether playback is shuffled.
575 ///
576 /// This property is *optional*. Clients should handle its absence
577 /// gracefully.
578 ///
579 /// When this property changes, the
580 /// `org.freedesktop.DBus.Properties.PropertiesChanged` signal via
581 /// [`properties_changed`] must be emitted with the new value.
582 ///
583 /// A value of **false** indicates that playback is progressing linearly
584 /// through a playlist, while **true** means playback is progressing through
585 /// a playlist in some other order.
586 ///
587 /// If [`CanControl`] is **false**, attempting to set this property should
588 /// have no effect and raise an error.
589 ///
590 /// [`properties_changed`]: Server::properties_changed
591 /// [`CanControl`]: Self::can_control
592 #[doc(alias = "Shuffle")]
593 async fn shuffle(&self) -> fdo::Result<bool>;
594
595 /// Sets whether playback is shuffled.
596 ///
597 /// See [`Shuffle`] for more details.
598 ///
599 /// [`Shuffle`]: Self::shuffle
600 #[doc(alias = "Shuffle")]
601 async fn set_shuffle(&self, shuffle: bool) -> Result<()>;
602
603 /// The metadata of the current element.
604 ///
605 /// When this property changes, the
606 /// `org.freedesktop.DBus.Properties.PropertiesChanged` signal via
607 /// [`properties_changed`] must be emitted with the new value.
608 ///
609 /// If there is a current track, this must have a [`mpris:trackid`] entry at
610 /// the very least, which contains a D-Bus path that uniquely identifies
611 /// this track.
612 ///
613 /// [`properties_changed`]: Server::properties_changed
614 /// [`mpris:trackid`]: Metadata::set_trackid
615 #[doc(alias = "Metadata")]
616 async fn metadata(&self) -> fdo::Result<Metadata>;
617
618 /// The volume level.
619 ///
620 /// When this property changes, the
621 /// `org.freedesktop.DBus.Properties.PropertiesChanged` signal via
622 /// [`properties_changed`] must be emitted with the new value.
623 ///
624 /// When setting, if a negative value is passed, the volume should be set to
625 /// 0.0.
626 ///
627 /// If [`CanControl`] is **false**, attempting to set this property should
628 /// have no effect and raise an error.
629 ///
630 /// [`properties_changed`]: Server::properties_changed
631 /// [`CanControl`]: Self::can_control
632 #[doc(alias = "Volume")]
633 async fn volume(&self) -> fdo::Result<Volume>;
634
635 /// Sets the volume level.
636 ///
637 /// See [`Volume`] for more details.
638 ///
639 /// [`Volume`]: Self::volume
640 #[doc(alias = "Volume")]
641 async fn set_volume(&self, volume: Volume) -> Result<()>;
642
643 /// The current track position, between 0 and the [`mpris:length`]
644 /// metadata entry.
645 ///
646 /// When this property changes, the
647 /// `org.freedesktop.DBus.Properties.PropertiesChanged` signal via
648 /// [`properties_changed`] must *not* be emitted.
649 ///
650 /// **Note:** If the media player allows it, the current playback position
651 /// can be changed either the [`SetPosition`] method or the [`Seek`]
652 /// method on this interface. If this is not the case, the [`CanSeek`]
653 /// property is **false**, and setting this property has no effect and
654 /// can raise an error.
655 ///
656 /// If the playback progresses in a way that is inconstistent with the
657 /// [`Rate`] property, the [`Seeked`] signal is emitted.
658 ///
659 /// [`mpris:length`]: Metadata::set_length
660 /// [`properties_changed`]: Server::properties_changed
661 /// [`SetPosition`]: Self::set_position
662 /// [`Seek`]: Self::seek
663 /// [`CanSeek`]: Self::can_seek
664 /// [`Rate`]: Self::rate
665 /// [`Seeked`]: Signal::Seeked
666 #[doc(alias = "Position")]
667 async fn position(&self) -> fdo::Result<Time>;
668
669 /// The minimum value which the [`Rate`] property can take. Clients should
670 /// not attempt to set the [`Rate`] property below this value.
671 ///
672 /// When this property changes, the
673 /// `org.freedesktop.DBus.Properties.PropertiesChanged` signal via
674 /// [`properties_changed`] must be emitted with the new value.
675 ///
676 /// Note that even if this value is 0.0 or negative, clients should not
677 /// attempt to set the [`Rate`] property to 0.0.
678 ///
679 /// This value should always be 1.0 or less.
680 ///
681 /// [`Rate`]: Self::rate
682 /// [`properties_changed`]: Server::properties_changed
683 #[doc(alias = "MinimumRate")]
684 async fn minimum_rate(&self) -> fdo::Result<PlaybackRate>;
685
686 /// The maximum value which the [`Rate`] property can take. Clients should
687 /// not attempt to set the [`Rate`] property above this value.
688 ///
689 /// When this property changes, the
690 /// `org.freedesktop.DBus.Properties.PropertiesChanged` signal via
691 /// [`properties_changed`] must be emitted with the new value.
692 ///
693 /// This value should always be 1.0 or greater.
694 ///
695 /// [`Rate`]: Self::rate
696 /// [`properties_changed`]: Server::properties_changed
697 #[doc(alias = "MaximumRate")]
698 async fn maximum_rate(&self) -> fdo::Result<PlaybackRate>;
699
700 /// Whether the client can call the [`Next`] method on this interface and
701 /// expect the current track to change.
702 ///
703 /// When this property changes, the
704 /// `org.freedesktop.DBus.Properties.PropertiesChanged` signal via
705 /// [`properties_changed`] must be emitted with the new value.
706 ///
707 /// If it is unknown whether a call to [`Next`] will be successful (for
708 /// example, when streaming tracks), this property should be set to
709 /// **true**.
710 ///
711 /// If [`CanControl`] is **false**, this property should also be **false**.
712 ///
713 /// <details><summary>Rationale</summary>
714 ///
715 /// Even when playback can generally be controlled, there may not always be
716 /// a next track to move to.
717 ///
718 /// </details>
719 ///
720 /// [`Next`]: Self::next
721 /// [`properties_changed`]: Server::properties_changed
722 /// [`CanControl`]: Self::can_control
723 #[doc(alias = "CanGoNext")]
724 async fn can_go_next(&self) -> fdo::Result<bool>;
725
726 /// Whether the client can call the [`Previous`] method on this interface
727 /// and expect the current track to change.
728 ///
729 /// When this property changes, the
730 /// `org.freedesktop.DBus.Properties.PropertiesChanged` signal via
731 /// [`properties_changed`] must be emitted with the new value.
732 ///
733 /// If it is unknown whether a call to [`Previous`] will be successful (for
734 /// example, when streaming tracks), this property should be set to
735 /// **true**.
736 ///
737 /// If [`CanControl`] is **false**, this property should also be **false**.
738 ///
739 /// <details><summary>Rationale</summary>
740 ///
741 /// Even when playback can generally be controlled, there may not always be
742 /// a next previous to move to.
743 ///
744 /// </details>
745 ///
746 /// [`Previous`]: Self::previous
747 /// [`properties_changed`]: Server::properties_changed
748 /// [`CanControl`]: Self::can_control
749 #[doc(alias = "CanGoPrevious")]
750 async fn can_go_previous(&self) -> fdo::Result<bool>;
751
752 /// Whether playback can be started using [`Play`] or [`PlayPause`].
753 ///
754 /// When this property changes, the
755 /// `org.freedesktop.DBus.Properties.PropertiesChanged` signal via
756 /// [`properties_changed`] must be emitted with the new value.
757 ///
758 /// Note that this is related to whether there is a "current track": the
759 /// value should not depend on whether the track is currently paused or
760 /// playing. In fact, if a track is currently playing (and [`CanControl`] is
761 /// **true**), this should be **true**.
762 ///
763 /// If [`CanControl`] is **false**, this property should also be **false**.
764 ///
765 /// <details><summary>Rationale</summary>
766 ///
767 /// Even when playback can generally be controlled, it may not be possible
768 /// to enter a "playing" state, for example if there is no "current track".
769 ///
770 /// </details>
771 ///
772 /// [`Play`]: Self::play
773 /// [`PlayPause`]: Self::play_pause
774 /// [`properties_changed`]: Server::properties_changed
775 /// [`CanControl`]: Self::can_control
776 #[doc(alias = "CanPlay")]
777 async fn can_play(&self) -> fdo::Result<bool>;
778
779 /// Whether playback can be paused using [`Pause`] or [`PlayPause`].
780 ///
781 /// When this property changes, the
782 /// `org.freedesktop.DBus.Properties.PropertiesChanged` signal via
783 /// [`properties_changed`] must be emitted with the new value.
784 ///
785 /// Note that this is an intrinsic property of the current track: its value
786 /// should not depend on whether the track is currently paused or playing.
787 /// In fact, if playback is currently paused (and [`CanControl`] is
788 /// **true**), this should be **true**.
789 ///
790 /// If [`CanControl`] is **false**, this property should also be **false**.
791 ///
792 /// <details><summary>Rationale</summary>
793 ///
794 /// Not all media is pausable: it may not be possible to pause some streamed
795 /// media, for example.
796 ///
797 /// </details>
798 ///
799 /// [`Pause`]: Self::pause
800 /// [`PlayPause`]: Self::play_pause
801 /// [`properties_changed`]: Server::properties_changed
802 /// [`CanControl`]: Self::can_control
803 #[doc(alias = "CanPause")]
804 async fn can_pause(&self) -> fdo::Result<bool>;
805
806 /// Whether the client can control the playback position using [`Seek`] and
807 /// [`SetPosition`]. This may be different for different tracks.
808 ///
809 /// When this property changes, the
810 /// `org.freedesktop.DBus.Properties.PropertiesChanged` signal via
811 /// [`properties_changed`] must be emitted with the new value.
812 ///
813 /// If [`CanControl`] is **false**, this property should also be **false**.
814 ///
815 /// <details><summary>Rationale</summary>
816 ///
817 /// Not all media is seekable: it may not be possible to seek when playing
818 /// some streamed media, for example.
819 ///
820 /// </details>
821 ///
822 /// [`Seek`]: Self::seek
823 /// [`SetPosition`]: Self::set_position
824 /// [`properties_changed`]: Server::properties_changed
825 /// [`CanControl`]: Self::can_control
826 #[doc(alias = "CanSeek")]
827 async fn can_seek(&self) -> fdo::Result<bool>;
828
829 /// Whether the media player may be controlled over this interface.
830 ///
831 /// When this property changes, the
832 /// `org.freedesktop.DBus.Properties.PropertiesChanged` signal via
833 /// [`properties_changed`] must *not* be emitted.
834 ///
835 /// This property is not expected to change, as it describes an intrinsic
836 /// capability of the implementation.
837 ///
838 /// If this is **false**, clients should assume that all properties on this
839 /// interface are read-only (and will raise errors if writing to them is
840 /// attempted), no methods are implemented and all other properties starting
841 /// with `Can` are also **false**.
842 ///
843 /// <details><summary>Rationale</summary>
844 ///
845 /// This allows clients to determine whether to present and enable controls
846 /// to the user in advance of attempting to call methods and write to
847 /// properties.
848 ///
849 /// </details>
850 ///
851 /// [`properties_changed`]: Server::properties_changed
852 #[doc(alias = "CanControl")]
853 async fn can_control(&self) -> fdo::Result<bool>;
854 }
855
856 #[doc = $extra_track_list_docs]
857 #[doc = ""]
858 /// Used to implement [org.mpris.MediaPlayer2.TrackList] interface, which
859 /// provides access to a short list of tracks which were recently played or will
860 /// be played shortly. This is intended to provide context to the
861 /// currently-playing track, rather than giving complete access to the media
862 /// player's playlist.
863 ///
864 /// Example use cases are the list of tracks from the same album as the
865 /// currently playing song or the [Rhythmbox] play queue.
866 ///
867 /// Each track in the tracklist has a unique identifier. The intention is that
868 /// this uniquely identifies the track within the scope of the tracklist. In
869 /// particular, if a media item (a particular music file, say) occurs twice in
870 /// the track list, each occurrence should have a different identifier. If a
871 /// track is removed from the middle of the playlist, it should not affect the
872 /// track ids of any other tracks in the tracklist.
873 ///
874 /// As a result, the traditional track identifiers of URLs and position in the
875 /// playlist cannot be used. Any scheme which satisfies the uniqueness
876 /// requirements is valid, as clients should not make any assumptions about the
877 /// value of the track id beyond the fact that it is a unique identifier.
878 ///
879 /// Note that the (memory and processing) burden of implementing this interface
880 /// and maintaining unique track ids for the playlist can be mitigated by only
881 /// exposing a subset of the playlist when it is very long (the 20 or so tracks
882 /// around the currently playing track, for example). This is a recommended
883 /// practice as the tracklist interface is not designed to enable browsing
884 /// through a large list of tracks, but rather to provide clients with context
885 /// about the currently playing track.
886 ///
887 /// [org.mpris.MediaPlayer2.TrackList]: https://specifications.freedesktop.org/mpris-spec/latest/Track_List_Interface.html
888 /// [Rhythmbox]: https://wiki.gnome.org/Apps/Rhythmbox
889 /// [`TrackList interface`]: TrackListInterface
890 #[$attr]
891 #[doc(alias = "org.mpris.MediaPlayer2.TrackList")]
892 pub trait $track_list_iface_ident: $player_iface_ident {
893 /// Gets all the metadata available for a set of tracks.
894 ///
895 /// # Parameters
896 ///
897 /// * `track_ids` - The list of track ids for which metadata is requested.
898 ///
899 /// # Returns
900 ///
901 /// * `metadata` - Metadata of the set of tracks given as input.
902 ///
903 /// Each set of metadata must have a [`mpris:trackid`] entry at the very
904 /// least, which contains a string that uniquely identifies this track
905 /// within the scope of the tracklist.
906 ///
907 /// [`mpris:trackid`]: Metadata::set_trackid
908 #[doc(alias = "GetTracksMetadata")]
909 async fn get_tracks_metadata(
910 &self,
911 track_ids: Vec<TrackId>,
912 ) -> fdo::Result<Vec<Metadata>>;
913
914 /// Adds a URI in the tracklist.
915 ///
916 /// # Parameters
917 ///
918 /// * `uri` - The uri of the item to add. Its uri scheme should be an
919 /// element of the [`SupportedUriSchemes`] property and the mime-type
920 /// should match one of the elements of the [`SupportedMimeTypes`]
921 /// property.
922 /// * `after_track` - The identifier of the track after which the new item
923 /// should be inserted. The path
924 /// [`/org/mpris/MediaPlayer2/TrackList/NoTrack`] indicates that the track
925 /// should be inserted at the start of the track list.
926 /// * `set_as_current` - Whether the newly inserted track should be
927 /// considered as the current track. Setting this to **true** has the same
928 /// effect as calling [`GoTo`] afterwards.
929 ///
930 /// If the [`CanEditTracks`] property is **false**, this has no effect.
931 ///
932 /// **Note:** Clients should not assume that the track has been added at the
933 /// time when this method returns. They should wait for a [`TrackAdded`] (or
934 /// [`TrackListReplaced`]) signal.
935 ///
936 /// [`SupportedUriSchemes`]: RootInterface::supported_uri_schemes
937 /// [`SupportedMimeTypes`]: RootInterface::supported_mime_types
938 /// [`/org/mpris/MediaPlayer2/TrackList/NoTrack`]: TrackId::NO_TRACK
939 /// [`GoTo`]: Self::go_to
940 /// [`CanEditTracks`]: Self::can_edit_tracks
941 /// [`TrackAdded`]: TrackListSignal::TrackAdded
942 /// [`TrackListReplaced`]: TrackListSignal::TrackListReplaced
943 #[doc(alias = "AddTrack")]
944 async fn add_track(
945 &self,
946 uri: Uri,
947 after_track: TrackId,
948 set_as_current: bool,
949 ) -> fdo::Result<()>;
950
951 /// Removes an item from the tracklist.
952 ///
953 /// # Parameters
954 ///
955 /// * `track_id` - Identifier of the track to be removed.
956 /// [`/org/mpris/MediaPlayer2/TrackList/NoTrack`] is *not* a valid value
957 /// for this argument.
958 ///
959 /// If the track is not part of this tracklist, this has no effect.
960 ///
961 /// If the [`CanEditTracks`] property is **false**, this has no effect.
962 ///
963 /// **Note:** Clients should not assume that the track has been removed at
964 /// the time when this method returns. They should wait for a
965 /// [`TrackRemoved`] (or [`TrackListReplaced`]) signal.
966 ///
967 /// [`/org/mpris/MediaPlayer2/TrackList/NoTrack`]: TrackId::NO_TRACK
968 /// [`CanEditTracks`]: Self::can_edit_tracks
969 /// [`TrackRemoved`]: TrackListSignal::TrackRemoved
970 /// [`TrackListReplaced`]: TrackListSignal::TrackListReplaced
971 #[doc(alias = "RemoveTrack")]
972 async fn remove_track(&self, track_id: TrackId) -> fdo::Result<()>;
973
974 /// Skip to the specified TrackId.
975 ///
976 /// # Parameters
977 ///
978 /// * `track_id` - Identifier of the track to skip to.
979 /// [`/org/mpris/MediaPlayer2/TrackList/NoTrack`] is *not* a valid value
980 /// for this argument.
981 ///
982 /// If the track is not part of this tracklist, this has no effect.
983 ///
984 /// If this object is not `/org/mpris/MediaPlayer2`, the current tracklist's
985 /// tracks should be replaced with the contents of this tracklist, and the
986 /// [`TrackListReplaced`] signal should be fired from
987 /// `/org/mpris/MediaPlayer2`.
988 ///
989 /// [`TrackListReplaced`]: TrackListSignal::TrackListReplaced
990 #[doc(alias = "GoTo")]
991 async fn go_to(&self, track_id: TrackId) -> fdo::Result<()>;
992
993 /// An array which contains the identifier of each track in the tracklist,
994 /// in order.
995 ///
996 /// When this property changes, the
997 /// `org.freedesktop.DBus.Properties.PropertiesChanged` signal via
998 /// [`track_list_properties_changed`] must be emitted *without* the new
999 /// value.
1000 ///
1001 /// The `org.freedesktop.DBus.Properties.PropertiesChanged` signal is
1002 /// emitted every time this property changes, but the signal message
1003 /// does not contain the new value. Client implementations should rather
1004 /// rely on the [`TrackAdded`], [`TrackRemoved`] and
1005 /// [`TrackListReplaced`] signals to keep their representation of the
1006 /// tracklist up to date.
1007 ///
1008 /// [`track_list_properties_changed`]: Server::track_list_properties_changed
1009 /// [`TrackAdded`]: TrackListSignal::TrackAdded
1010 /// [`TrackRemoved`]: TrackListSignal::TrackRemoved
1011 /// [`TrackListReplaced`]: TrackListSignal::TrackListReplaced
1012 #[doc(alias = "Tracks")]
1013 async fn tracks(&self) -> fdo::Result<Vec<TrackId>>;
1014
1015 /// Whether tracks can be added to and removed from the tracklist.
1016 ///
1017 /// When this property changes, the
1018 /// `org.freedesktop.DBus.Properties.PropertiesChanged` signal via
1019 /// [`track_list_properties_changed`] must be emitted with the new value.
1020 ///
1021 /// If **false**, calling [`AddTrack`] or [`RemoveTrack`] will have no
1022 /// effect, and may raise a [`NotSupported`] error.
1023 ///
1024 /// [`track_list_properties_changed`]: Server::track_list_properties_changed
1025 /// [`AddTrack`]: Self::add_track
1026 /// [`RemoveTrack`]: Self::remove_track
1027 /// [`NotSupported`]: fdo::Error::NotSupported
1028 #[doc(alias = "CanEditTracks")]
1029 async fn can_edit_tracks(&self) -> fdo::Result<bool>;
1030 }
1031
1032 #[doc = $extra_playlists_docs]
1033 #[doc = ""]
1034 /// Used to implement [org.mpris.MediaPlayer2.Playlists] interface, which
1035 /// provides access to the media player's playlists.
1036 ///
1037 /// Since D-Bus does not provide an easy way to check for what interfaces are
1038 /// exported on an object, clients should attempt to get one of the properties
1039 /// on this interface to see if it is implemented.
1040 ///
1041 /// [org.mpris.MediaPlayer2.Playlists]: https://specifications.freedesktop.org/mpris-spec/latest/Playlists_Interface.html
1042 #[$attr]
1043 #[doc(alias = "org.mpris.MediaPlayer2.Playlists")]
1044 pub trait $playlists_iface_ident: $player_iface_ident {
1045 /// Starts playing the given playlist.
1046 ///
1047 /// # Parameters
1048 ///
1049 /// * `playlist_id` - The id of the playlist to activate.
1050 ///
1051 /// Note that this must be implemented. If the media player does not allow
1052 /// clients to change the playlist, it should not implement this interface
1053 /// at all.
1054 ///
1055 /// It is up to the media player whether this completely replaces the
1056 /// current tracklist, or whether it is merely inserted into the tracklist
1057 /// and the first track starts. For example, if the media player is
1058 /// operating in a "jukebox" mode, it may just append the playlist to the
1059 /// list of upcoming tracks, and skip to the first track in the playlist.
1060 #[doc(alias = "ActivatePlaylist")]
1061 async fn activate_playlist(&self, playlist_id: PlaylistId) -> fdo::Result<()>;
1062
1063 /// Gets a set of playlists.
1064 ///
1065 /// # Parameters
1066 ///
1067 /// * `index` - The index of the first playlist to be fetched (according to
1068 /// the ordering).
1069 /// * `max_count` - The maximum number of playlists to fetch.
1070 /// * `order` - The ordering that should be used.
1071 /// * `reverse_order` - Whether the order should be reversed.
1072 ///
1073 /// # Returns
1074 ///
1075 /// * `playlists` - A list of (at most `max_count`) playlists.
1076 #[doc(alias = "GetPlaylists")]
1077 async fn get_playlists(
1078 &self,
1079 index: u32,
1080 max_count: u32,
1081 order: PlaylistOrdering,
1082 reverse_order: bool,
1083 ) -> fdo::Result<Vec<Playlist>>;
1084
1085 /// The number of playlists available.
1086 ///
1087 /// When this property changes, the
1088 /// `org.freedesktop.DBus.Properties.PropertiesChanged` signal via
1089 /// [`playlists_properties_changed`] must be emitted with the new value.
1090 ///
1091 /// [`playlists_properties_changed`]: Server::playlists_properties_changed
1092 #[doc(alias = "PlaylistCount")]
1093 async fn playlist_count(&self) -> fdo::Result<u32>;
1094
1095 /// The available orderings. At least one must be offered.
1096 ///
1097 /// When this property changes, the
1098 /// `org.freedesktop.DBus.Properties.PropertiesChanged` signal via
1099 /// [`playlists_properties_changed`] must be emitted with the new value.
1100 ///
1101 /// <details><summary>Rationale</summary>
1102 ///
1103 /// Media players may not have access to all the data required for some
1104 /// orderings. For example, creation times are not available on UNIX
1105 /// filesystems (don't let the ctime fool you!). On the other hand, clients
1106 /// should have some way to get the "most recent" playlists.
1107 ///
1108 /// </details>
1109 ///
1110 /// [`playlists_properties_changed`]: Server::playlists_properties_changed
1111 #[doc(alias = "Orderings")]
1112 async fn orderings(&self) -> fdo::Result<Vec<PlaylistOrdering>>;
1113
1114 /// The currently-active playlist.
1115 ///
1116 /// When this property changes, the
1117 /// `org.freedesktop.DBus.Properties.PropertiesChanged` signal via
1118 /// [`playlists_properties_changed`] must be emitted with the new value.
1119 ///
1120 /// If there is no currently-active playlist, this should return [`None`].
1121 ///
1122 /// Note that this may not have a value even after [`ActivatePlaylist`] is
1123 /// called with a valid playlist id as [`ActivatePlaylist`] implementations
1124 /// have the option of simply inserting the contents of the playlist
1125 /// into the current tracklist.
1126 ///
1127 /// [`playlists_properties_changed`]: Server::playlists_properties_changed
1128 /// [`ActivatePlaylist`]: Self::activate_playlist
1129 #[doc(alias = "ActivePlaylist")]
1130 async fn active_playlist(&self) -> fdo::Result<Option<Playlist>>;
1131 }
1132 };
1133}
1134
1135define_iface!(
1136 #[trait_variant::make(Send + Sync)],
1137 RootInterface extra_docs "",
1138 PlayerInterface extra_docs "",
1139 TrackListInterface extra_docs "",
1140 PlaylistsInterface extra_docs ""
1141);
1142
1143define_iface!(
1144 #[allow(async_fn_in_trait)],
1145 LocalRootInterface extra_docs "Local version of [`RootInterface`] to be used with [`LocalServer`].",
1146 LocalPlayerInterface extra_docs "Local version of [`PlayerInterface`] to be used with [`LocalServer`].",
1147 LocalTrackListInterface extra_docs "Local version of [`TrackListInterface`] to be used with [`LocalServer`].",
1148 LocalPlaylistsInterface extra_docs "Local version of [`PlaylistsInterface`] to be used with [`LocalServer`]."
1149);
1150
1151/// A playback rate.
1152///
1153/// This is a multiplier, so a value of 0.5 indicates that playback
1154/// is happening at half speed, while 1.5 means that 1.5 seconds of
1155/// "track time" is consumed every second.
1156#[doc(alias = "Playback_Rate")]
1157pub type PlaybackRate = f64;
1158
1159/// Audio volume level.
1160///
1161/// * 0.0 means mute.
1162/// * 1.0 is a sensible maximum volume level (ex: 0dB).
1163///
1164/// Note that the volume may be higher than 1.0, although generally
1165/// clients should not attempt to set it above 1.0.
1166pub type Volume = f64;
1167
1168/// Unique playlist identifier.
1169///
1170/// <details><summary>Rationale</summary>
1171///
1172/// Multiple playlists may have the same name.
1173///
1174/// This is a D-Bus object id as that is the definitive way to have unique
1175/// identifiers on D-Bus. It also allows for future optional expansions to
1176/// the specification where tracks are exported to D-Bus with an interface
1177/// similar to `org.gnome.UPnP.MediaItem2`.
1178///
1179/// </details>
1180#[doc(alias = "Playlist_Id")]
1181pub type PlaylistId = OwnedObjectPath;
1182
1183/// A unique resource identifier.
1184///
1185/// URIs should be sent as (UTF-8) strings. Local files should use the
1186/// "file://" schema.
1187pub type Uri = String;
1188
1189#[cfg(test)]
1190mod tests {
1191 use static_assertions::assert_trait_sub_all;
1192
1193 use super::*;
1194
1195 assert_trait_sub_all!(RootInterface: Send, Sync);
1196 assert_trait_sub_all!(PlayerInterface: Send, Sync);
1197 assert_trait_sub_all!(TrackListInterface: Send, Sync);
1198 assert_trait_sub_all!(PlaylistsInterface: Send, Sync);
1199
1200 assert_trait_sub_all!(PlayerInterface: RootInterface);
1201 assert_trait_sub_all!(TrackListInterface: PlayerInterface, RootInterface);
1202 assert_trait_sub_all!(PlaylistsInterface: PlayerInterface, RootInterface);
1203
1204 assert_trait_sub_all!(LocalPlayerInterface: LocalRootInterface);
1205 assert_trait_sub_all!(LocalTrackListInterface: LocalPlayerInterface, LocalRootInterface);
1206 assert_trait_sub_all!(LocalPlaylistsInterface: LocalPlayerInterface, LocalRootInterface);
1207}