diff --git a/Cargo.lock b/Cargo.lock index 7ef4555..1cdef54 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -172,22 +172,22 @@ dependencies = [ [[package]] name = "anstyle-query" -version = "1.1.4" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] name = "anstyle-wincon" -version = "3.0.10" +version = "3.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -200,6 +200,7 @@ checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" name = "api" version = "0.1.0" dependencies = [ + "bytes", "iref", "jiff", "reqwest", @@ -908,9 +909,9 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" [[package]] name = "bytes" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" [[package]] name = "calloop" @@ -992,9 +993,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.45" +version = "1.2.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35900b6c8d709fb1d854671ae27aeaa9eec2f8b01b364e1619a40da3e6fe2afe" +checksum = "b97463e1064cb1b1c1384ad0a0b9c8abd0988e2a91f52606c80ef14aadb63e36" dependencies = [ "find-msvc-tools", "jobserver", @@ -1098,9 +1099,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.51" +version = "4.5.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c26d721170e0295f191a69bd9a1f93efcdb0aff38684b61ab5750468972e5f5" +checksum = "aa8120877db0e5c011242f96806ce3c94e0737ab8108532a76a3300a01db2ab8" dependencies = [ "clap_builder", "clap_derive", @@ -1108,9 +1109,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.51" +version = "4.5.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75835f0c7bf681bfd05abe44e965760fea999a5286c6eb2d59883634fd02011a" +checksum = "02576b399397b659c26064fbc92a75fede9d18ffd5f80ca1cd74ddab167016e1" dependencies = [ "anstream", "anstyle", @@ -2073,9 +2074,9 @@ dependencies = [ [[package]] name = "find-msvc-tools" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127" +checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" [[package]] name = "flate2" @@ -3007,9 +3008,9 @@ checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] name = "hyper" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1744436df46f0bde35af3eda22aeaba453aada65d8f1c171cd8a5f59030bd69f" +checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" dependencies = [ "atomic-waker", "bytes", @@ -3062,9 +3063,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.17" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c6995591a8f1380fcb4ba966a252a4b29188d51d2b89e3a252f5305be65aea8" +checksum = "52e9a2a24dc5c6821e71a7030e1e14b7b632acac55c40e9d2e082c621261bb56" dependencies = [ "base64", "bytes", @@ -3083,7 +3084,7 @@ dependencies = [ "tokio", "tower-service", "tracing", - "windows-registry 0.5.3", + "windows-registry 0.6.1", ] [[package]] @@ -3194,6 +3195,7 @@ dependencies = [ "iced_core", "iced_futures", "raw-window-handle", + "sipper", "thiserror 2.0.17", ] @@ -3908,9 +3910,9 @@ dependencies = [ [[package]] name = "lyon_geom" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e16770d760c7848b0c1c2d209101e408207a65168109509f8483837a36cf2e7" +checksum = "e260b6de923e6e47adfedf6243013a7a874684165a6a277594ee3906021b2343" dependencies = [ "arrayvec", "euclid", @@ -4875,9 +4877,9 @@ dependencies = [ [[package]] name = "open" -version = "5.3.2" +version = "5.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2483562e62ea94312f3576a7aca397306df7990b8d89033e18766744377ef95" +checksum = "43bb73a7fa3799b198970490a51174027ba0d4ec504b03cd08caf513d40024bc" dependencies = [ "is-wsl", "libc", @@ -5499,9 +5501,9 @@ checksum = "d20581732dd76fa913c7dff1a2412b714afe3573e94d41c34719de73337cc8ab" [[package]] name = "rangemap" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93e7e49bb0bf967717f7bd674458b3d6b0c5f48ec7e3038166026a69fc22223" +checksum = "acbbbbea733ec66275512d0b9694f34102e7d5406fdbe2ad8d21b28dce92887c" [[package]] name = "rav1e" @@ -6275,6 +6277,16 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" +[[package]] +name = "sipper" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bccb4192828b3d9a08e0b5a73f17795080dfb278b50190216e3ae2132cf4f95" +dependencies = [ + "futures", + "pin-project-lite", +] + [[package]] name = "skrifa" version = "0.37.0" @@ -7319,8 +7331,11 @@ version = "0.1.0" dependencies = [ "api", "blurhash", + "bytes", "gpui_util", "iced", + "reqwest", + "tap", "tracing", "uuid", ] @@ -8301,6 +8316,17 @@ dependencies = [ "windows-strings 0.4.2", ] +[[package]] +name = "windows-registry" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" +dependencies = [ + "windows-link 0.2.1", + "windows-result 0.4.1", + "windows-strings 0.5.1", +] + [[package]] name = "windows-result" version = "0.2.0" diff --git a/api/Cargo.toml b/api/Cargo.toml index 184d98a..06c362a 100644 --- a/api/Cargo.toml +++ b/api/Cargo.toml @@ -4,6 +4,7 @@ version = "0.1.0" edition = "2024" [dependencies] +bytes = "1.11.0" iref = { version = "3.2.2", features = ["serde"] } jiff = { version = "0.2.16", features = ["serde"] } reqwest = { version = "0.12.24", features = ["json"] } diff --git a/api/examples/items.rs b/api/examples/items.rs index b91d8a0..1e70a38 100644 --- a/api/examples/items.rs +++ b/api/examples/items.rs @@ -17,8 +17,16 @@ pub async fn main() { for item in items { println!("{}: {:?}", item.id, item.name); let items = jellyfin.items(item.id).await.expect("Items"); - for item in items { - println!(" {}: {:?}", item.id, item.name); - } + std::fs::write( + format!("items_{:?}.json", item.name), + serde_json::to_string_pretty(&items).expect("Serialize items"), + ); + // for item in items { + // println!(" {}: {:?}", item.id, item.name); + // std::fs::write( + // format!("item_{}.json", item.id), + // serde_json::to_string_pretty(&item).expect("Serialize item"), + // ); + // } } } diff --git a/api/src/jellyfin.rs b/api/src/jellyfin.rs index 6261420..c105f3b 100644 --- a/api/src/jellyfin.rs +++ b/api/src/jellyfin.rs @@ -6013,7 +6013,6 @@ pub struct XbmcMetadataOptions { pub enable_extra_thumbs_duplication: bool, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum ApiName { #[serde(rename = "Mal")] Mal, @@ -6030,7 +6029,6 @@ pub enum ApiName { } /// An enum representing formats of spatial audio. #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum AudioSpatialFormat { #[serde(rename = "None")] None, @@ -6041,7 +6039,6 @@ pub enum AudioSpatialFormat { } /// The base item kind. #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum BaseItemKind { #[serde(rename = "AggregateFolder")] AggregateFolder, @@ -6119,7 +6116,6 @@ pub enum BaseItemKind { Year, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum ChannelItemSortField { #[serde(rename = "Name")] Name, @@ -6137,7 +6133,6 @@ pub enum ChannelItemSortField { CommunityPlayCount, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum ChannelMediaContentType { #[serde(rename = "Clip")] Clip, @@ -6157,7 +6152,6 @@ pub enum ChannelMediaContentType { TvExtra, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum ChannelMediaType { #[serde(rename = "Audio")] Audio, @@ -6168,7 +6162,6 @@ pub enum ChannelMediaType { } /// Enum ChannelType. #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum ChannelType { #[serde(rename = "TV")] Tv, @@ -6176,7 +6169,6 @@ pub enum ChannelType { Radio, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum CodecType { #[serde(rename = "Video")] Video, @@ -6187,7 +6179,6 @@ pub enum CodecType { } /// Collection type. #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum CollectionType { #[serde(rename = "unknown")] Unknown, @@ -6218,7 +6209,6 @@ pub enum CollectionType { } /// The collection type options. #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum CollectionTypeOptions { #[serde(rename = "movies")] Movies, @@ -6238,7 +6228,6 @@ pub enum CollectionTypeOptions { Mixed, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum DayOfWeek { #[serde(rename = "Sunday")] Sunday, @@ -6256,7 +6245,6 @@ pub enum DayOfWeek { Saturday, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum DayPattern { #[serde(rename = "Daily")] Daily, @@ -6267,7 +6255,6 @@ pub enum DayPattern { } /// Enum containing deinterlace methods. #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum DeinterlaceMethod { #[serde(rename = "yadif")] Yadif, @@ -6275,7 +6262,6 @@ pub enum DeinterlaceMethod { Bwdif, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum DlnaProfileType { #[serde(rename = "Audio")] Audio, @@ -6290,7 +6276,6 @@ pub enum DlnaProfileType { } /// An enum representing an algorithm to downmix surround sound to stereo. #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum DownMixStereoAlgorithms { #[serde(rename = "None")] None, @@ -6305,7 +6290,6 @@ pub enum DownMixStereoAlgorithms { } /// An enum that represents a day of the week, weekdays, weekends, or all days. #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum DynamicDayOfWeek { #[serde(rename = "Sunday")] Sunday, @@ -6330,7 +6314,6 @@ pub enum DynamicDayOfWeek { } /// An enum representing the options to disable embedded subs. #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum EmbeddedSubtitleOptions { #[serde(rename = "AllowAll")] AllowAll, @@ -6343,7 +6326,6 @@ pub enum EmbeddedSubtitleOptions { } /// Enum containing encoder presets. #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum EncoderPreset { #[serde(rename = "auto")] Auto, @@ -6369,7 +6351,6 @@ pub enum EncoderPreset { Ultrafast, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum EncodingContext { #[serde(rename = "Streaming")] Streaming, @@ -6378,7 +6359,6 @@ pub enum EncodingContext { } /// The specific media type of an MediaBrowser.Model.Providers.ExternalIdInfo. #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum ExternalIdMediaType { #[serde(rename = "Album")] Album, @@ -6408,7 +6388,6 @@ pub enum ExternalIdMediaType { Book, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum ExtraType { #[serde(rename = "Unknown")] Unknown, @@ -6437,7 +6416,6 @@ pub enum ExtraType { } /// Enum FileSystemEntryType. #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum FileSystemEntryType { #[serde(rename = "File")] File, @@ -6449,7 +6427,6 @@ pub enum FileSystemEntryType { NetworkShare, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum ForgotPasswordAction { #[serde(rename = "ContactAdmin")] ContactAdmin, @@ -6460,7 +6437,6 @@ pub enum ForgotPasswordAction { } /// This exists simply to identify a set of known commands. #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum GeneralCommandType { #[serde(rename = "MoveUp")] MoveUp, @@ -6551,7 +6527,6 @@ pub enum GeneralCommandType { } /// Enum GroupQueueMode. #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum GroupQueueMode { #[serde(rename = "Queue")] Queue, @@ -6560,7 +6535,6 @@ pub enum GroupQueueMode { } /// Enum GroupRepeatMode. #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum GroupRepeatMode { #[serde(rename = "RepeatOne")] RepeatOne, @@ -6571,7 +6545,6 @@ pub enum GroupRepeatMode { } /// Enum GroupShuffleMode. #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum GroupShuffleMode { #[serde(rename = "Sorted")] Sorted, @@ -6580,7 +6553,6 @@ pub enum GroupShuffleMode { } /// Enum GroupState. #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum GroupStateType { #[serde(rename = "Idle")] Idle, @@ -6593,7 +6565,6 @@ pub enum GroupStateType { } /// Enum GroupUpdateType. #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum GroupUpdateType { #[serde(rename = "UserJoined")] UserJoined, @@ -6620,7 +6591,6 @@ pub enum GroupUpdateType { } /// Enum containing hardware acceleration types. #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum HardwareAccelerationType { #[serde(rename = "none")] None, @@ -6641,7 +6611,6 @@ pub enum HardwareAccelerationType { } /// Enum ImageOutputFormat. #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum ImageFormat { #[serde(rename = "Bmp")] Bmp, @@ -6657,7 +6626,6 @@ pub enum ImageFormat { Svg, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum ImageOrientation { #[serde(rename = "TopLeft")] TopLeft, @@ -6678,7 +6646,6 @@ pub enum ImageOrientation { } /// Enum ImageResolution. #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum ImageResolution { #[serde(rename = "MatchSource")] MatchSource, @@ -6700,7 +6667,6 @@ pub enum ImageResolution { P2160, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum ImageSavingConvention { #[serde(rename = "Legacy")] Legacy, @@ -6709,7 +6675,6 @@ pub enum ImageSavingConvention { } /// Enum ImageType. #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum ImageType { #[serde(rename = "Primary")] Primary, @@ -6740,7 +6705,6 @@ pub enum ImageType { } /// Enum IsoType. #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum IsoType { #[serde(rename = "Dvd")] Dvd, @@ -6749,7 +6713,6 @@ pub enum IsoType { } /// Used to control the data that gets attached to DtoBaseItems. #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum ItemFields { #[serde(rename = "AirTime")] AirTime, @@ -6874,7 +6837,6 @@ pub enum ItemFields { } /// Enum ItemFilter. #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum ItemFilter { #[serde(rename = "IsFolder")] IsFolder, @@ -6897,7 +6859,6 @@ pub enum ItemFilter { } /// These represent sort orders. #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum ItemSortBy { #[serde(rename = "Default")] Default, @@ -6965,7 +6926,6 @@ pub enum ItemSortBy { SearchScore, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum KeepUntil { #[serde(rename = "UntilDeleted")] UntilDeleted, @@ -6977,7 +6937,6 @@ pub enum KeepUntil { UntilDate, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum LiveTvServiceStatus { #[serde(rename = "Ok")] Ok, @@ -6986,7 +6945,6 @@ pub enum LiveTvServiceStatus { } /// Enum LocationType. #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum LocationType { #[serde(rename = "FileSystem")] FileSystem, @@ -6998,7 +6956,6 @@ pub enum LocationType { Offline, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum LogLevel { #[serde(rename = "Trace")] Trace, @@ -7016,7 +6973,6 @@ pub enum LogLevel { None, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum MediaProtocol { #[serde(rename = "File")] File, @@ -7035,7 +6991,6 @@ pub enum MediaProtocol { } /// Defines the types of content an individual Jellyfin.Data.Entities.MediaSegment represents. #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum MediaSegmentType { #[serde(rename = "Unknown")] Unknown, @@ -7051,7 +7006,6 @@ pub enum MediaSegmentType { Intro, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum MediaSourceType { #[serde(rename = "Default")] Default, @@ -7063,7 +7017,6 @@ pub enum MediaSourceType { /** Media streaming protocol. Lowercase for backwards compatibility.*/ #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum MediaStreamProtocol { #[serde(rename = "http")] Http, @@ -7072,7 +7025,6 @@ pub enum MediaStreamProtocol { } /// Enum MediaStreamType. #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum MediaStreamType { #[serde(rename = "Audio")] Audio, @@ -7089,7 +7041,6 @@ pub enum MediaStreamType { } /// Media types. #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum MediaType { #[serde(rename = "Unknown")] Unknown, @@ -7104,7 +7055,6 @@ pub enum MediaType { } /// Enum MetadataFields. #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum MetadataField { #[serde(rename = "Cast")] Cast, @@ -7126,7 +7076,6 @@ pub enum MetadataField { OfficialRating, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum MetadataRefreshMode { #[serde(rename = "None")] None, @@ -7138,7 +7087,6 @@ pub enum MetadataRefreshMode { FullRefresh, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum ParameterInclude { #[serde(rename = "ProviderList")] ProviderList, @@ -7151,7 +7099,6 @@ pub enum ParameterInclude { } /// The person kind. #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum PersonKind { #[serde(rename = "Unknown")] Unknown, @@ -7205,7 +7152,6 @@ pub enum PersonKind { Translator, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum PlayAccess { #[serde(rename = "Full")] Full, @@ -7213,7 +7159,6 @@ pub enum PlayAccess { None, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum PlaybackErrorCode { #[serde(rename = "NotAllowed")] NotAllowed, @@ -7224,7 +7169,6 @@ pub enum PlaybackErrorCode { } /// Enum PlaybackOrder. #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum PlaybackOrder { #[serde(rename = "Default")] Default, @@ -7233,7 +7177,6 @@ pub enum PlaybackOrder { } /// Enum PlaybackRequestType. #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum PlaybackRequestType { #[serde(rename = "Play")] Play, @@ -7272,7 +7215,6 @@ pub enum PlaybackRequestType { } /// Enum PlayCommand. #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum PlayCommand { #[serde(rename = "PlayNow")] PlayNow, @@ -7286,7 +7228,6 @@ pub enum PlayCommand { PlayShuffle, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum PlayMethod { #[serde(rename = "Transcode")] Transcode, @@ -7297,7 +7238,6 @@ pub enum PlayMethod { } /// Enum PlayQueueUpdateReason. #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum PlayQueueUpdateReason { #[serde(rename = "NewPlaylist")] NewPlaylist, @@ -7322,7 +7262,6 @@ pub enum PlayQueueUpdateReason { } /// Enum PlaystateCommand. #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum PlaystateCommand { #[serde(rename = "Stop")] Stop, @@ -7345,7 +7284,6 @@ pub enum PlaystateCommand { } /// Plugin load status. #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum PluginStatus { #[serde(rename = "Active")] Active, @@ -7363,7 +7301,6 @@ pub enum PluginStatus { Disabled, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum ProcessPriorityClass { #[serde(rename = "Normal")] Normal, @@ -7379,7 +7316,6 @@ pub enum ProcessPriorityClass { AboveNormal, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum ProfileConditionType { #[serde(rename = "Equals")] Equals, @@ -7393,7 +7329,6 @@ pub enum ProfileConditionType { EqualsAny, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum ProfileConditionValue { #[serde(rename = "AudioChannels")] AudioChannels, @@ -7445,7 +7380,6 @@ pub enum ProfileConditionValue { VideoRangeType, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum ProgramAudio { #[serde(rename = "Mono")] Mono, @@ -7461,7 +7395,6 @@ pub enum ProgramAudio { Atmos, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum RatingType { #[serde(rename = "Score")] Score, @@ -7469,7 +7402,6 @@ pub enum RatingType { Likes, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum RecommendationType { #[serde(rename = "SimilarToRecentlyPlayed")] SimilarToRecentlyPlayed, @@ -7485,7 +7417,6 @@ pub enum RecommendationType { HasLikedActor, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum RecordingStatus { #[serde(rename = "New")] New, @@ -7503,7 +7434,6 @@ pub enum RecordingStatus { Error, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum RepeatMode { #[serde(rename = "RepeatNone")] RepeatNone, @@ -7514,7 +7444,6 @@ pub enum RepeatMode { } /// An enum representing the axis that should be scrolled. #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum ScrollDirection { #[serde(rename = "Horizontal")] Horizontal, @@ -7523,7 +7452,6 @@ pub enum ScrollDirection { } /// Enum SendCommandType. #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum SendCommandType { #[serde(rename = "Unpause")] Unpause, @@ -7536,7 +7464,6 @@ pub enum SendCommandType { } /// The status of a series. #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum SeriesStatus { #[serde(rename = "Continuing")] Continuing, @@ -7547,7 +7474,6 @@ pub enum SeriesStatus { } /// The different kinds of messages that are used in the WebSocket api. #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum SessionMessageType { #[serde(rename = "ForceKeepAlive")] ForceKeepAlive, @@ -7620,7 +7546,6 @@ pub enum SessionMessageType { } /// An enum representing the sorting order. #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum SortOrder { #[serde(rename = "Ascending")] Ascending, @@ -7628,7 +7553,6 @@ pub enum SortOrder { Descending, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum Status { #[serde(rename = "Completed")] Completed, @@ -7639,7 +7563,6 @@ pub enum Status { } /// Delivery method to use during playback of a specific subtitle format. #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum SubtitleDeliveryMethod { #[serde(rename = "Encode")] Encode, @@ -7654,7 +7577,6 @@ pub enum SubtitleDeliveryMethod { } /// An enum representing a subtitle playback mode. #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum SubtitlePlaybackMode { #[serde(rename = "Default")] Default, @@ -7668,7 +7590,6 @@ pub enum SubtitlePlaybackMode { Smart, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum SyncAction { #[serde(rename = "UpdateProvider")] UpdateProvider, @@ -7677,7 +7598,6 @@ pub enum SyncAction { } /// Enum SyncPlayUserAccessType. #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum SyncPlayUserAccessType { #[serde(rename = "CreateAndJoinGroups")] CreateAndJoinGroups, @@ -7688,7 +7608,6 @@ pub enum SyncPlayUserAccessType { } /// Enum TaskCompletionStatus. #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum TaskCompletionStatus { #[serde(rename = "Completed")] Completed, @@ -7701,7 +7620,6 @@ pub enum TaskCompletionStatus { } /// Enum TaskState. #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum TaskState { #[serde(rename = "Idle")] Idle, @@ -7712,7 +7630,6 @@ pub enum TaskState { } /// Enum containing tonemapping algorithms. #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum TonemappingAlgorithm { #[serde(rename = "none")] None, @@ -7733,7 +7650,6 @@ pub enum TonemappingAlgorithm { } /// Enum containing tonemapping modes. #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum TonemappingMode { #[serde(rename = "auto")] Auto, @@ -7748,7 +7664,6 @@ pub enum TonemappingMode { } /// Enum containing tonemapping ranges. #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum TonemappingRange { #[serde(rename = "auto")] Auto, @@ -7758,7 +7673,6 @@ pub enum TonemappingRange { Pc, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum TranscodeReason { #[serde(rename = "ContainerNotSupported")] ContainerNotSupported, @@ -7814,7 +7728,6 @@ pub enum TranscodeReason { VideoCodecTagNotSupported, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum TranscodeSeekInfo { #[serde(rename = "Auto")] Auto, @@ -7822,7 +7735,6 @@ pub enum TranscodeSeekInfo { Bytes, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum TransportStreamTimestamp { #[serde(rename = "None")] None, @@ -7833,7 +7745,6 @@ pub enum TransportStreamTimestamp { } /// Enum TrickplayScanBehavior. #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum TrickplayScanBehavior { #[serde(rename = "Blocking")] Blocking, @@ -7842,7 +7753,6 @@ pub enum TrickplayScanBehavior { } /// An enum representing an unrated item. #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum UnratedItem { #[serde(rename = "Movie")] Movie, @@ -7864,7 +7774,6 @@ pub enum UnratedItem { Other, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum Video3DFormat { #[serde(rename = "HalfSideBySide")] HalfSideBySide, @@ -7879,7 +7788,6 @@ pub enum Video3DFormat { } /// An enum representing video ranges. #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum VideoRange { #[serde(rename = "Unknown")] Unknown, @@ -7890,7 +7798,6 @@ pub enum VideoRange { } /// An enum representing types of video ranges. #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum VideoRangeType { #[serde(rename = "Unknown")] Unknown, @@ -7913,7 +7820,6 @@ pub enum VideoRangeType { } /// Enum VideoType. #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "PascalCase")] pub enum VideoType { #[serde(rename = "VideoFile")] VideoFile, diff --git a/api/src/lib.rs b/api/src/lib.rs index 73cdde1..3edebac 100644 --- a/api/src/lib.rs +++ b/api/src/lib.rs @@ -185,6 +185,39 @@ impl JellyfinClient { let out: jellyfin::BaseItemDtoQueryResult = serde_json::from_str(&text)?; Ok(out.items) } + + pub async fn search(&self, query: impl AsRef) -> Result> { + let text = &self + .request_builder(Method::GET, "Items/Search") + .query(&[("searchTerm", query.as_ref()), ("recursive", "true")]) + .send() + .await? + .error_for_status()? + .text() + .await?; + let out: jellyfin::BaseItemDtoQueryResult = serde_json::from_str(&text)?; + Ok(out.items) + } + + pub async fn thumbnail( + &self, + item: uuid::Uuid, + image_type: jellyfin::ImageType, + ) -> Result { + let uri = format!( + "Items/{}/Images/{}", + item, + serde_json::to_string(&image_type).expect("Failed to serialize image type") + ); + let bytes = self + .request_builder(Method::GET, uri) + .send() + .await? + .error_for_status()? + .bytes() + .await?; + Ok(bytes) + } } // pub trait Item { diff --git a/typegen/src/main.rs b/typegen/src/main.rs index 4e4bace..f2fdaf6 100644 --- a/typegen/src/main.rs +++ b/typegen/src/main.rs @@ -184,7 +184,6 @@ fn main() { quote::quote! { #[doc = #desc] #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] - #[serde(rename_all = "PascalCase")] pub enum #key { #(#variants),* } @@ -192,7 +191,6 @@ fn main() { } else { quote::quote! { #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] - #[serde(rename_all = "PascalCase")] pub enum #key { #(#variants),* } diff --git a/ui-iced/Cargo.toml b/ui-iced/Cargo.toml index ffaa639..2508edb 100644 --- a/ui-iced/Cargo.toml +++ b/ui-iced/Cargo.toml @@ -6,12 +6,10 @@ edition = "2024" [dependencies] api = { version = "0.1.0", path = "../api" } blurhash = "0.2.3" +bytes = "1.11.0" gpui_util = "0.2.2" -iced = { git = "https://github.com/iced-rs/iced", features = [ - "advanced", - "canvas", - "image", - "tokio", -] } +iced = { git = "https://github.com/iced-rs/iced", features = ["advanced", "canvas", "image", "sipper", "tokio"] } +reqwest = "0.12.24" +tap = "1.0.1" tracing = "0.1.41" uuid = "1.18.1" diff --git a/ui-iced/src/blur_hash.rs b/ui-iced/src/blur_hash.rs index 2ae3cc2..1acb162 100644 --- a/ui-iced/src/blur_hash.rs +++ b/ui-iced/src/blur_hash.rs @@ -13,6 +13,17 @@ pub struct BlurHash { punch: f32, } +impl core::fmt::Debug for BlurHash { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("BlurHash") + .field("hash", &self.hash) + .field("width", &self.width) + .field("height", &self.height) + .field("punch", &self.punch) + .finish() + } +} + impl BlurHash { pub fn recompute(&mut self, width: u32, height: u32, punch: f32) { let pixels = blurhash::decode(&self.hash, width, height, punch) diff --git a/ui-iced/src/lib.rs b/ui-iced/src/lib.rs index 5913bac..b2b9b14 100644 --- a/ui-iced/src/lib.rs +++ b/ui-iced/src/lib.rs @@ -4,14 +4,14 @@ use shared_string::SharedString; mod blur_hash; use blur_hash::BlurHash; +// mod preview; +// use preview::Preview; + use iced::{Alignment, Element, Length, Task, widget::*}; use std::collections::{BTreeMap, BTreeSet}; #[derive(Debug, Clone)] -pub struct Loading { - to: Screen, - from: Screen, -} +pub struct Loading {} #[derive(Default, Debug, Clone)] pub struct ItemCache { @@ -89,8 +89,10 @@ pub struct Item { pub enum Screen { #[default] Home, + Item(Option), + Search(String), Settings, - Profile, + User, } #[derive(Debug, Clone)] struct State { @@ -100,6 +102,7 @@ struct State { jellyfin_client: api::JellyfinClient, messages: Vec, history: Vec>, + query: Option, } impl State { @@ -111,6 +114,7 @@ impl State { jellyfin_client, messages: Vec::new(), history: Vec::new(), + query: None, } } } @@ -119,6 +123,8 @@ impl State { pub enum Message { OpenSettings, Refresh, + Search, + SearchQueryChanged(String), OpenItem(Option), LoadedItem(Option, Vec), Error(String), @@ -191,6 +197,23 @@ fn update(state: &mut State, message: Message) -> Task { state.current = None; Task::done(Message::Refresh) } + Message::SearchQueryChanged(query) => { + state.query = Some(query); + // Handle search query change + Task::none() + } + Message::Search => { + // Handle search action + let client = state.jellyfin_client.clone(); + let query = state.query.clone().unwrap_or_default(); + Task::perform(async move { client.search(query).await }, |r| match r { + Err(e) => Message::Error(format!("Search failed: {}", e)), + Ok(items) => { + let items = items.into_iter().map(Item::from).collect(); + Message::LoadedItem(None, items) + } + }) + } } } @@ -231,20 +254,15 @@ fn header(state: &State) -> Element<'_, Message> { .align_y(Alignment::Center) .style(container::rounded_box) .into(), - container( - row([ - button("Settings").on_press(Message::OpenSettings).into(), - button("Refresh").on_press(Message::Refresh).into(), - ]) - .spacing(10), - ) - .padding(10) - .width(Length::Fill) - .height(Length::Fill) - .align_x(Alignment::End) - .align_y(Alignment::Center) - .style(container::rounded_box) - .into(), + search(state), + container(row([button("Refresh").on_press(Message::Refresh).into()]).spacing(10)) + .padding(10) + .width(Length::Fill) + .height(Length::Fill) + .align_x(Alignment::End) + .align_y(Alignment::Center) + .style(container::rounded_box) + .into(), ]) .align_y(Alignment::Center) .width(Length::Fill) @@ -252,6 +270,22 @@ fn header(state: &State) -> Element<'_, Message> { .into() } +fn search(state: &State) -> Element<'_, Message> { + container( + TextInput::new("Search...", state.query.as_deref().unwrap_or_default()) + .padding(10) + .size(16) + .width(Length::Fill) + .on_input(Message::SearchQueryChanged) + .on_submit(Message::Search), + ) + .padding(10) + .width(Length::Fill) + .height(Length::Shrink) + .style(container::rounded_box) + .into() +} + fn footer(state: &State) -> Element<'_, Message> { container( column( diff --git a/ui-iced/src/preview.rs b/ui-iced/src/preview.rs new file mode 100644 index 0000000..71a056a --- /dev/null +++ b/ui-iced/src/preview.rs @@ -0,0 +1,105 @@ +use iced::{Animation, advanced::image::Handle, widget::image}; +use reqwest::Method; +use std::sync::Arc; + +use crate::blur_hash::BlurHash; + +#[derive(Clone)] +pub struct ImageDownloader { + client: reqwest::Client, + request_modifier: + Option reqwest::RequestBuilder + Send + Sync>>, +} + +impl ImageDownloader { + pub fn new() -> Self { + Self { + client: reqwest::Client::new(), + request_modifier: None, + } + } + + pub fn with_modifier(mut self, f: F) -> Self + where + F: Fn(reqwest::RequestBuilder) -> reqwest::RequestBuilder + Send + Sync + 'static, + { + self.request_modifier = Some(Arc::new(f)); + self + } + + pub async fn download(&self, url: &str) -> reqwest::Result { + use ::tap::*; + let response = self + .client + .request(Method::GET, url) + .pipe(|builder| { + if let Some(ref modifier) = self.request_modifier { + modifier(builder) + } else { + builder + } + }) + .send() + .await?; + let bytes = response.bytes().await?; + Ok(bytes) + } +} + +#[derive(Clone, Debug)] +pub enum Preview { + Thumbnail { + thumbnail: Image, + blur_hash: BlurHash, + }, + BlurHash { + blur_hash: BlurHash, + }, +} + +// impl Preview { +// pub fn thumbnail(image: Image, blur_hash: BlurHash) -> Self { +// Preview::Thumbnail { +// thumbnail: image, +// blur_hash, +// } +// } +// +// pub fn blur_hash(blur_hash: BlurHash) -> Self { +// Preview::BlurHash { blur_hash } +// } +// +// pub fn upgrade( +// self, +// fut: impl core::future::Future + 'static + Send, +// ) -> iced::Task { +// // let sip = iced::task::sipper(async move |mut sender| { +// // let bytes = fut.await; +// // let handle = Handle::from_bytes(bytes.clone()); +// // let allocation = image::allocate(handle); +// // let image = Image { +// // bytes, +// // handle, +// // allocation, +// // fade_in: Animation::new(false), +// // }; +// // let _ = sender.send(image).await; +// // }); +// // iced::Task::sip(sip, ||) +// Task:: +// } +// } +// +// enum PreviewMessage { +// BlurHashLoaded(BlurHash), +// ThumbnailLoaded(Image), +// ThumbnailAllocated(image::Allocation), +// } +// +#[derive(Clone, Debug)] +pub struct Image { + bytes: bytes::Bytes, + handle: Handle, + allocation: image::Allocation, + fade_in: Animation, +}