/* This file is part of jellything (https://codeberg.org/metamuffin/jellything) which is licensed under the GNU Affero General Public License (version 3); see /COPYING. Copyright (C) 2023 metamuffin */ use super::{assets::rocket_uri_macro_r_item_assets, error::MyError, player::player_uri}; use crate::{ database::Database, routes::{ api::AcceptJson, ui::{ account::session::Session, assets::AssetRole, layout::{DynLayoutPage, LayoutPage}, }, }, uri, }; use anyhow::{anyhow, Context}; use jellycommon::{Node, NodeKind}; use rocket::{get, serde::json::Json, Either, State}; #[get("/n/")] pub async fn r_library_node( _sess: Session, id: String, db: &State, aj: AcceptJson, ) -> Result, Json>, MyError> { let node = db .node .get(&id) .context("retrieving library node")? .ok_or(anyhow!("node does not exist"))?; if *aj { 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"))?, )) }) .collect::>>()? .into_iter() .collect(); Ok(Either::Left(LayoutPage { title: node.public.title.to_string(), show_back: matches!(node.public.kind, NodeKind::Movie | NodeKind::Episode), content: markup::new! { @NodePage { node: &node, id: &id, children: &children } }, ..Default::default() })) } markup::define! { NodePage<'a>(id: &'a str, node: &'a Node, children: &'a Vec<(String,Node)>) { @match node.public.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) { @PosterCard { wide: matches!(node.public.kind, NodeKind::Collection), dir: !matches!(node.public.kind, NodeKind::Movie | NodeKind::Episode), id, title: &node.public.title } } DirectoryPage<'a>(_id: &'a str, node: &'a Node, children: &'a Vec<(String,Node)>) { div.page.dir { h1 { @node.public.title } @if let Some(parent) = &node.public.parent { a.dirup[href=uri!(r_library_node(parent))] { "Go up" } } ul.directorylisting { @for (id, node) in children.iter() { li { @NodeCard { id, node } } } } } } PosterCard<'a>(id: &'a str, title: &'a str, wide: bool, dir: bool) { div[class=if *wide {"card wide poster"} else {"card poster"}] { div.banner { a[href=uri!(r_library_node(id))] { img[src=uri!(r_item_assets(id, AssetRole::Poster))]; } @if *dir { div.hoverdir { a[href=&uri!(r_library_node(id))] { "Open" } } } else { div.hoveritem { a[href=&player_uri(id)] { "▶" } } } } p.title { a[href=uri!(r_library_node(id))] { @title } } } } ItemPage<'a>(id: &'a str, node: &'a Node) { // TODO different image here img.backdrop[src=uri!(r_item_assets(id, AssetRole::Backdrop))]; div.page.item { div.banner { img[src=uri!(r_item_assets(id, AssetRole::Poster))]; } div.title { h1 { @node.public.title } // TODO release date, duration, ratings a.play[href=&player_uri(id)] { "Watch now" } } div.details { h3 { @node.public.tagline } p { @node.public.description } } } } SeriesPage<'a>(id: &'a str, node: &'a Node, children: &'a Vec<(String,Node)>) { // TODO different image here img.backdrop[src=uri!(r_item_assets(id, AssetRole::Backdrop))]; div.page.item { div.banner { img[src=uri!(r_item_assets(id, AssetRole::Poster))]; } div.title { h1 { @node.public.title } } div.details { h3 { @node.public.tagline } p { @node.public.description } } ol { @for (id, c) in children.iter() { li { a[href=uri!(r_library_node(id))] { @c.public.title } } } } } } }