sounds: upload, convert, play, rename, delete with references

/api/sounds (admin) takes the raw file, converts it with ffmpeg in one
pass to <id>.wav (8 kHz) and <id>.g722 (HD for G.722 calls) in SOUNDS_DIR,
measures it with ffprobe, and stores it as custom/<id>. Delete answers 409
with what still plays it (IVR greeting/invalid sound, announcements, the
recording notice). Sounds & music page: drop or pick a file (name
suggested from the file name), play, rename, delete. MOH classes stay in
M8. The web client and greetings share one raw-upload helper; the
api test audio helpers moved to test/audio.ts.

e2e m6-t1: drag-and-drop upload in the browser, Asterisk plays
custom/<id>, rename, delete refused while in use, then deleted.

Task: M6-T1
This commit is contained in:
iamdoubz committed 2026-10-18 10:13:58 -05:00
1 parent 609965bba5
commit 17db68a8c5
19 files changed
+752 -60

No files matched your search

+1
View File
@@ -11,3 +11,4 @@ export * from './trunks.ts';
export * from './dids.ts';
export * from './outbound.ts';
export * from './voicemail.ts';
export * from './sounds.ts';
+21
View File
@@ -0,0 +1,21 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
// Recorded prompts for IVRs, announcements and the recording notice (02 sound_files, 04 §6).
// Music-on-hold classes and their files arrive with M8.
import { z } from 'zod';
export const SoundName = z.string().trim().min(1, 'Required').max(80);
export const SoundUpdateSchema = z.object({ name: SoundName });
export type SoundUpdate = z.input<typeof SoundUpdateSchema>;
export type SoundDto = {
id: string;
name: string;
/** What dialplan plays, e.g. custom/3f2a…; never shown to people. */
asteriskName: string;
durationMs: number;
originalFilename: string | null;
createdAt: string;
};
/** Upload limits shared by the page and the api (04 §6). */
export const SOUND_MAX_BYTES = 20 * 1024 * 1024;