This commit is contained in:
parent
0fa042f494
commit
633aede41c
10
CHANGELOG.md
10
CHANGELOG.md
@ -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
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user