Immich XMP Sidecar 完全指南:元数据读取、写回机制与 Sidecar 队列管理
2026/9/13 9:10:13 网站建设 项目流程

Immich XMP Sidecar 完全指南:元数据读取、写回机制与 Sidecar 队列管理

【免费下载链接】immichHigh performance self-hosted photo and video management solution.项目地址: https://gitcode.com/GitHub_Trending/im/immich

Immich 通过 XMP Sidecar(旁车文件)机制,在不改动原始媒体文件的前提下,与 Lightroom、Darktable、digiKam 等专业工具共享元数据:上传时从.xmp文件导入描述、评分、时间、位置和标签,之后在 Web UI 中修改这些字段时再写回.xmp文件。本文基于官方文档与服务器端源码,完整讲解 Sidecar 的字段映射规则、文件命名约定、内外部库两条工作流,以及 Sidecar 队列在管理后台的运行原理。

什么是 XMP Sidecar

XMP 是一种以 XML 格式存储元数据的标准。Sidecar 是与媒体文件同名、以.xmp结尾的外部元数据文件,例如IMG_0001.jpg.xmp。与直接写入文件内嵌 EXIF 不同,Sidecar 方式完全不修改原始文件——这正是 Lightroom 等工具默认采用的"非破坏性编辑"模式。

Immich 在两个环节使用 Sidecar:

  • 元数据提取任务(Metadata Extraction job):读取并导入.xmp文件中的元数据到数据库;
  • Sidecar Write 任务:将数据库中的元数据写回.xmp文件。

文档还指出一个重要的互操作性:Lightroom、Darktable、digiKam 等应用都可以配置为把修改写入.xmp文件而非原图,这样 Immich 与这些工具就能通过同一份 Sidecar 文件交换元数据,而原始媒体文件始终保持原样。

支持的元数据字段:读取与写入映射

Immich 并不支持所有 XMP 字段。文档给出了明确的"读/写"字段映射表,并且强调:写操作不会替换整个文件内容,而是与文件中已有字段合并(merge)

元数据Immich 写入 XMP 的字段Immich 从 XMP 读取的字段
Descriptiondc:descriptiontiff:ImageDescriptiondc:descriptiontiff:ImageDescription
Ratingxmp:Ratingxmp:Rating
DateTimeexif:DateTimeOriginalphotoshop:DateCreated按优先级依次尝试:exif:SubSecDateTimeOriginalexif:DateTimeOriginalxmp:SubSecCreateDatexmp:CreateDatexmp:CreationDatexmp:MediaCreateDatexmp:SubSecMediaCreateDatexmp:DateTimeCreated
Locationexif:GPSLatitudeexif:GPSLongitudeexif:GPSLatitudeexif:GPSLongitude
TagsdigiKam:TagsList按优先级依次尝试:digiKam:TagsListlr:HierarchicalSubjectIPTC:Keywords

映射表之外的字段(如CreatorSource、IPTC 其他字段、Lightroom 的编辑记录等)会原样保留.xmp文件中,但不会被 Immich 索引、不可搜索

源码印证:读取合并与写回实现

读取阶段的实现在 metadata.service.ts 的getExifTags方法中(约 L565-L622):它对媒体文件本身和 Sidecar 文件分别调用readTags,然后按{ ...mediaTags, ...videoResult?.tags, ...sidecarTags }的顺序合并——Sidecar 标签覆盖媒体文件内嵌标签。其中日期字段有专门处理:如果 Sidecar 中解析出有效日期(firstDateTime,按EXIF_DATE_TAGS优先级列表匹配),会先把媒体文件的所有日期类标签删除,再让 Sidecar 的日期生效,从而保证"Sidecar 优先"的语义。

写回实现在handleSidecarWrite(约 L475-L527):它从资产的exifInfo中取出descriptiondateTimeOriginallatitudelongituderatingtags,组装为DescriptionImageDescriptionDateTimeOriginalGPSLatitudeGPSLongitudeRatingTagsList等标签对象,最后调用 metadata.repository.ts 的writeTags(L136-L146)。

这里有一个关键细节解释了"写入是合并而非替换":writeTags内部给每个标签名追加^前缀(如Description^)后再交给 exiftool-vendored 写入,对应 exiftool 的"追加/合并"写模式,因此.xmp文件中已有但 Immich 不认识的字段不会被清掉。

此外,写回时机由事件驱动:handleTagAsset/handleUntagAsset(L465-L473)监听AssetTag/AssetUntag事件并自动把SidecarWrite任务投入队列。也就是说,在 Web UI 中编辑描述、评分或更新标签后,Immich 会自动排队一次 Sidecar Write,与文档中"自动排队 Sidecar Write"的提示一致。

文件命名规则

Sidecar 必须与媒体文件共享基础名。文档给出的规则:

  • IMG_0001.jpg.xmp(推荐)
  • IMG_0001.xmp(兜底)
  • myphoto_meta.xmp(不会被识别)

IMG_0001.jpg.xmpIMG_0001.xmp同时存在,Immich 使用.jpg.xmp版本。

这段规则在源码中有精确对应。metadata.service.ts 的getSidecarCandidates(L529-L547)按顺序生成候选路径:

  1. 已关联的sidecarFile.path(数据库中已记录的 Sidecar 路径,如存在);
  2. ${originalPath}.xmp—— 即IMG_123.jpg.xmp
  3. ${join(assetPath.dir, assetPath.name)}.xmp—— 即IMG_123.xmp

SidecarCheck任务(handleSidecarCheck,L424-L463)逐个用storageRepository.checkFileExists(candidate, constants.R_OK)探测候选文件是否可读,取第一个命中的路径;若与数据库已记录路径不同,则通过assetRepository.upsertFile({ type: AssetFileType.Sidecar, path })更新关联,否则删除旧关联。这解释了"优先.jpg.xmp"——候选列表的顺序即优先级。

上传工作流:Detect → Copy → Extract → Write-back

对于内部库(用户上传的资产),Sidecar 经历四个阶段:

  1. Detect(检测)——上传时 Immich 检查每个媒体文件旁边是否放置了.xmp文件。上传接口的字段映射在 asset-media.service.ts 中定义:UploadFieldName.SIDECAR_DATA对应扩展名.xmp(约 L72、L97),即客户端可以随媒体文件同时提交一个 Sidecar 字段。
  2. Copy(复制)——媒体文件和 Sidecar 一起被复制进 Immich 内部库目录。Sidecar 会按内部存储模板重命名,与媒体文件保持同目录同名关系,例如:
    upload/library/<user>/YYYY/YYYY-MM-DD/IMG_0001.jpg upload/library/<user>/YYYY/YYYY-MM-DD/IMG_0001.jpg.xmp

    存储模板迁移时 Sidecar 的跟随重命名逻辑见 storage-template.service.ts(约 L253,newPath:${newPath}.xmp``)。

  3. Extract(提取)——从 Sidecar 中解析选定的元数据(标题、描述、日期、评分、标签)并存入数据库,即上文getExifTags的合并逻辑。
  4. Write-back(写回)——之后如果在 Web UI 中更新标签、评分或描述,Immich 会同时更新数据库和被复制的.xmp文件,保持两边同步。

外部库(挂载文件夹)工作流

对外部库(外部库通过IMMICH_EXTERNAL_LIBRARY挂载已有文件夹,见 外部库文档),流程有所不同:

  1. Detect——外部库的DISCOVER任务自动把挂载目录中与既有媒体文件相邻的.xmp文件建立关联,不移动、不重命名任何文件
  2. Extract——从 Sidecar 读取并保存与上文相同的一组元数据字段;
  3. Write-back——只要 Immich 对该挂载点有写权限,之后对评分、标签等元数据的编辑也会写回磁盘上的原始.xmp文件。

:::danger 注意:只读挂载的陷阱

如果挂载是只读的,Immich 既无法更新 Sidecar 也无法更新数据库——元数据编辑会静默失败且不产生任何警告。官方文档将这一已知问题链接到上游 issue #10538。生产部署中若 Sidecar 写入不生效,应优先检查挂载点的读写权限。 :::

Sidecar 管理队列(Admin Jobs)

Immich 提供两个管理员任务来管理 Sidecar:

任务作用
DISCOVER找到媒体文件旁边新出现的.xmp文件(媒体尚未关联任何 Sidecar 时建立关联)
SYNC重新读取已有的.xmp文件并刷新数据库中的元数据(例如外部工具在 Immich 之外编辑过 Sidecar 之后)

从源码结构看,这两个入口对应服务器端Sidecar队列:enum.ts 中定义了QueueName.Sidecar以及JobName.SidecarQueueAllJobName.SidecarCheckJobName.SidecarWrite(约 L906-L908)。管理端触发时,queue.service.ts 的runCommandQueueName.Sidecar投递SidecarQueueAll任务(L220-L221),后者流式取出待处理资产,为每个资产排队一个SidecarCheck任务——即上文的"检测/发现"环节;而元数据的重新读取则由元数据提取任务完成(可用force参数强制重新执行)。

Web 端在管理后台的"队列"与"任务设置"页面中暴露了该队列:queue.service.ts(L181-L185)将其映射为带 XML 图标的队列项,JobSettings.svelte 中可调整 Sidecar 任务的并发配置。

实践要点小结

  • 让 Lightroom/Darktable/digiKam 以 Sidecar 模式工作,Immich 即可与其共享描述、评分、日期、坐标、标签五类可搜索元数据;
  • Sidecar 命名务必使用媒体文件名.xmp(首选带原始扩展名的.jpg.xmp),其他命名不会被识别;
  • 写回是字段级合并:Immich 不会破坏 Sidecar 中其他工具留下的字段(如 Lightroom 编辑记录);
  • 外部库场景下确保挂载点可写,否则元数据编辑会静默失败;
  • 外部工具离线修改 Sidecar 后,通过管理后台触发SYNC让数据库刷新到最新状态。

【免费下载链接】immichHigh performance self-hosted photo and video management solution.项目地址: https://gitcode.com/GitHub_Trending/im/immich

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询