@enslo/sd-metadata 1.7.0 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ja.md +3 -3
- package/README.md +3 -3
- package/dist/index.js +434 -365
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -96,8 +96,7 @@ function parseSettings(settings) {
|
|
|
96
96
|
const result = /* @__PURE__ */ new Map();
|
|
97
97
|
if (!settings) return result;
|
|
98
98
|
const regex = /([A-Za-z][A-Za-z0-9 ]*?):\s*([^,]+?)(?=,\s*[A-Za-z][A-Za-z0-9 ]*?:|$)/g;
|
|
99
|
-
const
|
|
100
|
-
for (const match of matches) {
|
|
99
|
+
for (const match of settings.matchAll(regex)) {
|
|
101
100
|
const key = (match[1] ?? "").trim();
|
|
102
101
|
const value = (match[2] ?? "").trim();
|
|
103
102
|
result.set(key, value);
|
|
@@ -162,83 +161,45 @@ function toJsonResult(value) {
|
|
|
162
161
|
}
|
|
163
162
|
}
|
|
164
163
|
|
|
165
|
-
// src/parsers/comfyui-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
if (!extraMeta.transformations) return {};
|
|
198
|
-
const upscaleTransform = extraMeta.transformations.find(
|
|
199
|
-
(t) => t.type === "upscale"
|
|
200
|
-
);
|
|
201
|
-
if (!upscaleTransform?.upscaleWidth) return {};
|
|
202
|
-
const scale = calculateScale(
|
|
203
|
-
upscaleTransform.upscaleWidth,
|
|
204
|
-
extraMeta.width ?? 0
|
|
205
|
-
);
|
|
206
|
-
if (scale === void 0) return {};
|
|
207
|
-
return {
|
|
208
|
-
upscale: { scale }
|
|
209
|
-
};
|
|
210
|
-
}
|
|
211
|
-
function buildCivitaiSampling(extraMeta) {
|
|
212
|
-
if (extraMeta.seed === void 0 && extraMeta.steps === void 0 && extraMeta.cfgScale === void 0 && extraMeta.sampler === void 0) {
|
|
213
|
-
return {};
|
|
214
|
-
}
|
|
215
|
-
return {
|
|
216
|
-
sampling: {
|
|
217
|
-
seed: extraMeta.seed,
|
|
218
|
-
steps: extraMeta.steps,
|
|
219
|
-
cfg: extraMeta.cfgScale,
|
|
220
|
-
sampler: extraMeta.sampler
|
|
164
|
+
// src/parsers/comfyui-nodes.ts
|
|
165
|
+
var SAMPLER_TYPES = ["KSampler", "KSamplerAdvanced", "SamplerCustomAdvanced"];
|
|
166
|
+
var LATENT_IMAGE_TYPES = ["EmptyLatentImage"];
|
|
167
|
+
var LATENT_IMAGE_RGTHREE_TYPES = ["SDXL Empty Latent Image (rgthree)"];
|
|
168
|
+
var CHECKPOINT_TYPES = ["CheckpointLoaderSimple", "CheckpointLoader"];
|
|
169
|
+
var UNET_LOADER_TYPES = ["UNETLoader"];
|
|
170
|
+
var HIRES_MODEL_UPSCALE_TYPES = ["UpscaleModelLoader"];
|
|
171
|
+
var HIRES_IMAGE_SCALE_TYPES = ["ImageScale", "ImageScaleBy"];
|
|
172
|
+
var LATENT_UPSCALE_TYPES = ["LatentUpscale", "LatentUpscaleBy"];
|
|
173
|
+
var VAE_ENCODE_TYPES = ["VAEEncode", "VAEEncodeTiled"];
|
|
174
|
+
function classifyNodes(nodes) {
|
|
175
|
+
const result = {};
|
|
176
|
+
for (const node of Object.values(nodes)) {
|
|
177
|
+
const ct = node.class_type;
|
|
178
|
+
if (!result.sampler && SAMPLER_TYPES.includes(ct)) {
|
|
179
|
+
result.sampler = node;
|
|
180
|
+
} else if (!result.latentImage && LATENT_IMAGE_TYPES.includes(ct)) {
|
|
181
|
+
result.latentImage = node;
|
|
182
|
+
} else if (!result.latentImageRgthree && LATENT_IMAGE_RGTHREE_TYPES.includes(ct)) {
|
|
183
|
+
result.latentImageRgthree = node;
|
|
184
|
+
} else if (!result.checkpoint && CHECKPOINT_TYPES.includes(ct)) {
|
|
185
|
+
result.checkpoint = node;
|
|
186
|
+
} else if (!result.unetLoader && UNET_LOADER_TYPES.includes(ct)) {
|
|
187
|
+
result.unetLoader = node;
|
|
188
|
+
} else if (!result.hiresModelUpscale && HIRES_MODEL_UPSCALE_TYPES.includes(ct)) {
|
|
189
|
+
result.hiresModelUpscale = node;
|
|
190
|
+
} else if (!result.hiresImageScale && HIRES_IMAGE_SCALE_TYPES.includes(ct)) {
|
|
191
|
+
result.hiresImageScale = node;
|
|
192
|
+
} else if (!result.latentUpscale && LATENT_UPSCALE_TYPES.includes(ct)) {
|
|
193
|
+
result.latentUpscale = node;
|
|
194
|
+
} else if (!result.vaeEncode && VAE_ENCODE_TYPES.includes(ct)) {
|
|
195
|
+
result.vaeEncode = node;
|
|
221
196
|
}
|
|
222
|
-
}
|
|
197
|
+
}
|
|
198
|
+
return result;
|
|
223
199
|
}
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
sampler: ["KSampler", "KSamplerAdvanced", "SamplerCustomAdvanced"],
|
|
228
|
-
// Standard latent image nodes with width/height properties
|
|
229
|
-
latentImage: ["EmptyLatentImage"],
|
|
230
|
-
// rgthree latent image nodes with "dimensions" string property
|
|
231
|
-
latentImageRgthree: ["SDXL Empty Latent Image (rgthree)"],
|
|
232
|
-
checkpoint: ["CheckpointLoaderSimple", "CheckpointLoader"],
|
|
233
|
-
hiresModelUpscale: ["UpscaleModelLoader"],
|
|
234
|
-
hiresImageScale: ["ImageScale", "ImageScaleBy"],
|
|
235
|
-
latentUpscale: ["LatentUpscale", "LatentUpscaleBy"],
|
|
236
|
-
vaeEncode: ["VAEEncode", "VAEEncodeTiled"]
|
|
237
|
-
};
|
|
238
|
-
function findNode(nodes, classTypes) {
|
|
239
|
-
return Object.values(nodes).find(
|
|
240
|
-
(node) => classTypes.includes(node.class_type)
|
|
241
|
-
);
|
|
200
|
+
function resolveNode(nodes, ref) {
|
|
201
|
+
if (!isNodeReference(ref)) return void 0;
|
|
202
|
+
return nodes[String(ref[0])];
|
|
242
203
|
}
|
|
243
204
|
function isNodeReference(value) {
|
|
244
205
|
return Array.isArray(value) && value.length === 2 && (typeof value[0] === "string" || typeof value[0] === "number") && typeof value[1] === "number";
|
|
@@ -256,28 +217,31 @@ function extractText(nodes, nodeId, maxDepth = 10) {
|
|
|
256
217
|
}
|
|
257
218
|
return "";
|
|
258
219
|
}
|
|
259
|
-
function
|
|
260
|
-
const
|
|
220
|
+
function resolveConditioningSource(nodes, sampler) {
|
|
221
|
+
const guiderNode = resolveNode(nodes, sampler.inputs.guider);
|
|
222
|
+
if (guiderNode) return guiderNode;
|
|
223
|
+
return sampler;
|
|
224
|
+
}
|
|
225
|
+
function extractPromptTexts(nodes, sampler) {
|
|
261
226
|
if (!sampler) {
|
|
262
227
|
return { promptText: "", negativeText: "" };
|
|
263
228
|
}
|
|
264
|
-
const
|
|
265
|
-
const
|
|
229
|
+
const conditioningSource = resolveConditioningSource(nodes, sampler);
|
|
230
|
+
const positiveRef = conditioningSource.inputs.positive;
|
|
231
|
+
const negativeRef = conditioningSource.inputs.negative;
|
|
266
232
|
return {
|
|
267
233
|
promptText: isNodeReference(positiveRef) ? extractText(nodes, String(positiveRef[0])) : "",
|
|
268
234
|
negativeText: isNodeReference(negativeRef) ? extractText(nodes, String(negativeRef[0])) : ""
|
|
269
235
|
};
|
|
270
236
|
}
|
|
271
|
-
function extractDimensions(
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
const
|
|
275
|
-
const height = Number(standardLatent.inputs.height) || 0;
|
|
237
|
+
function extractDimensions(latentImage, latentImageRgthree) {
|
|
238
|
+
if (latentImage) {
|
|
239
|
+
const width = Number(latentImage.inputs.width) || 0;
|
|
240
|
+
const height = Number(latentImage.inputs.height) || 0;
|
|
276
241
|
if (width > 0 && height > 0) return { width, height };
|
|
277
242
|
}
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
const match = rgthreeLatent.inputs.dimensions.match(/^(\d+)\s*x\s*(\d+)/);
|
|
243
|
+
if (latentImageRgthree && typeof latentImageRgthree.inputs.dimensions === "string") {
|
|
244
|
+
const match = latentImageRgthree.inputs.dimensions.match(/^(\d+)\s*x\s*(\d+)/);
|
|
281
245
|
if (match?.[1] && match[2]) {
|
|
282
246
|
return {
|
|
283
247
|
width: Number.parseInt(match[1], 10),
|
|
@@ -287,9 +251,11 @@ function extractDimensions(nodes) {
|
|
|
287
251
|
}
|
|
288
252
|
return { width: 0, height: 0 };
|
|
289
253
|
}
|
|
290
|
-
function extractSampling(nodes) {
|
|
291
|
-
const sampler = findNode(nodes, CLASS_TYPES.sampler);
|
|
254
|
+
function extractSampling(nodes, sampler) {
|
|
292
255
|
if (!sampler) return void 0;
|
|
256
|
+
if (sampler.class_type === "SamplerCustomAdvanced") {
|
|
257
|
+
return extractAdvancedSampling(nodes, sampler);
|
|
258
|
+
}
|
|
293
259
|
let seed = sampler.inputs.seed;
|
|
294
260
|
if (isNodeReference(seed)) {
|
|
295
261
|
const seedNode = nodes[String(seed[0])];
|
|
@@ -306,36 +272,112 @@ function extractSampling(nodes) {
|
|
|
306
272
|
denoise
|
|
307
273
|
};
|
|
308
274
|
}
|
|
309
|
-
function
|
|
310
|
-
const
|
|
311
|
-
|
|
312
|
-
|
|
275
|
+
function extractAdvancedSampling(nodes, sampler) {
|
|
276
|
+
const noiseNode = resolveNode(nodes, sampler.inputs.noise);
|
|
277
|
+
const guiderNode = resolveNode(nodes, sampler.inputs.guider);
|
|
278
|
+
const samplerSelectNode = resolveNode(nodes, sampler.inputs.sampler);
|
|
279
|
+
const schedulerNode = resolveNode(nodes, sampler.inputs.sigmas);
|
|
280
|
+
const rawDenoise = schedulerNode?.inputs.denoise;
|
|
281
|
+
const denoise = typeof rawDenoise === "number" && rawDenoise < 1 ? rawDenoise : void 0;
|
|
282
|
+
return {
|
|
283
|
+
seed: noiseNode?.inputs.noise_seed,
|
|
284
|
+
steps: schedulerNode?.inputs.steps,
|
|
285
|
+
cfg: guiderNode?.inputs.cfg,
|
|
286
|
+
sampler: samplerSelectNode?.inputs.sampler_name,
|
|
287
|
+
scheduler: schedulerNode?.inputs.scheduler,
|
|
288
|
+
denoise
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
function extractModel(checkpoint, unetLoader) {
|
|
292
|
+
if (checkpoint?.inputs?.ckpt_name) {
|
|
293
|
+
return { name: String(checkpoint.inputs.ckpt_name) };
|
|
294
|
+
}
|
|
295
|
+
if (unetLoader?.inputs?.unet_name) {
|
|
296
|
+
return { name: String(unetLoader.inputs.unet_name) };
|
|
297
|
+
}
|
|
298
|
+
return void 0;
|
|
299
|
+
}
|
|
300
|
+
function calculateScale(targetWidth, baseWidth) {
|
|
301
|
+
if (baseWidth <= 0 || targetWidth <= 0) return void 0;
|
|
302
|
+
return Math.round(targetWidth / baseWidth * 100) / 100;
|
|
313
303
|
}
|
|
314
304
|
function isHiresSampler(nodes, sampler) {
|
|
315
305
|
const latentImageRef = sampler.inputs.latent_image;
|
|
316
306
|
if (!isNodeReference(latentImageRef)) return false;
|
|
317
307
|
const inputNode = nodes[String(latentImageRef[0])];
|
|
318
308
|
if (!inputNode) return false;
|
|
319
|
-
|
|
320
|
-
if (latentUpscaleTypes.includes(inputNode.class_type)) {
|
|
309
|
+
if (LATENT_UPSCALE_TYPES.includes(inputNode.class_type)) {
|
|
321
310
|
return true;
|
|
322
311
|
}
|
|
323
|
-
|
|
324
|
-
if (!vaeTypes.includes(inputNode.class_type)) return false;
|
|
312
|
+
if (!VAE_ENCODE_TYPES.includes(inputNode.class_type)) return false;
|
|
325
313
|
const pixelsRef = inputNode.inputs.pixels;
|
|
326
314
|
if (!isNodeReference(pixelsRef)) return false;
|
|
327
315
|
const upscaleNode = nodes[String(pixelsRef[0])];
|
|
328
316
|
if (!upscaleNode) return false;
|
|
329
|
-
|
|
330
|
-
return imageScaleTypes.includes(upscaleNode.class_type);
|
|
317
|
+
return HIRES_IMAGE_SCALE_TYPES.includes(upscaleNode.class_type);
|
|
331
318
|
}
|
|
332
319
|
function findHiresSampler(nodes) {
|
|
333
|
-
const samplerTypes = CLASS_TYPES.sampler;
|
|
334
320
|
return Object.values(nodes).find(
|
|
335
|
-
(node) =>
|
|
321
|
+
(node) => SAMPLER_TYPES.includes(node.class_type) && isHiresSampler(nodes, node)
|
|
336
322
|
);
|
|
337
323
|
}
|
|
338
324
|
|
|
325
|
+
// src/parsers/comfyui-civitai.ts
|
|
326
|
+
function extractExtraMetadata(prompt, entryRecord) {
|
|
327
|
+
const extraMetaField = prompt.extraMetadata;
|
|
328
|
+
if (typeof extraMetaField === "string") {
|
|
329
|
+
const parsed = parseJson(extraMetaField);
|
|
330
|
+
if (parsed.ok && parsed.type === "object") return parsed.value;
|
|
331
|
+
}
|
|
332
|
+
if (entryRecord?.extraMetadata) {
|
|
333
|
+
const parsed = parseJson(entryRecord.extraMetadata);
|
|
334
|
+
if (parsed.ok && parsed.type === "object") return parsed.value;
|
|
335
|
+
}
|
|
336
|
+
return void 0;
|
|
337
|
+
}
|
|
338
|
+
function extractCivitaiMetadata(extraMeta) {
|
|
339
|
+
if (!extraMeta) return void 0;
|
|
340
|
+
const upscale = buildCivitaiUpscale(extraMeta);
|
|
341
|
+
const sampling = buildCivitaiSampling(extraMeta);
|
|
342
|
+
return trimObject({
|
|
343
|
+
prompt: extraMeta.prompt,
|
|
344
|
+
negativePrompt: extraMeta.negativePrompt,
|
|
345
|
+
width: extraMeta.width,
|
|
346
|
+
height: extraMeta.height,
|
|
347
|
+
model: extraMeta.baseModel ? { name: extraMeta.baseModel } : void 0,
|
|
348
|
+
...sampling,
|
|
349
|
+
...upscale
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
function buildCivitaiUpscale(extraMeta) {
|
|
353
|
+
if (!extraMeta.transformations) return {};
|
|
354
|
+
const upscaleTransform = extraMeta.transformations.find(
|
|
355
|
+
(t) => t.type === "upscale"
|
|
356
|
+
);
|
|
357
|
+
if (!upscaleTransform?.upscaleWidth) return {};
|
|
358
|
+
const scale = calculateScale(
|
|
359
|
+
upscaleTransform.upscaleWidth,
|
|
360
|
+
extraMeta.width ?? 0
|
|
361
|
+
);
|
|
362
|
+
if (scale === void 0) return {};
|
|
363
|
+
return {
|
|
364
|
+
upscale: { scale }
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
function buildCivitaiSampling(extraMeta) {
|
|
368
|
+
if (extraMeta.seed === void 0 && extraMeta.steps === void 0 && extraMeta.cfgScale === void 0 && extraMeta.sampler === void 0) {
|
|
369
|
+
return {};
|
|
370
|
+
}
|
|
371
|
+
return {
|
|
372
|
+
sampling: {
|
|
373
|
+
seed: extraMeta.seed,
|
|
374
|
+
steps: extraMeta.steps,
|
|
375
|
+
cfg: extraMeta.cfgScale,
|
|
376
|
+
sampler: extraMeta.sampler
|
|
377
|
+
}
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
|
|
339
381
|
// src/parsers/comfyui.ts
|
|
340
382
|
var CIVITAI_EXTENSION_KEYS = ["extra", "extraMetadata", "resource-stack"];
|
|
341
383
|
function parseComfyUI(entries) {
|
|
@@ -373,10 +415,6 @@ function parseComfyUI(entries) {
|
|
|
373
415
|
function cleanJsonString(json) {
|
|
374
416
|
return json.replace(/\0+$/, "").replace(/:\s*NaN\b/g, ": null");
|
|
375
417
|
}
|
|
376
|
-
function calculateScale2(targetWidth, baseWidth) {
|
|
377
|
-
if (baseWidth <= 0 || targetWidth <= 0) return void 0;
|
|
378
|
-
return Math.round(targetWidth / baseWidth * 100) / 100;
|
|
379
|
-
}
|
|
380
418
|
function findPromptJson(entryRecord) {
|
|
381
419
|
if (entryRecord.prompt) {
|
|
382
420
|
return cleanJsonString(entryRecord.prompt);
|
|
@@ -408,51 +446,61 @@ function findPromptJson(entryRecord) {
|
|
|
408
446
|
return void 0;
|
|
409
447
|
}
|
|
410
448
|
function extractComfyUIMetadata(nodes) {
|
|
411
|
-
const
|
|
412
|
-
const {
|
|
413
|
-
const
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
449
|
+
const c = classifyNodes(nodes);
|
|
450
|
+
const { promptText, negativeText } = extractPromptTexts(nodes, c.sampler);
|
|
451
|
+
const { width, height } = extractDimensions(
|
|
452
|
+
c.latentImage,
|
|
453
|
+
c.latentImageRgthree
|
|
454
|
+
);
|
|
455
|
+
const hiresSamplerNode = findHiresSampler(nodes);
|
|
456
|
+
const hiresSampling = hiresSamplerNode ? extractSampling(nodes, hiresSamplerNode) : void 0;
|
|
457
|
+
const hiresScale = resolveHiresScale(nodes, c, width);
|
|
458
|
+
const upscalerName = c.hiresModelUpscale?.inputs.model_name;
|
|
417
459
|
return trimObject({
|
|
418
460
|
prompt: promptText || void 0,
|
|
419
461
|
negativePrompt: negativeText || void 0,
|
|
420
462
|
width: width > 0 ? width : void 0,
|
|
421
463
|
height: height > 0 ? height : void 0,
|
|
422
|
-
model: extractModel(
|
|
423
|
-
sampling: extractSampling(nodes),
|
|
424
|
-
...buildHiresOrUpscale(
|
|
425
|
-
hiresModel,
|
|
426
|
-
hiresImageScale,
|
|
427
|
-
latentUpscale,
|
|
428
|
-
hiresSampler,
|
|
429
|
-
width
|
|
430
|
-
)
|
|
464
|
+
model: extractModel(c.checkpoint, c.unetLoader),
|
|
465
|
+
sampling: extractSampling(nodes, c.sampler),
|
|
466
|
+
...buildHiresOrUpscale(upscalerName, hiresScale, hiresSampling)
|
|
431
467
|
});
|
|
432
468
|
}
|
|
433
|
-
function
|
|
434
|
-
|
|
435
|
-
let scale;
|
|
469
|
+
function resolveHiresScale(nodes, c, baseWidth) {
|
|
470
|
+
const latentUpscale = c.latentUpscale?.inputs;
|
|
436
471
|
if (latentUpscale?.scale_by !== void 0) {
|
|
437
|
-
|
|
438
|
-
}
|
|
439
|
-
|
|
472
|
+
return latentUpscale.scale_by;
|
|
473
|
+
}
|
|
474
|
+
const widthInput = c.hiresImageScale?.inputs.width;
|
|
475
|
+
if (widthInput === void 0) return void 0;
|
|
476
|
+
if (isNodeReference(widthInput)) {
|
|
477
|
+
const sourceNode = nodes[String(widthInput[0])];
|
|
478
|
+
if (typeof sourceNode?.inputs.clip_scale === "number") {
|
|
479
|
+
return sourceNode.inputs.clip_scale;
|
|
480
|
+
}
|
|
481
|
+
return void 0;
|
|
482
|
+
}
|
|
483
|
+
if (typeof widthInput === "number") {
|
|
484
|
+
return calculateScale(widthInput, baseWidth);
|
|
440
485
|
}
|
|
441
|
-
|
|
442
|
-
|
|
486
|
+
return void 0;
|
|
487
|
+
}
|
|
488
|
+
function buildHiresOrUpscale(upscalerName, scale, hiresSampling) {
|
|
489
|
+
if (!upscalerName && scale === void 0 && !hiresSampling) return {};
|
|
490
|
+
if (hiresSampling) {
|
|
443
491
|
return {
|
|
444
492
|
hires: {
|
|
445
|
-
upscaler,
|
|
493
|
+
upscaler: upscalerName,
|
|
446
494
|
scale,
|
|
447
|
-
steps:
|
|
448
|
-
denoise:
|
|
495
|
+
steps: hiresSampling.steps,
|
|
496
|
+
denoise: hiresSampling.denoise
|
|
449
497
|
}
|
|
450
498
|
};
|
|
451
499
|
}
|
|
452
|
-
if (!
|
|
500
|
+
if (!upscalerName) return {};
|
|
453
501
|
return {
|
|
454
502
|
upscale: {
|
|
455
|
-
upscaler,
|
|
503
|
+
upscaler: upscalerName,
|
|
456
504
|
scale
|
|
457
505
|
}
|
|
458
506
|
};
|
|
@@ -460,14 +508,10 @@ function buildHiresOrUpscale(hiresModel, hiresImageScale, latentUpscale, hiresSa
|
|
|
460
508
|
function mergeObjects(base, override) {
|
|
461
509
|
if (!base && !override) return void 0;
|
|
462
510
|
const merged = {};
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
}
|
|
468
|
-
if (override) {
|
|
469
|
-
for (const [key, value] of Object.entries(override)) {
|
|
470
|
-
if (value !== void 0) merged[key] = value;
|
|
511
|
+
for (const obj of [base, override]) {
|
|
512
|
+
if (!obj) continue;
|
|
513
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
514
|
+
if (v !== void 0) merged[k] = v;
|
|
471
515
|
}
|
|
472
516
|
}
|
|
473
517
|
return Object.keys(merged).length > 0 ? merged : void 0;
|
|
@@ -492,27 +536,23 @@ function mergeMetadata(base, override) {
|
|
|
492
536
|
}
|
|
493
537
|
|
|
494
538
|
// src/parsers/detect.ts
|
|
495
|
-
var
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
HF_MODEL: '"Model"',
|
|
513
|
-
HF_RESOLUTION: '"resolution"',
|
|
514
|
-
FOOOCUS_BASE: '"base_model"'
|
|
515
|
-
};
|
|
539
|
+
var M_INVOKEAI = "invokeai_metadata";
|
|
540
|
+
var M_TENSORART = "generation_data";
|
|
541
|
+
var M_STABILITY_MATRIX = "smproj";
|
|
542
|
+
var M_CIVITAI_EXTRA = "extraMetadata";
|
|
543
|
+
var M_SWARMUI = "sui_image_params";
|
|
544
|
+
var M_SWARM_VERSION = "swarm_version";
|
|
545
|
+
var M_COMFYUI_NODE = "class_type";
|
|
546
|
+
var M_NOVELAI_SCHEDULE = "noise_schedule";
|
|
547
|
+
var M_NOVELAI_V4 = "v4_prompt";
|
|
548
|
+
var M_NOVELAI_UNCOND = "uncond_scale";
|
|
549
|
+
var M_CIVITAI_NS = "civitai:";
|
|
550
|
+
var M_CIVITAI_RESOURCES = "Civitai resources:";
|
|
551
|
+
var M_RUINED_FOOOCUS = "RuinedFooocus";
|
|
552
|
+
var M_EASYDIFFUSION = "use_stable_diffusion_model";
|
|
553
|
+
var M_HF_MODEL = '"Model"';
|
|
554
|
+
var M_HF_RESOLUTION = '"resolution"';
|
|
555
|
+
var M_FOOOCUS_BASE = '"base_model"';
|
|
516
556
|
function detectSoftware(entries) {
|
|
517
557
|
const uniqueResult = detectUniqueKeywords(entries);
|
|
518
558
|
if (uniqueResult) return uniqueResult;
|
|
@@ -524,27 +564,24 @@ function detectSoftware(entries) {
|
|
|
524
564
|
}
|
|
525
565
|
return null;
|
|
526
566
|
}
|
|
567
|
+
function detectByUniqueKey(record) {
|
|
568
|
+
if (M_INVOKEAI in record) return "invokeai";
|
|
569
|
+
if (M_TENSORART in record) return "tensorart";
|
|
570
|
+
if (M_STABILITY_MATRIX in record) return "stability-matrix";
|
|
571
|
+
if (M_CIVITAI_EXTRA in record) return "civitai";
|
|
572
|
+
return null;
|
|
573
|
+
}
|
|
527
574
|
function detectUniqueKeywords(entryRecord) {
|
|
528
575
|
if (entryRecord.Software?.startsWith("NovelAI")) {
|
|
529
576
|
return "novelai";
|
|
530
577
|
}
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
}
|
|
534
|
-
if (MARKERS.TENSORART in entryRecord) {
|
|
535
|
-
return "tensorart";
|
|
536
|
-
}
|
|
537
|
-
if (MARKERS.STABILITY_MATRIX in entryRecord) {
|
|
538
|
-
return "stability-matrix";
|
|
539
|
-
}
|
|
578
|
+
const keyResult = detectByUniqueKey(entryRecord);
|
|
579
|
+
if (keyResult) return keyResult;
|
|
540
580
|
if ("negative_prompt" in entryRecord || "Negative Prompt" in entryRecord) {
|
|
541
581
|
return "easydiffusion";
|
|
542
582
|
}
|
|
543
|
-
if (MARKERS.CIVITAI_EXTRA in entryRecord) {
|
|
544
|
-
return "civitai";
|
|
545
|
-
}
|
|
546
583
|
const parameters = entryRecord.parameters;
|
|
547
|
-
if (parameters?.includes(
|
|
584
|
+
if (parameters?.includes(M_SWARMUI)) {
|
|
548
585
|
return "swarmui";
|
|
549
586
|
}
|
|
550
587
|
const comment = entryRecord.UserComment ?? entryRecord.Comment;
|
|
@@ -556,18 +593,8 @@ function detectUniqueKeywords(entryRecord) {
|
|
|
556
593
|
function detectFromCommentJson(comment) {
|
|
557
594
|
try {
|
|
558
595
|
const parsed = JSON.parse(comment);
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
}
|
|
562
|
-
if (MARKERS.TENSORART in parsed) {
|
|
563
|
-
return "tensorart";
|
|
564
|
-
}
|
|
565
|
-
if (MARKERS.STABILITY_MATRIX in parsed) {
|
|
566
|
-
return "stability-matrix";
|
|
567
|
-
}
|
|
568
|
-
if (MARKERS.CIVITAI_EXTRA in parsed) {
|
|
569
|
-
return "civitai";
|
|
570
|
-
}
|
|
596
|
+
const keyResult = detectByUniqueKey(parsed);
|
|
597
|
+
if (keyResult) return keyResult;
|
|
571
598
|
if ("prompt" in parsed && "workflow" in parsed) {
|
|
572
599
|
const workflow = parsed.workflow;
|
|
573
600
|
const prompt = parsed.prompt;
|
|
@@ -577,12 +604,12 @@ function detectFromCommentJson(comment) {
|
|
|
577
604
|
return "comfyui";
|
|
578
605
|
}
|
|
579
606
|
}
|
|
580
|
-
if (
|
|
607
|
+
if (M_SWARMUI in parsed) {
|
|
581
608
|
return "swarmui";
|
|
582
609
|
}
|
|
583
610
|
if ("prompt" in parsed && "parameters" in parsed) {
|
|
584
611
|
const params = String(parsed.parameters || "");
|
|
585
|
-
if (params.includes(
|
|
612
|
+
if (params.includes(M_SWARMUI) || params.includes(M_SWARM_VERSION)) {
|
|
586
613
|
return "swarmui";
|
|
587
614
|
}
|
|
588
615
|
}
|
|
@@ -600,13 +627,13 @@ function detectComfyUIEntries(entryRecord) {
|
|
|
600
627
|
if ("prompt" in entryRecord) {
|
|
601
628
|
const promptText = entryRecord.prompt;
|
|
602
629
|
if (promptText?.startsWith("{")) {
|
|
603
|
-
if (promptText.includes(
|
|
630
|
+
if (promptText.includes(M_SWARMUI)) {
|
|
604
631
|
return "swarmui";
|
|
605
632
|
}
|
|
606
|
-
if (promptText.includes(`"${
|
|
633
|
+
if (promptText.includes(`"${M_CIVITAI_EXTRA}"`)) {
|
|
607
634
|
return "civitai";
|
|
608
635
|
}
|
|
609
|
-
if (promptText.includes(
|
|
636
|
+
if (promptText.includes(M_COMFYUI_NODE)) {
|
|
610
637
|
return "comfyui";
|
|
611
638
|
}
|
|
612
639
|
}
|
|
@@ -620,25 +647,25 @@ function detectFromTextContent(text) {
|
|
|
620
647
|
return detectFromA1111Format(text);
|
|
621
648
|
}
|
|
622
649
|
function detectFromJsonFormat(json) {
|
|
623
|
-
if (json.includes(
|
|
650
|
+
if (json.includes(M_SWARMUI)) {
|
|
624
651
|
return "swarmui";
|
|
625
652
|
}
|
|
626
|
-
if (json.includes(`"software":"${
|
|
653
|
+
if (json.includes(`"software":"${M_RUINED_FOOOCUS}"`) || json.includes(`"software": "${M_RUINED_FOOOCUS}"`)) {
|
|
627
654
|
return "ruined-fooocus";
|
|
628
655
|
}
|
|
629
|
-
if (json.includes(`"${
|
|
656
|
+
if (json.includes(`"${M_EASYDIFFUSION}"`)) {
|
|
630
657
|
return "easydiffusion";
|
|
631
658
|
}
|
|
632
|
-
if (json.includes(
|
|
659
|
+
if (json.includes(M_CIVITAI_NS) || json.includes(`"${M_CIVITAI_EXTRA}"`)) {
|
|
633
660
|
return "civitai";
|
|
634
661
|
}
|
|
635
|
-
if (json.includes(`"${
|
|
662
|
+
if (json.includes(`"${M_NOVELAI_V4}"`) || json.includes(`"${M_NOVELAI_SCHEDULE}"`) || json.includes(`"${M_NOVELAI_UNCOND}"`) || json.includes('"Software":"NovelAI"') || json.includes(`\\"${M_NOVELAI_SCHEDULE}\\"`) || json.includes(`\\"${M_NOVELAI_V4}\\"`)) {
|
|
636
663
|
return "novelai";
|
|
637
664
|
}
|
|
638
|
-
if (json.includes(
|
|
665
|
+
if (json.includes(M_HF_MODEL) && json.includes(M_HF_RESOLUTION)) {
|
|
639
666
|
return "hf-space";
|
|
640
667
|
}
|
|
641
|
-
if (json.includes('"prompt"') && json.includes(
|
|
668
|
+
if (json.includes('"prompt"') && json.includes(M_FOOOCUS_BASE)) {
|
|
642
669
|
return "fooocus";
|
|
643
670
|
}
|
|
644
671
|
if (json.includes('"prompt"') || json.includes('"nodes"')) {
|
|
@@ -647,7 +674,7 @@ function detectFromJsonFormat(json) {
|
|
|
647
674
|
return null;
|
|
648
675
|
}
|
|
649
676
|
function detectFromA1111Format(text) {
|
|
650
|
-
if (text.includes(
|
|
677
|
+
if (text.includes(M_SWARMUI) || text.includes(M_SWARM_VERSION)) {
|
|
651
678
|
return "swarmui";
|
|
652
679
|
}
|
|
653
680
|
const versionMatch = text.match(/Version:\s*([^\s,]+)/);
|
|
@@ -666,7 +693,7 @@ function detectFromA1111Format(text) {
|
|
|
666
693
|
if (text.includes("App: SD.Next") || text.includes("App:SD.Next")) {
|
|
667
694
|
return "sd-next";
|
|
668
695
|
}
|
|
669
|
-
if (text.includes(
|
|
696
|
+
if (text.includes(M_CIVITAI_RESOURCES)) {
|
|
670
697
|
return "civitai";
|
|
671
698
|
}
|
|
672
699
|
if (text.includes("Steps:") && text.includes("Sampler:")) {
|
|
@@ -676,9 +703,6 @@ function detectFromA1111Format(text) {
|
|
|
676
703
|
}
|
|
677
704
|
|
|
678
705
|
// src/parsers/easydiffusion.ts
|
|
679
|
-
function getValue(json, keyA, keyB) {
|
|
680
|
-
return json[keyA] ?? json[keyB];
|
|
681
|
-
}
|
|
682
706
|
function extractModelName(path) {
|
|
683
707
|
if (!path) return void 0;
|
|
684
708
|
const parts = path.replace(/\\/g, "/").split("/");
|
|
@@ -686,7 +710,7 @@ function extractModelName(path) {
|
|
|
686
710
|
}
|
|
687
711
|
function parseEasyDiffusion(entries) {
|
|
688
712
|
if (entries.negative_prompt || entries["Negative Prompt"]) {
|
|
689
|
-
return
|
|
713
|
+
return buildMetadata(entries);
|
|
690
714
|
}
|
|
691
715
|
const jsonText = entries.parameters?.startsWith("{") ? entries.parameters : entries.UserComment?.startsWith("{") && entries.UserComment;
|
|
692
716
|
if (!jsonText) {
|
|
@@ -699,63 +723,38 @@ function parseEasyDiffusion(entries) {
|
|
|
699
723
|
message: "Invalid JSON in Easy Diffusion metadata"
|
|
700
724
|
});
|
|
701
725
|
}
|
|
702
|
-
return
|
|
726
|
+
return buildMetadata(parsed.value);
|
|
703
727
|
}
|
|
704
|
-
function
|
|
705
|
-
const
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
const width = Number(entryRecord.width ?? entryRecord.Width) || 0;
|
|
709
|
-
const height = Number(entryRecord.height ?? entryRecord.Height) || 0;
|
|
710
|
-
const metadata = {
|
|
711
|
-
software: "easydiffusion",
|
|
712
|
-
prompt: prompt.trim(),
|
|
713
|
-
negativePrompt: negativePrompt.trim(),
|
|
714
|
-
width,
|
|
715
|
-
height,
|
|
716
|
-
model: {
|
|
717
|
-
name: extractModelName(modelPath),
|
|
718
|
-
vae: entryRecord.use_vae_model ?? entryRecord["VAE model"]
|
|
719
|
-
},
|
|
720
|
-
sampling: {
|
|
721
|
-
sampler: entryRecord.sampler_name ?? entryRecord.Sampler,
|
|
722
|
-
steps: Number(entryRecord.num_inference_steps ?? entryRecord.Steps) || void 0,
|
|
723
|
-
cfg: Number(entryRecord.guidance_scale ?? entryRecord["Guidance Scale"]) || void 0,
|
|
724
|
-
seed: Number(entryRecord.seed ?? entryRecord.Seed) || void 0,
|
|
725
|
-
clipSkip: Number(entryRecord.clip_skip ?? entryRecord["Clip Skip"]) || void 0
|
|
726
|
-
}
|
|
728
|
+
function buildMetadata(data) {
|
|
729
|
+
const str = (keyA, keyB) => {
|
|
730
|
+
const v = data[keyA] ?? data[keyB];
|
|
731
|
+
return typeof v === "string" ? v : void 0;
|
|
727
732
|
};
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
const
|
|
733
|
-
const
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
"Stable Diffusion model"
|
|
737
|
-
);
|
|
738
|
-
const width = getValue(json, "width", "Width") ?? 0;
|
|
739
|
-
const height = getValue(json, "height", "Height") ?? 0;
|
|
740
|
-
const metadata = {
|
|
733
|
+
const num = (keyA, keyB) => {
|
|
734
|
+
const v = Number(data[keyA] ?? data[keyB]);
|
|
735
|
+
return v || void 0;
|
|
736
|
+
};
|
|
737
|
+
const prompt = (str("prompt", "Prompt") ?? "").trim();
|
|
738
|
+
const negativePrompt = (str("negative_prompt", "Negative Prompt") ?? "").trim();
|
|
739
|
+
const modelPath = str("use_stable_diffusion_model", "Stable Diffusion model");
|
|
740
|
+
return Result.ok({
|
|
741
741
|
software: "easydiffusion",
|
|
742
|
-
prompt
|
|
743
|
-
negativePrompt
|
|
744
|
-
width,
|
|
745
|
-
height,
|
|
742
|
+
prompt,
|
|
743
|
+
negativePrompt,
|
|
744
|
+
width: num("width", "Width") ?? 0,
|
|
745
|
+
height: num("height", "Height") ?? 0,
|
|
746
746
|
model: {
|
|
747
747
|
name: extractModelName(modelPath),
|
|
748
|
-
vae:
|
|
748
|
+
vae: str("use_vae_model", "VAE model")
|
|
749
749
|
},
|
|
750
750
|
sampling: {
|
|
751
|
-
sampler:
|
|
752
|
-
steps:
|
|
753
|
-
cfg:
|
|
754
|
-
seed:
|
|
755
|
-
clipSkip:
|
|
751
|
+
sampler: str("sampler_name", "Sampler"),
|
|
752
|
+
steps: num("num_inference_steps", "Steps"),
|
|
753
|
+
cfg: num("guidance_scale", "Guidance Scale"),
|
|
754
|
+
seed: num("seed", "Seed"),
|
|
755
|
+
clipSkip: num("clip_skip", "Clip Skip")
|
|
756
756
|
}
|
|
757
|
-
};
|
|
758
|
-
return Result.ok(metadata);
|
|
757
|
+
});
|
|
759
758
|
}
|
|
760
759
|
|
|
761
760
|
// src/parsers/fooocus.ts
|
|
@@ -832,7 +831,13 @@ function parseHfSpace(entries) {
|
|
|
832
831
|
steps: json.num_inference_steps,
|
|
833
832
|
cfg: json.guidance_scale,
|
|
834
833
|
seed: json.seed
|
|
835
|
-
})
|
|
834
|
+
}),
|
|
835
|
+
hires: json.use_upscaler ? trimObject({
|
|
836
|
+
upscaler: json.use_upscaler.upscale_method,
|
|
837
|
+
denoise: json.use_upscaler.upscaler_strength,
|
|
838
|
+
scale: json.use_upscaler.upscale_by,
|
|
839
|
+
steps: json.use_upscaler.upscale_steps
|
|
840
|
+
}) : void 0
|
|
836
841
|
};
|
|
837
842
|
return Result.ok(metadata);
|
|
838
843
|
}
|
|
@@ -1294,6 +1299,80 @@ function detectFormat(data) {
|
|
|
1294
1299
|
return null;
|
|
1295
1300
|
}
|
|
1296
1301
|
|
|
1302
|
+
// src/readers/dimensions.ts
|
|
1303
|
+
function readImageDimensions(data, format) {
|
|
1304
|
+
if (format === "png") return readPngDimensions(data);
|
|
1305
|
+
if (format === "jpeg") return readJpegDimensions(data);
|
|
1306
|
+
return readWebpDimensions(data);
|
|
1307
|
+
}
|
|
1308
|
+
function readPngDimensions(data) {
|
|
1309
|
+
const PNG_SIGNATURE_LENGTH2 = 8;
|
|
1310
|
+
if (data.length < 24) return null;
|
|
1311
|
+
return {
|
|
1312
|
+
width: readUint32BE(data, PNG_SIGNATURE_LENGTH2 + 8),
|
|
1313
|
+
height: readUint32BE(data, PNG_SIGNATURE_LENGTH2 + 12)
|
|
1314
|
+
};
|
|
1315
|
+
}
|
|
1316
|
+
function readJpegDimensions(data) {
|
|
1317
|
+
let offset = 2;
|
|
1318
|
+
while (offset < data.length - 4) {
|
|
1319
|
+
if (data[offset] !== 255) {
|
|
1320
|
+
offset++;
|
|
1321
|
+
continue;
|
|
1322
|
+
}
|
|
1323
|
+
const marker = data[offset + 1] ?? 0;
|
|
1324
|
+
if (marker === 255) {
|
|
1325
|
+
offset++;
|
|
1326
|
+
continue;
|
|
1327
|
+
}
|
|
1328
|
+
const length = (data[offset + 2] ?? 0) << 8 | (data[offset + 3] ?? 0);
|
|
1329
|
+
if (marker >= 192 && marker <= 207 && marker !== 196 && marker !== 200 && marker !== 204) {
|
|
1330
|
+
const height = (data[offset + 5] ?? 0) << 8 | (data[offset + 6] ?? 0);
|
|
1331
|
+
const width = (data[offset + 7] ?? 0) << 8 | (data[offset + 8] ?? 0);
|
|
1332
|
+
return { width, height };
|
|
1333
|
+
}
|
|
1334
|
+
offset += 2 + length;
|
|
1335
|
+
if (marker === 218) break;
|
|
1336
|
+
}
|
|
1337
|
+
return null;
|
|
1338
|
+
}
|
|
1339
|
+
function readWebpDimensions(data) {
|
|
1340
|
+
let offset = 12;
|
|
1341
|
+
while (offset < data.length) {
|
|
1342
|
+
if (offset + 8 > data.length) break;
|
|
1343
|
+
const chunkType = readChunkType(data, offset);
|
|
1344
|
+
const chunkSize = readUint32LE(data, offset + 4);
|
|
1345
|
+
const paddedSize = chunkSize + chunkSize % 2;
|
|
1346
|
+
if (chunkType === "VP8X") {
|
|
1347
|
+
const wMinus1 = readUint24LE(data, offset + 12);
|
|
1348
|
+
const hMinus1 = readUint24LE(data, offset + 15);
|
|
1349
|
+
return { width: wMinus1 + 1, height: hMinus1 + 1 };
|
|
1350
|
+
}
|
|
1351
|
+
if (chunkType === "VP8 ") {
|
|
1352
|
+
const start = offset + 8;
|
|
1353
|
+
const tag = (data[start] ?? 0) | (data[start + 1] ?? 0) << 8 | (data[start + 2] ?? 0) << 16;
|
|
1354
|
+
const keyFrame = !(tag & 1);
|
|
1355
|
+
if (keyFrame) {
|
|
1356
|
+
if (data[start + 3] === 157 && data[start + 4] === 1 && data[start + 5] === 42) {
|
|
1357
|
+
const wRaw = (data[start + 6] ?? 0) | (data[start + 7] ?? 0) << 8;
|
|
1358
|
+
const hRaw = (data[start + 8] ?? 0) | (data[start + 9] ?? 0) << 8;
|
|
1359
|
+
return { width: wRaw & 16383, height: hRaw & 16383 };
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
if (chunkType === "VP8L") {
|
|
1364
|
+
if (data[offset + 8] === 47) {
|
|
1365
|
+
const bits = readUint32LE(data, offset + 9);
|
|
1366
|
+
const width = (bits & 16383) + 1;
|
|
1367
|
+
const height = (bits >> 14 & 16383) + 1;
|
|
1368
|
+
return { width, height };
|
|
1369
|
+
}
|
|
1370
|
+
}
|
|
1371
|
+
offset += 8 + paddedSize;
|
|
1372
|
+
}
|
|
1373
|
+
return null;
|
|
1374
|
+
}
|
|
1375
|
+
|
|
1297
1376
|
// src/utils/exif-constants.ts
|
|
1298
1377
|
var USER_COMMENT_TAG = 37510;
|
|
1299
1378
|
var IMAGE_DESCRIPTION_TAG = 270;
|
|
@@ -1758,9 +1837,7 @@ function findExifChunk(data) {
|
|
|
1758
1837
|
|
|
1759
1838
|
// src/utils/convert.ts
|
|
1760
1839
|
function pngChunksToRecord(chunks) {
|
|
1761
|
-
return Object.
|
|
1762
|
-
Object.fromEntries(chunks.map((c) => [c.keyword, c.text]))
|
|
1763
|
-
);
|
|
1840
|
+
return Object.fromEntries(chunks.map((c) => [c.keyword, c.text]));
|
|
1764
1841
|
}
|
|
1765
1842
|
function segmentsToRecord(segments) {
|
|
1766
1843
|
const record = {};
|
|
@@ -1776,7 +1853,7 @@ function segmentsToRecord(segments) {
|
|
|
1776
1853
|
}
|
|
1777
1854
|
record[keyword] = text;
|
|
1778
1855
|
}
|
|
1779
|
-
return
|
|
1856
|
+
return record;
|
|
1780
1857
|
}
|
|
1781
1858
|
function tryExpandNovelAIWebpFormat(text) {
|
|
1782
1859
|
const outerParsed = parseJson(text);
|
|
@@ -1788,10 +1865,10 @@ function tryExpandNovelAIWebpFormat(text) {
|
|
|
1788
1865
|
return null;
|
|
1789
1866
|
}
|
|
1790
1867
|
const innerParsed = parseJson(outer.Comment);
|
|
1791
|
-
return
|
|
1868
|
+
return {
|
|
1792
1869
|
Software: typeof outer.Software === "string" ? outer.Software : "NovelAI",
|
|
1793
1870
|
Comment: innerParsed.ok ? JSON.stringify(innerParsed.value) : outer.Comment
|
|
1794
|
-
}
|
|
1871
|
+
};
|
|
1795
1872
|
}
|
|
1796
1873
|
function sourceToKeyword(source) {
|
|
1797
1874
|
switch (source.type) {
|
|
@@ -1825,7 +1902,7 @@ function read(input, options) {
|
|
|
1825
1902
|
}
|
|
1826
1903
|
const metadata = parseResult.value;
|
|
1827
1904
|
if (!options?.strict && (metadata.width === 0 || metadata.height === 0)) {
|
|
1828
|
-
const dims =
|
|
1905
|
+
const dims = readImageDimensions(data, format);
|
|
1829
1906
|
if (dims) {
|
|
1830
1907
|
metadata.width = metadata.width || dims.width;
|
|
1831
1908
|
metadata.height = metadata.height || dims.height;
|
|
@@ -1833,31 +1910,8 @@ function read(input, options) {
|
|
|
1833
1910
|
}
|
|
1834
1911
|
return { status: "success", metadata, raw };
|
|
1835
1912
|
}
|
|
1836
|
-
var HELPERS = {
|
|
1837
|
-
png: {
|
|
1838
|
-
readMetadata: readPngMetadata,
|
|
1839
|
-
readDimensions: readPngDimensions,
|
|
1840
|
-
createRaw: (chunks) => ({ format: "png", chunks })
|
|
1841
|
-
},
|
|
1842
|
-
jpeg: {
|
|
1843
|
-
readMetadata: readJpegMetadata,
|
|
1844
|
-
readDimensions: readJpegDimensions,
|
|
1845
|
-
createRaw: (segments) => ({
|
|
1846
|
-
format: "jpeg",
|
|
1847
|
-
segments
|
|
1848
|
-
})
|
|
1849
|
-
},
|
|
1850
|
-
webp: {
|
|
1851
|
-
readMetadata: readWebpMetadata,
|
|
1852
|
-
readDimensions: readWebpDimensions,
|
|
1853
|
-
createRaw: (segments) => ({
|
|
1854
|
-
format: "webp",
|
|
1855
|
-
segments
|
|
1856
|
-
})
|
|
1857
|
-
}
|
|
1858
|
-
};
|
|
1859
1913
|
function readRawMetadata(data, format) {
|
|
1860
|
-
const result =
|
|
1914
|
+
const result = format === "png" ? readPngMetadata(data) : format === "jpeg" ? readJpegMetadata(data) : readWebpMetadata(data);
|
|
1861
1915
|
if (!result.ok) {
|
|
1862
1916
|
const message = result.error.type === "invalidSignature" ? `Invalid ${format.toUpperCase()} signature` : result.error.message;
|
|
1863
1917
|
return { status: "invalid", message };
|
|
@@ -1866,81 +1920,14 @@ function readRawMetadata(data, format) {
|
|
|
1866
1920
|
if (format === "png") {
|
|
1867
1921
|
return {
|
|
1868
1922
|
status: "success",
|
|
1869
|
-
raw:
|
|
1923
|
+
raw: { format: "png", chunks: result.value }
|
|
1870
1924
|
};
|
|
1871
1925
|
}
|
|
1872
1926
|
return {
|
|
1873
1927
|
status: "success",
|
|
1874
|
-
raw:
|
|
1928
|
+
raw: { format, segments: result.value }
|
|
1875
1929
|
};
|
|
1876
1930
|
}
|
|
1877
|
-
function readPngDimensions(data) {
|
|
1878
|
-
const PNG_SIGNATURE_LENGTH2 = 8;
|
|
1879
|
-
if (data.length < 24) return null;
|
|
1880
|
-
return {
|
|
1881
|
-
width: readUint32BE(data, PNG_SIGNATURE_LENGTH2 + 8),
|
|
1882
|
-
height: readUint32BE(data, PNG_SIGNATURE_LENGTH2 + 12)
|
|
1883
|
-
};
|
|
1884
|
-
}
|
|
1885
|
-
function readJpegDimensions(data) {
|
|
1886
|
-
let offset = 2;
|
|
1887
|
-
while (offset < data.length - 4) {
|
|
1888
|
-
if (data[offset] !== 255) {
|
|
1889
|
-
offset++;
|
|
1890
|
-
continue;
|
|
1891
|
-
}
|
|
1892
|
-
const marker = data[offset + 1] ?? 0;
|
|
1893
|
-
if (marker === 255) {
|
|
1894
|
-
offset++;
|
|
1895
|
-
continue;
|
|
1896
|
-
}
|
|
1897
|
-
const length = (data[offset + 2] ?? 0) << 8 | (data[offset + 3] ?? 0);
|
|
1898
|
-
if (marker >= 192 && marker <= 207 && marker !== 196 && marker !== 200 && marker !== 204) {
|
|
1899
|
-
const height = (data[offset + 5] ?? 0) << 8 | (data[offset + 6] ?? 0);
|
|
1900
|
-
const width = (data[offset + 7] ?? 0) << 8 | (data[offset + 8] ?? 0);
|
|
1901
|
-
return { width, height };
|
|
1902
|
-
}
|
|
1903
|
-
offset += 2 + length;
|
|
1904
|
-
if (marker === 218) break;
|
|
1905
|
-
}
|
|
1906
|
-
return null;
|
|
1907
|
-
}
|
|
1908
|
-
function readWebpDimensions(data) {
|
|
1909
|
-
let offset = 12;
|
|
1910
|
-
while (offset < data.length) {
|
|
1911
|
-
if (offset + 8 > data.length) break;
|
|
1912
|
-
const chunkType = readChunkType(data, offset);
|
|
1913
|
-
const chunkSize = readUint32LE(data, offset + 4);
|
|
1914
|
-
const paddedSize = chunkSize + chunkSize % 2;
|
|
1915
|
-
if (chunkType === "VP8X") {
|
|
1916
|
-
const wMinus1 = readUint24LE(data, offset + 12);
|
|
1917
|
-
const hMinus1 = readUint24LE(data, offset + 15);
|
|
1918
|
-
return { width: wMinus1 + 1, height: hMinus1 + 1 };
|
|
1919
|
-
}
|
|
1920
|
-
if (chunkType === "VP8 ") {
|
|
1921
|
-
const start = offset + 8;
|
|
1922
|
-
const tag = (data[start] ?? 0) | (data[start + 1] ?? 0) << 8 | (data[start + 2] ?? 0) << 16;
|
|
1923
|
-
const keyFrame = !(tag & 1);
|
|
1924
|
-
if (keyFrame) {
|
|
1925
|
-
if (data[start + 3] === 157 && data[start + 4] === 1 && data[start + 5] === 42) {
|
|
1926
|
-
const wRaw = (data[start + 6] ?? 0) | (data[start + 7] ?? 0) << 8;
|
|
1927
|
-
const hRaw = (data[start + 8] ?? 0) | (data[start + 9] ?? 0) << 8;
|
|
1928
|
-
return { width: wRaw & 16383, height: hRaw & 16383 };
|
|
1929
|
-
}
|
|
1930
|
-
}
|
|
1931
|
-
}
|
|
1932
|
-
if (chunkType === "VP8L") {
|
|
1933
|
-
if (data[offset + 8] === 47) {
|
|
1934
|
-
const bits = readUint32LE(data, offset + 9);
|
|
1935
|
-
const width = (bits & 16383) + 1;
|
|
1936
|
-
const height = (bits >> 14 & 16383) + 1;
|
|
1937
|
-
return { width, height };
|
|
1938
|
-
}
|
|
1939
|
-
}
|
|
1940
|
-
offset += 8 + paddedSize;
|
|
1941
|
-
}
|
|
1942
|
-
return null;
|
|
1943
|
-
}
|
|
1944
1931
|
|
|
1945
1932
|
// src/converters/utils.ts
|
|
1946
1933
|
var createTextChunk = (keyword, text) => text !== void 0 ? [{ type: "tEXt", keyword, text }] : [];
|
|
@@ -1993,6 +1980,12 @@ function createEncodedChunk(keyword, text, strategy) {
|
|
|
1993
1980
|
}
|
|
1994
1981
|
}
|
|
1995
1982
|
}
|
|
1983
|
+
function createEncodedChunks(entries, encodingMap) {
|
|
1984
|
+
return entries.flatMap(([keyword, text]) => {
|
|
1985
|
+
const strategy = encodingMap[keyword] ?? encodingMap.default;
|
|
1986
|
+
return createEncodedChunk(keyword, text, strategy);
|
|
1987
|
+
});
|
|
1988
|
+
}
|
|
1996
1989
|
|
|
1997
1990
|
// src/converters/a1111.ts
|
|
1998
1991
|
function convertA1111PngToSegments(chunks) {
|
|
@@ -2049,7 +2042,7 @@ function convertCivitaiSegmentsToPng(segments) {
|
|
|
2049
2042
|
if (!isJson) {
|
|
2050
2043
|
return convertA1111SegmentsToPng(segments);
|
|
2051
2044
|
}
|
|
2052
|
-
return createEncodedChunk("prompt", userComment.data, "text-
|
|
2045
|
+
return createEncodedChunk("prompt", userComment.data, "text-unicode-escape");
|
|
2053
2046
|
}
|
|
2054
2047
|
|
|
2055
2048
|
// src/converters/base-json.ts
|
|
@@ -2109,7 +2102,7 @@ var tryParseExtendedFormat = (segments) => {
|
|
|
2109
2102
|
];
|
|
2110
2103
|
};
|
|
2111
2104
|
var tryParseSaveImagePlusFormat = (segments) => {
|
|
2112
|
-
const chunks = convertKvSegmentsToPng(segments, "text-
|
|
2105
|
+
const chunks = convertKvSegmentsToPng(segments, "text-unicode-escape");
|
|
2113
2106
|
return chunks.length > 0 ? chunks : null;
|
|
2114
2107
|
};
|
|
2115
2108
|
function convertComfyUISegmentsToPng(segments) {
|
|
@@ -2249,6 +2242,39 @@ function createSegmentsToPng(keyword, encodingStrategy) {
|
|
|
2249
2242
|
};
|
|
2250
2243
|
}
|
|
2251
2244
|
|
|
2245
|
+
// src/converters/stability-matrix.ts
|
|
2246
|
+
var STABILITY_MATRIX_ENCODING = {
|
|
2247
|
+
parameters: "text-utf8-raw",
|
|
2248
|
+
default: "text-unicode-escape"
|
|
2249
|
+
};
|
|
2250
|
+
function convertStabilityMatrixPngToSegments(chunks) {
|
|
2251
|
+
const data = {};
|
|
2252
|
+
for (const chunk of chunks) {
|
|
2253
|
+
const parsed = parseJson(chunk.text);
|
|
2254
|
+
data[chunk.keyword] = parsed.ok ? parsed.value : chunk.text;
|
|
2255
|
+
}
|
|
2256
|
+
return [
|
|
2257
|
+
{
|
|
2258
|
+
source: { type: "exifUserComment" },
|
|
2259
|
+
data: JSON.stringify(data)
|
|
2260
|
+
}
|
|
2261
|
+
];
|
|
2262
|
+
}
|
|
2263
|
+
function convertStabilityMatrixSegmentsToPng(segments) {
|
|
2264
|
+
const userComment = findSegment(segments, "exifUserComment");
|
|
2265
|
+
if (!userComment) return [];
|
|
2266
|
+
const parsed = parseJson(userComment.data);
|
|
2267
|
+
if (!parsed.ok || parsed.type !== "object") return [];
|
|
2268
|
+
const value = parsed.value;
|
|
2269
|
+
const entries = Object.entries(value).map(
|
|
2270
|
+
([key, val]) => {
|
|
2271
|
+
const text = stringify(val);
|
|
2272
|
+
return [key, text !== void 0 ? unescapeUnicode(text) : void 0];
|
|
2273
|
+
}
|
|
2274
|
+
);
|
|
2275
|
+
return createEncodedChunks(entries, STABILITY_MATRIX_ENCODING);
|
|
2276
|
+
}
|
|
2277
|
+
|
|
2252
2278
|
// src/converters/swarmui.ts
|
|
2253
2279
|
function convertSwarmUIPngToSegments(chunks) {
|
|
2254
2280
|
const parametersChunk = chunks.find((c) => c.keyword === "parameters");
|
|
@@ -2283,6 +2309,39 @@ function convertSwarmUISegmentsToPng(segments) {
|
|
|
2283
2309
|
return chunks;
|
|
2284
2310
|
}
|
|
2285
2311
|
|
|
2312
|
+
// src/converters/tensorart.ts
|
|
2313
|
+
var TENSORART_ENCODING = {
|
|
2314
|
+
generation_data: "text-utf8-raw",
|
|
2315
|
+
default: "text-unicode-escape"
|
|
2316
|
+
};
|
|
2317
|
+
function convertTensorArtPngToSegments(chunks) {
|
|
2318
|
+
const data = {};
|
|
2319
|
+
for (const chunk of chunks) {
|
|
2320
|
+
const parsed = parseJson(chunk.text);
|
|
2321
|
+
data[chunk.keyword] = parsed.ok ? parsed.value : chunk.text;
|
|
2322
|
+
}
|
|
2323
|
+
return [
|
|
2324
|
+
{
|
|
2325
|
+
source: { type: "exifUserComment" },
|
|
2326
|
+
data: JSON.stringify(data)
|
|
2327
|
+
}
|
|
2328
|
+
];
|
|
2329
|
+
}
|
|
2330
|
+
function convertTensorArtSegmentsToPng(segments) {
|
|
2331
|
+
const userComment = findSegment(segments, "exifUserComment");
|
|
2332
|
+
if (!userComment) return [];
|
|
2333
|
+
const parsed = parseJson(userComment.data);
|
|
2334
|
+
if (!parsed.ok || parsed.type !== "object") return [];
|
|
2335
|
+
const value = parsed.value;
|
|
2336
|
+
const entries = Object.entries(value).map(
|
|
2337
|
+
([key, val]) => {
|
|
2338
|
+
const text = stringify(val);
|
|
2339
|
+
return [key, text !== void 0 ? unescapeUnicode(text) : void 0];
|
|
2340
|
+
}
|
|
2341
|
+
);
|
|
2342
|
+
return createEncodedChunks(entries, TENSORART_ENCODING);
|
|
2343
|
+
}
|
|
2344
|
+
|
|
2286
2345
|
// src/converters/index.ts
|
|
2287
2346
|
function convertMetadata(parseResult, targetFormat) {
|
|
2288
2347
|
if (parseResult.status === "empty") {
|
|
@@ -2370,6 +2429,14 @@ var convertCivitai = createFormatConverter(
|
|
|
2370
2429
|
convertCivitaiPngToSegments,
|
|
2371
2430
|
convertCivitaiSegmentsToPng
|
|
2372
2431
|
);
|
|
2432
|
+
var convertStabilityMatrix = createFormatConverter(
|
|
2433
|
+
convertStabilityMatrixPngToSegments,
|
|
2434
|
+
convertStabilityMatrixSegmentsToPng
|
|
2435
|
+
);
|
|
2436
|
+
var convertTensorArt = createFormatConverter(
|
|
2437
|
+
convertTensorArtPngToSegments,
|
|
2438
|
+
convertTensorArtSegmentsToPng
|
|
2439
|
+
);
|
|
2373
2440
|
var softwareConverters = {
|
|
2374
2441
|
// NovelAI
|
|
2375
2442
|
novelai: convertNovelai,
|
|
@@ -2380,10 +2447,12 @@ var softwareConverters = {
|
|
|
2380
2447
|
"forge-neo": convertA1111,
|
|
2381
2448
|
// CivitAI Orchestration format
|
|
2382
2449
|
civitai: convertCivitai,
|
|
2383
|
-
// ComfyUI-format
|
|
2450
|
+
// ComfyUI-format
|
|
2384
2451
|
comfyui: convertComfyUI,
|
|
2385
|
-
|
|
2386
|
-
|
|
2452
|
+
// TensorArt (per-chunk encoding: generation_data uses raw UTF-8)
|
|
2453
|
+
tensorart: convertTensorArt,
|
|
2454
|
+
// Stability Matrix (per-chunk encoding: parameters uses raw UTF-8)
|
|
2455
|
+
"stability-matrix": convertStabilityMatrix,
|
|
2387
2456
|
// Easy Diffusion
|
|
2388
2457
|
easydiffusion: convertEasyDiffusion,
|
|
2389
2458
|
// Fooocus variants
|
|
@@ -2931,7 +3000,7 @@ function write(input, metadata) {
|
|
|
2931
3000
|
return { ok: false, error: { type: "unsupportedFormat" } };
|
|
2932
3001
|
}
|
|
2933
3002
|
if (metadata.status === "empty") {
|
|
2934
|
-
const result =
|
|
3003
|
+
const result = HELPERS[targetFormat].writeEmpty(data, []);
|
|
2935
3004
|
if (!result.ok) {
|
|
2936
3005
|
return {
|
|
2937
3006
|
ok: false,
|
|
@@ -2951,7 +3020,7 @@ function write(input, metadata) {
|
|
|
2951
3020
|
if (sourceFormat === targetFormat) {
|
|
2952
3021
|
return writeRaw(data, targetFormat, metadata.raw);
|
|
2953
3022
|
}
|
|
2954
|
-
const result =
|
|
3023
|
+
const result = HELPERS[targetFormat].writeEmpty(data, []);
|
|
2955
3024
|
if (!result.ok) {
|
|
2956
3025
|
return {
|
|
2957
3026
|
ok: false,
|
|
@@ -3015,7 +3084,7 @@ function writeRaw(data, targetFormat, raw) {
|
|
|
3015
3084
|
}
|
|
3016
3085
|
};
|
|
3017
3086
|
}
|
|
3018
|
-
var
|
|
3087
|
+
var HELPERS = {
|
|
3019
3088
|
png: {
|
|
3020
3089
|
writeEmpty: writePngMetadata
|
|
3021
3090
|
},
|