rust subsonic client basics

This commit is contained in:
Erica Z 2024-10-31 13:16:42 +01:00
parent 0320e12206
commit dee7c2e396
8 changed files with 1126 additions and 20 deletions

1033
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -5,8 +5,13 @@ edition = "2021"
[dependencies] [dependencies]
adw = { version = "0.7.0", package = "libadwaita", features = ["v1_6"] } adw = { version = "0.7.0", package = "libadwaita", features = ["v1_6"] }
async-channel = "2.3.1"
gettext-rs = { version = "0.7.2", features = ["gettext-system"] } gettext-rs = { version = "0.7.2", features = ["gettext-system"] }
gtk = { version = "0.9.2", package = "gtk4", features = ["v4_16"] } gtk = { version = "0.9.2", package = "gtk4", features = ["v4_16"] }
reqwest = { version = "0.12.9", features = ["json"] }
serde = { version = "1.0.214", features = ["derive"] }
serde_json = "1.0.132"
tokio = { version = "1", features = ["rt-multi-thread"] }
[build-dependencies] [build-dependencies]
bindgen = "0.70.1" bindgen = "0.70.1"

View file

@ -11,6 +11,7 @@ pub mod playbin;
pub use playbin::Playbin; pub use playbin::Playbin;
pub mod subsonic; pub mod subsonic;
pub mod subsonic_vala;
use gettextrs::{bind_textdomain_codeset, bindtextdomain, setlocale, textdomain, LocaleCategory}; use gettextrs::{bind_textdomain_codeset, bindtextdomain, setlocale, textdomain, LocaleCategory};
use gtk::prelude::*; use gtk::prelude::*;
@ -25,6 +26,13 @@ pub mod mpv;
#[link(name = "soup-3.0")] #[link(name = "soup-3.0")]
extern "C" {} extern "C" {}
pub fn runtime() -> &'static tokio::runtime::Runtime {
static RUNTIME: std::sync::OnceLock<tokio::runtime::Runtime> = std::sync::OnceLock::new();
RUNTIME.get_or_init(|| {
tokio::runtime::Runtime::new().expect("could not initialize the tokio runtime")
})
}
fn main() -> glib::ExitCode { fn main() -> glib::ExitCode {
gio::resources_register_include!("audrey.gresource").expect("could not register resources"); gio::resources_register_include!("audrey.gresource").expect("could not register resources");

View file

@ -1,5 +1,87 @@
mod client; #[derive(Debug)]
pub use client::Client; pub enum Error {
// FIXME: can't even return url's ParseError directly.........
UrlParseError(String),
ReqwestError(reqwest::Error),
SubsonicError(SubsonicError),
}
mod song; impl From<reqwest::Error> for Error {
pub use song::Song; fn from(err: reqwest::Error) -> Self {
Self::ReqwestError(err)
}
}
#[derive(Debug, serde::Deserialize)]
pub struct SubsonicError {
pub code: u32,
pub message: String,
}
#[derive(Debug, serde::Deserialize)]
pub struct SubsonicResponse<T> {
#[serde(rename = "subsonic-response")]
inner: SubsonicResponseInner<T>,
}
#[derive(Debug, serde::Deserialize)]
#[serde(tag = "status")]
pub enum SubsonicResponseInner<T> {
#[serde(rename = "ok")]
Ok(T),
#[serde(rename = "failed")]
Failed { error: SubsonicError },
}
impl<T> SubsonicResponseInner<T> {
fn fixup(self) -> Result<T, Error> {
match self {
Self::Ok(t) => Ok(t),
Self::Failed { error } => Err(Error::SubsonicError(error)),
}
}
}
pub struct Client {
client: reqwest::Client,
base_url: reqwest::Url,
}
impl Client {
pub fn new(url: &str, username: &str, token: &str, salt: &str) -> Result<Self, Error> {
Ok(Client {
client: reqwest::Client::builder()
.user_agent("audrey/linux") // Audrey.Const.user_agent
.build()?,
base_url: reqwest::Url::parse_with_params(
url,
&[
("u", username),
("t", token),
("s", salt),
("v", "1.16.1"),
("c", "eu.callcc.audrey"),
("f", "json"),
],
)
.map_err(|err| Error::UrlParseError(err.to_string()))?,
})
}
pub async fn ping(&self) -> Result<(), Error> {
let mut url = self.base_url.clone();
url.path_segments_mut()
.expect("can't modify url path segments")
.extend(&["rest", "ping"]);
self.client
.get(url)
.send()
.await?
.error_for_status()?
.json::<SubsonicResponse<()>>()
.await?
.inner
.fixup()
}
}

5
src/subsonic_vala.rs Normal file
View file

@ -0,0 +1,5 @@
mod client;
pub use client::Client;
mod song;
pub use song::Song;

View file

@ -65,7 +65,10 @@ mod imp {
child.bind( child.bind(
item.position(), item.position(),
item.item().unwrap().downcast_ref::<crate::playbin::Song>().unwrap(), item.item()
.unwrap()
.downcast_ref::<crate::playbin::Song>()
.unwrap(),
); );
} }