277 lines
8.1 KiB
JavaScript
277 lines
8.1 KiB
JavaScript
/**
|
|
* Data processing utilities for Models tab
|
|
* Handles filtering, sorting, and processing of model data
|
|
*/
|
|
|
|
import {
|
|
getFileType,
|
|
supportsPreview,
|
|
FILE_TYPES
|
|
} from "./config.js";
|
|
|
|
/**
|
|
* Processes raw model data combining hash data with info data
|
|
* @param {Object} hashData - Hash data from cache
|
|
* @param {Object} infoData - Info data from cache
|
|
* @param {Object} filters - Current filter settings
|
|
* @returns {Array} Processed model data array
|
|
*/
|
|
export function processModelData(hashData, infoData, filters = {}) {
|
|
if (!hashData || !infoData) {
|
|
return [];
|
|
}
|
|
|
|
const processedData = [];
|
|
|
|
// Combine hash data with info data
|
|
for (const [filename, hashInfo] of Object.entries(hashData)) {
|
|
const info = infoData[hashInfo.hash] || {};
|
|
|
|
const modelData = {
|
|
filename,
|
|
hash: hashInfo.hash,
|
|
lastUsed: hashInfo.lastUsed,
|
|
filePath: hashInfo.filePath,
|
|
fileSize: hashInfo.fileSize,
|
|
...info // Spread info data (name, description, baseModel, etc.)
|
|
};
|
|
|
|
processedData.push(modelData);
|
|
}
|
|
|
|
return processedData;
|
|
}
|
|
|
|
/**
|
|
* Filters model data based on filter criteria
|
|
* @param {Array} models - Array of model data objects
|
|
* @param {Object} filters - Filter criteria
|
|
* @returns {Array} Filtered model data array
|
|
*/
|
|
export function filterModels(models, filters = {}) {
|
|
if (!Array.isArray(models)) {
|
|
return [];
|
|
}
|
|
|
|
return models.filter(model => {
|
|
// Type filter
|
|
if (filters.type && filters.type !== 'all') {
|
|
const modelType = (model.type || '').toLowerCase();
|
|
if (modelType !== filters.type.toLowerCase()) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Search filter
|
|
if (filters.search && filters.search.trim()) {
|
|
const searchTerm = filters.search.toLowerCase().trim();
|
|
const searchableText = [
|
|
model.filename || '',
|
|
model.name || '',
|
|
model.description || '',
|
|
model.baseModel || '',
|
|
...(model.trainedWords || [])
|
|
].join(' ').toLowerCase();
|
|
|
|
if (!searchableText.includes(searchTerm)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Last used filter
|
|
if (filters.lastUsed && filters.lastUsed !== 'all') {
|
|
const lastUsed = model.lastUsed;
|
|
const now = Date.now();
|
|
const day = 24 * 60 * 60 * 1000;
|
|
|
|
switch (filters.lastUsed) {
|
|
case 'today':
|
|
if (!lastUsed || now - lastUsed > day) return false;
|
|
break;
|
|
case 'week':
|
|
if (!lastUsed || now - lastUsed > 7 * day) return false;
|
|
break;
|
|
case 'month':
|
|
if (!lastUsed || now - lastUsed > 30 * day) return false;
|
|
break;
|
|
case 'never':
|
|
if (lastUsed) return false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Update available filter
|
|
if (filters.updates && filters.updates !== 'all') {
|
|
const hasUpdate = model.update_available;
|
|
if (filters.updates === 'available' && !hasUpdate) return false;
|
|
if (filters.updates === 'none' && hasUpdate) return false;
|
|
}
|
|
|
|
// NSFW filter
|
|
if (!filters.showNSFW && model.nsfw === true) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Sorts model data based on sort criteria
|
|
* @param {Array} models - Array of model data objects
|
|
* @param {string} sortBy - Sort criteria
|
|
* @returns {Array} Sorted model data array
|
|
*/
|
|
export function sortModels(models, sortBy = 'name') {
|
|
if (!Array.isArray(models)) {
|
|
return [];
|
|
}
|
|
|
|
const sortedModels = [...models];
|
|
|
|
sortedModels.sort((a, b) => {
|
|
switch (sortBy) {
|
|
case 'name':
|
|
return (a.name || a.filename || '').localeCompare(b.name || b.filename || '');
|
|
|
|
case 'filename':
|
|
return (a.filename || '').localeCompare(b.filename || '');
|
|
|
|
case 'lastUsed':
|
|
return (b.lastUsed || 0) - (a.lastUsed || 0);
|
|
|
|
case 'fileSize':
|
|
return (b.fileSize || 0) - (a.fileSize || 0);
|
|
|
|
case 'type':
|
|
return (a.type || '').localeCompare(b.type || '');
|
|
|
|
case 'baseModel':
|
|
return (a.baseModel || '').localeCompare(b.baseModel || '');
|
|
|
|
default:
|
|
return 0;
|
|
}
|
|
});
|
|
|
|
return sortedModels;
|
|
}
|
|
|
|
/**
|
|
* Groups models by folder structure
|
|
* @param {Array} models - Array of model data objects
|
|
* @returns {Object} Grouped model data by folders
|
|
*/
|
|
export function groupModelsByFolder(models) {
|
|
if (!Array.isArray(models)) {
|
|
return {};
|
|
}
|
|
|
|
const grouped = {};
|
|
|
|
models.forEach(model => {
|
|
const filePath = model.filePath || '';
|
|
const pathParts = filePath.split('/').filter(part => part.length > 0);
|
|
|
|
// Remove filename from path
|
|
pathParts.pop();
|
|
|
|
const folderPath = pathParts.length > 0 ? pathParts.join('/') : 'root';
|
|
|
|
if (!grouped[folderPath]) {
|
|
grouped[folderPath] = [];
|
|
}
|
|
|
|
grouped[folderPath].push(model);
|
|
});
|
|
|
|
return grouped;
|
|
}
|
|
|
|
/**
|
|
* Finds other versions of the same model by modelId
|
|
* @param {Array} models - Array of all model data
|
|
* @param {string} modelId - The model ID to find versions for
|
|
* @param {string} currentHash - Current model hash to exclude
|
|
* @returns {Array} Array of other model versions
|
|
*/
|
|
export function findOtherModelVersions(models, modelId, currentHash) {
|
|
if (!Array.isArray(models) || !modelId) {
|
|
return [];
|
|
}
|
|
|
|
return models.filter(model => {
|
|
return model.modelId === modelId && model.hash !== currentHash;
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Extracts and formats metadata for display
|
|
* @param {Object} modelData - Model data object
|
|
* @returns {Object} Formatted metadata object
|
|
*/
|
|
export function formatModelMetadata(modelData) {
|
|
if (!modelData) {
|
|
return {};
|
|
}
|
|
|
|
return {
|
|
name: modelData.name || modelData.filename || 'Unknown',
|
|
type: modelData.type || 'Unknown',
|
|
baseModel: modelData.baseModel || 'Unknown',
|
|
description: modelData.description || '',
|
|
trainedWords: modelData.trainedWords || [],
|
|
hash: modelData.hash || '',
|
|
fileSize: formatFileSize(modelData.fileSize || 0),
|
|
lastUsed: formatLastUsed(modelData.lastUsed),
|
|
updateAvailable: modelData.update_available || false,
|
|
nsfw: modelData.nsfw || false,
|
|
external_url: modelData.external_url || '',
|
|
notes: modelData.notes || ''
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Formats file size in human readable format
|
|
* @param {number} bytes - File size in bytes
|
|
* @returns {string} Formatted file size
|
|
*/
|
|
function formatFileSize(bytes) {
|
|
if (!bytes || bytes === 0) return '0 B';
|
|
|
|
const k = 1024;
|
|
const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
|
|
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
|
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
|
}
|
|
|
|
/**
|
|
* Formats last used timestamp
|
|
* @param {number} timestamp - Unix timestamp
|
|
* @returns {string} Formatted last used string
|
|
*/
|
|
function formatLastUsed(timestamp) {
|
|
if (!timestamp) return 'Never';
|
|
|
|
const date = new Date(timestamp);
|
|
const now = new Date();
|
|
const diffMs = now - date;
|
|
const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));
|
|
|
|
if (diffDays === 0) {
|
|
const diffHours = Math.floor(diffMs / (1000 * 60 * 60));
|
|
if (diffHours === 0) {
|
|
const diffMinutes = Math.floor(diffMs / (1000 * 60));
|
|
return diffMinutes <= 1 ? 'Just now' : `${diffMinutes} minutes ago`;
|
|
}
|
|
return diffHours === 1 ? '1 hour ago' : `${diffHours} hours ago`;
|
|
} else if (diffDays === 1) {
|
|
return 'Yesterday';
|
|
} else if (diffDays < 7) {
|
|
return `${diffDays} days ago`;
|
|
} else {
|
|
return date.toLocaleDateString();
|
|
}
|
|
}
|