bizydraft 0.2.76.dev20251024093830__py3-none-any.whl → 0.2.78.dev20251029061927__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://uat87.bizyair.cn/api/special/comfyagent_node_config?t=' + Math.floor(Date.now() / 60000);
9
-
8
+ 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
+ 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,23 @@ 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
+ console.log('config', config);
74
+ if (config) {
75
+ if (config.hasOwnProperty(nodeName)) {
76
+ return true;
77
+ }
78
+ }
79
+
80
+ return false;
81
+ }
82
+
69
83
  // 启动时后台预取(不阻塞后续逻辑)
70
- try { void fetchMediaConfig(); } catch (e) {}
84
+ 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://uat87.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 } 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,154 @@ 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
+ console.warn(`Cannot redefine property 'value' on widget ${va_widget.name}`);
145
+ return;
146
+ }
147
+
148
+ // 删除现有属性(如果存在)
149
+ if (existingDescriptor) {
150
+ delete va_widget.value;
151
+ }
152
+
153
+ Object.defineProperty(va_widget, 'value', {
154
+ get: function() {
155
+ return _value;
156
+ },
157
+ set: function(newValue) {
158
+ _value = newValue;
159
+
160
+ // 批量更新时跳过监听逻辑
161
+ if (isBatchUpdating) {
162
+ return;
163
+ }
164
+ // 使用 Map 快速查找(O(1))
165
+ const name = urlToNameMap.get(newValue);
166
+ if (name) {
167
+ image_name_widget.value = name;
168
+ } else {
169
+ // 如果没找到,从URL提取文件名
170
+ const fileName = newValue.split('/').pop();
171
+ image_name_widget.value = fileName;
172
+ }
173
+ },
174
+ enumerable: true,
175
+ configurable: true
176
+ });
177
+ });
178
+
179
+
125
180
  // 为每个va_widget重写callback
126
181
  va_widgets.forEach(va_widget => {
127
182
  // 保存va_widget的原始callback
128
183
  const originalVaCallback = va_widget.callback;
129
-
130
184
  // 重写va_widget的callback,当被触发时给image_name_widget赋值
131
185
  va_widget.callback = function(e) {
132
- // 调用原始callback
133
- if (originalVaCallback) {
134
- originalVaCallback(e);
135
- }
136
-
137
- // 给image_name_widget赋值
138
186
  if (image_name_widget) {
139
187
  if (typeof e === 'string') {
140
- const item = image_list.find(item => item.url === e);
141
- if (item) {
142
- image_name_widget.value = item.name;
188
+ // 使用 Map 快速查找(O(1)
189
+ const name = urlToNameMap.get(e);
190
+ if (name) {
191
+ image_name_widget.value = name;
143
192
  } else {
144
- // 如果没找到对应的item,尝试从URL中提取文件名
193
+ // 如果没找到,从URL提取文件名
145
194
  const fileName = e.split('/').pop();
146
195
  image_name_widget.value = fileName;
147
196
  }
148
197
  }
149
198
  }
150
- };
151
199
 
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
- }
200
+ // 调用原始callback
201
+ if (originalVaCallback) {
202
+ originalVaCallback(e);
203
+ }
204
+ };
172
205
  });
173
206
  }
174
207
 
175
208
  // 如果va_widgets没有创建image_name_widget,使用原有逻辑创建
176
209
  if (!image_name_widget && media_widget) {
177
210
  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);
211
+ const item = nameToItemMap.get(e);
212
+ if (item) {
213
+ const image_url = decodeURIComponent(item.url);
214
+ media_widget.value = image_url;
215
+ if (media_widget.callback) {
216
+ media_widget.callback(image_url);
217
+ }
183
218
  }
184
219
  }, {
185
220
  serialize: true,
186
221
  values: image_list.map(item => item.name)
187
222
  });
188
223
  }
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)
224
+
225
+ // 如果进入了va_widgets分支,使用va_widgets中第一个作为media_widget的替代
226
+ const actualMediaWidget = va_widgets.length > 0 ? va_widgets[0] : media_widget;
227
+
228
+ if (image_name_widget && actualMediaWidget) {
229
+ const val = urlToNameMap.get(actualMediaWidget.value) || actualMediaWidget.value
230
+ image_name_widget.label = actualMediaWidget.label
231
+ image_name_widget.value = val
232
+
233
+ const currentIndex = this.widgets.indexOf(image_name_widget);
234
+ if (currentIndex > 1) {
235
+ this.widgets.splice(currentIndex, 1);
236
+ this.widgets.splice(1, 0, image_name_widget);
237
+ }
238
+
239
+ // 如果没有进入va_widgets分支,才隐藏media_widget
240
+ if (va_widgets.length === 0) {
241
+ hideWidget(this, media_widget.name)
242
+ }
243
+
244
+ actualMediaWidget.options.values = image_list.map(item => item.name);
245
+
246
+ // 对于va_widgets的情况,callback已经在上面重写过了,不需要再次重写
247
+ if (va_widgets.length === 0 && media_widget) {
248
+ const callback = media_widget.callback
249
+ media_widget.callback = function(e) {
250
+ if (typeof e == 'string') {
251
+ // 使用 Map 快速查找(O(1)
252
+ const item = e.includes('http') ?
253
+ (urlToNameMap.has(e) ? {url: e, name: urlToNameMap.get(e)} : null) :
254
+ (nameToItemMap ? nameToItemMap.get(e) : null);
255
+
256
+ const image_url = item ? decodeURIComponent(item.url) : e;
257
+
258
+ image_name_widget.value = item ? item.name : e;
259
+ media_widget.value = image_url;
260
+ if (callback) {
261
+ callback([image_url])
262
+ }
263
+ } else {
264
+ const item = e[0].split('/')
265
+ const fileName = item[item.length - 1];
266
+ image_name_widget.options.values.pop()
267
+ image_name_widget.options.values.push(fileName)
268
+ image_name_widget.value = fileName
269
+ image_list.push({
270
+ name: fileName,
271
+ url: e[0]
272
+ })
273
+ // 同步更新 Map
274
+ urlToNameMap.set(e[0], fileName);
275
+ if (nameToItemMap) {
276
+ nameToItemMap.set(fileName, {url: e[0], name: fileName});
277
+ }
278
+ if (callback) {
279
+ callback(e)
280
+ }
281
+ }
282
+ }
223
283
  }
224
284
  }
225
285
  return true
@@ -230,10 +290,10 @@ app.registerExtension({
230
290
  function applyWorkflowImageSettings(workflowParams, image_list, media_widget, image_name_widget, currentNodeId) {
231
291
  if (workflowParams && workflowParams.nodes) {
232
292
  // 根据当前节点ID查找对应的节点数据,而不是总是选择第一个
233
- const imageNode = workflowParams.nodes.find(item =>
234
- computeIsLoadNode(item.type) && item.id === currentNodeId
293
+ const imageNode = workflowParams.nodes.find(async item =>
294
+ await computeIsMediaNode(item.type) && item.id === currentNodeId
235
295
  )
236
- if (imageNode && imageNode.widgets_values) {
296
+ if (imageNode && imageNode.widgets_values && imageNode.widgets_values[0]) {
237
297
  const item = imageNode.widgets_values[0].split('/')
238
298
  image_list.push({
239
299
  name: item[item.length - 1],
@@ -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.dev20251024093830
3
+ Version: 0.2.78.dev20251029061927
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/hookLoadImage.js,sha256=R1HUNn-kml-e_NczEH2egVerydhHDoTNCNePN9yC-Tk,17244
19
19
  bizydraft/static/js/hookLoadModel.js,sha256=TWVmfKp45ta-nE6U5rY3Bv9wiaEBp0QMNt2xaQ3fs9g,6720
20
20
  bizydraft/static/js/main.js,sha256=PRe4LdsquEQWrZDnVd4ubpVQuD1eDIedAXFFazKdoKQ,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=BD9RJxTMGO8wagOI9IZNYzKfsOnpyl0JtEUghpURpaI,2620
29
- bizydraft/static/js/hookLoad/model.js,sha256=C7Hi6BkdtEAgNCwpT71ZRgv9BaYKBsyNu8wt9h5hYvk,10576
30
- bizydraft-0.2.76.dev20251024093830.dist-info/METADATA,sha256=dOZIzq3t2lIWw12pZh7FfhnXf0wBT9QLyrQkDjrpNJA,180
31
- bizydraft-0.2.76.dev20251024093830.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
32
- bizydraft-0.2.76.dev20251024093830.dist-info/top_level.txt,sha256=XtoBq6hjZhXIM7aas4GtPDtAiKo8FdLzMABXW8qqQ8M,10
33
- bizydraft-0.2.76.dev20251024093830.dist-info/RECORD,,
28
+ bizydraft/static/js/hookLoad/configLoader.js,sha256=R1k0GKdEmv4IIxdT2F-oOI9X9I19ECe77P_1tO3XxgY,2525
29
+ bizydraft/static/js/hookLoad/media.js,sha256=uL31qpH_Rv1oOieUQO3bMHqHEXCQ7o11Id5U43td6io,2460
30
+ bizydraft/static/js/hookLoad/model.js,sha256=aHvEPt9k3CcrawHSYnQYcvbtTRwIztk-XDRA3lvgXvA,9890
31
+ bizydraft-0.2.78.dev20251029061927.dist-info/METADATA,sha256=hf6fs-Oqq0ElZJIf8nCkJgly_Hkd315X1_h028psf8Y,180
32
+ bizydraft-0.2.78.dev20251029061927.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
33
+ bizydraft-0.2.78.dev20251029061927.dist-info/top_level.txt,sha256=XtoBq6hjZhXIM7aas4GtPDtAiKo8FdLzMABXW8qqQ8M,10
34
+ bizydraft-0.2.78.dev20251029061927.dist-info/RECORD,,