send a http 303 if thumbnail isn't applicable
This commit is contained in:
parent
7134a7db52
commit
4a02921bbd
5 changed files with 60 additions and 40 deletions
|
@ -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)
|
||||
} else {
|
||||
image
|
||||
};
|
||||
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(result.to_vec())
|
||||
Ok(Some(result.to_vec()))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "previews-image"))]
|
||||
|
|
|
@ -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.")),
|
||||
}?;
|
||||
|
||||
if let Some(data) = preview {
|
||||
std::fs::create_dir_all(&self.path)?;
|
||||
let mut file = File::create(&*thumbpath)?;
|
||||
file.write_all(&data)?;
|
||||
|
||||
Ok(thumbpath.clone())
|
||||
Ok(Some(thumbpath.clone()))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
} else {
|
||||
Err(anyhow!("Object not found, or is not a 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)?)
|
||||
let hash = hash.into_inner();
|
||||
let address = Address::decode(&decode(&hash).map_err(ErrorInternalServerError)?)
|
||||
.map_err(ErrorInternalServerError)?;
|
||||
if let Address::Hash(hash) = address {
|
||||
let preview_path = preview_store
|
||||
.get(hash)
|
||||
if let Address::Hash(address_hash) = address {
|
||||
let preview_result = preview_store
|
||||
.get(address_hash)
|
||||
.map_err(error::ErrorInternalServerError)?;
|
||||
|
||||
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(file);
|
||||
return Ok(Either::A(file));
|
||||
} else {
|
||||
return Ok(Either::B(
|
||||
HttpResponse::SeeOther()
|
||||
.header(http::header::LOCATION, format!("/api/raw/{hash}"))
|
||||
.finish(),
|
||||
));
|
||||
}
|
||||
} else {
|
||||
return Err(ErrorBadRequest(
|
||||
"Address does not refer to a previewable object.",
|
||||
|
|
Loading…
Reference in a new issue