diff --git a/data/aniworld.db-shm b/data/aniworld.db-shm new file mode 100644 index 0000000..fe9ac28 Binary files /dev/null and b/data/aniworld.db-shm differ diff --git a/data/aniworld.db-wal b/data/aniworld.db-wal new file mode 100644 index 0000000..e69de29 diff --git a/data/config.json b/data/config.json index 962f090..a044b48 100644 --- a/data/config.json +++ b/data/config.json @@ -17,7 +17,7 @@ "keep_days": 30 }, "other": { - "master_password_hash": "$pbkdf2-sha256$29000$aq3VOsfY21sLwfgfQwghJA$d33KHoETVV5.zpCfR.BqM.ICe.DwjDcfATrsrsZ/3yM", + "master_password_hash": "$pbkdf2-sha256$29000$LkUohZASQmgthdD6n9Nayw$6VmJzv/pYSdyW7..eU57P.YJpjK/6fXvXvef0L6PLDg", "anime_directory": "/mnt/server/serien/Serien/" }, "version": "1.0.0" diff --git a/src/server/web/static/js/shared/websocket-client.js b/src/server/web/static/js/shared/websocket-client.js index d68ae33..aecebfb 100644 --- a/src/server/web/static/js/shared/websocket-client.js +++ b/src/server/web/static/js/shared/websocket-client.js @@ -1,7 +1,8 @@ /** * AniWorld - WebSocket Client Module * - * WebSocket connection management and event handling. + * Native WebSocket connection management with Socket.IO-style interface. + * Uses FastAPI native WebSocket backend with room-based messaging. * * Dependencies: constants.js */ @@ -13,9 +14,24 @@ AniWorld.WebSocketClient = (function() { const WS_EVENTS = AniWorld.Constants.WS_EVENTS; - let socket = null; + let ws = null; let isConnected = false; let eventHandlers = {}; + let rooms = new Set(); + let messageQueue = []; + let reconnectAttempts = 0; + const maxReconnectAttempts = 5; + const reconnectDelay = 1000; + let autoReconnect = true; + + /** + * Get WebSocket URL based on current page URL + */ + function getWebSocketUrl() { + const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; + const host = window.location.host; + return protocol + '//' + host + '/ws/connect'; + } /** * Initialize WebSocket connection @@ -25,62 +41,181 @@ AniWorld.WebSocketClient = (function() { handlers = handlers || {}; eventHandlers = handlers; - // Check if Socket.IO is available - if (typeof io === 'undefined') { - console.error('Socket.IO not loaded'); - return; - } - - socket = io(); - - // Handle connection events - socket.on('connected', function(data) { - console.log('WebSocket connection confirmed', data); - }); - - socket.on('connect', function() { - isConnected = true; - console.log('Connected to server'); - - // Subscribe to rooms - if (socket.join) { - socket.join('scan'); - socket.join('downloads'); - socket.join('queue'); - } - - // Call custom connect handler if provided - if (eventHandlers.onConnect) { - eventHandlers.onConnect(); - } - }); - - socket.on('disconnect', function() { - isConnected = false; - console.log('Disconnected from server'); - - // Call custom disconnect handler if provided - if (eventHandlers.onDisconnect) { - eventHandlers.onDisconnect(); - } - }); - - // Set up event handlers for common events - setupDefaultHandlers(); + connect(); } /** - * Set up default event handlers + * Connect to WebSocket server */ - function setupDefaultHandlers() { - if (!socket) return; + function connect() { + if (ws && ws.readyState === WebSocket.OPEN) { + console.log('WebSocket already connected'); + return; + } - // Register any events that have handlers - Object.keys(eventHandlers).forEach(function(eventName) { - if (eventName !== 'onConnect' && eventName !== 'onDisconnect') { - socket.on(eventName, eventHandlers[eventName]); + try { + const url = getWebSocketUrl(); + console.log('Connecting to WebSocket:', url); + ws = new WebSocket(url); + + ws.onopen = function() { + console.log('WebSocket connected'); + isConnected = true; + reconnectAttempts = 0; + + // Emit connect event to handlers + emitToHandlers('connect'); + + // Join default rooms for this application + joinRoom('scan'); + joinRoom('downloads'); + joinRoom('queue'); + + // Rejoin any previously joined rooms + rejoinRooms(); + + // Process queued messages + processMessageQueue(); + + // Call custom connect handler if provided + if (eventHandlers.onConnect) { + eventHandlers.onConnect(); + } + }; + + ws.onmessage = function(event) { + handleMessage(event.data); + }; + + ws.onerror = function(error) { + console.error('WebSocket error:', error); + emitToHandlers('error', { error: 'WebSocket connection error' }); + }; + + ws.onclose = function(event) { + console.log('WebSocket disconnected', event.code, event.reason); + isConnected = false; + emitToHandlers('disconnect', { code: event.code, reason: event.reason }); + + // Call custom disconnect handler if provided + if (eventHandlers.onDisconnect) { + eventHandlers.onDisconnect(); + } + + // Attempt reconnection + if (autoReconnect && reconnectAttempts < maxReconnectAttempts) { + reconnectAttempts++; + var delay = reconnectDelay * reconnectAttempts; + console.log('Attempting reconnection in ' + delay + 'ms (attempt ' + reconnectAttempts + ')'); + setTimeout(connect, delay); + } + }; + } catch (error) { + console.error('Failed to create WebSocket connection:', error); + emitToHandlers('error', { error: 'Failed to connect' }); + } + } + + /** + * Handle incoming WebSocket message + * @param {string} data - Raw message data + */ + function handleMessage(data) { + try { + var message = JSON.parse(data); + var type = message.type; + var payload = message.data; + + console.log('WebSocket message: type=' + type, payload); + + // Emit event to registered handlers + if (type) { + emitToHandlers(type, payload || {}); } + } catch (error) { + console.error('Failed to parse WebSocket message:', error, data); + } + } + + /** + * Emit event to registered handlers (internal) + * @param {string} event - Event name + * @param {*} data - Event data + */ + function emitToHandlers(event, data) { + if (eventHandlers[event]) { + try { + if (data !== undefined) { + eventHandlers[event](data); + } else { + eventHandlers[event](); + } + } catch (error) { + console.error('Error in event handler for ' + event + ':', error); + } + } + } + + /** + * Send message to server + * @param {string} action - Action type + * @param {Object} data - Data payload + */ + function send(action, data) { + var message = JSON.stringify({ + action: action, + data: data || {} }); + + if (isConnected && ws && ws.readyState === WebSocket.OPEN) { + ws.send(message); + } else { + console.warn('WebSocket not connected, queueing message'); + messageQueue.push(message); + } + } + + /** + * Join a room (subscribe to topic) + * @param {string} room - Room name + */ + function joinRoom(room) { + rooms.add(room); + if (isConnected) { + send('join', { room: room }); + console.log('Joined room:', room); + } + } + + /** + * Leave a room (unsubscribe from topic) + * @param {string} room - Room name + */ + function leaveRoom(room) { + rooms.delete(room); + if (isConnected) { + send('leave', { room: room }); + console.log('Left room:', room); + } + } + + /** + * Rejoin all rooms after reconnection + */ + function rejoinRooms() { + rooms.forEach(function(room) { + send('join', { room: room }); + }); + } + + /** + * Process queued messages after connection + */ + function processMessageQueue() { + while (messageQueue.length > 0 && isConnected) { + var message = messageQueue.shift(); + ws.send(message); + } } /** @@ -89,14 +224,7 @@ AniWorld.WebSocketClient = (function() { * @param {Function} handler - The handler function */ function on(eventName, handler) { - if (!socket) { - console.warn('Socket not initialized'); - return; - } - eventHandlers[eventName] = handler; - socket.off(eventName); // Remove existing handler - socket.on(eventName, handler); } /** @@ -104,24 +232,20 @@ AniWorld.WebSocketClient = (function() { * @param {string} eventName - The event name */ function off(eventName) { - if (!socket) return; - delete eventHandlers[eventName]; - socket.off(eventName); } /** - * Emit an event to the server + * Emit an event to the server (Socket.IO compatibility) * @param {string} eventName - The event name * @param {*} data - The data to send */ function emit(eventName, data) { - if (!socket || !isConnected) { - console.warn('Socket not connected'); + if (!isConnected) { + console.warn('WebSocket not connected'); return; } - - socket.emit(eventName, data); + send(eventName, data); } /** @@ -133,20 +257,29 @@ AniWorld.WebSocketClient = (function() { } /** - * Get the socket instance - * @returns {Object} The Socket.IO socket instance + * Get the WebSocket instance (for compatibility) + * @returns {Object} The WebSocket instance wrapped with event methods */ function getSocket() { - return socket; + // Return a wrapper object that provides Socket.IO-like interface + return { + on: on, + off: off, + emit: emit, + join: joinRoom, + leave: leaveRoom, + connected: isConnected + }; } /** * Disconnect from server */ function disconnect() { - if (socket) { - socket.disconnect(); - socket = null; + autoReconnect = false; + if (ws) { + ws.close(1000, 'Client disconnected'); + ws = null; isConnected = false; } } @@ -157,6 +290,9 @@ AniWorld.WebSocketClient = (function() { on: on, off: off, emit: emit, + send: send, + join: joinRoom, + leave: leaveRoom, isConnected: getConnectionStatus, getSocket: getSocket, disconnect: disconnect diff --git a/src/server/web/templates/index.html b/src/server/web/templates/index.html index e6a4a13..2d1d655 100644 --- a/src/server/web/templates/index.html +++ b/src/server/web/templates/index.html @@ -440,9 +440,6 @@ - - - diff --git a/src/server/web/templates/queue.html b/src/server/web/templates/queue.html index 2562ea9..0d7f896 100644 --- a/src/server/web/templates/queue.html +++ b/src/server/web/templates/queue.html @@ -233,9 +233,6 @@ - - -