feat: Update typegen enums and the UI

This commit is contained in:
uttarayan21
2025-11-14 12:05:07 +05:30
parent 94105cdb83
commit df06190c14
9 changed files with 61481 additions and 60426 deletions

View File

@@ -10,6 +10,9 @@ serde = { version = "1.0.228", features = ["derive"] }
serde_json = "1.0.145"
tap = "1.0.1"
thiserror = "2.0.17"
tokio = { version = "1.48.0", features = ["fs"] }
toml = "0.9.8"
[dev-dependencies]
tokio = { version = "1.48.0", features = ["macros", "rt-multi-thread"] }
tokio-test = "0.4.4"

16
api/examples/items.rs Normal file
View File

@@ -0,0 +1,16 @@
use api::*;
#[tokio::main]
pub async fn main() {
let config = std::fs::read_to_string("config.toml").expect("Config.toml");
let config: JellyfinConfig =
toml::from_str(&config).expect("Failed to parse config.toml");
let mut jellyfin = JellyfinClient::new(config);
jellyfin.authenticate_with_cached_token(".session").await.expect("Auth");
let items = jellyfin.raw_items().await.expect("Items");
std::fs::write(
"items.json",
serde_json::to_string_pretty(&items).expect("Serialize items"),
);
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,7 @@
pub mod jellyfin;
use ::tap::*;
use reqwest::Method;
use serde::{Deserialize, Serialize};
#[derive(thiserror::Error, Debug)]
@@ -9,6 +10,8 @@ pub enum JellyfinApiError {
ReqwestError(#[from] reqwest::Error),
#[error("Serialization/Deserialization error: {0}")]
SerdeError(#[from] serde_json::Error),
#[error("IO error: {0}")]
IoError(#[from] std::io::Error),
}
type Result<T, E = JellyfinApiError> = std::result::Result<T, E>;
@@ -29,9 +32,9 @@ impl JellyfinClient {
}
}
pub fn save_token(&self, path: impl AsRef<std::path::Path>) -> std::io::Result<()> {
pub async fn save_token(&self, path: impl AsRef<std::path::Path>) -> std::io::Result<()> {
if let Some(token) = &self.access_token {
std::fs::write(path, token)
tokio::fs::write(path, token).await
} else {
Err(std::io::Error::new(
std::io::ErrorKind::Other,
@@ -40,17 +43,20 @@ impl JellyfinClient {
}
}
pub fn load_token(&mut self, path: impl AsRef<std::path::Path>) -> std::io::Result<()> {
let token = std::fs::read_to_string(path)?;
pub async fn load_token(&mut self, path: impl AsRef<std::path::Path>) -> std::io::Result<()> {
let token = tokio::fs::read_to_string(path).await?;
self.access_token = Some(token);
Ok(())
}
pub fn post_builder(&self, uri: impl AsRef<str>) -> reqwest::RequestBuilder {
pub fn request_builder(
&self,
method: reqwest::Method,
uri: impl AsRef<str>,
) -> reqwest::RequestBuilder {
let url = format!("{}/{}", self.config.server_url.as_str(), uri.as_ref());
self.client.post(&url)
self.client.request(method, &url)
.header("X-Emby-Authorization", format!("MediaBrowser Client=\"Jello\", Device=\"Jello\", DeviceId=\"{}\", Version=\"1.0.0\"", self.config.device_id))
.header("Content-Type", "application/json")
.pipe(|builder| {
if let Some(token) = &self.access_token {
builder.header("X-MediaBrowser-Token", token)
@@ -60,18 +66,18 @@ impl JellyfinClient {
})
}
pub fn get_builder(&self, uri: impl AsRef<str>) -> reqwest::RequestBuilder {
let url = format!("{}/{}", self.config.server_url.as_str(), uri.as_ref());
self.client.get(&url)
.header("X-Emby-Authorization", format!("MediaBrowser Client=\"Jello\", Device=\"Jello\", DeviceId=\"{}\", Version=\"1.0.0\"", self.config.device_id))
.pipe(|builder| {
if let Some(token) = &self.access_token {
builder.header("X-MediaBrowser-Token", token)
} else {
builder
}
})
}
// pub fn get_builder(&self, uri: impl AsRef<str>) -> reqwest::RequestBuilder {
// let url = format!("{}/{}", self.config.server_url.as_str(), uri.as_ref());
// self.client.get(&url)
// .header("X-Emby-Authorization", format!("MediaBrowser Client=\"Jello\", Device=\"Jello\", DeviceId=\"{}\", Version=\"1.0.0\"", self.config.device_id))
// .pipe(|builder| {
// if let Some(token) = &self.access_token {
// builder.header("X-MediaBrowser-Token", token)
// } else {
// builder
// }
// })
// }
pub async fn post<T: Serialize + ?Sized, U: serde::de::DeserializeOwned>(
&self,
@@ -79,7 +85,7 @@ impl JellyfinClient {
body: &T,
) -> Result<U> {
let text = self
.post_builder(uri)
.request_builder(reqwest::Method::POST, uri)
.json(body)
.send()
.await?
@@ -92,7 +98,7 @@ impl JellyfinClient {
pub async fn get<U: serde::de::DeserializeOwned>(&self, uri: impl AsRef<str>) -> Result<U> {
let text = self
.get_builder(uri)
.request_builder(reqwest::Method::GET, uri)
.send()
.await?
.error_for_status()?
@@ -103,25 +109,34 @@ impl JellyfinClient {
}
pub async fn authenticate(&mut self) -> Result<jellyfin::AuthenticationResult> {
let out = self
.post_builder("Users/AuthenticateByName")
.json(&jellyfin::AuthenticateUserByName {
username: Some(self.config.username.clone()),
pw: Some(self.config.password.clone()),
})
.send()
.await?
.error_for_status()?
.text()
let auth_result: jellyfin::AuthenticationResult = self
.post(
"Users/AuthenticateByName",
&jellyfin::AuthenticateUserByName {
username: Some(self.config.username.clone()),
pw: Some(self.config.password.clone()),
},
)
.await?;
let auth_result: jellyfin::AuthenticationResult = serde_json::from_str(&out)?;
self.access_token = auth_result.access_token.clone();
Ok(auth_result)
}
async fn items(&self) -> Result<jellyfin::BaseItemDtoQueryResult> {
pub async fn authenticate_with_cached_token(
&mut self,
path: impl AsRef<std::path::Path>,
) -> Result<()> {
let path = path.as_ref();
if !self.load_token(path).await.is_ok() {
self.authenticate().await?;
self.save_token(path).await?;
}
Ok(())
}
pub async fn raw_items(&self) -> Result<jellyfin::BaseItemDtoQueryResult> {
let text = &self
.get_builder("/Items")
.request_builder(Method::GET, "Items")
.send()
.await?
.error_for_status()?
@@ -130,8 +145,31 @@ impl JellyfinClient {
let out: jellyfin::BaseItemDtoQueryResult = serde_json::from_str(&text)?;
Ok(out)
}
pub async fn items(
&self,
root: impl Into<Option<String>>,
) -> Result<Vec<jellyfin::BaseItemDto>> {
let text = &self
.request_builder(Method::GET, "Items")
.query(&[("parentId", root.into())])
.send()
.await?
.error_for_status()?
.text()
.await?;
let out: jellyfin::BaseItemDtoQueryResult = serde_json::from_str(&text)?;
Ok(out.items)
}
}
// pub trait Item {
// fn id(&self) -> &str;
// fn name(&self) -> &str;
// fn type_(&self) -> jellyfin::BaseItemKind;
// fn media_type(&self) -> &str;
// }
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct JellyfinConfig {
pub username: String,