* fix: simplify metadata comparison by removing ignored keys logic * fix: enhance prompt history retrieval by grouping entries and improving metadata handling * lint
183 lines
4.7 KiB
JavaScript
183 lines
4.7 KiB
JavaScript
const IMAGE_EXTENSIONS = new Set([
|
|
".png",
|
|
".jpg",
|
|
".jpeg",
|
|
".gif",
|
|
".bmp",
|
|
".webp",
|
|
".tiff",
|
|
".tif",
|
|
".avif",
|
|
".svg",
|
|
]);
|
|
|
|
const OUTPUT_COLLECTION_KEYS = ["files", "images", "outputs"];
|
|
|
|
function lowerExtension(name) {
|
|
if (typeof name !== "string") return "";
|
|
const index = name.lastIndexOf(".");
|
|
if (index === -1) return "";
|
|
return name.slice(index).trim().toLowerCase();
|
|
}
|
|
|
|
function isLikelyImage(name) {
|
|
return IMAGE_EXTENSIONS.has(lowerExtension(name));
|
|
}
|
|
|
|
function resolveUrl(api, params) {
|
|
const search = new URLSearchParams(params);
|
|
const path = `/view?${search.toString()}`;
|
|
if (api?.fileURL) {
|
|
try {
|
|
return api.fileURL(path);
|
|
} catch (_) {
|
|
/* ignore and fall back */
|
|
}
|
|
}
|
|
return path;
|
|
}
|
|
|
|
function normalizeDescriptor(candidate, { allowMissingExtension = false } = {}) {
|
|
if (!candidate) return null;
|
|
if (typeof candidate === "string") {
|
|
const trimmed = candidate.trim();
|
|
if (!trimmed) return null;
|
|
if (!allowMissingExtension && !isLikelyImage(trimmed)) return null;
|
|
return { filename: trimmed };
|
|
}
|
|
if (typeof candidate !== "object") return null;
|
|
|
|
const filename = candidate.filename ?? candidate.name;
|
|
if (!filename) return null;
|
|
if (!allowMissingExtension && !isLikelyImage(filename)) return null;
|
|
|
|
const record = {
|
|
filename: String(filename),
|
|
};
|
|
|
|
const entryId = candidate.entry_id ?? candidate.entryId ?? candidate.source_entry_id;
|
|
if (entryId) {
|
|
record.entryId = String(entryId);
|
|
}
|
|
|
|
if (candidate.subfolder) {
|
|
record.subfolder = String(candidate.subfolder);
|
|
}
|
|
|
|
const type = candidate.type ?? candidate.kind ?? (candidate.metadata && candidate.metadata.type);
|
|
if (type) {
|
|
record.type = String(type);
|
|
}
|
|
|
|
if (candidate.preview !== undefined) {
|
|
record.preview = candidate.preview;
|
|
}
|
|
|
|
if (candidate.thumbnail ?? candidate.thumb ?? candidate.preview_url ?? candidate.url) {
|
|
record.thumbnail =
|
|
candidate.thumbnail ?? candidate.thumb ?? candidate.preview_url ?? candidate.url;
|
|
}
|
|
|
|
if (
|
|
candidate.title ??
|
|
candidate.label ??
|
|
candidate.caption ??
|
|
candidate.prompt ??
|
|
candidate.name
|
|
) {
|
|
record.title =
|
|
candidate.title ?? candidate.label ?? candidate.caption ?? candidate.prompt ?? candidate.name;
|
|
}
|
|
|
|
return record;
|
|
}
|
|
|
|
function appendFromCollection(result, seen, collection, { allowMissingExtension = false } = {}) {
|
|
if (!Array.isArray(collection)) return;
|
|
for (const item of collection) {
|
|
const descriptor = normalizeDescriptor(item, {
|
|
allowMissingExtension,
|
|
});
|
|
if (!descriptor) continue;
|
|
|
|
const params = {
|
|
filename: descriptor.filename,
|
|
type: descriptor.type ? String(descriptor.type) : "output",
|
|
};
|
|
if (descriptor.subfolder) {
|
|
params.subfolder = descriptor.subfolder;
|
|
}
|
|
if (descriptor.preview !== undefined) {
|
|
params.preview = String(descriptor.preview);
|
|
}
|
|
|
|
const rawUrl = resolveUrl(null, params);
|
|
const key = rawUrl;
|
|
if (seen.has(key)) continue;
|
|
seen.add(key);
|
|
|
|
result.push({
|
|
params,
|
|
title: descriptor.title ? String(descriptor.title) : descriptor.filename,
|
|
thumbHint: descriptor.thumbnail ?? null,
|
|
entryId: descriptor.entryId ?? null,
|
|
});
|
|
}
|
|
}
|
|
|
|
function collectMetadataCollections(entry) {
|
|
const collections = [];
|
|
const metadata = entry?.metadata;
|
|
if (!metadata || typeof metadata !== "object") {
|
|
return collections;
|
|
}
|
|
|
|
for (const key of OUTPUT_COLLECTION_KEYS) {
|
|
const value = metadata[key];
|
|
if (Array.isArray(value)) {
|
|
collections.push(value);
|
|
} else if (value && typeof value === "object") {
|
|
collections.push(...Object.values(value).filter(Array.isArray));
|
|
}
|
|
}
|
|
|
|
return collections;
|
|
}
|
|
|
|
export function buildImageSources(entry, api) {
|
|
const result = [];
|
|
const seen = new Set();
|
|
const collections = [
|
|
{ items: Array.isArray(entry?.files) ? entry.files : null, allowMissingExtension: false },
|
|
...collectMetadataCollections(entry).map((items) => ({
|
|
items,
|
|
allowMissingExtension: true,
|
|
})),
|
|
];
|
|
|
|
for (const { items, allowMissingExtension } of collections) {
|
|
if (!items) continue;
|
|
appendFromCollection(result, seen, items, { allowMissingExtension });
|
|
}
|
|
|
|
return result.map((item) => {
|
|
const url = resolveUrl(api, item.params);
|
|
let thumb = resolveUrl(api, item.params);
|
|
if (item.thumbHint) {
|
|
const hint = String(item.thumbHint);
|
|
if (hint.startsWith("/view?")) {
|
|
const searchParams = new URLSearchParams(hint.slice(hint.indexOf("?") + 1));
|
|
thumb = resolveUrl(api, Object.fromEntries(searchParams.entries()));
|
|
} else {
|
|
thumb = hint;
|
|
}
|
|
}
|
|
return {
|
|
url,
|
|
thumb,
|
|
title: item.title,
|
|
entryId: item.entryId ?? null,
|
|
};
|
|
});
|
|
}
|