Spaces:
Running
Running
| import whisper | |
| import tempfile | |
| import os | |
| import httpx | |
| from fastapi import FastAPI, UploadFile, File, Form | |
| from fastapi.responses import JSONResponse, HTMLResponse | |
| from fastapi.middleware.cors import CORSMiddleware | |
| import uvicorn | |
| app = FastAPI(title="Whisper Transcription API") | |
| print("Loading Whisper model...") | |
| model = whisper.load_model("base") | |
| print("Model loaded.") | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=["*"], | |
| allow_methods=["*"], | |
| allow_headers=["*"], | |
| ) | |
| async def root(): | |
| return """ | |
| <!DOCTYPE html> | |
| <html> | |
| <head> | |
| <title>Whisper Transcription API</title> | |
| <style> | |
| body { font-family: monospace; background: #0d0d0d; color: #e0e0e0; padding: 40px; } | |
| h1 { color: #7fff7f; } | |
| a { color: #7fbfff; } | |
| code { background: #1a1a1a; padding: 2px 6px; border-radius: 4px; } | |
| </style> | |
| </head> | |
| <body> | |
| <h1>ποΈ Whisper Transcription API</h1> | |
| <p>Status: <strong style="color:#7fff7f">Running</strong></p> | |
| <p>Endpoints:</p> | |
| <ul> | |
| <li><code>POST /transcribe</code> β Upload audio file</li> | |
| <li><code>POST /transcribe-url</code> β Transcribe from URL</li> | |
| <li><code>GET /health</code> β Health check</li> | |
| <li><a href="/docs">π Swagger Docs</a></li> | |
| </ul> | |
| </body> | |
| </html> | |
| """ | |
| async def transcribe( | |
| file: UploadFile = File(...), | |
| language: str = Form(default="auto") | |
| ): | |
| suffix = os.path.splitext(file.filename)[-1] if file.filename else ".wav" | |
| with tempfile.NamedTemporaryFile(suffix=suffix, delete=False) as tmp: | |
| content = await file.read() | |
| tmp.write(content) | |
| tmp_path = tmp.name | |
| try: | |
| lang = language if language != "auto" else None | |
| result = model.transcribe(tmp_path, language=lang) | |
| return JSONResponse({ | |
| "text": result["text"].strip(), | |
| "language": result.get("language", "unknown"), | |
| "segments": [ | |
| { | |
| "start": round(s["start"], 2), | |
| "end": round(s["end"], 2), | |
| "text": s["text"].strip() | |
| } | |
| for s in result.get("segments", []) | |
| ] | |
| }) | |
| except Exception as e: | |
| return JSONResponse({"error": str(e)}, status_code=500) | |
| finally: | |
| os.unlink(tmp_path) | |
| async def transcribe_url( | |
| url: str = Form(...), | |
| language: str = Form(default="auto") | |
| ): | |
| try: | |
| async with httpx.AsyncClient(timeout=60, follow_redirects=True) as client: | |
| r = await client.get(url, headers={ | |
| "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36", | |
| "Accept": "*/*", | |
| "Accept-Language": "en-US,en;q=0.9", | |
| "Referer": "https://www.google.com/" | |
| }) | |
| r.raise_for_status() | |
| except Exception as e: | |
| return JSONResponse({"error": f"Failed to download: {str(e)}"}, status_code=400) | |
| ext = os.path.splitext(url.split("?")[0])[-1] or ".mp3" | |
| with tempfile.NamedTemporaryFile(suffix=ext, delete=False) as tmp: | |
| tmp.write(r.content) | |
| tmp_path = tmp.name | |
| try: | |
| lang = language if language != "auto" else None | |
| result = model.transcribe(tmp_path, language=lang) | |
| return JSONResponse({ | |
| "text": result["text"].strip(), | |
| "language": result.get("language", "unknown"), | |
| "segments": [ | |
| { | |
| "start": round(s["start"], 2), | |
| "end": round(s["end"], 2), | |
| "text": s["text"].strip() | |
| } | |
| for s in result.get("segments", []) | |
| ] | |
| }) | |
| except Exception as e: | |
| return JSONResponse({"error": str(e)}, status_code=500) | |
| finally: | |
| os.unlink(tmp_path) | |
| async def health(): | |
| return {"status": "ok", "model": "whisper-base"} | |
| if __name__ == "__main__": | |
| uvicorn.run(app, host="0.0.0.0", port=7860) | |