157 lines
4.2 KiB
Python
157 lines
4.2 KiB
Python
from __future__ import annotations
|
|
|
|
import threading
|
|
import uuid
|
|
from pathlib import Path
|
|
|
|
from fastapi import FastAPI, UploadFile, File, Form, Request
|
|
from fastapi.responses import HTMLResponse, FileResponse, JSONResponse
|
|
from fastapi.templating import Jinja2Templates
|
|
|
|
from app.services.job_store import jobs
|
|
from app.services.subtitle_service import translate_srt_content, parse_srt
|
|
|
|
app = FastAPI()
|
|
templates = Jinja2Templates(directory="app/templates")
|
|
|
|
OUTPUT_DIR = Path("data/output")
|
|
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
|
|
|
|
|
|
@app.get("/", response_class=HTMLResponse)
|
|
async def home(request: Request):
|
|
return templates.TemplateResponse(
|
|
request=request,
|
|
name="index.html",
|
|
context={},
|
|
)
|
|
|
|
|
|
@app.post("/start")
|
|
async def start_translation(
|
|
file: UploadFile = File(...),
|
|
mode: str = Form("fast"),
|
|
source_lang: str = Form("auto"),
|
|
target_lang: str = Form("da"),
|
|
model: str = Form("gpt-4o-mini"),
|
|
workers: int = Form(4),
|
|
):
|
|
try:
|
|
raw = await file.read()
|
|
content = raw.decode("utf-8-sig")
|
|
except UnicodeDecodeError:
|
|
return JSONResponse(
|
|
{"error": "Kunne ikke læse filen som UTF-8/UTF-8-SIG"},
|
|
status_code=400,
|
|
)
|
|
|
|
if workers < 1:
|
|
workers = 1
|
|
if workers > 16:
|
|
workers = 16
|
|
|
|
job_id = str(uuid.uuid4())
|
|
output_path = OUTPUT_DIR / f"{job_id}.srt"
|
|
|
|
try:
|
|
parsed_blocks = parse_srt(content)
|
|
block_count = len(parsed_blocks)
|
|
except Exception:
|
|
block_count = 0
|
|
|
|
jobs[job_id] = {
|
|
"status": "queued",
|
|
"progress": 0,
|
|
"filename": file.filename or "translated.srt",
|
|
"output_path": str(output_path),
|
|
"blocks": block_count,
|
|
"error": "",
|
|
"done": False,
|
|
"mode": mode,
|
|
"source_lang": source_lang,
|
|
"target_lang": target_lang,
|
|
"model": model,
|
|
"workers": workers,
|
|
}
|
|
|
|
def progress_callback(done_blocks: int, total_blocks: int):
|
|
if total_blocks <= 0:
|
|
jobs[job_id]["progress"] = 1
|
|
return
|
|
|
|
percent = int((done_blocks / total_blocks) * 100)
|
|
|
|
if done_blocks > 0:
|
|
percent = max(2, percent)
|
|
|
|
percent = min(99, percent)
|
|
|
|
jobs[job_id]["progress"] = percent
|
|
jobs[job_id]["status"] = "running"
|
|
jobs[job_id]["blocks"] = total_blocks
|
|
|
|
def worker():
|
|
try:
|
|
jobs[job_id]["status"] = "starting"
|
|
jobs[job_id]["progress"] = 1
|
|
|
|
translated_srt = translate_srt_content(
|
|
content=content,
|
|
mode=mode,
|
|
source_lang=source_lang,
|
|
target_lang=target_lang,
|
|
job_id=job_id,
|
|
progress_callback=progress_callback,
|
|
model=model,
|
|
workers=workers,
|
|
)
|
|
|
|
output_path.write_text(translated_srt, encoding="utf-8")
|
|
|
|
jobs[job_id]["status"] = "done"
|
|
jobs[job_id]["progress"] = 100
|
|
jobs[job_id]["done"] = True
|
|
|
|
except Exception as e:
|
|
jobs[job_id]["status"] = "error"
|
|
jobs[job_id]["progress"] = 0
|
|
jobs[job_id]["error"] = str(e)
|
|
jobs[job_id]["done"] = False
|
|
|
|
threading.Thread(target=worker, daemon=True).start()
|
|
|
|
return JSONResponse({"job_id": job_id})
|
|
|
|
|
|
@app.get("/status/{job_id}")
|
|
async def get_status(job_id: str):
|
|
job = jobs.get(job_id)
|
|
if not job:
|
|
return JSONResponse(
|
|
{"status": "unknown", "progress": 0, "error": "Job not found"},
|
|
status_code=404,
|
|
)
|
|
return JSONResponse(job)
|
|
|
|
|
|
@app.get("/download/{job_id}")
|
|
async def download_result(job_id: str):
|
|
job = jobs.get(job_id)
|
|
if not job:
|
|
return JSONResponse({"error": "Job not found"}, status_code=404)
|
|
|
|
if not job.get("done"):
|
|
return JSONResponse({"error": "File not ready yet"}, status_code=400)
|
|
|
|
path = Path(job["output_path"])
|
|
if not path.exists():
|
|
return JSONResponse({"error": "Output file missing"}, status_code=404)
|
|
|
|
original_name = job.get("filename", "translated.srt")
|
|
download_name = f"translated_{original_name}"
|
|
|
|
return FileResponse(
|
|
path=path,
|
|
media_type="application/x-subrip",
|
|
filename=download_name,
|
|
)
|