From a117b0710fca0ba1ef1329a8921092d1bf46abcd Mon Sep 17 00:00:00 2001 From: metamuffin Date: Tue, 4 Feb 2025 15:18:45 +0100 Subject: jellyfin backdrop --- server/src/routes/compat/jellyfin/mod.rs | 117 +++++++++++++++++++--------- server/src/routes/compat/jellyfin/models.rs | 2 +- server/src/routes/mod.rs | 16 ++-- server/src/routes/ui/node.rs | 1 - server/src/routes/ui/sort.rs | 6 +- 5 files changed, 92 insertions(+), 50 deletions(-) (limited to 'server') diff --git a/server/src/routes/compat/jellyfin/mod.rs b/server/src/routes/compat/jellyfin/mod.rs index 0fed578..85d6768 100644 --- a/server/src/routes/compat/jellyfin/mod.rs +++ b/server/src/routes/compat/jellyfin/mod.rs @@ -9,9 +9,13 @@ use crate::routes::{ stream::rocket_uri_macro_r_stream, ui::{ account::{login_logic, session::Session}, - assets::{rocket_uri_macro_r_asset, rocket_uri_macro_r_item_poster}, + assets::{ + rocket_uri_macro_r_asset, rocket_uri_macro_r_item_backdrop, + rocket_uri_macro_r_item_poster, + }, error::MyResult, node::{aspect_class, DatabaseNodeUserDataExt}, + sort::{filter_and_sort_nodes, FilterProperty, NodeFilterSort, SortOrder, SortProperty}, }, }; use anyhow::{anyhow, Context}; @@ -151,15 +155,25 @@ pub fn r_jellyfin_items_image_primary( _session: Session, id: &str, fillWidth: Option, - tag: Option, + tag: String, ) -> Redirect { - if let Some(tag) = tag { - Redirect::permanent(rocket::uri!(r_asset(tag, fillWidth))) - } else { + if tag == "poster" { Redirect::permanent(rocket::uri!(r_item_poster(id, fillWidth))) + } else { + Redirect::permanent(rocket::uri!(r_asset(tag, fillWidth))) } } +#[get("/Items//Images/Backdrop/0?")] +#[allow(non_snake_case)] +pub fn r_jellyfin_items_images_backdrop( + _session: Session, + id: &str, + maxWidth: Option, +) -> Redirect { + Redirect::permanent(rocket::uri!(r_item_backdrop(id, maxWidth))) +} + #[get("/Items/")] #[allow(private_interfaces)] pub fn r_jellyfin_items_item( @@ -250,52 +264,70 @@ pub fn r_jellyfin_items( database: &State, query: JellyfinItemQuery, ) -> MyResult> { - let mut items = Vec::new(); - - let nodes = if let Some(q) = query.search_term { - database - .search(&q, query.limit, query.start_index.unwrap_or_default())? - .1 + let (nodes, parent_kind) = if let Some(q) = query.search_term { + ( + database + .search(&q, query.limit, query.start_index.unwrap_or_default())? + .1, + None, + ) } else if let Some(parent) = query.parent_id { - database - .get_node_children(NodeID::from_slug(&parent))? - .into_iter() - .skip(query.start_index.unwrap_or_default()) - .take(query.limit) - .collect() + let parent = NodeID::from_slug(&parent); + ( + database + .get_node_children(parent)? + .into_iter() + .skip(query.start_index.unwrap_or_default()) + .take(query.limit) + .collect(), + database.get_node(parent)?.map(|n| n.kind), + ) } else { - vec![] + (vec![], None) }; let filter_kind = query .include_item_types - .map(|n| match n.as_str() { - "Movie" => NodeKind::Movie, - "Audio" => NodeKind::Music, - "Video" => NodeKind::Video, - _ => NodeKind::Unknown, + .and_then(|n| match n.as_str() { + "Movie" => Some(FilterProperty::KindMovie), + "Audio" => Some(FilterProperty::KindMusic), + "Video" => Some(FilterProperty::KindVideo), + _ => None, }) .or(if query.internal_artists { - Some(NodeKind::Channel) + Some(FilterProperty::KindChannel) } else { None }) .or(if query.internal_persons { - Some(NodeKind::Channel) + Some(FilterProperty::KindChannel) } else { None }); - for nid in nodes { - let (n, ud) = database.get_node_with_userdata(nid, &session)?; - if let Some(fk) = filter_kind { - if n.kind != fk { - continue; - } - } - if n.visibility >= Visibility::Reduced { - items.push(item_object(&n, &ud)) - } - } + + let mut nodes = nodes + .into_iter() + .map(|nid| database.get_node_with_userdata(nid, &session)) + .collect::, anyhow::Error>>()?; + + filter_and_sort_nodes( + &NodeFilterSort { + sort_by: None, + filter_kind: filter_kind.map(|a| vec![a]), + sort_order: None, + }, + match parent_kind { + Some(NodeKind::Channel) => (SortProperty::ReleaseDate, SortOrder::Descending), + _ => (SortProperty::Title, SortOrder::Ascending), + }, + &mut nodes, + ); + + let items = nodes + .into_iter() + .filter(|(n, _)| n.visibility >= Visibility::Reduced) + .map(|(n, ud)| item_object(&n, &ud)) + .collect::>(); Ok(Json(json!({ "Items": items, @@ -361,6 +393,15 @@ pub fn r_jellyfin_items_intros(_session: Session, uid: &str, id: &str) -> Json Json { + Json(json!({ + "Items": [], + "TotalRecordCount": 0, + "StartIndex": 0 + })) +} + #[post("/Items//PlaybackInfo")] pub fn r_jellyfin_items_playbackinfo( _session: Session, @@ -704,8 +745,8 @@ fn item_object(node: &Node, userdata: &NodeUserData) -> JellyfinItem { "aspect-square" | _ => 1., }, collection_type: "unknown".to_owned(), - image_tags: BTreeMap::from_iter([("Primary".to_string(), "the-image".to_string())]), - backdrop_image_tags: vec![], + image_tags: BTreeMap::from_iter([("Primary".to_string(), "poster".to_string())]), + backdrop_image_tags: vec!["backdrop".to_string()], media_type: if node.media.is_some() { "Video".to_owned() } else { diff --git a/server/src/routes/compat/jellyfin/models.rs b/server/src/routes/compat/jellyfin/models.rs index eeffa34..be41835 100644 --- a/server/src/routes/compat/jellyfin/models.rs +++ b/server/src/routes/compat/jellyfin/models.rs @@ -163,7 +163,7 @@ pub(super) struct JellyfinItem { pub primary_image_aspect_ratio: f64, pub collection_type: String, pub image_tags: BTreeMap, - pub backdrop_image_tags: Vec<()>, + pub backdrop_image_tags: Vec, pub location_type: Option, pub media_type: String, pub video_type: Option, diff --git a/server/src/routes/mod.rs b/server/src/routes/mod.rs index 98ea4b0..a0c2de4 100644 --- a/server/src/routes/mod.rs +++ b/server/src/routes/mod.rs @@ -12,16 +12,16 @@ use compat::{ r_jellyfin_artists, r_jellyfin_branding_configuration, r_jellyfin_branding_css, r_jellyfin_displaypreferences_usersettings, r_jellyfin_displaypreferences_usersettings_post, r_jellyfin_items, - r_jellyfin_items_image_primary, r_jellyfin_items_intros, r_jellyfin_items_item, - r_jellyfin_items_playbackinfo, r_jellyfin_items_similar, + r_jellyfin_items_image_primary, r_jellyfin_items_images_backdrop, r_jellyfin_items_intros, + r_jellyfin_items_item, r_jellyfin_items_playbackinfo, r_jellyfin_items_similar, r_jellyfin_livetv_programs_recommended, r_jellyfin_persons, r_jellyfin_playback_bitratetest, r_jellyfin_quickconnect_enabled, r_jellyfin_sessions_capabilities_full, r_jellyfin_sessions_playing, - r_jellyfin_sessions_playing_progress, r_jellyfin_socket, r_jellyfin_system_endpoint, - r_jellyfin_system_info, r_jellyfin_system_info_public, r_jellyfin_system_info_public_case, - r_jellyfin_users_authenticatebyname, r_jellyfin_users_id, r_jellyfin_users_items, - r_jellyfin_users_items_item, r_jellyfin_users_public, r_jellyfin_users_views, - r_jellyfin_video_stream, + r_jellyfin_sessions_playing_progress, r_jellyfin_shows_nextup, r_jellyfin_socket, + r_jellyfin_system_endpoint, r_jellyfin_system_info, r_jellyfin_system_info_public, + r_jellyfin_system_info_public_case, r_jellyfin_users_authenticatebyname, + r_jellyfin_users_id, r_jellyfin_users_items, r_jellyfin_users_items_item, + r_jellyfin_users_public, r_jellyfin_users_views, r_jellyfin_video_stream, }, youtube::{r_youtube_channel, r_youtube_watch}, }; @@ -170,6 +170,7 @@ pub fn build_rocket(database: Database, federation: Federation) -> Rocket r_jellyfin_displaypreferences_usersettings_post, r_jellyfin_displaypreferences_usersettings, r_jellyfin_items_image_primary, + r_jellyfin_items_images_backdrop, r_jellyfin_items_intros, r_jellyfin_items_item, r_jellyfin_items_playbackinfo, @@ -182,6 +183,7 @@ pub fn build_rocket(database: Database, federation: Federation) -> Rocket r_jellyfin_sessions_capabilities_full, r_jellyfin_sessions_playing_progress, r_jellyfin_sessions_playing, + r_jellyfin_shows_nextup, r_jellyfin_socket, r_jellyfin_system_endpoint, r_jellyfin_system_info_public_case, diff --git a/server/src/routes/ui/node.rs b/server/src/routes/ui/node.rs index 7c89462..337d249 100644 --- a/server/src/routes/ui/node.rs +++ b/server/src/routes/ui/node.rs @@ -76,7 +76,6 @@ pub async fn r_library_node_filter<'a>( NodeKind::Channel => (SortProperty::ReleaseDate, SortOrder::Descending), _ => (SortProperty::Title, SortOrder::Ascending), }, - // TODO &mut children, ); diff --git a/server/src/routes/ui/sort.rs b/server/src/routes/ui/sort.rs index 3831431..06e0209 100644 --- a/server/src/routes/ui/sort.rs +++ b/server/src/routes/ui/sort.rs @@ -12,9 +12,9 @@ use std::sync::Arc; #[derive(FromForm, UriDisplayQuery, Default, Clone)] pub struct NodeFilterSort { - sort_by: Option, - filter_kind: Option>, - sort_order: Option, + pub sort_by: Option, + pub filter_kind: Option>, + pub sort_order: Option, } macro_rules! form_enum { -- cgit v1.2.3-70-g09d2