| import os |
| import shutil |
| import uuid |
| import time |
| from dotenv import load_dotenv |
| from fastapi import FastAPI, File, UploadFile, Form, Request |
| from fastapi.responses import HTMLResponse, FileResponse |
| from fastapi.staticfiles import StaticFiles |
| from fastapi.templating import Jinja2Templates |
|
|
| from app.services.transcribe import extract_audio, transcribe_audio |
| from app.services.srt_generator import save_srt, translate_srt |
| from app.services.precision_patch import apply_precision_patch |
| from app.services.translators.deep_translator_adapter import DeepTranslatorAdapter |
|
|
| |
| load_dotenv() |
|
|
| app = FastAPI(title="AI Subtitle Generator") |
|
|
| |
| os.makedirs("app/uploads", exist_ok=True) |
| os.makedirs("app/subtitles", exist_ok=True) |
| os.makedirs("app/static", exist_ok=True) |
| os.makedirs("app/templates", exist_ok=True) |
|
|
| app.mount("/static", StaticFiles(directory="app/static"), name="static") |
| templates = Jinja2Templates(directory="app/templates") |
|
|
| |
| WHISPER_PROMPT = "Naukri, NotebookLM, Razorpay, LinkedIn, Bay Area, San Francisco, notebooklm.google.com" |
|
|
| |
| PROJECT_GLOSSARY = { |
| "Naukri": "Naukri", |
| "NotebookLM": "NotebookLM", |
| "Razorpay": "Razorpay", |
| "LinkedIn": "LinkedIn", |
| "notebooklm.google.com": "notebooklm.google.com", |
| "nerve-wracking": "ആവേശകരമായ", |
| "see you": "കാണാം", |
| } |
|
|
| def get_translator(provider: str): |
| """Instantiate the chosen translation adapter. Falls back to Google Translate.""" |
| if provider == "groq": |
| try: |
| from app.services.translators.groq_adapter import GroqAdapter |
| return GroqAdapter() |
| except Exception as e: |
| print(f"Groq unavailable ({e}), falling back to Google Translate.") |
| return DeepTranslatorAdapter() |
| elif provider == "gemini": |
| try: |
| from app.services.translators.gemini_adapter import GeminiAdapter |
| return GeminiAdapter() |
| except Exception as e: |
| print(f"Gemini unavailable ({e}), falling back to Google Translate.") |
| return DeepTranslatorAdapter() |
| return DeepTranslatorAdapter() |
|
|
| @app.get("/", response_class=HTMLResponse) |
| async def index(request: Request): |
| groq_available = bool(os.environ.get("GROQ_API_KEY_2", "").strip()) |
| gemini_available = bool(os.environ.get("GEMINI_API_KEY", "").strip()) |
| return templates.TemplateResponse( |
| request=request, |
| name="index.html", |
| context={ |
| "groq_available": groq_available, |
| "gemini_available": gemini_available |
| } |
| ) |
|
|
|
|
|
|
| @app.post("/generate-subtitles") |
| async def generate_subtitles( |
| video_file: UploadFile = File(...), |
| target_lang: str = Form(...), |
| provider: str = Form("google"), |
| ): |
| |
| base_name = os.path.splitext(video_file.filename)[0] |
| safe_name = "".join([c for c in base_name if c.isalnum() or c in " ._-"]).strip() |
| file_id = safe_name if safe_name else "video" |
| |
| ext = os.path.splitext(video_file.filename)[1] |
| version = time.strftime("%I-%M-%p--%d-%m-%Y") |
| |
| upload_dir = f"app/uploads/{version}" |
| subtitles_dir = f"app/subtitles/{version}" |
| os.makedirs(upload_dir, exist_ok=True) |
| os.makedirs(subtitles_dir, exist_ok=True) |
| |
| video_path = f"{upload_dir}/{file_id}{ext}" |
| audio_path = f"{upload_dir}/{file_id}.wav" |
| |
| with open(video_path, "wb") as buffer: |
| shutil.copyfileobj(video_file.file, buffer) |
| |
| |
| extract_audio(video_path, audio_path) |
| |
| |
| segments, info = transcribe_audio(audio_path, initial_prompt=WHISPER_PROMPT) |
| |
| |
| apply_precision_patch(segments) |
| |
| |
| en_srt_path = f"{subtitles_dir}/{file_id}_en.srt" |
| save_srt(segments, en_srt_path) |
| |
| translator = get_translator(provider) |
| target_srt_path = f"{subtitles_dir}/{file_id}_{target_lang}.srt" |
| translate_srt( |
| en_srt_path, |
| target_srt_path, |
| target_lang, |
| translator, |
| validate=True, |
| glossary=PROJECT_GLOSSARY |
| ) |
| |
| |
| if os.path.exists(video_path): |
| os.remove(video_path) |
| if os.path.exists(audio_path): |
| os.remove(audio_path) |
| |
| return { |
| "english_srt": f"/download/{version}/{file_id}_en.srt", |
| "translated_srt": f"/download/{version}/{file_id}_{target_lang}.srt", |
| "message": "Subtitles generated successfully!" |
| } |
|
|
| @app.get("/download/{version_dir}/{filename}") |
| async def download_file(version_dir: str, filename: str): |
| file_path = f"app/subtitles/{version_dir}/{filename}" |
| return FileResponse(file_path, filename=filename) |
|
|
|
|