import os import shutil from fastapi import FastAPI, UploadFile, Form, HTTPException from fastapi.responses import FileResponse, JSONResponse from fastapi.middleware.cors import CORSMiddleware from fastapi.staticfiles import StaticFiles import time # Import existing logic exactly as it was from utils import load_and_validate_audio, save_temp_wav, save_generated_audio, generate_history_list from generate import generate_speech from config import OUTPUTS_DIR, UPLOADS_DIR app = FastAPI(title="Voice Cloning API") # Add CORS to allow frontend communication app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) @app.post("/api/generate") async def api_generate( audio_file: UploadFile, text_prompt: str = Form(...) ): if not text_prompt or not text_prompt.strip(): raise HTTPException(status_code=400, detail="Please enter some text to generate.") temp_upload_path = os.path.join(UPLOADS_DIR, audio_file.filename) try: # Save the uploaded file temporarily with open(temp_upload_path, "wb") as buffer: shutil.copyfileobj(audio_file.file, buffer) # 1. Load and validate (Using EXACT same logic as before) y, sr = load_and_validate_audio(temp_upload_path) # 2. Save processed wav for model processed_path = save_temp_wav(y, sr, temp_upload_path) # 3. Generate speech out_sr, out_y, gen_time = generate_speech(text_prompt, processed_path) # 4. Save output out_path = save_generated_audio(out_y, out_sr, text_prompt) # Cleanup processed path if os.path.exists(processed_path): os.remove(processed_path) return JSONResponse(content={ "success": True, "message": f"Generation successful in {gen_time:.1f} seconds.", "generation_time": gen_time, "audio_url": f"/outputs/{os.path.basename(out_path)}" }) except ValueError as ve: raise HTTPException(status_code=400, detail=str(ve)) except Exception as e: raise HTTPException(status_code=500, detail=f"An error occurred: {str(e)}") finally: # Cleanup original upload if os.path.exists(temp_upload_path): os.remove(temp_upload_path) @app.get("/api/history") def get_history(): history = generate_history_list() # history returns [(filename, path, size_str), ...] # We map it to JSON for the frontend items = [] for f in history: items.append({ "filename": f[0], "url": f"/outputs/{f[0]}", "size": f[2] }) return {"history": items} @app.delete("/api/history/{filename}") def delete_history_item(filename: str): file_path = os.path.join(OUTPUTS_DIR, filename) if os.path.exists(file_path): os.remove(file_path) return {"success": True} raise HTTPException(status_code=404, detail="File not found") @app.get("/outputs/{filename}") def serve_audio(filename: str): file_path = os.path.join(OUTPUTS_DIR, filename) if os.path.exists(file_path): return FileResponse(file_path) raise HTTPException(status_code=404, detail="File not found") # Serve the compiled React frontend static files dist_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "dist") if os.path.exists(dist_path): app.mount("/", StaticFiles(directory=dist_path, html=True), name="static")