Add Checkpoints Gallery with per-checkpoint generation settings
- New Checkpoint model (slug, name, checkpoint_path, data JSON, image_path) - sync_checkpoints() loads metadata from data/checkpoints/*.json and falls back to template defaults for models without a JSON file - _apply_checkpoint_settings() applies per-checkpoint steps, CFG, sampler, base positive/negative prompts, and VAE (with dynamic VAELoader node injection for non-integrated VAEs) to the ComfyUI workflow - Bulk Create from Checkpoints: scans Illustrious/Noob model directories, reads matching HTML files, uses LLM to populate metadata, falls back to template defaults when no HTML is present - Gallery index with batch cover generation and WebSocket progress bar - Detail page showing Generation Settings and Base Prompts cards - Checkpoints nav link added to layout - New data/prompts/checkpoint_system.txt LLM system prompt - Updated README with all current galleries and file structure - Also includes accumulated action/scene JSON updates, new actions, and other template/generator improvements from prior sessions Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -16,11 +16,27 @@
|
||||
<!-- Batch Progress Bar -->
|
||||
<div id="batch-progress-container" class="card mb-4 d-none">
|
||||
<div class="card-body">
|
||||
<h5 id="batch-status-text">Batch Generating Outfits...</h5>
|
||||
<div class="progress mt-2" role="progressbar" aria-label="Batch Progress" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">
|
||||
<div id="batch-progress-bar" class="progress-bar progress-bar-striped progress-bar-animated bg-success" style="width: 0%">0%</div>
|
||||
<div class="d-flex justify-content-between align-items-center mb-1">
|
||||
<h5 id="batch-status-text" class="mb-0">Batch Generating Outfits...</h5>
|
||||
<span id="batch-node-status" class="badge bg-info text-dark">Starting...</span>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<small class="text-muted">Overall Batch Progress</small>
|
||||
<div class="progress" role="progressbar" aria-label="Batch Progress" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="height: 10px;">
|
||||
<div id="batch-progress-bar" class="progress-bar bg-success" style="width: 0%"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-2">
|
||||
<div class="d-flex justify-content-between">
|
||||
<small id="current-item-name" class="text-muted mb-1"></small>
|
||||
<small id="current-step-progress" class="text-muted mb-1"></small>
|
||||
</div>
|
||||
<div class="progress" role="progressbar" aria-label="Task Progress" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="height: 20px;">
|
||||
<div id="task-progress-bar" class="progress-bar progress-bar-striped progress-bar-animated bg-info" style="width: 0%"></div>
|
||||
</div>
|
||||
</div>
|
||||
<p id="current-item-name" class="small text-muted mt-2 mb-0"></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -59,21 +75,58 @@
|
||||
const batchBtn = document.getElementById('batch-generate-btn');
|
||||
const regenAllBtn = document.getElementById('regenerate-all-btn');
|
||||
const progressBar = document.getElementById('batch-progress-bar');
|
||||
const taskProgressBar = document.getElementById('task-progress-bar');
|
||||
const container = document.getElementById('batch-progress-container');
|
||||
const statusText = document.getElementById('batch-status-text');
|
||||
const nodeStatus = document.getElementById('batch-node-status');
|
||||
const itemNameText = document.getElementById('current-item-name');
|
||||
const stepProgressText = document.getElementById('current-step-progress');
|
||||
|
||||
const clientId = 'outfits_batch_' + Math.random().toString(36).substring(2, 15);
|
||||
const socket = new WebSocket(`ws://127.0.0.1:8188/ws?clientId=${clientId}`);
|
||||
const socket = new WebSocket('{{ COMFYUI_WS_URL }}?clientId=' + clientId);
|
||||
|
||||
const nodeNames = {
|
||||
"3": "Sampling",
|
||||
"11": "Face Detailing",
|
||||
"13": "Hand Detailing",
|
||||
"4": "Loading Models",
|
||||
"16": "Character LoRA",
|
||||
"17": "Outfit LoRA",
|
||||
"18": "Action LoRA",
|
||||
"19": "Style/Detailer LoRA",
|
||||
"8": "Decoding",
|
||||
"9": "Saving"
|
||||
};
|
||||
|
||||
let currentPromptId = null;
|
||||
let resolveGeneration = null;
|
||||
|
||||
socket.addEventListener('message', (event) => {
|
||||
const msg = JSON.parse(event.data);
|
||||
if (msg.type === 'executing') {
|
||||
if (msg.data.node === null && msg.data.prompt_id === currentPromptId) {
|
||||
|
||||
if (msg.type === 'progress') {
|
||||
if (msg.data.prompt_id !== currentPromptId) return;
|
||||
const value = msg.data.value;
|
||||
const max = msg.data.max;
|
||||
const percent = Math.round((value / max) * 100);
|
||||
stepProgressText.textContent = `${percent}%`;
|
||||
taskProgressBar.style.width = `${percent}%`;
|
||||
taskProgressBar.textContent = `${percent}%`;
|
||||
taskProgressBar.classList.remove('progress-bar-striped', 'progress-bar-animated');
|
||||
}
|
||||
else if (msg.type === 'executing') {
|
||||
if (msg.data.prompt_id !== currentPromptId) return;
|
||||
const nodeId = msg.data.node;
|
||||
if (nodeId === null) {
|
||||
if (resolveGeneration) resolveGeneration();
|
||||
} else {
|
||||
nodeStatus.textContent = nodeNames[nodeId] || `Processing...`;
|
||||
stepProgressText.textContent = "";
|
||||
if (nodeId !== "3") {
|
||||
taskProgressBar.style.width = '100%';
|
||||
taskProgressBar.textContent = nodeNames[nodeId] || 'Processing...';
|
||||
taskProgressBar.classList.add('progress-bar-striped', 'progress-bar-animated');
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -113,15 +166,18 @@
|
||||
|
||||
let completed = 0;
|
||||
for (const item of missing) {
|
||||
completed++;
|
||||
const percent = Math.round((completed / missing.length) * 100);
|
||||
progressBar.style.width = `${percent}%`;
|
||||
progressBar.textContent = `${percent}%`;
|
||||
statusText.textContent = `Batch Generating Outfits: ${completed} / ${missing.length}`;
|
||||
statusText.textContent = `Batch Generating Outfits: ${completed + 1} / ${missing.length}`;
|
||||
itemNameText.textContent = `Current: ${item.name}`;
|
||||
nodeStatus.textContent = "Queuing...";
|
||||
|
||||
taskProgressBar.style.width = '100%';
|
||||
taskProgressBar.textContent = 'Queued';
|
||||
taskProgressBar.classList.add('progress-bar-striped', 'progress-bar-animated');
|
||||
|
||||
try {
|
||||
// Random character for outfit preview
|
||||
const genResp = await fetch(`/outfit/${item.slug}/generate`, {
|
||||
method: 'POST',
|
||||
body: new URLSearchParams({
|
||||
@@ -154,10 +210,17 @@
|
||||
} catch (err) {
|
||||
console.error(`Failed for ${item.name}:`, err);
|
||||
}
|
||||
completed++;
|
||||
}
|
||||
|
||||
progressBar.style.width = '100%';
|
||||
progressBar.textContent = '100%';
|
||||
statusText.textContent = "Batch Outfit Generation Complete!";
|
||||
itemNameText.textContent = "";
|
||||
nodeStatus.textContent = "Done";
|
||||
stepProgressText.textContent = "";
|
||||
taskProgressBar.style.width = '0%';
|
||||
taskProgressBar.textContent = '';
|
||||
batchBtn.disabled = false;
|
||||
regenAllBtn.disabled = false;
|
||||
setTimeout(() => { container.classList.add('d-none'); }, 5000);
|
||||
|
||||
Reference in New Issue
Block a user