@enslo/sd-metadata 1.0.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/LICENSE +21 -0
- package/README.md +252 -0
- package/dist/index.d.ts +320 -0
- package/dist/index.js +2805 -0
- package/dist/index.js.map +1 -0
- package/package.json +60 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Rikuto Nakao
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
# sd-metadata
|
|
2
|
+
|
|
3
|
+
A TypeScript library to read and write metadata embedded in AI-generated images.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Multi-format Support**: PNG (tEXt / iTXt), JPEG (COM / Exif), WebP (Exif)
|
|
8
|
+
- **Unified API**: Simple `read()` and `write()` functions work across all formats
|
|
9
|
+
- **TypeScript Native**: Written in TypeScript with full type definitions included
|
|
10
|
+
- **Zero Dependencies**: Works in Node.js and browsers without any external dependencies
|
|
11
|
+
- **Format Conversion**: Seamlessly convert metadata between PNG, JPEG, and WebP
|
|
12
|
+
- **Lossless Round-trip**: Preserves original metadata structure when converting back to native format
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install @enslo/sd-metadata
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Tool Support
|
|
21
|
+
|
|
22
|
+
| Tool | PNG | JPEG | WebP |
|
|
23
|
+
| ------ | :---: | :----: | :----: |
|
|
24
|
+
| [NovelAI](https://novelai.net/) * | ✅ | 🔄️ | ✅ |
|
|
25
|
+
| [ComfyUI](https://github.com/comfyanonymous/ComfyUI) * | ✅ | 🔄️ | 🔄️ |
|
|
26
|
+
| [AUTOMATIC1111](https://github.com/AUTOMATIC1111/stable-diffusion-webui) | ⚠️ | ⚠️ | ⚠️ |
|
|
27
|
+
| [Forge](https://github.com/lllyasviel/stable-diffusion-webui-forge) / [Forge Neo](https://github.com/neggles/sd-webui-forge-neoforge) | ✅ | ✅ | ✅ |
|
|
28
|
+
| [InvokeAI](https://github.com/invoke-ai/InvokeAI) | ✅ | 🔄️ | 🔄️ |
|
|
29
|
+
| [SwarmUI](https://github.com/Stability-AI/StableSwarmUI) * | ✅ | ✅ | ✅ |
|
|
30
|
+
| [Civitai](https://civitai.com/) | ⚠️ | ✅ | ⚠️ |
|
|
31
|
+
| [TensorArt](https://tensor.art/) | ✅ | 🔄️ | 🔄️ |
|
|
32
|
+
| [Stability Matrix](https://github.com/LykosAI/StabilityMatrix) | ✅ | 🔄️ | 🔄️ |
|
|
33
|
+
| [HuggingFace Space](https://huggingface.co/spaces) | ✅ | 🔄️ | 🔄️ |
|
|
34
|
+
| [Ruined Fooocus](https://github.com/runew0lf/RuinedFooocus) | ✅ | 🔄️ | 🔄️ |
|
|
35
|
+
| [Easy Diffusion](https://github.com/easydiffusion/easydiffusion) | ⚠️ | ⚠️ | ⚠️ |
|
|
36
|
+
| [Fooocus](https://github.com/lllyasviel/Fooocus) | ⚠️ | ⚠️ | ⚠️ |
|
|
37
|
+
|
|
38
|
+
**Legend:**
|
|
39
|
+
|
|
40
|
+
- ✅ **Fully Supported** - Formats natively supported by the tool, verified with sample files
|
|
41
|
+
- 🔄️ **Extended Support** - sd-metadata specific parsers, cross-format conversion supported
|
|
42
|
+
- ⚠️ **Experimental** - Implemented from reference code, not verified with samples
|
|
43
|
+
|
|
44
|
+
> [!NOTE]
|
|
45
|
+
> \* Tools with known limitations. See [Known Limitations](#known-limitations) for details.
|
|
46
|
+
>
|
|
47
|
+
> [!TIP]
|
|
48
|
+
> **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.
|
|
49
|
+
|
|
50
|
+
## Usage
|
|
51
|
+
|
|
52
|
+
### Node.js Usage
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
import { read, write } from 'sd-metadata';
|
|
56
|
+
import { readFileSync, writeFileSync } from 'fs';
|
|
57
|
+
|
|
58
|
+
// Read metadata from any supported format
|
|
59
|
+
const imageData = readFileSync('image.png');
|
|
60
|
+
const result = read(imageData);
|
|
61
|
+
|
|
62
|
+
if (result.status === 'success') {
|
|
63
|
+
console.log('Tool:', result.tool); // 'novelai', 'comfyui', etc.
|
|
64
|
+
console.log('Prompt:', result.prompt);
|
|
65
|
+
console.log('Model:', result.parameters?.model);
|
|
66
|
+
console.log('Size:', result.width, 'x', result.height);
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Browser Usage
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
import { read } from 'sd-metadata';
|
|
74
|
+
|
|
75
|
+
// Handle file input
|
|
76
|
+
const fileInput = document.querySelector('input[type="file"]');
|
|
77
|
+
fileInput.addEventListener('change', async (e) => {
|
|
78
|
+
const file = e.target.files[0];
|
|
79
|
+
const arrayBuffer = await file.arrayBuffer();
|
|
80
|
+
const imageData = new Uint8Array(arrayBuffer);
|
|
81
|
+
|
|
82
|
+
const result = read(imageData);
|
|
83
|
+
|
|
84
|
+
if (result.status === 'success') {
|
|
85
|
+
document.getElementById('tool').textContent = result.tool;
|
|
86
|
+
document.getElementById('prompt').textContent = result.prompt;
|
|
87
|
+
document.getElementById('model').textContent = result.parameters?.model || 'N/A';
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Format Conversion
|
|
93
|
+
|
|
94
|
+
Convert metadata between different image formats:
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
import { read, write } from 'sd-metadata';
|
|
98
|
+
|
|
99
|
+
// Read from PNG
|
|
100
|
+
const pngData = readFileSync('comfyui-output.png');
|
|
101
|
+
const metadata = read(pngData);
|
|
102
|
+
|
|
103
|
+
// Write to JPEG
|
|
104
|
+
const jpegData = readFileSync('target.jpg');
|
|
105
|
+
const result = write(jpegData, metadata);
|
|
106
|
+
|
|
107
|
+
if (result.type === 'success') {
|
|
108
|
+
writeFileSync('output.jpg', result.data);
|
|
109
|
+
console.log('Metadata converted from PNG to JPEG');
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Handling Different Result Types
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
import { read } from 'sd-metadata';
|
|
117
|
+
|
|
118
|
+
const result = read(imageData);
|
|
119
|
+
|
|
120
|
+
switch (result.status) {
|
|
121
|
+
case 'success':
|
|
122
|
+
// Metadata successfully parsed
|
|
123
|
+
console.log(`Generated by ${result.tool}`);
|
|
124
|
+
console.log(`Prompt: ${result.prompt}`);
|
|
125
|
+
break;
|
|
126
|
+
|
|
127
|
+
case 'unrecognized':
|
|
128
|
+
// Format detected but not recognized as AI-generated
|
|
129
|
+
console.log('Not an AI-generated image');
|
|
130
|
+
// You can still access raw metadata for debugging:
|
|
131
|
+
console.log('Raw chunks:', result.raw);
|
|
132
|
+
break;
|
|
133
|
+
|
|
134
|
+
case 'empty':
|
|
135
|
+
// No metadata found
|
|
136
|
+
console.log('No metadata in this image');
|
|
137
|
+
break;
|
|
138
|
+
|
|
139
|
+
case 'unsupportedFormat':
|
|
140
|
+
// Not a PNG, JPEG, or WebP file
|
|
141
|
+
console.log('Unsupported image format');
|
|
142
|
+
break;
|
|
143
|
+
|
|
144
|
+
case 'invalid':
|
|
145
|
+
// Corrupted or invalid image data
|
|
146
|
+
console.log('Error:', result.message);
|
|
147
|
+
break;
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Force Conversion for Unrecognized Formats
|
|
152
|
+
|
|
153
|
+
When you have unrecognized metadata but still want to convert it:
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
import { read, write } from 'sd-metadata';
|
|
157
|
+
|
|
158
|
+
const source = read(unknownImage);
|
|
159
|
+
// source.status === 'unrecognized'
|
|
160
|
+
|
|
161
|
+
// Force blind conversion (preserves all metadata chunks/segments)
|
|
162
|
+
const result = write(targetImage, source, { force: true });
|
|
163
|
+
|
|
164
|
+
if (result.type === 'success') {
|
|
165
|
+
// Metadata successfully converted even though format wasn't recognized
|
|
166
|
+
console.log('Forced conversion succeeded');
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Removing Metadata
|
|
171
|
+
|
|
172
|
+
To strip all metadata from an image:
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
import { write } from 'sd-metadata';
|
|
176
|
+
|
|
177
|
+
const result = write(imageData, { status: 'empty' });
|
|
178
|
+
if (result.type === 'success') {
|
|
179
|
+
writeFileSync('clean-image.png', result.data);
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## API Reference
|
|
184
|
+
|
|
185
|
+
### `read(data: Uint8Array): ParseResult`
|
|
186
|
+
|
|
187
|
+
Reads and parses metadata from an image file.
|
|
188
|
+
|
|
189
|
+
**Returns:**
|
|
190
|
+
|
|
191
|
+
- `{ status: 'success', tool, prompt, parameters, width, height, raw }` - Successfully parsed
|
|
192
|
+
- `{ status: 'unrecognized', raw }` - Image has metadata but not from a known AI tool
|
|
193
|
+
- `{ status: 'empty' }` - No metadata found
|
|
194
|
+
- `{ status: 'unsupportedFormat' }` - Not a PNG, JPEG, or WebP file
|
|
195
|
+
- `{ status: 'invalid', message }` - Corrupted or invalid image data
|
|
196
|
+
|
|
197
|
+
### `write(data: Uint8Array, metadata: ParseResult, options?: WriteOptions): WriteResult`
|
|
198
|
+
|
|
199
|
+
Writes metadata to an image file.
|
|
200
|
+
|
|
201
|
+
**Parameters:**
|
|
202
|
+
|
|
203
|
+
- `data` - Target image file data (PNG, JPEG, or WebP)
|
|
204
|
+
- `metadata` - ParseResult from `read()`
|
|
205
|
+
- `status: 'success'` or `'empty'` - Can write directly
|
|
206
|
+
- `status: 'unrecognized'` - Requires `force: true` option
|
|
207
|
+
- `options` - Optional settings:
|
|
208
|
+
- `force?: boolean` - Required when writing `status: 'unrecognized'` metadata
|
|
209
|
+
|
|
210
|
+
**Returns:**
|
|
211
|
+
|
|
212
|
+
- `{ type: 'success', data }` - Successfully written
|
|
213
|
+
- `{ type: 'unsupportedFormat' }` - Target is not PNG, JPEG, or WebP
|
|
214
|
+
- `{ type: 'conversionFailed', message }` - Metadata conversion failed
|
|
215
|
+
- `{ type: 'writeFailed', message }` - Failed to write metadata to image
|
|
216
|
+
|
|
217
|
+
## Known Limitations
|
|
218
|
+
|
|
219
|
+
> [!WARNING]
|
|
220
|
+
> **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.
|
|
221
|
+
>
|
|
222
|
+
> [!WARNING]
|
|
223
|
+
> **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).
|
|
224
|
+
>
|
|
225
|
+
> [!WARNING]
|
|
226
|
+
> **SwarmUI PNG→JPEG/WebP**: PNG files contain both ComfyUI workflow and SwarmUI parameters. When converting to JPEG/WebP, only parameters are preserved to match the native format. Metadata is fully preserved, but the ComfyUI workflow in the `prompt` chunk is lost.
|
|
227
|
+
|
|
228
|
+
## Development
|
|
229
|
+
|
|
230
|
+
```bash
|
|
231
|
+
# Install dependencies
|
|
232
|
+
npm install
|
|
233
|
+
|
|
234
|
+
# Run tests
|
|
235
|
+
npm test
|
|
236
|
+
|
|
237
|
+
# Watch mode
|
|
238
|
+
npm run test:watch
|
|
239
|
+
|
|
240
|
+
# Test coverage
|
|
241
|
+
npm run test:coverage
|
|
242
|
+
|
|
243
|
+
# Build
|
|
244
|
+
npm run build
|
|
245
|
+
|
|
246
|
+
# Lint
|
|
247
|
+
npm run lint
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
## License
|
|
251
|
+
|
|
252
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,320 @@
|
|
|
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
|
+
/**
|
|
19
|
+
* PNG text chunk (tEXt or iTXt)
|
|
20
|
+
*/
|
|
21
|
+
type PngTextChunk = TExtChunk | ITXtChunk;
|
|
22
|
+
/**
|
|
23
|
+
* Source location of a metadata segment.
|
|
24
|
+
* Used for round-tripping: reading and writing back to the correct location.
|
|
25
|
+
*/
|
|
26
|
+
type MetadataSegmentSource = {
|
|
27
|
+
type: 'exifUserComment';
|
|
28
|
+
} | {
|
|
29
|
+
type: 'exifImageDescription';
|
|
30
|
+
prefix?: string;
|
|
31
|
+
} | {
|
|
32
|
+
type: 'exifMake';
|
|
33
|
+
prefix?: string;
|
|
34
|
+
} | {
|
|
35
|
+
type: 'jpegCom';
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* A single metadata segment with source tracking
|
|
39
|
+
*/
|
|
40
|
+
interface MetadataSegment {
|
|
41
|
+
/** Source location of this segment */
|
|
42
|
+
source: MetadataSegmentSource;
|
|
43
|
+
/** Raw data string */
|
|
44
|
+
data: string;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Raw metadata for write-back (preserves original format)
|
|
48
|
+
*/
|
|
49
|
+
type RawMetadata = {
|
|
50
|
+
format: 'png';
|
|
51
|
+
chunks: PngTextChunk[];
|
|
52
|
+
} | {
|
|
53
|
+
format: 'jpeg';
|
|
54
|
+
segments: MetadataSegment[];
|
|
55
|
+
} | {
|
|
56
|
+
format: 'webp';
|
|
57
|
+
segments: MetadataSegment[];
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* tEXt chunk (Latin-1 encoded text)
|
|
61
|
+
*/
|
|
62
|
+
interface TExtChunk {
|
|
63
|
+
type: 'tEXt';
|
|
64
|
+
/** Chunk keyword (e.g., 'parameters', 'Comment') */
|
|
65
|
+
keyword: string;
|
|
66
|
+
/** Text content */
|
|
67
|
+
text: string;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* iTXt chunk (UTF-8 encoded international text)
|
|
71
|
+
*/
|
|
72
|
+
interface ITXtChunk {
|
|
73
|
+
type: 'iTXt';
|
|
74
|
+
/** Chunk keyword */
|
|
75
|
+
keyword: string;
|
|
76
|
+
/** Compression flag (0=uncompressed, 1=compressed) */
|
|
77
|
+
compressionFlag: number;
|
|
78
|
+
/** Compression method (0=zlib/deflate) */
|
|
79
|
+
compressionMethod: number;
|
|
80
|
+
/** Language tag (BCP 47) */
|
|
81
|
+
languageTag: string;
|
|
82
|
+
/** Translated keyword */
|
|
83
|
+
translatedKeyword: string;
|
|
84
|
+
/** Text content */
|
|
85
|
+
text: string;
|
|
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
|
+
/**
|
|
99
|
+
* Base metadata fields shared by all tools
|
|
100
|
+
*/
|
|
101
|
+
interface BaseMetadata {
|
|
102
|
+
/** Format classification (for type narrowing) */
|
|
103
|
+
type: MetadataFormat;
|
|
104
|
+
/** Positive prompt */
|
|
105
|
+
prompt: string;
|
|
106
|
+
/** Negative prompt */
|
|
107
|
+
negativePrompt: string;
|
|
108
|
+
/** Model settings */
|
|
109
|
+
model?: ModelSettings;
|
|
110
|
+
/** Sampling settings */
|
|
111
|
+
sampling?: SamplingSettings;
|
|
112
|
+
/** Hires.fix settings (if applied) */
|
|
113
|
+
hires?: HiresSettings;
|
|
114
|
+
/** Upscale settings (if applied) */
|
|
115
|
+
upscale?: UpscaleSettings;
|
|
116
|
+
/** Image width */
|
|
117
|
+
width: number;
|
|
118
|
+
/** Image height */
|
|
119
|
+
height: number;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* NovelAI-specific metadata
|
|
123
|
+
*/
|
|
124
|
+
interface NovelAIMetadata extends BaseMetadata {
|
|
125
|
+
type: 'novelai';
|
|
126
|
+
software: 'novelai';
|
|
127
|
+
/** V4 character prompts (when using character placement) */
|
|
128
|
+
characterPrompts?: CharacterPrompt[];
|
|
129
|
+
/** Use character coordinates for placement */
|
|
130
|
+
useCoords?: boolean;
|
|
131
|
+
/** Use character order */
|
|
132
|
+
useOrder?: boolean;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Character prompt with position (NovelAI V4)
|
|
136
|
+
*/
|
|
137
|
+
interface CharacterPrompt {
|
|
138
|
+
/** Character-specific prompt */
|
|
139
|
+
prompt: string;
|
|
140
|
+
/** Character position (normalized 0-1) */
|
|
141
|
+
center?: {
|
|
142
|
+
x: number;
|
|
143
|
+
y: number;
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* ComfyUI-format metadata (ComfyUI, TensorArt, Stability Matrix)
|
|
148
|
+
*
|
|
149
|
+
* These tools use ComfyUI-compatible workflow format.
|
|
150
|
+
*/
|
|
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
|
+
/**
|
|
158
|
+
* A1111-format metadata (SD WebUI, Forge, Forge Neo, Civitai)
|
|
159
|
+
*/
|
|
160
|
+
interface A1111Metadata extends BaseMetadata {
|
|
161
|
+
type: 'a1111';
|
|
162
|
+
software: 'sd-webui' | 'sd-next' | 'forge' | 'forge-neo' | 'civitai' | 'hf-space' | 'easydiffusion' | 'fooocus' | 'ruined-fooocus';
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* InvokeAI-specific metadata
|
|
166
|
+
*/
|
|
167
|
+
interface InvokeAIMetadata extends BaseMetadata {
|
|
168
|
+
type: 'invokeai';
|
|
169
|
+
software: 'invokeai';
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* SwarmUI-specific metadata
|
|
173
|
+
*/
|
|
174
|
+
interface SwarmUIMetadata extends BaseMetadata {
|
|
175
|
+
type: 'swarmui';
|
|
176
|
+
software: 'swarmui';
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Unified generation metadata (discriminated union)
|
|
180
|
+
*
|
|
181
|
+
* Use `metadata.type` to narrow by format, or `metadata.software` for specific tool:
|
|
182
|
+
* ```typescript
|
|
183
|
+
* if (metadata.type === 'comfyui') {
|
|
184
|
+
* // TypeScript knows metadata.workflow exists
|
|
185
|
+
* }
|
|
186
|
+
* if (metadata.software === 'tensorart') {
|
|
187
|
+
* // Specific tool within comfyui format
|
|
188
|
+
* }
|
|
189
|
+
* ```
|
|
190
|
+
*/
|
|
191
|
+
type GenerationMetadata = NovelAIMetadata | ComfyUIMetadata | A1111Metadata | InvokeAIMetadata | SwarmUIMetadata;
|
|
192
|
+
/**
|
|
193
|
+
* Model settings
|
|
194
|
+
*/
|
|
195
|
+
interface ModelSettings {
|
|
196
|
+
/** Model name */
|
|
197
|
+
name?: string;
|
|
198
|
+
/** Model hash */
|
|
199
|
+
hash?: string;
|
|
200
|
+
/** VAE name */
|
|
201
|
+
vae?: string;
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Sampling settings
|
|
205
|
+
*/
|
|
206
|
+
interface SamplingSettings {
|
|
207
|
+
/** Sampler name */
|
|
208
|
+
sampler?: string;
|
|
209
|
+
/** Scheduler (sometimes included in sampler, sometimes separate) */
|
|
210
|
+
scheduler?: string;
|
|
211
|
+
/** Sampling steps */
|
|
212
|
+
steps?: number;
|
|
213
|
+
/** CFG scale */
|
|
214
|
+
cfg?: number;
|
|
215
|
+
/** Random seed */
|
|
216
|
+
seed?: number;
|
|
217
|
+
/** CLIP skip layers */
|
|
218
|
+
clipSkip?: number;
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Hires.fix settings
|
|
222
|
+
*/
|
|
223
|
+
interface HiresSettings {
|
|
224
|
+
/** Upscale factor */
|
|
225
|
+
scale?: number;
|
|
226
|
+
/** Upscaler name */
|
|
227
|
+
upscaler?: string;
|
|
228
|
+
/** Hires steps */
|
|
229
|
+
steps?: number;
|
|
230
|
+
/** Hires denoising strength */
|
|
231
|
+
denoise?: number;
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Upscale settings (post-generation)
|
|
235
|
+
*/
|
|
236
|
+
interface UpscaleSettings {
|
|
237
|
+
/** Upscaler name */
|
|
238
|
+
upscaler?: string;
|
|
239
|
+
/** Scale factor */
|
|
240
|
+
scale?: number;
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Parse result with 4-status design
|
|
244
|
+
*
|
|
245
|
+
* - `success`: Parsing succeeded, metadata and raw data available
|
|
246
|
+
* - `empty`: No metadata found in the file
|
|
247
|
+
* - `unrecognized`: Metadata exists but format is not recognized
|
|
248
|
+
* - `invalid`: File is corrupted or not a valid image
|
|
249
|
+
*/
|
|
250
|
+
type ParseResult = {
|
|
251
|
+
status: 'success';
|
|
252
|
+
metadata: GenerationMetadata;
|
|
253
|
+
raw: RawMetadata;
|
|
254
|
+
} | {
|
|
255
|
+
status: 'empty';
|
|
256
|
+
} | {
|
|
257
|
+
status: 'unrecognized';
|
|
258
|
+
raw: RawMetadata;
|
|
259
|
+
} | {
|
|
260
|
+
status: 'invalid';
|
|
261
|
+
message?: string;
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* sd-metadata - Read and write AI-generated image metadata
|
|
266
|
+
*/
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Result of the write operation
|
|
270
|
+
*/
|
|
271
|
+
type WriteResult = Result<Uint8Array, {
|
|
272
|
+
type: 'unsupportedFormat';
|
|
273
|
+
} | {
|
|
274
|
+
type: 'conversionFailed';
|
|
275
|
+
message: string;
|
|
276
|
+
} | {
|
|
277
|
+
type: 'writeFailed';
|
|
278
|
+
message: string;
|
|
279
|
+
}>;
|
|
280
|
+
/**
|
|
281
|
+
* Options for write operation
|
|
282
|
+
*/
|
|
283
|
+
interface WriteOptions {
|
|
284
|
+
/**
|
|
285
|
+
* Force blind conversion for unrecognized formats
|
|
286
|
+
*
|
|
287
|
+
* When true, converts raw chunks/segments between formats even when
|
|
288
|
+
* the generating software is unknown. Enables format conversion for
|
|
289
|
+
* unknown/future tools without parser implementation.
|
|
290
|
+
*
|
|
291
|
+
* When false (default), returns error for unrecognized formats.
|
|
292
|
+
*
|
|
293
|
+
* @default false
|
|
294
|
+
*/
|
|
295
|
+
force?: boolean;
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Read and parse metadata from an image
|
|
299
|
+
*
|
|
300
|
+
* Automatically detects the image format (PNG, JPEG, WebP) and parses
|
|
301
|
+
* any embedded generation metadata.
|
|
302
|
+
*
|
|
303
|
+
* @param data - Image file data
|
|
304
|
+
* @returns Parse result containing metadata and raw data
|
|
305
|
+
*/
|
|
306
|
+
declare function read(data: Uint8Array): ParseResult;
|
|
307
|
+
/**
|
|
308
|
+
* Write metadata to an image
|
|
309
|
+
*
|
|
310
|
+
* Automatically detects the target image format and converts the metadata
|
|
311
|
+
* if necessary.
|
|
312
|
+
*
|
|
313
|
+
* @param data - Target image file data
|
|
314
|
+
* @param metadata - ParseResult from `read()` (must be 'success' or contain raw data)
|
|
315
|
+
* @param options - Write options (e.g., { force: true } for blind conversion)
|
|
316
|
+
* @returns New image data with embedded metadata
|
|
317
|
+
*/
|
|
318
|
+
declare function write(data: Uint8Array, metadata: ParseResult, options?: WriteOptions): WriteResult;
|
|
319
|
+
|
|
320
|
+
export { type GenerationMetadata, type GenerationSoftware, type MetadataFormat, type ParseResult, type RawMetadata, type WriteOptions, type WriteResult, read, write };
|