39 Commits
Author SHA1 Message Date
hyugogirubato 46b508adf8 release v3.4.0 2025-07-11 00:44:19 +02:00
hyugogirubato 6d771e3bf8 Merge pull request #19 from tssujt/typo
fix: Fix installation instructions
2025-07-11 00:34:17 +02:00
tssujt 48c99ba44f fix: Fix installation instructions 2025-07-10 10:56:23 +08:00
hyugogirubato 8af1c47e15 release v3.4.0 2025-07-09 23:42:10 +02:00
hyugogirubato 6aa6b0da21 fix markdown typo 2025-07-09 23:41:13 +02:00
hyugogirubato 526fee3617 update project date 2025-07-09 23:40:10 +02:00
hyugogirubato 6354c1d3bb add new contributor 2025-07-09 23:39:59 +02:00
hyugogirubato 3a80dfe47b add missing imports 2025-07-09 23:39:41 +02:00
hyugogirubato 58ef27f227 fix grammar 2025-07-09 23:39:28 +02:00
hyugogirubato 36b76f6d9e add missing repo reference 2025-07-09 23:39:15 +02:00
hyugogirubato 3c53f16d07 bump dep version 2025-07-09 23:39:00 +02:00
hyugogirubato 8161551ab6 specify complete py version 2025-07-09 23:38:47 +02:00
hyugogirubato 8ab1b37332 unified tk constants import 2025-07-09 23:38:24 +02:00
hyugogirubato 784f698044 update license date 2025-07-09 23:37:44 +02:00
hyugogirubato fc1e693e67 fix py 3.8 support 2025-07-09 23:37:34 +02:00
hyugogirubato 4d831f28a9 sort lib imports 2025-07-09 23:37:00 +02:00
hyugogirubato 195fca4370 apply git ignore 2025-07-09 23:00:48 +02:00
hyugogirubato 758e54fa4d Merge pull request #18 from tssujt/cbr
feat: Enhance support for CBR format
2025-07-09 22:56:53 +02:00
tssujt 7f1a0724e1 chore: Update document third-party licenses 2025-07-09 22:29:54 +08:00
tssujt f015d998e1 feat: Enhance support for CBR format 2025-07-09 22:29:53 +08:00
hyugogirubato 7d82752546 Merge pull request #17 from tssujt/pdf
Replace PyMuPDF with pypdf for PDF handling
2025-06-27 21:59:42 +02:00
tssujt e8e6eaf3ae Replace PyMuPDF with pypdf for PDF handling 2025-06-24 22:36:52 +08:00
hyugogirubato 4f777bc9bf Release v3.3.7 2025-01-04 12:51:54 +01:00
hyugogirubato c48d64cbe0 fix ctypes import for unix 2025-01-04 12:45:07 +01:00
hyugogirubato d195e25043 release v3.3.6 2024-12-22 12:05:07 +01:00
hyugogirubato 5fd7c019d1 sort imports 2024-12-22 12:04:33 +01:00
hyugogirubato c4169ebb84 more flexible dependencies 2024-12-22 12:03:39 +01:00
hyugogirubato a46bae2e2d skip poetry.lock 2024-12-22 12:00:36 +01:00
hyugogirubato 616b1eff8e fix deprecated datetime usage 2024-12-22 11:52:35 +01:00
hyugogirubato 27a313fe4c add missing name extract from zip unpack 2024-12-21 15:45:55 +01:00
hyugogirubato 72c14449fc option to skip page name rename 2024-12-21 15:37:14 +01:00
hyugogirubato 04feaa7027 Release v3.3.5 2024-07-22 19:52:31 +02:00
hyugogirubato 8b8069e38d Merge pull request #14 from gokender/fix-pathlib-import
Fix pathlib import in "utils.py"
2024-07-19 13:40:16 +02:00
gokender d357ad3e1d Fix pathlib import in "utils.py" 2024-07-19 10:24:37 +02:00
hyugogirubato b234a46e00 Release v3.3.4 2024-07-18 22:31:53 +02:00
hyugogirubato e6d77739e1 Merge remote-tracking branch 'origin/main' 2024-07-18 22:28:42 +02:00
hyugogirubato 0421d99c44 Fix invalid utc format 2024-07-18 22:28:26 +02:00
hyugogirubato 17084154a4 Remove invalid dict key 2024-07-18 22:27:03 +02:00
hyugogirubato cba561c2ec Update CHANGELOG.md 2024-07-18 22:20:31 +02:00
13 changed files with 302 additions and 491 deletions
+3 -2
View File
@@ -100,7 +100,7 @@ ipython_config.py
# This is especially recommended for binary packages to ensure reproducibility, and is more # This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries. # commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock poetry.lock
# pdm # pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
@@ -173,4 +173,5 @@ pyrightconfig.json
### CBZ ### ### CBZ ###
*.cbz *.cbz
*.pdf *.pdf
*.zip *.zip
*cbr
+58
View File
@@ -4,12 +4,64 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [3.4.1] - 2025-07-11
### Fixed
- Fix installation instructions.
## [3.4.0] - 2025-07-09
### Added
- Support for PDF files.
- Support for CBR (Comic Book RAR) files.
### Changed
- Replaced `PyMuPDF` with `pypdf` for PDF handling.
- Updated third-party license documentation for bundled libraries.
### New Contributors
- [tssujt](https://github.com/tssujt)
## [3.3.7] - 2025-01-04
### Fixed
- Import error on Unix systems for the comic player.
## [3.3.6] - 2024-12-22
### Added
- Option to not rename the name of image files loaded into pages.
### Fixed
- Removing deprecated datetime api for utcnow.
## [3.3.5] - 2024-07-22
### Fixed
- Fix `Path` import using `pathlib`.
## [3.3.4] - 2024-07-18
### Fixed
- Remove invalid dict key.
- Fix invalid utc format.
## [3.3.3] - 2024-07-18 ## [3.3.3] - 2024-07-18
### Added ### Added
- Reference links for XML schema. - Reference links for XML schema.
- New information fields in the XML. - New information fields in the XML.
- Support PDF files via `cbzplayer` command.
### Changed ### Changed
@@ -147,6 +199,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
- Initial release. - Initial release.
[3.4.1]: https://github.com/hyugogirubato/cbz/releases/tag/v3.4.1
[3.4.0]: https://github.com/hyugogirubato/cbz/releases/tag/v3.4.0
[3.3.7]: https://github.com/hyugogirubato/cbz/releases/tag/v3.3.7
[3.3.6]: https://github.com/hyugogirubato/cbz/releases/tag/v3.3.6
[3.3.5]: https://github.com/hyugogirubato/cbz/releases/tag/v3.3.5
[3.3.4]: https://github.com/hyugogirubato/cbz/releases/tag/v3.3.4
[3.3.3]: https://github.com/hyugogirubato/cbz/releases/tag/v3.3.3 [3.3.3]: https://github.com/hyugogirubato/cbz/releases/tag/v3.3.3
[3.3.2]: https://github.com/hyugogirubato/cbz/releases/tag/v3.3.2 [3.3.2]: https://github.com/hyugogirubato/cbz/releases/tag/v3.3.2
[3.3.1]: https://github.com/hyugogirubato/cbz/releases/tag/v3.3.1 [3.3.1]: https://github.com/hyugogirubato/cbz/releases/tag/v3.3.1
+1 -1
View File
@@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) 2024 hyugogirubato Copyright (c) 2025 hyugogirubato
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
+75 -14
View File
@@ -1,6 +1,6 @@
# CBZ # CBZ
CBZ is a Python library designed for creating, manipulating, and viewing comic book files in CBZ format. It offers a straightforward interface to pack comic pages into CBZ archives, extract metadata, and display comics using a built-in player. 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 ## Features
@@ -8,13 +8,14 @@ CBZ is a Python library designed for creating, manipulating, and viewing comic b
- 📚 Pack images into CBZ format for comics and manga - 📚 Pack images into CBZ format for comics and manga
- 📝 Extract and manage title, series, format, and more - 📝 Extract and manage title, series, format, and more
- 🖼️ Handle comic pages with attributes like type and format - 🖼️ Handle comic pages with attributes like type and format
- 📦 Unpack CBZ files to retrieve comic information - 📦 Unpack CBZ and CBR files to retrieve comic information, or extract images from PDF files
- 🛠️ Built-in player for viewing CBZ comics - 🛠️ Built-in player for viewing CBZ, CBR, and PDF comics
- 📚 Full CBR (RAR) format support for reading existing archives
- ❤️ Fully Open-Source! Pull Requests Welcome - ❤️ Fully Open-Source! Pull Requests Welcome
## Installation ## Installation
Install KeyDive from PyPI using Poetry: Install CBZ from PyPI using pip:
```shell ```shell
pip install cbz pip install cbz
@@ -73,7 +74,15 @@ if __name__ == '__main__':
## Player ## Player
CBZ includes a command-line player for viewing CBZ comic book files. Simply run cbzplayer <file> to launch the player with the specified CBZ file. CBZ includes a command-line player for viewing comic book files in multiple formats. Simply run `cbzplayer <file>` 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 ````shell
usage: cbzplayer [-h] <file> usage: cbzplayer [-h] <file>
@@ -81,13 +90,37 @@ usage: cbzplayer [-h] <file>
Launch CBZ player with a comic book file Launch CBZ player with a comic book file
positional arguments: positional arguments:
<file> Path to the CBZ comic book file. <file> Path to the CBZ, CBR, or PDF comic book file.
options: options:
-h, --help show this help message and exit -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
```
### 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 ## Detailed Usage
### Creating a ComicInfo Object ### Creating a ComicInfo Object
@@ -96,6 +129,7 @@ The `ComicInfo` class represents a comic book with metadata and pages. It suppor
```python ```python
from cbz.comic import ComicInfo from cbz.comic import ComicInfo
from cbz.constants import PageType, YesNo, Manga, AgeRating, Format
from cbz.page import PageInfo from cbz.page import PageInfo
# Example usage: # Example usage:
@@ -135,30 +169,57 @@ Pack the comic into a CBZ file format:
cbz_content = comic.pack() cbz_content = comic.pack()
``` ```
### Unpacking from CBZ ### Loading from Different Formats
Load a comic from an existing CBZ file: Load a comic from an existing CBZ file (with metadata):
```python ```python
comic_from_cbz = ComicInfo.from_cbz('/path/to/your_comic.cbz') comic_from_cbz = ComicInfo.from_cbz('/path/to/your_comic.cbz')
``` ```
Load a comic from an existing CBR file (with metadata):
```python
comic_from_cbr = ComicInfo.from_cbr('/path/to/your_comic.cbr')
```
Load a comic from a PDF file (images only, no metadata):
```python
comic_from_pdf = ComicInfo.from_pdf('/path/to/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.
## Contributors ## Contributors
<a href="https://github.com/hyugogirubato"><img src="https://images.weserv.nl/?url=avatars.githubusercontent.com/u/65763543?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="hyugogirubato"/></a> <a href="https://github.com/hyugogirubato"><img src="https://images.weserv.nl/?url=avatars.githubusercontent.com/u/65763543?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="hyugogirubato"/></a>
<a href="https://github.com/piskunqa"><img src="https://images.weserv.nl/?url=avatars.githubusercontent.com/u/38443069?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="piskunqa"/></a> <a href="https://github.com/piskunqa"><img src="https://images.weserv.nl/?url=avatars.githubusercontent.com/u/38443069?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="piskunqa"/></a>
<a href="https://github.com/OleskiiPyskun"><img src="https://images.weserv.nl/?url=avatars.githubusercontent.com/u/75667382?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="OleskiiPyskun"/></a> <a href="https://github.com/OleskiiPyskun"><img src="https://images.weserv.nl/?url=avatars.githubusercontent.com/u/75667382?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="OleskiiPyskun"/></a>
<a href="https://github.com/tssujt"><img src="https://images.weserv.nl/?url=avatars.githubusercontent.com/u/17313425?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="tssujt"/></a>
<a href="https://github.com/gokender"><img src="https://images.weserv.nl/?url=avatars.githubusercontent.com/u/3709740?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="gokender"/></a>
<a href="https://github.com/domenicoblanco"><img src="https://images.weserv.nl/?url=avatars.githubusercontent.com/u/9018104?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="domenicoblanco"/></a> <a href="https://github.com/domenicoblanco"><img src="https://images.weserv.nl/?url=avatars.githubusercontent.com/u/9018104?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="domenicoblanco"/></a>
<a href="https://github.com/RivMt"><img src="https://images.weserv.nl/?url=avatars.githubusercontent.com/u/40086827?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="RivMt"/></a> <a href="https://github.com/RivMt"><img src="https://images.weserv.nl/?url=avatars.githubusercontent.com/u/40086827?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="RivMt"/></a>
<a href="https://github.com/flolep2607"><img src="https://images.weserv.nl/?url=avatars.githubusercontent.com/u/24566964?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="flolep2607"/></a> <a href="https://github.com/flolep2607"><img src="https://images.weserv.nl/?url=avatars.githubusercontent.com/u/24566964?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="flolep2607"/></a>
<a href="https://github.com/gokender"><img src="https://images.weserv.nl/?url=avatars.githubusercontent.com/u/3709740?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="gokender"/></a>
## Licensing ## Licensing
This software is licensed under the terms of [MIT License](https://github.com/hyugogirubato/cbz/blob/main/LICENSE). 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. You can find a copy of the license in the LICENSE file in the root folder.
* * * ### Third-Party Licenses
© hyugogirubato 2024 This project uses the following third-party libraries:
- **[langcodes](https://pypi.org/project/langcodes/)** - MIT License
- **[Pillow](https://pypi.org/project/Pillow/)** - HPND 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 2025
+9 -3
View File
@@ -8,7 +8,7 @@ from cbz.comic import ComicInfo
def main() -> None: def main() -> None:
# Parse command-line arguments # Parse command-line arguments
parser = argparse.ArgumentParser(description='Launch CBZ player with a comic book file') parser = argparse.ArgumentParser(description='Launch CBZ player with a comic book file')
parser.add_argument('comic_path', type=Path, metavar='<file>', help='Path to the CBZ comic book file.') parser.add_argument('comic_path', type=Path, metavar='<file>', help='Path to the CBZ, CBR, or PDF comic book file.')
args = parser.parse_args() args = parser.parse_args()
# Validate the provided path # Validate the provided path
@@ -17,9 +17,15 @@ def main() -> None:
print(f'Error: The file "{comic_path}" does not exist or is not a valid file.') print(f'Error: The file "{comic_path}" does not exist or is not a valid file.')
exit(1) exit(1)
# Create ComicInfo object from CBZ file # Create ComicInfo object from comic file
try: try:
comic_info = ComicInfo.from_pdf(comic_path) if comic_path.suffix == '.pdf' else ComicInfo.from_cbz(comic_path) if comic_path.suffix == '.pdf':
comic_info = ComicInfo.from_pdf(comic_path)
elif comic_path.suffix == '.cbr':
comic_info = ComicInfo.from_cbr(comic_path)
else:
comic_info = ComicInfo.from_cbz(comic_path)
# Launch the CBZ player # Launch the CBZ player
comic_info.show() comic_info.show()
except Exception as e: except Exception as e:
+120 -71
View File
@@ -1,17 +1,18 @@
from __future__ import annotations from __future__ import annotations
import zipfile import zipfile
from datetime import datetime
from datetime import datetime, timezone
from enum import Enum from enum import Enum
from io import BytesIO from io import BytesIO
from typing import Union from typing import Union, List
from pathlib import Path from pathlib import Path
import fitz import rarfile
import xmltodict import xmltodict
from pypdf import PdfReader
from cbz.constants import XML_NAME, COMIC_FIELDS, IMAGE_FORMAT, PAGE_FIELDS from cbz.constants import XML_NAME, COMIC_FIELDS, IMAGE_FORMAT, PAGE_FIELDS
from cbz.models import ComicModel from cbz.models import ComicModel
from cbz.page import PageInfo from cbz.page import PageInfo
@@ -23,27 +24,27 @@ class ComicInfo(ComicModel):
ComicInfo class that represents the comic book information and pages. ComicInfo class that represents the comic book information and pages.
""" """
def __init__(self, pages: list[PageInfo], **kwargs): def __init__(self, pages: List[PageInfo], **kwargs):
""" """
Initialize the ComicInfo instance with pages and additional attributes. Initialize the ComicInfo instance with pages and additional attributes.
Args: Args:
pages (list[PageInfo]): List of PageInfo objects representing the comic pages. pages (List[PageInfo]): List of PageInfo objects representing the comic pages.
**kwargs: Additional attributes for the comic. **kwargs: Additional attributes for the comic.
Attributes: Attributes:
pages (list[PageInfo]): Stores the comic pages. pages (List[PageInfo]): Stores the comic pages.
""" """
super(ComicInfo, self).__init__(**kwargs) super(ComicInfo, self).__init__(**kwargs)
self.pages = pages self.pages = pages
@classmethod @classmethod
def from_pages(cls, pages: list[PageInfo], **kwargs) -> ComicInfo: def from_pages(cls, pages: List[PageInfo], **kwargs) -> ComicInfo:
""" """
Create a ComicInfo instance from pages and additional attributes. Create a ComicInfo instance from pages and additional attributes.
Args: Args:
pages (list[PageInfo]): List of PageInfo objects representing the comic pages. pages (List[PageInfo]): List of PageInfo objects representing the comic pages.
**kwargs: Additional attributes for the comic. **kwargs: Additional attributes for the comic.
Returns: Returns:
@@ -67,7 +68,25 @@ class ComicInfo(ComicModel):
""" """
if not isinstance(path, (Path, str)): if not isinstance(path, (Path, str)):
raise ValueError(f'Expecting Path object or path string, got {path!r}') raise ValueError(f'Expecting Path object or path string, got {path!r}')
return cls.__unpack_zip(path) return cls.__unpack_zip(Path(path))
@classmethod
def from_cbr(cls, path: Union[Path, str]) -> ComicInfo:
"""
Create a ComicInfo instance from a CBR file.
Args:
path (Union[Path, str]): Path to the CBR file.
Returns:
ComicInfo: An instance of ComicInfo.
Raises:
ValueError: If the provided path is not a Path object or a string.
"""
if not isinstance(path, (Path, str)):
raise ValueError(f'Expecting Path object or path string, got {path!r}')
return cls.__unpack_rar(Path(path))
@classmethod @classmethod
def from_pdf(cls, path: Union[Path, str]) -> ComicInfo: def from_pdf(cls, path: Union[Path, str]) -> ComicInfo:
@@ -85,7 +104,7 @@ class ComicInfo(ComicModel):
""" """
if not isinstance(path, (Path, str)): if not isinstance(path, (Path, str)):
raise ValueError(f'Expecting Path object or path string, got {path!r}') raise ValueError(f'Expecting Path object or path string, got {path!r}')
return cls.__unpack_pdf(path) return cls.__unpack_pdf(Path(path))
@staticmethod @staticmethod
def __unpack_pdf(path: Path) -> ComicInfo: def __unpack_pdf(path: Path) -> ComicInfo:
@@ -98,18 +117,89 @@ class ComicInfo(ComicModel):
Returns: Returns:
ComicInfo: An instance of ComicInfo. ComicInfo: An instance of ComicInfo.
""" """
pages: list[PageInfo] = [] pages: List[PageInfo] = []
with fitz.open(path) as pf: reader = PdfReader(path)
for element in pf: for page in reader.pages:
# Check if the page has images for image in page.images:
images = element.get_images(full=True) pages.append(PageInfo.loads(data=image.data))
for i, image in enumerate(images):
base = pf.extract_image(image[0])
pages.append(PageInfo.loads(data=base['image']))
assert pages, 'No valid images present in file' assert pages, 'No valid images present in file'
return ComicInfo.from_pages(pages=pages) return ComicInfo.from_pages(pages=pages)
@staticmethod
def __extract_info(items: dict, fields: dict) -> dict:
"""
Extract and convert field information from the provided items and fields.
Args:
items (dict): Dictionary containing item attributes.
fields (dict): Dictionary containing field mappings and types.
Returns:
dict: Dictionary with extracted and converted field information.
"""
content = {}
for key, (field_key, field_type) in fields.items():
if field_key in items:
content[key] = field_type(items[field_key])
return content
@staticmethod
def __process_archive(archive_file: Union[zipfile.ZipFile, rarfile.RarFile]) -> ComicInfo:
"""
Common logic for processing archive files (CBZ/CBR).
Args:
archive_file (Union[ZipFile, RarFile]): Archive file object (ZipFile or RarFile)
Returns:
ComicInfo: An instance of ComicInfo.
"""
pages = []
names = archive_file.namelist()
comic_info = {}
if XML_NAME in names:
with archive_file.open(XML_NAME, 'r') as f:
comic_info = xmltodict.parse(f.read()).get('ComicInfo', {})
names.remove(XML_NAME)
comic = ComicInfo.__extract_info(
items=comic_info,
fields=COMIC_FIELDS
)
pages_info = comic_info.get('Pages', {}).get('Page', [])
for i, name in enumerate(names):
suffix = Path(name).suffix
if suffix:
assert suffix in IMAGE_FORMAT, f'Unsupported image format: {suffix}'
with archive_file.open(name, 'r') as f:
page_info = {}
if i < len(pages_info):
page_info = ComicInfo.__extract_info(
items=pages_info[i],
fields=PAGE_FIELDS
)
page_info['name'] = Path(name).name
pages.append(PageInfo.loads(data=f.read(), **page_info))
return ComicInfo.from_pages(pages=pages, **comic)
@staticmethod
def __unpack_rar(path: Path) -> ComicInfo:
"""
Unpack a CBR file and create a ComicInfo instance.
Args:
path (Path): Path to the CBR file.
Returns:
ComicInfo: An instance of ComicInfo.
"""
with rarfile.RarFile(path, 'r') as rf:
return ComicInfo.__process_archive(rf)
@staticmethod @staticmethod
def __unpack_zip(path: Path) -> ComicInfo: def __unpack_zip(path: Path) -> ComicInfo:
""" """
@@ -121,55 +211,8 @@ class ComicInfo(ComicModel):
Returns: Returns:
ComicInfo: An instance of ComicInfo. ComicInfo: An instance of ComicInfo.
""" """
def __info(items: dict, fields: dict) -> dict:
"""
Extract and convert field information from the provided items and fields.
Args:
items (dict): Dictionary containing item attributes.
fields (dict): Dictionary containing field mappings and types.
Returns:
dict: Dictionary with extracted and converted field information.
"""
content = {}
for key, (field_key, field_type) in fields.items():
if field_key in items:
content[key] = field_type(items[field_key])
return content
pages = []
with zipfile.ZipFile(path, 'r', zipfile.ZIP_STORED) as zf: with zipfile.ZipFile(path, 'r', zipfile.ZIP_STORED) as zf:
names = zf.namelist() return ComicInfo.__process_archive(zf)
comic_info = {}
if XML_NAME in names:
with zf.open(XML_NAME, 'r') as f:
comic_info = xmltodict.parse(f.read()).get('ComicInfo', {})
names.remove(XML_NAME)
comic = __info(
items=comic_info,
fields=COMIC_FIELDS
)
pages_info = comic_info.get('Pages', {}).get('Page', [])
for i, name in enumerate(names):
suffix = Path(name).suffix
if suffix:
assert suffix in IMAGE_FORMAT, f'Unsupported image format: {suffix}'
with zf.open(name, 'r') as f:
page_info = {}
if i < len(pages_info):
page_info = __info(
items=pages_info[i],
fields=PAGE_FIELDS
)
pages.append(PageInfo.loads(data=f.read(), **page_info))
return ComicInfo.from_pages(pages=pages, **comic)
def get_info(self) -> dict: def get_info(self) -> dict:
""" """
@@ -202,7 +245,6 @@ class ComicInfo(ComicModel):
fields=COMIC_FIELDS) fields=COMIC_FIELDS)
comic_pages = [] comic_pages = []
comic_info['Pages'] = []
for i, page in enumerate(self.pages): for i, page in enumerate(self.pages):
page_info = __info( page_info = __info(
items={k: v for k, v in page.__dict__.items() if not k.startswith('_')}, items={k: v for k, v in page.__dict__.items() if not k.startswith('_')},
@@ -211,7 +253,7 @@ class ComicInfo(ComicModel):
comic_pages.append(dict(sorted(page_info.items()))) comic_pages.append(dict(sorted(page_info.items())))
# https://github.com/anansi-project/rfcs/issues/3#issuecomment-671631676 # https://github.com/anansi-project/rfcs/issues/3#issuecomment-671631676
utcnow = datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S.%f')[:-3] + '' utcnow = datetime.now(timezone.utc).strftime('%Y-%m-%dT%H:%M:%S.%f')[:-3] + 'Z'
comic_info.update({ comic_info.update({
'@xmlns:xsd': 'http://www.w3.org/2001/XMLSchema', '@xmlns:xsd': 'http://www.w3.org/2001/XMLSchema',
'@xmlns:xsi': 'http://www.w3.org/2001/XMLSchema-instance', '@xmlns:xsi': 'http://www.w3.org/2001/XMLSchema-instance',
@@ -223,10 +265,13 @@ class ComicInfo(ComicModel):
}) })
return comic_info return comic_info
def pack(self) -> bytes: def pack(self, rename: bool = True) -> bytes:
""" """
Pack the comic information and pages into a CBZ file format. Pack the comic information and pages into a CBZ file format.
Args:
rename (bool): Whether to rename pages to a sequential format (e.g., 'page-001.jpg').
Returns: Returns:
bytes: Bytes representing the packed CBZ file. bytes: Bytes representing the packed CBZ file.
""" """
@@ -238,7 +283,11 @@ class ComicInfo(ComicModel):
content.replace('></Page>', ' />').encode('utf-8') content.replace('></Page>', ' />').encode('utf-8')
) )
for i, page in enumerate(self.pages): for i, page in enumerate(self.pages):
zf.writestr(f'page-{i + 1:03d}{page.suffix}', page.content) name = page.name
# If the page does not have a name or renaming is enabled, generate a sequential name for the page.
if not name or rename:
name = f'page-{i + 1:03d}{page.suffix}'
zf.writestr(name, page.content)
packed = zip_buffer.getvalue() packed = zip_buffer.getvalue()
zip_buffer.close() zip_buffer.close()
+1
View File
@@ -132,6 +132,7 @@ class PageModel(BaseModel):
image_width: int image_width: int
image_height: int image_height: int
suffix: str suffix: str
name: str
__content: bytes __content: bytes
def __init__(self, **kwargs): def __init__(self, **kwargs):
+8 -4
View File
@@ -3,9 +3,9 @@ from __future__ import annotations
import base64 import base64
from io import BytesIO from io import BytesIO
from typing import Union from typing import Union, Optional
from pathlib import Path from pathlib import Path
from PIL import Image from PIL import Image
from cbz.constants import IMAGE_FORMAT from cbz.constants import IMAGE_FORMAT
@@ -17,15 +17,17 @@ class PageInfo(PageModel):
Model for representing comic book pages with additional content handling capabilities. Model for representing comic book pages with additional content handling capabilities.
""" """
def __init__(self, content: bytes, **kwargs): def __init__(self, content: bytes, name: Optional[str] = None, **kwargs):
""" """
Initializes a PageInfo instance. Initializes a PageInfo instance.
Args: Args:
content (bytes): The content of the page in bytes. content (bytes): The content of the page in bytes.
name (Optional[str]): The name of the page (default is None).
**kwargs: Additional keyword arguments passed to the base class initializer. **kwargs: Additional keyword arguments passed to the base class initializer.
""" """
super(PageInfo, self).__init__(**kwargs) super(PageInfo, self).__init__(**kwargs)
self.name = name
self.content = content self.content = content
@property @property
@@ -93,7 +95,9 @@ class PageInfo(PageModel):
""" """
if not isinstance(path, (Path, str)): if not isinstance(path, (Path, str)):
raise ValueError(f'Expecting Path object or path string, got {path!r}') raise ValueError(f'Expecting Path object or path string, got {path!r}')
with Path(path).open(mode='rb') as f: path = Path(path)
with path.open(mode='rb') as f:
kwargs.setdefault('name', path.name)
return cls(f.read(), **kwargs) return cls(f.read(), **kwargs)
def show(self) -> None: def show(self) -> None:
+12 -9
View File
@@ -1,13 +1,16 @@
import tkinter as tk
import os import os
import tkinter as tk
from io import BytesIO from io import BytesIO
from tkinter import ttk from tkinter import ttk
from tkinter.constants import DISABLED, NORMAL from pathlib import Path
from ctypes import windll
try:
from ctypes import windll
except ImportError:
windll = None # windll is not available on this platform
from PIL import Image, ImageTk from PIL import Image, ImageTk
from pathlib import Path
from cbz.comic import ComicInfo from cbz.comic import ComicInfo
from cbz.page import PageInfo from cbz.page import PageInfo
@@ -203,7 +206,7 @@ class Player:
self.summary_text = tk.Text(self.canvas, wrap=tk.WORD, bg='white', bd=0, padx=10, pady=10) self.summary_text = tk.Text(self.canvas, wrap=tk.WORD, bg='white', bd=0, padx=10, pady=10)
self.summary_text.tag_configure('bold', font=('Arial', 13, 'bold')) self.summary_text.tag_configure('bold', font=('Arial', 13, 'bold'))
self.summary_text.tag_configure('normal', font=('Arial', 12), lmargin1=10, lmargin2=10) self.summary_text.tag_configure('normal', font=('Arial', 12), lmargin1=10, lmargin2=10)
self.summary_text.config(state=DISABLED) self.summary_text.config(state=tk.DISABLED)
def show_page(self) -> None: def show_page(self) -> None:
""" """
@@ -219,8 +222,8 @@ class Player:
self._show_image_page() self._show_image_page()
# Update navigation button states based on current page index # Update navigation button states based on current page index
self.prev_button.config(state=DISABLED if self.current_page == -1 else NORMAL) self.prev_button.config(state=tk.DISABLED if self.current_page == -1 else tk.NORMAL)
self.next_button.config(state=DISABLED if self.current_page + 1 == len(self.comic_info.pages) else NORMAL) self.next_button.config(state=tk.DISABLED if self.current_page + 1 == len(self.comic_info.pages) else tk.NORMAL)
# Update page index label text # Update page index label text
self.page_index_label.config(text=f'{self.current_page + 1}/{len(self.comic_info.pages)}') self.page_index_label.config(text=f'{self.current_page + 1}/{len(self.comic_info.pages)}')
@@ -235,7 +238,7 @@ class Player:
# Display summary for the first page # Display summary for the first page
infos = self.comic_info.get_info() infos = self.comic_info.get_info()
self.summary_text.config(state=NORMAL) self.summary_text.config(state=tk.NORMAL)
self.summary_text.delete('1.0', tk.END) self.summary_text.delete('1.0', tk.END)
for key, value in infos.items(): for key, value in infos.items():
@@ -245,7 +248,7 @@ class Player:
self.summary_text.insert(tk.END, f'{key}\n', 'bold') self.summary_text.insert(tk.END, f'{key}\n', 'bold')
self.summary_text.insert(tk.END, f'{value}\n\n', 'normal') self.summary_text.insert(tk.END, f'{value}\n\n', 'normal')
self.summary_text.config(state=DISABLED) self.summary_text.config(state=tk.DISABLED)
self.summary_text.place(relwidth=1, relheight=1) self.summary_text.place(relwidth=1, relheight=1)
def _show_image_page(self) -> None: def _show_image_page(self) -> None:
+2 -2
View File
@@ -1,14 +1,14 @@
from enum import Enum from enum import Enum
from io import BytesIO from io import BytesIO
from pathlib import Path
from PIL import Image from PIL import Image
from PIL.IcoImagePlugin import IcoFile from PIL.IcoImagePlugin import IcoFile
from path import Path
def default_attr(value: any) -> any: def default_attr(value: any) -> any:
""" """
Provides a default value based on the expected type of an attribute. Provides a default value based on the expected type of attribute.
Args: Args:
value (any): Expected type or class of the attribute. value (any): Expected type or class of the attribute.
+1 -1
View File
@@ -50,7 +50,7 @@ if __name__ == '__main__':
comic.show() comic.show()
# Pack the comic book content into a CBZ file format # Pack the comic book content into a CBZ file format
cbz_content = comic.pack() cbz_content = comic.pack(rename=True)
# Define the path where the CBZ file will be saved # Define the path where the CBZ file will be saved
cbz_path = PARENT / f'{comic.title}.cbz' cbz_path = PARENT / f'{comic.title}.cbz'
Generated
-374
View File
@@ -1,374 +0,0 @@
# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.
[[package]]
name = "langcodes"
version = "3.4.0"
description = "Tools for labeling human languages with IETF language tags"
optional = false
python-versions = ">=3.8"
files = [
{file = "langcodes-3.4.0-py3-none-any.whl", hash = "sha256:10a4cc078b8e8937d8485d3352312a0a89a3125190db9f2bb2074250eef654e9"},
{file = "langcodes-3.4.0.tar.gz", hash = "sha256:ae5a77d1a01d0d1e91854a671890892b7ce9abb601ab7327fc5c874f899e1979"},
]
[package.dependencies]
language-data = ">=1.2"
[package.extras]
build = ["build", "twine"]
test = ["pytest", "pytest-cov"]
[package.source]
type = "legacy"
url = "https://pypi.org/simple"
reference = "localpypi"
[[package]]
name = "language-data"
version = "1.2.0"
description = "Supplementary data about languages used by the langcodes module"
optional = false
python-versions = "*"
files = [
{file = "language_data-1.2.0-py3-none-any.whl", hash = "sha256:77d5cab917f91ee0b2f1aa7018443e911cf8985ef734ca2ba3940770f6a3816b"},
{file = "language_data-1.2.0.tar.gz", hash = "sha256:82a86050bbd677bfde87d97885b17566cfe75dad3ac4f5ce44b52c28f752e773"},
]
[package.dependencies]
marisa-trie = ">=0.7.7"
[package.extras]
build = ["build", "twine"]
test = ["pytest", "pytest-cov"]
[package.source]
type = "legacy"
url = "https://pypi.org/simple"
reference = "localpypi"
[[package]]
name = "marisa-trie"
version = "1.2.0"
description = "Static memory-efficient and fast Trie-like structures for Python."
optional = false
python-versions = ">=3.7"
files = [
{file = "marisa_trie-1.2.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:61fab91fef677f0af0e818e61595f2334f7e0b3e122b24ec65889aae69ba468d"},
{file = "marisa_trie-1.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5f5b3080316de735bd2b07265de5eea3ae176fa2fc60f9871aeaa9cdcddfc8f7"},
{file = "marisa_trie-1.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:77bfde3287314e91e28d3a882c7b87519ef0ee104c921df72c7819987d5e4863"},
{file = "marisa_trie-1.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4fbb1ec1d9e891060a0aee9f9c243acec63de1e197097a14850ba38ec8a4013"},
{file = "marisa_trie-1.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e04e9c86fe8908b61c2aebb8444217cacaed15b93d2dccaac3849e36a6dc660"},
{file = "marisa_trie-1.2.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5a7c75a508f44e40f7af8448d466045a97534adcbb026e63989407cefb9ebfa6"},
{file = "marisa_trie-1.2.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5321211647609869907e81b0230ad2dfdfa7e19fe1ee469b46304a622391e6a1"},
{file = "marisa_trie-1.2.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:88660e6ee0f821872aaf63ba4b9a7513428b9cab20c69cc013c368bd72c3a4fe"},
{file = "marisa_trie-1.2.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:4e4535fc5458de2b59789e574cdd55923d63de5612dc159d33941af79cd62786"},
{file = "marisa_trie-1.2.0-cp310-cp310-win32.whl", hash = "sha256:bdd1d4d430e33abbe558971d1bd57da2d44ca129fa8a86924c51437dba5cb345"},
{file = "marisa_trie-1.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:c729e2b8f9699874b1372b5a01515b340eda1292f5e08a3fe4633b745f80ad7a"},
{file = "marisa_trie-1.2.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d62985a0e6f2cfeb36cd6afa0460063bbe83ef4bfd9afe189a99103487547210"},
{file = "marisa_trie-1.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1890cc993149db4aa8242973526589e8133c3f92949b0ac74c2c9a6596707ae3"},
{file = "marisa_trie-1.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:26177cd0dadb7b44f47c17c40e16ac157c4d22ac7ed83b5a47f44713239e10d1"},
{file = "marisa_trie-1.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3425dc81d49a374be49e3a063cb6ccdf57973201b0a30127082acea50562a85e"},
{file = "marisa_trie-1.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:525b8df41a1a7337ed7f982eb63b704d7d75f047e30970fcfbe9cf6fc22c5991"},
{file = "marisa_trie-1.2.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c643c66bbde6a115e4ec8713c087a9fe9cb7b7c684e6af4cf448c120fa427ea4"},
{file = "marisa_trie-1.2.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5a83fe83e0eab9154a2dc7c556898c86584b7779ddf4214c606fce4ceff07c13"},
{file = "marisa_trie-1.2.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:49701db6bb8f1ec0133abd95f0a4891cfd6f84f3bd019e343037e31a5a5b0210"},
{file = "marisa_trie-1.2.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a3f0562863deaad58c5dc3a51f706da92582bc9084189148a45f7a12fe261a51"},
{file = "marisa_trie-1.2.0-cp311-cp311-win32.whl", hash = "sha256:b08968ccad00f54f31e38516e4452fae59dd15a3fcee56aea3101ba2304680b3"},
{file = "marisa_trie-1.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:d3ef375491e7dd71a0a7e7bf288c88750942bd1ee0c379dcd6ad43e31af67d00"},
{file = "marisa_trie-1.2.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:39b88f126988ea83e8458259297d2b2f9391bfba8f4dc5d7a246813aae1c1def"},
{file = "marisa_trie-1.2.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ec167b006884a90d130ee30518a9aa44cb40211f702bf07031b2d7d4d1db569b"},
{file = "marisa_trie-1.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1b855e6286faef5411386bf9d676dfb545c09f7d109f197f347c9366aeb12f07"},
{file = "marisa_trie-1.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8cd287ff323224d87c2b739cba39614aac3737c95a254e0ff70e77d9b8df226d"},
{file = "marisa_trie-1.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d8a1c0361165231f4fb915237470afc8cc4803c535f535f4fc42ca72855b124"},
{file = "marisa_trie-1.2.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3267f438d258d7d85ee3dde363c4f96c3196ca9cd9e63fe429a59543cc544b15"},
{file = "marisa_trie-1.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7c87a0c2cccce12b07bfcb70708637c0816970282d966a1531ecda1a24bd1cc8"},
{file = "marisa_trie-1.2.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:d3c0e38f0501951e2322f7274a39b8e2344bbd91ceaa9da439f46022570ddc9d"},
{file = "marisa_trie-1.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:cd88a338c87e6dc130b6cea7b697580c21f0c83a8a8b46671cfecbb713d3fe24"},
{file = "marisa_trie-1.2.0-cp312-cp312-win32.whl", hash = "sha256:5cea60975184f03fbcff51339df0eb44d2abe106a1693983cc64415eb87b897b"},
{file = "marisa_trie-1.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:b04a07b99b62b9bdf3eaf1d44571a3293ce249ce8971944e780c9c709593462f"},
{file = "marisa_trie-1.2.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c11af35d9304de420b359741e12b885d04f11403697efcbbe8cb50f834261ebc"},
{file = "marisa_trie-1.2.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2db8e74493c3bffb480c54afaa88890a39bf90063ff5b322acf64bf076e4b36e"},
{file = "marisa_trie-1.2.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9bcc6613bc873136dc62609b66aaa27363e2bd46c03fdab62d638f7cf69d5f82"},
{file = "marisa_trie-1.2.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f5cb731581effb3e05258f3ddc2a155475de74bb00f61eb280f991e13b48f783"},
{file = "marisa_trie-1.2.0-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:eba1061bbeaeec4149282beab2ae163631606f119f549a10246b014e13f9047b"},
{file = "marisa_trie-1.2.0-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:015594427360c6ad0fa94d51ee3d50fb83b0f7278996497fd2d69f877c3de9bd"},
{file = "marisa_trie-1.2.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:36d65bcbf22a70cdd0202bd8608c2feecc58bdb9e5dd9a2f5a723b651fcab287"},
{file = "marisa_trie-1.2.0-cp37-cp37m-win32.whl", hash = "sha256:bc138625b383998f5cd0cbf6cd38d66d414f3786ae6d7b4e4a6fc970140ef4e9"},
{file = "marisa_trie-1.2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:27d270a64eb655754dfb4e352c60a084b16ab999b3a97a0cdc7dbecbca3c0e35"},
{file = "marisa_trie-1.2.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:fa1fa7f67d317a921315a65e266b9e156ce5a956076ec2b6dbf72d67c7df8216"},
{file = "marisa_trie-1.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9dccef41d4af11a03558c1d101de58bd723b3039a5bc4e064250008c118037ec"},
{file = "marisa_trie-1.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:873efd212dfef2b736ff2ff43e10b348c428d5dbac7b8cb8aa777004bc8c7b0e"},
{file = "marisa_trie-1.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8af7a21ac2ba6dc23e4257fc3a40b3070e776275d3d0b5b2ef44473ad92caf3a"},
{file = "marisa_trie-1.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7202ba0ca1db5245feaebbeb3d0c776b2da1fffb0abc3500dd505f679686aa1"},
{file = "marisa_trie-1.2.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:83d90be28c083323909d23ff8e9b4a2764b9e75520d1bae1a277e9fa7ca20d15"},
{file = "marisa_trie-1.2.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:40e2a374026492ac84232897f1f1d8f92a4a1f8bcf3f0ded1f2b8b708d1acfff"},
{file = "marisa_trie-1.2.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:7c6e6506bd24a5799b9b4b9cf1e8d6fa281f136396ba018a95d95d4d74715227"},
{file = "marisa_trie-1.2.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:437bf6c0d7ba4cf17656a0e3bdd0b3c2c92c01fedfa670904177eef3116a4f45"},
{file = "marisa_trie-1.2.0-cp38-cp38-win32.whl", hash = "sha256:6aeef7b364fb3b34dbba1cc57b79f1668fad0c3f039738d65a5b0d5ddce15f47"},
{file = "marisa_trie-1.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:02f773e85cc566a24c0e0e28c744052db7691c4f13d02e4257bc657a49b9ab14"},
{file = "marisa_trie-1.2.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:6ff705cb3b907bdeacb8c4b3bf0541691f52b101014d189a707ca41ebfacad59"},
{file = "marisa_trie-1.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:006419c59866979188906babc42ae0918081c18cabc2bdabca027f68c081c127"},
{file = "marisa_trie-1.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a7196691681ecb8a12629fb6277c33bafdb27cf2b6c18c28bc48fa42a15eab8f"},
{file = "marisa_trie-1.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eaf052c0a1f4531ee12fd4c637212e77ad2af8c3b38a0d3096622abd01a22212"},
{file = "marisa_trie-1.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fb95f3ab95ba933f6a2fa2629185e9deb9da45ff2aa4ba8cc8f722528c038ef"},
{file = "marisa_trie-1.2.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7459b1e1937e33daed65a6d55f8b95f9a8601f4f8749d01641cf548ecac03840"},
{file = "marisa_trie-1.2.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:902ea948677421093651ca98df62d255383f865f7c353f956ef666e92500e79f"},
{file = "marisa_trie-1.2.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:fdf7a2d066907816726f3bf241b8cb05b698d6ffaa3c5ea2658d4ba69e87ec57"},
{file = "marisa_trie-1.2.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:3540bb85b38dfc17060263e061c95a0a435681b04543d1ae7e8d7441a9790593"},
{file = "marisa_trie-1.2.0-cp39-cp39-win32.whl", hash = "sha256:fe1394e1f262e5b45d22d30bd1ef75174d1f2772e86716b5f93f9c29dfc1a779"},
{file = "marisa_trie-1.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:84c44cb13803723f0f76aa2ba1a657f762a0bb9d8a9b80dfff249bb1c3218dd6"},
{file = "marisa_trie-1.2.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:035c4c8f3b313b4d7b7451ddd539da811a11077a9e359c6a0345f816b1bdccb3"},
{file = "marisa_trie-1.2.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d4f05c2ee218a5ab09d269b640d06f9708b0cf37c842344cbdffb0661c74c472"},
{file = "marisa_trie-1.2.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:92ac63e1519598de946c7d9346df3bb52ed96968eb3021b4e89b51d79bc72a86"},
{file = "marisa_trie-1.2.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:045f32eaeb5dcdb5beadb571ba616d7a34141764b616eebb4decce71b366f5fa"},
{file = "marisa_trie-1.2.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cb60c2f9897ce2bfc31a69ac25a040de4f8643ab2a339bb0ff1185e1a9dedaf8"},
{file = "marisa_trie-1.2.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:f19c5fcf23c02f1303deb69c67603ee37ed8f01de2d8b19f1716a6cf5afd5455"},
{file = "marisa_trie-1.2.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a06a77075240eb83a47b780902322e66c968a06a2b6318cab06757c65ea64190"},
{file = "marisa_trie-1.2.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:125016400449e46ec0e5fabd14c8314959c4dfa02ffc2861195c99efa2b5b011"},
{file = "marisa_trie-1.2.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c57647dd9f9ba16fc5bb4679c915d7d48d5c0b25134fb10f095ccd839686a027"},
{file = "marisa_trie-1.2.0-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6601e74338fb31e1b20674257706150113463182a01d3a1310df6b8840720b17"},
{file = "marisa_trie-1.2.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:ce2f68e1000c4c72820c5b2c9d037f326fcf75f036453a5e629f225f99b92cfc"},
{file = "marisa_trie-1.2.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:069ac10a133d96b3f3ed1cc071b973a3f28490345e7941c778a1d81cf176f04a"},
{file = "marisa_trie-1.2.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:de9911480ce2a0513582cb84ee4484e5ee8791e692276c7f5cd7378e114d1988"},
{file = "marisa_trie-1.2.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4cfec001cf233e8853a29e1c2bb74031c217aa61e7bd19389007e04861855731"},
{file = "marisa_trie-1.2.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd1f3ef8de89684fbdd6aaead09d53b82e718bad4375d2beb938cbd24b48c51a"},
{file = "marisa_trie-1.2.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:65f5d8c1ecc85283b5b03a1475a5da723b94b3beda752c895b2f748477d8f1b1"},
{file = "marisa_trie-1.2.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:2e7540f844c1de493a90ad7d0f5bffc6a2cba19fe312d6db7b97aceff11d97f8"},
{file = "marisa_trie-1.2.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:2fb9243f66563285677079c9dccc697d35985287bacb36c8e685305687b0e025"},
{file = "marisa_trie-1.2.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:58e2b84cbb6394f9c567f1f4351fc2995a094e1b684da9b577d4139b145401d6"},
{file = "marisa_trie-1.2.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b4a8d3ed1f1b8f551b52e11a1265eaf0718f06bb206654b2c529cecda0913dd"},
{file = "marisa_trie-1.2.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a97652c5fbc92f52100afe1c4583625015611000fa81606ad17f1b3bbb9f3bfa"},
{file = "marisa_trie-1.2.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7183d84da20c89b2a366bf581f0d79d1e248909678f164e8536f291120432e8"},
{file = "marisa_trie-1.2.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:c7f4df4163202b0aa5dad3eeddf088ecb61e9101986c8b31f1e052ebd6df9292"},
{file = "marisa_trie-1.2.0.tar.gz", hash = "sha256:fedfc67497f8aa2757756b5cf493759f245d321fb78914ce125b6d75daa89b5f"},
]
[package.dependencies]
setuptools = "*"
[package.extras]
test = ["hypothesis", "pytest", "readme-renderer"]
[package.source]
type = "legacy"
url = "https://pypi.org/simple"
reference = "localpypi"
[[package]]
name = "pillow"
version = "10.4.0"
description = "Python Imaging Library (Fork)"
optional = false
python-versions = ">=3.8"
files = [
{file = "pillow-10.4.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:4d9667937cfa347525b319ae34375c37b9ee6b525440f3ef48542fcf66f2731e"},
{file = "pillow-10.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:543f3dc61c18dafb755773efc89aae60d06b6596a63914107f75459cf984164d"},
{file = "pillow-10.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7928ecbf1ece13956b95d9cbcfc77137652b02763ba384d9ab508099a2eca856"},
{file = "pillow-10.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4d49b85c4348ea0b31ea63bc75a9f3857869174e2bf17e7aba02945cd218e6f"},
{file = "pillow-10.4.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:6c762a5b0997f5659a5ef2266abc1d8851ad7749ad9a6a5506eb23d314e4f46b"},
{file = "pillow-10.4.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a985e028fc183bf12a77a8bbf36318db4238a3ded7fa9df1b9a133f1cb79f8fc"},
{file = "pillow-10.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:812f7342b0eee081eaec84d91423d1b4650bb9828eb53d8511bcef8ce5aecf1e"},
{file = "pillow-10.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ac1452d2fbe4978c2eec89fb5a23b8387aba707ac72810d9490118817d9c0b46"},
{file = "pillow-10.4.0-cp310-cp310-win32.whl", hash = "sha256:bcd5e41a859bf2e84fdc42f4edb7d9aba0a13d29a2abadccafad99de3feff984"},
{file = "pillow-10.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:ecd85a8d3e79cd7158dec1c9e5808e821feea088e2f69a974db5edf84dc53141"},
{file = "pillow-10.4.0-cp310-cp310-win_arm64.whl", hash = "sha256:ff337c552345e95702c5fde3158acb0625111017d0e5f24bf3acdb9cc16b90d1"},
{file = "pillow-10.4.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:0a9ec697746f268507404647e531e92889890a087e03681a3606d9b920fbee3c"},
{file = "pillow-10.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dfe91cb65544a1321e631e696759491ae04a2ea11d36715eca01ce07284738be"},
{file = "pillow-10.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5dc6761a6efc781e6a1544206f22c80c3af4c8cf461206d46a1e6006e4429ff3"},
{file = "pillow-10.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e84b6cc6a4a3d76c153a6b19270b3526a5a8ed6b09501d3af891daa2a9de7d6"},
{file = "pillow-10.4.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:bbc527b519bd3aa9d7f429d152fea69f9ad37c95f0b02aebddff592688998abe"},
{file = "pillow-10.4.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:76a911dfe51a36041f2e756b00f96ed84677cdeb75d25c767f296c1c1eda1319"},
{file = "pillow-10.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:59291fb29317122398786c2d44427bbd1a6d7ff54017075b22be9d21aa59bd8d"},
{file = "pillow-10.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:416d3a5d0e8cfe4f27f574362435bc9bae57f679a7158e0096ad2beb427b8696"},
{file = "pillow-10.4.0-cp311-cp311-win32.whl", hash = "sha256:7086cc1d5eebb91ad24ded9f58bec6c688e9f0ed7eb3dbbf1e4800280a896496"},
{file = "pillow-10.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cbed61494057c0f83b83eb3a310f0bf774b09513307c434d4366ed64f4128a91"},
{file = "pillow-10.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:f5f0c3e969c8f12dd2bb7e0b15d5c468b51e5017e01e2e867335c81903046a22"},
{file = "pillow-10.4.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:673655af3eadf4df6b5457033f086e90299fdd7a47983a13827acf7459c15d94"},
{file = "pillow-10.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:866b6942a92f56300012f5fbac71f2d610312ee65e22f1aa2609e491284e5597"},
{file = "pillow-10.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29dbdc4207642ea6aad70fbde1a9338753d33fb23ed6956e706936706f52dd80"},
{file = "pillow-10.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf2342ac639c4cf38799a44950bbc2dfcb685f052b9e262f446482afaf4bffca"},
{file = "pillow-10.4.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:f5b92f4d70791b4a67157321c4e8225d60b119c5cc9aee8ecf153aace4aad4ef"},
{file = "pillow-10.4.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:86dcb5a1eb778d8b25659d5e4341269e8590ad6b4e8b44d9f4b07f8d136c414a"},
{file = "pillow-10.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:780c072c2e11c9b2c7ca37f9a2ee8ba66f44367ac3e5c7832afcfe5104fd6d1b"},
{file = "pillow-10.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:37fb69d905be665f68f28a8bba3c6d3223c8efe1edf14cc4cfa06c241f8c81d9"},
{file = "pillow-10.4.0-cp312-cp312-win32.whl", hash = "sha256:7dfecdbad5c301d7b5bde160150b4db4c659cee2b69589705b6f8a0c509d9f42"},
{file = "pillow-10.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:1d846aea995ad352d4bdcc847535bd56e0fd88d36829d2c90be880ef1ee4668a"},
{file = "pillow-10.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:e553cad5179a66ba15bb18b353a19020e73a7921296a7979c4a2b7f6a5cd57f9"},
{file = "pillow-10.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8bc1a764ed8c957a2e9cacf97c8b2b053b70307cf2996aafd70e91a082e70df3"},
{file = "pillow-10.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6209bb41dc692ddfee4942517c19ee81b86c864b626dbfca272ec0f7cff5d9fb"},
{file = "pillow-10.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bee197b30783295d2eb680b311af15a20a8b24024a19c3a26431ff83eb8d1f70"},
{file = "pillow-10.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ef61f5dd14c300786318482456481463b9d6b91ebe5ef12f405afbba77ed0be"},
{file = "pillow-10.4.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:297e388da6e248c98bc4a02e018966af0c5f92dfacf5a5ca22fa01cb3179bca0"},
{file = "pillow-10.4.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:e4db64794ccdf6cb83a59d73405f63adbe2a1887012e308828596100a0b2f6cc"},
{file = "pillow-10.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bd2880a07482090a3bcb01f4265f1936a903d70bc740bfcb1fd4e8a2ffe5cf5a"},
{file = "pillow-10.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4b35b21b819ac1dbd1233317adeecd63495f6babf21b7b2512d244ff6c6ce309"},
{file = "pillow-10.4.0-cp313-cp313-win32.whl", hash = "sha256:551d3fd6e9dc15e4c1eb6fc4ba2b39c0c7933fa113b220057a34f4bb3268a060"},
{file = "pillow-10.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:030abdbe43ee02e0de642aee345efa443740aa4d828bfe8e2eb11922ea6a21ea"},
{file = "pillow-10.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:5b001114dd152cfd6b23befeb28d7aee43553e2402c9f159807bf55f33af8a8d"},
{file = "pillow-10.4.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:8d4d5063501b6dd4024b8ac2f04962d661222d120381272deea52e3fc52d3736"},
{file = "pillow-10.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7c1ee6f42250df403c5f103cbd2768a28fe1a0ea1f0f03fe151c8741e1469c8b"},
{file = "pillow-10.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b15e02e9bb4c21e39876698abf233c8c579127986f8207200bc8a8f6bb27acf2"},
{file = "pillow-10.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a8d4bade9952ea9a77d0c3e49cbd8b2890a399422258a77f357b9cc9be8d680"},
{file = "pillow-10.4.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:43efea75eb06b95d1631cb784aa40156177bf9dd5b4b03ff38979e048258bc6b"},
{file = "pillow-10.4.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:950be4d8ba92aca4b2bb0741285a46bfae3ca699ef913ec8416c1b78eadd64cd"},
{file = "pillow-10.4.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d7480af14364494365e89d6fddc510a13e5a2c3584cb19ef65415ca57252fb84"},
{file = "pillow-10.4.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:73664fe514b34c8f02452ffb73b7a92c6774e39a647087f83d67f010eb9a0cf0"},
{file = "pillow-10.4.0-cp38-cp38-win32.whl", hash = "sha256:e88d5e6ad0d026fba7bdab8c3f225a69f063f116462c49892b0149e21b6c0a0e"},
{file = "pillow-10.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:5161eef006d335e46895297f642341111945e2c1c899eb406882a6c61a4357ab"},
{file = "pillow-10.4.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:0ae24a547e8b711ccaaf99c9ae3cd975470e1a30caa80a6aaee9a2f19c05701d"},
{file = "pillow-10.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:298478fe4f77a4408895605f3482b6cc6222c018b2ce565c2b6b9c354ac3229b"},
{file = "pillow-10.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:134ace6dc392116566980ee7436477d844520a26a4b1bd4053f6f47d096997fd"},
{file = "pillow-10.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:930044bb7679ab003b14023138b50181899da3f25de50e9dbee23b61b4de2126"},
{file = "pillow-10.4.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:c76e5786951e72ed3686e122d14c5d7012f16c8303a674d18cdcd6d89557fc5b"},
{file = "pillow-10.4.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:b2724fdb354a868ddf9a880cb84d102da914e99119211ef7ecbdc613b8c96b3c"},
{file = "pillow-10.4.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:dbc6ae66518ab3c5847659e9988c3b60dc94ffb48ef9168656e0019a93dbf8a1"},
{file = "pillow-10.4.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:06b2f7898047ae93fad74467ec3d28fe84f7831370e3c258afa533f81ef7f3df"},
{file = "pillow-10.4.0-cp39-cp39-win32.whl", hash = "sha256:7970285ab628a3779aecc35823296a7869f889b8329c16ad5a71e4901a3dc4ef"},
{file = "pillow-10.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:961a7293b2457b405967af9c77dcaa43cc1a8cd50d23c532e62d48ab6cdd56f5"},
{file = "pillow-10.4.0-cp39-cp39-win_arm64.whl", hash = "sha256:32cda9e3d601a52baccb2856b8ea1fc213c90b340c542dcef77140dfa3278a9e"},
{file = "pillow-10.4.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5b4815f2e65b30f5fbae9dfffa8636d992d49705723fe86a3661806e069352d4"},
{file = "pillow-10.4.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:8f0aef4ef59694b12cadee839e2ba6afeab89c0f39a3adc02ed51d109117b8da"},
{file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f4727572e2918acaa9077c919cbbeb73bd2b3ebcfe033b72f858fc9fbef0026"},
{file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff25afb18123cea58a591ea0244b92eb1e61a1fd497bf6d6384f09bc3262ec3e"},
{file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:dc3e2db6ba09ffd7d02ae9141cfa0ae23393ee7687248d46a7507b75d610f4f5"},
{file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:02a2be69f9c9b8c1e97cf2713e789d4e398c751ecfd9967c18d0ce304efbf885"},
{file = "pillow-10.4.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:0755ffd4a0c6f267cccbae2e9903d95477ca2f77c4fcf3a3a09570001856c8a5"},
{file = "pillow-10.4.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:a02364621fe369e06200d4a16558e056fe2805d3468350df3aef21e00d26214b"},
{file = "pillow-10.4.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:1b5dea9831a90e9d0721ec417a80d4cbd7022093ac38a568db2dd78363b00908"},
{file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b885f89040bb8c4a1573566bbb2f44f5c505ef6e74cec7ab9068c900047f04b"},
{file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87dd88ded2e6d74d31e1e0a99a726a6765cda32d00ba72dc37f0651f306daaa8"},
{file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:2db98790afc70118bd0255c2eeb465e9767ecf1f3c25f9a1abb8ffc8cfd1fe0a"},
{file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f7baece4ce06bade126fb84b8af1c33439a76d8a6fd818970215e0560ca28c27"},
{file = "pillow-10.4.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:cfdd747216947628af7b259d274771d84db2268ca062dd5faf373639d00113a3"},
{file = "pillow-10.4.0.tar.gz", hash = "sha256:166c1cd4d24309b30d61f79f4a9114b7b2313d7450912277855ff5dfd7cd4a06"},
]
[package.extras]
docs = ["furo", "olefile", "sphinx (>=7.3)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinxext-opengraph"]
fpx = ["olefile"]
mic = ["olefile"]
tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"]
typing = ["typing-extensions"]
xmp = ["defusedxml"]
[package.source]
type = "legacy"
url = "https://pypi.org/simple"
reference = "localpypi"
[[package]]
name = "pymupdf"
version = "1.24.7"
description = "A high performance Python library for data extraction, analysis, conversion & manipulation of PDF (and other) documents."
optional = false
python-versions = ">=3.8"
files = [
{file = "PyMuPDF-1.24.7-cp310-none-macosx_10_9_x86_64.whl", hash = "sha256:9b6984d57e127e016231b2e89b247f2b0fef07af84d4cc91e4893c6d3fde52c7"},
{file = "PyMuPDF-1.24.7-cp310-none-macosx_11_0_arm64.whl", hash = "sha256:7b8405aac8011b445ceb1bc2d4f4c44b2c969024b301f1178399e0084541d747"},
{file = "PyMuPDF-1.24.7-cp310-none-manylinux2014_aarch64.whl", hash = "sha256:ada392c739f43608611d430305c92d1b8922f66a07bc36a4ea0c7f3f9c1b5dd2"},
{file = "PyMuPDF-1.24.7-cp310-none-manylinux2014_x86_64.whl", hash = "sha256:7a59e24873e6d135f9c07be9f47d98502171210957819c8ffa9a7cca1cac1fb8"},
{file = "PyMuPDF-1.24.7-cp310-none-musllinux_1_2_x86_64.whl", hash = "sha256:bdd9a1b703e3fedd9836d54a13b89ddf772a6eaea3b1e34dcd682b8f0b5b7123"},
{file = "PyMuPDF-1.24.7-cp310-none-win32.whl", hash = "sha256:3a2e8af6e2ef437c4e599c7e520299ea10ef02680be0a8533f78d6ee489c5a60"},
{file = "PyMuPDF-1.24.7-cp310-none-win_amd64.whl", hash = "sha256:8293773e973cf07f5d6398699d5d98151a025c9db04a333fd55a87c8f8c3c74b"},
{file = "PyMuPDF-1.24.7-cp311-none-macosx_10_9_x86_64.whl", hash = "sha256:11770619a1d5b90f5f81cc22c11967b2f473310fc9a8f2aa96c1aabff5971c63"},
{file = "PyMuPDF-1.24.7-cp311-none-macosx_11_0_arm64.whl", hash = "sha256:5e9b6018b3af45c6ca04adb0441369eefc0d66e5023f8d54befde9bb4f078c5a"},
{file = "PyMuPDF-1.24.7-cp311-none-manylinux2014_aarch64.whl", hash = "sha256:40743cbfa40a35e873d4b8642709f45849b3315292957d836be647cdd74d1107"},
{file = "PyMuPDF-1.24.7-cp311-none-manylinux2014_x86_64.whl", hash = "sha256:44f4c58040c496fcfc0719df17dab60348c7988d874c5e17233fa664dcf0a984"},
{file = "PyMuPDF-1.24.7-cp311-none-musllinux_1_2_x86_64.whl", hash = "sha256:0d7e8d2155bdc6e61242bdbf5dff2f6d0118f1a3f9e5d83db334a5cf71c90c5f"},
{file = "PyMuPDF-1.24.7-cp311-none-win32.whl", hash = "sha256:3ea0bb27512e72225822d98b6747b51b02de84cc1e17a5579d90ad957760de03"},
{file = "PyMuPDF-1.24.7-cp311-none-win_amd64.whl", hash = "sha256:785c8a40fd5d76b47759f4ade6db0b78a799bb1fb2dd61bbe59784538299ee21"},
{file = "PyMuPDF-1.24.7-cp312-none-macosx_10_9_x86_64.whl", hash = "sha256:999f40a6ead5aaad0c9e23b3c2ed009080bf51be9485ae122ff2cedd6487874b"},
{file = "PyMuPDF-1.24.7-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:03facd26907dcbba63429e8b56fdbabd717f22fb6d24b38bdefc591b309ce437"},
{file = "PyMuPDF-1.24.7-cp312-none-manylinux2014_aarch64.whl", hash = "sha256:7ec6da974092de0f842f9ef0e355c58a5529c2d5ec792709d2c6fec5ffcd80b1"},
{file = "PyMuPDF-1.24.7-cp312-none-manylinux2014_x86_64.whl", hash = "sha256:89ccd758b5b7d1e05d615e85fb5d358ff1994ff70aaa5df4f0724bd414847949"},
{file = "PyMuPDF-1.24.7-cp312-none-musllinux_1_2_x86_64.whl", hash = "sha256:8873253ffedb7bcc8dcafa9bfe8f1efa39ac91732ccc579e1c1746eeff4ed83a"},
{file = "PyMuPDF-1.24.7-cp312-none-win32.whl", hash = "sha256:5341440bfc6c89f025990d181896b74097f6b145f5e563e3d5e5fa944c79aae6"},
{file = "PyMuPDF-1.24.7-cp312-none-win_amd64.whl", hash = "sha256:a906b57dae8f3870663f3d3a1d4a66b67cc8f5cf644bcbdd175a391f5f74f2ef"},
{file = "PyMuPDF-1.24.7-cp38-none-macosx_10_9_x86_64.whl", hash = "sha256:b18630fead76175059b973230cc175c4ed30e9f4ea03d25963d2c74440bdb78b"},
{file = "PyMuPDF-1.24.7-cp38-none-macosx_11_0_arm64.whl", hash = "sha256:a94d556b7ea25e6195c46dc2c4114d30e8e51233a49f0ed589f8e0190d2cc3b5"},
{file = "PyMuPDF-1.24.7-cp38-none-manylinux2014_aarch64.whl", hash = "sha256:da82dc784e52b3635110995a75189cdef71b02d9a3a2635cd1d216133d547519"},
{file = "PyMuPDF-1.24.7-cp38-none-manylinux2014_x86_64.whl", hash = "sha256:d95c0cc4768ce09d06c31280cda6fc7619026e615d33beee2dbd1c74ea3a1e08"},
{file = "PyMuPDF-1.24.7-cp38-none-musllinux_1_2_x86_64.whl", hash = "sha256:5c7c964e4b3dfbcd7a0ec11f44090002967c9652cf1dbaf5e09de333c479eae3"},
{file = "PyMuPDF-1.24.7-cp38-none-win32.whl", hash = "sha256:5812645087eaf537efb168df1a78b40b76d6b01c694b890d31e362fd007926fe"},
{file = "PyMuPDF-1.24.7-cp38-none-win_amd64.whl", hash = "sha256:b11065d5192bdc6e80046505ed68447276d3698ec1d0a3eea8dc2533f972f8fb"},
{file = "PyMuPDF-1.24.7-cp39-none-macosx_10_9_x86_64.whl", hash = "sha256:102d3427fcc3a47084eb2faa670de8a58e5d2061b4e61365b323d12a7bac0afd"},
{file = "PyMuPDF-1.24.7-cp39-none-macosx_11_0_arm64.whl", hash = "sha256:5580c975a166a3fb1908fb055c0427bcd23abe7a7d3113777d15c0e5f8a2133b"},
{file = "PyMuPDF-1.24.7-cp39-none-manylinux2014_aarch64.whl", hash = "sha256:1553df16e6e141314fa33534800aee2bc4eab1daa8057bf791584b318aa114f6"},
{file = "PyMuPDF-1.24.7-cp39-none-manylinux2014_x86_64.whl", hash = "sha256:53cedffedc7ec5c019f8b06d0e18b1f410f4af15e0c122330b79d5e617739042"},
{file = "PyMuPDF-1.24.7-cp39-none-musllinux_1_2_x86_64.whl", hash = "sha256:371b983ba2b396a636518e2cfaa76ea8cccd38c9bdbc942256df678e421f7063"},
{file = "PyMuPDF-1.24.7-cp39-none-win32.whl", hash = "sha256:0a93d0f5c5b5cdc2f1718dbdf4b1c34aafc68ee4e44688b58c0a2894277ca941"},
{file = "PyMuPDF-1.24.7-cp39-none-win_amd64.whl", hash = "sha256:f82db6fd757fb3698075e2e68e4f6df7ae89b82a96ae102e178e5b01a250359b"},
{file = "PyMuPDF-1.24.7.tar.gz", hash = "sha256:a34ceae204f215bad51f49dd43987116c6a6269fc03d8770224f7067013b59b8"},
]
[package.dependencies]
PyMuPDFb = "1.24.6"
[package.source]
type = "legacy"
url = "https://pypi.org/simple"
reference = "localpypi"
[[package]]
name = "pymupdfb"
version = "1.24.6"
description = "MuPDF shared libraries for PyMuPDF."
optional = false
python-versions = ">=3.8"
files = [
{file = "PyMuPDFb-1.24.6-py3-none-macosx_10_9_x86_64.whl", hash = "sha256:21e3ed890f736def68b9a031122ae1fb854d5cb9a53aa144b6e2ca3092416a6b"},
{file = "PyMuPDFb-1.24.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:8704d2dfadc9448ce184597d8b0f9c30143e379ac948a517f9c4db7c0c71ed51"},
{file = "PyMuPDFb-1.24.6-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:01662584d5cfa7a91f77585f13fc23a12291cfd76a57e0a28dd5a56bf521cb2c"},
{file = "PyMuPDFb-1.24.6-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e1f7657353529ae3f88575c83ee49eac9adea311a034b9c97248a65cee7df0e5"},
{file = "PyMuPDFb-1.24.6-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:cebc2cedb870d1e1168e2f502eb06f05938f6df69103b0853a2b329611ec19a7"},
{file = "PyMuPDFb-1.24.6-py3-none-win32.whl", hash = "sha256:ac4b865cd1e239db04674f85e02844a0e405f8255ee7a74dfee0d86aad0d3576"},
{file = "PyMuPDFb-1.24.6-py3-none-win_amd64.whl", hash = "sha256:9224e088a0d3c188dea03831807789e245b812fbd071c8d498da8f7cc33142b2"},
{file = "PyMuPDFb-1.24.6.tar.gz", hash = "sha256:f5a40b1732d65a1e519916d698858b9ce7473e23edf9001ddd085c5293d59d30"},
]
[package.source]
type = "legacy"
url = "https://pypi.org/simple"
reference = "localpypi"
[[package]]
name = "setuptools"
version = "71.0.3"
description = "Easily download, build, install, upgrade, and uninstall Python packages"
optional = false
python-versions = ">=3.8"
files = [
{file = "setuptools-71.0.3-py3-none-any.whl", hash = "sha256:f501b6e6db709818dc76882582d9c516bf3b67b948864c5fa1d1624c09a49207"},
{file = "setuptools-71.0.3.tar.gz", hash = "sha256:3d8531791a27056f4a38cd3e54084d8b1c4228ff9cf3f2d7dd075ec99f9fd70d"},
]
[package.extras]
core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.text (>=3.7)", "more-itertools (>=8.8)", "ordered-set (>=3.1.1)", "packaging (>=24)", "platformdirs (>=2.6.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"]
doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (<7.4)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"]
test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "mypy (==1.10.0)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (<0.4)", "pytest-ruff (>=0.2.1)", "pytest-ruff (>=0.3.2)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"]
[package.source]
type = "legacy"
url = "https://pypi.org/simple"
reference = "localpypi"
[[package]]
name = "xmltodict"
version = "0.13.0"
description = "Makes working with XML feel like you are working with JSON"
optional = false
python-versions = ">=3.4"
files = [
{file = "xmltodict-0.13.0-py2.py3-none-any.whl", hash = "sha256:aa89e8fd76320154a40d19a0df04a4695fb9dc5ba977cbb68ab3e4eb225e7852"},
{file = "xmltodict-0.13.0.tar.gz", hash = "sha256:341595a488e3e01a85a9d8911d8912fd922ede5fecc4dce437eb4b6c8d037e56"},
]
[package.source]
type = "legacy"
url = "https://pypi.org/simple"
reference = "localpypi"
[metadata]
lock-version = "2.0"
python-versions = "^3.8"
content-hash = "080fe35089770ecee25895cdd4ee4aa14b9e9c818c199908f066bc94c4470530"
+12 -10
View File
@@ -4,12 +4,13 @@ build-backend = "poetry.core.masonry.api"
[tool.poetry] [tool.poetry]
name = "cbz" name = "cbz"
version = "3.3.3" version = "3.4.1"
description = "CBZ simplifies creating, managing, and viewing comic book files in CBZ format, offering seamless packaging, metadata handling, and built-in viewing capabilities" description = "CBZ simplifies creating, managing, and viewing comic book files in CBZ, CBR, and PDF formats, offering seamless packaging, metadata handling and built-in viewing capabilities."
license = "MIT" license = "MIT"
authors = ["hyugogirubato <65763543+hyugogirubato@users.noreply.github.com>"] authors = ["hyugogirubato <65763543+hyugogirubato@users.noreply.github.com>"]
readme = "README.md" readme = "README.md"
keywords = ["python", "cbz", "ebooks", "manga", "comics", "webtoons"] repository = "https://github.com/hyugogirubato/cbz"
keywords = ["python", "cbz", "cbr", "pdf", "ebooks", "manga", "comics", "webtoons"]
classifiers = [ classifiers = [
"Development Status :: 5 - Production/Stable", "Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers", "Intended Audience :: Developers",
@@ -17,7 +18,7 @@ classifiers = [
"Natural Language :: English", "Natural Language :: English",
"Operating System :: OS Independent", "Operating System :: OS Independent",
"Topic :: Utilities", "Topic :: Utilities",
"Topic :: Software Development :: Libraries :: Python Modules" "Topic :: Software Development :: Libraries :: Python Modules",
] ]
include = [ include = [
{ path = "CHANGELOG.md", format = "sdist" }, { path = "CHANGELOG.md", format = "sdist" },
@@ -30,11 +31,12 @@ include = [
"Changelog" = "https://github.com/hyugogirubato/cbz/blob/main/CHANGELOG.md" "Changelog" = "https://github.com/hyugogirubato/cbz/blob/main/CHANGELOG.md"
[tool.poetry.dependencies] [tool.poetry.dependencies]
python = "^3.8" python = ">=3.8,<4.0"
xmltodict = "^0.13.0" langcodes = ">=3.4.0"
langcodes = "^3.4.0" Pillow = ">=10.4.0"
Pillow = "^10.4.0" pypdf = ">=5.7.0"
PyMuPDF = "^1.24.7" rarfile = ">=4.2"
xmltodict = ">=0.14.2"
[tool.poetry.scripts] [tool.poetry.scripts]
cbzplayer = "cbz.__main__:main" cbzplayer = "cbz.__main__:main"
@@ -45,4 +47,4 @@ url = "https://pypi.org/simple/"
priority = "primary" priority = "primary"
[certificates] [certificates]
localpypi = { cert = false } localpypi = { cert = false }