Files
lang/js/pronounce.js

99 lines
4.0 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// ---------- Pronunciation Assessment ----------
async function assessPronunciation(expectedText, whisperResult) {
try {
const r = await fetch('/pronounce/assess', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
expected: expectedText,
whisper: whisperResult,
lang: 'it'
})
});
if (!r.ok) { console.log('[PRONOUNCE] HTTP', r.status); return null; }
const data = await r.json();
console.log('[PRONOUNCE] score=' + data.overall_score + ' ' + data.quality);
return data;
} catch(e) {
console.log('[PRONOUNCE] ERR', e);
return null;
}
}
function renderPronunciation(assess) {
if (!assess) return '';
const s = assess.overall_score;
const emoji = s >= 90 ? '🟢' : s >= 70 ? '🟡' : s >= 50 ? '🟠' : '🔴';
let html = '<div style="margin:10px 0;padding:12px;background:#1a1a2e;border-radius:8px;color:#e0e0e0">';
// Score bar
const barColor = s >= 90 ? '#4caf50' : s >= 70 ? '#ff9800' : s >= 50 ? '#f44336' : '#9e9e9e';
html += '<div style="display:flex;align-items:center;gap:12px;margin-bottom:10px">';
html += '<span style="font-size:2em">' + emoji + '</span>';
html += '<div style="flex:1">';
html += '<div style="font-size:1.4em;font-weight:bold">Произношение: <span style="color:' + barColor + '">' + s + '%</span></div>';
html += '<div style="color:#aaa;font-size:0.9em">' + (assess.quality || '') + '</div>';
// Bar
html += '<div style="height:6px;background:#333;border-radius:3px;margin-top:6px">';
html += '<div style="width:' + s + '%;height:100%;background:' + barColor + ';border-radius:3px;transition:width 0.5s"></div></div>';
html += '</div></div>';
// Details grid
html += '<div style="display:grid;grid-template-columns:1fr 1fr;gap:8px;font-size:0.85em">';
// Phonemes
if (assess.phoneme_comparison) {
const pc = assess.phoneme_comparison;
html += '<div><b>🔤 Фонемы:</b> ' + pc.accuracy + '%</div>';
html += '<div><b>✓ Совпадений:</b> ' + (pc.matches || 0) + '/' + (pc.total_phonemes || 0) + '</div>';
if (pc.errors && pc.errors.length > 0) {
const subs = pc.errors.filter(e => e.type === 'sub');
const dels = pc.errors.filter(e => e.type === 'del');
const inss = pc.errors.filter(e => e.type === 'ins');
html += '<div style="grid-column:1/-1">';
if (subs.length) html += '<span style="color:#ff9800">Замен: ' + subs.map(e => e.expected + '→' + e.actual).join(', ') + '</span> ';
if (dels.length) html += '<span style="color:#f44336">Пропущено: ' + dels.map(e => e.expected).join(', ') + '</span> ';
if (inss.length) html += '<span style="color:#9c27b0">Лишних: ' + inss.map(e => e.actual).join(', ') + '</span>';
html += '</div>';
}
}
// Timing
if (assess.timing) {
const tm = assess.timing;
html += '<div><b>⏱ Ритм:</b> ' + (tm.rhythm_score || 0) + '%</div>';
html += '<div><b>📏 Длит-ть:</b> ' + (tm.total_duration || 0).toFixed(1) + 'с</div>';
if (tm.timing_quality) {
const tq = tm.timing_quality;
html += '<div style="grid-column:1/-1;color:' + (tq === 'good' ? '#4caf50' : tq === 'ok' ? '#ff9800' : '#f44336') + '">';
html += tq === 'good' ? '✅ Ритм ровный' : tq === 'ok' ? '⚠️ Ритм неровный' : '❌ Ритм сбит';
html += '</div>';
}
}
html += '</div>';
// Feedback
if (assess.feedback) {
html += '<div style="margin-top:8px;padding:6px 10px;background:#2a2a3e;border-radius:4px;font-size:0.9em;color:#ccc">';
html += '💬 ' + assess.feedback;
html += '</div>';
}
// Phoneme detail
if (assess.expected && assess.expected.phonemes) {
html += '<div style="margin-top:6px;font-size:0.75em;color:#666">';
html += '🎯 Эталон: /' + assess.expected.phonemes.join(' ') + '/';
if (assess.actual && assess.actual.phonemes) {
html += ' | 🗣 Вы: /' + assess.actual.phonemes.join(' ') + '/';
}
html += '</div>';
}
html += '</div>';
return html;
}