diff options
Diffstat (limited to 'src/media-player-mpris.vala')
-rw-r--r-- | src/media-player-mpris.vala | 590 |
1 files changed, 295 insertions, 295 deletions
diff --git a/src/media-player-mpris.vala b/src/media-player-mpris.vala index 1b9dba5..061532b 100644 --- a/src/media-player-mpris.vala +++ b/src/media-player-mpris.vala @@ -22,300 +22,300 @@ */ public class MediaPlayerMpris: MediaPlayer { - public MediaPlayerMpris (DesktopAppInfo appinfo) { - this.appinfo = appinfo; - } - - /** Desktop id of the player */ - public override string id { - get { - return this.appinfo.get_id (); - } - } - - /** Display name of the player */ - public override string name { - get { - return this.appinfo.get_name (); - } - } - - /** Application icon of the player */ - public override Icon? icon { - get { - return this.appinfo.get_icon (); - } - } - - /** - * True if an instance of the player is currently running. - * - * See also: attach(), detach() - */ - public override bool is_running { - get { - return this.proxy != null; - } - } - - /** Name of the player on the bus, if an instance is currently running */ - public override string dbus_name { - get { - return this._dbus_name; - } - } - - public override string state { - get; set; default = "Paused"; - } - - public override MediaPlayer.Track? current_track { - get; set; - } - - public override bool can_raise { - get { - return this.root != null ? this.root.CanRaise : true; - } - } - - public override bool can_do_play { - get { - return this.proxy.CanPlay; - } - } - - public override bool can_do_prev { - get { - return this.proxy.CanGoPrevious; - } - } - - public override bool can_do_next { - get { - return this.proxy.CanGoNext; - } - } - - /** - * Attach this object to a process of the associated media player. The player must own @dbus_name and - * implement the org.mpris.MediaPlayer2.Player interface. - * - * Only one player can be attached at any given time. Use detach() to detach a player. - * - * This method does not block. If it is successful, "is-running" will be set to %TRUE. - */ - public void attach (MprisRoot root, string dbus_name) { - return_if_fail (this._dbus_name == null && this.proxy == null); - - this.root = root; - this.notify_property ("can-raise"); - - this._dbus_name = dbus_name; - Bus.get_proxy.begin<MprisPlayer> (BusType.SESSION, dbus_name, "/org/mpris/MediaPlayer2", - DBusProxyFlags.GET_INVALIDATED_PROPERTIES, null, got_proxy); - Bus.get_proxy.begin<MprisPlaylists> (BusType.SESSION, dbus_name, "/org/mpris/MediaPlayer2", - DBusProxyFlags.GET_INVALIDATED_PROPERTIES, null, got_playlists_proxy); - } - - /** - * Detach this object from a process running the associated media player. - * - * See also: attach() - */ - public void detach () { - this.root = null; - this.proxy = null; - this._dbus_name = null; - this.notify_property ("is-running"); - this.notify_property ("can-raise"); - this.state = "Paused"; - this.current_track = null; - } - - /** - * Activate the associated media player. - * - * Note: this will _not_ call attach(), because it doesn't know on which dbus-name the player will appear. - * Use attach() to attach this object to a running instance of the player. - */ - public override void activate () { - try { - if (this.proxy == null) { - this.appinfo.launch (null, null); - this.state = "Launching"; - } - else if (this.root != null && this.root.CanRaise) { - this.root.Raise (); - } - } - catch (Error e) { - warning ("unable to activate %s: %s", appinfo.get_name (), e.message); - } - } - - /** - * Toggles playing status. - */ - public override void play_pause () { - if (this.proxy != null) { - this.proxy.PlayPause.begin (); - } - else if (this.state != "Launching") { - this.play_when_attached = true; - this.activate (); - } - } - - /** - * Skips to the next track. - */ - public override void next () { - if (this.proxy != null) - this.proxy.Next.begin (); - } - - /** - * Skips to the previous track. - */ - public override void previous () { - if (this.proxy != null) - this.proxy.Previous.begin (); - } - - public override uint get_n_playlists () { - return this.playlists != null ? this.playlists.length : 0; - } - - public override string get_playlist_id (int index) { - return_val_if_fail (index < this.playlists.length, ""); - return this.playlists[index].path; - } - - public override string get_playlist_name (int index) { - return_val_if_fail (index < this.playlists.length, ""); - return this.playlists[index].name; - } - - public override void activate_playlist_by_name (string name) { - if (this.playlists_proxy != null) - this.playlists_proxy.ActivatePlaylist.begin (new ObjectPath (name)); - } - - DesktopAppInfo appinfo; - MprisPlayer? proxy; - MprisPlaylists ?playlists_proxy; - string _dbus_name; - bool play_when_attached = false; - MprisRoot root; - PlaylistDetails[] playlists = null; - - void got_proxy (Object? obj, AsyncResult res) { - try { - this.proxy = Bus.get_proxy.end (res); - - /* Connecting to GDBusProxy's "g-properties-changed" signal here, because vala's dbus objects don't - * emit notify signals */ - var gproxy = this.proxy as DBusProxy; - gproxy.g_properties_changed.connect (this.proxy_properties_changed); - - this.notify_property ("is-running"); - this.state = this.proxy.PlaybackStatus != null ? this.proxy.PlaybackStatus : "Unknown"; - this.update_current_track (gproxy.get_cached_property ("Metadata")); - - if (this.play_when_attached) { - /* wait a little before calling PlayPause, some players need some time to - set themselves up */ - Timeout.add (1000, () => { proxy.PlayPause.begin (); return Source.REMOVE; } ); - this.play_when_attached = false; - } - } - catch (Error e) { - this._dbus_name = null; - warning ("unable to attach to media player: %s", e.message); - } - } - - void fetch_playlists () { - /* The proxy is created even when the interface is not supported. GDBusProxy will - return 0 for the PlaylistCount property in that case. */ - if (this.playlists_proxy != null && this.playlists_proxy.PlaylistCount > 0) { - this.playlists_proxy.GetPlaylists.begin (0, 100, "Alphabetical", false, (obj, res) => { - try { - this.playlists = playlists_proxy.GetPlaylists.end (res); - this.playlists_changed (); - } - catch (Error e) { - warning ("could not fetch playlists: %s", e.message); - this.playlists = null; - } - }); - } - else { - this.playlists = null; - this.playlists_changed (); - } - } - - void got_playlists_proxy (Object? obj, AsyncResult res) { - try { - this.playlists_proxy = Bus.get_proxy.end (res); - - var gproxy = this.proxy as DBusProxy; - gproxy.g_properties_changed.connect (this.playlists_proxy_properties_changed); - } - catch (Error e) { - warning ("unable to create mpris plalists proxy: %s", e.message); - return; - } - - Timeout.add (500, () => { this.fetch_playlists (); return Source.REMOVE; } ); - } - - /* some players (e.g. Spotify) don't follow the spec closely and pass single strings in metadata fields - * where an array of string is expected */ - static string sanitize_metadata_value (Variant? v) { - if (v == null) - return ""; - else if (v.is_of_type (VariantType.STRING)) - return v.get_string (); - else if (v.is_of_type (VariantType.STRING_ARRAY)) - return string.joinv (",", v.get_strv ()); - - warn_if_reached (); - return ""; - } - - void proxy_properties_changed (DBusProxy proxy, Variant changed_properties, string[] invalidated_properties) { - if (changed_properties.lookup ("PlaybackStatus", "s", null)) { - this.state = this.proxy.PlaybackStatus != null ? this.proxy.PlaybackStatus : "Unknown"; - } - if (changed_properties.lookup ("CanGoNext", "b", null) || changed_properties.lookup ("CanGoPrevious", "b", null) || + public MediaPlayerMpris (DesktopAppInfo appinfo) { + this.appinfo = appinfo; + } + + /** Desktop id of the player */ + public override string id { + get { + return this.appinfo.get_id (); + } + } + + /** Display name of the player */ + public override string name { + get { + return this.appinfo.get_name (); + } + } + + /** Application icon of the player */ + public override Icon? icon { + get { + return this.appinfo.get_icon (); + } + } + + /** + * True if an instance of the player is currently running. + * + * See also: attach(), detach() + */ + public override bool is_running { + get { + return this.proxy != null; + } + } + + /** Name of the player on the bus, if an instance is currently running */ + public override string dbus_name { + get { + return this._dbus_name; + } + } + + public override string state { + get; set; default = "Paused"; + } + + public override MediaPlayer.Track? current_track { + get; set; + } + + public override bool can_raise { + get { + return this.root != null ? this.root.CanRaise : true; + } + } + + public override bool can_do_play { + get { + return this.proxy.CanPlay; + } + } + + public override bool can_do_prev { + get { + return this.proxy.CanGoPrevious; + } + } + + public override bool can_do_next { + get { + return this.proxy.CanGoNext; + } + } + + /** + * Attach this object to a process of the associated media player. The player must own @dbus_name and + * implement the org.mpris.MediaPlayer2.Player interface. + * + * Only one player can be attached at any given time. Use detach() to detach a player. + * + * This method does not block. If it is successful, "is-running" will be set to %TRUE. + */ + public void attach (MprisRoot root, string dbus_name) { + return_if_fail (this._dbus_name == null && this.proxy == null); + + this.root = root; + this.notify_property ("can-raise"); + + this._dbus_name = dbus_name; + Bus.get_proxy.begin<MprisPlayer> (BusType.SESSION, dbus_name, "/org/mpris/MediaPlayer2", + DBusProxyFlags.GET_INVALIDATED_PROPERTIES, null, got_proxy); + Bus.get_proxy.begin<MprisPlaylists> (BusType.SESSION, dbus_name, "/org/mpris/MediaPlayer2", + DBusProxyFlags.GET_INVALIDATED_PROPERTIES, null, got_playlists_proxy); + } + + /** + * Detach this object from a process running the associated media player. + * + * See also: attach() + */ + public void detach () { + this.root = null; + this.proxy = null; + this._dbus_name = null; + this.notify_property ("is-running"); + this.notify_property ("can-raise"); + this.state = "Paused"; + this.current_track = null; + } + + /** + * Activate the associated media player. + * + * Note: this will _not_ call attach(), because it doesn't know on which dbus-name the player will appear. + * Use attach() to attach this object to a running instance of the player. + */ + public override void activate () { + try { + if (this.proxy == null) { + this.appinfo.launch (null, null); + this.state = "Launching"; + } + else if (this.root != null && this.root.CanRaise) { + this.root.Raise (); + } + } + catch (Error e) { + warning ("unable to activate %s: %s", appinfo.get_name (), e.message); + } + } + + /** + * Toggles playing status. + */ + public override void play_pause () { + if (this.proxy != null) { + this.proxy.PlayPause.begin (); + } + else if (this.state != "Launching") { + this.play_when_attached = true; + this.activate (); + } + } + + /** + * Skips to the next track. + */ + public override void next () { + if (this.proxy != null) + this.proxy.Next.begin (); + } + + /** + * Skips to the previous track. + */ + public override void previous () { + if (this.proxy != null) + this.proxy.Previous.begin (); + } + + public override uint get_n_playlists () { + return this.playlists != null ? this.playlists.length : 0; + } + + public override string get_playlist_id (int index) { + return_val_if_fail (index < this.playlists.length, ""); + return this.playlists[index].path; + } + + public override string get_playlist_name (int index) { + return_val_if_fail (index < this.playlists.length, ""); + return this.playlists[index].name; + } + + public override void activate_playlist_by_name (string name) { + if (this.playlists_proxy != null) + this.playlists_proxy.ActivatePlaylist.begin (new ObjectPath (name)); + } + + DesktopAppInfo appinfo; + MprisPlayer? proxy; + MprisPlaylists ?playlists_proxy; + string _dbus_name; + bool play_when_attached = false; + MprisRoot root; + PlaylistDetails[] playlists = null; + + void got_proxy (Object? obj, AsyncResult res) { + try { + this.proxy = Bus.get_proxy.end (res); + + /* Connecting to GDBusProxy's "g-properties-changed" signal here, because vala's dbus objects don't + * emit notify signals */ + var gproxy = this.proxy as DBusProxy; + gproxy.g_properties_changed.connect (this.proxy_properties_changed); + + this.notify_property ("is-running"); + this.state = this.proxy.PlaybackStatus != null ? this.proxy.PlaybackStatus : "Unknown"; + this.update_current_track (gproxy.get_cached_property ("Metadata")); + + if (this.play_when_attached) { + /* wait a little before calling PlayPause, some players need some time to + set themselves up */ + Timeout.add (1000, () => { proxy.PlayPause.begin (); return Source.REMOVE; } ); + this.play_when_attached = false; + } + } + catch (Error e) { + this._dbus_name = null; + warning ("unable to attach to media player: %s", e.message); + } + } + + void fetch_playlists () { + /* The proxy is created even when the interface is not supported. GDBusProxy will + return 0 for the PlaylistCount property in that case. */ + if (this.playlists_proxy != null && this.playlists_proxy.PlaylistCount > 0) { + this.playlists_proxy.GetPlaylists.begin (0, 100, "Alphabetical", false, (obj, res) => { + try { + this.playlists = playlists_proxy.GetPlaylists.end (res); + this.playlists_changed (); + } + catch (Error e) { + warning ("could not fetch playlists: %s", e.message); + this.playlists = null; + } + }); + } + else { + this.playlists = null; + this.playlists_changed (); + } + } + + void got_playlists_proxy (Object? obj, AsyncResult res) { + try { + this.playlists_proxy = Bus.get_proxy.end (res); + + var gproxy = this.proxy as DBusProxy; + gproxy.g_properties_changed.connect (this.playlists_proxy_properties_changed); + } + catch (Error e) { + warning ("unable to create mpris plalists proxy: %s", e.message); + return; + } + + Timeout.add (500, () => { this.fetch_playlists (); return Source.REMOVE; } ); + } + + /* some players (e.g. Spotify) don't follow the spec closely and pass single strings in metadata fields + * where an array of string is expected */ + static string sanitize_metadata_value (Variant? v) { + if (v == null) + return ""; + else if (v.is_of_type (VariantType.STRING)) + return v.get_string (); + else if (v.is_of_type (VariantType.STRING_ARRAY)) + return string.joinv (",", v.get_strv ()); + + warn_if_reached (); + return ""; + } + + void proxy_properties_changed (DBusProxy proxy, Variant changed_properties, string[] invalidated_properties) { + if (changed_properties.lookup ("PlaybackStatus", "s", null)) { + this.state = this.proxy.PlaybackStatus != null ? this.proxy.PlaybackStatus : "Unknown"; + } + if (changed_properties.lookup ("CanGoNext", "b", null) || changed_properties.lookup ("CanGoPrevious", "b", null) || changed_properties.lookup ("CanPlay", "b", null) || changed_properties.lookup ("CanPause", "b", null)) { - this.playbackstatus_changed (); - } - - var metadata = changed_properties.lookup_value ("Metadata", VariantType.VARDICT); - if (metadata != null) - this.update_current_track (metadata); - } - - void playlists_proxy_properties_changed (DBusProxy proxy, Variant changed_properties, string[] invalidated_properties) { - if (changed_properties.lookup ("PlaylistCount", "u", null)) - this.fetch_playlists (); - } - - void update_current_track (Variant? metadata) { - if (metadata != null) { - this.current_track = new Track ( - sanitize_metadata_value (metadata.lookup_value ("xesam:artist", null)), - sanitize_metadata_value (metadata.lookup_value ("xesam:title", null)), - sanitize_metadata_value (metadata.lookup_value ("xesam:album", null)), - sanitize_metadata_value (metadata.lookup_value ("mpris:artUrl", null)) - ); - } - else { - this.current_track = null; - } - } + this.playbackstatus_changed (); + } + + var metadata = changed_properties.lookup_value ("Metadata", VariantType.VARDICT); + if (metadata != null) + this.update_current_track (metadata); + } + + void playlists_proxy_properties_changed (DBusProxy proxy, Variant changed_properties, string[] invalidated_properties) { + if (changed_properties.lookup ("PlaylistCount", "u", null)) + this.fetch_playlists (); + } + + void update_current_track (Variant? metadata) { + if (metadata != null) { + this.current_track = new Track ( + sanitize_metadata_value (metadata.lookup_value ("xesam:artist", null)), + sanitize_metadata_value (metadata.lookup_value ("xesam:title", null)), + sanitize_metadata_value (metadata.lookup_value ("xesam:album", null)), + sanitize_metadata_value (metadata.lookup_value ("mpris:artUrl", null)) + ); + } + else { + this.current_track = null; + } + } } |