@enslo/sd-metadata 1.0.2 → 1.1.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/dist/index.js CHANGED
@@ -407,23 +407,45 @@ function convertSwarmUIPngToSegments(chunks) {
407
407
  }
408
408
  const parsed = parseJson(parametersChunk.text);
409
409
  const data = parsed.ok ? parsed.value : parametersChunk.text;
410
- return [
410
+ const segments = [
411
411
  {
412
412
  source: { type: "exifUserComment" },
413
413
  data: typeof data === "string" ? data : JSON.stringify(data)
414
414
  }
415
415
  ];
416
+ const promptChunk = chunks.find((c) => c.keyword === "prompt");
417
+ if (promptChunk) {
418
+ segments.push({
419
+ source: { type: "exifMake" },
420
+ data: promptChunk.text
421
+ });
422
+ }
423
+ return segments;
416
424
  }
417
425
  function convertSwarmUISegmentsToPng(segments) {
418
426
  const userComment = findSegment(segments, "exifUserComment");
419
427
  if (!userComment) {
420
428
  return [];
421
429
  }
422
- return createEncodedChunk(
423
- "parameters",
424
- userComment.data,
425
- getEncodingStrategy("swarmui")
430
+ const chunks = [];
431
+ const make = findSegment(segments, "exifMake");
432
+ if (make) {
433
+ chunks.push(
434
+ ...createEncodedChunk(
435
+ "prompt",
436
+ make.data,
437
+ getEncodingStrategy("swarmui")
438
+ )
439
+ );
440
+ }
441
+ chunks.push(
442
+ ...createEncodedChunk(
443
+ "parameters",
444
+ userComment.data,
445
+ getEncodingStrategy("swarmui")
446
+ )
426
447
  );
448
+ return chunks;
427
449
  }
428
450
 
429
451
  // src/converters/index.ts
@@ -560,7 +582,6 @@ function parseA1111(entries) {
560
582
  const app = settingsMap.get("App");
561
583
  const software = detectSoftwareVariant(version, app);
562
584
  const metadata = {
563
- type: "a1111",
564
585
  software,
565
586
  prompt,
566
587
  negativePrompt,
@@ -709,12 +730,13 @@ function parseComfyUI(entries) {
709
730
  const width = latentWidth || extraMeta?.width || 0;
710
731
  const height = latentHeight || extraMeta?.height || 0;
711
732
  const metadata = {
712
- type: "comfyui",
713
733
  software: "comfyui",
714
734
  prompt: positiveText,
715
735
  negativePrompt: negativeText,
716
736
  width,
717
- height
737
+ height,
738
+ nodes: prompt
739
+ // Store the parsed node graph
718
740
  };
719
741
  const checkpoint = findNode(prompt, ["CheckpointLoader_Base"])?.inputs?.ckpt_name;
720
742
  if (checkpoint) {
@@ -1006,7 +1028,6 @@ function parseFromEntries(entryRecord) {
1006
1028
  const width = Number(entryRecord.width ?? entryRecord.Width) || 0;
1007
1029
  const height = Number(entryRecord.height ?? entryRecord.Height) || 0;
1008
1030
  const metadata = {
1009
- type: "a1111",
1010
1031
  software: "easydiffusion",
1011
1032
  prompt: prompt.trim(),
1012
1033
  negativePrompt: negativePrompt.trim(),
@@ -1037,7 +1058,6 @@ function parseFromJson(json) {
1037
1058
  const width = getValue(json, "width", "Width") ?? 0;
1038
1059
  const height = getValue(json, "height", "Height") ?? 0;
1039
1060
  const metadata = {
1040
- type: "a1111",
1041
1061
  software: "easydiffusion",
1042
1062
  prompt: prompt.trim(),
1043
1063
  negativePrompt: negativePrompt.trim(),
@@ -1077,7 +1097,6 @@ function parseFooocus(entries) {
1077
1097
  return Result.error({ type: "unsupportedFormat" });
1078
1098
  }
1079
1099
  const metadata = {
1080
- type: "a1111",
1081
1100
  software: "fooocus",
1082
1101
  prompt: json.prompt?.trim() ?? "",
1083
1102
  negativePrompt: json.negative_prompt?.trim() ?? "",
@@ -1121,7 +1140,6 @@ function parseHfSpace(entries) {
1121
1140
  };
1122
1141
  const { width, height } = parseResolution(json.resolution);
1123
1142
  const metadata = {
1124
- type: "a1111",
1125
1143
  software: "hf-space",
1126
1144
  prompt: json.prompt ?? "",
1127
1145
  negativePrompt: json.negative_prompt ?? "",
@@ -1172,7 +1190,6 @@ function parseInvokeAI(entries) {
1172
1190
  const width = data.width ?? 0;
1173
1191
  const height = data.height ?? 0;
1174
1192
  const metadata = {
1175
- type: "invokeai",
1176
1193
  software: "invokeai",
1177
1194
  prompt: data.positive_prompt ?? "",
1178
1195
  negativePrompt: data.negative_prompt ?? "",
@@ -1222,7 +1239,6 @@ function parseNovelAI(entries) {
1222
1239
  const prompt = comment.v4_prompt?.caption?.base_caption ?? comment.prompt ?? "";
1223
1240
  const negativePrompt = comment.v4_negative_prompt?.caption?.base_caption ?? comment.uc ?? "";
1224
1241
  const metadata = {
1225
- type: "novelai",
1226
1242
  software: "novelai",
1227
1243
  prompt,
1228
1244
  negativePrompt,
@@ -1272,7 +1288,6 @@ function parseRuinedFooocus(entries) {
1272
1288
  return Result.error({ type: "unsupportedFormat" });
1273
1289
  }
1274
1290
  const metadata = {
1275
- type: "a1111",
1276
1291
  software: "ruined-fooocus",
1277
1292
  prompt: json.Prompt?.trim() ?? "",
1278
1293
  negativePrompt: json.Negative?.trim() ?? "",
@@ -1298,12 +1313,11 @@ function parseRuinedFooocus(entries) {
1298
1313
  function parseStabilityMatrix(entries) {
1299
1314
  const entryRecord = buildEntryRecord(entries);
1300
1315
  const comfyResult = parseComfyUI(entries);
1301
- if (!comfyResult.ok) {
1316
+ if (!comfyResult.ok || comfyResult.value.software !== "comfyui") {
1302
1317
  return Result.error({ type: "unsupportedFormat" });
1303
1318
  }
1304
1319
  const metadata = {
1305
1320
  ...comfyResult.value,
1306
- type: "comfyui",
1307
1321
  software: "stability-matrix"
1308
1322
  };
1309
1323
  const jsonText = entryRecord["parameters-json"];
@@ -1365,13 +1379,19 @@ function parseSwarmUI(entries) {
1365
1379
  const width = params.width ?? 0;
1366
1380
  const height = params.height ?? 0;
1367
1381
  const metadata = {
1368
- type: "swarmui",
1369
1382
  software: "swarmui",
1370
1383
  prompt: params.prompt ?? "",
1371
1384
  negativePrompt: params.negativeprompt ?? "",
1372
1385
  width,
1373
1386
  height
1374
1387
  };
1388
+ const promptSource = entryRecord.prompt || entryRecord.Make;
1389
+ if (promptSource) {
1390
+ const promptParsed = parseJson(promptSource);
1391
+ if (promptParsed.ok) {
1392
+ metadata.nodes = promptParsed.value;
1393
+ }
1394
+ }
1375
1395
  if (params.model) {
1376
1396
  metadata.model = {
1377
1397
  name: params.model
@@ -1414,13 +1434,24 @@ function parseTensorArt(entries) {
1414
1434
  const data = parsed.value;
1415
1435
  const width = data.width ?? 0;
1416
1436
  const height = data.height ?? 0;
1437
+ const promptChunk = entryRecord.prompt;
1438
+ if (!promptChunk) {
1439
+ return Result.error({ type: "unsupportedFormat" });
1440
+ }
1441
+ const promptParsed = parseJson(promptChunk);
1442
+ if (!promptParsed.ok) {
1443
+ return Result.error({
1444
+ type: "parseError",
1445
+ message: "Invalid JSON in prompt chunk"
1446
+ });
1447
+ }
1417
1448
  const metadata = {
1418
- type: "comfyui",
1419
1449
  software: "tensorart",
1420
1450
  prompt: data.prompt ?? "",
1421
1451
  negativePrompt: data.negativePrompt ?? "",
1422
1452
  width,
1423
- height
1453
+ height,
1454
+ nodes: promptParsed.value
1424
1455
  };
1425
1456
  if (data.baseModel?.modelFileName || data.baseModel?.hash) {
1426
1457
  metadata.model = {
@@ -1429,8 +1460,9 @@ function parseTensorArt(entries) {
1429
1460
  };
1430
1461
  }
1431
1462
  if (data.seed !== void 0 || data.steps !== void 0 || data.cfgScale !== void 0 || data.clipSkip !== void 0) {
1463
+ const baseSeed = data.seed ? Number(data.seed) : void 0;
1432
1464
  metadata.sampling = {
1433
- seed: data.seed ? Number(data.seed) : void 0,
1465
+ seed: baseSeed === -1 ? findActualSeed(promptParsed.value) : baseSeed,
1434
1466
  steps: data.steps,
1435
1467
  cfg: data.cfgScale,
1436
1468
  clipSkip: data.clipSkip
@@ -1438,6 +1470,15 @@ function parseTensorArt(entries) {
1438
1470
  }
1439
1471
  return Result.ok(metadata);
1440
1472
  }
1473
+ function findActualSeed(nodes) {
1474
+ const samplerNode = findSamplerNode(nodes);
1475
+ return samplerNode && typeof samplerNode.inputs.seed === "number" ? samplerNode.inputs.seed : -1;
1476
+ }
1477
+ function findSamplerNode(nodes) {
1478
+ return Object.values(nodes).find(
1479
+ (node) => node.class_type === "KSampler" || node.class_type.toLowerCase().includes("sampler")
1480
+ );
1481
+ }
1441
1482
 
1442
1483
  // src/parsers/index.ts
1443
1484
  function parseMetadata(entries) {