From 46ef75431e1d34c63a690726a8ef584d175ddd30 Mon Sep 17 00:00:00 2001 From: metamuffin Date: Wed, 18 Jan 2023 16:16:03 +0100 Subject: restructure pages slightly --- server/src/frontend/mod.rs | 2 - server/src/frontend/pages/home.rs | 14 ------ server/src/frontend/pages/layout.rs | 20 -------- server/src/frontend/pages/mod.rs | 69 --------------------------- server/src/frontend/pages/node.rs | 61 ------------------------ server/src/frontend/style/cantarell.woff2 | Bin 93888 -> 0 bytes server/src/frontend/style/layout.css | 45 ------------------ server/src/frontend/style/mod.rs | 25 ---------- server/src/main.rs | 74 ++--------------------------- server/src/routes/mod.rs | 26 ++++++++++ server/src/routes/stream.rs | 46 ++++++++++++++++++ server/src/routes/ui/error.rs | 59 +++++++++++++++++++++++ server/src/routes/ui/home.rs | 14 ++++++ server/src/routes/ui/layout.rs | 20 ++++++++ server/src/routes/ui/mod.rs | 32 +++++++++++++ server/src/routes/ui/node.rs | 61 ++++++++++++++++++++++++ server/src/routes/ui/style/cantarell.woff2 | Bin 0 -> 93888 bytes server/src/routes/ui/style/layout.css | 45 ++++++++++++++++++ server/src/routes/ui/style/mod.rs | 51 ++++++++++++++++++++ 19 files changed, 359 insertions(+), 305 deletions(-) delete mode 100644 server/src/frontend/mod.rs delete mode 100644 server/src/frontend/pages/home.rs delete mode 100644 server/src/frontend/pages/layout.rs delete mode 100644 server/src/frontend/pages/mod.rs delete mode 100644 server/src/frontend/pages/node.rs delete mode 100644 server/src/frontend/style/cantarell.woff2 delete mode 100644 server/src/frontend/style/layout.css delete mode 100644 server/src/frontend/style/mod.rs create mode 100644 server/src/routes/mod.rs create mode 100644 server/src/routes/stream.rs create mode 100644 server/src/routes/ui/error.rs create mode 100644 server/src/routes/ui/home.rs create mode 100644 server/src/routes/ui/layout.rs create mode 100644 server/src/routes/ui/mod.rs create mode 100644 server/src/routes/ui/node.rs create mode 100644 server/src/routes/ui/style/cantarell.woff2 create mode 100644 server/src/routes/ui/style/layout.css create mode 100644 server/src/routes/ui/style/mod.rs (limited to 'server/src') diff --git a/server/src/frontend/mod.rs b/server/src/frontend/mod.rs deleted file mode 100644 index 043be24..0000000 --- a/server/src/frontend/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod pages; -pub mod style; diff --git a/server/src/frontend/pages/home.rs b/server/src/frontend/pages/home.rs deleted file mode 100644 index 5076177..0000000 --- a/server/src/frontend/pages/home.rs +++ /dev/null @@ -1,14 +0,0 @@ -use crate::frontend::pages::node::NodePage; -use crate::{frontend::pages::HtmlTemplate, AppState}; -use rocket::{get, State}; - -#[get("/")] -pub async fn page_home(state: &State) -> HtmlTemplate { - HtmlTemplate( - "Home".to_string(), - markup::new! { - p { "Welcome to Jellything" } - @NodePage { node: state.library.root.clone() } - }, - ) -} diff --git a/server/src/frontend/pages/layout.rs b/server/src/frontend/pages/layout.rs deleted file mode 100644 index 1085f84..0000000 --- a/server/src/frontend/pages/layout.rs +++ /dev/null @@ -1,20 +0,0 @@ -use markup::Render; - -markup::define! { - Layout(title: String, main: Main) { - @markup::doctype() - html { - head { - title { @title " - Jellything" } - link[rel="stylesheet", href="/assets/style.css"]; - } - body { - nav { - h1 { "Jellything" } - - } - #main { @main } - } - } - } -} diff --git a/server/src/frontend/pages/mod.rs b/server/src/frontend/pages/mod.rs deleted file mode 100644 index a20fa0e..0000000 --- a/server/src/frontend/pages/mod.rs +++ /dev/null @@ -1,69 +0,0 @@ -use markup::Render; -use rocket::{ - http::ContentType, - response::{self, Responder}, - Request, Response, -}; -use std::{fmt::Display, io::Cursor}; - -use self::layout::Layout; - -pub mod home; -pub mod layout; -pub mod node; - -pub struct HtmlTemplate(pub String, pub T); - -impl<'r, T: Render> Responder<'r, 'static> for HtmlTemplate { - fn respond_to(self, _: &'r Request<'_>) -> response::Result<'static> { - let mut out = String::new(); - Layout { - title: self.0, - main: self.1, - } - .render(&mut out) - .unwrap(); - Response::build() - .header(ContentType::HTML) - .streamed_body(Cursor::new(out)) - .ok() - } -} - -#[derive(Debug)] -pub struct MyError(anyhow::Error); - -impl<'r> Responder<'r, 'static> for MyError { - fn respond_to(self, _: &'r Request<'_>) -> response::Result<'static> { - let mut out = String::new(); - Layout { - title: "Error".to_string(), - main: markup::new! { - h2 { "An error occured. Nobody is sorry"} - pre.error { @format!("{:?}", self.0) } - }, - } - .render(&mut out) - .unwrap(); - Response::build() - .header(ContentType::HTML) - .streamed_body(Cursor::new(out)) - .ok() - } -} - -impl Display for MyError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.0.fmt(f) - } -} -impl From for MyError { - fn from(err: anyhow::Error) -> MyError { - MyError(err) - } -} -impl From for MyError { - fn from(err: std::fmt::Error) -> MyError { - MyError(anyhow::anyhow!("{err}")) - } -} diff --git a/server/src/frontend/pages/node.rs b/server/src/frontend/pages/node.rs deleted file mode 100644 index 6819bc9..0000000 --- a/server/src/frontend/pages/node.rs +++ /dev/null @@ -1,61 +0,0 @@ -use super::MyError; -use crate::rocket_uri_macro_stream; -use crate::{ - frontend::pages::HtmlTemplate, - library::{Directory, Item, Node}, - AppState, -}; -use anyhow::Context; -use rocket::{get, uri, State}; -use std::{ops::Deref, path::PathBuf, sync::Arc}; - -#[get("/library/")] -pub async fn page_library_node( - path: PathBuf, - state: &State, -) -> Result, MyError> { - let path = path.to_str().unwrap().to_string(); - let node = state - .library - .nested(&path) - .context("retrieving library node")? - .clone(); - Ok(HtmlTemplate( - format!("{}", node.title()), - markup::new! { - @NodePage { node: node.clone() } - }, - )) -} - -markup::define! { - NodePage(node: Arc) { - @match node.deref() { - Node::Directory(dir) => { @DirectoryPage { dir: dir.clone() } } - Node::Item(item) => { @ItemPage { item: item.clone() } } - } - } - DirectoryCard(dir: Arc) { - span { a[href=&uri!(page_library_node(&dir.lib_path)).to_string()] { @dir.data.name } } - } - DirectoryPage(dir: Arc) { - h1 { @dir.data.name } - ul.directorylisting { - @for el in &dir.children { - li { - span.title { @match el.deref().to_owned() { - Node::Directory(dir) => { @DirectoryCard { dir } } - Node::Item(item) => { @ItemCard { item } } - }} - } - } - } - } - ItemCard(item: Arc) { - span { a[href=&uri!(page_library_node(&item.lib_path)).to_string()] { @item.data.title } } - } - ItemPage(item: Arc) { - h1 { @item.data.title } - video[src=&uri!(stream(&item.lib_path, "1,2")).to_string(), controls] {} - } -} diff --git a/server/src/frontend/style/cantarell.woff2 b/server/src/frontend/style/cantarell.woff2 deleted file mode 100644 index 76fd894..0000000 Binary files a/server/src/frontend/style/cantarell.woff2 and /dev/null differ diff --git a/server/src/frontend/style/layout.css b/server/src/frontend/style/layout.css deleted file mode 100644 index 0612a7e..0000000 --- a/server/src/frontend/style/layout.css +++ /dev/null @@ -1,45 +0,0 @@ -@font-face { - font-family: 'Cantarell'; - src: url(/assets/cantarell.woff2) format('woff2'); -} - -* { - color: white; - font-family: "Cantarell", sans-serif; - font-weight: 300; - margin: 0px; - padding: 0px; -} - -body { - background-color: #1a1a1a; - width: 100vw; -} - -nav { - position: absolute; - top: 0px; - left: 0px; - padding: 1em; - width: calc(100vw - 2em); - height: 2em; - background-color: #41414144; -} - -nav h1 { - margin: 0px; - font-size: 1.5em; -} - -#main { - margin-top: 5em; - padding: 1em; - padding-left: 3em; - padding-right: 3em; -} - -.error { - padding: 1em; - color: rgb(255, 117, 117); - font-family: monospace; -} \ No newline at end of file diff --git a/server/src/frontend/style/mod.rs b/server/src/frontend/style/mod.rs deleted file mode 100644 index ca54aa7..0000000 --- a/server/src/frontend/style/mod.rs +++ /dev/null @@ -1,25 +0,0 @@ -use std::{ - fs::{read_to_string, File}, - io::Read, -}; - -pub fn css_bundle() -> String { - if cfg!(debug_assertions) { - read_to_string("server/src/frontend/style/layout.css").unwrap() - } else { - include_str!("layout.css").to_string() - } -} - -pub fn font_bundle() -> Vec { - if cfg!(debug_assertions) { - let mut woff = Vec::new(); - File::open("server/src/frontend/style/cantarell.woff2") - .unwrap() - .read_to_end(&mut woff) - .unwrap(); - woff - } else { - include_bytes!("cantarell.woff2").to_vec() - } -} diff --git a/server/src/main.rs b/server/src/main.rs index a76d80b..438051f 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -1,69 +1,15 @@ #![feature(box_syntax)] -use crate::frontend::pages::MyError; -use anyhow::{anyhow, Context}; use database::Database; -use frontend::{ - pages::{home::page_home, node::page_library_node}, - style::{css_bundle, font_bundle}, -}; use jellyremuxer::RemuxerContext; use library::Library; -use log::{debug, warn}; -use rocket::{get, http::ContentType, launch, response::stream::ReaderStream, routes, State}; -use std::{path::PathBuf, sync::Arc}; -use tokio::io::{duplex, DuplexStream}; -use tokio_util::io::SyncIoBridge; +use rocket::launch; +use routes::build_rocket; +use std::sync::Arc; pub mod database; -pub mod frontend; pub mod library; - -#[get("/assets/style.css")] -async fn assets_style() -> (ContentType, String) { - (ContentType::CSS, css_bundle()) -} - -#[get("/assets/cantarell.woff2")] -async fn assets_font() -> (ContentType, Vec) { - (ContentType::WOFF2, font_bundle()) -} - -#[get("/stream/?")] -fn stream( - path: PathBuf, - selection: String, - state: &State, -) -> Result<(ContentType, ReaderStream![DuplexStream]), MyError> { - let (a, b) = duplex(1024); - let path = path.to_str().unwrap().to_string(); - let item = state - .library - .nested(&path) - .context("retrieving library node")? - .get_item()?; - let remuxer = state.remuxer.clone(); - let selection = selection - .split(",") - .map(|e| e.parse().map_err(|_| anyhow!("invalid number"))) - .into_iter() - .collect::, _>>()?; - - let b = SyncIoBridge::new(b); - tokio::task::spawn_blocking(move || { - if let Err(e) = remuxer.generate_into( - b, - 0, - item.fs_path.parent().unwrap().to_path_buf(), - item.data.clone(), - selection, - ) { - warn!("stream stopped: {e}") - } - }); - debug!("starting stream"); - Ok((ContentType::WEBM, ReaderStream::one(a))) -} +pub mod routes; pub struct AppState { pub database: Database, @@ -81,15 +27,5 @@ fn rocket() -> _ { library: Library::open(&lib_path).unwrap(), database: Database::open(&db_path).unwrap(), }; - - rocket::build().manage(state).mount( - "/", - routes![ - page_home, - page_library_node, - assets_style, - assets_font, - stream - ], - ) + build_rocket(state) } diff --git a/server/src/routes/mod.rs b/server/src/routes/mod.rs new file mode 100644 index 0000000..4491e81 --- /dev/null +++ b/server/src/routes/mod.rs @@ -0,0 +1,26 @@ +use crate::AppState; +use rocket::{catchers, routes, Build, Rocket}; +use stream::r_stream; +use ui::error::r_not_found; +use ui::home::r_home; +use ui::node::r_library_node; +use ui::style::{r_assets_font, r_assets_style}; + +pub mod stream; +pub mod ui; + +pub fn build_rocket(state: AppState) -> Rocket { + rocket::build() + .manage(state) + .register("/", catchers![r_not_found]) + .mount( + "/", + routes![ + r_home, + r_library_node, + r_assets_style, + r_assets_font, + r_stream + ], + ) +} diff --git a/server/src/routes/stream.rs b/server/src/routes/stream.rs new file mode 100644 index 0000000..6ff6982 --- /dev/null +++ b/server/src/routes/stream.rs @@ -0,0 +1,46 @@ +use super::ui::error::MyError; +use crate::AppState; +use anyhow::anyhow; +use anyhow::Context; +use log::debug; +use log::warn; +use rocket::{get, http::ContentType, response::stream::ReaderStream, State}; +use std::path::PathBuf; +use tokio::io::{duplex, DuplexStream}; +use tokio_util::io::SyncIoBridge; + +#[get("/stream/?")] +pub fn r_stream( + path: PathBuf, + selection: String, + state: &State, +) -> Result<(ContentType, ReaderStream![DuplexStream]), MyError> { + let (a, b) = duplex(1024); + let path = path.to_str().unwrap().to_string(); + let item = state + .library + .nested(&path) + .context("retrieving library node")? + .get_item()?; + let remuxer = state.remuxer.clone(); + let selection = selection + .split(",") + .map(|e| e.parse().map_err(|_| anyhow!("invalid number"))) + .into_iter() + .collect::, _>>()?; + + let b = SyncIoBridge::new(b); + tokio::task::spawn_blocking(move || { + if let Err(e) = remuxer.generate_into( + b, + 0, + item.fs_path.parent().unwrap().to_path_buf(), + item.data.clone(), + selection, + ) { + warn!("stream stopped: {e}") + } + }); + debug!("starting stream"); + Ok((ContentType::WEBM, ReaderStream::one(a))) +} diff --git a/server/src/routes/ui/error.rs b/server/src/routes/ui/error.rs new file mode 100644 index 0000000..2a1f8e6 --- /dev/null +++ b/server/src/routes/ui/error.rs @@ -0,0 +1,59 @@ +use super::{layout::Layout, HtmlTemplate}; +use markup::Render; +use rocket::http::Status; +use rocket::{ + catch, + http::ContentType, + response::{self, Responder}, + Request, Response, +}; +use std::{fmt::Display, io::Cursor}; + +#[catch(default)] +pub fn r_not_found<'a>(status: Status, _request: &Request) -> HtmlTemplate> { + HtmlTemplate( + "Not found".to_string(), + markup::new! { + h2 { "Error" } + p { @format!("{status:?}") } + }, + ) +} + +#[derive(Debug)] +pub struct MyError(anyhow::Error); + +impl<'r> Responder<'r, 'static> for MyError { + fn respond_to(self, _: &'r Request<'_>) -> response::Result<'static> { + let mut out = String::new(); + Layout { + title: "Error".to_string(), + main: markup::new! { + h2 { "An error occured. Nobody is sorry"} + pre.error { @format!("{:?}", self.0) } + }, + } + .render(&mut out) + .unwrap(); + Response::build() + .header(ContentType::HTML) + .streamed_body(Cursor::new(out)) + .ok() + } +} + +impl Display for MyError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.0.fmt(f) + } +} +impl From for MyError { + fn from(err: anyhow::Error) -> MyError { + MyError(err) + } +} +impl From for MyError { + fn from(err: std::fmt::Error) -> MyError { + MyError(anyhow::anyhow!("{err}")) + } +} diff --git a/server/src/routes/ui/home.rs b/server/src/routes/ui/home.rs new file mode 100644 index 0000000..df95665 --- /dev/null +++ b/server/src/routes/ui/home.rs @@ -0,0 +1,14 @@ +use crate::routes::ui::node::NodePage; +use crate::{routes::ui::HtmlTemplate, AppState}; +use rocket::{get, State}; + +#[get("/")] +pub async fn r_home(state: &State) -> HtmlTemplate { + HtmlTemplate( + "Home".to_string(), + markup::new! { + p { "Welcome to Jellything" } + @NodePage { node: state.library.root.clone() } + }, + ) +} diff --git a/server/src/routes/ui/layout.rs b/server/src/routes/ui/layout.rs new file mode 100644 index 0000000..1085f84 --- /dev/null +++ b/server/src/routes/ui/layout.rs @@ -0,0 +1,20 @@ +use markup::Render; + +markup::define! { + Layout(title: String, main: Main) { + @markup::doctype() + html { + head { + title { @title " - Jellything" } + link[rel="stylesheet", href="/assets/style.css"]; + } + body { + nav { + h1 { "Jellything" } + + } + #main { @main } + } + } + } +} diff --git a/server/src/routes/ui/mod.rs b/server/src/routes/ui/mod.rs new file mode 100644 index 0000000..aa0259d --- /dev/null +++ b/server/src/routes/ui/mod.rs @@ -0,0 +1,32 @@ +use self::layout::Layout; +use markup::Render; +use rocket::{ + http::ContentType, + response::{self, Responder}, + Request, Response, +}; +use std::io::Cursor; + +pub mod error; +pub mod home; +pub mod layout; +pub mod node; +pub mod style; + +pub struct HtmlTemplate(pub String, pub T); + +impl<'r, T: Render> Responder<'r, 'static> for HtmlTemplate { + fn respond_to(self, _: &'r Request<'_>) -> response::Result<'static> { + let mut out = String::new(); + Layout { + title: self.0, + main: self.1, + } + .render(&mut out) + .unwrap(); + Response::build() + .header(ContentType::HTML) + .streamed_body(Cursor::new(out)) + .ok() + } +} diff --git a/server/src/routes/ui/node.rs b/server/src/routes/ui/node.rs new file mode 100644 index 0000000..b6d0d30 --- /dev/null +++ b/server/src/routes/ui/node.rs @@ -0,0 +1,61 @@ +use super::super::stream::rocket_uri_macro_r_stream; +use super::error::MyError; +use crate::{ + library::{Directory, Item, Node}, + routes::ui::HtmlTemplate, + AppState, +}; +use anyhow::Context; +use rocket::{get, uri, State}; +use std::{ops::Deref, path::PathBuf, sync::Arc}; + +#[get("/library/")] +pub async fn r_library_node( + path: PathBuf, + state: &State, +) -> Result, MyError> { + let path = path.to_str().unwrap().to_string(); + let node = state + .library + .nested(&path) + .context("retrieving library node")? + .clone(); + Ok(HtmlTemplate( + format!("{}", node.title()), + markup::new! { + @NodePage { node: node.clone() } + }, + )) +} + +markup::define! { + NodePage(node: Arc) { + @match node.deref() { + Node::Directory(dir) => { @DirectoryPage { dir: dir.clone() } } + Node::Item(item) => { @ItemPage { item: item.clone() } } + } + } + DirectoryCard(dir: Arc) { + span { a[href=&uri!(r_library_node(&dir.lib_path)).to_string()] { @dir.data.name } } + } + DirectoryPage(dir: Arc) { + h1 { @dir.data.name } + ul.directorylisting { + @for el in &dir.children { + li { + span.title { @match el.deref().to_owned() { + Node::Directory(dir) => { @DirectoryCard { dir } } + Node::Item(item) => { @ItemCard { item } } + }} + } + } + } + } + ItemCard(item: Arc) { + span { a[href=&uri!(r_library_node(&item.lib_path)).to_string()] { @item.data.title } } + } + ItemPage(item: Arc) { + h1 { @item.data.title } + video[src=&uri!(r_stream(&item.lib_path, "1,2")).to_string(), controls] {} + } +} diff --git a/server/src/routes/ui/style/cantarell.woff2 b/server/src/routes/ui/style/cantarell.woff2 new file mode 100644 index 0000000..76fd894 Binary files /dev/null and b/server/src/routes/ui/style/cantarell.woff2 differ diff --git a/server/src/routes/ui/style/layout.css b/server/src/routes/ui/style/layout.css new file mode 100644 index 0000000..0612a7e --- /dev/null +++ b/server/src/routes/ui/style/layout.css @@ -0,0 +1,45 @@ +@font-face { + font-family: 'Cantarell'; + src: url(/assets/cantarell.woff2) format('woff2'); +} + +* { + color: white; + font-family: "Cantarell", sans-serif; + font-weight: 300; + margin: 0px; + padding: 0px; +} + +body { + background-color: #1a1a1a; + width: 100vw; +} + +nav { + position: absolute; + top: 0px; + left: 0px; + padding: 1em; + width: calc(100vw - 2em); + height: 2em; + background-color: #41414144; +} + +nav h1 { + margin: 0px; + font-size: 1.5em; +} + +#main { + margin-top: 5em; + padding: 1em; + padding-left: 3em; + padding-right: 3em; +} + +.error { + padding: 1em; + color: rgb(255, 117, 117); + font-family: monospace; +} \ No newline at end of file diff --git a/server/src/routes/ui/style/mod.rs b/server/src/routes/ui/style/mod.rs new file mode 100644 index 0000000..f3d751b --- /dev/null +++ b/server/src/routes/ui/style/mod.rs @@ -0,0 +1,51 @@ +use rocket::{get, http::ContentType}; +use std::{ + fs::{read_to_string, File}, + io::Read, + path::PathBuf, + str::FromStr, +}; + +fn css_bundle() -> String { + if cfg!(debug_assertions) { + read_to_string( + PathBuf::from_str(file!()) + .unwrap() + .parent() + .unwrap() + .join("layout.css"), + ) + .unwrap() + } else { + include_str!("layout.css").to_string() + } +} +fn font_bundle() -> Vec { + if cfg!(debug_assertions) { + let mut woff = Vec::new(); + + File::open( + PathBuf::from_str(file!()) + .unwrap() + .parent() + .unwrap() + .join("cantarell.woff2"), + ) + .unwrap() + .read_to_end(&mut woff) + .unwrap(); + woff + } else { + include_bytes!("cantarell.woff2").to_vec() + } +} + +#[get("/assets/style.css")] +pub fn r_assets_style() -> (ContentType, String) { + (ContentType::CSS, css_bundle()) +} + +#[get("/assets/cantarell.woff2")] +pub fn r_assets_font() -> (ContentType, Vec) { + (ContentType::WOFF2, font_bundle()) +} -- cgit v1.2.3-70-g09d2