#!/usr/bin/env python3 from flask import Flask, request, Response import requests import base64 import uuid import time app = Flask(__name__) GROQ_BASE = "https://api.groq.com" CORS = { "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Methods": "GET, POST, OPTIONS", "Access-Control-Allow-Headers": "Authorization, Content-Type", } # Чанковая сборка аудио (DPI bypass — каждый POST <10KB) _sessions = {} # session_id -> {chunks, mime, total, token, expires} @app.route("/", methods=["OPTIONS"]) def options(path): return Response(status=204, headers=CORS) @app.route("/v1/transcribe", methods=["POST", "OPTIONS"]) def transcribe_chunked(): if request.method == "OPTIONS": return Response(status=204, headers=CORS) data = request.get_json(force=True) or {} idx = data.get("idx", 0) total = data.get("total", 1) chunk = data.get("chunk", "") mime = data.get("mime", "audio/webm") token = data.get("token", "") sid = data.get("sid", "") # Очистка старых сессий now = time.time() for k in list(_sessions.keys()): if _sessions[k]["expires"] < now: del _sessions[k] if not sid: sid = uuid.uuid4().hex[:12] _sessions[sid] = {"chunks": {}, "mime": mime, "total": total, "token": token, "expires": now + 120} else: s = _sessions.get(sid) if not s: return Response('{"error":"session not found"}', status=404, headers=CORS, content_type="application/json") _sessions[sid]["chunks"][idx] = chunk _sessions[sid]["expires"] = now + 120 if len(_sessions[sid]["chunks"]) >= total: s = _sessions.pop(sid) audio_b64 = "".join(s["chunks"][i] for i in sorted(s["chunks"])) audio_data = base64.b64decode(audio_b64) ext = "webm" if "ogg" in s["mime"]: ext = "ogg" elif "mp4" in s["mime"] or "aac" in s["mime"]: ext = "mp4" elif "wav" in s["mime"]: ext = "wav" boundary = "----ChunkedBoundary" + uuid.uuid4().hex[:16] payload = b"".join([ f'--{boundary}\r\nContent-Disposition: form-data; name="file"; filename="audio.{ext}"\r\nContent-Type: {s["mime"]}\r\n\r\n'.encode(), audio_data, f'\r\n--{boundary}\r\nContent-Disposition: form-data; name="model"\r\n\r\nwhisper-large-v3'.encode(), f'\r\n--{boundary}\r\nContent-Disposition: form-data; name="language"\r\n\r\nit'.encode(), f'\r\n--{boundary}--\r\n'.encode(), ]) r = requests.post(f"{GROQ_BASE}/openai/v1/audio/transcriptions", headers={"Authorization": f"Bearer {s['token']}", "Content-Type": f"multipart/form-data; boundary={boundary}"}, data=payload, timeout=60) result = r.json() out = dict(CORS) out["Content-Type"] = "application/json" return Response(r.text, status=r.status_code, headers=out) else: return Response(f'{{"ok":true,"sid":"{sid}"}}', status=200, headers=CORS, content_type="application/json") @app.route("/", methods=["GET", "POST", "PUT", "DELETE", "PATCH"]) def proxy(subpath): url = f"{GROQ_BASE}/{subpath}" hdrs = {} if request.headers.get("Authorization"): hdrs["Authorization"] = request.headers["Authorization"] if request.headers.get("Content-Type"): hdrs["Content-Type"] = request.headers["Content-Type"] r = requests.request(request.method, url, headers=hdrs, data=request.get_data(), timeout=60, stream=True) out = dict(CORS) out["Content-Type"] = r.headers.get("Content-Type", "application/json") return Response(r.iter_content(8192), status=r.status_code, headers=out) if __name__ == "__main__": app.run(host="127.0.0.1", port=8765)