diff --git a/CHANGELOG.md b/CHANGELOG.md index 7539d09..f807d34 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,11 @@ Follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) · versioned wi --- +## [1.20.46] — 2026-08-19 + +### Fixed +- **Completed synthesized voice previews now release their Blob URLs.** Repeated preview playback no longer retains every generated clip in browser memory. + ## [1.20.45] — 2026-08-19 ### Fixed diff --git a/VERSION b/VERSION index b741e05..0ef939e 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.20.45 +1.20.46 diff --git a/static/index.html b/static/index.html index 35a07b2..e9b5922 100644 --- a/static/index.html +++ b/static/index.html @@ -10,7 +10,7 @@ - + @@ -27,7 +27,7 @@ - + @@ -378,7 +378,7 @@ window.toggleNavTree = function(treeId, chevronId) { - + diff --git a/static/js/voice-library.js b/static/js/voice-library.js index f40ee85..73aee43 100644 --- a/static/js/voice-library.js +++ b/static/js/voice-library.js @@ -3252,7 +3252,17 @@ function makeVoiceRow(v) { $('lib-audio-label').textContent = v.id + ' · original recording'; } bar.style.display = ''; - audio.onended = () => { setLibraryPlayButtonState(playBtn, 'idle'); _activePlayVoiceId = null; }; + audio.onended = () => { + setLibraryPlayButtonState(playBtn, 'idle'); + _activePlayVoiceId = null; + // A synthesized preview is only needed while it is playing or + // paused for resume. Once it has ended, release the Blob URL so a + // sequence of previews cannot retain every generated clip in RAM. + if (_activePlayUrl) { + URL.revokeObjectURL(_activePlayUrl); + _activePlayUrl = null; + } + }; audio.onpause = () => { if (_activePlayButton === playBtn) setLibraryPlayButtonState(playBtn, 'idle'); }; audio.onplay = () => { setLibraryPlayButtonState(playBtn, 'playing'); }; await audio.play();