@enslo/sd-metadata
v1.0.2
Published
Read and write AI-generated image metadata
Maintainers
Readme
sd-metadata
A TypeScript library to read and write metadata embedded in AI-generated images.
Features
- Multi-format Support: PNG (tEXt / iTXt), JPEG (COM / Exif), WebP (Exif)
- Unified API: Simple
read()andwrite()functions work across all formats - TypeScript Native: Written in TypeScript with full type definitions included
- Zero Dependencies: Works in Node.js and browsers without any external dependencies
- Format Conversion: Seamlessly convert metadata between PNG, JPEG, and WebP
- Lossless Round-trip: Preserves original metadata structure when converting back to native format
Installation
npm install @enslo/sd-metadataTool Support
| Tool | PNG | JPEG | WebP | | ------ | :---: | :----: | :----: | | NovelAI * | ✅ | 🔄️ | ✅ | | ComfyUI * | ✅ | 🔄️ | 🔄️ | | AUTOMATIC1111 | ⚠️ | ⚠️ | ⚠️ | | Forge / Forge Neo | ✅ | ✅ | ✅ | | InvokeAI | ✅ | 🔄️ | 🔄️ | | SwarmUI * | ✅ | ✅ | ✅ | | Civitai | ⚠️ | ✅ | ⚠️ | | TensorArt | ✅ | 🔄️ | 🔄️ | | Stability Matrix | ✅ | 🔄️ | 🔄️ | | HuggingFace Space | ✅ | 🔄️ | 🔄️ | | Ruined Fooocus | ✅ | 🔄️ | 🔄️ | | Easy Diffusion | ⚠️ | ⚠️ | ⚠️ | | Fooocus | ⚠️ | ⚠️ | ⚠️ |
Legend:
- ✅ Fully Supported - Formats natively supported by the tool, verified with sample files
- 🔄️ Extended Support - sd-metadata specific parsers, cross-format conversion supported
- ⚠️ Experimental - Implemented from reference code, not verified with samples
[!NOTE] * Tools with known limitations. See Known Limitations for details.
[!TIP] 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 for details.
Usage
Node.js Usage
import { read, write } from 'sd-metadata';
import { readFileSync, writeFileSync } from 'fs';
// Read metadata from any supported format
const imageData = readFileSync('image.png');
const result = read(imageData);
if (result.status === 'success') {
console.log('Tool:', result.tool); // 'novelai', 'comfyui', etc.
console.log('Prompt:', result.prompt);
console.log('Model:', result.parameters?.model);
console.log('Size:', result.width, 'x', result.height);
}Browser Usage
import { read } from 'sd-metadata';
// Handle file input
const fileInput = document.querySelector('input[type="file"]');
fileInput.addEventListener('change', async (e) => {
const file = e.target.files[0];
const arrayBuffer = await file.arrayBuffer();
const imageData = new Uint8Array(arrayBuffer);
const result = read(imageData);
if (result.status === 'success') {
document.getElementById('tool').textContent = result.tool;
document.getElementById('prompt').textContent = result.prompt;
document.getElementById('model').textContent = result.parameters?.model || 'N/A';
}
});Format Conversion
Convert metadata between different image formats:
import { read, write } from 'sd-metadata';
// Read from PNG
const pngData = readFileSync('comfyui-output.png');
const metadata = read(pngData);
// Write to JPEG
const jpegData = readFileSync('target.jpg');
const result = write(jpegData, metadata);
if (result.type === 'success') {
writeFileSync('output.jpg', result.data);
console.log('Metadata converted from PNG to JPEG');
}Handling Different Result Types
import { read } from 'sd-metadata';
const result = read(imageData);
switch (result.status) {
case 'success':
// Metadata successfully parsed
console.log(`Generated by ${result.tool}`);
console.log(`Prompt: ${result.prompt}`);
break;
case 'unrecognized':
// Format detected but not recognized as AI-generated
console.log('Not an AI-generated image');
// You can still access raw metadata for debugging:
console.log('Raw chunks:', result.raw);
break;
case 'empty':
// No metadata found
console.log('No metadata in this image');
break;
case 'unsupportedFormat':
// Not a PNG, JPEG, or WebP file
console.log('Unsupported image format');
break;
case 'invalid':
// Corrupted or invalid image data
console.log('Error:', result.message);
break;
}Force Conversion for Unrecognized Formats
When you have unrecognized metadata but still want to convert it:
import { read, write } from 'sd-metadata';
const source = read(unknownImage);
// source.status === 'unrecognized'
// Force blind conversion (preserves all metadata chunks/segments)
const result = write(targetImage, source, { force: true });
if (result.type === 'success') {
// Metadata successfully converted even though format wasn't recognized
console.log('Forced conversion succeeded');
}Removing Metadata
To strip all metadata from an image:
import { write } from 'sd-metadata';
const result = write(imageData, { status: 'empty' });
if (result.type === 'success') {
writeFileSync('clean-image.png', result.data);
}API Reference
read(data: Uint8Array): ParseResult
Reads and parses metadata from an image file.
Returns:
{ status: 'success', tool, prompt, parameters, width, height, raw }- Successfully parsed{ status: 'unrecognized', raw }- Image has metadata but not from a known AI tool{ status: 'empty' }- No metadata found{ status: 'unsupportedFormat' }- Not a PNG, JPEG, or WebP file{ status: 'invalid', message }- Corrupted or invalid image data
write(data: Uint8Array, metadata: ParseResult, options?: WriteOptions): WriteResult
Writes metadata to an image file.
Parameters:
data- Target image file data (PNG, JPEG, or WebP)metadata- ParseResult fromread()status: 'success'or'empty'- Can write directlystatus: 'unrecognized'- Requiresforce: trueoption
options- Optional settings:force?: boolean- Required when writingstatus: 'unrecognized'metadata
Returns:
{ type: 'success', data }- Successfully written{ type: 'unsupportedFormat' }- Target is not PNG, JPEG, or WebP{ type: 'conversionFailed', message }- Metadata conversion failed{ type: 'writeFailed', message }- Failed to write metadata to image
Known Limitations
[!WARNING] ComfyUI JPEG/WebP: While reading supports major custom node formats (e.g.,
save-image-extended), writing always uses thecomfyui-saveimage-plusformat. This format provides the best information preservation and is compatible with ComfyUI's native drag-and-drop workflow loading.
[!WARNING] 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).
[!WARNING] 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
promptchunk is lost.
Development
# Install dependencies
npm install
# Run tests
npm test
# Watch mode
npm run test:watch
# Test coverage
npm run test:coverage
# Build
npm run build
# Lint
npm run lintLicense
MIT
