Audio info
An audio file's length, bitrate, sample rate, tags and cover art, for MP3, FLAC, M4A, Ogg, WAV and AIFF.
Version 1.0.0 · 3 functions · 1 crate
Functions
What use audio_info.qwer gives a program, with the types Quillo sees. To take only some, or under other names: use audio_info, audio_duration from audio_info (Quillo 0.6).
audio_info(path: str) → anyor an errorEverything known about an audio file: { format, duration, bitrate, sample_rate, channels, bit_depth, title, artist, album, album_artist, year, track, genre, has_cover }. duration is in seconds (a float), bitrate in kbit/s, sample_rate in Hz; what the file does not say is null.
audio_duration(path: str) → floator an errorThe length of an audio file in seconds (a float): for a player, or to refuse uploads that are too long.
audio_cover(path: str, to: str) → stror an errorSaves the file's cover art (the front cover when there are several pictures) as to + ".jpg" or ".png" by its type, folders made: the path written. An error when it has none.
Get it
$ quillo install audio_info # audio_info.qwer into this folder$ quillo install audio_info --example # and its example, audio_info.qweOr with curl:
$ curl -O https://quillolang.org/plugins/files/audio_info.qwer$ curl -O https://quillolang.org/plugins/files/audio_info.qwe$ curl -O https://quillolang.org/plugins/files/audio_info_sample.mp3Then, in your program, all of it or only what you call:
use audio_info.qweruse audio_info, audio_duration from audio_info // Quillo 0.6: only theseIt uses this crate, which cargo fetches the first time the plugin is built:
lofty = { version = "0.22", default-features = false }Example
// A podcast page: each episode's length and tags, and its cover art served beside it.//// quillo run audio_info.qwe then open http://localhost:8080/episodeuse "audio_info.qwer"let EPISODE = "audio_info_sample.mp3"fn mmss(seconds) = `${floor(seconds / 60)}:${slice(str(100 + round(seconds) % 60), 1, 3)}`route GET /episode { let info = audio_info(EPISODE)? merge(info, { length: mmss(info.duration) })}route GET /cover { let path = audio_cover(EPISODE, "data/covers/episode-1")? file(path)}route GET / { let info = audio_info(EPISODE)? <html><body> <img src="/cover" width="120" alt=""/> <h1>{info.title}</h1> <p>{info.artist} · {info.album} · {mmss(info.duration)}</p> <audio controls src="/episode.mp3"></audio> </body></html>}route GET /episode.mp3 = file(EPISODE)$ quillo run audio_info.qwe$ curl 'localhost:8080/episode'Source
//! What an audio file holds: length, bitrate, sample rate, channels, its tags (title, artist, album...) and cover art, for MP3, FLAC, M4A, Ogg, Opus, WAV and AIFF.//! version: 1.0.0//!//! [dependencies]//! lofty = { version = "0.22", default-features = false }use lofty::file::TaggedFile;use lofty::prelude::*;use serde_json::{json, Value};const MAX_COVER: usize = 32 * 1024 * 1024;fn read(path: &str) -> Result<TaggedFile, String> { let p = path.to_string(); // A damaged file must not take the server down with it. match std::panic::catch_unwind(move || lofty::read_from_path(&p)) { Ok(Ok(f)) => Ok(f), Ok(Err(e)) => Err(format!("{}: {}", path, e)), Err(_) => Err(format!("{}: the file could not be read", path)), }}fn format_name(f: &TaggedFile) -> String { format!("{:?}", f.file_type()).to_lowercase()}/// Everything known about an audio file: `{ format, duration, bitrate, sample_rate, channels, bit_depth, title, artist, album, album_artist, year, track, genre, has_cover }`./// `duration` is in seconds (a float), `bitrate` in kbit/s, `sample_rate` in Hz; what the file does not say is null.pub fn audio_info(path: &str) -> Result<Value, String> { let f = read(path)?; let p = f.properties(); let tag = f.primary_tag().or_else(|| f.first_tag()); let text = |v: Option<std::borrow::Cow<'_, str>>| v.map(|s| s.trim().to_string()).filter(|s| !s.is_empty()); Ok(json!({ "format": format_name(&f), "duration": (p.duration().as_millis() as f64) / 1000.0, "bitrate": p.audio_bitrate().or(p.overall_bitrate()), "sample_rate": p.sample_rate(), "channels": p.channels(), "bit_depth": p.bit_depth(), "title": tag.and_then(|t| text(t.title())), "artist": tag.and_then(|t| text(t.artist())), "album": tag.and_then(|t| text(t.album())), "album_artist": tag.and_then(|t| t.get_string(&ItemKey::AlbumArtist).map(str::to_string)), "year": tag.and_then(|t| t.year()), "track": tag.and_then(|t| t.track()), "genre": tag.and_then(|t| text(t.genre())), "has_cover": f.tags().iter().any(|t| !t.pictures().is_empty()), }))}/// The length of an audio file in seconds (a float): for a player, or to refuse uploads that are too long.pub fn audio_duration(path: &str) -> Result<f64, String> { Ok(read(path)?.properties().duration().as_millis() as f64 / 1000.0)}/// Saves the file's cover art (the front cover when there are several pictures) as `to` + ".jpg" or ".png" by its type, folders made: the path written. An error when it has none.pub fn audio_cover(path: &str, to: &str) -> Result<String, String> { let f = read(path)?; let pics: Vec<_> = f.tags().iter().flat_map(|t| t.pictures().iter()).collect(); let pic = pics.iter().find(|p| p.pic_type() == lofty::picture::PictureType::CoverFront).or(pics.first()).ok_or_else(|| format!("{}: no cover art", path))?; if pic.data().len() > MAX_COVER { return Err(format!("{}: the cover is larger than 32 MB", path)); } let ext = match pic.mime_type() { Some(lofty::picture::MimeType::Png) => "png", Some(lofty::picture::MimeType::Gif) => "gif", Some(lofty::picture::MimeType::Bmp) => "bmp", _ => if pic.data().starts_with(&[0x89, b'P']) { "png" } else { "jpg" }, }; let out = format!("{}.{}", to, ext); if let Some(dir) = std::path::Path::new(&out).parent().filter(|d| !d.as_os_str().is_empty()) { std::fs::create_dir_all(dir).map_err(|e| format!("{}: {}", dir.display(), e))?; } std::fs::write(&out, pic.data()).map_err(|e| format!("{}: {}", out, e))?; Ok(out)}Related
audio_tags.qwer · 1.0.0Audio tags written as well as read, for MP3, FLAC, Ogg, M4A, WAV and AIFF: title, artist, album, track, year, genre, composer, lyrics, BPM and more, cover art set, and every tag stripped; written to a copy.8 views · 7 downloadsLoudness loudness.qwer · 1.0.0Loudness of WAV audio as broadcasters measure it (EBU R128): integrated LUFS, loudness range and true peak; silences found; leading and trailing silence trimmed; and files normalised to -16, -14 or -23 LUFS under a peak limit.7 views · 9 downloadsMIDI files midi.qwer · 1.0.0MIDI files read and written: each track's notes with their names, start and length in beats and seconds, tempo and time signature, instruments by General MIDI name; songs written from lists of notes; notes named and transposed.7 views · 7 downloadsVideo info video_info.qwer · 1.0.0An MP4 or MOV video's length, size in pixels, frame rate, codecs, bitrate and tracks, read from its header.7 views · 7 downloadsWAV wav.qwer · 1.0.0WAV files made and read: tones, melodies and silence for alerts and tests, joined end to end, and a file's length, peak and loudness.7 views · 7 downloads3D models models_3d.qwer · 1.0.0STL and OBJ models read: triangles, size, volume, surface area and whether the mesh is closed, a 3D-print quote by weight, and OBJ turned into binary STL.6 views · 7 downloadsThread 0 posts
On Audio info: questions, tips, corrections, and code that uses it.
No posts yet. The first one opens the thread.