|
#!/usr/bin/env python3 |
|
|
|
from __future__ import annotations |
|
import argparse |
|
import re |
|
import shutil |
|
import struct |
|
from pathlib import Path |
|
|
|
|
|
def is_apng(file_path: Path) -> bool: |
|
"""检测PNG文件是否为APNG(动画PNG)。""" |
|
try: |
|
with open(file_path, "rb") as f: |
|
# PNG signature |
|
sig = f.read(8) |
|
if sig != b"\x89PNG\r\n\x1a\n": |
|
return False |
|
|
|
# 检查是否存在 acTL chunk(APNG的标记) |
|
while True: |
|
length_bytes = f.read(4) |
|
if not length_bytes: |
|
break |
|
length = struct.unpack(">I", length_bytes)[0] |
|
chunk_type = f.read(4) |
|
if chunk_type == b"acTL": |
|
return True |
|
if chunk_type == b"IEND": |
|
return False |
|
f.seek(length + 4, 1) # skip chunk data and CRC |
|
except Exception: |
|
return False |
|
return False |
|
|
|
|
|
def parse_ranges(text: str) -> list[int]: |
|
""" |
|
解析类似: 1-3,8,10-12 |
|
返回去重并排序后的数字列表。 |
|
""" |
|
result = set() |
|
for part in re.split(r"[,\s]+", text.strip()): |
|
if not part: |
|
continue |
|
if "-" in part: |
|
a, b = part.split("-", 1) |
|
start, end = int(a), int(b) |
|
if start > end: |
|
start, end = end, start |
|
result.update(range(start, end + 1)) |
|
else: |
|
result.add(int(part)) |
|
return sorted(result) |
|
|
|
|
|
def main() -> None: |
|
parser = argparse.ArgumentParser( |
|
description="从指定数字文件夹的 apng/*.png 收集到一个输出目录。" |
|
) |
|
parser.add_argument( |
|
"--ranges", |
|
required=True, |
|
help='数字范围,如 "1-3,8,10-12"(数字可不连续)', |
|
) |
|
parser.add_argument( |
|
"--src", |
|
default=".", |
|
help="源根目录(包含数字文件夹),默认当前目录", |
|
) |
|
parser.add_argument( |
|
"--out", |
|
default="./_export_png", |
|
help="输出目录,默认 ./_export_png", |
|
) |
|
args = parser.parse_args() |
|
|
|
src_root = Path(args.src).expanduser().resolve() |
|
out_dir = Path(args.out).expanduser().resolve() |
|
out_dir.mkdir(parents=True, exist_ok=True) |
|
|
|
folder_nums = parse_ranges(args.ranges) |
|
copied_apng = 0 |
|
copied_png = 0 |
|
missing_dirs = [] |
|
|
|
for n in folder_nums: |
|
folder = src_root / str(n) |
|
apng_dir = folder / "apng" |
|
|
|
if not apng_dir.is_dir(): |
|
missing_dirs.append(str(n)) |
|
continue |
|
|
|
for png_file in sorted(apng_dir.glob("*.png")): |
|
# 检测是否为APNG,用扩展名区分 |
|
if is_apng(png_file): |
|
target = out_dir / png_file.name.replace(".png", ".apng") |
|
copied_apng += 1 |
|
else: |
|
target = out_dir / png_file.name |
|
copied_png += 1 |
|
|
|
shutil.copy2(png_file, target) |
|
|
|
print(f"源目录: {src_root}") |
|
print(f"输出目录: {out_dir}") |
|
print(f"目标数字文件夹数: {len(folder_nums)}") |
|
print(f"成功复制 APNG 数: {copied_apng}") |
|
print(f"成功复制静态 PNG 数: {copied_png}") |
|
|
|
if missing_dirs: |
|
print(f"\n以下目录不存在或无 apng 子目录:{', '.join(missing_dirs)}") |
|
|
|
|
|
if __name__ == "__main__": |
|
main() |