mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-03-21 21:22:11 -03:00
refactor: consolidate model API client creation into a factory function for better maintainability feat: implement move operations for LoRAs and handle unsupported operations for Checkpoints and Embeddings
195 lines
6.2 KiB
JavaScript
195 lines
6.2 KiB
JavaScript
import { BaseModelApiClient } from './baseModelApi.js';
|
|
import { showToast } from '../utils/uiHelpers.js';
|
|
import { getSessionItem } from '../utils/storageHelpers.js';
|
|
|
|
/**
|
|
* LoRA-specific API client
|
|
*/
|
|
export class LoraApiClient extends BaseModelApiClient {
|
|
/**
|
|
* Add LoRA-specific parameters to query
|
|
*/
|
|
_addModelSpecificParams(params, pageState) {
|
|
const filterLoraHash = getSessionItem('recipe_to_lora_filterLoraHash');
|
|
const filterLoraHashes = getSessionItem('recipe_to_lora_filterLoraHashes');
|
|
|
|
if (filterLoraHash) {
|
|
params.append('lora_hash', filterLoraHash);
|
|
} else if (filterLoraHashes) {
|
|
try {
|
|
if (Array.isArray(filterLoraHashes) && filterLoraHashes.length > 0) {
|
|
params.append('lora_hashes', filterLoraHashes.join(','));
|
|
}
|
|
} catch (error) {
|
|
console.error('Error parsing lora hashes from session storage:', error);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Move a single LoRA to target path
|
|
*/
|
|
async moveSingleModel(filePath, targetPath) {
|
|
if (filePath.substring(0, filePath.lastIndexOf('/')) === targetPath) {
|
|
showToast('LoRA is already in the selected folder', 'info');
|
|
return null;
|
|
}
|
|
|
|
const response = await fetch(this.apiConfig.endpoints.specific.moveModel, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
file_path: filePath,
|
|
target_path: targetPath
|
|
})
|
|
});
|
|
|
|
const result = await response.json();
|
|
|
|
if (!response.ok) {
|
|
if (result && result.error) {
|
|
throw new Error(result.error);
|
|
}
|
|
throw new Error('Failed to move LoRA');
|
|
}
|
|
|
|
if (result && result.message) {
|
|
showToast(result.message, 'info');
|
|
} else {
|
|
showToast('LoRA moved successfully', 'success');
|
|
}
|
|
|
|
if (result.success) {
|
|
return result.new_file_path;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Move multiple LoRAs to target path
|
|
*/
|
|
async moveBulkModels(filePaths, targetPath) {
|
|
const movedPaths = filePaths.filter(path => {
|
|
return path.substring(0, path.lastIndexOf('/')) !== targetPath;
|
|
});
|
|
|
|
if (movedPaths.length === 0) {
|
|
showToast('All selected LoRAs are already in the target folder', 'info');
|
|
return [];
|
|
}
|
|
|
|
const response = await fetch(this.apiConfig.endpoints.specific.moveBulk, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
file_paths: movedPaths,
|
|
target_path: targetPath
|
|
})
|
|
});
|
|
|
|
const result = await response.json();
|
|
|
|
if (!response.ok) {
|
|
throw new Error('Failed to move LoRAs');
|
|
}
|
|
|
|
let successFilePaths = [];
|
|
if (result.success) {
|
|
if (result.failure_count > 0) {
|
|
showToast(`Moved ${result.success_count} LoRAs, ${result.failure_count} failed`, 'warning');
|
|
console.log('Move operation results:', result.results);
|
|
const failedFiles = result.results
|
|
.filter(r => !r.success)
|
|
.map(r => {
|
|
const fileName = r.path.substring(r.path.lastIndexOf('/') + 1);
|
|
return `${fileName}: ${r.message}`;
|
|
});
|
|
if (failedFiles.length > 0) {
|
|
const failureMessage = failedFiles.length <= 3
|
|
? failedFiles.join('\n')
|
|
: failedFiles.slice(0, 3).join('\n') + `\n(and ${failedFiles.length - 3} more)`;
|
|
showToast(`Failed moves:\n${failureMessage}`, 'warning', 6000);
|
|
}
|
|
} else {
|
|
showToast(`Successfully moved ${result.success_count} LoRAs`, 'success');
|
|
}
|
|
successFilePaths = result.results
|
|
.filter(r => r.success)
|
|
.map(r => r.path);
|
|
} else {
|
|
throw new Error(result.message || 'Failed to move LoRAs');
|
|
}
|
|
return successFilePaths;
|
|
}
|
|
|
|
/**
|
|
* Get LoRA notes
|
|
*/
|
|
async getLoraNote(filePath) {
|
|
try {
|
|
const response = await fetch(this.apiConfig.endpoints.specific.notes,
|
|
{
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify({ file_path: filePath })
|
|
}
|
|
);
|
|
|
|
if (!response.ok) {
|
|
throw new Error('Failed to fetch LoRA notes');
|
|
}
|
|
|
|
return await response.json();
|
|
} catch (error) {
|
|
console.error('Error fetching LoRA notes:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get LoRA trigger words
|
|
*/
|
|
async getLoraTriggerWords(filePath) {
|
|
try {
|
|
const response = await fetch(this.apiConfig.endpoints.specific.triggerWords, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify({ file_path: filePath })
|
|
});
|
|
|
|
if (!response.ok) {
|
|
throw new Error('Failed to fetch trigger words');
|
|
}
|
|
|
|
return await response.json();
|
|
} catch (error) {
|
|
console.error('Error fetching trigger words:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get letter counts for LoRAs
|
|
*/
|
|
async getLetterCounts() {
|
|
try {
|
|
const response = await fetch(this.apiConfig.endpoints.specific.letterCounts);
|
|
if (!response.ok) {
|
|
throw new Error('Failed to fetch letter counts');
|
|
}
|
|
return await response.json();
|
|
} catch (error) {
|
|
console.error('Error fetching letter counts:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
}
|