bug fix
This commit is contained in:
+327
-6
@@ -24,9 +24,11 @@ const translations = {
|
||||
noFilesSelected: "No files selected",
|
||||
removeFile: "Remove",
|
||||
zipOption: "Download as ZIP",
|
||||
processingFiles: "Processing {0}/{1} files...",
|
||||
processingFiles: "Processing files: {0}/{1}",
|
||||
successMultipleFiles: "Successfully processed {0} files",
|
||||
clearButton: "Clear All"
|
||||
clearButton: "Clear All",
|
||||
successSingleFile: "Successfully processed 1 file",
|
||||
errorProcessing: "Error occurred while processing files"
|
||||
},
|
||||
zh: {
|
||||
title: "ComfyUI 工作流修复工具",
|
||||
@@ -52,9 +54,11 @@ const translations = {
|
||||
noFilesSelected: "未选择文件",
|
||||
removeFile: "移除",
|
||||
zipOption: "以ZIP格式下载",
|
||||
processingFiles: "正在处理 {0}/{1} 个文件...",
|
||||
successMultipleFiles: "成功处理 {0} 个文件",
|
||||
clearButton: "清除全部"
|
||||
processingFiles: "正在处理文件:{0}/{1}",
|
||||
successMultipleFiles: "成功处理{0}个文件",
|
||||
clearButton: "清除全部",
|
||||
successSingleFile: "成功处理1个文件",
|
||||
errorProcessing: "处理文件时发生错误"
|
||||
}
|
||||
};
|
||||
|
||||
@@ -70,6 +74,13 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
setupEventListeners();
|
||||
updateHeaderImage();
|
||||
initializePathOptions();
|
||||
|
||||
// 初始化按钮文本
|
||||
const t = translations[currentLanguage];
|
||||
const fixButton = document.getElementById('fixButton');
|
||||
if (fixButton) {
|
||||
fixButton.textContent = t.fixButton;
|
||||
}
|
||||
});
|
||||
|
||||
// 添加路径选项初始化函数
|
||||
@@ -161,7 +172,13 @@ function updateLanguage() {
|
||||
// 更新所有文本内容
|
||||
document.getElementById('title').textContent = t.title;
|
||||
document.getElementById('dragText').textContent = t.dragText;
|
||||
document.getElementById('fixButton').textContent = t.fixButton;
|
||||
|
||||
// 确保更新 Fix & Download 按钮文本
|
||||
const fixButton = document.getElementById('fixButton');
|
||||
if (fixButton) {
|
||||
fixButton.textContent = t.fixButton;
|
||||
}
|
||||
|
||||
document.getElementById('langBtn').textContent = t.switchLang;
|
||||
document.getElementById('clearButton').textContent = t.clearButton;
|
||||
document.getElementById('zipLabel').textContent = t.zipOption;
|
||||
@@ -244,6 +261,10 @@ function updateFileList() {
|
||||
clearButton.disabled = true;
|
||||
document.getElementById('fixButton').disabled = true;
|
||||
filesContainer.classList.add('no-scroll');
|
||||
|
||||
// 清除错误和成功消息
|
||||
document.getElementById('error').textContent = '';
|
||||
document.getElementById('success').textContent = '';
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -274,8 +295,24 @@ function clearFiles() {
|
||||
selectedFiles = [];
|
||||
updateFileList();
|
||||
document.getElementById('fileInput').value = '';
|
||||
|
||||
// 清除错误和成功消息
|
||||
document.getElementById('error').textContent = '';
|
||||
document.getElementById('success').textContent = '';
|
||||
|
||||
// 禁用相关按钮
|
||||
document.getElementById('clearButton').disabled = true;
|
||||
document.getElementById('fixButton').disabled = true;
|
||||
|
||||
// 重置文件拖放区域的状态
|
||||
const dropZone = document.getElementById('dropZone');
|
||||
dropZone.classList.remove('dragover');
|
||||
|
||||
// 更新文件列表显示
|
||||
const t = translations[currentLanguage];
|
||||
const filesContainer = document.getElementById('selectedFiles');
|
||||
filesContainer.innerHTML = `<div class="file-item"><span class="file-name">${t.noFilesSelected}</span></div>`;
|
||||
filesContainer.classList.add('no-scroll');
|
||||
}
|
||||
|
||||
// UI更新函数
|
||||
@@ -289,4 +326,288 @@ function updateUI() {
|
||||
const fixType = document.getElementById('fixType').value;
|
||||
updateDescription();
|
||||
document.getElementById('pathOptions').classList.toggle('show', fixType === 'path');
|
||||
}
|
||||
|
||||
// 添加下载功能
|
||||
async function fixAndDownload() {
|
||||
const t = translations[currentLanguage];
|
||||
const fixType = document.getElementById('fixType').value;
|
||||
const useZip = document.getElementById('zipDownload').checked;
|
||||
const error = document.getElementById('error');
|
||||
const success = document.getElementById('success');
|
||||
|
||||
error.textContent = '';
|
||||
success.textContent = '';
|
||||
|
||||
if (selectedFiles.length === 0) {
|
||||
error.textContent = t.errorSelectFile;
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (useZip) {
|
||||
// ZIP下载逻辑保持不变
|
||||
const zip = new JSZip();
|
||||
|
||||
for (let i = 0; i < selectedFiles.length; i++) {
|
||||
const file = selectedFiles[i];
|
||||
const content = await file.text();
|
||||
const fixedContent = fixWorkflowContent(content, fixType);
|
||||
const fileName = file.name.replace('.json', '_fixed.json');
|
||||
zip.file(fileName, fixedContent);
|
||||
}
|
||||
|
||||
const blob = await zip.generateAsync({type: 'blob'});
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = 'fixed_workflows.zip';
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
URL.revokeObjectURL(url);
|
||||
|
||||
success.textContent = t.successMultipleFiles.replace('{0}', selectedFiles.length);
|
||||
} else {
|
||||
// 修改后的单文件处理逻辑
|
||||
let processedCount = 0;
|
||||
const totalFiles = selectedFiles.length;
|
||||
const fixedFiles = [];
|
||||
|
||||
// 首先处理所有文件
|
||||
for (const file of selectedFiles) {
|
||||
try {
|
||||
const content = await file.text();
|
||||
const fixedContent = fixWorkflowContent(content, fixType);
|
||||
const fileName = file.name.replace('.json', '_fixed.json');
|
||||
fixedFiles.push({
|
||||
name: fileName,
|
||||
content: fixedContent
|
||||
});
|
||||
processedCount++;
|
||||
success.textContent = t.processingFiles
|
||||
.replace('{0}', processedCount)
|
||||
.replace('{1}', totalFiles);
|
||||
} catch (err) {
|
||||
console.error(`Error processing ${file.name}:`, err);
|
||||
}
|
||||
}
|
||||
|
||||
// 然后逐个下载处理好的文件
|
||||
for (let i = 0; i < fixedFiles.length; i++) {
|
||||
const file = fixedFiles[i];
|
||||
const blob = new Blob([file.content], { type: 'application/json' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
|
||||
await new Promise(resolve => {
|
||||
setTimeout(() => {
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = file.name;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
URL.revokeObjectURL(url);
|
||||
resolve();
|
||||
}, i * 800); // 增加间隔时间到800ms,确保文件能够正确下载
|
||||
});
|
||||
}
|
||||
|
||||
// 更新最终状态
|
||||
if (processedCount > 0) {
|
||||
success.textContent = processedCount === 1
|
||||
? t.successSingleFile
|
||||
: t.successMultipleFiles.replace('{0}', processedCount);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
error.textContent = t.errorProcessing;
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
// 定义需要进行路径转换的节点类型
|
||||
const PATH_CONVERSION_NODES = new Set([
|
||||
"CheckpointLoaderSimple",
|
||||
"LoraLoader",
|
||||
"ControlNetLoader",
|
||||
"UNETLoader",
|
||||
"VAELoader",
|
||||
"CLIPLoader",
|
||||
"DiffControlNetLoader",
|
||||
"ModelLoader",
|
||||
"LoadDiffusionModel",
|
||||
"CheckpointLoader",
|
||||
"DiffusersLoader",
|
||||
"LoraLoaderModelOnly",
|
||||
"VAEDecode",
|
||||
"VAEEncode",
|
||||
"CLIPVisionLoader",
|
||||
"StyleModelLoader",
|
||||
"UpscaleModelLoader",
|
||||
"Load LoRA",
|
||||
"Lora List",
|
||||
"LoRA Stacker",
|
||||
"GridLoras",
|
||||
"CheckpointLoader|pysssss",
|
||||
"LoraLoader|pysssss",
|
||||
"LoraSave",
|
||||
"DualCLIPLoader",
|
||||
"DualUNETLoader",
|
||||
"DualVAELoader",
|
||||
"DualCLIPVisionLoader",
|
||||
"DualStyleModelLoader",
|
||||
"DualUpscaleModelLoader"
|
||||
]);
|
||||
|
||||
function fixWorkflowContent(content, fixType) {
|
||||
const workflow = JSON.parse(content);
|
||||
|
||||
if (fixType === 'namespace') {
|
||||
// 遍历所有节点
|
||||
for (const nodeId in workflow.nodes) {
|
||||
const node = workflow.nodes[nodeId];
|
||||
if (node && node.type) {
|
||||
// 只修改 nodeMapping 中定义的节点
|
||||
if (nodeMapping[node.type]) {
|
||||
node.type = nodeMapping[node.type];
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (fixType === 'path') {
|
||||
// 获取选中的路径类型
|
||||
const pathType = document.querySelector('input[name="pathType"]:checked').value;
|
||||
let conversionDirection = pathType;
|
||||
|
||||
// 处理自动检测
|
||||
if (pathType === 'auto') {
|
||||
const currentFormat = detectPathFormat(workflow);
|
||||
conversionDirection = currentFormat === 'unix' ? 'toWindows' : 'toUnix';
|
||||
}
|
||||
|
||||
// 遍历所有节点
|
||||
for (const nodeId in workflow.nodes) {
|
||||
const node = workflow.nodes[nodeId];
|
||||
const nodeType = node.type || node.class_type;
|
||||
|
||||
// 如果是需要处理的节点类型
|
||||
if (PATH_CONVERSION_NODES.has(nodeType) && node.widgets_values) {
|
||||
node.widgets_values = node.widgets_values.map(value => {
|
||||
if (typeof value === 'string' && !value.includes('base64')) {
|
||||
// 转换路径分隔符
|
||||
if (conversionDirection === 'toUnix') {
|
||||
return value.replace(/\\/g, '/');
|
||||
} else if (conversionDirection === 'toWindows') {
|
||||
return value.replace(/\//g, '\\');
|
||||
}
|
||||
} else if (typeof value === 'object' && value?.content) {
|
||||
// 处理特殊格式
|
||||
if (!value.content.includes('base64')) {
|
||||
const newContent = conversionDirection === 'toUnix'
|
||||
? value.content.replace(/\\/g, '/')
|
||||
: value.content.replace(/\//g, '\\');
|
||||
return { ...value, content: newContent };
|
||||
}
|
||||
}
|
||||
return value;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return JSON.stringify(workflow, null, 2);
|
||||
}
|
||||
|
||||
// 检测路径格式
|
||||
function detectPathFormat(workflow) {
|
||||
let unixCount = 0;
|
||||
let windowsCount = 0;
|
||||
|
||||
for (const nodeId in workflow.nodes) {
|
||||
const node = workflow.nodes[nodeId];
|
||||
const nodeType = node.type || node.class_type;
|
||||
|
||||
if (PATH_CONVERSION_NODES.has(nodeType) && node.widgets_values) {
|
||||
node.widgets_values.forEach(value => {
|
||||
if (typeof value === 'string' && !value.includes('base64')) {
|
||||
const unixSlashes = (value.match(/\//g) || []).length;
|
||||
const windowsSlashes = (value.match(/\\/g) || []).length;
|
||||
|
||||
if (unixSlashes > windowsSlashes) {
|
||||
unixCount++;
|
||||
} else if (windowsSlashes > unixSlashes) {
|
||||
windowsCount++;
|
||||
}
|
||||
} else if (typeof value === 'object' && value?.content) {
|
||||
if (!value.content.includes('base64')) {
|
||||
const unixSlashes = (value.content.match(/\//g) || []).length;
|
||||
const windowsSlashes = (value.content.match(/\\/g) || []).length;
|
||||
|
||||
if (unixSlashes > windowsSlashes) {
|
||||
unixCount++;
|
||||
} else if (windowsSlashes > unixSlashes) {
|
||||
windowsCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return unixCount > windowsCount ? 'unix' : 'windows';
|
||||
}
|
||||
|
||||
// 更新 nodeMapping 对象
|
||||
const nodeMapping = {
|
||||
"MaskPreview": "PaintingCoder::MaskPreview",
|
||||
"ImageSizeCreator": "PaintingCoder::ImageSizeCreator",
|
||||
"ImageToBase64": "PaintingCoder::ImageToBase64",
|
||||
"ImageLatentCreator": "PaintingCoder::ImageLatentCreator",
|
||||
"DynamicImageCombiner": "PaintingCoder::DynamicImageCombiner",
|
||||
"DynamicMaskCombiner": "PaintingCoder::DynamicMaskCombiner",
|
||||
"ImageResolutionAdjuster": "PaintingCoder::ImageResolutionAdjuster",
|
||||
"TextCombiner": "PaintingCoder::TextCombiner",
|
||||
"ShowTextPlus": "PaintingCoder::ShowTextPlus",
|
||||
"SimpleTextInput": "PaintingCoder::SimpleTextInput",
|
||||
"MultilineTextInput": "PaintingCoder::MultilineTextInput",
|
||||
"RemoveEmptyLinesAndLeadingSpaces": "PaintingCoder::RemoveEmptyLinesAndLeadingSpaces",
|
||||
"ImageSwitch": "PaintingCoder::ImageSwitch",
|
||||
"TextSwitch": "PaintingCoder::TextSwitch",
|
||||
"MaskSwitch": "PaintingCoder::MaskSwitch",
|
||||
"LatentSwitch": "PaintingCoder::LatentSwitch",
|
||||
"WebImageLoader": "PaintingCoder::WebImageLoader",
|
||||
"MaskPreview+": "PaintingCoder::MaskPreview"
|
||||
};
|
||||
|
||||
function isPaintingCoderNode(type) {
|
||||
// PaintingCoder 节点的类名前缀列表
|
||||
const paintingCoderPrefixes = [
|
||||
'Prompt',
|
||||
'ControlNet',
|
||||
'Model',
|
||||
'VAE',
|
||||
'CLIP',
|
||||
'Lora',
|
||||
'Checkpoint',
|
||||
'UNET',
|
||||
'Save',
|
||||
'Preview',
|
||||
'KSampler',
|
||||
'EmptyLatent',
|
||||
'Conditioning',
|
||||
'Latent',
|
||||
'Image',
|
||||
'Text',
|
||||
'Mask',
|
||||
'Dynamic',
|
||||
'Simple',
|
||||
'Show',
|
||||
'Web'
|
||||
];
|
||||
|
||||
// 检查类名是否以任一前缀开头
|
||||
return paintingCoderPrefixes.some(prefix =>
|
||||
type.startsWith(prefix) ||
|
||||
type.startsWith('PaintingCoder::' + prefix)
|
||||
);
|
||||
}
|
||||
@@ -80,7 +80,7 @@
|
||||
<a href="https://github.com/jammyfu/ComfyUI_PaintingCoderUtils" target="_blank" title="Visit PaintingCoder on GitHub">
|
||||
<img src="../images/workflow_fix_top_banner01.jpg" alt="Painting Coder Logo">
|
||||
</a>
|
||||
<span class="version-tag">Version 0.2.2</span>
|
||||
<span class="version-tag">Version 0.2.3</span>
|
||||
<div class="header-controls">
|
||||
<button class="header-btn theme-btn" onclick="toggleTheme()" title="Toggle theme">
|
||||
<!-- 月亮图标 -->
|
||||
|
||||
Reference in New Issue
Block a user