This commit is contained in:
JMARyA 2024-12-24 15:50:48 +01:00
parent 008648dda8
commit cd10c64a1f
Signed by: jmarya
GPG key ID: 901B2ADDF27C2263
3 changed files with 104 additions and 39 deletions

27
examples/static.rs Normal file
View file

@ -0,0 +1,27 @@
use based::get_pg;
use based::request::RequestContext;
use rocket::get;
use rocket::response::Responder;
use rocket::routes;
#[get("/")]
pub async fn index_page<'r>(ctx: RequestContext) -> impl Responder<'r, 'static> {
based::request::assets::DataResponse::new(
include_bytes!("../Cargo.toml").to_vec(),
"text/toml",
Some(60 * 60 * 3),
)
}
#[rocket::launch]
async fn launch() -> _ {
// Logging
env_logger::init();
// Database
let pg = get_pg!();
// sqlx::migrate!("./migrations").run(pg).await.unwrap();
rocket::build().mount("/", routes![index_page])
}

View file

@ -2,11 +2,11 @@ use tokio::sync::OnceCell;
pub mod auth; pub mod auth;
pub mod format; pub mod format;
#[cfg(feature = "htmx")]
pub mod htmx;
pub mod page; pub mod page;
pub mod request; pub mod request;
pub mod result; pub mod result;
#[cfg(feature = "htmx")]
pub mod htmx;
// TODO : API Pagination? // TODO : API Pagination?
// TODO : CORS? // TODO : CORS?

View file

@ -1,49 +1,87 @@
// TODO : Implement use rocket::Request;
use rocket::Response;
use rocket::http::Header;
use rocket::http::Status;
use rocket::response::Responder;
use std::io::Cursor;
/* // TODO: Implement file based response
#[get("/video/raw?<v>")] pub struct DataResponse {
pub async fn video_file( data: Vec<u8>,
v: &str, content_type: String,
library: &State<Library>, cache_duration: Option<u64>,
) -> Option<(Status, (ContentType, Vec<u8>))> { }
let video = if let Some(video) = library.get_video_by_id(v).await {
video impl DataResponse {
pub fn new(data: Vec<u8>, content_type: &str, cache_duration: Option<u64>) -> Self {
Self {
data,
content_type: content_type.to_string(),
cache_duration,
}
}
}
#[rocket::async_trait]
impl<'r> Responder<'r, 'static> for DataResponse {
fn respond_to(self, req: &'r Request<'_>) -> rocket::response::Result<'static> {
// Handle Range requests
if let Some(range) = req.headers().get_one("Range") {
if let Some((start, end)) = parse_range_header(range, self.data.len()) {
// TODO : Reject invalid ranges
// TODO : Multiple ranges?
let sliced_data = &self.data[start..=end];
return Ok(Response::build()
.header(Header::new(
"Content-Range",
format!("bytes {}-{}/{}", start, end, self.data.len()),
))
.header(Header::new("Accept-Ranges", "bytes"))
.header(Header::new("Content-Type", self.content_type.clone()))
.status(Status::PartialContent)
.streamed_body(Cursor::new(sliced_data.to_vec()))
.finalize());
}
}
// Add caching headers for static files
let cache_control_header = if let Some(duration) = self.cache_duration {
Header::new("Cache-Control", format!("public, max-age={}", duration))
} else { } else {
library.get_video_by_youtube_id(v).await.unwrap() Header::new("Cache-Control", "no-cache")
}; };
if let Ok(mut file) = File::open(&video.path).await { Ok(Response::build()
let mut buf = Vec::with_capacity(51200); .header(cache_control_header)
file.read_to_end(&mut buf).await.ok()?; .header(Header::new("Accept-Ranges", "bytes"))
let content_type = if video.path.ends_with("mp4") { .header(Header::new("Content-Type", self.content_type))
ContentType::new("video", "mp4") .streamed_body(Cursor::new(self.data))
} else { .finalize())
ContentType::new("video", "webm") }
};
return Some((Status::Ok, (content_type, buf)));
} }
None /// Parse the Range header and return the start and end indices.
fn parse_range_header(range: &str, total_len: usize) -> Option<(usize, usize)> {
if !range.starts_with("bytes=") {
return None;
} }
#[get("/video/thumbnail?<v>")] let range = &range[6..];
pub async fn video_thumbnail( let parts: Vec<&str> = range.split('-').collect();
v: &str,
library: &State<Library>,
) -> Option<(Status, (ContentType, Vec<u8>))> {
let video = if let Some(video) = library.get_video_by_id(v).await {
video
} else {
library.get_video_by_youtube_id(v).await.unwrap()
};
if let Some(data) = library.get_thumbnail(&video).await { if parts.len() != 2 {
return Some((Status::Ok, (ContentType::PNG, data))); return None;
} }
None let start = parts[0].parse::<usize>().ok();
} let end = parts[1].parse::<usize>().ok();
*/ match (start, end) {
(Some(start), Some(end)) if start <= end && end < total_len => Some((start, end)),
(Some(start), None) if start < total_len => Some((start, total_len - 1)),
(None, Some(end)) if end < total_len => Some((0, end)),
_ => None,
}
}