-
Notifications
You must be signed in to change notification settings - Fork 27
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Issue: #155 JavaScript API: * MediaPlayer.setTrackPosition(position) - to update current track position * MediaPlayer.setTrack(track) - supports track.length property * New action PlayerAction.SEEK - to seek in a desired position in the track, called with the new position as parameter * New flag MediaPlayer.setCanSeek(state) - to enable remote seeking Nuvola Runtime: * Developer sidebar updated to show track position and length * Test service got progress bar * MPRIS plugin updated IPC API: * /nuvola/mediaplayer/track-position - get track position * /nuvola/mediaplayer/set-track-position - set track position * /nuvola/mediaplayer/track-position-changed - notification Signed-off-by: Jiří Janoušek <[email protected]>
- Loading branch information
1 parent
0deb2a9
commit 347085e
Showing
10 changed files
with
308 additions
and
56 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
/* | ||
* Copyright 2014-2016 Jiří Janoušek <[email protected]> | ||
* Copyright 2014-2017 Jiří Janoušek <[email protected]> | ||
* | ||
* Redistribution and use in source and binary forms, with or without | ||
* modification, are permitted provided that the following conditions are met: | ||
|
@@ -69,6 +69,10 @@ var PlayerAction = { | |
* Show playback notification | ||
*/ | ||
PLAYBACK_NOTIFICATION: "playback-notification", | ||
/** | ||
* Seek to a new position | ||
*/ | ||
SEEK: "seek", | ||
} | ||
|
||
/** | ||
|
@@ -138,6 +142,7 @@ MediaPlayer.$init = function() | |
this._artworkLoop = 0; | ||
this._baseActions = [PlayerAction.TOGGLE_PLAY, PlayerAction.PLAY, PlayerAction.PAUSE, PlayerAction.PREV_SONG, PlayerAction.NEXT_SONG]; | ||
this._notification = null; | ||
this._trackPosition = 0; | ||
Nuvola.core.connect("InitAppRunner", this); | ||
Nuvola.core.connect("InitWebWorker", this); | ||
} | ||
|
@@ -152,9 +157,11 @@ MediaPlayer.$init = function() | |
* @param String|null track.album track album | ||
* @param String|null track.artLocation URL of album/track artwork | ||
* @param double|null track.rating track rating from `0.0` to `1.0`. *This item is ignored prior API 3.1.* | ||
* @param double|null track.length track length as a string (`HH:MM:SS.xxx`, e.g. `1:25.54`) or number of microseconds. *This item is ignored prior API 4.5.* | ||
*/ | ||
MediaPlayer.setTrack = function(track) | ||
{ | ||
track.length = Nuvola.parseTimeUsec(track.length); | ||
var changed = Nuvola.objectDiff(this._track, track); | ||
|
||
if (!changed.length) | ||
|
@@ -180,6 +187,25 @@ MediaPlayer.setTrack = function(track) | |
} | ||
} | ||
|
||
/** | ||
* Set current playback position | ||
* | ||
* If the current position is the same as the previous one, this method does nothing. | ||
* | ||
* @since API 4.5 | ||
* | ||
* @param String|Number position the current track position as a string (`HH:MM:SS.xxx`, e.g. `1:25.54`) or number of microseconds. | ||
*/ | ||
MediaPlayer.setTrackPosition = function (position) | ||
{ | ||
var position = Nuvola.parseTimeUsec(position); | ||
if (this._trackPosition != position) | ||
{ | ||
this._trackPosition = position; | ||
Nuvola._callIpcMethodAsync("/nuvola/mediaplayer/set-track-position", position); | ||
} | ||
} | ||
|
||
/** | ||
* Set current playback state | ||
* | ||
|
@@ -302,6 +328,25 @@ MediaPlayer.setCanRate = function(canRate) | |
} | ||
} | ||
|
||
/** | ||
* Set whether it is possible to seek to a specific position of the track | ||
* | ||
* If the argument is same as in the previous call, this method does nothing. | ||
* | ||
* @since API 4.5 | ||
* | ||
* @param Boolean canSeek true if remote seeking should be allowed | ||
*/ | ||
MediaPlayer.setCanSeek = function(canSeek) | ||
{ | ||
if (this._canSeek !== canSeek) | ||
{ | ||
this._canSeek = canSeek; | ||
Nuvola.actions.setEnabled(PlayerAction.SEEK, !!canSeek); | ||
this._setFlag("can-seek", !!canSeek); | ||
} | ||
} | ||
|
||
/** | ||
* Add actions for media player capabilities | ||
* | ||
|
@@ -341,6 +386,7 @@ MediaPlayer._onInitAppRunner = function(emitter) | |
Nuvola.actions.addAction("playback", "win", PlayerAction.STOP, "Stop", null, "media-playback-stop", null); | ||
Nuvola.actions.addAction("playback", "win", PlayerAction.PREV_SONG, "Previous song", null, "media-skip-backward", null); | ||
Nuvola.actions.addAction("playback", "win", PlayerAction.NEXT_SONG, "Next song", null, "media-skip-forward", null); | ||
Nuvola.actions.addAction("playback", "win", PlayerAction.SEEK, "Seek", null, null, 0); | ||
// FIXME: remove action if notifications compoment is disabled | ||
Nuvola.actions.addAction("playback", "win", PlayerAction.PLAYBACK_NOTIFICATION, "Show playback notification", null, null, null); | ||
|
||
|
@@ -411,6 +457,7 @@ MediaPlayer._sendDevelInfo = function() | |
"artist": this._track.artist || null, | ||
"album": this._track.album || null, | ||
"rating": rating, | ||
"length": this._track.length || 0, | ||
"artworkLocation": this._track.artLocation || null, | ||
"artworkFile": this._artworkFile || null, | ||
"playbackActions": this._baseActions.concat(this._extraActions), | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
/* | ||
* Copyright 2014 Jiří Janoušek <[email protected]> | ||
* Copyright 2014-2017 Jiří Janoušek <[email protected]> | ||
* | ||
* Redistribution and use in source and binary forms, with or without | ||
* modification, are permitted provided that the following conditions are met: | ||
|
@@ -152,3 +152,34 @@ Nuvola.objectDiff = function(object1, object2) | |
|
||
return changes; | ||
} | ||
|
||
/** | ||
* Parse time as number of microseconds | ||
* | ||
* @param String time time expression `HH:MM:SS' | ||
* @return the time in microseconds | ||
*/ | ||
Nuvola.parseTimeUsec = function(time) | ||
{ | ||
if (!time) | ||
return 0; | ||
if (time * 1 === time) | ||
return time; | ||
var parts = time.split(":"); | ||
var seconds = 0; | ||
var item = parts.pop(); | ||
if (item !== undefined) | ||
{ | ||
seconds = 1 * item; | ||
item = parts.pop(); | ||
if (item !== undefined) | ||
{ | ||
seconds += 60 * item; | ||
item = parts.pop(); | ||
if (item !== undefined) | ||
tseconds += 60 * 60 * item; | ||
} | ||
} | ||
return seconds !== NaN ? seconds * 1000 * 1000 : 0; | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
/* | ||
* Copyright 2014-2015 Jiří Janoušek <[email protected]> | ||
* Copyright 2014-2017 Jiří Janoušek <[email protected]> | ||
* | ||
* Redistribution and use in source and binary forms, with or without | ||
* modification, are permitted provided that the following conditions are met: | ||
|
@@ -27,6 +27,7 @@ using Diorite; | |
public class Nuvola.MediaPlayerBinding: ModelBinding<MediaPlayerModel> | ||
{ | ||
private const string TRACK_INFO_CHANGED = "track-info-changed"; | ||
private const string TRACK_POSITION_CHANGED = "track-position-changed"; | ||
|
||
public MediaPlayerBinding(Drt.ApiRouter router, WebWorker web_worker, MediaPlayerModel model) | ||
{ | ||
|
@@ -53,12 +54,21 @@ public class Nuvola.MediaPlayerBinding: ModelBinding<MediaPlayerModel> | |
new Drt.StringParam("artworkLocation", false, true), | ||
new Drt.StringParam("artworkFile", false, true), | ||
new Drt.DoubleParam("rating", false, 0.0), | ||
new Drt.DoubleParam("length", false, 0.0), | ||
new Drt.StringArrayParam("playbackActions", false), | ||
}); | ||
bind("set-track-position", Drt.ApiFlags.PRIVATE|Drt.ApiFlags.WRITABLE, null, handle_set_track_position, { | ||
new Drt.DoubleParam("position", false, 0.0) | ||
|
||
}); | ||
bind("track-info", Drt.ApiFlags.READABLE, "Returns information about currently playing track.", | ||
handle_get_track_info, null); | ||
bind("track-position", Drt.ApiFlags.READABLE, "Returns information about current track position.", | ||
handle_get_track_position, null); | ||
add_notification(TRACK_INFO_CHANGED, Drt.ApiFlags.WRITABLE|Drt.ApiFlags.SUBSCRIBE, | ||
"Sends a notification when track info is changed."); | ||
add_notification(TRACK_POSITION_CHANGED, Drt.ApiFlags.WRITABLE|Drt.ApiFlags.SUBSCRIBE, | ||
"Sends a notification when track position is changed."); | ||
model.set_rating.connect(on_set_rating); | ||
} | ||
|
||
|
@@ -72,7 +82,8 @@ public class Nuvola.MediaPlayerBinding: ModelBinding<MediaPlayerModel> | |
var artwork_location = params.pop_string(); | ||
var artwork_file = params.pop_string(); | ||
var rating = params.pop_double(); | ||
model.set_track_info(title, artist, album, state, artwork_location, artwork_file, rating); | ||
var length = params.pop_double(); | ||
model.set_track_info(title, artist, album, state, artwork_location, artwork_file, rating, (int) length); | ||
|
||
SList<string> playback_actions = null; | ||
var actions = params.pop_strv(); | ||
|
@@ -99,6 +110,21 @@ public class Nuvola.MediaPlayerBinding: ModelBinding<MediaPlayerModel> | |
return builder.end(); | ||
} | ||
|
||
private Variant? handle_set_track_position(GLib.Object source, Drt.ApiParams? params) throws Diorite.MessageError | ||
{ | ||
check_not_empty(); | ||
var position = params.pop_double(); | ||
model.track_position = (int) position; | ||
emit(TRACK_POSITION_CHANGED); | ||
return new Variant.boolean(true); | ||
} | ||
|
||
private Variant? handle_get_track_position(GLib.Object source, Drt.ApiParams? params) throws Diorite.MessageError | ||
{ | ||
check_not_empty(); | ||
return new Variant.double((double) model.track_position); | ||
} | ||
|
||
private Variant? handle_set_flag(GLib.Object source, Drt.ApiParams? params) throws Diorite.MessageError | ||
{ | ||
check_not_empty(); | ||
|
@@ -113,6 +139,7 @@ public class Nuvola.MediaPlayerBinding: ModelBinding<MediaPlayerModel> | |
case "can-pause": | ||
case "can-stop": | ||
case "can-rate": | ||
case "can-seek": | ||
handled = true; | ||
GLib.Value value = GLib.Value(typeof(bool)); | ||
value.set_boolean(state); | ||
|
@@ -137,6 +164,7 @@ public class Nuvola.MediaPlayerBinding: ModelBinding<MediaPlayerModel> | |
case "can-pause": | ||
case "can-stop": | ||
case "can-rate": | ||
case "can-seek": | ||
GLib.Value value = GLib.Value(typeof(bool)); | ||
model.@get_property(name, ref value); | ||
return new Variant.boolean(value.get_boolean()); | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
/* | ||
* Copyright 2014-2015 Jiří Janoušek <[email protected]> | ||
* Copyright 2014-2017 Jiří Janoušek <[email protected]> | ||
* | ||
* Redistribution and use in source and binary forms, with or without | ||
* modification, are permitted provided that the following conditions are met: | ||
|
@@ -42,6 +42,7 @@ public class DeveloperSidebar: Gtk.ScrolledWindow | |
private Diorite.Actions? actions_reg; | ||
private Gtk.Grid grid; | ||
private Gtk.Image? artwork = null; | ||
private Gtk.Label? position = null; | ||
private Gtk.Label? song = null; | ||
private Gtk.Label? artist = null; | ||
private Gtk.Label? album = null; | ||
|
@@ -63,9 +64,14 @@ public class DeveloperSidebar: Gtk.ScrolledWindow | |
artwork = new Gtk.Image(); | ||
clear_artwork(false); | ||
grid.add(artwork); | ||
position = new Gtk.Label( | ||
Utils.format_track_time(player.track_position) + " / " + Utils.format_track_time(player.track_length)); | ||
position.set_line_wrap(true); | ||
position.halign = Gtk.Align.CENTER; | ||
grid.attach_next_to(position, artwork, Gtk.PositionType.BOTTOM, 1, 1); | ||
var label = new HeaderLabel("Song"); | ||
label.halign = Gtk.Align.START; | ||
grid.attach_next_to(label, artwork, Gtk.PositionType.BOTTOM, 1, 1); | ||
grid.attach_next_to(label, position, Gtk.PositionType.BOTTOM, 1, 1); | ||
song = new Gtk.Label(player.title ?? "(null)"); | ||
song.set_line_wrap(true); | ||
song.halign = Gtk.Align.START; | ||
|
@@ -162,6 +168,11 @@ public class DeveloperSidebar: Gtk.ScrolledWindow | |
case "state": | ||
state.label = player.state ?? "(null)"; | ||
break; | ||
case "track-length": | ||
case "track-position": | ||
position.label = Utils.format_track_time(player.track_position) | ||
+ " / " + Utils.format_track_time(player.track_length); | ||
break; | ||
case "rating": | ||
rating.label = player.rating >= 0.0 ? player.rating.to_string() : "(null)"; | ||
break; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
/* | ||
* Copyright 2014-2015 Jiří Janoušek <[email protected]> | ||
* Copyright 2014-2017 Jiří Janoušek <[email protected]> | ||
* | ||
* Redistribution and use in source and binary forms, with or without | ||
* modification, are permitted provided that the following conditions are met: | ||
|
@@ -31,12 +31,15 @@ public class Nuvola.MediaPlayer: GLib.Object, Nuvola.MediaPlayerModel | |
public string? state {get; set; default = null;} | ||
public string? artwork_location {get; set; default = null;} | ||
public string? artwork_file {get; set; default = null;} | ||
public int track_length {get; set; default = 0;} | ||
public int track_position {get; set; default = 0;} | ||
public bool can_go_next {get; set; default = false;} | ||
public bool can_go_previous {get; set; default = false;} | ||
public bool can_play {get; set; default = false;} | ||
public bool can_pause {get; set; default = false;} | ||
public bool can_stop {get; set; default = false;} | ||
public bool can_rate {get; set; default = false;} | ||
public bool can_seek {get; set; default = false;} | ||
public SList<string> playback_actions {get; owned set;} | ||
private Diorite.Actions actions; | ||
|
||
|
@@ -47,7 +50,7 @@ public class Nuvola.MediaPlayer: GLib.Object, Nuvola.MediaPlayerModel | |
|
||
protected void handle_set_track_info( | ||
string? title, string? artist, string? album, string? state, string? artwork_location, string? artwork_file, | ||
double rating) | ||
double rating, int length) | ||
{ | ||
this.title = title; | ||
this.artist = artist; | ||
|
@@ -56,6 +59,7 @@ public class Nuvola.MediaPlayer: GLib.Object, Nuvola.MediaPlayerModel | |
this.state = state; | ||
this.artwork_location = artwork_location; | ||
this.artwork_file = artwork_file; | ||
this.track_length = length; | ||
} | ||
|
||
public void play() | ||
|
@@ -88,9 +92,14 @@ public class Nuvola.MediaPlayer: GLib.Object, Nuvola.MediaPlayerModel | |
activate_action("next-song"); | ||
} | ||
|
||
private void activate_action(string name) | ||
public void seek(int64 position) | ||
{ | ||
if (!actions.activate_action(name)) | ||
activate_action("seek", position); | ||
} | ||
|
||
private void activate_action(string name, Variant? parameter=null) | ||
{ | ||
if (!actions.activate_action(name, parameter)) | ||
critical("Failed to activate action '%s'.", name); | ||
} | ||
} |
Oops, something went wrong.