Compare commits

...

2 commits

Author SHA1 Message Date
a0e7c5d3c1
history feature
Some checks failed
ci/woodpecker/push/build Pipeline failed
2024-12-22 20:19:52 +01:00
95c4cf6fe1
static htmx 2024-12-22 19:48:45 +01:00
8 changed files with 277 additions and 47 deletions

191
Cargo.lock generated
View file

@ -137,6 +137,12 @@ dependencies = [
"windows-targets 0.52.6", "windows-targets 0.52.6",
] ]
[[package]]
name = "base64"
version = "0.21.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
[[package]] [[package]]
name = "base64" name = "base64"
version = "0.22.1" version = "0.22.1"
@ -152,7 +158,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#40d69b61e1a36d934c0c611e15a8fa26ec86fa83" source = "git+https://git.hydrar.de/jmarya/based#a89c5661208585b2a9f09d5ee337ad1c60ea9a49"
dependencies = [ dependencies = [
"bcrypt", "bcrypt",
"chrono", "chrono",
@ -166,6 +172,7 @@ dependencies = [
"rand", "rand",
"rayon", "rayon",
"regex", "regex",
"reqwest",
"ring", "ring",
"rocket", "rocket",
"rocket_cors", "rocket_cors",
@ -183,7 +190,7 @@ version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b1866ecef4f2d06a0bb77880015fdf2b89e25a1c2e5addacb87e459c86dc67e" checksum = "2b1866ecef4f2d06a0bb77880015fdf2b89e25a1c2e5addacb87e459c86dc67e"
dependencies = [ dependencies = [
"base64", "base64 0.22.1",
"blowfish", "blowfish",
"getrandom", "getrandom",
"subtle", "subtle",
@ -196,6 +203,12 @@ 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 = "383d29d513d8764dcdc42ea295d979eb99c3c9f00607b3692cf68a431f7dca72" checksum = "383d29d513d8764dcdc42ea295d979eb99c3c9f00607b3692cf68a431f7dca72"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "2.6.0" version = "2.6.0"
@ -232,9 +245,9 @@ checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
[[package]] [[package]]
name = "bytemuck" name = "bytemuck"
version = "1.20.0" version = "1.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b37c88a63ffd85d15b406896cc343916d7cf57838a847b3a6f2ca5d39a5695a" checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3"
[[package]] [[package]]
name = "byteorder" name = "byteorder"
@ -250,9 +263,9 @@ checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b"
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.2.4" version = "1.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9157bbaa6b165880c27a4293a474c91cdcf265cc68cc829bf10be0964a391caf" checksum = "c31a0499c1dc64f458ad13872de75c0eb7e3fdb0e67964610c914b034fc5956e"
dependencies = [ dependencies = [
"shlex", "shlex",
] ]
@ -464,7 +477,7 @@ version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b035a542cf7abf01f2e3c4d5a7acbaebfefe120ae4efc7bde3df98186e4b8af7" checksum = "b035a542cf7abf01f2e3c4d5a7acbaebfefe120ae4efc7bde3df98186e4b8af7"
dependencies = [ dependencies = [
"bitflags", "bitflags 2.6.0",
"proc-macro2", "proc-macro2",
"proc-macro2-diagnostics", "proc-macro2-diagnostics",
"quote", "quote",
@ -946,6 +959,19 @@ dependencies = [
"want", "want",
] ]
[[package]]
name = "hyper-tls"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905"
dependencies = [
"bytes",
"hyper",
"native-tls",
"tokio",
"tokio-native-tls",
]
[[package]] [[package]]
name = "iana-time-zone" name = "iana-time-zone"
version = "0.1.61" version = "0.1.61"
@ -1134,6 +1160,12 @@ dependencies = [
"generic-array", "generic-array",
] ]
[[package]]
name = "ipnet"
version = "2.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708"
[[package]] [[package]]
name = "is-terminal" name = "is-terminal"
version = "0.4.13" version = "0.4.13"
@ -1172,9 +1204,9 @@ dependencies = [
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.168" version = "0.2.169"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d" checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
[[package]] [[package]]
name = "libm" name = "libm"
@ -1436,9 +1468,9 @@ dependencies = [
[[package]] [[package]]
name = "object" name = "object"
version = "0.36.5" version = "0.36.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
dependencies = [ dependencies = [
"memchr", "memchr",
] ]
@ -1455,7 +1487,7 @@ version = "0.10.68"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5" checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5"
dependencies = [ dependencies = [
"bitflags", "bitflags 2.6.0",
"cfg-if", "cfg-if",
"foreign-types", "foreign-types",
"libc", "libc",
@ -1736,7 +1768,7 @@ version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834"
dependencies = [ dependencies = [
"bitflags", "bitflags 2.6.0",
] ]
[[package]] [[package]]
@ -1803,6 +1835,46 @@ 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 = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
[[package]]
name = "reqwest"
version = "0.11.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62"
dependencies = [
"base64 0.21.7",
"bytes",
"encoding_rs",
"futures-core",
"futures-util",
"h2",
"http 0.2.12",
"http-body",
"hyper",
"hyper-tls",
"ipnet",
"js-sys",
"log",
"mime",
"native-tls",
"once_cell",
"percent-encoding",
"pin-project-lite",
"rustls-pemfile",
"serde",
"serde_json",
"serde_urlencoded",
"sync_wrapper",
"system-configuration",
"tokio",
"tokio-native-tls",
"tower-service",
"url",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
"winreg",
]
[[package]] [[package]]
name = "ring" name = "ring"
version = "0.16.20" version = "0.16.20"
@ -1949,13 +2021,22 @@ version = "0.38.42"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85"
dependencies = [ dependencies = [
"bitflags", "bitflags 2.6.0",
"errno", "errno",
"libc", "libc",
"linux-raw-sys", "linux-raw-sys",
"windows-sys 0.59.0", "windows-sys 0.59.0",
] ]
[[package]]
name = "rustls-pemfile"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c"
dependencies = [
"base64 0.21.7",
]
[[package]] [[package]]
name = "rustversion" name = "rustversion"
version = "1.0.18" version = "1.0.18"
@ -2004,7 +2085,7 @@ version = "2.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
dependencies = [ dependencies = [
"bitflags", "bitflags 2.6.0",
"core-foundation", "core-foundation",
"core-foundation-sys", "core-foundation-sys",
"libc", "libc",
@ -2043,9 +2124,9 @@ dependencies = [
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.133" version = "1.0.134"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" checksum = "d00f4175c42ee48b15416f6193a959ba3a0d67fc699a0db9ad12df9f83991c7d"
dependencies = [ dependencies = [
"itoa", "itoa",
"memchr", "memchr",
@ -2294,8 +2375,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64bb4714269afa44aef2755150a0fc19d756fb580a67db8885608cf02f47d06a" checksum = "64bb4714269afa44aef2755150a0fc19d756fb580a67db8885608cf02f47d06a"
dependencies = [ dependencies = [
"atoi", "atoi",
"base64", "base64 0.22.1",
"bitflags", "bitflags 2.6.0",
"byteorder", "byteorder",
"bytes", "bytes",
"chrono", "chrono",
@ -2338,8 +2419,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fa91a732d854c5d7726349bb4bb879bb9478993ceb764247660aee25f67c2f8" checksum = "6fa91a732d854c5d7726349bb4bb879bb9478993ceb764247660aee25f67c2f8"
dependencies = [ dependencies = [
"atoi", "atoi",
"base64", "base64 0.22.1",
"bitflags", "bitflags 2.6.0",
"byteorder", "byteorder",
"chrono", "chrono",
"crc", "crc",
@ -2439,15 +2520,21 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.90" version = "2.0.91"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" checksum = "d53cbcb5a243bd33b7858b1d7f4aca2153490815872d86d955d6ea29f743c035"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"unicode-ident", "unicode-ident",
] ]
[[package]]
name = "sync_wrapper"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
[[package]] [[package]]
name = "synstructure" name = "synstructure"
version = "0.13.1" version = "0.13.1"
@ -2459,6 +2546,27 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "system-configuration"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7"
dependencies = [
"bitflags 1.3.2",
"core-foundation",
"system-configuration-sys",
]
[[package]]
name = "system-configuration-sys"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9"
dependencies = [
"core-foundation-sys",
"libc",
]
[[package]] [[package]]
name = "tempfile" name = "tempfile"
version = "3.14.0" version = "3.14.0"
@ -2554,9 +2662,9 @@ dependencies = [
[[package]] [[package]]
name = "tinyvec" name = "tinyvec"
version = "1.8.0" version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" checksum = "022db8904dfa342efe721985167e9fcd16c29b226db4397ed752a761cfce81e8"
dependencies = [ dependencies = [
"tinyvec_macros", "tinyvec_macros",
] ]
@ -2596,6 +2704,16 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "tokio-native-tls"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2"
dependencies = [
"native-tls",
"tokio",
]
[[package]] [[package]]
name = "tokio-stream" name = "tokio-stream"
version = "0.1.17" version = "0.1.17"
@ -2921,6 +3039,19 @@ dependencies = [
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]
[[package]]
name = "wasm-bindgen-futures"
version = "0.4.49"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38176d9b44ea84e9184eff0bc34cc167ed044f816accfe5922e54d84cf48eca2"
dependencies = [
"cfg-if",
"js-sys",
"once_cell",
"wasm-bindgen",
"web-sys",
]
[[package]] [[package]]
name = "wasm-bindgen-macro" name = "wasm-bindgen-macro"
version = "0.2.99" version = "0.2.99"
@ -3203,6 +3334,16 @@ dependencies = [
"memchr", "memchr",
] ]
[[package]]
name = "winreg"
version = "0.50.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1"
dependencies = [
"cfg-if",
"windows-sys 0.48.0",
]
[[package]] [[package]]
name = "write16" name = "write16"
version = "1.0.0" version = "1.0.0"

View file

@ -0,0 +1,9 @@
CREATE TABLE IF NOT EXISTS video_history (
id BIGSERIAL PRIMARY KEY,
username VARCHAR(255) NOT NULL,
video_id UUID NOT NULL,
timestamp TIMESTAMPTZ NOT NULL DEFAULT NOW(),
FOREIGN KEY (username) REFERENCES users (username) ON DELETE CASCADE,
FOREIGN KEY (video_id) REFERENCES videos (id) ON DELETE CASCADE
);

43
src/library/history.rs Normal file
View file

@ -0,0 +1,43 @@
use based::{auth::User, get_pg};
use super::Video;
pub trait VideoHistory {
async fn last_video(&self) -> Option<uuid::Uuid>;
async fn insert_history(&self, vid: uuid::Uuid);
async fn history_of(&self, n: i64) -> Vec<Video>;
}
impl VideoHistory for User {
async fn last_video(&self) -> Option<uuid::Uuid> {
let res: Option<(uuid::Uuid,)> = sqlx::query_as("SELECT video_id FROM video_history WHERE username = $1 ORDER BY timestamp DESC LIMIT 1")
.bind(&self.username)
.fetch_optional(get_pg!())
.await.unwrap();
res.map(|x| x.0)
}
async fn insert_history(&self, vid: uuid::Uuid) {
if let Some(prev) = self.last_video().await {
if prev == vid {
return;
}
}
sqlx::query("INSERT INTO video_history (username, video_id) VALUES ($1, $2)")
.bind(&self.username)
.bind(vid)
.execute(get_pg!())
.await
.unwrap();
}
async fn history_of(&self, n: i64) -> Vec<Video> {
sqlx::query_as("SELECT v.* FROM video_history vh JOIN videos v ON vh.video_id = v.id WHERE vh.username = $1 ORDER BY vh.timestamp DESC LIMIT $2")
.bind(&self.username)
.bind(n)
.fetch_all(get_pg!())
.await.unwrap()
}
}

View file

@ -1,3 +1,5 @@
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;
@ -9,6 +11,7 @@ pub use video::Video;
use crate::meta; use crate::meta;
mod func; mod func;
pub mod history;
mod video; mod video;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]

View file

@ -54,6 +54,7 @@ async fn launch() -> _ {
.mount( .mount(
"/", "/",
routes![ routes![
based::htmx::htmx_script_route,
pages::assets::video_file, pages::assets::video_file,
pages::assets::video_thumbnail, pages::assets::video_thumbnail,
pages::assets::fav_icon, pages::assets::fav_icon,
@ -65,7 +66,8 @@ async fn launch() -> _ {
pages::index::index_page, pages::index::index_page,
pages::watch::watch_page, pages::watch::watch_page,
pages::user::login, pages::user::login,
pages::user::login_post pages::user::login_post,
pages::user::history_page
], ],
) )
.attach(cors) .attach(cors)

View file

@ -13,30 +13,38 @@ pub async fn render_page(
title: &str, title: &str,
user: Option<User>, user: Option<User>,
) -> StringResponse { ) -> StringResponse {
based::page::render_page(content, title, ctx, &based::page::Shell::new( based::page::render_page(
html! { content,
script src="https://cdn.tailwindcss.com" {}; title,
script src="https://unpkg.com/htmx.org@2.0.3" integrity="sha384-0895/pl2MU10Hqc6jd4RvrthNlDiE9U1tWmX7WRESftEDRosgxNsQG/Ze9YMRzHq" crossorigin="anonymous" {}; ctx,
meta name="viewport" content="width=device-width, initial-scale=1.0"; &based::page::Shell::new(
}, html! {
html! { script src="https://cdn.tailwindcss.com" {};
header class="bg-gray-800 text-white shadow-md py-2" { script src="/assets/htmx.min.js" {};
(script(include_str!("../scripts/header.js"))); meta name="viewport" content="width=device-width, initial-scale=1.0";
},
html! {
header class="bg-gray-800 text-white shadow-md py-2" {
(script(include_str!("../scripts/header.js")));
div class="flex justify-between px-6" { div class="flex justify-between px-6" {
a href="/" class="flex items-center space-x-2" { a href="/" class="flex items-center space-x-2" {
img src="/favicon" alt="Logo" class="w-10 h-10 rounded-md"; img src="/favicon" alt="Logo" class="w-10 h-10 rounded-md";
span class="font-semibold text-xl" { "WatchDogs" }; span class="font-semibold text-xl" { "WatchDogs" };
}; };
@if user.is_some() { @if user.is_some() {
p { (user.unwrap().username) }; p { (user.unwrap().username) };
}; };
}; };
}; };
}, Some(String::from("bg-black text-white")))).await },
Some(String::from("bg-black text-white")),
),
)
.await
} }
pub fn loading_spinner() -> PreEscaped<String> { pub fn loading_spinner() -> PreEscaped<String> {

View file

@ -5,6 +5,9 @@ use maud::html;
use rocket::http::CookieJar; use rocket::http::CookieJar;
use rocket::{form::Form, get, http::Cookie, post, response::Redirect, FromForm}; use rocket::{form::Form, get, http::Cookie, post, response::Redirect, FromForm};
use crate::library::history::VideoHistory;
use crate::pages::components::video_element_wide;
use super::components::render_page; use super::components::render_page;
#[get("/login")] #[get("/login")]
@ -43,3 +46,17 @@ pub async fn login_post(login_form: Form<LoginForm>, cookies: &CookieJar<'_>) ->
Some(Redirect::to("/")) Some(Redirect::to("/"))
} }
#[get("/history")]
pub async fn history_page(ctx: RequestContext, user: User) -> StringResponse {
let content = html! {
h1 class="text-center text-4xl font-extrabold leading-tight mt-4 mb-2" { "History" };
div class="p-6" {
@for mut vid in user.history_of(10).await {
( video_element_wide(&mut vid).await );
};
};
};
render_page(ctx, content, "History", Some(user)).await
}

View file

@ -6,7 +6,10 @@ use based::{
use maud::html; use maud::html;
use rocket::{get, State}; use rocket::{get, State};
use crate::{library::Library, pages::components::video_element_wide}; use crate::{
library::{history::VideoHistory, Library},
pages::components::video_element_wide,
};
use super::components::render_page; use super::components::render_page;
@ -24,6 +27,10 @@ pub async fn watch_page(
library.get_video_by_youtube_id(&v).await.unwrap() library.get_video_by_youtube_id(&v).await.unwrap()
}; };
if let Some(user) = user.user() {
user.insert_history(video.id).await;
}
let content = html!( let content = html!(
main class="container mx-auto mt-6 flex flex-col lg:flex-row gap-6" { main class="container mx-auto mt-6 flex flex-col lg:flex-row gap-6" {
div class="lg:w-10/12 mt-10" { div class="lg:w-10/12 mt-10" {