Spaces:
Runtime error
Runtime error
from __future__ import annotations | |
import os | |
import folder_paths | |
import glob | |
from aiohttp import web | |
import json | |
import logging | |
from functools import lru_cache | |
from app_utils.json_util import merge_json_recursive | |
# Extra locale files to load into main.json | |
EXTRA_LOCALE_FILES = [ | |
"nodeDefs.json", | |
"commands.json", | |
"settings.json", | |
] | |
def safe_load_json_file(file_path: str) -> dict: | |
if not os.path.exists(file_path): | |
return {} | |
try: | |
with open(file_path, "r", encoding="utf-8") as f: | |
return json.load(f) | |
except json.JSONDecodeError: | |
logging.error(f"Error loading {file_path}") | |
return {} | |
class CustomNodeManager: | |
def build_translations(self): | |
"""Load all custom nodes translations during initialization. Translations are | |
expected to be loaded from `locales/` folder. | |
The folder structure is expected to be the following: | |
- custom_nodes/ | |
- custom_node_1/ | |
- locales/ | |
- en/ | |
- main.json | |
- commands.json | |
- settings.json | |
returned translations are expected to be in the following format: | |
{ | |
"en": { | |
"nodeDefs": {...}, | |
"commands": {...}, | |
"settings": {...}, | |
...{other main.json keys} | |
} | |
} | |
""" | |
translations = {} | |
for folder in folder_paths.get_folder_paths("custom_nodes"): | |
# Sort glob results for deterministic ordering | |
for custom_node_dir in sorted(glob.glob(os.path.join(folder, "*/"))): | |
locales_dir = os.path.join(custom_node_dir, "locales") | |
if not os.path.exists(locales_dir): | |
continue | |
for lang_dir in glob.glob(os.path.join(locales_dir, "*/")): | |
lang_code = os.path.basename(os.path.dirname(lang_dir)) | |
if lang_code not in translations: | |
translations[lang_code] = {} | |
# Load main.json | |
main_file = os.path.join(lang_dir, "main.json") | |
node_translations = safe_load_json_file(main_file) | |
# Load extra locale files | |
for extra_file in EXTRA_LOCALE_FILES: | |
extra_file_path = os.path.join(lang_dir, extra_file) | |
key = extra_file.split(".")[0] | |
json_data = safe_load_json_file(extra_file_path) | |
if json_data: | |
node_translations[key] = json_data | |
if node_translations: | |
translations[lang_code] = merge_json_recursive( | |
translations[lang_code], node_translations | |
) | |
return translations | |
def add_routes(self, routes, webapp, loadedModules): | |
async def get_workflow_templates(request): | |
"""Returns a web response that contains the map of custom_nodes names and their associated workflow templates. The ones without templates are omitted.""" | |
files = [ | |
file | |
for folder in folder_paths.get_folder_paths("custom_nodes") | |
for file in glob.glob( | |
os.path.join(folder, "*/example_workflows/*.json") | |
) | |
] | |
workflow_templates_dict = ( | |
{} | |
) # custom_nodes folder name -> example workflow names | |
for file in files: | |
custom_nodes_name = os.path.basename( | |
os.path.dirname(os.path.dirname(file)) | |
) | |
workflow_name = os.path.splitext(os.path.basename(file))[0] | |
workflow_templates_dict.setdefault(custom_nodes_name, []).append( | |
workflow_name | |
) | |
return web.json_response(workflow_templates_dict) | |
# Serve workflow templates from custom nodes. | |
for module_name, module_dir in loadedModules: | |
workflows_dir = os.path.join(module_dir, "example_workflows") | |
if os.path.exists(workflows_dir): | |
webapp.add_routes( | |
[ | |
web.static( | |
"/api/workflow_templates/" + module_name, workflows_dir | |
) | |
] | |
) | |
async def get_i18n(request): | |
"""Returns translations from all custom nodes' locales folders.""" | |
return web.json_response(self.build_translations()) | |