Compare commits

...

2 commits

Author SHA1 Message Date
fb55d396f8
ogp
Some checks failed
ci/woodpecker/push/build Pipeline failed
2025-02-23 23:39:25 +01:00
240d6cd5ce
user page + passwd 2025-02-23 23:11:08 +01:00
12 changed files with 219 additions and 62 deletions

66
Cargo.lock generated
View file

@ -146,7 +146,7 @@ checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
[[package]] [[package]]
name = "based" name = "based"
version = "0.1.0" 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 = [ dependencies = [
"bcrypt", "bcrypt",
"chrono", "chrono",
@ -251,9 +251,9 @@ checksum = "f61dac84819c6588b558454b194026eb1f09c293b9036ae9b159e74e73ab6cf9"
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.2.14" version = "1.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c3d1b2e905a3a7b00a6141adb0e4c0bb941d11caf55349d863942a1cc44e3c9" checksum = "c736e259eea577f443d5c86c304f9f4ae0295c43f3ba05c21f1d66b5f06001af"
dependencies = [ dependencies = [
"shlex", "shlex",
] ]
@ -503,9 +503,9 @@ checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b"
[[package]] [[package]]
name = "either" name = "either"
version = "1.13.0" version = "1.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" checksum = "b7914353092ddf589ad78f25c5c1c21b7f80b0ff8621e7c814c3485b5306da9d"
dependencies = [ dependencies = [
"serde", "serde",
] ]
@ -1160,9 +1160,9 @@ checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb"
[[package]] [[package]]
name = "inout" name = "inout"
version = "0.1.3" version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01"
dependencies = [ dependencies = [
"generic-array", "generic-array",
] ]
@ -1211,9 +1211,9 @@ dependencies = [
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.169" version = "0.2.170"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828"
[[package]] [[package]]
name = "libm" name = "libm"
@ -1255,9 +1255,9 @@ dependencies = [
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.25" version = "0.4.26"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e"
[[package]] [[package]]
name = "loom" name = "loom"
@ -1329,9 +1329,9 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
[[package]] [[package]]
name = "miniz_oxide" name = "miniz_oxide"
version = "0.8.4" version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3b1c9bd4fe1f0f8b387f6eb9eb3b4a1aa26185e5750efb9140301703f62cd1b" checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5"
dependencies = [ dependencies = [
"adler2", "adler2",
] ]
@ -1368,9 +1368,9 @@ dependencies = [
[[package]] [[package]]
name = "native-tls" name = "native-tls"
version = "0.2.13" version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0dab59f8e050d5df8e4dd87d9206fb6f65a483e20ac9fda365ade4fab353196c" checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e"
dependencies = [ dependencies = [
"libc", "libc",
"log", "log",
@ -1748,9 +1748,9 @@ dependencies = [
[[package]] [[package]]
name = "redox_syscall" name = "redox_syscall"
version = "0.5.8" version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" checksum = "82b568323e98e49e2a0899dcee453dd679fae22d69adf9b11dd508d1549b7e2f"
dependencies = [ dependencies = [
"bitflags 2.8.0", "bitflags 2.8.0",
] ]
@ -2088,18 +2088,18 @@ dependencies = [
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.217" version = "1.0.218"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" checksum = "e8dfc9d19bdbf6d17e22319da49161d5d0108e4188e8b680aef6299eed22df60"
dependencies = [ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.217" version = "1.0.218"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" checksum = "f09503e191f4e797cb8aac08e9a4a4695c5edf6a2e70e376d961ddd5c969f82b"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -2108,9 +2108,9 @@ dependencies = [
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.138" version = "1.0.139"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d434192e7da787e94a6ea7e9670b26a036d0ca41e0b7efb2676dd32bae872949" checksum = "44f86c3acccc9c65b153fe1b85a3be07fe5515274ec9f0653b4a0875731c72a6"
dependencies = [ dependencies = [
"itoa", "itoa",
"memchr", "memchr",
@ -2536,9 +2536,9 @@ dependencies = [
[[package]] [[package]]
name = "tempfile" name = "tempfile"
version = "3.16.0" version = "3.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38c246215d7d24f48ae091a2902398798e05d978b24315d6efbc00ede9a8bb91" checksum = "22e5a0acb1f3f55f65cc4a866c361b2fb2a0ff6366785ae6fbb5f85df07ba230"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"fastrand", "fastrand",
@ -2816,9 +2816,9 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
[[package]] [[package]]
name = "typenum" name = "typenum"
version = "1.17.0" version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f"
[[package]] [[package]]
name = "ubyte" name = "ubyte"
@ -2863,9 +2863,9 @@ checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5"
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.16" version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" checksum = "00e2473a93778eb0bad35909dff6a10d28e63f792f16ed15e404fca9d5eeedbe"
[[package]] [[package]]
name = "unicode-normalization" name = "unicode-normalization"
@ -2919,9 +2919,9 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
[[package]] [[package]]
name = "uuid" name = "uuid"
version = "1.13.1" version = "1.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ced87ca4be083373936a67f8de945faa23b6b42384bd5b64434850802c6dccd0" checksum = "93d59ca99a559661b96bf898d8fce28ed87935fd2bea9f05983c1464dd6c71b1"
dependencies = [ dependencies = [
"getrandom 0.3.1", "getrandom 0.3.1",
"serde", "serde",
@ -3303,9 +3303,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]] [[package]]
name = "winnow" name = "winnow"
version = "0.7.2" version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59690dea168f2198d1a3b0cac23b8063efcd11012f10ae4698f284808c8ef603" checksum = "0e7f4ea97f6f78012141bcdb6a216b2609f0979ada50b20ca5b52dde2eac2bb1"
dependencies = [ dependencies = [
"memchr", "memchr",
] ]

View file

@ -24,5 +24,5 @@ maud = "0.26.0"
rand = "0.8.5" rand = "0.8.5"
data-encoding = "2.6.0" data-encoding = "2.6.0"
bcrypt = "0.16.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" toml = "0.8.20"

View file

@ -3,5 +3,12 @@
# Private Instance (Login required for watching content) # Private Instance (Login required for watching content)
private = true 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 # Path to videos
video_path = "./videos" video_path = "./videos"

View file

@ -8,5 +8,7 @@ pub struct Config {
#[derive(Debug, Clone, Deserialize)] #[derive(Debug, Clone, Deserialize)]
pub struct GeneralConfig { pub struct GeneralConfig {
pub private: bool, pub private: bool,
pub allow_ogp: bool,
pub root_url: String,
pub video_path: String, pub video_path: String,
} }

View file

@ -1,5 +1,3 @@
use based::auth::User;
use based::get_pg;
use std::path::Path; use std::path::Path;
use std::path::PathBuf; use std::path::PathBuf;
use std::str::FromStr; use std::str::FromStr;

View file

@ -1,5 +1,6 @@
use crate::yt_meta::{self, get_vid_duration}; use crate::yt_meta::{self, get_vid_duration};
use based::{get_pg, request::api::ToAPI}; use based::{get_pg, request::api::ToAPI};
use chrono::Utc;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_json::json; use serde_json::json;
use sqlx::prelude::FromRow; use sqlx::prelude::FromRow;
@ -75,6 +76,7 @@ pub struct Video {
pub path: String, pub path: String,
pub duration: f64, pub duration: f64,
pub title: String, pub title: String,
pub date_added: chrono::DateTime<Utc>,
youtube_id: Option<String>, youtube_id: Option<String>,
} }

View file

@ -1,9 +1,4 @@
use based::{ use based::{asset::AssetRoutes, auth::User, get_pg, ui::components::prelude::Shell};
asset::AssetRoutes,
auth::User,
get_pg,
ui::components::{AppBar, Shell},
};
use rocket::{http::Method, routes}; use rocket::{http::Method, routes};
use std::path::Path; use std::path::Path;
mod config; mod config;
@ -79,7 +74,10 @@ async fn launch() -> _ {
pages::user::login_post, pages::user::login_post,
pages::user::history_page, pages::user::history_page,
pages::index::latest_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) .attach(cors)

View file

@ -48,7 +48,7 @@ pub async fn video_thumbnail(
conf: &State<Config>, conf: &State<Config>,
user: MaybeUser, user: MaybeUser,
) -> Option<DataResponse> { ) -> Option<DataResponse> {
if conf.general.private && user.user().is_none() { if (conf.general.private && user.user().is_none()) && !conf.general.allow_ogp {
return None; return None;
} }

View file

@ -1,8 +1,7 @@
use crate::library::Video; use crate::library::Video;
use based::ui::components::prelude::Avatar; use based::ui::components::prelude::Avatar;
use based::ui::components::prelude::*; use based::ui::components::prelude::*;
use based::ui::components::{AppBar, Card, NavBar, Shell}; use based::ui::primitives::flex::Row;
use based::ui::primitives::flex::Column;
use based::ui::primitives::Optional; use based::ui::primitives::Optional;
use based::ui::wrapper::HoverWrapper; use based::ui::wrapper::HoverWrapper;
use based::ui::{prelude::*, UIWidget}; use based::ui::{prelude::*, UIWidget};
@ -33,12 +32,28 @@ pub async fn render_page(
// TODO : profile pictures // TODO : profile pictures
DropDown( DropDown(
Avatar("", &user.username).use_initials(), Avatar("", &user.username).use_initials(),
DropdownMenu(vec![DropdownMenuEntry( DropdownMenu(vec![
DropdownMenuEntry(
"/account",
Row(vec![
MaterialIcon("account_circle"),
Text("Account").medium().render(),
])
.gap(ScreenValue::_2)
.items_center()
.justify(Justify::Start),
),
DropdownMenuEntry(
"/history", "/history",
Padding(Text("Video History").medium()) Row(vec![
.x(ScreenValue::_4) MaterialIcon("history"),
.y(ScreenValue::_2), Text("Video History").medium().render(),
)]), ])
.gap(ScreenValue::_2)
.items_center()
.justify(Justify::Start),
),
]),
) )
})) }))
.no_dropdown(), .no_dropdown(),

View file

@ -1,7 +1,7 @@
use based::page; use based::page;
use based::request::respond_html; use based::request::respond_html;
use based::ui::components::prelude::InfinityScroll; use based::ui::components::prelude::{InfinityScroll, Shell};
use based::ui::components::{ColoredSpinner, Shell}; use based::ui::components::ColoredSpinner;
use based::ui::prelude::*; use based::ui::prelude::*;
use based::ui::primitives::div::Center; use based::ui::primitives::div::Center;
use based::{ use based::{
@ -12,7 +12,7 @@ use based::{
}, },
}; };
use maud::{html, PreEscaped, Render}; use maud::{html, PreEscaped, Render};
use rocket::{get, uri, State}; use rocket::{get, State};
use serde_json::json; use serde_json::json;
use crate::config::Config; use crate::config::Config;

View file

@ -1,8 +1,11 @@
use based::auth::csrf::CSRF;
use based::auth::{Sessions, User}; use based::auth::{Sessions, User};
use based::page; use based::page;
use based::request::StringResponse; use based::request::StringResponse;
use based::ui::components::{Card, Shell}; use based::ui::components::prelude::Avatar;
use based::ui::prelude::*; use based::ui::components::prelude::*;
use based::ui::primitives::flex::Row;
use based::ui::{prelude::*, AttrExtendable};
use based::{auth::MaybeUser, request::RequestContext}; use based::{auth::MaybeUser, request::RequestContext};
use maud::{html, Render}; use maud::{html, Render};
use rocket::http::CookieJar; use rocket::http::CookieJar;
@ -82,3 +85,96 @@ pub async fn history_page(ctx: RequestContext, user: User, shell: &State<Shell>)
render_page(&shell, ctx, content, "History", Some(user)).await render_page(&shell, ctx, content, "History", Some(user)).await
} }
#[get("/account")]
pub async fn account(ctx: RequestContext, user: User, shell: &State<Shell>) -> 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 = "<form>")]
pub async fn change_password_post(form: Form<PasswordChangeForm>, 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<Shell>,
) -> 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
}

View file

@ -1,5 +1,7 @@
use based::format::format_number; 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::primitives::space::Fraction;
use based::ui::{prelude::*, AttrExtendable}; use based::ui::{prelude::*, AttrExtendable};
use based::{ use based::{
@ -10,7 +12,6 @@ use based::{
use maud::{html, PreEscaped, Render}; use maud::{html, PreEscaped, Render};
use rocket::{get, State}; use rocket::{get, State};
use crate::check_private;
use crate::config::Config; use crate::config::Config;
use crate::library::Video; use crate::library::Video;
use crate::{ use crate::{
@ -29,8 +30,6 @@ pub async fn watch_page(
conf: &State<Config>, conf: &State<Config>,
shell: &State<Shell>, shell: &State<Shell>,
) -> StringResponse { ) -> StringResponse {
check_private!(conf, user, shell, ctx);
let video = if let Some(video) = library.get_video_by_id(&v).await { let video = if let Some(video) = library.get_video_by_id(&v).await {
video video
} else { } else {
@ -38,6 +37,46 @@ pub async fn watch_page(
library.get_video_by_youtube_id(&v).await.unwrap() 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() { if let Some(user) = user.user() {
user.insert_history(video.id).await; user.insert_history(video.id).await;
} }
@ -104,7 +143,7 @@ pub async fn watch_page(
).x(ScreenValue::_10).top(ScreenValue::_6).render(); ).x(ScreenValue::_10).top(ScreenValue::_6).render();
render_page( render_page(
&shell, &shell.inner().extend().metadata(meta),
ctx, ctx,
content, content,
&format!("{} - WatchDogs", video.title), &format!("{} - WatchDogs", video.title),