All files / web/src/app/api/audio/collected-clips/manifest route.ts

0% Statements 0/65
0% Branches 0/1
0% Functions 0/1
0% Lines 0/65

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                                                                                                                                   
import { NextResponse } from 'next/server'
import { existsSync, readdirSync } from 'fs'
import { join } from 'path'
import { withAuth } from '@/lib/auth/withAuth'

const AUDIO_DIR = join(process.cwd(), 'data', 'audio')

/**
 * GET /api/audio/collected-clips/manifest?voices=onyx,nova
 *
 * Lightweight endpoint returning which clip IDs have pre-generated mp3 files
 * for each requested voice. Includes both static manifest clips ({clipId}.mp3)
 * and collected clips (cc-{clipId}.mp3). Used by TtsAudioManager at boot to
 * know which clips can play pre-generated audio.
 *
 * Response: { clipIdsByVoice: { onyx: ["abc123", ...], nova: [...] } }
 */
export const GET = withAuth(async (request) => {
  try {
    const voicesParam = request.nextUrl.searchParams.get('voices')
    if (!voicesParam) {
      return NextResponse.json({ clipIdsByVoice: {} })
    }

    const voices = voicesParam
      .split(',')
      .map((v) => v.trim())
      .filter(Boolean)
    const clipIdsByVoice: Record<string, string[]> = {}

    for (const voice of voices) {
      const voiceDir = join(AUDIO_DIR, voice)
      if (!existsSync(voiceDir)) {
        clipIdsByVoice[voice] = []
        continue
      }

      const files = readdirSync(voiceDir)
      const clipIds: string[] = []
      for (const f of files) {
        if (f === '.deactivated') continue
        if (f.endsWith('.mp3')) {
          if (f.startsWith('cc-')) {
            clipIds.push(f.slice(3, -4))
          } else {
            clipIds.push(f.slice(0, -4))
          }
        } else if (f.endsWith('.webm')) {
          if (f.startsWith('cc-')) {
            clipIds.push(f.slice(3, -5))
          } else {
            clipIds.push(f.slice(0, -5))
          }
        }
      }

      clipIdsByVoice[voice] = clipIds
    }

    return NextResponse.json({ clipIdsByVoice })
  } catch (error) {
    console.error('Error fetching collected clips manifest:', error)
    return NextResponse.json({ error: 'Failed to fetch manifest' }, { status: 500 })
  }
})