科技 · 实用指南
在 darktable 中把 JPEG 评级复制给不同文件夹的 RAW
保留按日期整理的 RAW 文件夹及其 Jpgs 子文件夹。先检查执行计划,再把明确记录的星级写入已有的 darktable 旁车文件,最后将更改读回图库。

给 JPEG 打好星,可以让大型照片库更容易整理。麻烦在于,对应的 RAW 往往放在上一级文件夹:照片已经选好了,真正要后期处理的文件却还没有这些筛选结果。把评级带过去,不应以重新整理多年的文件夹为前提。
本指南提供适用于这种目录布局的完整辅助脚本。它在每个日期文件夹内配对,只更新已有 RAW 旁车中的一个评级字段,并备份原旁车。之后还需要让 darktable 读回更改,整个流程才算完成。先从一个较小的日期文件夹开始,逐对检查并确认结果,再继续处理其余照片。
1. 配对范围限定在各自的日期文件夹内
支持的布局是:RAW 直接放在日期文件夹中,对应 JPEG 直接放在该文件夹的 Jpgs 子文件夹中。例如,Photos/2024/2024-05/Jpgs/DSC01234.JPG 与 Photos/2024/2024-05/DSC01234.ARW 配对。实际接收评级的文件是 Photos/2024/2024-05/DSC01234.ARW.xmp。
这两个图像文件共有的主文件名是 DSC01234,拼写和大小写都要一致。另一个月份文件夹中的 DSC01234.ARW 属于另一项候选,脚本不会用它补上当前缺失的配对。脚本会遍历你选定的照片目录树,但每一对都受这个父子目录关系约束。它不能判断两份文件是否来自同一次拍摄;你需要确认文件命名和目录安排确实能对应起来。
默认 RAW 扩展名为 ARW,JPEG 子文件夹名为 Jpgs。可用 --raw-ext 指定其他 RAW 扩展名,用 --jpeg-subdir 指定另一个精确的子文件夹名称。如果多个 JPEG 共用主文件名、同时匹配多种 RAW 格式、存在大小写歧义,或发现可能属于编号编辑版本的旁车,脚本会阻止处理并留待检查。Jpgs 内继续嵌套的文件夹不在这条配对规则内。
目标旁车必须已经含有 darktable 元数据。脚本不会创建一个空文件来代替缺失的编辑历史,也不会猜测 Lightroom 风格的 DSC01234.xmp 是否才是目标。darktable 导入时可以读取某些其他命名方式的旁车,但其原生旁车会保留输入图像的扩展名。这次转移应使用实际存在的原生文件。
2. 先确定 JPEG 评级以哪里为准
在 darktable 中做任何准备之前,先确定想要的评级目前存放在哪里。常见情况有三种,准备方式各不相同,虽然脚本只提供两种需要明确指定的来源模式。
如果评级内嵌在原始 JPEG 中,使用 --rating-source jpeg,并保持这些 JPEG 不变。不要仅为本指南准备来源,就把它们导入 darktable 或生成 JPEG 旁车。脚本读取的是其中明确记录的 XMP-xmp:Rating;它不会假定其他应用显示的评级都已写入这个内嵌字段。
如果所需评级目前保存在 darktable 数据库的 JPEG 条目中,在 lighttable 选中相关 JPEG,然后使用 history stack > write sidecar files。这会把当前信息写入 Jpgs/DSC01234.JPG.xmp 这样的原生旁车。关闭 darktable 后,选择 --rating-source sidecar。
如果已有的 JPEG 旁车已经包含所需评级,就保留这份文件并以它为准。不要为了照做上一段的步骤,而用较旧的 darktable 数据库内容覆盖它。旁车模式要求同一文件夹中存在由完整 JPEG 文件名加 .xmp 组成的精确名称;脚本不会寻找省略了 JPEG 扩展名的同主文件名旁车。
内嵌评级和旁车评级都可读取时,计划会同时列出两者。若不一致,警告会指出差异,并以你明确指定的来源模式为准。脚本不会比较修改时间来替你决定。如果选定来源没有评级,即使另一来源有值,这一对仍会跳过。应选择正确模式并重新生成计划,不要把信息缺失当成零星。
3. 确认当前 RAW 旁车,再关闭 darktable
确认每个目标旁车都包含你真正想保留的当前 RAW 编辑状态。如果这些编辑保存在 darktable 数据库中,在 lighttable 选中本批照片,再使用 history stack > write sidecar files;这一步应先于任何对旧旁车的重新导入。如果 RAW 旁车本身已经包含所需的较新编辑,就保留它来生成计划,不要从较旧的数据库覆盖它;执行后再按 XMP 到数据库的方向读回。
对于确实尚未加入图库的 RAW,使用 import > add to library 从其现有位置加入,随后确认原生旁车已经写出。JPEG 元数据只按所选来源分支的需要准备。转移之前,备份重要的图库数据和旁车;脚本之后创建的备份只覆盖它实际更改的 RAW 旁车。
退出前,打开 preferences > storage > XMP sidecar files,启用 look for updated XMP files on startup。在这套流程中,将 create XMP files 设为 on import,不要设为 never。after edit 策略不能确保每个尚未处理的 RAW 都已拥有旁车。
关闭 darktable,并在生成计划、检查和执行期间保持关闭。也暂停其他可能重写这些文件的应用,让准备好的状态保持稳定:图像加入 darktable 后,数据库内容可能优先生效,将外部对 XMP 的更改覆盖掉。

4. 准备 Python、RDFLib 和 ExifTool
需要 Python 3.10 或更高版本、RDFLib 7.6.0,以及 ExifTool 可执行文件。为脚本和计划文件新建一个可写文件夹。示例使用 python3;在 Windows 上,如果你的 Python 命令是 py -3,后续就统一使用它。
把 RDFLib 安装到实际运行脚本的同一个 Python 环境中。虚拟环境便于将这些依赖与其他项目分开。如果使用虚拟环境,请把下文所有命令中的 python3 替换为该环境的 Python 可执行文件。
python3 -m pip install rdflib==7.6.0
python3 -c "import sys,rdflib; print(sys.version); print(rdflib.__version__)"
从 ExifTool 官方发行渠道获取程序,并按其安装说明操作。在 Windows 上,为命令行使用,将 exiftool(-k).exe 改名为 exiftool.exe,并把随附的 exiftool_files 文件夹保留在旁边。若在 macOS 或 Linux 上使用便携的 Perl 发行版,则需将 lib 目录与 exiftool 脚本放在同一位置,并确保 Perl 可用。
如果 ExifTool 已在 PATH 中,脚本可通过 exiftool 找到它。否则,用 --exiftool 指定可执行文件路径。Windows 示例为 --exiftool "C:/Tools/ExifTool/exiftool.exe";macOS 或 Linux 则填写实际的可执行文件路径。这个参数接受文件路径,不接受 "perl exiftool" 这样的 shell 命令。
生成计划和执行计划时,必须使用同一可执行文件及版本。如果期间更新或移动了 ExifTool,请重新生成计划,使记录的运行环境与实际一致。
5. 保存完整脚本并检查代码
下面用一个紧凑的单行代码包提供完整脚本。把整段复制到工作文件夹内名为 unpack_rating_helper.py 的纯文本文件中。保留为一个逻辑行;编辑器仅在显示时自动折行没有关系。不要加上引号、Markdown 代码围栏或周围的文章文字。
解包程序离线工作:它解压内含的源码、校验 SHA-256,然后以只允许新建的方式创建 sync_jpeg_ratings.py,供你检查。它不会运行辅助脚本,也不会下载任何内容。如果同名文件已存在,它会拒绝覆盖。
import base64,zlib,hashlib,sys; from pathlib import Path; data=zlib.decompress(base64.b64decode('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')); f=Path("sync_jpeg_ratings.py").open("xb") if hashlib.sha256(data).hexdigest()=='ba1136a7f667973bf1fef54682971bf964f4518825c99d45955ed871fdb88158' else sys.exit("SHA-256 mismatch; no helper file written"); f.write(data); f.close(); print("Created sync_jpeg_ratings.py for inspection; it has not been run.")
python3 unpack_rating_helper.py
使用前,先在文本编辑器中打开生成的 sync_jpeg_ratings.py。还原后的文件为 34,478 字节,SHA-256 为 ba1136a7f667973bf1fef54682971bf964f4518825c99d45955ed871fdb88158。如果哈希不匹配,就不会写出脚本文件;应重新复制原始代码段,不要删掉校验步骤。
可读的脚本只会在已有 RAW 旁车的临时副本中更改 XMP-xmp:Rating。它用 RDFLib 比较其余全部 RDF 元数据,因此语义等价的 XML 写法可以接受,编辑数据被改动则不能通过。它不会写入图像像素、访问 darktable 数据库,也不会复制 JPEG 的整块 XMP。
6. 生成计划并逐对检查
先从一个较小的日期文件夹,或选定年份的目录树开始。将命令中的照片和 ExifTool 路径换成自己的路径。默认操作只会写出一个新的 JSON 计划文件,照片和旁车都保持不变。
python3 sync_jpeg_ratings.py "/path/to/Photos/2024" --rating-source sidecar --manifest "ratings-plan.json" --exiftool "/path/to/exiftool"
如果要读取 JPEG 内嵌评级,把 sidecar 改为 jpeg。需要时,可添加 --jpeg-subdir "Jpgs" 或 --raw-ext ARW,并填入适合自己的值。在 Windows 上,完整的等效命令可以这样写:
py -3 sync_jpeg_ratings.py "D:/Photos/2024" --rating-source sidecar --manifest "ratings-plan.json" --exiftool "C:/Tools/ExifTool/exiftool.exe"
打开 ratings-plan.json。先查看 counts,再逐一检查每个 ready 条目的 jpeg、raw、raw_sidecar、rating_source、target_rating 和 new_rating。确认路径、预期的旧值到新值变化,以及所有关于内嵌评级与旁车不一致的警告。试运行成功,并不表示每张照片都已准备好执行。
可执行的配对才会进入执行阶段。值相同的配对已经拥有所需评级,会保持不变。跳过的配对是因为选定来源没有明确评级。被阻止的配对需要处理,原因可能是旁车缺失、评级未知、文件名冲突、疑似其他编辑版本,或另一个不支持的情况。解决实际问题后,再生成新的计划文件。
计划记录文件标识、修改时间、完整文件的哈希值,以及相关相邻文件的名称。即使不编辑像素,计算大型 RAW 的哈希仍需要时间,因此应选择规模可控的目录树。空计划可能只是选定文件夹中没有符合条件的 Jpgs 子文件夹,并不能证明照片库中没有已评级的 JPEG。
7. 分清零星、已拒绝和已有评级冲突
来源评级必须是 -1 到 5 之间的整数。零是真实的零星评级,-1 是真实的拒绝标记。字段缺失与这两者都不同。选定来源缺失评级时,脚本会保留 RAW 的原评级;无法读取、小数、超出范围或存在歧义的评级都会被阻止。
默认情况下,有效来源可以填入缺失或零星的目标评级;已有值相同则不作更改。如果目标已有 1–5 星或 -1,而来源值与其不同,就会受到保护,包括来源打算把它清为零星的情况。darktable 文档给出的初始导入评级默认值为 1,所以刚导入的 RAW 也可能触发这项保护。
如果确实要替换这些已有值,先检查受影响的配对,再用 --replace-rated 生成一份单独的计划。这个设置会作用于新计划中所有符合条件的评级冲突,因此应选取足够小的目录树,不要把它当成只勾选某一行的开关。
python3 sync_jpeg_ratings.py "/path/to/Photos/2024" --rating-source sidecar --replace-rated --manifest "ratings-replace-plan.json" --exiftool "/path/to/exiftool"
执行前再次检查新计划。不要手动编辑 JSON 中的值或状态来强行执行。校验和用于发现已审核计划的意外变更;执行阶段还会核对配对、来源、冲突策略和当前文件。允许替换的选项不会使无效评级或有歧义的文件配对变得可接受。
8. 执行已确认的计划并保留回执
保持 darktable 关闭,执行刚刚检查过的那一份计划。只有当你确认的新替换计划就是 ratings-replace-plan.json 时,才在此处使用该文件。ExifTool 路径继续沿用之前的设置。
python3 sync_jpeg_ratings.py --apply "ratings-plan.json" --exiftool "/path/to/exiftool"
每次写入之前,脚本都会重新读取选定评级,核实计划中的文件和目录清单。它在计划文件旁创建名称唯一的 rating-sync-backup-<plan-id> 文件夹,将原 RAW 旁车保存在其中,并在 sidecars 内保留目标的相对目录路径。receipt.json 会记录每个目标、对应备份、旧评级、新评级和哈希值。
ExifTool 在暂存旁车中更改那个明确指定的评级字段。随后,脚本核对评级和除评级外的完整 RDF 数据,再次检查输入状态,最后替换目标文件。提交后,它会验证旁车,并确认 RAW、JPEG 和任何已记录的 JPEG 旁车仍符合各自的文件指纹。XML 排版或工具包标识可能变化,但备份保留原始字节。
文件发生变化、相关文件名清单变化,或计划文件被修改,都会让已过时的操作停止。如果一批处理到中途报错,之前已完成的配对仍然完成,后续配对则不会尝试。查看回执中的 result_counts 和 unattempted_ready_pairs。这是逐个文件提交的过程,不是整批全部成功或全部撤销的事务。
留意 committed_verification_failed 和 commit_state_requires_inspection。出现这些状态时,必须把当前文件与记录的哈希进行核对;替换完成后报错,并不代表什么都没改。中断后,如果最后保存的条目状态是 prepared,也需要检查。将计划、回执和备份放在一起保留。
唯一命名的备份目录会阻止盲目重复执行同一计划。处理好停止的批次后,为剩余工作重新生成计划。不要删除之前的备份目录来绕过这项保护。即使回执显示执行完成,darktable_reload 仍会标记为等待处理。
9. 让 darktable 读回旁车并确认结果
启动 darktable。在发现旁车更新的对话框中,选择本次要处理的 RAW 条目,并点击 keep the XMP edit,即把 XMP 内容读入数据库的操作。不要选 keep the database edit,那会把旧数据库状态写回旁车。这些按钮文字已在当前官方源码中核对过,具体版本或语言的措辞可能不同。
如果没有列出预期条目,不要据此认定转移失败或成功。启动检查受设置和时间戳影响。可使用 lighttable 中的 import > add to library 作为后备方式:找到原 RAW 文件夹,取消勾选 select only new images,并明确选中本批已有的 RAW 文件。该复选框控制的是初始选择,取消勾选不能代替实际选择目标文件。完成添加操作后,它们的 XMP 数据会重新读入。
在原位置使用这些文件。copy & import 走的是另一种导入流程,对已有旁车的处理方式不同。无需从图库移除或删除图像。refresh exif 会重读源图像,可能覆盖评级;把 JPEG 旁车作为 RAW 的历史堆栈加载,则可能替换编辑。这些都不是本次转移所需的读回步骤。
检查界面中 RAW 的星级、已拒绝状态和原有编辑,也检查你依赖的自定义顺序。自定义排序位置不会写入 XMP,因此仅保留旁车,无法保证重建所有只存在于数据库中的细节。如果这些状态很重要,就保留数据库备份。
若要在继续编辑前撤销某一项已确认的转移,先关闭 darktable,在回执中找到那一对准确的 raw_sidecar 与 backup。用 backup_sha256 校验备份,再用 new_sidecar_sha256 核对当前旁车。下面的只读命令会输出文件的 SHA-256;依次换成每个文件的实际路径:
python3 -c "import hashlib,sys; print(hashlib.sha256(open(sys.argv[1],'rb').read()).hexdigest())" "/full/path/to/sidecar.xmp"
如果当前目标仍与记录中的转移后版本一致,就用文件管理器把这一份原始备份复制回那个精确对应的 RAW 旁车,随后让 darktable 读入恢复后的旁车并验证。如果当前文件在执行后又发生了变化,应先另行保存它并检查这些后续编辑,再决定如何恢复。不要动无关目标。确认本批的评级和编辑状态后,再继续下一个日期文件夹。
验证范围:提供的脚本使用 Python 3.12、RDFLib 7.6.0,以及 13.59.3 供应方发行包中的 ExifTool 13.59,通过了 27 个完整 CLI 测试案例。测试使用生成的 JPEG、含编辑历史的合成 darktable 风格 XMP,以及不解析内容的 RAW 占位文件,核对了受控评级变更、图像字节与其他元数据不变、备份、冲突、过时输入、部分完成和明确选定单个目标后的恢复。解包程序还通过了四种还原测试;Python 开启优化时哈希校验仍然有效,已有的输出文件也不会被覆盖。这些结果不包含 darktable GUI/数据库读回、真实相机 RAW 解码或跨平台执行。界面操作说明依据官方 5.6 手册和当前源码核对,最终结果仍需在自己的图库中确认。