Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | import { readdirSync, statSync, existsSync } from 'fs' import { join } from 'path' import { eq, sql } from 'drizzle-orm' import { NextResponse } from 'next/server' import { db } from '@/db' import { appSettings, DEFAULT_APP_SETTINGS, ttsCollectedClips } from '@/db/schema' import { AUDIO_MANIFEST } from '@/lib/audio/audioManifest' import { ALL_VOICES } from '@/lib/audio/voices' import { withAuth } from '@/lib/auth/withAuth' const AUDIO_DIR = join(process.cwd(), 'data', 'audio') /** * GET /api/admin/audio * * Returns the audio manifest, active voice, and per-voice clip counts. */ export const GET = withAuth( async () => { try { // Fetch active voice from DB const [settings] = await db .select() .from(appSettings) .where(eq(appSettings.id, 'default')) .limit(1) const activeVoice = settings?.audioVoice ?? DEFAULT_APP_SETTINGS.audioVoice // Scan data/audio/ for voice directories const voices: Record<string, { total: number; existing: number }> = {} const manifestFilenames = new Set(AUDIO_MANIFEST.map((e) => e.filename)) if (existsSync(AUDIO_DIR)) { const entries = readdirSync(AUDIO_DIR) for (const entry of entries) { const entryPath = join(AUDIO_DIR, entry) if (statSync(entryPath).isDirectory()) { const files = readdirSync(entryPath) const existingCount = files.filter((f) => manifestFilenames.has(f)).length voices[entry] = { total: AUDIO_MANIFEST.length, existing: existingCount, } } } } // Count total collected clips (denominator for health metric) const [{ count: totalCollectedClips }] = await db .select({ count: sql<number>`count(*)` }) .from(ttsCollectedClips) // Count audio files per voice directory for all known + custom voices const voiceClipCounts: Record<string, number> = {} const knownVoices = new Set<string>(ALL_VOICES) // Scan all directories under data/audio/ to catch custom voices too const allVoiceDirs: string[] = [...ALL_VOICES] if (existsSync(AUDIO_DIR)) { for (const entry of readdirSync(AUDIO_DIR)) { const entryPath = join(AUDIO_DIR, entry) if (statSync(entryPath).isDirectory() && !knownVoices.has(entry)) { allVoiceDirs.push(entry) } } } for (const voice of allVoiceDirs) { const voiceDir = join(AUDIO_DIR, voice) if (existsSync(voiceDir)) { const files = readdirSync(voiceDir) voiceClipCounts[voice] = files.filter( (f) => f.endsWith('.mp3') || f.endsWith('.webm') ).length } else { voiceClipCounts[voice] = 0 } } return NextResponse.json({ activeVoice, manifest: AUDIO_MANIFEST, voices, totalCollectedClips, voiceClipCounts, }) } catch (error) { console.error('Error fetching audio status:', error) return NextResponse.json({ error: 'Failed to fetch audio status' }, { status: 500 }) } }, { role: 'admin' } ) |