From 4a02921bbd146b7687ea768de4837a915a38e4e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Ml=C3=A1dek?= Date: Mon, 24 Jan 2022 18:26:53 +0100 Subject: [PATCH] send a http 303 if thumbnail isn't applicable --- src/previews/image.rs | 20 ++++++++++---------- src/previews/mod.rs | 21 ++++++++++++--------- src/previews/text.rs | 19 +++++++++++++------ src/previews/video.rs | 8 +++++--- src/routes.rs | 32 ++++++++++++++++++++------------ 5 files changed, 60 insertions(+), 40 deletions(-) diff --git a/src/previews/image.rs b/src/previews/image.rs index 12bdccb..4a945cb 100644 --- a/src/previews/image.rs +++ b/src/previews/image.rs @@ -9,21 +9,21 @@ use super::Previewable; pub struct ImagePath<'a>(pub &'a Path); impl<'a> Previewable for ImagePath<'a> { - fn get_thumbnail(&self) -> Result> { + fn get_thumbnail(&self) -> Result>> { #[cfg(feature = "previews-image")] { let image = ImageReader::open(&self.0)?.decode()?; let (w, h) = image.dimensions(); - let thumbnail = if cmp::max(w, h) > 1024 { - image.thumbnail(1024, 1024) + if cmp::max(w, h) > 1024 { + let thumbnail = image.thumbnail(1024, 1024); + let thumbnail = thumbnail.into_rgba8(); + let (w, h) = thumbnail.dimensions(); + let encoder = webp::Encoder::from_rgba(&thumbnail, w, h); + let result = encoder.encode(90.0); + Ok(Some(result.to_vec())) } else { - image - }; - let thumbnail = thumbnail.into_rgba8(); - let (w, h) = thumbnail.dimensions(); - let encoder = webp::Encoder::from_rgba(&thumbnail, w, h); - let result = encoder.encode(90.0); - Ok(result.to_vec()) + Ok(None) + } } #[cfg(not(feature = "previews-image"))] diff --git a/src/previews/mod.rs b/src/previews/mod.rs index 488ca25..5c00cb6 100644 --- a/src/previews/mod.rs +++ b/src/previews/mod.rs @@ -18,7 +18,7 @@ pub mod text; pub mod video; pub trait Previewable { - fn get_thumbnail(&self) -> Result>; + fn get_thumbnail(&self) -> Result>>; } pub struct PreviewStore { path: PathBuf, @@ -49,16 +49,16 @@ impl PreviewStore { } } - pub fn get(&self, hash: Hash) -> Result { + pub fn get(&self, hash: Hash) -> Result> { let path_mutex = self.get_path(&hash); let thumbpath = path_mutex.lock().unwrap(); if thumbpath.exists() { - Ok(thumbpath.clone()) + Ok(Some(thumbpath.clone())) } else { let connection = self.db.connection()?; let files = connection.retrieve_file(hash)?; if let Some(file) = files.get(0) { - let data = match tree_magic_mini::from_filepath(&file.path) { + let preview = match tree_magic_mini::from_filepath(&file.path) { Some(tm) if tm.starts_with("text") => Ok(TextPath(&file.path).get_thumbnail()?), Some(tm) if tm.starts_with("video") => { Ok(VideoPath(&file.path).get_thumbnail()?) @@ -70,11 +70,14 @@ impl PreviewStore { _ => Err(anyhow!("Unknown file type, or file doesn't exist.")), }?; - std::fs::create_dir_all(&self.path)?; - let mut file = File::create(&*thumbpath)?; - file.write_all(&data)?; - - Ok(thumbpath.clone()) + if let Some(data) = preview { + std::fs::create_dir_all(&self.path)?; + let mut file = File::create(&*thumbpath)?; + file.write_all(&data)?; + Ok(Some(thumbpath.clone())) + } else { + Ok(None) + } } else { Err(anyhow!("Object not found, or is not a file.")) } diff --git a/src/previews/text.rs b/src/previews/text.rs index 6858de1..ed54df4 100644 --- a/src/previews/text.rs +++ b/src/previews/text.rs @@ -1,15 +1,22 @@ use anyhow::Result; -use std::{cmp::min, convert::TryInto, fs::File, io::Read, path::Path}; +use std::{convert::TryInto, fs::File, io::Read, path::Path}; use super::Previewable; pub struct TextPath<'a>(pub &'a Path); +const PREVIEW_SIZE: usize = 1024; + impl<'a> Previewable for TextPath<'a> { - fn get_thumbnail(&self) -> Result> { - let mut f = File::open(self.0)?; - let mut buffer = vec![0u8; min(1024, f.metadata()?.len().try_into()?)]; - f.read_exact(&mut buffer)?; - Ok(buffer) + fn get_thumbnail(&self) -> Result>> { + let mut file = File::open(self.0)?; + let size: usize = file.metadata()?.len().try_into()?; + if size > PREVIEW_SIZE { + let mut buffer = vec![0u8; PREVIEW_SIZE]; + file.read_exact(&mut buffer)?; + Ok(Some(buffer)) + } else { + Ok(None) + } } } diff --git a/src/previews/video.rs b/src/previews/video.rs index d5b0242..46f341d 100644 --- a/src/previews/video.rs +++ b/src/previews/video.rs @@ -10,7 +10,7 @@ use super::Previewable; pub struct VideoPath<'a>(pub &'a Path); impl<'a> Previewable for VideoPath<'a> { - fn get_thumbnail(&self) -> Result> { + fn get_thumbnail(&self) -> Result>> { let duration_cmd = Command::new("ffprobe") .args(["-v", "error"]) .args(["-show_entries", "format=duration"]) @@ -23,7 +23,9 @@ impl<'a> Previewable for VideoPath<'a> { String::from_utf8_lossy(&duration_cmd.stderr) )); } - let duration = String::from_utf8_lossy(&duration_cmd.stdout).trim().parse::()?; + let duration = String::from_utf8_lossy(&duration_cmd.stdout) + .trim() + .parse::()?; let outfile = tempfile::Builder::new().suffix(".png").tempfile()?; let thumbnail_cmd = Command::new("ffmpeg") .args(["-i", &self.0.to_string_lossy()]) @@ -42,6 +44,6 @@ impl<'a> Previewable for VideoPath<'a> { let mut buffer = Vec::new(); outfile.as_file().read_to_end(&mut buffer)?; - Ok(buffer) + Ok(Some(buffer)) } } diff --git a/src/routes.rs b/src/routes.rs index 59f4721..d4ab3d4 100644 --- a/src/routes.rs +++ b/src/routes.rs @@ -413,24 +413,32 @@ pub async fn get_info(state: web::Data) -> Result { pub async fn get_thumbnail( state: web::Data, hash: web::Path, -) -> Result { +) -> Result, Error> { #[cfg(feature = "previews")] if let Some(preview_store) = &state.preview_store { - let address = - Address::decode(&decode(hash.into_inner()).map_err(ErrorInternalServerError)?) - .map_err(ErrorInternalServerError)?; - if let Address::Hash(hash) = address { - let preview_path = preview_store - .get(hash) + let hash = hash.into_inner(); + let address = Address::decode(&decode(&hash).map_err(ErrorInternalServerError)?) + .map_err(ErrorInternalServerError)?; + if let Address::Hash(address_hash) = address { + let preview_result = preview_store + .get(address_hash) .map_err(error::ErrorInternalServerError)?; - let mut file = NamedFile::open(&preview_path)?.disable_content_disposition(); - if let Some(mime_type) = tree_magic_mini::from_filepath(&preview_path) { - if let Ok(mime) = mime_type.parse() { - file = file.set_content_type(mime); + if let Some(preview_path) = preview_result { + let mut file = NamedFile::open(&preview_path)?.disable_content_disposition(); + if let Some(mime_type) = tree_magic_mini::from_filepath(&preview_path) { + if let Ok(mime) = mime_type.parse() { + file = file.set_content_type(mime); + } } + return Ok(Either::A(file)); + } else { + return Ok(Either::B( + HttpResponse::SeeOther() + .header(http::header::LOCATION, format!("/api/raw/{hash}")) + .finish(), + )); } - return Ok(file); } else { return Err(ErrorBadRequest( "Address does not refer to a previewable object.",