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 environment variables from .env load_dotenv() app = FastAPI(title="AI Subtitle Generator") # Create required directories 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 Phonetic Bias List WHISPER_PROMPT = "Naukri, NotebookLM, Razorpay, LinkedIn, Bay Area, San Francisco, notebooklm.google.com" # Project-wide Glossary for AI Job Hunt and tech context PROJECT_GLOSSARY = { "Naukri": "Naukri", "NotebookLM": "NotebookLM", "Razorpay": "Razorpay", "LinkedIn": "LinkedIn", "notebooklm.google.com": "notebooklm.google.com", "nerve-wracking": "ആവേശകരമായ", # Contextual mapping for Malayalam "see you": "കാണാം", # Cultural closing } 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"), ): # Save uploaded video 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 extract_audio(video_path, audio_path) # Transcribe audio to get segments (with phonetic bias) segments, info = transcribe_audio(audio_path, initial_prompt=WHISPER_PROMPT) # Correct English transcription errors (brands/names) apply_precision_patch(segments) # Generate English SRT 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 ) # Clean up large video and audio files to save space 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)