* /api/categories/<cat> is now working properly

Output should be correct for the frontend
This commit is contained in:
shockrah 2021-10-11 20:39:51 -07:00
parent d0413cfff2
commit b9c4cb7c6b
4 changed files with 152 additions and 62 deletions

81
api/src/category.rs Normal file
View File

@ -0,0 +1,81 @@
use base64;
use serde::Serialize;
use std::fs::DirEntry;
use std::{io, env};
use rocket::serde::json::Json;
#[derive(Serialize)]
pub struct Category {
name: String,
thumbnail: Option<String>
}
#[derive(Serialize)]
struct List {
categories: Vec<Category>
}
fn get_category_metadata(entry: DirEntry) -> io::Result<(String, Option<String>)> {
use std::io::Read;
let name = entry.file_name().to_string_lossy().to_string();
let mut p = entry.path(); p.push(".thumbnail.jpg");
if p.is_file() {
let mut buf = Vec::new();
let mut file = std::fs::File::open(p)?;
file.read_to_end(&mut buf)?;
let nail = base64::encode(buf).to_string();
return match nail.len() {
0 => Ok((name, None)),
_ => Ok((name, Some(nail)))
};
}
return Ok((String::new(), None));
}
fn get_category_dirs(path: &str) -> std::io::Result<Vec<DirEntry>> {
let path = std::path::Path::new(path);
if !path.is_dir() {
panic!("<{:?}> is not a valid directory", path);
}
let mut ret: Vec<DirEntry> = Vec::new();
for entry in std::fs::read_dir(path)? {
if let Ok(entry) = entry {
ret.push(entry)
}
}
return Ok(ret);
}
#[get("/categories")]
pub fn list() -> Json<Vec<Category>> {
// WARN: misconfigured servers are just going to get shafted and serve up
// a tonne of 500's
let dir = match env::var("CLIPS_DIR") {
Ok(val) => val,
Err(_) => "/media/clips/".to_string()
};
match get_category_dirs(&dir) {
Ok(entries) => {
let mut cats: Vec<Category> = Vec::new();
for ent in entries {
match get_category_metadata(ent) {
Ok((name, thumbnail)) => {
cats.push(Category {name, thumbnail});
},
_ => continue
}
}
return Json(cats);
},
Err(e) => {
eprintln!("ERROR: {}", e);
}
}
Json(Vec::new())
}

View File

@ -4,6 +4,7 @@ use std::env;
use rocket_dyn_templates::Template; use rocket_dyn_templates::Template;
mod page; mod page;
mod category;
mod video; mod video;
#[tokio::main] #[tokio::main]
@ -16,7 +17,7 @@ async fn main() {
*/ */
let _ = rocket::build() let _ = rocket::build()
.mount("/", routes![page::home, page::category, page::video, page::files]) .mount("/", routes![page::home, page::category, page::video, page::files])
.mount("/api", routes![video::list_categories]) .mount("/api", routes![category::list, video::list])
.attach(Template::fairing()) .attach(Template::fairing())
.launch().await; .launch().await;
} }

View File

@ -18,7 +18,7 @@ pub async fn home() -> Template {
#[get("/category/<cat>")] #[get("/category/<cat>")]
pub async fn category(cat: String) -> Template { pub async fn category(cat: String) -> Template {
let mut h: HashMap<&'static str, &'static str> = HashMap::new(); let mut h: HashMap<&'static str, &'static str> = HashMap::new();
h.insert("script_src", "/js/category.js"); h.insert("script_src", "category.js");
h.insert("page", "category"); h.insert("page", "category");
return Template::render("list", &h); return Template::render("list", &h);

View File

@ -1,81 +1,89 @@
use base64; use std::env;
use serde::Serialize;
use std::fs::DirEntry; use std::fs::DirEntry;
use std::{io, env}; use serde::Serialize;
use rocket::serde::json::Json; use rocket::serde::json::Json;
#[derive(Serialize)] #[derive(Serialize)]
pub struct Category { pub struct VideoPreview {
name: String, name: String,
thumbnail: Option<String> thumbnail: Option<String>
} }
#[derive(Serialize)] fn vid_file_entries(path: &str) -> std::io::Result<Vec<DirEntry>> {
struct List {
categories: Vec<Category>
}
fn get_category_metadata(entry: DirEntry) -> io::Result<(String, Option<String>)> {
use std::io::Read;
let name = entry.file_name().to_string_lossy().to_string();
let mut p = entry.path(); p.push(".thumbnail.jpg");
if p.is_file() {
let mut buf = Vec::new();
let mut file = std::fs::File::open(p)?;
file.read_to_end(&mut buf)?;
let nail = base64::encode(buf).to_string();
return match nail.len() {
0 => Ok((name, None)),
_ => Ok((name, Some(nail)))
};
}
return Ok((String::new(), None));
}
fn get_category_dirs(path: &str) -> std::io::Result<Vec<DirEntry>> {
let path = std::path::Path::new(path); let path = std::path::Path::new(path);
if !path.is_dir() { if !path.is_dir() {
panic!("<{:?}> is not a valid directory", path); panic!("<{:?}> is not a valid directory", path);
} }
let mut entries: Vec<DirEntry> = Vec::new();
let mut ret: Vec<DirEntry> = Vec::new(); for ent in path.read_dir()? {
for entry in std::fs::read_dir(path)? { if let Ok(ent) = ent {
if let Ok(entry) = entry { let name = ent.file_name().into_string().unwrap();
ret.push(entry) if name.ends_with("mkv") || name.ends_with("mp4") || name.ends_with("webm") {
entries.push(ent);
} }
} }
return Ok(ret); }
return Ok(entries);
} }
#[get("/categories")] pub fn get_thumbnail(clips_dir: &str, vid_file: &str) -> std::io::Result<Option<String>> {
pub async fn list_categories() -> Json<Vec<Category>> { use std::io::Read;
// WARN: misconfigured servers are just going to get shafted and serve up // Contruct the full path to the video file itself
// a tonne of 500's // NOTE: thumbnail file names are basically file.mkv.jpg
let dir = match env::var("CLIPS_DIR") { let path = {
let mut s = clips_dir.to_string();
s.push_str("/thumbnails/");
s.push_str(vid_file);
s.push_str(".jpg");
s
};
let path = std::path::Path::new(&path);
if !path.is_file() {
println!("File {:?} not found", path);
return Ok(None)
}
println!("Found file {:?}", path);
// Read into a vec so that base64 can deal with it
let mut buf = Vec::new();
let mut file = std::fs::File::open(path)?;
file.read_to_end(&mut buf)?;
let nail = base64::encode(buf).to_string();
return match nail.len() {
0 => Ok(None),
_ => Ok(Some(nail))
};
}
#[get("/category/<cat>")]
pub fn list(cat: String) -> Json<Vec<VideoPreview>> {
// Strip out any and all '..' from the string
// Doing this to avoid any directory traversal memes
let cat = cat.replace("..","");
// Target the directory of clips that we're looking for
let mut clips_dir = match env::var("CLIPS_DIR") {
Ok(val) => val, Ok(val) => val,
Err(_) => "/media/clips/".to_string() Err(_) => "/media/clips/".to_string()
}; };
match get_category_dirs(&dir) { clips_dir.push('/'); clips_dir.push_str(&cat);
Ok(entries) => { let thumbs_dir = clips_dir.clone();
let mut cats: Vec<Category> = Vec::new();
for ent in entries { let mut json: Vec<VideoPreview> = Vec::new();
match get_category_metadata(ent) { // Grab direntires to all the target files
Ok((name, thumbnail)) => { let vid_files = vid_file_entries(&clips_dir).unwrap();
cats.push(Category {name, thumbnail}); for vf in vid_files {
}, // jfc this is rarted
_ => continue let name = vf.file_name().to_string_lossy().to_string();
let thumbnail = match get_thumbnail(&thumbs_dir, &name) {
Ok(val) => val,
_ => None
};
json.push(VideoPreview { name, thumbnail })
} }
} return Json(json)
return Json(cats);
},
Err(e) => {
eprintln!("ERROR: {}", e);
}
}
Json(Vec::new())
} }