@enslo/sd-metadata 1.0.1 → 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/README.md +223 -49
- package/dist/index.d.ts +80 -37
- package/dist/index.js +135 -69
- package/dist/index.js.map +1 -1
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@ A TypeScript library to read and write metadata embedded in AI-generated images.
|
|
|
13
13
|
- **TypeScript Native**: Written in TypeScript with full type definitions included
|
|
14
14
|
- **Zero Dependencies**: Works in Node.js and browsers without any external dependencies
|
|
15
15
|
- **Format Conversion**: Seamlessly convert metadata between PNG, JPEG, and WebP
|
|
16
|
-
- **
|
|
16
|
+
- **Metadata Preservation**: Preserves original metadata structure when converting formats (e.g., PNG → JPEG → PNG maintains all original data)
|
|
17
17
|
|
|
18
18
|
## Installation
|
|
19
19
|
|
|
@@ -42,15 +42,47 @@ npm install @enslo/sd-metadata
|
|
|
42
42
|
**Legend:**
|
|
43
43
|
|
|
44
44
|
- ✅ **Fully Supported** - Formats natively supported by the tool, verified with sample files
|
|
45
|
-
- 🔄️ **Extended Support** - sd-metadata
|
|
46
|
-
- ⚠️ **Experimental** - Implemented
|
|
45
|
+
- 🔄️ **Extended Support** - Formats not natively supported by the tool, but sd-metadata enables read/write through custom format conversion. Supports round-trip conversion back to native formats.
|
|
46
|
+
- ⚠️ **Experimental** - Implemented by analyzing reference code or documentation, not verified with actual sample files. May not extract all metadata fields correctly.
|
|
47
|
+
|
|
48
|
+
**Extended Support Examples:**
|
|
49
|
+
|
|
50
|
+
- **Stability Matrix** (native: PNG only) → sd-metadata enables JPEG/WebP support
|
|
51
|
+
- **NovelAI** (native: PNG, WebP) → sd-metadata enables JPEG support
|
|
52
|
+
|
|
53
|
+
When you convert from a native format to an extended format and back (e.g., PNG → JPEG → PNG), all metadata is preserved.
|
|
47
54
|
|
|
48
55
|
> [!NOTE]
|
|
49
|
-
> \* Tools with
|
|
56
|
+
> \* Tools with format-specific behaviors. See [Format-Specific Behaviors](#format-specific-behaviors) for details.
|
|
50
57
|
|
|
51
58
|
> [!TIP]
|
|
52
59
|
> **Help us expand tool support!** We're actively collecting sample images from experimental tools (Easy Diffusion, Fooocus) and unsupported tools. If you have sample images generated by these or other AI tools, please consider contributing them! See [CONTRIBUTING.md](CONTRIBUTING.md) for details.
|
|
53
60
|
|
|
61
|
+
## Format-Specific Behaviors
|
|
62
|
+
|
|
63
|
+
Some tools have specific behaviors when converting between formats:
|
|
64
|
+
|
|
65
|
+
- **ComfyUI JPEG/WebP**: Reading supports multiple custom node formats (e.g., `save-image-extended`), but writing always uses the `comfyui-saveimage-plus` format for best information preservation and compatibility with ComfyUI's native drag-and-drop workflow loading.
|
|
66
|
+
- **NovelAI WebP**: Automatically corrects corrupted UTF-8 in the Description field. WebP → PNG → WebP round-trip produces valid, readable metadata but with minor text corrections.
|
|
67
|
+
- **SwarmUI PNG→JPEG/WebP**: Native SwarmUI JPEG/WebP files do not include node information. When converting from PNG, this library preserves the ComfyUI workflow in the `Make` field for complete metadata retention (extended support).
|
|
68
|
+
|
|
69
|
+
## Import
|
|
70
|
+
|
|
71
|
+
**ESM (TypeScript / Modern JavaScript):**
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
import { read } from '@enslo/sd-metadata';
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
**CommonJS (Node.js):**
|
|
78
|
+
|
|
79
|
+
```javascript
|
|
80
|
+
const { read } = require('@enslo/sd-metadata');
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
> [!NOTE]
|
|
84
|
+
> All examples below use ESM syntax. CommonJS users can replace `import` with `require`.
|
|
85
|
+
|
|
54
86
|
## Usage
|
|
55
87
|
|
|
56
88
|
### Node.js Usage
|
|
@@ -64,10 +96,10 @@ const imageData = readFileSync('image.png');
|
|
|
64
96
|
const result = read(imageData);
|
|
65
97
|
|
|
66
98
|
if (result.status === 'success') {
|
|
67
|
-
console.log('Tool:', result.
|
|
68
|
-
console.log('Prompt:', result.prompt);
|
|
69
|
-
console.log('Model:', result.
|
|
70
|
-
console.log('Size:', result.width, 'x', result.height);
|
|
99
|
+
console.log('Tool:', result.metadata.software); // 'novelai', 'comfyui', etc.
|
|
100
|
+
console.log('Prompt:', result.metadata.prompt);
|
|
101
|
+
console.log('Model:', result.metadata.model?.name);
|
|
102
|
+
console.log('Size:', result.metadata.width, 'x', result.metadata.height);
|
|
71
103
|
}
|
|
72
104
|
```
|
|
73
105
|
|
|
@@ -80,19 +112,48 @@ import { read } from 'sd-metadata';
|
|
|
80
112
|
const fileInput = document.querySelector('input[type="file"]');
|
|
81
113
|
fileInput.addEventListener('change', async (e) => {
|
|
82
114
|
const file = e.target.files[0];
|
|
115
|
+
if (!file) return;
|
|
116
|
+
|
|
83
117
|
const arrayBuffer = await file.arrayBuffer();
|
|
84
118
|
const imageData = new Uint8Array(arrayBuffer);
|
|
85
119
|
|
|
86
120
|
const result = read(imageData);
|
|
87
121
|
|
|
88
122
|
if (result.status === 'success') {
|
|
89
|
-
document.getElementById('tool').textContent = result.
|
|
90
|
-
document.getElementById('prompt').textContent = result.prompt;
|
|
91
|
-
document.getElementById('model').textContent = result.
|
|
123
|
+
document.getElementById('tool').textContent = result.metadata.software;
|
|
124
|
+
document.getElementById('prompt').textContent = result.metadata.prompt;
|
|
125
|
+
document.getElementById('model').textContent = result.metadata.model?.name || 'N/A';
|
|
92
126
|
}
|
|
93
127
|
});
|
|
94
128
|
```
|
|
95
129
|
|
|
130
|
+
### CDN Usage (Bookmarklet / Userscript)
|
|
131
|
+
|
|
132
|
+
For bookmarklets or userscripts (Tampermonkey, Violentmonkey, etc.), load from jsDelivr CDN:
|
|
133
|
+
|
|
134
|
+
```javascript
|
|
135
|
+
// Import from CDN
|
|
136
|
+
import { read } from 'https://cdn.jsdelivr.net/npm/@enslo/sd-metadata@latest/dist/index.js';
|
|
137
|
+
|
|
138
|
+
// Fetch image and read metadata
|
|
139
|
+
const response = await fetch(imageUrl);
|
|
140
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
141
|
+
const imageData = new Uint8Array(arrayBuffer);
|
|
142
|
+
|
|
143
|
+
const result = read(imageData);
|
|
144
|
+
if (result.status === 'success') {
|
|
145
|
+
console.log('Tool:', result.metadata.software);
|
|
146
|
+
console.log('Prompt:', result.metadata.prompt);
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
> [!TIP]
|
|
151
|
+
> For production use, pin to a specific version instead of `@latest`:
|
|
152
|
+
>
|
|
153
|
+
> ```text
|
|
154
|
+
> https://cdn.jsdelivr.net/npm/@enslo/sd-metadata@1.0.2/dist/index.js
|
|
155
|
+
> ```
|
|
156
|
+
|
|
96
157
|
### Format Conversion
|
|
97
158
|
|
|
98
159
|
Convert metadata between different image formats:
|
|
@@ -100,20 +161,27 @@ Convert metadata between different image formats:
|
|
|
100
161
|
```typescript
|
|
101
162
|
import { read, write } from 'sd-metadata';
|
|
102
163
|
|
|
103
|
-
// Read from PNG
|
|
164
|
+
// Read metadata from PNG
|
|
104
165
|
const pngData = readFileSync('comfyui-output.png');
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
// Write to JPEG
|
|
108
|
-
const jpegData = readFileSync('target.jpg');
|
|
109
|
-
const result = write(jpegData, metadata);
|
|
166
|
+
const parseResult = read(pngData);
|
|
110
167
|
|
|
111
|
-
if (
|
|
112
|
-
|
|
113
|
-
|
|
168
|
+
if (parseResult.status === 'success') {
|
|
169
|
+
// Convert PNG to JPEG (using your preferred image processing library)
|
|
170
|
+
const jpegImageData = convertToJpeg(pngData); // Pseudo-code: use sharp, canvas, etc.
|
|
171
|
+
|
|
172
|
+
// Embed the metadata into the JPEG
|
|
173
|
+
const result = write(jpegImageData, parseResult);
|
|
174
|
+
|
|
175
|
+
if (result.ok) {
|
|
176
|
+
writeFileSync('output.jpg', result.value);
|
|
177
|
+
console.log('Image converted to JPEG with metadata preserved');
|
|
178
|
+
}
|
|
114
179
|
}
|
|
115
180
|
```
|
|
116
181
|
|
|
182
|
+
> [!TIP]
|
|
183
|
+
> 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
|
+
|
|
117
185
|
### Handling Different Result Types
|
|
118
186
|
|
|
119
187
|
```typescript
|
|
@@ -124,13 +192,13 @@ const result = read(imageData);
|
|
|
124
192
|
switch (result.status) {
|
|
125
193
|
case 'success':
|
|
126
194
|
// Metadata successfully parsed
|
|
127
|
-
console.log(`Generated by ${result.
|
|
128
|
-
console.log(`Prompt: ${result.prompt}`);
|
|
195
|
+
console.log(`Generated by ${result.metadata.software}`);
|
|
196
|
+
console.log(`Prompt: ${result.metadata.prompt}`);
|
|
129
197
|
break;
|
|
130
198
|
|
|
131
199
|
case 'unrecognized':
|
|
132
|
-
//
|
|
133
|
-
console.log('
|
|
200
|
+
// Metadata exists but format is not recognized
|
|
201
|
+
console.log('Unknown metadata format');
|
|
134
202
|
// You can still access raw metadata for debugging:
|
|
135
203
|
console.log('Raw chunks:', result.raw);
|
|
136
204
|
break;
|
|
@@ -140,11 +208,6 @@ switch (result.status) {
|
|
|
140
208
|
console.log('No metadata in this image');
|
|
141
209
|
break;
|
|
142
210
|
|
|
143
|
-
case 'unsupportedFormat':
|
|
144
|
-
// Not a PNG, JPEG, or WebP file
|
|
145
|
-
console.log('Unsupported image format');
|
|
146
|
-
break;
|
|
147
|
-
|
|
148
211
|
case 'invalid':
|
|
149
212
|
// Corrupted or invalid image data
|
|
150
213
|
console.log('Error:', result.message);
|
|
@@ -165,7 +228,7 @@ const source = read(unknownImage);
|
|
|
165
228
|
// Force blind conversion (preserves all metadata chunks/segments)
|
|
166
229
|
const result = write(targetImage, source, { force: true });
|
|
167
230
|
|
|
168
|
-
if (result.
|
|
231
|
+
if (result.ok) {
|
|
169
232
|
// Metadata successfully converted even though format wasn't recognized
|
|
170
233
|
console.log('Forced conversion succeeded');
|
|
171
234
|
}
|
|
@@ -179,8 +242,8 @@ To strip all metadata from an image:
|
|
|
179
242
|
import { write } from 'sd-metadata';
|
|
180
243
|
|
|
181
244
|
const result = write(imageData, { status: 'empty' });
|
|
182
|
-
if (result.
|
|
183
|
-
writeFileSync('clean-image.png', result.
|
|
245
|
+
if (result.ok) {
|
|
246
|
+
writeFileSync('clean-image.png', result.value);
|
|
184
247
|
}
|
|
185
248
|
```
|
|
186
249
|
|
|
@@ -192,11 +255,14 @@ Reads and parses metadata from an image file.
|
|
|
192
255
|
|
|
193
256
|
**Returns:**
|
|
194
257
|
|
|
195
|
-
- `{ status: 'success',
|
|
258
|
+
- `{ status: 'success', metadata, raw }` - Successfully parsed
|
|
259
|
+
- `metadata`: Unified metadata object (see `GenerationMetadata`)
|
|
260
|
+
- `raw`: Original format-specific data (chunks/segments)
|
|
196
261
|
- `{ status: 'unrecognized', raw }` - Image has metadata but not from a known AI tool
|
|
197
|
-
- `
|
|
198
|
-
- `{ status: '
|
|
199
|
-
- `{ status: 'invalid', message }` - Corrupted or
|
|
262
|
+
- `raw`: Original metadata preserved for conversion
|
|
263
|
+
- `{ status: 'empty' }` - No metadata found in the image
|
|
264
|
+
- `{ status: 'invalid', message? }` - Corrupted or unsupported image format
|
|
265
|
+
- `message`: Optional error description
|
|
200
266
|
|
|
201
267
|
### `write(data: Uint8Array, metadata: ParseResult, options?: WriteOptions): WriteResult`
|
|
202
268
|
|
|
@@ -205,29 +271,137 @@ Writes metadata to an image file.
|
|
|
205
271
|
**Parameters:**
|
|
206
272
|
|
|
207
273
|
- `data` - Target image file data (PNG, JPEG, or WebP)
|
|
208
|
-
- `metadata` - ParseResult from `read()`
|
|
274
|
+
- `metadata` - `ParseResult` from `read()`
|
|
209
275
|
- `status: 'success'` or `'empty'` - Can write directly
|
|
210
276
|
- `status: 'unrecognized'` - Requires `force: true` option
|
|
211
277
|
- `options` - Optional settings:
|
|
212
|
-
- `force?: boolean` - Required when writing `status: 'unrecognized'` metadata
|
|
278
|
+
- `force?: boolean` - Required when writing `status: 'unrecognized'` metadata (blind conversion)
|
|
213
279
|
|
|
214
280
|
**Returns:**
|
|
215
281
|
|
|
216
|
-
- `{
|
|
217
|
-
- `{
|
|
218
|
-
- `
|
|
219
|
-
- `{ type: 'writeFailed', message }` - Failed to write metadata to image
|
|
282
|
+
- `{ ok: true, value: Uint8Array }` - Successfully written (returns new image data)
|
|
283
|
+
- `{ ok: false, error: { type: string, message?: string } }` - Failed
|
|
284
|
+
- `type`: `'unsupportedFormat'`, `'conversionFailed'`, or `'writeFailed'`
|
|
220
285
|
|
|
221
|
-
##
|
|
286
|
+
## Type Reference
|
|
222
287
|
|
|
223
|
-
|
|
224
|
-
> **ComfyUI JPEG/WebP**: While reading supports major custom node formats (e.g., `save-image-extended`), writing always uses the `comfyui-saveimage-plus` format. This format provides the best information preservation and is compatible with ComfyUI's native drag-and-drop workflow loading.
|
|
288
|
+
This section provides an overview of the main types. For complete type definitions, see [Type Documentation](./docs/types.md).
|
|
225
289
|
|
|
226
|
-
|
|
227
|
-
> **NovelAI WebP**: Auto-corrects corrupted UTF-8 in the Description field, which means WebP → PNG → WebP round-trip is not content-equivalent (but provides valid, readable metadata).
|
|
290
|
+
### `ParseResult`
|
|
228
291
|
|
|
229
|
-
|
|
230
|
-
|
|
292
|
+
The result of the `read()` function. It uses a discriminated union with a `status` field.
|
|
293
|
+
|
|
294
|
+
```typescript
|
|
295
|
+
type ParseResult =
|
|
296
|
+
| { status: 'success'; metadata: GenerationMetadata; raw: RawMetadata }
|
|
297
|
+
| { status: 'unrecognized'; raw: RawMetadata }
|
|
298
|
+
| { status: 'empty' }
|
|
299
|
+
| { status: 'invalid'; message?: string };
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
### `GenerationMetadata`
|
|
303
|
+
|
|
304
|
+
Unified metadata structure returned by the `read()` function. This is a discriminated union of 3 specific metadata types, distinguished by the `software` field.
|
|
305
|
+
|
|
306
|
+
**Common Fields (Available in All Types):**
|
|
307
|
+
|
|
308
|
+
All metadata types include these base fields:
|
|
309
|
+
|
|
310
|
+
- `prompt: string` - Positive prompt text
|
|
311
|
+
- `negativePrompt: string` - Negative prompt text
|
|
312
|
+
- `width: number` - Image width in pixels
|
|
313
|
+
- `height: number` - Image height in pixels
|
|
314
|
+
- `model?: ModelSettings` - Model information (name, hash, VAE)
|
|
315
|
+
- `sampling?: SamplingSettings` - Sampling parameters (seed, steps, CFG, sampler, scheduler, clipSkip)
|
|
316
|
+
- `hires?: HiresSettings` - Hires.fix settings (if applied)
|
|
317
|
+
- `upscale?: UpscaleSettings` - Upscale settings (if applied)
|
|
318
|
+
|
|
319
|
+
**Metadata Type Variants:**
|
|
320
|
+
|
|
321
|
+
- **`NovelAIMetadata`** (`software: 'novelai'`)
|
|
322
|
+
Includes NovelAI-specific fields for V4 character placement:
|
|
323
|
+
- `characterPrompts?: CharacterPrompt[]` - Per-character prompts with positions
|
|
324
|
+
- `useCoords?: boolean` - Use character coordinates for placement
|
|
325
|
+
- `useOrder?: boolean` - Use character order
|
|
326
|
+
|
|
327
|
+
- **`ComfyUIMetadata`** (`software: 'comfyui' | 'tensorart' | 'stability-matrix' | 'swarmui'`)
|
|
328
|
+
Includes ComfyUI workflow graph:
|
|
329
|
+
- `nodes: ComfyNodeGraph` (required for comfyui/tensorart/stability-matrix)
|
|
330
|
+
- `nodes?: ComfyNodeGraph` (optional for swarmui - only in PNG format)
|
|
331
|
+
|
|
332
|
+
- **`StandardMetadata`** (`software: 'sd-webui' | 'forge' | 'invokeai' | 'civitai' | ...`)
|
|
333
|
+
Baseline metadata without tool-specific extensions. Used by most SD WebUI-based tools.
|
|
334
|
+
|
|
335
|
+
**Type Definition:**
|
|
336
|
+
|
|
337
|
+
```typescript
|
|
338
|
+
type GenerationMetadata =
|
|
339
|
+
| NovelAIMetadata
|
|
340
|
+
| ComfyUIMetadata
|
|
341
|
+
| StandardMetadata;
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
**Usage Example:**
|
|
345
|
+
|
|
346
|
+
```typescript
|
|
347
|
+
const result = read(imageData);
|
|
348
|
+
|
|
349
|
+
if (result.status === 'success') {
|
|
350
|
+
const metadata = result.metadata;
|
|
351
|
+
|
|
352
|
+
// Access common fields
|
|
353
|
+
console.log('Prompt:', metadata.prompt);
|
|
354
|
+
console.log('Model:', metadata.model?.name);
|
|
355
|
+
console.log('Seed:', metadata.sampling?.seed);
|
|
356
|
+
|
|
357
|
+
// Type-specific handling using discriminated union
|
|
358
|
+
if (metadata.software === 'novelai') {
|
|
359
|
+
// TypeScript knows this is NovelAIMetadata
|
|
360
|
+
console.log('Character prompts:', metadata.characterPrompts);
|
|
361
|
+
} else if (
|
|
362
|
+
metadata.software === 'comfyui' ||
|
|
363
|
+
metadata.software === 'tensorart' ||
|
|
364
|
+
metadata.software === 'stability-matrix'
|
|
365
|
+
) {
|
|
366
|
+
// TypeScript knows this is BasicComfyUIMetadata (nodes always present)
|
|
367
|
+
console.log('Node count:', Object.keys(metadata.nodes).length);
|
|
368
|
+
} else if (metadata.software === 'swarmui') {
|
|
369
|
+
// TypeScript knows this is SwarmUIMetadata (nodes optional)
|
|
370
|
+
if (metadata.nodes) {
|
|
371
|
+
console.log('Workflow included');
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
See [Type Documentation](./docs/types.md) for detailed interface definitions of each metadata type.
|
|
378
|
+
|
|
379
|
+
### `RawMetadata`
|
|
380
|
+
|
|
381
|
+
Preserves the original metadata structure for round-trip conversions.
|
|
382
|
+
|
|
383
|
+
```typescript
|
|
384
|
+
type RawMetadata =
|
|
385
|
+
| { format: 'png'; chunks: PngTextChunk[] }
|
|
386
|
+
| { format: 'jpeg'; segments: MetadataSegment[] }
|
|
387
|
+
| { format: 'webp'; segments: MetadataSegment[] };
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
> [!TIP]
|
|
391
|
+
> For TypeScript users: All types are exported and available for import.
|
|
392
|
+
>
|
|
393
|
+
> ```typescript
|
|
394
|
+
> import type {
|
|
395
|
+
> ParseResult,
|
|
396
|
+
> GenerationMetadata,
|
|
397
|
+
> ModelSettings,
|
|
398
|
+
> SamplingSettings
|
|
399
|
+
> } from '@enslo/sd-metadata';
|
|
400
|
+
> ```
|
|
401
|
+
>
|
|
402
|
+
> Use your IDE's IntelliSense for auto-completion and inline documentation.
|
|
403
|
+
|
|
404
|
+
For detailed documentation of all exported types including `BaseMetadata`, `ModelSettings`, `SamplingSettings`, and format-specific types, see the [Type Documentation](./docs/types.md).
|
|
231
405
|
|
|
232
406
|
## Development
|
|
233
407
|
|
package/dist/index.d.ts
CHANGED
|
@@ -84,23 +84,10 @@ interface ITXtChunk {
|
|
|
84
84
|
/** Text content */
|
|
85
85
|
text: string;
|
|
86
86
|
}
|
|
87
|
-
/**
|
|
88
|
-
* Known AI image generation software
|
|
89
|
-
*/
|
|
90
|
-
type GenerationSoftware = 'novelai' | 'comfyui' | 'swarmui' | 'tensorart' | 'stability-matrix' | 'invokeai' | 'forge-neo' | 'forge' | 'sd-webui' | 'sd-next' | 'civitai' | 'hf-space' | 'easydiffusion' | 'fooocus' | 'ruined-fooocus';
|
|
91
|
-
/**
|
|
92
|
-
* Metadata format classification
|
|
93
|
-
*
|
|
94
|
-
* This represents the format/structure of the metadata, not the specific tool.
|
|
95
|
-
* Use this to determine which fields are available and how to interpret them.
|
|
96
|
-
*/
|
|
97
|
-
type MetadataFormat = 'novelai' | 'comfyui' | 'a1111' | 'invokeai' | 'swarmui';
|
|
98
87
|
/**
|
|
99
88
|
* Base metadata fields shared by all tools
|
|
100
89
|
*/
|
|
101
90
|
interface BaseMetadata {
|
|
102
|
-
/** Format classification (for type narrowing) */
|
|
103
|
-
type: MetadataFormat;
|
|
104
91
|
/** Positive prompt */
|
|
105
92
|
prompt: string;
|
|
106
93
|
/** Negative prompt */
|
|
@@ -122,7 +109,6 @@ interface BaseMetadata {
|
|
|
122
109
|
* NovelAI-specific metadata
|
|
123
110
|
*/
|
|
124
111
|
interface NovelAIMetadata extends BaseMetadata {
|
|
125
|
-
type: 'novelai';
|
|
126
112
|
software: 'novelai';
|
|
127
113
|
/** V4 character prompts (when using character placement) */
|
|
128
114
|
characterPrompts?: CharacterPrompt[];
|
|
@@ -148,47 +134,104 @@ interface CharacterPrompt {
|
|
|
148
134
|
*
|
|
149
135
|
* These tools use ComfyUI-compatible workflow format.
|
|
150
136
|
*/
|
|
151
|
-
interface ComfyUIMetadata extends BaseMetadata {
|
|
152
|
-
type: 'comfyui';
|
|
153
|
-
software: 'comfyui' | 'tensorart' | 'stability-matrix';
|
|
154
|
-
/** Full workflow JSON (for reproducibility) */
|
|
155
|
-
workflow?: unknown;
|
|
156
|
-
}
|
|
157
137
|
/**
|
|
158
|
-
*
|
|
138
|
+
* ComfyUI node reference (for node outputs)
|
|
139
|
+
*
|
|
140
|
+
* Format: [nodeId, outputIndex]
|
|
141
|
+
* Example: ["CheckpointLoader_Base", 0]
|
|
159
142
|
*/
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
143
|
+
type ComfyNodeReference = [nodeId: string, outputIndex: number];
|
|
144
|
+
/**
|
|
145
|
+
* ComfyUI node input value
|
|
146
|
+
*/
|
|
147
|
+
type ComfyNodeInputValue = string | number | boolean | ComfyNodeReference | ComfyNodeInputValue[];
|
|
148
|
+
/**
|
|
149
|
+
* ComfyUI node structure
|
|
150
|
+
*/
|
|
151
|
+
interface ComfyNode {
|
|
152
|
+
/** Node class type (e.g., "CheckpointLoaderSimple", "KSampler") */
|
|
153
|
+
class_type: string;
|
|
154
|
+
/** Node inputs */
|
|
155
|
+
inputs: Record<string, ComfyNodeInputValue>;
|
|
156
|
+
/** Node metadata (ComfyUI only) */
|
|
157
|
+
_meta?: {
|
|
158
|
+
/** Node title for display */
|
|
159
|
+
title?: string;
|
|
160
|
+
};
|
|
161
|
+
/** Change detection hash (rare, for caching) */
|
|
162
|
+
is_changed?: string[] | null;
|
|
163
163
|
}
|
|
164
164
|
/**
|
|
165
|
-
*
|
|
165
|
+
* ComfyUI node graph
|
|
166
|
+
*
|
|
167
|
+
* Maps node IDs to their corresponding node data.
|
|
168
|
+
*/
|
|
169
|
+
type ComfyNodeGraph = Record<string, ComfyNode>;
|
|
170
|
+
/**
|
|
171
|
+
* ComfyUI-format metadata (ComfyUI, TensorArt, Stability Matrix)
|
|
172
|
+
*
|
|
173
|
+
* These tools always have nodes in all formats.
|
|
166
174
|
*/
|
|
167
|
-
interface
|
|
168
|
-
|
|
169
|
-
|
|
175
|
+
interface BasicComfyUIMetadata extends BaseMetadata {
|
|
176
|
+
software: 'comfyui' | 'tensorart' | 'stability-matrix';
|
|
177
|
+
/**
|
|
178
|
+
* ComfyUI node graph (required)
|
|
179
|
+
*
|
|
180
|
+
* Always present in all image formats (PNG, JPEG, WebP).
|
|
181
|
+
* Structure: Record<nodeId, ComfyNode> where ComfyNode contains inputs and class_type.
|
|
182
|
+
*/
|
|
183
|
+
nodes: ComfyNodeGraph;
|
|
170
184
|
}
|
|
171
185
|
/**
|
|
172
186
|
* SwarmUI-specific metadata
|
|
187
|
+
*
|
|
188
|
+
* SwarmUI uses ComfyUI workflow format but nodes are only present in PNG.
|
|
173
189
|
*/
|
|
174
190
|
interface SwarmUIMetadata extends BaseMetadata {
|
|
175
|
-
type: 'swarmui';
|
|
176
191
|
software: 'swarmui';
|
|
192
|
+
/**
|
|
193
|
+
* ComfyUI node graph (optional for SwarmUI)
|
|
194
|
+
*
|
|
195
|
+
* Only present in PNG format. JPEG/WebP contain SwarmUI parameters only.
|
|
196
|
+
* Structure: Record<nodeId, ComfyNode> where ComfyNode contains inputs and class_type.
|
|
197
|
+
*/
|
|
198
|
+
nodes?: ComfyNodeGraph;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* ComfyUI-format metadata (union of BasicComfyUI and SwarmUI)
|
|
202
|
+
*
|
|
203
|
+
* This is a union type to handle different node graph requirements:
|
|
204
|
+
* - ComfyUI/TensorArt/Stability Matrix: nodes are always present
|
|
205
|
+
* - SwarmUI: nodes are only present in PNG format
|
|
206
|
+
*/
|
|
207
|
+
type ComfyUIMetadata = BasicComfyUIMetadata | SwarmUIMetadata;
|
|
208
|
+
/**
|
|
209
|
+
* Standard metadata (SD WebUI, Forge, InvokeAI, and others)
|
|
210
|
+
*
|
|
211
|
+
* Baseline generation metadata without tool-specific extensions.
|
|
212
|
+
* Used by most SD tools that don't require special features like
|
|
213
|
+
* NovelAI's character prompts or ComfyUI's node graphs.
|
|
214
|
+
*/
|
|
215
|
+
interface StandardMetadata extends BaseMetadata {
|
|
216
|
+
software: 'sd-webui' | 'sd-next' | 'forge' | 'forge-neo' | 'invokeai' | 'civitai' | 'hf-space' | 'easydiffusion' | 'fooocus' | 'ruined-fooocus';
|
|
177
217
|
}
|
|
178
218
|
/**
|
|
179
219
|
* Unified generation metadata (discriminated union)
|
|
180
220
|
*
|
|
181
|
-
* Use `metadata.
|
|
221
|
+
* Use `metadata.software` to narrow by specific tool:
|
|
182
222
|
* ```typescript
|
|
183
|
-
* if (metadata.
|
|
184
|
-
*
|
|
185
|
-
*
|
|
186
|
-
*
|
|
187
|
-
* //
|
|
223
|
+
* if (metadata.software === 'comfyui' ||
|
|
224
|
+
* metadata.software === 'tensorart' ||
|
|
225
|
+
* metadata.software === 'stability-matrix' ||
|
|
226
|
+
* metadata.software === 'swarmui') {
|
|
227
|
+
* // TypeScript knows metadata is ComfyUIMetadata
|
|
228
|
+
* if (metadata.nodes) {
|
|
229
|
+
* // Access workflow graph
|
|
230
|
+
* }
|
|
188
231
|
* }
|
|
189
232
|
* ```
|
|
190
233
|
*/
|
|
191
|
-
type GenerationMetadata = NovelAIMetadata | ComfyUIMetadata |
|
|
234
|
+
type GenerationMetadata = NovelAIMetadata | ComfyUIMetadata | StandardMetadata;
|
|
192
235
|
/**
|
|
193
236
|
* Model settings
|
|
194
237
|
*/
|
|
@@ -317,4 +360,4 @@ declare function read(data: Uint8Array): ParseResult;
|
|
|
317
360
|
*/
|
|
318
361
|
declare function write(data: Uint8Array, metadata: ParseResult, options?: WriteOptions): WriteResult;
|
|
319
362
|
|
|
320
|
-
export { type GenerationMetadata, type
|
|
363
|
+
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 WriteOptions, type WriteResult, read, write };
|