@enslo/sd-metadata 1.2.0 โ 1.4.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 +603 -0
- package/README.md +98 -35
- package/dist/index.d.ts +60 -43
- package/dist/index.js +96 -83
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
[](https://www.npmjs.com/package/@enslo/sd-metadata)
|
|
5
5
|
[](https://github.com/enslo/sd-metadata/blob/main/LICENSE)
|
|
6
6
|
|
|
7
|
+
๐ฏ๐ต **[ๆฅๆฌ่ช็ใฏใใกใ](./README.ja.md)**
|
|
8
|
+
|
|
7
9
|
A TypeScript library to read and write metadata embedded in AI-generated images.
|
|
8
10
|
|
|
9
11
|
## Features
|
|
@@ -88,7 +90,7 @@ const { read } = require('@enslo/sd-metadata');
|
|
|
88
90
|
### Node.js Usage
|
|
89
91
|
|
|
90
92
|
```typescript
|
|
91
|
-
import { read, write } from 'sd-metadata';
|
|
93
|
+
import { read, write } from '@enslo/sd-metadata';
|
|
92
94
|
import { readFileSync, writeFileSync } from 'fs';
|
|
93
95
|
|
|
94
96
|
// Read metadata from any supported format
|
|
@@ -106,7 +108,7 @@ if (result.status === 'success') {
|
|
|
106
108
|
### Browser Usage
|
|
107
109
|
|
|
108
110
|
```typescript
|
|
109
|
-
import { read } from 'sd-metadata';
|
|
111
|
+
import { read } from '@enslo/sd-metadata';
|
|
110
112
|
|
|
111
113
|
// Handle file input
|
|
112
114
|
const fileInput = document.querySelector('input[type="file"]');
|
|
@@ -151,15 +153,18 @@ if (result.status === 'success') {
|
|
|
151
153
|
> For production use, pin to a specific version instead of `@latest`:
|
|
152
154
|
>
|
|
153
155
|
> ```text
|
|
154
|
-
> https://cdn.jsdelivr.net/npm/@enslo/sd-metadata@1.
|
|
156
|
+
> https://cdn.jsdelivr.net/npm/@enslo/sd-metadata@1.4.0/dist/index.js
|
|
155
157
|
> ```
|
|
156
158
|
|
|
157
|
-
###
|
|
159
|
+
### Advanced Examples
|
|
160
|
+
|
|
161
|
+
<details>
|
|
162
|
+
<summary>Format Conversion</summary>
|
|
158
163
|
|
|
159
164
|
Convert metadata between different image formats:
|
|
160
165
|
|
|
161
166
|
```typescript
|
|
162
|
-
import { read, write } from 'sd-metadata';
|
|
167
|
+
import { read, write } from '@enslo/sd-metadata';
|
|
163
168
|
|
|
164
169
|
// Read metadata from PNG
|
|
165
170
|
const pngData = readFileSync('comfyui-output.png');
|
|
@@ -182,10 +187,13 @@ if (parseResult.status === 'success') {
|
|
|
182
187
|
> [!TIP]
|
|
183
188
|
> This library handles metadata read/write only. For actual image format conversion (decoding/encoding pixels), use image processing libraries like [sharp](https://www.npmjs.com/package/sharp), [jimp](https://www.npmjs.com/package/jimp), or browser Canvas API.
|
|
184
189
|
|
|
185
|
-
|
|
190
|
+
</details>
|
|
191
|
+
|
|
192
|
+
<details>
|
|
193
|
+
<summary>Handling Different Result Types</summary>
|
|
186
194
|
|
|
187
195
|
```typescript
|
|
188
|
-
import { read } from 'sd-metadata';
|
|
196
|
+
import { read } from '@enslo/sd-metadata';
|
|
189
197
|
|
|
190
198
|
const result = read(imageData);
|
|
191
199
|
|
|
@@ -215,31 +223,41 @@ switch (result.status) {
|
|
|
215
223
|
}
|
|
216
224
|
```
|
|
217
225
|
|
|
218
|
-
|
|
226
|
+
</details>
|
|
219
227
|
|
|
220
|
-
|
|
228
|
+
<details>
|
|
229
|
+
<summary>Handling Unrecognized Metadata</summary>
|
|
230
|
+
|
|
231
|
+
When working with metadata from unsupported tools:
|
|
221
232
|
|
|
222
233
|
```typescript
|
|
223
|
-
import { read, write } from 'sd-metadata';
|
|
234
|
+
import { read, write } from '@enslo/sd-metadata';
|
|
224
235
|
|
|
225
236
|
const source = read(unknownImage);
|
|
226
237
|
// source.status === 'unrecognized'
|
|
227
238
|
|
|
228
|
-
//
|
|
229
|
-
|
|
230
|
-
|
|
239
|
+
// Write to target image
|
|
240
|
+
// - Same format (e.g., PNG โ PNG): metadata preserved as-is
|
|
241
|
+
// - Cross-format (e.g., PNG โ JPEG): metadata dropped with warning
|
|
242
|
+
const result = write(targetImage, source);
|
|
231
243
|
if (result.ok) {
|
|
232
|
-
|
|
233
|
-
|
|
244
|
+
saveFile('output.png', result.value);
|
|
245
|
+
if (result.warning) {
|
|
246
|
+
// Metadata was dropped during cross-format conversion
|
|
247
|
+
console.warn('Metadata was dropped:', result.warning.reason);
|
|
248
|
+
}
|
|
234
249
|
}
|
|
235
250
|
```
|
|
236
251
|
|
|
237
|
-
|
|
252
|
+
</details>
|
|
253
|
+
|
|
254
|
+
<details>
|
|
255
|
+
<summary>Removing Metadata</summary>
|
|
238
256
|
|
|
239
257
|
To strip all metadata from an image:
|
|
240
258
|
|
|
241
259
|
```typescript
|
|
242
|
-
import { write } from 'sd-metadata';
|
|
260
|
+
import { write } from '@enslo/sd-metadata';
|
|
243
261
|
|
|
244
262
|
const result = write(imageData, { status: 'empty' });
|
|
245
263
|
if (result.ok) {
|
|
@@ -247,12 +265,15 @@ if (result.ok) {
|
|
|
247
265
|
}
|
|
248
266
|
```
|
|
249
267
|
|
|
250
|
-
|
|
268
|
+
</details>
|
|
269
|
+
|
|
270
|
+
<details>
|
|
271
|
+
<summary>Writing Metadata in WebUI Format</summary>
|
|
251
272
|
|
|
252
273
|
Create and embed custom metadata in SD WebUI (A1111) format:
|
|
253
274
|
|
|
254
275
|
```typescript
|
|
255
|
-
import { writeAsWebUI } from 'sd-metadata';
|
|
276
|
+
import { writeAsWebUI } from '@enslo/sd-metadata';
|
|
256
277
|
|
|
257
278
|
// Create custom metadata from scratch
|
|
258
279
|
const metadata = {
|
|
@@ -284,20 +305,23 @@ if (result.ok) {
|
|
|
284
305
|
> - Converting metadata from proprietary formats to WebUI-compatible format
|
|
285
306
|
> - Building tools that need to output WebUI-readable metadata
|
|
286
307
|
|
|
287
|
-
|
|
308
|
+
</details>
|
|
288
309
|
|
|
289
|
-
|
|
310
|
+
<details>
|
|
311
|
+
<summary>Formatting Metadata for Display</summary>
|
|
312
|
+
|
|
313
|
+
Convert metadata from **any supported tool** to a unified, human-readable WebUI format. This normalizes the differences between tools (NovelAI, ComfyUI, Forge, etc.) into a consistent text format:
|
|
290
314
|
|
|
291
315
|
```typescript
|
|
292
|
-
import { read, formatAsWebUI } from 'sd-metadata';
|
|
316
|
+
import { read, formatAsWebUI } from '@enslo/sd-metadata';
|
|
293
317
|
|
|
294
318
|
const result = read(imageData);
|
|
295
319
|
if (result.status === 'success') {
|
|
296
|
-
//
|
|
320
|
+
// Works with any tool: NovelAI, ComfyUI, Forge, InvokeAI, etc.
|
|
297
321
|
const text = formatAsWebUI(result.metadata);
|
|
298
322
|
console.log(text);
|
|
299
323
|
|
|
300
|
-
//
|
|
324
|
+
// Always outputs in consistent WebUI format:
|
|
301
325
|
// masterpiece, best quality, 1girl
|
|
302
326
|
// Negative prompt: lowres, bad quality
|
|
303
327
|
// Steps: 20, Sampler: Euler a, CFG scale: 7, Seed: 12345, Size: 512x768, Model: model.safetensors
|
|
@@ -305,7 +329,9 @@ if (result.status === 'success') {
|
|
|
305
329
|
```
|
|
306
330
|
|
|
307
331
|
> [!NOTE]
|
|
308
|
-
> `formatAsWebUI`
|
|
332
|
+
> Regardless of which tool generated the image, `formatAsWebUI` extracts the common generation parameters and formats them in a standardized way. This is ideal for displaying metadata to users without worrying about tool-specific formats.
|
|
333
|
+
|
|
334
|
+
</details>
|
|
309
335
|
|
|
310
336
|
## API Reference
|
|
311
337
|
|
|
@@ -324,7 +350,7 @@ Reads and parses metadata from an image file.
|
|
|
324
350
|
- `{ status: 'invalid', message? }` - Corrupted or unsupported image format
|
|
325
351
|
- `message`: Optional error description
|
|
326
352
|
|
|
327
|
-
### `write(data: Uint8Array, metadata: ParseResult
|
|
353
|
+
### `write(data: Uint8Array, metadata: ParseResult): WriteResult`
|
|
328
354
|
|
|
329
355
|
Writes metadata to an image file.
|
|
330
356
|
|
|
@@ -333,15 +359,16 @@ Writes metadata to an image file.
|
|
|
333
359
|
- `data` - Target image file data (PNG, JPEG, or WebP)
|
|
334
360
|
- `metadata` - `ParseResult` from `read()`
|
|
335
361
|
- `status: 'success'` or `'empty'` - Can write directly
|
|
336
|
-
- `status: 'unrecognized'` -
|
|
337
|
-
- `options` - Optional settings:
|
|
338
|
-
- `force?: boolean` - Required when writing `status: 'unrecognized'` metadata (blind conversion)
|
|
362
|
+
- `status: 'unrecognized'` - Same format: writes as-is; Cross-format: drops metadata with warning
|
|
339
363
|
|
|
340
364
|
**Returns:**
|
|
341
365
|
|
|
342
|
-
- `{ ok: true, value: Uint8Array }` - Successfully written
|
|
343
|
-
- `
|
|
344
|
-
|
|
366
|
+
- `{ ok: true, value: Uint8Array, warning?: WriteWarning }` - Successfully written
|
|
367
|
+
- `warning` is set when metadata was intentionally dropped (e.g., unrecognized cross-format)
|
|
368
|
+
- `{ ok: false, error: { type, message? } }` - Failed. `type` is one of:
|
|
369
|
+
- `'unsupportedFormat'`: Target image is not PNG, JPEG, or WebP
|
|
370
|
+
- `'conversionFailed'`: Metadata conversion failed (e.g., incompatible format)
|
|
371
|
+
- `'writeFailed'`: Failed to embed metadata into the image
|
|
345
372
|
|
|
346
373
|
### `writeAsWebUI(data: Uint8Array, metadata: GenerationMetadata): WriteResult`
|
|
347
374
|
|
|
@@ -357,8 +384,9 @@ Writes metadata to an image in SD WebUI (A1111) format.
|
|
|
357
384
|
**Returns:**
|
|
358
385
|
|
|
359
386
|
- `{ ok: true, value: Uint8Array }` - Successfully written (returns new image data)
|
|
360
|
-
- `{ ok: false, error: { type
|
|
361
|
-
- `
|
|
387
|
+
- `{ ok: false, error: { type, message? } }` - Failed. `type` is one of:
|
|
388
|
+
- `'unsupportedFormat'`: Target image is not PNG, JPEG, or WebP
|
|
389
|
+
- `'writeFailed'`: Failed to embed metadata into the image
|
|
362
390
|
|
|
363
391
|
**Use cases:**
|
|
364
392
|
|
|
@@ -393,6 +421,41 @@ Steps: 20, Sampler: Euler a, CFG scale: 7, Seed: 12345, Size: 512x768, ...
|
|
|
393
421
|
- Copying generation parameters as text
|
|
394
422
|
- Logging or debugging generation settings
|
|
395
423
|
|
|
424
|
+
### `formatRaw(raw: RawMetadata): string`
|
|
425
|
+
|
|
426
|
+
Formats raw metadata as plain text.
|
|
427
|
+
|
|
428
|
+
**Parameters:**
|
|
429
|
+
|
|
430
|
+
- `raw` - Raw metadata from `ParseResult` (`result.raw`)
|
|
431
|
+
|
|
432
|
+
**Returns:**
|
|
433
|
+
|
|
434
|
+
- Plain text content from the metadata (multiple entries separated by blank lines)
|
|
435
|
+
|
|
436
|
+
**Use cases:**
|
|
437
|
+
|
|
438
|
+
- Displaying unrecognized metadata to users
|
|
439
|
+
- Quick inspection of raw metadata content
|
|
440
|
+
- Fallback display when parsing fails
|
|
441
|
+
|
|
442
|
+
**Example:**
|
|
443
|
+
|
|
444
|
+
```typescript
|
|
445
|
+
import { read, formatAsWebUI, formatRaw } from '@enslo/sd-metadata';
|
|
446
|
+
|
|
447
|
+
const result = read(imageData);
|
|
448
|
+
|
|
449
|
+
switch (result.status) {
|
|
450
|
+
case 'success':
|
|
451
|
+
console.log(formatAsWebUI(result.metadata));
|
|
452
|
+
break;
|
|
453
|
+
case 'unrecognized':
|
|
454
|
+
console.log(formatRaw(result.raw));
|
|
455
|
+
break;
|
|
456
|
+
}
|
|
457
|
+
```
|
|
458
|
+
|
|
396
459
|
## Type Reference
|
|
397
460
|
|
|
398
461
|
This section provides an overview of the main types. For complete type definitions, see [Type Documentation](./docs/types.md).
|
|
@@ -511,7 +574,7 @@ type RawMetadata =
|
|
|
511
574
|
>
|
|
512
575
|
> Use your IDE's IntelliSense for auto-completion and inline documentation.
|
|
513
576
|
|
|
514
|
-
For detailed documentation of all exported types including `
|
|
577
|
+
For detailed documentation of all exported types including `ModelSettings`, `SamplingSettings`, and format-specific types, see the [Type Documentation](./docs/types.md).
|
|
515
578
|
|
|
516
579
|
## Development
|
|
517
580
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,20 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Result type for explicit error handling
|
|
3
|
-
*/
|
|
4
|
-
type Result<T, E> = {
|
|
5
|
-
ok: true;
|
|
6
|
-
value: T;
|
|
7
|
-
} | {
|
|
8
|
-
ok: false;
|
|
9
|
-
error: E;
|
|
10
|
-
};
|
|
11
|
-
/**
|
|
12
|
-
* Helper functions for Result type
|
|
13
|
-
*/
|
|
14
|
-
declare const Result: {
|
|
15
|
-
ok: <T, E>(value: T) => Result<T, E>;
|
|
16
|
-
error: <T, E>(error: E) => Result<T, E>;
|
|
17
|
-
};
|
|
18
1
|
/**
|
|
19
2
|
* PNG text chunk (tEXt or iTXt)
|
|
20
3
|
*/
|
|
@@ -330,9 +313,16 @@ declare function read(data: Uint8Array): ParseResult;
|
|
|
330
313
|
*/
|
|
331
314
|
|
|
332
315
|
/**
|
|
333
|
-
*
|
|
316
|
+
* Warning types for write operations
|
|
317
|
+
*/
|
|
318
|
+
type WriteWarning = {
|
|
319
|
+
type: 'metadataDropped';
|
|
320
|
+
reason: 'unrecognizedCrossFormat';
|
|
321
|
+
};
|
|
322
|
+
/**
|
|
323
|
+
* Error types for write operations
|
|
334
324
|
*/
|
|
335
|
-
type
|
|
325
|
+
type WriteError = {
|
|
336
326
|
type: 'unsupportedFormat';
|
|
337
327
|
} | {
|
|
338
328
|
type: 'conversionFailed';
|
|
@@ -340,36 +330,32 @@ type WriteResult = Result<Uint8Array, {
|
|
|
340
330
|
} | {
|
|
341
331
|
type: 'writeFailed';
|
|
342
332
|
message: string;
|
|
343
|
-
}
|
|
333
|
+
};
|
|
344
334
|
/**
|
|
345
|
-
*
|
|
335
|
+
* Result of the write operation
|
|
336
|
+
*
|
|
337
|
+
* Success case may include a warning when metadata was intentionally dropped.
|
|
346
338
|
*/
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
* When false (default), returns error for unrecognized formats.
|
|
356
|
-
*
|
|
357
|
-
* @default false
|
|
358
|
-
*/
|
|
359
|
-
force?: boolean;
|
|
360
|
-
}
|
|
339
|
+
type WriteResult = {
|
|
340
|
+
ok: true;
|
|
341
|
+
value: Uint8Array;
|
|
342
|
+
warning?: WriteWarning;
|
|
343
|
+
} | {
|
|
344
|
+
ok: false;
|
|
345
|
+
error: WriteError;
|
|
346
|
+
};
|
|
361
347
|
/**
|
|
362
348
|
* Write metadata to an image
|
|
363
349
|
*
|
|
364
350
|
* Automatically detects the target image format and converts the metadata
|
|
365
|
-
* if necessary.
|
|
351
|
+
* if necessary. For unrecognized metadata with cross-format conversion,
|
|
352
|
+
* metadata is dropped and a warning is returned.
|
|
366
353
|
*
|
|
367
354
|
* @param data - Target image file data
|
|
368
|
-
* @param metadata - ParseResult from `read()`
|
|
369
|
-
* @
|
|
370
|
-
* @returns New image data with embedded metadata
|
|
355
|
+
* @param metadata - ParseResult from `read()`
|
|
356
|
+
* @returns New image data with embedded metadata (or warning if metadata was dropped)
|
|
371
357
|
*/
|
|
372
|
-
declare function write(data: Uint8Array, metadata: ParseResult
|
|
358
|
+
declare function write(data: Uint8Array, metadata: ParseResult): WriteResult;
|
|
373
359
|
|
|
374
360
|
/**
|
|
375
361
|
* WebUI (A1111) format writer for sd-metadata
|
|
@@ -397,7 +383,7 @@ declare function write(data: Uint8Array, metadata: ParseResult, options?: WriteO
|
|
|
397
383
|
*
|
|
398
384
|
* @example
|
|
399
385
|
* ```typescript
|
|
400
|
-
* import { writeAsWebUI } from 'sd-metadata';
|
|
386
|
+
* import { writeAsWebUI } from '@enslo/sd-metadata';
|
|
401
387
|
*
|
|
402
388
|
* // Create custom metadata
|
|
403
389
|
* const metadata = {
|
|
@@ -445,7 +431,7 @@ declare function writeAsWebUI(data: Uint8Array, metadata: GenerationMetadata): W
|
|
|
445
431
|
*
|
|
446
432
|
* @example
|
|
447
433
|
* ```typescript
|
|
448
|
-
* import { read, formatAsWebUI } from 'sd-metadata';
|
|
434
|
+
* import { read, formatAsWebUI } from '@enslo/sd-metadata';
|
|
449
435
|
*
|
|
450
436
|
* const result = read(imageData);
|
|
451
437
|
* if (result.status === 'success') {
|
|
@@ -460,4 +446,35 @@ declare function writeAsWebUI(data: Uint8Array, metadata: GenerationMetadata): W
|
|
|
460
446
|
*/
|
|
461
447
|
declare function formatAsWebUI(metadata: GenerationMetadata): string;
|
|
462
448
|
|
|
463
|
-
|
|
449
|
+
/**
|
|
450
|
+
* Raw metadata serialization utilities
|
|
451
|
+
*
|
|
452
|
+
* Formats RawMetadata as human-readable plain text.
|
|
453
|
+
*/
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* Format raw metadata as plain text
|
|
457
|
+
*
|
|
458
|
+
* Extracts text content from RawMetadata and returns it as a simple string.
|
|
459
|
+
* Multiple entries are separated by double newlines.
|
|
460
|
+
*
|
|
461
|
+
* This is useful for displaying unrecognized metadata to end users
|
|
462
|
+
* without needing to manually iterate over chunks or segments.
|
|
463
|
+
*
|
|
464
|
+
* @param raw - Raw metadata from ParseResult
|
|
465
|
+
* @returns Plain text content from the metadata
|
|
466
|
+
*
|
|
467
|
+
* @example
|
|
468
|
+
* ```typescript
|
|
469
|
+
* import { read, formatRaw } from '@enslo/sd-metadata';
|
|
470
|
+
*
|
|
471
|
+
* const result = read(imageData);
|
|
472
|
+
* if (result.status === 'unrecognized') {
|
|
473
|
+
* console.log(formatRaw(result.raw));
|
|
474
|
+
* // Output: the raw text content without prefixes
|
|
475
|
+
* }
|
|
476
|
+
* ```
|
|
477
|
+
*/
|
|
478
|
+
declare function formatRaw(raw: RawMetadata): string;
|
|
479
|
+
|
|
480
|
+
export { type CharacterPrompt, type GenerationMetadata, type HiresSettings, type ITXtChunk, type MetadataSegment, type MetadataSegmentSource, type ModelSettings, type ParseResult, type PngTextChunk, type RawMetadata, type SamplingSettings, type TExtChunk, type UpscaleSettings, type WriteResult, type WriteWarning, formatAsWebUI, formatRaw, read, write, writeAsWebUI };
|
package/dist/index.js
CHANGED
|
@@ -1768,7 +1768,6 @@ var CHUNK_ENCODING_STRATEGIES = {
|
|
|
1768
1768
|
novelai: "dynamic",
|
|
1769
1769
|
"sd-next": "dynamic",
|
|
1770
1770
|
easydiffusion: "dynamic",
|
|
1771
|
-
blind: "dynamic",
|
|
1772
1771
|
// Unicode escape tools (spec-compliant)
|
|
1773
1772
|
comfyui: "text-unicode-escape",
|
|
1774
1773
|
swarmui: "text-unicode-escape",
|
|
@@ -1833,37 +1832,6 @@ function convertA1111SegmentsToPng(segments) {
|
|
|
1833
1832
|
);
|
|
1834
1833
|
}
|
|
1835
1834
|
|
|
1836
|
-
// src/converters/blind.ts
|
|
1837
|
-
function blindPngToSegments(chunks) {
|
|
1838
|
-
if (chunks.length === 0) return [];
|
|
1839
|
-
const chunkMap = Object.fromEntries(
|
|
1840
|
-
chunks.map((chunk) => [chunk.keyword, chunk.text])
|
|
1841
|
-
);
|
|
1842
|
-
return [
|
|
1843
|
-
{
|
|
1844
|
-
source: { type: "exifUserComment" },
|
|
1845
|
-
data: JSON.stringify(chunkMap)
|
|
1846
|
-
}
|
|
1847
|
-
];
|
|
1848
|
-
}
|
|
1849
|
-
function blindSegmentsToPng(segments) {
|
|
1850
|
-
const userComment = segments.find((s) => s.source.type === "exifUserComment");
|
|
1851
|
-
if (!userComment) return [];
|
|
1852
|
-
const parsed = parseJson(userComment.data);
|
|
1853
|
-
if (parsed.ok) {
|
|
1854
|
-
return Object.entries(parsed.value).flatMap(([keyword, value]) => {
|
|
1855
|
-
const text = typeof value === "string" ? value : JSON.stringify(value);
|
|
1856
|
-
if (!text) return [];
|
|
1857
|
-
return createEncodedChunk(keyword, text, getEncodingStrategy("blind"));
|
|
1858
|
-
});
|
|
1859
|
-
}
|
|
1860
|
-
return createEncodedChunk(
|
|
1861
|
-
"metadata",
|
|
1862
|
-
userComment.data,
|
|
1863
|
-
getEncodingStrategy("blind")
|
|
1864
|
-
);
|
|
1865
|
-
}
|
|
1866
|
-
|
|
1867
1835
|
// src/converters/comfyui.ts
|
|
1868
1836
|
function convertComfyUIPngToSegments(chunks) {
|
|
1869
1837
|
const data = {};
|
|
@@ -2171,7 +2139,7 @@ function convertSwarmUISegmentsToPng(segments) {
|
|
|
2171
2139
|
}
|
|
2172
2140
|
|
|
2173
2141
|
// src/converters/index.ts
|
|
2174
|
-
function convertMetadata(parseResult, targetFormat
|
|
2142
|
+
function convertMetadata(parseResult, targetFormat) {
|
|
2175
2143
|
if (parseResult.status === "empty") {
|
|
2176
2144
|
return Result.error({ type: "missingRawData" });
|
|
2177
2145
|
}
|
|
@@ -2181,17 +2149,17 @@ function convertMetadata(parseResult, targetFormat, force = false) {
|
|
|
2181
2149
|
status: parseResult.status
|
|
2182
2150
|
});
|
|
2183
2151
|
}
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
return Result.ok(raw);
|
|
2187
|
-
}
|
|
2188
|
-
const software = parseResult.status === "success" ? parseResult.metadata.software : null;
|
|
2189
|
-
if (!software) {
|
|
2190
|
-
return force ? convertBlind(raw, targetFormat) : Result.error({
|
|
2152
|
+
if (parseResult.status === "unrecognized") {
|
|
2153
|
+
return Result.error({
|
|
2191
2154
|
type: "unsupportedSoftware",
|
|
2192
2155
|
software: "unknown"
|
|
2193
2156
|
});
|
|
2194
2157
|
}
|
|
2158
|
+
const raw = parseResult.raw;
|
|
2159
|
+
if (raw.format === "png" && targetFormat === "png" || raw.format === "jpeg" && targetFormat === "jpeg" || raw.format === "webp" && targetFormat === "webp") {
|
|
2160
|
+
return Result.ok(raw);
|
|
2161
|
+
}
|
|
2162
|
+
const software = parseResult.metadata.software;
|
|
2195
2163
|
const converter = softwareConverters[software];
|
|
2196
2164
|
if (!converter) {
|
|
2197
2165
|
return Result.error({
|
|
@@ -2253,10 +2221,6 @@ var convertHfSpace = createFormatConverter(
|
|
|
2253
2221
|
createPngToSegments("parameters"),
|
|
2254
2222
|
createSegmentsToPng("parameters")
|
|
2255
2223
|
);
|
|
2256
|
-
var convertBlind = createFormatConverter(
|
|
2257
|
-
blindPngToSegments,
|
|
2258
|
-
blindSegmentsToPng
|
|
2259
|
-
);
|
|
2260
2224
|
var softwareConverters = {
|
|
2261
2225
|
// NovelAI
|
|
2262
2226
|
novelai: convertNovelai,
|
|
@@ -2810,58 +2774,95 @@ function buildExifChunk(segments) {
|
|
|
2810
2774
|
}
|
|
2811
2775
|
|
|
2812
2776
|
// src/api/write.ts
|
|
2813
|
-
function write(data, metadata
|
|
2777
|
+
function write(data, metadata) {
|
|
2814
2778
|
const targetFormat = detectFormat(data);
|
|
2815
2779
|
if (!targetFormat) {
|
|
2816
|
-
return
|
|
2780
|
+
return { ok: false, error: { type: "unsupportedFormat" } };
|
|
2817
2781
|
}
|
|
2818
2782
|
if (metadata.status === "empty") {
|
|
2819
2783
|
const result = HELPERS2[targetFormat].writeEmpty(data, []);
|
|
2820
2784
|
if (!result.ok) {
|
|
2821
|
-
return
|
|
2785
|
+
return {
|
|
2786
|
+
ok: false,
|
|
2787
|
+
error: { type: "writeFailed", message: result.error.type }
|
|
2788
|
+
};
|
|
2822
2789
|
}
|
|
2823
|
-
return
|
|
2790
|
+
return { ok: true, value: result.value };
|
|
2824
2791
|
}
|
|
2825
2792
|
if (metadata.status === "invalid") {
|
|
2826
|
-
return
|
|
2827
|
-
|
|
2828
|
-
message: "Cannot write invalid metadata"
|
|
2829
|
-
}
|
|
2793
|
+
return {
|
|
2794
|
+
ok: false,
|
|
2795
|
+
error: { type: "writeFailed", message: "Cannot write invalid metadata" }
|
|
2796
|
+
};
|
|
2830
2797
|
}
|
|
2831
|
-
|
|
2832
|
-
metadata
|
|
2833
|
-
targetFormat
|
|
2834
|
-
|
|
2835
|
-
|
|
2798
|
+
if (metadata.status === "unrecognized") {
|
|
2799
|
+
const sourceFormat = metadata.raw.format;
|
|
2800
|
+
if (sourceFormat === targetFormat) {
|
|
2801
|
+
return writeRaw(data, targetFormat, metadata.raw);
|
|
2802
|
+
}
|
|
2803
|
+
const result = HELPERS2[targetFormat].writeEmpty(data, []);
|
|
2804
|
+
if (!result.ok) {
|
|
2805
|
+
return {
|
|
2806
|
+
ok: false,
|
|
2807
|
+
error: { type: "writeFailed", message: result.error.type }
|
|
2808
|
+
};
|
|
2809
|
+
}
|
|
2810
|
+
return {
|
|
2811
|
+
ok: true,
|
|
2812
|
+
value: result.value,
|
|
2813
|
+
warning: { type: "metadataDropped", reason: "unrecognizedCrossFormat" }
|
|
2814
|
+
};
|
|
2815
|
+
}
|
|
2816
|
+
const conversionResult = convertMetadata(metadata, targetFormat);
|
|
2836
2817
|
if (!conversionResult.ok) {
|
|
2837
|
-
return
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2818
|
+
return {
|
|
2819
|
+
ok: false,
|
|
2820
|
+
error: {
|
|
2821
|
+
type: "conversionFailed",
|
|
2822
|
+
message: `Failed to convert metadata: ${conversionResult.error.type}`
|
|
2823
|
+
}
|
|
2824
|
+
};
|
|
2841
2825
|
}
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
return
|
|
2854
|
-
}
|
|
2855
|
-
if (targetFormat === "webp" && newRaw.format === "webp") {
|
|
2856
|
-
const result = writeWebpMetadata(data, newRaw.segments);
|
|
2857
|
-
if (!result.ok)
|
|
2858
|
-
return Result.error({ type: "writeFailed", message: result.error.type });
|
|
2859
|
-
return Result.ok(result.value);
|
|
2826
|
+
return writeRaw(data, targetFormat, conversionResult.value);
|
|
2827
|
+
}
|
|
2828
|
+
function writeRaw(data, targetFormat, raw) {
|
|
2829
|
+
if (targetFormat === "png" && raw.format === "png") {
|
|
2830
|
+
const result = writePngMetadata(data, raw.chunks);
|
|
2831
|
+
if (!result.ok) {
|
|
2832
|
+
return {
|
|
2833
|
+
ok: false,
|
|
2834
|
+
error: { type: "writeFailed", message: result.error.type }
|
|
2835
|
+
};
|
|
2836
|
+
}
|
|
2837
|
+
return { ok: true, value: result.value };
|
|
2860
2838
|
}
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2839
|
+
if (targetFormat === "jpeg" && raw.format === "jpeg") {
|
|
2840
|
+
const result = writeJpegMetadata(data, raw.segments);
|
|
2841
|
+
if (!result.ok) {
|
|
2842
|
+
return {
|
|
2843
|
+
ok: false,
|
|
2844
|
+
error: { type: "writeFailed", message: result.error.type }
|
|
2845
|
+
};
|
|
2846
|
+
}
|
|
2847
|
+
return { ok: true, value: result.value };
|
|
2848
|
+
}
|
|
2849
|
+
if (targetFormat === "webp" && raw.format === "webp") {
|
|
2850
|
+
const result = writeWebpMetadata(data, raw.segments);
|
|
2851
|
+
if (!result.ok) {
|
|
2852
|
+
return {
|
|
2853
|
+
ok: false,
|
|
2854
|
+
error: { type: "writeFailed", message: result.error.type }
|
|
2855
|
+
};
|
|
2856
|
+
}
|
|
2857
|
+
return { ok: true, value: result.value };
|
|
2858
|
+
}
|
|
2859
|
+
return {
|
|
2860
|
+
ok: false,
|
|
2861
|
+
error: {
|
|
2862
|
+
type: "writeFailed",
|
|
2863
|
+
message: "Internal error: format mismatch after conversion"
|
|
2864
|
+
}
|
|
2865
|
+
};
|
|
2865
2866
|
}
|
|
2866
2867
|
var HELPERS2 = {
|
|
2867
2868
|
png: {
|
|
@@ -3012,8 +3013,20 @@ function createExifSegments(text) {
|
|
|
3012
3013
|
}
|
|
3013
3014
|
];
|
|
3014
3015
|
}
|
|
3016
|
+
|
|
3017
|
+
// src/serializers/raw.ts
|
|
3018
|
+
function formatRaw(raw) {
|
|
3019
|
+
switch (raw.format) {
|
|
3020
|
+
case "png":
|
|
3021
|
+
return raw.chunks.map((chunk) => chunk.text).join("\n\n");
|
|
3022
|
+
case "jpeg":
|
|
3023
|
+
case "webp":
|
|
3024
|
+
return raw.segments.map((segment) => segment.data).join("\n\n");
|
|
3025
|
+
}
|
|
3026
|
+
}
|
|
3015
3027
|
export {
|
|
3016
3028
|
formatAsWebUI,
|
|
3029
|
+
formatRaw,
|
|
3017
3030
|
read,
|
|
3018
3031
|
write,
|
|
3019
3032
|
writeAsWebUI
|