This commit is contained in:
parent
e00103dfb9
commit
0fa042f494
10
CHANGELOG.md
10
CHANGELOG.md
@ -9,6 +9,13 @@ Follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) · versioned wi
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## [1.47.1] — 2026-09-22
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- **Magazine: a progress bar for "Generate all audio."** The export status was a single line of text that jumped between "Article N/Total…" and a mid-render "sentence X/Y" — now a `<progress>` bar plus a plain "N / Total" count track the same numbers visually, parsed from the job's own progress text.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## [1.47.0] — 2026-09-22
|
## [1.47.0] — 2026-09-22
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
@ -4351,7 +4358,8 @@ Initial feature-complete release.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
[Unreleased]: https://github.com/mARTin-B78/tts-voice-creator-clone-and-design-2/compare/v1.47.0...HEAD
|
[Unreleased]: https://github.com/mARTin-B78/tts-voice-creator-clone-and-design-2/compare/v1.47.1...HEAD
|
||||||
|
[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.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
|
[1.46.0]: https://github.com/mARTin-B78/tts-voice-creator-clone-and-design-2/compare/v1.45.0...v1.46.0
|
||||||
[1.45.0]: https://github.com/mARTin-B78/tts-voice-creator-clone-and-design-2/compare/v1.44.0...v1.45.0
|
[1.45.0]: https://github.com/mARTin-B78/tts-voice-creator-clone-and-design-2/compare/v1.44.0...v1.45.0
|
||||||
|
|||||||
@ -10,7 +10,7 @@
|
|||||||
<meta name="format-detection" content="telephone=no">
|
<meta name="format-detection" content="telephone=no">
|
||||||
<meta name="color-scheme" content="light dark">
|
<meta name="color-scheme" content="light dark">
|
||||||
<meta name="theme-color" content="#2563EB">
|
<meta name="theme-color" content="#2563EB">
|
||||||
<meta name="app-version" content="1.47.0">
|
<meta name="app-version" content="1.47.1">
|
||||||
<link rel="manifest" href="/manifest.webmanifest">
|
<link rel="manifest" href="/manifest.webmanifest">
|
||||||
<link rel="icon" href="/static/icon.svg" type="image/svg+xml">
|
<link rel="icon" href="/static/icon.svg" type="image/svg+xml">
|
||||||
<link rel="apple-touch-icon" href="/static/icon.svg">
|
<link rel="apple-touch-icon" href="/static/icon.svg">
|
||||||
@ -27,12 +27,12 @@
|
|||||||
|
|
||||||
<!-- ── Core styles (local — no CDN dependency for first paint) ────────── -->
|
<!-- ── Core styles (local — no CDN dependency for first paint) ────────── -->
|
||||||
<link rel="stylesheet" href="/static/vendor/mdi/materialdesignicons.min.css">
|
<link rel="stylesheet" href="/static/vendor/mdi/materialdesignicons.min.css">
|
||||||
<link rel="stylesheet" href="/static/style.css?v=1.47.0">
|
<link rel="stylesheet" href="/static/style.css?v=1.47.1">
|
||||||
<link rel="stylesheet" href="/static/css/resource-diagnostics.css?v=1.47.0">
|
<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.0">
|
<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.0">
|
<link rel="stylesheet" href="/static/css/voice-design.css?v=1.47.1">
|
||||||
<link rel="stylesheet" href="/static/css/rehearser.css?v=1.47.0">
|
<link rel="stylesheet" href="/static/css/rehearser.css?v=1.47.1">
|
||||||
<link rel="stylesheet" href="/static/css/audiobook.css?v=1.47.0">
|
<link rel="stylesheet" href="/static/css/audiobook.css?v=1.47.1">
|
||||||
|
|
||||||
|
|
||||||
<!-- ── Flag icons — non-blocking (loaded async, icons appear after JS) ── -->
|
<!-- ── Flag icons — non-blocking (loaded async, icons appear after JS) ── -->
|
||||||
@ -403,7 +403,7 @@ window.toggleNavTree = function(treeId, chevronId) {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- loader.js: fetches sections → loads JS modules → removes skeleton -->
|
<!-- loader.js: fetches sections → loads JS modules → removes skeleton -->
|
||||||
<script src="/static/loader.js?v=1.47.0"></script>
|
<script src="/static/loader.js?v=1.47.1"></script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@ -822,7 +822,11 @@ async function magGenerateAllAudio() {
|
|||||||
if (!voice) return say('Pick a narrator voice for the export first', 'err');
|
if (!voice) return say('Pick a narrator voice for the export first', 'err');
|
||||||
const btn = $('mag-audio-export-btn');
|
const btn = $('mag-audio-export-btn');
|
||||||
const status = $('mag-audio-export-status');
|
const status = $('mag-audio-export-status');
|
||||||
|
const progressWrap = $('mag-audio-export-progress-wrap');
|
||||||
|
const progressBar = $('mag-audio-export-progress');
|
||||||
|
const progressCount = $('mag-audio-export-count');
|
||||||
if (btn) btn.disabled = true;
|
if (btn) btn.disabled = true;
|
||||||
|
if (progressWrap) progressWrap.hidden = false;
|
||||||
let jobId;
|
let jobId;
|
||||||
try {
|
try {
|
||||||
const r = await api('/api/magazine/' + state.doc.id + '/audio/batch', {
|
const r = await api('/api/magazine/' + state.doc.id + '/audio/batch', {
|
||||||
@ -832,6 +836,7 @@ async function magGenerateAllAudio() {
|
|||||||
jobId = r.job_id;
|
jobId = r.job_id;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (btn) btn.disabled = false;
|
if (btn) btn.disabled = false;
|
||||||
|
if (progressWrap) progressWrap.hidden = true;
|
||||||
return say('Could not start audio export: ' + e.message, 'err');
|
return say('Could not start audio export: ' + e.message, 'err');
|
||||||
}
|
}
|
||||||
for (;;) {
|
for (;;) {
|
||||||
@ -839,6 +844,15 @@ async function magGenerateAllAudio() {
|
|||||||
try { job = await api('/api/jobs/' + jobId); }
|
try { job = await api('/api/jobs/' + jobId); }
|
||||||
catch (e) { if (btn) btn.disabled = false; return say('Could not check audio export: ' + e.message, 'err'); }
|
catch (e) { if (btn) btn.disabled = false; return say('Could not check audio export: ' + e.message, 'err'); }
|
||||||
if (status) status.textContent = job.detail || 'Working…';
|
if (status) status.textContent = job.detail || 'Working…';
|
||||||
|
// The job's own detail text always leads with "N/Total" -- either
|
||||||
|
// "Article N/Total…" between articles, or "(N/Total) Title: sentence
|
||||||
|
// X/Y" while one is actively rendering -- so one simple match covers
|
||||||
|
// both without the backend needing a separate numeric progress field.
|
||||||
|
const m = /(\d+)\/(\d+)/.exec(job.detail || '');
|
||||||
|
if (m && progressBar && progressCount) {
|
||||||
|
progressBar.max = m[2]; progressBar.value = m[1];
|
||||||
|
progressCount.textContent = m[1] + ' / ' + m[2];
|
||||||
|
}
|
||||||
if (job.done) {
|
if (job.done) {
|
||||||
say(job.error ? 'Audio export failed: ' + job.error : (job.detail || 'Audio export done'), job.error ? 'err' : 'ok');
|
say(job.error ? 'Audio export failed: ' + job.error : (job.detail || 'Audio export done'), job.error ? 'err' : 'ok');
|
||||||
break;
|
break;
|
||||||
@ -847,6 +861,7 @@ async function magGenerateAllAudio() {
|
|||||||
}
|
}
|
||||||
if (btn) btn.disabled = false;
|
if (btn) btn.disabled = false;
|
||||||
if (status) status.textContent = '';
|
if (status) status.textContent = '';
|
||||||
|
if (progressWrap) progressWrap.hidden = true;
|
||||||
await magRefreshArticles();
|
await magRefreshArticles();
|
||||||
renderContents();
|
renderContents();
|
||||||
if (!$('mag-playlist')?.hidden) renderPlaylistTracks();
|
if (!$('mag-playlist')?.hidden) renderPlaylistTracks();
|
||||||
|
|||||||
@ -90,6 +90,10 @@
|
|||||||
<a class="btn-sm" id="mag-audio-zip-link" href="#" hidden download>
|
<a class="btn-sm" id="mag-audio-zip-link" href="#" hidden download>
|
||||||
<span class="mdi mdi-folder-zip-outline"></span> Download all (ZIP)
|
<span class="mdi mdi-folder-zip-outline"></span> Download all (ZIP)
|
||||||
</a>
|
</a>
|
||||||
|
<span class="mag-audio-export-progress-wrap" hidden id="mag-audio-export-progress-wrap">
|
||||||
|
<progress id="mag-audio-export-progress" max="1" value="0"></progress>
|
||||||
|
<span id="mag-audio-export-count" class="muted"></span>
|
||||||
|
</span>
|
||||||
<span id="mag-audio-export-status" class="muted" role="status" aria-live="polite"></span>
|
<span id="mag-audio-export-status" class="muted" role="status" aria-live="polite"></span>
|
||||||
</div>
|
</div>
|
||||||
<div id="mag-playlist" class="mag-playlist" hidden>
|
<div id="mag-playlist" class="mag-playlist" hidden>
|
||||||
|
|||||||
@ -4963,6 +4963,10 @@ input[list="lang-codes"] {
|
|||||||
}
|
}
|
||||||
.mag-audio-export select { padding: 6px 8px; border: 1px solid var(--border); border-radius: 6px;
|
.mag-audio-export select { padding: 6px 8px; border: 1px solid var(--border); border-radius: 6px;
|
||||||
background: var(--surface); color: var(--text); }
|
background: var(--surface); color: var(--text); }
|
||||||
|
.mag-audio-export-progress-wrap { display: flex; align-items: center; gap: 8px; flex: 1; min-width: 120px; }
|
||||||
|
.mag-audio-export-progress-wrap progress { flex: 1; min-width: 80px; max-width: 220px; height: 8px;
|
||||||
|
accent-color: var(--accent); }
|
||||||
|
#mag-audio-export-count { flex-shrink: 0; font-variant-numeric: tabular-nums; }
|
||||||
|
|
||||||
/* Clicking a title edits it in place -- a dashed underline on hover/focus is
|
/* Clicking a title edits it in place -- a dashed underline on hover/focus is
|
||||||
the only cue it's editable, since a permanent border on every card title
|
the only cue it's editable, since a permanent border on every card title
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user