Files
x0x0b f4ce0e2780 fix: image metadata in viewer (#37)
* fix: simplify metadata comparison by removing ignored keys logic

* fix: enhance prompt history retrieval by grouping entries and improving metadata handling

* lint
2025-12-25 09:33:00 +09:00

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,
};
});
}