comicmeta
A CLI for the controlled ComicVine metadata pipeline for comic archives. Review
matches, then write ComicInfo.xml into your CBZ files — the metadata format
Kavita, Jellyfin, and Komga read for library organization.
Every step before write is read-only. write requires an explicit
reviewed mapping and creates verified backups. CBZ is the only writeable
format; CBR is reported but never modified. The interactive experience is
designed for macOS Terminal, with local, external-drive, and NAS libraries
using the same commands.
Pure Python stdlib — zero production runtime dependencies. Works on Python 3.11+.
Install
pip install comicmeta
Or via Homebrew:
brew tap CripWal/comicmeta
brew install comicmeta
Or Docker (for NAS / headless batch operations — see Docker):
docker build -t comicmeta .
Quick start
cd /path/to/comics
comicmeta
Running comicmeta with no arguments opens an interactive dashboard
navigable with arrow keys. It guides you through health, browse, review,
organize, and write. For a direct safe preview:
comicmeta health --source /path/to/comics
comicmeta organize --source /path/to/comics --dry-run
Organize offers an explicit apply step after showing its proposed changes.
Ctrl-C quits cleanly.
Interactive walkthrough
Animated SVG recordings of the terminal UI (open in a browser to play — CSS animations, no video):
Dashboard — arrow-key navigation
Browse — expand the library tree and open an issue card
Browse an issue — cover art preview
Volume review — scroll ComicVine candidates and accept
Review flow — CBR warning, convert picker, volume review
Settings panel
The pipeline
Review-then-write. The read-only phases produce a reviewed mapping; one
mutating phase writes ComicInfo.xml into CBZ archives.
discover → comicvine-candidates.json
review-volumes → comicvine-review-state.json + review.md
fetch-issues → comicvine-issue-candidates.json
review-issues → comicvine-issue-review-state.json + issue-review.md
map → comic-metadata-reviewed-mapping.json
+ comicmeta-kavita-export.json
──────────────────────────────────────────────────────────────────
write → ComicInfo.xml inside CBZs + write-report.json
Each phase is resumable — state persists between runs. Re-running discover
skips files that already have complete metadata. write refuses to replace any
existing ComicInfo.xml automatically; those files require a separately
approved replacement flow.
Mac vs NAS
If your comic library lives on a local drive (external HDD/SSD, internal storage), run comicmeta directly — it's fast.
If your library lives on a NAS (TrueNAS, unRAID, Synology), create a context
once and select it from the dashboard or with --context. You can launch
comicmeta from any working directory; the selected context supplies the
correct remote library path:
comicmeta context add nas --host nas.example.local --ssh-user comics \
--library-path /path/on/nas/comics
comicmeta context use nas
comicmeta --context nas health
comicmeta --context nas organize
For a mounted share or external drive, use its path directly. The path does not need to be the directory where the command is launched:
comicmeta health --source /Volumes/media/comics
cd /Volumes/T7-Storage/comics
comicmeta organize
For large libraries accessed over SMB, running I/O-heavy commands on the NAS
or through an rsync context avoids network latency and unreliable large-file
renames:
| Command | Where | Why |
|---|---|---|
| Metadata review and mapping | Either | Uses JSON state and reports; run it wherever your working state is available |
| Archive inspection, health, and discovery | Near the library | Reads CBZ/CBR archives, so local or NAS execution avoids unnecessary network traffic |
| Write and organize | On the library’s host | Mutates files and folders; use the configured context or run locally on the mounted drive |
Contexts replace the need for a hand-maintained NAS wrapper: they store the remote host, library path, and state path in ComicMeta and work regardless of the directory from which you launch the command.
Docker
For NAS users who prefer a container over SSH:
# docker-compose.yaml is included in the repo
docker compose run --rm comicmeta inspect --quick --source /comics
# Or stand-alone:
docker run --rm -v /path/to/comics:/comics -v comicmeta-data:/data \
comicmeta discover --source /comics
# Write (with API key):
COMICVINE_API_KEY=... docker compose run --rm comicmeta \
write --yes --source /comics \
--mapping /data/config/libraries/<hash>/comic-metadata-reviewed-mapping.json \
--backup-dir /data/backups \
--report /data/config/libraries/<hash>/write-report.json
The Docker container runs headless batch commands only (discover, inspect, write, health, etc.). Interactive review stays on your workstation against synced state files.
Full command reference
# Query ComicVine and write a candidate report (read-only)
COMICVINE_API_KEY=... comicmeta discover --source /path/to/comics --report candidates.json
# Interactively review volume candidates (read-only)
comicmeta review-volumes --report candidates.json --state review-state.json \
--summary review.md
# Fetch issue-level ComicVine data for selected volumes (read-only)
COMICVINE_API_KEY=... comicmeta fetch-issues --selections review-state.json \
--report issue-candidates.json
# Interactively review issue-level candidates (read-only)
comicmeta review-issues --report issue-candidates.json \
--state issue-review-state.json --summary issue-review.md
# Generate a CBZ-only writer mapping from completed review state
comicmeta map --candidates issue-candidates.json --review issue-review-state.json \
--output reviewed-mapping.json
# Copy reviewed CBZ files into an empty staging root (read-only)
comicmeta stage --source /path/to/comics --destination /tmp/staging \
--mapping reviewed-mapping.json --report stage-report.json
# Validate staged ComicInfo writes against production state (read-only)
comicmeta validate --source /tmp/staging --production /path/to/comics \
--backup-dir /tmp/backup --mapping reviewed-mapping.json \
--copy-report stage-report.json --write-report write-report.json
# Write reviewed ComicInfo.xml into production CBZ archives
comicmeta write --source /path/to/comics --mapping reviewed-mapping.json \
--expected-hashes stage-report.json --backup-dir /tmp/backup \
--report write-report.json
Other commands
comicmeta status [--json] # one-glance view of context, library, pipeline state
comicmeta inspect [--quick] # list library + per-file ComicInfo status
comicmeta browse # interactive tree view with covers + flags
comicmeta health [--deep] # scan for corrupt archives, missing metadata
comicmeta convert # CBR → CBZ via bsdtar (needs bsdtar on PATH)
comicmeta organize # preview and report safe organization changes
comicmeta organize --execute # apply folder/file normalization to Series (Year) #NNN
comicmeta flags # list/clear research-flagged series + issues
comicmeta missing # report ComicVine issues absent from your library
comicmeta backups [--delete] # list/clean stored write backups
comicmeta self-test # smoke-test the environment + config
comicmeta settings [--init] # show/scaffold/edit comicmeta.toml
comicmeta help [command] # show help for a command (alias for --help)
comicmeta completion zsh|bash # generate a shell completion script
For full-color cover previews in macOS Terminal, comicmeta offers to install
the optional `timg` renderer with Homebrew on first run. No software is
installed on the library drive. Cover previews can be enabled or disabled
later from Settings. Browse supports flagging/unflagging from the issue view,
named alternate-cover selection, and a gallery for a series.
Global flags: --context NAME (run against a NAS context), --debug (show a
full traceback on unexpected errors), --no-input (never prompt; fail instead).
The dashboard runs organize as a dry-run first, then offers an explicit apply
step. It can infer missing starting years and series names from numbered archive
filenames, including collected editions named like 01 (of 5) (1993).
For rsync NAS contexts, the current comicmeta source is synchronized
automatically before remote commands run; context edit NAME --sync remains
available for an explicit manual sync.
comicmeta status answers "where am I?" — the active context, library size, and
which pipeline phases have data, with a suggested next command. Errors link to
the GitHub issue tracker so bugs are easy to report.
Settings
Configuration lives in comicmeta.toml in the library directory (or
~/.config/comicmeta/comicmeta.toml). Precedence: CLI flags > environment
variables > settings file > built-in defaults.
comicmeta settings # show resolved settings
comicmeta settings --init # scaffold comicmeta.toml in the current directory
comicmeta settings --set api.request_delay=0.5
comicmeta settings --set review.high_confidence_score=80
| Section | Keys |
|---|---|
paths |
source, candidates, volume_state, volume_summary, policy, issue_candidates, issue_state, issue_summary, mapping, kavita_export, backup_dir, write_report |
api |
key_env, key_file, request_delay, timeout, user_agent, candidate_limit |
review |
active_source, blocked_queries, high_confidence_score, high_confidence_margin, continue_to_write |
write |
enforce_expected_hashes, auto_confirm |
appearance |
color, dashboard, theme, cover_previews |
The ComicVine API key is read from an environment variable (default
COMICVINE_API_KEY) or a file path (api.key_file in settings). The key is
never persisted by comicmeta or written to logs.
Running comicmeta settings opens a centered interactive panel. Appearance
controls and compact connection summaries are shown first; press [a] for API,
paths, review, and write-safety settings. Press Enter on a connection to open
its SSH settings. The selected row is preserved while searching, expanding
connections, and changing settings:
| Context field | Default | Meaning |
|---|---|---|
host |
— | NAS hostname or IP |
ssh_user |
— | SSH username |
ssh_port |
22 |
SSH port |
identity_file |
(default keys) | SSH identity/private-key path |
connect_timeout |
10 |
SSH connect timeout (seconds) |
library_path |
— | comic library path on the NAS |
exec |
rsync |
rsync (source + NAS Python) or docker |
Set them per-context: comicmeta context edit nas --ssh-port 2222 --identity-file ~/.ssh/id_ed25519.
Exit codes
0 success; non-zero on failure. The write command refuses path traversal,
existing ComicInfo.xml, missing required identity fields, backup
collisions, and unsupported archive writes. A failure at file N rolls back
only file N from its verified backup; successfully written files earlier in
the run are kept, the failed file is logged, and the batch continues. Re-running
comicmeta write skips files that already have ComicInfo.
Development
The user guide is available in the GitHub wiki-ready pages. The evidence-backed contributor map is in docs/codebase/.
pip install -e .
python -m pytest tests/ stress/
python -m build --sdist
GitHub Actions runs the test suite on Python 3.11 through 3.14 for pushes and pull requests.
Run python -m comicmeta --help for the full command list.
License
MIT
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file comicmeta-1.0.1.tar.gz.
File metadata
- Download URL: comicmeta-1.0.1.tar.gz
- Upload date:
- Size: 113.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.14.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1bddd6417c27ba54627f190b28e0ea664473f401b80233623bae541acee3729e
|
|
| MD5 |
3d572d764922cf988e0947df474e523f
|
|
| BLAKE2b-256 |
38be0730efed7828a2c50c809101dd2e1e418a083e1f7401177b2b677221cda6
|
File details
Details for the file comicmeta-1.0.1-py3-none-any.whl.
File metadata
- Download URL: comicmeta-1.0.1-py3-none-any.whl
- Upload date:
- Size: 134.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.14.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c08ea78a5c12433f3b73a8b07957dd9e506a8be078f3817458f71b6bdde3c6c0
|
|
| MD5 |
b65ac6e2111827d995b05257deaf0697
|
|
| BLAKE2b-256 |
6480a72a3468deadccc3ea2265ef96096f0b4928013684a0827e4fd969a52dc1
|