diff --git a/src/previews/audio.rs b/src/previews/audio.rs new file mode 100644 index 0000000..686b769 --- /dev/null +++ b/src/previews/audio.rs @@ -0,0 +1,43 @@ +use anyhow::anyhow; +use std::io::Read; +use std::path::Path; +use std::process::Command; + +use anyhow::Result; + +use super::Previewable; + +pub struct AudioPath<'a>(pub &'a Path); + +const COLOR: &str = "#dc322f"; // solarized red + +impl<'a> Previewable for AudioPath<'a> { + fn get_thumbnail(&self) -> Result>> { + let outfile = tempfile::Builder::new().suffix(".webp").tempfile()?; + let thumbnail_cmd = Command::new("ffmpeg") + .args(["-i", &self.0.to_string_lossy()]) + .args([ + "-filter_complex", + &format!( + "[0:a]aformat=channel_layouts=mono, compand=gain=-2, + showwavespic=s=860x256:colors={COLOR}, + drawbox=x=(iw-w)/2:y=(ih-h)/2:w=iw:h=1:color={COLOR}" + ), + ]) + .args(["-vframes", "1"]) + .arg(&*outfile.path().to_string_lossy()) + .arg("-y") + .output()?; + + if !thumbnail_cmd.status.success() { + return Err(anyhow!( + "Failed to render thumbnail: {:?}", + String::from_utf8_lossy(&thumbnail_cmd.stderr) + )); + } + + let mut buffer = Vec::new(); + outfile.as_file().read_to_end(&mut buffer)?; + Ok(Some(buffer)) + } +} diff --git a/src/previews/mod.rs b/src/previews/mod.rs index e604504..9a2e9f1 100644 --- a/src/previews/mod.rs +++ b/src/previews/mod.rs @@ -12,10 +12,12 @@ use std::{ use self::image::ImagePath; use self::text::TextPath; use self::video::VideoPath; +use self::audio::AudioPath; pub mod image; pub mod text; pub mod video; +pub mod audio; pub trait Previewable { fn get_thumbnail(&self) -> Result>>; @@ -63,6 +65,9 @@ impl PreviewStore { Some(tm) if tm.starts_with("video") || tm == "application/x-matroska" => { Ok(VideoPath(&file.path).get_thumbnail()?) } + Some(tm) if tm.starts_with("audio") => { + Ok(AudioPath(&file.path).get_thumbnail()?) + } Some(tm) if tm.starts_with("image") => { Ok(ImagePath(&file.path).get_thumbnail()?) } diff --git a/webui/src/components/display/BlobPreview.svelte b/webui/src/components/display/BlobPreview.svelte index a2be168..00d9df8 100644 --- a/webui/src/components/display/BlobPreview.svelte +++ b/webui/src/components/display/BlobPreview.svelte @@ -31,10 +31,19 @@ {/if} {#if audio} + {#if imageLoaded != address} + + {/if} + {address} (imageLoaded = address)} + on:error={() => (imageLoaded = address)} + />