From 939d33f7cc530507454eed4e7460bb890d0ac074 Mon Sep 17 00:00:00 2001 From: mARTin-B78 Date: Wed, 8 Jul 2026 21:26:23 +0200 Subject: [PATCH] Fix Apply effects button staying disabled after streaming preview (v1.14.4) Streaming playback never produces an in-memory blob, but the enable logic required one. Now enables unconditionally and lazily re-synthesizes a buffered blob on demand if effects are applied before one exists. Co-Authored-By: Claude Fable 5 --- CHANGELOG.md | 7 +++++++ VERSION | 2 +- static/index.html | 6 +++--- static/js/generation.js | 9 +++++++-- static/js/tts-preview.js | 13 ++++++++++--- 5 files changed, 28 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2109bf3..2135ab0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,13 @@ Follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) · versioned wi --- +## [1.14.4] — 2026-07-08 + +### Fixed +- **"Apply effects" button stayed disabled after generating a preview** — streaming playback (the default, lower-latency mode) never produces an in-memory audio blob, and the button's enable logic required one. It now enables regardless of playback mode; if effects are applied before a buffered blob exists, one is synthesized on demand from the same voice/text/instruct/backend used for the preview. + +--- + ## [1.14.3] — 2026-07-06 ### Added diff --git a/VERSION b/VERSION index 4ea8ad8..4e00d0a 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.14.3 +1.14.4 diff --git a/static/index.html b/static/index.html index 2d5a6e4..35ae873 100644 --- a/static/index.html +++ b/static/index.html @@ -10,7 +10,7 @@ - + @@ -27,7 +27,7 @@ - + @@ -365,7 +365,7 @@ window.toggleNavTree = function(treeId, chevronId) { - + diff --git a/static/js/generation.js b/static/js/generation.js index 82e6c54..8b3b4b8 100644 --- a/static/js/generation.js +++ b/static/js/generation.js @@ -339,8 +339,6 @@ $('effects-reset-btn')?.addEventListener('click', () => { let _effectsSourceBlob = null; $('effects-apply-btn')?.addEventListener('click', async () => { - const blob = previewBlob || _effectsSourceBlob; - if (!blob) { toast('Generate audio first', 'error'); return; } const chain = []; if ($('fx-reverb-on')?.checked) chain.push({ type:'reverb', params: { room_size: +$('fx-reverb-room').value, wet: +$('fx-reverb-wet').value, dry: 1 - +$('fx-reverb-wet').value } }); if ($('fx-compressor-on')?.checked) chain.push({ type:'compressor', params: { threshold_db: +$('fx-comp-thresh').value, ratio: +$('fx-comp-ratio').value } }); @@ -350,6 +348,13 @@ $('effects-apply-btn')?.addEventListener('click', async () => { const btn = $('effects-apply-btn'), st = $('effects-status'); btn.disabled = true; if (st) st.textContent = 'Processing…'; try { + let blob = previewBlob || _effectsSourceBlob; + if (!blob) { + const args = window._effectsSynthArgs; + if (!args || typeof fetchTtsPreviewBlob !== 'function') { toast('Generate audio first', 'error'); return; } + if (st) st.textContent = 'Synthesizing audio…'; + blob = await fetchTtsPreviewBlob(args.voice, args.text, 'wav', args.instruct, args.backend); + } const fd = new FormData(); fd.append('audio', blob, 'audio.wav'); fd.append('effects', JSON.stringify(chain)); diff --git a/static/js/tts-preview.js b/static/js/tts-preview.js index 4822dcd..6a2dff7 100644 --- a/static/js/tts-preview.js +++ b/static/js/tts-preview.js @@ -239,9 +239,16 @@ $('preview-text-area').addEventListener('focus', () => { if ($('preview-text-area').value === PREVIEW_SAMPLE_TEXT) $('preview-text-area').value = ''; }, { once:true }); -function _onPreviewGenerated(source, voice, text, backend) { +function _onPreviewGenerated(source, voice, text, backend, instruct) { if (typeof effectsSourceBlob !== 'undefined') window._effectsSourceBlob = null; - const ea = $('effects-apply-btn'); if (ea && source.blob) ea.disabled = false; + // Streaming playback (the default, lower-latency path) never produces a + // blob — audio plays via MSE without ever being fully buffered client-side + // — so gating "Apply effects" on source.blob left it permanently disabled + // whenever streaming succeeded, which is the common case. Enable it + // regardless; the effects handler re-fetches a buffered blob on demand + // (see window._effectsSynthArgs) if one isn't already sitting in memory. + window._effectsSynthArgs = { voice, text, instruct: instruct || '', backend }; + const ea = $('effects-apply-btn'); if (ea) ea.disabled = false; const ap = $('add-to-playlist-btn'); if (ap && source.blob) ap.disabled = false; if (typeof historyPush === 'function' && source.blob) historyPush(voice, text, backend, source.blob, source.url); } @@ -279,7 +286,7 @@ $('preview-btn').addEventListener('click', async () => { await audio.play(); $('save-preview-mp3-btn').disabled = false; $('save-preview-btn').disabled = source.streaming; - _onPreviewGenerated(source, voice, text, backend); + _onPreviewGenerated(source, voice, text, backend, instruct); toast(source.streaming ? 'Streaming preview playing' : source.label === 'chunked' ? `Chunked (${text.length} chars) playing` : 'Preview playing', 'success'); } catch(e) { toast('TTS failed: '+e.message,'error'); } finally { $('preview-btn').disabled=false; }