ocr-app-private / api_server.py
MasterSayn's picture
Upload api_server.py with huggingface_hub
b22c376 verified
Raw History Blame Contribute Delete
3.13 kB
"""
FastAPI Server-Sent Events (SSE) Streaming Server für Math & Handwritten OCR Studio Pro.
Ermöglicht seitenweises Echtzeit-Streaming von OCR-Ergebnissen an das iPadOS Frontend.
"""
import os
import sys
import json
import asyncio
from fastapi import FastAPI, File, UploadFile, Form, HTTPException
from fastapi.responses import StreamingResponse, JSONResponse
from fastapi.middleware.cors import CORSMiddleware
import fitz
# Importiere Core-Pipeline
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from gradio_api import process_pdf, get_gemini_api_keys
app = FastAPI(title="Math & Handwritten OCR Studio Pro API", version="2.6.0")
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
@app.get("/health")
async def health_check():
return {"status": "ok", "version": "2.6.0", "streaming": "SSE active"}
@app.post("/api/stream_ocr")
async def stream_ocr(
file: UploadFile = File(...),
mode: str = Form("Schnell (Gemini Full-Page)"),
api_key: str = Form("")
):
"""
Asynchroner SSE-Endpunkt: Streamt OCR-Ergebnisse seitenweise als Server-Sent Events.
"""
if not file.filename.endswith(".pdf"):
raise HTTPException(status_code=400, detail="Nur PDF-Dateien werden unterstützt.")
# Speichere Upload temporär
temp_dir = "temp_uploads"
os.makedirs(temp_dir, exist_ok=True)
temp_path = os.path.join(temp_dir, file.filename)
with open(temp_path, "wb") as f:
f.write(await file.read())
async def event_generator():
try:
# Sende Initial Event
yield f"data: {json.dumps({'event': 'start', 'file': file.filename})}\n\n"
await asyncio.sleep(0.01)
# Verarbeite PDF in der Pipeline
out_pdf_path = await asyncio.to_thread(
process_pdf,
temp_path,
mode,
False, # smart_skip
api_key if api_key.strip() else None
)
# Lese generiertes PDF für Vorschau-Seiten
doc = fitz.open(out_pdf_path)
num_pages = len(doc)
for p_idx in range(num_pages):
page = doc[p_idx]
text = page.get_text()
payload = {
'event': 'page_completed',
'page': p_idx + 1,
'total_pages': num_pages,
'text_snippet': text[:300],
'pdf_url': out_pdf_path if p_idx == num_pages - 1 else None
}
yield f"data: {json.dumps(payload)}\n\n"
await asyncio.sleep(0.05)
doc.close()
yield f"data: {json.dumps({'event': 'finished', 'output_pdf': out_pdf_path})}\n\n"
except Exception as err:
yield f"data: {json.dumps({'event': 'error', 'message': str(err)})}\n\n"
return StreamingResponse(event_generator(), media_type="text/event-stream")
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=7860)