refactor: centralize API calls through ResolverManagerDialog.fetchJson

Replace scattered api.fetchApi(...) + response.ok + response.json() boilerplate
across all web/modules with a unified this.fetchJson() helper defined on the
base dialog class.

Changes:
- dialog.fetchJson(url, options, context) handles headers, status checks, and
  JSON parsing in one place
- Extracts server-side error messages (errData.error) for better UX
- Optional silent flag suppresses notifications for background/refresh requests
- All callers simplified: async return, shorter bodies, consistent error handling
- BOM removed from workflow_update_methods.js and tabs_loaded_methods.js
This commit is contained in:
Dariusz L
2026-06-25 02:01:18 +02:00
parent d1a5ccfedb
commit d49ae0becc
12 changed files with 124 additions and 286 deletions
+22 -31
View File
@@ -1795,15 +1795,10 @@ export const queueMethods = {
return;
}
const response = await api.fetchApi('/model_resolver/resolve', {
const data = await this.fetchJson('/model_resolver/resolve', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ workflow, resolutions: applyResolutions })
});
if (!response.ok) throw new Error(`API error: ${response.status}`);
const data = await response.json();
}, 'Apply resolutions');
if (data.success) {
const optimisticData = this.getOptimisticAnalysisDataAfterApply(appliedSelections);
await this.refreshComfyModelCatalogAfterApply?.(data.workflow, applyResolutions);
@@ -2168,34 +2163,30 @@ export const queueMethods = {
return true;
},
refreshAnalysisInBackground(workflow, expectedSignature = null) {
async refreshAnalysisInBackground(workflow, expectedSignature = null) {
if (!workflow) return;
const signature = expectedSignature || this.getWorkflowSignature(workflow);
api.fetchApi('/model_resolver/analyze', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ workflow })
})
.then(response => {
if (!response.ok) throw new Error(`API error: ${response.status}`);
return response.json();
})
.then(data => {
if (signature && this.activeWorkflowSignature !== signature) return;
this.applyResolvedSelectionAliasesToAnalysisData(data);
this.cachedWorkflowSignature = signature || this.cachedWorkflowSignature;
this.cachedAnalysisData = data;
this.saveAnalysisCacheForActiveWorkflow?.();
try {
const data = await this.fetchJson('/model_resolver/analyze', {
method: 'POST',
body: JSON.stringify({ workflow }),
silent: true
}, 'Background analysis');
if (this.activeTab === 'missing' && this.contentElement && !this._analysisProgressToken) {
this.displayMissingModels(this.contentElement, data);
this.reconnectActiveDownloads?.();
}
})
.catch(error => {
console.warn('Model Resolver: background analysis refresh failed:', error);
});
if (signature && this.activeWorkflowSignature !== signature) return;
this.applyResolvedSelectionAliasesToAnalysisData(data);
this.cachedWorkflowSignature = signature || this.cachedWorkflowSignature;
this.cachedAnalysisData = data;
this.saveAnalysisCacheForActiveWorkflow?.();
if (this.activeTab === 'missing' && this.contentElement && !this._analysisProgressToken) {
this.displayMissingModels(this.contentElement, data);
this.reconnectActiveDownloads?.();
}
} catch (error) {
console.warn('Model Resolver: background analysis refresh failed:', error);
}
},
previewFooterMenuModels(models = []) {
@@ -41,20 +41,13 @@ export const resolveDownloadMethods = {
original_lora_name: ref.name || ref.original_path
}));
const response = await api.fetchApi('/model_resolver/resolve', {
const data = await this.fetchJson('/model_resolver/resolve', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
workflow,
resolutions: resolutions
})
});
if (!response.ok) {
throw new Error(`API error: ${response.status}`);
}
const data = await response.json();
}, 'Resolve model');
log.debug('Resolve response: success=', data.success, ' missing count:', data.workflow?.nodes?.length);
if (data.success) {
@@ -95,17 +88,10 @@ export const resolveDownloadMethods = {
}
// Analyze workflow first
const analyzeResponse = await api.fetchApi('/model_resolver/analyze', {
const analyzeData = await this.fetchJson('/model_resolver/analyze', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ workflow })
});
if (!analyzeResponse.ok) {
throw new Error(`API error: ${analyzeResponse.status}`);
}
const analyzeData = await analyzeResponse.json();
}, 'Analyze workflow');
const missingModels = analyzeData.missing_models || [];
// Collect all 100% matches
@@ -135,20 +121,13 @@ export const resolveDownloadMethods = {
}
// Apply resolutions
const resolveResponse = await api.fetchApi('/model_resolver/resolve', {
const resolveData = await this.fetchJson('/model_resolver/resolve', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
workflow,
resolutions
})
});
if (!resolveResponse.ok) {
throw new Error(`API error: ${resolveResponse.status}`);
}
const resolveData = await resolveResponse.json();
}, 'Resolve models');
if (resolveData.success) {
await this.refreshComfyModelCatalogAfterApply?.(resolveData.workflow, resolutions);
@@ -195,17 +174,10 @@ export const resolveDownloadMethods = {
}
// Analyze workflow first
const analyzeResponse = await api.fetchApi('/model_resolver/analyze', {
const analyzeData = await this.fetchJson('/model_resolver/analyze', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ workflow })
});
if (!analyzeResponse.ok) {
throw new Error(`API error: ${analyzeResponse.status}`);
}
const analyzeData = await analyzeResponse.json();
}, 'Analyze workflow');
const missingModels = analyzeData.missing_models || [];
// Collect models that need downloading:
@@ -1241,9 +1213,8 @@ export const resolveDownloadMethods = {
}
// Start download
const response = await api.fetchApi('/model_resolver/download', {
const data = await this.fetchJson('/model_resolver/download', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
url,
filename,
@@ -1255,13 +1226,8 @@ export const resolveDownloadMethods = {
hf_token: tokens.hf_token,
civitai_key: tokens.civitai_key
})
});
}, 'Start download');
if (!response.ok) {
throw new Error(`Download failed: ${response.status}`);
}
const data = await response.json();
if (!data.success) {
throw new Error(data.error || 'Download failed');
}
@@ -1398,12 +1364,7 @@ export const resolveDownloadMethods = {
if (!info) return;
try {
const response = await api.fetchApi(`/model_resolver/progress/${downloadId}`);
if (!response.ok) {
throw new Error('Failed to get progress');
}
const progress = await response.json();
const progress = await this.fetchJson(`/model_resolver/progress/${downloadId}`, { silent: true }, 'Get download progress');
const snapshot = this.rememberDownloadUiState(downloadId, info, progress, { isActive: true });
const { progressDiv, downloadBtn } = this.resolveDownloadUiElements(info);
const { missing, category } = info;
@@ -1523,13 +1484,9 @@ export const resolveDownloadMethods = {
*/
async cancelDownload(downloadId) {
try {
const response = await api.fetchApi(`/model_resolver/cancel/${downloadId}`, {
await this.fetchJson(`/model_resolver/cancel/${downloadId}`, {
method: 'POST'
});
if (!response.ok) {
throw new Error('Failed to cancel download');
}
}, 'Cancel download');
const info = this.activeDownloads[downloadId];
if (info) {
@@ -1727,22 +1684,12 @@ export const resolveDownloadMethods = {
log.debug('Model Resolver: Search request:', JSON.stringify(searchData));
const requestOptions = {
const data = await this.fetchJson('/model_resolver/search', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(searchData)
};
if (sourceController) {
requestOptions.signal = sourceController.signal;
}
const response = await api.fetchApi('/model_resolver/search', requestOptions);
if (!response.ok) {
throw new Error(`Search failed: ${response.status}`);
}
const data = await response.json();
body: JSON.stringify(searchData),
signal: sourceController?.signal,
silent: true
}, 'Search source');
log.debug('Model Resolver: Search response:', JSON.stringify(data));
if (!this.isBackgroundSearchRunActive(workflowKey, missingSearchKey, searchRunId)) {
@@ -1948,17 +1895,12 @@ export const resolveDownloadMethods = {
};
log.debug('resolveUrnAsync payload:', JSON.stringify(payload));
const response = await api.fetchApi('/model_resolver/search', {
data = await this.fetchJson('/model_resolver/search', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
});
log.debug('resolveUrnAsync response status:', response.status);
if (!response.ok) {
throw new Error(`URN resolve failed: ${response.status}`);
}
data = await response.json();
body: JSON.stringify(payload),
silent: true
}, 'Resolve URN');
log.debug('resolveUrnAsync search completed');
}
if (data) {
@@ -2490,20 +2432,13 @@ export const resolveDownloadMethods = {
},
async fetchLocalMatches(filename, category = '', forceRescan = false) {
const response = await api.fetchApi('/model_resolver/local-matches', {
return await this.fetchJson('/model_resolver/local-matches', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
filename,
category: category || '',
force_rescan: Boolean(forceRescan)
})
});
if (!response.ok) {
throw new Error(`Local match search failed: ${response.status}`);
}
return await response.json();
}, 'Fetch local matches');
}
};
+8 -9
View File
@@ -659,18 +659,17 @@ export class ModelResolver {
this.lastCheckedWorkflow = workflowHash;
// Call analyze endpoint to check for missing models
const response = await api.fetchApi('/model_resolver/analyze', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ workflow })
});
if (!response.ok) {
let data;
try {
data = await this.dialog.fetchJson('/model_resolver/analyze', {
method: 'POST',
body: JSON.stringify({ workflow }),
silent: true
}, 'Analyze workflow');
} catch (error) {
console.warn('Model Resolver: Failed to analyze workflow for missing models');
return;
}
const data = await response.json();
// Auto-open dialog if there are missing models
if (data.total_missing > 0) {
+9 -2
View File
@@ -222,7 +222,14 @@ export class ResolverManagerDialog extends ComfyDialog {
};
const response = await api.fetchApi(endpoint, fetchOptions);
if (!response.ok) {
throw new Error(`Server returned ${response.status}: ${response.statusText}`);
let errorMsg = `Server returned ${response.status}: ${response.statusText}`;
try {
const errData = await response.json();
if (errData && errData.error) {
errorMsg = errData.error;
}
} catch (_) {}
throw new Error(errorMsg);
}
if (response.status === 204) {
return null;
@@ -230,7 +237,7 @@ export class ResolverManagerDialog extends ComfyDialog {
return await response.json();
} catch (error) {
console.error(`Model Resolver: ${errorContext} failed:`, error);
if (typeof this.showNotification === 'function') {
if (!options.silent && typeof this.showNotification === 'function') {
this.showNotification(error.message || 'API request failed', 'error');
}
throw error;
+8 -23
View File
@@ -11,9 +11,7 @@ export const downloadTargetMethods = {
const force = options === true || Boolean(options?.force);
if (!force && this.allModels && this.allModels.length) return;
try {
const resp = await api.fetchApi(`/model_resolver/models${force ? '?force=1' : ''}`);
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
const models = await resp.json();
const models = await this.fetchJson(`/model_resolver/models${force ? '?force=1' : ''}`, {}, 'Load all models');
const list = Array.isArray(models) ? models : [];
// Build labels and sort alphabetically
this.allModels = list.map((m) => ({
@@ -29,9 +27,7 @@ export const downloadTargetMethods = {
async ensureDownloadDirectoriesLoaded() {
if (this.downloadDirectories) return;
try {
const resp = await api.fetchApi('/model_resolver/directories');
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
const directories = await resp.json();
const directories = await this.fetchJson('/model_resolver/directories', {}, 'Load download directories');
if (directories && typeof directories === 'object') {
this.downloadDirectories = Object.entries(directories).reduce((acc, [key, value]) => {
const normalizedKey = this.normalizeDownloadCategory(key);
@@ -52,9 +48,7 @@ export const downloadTargetMethods = {
async ensureCapabilitiesLoaded() {
if (this.capabilities) return;
try {
const resp = await api.fetchApi('/model_resolver/capabilities');
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
const data = await resp.json();
const data = await this.fetchJson('/model_resolver/capabilities', {}, 'Load capabilities');
this.capabilities = data && typeof data === 'object' ? data : { sources: {} };
} catch (e) {
console.warn('Model Resolver: could not load capabilities', e);
@@ -65,9 +59,7 @@ export const downloadTargetMethods = {
async ensureDownloadRootDirectoriesLoaded() {
if (this.downloadRootDirectories) return this.downloadRootDirectories;
try {
const resp = await api.fetchApi('/model_resolver/root-directories');
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
const data = await resp.json();
const data = await this.fetchJson('/model_resolver/root-directories', {}, 'Load root directories');
this.downloadRootDirectories = data && typeof data === 'object' ? data : {};
} catch (e) {
console.warn('Model Resolver: could not load root directories', e);
@@ -79,9 +71,7 @@ export const downloadTargetMethods = {
async ensureBaseModelsLoaded() {
if (this.baseModels) return;
try {
const resp = await api.fetchApi('/model_resolver/base-models');
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
const data = await resp.json();
const data = await this.fetchJson('/model_resolver/base-models', {}, 'Load base models config');
this.baseModels = data && typeof data === 'object' ? data : {};
} catch (e) {
console.warn('Model Resolver: could not load base-models config', e);
@@ -1500,9 +1490,7 @@ export const downloadTargetMethods = {
}
try {
const resp = await api.fetchApi(`/model_resolver/subfolders/${encodeURIComponent(key)}`);
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
const subfolders = await resp.json();
const subfolders = await this.fetchJson(`/model_resolver/subfolders/${encodeURIComponent(key)}`, {}, 'Load subfolders');
const list = Array.isArray(subfolders) ? subfolders : [];
this.downloadSubfolders.set(key, list);
return list;
@@ -2300,12 +2288,9 @@ export const downloadTargetMethods = {
async clearBackendSearchCaches({ throwOnError = false } = {}) {
try {
const response = await api.fetchApi('/model_resolver/clear-search-cache', {
await this.fetchJson('/model_resolver/clear-search-cache', {
method: 'POST'
});
if (!response.ok) {
throw new Error('Failed to clear backend search cache');
}
}, 'Clear search cache');
return true;
} catch (error) {
console.error('Model Resolver: Clear search cache error:', error);
+6 -12
View File
@@ -992,9 +992,10 @@ export const searchPanelMethods = {
timerRecord.inFlight = true;
try {
const response = await api.fetchApi(`/model_resolver/search-progress/${encodeURIComponent(progressId)}`);
if (!response.ok) return;
const progress = await response.json();
const progress = await this.fetchJson(`/model_resolver/search-progress/${encodeURIComponent(progressId)}`, {
silent: true
}, 'Poll search progress');
if (!progress) return;
if (!progress?.exists) return;
const latestProgress = state.sourceProgress?.[source];
if (!latestProgress || (latestProgress.status !== 'pending' && latestProgress.status !== 'running')) {
@@ -2096,17 +2097,10 @@ export const searchPanelMethods = {
civitai_candidate_limit: tokens.civitai_candidate_limit
};
const response = await api.fetchApi('/model_resolver/search', {
const data = await this.fetchJson('/model_resolver/search', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
});
if (!response.ok) {
throw new Error(`URN resolve failed: ${response.status}`);
}
const data = await response.json();
}, 'Resolve URN');
if (data.civitai) {
this.applyCivitaiUrnResult(missing, data.civitai);
}
+11 -14
View File
@@ -198,21 +198,18 @@ export const lifecycleGraphMethods = {
// Call analyze endpoint
const progressPromise = this.pollAnalysisProgress(analysisId, analysisId);
const response = await api.fetchApi('/model_resolver/analyze', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ workflow, analysis_id: analysisId, force_rescan: Boolean(forceRescan) })
});
if (this._analysisProgressToken === analysisId) {
this._analysisProgressToken = null;
let data;
try {
data = await this.fetchJson('/model_resolver/analyze', {
method: 'POST',
body: JSON.stringify({ workflow, analysis_id: analysisId, force_rescan: Boolean(forceRescan) })
}, 'Analyze workflow');
} finally {
if (this._analysisProgressToken === analysisId) {
this._analysisProgressToken = null;
}
await progressPromise;
}
await progressPromise;
if (!response.ok) {
throw new Error(`API error: ${response.status}`);
}
const data = await response.json();
if (this._workflowDataLoadToken === loadToken) {
this.cachedWorkflowSignature = workflowSignature;
this.cachedAnalysisData = data;
@@ -1,4 +1,4 @@
import { app } from "../../../../../scripts/app.js";
import { app } from "../../../../../scripts/app.js";
import { api } from "../../../../../scripts/api.js";
import { $el } from "../../../../../scripts/ui.js";
import { getSvgIcon } from "../../utils/icon_utils.js";
@@ -45,12 +45,7 @@ export const workflowUpdateMethods = {
},
async fetchComfyNodeDefs() {
const response = await api.fetchApi(`/object_info?model_resolver_refresh=${Date.now()}`);
if (!response.ok) {
throw new Error(`object_info refresh failed: ${response.status}`);
}
return await response.json();
return await this.fetchJson(`/object_info?model_resolver_refresh=${Date.now()}`, {}, 'Fetch ComfyUI node definitions');
},
shouldRefreshComfyModelCatalogForApply(workflow = null, resolutions = []) {
+4 -3
View File
@@ -220,14 +220,15 @@ export const renderFormatMethods = {
async pollAnalysisProgress(analysisId, token) {
while (this._analysisProgressToken === token) {
try {
const response = await api.fetchApi(`/model_resolver/analyze-progress/${analysisId}`);
const progress = await this.fetchJson(`/model_resolver/analyze-progress/${analysisId}`, {
silent: true
}, 'Poll analysis progress');
if (
response.ok &&
progress &&
this.contentElement &&
this._analysisProgressToken === token &&
this.activeTab === 'missing'
) {
const progress = await response.json();
this.contentElement.innerHTML = this.renderAnalysisProgress(progress);
if (progress.status === 'completed' || progress.status === 'error') {
if (this._analysisProgressToken === token) {
+8 -30
View File
@@ -831,25 +831,17 @@ export const modelInfoMethods = {
|| modelData?.folder_path
|| modelData?.download_directory
|| '';
const response = await api.fetchApi('/model_resolver/civitai-search', {
const data = await this.fetchJson('/model_resolver/civitai-search', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
filename: loraName,
category: modelData?.category || '',
resolved_path: resolvedPath
})
});
if (response.ok) {
const data = await response.json();
this.updateInfoDialogWithData(dialog, data);
} else {
this.updateInfoDialogError(dialog, 'Model not found on CivitAI');
}
}, 'Fetch CivitAI model info');
this.updateInfoDialogWithData(dialog, data);
} catch (e) {
console.error('Model Resolver: Error fetching model info:', e);
this.updateInfoDialogError(dialog, 'Error fetching info');
this.updateInfoDialogError(dialog, 'Error fetching info from CivitAI');
}
},
@@ -1415,22 +1407,15 @@ export const modelInfoMethods = {
try {
const tokens = this.getStoredTokens?.() || {};
const response = await api.fetchApi('/model_resolver/model-details', {
const data = await this.fetchJson('/model_resolver/model-details', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
source: model.details_source || model.source,
model_id: model.model_id || model.modelId,
version_id: model.version_id || model.versionId,
civitai_key: tokens.civitai_key || ''
})
});
if (!response.ok) {
throw new Error(`Details request failed: ${response.status}`);
}
const data = await response.json();
}, 'Fetch model details');
details._detailsData = data;
details._selectedVersionId = String(data.selected_version?.id || data.version_id || '');
details.innerHTML = this.renderSourceModelDetails(data, model);
@@ -1486,22 +1471,15 @@ export const modelInfoMethods = {
try {
const data = details._detailsData;
const tokens = this.getStoredTokens?.() || {};
const response = await api.fetchApi('/model_resolver/model-details', {
const fresh = await this.fetchJson('/model_resolver/model-details', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
source: data.source || details._sourceModel?.details_source || details._sourceModel?.source,
model_id: data.model_id || details._sourceModel?.model_id || details._sourceModel?.modelId,
version_id: versionId,
civitai_key: tokens.civitai_key || ''
})
});
if (!response.ok) {
throw new Error(`Version details request failed: ${response.status}`);
}
const fresh = await response.json();
}, 'Fetch model version details');
const freshVersion = fresh.selected_version;
if (!freshVersion?.id) return;
+18 -55
View File
@@ -731,15 +731,10 @@ export const optionsMethods = {
try {
if (button) button.disabled = true;
setTokenCheckStatus(statusEl, 'Checking...', 'is-pending');
const response = await api.fetchApi(endpoint, {
const data = await this.fetchJson(endpoint, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ [payloadKey]: value })
});
const data = await response.json();
if (!response.ok) {
throw new Error(data.error || `Check failed: ${response.status}`);
}
}, 'Check credential');
setTokenCheckStatus(
statusEl,
data.message || (data.valid ? 'Valid' : 'Invalid'),
@@ -794,11 +789,7 @@ export const optionsMethods = {
if (modelListStateEl) {
modelListStateEl.textContent = checkRemote ? 'Checking GitHub...' : 'Loading...';
}
const response = await api.fetchApi(`/model_resolver/model-list/status${checkRemote ? '?check_remote=1' : ''}`);
if (!response.ok) {
throw new Error(`Status failed: ${response.status}`);
}
const data = await response.json();
const data = await this.fetchJson(`/model_resolver/model-list/status${checkRemote ? '?check_remote=1' : ''}`, {}, 'Load model list status');
renderModelListStatus(data, checkRemote);
return data;
} catch (error) {
@@ -816,13 +807,9 @@ export const optionsMethods = {
setModelListBusy(true);
if (modelListStateEl) modelListStateEl.textContent = 'Updating...';
if (modelListMessageEl) modelListMessageEl.textContent = 'Downloading latest ComfyUI-Manager model-list.json...';
const response = await api.fetchApi('/model_resolver/model-list/update', {
const data = await this.fetchJson('/model_resolver/model-list/update', {
method: 'POST'
});
const data = await response.json();
if (!response.ok) {
throw new Error(data.error || `Update failed: ${response.status}`);
}
}, 'Update model list');
await this.clearSearchCaches();
renderModelListStatus(data, false);
if (modelListStateEl) modelListStateEl.textContent = 'Updated';
@@ -873,11 +860,7 @@ export const optionsMethods = {
if (baseModelsStateEl) {
baseModelsStateEl.textContent = checkRemote ? 'Checking CivitAI...' : 'Loading...';
}
const response = await api.fetchApi(`/model_resolver/base-models/status${checkRemote ? '?check_remote=1' : ''}`);
if (!response.ok) {
throw new Error(`Status failed: ${response.status}`);
}
const data = await response.json();
const data = await this.fetchJson(`/model_resolver/base-models/status${checkRemote ? '?check_remote=1' : ''}`, {}, 'Load base models status');
renderBaseModelsStatus(data, checkRemote);
return data;
} catch (error) {
@@ -907,13 +890,9 @@ export const optionsMethods = {
setBaseModelsBusy(true);
if (baseModelsStateEl) baseModelsStateEl.textContent = 'Updating...';
if (baseModelsMessageEl) baseModelsMessageEl.textContent = 'Downloading latest base models from CivitAI...';
const response = await api.fetchApi('/model_resolver/base-models/update', {
const data = await this.fetchJson('/model_resolver/base-models/update', {
method: 'POST'
});
const data = await response.json();
if (!response.ok) {
throw new Error(data.error || `Update failed: ${response.status}`);
}
}, 'Update base models');
await this.clearSearchCaches();
// Invalidate the in-memory cache so the base model dropdown
// reloads fresh data from the server next time it is opened.
@@ -978,11 +957,7 @@ export const optionsMethods = {
try {
setHfIndexBusy(true);
if (hfIndexStateEl) hfIndexStateEl.textContent = 'Loading...';
const response = await api.fetchApi('/model_resolver/huggingface/author-index/status');
if (!response.ok) {
throw new Error(`Status failed: ${response.status}`);
}
const data = await response.json();
const data = await this.fetchJson('/model_resolver/huggingface/author-index/status', {}, 'Load HuggingFace index status');
renderHfIndexStatus(data);
return data;
} catch (error) {
@@ -1000,14 +975,12 @@ export const optionsMethods = {
setHfIndexBusy(true);
if (hfIndexStateEl) hfIndexStateEl.textContent = 'Refreshing...';
if (hfIndexMessageEl) hfIndexMessageEl.textContent = 'Downloading Comfy-Org file index from HuggingFace...';
const response = await api.fetchApi('/model_resolver/huggingface/author-index/refresh', {
const data = await this.fetchJson('/model_resolver/huggingface/author-index/refresh', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ hf_token: hfInput?.value || '' })
});
const data = await response.json();
if (!response.ok || data.success === false) {
throw new Error(data.error || `Refresh failed: ${response.status}`);
}, 'Refresh HuggingFace index');
if (data?.success === false) {
throw new Error(data.error || 'Refresh failed');
}
await this.clearSearchCaches();
renderHfIndexStatus(data);
@@ -1328,9 +1301,7 @@ export const optionsMethods = {
setTemplateDetectStatus('Scanning existing model folders...', 'is-pending');
try {
const resp = await api.fetchApi('/model_resolver/path-template-suggestions?force=1');
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
const data = await resp.json();
const data = await this.fetchJson('/model_resolver/path-template-suggestions?force=1', {}, 'Detect path templates');
const categories = data?.categories && typeof data.categories === 'object'
? data.categories
: {};
@@ -1675,12 +1646,10 @@ export const optionsMethods = {
// 2. Persist to server (survives new browsers / profiles)
try {
setStatus('Saving…', '');
const resp = await api.fetchApi('/model_resolver/settings', {
await this.fetchJson('/model_resolver/settings', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(newSettings),
});
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
body: JSON.stringify(newSettings)
}, 'Save settings');
setStatus('Saved on this machine (all browsers).', 'is-saved');
this.showNotification('Options saved', 'success');
} catch (err) {
@@ -1750,13 +1719,7 @@ export const optionsMethods = {
*/
async loadSettingsFromServer() {
try {
const resp = await api.fetchApi('/model_resolver/settings');
if (!resp.ok) {
const tokens = this.getStoredTokens();
this.applyFrontendLoggingPreference(tokens.frontend_logs_enabled, tokens.frontend_log_level);
return;
}
const data = await resp.json();
const data = await this.fetchJson('/model_resolver/settings', { silent: true }, 'Load settings from server');
if (!data || typeof data !== 'object') {
const tokens = this.getStoredTokens();
this.applyFrontendLoggingPreference(tokens.frontend_logs_enabled, tokens.frontend_log_level);
+3 -10
View File
@@ -1,4 +1,4 @@
import { app } from "../../../../../scripts/app.js";
import { app } from "../../../../../scripts/app.js";
import { api } from "../../../../../scripts/api.js";
import { $el } from "../../../../../scripts/ui.js";
import { getSvgIcon } from "../../utils/icon_utils.js";
@@ -114,17 +114,10 @@ export const tabsLoadedMethods = {
this.contentElement.innerHTML = '<p>Loading loaded models...</p>';
}
const response = await api.fetchApi('/model_resolver/loaded', {
const data = await this.fetchJson('/model_resolver/loaded', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ workflow })
});
if (!response.ok) {
throw new Error(`API error: ${response.status}`);
}
const data = await response.json();
}, 'Fetch loaded models');
if (this._loadedModelsLoadToken === loadToken) {
this.cachedLoadedModelsSignature = workflowSignature;
this.cachedLoadedModelsData = data;