feat: 新增repack_3mf脚本,解压目录重新打包为3MF文件
This commit is contained in:
@@ -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())
|
||||
Reference in New Issue
Block a user