Subtrans / app /main.py
arjun-ms's picture
feat: show Gemini option in UI and fix provider logic
a181359
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)