diff --git a/Cargo.lock b/Cargo.lock index 6fa0ce1..f8b7594 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -146,7 +146,7 @@ checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" [[package]] name = "based" version = "0.1.0" -source = "git+https://git.hydrar.de/jmarya/based?branch=ui#f2cfcf27bbe7668caf95a99e7b8f7698e023fdec" +source = "git+https://git.hydrar.de/jmarya/based#696b34f2f17ef2d86f0bc77993f9b0b8b652c0f6" dependencies = [ "bcrypt", "chrono", @@ -251,9 +251,9 @@ checksum = "f61dac84819c6588b558454b194026eb1f09c293b9036ae9b159e74e73ab6cf9" [[package]] name = "cc" -version = "1.2.14" +version = "1.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c3d1b2e905a3a7b00a6141adb0e4c0bb941d11caf55349d863942a1cc44e3c9" +checksum = "c736e259eea577f443d5c86c304f9f4ae0295c43f3ba05c21f1d66b5f06001af" dependencies = [ "shlex", ] @@ -503,9 +503,9 @@ checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" [[package]] name = "either" -version = "1.13.0" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +checksum = "b7914353092ddf589ad78f25c5c1c21b7f80b0ff8621e7c814c3485b5306da9d" dependencies = [ "serde", ] @@ -1160,9 +1160,9 @@ checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb" [[package]] name = "inout" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" dependencies = [ "generic-array", ] @@ -1211,9 +1211,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.169" +version = "0.2.170" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" +checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828" [[package]] name = "libm" @@ -1255,9 +1255,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.25" +version = "0.4.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" +checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" [[package]] name = "loom" @@ -1329,9 +1329,9 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "miniz_oxide" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3b1c9bd4fe1f0f8b387f6eb9eb3b4a1aa26185e5750efb9140301703f62cd1b" +checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5" dependencies = [ "adler2", ] @@ -1368,9 +1368,9 @@ dependencies = [ [[package]] name = "native-tls" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dab59f8e050d5df8e4dd87d9206fb6f65a483e20ac9fda365ade4fab353196c" +checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" dependencies = [ "libc", "log", @@ -1748,9 +1748,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.8" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" +checksum = "82b568323e98e49e2a0899dcee453dd679fae22d69adf9b11dd508d1549b7e2f" dependencies = [ "bitflags 2.8.0", ] @@ -2088,18 +2088,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.217" +version = "1.0.218" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" +checksum = "e8dfc9d19bdbf6d17e22319da49161d5d0108e4188e8b680aef6299eed22df60" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.217" +version = "1.0.218" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" +checksum = "f09503e191f4e797cb8aac08e9a4a4695c5edf6a2e70e376d961ddd5c969f82b" dependencies = [ "proc-macro2", "quote", @@ -2108,9 +2108,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.138" +version = "1.0.139" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d434192e7da787e94a6ea7e9670b26a036d0ca41e0b7efb2676dd32bae872949" +checksum = "44f86c3acccc9c65b153fe1b85a3be07fe5515274ec9f0653b4a0875731c72a6" dependencies = [ "itoa", "memchr", @@ -2536,9 +2536,9 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.16.0" +version = "3.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38c246215d7d24f48ae091a2902398798e05d978b24315d6efbc00ede9a8bb91" +checksum = "22e5a0acb1f3f55f65cc4a866c361b2fb2a0ff6366785ae6fbb5f85df07ba230" dependencies = [ "cfg-if", "fastrand", @@ -2816,9 +2816,9 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "typenum" -version = "1.17.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" [[package]] name = "ubyte" @@ -2863,9 +2863,9 @@ checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" [[package]] name = "unicode-ident" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" +checksum = "00e2473a93778eb0bad35909dff6a10d28e63f792f16ed15e404fca9d5eeedbe" [[package]] name = "unicode-normalization" @@ -2919,9 +2919,9 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "uuid" -version = "1.13.1" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced87ca4be083373936a67f8de945faa23b6b42384bd5b64434850802c6dccd0" +checksum = "93d59ca99a559661b96bf898d8fce28ed87935fd2bea9f05983c1464dd6c71b1" dependencies = [ "getrandom 0.3.1", "serde", @@ -3303,9 +3303,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59690dea168f2198d1a3b0cac23b8063efcd11012f10ae4698f284808c8ef603" +checksum = "0e7f4ea97f6f78012141bcdb6a216b2609f0979ada50b20ca5b52dde2eac2bb1" dependencies = [ "memchr", ] diff --git a/Cargo.toml b/Cargo.toml index af1c971..7629627 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,5 +24,5 @@ maud = "0.26.0" 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" } +based = { git = "https://git.hydrar.de/jmarya/based", features = ["cache"] } toml = "0.8.20" diff --git a/config.toml b/config.toml index bbfe29d..a78d162 100644 --- a/config.toml +++ b/config.toml @@ -3,5 +3,12 @@ # Private Instance (Login required for watching content) private = true +# Allow OGP (Open Graph Protocol) +# This might expose metadata and thumbnails of videos (even if private) +allow_ogp = true + +# Root URL +root_url = "http://127.0.0.1:8080" + # Path to videos video_path = "./videos" diff --git a/src/config.rs b/src/config.rs index 8d32d98..0eaedf3 100644 --- a/src/config.rs +++ b/src/config.rs @@ -8,5 +8,7 @@ pub struct Config { #[derive(Debug, Clone, Deserialize)] pub struct GeneralConfig { pub private: bool, + pub allow_ogp: bool, + pub root_url: String, pub video_path: String, } diff --git a/src/library/mod.rs b/src/library/mod.rs index 3e1d38e..94b73f2 100644 --- a/src/library/mod.rs +++ b/src/library/mod.rs @@ -1,5 +1,3 @@ -use based::auth::User; -use based::get_pg; use std::path::Path; use std::path::PathBuf; use std::str::FromStr; diff --git a/src/library/video.rs b/src/library/video.rs index ba49d1e..e0411c7 100644 --- a/src/library/video.rs +++ b/src/library/video.rs @@ -1,5 +1,6 @@ use crate::yt_meta::{self, get_vid_duration}; use based::{get_pg, request::api::ToAPI}; +use chrono::Utc; use serde::{Deserialize, Serialize}; use serde_json::json; use sqlx::prelude::FromRow; @@ -75,6 +76,7 @@ pub struct Video { pub path: String, pub duration: f64, pub title: String, + pub date_added: chrono::DateTime, youtube_id: Option, } diff --git a/src/main.rs b/src/main.rs index 4e928ff..9d27a34 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,4 @@ -use based::{ - asset::AssetRoutes, - auth::User, - get_pg, - ui::components::{AppBar, Shell}, -}; +use based::{asset::AssetRoutes, auth::User, get_pg, ui::components::prelude::Shell}; use rocket::{http::Method, routes}; use std::path::Path; mod config; @@ -79,7 +74,10 @@ async fn launch() -> _ { pages::user::login_post, pages::user::history_page, pages::index::latest_page, - pages::index::latest_api + pages::index::latest_api, + pages::user::account, + pages::user::change_password, + pages::user::change_password_post ], ) .attach(cors) diff --git a/src/pages/assets.rs b/src/pages/assets.rs index ed9e513..02d633a 100644 --- a/src/pages/assets.rs +++ b/src/pages/assets.rs @@ -48,7 +48,7 @@ pub async fn video_thumbnail( conf: &State, user: MaybeUser, ) -> Option { - if conf.general.private && user.user().is_none() { + if (conf.general.private && user.user().is_none()) && !conf.general.allow_ogp { return None; } diff --git a/src/pages/components.rs b/src/pages/components.rs index e4ed71c..b903adc 100644 --- a/src/pages/components.rs +++ b/src/pages/components.rs @@ -1,8 +1,7 @@ use crate::library::Video; 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::flex::Row; use based::ui::primitives::Optional; use based::ui::wrapper::HoverWrapper; use based::ui::{prelude::*, UIWidget}; @@ -33,12 +32,28 @@ pub async fn render_page( // TODO : profile pictures DropDown( Avatar("", &user.username).use_initials(), - DropdownMenu(vec![DropdownMenuEntry( - "/history", - Padding(Text("Video History").medium()) - .x(ScreenValue::_4) - .y(ScreenValue::_2), - )]), + DropdownMenu(vec![ + DropdownMenuEntry( + "/account", + Row(vec![ + MaterialIcon("account_circle"), + Text("Account").medium().render(), + ]) + .gap(ScreenValue::_2) + .items_center() + .justify(Justify::Start), + ), + DropdownMenuEntry( + "/history", + Row(vec![ + MaterialIcon("history"), + Text("Video History").medium().render(), + ]) + .gap(ScreenValue::_2) + .items_center() + .justify(Justify::Start), + ), + ]), ) })) .no_dropdown(), diff --git a/src/pages/index.rs b/src/pages/index.rs index 31addb9..ed07fb0 100644 --- a/src/pages/index.rs +++ b/src/pages/index.rs @@ -1,7 +1,7 @@ use based::page; use based::request::respond_html; -use based::ui::components::prelude::InfinityScroll; -use based::ui::components::{ColoredSpinner, Shell}; +use based::ui::components::prelude::{InfinityScroll, Shell}; +use based::ui::components::ColoredSpinner; use based::ui::prelude::*; use based::ui::primitives::div::Center; use based::{ @@ -12,7 +12,7 @@ use based::{ }, }; use maud::{html, PreEscaped, Render}; -use rocket::{get, uri, State}; +use rocket::{get, State}; use serde_json::json; use crate::config::Config; diff --git a/src/pages/user.rs b/src/pages/user.rs index cbebc61..56f743e 100644 --- a/src/pages/user.rs +++ b/src/pages/user.rs @@ -1,8 +1,11 @@ +use based::auth::csrf::CSRF; use based::auth::{Sessions, User}; use based::page; use based::request::StringResponse; -use based::ui::components::{Card, Shell}; -use based::ui::prelude::*; +use based::ui::components::prelude::Avatar; +use based::ui::components::prelude::*; +use based::ui::primitives::flex::Row; +use based::ui::{prelude::*, AttrExtendable}; use based::{auth::MaybeUser, request::RequestContext}; use maud::{html, Render}; use rocket::http::CookieJar; @@ -82,3 +85,96 @@ pub async fn history_page(ctx: RequestContext, user: User, shell: &State) render_page(&shell, ctx, content, "History", Some(user)).await } + +#[get("/account")] +pub async fn account(ctx: RequestContext, user: User, shell: &State) -> StringResponse { + let content = Width( + ScreenValue::fit, + Margin( + Div() + .push( + Row(vec![ + Avatar("", &user.username).render(), + Text(&user.username)._2xl().bold().render(), + ]) + .gap(ScreenValue::_2), + ) + .push(Link("/passwd", Button(Text("Change Password")))), + ) + .top(ScreenValue::_8) + .x(ScreenValue::auto), + ) + .render(); + + render_page(&shell, ctx, content, "Account", Some(user)).await +} + +#[derive(FromForm)] +pub struct PasswordChangeForm { + password_old: String, + password_new: String, + password_new_repeat: String, + csrf: String, +} + +#[post("/passwd", data = "
")] +pub async fn change_password_post(form: Form, user: User) -> Redirect { + if form.password_new != form.password_new_repeat { + return Redirect::to("/passwd"); + } + + if !user.verify_csrf(&form.csrf).await { + return Redirect::to("/passwd"); + } + + user.passwd(&form.password_old, &form.password_new) + .await + .unwrap(); + + Redirect::to("/account") +} + +#[get("/passwd")] +pub async fn change_password( + ctx: RequestContext, + user: User, + shell: &State, +) -> StringResponse { + let csrf = user.get_csrf().await.to_string(); + let form = based::ui::primitives::input::Form::new("/passwd") + .method(FormMethod::POST) + .add_input( + Margin( + Text("Change Password") + ._2xl() + .bold() + .align(TextAlignment::Center), + ) + .bottom(ScreenValue::_6), + ) + .add_input( + TextInput("password_old") + .password() + .placeholder("Old Password") + .required(), + ) + .add_input( + TextInput("password_new") + .password() + .placeholder("New Password") + .required(), + ) + .add_input( + TextInput("password_new_repeat") + .password() + .placeholder("Repeat new password") + .required(), + ) + .add_input(FormSubmitButton("Change")) + .add_input(HiddenInput("csrf", &csrf).add_attr("class", "csrf")) + .render(); + + let content = Div().vanish().push(form).render(); + + render_page(&shell, ctx, content, "Change Password", Some(user)).await +} diff --git a/src/pages/watch.rs b/src/pages/watch.rs index 2d738e8..bc332a4 100644 --- a/src/pages/watch.rs +++ b/src/pages/watch.rs @@ -1,5 +1,7 @@ use based::format::format_number; -use based::ui::components::Shell; +use based::ogp::{MediaItem, Metadata}; +use based::request::respond_html; +use based::ui::components::prelude::Shell; use based::ui::primitives::space::Fraction; use based::ui::{prelude::*, AttrExtendable}; use based::{ @@ -10,7 +12,6 @@ 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::{ @@ -29,8 +30,6 @@ pub async fn watch_page( 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 { @@ -38,6 +37,46 @@ pub async fn watch_page( library.get_video_by_youtube_id(&v).await.unwrap() }; + let yt_meta = video.youtube_meta().await; + + let mut vid_meta = + based::ogp::Video::Other(video.duration as u32, video.date_added.date_naive()); + + if let Some(yt_meta) = &yt_meta { + for t in yt_meta.tags().await { + vid_meta = vid_meta.tag(&t); + } + } + + let meta = Metadata::new( + &format!("{}/watch?v={}", conf.general.root_url, video.id), + &video.title, + MediaItem::Image( + &format!("{}/video/thumbnail?v={}", conf.general.root_url, video.id), + "image/png", + "Video Thumbnail", + ), + vid_meta, + ) + .site_name("WatchDogs"); + + if conf.general.private && user.user().is_none() { + return respond_html( + html! { + (maud::DOCTYPE) + html { + head { + (meta.render()) + } + body { + + } + } + } + .0, + ); + } + if let Some(user) = user.user() { user.insert_history(video.id).await; } @@ -104,7 +143,7 @@ pub async fn watch_page( ).x(ScreenValue::_10).top(ScreenValue::_6).render(); render_page( - &shell, + &shell.inner().extend().metadata(meta), ctx, content, &format!("{} - WatchDogs", video.title),