- Deterministic hash-based character portraits and reference sheets - Implemented strict early Renaissance aesthetic controls and role-specific headwear rules - Added audiobook batch export scripts and progress tooling - Verified book profiles with a new 'confirmed' state tracking mechanism - Updated Pillow dependency for robust image handling - Configured .gitignore for audiobook and audio cache exports
66 lines
3.6 KiB
JavaScript
66 lines
3.6 KiB
JavaScript
import { chromium } from '@playwright/test';
|
|
|
|
const base = process.env.TTSC_URL || 'http://localhost:7890';
|
|
const browser = await chromium.launch({ headless: true, executablePath: process.env.CHROMIUM_PATH || '/snap/bin/chromium' });
|
|
const page = await browser.newPage();
|
|
page.on('console', msg => { if (msg.type() === 'error') console.error('[browser]', msg.text()); });
|
|
await page.goto(base + '/#s-library', { waitUntil: 'domcontentloaded' });
|
|
await page.waitForFunction(() => typeof window.clGetAll === 'function' && typeof window._charAutoDesignVoice === 'function', null, { timeout: 30000 });
|
|
await page.evaluate(async () => {
|
|
if (typeof window.loadVoiceLibrary === 'function') await window.loadVoiceLibrary();
|
|
});
|
|
|
|
const targets = await page.evaluate(async () => {
|
|
const books = new Set([
|
|
'Hohlbein & Hennen - DSA - Das Jahr des Greifen 01 - Der Sturm',
|
|
'Hohlbein & Hennen - DSA - Das Jahr des Greifen 02 - Die Entdeckung',
|
|
'Hohlbein & Hennen - DSA - Das Jahr des Greifen 03 - Die Amazone',
|
|
]);
|
|
const chars = await window.clGetAll();
|
|
const voices = window._voices || [];
|
|
const ratings = new Map(voices.map(v => [v.id, Number(v.rating || 0)]));
|
|
return chars.filter(c => books.has(c.book) && c.voice && ratings.get(typeof c.voice === 'object' ? c.voice.id : c.voice) >= 1 && ratings.get(typeof c.voice === 'object' ? c.voice.id : c.voice) <= 2)
|
|
.map(c => {
|
|
const voiceId = typeof c.voice === 'object' ? c.voice.id : c.voice;
|
|
const voice = voices.find(v => v.id === voiceId) || {};
|
|
return { id: c.id, book: c.book, name: c.name, rating: ratings.get(voiceId), feedback: String(voice.note || '').trim() };
|
|
});
|
|
});
|
|
|
|
console.log(`Quality pass starting for ${targets.length} voices`);
|
|
let done = 0;
|
|
for (const target of targets) {
|
|
const result = await page.evaluate(async (target) => {
|
|
const all = await window.clGetAll();
|
|
const rec = all.find(c => c.id === target.id);
|
|
if (!rec) throw new Error('Character no longer exists');
|
|
const sheet = rec.sheet || {};
|
|
const sample = [sheet.physical, sheet.backstory, sheet.motivation].filter(Boolean).join(' ');
|
|
const language = typeof window.detectLang === 'function' && sample ? (window.detectLang(sample) || '') : '';
|
|
const llm = typeof window.statusLlmTarget === 'function' ? window.statusLlmTarget() : { url: '', model: '' };
|
|
const promptResponse = await fetch('/api/character-generate-prompts', {
|
|
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ name: rec.name, book: rec.book || '', sheet, voice_feedback: target.feedback || '', language, llm_url: llm.url, model: llm.model, fields: ['voice_design_prompt'] }),
|
|
});
|
|
if (!promptResponse.ok) throw new Error('Prompt generation: ' + promptResponse.status + ' ' + promptResponse.statusText);
|
|
const promptData = await promptResponse.json();
|
|
const prompt = String(promptData.voice_design_prompt || '').trim();
|
|
if (!prompt) throw new Error('Empty regenerated prompt');
|
|
rec.sheet = rec.sheet || {};
|
|
rec.sheet.voice_design_prompt = prompt;
|
|
rec.updated = new Date();
|
|
await window.clPut(rec);
|
|
await window._charAutoDesignVoice(rec, true, prompt);
|
|
return { name: rec.name, prompt };
|
|
}, target);
|
|
done += 1;
|
|
console.log(`[${done}/${targets.length}] ${target.rating}★ ${target.book} — ${result.name}`);
|
|
}
|
|
console.log('Quality pass complete');
|
|
await page.evaluate(async () => {
|
|
const r = await fetch('/api/tts/restart', { method: 'POST' });
|
|
if (!r.ok) throw new Error('TTS restart failed: ' + r.status);
|
|
});
|
|
console.log('TTS backend restarted; redesigned audio cache flushed');
|
|
await browser.close();
|