本文主要说明自托管媒体库在把刮削结果落到磁盘 NFO 时,应如何看待「标准」:Kodi 方言是事实母版、字段全集与默认可互操作子集不是一回事,以及为何宜用 standard / extended 双版本而不是「一份超集 NFO 打天下」。以 Octans 一类治理型媒体库为背景,结论面向 Kodi / Emby / Jellyfin 兼容心智,不展开某一实现的完整字段表。

NFO 常被当成「随便导出一份 XML」。真正踩坑的地方多半不是 well-formed,而是:弱消费端按全局标签名误读、同一语义多标签互相覆盖、技术轨标题污染集标题、用户观看状态写进库级文件。下面按生态背景、分层原则、四类文档角色与验收口径整理,方便做导出设计或对照现有导出做减负。

NFO 到底「标准」在哪里

没有统一 XSD,Kodi 是事实母标准

媒体库生态里的 NFO,本质是 UTF-8 的 XML + 约定根节点 + 约定字段名,而不是某组织发布的强 schema。Kodi / XBMC 文档覆盖电影、剧集、单集等结构最完整,也是后续工具与服务端兼容的主要参照。

常见消费端大致分层理解即可:

角色怎么看待 NFO
Kodi字段母版;本地 NFO 可优先于远程刮削
Jellyfin可读写本地 .nfo;字段更宽、映射更松;多标签同义时后写覆盖前写
Emby大体兼容 Kodi 方言;公开完整 tag reference 不如 Kodi / Jellyfin 清晰
Plex NFO Agent官方支持电影 / 剧集的 Kodi 子集;不指望全字段、不指望 Music / extras
设备端海报墙(弱解析)常按标签名粗解析;跨层级复用 title 等高频词风险最大

工程含义:

  • 内部可以有最大字段模型(数据库 / 探针 / 人物 / 技术轨都齐全)。
  • 默认写出的 XML 必须是保守子集,以「最多软件稳定读」为成功标准。
  • 不要为每个客户端单独做一套产品 profile(kodi / plex / zidoo…);验证目标可以很多,产品层导出版本宜收敛。

为什么还要单独做 NFO 模块

NFO 不是「序列化一下数据库对象」的附属功能,而是媒体库规范化链路里的一环:

扫描(物理事实)
  → 刮削(逻辑身份与元数据骨架)
  → 身份稳定
  → NFO 落盘(磁盘层主权与生态交接)
  →(并行)重命名 / 资产规范化

它主要解决三件事:

  1. 数据主权:元数据不只活在私有库里,磁盘上有可迁移的结构化结果。
  2. 下游兼容:Kodi、Emby、Jellyfin、设备端海报墙等可以直接接手。
  3. 识别完成落到物理层:库内身份与目录旁 sidecar 对齐,迁移与重建库才可复现。

扫描负责「文件在哪、是什么容器」;刮削负责「是哪部作品」;NFO 负责「把已稳定身份按约定广播到磁盘」。三者不要互相顶替。

核心原则:全集、默认可写、用户状态拆开

三个易混概念

概念回答什么默认策略
字段全集 / 内部模型系统「知道」什么尽量全,服务 UI / 播放 / 管理
默认可互操作导出别人「读得稳」什么白名单子集,少即是多
用户状态谁看过、看到哪默认不写进 NFO

用户状态包括 playcountwatchedlastplayedresumeuserrating 等。多用户系统更不应把某一用户进度写成全局 sidecar。需要迁移观看历史时,用独立开关或独立通道,不要绑在「标准元数据导出」上。

推荐产品形态:standard + extended

实践中比「五个客户端 profile」更可维护的是 两个导出档位

档位目标默认是否写 fileinfo典型读者
standard最小可互操作Kodi / Jellyfin / Emby / Plex 子集 / 弱解析设备
extended信息完整、语义不污染是(完整技术轨)本系统读回、高级整理工具、需要探针事实的场景

共同约束(两个档位都要满足):

  • 合法 XML:UTF-8、单根节点、禁止手工字符串拼接。
  • 根节点与类型匹配:movie / tvshow / season / episodedetails
  • 空值默认省略,不输出空标签(除非语义明确需要空值)。
  • 同一文档最多一个 uniqueid default="true"ratings 内最多一个默认评分。
  • 日期 YYYY-MM-DDruntime 用分钟整数;时长秒用 durationinseconds

standard 不是「字段少所以低级」,而是「字段少所以能被最多软件稳定读」。extended 不是「随便堆字段」,而是「完整但标签语义明确,不污染主标题 / 主评分路径」。

不建议继续拆 ZidooSafePlexSafeKodiStrict 等第三、第四档:弱解析与子集消费端应作为 standard 的验证目标,而不是无限增生 profile。

根节点、命名与剧集层级

四类文档角色

类型根节点常见文件名角色
电影movie优先 <VideoFileName>.nfo;一片一夹才考虑 movie.nfo单部电影元数据
剧集根tvshow固定 tvshow.nfo(剧集根目录)父级身份与剧级元数据
seasonseason.nfo季级补充;不能当季信息唯一来源
单集episodedetails与视频同名 .nfo单集事实

Kodi 对剧集的经典模型是:1 × tvshow.nfo + N × episode.nfoseason.nfo 在 Jellyfin / Emby 侧更常见,Plex 可有可无;Kodi 更强调在 tvshow.nfo 里用 namedseason / seasonplot 表达季名与季简介。因此:

  • 可以写 season.nfo,但季名 / 季简介 / 季海报应 镜像进 tvshow.nfo
  • 单集 standard 不要重复堆叠剧级字段(country / studio / tag / languages / 剧级 premiered 等);需要时用 showtitleseasonepisodeaired、本集 plot 与本集 uniqueid 即可。

电影命名建议

  • 默认文件级 <VideoFileName>.nfo:多版本、同目录多文件、刷新优先级都更稳(也与 Kodi 推荐一致)。
  • 目录级 movie.nfo:仅当确认一片一夹且目录语义清晰时再开。
  • 扩展版若写入 fileinfo、片源、edition 等 文件事实,更应坚持文件级 NFO:文件事实不是电影条目事实。

多集同文件

这是生态冲突点,必须产品化处理,而不是 silently 生成「多个顶层 episodedetails」:

  • 严格 well-formed 单根 XML 与部分历史 / 子集客户端习惯冲突。
  • 新基线(如 Kodi v22 方向)倾向分集命名的独立 NFO / artwork。
  • 若短期不支持多集同文件,应明确拒绝或提示,而不是输出非法多根文档。

字段分层:写什么、不写什么

下面用「原则 + 示例」代替完整 allowlist 表;实现侧仍建议按文档类型维护路径白名单,用 linter 卡住回归。

身份:uniqueid 优先

<uniqueid type="tmdb" default="true">12345</uniqueid>
<uniqueid type="imdb" default="false">tt1234567</uniqueid>

原则:

  • 有 provider ID 时,uniqueidcore;默认 ID 全库策略要稳定(电影常优先 TMDB,剧集跟主刮削源)。
  • tmdbid / imdbid / tvdbid 等 alias 适合 extended 或宽松服务端;standard 不必双写,避免重复语义。
  • 手写遗留 id 不进入 standard;需要时再作子集兼容。

Plex 一类客户端会基于 ID 构建稳定 GUID,从而影响重扫后观看历史是否保留——这是 standard 必须把 ID 写对的原因,而不是因为要堆技术字段。

文本与评分:避免同义竞争

优先慎用 / 档位控制原因
titleplotname / localtitle / review 同写宽松解析器后写覆盖前写
ratings 块 + 单一 default同时再写根级 rating(standard)双通道竞争
mpaa同时写 certification(standard)同义分级
premiered / 单集 aired过度依赖 year日期主字段更稳;year 可作兼容派生

standard 简介以 plot 为主;outline 仅在有真正短简介且与 plot 不同时再写。评分保留 1~3 个主流源足够,不要把所有源塞进默认可互操作输出。

演职员与图片

  • directorcredits(Kodi 侧 writer)、actorname / role / order / thumb)是跨端最稳的人员结构。
  • writer 可作 extended / Emby-Jellyfin 兼容补充,standard 不必与 credits 双写。
  • 图片优先 本地 sidecar 相对路径poster.jpgfanart.jpg<file>-thumb.jpg)。受保护 API URL、短期签名 URL 对离线扫描与设备端几乎无用,standard 应省略。

技术流 fileinfo:收益与风险不对称

Kodi 文档里 streamdetails 核心其实很克制:视频 codec / aspect / width / height / durationinseconds / stereomode / hdrtype;音频 codec / language / channels;字幕 language

判断说明
standard 默认不写 fileinfo对「识别是哪部片」收益低;对弱解析风险高
播放器本可自探测技术信息不必靠 NFO 才能播
extended 再写完整探针服务读回、高级整理、减少部分客户端重新分析

扩展写入时的硬约束:

  1. 禁止audio / subtitle 下写 <title>。弱解析器若全局找 title,会把「英语字幕」盖成集标题 / 电影标题。流标题用 tracktitle 或扩展容器。
  2. language 走 ISO 639-2 三字母engzho…);BCP-47(zh-Hanten-US)放 languagetag
  3. hdrtype 只允许空 / hdr10 / dolbyvision / hlg。不要写 sdr;HDR10+ 在标准路径降级为 hdr10,细节放 hdrformat 等扩展字段。动态范围分类本身见轨 / HDR 系列文,NFO 只消费已归一化结果。
  4. codec 名称要归一化到消费端可识别集合,不要直接落 MediaInfo 原始商业长串。

锁定字段与「保护本地元数据」

lockdata / lockedfields用户策略,不是作品元数据。仅在用户明确要求「下游不要覆盖本地 NFO」时,在 Emby / Jellyfin 友好输出中写入;不要默认打开。

电影 vs 剧集:导出范围两维度

产品上常混在一起的是「处理哪些文件」与「生成哪些层级」:

维度回答例子
应用范围(apply)本次命中哪些版本 / 集当前集 → 整季 → 整剧;电影单版本 → 全版本
导出层级(export)为命中对象写哪些 NFO只要 episode;或 episode + season + tvshow

两者不能合成一个开关:用户可能「按整季展开处理,但只生成单集 NFO」。预览(会写到哪、是否冲突、是否覆盖)应是正式能力,而不是事后看磁盘。

剧集目录形态示意:

Show Folder/
  tvshow.nfo
  poster.jpg
  fanart.jpg
  Season 01/
    season.nfo          # 可选补充
    Show.S01E01.mkv
    Show.S01E01.nfo
    Show.S01E01-thumb.jpg

高风险模式清单(验收向)

实现或审查导出结果时,可按下面清单做回归,比对照「是否字段最全」更有用:

风险症状 / 后果防护
流标题用 <title>设备端标题变成「繁中字幕」extended 改 tracktitle;standard 不写流块
同义多标签简介 / 分级被后写覆盖standard 每语义单通道
多个 default ID / ratingGUID 与评分抖动全局唯一 default
单集堆剧级字段弱端误用父字段episode allowlist 收紧
远程受保护图片 URL离线与第三方扫库失败本地 sidecar
用户状态进 NFO污染观看历史默认关闭
多根 episodedetails非 well-formed分集 NFO 或明确不支持
语言码混用轨语言识别失败三字母 + 可选 languagetag
HDR 枚举自创hdrtype=sdr / hdr10plus标准枚举 + 扩展详述

建议至少保留一组 golden 样例:电影 standard/extended、tvshow / season / episode 各一档,覆盖多音轨多字幕、HDR10 / DV / HDR10+、BCP-47 字幕语言,并用弱解析心智检查「全文是否只有主对象语义标题路径」。

和扫描、探针事实的边界

NFO 消费的是 已经稳定的逻辑元数据 +(扩展档)已归一化的技术事实,不是替代扫描:

  • 轨清单、语言启发式、HDR / DV 分层探针 → 扫描与轨事实层(见相关阅读)。
  • NFO 扩展档的 fileinfo 应引用归一化结果,而不是在写盘瞬间再发明一套动态范围文案。
  • 扫描器目录拓扑决定「写到哪」;没有可靠物理入口就没有可靠 sidecar 路径。

后续扫描器专题(目录策略、增量、图谱 sidecar 归属等)可与本文的「落盘层级」对照阅读;本文不展开扫描实现。

注意事项

  1. 本文是 字段标准与导出策略,不是某一版本 writer 的契约或完整 XML 模板全集。
  2. 消费端行为随版本变化;Plex NFO Agent、Kodi 多集策略、设备固件解析都应以目标版本复测为准。
  3. 「字段多」不等于「兼容好」;弱解析与后写覆盖会让超集输出比子集更危险。
  4. 内网样例路径、真实片库 NFO 全文、私有签名 URL 不进入对外文档。

相关阅读

同系列还可对照:

来源

本文综合改写自 Octans 项目研究文档中的 NFO 导出字段标准与扩展标准结论,并参考 NFO 模块架构中的导出范围模型(应用范围与导出层级分离)。字段表与客户端差异已收敛为通用互操作原则;具体 tag 覆盖率以目标消费端版本复测为准。