@enslo/sd-metadata

中文文档
4.1.0发布于 2026/11/09MITTypeScriptES Module零依赖构建来源已验证近 30 天下载 3878

读取和写入AI生成的图像元数据

中文使用文档
文档目录30 节
  1. 特征
  2. 安装
  3. 快速启动
  4. 工具支持
  5. 视频
  6. 格式 - 特定行为
  7. 使用量
  8. 节点使用
  9. 浏览器使用
  10. 用户标记使用
  11. 隐形 PNGInfo 恢复
  12. 视频文件
  13. 高级示例
  14. API 参考
  15. parse(input: Uint8Array | ArrayBuffer, options?: ReadOption…
  16. parseVideo(input: Uint8Array | ArrayBuffer | Blob, options?…
  17. read(input: Uint8Array | ArrayBuffer, options?: ReadOptions…
  18. write(input: Uint8Array | ArrayBuffer, metadata: ParseResul…
  19. embed(input: Uint8Array | ArrayBuffer, metadata: EmbedMetad…
  20. stringify(input: ParseResult | VideoParseResult | EmbedMeta…
  21. originLabel(input: ParseResult | VideoParseResult | Generat…
  22. softwareLabels: Record<GenerationSoftware, string>
  23. vendorLabels: Record<Vendor, string>
  24. productLabel(info: VendorInfo): string
  25. 类型参考
  26. ParseResult
  27. VideoParseResult
  28. GenerationMetadata
  29. 发展
  30. 许可证

@enslo/sd-metadata (英语)

npm version npm downloads license

🌐 日本語版はこちら

🔗 现场直播

一个TypeScript库来读取并写入嵌入到AI生成的图像中的元数据.

特征

  • 多形式支助: PNG (tEXt / iTXt / eXIF), JPEG (COM / Exif), WebP (Exif), 加上 MP4 / MOV 和 WebM / MKV 视频(只读)
  • 简单 API数字 : parse(), (中文(简体) ), write(), (中文(简体) ), embed(), (中文(简体) ), stringify() 四种功能涵盖所有图像使用案例; parseVideo() 添加视频
  • 隐形 PNGInfo 恢复: 回收被 NovelAI 和 seftth-pnginfo 扩展隐藏在像素 LSB 中的元数据, 即使图像主机剥去普通元数据
  • 洛拉意识到: 列出应用于图像的 LORAs, 从 A1111 标记、 工具 JSON 和 ComfyUI 加载器节点读取
  • 脚本原生类型: 写入 TypeScript, 包含完整类型定义
  • 无依赖: 在节点和浏览器中工作, 没有任何外部依赖性
  • 格式转换: 在 PNG 、 JPEG 和 WebP 之间无缝转换元数据
  • 元数据保存: 转换格式时保留原始元数据结构(如PNG → JPEG → PNG 保存所有原始数据)
  • AI 取证检测:识别商业AI服务(OpenAI,Google,xAI,Meta)通过C2PA,XMP或签名标记声明为自己的图像和视频——只检测,不验证签名

安装

npm install @enslo/sd-metadata

快速启动

import { parse } from '@enslo/sd-metadata';

// `imageBytes` is a Uint8Array or ArrayBuffer (from fs, fetch, a file input, ...)
const result = await parse(imageBytes);
if (result.status === 'success') {
  console.log('Tool:', result.metadata.software); // 'novelai', 'comfyui', ...
  console.log('Prompt:', result.metadata.prompt);
  console.log('Size:', result.metadata.width, 'x', result.metadata.height);
}

见 使用量 用于Node.js、浏览器和用户标注入口,以及 高级示例 其他结果状况(vendor, (中文(简体) ), unrecognized, (中文(简体) ), empty, (中文(简体) ), invalid) (中文(简体) ).

工具支持

工具巴布亚新几内亚JPEG 组合网络P
小说 * 联合国✅🔄️✅
康菲乌伊 * 联合国✅🔄️✅
稳定扩散网络UI✅✅✅
福尔吉✅✅✅
Forge 经典✅✅✅
福尔格·尼欧✅✅✅
重新定义✅✅✅
轻松的Reforge✅✅✅
(原始内容存档于2017-03-21). SD. 下一个✅✅✅
印度✅🔄️🔄️
沼泽地 * 联合国✅✅✅
锡维泰✅✅⚠️
日光艺术✅🔄️🔄️
稳定矩阵✅🔄️🔄️
拥抱 面相空间✅🔄️🔄️
风云✅✅✅
被破坏的福奥克✅🔄️🔄️
容易扩散✅✅✅
绘制事物✅🔄️🔄️
德兹戈✅✅✅
库斯阿特✅🔄️🔄️

图例:

  • ✅ 已完全支持 - 工具本身支持的格式,用样本文件验证
  • 🔄️ 扩展支持 - 工具不本地支持的格式,但 sd-metadata 允许通过自定义格式转换来读取/写入。 支持回转转换为本地格式.
  • ⚠️ 实验 - 通过分析参考代码或文件加以执行,未用实际样本文件加以核实。 可能无法正确提取所有元数据字段 .
扩展支持示例
  • 稳定矩阵 (内在:只限PNG) → sd-元数据启用 JPEG/WebP 支持
  • 小说 (原生:PNG, WebP) → sd-metadata 启用了 JPEG 支持

当从本地格式转换为扩展格式并返回(如PNG → JPEG → PNG)时,所有元数据都会被保存.

[注] * 有特定格式行为的工具——见下文.

[TIP] 帮助我们扩展工具支持! 我们正在积极收集实验组合(Civitai WebP)和无支持工具的样本图像. 如果您有这些或其他AI工具生成的样本图像,请考虑贡献! 见 捐款额 详细情况.

视频

parseVideo() 读取 MP4 / MOV (QuickTime)和 WebM / MKV (Matroska) 文件. 生成元数据通过和图像相同的解析器,所以将通常文本写入视频标签的工具被报告为相同 software。通过样本文件从:

视频只读 : write() 财务报告和审定财务报表 embed() 拍摄图像。 见 视频文件。 。 。 .

格式 - 特定行为

一些工具在格式转换时有特定的行为:

  • ComfyUI JPEG/网络P:读取支持多个节点格式(例如内置) Save Animated WEBP 财务报告和审定财务报表 save-image-extended) (中文(简体) ). 写入总是匹配内置 Save Animated WEBP 节点 Make编号 :Model EXIF 标记为字节的字节- 布局 ComfyUI 本身为 WebP 拖放读取本地读取 – 放下除此之外的任何 PNG 块 prompt编号 :workflow (例如:. parameters 从第三方保存节点.
  • 小说网P:在"描述"字段中自动更正已腐败的UTF-8. WebP → PNG → WebP 往返生成有效,可读的元数据,但文字有小的校正.
  • SWARMUI PNG JPEG/WebP: 原生 SwarmUI JPEG/WebP文件不包括节点信息. 在从PNG转换时,此库保存了 ComfyUI 工作流程 Make 字段用于完整保存元数据(扩展支持).

使用量

[注] 所有例子都使用无害环境管理语法。 普通JS 用户可以替换 import 与 require数字 : const { parse } = require('@enslo/sd-metadata');

节点使用

import { parse, stringify } from '@enslo/sd-metadata';
import { readFileSync } from 'fs';

const imageData = readFileSync('image.png');
const result = await parse(imageData);

if (result.status === 'success') {
  console.log('Tool:', result.metadata.software);       // 'novelai', 'comfyui', etc.
  console.log('Prompt:', result.metadata.prompt);
  console.log('Model:', result.metadata.model?.name);
  console.log('Size:', result.metadata.width, 'x', result.metadata.height);
}

// Format as human-readable text (works with any status)
const text = stringify(result);
if (text) {
  console.log(text);
}

浏览器使用

import { parse, softwareLabels } from '@enslo/sd-metadata';

// Handle file input
const fileInput = document.querySelector('input[type="file"]');
fileInput.addEventListener('change', async (e) => {
  const file = e.target.files[0];
  if (!file) return;

  const arrayBuffer = await file.arrayBuffer();
  const result = await parse(arrayBuffer);

  if (result.status === 'success') {
    document.getElementById('tool').textContent = softwareLabels[result.metadata.software];
    document.getElementById('prompt').textContent = result.metadata.prompt;
    document.getElementById('model').textContent = result.metadata.model?.name || 'N/A';
  }
});

用户标记使用

对于用户标注(Tampermonkey, violencemonkey等),通过IFE架构加载 @require数字 :

// ==UserScript==
// @name        My Script
// @namespace   https://example.com
// @require     https://cdn.jsdelivr.net/npm/@enslo/[email protected]/dist/index.global.js
// ==/UserScript==

const response = await fetch(imageUrl);
const arrayBuffer = await response.arrayBuffer();
const result = await sdMetadata.parse(arrayBuffer);

if (result.status === 'success') {
  console.log('Tool:', result.metadata.software);
  console.log('Prompt:', result.metadata.prompt);
}

[TIP] 总是固定在特定的版本 @require 为了稳定.

隐形 PNGInfo 恢复

小说(默认)和 隐形的 pnginfo 扩展家族(A1111/Forge, 康菲乌伊)将生成元数据的副本隐藏在像素最小的位点上,当图像主机剥去元数据块时,它在那里生存. parse() 自动恢复:当一个图像没有可读元数据时,它扫描像素作为回放,回收的结果带有 stealth: true. 宣布其供应商(ChatGPT或带有C2PA清单的双子座PNG)的图像没有扫描,因为这些服务不会写入隐形数据:

import { parse } from '@enslo/sd-metadata';

const result = await parse(strippedImage);
if (result.status === 'success') {
  console.log(result.stealth); // true — recovered from pixels
  console.log(result.metadata.prompt);
}
  • 所有四个变体 被读取——α-或RGB-通道嵌入,被压缩或未被压缩——对于NovelAI的JSON有效载荷和扩展的纯信息有效载荷来说都是.
  • 网络P: NovelAI在其无损失的WebP出口中也嵌入了被窃取的数据. 浏览器自动解码像素;在Node.js中,Bun或Deno通过解码器 decodePixels 选项(例如由锐口支持),否则将跳过WebP扫描。 见下面的"服务器上的 Stealth recover" 高级示例。 。 。 .
  • 救援: 回收结果种子 write() 不变,将原始工具的本地元数据恢复到已剥离的文件。 参见"检索剥去的元数据"下 高级示例。 。 。 .
  • 服务器:扫描膨胀并解开整幅图像(一台一相元像素PNG约50ms,4兆像素约200ms,在Node.js 24上). 解压被封顶在标题上,256个像素的米B和16个有效载荷的米B上,所以一个精心制作的文件不能声称是千兆字节,而是在未信任的上传上,在呼叫前限制文件大小和尺寸 parse(),或使用 read() 在不需要康复的地方.
  • 扫描需要 DecompressionStream (Node.js 18+, Bun 1.4+, Deno, 所有现代浏览器);没有它 parse() 降解为以块为基础的读取。 撰写隐形数据是故意超出范围.

视频文件

parseVideo() 从 MP4 / MOV 和 WebM / MKV 文件读取生成元数据,结果状态与 parse()通过 a Blob (浏览器) File,或节点 fs.openAsBlob()),并且只读取了持有元数据的字节范围,因此一个大文件从未被装入整个:

import { parseVideo, stringify } from '@enslo/sd-metadata';
import { openAsBlob } from 'node:fs';

const result = await parseVideo(await openAsBlob('output.mp4')); // or a File from an <input>
if (result.status === 'success') {
  console.log(result.metadata.software); // 'comfyui', 'forge-neo', ...
  console.log(result.metadata.prompt);
  console.log(result.raw.format);        // 'mp4' | 'mov' | 'webm' | 'mkv'
  console.log(result.raw.tags);          // the file's text tags, in file order
}

console.log(stringify(result)); // A1111-format text, as for images

商业服务(Google Veo、xAI Grok Imagine、Meta Muse、OpenAI Sora)的视频被报道 vendor 状态——见下面的"AI验证检测" 高级示例。不支持将元数据写入视频.

高级示例

格式转换

在不同图像格式之间转换元数据 :

import { parse, write } from '@enslo/sd-metadata';

// Read metadata from PNG
const pngData = readFileSync('comfyui-output.png');
const parseResult = await parse(pngData);

if (parseResult.status === 'success') {
  // Convert PNG to JPEG (using your preferred image processing library)
  const jpegImageData = convertToJpeg(pngData); // Pseudo-code: use sharp, canvas, etc.

  // Embed the metadata into the JPEG
  const result = write(jpegImageData, parseResult);

  if (result.ok) {
    writeFileSync('output.jpg', result.value);
    console.log('Image converted to JPEG with metadata preserved');
  }
}

提示 : 此库只处理读/写元数据 。 对于图像格式的实际转换(解码/编码像素),使用像图像处理库 锐口, (中文(简体) ), 竖起,或浏览器 Canvas API.

处理不同的结果类型
import { originLabel, parse } from '@enslo/sd-metadata';

const result = await parse(imageData);

switch (result.status) {
  case 'success':
    // Metadata successfully parsed
    console.log(`Generated by ${result.metadata.software}`);
    console.log(`Prompt: ${result.metadata.prompt}`);
    break;

  case 'vendor':
    // A commercial AI service declared the image as its own — see "AI Provenance Detection" below
    console.log(`Made by ${originLabel(result)} (unverified)`); // e.g. "xAI Grok Imagine"
    break;

  case 'unrecognized':
    // Metadata exists but format is not recognized
    console.log('Unknown metadata format');
    // 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 'invalid':
    // Corrupted or invalid image data
    console.log('Error:', result.message);
    break;
}
处理未识别元数据

在使用不支持的工具处理元数据时:

import { parse, write } from '@enslo/sd-metadata';

const source = await parse(unknownImage);
// source.status === 'unrecognized'

// Write to target image
// - Same format (e.g., PNG → PNG): metadata preserved as-is
// - Cross-format (e.g., PNG → JPEG): metadata dropped with warning
const result = write(targetImage, source);
if (result.ok) {
  saveFile('output.png', result.value);
  if (result.warning) {
    // Metadata was dropped during cross-format conversion
    console.warn('Metadata was dropped:', result.warning.reason);
  }
}
AI 鉴定(证人申报)

商业AI服务嵌入了一个他们制作图像的声明——一个签名的C2PA"Content Certification"清单,一个XMP包中的IPTC Digital SourceType,或者一个供应商自己的签名——而不是生成参数. parse() 财务报告和审定财务报表 parseVideo() 返回时 { status: 'vendor', info } 对于这些文件:

import { originLabel, parse, productLabel, vendorLabels } from '@enslo/sd-metadata';

const result = await parse(imageData);

if (result.status === 'vendor') {
  console.log('Vendor:', vendorLabels[result.info.vendor]); // "OpenAI", "Google", "xAI", "Meta"
  console.log('Service:', productLabel(result.info));       // "GPT Image", "Gemini", "Grok Imagine", "Muse"
  console.log('Declared in:', result.info.source);          // 'c2pa' | 'xmp' | 'signature'
  console.log('As written:', result.info.product);          // e.g. "ChatGPT", or undefined
  console.log(originLabel(result));                         // "xAI Grok Imagine" — the two joined
}
供应商服务声明图像视频
开放AIGPT 图像 (ChatGPT), SoraC2PA 数据巴布亚新几内亚马普4
谷歌双子座,VeoC2PA 数据巴布亚新几内亚, JPEG马普4
页:1 大赦国际格鲁克 想象一下C2PA, 签名JPEG 组合马普4
元数据缪斯XMP 电源JPEG, 网络程序马普4

productLabel() 名称服务:已知产品值( )ChatGPT, (中文(简体) ), gpt-image,.)被映射到它的服务名上,一个没有一张的申报回落到卖家为媒体提供的主要服务(图片:GPT Image,双子座,Grok Imagine,Muse;视频:Sora,Veo,Grok Imagine,Muse). 所声明的数值保留在 info.product。 。 。 .

侦测只是证据. 没有签名,所以 vendor 结果可以伪造,不能证明真实性。 这些标记也很容易被剥去(屏幕快照、重装、再编码),所以没有 vendor 结果不能证明图像是 没有 AI生成.

读取 LORAs

metadata.loras 列出影响图像的 LORAs, 按工具录入的顺序排列, 当没有时则缺席 :

import { parse } from '@enslo/sd-metadata';

const result = await parse(imageData);
if (result.status === 'success') {
  for (const lora of result.metadata.loras ?? []) {
    console.log(lora.name);       // exactly as the tool wrote it: file stem, path, URN or display name
    console.log(lora.weight);     // UNet / model strength, when recorded
    console.log(lora.clipWeight); // text-encoder strength, only when recorded separately
    console.log(lora.hash);       // as written (AutoV2, sha256, "blake3:..."), never normalized
  }
}

名单来自 <lora:…> 标签和 Lora hashes 在A1111风格文本中,从LORA列出在他们的JSON(SwarmUI,InvokeAI,Foocus, Easy Difusion, Draw Tthings, TensorArt, Dezgo, KusArt,.)中写出的工具,从ComfyUI加载器节点中——只有采样器实际依赖的加载器,所以没有列出被绕过或未连接加载器.

服务器上的隐形回收( WebP)

PNG像素由库本身解码,但WebP解码取决于运行时间. 浏览器使用平台解码器( WebCodecs) ImageDecoder后退为 createImageBitmap + 键 OffscreenCanvas无配置。 在Node.js, Bun 或 Deno 中通过 decodePixels,例如由 锐口没有它,WebP隐形扫描被优雅地跳过:

import { parse } from '@enslo/sd-metadata';
import sharp from 'sharp';

const result = await parse(webpData, {
  decodePixels: async (data) => {
    const { data: pixels, info } = await sharp(data)
      .ensureAlpha()
      .raw()
      .toBuffer({ resolveWithObject: true });
    return { data: new Uint8Array(pixels), width: info.width, height: info.height };
  },
});
检索剥去的元数据

隐藏结果种子 write() 直接将原始工具的本地元数据恢复到已剥离的文件——不需要单独的API :

import { parse, write } from '@enslo/sd-metadata';

const rescued = await parse(strippedImage);
if (rescued.status === 'success') {
  const restored = write(strippedImage, rescued);
  if (restored.ok) {
    // restored.value now carries regular metadata again,
    // readable by any tool — stealth data stays untouched in the pixels.
  }
}
删除元数据

从图像中去除所有元数据 :

import { write } from '@enslo/sd-metadata';

const result = write(imageData, { status: 'empty' });
if (result.ok) {
  writeFileSync('clean-image.png', result.value);
}
嵌入自定义元数据

以 A1111 格式创建并嵌入自定义元数据 :

import { embed } from '@enslo/sd-metadata';

const metadata = {
  prompt: 'masterpiece, best quality, 1girl',
  negativePrompt: 'lowres, bad quality',
  width: 512,
  height: 768,
  sampling: {
    steps: 20,
    sampler: 'Euler a',
    cfg: 7,
    seed: '12345', // seeds are strings — some tools use 64-bit values
  },
  model: { name: 'model.safetensors' },
  loras: [{ name: 'detail_tweaker', weight: 0.8 }], // written as <lora:detail_tweaker:0.8>
};

// Write to any image format (PNG, JPEG, WebP)
const result = embed(imageData, metadata);
if (result.ok) {
  writeFileSync('output.png', result.value);
}

您还可以在设置行中添加任意的密钥值对 extras数字 :

const result = embed(imageData, {
  ...metadata,
  extras: { Version: 'v1.10.0', 'Lora hashes': 'abc123' },
});

提示 : 如果一个额外的密钥匹配结构化字段( 例如, ), Steps),外值在原位上超过结构值. 结尾处附上了新的密钥.

自兹 EmbedMetadata 是一个子集 GenerationMetadata 变量,您可以直接通过解析的元数据——包括小说AI及其 characterPrompts数字 :

import { parse, embed } from '@enslo/sd-metadata';

const result = await parse(novelaiPng);
if (result.status === 'success') {
  // NovelAI metadata (or any other tool) works as-is
  const output = embed(blankJpeg, result.metadata);
}

API 参考

parse(input: Uint8Array | ArrayBuffer, options?: ReadOptions): Promise<ParseResult>

读取并解析图像文件中的元数据 。 当一个图像没有可读的元数据时, 另外扫描像素以获取 Steather PNGInfo (参见 隐形 PNGInfo 恢复);扫描只在这样的情况下运行,而不是用于有供应商申报的图像,因此有普通元数据的图像不支付额外费用.

参数:

  • input - 图像文件数据(PNG、JPEG或WebP)
  • options - 可选读取选项
    • strict?: boolean (默认 : false——何时 true,维度(width 编号 : height)严格只取自元数据。 何时 false,从图像头取出缺失的维度.
    • decodePixels?: (data, format) => Promise<RgbaPixels | null> ——解码WebP像素,用于平台无法进行隐形扫描的地方(服务器). 被称作"懒惰",只有在一个WebP没有可读元数据时才会使用. 见下面的"服务器上的 Stealth recover" 高级示例。 。 。 .

回返情况:

  • { status: 'success', metadata, raw, stealth? } - 成功解析
    • metadata: 统一元数据对象(见 GenerationMetadata(中文(简体) )
    • raw: 原始格式特定数据(圆柱/分集)
    • stealth数字 : true 当元数据从像素 LSB 中恢复时
  • { status: 'vendor', info } - 一个商业AI服务(OpenAI,Google,xAI,Meta)宣布该图像为自己的;没有生成参数可读
    • info数字 : VendorInfo —— 说 vendor, (中文(简体) ), product (如有), source (单位:千美元)'c2pa' | 'xmp' | 'signature'和 media: 'image'. 侦测只是——没有经过核实.
  • { status: 'unrecognized', raw, stealth? } - 图像有元数据,但没有来自已知的AI工具
    • raw: 保存供转换的原始元数据
    • stealth数字 : true 当原始数据从像素LSB中恢复时
  • { status: 'empty' } - 在图像中找不到元数据
  • { status: 'invalid', message? } - 损坏或不支持的图像格式
    • message: 可选错误描述

parseVideo(input: Uint8Array | ArrayBuffer | Blob, options?: VideoReadOptions): Promise<VideoParseResult>

从视频文件(MP4/MOV,WebM/MKV)读取并解析元数据. 见 视频文件。 。 。 .

参数:

  • input - 视频文件数据,或 Blob 编号 : FileA级 Blob 以小范围阅读,只读有元数据的框和元素,而不是装入全部.
  • options - 可选读取选项
    • strict?: boolean (默认 : false——何时 true,维度严格取自元数据. 何时 false,缺失的维度取自视频轨道.

回返情况:

状态与 parse(),与 VideoRawMetadata (单位:千美元)format 文件的文本 tags改为: RawMetadata 财务报告和审定财务报表 media: 'video' 在供应商中 info视频没有隐形数据,a VideoParseResult 无法传递到 write()。 。 。 .

read(input: Uint8Array | ArrayBuffer, options?: ReadOptions): ParseResult

[尖叫声] 折旧 ——使用 parse() 换句话说. read() 仍然可供需要同步、只读块的呼叫者使用,但无法恢复Steftth PNGInfo.

页:1 parse() 但它是同步的,从不扫描像素.

write(input: Uint8Array | ArrayBuffer, metadata: ParseResult): WriteResult

将元数据写入图像文件。 目标现有的元数据被替换,而不是合并:它的文本块, EXIF 和 XMP 是从 metadata,因此不结转诸如方向、日期时间或Make / Model等相机 EXIF.

参数:

  • input - 目标图像文件数据(PNG、JPEG或WebP)
  • metadata - 说吧 ParseResult 从 parse()
    • status: 'success' 或者说 'empty' - 可以直接写作
    • status: 'unrecognized' - 格式相同:写为-is;跨格式:删除带有警告的元数据
    • 被窃取的结果是可行的——写作恢复了原始工具的本土元数据(见"Rescuring extract medata"下 高级示例(中文(简体) )

回返情况:

  • { ok: true, value: Uint8Array, warning?: WriteWarning } - 成功写入
    • warning 当部分或全部元数据被忽略时设置: reason 实值 'unrecognizedCrossFormat' (未确认元数据为另一种格式), 'conversionEmpty' (工具的转换器找不到目标格式可以持有的任何东西)或 'segmentTooLarge' (跳过一个超过65 535字节的JPEG COM/XMP段; message 说吧
  • { ok: false, error: { type, message? } } - 失败. type 这是:
    • 'unsupportedFormat': 目标图像不是 PNG、 JPEG 或 WebP
    • 'conversionFailed': 元数据转换失败( 如不兼容格式)
    • 'writeFailed': 将元数据嵌入图像失败, 或者输入无法回写

embed(input: Uint8Array | ArrayBuffer, metadata: EmbedMetadata | GenerationMetadata): WriteResult

将自定义元数据嵌入到SD WebUI(A1111)格式的图像中. 一样 write(),将替换目标的现有元数据(文本块、EXIF、XMP).

参数:

  • input - 目标图像文件数据(PNG、JPEG或WebP)
  • metadata - 说吧 EmbedMetadata 或者说 GenerationMetadata 嵌入( 使用) extras 自定义设置字段)

回返情况:

  • { ok: true, value: Uint8Array } - 成功写入(返回新图像数据)
  • { ok: false, error: { type, message? } } - 失败. type 这是:
    • 'unsupportedFormat': 目标图像不是 PNG、 JPEG 或 WebP
    • 'writeFailed': 将元数据嵌入图像失败

使用大小写 :

  • 为程序生成的图像创建自定义元数据
  • 将元数据从其他工具转换为WebUI兼容格式
  • 构建输出WebUI可读元数据的应用程序

stringify(input: ParseResult | VideoParseResult | EmbedMetadata | GenerationMetadata): string

将元数据转换为人可读字符串.

参数:

  • input - 一个:
    • ParseResult 从 parse() 或者说 VideoParseResult 从 parseVideo() ——根据地位选择最佳代表性
    • EmbedMetadata 或者说 GenerationMetadata — 格式为 A1111 文本

回返情况:

  • ParseResult 编号 : VideoParseResult数字 :
    • success + A1111格式文本。 对于自己写出A1111文本的工具(sd-webui,Forge family, reForge,SD.Next,.),原始文本作为工具所写的返回,所以密钥库没有模型存活;对于其他工具,文本从元数据重建,LORAS包含
    • vendor → <Vendor> <Service> (例如:. OpenAI GPT Image)),或当销售商身份不明时仅提供服务
    • unrecognized + 原始元数据为纯文本
    • empty 编号 : invalid — 空字符串
  • EmbedMetadata 编号 : GenerationMetadata • A1111格式文本

使用大小写 :

  • 在图像查看器或画廊中显示生成参数
  • 将元数据复制到剪贴板, 作为可读文本
  • 日志或调试已解析的元数据
  • 预览 EmbedMetadata 嵌入前

originLabel(input: ParseResult | VideoParseResult | GenerationMetadata | VendorInfo): string

在一个呼叫中显示所编文件的名称: 软件 success 结果(如 softwareLabels)),或供应商及其服务 vendor 结果(如 vendorLabels 财务报告和审定财务报表 productLabel(),当销售商身份不明时仅提供服务。 任何其他状态都给出了空字符串 。 元数据或供应商申报也可以自行传递.

import { originLabel, parse } from '@enslo/sd-metadata';

const result = await parse(imageData);
console.log(originLabel(result));
// => "NovelAI", "ComfyUI", "OpenAI GPT Image", "xAI Grok Imagine", ... or "" when there is nothing to name

softwareLabels: Record<GenerationSoftware, string>

仅读绘图 GenerationSoftware 其人可读的显示名称的标识符.

import { softwareLabels } from '@enslo/sd-metadata';

const result = await parse(imageData);
if (result.status === 'success') {
  console.log(softwareLabels[result.metadata.software]);
  // => "NovelAI", "ComfyUI", "Stable Diffusion WebUI", etc.
}

vendorLabels: Record<Vendor, string>

仅读绘图 Vendor 公司名称的标识符.

import { vendorLabels } from '@enslo/sd-metadata';

const result = await parse(imageData);
if (result.status === 'vendor') {
  console.log(vendorLabels[result.info.vendor]);
  // => "OpenAI", "Google", "xAI", "Meta", "Unknown"
}

productLabel(info: VendorInfo): string

显示制作供应商图像或视频的AI服务名称 。 已知的已申报产品(ChatGPT, (中文(简体) ), gpt-image, (中文(简体) ), Grok Imagine,.)被映射到其服务名;没有产品的声明回落到卖家为媒体提供的主要服务(图像:GPT Image,双子座,Grok Imagine,Muse;视频:Sora,Veo,Grok Imagine,Muse). 未知产品值按正文返回 .

import { productLabel, vendorLabels } from '@enslo/sd-metadata';

const result = await parse(imageData);
if (result.status === 'vendor') {
  console.log(`${vendorLabels[result.info.vendor]} ${productLabel(result.info)}`);
  // => "OpenAI GPT Image", "Google Gemini", "xAI Grok Imagine", "Meta Muse"
}

类型参考

本节概述主要类型. 完整类型定义见 文档类型。 。 。 .

ParseResult

产生的结果 parse() (已贬值) read()) 函数。 它使用受歧视的结合 status 字段. stealth 设置为 true 以 parse() 当结果从像素LSBs(Stealth PNGInfo)中恢复时.

type ParseResult =
  | { status: 'success'; metadata: GenerationMetadata; raw: RawMetadata; stealth?: boolean }
  | { status: 'vendor'; info: VendorInfo }
  | { status: 'unrecognized'; raw: RawMetadata; stealth?: boolean }
  | { status: 'empty' }
  | { status: 'invalid'; message?: string };

VideoParseResult

结果是: parseVideo()状态相同. raw 保存容器格式和文件的文本标记,而不是块或块.

type VideoParseResult =
  | { status: 'success'; metadata: GenerationMetadata; raw: VideoRawMetadata }
  | { status: 'vendor'; info: VendorInfo }
  | { status: 'unrecognized'; raw: VideoRawMetadata }
  | { status: 'empty' }
  | { status: 'invalid'; message?: string };

GenerationMetadata

返回的统一元数据结构 parse() 财务报告和审定财务报表 parseVideo() 由3个元数据类型组成的受歧视的联盟,以 software 字段 :

type GenerationMetadata =
  | NovelAIMetadata   // 'novelai' — adds V4 character-placement fields
  | ComfyUIMetadata   // 'comfyui' | 'tensorart' | 'stability-matrix' | 'swarmui' — adds the workflow graph (nodes)
  | StandardMetadata; // 'sd-webui', 'forge', 'invokeai', 'dezgo', ... — baseline fields only

所有变体共用 BaseMetadata 字段 : prompt, (中文(简体) ), negativePrompt, (中文(简体) ), width, (中文(简体) ), height选择 model 编号 : sampling 编号 : hires 编号 : upscale 设置和 loras 列表中选择一个选项。 紧紧地紧紧地 software 使用特定工具领域的歧视者:

if (result.status === 'success') {
  const metadata = result.metadata;
  console.log('Prompt:', metadata.prompt);
  console.log('Model:', metadata.model?.name);
  console.log('Seed:', metadata.sampling?.seed); // a string

  if (metadata.software === 'novelai') {
    // TypeScript knows this is NovelAIMetadata
    console.log('Character prompts:', metadata.characterPrompts);
  }
}

对于每一种出口类型的完整定义,包括: BaseMetadata, (中文(简体) ), EmbedMetadata, (中文(简体) ), RawMetadata, (中文(简体) ), VendorInfo, (中文(简体) ), LoraSettings, (中文(简体) ), GenerationSoftware设置类型——参见 文档类型。 。 。 .

发展

见 贡献指南 (c) 发展结构和准则.

许可证

麻省理工学院

版本与发布记录

@enslo/sd-metadata 共发布 29 个版本,首次发布于 2026/01/12;过去 12 个月发布了 29 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
4.1.0latest2026/11/094 天前
4.0.02026/11/058 天前
3.3.02026/08/202 个月前
3.2.02026/08/182 个月前
3.1.02026/07/183 个月前
3.0.02026/06/075 个月前

关于 @enslo/sd-metadata 的常见问题

@enslo/sd-metadata 是什么?

读取和写入AI生成的图像元数据

如何安装 @enslo/sd-metadata?

在项目目录运行 npm install @enslo/sd-metadata,或使用 pnpm add / yarn add / bun add。

@enslo/sd-metadata 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@enslo/sd-metadata 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@enslo/sd-metadata 可以商用吗?

@enslo/sd-metadata 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@enslo/sd-metadata 的下载量有多少?

根据 npm 官方下载统计,@enslo/sd-metadata 在 2026/10/14 – 2026/11/12 这 30 天内被下载 3,878 次,日均约 129 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@enslo/sd-metadata 还在维护吗?

最新版本 4.1.0 发布于 2026/11/09(4 天前);至今共发布 29 个版本,其中过去 12 个月发布了 29 个;首次发布于 2026/01/12。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。