From de4f22f7048e46d2b4433ca02b9f399bd4ad8d40 Mon Sep 17 00:00:00 2001 From: misyaguziya <53165965+misyaguziya@users.noreply.github.com> Date: Tue, 20 May 2025 14:28:55 +0900 Subject: [PATCH 1/3] [Update] Controller: Update WebSocket error messages to clarify host and port availability --- src-python/controller.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src-python/controller.py b/src-python/controller.py index c2db0f45..ccf44227 100644 --- a/src-python/controller.py +++ b/src-python/controller.py @@ -1844,7 +1844,7 @@ class Controller: response = { "status":400, "result":{ - "message":"WebSocket server port is not available", + "message":"WebSocket server host is not available", "data": config.WEBSOCKET_HOST } } @@ -1890,7 +1890,7 @@ class Controller: response = { "status":400, "result":{ - "message":"WebSocket server port is not available", + "message":"WebSocket server host or port is not available", "data": config.WEBSOCKET_SERVER } } @@ -2008,7 +2008,7 @@ class Controller: else: config.WEBSOCKET_SERVER = False model.stopWebSocketServer() - printLog("WebSocket server port is not available") + printLog("WebSocket server host or port is not available") printLog("Update settings") self.updateConfigSettings() From ed27a8c7bac598f7bbecdd2fbad3f0b72bdd0017 Mon Sep 17 00:00:00 2001 From: Sakamoto Shiina <68018796+ShiinaSakamoto@users.noreply.github.com> Date: Mon, 26 May 2025 12:06:36 +0900 Subject: [PATCH 2/3] [Update/Refactor] UI: Add websocket error handlings. Refactor. Change the error handling switch method from message to endpoint primarily. --- src-ui/logics/_useBackendErrorHandling.js | 207 +++++++++++------- .../configs/translation/useDeepLAuthKey.js | 6 - src-ui/logics/useReceiveRoutes.js | 41 +--- 3 files changed, 129 insertions(+), 125 deletions(-) diff --git a/src-ui/logics/_useBackendErrorHandling.js b/src-ui/logics/_useBackendErrorHandling.js index c9d99e50..d5987a1b 100644 --- a/src-ui/logics/_useBackendErrorHandling.js +++ b/src-ui/logics/_useBackendErrorHandling.js @@ -16,6 +16,7 @@ import { useDeepLAuthKey, useOscIpAddress, + useWebsocket, } from "@logics_configs"; import { ui_configs } from "../ui_configs"; @@ -31,106 +32,144 @@ export const _useBackendErrorHandling = () => { const { updateSpeakerPhraseTimeout } = useSpeakerPhraseTimeout(); const { updateSpeakerMaxWords } = useSpeakerMaxWords(); - const { updateDeepLAuthKey, saveErrorDeepLAuthKey } = useDeepLAuthKey(); + const { updateDeepLAuthKey } = useDeepLAuthKey(); const { updateOscIpAddress } = useOscIpAddress(); + const { updateEnableWebsocket, updateWebsocketHost, updateWebsocketPort } = useWebsocket(); - const errorHandling_Backend = ({message, data, endpoint, _result}) => { - switch (message) { - case "No mic device detected": - showNotification_Error(t("common_error.no_device_mic")); - break; - case "No speaker device detected": - showNotification_Error(t("common_error.no_device_speaker")); - break; + const errorHandling_Backend = ({message, data, endpoint, result}) => { + switch (endpoint) { + case "/run/error_device": + if (message === "No mic device detected") showNotification_Error(t("common_error.no_device_mic")); + if (message === "No speaker device detected") showNotification_Error(t("common_error.no_device_speaker")); + return; - case "Mic energy threshold value is out of range": - showNotification_Error(t("common_error.threshold_invalid_value", - { min: ui_configs.mic_threshold_min, max: ui_configs.mic_threshold_max }, - )); - break; - case "Speaker energy threshold value is out of range": - showNotification_Error(t("common_error.threshold_invalid_value", - { min: ui_configs.speaker_threshold_min, max: ui_configs.speaker_threshold_max }, - )); - break; + case "/set/data/mic_threshold": + if (message === "Mic energy threshold value is out of range") { + showNotification_Error(t("common_error.threshold_invalid_value", + { min: ui_configs.mic_threshold_min, max: ui_configs.mic_threshold_max }, + )); + }; + return; + case "/set/data/speaker_threshold": + if (message === "Speaker energy threshold value is out of range") { + showNotification_Error(t("common_error.threshold_invalid_value", + { min: ui_configs.speaker_threshold_min, max: ui_configs.speaker_threshold_max }, + )); + } + return; - case "CTranslate2 weight download error": - showNotification_Error(t("common_error.failed_download_weight_ctranslate2")); - break; - case "Whisper weight download error": - showNotification_Error(t("common_error.failed_download_weight_whisper")); - break; + case "/run/error_ctranslate2_weight": + if (message === "CTranslate2 weight download error") showNotification_Error(t("common_error.failed_download_weight_ctranslate2")); + return; + case "/run/error_whisper_weight": + if (message === "Whisper weight download error") showNotification_Error(t("common_error.failed_download_weight_whisper")); + return; - case "Translation engine limit error": - showNotification_Error(t("common_error.translation_limit")); - break; + case "/run/error_translation_engine": + if (message === "Translation engine limit error") showNotification_Error(t("common_error.translation_limit")); + return; - case "DeepL auth key length is not correct": - updateDeepLAuthKey(data); - showNotification_Error(t("common_error.deepl_auth_key_invalid_length")); - break; - case "Authentication failure of deepL auth key": - updateDeepLAuthKey(data); - showNotification_Error(t("common_error.deepl_auth_key_failed_authentication")); - break; + case "/set/data/deepl_auth_key": + if (message === "DeepL auth key length is not correct") { + updateDeepLAuthKey(data); + showNotification_Error(t("common_error.deepl_auth_key_invalid_length")); + } else if (message === "Authentication failure of deepL auth key") { + updateDeepLAuthKey(data); + showNotification_Error(t("common_error.deepl_auth_key_failed_authentication")); + } else { // Exception + updateDeepLAuthKey(data); + showNotification_Error(message); + } + return; - case "Mic record timeout value is out of range": - updateMicRecordTimeout(data); - showNotification_Error( - t("common_error.invalid_value_mic_record_timeout", - { mic_phrase_timeout_label: t("config_page.transcription.mic_phrase_timeout.label") } - )); - break; - case "Mic phrase timeout value is out of range": - updateMicPhraseTimeout(data); - showNotification_Error( - t("common_error.invalid_value_mic_phrase_timeout", - { mic_record_timeout_label: t("config_page.transcription.mic_record_timeout.label") } - )); - break; - case "Mic max phrases value is out of range": - updateMicMaxWords(data); - showNotification_Error(t("common_error.invalid_value_mic_max_phrase")); - break; + case "/set/data/mic_record_timeout": + if (message === "Mic record timeout value is out of range") { + updateMicRecordTimeout(data); + showNotification_Error(t("common_error.invalid_value_mic_record_timeout", { + mic_phrase_timeout_label: t("config_page.transcription.mic_phrase_timeout.label") + })); + } + return; + case "/set/data/mic_phrase_timeout": + if (message === "Mic phrase timeout value is out of range") { + updateMicPhraseTimeout(data); + showNotification_Error(t("common_error.invalid_value_mic_phrase_timeout", { + mic_record_timeout_label: t("config_page.transcription.mic_record_timeout.label") + })); + } + return; + case "/set/data/mic_max_phrases": + if (message === "Mic max phrases value is out of range") { + updateMicMaxWords(data); + showNotification_Error(t("common_error.invalid_value_mic_max_phrase")); + } + return; - case "Speaker record timeout value is out of range": - updateSpeakerRecordTimeout(data); - showNotification_Error( - t("common_error.invalid_value_speaker_record_timeout", - { speaker_phrase_timeout_label: t("config_page.transcription.speaker_phrase_timeout.label") } - )); - break; - case "Speaker phrase timeout value is out of range": - updateSpeakerPhraseTimeout(data); - showNotification_Error( - t("common_error.invalid_value_speaker_phrase_timeout", - { speaker_record_timeout_label: t("config_page.transcription.speaker_record_timeout.label") } - )); - break; - case "Speaker max phrases value is out of range": - updateSpeakerMaxWords(data); - showNotification_Error(t("common_error.invalid_value_speaker_max_phrase")); - break; + case "/set/data/speaker_record_timeout": + if (message === "Speaker record timeout value is out of range") { + updateSpeakerRecordTimeout(data); + showNotification_Error(t("common_error.invalid_value_speaker_record_timeout", { + speaker_phrase_timeout_label: t("config_page.transcription.speaker_phrase_timeout.label") + })); + } + return; + case "/set/data/speaker_phrase_timeout": + if (message === "Speaker phrase timeout value is out of range") { + updateSpeakerPhraseTimeout(data); + showNotification_Error(t("common_error.invalid_value_speaker_phrase_timeout", { + speaker_record_timeout_label: t("config_page.transcription.speaker_record_timeout.label") + })); + } + return; + case "/set/data/speaker_max_phrases": + if (message === "Speaker max phrases value is out of range") { + updateSpeakerMaxWords(data); + showNotification_Error(t("common_error.invalid_value_speaker_max_phrase")); + } + return; // Advanced Settings, error messages are set by Backend (EN only) - case "Invalid IP address": - updateOscIpAddress(data); - showNotification_Error(message); - break; + case "/set/data/osc_ip_address": + if (message === "Invalid IP address") { + updateOscIpAddress(data); + showNotification_Error(message); + } else if (message === "Cannot set IP address") { + updateOscIpAddress(data); + showNotification_Error(message); + } // else? (Backend will send the message "Cannot set IP address" when throw Exception) + return; - case "Cannot set IP address": - updateOscIpAddress(data); - showNotification_Error(message); - break; + + case "/set/enable/websocket_server": + if (message === "WebSocket server host or port is not available") { + updateEnableWebsocket(data); + showNotification_Error(message); + } + return; + + case "/set/data/websocket_host": + if (message === "Invalid IP address") { + updateWebsocketHost(data); + showNotification_Error(message); + } else if (message === "WebSocket server host is not available") { + updateWebsocketHost(data); + showNotification_Error(message); + } + return; + + case "/set/data/websocket_port": + if (message === "WebSocket server port is not available") { + updateWebsocketPort(data); + showNotification_Error(message); + } + return; default: - // determine by endpoint, not message. - if (endpoint === "/set/data/deepl_auth_key") saveErrorDeepLAuthKey({message, data, endpoint, _result}); - - break; + console.error(`Invalid endpoint or message: ${endpoint}\nmessage: ${message}\nresult: ${JSON.stringify(result)}`); + return; } } diff --git a/src-ui/logics/configs/translation/useDeepLAuthKey.js b/src-ui/logics/configs/translation/useDeepLAuthKey.js index f93fdd4f..a568dfe9 100644 --- a/src-ui/logics/configs/translation/useDeepLAuthKey.js +++ b/src-ui/logics/configs/translation/useDeepLAuthKey.js @@ -29,11 +29,6 @@ export const useDeepLAuthKey = () => { showNotification_Success(t("config_page.translation.deepl_auth_key.auth_key_success")); }; - const saveErrorDeepLAuthKey = ({data, message}) => { - updateDeepLAuthKey(data); - showNotification_Error(message); - }; - return { currentDeepLAuthKey, getDeepLAuthKey, @@ -41,7 +36,6 @@ export const useDeepLAuthKey = () => { setDeepLAuthKey, deleteDeepLAuthKey, - saveErrorDeepLAuthKey, savedDeepLAuthKey, }; }; \ No newline at end of file diff --git a/src-ui/logics/useReceiveRoutes.js b/src-ui/logics/useReceiveRoutes.js index 1d89be33..3cde999a 100644 --- a/src-ui/logics/useReceiveRoutes.js +++ b/src-ui/logics/useReceiveRoutes.js @@ -530,30 +530,6 @@ export const useReceiveRoutes = () => { "/get/data/transcription_engines": ()=>{}, // Not implemented on UI yet. (if ai_models has not been detected, this will be blank array[]. if the ai_models are ok but just network has not connected, it'l be only ["Whisper"]) }; - const error_status_routes = { - "/run/error_device": errorHandling_Backend, - - "/run/error_ctranslate2_weight": errorHandling_Backend, - "/run/error_whisper_weight": errorHandling_Backend, - - "/set/data/deepl_auth_key": errorHandling_Backend, - - "/run/error_translation_engine": errorHandling_Backend, - - "/set/data/mic_threshold": errorHandling_Backend, - "/set/data/mic_record_timeout": errorHandling_Backend, - "/set/data/mic_phrase_timeout": errorHandling_Backend, - "/set/data/mic_max_phrases": errorHandling_Backend, - - "/set/data/speaker_threshold": errorHandling_Backend, - "/set/data/speaker_record_timeout": errorHandling_Backend, - "/set/data/speaker_phrase_timeout": errorHandling_Backend, - "/set/data/speaker_max_phrases": errorHandling_Backend, - - "/set/data/osc_ip_address": errorHandling_Backend, - }; - - const receiveRoutes = (parsed_data) => { const initDataSyncProcess = (payload) => { for (const [endpoint, value] of Object.entries(payload)) { @@ -583,17 +559,12 @@ export const useReceiveRoutes = () => { break; case 400: - const error_route = error_status_routes[parsed_data.endpoint]; - if (error_route) { - error_route({ - message: parsed_data.result.message, - data: parsed_data.result.data, - endpoint: parsed_data.endpoint, - _result: parsed_data.result, - }); - } else { - handleInvalidEndpoint(parsed_data); - } + errorHandling_Backend({ + message: parsed_data.result.message, + data: parsed_data.result.data, + endpoint: parsed_data.endpoint, + result: parsed_data.result, + }); break; case 500: showNotification_Error( From 88c27a9c700cada31f2fbaa521cd7cae1e97f807 Mon Sep 17 00:00:00 2001 From: misyaguziya <53165965+misyaguziya@users.noreply.github.com> Date: Mon, 26 May 2025 16:15:04 +0900 Subject: [PATCH 3/3] [Update] WebSocket: Implement availability check for WebSocket server and refactor related logic --- src-python/controller.py | 18 +++++++++++------- src-python/model.py | 25 ------------------------- src-python/utils.py | 20 ++++++++++++++++++++ 3 files changed, 31 insertions(+), 32 deletions(-) diff --git a/src-python/controller.py b/src-python/controller.py index ccf44227..75c4439a 100644 --- a/src-python/controller.py +++ b/src-python/controller.py @@ -6,7 +6,7 @@ import re from device_manager import device_manager from config import config from model import model -from utils import removeLog, printLog, errorLogging, isConnectedNetwork, isValidIpAddress +from utils import removeLog, printLog, errorLogging, isConnectedNetwork, isValidIpAddress, isAvailableWebSocketServer class Controller: def __init__(self) -> None: @@ -1835,8 +1835,10 @@ class Controller: config.WEBSOCKET_HOST = data response = {"status":200, "result":config.WEBSOCKET_HOST} else: - model.stopWebSocketServer() - if model.checkWebSocketServerAvailable() is True: + if data == config.WEBSOCKET_HOST: + response = {"status":200, "result":config.WEBSOCKET_HOST} + elif isAvailableWebSocketServer(data, config.WEBSOCKET_PORT): + model.stopWebSocketServer() model.startWebSocketServer(data, config.WEBSOCKET_PORT) config.WEBSOCKET_HOST = data response = {"status":200, "result":config.WEBSOCKET_HOST} @@ -1861,8 +1863,10 @@ class Controller: config.WEBSOCKET_PORT = int(data) response = {"status":200, "result":config.WEBSOCKET_PORT} else: - model.stopWebSocketServer() - if model.checkWebSocketServerAvailable() is True: + if int(data) == config.WEBSOCKET_PORT: + return {"status":200, "result":config.WEBSOCKET_PORT} + elif isAvailableWebSocketServer(config.WEBSOCKET_HOST, int(data)) is True: + model.stopWebSocketServer() model.startWebSocketServer(config.WEBSOCKET_HOST, int(data)) config.WEBSOCKET_PORT = int(data) response = {"status":200, "result":config.WEBSOCKET_PORT} @@ -1882,7 +1886,7 @@ class Controller: @staticmethod def setEnableWebSocketServer(*args, **kwargs) -> dict: - if model.checkWebSocketServerAvailable() is True: + if isAvailableWebSocketServer(config.WEBSOCKET_HOST, config.WEBSOCKET_PORT) is True: model.startWebSocketServer(config.WEBSOCKET_HOST, config.WEBSOCKET_PORT) config.WEBSOCKET_SERVER = True response = {"status":200, "result":config.WEBSOCKET_SERVER} @@ -2003,7 +2007,7 @@ class Controller: printLog("Init WebSocket Server") if config.WEBSOCKET_SERVER is True: - if model.checkWebSocketServerAvailable() is True: + if isAvailableWebSocketServer(config.WEBSOCKET_HOST, config.WEBSOCKET_PORT) is True: model.startWebSocketServer(config.WEBSOCKET_HOST, config.WEBSOCKET_PORT) else: config.WEBSOCKET_SERVER = False diff --git a/src-python/model.py b/src-python/model.py index d52d6341..c4bd6466 100644 --- a/src-python/model.py +++ b/src-python/model.py @@ -1,8 +1,6 @@ import copy import gc import asyncio -import socket -import errno import json from subprocess import Popen from os import makedirs as os_makedirs @@ -840,29 +838,6 @@ class Model: """WebSocketメッセージ受信時の処理""" pass - def checkWebSocketServerAvailable(self): - """WebSocketサーバーのポートが使用中かどうかを確認する""" - response = True - try: - with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as chk: - # SO_REUSEADDRを設定してソケットの再利用を許可 - chk.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - try: - chk.bind((config.WEBSOCKET_HOST, config.WEBSOCKET_PORT)) - # シャットダウン前にリッスン状態にする必要はない - chk.close() - except OSError as e: - if e.errno == errno.EADDRINUSE: - response = False - else: - errorLogging() - response = False - except Exception: - errorLogging() - response = False - - return response - def startWebSocketServer(self, host, port): """WebSocketサーバーを起動し、別スレッドで実行する""" if self.websocket_server_alive is True: diff --git a/src-python/utils.py b/src-python/utils.py index de538dae..7bc0997b 100644 --- a/src-python/utils.py +++ b/src-python/utils.py @@ -8,6 +8,7 @@ from logging.handlers import RotatingFileHandler from ctranslate2 import get_supported_compute_types import requests import ipaddress +import socket def isConnectedNetwork(url="http://www.google.com", timeout=3) -> bool: try: @@ -16,6 +17,25 @@ def isConnectedNetwork(url="http://www.google.com", timeout=3) -> bool: except requests.RequestException: return False +def isAvailableWebSocketServer(host:str, port:int) -> bool: + """WebSocketサーバーのポートが使用中かどうかを確認する""" + response = True + try: + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as chk: + try: + # SO_REUSEADDRを設定してソケットの再利用を許可 + chk.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + chk.bind((host, port)) + # シャットダウン前にリッスン状態にする必要はない + chk.close() + except Exception: + response = False + except Exception: + errorLogging() + response = False + + return response + def isValidIpAddress(ip_address: str) -> bool: try: ipaddress.ip_address(ip_address)