Skip to content

Commit

Permalink
Add API for Toggle repeat on/off
Browse files Browse the repository at this point in the history
Issue: #21

Signed-off-by: Jiří Janoušek <[email protected]>
  • Loading branch information
jiri-janousek committed Sep 10, 2018
1 parent 25fb9c6 commit 31ff673
Show file tree
Hide file tree
Showing 4 changed files with 128 additions and 6 deletions.
41 changes: 38 additions & 3 deletions src/mainjs/mediaplayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,29 @@ var PlayerAction = {
/**
* Change volume
*/
CHANGE_VOLUME: 'change-volume'
CHANGE_VOLUME: 'change-volume',
/**
* Repeat status
*/
REPEAT: 'repeat'
}

/**
* @enum Media player repeat status
*/
var PlayerRepeat = {
/**
* The playback will stop when there are no more tracks to play.
*/
NONE: 0,
/**
* The current track will start again from the beginning once it has finished playing.
*/
TRACK: 1,
/**
* The playback loops through a list of tracks.
*/
PLAYLIST: 2
}

/**
Expand All @@ -97,6 +119,13 @@ var PlaybackState = {
PLAYING: 2
}

var repeatOptions = [
// stateId, label, mnemo_label, icon, keybinding
[PlayerRepeat.NONE, _('No repeat'), null, null, null, null],
[PlayerRepeat.TRACK, _('Repeat track'), null, null, null, null],
[PlayerRepeat.PLAYLIST, _('Repeat playlist'), null, null, null, null]
]

// New key
var RUN_IN_BACKGROUND = 'player.run_in_background'
// Deprecated key - for backward compatibility
Expand Down Expand Up @@ -146,6 +175,10 @@ MediaPlayer.$init = function () {
this._extraActions = []
this._artworkLoop = 0
this._baseActions = [PlayerAction.TOGGLE_PLAY, PlayerAction.PLAY, PlayerAction.PAUSE, PlayerAction.PREV_SONG, PlayerAction.NEXT_SONG]
this._repeatActions = []
for (var i = 0; i <= PlayerRepeat.PLAYLIST; i++) {
this._repeatActions.push(PlayerAction.REPEAT + '::' + i)
}
this._notification = null
this._trackPosition = 0
this._volume = 1.0
Expand Down Expand Up @@ -401,6 +434,7 @@ MediaPlayer._onInitAppRunner = function (emitter) {
Nuvola.actions.addAction('playback', 'win', PlayerAction.CHANGE_VOLUME, 'Change volume', null, null, null, -1.0)
// FIXME: remove action if notifications compoment is disabled
Nuvola.actions.addAction('playback', 'win', PlayerAction.PLAYBACK_NOTIFICATION, 'Show playback notification', null, null, null)
Nuvola.actions.addRadioAction('playback', 'win', PlayerAction.REPEAT, 0, repeatOptions)

Nuvola.core.connect('ComponentLoaded', this)
Nuvola.core.connect('ComponentUnloaded', this)
Expand Down Expand Up @@ -454,7 +488,7 @@ MediaPlayer._onActionActivated = function (emitter, name, param) {

MediaPlayer._setActions = function () {
var actions = [this._state === PlaybackState.PLAYING ? PlayerAction.PAUSE : PlayerAction.PLAY, PlayerAction.PREV_SONG, PlayerAction.NEXT_SONG]
actions = actions.concat(this._extraActions)
actions = actions.concat(this._repeatActions).concat(this._extraActions)
actions.push('quit')
Nuvola.launcher.setActions(actions)
}
Expand All @@ -471,7 +505,7 @@ MediaPlayer._sendDevelInfo = function () {
'length': this._track.length || 0,
'artworkLocation': this._track.artLocation || null,
'artworkFile': this._artworkFile || null,
'playbackActions': this._baseActions.concat(this._extraActions),
'playbackActions': this._baseActions.concat(this._repeatActions).concat(this._extraActions),
'state': ['unknown', 'paused', 'playing'][this._state]
}
Nuvola._callIpcMethodVoid('/nuvola/mediaplayer/set-track-info', info)
Expand Down Expand Up @@ -600,4 +634,5 @@ MediaPlayer._onConfigChanged = function (emitter, key) {
// export public items
Nuvola.PlayerAction = PlayerAction
Nuvola.PlaybackState = PlaybackState
Nuvola.PlayerRepeat = PlayerRepeat
Nuvola.MediaPlayer = MediaPlayer
37 changes: 37 additions & 0 deletions src/nuvolakit-runner/components/mediaplayer/MediaPlayer.vala
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public class Nuvola.MediaPlayer: GLib.Object, Nuvola.MediaPlayerModel {
public string? album {get; set; default = null;}
public double rating {get; set; default = 0.0;}
public string? state {get; set; default = null;}
public PlaybackRepeat repeat {get; set; default = PlaybackRepeat.NONE;}
public string? artwork_location {get; set; default = null;}
public string? artwork_file {get; set; default = null;}
public int64 track_length {get; set; default = 0;}
Expand All @@ -46,6 +47,33 @@ public class Nuvola.MediaPlayer: GLib.Object, Nuvola.MediaPlayerModel {

public MediaPlayer(Drtgtk.Actions actions) {
this.actions = actions;
if (!bind_repeat_action()) {
actions.action_added.connect(on_action_added);
}
}

~MediaPlayer() {
Drtgtk.Action? repeat_action = actions.get_action("repeat");
if (repeat_action != null) {
repeat_action.notify["state"].disconnect(on_repeat_action_changed);
}
}

private void on_action_added(Drtgtk.Action action) {
if (action.name == "repeat") {
bind_repeat_action();
actions.action_added.disconnect(on_action_added);
}
}

private bool bind_repeat_action() {
Drtgtk.Action? repeat_action = actions.get_action("repeat");
if (repeat_action == null) {
return false;
}
repeat_action.notify["state"].connect_after(on_repeat_action_changed);
repeat = (PlaybackRepeat) (double) repeat_action.state;
return true;
}

protected void handle_set_track_info(
Expand Down Expand Up @@ -93,9 +121,18 @@ public class Nuvola.MediaPlayer: GLib.Object, Nuvola.MediaPlayerModel {
activate_action("change-volume", volume);
}

public void change_repeat(PlaybackRepeat repeat) {
activate_action("repeat", new Variant.double((double) repeat));
}

private void activate_action(string name, Variant? parameter=null) {
if (!actions.activate_action(name, parameter)) {
critical("Failed to activate action '%s'.", name);
}
}

private void on_repeat_action_changed(GLib.Object emitter, ParamSpec parameter) {
var action = (Drtgtk.Action) emitter;
repeat = (PlaybackRepeat) (double) action.state;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,19 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

public enum PlaybackRepeat {
NONE,
TRACK,
PLAYLIST;
}

public interface Nuvola.MediaPlayerModel: GLib.Object {
public abstract string? title {get; set; default = null;}
public abstract string? artist {get; set; default = null;}
public abstract string? album {get; set; default = null;}
public abstract double rating {get; set; default = 0.0;}
public abstract string? state {get; set; default = null;}
public abstract PlaybackRepeat repeat {get; set; default = PlaybackRepeat.NONE;}
public abstract string? artwork_location {get; set; default = null;}
public abstract string? artwork_file {get; set; default = null;}
public abstract int64 track_length {get; set; default = 0;}
Expand Down Expand Up @@ -68,5 +75,7 @@ public interface Nuvola.MediaPlayerModel: GLib.Object {

public abstract void change_volume(double volume);

public abstract void change_repeat(PlaybackRepeat repeat);

public signal void set_rating(double rating);
}
47 changes: 44 additions & 3 deletions src/nuvolakit-runner/components/mpris/MPRISPlayer.vala
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,25 @@ public class MPRISPlayer : GLib.Object {
position = player.track_position;
_volume = player.volume;
playback_status = map_playback_state();
loop_status = map_repeat_state_to_string();
pending_update = new HashTable<string, Variant>(str_hash, str_equal);
can_go_next = player.can_go_next;
can_go_previous = player.can_go_previous;
can_seek = player.can_seek;
}

public string playback_status {get; private set;}
private string _loop_status = "None";
public string loop_status {
get { return _loop_status;}
set {
_loop_status = value;
if (_loop_status != map_repeat_state_to_string()) {
player.change_repeat(map_repeat_state_to_enum());
}
}
}

public string playback_status {get; private set; default = "None";}
/* If the media player has no ability to play at speeds other than the normal playback rate,
* this must still be implemented, and must return 1.0. The MinimumRate and MaximumRate properties
* must also be set to 1.0. A value of 0.0 set by the client should act as though Pause was called. */
Expand Down Expand Up @@ -157,8 +169,15 @@ public class MPRISPlayer : GLib.Object {
pending_update["Volume"] = _volume = player.volume;
}
break;
case "repeat":
unowned string repeat = map_repeat_state_to_string();
if (_loop_status == repeat) {
return;
}
pending_update["LoopStatus"] = _loop_status = repeat;
break;
case "state":
string status = map_playback_state();
unowned string status = map_playback_state();
if (playback_status == status) {
return;
}
Expand Down Expand Up @@ -252,7 +271,7 @@ public class MPRISPlayer : GLib.Object {
return metadata;
}

private string map_playback_state() {
private unowned string map_playback_state() {
switch (player.state) {
case "paused":
return "Paused";
Expand All @@ -262,6 +281,28 @@ public class MPRISPlayer : GLib.Object {
return "Stopped";
}
}

private unowned string map_repeat_state_to_string() {
switch (player.repeat) {
case PlaybackRepeat.TRACK:
return "Track";
case PlaybackRepeat.PLAYLIST:
return "Playlist";
default:
return "None";
}
}

private PlaybackRepeat map_repeat_state_to_enum() {
switch (_loop_status) {
case "Track":
return PlaybackRepeat.TRACK;
case "Playlist":
return PlaybackRepeat.PLAYLIST;
default:
return PlaybackRepeat.NONE;
}
}
}

} // namespace Nuvola

0 comments on commit 31ff673

Please sign in to comment.