mirror of
https://github.com/Azornes/Comfyui-LayerForge.git
synced 2026-03-25 06:22:14 -03:00
Improve matting error handling and user feedback
Adds specific handling for JSONDecodeError during model loading in Python, returning a clear error message if the model config is corrupted. Updates the JS/TS frontends to show a custom error dialog with details and copy-to-clipboard functionality instead of a simple alert, and ensures spinner removal is safe. This improves user experience and troubleshooting for matting model errors.
This commit is contained in:
@@ -612,11 +612,11 @@ class BiRefNetMatting:
|
|||||||
"models")
|
"models")
|
||||||
|
|
||||||
def load_model(self, model_path):
|
def load_model(self, model_path):
|
||||||
|
from json.decoder import JSONDecodeError
|
||||||
try:
|
try:
|
||||||
if model_path not in self.model_cache:
|
if model_path not in self.model_cache:
|
||||||
full_model_path = os.path.join(self.base_path, "BiRefNet")
|
full_model_path = os.path.join(self.base_path, "BiRefNet")
|
||||||
log_info(f"Loading BiRefNet model from {full_model_path}...")
|
log_info(f"Loading BiRefNet model from {full_model_path}...")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.model = AutoModelForImageSegmentation.from_pretrained(
|
self.model = AutoModelForImageSegmentation.from_pretrained(
|
||||||
"ZhengPeng7/BiRefNet",
|
"ZhengPeng7/BiRefNet",
|
||||||
@@ -628,6 +628,13 @@ class BiRefNetMatting:
|
|||||||
self.model = self.model.cuda()
|
self.model = self.model.cuda()
|
||||||
self.model_cache[model_path] = self.model
|
self.model_cache[model_path] = self.model
|
||||||
log_info("Model loaded successfully from Hugging Face")
|
log_info("Model loaded successfully from Hugging Face")
|
||||||
|
except JSONDecodeError as e:
|
||||||
|
log_error(f"JSONDecodeError: Failed to load model from {full_model_path}. The model's config.json may be corrupted.")
|
||||||
|
raise RuntimeError(
|
||||||
|
"The matting model's configuration file (config.json) appears to be corrupted. "
|
||||||
|
f"Please manually delete the directory '{full_model_path}' and try again. "
|
||||||
|
"This will force a fresh download of the model."
|
||||||
|
) from e
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log_error(f"Failed to load model from Hugging Face: {str(e)}")
|
log_error(f"Failed to load model from Hugging Face: {str(e)}")
|
||||||
# Re-raise with a more informative message
|
# Re-raise with a more informative message
|
||||||
@@ -799,6 +806,12 @@ async def matting(request):
|
|||||||
"error": "Network Connection Error",
|
"error": "Network Connection Error",
|
||||||
"details": "Failed to download the matting model from Hugging Face. Please check your internet connection."
|
"details": "Failed to download the matting model from Hugging Face. Please check your internet connection."
|
||||||
}, status=400)
|
}, status=400)
|
||||||
|
except RuntimeError as e:
|
||||||
|
log_error(f"Runtime error during matting: {e}")
|
||||||
|
return web.json_response({
|
||||||
|
"error": "Matting Model Error",
|
||||||
|
"details": str(e)
|
||||||
|
}, status=500)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log_exception(f"Error in matting endpoint: {e}")
|
log_exception(f"Error in matting endpoint: {e}")
|
||||||
# Check for offline error message from Hugging Face
|
# Check for offline error message from Hugging Face
|
||||||
|
|||||||
@@ -339,11 +339,15 @@ async function createCanvasWidget(node, widget, app) {
|
|||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
log.error("Matting error:", error);
|
log.error("Matting error:", error);
|
||||||
alert(`Matting process failed:\n\n${error.message}`);
|
const errorMessage = error.message || "An unknown error occurred.";
|
||||||
|
const errorDetails = error.stack || (error.details ? JSON.stringify(error.details, null, 2) : "No details available.");
|
||||||
|
showErrorDialog(errorMessage, errorDetails);
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
button.classList.remove('loading');
|
button.classList.remove('loading');
|
||||||
button.removeChild(spinner);
|
if (button.contains(spinner)) {
|
||||||
|
button.removeChild(spinner);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
@@ -669,6 +673,55 @@ async function createCanvasWidget(node, widget, app) {
|
|||||||
panel: controlPanel
|
panel: controlPanel
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
function showErrorDialog(message, details) {
|
||||||
|
const dialog = $el("div.painter-dialog.error-dialog", {
|
||||||
|
style: {
|
||||||
|
position: 'fixed',
|
||||||
|
left: '50%',
|
||||||
|
top: '50%',
|
||||||
|
transform: 'translate(-50%, -50%)',
|
||||||
|
zIndex: '9999',
|
||||||
|
padding: '20px',
|
||||||
|
background: '#282828',
|
||||||
|
border: '1px solid #ff4444',
|
||||||
|
borderRadius: '8px',
|
||||||
|
minWidth: '400px',
|
||||||
|
maxWidth: '80vw',
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
$el("h3", { textContent: "Matting Error", style: { color: "#ff4444", marginTop: "0" } }),
|
||||||
|
$el("p", { textContent: message, style: { color: "white" } }),
|
||||||
|
$el("pre.error-details", {
|
||||||
|
textContent: details,
|
||||||
|
style: {
|
||||||
|
background: "#1e1e1e",
|
||||||
|
border: "1px solid #444",
|
||||||
|
padding: "10px",
|
||||||
|
maxHeight: "300px",
|
||||||
|
overflowY: "auto",
|
||||||
|
whiteSpace: "pre-wrap",
|
||||||
|
wordBreak: "break-all",
|
||||||
|
color: "#ccc"
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
$el("div.dialog-buttons", { style: { textAlign: "right", marginTop: "20px" } }, [
|
||||||
|
$el("button", {
|
||||||
|
textContent: "Copy Details",
|
||||||
|
onclick: () => {
|
||||||
|
navigator.clipboard.writeText(details)
|
||||||
|
.then(() => alert("Error details copied to clipboard!"))
|
||||||
|
.catch(err => alert("Failed to copy details: " + err));
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
$el("button", {
|
||||||
|
textContent: "Close",
|
||||||
|
style: { marginLeft: "10px" },
|
||||||
|
onclick: () => document.body.removeChild(dialog)
|
||||||
|
})
|
||||||
|
])
|
||||||
|
]);
|
||||||
|
document.body.appendChild(dialog);
|
||||||
|
}
|
||||||
const canvasNodeInstances = new Map();
|
const canvasNodeInstances = new Map();
|
||||||
app.registerExtension({
|
app.registerExtension({
|
||||||
name: "Comfy.CanvasNode",
|
name: "Comfy.CanvasNode",
|
||||||
|
|||||||
@@ -371,10 +371,14 @@ async function createCanvasWidget(node: ComfyNode, widget: any, app: ComfyApp):
|
|||||||
canvas.saveState();
|
canvas.saveState();
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
log.error("Matting error:", error);
|
log.error("Matting error:", error);
|
||||||
alert(`Matting process failed:\n\n${error.message}`);
|
const errorMessage = error.message || "An unknown error occurred.";
|
||||||
|
const errorDetails = error.stack || (error.details ? JSON.stringify(error.details, null, 2) : "No details available.");
|
||||||
|
showErrorDialog(errorMessage, errorDetails);
|
||||||
} finally {
|
} finally {
|
||||||
button.classList.remove('loading');
|
button.classList.remove('loading');
|
||||||
button.removeChild(spinner);
|
if (button.contains(spinner)) {
|
||||||
|
button.removeChild(spinner);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
@@ -734,6 +738,57 @@ async function createCanvasWidget(node: ComfyNode, widget: any, app: ComfyApp):
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function showErrorDialog(message: string, details: string) {
|
||||||
|
const dialog = $el("div.painter-dialog.error-dialog", {
|
||||||
|
style: {
|
||||||
|
position: 'fixed',
|
||||||
|
left: '50%',
|
||||||
|
top: '50%',
|
||||||
|
transform: 'translate(-50%, -50%)',
|
||||||
|
zIndex: '9999',
|
||||||
|
padding: '20px',
|
||||||
|
background: '#282828',
|
||||||
|
border: '1px solid #ff4444',
|
||||||
|
borderRadius: '8px',
|
||||||
|
minWidth: '400px',
|
||||||
|
maxWidth: '80vw',
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
$el("h3", { textContent: "Matting Error", style: { color: "#ff4444", marginTop: "0" } }),
|
||||||
|
$el("p", { textContent: message, style: { color: "white" } }),
|
||||||
|
$el("pre.error-details", {
|
||||||
|
textContent: details,
|
||||||
|
style: {
|
||||||
|
background: "#1e1e1e",
|
||||||
|
border: "1px solid #444",
|
||||||
|
padding: "10px",
|
||||||
|
maxHeight: "300px",
|
||||||
|
overflowY: "auto",
|
||||||
|
whiteSpace: "pre-wrap",
|
||||||
|
wordBreak: "break-all",
|
||||||
|
color: "#ccc"
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
$el("div.dialog-buttons", { style: { textAlign: "right", marginTop: "20px" } }, [
|
||||||
|
$el("button", {
|
||||||
|
textContent: "Copy Details",
|
||||||
|
onclick: () => {
|
||||||
|
navigator.clipboard.writeText(details)
|
||||||
|
.then(() => alert("Error details copied to clipboard!"))
|
||||||
|
.catch(err => alert("Failed to copy details: " + err));
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
$el("button", {
|
||||||
|
textContent: "Close",
|
||||||
|
style: { marginLeft: "10px" },
|
||||||
|
onclick: () => document.body.removeChild(dialog)
|
||||||
|
})
|
||||||
|
])
|
||||||
|
]);
|
||||||
|
|
||||||
|
document.body.appendChild(dialog);
|
||||||
|
}
|
||||||
|
|
||||||
const canvasNodeInstances = new Map<number, CanvasWidget>();
|
const canvasNodeInstances = new Map<number, CanvasWidget>();
|
||||||
|
|
||||||
app.registerExtension({
|
app.registerExtension({
|
||||||
|
|||||||
Reference in New Issue
Block a user