Stabilize TTS voice reload and reconnect logging
This commit is contained in:
+1
-19
@@ -245,6 +245,7 @@
|
|||||||
border: 1px none rgba(255,255,255,0);
|
border: 1px none rgba(255,255,255,0);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
<script src="/js/logger.js?v=20260519-logger"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<!-- Debug output area -->
|
<!-- Debug output area -->
|
||||||
@@ -279,25 +280,6 @@
|
|||||||
console.log(message);
|
console.log(message);
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
<script>
|
|
||||||
// Redefine console.log to expose browser logs to model
|
|
||||||
const originalLog = console.log;
|
|
||||||
console.log = function(...args) {
|
|
||||||
if (typeof debug !== 'undefined' && debug) {
|
|
||||||
const debugContent = document.getElementById('debug-content');
|
|
||||||
if (debugContent) {
|
|
||||||
const logMsg = document.createElement('div');
|
|
||||||
// Convert all arguments to string and join them
|
|
||||||
logMsg.textContent = args.map(arg =>
|
|
||||||
typeof arg === 'object' ? JSON.stringify(arg) : String(arg)
|
|
||||||
).join(' ');
|
|
||||||
debugContent.appendChild(logMsg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Pass all arguments to the original console.log
|
|
||||||
originalLog.apply(console, args);
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
<script type="module" src="/js/loader.js?v=20260516-scroll-window"></script>
|
<script type="module" src="/js/loader.js?v=20260516-scroll-window"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -0,0 +1,85 @@
|
|||||||
|
(function () {
|
||||||
|
const originalConsole = {
|
||||||
|
debug: console.debug.bind(console),
|
||||||
|
log: console.log.bind(console),
|
||||||
|
info: console.info.bind(console),
|
||||||
|
warn: console.warn.bind(console),
|
||||||
|
error: console.error.bind(console)
|
||||||
|
};
|
||||||
|
|
||||||
|
const levels = {
|
||||||
|
silent: 0,
|
||||||
|
error: 1,
|
||||||
|
warn: 2,
|
||||||
|
info: 3,
|
||||||
|
debug: 4
|
||||||
|
};
|
||||||
|
|
||||||
|
function readLevel() {
|
||||||
|
const params = new URLSearchParams(window.location.search);
|
||||||
|
const queryLevel = params.get('log') || params.get('debug');
|
||||||
|
if (queryLevel === '1' || queryLevel === 'true') return 'debug';
|
||||||
|
if (queryLevel && levels[queryLevel]) return queryLevel;
|
||||||
|
|
||||||
|
const savedLevel = localStorage.getItem('ai-if-log-level');
|
||||||
|
if (savedLevel && Object.prototype.hasOwnProperty.call(levels, savedLevel)) {
|
||||||
|
return savedLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'warn';
|
||||||
|
}
|
||||||
|
|
||||||
|
const logger = {
|
||||||
|
level: readLevel(),
|
||||||
|
levels,
|
||||||
|
originalConsole,
|
||||||
|
|
||||||
|
setLevel(level) {
|
||||||
|
if (!Object.prototype.hasOwnProperty.call(levels, level)) {
|
||||||
|
originalConsole.warn(`Unknown log level "${level}". Use silent, error, warn, info, or debug.`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.level = level;
|
||||||
|
localStorage.setItem('ai-if-log-level', level);
|
||||||
|
originalConsole.info(`AI IF log level set to ${level}`);
|
||||||
|
},
|
||||||
|
|
||||||
|
shouldPrint(level) {
|
||||||
|
return levels[this.level] >= levels[level];
|
||||||
|
},
|
||||||
|
|
||||||
|
write(level, args) {
|
||||||
|
if (!this.shouldPrint(level)) return;
|
||||||
|
const writer = originalConsole[level] || originalConsole.log;
|
||||||
|
writer(...args);
|
||||||
|
},
|
||||||
|
|
||||||
|
debug(...args) {
|
||||||
|
this.write('debug', args);
|
||||||
|
},
|
||||||
|
|
||||||
|
log(...args) {
|
||||||
|
this.write('debug', args);
|
||||||
|
},
|
||||||
|
|
||||||
|
info(...args) {
|
||||||
|
this.write('info', args);
|
||||||
|
},
|
||||||
|
|
||||||
|
warn(...args) {
|
||||||
|
this.write('warn', args);
|
||||||
|
},
|
||||||
|
|
||||||
|
error(...args) {
|
||||||
|
this.write('error', args);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
console.debug = (...args) => logger.debug(...args);
|
||||||
|
console.log = (...args) => logger.log(...args);
|
||||||
|
console.info = (...args) => logger.info(...args);
|
||||||
|
console.warn = (...args) => logger.warn(...args);
|
||||||
|
console.error = (...args) => logger.error(...args);
|
||||||
|
|
||||||
|
window.AppLogger = logger;
|
||||||
|
})();
|
||||||
@@ -19,9 +19,11 @@ class SocketClientModule extends BaseModule {
|
|||||||
this.storyHistory = null;
|
this.storyHistory = null;
|
||||||
this.isConnected = false;
|
this.isConnected = false;
|
||||||
this.reconnectAttempts = 0;
|
this.reconnectAttempts = 0;
|
||||||
this.maxReconnectAttempts = Infinity;
|
this.maxReconnectAttempts = 12;
|
||||||
this.reconnectDelay = 2000;
|
this.reconnectDelay = 5000;
|
||||||
this.maxReconnectDelay = 30000;
|
this.maxReconnectDelay = 5000;
|
||||||
|
this.reconnectTimer = null;
|
||||||
|
this.reconnectAlerted = false;
|
||||||
this.url = null;
|
this.url = null;
|
||||||
this.eventListeners = {};
|
this.eventListeners = {};
|
||||||
this.defaultHost = 'localhost:3000';
|
this.defaultHost = 'localhost:3000';
|
||||||
@@ -77,6 +79,8 @@ class SocketClientModule extends BaseModule {
|
|||||||
'resolveAssetUrl',
|
'resolveAssetUrl',
|
||||||
'looksLikeAssetPath',
|
'looksLikeAssetPath',
|
||||||
'attemptReconnect',
|
'attemptReconnect',
|
||||||
|
'stopReconnectLoop',
|
||||||
|
'notifyReconnectFailed',
|
||||||
'getConnectionStatus',
|
'getConnectionStatus',
|
||||||
'loadSocketIO'
|
'loadSocketIO'
|
||||||
]);
|
]);
|
||||||
@@ -152,6 +156,12 @@ class SocketClientModule extends BaseModule {
|
|||||||
try {
|
try {
|
||||||
console.log(`Socket Client: Connecting to ${socketUrl}`);
|
console.log(`Socket Client: Connecting to ${socketUrl}`);
|
||||||
|
|
||||||
|
if (this.socket) {
|
||||||
|
this.socket.removeAllListeners();
|
||||||
|
this.socket.close();
|
||||||
|
this.socket = null;
|
||||||
|
}
|
||||||
|
|
||||||
// Create Socket.IO connection (will automatically use /socket.io endpoint)
|
// Create Socket.IO connection (will automatically use /socket.io endpoint)
|
||||||
this.socket = window.io(socketUrl, {
|
this.socket = window.io(socketUrl, {
|
||||||
reconnection: false, // We handle reconnection ourselves
|
reconnection: false, // We handle reconnection ourselves
|
||||||
@@ -162,6 +172,8 @@ class SocketClientModule extends BaseModule {
|
|||||||
console.log('Socket Client: Connected to server with ID:', this.socket.id);
|
console.log('Socket Client: Connected to server with ID:', this.socket.id);
|
||||||
this.isConnected = true;
|
this.isConnected = true;
|
||||||
this.reconnectAttempts = 0;
|
this.reconnectAttempts = 0;
|
||||||
|
this.stopReconnectLoop();
|
||||||
|
this.reconnectAlerted = false;
|
||||||
this.emitEvent('connect');
|
this.emitEvent('connect');
|
||||||
resolve(true);
|
resolve(true);
|
||||||
});
|
});
|
||||||
@@ -177,6 +189,9 @@ class SocketClientModule extends BaseModule {
|
|||||||
this.socket.on('connect_error', (error) => {
|
this.socket.on('connect_error', (error) => {
|
||||||
console.error('Socket Client: Connection error:', error);
|
console.error('Socket Client: Connection error:', error);
|
||||||
this.emitEvent('connect_error', error);
|
this.emitEvent('connect_error', error);
|
||||||
|
if (!this.isConnected) {
|
||||||
|
this.attemptReconnect();
|
||||||
|
}
|
||||||
resolve(false);
|
resolve(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -659,7 +674,11 @@ class SocketClientModule extends BaseModule {
|
|||||||
*/
|
*/
|
||||||
attemptReconnect() {
|
attemptReconnect() {
|
||||||
if (this.reconnectAttempts >= this.maxReconnectAttempts) {
|
if (this.reconnectAttempts >= this.maxReconnectAttempts) {
|
||||||
console.error('Socket Client: Max reconnect attempts reached');
|
this.notifyReconnectFailed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.reconnectTimer) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -667,13 +686,45 @@ class SocketClientModule extends BaseModule {
|
|||||||
const delay = Math.min(this.maxReconnectDelay, this.reconnectDelay * this.reconnectAttempts);
|
const delay = Math.min(this.maxReconnectDelay, this.reconnectDelay * this.reconnectAttempts);
|
||||||
|
|
||||||
console.log(`Socket Client: Attempting to reconnect in ${delay}ms (attempt ${this.reconnectAttempts})`);
|
console.log(`Socket Client: Attempting to reconnect in ${delay}ms (attempt ${this.reconnectAttempts})`);
|
||||||
|
document.dispatchEvent(new CustomEvent('story:process-state', {
|
||||||
|
detail: { state: 'waiting-generating', reason: 'socket-reconnecting', attempt: this.reconnectAttempts }
|
||||||
|
}));
|
||||||
|
|
||||||
setTimeout(() => {
|
this.reconnectTimer = setTimeout(() => {
|
||||||
|
this.reconnectTimer = null;
|
||||||
if (!this.isConnected) {
|
if (!this.isConnected) {
|
||||||
this.connect();
|
this.connect();
|
||||||
}
|
}
|
||||||
}, delay);
|
}, delay);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stopReconnectLoop() {
|
||||||
|
if (this.reconnectTimer) {
|
||||||
|
clearTimeout(this.reconnectTimer);
|
||||||
|
this.reconnectTimer = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
notifyReconnectFailed() {
|
||||||
|
if (this.reconnectAlerted) return;
|
||||||
|
this.reconnectAlerted = true;
|
||||||
|
this.stopReconnectLoop();
|
||||||
|
const message = this.translate(
|
||||||
|
'popup.serverUnavailable',
|
||||||
|
'The game server is currently unavailable. The client tried to reconnect for one minute. Please reload the page after the server is running again.'
|
||||||
|
);
|
||||||
|
console.error('Socket Client: Reconnect failed after one minute');
|
||||||
|
document.dispatchEvent(new CustomEvent('story:tag', {
|
||||||
|
detail: {
|
||||||
|
key: 'error',
|
||||||
|
value: message,
|
||||||
|
source: 'socket-reconnect'
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
document.dispatchEvent(new CustomEvent('story:process-state', {
|
||||||
|
detail: { state: 'ready', reason: 'socket-reconnect-failed' }
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disconnect from the server
|
* Disconnect from the server
|
||||||
|
|||||||
@@ -1199,7 +1199,8 @@ class TTSFactoryModule extends BaseModule {
|
|||||||
} else if (handler && typeof handler.configure === 'function') {
|
} else if (handler && typeof handler.configure === 'function') {
|
||||||
handler.configure(voiceOptions);
|
handler.configure(voiceOptions);
|
||||||
}
|
}
|
||||||
if (voiceOptions.language && !voiceOptions.voice && handler && typeof handler.selectVoiceForLocale === 'function') {
|
const handlerHasVoice = Boolean(this.getEffectiveVoiceId(handler));
|
||||||
|
if (voiceOptions.language && !voiceOptions.voice && !handlerHasVoice && handler && typeof handler.selectVoiceForLocale === 'function') {
|
||||||
handler.selectVoiceForLocale(voiceOptions.language);
|
handler.selectVoiceForLocale(voiceOptions.language);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,5 +64,6 @@
|
|||||||
"popup.defaultError": "Das Spiel wurde wegen eines nicht behebbaren Fehlers beendet.",
|
"popup.defaultError": "Das Spiel wurde wegen eines nicht behebbaren Fehlers beendet.",
|
||||||
"popup.defaultAchievement": "Errungenschaft freigeschaltet.",
|
"popup.defaultAchievement": "Errungenschaft freigeschaltet.",
|
||||||
"popup.defaultAlert": "Hinweis",
|
"popup.defaultAlert": "Hinweis",
|
||||||
"popup.commandTimeout": "Der Spielserver hat nicht rechtzeitig geantwortet. Du kannst es noch einmal versuchen."
|
"popup.commandTimeout": "Der Spielserver hat nicht rechtzeitig geantwortet. Du kannst es noch einmal versuchen.",
|
||||||
|
"popup.serverUnavailable": "Der Spielserver ist im Moment nicht erreichbar. Der Client hat eine Minute lang versucht, die Verbindung wiederherzustellen. Bitte lade die Seite neu, sobald der Server wieder läuft."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,5 +64,6 @@
|
|||||||
"popup.defaultError": "The game ended because of an unrecoverable error.",
|
"popup.defaultError": "The game ended because of an unrecoverable error.",
|
||||||
"popup.defaultAchievement": "Achievement unlocked.",
|
"popup.defaultAchievement": "Achievement unlocked.",
|
||||||
"popup.defaultAlert": "Hint",
|
"popup.defaultAlert": "Hint",
|
||||||
"popup.commandTimeout": "The game server did not answer in time. You can try again."
|
"popup.commandTimeout": "The game server did not answer in time. You can try again.",
|
||||||
|
"popup.serverUnavailable": "The game server is currently unavailable. The client tried to reconnect for one minute. Please reload the page after the server is running again."
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user