diff --git a/webui_controller.py b/webui_controller.py new file mode 100644 index 00000000..25bd65c9 --- /dev/null +++ b/webui_controller.py @@ -0,0 +1,1205 @@ +from time import sleep +from subprocess import Popen +from threading import Thread +from config import config +from model import model +from view import view +from utils import getKeyByValue, isUniqueStrings, strPctToInt +import argparse + +# Common +def callbackUpdateSoftware(func=None): + setMainWindowGeometry() + model.updateSoftware(restart=True, func=func) + +def callbackRestartSoftware(): + setMainWindowGeometry() + model.reStartSoftware() + +def callbackFilepathLogs(): + print("callbackFilepathLogs", config.PATH_LOGS.replace('/', '\\')) + Popen(['explorer', config.PATH_LOGS.replace('/', '\\')], shell=True) + +def callbackFilepathConfigFile(): + print("callbackFilepathConfigFile", config.PATH_LOCAL.replace('/', '\\')) + Popen(['explorer', config.PATH_LOCAL.replace('/', '\\')], shell=True) + +def callbackQuitVrct(): + setMainWindowGeometry() + +def callbackEnableEasterEgg(): + config.IS_EASTER_EGG_ENABLED = True + config.OVERLAY_UI_TYPE = "sakura" + view.printToTextbox_enableEasterEgg() + +def setMainWindowGeometry(): + PRE_SCALING_INT = strPctToInt(view.getPreUiScaling()) + NEW_SCALING_INT = strPctToInt(config.UI_SCALING) + MULTIPLY_FLOAT = (NEW_SCALING_INT / PRE_SCALING_INT) + main_window_geometry = view.getMainWindowGeometry(return_int=True) + main_window_geometry["width"] = str(int(main_window_geometry["width"] * MULTIPLY_FLOAT)) + main_window_geometry["height"] = str(int(main_window_geometry["height"] * MULTIPLY_FLOAT)) + main_window_geometry["x_pos"] = str(main_window_geometry["x_pos"]) + main_window_geometry["y_pos"] = str(main_window_geometry["y_pos"]) + config.MAIN_WINDOW_GEOMETRY = main_window_geometry + +def messageFormatter(format_type:str, translation, message): + if format_type == "RECEIVED": + FORMAT_WITH_T = config.RECEIVED_MESSAGE_FORMAT_WITH_T + FORMAT = config.RECEIVED_MESSAGE_FORMAT + elif format_type == "SEND": + FORMAT_WITH_T = config.SEND_MESSAGE_FORMAT_WITH_T + FORMAT = config.SEND_MESSAGE_FORMAT + else: + raise ValueError("format_type is not found", format_type) + + if len(translation) > 0: + osc_message = FORMAT_WITH_T.replace("[message]", message) + osc_message = osc_message.replace("[translation]", translation) + else: + osc_message = FORMAT.replace("[message]", message) + return osc_message + +def changeToCTranslate2Process(): + if config.CHOICE_INPUT_TRANSLATOR != "CTranslate2" or config.CHOICE_OUTPUT_TRANSLATOR != "CTranslate2": + config.CHOICE_INPUT_TRANSLATOR = "CTranslate2" + config.CHOICE_OUTPUT_TRANSLATOR = "CTranslate2" + updateTranslationEngineAndEngineList() + view.printToTextbox_TranslationEngineLimitError() + +# func transcription send message +def sendMicMessage(message): + if len(message) > 0: + addSentMessageLog(message) + translation = "" + if model.checkKeywords(message): + view.printToTextbox_DetectedByWordFilter(detected_message=message) + return + elif model.detectRepeatSendMessage(message): + return + elif config.ENABLE_TRANSLATION is False: + pass + else: + translation, success = model.getInputTranslate(message) + if success is False: + changeToCTranslate2Process() + + if config.ENABLE_TRANSCRIPTION_SEND is True: + if config.ENABLE_SEND_MESSAGE_TO_VRC is True: + if config.ENABLE_SEND_ONLY_TRANSLATED_MESSAGES is True: + if config.ENABLE_TRANSLATION is False: + osc_message = messageFormatter("SEND", "", message) + else: + osc_message = messageFormatter("SEND", "", translation) + else: + osc_message = messageFormatter("SEND", translation, message) + model.oscSendMessage(osc_message) + + + view.printToTextbox_SentMessage(message, translation) + if config.ENABLE_LOGGER is True: + if len(translation) > 0: + translation = f" ({translation})" + model.logger.info(f"[SENT] {message}{translation}") + + # if config.ENABLE_OVERLAY_SMALL_LOG is True: + # overlay_image = model.createOverlayImageShort(message, translation) + # model.updateOverlay(overlay_image) + # overlay_image = model.createOverlayImageLong("send", message, translation) + # model.updateOverlay(overlay_image) + +def startTranscriptionSendMessage(): + model.startMicTranscript(sendMicMessage, view.printToTextbox_TranscriptionSendNoDeviceError) + view.setMainWindowAllWidgetsStatusToNormal() + +def stopTranscriptionSendMessage(): + model.stopMicTranscript() + view.setMainWindowAllWidgetsStatusToNormal() + +def startThreadingTranscriptionSendMessage(): + view.printToTextbox_enableTranscriptionSend() + th_startTranscriptionSendMessage = Thread(target=startTranscriptionSendMessage) + th_startTranscriptionSendMessage.daemon = True + th_startTranscriptionSendMessage.start() + +def stopThreadingTranscriptionSendMessage(): + view.printToTextbox_disableTranscriptionSend() + th_stopTranscriptionSendMessage = Thread(target=stopTranscriptionSendMessage) + th_stopTranscriptionSendMessage.daemon = True + th_stopTranscriptionSendMessage.start() + +def startTranscriptionSendMessageOnCloseConfigWindow(): + model.startMicTranscript(sendMicMessage, view.printToTextbox_TranscriptionSendNoDeviceError) + +def stopTranscriptionSendMessageOnOpenConfigWindow(): + model.stopMicTranscript() + +def startThreadingTranscriptionSendMessageOnCloseConfigWindow(): + th_startTranscriptionSendMessage = Thread(target=startTranscriptionSendMessageOnCloseConfigWindow) + th_startTranscriptionSendMessage.daemon = True + th_startTranscriptionSendMessage.start() + +def stopThreadingTranscriptionSendMessageOnOpenConfigWindow(): + th_stopTranscriptionSendMessage = Thread(target=stopTranscriptionSendMessageOnOpenConfigWindow) + th_stopTranscriptionSendMessage.daemon = True + th_stopTranscriptionSendMessage.start() + +# func transcription receive message +def receiveSpeakerMessage(message): + if len(message) > 0: + translation = "" + if model.detectRepeatReceiveMessage(message): + return + elif config.ENABLE_TRANSLATION is False: + pass + else: + translation, success = model.getOutputTranslate(message) + if success is False: + changeToCTranslate2Process() + + if config.ENABLE_TRANSCRIPTION_RECEIVE is True: + if config.ENABLE_NOTICE_XSOVERLAY is True: + xsoverlay_message = messageFormatter("RECEIVED", translation, message) + model.notificationXSOverlay(xsoverlay_message) + + if config.ENABLE_OVERLAY_SMALL_LOG is True: + if model.overlay.initialized is True: + overlay_image = model.createOverlayImageShort(message, translation) + model.updateOverlay(overlay_image) + # overlay_image = model.createOverlayImageLong("receive", message, translation) + # model.updateOverlay(overlay_image) + + # ------------Speaker2Chatbox------------ + if config.ENABLE_SPEAKER2CHATBOX is True: + # send OSC message + if config.ENABLE_SEND_RECEIVED_MESSAGE_TO_VRC is True: + osc_message = messageFormatter("RECEIVED", translation, message) + model.oscSendMessage(osc_message) + # ------------Speaker2Chatbox------------ + + # update textbox message log (Received) + view.printToTextbox_ReceivedMessage(message, translation) + if config.ENABLE_LOGGER is True: + if len(translation) > 0: + translation = f" ({translation})" + model.logger.info(f"[RECEIVED] {message}{translation}") + +def startTranscriptionReceiveMessage(): + model.startSpeakerTranscript(receiveSpeakerMessage, view.printToTextbox_TranscriptionReceiveNoDeviceError) + view.setMainWindowAllWidgetsStatusToNormal() + +def stopTranscriptionReceiveMessage(): + model.stopSpeakerTranscript() + view.setMainWindowAllWidgetsStatusToNormal() + +def startThreadingTranscriptionReceiveMessage(): + view.printToTextbox_enableTranscriptionReceive() + th_startTranscriptionReceiveMessage = Thread(target=startTranscriptionReceiveMessage) + th_startTranscriptionReceiveMessage.daemon = True + th_startTranscriptionReceiveMessage.start() + +def stopThreadingTranscriptionReceiveMessage(): + view.printToTextbox_disableTranscriptionReceive() + th_stopTranscriptionReceiveMessage = Thread(target=stopTranscriptionReceiveMessage) + th_stopTranscriptionReceiveMessage.daemon = True + th_stopTranscriptionReceiveMessage.start() + +def startTranscriptionReceiveMessageOnCloseConfigWindow(): + model.startSpeakerTranscript(receiveSpeakerMessage, view.printToTextbox_TranscriptionReceiveNoDeviceError) + + +def stopTranscriptionReceiveMessageOnOpenConfigWindow(): + model.stopSpeakerTranscript() + +def startThreadingTranscriptionReceiveMessageOnCloseConfigWindow(): + th_startTranscriptionReceiveMessage = Thread(target=startTranscriptionReceiveMessageOnCloseConfigWindow) + th_startTranscriptionReceiveMessage.daemon = True + th_startTranscriptionReceiveMessage.start() + +def stopThreadingTranscriptionReceiveMessageOnOpenConfigWindow(): + th_stopTranscriptionReceiveMessage = Thread(target=stopTranscriptionReceiveMessageOnOpenConfigWindow) + th_stopTranscriptionReceiveMessage.daemon = True + th_stopTranscriptionReceiveMessage.start() + +# func message box +def sendChatMessage(message): + if len(message) > 0: + addSentMessageLog(message) + translation = "" + if config.ENABLE_TRANSLATION is False: + pass + else: + translation, success = model.getInputTranslate(message) + if success is False: + changeToCTranslate2Process() + + # send OSC message + if config.ENABLE_SEND_MESSAGE_TO_VRC is True: + if config.ENABLE_SEND_ONLY_TRANSLATED_MESSAGES is True: + if config.ENABLE_TRANSLATION is False: + osc_message = messageFormatter("SEND", "", message) + else: + osc_message = messageFormatter("SEND", "", translation) + else: + osc_message = messageFormatter("SEND", translation, message) + model.oscSendMessage(osc_message) + + # if config.ENABLE_OVERLAY_SMALL_LOG is True: + # overlay_image = model.createOverlayImageShort(message, translation) + # model.updateOverlay(overlay_image) + # overlay_image = model.createOverlayImageLong("send", message, translation) + # model.updateOverlay(overlay_image) + + # update textbox message log (Sent) + view.printToTextbox_SentMessage(message, translation) + if config.ENABLE_LOGGER is True: + if len(translation) > 0: + translation = f" ({translation})" + model.logger.info(f"[SENT] {message}{translation}") + + # delete message in entry message box + if config.ENABLE_AUTO_CLEAR_MESSAGE_BOX is True: + view.clearMessageBox() + +def messageBoxPressKeyEnter(): + model.oscStopSendTyping() + message = view.getTextFromMessageBox() + sendChatMessage(message) + +def messageBoxPressKeyAny(e): + if config.ENABLE_SEND_MESSAGE_TO_VRC is True: + model.oscStartSendTyping() + else: + model.oscStopSendTyping() + +def messageBoxFocusIn(e): + view.foregroundOffIfForegroundEnabled() + +def messageBoxFocusOut(e): + view.foregroundOnIfForegroundEnabled() + if config.ENABLE_SEND_MESSAGE_TO_VRC is True: + model.oscStopSendTyping() + +def addSentMessageLog(sent_message): + config.SENT_MESSAGES_LOG.append(sent_message) + config.CURRENT_SENT_MESSAGES_LOG_INDEX = len(config.SENT_MESSAGES_LOG) + +def updateMessageBox(index_offset): + if len(config.SENT_MESSAGES_LOG) == 0: + return + try: + new_index = config.CURRENT_SENT_MESSAGES_LOG_INDEX + index_offset + target_message_text = config.SENT_MESSAGES_LOG[new_index] + view.replaceMessageBox(target_message_text) + config.CURRENT_SENT_MESSAGES_LOG_INDEX = new_index + except IndexError: + pass + +def messageBoxUpKeyPress(): + if config.CURRENT_SENT_MESSAGES_LOG_INDEX > 0: + updateMessageBox(-1) + +def messageBoxDownKeyPress(): + if config.CURRENT_SENT_MESSAGES_LOG_INDEX < len(config.SENT_MESSAGES_LOG) - 1: + updateMessageBox(1) + +def updateTranslationEngineAndEngineList(): + engine = config.CHOICE_INPUT_TRANSLATOR + engines = model.findTranslationEngines(config.SOURCE_LANGUAGE, config.TARGET_LANGUAGE) + if engine not in engines: + engine = engines[0] + config.CHOICE_INPUT_TRANSLATOR = engine + config.CHOICE_OUTPUT_TRANSLATOR = engine + view.updateSelectableTranslationEngineList(engines) + view.setGuiVariable_SelectedTranslationEngine(engine) + +def initSetTranslateEngine(): + engine = config.SELECTED_TAB_YOUR_TRANSLATOR_ENGINES[config.SELECTED_TAB_NO] + config.CHOICE_INPUT_TRANSLATOR = engine + engine = config.SELECTED_TAB_TARGET_TRANSLATOR_ENGINES[config.SELECTED_TAB_NO] + config.CHOICE_OUTPUT_TRANSLATOR = engine + +def initSetLanguageAndCountry(): + select = config.SELECTED_TAB_YOUR_LANGUAGES[config.SELECTED_TAB_NO] + config.SOURCE_LANGUAGE = select["language"] + config.SOURCE_COUNTRY = select["country"] + select = config.SELECTED_TAB_TARGET_LANGUAGES[config.SELECTED_TAB_NO] + config.TARGET_LANGUAGE = select["language"] + config.TARGET_COUNTRY = select["country"] + +def setYourTranslateEngine(select): + engines = config.SELECTED_TAB_YOUR_TRANSLATOR_ENGINES + engines[config.SELECTED_TAB_NO] = select + config.SELECTED_TAB_YOUR_TRANSLATOR_ENGINES = engines + config.CHOICE_INPUT_TRANSLATOR = select + +def setTargetTranslateEngine(select): + engines = config.SELECTED_TAB_TARGET_TRANSLATOR_ENGINES + engines[config.SELECTED_TAB_NO] = select + config.SELECTED_TAB_TARGET_TRANSLATOR_ENGINES = engines + config.CHOICE_OUTPUT_TRANSLATOR = select + +def setYourLanguageAndCountry(select): + languages = config.SELECTED_TAB_YOUR_LANGUAGES + languages[config.SELECTED_TAB_NO] = select + config.SELECTED_TAB_YOUR_LANGUAGES = languages + config.SOURCE_LANGUAGE = select["language"] + config.SOURCE_COUNTRY = select["country"] + updateTranslationEngineAndEngineList() + view.printToTextbox_selectedYourLanguages(select) + +def setTargetLanguageAndCountry(select): + languages = config.SELECTED_TAB_TARGET_LANGUAGES + languages[config.SELECTED_TAB_NO] = select + config.SELECTED_TAB_TARGET_LANGUAGES = languages + config.TARGET_LANGUAGE = select["language"] + config.TARGET_COUNTRY = select["country"] + updateTranslationEngineAndEngineList() + view.printToTextbox_selectedTargetLanguages(select) + +def swapYourLanguageAndTargetLanguage(): + your_language = config.SELECTED_TAB_YOUR_LANGUAGES[config.SELECTED_TAB_NO] + target_language = config.SELECTED_TAB_TARGET_LANGUAGES[config.SELECTED_TAB_NO] + setYourLanguageAndCountry(target_language) + setTargetLanguageAndCountry(your_language) + # Update Selected Languages for UI + view.updateGuiVariableByPresetTabNo(config.SELECTED_TAB_NO) + + +def callbackSelectedLanguagePresetTab(selected_tab_no): + config.SELECTED_TAB_NO = selected_tab_no + view.updateGuiVariableByPresetTabNo(config.SELECTED_TAB_NO) + + engines = config.SELECTED_TAB_YOUR_TRANSLATOR_ENGINES + engine = engines[config.SELECTED_TAB_NO] + config.CHOICE_INPUT_TRANSLATOR = engine + + engines = config.SELECTED_TAB_TARGET_TRANSLATOR_ENGINES + engine = engines[config.SELECTED_TAB_NO] + config.CHOICE_OUTPUT_TRANSLATOR = engine + + languages = config.SELECTED_TAB_YOUR_LANGUAGES + select = languages[config.SELECTED_TAB_NO] + config.SOURCE_LANGUAGE = select["language"] + config.SOURCE_COUNTRY = select["country"] + + languages = config.SELECTED_TAB_TARGET_LANGUAGES + select = languages[config.SELECTED_TAB_NO] + config.TARGET_LANGUAGE = select["language"] + config.TARGET_COUNTRY = select["country"] + view.printToTextbox_changedLanguagePresetTab(config.SELECTED_TAB_NO) + updateTranslationEngineAndEngineList() + +def callbackSelectedTranslationEngine(selected_translation_engine): + print("callbackSelectedTranslationEngine", selected_translation_engine) + setYourTranslateEngine(selected_translation_engine) + setTargetTranslateEngine(selected_translation_engine) + view.setGuiVariable_SelectedTranslationEngine(config.CHOICE_OUTPUT_TRANSLATOR) + +# command func +def callbackToggleTranslation(is_turned_on): + config.ENABLE_TRANSLATION = is_turned_on + if config.ENABLE_TRANSLATION is True: + if model.isLoadedCTranslate2Model() is False: + model.changeTranslatorCTranslate2Model() + view.printToTextbox_enableTranslation() + else: + view.printToTextbox_disableTranslation() + +def callbackToggleTranscriptionSend(is_turned_on): + view.setMainWindowAllWidgetsStatusToDisabled() + config.ENABLE_TRANSCRIPTION_SEND = is_turned_on + if config.ENABLE_TRANSCRIPTION_SEND is True: + startThreadingTranscriptionSendMessage() + view.changeTranscriptionDisplayStatus("MIC_ON") + else: + stopThreadingTranscriptionSendMessage() + view.changeTranscriptionDisplayStatus("MIC_OFF") + +def callbackToggleTranscriptionReceive(is_turned_on): + view.setMainWindowAllWidgetsStatusToDisabled() + config.ENABLE_TRANSCRIPTION_RECEIVE = is_turned_on + if config.ENABLE_TRANSCRIPTION_RECEIVE is True: + startThreadingTranscriptionReceiveMessage() + view.changeTranscriptionDisplayStatus("SPEAKER_ON") + else: + stopThreadingTranscriptionReceiveMessage() + view.changeTranscriptionDisplayStatus("SPEAKER_OFF") + + if config.ENABLE_TRANSCRIPTION_RECEIVE is True and config.ENABLE_OVERLAY_SMALL_LOG is True: + if model.overlay.initialized is False and model.overlay.checkSteamvrRunning() is True: + model.startOverlay() + elif config.ENABLE_TRANSCRIPTION_RECEIVE is False: + pass + +def callbackToggleForeground(is_turned_on): + config.ENABLE_FOREGROUND = is_turned_on + if config.ENABLE_FOREGROUND is True: + view.printToTextbox_enableForeground() + view.foregroundOn() + else: + view.printToTextbox_disableForeground() + view.foregroundOff() + +def callbackEnableMainWindowSidebarCompactMode(): + config.IS_MAIN_WINDOW_SIDEBAR_COMPACT_MODE = True + view.enableMainWindowSidebarCompactMode() + +def callbackDisableMainWindowSidebarCompactMode(): + config.IS_MAIN_WINDOW_SIDEBAR_COMPACT_MODE = False + view.disableMainWindowSidebarCompactMode() + +# Config Window +def callbackOpenConfigWindow(): + view.setMainWindowAllWidgetsStatusToDisabled() + if config.ENABLE_TRANSCRIPTION_SEND is True: + stopThreadingTranscriptionSendMessageOnOpenConfigWindow() + if config.ENABLE_TRANSCRIPTION_RECEIVE is True: + stopThreadingTranscriptionReceiveMessageOnOpenConfigWindow() + if config.ENABLE_FOREGROUND is True: + view.foregroundOff() + +def callbackCloseConfigWindow(): + model.stopCheckMicEnergy() + model.stopCheckSpeakerEnergy() + view.initMicThresholdCheckButton() + view.initSpeakerThresholdCheckButton() + + if config.ENABLE_TRANSCRIPTION_SEND is True: + startThreadingTranscriptionSendMessageOnCloseConfigWindow() + if config.ENABLE_TRANSCRIPTION_RECEIVE is True: + sleep(2) + if config.ENABLE_TRANSCRIPTION_RECEIVE is True: + startThreadingTranscriptionReceiveMessageOnCloseConfigWindow() + if config.ENABLE_FOREGROUND is True: + view.foregroundOn() + view.setMainWindowAllWidgetsStatusToNormal() + +# Compact Mode Switch +def callbackEnableConfigWindowCompactMode(): + config.IS_CONFIG_WINDOW_COMPACT_MODE = True + model.stopCheckMicEnergy() + view.initMicThresholdCheckButton() + model.stopCheckSpeakerEnergy() + view.initSpeakerThresholdCheckButton() + + view.enableConfigWindowCompactMode() + +def callbackDisableConfigWindowCompactMode(): + config.IS_CONFIG_WINDOW_COMPACT_MODE = False + model.stopCheckMicEnergy() + view.initMicThresholdCheckButton() + model.stopCheckSpeakerEnergy() + view.initSpeakerThresholdCheckButton() + + view.disableConfigWindowCompactMode() + +# Appearance Tab +def callbackSetTransparency(value): + print("callbackSetTransparency", int(value)) + config.TRANSPARENCY = int(value) + view.setMainWindowTransparency(config.TRANSPARENCY/100) + +def callbackSetAppearance(value): + print("callbackSetAppearance", value) + config.APPEARANCE_THEME = value + view.showRestartButtonIfRequired() + +def callbackSetUiScaling(value): + print("callbackSetUiScaling", value) + config.UI_SCALING = value + new_scaling_float = strPctToInt(value) / 100 + print("callbackSetUiScaling_new_scaling_float", new_scaling_float) + view.showRestartButtonIfRequired() + +def callbackSetTextboxUiScaling(value): + print("callbackSetTextboxUiScaling", int(value)) + config.TEXTBOX_UI_SCALING = int(value) + view.setMainWindowTextboxUiSize(config.TEXTBOX_UI_SCALING/100) + +def callbackSetMessageBoxRatio(value): + print("callbackSetMessageBoxRatio", int(value)) + config.MESSAGE_BOX_RATIO = int(value) + view.setMainWindowMessageBoxRatio(config.MESSAGE_BOX_RATIO) + +def callbackSetFontFamily(value): + print("callbackSetFontFamily", value) + config.FONT_FAMILY = value + view.showRestartButtonIfRequired() + +def callbackSetUiLanguage(value): + print("callbackSetUiLanguage", value) + value = getKeyByValue(config.SELECTABLE_UI_LANGUAGES_DICT, value) + print("callbackSetUiLanguage__after_getKeyByValue", value) + config.UI_LANGUAGE = value + view.showRestartButtonIfRequired(locale=config.UI_LANGUAGE) + +def callbackSetEnableRestoreMainWindowGeometry(value): + print("callbackSetEnableRestoreMainWindowGeometry", value) + config.ENABLE_RESTORE_MAIN_WINDOW_GEOMETRY = value + +# Translation Tab +def callbackSetUseTranslationFeature(value): + print("callbackSetUseTranslationFeature", value) + config.USE_TRANSLATION_FEATURE = value + if config.USE_TRANSLATION_FEATURE is True: + view.useTranslationFeatureProcess("Normal") + if model.checkCTranslatorCTranslate2ModelWeight(): + config.IS_RESET_BUTTON_DISPLAYED_FOR_TRANSLATION = False + def callback(): + model.changeTranslatorCTranslate2Model() + th_callback = Thread(target=callback) + th_callback.daemon = True + th_callback.start() + else: + config.IS_RESET_BUTTON_DISPLAYED_FOR_TRANSLATION = True + view.useTranslationFeatureProcess("Restart") + else: + config.IS_RESET_BUTTON_DISPLAYED_FOR_TRANSLATION = False + view.useTranslationFeatureProcess("Disable") + view.showRestartButtonIfRequired() + +def callbackSetCtranslate2WeightType(value): + print("callbackSetCtranslate2WeightType", value) + config.CTRANSLATE2_WEIGHT_TYPE = str(value) + view.updateSelectedCtranslate2WeightType(config.CTRANSLATE2_WEIGHT_TYPE) + view.setWidgetsStatus_changeWeightType_Pending() + if model.checkCTranslatorCTranslate2ModelWeight(): + config.IS_RESET_BUTTON_DISPLAYED_FOR_TRANSLATION = False + def callback(): + model.changeTranslatorCTranslate2Model() + view.useTranslationFeatureProcess("Normal") + view.setWidgetsStatus_changeWeightType_Done() + th_callback = Thread(target=callback) + th_callback.daemon = True + th_callback.start() + else: + config.IS_RESET_BUTTON_DISPLAYED_FOR_TRANSLATION = True + view.useTranslationFeatureProcess("Restart") + view.setWidgetsStatus_changeWeightType_Done() + view.showRestartButtonIfRequired() + +def callbackSetDeeplAuthKey(value): + print("callbackSetDeeplAuthKey", str(value)) + view.clearNotificationMessage() + if len(value) == 36 or len(value) == 39: + result = model.authenticationTranslatorDeepLAuthKey(auth_key=value) + if result is True: + key = value + view.printToTextbox_AuthenticationSuccess() + view.showSuccessMessage_DeeplAuthKey() + else: + key = None + view.printToTextbox_AuthenticationError() + view.showErrorMessage_DeeplAuthKey() + auth_keys = config.AUTH_KEYS + auth_keys["DeepL_API"] = key + config.AUTH_KEYS = auth_keys + elif len(value) == 0: + auth_keys = config.AUTH_KEYS + auth_keys["DeepL_API"] = None + config.AUTH_KEYS = auth_keys + updateTranslationEngineAndEngineList() + +# Transcription Tab +# Transcription (Mic) +def callbackSetMicHost(value): + print("callbackSetMicHost", value) + config.CHOICE_MIC_HOST = value + config.CHOICE_MIC_DEVICE = model.getInputDefaultDevice() + + view.updateSelected_MicDevice(config.CHOICE_MIC_DEVICE) + view.updateList_MicDevice(model.getListInputDevice()) + + model.stopCheckMicEnergy() + view.replaceMicThresholdCheckButton_Passive() + +def callbackSetMicDevice(value): + print("callbackSetMicDevice", value) + config.CHOICE_MIC_DEVICE = value + + model.stopCheckMicEnergy() + view.replaceMicThresholdCheckButton_Passive() + +def callbackSetMicEnergyThreshold(value): + print("callbackSetMicEnergyThreshold", value) + if value == "": + return + try: + value = int(value) + if 0 <= value and value <= config.MAX_MIC_ENERGY_THRESHOLD: + view.clearNotificationMessage() + config.INPUT_MIC_ENERGY_THRESHOLD = value + view.setGuiVariable_MicEnergyThreshold(config.INPUT_MIC_ENERGY_THRESHOLD) + else: + raise ValueError() + except Exception: + view.showErrorMessage_MicEnergyThreshold() + +def callbackSetMicDynamicEnergyThreshold(value): + print("callbackSetMicDynamicEnergyThreshold", value) + config.INPUT_MIC_DYNAMIC_ENERGY_THRESHOLD = value + if config.INPUT_MIC_DYNAMIC_ENERGY_THRESHOLD is True: + view.closeMicEnergyThresholdWidget() + else: + view.openMicEnergyThresholdWidget() + +def setProgressBarMicEnergy(energy): + view.updateSetProgressBar_MicEnergy(energy) + +def callbackCheckMicThreshold(is_turned_on): + print("callbackCheckMicThreshold", is_turned_on) + if is_turned_on is True: + view.replaceMicThresholdCheckButton_Disabled() + model.startCheckMicEnergy(setProgressBarMicEnergy, view.initProgressBar_MicEnergy) + view.replaceMicThresholdCheckButton_Active() + else: + view.replaceMicThresholdCheckButton_Disabled() + model.stopCheckMicEnergy() + view.replaceMicThresholdCheckButton_Passive() + +def callbackSetMicRecordTimeout(value): + print("callbackSetMicRecordTimeout", value) + if value == "": + return + try: + value = int(value) + if 0 <= value and value <= config.INPUT_MIC_PHRASE_TIMEOUT: + view.clearNotificationMessage() + config.INPUT_MIC_RECORD_TIMEOUT = value + view.setGuiVariable_MicRecordTimeout(config.INPUT_MIC_RECORD_TIMEOUT) + else: + raise ValueError() + except Exception: + view.showErrorMessage_MicRecordTimeout() + +def callbackSetMicPhraseTimeout(value): + print("callbackSetMicPhraseTimeout", value) + if value == "": + return + try: + value = int(value) + if 0 <= value and value >= config.INPUT_MIC_RECORD_TIMEOUT: + view.clearNotificationMessage() + config.INPUT_MIC_PHRASE_TIMEOUT = value + view.setGuiVariable_MicPhraseTimeout(config.INPUT_MIC_PHRASE_TIMEOUT) + else: + raise ValueError() + except Exception: + view.showErrorMessage_MicPhraseTimeout() + +def callbackSetMicMaxPhrases(value): + print("callbackSetMicMaxPhrases", value) + if value == "": + return + try: + value = int(value) + if 0 <= value: + view.clearNotificationMessage() + config.INPUT_MIC_MAX_PHRASES = value + view.setGuiVariable_MicMaxPhrases(config.INPUT_MIC_MAX_PHRASES) + else: + raise ValueError() + except Exception: + view.showErrorMessage_MicMaxPhrases() + +def callbackSetMicWordFilter(values): + print("callbackSetMicWordFilter", values) + values = str(values) + values = [w.strip() for w in values.split(",") if len(w.strip()) > 0] + # Copy the list + new_input_mic_word_filter_list = config.INPUT_MIC_WORD_FILTER + new_added_value = [] + for value in values: + if value in new_input_mic_word_filter_list: + # If the value is already in the list, do nothing. + pass + else: + new_input_mic_word_filter_list.append(value) + new_added_value.append(value) + config.INPUT_MIC_WORD_FILTER = new_input_mic_word_filter_list + + view.addValueToList_WordFilter(new_added_value) + view.clearEntryBox_WordFilter() + view.setLatestConfigVariable("MicMicWordFilter") + + model.resetKeywordProcessor() + model.addKeywords() + +def callbackDeleteMicWordFilter(value): + print("callbackDeleteMicWordFilter", value) + try: + new_input_mic_word_filter_list = config.INPUT_MIC_WORD_FILTER + new_input_mic_word_filter_list.remove(str(value)) + config.INPUT_MIC_WORD_FILTER = new_input_mic_word_filter_list + view.setLatestConfigVariable("MicMicWordFilter") + model.resetKeywordProcessor() + model.addKeywords() + except Exception: + print("There was no the target word in config.INPUT_MIC_WORD_FILTER") + +# Transcription (Speaker) +def callbackSetSpeakerDevice(value): + print("callbackSetSpeakerDevice", value) + config.CHOICE_SPEAKER_DEVICE = value + + model.stopCheckSpeakerEnergy() + view.replaceSpeakerThresholdCheckButton_Passive() + +def callbackSetSpeakerEnergyThreshold(value): + print("callbackSetSpeakerEnergyThreshold", value) + if value == "": + return + try: + value = int(value) + if 0 <= value and value <= config.MAX_SPEAKER_ENERGY_THRESHOLD: + view.clearNotificationMessage() + config.INPUT_SPEAKER_ENERGY_THRESHOLD = value + view.setGuiVariable_SpeakerEnergyThreshold(config.INPUT_SPEAKER_ENERGY_THRESHOLD) + else: + raise ValueError() + except Exception: + view.showErrorMessage_SpeakerEnergyThreshold() + +def callbackSetSpeakerDynamicEnergyThreshold(value): + print("callbackSetSpeakerDynamicEnergyThreshold", value) + config.INPUT_SPEAKER_DYNAMIC_ENERGY_THRESHOLD = value + if config.INPUT_SPEAKER_DYNAMIC_ENERGY_THRESHOLD is True: + view.closeSpeakerEnergyThresholdWidget() + else: + view.openSpeakerEnergyThresholdWidget() + +def setProgressBarSpeakerEnergy(energy): + view.updateSetProgressBar_SpeakerEnergy(energy) + +def callbackCheckSpeakerThreshold(is_turned_on): + print("callbackCheckSpeakerThreshold", is_turned_on) + if is_turned_on is True: + view.replaceSpeakerThresholdCheckButton_Disabled() + model.startCheckSpeakerEnergy( + setProgressBarSpeakerEnergy, + view.initProgressBar_SpeakerEnergy, + view.showErrorMessage_CheckSpeakerThreshold_NoDevice + ) + + view.replaceSpeakerThresholdCheckButton_Active() + else: + view.replaceSpeakerThresholdCheckButton_Disabled() + model.stopCheckSpeakerEnergy() + view.replaceSpeakerThresholdCheckButton_Passive() + +def callbackSetSpeakerRecordTimeout(value): + print("callbackSetSpeakerRecordTimeout", value) + if value == "": + return + try: + value = int(value) + if 0 <= value and value <= config.INPUT_SPEAKER_PHRASE_TIMEOUT: + view.clearNotificationMessage() + config.INPUT_SPEAKER_RECORD_TIMEOUT = value + view.setGuiVariable_SpeakerRecordTimeout(config.INPUT_SPEAKER_RECORD_TIMEOUT) + else: + raise ValueError() + except Exception: + view.showErrorMessage_SpeakerRecordTimeout() + +def callbackSetSpeakerPhraseTimeout(value): + print("callbackSetSpeakerPhraseTimeout", value) + if value == "": + return + try: + value = int(value) + if 0 <= value and value >= config.INPUT_SPEAKER_RECORD_TIMEOUT: + view.clearNotificationMessage() + config.INPUT_SPEAKER_PHRASE_TIMEOUT = value + view.setGuiVariable_SpeakerPhraseTimeout(config.INPUT_SPEAKER_PHRASE_TIMEOUT) + else: + raise ValueError() + except Exception: + view.showErrorMessage_SpeakerPhraseTimeout() + +def callbackSetSpeakerMaxPhrases(value): + print("callbackSetSpeakerMaxPhrases", value) + if value == "": + return + try: + value = int(value) + if 0 <= value: + view.clearNotificationMessage() + config.INPUT_SPEAKER_MAX_PHRASES = value + view.setGuiVariable_SpeakerMaxPhrases(config.INPUT_SPEAKER_MAX_PHRASES) + else: + raise ValueError() + except Exception: + view.showErrorMessage_SpeakerMaxPhrases() + +# Transcription (Internal AI Model) +def callbackSetUserWhisperFeature(value): + print("callbackSetUserWhisperFeature", value) + config.USE_WHISPER_FEATURE = value + if config.USE_WHISPER_FEATURE is True: + view.openWhisperWeightTypeWidget() + if model.checkTranscriptionWhisperModelWeight() is True: + config.IS_RESET_BUTTON_DISPLAYED_FOR_WHISPER = False + config.SELECTED_TRANSCRIPTION_ENGINE = "Whisper" + else: + config.IS_RESET_BUTTON_DISPLAYED_FOR_WHISPER = True + config.SELECTED_TRANSCRIPTION_ENGINE = "Google" + else: + view.closeWhisperWeightTypeWidget() + config.IS_RESET_BUTTON_DISPLAYED_FOR_WHISPER = False + config.SELECTED_TRANSCRIPTION_ENGINE = "Google" + view.showRestartButtonIfRequired() + +def callbackSetWhisperWeightType(value): + print("callbackSetWhisperWeightType", value) + config.WHISPER_WEIGHT_TYPE = str(value) + view.updateSelectedWhisperWeightType(config.WHISPER_WEIGHT_TYPE) + if model.checkTranscriptionWhisperModelWeight() is True: + config.IS_RESET_BUTTON_DISPLAYED_FOR_WHISPER = False + config.SELECTED_TRANSCRIPTION_ENGINE = "Whisper" + else: + config.IS_RESET_BUTTON_DISPLAYED_FOR_WHISPER = True + config.SELECTED_TRANSCRIPTION_ENGINE = "Google" + view.showRestartButtonIfRequired() + +# VR Tab +def callbackSetOverlaySettings(value, set_type:str): + print("callbackSetOverlaySettings", value, set_type) + pre_settings = config.OVERLAY_SETTINGS + pre_settings[set_type] = value + config.OVERLAY_SETTINGS = pre_settings + match (set_type): + case "opacity": + model.updateOverlayImageOpacity() + case "ui_scaling": + model.updateOverlayImageUiScaling() + +def callbackSetEnableOverlaySmallLog(value): + print("callbackSetEnableOverlaySmallLog", value) + config.ENABLE_OVERLAY_SMALL_LOG = value + + if config.ENABLE_OVERLAY_SMALL_LOG is True and config.ENABLE_TRANSCRIPTION_RECEIVE is True: + if model.overlay.initialized is False and model.overlay.checkSteamvrRunning() is True: + model.startOverlay() + elif config.ENABLE_OVERLAY_SMALL_LOG is False: + model.clearOverlayImage() + model.shutdownOverlay() + + if config.ENABLE_OVERLAY_SMALL_LOG is True: + view.setStateOverlaySmallLog("enabled") + elif config.ENABLE_OVERLAY_SMALL_LOG is False: + view.setStateOverlaySmallLog("disabled") + +def callbackSetOverlaySmallLogSettings(value, set_type:str): + print("callbackSetOverlaySmallLogSettings", value, set_type) + pre_settings = config.OVERLAY_SMALL_LOG_SETTINGS + pre_settings[set_type] = value + config.OVERLAY_SMALL_LOG_SETTINGS = pre_settings + match (set_type): + case "x_pos" | "y_pos" | "z_pos" | "x_rotation" | "y_rotation" | "z_rotation": + model.updateOverlayPosition() + case "display_duration" | "fadeout_duration": + model.updateOverlayTimes() + +# Others Tab +def callbackSetEnableAutoClearMessageBox(value): + print("callbackSetEnableAutoClearMessageBox", value) + config.ENABLE_AUTO_CLEAR_MESSAGE_BOX = value + +def callbackSetEnableSendOnlyTranslatedMessages(value): + print("callbackSetEnableSendOnlyTranslatedMessages", value) + config.ENABLE_SEND_ONLY_TRANSLATED_MESSAGES = value + +def callbackSetSendMessageButtonType(value): + print("callbackSetSendMessageButtonType", value) + config.SEND_MESSAGE_BUTTON_TYPE = value + view.changeMainWindowSendMessageButton(config.SEND_MESSAGE_BUTTON_TYPE) + +def callbackSetEnableNoticeXsoverlay(value): + print("callbackSetEnableNoticeXsoverlay", value) + config.ENABLE_NOTICE_XSOVERLAY = value + +def callbackSetEnableAutoExportMessageLogs(value): + print("callbackSetEnableAutoExportMessageLogs", value) + config.ENABLE_LOGGER = value + + if config.ENABLE_LOGGER is True: + model.startLogger() + else: + model.stopLogger() + +def callbackSetEnableVrcMicMuteSync(value): + print("callbackSetEnableVrcMicMuteSync", value) + config.ENABLE_VRC_MIC_MUTE_SYNC = value + if config.ENABLE_VRC_MIC_MUTE_SYNC is True: + model.startCheckMuteSelfStatus() + view.setStateVrcMicMuteSync("enabled") + else: + model.stopCheckMuteSelfStatus() + view.setStateVrcMicMuteSync("disabled") + model.changeMicTranscriptStatus() + + +def callbackSetEnableSendMessageToVrc(value): + print("callbackSetEnableSendMessageToVrc", value) + config.ENABLE_SEND_MESSAGE_TO_VRC = value + +# Others (Message Formats(Send) +def callbackSetSendMessageFormat(value): + print("callbackSetSendMessageFormat", value) + if isUniqueStrings(["[message]"], value) is True: + config.SEND_MESSAGE_FORMAT = value + view.clearNotificationMessage() + view.setSendMessageFormat_EntryWidgets(config.SEND_MESSAGE_FORMAT) + else: + view.showErrorMessage_SendMessageFormat() + view.setSendMessageFormat_EntryWidgets(config.SEND_MESSAGE_FORMAT) + +def callbackSetSendMessageFormatWithT(value): + print("callbackSetSendMessageFormatWithT", value) + if len(value) > 0: + if isUniqueStrings(["[message]", "[translation]"], value) is True: + config.SEND_MESSAGE_FORMAT_WITH_T = value + view.clearNotificationMessage() + view.setSendMessageFormatWithT_EntryWidgets(config.SEND_MESSAGE_FORMAT_WITH_T) + else: + view.showErrorMessage_SendMessageFormatWithT() + view.setSendMessageFormatWithT_EntryWidgets(config.SEND_MESSAGE_FORMAT_WITH_T) + +# Others (Message Formats(Received) +def callbackSetReceivedMessageFormat(value): + print("callbackSetReceivedMessageFormat", value) + if isUniqueStrings(["[message]"], value) is True: + config.RECEIVED_MESSAGE_FORMAT = value + view.clearNotificationMessage() + view.setReceivedMessageFormat_EntryWidgets(config.RECEIVED_MESSAGE_FORMAT) + else: + view.showErrorMessage_ReceivedMessageFormat() + view.setReceivedMessageFormat_EntryWidgets(config.RECEIVED_MESSAGE_FORMAT) + +def callbackSetReceivedMessageFormatWithT(value): + print("callbackSetReceivedMessageFormatWithT", value) + if len(value) > 0: + if isUniqueStrings(["[message]", "[translation]"], value) is True: + config.RECEIVED_MESSAGE_FORMAT_WITH_T = value + view.clearNotificationMessage() + view.setReceivedMessageFormatWithT_EntryWidgets(config.RECEIVED_MESSAGE_FORMAT_WITH_T) + else: + view.showErrorMessage_ReceivedMessageFormatWithT() + view.setReceivedMessageFormatWithT_EntryWidgets(config.RECEIVED_MESSAGE_FORMAT_WITH_T) + +# ---------------------Speaker2Chatbox--------------------- +def callbackSetEnableSendReceivedMessageToVrc(value): + print("callbackSetEnableSendReceivedMessageToVrc", value) + config.ENABLE_SEND_RECEIVED_MESSAGE_TO_VRC = value +# ---------------------Speaker2Chatbox--------------------- + +# Advanced Settings Tab +def callbackSetOscIpAddress(value): + if value == "": + return + print("callbackSetOscIpAddress", str(value)) + config.OSC_IP_ADDRESS = str(value) + +def callbackSetOscPort(value): + if value == "": + return + print("callbackSetOscPort", int(value)) + config.OSC_PORT = int(value) + + +def getListLanguageAndCountry(): + return model.getListLanguageAndCountry() + +def getListInputHost(): + return model.getListInputHost() + +def getListInputDevice(): + return model.getListInputDevice() + +def getListOutputDevice(): + return model.getListOutputDevice() + + +def initSetConfigByExeArguments(): + parser = argparse.ArgumentParser() + parser.add_argument("--ip") + parser.add_argument("--port") + args = parser.parse_args() + if args.ip is not None: + config.OSC_IP_ADDRESS = str(args.ip) + view.setGuiVariable_OscIpAddress(config.OSC_IP_ADDRESS) + if args.port is not None: + config.OSC_PORT = int(args.port) + view.setGuiVariable_OscPort(config.OSC_PORT) + +def createMainWindow(splash): + splash.toProgress(1) + # create GUI + view.createGUI() + splash.toProgress(2) + + # init config + initSetConfigByExeArguments() + initSetTranslateEngine() + initSetLanguageAndCountry() + + if config.AUTH_KEYS["DeepL_API"] is not None: + if model.authenticationTranslatorDeepLAuthKey(auth_key=config.AUTH_KEYS["DeepL_API"]) is False: + # error update Auth key + auth_keys = config.AUTH_KEYS + auth_keys["DeepL_API"] = None + config.AUTH_KEYS = auth_keys + view.printToTextbox_AuthenticationError() + + # set Translation Engine + updateTranslationEngineAndEngineList() + + # set Transcription Engine + if config.USE_WHISPER_FEATURE is True: + config.SELECTED_TRANSCRIPTION_ENGINE = "Whisper" + else: + config.SELECTED_TRANSCRIPTION_ENGINE = "Google" + + # set word filter + model.addKeywords() + + # check Software Updated + if model.checkSoftwareUpdated() is True: + view.showUpdateAvailableButton() + + # init logger + if config.ENABLE_LOGGER is True: + model.startLogger() + + # init OSC receive + model.startReceiveOSC() + if config.ENABLE_VRC_MIC_MUTE_SYNC is True: + model.startCheckMuteSelfStatus() + + splash.toProgress(3) # Last one. + + # set UI and callback + view.register( + common_registers={ + "callback_enable_easter_egg": callbackEnableEasterEgg, + + "callback_update_software": callbackUpdateSoftware, + "callback_restart_software": callbackRestartSoftware, + "callback_filepath_logs": callbackFilepathLogs, + "callback_filepath_config_file": callbackFilepathConfigFile, + "callback_quit_vrct": callbackQuitVrct, + }, + + window_action_registers={ + "callback_open_config_window": callbackOpenConfigWindow, + "callback_close_config_window": callbackCloseConfigWindow, + }, + + main_window_registers={ + "callback_enable_main_window_sidebar_compact_mode": callbackEnableMainWindowSidebarCompactMode, + "callback_disable_main_window_sidebar_compact_mode": callbackDisableMainWindowSidebarCompactMode, + + "callback_toggle_translation": callbackToggleTranslation, + "callback_toggle_transcription_send": callbackToggleTranscriptionSend, + "callback_toggle_transcription_receive": callbackToggleTranscriptionReceive, + "callback_toggle_foreground": callbackToggleForeground, + + "callback_your_language": setYourLanguageAndCountry, + "callback_target_language": setTargetLanguageAndCountry, + "values": model.getListLanguageAndCountry(), + "callback_swap_languages": swapYourLanguageAndTargetLanguage, + + "callback_selected_language_preset_tab": callbackSelectedLanguagePresetTab, + + "callback_selected_translation_engine": callbackSelectedTranslationEngine, + + "message_box_bind_Return": messageBoxPressKeyEnter, + "message_box_bind_Any_KeyPress": messageBoxPressKeyAny, + "message_box_bind_FocusIn": messageBoxFocusIn, + "message_box_bind_FocusOut": messageBoxFocusOut, + "message_box_bind_Up_KeyPress": messageBoxUpKeyPress, + "message_box_bind_Down_KeyPress": messageBoxDownKeyPress, + }, + + config_window_registers={ + # Compact Mode Switch + "callback_disable_config_window_compact_mode": callbackEnableConfigWindowCompactMode, + "callback_enable_config_window_compact_mode": callbackDisableConfigWindowCompactMode, + + # Appearance Tab + "callback_set_transparency": callbackSetTransparency, + "callback_set_appearance": callbackSetAppearance, + "callback_set_ui_scaling": callbackSetUiScaling, + "callback_set_textbox_ui_scaling": callbackSetTextboxUiScaling, + "callback_set_message_box_ratio": callbackSetMessageBoxRatio, + "callback_set_font_family": callbackSetFontFamily, + "callback_set_ui_language": callbackSetUiLanguage, + "callback_set_enable_restore_main_window_geometry": callbackSetEnableRestoreMainWindowGeometry, + + # Translation Tab + "callback_set_use_translation_feature": callbackSetUseTranslationFeature, + "callback_set_ctranslate2_weight_type": callbackSetCtranslate2WeightType, + "callback_set_deepl_auth_key": callbackSetDeeplAuthKey, + + # Transcription Tab (Mic) + "callback_set_mic_host": callbackSetMicHost, + "list_mic_host": model.getListInputHost(), + "callback_set_mic_device": callbackSetMicDevice, + "list_mic_device": model.getListInputDevice(), + "callback_set_mic_energy_threshold": callbackSetMicEnergyThreshold, + "callback_set_mic_dynamic_energy_threshold": callbackSetMicDynamicEnergyThreshold, + "callback_check_mic_threshold": callbackCheckMicThreshold, + "callback_set_mic_record_timeout": callbackSetMicRecordTimeout, + "callback_set_mic_phrase_timeout": callbackSetMicPhraseTimeout, + "callback_set_mic_max_phrases": callbackSetMicMaxPhrases, + "callback_set_mic_word_filter": callbackSetMicWordFilter, + "callback_delete_mic_word_filter": callbackDeleteMicWordFilter, + + # Transcription Tab (Speaker) + "callback_set_speaker_device": callbackSetSpeakerDevice, + "list_speaker_device": model.getListOutputDevice(), + "callback_set_speaker_energy_threshold": callbackSetSpeakerEnergyThreshold, + "callback_set_speaker_dynamic_energy_threshold": callbackSetSpeakerDynamicEnergyThreshold, + "callback_check_speaker_threshold": callbackCheckSpeakerThreshold, + "callback_set_speaker_record_timeout": callbackSetSpeakerRecordTimeout, + "callback_set_speaker_phrase_timeout": callbackSetSpeakerPhraseTimeout, + "callback_set_speaker_max_phrases": callbackSetSpeakerMaxPhrases, + + # Transcription Tab (Internal AI Model) + "callback_set_use_whisper_feature": callbackSetUserWhisperFeature, + "callback_set_whisper_weight_type": callbackSetWhisperWeightType, + + # VR Tab + "callback_set_overlay_settings": callbackSetOverlaySettings, + "callback_set_enable_overlay_small_log": callbackSetEnableOverlaySmallLog, + "callback_set_overlay_small_log_settings": callbackSetOverlaySmallLogSettings, + + # Others Tab + "callback_set_enable_auto_clear_chatbox": callbackSetEnableAutoClearMessageBox, + "callback_set_send_only_translated_messages": callbackSetEnableSendOnlyTranslatedMessages, + "callback_set_send_message_button_type": callbackSetSendMessageButtonType, + "callback_set_enable_notice_xsoverlay": callbackSetEnableNoticeXsoverlay, + "callback_set_enable_auto_export_message_logs": callbackSetEnableAutoExportMessageLogs, + "callback_set_enable_vrc_mic_mute_sync": callbackSetEnableVrcMicMuteSync, + "callback_set_enable_send_message_to_vrc": callbackSetEnableSendMessageToVrc, + # Others(Message Formats(Send) + "callback_set_send_message_format": callbackSetSendMessageFormat, + "callback_set_send_message_format_with_t": callbackSetSendMessageFormatWithT, + # Others(Message Formats(Received) + "callback_set_received_message_format": callbackSetReceivedMessageFormat, + "callback_set_received_message_format_with_t": callbackSetReceivedMessageFormatWithT, + + # Speaker2Chatbox---------------- + "callback_set_enable_send_received_message_to_vrc": callbackSetEnableSendReceivedMessageToVrc, + # Speaker2Chatbox---------------- + + # Advanced Settings Tab + "callback_set_osc_ip_address": callbackSetOscIpAddress, + "callback_set_osc_port": callbackSetOscPort, + }, + ) + +def showMainWindow(): + view.startMainLoop() \ No newline at end of file diff --git a/webui_mainloop.py b/webui_mainloop.py new file mode 100644 index 00000000..eb9591b2 --- /dev/null +++ b/webui_mainloop.py @@ -0,0 +1,228 @@ +import sys +import json +import time +from config import config +import webui_controller as controller + +config_mapping = { + "/config/version": "VERSION", + "/config/transparency_range": "TRANSPARENCY_RANGE", + "/config/appearance_theme_list": "APPEARANCE_THEME_LIST", + "/config/ui_scaling_list": "UI_SCALING_LIST", + "/config/textbox_ui_scaling_range": "TEXTBOX_UI_SCALING_RANGE", + "/config/message_box_ratio_range": "MESSAGE_BOX_RATIO_RANGE", + "/config/selectable_ui_languages_dict": "SELECTABLE_UI_LANGUAGES_DICT", + "/config/selectable_ctranslate2_weight_type_dict": "SELECTABLE_CTRANSLATE2_WEIGHT_TYPE_DICT", + "/config/selectable_whisper_weight_type_dict": "SELECTABLE_WHISPER_WEIGHT_TYPE_DICT", + "/config/max_mic_energy_threshold": "MAX_MIC_ENERGY_THRESHOLD", + "/config/max_speaker_energy_threshold": "MAX_SPEAKER_ENERGY_THRESHOLD", + "/config/enable_translation": "ENABLE_TRANSLATION", + "/config/enable_transcription_send": "ENABLE_TRANSCRIPTION_SEND", + "/config/enable_transcription_receive": "ENABLE_TRANSCRIPTION_RECEIVE", + "/config/enable_foreground": "ENABLE_FOREGROUND", + "/config/source_country": "SOURCE_COUNTRY", + "/config/source_language": "SOURCE_LANGUAGE", + "/config/target_country": "TARGET_COUNTRY", + "/config/target_language": "TARGET_LANGUAGE", + "/config/choice_input_translator": "CHOICE_INPUT_TRANSLATOR", + "/config/choice_output_translator": "CHOICE_OUTPUT_TRANSLATOR", + "/config/is_reset_button_displayed_for_translation": "IS_RESET_BUTTON_DISPLAYED_FOR_TRANSLATION", + "/config/is_reset_button_displayed_for_whisper": "IS_RESET_BUTTON_DISPLAYED_FOR_WHISPER", + "/config/selected_tab_no": "SELECTED_TAB_NO", + "/config/selected_tab_your_translator_engines": "SELECTED_TAB_YOUR_TRANSLATOR_ENGINES", + "/config/selected_tab_target_translator_engines": "SELECTED_TAB_TARGET_TRANSLATOR_ENGINES", + "/config/selected_tab_your_languages": "SELECTED_TAB_YOUR_LANGUAGES", + "/config/selected_tab_target_languages": "SELECTED_TAB_TARGET_LANGUAGES", + "/config/selected_transcription_engine": "SELECTED_TRANSCRIPTION_ENGINE", + "/config/is_main_window_sidebar_compact_mode": "IS_MAIN_WINDOW_SIDEBAR_COMPACT_MODE", + "/config/transparency": "TRANSPARENCY", + "/config/appearance_theme": "APPEARANCE_THEME", + "/config/ui_scaling": "UI_SCALING", + "/config/textbox_ui_scaling": "TEXTBOX_UI_SCALING", + "/config/message_box_ratio": "MESSAGE_BOX_RATIO", + "/config/font_family": "FONT_FAMILY", + "/config/ui_language": "UI_LANGUAGE", + "/config/enable_restore_main_window_geometry": "ENABLE_RESTORE_MAIN_WINDOW_GEOMETRY", + "/config/main_window_geometry": "MAIN_WINDOW_GEOMETRY", + "/config/choice_mic_host": "CHOICE_MIC_HOST", + "/config/choice_mic_device": "CHOICE_MIC_DEVICE", + "/config/input_mic_energy_threshold": "INPUT_MIC_ENERGY_THRESHOLD", + "/config/input_mic_dynamic_energy_threshold": "INPUT_MIC_DYNAMIC_ENERGY_THRESHOLD", + "/config/input_mic_record_timeout": "INPUT_MIC_RECORD_TIMEOUT", + "/config/input_mic_phrase_timeout": "INPUT_MIC_PHRASE_TIMEOUT", + "/config/input_mic_max_phrases": "INPUT_MIC_MAX_PHRASES", + "/config/input_mic_word_filter": "INPUT_MIC_WORD_FILTER", + "/config/input_mic_avg_logprob": "INPUT_MIC_AVG_LOGPROB", + "/config/input_mic_no_speech_prob": "INPUT_MIC_NO_SPEECH_PROB", + "/config/choice_speaker_device": "CHOICE_SPEAKER_DEVICE", + "/config/input_speaker_energy_threshold": "INPUT_SPEAKER_ENERGY_THRESHOLD", + "/config/input_speaker_dynamic_energy_threshold": "INPUT_SPEAKER_DYNAMIC_ENERGY_THRESHOLD", + "/config/input_speaker_record_timeout": "INPUT_SPEAKER_RECORD_TIMEOUT", + "/config/input_speaker_phrase_timeout": "INPUT_SPEAKER_PHRASE_TIMEOUT", + "/config/input_speaker_max_phrases": "INPUT_SPEAKER_MAX_PHRASES", + "/config/input_speaker_avg_logprob": "INPUT_SPEAKER_AVG_LOGPROB", + "/config/input_speaker_no_speech_prob": "INPUT_SPEAKER_NO_SPEECH_PROB", + "/config/osc_ip_address": "OSC_IP_ADDRESS", + "/config/osc_port": "OSC_PORT", + "/config/auth_keys": "AUTH_KEYS", + "/config/use_translation_feature": "USE_TRANSLATION_FEATURE", + "/config/use_whisper_feature": "USE_WHISPER_FEATURE", + "/config/ctranslate2_weight_type": "CTRANSLATE2_WEIGHT_TYPE", + "/config/whisper_weight_type": "WHISPER_WEIGHT_TYPE", + "/config/enable_auto_clear_message_box": "ENABLE_AUTO_CLEAR_MESSAGE_BOX", + "/config/enable_send_only_translated_messages": "ENABLE_SEND_ONLY_TRANSLATED_MESSAGES", + "/config/send_message_button_type": "SEND_MESSAGE_BUTTON_TYPE", + "/config/enable_notice_xsoverlay": "ENABLE_NOTICE_XSOVERLAY", + "/config/overlay_settings": "OVERLAY_SETTINGS", + "/config/enable_overlay_small_log": "ENABLE_OVERLAY_SMALL_LOG", + "/config/overlay_small_log_settings": "OVERLAY_SMALL_LOG_SETTINGS", + "/config/overlay_ui_type": "OVERLAY_UI_TYPE", + "/config/enable_send_message_to_vrc": "ENABLE_SEND_MESSAGE_TO_VRC", + "/config/send_message_format": "SEND_MESSAGE_FORMAT", + "/config/send_message_format_with_t": "SEND_MESSAGE_FORMAT_WITH_T", + "/config/received_message_format": "RECEIVED_MESSAGE_FORMAT", + "/config/received_message_format_with_t": "RECEIVED_MESSAGE_FORMAT_WITH_T", + "/config/enable_speaker2chatbox_pass": "ENABLE_SPEAKER2CHATBOX_PASS", + "/config/enable_send_received_message_to_vrc": "ENABLE_SEND_RECEIVED_MESSAGE_TO_VRC", + "/config/enable_logger": "ENABLE_LOGGER", + "/config/enable_vrc_mic_mute_sync": "ENABLE_VRC_MIC_MUTE_SYNC", + "/config/is_config_window_compact_mode": "IS_CONFIG_WINDOW_COMPACT_MODE", +} + +controller_mapping = { + "/controller/list_language_and_country": controller.getListLanguageAndCountry, + "/controller/list_mic_host": controller.getListInputHost, + "/controller/list_mic_device": controller.getListInputDevice, + "/controller/list_speaker_device": controller.getListOutputDevice, + "/controller/callback_update_software": controller.callbackUpdateSoftware, + "/controller/callback_restart_software": controller.callbackRestartSoftware, + "/controller/callback_filepath_logs": controller.callbackFilepathLogs, + "/controller/callback_filepath_config_file": controller.callbackFilepathConfigFile, + "/controller/callback_open_config_window": controller.callbackOpenConfigWindow, + "/controller/callback_close_config_window": controller.callbackCloseConfigWindow, + "/controller/callback_enable_main_window_sidebar_compact_mode": controller.callbackEnableMainWindowSidebarCompactMode, + "/controller/callback_disable_main_window_sidebar_compact_mode": controller.callbackDisableMainWindowSidebarCompactMode, + "/controller/callback_toggle_translation": controller.callbackToggleTranslation, + "/controller/callback_toggle_transcription_send": controller.callbackToggleTranscriptionSend, + "/controller/callback_toggle_transcription_receive": controller.callbackToggleTranscriptionReceive, + "/controller/callback_toggle_foreground": controller.callbackToggleForeground, + "/controller/callback_your_language": controller.setYourLanguageAndCountry, + "/controller/callback_target_language": controller.setTargetLanguageAndCountry, + "/controller/callback_swap_languages": controller.swapYourLanguageAndTargetLanguage, + "/controller/callback_selected_language_preset_tab": controller.callbackSelectedLanguagePresetTab, + "/controller/callback_selected_translation_engine": controller.callbackSelectedTranslationEngine, + "/controller/callback_disable_config_window_compact_mode": controller.callbackEnableConfigWindowCompactMode, + "/controller/callback_enable_config_window_compact_mode": controller.callbackDisableConfigWindowCompactMode, + "/controller/callback_set_transparency": controller.callbackSetTransparency, + "/controller/callback_set_appearance": controller.callbackSetAppearance, + "/controller/callback_set_ui_scaling": controller.callbackSetUiScaling, + "/controller/callback_set_textbox_ui_scaling": controller.callbackSetTextboxUiScaling, + "/controller/callback_set_message_box_ratio": controller.callbackSetMessageBoxRatio, + "/controller/callback_set_font_family": controller.callbackSetFontFamily, + "/controller/callback_set_ui_language": controller.callbackSetUiLanguage, + "/controller/callback_set_enable_restore_main_window_geometry": controller.callbackSetEnableRestoreMainWindowGeometry, + "/controller/callback_set_use_translation_feature": controller.callbackSetUseTranslationFeature, + "/controller/callback_set_ctranslate2_weight_type": controller.callbackSetCtranslate2WeightType, + "/controller/callback_set_deepl_auth_key": controller.callbackSetDeeplAuthKey, + "/controller/callback_set_mic_host": controller.callbackSetMicHost, + "/controller/callback_set_mic_device": controller.callbackSetMicDevice, + "/controller/callback_set_mic_energy_threshold": controller.callbackSetMicEnergyThreshold, + "/controller/callback_set_mic_dynamic_energy_threshold": controller.callbackSetMicDynamicEnergyThreshold, + "/controller/callback_check_mic_threshold": controller.callbackCheckMicThreshold, + "/controller/callback_set_mic_record_timeout": controller.callbackSetMicRecordTimeout, + "/controller/callback_set_mic_phrase_timeout": controller.callbackSetMicPhraseTimeout, + "/controller/callback_set_mic_max_phrases": controller.callbackSetMicMaxPhrases, + "/controller/callback_set_mic_word_filter": controller.callbackSetMicWordFilter, + "/controller/callback_delete_mic_word_filter": controller.callbackDeleteMicWordFilter, + "/controller/callback_set_speaker_device": controller.callbackSetSpeakerDevice, + "/controller/callback_set_speaker_energy_threshold": controller.callbackSetSpeakerEnergyThreshold, + "/controller/callback_set_speaker_dynamic_energy_threshold": controller.callbackSetSpeakerDynamicEnergyThreshold, + "/controller/callback_check_speaker_threshold": controller.callbackCheckSpeakerThreshold, + "/controller/callback_set_speaker_record_timeout": controller.callbackSetSpeakerRecordTimeout, + "/controller/callback_set_speaker_phrase_timeout": controller.callbackSetSpeakerPhraseTimeout, + "/controller/callback_set_speaker_max_phrases": controller.callbackSetSpeakerMaxPhrases, + "/controller/callback_set_use_whisper_feature": controller.callbackSetUserWhisperFeature, + "/controller/callback_set_whisper_weight_type": controller.callbackSetWhisperWeightType, + "/controller/callback_set_overlay_settings": controller.callbackSetOverlaySettings, + "/controller/callback_set_enable_overlay_small_log": controller.callbackSetEnableOverlaySmallLog, + "/controller/callback_set_overlay_small_log_settings": controller.callbackSetOverlaySmallLogSettings, + "/controller/callback_set_enable_auto_clear_chatbox": controller.callbackSetEnableAutoClearMessageBox, + "/controller/callback_set_send_only_translated_messages": controller.callbackSetEnableSendOnlyTranslatedMessages, + "/controller/callback_set_send_message_button_type": controller.callbackSetSendMessageButtonType, + "/controller/callback_set_enable_notice_xsoverlay": controller.callbackSetEnableNoticeXsoverlay, + "/controller/callback_set_enable_auto_export_message_logs": controller.callbackSetEnableAutoExportMessageLogs, + "/controller/callback_set_enable_vrc_mic_mute_sync": controller.callbackSetEnableVrcMicMuteSync, + "/controller/callback_set_enable_send_message_to_vrc": controller.callbackSetEnableSendMessageToVrc, + "/controller/callback_set_send_message_format": controller.callbackSetSendMessageFormat, + "/controller/callback_set_send_message_format_with_t": controller.callbackSetSendMessageFormatWithT, + "/controller/callback_set_received_message_format": controller.callbackSetReceivedMessageFormat, + "/controller/callback_set_received_message_format_with_t": controller.callbackSetReceivedMessageFormatWithT, + "/controller/callback_set_enable_send_received_message_to_vrc": controller.callbackSetEnableSendReceivedMessageToVrc, + "/controller/callback_set_osc_ip_address": controller.callbackSetOscIpAddress, + "/controller/callback_set_osc_port": controller.callbackSetOscPort, +} + +def handle_config_request(endpoint, method, data=None): + handler = config_mapping.get(endpoint) + if handler is None: + response = "Invalid endpoint" + status = 404 + elif method == "GET": + response = getattr(config, handler) + status = 200 + elif method == "POST": + setattr(config, handler, data) + response = getattr(config, handler) + status = 200 + return response, status + +def handle_controller_request(endpoint, method, data=None): + handler = controller_mapping.get(endpoint) + if handler is None: + response = "Invalid endpoint" + status = 404 + elif method == "GET": + response = handler() + status = 200 + elif method == "POST": + response = handler(data) + status = 200 + return response, status + +def main(): + received_data = sys.stdin.readline().strip() + received_data = json.loads(received_data) + + if received_data is True: + endpoint = received_data.get("endpoint", None) + method = received_data.get("method", None) + data = received_data.get("data", None) + + match endpoint.split("/")[1]: + case "config": + response_data, status = handle_config_request(endpoint, method, data) + case "controller": + response_data, status = handle_controller_request(endpoint, method, data) + case _: + pass + + response = { + "method": method, + "status": status, + "endpoint": endpoint, + "data": response_data, + } + + response = json.dumps(response) + time.sleep(2) + print(response, flush=True) + +if __name__ == "__main__": + try: + print(json.dumps({"init_key_from_py": "Initialization from Python."}), flush=True) + while True: + main() + except Exception: + import traceback + with open('error.log', 'a') as f: + traceback.print_exc(file=f) \ No newline at end of file