audrey/src/subsonic.vala

256 lines
8.1 KiB
Vala
Raw Normal View History

2024-10-16 11:02:32 +00:00
public errordomain Subsonic.Error {
2024-10-12 17:53:13 +00:00
BAD_AUTHN,
ERROR,
2024-10-06 11:21:53 +00:00
}
2024-10-16 11:02:32 +00:00
public delegate void Subsonic.SongCallback (Song song);
2024-10-10 10:51:12 +00:00
2024-10-16 11:02:32 +00:00
public class Subsonic.Artist : Object {
2024-10-06 11:21:53 +00:00
public string index;
public string id;
public string name { get; private set; }
public string? cover_art;
public string? artist_image_url;
public int64 album_count;
public Artist (string index, Json.Reader reader) {
this.index = index;
reader.read_member ("id");
this.id = reader.get_string_value ();
reader.end_member ();
reader.read_member ("name");
this.name = reader.get_string_value ();
reader.end_member ();
reader.read_member ("coverArt");
this.cover_art = reader.get_string_value ();
reader.end_member ();
reader.read_member ("artistImageUrl");
this.artist_image_url = reader.get_string_value ();
reader.end_member ();
reader.read_member ("albumCount");
this.album_count = reader.get_int_value ();
reader.end_member ();
}
}
2024-10-16 11:02:32 +00:00
public class Subsonic.Album : Object {
2024-10-06 11:21:53 +00:00
public string id;
public string name;
public Album (Json.Reader reader) {
reader.read_member ("id");
this.id = reader.get_string_value ();
reader.end_member ();
reader.read_member ("name");
this.name = reader.get_string_value ();
reader.end_member ();
}
}
2024-10-16 11:02:32 +00:00
public class Subsonic.Song : Object {
2024-10-06 11:21:53 +00:00
public string id { get; private set; }
public string title { get; private set; }
public string album { get; private set; }
public string artist { get; private set; }
public int64 track { get; private set; }
2024-10-11 07:53:30 +00:00
public int64 year { get; private set; }
2024-10-12 16:35:42 +00:00
public DateTime? starred { get; private set; }
2024-10-16 11:12:58 +00:00
public int64 duration { get; private set; }
2024-10-06 11:21:53 +00:00
public Song (Json.Reader reader) {
reader.read_member ("id");
this.id = reader.get_string_value ();
reader.end_member ();
reader.read_member ("title");
this.title = reader.get_string_value ();
reader.end_member ();
reader.read_member ("album");
this.album = reader.get_string_value ();
reader.end_member ();
reader.read_member ("artist");
this.artist = reader.get_string_value ();
reader.end_member ();
reader.read_member ("track");
this.track = reader.get_int_value ();
reader.end_member ();
2024-10-11 07:53:30 +00:00
reader.read_member ("year");
this.year = reader.get_int_value ();
reader.end_member ();
2024-10-16 11:12:58 +00:00
reader.read_member ("duration");
this.duration = reader.get_int_value ();
reader.end_member ();
2024-10-06 11:21:53 +00:00
}
}
2024-10-16 11:02:32 +00:00
public struct Subsonic.PlayQueue {
2024-10-10 09:53:52 +00:00
public string current;
public int64 position;
public DateTime changed;
public string changed_by;
public ListStore songs;
internal PlayQueue.from_reader (Json.Reader reader) {
reader.read_member ("current");
this.current = reader.get_string_value ();
reader.end_member ();
reader.read_member ("position");
this.position = reader.get_int_value ();
reader.end_member ();
reader.read_member ("changed");
this.changed = new DateTime.from_iso8601 (reader.get_string_value (), null);
reader.end_member ();
reader.read_member ("changed_by");
this.changed_by = reader.get_string_value ();
reader.end_member ();
2024-10-10 10:51:12 +00:00
//print("%s %lli %s %s\n",this.current, this.position, this.changed, this.changed_by);
2024-10-10 09:53:52 +00:00
this.songs = new ListStore (typeof (Song));
reader.read_member ("song");
for (int i = 0; i < reader.count_elements (); i += 1) {
reader.read_element (i);
this.songs.append (new Song (reader));
reader.end_element ();
}
reader.end_member ();
assert (reader.get_error () == null);
}
}
2024-10-16 11:02:32 +00:00
public class Subsonic.Client : Object {
2024-10-06 11:21:53 +00:00
public ListStore artist_list;
public ListStore album_list;
public ListStore song_list;
private Soup.Session session;
private string url;
private string parameters;
2024-10-16 11:02:32 +00:00
public Client.with_token (string url, string username, string token, string salt) {
2024-10-06 11:21:53 +00:00
this.url = url;
2024-10-12 20:52:29 +00:00
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";
2024-10-06 11:21:53 +00:00
this.session = new Soup.Session ();
2024-10-15 21:16:43 +00:00
this.session.user_agent = Audrey.Const.user_agent;
2024-10-06 11:21:53 +00:00
this.artist_list = new ListStore (typeof (Artist));
this.album_list = new ListStore (typeof (Album));
this.song_list = new ListStore (typeof (Song));
}
2024-10-16 11:02:32 +00:00
private void unwrap_response (Json.Reader reader) throws GLib.Error {
2024-10-06 11:21:53 +00:00
reader.read_member ("subsonic-response");
reader.read_member ("status");
if (reader.get_string_value () != "ok") {
reader.end_member ();
reader.read_member ("error");
reader.read_member ("message");
2024-10-16 11:02:32 +00:00
throw new Subsonic.Error.ERROR (reader.get_string_value () ?? "???");
2024-10-06 11:21:53 +00:00
}
reader.end_member();
}
2024-10-16 11:02:32 +00:00
public async void ping () throws GLib.Error {
2024-10-11 09:09:47 +00:00
var msg = new Soup.Message ("GET", @"$(this.url)/rest/ping?f=json&$(this.parameters)");
2024-10-12 17:53:13 +00:00
if (msg == null) {
2024-10-16 11:02:32 +00:00
throw new Subsonic.Error.BAD_AUTHN ("Bad message");
2024-10-12 17:53:13 +00:00
}
2024-10-06 11:21:53 +00:00
var bytes = yield this.session.send_and_read_async (msg, Priority.DEFAULT, null);
2024-10-10 10:51:12 +00:00
assert (msg.get_status () == Soup.Status.OK);
2024-10-12 16:40:52 +00:00
var parser = new Json.Parser ();
parser.load_from_data ((string) bytes.get_data ());
var reader = new Json.Reader (parser.get_root ());
this.unwrap_response (reader);
}
2024-10-16 11:02:32 +00:00
public async void scrobble (string id) throws GLib.Error {
2024-10-12 16:40:52 +00:00
var msg = new Soup.Message ("GET", @"$(this.url)/rest/scrobble?id=$(Uri.escape_string (id))&f=json&$(this.parameters)");
2024-10-12 17:53:13 +00:00
assert (msg != null);
2024-10-12 16:40:52 +00:00
var bytes = yield this.session.send_and_read_async (msg, Priority.DEFAULT, null);
assert (msg.get_status () == Soup.Status.OK);
2024-10-06 11:21:53 +00:00
var parser = new Json.Parser ();
parser.load_from_data ((string) bytes.get_data ());
var reader = new Json.Reader (parser.get_root ());
this.unwrap_response (reader);
}
2024-10-16 11:02:32 +00:00
public async void get_random_songs (string? parameters, SongCallback callback) throws GLib.Error {
2024-10-10 10:51:12 +00:00
string str_parameters;
if (parameters == null) {
str_parameters = "";
} else {
str_parameters = @"$parameters&";
}
2024-10-11 09:09:47 +00:00
var msg = new Soup.Message("GET", @"$(this.url)/rest/getRandomSongs?$(str_parameters)size=500&f=json&$(this.parameters)");
2024-10-12 17:53:13 +00:00
assert (msg != null);
2024-10-10 10:51:12 +00:00
var bytes = yield this.session.send_and_read_async (msg, Priority.DEFAULT, null);
assert (msg.get_status () == Soup.Status.OK);
var parser = new Json.Parser ();
parser.load_from_data ((string) bytes.get_data ());
var reader = new Json.Reader (parser.get_root ());
this.unwrap_response (reader);
reader.read_member ("randomSongs");
reader.read_member ("song");
for (int i = 0; i < reader.count_elements (); i += 1) {
reader.read_element (i);
callback (new Song (reader));
reader.end_element ();
2024-10-25 20:09:57 +00:00
Idle.add (this.get_random_songs.callback);
yield;
2024-10-10 10:51:12 +00:00
}
assert (reader.get_error () == null);
}
2024-10-10 20:04:55 +00:00
public string stream_uri (string id) {
2024-10-11 09:09:47 +00:00
return @"$(this.url)/rest/stream?id=$(Uri.escape_string(id))&$(this.parameters)";
}
2024-10-13 17:00:47 +00:00
public string cover_art_uri (string id) {
return @"$(this.url)/rest/getCoverArt?id=$(Uri.escape_string(id))&$(this.parameters)";
2024-10-11 09:09:47 +00:00
}
2024-10-16 11:02:32 +00:00
public async Gdk.Pixbuf cover_art (string id, Cancellable cancellable) throws GLib.Error {
2024-10-13 17:00:47 +00:00
var msg = new Soup.Message("GET", this.cover_art_uri (id));
2024-10-12 17:53:13 +00:00
assert (msg != null);
2024-10-11 09:09:47 +00:00
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);
2024-10-10 20:04:55 +00:00
}
2024-10-25 22:00:20 +00:00
~Client () {
debug ("destroying subsonic client");
}
2024-10-06 11:21:53 +00:00
}