@enslo/sd-metadata 1.4.1 → 1.5.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 +8 -12
- package/README.md +8 -13
- package/dist/index.d.ts +6 -10
- package/dist/index.js +83 -126
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.ja.md
CHANGED
|
@@ -117,9 +117,7 @@ fileInput.addEventListener('change', async (e) => {
|
|
|
117
117
|
if (!file) return;
|
|
118
118
|
|
|
119
119
|
const arrayBuffer = await file.arrayBuffer();
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
const result = read(imageData);
|
|
120
|
+
const result = read(arrayBuffer);
|
|
123
121
|
|
|
124
122
|
if (result.status === 'success') {
|
|
125
123
|
document.getElementById('tool').textContent = result.metadata.software;
|
|
@@ -137,12 +135,10 @@ fileInput.addEventListener('change', async (e) => {
|
|
|
137
135
|
// CDNからインポート
|
|
138
136
|
import { read } from 'https://cdn.jsdelivr.net/npm/@enslo/sd-metadata@latest/dist/index.js';
|
|
139
137
|
|
|
140
|
-
// 画像を取得してメタデータを読み込み
|
|
141
138
|
const response = await fetch(imageUrl);
|
|
142
139
|
const arrayBuffer = await response.arrayBuffer();
|
|
143
|
-
const
|
|
140
|
+
const result = read(arrayBuffer);
|
|
144
141
|
|
|
145
|
-
const result = read(imageData);
|
|
146
142
|
if (result.status === 'success') {
|
|
147
143
|
console.log('Tool:', result.metadata.software);
|
|
148
144
|
console.log('Prompt:', result.metadata.prompt);
|
|
@@ -153,7 +149,7 @@ if (result.status === 'success') {
|
|
|
153
149
|
> 本番環境では `@latest` の代わりに特定のバージョンを指定してください:
|
|
154
150
|
>
|
|
155
151
|
> ```text
|
|
156
|
-
> https://cdn.jsdelivr.net/npm/@enslo/sd-metadata@1.
|
|
152
|
+
> https://cdn.jsdelivr.net/npm/@enslo/sd-metadata@1.5.0/dist/index.js
|
|
157
153
|
> ```
|
|
158
154
|
|
|
159
155
|
### 応用例
|
|
@@ -335,7 +331,7 @@ if (result.status === 'success') {
|
|
|
335
331
|
|
|
336
332
|
## APIリファレンス
|
|
337
333
|
|
|
338
|
-
### `read(
|
|
334
|
+
### `read(input: Uint8Array | ArrayBuffer): ParseResult`
|
|
339
335
|
|
|
340
336
|
画像ファイルからメタデータを読み込み、パースします。
|
|
341
337
|
|
|
@@ -350,13 +346,13 @@ if (result.status === 'success') {
|
|
|
350
346
|
- `{ status: 'invalid', message? }` - 破損または非対応の画像フォーマット
|
|
351
347
|
- `message`: オプションのエラー説明
|
|
352
348
|
|
|
353
|
-
### `write(
|
|
349
|
+
### `write(input: Uint8Array | ArrayBuffer, metadata: ParseResult): WriteResult`
|
|
354
350
|
|
|
355
351
|
画像ファイルにメタデータを書き込みます。
|
|
356
352
|
|
|
357
353
|
**パラメータ:**
|
|
358
354
|
|
|
359
|
-
- `
|
|
355
|
+
- `input` - ターゲット画像ファイルデータ(PNG、JPEG、またはWebP)
|
|
360
356
|
- `metadata` - `read()` から得られた `ParseResult`
|
|
361
357
|
- `status: 'success'` または `'empty'` - 直接書き込み可能
|
|
362
358
|
- `status: 'unrecognized'` - 同じフォーマット:そのまま書き込み、異なるフォーマット:warning付きでメタデータ削除
|
|
@@ -370,13 +366,13 @@ if (result.status === 'success') {
|
|
|
370
366
|
- `'conversionFailed'`: メタデータ変換に失敗(例:互換性のないフォーマット)
|
|
371
367
|
- `'writeFailed'`: 画像へのメタデータ埋め込みに失敗
|
|
372
368
|
|
|
373
|
-
### `writeAsWebUI(
|
|
369
|
+
### `writeAsWebUI(input: Uint8Array | ArrayBuffer, metadata: GenerationMetadata): WriteResult`
|
|
374
370
|
|
|
375
371
|
SD WebUI (A1111) フォーマットで画像にメタデータを書き込みます。
|
|
376
372
|
|
|
377
373
|
**パラメータ:**
|
|
378
374
|
|
|
379
|
-
- `
|
|
375
|
+
- `input` - ターゲット画像ファイルデータ(PNG、JPEG、またはWebP)
|
|
380
376
|
- `metadata` - 埋め込む生成メタデータ
|
|
381
377
|
- 任意のツールからでも、カスタム作成でも可能
|
|
382
378
|
- 自動的にWebUIフォーマットに変換される
|
package/README.md
CHANGED
|
@@ -93,7 +93,6 @@ const { read } = require('@enslo/sd-metadata');
|
|
|
93
93
|
import { read, write } from '@enslo/sd-metadata';
|
|
94
94
|
import { readFileSync, writeFileSync } from 'fs';
|
|
95
95
|
|
|
96
|
-
// Read metadata from any supported format
|
|
97
96
|
const imageData = readFileSync('image.png');
|
|
98
97
|
const result = read(imageData);
|
|
99
98
|
|
|
@@ -117,9 +116,7 @@ fileInput.addEventListener('change', async (e) => {
|
|
|
117
116
|
if (!file) return;
|
|
118
117
|
|
|
119
118
|
const arrayBuffer = await file.arrayBuffer();
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
const result = read(imageData);
|
|
119
|
+
const result = read(arrayBuffer);
|
|
123
120
|
|
|
124
121
|
if (result.status === 'success') {
|
|
125
122
|
document.getElementById('tool').textContent = result.metadata.software;
|
|
@@ -137,12 +134,10 @@ For bookmarklets or userscripts (Tampermonkey, Violentmonkey, etc.), load from j
|
|
|
137
134
|
// Import from CDN
|
|
138
135
|
import { read } from 'https://cdn.jsdelivr.net/npm/@enslo/sd-metadata@latest/dist/index.js';
|
|
139
136
|
|
|
140
|
-
// Fetch image and read metadata
|
|
141
137
|
const response = await fetch(imageUrl);
|
|
142
138
|
const arrayBuffer = await response.arrayBuffer();
|
|
143
|
-
const
|
|
139
|
+
const result = read(arrayBuffer);
|
|
144
140
|
|
|
145
|
-
const result = read(imageData);
|
|
146
141
|
if (result.status === 'success') {
|
|
147
142
|
console.log('Tool:', result.metadata.software);
|
|
148
143
|
console.log('Prompt:', result.metadata.prompt);
|
|
@@ -153,7 +148,7 @@ if (result.status === 'success') {
|
|
|
153
148
|
> For production use, pin to a specific version instead of `@latest`:
|
|
154
149
|
>
|
|
155
150
|
> ```text
|
|
156
|
-
> https://cdn.jsdelivr.net/npm/@enslo/sd-metadata@1.
|
|
151
|
+
> https://cdn.jsdelivr.net/npm/@enslo/sd-metadata@1.5.0/dist/index.js
|
|
157
152
|
> ```
|
|
158
153
|
|
|
159
154
|
### Advanced Examples
|
|
@@ -335,7 +330,7 @@ if (result.status === 'success') {
|
|
|
335
330
|
|
|
336
331
|
## API Reference
|
|
337
332
|
|
|
338
|
-
### `read(
|
|
333
|
+
### `read(input: Uint8Array | ArrayBuffer): ParseResult`
|
|
339
334
|
|
|
340
335
|
Reads and parses metadata from an image file.
|
|
341
336
|
|
|
@@ -350,13 +345,13 @@ Reads and parses metadata from an image file.
|
|
|
350
345
|
- `{ status: 'invalid', message? }` - Corrupted or unsupported image format
|
|
351
346
|
- `message`: Optional error description
|
|
352
347
|
|
|
353
|
-
### `write(
|
|
348
|
+
### `write(input: Uint8Array | ArrayBuffer, metadata: ParseResult): WriteResult`
|
|
354
349
|
|
|
355
350
|
Writes metadata to an image file.
|
|
356
351
|
|
|
357
352
|
**Parameters:**
|
|
358
353
|
|
|
359
|
-
- `
|
|
354
|
+
- `input` - Target image file data (PNG, JPEG, or WebP)
|
|
360
355
|
- `metadata` - `ParseResult` from `read()`
|
|
361
356
|
- `status: 'success'` or `'empty'` - Can write directly
|
|
362
357
|
- `status: 'unrecognized'` - Same format: writes as-is; Cross-format: drops metadata with warning
|
|
@@ -370,13 +365,13 @@ Writes metadata to an image file.
|
|
|
370
365
|
- `'conversionFailed'`: Metadata conversion failed (e.g., incompatible format)
|
|
371
366
|
- `'writeFailed'`: Failed to embed metadata into the image
|
|
372
367
|
|
|
373
|
-
### `writeAsWebUI(
|
|
368
|
+
### `writeAsWebUI(input: Uint8Array | ArrayBuffer, metadata: GenerationMetadata): WriteResult`
|
|
374
369
|
|
|
375
370
|
Writes metadata to an image in SD WebUI (A1111) format.
|
|
376
371
|
|
|
377
372
|
**Parameters:**
|
|
378
373
|
|
|
379
|
-
- `
|
|
374
|
+
- `input` - Target image file data (PNG, JPEG, or WebP)
|
|
380
375
|
- `metadata` - Generation metadata to embed
|
|
381
376
|
- Can be from any tool or custom-created
|
|
382
377
|
- Automatically converted to WebUI format
|
package/dist/index.d.ts
CHANGED
|
@@ -14,10 +14,6 @@ type MetadataSegmentSource = {
|
|
|
14
14
|
} | {
|
|
15
15
|
type: 'exifMake';
|
|
16
16
|
prefix?: string;
|
|
17
|
-
} | {
|
|
18
|
-
type: 'exifSoftware';
|
|
19
|
-
} | {
|
|
20
|
-
type: 'exifDocumentName';
|
|
21
17
|
} | {
|
|
22
18
|
type: 'jpegCom';
|
|
23
19
|
};
|
|
@@ -304,10 +300,10 @@ type ParseResult = {
|
|
|
304
300
|
* Automatically detects the image format (PNG, JPEG, WebP) and parses
|
|
305
301
|
* any embedded generation metadata.
|
|
306
302
|
*
|
|
307
|
-
* @param
|
|
303
|
+
* @param input - Image file data (Uint8Array or ArrayBuffer)
|
|
308
304
|
* @returns Parse result containing metadata and raw data
|
|
309
305
|
*/
|
|
310
|
-
declare function read(
|
|
306
|
+
declare function read(input: Uint8Array | ArrayBuffer): ParseResult;
|
|
311
307
|
|
|
312
308
|
/**
|
|
313
309
|
* Write API for sd-metadata
|
|
@@ -355,11 +351,11 @@ type WriteResult = {
|
|
|
355
351
|
* if necessary. For unrecognized metadata with cross-format conversion,
|
|
356
352
|
* metadata is dropped and a warning is returned.
|
|
357
353
|
*
|
|
358
|
-
* @param
|
|
354
|
+
* @param input - Target image file data (Uint8Array or ArrayBuffer)
|
|
359
355
|
* @param metadata - ParseResult from `read()`
|
|
360
356
|
* @returns New image data with embedded metadata (or warning if metadata was dropped)
|
|
361
357
|
*/
|
|
362
|
-
declare function write(
|
|
358
|
+
declare function write(input: Uint8Array | ArrayBuffer, metadata: ParseResult): WriteResult;
|
|
363
359
|
|
|
364
360
|
/**
|
|
365
361
|
* WebUI (A1111) format writer for sd-metadata
|
|
@@ -381,7 +377,7 @@ declare function write(data: Uint8Array, metadata: ParseResult): WriteResult;
|
|
|
381
377
|
* - PNG: `parameters` tEXt/iTXt chunk (encoding auto-selected based on content)
|
|
382
378
|
* - JPEG/WebP: Exif UserComment field
|
|
383
379
|
*
|
|
384
|
-
* @param
|
|
380
|
+
* @param input - Target image file data (Uint8Array or ArrayBuffer)
|
|
385
381
|
* @param metadata - Generation metadata to embed
|
|
386
382
|
* @returns New image data with embedded metadata, or error
|
|
387
383
|
*
|
|
@@ -407,7 +403,7 @@ declare function write(data: Uint8Array, metadata: ParseResult): WriteResult;
|
|
|
407
403
|
* }
|
|
408
404
|
* ```
|
|
409
405
|
*/
|
|
410
|
-
declare function writeAsWebUI(
|
|
406
|
+
declare function writeAsWebUI(input: Uint8Array | ArrayBuffer, metadata: GenerationMetadata): WriteResult;
|
|
411
407
|
|
|
412
408
|
/**
|
|
413
409
|
* A1111-format metadata serialization utilities
|
package/dist/index.js
CHANGED
|
@@ -345,6 +345,12 @@ function detectFromCommentJson(comment) {
|
|
|
345
345
|
if ("invokeai_metadata" in parsed) {
|
|
346
346
|
return "invokeai";
|
|
347
347
|
}
|
|
348
|
+
if ("generation_data" in parsed) {
|
|
349
|
+
return "tensorart";
|
|
350
|
+
}
|
|
351
|
+
if ("smproj" in parsed) {
|
|
352
|
+
return "stability-matrix";
|
|
353
|
+
}
|
|
348
354
|
if ("prompt" in parsed && "workflow" in parsed) {
|
|
349
355
|
const workflow = parsed.workflow;
|
|
350
356
|
const prompt = parsed.prompt;
|
|
@@ -574,7 +580,7 @@ function parseFooocus(entries) {
|
|
|
574
580
|
// src/parsers/hf-space.ts
|
|
575
581
|
function parseHfSpace(entries) {
|
|
576
582
|
const entryRecord = buildEntryRecord(entries);
|
|
577
|
-
const parametersText = entryRecord.parameters;
|
|
583
|
+
const parametersText = entryRecord.parameters ?? entryRecord.Comment;
|
|
578
584
|
if (!parametersText) {
|
|
579
585
|
return Result.error({ type: "unsupportedFormat" });
|
|
580
586
|
}
|
|
@@ -727,7 +733,7 @@ function parseNovelAI(entries) {
|
|
|
727
733
|
// src/parsers/ruined-fooocus.ts
|
|
728
734
|
function parseRuinedFooocus(entries) {
|
|
729
735
|
const entryRecord = buildEntryRecord(entries);
|
|
730
|
-
const jsonText = entryRecord.parameters;
|
|
736
|
+
const jsonText = entryRecord.parameters ?? entryRecord.Comment;
|
|
731
737
|
if (!jsonText || !jsonText.startsWith("{")) {
|
|
732
738
|
return Result.error({ type: "unsupportedFormat" });
|
|
733
739
|
}
|
|
@@ -775,7 +781,20 @@ function parseStabilityMatrix(entries) {
|
|
|
775
781
|
...comfyResult.value,
|
|
776
782
|
software: "stability-matrix"
|
|
777
783
|
};
|
|
778
|
-
|
|
784
|
+
let jsonText = entryRecord["parameters-json"];
|
|
785
|
+
if (!jsonText && entryRecord.Comment?.startsWith("{")) {
|
|
786
|
+
const commentParsed = parseJson(
|
|
787
|
+
entryRecord.Comment
|
|
788
|
+
);
|
|
789
|
+
if (commentParsed.ok) {
|
|
790
|
+
const commentData = commentParsed.value;
|
|
791
|
+
if (typeof commentData["parameters-json"] === "string") {
|
|
792
|
+
jsonText = commentData["parameters-json"];
|
|
793
|
+
} else if (typeof commentData["parameters-json"] === "object") {
|
|
794
|
+
jsonText = JSON.stringify(commentData["parameters-json"]);
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
}
|
|
779
798
|
if (jsonText) {
|
|
780
799
|
const parsed = parseJson(jsonText);
|
|
781
800
|
if (parsed.ok) {
|
|
@@ -874,7 +893,26 @@ function parseSwarmUI(entries) {
|
|
|
874
893
|
// src/parsers/tensorart.ts
|
|
875
894
|
function parseTensorArt(entries) {
|
|
876
895
|
const entryRecord = buildEntryRecord(entries);
|
|
877
|
-
|
|
896
|
+
let dataText = entryRecord.generation_data;
|
|
897
|
+
let promptChunk = entryRecord.prompt;
|
|
898
|
+
if (!dataText && entryRecord.Comment?.startsWith("{")) {
|
|
899
|
+
const commentParsed = parseJson(
|
|
900
|
+
entryRecord.Comment
|
|
901
|
+
);
|
|
902
|
+
if (commentParsed.ok) {
|
|
903
|
+
const commentData = commentParsed.value;
|
|
904
|
+
if (typeof commentData.generation_data === "string") {
|
|
905
|
+
dataText = commentData.generation_data;
|
|
906
|
+
} else if (typeof commentData.generation_data === "object") {
|
|
907
|
+
dataText = JSON.stringify(commentData.generation_data);
|
|
908
|
+
}
|
|
909
|
+
if (typeof commentData.prompt === "string") {
|
|
910
|
+
promptChunk = commentData.prompt;
|
|
911
|
+
} else if (typeof commentData.prompt === "object") {
|
|
912
|
+
promptChunk = JSON.stringify(commentData.prompt);
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
}
|
|
878
916
|
if (!dataText) {
|
|
879
917
|
return Result.error({ type: "unsupportedFormat" });
|
|
880
918
|
}
|
|
@@ -889,7 +927,6 @@ function parseTensorArt(entries) {
|
|
|
889
927
|
const data = parsed.value;
|
|
890
928
|
const width = data.width ?? 0;
|
|
891
929
|
const height = data.height ?? 0;
|
|
892
|
-
const promptChunk = entryRecord.prompt;
|
|
893
930
|
if (!promptChunk) {
|
|
894
931
|
return Result.error({ type: "unsupportedFormat" });
|
|
895
932
|
}
|
|
@@ -993,6 +1030,9 @@ function parseMetadata(entries) {
|
|
|
993
1030
|
}
|
|
994
1031
|
|
|
995
1032
|
// src/utils/binary.ts
|
|
1033
|
+
function toUint8Array(input) {
|
|
1034
|
+
return input instanceof ArrayBuffer ? new Uint8Array(input) : input;
|
|
1035
|
+
}
|
|
996
1036
|
function readUint24LE(data, offset) {
|
|
997
1037
|
return (data[offset] ?? 0) | (data[offset + 1] ?? 0) << 8 | (data[offset + 2] ?? 0) << 16;
|
|
998
1038
|
}
|
|
@@ -1092,9 +1132,7 @@ function detectFormat(data) {
|
|
|
1092
1132
|
// src/utils/exif-constants.ts
|
|
1093
1133
|
var USER_COMMENT_TAG = 37510;
|
|
1094
1134
|
var IMAGE_DESCRIPTION_TAG = 270;
|
|
1095
|
-
var DOCUMENT_NAME_TAG = 269;
|
|
1096
1135
|
var MAKE_TAG = 271;
|
|
1097
|
-
var SOFTWARE_TAG = 305;
|
|
1098
1136
|
var EXIF_IFD_POINTER_TAG = 34665;
|
|
1099
1137
|
|
|
1100
1138
|
// src/readers/exif.ts
|
|
@@ -1152,22 +1190,6 @@ function extractTagsFromIfd(data, ifdOffset, isLittleEndian) {
|
|
|
1152
1190
|
data: prefix ? text.slice(prefix.length + 2) : text
|
|
1153
1191
|
});
|
|
1154
1192
|
}
|
|
1155
|
-
} else if (tag === SOFTWARE_TAG) {
|
|
1156
|
-
const text = decodeAsciiString(tagData);
|
|
1157
|
-
if (text) {
|
|
1158
|
-
segments.push({
|
|
1159
|
-
source: { type: "exifSoftware" },
|
|
1160
|
-
data: text
|
|
1161
|
-
});
|
|
1162
|
-
}
|
|
1163
|
-
} else if (tag === DOCUMENT_NAME_TAG) {
|
|
1164
|
-
const text = decodeAsciiString(tagData);
|
|
1165
|
-
if (text) {
|
|
1166
|
-
segments.push({
|
|
1167
|
-
source: { type: "exifDocumentName" },
|
|
1168
|
-
data: text
|
|
1169
|
-
});
|
|
1170
|
-
}
|
|
1171
1193
|
} else if (tag === USER_COMMENT_TAG) {
|
|
1172
1194
|
const text = decodeUserComment(tagData);
|
|
1173
1195
|
if (text) {
|
|
@@ -1618,15 +1640,12 @@ function sourceToKeyword(source) {
|
|
|
1618
1640
|
return source.prefix ?? "Description";
|
|
1619
1641
|
case "exifMake":
|
|
1620
1642
|
return source.prefix ?? "Make";
|
|
1621
|
-
case "exifSoftware":
|
|
1622
|
-
return "Software";
|
|
1623
|
-
case "exifDocumentName":
|
|
1624
|
-
return "Title";
|
|
1625
1643
|
}
|
|
1626
1644
|
}
|
|
1627
1645
|
|
|
1628
1646
|
// src/api/read.ts
|
|
1629
|
-
function read(
|
|
1647
|
+
function read(input) {
|
|
1648
|
+
const data = toUint8Array(input);
|
|
1630
1649
|
const format = detectFormat(data);
|
|
1631
1650
|
if (!format) {
|
|
1632
1651
|
return { status: "invalid", message: "Unknown image format" };
|
|
@@ -1786,6 +1805,12 @@ function escapeUnicode(text) {
|
|
|
1786
1805
|
return `\\u${code}`;
|
|
1787
1806
|
});
|
|
1788
1807
|
}
|
|
1808
|
+
function unescapeUnicode(text) {
|
|
1809
|
+
return text.replace(
|
|
1810
|
+
/\\u([0-9a-fA-F]{4})/g,
|
|
1811
|
+
(_, hex) => String.fromCharCode(Number.parseInt(hex, 16))
|
|
1812
|
+
);
|
|
1813
|
+
}
|
|
1789
1814
|
function hasNonLatin1(text) {
|
|
1790
1815
|
return /[^\x00-\xFF]/.test(text);
|
|
1791
1816
|
}
|
|
@@ -1812,70 +1837,19 @@ function convertA1111PngToSegments(chunks) {
|
|
|
1812
1837
|
if (!parameters) {
|
|
1813
1838
|
return [];
|
|
1814
1839
|
}
|
|
1815
|
-
|
|
1840
|
+
return [
|
|
1816
1841
|
{
|
|
1817
1842
|
source: { type: "exifUserComment" },
|
|
1818
1843
|
data: parameters.text
|
|
1819
1844
|
}
|
|
1820
1845
|
];
|
|
1821
|
-
const software = chunks.find((c) => c.keyword === "Software");
|
|
1822
|
-
if (software) {
|
|
1823
|
-
segments.push({
|
|
1824
|
-
source: { type: "exifSoftware" },
|
|
1825
|
-
data: software.text
|
|
1826
|
-
});
|
|
1827
|
-
}
|
|
1828
|
-
const title = chunks.find((c) => c.keyword === "Title");
|
|
1829
|
-
if (title) {
|
|
1830
|
-
segments.push({
|
|
1831
|
-
source: { type: "exifDocumentName" },
|
|
1832
|
-
data: title.text
|
|
1833
|
-
});
|
|
1834
|
-
}
|
|
1835
|
-
const description = chunks.find((c) => c.keyword === "Description");
|
|
1836
|
-
if (description) {
|
|
1837
|
-
segments.push({
|
|
1838
|
-
source: { type: "exifImageDescription" },
|
|
1839
|
-
data: description.text
|
|
1840
|
-
});
|
|
1841
|
-
}
|
|
1842
|
-
const make = chunks.find((c) => c.keyword === "Make");
|
|
1843
|
-
if (make) {
|
|
1844
|
-
segments.push({
|
|
1845
|
-
source: { type: "exifMake" },
|
|
1846
|
-
data: make.text
|
|
1847
|
-
});
|
|
1848
|
-
}
|
|
1849
|
-
return segments;
|
|
1850
1846
|
}
|
|
1851
1847
|
function convertA1111SegmentsToPng(segments) {
|
|
1852
1848
|
const userComment = segments.find((s) => s.source.type === "exifUserComment");
|
|
1853
1849
|
if (!userComment) {
|
|
1854
1850
|
return [];
|
|
1855
1851
|
}
|
|
1856
|
-
|
|
1857
|
-
"parameters",
|
|
1858
|
-
userComment.data,
|
|
1859
|
-
"dynamic"
|
|
1860
|
-
);
|
|
1861
|
-
const chunks = [...parametersChunks];
|
|
1862
|
-
const software = findSegment(segments, "exifSoftware");
|
|
1863
|
-
if (software) {
|
|
1864
|
-
chunks.push(...createTextChunk("Software", software.data));
|
|
1865
|
-
}
|
|
1866
|
-
const title = findSegment(segments, "exifDocumentName");
|
|
1867
|
-
if (title) {
|
|
1868
|
-
chunks.push(...createTextChunk("Title", title.data));
|
|
1869
|
-
}
|
|
1870
|
-
const description = findSegment(segments, "exifImageDescription");
|
|
1871
|
-
if (description) {
|
|
1872
|
-
chunks.push(...createTextChunk("Description", description.data));
|
|
1873
|
-
}
|
|
1874
|
-
const make = findSegment(segments, "exifMake");
|
|
1875
|
-
if (make) {
|
|
1876
|
-
chunks.push(...createTextChunk("Make", make.data));
|
|
1877
|
-
}
|
|
1878
|
-
return chunks;
|
|
1852
|
+
return createEncodedChunk("parameters", userComment.data, "dynamic");
|
|
1879
1853
|
}
|
|
1880
1854
|
|
|
1881
1855
|
// src/converters/base-json.ts
|
|
@@ -1905,9 +1879,14 @@ function convertKvSegmentsToPng(segments, encodingStrategy) {
|
|
|
1905
1879
|
if (!parsed.ok) {
|
|
1906
1880
|
return [];
|
|
1907
1881
|
}
|
|
1908
|
-
return Object.entries(parsed.value).flatMap(
|
|
1909
|
-
|
|
1910
|
-
|
|
1882
|
+
return Object.entries(parsed.value).flatMap(([keyword, value]) => {
|
|
1883
|
+
const text = stringify(value);
|
|
1884
|
+
return createEncodedChunk(
|
|
1885
|
+
keyword,
|
|
1886
|
+
text !== void 0 ? unescapeUnicode(text) : void 0,
|
|
1887
|
+
encodingStrategy
|
|
1888
|
+
);
|
|
1889
|
+
});
|
|
1911
1890
|
}
|
|
1912
1891
|
|
|
1913
1892
|
// src/converters/comfyui.ts
|
|
@@ -1930,7 +1909,7 @@ var tryParseExtendedFormat = (segments) => {
|
|
|
1930
1909
|
];
|
|
1931
1910
|
};
|
|
1932
1911
|
var tryParseSaveImagePlusFormat = (segments) => {
|
|
1933
|
-
const chunks = convertKvSegmentsToPng(segments, "text-
|
|
1912
|
+
const chunks = convertKvSegmentsToPng(segments, "text-utf8-raw");
|
|
1934
1913
|
return chunks.length > 0 ? chunks : null;
|
|
1935
1914
|
};
|
|
1936
1915
|
function convertComfyUISegmentsToPng(segments) {
|
|
@@ -1972,35 +1951,22 @@ function convertInvokeAISegmentsToPng(segments) {
|
|
|
1972
1951
|
}
|
|
1973
1952
|
|
|
1974
1953
|
// src/converters/novelai.ts
|
|
1975
|
-
var NOVELAI_SOFTWARE = "NovelAI";
|
|
1976
1954
|
var NOVELAI_TITLE = "NovelAI generated image";
|
|
1955
|
+
var NOVELAI_SOFTWARE = "NovelAI";
|
|
1977
1956
|
function convertNovelaiPngToSegments(chunks) {
|
|
1978
|
-
const data = buildUserCommentJson(chunks);
|
|
1979
|
-
const userCommentSegment = {
|
|
1980
|
-
source: { type: "exifUserComment" },
|
|
1981
|
-
data: JSON.stringify(data)
|
|
1982
|
-
};
|
|
1983
1957
|
const description = chunks.find((c) => c.keyword === "Description");
|
|
1984
1958
|
const descriptionSegment = description && {
|
|
1985
1959
|
source: { type: "exifImageDescription" },
|
|
1986
1960
|
data: `\0\0\0\0${description.text}`
|
|
1987
1961
|
};
|
|
1988
|
-
const
|
|
1989
|
-
const
|
|
1990
|
-
source: { type: "
|
|
1991
|
-
data:
|
|
1992
|
-
};
|
|
1993
|
-
const title = chunks.find((c) => c.keyword === "Title");
|
|
1994
|
-
const titleSegment = title && {
|
|
1995
|
-
source: { type: "exifDocumentName" },
|
|
1996
|
-
data: title.text
|
|
1962
|
+
const data = buildUserCommentJson(chunks);
|
|
1963
|
+
const userCommentSegment = {
|
|
1964
|
+
source: { type: "exifUserComment" },
|
|
1965
|
+
data: JSON.stringify(data)
|
|
1997
1966
|
};
|
|
1998
|
-
return [
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
softwareSegment,
|
|
2002
|
-
titleSegment
|
|
2003
|
-
].filter((segment) => Boolean(segment));
|
|
1967
|
+
return [descriptionSegment, userCommentSegment].filter(
|
|
1968
|
+
(segment) => segment !== void 0
|
|
1969
|
+
);
|
|
2004
1970
|
}
|
|
2005
1971
|
function buildUserCommentJson(chunks) {
|
|
2006
1972
|
return NOVELAI_KEY_ORDER.map((key) => {
|
|
@@ -2022,11 +1988,9 @@ var NOVELAI_KEY_ORDER = [
|
|
|
2022
1988
|
function convertNovelaiSegmentsToPng(segments) {
|
|
2023
1989
|
const userCommentSeg = findSegment(segments, "exifUserComment");
|
|
2024
1990
|
const descriptionSeg = findSegment(segments, "exifImageDescription");
|
|
2025
|
-
|
|
2026
|
-
const titleSeg = findSegment(segments, "exifDocumentName");
|
|
2027
|
-
return parseSegments(userCommentSeg, descriptionSeg, softwareSeg, titleSeg);
|
|
1991
|
+
return parseSegments(userCommentSeg, descriptionSeg);
|
|
2028
1992
|
}
|
|
2029
|
-
function parseSegments(userCommentSeg, descriptionSeg
|
|
1993
|
+
function parseSegments(userCommentSeg, descriptionSeg) {
|
|
2030
1994
|
if (!userCommentSeg || !descriptionSeg) {
|
|
2031
1995
|
return [];
|
|
2032
1996
|
}
|
|
@@ -2041,16 +2005,13 @@ function parseSegments(userCommentSeg, descriptionSeg, softwareSeg, titleSeg) {
|
|
|
2041
2005
|
);
|
|
2042
2006
|
return [
|
|
2043
2007
|
// Title (required, use default if missing)
|
|
2044
|
-
createTextChunk(
|
|
2045
|
-
"Title",
|
|
2046
|
-
titleSeg?.data ?? stringify(jsonData.Title) ?? NOVELAI_TITLE
|
|
2047
|
-
),
|
|
2008
|
+
createTextChunk("Title", stringify(jsonData.Title) ?? NOVELAI_TITLE),
|
|
2048
2009
|
// Description (optional, prefer exifImageDescription over JSON)
|
|
2049
2010
|
createEncodedChunk("Description", descriptionText, "dynamic"),
|
|
2050
2011
|
// Software (required, use default if missing)
|
|
2051
2012
|
createTextChunk(
|
|
2052
2013
|
"Software",
|
|
2053
|
-
|
|
2014
|
+
stringify(jsonData.Software) ?? NOVELAI_SOFTWARE
|
|
2054
2015
|
),
|
|
2055
2016
|
// Source (optional)
|
|
2056
2017
|
createTextChunk("Source", stringify(jsonData.Source)),
|
|
@@ -2234,7 +2195,7 @@ var softwareConverters = {
|
|
|
2234
2195
|
// src/writers/exif.ts
|
|
2235
2196
|
function buildExifTiffData(segments) {
|
|
2236
2197
|
const ifd0Segments = segments.filter(
|
|
2237
|
-
(s) => s.source.type === "exifImageDescription" || s.source.type === "exifMake"
|
|
2198
|
+
(s) => s.source.type === "exifImageDescription" || s.source.type === "exifMake"
|
|
2238
2199
|
);
|
|
2239
2200
|
const exifIfdSegments = segments.filter(
|
|
2240
2201
|
(s) => s.source.type === "exifUserComment"
|
|
@@ -2252,12 +2213,6 @@ function buildExifTiffData(segments) {
|
|
|
2252
2213
|
} else if (seg.source.type === "exifMake") {
|
|
2253
2214
|
const data = encodeAsciiTag(seg.data, seg.source.prefix);
|
|
2254
2215
|
ifd0Tags.push({ tag: MAKE_TAG, type: 2, data });
|
|
2255
|
-
} else if (seg.source.type === "exifSoftware") {
|
|
2256
|
-
const data = encodeAsciiTag(seg.data);
|
|
2257
|
-
ifd0Tags.push({ tag: SOFTWARE_TAG, type: 2, data });
|
|
2258
|
-
} else if (seg.source.type === "exifDocumentName") {
|
|
2259
|
-
const data = encodeAsciiTag(seg.data);
|
|
2260
|
-
ifd0Tags.push({ tag: DOCUMENT_NAME_TAG, type: 2, data });
|
|
2261
2216
|
}
|
|
2262
2217
|
}
|
|
2263
2218
|
for (const seg of exifIfdSegments) {
|
|
@@ -2385,7 +2340,7 @@ function writeJpegMetadata(data, segments) {
|
|
|
2385
2340
|
}
|
|
2386
2341
|
const comSegments = segments.filter((s) => s.source.type === "jpegCom");
|
|
2387
2342
|
const exifSegments = segments.filter(
|
|
2388
|
-
(s) => s.source.type === "exifUserComment" || s.source.type === "exifImageDescription" || s.source.type === "exifMake"
|
|
2343
|
+
(s) => s.source.type === "exifUserComment" || s.source.type === "exifImageDescription" || s.source.type === "exifMake"
|
|
2389
2344
|
);
|
|
2390
2345
|
const collectResult = collectNonMetadataSegments(data);
|
|
2391
2346
|
if (!collectResult.ok) {
|
|
@@ -2745,7 +2700,7 @@ function collectNonExifChunks(data) {
|
|
|
2745
2700
|
}
|
|
2746
2701
|
function buildExifChunk(segments) {
|
|
2747
2702
|
const exifSegments = segments.filter(
|
|
2748
|
-
(s) => s.source.type === "exifUserComment" || s.source.type === "exifImageDescription" || s.source.type === "exifMake"
|
|
2703
|
+
(s) => s.source.type === "exifUserComment" || s.source.type === "exifImageDescription" || s.source.type === "exifMake"
|
|
2749
2704
|
);
|
|
2750
2705
|
if (exifSegments.length === 0) {
|
|
2751
2706
|
return null;
|
|
@@ -2764,7 +2719,8 @@ function buildExifChunk(segments) {
|
|
|
2764
2719
|
}
|
|
2765
2720
|
|
|
2766
2721
|
// src/api/write.ts
|
|
2767
|
-
function write(
|
|
2722
|
+
function write(input, metadata) {
|
|
2723
|
+
const data = toUint8Array(input);
|
|
2768
2724
|
const targetFormat = detectFormat(data);
|
|
2769
2725
|
if (!targetFormat) {
|
|
2770
2726
|
return { ok: false, error: { type: "unsupportedFormat" } };
|
|
@@ -2964,7 +2920,8 @@ function formatAsWebUI(metadata) {
|
|
|
2964
2920
|
}
|
|
2965
2921
|
|
|
2966
2922
|
// src/api/write-webui.ts
|
|
2967
|
-
function writeAsWebUI(
|
|
2923
|
+
function writeAsWebUI(input, metadata) {
|
|
2924
|
+
const data = toUint8Array(input);
|
|
2968
2925
|
const format = detectFormat(data);
|
|
2969
2926
|
if (!format) {
|
|
2970
2927
|
return Result.error({ type: "unsupportedFormat" });
|