diff --git a/repack_3mf.py b/repack_3mf.py new file mode 100644 index 0000000..6a6eb84 --- /dev/null +++ b/repack_3mf.py @@ -0,0 +1,90 @@ +"""将解压后的 3MF 文件夹重新打包为 .3mf 文件的工具脚本。 + +用法: + python repack_3mf.py "解压目录" "output.3mf" + +会自动更新 Metadata/plate_N.gcode.md5 并确保 [Content_Types].xml 为 ZIP 第一条目。 + +如果省略输出路径,自动在解压目录同级生成同名 .3mf。 +""" + +from __future__ import annotations + +import hashlib +import os +import re +import sys +import zipfile +from pathlib import Path + +GCODE_MEMBER_RE = re.compile(r"^Metadata[\\/]plate_(\d+)\.gcode$") + + +def update_md5(src_dir: Path) -> None: + """更新所有 plate_N.gcode 对应的 .md5 文件。""" + metadata = src_dir / "Metadata" + if not metadata.is_dir(): + return + for gcode_file in sorted(metadata.glob("plate_*.gcode")): + if not GCODE_MEMBER_RE.match(str(gcode_file.relative_to(src_dir)).replace("\\", "/")): + continue + h = hashlib.md5() + with open(gcode_file, "rb") as f: + h.update(f.read()) + md5_path = Path(str(gcode_file) + ".md5") + md5_path.write_text(h.hexdigest(), encoding="ascii") + print(f" MD5 已更新: {md5_path.name} → {h.hexdigest()}") + + +def repack(src_dir: Path, output: Path) -> None: + """将解压目录重新打包为 3MF。""" + ct_path = src_dir / "[Content_Types].xml" + if not ct_path.exists(): + raise FileNotFoundError(f"未找到 [Content_Types].xml,请确认 {src_dir} 是有效的 3MF 解压目录") + + print(f"源目录: {src_dir}") + update_md5(src_dir) + + with zipfile.ZipFile(output, "w", zipfile.ZIP_DEFLATED) as z: + z.write(ct_path, "[Content_Types].xml") + + count = 1 + for root, _dirs, files in os.walk(str(src_dir)): + for fn in files: + fp = Path(root) / fn + arcname = fp.relative_to(src_dir).as_posix() + if arcname == "[Content_Types].xml": + continue + z.write(str(fp), arcname) + count += 1 + + size_mb = output.stat().st_size / (1024 * 1024) + print(f"输出文件: {output} ({size_mb:.1f} MB, {count} 条目)") + print("打包完成。") + + +def main() -> int: + if len(sys.argv) < 2: + print(__doc__) + return 1 + + src = Path(sys.argv[1]) + if not src.is_dir(): + print(f"错误: 目录不存在 — {src}") + return 1 + + if len(sys.argv) >= 3: + output = Path(sys.argv[2]) + else: + output = src.parent / (src.name.rstrip("/\\") + ".3mf") + + try: + repack(src, output) + except Exception as exc: + print(f"错误: {exc}") + return 1 + return 0 + + +if __name__ == "__main__": + raise SystemExit(main())