add audio waveform thumbnails

feat/vaults
Tomáš Mládek 2022-02-03 18:40:19 +01:00
parent 7540b31ab8
commit 20a6fa0de7
No known key found for this signature in database
GPG Key ID: ED21612889E75EC5
3 changed files with 59 additions and 1 deletions

43
src/previews/audio.rs Normal file
View File

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

View File

@ -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<Option<Vec<u8>>>;
@ -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()?)
}

View File

@ -31,10 +31,19 @@
</div>
{/if}
{#if audio}
{#if imageLoaded != address}
<Spinner />
{/if}
<img
src="/api/thumb/{address}"
alt={address}
on:load={() => (imageLoaded = address)}
on:error={() => (imageLoaded = address)}
/>
<audio controls preload="auto" src="/api/raw/{address}" />
{/if}
{#if video}
{#if imageLoaded != address }
{#if imageLoaded != address}
<Spinner />
<img
@ -83,6 +92,7 @@
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
audio,