summaryrefslogtreecommitdiff
path: root/lib/mpris/player.vala
diff options
context:
space:
mode:
Diffstat (limited to 'lib/mpris/player.vala')
-rw-r--r--lib/mpris/player.vala317
1 files changed, 273 insertions, 44 deletions
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 {
}
}
}
-}