feat(soundboard): clip duration, playing indicator, volume layout, name wrap
Editor (SoundboardPackEditor): show each clip's length in seconds (stored on upload via getAudioDurationMs, and captured on preview for existing clips); the preview button now toggles play/stop with a 'now playing' equalizer indicator; reworked the volume control into a fixed cell with a % readout so the slider's max no longer collides with the delete button. Call soundboard: clip names wrap (up to 3 lines, word-break) instead of being truncated with an ellipsis; cards grow to fit. TODO: logged the basic audio-editor / video->audio-extractor as a large project. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -53,3 +53,20 @@ export const playClipLocally = (
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
/** Read an audio file's duration in milliseconds from its metadata (no playback). */
|
||||
export const getAudioDurationMs = (file: Blob): Promise<number | undefined> =>
|
||||
new Promise((resolve) => {
|
||||
const url = URL.createObjectURL(file);
|
||||
const audio = new Audio();
|
||||
audio.preload = 'metadata';
|
||||
const done = (ms: number | undefined) => {
|
||||
URL.revokeObjectURL(url);
|
||||
resolve(ms);
|
||||
};
|
||||
audio.addEventListener('loadedmetadata', () =>
|
||||
done(Number.isFinite(audio.duration) ? Math.round(audio.duration * 1000) : undefined),
|
||||
);
|
||||
audio.addEventListener('error', () => done(undefined));
|
||||
audio.src = url;
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user