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:
@@ -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');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 = []) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user