feat: Update the api crate

This commit is contained in:
uttarayan21
2025-11-25 18:48:13 +05:30
parent 77fe7b6bb4
commit ca1fd2e977
9 changed files with 229 additions and 260 deletions

View File

@@ -77,7 +77,7 @@ pub struct ActivityLogEntryQueryResult {
#[serde(rename = "StartIndex")]
pub start_index: i32,
}
/** Activity log entry start message.
/** Activity log entry start message.
Data is the timing data encoded as "$initialDelay,$interval" in ms.*/
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct ActivityLogEntryStartMessage {
@@ -290,7 +290,7 @@ pub struct AuthenticationResult {
#[serde(rename = "ServerId")]
pub server_id: Option<String>,
}
/** This is strictly used as a data transfer object from the api layer.
/** This is strictly used as a data transfer object from the api layer.
This holds information about a BaseItem in a format that is convenient for the client.*/
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct BaseItemDto {
@@ -565,8 +565,8 @@ pub struct BaseItemDto {
/// Gets or sets the series thumb image tag.
#[serde(rename = "SeriesThumbImageTag")]
pub series_thumb_image_tag: Option<String>,
/** Gets or sets the blurhashes for the image tags.
Maps image type to dictionary mapping image tag to blurhash value.*/
/** Gets or sets the blurhashes for the image tags.
Maps image type to dictionary mapping image tag to blurhash value.*/
#[serde(rename = "ImageBlurHashes")]
pub image_blur_hashes: BaseItemDtoImageBlurHashes,
/// Gets or sets the series studio.
@@ -590,10 +590,7 @@ Maps image type to dictionary mapping image tag to blurhash value.*/
/// Gets or sets the trickplay manifest.
#[serde(rename = "Trickplay")]
pub trickplay: Option<
std::collections::HashMap<
String,
Option<std::collections::HashMap<String, TrickplayInfo>>,
>,
std::collections::HashMap<String, Option<std::collections::HashMap<String, TrickplayInfo>>>,
>,
/// Gets or sets the type of the location.
#[serde(rename = "LocationType")]
@@ -724,7 +721,7 @@ Maps image type to dictionary mapping image tag to blurhash value.*/
#[serde(rename = "CurrentProgram")]
pub current_program: Option<Box<BaseItemDto>>,
}
/** Gets or sets the blurhashes for the image tags.
/** Gets or sets the blurhashes for the image tags.
Maps image type to dictionary mapping image tag to blurhash value.*/
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct BaseItemDtoImageBlurHashes {
@@ -1278,11 +1275,11 @@ pub struct DeviceOptionsDto {
#[serde(rename = "CustomName")]
pub custom_name: Option<String>,
}
/** A MediaBrowser.Model.Dlna.DeviceProfile represents a set of metadata which determines which content a certain device is able to play.
<br />
Specifically, it defines the supported <see cref="P:MediaBrowser.Model.Dlna.DeviceProfile.ContainerProfiles">containers</see> and
<see cref="P:MediaBrowser.Model.Dlna.DeviceProfile.CodecProfiles">codecs</see> (video and/or audio, including codec profiles and levels)
the device is able to direct play (without transcoding or remuxing),
/** A MediaBrowser.Model.Dlna.DeviceProfile represents a set of metadata which determines which content a certain device is able to play.
<br />
Specifically, it defines the supported <see cref="P:MediaBrowser.Model.Dlna.DeviceProfile.ContainerProfiles">containers</see> and
<see cref="P:MediaBrowser.Model.Dlna.DeviceProfile.CodecProfiles">codecs</see> (video and/or audio, including codec profiles and levels)
the device is able to direct play (without transcoding or remuxing),
as well as which <see cref="P:MediaBrowser.Model.Dlna.DeviceProfile.TranscodingProfiles">containers/codecs to transcode to</see> in case it isn't.*/
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct DeviceProfile {
@@ -1525,9 +1522,7 @@ pub struct EncodingOptions {
pub hardware_decoding_codecs: Option<Vec<String>>,
/// Gets or sets the file extensions on-demand metadata based keyframe extraction is enabled for.
#[serde(rename = "AllowOnDemandMetadataBasedKeyframeExtractionForExtensions")]
pub allow_on_demand_metadata_based_keyframe_extraction_for_extensions: Option<
Vec<String>,
>,
pub allow_on_demand_metadata_based_keyframe_extraction_for_extensions: Option<Vec<String>>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct EndPointInfo {
@@ -1545,10 +1540,10 @@ pub struct ExternalIdInfo {
/// Gets or sets the unique key for this id. This key should be unique across all providers.
#[serde(rename = "Key")]
pub key: String,
/** Gets or sets the specific media type for this id. This is used to distinguish between the different
external id types for providers with multiple ids.
A null value indicates there is no specific media type associated with the external id, or this is the
default id for the external provider so there is no need to specify a type.*/
/** Gets or sets the specific media type for this id. This is used to distinguish between the different
external id types for providers with multiple ids.
A null value indicates there is no specific media type associated with the external id, or this is the
default id for the external provider so there is no need to specify a type.*/
#[serde(rename = "Type")]
pub _type: Option<ExternalIdMediaType>,
/// Gets or sets the URL format string.
@@ -2443,8 +2438,8 @@ pub struct MediaSourceInfo {
pub size: Option<i64>,
#[serde(rename = "Name")]
pub name: Option<String>,
/** Gets or sets a value indicating whether the media is remote.
Differentiate internet url vs local network.*/
/** Gets or sets a value indicating whether the media is remote.
Differentiate internet url vs local network.*/
#[serde(rename = "IsRemote")]
pub is_remote: bool,
#[serde(rename = "ETag")]
@@ -2505,8 +2500,8 @@ Differentiate internet url vs local network.*/
pub required_http_headers: Option<std::collections::HashMap<String, Option<String>>>,
#[serde(rename = "TranscodingUrl")]
pub transcoding_url: Option<String>,
/** Media streaming protocol.
Lowercase for backwards compatibility.*/
/** Media streaming protocol.
Lowercase for backwards compatibility.*/
#[serde(rename = "TranscodingSubProtocol")]
pub transcoding_sub_protocol: MediaStreamProtocol,
#[serde(rename = "TranscodingContainer")]
@@ -2656,9 +2651,9 @@ pub struct MediaStream {
/// Gets or sets the real frame rate.
#[serde(rename = "RealFrameRate")]
pub real_frame_rate: Option<f32>,
/** Gets the framerate used as reference.
Prefer AverageFrameRate, if that is null or an unrealistic value
then fallback to RealFrameRate.*/
/** Gets the framerate used as reference.
Prefer AverageFrameRate, if that is null or an unrealistic value
then fallback to RealFrameRate.*/
#[serde(rename = "ReferenceFrameRate")]
pub reference_frame_rate: Option<f32>,
/// Gets or sets the profile.
@@ -2719,8 +2714,8 @@ pub struct MediaUpdateInfoPathDto {
/// Gets or sets media path.
#[serde(rename = "Path")]
pub path: Option<String>,
/** Gets or sets media update type.
Created, Modified, Deleted.*/
/** Gets or sets media update type.
Created, Modified, Deleted.*/
#[serde(rename = "UpdateType")]
pub update_type: Option<String>,
}
@@ -2968,8 +2963,8 @@ pub struct NetworkConfiguration {
/// Gets or sets a value indicating whether the published server uri is based on information in HTTP requests.
#[serde(rename = "EnablePublishedServerUriByRequest")]
pub enable_published_server_uri_by_request: bool,
/** Gets or sets the PublishedServerUriBySubnet
Gets or sets PublishedServerUri to advertise for specific subnets.*/
/** Gets or sets the PublishedServerUriBySubnet
Gets or sets PublishedServerUri to advertise for specific subnets.*/
#[serde(rename = "PublishedServerUriBySubnet")]
pub published_server_uri_by_subnet: Vec<String>,
/// Gets or sets the filter for remote IP connectivity. Used in conjunction with <seealso cref="P:MediaBrowser.Common.Net.NetworkConfiguration.IsRemoteIPFilterBlacklist" />.
@@ -3032,12 +3027,12 @@ pub struct OpenLiveStreamDto {
/// Gets or sets a value indicating whether always burn in subtitles when transcoding.
#[serde(rename = "AlwaysBurnInSubtitleWhenTranscoding")]
pub always_burn_in_subtitle_when_transcoding: Option<bool>,
/** A MediaBrowser.Model.Dlna.DeviceProfile represents a set of metadata which determines which content a certain device is able to play.
<br />
Specifically, it defines the supported <see cref="P:MediaBrowser.Model.Dlna.DeviceProfile.ContainerProfiles">containers</see> and
<see cref="P:MediaBrowser.Model.Dlna.DeviceProfile.CodecProfiles">codecs</see> (video and/or audio, including codec profiles and levels)
the device is able to direct play (without transcoding or remuxing),
as well as which <see cref="P:MediaBrowser.Model.Dlna.DeviceProfile.TranscodingProfiles">containers/codecs to transcode to</see> in case it isn't.*/
/** A MediaBrowser.Model.Dlna.DeviceProfile represents a set of metadata which determines which content a certain device is able to play.
<br />
Specifically, it defines the supported <see cref="P:MediaBrowser.Model.Dlna.DeviceProfile.ContainerProfiles">containers</see> and
<see cref="P:MediaBrowser.Model.Dlna.DeviceProfile.CodecProfiles">codecs</see> (video and/or audio, including codec profiles and levels)
the device is able to direct play (without transcoding or remuxing),
as well as which <see cref="P:MediaBrowser.Model.Dlna.DeviceProfile.TranscodingProfiles">containers/codecs to transcode to</see> in case it isn't.*/
#[serde(rename = "DeviceProfile")]
pub device_profile: Option<DeviceProfile>,
/// Gets or sets the device play protocols.
@@ -3072,8 +3067,8 @@ pub struct PackageInfo {
/// Gets or sets the category.
#[serde(rename = "category")]
pub category: String,
/** Gets or sets the guid of the assembly associated with this plugin.
This is used to identify the proper item for automatic updates.*/
/** Gets or sets the guid of the assembly associated with this plugin.
This is used to identify the proper item for automatic updates.*/
#[serde(rename = "guid")]
pub guid: uuid::Uuid,
/// Gets or sets the versions.
@@ -3191,12 +3186,12 @@ pub struct PlaybackInfoDto {
/// Gets or sets the live stream id.
#[serde(rename = "LiveStreamId")]
pub live_stream_id: Option<String>,
/** A MediaBrowser.Model.Dlna.DeviceProfile represents a set of metadata which determines which content a certain device is able to play.
<br />
Specifically, it defines the supported <see cref="P:MediaBrowser.Model.Dlna.DeviceProfile.ContainerProfiles">containers</see> and
<see cref="P:MediaBrowser.Model.Dlna.DeviceProfile.CodecProfiles">codecs</see> (video and/or audio, including codec profiles and levels)
the device is able to direct play (without transcoding or remuxing),
as well as which <see cref="P:MediaBrowser.Model.Dlna.DeviceProfile.TranscodingProfiles">containers/codecs to transcode to</see> in case it isn't.*/
/** A MediaBrowser.Model.Dlna.DeviceProfile represents a set of metadata which determines which content a certain device is able to play.
<br />
Specifically, it defines the supported <see cref="P:MediaBrowser.Model.Dlna.DeviceProfile.ContainerProfiles">containers</see> and
<see cref="P:MediaBrowser.Model.Dlna.DeviceProfile.CodecProfiles">codecs</see> (video and/or audio, including codec profiles and levels)
the device is able to direct play (without transcoding or remuxing),
as well as which <see cref="P:MediaBrowser.Model.Dlna.DeviceProfile.TranscodingProfiles">containers/codecs to transcode to</see> in case it isn't.*/
#[serde(rename = "DeviceProfile")]
pub device_profile: Option<DeviceProfile>,
/// Gets or sets a value indicating whether to enable direct play.
@@ -4041,7 +4036,7 @@ pub struct ScheduledTasksInfoMessage {
#[serde(rename = "MessageType")]
pub message_type: SessionMessageType,
}
/** Scheduled tasks info start message.
/** Scheduled tasks info start message.
Data is the timing data encoded as "$initialDelay,$interval" in ms.*/
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct ScheduledTasksInfoStartMessage {
@@ -4392,8 +4387,8 @@ pub struct ServerConfiguration {
/// Gets or sets the last known version that was ran using the configuration.
#[serde(rename = "PreviousVersion")]
pub previous_version: Option<String>,
/** Gets or sets the stringified PreviousVersion to be stored/loaded,
because System.Version itself isn't xml-serializable.*/
/** Gets or sets the stringified PreviousVersion to be stored/loaded,
because System.Version itself isn't xml-serializable.*/
#[serde(rename = "PreviousVersionStr")]
pub previous_version_str: Option<String>,
/// Gets or sets a value indicating whether to enable prometheus metrics exporting.
@@ -4445,13 +4440,13 @@ because System.Version itself isn't xml-serializable.*/
/// Gets or sets the remaining minutes of a book that can be played while still saving playstate. If this percentage is crossed playstate will be reset to the beginning and the item will be marked watched.
#[serde(rename = "MaxAudiobookResume")]
pub max_audiobook_resume: i32,
/** Gets or sets the threshold in minutes after a inactive session gets closed automatically.
If set to 0 the check for inactive sessions gets disabled.*/
/** Gets or sets the threshold in minutes after a inactive session gets closed automatically.
If set to 0 the check for inactive sessions gets disabled.*/
#[serde(rename = "InactiveSessionThreshold")]
pub inactive_session_threshold: i32,
/** Gets or sets the delay in seconds that we will wait after a file system change to try and discover what has been added/removed
Some delay is necessary with some items because their creation is not atomic. It involves the creation of several
different directories and files.*/
/** Gets or sets the delay in seconds that we will wait after a file system change to try and discover what has been added/removed
Some delay is necessary with some items because their creation is not atomic. It involves the creation of several
different directories and files.*/
#[serde(rename = "LibraryMonitorDelay")]
pub library_monitor_delay: i32,
/// Gets or sets the duration in seconds that we will wait after a library updated event before executing the library changed notification.
@@ -4670,7 +4665,7 @@ pub struct SessionsMessage {
#[serde(rename = "MessageType")]
pub message_type: SessionMessageType,
}
/** Sessions start message.
/** Sessions start message.
Data is the timing data encoded as "$initialDelay,$interval" in ms.*/
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct SessionsStartMessage {
@@ -5329,7 +5324,7 @@ pub struct TranscodingInfo {
#[serde(rename = "TranscodeReasons")]
pub transcode_reasons: Vec<TranscodeReason>,
}
/** A class for transcoding profile information.
/** A class for transcoding profile information.
Note for client developers: Conditions defined in MediaBrowser.Model.Dlna.CodecProfile has higher priority and can override values defined here.*/
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct TranscodingProfile {
@@ -5419,8 +5414,8 @@ pub struct TrickplayOptions {
/// Gets or sets a value indicating whether or not to use HW accelerated MJPEG encoding.
#[serde(rename = "EnableHwEncoding")]
pub enable_hw_encoding: bool,
/** Gets or sets a value indicating whether to only extract key frames.
Significantly faster, but is not compatible with all decoders and/or video files.*/
/** Gets or sets a value indicating whether to only extract key frames.
Significantly faster, but is not compatible with all decoders and/or video files.*/
#[serde(rename = "EnableKeyFrameOnlyExtraction")]
pub enable_key_frame_only_extraction: bool,
/// Gets or sets the behavior used by trickplay provider on library scan/update.
@@ -5711,8 +5706,8 @@ pub struct UserDto {
/// Gets or sets the server identifier.
#[serde(rename = "ServerId")]
pub server_id: Option<String>,
/** Gets or sets the name of the server.
This is not used by the server and is for client-side usage only.*/
/** Gets or sets the name of the server.
This is not used by the server and is for client-side usage only.*/
#[serde(rename = "ServerName")]
pub server_name: Option<String>,
/// Gets or sets the id.
@@ -7014,7 +7009,7 @@ pub enum MediaSourceType {
#[serde(rename = "Placeholder")]
Placeholder,
}
/** Media streaming protocol.
/** Media streaming protocol.
Lowercase for backwards compatibility.*/
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub enum MediaStreamProtocol {

View File

@@ -3,7 +3,7 @@ pub mod jellyfin;
use std::sync::Arc;
use ::tap::*;
use reqwest::Method;
use reqwest::{Method, header::InvalidHeaderValue};
use serde::{Deserialize, Serialize};
#[derive(thiserror::Error, Debug)]
@@ -15,6 +15,8 @@ pub enum JellyfinApiError {
#[error("IO error: {0}")]
IoError(#[from] std::io::Error),
#[error("Unknown Jellyfin API error")]
InvalidHeader(#[from] InvalidHeaderValue),
#[error("Unknown Jellyfin API error")]
Unknown,
}
@@ -28,7 +30,49 @@ pub struct JellyfinClient {
}
impl JellyfinClient {
pub fn new(config: JellyfinConfig) -> Self {
pub async fn authenticate(
username: impl AsRef<str>,
password: impl AsRef<str>,
config: JellyfinConfig,
) -> Result<Self> {
let url = format!("{}/Users/AuthenticateByName", config.server_url);
let client = reqwest::Client::new();
let token = client
.post(url)
.json(&jellyfin::AuthenticateUserByName {
username: Some(username.as_ref().to_string()),
pw: Some(password.as_ref().to_string()),
})
.send()
.await?
.error_for_status()?
.json::<jellyfin::AuthenticationResult>()
.await?
.access_token
.ok_or_else(|| std::io::Error::other("No field access_token in auth response"))?;
Self::pre_authenticated(token, config)
}
pub fn pre_authenticated(token: impl AsRef<str>, config: JellyfinConfig) -> Result<Self> {
let auth_header = core::iter::once((
reqwest::header::HeaderName::from_static("X-Emby-Authorization"),
reqwest::header::HeaderValue::from_str(&format!(
"MediaBrowser Client=\"{}\", Device=\"{}\", DeviceId=\"{}\", Version=\"{}\"",
config.client_name, config.device_name, config.device_id, config.version
))?,
))
.collect();
let client = reqwest::Client::builder()
.default_headers(auth_header)
.build()?;
Ok(Self {
client,
access_token: Some(token.as_ref().to_string().into()),
config: Arc::new(config),
})
}
pub fn new_with_config(config: JellyfinConfig) -> Self {
JellyfinClient {
client: reqwest::Client::new(),
access_token: None,
@@ -119,45 +163,6 @@ impl JellyfinClient {
Ok(out)
}
pub async fn authenticate(&mut self) -> Result<jellyfin::AuthenticationResult> {
let auth_result: jellyfin::AuthenticationResult = self
.post(
"Users/AuthenticateByName",
&jellyfin::AuthenticateUserByName {
username: Some(self.config.username.clone()),
pw: Some(self.config.password.clone()),
},
)
.await?;
self.access_token = auth_result.access_token.clone().map(Into::into);
Ok(auth_result)
}
pub async fn authenticate_with_cached_token(
&mut self,
path: impl AsRef<std::path::Path>,
) -> Result<String> {
let path = path.as_ref();
if let Ok(token) = self
.load_token(path)
.await
.inspect_err(|err| tracing::warn!("Failed to load cached token: {}", err))
{
tracing::info!("Authenticating with cached token from {:?}", path);
self.access_token = Some(token.clone().into());
Ok(token)
} else {
tracing::info!("No cached token found at {:?}, authenticating...", path);
let token = self
.authenticate()
.await?
.access_token
.ok_or_else(|| JellyfinApiError::Unknown)?;
self.save_token(path).await?;
Ok(token)
}
}
pub async fn raw_items(&self) -> Result<jellyfin::BaseItemDtoQueryResult> {
let text = &self
.request_builder(Method::GET, "Items")
@@ -256,47 +261,11 @@ impl JellyfinClient {
}
}
// 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,
pub password: String,
pub server_url: iref::IriBuf,
pub device_id: String,
}
impl JellyfinConfig {
pub fn new(
username: String,
password: String,
server_url: impl AsRef<str>,
device_id: String,
) -> Self {
JellyfinConfig {
username,
password,
server_url: iref::IriBuf::new(server_url.as_ref().into())
.expect("Failed to parse server URL"),
device_id,
}
}
}
#[test]
fn test_client_authenticate() {
let config = JellyfinConfig {
username: "servius".to_string(),
password: "nfz6yqr_NZD1nxk!faj".to_string(),
server_url: iref::IriBuf::new("https://jellyfin.tsuba.darksailor.dev".into()).unwrap(),
device_id: "testdeviceid".to_string(),
};
let mut client = JellyfinClient::new(config);
let auth_result = tokio_test::block_on(client.authenticate());
assert!(auth_result.is_ok());
pub device_name: String,
pub client_name: String,
pub version: String,
}