"""API Routes for VoiceGuard - Matching Hackathon Requirements Exactly. Endpoint: POST /api/voice-detection Auth: x-api-key header Request: {language, audioFormat, audioBase64} Response: {status, language, classification, confidenceScore, explanation} """ import time from fastapi import APIRouter, HTTPException, Header, UploadFile, File, Form from typing import Optional from app.api.schemas import ( VoiceDetectionRequest, VoiceDetectionResponse, ErrorResponse, HealthResponse ) from app.core.audio_processor import ( audio_processor, AudioProcessingError, InvalidBase64Error, ) from app.core.detector import detector from app.config import settings # Create router router = APIRouter() # ============== API KEY VALIDATION ============== def validate_api_key(x_api_key: Optional[str] = Header(None, alias="x-api-key")) -> str: """Validate API key from header.""" if not x_api_key: raise HTTPException( status_code=401, detail={"status": "error", "message": "Missing API key. Use x-api-key header."} ) if x_api_key != settings.API_KEY: raise HTTPException( status_code=401, detail={"status": "error", "message": "Invalid API key"} ) return x_api_key # ============== LOGIC HELPER ============== async def _process_detection_logic( audio_bytes: bytes, language: str ) -> VoiceDetectionResponse: """Core logic to process audio/bytes and generate response using Hybrid Detector.""" start_time = time.time() # Step 1: Process audio (bytes → convert → resample to 16kHz) print(f"📝 Processing audio for language: {language}") waveform, duration = audio_processor.process_bytes(audio_bytes) print(f"✅ Audio processed: {duration:.2f}s duration") # Step 2: Run Detection (HuBERT Model) print("🔍 Running deepfake detection...") result = detector.detect(waveform, language=language) # Calculate processing time processing_time_ms = int((time.time() - start_time) * 1000) print(f"⏱️ Total processing time: {processing_time_ms}ms") # Build response return VoiceDetectionResponse( status="success", language=language.capitalize(), classification=result.classification, confidenceScore=round(result.confidence, 2), explanation=result.explanation ) # ============== MAIN DETECTION ENDPOINT (Reference Implementation) ============== # Matches exact hackathon requirements (JSON Base64) @router.post( "/voice-detection", response_model=VoiceDetectionResponse, responses={ 401: {"model": ErrorResponse, "description": "Invalid API key"}, 400: {"model": ErrorResponse, "description": "Invalid input"}, 500: {"model": ErrorResponse, "description": "Processing error"} }, summary="Detect AI-Generated Voice (JSON)", description="Analyze an MP3 audio file (Base64) to detect if it's AI-generated." ) async def detect_voice_json( request: VoiceDetectionRequest, x_api_key: str = Header(..., alias="x-api-key") ): """Main detection endpoint (JSON/Base64).""" validate_api_key(x_api_key) try: # Decode base64 to bytes locally to reuse common logic # audio_processor.decode_base64 raises InvalidBase64Error audio_bytes = audio_processor.decode_base64(request.audioBase64) return await _process_detection_logic(audio_bytes, request.language) except InvalidBase64Error as e: raise HTTPException( status_code=400, detail={"status": "error", "message": f"Invalid Base64 encoding: {str(e)}"} ) except AudioProcessingError as e: raise HTTPException( status_code=400, detail={"status": "error", "message": str(e)} ) except Exception as e: print(f"❌ Error during detection: {e}") raise HTTPException( status_code=500, detail={"status": "error", "message": f"Detection failed: {str(e)}"} ) # ============== FILE UPLOAD ENDPOINT (For Testing/Ease of Use) ============== @router.post( "/voice-detection/file", response_model=VoiceDetectionResponse, responses={ 401: {"model": ErrorResponse, "description": "Invalid API key"}, 400: {"model": ErrorResponse, "description": "Invalid input"}, }, summary="Detect AI-Generated Voice (File Upload)", description="Upload an audio file (MP3/WAV) to detect if it's AI-generated." ) async def detect_voice_file( file: UploadFile = File(..., description="Audio file (MP3/WAV)"), language: str = Form(..., description="Language: Tamil, English, Hindi, Malayalam, Telugu"), x_api_key: str = Header(..., alias="x-api-key") ): """File upload detection endpoint.""" validate_api_key(x_api_key) try: content = await file.read() return await _process_detection_logic(content, language) except AudioProcessingError as e: raise HTTPException( status_code=400, detail={"status": "error", "message": str(e)} ) except Exception as e: print(f"❌ Error during detection: {e}") raise HTTPException( status_code=500, detail={"status": "error", "message": f"Detection failed: {str(e)}"} ) # ============== HEALTH CHECK (bonus) ============== @router.get( "/health", response_model=HealthResponse, summary="Health Check", description="Check API health and model status" ) async def health_check(): """Health check endpoint.""" return HealthResponse( status="healthy", model_loaded=detector.is_loaded, version="1.0.0" )