"""检测 recordings/ 目录下内容完全相同(字节级重复)的录像文件。 根因:automate.py 的 ADB 自动化下载在滚动/点击时序上有已知问题,会把同一张 缩略图卡片的下载重复触发,导致同一段录像被存成多份文件名不同的拷贝。 先按文件大小分组(成本低,能筛掉绝大多数内容不同的文件),只对同大小的文件 再计算 MD5 确认是否真的内容相同,避免对全部文件逐个算 MD5 的开销。 只生成报告,不删除任何文件、不改动数据库。 """ from collections import defaultdict from analysis.config import PROJECT_ROOT, RECORDINGS_DIR from analysis.hashing import compute_md5 REPORT_PATH = PROJECT_ROOT / "dedup_report.txt" def find_duplicates(): """返回 {md5: [文件路径, ...]},只包含重复数≥2的分组。""" by_size = defaultdict(list) for path in RECORDINGS_DIR.rglob("*.mp4"): by_size[path.stat().st_size].append(path) by_md5 = defaultdict(list) for size, paths in by_size.items(): if len(paths) < 2: continue for path in paths: by_md5[compute_md5(path)].append(path) return {md5: paths for md5, paths in by_md5.items() if len(paths) > 1} def build_report(): dup_groups = find_duplicates() lines = [] total_extra_files = 0 total_wasted_bytes = 0 for md5, paths in sorted(dup_groups.items(), key=lambda kv: -len(kv[1])): paths = sorted(paths, key=lambda p: p.name) size = paths[0].stat().st_size extra = len(paths) - 1 total_extra_files += extra total_wasted_bytes += extra * size lines.append(f"[{md5}] {len(paths)} 份重复,单份 {size / 1024:.1f} KB") for p in paths: lines.append(f" {p.relative_to(PROJECT_ROOT)}") summary = ( f"重复分组数:{len(dup_groups)}\n" f"多余拷贝数(可释放的文件数):{total_extra_files}\n" f"可释放空间:{total_wasted_bytes / 1024 / 1024:.1f} MB\n" ) return summary + "\n" + "\n".join(lines) if __name__ == "__main__": print("===== 开始检测重复视频 =====") report = build_report() REPORT_PATH.write_text(report, encoding="utf-8") print(report.split("\n\n")[0]) print(f"→ 详细清单已写入 {REPORT_PATH.relative_to(PROJECT_ROOT)}") print("===== 检测完成 =====")