Merge branch 'develop'
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -12,7 +12,8 @@ weights/
|
|||||||
error.log
|
error.log
|
||||||
*.exe
|
*.exe
|
||||||
*.ipynb
|
*.ipynb
|
||||||
|
VRCT.zip
|
||||||
|
VRCT_cuda.zip
|
||||||
|
|
||||||
# Added by WebUI migration
|
# Added by WebUI migration
|
||||||
# Logs
|
# Logs
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ a = Analysis(
|
|||||||
['src-python\\mainloop.py'],
|
['src-python\\mainloop.py'],
|
||||||
pathex=[],
|
pathex=[],
|
||||||
binaries=[],
|
binaries=[],
|
||||||
datas=[('./fonts', 'fonts/'), ('.venv/Lib/site-packages/zeroconf', 'zeroconf/'), ('.venv/Lib/site-packages/openvr', 'openvr/'), ('.venv/Lib/site-packages/pykakasi', 'pykakasi/'), ('.venv/Lib/site-packages/faster_whisper', 'faster_whisper/')],
|
datas=[('./fonts', 'fonts/'), ('.venv/Lib/site-packages/zeroconf', 'zeroconf/'), ('.venv/Lib/site-packages/openvr', 'openvr/'), ('.venv/Lib/site-packages/pykakasi', 'pykakasi/'), ('.venv/Lib/site-packages/faster_whisper', 'faster_whisper/'), ('.venv/Lib/site-packages/hf_xet', 'hf_xet/')],
|
||||||
hiddenimports=[],
|
hiddenimports=[],
|
||||||
hookspath=[],
|
hookspath=[],
|
||||||
hooksconfig={},
|
hooksconfig={},
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ a = Analysis(
|
|||||||
['src-python\\mainloop.py'],
|
['src-python\\mainloop.py'],
|
||||||
pathex=[],
|
pathex=[],
|
||||||
binaries=[],
|
binaries=[],
|
||||||
datas=[('./fonts', 'fonts/'), ('.venv_cuda/Lib/site-packages/zeroconf', 'zeroconf/'), ('.venv_cuda/Lib/site-packages/openvr', 'openvr/'), ('.venv_cuda/Lib/site-packages/pykakasi', 'pykakasi/'), ('.venv_cuda/Lib/site-packages/faster_whisper', 'faster_whisper/')],
|
datas=[('./fonts', 'fonts/'), ('.venv_cuda/Lib/site-packages/zeroconf', 'zeroconf/'), ('.venv_cuda/Lib/site-packages/openvr', 'openvr/'), ('.venv_cuda/Lib/site-packages/pykakasi', 'pykakasi/'), ('.venv_cuda/Lib/site-packages/faster_whisper', 'faster_whisper/'), ('.venv/Lib/site-packages/hf_xet', 'hf_xet/')],
|
||||||
hiddenimports=[],
|
hiddenimports=[],
|
||||||
hookspath=[],
|
hookspath=[],
|
||||||
hooksconfig={},
|
hooksconfig={},
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
call .venv/Scripts/activate
|
call .venv/Scripts/activate
|
||||||
pyinstaller backend.spec --distpath src-tauri/bin --clean --noconfirm
|
pyinstaller backend.spec --distpath src-tauri/bin --clean --noconfirm --log-level ERROR
|
||||||
@@ -1,2 +1,2 @@
|
|||||||
call .venv_cuda/Scripts/activate
|
call .venv_cuda/Scripts/activate
|
||||||
pyinstaller backend_cuda.spec --distpath src-tauri/bin --clean --noconfirm
|
pyinstaller backend_cuda.spec --distpath src-tauri/bin --clean --noconfirm --log-level ERROR
|
||||||
19
install.bat
19
install.bat
@@ -1,10 +1,25 @@
|
|||||||
python -m venv .venv
|
REM .venv exists
|
||||||
python -m venv .venv_cuda
|
if exist .venv (
|
||||||
|
rmdir /s /q .venv
|
||||||
|
)
|
||||||
|
|
||||||
|
REM make .venv
|
||||||
|
python -m venv .venv
|
||||||
|
|
||||||
|
REM install packages for .venv
|
||||||
call .venv/Scripts/activate
|
call .venv/Scripts/activate
|
||||||
python.exe -m pip install --upgrade pip
|
python.exe -m pip install --upgrade pip
|
||||||
pip install --no-cache-dir --force-reinstall -r requirements.txt
|
pip install --no-cache-dir --force-reinstall -r requirements.txt
|
||||||
|
|
||||||
|
REM if .venv_cuda exists
|
||||||
|
if exist .venv_cuda (
|
||||||
|
rmdir /s /q .venv_cuda
|
||||||
|
)
|
||||||
|
|
||||||
|
REM make .venv_cuda
|
||||||
|
python -m venv .venv_cuda
|
||||||
|
|
||||||
|
REM install packages for .venv_cuda
|
||||||
call .venv_cuda/Scripts/activate
|
call .venv_cuda/Scripts/activate
|
||||||
python.exe -m pip install --upgrade pip
|
python.exe -m pip install --upgrade pip
|
||||||
pip install --no-cache-dir --force-reinstall -r requirements_cuda.txt
|
pip install --no-cache-dir --force-reinstall -r requirements_cuda.txt
|
||||||
@@ -24,6 +24,9 @@ common_error:
|
|||||||
invalid_value_speaker_phrase_timeout: "It cannot be set lower than '{{speaker_record_timeout_label}}' with a value of 0 or more."
|
invalid_value_speaker_phrase_timeout: "It cannot be set lower than '{{speaker_record_timeout_label}}' with a value of 0 or more."
|
||||||
invalid_value_speaker_max_phrase: "You can set a number equal to or greater than 0."
|
invalid_value_speaker_max_phrase: "You can set a number equal to or greater than 0."
|
||||||
|
|
||||||
|
common_warning:
|
||||||
|
unable_to_use_osc_query: "The functions below have been automatically disabled because receiving OSC data is not possible due to OSC IP Address settings."
|
||||||
|
|
||||||
main_page:
|
main_page:
|
||||||
translation: "Translation"
|
translation: "Translation"
|
||||||
transcription_send: "Voice2Chatbox"
|
transcription_send: "Voice2Chatbox"
|
||||||
@@ -257,6 +260,12 @@ config_page:
|
|||||||
label: "Open Config File"
|
label: "Open Config File"
|
||||||
switch_compute_device:
|
switch_compute_device:
|
||||||
label: "Switch VRCT To CPU/GPU Version"
|
label: "Switch VRCT To CPU/GPU Version"
|
||||||
|
enable_websocket:
|
||||||
|
label: "Enable WebSocket Server"
|
||||||
|
websocket_host:
|
||||||
|
label: "WebSocket Host"
|
||||||
|
websocket_port:
|
||||||
|
label: "WebSocket Port"
|
||||||
|
|
||||||
plugin_notifications:
|
plugin_notifications:
|
||||||
downloading: Downloading the plugin.
|
downloading: Downloading the plugin.
|
||||||
|
|||||||
@@ -24,10 +24,13 @@ common_error:
|
|||||||
invalid_value_speaker_phrase_timeout: "0 以上で 「{{speaker_record_timeout_label}}」 より小さくすることはできません。"
|
invalid_value_speaker_phrase_timeout: "0 以上で 「{{speaker_record_timeout_label}}」 より小さくすることはできません。"
|
||||||
invalid_value_speaker_max_phrase: "0 以上の数値を設定できます。"
|
invalid_value_speaker_max_phrase: "0 以上の数値を設定できます。"
|
||||||
|
|
||||||
|
common_warning:
|
||||||
|
unable_to_use_osc_query: "OSC IP Address の設定によりOSCデータの受信ができないため、以下の機能が自動的に無効になっています。"
|
||||||
|
|
||||||
main_page:
|
main_page:
|
||||||
translation: "翻訳"
|
translation: "翻訳"
|
||||||
transcription_send: "音声認識 マイク"
|
transcription_send: "マイク入力"
|
||||||
transcription_receive: "音声認識 スピーカー"
|
transcription_receive: "聞き取り"
|
||||||
foreground: "最前面固定"
|
foreground: "最前面固定"
|
||||||
|
|
||||||
language_settings: "言語設定"
|
language_settings: "言語設定"
|
||||||
@@ -131,7 +134,7 @@ config_page:
|
|||||||
ctranslate2_compute_device:
|
ctranslate2_compute_device:
|
||||||
label: "AI翻訳 {{ctranslate2}} の処理デバイス"
|
label: "AI翻訳 {{ctranslate2}} の処理デバイス"
|
||||||
deepl_auth_key:
|
deepl_auth_key:
|
||||||
label: "DeepL API 認証キー"
|
label: "DeepL APIキーの登録"
|
||||||
desc: "使用の際は、メイン画面にある {{translator}} をDeepL_APIに変更してください。\n※対応していない言語もあります。"
|
desc: "使用の際は、メイン画面にある {{translator}} をDeepL_APIに変更してください。\n※対応していない言語もあります。"
|
||||||
open_auth_key_webpage: "DeepLアカウントページを開く"
|
open_auth_key_webpage: "DeepLアカウントページを開く"
|
||||||
save: "保存"
|
save: "保存"
|
||||||
@@ -257,7 +260,12 @@ config_page:
|
|||||||
label: "設定ファイルを開く"
|
label: "設定ファイルを開く"
|
||||||
switch_compute_device:
|
switch_compute_device:
|
||||||
label: "VRCT CPU/GPUバージョンの切り替え"
|
label: "VRCT CPU/GPUバージョンの切り替え"
|
||||||
|
enable_websocket:
|
||||||
|
label: "WebSocketサーバーを有効にする"
|
||||||
|
websocket_host:
|
||||||
|
label: "WebSocket Host"
|
||||||
|
websocket_port:
|
||||||
|
label: "WebSocket Port"
|
||||||
|
|
||||||
plugin_notifications:
|
plugin_notifications:
|
||||||
downloading: プラグインをダウンロード中。
|
downloading: プラグインをダウンロード中。
|
||||||
|
|||||||
@@ -15,6 +15,10 @@ pydub==0.25.1
|
|||||||
psutil==5.9.8
|
psutil==5.9.8
|
||||||
pykakasi==2.3.0
|
pykakasi==2.3.0
|
||||||
pycaw==20240210
|
pycaw==20240210
|
||||||
|
websockets==15.0.1
|
||||||
|
huggingface_hub==0.32.2
|
||||||
|
hf-xet==1.1.2
|
||||||
|
setuptools==80.8.0
|
||||||
translators @ git+https://github.com/misyaguziya/translators@5.9.2.1
|
translators @ git+https://github.com/misyaguziya/translators@5.9.2.1
|
||||||
SpeechRecognition @ git+https://github.com/misyaguziya/custom_speech_recognition@3.10.4.1
|
SpeechRecognition @ git+https://github.com/misyaguziya/custom_speech_recognition@3.10.4.1
|
||||||
tinyoscquery @ git+https://github.com/cyberkitsune/tinyoscquery@0.1.3
|
tinyoscquery @ git+https://github.com/cyberkitsune/tinyoscquery@0.1.3
|
||||||
@@ -16,6 +16,10 @@ pydub==0.25.1
|
|||||||
psutil==5.9.8
|
psutil==5.9.8
|
||||||
pykakasi==2.3.0
|
pykakasi==2.3.0
|
||||||
pycaw==20240210
|
pycaw==20240210
|
||||||
|
websockets==15.0.1
|
||||||
|
huggingface_hub==0.32.2
|
||||||
|
hf-xet==1.1.2
|
||||||
|
setuptools==80.8.0
|
||||||
translators @ git+https://github.com/misyaguziya/translators@5.9.2.1
|
translators @ git+https://github.com/misyaguziya/translators@5.9.2.1
|
||||||
SpeechRecognition @ git+https://github.com/misyaguziya/custom_speech_recognition@3.10.4.1
|
SpeechRecognition @ git+https://github.com/misyaguziya/custom_speech_recognition@3.10.4.1
|
||||||
tinyoscquery @ git+https://github.com/cyberkitsune/tinyoscquery@0.1.3
|
tinyoscquery @ git+https://github.com/cyberkitsune/tinyoscquery@0.1.3
|
||||||
@@ -954,9 +954,42 @@ class Config:
|
|||||||
self._NOTIFICATION_VRC_SFX = value
|
self._NOTIFICATION_VRC_SFX = value
|
||||||
self.saveConfig(inspect.currentframe().f_code.co_name, value)
|
self.saveConfig(inspect.currentframe().f_code.co_name, value)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def WEBSOCKET_SERVER(self):
|
||||||
|
return self._WEBSOCKET_SERVER
|
||||||
|
|
||||||
|
@WEBSOCKET_SERVER.setter
|
||||||
|
def WEBSOCKET_SERVER(self, value):
|
||||||
|
if isinstance(value, bool):
|
||||||
|
self._WEBSOCKET_SERVER = value
|
||||||
|
self.saveConfig(inspect.currentframe().f_code.co_name, value)
|
||||||
|
|
||||||
|
@property
|
||||||
|
@json_serializable('WEBSOCKET_HOST')
|
||||||
|
def WEBSOCKET_HOST(self):
|
||||||
|
return self._WEBSOCKET_HOST
|
||||||
|
|
||||||
|
@WEBSOCKET_HOST.setter
|
||||||
|
def WEBSOCKET_HOST(self, value):
|
||||||
|
if isinstance(value, str):
|
||||||
|
self._WEBSOCKET_HOST = value
|
||||||
|
self.saveConfig(inspect.currentframe().f_code.co_name, value)
|
||||||
|
|
||||||
|
@property
|
||||||
|
@json_serializable('WEBSOCKET_PORT')
|
||||||
|
def WEBSOCKET_PORT(self):
|
||||||
|
return self._WEBSOCKET_PORT
|
||||||
|
|
||||||
|
@WEBSOCKET_PORT.setter
|
||||||
|
def WEBSOCKET_PORT(self, value):
|
||||||
|
if isinstance(value, int):
|
||||||
|
self._WEBSOCKET_PORT = value
|
||||||
|
self.saveConfig(inspect.currentframe().f_code.co_name, value)
|
||||||
|
|
||||||
|
|
||||||
def init_config(self):
|
def init_config(self):
|
||||||
# Read Only
|
# Read Only
|
||||||
self._VERSION = "3.1.2"
|
self._VERSION = "3.2.0"
|
||||||
if getattr(sys, 'frozen', False):
|
if getattr(sys, 'frozen', False):
|
||||||
self._PATH_LOCAL = os_path.dirname(sys.executable)
|
self._PATH_LOCAL = os_path.dirname(sys.executable)
|
||||||
else:
|
else:
|
||||||
@@ -1139,6 +1172,9 @@ class Config:
|
|||||||
self._LOGGER_FEATURE = False
|
self._LOGGER_FEATURE = False
|
||||||
self._VRC_MIC_MUTE_SYNC = False
|
self._VRC_MIC_MUTE_SYNC = False
|
||||||
self._NOTIFICATION_VRC_SFX = True
|
self._NOTIFICATION_VRC_SFX = True
|
||||||
|
self._WEBSOCKET_SERVER = False
|
||||||
|
self._WEBSOCKET_HOST = "127.0.0.1"
|
||||||
|
self._WEBSOCKET_PORT = 2231
|
||||||
|
|
||||||
def load_config(self):
|
def load_config(self):
|
||||||
if os_path.isfile(self.PATH_CONFIG) is not False:
|
if os_path.isfile(self.PATH_CONFIG) is not False:
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from typing import Callable, Union, Any
|
from typing import Callable, Any
|
||||||
from time import sleep
|
from time import sleep
|
||||||
from subprocess import Popen
|
from subprocess import Popen
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
@@ -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:
|
||||||
@@ -294,6 +294,19 @@ class Controller:
|
|||||||
"translation":translation,
|
"translation":translation,
|
||||||
"transliteration":transliteration
|
"transliteration":transliteration
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if model.checkWebSocketServerAlive() is True:
|
||||||
|
model.websocketSendMessage(
|
||||||
|
{
|
||||||
|
"type":"SENT",
|
||||||
|
"src_languages":config.SELECTED_YOUR_LANGUAGES[config.SELECTED_TAB_NO],
|
||||||
|
"dst_languages":config.SELECTED_TARGET_LANGUAGES[config.SELECTED_TAB_NO],
|
||||||
|
"message":message,
|
||||||
|
"translation":translation,
|
||||||
|
"transliteration":transliteration
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
if config.LOGGER_FEATURE is True:
|
if config.LOGGER_FEATURE is True:
|
||||||
if len(translation) > 0:
|
if len(translation) > 0:
|
||||||
translation = " (" + "/".join(translation) + ")"
|
translation = " (" + "/".join(translation) + ")"
|
||||||
@@ -377,6 +390,19 @@ class Controller:
|
|||||||
"translation":translation,
|
"translation":translation,
|
||||||
"transliteration":transliteration,
|
"transliteration":transliteration,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if model.checkWebSocketServerAlive() is True:
|
||||||
|
model.websocketSendMessage(
|
||||||
|
{
|
||||||
|
"type":"RECEIVED",
|
||||||
|
"src_languages":config.SELECTED_TARGET_LANGUAGES[config.SELECTED_TAB_NO],
|
||||||
|
"dst_languages":config.SELECTED_YOUR_LANGUAGES[config.SELECTED_TAB_NO],
|
||||||
|
"message":message,
|
||||||
|
"translation":translation,
|
||||||
|
"transliteration":transliteration
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
if config.LOGGER_FEATURE is True:
|
if config.LOGGER_FEATURE is True:
|
||||||
if len(translation) > 0:
|
if len(translation) > 0:
|
||||||
translation = " (" + "/".join(translation) + ")"
|
translation = " (" + "/".join(translation) + ")"
|
||||||
@@ -434,11 +460,23 @@ class Controller:
|
|||||||
overlay_image = model.createOverlayImageLargeLog("send", message, translation[0] if len(translation) > 0 else "")
|
overlay_image = model.createOverlayImageLargeLog("send", message, translation[0] if len(translation) > 0 else "")
|
||||||
model.updateOverlayLargeLog(overlay_image)
|
model.updateOverlayLargeLog(overlay_image)
|
||||||
|
|
||||||
# update textbox message log (Sent)
|
if model.checkWebSocketServerAlive() is True:
|
||||||
|
model.websocketSendMessage(
|
||||||
|
{
|
||||||
|
"type":"CHAT",
|
||||||
|
"src_languages":config.SELECTED_YOUR_LANGUAGES[config.SELECTED_TAB_NO],
|
||||||
|
"dst_languages":config.SELECTED_TARGET_LANGUAGES[config.SELECTED_TAB_NO],
|
||||||
|
"message":message,
|
||||||
|
"translation":translation,
|
||||||
|
"transliteration":transliteration
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# update textbox message log (Chat)
|
||||||
if config.LOGGER_FEATURE is True:
|
if config.LOGGER_FEATURE is True:
|
||||||
if len(translation) > 0:
|
if len(translation) > 0:
|
||||||
translation_text = " (" + "/".join(translation) + ")"
|
translation_text = " (" + "/".join(translation) + ")"
|
||||||
model.logger.info(f"[SENT] {message}{translation_text}")
|
model.logger.info(f"[CHAT] {message}{translation_text}")
|
||||||
|
|
||||||
return {"status":200,
|
return {"status":200,
|
||||||
"result":{
|
"result":{
|
||||||
@@ -1099,8 +1137,7 @@ class Controller:
|
|||||||
def getOscIpAddress(*args, **kwargs) -> dict:
|
def getOscIpAddress(*args, **kwargs) -> dict:
|
||||||
return {"status":200, "result":config.OSC_IP_ADDRESS}
|
return {"status":200, "result":config.OSC_IP_ADDRESS}
|
||||||
|
|
||||||
@staticmethod
|
def setOscIpAddress(self, data, *args, **kwargs) -> dict:
|
||||||
def setOscIpAddress(data, *args, **kwargs) -> dict:
|
|
||||||
if isValidIpAddress(data) is False:
|
if isValidIpAddress(data) is False:
|
||||||
response = {
|
response = {
|
||||||
"status":400,
|
"status":400,
|
||||||
@@ -1113,6 +1150,11 @@ class Controller:
|
|||||||
try:
|
try:
|
||||||
model.setOscIpAddress(data)
|
model.setOscIpAddress(data)
|
||||||
config.OSC_IP_ADDRESS = data
|
config.OSC_IP_ADDRESS = data
|
||||||
|
if model.getIsOscQueryEnabled() is True:
|
||||||
|
self.enableOscQuery()
|
||||||
|
else:
|
||||||
|
self.setDisableVrcMicMuteSync()
|
||||||
|
self.disableOscQuery()
|
||||||
response = {"status":200, "result":config.OSC_IP_ADDRESS}
|
response = {"status":200, "result":config.OSC_IP_ADDRESS}
|
||||||
except Exception:
|
except Exception:
|
||||||
model.setOscIpAddress(config.OSC_IP_ADDRESS)
|
model.setOscIpAddress(config.OSC_IP_ADDRESS)
|
||||||
@@ -1388,10 +1430,20 @@ class Controller:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def setEnableVrcMicMuteSync(*args, **kwargs) -> dict:
|
def setEnableVrcMicMuteSync(*args, **kwargs) -> dict:
|
||||||
config.VRC_MIC_MUTE_SYNC = True
|
if model.getIsOscQueryEnabled() is True:
|
||||||
model.setMuteSelfStatus()
|
config.VRC_MIC_MUTE_SYNC = True
|
||||||
model.changeMicTranscriptStatus()
|
model.setMuteSelfStatus()
|
||||||
return {"status":200, "result":config.VRC_MIC_MUTE_SYNC}
|
model.changeMicTranscriptStatus()
|
||||||
|
response = {"status":200, "result":config.VRC_MIC_MUTE_SYNC}
|
||||||
|
else:
|
||||||
|
response = {
|
||||||
|
"status":400,
|
||||||
|
"result":{
|
||||||
|
"message":"Cannot enable VRC mic mute sync while OSC query is disabled",
|
||||||
|
"data": config.VRC_MIC_MUTE_SYNC
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return response
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def setDisableVrcMicMuteSync(*args, **kwargs) -> dict:
|
def setDisableVrcMicMuteSync(*args, **kwargs) -> dict:
|
||||||
@@ -1778,9 +1830,117 @@ class Controller:
|
|||||||
model.stopWatchdog()
|
model.stopWatchdog()
|
||||||
return {"status":200, "result":True}
|
return {"status":200, "result":True}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def getWebSocketHost(*args, **kwargs) -> dict:
|
||||||
|
return {"status":200, "result":config.WEBSOCKET_HOST}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def setWebSocketHost(data, *args, **kwargs) -> dict:
|
||||||
|
if isValidIpAddress(data) is False:
|
||||||
|
response = {
|
||||||
|
"status":400,
|
||||||
|
"result":{
|
||||||
|
"message":"Invalid IP address",
|
||||||
|
"data": config.WEBSOCKET_HOST
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
if model.checkWebSocketServerAlive() is False:
|
||||||
|
config.WEBSOCKET_HOST = data
|
||||||
|
response = {"status":200, "result":config.WEBSOCKET_HOST}
|
||||||
|
else:
|
||||||
|
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}
|
||||||
|
else:
|
||||||
|
response = {
|
||||||
|
"status":400,
|
||||||
|
"result":{
|
||||||
|
"message":"WebSocket server host is not available",
|
||||||
|
"data": config.WEBSOCKET_HOST
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def getWebSocketPort(*args, **kwargs) -> dict:
|
||||||
|
return {"status":200, "result":config.WEBSOCKET_PORT}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def setWebSocketPort(data, *args, **kwargs) -> dict:
|
||||||
|
if model.checkWebSocketServerAlive() is False:
|
||||||
|
config.WEBSOCKET_PORT = int(data)
|
||||||
|
response = {"status":200, "result":config.WEBSOCKET_PORT}
|
||||||
|
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.startWebSocketServer(config.WEBSOCKET_HOST, int(data))
|
||||||
|
config.WEBSOCKET_PORT = int(data)
|
||||||
|
response = {"status":200, "result":config.WEBSOCKET_PORT}
|
||||||
|
else:
|
||||||
|
response = {
|
||||||
|
"status":400,
|
||||||
|
"result":{
|
||||||
|
"message":"WebSocket server port is not available",
|
||||||
|
"data": config.WEBSOCKET_PORT
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return response
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def getWebSocketServer(*args, **kwargs) -> dict:
|
||||||
|
return {"status":200, "result":config.WEBSOCKET_SERVER}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def setEnableWebSocketServer(*args, **kwargs) -> dict:
|
||||||
|
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}
|
||||||
|
else:
|
||||||
|
response = {
|
||||||
|
"status":400,
|
||||||
|
"result":{
|
||||||
|
"message":"WebSocket server host or port is not available",
|
||||||
|
"data": config.WEBSOCKET_SERVER
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return response
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def setDisableWebSocketServer(*args, **kwargs) -> dict:
|
||||||
|
config.WEBSOCKET_SERVER = False
|
||||||
|
model.stopWebSocketServer()
|
||||||
|
return {"status":200, "result":config.WEBSOCKET_SERVER}
|
||||||
|
|
||||||
def initializationProgress(self, progress):
|
def initializationProgress(self, progress):
|
||||||
self.run(200, self.run_mapping["initialization_progress"], progress)
|
self.run(200, self.run_mapping["initialization_progress"], progress)
|
||||||
|
|
||||||
|
def enableOscQuery(self):
|
||||||
|
self.run(
|
||||||
|
200,
|
||||||
|
self.run_mapping["enable_osc_query"],
|
||||||
|
{
|
||||||
|
"data": True,
|
||||||
|
"disabled_functions": []
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
def disableOscQuery(self):
|
||||||
|
self.run(200, self.run_mapping["enable_osc_query"], {
|
||||||
|
"data": False,
|
||||||
|
"disabled_functions": [
|
||||||
|
"vrc_mic_mute_sync",
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
def init(self, *args, **kwargs) -> None:
|
def init(self, *args, **kwargs) -> None:
|
||||||
removeLog()
|
removeLog()
|
||||||
printLog("Start Initialization")
|
printLog("Start Initialization")
|
||||||
@@ -1892,6 +2052,14 @@ class Controller:
|
|||||||
# init OSC receive
|
# init OSC receive
|
||||||
printLog("Init OSC Receive")
|
printLog("Init OSC Receive")
|
||||||
model.startReceiveOSC()
|
model.startReceiveOSC()
|
||||||
|
osc_query_enabled = model.getIsOscQueryEnabled()
|
||||||
|
if osc_query_enabled is True:
|
||||||
|
self.enableOscQuery()
|
||||||
|
else:
|
||||||
|
# OSC Query is disabled, so disable VRC some features
|
||||||
|
config.VRC_MIC_MUTE_SYNC = False
|
||||||
|
self.disableOscQuery()
|
||||||
|
|
||||||
if config.VRC_MIC_MUTE_SYNC is True:
|
if config.VRC_MIC_MUTE_SYNC is True:
|
||||||
self.setEnableVrcMicMuteSync()
|
self.setEnableVrcMicMuteSync()
|
||||||
|
|
||||||
@@ -1911,6 +2079,15 @@ class Controller:
|
|||||||
if (config.OVERLAY_SMALL_LOG is True or config.OVERLAY_LARGE_LOG is True):
|
if (config.OVERLAY_SMALL_LOG is True or config.OVERLAY_LARGE_LOG is True):
|
||||||
model.startOverlay()
|
model.startOverlay()
|
||||||
|
|
||||||
|
printLog("Init WebSocket Server")
|
||||||
|
if config.WEBSOCKET_SERVER 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
|
||||||
|
model.stopWebSocketServer()
|
||||||
|
printLog("WebSocket server host or port is not available")
|
||||||
|
|
||||||
printLog("Update settings")
|
printLog("Update settings")
|
||||||
self.updateConfigSettings()
|
self.updateConfigSettings()
|
||||||
|
|
||||||
|
|||||||
@@ -4,8 +4,11 @@ import time
|
|||||||
from typing import Any
|
from typing import Any
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
from queue import Queue
|
from queue import Queue
|
||||||
from controller import Controller
|
import logging
|
||||||
from utils import printLog, printResponse, errorLogging, encodeBase64
|
from controller import Controller # noqa: E402
|
||||||
|
from utils import printLog, printResponse, errorLogging, encodeBase64 # noqa: E402
|
||||||
|
|
||||||
|
logging.getLogger("huggingface_hub").setLevel(logging.ERROR)
|
||||||
|
|
||||||
run_mapping = {
|
run_mapping = {
|
||||||
"connected_network":"/run/connected_network",
|
"connected_network":"/run/connected_network",
|
||||||
@@ -42,6 +45,8 @@ run_mapping = {
|
|||||||
|
|
||||||
"initialization_progress":"/run/initialization_progress",
|
"initialization_progress":"/run/initialization_progress",
|
||||||
"initialization_complete":"/run/initialization_complete",
|
"initialization_complete":"/run/initialization_complete",
|
||||||
|
|
||||||
|
"enable_osc_query":"/run/enable_osc_query",
|
||||||
}
|
}
|
||||||
|
|
||||||
def run(status:int, endpoint:str, result:Any) -> None:
|
def run(status:int, endpoint:str, result:Any) -> None:
|
||||||
@@ -291,6 +296,15 @@ mapping = {
|
|||||||
"/set/enable/send_received_message_to_vrc": {"status": True, "variable":controller.setEnableSendReceivedMessageToVrc},
|
"/set/enable/send_received_message_to_vrc": {"status": True, "variable":controller.setEnableSendReceivedMessageToVrc},
|
||||||
"/set/disable/send_received_message_to_vrc": {"status": True, "variable":controller.setDisableSendReceivedMessageToVrc},
|
"/set/disable/send_received_message_to_vrc": {"status": True, "variable":controller.setDisableSendReceivedMessageToVrc},
|
||||||
|
|
||||||
|
# WebSocket Settings
|
||||||
|
"/get/data/websocket_host": {"status": True, "variable":controller.getWebSocketHost},
|
||||||
|
"/set/data/websocket_host": {"status": True, "variable":controller.setWebSocketHost},
|
||||||
|
"/get/data/websocket_port": {"status": True, "variable":controller.getWebSocketPort},
|
||||||
|
"/set/data/websocket_port": {"status": True, "variable":controller.setWebSocketPort},
|
||||||
|
"/get/data/websocket_server": {"status": True, "variable":controller.getWebSocketServer},
|
||||||
|
"/set/enable/websocket_server": {"status": True, "variable":controller.setEnableWebSocketServer},
|
||||||
|
"/set/disable/websocket_server": {"status": True, "variable":controller.setDisableWebSocketServer},
|
||||||
|
|
||||||
# Advanced Settings
|
# Advanced Settings
|
||||||
"/get/data/osc_ip_address": {"status": True, "variable":controller.getOscIpAddress},
|
"/get/data/osc_ip_address": {"status": True, "variable":controller.getOscIpAddress},
|
||||||
"/set/data/osc_ip_address": {"status": True, "variable":controller.setOscIpAddress},
|
"/set/data/osc_ip_address": {"status": True, "variable":controller.setOscIpAddress},
|
||||||
@@ -367,7 +381,7 @@ class Main:
|
|||||||
if status == 423:
|
if status == 423:
|
||||||
self.queue.put((endpoint, data))
|
self.queue.put((endpoint, data))
|
||||||
else:
|
else:
|
||||||
printLog(endpoint, {"send_data":result})
|
printLog(endpoint, {"status": status, "send_data": result})
|
||||||
printResponse(status, endpoint, result)
|
printResponse(status, endpoint, result)
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import copy
|
import copy
|
||||||
import gc
|
import gc
|
||||||
|
import asyncio
|
||||||
|
import json
|
||||||
from subprocess import Popen
|
from subprocess import Popen
|
||||||
from os import makedirs as os_makedirs
|
from os import makedirs as os_makedirs
|
||||||
from os import path as os_path
|
from os import path as os_path
|
||||||
@@ -29,6 +31,7 @@ from models.transcription.transcription_whisper import checkWhisperWeight, downl
|
|||||||
from models.overlay.overlay import Overlay
|
from models.overlay.overlay import Overlay
|
||||||
from models.overlay.overlay_image import OverlayImage
|
from models.overlay.overlay_image import OverlayImage
|
||||||
from models.watchdog.watchdog import Watchdog
|
from models.watchdog.watchdog import Watchdog
|
||||||
|
from models.websocket.websocket_server import WebSocketServer
|
||||||
from utils import errorLogging, setupLogger
|
from utils import errorLogging, setupLogger
|
||||||
|
|
||||||
class threadFnc(Thread):
|
class threadFnc(Thread):
|
||||||
@@ -99,6 +102,10 @@ class Model:
|
|||||||
self.kks = kakasi()
|
self.kks = kakasi()
|
||||||
self.watchdog = Watchdog(config.WATCHDOG_TIMEOUT, config.WATCHDOG_INTERVAL)
|
self.watchdog = Watchdog(config.WATCHDOG_TIMEOUT, config.WATCHDOG_INTERVAL)
|
||||||
self.osc_handler = OSCHandler(config.OSC_IP_ADDRESS, config.OSC_PORT)
|
self.osc_handler = OSCHandler(config.OSC_IP_ADDRESS, config.OSC_PORT)
|
||||||
|
self.websocket_server = None
|
||||||
|
self.websocket_server_loop = False
|
||||||
|
self.websocket_server_alive = False
|
||||||
|
self.th_websocket_server = None
|
||||||
|
|
||||||
def checkTranslatorCTranslate2ModelWeight(self, weight_type:str):
|
def checkTranslatorCTranslate2ModelWeight(self, weight_type:str):
|
||||||
return checkCTranslate2Weight(config.PATH_LOCAL, weight_type)
|
return checkCTranslate2Weight(config.PATH_LOCAL, weight_type)
|
||||||
@@ -292,11 +299,8 @@ class Model:
|
|||||||
def oscSendMessage(self, message:str):
|
def oscSendMessage(self, message:str):
|
||||||
self.osc_handler.sendMessage(message=message, notification=config.NOTIFICATION_VRC_SFX)
|
self.osc_handler.sendMessage(message=message, notification=config.NOTIFICATION_VRC_SFX)
|
||||||
|
|
||||||
def getMuteSelfStatus(self):
|
|
||||||
return self.osc_handler.getOSCParameterMuteSelf()
|
|
||||||
|
|
||||||
def setMuteSelfStatus(self):
|
def setMuteSelfStatus(self):
|
||||||
self.mic_mute_status = self.getMuteSelfStatus()
|
self.mic_mute_status = self.osc_handler.getOSCParameterMuteSelf()
|
||||||
|
|
||||||
def startReceiveOSC(self):
|
def startReceiveOSC(self):
|
||||||
def changeHandlerMute(address, osc_arguments):
|
def changeHandlerMute(address, osc_arguments):
|
||||||
@@ -311,11 +315,15 @@ class Model:
|
|||||||
dict_filter_and_target = {
|
dict_filter_and_target = {
|
||||||
self.osc_handler.osc_parameter_muteself: changeHandlerMute,
|
self.osc_handler.osc_parameter_muteself: changeHandlerMute,
|
||||||
}
|
}
|
||||||
self.osc_handler.receiveOscParameters(dict_filter_and_target)
|
self.osc_handler.setDictFilterAndTarget(dict_filter_and_target)
|
||||||
|
self.osc_handler.receiveOscParameters()
|
||||||
|
|
||||||
def stopReceiveOSC(self):
|
def stopReceiveOSC(self):
|
||||||
self.osc_handler.oscServerStop()
|
self.osc_handler.oscServerStop()
|
||||||
|
|
||||||
|
def getIsOscQueryEnabled(self):
|
||||||
|
return self.osc_handler.getIsOscQueryEnabled()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def checkSoftwareUpdated():
|
def checkSoftwareUpdated():
|
||||||
# check update
|
# check update
|
||||||
@@ -503,12 +511,16 @@ class Model:
|
|||||||
|
|
||||||
def changeMicTranscriptStatus(self):
|
def changeMicTranscriptStatus(self):
|
||||||
if config.VRC_MIC_MUTE_SYNC is True:
|
if config.VRC_MIC_MUTE_SYNC is True:
|
||||||
if self.mic_mute_status is True:
|
match self.mic_mute_status:
|
||||||
self.pauseMicTranscript()
|
case True:
|
||||||
elif self.mic_mute_status is False:
|
self.pauseMicTranscript()
|
||||||
self.resumeMicTranscript()
|
case False:
|
||||||
else:
|
self.resumeMicTranscript()
|
||||||
pass
|
case None:
|
||||||
|
# mute selfの状態が不明な場合は一時停止しない
|
||||||
|
self.resumeMicTranscript()
|
||||||
|
case _:
|
||||||
|
pass
|
||||||
else:
|
else:
|
||||||
self.resumeMicTranscript()
|
self.resumeMicTranscript()
|
||||||
|
|
||||||
@@ -827,4 +839,86 @@ class Model:
|
|||||||
self.th_watchdog.join()
|
self.th_watchdog.join()
|
||||||
self.th_watchdog = None
|
self.th_watchdog = None
|
||||||
|
|
||||||
|
def message_handler(websocket, message):
|
||||||
|
"""WebSocketメッセージ受信時の処理"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def startWebSocketServer(self, host, port):
|
||||||
|
"""WebSocketサーバーを起動し、別スレッドで実行する"""
|
||||||
|
if self.websocket_server_alive is True:
|
||||||
|
# サーバーが既に起動している場合は何もしない
|
||||||
|
return
|
||||||
|
|
||||||
|
self.websocket_server_loop = True
|
||||||
|
self.websocket_server_alive = False # 初期状態を明示
|
||||||
|
|
||||||
|
async def WebSocketServerMain():
|
||||||
|
try:
|
||||||
|
self.websocket_server = WebSocketServer(
|
||||||
|
host=host,
|
||||||
|
port=port,
|
||||||
|
)
|
||||||
|
self.websocket_server.set_message_handler(self.message_handler)
|
||||||
|
self.websocket_server.start()
|
||||||
|
self.websocket_server_alive = True
|
||||||
|
|
||||||
|
# イベントループが終了するまで待機
|
||||||
|
while self.websocket_server_loop:
|
||||||
|
# self.websocket_server.send("Server is running...")
|
||||||
|
await asyncio.sleep(0.5) # 応答性向上のため間隔短縮
|
||||||
|
|
||||||
|
except Exception:
|
||||||
|
errorLogging()
|
||||||
|
# 具体的なエラー内容をログに残す場合
|
||||||
|
# self.logger.error(f"WebSocket server error: {str(e)}")
|
||||||
|
finally:
|
||||||
|
# 確実にサーバーを停止
|
||||||
|
if hasattr(self, 'websocket_server') and self.websocket_server:
|
||||||
|
self.websocket_server.stop()
|
||||||
|
self.websocket_server_alive = False
|
||||||
|
|
||||||
|
self.th_websocket_server = Thread(target=lambda: asyncio.run(WebSocketServerMain()))
|
||||||
|
self.th_websocket_server.daemon = True
|
||||||
|
self.th_websocket_server.start()
|
||||||
|
|
||||||
|
def stopWebSocketServer(self):
|
||||||
|
"""WebSocketサーバーを停止する"""
|
||||||
|
if not hasattr(self, 'th_websocket_server') or self.th_websocket_server is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.websocket_server_loop = False
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 一定時間待機してからタイムアウト
|
||||||
|
self.th_websocket_server.join(timeout=2.0)
|
||||||
|
|
||||||
|
if self.th_websocket_server.is_alive():
|
||||||
|
# タイムアウト後もスレッドが生きている場合の処理
|
||||||
|
self.logger.warning("WebSocket server thread did not terminate properly")
|
||||||
|
except Exception:
|
||||||
|
errorLogging()
|
||||||
|
finally:
|
||||||
|
self.th_websocket_server = None
|
||||||
|
self.websocket_server = None
|
||||||
|
self.websocket_server_alive = False
|
||||||
|
|
||||||
|
def checkWebSocketServerAlive(self):
|
||||||
|
"""WebSocketサーバーの稼働状態を確認する"""
|
||||||
|
return self.websocket_server_alive
|
||||||
|
|
||||||
|
def websocketSendMessage(self, message_dict:dict):
|
||||||
|
"""
|
||||||
|
WebSocketサーバーから全クライアントにメッセージを送信する
|
||||||
|
:param message_dict: 送信するメッセージの辞書
|
||||||
|
:return: 送信成功したかどうか
|
||||||
|
"""
|
||||||
|
if not self.websocket_server_alive or not self.websocket_server:
|
||||||
|
return False
|
||||||
|
try:
|
||||||
|
message_json = json.dumps(message_dict)
|
||||||
|
return self.websocket_server.send(message_json)
|
||||||
|
except Exception:
|
||||||
|
errorLogging()
|
||||||
|
return False
|
||||||
|
|
||||||
model = Model()
|
model = Model()
|
||||||
@@ -7,10 +7,22 @@ from tinyoscquery.queryservice import OSCQueryService
|
|||||||
from tinyoscquery.query import OSCQueryBrowser, OSCQueryClient
|
from tinyoscquery.query import OSCQueryBrowser, OSCQueryClient
|
||||||
from tinyoscquery.utility import get_open_udp_port, get_open_tcp_port
|
from tinyoscquery.utility import get_open_udp_port, get_open_tcp_port
|
||||||
from tinyoscquery.shared.node import OSCAccess
|
from tinyoscquery.shared.node import OSCAccess
|
||||||
from utils import errorLogging
|
|
||||||
|
try:
|
||||||
|
from utils import errorLogging
|
||||||
|
except ImportError:
|
||||||
|
def errorLogging():
|
||||||
|
import traceback
|
||||||
|
print("Error occurred:", traceback.format_exc())
|
||||||
|
|
||||||
class OSCHandler:
|
class OSCHandler:
|
||||||
def __init__(self, ip_address="127.0.0.1", port=9000) -> None:
|
def __init__(self, ip_address="127.0.0.1", port=9000) -> None:
|
||||||
|
|
||||||
|
if ip_address in ["127.0.0.1", "localhost"]:
|
||||||
|
self.is_osc_query_enabled = True
|
||||||
|
else:
|
||||||
|
self.is_osc_query_enabled = False
|
||||||
|
|
||||||
self.osc_ip_address = ip_address
|
self.osc_ip_address = ip_address
|
||||||
self.osc_port = port
|
self.osc_port = port
|
||||||
self.osc_parameter_muteself = "/avatar/parameters/MuteSelf"
|
self.osc_parameter_muteself = "/avatar/parameters/MuteSelf"
|
||||||
@@ -24,15 +36,28 @@ class OSCHandler:
|
|||||||
self.osc_server_ip_address = ip_address
|
self.osc_server_ip_address = ip_address
|
||||||
self.http_port = None
|
self.http_port = None
|
||||||
self.osc_server_port = None
|
self.osc_server_port = None
|
||||||
|
self.dict_filter_and_target = {}
|
||||||
self.browser = None
|
self.browser = None
|
||||||
|
|
||||||
|
def getIsOscQueryEnabled(self) -> bool:
|
||||||
|
return self.is_osc_query_enabled
|
||||||
|
|
||||||
def setOscIpAddress(self, ip_address:str) -> None:
|
def setOscIpAddress(self, ip_address:str) -> None:
|
||||||
|
if ip_address in ["127.0.0.1", "localhost"]:
|
||||||
|
self.is_osc_query_enabled = True
|
||||||
|
else:
|
||||||
|
self.is_osc_query_enabled = False
|
||||||
|
|
||||||
|
self.oscServerStop()
|
||||||
self.osc_ip_address = ip_address
|
self.osc_ip_address = ip_address
|
||||||
self.udp_client = udp_client.SimpleUDPClient(self.osc_ip_address, self.osc_port)
|
self.udp_client = udp_client.SimpleUDPClient(self.osc_ip_address, self.osc_port)
|
||||||
|
self.receiveOscParameters()
|
||||||
|
|
||||||
def setOscPort(self, port:int) -> None:
|
def setOscPort(self, port:int) -> None:
|
||||||
|
self.oscServerStop()
|
||||||
self.osc_port = port
|
self.osc_port = port
|
||||||
self.udp_client = udp_client.SimpleUDPClient(self.osc_ip_address, self.osc_port)
|
self.udp_client = udp_client.SimpleUDPClient(self.osc_ip_address, self.osc_port)
|
||||||
|
self.receiveOscParameters()
|
||||||
|
|
||||||
# send OSC message typing
|
# send OSC message typing
|
||||||
def sendTyping(self, flag:bool=False) -> None:
|
def sendTyping(self, flag:bool=False) -> None:
|
||||||
@@ -44,6 +69,10 @@ class OSCHandler:
|
|||||||
self.udp_client.send_message(self.osc_parameter_chatbox_input, [f"{message}", True, notification])
|
self.udp_client.send_message(self.osc_parameter_chatbox_input, [f"{message}", True, notification])
|
||||||
|
|
||||||
def getOSCParameterValue(self, address:str) -> Any:
|
def getOSCParameterValue(self, address:str) -> Any:
|
||||||
|
if not self.is_osc_query_enabled:
|
||||||
|
# OSCQueryが無効な場合はNoneを返す
|
||||||
|
return None
|
||||||
|
|
||||||
value = None
|
value = None
|
||||||
try:
|
try:
|
||||||
# browserインスタンスを再利用し、毎回の生成と破棄を避ける
|
# browserインスタンスを再利用し、毎回の生成と破棄を避ける
|
||||||
@@ -71,19 +100,26 @@ class OSCHandler:
|
|||||||
def getOSCParameterMuteSelf(self) -> bool:
|
def getOSCParameterMuteSelf(self) -> bool:
|
||||||
return self.getOSCParameterValue(self.osc_parameter_muteself)
|
return self.getOSCParameterValue(self.osc_parameter_muteself)
|
||||||
|
|
||||||
def receiveOscParameters(self, dict_filter_and_target:dict) -> None:
|
def setDictFilterAndTarget(self, dict_filter_and_target:dict) -> None:
|
||||||
|
self.dict_filter_and_target = dict_filter_and_target
|
||||||
|
|
||||||
|
def receiveOscParameters(self) -> None:
|
||||||
|
if self.is_osc_query_enabled is False:
|
||||||
|
# OSCQueryが無効な場合は何もしない
|
||||||
|
return
|
||||||
|
|
||||||
self.osc_server_port = get_open_udp_port()
|
self.osc_server_port = get_open_udp_port()
|
||||||
self.http_port = get_open_tcp_port()
|
self.http_port = get_open_tcp_port()
|
||||||
osc_dispatcher = dispatcher.Dispatcher()
|
osc_dispatcher = dispatcher.Dispatcher()
|
||||||
for filter, target in dict_filter_and_target.items():
|
for filter, target in self.dict_filter_and_target.items():
|
||||||
osc_dispatcher.map(filter, target)
|
osc_dispatcher.map(filter, target)
|
||||||
self.osc_server = osc_server.ThreadingOSCUDPServer((self.osc_server_ip_address, self.osc_server_port), osc_dispatcher, asyncio.get_event_loop())
|
self.osc_server = osc_server.ThreadingOSCUDPServer((self.osc_server_ip_address, self.osc_server_port), osc_dispatcher)
|
||||||
Thread(target=self.oscServerServe, daemon=True).start()
|
Thread(target=self.oscServerServe, daemon=True).start()
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
self.osc_query_service = OSCQueryService(self.osc_query_service_name, self.http_port, self.osc_server_port)
|
self.osc_query_service = OSCQueryService(self.osc_query_service_name, self.http_port, self.osc_server_port)
|
||||||
for filter, target in dict_filter_and_target.items():
|
for filter, target in self.dict_filter_and_target.items():
|
||||||
self.osc_query_service.advertise_endpoint(filter, access=OSCAccess.READWRITE_VALUE)
|
self.osc_query_service.advertise_endpoint(filter, access=OSCAccess.READWRITE_VALUE)
|
||||||
break
|
break
|
||||||
except Exception:
|
except Exception:
|
||||||
@@ -112,12 +148,26 @@ class OSCHandler:
|
|||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
handler = OSCHandler()
|
handler = OSCHandler()
|
||||||
handler.receiveOscParameters({
|
handler.setDictFilterAndTarget({
|
||||||
"/avatar/parameters/MuteSelf": print,
|
"/avatar/parameters/MuteSelf": lambda address, *args: print(f"Received {address} with args {args}"),
|
||||||
|
"/chatbox/typing": lambda address, *args: print(f"Received {address} with args {args}"),
|
||||||
|
"/chatbox/input": lambda address, *args: print(f"Received {address} with args {args}"),
|
||||||
})
|
})
|
||||||
|
handler.receiveOscParameters()
|
||||||
sleep(5)
|
sleep(5)
|
||||||
handler.sendTyping(True)
|
handler.sendTyping(True)
|
||||||
sleep(1)
|
sleep(1)
|
||||||
handler.sendMessage(message="Hello World", notification=True)
|
handler.sendMessage(message="Hello World 1", notification=True)
|
||||||
sleep(60)
|
sleep(10)
|
||||||
|
|
||||||
|
print("IP address changed to 192.168.193.2")
|
||||||
|
handler.setOscIpAddress("192.168.193.2")
|
||||||
|
sleep(5)
|
||||||
|
handler.sendMessage(message="Hello World 2", notification=True)
|
||||||
|
|
||||||
|
print("IP address changed to 127.0.0.1")
|
||||||
|
handler.setOscIpAddress("127.0.0.1")
|
||||||
|
sleep(5)
|
||||||
|
handler.sendMessage(message="Hello World 3", notification=True)
|
||||||
|
sleep(10)
|
||||||
handler.oscServerStop()
|
handler.oscServerStop()
|
||||||
4
src-python/models/websocket/__init__.py
Normal file
4
src-python/models/websocket/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
# WebSocketサーバーモジュール
|
||||||
|
from .websocket_server import WebSocketServer
|
||||||
|
|
||||||
|
__all__ = ["WebSocketServer"]
|
||||||
221
src-python/models/websocket/websocket_server.py
Normal file
221
src-python/models/websocket/websocket_server.py
Normal file
@@ -0,0 +1,221 @@
|
|||||||
|
import asyncio
|
||||||
|
import threading
|
||||||
|
import websockets
|
||||||
|
from websockets.legacy.server import WebSocketServerProtocol
|
||||||
|
from typing import Callable, Set, Optional
|
||||||
|
|
||||||
|
class WebSocketServer:
|
||||||
|
"""
|
||||||
|
WebSocketサーバーを管理するクラス。
|
||||||
|
主な機能:
|
||||||
|
- サーバーの起動・停止
|
||||||
|
- クライアント接続管理 (接続/切断の追跡)
|
||||||
|
- メッセージ受信のコールバック処理
|
||||||
|
- メッセージのブロードキャスト機能
|
||||||
|
- GUIスレッド等からメッセージ送信するためのキュー
|
||||||
|
"""
|
||||||
|
def __init__(self, host: str='127.0.0.1', port: int=8765):
|
||||||
|
"""
|
||||||
|
サーバーのホスト名とポートを指定して初期化します。
|
||||||
|
"""
|
||||||
|
self.host = host
|
||||||
|
self.port = port
|
||||||
|
self.clients: Set[WebSocketServerProtocol] = set() # 接続クライアント集合
|
||||||
|
self._message_handler: Optional[Callable[['WebSocketServer', WebSocketServerProtocol, str], None]] = None
|
||||||
|
self._loop: Optional[asyncio.AbstractEventLoop] = None
|
||||||
|
self._server: Optional[websockets.serve] = None
|
||||||
|
self._thread: Optional[threading.Thread] = None
|
||||||
|
self._send_queue: Optional[asyncio.Queue] = None # 外部スレッド向け非同期キュー
|
||||||
|
self.is_running: bool = False # サーバーの起動状態を示すフラグ
|
||||||
|
|
||||||
|
def set_message_handler(self, handler: Callable[['WebSocketServer', WebSocketServerProtocol, str], None]):
|
||||||
|
"""
|
||||||
|
クライアントからメッセージ受信時に呼び出すコールバックを設定します。
|
||||||
|
コールバックのシグネチャ: (server, websocket, message) -> None
|
||||||
|
"""
|
||||||
|
self._message_handler = handler
|
||||||
|
|
||||||
|
async def _handler(self, websocket):
|
||||||
|
"""
|
||||||
|
単一クライアントとのセッションを処理するハンドラです。
|
||||||
|
新規接続時にクライアントを集合に追加し、メッセージを受信してコールバックを呼び出します。
|
||||||
|
切断時には集合からクライアントを削除します。
|
||||||
|
"""
|
||||||
|
# 接続クライアントを集合に追加
|
||||||
|
self.clients.add(websocket)
|
||||||
|
try:
|
||||||
|
async for message in websocket:
|
||||||
|
# メッセージ受信時にコールバック呼び出し
|
||||||
|
if self._message_handler:
|
||||||
|
self._message_handler(self, websocket, message)
|
||||||
|
except websockets.exceptions.ConnectionClosed:
|
||||||
|
# クライアントが切断した場合
|
||||||
|
pass
|
||||||
|
finally:
|
||||||
|
# 切断時に集合から削除
|
||||||
|
self.clients.remove(websocket)
|
||||||
|
|
||||||
|
async def _broadcast_async(self, message: str):
|
||||||
|
"""
|
||||||
|
すべての接続クライアントにメッセージを送信する非同期メソッド。
|
||||||
|
"""
|
||||||
|
if not self.clients:
|
||||||
|
return
|
||||||
|
# 全クライアントへ並列に送信
|
||||||
|
await asyncio.gather(
|
||||||
|
*[client.send(message) for client in self.clients],
|
||||||
|
return_exceptions=True
|
||||||
|
)
|
||||||
|
|
||||||
|
async def _send_loop(self):
|
||||||
|
"""
|
||||||
|
内部キューからメッセージを取り出し、すべてのクライアントに送信するループ処理。
|
||||||
|
GUIなど他スレッドから送信メッセージをキューに入れてもらい、このコルーチンで配信します。
|
||||||
|
"""
|
||||||
|
assert self._send_queue is not None
|
||||||
|
while True:
|
||||||
|
message = await self._send_queue.get()
|
||||||
|
if message is None:
|
||||||
|
# Noneを受け取ったらシャットダウン指示とみなしてループを抜ける
|
||||||
|
break
|
||||||
|
await self._broadcast_async(message)
|
||||||
|
|
||||||
|
def send(self, message: str):
|
||||||
|
"""
|
||||||
|
外部スレッドからサーバーにメッセージを送信するためのメソッドです。
|
||||||
|
イベントループ上で安全にキューにメッセージを積み、_send_loop()経由でブロードキャストします。
|
||||||
|
"""
|
||||||
|
if self._loop and self._send_queue:
|
||||||
|
# キューにput_nowaitするコールをイベントループにスケジュール
|
||||||
|
self._loop.call_soon_threadsafe(self._send_queue.put_nowait, message)
|
||||||
|
|
||||||
|
def broadcast(self, message: str):
|
||||||
|
"""
|
||||||
|
外部スレッドや他コルーチンから全クライアントにメッセージを送信するユーティリティ。
|
||||||
|
asyncio.run_coroutine_threadsafe を使ってループ上でブロードキャストを実行します。
|
||||||
|
"""
|
||||||
|
if self._loop:
|
||||||
|
# コルーチン自体をrun_coroutine_threadsafeに渡す
|
||||||
|
asyncio.run_coroutine_threadsafe(
|
||||||
|
self._broadcast_async(message), self._loop
|
||||||
|
)
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
"""
|
||||||
|
サーバーを起動します。新しいスレッド上で asyncio イベントループを動かし、serve()を実行します。
|
||||||
|
"""
|
||||||
|
if self._thread and self._thread.is_alive():
|
||||||
|
return # 既に起動中
|
||||||
|
# 新しいスレッドでイベントループを開始
|
||||||
|
self._thread = threading.Thread(target=self._run_loop, daemon=True)
|
||||||
|
self._thread.start()
|
||||||
|
|
||||||
|
def _run_loop(self):
|
||||||
|
"""
|
||||||
|
別スレッド上で実行されるイベントループ用のメソッド。
|
||||||
|
サーバーの起動と、送信用キューのタスク登録を行います。
|
||||||
|
"""
|
||||||
|
# 新しいイベントループを作成してこのスレッドの現在のループとして設定
|
||||||
|
self._loop = asyncio.new_event_loop()
|
||||||
|
asyncio.set_event_loop(self._loop)
|
||||||
|
|
||||||
|
async def setup_server():
|
||||||
|
# サーバーを起動し、listenを開始
|
||||||
|
self._server = await websockets.serve(self._handler, self.host, self.port)
|
||||||
|
# 送信キューを初期化
|
||||||
|
self._send_queue = asyncio.Queue()
|
||||||
|
# 送信ループタスクを開始
|
||||||
|
self._loop.create_task(self._send_loop())
|
||||||
|
# サーバーの起動を待機
|
||||||
|
# 設定関数を実行してサーバーを起動
|
||||||
|
self._loop.run_until_complete(setup_server())
|
||||||
|
self.is_running = True
|
||||||
|
# サーバーが起動したら、接続待機を開始
|
||||||
|
# print(f"WebSocket server started on ws://{self.host}:{self.port}")
|
||||||
|
try:
|
||||||
|
# サーバーが停止するまでループを継続
|
||||||
|
self._loop.run_forever()
|
||||||
|
finally:
|
||||||
|
# 停止指示が出たらすべての接続を閉じ、イベントループを終了
|
||||||
|
self._loop.run_until_complete(self._shutdown())
|
||||||
|
self._loop.close()
|
||||||
|
|
||||||
|
async def _shutdown(self):
|
||||||
|
"""
|
||||||
|
サーバーとクライアントを安全にシャットダウンする非同期処理。
|
||||||
|
serveオブジェクトをcloseし、wait_closed()で完全に終了を待ちます。
|
||||||
|
さらに接続中の各WebSocketをcloseします。
|
||||||
|
"""
|
||||||
|
# サーバーのListenを停止
|
||||||
|
if self._server:
|
||||||
|
self._server.close()
|
||||||
|
await self._server.wait_closed()
|
||||||
|
# 接続中クライアントを順次クローズ
|
||||||
|
for ws in list(self.clients):
|
||||||
|
try:
|
||||||
|
await ws.close()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
"""
|
||||||
|
サーバーを停止します。別スレッドで動作中のイベントループに停止を指示し、スレッドを終了させます。
|
||||||
|
"""
|
||||||
|
self.is_running = False
|
||||||
|
if self._loop:
|
||||||
|
# サーバーのlistenを停止し、ループ停止をスケジュール
|
||||||
|
self._loop.call_soon_threadsafe(self._server.close)
|
||||||
|
# None をキューに入れて_send_loopを抜けさせる
|
||||||
|
self._loop.call_soon_threadsafe(self._send_queue.put_nowait, None)
|
||||||
|
# ループ停止
|
||||||
|
self._loop.call_soon_threadsafe(self._loop.stop)
|
||||||
|
# スレッドの終了を待つ
|
||||||
|
if self._thread:
|
||||||
|
self._thread.join()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
# テスト用の簡単なメッセージハンドラ
|
||||||
|
def message_handler(server: WebSocketServer, websocket: WebSocketServerProtocol, message: str):
|
||||||
|
print(f"Received message from {websocket.remote_address}: {message}")
|
||||||
|
server.send(f"Echo: {message}")
|
||||||
|
|
||||||
|
def send_message(server: WebSocketServer, message: str):
|
||||||
|
server.send(message)
|
||||||
|
|
||||||
|
# メイン処理を非同期関数に変更
|
||||||
|
async def main():
|
||||||
|
# サーバーを起動してメッセージハンドラを設定
|
||||||
|
ws_server = WebSocketServer()
|
||||||
|
ws_server.set_message_handler(message_handler)
|
||||||
|
ws_server.start()
|
||||||
|
print("WebSocket server started.")
|
||||||
|
# 定期的にサーバーからメッセージを送信する例
|
||||||
|
import threading
|
||||||
|
import time
|
||||||
|
def periodic_send():
|
||||||
|
print("Starting periodic message sender...")
|
||||||
|
while ws_server.is_running:
|
||||||
|
time.sleep(5)
|
||||||
|
print("Sending periodic message...")
|
||||||
|
send_message(ws_server, "Periodic message")
|
||||||
|
print("Periodic message sender stopped.")
|
||||||
|
# 別スレッドで定期的にメッセージを送信
|
||||||
|
time.sleep(5)
|
||||||
|
send_thread = threading.Thread(target=periodic_send, daemon=True)
|
||||||
|
send_thread.start()
|
||||||
|
# メインスレッドでサーバーを動かし続ける
|
||||||
|
try:
|
||||||
|
while True:
|
||||||
|
# 非同期スリープで待機
|
||||||
|
await asyncio.sleep(1)
|
||||||
|
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
# Ctrl+Cでサーバーを停止
|
||||||
|
print("Stopping WebSocket server...")
|
||||||
|
ws_server.stop()
|
||||||
|
|
||||||
|
# 非同期メイン関数を実行
|
||||||
|
try:
|
||||||
|
asyncio.run(main())
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print("Stopping WebSocket server...")
|
||||||
@@ -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)
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
--error_bc_active_color: #9c3938;
|
--error_bc_active_color: #9c3938;
|
||||||
--success_bc_color: #368777;
|
--success_bc_color: #368777;
|
||||||
--waring_color: #cb944f;
|
--waring_color: #cb944f;
|
||||||
|
--waring_bc_color: #cf7b1b;
|
||||||
|
|
||||||
--dark_basic_text_color: #f2f2f2;
|
--dark_basic_text_color: #f2f2f2;
|
||||||
--dark_100_color: #f5f7fb;
|
--dark_100_color: #f5f7fb;
|
||||||
|
|||||||
@@ -4,44 +4,107 @@ import MUI_Slider from "@mui/material/Slider";
|
|||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
|
|
||||||
export const Slider = (props) => {
|
export const Slider = (props) => {
|
||||||
|
const location = props.valueLabelDisplayLocation || "top";
|
||||||
|
|
||||||
|
const sliderSx = {
|
||||||
|
color: "var(--dark_700_color)",
|
||||||
|
"& .MuiSlider-thumb": {
|
||||||
|
backgroundColor: "var(--primary_600_color)",
|
||||||
|
"&:hover, &.Mui-focusVisible, &.Mui-active": {
|
||||||
|
boxShadow: `0 0 0 0.8rem var(--primary_600_color_44)`,
|
||||||
|
},
|
||||||
|
"& .MuiSlider-valueLabel": {
|
||||||
|
position: "absolute",
|
||||||
|
backgroundColor: "var(--dark_800_color)",
|
||||||
|
width: "fit-content",
|
||||||
|
minWidth: "4.8rem",
|
||||||
|
padding: "0.4rem 0.8rem",
|
||||||
|
lineHeight: "1.15",
|
||||||
|
"& .MuiSlider-valueLabelLabel": {
|
||||||
|
fontSize: "1.4rem",
|
||||||
|
},
|
||||||
|
...(location === "top" && {
|
||||||
|
top: "-110%",
|
||||||
|
left: "50%",
|
||||||
|
transform: "translate(-50%, -50%) scale(0)",
|
||||||
|
transformOrigin: "bottom center",
|
||||||
|
"&.MuiSlider-valueLabelOpen": {
|
||||||
|
transform: "translate(-50%, -50%) scale(1)",
|
||||||
|
},
|
||||||
|
"&::before": {
|
||||||
|
bottom: "0%",
|
||||||
|
left: "50%",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
...(location === "right" && {
|
||||||
|
top: "50%",
|
||||||
|
left: "150%",
|
||||||
|
transform: "translate(0, -50%) scale(0)",
|
||||||
|
transformOrigin: "left center",
|
||||||
|
"&.MuiSlider-valueLabelOpen": {
|
||||||
|
transform: "translate(0, -50%) scale(1)",
|
||||||
|
},
|
||||||
|
"&::before": {
|
||||||
|
bottom: "50%",
|
||||||
|
left: "0",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
...(location === "left" && {
|
||||||
|
// top: "50%",
|
||||||
|
// right: "50%",
|
||||||
|
// transform: "translate(-50%, -50%) scale(0)",
|
||||||
|
// transformOrigin: "bottom center",
|
||||||
|
// "&.MuiSlider-valueLabelOpen": {
|
||||||
|
// transform: "translate(-50%, -50%) scale(1)",
|
||||||
|
// },
|
||||||
|
// "&::before": {
|
||||||
|
// bottom: "50%",
|
||||||
|
// left: "100%",
|
||||||
|
// },
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"& .MuiSlider-markLabel": {
|
||||||
|
fontSize: "1.4rem",
|
||||||
|
color: "var(--dark_550_color)",
|
||||||
|
whiteSpace: "nowrap",
|
||||||
|
},
|
||||||
|
"& .MuiSlider-markLabelActive": {
|
||||||
|
color: "var(--primary_300_color)",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={clsx(styles.container, props.className, {[styles.no_padding]: props.no_padding || props.is_break_point})}>
|
<div
|
||||||
|
className={clsx(
|
||||||
|
styles.container,
|
||||||
|
props.className,
|
||||||
|
{ [styles.no_padding]: props.no_padding || props.is_break_point }
|
||||||
|
)}
|
||||||
|
>
|
||||||
<MUI_Slider
|
<MUI_Slider
|
||||||
aria-label="Default"
|
aria-label="Default"
|
||||||
valueLabelDisplay="auto"
|
// valueLabelDisplay="on"
|
||||||
|
valueLabelDisplay={props.valueLabelDisplay ? props.valueLabelDisplay : "auto"}
|
||||||
value={props.variable}
|
value={props.variable}
|
||||||
step={props.step}
|
step={props.step}
|
||||||
min={Number(props.min)}
|
min={Number(props.min)}
|
||||||
max={Number(props.max)}
|
max={Number(props.max)}
|
||||||
onChange={(_e, value) => props.onchangeFunction(value)}
|
onChange={(_e, value) => props.onchangeFunction(value)}
|
||||||
onChangeCommitted={(_e, value) => props.onchangeCommittedFunction ? props.onchangeCommittedFunction(value) : null}
|
onChangeCommitted={(_e, value) =>
|
||||||
|
props.onchangeCommittedFunction ? props.onchangeCommittedFunction(value) : null
|
||||||
|
}
|
||||||
|
onMouseEnter={(event) =>
|
||||||
|
props.onMouseEnterFunction ? props.onMouseEnterFunction(event) : null
|
||||||
|
}
|
||||||
|
onMouseLeave={(event) =>
|
||||||
|
props.onMouseLeaveFunction ? props.onMouseLeaveFunction(event) : null
|
||||||
|
}
|
||||||
marks={props.marks}
|
marks={props.marks}
|
||||||
track={props.track}
|
track={props.track}
|
||||||
orientation={props.orientation}
|
orientation={props.orientation}
|
||||||
valueLabelFormat={`${props.valueLabelFormat ? props.valueLabelFormat : props.variable}`}
|
valueLabelFormat={`${props.valueLabelFormat ? props.valueLabelFormat : props.variable}`}
|
||||||
sx={{
|
sx={sliderSx}
|
||||||
color: "var(--dark_700_color)",
|
|
||||||
"& .MuiSlider-thumb": {
|
|
||||||
backgroundColor: "var(--primary_600_color)",
|
|
||||||
"&:hover, &.Mui-focusVisible, &.Mui-active": {
|
|
||||||
boxShadow: `0 0 0 0.8rem var(--primary_600_color_44)`,
|
|
||||||
},
|
|
||||||
"& .MuiSlider-valueLabel": {
|
|
||||||
fontSize: "1.4rem",
|
|
||||||
backgroundColor: "var(--dark_800_color)",
|
|
||||||
padding: "0.6rem 1rem",
|
|
||||||
lineHeight: "1.15",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"& .MuiSlider-markLabel": {
|
|
||||||
fontSize: "1.4rem",
|
|
||||||
color: "var(--dark_550_color)",
|
|
||||||
whiteSpace: "nowrap",
|
|
||||||
},
|
|
||||||
"& .MuiSlider-markLabelActive": {
|
|
||||||
color: "var(--primary_300_color)",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -6,13 +6,19 @@ import { useOpenFolder } from "@logics_common";
|
|||||||
import {
|
import {
|
||||||
useOscIpAddress,
|
useOscIpAddress,
|
||||||
useOscPort,
|
useOscPort,
|
||||||
|
useWebsocket,
|
||||||
} from "@logics_configs";
|
} from "@logics_configs";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
CheckboxContainer,
|
||||||
ActionButtonContainer,
|
ActionButtonContainer,
|
||||||
EntryWithSaveButtonContainer,
|
EntryWithSaveButtonContainer,
|
||||||
} from "../_templates/Templates";
|
} from "../_templates/Templates";
|
||||||
|
|
||||||
|
import {
|
||||||
|
SectionLabelComponent,
|
||||||
|
} from "../_components/";
|
||||||
|
|
||||||
import OpenFolderSvg from "@images/open_folder.svg?react";
|
import OpenFolderSvg from "@images/open_folder.svg?react";
|
||||||
import HelpSvg from "@images/help.svg?react";
|
import HelpSvg from "@images/help.svg?react";
|
||||||
|
|
||||||
@@ -25,6 +31,7 @@ export const AdvancedSettings = () => {
|
|||||||
<OpenConfigFolderContainer />
|
<OpenConfigFolderContainer />
|
||||||
<OpenSwitchComputeDeviceModalContainer />
|
<OpenSwitchComputeDeviceModalContainer />
|
||||||
</div>
|
</div>
|
||||||
|
<WebsocketContainer />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -87,6 +94,7 @@ const OscPortContainer = () => {
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const OpenConfigFolderContainer = () => {
|
const OpenConfigFolderContainer = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { openFolder_ConfigFile } = useOpenFolder();
|
const { openFolder_ConfigFile } = useOpenFolder();
|
||||||
@@ -121,3 +129,87 @@ const OpenSwitchComputeDeviceModalContainer = () => {
|
|||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const WebsocketContainer = () => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<SectionLabelComponent label="WebSocket" />
|
||||||
|
<EnableWebsocketContainer />
|
||||||
|
<WebsocketHostContainer />
|
||||||
|
<WebsocketPortContainer />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const EnableWebsocketContainer = () => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const { currentEnableWebsocket, toggleEnableWebsocket } = useWebsocket();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<CheckboxContainer
|
||||||
|
label={t("config_page.advanced_settings.enable_websocket.label")}
|
||||||
|
variable={currentEnableWebsocket}
|
||||||
|
toggleFunction={toggleEnableWebsocket}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const WebsocketHostContainer = () => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const { currentWebsocketHost, setWebsocketHost } = useWebsocket();
|
||||||
|
const [input_value, setInputValue] = useState(currentWebsocketHost.data);
|
||||||
|
|
||||||
|
const onChangeFunction = (value) => {
|
||||||
|
setInputValue(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const saveFunction = () => {
|
||||||
|
setWebsocketHost(input_value);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(()=> {
|
||||||
|
setInputValue(currentWebsocketHost.data);
|
||||||
|
}, [currentWebsocketHost]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<EntryWithSaveButtonContainer
|
||||||
|
label={t("config_page.advanced_settings.websocket_host.label")}
|
||||||
|
variable={input_value}
|
||||||
|
saveFunction={saveFunction}
|
||||||
|
onChangeFunction={onChangeFunction}
|
||||||
|
state={currentWebsocketHost.state}
|
||||||
|
width="14rem"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const WebsocketPortContainer = () => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const { currentWebsocketPort, setWebsocketPort } = useWebsocket();
|
||||||
|
const [input_value, setInputValue] = useState(currentWebsocketPort.data);
|
||||||
|
|
||||||
|
const onChangeFunction = (value) => {
|
||||||
|
value = value.replace(/[^0-9]/g, "");
|
||||||
|
setInputValue(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const saveFunction = () => {
|
||||||
|
setWebsocketPort(input_value);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(()=> {
|
||||||
|
setInputValue(currentWebsocketPort.data);
|
||||||
|
}, [currentWebsocketPort]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<EntryWithSaveButtonContainer
|
||||||
|
label={t("config_page.advanced_settings.websocket_port.label")}
|
||||||
|
variable={input_value}
|
||||||
|
saveFunction={saveFunction}
|
||||||
|
onChangeFunction={onChangeFunction}
|
||||||
|
state={currentWebsocketPort.state}
|
||||||
|
width="10rem"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -101,11 +101,17 @@ export const VrcMicMuteSyncContainer = () => {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { currentEnableVrcMicMuteSync, toggleEnableVrcMicMuteSync } = useEnableVrcMicMuteSync();
|
const { currentEnableVrcMicMuteSync, toggleEnableVrcMicMuteSync } = useEnableVrcMicMuteSync();
|
||||||
|
|
||||||
|
const variable = {
|
||||||
|
state: currentEnableVrcMicMuteSync.state,
|
||||||
|
data: currentEnableVrcMicMuteSync.data.is_enabled,
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CheckboxContainer
|
<CheckboxContainer
|
||||||
label={t("config_page.others.vrc_mic_mute_sync.label")}
|
label={t("config_page.others.vrc_mic_mute_sync.label")}
|
||||||
desc={t("config_page.others.vrc_mic_mute_sync.desc")}
|
desc={t("config_page.others.vrc_mic_mute_sync.desc")}
|
||||||
variable={currentEnableVrcMicMuteSync}
|
variable={variable}
|
||||||
|
is_available={currentEnableVrcMicMuteSync.data.is_available}
|
||||||
toggleFunction={toggleEnableVrcMicMuteSync}
|
toggleFunction={toggleEnableVrcMicMuteSync}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -272,30 +272,32 @@ const SupporterPeriodContainer = ({ settings, calc_support_period }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.supporter_period_container}>
|
<div className={styles.supporter_period_container}>
|
||||||
{Object.entries(period_data).map(([key, item], index) => {
|
<div className={styles.supporter_period_wrapper}>
|
||||||
if (item === "") return null;
|
{Object.entries(period_data).map(([key, item], index) => {
|
||||||
const period_box_class_name = clsx(styles.period_box, {
|
if (item === "") return null;
|
||||||
[styles.mogu_bar]: item === "mogu_2000",
|
const period_box_class_name = clsx(styles.period_box, {
|
||||||
[styles.mochi_bar]: item === "mochi_1000",
|
[styles.mogu_bar]: item === "mogu_2000",
|
||||||
[styles.fuwa_bar]: item === "fuwa_500",
|
[styles.mochi_bar]: item === "mochi_1000",
|
||||||
[styles.basic_bar]: item === "basic_300",
|
[styles.fuwa_bar]: item === "fuwa_500",
|
||||||
});
|
[styles.basic_bar]: item === "basic_300",
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
key={index}
|
key={index}
|
||||||
title={
|
title={
|
||||||
<p className={styles.tooltip_period_label}>{key}</p>
|
<p className={styles.tooltip_period_label}>{key}</p>
|
||||||
}
|
}
|
||||||
placement="top"
|
placement="top"
|
||||||
slotProps={offset}
|
slotProps={offset}
|
||||||
>
|
>
|
||||||
<div className={styles.period_box_wrapper}>
|
<div className={styles.period_box_wrapper}>
|
||||||
<div className={period_box_class_name}></div>
|
<div className={period_box_class_name}></div>
|
||||||
</div>
|
</div>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
align-content: start;
|
align-content: start;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
column-gap: 1.8rem;
|
column-gap: 1.8rem;
|
||||||
row-gap: 0.4rem;
|
row-gap: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.supporter_image_container {
|
.supporter_image_container {
|
||||||
@@ -209,13 +209,24 @@ $progress_ease: cubic-bezier(0, 1, 0.75, 1);
|
|||||||
|
|
||||||
|
|
||||||
.supporter_period_container {
|
.supporter_period_container {
|
||||||
|
position: absolute;
|
||||||
|
top: 100%;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
.supporter_period_wrapper {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 0.2rem;
|
gap: 0.4rem 0.2rem;
|
||||||
padding-left: 0.4rem;
|
flex-shrink: 0;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
padding: 0.3rem 0.4rem 0.4rem 0.4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.period_box_wrapper {
|
||||||
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.period_box {
|
.period_box {
|
||||||
width: 1.8rem;
|
width: 1.7rem;
|
||||||
height: 0.3rem;
|
height: 0.3rem;
|
||||||
border-radius: 0.3rem;
|
border-radius: 0.3rem;
|
||||||
&.mogu_bar {
|
&.mogu_bar {
|
||||||
@@ -231,9 +242,7 @@ $progress_ease: cubic-bezier(0, 1, 0.75, 1);
|
|||||||
background-color: var(--dark_800_color);
|
background-color: var(--dark_800_color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.period_box_wrapper {
|
|
||||||
padding: 0.3rem 0 0.4rem 0;
|
|
||||||
}
|
|
||||||
.tooltip_period_label {
|
.tooltip_period_label {
|
||||||
font-size: 1.4rem;
|
font-size: 1.4rem;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect, useRef } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
import styles from "./Vr.module.scss";
|
import styles from "./Vr.module.scss";
|
||||||
@@ -25,6 +25,10 @@ import {
|
|||||||
|
|
||||||
import RedoSvg from "@images/redo.svg?react";
|
import RedoSvg from "@images/redo.svg?react";
|
||||||
|
|
||||||
|
import SquareSvg from "@images/square.svg?react";
|
||||||
|
import TriangleSvg from "@images/triangle.svg?react";
|
||||||
|
import { randomIntMinMax } from "@utils";
|
||||||
|
|
||||||
export const Vr = () => {
|
export const Vr = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [is_opened_small_settings, setIsOpenedSmallSettings] = useState(true);
|
const [is_opened_small_settings, setIsOpenedSmallSettings] = useState(true);
|
||||||
@@ -147,7 +151,7 @@ const OverlaySettingsContainer = ({
|
|||||||
) : (
|
) : (
|
||||||
<RotationControls settings={settings} onchangeFunction={onchangeFunction} ui_configs={ui_configs} default_ui_configs={default_ui_configs} selectFunction={selectFunction}/>
|
<RotationControls settings={settings} onchangeFunction={onchangeFunction} ui_configs={ui_configs} default_ui_configs={default_ui_configs} selectFunction={selectFunction}/>
|
||||||
)}
|
)}
|
||||||
<SendSampleTextToggleButton />
|
<SendSampleTextToggleButton />
|
||||||
</div>
|
</div>
|
||||||
<OtherControls settings={settings} onchangeFunction={onchangeFunction} ui_configs={ui_configs} />
|
<OtherControls settings={settings} onchangeFunction={onchangeFunction} ui_configs={ui_configs} />
|
||||||
<RadioButtonContainer
|
<RadioButtonContainer
|
||||||
@@ -187,9 +191,34 @@ const PageSwitcherContainer = (props) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const PositionControls = ({settings, onchangeFunction, selectFunction, ui_configs, default_ui_configs}) => {
|
|
||||||
|
export const PositionControls = ({ settings, onchangeFunction, selectFunction, ui_configs, default_ui_configs }) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const {
|
||||||
|
variable_display: x_variable_display,
|
||||||
|
is_max: is_max_position_x,
|
||||||
|
is_min: is_min_position_x,
|
||||||
|
countUp: countUpPositionX,
|
||||||
|
countDown: countDownPositionX,
|
||||||
|
} = useVariableControl("x_pos", settings, onchangeFunction, ui_configs);
|
||||||
|
|
||||||
|
const {
|
||||||
|
variable_display: y_variable_display,
|
||||||
|
is_max: is_max_position_y,
|
||||||
|
is_min: is_min_position_y,
|
||||||
|
countUp: countUpPositionY,
|
||||||
|
countDown: countDownPositionY,
|
||||||
|
} = useVariableControl("y_pos", settings, onchangeFunction, ui_configs);
|
||||||
|
|
||||||
|
const {
|
||||||
|
variable_display: z_variable_display,
|
||||||
|
is_max: is_max_position_z,
|
||||||
|
is_min: is_min_position_z,
|
||||||
|
countUp: countUpPositionZ,
|
||||||
|
countDown: countDownPositionZ,
|
||||||
|
} = useVariableControl("z_pos", settings, onchangeFunction, ui_configs);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.position_controls}>
|
<div className={styles.position_controls}>
|
||||||
<div className={styles.position_wrapper}>
|
<div className={styles.position_wrapper}>
|
||||||
@@ -205,8 +234,18 @@ const PositionControls = ({settings, onchangeFunction, selectFunction, ui_config
|
|||||||
min={ui_configs.x_pos.min}
|
min={ui_configs.x_pos.min}
|
||||||
max={ui_configs.x_pos.max}
|
max={ui_configs.x_pos.max}
|
||||||
onchangeFunction={(value) => onchangeFunction("x_pos", value)}
|
onchangeFunction={(value) => onchangeFunction("x_pos", value)}
|
||||||
|
valueLabelDisplay={x_variable_display}
|
||||||
|
valueLabelDisplayLocation="top"
|
||||||
|
/>
|
||||||
|
<AdjustButtonContainer
|
||||||
|
wrapper_class_name={styles.x_position_button_wrapper}
|
||||||
|
is_max={is_max_position_x}
|
||||||
|
is_min={is_min_position_x}
|
||||||
|
countUp={countUpPositionX}
|
||||||
|
countDown={countDownPositionX}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.position_wrapper}>
|
<div className={styles.position_wrapper}>
|
||||||
<p className={clsx(styles.slider_label, styles.y_position_label)}>
|
<p className={clsx(styles.slider_label, styles.y_position_label)}>
|
||||||
{t("config_page.vr.y_position")}
|
{t("config_page.vr.y_position")}
|
||||||
@@ -221,8 +260,18 @@ const PositionControls = ({settings, onchangeFunction, selectFunction, ui_config
|
|||||||
max={ui_configs.y_pos.max}
|
max={ui_configs.y_pos.max}
|
||||||
onchangeFunction={(value) => onchangeFunction("y_pos", value)}
|
onchangeFunction={(value) => onchangeFunction("y_pos", value)}
|
||||||
orientation="vertical"
|
orientation="vertical"
|
||||||
|
valueLabelDisplay={y_variable_display}
|
||||||
|
valueLabelDisplayLocation="right"
|
||||||
|
/>
|
||||||
|
<AdjustButtonContainer
|
||||||
|
wrapper_class_name={styles.y_position_button_wrapper}
|
||||||
|
is_max={is_max_position_y}
|
||||||
|
is_min={is_min_position_y}
|
||||||
|
countUp={countUpPositionY}
|
||||||
|
countDown={countDownPositionY}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.position_wrapper}>
|
<div className={styles.position_wrapper}>
|
||||||
<p className={clsx(styles.slider_label, styles.z_position_label)}>
|
<p className={clsx(styles.slider_label, styles.z_position_label)}>
|
||||||
{t("config_page.vr.z_position")}
|
{t("config_page.vr.z_position")}
|
||||||
@@ -237,69 +286,162 @@ const PositionControls = ({settings, onchangeFunction, selectFunction, ui_config
|
|||||||
max={ui_configs.z_pos.max}
|
max={ui_configs.z_pos.max}
|
||||||
onchangeFunction={(value) => onchangeFunction("z_pos", value)}
|
onchangeFunction={(value) => onchangeFunction("z_pos", value)}
|
||||||
orientation="vertical"
|
orientation="vertical"
|
||||||
|
valueLabelDisplay={z_variable_display}
|
||||||
|
valueLabelDisplayLocation="left"
|
||||||
|
/>
|
||||||
|
<AdjustButtonContainer
|
||||||
|
wrapper_class_name={styles.z_position_button_wrapper}
|
||||||
|
is_max={is_max_position_z}
|
||||||
|
is_min={is_min_position_z}
|
||||||
|
countUp={countUpPositionZ}
|
||||||
|
countDown={countDownPositionZ}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const RotationControls = ({settings, onchangeFunction, selectFunction, default_ui_configs}) => {
|
export const RotationControls = ({ settings, onchangeFunction, selectFunction, ui_configs, default_ui_configs }) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const {
|
||||||
|
variable_display: x_variable_display,
|
||||||
|
is_max: is_max_rotation_x,
|
||||||
|
is_min: is_min_rotation_x,
|
||||||
|
countUp: countUpRotationX,
|
||||||
|
countDown: countDownRotationX,
|
||||||
|
} = useVariableControl("x_rotation", settings, onchangeFunction, ui_configs);
|
||||||
|
|
||||||
|
const {
|
||||||
|
variable_display: y_variable_display,
|
||||||
|
is_max: is_max_rotation_y,
|
||||||
|
is_min: is_min_rotation_y,
|
||||||
|
countUp: countUpRotationY,
|
||||||
|
countDown: countDownRotationY,
|
||||||
|
} = useVariableControl("y_rotation", settings, onchangeFunction, ui_configs);
|
||||||
|
|
||||||
|
const {
|
||||||
|
variable_display: z_variable_display,
|
||||||
|
is_max: is_max_rotation_z,
|
||||||
|
is_min: is_min_rotation_z,
|
||||||
|
countUp: countUpRotationZ,
|
||||||
|
countDown: countDownRotationZ,
|
||||||
|
} = useVariableControl("z_rotation", settings, onchangeFunction, ui_configs);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.rotation_controls}>
|
<div className={styles.rotation_controls}>
|
||||||
<div className={styles.rotation_wrapper}>
|
<div className={styles.rotation_wrapper}>
|
||||||
<p className={clsx(styles.slider_label, styles.x_rotation_label)}>
|
<p className={clsx(styles.slider_label, styles.x_rotation_label)}>
|
||||||
{t("config_page.vr.x_rotation")}
|
{t("config_page.vr.x_rotation")}
|
||||||
<ResetButton onClickFunction={() => selectFunction("x_rotation", default_ui_configs.y_pos)} />
|
<ResetButton onClickFunction={() => selectFunction("x_rotation", default_ui_configs.x_rotation)} />
|
||||||
</p>
|
</p>
|
||||||
<Slider
|
<Slider
|
||||||
className={styles.x_rotation_slider}
|
className={styles.x_rotation_slider}
|
||||||
no_padding={true}
|
no_padding={true}
|
||||||
variable={-settings.x_rotation}
|
variable={-settings.x_rotation}
|
||||||
valueLabelFormat={settings.x_rotation}
|
valueLabelFormat={settings.x_rotation}
|
||||||
step={5}
|
step={ui_configs.x_rotation.step}
|
||||||
min={-180}
|
min={ui_configs.x_rotation.min}
|
||||||
max={180}
|
max={ui_configs.x_rotation.max}
|
||||||
onchangeFunction={(value) => onchangeFunction("x_rotation", -value)}
|
onchangeFunction={(value) => onchangeFunction("x_rotation", -value)}
|
||||||
orientation="vertical"
|
orientation="vertical"
|
||||||
|
valueLabelDisplay={x_variable_display}
|
||||||
|
valueLabelDisplayLocation="right"
|
||||||
|
/>
|
||||||
|
<AdjustButtonContainer
|
||||||
|
wrapper_class_name={styles.x_rotation_button_wrapper}
|
||||||
|
is_max={is_min_rotation_x}
|
||||||
|
is_min={is_max_rotation_x}
|
||||||
|
countUp={countDownRotationX}
|
||||||
|
countDown={countUpRotationX}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.rotation_wrapper}>
|
<div className={styles.rotation_wrapper}>
|
||||||
<p className={clsx(styles.slider_label, styles.y_rotation_label)}>
|
<p className={clsx(styles.slider_label, styles.y_rotation_label)}>
|
||||||
{t("config_page.vr.y_rotation")}
|
{t("config_page.vr.y_rotation")}
|
||||||
<ResetButton onClickFunction={() => selectFunction("y_rotation", default_ui_configs.y_pos)} />
|
<ResetButton onClickFunction={() => selectFunction("y_rotation", default_ui_configs.y_rotation)} />
|
||||||
</p>
|
</p>
|
||||||
<Slider
|
<Slider
|
||||||
className={styles.y_rotation_slider}
|
className={styles.y_rotation_slider}
|
||||||
no_padding={true}
|
no_padding={true}
|
||||||
variable={settings.y_rotation}
|
variable={settings.y_rotation}
|
||||||
step={5}
|
step={ui_configs.y_rotation.step}
|
||||||
min={-180}
|
min={ui_configs.y_rotation.min}
|
||||||
max={180}
|
max={ui_configs.y_rotation.max}
|
||||||
onchangeFunction={(value) => onchangeFunction("y_rotation", value)}
|
onchangeFunction={(value) => onchangeFunction("y_rotation", value)}
|
||||||
|
valueLabelDisplay={y_variable_display}
|
||||||
|
valueLabelDisplayLocation="top"
|
||||||
|
/>
|
||||||
|
<AdjustButtonContainer
|
||||||
|
wrapper_class_name={styles.y_rotation_button_wrapper}
|
||||||
|
is_max={is_max_rotation_y}
|
||||||
|
is_min={is_min_rotation_y}
|
||||||
|
countUp={countUpRotationY}
|
||||||
|
countDown={countDownRotationY}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.rotation_wrapper}>
|
<div className={styles.rotation_wrapper}>
|
||||||
<p className={clsx(styles.slider_label, styles.z_rotation_label)}>
|
<p className={clsx(styles.slider_label, styles.z_rotation_label)}>
|
||||||
{t("config_page.vr.z_rotation")}
|
{t("config_page.vr.z_rotation")}
|
||||||
<ResetButton onClickFunction={() => selectFunction("z_rotation", default_ui_configs.y_pos)} />
|
<ResetButton onClickFunction={() => selectFunction("z_rotation", default_ui_configs.z_rotation)} />
|
||||||
</p>
|
</p>
|
||||||
<Slider
|
<Slider
|
||||||
className={styles.z_rotation_slider}
|
className={styles.z_rotation_slider}
|
||||||
no_padding={true}
|
no_padding={true}
|
||||||
variable={settings.z_rotation}
|
variable={settings.z_rotation}
|
||||||
step={5}
|
step={ui_configs.z_rotation.step}
|
||||||
min={-180}
|
min={ui_configs.z_rotation.min}
|
||||||
max={180}
|
max={ui_configs.z_rotation.max}
|
||||||
onchangeFunction={(value) => onchangeFunction("z_rotation", value)}
|
onchangeFunction={(value) => onchangeFunction("z_rotation", value)}
|
||||||
orientation="vertical"
|
orientation="vertical"
|
||||||
|
valueLabelDisplay={z_variable_display}
|
||||||
|
valueLabelDisplayLocation="left"
|
||||||
|
/>
|
||||||
|
<AdjustButtonContainer
|
||||||
|
wrapper_class_name={styles.z_rotation_button_wrapper}
|
||||||
|
is_max={is_max_rotation_z}
|
||||||
|
is_min={is_min_rotation_z}
|
||||||
|
countUp={countUpRotationZ}
|
||||||
|
countDown={countDownRotationZ}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const AdjustButtonContainer = ({ wrapper_class_name, is_max, is_min, countUp, countDown }) => {
|
||||||
|
return (
|
||||||
|
<div className={wrapper_class_name}>
|
||||||
|
<div
|
||||||
|
className={clsx(
|
||||||
|
styles.button_wrapper,
|
||||||
|
{
|
||||||
|
[styles.is_disabled]: is_max,
|
||||||
|
[styles.up]: true,
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
onClick={countUp}
|
||||||
|
>
|
||||||
|
<TriangleSvg className={styles.adjust_button_triangle_svg} />
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className={clsx(
|
||||||
|
styles.button_wrapper,
|
||||||
|
{
|
||||||
|
[styles.is_disabled]: is_min,
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
onClick={countDown}
|
||||||
|
>
|
||||||
|
<TriangleSvg className={styles.adjust_button_triangle_svg} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
const OtherControls = ({settings, onchangeFunction, ui_configs}) => {
|
const OtherControls = ({settings, onchangeFunction, ui_configs}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
@@ -393,10 +535,6 @@ const ResetButton = ({onClickFunction}) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
import SquareSvg from "@images/square.svg?react";
|
|
||||||
import TriangleSvg from "@images/triangle.svg?react";
|
|
||||||
import { randomIntMinMax } from "@utils";
|
|
||||||
|
|
||||||
const SendSampleTextToggleButton = () => {
|
const SendSampleTextToggleButton = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { sendTextToOverlay } = useSendTextToOverlay();
|
const { sendTextToOverlay } = useSendTextToOverlay();
|
||||||
@@ -446,3 +584,57 @@ const SendSampleTextToggleButton = () => {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const useVariableControl = (key, settings, onchangeFunction, ui_configs) => {
|
||||||
|
const [variable_display, setVariableDisplay] = useState("auto");
|
||||||
|
|
||||||
|
const [is_max, setIsMax] = useState(settings[key] >= ui_configs[key].max);
|
||||||
|
const [is_min, setIsMin] = useState(settings[key] <= ui_configs[key].min);
|
||||||
|
|
||||||
|
const timerRef = useRef();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
clearTimeout(timerRef.current);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const triggerDisplay = () => {
|
||||||
|
setVariableDisplay("on");
|
||||||
|
clearTimeout(timerRef.current);
|
||||||
|
timerRef.current = setTimeout(() => {
|
||||||
|
setVariableDisplay("auto");
|
||||||
|
}, 2000);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setIsMax(settings[key] >= ui_configs[key].max);
|
||||||
|
setIsMin(settings[key] <= ui_configs[key].min);
|
||||||
|
}, [settings[key]]);
|
||||||
|
|
||||||
|
const countUp = () => {
|
||||||
|
if (is_max) return;
|
||||||
|
const step = ui_configs[key].step;
|
||||||
|
const new_value = parseFloat((settings[key] + step).toFixed(2));
|
||||||
|
onchangeFunction(key, new_value);
|
||||||
|
triggerDisplay();
|
||||||
|
};
|
||||||
|
|
||||||
|
const countDown = () => {
|
||||||
|
if (is_min) return;
|
||||||
|
const step = ui_configs[key].step;
|
||||||
|
const new_value = parseFloat((settings[key] - step).toFixed(2));
|
||||||
|
onchangeFunction(key, new_value);
|
||||||
|
triggerDisplay();
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
variable_display,
|
||||||
|
is_max,
|
||||||
|
is_min,
|
||||||
|
countUp,
|
||||||
|
countDown,
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 56rem;
|
max-width: 56rem;
|
||||||
gap: 2rem;
|
gap: 4rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.controller_type_switch {
|
.controller_type_switch {
|
||||||
@@ -58,12 +58,13 @@
|
|||||||
|
|
||||||
.sample_text_button_wrapper {
|
.sample_text_button_wrapper {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 0;
|
bottom: -12%;
|
||||||
left: -74%;
|
left: -80%;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
// transform: translate(-50%, -50%);
|
||||||
}
|
}
|
||||||
.sample_text_button {
|
.sample_text_button {
|
||||||
background-color: var(--dark_850_color);
|
background-color: var(--dark_850_color);
|
||||||
@@ -121,20 +122,20 @@
|
|||||||
}
|
}
|
||||||
.x_position_label {
|
.x_position_label {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: -4.6rem;
|
bottom: -5rem;
|
||||||
right: -46%;
|
right: -46%;
|
||||||
justify-content: end;
|
justify-content: end;
|
||||||
}
|
}
|
||||||
.y_position_label {
|
.y_position_label {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: -44%;
|
bottom: 110%;
|
||||||
left: 10%;
|
right: 119%;
|
||||||
justify-content: start;
|
justify-content: end;
|
||||||
}
|
}
|
||||||
.z_position_label {
|
.z_position_label {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 30%;
|
top: 14%;
|
||||||
left: 80%;
|
left: 110%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.x_position_slider {
|
.x_position_slider {
|
||||||
@@ -155,14 +156,84 @@
|
|||||||
|
|
||||||
.z_position_slider {
|
.z_position_slider {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 61%;
|
bottom: 80%;
|
||||||
left: 61%;
|
left: 88%;
|
||||||
transform: translate(50%,50%) rotate(45deg);
|
transform: translate(50%,50%) rotate(45deg);
|
||||||
width: 0%;
|
width: 0%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
%variable-button {
|
||||||
|
width: 3.8rem;
|
||||||
|
border-radius: 0.4rem;
|
||||||
|
aspect-ratio: 1.2 / 1;
|
||||||
|
background-color: var(--dark_850_color);
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 1.6rem;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--primary_500_color);
|
||||||
|
}
|
||||||
|
&:active {
|
||||||
|
background-color: var(--primary_600_color);
|
||||||
|
}
|
||||||
|
&.is_disabled {
|
||||||
|
pointer-events: none;
|
||||||
|
background-color: var(--dark_875_color);
|
||||||
|
& .adjust_button_triangle_svg {
|
||||||
|
color: var(--dark_800_color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin variable-button-wrapper($vertical-pos, $vertical-value, $horizontal-pos, $horizontal-value, $rotate: 0deg) {
|
||||||
|
position: absolute;
|
||||||
|
#{$vertical-pos}: $vertical-value;
|
||||||
|
#{$horizontal-pos}: $horizontal-value;
|
||||||
|
display: flex;
|
||||||
|
gap: 1.6rem;
|
||||||
|
flex-direction: column;
|
||||||
|
transform: translate(-50%) rotate($rotate);
|
||||||
|
}
|
||||||
|
|
||||||
|
.button_wrapper {
|
||||||
|
@extend %variable-button;
|
||||||
|
|
||||||
|
&.up .adjust_button_triangle_svg {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
&:not(.up) .adjust_button_triangle_svg {
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
&.is_disabled {
|
||||||
|
pointer-events: none;
|
||||||
|
color: var(--dark_875_color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.adjust_button_triangle_svg {
|
||||||
|
width: 1.8rem;
|
||||||
|
color: var(--dark_400_color);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.y_position_button_wrapper {
|
||||||
|
@include variable-button-wrapper(top, 30%, left, -26%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.x_position_button_wrapper {
|
||||||
|
@include variable-button-wrapper(bottom, -38%, left, 46%, 90deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.z_position_button_wrapper {
|
||||||
|
@include variable-button-wrapper(bottom, 26%, right, -4%, 45deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// .rotation_controls {
|
// .rotation_controls {
|
||||||
@@ -175,19 +246,20 @@
|
|||||||
|
|
||||||
.x_rotation_label {
|
.x_rotation_label {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: -44%;
|
bottom: 110%;
|
||||||
left: 10%;
|
right: 119%;
|
||||||
|
justify-content: end;
|
||||||
}
|
}
|
||||||
.y_rotation_label {
|
.y_rotation_label {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: -4.6rem;
|
bottom: -5rem;
|
||||||
right: -46%;
|
right: -46%;
|
||||||
justify-content: end;
|
justify-content: end;
|
||||||
}
|
}
|
||||||
.z_rotation_label {
|
.z_rotation_label {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: -10%;
|
top: -20%;
|
||||||
right: -110%;
|
right: -100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.x_rotation_slider {
|
.x_rotation_slider {
|
||||||
@@ -216,6 +288,21 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.x_rotation_button_wrapper {
|
||||||
|
@include variable-button-wrapper(top, 30%, left, -26%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.y_rotation_button_wrapper {
|
||||||
|
@include variable-button-wrapper(bottom, -38%, left, 46%, 90deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.z_rotation_button_wrapper {
|
||||||
|
@include variable-button-wrapper(bottom, 50%, right, -60%, -45deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.slider_reset_button {
|
.slider_reset_button {
|
||||||
background-color: var(--dark_875_color);
|
background-color: var(--dark_875_color);
|
||||||
padding: 0.6rem;
|
padding: 0.6rem;
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ const OpenVrcMicMuteSyncQuickSetting = () => {
|
|||||||
return (
|
return (
|
||||||
<OpenQuickSettingButton
|
<OpenQuickSettingButton
|
||||||
label={t("config_page.others.vrc_mic_mute_sync.label")}
|
label={t("config_page.others.vrc_mic_mute_sync.label")}
|
||||||
variable={currentEnableVrcMicMuteSync.data}
|
variable={currentEnableVrcMicMuteSync.data.is_enabled}
|
||||||
onClickFunction={onClickFunction}
|
onClickFunction={onClickFunction}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ export const UpdateModal = () => {
|
|||||||
</div>
|
</div>
|
||||||
<div className={styles.cuda_section}>
|
<div className={styles.cuda_section}>
|
||||||
<div className={styles.button_wrapper}>
|
<div className={styles.button_wrapper}>
|
||||||
<button className={cuda_accept_button_class_name} onClick={onClickUpdateSoftware_CUDA}>CUDA (GPU)</button>
|
<button className={cuda_accept_button_class_name} onClick={onClickUpdateSoftware_CUDA}>CUDA (CPU/GPU)</button>
|
||||||
{!is_cpu_version ? <CurrentVersionLabel is_latest_version_already={is_latest_version_already} is_cuda={true}/> : null}
|
{!is_cpu_version ? <CurrentVersionLabel is_latest_version_already={is_latest_version_already} is_cuda={true}/> : null}
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.version_desc_container}>
|
<div className={styles.version_desc_container}>
|
||||||
@@ -85,7 +85,7 @@ const VersionDescComponent = (props) => {
|
|||||||
return (
|
return (
|
||||||
<div className={styles.version_desc_wrapper}>
|
<div className={styles.version_desc_wrapper}>
|
||||||
<div className={styles.version_desc_point}></div>
|
<div className={styles.version_desc_point}></div>
|
||||||
<p className={styles.version_desc}>{props.desc}</p>
|
<p className={styles.version_desc}>{`- ${props.desc}`}</p>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ export const SnackbarController = () => {
|
|||||||
|
|
||||||
const snackbar_classname = clsx(styles.snackbar_content, {
|
const snackbar_classname = clsx(styles.snackbar_content, {
|
||||||
[styles.is_success]: currentNotificationStatus.data.status === "success",
|
[styles.is_success]: currentNotificationStatus.data.status === "success",
|
||||||
|
[styles.is_warning]: currentNotificationStatus.data.status === "warning",
|
||||||
[styles.is_error]: currentNotificationStatus.data.status === "error",
|
[styles.is_error]: currentNotificationStatus.data.status === "error",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,9 @@
|
|||||||
&.is_success {
|
&.is_success {
|
||||||
background-color: var(--success_bc_color);
|
background-color: var(--success_bc_color);
|
||||||
}
|
}
|
||||||
|
&.is_warning {
|
||||||
|
background-color: var(--waring_bc_color);
|
||||||
|
}
|
||||||
&.is_error {
|
&.is_error {
|
||||||
background-color: var(--error_bc_color);
|
background-color: var(--error_bc_color);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,15 +3,16 @@ import styles from "./Checkbox.module.scss";
|
|||||||
export const Checkbox = ({
|
export const Checkbox = ({
|
||||||
checkboxId,
|
checkboxId,
|
||||||
variable,
|
variable,
|
||||||
|
is_available = true,
|
||||||
toggleFunction,
|
toggleFunction,
|
||||||
size = "2.8rem",
|
size = "2.8rem",
|
||||||
color = "var(--primary_600_color)",
|
|
||||||
borderWidth = "0.2rem",
|
borderWidth = "0.2rem",
|
||||||
padding = "2rem",
|
padding = "2rem",
|
||||||
}) => {
|
}) => {
|
||||||
|
|
||||||
const wrapper_class_names = clsx(styles.checkbox_wrapper, {
|
const wrapper_class_names = clsx(styles.checkbox_wrapper, {
|
||||||
[styles.is_disabled]: variable.state === "pending",
|
[styles.is_disabled]: !is_available,
|
||||||
|
[styles.is_pending]: variable.state === "pending",
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -21,7 +22,6 @@ export const Checkbox = ({
|
|||||||
htmlFor={checkboxId}
|
htmlFor={checkboxId}
|
||||||
style={{
|
style={{
|
||||||
"--checkbox-size": size,
|
"--checkbox-size": size,
|
||||||
"--checkbox-color": color,
|
|
||||||
"--checkbox-border-width": borderWidth,
|
"--checkbox-border-width": borderWidth,
|
||||||
"--checkbox-padding": padding,
|
"--checkbox-padding": padding,
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -18,12 +18,19 @@
|
|||||||
border: var(--checkbox-color, var(--primary_600_color)) solid var(--checkbox-border-width, 0.2rem);
|
border: var(--checkbox-color, var(--primary_600_color)) solid var(--checkbox-border-width, 0.2rem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&.is_disabled {
|
&.is_pending {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
& .cbx {
|
& .cbx {
|
||||||
border-color: var(--primary_800_color);
|
border-color: var(--primary_800_color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
&.is_disabled {
|
||||||
|
pointer-events: none;
|
||||||
|
& .cbx {
|
||||||
|
filter: grayscale(100%);
|
||||||
|
border-color: var(--dark_800_color);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.checkbox_wrapper .cbx {
|
.checkbox_wrapper .cbx {
|
||||||
|
|||||||
@@ -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":
|
||||||
showNotification_Error(t("common_error.threshold_invalid_value",
|
if (message === "Mic energy threshold value is out of range") {
|
||||||
{ min: ui_configs.mic_threshold_min, max: ui_configs.mic_threshold_max },
|
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",
|
return;
|
||||||
{ min: ui_configs.speaker_threshold_min, max: ui_configs.speaker_threshold_max },
|
case "/set/data/speaker_threshold":
|
||||||
));
|
if (message === "Speaker energy threshold value is out of range") {
|
||||||
break;
|
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":
|
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":
|
||||||
updateDeepLAuthKey(data);
|
if (message === "DeepL auth key length is not correct") {
|
||||||
showNotification_Error(t("common_error.deepl_auth_key_invalid_length"));
|
updateDeepLAuthKey(data);
|
||||||
break;
|
showNotification_Error(t("common_error.deepl_auth_key_invalid_length"));
|
||||||
case "Authentication failure of deepL auth key":
|
} else if (message === "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":
|
||||||
updateMicRecordTimeout(data);
|
if (message === "Mic record timeout value is out of range") {
|
||||||
showNotification_Error(
|
updateMicRecordTimeout(data);
|
||||||
t("common_error.invalid_value_mic_record_timeout",
|
showNotification_Error(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;
|
}
|
||||||
case "Mic phrase timeout value is out of range":
|
return;
|
||||||
updateMicPhraseTimeout(data);
|
case "/set/data/mic_phrase_timeout":
|
||||||
showNotification_Error(
|
if (message === "Mic phrase timeout value is out of range") {
|
||||||
t("common_error.invalid_value_mic_phrase_timeout",
|
updateMicPhraseTimeout(data);
|
||||||
{ mic_record_timeout_label: t("config_page.transcription.mic_record_timeout.label") }
|
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);
|
return;
|
||||||
showNotification_Error(t("common_error.invalid_value_mic_max_phrase"));
|
case "/set/data/mic_max_phrases":
|
||||||
break;
|
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":
|
case "/set/data/speaker_record_timeout":
|
||||||
updateSpeakerRecordTimeout(data);
|
if (message === "Speaker record timeout value is out of range") {
|
||||||
showNotification_Error(
|
updateSpeakerRecordTimeout(data);
|
||||||
t("common_error.invalid_value_speaker_record_timeout",
|
showNotification_Error(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;
|
}
|
||||||
case "Speaker phrase timeout value is out of range":
|
return;
|
||||||
updateSpeakerPhraseTimeout(data);
|
case "/set/data/speaker_phrase_timeout":
|
||||||
showNotification_Error(
|
if (message === "Speaker phrase timeout value is out of range") {
|
||||||
t("common_error.invalid_value_speaker_phrase_timeout",
|
updateSpeakerPhraseTimeout(data);
|
||||||
{ speaker_record_timeout_label: t("config_page.transcription.speaker_record_timeout.label") }
|
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);
|
return;
|
||||||
showNotification_Error(t("common_error.invalid_value_speaker_max_phrase"));
|
case "/set/data/speaker_max_phrases":
|
||||||
break;
|
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)
|
// Advanced Settings, error messages are set by Backend (EN only)
|
||||||
case "Invalid IP address":
|
case "/set/data/osc_ip_address":
|
||||||
updateOscIpAddress(data);
|
if (message === "Invalid IP address") {
|
||||||
showNotification_Error(message);
|
updateOscIpAddress(data);
|
||||||
break;
|
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);
|
case "/set/enable/websocket_server":
|
||||||
showNotification_Error(message);
|
if (message === "WebSocket server host or port is not available") {
|
||||||
break;
|
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:
|
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,5 +11,6 @@ export { useMessage } from "./useMessage";
|
|||||||
export { useUpdateSoftware } from "./useUpdateSoftware";
|
export { useUpdateSoftware } from "./useUpdateSoftware";
|
||||||
export { useVolume } from "./useVolume";
|
export { useVolume } from "./useVolume";
|
||||||
export { useHandleNetworkConnection } from "./useHandleNetworkConnection";
|
export { useHandleNetworkConnection } from "./useHandleNetworkConnection";
|
||||||
|
export { useHandleOscQuery } from "./useHandleOscQuery";
|
||||||
export { useIsVrctAvailable } from "./useIsVrctAvailable";
|
export { useIsVrctAvailable } from "./useIsVrctAvailable";
|
||||||
export { useFetch } from "./useFetch";
|
export { useFetch } from "./useFetch";
|
||||||
44
src-ui/logics/common/useHandleOscQuery.js
Normal file
44
src-ui/logics/common/useHandleOscQuery.js
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { useNotificationStatus } from "@logics_common";
|
||||||
|
import {
|
||||||
|
useEnableVrcMicMuteSync,
|
||||||
|
} from "@logics_configs";
|
||||||
|
|
||||||
|
export const useHandleOscQuery = () => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const { showNotification_Warning } = useNotificationStatus();
|
||||||
|
const { updateEnableVrcMicMuteSync } = useEnableVrcMicMuteSync();
|
||||||
|
|
||||||
|
const handleOscQuery = ({is_osc_query_enabled, disabled_functions}) => {
|
||||||
|
if (!is_osc_query_enabled && disabled_functions.length > 0) {
|
||||||
|
const BASE_LABEL = t("common_warning.unable_to_use_osc_query");
|
||||||
|
let items_label = "";
|
||||||
|
|
||||||
|
for (const disabled_function of disabled_functions) {
|
||||||
|
if (disabled_function === "vrc_mic_mute_sync") {
|
||||||
|
updateEnableVrcMicMuteSync({
|
||||||
|
is_enabled: false,
|
||||||
|
is_available: false,
|
||||||
|
});
|
||||||
|
const item = `- ${t("config_page.others.vrc_mic_mute_sync.label")}`;
|
||||||
|
items_label = `${items_label}\n${item}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const label = `${BASE_LABEL}${items_label}`;
|
||||||
|
showNotification_Warning(
|
||||||
|
label,
|
||||||
|
{ hide_duration: 10000, }
|
||||||
|
);
|
||||||
|
} else if (is_osc_query_enabled) {
|
||||||
|
updateEnableVrcMicMuteSync((old_value) => ({
|
||||||
|
...old_value.data,
|
||||||
|
is_available: true,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
handleOscQuery,
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -5,6 +5,16 @@ export const useNotificationStatus = () => {
|
|||||||
|
|
||||||
const generateRandomKey = () => Math.random();
|
const generateRandomKey = () => Math.random();
|
||||||
|
|
||||||
|
const showNotification_Warning = (message, options = {}) => {
|
||||||
|
updateNotificationStatus({
|
||||||
|
status: "warning",
|
||||||
|
is_open: true,
|
||||||
|
key: generateRandomKey(),
|
||||||
|
message: message,
|
||||||
|
options: options,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const showNotification_Error = (message, options = {}) => {
|
const showNotification_Error = (message, options = {}) => {
|
||||||
updateNotificationStatus({
|
updateNotificationStatus({
|
||||||
status: "error",
|
status: "error",
|
||||||
@@ -37,6 +47,7 @@ export const useNotificationStatus = () => {
|
|||||||
currentNotificationStatus,
|
currentNotificationStatus,
|
||||||
updateNotificationStatus,
|
updateNotificationStatus,
|
||||||
|
|
||||||
|
showNotification_Warning,
|
||||||
showNotification_Error,
|
showNotification_Error,
|
||||||
showNotification_Success,
|
showNotification_Success,
|
||||||
closeNotification,
|
closeNotification,
|
||||||
|
|||||||
67
src-ui/logics/configs/advanced_settings/useWebsocket.js
Normal file
67
src-ui/logics/configs/advanced_settings/useWebsocket.js
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
import {
|
||||||
|
useStore_EnableWebsocket,
|
||||||
|
useStore_WebsocketHost,
|
||||||
|
useStore_WebsocketPort,
|
||||||
|
} from "@store";
|
||||||
|
import { useStdoutToPython } from "@logics/useStdoutToPython";
|
||||||
|
|
||||||
|
export const useWebsocket = () => {
|
||||||
|
const { asyncStdoutToPython } = useStdoutToPython();
|
||||||
|
const { currentEnableWebsocket, updateEnableWebsocket, pendingEnableWebsocket } = useStore_EnableWebsocket();
|
||||||
|
const { currentWebsocketHost, updateWebsocketHost, pendingWebsocketHost } = useStore_WebsocketHost();
|
||||||
|
const { currentWebsocketPort, updateWebsocketPort, pendingWebsocketPort } = useStore_WebsocketPort();
|
||||||
|
|
||||||
|
const getEnableWebsocket = () => {
|
||||||
|
pendingEnableWebsocket();
|
||||||
|
asyncStdoutToPython("/get/data/websocket_server");
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleEnableWebsocket = () => {
|
||||||
|
pendingEnableWebsocket();
|
||||||
|
if (currentEnableWebsocket.data) {
|
||||||
|
asyncStdoutToPython("/set/disable/websocket_server");
|
||||||
|
} else {
|
||||||
|
asyncStdoutToPython("/set/enable/websocket_server");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const getWebsocketHost = () => {
|
||||||
|
pendingWebsocketHost();
|
||||||
|
asyncStdoutToPython("/get/data/websocket_host");
|
||||||
|
};
|
||||||
|
|
||||||
|
const setWebsocketHost = (websocket_host) => {
|
||||||
|
pendingWebsocketHost();
|
||||||
|
asyncStdoutToPython("/set/data/websocket_host", websocket_host);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const getWebsocketPort = () => {
|
||||||
|
pendingWebsocketPort();
|
||||||
|
asyncStdoutToPython("/get/data/websocket_port");
|
||||||
|
};
|
||||||
|
|
||||||
|
const setWebsocketPort = (websocket_port) => {
|
||||||
|
pendingWebsocketPort();
|
||||||
|
asyncStdoutToPython("/set/data/websocket_port", websocket_port);
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
currentEnableWebsocket,
|
||||||
|
updateEnableWebsocket,
|
||||||
|
getEnableWebsocket,
|
||||||
|
toggleEnableWebsocket,
|
||||||
|
|
||||||
|
currentWebsocketHost,
|
||||||
|
updateWebsocketHost,
|
||||||
|
getWebsocketHost,
|
||||||
|
setWebsocketHost,
|
||||||
|
|
||||||
|
currentWebsocketPort,
|
||||||
|
updateWebsocketPort,
|
||||||
|
getWebsocketPort,
|
||||||
|
setWebsocketPort,
|
||||||
|
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -56,6 +56,7 @@ export { useHotkeys } from "./hotkeys/useHotkeys";
|
|||||||
|
|
||||||
export { useOscIpAddress } from "./advanced_settings/useOscIpAddress";
|
export { useOscIpAddress } from "./advanced_settings/useOscIpAddress";
|
||||||
export { useOscPort } from "./advanced_settings/useOscPort";
|
export { useOscPort } from "./advanced_settings/useOscPort";
|
||||||
|
export { useWebsocket } from "./advanced_settings/useWebsocket";
|
||||||
|
|
||||||
|
|
||||||
export { useSupporters } from "./supporters/useSupporters";
|
export { useSupporters } from "./supporters/useSupporters";
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ export const useEnableVrcMicMuteSync = () => {
|
|||||||
|
|
||||||
const toggleEnableVrcMicMuteSync = () => {
|
const toggleEnableVrcMicMuteSync = () => {
|
||||||
pendingEnableVrcMicMuteSync();
|
pendingEnableVrcMicMuteSync();
|
||||||
if (currentEnableVrcMicMuteSync.data) {
|
if (currentEnableVrcMicMuteSync.data.is_enabled) {
|
||||||
asyncStdoutToPython("/set/disable/vrc_mic_mute_sync");
|
asyncStdoutToPython("/set/disable/vrc_mic_mute_sync");
|
||||||
} else {
|
} else {
|
||||||
asyncStdoutToPython("/set/enable/vrc_mic_mute_sync");
|
asyncStdoutToPython("/set/enable/vrc_mic_mute_sync");
|
||||||
|
|||||||
@@ -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,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
useIsVrctAvailable,
|
useIsVrctAvailable,
|
||||||
useNotificationStatus,
|
useNotificationStatus,
|
||||||
useHandleNetworkConnection,
|
useHandleNetworkConnection,
|
||||||
|
useHandleOscQuery,
|
||||||
|
|
||||||
useSoftwareVersion,
|
useSoftwareVersion,
|
||||||
useComputeMode,
|
useComputeMode,
|
||||||
@@ -75,6 +76,7 @@ import {
|
|||||||
usePlugins,
|
usePlugins,
|
||||||
useOscIpAddress,
|
useOscIpAddress,
|
||||||
useOscPort,
|
useOscPort,
|
||||||
|
useWebsocket,
|
||||||
} from "@logics_configs";
|
} from "@logics_configs";
|
||||||
|
|
||||||
export const useReceiveRoutes = () => {
|
export const useReceiveRoutes = () => {
|
||||||
@@ -82,6 +84,7 @@ export const useReceiveRoutes = () => {
|
|||||||
const { updateComputeMode } = useComputeMode();
|
const { updateComputeMode } = useComputeMode();
|
||||||
const { updateInitProgress } = useInitProgress();
|
const { updateInitProgress } = useInitProgress();
|
||||||
const { updateIsBackendReady } = useIsBackendReady();
|
const { updateIsBackendReady } = useIsBackendReady();
|
||||||
|
const { handleOscQuery } = useHandleOscQuery();
|
||||||
const { restoreWindowGeometry } = useWindow();
|
const { restoreWindowGeometry } = useWindow();
|
||||||
const { updateIsMainPageCompactMode } = useIsMainPageCompactMode();
|
const { updateIsMainPageCompactMode } = useIsMainPageCompactMode();
|
||||||
const {
|
const {
|
||||||
@@ -180,6 +183,11 @@ export const useReceiveRoutes = () => {
|
|||||||
|
|
||||||
const { updateOscIpAddress } = useOscIpAddress();
|
const { updateOscIpAddress } = useOscIpAddress();
|
||||||
const { updateOscPort } = useOscPort();
|
const { updateOscPort } = useOscPort();
|
||||||
|
const {
|
||||||
|
updateEnableWebsocket,
|
||||||
|
updateWebsocketHost,
|
||||||
|
updateWebsocketPort,
|
||||||
|
} = useWebsocket();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -213,6 +221,12 @@ export const useReceiveRoutes = () => {
|
|||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
"/run/connected_network": handleNetworkConnection,
|
"/run/connected_network": handleNetworkConnection,
|
||||||
|
"/run/enable_osc_query": ({data, disabled_functions}) => {
|
||||||
|
handleOscQuery({
|
||||||
|
is_osc_query_enabled: data,
|
||||||
|
disabled_functions: disabled_functions,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
// Main Page
|
// Main Page
|
||||||
// Page Controls
|
// Page Controls
|
||||||
@@ -474,9 +488,15 @@ export const useReceiveRoutes = () => {
|
|||||||
"/set/enable/logger_feature": updateEnableAutoExportMessageLogs,
|
"/set/enable/logger_feature": updateEnableAutoExportMessageLogs,
|
||||||
"/set/disable/logger_feature": updateEnableAutoExportMessageLogs,
|
"/set/disable/logger_feature": updateEnableAutoExportMessageLogs,
|
||||||
|
|
||||||
"/get/data/vrc_mic_mute_sync": updateEnableVrcMicMuteSync,
|
"/get/data/vrc_mic_mute_sync": (payload) => updateEnableVrcMicMuteSync((old_value) => {
|
||||||
"/set/enable/vrc_mic_mute_sync": updateEnableVrcMicMuteSync,
|
return {...old_value.data, is_enabled: payload};
|
||||||
"/set/disable/vrc_mic_mute_sync": updateEnableVrcMicMuteSync,
|
}),
|
||||||
|
"/set/enable/vrc_mic_mute_sync": (payload) => updateEnableVrcMicMuteSync((old_value) => {
|
||||||
|
return {...old_value.data, is_enabled: payload};
|
||||||
|
}),
|
||||||
|
"/set/disable/vrc_mic_mute_sync": (payload) => updateEnableVrcMicMuteSync((old_value) => {
|
||||||
|
return {...old_value.data, is_enabled: payload};
|
||||||
|
}),
|
||||||
|
|
||||||
"/get/data/send_message_to_vrc": updateEnableSendMessageToVrc,
|
"/get/data/send_message_to_vrc": updateEnableSendMessageToVrc,
|
||||||
"/set/enable/send_message_to_vrc": updateEnableSendMessageToVrc,
|
"/set/enable/send_message_to_vrc": updateEnableSendMessageToVrc,
|
||||||
@@ -505,6 +525,16 @@ export const useReceiveRoutes = () => {
|
|||||||
"/get/data/osc_port": updateOscPort,
|
"/get/data/osc_port": updateOscPort,
|
||||||
"/set/data/osc_port": updateOscPort,
|
"/set/data/osc_port": updateOscPort,
|
||||||
|
|
||||||
|
"/get/data/websocket_server": updateEnableWebsocket,
|
||||||
|
"/set/enable/websocket_server": updateEnableWebsocket,
|
||||||
|
"/set/disable/websocket_server": updateEnableWebsocket,
|
||||||
|
|
||||||
|
"/get/data/websocket_host": updateWebsocketHost,
|
||||||
|
"/set/data/websocket_host": updateWebsocketHost,
|
||||||
|
|
||||||
|
"/get/data/websocket_port": updateWebsocketPort,
|
||||||
|
"/set/data/websocket_port": updateWebsocketPort,
|
||||||
|
|
||||||
"/get/data/mic_avg_logprob": ()=>{}, // Not implemented on UI yet
|
"/get/data/mic_avg_logprob": ()=>{}, // Not implemented on UI yet
|
||||||
"/get/data/mic_no_speech_prob": ()=>{}, // Not implemented on UI yet
|
"/get/data/mic_no_speech_prob": ()=>{}, // Not implemented on UI yet
|
||||||
"/get/data/speaker_avg_logprob": ()=>{}, // Not implemented on UI yet
|
"/get/data/speaker_avg_logprob": ()=>{}, // Not implemented on UI yet
|
||||||
@@ -514,30 +544,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)) {
|
||||||
@@ -567,17 +573,12 @@ export const useReceiveRoutes = () => {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 400:
|
case 400:
|
||||||
const error_route = error_status_routes[parsed_data.endpoint];
|
errorHandling_Backend({
|
||||||
if (error_route) {
|
message: parsed_data.result.message,
|
||||||
error_route({
|
data: parsed_data.result.data,
|
||||||
message: parsed_data.result.message,
|
endpoint: parsed_data.endpoint,
|
||||||
data: parsed_data.result.data,
|
result: parsed_data.result,
|
||||||
endpoint: parsed_data.endpoint,
|
});
|
||||||
_result: parsed_data.result,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
handleInvalidEndpoint(parsed_data);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case 500:
|
case 500:
|
||||||
showNotification_Error(
|
showNotification_Error(
|
||||||
|
|||||||
@@ -60,10 +60,19 @@ export const createAtomWithHook = (initialValue, base_name, options) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const updateAtom = (payload, options = {}) => {
|
const updateAtom = (payload, options = {}) => {
|
||||||
const { remain_state = false, set_state } = options;
|
const { remain_state = false, set_state, lock_state } = options;
|
||||||
|
|
||||||
setAtom((currentValue) => {
|
setAtom((currentValue) => {
|
||||||
const new_state = set_state ?? (remain_state ? currentValue.state : "ok");
|
let new_state;
|
||||||
|
if (lock_state) {
|
||||||
|
new_state = set_state;
|
||||||
|
} else {
|
||||||
|
if (currentValue.lock_state) {
|
||||||
|
new_state = currentValue.state;
|
||||||
|
} else {
|
||||||
|
new_state = set_state ?? (remain_state ? currentValue.state : "ok");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const updated_data = typeof payload === "function"
|
const updated_data = typeof payload === "function"
|
||||||
? payload(currentValue)
|
? payload(currentValue)
|
||||||
@@ -290,6 +299,10 @@ export const { atomInstance: Atom_PluginsData, useHook: useStore_PluginsData } =
|
|||||||
export const { atomInstance: Atom_OscIpAddress, useHook: useStore_OscIpAddress } = createAtomWithHook("127.0.0.1", "OscIpAddress");
|
export const { atomInstance: Atom_OscIpAddress, useHook: useStore_OscIpAddress } = createAtomWithHook("127.0.0.1", "OscIpAddress");
|
||||||
export const { atomInstance: Atom_OscPort, useHook: useStore_OscPort } = createAtomWithHook("9000", "OscPort");
|
export const { atomInstance: Atom_OscPort, useHook: useStore_OscPort } = createAtomWithHook("9000", "OscPort");
|
||||||
|
|
||||||
|
export const { atomInstance: Atom_EnableWebsocket, useHook: useStore_EnableWebsocket } = createAtomWithHook(true, "EnableWebsocket");
|
||||||
|
export const { atomInstance: Atom_WebsocketHost, useHook: useStore_WebsocketHost } = createAtomWithHook("127.0.0.1", "WebsocketHost");
|
||||||
|
export const { atomInstance: Atom_WebsocketPort, useHook: useStore_WebsocketPort } = createAtomWithHook("2231", "WebsocketPort");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Supporters
|
// Supporters
|
||||||
|
|||||||
@@ -7,12 +7,18 @@ export const ui_configs = {
|
|||||||
x_pos: { step: 0.05, min: -0.5, max: 0.5 },
|
x_pos: { step: 0.05, min: -0.5, max: 0.5 },
|
||||||
y_pos: { step: 0.05, min: -0.8, max: 0.8 },
|
y_pos: { step: 0.05, min: -0.8, max: 0.8 },
|
||||||
z_pos: { step: 0.05, min: -0.5, max: 1.5 },
|
z_pos: { step: 0.05, min: -0.5, max: 1.5 },
|
||||||
|
x_rotation: { min: -180, max: 180, step: 5 },
|
||||||
|
y_rotation: { min: -180, max: 180, step: 5 },
|
||||||
|
z_rotation: { min: -180, max: 180, step: 5 },
|
||||||
ui_scaling: { step: 10, min: 40, max: 200 },
|
ui_scaling: { step: 10, min: 40, max: 200 },
|
||||||
},
|
},
|
||||||
overlay_large_log: {
|
overlay_large_log: {
|
||||||
x_pos: { step: 0.05, min: -0.5, max: 0.5 },
|
x_pos: { step: 0.05, min: -0.5, max: 0.5 },
|
||||||
y_pos: { step: 0.05, min: -0.8, max: 0.8 },
|
y_pos: { step: 0.05, min: -0.8, max: 0.8 },
|
||||||
z_pos: { step: 0.05, min: -0.5, max: 1.5 },
|
z_pos: { step: 0.05, min: -0.5, max: 1.5 },
|
||||||
|
x_rotation: { min: -180, max: 180, step: 5 },
|
||||||
|
y_rotation: { min: -180, max: 180, step: 5 },
|
||||||
|
z_rotation: { min: -180, max: 180, step: 5 },
|
||||||
ui_scaling: { step: 10, min: 40, max: 200 },
|
ui_scaling: { step: 10, min: 40, max: 200 },
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
87
zip.py
87
zip.py
@@ -1,37 +1,70 @@
|
|||||||
import os
|
|
||||||
import zipfile
|
import zipfile
|
||||||
import argparse
|
import argparse
|
||||||
|
from pathlib import Path
|
||||||
|
import time
|
||||||
|
from tqdm import tqdm # tqdmをインポート
|
||||||
|
|
||||||
def zip_files_and_directory(zip_name, file_paths, dir_paths):
|
def zip_files_and_directory(zip_name, file_paths, dir_paths, verbose=False):
|
||||||
|
zip_file_path = Path(zip_name)
|
||||||
# ZIPファイルを作成
|
# ZIPファイルを作成
|
||||||
with zipfile.ZipFile(zip_name, 'w', zipfile.ZIP_DEFLATED) as zipf:
|
try:
|
||||||
# ファイルを追加
|
with zipfile.ZipFile(zip_file_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
|
||||||
for file_path in file_paths:
|
# ファイルを追加
|
||||||
if os.path.isfile(file_path):
|
for file_path_str in tqdm(file_paths, desc="Adding files", unit="file"):
|
||||||
zipf.write(file_path, os.path.basename(file_path))
|
file_path = Path(file_path_str)
|
||||||
print(f"Add file: {file_path}")
|
if file_path.is_file():
|
||||||
|
zipf.write(file_path, file_path.name)
|
||||||
|
if verbose:
|
||||||
|
print(f"Add file: {file_path}")
|
||||||
|
else:
|
||||||
|
print(f"Warning: File not found or is not a file: {file_path}")
|
||||||
|
|
||||||
# ディレクトリを追加
|
# ディレクトリを追加
|
||||||
for dir_path in dir_paths:
|
for dir_path_str in dir_paths:
|
||||||
if os.path.isdir(dir_path):
|
dir_path = Path(dir_path_str)
|
||||||
for foldername, subfolders, filenames in os.walk(dir_path):
|
if dir_path.is_dir():
|
||||||
for filename in filenames:
|
all_files_in_dir = [item for item in dir_path.rglob("*") if item.is_file()]
|
||||||
file_full_path = os.path.join(foldername, filename)
|
for item in tqdm(all_files_in_dir, desc=f"Adding files from {dir_path.name}", unit="file"):
|
||||||
# ディレクトリを保持しつつ、ルートに配置
|
# ディレクトリ構造を保持しつつ、ルートに配置
|
||||||
arcname = os.path.join(
|
arcname = Path(dir_path.name) / item.relative_to(dir_path)
|
||||||
os.path.basename(dir_path),
|
zipf.write(item, arcname)
|
||||||
os.path.relpath(file_full_path, dir_path)
|
if verbose:
|
||||||
)
|
print(f"Add file: {item}")
|
||||||
zipf.write(file_full_path, arcname)
|
else:
|
||||||
print(f"Add file: {file_full_path}")
|
print(f"Warning: Directory not found or is not a directory: {dir_path}")
|
||||||
|
print(f"Successfully created zip file: {zip_file_path}")
|
||||||
|
except IOError as e:
|
||||||
|
print(f"Error: Could not create zip file {zip_file_path}. Reason: {e}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"An unexpected error occurred: {e}")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
parser = argparse.ArgumentParser()
|
start_time = time.time()
|
||||||
parser.add_argument("--zip_name", type=str, default="VRCT.zip")
|
parser = argparse.ArgumentParser(description="Create a zip file from specified files and directories.")
|
||||||
parser.add_argument("--file_paths", type=str, nargs="*", default=["src-tauri/target/release/VRCT.exe", "src-tauri/target/release/VRCT-sidecar.exe"])
|
parser.add_argument("--zip_name", type=str, default="VRCT.zip", help="Name of the output zip file.")
|
||||||
parser.add_argument("--dir_paths", type=str, nargs="*", default=["src-tauri/target/release/_internal"])
|
parser.add_argument(
|
||||||
|
"--file_paths",
|
||||||
|
type=str,
|
||||||
|
nargs="*",
|
||||||
|
default=["src-tauri/target/release/VRCT.exe", "src-tauri/target/release/VRCT-sidecar.exe"],
|
||||||
|
help="List of file paths to include in the zip."
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--dir_paths",
|
||||||
|
type=str,
|
||||||
|
nargs="*",
|
||||||
|
default=["src-tauri/target/release/_internal"],
|
||||||
|
help="List of directory paths to include in the zip."
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-v", "--verbose",
|
||||||
|
action="store_true",
|
||||||
|
help="Increase output verbosity."
|
||||||
|
)
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
zip_files_and_directory(args.zip_name, args.file_paths, args.dir_paths)
|
zip_files_and_directory(args.zip_name, args.file_paths, args.dir_paths, args.verbose)
|
||||||
print("Complete!")
|
end_time = time.time()
|
||||||
|
processing_time = end_time - start_time
|
||||||
|
print(f"Complete! Processing time: {processing_time:.2f} seconds")
|
||||||
Reference in New Issue
Block a user