# CBZ CBZ is a Python library designed for creating, manipulating, and viewing comic book files in CBZ, CBR, and PDF formats. It offers a straightforward interface to pack comic pages into CBZ archives, extract metadata from CBZ and CBR files, and display comics using a built-in player. ## Features - Seamless installation via [pip](#installation) - Pack images into CBZ format for comics and manga - Extract and manage metadata: title, series, format, and more - Handle comic pages with attributes like type, dimensions, and bookmarks - Unpack CBZ and CBR files to retrieve comic information, or extract images from PDF files - Built-in player for viewing CBZ, CBR, and PDF comics - Sequence protocol: iterate, index, and slice comic pages directly - Dataclass-based models with automatic XML mapping and strict type validation - Image support: JPEG, PNG, GIF, BMP, TIFF, WebP, JPEG XL, AVIF - Fully open-source! Pull requests welcome ## Installation Install CBZ from PyPI using pip: ```shell pip install cbz ``` With AVIF and JPEG XL support: ```shell pip install cbz[pillow] ``` ## Quick Start Here's a quick example of how to create a CBZ file from a series of images: ```python from pathlib import Path from cbz import ComicInfo, PageInfo, PageType, Format, YesNo, Manga, AgeRating if __name__ == "__main__": paths = sorted(Path("path/to/your/images").iterdir()) # Load each page from the images folder into a list of PageInfo objects pages = [ PageInfo.load( path=path, type=( PageType.FRONT_COVER if i == 0 else PageType.BACK_COVER if i == len(paths) - 1 else PageType.STORY ), ) for i, path in enumerate(paths) ] # Create a ComicInfo object with metadata comic = ComicInfo.from_pages( pages=pages, title="Your Comic Title", series="Your Comic Series", number=1, language_iso="en", format=Format.WEB_COMIC, black_white=YesNo.NO, manga=Manga.NO, age_rating=AgeRating.RATING_PENDING, ) # Display the comic in the built-in reader comic.show() # Save directly as a CBZ file comic.save("your_comic.cbz") # Or pack to bytes for custom handling cbz_content = comic.pack() Path("your_comic.cbz").write_bytes(cbz_content) ``` ## Player CBZ includes a command-line player for viewing comic book files in multiple formats. Simply run `cbzplayer ` to launch the player with the specified comic book file. ### Supported Formats - **CBZ** (Comic Book ZIP) - Standard ZIP archives containing images and metadata - **CBR** (Comic Book RAR) - RAR archives containing images and metadata - **PDF** - Portable Document Format files with embedded images (images only, no metadata) ### Usage ```shell usage: cbzplayer [-h] CBZ/CBR/PDF comic reader positional arguments: Path to the CBZ, CBR or PDF comic book file. options: -h, --help show this help message and exit ``` ### Examples ```shell # View a CBZ file cbzplayer my_comic.cbz # View a CBR file cbzplayer my_comic.cbr # View a PDF file cbzplayer my_comic.pdf ``` ### Keyboard Shortcuts | Shortcut | Action | |---------------------|-------------------| | Left / Right arrows | Navigate pages | | + / - | Zoom in / out | | Ctrl+Q | Quit | | Mouse wheel | Vertical scroll | | Shift+Mouse wheel | Horizontal scroll | | Ctrl+Mouse wheel | Zoom | ### Requirements for CBR Support CBR file support requires: - The `rarfile` Python package (automatically installed with CBZ) - An external RAR extraction tool such as: - `unrar` (recommended) - Available in most package managers - `rar` - Commercial RAR archiver - `7zip` - Free alternative with RAR support For installation instructions and compatibility details, see the [rarfile documentation](https://github.com/markokr/rarfile). ## Detailed Usage ### Creating a ComicInfo Object The `ComicInfo` class represents a comic book with metadata and pages. It supports initialization from a list of `PageInfo` objects: ```python from cbz import ComicInfo, PageInfo, PageType, Format, YesNo, Manga, AgeRating pages = [ PageInfo.load(path="page1.jpg", type=PageType.FRONT_COVER), PageInfo.load(path="page2.jpg", type=PageType.STORY), PageInfo.load(path="page3.jpg", type=PageType.BACK_COVER), ] comic = ComicInfo.from_pages( pages=pages, title="My Comic", series="Comic Series", number=1, language_iso="en", format=Format.WEB_COMIC, black_white=YesNo.NO, manga=Manga.NO, age_rating=AgeRating.RATING_PENDING, ) ``` You can also create pages directly from bytes or base64-encoded data: ```python page = PageInfo.loads(data=image_bytes, name="page.jpg", type=PageType.STORY) ``` ### Sequence Protocol `ComicInfo` implements the full sequence protocol, so you can interact with pages directly: ```python len(comic) # Number of pages comic[0] # First page comic[-1] # Last page comic[1:3] # Slice of pages for page in comic: # Iteration print(page.image_width, page.image_height) page in comic # Containment check ``` ### Extracting Metadata Retrieve comic information as a dictionary using `get_info()`: ```python info = comic.get_info() print(info) ``` ### Packing and Saving Pack the comic into CBZ format as bytes: ```python cbz_content = comic.pack() ``` Or save directly to disk (more memory-efficient for large archives): ```python comic.save("output.cbz") ``` ### Loading from Different Formats Load a comic from an existing CBZ file (with metadata): ```python comic = ComicInfo.from_cbz("your_comic.cbz") ``` Load a comic from an existing CBR file (with metadata): ```python comic = ComicInfo.from_cbr("your_comic.cbr") ``` Load a comic from a PDF file (images only, no metadata): ```python comic = ComicInfo.from_pdf("your_comic.pdf") ``` **Notes:** - CBR support requires an external RAR extraction tool. For detailed compatibility information and advanced configuration, see the [rarfile documentation](https://github.com/markokr/rarfile). - PDF files only provide image content; comic metadata (title, series, etc.) is not available from PDF files. ### Page Properties Each `PageInfo` object exposes the following properties, automatically extracted from the image content: ```python page = comic[0] page.content # bytes - raw image data page.image_width # int - width in pixels page.image_height # int - height in pixels page.image_size # int - file size in bytes page.suffix # str - file extension (.jpg, .png, etc.) page.name # str - original file name page.type # PageType - page type (FrontCover, Story, etc.) page.bookmark # str - bookmark / chapter name page.double # bool - double page spread ``` ### Metadata Fields All [ComicInfo.xml](docs/RFC-CBZ.md) v2.1 metadata fields are supported as dataclass attributes: | Attribute | Type | Default | Description | |--------------------------|--------------------|-----------|------------------------------------------| | `title` | `str` | `""` | Issue title | | `series` | `str` | `""` | Series name | | `number` | `Optional[int]` | `None` | Issue number | | `count` | `Optional[int]` | `None` | Total number of issues | | `volume` | `Optional[int]` | `None` | Volume number | | `year` | `Optional[int]` | `None` | Publication year | | `month` | `Optional[int]` | `None` | Publication month | | `day` | `Optional[int]` | `None` | Publication day | | `writer` | `str` | `""` | Writer(s), comma-separated | | `penciller` | `str` | `""` | Pencil artist(s) | | `inker` | `str` | `""` | Inker(s) | | `colorist` | `str` | `""` | Colorist(s) | | `letterer` | `str` | `""` | Letterer(s) | | `cover_artist` | `str` | `""` | Cover artist(s) | | `editor` | `str` | `""` | Editor(s) | | `translator` | `str` | `""` | Translator(s) | | `publisher` | `str` | `""` | Publisher | | `imprint` | `str` | `""` | Publisher imprint | | `genre` | `str` | `""` | Genre(s), comma-separated | | `tags` | `str` | `""` | Tags, comma-separated | | `web` | `str` | `""` | Web URL | | `language_iso` | `LanguageISO` | `""` | ISO language code (e.g., `"en"`, `"fr"`) | | `format` | `Format` | `UNKNOWN` | Publication format | | `black_white` | `YesNo` | `UNKNOWN` | Black and white | | `manga` | `Manga` | `UNKNOWN` | Manga / reading direction | | `age_rating` | `AgeRating` | `UNKNOWN` | Content age rating | | `community_rating` | `Optional[Rating]` | `None` | Community rating (0.0-5.0) | | `summary` | `str` | `""` | Synopsis / description | | `characters` | `str` | `""` | Character names, comma-separated | | `teams` | `str` | `""` | Team names, comma-separated | | `locations` | `str` | `""` | Locations, comma-separated | | `story_arc` | `str` | `""` | Story arc name | | `story_arc_number` | `Optional[int]` | `None` | Position in story arc | | `main_character_or_team` | `str` | `""` | Primary character or team | | `scan_information` | `str` | `""` | Scan / digitization notes | | `ean` | `str` | `""` | EAN / ISBN | | `book_price` | `str` | `""` | Cover price | ### Enumerations ```python from cbz import PageType, Format, YesNo, Manga, AgeRating # Page types PageType.FRONT_COVER # Front cover PageType.STORY # Story page (default) PageType.BACK_COVER # Back cover PageType.INNER_COVER # Inner cover / dust jacket PageType.ADVERTISEMENT # Advertisement PageType.EDITORIAL # Editorial / credits PageType.LETTERS # Letters page PageType.PREVIEW # Preview of upcoming issues PageType.ROUNDUP # Recap / summary PageType.OTHER # Other PageType.DELETED # Marked for deletion # Publication formats Format.SERIES # Regular series Format.GRAPHIC_NOVEL # Graphic novel Format.WEB_COMIC # Webcomic Format.ONE_SHOT # One-shot Format.TRADE_PAPERBACK # Trade paperback Format.ANNUAL # Annual Format.ANTHOLOGY # Anthology Format.LIMITED_SERIES # Limited series Format.MAGAZINE # Magazine # ... and more # Reading direction Manga.UNKNOWN # Not specified Manga.NO # Western (left to right) Manga.YES # Manga Manga.YES_AND_RIGHT_TO_LEFT # Manga (right to left) # Age ratings AgeRating.UNKNOWN # Not rated AgeRating.EVERYONE # All ages AgeRating.TEEN # Teens AgeRating.MATURE_17_PLUS # Mature 17+ AgeRating.RATING_PENDING # Rating pending # ... and more ``` ### Error Handling The library provides a hierarchy of specific exceptions: ```python from cbz import CBZError, InvalidImageError, EmptyArchiveError, InvalidMetadataError try: comic = ComicInfo.from_cbz("corrupted.cbz") except InvalidMetadataError: print("ComicInfo.xml is invalid or corrupted") except EmptyArchiveError: print("No valid images found in the archive") except InvalidImageError: print("An image in the archive could not be read") except CBZError: print("General CBZ error") ``` ## Format Specification A complete RFC specification of the CBZ format is available in [`docs/RFC-CBZ.md`](docs/RFC-CBZ.md). The ComicInfo.xml XSD schemas (v1.0, v2.0, v2.1) are in [`docs/schema/`](docs/schema/). ## Changelog See [`CHANGELOG.md`](CHANGELOG.md) for the full version history, including migration notes for v4.0. ## Contributors hyugogirubato piskunqa OleskiiPyskun tssujt gokender domenicoblanco RivMt flolep2607 chase-roohms ## Licensing This software is licensed under the terms of [MIT License](https://github.com/hyugogirubato/cbz/blob/main/LICENSE). You can find a copy of the license in the LICENSE file in the root folder. ### Third-Party Licenses This project uses the following third-party libraries: - **[langcodes](https://pypi.org/project/langcodes/)** - MIT License - **[Pillow](https://pypi.org/project/Pillow/)** - HPND License - **[pillow-avif-plugin](https://pypi.org/project/pillow-avif-plugin/)** - MIT License - **[pillow-jxl-plugin](https://pypi.org/project/pillow-jxl-plugin/)** - MIT License - **[pypdf](https://pypi.org/project/pypdf/)** - BSD License - **[rarfile](https://pypi.org/project/rarfile/)** - ISC License - **[xmltodict](https://pypi.org/project/xmltodict/)** - MIT License --- © hyugogirubato