Merge branch 'websocket' into develop

This commit is contained in:
misyaguziya
2025-05-29 09:16:39 +09:00
6 changed files with 163 additions and 160 deletions

View File

@@ -6,7 +6,7 @@ import re
from device_manager import device_manager from device_manager import device_manager
from config import config from config import config
from model import model from model import model
from utils import removeLog, printLog, errorLogging, isConnectedNetwork, isValidIpAddress from utils import removeLog, printLog, errorLogging, isConnectedNetwork, isValidIpAddress, isAvailableWebSocketServer
class Controller: class Controller:
def __init__(self) -> None: def __init__(self) -> None:
@@ -1835,8 +1835,10 @@ class Controller:
config.WEBSOCKET_HOST = data config.WEBSOCKET_HOST = data
response = {"status":200, "result":config.WEBSOCKET_HOST} response = {"status":200, "result":config.WEBSOCKET_HOST}
else: else:
if data == config.WEBSOCKET_HOST:
response = {"status":200, "result":config.WEBSOCKET_HOST}
elif isAvailableWebSocketServer(data, config.WEBSOCKET_PORT):
model.stopWebSocketServer() model.stopWebSocketServer()
if model.checkWebSocketServerAvailable() is True:
model.startWebSocketServer(data, config.WEBSOCKET_PORT) model.startWebSocketServer(data, config.WEBSOCKET_PORT)
config.WEBSOCKET_HOST = data config.WEBSOCKET_HOST = data
response = {"status":200, "result":config.WEBSOCKET_HOST} response = {"status":200, "result":config.WEBSOCKET_HOST}
@@ -1844,7 +1846,7 @@ class Controller:
response = { response = {
"status":400, "status":400,
"result":{ "result":{
"message":"WebSocket server port is not available", "message":"WebSocket server host is not available",
"data": config.WEBSOCKET_HOST "data": config.WEBSOCKET_HOST
} }
} }
@@ -1861,8 +1863,10 @@ class Controller:
config.WEBSOCKET_PORT = int(data) config.WEBSOCKET_PORT = int(data)
response = {"status":200, "result":config.WEBSOCKET_PORT} response = {"status":200, "result":config.WEBSOCKET_PORT}
else: else:
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.stopWebSocketServer()
if model.checkWebSocketServerAvailable() is True:
model.startWebSocketServer(config.WEBSOCKET_HOST, int(data)) model.startWebSocketServer(config.WEBSOCKET_HOST, int(data))
config.WEBSOCKET_PORT = int(data) config.WEBSOCKET_PORT = int(data)
response = {"status":200, "result":config.WEBSOCKET_PORT} response = {"status":200, "result":config.WEBSOCKET_PORT}
@@ -1882,7 +1886,7 @@ class Controller:
@staticmethod @staticmethod
def setEnableWebSocketServer(*args, **kwargs) -> dict: 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) model.startWebSocketServer(config.WEBSOCKET_HOST, config.WEBSOCKET_PORT)
config.WEBSOCKET_SERVER = True config.WEBSOCKET_SERVER = True
response = {"status":200, "result":config.WEBSOCKET_SERVER} response = {"status":200, "result":config.WEBSOCKET_SERVER}
@@ -1890,7 +1894,7 @@ class Controller:
response = { response = {
"status":400, "status":400,
"result":{ "result":{
"message":"WebSocket server port is not available", "message":"WebSocket server host or port is not available",
"data": config.WEBSOCKET_SERVER "data": config.WEBSOCKET_SERVER
} }
} }
@@ -2003,12 +2007,12 @@ class Controller:
printLog("Init WebSocket Server") printLog("Init WebSocket Server")
if config.WEBSOCKET_SERVER is True: 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) model.startWebSocketServer(config.WEBSOCKET_HOST, config.WEBSOCKET_PORT)
else: else:
config.WEBSOCKET_SERVER = False config.WEBSOCKET_SERVER = False
model.stopWebSocketServer() model.stopWebSocketServer()
printLog("WebSocket server port is not available") printLog("WebSocket server host or port is not available")
printLog("Update settings") printLog("Update settings")
self.updateConfigSettings() self.updateConfigSettings()

View File

@@ -1,8 +1,6 @@
import copy import copy
import gc import gc
import asyncio import asyncio
import socket
import errno
import json import json
from subprocess import Popen from subprocess import Popen
from os import makedirs as os_makedirs from os import makedirs as os_makedirs
@@ -840,29 +838,6 @@ class Model:
"""WebSocketメッセージ受信時の処理""" """WebSocketメッセージ受信時の処理"""
pass 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): def startWebSocketServer(self, host, port):
"""WebSocketサーバーを起動し、別スレッドで実行する""" """WebSocketサーバーを起動し、別スレッドで実行する"""
if self.websocket_server_alive is True: if self.websocket_server_alive is True:

View File

@@ -8,6 +8,7 @@ from logging.handlers import RotatingFileHandler
from ctranslate2 import get_supported_compute_types from ctranslate2 import get_supported_compute_types
import requests import requests
import ipaddress import ipaddress
import socket
def isConnectedNetwork(url="http://www.google.com", timeout=3) -> bool: def isConnectedNetwork(url="http://www.google.com", timeout=3) -> bool:
try: try:
@@ -16,6 +17,25 @@ def isConnectedNetwork(url="http://www.google.com", timeout=3) -> bool:
except requests.RequestException: except requests.RequestException:
return False 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: def isValidIpAddress(ip_address: str) -> bool:
try: try:
ipaddress.ip_address(ip_address) ipaddress.ip_address(ip_address)

View File

@@ -16,6 +16,7 @@ import {
useDeepLAuthKey, useDeepLAuthKey,
useOscIpAddress, useOscIpAddress,
useWebsocket,
} from "@logics_configs"; } from "@logics_configs";
import { ui_configs } from "../ui_configs"; import { ui_configs } from "../ui_configs";
@@ -31,106 +32,144 @@ export const _useBackendErrorHandling = () => {
const { updateSpeakerPhraseTimeout } = useSpeakerPhraseTimeout(); const { updateSpeakerPhraseTimeout } = useSpeakerPhraseTimeout();
const { updateSpeakerMaxWords } = useSpeakerMaxWords(); const { updateSpeakerMaxWords } = useSpeakerMaxWords();
const { updateDeepLAuthKey, saveErrorDeepLAuthKey } = useDeepLAuthKey(); const { updateDeepLAuthKey } = useDeepLAuthKey();
const { updateOscIpAddress } = useOscIpAddress(); const { updateOscIpAddress } = useOscIpAddress();
const { updateEnableWebsocket, updateWebsocketHost, updateWebsocketPort } = useWebsocket();
const errorHandling_Backend = ({message, data, endpoint, _result}) => { const errorHandling_Backend = ({message, data, endpoint, result}) => {
switch (message) { switch (endpoint) {
case "No mic device detected": case "/run/error_device":
showNotification_Error(t("common_error.no_device_mic")); if (message === "No mic device detected") showNotification_Error(t("common_error.no_device_mic"));
break; if (message === "No speaker device detected") showNotification_Error(t("common_error.no_device_speaker"));
case "No speaker device detected": return;
showNotification_Error(t("common_error.no_device_speaker"));
break;
case "Mic energy threshold value is out of range": case "/set/data/mic_threshold":
if (message === "Mic energy threshold value is out of range") {
showNotification_Error(t("common_error.threshold_invalid_value", showNotification_Error(t("common_error.threshold_invalid_value",
{ min: ui_configs.mic_threshold_min, max: ui_configs.mic_threshold_max }, { min: ui_configs.mic_threshold_min, max: ui_configs.mic_threshold_max },
)); ));
break; };
case "Speaker energy threshold value is out of range": return;
case "/set/data/speaker_threshold":
if (message === "Speaker energy threshold value is out of range") {
showNotification_Error(t("common_error.threshold_invalid_value", showNotification_Error(t("common_error.threshold_invalid_value",
{ min: ui_configs.speaker_threshold_min, max: ui_configs.speaker_threshold_max }, { min: ui_configs.speaker_threshold_min, max: ui_configs.speaker_threshold_max },
)); ));
break; }
return;
case "CTranslate2 weight download error": case "/run/error_ctranslate2_weight":
showNotification_Error(t("common_error.failed_download_weight_ctranslate2")); if (message === "CTranslate2 weight download error") showNotification_Error(t("common_error.failed_download_weight_ctranslate2"));
break; return;
case "Whisper weight download error": case "/run/error_whisper_weight":
showNotification_Error(t("common_error.failed_download_weight_whisper")); if (message === "Whisper weight download error") showNotification_Error(t("common_error.failed_download_weight_whisper"));
break; return;
case "Translation engine limit error": case "/run/error_translation_engine":
showNotification_Error(t("common_error.translation_limit")); if (message === "Translation engine limit error") showNotification_Error(t("common_error.translation_limit"));
break; return;
case "DeepL auth key length is not correct": case "/set/data/deepl_auth_key":
if (message === "DeepL auth key length is not correct") {
updateDeepLAuthKey(data); updateDeepLAuthKey(data);
showNotification_Error(t("common_error.deepl_auth_key_invalid_length")); showNotification_Error(t("common_error.deepl_auth_key_invalid_length"));
break; } else if (message === "Authentication failure of deepL auth key") {
case "Authentication failure of deepL auth key":
updateDeepLAuthKey(data); updateDeepLAuthKey(data);
showNotification_Error(t("common_error.deepl_auth_key_failed_authentication")); showNotification_Error(t("common_error.deepl_auth_key_failed_authentication"));
break; } else { // Exception
updateDeepLAuthKey(data);
showNotification_Error(message);
}
return;
case "Mic record timeout value is out of range": case "/set/data/mic_record_timeout":
if (message === "Mic record timeout value is out of range") {
updateMicRecordTimeout(data); updateMicRecordTimeout(data);
showNotification_Error( showNotification_Error(t("common_error.invalid_value_mic_record_timeout", {
t("common_error.invalid_value_mic_record_timeout", mic_phrase_timeout_label: t("config_page.transcription.mic_phrase_timeout.label")
{ mic_phrase_timeout_label: t("config_page.transcription.mic_phrase_timeout.label") } }));
)); }
break; return;
case "Mic phrase timeout value is out of range": case "/set/data/mic_phrase_timeout":
if (message === "Mic phrase timeout value is out of range") {
updateMicPhraseTimeout(data); updateMicPhraseTimeout(data);
showNotification_Error( showNotification_Error(t("common_error.invalid_value_mic_phrase_timeout", {
t("common_error.invalid_value_mic_phrase_timeout", mic_record_timeout_label: t("config_page.transcription.mic_record_timeout.label")
{ mic_record_timeout_label: t("config_page.transcription.mic_record_timeout.label") } }));
)); }
break; return;
case "Mic max phrases value is out of range": case "/set/data/mic_max_phrases":
if (message === "Mic max phrases value is out of range") {
updateMicMaxWords(data); updateMicMaxWords(data);
showNotification_Error(t("common_error.invalid_value_mic_max_phrase")); showNotification_Error(t("common_error.invalid_value_mic_max_phrase"));
break; }
return;
case "Speaker record timeout value is out of range": case "/set/data/speaker_record_timeout":
if (message === "Speaker record timeout value is out of range") {
updateSpeakerRecordTimeout(data); updateSpeakerRecordTimeout(data);
showNotification_Error( showNotification_Error(t("common_error.invalid_value_speaker_record_timeout", {
t("common_error.invalid_value_speaker_record_timeout", speaker_phrase_timeout_label: t("config_page.transcription.speaker_phrase_timeout.label")
{ speaker_phrase_timeout_label: t("config_page.transcription.speaker_phrase_timeout.label") } }));
)); }
break; return;
case "Speaker phrase timeout value is out of range": case "/set/data/speaker_phrase_timeout":
if (message === "Speaker phrase timeout value is out of range") {
updateSpeakerPhraseTimeout(data); updateSpeakerPhraseTimeout(data);
showNotification_Error( showNotification_Error(t("common_error.invalid_value_speaker_phrase_timeout", {
t("common_error.invalid_value_speaker_phrase_timeout", speaker_record_timeout_label: t("config_page.transcription.speaker_record_timeout.label")
{ speaker_record_timeout_label: t("config_page.transcription.speaker_record_timeout.label") } }));
)); }
break; return;
case "Speaker max phrases value is out of range": case "/set/data/speaker_max_phrases":
if (message === "Speaker max phrases value is out of range") {
updateSpeakerMaxWords(data); updateSpeakerMaxWords(data);
showNotification_Error(t("common_error.invalid_value_speaker_max_phrase")); showNotification_Error(t("common_error.invalid_value_speaker_max_phrase"));
break; }
return;
// Advanced Settings, error messages are set by Backend (EN only) // Advanced Settings, error messages are set by Backend (EN only)
case "Invalid IP address": case "/set/data/osc_ip_address":
if (message === "Invalid IP address") {
updateOscIpAddress(data); updateOscIpAddress(data);
showNotification_Error(message); showNotification_Error(message);
break; } 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); case "/set/enable/websocket_server":
if (message === "WebSocket server host or port is not available") {
updateEnableWebsocket(data);
showNotification_Error(message); showNotification_Error(message);
break; }
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: default:
// determine by endpoint, not message. console.error(`Invalid endpoint or message: ${endpoint}\nmessage: ${message}\nresult: ${JSON.stringify(result)}`);
if (endpoint === "/set/data/deepl_auth_key") saveErrorDeepLAuthKey({message, data, endpoint, _result}); return;
break;
} }
} }

View File

@@ -29,11 +29,6 @@ export const useDeepLAuthKey = () => {
showNotification_Success(t("config_page.translation.deepl_auth_key.auth_key_success")); showNotification_Success(t("config_page.translation.deepl_auth_key.auth_key_success"));
}; };
const saveErrorDeepLAuthKey = ({data, message}) => {
updateDeepLAuthKey(data);
showNotification_Error(message);
};
return { return {
currentDeepLAuthKey, currentDeepLAuthKey,
getDeepLAuthKey, getDeepLAuthKey,
@@ -41,7 +36,6 @@ export const useDeepLAuthKey = () => {
setDeepLAuthKey, setDeepLAuthKey,
deleteDeepLAuthKey, deleteDeepLAuthKey,
saveErrorDeepLAuthKey,
savedDeepLAuthKey, savedDeepLAuthKey,
}; };
}; };

View File

@@ -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"]) "/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 receiveRoutes = (parsed_data) => {
const initDataSyncProcess = (payload) => { const initDataSyncProcess = (payload) => {
for (const [endpoint, value] of Object.entries(payload)) { for (const [endpoint, value] of Object.entries(payload)) {
@@ -583,17 +559,12 @@ export const useReceiveRoutes = () => {
break; break;
case 400: case 400:
const error_route = error_status_routes[parsed_data.endpoint]; errorHandling_Backend({
if (error_route) {
error_route({
message: parsed_data.result.message, message: parsed_data.result.message,
data: parsed_data.result.data, data: parsed_data.result.data,
endpoint: parsed_data.endpoint, endpoint: parsed_data.endpoint,
_result: parsed_data.result, result: parsed_data.result,
}); });
} else {
handleInvalidEndpoint(parsed_data);
}
break; break;
case 500: case 500:
showNotification_Error( showNotification_Error(