228 lines
9.4 KiB
JavaScript
228 lines
9.4 KiB
JavaScript
/**
|
|
* Model Sorting and Grouping Utilities for SageUtils Reports
|
|
* Handles sorting models by various criteria and grouping by Civitai ID
|
|
*/
|
|
|
|
import {
|
|
hasUpdateAvailable
|
|
} from '../shared/civitai.js';
|
|
|
|
/**
|
|
* Sort models array based on the specified criteria
|
|
* @param {Array} models - Array of model objects
|
|
* @param {string} sortBy - Sort criteria
|
|
* @returns {Array} - Sorted array of models
|
|
*/
|
|
export function sortModels(models, sortBy) {
|
|
return models.sort((a, b) => {
|
|
const infoA = a.info || {};
|
|
const infoB = b.info || {};
|
|
|
|
switch (sortBy) {
|
|
case 'name': {
|
|
const nameA = (infoA.model && infoA.model.name) || infoA.name || a.filePath.split('/').pop() || '';
|
|
const nameB = (infoB.model && infoB.model.name) || infoB.name || b.filePath.split('/').pop() || '';
|
|
return nameA.localeCompare(nameB);
|
|
}
|
|
case 'name-desc': {
|
|
const nameA = (infoA.model && infoA.model.name) || infoA.name || a.filePath.split('/').pop() || '';
|
|
const nameB = (infoB.model && infoB.model.name) || infoB.name || b.filePath.split('/').pop() || '';
|
|
return nameB.localeCompare(nameA);
|
|
}
|
|
case 'lastused': {
|
|
const lastUsedA = (infoA.lastUsed || infoA.last_accessed) ? new Date(infoA.lastUsed || infoA.last_accessed) : new Date(0);
|
|
const lastUsedB = (infoB.lastUsed || infoB.last_accessed) ? new Date(infoB.lastUsed || infoB.last_accessed) : new Date(0);
|
|
return lastUsedB - lastUsedA; // Recent first
|
|
}
|
|
case 'lastused-desc': {
|
|
const lastUsedA = (infoA.lastUsed || infoA.last_accessed) ? new Date(infoA.lastUsed || infoA.last_accessed) : new Date(0);
|
|
const lastUsedB = (infoB.lastUsed || infoB.last_accessed) ? new Date(infoB.lastUsed || infoB.last_accessed) : new Date(0);
|
|
return lastUsedA - lastUsedB; // Oldest first
|
|
}
|
|
case 'size': {
|
|
const sizeA = infoA.file_size || 0;
|
|
const sizeB = infoB.file_size || 0;
|
|
return sizeA - sizeB; // Small to large
|
|
}
|
|
case 'size-desc': {
|
|
const sizeA = infoA.file_size || 0;
|
|
const sizeB = infoB.file_size || 0;
|
|
return sizeB - sizeA; // Large to small
|
|
}
|
|
case 'type': {
|
|
const typeA = (infoA.model && infoA.model.type) || infoA.model_type || 'ZZZ';
|
|
const typeB = (infoB.model && infoB.model.type) || infoB.model_type || 'ZZZ';
|
|
if (typeA !== typeB) {
|
|
return typeA.localeCompare(typeB);
|
|
}
|
|
// Secondary sort by name
|
|
const nameA = (infoA.model && infoA.model.name) || infoA.name || a.filePath.split('/').pop() || '';
|
|
const nameB = (infoB.model && infoB.model.name) || infoB.name || b.filePath.split('/').pop() || '';
|
|
return nameA.localeCompare(nameB);
|
|
}
|
|
case 'type-desc': {
|
|
const typeA = (infoA.model && infoA.model.type) || infoA.model_type || 'ZZZ';
|
|
const typeB = (infoB.model && infoB.model.type) || infoB.model_type || 'ZZZ';
|
|
if (typeA !== typeB) {
|
|
return typeB.localeCompare(typeA); // Reversed for descending
|
|
}
|
|
// Secondary sort by name (also reversed for descending)
|
|
const nameA = (infoA.model && infoA.model.name) || infoA.name || a.filePath.split('/').pop() || '';
|
|
const nameB = (infoB.model && infoB.model.name) || infoB.name || b.filePath.split('/').pop() || '';
|
|
return nameB.localeCompare(nameA);
|
|
}
|
|
default: {
|
|
// Default to name sorting
|
|
const nameA = (infoA.model && infoA.model.name) || infoA.name || a.filePath.split('/').pop() || '';
|
|
const nameB = (infoB.model && infoB.model.name) || infoB.name || b.filePath.split('/').pop() || '';
|
|
return nameA.localeCompare(nameB);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Group models by Civitai ID, keeping different versions of the same model together
|
|
* @param {Array} models - Array of model objects
|
|
* @param {string} sortBy - Sort criteria to apply within groups
|
|
* @returns {Array} - Array of models grouped and sorted
|
|
*/
|
|
export function groupModelsByCivitaiId(models, sortBy) {
|
|
// First, group models by Civitai ID
|
|
const groupedModels = new Map();
|
|
const ungroupedModels = [];
|
|
|
|
models.forEach(model => {
|
|
const modelId = model.info && (model.info.modelId || model.info.model_id);
|
|
|
|
if (modelId && modelId !== 'Unknown' && modelId !== null) {
|
|
if (!groupedModels.has(modelId)) {
|
|
groupedModels.set(modelId, []);
|
|
}
|
|
groupedModels.get(modelId).push(model);
|
|
} else {
|
|
ungroupedModels.push(model);
|
|
}
|
|
});
|
|
|
|
// Sort models within each group
|
|
for (const [modelId, group] of groupedModels) {
|
|
groupedModels.set(modelId, sortModels(group, sortBy));
|
|
}
|
|
|
|
// Sort ungrouped models
|
|
const sortedUngrouped = sortModels(ungroupedModels, sortBy);
|
|
|
|
// Combine grouped and ungrouped models
|
|
// First, get all groups and sort them by the first model in each group
|
|
const sortedGroups = Array.from(groupedModels.entries()).sort((a, b) => {
|
|
const firstModelA = a[1][0];
|
|
const firstModelB = b[1][0];
|
|
|
|
// Use the same sorting logic as the main sort
|
|
const tempModels = [firstModelA, firstModelB];
|
|
const sorted = sortModels(tempModels, sortBy);
|
|
return sorted[0] === firstModelA ? -1 : 1;
|
|
});
|
|
|
|
// Flatten the result: grouped models first, then ungrouped models
|
|
const result = [];
|
|
|
|
sortedGroups.forEach(([modelId, group]) => {
|
|
result.push(...group);
|
|
});
|
|
|
|
result.push(...sortedUngrouped);
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Generate grouping information for models based on Civitai ID
|
|
* @param {Array} models - Array of models (already grouped and sorted)
|
|
* @returns {Array} - Array of group info objects for each model
|
|
*/
|
|
export function generateGroupInfo(models) {
|
|
const groupInfo = [];
|
|
let currentModelId = null;
|
|
let groupStartIndex = -1;
|
|
let groupSize = 0;
|
|
|
|
models.forEach((model, index) => {
|
|
const modelId = model.info && (model.info.modelId || model.info.model_id);
|
|
const effectiveModelId = (modelId && modelId !== 'Unknown' && modelId !== null) ? modelId : null;
|
|
|
|
if (effectiveModelId && effectiveModelId === currentModelId) {
|
|
// Continue current group
|
|
groupSize++;
|
|
groupInfo[index] = {
|
|
isGroupMember: true,
|
|
isGroupFirst: false,
|
|
isGroupLast: false,
|
|
groupSize: groupSize,
|
|
modelId: effectiveModelId
|
|
};
|
|
} else {
|
|
// End previous group if it had more than one model
|
|
if (groupSize > 1 && groupStartIndex !== -1) {
|
|
groupInfo[groupStartIndex].isGroupFirst = true;
|
|
groupInfo[index - 1].isGroupLast = true;
|
|
|
|
// Check if any model in the group doesn't have an update available
|
|
// If so, we likely already have the latest version
|
|
let groupHasLatestVersion = false;
|
|
for (let i = groupStartIndex; i < index; i++) {
|
|
const modelInGroup = models[i];
|
|
if (modelInGroup && modelInGroup.info && !hasUpdateAvailable(modelInGroup.info)) {
|
|
groupHasLatestVersion = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Mark all models in the group
|
|
for (let i = groupStartIndex; i < index; i++) {
|
|
groupInfo[i].isGroupMember = true;
|
|
groupInfo[i].groupHasLatestVersion = groupHasLatestVersion;
|
|
}
|
|
}
|
|
|
|
// Start new group or single model
|
|
currentModelId = effectiveModelId;
|
|
groupStartIndex = effectiveModelId ? index : -1;
|
|
groupSize = effectiveModelId ? 1 : 0;
|
|
|
|
groupInfo[index] = {
|
|
isGroupMember: effectiveModelId ? true : false,
|
|
isGroupFirst: false,
|
|
isGroupLast: false,
|
|
groupSize: groupSize,
|
|
modelId: effectiveModelId
|
|
};
|
|
}
|
|
});
|
|
|
|
// Handle the last group if needed
|
|
if (groupSize > 1 && groupStartIndex !== -1) {
|
|
groupInfo[groupStartIndex].isGroupFirst = true;
|
|
groupInfo[models.length - 1].isGroupLast = true;
|
|
|
|
// Check if any model in the final group doesn't have an update available
|
|
let groupHasLatestVersion = false;
|
|
for (let i = groupStartIndex; i < models.length; i++) {
|
|
const modelInGroup = models[i];
|
|
if (modelInGroup && modelInGroup.info && !hasUpdateAvailable(modelInGroup.info)) {
|
|
groupHasLatestVersion = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Mark all models in the final group
|
|
for (let i = groupStartIndex; i < models.length; i++) {
|
|
groupInfo[i].isGroupMember = true;
|
|
groupInfo[i].groupHasLatestVersion = groupHasLatestVersion;
|
|
}
|
|
}
|
|
|
|
return groupInfo;
|
|
}
|