send a http 303 if thumbnail isn't applicable

feat/vaults
Tomáš Mládek 2022-01-24 18:26:53 +01:00
parent 7134a7db52
commit 4a02921bbd
5 changed files with 60 additions and 40 deletions

View File

@ -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<Vec<u8>> {
fn get_thumbnail(&self) -> Result<Option<Vec<u8>>> {
#[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"))]

View File

@ -18,7 +18,7 @@ pub mod text;
pub mod video;
pub trait Previewable {
fn get_thumbnail(&self) -> Result<Vec<u8>>;
fn get_thumbnail(&self) -> Result<Option<Vec<u8>>>;
}
pub struct PreviewStore {
path: PathBuf,
@ -49,16 +49,16 @@ impl PreviewStore {
}
}
pub fn get(&self, hash: Hash) -> Result<PathBuf> {
pub fn get(&self, hash: Hash) -> Result<Option<PathBuf>> {
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."))
}

View File

@ -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<Vec<u8>> {
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<Option<Vec<u8>>> {
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)
}
}
}

View File

@ -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<Vec<u8>> {
fn get_thumbnail(&self) -> Result<Option<Vec<u8>>> {
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::<f64>()?;
let duration = String::from_utf8_lossy(&duration_cmd.stdout)
.trim()
.parse::<f64>()?;
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))
}
}

View File

@ -413,24 +413,32 @@ pub async fn get_info(state: web::Data<State>) -> Result<HttpResponse, Error> {
pub async fn get_thumbnail(
state: web::Data<State>,
hash: web::Path<String>,
) -> Result<NamedFile, Error> {
) -> Result<Either<NamedFile, HttpResponse>, 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.",