diff --git a/DeepLXTranslateNode/.gitignore b/DeepLXTranslateNode/.gitignore
deleted file mode 100644
index a0668f0..0000000
--- a/DeepLXTranslateNode/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
-go/
-DeepLX/
-config.json
\ No newline at end of file
diff --git a/DeepLXTranslateNode/README.md b/DeepLXTranslateNode/README.md
deleted file mode 100644
index 5422d81..0000000
--- a/DeepLXTranslateNode/README.md
+++ /dev/null
@@ -1,134 +0,0 @@
-# DeepLX nodes for ComfyUI, for translating prompts into other languages.
-
-Powerful Free DeepL API, No Token Required. Used module [DeepLX](https://github.com/OwO-Network/DeepLX) .
-
-### Languages supported:
-
-[List languages supported](https://developers.deepl.com/docs/getting-started/supported-languages)
-
-
- Source Languages
-
-- Arabic: AR
-- Bulgarian: BG
-- Czech: CS
-- Danish: DA
-- German: DE
-- Greek: EL
-- English: EN
-- Spanish: ES
-- Estonian: ET
-- Finnish: FI
-- French: FR
-- Hungarian: HU
-- Indonesian: ID
-- Italian: IT
-- Japanese: JA
-- Korean: KO
-- Lithuanian: LT
-- Latvian: LV
-- Norwegian Bokmål: NB
-- Dutch: NL
-- Polish: PL
-- Portuguese: PT
-- Romanian: RO
-- Russian: RU
-- Slovak: SK
-- Slovenian: SL
-- Swedish: SV
-- Turkish: TR
-- Ukrainian: UK
-- Chinese: ZH
-
-
-
-
- Target Languages
-
-- Arabic: AR
-- Bulgarian: BG
-- Czech: CS
-- Danish: DA
-- German: DE
-- Greek: EL
-- English: EN
-- English (British): EN-GB
-- English (American): EN-US
-- Spanish: ES
-- Estonian: ET
-- Finnish: FI
-- French: FR
-- Hungarian: HU
-- Indonesian: ID
-- Italian: IT
-- Japanese: JA
-- Korean: KO
-- Lithuanian: LT
-- Latvian: LV
-- Norwegian Bokmål: NB
-- Dutch: NL
-- Polish: PL
-- Portuguese: PT
-- Portuguese (Brazilian): PT-BR
-- Portuguese (all Portuguese variants excluding Brazilian Portuguese): PT-PT
-- Romanian: RO
-- Russian: RU
-- Slovak: SK
-- Slovenian: SL
-- Swedish: SV
-- Turkish: TR
-- Ukrainian: UK
-- Chinese (simplified): ZH-HANS
-- Chinese (traditional): ZH-HANT
-
-
-
-### Autoinstaller
-
-**Golang (Go)** and **DeepLX** installation occurs automatically if the **Go** or **DeepLX** directory is missing.
-
-You can also call the automatic installer by running shell `run_install_deeplx_windows.cmd` or `run_install_deeplx_linux.sh` for your platform.
-
-**_Note: If you encounter any problems, you can use manual installation._**
-
-### Manual install and use:
-
-1. Install my custom nodes in ComfyUI, used [HERE](https://github.com/AlekPet/ComfyUI_Custom_Nodes_AlekPet#installing)
-2. Download zip archive Golang (programming language) from your system (Widnows, Linux ...) [Download Golang](https://go.dev/dl/)
-3. Extract archive (inside archive folder `go`) in the folder `\ComfyUI\custom_nodes\ComfyUI_Custom_Nodes_AlekPet\DeepLXTranslateNode`
-4. Open shell in the folder `\ComfyUI\custom_nodes\ComfyUI_Custom_Nodes_AlekPet\DeepLXTranslateNode` and run command `git clone https://github.com/OwO-Network/DeepLX` (you need install Git if not exists [Git](https://git-scm.com/downloads))
-
-**Structure folders:**
-
-
-
-5. Run comfyui (**see note after**) and add node `DeepLXTranslateCLIPTextEncodeNode` or `DeepLXTranslateTextNode` context menu in the category `CONDITIONING`.
-6. [Optional] If you are in **CH**, you need in the `\ComfyUI\custom_nodes\ComfyUI_Custom_Nodes_AlekPet\DeepLXTranslateNode\go`, edit file **go.env**, modify this line `GOPROXY=https://proxy.golang.org,direct` to `GOPROXY=https://goproxy.cn,direct` [Thanks tongpeng1988](https://github.com/AlekPet/ComfyUI_Custom_Nodes_AlekPet/issues/136#issuecomment-2804126821) .
-
-**_Note: The first launch may fail, Golang is installing packages for DeepLX, just restart ComfyUI!_**
-
-### Note
-
-On first launch, a `config.json` file will be created where you can specify the initial values from which language to translate and to which language. You can also disable DeepLXTranslate nodes using the 'run_deeplx' property.
-**Default config.json values:**
-
-```json
-{
- "settings": {
- "__commnet": "Please check the list of available languages before specifying, especially target_lang! Property run_deeplx disable DeepLXTranslate nodes. See README file",
- "source_lang": "Russian",
- "target_lang": "English",
- "run_deeplx": true
- }
-}
-```
-
-> Includes:
-
-> **DeepLXTranslateCLIPTextEncodeNode** - translate text, and return CONDITIONING
->
-> **DeepLXTranslateTextNode** - translate text and return text (STRING)
-
-## Image:
-
-
diff --git a/DeepLXTranslateNode/deeplx_translate_node.py b/DeepLXTranslateNode/deeplx_translate_node.py
deleted file mode 100644
index 10d5c90..0000000
--- a/DeepLXTranslateNode/deeplx_translate_node.py
+++ /dev/null
@@ -1,386 +0,0 @@
-# Title: DeepLX Translate promts nodes
-# Author: AlekPet (https://github.com/AlekPet/ComfyUI_Custom_Nodes_AlekPet)
-# Github DeepLX: https://github.com/OwO-Network/DeepLX
-
-import os
-import json
-import subprocess
-import requests
-import time
-from . import install_deeplx
-
-
-LANGUAGES_CODES = {
- "source": {
- "Arabic": "AR",
- "Bulgarian": "BG",
- "Czech": "CS",
- "Danish": "DA",
- "German": "DE",
- "Greek": "EL",
- "English": "EN",
- "Spanish": "ES",
- "Estonian": "ET",
- "Finnish": "FI",
- "French": "FR",
- "Hungarian": "HU",
- "Indonesian": "ID",
- "Italian": "IT",
- "Japanese": "JA",
- "Korean": "KO",
- "Lithuanian": "LT",
- "Latvian": "LV",
- "Norwegian Bokmål": "NB",
- "Dutch": "NL",
- "Polish": "PL",
- "Portuguese": "PT",
- "Romanian": "RO",
- "Russian": "RU",
- "Slovak": "SK",
- "Slovenian": "SL",
- "Swedish": "SV",
- "Turkish": "TR",
- "Ukrainian": "UK",
- "Chinese": "ZH",
- },
- "target": {
- "Arabic": "AR",
- "Bulgarian": "BG",
- "Czech": "CS",
- "Danish": "DA",
- "German": "DE",
- "Greek": "EL",
- "English": "EN",
- "English (British)": "EN-GB",
- "English (American)": "EN-US",
- "Spanish": "ES",
- "Estonian": "ET",
- "Finnish": "FI",
- "French": "FR",
- "Hungarian": "HU",
- "Indonesian": "ID",
- "Italian": "IT",
- "Japanese": "JA",
- "Korean": "KO",
- "Lithuanian": "LT",
- "Latvian": "LV",
- "Norwegian Bokmål": "NB",
- "Dutch": "NL",
- "Polish": "PL",
- "Portuguese": "PT",
- "Portuguese (Brazilian)": "PT-BR",
- "Portuguese (all Portuguese variants excluding Brazilian Portuguese)": "PT-PT",
- "Romanian": "RO",
- "Russian": "RU",
- "Slovak": "SK",
- "Slovenian": "SL",
- "Swedish": "SV",
- "Turkish": "TR",
- "Ukrainian": "UK",
- "Chinese (simplified)": "ZH-HANS",
- "Chinese (traditional)": "ZH-HANT",
- },
-}
-
-
-# Colors
-class ColPrint:
- RED = "\033[1;31;40m"
- GREEN = "\033[92m"
- YELLOW = "\033[93m"
- MAGNETA = "\033[95m"
- BLUE = "\033[94m"
- CLEAR = "\033[0m"
-
-
-# Node directory
-NODE_DIR = os.path.dirname(os.path.abspath(__file__))
-SETTINGS = {}
-
-# Directory node and config file
-config_path = os.path.join(NODE_DIR, "config.json")
-
-# Load config.js file
-if not os.path.exists(config_path):
- print(
- f"{ColPrint.YELLOW}[DeepLXTranslateNode] {ColPrint.MAGNETA}File config.js file not found! Create default!{ColPrint.CLEAR}"
- )
-
- with open(config_path, "w", encoding="utf-8") as f:
- defaultJSON = {
- "settings": {
- "__commnet": "Please check the list of available languages before specifying, especially target_lang! Property run_deeplx disable DeepLXTranslate nodes. See README file",
- "source_lang": "Russian",
- "target_lang": "English",
- "run_deeplx": True
- }
- }
- json.dump(defaultJSON, f, ensure_ascii=False, indent=4)
-
-else:
- with open(config_path, "r", encoding="utf-8") as f:
- config = json.load(f)
- SETTINGS = config.get("settings", {})
-
- if SETTINGS.keys():
- reset_lang = False
-
- # Getting user settings for config.json
- source_lang = SETTINGS.get("source_lang")
- target_lang = SETTINGS.get("target_lang")
-
- if source_lang not in LANGUAGES_CODES["source"]:
- SETTINGS.update({"source_lang": "Russian"})
- reset_lang = True
-
- if target_lang not in LANGUAGES_CODES["target"]:
- SETTINGS.update({"target_lang": "English"})
- reset_lang = True
-
- if reset_lang:
- print(
- f"{ColPrint.YELLOW}[DeepLXTranslateNode]{ColPrint.MAGNETA} Source or target language not found in list of available languages, check config.js!{ColPrint.CLEAR}"
- )
-
-
-# DeepLX and go constants
-DEEPLX_SERVER_RUNNING = False
-DEEPLX_SERVER_URL = "http://127.0.0.1:1188/"
-DEEPLX_SERVER_URL_TRANSLATE = "http://127.0.0.1:1188/translate"
-RETRY_CHECK_SERVER = 10
-
-PATH_TO_DEEPLX_SERVER = os.path.join(NODE_DIR, "DeepLX")
-PATH_TO_GO = os.path.join(NODE_DIR, "go", "bin")
-
-# We check whether the user has disabled the launch of the DeepLX server.
-if not SETTINGS.get("run_deeplx", True): # Setting whether to run DeepLX server
- DEEPLX_SERVER_RUNNING = False
- raise Exception("User disabled DeepLX server startup in config.json (proprety: 'run_deeplx')")
-
-
-### Automatical install Golang (Go) and DeepLX ###
-try:
- # Checking exists path to Go and install Golang (Go)
- if not os.path.exists(PATH_TO_GO):
- print(f"{ColPrint.YELLOW}[DeepLXTranslateNode]{ColPrint.MAGNETA} Path to 'Golang (Go)' not exists, try install...{ColPrint.CLEAR}")
-
- # Get platform
- platform = install_deeplx.getPlatform()
-
- if platform is not None:
- print(f"{ColPrint.YELLOW}[DeepLXTranslateNode]{ColPrint.GREEN} Your platform is: {'macOS (Darwin)' if platform == 'darwin' else platform.capitalize()}{ColPrint.CLEAR}")
- # Get link
- go_url = install_deeplx.GO_URLS[platform]
-
- # Download file
- file_name, path_to_file = install_deeplx.download(go_url, NODE_DIR)
-
- # Extract file
- install_deeplx.extractArchive(path_to_file)
- else:
- print(f"{ColPrint.RED}[DeepLXTranslateNode] Unable to determine identifying the underlying platform! Use manual installation!{ColPrint.CLEAR} ")
-
-
- # # Checking exists path to Go and install DeepLX
- if not os.path.exists(PATH_TO_DEEPLX_SERVER):
- print(
- f"{ColPrint.YELLOW}[DeepLXTranslateNode]{ColPrint.MAGNETA} Path to DeepLX server folder not exists, try install...{ColPrint.CLEAR}")
-
- # Get link
- deeplx_url = install_deeplx.FILES_DOWNLOAD["DeepLX"].get("url")
-
- # Download file
- file_name, path_to_file = install_deeplx.download(deeplx_url, NODE_DIR)
-
- # Extract file
- install_deeplx.extractArchive(path_to_file)
-
- # Rename DeepLX-master to DeepLX
- install_deeplx.otherOperations("DeepLX", NODE_DIR)
-
-except Exception as e:
- raise e
-
-
-
-### end - Automatical install Golang (Go) and DeepLX ###
-
-# Checking pathes variables
-if not os.path.exists(PATH_TO_GO):
- raise FileNotFoundError(f"Error path to 'Golang (Go)' not exists: {PATH_TO_GO}")
-
-
-if not os.path.exists(PATH_TO_DEEPLX_SERVER):
- raise FileNotFoundError(f"Error path to DeepLX server folder not exists: {PATH_TO_DEEPLX_SERVER}")
-
-
-# Detect platform
-go_executable = os.path.join(PATH_TO_GO, "go")
-if os.name == "nt":
- go_executable += ".exe"
-
-# Try run DeepLX server
-try:
- print(f"{ColPrint.YELLOW}[DeepLXTranslateNode] {ColPrint.BLUE}Running server DeepLX...{ColPrint.CLEAR}")
- proc_deeplx = subprocess.Popen([go_executable, "run", "main.go"], cwd=PATH_TO_DEEPLX_SERVER)
- DEEPLX_SERVER_RUNNING = True
- time.sleep(2)
-
-except Exception as e:
- DEEPLX_SERVER_RUNNING = False
- raise Exception(
- f"{ColPrint.RED}[DeepLXTranslateNode] Error running server DeepLX: {ColPrint.MAGNETA}{e}{ColPrint.CLEAR}"
- )
-
-if DEEPLX_SERVER_RUNNING:
- for retry in range(RETRY_CHECK_SERVER, 0, -1):
- print(
- f"{ColPrint.YELLOW}[DeepLXTranslateNode] {ColPrint.BLUE}Checking DeepLX server. Attempt {retry}...{ColPrint.CLEAR}"
- )
-
- try:
- response = requests.get(DEEPLX_SERVER_URL)
- response.raise_for_status()
-
- print(
- f"{ColPrint.YELLOW}[DeepLXTranslateNode]{ColPrint.GREEN} Server responded successfully: {ColPrint.CLEAR}",
- response.text,
- )
- DEEPLX_SERVER_RUNNING = True
- break
-
- except requests.HTTPError as e:
- print(
- f"{ColPrint.RED}[DeepLXTranslateNode] HTTP error while connecting to DeepLX: {ColPrint.MAGNETA}{e}{ColPrint.CLEAR}"
- )
- if retry == 1:
- DEEPLX_SERVER_RUNNING = False
- raise
-
- except Exception as e:
- if retry == 1:
- DEEPLX_SERVER_RUNNING = False
- raise Exception(
- f"{ColPrint.RED}[DeepLXTranslateNode] Unexpected error while checking DeepLX server: {ColPrint.MAGNETA}{e}{ColPrint.CLEAR}"
- )
-
- time.sleep(2)
-
- else:
- DEEPLX_SERVER_RUNNING = False
- print(
- f"{ColPrint.RED}[DeepLXTranslateNode] All attempts to reach the DeepLX server have failed.{ColPrint.CLEAR}"
- )
-
-
-def createRequest(payload):
- """Request function"""
- try:
- response = requests.post(DEEPLX_SERVER_URL_TRANSLATE, json=payload)
- response.raise_for_status()
-
- if response.status_code == 200:
- json_data = response.json()
- response_text = json_data.get("data").strip()
-
- return response_text
-
- except requests.HTTPError as e:
- print(
- f"DeepLX server is not running! Please check Go and DeepLX repository installation or information in the ComfyUI terminal!: {response.status_code}, {response.text}"
- )
- raise e
- except Exception as e:
- print(f"Error DeepLX: {e}")
- raise e
-
-
-def translate(prompt, source, target):
- """Translate function"""
- # Check prompt exist
- if prompt is None or prompt.strip() == "":
- return ""
-
- # Create body request
- payload = {"source_lang": source, "target_lang": target, "text": prompt}
-
- response_translate_text = createRequest(payload)
-
- return response_translate_text
-
-
-class DeepLXTranslateCLIPTextEncodeNode:
- @classmethod
- def INPUT_TYPES(self):
- source_langs = list(LANGUAGES_CODES.get("source").keys())
- target_langs = list(LANGUAGES_CODES.get("target").keys())
-
- return {
- "required": {
- "source_translate": (
- source_langs,
- {"default": SETTINGS.get("source_lang", "Russian"), "tooltip": "Translation source languge"},
- ),
- "target_translate": (
- target_langs,
- {"default": SETTINGS.get("target_lang", "English"), "tooltip": "Translation target languge"},
- ),
- "prompt": (
- "STRING",
- {"multiline": True, "placeholder": "Input prompt text"},
- ),
- "clip": ("CLIP",),
- }
- }
-
- RETURN_TYPES = (
- "CONDITIONING",
- "STRING",
- )
- FUNCTION = "deeplx_translate_text"
- DESCRIPTION = "This is a node that translates the prompt into another language using DeepLX."
- CATEGORY = "AlekPet Nodes/conditioning"
-
- def deeplx_translate_text(self, source_translate, target_translate, prompt, clip):
- if not DEEPLX_SERVER_RUNNING:
- raise ValueError(
- "DeepLX server is not running! Please check Go and DeepLX repository installation or information in the ComfyUI terminal!"
- )
-
- # Get short value source and target code languages
- source_translate_code = LANGUAGES_CODES["source"][source_translate]
- target_translate_code = LANGUAGES_CODES["target"][target_translate]
-
- prompt = translate(prompt, source_translate_code, target_translate_code)
-
- tokens = clip.tokenize(prompt)
- cond, pooled = clip.encode_from_tokens(tokens, return_pooled=True)
- return ([[cond, {"pooled_output": pooled}]], prompt)
-
-
-class DeepLXTranslateTextNode(DeepLXTranslateCLIPTextEncodeNode):
- @classmethod
- def INPUT_TYPES(self):
- return_types = super().INPUT_TYPES()
- del return_types["required"]["clip"]
- return return_types
-
- RETURN_TYPES = ("STRING",)
- RETURN_NAMES = ("text",)
- FUNCTION = "deeplx_translate_text"
-
- CATEGORY = "AlekPet Nodes/text"
-
- def deeplx_translate_text(self, source_translate, target_translate, prompt):
- if not DEEPLX_SERVER_RUNNING:
- raise ValueError(
- "DeepLX server is not running! Please check Go and DeepLX repository installation or information in the ComfyUI terminal!"
- )
-
- # Get short value source and target code languages
- source_translate_code = LANGUAGES_CODES["source"][source_translate]
- target_translate_code = LANGUAGES_CODES["target"][target_translate]
-
- prompt = translate(prompt, source_translate_code, target_translate_code)
-
- return (prompt,)
diff --git a/DeepLXTranslateNode/image_deeplx_structures_folders.png b/DeepLXTranslateNode/image_deeplx_structures_folders.png
deleted file mode 100644
index 7dadaff..0000000
Binary files a/DeepLXTranslateNode/image_deeplx_structures_folders.png and /dev/null differ
diff --git a/DeepLXTranslateNode/image_deeplx_translate_node.jpg b/DeepLXTranslateNode/image_deeplx_translate_node.jpg
deleted file mode 100644
index 3f2764d..0000000
Binary files a/DeepLXTranslateNode/image_deeplx_translate_node.jpg and /dev/null differ
diff --git a/DeepLXTranslateNode/install_deeplx.py b/DeepLXTranslateNode/install_deeplx.py
deleted file mode 100644
index f3649ec..0000000
--- a/DeepLXTranslateNode/install_deeplx.py
+++ /dev/null
@@ -1,219 +0,0 @@
-# Title: Autoinstall Golang (Go) and DeepLX for ComfyUI DeepLXTranslateNode
-# Author: AlekPet (https://github.com/AlekPet/ComfyUI_Custom_Nodes_AlekPet)
-# DeepLXTranslateNode README: https://github.com/AlekPet/ComfyUI_Custom_Nodes_AlekPet/blob/master/DeepLXTranslateNode/README.md
-
-import os
-from sys import version_info, platform as _platform
-import platform
-import requests
-from tqdm import tqdm
-import shutil
-
-class ColPrintInstall:
- RED = "\033[1;31;40m"
- GREEN = "\033[92m"
- YELLOW = "\033[93m"
- MAGNETA = "\033[95m"
- BLUE = "\033[94m"
- CLEAR = "\033[0m"
-
-
-GO_URLS = {
- "win32": "https://go.dev/dl/go1.24.2.windows-386.zip",
- "win64": "https://go.dev/dl/go1.24.2.windows-amd64.zip",
- "darwin": "https://go.dev/dl/go1.24.2.darwin-arm64.tar.gz",
- "linux_i386_i686": "https://go.dev/dl/go1.24.2.linux-386.tar.gz",
- "linux_x86_64": "https://go.dev/dl/go1.24.3.linux-amd64.tar.gz",
- "linux_aarch64": "https://go.dev/dl/go1.24.3.linux-arm64.tar.gz",
- "linux_armv6l": "https://go.dev/dl/go1.24.3.linux-armv6l.tar.gz",
- }
-
-FILES_DOWNLOAD = {
- "Golang": {
- "url": None,
- },
- "DeepLX": {
- "url":"https://github.com/OwO-Network/DeepLX/archive/refs/heads/main.zip",
- },
-}
-
-WORK_DIR = os.path.dirname(os.path.abspath(__file__))
-
-
-def getPlatform():
- """ Get platform machine """
- platform_name = None
-
- if _platform.startswith("linux"):
- # Check architecture linux
- arch = platform.uname()
- if arch.machine == "x86_64":
- platform_name = "linux_x86_64"
-
- elif arch.machine == "i386" or arch.machine == "i686":
- platform_name = "linux_i386_i686"
-
- elif arch.machine == "aarch64":
- platform_name = "linux_aarch64"
-
- elif arch.machine == "armv6l" or arch.machine == "armv7l":
- platform_name = "linux_armv6l"
-
- else:
- platform_name = "linux_i386_i686"
-
- elif _platform == "darwin":
- platform_name = "darwin"
-
- elif _platform == "win32":
- # Check Windows id 64-bit
- if platform.machine().endswith('64'):
- platform_name = "win64"
- else:
- platform_name = "win32"
-
- return platform_name
-
-
-def download(url, save_to = WORK_DIR):
- """ Download file """
- file_name = url.split('/')[-1]
- print(f"{ColPrintInstall.YELLOW}[DeepLXTranslateNode]{ColPrintInstall.BLUE} Downloading file: {url}{ColPrintInstall.CLEAR}")
-
- response = requests.get(url, stream=True)
- file_size = int(response.headers.get('Content-Length', 0))
-
- save_file_path = os.path.join(save_to, file_name)
- chunk_size = 1024
-
- with open(save_file_path, "wb") as handle, tqdm(
- desc=file_name,
- total=file_size,
- unit='B',
- unit_scale=True,
- unit_divisor=chunk_size,
-) as progress_bar:
- for data in response.iter_content(chunk_size=chunk_size):
- handle.write(data)
- progress_bar.update(len(data))
-
-
- print(f"{ColPrintInstall.YELLOW}[DeepLXTranslateNode]{ColPrintInstall.GREEN} File '{file_name}' downloaded!{ColPrintInstall.CLEAR}")
- return file_name, save_file_path
-
-
-def extractArchive(file_path, extract_to = WORK_DIR, removeArchive = True):
- """ Extract zip or tar.gzip archive """
- try:
- file_name = os.path.basename(file_path)
- ext = os.path.splitext(file_path)[-1]
- desc = f"Extracting '{file_name}'"
-
- # Type archive
- if ext == ".gz":
- # TarFile
- import tarfile
-
- # Safe extract for python < 3.12 (secure path traversal)
- def safe_extract(tar, path=".", members=None):
- if members is None:
- members = tar.getmembers()
-
- safe_members = []
- for member in members:
- member_path = os.path.join(path, member.name)
- if not os.path.abspath(member_path).startswith(os.path.abspath(path)):
- continue
- safe_members.append(member)
-
- tar.extractall(path, safe_members)
-
- # Open tar archive
- with tarfile.open(file_path, 'r') as tar_archive:
- members = tar_archive.getmembers()
-
- # Python >= 3.12
- if version_info >= (3, 12):
- for file in tqdm(members, desc=desc, unit="file"):
- tar_archive.extract(file, extract_to, filter='data')
- else:
- for member in tqdm(members, desc=desc):
- safe_extract(tar_archive, extract_to, [member])
-
-
- elif ext == ".zip":
- # ZipFile
- import zipfile
-
- # Open zip archive
- with zipfile.ZipFile(file_path, 'r') as zip_archive:
- file_list = zip_archive.namelist()
-
- with tqdm(file_list, desc=desc, unit="file") as pbar:
- for file in pbar:
- zip_archive.extract(file, extract_to)
-
- print(f"{ColPrintInstall.YELLOW}[DeepLXTranslateNode]{ColPrintInstall.GREEN} Archive '{file_name}' extracted.{ColPrintInstall.CLEAR}")
-
- # Remove archive file
- if removeArchive:
- os.remove(file_path)
- print(f"{ColPrintInstall.YELLOW}[DeepLXTranslateNode]{ColPrintInstall.MAGNETA} File {file_name} removed, after extract.{ColPrintInstall.CLEAR}\n")
-
- except Exception as e:
- raise e
-
-
-def otherOperations(name, save_path_dir):
- if name == "DeepLX":
- path_to_deeplx_master = os.path.join(save_path_dir, "DeepLX-main")
- path_to_rename = os.path.join(save_path_dir, "DeepLX")
-
- if os.path.isdir(path_to_deeplx_master) and not os.path.exists(path_to_rename):
- os.rename(path_to_deeplx_master, path_to_rename)
- print(f"{ColPrintInstall.YELLOW}[DeepLXTranslateNode]{ColPrintInstall.MAGNETA} Directory 'DeepLX-main' renamed to 'DeepLX'!{ColPrintInstall.CLEAR}")
- else:
- print(f"{ColPrintInstall.YELLOW}[DeepLXTranslateNode]{ColPrintInstall.MAGNETA} Directory 'DeepLX' already exists or directory {path_to_deeplx_master}' is not correct! Renaming operation abort!{ColPrintInstall.CLEAR}")
- shutil.rmtree(path_to_deeplx_master, ignore_errors=True)
-
-
-def main():
- print(f"""{ColPrintInstall.MAGNETA}~~~~~~~~~ Autoinstall Golang (Go) and DeepLX for ComfyUI DeepLXTranslateNode by AlekPet ~~~~~~~~~
-#{ColPrintInstall.CLEAR} Github: {ColPrintInstall.BLUE}https://github.com/AlekPet/ComfyUI_Custom_Nodes_AlekPet
-{ColPrintInstall.MAGNETA}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{ColPrintInstall.CLEAR}""")
- try:
- platform_name = getPlatform()
- print(f"{ColPrintInstall.YELLOW}[DeepLXTranslateNode]{ColPrintInstall.GREEN} Your platform is: {'macOS (Darwin)' if platform_name == 'darwin' else platform_name.capitalize()}{ColPrintInstall.CLEAR}")
-
- if platform_name is None:
- raise Exception("Unable to determine identifying the underlying platform!")
-
- url_down = GO_URLS[platform_name]
-
- # Download links (Golang and DeepLX)
- FILES_DOWNLOAD["Golang"]["url"] = url_down
-
- for keyName in FILES_DOWNLOAD:
- url = FILES_DOWNLOAD[keyName].get("url")
-
- if url is None:
- continue
-
- # Download file
- file_name, path_to_file = download(url, WORK_DIR)
-
- # Extract file
- extractArchive(path_to_file)
-
- # Other operation
- otherOperations(keyName, WORK_DIR)
-
-
- print(f"{ColPrintInstall.GREEN}--------------------\nAutoinstall has completed its work...{ColPrintInstall.CLEAR}")
-
- except Exception as e:
- raise e
-
-if __name__ == "__main__":
- main()
-
diff --git a/DeepLXTranslateNode/run_install_deeplx_linux.sh b/DeepLXTranslateNode/run_install_deeplx_linux.sh
deleted file mode 100644
index 38f8c3d..0000000
--- a/DeepLXTranslateNode/run_install_deeplx_linux.sh
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/bin/bash
-# Install Golang and DeepLX for ComfyUI DeepLXTranslateNode by AlekPet
-# Github repository: https://github.com/AlekPet/ComfyUI_Custom_Nodes_AlekPet/tree/master/DeepLXTranslateNode
-
-python install_deeplx.py
\ No newline at end of file
diff --git a/DeepLXTranslateNode/run_install_deeplx_windows.cmd b/DeepLXTranslateNode/run_install_deeplx_windows.cmd
deleted file mode 100644
index bf9ead9..0000000
--- a/DeepLXTranslateNode/run_install_deeplx_windows.cmd
+++ /dev/null
@@ -1,6 +0,0 @@
-@echo off
-REM ### Install Golang and DeepLX for ComfyUI DeepLXTranslateNode by AlekPet ###
-REM ### Github repository: https://github.com/AlekPet/ComfyUI_Custom_Nodes_AlekPet/tree/master/DeepLXTranslateNode
-
-..\..\..\..\python_embeded\python.exe install_deeplx.py
-pause
\ No newline at end of file
diff --git a/DeepTranslatorNode/js/deep_translator_node.js b/DeepTranslatorNode/js/deep_translator_node.js
index e187043..b2d6bc8 100644
--- a/DeepTranslatorNode/js/deep_translator_node.js
+++ b/DeepTranslatorNode/js/deep_translator_node.js
@@ -188,7 +188,11 @@ function get_support_langs() {
// Auth
const auth_data = responseData?.auth_data;
- if (widget_auth_data) {
+ if (
+ widget_auth_data &&
+ auth_data instanceof Object &&
+ Object.keys(auth_data)?.length
+ ) {
let placeholder = Object.keys(placeholders).find(
(ph_key) => ph_key.includes(service) || ph_key === service
);
diff --git a/ExtrasNode/css/extras_node_speech_recognition_style.css b/ExtrasNode/css/extras_node_speech_recognition_style.css
new file mode 100644
index 0000000..12bcc86
--- /dev/null
+++ b/ExtrasNode/css/extras_node_speech_recognition_style.css
@@ -0,0 +1,130 @@
+.alekpet_extras_node_speechrecognition_box {
+ margin: 0px;
+ padding: 0px;
+ position: absolute;
+ text-align: center;
+ cursor: pointer;
+ font-size: 1rem;
+ display: flex;
+ justify-content: center;
+ flex-flow: column;
+ width: 45px;
+ font-family: monospace;
+ align-items: center;
+ margin-top: 3px;
+}
+
+.alekpet_extras_node_speechrecognition_row {
+ position: relative;
+ display: flex;
+ gap: 3px;
+ justify-content: center;
+ align-items: center;
+}
+
+.alekpet_extras_node_recognition_icon {
+ width: 8px;
+ height: 8px;
+ border: 1px solid var(--input-text);
+ background: transparent;
+ border-radius: 50%;
+ padding: 0;
+ margin: 0px 5px 0px 0px;
+ display: inline-block;
+}
+
+.alekpet_extras_node_recognition_icon_active {
+ border: 1px solid var(--input-text);
+ background: var(--error-text);
+ border-radius: 0;
+}
+
+.alekpet_extras_node_recognition_icon_box {
+ display: flex;
+ justify-content: center;
+ align-items: baseline;
+ gap: 3px;
+ transition: all 0.7s;
+ margin-top: 2px;
+}
+
+.alekpet_extras_node_recognition_icon:hover {
+ background: var(--error-text);
+}
+
+.alekpet_extras_node_recognition_aborted {
+ transition: all 0.3s;
+ font-size: 0.6rem;
+}
+
+.alekpet_extras_node_recognition_aborted:hover {
+ color: var(--error-text);
+}
+
+.alekpet_extras_node_info {
+ display: flex;
+ align-self: center;
+}
+
+.alekpet_extras_node_speech_recognition_checkbox {
+ width: 10px;
+ height: 10px;
+ accent-color: var(--input-text);
+ padding: 0;
+ margin: 0;
+}
+
+.alekpet_extras_node_speech_recognition_checkbox:not(:checked) {
+ opacity: 0.5;
+}
+
+.alekpet_extras_node_recognition_save {
+ accent-color: var(--p-red-400);
+}
+
+.alekpet_extras_node_speech_icon {
+ font-size: 0.6em;
+ opacity: 1;
+ line-height: 1;
+ margin-top: 2px;
+}
+
+.alekpet_extras_node_speech_icon_playing {
+ opacity: 0.7;
+}
+
+.alekpet_extras_node_speech_icon_playing::after {
+ content: "✖";
+ position: absolute;
+ right: 3px;
+ color: var(--error-text);
+}
+
+/* Settings */
+.alekpet_extras_node_speech_recognition_settings_props_label {
+ background: #646363;
+ color: #cdcbcb;
+ border-radius: 5px;
+ padding: 3px;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ flex-wrap: nowrap;
+}
+
+.alekpet_extras_node_speech_recognition_settings_props_label:hover {
+ background: darkgrey;
+ color: white;
+}
+
+.alekpet_extras_node_speech_recognition_settings_props_input {
+ max-width: 10px;
+ padding: 0;
+ margin: 0;
+ outline: 0;
+}
+
+.alekpet_extras_node_speech_recognition_settings_button {
+ display: flex;
+ font-size: 0.6rem;
+}
diff --git a/ExtrasNode/css/extras_node_styles.css b/ExtrasNode/css/extras_node_styles.css
index 35d1690..6b719f4 100644
--- a/ExtrasNode/css/extras_node_styles.css
+++ b/ExtrasNode/css/extras_node_styles.css
@@ -1,134 +1,3 @@
-.alekpet_extras_node_speechrecognition_box {
- margin: 0px;
- padding: 0px;
- position: absolute;
- text-align: center;
- cursor: pointer;
- font-size: 1rem;
- display: flex;
- justify-content: center;
- flex-flow: column;
- width: 45px;
- font-family: monospace;
- align-items: center;
- margin-top: 3px;
-}
-
-.alekpet_extras_node_speechrecognition_row {
- position: relative;
- display: flex;
- gap: 3px;
- justify-content: center;
- align-items: center;
-}
-
-.alekpet_extras_node_recognition_icon {
- width: 8px;
- height: 8px;
- border: 1px solid var(--input-text);
- background: transparent;
- border-radius: 50%;
- padding: 0;
- margin: 0px 5px 0px 0px;
- display: inline-block;
-}
-
-.alekpet_extras_node_recognition_icon_active {
- border: 1px solid var(--input-text);
- background: var(--error-text);
- border-radius: 0;
-}
-
-.alekpet_extras_node_recognition_icon_box {
- display: flex;
- justify-content: center;
- align-items: baseline;
- gap: 3px;
- transition: all 0.7s;
- margin-top: 2px;
-}
-
-.alekpet_extras_node_recognition_icon:hover {
- background: var(--error-text);
-}
-
-.alekpet_extras_node_recognition_aborted {
- transition: all 0.3s;
- font-size: 0.6rem;
-}
-
-.alekpet_extras_node_recognition_aborted:hover {
- color: var(--error-text);
-}
-
-.alekpet_extras_node_info {
- display: flex;
- align-self: center;
-}
-
-.alekpet_extras_node_speech_recognition_checkbox {
- width: 10px;
- height: 10px;
- accent-color: var(--input-text);
- padding: 0;
- margin: 0;
-}
-
-.alekpet_extras_node_speech_recognition_checkbox:not(:checked) {
- opacity: 0.5;
-}
-
-.alekpet_extras_node_recognition_save {
- accent-color: var(--p-red-400);
-}
-
-.alekpet_extras_node_speech_icon {
- font-size: 0.6em;
- opacity: 1;
- line-height: 1;
- margin-top: 2px;
-}
-
-.alekpet_extras_node_speech_icon_playing {
- opacity: 0.7;
-}
-
-.alekpet_extras_node_speech_icon_playing::after {
- content: "✖";
- position: absolute;
- right: 3px;
- color: var(--error-text);
-}
-
-/* Settings */
-.alekpet_extras_node_speech_recognition_settings_props_label {
- background: #646363;
- color: #cdcbcb;
- border-radius: 5px;
- padding: 3px;
- display: flex;
- align-items: center;
- justify-content: space-between;
- flex-wrap: nowrap;
-}
-
-.alekpet_extras_node_speech_recognition_settings_props_label:hover {
- background: darkgrey;
- color: white;
-}
-
-.alekpet_extras_node_speech_recognition_settings_props_input {
- max-width: 10px;
- padding: 0;
- margin: 0;
- outline: 0;
-}
-
-.alekpet_extras_node_speech_recognition_settings_button {
- display: flex;
- font-size: 0.6rem;
-}
-
/* Style custom Confirm buttons */
.alekpet_confButtons {
border-radius: 3px;
diff --git a/ExtrasNode/js/extras_node.js b/ExtrasNode/js/extras_node.js
index a169438..a4b8b5c 100644
--- a/ExtrasNode/js/extras_node.js
+++ b/ExtrasNode/js/extras_node.js
@@ -7,564 +7,17 @@
import { app } from "../../scripts/app.js";
import { api } from "../../scripts/api.js";
import { ComfyWidgets } from "../../scripts/widgets.js";
-import { $el } from "../../scripts/ui.js";
-import { isValidStyle, comfyuiDesktopConfirm } from "./utils.js";
import { addStylesheet } from "../../scripts/utils.js";
-import {
- SpeechWidget,
- makeColorWidget,
- createPreiviewSize,
- speechRect,
- SpeechSynthesis,
- checkboxLSCheckedByKey,
-} from "./lib/extrasnode/extras_node_widgets.js";
-import { RecognationSpeechDialog } from "./lib/extrasnode/extras_node_dialogs.js";
+import { makeColorWidget } from "./lib/extrasnode/extras_node_widgets.js";
-const convertIdClass = (text) => text.replaceAll(".", "_");
const idExt = "alekpet.ExtrasNode";
-// LocalStorage settings
-const PreviewImageSizeLS = localStorage.getItem(
- `Comfy.Settings.${idExt}.PreviewImage`
-);
-const PreviewImageColorTextLS = localStorage.getItem(
- `Comfy.Settings.${idExt}.PreviewImageColorText`
-);
-const PreviewImageColorBgLS = localStorage.getItem(
- `Comfy.Settings.${idExt}.PreviewImageColorBg`
-);
-
-// Recognation & speech
-const SpeechAndRecognationSpeechLS = localStorage.getItem(
- `Comfy.Settings.${idExt}.SpeechAndRecognationSpeech`
-);
-// end - Recognation & speech
-
-// Preview image, video and audio select list combo
-const PreviewImageVideoComboLS = localStorage.getItem(
- `Comfy.Settings.${idExt}.PreviewImageVideoCombo`
-);
-
-// Settings set values from LS or default
-let PreviewImageSize = PreviewImageSizeLS
- ? JSON.parse(PreviewImageSizeLS)
- : false,
- PreviewImageColorText =
- PreviewImageColorTextLS && PreviewImageColorTextLS.trim() !== ""
- ? PreviewImageColorTextLS
- : document.documentElement.style.getPropertyValue("--input-text") ||
- "#dddddd",
- PreviewImageColorBg = PreviewImageColorBgLS ? PreviewImageColorBgLS : "",
- // Speech & Recognition widget settings
- SpeechAndRecognationSpeech = SpeechAndRecognationSpeechLS
- ? JSON.parse(SpeechAndRecognationSpeechLS)
- : true,
- SpeechAndRecognationSpeechSaveAs = JSON.parse(
- localStorage.getItem(`${idExt}.SpeechAndRecognationSpeechSaveAs`),
- false
- ),
- // Preview image, video and audio select list combo
- PreviewImageVideoCombo = PreviewImageVideoComboLS
- ? JSON.parse(PreviewImageVideoComboLS)
- : true;
-
-// Preview image, video and audio content loading function
-const SUPPORTS_FORMAT = {
- image: ["jpg", "jpeg", "bmp", "png", "gif", "tiff", "avif"],
- video: ["mp4", "webm"],
- audio: ["ogg", "wav", "mp3", "webm"],
-};
-
-const loadingContent = (src) => {
- return new Promise((res, rej) => {
- let ext = src.slice(src.lastIndexOf(".") + 1).toLowerCase();
- ext = /\w+/.exec(ext)[0];
-
- if (SUPPORTS_FORMAT.image.includes(ext)) {
- const img = new Image();
- img.crossOrigin = "";
- img.onload = (e) => res({ raw: img, type: "image", src });
- img.onerror = (err) => rej(err);
- img.src = src;
- }
-
- if (SUPPORTS_FORMAT.video.includes(ext)) {
- const video = document.querySelector(".preview_vid");
- video.onerror = (err) => rej(err);
-
- video.addEventListener("canplay", (e) => {
- if (video.videoWidth !== 0) {
- res({ raw: video, type: "video", src });
- }
- });
- video.src = src;
- }
-
- if (SUPPORTS_FORMAT.audio.includes(ext)) {
- const audio = document.querySelector(".preview_audio");
- audio.onerror = (err) => rej(err);
-
- audio.addEventListener("canplaythrough", (e) => {
- res({ raw: audio, type: "audio", src });
- });
- audio.src = src;
- }
- });
-};
-
-function drawVid(raw, canvas, ctx, type) {
- ctx.drawImage(raw, 0, 0, canvas.width, canvas.height);
-
- canvas.requanim = requestAnimationFrame(
- drawVid.bind(this, raw, canvas, ctx, type)
- );
-}
-//
-
// Register Extension
app.registerExtension({
name: idExt,
init() {
addStylesheet("css/extrasnode/extras_node_styles.css", import.meta.url);
-
- // -- Preview image, audio, video --
-
- // Close when click on item inside context menu
- function closePreviewContextClick() {
- if (this.root?.preview_content_combo) {
- this.root.preview_content_combo.remove();
- this.root.preview_content_combo = null;
- }
- }
-
- // Add preview element
- function addPreviewModule(params, element) {
- if (!PreviewImageVideoCombo) return;
-
- const [name, value, options] = params;
-
- if (value instanceof Object) return;
-
- if (!this.root?.preview_content_combo) {
- const preview_content_combo = $el(
- "div.preview_content_combo",
- {
- style: {
- position: "absolute",
- maxWidth: "160px",
- zIndex: 9999999,
- },
- parent: document.body,
- },
- [
- $el("canvas.preview_img", {
- style: { width: "100%" },
- }),
- $el("video.preview_vid", {
- style: { opacity: 0, width: 0, height: 0 },
- crossOrigin: "anonymous",
- autoplay: false,
- muted: false,
- preload: "auto",
- }),
- $el("audio.preview_audio", {
- style: { opacity: 0, position: "absolute", left: 0 },
- crossOrigin: "anonymous",
- autoplay: false,
- muted: false,
- preload: "auto",
- controls: true,
- }),
- ]
- );
-
- this.root.preview_content_combo = preview_content_combo;
- }
-
- LiteGraph.pointerListenerAdd(element, "enter", (e) => {
- if (element?.dataset?.value) {
- const body_rect = document.body.getBoundingClientRect();
- const root_rect = this.root.getBoundingClientRect();
- const { x, y } = element.getBoundingClientRect();
- //const preview_rect = this.root.preview_content_combo.getBoundingClientRect();
-
- //const scale = app.graph.extra.ds.scale;
-
- const canvas = this.root.preview_content_combo.children[0];
- const ctx = canvas.getContext("2d");
-
- loadingContent(
- api.apiURL(
- `/view?filename=${encodeURIComponent(
- element.dataset.value
- )}&type=input`
- )
- ).then(({ raw, type, src }) => {
- this.root.preview_content_combo.style.maxWidth =
- type === "audio" ? "300px" : "160px";
-
- const previewWidth = parseInt(
- this.root.preview_content_combo.style.maxWidth
- );
-
- // if (scale >= 1) {
- // this.root.preview_content_combo.style.top = `${
- // (y - root_rect.top) / scale
- // }px`;
- // } else {
- // this.root.preview_content_combo.style.top = `${
- // y - root_rect.top
- // }px`;
- // }
-
- this.root.preview_content_combo.style.top = `${y}px`;
-
- if (
- body_rect.width &&
- root_rect.right + previewWidth > body_rect.width
- ) {
- this.root.preview_content_combo.style.left = `${
- root_rect.left - previewWidth - 10
- }px`;
- } else {
- this.root.preview_content_combo.style.left = `${
- root_rect.left + root_rect.width + 10
- }px`;
- }
-
- ctx.clearRect(0, 0, canvas.width, canvas.height);
- const video = document.querySelector(".preview_vid");
- const audio = document.querySelector(".preview_audio");
-
- if (type === "image") {
- canvas.width = raw.naturalWidth;
- canvas.height = raw.naturalHeight;
- if (!video.paused) {
- video.pause();
- video.currentTime = 0;
- }
- cancelAnimationFrame(canvas.requanim);
- } else if (type === "video") {
- canvas.width = raw.videoWidth;
- canvas.height = raw.videoHeight;
- video.play();
- } else if (type === "audio") {
- Object.assign(audio.style, {
- opacity: 1,
- display: "",
- });
- audio.play();
- }
-
- if (type === "video" || type === "image") {
- Object.assign(audio.style, {
- opacity: 0,
- display: "none",
- });
- !audio.paused && audio.pause();
- ctx.drawImage(raw, 0, 0, canvas.width, canvas.height);
- }
-
- if (type === "video")
- canvas.requanim = requestAnimationFrame(
- drawVid.bind(this, raw, canvas, ctx, type)
- );
- });
- }
- });
-
- LiteGraph.pointerListenerAdd(element, "leave", (e) => {
- if (element?.dataset?.value) {
- const canvas = this.root.preview_content_combo.children[0];
- const video = document.querySelector(".preview_vid");
- if (!video.paused) {
- video.pause();
- video.currentTime = 0;
- }
- cancelAnimationFrame(canvas.requanim);
- }
- });
- }
-
- // AddItem
- const addItem = ContextMenu.prototype.addItem;
- ContextMenu.prototype.addItem = function () {
- const element = addItem?.apply(this, arguments);
-
- addPreviewModule.call(this, arguments, element);
- };
-
- // CloseItem
- const closeItem = ContextMenu.prototype.close;
- ContextMenu.prototype.close = function () {
- closeItem?.apply(this, arguments);
-
- closePreviewContextClick.call(this);
- };
-
- // Old verison comfyui, support context menu LiteGraph path...
- // AddItem
- const addItemLitegraph = LiteGraph.ContextMenu.prototype.addItem;
- LiteGraph.ContextMenu.prototype.addItem = function () {
- const element = addItemLitegraph?.apply(this, arguments);
-
- addPreviewModule.call(this, arguments, element);
- };
-
- // CloseItem
- const closeItemLitegraph = LiteGraph.ContextMenu.prototype.close;
- LiteGraph.ContextMenu.prototype.close = function () {
- closeItemLitegraph?.apply(this, arguments);
-
- closePreviewContextClick.call(this);
- };
-
- const originalCloseAllContextMenus = LiteGraph.closeAllContextMenus;
- LiteGraph.closeAllContextMenus = function () {
- originalCloseAllContextMenus?.apply(this, arguments);
-
- const previewCombo = document.querySelector(".preview_content_combo");
- if (previewCombo) {
- previewCombo.remove();
- }
- };
- // -- end - Preview image, audio, video --
-
- // PreviewImage settings ui
- app.ui.settings.addSetting({
- id: `${idExt}.PreviewImage`,
- name: "🔸 Preview Image",
- defaultValue: true,
- type: (name, sett, val) => {
- const newUI = document.querySelector(".p-dialog-header");
- return $el("tr", [
- !newUI
- ? $el("td", [
- $el("label", {
- textContent: name,
- for: convertIdClass(`${idExt}.PreviewImage_size_checkbox`),
- }),
- ])
- : "",
- $el("td", [
- $el(
- "label",
- {
- style: { display: "block" },
- textContent: "Display image size: ",
- for: convertIdClass(`${idExt}.PreviewImage_size_checkbox`),
- },
- [
- $el("input", {
- id: convertIdClass(`${idExt}.PreviewImage_size_checkbox`),
- type: "checkbox",
- checked: val,
- onchange: (e) => {
- const checked = !!e.target.checked;
- PreviewImageSize = checked;
-
- localStorage.setItem(
- `Comfy.Settings.${idExt}.PreviewImage`,
- checked
- );
- sett(checked);
- },
- }),
- ]
- ),
- $el(
- "label",
- {
- textContent: "Color text: ",
- style: { display: "block" },
- },
- [
- $el("input", {
- id: convertIdClass(`${idExt}.PreviewImage_color_input`),
- type: "color",
- value: PreviewImageColorText,
- onchange: (e) => {
- const colorVal =
- e.target.value ||
- document.documentElement.style.getPropertyValue(
- "--input-text"
- ) ||
- "#dddddd";
- PreviewImageColorText = colorVal;
- localStorage.setItem(
- `Comfy.Settings.${idExt}.PreviewImageColorText`,
- PreviewImageColorText
- );
- },
- }),
- ]
- ),
- $el(
- "label",
- {
- textContent: "Color background: ",
- style: { display: "block" },
- },
- [
- $el("input", {
- id: convertIdClass(
- `${idExt}.PreviewImage_color_background_input`
- ),
- type: "color",
- value: PreviewImageColorBg,
- onchange: (e) => {
- const colorVal = e.target.value || "";
- PreviewImageColorBg = colorVal;
- localStorage.setItem(
- `Comfy.Settings.${idExt}.PreviewImageColorBg`,
- PreviewImageColorBg
- );
- },
- }),
- ]
- ),
- $el("button", {
- textContent: "Reset colors",
- onclick: () => {
- PreviewImageColorText = isValidStyle(
- "color",
- document.documentElement.style.getPropertyValue(
- "--input-text"
- ) || "#ddd"
- ).color_hex;
- PreviewImageColorBg = "";
-
- document.querySelector(
- "#alekpet_ExtrasNode_PreviewImage_color_input"
- ).value = PreviewImageColorText;
- document.querySelector(
- "#alekpet_ExtrasNode_PreviewImage_color_background_input"
- ).value = PreviewImageColorBg;
-
- localStorage.setItem(
- `Comfy.Settings.${idExt}.PreviewImageColorText`,
- PreviewImageColorText
- );
- localStorage.setItem(
- `Comfy.Settings.${idExt}.PreviewImageColorBg`,
- PreviewImageColorBg
- );
- },
- style: {
- display: "block",
- },
- }),
- ]),
- ]);
- },
- });
-
- // Speech & Recognition speech settings ui
- app.ui.settings.addSetting({
- id: `${idExt}.SpeechAndRecognationSpeech`,
- name: "🔸 Speak text & Recognition speech",
- defaultValue: true,
- type: (name, sett, val) => {
- const newUI = document.querySelector(".p-dialog-header");
- return $el("tr", [
- !newUI
- ? $el("td", [
- $el("label", {
- textContent: name,
- for: convertIdClass(
- `${idExt}.SpeechAndRecognationSpeech_show`
- ),
- }),
- ])
- : "",
- $el("td", [
- $el(
- "label",
- {
- style: { display: "flex", alignItems: "center" },
- textContent: "Enabled: ",
- for: convertIdClass(`${idExt}.SpeechAndRecognationSpeech_show`),
- },
- [
- $el("input", {
- id: convertIdClass(
- `${idExt}.SpeechAndRecognationSpeech_show`
- ),
- type: "checkbox",
- checked: val,
- onchange: async (e) => {
- const checked = !!e.target.checked;
- SpeechAndRecognationSpeech = checked;
- localStorage.setItem(
- `Comfy.Settings.${idExt}.SpeechAndRecognationSpeech`,
- checked
- );
- if (await comfyuiDesktopConfirm("Reload page?")) {
- location.reload();
- }
- sett(checked);
- },
- }),
- $el("span", {
- textContent: "(Then reload the page)",
- style: { fontSize: "0.6rem", color: "yellow" },
- }),
- ]
- ),
- $el(
- "div",
- {
- style: {
- display: "flex",
- gap: "5px",
- margin: "5px 0",
- },
- title: "Show modal window when saving recorded audio.",
- },
- [
- $el("span", { textContent: "Output Save as?" }),
- $el("input", {
- type: "checkbox",
- checked: SpeechAndRecognationSpeechSaveAs,
- onchange: (e) => {
- localStorage.setItem(
- `${idExt}.SpeechAndRecognationSpeechSaveAs`,
- !!e.target.checked
- );
- SpeechAndRecognationSpeechSaveAs = !!e.target.checked;
- checkboxLSCheckedByKey(
- `${idExt}.SpeechAndRecognationSpeechSaveAs`,
- ".alekpet_extras_node_recognition_saveAs"
- );
- },
- }),
- ]
- ),
- $el("button", {
- textContent: "Speech settings",
- onclick: () => {
- new RecognationSpeechDialog().show();
- },
- style: {
- display: "block",
- },
- }),
- ]),
- ]);
- },
- });
-
- // Preview image, video and audio settings ui
- app.ui.settings.addSetting({
- id: `${idExt}.PreviewImageVideoCombo`,
- name: "🔸 Preview images and videos in the list",
- defaultValue: true,
- type: "boolean",
- onChange(value) {
- PreviewImageVideoCombo = value;
- },
- });
},
async beforeRegisterNodeDef(nodeType, nodeData, app) {
// --- Preview Text Node
@@ -698,172 +151,10 @@ app.registerExtension({
};
break;
}
- case "PreviewImage": {
- // Node Created
- const onNodeCreated = nodeType.prototype.onNodeCreated;
- nodeType.prototype.onNodeCreated = function () {
- const ret = onNodeCreated
- ? onNodeCreated.apply(this, arguments)
- : undefined;
-
- if (PreviewImageSize)
- createPreiviewSize(this, nodeData.name, {
- color: PreviewImageColorText,
- });
-
- return ret;
- };
-
- const onExecuted = nodeType.prototype.onExecuted;
- nodeType.prototype.onExecuted = function ({ images }) {
- onExecuted?.apply(this, arguments);
-
- if (PreviewImageSize !== undefined) {
- let widgetRes = this?.widgets?.find(
- (w) => w.type === "show_resolution"
- );
-
- if (!PreviewImageSize) {
- if (widgetRes) {
- for (const w of this.widgets) {
- if (w.type === "show_resolution") {
- w?.onRemove();
- }
- }
- this.widgets.length = 0;
- this.onResize(this.size);
- }
- } else {
- if (!widgetRes) {
- widgetRes = createPreiviewSize(this, nodeData.name, {
- color: PreviewImageColorText,
- });
- this.onResize(this.size);
- } else {
- widgetRes.element.style.color = PreviewImageColorText;
- }
-
- if (images.length) {
- const image = new Image();
- image.onload = () => {
- widgetRes.value = `
Width: ${image.naturalWidth}, Height: ${
- image.naturalHeight
- }
`;
- };
- image.src = api.apiURL(
- "/view?" +
- new URLSearchParams(images[0]).toString() +
- app.getPreviewFormatParam() +
- app.getRandParam()
- );
- }
- }
- }
- };
-
- break;
- }
default: {
break;
}
}
-
- // -- Speech & Recognition speech widget
- // If ui settings is true and SpeechSynthesis or speechRecognition is not undefined
- if (SpeechAndRecognationSpeech && (speechRect || SpeechSynthesis)) {
- let nodeIsMultiString = false;
- let outputNode = false;
-
- if (nodeData?.input && nodeData?.input?.required) {
- for (const inp of Object.keys(nodeData.input.required)) {
- if (nodeData.input.required[inp][1]?.multiline) {
- const type = nodeData.input.required[inp][0];
-
- if (["STRING"].includes(type)) {
- nodeIsMultiString = true;
- break;
- }
- }
- }
- }
-
- if (nodeData?.output) {
- for (const out of nodeData.output) {
- if (["STRING"].includes(out)) {
- nodeIsMultiString = true;
- outputNode = true;
- break;
- }
- }
- }
-
- if (nodeIsMultiString) {
- // Node Created
- const onNodeCreated = nodeType.prototype.onNodeCreated;
- nodeType.prototype.onNodeCreated = async function () {
- const ret = onNodeCreated
- ? onNodeCreated.apply(this, arguments)
- : undefined;
-
- // Find all widget type customtext
- const widgetsTextMulti = this?.widgets?.filter((w) =>
- !outputNode
- ? ["customtext", "converted-widget"].includes(w.type)
- : ["customtext"].includes(w.type)
- );
- const isIncludesSpeech = this?.widgets?.some(
- (w) => w.type === "speak_and_recognation_type"
- );
-
- await new Promise((res) =>
- setTimeout(() => {
- res();
- }, 16 * this.widgets?.length ?? 1)
- );
-
- if (!isIncludesSpeech && widgetsTextMulti?.length) {
- widgetsTextMulti.forEach(async (w) => {
- this.addCustomWidget(
- SpeechWidget(this, "speak_and_recognation", [false, true], w)
- );
- });
- }
-
- return ret;
- };
-
- // onConfigure
- const onConfigure = nodeType.prototype.onConfigure;
- nodeType.prototype.onConfigure = async function (w) {
- onConfigure?.apply(this, arguments);
- if (w?.widgets_values?.length) {
- await new Promise((res) =>
- setTimeout(() => {
- res();
- }, 16 * this.widgets?.length ?? 1)
- );
-
- const ids_speech_clear = this.widgets.reduce(function (
- arr,
- el,
- idx
- ) {
- if (el.type === "speak_and_recognation_type") arr.push(idx);
- return arr;
- },
- []);
-
- for (const id of ids_speech_clear)
- this?.widgets[id]?.callback(w.widgets_values[id]);
- }
- };
- }
- }
- // -- end - Speech & Recognition speech widget
},
});
diff --git a/ExtrasNode/js/extras_preview_image_size.js b/ExtrasNode/js/extras_preview_image_size.js
new file mode 100644
index 0000000..654f8db
--- /dev/null
+++ b/ExtrasNode/js/extras_preview_image_size.js
@@ -0,0 +1,241 @@
+/*
+ * Title: Extras extension - Preview Image Size
+ * Author: AlekPet
+ * Github: https://github.com/AlekPet/ComfyUI_Custom_Nodes_AlekPet/tree/master/ExtrasNode
+ */
+
+import { app } from "../../scripts/app.js";
+import { api } from "../../scripts/api.js";
+import { $el } from "../../scripts/ui.js";
+import { isValidStyle } from "./utils.js";
+import { createPreiviewSize } from "./lib/extrasnode/extras_node_widgets.js";
+
+const convertIdClass = (text) => text.replaceAll(".", "_");
+const idExt = "alekpet.ExtrasNode";
+
+// LocalStorage settings
+const PreviewImageSizeLS = localStorage.getItem(
+ `Comfy.Settings.${idExt}.PreviewImage`
+);
+const PreviewImageColorTextLS = localStorage.getItem(
+ `Comfy.Settings.${idExt}.PreviewImageColorText`
+);
+const PreviewImageColorBgLS = localStorage.getItem(
+ `Comfy.Settings.${idExt}.PreviewImageColorBg`
+);
+
+// Settings set values from LS or default
+let PreviewImageSize = PreviewImageSizeLS
+ ? JSON.parse(PreviewImageSizeLS)
+ : false,
+ PreviewImageColorText =
+ PreviewImageColorTextLS && PreviewImageColorTextLS.trim() !== ""
+ ? PreviewImageColorTextLS
+ : document.documentElement.style.getPropertyValue("--input-text") ||
+ "#dddddd",
+ PreviewImageColorBg = PreviewImageColorBgLS ? PreviewImageColorBgLS : "";
+
+// -- Extension: PreviewImageSize --
+app.registerExtension({
+ name: `${idExt}.PreviewImageSize`,
+
+ init() {
+ // PreviewImage settings ui
+ app.ui.settings.addSetting({
+ id: `${idExt}.PreviewImage`,
+ name: "🔸 Preview Image Size",
+ defaultValue: true,
+ type: (name, sett, val) => {
+ const newUI = document.querySelector(".p-dialog-header");
+ return $el("tr", [
+ !newUI
+ ? $el("td", [
+ $el("label", {
+ textContent: name,
+ for: convertIdClass(`${idExt}.PreviewImage_size_checkbox`),
+ }),
+ ])
+ : "",
+ $el("td", [
+ $el(
+ "label",
+ {
+ style: { display: "block" },
+ textContent: "Display image size: ",
+ for: convertIdClass(`${idExt}.PreviewImage_size_checkbox`),
+ },
+ [
+ $el("input", {
+ id: convertIdClass(`${idExt}.PreviewImage_size_checkbox`),
+ type: "checkbox",
+ checked: val,
+ onchange: (e) => {
+ const checked = !!e.target.checked;
+ PreviewImageSize = checked;
+
+ localStorage.setItem(
+ `Comfy.Settings.${idExt}.PreviewImage`,
+ checked
+ );
+ sett(checked);
+ },
+ }),
+ ]
+ ),
+ $el(
+ "label",
+ {
+ textContent: "Color text: ",
+ style: { display: "block" },
+ },
+ [
+ $el("input", {
+ id: convertIdClass(`${idExt}.PreviewImage_color_input`),
+ type: "color",
+ value: PreviewImageColorText,
+ onchange: (e) => {
+ const colorVal =
+ e.target.value ||
+ document.documentElement.style.getPropertyValue(
+ "--input-text"
+ ) ||
+ "#dddddd";
+ PreviewImageColorText = colorVal;
+ localStorage.setItem(
+ `Comfy.Settings.${idExt}.PreviewImageColorText`,
+ PreviewImageColorText
+ );
+ },
+ }),
+ ]
+ ),
+ $el(
+ "label",
+ {
+ textContent: "Color background: ",
+ style: { display: "block" },
+ },
+ [
+ $el("input", {
+ id: convertIdClass(
+ `${idExt}.PreviewImage_color_background_input`
+ ),
+ type: "color",
+ value: PreviewImageColorBg,
+ onchange: (e) => {
+ const colorVal = e.target.value || "";
+ PreviewImageColorBg = colorVal;
+ localStorage.setItem(
+ `Comfy.Settings.${idExt}.PreviewImageColorBg`,
+ PreviewImageColorBg
+ );
+ },
+ }),
+ ]
+ ),
+ $el("button", {
+ textContent: "Reset colors",
+ onclick: () => {
+ PreviewImageColorText = isValidStyle(
+ "color",
+ document.documentElement.style.getPropertyValue(
+ "--input-text"
+ ) || "#ddd"
+ ).color_hex;
+ PreviewImageColorBg = "";
+
+ document.querySelector(
+ "#alekpet_ExtrasNode_PreviewImage_color_input"
+ ).value = PreviewImageColorText;
+ document.querySelector(
+ "#alekpet_ExtrasNode_PreviewImage_color_background_input"
+ ).value = PreviewImageColorBg;
+
+ localStorage.setItem(
+ `Comfy.Settings.${idExt}.PreviewImageColorText`,
+ PreviewImageColorText
+ );
+ localStorage.setItem(
+ `Comfy.Settings.${idExt}.PreviewImageColorBg`,
+ PreviewImageColorBg
+ );
+ },
+ style: {
+ display: "block",
+ },
+ }),
+ ]),
+ ]);
+ },
+ });
+ },
+ async beforeRegisterNodeDef(nodeType, nodeData, app) {
+ if (nodeData.name === "PreviewImage") {
+ // Node Created
+ const onNodeCreated = nodeType.prototype.onNodeCreated;
+ nodeType.prototype.onNodeCreated = function () {
+ const ret = onNodeCreated
+ ? onNodeCreated.apply(this, arguments)
+ : undefined;
+
+ if (PreviewImageSize)
+ createPreiviewSize(this, nodeData.name, {
+ color: PreviewImageColorText,
+ });
+
+ return ret;
+ };
+
+ const onExecuted = nodeType.prototype.onExecuted;
+ nodeType.prototype.onExecuted = function ({ images }) {
+ onExecuted?.apply(this, arguments);
+
+ if (PreviewImageSize !== undefined) {
+ let widgetRes = this?.widgets?.find(
+ (w) => w.type === "show_resolution"
+ );
+
+ if (!PreviewImageSize) {
+ if (widgetRes) {
+ for (const w of this.widgets) {
+ if (w.type === "show_resolution") {
+ w?.onRemove();
+ }
+ }
+ this.widgets.length = 0;
+ this.onResize(this.size);
+ }
+ } else {
+ if (!widgetRes) {
+ widgetRes = createPreiviewSize(this, nodeData.name, {
+ color: PreviewImageColorText,
+ });
+ this.onResize(this.size);
+ } else {
+ widgetRes.element.style.color = PreviewImageColorText;
+ }
+
+ if (images.length) {
+ const image = new Image();
+ image.onload = () => {
+ widgetRes.value = `Width: ${image.naturalWidth}, Height: ${
+ image.naturalHeight
+ }
`;
+ };
+ image.src = api.apiURL(
+ "/view?" +
+ new URLSearchParams(images[0]).toString() +
+ app.getPreviewFormatParam() +
+ app.getRandParam()
+ );
+ }
+ }
+ }
+ };
+ }
+ },
+});
diff --git a/ExtrasNode/js/extras_preview_media.js b/ExtrasNode/js/extras_preview_media.js
new file mode 100644
index 0000000..df3a264
--- /dev/null
+++ b/ExtrasNode/js/extras_preview_media.js
@@ -0,0 +1,294 @@
+/*
+ * Title: Extras extension - Preview Media (image, audio, video)
+ * Author: AlekPet
+ * Github: https://github.com/AlekPet/ComfyUI_Custom_Nodes_AlekPet/tree/master/ExtrasNode
+ */
+
+import { app } from "../../scripts/app.js";
+import { api } from "../../scripts/api.js";
+import { $el } from "../../scripts/ui.js";
+
+const idExt = "alekpet.ExtrasNode";
+
+// LocalStorage settings - Preview image, video and audio select list combo
+const PreviewImageVideoComboLS = localStorage.getItem(
+ `Comfy.Settings.${idExt}.PreviewImageVideoCombo`
+);
+
+// Settings set values from LS or default - Preview image, video and audio select list combo
+let PreviewImageVideoCombo = PreviewImageVideoComboLS
+ ? JSON.parse(PreviewImageVideoComboLS)
+ : true;
+
+// Preview image, video and audio content loading function
+const SUPPORTS_FORMAT = {
+ image: ["jpg", "jpeg", "bmp", "png", "gif", "tiff", "avif"],
+ video: ["mp4", "webm"],
+ audio: ["ogg", "wav", "mp3", "webm"],
+};
+
+const loadingContent = (src) => {
+ return new Promise((res, rej) => {
+ let ext = src.slice(src.lastIndexOf(".") + 1).toLowerCase();
+ ext = /\w+/.exec(ext)[0];
+
+ if (SUPPORTS_FORMAT.image.includes(ext)) {
+ const img = new Image();
+ img.crossOrigin = "";
+ img.onload = (e) => res({ raw: img, type: "image", src });
+ img.onerror = (err) => rej(err);
+ img.src = src;
+ }
+
+ if (SUPPORTS_FORMAT.video.includes(ext)) {
+ const video = document.querySelector(".preview_vid");
+ video.onerror = (err) => rej(err);
+
+ video.addEventListener("canplay", (e) => {
+ if (video.videoWidth !== 0) {
+ res({ raw: video, type: "video", src });
+ }
+ });
+ video.src = src;
+ }
+
+ if (SUPPORTS_FORMAT.audio.includes(ext)) {
+ const audio = document.querySelector(".preview_audio");
+ audio.onerror = (err) => rej(err);
+
+ audio.addEventListener("canplaythrough", (e) => {
+ res({ raw: audio, type: "audio", src });
+ });
+ audio.src = src;
+ }
+ });
+};
+
+function drawVid(raw, canvas, ctx, type) {
+ ctx.drawImage(raw, 0, 0, canvas.width, canvas.height);
+
+ canvas.requanim = requestAnimationFrame(
+ drawVid.bind(this, raw, canvas, ctx, type)
+ );
+}
+//
+
+// -- Extension: Preview image, audio, video --
+app.registerExtension({
+ name: `${idExt}.PreviewMedia`,
+ init() {
+ // Preview image, video and audio settings ui
+ app.ui.settings.addSetting({
+ id: `${idExt}.PreviewImageVideoCombo`,
+ name: "🔸 Preview images and videos in the list",
+ defaultValue: true,
+ type: "boolean",
+ onChange(value) {
+ PreviewImageVideoCombo = value;
+ },
+ });
+
+ // -- Preview image, audio, video --
+
+ // Close when click on item inside context menu
+ function closePreviewContextClick() {
+ if (this.root?.preview_content_combo) {
+ this.root.preview_content_combo.remove();
+ this.root.preview_content_combo = null;
+ }
+ }
+
+ // Add preview element
+ function addPreviewModule(params, element) {
+ if (!PreviewImageVideoCombo) return;
+
+ const [name, value, options] = params;
+
+ if (value instanceof Object) return;
+
+ if (!this.root?.preview_content_combo) {
+ const preview_content_combo = $el(
+ "div.preview_content_combo",
+ {
+ style: {
+ position: "absolute",
+ maxWidth: "160px",
+ zIndex: 9999999,
+ },
+ parent: document.body,
+ },
+ [
+ $el("canvas.preview_img", {
+ style: { width: "100%" },
+ }),
+ $el("video.preview_vid", {
+ style: { opacity: 0, width: 0, height: 0 },
+ crossOrigin: "anonymous",
+ autoplay: false,
+ muted: false,
+ preload: "auto",
+ }),
+ $el("audio.preview_audio", {
+ style: { opacity: 0, position: "absolute", left: 0 },
+ crossOrigin: "anonymous",
+ autoplay: false,
+ muted: false,
+ preload: "auto",
+ controls: true,
+ }),
+ ]
+ );
+
+ this.root.preview_content_combo = preview_content_combo;
+ }
+
+ LiteGraph.pointerListenerAdd(element, "enter", (e) => {
+ if (element?.dataset?.value) {
+ const body_rect = document.body.getBoundingClientRect();
+ const root_rect = this.root.getBoundingClientRect();
+ const { x, y } = element.getBoundingClientRect();
+ //const preview_rect = this.root.preview_content_combo.getBoundingClientRect();
+
+ //const scale = app.graph.extra.ds.scale;
+
+ const canvas = this.root.preview_content_combo.children[0];
+ const ctx = canvas.getContext("2d");
+
+ loadingContent(
+ api.apiURL(
+ `/view?filename=${encodeURIComponent(
+ element.dataset.value
+ )}&type=input`
+ )
+ ).then(({ raw, type, src }) => {
+ this.root.preview_content_combo.style.maxWidth =
+ type === "audio" ? "300px" : "160px";
+
+ const previewWidth = parseInt(
+ this.root.preview_content_combo.style.maxWidth
+ );
+
+ // if (scale >= 1) {
+ // this.root.preview_content_combo.style.top = `${
+ // (y - root_rect.top) / scale
+ // }px`;
+ // } else {
+ // this.root.preview_content_combo.style.top = `${
+ // y - root_rect.top
+ // }px`;
+ // }
+
+ this.root.preview_content_combo.style.top = `${y}px`;
+
+ if (
+ body_rect.width &&
+ root_rect.right + previewWidth > body_rect.width
+ ) {
+ this.root.preview_content_combo.style.left = `${
+ root_rect.left - previewWidth - 10
+ }px`;
+ } else {
+ this.root.preview_content_combo.style.left = `${
+ root_rect.left + root_rect.width + 10
+ }px`;
+ }
+
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ const video = document.querySelector(".preview_vid");
+ const audio = document.querySelector(".preview_audio");
+
+ if (type === "image") {
+ canvas.width = raw.naturalWidth;
+ canvas.height = raw.naturalHeight;
+ if (!video.paused) {
+ video.pause();
+ video.currentTime = 0;
+ }
+ cancelAnimationFrame(canvas.requanim);
+ } else if (type === "video") {
+ canvas.width = raw.videoWidth;
+ canvas.height = raw.videoHeight;
+ video.play();
+ } else if (type === "audio") {
+ Object.assign(audio.style, {
+ opacity: 1,
+ display: "",
+ });
+ audio.play();
+ }
+
+ if (type === "video" || type === "image") {
+ Object.assign(audio.style, {
+ opacity: 0,
+ display: "none",
+ });
+ !audio.paused && audio.pause();
+ ctx.drawImage(raw, 0, 0, canvas.width, canvas.height);
+ }
+
+ if (type === "video")
+ canvas.requanim = requestAnimationFrame(
+ drawVid.bind(this, raw, canvas, ctx, type)
+ );
+ });
+ }
+ });
+
+ LiteGraph.pointerListenerAdd(element, "leave", (e) => {
+ if (element?.dataset?.value) {
+ const canvas = this.root.preview_content_combo.children[0];
+ const video = document.querySelector(".preview_vid");
+ if (!video.paused) {
+ video.pause();
+ video.currentTime = 0;
+ }
+ cancelAnimationFrame(canvas.requanim);
+ }
+ });
+ }
+
+ // AddItem
+ const addItem = ContextMenu.prototype.addItem;
+ ContextMenu.prototype.addItem = function () {
+ const element = addItem?.apply(this, arguments);
+
+ addPreviewModule.call(this, arguments, element);
+ };
+
+ // CloseItem
+ const closeItem = ContextMenu.prototype.close;
+ ContextMenu.prototype.close = function () {
+ closeItem?.apply(this, arguments);
+
+ closePreviewContextClick.call(this);
+ };
+
+ // Old verison comfyui, support context menu LiteGraph path...
+ // AddItem
+ const addItemLitegraph = LiteGraph.ContextMenu.prototype.addItem;
+ LiteGraph.ContextMenu.prototype.addItem = function () {
+ const element = addItemLitegraph?.apply(this, arguments);
+
+ addPreviewModule.call(this, arguments, element);
+ };
+
+ // CloseItem
+ const closeItemLitegraph = LiteGraph.ContextMenu.prototype.close;
+ LiteGraph.ContextMenu.prototype.close = function () {
+ closeItemLitegraph?.apply(this, arguments);
+
+ closePreviewContextClick.call(this);
+ };
+
+ const originalCloseAllContextMenus = LiteGraph.closeAllContextMenus;
+ LiteGraph.closeAllContextMenus = function () {
+ originalCloseAllContextMenus?.apply(this, arguments);
+
+ const previewCombo = document.querySelector(".preview_content_combo");
+ if (previewCombo) {
+ previewCombo.remove();
+ }
+ };
+ // -- end - Preview image, audio, video --
+ },
+});
diff --git a/ExtrasNode/js/extras_speech_and_recognition.js b/ExtrasNode/js/extras_speech_and_recognition.js
new file mode 100644
index 0000000..ecd1a0b
--- /dev/null
+++ b/ExtrasNode/js/extras_speech_and_recognition.js
@@ -0,0 +1,226 @@
+/*
+ * Title: Extras extension - Speech And Recognition
+ * Author: AlekPet
+ * Github: https://github.com/AlekPet/ComfyUI_Custom_Nodes_AlekPet/tree/master/ExtrasNode
+ */
+import { app } from "../../scripts/app.js";
+import { $el } from "../../scripts/ui.js";
+import { comfyuiDesktopConfirm } from "./utils.js";
+import { addStylesheet } from "../../scripts/utils.js";
+import {
+ SpeechWidget,
+ speechRect,
+ SpeechSynthesis,
+ checkboxLSCheckedByKey,
+} from "./lib/extrasnode/extras_speech_and_recognation_widget.js";
+import { RecognationSpeechDialog } from "./lib/extrasnode/extras_speech_and_recognation_dialog.js";
+
+const convertIdClass = (text) => text.replaceAll(".", "_");
+const idExt = "alekpet.ExtrasNode";
+
+// Recognation & speech localStorage settings
+const SpeechAndRecognationSpeechLS = localStorage.getItem(
+ `Comfy.Settings.${idExt}.SpeechAndRecognationSpeech`
+);
+
+// Settings set values from LS or default for Speech & Recognition widget settings
+let SpeechAndRecognationSpeech = SpeechAndRecognationSpeechLS
+ ? JSON.parse(SpeechAndRecognationSpeechLS)
+ : true,
+ SpeechAndRecognationSpeechSaveAs = JSON.parse(
+ localStorage.getItem(`${idExt}.SpeechAndRecognationSpeechSaveAs`),
+ false
+ );
+
+// -- Extension: Speak text & Recognition speech --
+app.registerExtension({
+ name: `${idExt}.SpeechAndRecognationSpeech`,
+
+ init() {
+ addStylesheet(
+ "css/extrasnode/extras_node_speech_recognition_style.css",
+ import.meta.url
+ );
+
+ // Speech & Recognition speech settings ui
+ app.ui.settings.addSetting({
+ id: `${idExt}.SpeechAndRecognationSpeech`,
+ name: "🔸 Speak text & Recognition speech",
+ defaultValue: true,
+ type: (name, sett, val) => {
+ const newUI = document.querySelector(".p-dialog-header");
+ return $el("tr", [
+ !newUI
+ ? $el("td", [
+ $el("label", {
+ textContent: name,
+ for: convertIdClass(
+ `${idExt}.SpeechAndRecognationSpeech_show`
+ ),
+ }),
+ ])
+ : "",
+ $el("td", [
+ $el(
+ "label",
+ {
+ style: { display: "flex", alignItems: "center" },
+ textContent: "Enabled: ",
+ for: convertIdClass(`${idExt}.SpeechAndRecognationSpeech_show`),
+ },
+ [
+ $el("input", {
+ id: convertIdClass(
+ `${idExt}.SpeechAndRecognationSpeech_show`
+ ),
+ type: "checkbox",
+ checked: val,
+ onchange: async (e) => {
+ const checked = !!e.target.checked;
+ SpeechAndRecognationSpeech = checked;
+ localStorage.setItem(
+ `Comfy.Settings.${idExt}.SpeechAndRecognationSpeech`,
+ checked
+ );
+ if (await comfyuiDesktopConfirm("Reload page?")) {
+ location.reload();
+ }
+ sett(checked);
+ },
+ }),
+ $el("span", {
+ textContent: "(Then reload the page)",
+ style: { fontSize: "0.6rem", color: "yellow" },
+ }),
+ ]
+ ),
+ $el(
+ "div",
+ {
+ style: {
+ display: "flex",
+ gap: "5px",
+ margin: "5px 0",
+ },
+ title: "Show modal window when saving recorded audio.",
+ },
+ [
+ $el("span", { textContent: "Output Save as?" }),
+ $el("input", {
+ type: "checkbox",
+ checked: SpeechAndRecognationSpeechSaveAs,
+ onchange: (e) => {
+ localStorage.setItem(
+ `${idExt}.SpeechAndRecognationSpeechSaveAs`,
+ !!e.target.checked
+ );
+ SpeechAndRecognationSpeechSaveAs = !!e.target.checked;
+ checkboxLSCheckedByKey(
+ `${idExt}.SpeechAndRecognationSpeechSaveAs`,
+ ".alekpet_extras_node_recognition_saveAs"
+ );
+ },
+ }),
+ ]
+ ),
+ $el("button", {
+ textContent: "Speech settings",
+ onclick: () => {
+ new RecognationSpeechDialog().show();
+ },
+ style: {
+ display: "block",
+ },
+ }),
+ ]),
+ ]);
+ },
+ });
+ },
+ async beforeRegisterNodeDef(nodeType, nodeData, app) {
+ // -- Speech & Recognition speech widget
+ // If ui settings is true and SpeechSynthesis or speechRecognition is not undefined
+ if (SpeechAndRecognationSpeech && (speechRect || SpeechSynthesis)) {
+ // Node Created
+ const onNodeCreated = nodeType.prototype.onNodeCreated;
+ nodeType.prototype.onNodeCreated = async function () {
+ const ret = onNodeCreated
+ ? onNodeCreated.apply(this, arguments)
+ : undefined;
+
+ let nodeIsMultiString = false;
+
+ if (nodeData?.input && nodeData?.input?.required) {
+ for (const inp of Object.keys(nodeData.input.required)) {
+ if (
+ nodeData.input.required[inp][1]?.multiline &&
+ !nodeData.input.required[inp][1]?.forceInput
+ ) {
+ const type = nodeData.input.required[inp][0];
+
+ if (["STRING"].includes(type)) {
+ nodeIsMultiString = true;
+ break;
+ }
+ }
+ }
+ }
+
+ if (nodeData?.output) {
+ for (const out of nodeData.output) {
+ const isElementTextArea = this?.widgets?.some(
+ (w) =>
+ w?.element?.tagName === "TEXTAREA" ||
+ w?.inputEl?.tagName === "TEXTAREA"
+ );
+
+ if (isElementTextArea && ["STRING"].includes(out)) {
+ nodeIsMultiString = true;
+ break;
+ }
+ }
+ }
+
+ if (!nodeIsMultiString) return ret;
+
+ // Find all widget type customtext
+ const widgetsTextMulti = this?.widgets?.filter((w) =>
+ ["customtext", "converted-widget"].includes(w.type)
+ );
+
+ const isIncludesSpeech = this?.widgets?.some(
+ (w) => w.type === "speak_and_recognation_type"
+ );
+
+ if (!isIncludesSpeech && widgetsTextMulti?.length) {
+ widgetsTextMulti.forEach(async (w) => {
+ this.addCustomWidget(
+ SpeechWidget(this, "speak_and_recognation", [false, true], w)
+ );
+ });
+ }
+
+ return ret;
+ };
+
+ // onConfigure
+ const onConfigure = nodeType.prototype.onConfigure;
+ nodeType.prototype.onConfigure = async function (w) {
+ onConfigure?.apply(this, arguments);
+
+ if (w?.widgets_values?.length) {
+ const ids_speech_clear = this.widgets.reduce(function (arr, el, idx) {
+ if (el.type === "speak_and_recognation_type") arr.push(idx);
+ return arr;
+ }, []);
+
+ if (!ids_speech_clear.length) return;
+
+ for (const id of ids_speech_clear)
+ this?.widgets[id]?.callback(w.widgets_values[id]);
+ }
+ };
+ }
+ // -- end - Speech & Recognition speech widget
+ },
+});
diff --git a/ExtrasNode/lib/extras_node_widgets.js b/ExtrasNode/lib/extras_node_widgets.js
index d7f930e..92ce2fc 100644
--- a/ExtrasNode/lib/extras_node_widgets.js
+++ b/ExtrasNode/lib/extras_node_widgets.js
@@ -4,687 +4,12 @@
* Github: https://github.com/AlekPet/ComfyUI_Custom_Nodes_AlekPet/tree/master/ExtrasNode
*/
-import { api } from "../../../../scripts/api.js";
-import { app } from "../../../../scripts/app.js";
import { $el } from "../../../../scripts/ui.js";
-import {
- rgbToHex,
- isValidStyle,
- createWindowModal,
- animateClick,
- comfyuiDesktopAlert,
-} from "../../utils.js";
-import { RecognationSpeechDialog } from "./extras_node_dialogs.js";
+import { rgbToHex, isValidStyle } from "../../utils.js";
const idExt = "alekpet.ExtrasNode";
const CONVERTED_TYPE = "converted-widget";
-/* ~~~ Speech & Recognition speech Widget ~~~ */
-const spRecognition =
- window.SpeechRecognition || window.webkitSpeechRecognition;
-const SpeechSynthesis = window.speechSynthesis;
-
-const regExpFileName = /[^\p{L}\d]+/gu;
-const maxLenAudioFileName = 50;
-
-let speechRect;
-let mediaRecorder = null;
-let audioChunks = [];
-
-if (spRecognition) {
- speechRect = new spRecognition();
- speechRect.elements = null;
- speechRect.isRecognition = false;
- speechRect.isRecognitionAbort = false;
- speechRect.lastText = "";
-
- speechRect.addEventListener("result", (event) => {
- const results = event?.results[0][0]?.transcript;
- if (results && speechRect?.elements?.length) {
- const clear_text = speechRect.elements[0].querySelector(
- ".alekpet_extras_node_recognition_clear"
- );
-
- if (clear_text.checked) {
- speechRect.elements[1].value = results;
- } else {
- speechRect.elements[1].value += " " + results;
- }
- }
- });
-
- const resetSpeechRecognition = () => {
- if (speechRect?.elements?.length) {
- const info = speechRect.elements[0].querySelector(
- ".alekpet_extras_node_info span"
- );
- const icon_rec = speechRect.elements[0].querySelector(
- ".alekpet_extras_node_recognition_icon"
- );
-
- speechRect.elements[0].querySelector(
- ".alekpet_extras_node_recognition_aborted"
- ).style.display = "none";
-
- icon_rec.classList.remove("alekpet_extras_node_recognition_icon_active");
- setTimeout(() => (info.textContent = ""), 1000);
- }
-
- setStylesAllElements(".alekpet_extras_node_recognition_icon", null, {
- display: "inline-block",
- });
-
- if (mediaRecorder) {
- speechRect.lastText = speechRect?.elements[1].value;
- mediaRecorder.stop();
- } else {
- speechRect.lastText = "";
- speechRect.isRecognitionAbort = false;
- }
-
- mediaRecorder = null;
- audioChunks = [];
- speechRect.isRecognition = false;
-
- speechRect.elements = null;
- };
-
- speechRect.addEventListener("audiostart", (e) => {
- speechRect.isRecognition = true;
- });
-
- speechRect.addEventListener("end", () => {
- resetSpeechRecognition();
- speechRect.stop();
- });
-
- speechRect.addEventListener("speechend", () => {
- resetSpeechRecognition();
- speechRect.stop();
- });
-
- speechRect.addEventListener("error", (event) => {
- if (speechRect?.elements?.length) {
- const info = speechRect.elements[0].querySelector(
- ".alekpet_extras_node_info span"
- );
- info.textContent = event.error;
- }
- resetSpeechRecognition();
- console.log(">> Error recognition: " + event.error);
- });
-} else {
- console.warn(
- "Your browser does not support: SpeechRecognition.\nIf Firefox: go to about:config > media.webspeech.recognition.enable and media.webspeech.recognition.force_enable > set true"
- );
-}
-
-if (!SpeechSynthesis) {
- console.warn("Your browser does not support: speechSynthesis");
-}
-
-// Get position speech SpeechRecognition widget
-function getPostition(ctx, w_width, y, n_height, wInput) {
- const MARGIN = 10;
-
- const rect = ctx.canvas.getBoundingClientRect();
- const transform = new DOMMatrix()
- .scaleSelf(rect.width / ctx.canvas.width, rect.height / ctx.canvas.height)
- .multiplySelf(ctx.getTransform())
- .translateSelf(MARGIN, MARGIN);
- const scale = new DOMMatrix().scaleSelf(transform.a, transform.d);
-
- return {
- transformOrigin: "0 0",
- transform: scale,
- transform: transform,
- left: `${transform.a * w_width - 70 * scale.a + rect.left}px`,
- top: `${(wInput.last_y - 15) * scale.d + scale.f + rect.top}px`,
- maxWidth: `${w_width - MARGIN * 2}px`,
- maxHeight: `${n_height - MARGIN * 2}px`,
- zIndex: wInput?.inputEl?.style?.zIndex
- ? +wInput?.inputEl.style.zIndex + 1
- : 20,
- };
-}
-
-function getVoiceAndSettings() {
- const voices = speechSynthesis.getVoices();
-
- const { voice, volume, pitch, rate } =
- RecognationSpeechDialog.getSettingsRecSpeechLS();
- const voiceSelected = voices.filter((v) => v.name === voice);
-
- return {
- voice: voiceSelected.length ? voiceSelected[0] : null,
- volume,
- pitch,
- rate,
- };
-}
-
-// Function return utterance
-function speakSynthesisUtterance(text, options = {}) {
- const utterance = new SpeechSynthesisUtterance(text);
-
- Object.assign(utterance, { ...options, ...getVoiceAndSettings() });
- return utterance;
-}
-
-// Check premissions
-async function checkPremissions(
- device = { name: "microphone" },
- update = null
-) {
- return navigator.permissions
- .query(device)
- .then((result) => {
- const state = result.state;
- if (state == "granted") {
- return { device, state, status: true };
- } else if (state == "prompt") {
- return { device, state, status: false };
- } else if (state == "denied") {
- return { device, state, status: false };
- }
- result.onchange = update;
- })
- .catch((e) => ({ device, state: "error", status: false }));
-}
-
-// Check checkbox
-function checkboxLSCheckedByKey(lsKey, selector, defaultVal = false) {
- const currValue = JSON.parse(localStorage.getItem(lsKey, defaultVal));
-
- Array.from(document.querySelectorAll(selector)).forEach(
- (saveAs) => (saveAs.checked = currValue)
- );
-}
-
-// Set styles
-const setStylesAllElements = (selector, exclude = null, styles = {}) => {
- let elements = Array.from(document.querySelectorAll(selector));
- if (exclude) elements = elements.filter((r) => r !== exclude);
- elements = elements.map((r) => Object.assign(r.style, styles));
-};
-
-function SpeechWidget(node, inputName, inputData, widgetsText) {
- const widget = {
- type: "speak_and_recognation_type",
- name: inputName,
- value: inputData,
- size: [22, 1],
- options: { hideOnZoom: true },
- text_element: widgetsText?.inputEl,
- draw(ctx, node, widget_width, y, widget_height) {
- const hidden = widgetsText?.element?.hidden || widgetsText.hidden;
-
- widget.element.style.display =
- hidden || app.canvas.ds.scale < 0.6 ? "none" : "flex";
- if (hidden) {
- widget.options.onHide?.(widget);
- }
-
- if (hidden) {
- return;
- }
-
- Object.assign(
- widget.element.style,
- getPostition(ctx, widget_width, y, node.size[1], widgetsText)
- );
- },
- computeSize(...args) {
- return [22, 1];
- },
- async callback(v) {
- if (widgetsText?.element?.hasAttribute("readonly")) return;
-
- widget.value = v ?? inputData ?? [false, true];
-
- const checkboxSave = widget.element.querySelector(
- ".alekpet_extras_node_recognition_save"
- );
- const checkboxClear = widget.element.querySelector(
- ".alekpet_extras_node_recognition_clear"
- );
-
- if (checkboxClear) checkboxClear.checked = widget.value[1] ?? false;
- if (checkboxSave) checkboxSave.checked = widget.value[0] ?? false;
- },
- onRemove() {
- widget.element?.remove();
- },
- };
-
- const buttons = [];
-
- if (speechRect && !widgetsText?.element?.hasAttribute("readonly")) {
- buttons.push(
- $el("div.alekpet_extras_node_recognition_icon_box", [
- $el("span.alekpet_extras_node_recognition_aborted", {
- title: "Speech recognition abort",
- textContent: "✖",
- style: { display: "none" },
- onclick: async function (e) {
- const info = widget.element.querySelector(
- ".alekpet_extras_node_info span"
- );
-
- if (speechRect.isRecognition) {
- e.currentTarget.style.display = "none";
- speechRect.isRecognitionAbort = true;
- speechRect.abort();
- info.textContent = "aborted";
- this.classList.remove(
- "alekpet_extras_node_recognition_icon_active"
- );
- setTimeout(() => (info.textContent = ""), 2000);
- }
- },
- }),
- $el("span.alekpet_extras_node_recognition_icon", {
- title: "Speech recognition",
- onclick: async function (e) {
- const info = widget.element.querySelector(
- ".alekpet_extras_node_info span"
- );
- const abort = widget.element.querySelector(
- ".alekpet_extras_node_recognition_aborted"
- );
- const checkboxSave = widget.element.querySelector(
- ".alekpet_extras_node_recognition_save"
- );
-
- // Hide other recognitions buttons
- setStylesAllElements(
- ".alekpet_extras_node_recognition_icon",
- e.currentTarget,
- {
- display: "none",
- }
- );
-
- // Recognition
- if (speechRect.elements === null) {
- // Record audio
- if (checkboxSave.checked) {
- try {
- const stream = await navigator.mediaDevices.getUserMedia({
- audio: true,
- });
-
- mediaRecorder = new MediaRecorder(stream);
-
- mediaRecorder.ondataavailable = (event) => {
- audioChunks.push(event.data);
- };
-
- mediaRecorder.onstop = async () => {
- if (speechRect.isRecognitionAbort) {
- speechRect.lastText = "";
- speechRect.isRecognitionAbort = false;
- return;
- }
-
- const saveAsWindow = JSON.parse(
- localStorage.getItem(
- `${idExt}.SpeechAndRecognationSpeechSaveAs`,
- false
- )
- );
-
- // Filename
- let nameFile = "recording.webm";
- if (speechRect?.lastText?.length) {
- nameFile = `${speechRect.lastText
- .slice(0, maxLenAudioFileName)
- .replaceAll(regExpFileName, "_")}.webm`;
- }
-
- // Get audio
- const audioBlob = new Blob(audioChunks, {
- type: "audio/webm",
- });
-
- if (!saveAsWindow) {
- const body = new FormData();
- body.append("image", audioBlob, nameFile);
- body.append("overwrite", "true");
- const resp = await api.fetchApi("/upload/image", {
- method: "POST",
- body,
- });
-
- if (resp.status !== 200) {
- console.error(
- "[ExtrasNode] Recording audio not saved!"
- );
- return;
- }
-
- console.log(
- `[ExtrasNode] Recording audio "${nameFile}" saved successfully!`
- );
- } else {
- const audioUrl = URL.createObjectURL(audioBlob);
- const linkDown = document.createElement("a");
- linkDown.href = audioUrl;
- linkDown.download = nameFile;
- linkDown.click();
- }
-
- speechRect.lastText = "";
- };
- } catch (err) {
- comfyuiDesktopAlert(
- `Device "Microphone" - ${err.message}!\n\nCheck device or allow access!`
- );
- }
- }
- // end - Record audio
-
- speechRect.elements = [widget.element, widgetsText.inputEl];
- info.textContent = "recognition";
- this.classList.add("alekpet_extras_node_recognition_icon_active");
- speechRect.start();
- mediaRecorder && mediaRecorder.start();
- abort.style.display = "inline-block";
- } else {
- speechRect.stop();
- abort.style.display = "none";
- info.textContent = "stoped";
- this.classList.remove(
- "alekpet_extras_node_recognition_icon_active"
- );
- setTimeout(() => (info.textContent = ""), 2000);
- }
- },
- }),
- // Settings elements
- $el("div.alekpet_extras_node_speech_recognition_settings", [
- $el(
- "div.alekpet_extras_node_speech_recognition_settings_button.pi.pi-cog",
- {
- title: "Settings",
- onclick: (e) => {
- checkboxLSCheckedByKey(
- `${idExt}.SpeechAndRecognationSpeechSaveAs`,
- ".alekpet_extras_node_recognition_saveAs"
- );
- animateClick(e.currentTarget.nextElementSibling);
- },
- }
- ),
- createWindowModal({
- textTitle: null,
- stylesClose: {
- top: "-4px",
- right: "4px",
- width: "auto",
- height: "auto",
- padding: "2px",
- fontSize: "0.4rem",
- lineHeight: 1,
- },
- stylesBox: {
- background: "transparent",
- border: 0,
- padding: 0,
- boxShadow: "none",
- },
- stylesBody: {
- display: "flex",
- flexDirection: "column",
- alignItems: "stretch",
- gap: "3px",
- textWrap: "wrap",
- background: "rgb(131, 131, 131)",
- color: "white",
- padding: "2px",
- marginTop: "2px",
- borderRadius: "6px",
- fontSize: "0.4rem",
- minWidth: "85px",
- },
- stylesWrapper: {
- minWidth: "100px",
- transform: "translate(0%, 0%)",
- },
- textBody: [
- $el(
- "label.alekpet_extras_node_speech_recognition_settings_props_label",
- [
- $el(
- "span.alekpet_extras_node_speech_recognition_settings_props_name",
- {
- textContent: "Recoding audio",
- title: "Save in audio file after recognition",
- }
- ),
- $el(
- "input.alekpet_extras_node_speech_recognition_settings_props_input.alekpet_extras_node_speech_recognition_checkbox.alekpet_extras_node_recognition_save",
- {
- type: "checkbox",
- checked: widget.value[0] ?? false,
- onchange: (e) =>
- widget?.callback([!!e.target.checked, widget.value[1]]),
- }
- ),
- ]
- ),
- $el(
- "label.alekpet_extras_node_speech_recognition_settings_props_label",
- [
- $el(
- "span.alekpet_extras_node_speech_recognition_settings_props_name",
- {
- textContent: "Clear text",
- title: "Clear text after recognition",
- }
- ),
- $el(
- "input.alekpet_extras_node_speech_recognition_settings_props_input.alekpet_extras_node_speech_recognition_checkbox.alekpet_extras_node_recognition_clear",
- {
- type: "checkbox",
- checked: widget.value[1] ?? true,
- onchange: (e) =>
- widget?.callback([widget.value[0], !!e.target.checked]),
- }
- ),
- ]
- ),
- $el("hr", { style: { padding: 0, margin: 0 } }),
- $el(
- "label.alekpet_extras_node_speech_recognition_settings_props_label",
- [
- $el(
- "span.alekpet_extras_node_speech_recognition_settings_props_name",
- {
- textContent: "'Save as' window?",
- title: "Show modal window when saving recorded audio.",
- }
- ),
- $el(
- "input.alekpet_extras_node_speech_recognition_settings_props_input.alekpet_extras_node_speech_recognition_checkbox.alekpet_extras_node_recognition_saveAs",
- {
- type: "checkbox",
- checked: JSON.parse(
- localStorage.getItem(
- `${idExt}.SpeechAndRecognationSpeechSaveAs`,
- false
- )
- ),
- onchange: (e) => {
- const check = !!e.target.checked;
- const settCheck = document.body.querySelector(
- "[id$='.SpeechAndRecognationSpeech'] div input"
- );
- settCheck && (settCheck.checked = check);
-
- localStorage.setItem(
- `${idExt}.SpeechAndRecognationSpeechSaveAs`,
- check
- );
-
- checkboxLSCheckedByKey(
- `${idExt}.SpeechAndRecognationSpeechSaveAs`,
- ".alekpet_extras_node_recognition_saveAs"
- );
- },
- }
- ),
- ]
- ),
- ],
- }),
- ]),
- // end -- Settings elements
- ])
- );
- }
-
- if (SpeechSynthesis && speechSynthesis) {
- function buttonsStyles(
- speechesButtons,
- action = "add",
- className = "alekpet_extras_node_speech_icon_playing"
- ) {
- speechesButtons?.forEach((speechButton) =>
- speechButton?.classList[action](className)
- );
-
- const settingTestSpeech = document.querySelector(
- ".panel_settings_recognation_speech_voice_test"
- );
- if (settingTestSpeech) {
- if (action === "add") {
- settingTestSpeech.style.opacity = 0.7;
- settingTestSpeech.style.color = "var(--error-text)";
- settingTestSpeech.title = "Cancel test speech";
- settingTestSpeech.textContent = "Cancel test";
- } else {
- settingTestSpeech.style.opacity = 1;
- settingTestSpeech.title = "Run test speech";
- settingTestSpeech.textContent = "Test speech";
- settingTestSpeech.style.color = "limegreen";
- }
- }
- }
-
- buttons.push(
- $el("span.alekpet_extras_node_speech_icon", {
- onclick: function () {
- try {
- const info = widget.element.querySelector(
- ".alekpet_extras_node_info span"
- );
-
- const speechesButtons = Array.from(
- document.querySelectorAll(".alekpet_extras_node_speech_icon")
- );
-
- // Already playing
- if (speechSynthesis.speaking) {
- SpeechSynthesis.cancel();
-
- info.textContent = "canceled";
- this.title = "Speak text";
-
- buttonsStyles(speechesButtons, "remove");
- setTimeout(() => (info.textContent = ""), 2000);
- return;
- }
-
- // Start playing text
- const text = widgetsText?.element?.value;
- if (text.trim() !== "") {
- const utterance = speakSynthesisUtterance(text, {
- onend: (e) => {
- this.title = "Speak text";
- info.textContent = "";
-
- buttonsStyles(speechesButtons, "remove");
- },
- });
-
- if (utterance) {
- this.title = "Cancel speech";
- info.textContent = "saying now";
-
- buttonsStyles(speechesButtons);
- SpeechSynthesis.speak(utterance);
- }
- } else {
- const utterance = speakSynthesisUtterance(
- `${widgetsText?.name || "This"}}, field is empty!`,
- {
- onend: (e) => {
- this.title = "Speak text";
- info.textContent = "";
-
- buttonsStyles(speechesButtons, "remove");
- },
- }
- );
-
- if (utterance) {
- this.title = "Cancel speech";
- info.textContent = "empty text!";
- buttonsStyles(speechesButtons);
- SpeechSynthesis.speak(utterance);
- }
- }
- } catch (err) {
- console.log(err);
- }
- },
- textContent: "🔊",
- title: "Speak text",
- })
- );
- }
-
- widget.element = $el(
- "div.alekpet_extras_node_speechrecognition_box",
- { style: { display: "none" } },
- [
- $el("div.alekpet_extras_node_speechrecognition_row", [...buttons]),
- $el("div.alekpet_extras_node_info", [
- $el("span", {
- textContent: "",
- style: { fontSize: "0.4em" },
- }),
- ]),
- ]
- );
-
- const collapse = node.collapse;
- node.collapse = function () {
- collapse.apply(this, arguments);
- if (this.flags?.collapsed) {
- widget.element.hidden = true;
- widget.element.style.display = "none";
- }
- widget.element.dataset.collapsed = this.flags?.collapsed ? "true" : "false";
- };
-
- const onRemovedOrig = node.onRemoved;
- node.onRemoved = function () {
- node?.widgets?.forEach((w) => {
- if (w.type === "speak_and_recognation_type") {
- w?.onRemove();
- }
- });
- onRemovedOrig?.apply(node, arguments);
- };
-
- document.body.appendChild(widget.element);
-
- return widget;
-}
-/* ~~~ end - Speech & Recognition speech Widget ~~~ */
-
/* ~~~ Color Widget ~~~ */
function makeColorWidget(node, inputName, inputData, widget) {
const color_hex = $el("input", {
@@ -781,12 +106,4 @@ function createPreiviewSize(node, name, options) {
}
/* ~~~ end - Preview Image Size Widget ~~~ */
-export {
- SpeechWidget,
- makeColorWidget,
- createPreiviewSize,
- speechRect,
- SpeechSynthesis,
- speakSynthesisUtterance,
- checkboxLSCheckedByKey,
-};
+export { makeColorWidget, createPreiviewSize };
diff --git a/ExtrasNode/lib/extras_node_dialogs.js b/ExtrasNode/lib/extras_speech_and_recognation_dialog.js
similarity index 95%
rename from ExtrasNode/lib/extras_node_dialogs.js
rename to ExtrasNode/lib/extras_speech_and_recognation_dialog.js
index 32fd7a1..e092ed2 100644
--- a/ExtrasNode/lib/extras_node_dialogs.js
+++ b/ExtrasNode/lib/extras_speech_and_recognation_dialog.js
@@ -1,6 +1,12 @@
+/*
+ * Title: Extras extension - Speech And Recognition Dialog
+ * Author: AlekPet
+ * Github: https://github.com/AlekPet/ComfyUI_Custom_Nodes_AlekPet/tree/master/ExtrasNode
+ */
+
import { ComfyDialog } from "../../../../../scripts/ui.js";
import { makeElement } from "../../utils.js";
-import { speakSynthesisUtterance } from "./extras_node_widgets.js";
+import { speakSynthesisUtterance } from "./extras_speech_and_recognation_widget.js";
const idExt = "alekpet.ExtrasNode";
@@ -34,7 +40,7 @@ export class RecognationSpeechDialog extends ComfyDialog {
class: ["panel_settings_recognation_speech"],
style: {
display: "grid",
- gridTemplateColumns: "1fr 1fr 1fr",
+ gridTemplateColumns: "0.2fr 1fr 0.2fr",
alignItems: "center",
justifyContent: "center",
gap: "5px",
@@ -198,8 +204,9 @@ export class RecognationSpeechDialog extends ComfyDialog {
const box = makeElement("div", {
class: ["box_recognation_speech"],
style: {
- width: "25vw",
+ width: "80vw",
color: "var(--input-text)",
+ padding: "20px",
},
children: [await this.createPanelSettings()],
});
diff --git a/ExtrasNode/lib/extras_speech_and_recognation_widget.js b/ExtrasNode/lib/extras_speech_and_recognation_widget.js
new file mode 100644
index 0000000..da37e60
--- /dev/null
+++ b/ExtrasNode/lib/extras_speech_and_recognation_widget.js
@@ -0,0 +1,691 @@
+/*
+ * Title: Extras Speech And Recognation Widget
+ * Author: AlekPet
+ * Github: https://github.com/AlekPet/ComfyUI_Custom_Nodes_AlekPet/tree/master/ExtrasNode
+ */
+
+import { api } from "../../../../scripts/api.js";
+import { app } from "../../../../scripts/app.js";
+import { $el } from "../../../../scripts/ui.js";
+import {
+ createWindowModal,
+ animateClick,
+ comfyuiDesktopAlert,
+} from "../../utils.js";
+import { RecognationSpeechDialog } from "./extras_speech_and_recognation_dialog.js";
+
+const idExt = "alekpet.ExtrasNode";
+
+/* ~~~ Speech & Recognition speech Widget ~~~ */
+const spRecognition =
+ window.SpeechRecognition || window.webkitSpeechRecognition;
+const SpeechSynthesis = window.speechSynthesis;
+
+const regExpFileName = /[^\p{L}\d]+/gu;
+const maxLenAudioFileName = 50;
+
+let speechRect;
+let mediaRecorder = null;
+let audioChunks = [];
+
+if (spRecognition) {
+ speechRect = new spRecognition();
+ speechRect.elements = null;
+ speechRect.isRecognition = false;
+ speechRect.isRecognitionAbort = false;
+ speechRect.lastText = "";
+
+ speechRect.addEventListener("result", (event) => {
+ const results = event?.results[0][0]?.transcript;
+ if (results && speechRect?.elements?.length) {
+ const clear_text = speechRect.elements[0].querySelector(
+ ".alekpet_extras_node_recognition_clear"
+ );
+
+ if (clear_text.checked) {
+ speechRect.elements[1].value = results;
+ } else {
+ speechRect.elements[1].value += " " + results;
+ }
+ }
+ });
+
+ const resetSpeechRecognition = () => {
+ if (speechRect?.elements?.length) {
+ const info = speechRect.elements[0].querySelector(
+ ".alekpet_extras_node_info span"
+ );
+ const icon_rec = speechRect.elements[0].querySelector(
+ ".alekpet_extras_node_recognition_icon"
+ );
+
+ speechRect.elements[0].querySelector(
+ ".alekpet_extras_node_recognition_aborted"
+ ).style.display = "none";
+
+ icon_rec.classList.remove("alekpet_extras_node_recognition_icon_active");
+ setTimeout(() => (info.textContent = ""), 1000);
+ }
+
+ setStylesAllElements(".alekpet_extras_node_recognition_icon", null, {
+ display: "inline-block",
+ });
+
+ if (mediaRecorder) {
+ speechRect.lastText = speechRect?.elements[1].value;
+ mediaRecorder.stop();
+ } else {
+ speechRect.lastText = "";
+ speechRect.isRecognitionAbort = false;
+ }
+
+ mediaRecorder = null;
+ audioChunks = [];
+ speechRect.isRecognition = false;
+
+ speechRect.elements = null;
+ };
+
+ speechRect.addEventListener("audiostart", (e) => {
+ speechRect.isRecognition = true;
+ });
+
+ speechRect.addEventListener("end", () => {
+ resetSpeechRecognition();
+ speechRect.stop();
+ });
+
+ speechRect.addEventListener("speechend", () => {
+ resetSpeechRecognition();
+ speechRect.stop();
+ });
+
+ speechRect.addEventListener("error", (event) => {
+ if (speechRect?.elements?.length) {
+ const info = speechRect.elements[0].querySelector(
+ ".alekpet_extras_node_info span"
+ );
+ info.textContent = event.error;
+ }
+ resetSpeechRecognition();
+ console.log(">> Error recognition: " + event.error);
+ });
+} else {
+ console.warn(
+ "Your browser does not support: SpeechRecognition.\nIf Firefox: go to about:config > media.webspeech.recognition.enable and media.webspeech.recognition.force_enable > set true"
+ );
+}
+
+if (!SpeechSynthesis) {
+ console.warn("Your browser does not support: speechSynthesis");
+}
+
+// Get position speech SpeechRecognition widget
+function getPostition(ctx, w_width, y, n_height, wInput) {
+ const MARGIN = 10;
+
+ const rect = ctx.canvas.getBoundingClientRect();
+ const transform = new DOMMatrix()
+ .scaleSelf(rect.width / ctx.canvas.width, rect.height / ctx.canvas.height)
+ .multiplySelf(ctx.getTransform())
+ .translateSelf(MARGIN, MARGIN);
+ const scale = new DOMMatrix().scaleSelf(transform.a, transform.d);
+
+ return {
+ transformOrigin: "0 0",
+ transform: scale,
+ transform: transform,
+ left: `${transform.a * w_width - 70 * scale.a + rect.left}px`,
+ top: `${(wInput.last_y - 15) * scale.d + scale.f + rect.top}px`,
+ maxWidth: `${w_width - MARGIN * 2}px`,
+ maxHeight: `${n_height - MARGIN * 2}px`,
+ zIndex: wInput?.inputEl?.style?.zIndex
+ ? +wInput?.inputEl.style.zIndex + 1
+ : 20,
+ };
+}
+
+function getVoiceAndSettings() {
+ const voices = speechSynthesis.getVoices();
+
+ const { voice, volume, pitch, rate } =
+ RecognationSpeechDialog.getSettingsRecSpeechLS();
+ const voiceSelected = voices.filter((v) => v.name === voice);
+
+ return {
+ voice: voiceSelected.length ? voiceSelected[0] : null,
+ volume,
+ pitch,
+ rate,
+ };
+}
+
+// Function return utterance
+function speakSynthesisUtterance(text, options = {}) {
+ const utterance = new SpeechSynthesisUtterance(text);
+
+ Object.assign(utterance, { ...options, ...getVoiceAndSettings() });
+ return utterance;
+}
+
+// Check premissions
+async function checkPremissions(
+ device = { name: "microphone" },
+ update = null
+) {
+ return navigator.permissions
+ .query(device)
+ .then((result) => {
+ const state = result.state;
+ if (state == "granted") {
+ return { device, state, status: true };
+ } else if (state == "prompt") {
+ return { device, state, status: false };
+ } else if (state == "denied") {
+ return { device, state, status: false };
+ }
+ result.onchange = update;
+ })
+ .catch((e) => ({ device, state: "error", status: false }));
+}
+
+// Check checkbox
+function checkboxLSCheckedByKey(lsKey, selector, defaultVal = false) {
+ const currValue = JSON.parse(localStorage.getItem(lsKey, defaultVal));
+
+ Array.from(document.querySelectorAll(selector)).forEach(
+ (saveAs) => (saveAs.checked = currValue)
+ );
+}
+
+// Set styles
+const setStylesAllElements = (selector, exclude = null, styles = {}) => {
+ let elements = Array.from(document.querySelectorAll(selector));
+ if (exclude) elements = elements.filter((r) => r !== exclude);
+ elements = elements.map((r) => Object.assign(r.style, styles));
+};
+
+function SpeechWidget(node, inputName, inputData, widgetsText) {
+ const widget = {
+ type: "speak_and_recognation_type",
+ name: inputName,
+ value: inputData,
+ size: [22, 1],
+ options: { hideOnZoom: true },
+ text_element: widgetsText?.inputEl,
+ draw(ctx, node, widget_width, y, widget_height) {
+ const hidden = widgetsText?.element?.hidden || widgetsText.hidden;
+
+ widget.element.style.display =
+ hidden || app.canvas.ds.scale < 0.6 ? "none" : "flex";
+ if (hidden) {
+ widget.options.onHide?.(widget);
+ }
+
+ if (hidden) {
+ return;
+ }
+
+ Object.assign(
+ widget.element.style,
+ getPostition(ctx, widget_width, y, node.size[1], widgetsText)
+ );
+ },
+ computeSize(...args) {
+ return [22, 1];
+ },
+ async callback(v) {
+ if (widgetsText?.element?.hasAttribute("readonly")) return;
+
+ widget.value = v ?? inputData ?? [false, true];
+
+ const checkboxSave = widget.element.querySelector(
+ ".alekpet_extras_node_recognition_save"
+ );
+ const checkboxClear = widget.element.querySelector(
+ ".alekpet_extras_node_recognition_clear"
+ );
+
+ if (checkboxClear) checkboxClear.checked = widget.value[1] ?? false;
+ if (checkboxSave) checkboxSave.checked = widget.value[0] ?? false;
+ },
+ onRemove() {
+ widget.element?.remove();
+ },
+ };
+
+ const buttons = [];
+
+ if (speechRect && !widgetsText?.element?.hasAttribute("readonly")) {
+ buttons.push(
+ $el("div.alekpet_extras_node_recognition_icon_box", [
+ $el("span.alekpet_extras_node_recognition_aborted", {
+ title: "Speech recognition abort",
+ textContent: "✖",
+ style: { display: "none" },
+ onclick: async function (e) {
+ const info = widget.element.querySelector(
+ ".alekpet_extras_node_info span"
+ );
+
+ if (speechRect.isRecognition) {
+ e.currentTarget.style.display = "none";
+ speechRect.isRecognitionAbort = true;
+ speechRect.abort();
+ info.textContent = "aborted";
+ this.classList.remove(
+ "alekpet_extras_node_recognition_icon_active"
+ );
+ setTimeout(() => (info.textContent = ""), 2000);
+ }
+ },
+ }),
+ $el("span.alekpet_extras_node_recognition_icon", {
+ title: "Speech recognition",
+ onclick: async function (e) {
+ const info = widget.element.querySelector(
+ ".alekpet_extras_node_info span"
+ );
+ const abort = widget.element.querySelector(
+ ".alekpet_extras_node_recognition_aborted"
+ );
+ const checkboxSave = widget.element.querySelector(
+ ".alekpet_extras_node_recognition_save"
+ );
+
+ // Hide other recognitions buttons
+ setStylesAllElements(
+ ".alekpet_extras_node_recognition_icon",
+ e.currentTarget,
+ {
+ display: "none",
+ }
+ );
+
+ // Recognition
+ if (speechRect.elements === null) {
+ // Record audio
+ if (checkboxSave.checked) {
+ try {
+ const stream = await navigator.mediaDevices.getUserMedia({
+ audio: true,
+ });
+
+ mediaRecorder = new MediaRecorder(stream);
+
+ mediaRecorder.ondataavailable = (event) => {
+ audioChunks.push(event.data);
+ };
+
+ mediaRecorder.onstop = async () => {
+ if (speechRect.isRecognitionAbort) {
+ speechRect.lastText = "";
+ speechRect.isRecognitionAbort = false;
+ return;
+ }
+
+ const saveAsWindow = JSON.parse(
+ localStorage.getItem(
+ `${idExt}.SpeechAndRecognationSpeechSaveAs`,
+ false
+ )
+ );
+
+ // Filename
+ let nameFile = "recording.webm";
+ if (speechRect?.lastText?.length) {
+ nameFile = `${speechRect.lastText
+ .slice(0, maxLenAudioFileName)
+ .replaceAll(regExpFileName, "_")}.webm`;
+ }
+
+ // Get audio
+ const audioBlob = new Blob(audioChunks, {
+ type: "audio/webm",
+ });
+
+ if (!saveAsWindow) {
+ const body = new FormData();
+ body.append("image", audioBlob, nameFile);
+ body.append("overwrite", "true");
+ const resp = await api.fetchApi("/upload/image", {
+ method: "POST",
+ body,
+ });
+
+ if (resp.status !== 200) {
+ console.error(
+ "[ExtrasNode] Recording audio not saved!"
+ );
+ return;
+ }
+
+ console.log(
+ `[ExtrasNode] Recording audio "${nameFile}" saved successfully!`
+ );
+ } else {
+ const audioUrl = URL.createObjectURL(audioBlob);
+ const linkDown = document.createElement("a");
+ linkDown.href = audioUrl;
+ linkDown.download = nameFile;
+ linkDown.click();
+ }
+
+ speechRect.lastText = "";
+ };
+ } catch (err) {
+ comfyuiDesktopAlert(
+ `Device "Microphone" - ${err.message}!\n\nCheck device or allow access!`
+ );
+ }
+ }
+ // end - Record audio
+
+ speechRect.elements = [widget.element, widgetsText.inputEl];
+ info.textContent = "recognition";
+ this.classList.add("alekpet_extras_node_recognition_icon_active");
+ speechRect.start();
+ mediaRecorder && mediaRecorder.start();
+ abort.style.display = "inline-block";
+ } else {
+ speechRect.stop();
+ abort.style.display = "none";
+ info.textContent = "stoped";
+ this.classList.remove(
+ "alekpet_extras_node_recognition_icon_active"
+ );
+ setTimeout(() => (info.textContent = ""), 2000);
+ }
+ },
+ }),
+ // Settings elements
+ $el("div.alekpet_extras_node_speech_recognition_settings", [
+ $el(
+ "div.alekpet_extras_node_speech_recognition_settings_button.pi.pi-cog",
+ {
+ title: "Settings",
+ onclick: (e) => {
+ checkboxLSCheckedByKey(
+ `${idExt}.SpeechAndRecognationSpeechSaveAs`,
+ ".alekpet_extras_node_recognition_saveAs"
+ );
+ animateClick(e.currentTarget.nextElementSibling);
+ },
+ }
+ ),
+ createWindowModal({
+ textTitle: null,
+ stylesClose: {
+ top: "-4px",
+ right: "4px",
+ width: "auto",
+ height: "auto",
+ padding: "2px",
+ fontSize: "0.4rem",
+ lineHeight: 1,
+ },
+ stylesBox: {
+ background: "transparent",
+ border: 0,
+ padding: 0,
+ boxShadow: "none",
+ },
+ stylesBody: {
+ display: "flex",
+ flexDirection: "column",
+ alignItems: "stretch",
+ gap: "3px",
+ textWrap: "wrap",
+ background: "rgb(131, 131, 131)",
+ color: "white",
+ padding: "2px",
+ marginTop: "2px",
+ borderRadius: "6px",
+ fontSize: "0.4rem",
+ minWidth: "85px",
+ },
+ stylesWrapper: {
+ minWidth: "100px",
+ transform: "translate(0%, 0%)",
+ },
+ textBody: [
+ $el(
+ "label.alekpet_extras_node_speech_recognition_settings_props_label",
+ [
+ $el(
+ "span.alekpet_extras_node_speech_recognition_settings_props_name",
+ {
+ textContent: "Recoding audio",
+ title: "Save in audio file after recognition",
+ }
+ ),
+ $el(
+ "input.alekpet_extras_node_speech_recognition_settings_props_input.alekpet_extras_node_speech_recognition_checkbox.alekpet_extras_node_recognition_save",
+ {
+ type: "checkbox",
+ checked: widget.value[0] ?? false,
+ onchange: (e) =>
+ widget?.callback([!!e.target.checked, widget.value[1]]),
+ }
+ ),
+ ]
+ ),
+ $el(
+ "label.alekpet_extras_node_speech_recognition_settings_props_label",
+ [
+ $el(
+ "span.alekpet_extras_node_speech_recognition_settings_props_name",
+ {
+ textContent: "Clear text",
+ title: "Clear text after recognition",
+ }
+ ),
+ $el(
+ "input.alekpet_extras_node_speech_recognition_settings_props_input.alekpet_extras_node_speech_recognition_checkbox.alekpet_extras_node_recognition_clear",
+ {
+ type: "checkbox",
+ checked: widget.value[1] ?? true,
+ onchange: (e) =>
+ widget?.callback([widget.value[0], !!e.target.checked]),
+ }
+ ),
+ ]
+ ),
+ $el("hr", { style: { padding: 0, margin: 0 } }),
+ $el(
+ "label.alekpet_extras_node_speech_recognition_settings_props_label",
+ [
+ $el(
+ "span.alekpet_extras_node_speech_recognition_settings_props_name",
+ {
+ textContent: "'Save as' window?",
+ title: "Show modal window when saving recorded audio.",
+ }
+ ),
+ $el(
+ "input.alekpet_extras_node_speech_recognition_settings_props_input.alekpet_extras_node_speech_recognition_checkbox.alekpet_extras_node_recognition_saveAs",
+ {
+ type: "checkbox",
+ checked: JSON.parse(
+ localStorage.getItem(
+ `${idExt}.SpeechAndRecognationSpeechSaveAs`,
+ false
+ )
+ ),
+ onchange: (e) => {
+ const check = !!e.target.checked;
+ const settCheck = document.body.querySelector(
+ "[id$='.SpeechAndRecognationSpeech'] div input"
+ );
+ settCheck && (settCheck.checked = check);
+
+ localStorage.setItem(
+ `${idExt}.SpeechAndRecognationSpeechSaveAs`,
+ check
+ );
+
+ checkboxLSCheckedByKey(
+ `${idExt}.SpeechAndRecognationSpeechSaveAs`,
+ ".alekpet_extras_node_recognition_saveAs"
+ );
+ },
+ }
+ ),
+ ]
+ ),
+ ],
+ }),
+ ]),
+ // end -- Settings elements
+ ])
+ );
+ }
+
+ if (SpeechSynthesis && speechSynthesis) {
+ function buttonsStyles(
+ speechesButtons,
+ action = "add",
+ className = "alekpet_extras_node_speech_icon_playing"
+ ) {
+ speechesButtons?.forEach((speechButton) =>
+ speechButton?.classList[action](className)
+ );
+
+ const settingTestSpeech = document.querySelector(
+ ".panel_settings_recognation_speech_voice_test"
+ );
+ if (settingTestSpeech) {
+ if (action === "add") {
+ settingTestSpeech.style.opacity = 0.7;
+ settingTestSpeech.style.color = "var(--error-text)";
+ settingTestSpeech.title = "Cancel test speech";
+ settingTestSpeech.textContent = "Cancel test";
+ } else {
+ settingTestSpeech.style.opacity = 1;
+ settingTestSpeech.title = "Run test speech";
+ settingTestSpeech.textContent = "Test speech";
+ settingTestSpeech.style.color = "limegreen";
+ }
+ }
+ }
+
+ buttons.push(
+ $el("span.alekpet_extras_node_speech_icon", {
+ onclick: function () {
+ try {
+ const info = widget.element.querySelector(
+ ".alekpet_extras_node_info span"
+ );
+
+ const speechesButtons = Array.from(
+ document.querySelectorAll(".alekpet_extras_node_speech_icon")
+ );
+
+ // Already playing
+ if (speechSynthesis.speaking) {
+ SpeechSynthesis.cancel();
+
+ info.textContent = "canceled";
+ this.title = "Speak text";
+
+ buttonsStyles(speechesButtons, "remove");
+ setTimeout(() => (info.textContent = ""), 2000);
+ return;
+ }
+
+ // Start playing text
+ const text = widgetsText?.element?.value;
+ if (text.trim() !== "") {
+ const utterance = speakSynthesisUtterance(text, {
+ onend: (e) => {
+ this.title = "Speak text";
+ info.textContent = "";
+
+ buttonsStyles(speechesButtons, "remove");
+ },
+ });
+
+ if (utterance) {
+ this.title = "Cancel speech";
+ info.textContent = "saying now";
+
+ buttonsStyles(speechesButtons);
+ SpeechSynthesis.speak(utterance);
+ }
+ } else {
+ const utterance = speakSynthesisUtterance(
+ `${widgetsText?.name || "This"}}, field is empty!`,
+ {
+ onend: (e) => {
+ this.title = "Speak text";
+ info.textContent = "";
+
+ buttonsStyles(speechesButtons, "remove");
+ },
+ }
+ );
+
+ if (utterance) {
+ this.title = "Cancel speech";
+ info.textContent = "empty text!";
+ buttonsStyles(speechesButtons);
+ SpeechSynthesis.speak(utterance);
+ }
+ }
+ } catch (err) {
+ console.log(err);
+ }
+ },
+ textContent: "🔊",
+ title: "Speak text",
+ })
+ );
+ }
+
+ widget.element = $el(
+ "div.alekpet_extras_node_speechrecognition_box",
+ { style: { display: "none" } },
+ [
+ $el("div.alekpet_extras_node_speechrecognition_row", [...buttons]),
+ $el("div.alekpet_extras_node_info", [
+ $el("span", {
+ textContent: "",
+ style: { fontSize: "0.4em" },
+ }),
+ ]),
+ ]
+ );
+
+ const collapse = node.collapse;
+ node.collapse = function () {
+ collapse.apply(this, arguments);
+ if (this.flags?.collapsed) {
+ widget.element.hidden = true;
+ widget.element.style.display = "none";
+ }
+ widget.element.dataset.collapsed = this.flags?.collapsed ? "true" : "false";
+ };
+
+ const onRemovedOrig = node.onRemoved;
+ node.onRemoved = function () {
+ node?.widgets?.forEach((w) => {
+ if (w.type === "speak_and_recognation_type") {
+ w?.onRemove();
+ }
+ });
+ onRemovedOrig?.apply(node, arguments);
+ };
+
+ document.body.appendChild(widget.element);
+
+ return widget;
+}
+/* ~~~ end - Speech & Recognition speech Widget ~~~ */
+
+export {
+ SpeechWidget,
+ speechRect,
+ SpeechSynthesis,
+ speakSynthesisUtterance,
+ checkboxLSCheckedByKey,
+};
diff --git a/README.md b/README.md
index f071832..2de242d 100644
--- a/README.md
+++ b/README.md
@@ -8,26 +8,24 @@ If you enjoy my work, consider **[supporting me](https://alekpet.github.io/suppo
# List Nodes:
-| Name | Description | ComfyUI category |
-| :------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------: | :-----------------------: |
-| [**PoseNode**](https://github.com/AlekPet/ComfyUI_Custom_Nodes_AlekPet/tree/master/PoseNode) | The node set pose ControlNet | AlekPet Node/image |
-| [**PainterNode**](https://github.com/AlekPet/ComfyUI_Custom_Nodes_AlekPet/tree/master/PainterNode) | The node set sketch, scrumble image ControlNet and other nodes | AlekPet Node/image |
-| [**GoogleTranslateTextNode**](https://github.com/AlekPet/ComfyUI_Custom_Nodes_AlekPet/tree/master/GoogleTranslateNode) | The node translate promt uses module **googletrans** from other languages into english and return string | AlekPet Node/conditioning |
-| [**GoogleTranslateCLIPTextEncodeNode**](https://github.com/AlekPet/ComfyUI_Custom_Nodes_AlekPet/tree/master/GoogleTranslateNode) | The node translate promt uses module **googletrans** from other languages into english, and return conditioning | AlekPet Node/text |
-| [**DeepTranslatorTextNode**](https://github.com/AlekPet/ComfyUI_Custom_Nodes_AlekPet/tree/master/DeepTranslatorNode) | The node translate promt uses module **Deep Translator** from other languages into english and return string | AlekPet Node/text |
-| [**DeepTranslatorCLIPTextEncodeNode**](https://github.com/AlekPet/ComfyUI_Custom_Nodes_AlekPet/tree/master/DeepTranslatorNode) | The node translate promt uses module **Deep Translator** from other languages into english, and return conditioning | AlekPet Node/conditioning |
-| [**DeepLXTranslateTextNode**](https://github.com/AlekPet/ComfyUI_Custom_Nodes_AlekPet/tree/master/DeepLXTranslateNode) | The node translate promt uses **DeepLX** server written for Golang(Go) from other languages into english and return string | AlekPet Node/text |
-| [**DeepLXTranslateCLIPTextEncodeNode**](https://github.com/AlekPet/ComfyUI_Custom_Nodes_AlekPet/tree/master/DeepLXTranslateNode) | The node translate promt uses **DeepLX** server written for Golang(Go) from other languages into english, and return conditioning | AlekPet Node/conditioning |
-| [**ArgosTranslateTextNode**](https://github.com/AlekPet/ComfyUI_Custom_Nodes_AlekPet/tree/master/ArgosTranslateNode) | The node translate promt uses module **Argos Translator** from other languages into english and return string | AlekPet Node/text |
-| [**ArgosTranslateCLIPTextEncodeNode**](https://github.com/AlekPet/ComfyUI_Custom_Nodes_AlekPet/tree/master/ArgosTranslateNode) | The node translate promt uses module **Argos Translator** from other languages into english, and return conditioning | AlekPet Node/conditioning |
-| [**ChatGLM4TranslateTextNode**](https://github.com/AlekPet/ComfyUI_Custom_Nodes_AlekPet/tree/master/ChatGLMNode) | This translator node uses artificial intelligence to translate prompts and return string | AlekPet Node/text |
-| [**ChatGLM4TranslateCLIPTextEncodeNode**](https://github.com/AlekPet/ComfyUI_Custom_Nodes_AlekPet/tree/master/ChatGLMNode) | This translator node uses artificial intelligence to translate prompts and return conditioning | AlekPet Node/conditioning |
-| [**ChatGLM4InstructNode**](https://github.com/AlekPet/ComfyUI_Custom_Nodes_AlekPet/tree/master/ChatGLMNode) | This node uses artificial intelligence to generate prompt | AlekPet Node/Instruct |
-| [**ChatGLM4InstructMediaNode**](https://github.com/AlekPet/ComfyUI_Custom_Nodes_AlekPet/tree/master/ChatGLMNode) | This node uses artificial intelligence to describe what is shown in the media | AlekPet Node/Instruct |
-| [**PreviewTextNode**](https://github.com/AlekPet/ComfyUI_Custom_Nodes_AlekPet/tree/master/ExtrasNode) | The node displays the input text | AlekPet Node/extras |
-| [**ColorsCorrectNode**](https://github.com/AlekPet/ComfyUI_Custom_Nodes_AlekPet/tree/master/ExtrasNode) | The node for correcting image colors | AlekPet Node/extras |
-| [**HexToHueNode**](https://github.com/AlekPet/ComfyUI_Custom_Nodes_AlekPet/tree/master/ExtrasNode) | The node convert HEX color to HUE (degrees and normal [-0.5, 0.5]) | AlekPet Node/extras |
-| [**IDENode**](https://github.com/AlekPet/ComfyUI_Custom_Nodes_AlekPet/tree/master/IDENode) | The node that allows you to run code written in **Python** or **Javascript** directly in the node | AlekPet Node/experiments |
+| Name | Description | ComfyUI category |
+| :------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------: | :-----------------------: |
+| [**PoseNode**](https://github.com/AlekPet/ComfyUI_Custom_Nodes_AlekPet/tree/master/PoseNode) | The node set pose ControlNet | AlekPet Node/image |
+| [**PainterNode**](https://github.com/AlekPet/ComfyUI_Custom_Nodes_AlekPet/tree/master/PainterNode) | The node set sketch, scrumble image ControlNet and other nodes | AlekPet Node/image |
+| [**GoogleTranslateTextNode**](https://github.com/AlekPet/ComfyUI_Custom_Nodes_AlekPet/tree/master/GoogleTranslateNode) | The node translate promt uses module **googletrans** from other languages into english and return string | AlekPet Node/conditioning |
+| [**GoogleTranslateCLIPTextEncodeNode**](https://github.com/AlekPet/ComfyUI_Custom_Nodes_AlekPet/tree/master/GoogleTranslateNode) | The node translate promt uses module **googletrans** from other languages into english, and return conditioning | AlekPet Node/text |
+| [**DeepTranslatorTextNode**](https://github.com/AlekPet/ComfyUI_Custom_Nodes_AlekPet/tree/master/DeepTranslatorNode) | The node translate promt uses module **Deep Translator** from other languages into english and return string | AlekPet Node/text |
+| [**DeepTranslatorCLIPTextEncodeNode**](https://github.com/AlekPet/ComfyUI_Custom_Nodes_AlekPet/tree/master/DeepTranslatorNode) | The node translate promt uses module **Deep Translator** from other languages into english, and return conditioning | AlekPet Node/conditioning |
+| [**ArgosTranslateTextNode**](https://github.com/AlekPet/ComfyUI_Custom_Nodes_AlekPet/tree/master/ArgosTranslateNode) | The node translate promt uses module **Argos Translator** from other languages into english and return string | AlekPet Node/text |
+| [**ArgosTranslateCLIPTextEncodeNode**](https://github.com/AlekPet/ComfyUI_Custom_Nodes_AlekPet/tree/master/ArgosTranslateNode) | The node translate promt uses module **Argos Translator** from other languages into english, and return conditioning | AlekPet Node/conditioning |
+| [**ChatGLM4TranslateTextNode**](https://github.com/AlekPet/ComfyUI_Custom_Nodes_AlekPet/tree/master/ChatGLMNode) | This translator node uses artificial intelligence to translate prompts and return string | AlekPet Node/text |
+| [**ChatGLM4TranslateCLIPTextEncodeNode**](https://github.com/AlekPet/ComfyUI_Custom_Nodes_AlekPet/tree/master/ChatGLMNode) | This translator node uses artificial intelligence to translate prompts and return conditioning | AlekPet Node/conditioning |
+| [**ChatGLM4InstructNode**](https://github.com/AlekPet/ComfyUI_Custom_Nodes_AlekPet/tree/master/ChatGLMNode) | This node uses artificial intelligence to generate prompt | AlekPet Node/Instruct |
+| [**ChatGLM4InstructMediaNode**](https://github.com/AlekPet/ComfyUI_Custom_Nodes_AlekPet/tree/master/ChatGLMNode) | This node uses artificial intelligence to describe what is shown in the media | AlekPet Node/Instruct |
+| [**PreviewTextNode**](https://github.com/AlekPet/ComfyUI_Custom_Nodes_AlekPet/tree/master/ExtrasNode) | The node displays the input text | AlekPet Node/extras |
+| [**ColorsCorrectNode**](https://github.com/AlekPet/ComfyUI_Custom_Nodes_AlekPet/tree/master/ExtrasNode) | The node for correcting image colors | AlekPet Node/extras |
+| [**HexToHueNode**](https://github.com/AlekPet/ComfyUI_Custom_Nodes_AlekPet/tree/master/ExtrasNode) | The node convert HEX color to HUE (degrees and normal [-0.5, 0.5]) | AlekPet Node/extras |
+| [**IDENode**](https://github.com/AlekPet/ComfyUI_Custom_Nodes_AlekPet/tree/master/IDENode) | The node that allows you to run code written in **Python** or **Javascript** directly in the node | AlekPet Node/experiments |
# Installing
diff --git a/__init__.py b/__init__.py
index 246cc50..b616864 100644
--- a/__init__.py
+++ b/__init__.py
@@ -1,6 +1,6 @@
# Title: ComfyUI Install Customs Nodes and javascript files
# Author: AlekPet
-# Version: 2024.08.08
+# Version: 2025.06.26
import os
import importlib.util
import subprocess
@@ -32,7 +32,8 @@ extension_dirs = [
DEBUG = False
NODE_CLASS_MAPPINGS = dict() # dynamic class nodes append in mappings
-NODE_DISPLAY_NAME_MAPPINGS = dict() # dynamic display names nodes append mappings names
+# dynamic display names nodes append mappings names
+NODE_DISPLAY_NAME_MAPPINGS = dict()
humanReadableTextReg = re.compile("(?<=[a-z0-9])([A-Z])|(?<=[A-Z0-9])([A-Z][a-z]+)")
module_name_cut_version = re.compile("[>=<]")
@@ -47,9 +48,7 @@ def get_version_extension():
if os.path.isfile(toml_file):
try:
with open(toml_file, "r") as v:
- version = list(
- filter(lambda l: l.startswith("version"), v.readlines())
- )[0]
+ version = list(filter(lambda l: l.startswith("version"), v.readlines()))[0]
version = version.split("=")[1].replace('"', "").strip()
return f" \033[1;34mv{version}\033[0m\033[1;35m"
except Exception as e:
@@ -69,7 +68,7 @@ def information(datas):
try:
print(info, end="\r", flush=True)
except UnicodeError:
- safe_text = info.encode('utf-8', errors='replace').decode('utf-8', errors='replace')
+ safe_text = info.encode("utf-8", errors="replace").decode("utf-8", errors="replace")
print(safe_text, end="\r", flush=True)
@@ -80,11 +79,7 @@ def printColorInfo(text, color="\033[92m"):
def get_classes(code):
tree = ast.parse(code)
- return [
- n.name
- for n in ast.walk(tree)
- if isinstance(n, ast.ClassDef) and "Node" in n.name
- ]
+ return [n.name for n in ast.walk(tree) if isinstance(n, ast.ClassDef) and "Node" in n.name]
def addComfyUINodesToMapping(nodeElement):
@@ -102,9 +97,7 @@ def addComfyUINodesToMapping(nodeElement):
# remove extensions .py
module_without_py = f.replace(ext[1], "")
# Import module
- spec = importlib.util.spec_from_file_location(
- module_without_py, os.path.join(node_folder, f)
- )
+ spec = importlib.util.spec_from_file_location(module_without_py, os.path.join(node_folder, f))
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
classes_names = list(
@@ -115,22 +108,11 @@ def addComfyUINodesToMapping(nodeElement):
)
for class_module_name in classes_names:
# Check module
- if (
- class_module_name
- and class_module_name not in NODE_CLASS_MAPPINGS.keys()
- ):
- log(
- f" [*] Class node found '{class_module_name}' add to NODE_CLASS_MAPPINGS..."
- )
- NODE_CLASS_MAPPINGS.update(
- {class_module_name: getattr(module, class_module_name)}
- )
+ if class_module_name and class_module_name not in NODE_CLASS_MAPPINGS.keys():
+ log(f" [*] Class node found '{class_module_name}' add to NODE_CLASS_MAPPINGS...")
+ NODE_CLASS_MAPPINGS.update({class_module_name: getattr(module, class_module_name)})
NODE_DISPLAY_NAME_MAPPINGS.update(
- {
- class_module_name: humanReadableTextReg.sub(
- " \\1\\2", class_module_name
- )
- }
+ {class_module_name: humanReadableTextReg.sub(" \\1\\2", class_module_name)}
)
@@ -168,8 +150,8 @@ def module_install(commands, cwd="."):
stderr=subprocess.PIPE,
text=True,
bufsize=1,
- encoding='utf-8',
- errors='replace'
+ encoding="utf-8",
+ errors="replace",
)
out = threading.Thread(target=information, args=(result.stdout,))
err = threading.Thread(target=information, args=(result.stderr,))
@@ -197,17 +179,13 @@ def checkModules(nodeElement):
log(" -> File 'requirements.txt' found!")
with open(file_requir) as f:
required_modules = {
- module_name_cut_version.split(line.strip())[0]
- for line in f
- if not line.startswith("#")
+ module_name_cut_version.split(line.strip())[0] for line in f if not line.startswith("#")
}
modules_to_install = required_modules - installed_modules
if modules_to_install:
- module_install(
- [sys.executable, "-m", "pip", "install", *modules_to_install]
- )
+ module_install([sys.executable, "-m", "pip", "install", *modules_to_install])
nodes_list_dict = {}
@@ -318,7 +296,8 @@ try:
)
except Exception as e:
- nodes_list_dict["ArgosTranslateNode"]["error"] = e
+ if "ArgosTranslateNode" in nodes_list_dict:
+ nodes_list_dict["ArgosTranslateNode"]["error"] = e
# DeepTranslatorNode
@@ -342,31 +321,8 @@ try:
)
except Exception as e:
- nodes_list_dict["DeepTranslatorNode"]["error"] = e
-
-
-# DeepLXTranslateNode
-try:
- from .DeepLXTranslateNode.deeplx_translate_node import (
- DeepLXTranslateCLIPTextEncodeNode,
- DeepLXTranslateTextNode,
- )
-
- NODE_CLASS_MAPPINGS.update(
- {
- "DeepLXTranslateCLIPTextEncodeNode": DeepLXTranslateCLIPTextEncodeNode,
- "DeepLXTranslateTextNode": DeepLXTranslateTextNode,
- }
- )
- NODE_DISPLAY_NAME_MAPPINGS.update(
- {
- "DeepLXTranslateCLIPTextEncodeNode": "DeepLX Translate CLIP Text Encode Node",
- "DeepLXTranslateTextNode": "DeepLX Translate Text Node",
- }
- )
-
-except Exception as e:
- nodes_list_dict["DeepLXTranslateNode"]["error"] = e
+ if "DeepTranslatorNode" in nodes_list_dict:
+ nodes_list_dict["DeepTranslatorNode"]["error"] = e
# GoogleTranslateNode
@@ -390,83 +346,157 @@ try:
)
except Exception as e:
- nodes_list_dict["GoogleTranslateNode"]["error"] = e
+ if "GoogleTranslateNode" in nodes_list_dict:
+ nodes_list_dict["GoogleTranslateNode"]["error"] = e
+
# ChatGLMNode
-from .ChatGLMNode.chatglm_node import (
- ChatGLM4TranslateCLIPTextEncodeNode,
- ChatGLM4TranslateTextNode,
- ChatGLM4InstructNode,
- ChatGLM4InstructMediaNode,
-)
+try:
+ from .ChatGLMNode.chatglm_node import (
+ ChatGLM4TranslateCLIPTextEncodeNode,
+ ChatGLM4TranslateTextNode,
+ ChatGLM4InstructNode,
+ ChatGLM4InstructMediaNode,
+ )
+
+ NODE_CLASS_MAPPINGS.update(
+ {
+ "ChatGLM4TranslateCLIPTextEncodeNode": ChatGLM4TranslateCLIPTextEncodeNode,
+ "ChatGLM4TranslateTextNode": ChatGLM4TranslateTextNode,
+ "ChatGLM4InstructNode": ChatGLM4InstructNode,
+ "ChatGLM4InstructMediaNode": ChatGLM4InstructMediaNode,
+ }
+ )
+
+ NODE_DISPLAY_NAME_MAPPINGS.update(
+ {
+ "ChatGLM4TranslateCLIPTextEncodeNode": "ChatGLM-4 Translate CLIP Text Encode Node",
+ "ChatGLM4TranslateTextNode": "ChatGLM-4 Translate Text Node",
+ "ChatGLM4InstructNode": "ChatGLM-4 Instruct Node",
+ "ChatGLM4InstructMediaNode": "ChatGLM-4 Instruct Media Node",
+ }
+ )
+
+except Exception as e:
+ if "ChatGLMNode" in nodes_list_dict:
+ nodes_list_dict["ChatGLMNode"]["error"] = e
+
# ExtrasNode
-from .ExtrasNode.extras_node import PreviewTextNode, HexToHueNode, ColorsCorrectNode
+try:
+ from .ExtrasNode.extras_node import PreviewTextNode, HexToHueNode, ColorsCorrectNode
+
+ NODE_CLASS_MAPPINGS.update(
+ {
+ "PreviewTextNode": PreviewTextNode,
+ "HexToHueNode": HexToHueNode,
+ "ColorsCorrectNode": ColorsCorrectNode,
+ }
+ )
+ NODE_DISPLAY_NAME_MAPPINGS.update(
+ {
+ "PreviewTextNode": "Preview Text Node",
+ "HexToHueNode": "HEX to HUE Node",
+ "ColorsCorrectNode": "Colors Correct Node",
+ }
+ )
+except Exception as e:
+ if "ExtrasNode" in nodes_list_dict:
+ nodes_list_dict["ExtrasNode"]["error"] = e
+
# PainterNode
-from .PainterNode.painter_node import PainterNode
+try:
+ from .PainterNode.painter_node import PainterNode
+
+ NODE_CLASS_MAPPINGS.update(
+ {
+ "PainterNode": PainterNode,
+ }
+ )
+ NODE_DISPLAY_NAME_MAPPINGS.update(
+ {
+ "PainterNode": "Painter Node",
+ }
+ )
+
+except Exception as e:
+ if "PainterNode" in nodes_list_dict:
+ nodes_list_dict["PainterNode"]["error"] = e
+
# PoseNode
-from .PoseNode.pose_node import PoseNode
+try:
+ from .PoseNode.pose_node import PoseNode
+
+ NODE_CLASS_MAPPINGS.update(
+ {
+ "PoseNode": PoseNode,
+ }
+ )
+
+ NODE_DISPLAY_NAME_MAPPINGS.update(
+ {
+ "PoseNode": "Pose Node",
+ }
+ )
+
+except Exception as e:
+ if "PoseNode" in nodes_list_dict:
+ nodes_list_dict["PoseNode"]["error"] = e
+
# IDENode
-from .IDENode.ide_node import IDENode
+try:
+ from .IDENode.ide_node import IDENode
+ NODE_CLASS_MAPPINGS.update(
+ {
+ "IDENode": IDENode,
+ }
+ )
-NODE_CLASS_MAPPINGS.update(
- {
- "ChatGLM4TranslateCLIPTextEncodeNode": ChatGLM4TranslateCLIPTextEncodeNode,
- "ChatGLM4TranslateTextNode": ChatGLM4TranslateTextNode,
- "ChatGLM4InstructNode": ChatGLM4InstructNode,
- "ChatGLM4InstructMediaNode": ChatGLM4InstructMediaNode,
- "PreviewTextNode": PreviewTextNode,
- "HexToHueNode": HexToHueNode,
- "ColorsCorrectNode": ColorsCorrectNode,
- "PainterNode": PainterNode,
- "PoseNode": PoseNode,
- "IDENode": IDENode,
- }
-)
+ NODE_DISPLAY_NAME_MAPPINGS.update(
+ {
+ "IDENode": "IDE Node",
+ }
+ )
-
-NODE_DISPLAY_NAME_MAPPINGS.update(
- {
- "ChatGLM4TranslateCLIPTextEncodeNode": "ChatGLM-4 Translate CLIP Text Encode Node",
- "ChatGLM4TranslateTextNode": "ChatGLM-4 Translate Text Node",
- "ChatGLM4InstructNode": "ChatGLM-4 Instruct Node",
- "ChatGLM4InstructMediaNode": "ChatGLM-4 Instruct Media Node",
- "PreviewTextNode": "Preview Text Node",
- "HexToHueNode": "HEX to HUE Node",
- "ColorsCorrectNode": "Colors Correct Node",
- "PainterNode": "Painter Node",
- "PoseNode": "Pose Node",
- "IDENode": "IDE Node",
- }
-)
+except Exception as e:
+ if "IDENode" in nodes_list_dict:
+ nodes_list_dict["IDENode"]["error"] = e
# Information
-printColorInfo(
- f"\n### [START] ComfyUI AlekPet Nodes{get_version_extension()} ###", "\033[1;35m"
-)
+printColorInfo(f"\n### [START] ComfyUI AlekPet Nodes{get_version_extension()} ###", "\033[1;35m")
failed_nodes_text = ""
len_nodes = len(nodes_list_dict)
for key, node in enumerate(nodes_list_dict):
currentNode = nodes_list_dict[node]
- if currentNode.get("error", None) is None:
+
+ currentNodeVals = currentNode.get("error")
+
+ if currentNodeVals is None:
status = "\033[92m[Loading]"
else:
- status = "\033[1;31;40m[Failed]"
+ color_error = "\033[1;31;40m"
+ status = color_error + " [Failed]"
error_message = currentNode.get("error", "Error")
- failed_nodes_text += f"\033[93m{node} -> \033[1;31;40m{error_message}\033[0m\n"
+
+ if(len(currentNodeVals.args) == 2 and currentNodeVals.args[1] == "warning"):
+ color_error = "\033[33m"
+ status = color_error + "[Warning]"
+ error_message = currentNodeVals.args[0]
+
+ failed_nodes_text += f"\033[93m{node} -> {color_error}{error_message}\033[0m\n"
message = currentNode.get("message", "No message available")
printColorInfo(f"{message}{status}\033[0m")
if key == len_nodes - 1 and failed_nodes_text:
printColorInfo(
- f"\n\033[1;31;40m* Nodes have been temporarily disabled due to the error *\033[0m\n{failed_nodes_text}"
+ f"\n\033[1;31;40m* Nodes have been temporarily disabled due to the error or specially *\033[0m\n{failed_nodes_text}"
)
printColorInfo(f"### [END] ComfyUI AlekPet Nodes ###", "\033[1;35m")
diff --git a/pyproject.toml b/pyproject.toml
index e89e862..9e3ba5e 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,7 +1,7 @@
[project]
name = "comfyui_custom_nodes_alekpet"
-description = "Nodes: PoseNode, PainterNode, TranslateTextNode, TranslateCLIPTextEncodeNode, DeepTranslatorTextNode, DeepTranslatorCLIPTextEncodeNode, ArgosTranslateTextNode, ArgosTranslateCLIPTextEncodeNode, ChatGLM4TranslateCLIPTextEncodeNode, ChatGLM4TranslateTextNode, ChatGLM4InstructNode, ChatGLM4InstructMediaNode, DeepLXTranslateTextNode, DeepLXTranslateCLIPTextEncodeNode, PreviewTextNode, HexToHueNode, ColorsCorrectNode, IDENode."
-version = "1.0.71"
+description = "Nodes: PoseNode, PainterNode, TranslateTextNode, TranslateCLIPTextEncodeNode, DeepTranslatorTextNode, DeepTranslatorCLIPTextEncodeNode, ArgosTranslateTextNode, ArgosTranslateCLIPTextEncodeNode, ChatGLM4TranslateCLIPTextEncodeNode, ChatGLM4TranslateTextNode, ChatGLM4InstructNode, ChatGLM4InstructMediaNode, PreviewTextNode, HexToHueNode, ColorsCorrectNode, IDENode."
+version = "1.0.74"
license = { file = "LICENSE" }
[project.urls]