Skip to content

Commit bb68a0c

Browse files
committed
feat: Add support for MOBI and AZW file formats
1 parent dafd6eb commit bb68a0c

File tree

4 files changed

+41
-15
lines changed

4 files changed

+41
-15
lines changed

README.md

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,17 @@ The resulting PDF files are optimized to resemble Japanese manga in terms of pag
1212

1313
## Requirement
1414
The script uses the Python libraries **[img2pdf](https://pypi.org/project/img2pdf/)** and **[pikepdf](https://pypi.org/project/pikepdf/)** to do the conversion.
15-
Moreover, it uses **[lxml](https://pypi.org/project/lxml/)** to read the EPUB files and **[rarfile](https://pypi.org/project/rarfile/)** to read the RAR archive files.
15+
Moreover, it uses **[lxml](https://pypi.org/project/lxml/)** to read the EPUB files and **[rarfile](https://pypi.org/project/rarfile/)** to read the RAR archive files, and **[mobi](https://pypi.org/project/mobi/)** to handle both MOBI and AZW files.
1616

1717
It requires the installation of these packages in order to work properly.
1818

1919
**Note**
20-
- This script can only handle DRM-free fixed-layout EPUB files.
20+
- This script can only handle DRM-free fixed-layout EPUB, MOBI, and AZW3 files.
2121
- Please ensure that the image files you input are named in numerical order according to their page sequence. For example, `page_01.jpg`, `page_02.jpg`, `page_03.jpg`, and so on, or `001.jpg`, `002.jpg`, `003.jpg`, and so on. This will ensure that the pages are converted and compiled in the correct order.
2222

2323
## Usage
24-
This script can take input in the form of `zip`, `cbz`, `rar`, `cbr`, `epub` files or directories containing images (`jpg`, `jpeg`, `png`, `gif`, `bmp`) of manga or comic pages.
25-
2624
The program can be executed from the command line with the following options:
27-
- The `input_path` argument represents the path to the input file. To execute the Python script correctly, specify the `input_path` argument as the path to the input file containing manga or comic images in any of the supported formats, such as `zip`, `cbz`, `rar`, `cbr`, `epub`, or a directory containing images in formats such as `jpg`, `jpeg`, `png`, `gif`, or `bmp`.
25+
- The `input_path` argument is the path to the input file. To execute the Python script correctly, specify the `input_path` argument as the path to the input file containing manga or comic images in one of the supported formats, such as `zip`, `cbz`, `rar`, `cbr`, `epub`, `mobi`, `azw`, or a directory containing images in formats such as `jpg`, `jpeg`, `png`, `gif`, or `bmp`.
2826
- The `output_path` argument is the path to the output PDF file. To use the script, simply run the Python script with the path to the input file or directory as the argument. If the `--output` option is not specified, the output file name will be automatically generated based on the name of the input file or directory.
2927
- The `pagelayout` parameter can take in the following values:
3028
- `SinglePage` -> Single page display

setup.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
from setuptools import setup, find_packages
22

3-
VERSION = "0.2.0"
3+
VERSION = "0.2.1"
44

55
INSTALL_REQUIRES = (
66
"lxml",
7+
"mobi",
78
"numpy",
89
"img2pdf",
910
"Pillow",

src/manga2pdf.py

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,14 @@
66
import os
77
import re
88
import sys
9+
import mobi
10+
import shutil
911
import img2pdf
1012
import pikepdf
11-
import tempfile
1213
import rarfile
1314
import zipfile
1415
import argparse
16+
import tempfile
1517
import warnings
1618
import numpy as np
1719
from PIL import Image
@@ -38,6 +40,11 @@ def set_convert_to_grayscale(self, flag):
3840
def is_image_file(self, filename):
3941
return any(filename.lower().endswith(ext) for ext in ['.jpg', '.jpeg', '.png', '.gif', '.bmp'])
4042

43+
# Function to determine whether the given path is a mobi filr or azw file or not
44+
def is_mobi_file(self, path):
45+
ext = os.path.splitext(path)[1].lower()
46+
return ext in ['.mobi', '.azw3', '.azw']
47+
4148
# Function to determine whether the given path is an epub file or not
4249
def is_epub_file(self, path):
4350
ext = os.path.splitext(path)[1].lower()
@@ -241,10 +248,27 @@ def extract_epub_metadata(self, epub, opf_name: str):
241248
else:
242249
epub_metadata[key] = None
243250
return epub_metadata
244-
251+
245252
# Function to convert input files to a PDF file
246253
def convert(self):
247-
if self.is_epub_file(self.input_path):
254+
if self.is_mobi_file(self.input_path):
255+
try:
256+
sys.stdout = open(os.devnull, 'w')
257+
tmpfile, self.epub_path = mobi.extract(self.input_path)
258+
sys.stdout.close()
259+
sys.stdout = sys.__stdout__
260+
if self.is_epub_file(self.epub_path):
261+
with zipfile.ZipFile(self.epub_path) as epub:
262+
page_names = self.extract_epub_contents(epub)[0]
263+
page_items = self.extract_epub_contents(epub)[1]
264+
ncx_name = self.extract_epub_contents(epub)[2]
265+
opf_name = self.extract_epub_contents(epub)[3]
266+
page_index = self.extract_epub_index(epub, page_names, ncx_name)
267+
epub_metadata = self.extract_epub_metadata(epub, opf_name)
268+
shutil.rmtree(tmpfile)
269+
except Exception as e:
270+
print(f"Error extracting the MOBI file: {e}")
271+
elif self.is_epub_file(self.input_path):
248272
with zipfile.ZipFile(self.input_path) as epub:
249273
page_names = self.extract_epub_contents(epub)[0]
250274
page_items = self.extract_epub_contents(epub)[1]
@@ -283,7 +307,7 @@ def convert(self):
283307
pdf_obj = io.BytesIO(img2pdf.convert(page_items))
284308

285309
with pikepdf.Pdf.open(pdf_obj) as pdf:
286-
if self.is_epub_file(self.input_path):
310+
if self.is_epub_file(self.input_path) or self.is_epub_file(self.epub_path):
287311
with pdf.open_metadata(set_pikepdf_as_editor=False) as pdf_metadata:
288312
pdf_metadata['dc:title'] = epub_metadata['title'] if epub_metadata['title'] else ''
289313
pdf_metadata['dc:creator'] = epub_metadata['creator'] if epub_metadata['creator'] else ''
@@ -350,7 +374,7 @@ def main():
350374
parser.add_argument('-m', '--pagemode', type=str, default='UseNone',
351375
choices=['UseOutlines', 'UseThumbs', 'FullScreen', 'UseOC', 'UseAttachments'],
352376
help='''\
353-
(default)UseNone -> Neither document outline nor thumbnail images visible
377+
(default) UseNone -> Neither document outline nor thumbnail images visible
354378
UseOutlines -> Document outline visible
355379
UseThumbs -> Thumbnail images visible
356380
FullScreen -> Full-screen mode
@@ -359,7 +383,7 @@ def main():
359383
parser.add_argument('-d', '--direction', type=str, default='R2L', choices=['L2R', 'R2L'],
360384
help='''\
361385
L2R -> Left Binding
362-
(default)R2L -> Right Binding''')
386+
(default) R2L -> Right Binding''')
363387
parser.add_argument('-j', '--jpeg', action='store_true', help='Convert images to JPEG')
364388
parser.add_argument('-g', '--grayscale', action='store_true', help='Convert images to grayscale')
365389
parser.add_argument('-gui', action='store_true', help='Launch GUI')
@@ -375,8 +399,8 @@ def main():
375399
sys.exit(1)
376400
if not os.path.isdir(args.input_path):
377401
ext = os.path.splitext(args.input_path)[1].lower()
378-
if not ext in ['.zip', '.cbz', '.rar', '.cbr', '.epub']:
379-
print('Error: The input file format is not supported. The currently supported formats are: .zip, .cbz, .rar, .cbr, and .epub.')
402+
if not ext in ['.zip', '.cbz', '.rar', '.cbr', '.epub', '.mobi', '.azw3', '.azw']:
403+
print('Error: The input file format is not supported. The currently supported formats are: .zip, .cbz, .rar, .cbr, .mobi, .azw3, .azw, and .epub.')
380404
sys.exit(1)
381405
if args.output_path is not None:
382406
if not args.output_path.endswith('.pdf'):

src/manga2pdf_gui.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ def browse_output_path(self):
220220
self.set_output_path(path)
221221

222222
def browse_input_file(self):
223-
filetypes = (("zip files", "*.zip"), ("cbz files", "*.cbz"), ("rar files", "*.rar"), ("cbr files", "*.cbr"), ("epub files", "*.epub"), ("all files", "*.*"))
223+
filetypes = (("zip files", "*.zip"), ("cbz files", "*.cbz"), ("rar files", "*.rar"), ("cbr files", "*.cbr"), ("epub files", "*.epub"), ("mobi files", "*.mobi"), ("azw files", "*.azw3"), ("azw files", "*.azw"), ("all files", "*.*"))
224224
path = filedialog.askopenfilename(filetypes=filetypes)
225225
if path:
226226
self.input_path = path.replace('/', os.sep)
@@ -368,6 +368,9 @@ def run_convert(self):
368368
messagebox.showerror("Error", complete_error_text[self.language], parent=self.master)
369369

370370
except Exception as e:
371+
# Close process window when done
372+
processing_window.grab_release()
373+
processing_window.destroy()
371374
complete_error_text = {"en": "Conversion failed", "ja": "エラーで変換処理に失敗しました"}
372375
messagebox.showerror(title="Error", message=f"{complete_error_text[self.language]}\n{str(e)}", parent=self.master)
373376

0 commit comments

Comments
 (0)