bizydraft 0.2.76.dev20251027100756__py3-none-any.whl → 0.2.78.dev20251029103128__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of bizydraft might be problematic. Click here for more details.

bizydraft/oss_utils.py CHANGED
@@ -95,7 +95,13 @@ async def get_upload_token(
95
95
  filename: str,
96
96
  api_key: str,
97
97
  ) -> Dict[str, Any]:
98
- url = f"{BIZYDRAFT_SERVER}/upload/token?file_name={filename}&file_type=inputs"
98
+ from urllib.parse import quote
99
+
100
+ # 对文件名进行URL编码,避免特殊字符导致问题
101
+ encoded_filename = quote(filename, safe="")
102
+ url = (
103
+ f"{BIZYDRAFT_SERVER}/upload/token?file_name={encoded_filename}&file_type=inputs"
104
+ )
99
105
 
100
106
  headers = {
101
107
  "Content-Type": "application/json",
@@ -412,9 +418,22 @@ async def upload_mask(request):
412
418
 
413
419
 
414
420
  def _should_clean(name: str) -> bool:
415
- """True -> 乱码;False -> 正常"""
416
- # 主名部分含 URL 参数符号且最后有扩展名
417
- return bool(re.search(r"[&=,].+\.[\w]+$", name))
421
+ """True -> 包含非白名单字符;False -> 正常
422
+
423
+ 使用白名单机制:只允许安全字符(中英文、数字、下划线、连字符、点、空格)
424
+ 如果文件名包含白名单之外的字符,则需要清理
425
+ """
426
+ if not name:
427
+ return False
428
+
429
+ # 分离文件名和扩展名
430
+ if "." not in name:
431
+ return False
432
+
433
+ # 白名单:允许中英文、数字、下划线、连字符、点、空格、圆括号
434
+ safe_pattern = r"^[\w\u4e00-\u9fa5\s\-().]+$"
435
+
436
+ return not bool(re.match(safe_pattern, name))
418
437
 
419
438
 
420
439
  def clean_filename(bad: str) -> (bool, str):
@@ -1,6 +1,15 @@
1
1
  // 保存原始的 WebSocket 构造函数
2
2
  const OriginalWebSocket = window.WebSocket;
3
3
 
4
+ // 保存原始的 fetch 函数
5
+ const OriginalFetch = window.fetch;
6
+
7
+ // 需要跳过的 URL 数组
8
+ const skipFetchUrls = [
9
+ 'manager/badge_mode',
10
+ 'pysssss/autocomplete'
11
+ ];
12
+
4
13
  class FakeWebSocket {
5
14
  constructor(url) {
6
15
  this.url = url;
@@ -35,3 +44,22 @@ window.WebSocket.prototype = OriginalWebSocket.prototype;
35
44
  configurable: true
36
45
  });
37
46
  });
47
+
48
+ // 拦截 fetch 请求
49
+ window.fetch = function(url) {
50
+ // 将 URL 转换为字符串进行比较
51
+ const urlString = typeof url === 'string' ? url : url.toString();
52
+ // 检查 URL 是否在跳过列表中
53
+ if (skipFetchUrls.some(skipUrl => urlString.includes(skipUrl))) {
54
+ console.warn('[BizyDraft] 已阻止 fetch 请求:', urlString);
55
+ // 返回一个模拟的 Response 对象,状态为 200
56
+ return Promise.resolve(new Response(null, {
57
+ status: 200,
58
+ statusText: 'OK',
59
+ headers: new Headers({ 'Content-Type': 'application/json' })
60
+ }));
61
+ }
62
+
63
+ // 其他请求正常发送
64
+ return OriginalFetch.apply(this, arguments);
65
+ };
@@ -56,6 +56,10 @@ const styleMenus = `
56
56
  .side-tool-bar-container.small-sidebar .side-bar-button-label{
57
57
  display: none;
58
58
  }
59
+ body .comfy-img-preview video{
60
+ width: 100%;
61
+ height: 100%;
62
+ }
59
63
  `;
60
64
  app.registerExtension({
61
65
  name: "comfy.BizyAir.Style",
@@ -0,0 +1,68 @@
1
+ // 统一的配置加载器 - 合并 model.js 和 media.js 的配置请求
2
+ // 只发送一次 API 请求,同时获取 weight_load_nodes 和 media_load_nodes
3
+
4
+ // 统一的配置缓存
5
+ let fullConfigCache = null;
6
+ let configLoadPromise = null;
7
+
8
+ // API配置(统一使用一个域名,根据实际需要调整)
9
+ const CONFIG_API_URL = 'https://bizyair.cn/api/special/comfyagent_node_config?t=' + Math.floor(Date.now() / 60000);
10
+
11
+ // 获取完整配置的API函数(返回包含 weight_load_nodes 和 media_load_nodes 的完整对象)
12
+ export async function fetchFullConfig() {
13
+ if (fullConfigCache) {
14
+ return fullConfigCache;
15
+ }
16
+
17
+ if (configLoadPromise) {
18
+ return configLoadPromise;
19
+ }
20
+
21
+ configLoadPromise = (async () => {
22
+ try {
23
+ console.log('正在从API获取完整节点配置...');
24
+ const response = await fetch(CONFIG_API_URL, { credentials: 'include' });
25
+
26
+ if (!response.ok) {
27
+ throw new Error(`HTTP error! status: ${response.status}`);
28
+ }
29
+
30
+ const result = await response.json();
31
+
32
+ if (result.code === 20000 && result.data) {
33
+ fullConfigCache = {
34
+ weight_load_nodes: result.data.weight_load_nodes || {},
35
+ media_load_nodes: result.data.media_load_nodes || {}
36
+ };
37
+ console.log('完整配置加载成功:', {
38
+ weight_nodes: Object.keys(fullConfigCache.weight_load_nodes).length,
39
+ media_nodes: Object.keys(fullConfigCache.media_load_nodes).length
40
+ });
41
+ return fullConfigCache;
42
+ } else {
43
+ throw new Error('API返回数据格式不正确');
44
+ }
45
+ } catch (error) {
46
+ console.error('获取完整配置失败:', error);
47
+ fullConfigCache = null;
48
+ return null;
49
+ }
50
+ })();
51
+
52
+ return configLoadPromise;
53
+ }
54
+
55
+ // 获取模型节点配置(从完整配置中提取 weight_load_nodes)
56
+ export async function fetchNodeConfig() {
57
+ const fullConfig = await fetchFullConfig();
58
+ return fullConfig ? fullConfig.weight_load_nodes : null;
59
+ }
60
+
61
+ // 获取媒体节点配置(从完整配置中提取 media_load_nodes)
62
+ export async function fetchMediaConfig() {
63
+ const fullConfig = await fetchFullConfig();
64
+ return fullConfig ? fullConfig.media_load_nodes : null;
65
+ }
66
+
67
+ // 启动时后台预取(不阻塞后续逻辑)
68
+ try { void fetchFullConfig(); } catch (e) { /* noop */ }
@@ -1,12 +1,19 @@
1
1
  // 媒体节点配置获取与工具函数(与 hookLoad/model.js 结构一致,面向 media_load_nodes)
2
+ import { fetchMediaConfig } from './configLoader.js'
2
3
 
3
4
  // 动态配置缓存(仅缓存媒体部分)
4
5
  let mediaConfigCache = null;
5
6
  let mediaConfigLoadPromise = null;
6
7
 
7
- // API配置(与模型相同接口,不同数据块)
8
- const CONFIG_API_URL = 'https://bizyair.cn/api/special/comfyagent_node_config?t=' + Math.floor(Date.now() / 60000);
9
-
8
+ export const mediaNodeList = [
9
+ 'LoadImage',
10
+ 'LoadImageMask',
11
+ 'LoadAudio',
12
+ 'LoadVideo',
13
+ 'Load3D',
14
+ 'VHS_LoadVideo',
15
+ 'VHS_LoadAudioUpload'
16
+ ]
10
17
  // 常见的媒体输入字段名(作为回退匹配)
11
18
  export const possibleMediaWidgetNames = [
12
19
  "image",
@@ -16,28 +23,17 @@ export const possibleMediaWidgetNames = [
16
23
  "model_file"
17
24
  ];
18
25
 
19
- // 获取媒体配置的API函数(只解析 media_load_nodes)
20
- export async function fetchMediaConfig() {
26
+ // 获取媒体配置的API函数(使用共享配置加载器)
27
+ export async function fetchMediaConfigWithCache() {
21
28
  if (mediaConfigCache) return mediaConfigCache;
22
29
  if (mediaConfigLoadPromise) return mediaConfigLoadPromise;
23
30
 
24
31
  mediaConfigLoadPromise = (async () => {
25
- try {
26
- const response = await fetch(CONFIG_API_URL, { credentials: 'include' });
27
- if (!response || !response.ok) {
28
- throw new Error('HTTP error ' + (response && response.status));
29
- }
30
- const result = await response.json();
31
- if (result && result.code === 20000 && result.data && result.data.media_load_nodes) {
32
- mediaConfigCache = result.data.media_load_nodes;
33
- return mediaConfigCache;
34
- }
35
- throw new Error('API返回数据不包含 media_load_nodes');
36
- } catch (err) {
37
- console.error('获取媒体配置失败:', err);
38
- mediaConfigCache = null;
39
- return null;
32
+ const config = await fetchMediaConfig();
33
+ if (config) {
34
+ mediaConfigCache = config;
40
35
  }
36
+ return config;
41
37
  })();
42
38
 
43
39
  return mediaConfigLoadPromise;
@@ -46,7 +42,7 @@ export async function fetchMediaConfig() {
46
42
  // 根据节点名称获取媒体节点配置(仅使用缓存,不阻塞返回;触发后台预取)
47
43
  export async function getMediaNodeConfig(nodeName) {
48
44
  // 后台触发一次预取
49
- if (!mediaConfigLoadPromise) { try { void fetchMediaConfig(); } catch (e) {} }
45
+ if (!mediaConfigLoadPromise) { try { void fetchMediaConfigWithCache(); } catch (e) {} }
50
46
 
51
47
  if (mediaConfigCache && mediaConfigCache[nodeName]) {
52
48
  return { nodeName, config: mediaConfigCache[nodeName] };
@@ -66,5 +62,22 @@ export function getMediaInputKeys(mediaNodeConfig) {
66
62
  return keys;
67
63
  }
68
64
 
65
+
66
+ export async function computeIsMediaNode(nodeName) {
67
+ if (mediaNodeList.includes(nodeName)) {
68
+ return true;
69
+ }
70
+
71
+ // 2. 检查media_load_nodes的keys
72
+ const config = await fetchMediaConfigWithCache();
73
+ if (config) {
74
+ if (config.hasOwnProperty(nodeName)) {
75
+ return true;
76
+ }
77
+ }
78
+
79
+ return false;
80
+ }
81
+
69
82
  // 启动时后台预取(不阻塞后续逻辑)
70
- try { void fetchMediaConfig(); } catch (e) {}
83
+ try { void fetchMediaConfigWithCache(); } catch (e) {}
@@ -1,13 +1,11 @@
1
1
  import { hideWidget } from '../tool.js'
2
+ import { fetchNodeConfig } from './configLoader.js'
2
3
 
3
4
  // 动态配置缓存
4
5
  let nodeConfigCache = null;
5
6
  let configLoadPromise = null;
6
7
  let storageClearedOnce = false;
7
8
 
8
- // API配置
9
- const CONFIG_API_URL = 'https://bizyair.cn/api/special/comfyagent_node_config?t=' + Math.floor(Date.now() / 60000);
10
-
11
9
  const HAZY_WHITELIST_NODES = {
12
10
 
13
11
  }
@@ -64,8 +62,8 @@ export const possibleWidgetNames=[
64
62
  "grounding_dino_model"
65
63
  ]
66
64
 
67
- // 获取节点配置的API函数
68
- export async function fetchNodeConfig() {
65
+ // 获取节点配置的API函数(使用共享配置加载器)
66
+ async function fetchNodeConfigWithCache() {
69
67
  if (nodeConfigCache) {
70
68
  return nodeConfigCache;
71
69
  }
@@ -75,28 +73,12 @@ export async function fetchNodeConfig() {
75
73
  }
76
74
 
77
75
  configLoadPromise = (async () => {
78
- try {
79
- console.log('正在从API获取节点配置...');
80
- const response = await fetch(CONFIG_API_URL, { credentials: 'include' });
81
-
82
- if (!response.ok) {
83
- throw new Error(`HTTP error! status: ${response.status}`);
84
- }
85
-
86
- const result = await response.json();
87
-
88
- if (result.code === 20000 && result.data && result.data.weight_load_nodes) {
89
- nodeConfigCache = result.data.weight_load_nodes;
90
- console.log('节点配置加载成功:', Object.keys(nodeConfigCache).length, '个节点');
91
- return nodeConfigCache;
92
- } else {
93
- throw new Error('API返回数据格式不正确');
94
- }
95
- } catch (error) {
96
- console.error('获取节点配置失败:', error);
97
- nodeConfigCache = null;
98
- return null;
76
+ const config = await fetchNodeConfig();
77
+ if (config) {
78
+ nodeConfigCache = config;
79
+ console.log('节点配置加载成功:', Object.keys(nodeConfigCache).length, '个节点');
99
80
  }
81
+ return config;
100
82
  })();
101
83
 
102
84
  return configLoadPromise;
@@ -122,7 +104,7 @@ export async function getNodeConfig(nodeName) {
122
104
  }
123
105
 
124
106
  // 若尚未发起请求,后台发起一次
125
- if (!configLoadPromise) { try { void fetchNodeConfig(); } catch (e) {} }
107
+ if (!configLoadPromise) { try { void fetchNodeConfigWithCache(); } catch (e) {} }
126
108
 
127
109
  // 2) 正则补充:如 XxxLoader => Xxx(立即返回,不等待API)
128
110
  const regex = /^(\w+).*Loader.*/i;
@@ -293,4 +275,4 @@ export function addBadge(node) {
293
275
  }
294
276
 
295
277
  // 启动时后台预取(不阻塞后续逻辑)
296
- try { void fetchNodeConfig(); } catch (e) { /* noop */ }
278
+ try { void fetchNodeConfigWithCache(); } catch (e) { /* noop */ }
@@ -1,11 +1,12 @@
1
1
  import { app } from "../../scripts/app.js";
2
- import { getCookie, computeIsLoadNode, computeExt, hideWidget } from './tool.js';
3
- import { getMediaNodeConfig, getMediaInputKeys, possibleMediaWidgetNames } from './hookLoad/media.js';
2
+ import { getCookie, computeExt, hideWidget } from './tool.js';
3
+ import { getMediaNodeConfig, getMediaInputKeys, possibleMediaWidgetNames, computeIsMediaNode, mediaNodeList, fetchMediaConfigWithCache } from './hookLoad/media.js';
4
+
4
5
 
5
6
 
6
7
  app.registerExtension({
7
8
  name: "bizyair.image.to.oss",
8
- async beforeRegisterNodeDef(nodeType, nodeData) {
9
+ beforeRegisterNodeDef(nodeType, nodeData) {
9
10
  let workflowParams = null
10
11
  document.addEventListener('workflowLoaded', (event) => {
11
12
  workflowParams = event.detail;
@@ -31,8 +32,8 @@ app.registerExtension({
31
32
  }
32
33
  });
33
34
  })
34
- if (computeIsLoadNode(nodeData.name)) {
35
- nodeType.prototype.onNodeCreated = async function() {
35
+ nodeType.prototype.onNodeCreated = async function() {
36
+ if (await computeIsMediaNode(nodeData.name)) {
36
37
  const apiHost = 'https://bizyair.cn/api'
37
38
  // 优先使用 API 的媒体输入键匹配到具体的 widget;若未命中则回退到原有字段集合
38
39
  let media_widget = null;
@@ -95,26 +96,35 @@ app.registerExtension({
95
96
  name: item.name
96
97
  }
97
98
  })
99
+
100
+ // 📊 方案:使用 Map 缓存 URL→Name 映射(O(1) 查找)
101
+ const urlToNameMap = new Map();
102
+ const nameToItemMap = new Map();
103
+ image_list.forEach(item => {
104
+ urlToNameMap.set(item.url, item.name);
105
+ nameToItemMap.set(item.name, item);
106
+ });
107
+
98
108
  // 如果找到va_widgets,处理它们
99
109
  if (va_widgets.length > 0) {
100
- // 隐藏所有va_widgets
101
- va_widgets.forEach(va_widget => {
102
- hideWidget(this, va_widget.name);
103
- });
110
+ // 标志位:防止批量更新时触发监听
111
+ let isBatchUpdating = false;
104
112
 
105
113
  // 创建image_name_widget来替代显示
106
114
  if (!image_name_widget) {
107
115
  image_name_widget = this.addWidget("combo", "image_name", "", function(e){
108
- const item = image_list.find(item => item.name === e)
116
+ const item = nameToItemMap.get(e);
109
117
  if (item) {
110
118
  const image_url = decodeURIComponent(item.url);
111
- // 更新所有va_widgets的值
119
+ // 批量更新时跳过监听
120
+ isBatchUpdating = true;
112
121
  va_widgets.forEach(va_widget => {
113
122
  va_widget.value = image_url;
114
123
  if (va_widget.callback) {
115
- va_widget.callback(e);
124
+ va_widget.callback(image_url);
116
125
  }
117
126
  });
127
+ isBatchUpdating = false;
118
128
  }
119
129
  }, {
120
130
  serialize: true,
@@ -122,104 +132,153 @@ app.registerExtension({
122
132
  });
123
133
  }
124
134
 
135
+ // 隐藏所有va_widgets 并设置监听
136
+ va_widgets.forEach(va_widget => {
137
+ hideWidget(this, va_widget.name);
138
+ let _value = va_widget.value;
139
+
140
+ // 检查并删除现有的 value 属性描述符(如果存在)
141
+ const existingDescriptor = Object.getOwnPropertyDescriptor(va_widget, 'value');
142
+ if (existingDescriptor && !existingDescriptor.configurable) {
143
+ // 如果不可配置,跳过重新定义
144
+ return;
145
+ }
146
+
147
+ // 删除现有属性(如果存在)
148
+ if (existingDescriptor) {
149
+ delete va_widget.value;
150
+ }
151
+
152
+ Object.defineProperty(va_widget, 'value', {
153
+ get: function() {
154
+ return _value;
155
+ },
156
+ set: function(newValue) {
157
+ _value = newValue;
158
+
159
+ // 批量更新时跳过监听逻辑
160
+ if (isBatchUpdating) {
161
+ return;
162
+ }
163
+ // 使用 Map 快速查找(O(1))
164
+ const name = urlToNameMap.get(newValue);
165
+ if (name) {
166
+ image_name_widget.value = name;
167
+ } else {
168
+ // 如果没找到,从URL提取文件名
169
+ const fileName = newValue.split('/').pop();
170
+ image_name_widget.value = fileName;
171
+ }
172
+ },
173
+ enumerable: true,
174
+ configurable: true
175
+ });
176
+ });
177
+
178
+
125
179
  // 为每个va_widget重写callback
126
180
  va_widgets.forEach(va_widget => {
127
181
  // 保存va_widget的原始callback
128
182
  const originalVaCallback = va_widget.callback;
129
-
130
183
  // 重写va_widget的callback,当被触发时给image_name_widget赋值
131
184
  va_widget.callback = function(e) {
132
- // 调用原始callback
133
- if (originalVaCallback) {
134
- originalVaCallback(e);
135
- }
136
-
137
- // 给image_name_widget赋值
138
185
  if (image_name_widget) {
139
186
  if (typeof e === 'string') {
140
- const item = image_list.find(item => item.url === e);
141
- if (item) {
142
- image_name_widget.value = item.name;
187
+ // 使用 Map 快速查找(O(1)
188
+ const name = urlToNameMap.get(e);
189
+ if (name) {
190
+ image_name_widget.value = name;
143
191
  } else {
144
- // 如果没找到对应的item,尝试从URL中提取文件名
192
+ // 如果没找到,从URL提取文件名
145
193
  const fileName = e.split('/').pop();
146
194
  image_name_widget.value = fileName;
147
195
  }
148
196
  }
149
197
  }
150
- };
151
198
 
152
- // 监听widget的value变化
153
- const originalSetValue = va_widget.setValue;
154
- if (originalSetValue) {
155
- va_widget.setValue = function(value) {
156
- originalSetValue.call(this, value);
157
-
158
- // 当value变化时,更新image_name_widget
159
- if (image_name_widget && value) {
160
- if (typeof value === 'string') {
161
- const item = image_list.find(item => item.url === value);
162
- if (item) {
163
- image_name_widget.value = item.name;
164
- } else {
165
- const fileName = value.split('/').pop();
166
- image_name_widget.value = fileName;
167
- }
168
- }
169
- }
170
- };
171
- }
199
+ // 调用原始callback
200
+ if (originalVaCallback) {
201
+ originalVaCallback(e);
202
+ }
203
+ };
172
204
  });
173
205
  }
174
206
 
175
207
  // 如果va_widgets没有创建image_name_widget,使用原有逻辑创建
176
208
  if (!image_name_widget && media_widget) {
177
209
  image_name_widget = this.addWidget("combo", "image_name", "", function(e){
178
- const item = image_list.find(item => item.name === e)
179
- const image_url = decodeURIComponent(item.url);
180
- media_widget.value = image_url;
181
- if (media_widget.callback) {
182
- media_widget.callback(e);
210
+ const item = nameToItemMap.get(e);
211
+ if (item) {
212
+ const image_url = decodeURIComponent(item.url);
213
+ media_widget.value = image_url;
214
+ if (media_widget.callback) {
215
+ media_widget.callback(image_url);
216
+ }
183
217
  }
184
218
  }, {
185
219
  serialize: true,
186
220
  values: image_list.map(item => item.name)
187
221
  });
188
222
  }
189
- const val = image_list.find(item => item.url === media_widget.value)?.name || media_widget.value
190
- image_name_widget.label = media_widget.label
191
- image_name_widget.value = val
192
-
193
- const currentIndex = this.widgets.indexOf(image_name_widget);
194
- if (currentIndex > 1) {
195
- this.widgets.splice(currentIndex, 1);
196
- this.widgets.splice(1, 0, image_name_widget);
197
- }
198
- hideWidget(this, media_widget.name)
199
- media_widget.options.values = image_list.map(item => item.name);
200
-
201
- const callback = media_widget.callback
202
- media_widget.callback = async function(e) {
203
- if (typeof e == 'string') {
204
- const item = e.includes('http') ?
205
- image_list.find(item => item.url === e) :
206
- image_list.find(item => item.name === e)
207
-
208
- const image_url = item ? decodeURIComponent(item.url) : e;
209
-
210
- image_name_widget.value = item ? item.name : e;
211
- media_widget.value = image_url;
212
- callback([image_url])
213
- } else {
214
- const item = e[0].split('/')
215
- image_name_widget.options.values.pop()
216
- image_name_widget.options.values.push(item[item.length - 1])
217
- image_name_widget.value = item[item.length - 1]
218
- image_list.push({
219
- name: item[item.length - 1],
220
- url: e[0]
221
- })
222
- callback(e)
223
+
224
+ // 如果进入了va_widgets分支,使用va_widgets中第一个作为media_widget的替代
225
+ const actualMediaWidget = va_widgets.length > 0 ? va_widgets[0] : media_widget;
226
+
227
+ if (image_name_widget && actualMediaWidget) {
228
+ const val = urlToNameMap.get(actualMediaWidget.value) || actualMediaWidget.value
229
+ image_name_widget.label = actualMediaWidget.label
230
+ image_name_widget.value = val
231
+
232
+ const currentIndex = this.widgets.indexOf(image_name_widget);
233
+ if (currentIndex > 1) {
234
+ this.widgets.splice(currentIndex, 1);
235
+ this.widgets.splice(1, 0, image_name_widget);
236
+ }
237
+
238
+ // 如果没有进入va_widgets分支,才隐藏media_widget
239
+ if (va_widgets.length === 0) {
240
+ hideWidget(this, media_widget.name)
241
+ }
242
+
243
+ actualMediaWidget.options.values = image_list.map(item => item.name);
244
+
245
+ // 对于va_widgets的情况,callback已经在上面重写过了,不需要再次重写
246
+ if (va_widgets.length === 0 && media_widget) {
247
+ const callback = media_widget.callback
248
+ media_widget.callback = function(e) {
249
+ if (typeof e == 'string') {
250
+ // 使用 Map 快速查找(O(1)
251
+ const item = e.includes('http') ?
252
+ (urlToNameMap.has(e) ? {url: e, name: urlToNameMap.get(e)} : null) :
253
+ (nameToItemMap ? nameToItemMap.get(e) : null);
254
+
255
+ const image_url = item ? decodeURIComponent(item.url) : e;
256
+
257
+ image_name_widget.value = item ? item.name : e;
258
+ media_widget.value = image_url;
259
+ if (callback) {
260
+ callback([image_url])
261
+ }
262
+ } else {
263
+ const item = e[0].split('/')
264
+ const fileName = item[item.length - 1];
265
+ image_name_widget.options.values.pop()
266
+ image_name_widget.options.values.push(fileName)
267
+ image_name_widget.value = fileName
268
+ image_list.push({
269
+ name: fileName,
270
+ url: e[0]
271
+ })
272
+ // 同步更新 Map
273
+ urlToNameMap.set(e[0], fileName);
274
+ if (nameToItemMap) {
275
+ nameToItemMap.set(fileName, {url: e[0], name: fileName});
276
+ }
277
+ if (callback) {
278
+ callback(e)
279
+ }
280
+ }
281
+ }
223
282
  }
224
283
  }
225
284
  return true
@@ -227,13 +286,24 @@ app.registerExtension({
227
286
  await getData()
228
287
 
229
288
 
230
- function applyWorkflowImageSettings(workflowParams, image_list, media_widget, image_name_widget, currentNodeId) {
289
+ async function applyWorkflowImageSettings(workflowParams, image_list, media_widget, image_name_widget, currentNodeId) {
231
290
  if (workflowParams && workflowParams.nodes) {
232
- // 根据当前节点ID查找对应的节点数据,而不是总是选择第一个
291
+ // 先获取配置,然后将 mediaNodeList 和配置的 keys 合并
292
+ const config = await fetchMediaConfigWithCache();
293
+ const allMediaNodeTypes = new Set(mediaNodeList);
294
+ if (config) {
295
+ // 将配置中的 keys 添加到集合中
296
+ for (const key of Object.keys(config)) {
297
+ allMediaNodeTypes.add(key);
298
+ }
299
+ }
300
+
301
+ // 使用同步的 includes 查找匹配的节点(完全避免循环中的异步)
233
302
  const imageNode = workflowParams.nodes.find(item =>
234
- computeIsLoadNode(item.type) && item.id === currentNodeId
303
+ item.id === currentNodeId && allMediaNodeTypes.has(item.type)
235
304
  )
236
- if (imageNode && imageNode.widgets_values) {
305
+
306
+ if (imageNode && imageNode.widgets_values && imageNode.widgets_values[0]) {
237
307
  const item = imageNode.widgets_values[0].split('/')
238
308
  image_list.push({
239
309
  name: item[item.length - 1],
@@ -250,12 +320,12 @@ app.registerExtension({
250
320
 
251
321
  // 如果有存储的工作流数据,应用图像设置
252
322
  if (window.currentWorkflowData) {
253
- applyWorkflowImageSettings(window.currentWorkflowData, image_list, media_widget, image_name_widget, this.id);
323
+ await applyWorkflowImageSettings(window.currentWorkflowData, image_list, media_widget, image_name_widget, this.id);
254
324
  // 清除存储的数据,避免重复处理
255
325
  delete window.currentWorkflowData;
256
326
  } else {
257
327
  // 原有的调用
258
- applyWorkflowImageSettings(workflowParams, image_list, media_widget, image_name_widget, this.id);
328
+ await applyWorkflowImageSettings(workflowParams, image_list, media_widget, image_name_widget, this.id);
259
329
  }
260
330
  //在这里发个postmessage
261
331
  window.parent.postMessage({
@@ -1,6 +1,6 @@
1
1
  // 主入口文件,导入所有模块
2
2
  import "./disableComfyWebSocket.js";
3
- import "./hookLoadImage.js";
3
+ import "./hookLoadMedia.js";
4
4
  import "./postEvent.js";
5
5
  import "./handleStyle.js";
6
6
  import "./clipspaceToOss.js";
@@ -510,7 +510,9 @@ app.registerExtension({
510
510
 
511
511
  const customErrorStyles = new Map()
512
512
 
513
-
513
+ // 用于节流的时间戳
514
+ let lastRunWorkflowTime = 0;
515
+ const THROTTLE_TIME = 2000; // 2秒
514
516
 
515
517
  // 方法映射
516
518
  const methods = {
@@ -1032,6 +1034,33 @@ app.registerExtension({
1032
1034
  window.saveOriginalNodeColors(params.workflowId);
1033
1035
  }
1034
1036
  };
1037
+
1038
+ // 监听 Ctrl+Enter 快捷键执行工作流
1039
+ document.addEventListener('keydown', (event) => {
1040
+ if (event.ctrlKey && event.key === 'Enter') {
1041
+ event.preventDefault();
1042
+
1043
+ // 节流:检查距离上次执行是否已经过了2秒
1044
+ const now = Date.now();
1045
+ if (now - lastRunWorkflowTime < THROTTLE_TIME) {
1046
+ console.log('触发频率过高,请稍后再试');
1047
+ return;
1048
+ }
1049
+
1050
+ // 更新上次执行时间
1051
+ lastRunWorkflowTime = now;
1052
+
1053
+ if (methods.runWorkflow) {
1054
+ window.parent.postMessage({
1055
+ type: 'functionResult',
1056
+ method: 'ctrlEnter',
1057
+ result: true
1058
+ }, '*');
1059
+ methods.runWorkflow();
1060
+ }
1061
+ }
1062
+ });
1063
+
1035
1064
  window.addEventListener('message', function (event) {
1036
1065
  if (event.data && event.data.type === 'callMethod') {
1037
1066
  const methodName = event.data.method;
@@ -4,7 +4,8 @@ const loadNodeList = [
4
4
  'LoadAudio',
5
5
  'LoadVideo',
6
6
  'Load3D',
7
- 'VHS_LoadVideo'
7
+ 'VHS_LoadVideo',
8
+ 'VHS_LoadAudioUpload'
8
9
  ]
9
10
  const extMap = {
10
11
  'LoadImage': '.png,.jpg,.jpeg,.webp,.gif,.svg,.ico,.bmp,.tiff,.tif,.heic,.heif',
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bizydraft
3
- Version: 0.2.76.dev20251027100756
3
+ Version: 0.2.78.dev20251029103128
4
4
  Summary: bizydraft
5
5
  Requires-Dist: loguru
6
6
  Requires-Dist: aiohttp
@@ -3,7 +3,7 @@ bizydraft/block_nodes.py,sha256=Lqn3oSCaGDHR2OICc8a2iRoRCVVK9v1-9MM3r-qIZgA,1092
3
3
  bizydraft/env.py,sha256=VFmGopVL2TGWA6hwxyFhIglCEcQxy6iVvL_raMNd6u4,407
4
4
  bizydraft/hijack_nodes.py,sha256=riHsp4DhU60E60bfXUl8kVqjs1QvQWmx_FsgQAdWTa4,4197
5
5
  bizydraft/hijack_routes.py,sha256=TVK7zdSIe4wN5dusjnAr1we43K6B_Zup5-dWcBj5rxo,3545
6
- bizydraft/oss_utils.py,sha256=JHpMA61NxFzA053y8IzBc01xxMJCF6G2PTHk-rXqIFo,15590
6
+ bizydraft/oss_utils.py,sha256=XM08pOXGo2jsJlJYmctjL9ZSLUZK8NZuhHCUROXt6GA,16171
7
7
  bizydraft/patch_handlers.py,sha256=JWIEK4yKOPpzs3KLAiBCEYfBohyoVUOdlsVOTt3ULeY,9657
8
8
  bizydraft/postload.py,sha256=XFElKcmCajT_oO7SVJJBaN04XcWro54N5HB5cSCxfvI,1308
9
9
  bizydraft/prestartup_patch.py,sha256=4FGjmRcDHELjtlQOrfTfk2Un5OS89QIqfq-gEcB9WDs,998
@@ -12,22 +12,23 @@ bizydraft/server.py,sha256=L2zoJgOisr65IRphOyko74AdsLel59gh55peyMaUrO8,2102
12
12
  bizydraft/workflow_io.py,sha256=MYhJbpgkv8hrA5k_aolijOTrWpTtu62nzRznA4hv8JE,4298
13
13
  bizydraft/static/js/aiAppHandler.js,sha256=OQRhhoqvc8iZeCvHTtdaD2VTYBGzkeAGdCk1UMO2RZs,17525
14
14
  bizydraft/static/js/clipspaceToOss.js,sha256=brfEPs71Tky5Dnc47UXNEFeFlESDE3kQvUH8ducpIew,14265
15
- bizydraft/static/js/disableComfyWebSocket.js,sha256=nL6DjLUdC2FlAqfYPaFW-dAtkamv01c461W0DUupIKk,1124
15
+ bizydraft/static/js/disableComfyWebSocket.js,sha256=ZDOVlU3v3EvWpMRkD8s_AnO43vuWojVLLjkF2pNEVrA,1957
16
16
  bizydraft/static/js/freezeModeHandler.js,sha256=SjpHD2nYymR-E13B0YcqkA6e4WycZOVI3c48Ts9qvWE,18027
17
- bizydraft/static/js/handleStyle.js,sha256=liIzTu-wnV172g58gHWGLYTfd86xpJxL4A-HuHpFnq4,3616
18
- bizydraft/static/js/hookLoadImage.js,sha256=Vf4uzD8qjSBQjauhS2EBMlYBDG5yuyp5M799foWgv2g,14055
17
+ bizydraft/static/js/handleStyle.js,sha256=YvafKzypgXH8nD-7Fwwf_lXUSU80OWk7D4Emqj-PV88,3700
18
+ bizydraft/static/js/hookLoadMedia.js,sha256=JYKKqapidi6PUIyRhyPhdGNqQW90nnLANk6qnvojIlU,17725
19
19
  bizydraft/static/js/hookLoadModel.js,sha256=TWVmfKp45ta-nE6U5rY3Bv9wiaEBp0QMNt2xaQ3fs9g,6720
20
- bizydraft/static/js/main.js,sha256=PRe4LdsquEQWrZDnVd4ubpVQuD1eDIedAXFFazKdoKQ,188
20
+ bizydraft/static/js/main.js,sha256=-pMeAG8vkKO6yq9zXCeHI78KezGn0r9AhYm65DuZrVI,188
21
21
  bizydraft/static/js/nodeFocusHandler.js,sha256=24xXbS4Q-GjJdRqf11i-1pBo8MkOJ24F7MHFV44EG6Q,4683
22
22
  bizydraft/static/js/nodeParamsFilter.js,sha256=H7lBB0G8HNqoGhOCH1hNXqPU-rPlrFyTxg_f_JgLEMk,4168
23
- bizydraft/static/js/postEvent.js,sha256=o9yA3d3fuIHG7KBXI8GbZA4iGGWaPsw6Ec6N1xB4QsA,42427
23
+ bizydraft/static/js/postEvent.js,sha256=_EyA71m5DwGbhHsuf1amKhpLUBxfBWZaMMxViNhuYzU,43481
24
24
  bizydraft/static/js/socket.js,sha256=VE3fTAgEfM0FZhL526Skt7OCRokOa3mzTCAjAomI_tE,2432
25
- bizydraft/static/js/tool.js,sha256=VupamUuh7tYiDnBTrL5Z_yLmhJinskhzRXwE3zfsKZM,2901
25
+ bizydraft/static/js/tool.js,sha256=2Hhv2J18OaFZRWmHIlseahjd5Ot7ZVxUPb5z46YeXIo,2928
26
26
  bizydraft/static/js/uploadFile.js,sha256=WvglKzHMeOzDhOH3P-fLcPHxCLbKOJpo4DntoRxeJtI,4908
27
27
  bizydraft/static/js/workflow_io.js,sha256=FWAjncvWhvy-3nN_legD2fpRwgnIncpRLHU5X016a-U,5236
28
- bizydraft/static/js/hookLoad/media.js,sha256=5I34YUKBzxOhNQqdIvQrzaNhJcSpEUadAeDC8HsJ2Z4,2614
29
- bizydraft/static/js/hookLoad/model.js,sha256=JNFwnkiEQi_g1JauhjvA6V1XfXr3D5CuRodI4p_B0Ts,10570
30
- bizydraft-0.2.76.dev20251027100756.dist-info/METADATA,sha256=ddd2ywM1bCzBQuPyRQ8tA_QU-QOieBQYILLSQO6ZGeo,180
31
- bizydraft-0.2.76.dev20251027100756.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
32
- bizydraft-0.2.76.dev20251027100756.dist-info/top_level.txt,sha256=XtoBq6hjZhXIM7aas4GtPDtAiKo8FdLzMABXW8qqQ8M,10
33
- bizydraft-0.2.76.dev20251027100756.dist-info/RECORD,,
28
+ bizydraft/static/js/hookLoad/configLoader.js,sha256=R1k0GKdEmv4IIxdT2F-oOI9X9I19ECe77P_1tO3XxgY,2525
29
+ bizydraft/static/js/hookLoad/media.js,sha256=_JhQhXmNiwfCEBtrQLrsIhEzdjL_mdY-9s5bXXjzB7Y,2439
30
+ bizydraft/static/js/hookLoad/model.js,sha256=aHvEPt9k3CcrawHSYnQYcvbtTRwIztk-XDRA3lvgXvA,9890
31
+ bizydraft-0.2.78.dev20251029103128.dist-info/METADATA,sha256=CZUbnuHbXd_6SSMdbqpFqDDBWKXeu64U4R3XiFwJODg,180
32
+ bizydraft-0.2.78.dev20251029103128.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
33
+ bizydraft-0.2.78.dev20251029103128.dist-info/top_level.txt,sha256=XtoBq6hjZhXIM7aas4GtPDtAiKo8FdLzMABXW8qqQ8M,10
34
+ bizydraft-0.2.78.dev20251029103128.dist-info/RECORD,,