103 lines
3.1 KiB
Python
103 lines
3.1 KiB
Python
import argparse
|
|
import mimetypes
|
|
from pathlib import Path
|
|
|
|
from cista.preview import process_image_with_timing
|
|
|
|
|
|
def parse_args() -> argparse.Namespace:
|
|
parser = argparse.ArgumentParser(
|
|
description="Generate image previews for all files in a folder, one at a time.",
|
|
)
|
|
parser.add_argument("folder", type=Path, help="Folder to scan recursively")
|
|
parser.add_argument(
|
|
"--px",
|
|
type=int,
|
|
default=1024,
|
|
help="Maximum preview dimension in pixels (default: 1024)",
|
|
)
|
|
parser.add_argument(
|
|
"--quality",
|
|
type=int,
|
|
default=60,
|
|
help="AVIF quality passed to preview generation (default: 60)",
|
|
)
|
|
return parser.parse_args()
|
|
|
|
|
|
def is_image_file(path: Path) -> bool:
|
|
mime_type, _ = mimetypes.guess_type(path.name)
|
|
return bool(mime_type and mime_type.startswith("image/"))
|
|
|
|
|
|
def main() -> int:
|
|
args = parse_args()
|
|
folder = args.folder.resolve()
|
|
if not folder.is_dir():
|
|
raise SystemExit(f"Not a directory: {folder}")
|
|
|
|
files = sorted(
|
|
path for path in folder.rglob("*") if path.is_file() and is_image_file(path)
|
|
)
|
|
if not files:
|
|
print(f"No image files found under {folder}")
|
|
return 0
|
|
|
|
total_files = 0
|
|
total_bytes = 0
|
|
total_timing_slots: list[float] = []
|
|
total_preview_ms: float = 0.0
|
|
failures = 0
|
|
|
|
print(f"Scanning {folder}")
|
|
print(f"Generating previews for {len(files)} image files")
|
|
|
|
for path in files:
|
|
total_files += 1
|
|
rel = path.relative_to(folder)
|
|
try:
|
|
preview, timing = process_image_with_timing(
|
|
path,
|
|
maxsize=args.px,
|
|
quality=args.quality,
|
|
)
|
|
except Exception as exc:
|
|
failures += 1
|
|
print(f"FAIL {rel} error={exc}")
|
|
continue
|
|
|
|
total_bytes += len(preview)
|
|
timings = timing.timings or []
|
|
if len(total_timing_slots) < len(timings):
|
|
total_timing_slots.extend([0.0] * (len(timings) - len(total_timing_slots)))
|
|
for i, value in enumerate(timings):
|
|
total_timing_slots[i] += value
|
|
total_ms = sum(timings)
|
|
total_preview_ms += total_ms
|
|
|
|
detail = " / ".join(f"{value:.1f}ms" for value in timings)
|
|
if detail:
|
|
detail = f"timings={detail} total={total_ms:.1f}ms"
|
|
else:
|
|
detail = f"total={total_ms:.1f}ms"
|
|
print(f"OK {rel} backend={timing.backend} bytes={len(preview)} {detail}")
|
|
|
|
completed = total_files - failures
|
|
print()
|
|
print("Summary")
|
|
print(f" files={total_files}")
|
|
print(f" completed={completed}")
|
|
print(f" failed={failures}")
|
|
print(f" preview_bytes={total_bytes}")
|
|
if completed:
|
|
if total_timing_slots:
|
|
for i, value in enumerate(total_timing_slots, start=1):
|
|
print(f" timing{i}_total_ms={value:.1f}")
|
|
print(f" preview_total_ms={total_preview_ms:.1f}")
|
|
print(f" preview_avg_ms={total_preview_ms / completed:.1f}")
|
|
return 0 if failures == 0 else 1
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|