diff --git a/data/music.gschema.xml b/data/music.gschema.xml index e4ffeab87..bbb08d850 100644 --- a/data/music.gschema.xml +++ b/data/music.gschema.xml @@ -22,6 +22,11 @@ The last song played by Music A string representing the uri of the last played music file + + 0 + Playback progression of the last song played by Music + Number of seconds elapsed since the beginning of the last track that was played + 475 diff --git a/src/PlaybackManager.vala b/src/PlaybackManager.vala index c759b0807..96b028df4 100644 --- a/src/PlaybackManager.vala +++ b/src/PlaybackManager.vala @@ -45,7 +45,6 @@ public class Music.PlaybackManager : Object { private PlaybackManager () {} construct { - settings = new Settings ("io.elementary.music"); queue_liststore = new ListStore (typeof (AudioObject)); playbin = Gst.ElementFactory.make ("playbin", "playbin"); @@ -68,7 +67,8 @@ public class Music.PlaybackManager : Object { next_action = new SimpleAction (Application.ACTION_NEXT, null); next_action.activate.connect (() => next ()); - play_pause_action = new SimpleAction.stateful (Application.ACTION_PLAY_PAUSE, null, new Variant.boolean (false)); + play_pause_action = + new SimpleAction.stateful (Application.ACTION_PLAY_PAUSE, null, new Variant.boolean (false)); play_pause_action.change_state.connect (play_pause); previous_action = new SimpleAction (Application.ACTION_PREVIOUS, null); @@ -98,8 +98,12 @@ public class Music.PlaybackManager : Object { bind_property ("has-items", save_playlist_action, "enabled", SYNC_CREATE); } + public void seek_to_progress_nano_seconds (int64 progress) { + playbin.seek_simple (Gst.Format.TIME, Gst.SeekFlags.FLUSH, progress); + } + public void seek_to_progress (double percent) { - playbin.seek_simple (Gst.Format.TIME, Gst.SeekFlags.FLUSH, (int64)(percent * current_audio.duration)); + seek_to_progress_nano_seconds ((int64)(percent * current_audio.duration)); } // Files[] must not contain any null entries @@ -214,6 +218,13 @@ public class Music.PlaybackManager : Object { playbin.query_position (Gst.Format.TIME, out position); playback_position = position.clamp (0, current_audio.duration); + int playback_position_seconds = (int)(playback_position / 1000000000); + // Save progress every other second, to reduce disk writes + if (playback_position_seconds % 2 == 0) { + debug ("setting progress: " + (playback_position_seconds).to_string () + "s"); + settings.set_int ("progression-last-played", playback_position_seconds); + } + return Source.CONTINUE; }); } else { @@ -409,6 +420,27 @@ public class Music.PlaybackManager : Object { } current_audio = (AudioObject) queue_liststore.get_item (position); + + // Read then restore saved progress + var progression_last_played_seconds = settings.get_int ("progression-last-played"); + if (progression_last_played_seconds > 0) { + int64 progression_last_played = (int64)progression_last_played_seconds * 1000000000; + + // Wait for current_audio.duration to be set by the object responsible for setting it + GLib.Timeout.add (50, () => { + if (current_audio.duration != 0) { + // Do not seek if duration is inferior to seeking position + if (current_audio.duration > progression_last_played) { + info ("saved progress found and valid, restoring it: %i", progression_last_played_seconds); + seek_to_progress_nano_seconds (progression_last_played); + } + return Source.REMOVE; + } + + // Track metadata are not set yet, continue to wait before restoring playback position + return Source.CONTINUE; + }); + } } } diff --git a/src/Views/NowPlayingView.vala b/src/Views/NowPlayingView.vala index ed8e43b6d..512d865b8 100644 --- a/src/Views/NowPlayingView.vala +++ b/src/Views/NowPlayingView.vala @@ -108,10 +108,14 @@ public class Music.NowPlayingView : Gtk.Box { playback_manager.notify["current-audio"].connect (() => { if (playback_manager.current_audio != null) { - playback_manager.current_audio.bind_property ("artist", artist_label, "label", BindingFlags.SYNC_CREATE); - playback_manager.current_audio.bind_property ("title", title_label, "label", BindingFlags.SYNC_CREATE); - playback_manager.current_audio.bind_property ("duration", seekbar, "playback-duration", BindingFlags.SYNC_CREATE); - playback_manager.current_audio.bind_property ("texture", album_image.image, "paintable", BindingFlags.SYNC_CREATE); + playback_manager.current_audio.bind_property ( + "artist", artist_label, "label", BindingFlags.SYNC_CREATE); + playback_manager.current_audio.bind_property ( + "title", title_label, "label", BindingFlags.SYNC_CREATE); + playback_manager.current_audio.bind_property ( + "duration", seekbar, "playback-duration", BindingFlags.SYNC_CREATE); + playback_manager.current_audio.bind_property ( + "texture", album_image.image, "paintable", BindingFlags.SYNC_CREATE); } else { album_image.image.clear (); artist_label.label = _("Not playing"); diff --git a/src/Widgets/TrackRow.vala b/src/Widgets/TrackRow.vala index 41b4dd465..76e80065e 100644 --- a/src/Widgets/TrackRow.vala +++ b/src/Widgets/TrackRow.vala @@ -88,7 +88,8 @@ public class Music.TrackRow : Granite.Bin { play_icon.spinning = playback_manager.current_audio == audio_object; }); - var play_pause_action = (SimpleAction) GLib.Application.get_default ().lookup_action (Application.ACTION_PLAY_PAUSE); + var play_pause_action = + (SimpleAction) GLib.Application.get_default ().lookup_action (Application.ACTION_PLAY_PAUSE); update_playing (play_pause_action.get_state ().get_boolean ()); GLib.Application.get_default ().action_state_changed.connect ((name, new_state) => {