rust subsonic client basics
This commit is contained in:
parent
0320e12206
commit
dee7c2e396
8 changed files with 1126 additions and 20 deletions
1033
Cargo.lock
generated
1033
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -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"
|
||||||
|
|
|
@ -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");
|
||||||
|
|
||||||
|
|
|
@ -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
5
src/subsonic_vala.rs
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
mod client;
|
||||||
|
pub use client::Client;
|
||||||
|
|
||||||
|
mod song;
|
||||||
|
pub use song::Song;
|
|
@ -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(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue