bizydraft 0.2.78.dev20251030100819__py3-none-any.whl → 0.2.82.dev20251209023307__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.
@@ -1,8 +1,23 @@
1
1
  import { app } from "../../scripts/app.js";
2
- import { getCookie, computeExt, hideWidget } from './tool.js';
3
- import { getMediaNodeConfig, getMediaInputKeys, possibleMediaWidgetNames, computeIsMediaNode, mediaNodeList, fetchMediaConfigWithCache } from './hookLoad/media.js';
4
-
5
-
2
+ import { hideWidget } from './tool.js';
3
+ import {
4
+ getMediaNodeConfig,
5
+ getMediaInputKeys,
6
+ computeIsMediaNode,
7
+ mediaNodeList,
8
+ fetchMediaConfigWithCache,
9
+ findMediaWidget,
10
+ findMediaWidgets,
11
+ updateWidgetsOptions,
12
+ handleNewUploadedFile,
13
+ extractUrlFromInput,
14
+ initMaps,
15
+ createImageNameWidgetCallbackForMediaWidget,
16
+ createMediaWidgetCallback,
17
+ setupVaWidgets,
18
+ applyWorkflowImageSettings,
19
+ fetchImageList
20
+ } from './hookLoad/media.js';
6
21
 
7
22
  app.registerExtension({
8
23
  name: "bizyair.image.to.oss",
@@ -33,307 +48,99 @@ app.registerExtension({
33
48
  });
34
49
  })
35
50
  nodeType.prototype.onNodeCreated = async function() {
36
- if (await computeIsMediaNode(nodeData.name)) {
37
- const apiHost = 'https://bizyair.cn/api'
38
- // 优先使用 API 的媒体输入键匹配到具体的 widget;若未命中则回退到原有字段集合
39
- let media_widget = null;
40
- const mediaNodeConfig = await getMediaNodeConfig(nodeData.name);
41
- const apiInputKeys = getMediaInputKeys(mediaNodeConfig);
42
- if (apiInputKeys && apiInputKeys.length > 0) {
43
- for (const key of apiInputKeys) {
44
- const w = this.widgets.find(x => x.name === key);
45
- if (w) { media_widget = w; break; }
46
- }
47
- }
48
- if (!media_widget) {
49
- media_widget = this.widgets.find(w => {
50
- return possibleMediaWidgetNames.includes(w.name);
51
- });
52
- }
53
- // 查找所有name等于接口配置中inputs下的字段的widget(如video、audio等)
54
- let va_widgets = [];
55
- if (apiInputKeys && apiInputKeys.length > 0) {
56
- for (const key of apiInputKeys) {
57
- const w = this.widgets.find(x => x.name === key);
58
- if (w) {
59
- va_widgets.push(w);
60
- }
61
- }
62
- }
63
-
64
- // 如果API配置没有找到,使用回退逻辑查找常见的媒体widget
65
- if (va_widgets.length === 0) {
66
- for (const widgetName of possibleMediaWidgetNames) {
67
- const w = this.widgets.find(x => x.name === widgetName);
68
- if (w) {
69
- va_widgets.push(w);
70
- }
71
- }
72
- }
73
- let image_name_widget = this.widgets.find(w => w.name === 'image_name');
74
- let image_list = []
75
- const getData = async () => {
76
- const res = await fetch(`${apiHost}/special/community/commit_input_resource?${
77
- new URLSearchParams({
78
- ext: computeExt(nodeData.name),
79
- current: 1,
80
- page_size: 100
81
-
82
- }).toString()
83
- }`, {
84
- method: 'GET',
85
- headers: {
86
- 'Content-Type': 'application/json',
87
- 'Authorization': `Bearer ${getCookie('bizy_token')}`
88
- }
89
- })
90
- const {data} = await res.json()
91
- const list = (data && data.data && data.data.data && data.data.data.list) || []
92
- image_list = list.filter(item => item.name).map(item => {
93
- return {
94
- url: item.url,
95
- id: item.id,
96
- name: item.name
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
-
108
- // 如果找到va_widgets,处理它们
109
- if (va_widgets.length > 0) {
110
- // 标志位:防止批量更新时触发监听
111
- let isBatchUpdating = false;
112
-
113
- // 创建image_name_widget来替代显示
114
- if (!image_name_widget) {
115
- image_name_widget = this.addWidget("combo", "image_name", "", function(e){
116
- const item = nameToItemMap.get(e);
117
- if (item) {
118
- const image_url = decodeURIComponent(item.url);
119
- // 批量更新时跳过监听
120
- isBatchUpdating = true;
121
- va_widgets.forEach(va_widget => {
122
- va_widget.value = image_url;
123
- if (va_widget.callback) {
124
- va_widget.callback(image_url);
125
- }
126
- });
127
- isBatchUpdating = false;
128
- }
129
- }, {
130
- serialize: true,
131
- values: image_list.map(item => item.name)
132
- });
133
- }
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
- }
51
+ if (!(await computeIsMediaNode(nodeData.name))) {
52
+ return;
53
+ }
146
54
 
147
- // 删除现有属性(如果存在)
148
- if (existingDescriptor) {
149
- delete va_widget.value;
150
- }
55
+ const apiHost = 'https://bizyair.cn/api'
56
+ // const apiHost = 'http://localhost:3000/api'
151
57
 
152
- Object.defineProperty(va_widget, 'value', {
153
- get: function() {
154
- return _value;
155
- },
156
- set: function(newValue) {
157
- _value = newValue;
58
+ // 获取 API 配置
59
+ const mediaNodeConfig = await getMediaNodeConfig(nodeData.name);
60
+ const apiInputKeys = getMediaInputKeys(mediaNodeConfig);
158
61
 
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
- });
62
+ // 查找媒体 widget
63
+ const media_widget = findMediaWidget(this.widgets, apiInputKeys);
64
+ const va_widgets = findMediaWidgets(this.widgets, apiInputKeys);
65
+ let image_name_widget = this.widgets.find(w => w.name === 'image_name');
177
66
 
67
+ // 获取文件列表
68
+ const image_list = await fetchImageList(apiHost, nodeData.name);
178
69
 
179
- // 为每个va_widget重写callback
180
- va_widgets.forEach(va_widget => {
181
- // 保存va_widget的原始callback
182
- const originalVaCallback = va_widget.callback;
183
- // 重写va_widget的callback,当被触发时给image_name_widget赋值
184
- va_widget.callback = function(e) {
185
- if (image_name_widget) {
186
- if (typeof e === 'string') {
187
- // 使用 Map 快速查找(O(1))
188
- const name = urlToNameMap.get(e);
189
- if (name) {
190
- image_name_widget.value = name;
191
- } else {
192
- // 如果没找到,从URL提取文件名
193
- const fileName = e.split('/').pop();
194
- image_name_widget.value = fileName;
195
- }
196
- }
197
- }
70
+ // 初始化 Map 映射
71
+ const { urlToNameMap, nameToItemMap } = initMaps(image_list);
198
72
 
199
- // 调用原始callback
200
- if (originalVaCallback) {
201
- originalVaCallback(e);
202
- }
203
- };
204
- });
205
- }
73
+ // 如果找到va_widgets,处理它们
74
+ if (va_widgets.length > 0) {
75
+ image_name_widget = setupVaWidgets(this, va_widgets, image_list, urlToNameMap, nameToItemMap, image_name_widget, va_widgets[0]);
76
+ }
206
77
 
207
- // 如果va_widgets没有创建image_name_widget,使用原有逻辑创建
208
- if (!image_name_widget && media_widget) {
209
- image_name_widget = this.addWidget("combo", "image_name", "", function(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
- }
217
- }
218
- }, {
219
- serialize: true,
220
- values: image_list.map(item => item.name)
221
- });
78
+ // 如果va_widgets没有创建image_name_widget,使用原有逻辑创建
79
+ if (!image_name_widget && media_widget) {
80
+ image_name_widget = this.addWidget("combo", "image_name", "",
81
+ createImageNameWidgetCallbackForMediaWidget(nameToItemMap, media_widget),
82
+ {
83
+ serialize: true,
84
+ values: image_list.map(item => item.name)
222
85
  }
86
+ );
87
+ }
223
88
 
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);
89
+ // 如果进入了va_widgets分支,使用va_widgets中第一个作为media_widget的替代
90
+ const actualMediaWidget = va_widgets.length > 0 ? va_widgets[0] : media_widget;
254
91
 
255
- const image_url = item ? decodeURIComponent(item.url) : e;
92
+ if (image_name_widget && actualMediaWidget) {
93
+ const val = urlToNameMap.get(actualMediaWidget.value) || actualMediaWidget.value;
94
+ image_name_widget.label = actualMediaWidget.label;
95
+ image_name_widget.value = val;
256
96
 
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
- }
282
- }
283
- }
284
- return true
97
+ // 调整 widget 顺序
98
+ const currentIndex = this.widgets.indexOf(image_name_widget);
99
+ if (currentIndex > 1) {
100
+ this.widgets.splice(currentIndex, 1);
101
+ this.widgets.splice(1, 0, image_name_widget);
285
102
  }
286
- await getData()
287
-
288
-
289
- async function applyWorkflowImageSettings(workflowParams, image_list, media_widget, image_name_widget, currentNodeId) {
290
- if (workflowParams && workflowParams.nodes) {
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 查找匹配的节点(完全避免循环中的异步)
302
- const imageNode = workflowParams.nodes.find(item =>
303
- item.id === currentNodeId && allMediaNodeTypes.has(item.type)
304
- )
305
103
 
306
- if (imageNode && imageNode.widgets_values && imageNode.widgets_values[0]) {
307
- const item = imageNode.widgets_values[0].split('/')
308
- image_list.push({
309
- name: item[item.length - 1],
310
- url: imageNode.widgets_values[0]
311
- })
312
- media_widget.value = imageNode.widgets_values[0]
104
+ // 如果没有进入va_widgets分支,才隐藏media_widget
105
+ if (va_widgets.length === 0) {
106
+ hideWidget(this, media_widget.name);
107
+ }
313
108
 
314
- media_widget.options.values = image_list.map(item => item.url)
315
- image_name_widget.options.values = image_list.map(item => item.name)
316
- media_widget.callback(imageNode.widgets_values[0])
317
- }
318
- }
109
+ // 更新 widget 选项列表
110
+ updateWidgetsOptions([actualMediaWidget], image_list);
111
+
112
+ // 对于va_widgets的情况,callback已经在上面重写过了,不需要再次重写
113
+ if (va_widgets.length === 0 && media_widget) {
114
+ const callback = media_widget.callback;
115
+ media_widget.callback = createMediaWidgetCallback(
116
+ media_widget,
117
+ urlToNameMap,
118
+ nameToItemMap,
119
+ image_list,
120
+ image_name_widget,
121
+ actualMediaWidget,
122
+ callback
123
+ );
319
124
  }
125
+ }
320
126
 
321
- // 如果有存储的工作流数据,应用图像设置
127
+ // 应用工作流设置
128
+ const workflowData = window.currentWorkflowData || workflowParams;
129
+ if (workflowData) {
130
+ await applyWorkflowImageSettings(workflowData, image_list, media_widget, image_name_widget, this.id, va_widgets, actualMediaWidget);
322
131
  if (window.currentWorkflowData) {
323
- await applyWorkflowImageSettings(window.currentWorkflowData, image_list, media_widget, image_name_widget, this.id);
324
- // 清除存储的数据,避免重复处理
325
132
  delete window.currentWorkflowData;
326
- } else {
327
- // 原有的调用
328
- await applyWorkflowImageSettings(workflowParams, image_list, media_widget, image_name_widget, this.id);
329
133
  }
330
- //在这里发个postmessage
331
- window.parent.postMessage({
332
- type: 'functionResult',
333
- method: 'hookLoadImageCompleted',
334
- params: {}
335
- }, '*');
134
+ } else {
135
+ await applyWorkflowImageSettings(workflowParams, image_list, media_widget, image_name_widget, this.id, va_widgets, actualMediaWidget);
336
136
  }
137
+
138
+ // 发送完成消息
139
+ window.parent.postMessage({
140
+ type: 'functionResult',
141
+ method: 'hookLoadImageCompleted',
142
+ params: {}
143
+ }, '*');
337
144
  }
338
145
  }
339
146
  })
@@ -29,9 +29,9 @@ app.registerExtension({
29
29
 
30
30
  // 异步获取节点配置
31
31
  const onNodeCreated = nodeType.prototype.onNodeCreated;
32
- nodeType.prototype.onNodeCreated = async function() {
33
- const nodeConfig = await getNodeConfig(nodeData.name);
34
- if (nodeConfig) {
32
+ const nodeConfig = await getNodeConfig(nodeData.name);
33
+ if (nodeConfig) {
34
+ nodeType.prototype.onNodeCreated = function() {
35
35
  try {
36
36
  const inputs = nodeConfig.config.inputs;
37
37
  const inputKeys = Object.keys(inputs);
@@ -140,5 +140,51 @@ app.registerExtension({
140
140
  if (nodeConfig) {
141
141
  setupNodeMouseBehavior(node, nodeConfig);
142
142
  }
143
+ },
144
+ async setup() {
145
+ const app = document.querySelector('#vue-app').__vue_app__
146
+ const pinia = app.config.globalProperties.$pinia
147
+ const settingStore = pinia._s.get('setting')
148
+ await settingStore.set('Comfy.Workflow.ShowMissingModelsWarning', false)
149
+
150
+
151
+
152
+
153
+ // 获取 toastStore
154
+ const toastStore = pinia._s.get('toast');
155
+
156
+ // 保存原始的 add 方法
157
+ const originalAdd = toastStore.add;
158
+
159
+ // 重写 add 方法,添加过滤逻辑
160
+ toastStore.add = function(message) {
161
+ const detail = (message.detail || '').toLowerCase();
162
+ const summary = (message.summary || '').toLowerCase();
163
+ const text = `${summary} ${detail}`;
164
+
165
+ // 检查是否包含阻止的关键词
166
+ const blockedKeywords = [
167
+ 'missing dependencies',
168
+ 'comfyui logs',
169
+ 'comfyullogs',
170
+ 'refer to the comfyui',
171
+ 'you may be missing'
172
+ ];
173
+
174
+ const shouldBlock = blockedKeywords.some(keyword =>
175
+ text.includes(keyword.toLowerCase())
176
+ );
177
+
178
+ // 如果包含阻止的关键词,则不添加 toast
179
+ if (shouldBlock) {
180
+ console.log('Blocked toast:', message);
181
+ return;
182
+ }
183
+
184
+ // 否则正常添加
185
+ return originalAdd.call(this, message);
186
+ };
187
+
188
+ console.log('Toast blocker activated!');
143
189
  }
144
190
  })
@@ -0,0 +1,146 @@
1
+ import { app } from "../../../scripts/app.js";
2
+
3
+ // 简单的链式callback实现
4
+ function chainCallback(originalCallback, newCallback) {
5
+ return function (...args) {
6
+ if (originalCallback) {
7
+ originalCallback.apply(this, args);
8
+ }
9
+ newCallback.apply(this, args);
10
+ };
11
+ }
12
+
13
+ // 统一的函数:初始化动态输入相关的 widgets
14
+ function initializeDynamicInputs(node) {
15
+ // 只处理目标节点类型
16
+ if (
17
+ node.type !== "BizyAir_Seedream4_5" &&
18
+ node.type !== "BizyAir_NanoBananaPro" &&
19
+ node.type !== "BizyAir_NanoBananaProOfficial"
20
+ ) {
21
+ return;
22
+ }
23
+
24
+ node._type = "IMAGE";
25
+
26
+ if (!node.inputs) {
27
+ node.inputs = [];
28
+ }
29
+
30
+ // 先检查是否存在 inputcount widget
31
+ let inputCountWidget = node.widgets?.find(
32
+ (w) => w.name === "inputcount"
33
+ );
34
+
35
+ // 如果不存在,先创建 inputcount widget(在按钮之前创建,确保显示顺序)
36
+ if (!inputCountWidget) {
37
+ // 计算当前 IMAGE 类型输入数量
38
+ const currentImageInputs = node.inputs.filter(
39
+ (input) => input.type === node._type
40
+ ).length;
41
+ // 创建 inputcount widget,默认值为当前输入数量或1
42
+ // 使用 precision: 0 确保是整数类型
43
+ inputCountWidget = node.addWidget(
44
+ "number",
45
+ "inputcount",
46
+ currentImageInputs || 1,
47
+ null,
48
+ {
49
+ min: 1,
50
+ max: 10,
51
+ step: 10, // 旧参数(兼容性),是 step2 的 10 倍
52
+ step2: 1, // 新参数,实际的步长
53
+ precision: 0, // 设置为 0 表示整数
54
+ }
55
+ );
56
+ }
57
+
58
+ // 检查是否已经存在 "Update inputs" 按钮,避免重复添加
59
+ const updateButton = node.widgets?.find(
60
+ (w) => w.name === "Update inputs"
61
+ );
62
+ if (updateButton) {
63
+ return; // 已经存在,不需要重复添加
64
+ }
65
+
66
+ // 然后添加 "Update inputs" 按钮(会在 inputcount 之后显示)
67
+ node.addWidget("button", "Update inputs", null, () => {
68
+ if (!node.inputs) {
69
+ node.inputs = [];
70
+ }
71
+
72
+ // 查找 inputcount widget
73
+ const inputCountWidget = node.widgets?.find(
74
+ (w) => w.name === "inputcount"
75
+ );
76
+
77
+ // 如果仍然找不到,创建它
78
+ if (!inputCountWidget) {
79
+ const currentImageInputs = node.inputs.filter(
80
+ (input) => input.type === node._type
81
+ ).length;
82
+ node.addWidget(
83
+ "number",
84
+ "inputcount",
85
+ currentImageInputs || 1,
86
+ null,
87
+ {
88
+ min: 1,
89
+ max: 10,
90
+ step: 10, // 旧参数(兼容性),是 step2 的 10 倍
91
+ step2: 1, // 新参数,实际的步长
92
+ precision: 0, // 设置为 0 表示整数
93
+ }
94
+ );
95
+ return;
96
+ }
97
+
98
+ const target_number_of_inputs = inputCountWidget.value || 1;
99
+ const num_inputs = node.inputs.filter(
100
+ (input) => input.type === node._type
101
+ ).length;
102
+
103
+ if (target_number_of_inputs === num_inputs) {
104
+ return; // already set, do nothing
105
+ }
106
+
107
+ if (target_number_of_inputs < num_inputs) {
108
+ const inputs_to_remove = num_inputs - target_number_of_inputs;
109
+ for (let i = 0; i < inputs_to_remove; i++) {
110
+ node.removeInput(node.inputs.length - 1);
111
+ }
112
+ } else {
113
+ for (let i = num_inputs + 1; i <= target_number_of_inputs; ++i) {
114
+ node.addInput(`image_${i}`, node._type, { shape: 7 });
115
+ }
116
+ }
117
+ });
118
+ }
119
+
120
+ app.registerExtension({
121
+ name: "bizyair.dynamicImageInputs",
122
+ async beforeRegisterNodeDef(nodeType, nodeData, app) {
123
+ // 只处理目标节点类型
124
+ if (
125
+ nodeData.name !== "BizyAir_Seedream4_5" &&
126
+ nodeData.name !== "BizyAir_NanoBananaPro" &&
127
+ nodeData.name !== "BizyAir_NanoBananaProOfficial"
128
+ ) {
129
+ return;
130
+ }
131
+
132
+ // 使用 chainCallback 设置 onConfigure,确保在节点配置完成后也初始化 widgets
133
+ const originalOnConfigure = nodeType.prototype.onConfigure;
134
+ nodeType.prototype.onConfigure = chainCallback(
135
+ originalOnConfigure,
136
+ function (info) {
137
+ // 在 configure 完成后初始化 widgets
138
+ initializeDynamicInputs(this);
139
+ }
140
+ );
141
+ },
142
+ nodeCreated(node) {
143
+ // 在节点创建时也初始化 widgets(用于首次创建节点时)
144
+ initializeDynamicInputs(node);
145
+ },
146
+ });