import os import re import json from server import PromptServer from aiohttp import web import requests import deep_translator from deep_translator import ( BaiduTranslator, ChatGptTranslator, DeeplTranslator, GoogleTranslator, LibreTranslator, LingueeTranslator, MyMemoryTranslator, MicrosoftTranslator, PapagoTranslator, PonsTranslator, QcriTranslator, YandexTranslator, single_detection, batch_detection, ) # RegExp empty_str = re.compile(r"^\s*$", re.I | re.M) remove_brackets_reg = re.compile(r"[\[\]]*") key_val_reg = re.compile(r"^[\w-]+=[^=][.\w-]*$", re.I) key_val_proxy_reg = re.compile(r"^https?=\d{1,3}(?:\.\d{1,3}){3}:\d{1,5}$", re.I) service_correct_reg = re.compile(r"\s*\[.*\]") check_proxy_reg = re.compile(r"\d{1,3}\.\d{1,3}\.\d{1,3}\:\d+") # Debug mode DEBUG = False ### ===== Deep Translator Nodes ===== ### def log(*text, desc="[Deep Translator => "): if DEBUG: print(desc) print(*text, sep=", ") # Default API Keys variables DEFAULT_API_KEYS_SERVICES = { "QcriTranslator": "c8af063b6c350215bc74340e16eebf51", "DetectLanguage": "26838885af95f01110f154dac9d6a235", } # Global variables CONFIG = {} CONFIG_SERVICES = {} CONFIG_PROXYES = {} CONFIG_SETTINGS = {} # Global variable detect languages support DETECT_LANGS_SUPPORT = {} # Directory translate node and config file dir_translate_node = os.path.dirname(__file__) config_path = os.path.join(os.path.abspath(dir_translate_node), "config.json") detect_languages_list = os.path.join( os.path.abspath(dir_translate_node), "detect_languages_list.json" ) # Check service view and api_key def check_service_view(services_prop={}, service=""): if not service and not services_prop: return False if ( services_prop.get("show_service", False) and CONFIG_SETTINGS and CONFIG_SETTINGS.get("show_services_no_check_api", False) ): return True else: # Not free_api and check api_key if incorrect free_api = services_prop.get("free_api", None) if not free_api is None and free_api == False: if ( service == "BaiduTranslator" and services_prop.get("appid", "your_appid") not in ("your_appid", "") and services_prop.get("appkey", "your_appkey") not in ("your_appkey", "") ): return True if ( service == "PapagoTranslator" and services_prop.get("client_id", "your_client_id") not in ("your_client_id", "") and services_prop.get("secret_key", "your_secret_key") not in ("your_secret_key", "") ): return True if ( service == "DeeplTranslator" and services_prop.get("use_free_api", True) and services_prop.get("api_key", "your_api_key") not in ("your_api_key", "") ): return True if services_prop.get("api_key", "your_api_key") not in ("your_api_key", ""): return True else: return True log(f"Service: {service} is not showing...") return False # Load config.js file if not os.path.exists(config_path): print("File config.js file not found! Reinstall extensions! Used default settings!") else: with open(config_path, "r") as f: CONFIG = json.load(f) # JSON data for prop in CONFIG.get("deep_translator_node", {}): deep_config = CONFIG["deep_translator_node"].get(prop, {}) if prop == "services": services_prop = deep_config CONFIG_SERVICES = deep_config if prop == "proxies": CONFIG_PROXYES = deep_config if prop == "settings": CONFIG_SETTINGS = deep_config CONFIG_SERVICES = { s: services_prop[s] for s in CONFIG_SERVICES if check_service_view(services_prop[s], s) } # ===== # Support languages - detectlanguage try: if not os.path.exists(detect_languages_list): log("File detect_languages_list.json file not found! Get List from site!") DETECT_LANGS_SUPPORT = requests.get( "https://ws.detectlanguage.com/0.2/languages" ).json() with open(detect_languages_list, "w") as f: json.dump(DETECT_LANGS_SUPPORT, f) log("Loading detect languages list support from site complete and save!") else: with open(detect_languages_list, "r") as f: DETECT_LANGS_SUPPORT = json.load(f) log("Loading detect languages list support from json file!") except Exception as e: log(f"Error loading of the dictionary to determine the language: {e}") def selectService(service): if service: class_translate = ( getattr(deep_translator, service) if service in dir(deep_translator) else getattr(deep_translator, "GoogleTranslator") ) langs_support = {} auto_support = True service_data = CONFIG_SERVICES.get(service, {}) if service in ("GoogleTranslator", "ChatGptTranslator", "YandexTranslator"): langs_support = GoogleTranslator().get_supported_languages(as_dict=True) if service in ( "MicrosoftTranslator", "LibreTranslator", "DeeplTranslator", "PonsTranslator", ): langs_support = class_translate( api_key="api_key", source="en" ).get_supported_languages(as_dict=True) if service == "QcriTranslator": api_key = service_data.get("api_key", None) api_key = ( api_key if not api_key is None else DEFAULT_API_KEYS_SERVICES["QcriTranslator"] ) langs_support = class_translate(api_key=api_key).get_supported_languages( as_dict=True ) if service in ("MyMemoryTranslator", "LingueeTranslator"): langs_support = class_translate( api_key="api_key", source="english", target="english" ).get_supported_languages(as_dict=True) if service == "BaiduTranslator": langs_support = BaiduTranslator( appid="appid", appkey="appkey" ).get_supported_languages(as_dict=True) if service == "PapagoTranslator": langs_support = PapagoTranslator( client_id="client_id", secret_key="secret_key" ).get_supported_languages(as_dict=True) if service in ( "DeeplTranslator", "PonsTranslator", "QcriTranslator", "LingueeTranslator", "PapagoTranslator", "BaiduTranslator", ): # "MyMemoryTranslator" ??? auto_support = False return langs_support @PromptServer.instance.routes.get("/alekpet/tranlsate_langs_support/{service}") async def langs_support(request): service = request.match_info["service"] settings = { "auth_input_in_node": CONFIG_SETTINGS.get("auth_input_in_node", False), "proxyes_input_in_node": CONFIG_SETTINGS.get("proxyes_input_in_node", False), } proxies = { k.lower(): p for k, p in CONFIG_PROXYES.items() if k.lower() in ("http", "https") and check_proxy_reg.search(p) } if service: proxies = {} auth_data = {} langs_support = selectService(service) if service in CONFIG_SERVICES.keys() and not CONFIG_SETTINGS.get("hide_keys", True): auth_data = { keyS: servP for keyS, servP in CONFIG_SERVICES.get(service, {}).items() if keyS in ( "api_key", "domain", "use_free_api", "appid", "appkey", "client_id", "secret_key", ) } return web.json_response( { "langs_service": langs_support, "auth_data": auth_data, "proxies": proxies, "settings": settings, } ) return web.json_response( {"langs_service": {}, "auth_data": {}, "proxies": proxies, "settings": settings} ) ### Service translate function def service_translate( service, text, from_translate="auto", to_translate="en", prop_data={} ): translated = "No tranlsate, see ComfyUI console..." proxyes = prop_data.get("proxies", {}) auth_data = prop_data.get("auth_data", {}) api_key = auth_data.get("api_key", "your_api_key") log( f"Translate from={from_translate}, to={to_translate}, prop_data={prop_data}, proxy = {proxyes}" ) # --- Free --- # Google if service == "GoogleTranslator": translated = GoogleTranslator( source=from_translate, target=to_translate, proxies=proxyes ).translate(text) # MyMemoryTranslator elif service == "MyMemoryTranslator": translated = MyMemoryTranslator( source=from_translate, target=to_translate, proxies=proxyes ).translate(text) # LingueeTranslator and PonsTranslator elif service == "LingueeTranslator" or service == "PonsTranslator": words = list(filter(bool, re.split(r"[,.!;\s\t]", text))) log(f"List words: {', '.join(words)}") if service == "LingueeTranslator": translated = LingueeTranslator( source=from_translate, target=to_translate, proxies=proxyes ).translate_words(words) else: try: translated = PonsTranslator( source=from_translate, target=to_translate, proxies=proxyes ).translate_words(words) except Exception as err: print( f'[Deep Translator] Service "{service}", it gives an error if words from other languages that do not correspond to the source are used : {err}' ) # LibreTranslator elif service == "LibreTranslator": use_free_api = auth_data.get("use_free_api", True) translated = LibreTranslator( source=from_translate, target=to_translate, base_url='"https://libretranslate.com/translate', api_key=api_key, proxies=proxyes, ).translate(text=text) # --- Need API KEY AND OTHER DATA --- # DeeplTranslator elif service == "DeeplTranslator": use_free_api = auth_data.get("use_free_api", True) translated = DeeplTranslator( api_key=api_key, source=from_translate, target=to_translate, use_free_api=use_free_api, proxies=proxyes, ).translate(text) # QcriTranslator elif service == "QcriTranslator": domain = auth_data.get("domain", "general") translated = QcriTranslator( api_key=api_key, source=from_translate, target=to_translate ).translate(domain=domain, text=text, proxies=proxyes) # BaiduTranslator elif service == "BaiduTranslator": appid = auth_data.get("appid", "your-appid") appkey = auth_data.get("appkey", "your-appkey") translated = BaiduTranslator( appid=appid, appkey=appkey, source=from_translate, target=to_translate, proxies=proxyes, ).translate(text) # ChatGptTranslator elif service == "ChatGptTranslator": translated = ChatGptTranslator( api_key=api_key, target=to_translate, proxies=proxyes ).translate(text=text) # MicrosoftTranslator elif service == "MicrosoftTranslator": translated = MicrosoftTranslator( api_key=api_key, target=to_translate, proxies=proxyes ).translate(text=text) # PapagoTranslator elif service == "PapagoTranslator": client_id = auth_data.get("client_id", "your_client_id") secret_key = auth_data.get("secret_key", "your_secret_key") translated = PapagoTranslator( client_id=client_id, secret_key=secret_key, source=from_translate, target=to_translate, proxies=proxyes, ).translate(text=text) # YandexTranslator elif service == "YandexTranslator": translated = YandexTranslator(api_key=api_key).translate( source=from_translate, target=to_translate, text=text, proxies=proxyes ) return translated ### Function get dictinary from text, from auth_data and proxies def makeDictText(name_prop, text="", regexp=key_val_reg): data = {name_prop: {}} try: split_text = text.splitlines() if len(split_text) > 0: split_text = [ line for line in split_text if line and line.strip() != "" and regexp.search(line) ] split_text = list(map(lambda p: p.split("="), split_text)) for text_data in split_text: if len(text_data) == 2: key, val = text_data data[name_prop][key.strip()] = val.strip() log(f"Value {name_prop}: {data[name_prop]}") except Exception as e: print(f"[Deep Translator] Error {name_prop} exception: ", e) finally: return data # Detect languages in input def isset_languages(text, service, from_translate, langs_support={}, auth_data={}): is_support = False detect_lang_short = "" detect = [] try: # Get api from input auth_data if empty from service detect_lang_api_key = auth_data.get("detect_lang_api_key", "") if detect_lang_api_key.strip() == "": detectServiceData = CONFIG_SERVICES.get("DetectLanguage", {}) detect_lang_api_key = detectServiceData.get("api_key", None) detect_lang_api_key = ( detect_lang_api_key if not detect_lang_api_key is None else DEFAULT_API_KEYS_SERVICES["DetectLanguage"] ) # Function detect source language detect_lang_short = single_detection(text, api_key=detect_lang_api_key) detect = list( filter(lambda d: d["code"] == detect_lang_short, DETECT_LANGS_SUPPORT) )[0] log( f"[{service}] Detect short: {detect_lang_short}, detect: {detect}, lang support: {len(langs_support.keys())}" ) if ( detect_lang_short and len(detect) > 0 and detect.get("name", "") and langs_support ): detect_lang_full = detect["name"].lower() langs_support_keys = langs_support.keys() detect_in_base = list( filter( lambda lang: lang.lower() == detect_lang_full or lang.capitalize() == detect_lang_full.capitalize() or lang.lower() == detect_lang_short, langs_support_keys, ) ) if detect_in_base: if service in ("QcriTranslator",): detect_lang_full = detect_lang_full.capitalize() if service in ("PonsTranslator",): detect_lang_full = detect_lang_short from_translate = detect_lang_full is_support = True log(f"[Deep Translator> Detect in base: {detect_lang_full}") else: log(f"[Deep Translator>No detect in base: {detect_lang_full}") except Exception as e: print(f"[Deep Translator] Error detect language: {e}") return (from_translate, is_support, detect) ### Function deep translator for all deep_translator nodes def deep_translator_function( from_translate, to_translate, add_proxies, proxies, auth_data, service, text, lang_support={}, ): text_tranlsated = "" prop_data = {"proxies": {}, "auth_data": {}} try: if text: print(f'[Deep Translator] Service: "{service}"') # Proxy prop if add_proxies: if isinstance(proxies, (str,)) and proxies.strip() != "": prop_data.update( makeDictText("proxies", proxies, key_val_proxy_reg) ) elif proxies is None: prop_data.update( { "proxies": { k.lower(): p for k, p in CONFIG_PROXYES.items() if k.lower() in ("http", "https") and check_proxy_reg.search(p) } } ) proxies_info = ", ".join([f"{prop}={val}" for prop, val in prop_data["proxies"].items()]) print(f"[Deep Translator] Proxy is enabled. Proxies: {proxies_info}") else: print("[Deep Translator] Proxy disabled or input field is empty!") # Auth prop if auth_data is None or not auth_data.strip() and CONFIG_SETTINGS.get("hide_keys", True): prop_data.update( { "auth_data": { keyS: servP for keyS, servP in CONFIG_SERVICES.get(service, {}).items() if keyS in ( "api_key", "domain", "use_free_api", "appid", "appkey", "client_id", "secret_key", ) } } ) elif isinstance(auth_data, (str,)) and auth_data.strip() != "": prop_data.update(makeDictText("auth_data", auth_data)) else: print("[Deep Translator] Authorization input field is empty!") # Detect language tServices = ( "DeeplTranslator", "QcriTranslator", "LingueeTranslator", "PonsTranslator", "PapagoTranslator", "BaiduTranslator", "MyMemoryTranslator", ) if from_translate == "auto" and service in tServices: from_translate, is_support, detect = isset_languages( text, service, from_translate, lang_support, prop_data ) log(f"Detect turple: {(from_translate, is_support, detect)}") else: print( f"Deep Translator] Service detect language disabled! Services support: {', '.join(tServices)}.\nThe selected service has its own way of detecting the language.\nProperty \"detect_lang_api_key\" in Authorization data is empty or incorrect!" ) log(f"[{service}] => Data: {prop_data}") text_tranlsated = service_translate( service, text, from_translate, to_translate, prop_data ) if not text_tranlsated or text_tranlsated is None: text_tranlsated = "" elif isinstance(text_tranlsated, (tuple, list)): text_tranlsated = " ".join(text_tranlsated) except Exception as e: print(f"[Deep Translator] Error: {e}") finally: return text_tranlsated, from_translate def makeRequiredFields(langs_support=[]): params = { "from_translate": (["auto"] + langs_support, {"default": "auto"}), "to_translate": (langs_support, {"default": "english"}), "add_proxies": ("BOOLEAN", {"default": False}), "proxies": ( "STRING", { "multiline": True, "placeholder": "Proxies list (http=proxy), example:\nhttps=34.195.196.27:8080\nhttp=34.195.196.27:8080", "default": ( "" if len(CONFIG_PROXYES.values()) == 0 else "\n".join( [ f"{k.lower()}={p}" for k, p in CONFIG_PROXYES.items() if k.lower() in ("http", "https") and check_proxy_reg.search(p) ] ) ), }, ), "auth_data": ( "STRING", { "multiline": True, "placeholder": "Authorization data...\nExample:\napi_key=your_api_key\ndetect_lang_api_key=your_api_key\nclient_id=your_client_id\nsecret_key=your_secret_key\nappid=your-appid\nappkey=your-appkey", }, ), "service": (), "text": ("STRING", {"multiline": True, "placeholder": "Input text"}), "clip": ("CLIP",), } if CONFIG_SERVICES and isinstance(CONFIG_SERVICES, (dict,)): if CONFIG_SETTINGS and CONFIG_SETTINGS.get("help_text_services"): services_combo = [] for service_key, service_prop in CONFIG_SERVICES.items(): if service_prop.get("show_service", False): service_help = service_prop.get("help", "") service_help = remove_brackets_reg.sub("", service_help) service_val = ( f"{service_key} [{service_help}]" if service_help.strip() != "" else service_key ) services_combo.append(service_val) params["service"] = (services_combo, {"default": "GoogleTranslator"}) else: params["service"] = ( list( filter( lambda s: CONFIG_SERVICES[s].get("show_service", False), CONFIG_SERVICES.keys(), ) ), {"default": "GoogleTranslator"}, ) else: params["service"] = ( [ "GoogleTranslator [free]", "MyMemoryTranslator [free]", "LibreTranslator [free or api_key]", "LingueeTranslator [free - word(s) only]", "PonsTranslator [free - word(s) only]", "DeeplTranslator [api-key]", "ChatGptTranslator [api-key]", "BaiduTranslator [appid and appkey]", "MicrosoftTranslator [api-key]", "PapagoTranslator [client_id, secret_key]", "QcriTranslator [api-key]", "YandexTranslator [api-key]", ], {"default": "GoogleTranslator"}, ) return params ### Deep Translator output -> CONDITIONING class DeepTranslatorCLIPTextEncodeNode: @classmethod def INPUT_TYPES(self): self.current_service = "" self.proxies = "" self.auth_data = "" self.langs_support = selectService("GoogleTranslator") langs_support = list(self.langs_support.keys()) return { "required": makeRequiredFields(langs_support), } RETURN_TYPES = ( "CONDITIONING", "STRING", ) OUTPUT_NODE = True DESCRIPTION = "This is a node that translates the hint text into another language using various services included in Deep Translator." FUNCTION = "deep_translate_text" CATEGORY = "AlekPet Nodes/conditioning" def deep_translate_text( self, from_translate, to_translate, add_proxies, proxies, auth_data, service, text, clip, ): # Select service service = service_correct_reg.sub("", service) if self.current_service != service: self.langs_support = selectService(service) self.current_service = service # Translate text_tranlsated, detected_lang = deep_translator_function( from_translate, to_translate, add_proxies, proxies, auth_data, service, text, self.langs_support, ) tokens = clip.tokenize(text_tranlsated) cond, pooled = clip.encode_from_tokens(tokens, return_pooled=True) return { "ui": {"detected_lang": (detected_lang,)}, "result": ( [[cond, {"pooled_output": pooled}]], text_tranlsated, ), } @classmethod def VALIDATE_INPUTS( cls, from_translate, to_translate, add_proxies, proxies, auth_data, service, text, clip, ): # lang, is_support, detect = isset_languages(text, service, from_translate, self.langs_support) # if not is_support: # return f"[Deep Translator] Service \"{service}\", no support for the provided language: {detect}!" return True ### Deep Translator output -> TEXT class DeepTranslatorTextNode(DeepTranslatorCLIPTextEncodeNode): @classmethod def INPUT_TYPES(self): types = super().INPUT_TYPES() del types["required"]["clip"] return types RETURN_TYPES = ("STRING",) RETURN_NAMES = ("text",) OUTPUT_NODE = True FUNCTION = "deep_translate_text" CATEGORY = "AlekPet Nodes/text" def deep_translate_text( self, from_translate, to_translate, add_proxies, proxies, auth_data, service, text, ): # Select service service = service_correct_reg.sub("", service) if self.current_service != service: self.langs_support = selectService(service) self.current_service = service # Translate text_tranlsated, detected_lang = deep_translator_function( from_translate, to_translate, add_proxies, proxies, auth_data, service, text, self.langs_support, ) return { "ui": {"detected_lang": (detected_lang,)}, "result": (text_tranlsated,), } @classmethod def VALIDATE_INPUTS( cls, from_translate, to_translate, add_proxies, proxies, auth_data, service, text, ): # lang, is_support, detect = isset_languages(text, service, from_translate, self.langs_support) # if not is_support: # return f"[Deep Translator] Service \"{service}\", no support for the provided language: {detect}!" return True ### ===== Deep Translator Node -> end ===== ###