move a bunch of logic into playbin
also yeah yeah fixme those buttons are gone for now
This commit is contained in:
parent
f7afcbca01
commit
72e0745d52
5 changed files with 193 additions and 188 deletions
143
src/playbin.vala
143
src/playbin.vala
|
@ -31,19 +31,24 @@ class Playbin : Object {
|
||||||
public int64 position { get; private set; }
|
public int64 position { get; private set; }
|
||||||
public int64 duration { get; private set; }
|
public int64 duration { get; private set; }
|
||||||
|
|
||||||
private bool notify_next_transition;
|
public Subsonic api { get; set; }
|
||||||
|
private ListModel play_queue;
|
||||||
|
|
||||||
// public void begin_playback (string uri);
|
public signal void now_playing (uint index, Song song, int64 duration);
|
||||||
// public void prepare_next (string? next_uri);
|
private uint playing_index;
|
||||||
public signal void song_transition (string uri);
|
|
||||||
public signal void playback_finished ();
|
private bool next_gapless;
|
||||||
// public void stop_playback ();
|
|
||||||
|
|
||||||
private void source_setup (Gst.Element playbin, dynamic Gst.Element source) {
|
private void source_setup (Gst.Element playbin, dynamic Gst.Element source) {
|
||||||
source.user_agent = "audrey/linux";
|
source.user_agent = "audrey/linux";
|
||||||
}
|
}
|
||||||
|
|
||||||
construct {
|
public Playbin (ListModel play_queue) {
|
||||||
|
this.play_queue = play_queue;
|
||||||
|
|
||||||
|
this.next_uri = new AsyncQueue<string?> ();
|
||||||
|
this.next_uri.push ("");
|
||||||
|
|
||||||
// gstreamer docs: GNOME-based applications, for example, will usually
|
// gstreamer docs: GNOME-based applications, for example, will usually
|
||||||
// want to create gconfaudiosink and gconfvideosink elements and make
|
// want to create gconfaudiosink and gconfvideosink elements and make
|
||||||
// playbin3 use those, so that output happens to whatever the user has
|
// playbin3 use those, so that output happens to whatever the user has
|
||||||
|
@ -53,6 +58,53 @@ class Playbin : Object {
|
||||||
this.playbin.source_setup.connect (this.source_setup);
|
this.playbin.source_setup.connect (this.source_setup);
|
||||||
this.playbin.about_to_finish.connect (this.about_to_finish);
|
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
|
||||||
|
this.next_uri.lock ();
|
||||||
|
switch (this.next_uri.length_unlocked ()) {
|
||||||
|
case 1:
|
||||||
|
// we're in luck, about-to-finish hasn't been triggered yet
|
||||||
|
// we can get away with replacing it
|
||||||
|
this.next_uri.try_pop_unlocked ();
|
||||||
|
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_unlocked (this.api.stream_uri (song.id));
|
||||||
|
} else {
|
||||||
|
this.next_uri.push_unlocked ("");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0:
|
||||||
|
// about-to-finish already triggered
|
||||||
|
// we'll need to stop the new track when it starts playing
|
||||||
|
assert (false); // TODO
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
error ("invalid next_uri queue length %u\n", this.next_uri.length_unlocked ());
|
||||||
|
}
|
||||||
|
this.next_uri.unlock ();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// regularly update position/duration
|
// regularly update position/duration
|
||||||
Timeout.add (500, () => {
|
Timeout.add (500, () => {
|
||||||
if (this.update_position) {
|
if (this.update_position) {
|
||||||
|
@ -95,10 +147,20 @@ class Playbin : Object {
|
||||||
|
|
||||||
this.position = 0;
|
this.position = 0;
|
||||||
|
|
||||||
if (notify_next_transition) {
|
if (this.next_gapless) {
|
||||||
this.song_transition ((string) this.playbin.current_uri);
|
// advance position in play queue
|
||||||
|
this.playing_index += 1;
|
||||||
} else {
|
} else {
|
||||||
notify_next_transition = true;
|
this.next_gapless = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.now_playing (this.playing_index, (Song) play_queue.get_item (this.playing_index), this.duration);
|
||||||
|
|
||||||
|
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 ("");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -112,8 +174,7 @@ class Playbin : Object {
|
||||||
});
|
});
|
||||||
|
|
||||||
bus.message["eos"].connect ((message) => {
|
bus.message["eos"].connect ((message) => {
|
||||||
assert (notify_next_transition);
|
assert (false); // TODO
|
||||||
this.playback_finished ();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,32 +184,54 @@ class Playbin : Object {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void begin_playback (string uri) {
|
public void begin_playback (uint position) {
|
||||||
this.playbin.set_state (Gst.State.READY);
|
|
||||||
this.playbin.uri = uri;
|
|
||||||
this.playbin.set_state (Gst.State.PLAYING);
|
|
||||||
|
|
||||||
this.state = PlaybinState.PLAYING;
|
this.state = PlaybinState.PLAYING;
|
||||||
this.notify_next_transition = false;
|
|
||||||
|
this.playing_index = position;
|
||||||
|
this.playbin.set_state (Gst.State.READY);
|
||||||
|
this.playbin.uri = this.api.stream_uri (((Song) this.play_queue.get_item (position)).id);
|
||||||
|
this.playbin.set_state (Gst.State.PLAYING);
|
||||||
|
this.next_gapless = false;
|
||||||
|
|
||||||
|
this.next_uri.lock ();
|
||||||
|
switch (this.next_uri.length_unlocked ()) {
|
||||||
|
case 1:
|
||||||
|
// we're in luck, about-to-finish hasn't been triggered yet
|
||||||
|
this.next_uri.try_pop_unlocked ();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0:
|
||||||
|
// about-to-finish already triggered
|
||||||
|
// we'll need to stop the new track when it starts playing
|
||||||
|
assert (false); // TODO
|
||||||
|
break;
|
||||||
|
|
||||||
|
case -1:
|
||||||
|
// about-to-finish is blocked
|
||||||
|
// extremely stupid edge case
|
||||||
|
assert (false); // TODO
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
error ("invalid next_uri queue length %u\n", this.next_uri.length_unlocked ());
|
||||||
|
}
|
||||||
|
this.next_uri.unlock ();
|
||||||
}
|
}
|
||||||
|
|
||||||
Mutex next_uri_lock;
|
// ASSUMPTION: about-to-finish will be signalled exactly once per track
|
||||||
string? next_uri;
|
// even if seeking backwards after
|
||||||
|
AsyncQueue<string?> next_uri;
|
||||||
public void prepare_next (string? next_uri) {
|
|
||||||
this.next_uri_lock.lock ();
|
|
||||||
this.next_uri = next_uri;
|
|
||||||
this.next_uri_lock.unlock ();
|
|
||||||
}
|
|
||||||
|
|
||||||
// called when uri can be switched for gapless playback
|
// called when uri can be switched for gapless playback
|
||||||
// need async queue because this might be called from a gstreamer thread
|
// need async queue because this might be called from a gstreamer thread
|
||||||
private void about_to_finish (dynamic Gst.Element playbin) {
|
private void about_to_finish (dynamic Gst.Element playbin) {
|
||||||
this.next_uri_lock.lock ();
|
print ("about to finish\n");
|
||||||
string? next_uri = this.next_uri;
|
|
||||||
this.next_uri_lock.unlock ();
|
|
||||||
|
|
||||||
if (next_uri != null) {
|
// will block if the next uri isn't ready yet
|
||||||
|
// leaves the queue empty as per the ASSUMPTION above
|
||||||
|
string? next_uri = this.next_uri.pop ();
|
||||||
|
|
||||||
|
if (next_uri != "") {
|
||||||
playbin.uri = next_uri;
|
playbin.uri = next_uri;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ template $UiPlayQueue: Adw.NavigationPage {
|
||||||
Adw.HeaderBar {
|
Adw.HeaderBar {
|
||||||
Button {
|
Button {
|
||||||
icon-name: "edit-clear-all";
|
icon-name: "edit-clear-all";
|
||||||
clicked => $clear ();
|
clicked => $on_clear ();
|
||||||
sensitive: bind template.can_clear_all;
|
sensitive: bind template.can_clear_all;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,8 @@ template $UiPlayQueue: Adw.NavigationPage {
|
||||||
ColumnView view {
|
ColumnView view {
|
||||||
styles [ "data-table" ]
|
styles [ "data-table" ]
|
||||||
|
|
||||||
|
model: bind template.model;
|
||||||
|
|
||||||
//ColumnViewColumn {
|
//ColumnViewColumn {
|
||||||
// factory: SignalListItemFactory {
|
// factory: SignalListItemFactory {
|
||||||
// setup => $on_delete_cell_setup ();
|
// setup => $on_delete_cell_setup ();
|
||||||
|
|
|
@ -1,64 +1,58 @@
|
||||||
class Ui.PlayQueueStore : Object, ListModel, Gtk.SelectionModel {
|
// this is a custom SelectionModel that lets us only signal the
|
||||||
public ListStore inner = new ListStore (typeof (Song));
|
// selection has changed on user interaction
|
||||||
public uint playing_index { get; private set; default = 0; }
|
class PlayQueueSelection : Object, ListModel, Gtk.SelectionModel {
|
||||||
|
public ListStore inner { get; private set; }
|
||||||
|
|
||||||
public signal void begin_playback (Song song);
|
private uint _selected_position;
|
||||||
public signal void prepare_next (Song? song);
|
public uint selected_position {
|
||||||
public signal void playback_continues (Song song);
|
get { return _selected_position; }
|
||||||
public signal void stop_playback ();
|
set {
|
||||||
|
var previous = _selected_position;
|
||||||
|
_selected_position = value;
|
||||||
|
|
||||||
|
if (previous < inner.get_n_items ()) {
|
||||||
|
this.selection_changed (previous, 1);
|
||||||
|
}
|
||||||
|
if (value < inner.get_n_items ()) {
|
||||||
|
this.selection_changed (value, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public signal void user_selected (uint position);
|
||||||
|
|
||||||
|
internal PlayQueueSelection () {
|
||||||
|
this.inner = new ListStore (typeof (Song));
|
||||||
|
this._selected_position = inner.get_n_items ();
|
||||||
|
|
||||||
construct {
|
|
||||||
this.inner.items_changed.connect ((position, removed, added) => {
|
this.inner.items_changed.connect ((position, removed, added) => {
|
||||||
bool emit_signal = false;
|
bool emit_signal = false;
|
||||||
|
|
||||||
if (this.playing_index >= position) {
|
if (this.selected_position >= position) {
|
||||||
if (this.playing_index < position+removed) {
|
if (this.selected_position < position+removed && added == 0) {
|
||||||
this.playing_index = position;
|
|
||||||
if (this.playing_index < this.inner.get_n_items ()) {
|
|
||||||
emit_signal = true;
|
emit_signal = true;
|
||||||
this.begin_playback ((Song) this.inner.get_item (this.playing_index));
|
this._selected_position = position;
|
||||||
this.prepare_next ((Song?) this.inner.get_item (this.playing_index+1));
|
|
||||||
} else {
|
} else {
|
||||||
this.stop_playback ();
|
this._selected_position += added;
|
||||||
|
this._selected_position -= removed;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
this.playing_index += added;
|
|
||||||
this.playing_index -= removed;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.prepare_next ((Song?) this.inner.get_item (this.playing_index+1));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.items_changed (position, removed, added);
|
this.items_changed (position, removed, added);
|
||||||
if (emit_signal) {
|
if (emit_signal) this.selection_changed (position, 1);
|
||||||
this.selection_changed (this.playing_index, 1);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void song_transition () {
|
|
||||||
this.playing_index += 1;
|
|
||||||
this.selection_changed (this.playing_index-1, 2);
|
|
||||||
this.playback_continues ((Song) this.inner.get_item (this.playing_index));
|
|
||||||
this.prepare_next ((Song?) this.inner.get_item (this.playing_index+1));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void playback_finished () {
|
|
||||||
this.playing_index += 1;
|
|
||||||
assert (this.playing_index == this.inner.get_n_items ());
|
|
||||||
this.selection_changed (this.playing_index-1, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
Gtk.Bitset get_selection_in_range (uint position, uint n_items) {
|
Gtk.Bitset get_selection_in_range (uint position, uint n_items) {
|
||||||
var bitset = new Gtk.Bitset.empty ();
|
var bitset = new Gtk.Bitset.empty ();
|
||||||
if (this.playing_index < this.inner.get_n_items ()) {
|
if (this.selected_position < this.inner.get_n_items ()) {
|
||||||
bitset.add (playing_index);
|
bitset.add (selected_position);
|
||||||
}
|
}
|
||||||
return bitset;
|
return bitset;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_selected (uint position) {
|
bool is_selected (uint position) {
|
||||||
return position == this.playing_index;
|
return position == this.selected_position;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool select_all () {
|
bool select_all () {
|
||||||
|
@ -70,21 +64,8 @@ class Ui.PlayQueueStore : Object, ListModel, Gtk.SelectionModel {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var previous = this.playing_index;
|
this.selected_position = position;
|
||||||
this.playing_index = position;
|
this.user_selected (position);
|
||||||
|
|
||||||
if (previous < this.inner.get_n_items ()) {
|
|
||||||
if (previous < position) {
|
|
||||||
this.selection_changed (previous, position-previous+1);
|
|
||||||
} else if (previous > position) {
|
|
||||||
this.selection_changed (position, previous-position+1);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.selection_changed (position, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.begin_playback ((Song) this.inner.get_item (this.playing_index));
|
|
||||||
this.prepare_next ((Song) this.inner.get_item (this.playing_index+1));
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -125,62 +106,13 @@ class Ui.PlayQueueStore : Object, ListModel, Gtk.SelectionModel {
|
||||||
[GtkTemplate (ui = "/eu/callcc/audrey/ui/play_queue.ui")]
|
[GtkTemplate (ui = "/eu/callcc/audrey/ui/play_queue.ui")]
|
||||||
public class Ui.PlayQueue : Adw.NavigationPage {
|
public class Ui.PlayQueue : Adw.NavigationPage {
|
||||||
[GtkChild] private unowned Gtk.ColumnView view;
|
[GtkChild] private unowned Gtk.ColumnView view;
|
||||||
PlayQueueStore store = new PlayQueueStore ();
|
public Gtk.SelectionModel model { get; set; }
|
||||||
|
|
||||||
public signal void begin_playback (Song song);
|
|
||||||
public signal void prepare_next (Song? next_song);
|
|
||||||
public signal void playback_continues (Song song);
|
|
||||||
public signal void stop_playback ();
|
|
||||||
|
|
||||||
public bool can_clear_all { get; private set; default = false; }
|
public bool can_clear_all { get; private set; default = false; }
|
||||||
|
|
||||||
construct {
|
public signal void clear ();
|
||||||
this.view.model = this.store;
|
|
||||||
|
|
||||||
this.store.begin_playback.connect ((song) => this.begin_playback (song));
|
[GtkCallback] private void on_clear () {
|
||||||
this.store.prepare_next.connect ((next_song) => this.prepare_next (next_song));
|
this.clear ();
|
||||||
this.store.playback_continues.connect ((song) => this.playback_continues (song));
|
|
||||||
this.store.stop_playback.connect (() => this.stop_playback ());
|
|
||||||
}
|
|
||||||
|
|
||||||
[GtkCallback] public void clear () {
|
|
||||||
this.store.inner.remove_all ();
|
|
||||||
this.can_clear_all = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void queue (Song song) {
|
|
||||||
this.store.inner.append (song);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void song_transition () {
|
|
||||||
this.store.song_transition ();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void playback_finished () {
|
|
||||||
this.store.playback_finished ();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void restart () {
|
|
||||||
this.store.select_item (0, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void skip_forward () {
|
|
||||||
this.store.select_item (this.store.playing_index+1, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void skip_backward () {
|
|
||||||
if (this.store.playing_index >= 1) {
|
|
||||||
this.store.select_item (this.store.playing_index-1, true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
[GtkCallback] private void on_delete_cell_setup (Object object) {
|
|
||||||
var cell = (Gtk.ColumnViewCell) object;
|
|
||||||
var button = new Gtk.Button.from_icon_name ("edit-delete");
|
|
||||||
button.add_css_class ("flat");
|
|
||||||
button.clicked.connect (() => {
|
|
||||||
//this.store.inner.remove (cell.position);
|
|
||||||
});
|
|
||||||
cell.child = button;
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
|
|
|
@ -81,7 +81,9 @@ template $UiWindow: Adw.ApplicationWindow {
|
||||||
name: "play_queue";
|
name: "play_queue";
|
||||||
title: _("Play queue");
|
title: _("Play queue");
|
||||||
|
|
||||||
child: $UiPlayQueue play_queue {};
|
child: $UiPlayQueue play_queue {
|
||||||
|
model: bind template.play_queue_model;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -146,7 +148,7 @@ template $UiWindow: Adw.ApplicationWindow {
|
||||||
adjustment: Adjustment {
|
adjustment: Adjustment {
|
||||||
lower: 0;
|
lower: 0;
|
||||||
value: bind template.position;
|
value: bind template.position;
|
||||||
upper: bind template.playbin as <$Playbin>.duration;
|
upper: bind template.duration;
|
||||||
};
|
};
|
||||||
|
|
||||||
change-value => $on_play_position_seek ();
|
change-value => $on_play_position_seek ();
|
||||||
|
@ -159,7 +161,7 @@ template $UiWindow: Adw.ApplicationWindow {
|
||||||
"numeric",
|
"numeric",
|
||||||
]
|
]
|
||||||
|
|
||||||
label: bind $format_timestamp (template.playbin as <$Playbin>.duration) as <string>;
|
label: bind $format_timestamp (template.duration) as <string>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,7 +172,7 @@ template $UiWindow: Adw.ApplicationWindow {
|
||||||
Button {
|
Button {
|
||||||
icon-name: "media-skip-backward";
|
icon-name: "media-skip-backward";
|
||||||
valign: center;
|
valign: center;
|
||||||
sensitive: bind $playbin_active (template.playbin as <$Playbin>.state as <$PlaybinState>) as <bool>;
|
//sensitive: bind $playbin_active (template.playbin as <$Playbin>.state as <$PlaybinState>) as <bool>;
|
||||||
|
|
||||||
clicked => $on_skip_backward_clicked ();
|
clicked => $on_skip_backward_clicked ();
|
||||||
}
|
}
|
||||||
|
@ -178,15 +180,15 @@ template $UiWindow: Adw.ApplicationWindow {
|
||||||
Button {
|
Button {
|
||||||
icon-name: "media-seek-backward";
|
icon-name: "media-seek-backward";
|
||||||
valign: center;
|
valign: center;
|
||||||
sensitive: bind $playbin_active (template.playbin as <$Playbin>.state as <$PlaybinState>) as <bool>;
|
//sensitive: bind $playbin_active (template.playbin as <$Playbin>.state as <$PlaybinState>) as <bool>;
|
||||||
|
|
||||||
clicked => $seek_backward ();
|
clicked => $seek_backward ();
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
icon-name: bind $play_pause_icon_name (template.playbin as <$Playbin>.state as <$PlaybinState>) as <string>;
|
//icon-name: bind $play_pause_icon_name (template.playbin as <$Playbin>.state as <$PlaybinState>) as <string>;
|
||||||
valign: center;
|
valign: center;
|
||||||
sensitive: bind $playbin_active (template.playbin as <$Playbin>.state as <$PlaybinState>) as <bool>;
|
//sensitive: bind $playbin_active (template.playbin as <$Playbin>.state as <$PlaybinState>) as <bool>;
|
||||||
|
|
||||||
clicked => $on_play_pause_clicked ();
|
clicked => $on_play_pause_clicked ();
|
||||||
}
|
}
|
||||||
|
@ -194,7 +196,7 @@ template $UiWindow: Adw.ApplicationWindow {
|
||||||
Button {
|
Button {
|
||||||
icon-name: "media-seek-forward";
|
icon-name: "media-seek-forward";
|
||||||
valign: center;
|
valign: center;
|
||||||
sensitive: bind $playbin_active (template.playbin as <$Playbin>.state as <$PlaybinState>) as <bool>;
|
//sensitive: bind $playbin_active (template.playbin as <$Playbin>.state as <$PlaybinState>) as <bool>;
|
||||||
|
|
||||||
clicked => $seek_forward ();
|
clicked => $seek_forward ();
|
||||||
}
|
}
|
||||||
|
@ -202,7 +204,7 @@ template $UiWindow: Adw.ApplicationWindow {
|
||||||
Button {
|
Button {
|
||||||
icon-name: "media-skip-forward";
|
icon-name: "media-skip-forward";
|
||||||
valign: center;
|
valign: center;
|
||||||
sensitive: bind $playbin_active (template.playbin as <$Playbin>.state as <$PlaybinState>) as <bool>;
|
//sensitive: bind $playbin_active (template.playbin as <$Playbin>.state as <$PlaybinState>) as <bool>;
|
||||||
|
|
||||||
clicked => $on_skip_forward_clicked ();
|
clicked => $on_skip_forward_clicked ();
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ class Ui.Window : Adw.ApplicationWindow {
|
||||||
private Setup setup;
|
private Setup setup;
|
||||||
|
|
||||||
public int64 position { get; private set; }
|
public int64 position { get; private set; }
|
||||||
|
public int64 duration { get; private set; }
|
||||||
|
|
||||||
public double volume {
|
public double volume {
|
||||||
get { return this.playbin.volume; }
|
get { return this.playbin.volume; }
|
||||||
|
@ -28,7 +29,8 @@ class Ui.Window : Adw.ApplicationWindow {
|
||||||
|
|
||||||
private Gdk.Paintable next_cover_art = null;
|
private Gdk.Paintable next_cover_art = null;
|
||||||
|
|
||||||
internal Playbin playbin { get; default = new Playbin (); }
|
internal Playbin playbin;
|
||||||
|
public PlayQueueSelection play_queue_model { get; private set; default = new PlayQueueSelection (); }
|
||||||
|
|
||||||
public Window (Gtk.Application app) {
|
public Window (Gtk.Application app) {
|
||||||
Object (application: app);
|
Object (application: app);
|
||||||
|
@ -61,15 +63,30 @@ class Ui.Window : Adw.ApplicationWindow {
|
||||||
|
|
||||||
this.setup = new Setup ();
|
this.setup = new Setup ();
|
||||||
|
|
||||||
|
this.playbin = new Playbin (this.play_queue_model);
|
||||||
|
|
||||||
this.setup.connected.connect ((api) => {
|
this.setup.connected.connect ((api) => {
|
||||||
|
this.playbin.api = api;
|
||||||
|
|
||||||
|
this.playbin.now_playing.connect ((position, song, duration) => {
|
||||||
|
this.song = song;
|
||||||
|
this.duration = duration;
|
||||||
|
api.scrobble.begin (song.id);
|
||||||
|
this.play_queue_model.selected_position = position;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.play_queue_model.user_selected.connect ((position) => {
|
||||||
|
this.playbin.begin_playback (position);
|
||||||
|
});
|
||||||
|
|
||||||
public_api = api;
|
public_api = api;
|
||||||
|
|
||||||
this.shuffle_all_tracks.sensitive = true;
|
this.shuffle_all_tracks.sensitive = true;
|
||||||
this.shuffle_all_tracks.activated.connect (() => {
|
this.shuffle_all_tracks.activated.connect (() => {
|
||||||
this.shuffle_all_tracks.sensitive = false;
|
this.shuffle_all_tracks.sensitive = false;
|
||||||
this.play_queue.clear ();
|
this.play_queue_model.inner.remove_all ();
|
||||||
api.get_random_songs.begin (null, (song) => {
|
api.get_random_songs.begin (null, (song) => {
|
||||||
this.play_queue.queue (song);
|
this.play_queue_model.inner.append (song);
|
||||||
}, (obj, res) => {
|
}, (obj, res) => {
|
||||||
try {
|
try {
|
||||||
api.get_random_songs.end (res);
|
api.get_random_songs.end (res);
|
||||||
|
@ -78,41 +95,9 @@ class Ui.Window : Adw.ApplicationWindow {
|
||||||
}
|
}
|
||||||
this.shuffle_all_tracks.sensitive = true;
|
this.shuffle_all_tracks.sensitive = true;
|
||||||
|
|
||||||
this.play_queue.restart ();
|
this.playbin.begin_playback (0);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
this.play_queue.begin_playback.connect ((song) => {
|
|
||||||
var uri = api.stream_uri (song.id);
|
|
||||||
this.playbin.begin_playback (uri);
|
|
||||||
|
|
||||||
this.song = song;
|
|
||||||
api.scrobble.begin (song.id);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.play_queue.prepare_next.connect ((next_song) => {
|
|
||||||
var next_uri = next_song == null ? null : api.stream_uri (next_song.id);
|
|
||||||
this.playbin.prepare_next (next_uri);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.playbin.song_transition.connect ((uri) => {
|
|
||||||
this.play_queue.song_transition ();
|
|
||||||
});
|
|
||||||
|
|
||||||
this.play_queue.playback_continues.connect ((song) => {
|
|
||||||
this.song = song;
|
|
||||||
api.scrobble.begin (song.id);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.playbin.playback_finished.connect (() => {
|
|
||||||
this.play_queue.playback_finished ();
|
|
||||||
this.song = null;
|
|
||||||
});
|
|
||||||
|
|
||||||
this.play_queue.stop_playback.connect (() => {
|
|
||||||
this.playbin.stop_playback ();
|
|
||||||
this.song = null;
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
this.setup.load ();
|
this.setup.load ();
|
||||||
|
|
||||||
|
@ -190,6 +175,7 @@ class Ui.Window : Adw.ApplicationWindow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
[GtkCallback] private string play_pause_icon_name (PlaybinState state) {
|
[GtkCallback] private string play_pause_icon_name (PlaybinState state) {
|
||||||
if (state == PlaybinState.PLAYING) {
|
if (state == PlaybinState.PLAYING) {
|
||||||
return "media-playback-pause";
|
return "media-playback-pause";
|
||||||
|
@ -200,7 +186,7 @@ class Ui.Window : Adw.ApplicationWindow {
|
||||||
|
|
||||||
[GtkCallback] private bool playbin_active (PlaybinState state) {
|
[GtkCallback] private bool playbin_active (PlaybinState state) {
|
||||||
return state != PlaybinState.STOPPED;
|
return state != PlaybinState.STOPPED;
|
||||||
}
|
}*/
|
||||||
|
|
||||||
[GtkCallback] private string mute_button_icon_name (bool mute) {
|
[GtkCallback] private string mute_button_icon_name (bool mute) {
|
||||||
return mute ? "audio-volume-muted" : "audio-volume-high";
|
return mute ? "audio-volume-muted" : "audio-volume-high";
|
||||||
|
@ -211,11 +197,11 @@ class Ui.Window : Adw.ApplicationWindow {
|
||||||
}
|
}
|
||||||
|
|
||||||
[GtkCallback] private void on_skip_forward_clicked () {
|
[GtkCallback] private void on_skip_forward_clicked () {
|
||||||
this.play_queue.skip_forward ();
|
//this.play_queue.skip_forward ();
|
||||||
}
|
}
|
||||||
|
|
||||||
[GtkCallback] private void on_skip_backward_clicked () {
|
[GtkCallback] private void on_skip_backward_clicked () {
|
||||||
this.play_queue.skip_backward ();
|
//this.play_queue.skip_backward ();
|
||||||
}
|
}
|
||||||
|
|
||||||
[GtkCallback] private void show_setup_dialog () {
|
[GtkCallback] private void show_setup_dialog () {
|
||||||
|
|
Loading…
Reference in a new issue