diff --git a/Cargo.lock b/Cargo.lock index 2f5f000..c9ce27b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1009,9 +1009,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.46" +version = "1.2.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97463e1064cb1b1c1384ad0a0b9c8abd0988e2a91f52606c80ef14aadb63e36" +checksum = "cd405d82c84ff7f35739f175f67d8b9fb7687a0e84ccdc78bd3568839827cf07" dependencies = [ "find-msvc-tools", "jobserver", @@ -1125,9 +1125,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.52" +version = "4.5.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa8120877db0e5c011242f96806ce3c94e0737ab8108532a76a3300a01db2ab8" +checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8" dependencies = [ "clap_builder", "clap_derive", @@ -1135,9 +1135,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.52" +version = "4.5.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02576b399397b659c26064fbc92a75fede9d18ffd5f80ca1cd74ddab167016e1" +checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00" dependencies = [ "anstream", "anstyle", @@ -1147,9 +1147,9 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.5.60" +version = "4.5.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e602857739c5a4291dfa33b5a298aeac9006185229a700e5810a3ef7272d971" +checksum = "39615915e2ece2550c0149addac32fb5bd312c657f43845bb9088cb9c8a7c992" dependencies = [ "clap", ] @@ -2457,15 +2457,15 @@ dependencies = [ [[package]] name = "gio-sys" -version = "0.20.10" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "521e93a7e56fc89e84aea9a52cfc9436816a4b363b030260b699950ff1336c83" +checksum = "171ed2f6dd927abbe108cfd9eebff2052c335013f5879d55bab0dc1dee19b706" dependencies = [ "glib-sys", "gobject-sys", "libc", "system-deps", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -2487,9 +2487,9 @@ checksum = "151665d9be52f9bb40fc7966565d39666f2d1e69233571b71b87791c7e0528b3" [[package]] name = "glib" -version = "0.20.12" +version = "0.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffc4b6e352d4716d84d7dde562dd9aee2a7d48beb872dd9ece7f2d1515b2d683" +checksum = "5b9dbecb1c33e483a98be4acfea2ab369e1c28f517c6eadb674537409c25c4b2" dependencies = [ "bitflags 2.10.0", "futures-channel", @@ -2508,9 +2508,9 @@ dependencies = [ [[package]] name = "glib-macros" -version = "0.20.12" +version = "0.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8084af62f09475a3f529b1629c10c429d7600ee1398ae12dd3bf175d74e7145" +checksum = "880e524e0085f3546cfb38532b2c202c0d64741d9977a6e4aa24704bfc9f19fb" dependencies = [ "heck 0.5.0", "proc-macro-crate", @@ -2521,9 +2521,9 @@ dependencies = [ [[package]] name = "glib-sys" -version = "0.20.10" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ab79e1ed126803a8fb827e3de0e2ff95191912b8db65cee467edb56fc4cc215" +checksum = "d09d3d0fddf7239521674e57b0465dfbd844632fec54f059f7f56112e3f927e1" dependencies = [ "libc", "system-deps", @@ -2583,9 +2583,9 @@ dependencies = [ [[package]] name = "gobject-sys" -version = "0.20.10" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec9aca94bb73989e3cfdbf8f2e0f1f6da04db4d291c431f444838925c4c63eda" +checksum = "538e41d8776173ec107e7b0f2aceced60abc368d7e1d81c1f0e2ecd35f59080d" dependencies = [ "glib-sys", "libc", @@ -2916,9 +2916,9 @@ checksum = "12101ecc8225ea6d675bc70263074eab6169079621c2186fe0c66590b2df9681" [[package]] name = "gstreamer" -version = "0.23.7" +version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8757a87f3706560037a01a9f06a59fcc7bdb0864744dcf73546606e60c4316e1" +checksum = "69ac2f12970a2f85a681d2ceaa40c32fe86cc202ead315e0dfa2223a1217cd24" dependencies = [ "cfg-if", "futures-channel", @@ -2927,13 +2927,13 @@ dependencies = [ "glib", "gstreamer-sys", "itertools 0.14.0", + "kstring", "libc", "muldiv", "num-integer", "num-rational", - "once_cell", "option-operations", - "paste", + "pastey", "pin-project-lite", "smallvec", "thiserror 2.0.17", @@ -2941,9 +2941,9 @@ dependencies = [ [[package]] name = "gstreamer-app" -version = "0.23.5" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e9a883eb21aebcf1289158225c05f7aea5da6ecf71fa7f0ff1ce4d25baf004e" +checksum = "0af5d403738faf03494dfd502d223444b4b44feb997ba28ab3f118ee6d40a0b2" dependencies = [ "futures-core", "futures-sink", @@ -2956,9 +2956,9 @@ dependencies = [ [[package]] name = "gstreamer-app-sys" -version = "0.23.5" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94f7ef838306fe51852d503a14dc79ac42de005a59008a05098de3ecdaf05455" +checksum = "aaf1a3af017f9493c34ccc8439cbce5c48f6ddff6ec0514c23996b374ff25f9a" dependencies = [ "glib-sys", "gstreamer-base-sys", @@ -2969,9 +2969,9 @@ dependencies = [ [[package]] name = "gstreamer-base" -version = "0.23.6" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f19a74fd04ffdcb847dd322640f2cf520897129d00a7bcb92fd62a63f3e27404" +checksum = "71ff9b0bbc8041f0c6c8a53b206a6542f86c7d9fa8a7dff3f27d9c374d9f39b4" dependencies = [ "atomic_refcell", "cfg-if", @@ -2983,9 +2983,9 @@ dependencies = [ [[package]] name = "gstreamer-base-sys" -version = "0.23.6" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f2fb0037b6d3c5b51f60dea11e667910f33be222308ca5a101450018a09840" +checksum = "fed78852b92db1459b8f4288f86e6530274073c20be2f94ba642cddaca08b00e" dependencies = [ "glib-sys", "gobject-sys", @@ -2996,10 +2996,11 @@ dependencies = [ [[package]] name = "gstreamer-sys" -version = "0.23.6" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "feea73b4d92dbf9c24a203c9cd0bcc740d584f6b5960d5faf359febf288919b2" +checksum = "a24ae2930e683665832a19ef02466094b09d1f2da5673f001515ed5486aa9377" dependencies = [ + "cfg-if", "glib-sys", "gobject-sys", "libc", @@ -3071,9 +3072,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.16.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" dependencies = [ "foldhash 0.2.0", ] @@ -3276,7 +3277,7 @@ dependencies = [ [[package]] name = "iced" version = "0.14.0-dev" -source = "git+https://github.com/iced-rs/iced#645643bfd63ed4c01aa281f97992e3c276e71498" +source = "git+https://github.com/iced-rs/iced#8bfd099c5929d927a3fdde666d4c645d0bd83cb7" dependencies = [ "iced_core", "iced_debug", @@ -3293,7 +3294,7 @@ dependencies = [ [[package]] name = "iced_beacon" version = "0.14.0-dev" -source = "git+https://github.com/iced-rs/iced#645643bfd63ed4c01aa281f97992e3c276e71498" +source = "git+https://github.com/iced-rs/iced#8bfd099c5929d927a3fdde666d4c645d0bd83cb7" dependencies = [ "bincode", "futures", @@ -3308,7 +3309,7 @@ dependencies = [ [[package]] name = "iced_core" version = "0.14.0-dev" -source = "git+https://github.com/iced-rs/iced#645643bfd63ed4c01aa281f97992e3c276e71498" +source = "git+https://github.com/iced-rs/iced#8bfd099c5929d927a3fdde666d4c645d0bd83cb7" dependencies = [ "bitflags 2.10.0", "bytes", @@ -3326,7 +3327,7 @@ dependencies = [ [[package]] name = "iced_debug" version = "0.14.0-dev" -source = "git+https://github.com/iced-rs/iced#645643bfd63ed4c01aa281f97992e3c276e71498" +source = "git+https://github.com/iced-rs/iced#8bfd099c5929d927a3fdde666d4c645d0bd83cb7" dependencies = [ "iced_beacon", "iced_core", @@ -3337,7 +3338,7 @@ dependencies = [ [[package]] name = "iced_devtools" version = "0.14.0-dev" -source = "git+https://github.com/iced-rs/iced#645643bfd63ed4c01aa281f97992e3c276e71498" +source = "git+https://github.com/iced-rs/iced#8bfd099c5929d927a3fdde666d4c645d0bd83cb7" dependencies = [ "iced_debug", "iced_program", @@ -3348,7 +3349,7 @@ dependencies = [ [[package]] name = "iced_futures" version = "0.14.0-dev" -source = "git+https://github.com/iced-rs/iced#645643bfd63ed4c01aa281f97992e3c276e71498" +source = "git+https://github.com/iced-rs/iced#8bfd099c5929d927a3fdde666d4c645d0bd83cb7" dependencies = [ "futures", "iced_core", @@ -3362,7 +3363,7 @@ dependencies = [ [[package]] name = "iced_graphics" version = "0.14.0-dev" -source = "git+https://github.com/iced-rs/iced#645643bfd63ed4c01aa281f97992e3c276e71498" +source = "git+https://github.com/iced-rs/iced#8bfd099c5929d927a3fdde666d4c645d0bd83cb7" dependencies = [ "bitflags 2.10.0", "bytemuck", @@ -3383,7 +3384,7 @@ dependencies = [ [[package]] name = "iced_program" version = "0.14.0-dev" -source = "git+https://github.com/iced-rs/iced#645643bfd63ed4c01aa281f97992e3c276e71498" +source = "git+https://github.com/iced-rs/iced#8bfd099c5929d927a3fdde666d4c645d0bd83cb7" dependencies = [ "iced_graphics", "iced_runtime", @@ -3392,7 +3393,7 @@ dependencies = [ [[package]] name = "iced_renderer" version = "0.14.0-dev" -source = "git+https://github.com/iced-rs/iced#645643bfd63ed4c01aa281f97992e3c276e71498" +source = "git+https://github.com/iced-rs/iced#8bfd099c5929d927a3fdde666d4c645d0bd83cb7" dependencies = [ "iced_graphics", "iced_tiny_skia", @@ -3404,7 +3405,7 @@ dependencies = [ [[package]] name = "iced_runtime" version = "0.14.0-dev" -source = "git+https://github.com/iced-rs/iced#645643bfd63ed4c01aa281f97992e3c276e71498" +source = "git+https://github.com/iced-rs/iced#8bfd099c5929d927a3fdde666d4c645d0bd83cb7" dependencies = [ "bytes", "iced_core", @@ -3417,7 +3418,7 @@ dependencies = [ [[package]] name = "iced_tiny_skia" version = "0.14.0-dev" -source = "git+https://github.com/iced-rs/iced#645643bfd63ed4c01aa281f97992e3c276e71498" +source = "git+https://github.com/iced-rs/iced#8bfd099c5929d927a3fdde666d4c645d0bd83cb7" dependencies = [ "bytemuck", "cosmic-text 0.15.0", @@ -3441,14 +3442,14 @@ dependencies = [ "iced", "iced_wgpu", "log", - "thiserror 1.0.69", + "thiserror 2.0.17", "url", ] [[package]] name = "iced_wgpu" version = "0.14.0-dev" -source = "git+https://github.com/iced-rs/iced#645643bfd63ed4c01aa281f97992e3c276e71498" +source = "git+https://github.com/iced-rs/iced#8bfd099c5929d927a3fdde666d4c645d0bd83cb7" dependencies = [ "bitflags 2.10.0", "bytemuck", @@ -3468,7 +3469,7 @@ dependencies = [ [[package]] name = "iced_widget" version = "0.14.0-dev" -source = "git+https://github.com/iced-rs/iced#645643bfd63ed4c01aa281f97992e3c276e71498" +source = "git+https://github.com/iced-rs/iced#8bfd099c5929d927a3fdde666d4c645d0bd83cb7" dependencies = [ "iced_renderer", "log", @@ -3481,7 +3482,7 @@ dependencies = [ [[package]] name = "iced_winit" version = "0.14.0-dev" -source = "git+https://github.com/iced-rs/iced#645643bfd63ed4c01aa281f97992e3c276e71498" +source = "git+https://github.com/iced-rs/iced#8bfd099c5929d927a3fdde666d4c645d0bd83cb7" dependencies = [ "iced_debug", "iced_program", @@ -3646,12 +3647,12 @@ checksum = "e7c5cedc30da3a610cac6b4ba17597bdf7152cf974e8aab3afb3d54455e371c8" [[package]] name = "indexmap" -version = "2.12.0" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f" +checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" dependencies = [ "equivalent", - "hashbrown 0.16.0", + "hashbrown 0.16.1", "serde", "serde_core", ] @@ -3930,6 +3931,15 @@ version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" +[[package]] +name = "kstring" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "558bf9508a558512042d3095138b1f7b8fe90c5467d94f9f1da28b3731c5dbd1" +dependencies = [ + "static_assertions", +] + [[package]] name = "kurbo" version = "0.10.4" @@ -4402,7 +4412,7 @@ dependencies = [ "cfg_aliases", "codespan-reporting", "half", - "hashbrown 0.16.0", + "hashbrown 0.16.1", "hexf-parse", "indexmap", "libm", @@ -5174,11 +5184,11 @@ checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" [[package]] name = "option-operations" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c26d27bb1aeab65138e4bf7666045169d1717febcc9ff870166be8348b223d0" +checksum = "b31ce827892359f23d3cd1cc4c75a6c241772bbd2db17a92dcf27cbefdf52689" dependencies = [ - "paste", + "pastey", ] [[package]] @@ -7789,9 +7799,9 @@ checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" [[package]] name = "value-bag" -version = "1.11.1" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "943ce29a8a743eb10d6082545d861b24f9d1b160b7d741e0f2cdf726bec909c5" +checksum = "7ba6f5989077681266825251a52748b8c1d8a4ad098cc37e440103d0ea717fc0" dependencies = [ "value-bag-serde1", "value-bag-sval2", @@ -7799,20 +7809,20 @@ dependencies = [ [[package]] name = "value-bag-serde1" -version = "1.11.1" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35540706617d373b118d550d41f5dfe0b78a0c195dc13c6815e92e2638432306" +checksum = "16530907bfe2999a1773ca5900a65101e092c70f642f25cc23ca0c43573262c5" dependencies = [ "erased-serde", - "serde", + "serde_core", "serde_fmt", ] [[package]] name = "value-bag-sval2" -version = "1.11.1" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fe7e140a2658cc16f7ee7a86e413e803fc8f9b5127adc8755c19f9fefa63a52" +checksum = "d00ae130edd690eaa877e4f40605d534790d1cf1d651e7685bd6a144521b251f" dependencies = [ "sval", "sval_buffer", @@ -8183,7 +8193,7 @@ dependencies = [ "cfg-if", "cfg_aliases", "document-features", - "hashbrown 0.16.0", + "hashbrown 0.16.1", "js-sys", "log", "naga 27.0.3", @@ -8214,7 +8224,7 @@ dependencies = [ "bytemuck", "cfg_aliases", "document-features", - "hashbrown 0.16.0", + "hashbrown 0.16.1", "indexmap", "log", "naga 27.0.3", @@ -8281,7 +8291,7 @@ dependencies = [ "gpu-alloc", "gpu-allocator", "gpu-descriptor", - "hashbrown 0.16.0", + "hashbrown 0.16.1", "js-sys", "khronos-egl", "libc", @@ -9373,18 +9383,18 @@ checksum = "6df3dc4292935e51816d896edcd52aa30bc297907c26167fec31e2b0c6a32524" [[package]] name = "zerocopy" -version = "0.8.27" +version = "0.8.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" +checksum = "43fa6694ed34d6e57407afbccdeecfa268c470a7d2a5b0cf49ce9fcc345afb90" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.27" +version = "0.8.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" +checksum = "c640b22cd9817fae95be82f0d2f90b11f7605f6c319d16705c459b27ac2cbc26" dependencies = [ "proc-macro2", "quote", diff --git a/api/examples/items.rs b/api/examples/items.rs index 1e70a38..03bb560 100644 --- a/api/examples/items.rs +++ b/api/examples/items.rs @@ -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 diff --git a/api/src/jellyfin.rs b/api/src/jellyfin.rs index c105f3b..05607b7 100644 --- a/api/src/jellyfin.rs +++ b/api/src/jellyfin.rs @@ -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, } -/** 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, - /** 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>>, >, /// 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>, } -/** 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, } -/** A MediaBrowser.Model.Dlna.DeviceProfile represents a set of metadata which determines which content a certain device is able to play. -
-Specifically, it defines the supported containers and -codecs (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. +
+Specifically, it defines the supported containers and +codecs (video and/or audio, including codec profiles and levels) +the device is able to direct play (without transcoding or remuxing), as well as which containers/codecs to transcode to 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>, /// 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, - >, + pub allow_on_demand_metadata_based_keyframe_extraction_for_extensions: Option>, } #[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, /// Gets or sets the URL format string. @@ -2443,8 +2438,8 @@ pub struct MediaSourceInfo { pub size: Option, #[serde(rename = "Name")] pub name: Option, - /** 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>>, #[serde(rename = "TranscodingUrl")] pub transcoding_url: Option, - /** 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, - /** 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, /// Gets or sets the profile. @@ -2719,8 +2714,8 @@ pub struct MediaUpdateInfoPathDto { /// Gets or sets media path. #[serde(rename = "Path")] pub path: Option, - /** 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, } @@ -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, /// Gets or sets the filter for remote IP connectivity. Used in conjunction with . @@ -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, - /** A MediaBrowser.Model.Dlna.DeviceProfile represents a set of metadata which determines which content a certain device is able to play. -
-Specifically, it defines the supported containers and -codecs (video and/or audio, including codec profiles and levels) -the device is able to direct play (without transcoding or remuxing), -as well as which containers/codecs to transcode to 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. +
+ Specifically, it defines the supported containers and + codecs (video and/or audio, including codec profiles and levels) + the device is able to direct play (without transcoding or remuxing), + as well as which containers/codecs to transcode to in case it isn't.*/ #[serde(rename = "DeviceProfile")] pub device_profile: Option, /// 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, - /** A MediaBrowser.Model.Dlna.DeviceProfile represents a set of metadata which determines which content a certain device is able to play. -
-Specifically, it defines the supported containers and -codecs (video and/or audio, including codec profiles and levels) -the device is able to direct play (without transcoding or remuxing), -as well as which containers/codecs to transcode to 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. +
+ Specifically, it defines the supported containers and + codecs (video and/or audio, including codec profiles and levels) + the device is able to direct play (without transcoding or remuxing), + as well as which containers/codecs to transcode to in case it isn't.*/ #[serde(rename = "DeviceProfile")] pub device_profile: Option, /// 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, - /** 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, /// 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, } -/** 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, - /** 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, /// 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 { diff --git a/api/src/lib.rs b/api/src/lib.rs index 93118b4..c11cd5c 100644 --- a/api/src/lib.rs +++ b/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, + password: impl AsRef, + config: JellyfinConfig, + ) -> Result { + 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::() + .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, config: JellyfinConfig) -> Result { + 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 { - 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, - ) -> Result { - 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 { 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, - 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, } diff --git a/crates/iced_video_player/Cargo.toml b/crates/iced_video_player/Cargo.toml index e32a3a3..35adbe6 100644 --- a/crates/iced_video_player/Cargo.toml +++ b/crates/iced_video_player/Cargo.toml @@ -20,12 +20,12 @@ iced = { git = "https://github.com/iced-rs/iced", features = [ "wgpu", ] } iced_wgpu = { git = "https://github.com/iced-rs/iced" } -gstreamer = "0.23" -gstreamer-app = "0.23" # appsink -gstreamer-base = "0.23" # basesrc -glib = "0.20" # gobject traits and error type +gstreamer = { version = "0.24", features = ["v1_26"] } +gstreamer-app = { version = "0.24", features = ["v1_26"] } # appsink +gstreamer-base = { version = "0.24", features = ["v1_26"] } # basesrc +glib = "0.21" # gobject traits and error type log = "0.4" -thiserror = "1" +thiserror = "2" url = "2" # media uri [package.metadata.nix] @@ -46,6 +46,8 @@ runtimeLibs = [ "gst_all_1.gst-plugins-ugly", "gst_all_1.gst-plugins-good", "gst_all_1.gst-plugins-base", + "glib", + "glib-networking", ] buildInputs = [ "libxkbcommon", @@ -55,6 +57,8 @@ buildInputs = [ "gst_all_1.gst-plugins-ugly", "gst_all_1.gst-plugins-good", "gst_all_1.gst-plugins-base", + "glib", + "glib-networking", ] [package.metadata.docs.rs] diff --git a/crates/iced_video_player/src/video.rs b/crates/iced_video_player/src/video.rs index f382e63..474d4a5 100644 --- a/crates/iced_video_player/src/video.rs +++ b/crates/iced_video_player/src/video.rs @@ -307,7 +307,7 @@ impl Video { .unwrap_or(0), ); - let sync_av = pipeline.has_property("av-offset", None); + let sync_av = pipeline.has_property("av-offset"); // NV12 = 12bpp let frame = Arc::new(Mutex::new(Frame::empty())); diff --git a/flake.nix b/flake.nix index dc5faff..32557d1 100644 --- a/flake.nix +++ b/flake.nix @@ -56,7 +56,7 @@ src = let filterBySuffix = path: exts: lib.any (ext: lib.hasSuffix ext path) exts; - sourceFilters = path: type: (craneLib.filterCargoSources path type) || filterBySuffix path [".c" ".h" ".hpp" ".cpp" ".cc"]; + sourceFilters = path: type: (craneLib.filterCargoSources path type) || filterBySuffix path [".c" ".h" ".hpp" ".cpp" ".cc" "wgsl"]; in lib.cleanSourceWith { filter = sourceFilters; @@ -74,19 +74,24 @@ buildInputs = with pkgs; [ - gst_all_1.gst-editing-services gst_all_1.gst-libav - gst_all_1.gst-plugins-bad gst_all_1.gst-plugins-base gst_all_1.gst-plugins-good - gst_all_1.gst-plugins-rs + gst_all_1.gst-plugins-bad gst_all_1.gst-plugins-ugly - gst_all_1.gst-rtsp-server + gst_all_1.gst-plugins-rs gst_all_1.gstreamer + glib + glib-networking + + libsysprof-capture + pcre2 + libunwind + elfutils + zstd openssl vulkan-loader - glib ] ++ (lib.optionals pkgs.stdenv.isLinux [ gst_all_1.gstreamermm @@ -159,8 +164,8 @@ default = pkg; }; - devShells = { - default = + devShells = rec { + rust-shell = pkgs.mkShell.override { stdenv = if pkgs.stdenv.isLinux @@ -168,6 +173,8 @@ else pkgs.clangStdenv; } (commonArgs // { + GST_PLUGIN_PATH = "/run/current-system/sw/lib/gstreamer-1.0/"; + GIO_EXTRA_MODULES = "${pkgs.glib-networking}/lib/gio/modules"; packages = with pkgs; [ toolchainWithRustAnalyzer @@ -188,6 +195,7 @@ mold ]); }); + default = rust-shell; }; } ) diff --git a/src/main.rs b/src/main.rs index 5b702f7..b171020 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,24 +2,9 @@ mod errors; use api::JellyfinConfig; use errors::*; -fn jellyfin_config_try() -> Result { - let file = std::fs::read("config.toml").change_context(Error)?; - let config: JellyfinConfig = toml::from_slice(&file) - .change_context(Error) - .attach("Failed to parse Jellyfin Config")?; - Ok(config) -} - -fn jellyfin_config() -> JellyfinConfig { - jellyfin_config_try().unwrap_or_else(|err| { - eprintln!("Error loading Jellyfin configuration: {:?}", err); - std::process::exit(1); - }) -} - fn main() -> Result<()> { tracing_subscriber::fmt::init(); - ui_iced::ui(jellyfin_config).change_context(Error)?; + ui_iced::ui().change_context(Error)?; Ok(()) } diff --git a/ui-iced/src/lib.rs b/ui-iced/src/lib.rs index 3072948..8cd6de5 100644 --- a/ui-iced/src/lib.rs +++ b/ui-iced/src/lib.rs @@ -201,7 +201,7 @@ fn update(state: &mut State, message: Message) -> Task { Task::perform( async move { - let mut client = api::JellyfinClient::new(config); + let mut client = api::JellyfinClient::new_with_config(config); client.authenticate().await }, |result| match result { @@ -662,23 +662,21 @@ fn card(item: &Item) -> Element<'_, Message> { // fn video(url: &str -fn init(config: impl Fn() -> api::JellyfinConfig + 'static) -> impl Fn() -> (State, Task) { - move || { - let mut jellyfin = api::JellyfinClient::new(config()); - ( - State::new(jellyfin.clone()), - Task::perform( - async move { jellyfin.authenticate_with_cached_token(".session").await }, - |token| match token { - Ok(token) => Message::SetToken(token), - Err(e) => Message::Error(format!("Authentication failed: {}", e)), - }, - ) - .chain(Task::done(Message::Refresh)), +fn init() -> (State, Task) { + let mut jellyfin = api::JellyfinClient::new_with_config(); + ( + State::new(jellyfin.clone()), + Task::perform( + async move { jellyfin.authenticate_with_cached_token(".session").await }, + |token| match token { + Ok(token) => Message::SetToken(token), + Err(e) => Message::Error(format!("Authentication failed: {}", e)), + }, ) - } + .chain(Task::done(Message::Refresh)), + ) } -pub fn ui(config: impl Fn() -> api::JellyfinConfig + 'static) -> iced::Result { - iced::application(init(config), update, view).run() +pub fn ui() -> iced::Result { + iced::application(init, update, view).run() }