Completed options menu and got kokoro to load.

This commit is contained in:
2025-04-05 09:24:24 +00:00
parent c27ba8be6b
commit b50f60e195
13 changed files with 2170 additions and 1889 deletions
+163 -84
View File
@@ -15,21 +15,33 @@ class TTSFactoryModule extends BaseModule {
constructor() {
super('tts-factory', 'TTS Factory');
// Available TTS handlers
this.dependencies = ['persistence-manager', 'localization'];
this.handlers = {};
// Current active handler
this.initStatus = {};
this.activeHandler = null;
// Handler initialization status
this.initStatus = {
browser: false,
api: false,
kokoro: false
};
// TTS availability flag
this.ttsAvailable = false;
this.speed = 1; // Default speed
// Listen for kokoro:ready event
document.addEventListener('kokoro:ready', (event) => {
if (event.detail && typeof event.detail.success === 'boolean') {
console.log('TTS Factory: Received kokoro:ready event with success =', event.detail.success);
this.initStatus['kokoro'] = event.detail.success;
// If this is the current active handler or we don't have an active handler yet,
// try to activate Kokoro if it's now ready
if ((this.activeHandler === 'kokoro' || !this.activeHandler) && event.detail.success) {
// Only attempt to set active handler if TTS is enabled
const ttsEnabled = this.getPreference('tts', 'enabled', false);
if (ttsEnabled) {
this.setActiveHandler('kokoro');
}
}
// Update overall TTS availability
this.updateTTSAvailability();
}
});
// Bind methods
this.bindMethods([
@@ -45,11 +57,9 @@ class TTSFactoryModule extends BaseModule {
'resume',
'getVoices',
'getPreference',
'isSpeaking'
'isSpeaking',
'configure'
]);
// Add dependencies
this.dependencies = ['persistence-manager', 'localization'];
}
/**
@@ -75,19 +85,37 @@ class TTSFactoryModule extends BaseModule {
this.registerHandler('api', new ApiTTSHandler());
this.registerHandler('kokoro', new KokoroHandler());
console.log('TTS Factory: Registered handlers:', Object.keys(this.handlers));
this.reportProgress(30, "Registered TTS handlers");
// Force the initialization of all handlers for diagnostics
// This ensures they're all initialized even if not selected
const initPromises = [];
for (const id of Object.keys(this.handlers)) {
console.log(`TTS Factory: Initializing handler ${id}`);
initPromises.push(this.initializeHandler(id).then(success => {
console.log(`TTS Factory: Handler ${id} initialization ${success ? 'succeeded' : 'failed'}`);
return { id, success };
}));
}
// Wait for all handlers to initialize
const results = await Promise.all(initPromises);
console.log('TTS Factory: All handler initialization results:', results);
// Get user preferences
const ttsEnabled = this.getPreference('tts', 'enabled', false);
const preferredProvider = this.getPreference('tts', 'provider', 'browser');
console.log(`TTS Factory: User preferences - enabled: ${ttsEnabled}, provider: ${preferredProvider}`);
// Initialize handlers based on preferences
let initSuccess = false;
if (ttsEnabled) {
// Try to initialize preferred handler first
this.reportProgress(50, `Initializing preferred TTS handler: ${preferredProvider}`);
initSuccess = await this.initializeHandler(preferredProvider);
initSuccess = this.initStatus[preferredProvider] || false;
if (initSuccess) {
this.setActiveHandler(preferredProvider);
@@ -96,71 +124,44 @@ class TTSFactoryModule extends BaseModule {
console.warn(`Failed to initialize preferred TTS handler: ${preferredProvider}, trying alternatives`);
// Try Kokoro TTS as fallback if not already tried
if (preferredProvider !== 'kokoro') {
this.reportProgress(60, "Trying Kokoro TTS as fallback");
initSuccess = await this.initializeHandler('kokoro');
if (initSuccess) {
this.setActiveHandler('kokoro');
// Update preference to Kokoro since it worked
this.getModule('persistence-manager').updatePreference('tts', 'provider', 'kokoro');
}
if (preferredProvider !== 'kokoro' && this.initStatus.kokoro) {
this.reportProgress(60, "Using Kokoro TTS as fallback");
this.setActiveHandler('kokoro');
// Update preference to Kokoro since it worked
this.getModule('persistence-manager').updatePreference('tts', 'provider', 'kokoro');
initSuccess = true;
}
// If Kokoro TTS failed, try Browser TTS
if (!initSuccess && preferredProvider !== 'browser') {
this.reportProgress(70, "Trying Browser TTS as fallback");
initSuccess = await this.initializeHandler('browser');
if (initSuccess) {
this.setActiveHandler('browser');
// Update preference to browser since it worked
this.getModule('persistence-manager').updatePreference('tts', 'provider', 'browser');
}
// Try Browser TTS as fallback if not already tried
else if (preferredProvider !== 'browser' && this.initStatus.browser) {
this.reportProgress(70, "Using Browser TTS as fallback");
this.setActiveHandler('browser');
// Update preference to Browser since it worked
this.getModule('persistence-manager').updatePreference('tts', 'provider', 'browser');
initSuccess = true;
}
else {
// If all failed, disable TTS
this.reportProgress(80, "All TTS handlers failed, disabling TTS");
this.getModule('persistence-manager').updatePreference('tts', 'enabled', false);
this.getModule('persistence-manager').updatePreference('tts', 'provider', 'none');
}
// Note: API TTS is not used as a fallback as it requires manual configuration
}
} else {
// Even if TTS is disabled, initialize handlers in the background
// so they're ready if the user enables TTS later
this.reportProgress(50, "TTS disabled, initializing handlers in background");
// Initialize Kokoro and Browser handlers in parallel (not API as it requires configuration)
const initPromises = [
this.initializeHandler('kokoro'),
this.initializeHandler('browser')
];
// Wait for all handlers to initialize
await Promise.allSettled(initPromises);
// Check if any handler initialized successfully
initSuccess = this.initStatus.kokoro || this.initStatus.browser;
}
// Set TTS availability flag and dispatch event
this.ttsAvailable = initSuccess;
// Determine overall TTS availability
this.ttsAvailable = this.initStatus.kokoro || this.initStatus.browser;
// Dispatch event to notify UI about TTS availability
document.dispatchEvent(new CustomEvent('tts:availability', {
// Dispatch TTS availability event
window.dispatchEvent(new CustomEvent('tts:availability', {
detail: { available: this.ttsAvailable }
}));
this.reportProgress(100, initSuccess ? "TTS factory ready" : "TTS factory ready (no handlers available)");
// Always return true since TTS is optional for the application
return true;
this.reportProgress(100, "TTS factory initialized");
return true; // TTS is optional, so always return true
} catch (error) {
console.error("Error initializing TTS factory:", error);
console.error("TTS Factory: Error during initialization:", error);
this.reportProgress(100, "TTS factory failed");
// Set TTS availability to false and dispatch event
this.ttsAvailable = false;
document.dispatchEvent(new CustomEvent('tts:availability', {
detail: { available: false }
}));
// Still return true since TTS is optional
return true;
return true; // TTS is optional, so always return true
}
}
@@ -228,27 +229,43 @@ class TTSFactoryModule extends BaseModule {
* @returns {boolean} - Success status
*/
setActiveHandler(id) {
if (!id || !this.handlers[id] || !this.initStatus[id]) {
console.warn(`Cannot set active handler to ${id}: handler not found or not initialized`);
// Handle 'none' option specially
if (id === 'none') {
this.activeHandler = null;
// Update TTS availability state
this.ttsAvailable = false;
// Notify about TTS availability change
document.dispatchEvent(new CustomEvent('tts:availability', {
detail: { available: false }
}));
console.log("TTS Factory: TTS disabled (none selected)");
return true;
}
if (!this.handlers[id]) {
console.error(`TTS Factory: Handler not found: ${id}`);
return false;
}
// Stop current handler if active
if (this.activeHandler) {
this.handlers[this.activeHandler].stop();
if (!this.initStatus[id]) {
console.error(`TTS Factory: Handler not initialized: ${id}`);
return false;
}
// Set new active handler
this.activeHandler = id;
// Update preference
this.getModule('persistence-manager').updatePreference('tts', 'provider', id);
// Update TTS availability state
this.ttsAvailable = true;
// Dispatch event
this.dispatchEvent('tts-handler-changed', {
handler: id
});
// Notify about TTS availability change
document.dispatchEvent(new CustomEvent('tts:availability', {
detail: { available: true }
}));
console.log(`TTS Factory: Active handler set to ${id}`);
return true;
}
@@ -268,8 +285,16 @@ class TTSFactoryModule extends BaseModule {
getAvailableHandlers() {
const available = {};
// Debug logging for diagnostic purposes
console.log('TTS Factory: getAvailableHandlers called');
console.log('TTS Factory: Current initialization status:', this.initStatus);
console.log('TTS Factory: Registered handlers:', Object.keys(this.handlers).join(', '));
for (const id in this.handlers) {
available[id] = this.initStatus[id];
// Add the handler to the available list even if it's not initialized yet
// This ensures all registered handlers appear in the options
available[id] = true;
console.log(`TTS Factory: Including handler ${id} in options`);
}
return available;
@@ -387,6 +412,60 @@ class TTSFactoryModule extends BaseModule {
}
}
/**
* Update overall TTS availability
*/
updateTTSAvailability() {
this.ttsAvailable = this.initStatus.kokoro || this.initStatus.browser;
// Dispatch TTS availability event
window.dispatchEvent(new CustomEvent('tts:availability', {
detail: { available: this.ttsAvailable }
}));
}
/**
* Configure TTS settings for all handlers
* @param {Object} options - TTS options
* @param {number} [options.speed] - Normalized speech rate (0-1 range)
*/
configure(options = {}) {
// If speed is provided, convert the normalized speed (0-1) to the appropriate scale for each handler
if (typeof options.speed === 'number') {
const normalizedSpeed = Math.max(0, Math.min(1, options.speed));
// Scale for each handler type
for (const id in this.handlers) {
// Ensure the handler exists and has the setVoiceOptions method
if (this.handlers[id] && typeof this.handlers[id].setVoiceOptions === 'function') {
let scaledOptions = {};
// Scale the speed value appropriately for each handler type
if (id === 'browser') {
// Browser TTS uses rate from 0.1 to 2.0
scaledOptions.rate = 0.1 + (normalizedSpeed * 1.9);
} else if (id === 'kokoro') {
// Kokoro uses rate from 0.5 to 1.5
scaledOptions.rate = 0.5 + (normalizedSpeed);
} else if (id === 'api') {
// API uses speed from 0.5 to 2.0
scaledOptions.speed = 0.5 + (normalizedSpeed * 1.5);
}
// Apply the scaled options to the handler
this.handlers[id].setVoiceOptions(scaledOptions);
}
}
// Store the normalized value
this.speed = normalizedSpeed;
console.log(`TTS Factory: Speed set to ${normalizedSpeed} (normalized), ${Math.round(normalizedSpeed * 100)}/100`);
}
return true;
}
/**
* Clean up when module is disposed
*/