/* 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::error::MyError; use super::player::player_uri; use crate::uri; use crate::{ library::{Directory, Item, Library, Node}, routes::ui::{ account::session::Session, layout::{DynLayoutPage, LayoutPage}, }, }; use anyhow::Context; use jellycommon::DirectoryKind; use log::info; use rocket::{get, http::ContentType, State}; use rocket::{FromFormField, UriDisplayQuery}; use std::{path::PathBuf, sync::Arc}; use tokio::fs::File; #[get("/library/")] pub async fn r_library_node( _sess: Session, path: PathBuf, library: &State, ) -> Result, MyError> { let node = library .nested_path(&path) .context("retrieving library node")?; Ok(LayoutPage { title: node.common().title.to_string(), show_back: node.get_item().is_ok(), content: markup::new! { @NodePage { node: &node } }, ..Default::default() }) } markup::define! { NodePage<'a>(node: &'a Arc) { @match node.as_ref() { Node::Directory(dir) => { @match dir.info.kind { DirectoryKind::Series => { @SeriesPage { dir } } _ => { @DirectoryPage { dir } } } } Node::Item(item) => { @ItemPage { item } } } } NodeCard<'a>(node: &'a Arc) { @match node.as_ref() { Node::Directory(dir) => {@PosterCard { wide: !matches!(dir.info.kind, DirectoryKind::Series | DirectoryKind::Season), dir: true, path: dir.lib_path.clone(), title: &dir.info.title }} Node::Item(item) => {@PosterCard { wide: false, dir: false, path: item.lib_path.clone(), title: &item.info.title }} } } DirectoryPage<'a>(dir: &'a Arc) { div.page.dir { h1 { @dir.info.title } @if let Some(parent) = dir.lib_path.parent() { a.dirup[href=uri!(r_library_node(&parent))] { "Go up" } } ul.directorylisting { @for node in &dir.children { li { @NodeCard { node } } } } } } PosterCard<'a>(path: PathBuf, 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(path))] { img[src=uri!(r_item_assets(path, AssetRole::Poster))]; } @if *dir { div.hoverdir { a[href=&uri!(r_library_node(path))] { "Open" } } } else { div.hoveritem { a[href=&player_uri(path)] { "▶" } } } } p.title { a[href=uri!(r_library_node(path))] { @title } } } } ItemPage<'a>(item: &'a Arc) { // TODO different image here img.backdrop[src=uri!(r_item_assets(&item.lib_path, AssetRole::Backdrop))]; div.page.item { div.banner { img[src=uri!(r_item_assets(&item.lib_path, AssetRole::Poster))]; } div.title { h1 { @item.info.title } // TODO release date, duration, ratings a.play[href=&player_uri(&item.lib_path)] { "Watch now" } } div.details { h3 { @item.info.tagline } p { @item.info.description } } } } SeriesPage<'a>(dir: &'a Arc) { // TODO different image here img.backdrop[src=uri!(r_item_assets(&dir.lib_path, AssetRole::Backdrop))]; div.page.item { div.banner { img[src=uri!(r_item_assets(&dir.lib_path, AssetRole::Poster))]; } div.title { h1 { @dir.info.title } } div.details { h3 { @dir.info.tagline } p { @dir.info.description } } ol { @for ep in &dir.children { li { a[href=uri!(r_library_node(ep.lib_path()))] { @ep.common().title } } } } } } } #[derive(FromFormField, UriDisplayQuery)] pub enum AssetRole { Poster, Backdrop, } #[get("/item_assets/?")] pub async fn r_item_assets( _sess: Session, path: PathBuf, role: AssetRole, library: &State, ) -> Result<(ContentType, File), MyError> { let node = library .nested_path(&path) .context("retrieving library node")?; let path = node.get_asset(library, role); info!("loading asset from {path:?}"); let ext = path.extension().unwrap().to_str().unwrap(); Ok(( ContentType::from_extension(ext).unwrap(), File::open(path).await?, )) }