v64: рефакторинг — разбил монолит на css/ js/ build.sh dist/ (781→43 строки index.html)
This commit is contained in:
+120
@@ -0,0 +1,120 @@
|
||||
// ---------- Глобальные переменные и инициализация ----------
|
||||
const GROQ_API_KEY = '';
|
||||
const VERSION = 'v64';
|
||||
|
||||
let mediaRecorder, audioChunks = [], userAudioBlob = null, ttsText = '';
|
||||
let isRecording = false, isAnalyzing = false, micAnalyser = null, micAnimId = null;
|
||||
let recordStartTime = 0, recordDuration = 0;
|
||||
|
||||
function setStatus(msg) { document.getElementById('status').textContent = msg; }
|
||||
function enable(id, val) { document.getElementById(id).disabled = !val; }
|
||||
|
||||
// ---------- Инициализация ----------
|
||||
document.getElementById('ver').textContent = VERSION;
|
||||
renderHistory();
|
||||
renderRecHistory();
|
||||
|
||||
// ---------- Кнопка Запись ----------
|
||||
document.getElementById('btnRecord').onclick = async function () {
|
||||
const btn = this;
|
||||
if (isRecording) {
|
||||
btn.disabled = true;
|
||||
stopRecording();
|
||||
return;
|
||||
}
|
||||
try {
|
||||
isRecording = true;
|
||||
btn.textContent = '■ Стоп';
|
||||
await startRecording();
|
||||
} catch (err) {
|
||||
let msg = '⛔ ';
|
||||
if (err.name === 'NotAllowedError') msg += 'Доступ к микрофону запрещён. Нажмите на замочек 🔒 в адресной строке → Микрофон → Разрешить.';
|
||||
else if (err.name === 'NotFoundError') msg += 'Микрофон не найден. Подключите микрофон.';
|
||||
else if (err.name === 'NotReadableError') msg += 'Микрофон занят другим приложением.';
|
||||
else msg += 'Ошибка: ' + (err.message || err.name);
|
||||
setStatus(msg);
|
||||
btn.textContent = '🎙 Записать';
|
||||
isRecording = false;
|
||||
}
|
||||
};
|
||||
|
||||
// ---------- Воспроизведение своей записи ----------
|
||||
document.getElementById('btnPlayUser').onclick = async () => {
|
||||
if (!userAudioBlob) return setStatus('Нет записи!');
|
||||
if (userAudioBlob.size === 0) return setStatus('Запись пустая!');
|
||||
const url = URL.createObjectURL(userAudioBlob);
|
||||
const audio = new Audio(url);
|
||||
audio.volume = 1.0;
|
||||
audio.onended = () => { URL.revokeObjectURL(url); setStatus('Готово.'); };
|
||||
audio.onerror = () => { URL.revokeObjectURL(url); setStatus('Ошибка воспроизведения.'); };
|
||||
setStatus('🔊 Ваша запись...');
|
||||
try {
|
||||
await audio.play();
|
||||
} catch (err) {
|
||||
URL.revokeObjectURL(url);
|
||||
setStatus('Ошибка воспроизведения — нажмите ещё раз.');
|
||||
return;
|
||||
}
|
||||
const original = document.getElementById('inputText').value.trim();
|
||||
if (original && userAudioBlob) {
|
||||
await doCompare(userAudioBlob, original, recordDuration);
|
||||
}
|
||||
};
|
||||
|
||||
// ---------- Кнопки ----------
|
||||
document.getElementById('btnTTS').onclick = async () => {
|
||||
ttsText = document.getElementById('inputText').value.trim();
|
||||
if (!ttsText) return setStatus('Введите текст!');
|
||||
saveToHistory(ttsText);
|
||||
translateToRussian(ttsText);
|
||||
setStatus('🔊 Эталон...');
|
||||
await playTTS(ttsText);
|
||||
setStatus('Готово.');
|
||||
};
|
||||
|
||||
document.getElementById('btnCompare').onclick = async () => {
|
||||
if (!userAudioBlob) return setStatus('Нет записи!');
|
||||
const original = document.getElementById('inputText').value.trim();
|
||||
await doCompare(userAudioBlob, original, recordDuration);
|
||||
};
|
||||
|
||||
document.getElementById('btnStereo').onclick = async () => {
|
||||
const text = document.getElementById('inputText').value.trim();
|
||||
if (!text) return setStatus('Введите текст для эталона!');
|
||||
if (!userAudioBlob) return setStatus('Нет записи! Нажмите 🎙 Записать.');
|
||||
await playStereo(text, userAudioBlob);
|
||||
const original = document.getElementById('inputText').value.trim();
|
||||
if (original && userAudioBlob) {
|
||||
await doCompare(userAudioBlob, original, recordDuration);
|
||||
}
|
||||
};
|
||||
|
||||
// ---------- История: compareRec / stereoRec ----------
|
||||
async function compareRec(id) {
|
||||
try {
|
||||
const db = await openRecDB();
|
||||
const tx = db.transaction('recs', 'readonly');
|
||||
const store = tx.objectStore('recs');
|
||||
const rec = await new Promise(r => { const req = store.get(id); req.onsuccess = () => r(req.result); });
|
||||
if (rec?.blob) {
|
||||
document.getElementById('inputText').value = rec.word;
|
||||
const tr = await doCompare(rec.blob, rec.word, rec.duration);
|
||||
if (tr) saveTranscription(id, tr);
|
||||
}
|
||||
} catch(e) { setStatus('Ошибка загрузки записи.'); }
|
||||
}
|
||||
|
||||
async function stereoRec(id) {
|
||||
try {
|
||||
const db = await openRecDB();
|
||||
const tx = db.transaction('recs', 'readonly');
|
||||
const store = tx.objectStore('recs');
|
||||
const rec = await new Promise(r => { const req = store.get(id); req.onsuccess = () => r(req.result); });
|
||||
if (rec?.blob) {
|
||||
document.getElementById('inputText').value = rec.word;
|
||||
await playStereo(rec.word, rec.blob);
|
||||
const tr = await doCompare(rec.blob, rec.word, rec.duration);
|
||||
if (tr) saveTranscription(id, tr);
|
||||
}
|
||||
} catch(e) { setStatus('Ошибка загрузки записи.'); }
|
||||
}
|
||||
Reference in New Issue
Block a user