diff --git a/src/mpris.vala b/src/mpris.vala index e99e2ad..d68cd2d 100644 --- a/src/mpris.vala +++ b/src/mpris.vala @@ -21,6 +21,11 @@ class Mpris : Object { public string[] supported_uri_schemes { owned get { return {}; } } public string[] supported_mime_types { owned get { return {}; } } + internal Mpris (Ui.Window window) { + this.on_raise.connect (() => window.present ()); + this.on_quit.connect (() => window.close ()); + } + ~Mpris () { debug ("destroying mpris"); } @@ -50,22 +55,183 @@ class MprisPlayer : Object { public signal void seeked (int64 position); public string playback_status { owned get; internal set; default = "Stopped"; } - public string loop_status { owned get; set; default = "None"; } - public double rate { get; set; default = 1.0; } - public bool shuffle { get; set; default = false; } - public HashTable metadata_map { owned get; default = new HashTable(null, null); } + public string loop_status { owned get; /*set;*/ default = "None"; } + public double rate { get; /*set*/ default = 1.0; } + public bool shuffle { get; /*set*/ default = false; } + public HashTable metadata { owned get; private set; default = new HashTable (null, null); } public double volume { get; set; default = 1.0; } [CCode (notify = false)] public int64 position { get; default = 0; } public double minimum_rate { get { return 1.0; } } public double maximum_rate { get { return 1.0; } } - public bool can_go_next { get; default = false; } - public bool can_go_previous { get; default = false; } - public bool can_play { get; default = false; } - public bool can_pause { get; default = false; } - public bool can_seek { get; default = false; } + public bool can_go_next { get; private set; default = false; } + public bool can_go_previous { get; private set; default = false; } + public bool can_play { get; private set; default = false; } + public bool can_pause { get; private set; default = false; } + public bool can_seek { get; private set; default = false; } [CCode (notify = false)] - public bool can_control { get { return false; } } + public bool can_control { get { return true; } } + + internal MprisPlayer (DBusConnection conn, Playbin playbin) { + playbin.bind_property ( + "state", + this, + "playback_status", + BindingFlags.DEFAULT, + (binding, from, ref to) => { + switch (from.get_enum ()) { + case PlaybinState.STOPPED: + to.set_string ("Stopped"); + this.can_go_next = false; + this.can_go_previous = false; + this.can_play = false; + this.can_pause = false; + this.can_seek = false; + return true; + case PlaybinState.PAUSED: + to.set_string ("Paused"); + this.can_go_next = true; + this.can_go_previous = true; + this.can_play = true; + this.can_pause = true; + this.can_seek = true; + return true; + case PlaybinState.PLAYING: + to.set_string ("Playing"); + this.can_go_next = true; + this.can_go_previous = true; + this.can_play = true; + this.can_pause = true; + this.can_seek = true; + return true; + } + return false; + }); + + playbin.bind_property ( + "volume", + this, + "volume", + BindingFlags.BIDIRECTIONAL, + (binding, from, ref to) => { + to.set_double (from.get_int () / 100.0); + return true; + }, + (binding, from, ref to) => { + to.set_int ((int) (from.get_double () * 100.0)); + return true; + }); + + playbin.new_track.connect ((playbin) => { + Subsonic.Song song = (Subsonic.Song) playbin.play_queue.get_item (playbin.play_queue_position); + + var metadata = new HashTable (null, null); + metadata["mpris:trackid"] = new ObjectPath (@"/eu/callcc/audrey/track/$(song.id)"); + metadata["mpris:length"] = (int64) song.duration * 1000000; + // TODO: metadata["mpris:artUrl"] = + metadata["xesam:album"] = song.album; + metadata["xesam:artist"] = new string[] {song.artist}; + if (song.genre != null) metadata["xesam:genre"] = song.genre; + metadata["xesam:title"] = song.title; + metadata["xesam:trackNumber"] = song.track; + metadata["xesam:useCount"] = song.play_count; + metadata["xesam:userRating"] = song.starred != null ? 1.0 : 0.0; + + this.metadata = metadata; + }); + + this.on_next.connect (() => playbin.go_to_next_track ()); + this.on_previous.connect (() => playbin.go_to_prev_track ()); + this.on_play.connect (() => playbin.play ()); + this.on_pause.connect (() => playbin.pause ()); + this.on_play_pause.connect (() => { + if (playbin.state == PlaybinState.PAUSED) playbin.play (); + else if (playbin.state == PlaybinState.PLAYING) playbin.pause (); + }); + + this.notify.connect ((p) => { + var builder = new VariantBuilder (VariantType.ARRAY); + var invalid_builder = new VariantBuilder (new VariantType ("as")); + + string dbus_name; + Variant dbus_value; + + switch (p.name) { + case "playback-status": + dbus_name = "PlaybackStatus"; + dbus_value = this.playback_status; + break; + case "loop-status": + dbus_name = "LoopStatus"; + dbus_value = this.loop_status; + break; + case "rate": + dbus_name = "Rate"; + dbus_value = this.rate; + break; + case "shuffle": + dbus_name = "Shuffle"; + dbus_value = this.shuffle; + break; + case "metadata": + dbus_name = "Metadata"; + dbus_value = this.metadata; + break; + case "volume": + dbus_name = "Volume"; + dbus_value = this.volume; + break; + case "minimum-rate": + dbus_name = "MinimumRate"; + dbus_value = this.minimum_rate; + break; + case "maximum-rate": + dbus_name = "MaximumRate"; + dbus_value = this.maximum_rate; + break; + case "can-go-next": + dbus_name = "CanGoNext"; + dbus_value = this.can_go_next; + break; + case "can-go-previous": + dbus_name = "CanGoPrevious"; + dbus_value = this.can_go_previous; + break; + case "can-play": + dbus_name = "CanPlay"; + dbus_value = this.can_play; + break; + case "can-pause": + dbus_name = "CanPause"; + dbus_value = this.can_pause; + break; + case "can-seek": + dbus_name = "CanSeek"; + dbus_value = this.can_seek; + break; + default: + warning (@"unknown mpris player property $(p.name)"); + return; + } + + builder.add ("{sv}", dbus_name, dbus_value); + + try { + conn.emit_signal ( + null, + "/org/mpris/MediaPlayer2", + "org.freedesktop.DBus.Properties", + "PropertiesChanged", + new Variant ( + "(sa{sv}as)", + "org.mpris.MediaPlayer2.Player", + builder, + invalid_builder)); + } catch (Error e) { + error (@"could not notify of mpris property changes: $(e.message)"); + } + }); + } ~MprisPlayer () { debug ("destroying mpris player"); diff --git a/src/subsonic.vala b/src/subsonic.vala index 427b4c9..7935068 100644 --- a/src/subsonic.vala +++ b/src/subsonic.vala @@ -60,8 +60,10 @@ public class Subsonic.Song : Object { public string artist { get; private set; } public int64 track { get; private set; } public int64 year { get; private set; } - public DateTime? starred { get; private set; } + public DateTime? starred { get; private set; } // TODO public int64 duration { get; private set; } + public int64 play_count { get; private set; } + public string? genre { get; private set; } public Song (Json.Reader reader) { reader.read_member ("id"); @@ -91,6 +93,14 @@ public class Subsonic.Song : Object { reader.read_member ("duration"); this.duration = reader.get_int_value (); reader.end_member (); + + reader.read_member ("playCount"); + this.play_count = reader.get_int_value (); + reader.end_member (); + + reader.read_member ("genre"); + this.genre = reader.get_string_value (); + reader.end_member (); } } diff --git a/src/ui/window.vala b/src/ui/window.vala index cd0dd09..934b9ae 100644 --- a/src/ui/window.vala +++ b/src/ui/window.vala @@ -56,19 +56,20 @@ class Ui.Window : Adw.ApplicationWindow { } construct { - // TODO: mpris - // Bus.own_name ( - // BusType.SESSION, - // "org.mpris.MediaPlayer2.audrey", - // BusNameOwnerFlags.NONE, - // (conn) => { - // try { - // } catch (IOError e) { - // error ("could not register dbus service: %s", e.message); - // } - // }, - // () => {}, - // () => { error ("could not acquire dbus name"); }); + Bus.own_name ( + BusType.SESSION, + "org.mpris.MediaPlayer2.audrey", + BusNameOwnerFlags.NONE, + (conn) => { + try { + conn.register_object ("/org/mpris/MediaPlayer2", new Mpris (this)); + conn.register_object ("/org/mpris/MediaPlayer2", new MprisPlayer (conn, this.playbin)); + } catch (IOError e) { + error ("could not register dbus service: %s", e.message); + } + }, + () => {}, + () => { error ("could not acquire dbus name"); }); this.setup = new Setup ();