# @enslo/sd-metadata 中文文档

规范地址：https://npmcn.com/package/%40enslo/sd-metadata/

版本：4\.1\.0
npm 元数据更新时间：2026-11-09T10:56:19.413Z
中文译文更新时间：2026-11-13T18:06:51.489Z
软件包许可：MIT

本文由上游 README 翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%40enslo/sd-metadata
原文对照：https://npmcn.com/package/%40enslo/sd-metadata/?lang=original
项目主页：https://sd-metadata.pages.dev/
源码仓库：https://github.com/enslo/sd-metadata

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：1.2 MB，11 个文件
- 模块格式：ES Module
- TypeScript 类型：包内自带
- 运行依赖：0 个
- 首次发布：2026-01-12T14:52:50.018Z
- 最新版本发布：2026-11-09T10:56:19.018Z
- 近 30 天下载量：3878（2026-10-14 至 2026-11-12，npm 官方下载统计）
- 已发布版本数：29（近 12 个月 29 个）

## 中文简介

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

## 安装

```sh
npm install @enslo/sd-metadata
```

也可以使用：`pnpm add @enslo/sd-metadata`、`yarn add @enslo/sd-metadata`、`bun add @enslo/sd-metadata`

## README

# @enslo/sd-metadata (英语)

[![npm version](https://img.shields.io/npm/v/@enslo/sd-metadata.svg)](https://www.npmjs.com/package/@enslo/sd-metadata) [![npm downloads](https://img.shields.io/npm/dm/@enslo/sd-metadata.svg)](https://www.npmjs.com/package/@enslo/sd-metadata) [![license](https://img.shields.io/npm/l/@enslo/sd-metadata.svg)](https://github.com/enslo/sd-metadata/blob/main/LICENSE)

🌐 **[日本語版はこちら](https://npmcn.com/package/%40enslo/sd-metadata/README.ja.md)**

🔗 **[现场直播](https://sd-metadata.pages.dev/)**

一个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或签名标记声明为自己的图像和视频——只检测,不验证签名

## 安装

```bash
npm install @enslo/sd-metadata
```

## 快速启动

```typescript
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);
}
```

见 [使用量](https://npmcn.com/package/%40enslo/sd-metadata/#readme-usage) 用于Node.js、浏览器和用户标注入口,以及 [高级示例](https://npmcn.com/package/%40enslo/sd-metadata/#readme-advanced-examples) 其他结果状况(`vendor`, (中文(简体) ), `unrecognized`, (中文(简体) ), `empty`, (中文(简体) ), `invalid`) (中文(简体) ).

## 工具支持

| 工具 | 巴布亚新几内亚 | JPEG 组合 | 网络P |
| --- | --- | --- | --- |
| [小说](https://novelai.net/) \* 联合国 | ✅ | 🔄️ | ✅ |
| [康菲乌伊](https://github.com/comfyanonymous/ComfyUI) \* 联合国 | ✅ | 🔄️ | ✅ |
| [稳定扩散网络UI](https://github.com/AUTOMATIC1111/stable-diffusion-webui) | ✅ | ✅ | ✅ |
| [福尔吉](https://github.com/lllyasviel/stable-diffusion-webui-forge) | ✅ | ✅ | ✅ |
| [Forge 经典](https://github.com/Haoming02/sd-webui-forge-classic/tree/classic) | ✅ | ✅ | ✅ |
| [福尔格·尼欧](https://github.com/Haoming02/sd-webui-forge-classic/tree/neo) | ✅ | ✅ | ✅ |
| [重新定义](https://github.com/Panchovix/stable-diffusion-webui-reForge) | ✅ | ✅ | ✅ |
| [轻松的Reforge](https://github.com/Zuntan03/EasyReforge) | ✅ | ✅ | ✅ |
| [(原始内容存档于2017-03-21). SD. 下一个](https://github.com/vladmandic/automatic) | ✅ | ✅ | ✅ |
| [印度](https://github.com/invoke-ai/InvokeAI) | ✅ | 🔄️ | 🔄️ |
| [沼泽地](https://github.com/mcmonkeyprojects/SwarmUI) \* 联合国 | ✅ | ✅ | ✅ |
| [锡维泰](https://civitai.com/) | ✅ | ✅ | ⚠️ |
| [日光艺术](https://tensor.art/) | ✅ | 🔄️ | 🔄️ |
| [稳定矩阵](https://github.com/LykosAI/StabilityMatrix) | ✅ | 🔄️ | 🔄️ |
| [拥抱 面相空间](https://huggingface.co/spaces) | ✅ | 🔄️ | 🔄️ |
| [风云](https://github.com/lllyasviel/Fooocus) | ✅ | ✅ | ✅ |
| [被破坏的福奥克](https://github.com/runew0lf/RuinedFooocus) | ✅ | 🔄️ | 🔄️ |
| [容易扩散](https://github.com/easydiffusion/easydiffusion) | ✅ | ✅ | ✅ |
| [绘制事物](https://drawthings.ai/) | ✅ | 🔄️ | 🔄️ |
| [德兹戈](https://dezgo.com/) | ✅ | ✅ | ✅ |
| [库斯阿特](https://kusart.com/) | ✅ | 🔄️ | 🔄️ |

**图例:**

-   ✅ **已完全支持** - 工具本身支持的格式,用样本文件验证
-   🔄️ **扩展支持** - 工具不本地支持的格式,但 sd-metadata 允许通过自定义格式转换来读取/写入。 支持回转转换为本地格式.
-   ⚠️ **实验** - 通过分析参考代码或文件加以执行,未用实际样本文件加以核实。 可能无法正确提取所有元数据字段 .

扩展支持示例

-   **稳定矩阵** (内在:只限PNG) → sd-元数据启用 JPEG/WebP 支持
-   **小说** (原生:PNG, WebP) → sd-metadata 启用了 JPEG 支持

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

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

> \[TIP\] **帮助我们扩展工具支持!** 我们正在积极收集实验组合(Civitai WebP)和无支持工具的样本图像. 如果您有这些或其他AI工具生成的样本图像,请考虑贡献! 见 [捐款额](https://github.com/enslo/sd-metadata/blob/main/CONTRIBUTING.md) 详细情况.

### 视频

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

-   [康菲乌伊](https://github.com/comfyanonymous/ComfyUI) ——内设 `SaveVideo` 节点和 [视频帮助套件](https://github.com/Kosinkadink/ComfyUI-VideoHelperSuite)是因为 `Video Combine` (MP4、WebM、MKV;其ProRes MOV输出没有元数据)
-   [福尔格·尼欧](https://github.com/Haoming02/sd-webui-forge-classic/tree/neo) - Wan 2.2视频(MP4、MKV)

视频只读 : `write()` 财务报告和审定财务报表 `embed()` 拍摄图像。 见 [视频文件](https://npmcn.com/package/%40enslo/sd-metadata/#readme-video-files)。 。 。 .

## 格式 - 特定行为

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

-   **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');`

### 节点使用

```typescript
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);
}
```

### 浏览器使用

```typescript
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`数字 :

```javascript
// ==UserScript==
// @name        My Script
// @namespace   https://example.com
// @require     https://cdn.jsdelivr.net/npm/@enslo/sd-metadata@4.1.0/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](https://github.com/ashen-sensored/sd_webui_stealth_pnginfo) 扩展家族(A1111/Forge, [康菲乌伊](https://github.com/catboxanon/comfyui_stealth_pnginfo))将生成元数据的副本隐藏在像素最小的位点上,当图像主机剥去元数据块时,它在那里生存. `parse()` 自动恢复:当一个图像没有可读元数据时,它扫描像素作为回放,回收的结果带有 `stealth: true`. 宣布其供应商(ChatGPT或带有C2PA清单的双子座PNG)的图像没有扫描,因为这些服务不会写入隐形数据:

```typescript
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" [高级示例](https://npmcn.com/package/%40enslo/sd-metadata/#readme-advanced-examples)。 。 。 .
-   **救援**: 回收结果种子 `write()` 不变,将原始工具的本地元数据恢复到已剥离的文件。 参见"检索剥去的元数据"下 [高级示例](https://npmcn.com/package/%40enslo/sd-metadata/#readme-advanced-examples)。 。 。 .
-   **服务器**:扫描膨胀并解开整幅图像(一台一相元像素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()`),并且只读取了持有元数据的字节范围,因此一个大文件从未被装入整个:

```typescript
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验证检测" [高级示例](https://npmcn.com/package/%40enslo/sd-metadata/#readme-advanced-examples)。不支持将元数据写入视频.

## 高级示例

格式转换

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

```typescript
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');
  }
}
```

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

处理不同的结果类型

```typescript
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;
}
```
处理未识别元数据

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

```typescript
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 }` 对于这些文件:

```typescript
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
}
```

| 供应商 | 服务 | 声明 | 图像 | 视频 |
| --- | --- | --- | --- | --- |
| 开放AI | GPT 图像 (ChatGPT), Sora | C2PA 数据 | 巴布亚新几内亚 | 马普4 |
| 谷歌 | 双子座,Veo | C2PA 数据 | 巴布亚新几内亚, 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, 按工具录入的顺序排列, 当没有时则缺席 :

```typescript
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`,例如由 [锐口](https://www.npmjs.com/package/sharp)没有它,WebP隐形扫描被优雅地跳过:

```typescript
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 :

```typescript
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.
  }
}
```
删除元数据

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

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

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

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

```typescript
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`数字 :

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

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

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

```typescript
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 恢复](https://npmcn.com/package/%40enslo/sd-metadata/#readme-stealth-pnginfo-recovery));扫描只在这样的情况下运行,而不是用于有供应商申报的图像,因此有普通元数据的图像不支付额外费用.

**参数:**

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

**回返情况:**

-   `{ 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)读取并解析元数据. 见 [视频文件](https://npmcn.com/package/%40enslo/sd-metadata/#readme-video-files)。 。 。 .

**参数:**

-   `input` - 视频文件数据,或 `Blob` 编号 : `File`A级 `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()`](https://npmcn.com/package/%40enslo/sd-metadata/#readme-parseinput-uint8array--arraybuffer-options-readoptions-promiseparseresult) 换句话说. `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"下 [高级示例](https://npmcn.com/package/%40enslo/sd-metadata/#readme-advanced-examples)(中文(简体) )

**回返情况:**

-   `{ 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()`,当销售商身份不明时仅提供服务。 任何其他状态都给出了空字符串 。 元数据或供应商申报也可以自行传递.

```typescript
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` 其人可读的显示名称的标识符.

```typescript
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` 公司名称的标识符.

```typescript
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). 未知产品值按正文返回 .

```typescript
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"
}
```

## 类型参考

本节概述主要类型. 完整类型定义见 [文档类型](https://npmcn.com/package/%40enslo/sd-metadata/docs/types.md)。 。 。 .

### `ParseResult`

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

```typescript
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` 保存容器格式和文件的文本标记,而不是块或块.

```typescript
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` 字段 :

```typescript
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` 使用特定工具领域的歧视者:

```typescript
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`设置类型——参见 [文档类型](https://npmcn.com/package/%40enslo/sd-metadata/docs/types.md)。 。 。 .

## 发展

见 [贡献指南](https://github.com/enslo/sd-metadata/blob/main/CONTRIBUTING.md) (c) 发展结构和准则.

## 许可证

麻省理工学院

## 常见问题

### @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 和提交记录判断。

