Skip to content

mate2/cbzit

Repository files navigation

Cbz It

CI npm license: MIT

Turn folders of manga/comic page images into well-named .cbz archives — smart scan, dry-run report, confirm, then build. Your originals are never touched.

Why

You've got a folder of scanned/downloaded manga or comic pages with an unpredictable structure — loose images, per-volume folders, chapter folders with the volume number buried in the name, mixed languages, a stray .txt file here and there. You want clean, well-named .cbz files out the other end, without hand-renaming hundreds of pages.

cbzit scans the folder, guesses the structure (series, volumes, chapters, pages, language, titles), shows you a dry-run report of exactly what it would build, lets you tweak a couple of things, and only writes once you confirm.

Install / quickstart

No install needed — run it with npx:

npx cbzit ./my-folder
npx cbzit ./my-folder -o ./out -y   # flags after the package name pass straight through

Or install it globally:

npm install -g cbzit
cbzit ./my-folder

Running from a clone

npm ci
node bin/cbzit.js ./my-folder -o ./out     # direct — flags just work
npx . ./my-folder -o ./out                 # the local package's bin, no gotcha
npm run start -- ./my-folder -o ./out      # via npm — note the "--"

Heads-up on the second form: without the -- separator, npm run swallows flags itself (npm run start ./my-folder -o ./out silently loses the -o). cbzit fails loudly on leftover stray arguments instead of writing to the wrong place, but the -- is what makes the flags reach it at all.

How it works

  1. Scancbzit reads the folder tree (names and file sizes only, never image contents) and finds the "leaf" folders: the ones that directly contain image files. That's the only unambiguous unit; everything above it (series, volumes) is inferred by grouping.
  2. Report (dry-run) — before touching anything, it prints exactly what it would produce: how many .cbz files, their names, and page counts.
  3. Confirm / refine — you're asked a couple of quick questions (series name, volume label, language) with sensible defaults already pre-filled from what was detected; the language question proposes the detected value by default, but you can pick fr/en, type a custom tag, or drop it entirely. Answer them or just accept the defaults. Multi-series folders skip the series name prompt and keep each series' detected name — per-series renaming is a planned flag, not an interactive option yet.
  4. Build — once confirmed, it reads the images, renames pages in reading order, and zips everything into .cbz files under an output folder. Nothing outside that folder is ever written.

Sample report

Plan: 3 archive(s) → ./cbzit-output/
  • Cobra Tome 01.cbz  (24 pages)
  • Cobra Tome 02.cbz  (22 pages)
  • Cobra Tome 03.cbz  (20 pages)

If there are non-image files lying around (notes, .txt, thumbnails db, etc.), they're listed as ignored rather than causing an error:

Ignored non-image files (2):
  - Cobra/notes.txt
  - Cobra/Thumbs.db

Anything genuinely ambiguous (e.g. mixed folder depths) shows up as a warning in the same report, so you can decide instead of getting a silent wrong guess.

cbzit also handles more than a single flat series folder: point it at a root containing several series subfolders (e.g. Root/{One Piece,Naruto,Bleach}) and each series is grouped and archived separately, as well as deeper trees like Series/Volume/Chapter. Ambiguous or mixed-depth structures still produce a plan (best guess applied) plus a warning rather than an error. Duplicate chapter numbers within the same volume are never dropped and never merged into one continuous stream: each duplicate is kept as its own, distinctly-labelled chapter — the first stays Ch001, the second becomes Ch001-2, the third Ch001-3, and so on (sort-safe: Ch001 < Ch001-2 < Ch001.5 under plain alphabetical sort, so CBZ readers still page through them in the right order) — and pages reset to p001 for each one. A warning still flags that duplicates were found, so you can clean up the source if they were unintentional.

Each folder is named from its own name, not its container's: pointing cbzit at a staging/dump folder that holds several unrelated works (e.g. __ToConvert/{Work A, Work B}) gives each work its own archive, named after itself — they are not treated as volumes of a fake "ToConvert" series.

The merge subcommand

If you already have a pile of chapter-level .cbz files (e.g. Naruto Vol.01 Ch.0001 (en).cbz, Naruto Vol.01 Ch.0002 (en).cbz, …) and want to consolidate them into volume .cbz files, use merge:

npx cbzit merge ./my-chapters-folder

It unzips each chapter archive in memory, groups them by the volume number parsed from their filenames, re-paginates in reading order, and re-zips into one .cbz per volume. If all input files have no detectable volume number, they're merged into a single <Series> - One Shot.cbz. If only some of them lack a volume number (mixed input), those are grouped separately into <Series> - Unnumbered.cbz instead of being silently lumped into a fake volume — and a warning is added to the report so you notice.

Size guard: before writing, merge checks each planned archive and warns loudly if it would be unreasonably large — over ~1 GiB, or built from more than 60 chapters in one go — so you don't accidentally end up with a multi-gigabyte single file merged by mistake.

Page naming: merged volumes keep the same in-archive page naming as the main command — {Series} - Vol{VV} - Ch{CCC} - p{PPP}.ext, with the page counter reset for each chapter — as long as every chapter file going into a volume has a detectable chapter number. If any are missing, that volume falls back to continuous flat pagination ({Series} - Vol{VV} - p{PPP}.ext, no Ch token) and a warning is added to the report so you notice. If chapter numbers are detected but duplicated (e.g. two releases of the same chapter), they're kept as separate entries with the same sort-safe Ch001/Ch001-2 suffixing as the main command, each resetting to p001 — again with a warning so you know duplicates were found.

Options

cbzit <dir> [options]
cbzit merge <dir> [options]

Options:
  -o, --out <dir>   output directory (default: "./cbzit-output")
  -y, --yes         accept the detected plan without prompts
  -h, --help        display help for command

Supported image formats

jpg, jpeg, png, webp, gif, bmp (case-insensitive extension match). Anything else found inside a leaf folder is reported as ignored, never included in the archive.

Naming scheme

CBZ file name:

{Series} {Label} {NN}[ - {Title}][ (lang)].cbz
  • Label is the volume label, chosen from Tome / Volume / Vol / T (defaults to Tome). Single-character labels glue directly to the number (T01), multi-character labels keep the space (Tome 01, Volume 01).
  • NN is the zero-padded volume number (01, 02, …), with sensible floors (volumes: 2 digits, chapters: 3 digits, pages: 3 digits) that grow automatically when a series exceeds them — e.g. a 112-volume series gets Tome 001Tome 112 instead of breaking alphabetical order after Tome 99. Chapter and page numbers widen the same way when needed, so alphabetical order always matches reading order.
  • {Title} is preserved when a volume/chapter folder carries a title after its number (e.g. a folder named Tome 01 - This is the title becomes X Tome 01 - This is the title.cbz) — this is optional and only appears when detected.
  • (lang) is appended only when a language was detected in the file names or requested — the interactive flow proposes the detected tag by default, offers fr/en shortcuts, and accepts any custom tag (e.g. fr-en, es), or none to drop it.

Examples: Cobra Tome 01.cbz, Solaris Tome 16 (fr-en).cbz, X Tome 01 - This is the title.cbz.

Pages inside the archive are renamed with a per-chapter running counter that preserves reading order, so unzipping gives you a tidy, correctly-sorted folder instead of the original mess:

{Series} - Vol{VV}[ - Ch{CCC[.d]}] - p{PPP} (lang).ext
  • Chapters can be decimal (Ch012.5 for an epilogue-style chapter), and the page counter restarts at p001 for each chapter/source folder while the overall archive stays in reading order.
  • Double pages (e.g. a spread scanned as one image named 30-31.jpg) take up two slots in that counter and are kept as a single entry named after the range, e.g. p001-002.jpg.

Safety

  • cbzit never reads, modifies, renames, or deletes anything in your input folder. Only file names and sizes are inspected during scanning/planning; image bytes are only read at build time, and only to copy them (unmodified) into the new archive.
  • All output goes to a separate folder — by default ./cbzit-output/ — never back into the folder you scanned.
  • Override the output location with --out <dir>.

Roadmap

Not in v0, but planned, roughly in this order:

  • PDF → CBZ — extract pages straight out of a PDF into a CBZ (also worth checking whether this shrinks total size).
  • Advanced multi-language support — beyond the current fr / en / fr-en recognition, driven by config.
  • Fully non-interactive mode (flags) — every interactive prompt already maps to a planned flag (--series, --unit volume|chapter, --label, --lang <tag|none>, --out, --yes); a complete flags-only mode will let you script cbzit end-to-end with no prompts at all.

CBR output was considered and dropped: RAR is a proprietary format with no free/pure-JS compressor, and since manga page images are already compressed, there's no size benefit anyway — CBZ is universally supported by readers.

Credits

cbzit is inspired by a set of personal bash scripts (cbz-helper) the author had been using to do the same job by hand — this CLI rebuilds that logic as a proper, tested, cross-platform tool.

License

MIT

About

Turn folders of manga/comic page images into well-named CBZ archives

Topics

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors