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 @@
-
-
-