Files
MajoorWaldi-ComfyUI-Majoor-…/js/grid/fetch.js
T

181 lines
6.0 KiB
JavaScript

import { api } from "../../../../scripts/api.js";
export function createMetadataFetcher(state, gridView, opts = {}) {
let metadataAbortCtrl = null;
let metadataFetchInFlight = null;
let metadataDebounceTimer = null;
let currentGridView = gridView || null;
let onMetadataUpdated = typeof opts.onMetadataUpdated === "function" ? opts.onMetadataUpdated : null;
const setOnMetadataUpdated = (fn) => {
onMetadataUpdated = typeof fn === "function" ? fn : null;
};
const shouldHydrateForFilter = () => {
const tagFilter = (state.tagFilter || "").trim();
const minRating = Number(state.minRating || 0);
return !!tagFilter || minRating > 0;
};
const applyBatchResults = (data) => {
const metaByKey = new Map();
(data.metadatas || []).forEach((m) => metaByKey.set(`${m.subfolder || ""}/${m.filename || ""}`, m));
let updatedCount = 0;
state.files.forEach((f) => {
const key = `${f.subfolder || ""}/${f.filename || f.name || ""}`;
const m = metaByKey.get(key);
if (m) {
f.rating = m.rating ?? 0;
f.tags = m.tags || [];
if (m.has_workflow !== undefined) f.hasWorkflow = !!m.has_workflow;
f.__metaLoaded = true;
updatedCount += 1;
}
});
return updatedCount;
};
async function fetchMetadataForVisible() {
if (metadataFetchInFlight) return;
const reqVersion = state.renderVersion;
const fullList = state.filtered || [];
const { visibleStart: start, visibleEnd: end } = state;
const buffer = 20;
const effectiveStart = Math.max(0, start - buffer);
const effectiveEnd = Math.min(fullList.length, end + buffer);
const visibleSlice = fullList.slice(effectiveStart, effectiveEnd);
const targetFiles = visibleSlice.filter((f) => !f.__metaLoaded);
if (!targetFiles.length) return;
const batch = targetFiles.slice(0, 30).map((f) => ({ filename: f.filename || f.name, subfolder: f.subfolder || "" }));
if (metadataAbortCtrl) {
try {
metadataAbortCtrl.abort();
} catch (_) {}
}
metadataAbortCtrl = new AbortController();
const thisCtrl = metadataAbortCtrl;
metadataFetchInFlight = api.fetchApi("/mjr/filemanager/metadata/batch", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ items: batch }),
signal: thisCtrl.signal,
});
let updatedAny = false;
try {
const res = await metadataFetchInFlight;
if (thisCtrl.signal.aborted || reqVersion !== state.renderVersion) return;
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const data = await res.json();
if (thisCtrl.signal.aborted || reqVersion !== state.renderVersion) return;
updatedAny = applyBatchResults(data) > 0;
if (currentGridView) currentGridView.renderGrid();
} catch (err) {
if (err?.name === "AbortError") {
// ignore aborted fetch
} else {
console.warn("[Majoor.AssetsManager] batch metadata load failed", err);
}
} finally {
metadataFetchInFlight = null;
if (metadataAbortCtrl === thisCtrl) metadataAbortCtrl = null;
if (updatedAny && onMetadataUpdated) {
setTimeout(() => {
try {
onMetadataUpdated();
} catch (_) {}
}, 0);
}
if (reqVersion === state.renderVersion && fullList.slice(effectiveStart, effectiveEnd).some((f) => !f.__metaLoaded)) {
fetchMetadataForVisible();
}
}
}
async function fetchMetadataForFilter() {
if (metadataFetchInFlight) return;
if (!shouldHydrateForFilter()) return;
const reqVersion = state.renderVersion;
const allFiles = state.files || [];
const targetFiles = allFiles.filter((f) => !f.__metaLoaded).slice(0, 30);
if (!targetFiles.length) return;
const batch = targetFiles.map((f) => ({ filename: f.filename || f.name, subfolder: f.subfolder || "" }));
if (metadataAbortCtrl) {
try {
metadataAbortCtrl.abort();
} catch (_) {}
}
metadataAbortCtrl = new AbortController();
const thisCtrl = metadataAbortCtrl;
metadataFetchInFlight = api.fetchApi("/mjr/filemanager/metadata/batch", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ items: batch }),
signal: thisCtrl.signal,
});
let updatedAny = false;
try {
const res = await metadataFetchInFlight;
if (thisCtrl.signal.aborted || reqVersion !== state.renderVersion) return;
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const data = await res.json();
if (thisCtrl.signal.aborted || reqVersion !== state.renderVersion) return;
updatedAny = applyBatchResults(data) > 0;
if (currentGridView) currentGridView.renderGrid();
} catch (err) {
if (err?.name === "AbortError") {
// ignore aborted fetch
} else {
console.warn("[Majoor.AssetsManager] filter metadata load failed", err);
}
} finally {
metadataFetchInFlight = null;
if (metadataAbortCtrl === thisCtrl) metadataAbortCtrl = null;
if (updatedAny && onMetadataUpdated) {
setTimeout(() => {
try {
onMetadataUpdated();
} catch (_) {}
}, 0);
}
if (reqVersion === state.renderVersion && shouldHydrateForFilter() && allFiles.some((f) => !f.__metaLoaded)) {
fetchMetadataForFilter();
}
}
}
const onRequestMetadata = (start, end) => {
state.visibleStart = start;
state.visibleEnd = end;
if (metadataDebounceTimer) clearTimeout(metadataDebounceTimer);
metadataDebounceTimer = setTimeout(() => fetchMetadataForVisible(), 150);
};
const cleanup = () => {
if (metadataDebounceTimer) clearTimeout(metadataDebounceTimer);
if (metadataAbortCtrl) {
try {
metadataAbortCtrl.abort();
} catch (_) {}
}
};
const setGridView = (gv) => {
currentGridView = gv;
};
return { fetchMetadataForVisible, fetchMetadataForFilter, onRequestMetadata, cleanup, setGridView, setOnMetadataUpdated };
}