This commit is contained in:
me 2024-10-11 09:09:47 +00:00
parent 45cbe8cb2c
commit e332a2c706
5 changed files with 76 additions and 19 deletions

View file

@ -1,7 +1,7 @@
project('wavelet', ['c', 'vala'], project('wavelet', ['c', 'vala'],
version: '0.1.0', version: '0.1.0',
meson_version: '>= 1.0.0', meson_version: '>= 1.0.0',
default_options: [ 'warning_level=2', 'werror=false', ], default_options: [ 'warning_level=none', 'werror=false', ],
) )
i18n = import('i18n') i18n = import('i18n')

View file

@ -164,7 +164,7 @@ public class Wavelet.Subsonic : Object {
public Subsonic.with_password (string url, string username, string password) { public Subsonic.with_password (string url, string username, string password) {
this.url = url; 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 (); 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) { public Subsonic.with_token (string url, string username, string token, string salt) {
this.url = url; 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 (); this.session = new Soup.Session ();
@ -198,7 +198,7 @@ public class Wavelet.Subsonic : Object {
} }
public async void ping () throws Error { 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); var bytes = yield this.session.send_and_read_async (msg, Priority.DEFAULT, null);
assert (msg.get_status () == Soup.Status.OK); assert (msg.get_status () == Soup.Status.OK);
@ -222,7 +222,7 @@ public class Wavelet.Subsonic : Object {
this.remaining_artists = 0; this.remaining_artists = 0;
this.remaining_albums = 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 bytes = yield this.session.send_and_read_async (msg, Priority.DEFAULT, null);
var parser = new Json.Parser (); var parser = new Json.Parser ();
@ -264,7 +264,7 @@ public class Wavelet.Subsonic : Object {
private async void reload_artist (string artist_id) throws Error { private async void reload_artist (string artist_id) throws Error {
try { 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); var bytes = yield this.session.send_and_read_async (msg, Priority.DEFAULT, null);
assert (msg.get_status () == Soup.Status.OK); 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 { private async void reload_album (string album_id) throws Error {
try { 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); var bytes = yield this.session.send_and_read_async (msg, Priority.DEFAULT, null);
assert (msg.get_status () == Soup.Status.OK); assert (msg.get_status () == Soup.Status.OK);
@ -340,7 +340,7 @@ public class Wavelet.Subsonic : Object {
str_parameters = @"$parameters&"; 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); var bytes = yield this.session.send_and_read_async (msg, Priority.DEFAULT, null);
assert (msg.get_status () == Soup.Status.OK); assert (msg.get_status () == Soup.Status.OK);
@ -363,6 +363,17 @@ public class Wavelet.Subsonic : Object {
} }
public string stream_uri (string id) { 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);
} }
} }

View file

@ -20,6 +20,7 @@
Sqlite.Database config_db; Sqlite.Database config_db;
Gst.Element playbin; Gst.Element playbin;
Wavelet.Subsonic public_api;
public class Wavelet.Application : Adw.Application { public class Wavelet.Application : Adw.Application {
public Application () { public Application () {
@ -96,16 +97,16 @@ public class Wavelet.Application : Adw.Application {
win.play_position.sensitive = true; win.play_position.sensitive = true;
int64 duration_ns; int64 duration_ns;
assert(playbin.query_duration (Gst.Format.TIME, out duration_ns)); if (playbin.query_duration (Gst.Format.TIME, out duration_ns)) {
print("%lld\n", duration_ns);
win.play_position_ms = 0; win.play_position_ms = 0;
win.play_duration_ms = (int) (duration_ns / 1000000); win.play_duration_ms = (int) (duration_ns / 1000000);
} else {
warning ("could not query playbin duration after ASYNC_DONE");
}
break; break;
default: default:
print ("%s\n", message.type.to_string ());
break; break;
} }
return true; return true;
@ -114,6 +115,8 @@ public class Wavelet.Application : Adw.Application {
var next_queue = new AsyncQueue<string?> (); var next_queue = new AsyncQueue<string?> ();
win.setup.connected.connect ((api) => { win.setup.connected.connect ((api) => {
public_api = api;
win.artist_list.model = api.artist_list; win.artist_list.model = api.artist_list;
win.song_list.model = api.song_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) => { api.get_random_songs.begin (null, (song) => {
win.play_queue.queue (song); win.play_queue.queue (song);
}, (obj, res) => { }, (obj, res) => {
try {
api.get_random_songs.end (res); api.get_random_songs.end (res);
} catch (Error e) {
error ("could not get random songs: %s", e.message);
}
win.shuffle_all_tracks.sensitive = true; win.shuffle_all_tracks.sensitive = true;
}); });
}); });

View file

@ -105,9 +105,18 @@ template $WaveletWindow: Adw.ApplicationWindow {
"toolbar", "toolbar",
] ]
Image { Overlay {
height-request: 100; [overlay] Adw.Spinner {
width-request: 100; halign: center;
valign: center;
width-request: 20;
height-request: 20;
visible: bind template.cover_art_loading;
}
Picture {
paintable: bind template.playing_cover_art;
}
} }
Box { Box {

View file

@ -43,12 +43,42 @@ public class Wavelet.Window : Adw.ApplicationWindow {
public Song? song { get; set; default = null; } 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) { public Window (Gtk.Application app) {
Object (application: app); Object (application: app);
} }
construct { construct {
this.sidebar.select_row (this.sidebar.get_row_at_index (0)); 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 () { public void show_mute () {