From 46412599a3f62f467938830a0986d8d4ae1b5e2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CNaeel=E2=80=9D?= Date: Sat, 23 May 2026 07:02:01 +0400 Subject: [PATCH] =?UTF-8?q?v86:=20WebSocket=20base64-=D1=87=D0=B0=D0=BD?= =?UTF-8?q?=D0=BA=D0=B8=20=E2=80=94=20=D0=BE=D0=B1=D1=85=D0=BE=D0=B4=20DPI?= =?UTF-8?q?=20=D0=B4=D0=BB=D1=8F=20=D1=82=D1=80=D0=B0=D0=BD=D1=81=D0=BA?= =?UTF-8?q?=D1=80=D0=B8=D0=BF=D1=86=D0=B8=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- js/main.js | 2 +- js/transcribe.js | 65 ++++++++++++++++++++++++++++++++++-------------- 2 files changed, 47 insertions(+), 20 deletions(-) diff --git a/js/main.js b/js/main.js index a705e8b..78b4595 100644 --- a/js/main.js +++ b/js/main.js @@ -1,6 +1,6 @@ // ---------- Глобальные переменные и инициализация ---------- const GROQ_API_KEY = ''; -const VERSION = 'v85'; +const VERSION = 'v86'; let mediaRecorder, audioChunks = [], userAudioBlob = null, ttsText = ''; let isRecording = false, isAnalyzing = false, micAnalyser = null, micAnimId = null; diff --git a/js/transcribe.js b/js/transcribe.js index f5313db..95f1f59 100644 --- a/js/transcribe.js +++ b/js/transcribe.js @@ -1,29 +1,56 @@ -// ---------- Groq API: транскрипция и перевод ---------- +// ---------- Groq API: транскрипция (WebSocket, base64-чанки — обход DPI) ---------- async function transcribe(blob) { if (!GROQ_API_KEY) return { text: '', error: 'no_key' }; const tStart = performance.now(); - const form = new FormData(); - form.append('file', blob, 'audio.webm'); - form.append('model', 'whisper-large-v3'); - form.append('language', 'it'); + // Кодируем 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); + }); - console.log('[HTTP] blob ' + (blob.size/1024).toFixed(1) + 'KB → multipart POST'); + const CHUNK = 4096; // 4KB base64 на чанк + const total = Math.ceil(base64.length / CHUNK); + const mime = blob.type || 'audio/webm'; + console.log('[WS] blob=' + (blob.size/1024).toFixed(1) + 'KB b64=' + base64.length + ' chunks=' + total); - try { - const r = await fetch('https://lang.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' }; - } + return new Promise((resolve) => { + try { + const ws = new WebSocket('wss://proxy.kube5s.ru/ws'); + const timeout = setTimeout(() => { + console.log('[WS] timeout'); ws.close(); resolve({ text: '', error: 'timeout' }); + }, 60000); + + ws.onopen = () => { + console.log('[WS] connected, sending ' + total + ' chunks'); + for (let i = 0; i < base64.length; i += CHUNK) { + ws.send(JSON.stringify({ + c: Math.floor(i / CHUNK), + b: base64.slice(i, i + CHUNK), + t: GROQ_API_KEY, + m: mime, + n: total + })); + } + ws.send(JSON.stringify({ c: -1 })); + console.log('[WS] all sent'); + }; + + 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); ws.close(); resolve({ text: '', error: 'network' }); }; + } catch (err) { + resolve({ text: '', error: 'network' }); + } + }); } async function translateToRussian(text) {