Files
ComfyUI-Lora-Manager/static/js/api/civitaiBaseModelApi.js
Will Miao 00f5c1e887 feat: Dynamic base model fetching from Civitai API (#854)
Implement automatic fetching of base models from Civitai API to keep
data up-to-date without manual updates.

Backend:
- Add CivitaiBaseModelService with 7-day TTL caching
- Add /api/lm/base-models endpoints for fetching and refreshing
- Merge hardcoded and remote models for backward compatibility
- Smart abbreviation generation for unknown models

Frontend:
- Add civitaiBaseModelApi client for API communication
- Dynamic base model loading on app initialization
- Update SettingsManager to use merged model lists
- Add support for 8 new models: Anima, CogVideoX, LTXV 2.3, Mochi,
  Pony V7, Wan Video 2.5 T2V/I2V

API Endpoints:
- GET /api/lm/base-models - Get merged models
- POST /api/lm/base-models/refresh - Force refresh
- GET /api/lm/base-models/categories - Get categories
- GET /api/lm/base-models/cache-status - Check cache status

Closes #854
2026-03-29 00:18:15 +08:00

165 lines
4.8 KiB
JavaScript

/**
* API client for Civitai base model management
* Handles fetching and refreshing base models from Civitai API
*/
import { showToast } from '../utils/uiHelpers.js';
const BASE_MODEL_ENDPOINTS = {
getModels: '/api/lm/base-models',
refresh: '/api/lm/base-models/refresh',
categories: '/api/lm/base-models/categories',
cacheStatus: '/api/lm/base-models/cache-status',
};
/**
* Civitai Base Model API Client
*/
export class CivitaiBaseModelApi {
constructor() {
this.cache = null;
this.cacheTimestamp = null;
}
/**
* Get base models (with caching)
* @param {boolean} forceRefresh - Force refresh from API
* @returns {Promise<Object>} Response with models, source, and counts
*/
async getBaseModels(forceRefresh = false) {
try {
const url = new URL(BASE_MODEL_ENDPOINTS.getModels, window.location.origin);
if (forceRefresh) {
url.searchParams.append('refresh', 'true');
}
const response = await fetch(url);
if (!response.ok) {
throw new Error(`Failed to fetch base models: ${response.statusText}`);
}
const data = await response.json();
if (data.success) {
this.cache = data.data;
this.cacheTimestamp = Date.now();
return data.data;
} else {
throw new Error(data.error || 'Failed to fetch base models');
}
} catch (error) {
console.error('Error fetching base models:', error);
showToast('Failed to fetch base models', { message: error.message }, 'error');
throw error;
}
}
/**
* Force refresh base models from Civitai API
* @returns {Promise<Object>} Refreshed data
*/
async refreshBaseModels() {
try {
const response = await fetch(BASE_MODEL_ENDPOINTS.refresh, {
method: 'POST',
headers: { 'Content-Type': 'application/json' }
});
if (!response.ok) {
throw new Error(`Failed to refresh base models: ${response.statusText}`);
}
const data = await response.json();
if (data.success) {
this.cache = data.data;
this.cacheTimestamp = Date.now();
showToast('Base models refreshed successfully', {}, 'success');
return data.data;
} else {
throw new Error(data.error || 'Failed to refresh base models');
}
} catch (error) {
console.error('Error refreshing base models:', error);
showToast('Failed to refresh base models', { message: error.message }, 'error');
throw error;
}
}
/**
* Get base model categories
* @returns {Promise<Object>} Categories with model lists
*/
async getCategories() {
try {
const response = await fetch(BASE_MODEL_ENDPOINTS.categories);
if (!response.ok) {
throw new Error(`Failed to fetch categories: ${response.statusText}`);
}
const data = await response.json();
if (data.success) {
return data.data;
} else {
throw new Error(data.error || 'Failed to fetch categories');
}
} catch (error) {
console.error('Error fetching categories:', error);
throw error;
}
}
/**
* Get cache status
* @returns {Promise<Object>} Cache status information
*/
async getCacheStatus() {
try {
const response = await fetch(BASE_MODEL_ENDPOINTS.cacheStatus);
if (!response.ok) {
throw new Error(`Failed to fetch cache status: ${response.statusText}`);
}
const data = await response.json();
if (data.success) {
return data.data;
} else {
throw new Error(data.error || 'Failed to fetch cache status');
}
} catch (error) {
console.error('Error fetching cache status:', error);
throw error;
}
}
/**
* Get cached models (if available)
* @returns {Object|null} Cached data or null
*/
getCachedModels() {
return this.cache;
}
/**
* Check if cache is available
* @returns {boolean}
*/
hasCache() {
return this.cache !== null;
}
/**
* Get cache age in milliseconds
* @returns {number|null} Age in ms or null if no cache
*/
getCacheAge() {
if (!this.cacheTimestamp) return null;
return Date.now() - this.cacheTimestamp;
}
}
// Export singleton instance
export const civitaiBaseModelApi = new CivitaiBaseModelApi();