From e332a2c7068647886222a262f4ce1a10cb72f1b3 Mon Sep 17 00:00:00 2001 From: me Date: Fri, 11 Oct 2024 09:09:47 +0000 Subject: [PATCH] progress --- meson.build | 2 +- src/api.vala | 27 +++++++++++++++++++-------- src/application.vala | 21 ++++++++++++++------- src/window.blp | 15 ++++++++++++--- src/window.vala | 30 ++++++++++++++++++++++++++++++ 5 files changed, 76 insertions(+), 19 deletions(-) diff --git a/meson.build b/meson.build index 7343a54..3d143de 100644 --- a/meson.build +++ b/meson.build @@ -1,7 +1,7 @@ project('wavelet', ['c', 'vala'], version: '0.1.0', meson_version: '>= 1.0.0', - default_options: [ 'warning_level=2', 'werror=false', ], + default_options: [ 'warning_level=none', 'werror=false', ], ) i18n = import('i18n') diff --git a/src/api.vala b/src/api.vala index 493c112..8402282 100644 --- a/src/api.vala +++ b/src/api.vala @@ -164,7 +164,7 @@ public class Wavelet.Subsonic : Object { public Subsonic.with_password (string url, string username, string password) { this.url = url; - this.parameters = @"u=$(Uri.escape_string(username))&p=$(Uri.escape_string(password))&v=1.16.1&c=eu.callcc.Wavelet&f=json"; + this.parameters = @"u=$(Uri.escape_string(username))&p=$(Uri.escape_string(password))&v=1.16.1&c=eu.callcc.Wavelet"; this.session = new Soup.Session (); @@ -175,7 +175,7 @@ public class Wavelet.Subsonic : Object { public Subsonic.with_token (string url, string username, string token, string salt) { this.url = url; - this.parameters = @"u=$(Uri.escape_string(username))&t=$(Uri.escape_string(token))&s=$(Uri.escape_string(salt))&v=1.16.1&c=eu.callcc.Wavelet&f=json"; + this.parameters = @"u=$(Uri.escape_string(username))&t=$(Uri.escape_string(token))&s=$(Uri.escape_string(salt))&v=1.16.1&c=eu.callcc.Wavelet"; this.session = new Soup.Session (); @@ -198,7 +198,7 @@ public class Wavelet.Subsonic : Object { } public async void ping () throws Error { - var msg = new Soup.Message ("GET", @"$(this.url)/rest/ping?$(this.parameters)"); + var msg = new Soup.Message ("GET", @"$(this.url)/rest/ping?f=json&$(this.parameters)"); var bytes = yield this.session.send_and_read_async (msg, Priority.DEFAULT, null); assert (msg.get_status () == Soup.Status.OK); @@ -222,7 +222,7 @@ public class Wavelet.Subsonic : Object { this.remaining_artists = 0; this.remaining_albums = 0; - var msg = new Soup.Message ("GET", @"$(this.url)/rest/getArtists?$(this.parameters)"); + var msg = new Soup.Message ("GET", @"$(this.url)/rest/getArtists?f=json&$(this.parameters)"); var bytes = yield this.session.send_and_read_async (msg, Priority.DEFAULT, null); var parser = new Json.Parser (); @@ -264,7 +264,7 @@ public class Wavelet.Subsonic : Object { private async void reload_artist (string artist_id) throws Error { try { - var msg = new Soup.Message ("GET", @"$(this.url)/rest/getArtist?id=$(artist_id)&$(this.parameters)"); + var msg = new Soup.Message ("GET", @"$(this.url)/rest/getArtist?id=$(artist_id)&f=json&$(this.parameters)"); var bytes = yield this.session.send_and_read_async (msg, Priority.DEFAULT, null); assert (msg.get_status () == Soup.Status.OK); @@ -299,7 +299,7 @@ public class Wavelet.Subsonic : Object { private async void reload_album (string album_id) throws Error { try { - var msg = new Soup.Message ("GET", @"$(this.url)/rest/getAlbum?id=$(album_id)&$(this.parameters)"); + var msg = new Soup.Message ("GET", @"$(this.url)/rest/getAlbum?id=$(album_id)&f=json&$(this.parameters)"); var bytes = yield this.session.send_and_read_async (msg, Priority.DEFAULT, null); assert (msg.get_status () == Soup.Status.OK); @@ -340,7 +340,7 @@ public class Wavelet.Subsonic : Object { str_parameters = @"$parameters&"; } - var msg = new Soup.Message("GET", @"$(this.url)/rest/getRandomSongs?$(str_parameters)size=500&$(this.parameters)"); + var msg = new Soup.Message("GET", @"$(this.url)/rest/getRandomSongs?$(str_parameters)size=500&f=json&$(this.parameters)"); var bytes = yield this.session.send_and_read_async (msg, Priority.DEFAULT, null); assert (msg.get_status () == Soup.Status.OK); @@ -363,6 +363,17 @@ public class Wavelet.Subsonic : Object { } public string stream_uri (string id) { - return @"$(this.url)/rest/stream?id=$(id)&$(this.parameters)"; + return @"$(this.url)/rest/stream?id=$(Uri.escape_string(id))&$(this.parameters)"; + } + + public string cover_art_uri (string id, int size) { + return @"$(this.url)/rest/getCoverArt?id=$(Uri.escape_string(id))&$(size)&$(this.parameters)"; + } + + public async Gdk.Pixbuf cover_art (string id, int size, Cancellable cancellable) throws Error { + var msg = new Soup.Message("GET", this.cover_art_uri (id, size)); + var stream = yield this.session.send_async (msg, Priority.DEFAULT, cancellable); + assert (msg.get_status () == Soup.Status.OK); + return yield new Gdk.Pixbuf.from_stream_async (stream, cancellable); } } diff --git a/src/application.vala b/src/application.vala index f5d7205..4ac4be5 100644 --- a/src/application.vala +++ b/src/application.vala @@ -20,6 +20,7 @@ Sqlite.Database config_db; Gst.Element playbin; +Wavelet.Subsonic public_api; public class Wavelet.Application : Adw.Application { public Application () { @@ -96,16 +97,16 @@ public class Wavelet.Application : Adw.Application { win.play_position.sensitive = true; int64 duration_ns; - assert(playbin.query_duration (Gst.Format.TIME, out duration_ns)); - print("%lld\n", duration_ns); - - win.play_position_ms = 0; - win.play_duration_ms = (int) (duration_ns / 1000000); + if (playbin.query_duration (Gst.Format.TIME, out duration_ns)) { + win.play_position_ms = 0; + win.play_duration_ms = (int) (duration_ns / 1000000); + } else { + warning ("could not query playbin duration after ASYNC_DONE"); + } break; default: - print ("%s\n", message.type.to_string ()); break; } return true; @@ -114,6 +115,8 @@ public class Wavelet.Application : Adw.Application { var next_queue = new AsyncQueue (); win.setup.connected.connect ((api) => { + public_api = api; + win.artist_list.model = api.artist_list; win.song_list.model = api.song_list; @@ -130,7 +133,11 @@ public class Wavelet.Application : Adw.Application { api.get_random_songs.begin (null, (song) => { win.play_queue.queue (song); }, (obj, res) => { - api.get_random_songs.end (res); + try { + api.get_random_songs.end (res); + } catch (Error e) { + error ("could not get random songs: %s", e.message); + } win.shuffle_all_tracks.sensitive = true; }); }); diff --git a/src/window.blp b/src/window.blp index 4b24e1e..99f81ff 100644 --- a/src/window.blp +++ b/src/window.blp @@ -105,9 +105,18 @@ template $WaveletWindow: Adw.ApplicationWindow { "toolbar", ] - Image { - height-request: 100; - width-request: 100; + Overlay { + [overlay] Adw.Spinner { + halign: center; + valign: center; + width-request: 20; + height-request: 20; + visible: bind template.cover_art_loading; + } + + Picture { + paintable: bind template.playing_cover_art; + } } Box { diff --git a/src/window.vala b/src/window.vala index f75d86f..3348a0f 100644 --- a/src/window.vala +++ b/src/window.vala @@ -43,12 +43,42 @@ public class Wavelet.Window : Adw.ApplicationWindow { public Song? song { get; set; default = null; } + private Cancellable cancel_loading_art; + public bool cover_art_loading { get; set; default = false; } + public Gdk.Paintable playing_cover_art { get; set; } + public Window (Gtk.Application app) { Object (application: app); } construct { this.sidebar.select_row (this.sidebar.get_row_at_index (0)); + + this.notify["song"].connect (() => { + if (this.cancel_loading_art != null) { + this.cancel_loading_art.cancel (); + } + this.cancel_loading_art = new Cancellable (); + + this.playing_cover_art = Gdk.Paintable.empty (100, 100); + if (this.song != null) { + this.cover_art_loading = true; + + string song_id = this.song.id; + public_api.cover_art.begin (song_id, 100, this.cancel_loading_art, (obj, res) => { + try { + this.playing_cover_art = Gdk.Texture.for_pixbuf (public_api.cover_art.end (res)); + this.cover_art_loading = false; + } catch (Error e) { + if (!(e is IOError.CANCELLED)) { + warning ("could not load cover for %s: %s", song_id, e.message); + this.cover_art_loading = false; + } + } + }); + } + }); + this.set("song", null); } public void show_mute () {