Update TTS providers and story markup
This commit is contained in:
@@ -45,6 +45,8 @@ class SentenceQueueModule extends BaseModule {
|
||||
'prepareSpeechMetadata',
|
||||
'preloadAssetsForItem',
|
||||
'normalizeTtsText',
|
||||
'getConfiguredTtsGenerationTimeoutMs',
|
||||
'normalizeTtsGenerationTimeoutMs',
|
||||
'runTtsPreloadWithTimeout',
|
||||
'cancelBlockingGeneration',
|
||||
'cancelGenerationRequests',
|
||||
@@ -89,19 +91,25 @@ class SentenceQueueModule extends BaseModule {
|
||||
const persistenceManager = this.getModule('persistence-manager');
|
||||
if (persistenceManager && typeof persistenceManager.getPreference === 'function') {
|
||||
this.autoplay = persistenceManager.getPreference('app', 'autoplay', true) !== false;
|
||||
this.ttsGenerationTimeoutMs = this.getConfiguredTtsGenerationTimeoutMs();
|
||||
}
|
||||
this.addEventListener(document, 'preference-updated', (event) => {
|
||||
const { category, key, value } = event.detail || {};
|
||||
if (category === 'app' && key === 'autoplay') {
|
||||
this.autoplay = value !== false;
|
||||
}
|
||||
if (category === 'tts' && (key === 'preferred_handler' || key.endsWith('_timeout_ms'))) {
|
||||
this.ttsGenerationTimeoutMs = this.getConfiguredTtsGenerationTimeoutMs();
|
||||
}
|
||||
});
|
||||
this.addEventListener(document, 'story:input-mode', (event) => {
|
||||
this.inputMode = ['text', 'choice', 'end'].includes(event.detail) ? event.detail : 'text';
|
||||
});
|
||||
this.addEventListener(document, 'ui:command', (event) => {
|
||||
if (event.detail?.type === 'continue') {
|
||||
this.lastContinueAt = performance.now();
|
||||
if (event.detail?.source !== 'display-clear') {
|
||||
this.lastContinueAt = performance.now();
|
||||
}
|
||||
this.cancelBlockingGeneration('user-fast-forward', {
|
||||
minWaitMs: USER_CANCEL_BLOCKING_WAIT_MIN_MS
|
||||
});
|
||||
@@ -305,11 +313,35 @@ class SentenceQueueModule extends BaseModule {
|
||||
.trim();
|
||||
}
|
||||
|
||||
getConfiguredTtsGenerationTimeoutMs() {
|
||||
const persistenceManager = this.getModule('persistence-manager');
|
||||
if (!persistenceManager || typeof persistenceManager.getPreference !== 'function') {
|
||||
return TTS_GENERATION_TIMEOUT_MS;
|
||||
}
|
||||
|
||||
const preferredHandler = persistenceManager.getPreference('tts', 'preferred_handler', 'none');
|
||||
const providerTimeout = preferredHandler && preferredHandler !== 'none'
|
||||
? persistenceManager.getPreference('tts', `${preferredHandler}_timeout_ms`)
|
||||
: undefined;
|
||||
const genericTimeout = persistenceManager.getPreference('tts', 'generation_timeout_ms');
|
||||
|
||||
return this.normalizeTtsGenerationTimeoutMs(providerTimeout ?? genericTimeout ?? TTS_GENERATION_TIMEOUT_MS);
|
||||
}
|
||||
|
||||
normalizeTtsGenerationTimeoutMs(value) {
|
||||
const timeout = Number(value);
|
||||
if (!Number.isFinite(timeout)) {
|
||||
return TTS_GENERATION_TIMEOUT_MS;
|
||||
}
|
||||
return Math.max(1000, Math.min(600000, Math.round(timeout)));
|
||||
}
|
||||
|
||||
runTtsPreloadWithTimeout(ttsFactory, text, context = {}) {
|
||||
const sentenceId = context.sentenceId || context.id || `tts-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
||||
const requestId = `${sentenceId}:${context.prefetch ? 'prefetch' : 'blocking'}:${Date.now()}`;
|
||||
const controller = new AbortController();
|
||||
const startedAt = performance.now();
|
||||
const timeoutMs = this.getConfiguredTtsGenerationTimeoutMs();
|
||||
|
||||
return new Promise((resolve) => {
|
||||
let settled = false;
|
||||
@@ -324,12 +356,12 @@ class SentenceQueueModule extends BaseModule {
|
||||
const timeoutId = setTimeout(() => {
|
||||
console.warn('SentenceQueue: TTS generation timed out; continuing without audio', {
|
||||
sentenceId,
|
||||
timeoutMs: this.ttsGenerationTimeoutMs,
|
||||
timeoutMs,
|
||||
textPreview: text.slice(0, 120)
|
||||
});
|
||||
controller.abort('tts-generation-timeout');
|
||||
finish({ success: false, reason: 'tts_generation_timeout', timedOut: true });
|
||||
}, this.ttsGenerationTimeoutMs);
|
||||
}, timeoutMs);
|
||||
|
||||
this.generationRequests.set(requestId, {
|
||||
controller,
|
||||
@@ -340,7 +372,10 @@ class SentenceQueueModule extends BaseModule {
|
||||
finish
|
||||
});
|
||||
|
||||
Promise.resolve(ttsFactory.preloadSpeech(text, { signal: controller.signal }))
|
||||
Promise.resolve(ttsFactory.preloadSpeech(text, {
|
||||
signal: controller.signal,
|
||||
ttsInstructions: Array.isArray(context.ttsInstructions) ? context.ttsInstructions : []
|
||||
}))
|
||||
.then(result => finish(result || { success: false, reason: 'empty_tts_result' }))
|
||||
.catch(error => {
|
||||
if (controller.signal.aborted) {
|
||||
@@ -426,7 +461,10 @@ class SentenceQueueModule extends BaseModule {
|
||||
let speedMultiplier = 1.0;
|
||||
const ttsFactory = this.getModule('tts-factory');
|
||||
if (ttsFactory) {
|
||||
speedMultiplier = Number.isFinite(ttsFactory.speed) ? Math.max(0.25, ttsFactory.speed) : 1.0;
|
||||
const configuredSpeed = Number(ttsFactory.speed);
|
||||
speedMultiplier = Number.isFinite(configuredSpeed)
|
||||
? Math.max(0.5, Math.min(2.0, configuredSpeed))
|
||||
: 1.0;
|
||||
}
|
||||
|
||||
// Calculate estimated duration in milliseconds
|
||||
@@ -486,6 +524,7 @@ class SentenceQueueModule extends BaseModule {
|
||||
sentenceId: id,
|
||||
blockId: metadata.blockId ?? null,
|
||||
turnId: metadata.turnId ?? null,
|
||||
ttsInstructions: Array.isArray(metadata.ttsInstructions) ? metadata.ttsInstructions : [],
|
||||
blocking: true
|
||||
});
|
||||
|
||||
@@ -501,6 +540,7 @@ class SentenceQueueModule extends BaseModule {
|
||||
paragraphIndex: metadata.paragraphIndex ?? null,
|
||||
layoutText: metadata.layoutText || text,
|
||||
glossaryEntries: Array.isArray(metadata.glossaryEntries) ? metadata.glossaryEntries : [],
|
||||
ttsInstructions: Array.isArray(metadata.ttsInstructions) ? metadata.ttsInstructions : [],
|
||||
isFirstParagraphInChapter: Boolean(metadata.isFirstParagraphInChapter),
|
||||
role: metadata.role || (metadata.type === 'heading' ? 'chapter-heading' : 'body'),
|
||||
dropCap: Boolean(metadata.dropCap),
|
||||
@@ -753,9 +793,6 @@ class SentenceQueueModule extends BaseModule {
|
||||
if (this.lastContinueAt >= (sentence.playbackStartedAt || 0)) {
|
||||
return false;
|
||||
}
|
||||
if (this.inputMode === 'choice') {
|
||||
return false;
|
||||
}
|
||||
return this.sentenceQueue.length > 1;
|
||||
}
|
||||
|
||||
@@ -848,6 +885,7 @@ class SentenceQueueModule extends BaseModule {
|
||||
sentenceId: nextItem.id,
|
||||
blockId: nextItem.blockId ?? null,
|
||||
turnId: nextItem.turnId ?? null,
|
||||
ttsInstructions: Array.isArray(nextItem.ttsInstructions) ? nextItem.ttsInstructions : [],
|
||||
queueIndex: index,
|
||||
prefetch: true,
|
||||
blocking: false
|
||||
|
||||
Reference in New Issue
Block a user