From 20a6fa0de748d6b5c4f1f85f85cd04dd89a4a677 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Ml=C3=A1dek?= Date: Thu, 3 Feb 2022 18:40:19 +0100 Subject: [PATCH] add audio waveform thumbnails --- src/previews/audio.rs | 43 +++++++++++++++++++ src/previews/mod.rs | 5 +++ .../src/components/display/BlobPreview.svelte | 12 +++++- 3 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 src/previews/audio.rs 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)} + />