feat: Update the api crate
This commit is contained in:
@@ -4,7 +4,7 @@ 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);
|
||||
let mut jellyfin = JellyfinClient::new_with_config(config);
|
||||
jellyfin
|
||||
.authenticate_with_cached_token(".session")
|
||||
.await
|
||||
|
||||
@@ -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 {
|
||||
|
||||
129
api/src/lib.rs
129
api/src/lib.rs
@@ -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,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user