From df768b33d0098a479b21abe30853537b4be3a813 Mon Sep 17 00:00:00 2001 From: metamuffin Date: Thu, 3 Aug 2023 17:56:25 +0200 Subject: show flags on node --- server/src/import.rs | 1 + server/src/routes/ui/node.rs | 99 ++++++++++++++++++++++++--------- server/src/routes/ui/style/itempage.css | 9 +++ 3 files changed, 83 insertions(+), 26 deletions(-) (limited to 'server/src') diff --git a/server/src/import.rs b/server/src/import.rs index a059694..4750196 100644 --- a/server/src/import.rs +++ b/server/src/import.rs @@ -162,6 +162,7 @@ async fn import_remote( } node.public.parent = parent; + node.public.federated = Some(opts.host.clone()); info!("adding {identifier}"); db.node.insert(identifier, &node)?; diff --git a/server/src/routes/ui/node.rs b/server/src/routes/ui/node.rs index a784482..1d74b9e 100644 --- a/server/src/routes/ui/node.rs +++ b/server/src/routes/ui/node.rs @@ -17,7 +17,7 @@ use crate::{ uri, }; use anyhow::{anyhow, Context}; -use jellycommon::{Node, NodeKind, NodePublic}; +use jellycommon::{MediaInfo, NodeKind, NodePublic, SourceTrackKind}; use rocket::{get, serde::json::Json, Either, State}; #[get("/n/")] @@ -31,20 +31,23 @@ pub async fn r_library_node( .node .get(&id) .context("retrieving library node")? - .ok_or(anyhow!("node does not exist"))?; + .ok_or(anyhow!("node does not exist"))? + .public; if *aj { - return Ok(Either::Right(Json(node.public))); + return Ok(Either::Right(Json(node))); } let children = node - .public .children .iter() .map(|c| { Ok(( c.to_owned(), - db.node.get(c)?.ok_or(anyhow!("child does not exist"))?, + db.node + .get(c)? + .ok_or(anyhow!("child does not exist"))? + .public, )) }) .collect::>>()? @@ -52,8 +55,8 @@ pub async fn r_library_node( .collect(); Ok(Either::Left(LayoutPage { - title: node.public.title.to_string(), - show_back: matches!(node.public.kind, NodeKind::Movie | NodeKind::Episode), + title: node.title.to_string(), + show_back: matches!(node.kind, NodeKind::Movie | NodeKind::Episode), content: markup::new! { @NodePage { node: &node, id: &id, children: &children } }, @@ -62,25 +65,25 @@ pub async fn r_library_node( } markup::define! { - NodePage<'a>(id: &'a str, node: &'a Node, children: &'a Vec<(String,Node)>) { - @match node.public.kind { + NodePage<'a>(id: &'a str, node: &'a NodePublic, children: &'a Vec<(String, NodePublic)>) { + @match node.kind { NodeKind::Collection | NodeKind::Show | NodeKind::Season => { @DirectoryPage { node, _id: id, children } } NodeKind::Series => { @SeriesPage { node, children, id } } NodeKind::Movie | NodeKind::Episode => { @ItemPage { node, id } } } } - NodeCard<'a>(id: &'a str, node: &'a Node) { + NodeCard<'a>(id: &'a str, node: &'a NodePublic) { @PosterCard { - wide: matches!(node.public.kind, NodeKind::Collection), - dir: !matches!(node.public.kind, NodeKind::Movie | NodeKind::Episode), + wide: matches!(node.kind, NodeKind::Collection), + dir: !matches!(node.kind, NodeKind::Movie | NodeKind::Episode), id, - title: &node.public.title + title: &node.title } } - DirectoryPage<'a>(_id: &'a str, node: &'a Node, children: &'a Vec<(String,Node)>) { + DirectoryPage<'a>(_id: &'a str, node: &'a NodePublic, children: &'a Vec<(String, NodePublic)>) { div.page.dir { - h1 { @node.public.title } - @if let Some(parent) = &node.public.parent { + h1 { @node.title } + @if let Some(parent) = &node.parent { a.dirup[href=uri!(r_library_node(parent))] { "Go up" } } ul.directorylisting { @@ -109,7 +112,7 @@ markup::define! { } } } - ItemPage<'a>(id: &'a str, node: &'a Node) { + ItemPage<'a>(id: &'a str, node: &'a NodePublic) { // TODO different image here img.backdrop[src=uri!(r_item_assets(id, AssetRole::Backdrop))]; div.page.item { @@ -117,17 +120,22 @@ markup::define! { img[src=uri!(r_item_assets(id, AssetRole::Poster))]; } div.title { - h1 { @node.public.title } - // TODO release date, duration, ratings + h1 { @node.title } a.play[href=&player_uri(id)] { "Watch now" } } div.details { - h3 { @node.public.tagline } - p { @node.public.description } + div.props { + @if let Some(m) = &node.media { + p { @format_duration(m.duration) } + p { @m.resolution_name() } + } + } + h3 { @node.tagline } + p { @node.description } } } } - SeriesPage<'a>(id: &'a str, node: &'a Node, children: &'a Vec<(String,Node)>) { + SeriesPage<'a>(id: &'a str, node: &'a NodePublic, children: &'a Vec<(String, NodePublic)>) { // TODO different image here img.backdrop[src=uri!(r_item_assets(id, AssetRole::Backdrop))]; div.page.item { @@ -135,15 +143,54 @@ markup::define! { img[src=uri!(r_item_assets(id, AssetRole::Poster))]; } div.title { - h1 { @node.public.title } + h1 { @node.title } } div.details { - h3 { @node.public.tagline } - p { @node.public.description } + h3 { @node.tagline } + p { @node.description } } ol { @for (id, c) in children.iter() { - li { a[href=uri!(r_library_node(id))] { @c.public.title } } + li { a[href=uri!(r_library_node(id))] { @c.title } } } } } } } + +pub fn format_duration(mut d: f64) -> String { + let mut s = String::new(); + for (unit, k) in [("h", 60. * 60.), ("m", 60.), ("s", 1.)] { + let mut h = 0; + while d > k { + d -= k; + h += 1; + } + if h > 0 { + s += &format!("{h}{unit}") + } + } + s +} + +trait MediaInfoExt { + fn resolution_name(&self) -> &'static str; +} +impl MediaInfoExt for MediaInfo { + fn resolution_name(&self) -> &'static str { + let mut maxw = 0; + for t in &self.tracks { + match &t.kind { + SourceTrackKind::Video { width, .. } => maxw = maxw.max(*width), + _ => (), + } + } + + match maxw { + 7680.. => "8K", + 3840.. => "4K", + 1920.. => "Full HD", + 1280.. => "HD 720p", + 640.. => "NTSC", + _ => "Unkown", + } + } +} diff --git a/server/src/routes/ui/style/itempage.css b/server/src/routes/ui/style/itempage.css index 22b2774..2913ff7 100644 --- a/server/src/routes/ui/style/itempage.css +++ b/server/src/routes/ui/style/itempage.css @@ -53,3 +53,12 @@ .page.item .title .play::before { content: "▶"; } + +.page.item .props p { + margin: 0.4em; + font-size: small; + font-weight: bolder; + display: inline-block; + padding: 0.2em; + background: #ffffff15; +} -- cgit v1.2.3-70-g09d2