From 59dae90b4d58a470d53627f4ee4b8036d257c224 Mon Sep 17 00:00:00 2001 From: JMARyA Date: Sun, 9 Feb 2025 07:09:20 +0100 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.lock | 125 +++++++++++++++++++++------------- Cargo.toml | 1 + README.md | 2 + config.toml | 7 ++ docker-compose.yml | 1 - src/config.rs | 12 ++++ src/main.rs | 28 +++++--- src/pages/assets.rs | 26 +++++-- src/pages/components.rs | 76 ++++++++++++++++----- src/pages/index.rs | 146 ++++++++++++++++++++++++++++++---------- src/pages/user.rs | 44 ++++++++---- src/pages/watch.rs | 31 ++++++--- src/scripts/header.js | 7 -- 13 files changed, 368 insertions(+), 138 deletions(-) create mode 100644 README.md create mode 100644 config.toml create mode 100644 src/config.rs delete mode 100644 src/scripts/header.js diff --git a/Cargo.lock b/Cargo.lock index a998cb3..ea4cf6b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -71,9 +71,9 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.85" +version = "0.1.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f934833b4b7233644e5848f235df3f57ed8c80f1528a26c3dfa13d2147fa056" +checksum = "644dd749086bf3771a2fbc5f256fdb982d53f011c7d5d560304eafeecebce79d" dependencies = [ "proc-macro2", "quote", @@ -146,7 +146,7 @@ checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" [[package]] name = "based" version = "0.1.0" -source = "git+https://git.hydrar.de/jmarya/based?branch=ui#6a39c0441d72cbaa1f9864af9c53ac41362f549e" +source = "git+https://git.hydrar.de/jmarya/based?branch=ui#c05d0dcc0a46e721e8664a15e6e2f264aa8d4b53" dependencies = [ "bcrypt", "chrono", @@ -180,7 +180,7 @@ checksum = "2b1866ecef4f2d06a0bb77880015fdf2b89e25a1c2e5addacb87e459c86dc67e" dependencies = [ "base64 0.22.1", "blowfish", - "getrandom", + "getrandom 0.2.15", "subtle", "zeroize", ] @@ -227,9 +227,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.16.0" +version = "3.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" [[package]] name = "bytemuck" @@ -245,15 +245,15 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" +checksum = "f61dac84819c6588b558454b194026eb1f09c293b9036ae9b159e74e73ab6cf9" [[package]] name = "cc" -version = "1.2.10" +version = "1.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13208fcbb66eaeffe09b99fffbe1af420f00a7b35aa99ad683dfc1aa76145229" +checksum = "c7777341816418c02e033934a09f20dc0ccaf65a5201ef8a450ae0105a573fda" dependencies = [ "shlex", ] @@ -333,9 +333,9 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpufeatures" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" dependencies = [ "libc", ] @@ -768,7 +768,19 @@ checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.13.3+wasi-0.2.2", + "windows-targets 0.52.6", ] [[package]] @@ -914,9 +926,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.9.5" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" +checksum = "f2d708df4e7140240a16cd6ab0ab65c972d7433ab77819ea693fde9c43811e2a" [[package]] name = "httpdate" @@ -1331,7 +1343,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" dependencies = [ "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys 0.52.0", ] @@ -1356,9 +1368,9 @@ dependencies = [ [[package]] name = "native-tls" -version = "0.2.12" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" +checksum = "0dab59f8e050d5df8e4dd87d9206fb6f65a483e20ac9fda365ade4fab353196c" dependencies = [ "libc", "log", @@ -1455,15 +1467,15 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.20.2" +version = "1.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" +checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" [[package]] name = "openssl" -version = "0.10.68" +version = "0.10.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5" +checksum = "61cfb4e166a8bb8c9b55c500bc2308550148ece889be90f609377e58140f42c6" dependencies = [ "bitflags 2.8.0", "cfg-if", @@ -1487,15 +1499,15 @@ dependencies = [ [[package]] name = "openssl-probe" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "openssl-sys" -version = "0.9.104" +version = "0.9.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741" +checksum = "8b22d5b84be05a8d6947c7cb71f7c849aa0f112acd4bf51c2a7c1c988ac0a9dc" dependencies = [ "cc", "libc", @@ -1711,7 +1723,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom", + "getrandom 0.2.15", ] [[package]] @@ -2017,9 +2029,9 @@ checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" [[package]] name = "ryu" -version = "1.0.18" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd" [[package]] name = "same-file" @@ -2096,9 +2108,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.137" +version = "1.0.138" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "930cfb6e6abf99298aaad7d29abbef7a9999a9a8806a40088f55f0dcec03146b" +checksum = "d434192e7da787e94a6ea7e9670b26a036d0ca41e0b7efb2676dd32bae872949" dependencies = [ "itoa", "memchr", @@ -2475,9 +2487,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.96" +version = "2.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" +checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" dependencies = [ "proc-macro2", "quote", @@ -2524,13 +2536,13 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.15.0" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704" +checksum = "38c246215d7d24f48ae091a2902398798e05d978b24315d6efbc00ede9a8bb91" dependencies = [ "cfg-if", "fastrand", - "getrandom", + "getrandom 0.3.1", "once_cell", "rustix", "windows-sys 0.59.0", @@ -2696,9 +2708,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.19" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" +checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148" dependencies = [ "serde", "serde_spanned", @@ -2717,9 +2729,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.22" +version = "0.22.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" +checksum = "02a8b472d1a3d7c18e2d61a489aee3453fd9031c33e4f55bd533f4a7adca1bee" dependencies = [ "indexmap", "serde", @@ -2851,9 +2863,9 @@ checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" [[package]] name = "unicode-ident" -version = "1.0.14" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" +checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" [[package]] name = "unicode-normalization" @@ -2907,11 +2919,11 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "uuid" -version = "1.12.1" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3758f5e68192bb96cc8f9b7e2c2cfdabb435499a28499a42f8f984092adad4b" +checksum = "ced87ca4be083373936a67f8de945faa23b6b42384bd5b64434850802c6dccd0" dependencies = [ - "getrandom", + "getrandom 0.3.1", "serde", ] @@ -2958,6 +2970,15 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasi" +version = "0.13.3+wasi-0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" +dependencies = [ + "wit-bindgen-rt", +] + [[package]] name = "wasite" version = "0.1.0" @@ -3058,6 +3079,7 @@ dependencies = [ "serde_json", "sqlx", "tokio", + "toml", "uuid", "walkdir", ] @@ -3281,9 +3303,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.6.24" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8d71a593cc5c42ad7876e2c1fda56f314f3754c084128833e64f1345ff8a03a" +checksum = "86e376c75f4f43f44db463cf729e0d3acbf954d13e22c51e26e4c264b4ab545f" dependencies = [ "memchr", ] @@ -3298,6 +3320,15 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "wit-bindgen-rt" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" +dependencies = [ + "bitflags 2.8.0", +] + [[package]] name = "write16" version = "1.0.0" diff --git a/Cargo.toml b/Cargo.toml index 702ac41..af1c971 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,3 +25,4 @@ rand = "0.8.5" data-encoding = "2.6.0" bcrypt = "0.16.0" based = { git = "https://git.hydrar.de/jmarya/based", features = ["cache"], branch = "ui" } +toml = "0.8.20" diff --git a/README.md b/README.md new file mode 100644 index 0000000..4295e91 --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# WatchDogs +WatchDogs is a video server diff --git a/config.toml b/config.toml new file mode 100644 index 0000000..bbfe29d --- /dev/null +++ b/config.toml @@ -0,0 +1,7 @@ + +[general] +# Private Instance (Login required for watching content) +private = true + +# Path to videos +video_path = "./videos" diff --git a/docker-compose.yml b/docker-compose.yml index 6ec048d..19f4f77 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,7 +11,6 @@ services: - "DATABASE_URL=postgres://user:pass@postgres/watchdogs" - "RUST_LOG=info" - "ROCKET_ADDRESS=0.0.0.0" - command: "/watchdogs /videos" postgres: image: timescale/timescaledb:latest-pg16 diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..8d32d98 --- /dev/null +++ b/src/config.rs @@ -0,0 +1,12 @@ +use serde::Deserialize; + +#[derive(Debug, Clone, Deserialize)] +pub struct Config { + pub general: GeneralConfig, +} + +#[derive(Debug, Clone, Deserialize)] +pub struct GeneralConfig { + pub private: bool, + pub video_path: String, +} diff --git a/src/main.rs b/src/main.rs index 5c34c03..4e928ff 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,10 +1,17 @@ -use based::{asset::AssetRoutes, auth::User, get_pg}; +use based::{ + asset::AssetRoutes, + auth::User, + get_pg, + ui::components::{AppBar, Shell}, +}; use rocket::{http::Method, routes}; use std::path::Path; +mod config; mod library; mod meta; mod pages; mod yt_meta; +use based::ui::prelude::*; #[rocket::launch] async fn launch() -> _ { @@ -12,14 +19,10 @@ async fn launch() -> _ { std::env::set_var("RUST_BACKTRACE", "1"); env_logger::init(); - let args: Vec = std::env::args().collect(); + let conf: config::Config = + toml::from_str(&std::fs::read_to_string("config.toml").unwrap()).unwrap(); - if args.len() != 2 { - eprintln!("Usage: {} ", args[0]); - std::process::exit(1); - } - - let dir_path = args[1].clone(); + let dir_path = conf.general.video_path.clone(); let pg = get_pg!(); @@ -50,6 +53,13 @@ async fn launch() -> _ { .to_cors() .expect("error creating CORS options"); + let shell = Shell::new( + Nothing(), + Nothing(), + Background(Text("").white()).color(Colors::Black), + ) + .use_ui(); + rocket::build() .mount_assets() .mount( @@ -74,4 +84,6 @@ async fn launch() -> _ { ) .attach(cors) .manage(lib) + .manage(conf) + .manage(shell) } diff --git a/src/pages/assets.rs b/src/pages/assets.rs index cbe1366..ed9e513 100644 --- a/src/pages/assets.rs +++ b/src/pages/assets.rs @@ -1,12 +1,21 @@ -use based::request::assets::DataResponse; +use based::{auth::MaybeUser, request::assets::DataResponse}; use rocket::{get, State}; use tokio::{fs::File, io::AsyncReadExt}; -use crate::library::Library; +use crate::{config::Config, library::Library}; #[get("/video/raw?")] -pub async fn video_file(v: &str, library: &State) -> Option { +pub async fn video_file( + v: &str, + library: &State, + conf: &State, + user: MaybeUser, +) -> Option { + if conf.general.private && user.user().is_none() { + return None; + } + let video = if let Some(video) = library.get_video_by_id(v).await { video } else { @@ -33,7 +42,16 @@ pub async fn video_file(v: &str, library: &State) -> Option")] -pub async fn video_thumbnail(v: &str, library: &State) -> Option { +pub async fn video_thumbnail( + v: &str, + library: &State, + conf: &State, + user: MaybeUser, +) -> Option { + if conf.general.private && user.user().is_none() { + return None; + } + let video = if let Some(video) = library.get_video_by_id(v).await { video } else { diff --git a/src/pages/components.rs b/src/pages/components.rs index 3a4d1bb..c09ab19 100644 --- a/src/pages/components.rs +++ b/src/pages/components.rs @@ -1,5 +1,9 @@ use crate::library::Video; -use based::ui::components::{AppBar, Shell}; +use based::ui::components::prelude::Avatar; +use based::ui::components::prelude::*; +use based::ui::components::{AppBar, Card, NavBar, Shell}; +use based::ui::primitives::flex::Column; +use based::ui::primitives::Optional; use based::ui::wrapper::HoverWrapper; use based::ui::{prelude::*, UIWidget}; use based::{ @@ -10,22 +14,37 @@ use based::{ use maud::{html, PreEscaped, Render}; pub async fn render_page( + shell: &Shell, ctx: RequestContext, content: PreEscaped, title: &str, user: Option, ) -> StringResponse { - Shell::new( - Nothing(), - Div() - .vanish() - .push(script(include_str!("../scripts/header.js"))) - .push(AppBar("WatchDogs", user)), - Background(Text("").white()).color(Colors::Black), - ) - .use_ui() - .render_page(content, title, ctx) - .await + shell + .extend() + .with_navbar( + NavBar("WatchDogs") + .icon(Sized( + ScreenValue::_8, + ScreenValue::_8, + Rounded(Image("/favicon").alt("Logo")).size(Size::Medium), + )) + .extra(Optional(user.as_ref(), |user| { + // TODO : profile pictures + DropDown( + Avatar("", &user.username).use_initials(), + Column(vec![Link( + "/history", + Padding(Text("Video History").medium()) + .x(ScreenValue::_4) + .y(ScreenValue::_2), + ) + .use_htmx()]), + ) + })), + ) + .render_page(content, title, ctx) + .await } #[allow(non_snake_case)] @@ -48,7 +67,18 @@ pub fn Title(title: T) -> PreEscaped { } #[allow(non_snake_case)] -pub fn VerticalVideoGrid(inner: T) -> PreEscaped { +pub async fn VerticalVideoGrid(videos: &mut [Video]) -> PreEscaped { + let video_elements = html! { + @for mut vid in videos { + ( video_element(&mut vid).await ); + }; + }; + + VerticalVideoGridRaw(video_elements) +} + +#[allow(non_snake_case)] +pub fn VerticalVideoGridRaw(inner: T) -> PreEscaped { Screen::large(Grid(Nothing()).columns(GridAmount::_3).gap(ScreenValue::_6)) .on(Padding(Margin(inner).bottom(ScreenValue::_4)).all(ScreenValue::_6)) .render() @@ -141,7 +171,7 @@ pub fn video_thumbnail_with_time(video: &Video) -> PreEscaped { Position( PositionKind::Relative, Div() - .push(Context(Width( + .push(Context(ObjectFit::Cover(Width( ScreenValue::full, Height( ScreenValue::auto, @@ -151,7 +181,7 @@ pub fn video_thumbnail_with_time(video: &Video) -> PreEscaped { .side(Side::Top), ), ), - ))) + )))) .push(Context( Position( PositionKind::Absolute, @@ -216,3 +246,19 @@ pub async fn video_element(video: &mut Video) -> PreEscaped { ) .render() } + +#[allow(non_snake_case)] +pub fn DirectoryBadge(dir: &str) -> PreEscaped { + Margin( + Padding( + Hover(Background(Cursor::Pointer.on(Nothing())).color(Purple::_600)).on(Background( + Rounded(Link(&format!("/d/{dir}"), Text(dir).white()).use_htmx()).size(Size::Full), + ) + .color(Purple::_500)), + ) + .x(ScreenValue::_3) + .y(ScreenValue::_2), + ) + .all(ScreenValue::_2) + .render() +} diff --git a/src/pages/index.rs b/src/pages/index.rs index 256c266..30784a6 100644 --- a/src/pages/index.rs +++ b/src/pages/index.rs @@ -1,4 +1,7 @@ -use based::ui::htmx::HTMXAttributes; +use based::page; +use based::request::respond_html; +use based::ui::components::prelude::InfinityScroll; +use based::ui::components::{ColoredSpinner, Shell}; use based::ui::prelude::*; use based::{ auth::MaybeUser, @@ -7,24 +10,42 @@ use based::{ RequestContext, StringResponse, }, }; -use maud::{html, Render}; -use rocket::{get, State}; +use maud::{html, PreEscaped, Render}; +use rocket::{get, uri, State}; use serde_json::json; -use crate::pages::components::{Title, VerticalVideoGrid}; +use crate::config::Config; +use crate::library::Video; +use crate::pages::components::{Title, VerticalVideoGrid, VerticalVideoGridRaw}; use crate::{library::Library, pages::components::video_element}; +use super::components::DirectoryBadge; use super::{ api_response, components::{render_page, video_element_wide}, }; +#[macro_export] +macro_rules! check_private { + ($conf:ident, $user:ident, $shell:ident, $ctx:ident) => { + if $conf.general.private && $user.user().is_none() { + return $crate::pages::index::is_private_page($shell, $ctx).await; + } + }; +} + #[get("/search?&")] pub async fn search( query: &str, offset: Option, library: &State, + conf: &State, + user: MaybeUser, + shell: &State, + ctx: RequestContext, ) -> Option { + // todo : check_private!(conf, user, shell, ctx); + const NUM_OF_RESULTS: i64 = 20; // get start parameter for search result chunks @@ -36,32 +57,87 @@ pub async fn search( } #[get("/latest.json")] -pub async fn latest_api(library: &State) -> FallibleApiResponse { +pub async fn latest_api( + library: &State, + user: MaybeUser, + conf: &State, + shell: &State, + ctx: RequestContext, +) -> FallibleApiResponse { + // todo : check_private!(conf, user, shell, ctx); + let videos = library.get_newly_added(20).await; let vid_api = vec_to_api(&videos).await; return Ok(serde_json::json!(vid_api)); } -#[get("/latest")] -pub async fn latest_page( - ctx: RequestContext, - library: &State, - user: MaybeUser, -) -> StringResponse { - let videos = library.get_newly_added(20).await; +#[allow(non_snake_case)] +pub async fn VideoList(videos: &[Video]) -> PreEscaped { let video_elements = html! { @for mut vid in videos { ( video_element_wide(&mut vid).await ); }; }; + Padding(video_elements).all(ScreenValue::_6).render() +} + +#[get("/latest?")] +pub async fn latest_page( + ctx: RequestContext, + library: &State, + user: MaybeUser, + shell: &State, + conf: &State, + offset: Option, +) -> StringResponse { + check_private!(conf, user, shell, ctx); + + let mut videos: Vec<_> = library + .get_newly_added(offset.unwrap_or_default() as i64 + 20) + .await + .into_iter() + .skip(offset.unwrap_or_default() as usize) + .take(20) + .collect(); + let has_content = !videos.is_empty(); + let video_elements = VerticalVideoGrid(&mut videos).await; + + if ctx.is_htmx && !ctx.htmx_redirect { + return respond_html( + Div() + .vanish() + .push(video_elements) + .push_if(has_content, || { + Width( + ScreenValue::fit, + Margin(InfinityScroll( + ColoredSpinner(Purple::_600), + &format!("/latest?offset={}", offset.unwrap_or_default() + 20), + )) + .x(ScreenValue::auto), + ) + }) + .render() + .0, + ); + } + let content = Div() .vanish() .push(Title("Recent videos")) - .push(Padding(video_elements).all(ScreenValue::_6)) + .push(video_elements) + .push(Width( + ScreenValue::fit, + Margin(InfinityScroll( + ColoredSpinner(Purple::_600), + &format!("/latest?offset={}", offset.unwrap_or_default() + 20), + )) + .x(ScreenValue::auto), + )) .render(); - render_page(ctx, content, "Recent videos", user.into()).await + render_page(&shell, ctx, content, "Recent videos", user.into()).await } #[get("/d/")] @@ -70,7 +146,11 @@ pub async fn dir_page( dir: &str, library: &State, user: MaybeUser, + shell: &State, + conf: &State, ) -> StringResponse { + check_private!(conf, user, shell, ctx); + if dir.ends_with(".json") { let dir_videos = library .get_directory_videos(dir.split_once(".json").map(|x| x.0).unwrap_or_default()) @@ -91,7 +171,11 @@ pub async fn dir_page( .push(Padding(video_elements).all(ScreenValue::_6)) .render(); - render_page(ctx, content, dir, user.into()).await + render_page(&shell, ctx, content, dir, user.into()).await +} + +pub async fn is_private_page(shell: &Shell, ctx: RequestContext) -> StringResponse { + page!(shell, ctx, "WatchDogs", Text("This is a private instance")) } #[get("/")] @@ -99,7 +183,11 @@ pub async fn index_page( ctx: RequestContext, library: &State, user: MaybeUser, + shell: &State, + conf: &State, ) -> StringResponse { + check_private!(conf, user, shell, ctx); + let random_video_elements = html! { @for mut vid in library.get_random_videos(3).await { ( video_element(&mut vid).await ); @@ -115,34 +203,22 @@ pub async fn index_page( let content = Div() .vanish() .push(Title("Random Videos")) - .push(VerticalVideoGrid(random_video_elements)) + .push(VerticalVideoGridRaw(random_video_elements)) .push(Title(Link("/latest", "Latest Videos").use_htmx())) - .push(VerticalVideoGrid(newly_added_elements)) + .push(VerticalVideoGridRaw(newly_added_elements)) .push(Title("Directories:")) .push( Padding( - Flex(Div().vanish().push_for_each(&directories, |dir: &_| { - Margin( - Padding( - Hover(Background(Cursor::Pointer.on(Nothing())).color(Purple::_600)) - .on(Background( - Rounded( - Link(&format!("/d/{dir}"), Text(&dir).white()).use_htmx(), - ) - .size(Size::Full), - ) - .color(Purple::_500)), - ) - .x(ScreenValue::_3) - .y(ScreenValue::_2), - ) - .all(ScreenValue::_2) - })) + Flex( + Div() + .vanish() + .push_for_each(&directories, |dir: &_| DirectoryBadge(dir)), + ) .wrap(Wrap::Wrap), ) .all(ScreenValue::_10), ) .render(); - render_page(ctx, content, "WatchDogs", user.into()).await + render_page(&shell, ctx, content, "WatchDogs", user.into()).await } diff --git a/src/pages/user.rs b/src/pages/user.rs index 4bee7e4..cbebc61 100644 --- a/src/pages/user.rs +++ b/src/pages/user.rs @@ -1,9 +1,12 @@ use based::auth::{Sessions, User}; +use based::page; use based::request::StringResponse; +use based::ui::components::{Card, Shell}; use based::ui::prelude::*; use based::{auth::MaybeUser, request::RequestContext}; use maud::{html, Render}; use rocket::http::CookieJar; +use rocket::State; use rocket::{form::Form, get, http::Cookie, post, response::Redirect, FromForm}; use crate::library::history::VideoHistory; @@ -12,17 +15,32 @@ use crate::pages::components::{video_element_wide, Title}; use super::components::render_page; #[get("/login")] -pub async fn login(ctx: RequestContext, user: MaybeUser) -> StringResponse { - let content = html!( - h2 { "Login" }; - form action="/login" method="POST" { - input type="text" name="username" placeholder="Username" required; - input type="password" name="password" placeholder="Password" required; - input type="submit" value="Login"; - } - ); - - render_page(ctx, content, "Login", user.into()).await +pub async fn login(ctx: RequestContext, user: MaybeUser, shell: &State) -> StringResponse { + page!( + shell, + ctx, + "Login", + Margin(Width( + ScreenValue::_80, + Div().push(Text("Login")._2xl()).push( + Margin( + based::ui::primitives::input::Form::new("/login") + .method(FormMethod::POST) + .add_input(TextInput("username").placeholder("Username").required()) + .add_input( + TextInput("password") + .placeholder("Password") + .required() + .password() + ) + .add_input(FormSubmitButton("Login").value("Login")) + ) + .top(ScreenValue::_4) + ) + )) + .x(ScreenValue::auto) + .top(ScreenValue::_6) + ) } #[derive(FromForm)] @@ -49,7 +67,7 @@ pub async fn login_post(login_form: Form, cookies: &CookieJar<'_>) -> } #[get("/history")] -pub async fn history_page(ctx: RequestContext, user: User) -> StringResponse { +pub async fn history_page(ctx: RequestContext, user: User, shell: &State) -> StringResponse { let video_elements = html! { @for mut vid in user.history_of(10).await { ( video_element_wide(&mut vid).await ); @@ -62,5 +80,5 @@ pub async fn history_page(ctx: RequestContext, user: User) -> StringResponse { .push(Padding(video_elements).all(ScreenValue::_6)) .render(); - render_page(ctx, content, "History", Some(user)).await + render_page(&shell, ctx, content, "History", Some(user)).await } diff --git a/src/pages/watch.rs b/src/pages/watch.rs index 60d8c6f..2140ba8 100644 --- a/src/pages/watch.rs +++ b/src/pages/watch.rs @@ -1,3 +1,4 @@ +use based::ui::components::Shell; use based::ui::primitives::space::Fraction; use based::ui::{prelude::*, AttrExtendable}; use based::{ @@ -8,6 +9,8 @@ use based::{ use maud::{html, PreEscaped, Render}; use rocket::{get, State}; +use crate::check_private; +use crate::config::Config; use crate::library::Video; use crate::{ library::{history::VideoHistory, Library}, @@ -22,7 +25,11 @@ pub async fn watch_page( library: &State, v: String, user: MaybeUser, + conf: &State, + shell: &State, ) -> StringResponse { + check_private!(conf, user, shell, ctx); + let video = if let Some(video) = library.get_video_by_id(&v).await { video } else { @@ -37,7 +44,7 @@ pub async fn watch_page( let youtube_meta = video.youtube_meta().await; let rec = build_rec(&library, &video).await; - let content = Container( + let content = Margin( Screen::large(Flex(Nothing()).direction(Direction::Row)).on( Flex( @@ -50,7 +57,7 @@ pub async fn watch_page( Rounded( Video().controls().autoplay().width(1080).add_src( Source(&format!("/video/raw?v={}", video.id), Some("video/mp4".to_string())) - ) + ).poster(&format!("/video/thumbnail?v={}", video.id)) ).size(Size::Large) ).color(Colors::Black) )) @@ -94,10 +101,10 @@ pub async fn watch_page( rec ) ).direction(Direction::Column).gap(ScreenValue::_6)) - ).x(ScreenValue::_10).top(ScreenValue::_6) - ).render(); + ).x(ScreenValue::_10).top(ScreenValue::_6).render(); render_page( + &shell, ctx, content, &format!("{} - WatchDogs", video.title), @@ -123,10 +130,18 @@ pub async fn build_rec(library: &Library, video: &Video) -> PreEscaped { Margin( Paragraph(Context( SpaceBetween( - Flex(Div().vanish().push(Span("In ")).push(Link( - &format!("/d/{}", video.directory), - Text(&video.directory).color(&Blue::_500), - ))) + Flex( + Div() + .vanish() + .push(Span("In ")._4xl().extrabold()) + .push(Link( + &format!("/d/{}", video.directory), + Text(&video.directory) + .color(&Blue::_500) + ._4xl() + .extrabold(), + )), + ) .group() .justify(Justify::Center), ) diff --git a/src/scripts/header.js b/src/scripts/header.js deleted file mode 100644 index 6d3d76b..0000000 --- a/src/scripts/header.js +++ /dev/null @@ -1,7 +0,0 @@ -function stopAllVideos() { - const videos = document.querySelectorAll('video'); - - videos.forEach(video => { - video.pause(); - }); -} \ No newline at end of file