e1625a717d
- Format endpoint now calls ACE-Step /format_input REST API directly instead of spawning Python, fixing ENOENT errors on Windows (#44, #27, #34) - isGradioAvailable() tries /gradio_api/info, /info, / in sequence to handle Gradio 4.x/5.x/6.x version differences, fixing generation fallback (#53, #20) - Storage getUrl/getPublicUrl normalize /audio/ prefix to prevent double-prefix URLs when reference tracks are used for cover generation (#10) - Gradio args: fix normalization_db default from 0.0 to -1.0 (Gradio default) - Volume popover: add 400ms delay before hiding to prevent accidental dismissal (#51) - Polling: skip setSongs state update when nothing changed to reduce re-renders (#51) - FFmpeg: add jsdelivr CDN fallback when unpkg fails (#30) - Model switching: add switchModelIfNeeded() via /v1/init REST API (#45)
63 lines
1.9 KiB
TypeScript
63 lines
1.9 KiB
TypeScript
import { writeFile, unlink, stat, mkdir, copyFile } from 'fs/promises';
|
|
import path from 'path';
|
|
import { fileURLToPath } from 'url';
|
|
import type { StorageProvider } from './index.js';
|
|
|
|
const __filename = fileURLToPath(import.meta.url);
|
|
const __dirname = path.dirname(__filename);
|
|
const AUDIO_DIR = path.join(__dirname, '../../../public/audio');
|
|
|
|
export class LocalStorageProvider implements StorageProvider {
|
|
private audioDir: string;
|
|
|
|
constructor() {
|
|
this.audioDir = AUDIO_DIR;
|
|
}
|
|
|
|
async upload(key: string, data: Buffer, _contentType: string): Promise<string> {
|
|
const filepath = path.join(this.audioDir, key);
|
|
await mkdir(path.dirname(filepath), { recursive: true });
|
|
await writeFile(filepath, data);
|
|
return key;
|
|
}
|
|
|
|
async getUrl(key: string, _expiresIn?: number): Promise<string> {
|
|
const cleanKey = key.startsWith('/audio/') ? key.slice('/audio/'.length) : key;
|
|
return `/audio/${cleanKey.replace(/^\/+/, '')}`;
|
|
}
|
|
|
|
getPublicUrl(key: string): string {
|
|
const cleanKey = key.startsWith('/audio/') ? key.slice('/audio/'.length) : key;
|
|
return `/audio/${cleanKey.replace(/^\/+/, '')}`;
|
|
}
|
|
|
|
async delete(key: string): Promise<void> {
|
|
const filepath = path.join(this.audioDir, key);
|
|
try {
|
|
await unlink(filepath);
|
|
} catch (err) {
|
|
const error = err as NodeJS.ErrnoException;
|
|
if (error.code !== 'ENOENT') {
|
|
throw error;
|
|
}
|
|
}
|
|
}
|
|
|
|
async exists(key: string): Promise<boolean> {
|
|
const filepath = path.join(this.audioDir, key);
|
|
try {
|
|
await stat(filepath);
|
|
return true;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
async copy(sourceKey: string, destKey: string): Promise<void> {
|
|
const sourcePath = path.join(this.audioDir, sourceKey);
|
|
const destPath = path.join(this.audioDir, destKey);
|
|
await mkdir(path.dirname(destPath), { recursive: true });
|
|
await copyFile(sourcePath, destPath);
|
|
}
|
|
}
|