v103: РАБОТАЕТ — транскрипция через ProxyAPI.ru (whisper-1), конвертация webm→WAV в браузере
This commit is contained in:
+7
-68
@@ -1,12 +1,9 @@
|
||||
#!/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"
|
||||
GROQ_BASE = "https://api.proxyapi.ru/openai"
|
||||
|
||||
CORS = {
|
||||
"Access-Control-Allow-Origin": "*",
|
||||
@@ -14,72 +11,10 @@ CORS = {
|
||||
"Access-Control-Allow-Headers": "Authorization, Content-Type",
|
||||
}
|
||||
|
||||
# Чанковая сборка аудио (DPI bypass — каждый POST <10KB)
|
||||
_sessions = {} # session_id -> {chunks, mime, total, token, expires}
|
||||
|
||||
@app.route("/<path:path>", 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("/<path:subpath>", methods=["GET", "POST", "PUT", "DELETE", "PATCH"])
|
||||
def proxy(subpath):
|
||||
url = f"{GROQ_BASE}/{subpath}"
|
||||
@@ -88,11 +23,15 @@ def proxy(subpath):
|
||||
hdrs["Authorization"] = request.headers["Authorization"]
|
||||
if request.headers.get("Content-Type"):
|
||||
hdrs["Content-Type"] = request.headers["Content-Type"]
|
||||
body = request.get_data()
|
||||
print(f"[PROXY] {request.method} {subpath} body={len(body)}b ct={hdrs.get('Content-Type','')[:50]}", flush=True)
|
||||
r = requests.request(request.method, url, headers=hdrs,
|
||||
data=request.get_data(), timeout=60, stream=True)
|
||||
data=body, 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)
|
||||
raw = r.content
|
||||
print(f"[PROXY] ← {r.status_code} {raw[:200]}", flush=True)
|
||||
return Response(raw, status=r.status_code, headers=out)
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(host="127.0.0.1", port=8765)
|
||||
|
||||
Reference in New Issue
Block a user