diff --git a/src/playbin.vala b/src/playbin.vala index 1ace5cf..2d9e777 100644 --- a/src/playbin.vala +++ b/src/playbin.vala @@ -29,10 +29,8 @@ class Playbin : Object { private bool update_position = false; public int64 position { get; private set; } - public int64 duration { get; private set; } public Subsonic api { get; set; } - private ListModel play_queue; public signal void now_playing (uint index, Song song, int64 duration); private uint playing_index; @@ -43,9 +41,58 @@ class Playbin : Object { source.user_agent = "audrey/linux"; } - public Playbin (ListModel play_queue) { - this.play_queue = play_queue; + private void on_play_queue_items_changed (ListModel play_queue, uint position, uint removed, uint added) { + if (this.state == PlaybinState.STOPPED) { + return; + } + if (this.playing_index >= position) { + if (this.playing_index < position+removed) { + // current track was removed, start playing something else + // TODO check if it was actually reordered + + this.begin_playback (position); + } else { + // unaffected + // fix up playing index though + this.playing_index += added; + this.playing_index -= removed; + } + } else if (this.playing_index+1 == position) { + // next track was changed + // try to fix up gapless transition + string? next_uri = this.next_uri.try_pop (); + if (next_uri != null) { + // we're in luck, about-to-finish hasn't been triggered yet + // we can get away with replacing it + if (this.playing_index+1 < play_queue.get_n_items ()) { + Song song = (Song) play_queue.get_item (this.playing_index+1); + this.next_uri.push (this.api.stream_uri (song.id)); + } else { + this.next_uri.push (""); + } + } else { + // about-to-finish already triggered + // we'll need to stop the new track when it starts playing + assert (false); // TODO + } + } + } + + private ListModel _play_queue = null; + private ulong _play_queue_items_changed; + public ListModel play_queue { + get { return _play_queue; } + set { + if (_play_queue != null) { + SignalHandler.disconnect (_play_queue, _play_queue_items_changed); + } + _play_queue = value; + _play_queue_items_changed = value.items_changed.connect (on_play_queue_items_changed); + } + } + + public Playbin () { this.next_uri = new AsyncQueue (); this.next_uri.push (""); @@ -58,50 +105,12 @@ class Playbin : Object { this.playbin.source_setup.connect (this.source_setup); this.playbin.about_to_finish.connect (this.about_to_finish); - play_queue.items_changed.connect ((play_queue, position, removed, added) => { - if (this.state == PlaybinState.STOPPED) { - return; - } - - if (this.playing_index >= position) { - if (this.playing_index < position+removed) { - // current track was removed, start playing something else - // TODO check if it was actually reordered - - this.begin_playback (position); - } else { - // unaffected - // fix up playing index though - this.playing_index += added; - this.playing_index -= removed; - } - } else if (this.playing_index+1 == position) { - // next track was changed - // try to fix up gapless transition - string? next_uri = this.next_uri.try_pop (); - if (next_uri != null) { - // we're in luck, about-to-finish hasn't been triggered yet - // we can get away with replacing it - if (this.playing_index+1 < play_queue.get_n_items ()) { - Song song = (Song) play_queue.get_item (this.playing_index+1); - this.next_uri.push (this.api.stream_uri (song.id)); - } else { - this.next_uri.push (""); - } - } else { - // about-to-finish already triggered - // we'll need to stop the new track when it starts playing - assert (false); // TODO - } - } - }); - - // regularly update position/duration + // regularly update position Timeout.add (500, () => { if (this.update_position) { int64 new_position; if (this.playbin.query_position (Gst.Format.TIME, out new_position)) { - this.position = new_position < this.duration ? new_position : this.duration; + this.position = new_position; } else { this.position = 0; } @@ -129,12 +138,8 @@ class Playbin : Object { }); bus.message["stream-start"].connect ((message) => { - int64 new_duration; - if (this.playbin.query_duration (Gst.Format.TIME, out new_duration)) { - this.duration = new_duration; - } else { - warning ("could not obtain new stream duration"); - } + int64 duration; + assert (this.playbin.query_duration (Gst.Format.TIME, out duration)); this.position = 0; @@ -145,7 +150,13 @@ class Playbin : Object { this.next_gapless = true; } - this.now_playing (this.playing_index, (Song) play_queue.get_item (this.playing_index), this.duration); + var now_playing = (Song) play_queue.get_item (this.playing_index); + if (this.api.stream_uri (now_playing.id) != (string) this.playbin.current_uri) { + // edge case + assert (false); // TODO + } + + this.now_playing (this.playing_index, now_playing, duration); if (this.playing_index+1 < play_queue.get_n_items ()) { Song song = (Song) play_queue.get_item (this.playing_index+1); diff --git a/src/ui/window.vala b/src/ui/window.vala index 54cd178..d76c820 100644 --- a/src/ui/window.vala +++ b/src/ui/window.vala @@ -27,9 +27,7 @@ class Ui.Window : Adw.ApplicationWindow { public bool cover_art_loading { get; set; default = false; } public Gdk.Paintable playing_cover_art { get; set; } - private Gdk.Paintable next_cover_art = null; - - internal Playbin playbin; + internal Playbin playbin = new Playbin (); public PlayQueueSelection play_queue_model { get; private set; default = new PlayQueueSelection (); } public Window (Gtk.Application app) { @@ -39,11 +37,6 @@ class Ui.Window : Adw.ApplicationWindow { provider.load_from_resource("/eu/callcc/audrey/audrey.css"); Gtk.StyleContext.add_provider_for_display (Gdk.Display.get_default (), provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION); - - this.close_request.connect (() => { - app.quit (); - return false; - }); } construct { @@ -61,9 +54,9 @@ class Ui.Window : Adw.ApplicationWindow { () => {}, () => { error ("could not acquire dbus name"); }); - this.setup = new Setup (); + this.playbin.play_queue = this.play_queue_model; - this.playbin = new Playbin (this.play_queue_model); + this.setup = new Setup (); this.setup.connected.connect ((api) => { this.playbin.api = api; @@ -218,7 +211,7 @@ class Ui.Window : Adw.ApplicationWindow { [GtkCallback] private void seek_forward () { // 10 seconds int64 new_position = position + (int64)10 * 1000 * 1000000; - if (new_position > this.playbin.duration) new_position = this.playbin.duration; + if (new_position > this.duration) new_position = this.duration; this.seek_impl (new_position); } }