refactor: share matting model status checks
This commit is contained in:
+4
-8
@@ -13,6 +13,7 @@ import { createModuleLogger } from "./log_system/log_funcs.js";
|
||||
import { showErrorNotification, showSuccessNotification, showInfoNotification, showWarningNotification } from "./utils/NotificationUtils.js";
|
||||
import { iconLoader, LAYERFORGE_TOOLS } from "./utils/IconLoader.js";
|
||||
import { exportCanvasImage } from "./utils/CanvasExportUtils.js";
|
||||
import { fetchMattingModelStatus } from "./utils/MattingUtils.js";
|
||||
import { setupSAMDetectorHook } from "./SAMDetectorIntegration.js";
|
||||
const log = createModuleLogger('Canvas_view');
|
||||
const MATTING_SETTINGS_STORAGE_KEY = 'layerforge.matting.settings';
|
||||
@@ -188,9 +189,8 @@ async function createCanvasWidget(node, widget, app) {
|
||||
let modelOptions = [];
|
||||
let modelStatusMessage = 'Model options are loaded from ComfyUI background-removal storage.';
|
||||
try {
|
||||
const response = await fetch('/matting/check-model');
|
||||
if (response.ok) {
|
||||
const status = await response.json();
|
||||
const { ok, data: status } = await fetchMattingModelStatus();
|
||||
if (ok) {
|
||||
if (Array.isArray(status.models)) {
|
||||
modelOptions = status.models.filter((option) => (option && typeof option.path === 'string' && typeof option.label === 'string'));
|
||||
}
|
||||
@@ -655,11 +655,7 @@ async function createCanvasWidget(node, widget, app) {
|
||||
const mattingSettings = loadMattingSettings();
|
||||
try {
|
||||
// First check if model is available
|
||||
const modelCheckUrl = mattingSettings.modelPath
|
||||
? `/matting/check-model?model_path=${encodeURIComponent(mattingSettings.modelPath)}`
|
||||
: "/matting/check-model";
|
||||
const modelCheckResponse = await fetch(modelCheckUrl);
|
||||
const modelStatus = await modelCheckResponse.json();
|
||||
const { data: modelStatus } = await fetchMattingModelStatus(mattingSettings.modelPath);
|
||||
if (!modelStatus.available) {
|
||||
switch (modelStatus.reason) {
|
||||
case 'missing_dependency':
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
export async function fetchMattingModelStatus(modelPath) {
|
||||
const query = modelPath
|
||||
? `?model_path=${encodeURIComponent(modelPath)}`
|
||||
: '';
|
||||
const response = await fetch(`/matting/check-model${query}`);
|
||||
const data = await response.json();
|
||||
return { ok: response.ok, data };
|
||||
}
|
||||
+4
-8
@@ -19,6 +19,7 @@ import {createModuleLogger} from "./log_system/log_funcs.js";
|
||||
import {showErrorNotification, showSuccessNotification, showInfoNotification, showWarningNotification} from "./utils/NotificationUtils.js";
|
||||
import { iconLoader, LAYERFORGE_TOOLS } from "./utils/IconLoader.js";
|
||||
import { exportCanvasImage, type CanvasExportAction, type CanvasExportVariant } from "./utils/CanvasExportUtils.js";
|
||||
import { fetchMattingModelStatus } from "./utils/MattingUtils.js";
|
||||
import { registerImageInClipspace, startSAMDetectorMonitoring, setupSAMDetectorHook } from "./SAMDetectorIntegration.js";
|
||||
import type { ComfyNode, Layer, AddMode } from './types';
|
||||
|
||||
@@ -261,9 +262,8 @@ async function createCanvasWidget(node: ComfyNode, widget: any, app: ComfyApp):
|
||||
let modelStatusMessage = 'Model options are loaded from ComfyUI background-removal storage.';
|
||||
|
||||
try {
|
||||
const response = await fetch('/matting/check-model');
|
||||
if (response.ok) {
|
||||
const status = await response.json() as { models?: MattingModelOption[] };
|
||||
const { ok, data: status } = await fetchMattingModelStatus<MattingModelOption>();
|
||||
if (ok) {
|
||||
if (Array.isArray(status.models)) {
|
||||
modelOptions = status.models.filter((option) => (
|
||||
option && typeof option.path === 'string' && typeof option.label === 'string'
|
||||
@@ -815,11 +815,7 @@ async function createCanvasWidget(node: ComfyNode, widget: any, app: ComfyApp):
|
||||
|
||||
try {
|
||||
// First check if model is available
|
||||
const modelCheckUrl = mattingSettings.modelPath
|
||||
? `/matting/check-model?model_path=${encodeURIComponent(mattingSettings.modelPath)}`
|
||||
: "/matting/check-model";
|
||||
const modelCheckResponse = await fetch(modelCheckUrl);
|
||||
const modelStatus = await modelCheckResponse.json();
|
||||
const { data: modelStatus } = await fetchMattingModelStatus(mattingSettings.modelPath);
|
||||
|
||||
if (!modelStatus.available) {
|
||||
switch (modelStatus.reason) {
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
export interface MattingModelStatus<Model = unknown> {
|
||||
available: boolean;
|
||||
reason: string;
|
||||
message: string;
|
||||
models?: Model[];
|
||||
}
|
||||
|
||||
export interface MattingModelStatusResult<Model = unknown> {
|
||||
ok: boolean;
|
||||
data: MattingModelStatus<Model>;
|
||||
}
|
||||
|
||||
export async function fetchMattingModelStatus<Model = unknown>(
|
||||
modelPath?: string,
|
||||
): Promise<MattingModelStatusResult<Model>> {
|
||||
const query = modelPath
|
||||
? `?model_path=${encodeURIComponent(modelPath)}`
|
||||
: '';
|
||||
const response = await fetch(`/matting/check-model${query}`);
|
||||
const data = await response.json() as MattingModelStatus<Model>;
|
||||
|
||||
return { ok: response.ok, data };
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import { readFile } from 'node:fs/promises';
|
||||
import test from 'node:test';
|
||||
|
||||
import { fetchMattingModelStatus } from '../js/utils/MattingUtils.js';
|
||||
|
||||
const canvasViewSource = await readFile(
|
||||
new URL('../src/CanvasView.ts', import.meta.url),
|
||||
'utf8',
|
||||
);
|
||||
|
||||
test('shared matting status helper preserves endpoint, payload, and errors', async () => {
|
||||
const originalFetch = globalThis.fetch;
|
||||
const requests = [];
|
||||
|
||||
globalThis.fetch = async (url) => {
|
||||
requests.push(url);
|
||||
const isErrorResponse = requests.length === 3;
|
||||
return {
|
||||
ok: !isErrorResponse,
|
||||
json: async () => ({
|
||||
available: !isErrorResponse,
|
||||
reason: isErrorResponse ? 'error' : 'ready',
|
||||
message: isErrorResponse ? 'failed' : 'ready',
|
||||
models: [],
|
||||
}),
|
||||
};
|
||||
};
|
||||
|
||||
try {
|
||||
const automatic = await fetchMattingModelStatus();
|
||||
const selected = await fetchMattingModelStatus('models/my model.safetensors');
|
||||
const failed = await fetchMattingModelStatus('models/missing.safetensors');
|
||||
|
||||
assert.deepEqual(requests, [
|
||||
'/matting/check-model',
|
||||
'/matting/check-model?model_path=models%2Fmy%20model.safetensors',
|
||||
'/matting/check-model?model_path=models%2Fmissing.safetensors',
|
||||
]);
|
||||
assert.equal(automatic.ok, true);
|
||||
assert.equal(automatic.data.reason, 'ready');
|
||||
assert.equal(selected.data.message, 'ready');
|
||||
assert.equal(failed.ok, false);
|
||||
assert.equal(failed.data.reason, 'error');
|
||||
|
||||
globalThis.fetch = async () => {
|
||||
throw new Error('offline');
|
||||
};
|
||||
await assert.rejects(fetchMattingModelStatus(), /offline/);
|
||||
} finally {
|
||||
globalThis.fetch = originalFetch;
|
||||
}
|
||||
});
|
||||
|
||||
test('CanvasView delegates both model status checks to the shared helper', () => {
|
||||
assert.match(canvasViewSource, /fetchMattingModelStatus<MattingModelOption>\(\)/);
|
||||
assert.match(canvasViewSource, /fetchMattingModelStatus\(mattingSettings\.modelPath\)/);
|
||||
assert.doesNotMatch(canvasViewSource, /const modelCheckUrl =/);
|
||||
assert.doesNotMatch(canvasViewSource, /fetch\(['"]\/matting\/check-model/);
|
||||
});
|
||||
Reference in New Issue
Block a user