parent
09beb0bcc2
commit
bb61c16429
5 changed files with 108 additions and 60 deletions
|
@ -74,6 +74,7 @@ async fn launch() -> _ {
|
||||||
routes![
|
routes![
|
||||||
pages::assets::video_file,
|
pages::assets::video_file,
|
||||||
pages::assets::video_thumbnail,
|
pages::assets::video_thumbnail,
|
||||||
|
pages::assets::fav_icon,
|
||||||
pages::index::search,
|
pages::index::search,
|
||||||
pages::index::channel_page,
|
pages::index::channel_page,
|
||||||
pages::yt::yt_tags,
|
pages::yt::yt_tags,
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
use rocket::{fs::NamedFile, get, State};
|
use rocket::{
|
||||||
|
fs::NamedFile,
|
||||||
|
get,
|
||||||
|
http::{ContentType, Status},
|
||||||
|
State,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::library::Library;
|
use crate::library::Library;
|
||||||
|
|
||||||
|
@ -29,3 +34,11 @@ pub async fn video_thumbnail(v: &str, library: &State<Library>) -> Option<NamedF
|
||||||
|
|
||||||
NamedFile::open(format!("{thumbnail_path}.jpg")).await.ok()
|
NamedFile::open(format!("{thumbnail_path}.jpg")).await.ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[get("/favicon")]
|
||||||
|
pub async fn fav_icon() -> (Status, (ContentType, &'static [u8])) {
|
||||||
|
(
|
||||||
|
Status::Ok,
|
||||||
|
(ContentType::PNG, include_bytes!("../../src/icon.png")),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -28,6 +28,27 @@ impl<'r> FromRequest<'r> for HTMX {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn htmx_link(
|
||||||
|
url: &str,
|
||||||
|
class: &str,
|
||||||
|
onclick: &str,
|
||||||
|
content: PreEscaped<String>,
|
||||||
|
) -> PreEscaped<String> {
|
||||||
|
html!(
|
||||||
|
a class=(class) onclick=(onclick) href=(url) hx-get=(url) hx-target="#main_content" hx-push-url="true" hx-swap="innerHTML" {
|
||||||
|
(content);
|
||||||
|
};
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn script(script: &str) -> PreEscaped<String> {
|
||||||
|
html!(
|
||||||
|
script {
|
||||||
|
(PreEscaped(script))
|
||||||
|
};
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn shell(content: PreEscaped<String>, title: &str) -> PreEscaped<String> {
|
pub fn shell(content: PreEscaped<String>, title: &str) -> PreEscaped<String> {
|
||||||
html! {
|
html! {
|
||||||
html {
|
html {
|
||||||
|
@ -38,11 +59,25 @@ pub fn shell(content: PreEscaped<String>, title: &str) -> PreEscaped<String> {
|
||||||
meta name="viewport" content="width=device-width, initial-scale=1.0";
|
meta name="viewport" content="width=device-width, initial-scale=1.0";
|
||||||
};
|
};
|
||||||
body class="bg-black text-white" {
|
body class="bg-black text-white" {
|
||||||
|
header class="bg-gray-800 text-white shadow-md py-2" {
|
||||||
|
(script(include_str!("../scripts/header.js")));
|
||||||
|
|
||||||
|
div class="flex justify-start px-6" {
|
||||||
|
|
||||||
|
(htmx_link("/", "flex items-center space-x-2", "stopAllVideos()", html!(
|
||||||
|
img src="/favicon" alt="Logo" class="w-10 h-10 rounded-md";
|
||||||
|
span class="font-semibold text-xl" { "WatchDogs" };
|
||||||
|
)))
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
div id="main_content" {
|
div id="main_content" {
|
||||||
(content)
|
(content)
|
||||||
};
|
};
|
||||||
}
|
};
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,74 +140,63 @@ pub fn format_number(num: i32) -> String {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn video_element_wide(video: &mut Video) -> PreEscaped<String> {
|
pub async fn video_element_wide(video: &mut Video) -> PreEscaped<String> {
|
||||||
html!(
|
htmx_link(
|
||||||
a href=(format!("/watch?v={}", video.id)) class="flex items-center w-full p-4 bg-gray-900 shadow-lg rounded-lg overflow-hidden mb-2 mt-2" {
|
&format!("/watch?v={}", video.id),
|
||||||
div class="flex-shrink-0" {
|
"flex items-center w-full p-4 bg-gray-900 shadow-lg rounded-lg overflow-hidden mb-2 mt-2",
|
||||||
img width="480" src=(format!("/video/thumbnail?v={}", video.id)) alt="Video Thumbnail" class="w-48 h-32 object-cover rounded-md";
|
"stopAllVideos()",
|
||||||
};
|
html!(
|
||||||
|
div class="flex-shrink-0" {
|
||||||
div class="flex flex-col flex-grow ml-4" {
|
img width="480" src=(format!("/video/thumbnail?v={}", video.id)) alt="Video Thumbnail" class="w-48 h-32 object-cover rounded-md";
|
||||||
h3 class="text-lg font-semibold truncate mb-1" {
|
|
||||||
( video.title )
|
|
||||||
};
|
};
|
||||||
|
|
||||||
@if let Some(meta) = video.youtube_meta().await {
|
div class="flex flex-col flex-grow ml-4" {
|
||||||
div class="text-sm text-gray-400 mb-2" {
|
h3 class="text-lg font-semibold truncate mb-1" {
|
||||||
span class="font-medium" { ( meta.uploader_name ) }
|
( video.title )
|
||||||
span { " - " }
|
|
||||||
span { ( format_date(&meta.upload_date) ) }
|
|
||||||
};
|
|
||||||
|
|
||||||
div class="text-sm text-gray-400" {
|
|
||||||
span { ( format_number(meta.views) ) }
|
|
||||||
span { " views" }
|
|
||||||
span { " - " }
|
|
||||||
span class="ml-2 bg-black text-white text-xs px-2 py-1 rounded-sm opacity-90" {
|
|
||||||
(( format_seconds_to_hhmmss(video.duration) ))
|
|
||||||
};
|
};
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn video_element(video: &mut Video) -> PreEscaped<String> {
|
@if let Some(meta) = video.youtube_meta().await {
|
||||||
html!(
|
div class="text-sm text-gray-400 mb-2" {
|
||||||
a href=(format!("/watch?v={}", video.id)) class="max-w-sm mx-auto p-4 max-h-60 aspect-video" {
|
span class="font-medium" { ( meta.uploader_name ) }
|
||||||
div class="bg-gray-900 shadow-lg rounded-lg overflow-hidden" {
|
span { " - " }
|
||||||
div class="relative" {
|
span { ( format_date(&meta.upload_date) ) }
|
||||||
img width="480" src=(format!("/video/thumbnail?v={}", video.id)) alt="Video Thumbnail" class="w-full h-auto object-cover aspect-video";
|
|
||||||
span class="absolute bottom-2 right-2 bg-black text-white text-xs px-2 py-1 rounded-sm opacity-90" {
|
|
||||||
(( format_seconds_to_hhmmss(video.duration) ))
|
|
||||||
};
|
};
|
||||||
};
|
|
||||||
|
|
||||||
div class="bg-gray-900 shadow-lg rounded-lg overflow-hidden" {
|
div class="text-sm text-gray-400" {
|
||||||
div class="p-4" {
|
span { ( format_number(meta.views) ) }
|
||||||
h3 class="text-lg font-semibold truncate" {
|
span { " views" }
|
||||||
( video.title )
|
span { " - " }
|
||||||
|
span class="ml-2 bg-black text-white text-xs px-2 py-1 rounded-sm opacity-90" {
|
||||||
|
(( format_seconds_to_hhmmss(video.duration) ))
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn header(query: &str) -> PreEscaped<String> {
|
pub async fn video_element(video: &mut Video) -> PreEscaped<String> {
|
||||||
html!(
|
htmx_link(
|
||||||
header style="padding: 10px 0; display: flex; justify-content: space-between;" {
|
&format!("/watch?v={}", video.id),
|
||||||
a href="/" style="text-decoration: none; margin-left: 20px;" {
|
"max-w-sm mx-auto p-4 max-h-60 aspect-video",
|
||||||
div style="margin-right: 20px;display:flex;align-items: center" {
|
"stopAllVideos()",
|
||||||
img src="/icon" width="64" style="margin-top: -25px;margin-right: 15px;border-radius: 20%;";
|
html!(
|
||||||
p style="font-size: 42px;" { "WatchDogs" };
|
div class="bg-gray-900 shadow-lg rounded-lg overflow-hidden" {
|
||||||
|
div class="relative" {
|
||||||
|
img width="480" src=(format!("/video/thumbnail?v={}", video.id)) alt="Video Thumbnail" class="w-full h-auto object-cover aspect-video";
|
||||||
|
span class="absolute bottom-2 right-2 bg-black text-white text-xs px-2 py-1 rounded-sm opacity-90" {
|
||||||
|
(( format_seconds_to_hhmmss(video.duration) ))
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
div class="bg-gray-900 shadow-lg rounded-lg overflow-hidden" {
|
||||||
|
div class="p-4" {
|
||||||
|
h3 class="text-lg font-semibold truncate" {
|
||||||
|
( video.title )
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
),
|
||||||
div style="width: 35px;" {};
|
|
||||||
div style="flex-grow: 1; text-align: center;" {
|
|
||||||
(search_bar(query));
|
|
||||||
};
|
|
||||||
};
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,10 @@ use rocket::{
|
||||||
};
|
};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
|
||||||
use crate::{library::Library, pages::components::video_element};
|
use crate::{
|
||||||
|
library::Library,
|
||||||
|
pages::components::{htmx_link, video_element},
|
||||||
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
api_response,
|
api_response,
|
||||||
|
@ -69,7 +72,7 @@ pub async fn index_page(htmx: HTMX, library: &State<Library>) -> (Status, (Conte
|
||||||
h1 class="text-center text-4xl font-extrabold leading-tight mt-8" { "Directories:" };
|
h1 class="text-center text-4xl font-extrabold leading-tight mt-8" { "Directories:" };
|
||||||
div class="flex flex-wrap p-10" {
|
div class="flex flex-wrap p-10" {
|
||||||
@for dir in library.get_directories().await {
|
@for dir in library.get_directories().await {
|
||||||
a class="px-3 py-2 m-2 bg-purple-500 text-white rounded-full cursor-pointer hover:bg-purple-600" href=(format!("/d/{dir}")) { (dir) };
|
(htmx_link(&format!("/d/{dir}"), "px-3 py-2 m-2 bg-purple-500 text-white rounded-full cursor-pointer hover:bg-purple-600", "", html! { (dir) }));
|
||||||
br;
|
br;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
7
src/scripts/header.js
Normal file
7
src/scripts/header.js
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
function stopAllVideos() {
|
||||||
|
const videos = document.querySelectorAll('video');
|
||||||
|
|
||||||
|
videos.forEach(video => {
|
||||||
|
video.pause();
|
||||||
|
});
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue