Files
ComfyUI-Tenaciousload/web/tenaciousload.js
T
Ethanfel a8d8b3792c feat: add Quick (incremental) and Register refresh modes
- quick:    incremental rescan — re-walks only folders whose mtime changed
            (per-dir snapshot persisted to cache/scan_snapshot.json); reuses
            the cache for unchanged folders. Catches new/removed/renamed files.
- register: append specific file path(s) with NO folder walk (instant disk-wise)
- full:     unchanged default (clear cache -> full re-walk)

Frontend exposes all three as Extensions-menu commands; the graph node gains a
quick/full mode widget. POST /tenaciousload/refresh now takes {mode, folder, files}.
Unit-tested: incremental scan rescans only the changed dir; register adds/skips.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 01:25:12 +02:00

97 lines
3.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { app } from "../../scripts/app.js";
import { api } from "../../scripts/api.js";
let busy = false;
function notify(severity, detail, life = 6000) {
const toast = app.extensionManager?.toast;
if (toast?.add) {
toast.add({ severity, summary: "Tenaciousload", detail, life });
} else if (severity === "error") {
alert("Tenaciousload: " + detail);
}
console[severity === "error" ? "error" : "log"]("[Tenaciousload]", detail);
}
async function runRefresh(mode, extra) {
if (busy) {
notify("warn", "A refresh is already running…");
return;
}
busy = true;
const label =
mode === "quick" ? "Quick refresh (changed folders)" :
mode === "register" ? "Registering file(s)" : "Full refresh (rescan all)";
notify("info", `${label} — rebuilding model lists… this can take a moment.`, 10000);
try {
// 1) run the chosen refresh mode on the server
const res = await api.fetchApi("/tenaciousload/refresh", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ mode, ...(extra || {}) }),
});
const data = await res.json().catch(() => ({}));
// 2) rebuild + re-cache object_info (the slow build, if any)
await api.fetchApi("/object_info?nocache=1").then((r) => r.arrayBuffer());
// 3) update open dropdowns live if supported
try { await app.refreshComboInNodes?.(); } catch (e) { /* non-fatal */ }
let detail = "Done — new models are available (reload the page if a dropdown still looks stale).";
if (mode === "register") {
detail = `Registered ${data.added || 0} file(s)` +
(data.skipped ? `, ${data.skipped} not found on disk` : "") + ". " + detail;
} else if (mode === "quick") {
const dirs = (data.folders || []).reduce((a, f) => a + (f.scanned || 0), 0);
detail = `Quick scan: ${dirs} folder(s) rescanned. ` + detail;
}
notify("success", detail, 8000);
} catch (e) {
notify("error", "Refresh failed: " + (e?.message || e), 10000);
} finally {
busy = false;
}
}
async function doRegister() {
const folder = (prompt("Model folder type (loras, checkpoints, vae, …):", "loras") || "").trim();
if (!folder) return;
const raw = prompt(
`New file path(s) relative to the '${folder}' folder\n(comma- or newline-separated, e.g. mypack/newlora.safetensors):`,
"",
);
if (!raw) return;
const files = raw.split(/[\n,]+/).map((s) => s.trim()).filter(Boolean);
if (!files.length) return;
await runRefresh("register", { folder, files });
}
app.registerExtension({
name: "Tenaciousload.Refresh",
commands: [
{
id: "Tenaciousload.quick",
label: "⚡ Quick refresh (changed folders)",
icon: "pi pi-bolt",
function: () => runRefresh("quick"),
},
{
id: "Tenaciousload.full",
label: "🔄 Full refresh (rescan all models/LoRAs)",
icon: "pi pi-refresh",
function: () => runRefresh("full"),
},
{
id: "Tenaciousload.register",
label: " Register new model file…",
icon: "pi pi-plus",
function: doRegister,
},
],
menuCommands: [
{
path: ["Extensions"],
commands: ["Tenaciousload.quick", "Tenaciousload.full", "Tenaciousload.register"],
},
],
});