api -> subsonic

This commit is contained in:
Erica Z 2024-10-16 13:02:32 +02:00
parent e9bfd056c8
commit 19dd3b13e2
7 changed files with 37 additions and 38 deletions

View file

@ -1,5 +1,3 @@
Subsonic public_api;
public class Audrey.Application : Adw.Application { public class Audrey.Application : Adw.Application {
public Application () { public Application () {
Object ( Object (

View file

@ -1,10 +1,10 @@
audrey_sources = [ audrey_sources = [
'api.vala',
'application.vala', 'application.vala',
'globalconf.vala', 'globalconf.vala',
'main.vala', 'main.vala',
'mpris.vala', 'mpris.vala',
'playbin.vala', 'playbin.vala',
'subsonic.vala',
'ui/play_queue.vala', 'ui/play_queue.vala',
'ui/setup.vala', 'ui/setup.vala',
'ui/window.vala', 'ui/window.vala',

View file

@ -25,14 +25,14 @@ class Playbin : GLib.Object {
public PlaybinState state { get; private set; default = PlaybinState.STOPPED; } public PlaybinState state { get; private set; default = PlaybinState.STOPPED; }
public Song? current_song { get; private set; default = null; } public Subsonic.Song? current_song { get; private set; default = null; }
// true if a timer should update the position property // true if a timer should update the position property
private bool update_position = false; private bool update_position = false;
public int64 position { get; private set; default = 0; } public int64 position { get; private set; default = 0; }
public int64 duration { get; private set; default = 1; } // if 0, the seekbar vanishes public int64 duration { get; private set; default = 1; } // if 0, the seekbar vanishes
public Subsonic api { get; set; default = null; } public Subsonic.Client api { get; set; default = null; }
// sent when a new song starts playing // sent when a new song starts playing
// continues: whether the track is a gapless continuation // continues: whether the track is a gapless continuation
@ -109,7 +109,7 @@ class Playbin : GLib.Object {
// we're in luck, about-to-finish hasn't been triggered yet // we're in luck, about-to-finish hasn't been triggered yet
// we can get away with replacing it // we can get away with replacing it
if (this.current_position+1 < play_queue.get_n_items ()) { if (this.current_position+1 < play_queue.get_n_items ()) {
Song song = (Song) play_queue.get_item (this.current_position+1); var song = (Subsonic.Song) play_queue.get_item (this.current_position+1);
this.next_uri.push (this.api.stream_uri (song.id)); this.next_uri.push (this.api.stream_uri (song.id));
} else { } else {
this.next_uri.push (""); this.next_uri.push ("");
@ -182,7 +182,7 @@ class Playbin : GLib.Object {
this.next_gapless = true; this.next_gapless = true;
} }
var now_playing = (Song) play_queue.get_item (this.current_position); var now_playing = (Subsonic.Song) play_queue.get_item (this.current_position);
if (this.api.stream_uri (now_playing.id) == (string) this.playbin.current_uri) { if (this.api.stream_uri (now_playing.id) == (string) this.playbin.current_uri) {
if (continues) { if (continues) {
this.current_song = now_playing; this.current_song = now_playing;
@ -190,7 +190,7 @@ class Playbin : GLib.Object {
} }
if (this.current_position+1 < play_queue.get_n_items ()) { if (this.current_position+1 < play_queue.get_n_items ()) {
Song song = (Song) play_queue.get_item (this.current_position+1); var song = (Subsonic.Song) play_queue.get_item (this.current_position+1);
this.next_uri.push (this.api.stream_uri (song.id)); this.next_uri.push (this.api.stream_uri (song.id));
} else { } else {
this.next_uri.push (""); this.next_uri.push ("");
@ -230,7 +230,7 @@ class Playbin : GLib.Object {
this.current_position = position; this.current_position = position;
this.playbin.set_state (Gst.State.READY); this.playbin.set_state (Gst.State.READY);
var song = (Song) this.play_queue.get_item (position); var song = (Subsonic.Song) this.play_queue.get_item (position);
this.playbin.uri = this.api.stream_uri (song.id); this.playbin.uri = this.api.stream_uri (song.id);
this.playbin.set_state (Gst.State.PLAYING); this.playbin.set_state (Gst.State.PLAYING);
this.next_gapless = false; this.next_gapless = false;

View file

@ -1,11 +1,11 @@
errordomain SubsonicError { public errordomain Subsonic.Error {
BAD_AUTHN, BAD_AUTHN,
ERROR, ERROR,
} }
public delegate void SongCallback (Song song); public delegate void Subsonic.SongCallback (Song song);
public class Artist : Object, Json.Serializable { public class Subsonic.Artist : Object {
public string index; public string index;
public string id; public string id;
public string name { get; private set; } public string name { get; private set; }
@ -38,7 +38,7 @@ public class Artist : Object, Json.Serializable {
} }
} }
public class Album : Object { public class Subsonic.Album : Object {
public string id; public string id;
public string name; public string name;
@ -53,7 +53,7 @@ public class Album : Object {
} }
} }
public class Song : Object { public class Subsonic.Song : Object {
public string id { get; private set; } public string id { get; private set; }
public string title { get; private set; } public string title { get; private set; }
public string album { get; private set; } public string album { get; private set; }
@ -89,7 +89,7 @@ public class Song : Object {
} }
} }
public struct API.PlayQueue { public struct Subsonic.PlayQueue {
public string current; public string current;
public int64 position; public int64 position;
public DateTime changed; public DateTime changed;
@ -128,7 +128,7 @@ public struct API.PlayQueue {
} }
} }
public class Subsonic : Object { public class Subsonic.Client : Object {
public ListStore artist_list; public ListStore artist_list;
public ListStore album_list; public ListStore album_list;
public ListStore song_list; public ListStore song_list;
@ -137,7 +137,7 @@ public class Subsonic : Object {
private string url; private string url;
private string parameters; private string parameters;
public Subsonic.with_token (string url, string username, string token, string salt) { public Client.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.audrey"; this.parameters = @"u=$(Uri.escape_string(username))&t=$(Uri.escape_string(token))&s=$(Uri.escape_string(salt))&v=1.16.1&c=eu.callcc.audrey";
@ -149,7 +149,7 @@ public class Subsonic : Object {
this.song_list = new ListStore (typeof (Song)); this.song_list = new ListStore (typeof (Song));
} }
private void unwrap_response (Json.Reader reader) throws Error { private void unwrap_response (Json.Reader reader) throws GLib.Error {
reader.read_member ("subsonic-response"); reader.read_member ("subsonic-response");
reader.read_member ("status"); reader.read_member ("status");
@ -157,15 +157,15 @@ public class Subsonic : Object {
reader.end_member (); reader.end_member ();
reader.read_member ("error"); reader.read_member ("error");
reader.read_member ("message"); reader.read_member ("message");
throw new SubsonicError.ERROR (reader.get_string_value () ?? "???"); throw new Subsonic.Error.ERROR (reader.get_string_value () ?? "???");
} }
reader.end_member(); reader.end_member();
} }
public async void ping () throws Error { public async void ping () throws GLib.Error {
var msg = new Soup.Message ("GET", @"$(this.url)/rest/ping?f=json&$(this.parameters)"); var msg = new Soup.Message ("GET", @"$(this.url)/rest/ping?f=json&$(this.parameters)");
if (msg == null) { if (msg == null) {
throw new SubsonicError.BAD_AUTHN ("Bad message"); throw new Subsonic.Error.BAD_AUTHN ("Bad message");
} }
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);
@ -178,7 +178,7 @@ public class Subsonic : Object {
this.unwrap_response (reader); this.unwrap_response (reader);
} }
public async void scrobble (string id) throws Error { public async void scrobble (string id) throws GLib.Error {
var msg = new Soup.Message ("GET", @"$(this.url)/rest/scrobble?id=$(Uri.escape_string (id))&f=json&$(this.parameters)"); var msg = new Soup.Message ("GET", @"$(this.url)/rest/scrobble?id=$(Uri.escape_string (id))&f=json&$(this.parameters)");
assert (msg != null); assert (msg != null);
@ -192,7 +192,7 @@ public class Subsonic : Object {
this.unwrap_response (reader); this.unwrap_response (reader);
} }
public async void get_random_songs (string? parameters, SongCallback callback) throws Error { public async void get_random_songs (string? parameters, SongCallback callback) throws GLib.Error {
string str_parameters; string str_parameters;
if (parameters == null) { if (parameters == null) {
str_parameters = ""; str_parameters = "";
@ -232,7 +232,7 @@ public class Subsonic : Object {
return @"$(this.url)/rest/getCoverArt?id=$(Uri.escape_string(id))&$(this.parameters)"; return @"$(this.url)/rest/getCoverArt?id=$(Uri.escape_string(id))&$(this.parameters)";
} }
public async Gdk.Pixbuf cover_art (string id, Cancellable cancellable) throws Error { public async Gdk.Pixbuf cover_art (string id, Cancellable cancellable) throws GLib.Error {
var msg = new Soup.Message("GET", this.cover_art_uri (id)); var msg = new Soup.Message("GET", this.cover_art_uri (id));
assert (msg != null); assert (msg != null);

View file

@ -34,8 +34,8 @@ template $UiPlayQueue: Adw.NavigationPage {
template ColumnViewCell { template ColumnViewCell {
child: Label { child: Label {
halign: start; halign: start;
label: bind template.item as <$Song>.title; label: bind template.item as <$SubsonicSong>.title;
tooltip-text: bind template.item as <$Song>.title; tooltip-text: bind template.item as <$SubsonicSong>.title;
ellipsize: end; ellipsize: end;
}; };
} }
@ -50,8 +50,8 @@ template $UiPlayQueue: Adw.NavigationPage {
template ColumnViewCell { template ColumnViewCell {
child: Label { child: Label {
halign: start; halign: start;
label: bind template.item as <$Song>.artist; label: bind template.item as <$SubsonicSong>.artist;
tooltip-text: bind template.item as <$Song>.artist; tooltip-text: bind template.item as <$SubsonicSong>.artist;
ellipsize: end; ellipsize: end;
}; };
} }

View file

@ -29,7 +29,7 @@ public class Ui.Setup : Adw.PreferencesDialog {
public string token; public string token;
public string salt; public string salt;
public signal void connected (Subsonic api); public signal void connected (Subsonic.Client api);
private static Secret.Schema secret_schema = new Secret.Schema ( private static Secret.Schema secret_schema = new Secret.Schema (
"eu.callcc.audrey", "eu.callcc.audrey",
@ -49,7 +49,7 @@ public class Ui.Setup : Adw.PreferencesDialog {
string new_token, new_salt; string new_token, new_salt;
salt_password (this.password, out new_token, out new_salt); salt_password (this.password, out new_token, out new_salt);
var api = new Subsonic.with_token ( var api = new Subsonic.Client.with_token (
this.server_url, this.server_url,
this.username, this.username,
new_token, new_token,

View file

@ -9,6 +9,8 @@ class Ui.Window : Adw.ApplicationWindow {
private Setup setup; private Setup setup;
private Subsonic.Client api;
public int64 position { get; private set; } public int64 position { get; private set; }
public double volume { public double volume {
@ -25,7 +27,7 @@ class Ui.Window : Adw.ApplicationWindow {
public Gdk.Paintable playing_cover_art { get; set; } public Gdk.Paintable playing_cover_art { get; set; }
public Playbin playbin { get; private set; default = new Playbin (); } public Playbin playbin { get; private set; default = new Playbin (); }
public ListStore play_queue_store { get; private set; default = new ListStore (typeof (Song)); } public ListStore play_queue_store { get; private set; default = new ListStore (typeof (Subsonic.Song)); }
public Window (Gtk.Application app) { public Window (Gtk.Application app) {
Object (application: app); Object (application: app);
@ -56,6 +58,7 @@ class Ui.Window : Adw.ApplicationWindow {
this.setup = new Setup (); this.setup = new Setup ();
this.setup.connected.connect ((api) => { this.setup.connected.connect ((api) => {
this.api = api;
this.playbin.api = api; this.playbin.api = api;
this.playbin.now_playing.connect ((continues) => { this.playbin.now_playing.connect ((continues) => {
@ -70,8 +73,6 @@ class Ui.Window : Adw.ApplicationWindow {
this.playbin.select_track (position); this.playbin.select_track (position);
}); });
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;
@ -105,9 +106,9 @@ class Ui.Window : Adw.ApplicationWindow {
this.cover_art_loading = true; this.cover_art_loading = true;
string song_id = playbin.current_song.id; string song_id = playbin.current_song.id;
public_api.cover_art.begin (song_id, this.cancel_loading_art, (obj, res) => { this.api.cover_art.begin (song_id, this.cancel_loading_art, (obj, res) => {
try { try {
this.playing_cover_art = Gdk.Texture.for_pixbuf (public_api.cover_art.end (res)); this.playing_cover_art = Gdk.Texture.for_pixbuf (this.api.cover_art.end (res));
this.cover_art_loading = false; this.cover_art_loading = false;
} catch (Error e) { } catch (Error e) {
if (!(e is IOError.CANCELLED)) { if (!(e is IOError.CANCELLED)) {
@ -215,15 +216,15 @@ class Ui.Window : Adw.ApplicationWindow {
this.seek_impl (new_position); this.seek_impl (new_position);
} }
[GtkCallback] private string song_title (Song? song) { [GtkCallback] private string song_title (Subsonic.Song? song) {
return song == null ? "" : song.title; return song == null ? "" : song.title;
} }
[GtkCallback] private string song_artist (Song? song) { [GtkCallback] private string song_artist (Subsonic.Song? song) {
return song == null ? "" : song.artist; return song == null ? "" : song.artist;
} }
[GtkCallback] private string song_album (Song? song) { [GtkCallback] private string song_album (Subsonic.Song? song) {
return song == null ? "" : song.album; return song == null ? "" : song.album;
} }
} }