Fix JS caching root cause + robustness for sample text and STT dropdown

Root cause: VERSION was not volume-mounted, so the server always reported
1.2.0, causing browsers to serve 1-year-immutable cached JS even after
code changes.

Fixes:
- docker-compose.yml: add ./VERSION:/app/VERSION:ro volume mount.
  After `docker compose up -d`, the server reads the current VERSION file
  and JS is cache-busted by the correct version string.
- loader.js: append session timestamp to _appVersion so every page load
  generates a unique JS URL. JS is always fresh regardless of whether
  VERSION is current, at the cost of one network round-trip per file per
  session (acceptable for a local tool).
- s-clone.html: embed EN default text directly in the textarea so the
  field is never empty even before JS runs.
- voice-clone.js: remove the 'skip if already filled' guard in
  initCloneSampleText so navigating back always resets to the language
  text; call refreshSttBackends on load with sttReady event fallback.
- stt.js: dispatch 'sttReady' event after all STT listeners are wired.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
mARTin-B78 2026-05-31 18:23:22 +02:00
parent f45a1e6918
commit b3c1e4cc7f
5 changed files with 11 additions and 2 deletions

View File

@ -23,6 +23,7 @@ services:
- ./server.py:/app/server.py:ro - ./server.py:/app/server.py:ro
- ./core:/app/core:ro - ./core:/app/core:ro
- ./routes:/app/routes:ro - ./routes:/app/routes:ro
- ./VERSION:/app/VERSION:ro
environment: environment:
- PYTHONUNBUFFERED=1 - PYTHONUNBUFFERED=1

View File

@ -286,3 +286,5 @@ $('stt-tts-save-wav-btn')?.addEventListener('click', () => {
sttTtsDownload(sttTtsOutputBlob, ($('stt-tts-voice-select').value || 'stt_tts') + '_stt_tts.wav'); sttTtsDownload(sttTtsOutputBlob, ($('stt-tts-voice-select').value || 'stt_tts') + '_stt_tts.wav');
}); });
window.dispatchEvent(new Event('sttReady'));

View File

@ -15,7 +15,7 @@ window.initCloneSampleText = function () {
const sel = $('clone-sample-lang'); const sel = $('clone-sample-lang');
const txt = $('clone-sample-text'); const txt = $('clone-sample-text');
if (!sel || !txt) return; if (!sel || !txt) return;
if (!txt.value.trim()) txt.value = CLONE_SAMPLE_TEXTS[sel.value] || CLONE_SAMPLE_TEXTS.EN; txt.value = CLONE_SAMPLE_TEXTS[sel.value] || CLONE_SAMPLE_TEXTS.EN;
if (!sel._cloneSampleBound) { if (!sel._cloneSampleBound) {
sel.addEventListener('change', () => { sel.addEventListener('change', () => {
txt.value = CLONE_SAMPLE_TEXTS[sel.value] || CLONE_SAMPLE_TEXTS.EN; txt.value = CLONE_SAMPLE_TEXTS[sel.value] || CLONE_SAMPLE_TEXTS.EN;
@ -903,6 +903,9 @@ $('clone-refresh-stt-btn')?.addEventListener('click', async () => {
try { await refreshSttBackends($('clone-stt-backend')?.value); } try { await refreshSttBackends($('clone-stt-backend')?.value); }
finally { $('clone-refresh-stt-btn').disabled = false; } finally { $('clone-refresh-stt-btn').disabled = false; }
}); });
// Populate STT dropdown on load (refreshSttBackends is defined in stt.js which loads after us)
if (typeof refreshSttBackends === 'function') refreshSttBackends().catch(() => {});
else window.addEventListener('sttReady', () => refreshSttBackends().catch(() => {}));
$('transcribe-btn').addEventListener('click', async () => { $('transcribe-btn').addEventListener('click', async () => {
const id = trimmedFileId||designedFileId||currentFileId; const id = trimmedFileId||designedFileId||currentFileId;

View File

@ -21,6 +21,9 @@
} catch (_) {} } catch (_) {}
var _ts = Date.now(); // sections always fresh during a session var _ts = Date.now(); // sections always fresh during a session
// Append session timestamp so that a version bump + container restart
// always invalidates previously cached JS, even on the same version string.
_appVersion = _appVersion + '-' + _ts;
function _inject(id, html) { function _inject(id, html) {
var el = document.getElementById(id); var el = document.getElementById(id);

View File

@ -44,7 +44,7 @@
<option value="PL">Polski</option> <option value="PL">Polski</option>
</select> </select>
</div> </div>
<textarea class="sample-sentence" id="clone-sample-text" spellcheck="true"></textarea> <textarea class="sample-sentence" id="clone-sample-text" spellcheck="true">Hello! My name is Sam, and this is my voice. I can speak softly or with great strength. The crisp winter air, warm firelight, and the gentle sound of rain — these are the things I love. Can you hear how clearly I speak?</textarea>
</div> </div>
<div class="btn-row"> <div class="btn-row">
<button class="btn-secondary" id="clone-monitor-btn"><span class="mdi mdi-waveform"></span> Check level</button> <button class="btn-secondary" id="clone-monitor-btn"><span class="mdi mdi-waveform"></span> Check level</button>