merlemcdurrrmott commited on
Commit
15a8a7f
·
verified ·
1 Parent(s): bc57e00

Upload app/custom_node_manager.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. app/custom_node_manager.py +143 -0
app/custom_node_manager.py ADDED
@@ -0,0 +1,143 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import folder_paths
3
+ import glob
4
+ from aiohttp import web
5
+ import json
6
+ import logging
7
+ from functools import lru_cache
8
+
9
+ from utils.json_util import merge_json_recursive
10
+
11
+
12
+ # Extra locale files to load into main.json
13
+ EXTRA_LOCALE_FILES = [
14
+ "nodeDefs.json",
15
+ "commands.json",
16
+ "settings.json",
17
+ ]
18
+
19
+
20
+ def safe_load_json_file(file_path: str) -> dict:
21
+ if not os.path.exists(file_path):
22
+ return {}
23
+
24
+ try:
25
+ with open(file_path, "r", encoding="utf-8") as f:
26
+ return json.load(f)
27
+ except json.JSONDecodeError:
28
+ logging.error(f"Error loading {file_path}")
29
+ return {}
30
+
31
+
32
+ class CustomNodeManager:
33
+ @lru_cache(maxsize=1)
34
+ def build_translations(self):
35
+ """Load all custom nodes translations during initialization. Translations are
36
+ expected to be loaded from `locales/` folder.
37
+
38
+ The folder structure is expected to be the following:
39
+ - custom_nodes/
40
+ - custom_node_1/
41
+ - locales/
42
+ - en/
43
+ - main.json
44
+ - commands.json
45
+ - settings.json
46
+
47
+ returned translations are expected to be in the following format:
48
+ {
49
+ "en": {
50
+ "nodeDefs": {...},
51
+ "commands": {...},
52
+ "settings": {...},
53
+ ...{other main.json keys}
54
+ }
55
+ }
56
+ """
57
+
58
+ translations = {}
59
+
60
+ for folder in folder_paths.get_folder_paths("custom_nodes"):
61
+ # Sort glob results for deterministic ordering
62
+ for custom_node_dir in sorted(glob.glob(os.path.join(folder, "*/"))):
63
+ locales_dir = os.path.join(custom_node_dir, "locales")
64
+ if not os.path.exists(locales_dir):
65
+ continue
66
+
67
+ for lang_dir in glob.glob(os.path.join(locales_dir, "*/")):
68
+ lang_code = os.path.basename(os.path.dirname(lang_dir))
69
+
70
+ if lang_code not in translations:
71
+ translations[lang_code] = {}
72
+
73
+ # Load main.json
74
+ main_file = os.path.join(lang_dir, "main.json")
75
+ node_translations = safe_load_json_file(main_file)
76
+
77
+ # Load extra locale files
78
+ for extra_file in EXTRA_LOCALE_FILES:
79
+ extra_file_path = os.path.join(lang_dir, extra_file)
80
+ key = extra_file.split(".")[0]
81
+ json_data = safe_load_json_file(extra_file_path)
82
+ if json_data:
83
+ node_translations[key] = json_data
84
+
85
+ if node_translations:
86
+ translations[lang_code] = merge_json_recursive(
87
+ translations[lang_code], node_translations
88
+ )
89
+
90
+ return translations
91
+
92
+ def add_routes(self, routes, webapp, loadedModules):
93
+
94
+ example_workflow_folder_names = ["example_workflows", "example", "examples", "workflow", "workflows"]
95
+
96
+ @routes.get("/workflow_templates")
97
+ async def get_workflow_templates(request):
98
+ """Returns a web response that contains the map of custom_nodes names and their associated workflow templates. The ones without templates are omitted."""
99
+
100
+ files = []
101
+
102
+ for folder in folder_paths.get_folder_paths("custom_nodes"):
103
+ for folder_name in example_workflow_folder_names:
104
+ pattern = os.path.join(folder, f"*/{folder_name}/*.json")
105
+ matched_files = glob.glob(pattern)
106
+ files.extend(matched_files)
107
+
108
+ workflow_templates_dict = (
109
+ {}
110
+ ) # custom_nodes folder name -> example workflow names
111
+ for file in files:
112
+ custom_nodes_name = os.path.basename(
113
+ os.path.dirname(os.path.dirname(file))
114
+ )
115
+ workflow_name = os.path.splitext(os.path.basename(file))[0]
116
+ workflow_templates_dict.setdefault(custom_nodes_name, []).append(
117
+ workflow_name
118
+ )
119
+ return web.json_response(workflow_templates_dict)
120
+
121
+ # Serve workflow templates from custom nodes.
122
+ for module_name, module_dir in loadedModules:
123
+ for folder_name in example_workflow_folder_names:
124
+ workflows_dir = os.path.join(module_dir, folder_name)
125
+
126
+ if os.path.exists(workflows_dir):
127
+ if folder_name != "example_workflows":
128
+ logging.debug(
129
+ "Found example workflow folder '%s' for custom node '%s', consider renaming it to 'example_workflows'",
130
+ folder_name, module_name)
131
+
132
+ webapp.add_routes(
133
+ [
134
+ web.static(
135
+ "/api/workflow_templates/" + module_name, workflows_dir
136
+ )
137
+ ]
138
+ )
139
+
140
+ @routes.get("/i18n")
141
+ async def get_i18n(request):
142
+ """Returns translations from all custom nodes' locales folders."""
143
+ return web.json_response(self.build_translations())