tts-voice-creator-clone-and.../static/js/audiobook-cast.js

57 lines
2.6 KiB
JavaScript

// ── Audiobook cast serialization ────────────────────────────────────────────
// Live casting remains in audiobook.js; these deterministic export/migration
// helpers have no casting-state side effects and can be loaded independently.
function _abCastMarkdown(data) {
const payload = data || {};
const segments = Array.isArray(payload.segments) ? payload.segments : [];
const speakers = [...new Set(segments.filter(s => s?.type === 'dialogue' && s.speaker).map(s => String(s.speaker)))].sort();
const lines = [`# ${payload.title || 'Cast Script'}`, ''];
const metaBits = [];
if (payload.savedAt) metaBits.push('exported ' + new Date(payload.savedAt).toISOString().slice(0, 16).replace('T', ' '));
metaBits.push(`${speakers.length} character${speakers.length !== 1 ? 's' : ''}`);
metaBits.push(`${segments.length} segment${segments.length !== 1 ? 's' : ''}`);
lines.push(`*${metaBits.join(' · ')}*`, '');
if (speakers.length) lines.push(`**Characters:** ${speakers.join(', ')}`, '');
let lastPage = null;
for (const seg of segments) {
const text = String(seg?.text || '').trim();
if (!text) continue;
if (seg.page != null && seg.page !== lastPage) {
lines.push('---', '', `## Page ${seg.page}`, '');
lastPage = seg.page;
}
if (seg.type === 'dialogue') {
const speaker = String(seg.speaker || 'Narrator');
const tag = seg.emotion ? ` *(${seg.emotion})*` : '';
lines.push(`**${speaker.toUpperCase()}**${tag}: "${text}"`);
} else lines.push(text);
lines.push('');
}
return lines.join('\n');
}
function _abStampSegmentPages(segs, pageMarks, text) {
if (!Array.isArray(segs) || !segs.length || segs.some(s => s && s.page != null)) return;
const marks = (pageMarks || []).slice().sort((a, b) => (a.offset || 0) - (b.offset || 0));
const src = text || '';
if (!marks.length || !src) return;
let markIdx = 0, searchPos = 0, cur = null;
if (marks[0].offset <= 2) { cur = marks[0].page + 1; markIdx = 1; }
for (const s of segs) {
const probe = String(s.text || '').trim().slice(0, 24);
const at = probe ? src.indexOf(probe, searchPos) : -1;
const pos = at >= 0 ? at : searchPos;
if (at >= 0) searchPos = at + probe.length;
while (markIdx < marks.length && pos >= marks[markIdx].offset) {
cur = marks[markIdx].page + 1;
markIdx++;
}
if (cur != null) s.page = cur;
}
}
window.TTVC = window.TTVC || {};
window.TTVC.audiobook = window.TTVC.audiobook || {};
window.TTVC.audiobook.cast = { toMarkdown: _abCastMarkdown, stampPages: _abStampSegmentPages };