Skip to content

Instantly share code, notes, and snippets.

@benoit74
Created August 21, 2026 15:12
Show Gist options
  • Select an option

  • Save benoit74/6c8f5cce216f3922b2a2c49d69a1324e to your computer and use it in GitHub Desktop.

Select an option

Save benoit74/6c8f5cce216f3922b2a2c49d69a1324e to your computer and use it in GitHub Desktop.
#!/usr/bin/env python3
"""List media entries in a single local ZIM file.
Prints a summary of media counts grouped by mimetype on the console,
and writes the full list of media entries (path, mimetype, size) to
a CSV file.
Usage:
./zim_media_list_local.py <path-to-zim> [--output FILE]
"""
import argparse
import csv
import os
import sys
from collections import defaultdict
from zimscraperlib.zim.archive import Archive
MEDIA_MIME_PREFIXES = ("image/", "audio/", "video/")
def check_local_file(path):
if not os.path.isfile(path):
return False, f"file not found: {path}"
if not os.access(path, os.R_OK):
return False, f"file not readable: {path}"
size = os.path.getsize(path)
return True, f"File found locally ({size:,} bytes)"
def collect_media_entries(archive):
"""List of (path, mimetype, size) for every non-redirect media entry."""
entries = []
for entry_id in range(archive.all_entry_count):
entry = archive.get_entry_by_id(entry_id)
if entry.is_redirect:
continue
item = entry.get_item()
mimetype = item.mimetype
if not mimetype.startswith(MEDIA_MIME_PREFIXES):
continue
entries.append((entry.path, mimetype, item.size))
return entries
def print_summary(entries):
counts = defaultdict(int)
sizes = defaultdict(int)
for _path, mimetype, size in entries:
counts[mimetype] += 1
sizes[mimetype] += size
print(f"\nTotal media entries: {len(entries):,}")
print(f"{'mimetype':<60} {'count':>10} {'total size':>15}")
for mimetype in sorted(counts, key=lambda m: -counts[m]):
print(f"{mimetype:<60} {counts[mimetype]:>10,} {sizes[mimetype]:>15,}")
def write_csv(output_path, entries):
with open(output_path, "w", newline="") as f:
writer = csv.writer(f)
writer.writerow(["path", "mimetype", "size"])
for path, mimetype, size in sorted(entries):
writer.writerow([path, mimetype, size])
print(f"\nFull media list written to {output_path}")
def default_output_path(zim_path):
stem = os.path.splitext(os.path.basename(zim_path))[0]
return f"{stem}_media.csv"
def parse_args():
parser = argparse.ArgumentParser(
description="List media entries (by mimetype) found in a local ZIM file."
)
parser.add_argument("zim_path", help="Local path to the ZIM file")
parser.add_argument(
"--output",
metavar="FILE",
help="CSV output path (default: <zim-name>_media.csv in the current directory)",
)
return parser.parse_args()
def main():
args = parse_args()
ok, msg = check_local_file(args.zim_path)
print(f"ZIM: {args.zim_path}")
print(f" {'OK ' if ok else 'FAIL'} - {msg}")
if not ok:
print(f"\nError: {msg}", file=sys.stderr)
return 1
output_path = args.output or default_output_path(args.zim_path)
print("\nScanning entries (this can take a little while)...")
with Archive(args.zim_path) as archive:
native_media_count = archive.media_count
entries = collect_media_entries(archive)
print(f"\nNative media_count: {native_media_count:,}")
print_summary(entries)
write_csv(output_path, entries)
return 0
if __name__ == "__main__":
sys.exit(main())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment