diff options
Diffstat (limited to 'server/src/routes/ui/node.rs')
-rw-r--r-- | server/src/routes/ui/node.rs | 101 |
1 files changed, 65 insertions, 36 deletions
diff --git a/server/src/routes/ui/node.rs b/server/src/routes/ui/node.rs index cc31de2..2932bf3 100644 --- a/server/src/routes/ui/node.rs +++ b/server/src/routes/ui/node.rs @@ -12,6 +12,7 @@ use crate::{ database::Database, routes::{ api::AcceptJson, + progress::rocket_uri_macro_r_player_watched, ui::{ account::session::Session, assets::AssetRole, @@ -21,9 +22,12 @@ use crate::{ }, uri, }; -use anyhow::{anyhow, Context}; +use anyhow::{anyhow, Context, Result}; use jellybase::permission::NodePermissionExt; -use jellycommon::{MediaInfo, NodeKind, NodePublic, Rating, SourceTrackKind}; +use jellycommon::{ + user::{NodeUserData, WatchedState}, + MediaInfo, NodeKind, NodePublic, Rating, SourceTrackKind, +}; use rocket::{get, serde::json::Json, Either, State}; /// This function is a stub and only useful for use in the uri! macro. @@ -48,6 +52,11 @@ pub async fn r_library_node_filter<'a>( .ok_or(anyhow!("node does not exist"))? .public; + let udata = db + .user_node + .get(&(session.user.name.clone(), id.to_string()))? + .unwrap_or_default(); + if *aj { return Ok(Either::Right(Json(node))); } @@ -55,34 +64,24 @@ pub async fn r_library_node_filter<'a>( let mut children = node .children .iter() - .map(|c| { - Ok(( - c.to_owned(), - db.node - .get(c)? - .ok_or(anyhow!("child does not exist: {c}"))? - .public, - )) - }) + .map(|c| Ok(db.get_node_with_userdata(c, &session)?)) .collect::<anyhow::Result<Vec<_>>>()? .into_iter() .collect(); filter_and_sort_nodes(&filter, &mut children); - // node.media.unwrap().tracks[0]. - Ok(Either::Left(LayoutPage { title: node.title.to_string(), content: markup::new! { - @NodePage { node: &node, id: &id, children: &children, filter: &filter } + @NodePage { node: &node, id: &id, udata: &udata, children: &children, filter: &filter } }, ..Default::default() })) } markup::define! { - NodeCard<'a>(id: &'a str, node: &'a NodePublic) { + NodeCard<'a>(id: &'a str, node: &'a NodePublic, udata: &'a NodeUserData) { @let cls = format!("node card poster {}", match node.kind {NodeKind::Channel => "aspect-square", NodeKind::Video => "aspect-thumb", NodeKind::Collection => "aspect-land", _ => "aspect-port"}); div[class=cls] { .poster { @@ -93,22 +92,8 @@ markup::define! { @if !(matches!(node.kind, NodeKind::Collection | NodeKind::Channel)) { a.play.icon[href=&uri!(r_player(id, PlayerConfig::default()))] { "play_arrow" } } - @Props { node } + @Props { node, udata } } - // .inner { - // a[href=uri!(r_library_node(id))] { - // img[src=uri!(r_item_assets(id, AssetRole::Poster, Some(1024)))]; - // } - // div.details { - // h3 { @node.title } - // p.description { @node.description } - // @if matches!(node.kind, NodeKind::Collection | NodeKind::Channel) { - // a[href=&uri!(r_library_node(id))] { "Open" } - // } else { - // a.play[href=&uri!(r_player(id, PlayerConfig::default()))] { "Watch now" } - // } - // } - // } } div.title { a[href=uri!(r_library_node(id))] { @@ -117,7 +102,7 @@ markup::define! { } } } - NodePage<'a>(id: &'a str, node: &'a NodePublic, children: &'a Vec<(String, NodePublic)>, filter: &'a NodeFilterSort) { + NodePage<'a>(id: &'a str, node: &'a NodePublic, udata: &'a NodeUserData, children: &'a Vec<(String, NodePublic, NodeUserData)>, filter: &'a NodeFilterSort) { @if !matches!(node.kind, NodeKind::Collection) { img.backdrop[src=uri!(r_item_assets(id, AssetRole::Backdrop, Some(2048)))]; } @@ -128,9 +113,22 @@ markup::define! { .title { h1 { @node.title } @if node.media.is_some() { a.play[href=&uri!(r_player(id, PlayerConfig::default()))] { "Watch now" }} + @match udata.watched { + WatchedState::None | + WatchedState::Progress(_) => { + form.mark_watched[method="POST", action=uri!(r_player_watched(id, true))] { + input[type="submit", value="Mark Watched"]; + } + } + WatchedState::Watched => { + form.mark_unwatched[method="POST", action=uri!(r_player_watched(id, false))] { + input[type="submit", value="Mark Unwatched"]; + } + } + } } .details { - @Props { node } + @Props { node, udata } h3 { @node.tagline } @if let Some(description) = &node.description { p { @for line in description.lines() { @line br; } } @@ -154,12 +152,12 @@ markup::define! { } @match node.kind { NodeKind::Collection | NodeKind::Channel => { - ul.children {@for (id, node) in children.iter() { - li { @NodeCard { id, node } } + ul.children {@for (id, node, udata) in children.iter() { + li { @NodeCard { id, node, udata } } }} } NodeKind::Series => { - ol { @for (id, c) in children.iter() { + ol { @for (id, c, _) in children.iter() { li { a[href=uri!(r_library_node(id))] { @c.title } } }} } @@ -168,7 +166,7 @@ markup::define! { } } - Props<'a>(node: &'a NodePublic) { + Props<'a>(node: &'a NodePublic, udata: &'a NodeUserData) { .props { @if let Some(m) = &node.media { p { @format_duration(m.duration) } @@ -193,6 +191,11 @@ markup::define! { @if let Some(f) = &node.federated { p.federation { @f } } + @match udata.watched { + WatchedState::None => {} + WatchedState::Progress(x) => { p.progress { "Watched up to " @format_duration(x) } } + WatchedState::Watched => { p.watched { "Watched" } } + } } } } @@ -212,6 +215,32 @@ pub fn format_duration(mut d: f64) -> String { s } +pub trait DatabaseNodeUserDataExt { + fn get_node_with_userdata( + &self, + id: &str, + session: &Session, + ) -> Result<(String, NodePublic, NodeUserData)>; +} +impl DatabaseNodeUserDataExt for Database { + fn get_node_with_userdata( + &self, + id: &str, + session: &Session, + ) -> Result<(String, NodePublic, NodeUserData)> { + Ok(( + id.to_owned(), + self.node + .get(&id.to_owned())? + .ok_or(anyhow!("node does not exist: {id}"))? + .public, + self.user_node + .get(&(session.user.name.to_owned(), id.to_owned()))? + .unwrap_or_default(), + )) + } +} + trait MediaInfoExt { fn resolution_name(&self) -> &'static str; } |