dsh-duplicate-file-check
jediliu005/dsh-duplicate-file-check
File dedupe and unify toolkit, shown as "小视频整理" in the DSH sidebar: exact matches by head/middle/tail sampling or full byte compare, near-duplicate MP4s grouped by duration and a configurable first-N-seconds frame window (default 10s, 1–5 fps, default 2; longer videos are compared on their first N seconds too), batch rename or move by extension, UUID, file size or video length, empty-folder cleanup under the selected sources, and MOV/AVI/WMV detected by file header and transcoded to MP4 keeping resolution, frame rate and audio parameters. Requires ffmpeg and ffprobe on PATH. Concurrent workers per task with a shared setting; one task at a time; per-group help dialogs; local-only API. Every task that moves files has one-click restore: per-group/target _dupfc_restore.json records, deleted files skipped and counted, files outside the record renamed to UUID and moved into resetFail; orphaned ffmpeg processes left by a crashed host are swept on plugin load.
설치
dsh plugin --profile web add github:jediliu005/dsh-duplicate-file-checkREADME
dsh-duplicate-file-check
DSH(DeepSeek Harness)的重复文件 / 视频整理插件(面板与侧边栏显示为「小视频整理」):查重、抓帧比对、批量改名、按大小或时长重命名并移动、把 MOV/AVI/WMV 统一转成 MP4。
面板按任务分组折叠,每个任务都有独立的开始按钮、进度和状态;同一时刻只允许一个任务在跑。
九个任务
| 任务 | 做什么 | 处理对象 |
|---|---|---|
| 完全一致文件校验 | 先按大小分组,再抽样(头/中/尾各 100KB)或逐字节全量比对,列出可删除的多余副本 | 来源目录里的全部文件 |
| 抓帧疑似重复校验_时长匹配 | 先排除完全一致的文件,按时长分组,比对前 N 秒画面相似度(旧算法,完全保留) | 来源目录里的 MP4 |
| 抓帧疑似重复校验_忽略片头 | 灰度结构 + dHash/pHash 指纹跨时长匹配,支持片头错位(独立模块与独立配置) | 来源目录里的 MP4 |
| 复位文件 | 按记录文件(分组目录或目标目录根部的 _dupfc_restore.json)把文件搬回原目录;已删除跳过、原目录不存在报错;记录外的文件 UUID 改名移入 resetFail | 两个抓帧模块 + 按大小/按时长/统一编码 |
| 批量改后缀 | 只改扩展名(如 dat→mp4),不动内容,同名目标自动跳过 | 来源目录里的指定后缀文件 |
| 随机文件名 | 改名为 UUID,后缀不变 | 来源目录里的指定后缀文件 |
| 按文件大小重命名并移动 | 从小到大排序,重命名为 100000 起的序号并移动到目标目录 | 来源目录里的指定后缀文件 |
| 按视频长度重命名并移动 | 按时长从短到长排序,重命名为 100000 起的序号并移动到目标目录 | 来源目录里的指定后缀视频 |
| 删除空文件夹 | 递归检查子目录,里面没有任何文件就删除(先深后浅,父目录变空也会删) | 来源目录下的所有子目录 |
| 统一编码 | 按文件头识别 MOV/AVI/WMV,移动到对比目录并转成同名 MP4 | 来源目录里的 MOV/AVI/WMV |
完全一致校验
- 先按文件大小分组,只处理「同大小、数量 > 1」的组。
- 快速模式取头/中/尾各 100KB 比对;严格模式逐字节全量比对(慢但绝对可靠)。
- 「删除完全一致文件」每组保留最早的一个,其余删除,不可撤销。
抓帧疑似重复
- 仅处理 MP4;先排除完全一致的文件,再按时长分组(容差可配,默认 100ms)。
- 抓帧时长可配置:默认 10 秒、上限 20 秒;抓帧频率默认每秒 2 帧(1–5 可调);长视频同样只比对前 N 秒,不再跳过。
- 片头容差可配置:默认 10 秒、上限 10 秒(且不超过抓帧时长−5);比对时在 ±该值 内搜索最佳偏移,共享片头不会被误判为重复。
- 「移动疑似重复」把检测出的疑似重复文件整组移到输出目录(2 个及以上算一组,不做取舍),按
1xxxxx.mp4递增重命名,方便人工确认。
按大小 / 按时长重命名并移动
- 排序依据:文件大小,或视频真实时长(ffprobe 读取)。
- 序号从 100000 起,目标目录已有编号时自动续号,不覆盖。
- 目标目录不能等于或在来源目录内部,否则直接拒绝——避免下次运行把结果再搬一遍。
- 搬移:同盘 rename,跨盘复制+删除;并发执行(见下)。
统一编码(MOV / AVI / WMV → MP4)
- 按文件头判定,不看扩展名:MOV 认
ftyp+qt,AVI 认RIFF....AVI,WMV 认 ASF 头。改错后缀的文件也能认出来;本来就是 MP4 的跳过。 - 命中后先移动到「对比目录」,再转成同名 MP4(
clip.avi→clip.mp4);同名自动加(1)、(2),绝不覆盖。 - 源文件本来就叫
.mp4时,原件进对比目录\original\,避免转码结果把原件覆盖掉。 - 转码参数对齐原片:分辨率、帧率、视频码率(含 maxrate/bufsize)、音频取样率、声道数、音频码率;视频 H.264 + yuv420p,音频 AAC,
+faststart。 - 保留原文件默认勾选。取消勾选时,只有转码成功且校验通过的原件才会删除。
- 智能直通(默认开):源本来就是 H.264 + AAC 且非 HDR → 只换容器,秒级完成、零画质损失。
- 编码器:x264(默认)/ NVIDIA NVENC / Intel QSV / AMD AMF,运行时探测;硬件编码器失败会自动退回 x264 重试一次。
- HDR:强制 8bit(默认)/ 色调映射到 SDR / 保留 HDR(HEVC)。色调映射需要 zscale 可用,会先实测,不可用时降级并写日志。
并发
面板最上方「来源目录」里的「并发数」对所有任务生效,0 表示自动:
| 任务 | 取值 | 说明 |
|---|---|---|
| 完全一致校验 | 配置值(自动 2–8) | 并行读文件头/中/尾或全量比对 |
| 抓帧疑似重复 | 配置值(自动 2–8) | 并行抽帧比对 |
| 按大小 / 按时长 | 读大小/时长 ≤16,搬移 ≤8 | 读时长是瓶颈;搬移同盘 rename、跨盘复制+删除 |
| 统一编码 | 扫文件头 ≤16,转码 ≤4 | 转码受 NVENC 并发会话数与 CPU 限制 |
并发下的安全保证:命名用内存占位,两个 worker 不会抢同一个文件名;原件与成品分开存放;失败互不影响。
来源目录与目标目录
- 不存在的来源目录会在读取/保存配置时自动剔除,面板不会留下僵尸条目。
- 来源目录可以互相嵌套或重复配置,同一个文件只处理一次(自动去重并记日志)。
- 目标目录不存在会自动创建(只要上级目录存在)。
- 目标目录不能等于或在来源目录内部。
失败与停止
- 失败:原件移到
对比目录\fail\,日志与结果里逐条写明原因;权限类错误会提示检查 ffmpeg/ffprobe 所在目录权限。 - 停止:结束在跑的子进程,未完成的文件放回来源目录并记为「停止」,不计入失败;已完成的转换不回滚。
边界处理
- 可变帧率:
avg_frame_rate与r_frame_rate差值 <1% 才按恒定帧率处理,否则-fps_mode passthrough。 - 码率缺失:用
体积×8/时长 − 音频码率估算;估不出用 CRF 18。 - 多音轨与元数据:
-map 0:a?保留全部音轨,-map_metadata 0保留元数据。 - 旋转:读 Display Matrix / rotate 标签,90°/270° 交换宽高,竖拍不压扁。
- 空间预检:按总大小 ×1.2 估算,目标盘不够提前报错。
- 输出校验:先写
<名字>.mp4.part,校验非空、可解析、时长误差 ≤ max(2s, 2%),通过后才改名;失败清理半成品。 - 盘符根来源:来源是
D:\时,目标在其内部同样会被拒绝。
安装
dsh plugin install dsh-duplicate-file-check
或在 DSH 配置文件的 bundles 里加入本包。依赖:ffmpeg / ffprobe(统一编码、抓帧任务需要)。
许可
MIT
删除空文件夹
- 递归扫描来源目录下的每个子目录;某个目录里没有任何文件(也不含还有文件的子目录)就删掉它。
- 先删深层再删父目录,
a/b删掉后a变空也会被删。 - 来源目录本身不删;符号链接与联接点(junction)不进入、不删除。
- 结果统计:扫描 N 个子目录、删除 M 个、失败 K 个(被占用或无权限的会列出原因)。
抓帧疑似重复校验_忽略片头
- 独立模块:自己的抓帧时长(默认 10、上限 20 秒)、片头容差(默认 10 秒、上限 10 秒)、输出目录;并发数用「来源目录」里的公共设置。
- 比对方式:16×16 灰度 MAD + 64 位 dHash + 63 位 pHash,跨时长匹配、不受时长分组限制;共享片头不会被误判。
- 已知限制:只看前 N 秒(默认 10、最多 20 秒);片头最多对齐 10 秒;完全一致的副本交给「完全一致文件校验」,本模块不移动。
- 对齐判定:只认重叠最大的对齐位置(重叠相同时优先不偏移),避免靠滑动偏移把差异帧挤出比较范围。
- 命中整组文件可一键移动:在输出目录下为每个相似组新建子文件夹(重复组_001、重复组_002…),组内保留原文件名。
- 复位:五个会移动文件的功能(时长匹配、忽略片头、按大小、按时长、统一编码)都有「复位文件」。移动时写
_dupfc_restore.json记录(抓帧两模块写在各分组目录内,其余写在目标目录根部);复位按记录把文件搬回各自原目录,已被删除的跳过并计数,原目录不存在会报错、文件留在原处;分组目录/统一编码对比目录里不在记录中的文件会统一 UUID 改名移到该目录下的resetFail。 - 可调参数(界面上都有,默认值即推荐值):
- 抓帧时长(秒):只看每个视频开头多少秒,默认 10、最大 20;长视频也只取开头。
- 抓帧频率(帧/秒):每秒抓几帧来比较,默认 2,范围 1–5。
- 片头容差(秒):允许片头错开多少秒,默认 10、最多 10;0 = 必须对齐。
- 单帧灰度差上限:两帧缩成 16×16 黑白小图后亮度差的平均值上限,默认 15,越小越严格。
- dHash 差异上限:明暗结构指纹的差异位数上限,默认 4,越小越严格。
- pHash 差异上限:整体轮廓指纹的差异位数上限,默认 8,要和 dHash 同时达标。
- 整组吻合率(%):对齐后至少多少比例的帧像,默认 90%,调低更容易成组也更易误判。
- 连续吻合率(%):还要连续像多少比例,默认 85%,防止零散几帧碰巧相似就拼成组。