import { CreateSoundCommand } from '../core/commands/assetCommands'; import type { SoundStore, UploadedSound } from '../core/store/SoundStore'; import { type ImportKind, type ImportOutcome, importFiles, importStored, pickAndImport } from './assetImport'; import type { EditorState } from './state'; /** What the file dialog offers and a drop accepts; the server rejects everything else anyway. */ export const SOUND_ACCEPT = '.mp3,.ogg,.wav,.m4a'; const SOUND_EXTENSION = /\.(mp3|ogg|wav|m4a)$/i; /** * Sounds are told apart by their name, not their MIME type: browsers report a dropped `.m4a` as * `audio/mp4`, `audio/x-m4a` or nothing at all, and an `.ogg` as `audio/ogg` or `video/ogg`. */ export function isSoundFileName(name: string): boolean { return SOUND_EXTENSION.test(name); } export interface SoundImportDeps { state: EditorState; sounds: SoundStore; } const SOUND_KIND: ImportKind = { type: 'sound', noun: 'sound', fallbackName: 'Sound', store: (deps) => deps.sounds, // The bytes go up untouched: nothing here could encode audio, and a re-encode would be a loss. prepare: (file) => isSoundFileName(file.name) ? Promise.resolve({ blob: file, type: file.type || 'application/octet-stream' }) : Promise.reject(new Error('Not a sound file – use MP3, OGG, WAV or M4A.')), create: (name, sound) => new CreateSoundCommand(name, sound.file), }; export function importSounds(files: readonly File[], deps: SoundImportDeps): Promise { return importFiles(SOUND_KIND, files, deps); } export function importStoredSound(sound: UploadedSound, deps: SoundImportDeps): ImportOutcome { return importStored(SOUND_KIND, sound, deps); } export function pickAndImportSounds(deps: SoundImportDeps): void { pickAndImport(SOUND_KIND, SOUND_ACCEPT, deps); }