diff --git a/index.html b/index.html index 00e43f4..4497c31 100644 --- a/index.html +++ b/index.html @@ -53,7 +53,7 @@ let mediaRecorder, audioChunks = [], userAudioBlob = null, ttsText = ''; let isRecording = false, isAnalyzing = false, micAnalyser = null, micAnimId = null; let recordStartTime = 0, recordDuration = 0; // ---------- Версия ---------- -const VERSION = 'v61'; +const VERSION = 'v62'; document.getElementById('ver').textContent = VERSION; // ---------- Перевод на русский (Groq) ---------- @@ -615,45 +615,26 @@ async function transcribe(blob) { if (!GROQ_API_KEY) return { text: '', error: 'no_key' }; const tStart = performance.now(); - // Кодируем blob в base64 (только текстовые WS-фреймы — DPI не режет) - const base64 = await new Promise((resolve, reject) => { - const reader = new FileReader(); - reader.onload = () => resolve(reader.result.split(',')[1]); - reader.onerror = reject; - reader.readAsDataURL(blob); - }); + const form = new FormData(); + form.append('file', blob, 'audio.webm'); + form.append('model', 'whisper-large-v3'); + form.append('language', 'it'); - console.log('[WS] b64=' + (base64.length/1024).toFixed(1) + 'KB'); + console.log('[HTTP] blob ' + (blob.size/1024).toFixed(1) + 'KB → multipart POST'); - return new Promise((resolve) => { - try { - const CHUNK = 14000; // макс 14KB на фрейм (DPI режет >48KB) - const total = Math.ceil(base64.length / CHUNK); - const ws = new WebSocket('wss://proxy.kube5s.ru/ws'); - const timeout = setTimeout(() => { - console.log('[WS] timeout'); ws.close(); resolve({ text: '', error: 'timeout' }); - }, 90000); - - ws.onopen = () => { - for (let i = 0; i < total; i++) { - ws.send(JSON.stringify({ c: i, n: total, t: GROQ_API_KEY, b: base64.slice(i * CHUNK, (i + 1) * CHUNK), m: blob.type || 'audio/webm' })); - } - ws.send(JSON.stringify({ c: -1 })); - console.log('[WS] sent ' + total + ' chunks'); - }; - - ws.onmessage = (e) => { - clearTimeout(timeout); ws.close(); - const data = JSON.parse(e.data); - console.log('[WS] ← ' + ((performance.now()-tStart)/1000).toFixed(2) + 's → ' + (data.text||'(empty)')); - resolve(data.error ? { text: '', error: data.error } : { text: data.text || '', error: null }); - }; - - ws.onerror = () => { clearTimeout(timeout); resolve({ text: '', error: 'network' }); }; - } catch (err) { - resolve({ text: '', error: 'network' }); - } - }); + try { + const r = await fetch('https://proxy.kube5s.ru/openai/v1/audio/transcriptions', { + method: 'POST', + headers: { 'Authorization': 'Bearer ' + GROQ_API_KEY }, + body: form + }); + const data = await r.json(); + console.log('[HTTP] ← ' + ((performance.now()-tStart)/1000).toFixed(2) + 's → ' + (data.text||'(empty)')); + return data.error ? { text: '', error: data.error } : { text: data.text || '', error: null }; + } catch(e) { + console.log('[HTTP] ERR', e); + return { text: '', error: 'network' }; + } } function normalize(text) { diff --git a/test_chunks.py b/test_chunks.py new file mode 100644 index 0000000..716f804 --- /dev/null +++ b/test_chunks.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 +"""Test: send chunked base64 to WS, check if server receives all chunks.""" +import asyncio, json, websockets, base64, os, sys + +# Generate fake audio — random bytes, base64 encoded +fake_audio = base64.b64encode(os.urandom(40000)).decode() # ~53KB base64 +CHUNK = 14000 +total = (len(fake_audio) + CHUNK - 1) // CHUNK + +async def test(): + uri = "wss://proxy.kube5s.ru/ws" + print(f"Connecting to {uri}...") + async with websockets.connect(uri) as ws: + print(f"Connected. Sending {total} chunks of {CHUNK} chars each (total b64: {len(fake_audio)} chars)...") + for i in range(total): + chunk = fake_audio[i*CHUNK:(i+1)*CHUNK] + msg = json.dumps({"c": i, "n": total, "t": "test", "b": chunk, "m": "audio/webm"}) + await ws.send(msg) + print(f" chunk {i+1}/{total} sent ({len(chunk)} chars)") + await asyncio.sleep(0.15) # 150ms между чанками — чтобы DPI не дрочил + + # Final + await asyncio.sleep(0.15) + await ws.send(json.dumps({"c": -1})) + print(" final sent, waiting for response...") + + try: + resp = await asyncio.wait_for(ws.recv(), timeout=30) + data = json.loads(resp) + print(f"Response: {data}") + except asyncio.TimeoutError: + print("TIMEOUT — no response") + +asyncio.run(test()) diff --git a/test_hello.py b/test_hello.py new file mode 100644 index 0000000..a002768 --- /dev/null +++ b/test_hello.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 +"""Test: bare minimum — connect and send hello.""" +import asyncio, json, websockets + +async def test(): + uri = "wss://proxy.kube5s.ru/ws" + print(f"Connecting to {uri}...") + try: + async with websockets.connect(uri) as ws: + print("Connected!") + await ws.send(json.dumps({"c": -1})) + print("Sent empty final, waiting...") + try: + resp = await asyncio.wait_for(ws.recv(), timeout=15) + print(f"Response: {resp}") + except asyncio.TimeoutError: + print("TIMEOUT") + except Exception as e: + print(f"ERROR: {e}") + +asyncio.run(test()) diff --git a/test_http.py b/test_http.py new file mode 100644 index 0000000..18a2dfd --- /dev/null +++ b/test_http.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +"""Test: HTTP POST large JSON body — does it pass DPI?""" +import requests, base64, os, json, time + +# Fake audio of various sizes +for kb in [30, 50, 70, 100, 150]: + audio_b64 = base64.b64encode(os.urandom(kb * 1024)).decode() + body = json.dumps({"audio_b64": audio_b64, "mime": "audio/webm"}) + size = len(body) + + t0 = time.time() + try: + r = requests.post( + "https://proxy.kube5s.ru/", + data=body, + headers={"Content-Type": "application/json"}, + timeout=30 + ) + dt = time.time() - t0 + print(f" {kb}KB audio → JSON {size/1024:.1f}KB: HTTP {r.status_code} in {dt:.1f}s ✅") + except requests.Timeout: + print(f" {kb}KB audio → JSON {size/1024:.1f}KB: TIMEOUT ❌") + except Exception as e: + print(f" {kb}KB audio → JSON {size/1024:.1f}KB: ERR {e}") + + time.sleep(1) diff --git a/test_multipart.py b/test_multipart.py new file mode 100644 index 0000000..ea9436f --- /dev/null +++ b/test_multipart.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python3 +"""Test: multipart/form-data POST binary blob — no JSON, no base64, no WS.""" +import requests, os, time + +for kb in [30, 60, 100, 150]: + blob = os.urandom(kb * 1024) + t0 = time.time() + try: + r = requests.post( + "https://proxy.kube5s.ru/", + files={"file": ("audio.webm", blob, "audio/webm")}, + timeout=30 + ) + dt = time.time() - t0 + print(f" {kb}KB blob multipart: HTTP {r.status_code} in {dt:.1f}s ✅") + except requests.Timeout: + print(f" {kb}KB blob multipart: TIMEOUT ❌") + except Exception as e: + print(f" {kb}KB blob multipart: ERR {e}") + time.sleep(1) diff --git a/test_single.py b/test_single.py new file mode 100644 index 0000000..38abfb2 --- /dev/null +++ b/test_single.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python3 +"""Test: ONE big frame — does it arrive?""" +import asyncio, json, websockets, base64, os + +async def test(size_kb): + uri = "wss://proxy.kube5s.ru/ws" + # old format: single message with all data + audio_b64 = base64.b64encode(os.urandom(size_kb * 1024)).decode() + msg = json.dumps({"token": "test", "audio_b64": audio_b64, "mime": "audio/webm"}) + print(f"Single frame {len(msg)} chars ({size_kb}KB audio → {len(msg)/1024:.1f}KB JSON)...") + try: + async with websockets.connect(uri) as ws: + await ws.send(msg) + print(" sent, waiting...") + try: + resp = await asyncio.wait_for(ws.recv(), timeout=20) + data = json.loads(resp) + print(f" => {data.get('error','OK')} ({data.get('status','')})") + except asyncio.TimeoutError: + print(" => TIMEOUT") + except Exception as e: + print(f" => CONN_ERR: {e}") + +async def main(): + for kb in [10, 25, 40, 55, 70, 100]: + await test(kb) + await asyncio.sleep(1) + +asyncio.run(main()) diff --git a/test_threshold.py b/test_threshold.py new file mode 100644 index 0000000..970e42c --- /dev/null +++ b/test_threshold.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python3 +"""Test: find DPI threshold — how many WS frames before DPI kills connection.""" +import asyncio, json, websockets, base64, os + +async def test_chunks(n_chunks, delay=0.15): + uri = "wss://proxy.kube5s.ru/ws" + chunk_data = base64.b64encode(os.urandom(5000)).decode() # ~6.7KB per chunk + try: + async with websockets.connect(uri) as ws: + # Send data chunks + for i in range(n_chunks): + msg = json.dumps({"c": i, "n": n_chunks, "t": "test", "b": chunk_data, "m": "audio/webm"}) + await ws.send(msg) + await asyncio.sleep(delay) + # Final + await ws.send(json.dumps({"c": -1})) + try: + resp = await asyncio.wait_for(ws.recv(), timeout=15) + data = json.loads(resp) + return f"OK: {data.get('error','?')}" + except asyncio.TimeoutError: + return "TIMEOUT" + except Exception as e: + return f"CONN_ERR: {e}" + +async def main(): + for n in [1, 2, 3, 4, 5, 8]: + result = await test_chunks(n, delay=0.2) + print(f" chunks={n}: {result}") + +asyncio.run(main())