bizydraft 0.2.73.dev20251015100429__py3-none-any.whl → 0.2.75.dev20251022024104__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/hijack_nodes.py CHANGED
@@ -20,34 +20,42 @@ class BizyDraftLoadVideo(LoadVideo):
20
20
  super().__init__(*args, **kwargs)
21
21
 
22
22
  @classmethod
23
- def INPUT_TYPES(cls):
24
- return {
25
- "required": {"file": (["choose your file"], {"video_upload": True})},
26
- }
23
+ def INPUT_TYPES(cls, **kwargs):
24
+ # 调用父类方法,保持兼容性
25
+ return super().INPUT_TYPES(**kwargs)
27
26
 
28
27
  @classmethod
29
28
  def VALIDATE_INPUTS(s, *args, **kwargs):
30
29
  return True
31
30
 
31
+ @classmethod
32
+ def validate_inputs(s, *args, **kwargs):
33
+ # V3 API 使用小写的 validate_inputs
34
+ return True
35
+
32
36
 
33
37
  class BizyDraftLoadImage(LoadImage):
34
38
  def __init__(self, *args, **kwargs):
35
39
  super().__init__(*args, **kwargs)
36
40
 
37
41
  @classmethod
38
- def INPUT_TYPES(s):
39
- return {
40
- "required": {"image": (["choose your file"], {"image_upload": True})},
41
- }
42
+ def INPUT_TYPES(cls, **kwargs):
43
+ # 调用父类方法,保持兼容性
44
+ return super().INPUT_TYPES(**kwargs)
42
45
 
43
46
  @classmethod
44
47
  def VALIDATE_INPUTS(s, *args, **kwargs):
45
48
  return True
46
49
 
50
+ @classmethod
51
+ def validate_inputs(s, *args, **kwargs):
52
+ # V3 API 使用小写的 validate_inputs
53
+ return True
54
+
47
55
 
48
56
  CLASS_PATCHES = {
49
- # "LoadImage": BizyDraftLoadImage,
50
- # "LoadVideo": BizyDraftLoadVideo,
57
+ # "LoadImage": BizyDraftLoadImage,
58
+ # "LoadVideo": BizyDraftLoadVideo,
51
59
  }
52
60
 
53
61
 
@@ -62,7 +70,12 @@ def get_data_load_classes_from_url(config_url):
62
70
  response = requests.get(config_url)
63
71
  response.raise_for_status()
64
72
  data = response.json()
65
- keys_list = list(data["weight_load_nodes"].keys())
73
+ keys_list = []
74
+ if "weight_load_nodes" in data:
75
+ keys_list.extend(list(data["weight_load_nodes"].keys()))
76
+ if "media_load_nodes" in data:
77
+ keys_list.extend(list(data["media_load_nodes"].keys()))
78
+
66
79
  return keys_list
67
80
  except Exception as e:
68
81
  logger.error(
@@ -104,6 +117,7 @@ def hijack_nodes():
104
117
 
105
118
  # 通用情况,正则匹配后,打通用patch、替换
106
119
  for node_name, base_class in NODE_CLASS_MAPPINGS.items():
120
+
107
121
  regex = r"^(?!BizyAir_)\w+.*Loader.*"
108
122
  match = re.match(regex, node_name, re.IGNORECASE)
109
123
  if (match and (node_name not in CLASS_PATCHES)) or (
@@ -116,7 +130,10 @@ def hijack_nodes():
116
130
 
117
131
  def create_patched_class(base_class, validate_inputs_func=None):
118
132
  class PatchedClass(base_class):
119
- pass
133
+ @classmethod
134
+ def validate_inputs(cls, *args, **kwargs):
135
+ # V3 API
136
+ return True
120
137
 
121
138
  if validate_inputs_func:
122
139
  PatchedClass.VALIDATE_INPUTS = classmethod(validate_inputs_func)
@@ -2,7 +2,7 @@ from aiohttp import web
2
2
  from loguru import logger
3
3
 
4
4
  from bizydraft.oss_utils import upload_image, upload_mask
5
- from bizydraft.patch_handlers import post_prompt, view_image
5
+ from bizydraft.patch_handlers import post_prompt, view_image, view_video
6
6
 
7
7
  try:
8
8
  from server import PromptServer
@@ -29,6 +29,8 @@ def hijack_routes_pre_add_routes():
29
29
  ("/api/prompt", "POST"): post_prompt,
30
30
  ("/api/upload/image", "POST"): upload_image,
31
31
  ("/api/upload/mask", "POST"): upload_mask,
32
+ # VHS plugin support
33
+ ("/api/vhs/viewvideo", "GET"): view_video,
32
34
  }
33
35
 
34
36
  async def middleware_handler(request):
@@ -55,6 +57,7 @@ def hijack_routes_pre_add_routes():
55
57
  "/view",
56
58
  "/upload/image",
57
59
  "/upload/mask",
60
+ "/vhs/viewvideo",
58
61
  "/",
59
62
  "/ws",
60
63
  "/extensions",
@@ -51,7 +51,11 @@ async def view_image(request):
51
51
  try:
52
52
  if "http" in subfolder:
53
53
  subfolder = subfolder[subfolder.find("http") :]
54
- subfolder = unquote(subfolder).replace("https:/", "https://")
54
+ subfolder = unquote(subfolder)
55
+ if "https:/" in subfolder and not subfolder.startswith("https://"):
56
+ subfolder = subfolder.replace("https:/", "https://", 1)
57
+ if "http:/" in subfolder and not subfolder.startswith("http://"):
58
+ subfolder = subfolder.replace("http:/", "http://", 1)
55
59
  filename = (
56
60
  f"{subfolder}/{filename}"
57
61
  if not filename.startswith(http_prefix_options)
@@ -124,6 +128,84 @@ def human_readable_size(size_bytes):
124
128
  return f"{s} {size_name[i]}"
125
129
 
126
130
 
131
+ async def view_video(request):
132
+ """处理VHS插件的viewvideo接口,支持从OSS URL加载视频"""
133
+ logger.debug(
134
+ f"Received request for /vhs/viewvideo with query: {request.rel_url.query}"
135
+ )
136
+
137
+ if "filename" not in request.rel_url.query:
138
+ logger.warning("'filename' not provided in query string, returning 404")
139
+ return web.Response(status=404, text="'filename' not provided in query string")
140
+
141
+ # VHS插件的filename参数本身就是完整的URL(可能是URL编码的)
142
+ filename = unquote(request.rel_url.query["filename"])
143
+
144
+ http_prefix_options = ("http:", "https:")
145
+
146
+ if not filename.startswith(http_prefix_options):
147
+ logger.warning(f"Invalid filename format: {filename=}, only URLs are supported")
148
+ return web.Response(
149
+ status=400, text="Invalid filename format(only url supported)"
150
+ )
151
+
152
+ try:
153
+ content_type, _ = mimetypes.guess_type(filename)
154
+
155
+ timeout = ClientTimeout(total=BIZYDRAFT_REQUEST_TIMEOUT)
156
+ async with ClientSession(timeout=timeout) as session:
157
+ async with session.get(filename) as resp:
158
+ resp.raise_for_status()
159
+
160
+ # 优先使用服务器返回的Content-Type
161
+ final_content_type = (
162
+ resp.headers.get("Content-Type")
163
+ or content_type
164
+ or "application/octet-stream"
165
+ )
166
+
167
+ content_length = int(resp.headers.get("Content-Length", 0))
168
+ if content_length > BIZYDRAFT_MAX_FILE_SIZE:
169
+ logger.warning(
170
+ f"File size {human_readable_size(content_length)} exceeds limit {human_readable_size(BIZYDRAFT_MAX_FILE_SIZE)}"
171
+ )
172
+ return web.Response(
173
+ status=413,
174
+ text=f"File size exceeds limit ({human_readable_size(BIZYDRAFT_MAX_FILE_SIZE)})",
175
+ )
176
+
177
+ headers = {
178
+ "Content-Disposition": f'attachment; filename="{uuid.uuid4()}"',
179
+ "Content-Type": final_content_type,
180
+ }
181
+
182
+ proxy_response = web.StreamResponse(headers=headers)
183
+ await proxy_response.prepare(request)
184
+
185
+ total_bytes = 0
186
+ async for chunk in resp.content.iter_chunked(BIZYDRAFT_CHUNK_SIZE):
187
+ total_bytes += len(chunk)
188
+ if total_bytes > BIZYDRAFT_MAX_FILE_SIZE:
189
+ await proxy_response.write(b"")
190
+ return web.Response(
191
+ status=413,
192
+ text=f"File size exceeds limit during streaming ({human_readable_size(BIZYDRAFT_MAX_FILE_SIZE)})",
193
+ )
194
+ await proxy_response.write(chunk)
195
+
196
+ return proxy_response
197
+
198
+ except asyncio.TimeoutError:
199
+ return web.Response(
200
+ status=504,
201
+ text=f"Request timed out (max {BIZYDRAFT_REQUEST_TIMEOUT//60} minutes)",
202
+ )
203
+ except Exception as e:
204
+ return web.Response(
205
+ status=502, text=f"Failed to fetch remote resource: {str(e)}"
206
+ )
207
+
208
+
127
209
  async def post_prompt(request):
128
210
  json_data = await request.json()
129
211
 
@@ -1,10 +1,13 @@
1
1
  import { app } from "../../scripts/app.js";
2
2
  import { getCookie, computeIsLoadNode, computeExt, hideWidget } from './tool.js';
3
+ import { getMediaNodeConfig, getMediaInputKeys, possibleMediaWidgetNames } from './hookLoad/media.js';
3
4
 
4
5
 
6
+ console.log('hookLoadImage.js 已加载');
5
7
  app.registerExtension({
6
8
  name: "bizyair.image.to.oss",
7
9
  async beforeRegisterNodeDef(nodeType, nodeData) {
10
+ console.log('beforeRegisterNodeDef 被调用,节点:', nodeData.name);
8
11
  let workflowParams = null
9
12
  document.addEventListener('workflowLoaded', (event) => {
10
13
  workflowParams = event.detail;
@@ -30,15 +33,51 @@ app.registerExtension({
30
33
  }
31
34
  });
32
35
  })
36
+ console.log('检查节点:', nodeData.name, '是否在loadNodeList中:', computeIsLoadNode(nodeData.name));
33
37
  if (computeIsLoadNode(nodeData.name)) {
38
+ console.log('节点', nodeData.name, '被处理');
34
39
  nodeType.prototype.onNodeCreated = async function() {
35
40
  const apiHost = 'https://bizyair.cn/api'
36
- const image_widget = this.widgets.find(w => {
37
- return w.name === 'image'
38
- || w.name === 'file'
39
- || w.name === 'audio'
40
- || w.name === 'model_file'
41
- });
41
+ // 优先使用 API 的媒体输入键匹配到具体的 widget;若未命中则回退到原有字段集合
42
+ let media_widget = null;
43
+ const mediaNodeConfig = await getMediaNodeConfig(nodeData.name);
44
+ const apiInputKeys = getMediaInputKeys(mediaNodeConfig);
45
+ if (apiInputKeys && apiInputKeys.length > 0) {
46
+ for (const key of apiInputKeys) {
47
+ const w = this.widgets.find(x => x.name === key);
48
+ if (w) { media_widget = w; break; }
49
+ }
50
+ }
51
+ if (!media_widget) {
52
+ media_widget = this.widgets.find(w => {
53
+ return possibleMediaWidgetNames.includes(w.name);
54
+ });
55
+ }
56
+ // 查找所有name等于接口配置中inputs下的字段的widget(如video、audio等)
57
+ let va_widgets = [];
58
+ if (apiInputKeys && apiInputKeys.length > 0) {
59
+ for (const key of apiInputKeys) {
60
+ const w = this.widgets.find(x => x.name === key);
61
+ if (w) {
62
+ va_widgets.push(w);
63
+ }
64
+ }
65
+ }
66
+
67
+ // 如果API配置没有找到,使用回退逻辑查找常见的媒体widget
68
+ if (va_widgets.length === 0) {
69
+ for (const widgetName of possibleMediaWidgetNames) {
70
+ const w = this.widgets.find(x => x.name === widgetName);
71
+ if (w) {
72
+ va_widgets.push(w);
73
+ }
74
+ }
75
+ }
76
+
77
+ console.log('节点名称:', nodeData.name);
78
+ console.log('apiInputKeys:', apiInputKeys);
79
+ console.log('找到的va_widgets:', va_widgets.map(w => w.name));
80
+ console.log('所有widgets:', this.widgets.map(w => w.name));
42
81
  let image_name_widget = this.widgets.find(w => w.name === 'image_name');
43
82
  let image_list = []
44
83
  const getData = async () => {
@@ -65,21 +104,104 @@ app.registerExtension({
65
104
  name: item.name
66
105
  }
67
106
  })
68
- if (!image_name_widget) {
107
+ // 如果找到va_widgets,处理它们
108
+ if (va_widgets.length > 0) {
109
+ // 隐藏所有va_widgets
110
+ va_widgets.forEach(va_widget => {
111
+ hideWidget(this, va_widget.name);
112
+ });
113
+
114
+ // 创建image_name_widget来替代显示
115
+ if (!image_name_widget) {
116
+ image_name_widget = this.addWidget("combo", "image_name", "", function(e){
117
+ const item = image_list.find(item => item.name === e)
118
+ if (item) {
119
+ const image_url = decodeURIComponent(item.url);
120
+ // 更新所有va_widgets的值
121
+ va_widgets.forEach(va_widget => {
122
+ va_widget.value = image_url;
123
+ if (va_widget.callback) {
124
+ va_widget.callback(e);
125
+ }
126
+ });
127
+ }
128
+ }, {
129
+ serialize: true,
130
+ values: image_list.map(item => item.name)
131
+ });
132
+ }
133
+
134
+ // 为每个va_widget重写callback
135
+ va_widgets.forEach(va_widget => {
136
+ console.log('处理va_widget:', va_widget.name, '原始callback:', va_widget.callback);
137
+
138
+ // 保存va_widget的原始callback
139
+ const originalVaCallback = va_widget.callback;
140
+
141
+ // 重写va_widget的callback,当被触发时给image_name_widget赋值
142
+ va_widget.callback = function(e) {
143
+ console.log('va_widget callback被触发:', va_widget.name, '参数:', e);
144
+
145
+ // 调用原始callback
146
+ if (originalVaCallback) {
147
+ originalVaCallback(e);
148
+ }
149
+
150
+ // 给image_name_widget赋值
151
+ if (image_name_widget) {
152
+ if (typeof e === 'string') {
153
+ const item = image_list.find(item => item.url === e);
154
+ if (item) {
155
+ image_name_widget.value = item.name;
156
+ } else {
157
+ // 如果没找到对应的item,尝试从URL中提取文件名
158
+ const fileName = e.split('/').pop();
159
+ image_name_widget.value = fileName;
160
+ }
161
+ }
162
+ }
163
+ };
164
+
165
+ // 监听widget的value变化
166
+ const originalSetValue = va_widget.setValue;
167
+ if (originalSetValue) {
168
+ va_widget.setValue = function(value) {
169
+ console.log('va_widget setValue被调用:', va_widget.name, '新值:', value);
170
+ originalSetValue.call(this, value);
171
+
172
+ // 当value变化时,更新image_name_widget
173
+ if (image_name_widget && value) {
174
+ if (typeof value === 'string') {
175
+ const item = image_list.find(item => item.url === value);
176
+ if (item) {
177
+ image_name_widget.value = item.name;
178
+ } else {
179
+ const fileName = value.split('/').pop();
180
+ image_name_widget.value = fileName;
181
+ }
182
+ }
183
+ }
184
+ };
185
+ }
186
+ });
187
+ }
188
+
189
+ // 如果va_widgets没有创建image_name_widget,使用原有逻辑创建
190
+ if (!image_name_widget && media_widget) {
69
191
  image_name_widget = this.addWidget("combo", "image_name", "", function(e){
70
192
  const item = image_list.find(item => item.name === e)
71
193
  const image_url = decodeURIComponent(item.url);
72
- image_widget.value = image_url;
73
- if (image_widget.callback) {
74
- image_widget.callback(e);
194
+ media_widget.value = image_url;
195
+ if (media_widget.callback) {
196
+ media_widget.callback(e);
75
197
  }
76
198
  }, {
77
199
  serialize: true,
78
200
  values: image_list.map(item => item.name)
79
201
  });
80
202
  }
81
- const val = image_list.find(item => item.url === image_widget.value)?.name || image_widget.value
82
- image_name_widget.label = image_widget.label
203
+ const val = image_list.find(item => item.url === media_widget.value)?.name || media_widget.value
204
+ image_name_widget.label = media_widget.label
83
205
  image_name_widget.value = val
84
206
 
85
207
  const currentIndex = this.widgets.indexOf(image_name_widget);
@@ -87,11 +209,11 @@ app.registerExtension({
87
209
  this.widgets.splice(currentIndex, 1);
88
210
  this.widgets.splice(1, 0, image_name_widget);
89
211
  }
90
- hideWidget(this, image_widget.name)
91
- image_widget.options.values = image_list.map(item => item.name);
212
+ hideWidget(this, media_widget.name)
213
+ media_widget.options.values = image_list.map(item => item.name);
92
214
 
93
- const callback = image_widget.callback
94
- image_widget.callback = async function(e) {
215
+ const callback = media_widget.callback
216
+ media_widget.callback = async function(e) {
95
217
  if (typeof e == 'string') {
96
218
  const item = e.includes('http') ?
97
219
  image_list.find(item => item.url === e) :
@@ -100,7 +222,7 @@ app.registerExtension({
100
222
  const image_url = item ? decodeURIComponent(item.url) : e;
101
223
 
102
224
  image_name_widget.value = item ? item.name : e;
103
- image_widget.value = image_url;
225
+ media_widget.value = image_url;
104
226
  callback([image_url])
105
227
  } else {
106
228
  const item = e[0].split('/')
@@ -119,7 +241,7 @@ app.registerExtension({
119
241
  await getData()
120
242
 
121
243
 
122
- function applyWorkflowImageSettings(workflowParams, image_list, image_widget, image_name_widget, currentNodeId) {
244
+ function applyWorkflowImageSettings(workflowParams, image_list, media_widget, image_name_widget, currentNodeId) {
123
245
  if (workflowParams && workflowParams.nodes) {
124
246
  // 根据当前节点ID查找对应的节点数据,而不是总是选择第一个
125
247
  const imageNode = workflowParams.nodes.find(item =>
@@ -131,23 +253,23 @@ app.registerExtension({
131
253
  name: item[item.length - 1],
132
254
  url: imageNode.widgets_values[0]
133
255
  })
134
- image_widget.value = imageNode.widgets_values[0]
256
+ media_widget.value = imageNode.widgets_values[0]
135
257
 
136
- image_widget.options.values = image_list.map(item => item.url)
258
+ media_widget.options.values = image_list.map(item => item.url)
137
259
  image_name_widget.options.values = image_list.map(item => item.name)
138
- image_widget.callback(imageNode.widgets_values[0])
260
+ media_widget.callback(imageNode.widgets_values[0])
139
261
  }
140
262
  }
141
263
  }
142
264
 
143
265
  // 如果有存储的工作流数据,应用图像设置
144
266
  if (window.currentWorkflowData) {
145
- applyWorkflowImageSettings(window.currentWorkflowData, image_list, image_widget, image_name_widget, this.id);
267
+ applyWorkflowImageSettings(window.currentWorkflowData, image_list, media_widget, image_name_widget, this.id);
146
268
  // 清除存储的数据,避免重复处理
147
269
  delete window.currentWorkflowData;
148
270
  } else {
149
271
  // 原有的调用
150
- applyWorkflowImageSettings(workflowParams, image_list, image_widget, image_name_widget, this.id);
272
+ applyWorkflowImageSettings(workflowParams, image_list, media_widget, image_name_widget, this.id);
151
273
  }
152
274
  //在这里发个postmessage
153
275
  window.parent.postMessage({
@@ -1,300 +1,15 @@
1
1
  import { app } from "../../scripts/app.js";
2
2
  import '../BizyAir/bizyair_frontend.js'
3
- import { hideWidget } from './tool.js'
4
-
5
- // 动态配置缓存
6
- let nodeConfigCache = null;
7
- let configLoadPromise = null;
3
+ import {
4
+ getNodeConfig,
5
+ createSetWidgetCallback,
6
+ setupNodeMouseBehavior,
7
+ addBadge,
8
+ possibleWidgetNames
9
+ } from './hookLoad/model.js'
10
+
11
+ // 存储清理标志
8
12
  let storageClearedOnce = false;
9
-
10
- // API配置
11
- const CONFIG_API_URL = 'https://bizyair.cn/api/special/comfyagent_node_config?t=' + Math.floor(Date.now() / 60000);
12
-
13
- // 获取节点配置的API函数
14
- async function fetchNodeConfig() {
15
- if (nodeConfigCache) {
16
- return nodeConfigCache;
17
- }
18
-
19
- if (configLoadPromise) {
20
- return configLoadPromise;
21
- }
22
-
23
- configLoadPromise = (async () => {
24
- try {
25
- console.log('正在从API获取节点配置...');
26
- const response = await fetch(CONFIG_API_URL, { credentials: 'include' });
27
-
28
- if (!response.ok) {
29
- throw new Error(`HTTP error! status: ${response.status}`);
30
- }
31
-
32
- const result = await response.json();
33
-
34
- if (result.code === 20000 && result.data && result.data.weight_load_nodes) {
35
- nodeConfigCache = result.data.weight_load_nodes;
36
- console.log('节点配置加载成功:', Object.keys(nodeConfigCache).length, '个节点');
37
- return nodeConfigCache;
38
- } else {
39
- throw new Error('API返回数据格式不正确');
40
- }
41
- } catch (error) {
42
- console.error('获取节点配置失败:', error);
43
- nodeConfigCache = null;
44
- return null;
45
- }
46
- })();
47
-
48
- return configLoadPromise;
49
- }
50
-
51
- // 启动时后台预取(不阻塞后续逻辑)
52
- try { void fetchNodeConfig(); } catch (e) { /* noop */ }
53
-
54
- const HAZY_WHITELIST_NODES = {
55
-
56
- }
57
-
58
- const possibleWidgetNames=[
59
- "clip_name",
60
- "clip_name1",
61
- "clip_name2",
62
- "clip_name3",
63
- "clip_name4",
64
- "ckpt_name",
65
- "lora_name",
66
- "name",
67
- "lora",
68
- "lora_01",
69
- "lora_02",
70
- "lora_03",
71
- "lora_04",
72
- "lora_1_name",
73
- "lora_2_name",
74
- "lora_3_name",
75
- "lora_4_name",
76
- "lora_5_name",
77
- "lora_6_name",
78
- "lora_7_name",
79
- "lora_8_name",
80
- "lora_9_name",
81
- "lora_10_name",
82
- "lora_11_name",
83
- "lora_12_name",
84
- "model_name",
85
- "control_net_name",
86
- "ipadapter_file",
87
- "unet_name",
88
- "vae_name",
89
- "model",
90
- "model_name",
91
- "instantid_file",
92
- "pulid_file",
93
- "style_model_name",
94
- "yolo_model",
95
- "face_model",
96
- "bbox_model_name",
97
- "sam_model_name",
98
- "model_path",
99
- "upscale_model",
100
- "supir_model",
101
- "sdxl_model",
102
- "upscale_model_1",
103
- "upscale_model_2",
104
- "upscale_model_3",
105
- "sam_model",
106
- "sam2_model",
107
- "grounding_dino_model"
108
- ]
109
-
110
- // 读取 mode_type(不再兼容旧的 modelType)
111
- function getModelTypeFromInput(inputConfig) {
112
- console.log('inputConfig', inputConfig);
113
- if (!inputConfig) return undefined;
114
- console.log('inputConfig', inputConfig);
115
- return inputConfig.mode_type;
116
- }
117
-
118
- // 根据节点名称获取节点配置信息(名单优先,正则补充;不阻塞返回)
119
- async function getNodeConfig(nodeName) {
120
- if (/bizyair/i.test(nodeName)) {
121
- return null;
122
- }
123
-
124
- // 1) 名单(API)优先(仅用缓存,不等待网络)
125
- if (nodeConfigCache && nodeConfigCache[nodeName]) {
126
- return { nodeName, config: nodeConfigCache[nodeName] };
127
- }
128
-
129
- // 若尚未发起请求,后台发起一次
130
- if (!configLoadPromise) { try { void fetchNodeConfig(); } catch (e) {} }
131
-
132
- // 2) 正则补充:如 XxxLoader => Xxx(立即返回,不等待API)
133
- const regex = /^(\w+).*Loader.*/i;
134
- const match = nodeName.match(regex);
135
- if (match) {
136
- const inferredType = match[1];
137
- return { nodeName, config: { inputs: { [nodeName]: { mode_type: inferredType, required: true } } } };
138
- }
139
- return null;
140
- }
141
-
142
- function createSetWidgetCallback(nodeConfig, selectedBaseModels = []) {
143
- return function setWidgetCallback() {
144
- if (!nodeConfig || !nodeConfig.config || !nodeConfig.config.inputs) {
145
- console.warn('节点配置无效:', nodeConfig);
146
- return;
147
- }
148
-
149
- const inputs = nodeConfig.config.inputs;
150
- const inputKeys = Object.keys(inputs);
151
-
152
- // 根据API配置找到对应的widget
153
- const targetWidgets = [];
154
- inputKeys.forEach(inputKey => {
155
- const widget = this.widgets.find(w => w.name === inputKey);
156
- if (widget) {
157
- targetWidgets.push({
158
- widget: widget,
159
- inputKey: inputKey,
160
- inputConfig: inputs[inputKey]
161
- });
162
- }
163
- });
164
-
165
- // 如果没有找到匹配的widget,使用原来的逻辑作为备选
166
- if (targetWidgets.length === 0) {
167
- const fallbackWidgets = this.widgets.filter(widget => possibleWidgetNames.includes(widget.name));
168
- fallbackWidgets.forEach((wdt, index) => {
169
- const firstInput = Object.values(inputs)[0];
170
- if (firstInput) {
171
- targetWidgets.push({
172
- widget: wdt,
173
- inputKey: wdt.name,
174
- inputConfig: firstInput,
175
- index: index
176
- });
177
- }
178
- });
179
- }
180
-
181
- targetWidgets.forEach(({ widget, inputKey, inputConfig, index }) => {
182
- // 检查是否禁用comfyagent
183
- if (inputConfig.disable_comfyagent) {
184
- console.log(`跳过禁用的widget: ${inputKey}`);
185
- return;
186
- }
187
-
188
- widget.value = widget.value || "to choose";
189
- widget.mouse = function(e, pos, canvas) {
190
- try {
191
- if (e.type === "pointerdown" || e.type === "mousedown" || e.type === "click" || e.type === "pointerup") {
192
- e.preventDefault();
193
- e.stopPropagation();
194
- e.widgetClick = true;
195
- window.parent.postMessage({
196
- type: 'collapsePublishWorkflowDialog',
197
- method: 'collapsePublishWorkflowDialog',
198
- result: true
199
- }, '*');
200
- const currentNode = this.node;
201
-
202
- if (!currentNode || !currentNode.widgets) {
203
- console.warn("Node or widgets not available");
204
- return false;
205
- }
206
-
207
- if (typeof bizyAirLib !== 'undefined' && typeof bizyAirLib.showModelSelect === 'function') {
208
- bizyAirLib.showModelSelect({
209
- modelType: [getModelTypeFromInput(inputConfig)],
210
- selectedBaseModels,
211
- onApply: (version, model) => {
212
- if (!currentNode || !currentNode.widgets) return;
213
-
214
- // 更新widget值
215
- widget.value = version.file_name;
216
-
217
- // 找到对应的隐藏字段(固定命名:model_version_id, model_version_id2...)
218
- let modelVersionField;
219
- // 真实绑定顺序应与targetWidgets相同,因此通过当前widget在targetWidgets的索引定位
220
- const twIndex = targetWidgets.findIndex(tw => tw.widget === widget);
221
- const fieldName = twIndex === 0 ? "model_version_id" : `model_version_id${twIndex + 1}`;
222
- modelVersionField = currentNode.widgets.find(w => w.name === fieldName);
223
-
224
- if (model && modelVersionField && version) {
225
- modelVersionField.value = version.id;
226
- currentNode.setDirtyCanvas(true);
227
-
228
- // 删除节点上的感叹号徽章
229
- if (currentNode && currentNode.badges && Array.isArray(currentNode.badges)) {
230
- // 移除 text 为 '!' 的徽章
231
- currentNode.badges = currentNode.badges.filter(badgeFn => {
232
- try {
233
- const badge = typeof badgeFn === 'function' ? badgeFn() : badgeFn;
234
- return badge.text !== '!';
235
- } catch (e) {
236
- return true;
237
- }
238
- });
239
- // 同时移除 hasTips 标记
240
- if (currentNode.hasTips) {
241
- delete currentNode.hasTips;
242
- }
243
- }
244
- }
245
- }
246
- });
247
- } else {
248
- console.error("bizyAirLib not available");
249
- }
250
- return false;
251
- }
252
- } catch (error) {
253
- console.error("Error handling mouse event:", error);
254
- }
255
- };
256
-
257
- widget.options = widget.options || {};
258
- widget.options.values = () => [];
259
- widget.options.editable = false;
260
- widget.clickable = true;
261
- widget.processMouse = true;
262
- });
263
- }
264
- }
265
-
266
- function setupNodeMouseBehavior(node, nodeConfig) {
267
- // 固定隐藏主版本ID字段(其余编号字段为hidden类型本身不可见)
268
- hideWidget(node, "model_version_id");
269
-
270
- // 只设置必要的状态信息,不修改onMouseDown(已在上面的扩展中处理)
271
- if (!node._bizyairState) {
272
- node._bizyairState = {
273
- lastClickTime: 0,
274
- DEBOUNCE_DELAY: 300,
275
- nodeConfig: nodeConfig
276
- };
277
- }
278
- }
279
- function addBadge(node) {
280
- const customBadge = new LGraphBadge({
281
- text: '!',
282
- fgColor: 'white',
283
- bgColor: '#FF6B6B',
284
- fontSize: 12,
285
- padding: 8,
286
- height: 20,
287
- cornerRadius: 10
288
- })
289
- if (!Array.isArray(node.badges)) {
290
- node.badges = []
291
- }
292
- if (node.hasTips) {
293
- return
294
- }
295
- node.badges.push(() => customBadge);
296
- node.hasTips = true;
297
- }
298
13
  app.registerExtension({
299
14
  name: "bizyair.hook.load.model",
300
15
  async beforeRegisterNodeDef(nodeType, nodeData, app) {
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bizydraft
3
- Version: 0.2.73.dev20251015100429
3
+ Version: 0.2.75.dev20251022024104
4
4
  Summary: bizydraft
5
5
  Requires-Dist: loguru
6
6
  Requires-Dist: aiohttp
@@ -1,10 +1,10 @@
1
1
  bizydraft/__init__.py,sha256=OM-sKCQrPh25nHVJIX-DgF1raMYyoWLSuyduIAHt0Gs,78
2
2
  bizydraft/block_nodes.py,sha256=Lqn3oSCaGDHR2OICc8a2iRoRCVVK9v1-9MM3r-qIZgA,1092
3
3
  bizydraft/env.py,sha256=VFmGopVL2TGWA6hwxyFhIglCEcQxy6iVvL_raMNd6u4,407
4
- bizydraft/hijack_nodes.py,sha256=GivcoUsYAOfMjoEMxeViEkSQlmYjMA0RORy04fCbG60,3652
5
- bizydraft/hijack_routes.py,sha256=wLu_PWUbUzhN2uZeayTAj1ShdLXVuKsp85a_FX1UCYY,3415
4
+ bizydraft/hijack_nodes.py,sha256=riHsp4DhU60E60bfXUl8kVqjs1QvQWmx_FsgQAdWTa4,4197
5
+ bizydraft/hijack_routes.py,sha256=TVK7zdSIe4wN5dusjnAr1we43K6B_Zup5-dWcBj5rxo,3545
6
6
  bizydraft/oss_utils.py,sha256=JHpMA61NxFzA053y8IzBc01xxMJCF6G2PTHk-rXqIFo,15590
7
- bizydraft/patch_handlers.py,sha256=UQudnqKtDTYPnlS3Aq_k7txg7Je6ph9rkioou5-FgZI,6194
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
10
10
  bizydraft/resp.py,sha256=8INvKOe5Dgai3peKfqKjrhUoYeuXWXn358w30-_cY-A,369
@@ -15,8 +15,8 @@ bizydraft/static/js/clipspaceToOss.js,sha256=brfEPs71Tky5Dnc47UXNEFeFlESDE3kQvUH
15
15
  bizydraft/static/js/disableComfyWebSocket.js,sha256=nL6DjLUdC2FlAqfYPaFW-dAtkamv01c461W0DUupIKk,1124
16
16
  bizydraft/static/js/freezeModeHandler.js,sha256=SjpHD2nYymR-E13B0YcqkA6e4WycZOVI3c48Ts9qvWE,18027
17
17
  bizydraft/static/js/handleStyle.js,sha256=liIzTu-wnV172g58gHWGLYTfd86xpJxL4A-HuHpFnq4,3616
18
- bizydraft/static/js/hookLoadImage.js,sha256=aFRWkgJW-Cp-YHjZh-3j-vsVcNaDZpBVoQqcFZ2Po0g,8186
19
- bizydraft/static/js/hookLoadModel.js,sha256=GIvew1mDcJhgyyaJif0tFGNdFQo_LzlTQfBZQ2lg2Tw,17032
18
+ bizydraft/static/js/hookLoadImage.js,sha256=SOxkmSO8gRiiTw9QZ_LzIGtnFFDkBTieG1f7ZfVF2b0,14980
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
@@ -25,7 +25,7 @@ bizydraft/static/js/socket.js,sha256=VE3fTAgEfM0FZhL526Skt7OCRokOa3mzTCAjAomI_tE
25
25
  bizydraft/static/js/tool.js,sha256=VupamUuh7tYiDnBTrL5Z_yLmhJinskhzRXwE3zfsKZM,2901
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-0.2.73.dev20251015100429.dist-info/METADATA,sha256=VQh_RS2B5UguQtmdCPuD0np5AG4PFB5B8WMYLvuxPOo,180
29
- bizydraft-0.2.73.dev20251015100429.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
30
- bizydraft-0.2.73.dev20251015100429.dist-info/top_level.txt,sha256=XtoBq6hjZhXIM7aas4GtPDtAiKo8FdLzMABXW8qqQ8M,10
31
- bizydraft-0.2.73.dev20251015100429.dist-info/RECORD,,
28
+ bizydraft-0.2.75.dev20251022024104.dist-info/METADATA,sha256=inxigvbqmmO2O7EABnHlm1GRCaNVx-G-SpVz8OoNfNI,180
29
+ bizydraft-0.2.75.dev20251022024104.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
30
+ bizydraft-0.2.75.dev20251022024104.dist-info/top_level.txt,sha256=XtoBq6hjZhXIM7aas4GtPDtAiKo8FdLzMABXW8qqQ8M,10
31
+ bizydraft-0.2.75.dev20251022024104.dist-info/RECORD,,