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.
- bizydraft/hijack_routes.py +28 -1
- bizydraft/oss_utils.py +14 -3
- bizydraft/patch_handlers.py +105 -5
- bizydraft/static/js/aspectRatio.js +751 -0
- bizydraft/static/js/clipspaceToOss.js +57 -26
- bizydraft/static/js/handleStyle.js +54 -12
- bizydraft/static/js/hookLoad/media.js +415 -0
- bizydraft/static/js/hookLoadMedia.js +92 -285
- bizydraft/static/js/hookLoadModel.js +49 -3
- bizydraft/static/js/imageUpload.js +146 -0
- bizydraft/static/js/limitTimeRange.js +129 -0
- bizydraft/static/js/main.js +4 -1
- {bizydraft-0.2.78.dev20251030100819.dist-info → bizydraft-0.2.82.dev20251209023307.dist-info}/METADATA +1 -1
- {bizydraft-0.2.78.dev20251030100819.dist-info → bizydraft-0.2.82.dev20251209023307.dist-info}/RECORD +16 -13
- {bizydraft-0.2.78.dev20251030100819.dist-info → bizydraft-0.2.82.dev20251209023307.dist-info}/WHEEL +0 -0
- {bizydraft-0.2.78.dev20251030100819.dist-info → bizydraft-0.2.82.dev20251209023307.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,751 @@
|
|
|
1
|
+
import { app } from "../../scripts/app.js";
|
|
2
|
+
|
|
3
|
+
// Seedream 4.5 节点的宽高比和总像素限制
|
|
4
|
+
const MIN_ASPECT_RATIO = 1 / 16; // 1/16
|
|
5
|
+
const MAX_ASPECT_RATIO = 16; // 16
|
|
6
|
+
const MIN_TOTAL_PIXELS = 2560 * 1440; // 3686400
|
|
7
|
+
const MAX_TOTAL_PIXELS = 4096 * 4096; // 16777216
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* 验证所有约束是否满足
|
|
11
|
+
* @param {number} width - 宽度
|
|
12
|
+
* @param {number} height - 高度
|
|
13
|
+
* @returns {boolean} 是否满足所有约束
|
|
14
|
+
*/
|
|
15
|
+
function satisfiesAllConstraints(width, height) {
|
|
16
|
+
const totalPixels = width * height;
|
|
17
|
+
const aspectRatio = width / height;
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
totalPixels >= MIN_TOTAL_PIXELS &&
|
|
21
|
+
totalPixels <= MAX_TOTAL_PIXELS &&
|
|
22
|
+
aspectRatio >= MIN_ASPECT_RATIO &&
|
|
23
|
+
aspectRatio <= MAX_ASPECT_RATIO &&
|
|
24
|
+
width >= 480 &&
|
|
25
|
+
width <= 4096 &&
|
|
26
|
+
height >= 480 &&
|
|
27
|
+
height <= 4096
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* 计算给定宽度时,高度的合法范围
|
|
33
|
+
* @param {number} width - 宽度
|
|
34
|
+
* @returns {{min: number, max: number}} 高度的最小值和最大值
|
|
35
|
+
*/
|
|
36
|
+
function calculateHeightRange(width) {
|
|
37
|
+
// 来自总像素的限制
|
|
38
|
+
const minHeightByPixel = MIN_TOTAL_PIXELS / width;
|
|
39
|
+
const maxHeightByPixel = MAX_TOTAL_PIXELS / width;
|
|
40
|
+
|
|
41
|
+
// 来自宽高比的限制
|
|
42
|
+
// width/height <= 16 => height >= width/16
|
|
43
|
+
// width/height >= 1/16 => height <= width*16
|
|
44
|
+
const minHeightByRatio = width / MAX_ASPECT_RATIO;
|
|
45
|
+
const maxHeightByRatio = width * MAX_ASPECT_RATIO;
|
|
46
|
+
|
|
47
|
+
// 取交集:下限取最大值(最严格),上限取最小值(最严格)
|
|
48
|
+
const minHeight = Math.max(
|
|
49
|
+
Math.max(minHeightByPixel, minHeightByRatio),
|
|
50
|
+
480 // 单边最小值
|
|
51
|
+
);
|
|
52
|
+
const maxHeight = Math.min(
|
|
53
|
+
Math.min(maxHeightByPixel, maxHeightByRatio),
|
|
54
|
+
4096 // 单边最大值
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
return { min: minHeight, max: maxHeight };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* 计算给定高度时,宽度的合法范围
|
|
62
|
+
* @param {number} height - 高度
|
|
63
|
+
* @returns {{min: number, max: number}} 宽度的最小值和最大值
|
|
64
|
+
*/
|
|
65
|
+
function calculateWidthRange(height) {
|
|
66
|
+
// 来自总像素的限制
|
|
67
|
+
const minWidthByPixel = MIN_TOTAL_PIXELS / height;
|
|
68
|
+
const maxWidthByPixel = MAX_TOTAL_PIXELS / height;
|
|
69
|
+
|
|
70
|
+
// 来自宽高比的限制
|
|
71
|
+
// width/height >= 1/16 => width >= height/16
|
|
72
|
+
// width/height <= 16 => width <= height*16
|
|
73
|
+
const minWidthByRatio = height * MIN_ASPECT_RATIO;
|
|
74
|
+
const maxWidthByRatio = height * MAX_ASPECT_RATIO;
|
|
75
|
+
|
|
76
|
+
// 取交集
|
|
77
|
+
const minWidth = Math.max(Math.max(minWidthByPixel, minWidthByRatio), 480);
|
|
78
|
+
const maxWidth = Math.min(Math.min(maxWidthByPixel, maxWidthByRatio), 4096);
|
|
79
|
+
|
|
80
|
+
return { min: minWidth, max: maxWidth };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* 根据改变的维度和当前另一个维度,计算满足所有约束的新尺寸
|
|
85
|
+
* 使用橡皮筋算法(Clamp Algorithm):尽量保持原宽高比,超出范围则截断
|
|
86
|
+
* @param {number} changedValue - 改变的维度值(宽或高)
|
|
87
|
+
* @param {number} otherValue - 另一个维度的当前值
|
|
88
|
+
* @param {boolean} isWidthChanged - 是否是宽度改变
|
|
89
|
+
* @param {number} oldValue - 改变前的维度值(用于计算宽高比),如果未提供则使用 otherValue 作为参考
|
|
90
|
+
* @returns {{width: number, height: number}} 计算后的宽高值
|
|
91
|
+
*/
|
|
92
|
+
function calculateNewDimensions(
|
|
93
|
+
changedValue,
|
|
94
|
+
otherValue,
|
|
95
|
+
isWidthChanged,
|
|
96
|
+
oldValue = null
|
|
97
|
+
) {
|
|
98
|
+
// 如果另一个值为0或无效,使用默认值
|
|
99
|
+
if (!otherValue || otherValue === 0) {
|
|
100
|
+
otherValue = changedValue;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// 第一步:先限制改变的维度本身在合法范围内
|
|
104
|
+
let newWidth, newHeight;
|
|
105
|
+
if (isWidthChanged) {
|
|
106
|
+
// 宽度改变,先限制宽度在合法范围内
|
|
107
|
+
newWidth = Math.max(480, Math.min(4096, changedValue));
|
|
108
|
+
|
|
109
|
+
// 计算高度的合法范围
|
|
110
|
+
const heightRange = calculateHeightRange(newWidth);
|
|
111
|
+
|
|
112
|
+
// 如果合法范围无效(下限大于上限),说明宽度本身不合法,需要调整宽度
|
|
113
|
+
if (heightRange.min > heightRange.max) {
|
|
114
|
+
// 宽度太极端,无法找到匹配的高度
|
|
115
|
+
// 尝试调整宽度到合理范围
|
|
116
|
+
// 计算宽度的合法范围(基于一个合理的高度值,比如2048)
|
|
117
|
+
const tempHeight = 2048;
|
|
118
|
+
const widthRange = calculateWidthRange(tempHeight);
|
|
119
|
+
newWidth = Math.max(widthRange.min, Math.min(widthRange.max, newWidth));
|
|
120
|
+
|
|
121
|
+
// 重新计算高度范围
|
|
122
|
+
const newHeightRange = calculateHeightRange(newWidth);
|
|
123
|
+
if (newHeightRange.min > newHeightRange.max) {
|
|
124
|
+
// 仍然无效,使用默认值
|
|
125
|
+
newWidth = 2048;
|
|
126
|
+
newHeight = 2048;
|
|
127
|
+
return { width: Math.round(newWidth), height: Math.round(newHeight) };
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// 第二步:计算理想高度(保持原宽高比)
|
|
132
|
+
// 如果提供了旧宽度,使用旧宽高比:idealHeight = newWidth * (oldHeight / oldWidth)
|
|
133
|
+
// 如果没有提供旧宽度,假设用户希望保持 newWidth/otherValue 的比例(即高度不变)
|
|
134
|
+
let idealHeight;
|
|
135
|
+
if (oldValue !== null && oldValue > 0 && otherValue > 0) {
|
|
136
|
+
// 使用旧宽高比计算理想高度
|
|
137
|
+
const oldRatio = oldValue / otherValue;
|
|
138
|
+
idealHeight = newWidth / oldRatio;
|
|
139
|
+
} else {
|
|
140
|
+
// 没有旧值,假设保持当前高度(即比例 = newWidth / otherValue)
|
|
141
|
+
idealHeight = otherValue;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// 第三步:将理想高度"截断"到合法范围(橡皮筋逻辑)
|
|
145
|
+
newHeight = Math.max(
|
|
146
|
+
heightRange.min,
|
|
147
|
+
Math.min(heightRange.max, idealHeight)
|
|
148
|
+
);
|
|
149
|
+
newHeight = Math.round(newHeight);
|
|
150
|
+
|
|
151
|
+
// 第四步:如果高度被截断,可能需要微调宽度以更好地满足约束
|
|
152
|
+
// 但为了保持"尽量不动a"的原则,这里只做轻微调整
|
|
153
|
+
// 重新计算宽度范围(基于调整后的高度)
|
|
154
|
+
const finalWidthRange = calculateWidthRange(newHeight);
|
|
155
|
+
newWidth = Math.max(
|
|
156
|
+
finalWidthRange.min,
|
|
157
|
+
Math.min(finalWidthRange.max, newWidth)
|
|
158
|
+
);
|
|
159
|
+
newWidth = Math.round(newWidth);
|
|
160
|
+
} else {
|
|
161
|
+
// 高度改变,先限制高度在合法范围内
|
|
162
|
+
newHeight = Math.max(480, Math.min(4096, changedValue));
|
|
163
|
+
|
|
164
|
+
// 计算宽度的合法范围
|
|
165
|
+
const widthRange = calculateWidthRange(newHeight);
|
|
166
|
+
|
|
167
|
+
// 如果合法范围无效,需要调整高度
|
|
168
|
+
if (widthRange.min > widthRange.max) {
|
|
169
|
+
const tempWidth = 2048;
|
|
170
|
+
const heightRange = calculateHeightRange(tempWidth);
|
|
171
|
+
newHeight = Math.max(
|
|
172
|
+
heightRange.min,
|
|
173
|
+
Math.min(heightRange.max, newHeight)
|
|
174
|
+
);
|
|
175
|
+
|
|
176
|
+
const newWidthRange = calculateWidthRange(newHeight);
|
|
177
|
+
if (newWidthRange.min > newWidthRange.max) {
|
|
178
|
+
newWidth = 2048;
|
|
179
|
+
newHeight = 2048;
|
|
180
|
+
return { width: Math.round(newWidth), height: Math.round(newHeight) };
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// 计算理想宽度(保持原宽高比)
|
|
185
|
+
// 如果提供了旧高度,使用旧宽高比:idealWidth = newHeight * (oldWidth / oldHeight)
|
|
186
|
+
// 如果没有提供旧高度,假设用户希望保持 otherValue/newHeight 的比例(即宽度不变)
|
|
187
|
+
let idealWidth;
|
|
188
|
+
if (oldValue !== null && oldValue > 0 && otherValue > 0) {
|
|
189
|
+
// 使用旧宽高比计算理想宽度
|
|
190
|
+
const oldRatio = otherValue / oldValue;
|
|
191
|
+
idealWidth = newHeight * oldRatio;
|
|
192
|
+
} else {
|
|
193
|
+
// 没有旧值,假设保持当前宽度(即比例 = otherValue / newHeight)
|
|
194
|
+
idealWidth = otherValue;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// 将理想宽度截断到合法范围
|
|
198
|
+
newWidth = Math.max(widthRange.min, Math.min(widthRange.max, idealWidth));
|
|
199
|
+
newWidth = Math.round(newWidth);
|
|
200
|
+
|
|
201
|
+
// 微调高度
|
|
202
|
+
const finalHeightRange = calculateHeightRange(newWidth);
|
|
203
|
+
newHeight = Math.max(
|
|
204
|
+
finalHeightRange.min,
|
|
205
|
+
Math.min(finalHeightRange.max, newHeight)
|
|
206
|
+
);
|
|
207
|
+
newHeight = Math.round(newHeight);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// 最终确保在合理范围内(双重保险)
|
|
211
|
+
newWidth = Math.max(480, Math.min(4096, newWidth));
|
|
212
|
+
newHeight = Math.max(480, Math.min(4096, newHeight));
|
|
213
|
+
|
|
214
|
+
return { width: newWidth, height: newHeight };
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* 验证并调整宽高值,确保满足所有约束(用于size切换到Custom时的验证)
|
|
219
|
+
* 使用橡皮筋算法:尽量保持原宽高比,超出范围则截断
|
|
220
|
+
* @param {number} width - 宽度
|
|
221
|
+
* @param {number} height - 高度
|
|
222
|
+
* @returns {{width: number, height: number}} 调整后的宽高
|
|
223
|
+
*/
|
|
224
|
+
function validateAndAdjustDimensions(width, height) {
|
|
225
|
+
// 如果已经满足所有约束,直接返回
|
|
226
|
+
if (satisfiesAllConstraints(width, height)) {
|
|
227
|
+
return { width: Math.round(width), height: Math.round(height) };
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// 保存原始宽高比(尽量保持)
|
|
231
|
+
const originalRatio = width / height;
|
|
232
|
+
|
|
233
|
+
// 第一步:限制单边尺寸范围(480-4096)
|
|
234
|
+
width = Math.max(480, Math.min(4096, width));
|
|
235
|
+
height = Math.max(480, Math.min(4096, height));
|
|
236
|
+
|
|
237
|
+
// 第二步:调整宽高比到合法范围(如果超出)
|
|
238
|
+
let aspectRatio = width / height;
|
|
239
|
+
if (aspectRatio < MIN_ASPECT_RATIO) {
|
|
240
|
+
// 宽高比太小,调整宽度
|
|
241
|
+
width = Math.round(height * MIN_ASPECT_RATIO);
|
|
242
|
+
width = Math.max(480, Math.min(4096, width));
|
|
243
|
+
} else if (aspectRatio > MAX_ASPECT_RATIO) {
|
|
244
|
+
// 宽高比太大,调整宽度
|
|
245
|
+
width = Math.round(height * MAX_ASPECT_RATIO);
|
|
246
|
+
width = Math.max(480, Math.min(4096, width));
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// 第三步:调整总像素到合法范围(保持当前宽高比)
|
|
250
|
+
let totalPixels = width * height;
|
|
251
|
+
if (totalPixels < MIN_TOTAL_PIXELS) {
|
|
252
|
+
// 总像素太小,按比例放大
|
|
253
|
+
const scale = Math.sqrt(MIN_TOTAL_PIXELS / totalPixels);
|
|
254
|
+
const newWidth = Math.round(width * scale);
|
|
255
|
+
const newHeight = Math.round(height * scale);
|
|
256
|
+
|
|
257
|
+
// 检查放大后是否超出单边限制
|
|
258
|
+
if (newWidth <= 4096 && newHeight <= 4096) {
|
|
259
|
+
width = newWidth;
|
|
260
|
+
height = newHeight;
|
|
261
|
+
} else {
|
|
262
|
+
// 超出限制,调整到最大可能的尺寸(保持宽高比)
|
|
263
|
+
aspectRatio = width / height;
|
|
264
|
+
if (aspectRatio >= MIN_ASPECT_RATIO && aspectRatio <= MAX_ASPECT_RATIO) {
|
|
265
|
+
if (width > height) {
|
|
266
|
+
width = 4096;
|
|
267
|
+
height = Math.round(4096 / aspectRatio);
|
|
268
|
+
height = Math.max(480, Math.min(4096, height));
|
|
269
|
+
} else {
|
|
270
|
+
height = 4096;
|
|
271
|
+
width = Math.round(4096 * aspectRatio);
|
|
272
|
+
width = Math.max(480, Math.min(4096, width));
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
} else if (totalPixels > MAX_TOTAL_PIXELS) {
|
|
277
|
+
// 总像素太大,按比例缩小
|
|
278
|
+
const scale = Math.sqrt(MAX_TOTAL_PIXELS / totalPixels);
|
|
279
|
+
width = Math.round(width * scale);
|
|
280
|
+
height = Math.round(height * scale);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// 第四步:最终验证和微调(最多一次回退检查)
|
|
284
|
+
// 重新计算当前值
|
|
285
|
+
width = Math.max(480, Math.min(4096, width));
|
|
286
|
+
height = Math.max(480, Math.min(4096, height));
|
|
287
|
+
aspectRatio = width / height;
|
|
288
|
+
totalPixels = width * height;
|
|
289
|
+
|
|
290
|
+
// 如果宽高比仍然不合法,强制调整
|
|
291
|
+
if (aspectRatio < MIN_ASPECT_RATIO) {
|
|
292
|
+
width = Math.round(height * MIN_ASPECT_RATIO);
|
|
293
|
+
if (width > 4096) {
|
|
294
|
+
width = 4096;
|
|
295
|
+
height = Math.round(4096 / MIN_ASPECT_RATIO);
|
|
296
|
+
if (height > 4096) {
|
|
297
|
+
height = 4096;
|
|
298
|
+
width = Math.round(4096 * MIN_ASPECT_RATIO);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
totalPixels = width * height;
|
|
302
|
+
if (totalPixels > MAX_TOTAL_PIXELS) {
|
|
303
|
+
const scale = Math.sqrt(MAX_TOTAL_PIXELS / totalPixels);
|
|
304
|
+
width = Math.round(width * scale);
|
|
305
|
+
height = Math.round(height * scale);
|
|
306
|
+
}
|
|
307
|
+
} else if (aspectRatio > MAX_ASPECT_RATIO) {
|
|
308
|
+
width = Math.round(height * MAX_ASPECT_RATIO);
|
|
309
|
+
if (width > 4096) {
|
|
310
|
+
width = 4096;
|
|
311
|
+
height = Math.round(4096 / MAX_ASPECT_RATIO);
|
|
312
|
+
if (height > 4096) {
|
|
313
|
+
height = 4096;
|
|
314
|
+
width = Math.round(4096 * MAX_ASPECT_RATIO);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
totalPixels = width * height;
|
|
318
|
+
if (totalPixels > MAX_TOTAL_PIXELS) {
|
|
319
|
+
const scale = Math.sqrt(MAX_TOTAL_PIXELS / totalPixels);
|
|
320
|
+
width = Math.round(width * scale);
|
|
321
|
+
height = Math.round(height * scale);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// 最终确保在合理范围内
|
|
326
|
+
width = Math.max(480, Math.min(4096, width));
|
|
327
|
+
height = Math.max(480, Math.min(4096, height));
|
|
328
|
+
|
|
329
|
+
return { width: Math.round(width), height: Math.round(height) };
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// 简单的链式callback实现(类似useChainCallback)
|
|
333
|
+
function chainCallback(originalCallback, newCallback) {
|
|
334
|
+
return function (...args) {
|
|
335
|
+
if (originalCallback) {
|
|
336
|
+
originalCallback.apply(this, args);
|
|
337
|
+
}
|
|
338
|
+
newCallback.apply(this, args);
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
app.registerExtension({
|
|
343
|
+
name: "bizyair.seedream45.aspectRatio",
|
|
344
|
+
nodeCreated(node, app) {
|
|
345
|
+
// 只处理 Seedream 4.5 节点
|
|
346
|
+
if (node.title !== "☁️BizyAir Seedream 4.5") {
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// 延迟执行,确保widget完全初始化,并且ComfyUI的setupNodeWidgetCallbacks已经执行
|
|
351
|
+
setTimeout(() => {
|
|
352
|
+
try {
|
|
353
|
+
// 查找 width、height 和 size widget
|
|
354
|
+
const widthWidget = node.widgets?.find(
|
|
355
|
+
(w) => w.name === "custom_width"
|
|
356
|
+
);
|
|
357
|
+
const heightWidget = node.widgets?.find(
|
|
358
|
+
(w) => w.name === "custom_height"
|
|
359
|
+
);
|
|
360
|
+
const sizeWidget = node.widgets?.find((w) => w.name === "size");
|
|
361
|
+
|
|
362
|
+
if (!widthWidget || !heightWidget) {
|
|
363
|
+
console.warn(
|
|
364
|
+
"[Seedream4.5] 未找到 custom_width 或 custom_height widget"
|
|
365
|
+
);
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
console.log("[Seedream4.5] 节点创建,开始设置宽高比联动", {
|
|
370
|
+
nodeId: node.id,
|
|
371
|
+
nodeType: node.type,
|
|
372
|
+
sizeWidget: sizeWidget,
|
|
373
|
+
sizeValue: sizeWidget?.value,
|
|
374
|
+
sizeType: sizeWidget?.type,
|
|
375
|
+
sizeOptions: sizeWidget?.options,
|
|
376
|
+
sizeOptionsValues: sizeWidget?.options?.values,
|
|
377
|
+
widthValue: widthWidget.value,
|
|
378
|
+
heightValue: heightWidget.value,
|
|
379
|
+
widthCallback: widthWidget.callback,
|
|
380
|
+
heightCallback: heightWidget.callback,
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
// 标记是否正在更新,避免循环触发
|
|
384
|
+
let isUpdating = false;
|
|
385
|
+
|
|
386
|
+
// 保存当前的 callback(可能已经被ComfyUI包装过)
|
|
387
|
+
const currentWidthCallback = widthWidget.callback;
|
|
388
|
+
const currentHeightCallback = heightWidget.callback;
|
|
389
|
+
|
|
390
|
+
// 检查 size 是否为 "Custom",只有 Custom 模式才需要联动
|
|
391
|
+
// 实时获取size widget的值,而不是在闭包中捕获
|
|
392
|
+
const isCustomSize = () => {
|
|
393
|
+
if (!sizeWidget) {
|
|
394
|
+
console.log("[Seedream4.5] sizeWidget不存在");
|
|
395
|
+
return false;
|
|
396
|
+
}
|
|
397
|
+
const sizeValue = sizeWidget.value;
|
|
398
|
+
|
|
399
|
+
// 获取size widget的所有选项值
|
|
400
|
+
const sizeOptions = sizeWidget.options?.values || [];
|
|
401
|
+
const hasCustomOption = sizeOptions.some((opt) => {
|
|
402
|
+
const optValue = typeof opt === "string" ? opt : opt?.value || opt;
|
|
403
|
+
return (
|
|
404
|
+
optValue === "Custom" ||
|
|
405
|
+
optValue === "custom" ||
|
|
406
|
+
optValue === "CUSTOM"
|
|
407
|
+
);
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
// 打印sizeOptions的详细信息
|
|
411
|
+
const sizeOptionsDetails = sizeOptions.map((opt, idx) => {
|
|
412
|
+
const optValue = typeof opt === "string" ? opt : opt?.value || opt;
|
|
413
|
+
const optLabel =
|
|
414
|
+
typeof opt === "string" ? opt : opt?.label || opt?.value || opt;
|
|
415
|
+
return { index: idx, value: optValue, label: optLabel, raw: opt };
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
// 查找Custom选项的实际值
|
|
419
|
+
let customOptionValue = null;
|
|
420
|
+
sizeOptions.forEach((opt) => {
|
|
421
|
+
const optValue = typeof opt === "string" ? opt : opt?.value || opt;
|
|
422
|
+
const optLabel =
|
|
423
|
+
typeof opt === "string" ? opt : opt?.label || opt?.value || opt;
|
|
424
|
+
if (
|
|
425
|
+
optValue === "Custom" ||
|
|
426
|
+
optValue === "custom" ||
|
|
427
|
+
optValue === "CUSTOM" ||
|
|
428
|
+
optLabel === "Custom" ||
|
|
429
|
+
optLabel === "custom" ||
|
|
430
|
+
optLabel === "CUSTOM" ||
|
|
431
|
+
String(optValue).toLowerCase().includes("custom") ||
|
|
432
|
+
String(optLabel).toLowerCase().includes("custom")
|
|
433
|
+
) {
|
|
434
|
+
customOptionValue = optValue;
|
|
435
|
+
}
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
console.log("[Seedream4.5] 检查isCustomSize", {
|
|
439
|
+
sizeValue,
|
|
440
|
+
type: typeof sizeValue,
|
|
441
|
+
sizeOptions: sizeOptions,
|
|
442
|
+
sizeOptionsDetails: sizeOptionsDetails,
|
|
443
|
+
customOptionValue: customOptionValue,
|
|
444
|
+
hasCustomOption,
|
|
445
|
+
equalsCustom:
|
|
446
|
+
sizeValue === "Custom" ||
|
|
447
|
+
sizeValue === "custom" ||
|
|
448
|
+
sizeValue === "CUSTOM" ||
|
|
449
|
+
sizeValue === customOptionValue,
|
|
450
|
+
sizeWidget: sizeWidget,
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
// 检查多种可能的"Custom"值
|
|
454
|
+
// 如果size widget有Custom选项,检查当前值是否等于Custom(包括找到的实际值)
|
|
455
|
+
if (hasCustomOption) {
|
|
456
|
+
const isCustom =
|
|
457
|
+
sizeValue === "Custom" ||
|
|
458
|
+
sizeValue === "custom" ||
|
|
459
|
+
sizeValue === "CUSTOM" ||
|
|
460
|
+
(customOptionValue && sizeValue === customOptionValue);
|
|
461
|
+
|
|
462
|
+
console.log("[Seedream4.5] Custom检查结果", {
|
|
463
|
+
sizeValue,
|
|
464
|
+
customOptionValue,
|
|
465
|
+
isCustom,
|
|
466
|
+
checks: {
|
|
467
|
+
equalsCustom: sizeValue === "Custom",
|
|
468
|
+
equalsLowercase: sizeValue === "custom",
|
|
469
|
+
equalsUppercase: sizeValue === "CUSTOM",
|
|
470
|
+
equalsFoundValue:
|
|
471
|
+
customOptionValue && sizeValue === customOptionValue,
|
|
472
|
+
},
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
return isCustom;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// 如果没有Custom选项,但当前值不在预设选项中,也认为是Custom模式
|
|
479
|
+
// 这适用于动态选项的情况
|
|
480
|
+
const isPresetValue = sizeOptions.some((opt) => {
|
|
481
|
+
const optValue = typeof opt === "string" ? opt : opt?.value || opt;
|
|
482
|
+
return optValue === sizeValue;
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
return !isPresetValue;
|
|
486
|
+
};
|
|
487
|
+
|
|
488
|
+
// 处理宽度变化的函数
|
|
489
|
+
const handleWidthChange = (newValue) => {
|
|
490
|
+
const customSize = isCustomSize();
|
|
491
|
+
console.log("[Seedream4.5] handleWidthChange 被调用", {
|
|
492
|
+
newValue,
|
|
493
|
+
isCustomSize: customSize,
|
|
494
|
+
isUpdating,
|
|
495
|
+
sizeWidgetValue: sizeWidget?.value,
|
|
496
|
+
sizeWidgetExists: !!sizeWidget,
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
if (!customSize || isUpdating) {
|
|
500
|
+
console.log("[Seedream4.5] 跳过处理", {
|
|
501
|
+
reason: !customSize ? "size不是Custom" : "正在更新中",
|
|
502
|
+
});
|
|
503
|
+
return;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
isUpdating = true;
|
|
507
|
+
try {
|
|
508
|
+
const numValue = Number(newValue);
|
|
509
|
+
if (isNaN(numValue)) {
|
|
510
|
+
return;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
const currentHeight = Number(heightWidget.value) || 2048;
|
|
514
|
+
// 获取旧宽度:在回调触发时,widthWidget.value 可能还是旧值(ComfyUI通常在callback中先调用callback再更新value)
|
|
515
|
+
// 但如果已经更新了,我们使用传入的 value 作为新值,尝试从 widget.value 获取旧值
|
|
516
|
+
// 如果 widget.value 等于新值,说明已经更新了,我们无法获取真正的旧值,使用新值作为参考
|
|
517
|
+
let oldWidth = Number(widthWidget.value) || 2048;
|
|
518
|
+
if (Math.abs(oldWidth - numValue) < 0.5) {
|
|
519
|
+
// widget.value 已经等于新值,说明值已经更新
|
|
520
|
+
// 在这种情况下,我们无法获取真正的旧值,使用新值作为参考(会假设保持当前比例)
|
|
521
|
+
oldWidth = numValue;
|
|
522
|
+
}
|
|
523
|
+
// 使用新的计算函数,同时考虑总像素和宽高比约束
|
|
524
|
+
// 传入旧宽度以正确计算宽高比
|
|
525
|
+
const adjusted = calculateNewDimensions(
|
|
526
|
+
numValue,
|
|
527
|
+
currentHeight,
|
|
528
|
+
true,
|
|
529
|
+
oldWidth
|
|
530
|
+
);
|
|
531
|
+
|
|
532
|
+
console.log("[Seedream4.5] 计算完成", {
|
|
533
|
+
width: numValue,
|
|
534
|
+
currentHeight,
|
|
535
|
+
adjustedHeight: adjusted.height,
|
|
536
|
+
adjusted,
|
|
537
|
+
heightDiff: Math.abs(adjusted.height - currentHeight),
|
|
538
|
+
widthDiff: Math.abs(adjusted.width - numValue),
|
|
539
|
+
willUpdateHeight: Math.abs(adjusted.height - currentHeight) > 0.5,
|
|
540
|
+
willUpdateWidth: Math.abs(adjusted.width - numValue) > 0.5,
|
|
541
|
+
});
|
|
542
|
+
|
|
543
|
+
if (Math.abs(adjusted.height - currentHeight) > 0.5) {
|
|
544
|
+
console.log("[Seedream4.5] 更新高度", {
|
|
545
|
+
from: currentHeight,
|
|
546
|
+
to: adjusted.height,
|
|
547
|
+
beforeUpdate: heightWidget.value,
|
|
548
|
+
});
|
|
549
|
+
// 直接设置值并调用callback
|
|
550
|
+
heightWidget.value = adjusted.height;
|
|
551
|
+
console.log("[Seedream4.5] 高度值已更新", {
|
|
552
|
+
afterUpdate: heightWidget.value,
|
|
553
|
+
});
|
|
554
|
+
if (currentHeightCallback) {
|
|
555
|
+
currentHeightCallback.call(heightWidget, adjusted.height);
|
|
556
|
+
console.log("[Seedream4.5] 高度callback已调用");
|
|
557
|
+
}
|
|
558
|
+
} else {
|
|
559
|
+
console.log("[Seedream4.5] 跳过高度更新", {
|
|
560
|
+
reason: "高度差值太小",
|
|
561
|
+
currentHeight,
|
|
562
|
+
adjustedHeight: adjusted.height,
|
|
563
|
+
diff: Math.abs(adjusted.height - currentHeight),
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
if (Math.abs(adjusted.width - numValue) > 0.5) {
|
|
568
|
+
console.log("[Seedream4.5] 更新宽度", {
|
|
569
|
+
from: numValue,
|
|
570
|
+
to: adjusted.width,
|
|
571
|
+
beforeUpdate: widthWidget.value,
|
|
572
|
+
});
|
|
573
|
+
widthWidget.value = adjusted.width;
|
|
574
|
+
console.log("[Seedream4.5] 宽度值已更新", {
|
|
575
|
+
afterUpdate: widthWidget.value,
|
|
576
|
+
});
|
|
577
|
+
if (currentWidthCallback) {
|
|
578
|
+
currentWidthCallback.call(widthWidget, adjusted.width);
|
|
579
|
+
console.log("[Seedream4.5] 宽度callback已调用");
|
|
580
|
+
}
|
|
581
|
+
} else {
|
|
582
|
+
console.log("[Seedream4.5] 跳过宽度更新", {
|
|
583
|
+
reason: "宽度差值太小",
|
|
584
|
+
numValue,
|
|
585
|
+
adjustedWidth: adjusted.width,
|
|
586
|
+
diff: Math.abs(adjusted.width - numValue),
|
|
587
|
+
});
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
if (node && node.setDirtyCanvas) {
|
|
591
|
+
node.setDirtyCanvas(true, true);
|
|
592
|
+
}
|
|
593
|
+
} catch (error) {
|
|
594
|
+
console.error("[Seedream4.5] 更新宽度时出错:", error);
|
|
595
|
+
} finally {
|
|
596
|
+
isUpdating = false;
|
|
597
|
+
}
|
|
598
|
+
};
|
|
599
|
+
|
|
600
|
+
// 处理高度变化的函数
|
|
601
|
+
const handleHeightChange = (newValue) => {
|
|
602
|
+
const customSize = isCustomSize();
|
|
603
|
+
console.log("[Seedream4.5] handleHeightChange 被调用", {
|
|
604
|
+
newValue,
|
|
605
|
+
isCustomSize: customSize,
|
|
606
|
+
isUpdating,
|
|
607
|
+
sizeWidgetValue: sizeWidget?.value,
|
|
608
|
+
sizeWidgetExists: !!sizeWidget,
|
|
609
|
+
});
|
|
610
|
+
|
|
611
|
+
if (!customSize || isUpdating) {
|
|
612
|
+
console.log("[Seedream4.5] 跳过处理", {
|
|
613
|
+
reason: !customSize ? "size不是Custom" : "正在更新中",
|
|
614
|
+
});
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
isUpdating = true;
|
|
619
|
+
try {
|
|
620
|
+
const numValue = Number(newValue);
|
|
621
|
+
if (isNaN(numValue)) {
|
|
622
|
+
return;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
const currentWidth = Number(widthWidget.value) || 2048;
|
|
626
|
+
// 获取旧高度:类似宽度的处理
|
|
627
|
+
let oldHeight = Number(heightWidget.value) || 2048;
|
|
628
|
+
if (Math.abs(oldHeight - numValue) < 0.5) {
|
|
629
|
+
// widget.value 已经等于新值,说明值已经更新
|
|
630
|
+
// 使用新值作为参考(会假设保持当前比例)
|
|
631
|
+
oldHeight = numValue;
|
|
632
|
+
}
|
|
633
|
+
// 使用新的计算函数,同时考虑总像素和宽高比约束
|
|
634
|
+
// 传入旧高度以正确计算宽高比
|
|
635
|
+
const adjusted = calculateNewDimensions(
|
|
636
|
+
numValue,
|
|
637
|
+
currentWidth,
|
|
638
|
+
false,
|
|
639
|
+
oldHeight
|
|
640
|
+
);
|
|
641
|
+
|
|
642
|
+
console.log("[Seedream4.5] 计算完成", {
|
|
643
|
+
height: numValue,
|
|
644
|
+
currentWidth,
|
|
645
|
+
adjustedWidth: adjusted.width,
|
|
646
|
+
adjusted,
|
|
647
|
+
});
|
|
648
|
+
|
|
649
|
+
if (Math.abs(adjusted.width - currentWidth) > 0.5) {
|
|
650
|
+
console.log("[Seedream4.5] 更新宽度", {
|
|
651
|
+
from: currentWidth,
|
|
652
|
+
to: adjusted.width,
|
|
653
|
+
});
|
|
654
|
+
widthWidget.value = adjusted.width;
|
|
655
|
+
if (currentWidthCallback) {
|
|
656
|
+
currentWidthCallback.call(widthWidget, adjusted.width);
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
if (Math.abs(adjusted.height - numValue) > 0.5) {
|
|
661
|
+
console.log("[Seedream4.5] 更新高度", {
|
|
662
|
+
from: numValue,
|
|
663
|
+
to: adjusted.height,
|
|
664
|
+
});
|
|
665
|
+
heightWidget.value = adjusted.height;
|
|
666
|
+
if (currentHeightCallback) {
|
|
667
|
+
currentHeightCallback.call(heightWidget, adjusted.height);
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
if (node && node.setDirtyCanvas) {
|
|
672
|
+
node.setDirtyCanvas(true, true);
|
|
673
|
+
}
|
|
674
|
+
} catch (error) {
|
|
675
|
+
console.error("[Seedream4.5] 更新高度时出错:", error);
|
|
676
|
+
} finally {
|
|
677
|
+
isUpdating = false;
|
|
678
|
+
}
|
|
679
|
+
};
|
|
680
|
+
|
|
681
|
+
// 使用链式callback包装width widget的callback
|
|
682
|
+
widthWidget.callback = chainCallback(
|
|
683
|
+
currentWidthCallback,
|
|
684
|
+
function (value) {
|
|
685
|
+
console.log("[Seedream4.5] width callback 被调用", {
|
|
686
|
+
value,
|
|
687
|
+
widgetValue: widthWidget.value,
|
|
688
|
+
});
|
|
689
|
+
// 在回调中,value 是新值,widthWidget.value 可能还是旧值(取决于ComfyUI的实现)
|
|
690
|
+
// 为了安全,我们使用 value 作为新值,widthWidget.value 作为旧值的参考
|
|
691
|
+
handleWidthChange(value);
|
|
692
|
+
}
|
|
693
|
+
);
|
|
694
|
+
|
|
695
|
+
// 使用链式callback包装height widget的callback
|
|
696
|
+
heightWidget.callback = chainCallback(
|
|
697
|
+
currentHeightCallback,
|
|
698
|
+
function (value) {
|
|
699
|
+
console.log("[Seedream4.5] height callback 被调用", {
|
|
700
|
+
value,
|
|
701
|
+
widgetValue: heightWidget.value,
|
|
702
|
+
});
|
|
703
|
+
handleHeightChange(value);
|
|
704
|
+
}
|
|
705
|
+
);
|
|
706
|
+
|
|
707
|
+
// 监听 size widget 的变化,当切换到 Custom 时,重新验证宽高
|
|
708
|
+
if (sizeWidget) {
|
|
709
|
+
const originalSizeCallback = sizeWidget.callback;
|
|
710
|
+
sizeWidget.callback = chainCallback(
|
|
711
|
+
originalSizeCallback,
|
|
712
|
+
function (value) {
|
|
713
|
+
if (value === "Custom") {
|
|
714
|
+
const currentWidth = Number(widthWidget.value) || 2048;
|
|
715
|
+
const currentHeight = Number(heightWidget.value) || 2048;
|
|
716
|
+
const adjusted = validateAndAdjustDimensions(
|
|
717
|
+
currentWidth,
|
|
718
|
+
currentHeight
|
|
719
|
+
);
|
|
720
|
+
|
|
721
|
+
if (
|
|
722
|
+
Math.abs(adjusted.width - currentWidth) > 0.5 ||
|
|
723
|
+
Math.abs(adjusted.height - currentHeight) > 0.5
|
|
724
|
+
) {
|
|
725
|
+
isUpdating = true;
|
|
726
|
+
try {
|
|
727
|
+
widthWidget.value = adjusted.width;
|
|
728
|
+
heightWidget.value = adjusted.height;
|
|
729
|
+
if (currentWidthCallback) {
|
|
730
|
+
currentWidthCallback.call(widthWidget, adjusted.width);
|
|
731
|
+
}
|
|
732
|
+
if (currentHeightCallback) {
|
|
733
|
+
currentHeightCallback.call(heightWidget, adjusted.height);
|
|
734
|
+
}
|
|
735
|
+
if (node && node.setDirtyCanvas) {
|
|
736
|
+
node.setDirtyCanvas(true, true);
|
|
737
|
+
}
|
|
738
|
+
} finally {
|
|
739
|
+
isUpdating = false;
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
);
|
|
745
|
+
}
|
|
746
|
+
} catch (error) {
|
|
747
|
+
console.error("[Seedream4.5] 初始化宽高比联动失败:", error);
|
|
748
|
+
}
|
|
749
|
+
}, 200); // 增加延迟,确保ComfyUI的setupNodeWidgetCallbacks已经执行
|
|
750
|
+
},
|
|
751
|
+
});
|