This commit is contained in:
parent
2ac2559c23
commit
59dae90b4d
13 changed files with 368 additions and 138 deletions
125
Cargo.lock
generated
125
Cargo.lock
generated
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
2
README.md
Normal file
2
README.md
Normal file
|
@ -0,0 +1,2 @@
|
|||
# WatchDogs
|
||||
WatchDogs is a video server
|
7
config.toml
Normal file
7
config.toml
Normal file
|
@ -0,0 +1,7 @@
|
|||
|
||||
[general]
|
||||
# Private Instance (Login required for watching content)
|
||||
private = true
|
||||
|
||||
# Path to videos
|
||||
video_path = "./videos"
|
|
@ -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
|
||||
|
|
12
src/config.rs
Normal file
12
src/config.rs
Normal file
|
@ -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,
|
||||
}
|
28
src/main.rs
28
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<String> = 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: {} <directory_path>", 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)
|
||||
}
|
||||
|
|
|
@ -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?<v>")]
|
||||
pub async fn video_file(v: &str, library: &State<Library>) -> Option<DataResponse> {
|
||||
pub async fn video_file(
|
||||
v: &str,
|
||||
library: &State<Library>,
|
||||
conf: &State<Config>,
|
||||
user: MaybeUser,
|
||||
) -> Option<DataResponse> {
|
||||
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<Library>) -> Option<DataRespons
|
|||
}
|
||||
|
||||
#[get("/video/thumbnail?<v>")]
|
||||
pub async fn video_thumbnail(v: &str, library: &State<Library>) -> Option<DataResponse> {
|
||||
pub async fn video_thumbnail(
|
||||
v: &str,
|
||||
library: &State<Library>,
|
||||
conf: &State<Config>,
|
||||
user: MaybeUser,
|
||||
) -> Option<DataResponse> {
|
||||
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 {
|
||||
|
|
|
@ -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,20 +14,35 @@ use based::{
|
|||
use maud::{html, PreEscaped, Render};
|
||||
|
||||
pub async fn render_page(
|
||||
shell: &Shell,
|
||||
ctx: RequestContext,
|
||||
content: PreEscaped<String>,
|
||||
title: &str,
|
||||
user: Option<User>,
|
||||
) -> StringResponse {
|
||||
Shell::new(
|
||||
Nothing(),
|
||||
Div()
|
||||
.vanish()
|
||||
.push(script(include_str!("../scripts/header.js")))
|
||||
.push(AppBar("WatchDogs", user)),
|
||||
Background(Text("").white()).color(Colors::Black),
|
||||
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()]),
|
||||
)
|
||||
})),
|
||||
)
|
||||
.use_ui()
|
||||
.render_page(content, title, ctx)
|
||||
.await
|
||||
}
|
||||
|
@ -48,7 +67,18 @@ pub fn Title<T: UIWidget + 'static>(title: T) -> PreEscaped<String> {
|
|||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
pub fn VerticalVideoGrid<T: UIWidget + 'static>(inner: T) -> PreEscaped<String> {
|
||||
pub async fn VerticalVideoGrid(videos: &mut [Video]) -> PreEscaped<String> {
|
||||
let video_elements = html! {
|
||||
@for mut vid in videos {
|
||||
( video_element(&mut vid).await );
|
||||
};
|
||||
};
|
||||
|
||||
VerticalVideoGridRaw(video_elements)
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
pub fn VerticalVideoGridRaw<T: UIWidget + 'static>(inner: T) -> PreEscaped<String> {
|
||||
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<String> {
|
|||
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<String> {
|
|||
.side(Side::Top),
|
||||
),
|
||||
),
|
||||
)))
|
||||
))))
|
||||
.push(Context(
|
||||
Position(
|
||||
PositionKind::Absolute,
|
||||
|
@ -216,3 +246,19 @@ pub async fn video_element(video: &mut Video) -> PreEscaped<String> {
|
|||
)
|
||||
.render()
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
pub fn DirectoryBadge(dir: &str) -> PreEscaped<String> {
|
||||
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()
|
||||
}
|
||||
|
|
|
@ -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?<query>&<offset>")]
|
||||
pub async fn search(
|
||||
query: &str,
|
||||
offset: Option<i64>,
|
||||
library: &State<Library>,
|
||||
conf: &State<Config>,
|
||||
user: MaybeUser,
|
||||
shell: &State<Shell>,
|
||||
ctx: RequestContext,
|
||||
) -> Option<serde_json::Value> {
|
||||
// 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<Library>) -> FallibleApiResponse {
|
||||
pub async fn latest_api(
|
||||
library: &State<Library>,
|
||||
user: MaybeUser,
|
||||
conf: &State<Config>,
|
||||
shell: &State<Shell>,
|
||||
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<Library>,
|
||||
user: MaybeUser,
|
||||
) -> StringResponse {
|
||||
let videos = library.get_newly_added(20).await;
|
||||
#[allow(non_snake_case)]
|
||||
pub async fn VideoList(videos: &[Video]) -> PreEscaped<String> {
|
||||
let video_elements = html! {
|
||||
@for mut vid in videos {
|
||||
( video_element_wide(&mut vid).await );
|
||||
};
|
||||
};
|
||||
|
||||
Padding(video_elements).all(ScreenValue::_6).render()
|
||||
}
|
||||
|
||||
#[get("/latest?<offset>")]
|
||||
pub async fn latest_page(
|
||||
ctx: RequestContext,
|
||||
library: &State<Library>,
|
||||
user: MaybeUser,
|
||||
shell: &State<Shell>,
|
||||
conf: &State<Config>,
|
||||
offset: Option<u32>,
|
||||
) -> 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/<dir>")]
|
||||
|
@ -70,7 +146,11 @@ pub async fn dir_page(
|
|||
dir: &str,
|
||||
library: &State<Library>,
|
||||
user: MaybeUser,
|
||||
shell: &State<Shell>,
|
||||
conf: &State<Config>,
|
||||
) -> 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<Library>,
|
||||
user: MaybeUser,
|
||||
shell: &State<Shell>,
|
||||
conf: &State<Config>,
|
||||
) -> 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(),
|
||||
Flex(
|
||||
Div()
|
||||
.vanish()
|
||||
.push_for_each(&directories, |dir: &_| DirectoryBadge(dir)),
|
||||
)
|
||||
.size(Size::Full),
|
||||
)
|
||||
.color(Purple::_500)),
|
||||
)
|
||||
.x(ScreenValue::_3)
|
||||
.y(ScreenValue::_2),
|
||||
)
|
||||
.all(ScreenValue::_2)
|
||||
}))
|
||||
.wrap(Wrap::Wrap),
|
||||
)
|
||||
.all(ScreenValue::_10),
|
||||
)
|
||||
.render();
|
||||
|
||||
render_page(ctx, content, "WatchDogs", user.into()).await
|
||||
render_page(&shell, ctx, content, "WatchDogs", user.into()).await
|
||||
}
|
||||
|
|
|
@ -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<Shell>) -> 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<LoginForm>, 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<Shell>) -> 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
|
||||
}
|
||||
|
|
|
@ -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<Library>,
|
||||
v: String,
|
||||
user: MaybeUser,
|
||||
conf: &State<Config>,
|
||||
shell: &State<Shell>,
|
||||
) -> 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<String> {
|
|||
Margin(
|
||||
Paragraph(Context(
|
||||
SpaceBetween(
|
||||
Flex(Div().vanish().push(Span("In ")).push(Link(
|
||||
Flex(
|
||||
Div()
|
||||
.vanish()
|
||||
.push(Span("In ")._4xl().extrabold())
|
||||
.push(Link(
|
||||
&format!("/d/{}", video.directory),
|
||||
Text(&video.directory).color(&Blue::_500),
|
||||
)))
|
||||
Text(&video.directory)
|
||||
.color(&Blue::_500)
|
||||
._4xl()
|
||||
.extrabold(),
|
||||
)),
|
||||
)
|
||||
.group()
|
||||
.justify(Justify::Center),
|
||||
)
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
function stopAllVideos() {
|
||||
const videos = document.querySelectorAll('video');
|
||||
|
||||
videos.forEach(video => {
|
||||
video.pause();
|
||||
});
|
||||
}
|
Loading…
Add table
Reference in a new issue