diff options
Diffstat (limited to 'server')
-rw-r--r-- | server/src/routes/mod.rs | 8 | ||||
-rw-r--r-- | server/src/routes/ui/assets.rs | 36 | ||||
-rw-r--r-- | server/src/routes/ui/node.rs | 52 |
3 files changed, 80 insertions, 16 deletions
diff --git a/server/src/routes/mod.rs b/server/src/routes/mod.rs index a6a086f..d607eef 100644 --- a/server/src/routes/mod.rs +++ b/server/src/routes/mod.rs @@ -33,11 +33,11 @@ use ui::{ r_admin_remove_invite, user::{r_admin_remove_user, r_admin_user, r_admin_user_permission, r_admin_users}, }, - assets::{r_item_assets, r_node_thumbnail}, + assets::{r_item_assets, r_node_thumbnail, r_person_asset}, browser::r_all_items_filter, error::{r_api_catch, r_catch}, home::{r_home, r_home_unpriv}, - node::r_library_node_filter, + node::{r_library_node_ext, r_library_node_filter}, player::r_player, style::{r_assets_font, r_assets_js, r_assets_js_map, r_assets_style}, }; @@ -46,8 +46,8 @@ use userdata::{r_node_userdata, r_player_progress, r_player_watched}; use self::playersync::{r_streamsync, PlayersyncChannels}; pub mod api; -pub mod stream; pub mod playersync; +pub mod stream; pub mod ui; pub mod userdata; @@ -98,8 +98,10 @@ pub fn build_rocket(database: DataAcid, federation: Federation) -> Rocket<Build> r_streamsync, r_favicon, r_item_assets, + r_person_asset, r_all_items_filter, r_library_node_filter, + r_library_node_ext, r_assets_style, r_assets_font, r_assets_js, diff --git a/server/src/routes/ui/assets.rs b/server/src/routes/ui/assets.rs index 05ddc7c..ebd9453 100644 --- a/server/src/routes/ui/assets.rs +++ b/server/src/routes/ui/assets.rs @@ -10,13 +10,13 @@ use crate::{ use anyhow::{anyhow, Context}; use jellybase::{ cache::async_cache_file, - database::{TableExt, T_NODE}, + database::{TableExt, T_NODE, T_NODE_EXTENDED}, federation::Federation, permission::NodePermissionExt, AssetLocationExt, }; pub use jellycommon::AssetRole; -use jellycommon::{AssetLocation, LocalTrack, SourceTrackKind, TrackSource}; +use jellycommon::{AssetLocation, LocalTrack, PeopleGroup, SourceTrackKind, TrackSource}; use log::info; use rocket::{get, http::ContentType, State}; use std::{path::PathBuf, str::FromStr}; @@ -56,6 +56,38 @@ pub async fn r_item_assets( Ok(asset_with_res(asset, width).await?) } +#[get("/n/<id>/person/<index>/asset?<group>&<width>")] +pub async fn r_person_asset( + session: Session, + db: &State<DataAcid>, + id: &str, + index: usize, + group: PeopleGroup, + width: Option<usize>, +) -> MyResult<(ContentType, CacheControlFile)> { + T_NODE + .get(&db, id)? + .only_if_permitted(&session.user.permissions) + .ok_or(anyhow!("node does not exist"))?; + + let ext = T_NODE_EXTENDED.get(db, id)?.unwrap_or_default(); + let app = ext + .people + .get(&group) + .ok_or(anyhow!("group has no members"))? + .get(index) + .ok_or(anyhow!("person does not exist"))?; + + let asset = app + .person + .asset + .as_ref() + .ok_or(anyhow!("no asset"))? + .to_owned(); + + Ok(asset_with_res(asset, width).await?) +} + // TODO this can create "federation recursion" because track selection cannot be relied on. #[get("/n/<id>/thumbnail?<t>&<width>")] pub async fn r_node_thumbnail( diff --git a/server/src/routes/ui/node.rs b/server/src/routes/ui/node.rs index 3820b14..fc12db3 100644 --- a/server/src/routes/ui/node.rs +++ b/server/src/routes/ui/node.rs @@ -5,7 +5,7 @@ */ use super::{ assets::{rocket_uri_macro_r_item_assets, rocket_uri_macro_r_node_thumbnail}, - error::MyError, + error::MyResult, sort::{filter_and_sort_nodes, NodeFilterSort, NodeFilterSortForm}, }; use crate::{ @@ -14,7 +14,7 @@ use crate::{ api::AcceptJson, ui::{ account::session::Session, - assets::AssetRole, + assets::{rocket_uri_macro_r_person_asset, AssetRole}, layout::{DynLayoutPage, LayoutPage}, player::{rocket_uri_macro_r_player, PlayerConfig}, }, @@ -25,12 +25,12 @@ use crate::{ use anyhow::{anyhow, Result}; use chrono::NaiveDateTime; use jellybase::{ - database::{TableExt, T_NODE, T_USER_NODE}, + database::{TableExt, T_NODE, T_NODE_EXTENDED, T_USER_NODE}, permission::NodePermissionExt, }; use jellycommon::{ user::{NodeUserData, WatchedState}, - Chapter, MediaInfo, NodeKind, NodePublic, Rating, SourceTrackKind, + Chapter, ExtendedNode, MediaInfo, NodeKind, NodePublic, PeopleGroup, Rating, SourceTrackKind, }; use rocket::{get, serde::json::Json, Either, State}; @@ -40,6 +40,20 @@ pub fn r_library_node(id: String) { drop(id) } +#[get("/n/<id>/extended")] +pub async fn r_library_node_ext<'a>( + session: Session, + id: &'a str, + db: &'a State<DataAcid>, +) -> MyResult<Json<ExtendedNode>> { + T_NODE + .get(&db, id)? + .only_if_permitted(&session.user.permissions) + .ok_or(anyhow!("node does not exist"))?; + + Ok(Json(T_NODE_EXTENDED.get(db, id)?.unwrap_or_default())) +} + #[get("/n/<id>?<filter..>")] pub async fn r_library_node_filter<'a>( session: Session, @@ -47,12 +61,13 @@ pub async fn r_library_node_filter<'a>( db: &'a State<DataAcid>, aj: AcceptJson, filter: NodeFilterSort, -) -> Result<Either<DynLayoutPage<'a>, Json<NodePublic>>, MyError> { +) -> MyResult<Either<DynLayoutPage<'a>, Json<NodePublic>>> { let node = T_NODE .get(&db, id)? .only_if_permitted(&session.user.permissions) .ok_or(anyhow!("node does not exist"))? .public; + let node_ext = T_NODE_EXTENDED.get(db, id)?.unwrap_or_default(); let udata = T_USER_NODE .get(&db, &(session.user.name.as_str(), id))? @@ -75,7 +90,7 @@ pub async fn r_library_node_filter<'a>( Ok(Either::Left(LayoutPage { title: node.title.clone().unwrap_or_default(), content: markup::new! { - @NodePage { node: &node, id: &id, udata: &udata, children: &children, filter: &filter } + @NodePage { node: &node, id: &id, udata: &udata, children: &children, filter: &filter, node_ext: &node_ext } }, ..Default::default() })) @@ -87,7 +102,7 @@ markup::define! { div[class=cls] { .poster { a[href=uri!(r_library_node(id))] { - img[src=uri!(r_item_assets(id, AssetRole::Poster, Some(1024)))]; + img[src=uri!(r_item_assets(id, AssetRole::Poster, Some(1024))), loading="lazy"]; } .cardhover.item { @if !(matches!(node.kind.unwrap_or_default(), NodeKind::Collection | NodeKind::Channel)) { @@ -103,13 +118,13 @@ markup::define! { } } } - NodePage<'a>(id: &'a str, node: &'a NodePublic, udata: &'a NodeUserData, children: &'a Vec<(String, NodePublic, NodeUserData)>, filter: &'a NodeFilterSort) { + NodePage<'a>(id: &'a str, node: &'a NodePublic, node_ext: &'a ExtendedNode, udata: &'a NodeUserData, children: &'a Vec<(String, NodePublic, NodeUserData)>, filter: &'a NodeFilterSort) { @if !matches!(node.kind.unwrap_or_default(), NodeKind::Collection) { - img.backdrop[src=uri!(r_item_assets(id, AssetRole::Backdrop, Some(2048)))]; + img.backdrop[src=uri!(r_item_assets(id, AssetRole::Backdrop, Some(2048))), loading="lazy"]; } .page.node { @if !matches!(node.kind.unwrap_or_default(), NodeKind::Collection) { - div.bigposter { img[src=uri!(r_item_assets(id, AssetRole::Poster, Some(2048)))]; } + div.bigposter { img[src=uri!(r_item_assets(id, AssetRole::Poster, Some(2048))), loading="lazy"]; } } .title { h1 { @node.title } @@ -151,7 +166,7 @@ markup::define! { li { .card."aspect-thumb" { .poster { a[href=&uri!(r_player(id, PlayerConfig::seek(chap.time_start.unwrap_or(0.))))] { - img[src=&uri!(r_node_thumbnail(id, chapter_key_time(chap, media.duration), Some(1024)))]; + img[src=&uri!(r_node_thumbnail(id, chapter_key_time(chap, media.duration), Some(1024))), loading="lazy"]; } .cardhover { .props { p { @inl } } } } @@ -159,6 +174,21 @@ markup::define! { }} }}} } + h2 { "Cast & Crew" } + @for (group, people) in &node_ext.people { + h3 { @format!("{:?}", group) } + .hlist { ul.children { @for (i, pe) in people.iter().enumerate() { + li { .card."aspect-port" { + .poster { + a[href="#"] { + img[src=&uri!(r_person_asset(id, i, group, Some(1024))), loading="lazy"]; + } + // .cardhover { .props { p { @pe.person.name } } } + } + .title { @pe.person.name } + }} + }}} + } details { summary { "Tracks" } ol { @for track in &media.tracks { |