summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAylur <[email protected]>2024-10-27 20:35:37 +0000
committerAylur <[email protected]>2024-10-27 20:35:45 +0000
commit197bffbf05d62aeefceb58011b0f031cd8836ca6 (patch)
tree84beb8f89b71dff0f04a71b3af4f12de9e28cbb1
parent17987c6fac21bb9f30c66cae93cd38bb4e8d2dd0 (diff)
docs(mpris): doc comments
l---------lib/mpris/gir.py1
-rw-r--r--lib/mpris/ifaces.vala40
-rw-r--r--lib/mpris/meson.build46
-rw-r--r--lib/mpris/mpris.vala45
-rw-r--r--lib/mpris/player.vala317
5 files changed, 357 insertions, 92 deletions
diff --git a/lib/mpris/gir.py b/lib/mpris/gir.py
new file mode 120000
index 0000000..b5b4f1d
--- /dev/null
+++ b/lib/mpris/gir.py
@@ -0,0 +1 @@
+../gir.py \ No newline at end of file
diff --git a/lib/mpris/ifaces.vala b/lib/mpris/ifaces.vala
index 4a9d715..298a288 100644
--- a/lib/mpris/ifaces.vala
+++ b/lib/mpris/ifaces.vala
@@ -1,19 +1,18 @@
-namespace AstalMpris {
[DBus (name="org.freedesktop.DBus")]
-internal interface DBusImpl : DBusProxy {
- public abstract string[] list_names () throws GLib.Error;
- public signal void name_owner_changed (string name, string old_owner, string new_owner);
+private interface AstalMpris.DBusImpl : DBusProxy {
+ public abstract string[] list_names() throws GLib.Error;
+ public signal void name_owner_changed(string name, string old_owner, string new_owner);
}
[DBus (name="org.freedesktop.DBus.Properties")]
-internal interface PropsIface : DBusProxy {
- public abstract HashTable<string, Variant> get_all (string iface);
+private interface AstalMpris.PropsIface : DBusProxy {
+ public abstract HashTable<string, Variant> get_all(string iface);
}
[DBus (name="org.mpris.MediaPlayer2")]
-internal interface IMpris : PropsIface {
- public abstract void raise () throws GLib.Error;
- public abstract void quit () throws GLib.Error;
+private interface AstalMpris.IMpris : PropsIface {
+ public abstract void raise() throws GLib.Error;
+ public abstract void quit() throws GLib.Error;
public abstract bool can_quit { get; }
public abstract bool fullscreen { get; set; }
@@ -27,18 +26,18 @@ internal interface IMpris : PropsIface {
}
[DBus (name="org.mpris.MediaPlayer2.Player")]
-internal interface IPlayer : IMpris {
- public abstract void next () throws GLib.Error;
- public abstract void previous () throws GLib.Error;
- public abstract void pause () throws GLib.Error;
- public abstract void play_pause () throws GLib.Error;
- public abstract void stop () throws GLib.Error;
- public abstract void play () throws GLib.Error;
- public abstract void seek (int64 offset) throws GLib.Error;
- public abstract void set_position (ObjectPath track_id, int64 position) throws GLib.Error;
- public abstract void open_uri (string uri) throws GLib.Error;
+private interface AstalMpris.IPlayer : IMpris {
+ public abstract void next() throws GLib.Error;
+ public abstract void previous() throws GLib.Error;
+ public abstract void pause() throws GLib.Error;
+ public abstract void play_pause() throws GLib.Error;
+ public abstract void stop() throws GLib.Error;
+ public abstract void play() throws GLib.Error;
+ public abstract void seek(int64 offset) throws GLib.Error;
+ public abstract void set_position(ObjectPath track_id, int64 position) throws GLib.Error;
+ public abstract void open_uri(string uri) throws GLib.Error;
- public signal void seeked (int64 position);
+ public signal void seeked(int64 position);
public abstract string playback_status { owned get; }
public abstract string loop_status { owned get; set; }
@@ -57,4 +56,3 @@ internal interface IPlayer : IMpris {
public abstract bool can_seek { get; }
public abstract bool can_control { get; }
}
-}
diff --git a/lib/mpris/meson.build b/lib/mpris/meson.build
index c9a5c53..bf215c9 100644
--- a/lib/mpris/meson.build
+++ b/lib/mpris/meson.build
@@ -38,34 +38,40 @@ deps = [
dependency('json-glib-1.0'),
]
-sources = [
- config,
+sources = [config] + files(
'ifaces.vala',
- 'player.vala',
'mpris.vala',
-]
+ 'player.vala',
+)
if get_option('lib')
lib = library(
meson.project_name(),
sources,
dependencies: deps,
+ vala_args: ['--vapi-comments'],
vala_header: meson.project_name() + '.h',
vala_vapi: meson.project_name() + '-' + api_version + '.vapi',
- vala_gir: gir,
version: meson.project_version(),
install: true,
- install_dir: [true, true, true, true],
+ install_dir: [true, true, true],
)
- import('pkgconfig').generate(
- lib,
- name: meson.project_name(),
- filebase: meson.project_name() + '-' + api_version,
- version: meson.project_version(),
- subdirs: meson.project_name(),
- requires: deps,
- install_dir: get_option('libdir') / 'pkgconfig',
+ pkgs = []
+ foreach dep : deps
+ pkgs += ['--pkg=' + dep.name()]
+ endforeach
+
+ gir_tgt = custom_target(
+ gir,
+ command: [find_program('python3'), files('gir.py'), meson.project_name(), gir]
+ + pkgs
+ + sources,
+ input: sources,
+ depends: lib,
+ output: gir,
+ install: true,
+ install_dir: get_option('datadir') / 'gir-1.0',
)
custom_target(
@@ -78,10 +84,20 @@ if get_option('lib')
],
input: lib,
output: typelib,
- depends: lib,
+ depends: [lib, gir_tgt],
install: true,
install_dir: get_option('libdir') / 'girepository-1.0',
)
+
+ import('pkgconfig').generate(
+ lib,
+ name: meson.project_name(),
+ filebase: meson.project_name() + '-' + api_version,
+ version: meson.project_version(),
+ subdirs: meson.project_name(),
+ requires: deps,
+ install_dir: get_option('libdir') / 'pkgconfig',
+ )
endif
if get_option('cli')
diff --git a/lib/mpris/mpris.vala b/lib/mpris/mpris.vala
index 0e55a2e..8eaffa5 100644
--- a/lib/mpris/mpris.vala
+++ b/lib/mpris/mpris.vala
@@ -1,12 +1,24 @@
namespace AstalMpris {
-public Mpris get_default() {
- return Mpris.get_default();
+ /**
+ * Gets the default singleton Mpris instance.
+ */
+ public Mpris get_default() {
+ return Mpris.get_default();
+ }
}
-public class Mpris : Object {
+/**
+ * Object that monitors dbus for players to appear and disappear.
+ */
+public class AstalMpris.Mpris : Object {
internal static string PREFIX = "org.mpris.MediaPlayer2.";
private static Mpris instance;
+ private DBusImpl proxy;
+
+ /**
+ * Gets the default singleton Mpris instance.
+ */
public static Mpris get_default() {
if (instance == null)
instance = new Mpris();
@@ -14,15 +26,23 @@ public class Mpris : Object {
return instance;
}
- private DBusImpl proxy;
-
private HashTable<string, Player> _players =
new HashTable<string, Player> (str_hash, str_equal);
+ /**
+ * List of currently available players.
+ */
public List<weak Player> players { owned get { return _players.get_values(); } }
- public signal void player_added (Player player);
- public signal void player_closed (Player player);
+ /**
+ * Emitted when a new mpris Player appears.
+ */
+ public signal void player_added(Player player);
+
+ /**
+ * Emitted when a Player disappears.
+ */
+ public signal void player_closed(Player player);
construct {
try {
@@ -53,14 +73,15 @@ public class Mpris : Object {
var p = new Player(busname);
_players.set(busname, p);
- p.closed.connect(() => {
- player_closed(p);
- _players.remove(busname);
- notify_property("players");
+ p.notify["available"].connect(() => {
+ if (!p.available) {
+ player_closed(p);
+ _players.remove(busname);
+ notify_property("players");
+ }
});
player_added(p);
notify_property("players");
}
}
-}
diff --git a/lib/mpris/player.vala b/lib/mpris/player.vala
index 6764d2b..2776047 100644
--- a/lib/mpris/player.vala
+++ b/lib/mpris/player.vala
@@ -1,75 +1,185 @@
-namespace AstalMpris {
-public class Player : Object {
+/**
+ * Object which tracks players through their mpris dbus interface.
+ * The most simple way is to use [[email protected]] which tracks every player,
+ * but [[email protected]] can be constructed for a dedicated players too.
+ */
+public class AstalMpris.Player : Object {
private static string COVER_CACHE = Environment.get_user_cache_dir() + "/astal/mpris";
private IPlayer proxy;
-
- public signal void appeared () { available = true; }
- public signal void closed () { available = false; }
+ private uint pollid; // periodically notify position
// identifiers
- public string bus_name { owned get; construct set; }
- public bool available { get; private set; }
+ public string bus_name { owned get; private set; }
- // periodically notify position
- private uint pollid;
+ /**
+ * Indicates if [[email protected]:bus_name] is available on dbus.
+ */
+ public bool available { get; private set; }
// mpris
+
+ /**
+ * Brings the player's user interface to the front
+ * using any appropriate mechanism available.
+ *
+ * The media player may be unable to control how its user interface is displayed,
+ * or it may not have a graphical user interface at all.
+ * In this case, the [[email protected]:can_raise] is `false` and this method does nothing.
+ */
public void raise() {
- try { proxy.raise(); } catch (Error error) { critical(error.message); }
+ try { proxy.raise(); } catch (Error err) { critical(err.message); }
}
- public void quit() {
- try { proxy.quit(); } catch (Error error) { critical(error.message); }
+ /**
+ * Causes the media player to stop running.
+ *
+ * The media player may refuse to allow clients to shut it down.
+ * In this case, the [[email protected]:can_quit] property is false and this method does nothing.
+ */
+ public void quit() throws Error {
+ try { proxy.quit(); } catch (Error err) { critical(err.message); }
}
+ /**
+ * Indicates if [[email protected]] has any effect.
+ */
public bool can_quit { get; private set; }
+
+ /**
+ * Indicates if the player is occupying the fullscreen. This is typically used for videos.
+ * Use [[email protected]_fullscreen] to toggle fullscreen state.
+ */
public bool fullscreen { get; private set; }
+
+ /**
+ * Indicates if [[email protected]_fullscreen] has any effect.
+ */
public bool can_set_fullscreen { get; private set; }
+
+ /**
+ * Indicates if [[email protected]] has any effect.
+ */
public bool can_raise { get; private set; }
- public bool has_track_list { get; private set; }
+
+ // TODO: Tracklist interface
+ // public bool has_track_list { get; private set; }
+
+ /**
+ * A human friendly name to identify the player.
+ */
public string identity { owned get; private set; }
+
+ /**
+ * The base name of a .desktop file
+ */
public string entry { owned get; private set; }
+
+ /**
+ * The URI schemes supported by the media player.
+ *
+ * This can be viewed as protocols supported by the player in almost all cases.
+ * Almost every media player will include support for the "file" scheme.
+ * Other common schemes are "http" and "rtsp".
+ */
public string[] supported_uri_schemas { owned get; private set; }
+
+ /**
+ * The mime-types supported by the player.
+ */
public string[] supported_mime_types { owned get; private set; }
+ /**
+ * Toggle [[email protected]:fullscreen] state.
+ */
public void toggle_fullscreen() {
if (!can_set_fullscreen)
- critical("can not set fullscreen on " + bus_name);
+ critical(@"can not set fullscreen on $bus_name");
proxy.fullscreen = !fullscreen;
}
- // player
+ /**
+ * Skips to the next track in the tracklist.
+ *
+ * If there is no next track (and endless playback and track repeat are both off), stop playback.
+ * If [[email protected]:can_go_next] is `false` this method has no effect.
+ */
public void next() {
try { proxy.next(); } catch (Error error) { critical(error.message); }
}
+ /**
+ * Skips to the previous track in the tracklist.
+ *
+ * If there is no previous track (and endless playback and track repeat are both off), stop playback.
+ * If [[email protected]:can_go_previous] is `false` this method has no effect.
+ */
public void previous() {
try { proxy.previous(); } catch (Error error) { critical(error.message); }
}
+ /**
+ * Pauses playback.
+ *
+ * If playback is already paused, this has no effect.
+ * If [[email protected]:can_pause] is `false` this method has no effect.
+ */
public void pause() {
try { proxy.pause(); } catch (Error error) { critical(error.message); }
}
+ /**
+ * Pauses playback.
+ *
+ * If playback is already paused, resumes playback.
+ * If playback is stopped, starts playback.
+ */
public void play_pause() {
try { proxy.play_pause(); } catch (Error error) { critical(error.message); }
}
+ /**
+ * Stops playback.
+ *
+ * If playback is already stopped, this has no effect.
+ * If [[email protected]:can_control] is `false` this method has no effect.
+ */
public void stop() {
try { proxy.stop(); } catch (Error error) { critical(error.message); }
}
+ /**
+ * Starts or resumes playback.
+ *
+ * If already playing, this has no effect.
+ * If paused, playback resumes from the current position.
+ * If [[email protected]:can_play] is `false` this method has no effect.
+ */
public void play() {
try { proxy.play(); } catch (Error error) { critical(error.message); }
}
+ /**
+ * uri scheme should be an element of [[email protected]:supported_uri_schemas]
+ * and the mime-type should match one of the elements of [[email protected]:supported_mime_types].
+ *
+ * @param uri Uri of the track to load.
+ */
public void open_uri(string uri) {
try { proxy.open_uri(uri); } catch (Error error) { critical(error.message); }
}
+ /**
+ * Change [[email protected]:loop_status] from none to track,
+ * from track to playlist, from playlist to none.
+ */
public void loop() {
+ if (loop_status == Loop.UNSUPPORTED) {
+ critical(@"loop is unsupported by $bus_name");
+ return;
+ }
+
switch (loop_status) {
case Loop.NONE:
loop_status = Loop.TRACK;
@@ -85,15 +195,21 @@ public class Player : Object {
}
}
+ /**
+ * Toggle [[email protected]:shuffle_status].
+ */
public void shuffle() {
+ if (shuffle_status == Shuffle.UNSUPPORTED) {
+ critical(@"shuffle is unsupported by $bus_name");
+ return;
+ }
+
shuffle_status = shuffle_status == Shuffle.ON
? Shuffle.OFF
: Shuffle.ON;
}
- public signal void seeked (int64 position);
-
- public double _get_position() {
+ private double _get_position() {
try {
var reply = proxy.call_sync(
"org.freedesktop.DBus.Properties.Get",
@@ -130,60 +246,172 @@ public class Player : Object {
private Shuffle _shuffle_status = Shuffle.UNSUPPORTED;
private double _volume = -1;
+ /**
+ * The current loop/repeat status.
+ */
public Loop loop_status {
get { return _loop_status; }
set { proxy.loop_status = value.to_string(); }
}
+ /**
+ * The current playback rate.
+ */
public double rate {
get { return _rate; }
set { proxy.rate = value; }
}
+ /**
+ * The current shuffle status.
+ */
public Shuffle shuffle_status {
get { return _shuffle_status; }
set { proxy.shuffle = value == Shuffle.ON; }
}
+ /**
+ * The current volume level between 0 and 1.
+ */
public double volume {
get { return _volume; }
set { proxy.volume = value; }
}
+ /**
+ * The current position of the track in seconds.
+ * To get a progress percentage simply divide this with [[email protected]:length].
+ */
public double position {
get { return _get_position(); }
set { _set_position(value); }
}
+ /**
+ * The current playback status.
+ */
public PlaybackStatus playback_status { get; private set; }
+
+ /**
+ * The minimum value which the [[email protected]:rate] can take.
+ */
public double minimum_rate { get; private set; }
+
+ /**
+ * The maximum value which the [[email protected]:rate] can take.
+ */
public double maximum_rate { get; private set; }
+
+ /**
+ * Indicates if invoking [[email protected]] has effect.
+ */
public bool can_go_next { get; private set; }
+
+ /**
+ * Indicates if invoking [[email protected]] has effect.
+ */
public bool can_go_previous { get; private set; }
+
+ /**
+ * Indicates if invoking [[email protected]] has effect.
+ */
public bool can_play { get; private set; }
+
+ /**
+ * Indicates if invoking [[email protected]] has effect.
+ */
public bool can_pause { get; private set; }
+
+ /**
+ * Indicates if setting [[email protected]:position] has effect.
+ */
public bool can_seek { get; private set; }
- public bool can_control { get; private set; }
- // metadata
- [CCode (notify = false)]
- public HashTable<string,Variant> metadata { owned get; private set; }
+ /**
+ * Indicates if the player can be controlled with
+ * methods such as [[email protected]_pause].
+ */
+ public bool can_control { get; private set; }
+ /**
+ * Metadata hashtable of this player.
+ * In languages that cannot introspect this
+ * use [[email protected]_meta].
+ */
+ [CCode (notify = false)] // notified manually in sync
+ public HashTable<string, Variant> metadata { owned get; private set; }
+
+ /**
+ * Currently playing track's id.
+ */
public string trackid { owned get; private set; }
+
+ /**
+ * Length of the currently playing track in seconds.
+ */
public double length { get; private set; }
+
+ /**
+ * The location of an image representing the track or album.
+ * You should always prefer to use [[email protected]:cover_art].
+ */
public string art_url { owned get; private set; }
+ /**
+ * Title of the currently playing album.
+ */
public string album { owned get; private set; }
+
+ /**
+ * Artists of the currently playing album.
+ */
public string album_artist { owned get; private set; }
+
+ /**
+ * Artists of the currently playing track.
+ */
public string artist { owned get; private set; }
+
+ /**
+ * Lyrics of the currently playing track.
+ */
public string lyrics { owned get; private set; }
+
+ /**
+ * Title of the currently playing track.
+ */
public string title { owned get; private set; }
+
+ /**
+ * Composers of the currently playing track.
+ */
public string composer { owned get; private set; }
+
+ /**
+ * Comments of the currently playing track.
+ */
public string comments { owned get; private set; }
- // cached cover art
+ /**
+ * Path of the cached [[email protected]:art_url].
+ */
public string cover_art { owned get; private set; }
+ /**
+ * Lookup a key from [[email protected]:metadata].
+ * This method is useful for languages that fail to introspect hashtables.
+ */
+ public Variant? get_meta(string key) {
+ return metadata.lookup(key);
+ }
+
+ /**
+ * Construct a Player that tracks a dbus name. For example "org.mpris.MediaPlayer2.spotify".
+ * The "org.mpris.MediaPlayer2." prefix can be leftout so simply "spotify" would mean the same.
+ * [[email protected]:available] indicates whether the player is actually running or not.
+ *
+ * @param name dbus name of the player.
+ */
public Player(string name) {
Object(bus_name: name.has_prefix("org.mpris.MediaPlayer2.")
? name : "org.mpris.MediaPlayer2." + name);
@@ -195,7 +423,7 @@ public class Player : Object {
fullscreen = proxy.fullscreen;
can_set_fullscreen = proxy.can_set_fullscreen;
can_raise = proxy.can_raise;
- has_track_list = proxy.has_track_list;
+ // has_track_list = proxy.has_track_list;
identity = proxy.identity;
entry = proxy.desktop_entry;
supported_uri_schemas = proxy.supported_uri_schemas;
@@ -310,10 +538,6 @@ public class Player : Object {
}
}
- public Variant? get_meta(string key) {
- return metadata.lookup(key);
- }
-
private string get_str(string key) {
if (metadata.get(key) == null)
return "";
@@ -349,7 +573,7 @@ public class Player : Object {
}
}
- public void try_proxy() throws Error {
+ private void try_proxy() throws Error {
if (proxy != null)
return;
@@ -360,13 +584,14 @@ public class Player : Object {
);
if (proxy.g_name_owner != null)
- appeared();
+ available = false;
proxy.notify["g-name-owner"].connect(() => {
- if (proxy.g_name_owner != null)
- appeared();
- else
- closed();
+ if (proxy.g_name_owner != null) {
+ available = true;
+ } else {
+ available = false;
+ }
});
proxy.g_properties_changed.connect(sync);
@@ -387,12 +612,12 @@ public class Player : Object {
}
}
-public enum PlaybackStatus {
+public enum AstalMpris.PlaybackStatus {
PLAYING,
PAUSED,
STOPPED;
- public static PlaybackStatus from_string(string? str) {
+ internal static PlaybackStatus from_string(string? str) {
switch (str) {
case "Playing":
return PLAYING;
@@ -404,7 +629,7 @@ public enum PlaybackStatus {
}
}
- public string to_string() {
+ internal string to_string() {
switch (this) {
case PLAYING:
return "Playing";
@@ -417,13 +642,16 @@ public enum PlaybackStatus {
}
}
-public enum Loop {
+public enum AstalMpris.Loop {
UNSUPPORTED,
+ /** The playback will stop when there are no more tracks to play. */
NONE,
+ /** The current track will start again from the begining once it has finished playing. */
TRACK,
+ /** The playback loops through a list of tracks. */
PLAYLIST;
- public static Loop from_string(string? str) {
+ internal static Loop from_string(string? str) {
switch (str) {
case "None":
return NONE;
@@ -436,7 +664,7 @@ public enum Loop {
}
}
- public string? to_string() {
+ internal string? to_string() {
switch (this) {
case NONE:
return "None";
@@ -450,16 +678,18 @@ public enum Loop {
}
}
-public enum Shuffle {
+public enum AstalMpris.Shuffle {
UNSUPPORTED,
+ /** Playback is progressing through a playlist in some other order. */
ON,
+ /** Playback is progressing linearly through a playlist. */
OFF;
- public static Shuffle from_bool(bool b) {
+ internal static Shuffle from_bool(bool b) {
return b ? Shuffle.ON : Shuffle.OFF;
}
- public string? to_string() {
+ internal string? to_string() {
switch (this) {
case OFF:
return "Off";
@@ -470,4 +700,3 @@ public enum Shuffle {
}
}
}
-}