Release v1.48.0
Some checks are pending
Test and SonarQube / quality (push) Waiting to run

This commit is contained in:
mARTin-B78 2026-09-22 23:00:00 +02:00
parent 0fa042f494
commit 633aede41c
5 changed files with 27 additions and 12 deletions

View File

@ -9,6 +9,13 @@ Follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) · versioned wi
---
## [1.48.0] — 2026-09-22
### Added
- **Magazine: "Generate all audio" resumes instead of redoing everything.** It now only renders approved articles that don't already have an MP3 — safe to click again after an aborted or partial run (a container restart mid-batch, closing the tab, etc.) without re-synthesising what already finished.
---
## [1.47.1] — 2026-09-22
### Added
@ -4358,7 +4365,8 @@ Initial feature-complete release.
---
[Unreleased]: https://github.com/mARTin-B78/tts-voice-creator-clone-and-design-2/compare/v1.47.1...HEAD
[Unreleased]: https://github.com/mARTin-B78/tts-voice-creator-clone-and-design-2/compare/v1.48.0...HEAD
[1.48.0]: https://github.com/mARTin-B78/tts-voice-creator-clone-and-design-2/compare/v1.47.1...v1.48.0
[1.47.1]: https://github.com/mARTin-B78/tts-voice-creator-clone-and-design-2/compare/v1.47.0...v1.47.1
[1.47.0]: https://github.com/mARTin-B78/tts-voice-creator-clone-and-design-2/compare/v1.46.0...v1.47.0
[1.46.0]: https://github.com/mARTin-B78/tts-voice-creator-clone-and-design-2/compare/v1.45.0...v1.46.0

View File

@ -1 +1 @@
1.47.1
1.48.0

View File

@ -10,7 +10,7 @@
<meta name="format-detection" content="telephone=no">
<meta name="color-scheme" content="light dark">
<meta name="theme-color" content="#2563EB">
<meta name="app-version" content="1.47.1">
<meta name="app-version" content="1.48.0">
<link rel="manifest" href="/manifest.webmanifest">
<link rel="icon" href="/static/icon.svg" type="image/svg+xml">
<link rel="apple-touch-icon" href="/static/icon.svg">
@ -27,12 +27,12 @@
<!-- ── Core styles (local — no CDN dependency for first paint) ────────── -->
<link rel="stylesheet" href="/static/vendor/mdi/materialdesignicons.min.css">
<link rel="stylesheet" href="/static/style.css?v=1.47.1">
<link rel="stylesheet" href="/static/css/resource-diagnostics.css?v=1.47.1">
<link rel="stylesheet" href="/static/css/background-jobs.css?v=1.47.1">
<link rel="stylesheet" href="/static/css/voice-design.css?v=1.47.1">
<link rel="stylesheet" href="/static/css/rehearser.css?v=1.47.1">
<link rel="stylesheet" href="/static/css/audiobook.css?v=1.47.1">
<link rel="stylesheet" href="/static/style.css?v=1.48.0">
<link rel="stylesheet" href="/static/css/resource-diagnostics.css?v=1.48.0">
<link rel="stylesheet" href="/static/css/background-jobs.css?v=1.48.0">
<link rel="stylesheet" href="/static/css/voice-design.css?v=1.48.0">
<link rel="stylesheet" href="/static/css/rehearser.css?v=1.48.0">
<link rel="stylesheet" href="/static/css/audiobook.css?v=1.48.0">
<!-- ── Flag icons — non-blocking (loaded async, icons appear after JS) ── -->
@ -403,7 +403,7 @@ window.toggleNavTree = function(treeId, chevronId) {
</script>
<!-- loader.js: fetches sections → loads JS modules → removes skeleton -->
<script src="/static/loader.js?v=1.47.1"></script>
<script src="/static/loader.js?v=1.48.0"></script>
</body>
</html>

View File

@ -820,6 +820,13 @@ function renderContentsBulkBar() {
async function magGenerateAllAudio() {
const voice = ($('mag-audio-voice')?.value || '').trim();
if (!voice) return say('Pick a narrator voice for the export first', 'err');
// Skip whatever already has audio -- resuming after an aborted or
// partial run (confirmed live: a magazine this size can genuinely take
// a while, and killing the job partway through, deliberately or via a
// container restart, is a real scenario, not an edge case) should not
// mean re-synthesising articles that are already done.
const missing = state.articles.filter((a) => a.status === 'approved' && !a.has_audio).map((a) => a.id);
if (!missing.length) return say('Every approved article already has audio', '');
const btn = $('mag-audio-export-btn');
const status = $('mag-audio-export-status');
const progressWrap = $('mag-audio-export-progress-wrap');
@ -831,7 +838,7 @@ async function magGenerateAllAudio() {
try {
const r = await api('/api/magazine/' + state.doc.id + '/audio/batch', {
method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ voice }),
body: JSON.stringify({ voice, article_ids: missing }),
});
jobId = r.job_id;
} catch (e) {

View File

@ -84,7 +84,7 @@
<label for="mag-audio-voice">Narrator for exported MP3s</label>
<select id="mag-audio-voice"></select>
<button type="button" class="btn-sm" id="mag-audio-export-btn" onclick="magGenerateAllAudio()"
title="Render every approved article to a single MP3 with its title, author, cover image and full text embedded as tags -- for a phone's own music/podcast player or a car head unit">
title="Render every approved article that doesn't already have an MP3 -- title, author, cover image and full text embedded as tags -- for a phone's own music/podcast player or a car head unit. Safe to click again after an aborted run: already-generated articles are skipped.">
<span class="mdi mdi-file-music-outline"></span> Generate all audio (MP3)
</button>
<a class="btn-sm" id="mag-audio-zip-link" href="#" hidden download>