aboutsummaryrefslogtreecommitdiff
path: root/server/src/routes/ui/node.rs
diff options
context:
space:
mode:
Diffstat (limited to 'server/src/routes/ui/node.rs')
-rw-r--r--server/src/routes/ui/node.rs99
1 files changed, 73 insertions, 26 deletions
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/<id>")]
@@ -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::<anyhow::Result<Vec<_>>>()?
@@ -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",
+ }
+ }
+}