1
0
mirror of https://github.com/ciromattia/kcc synced 2026-06-30 10:05:25 +00:00

Compare commits

..

1 Commits

Author SHA1 Message Date
Alex Xu 5e68ce380c double webtoon max height 2026-05-31 18:42:31 -07:00
19 changed files with 1379 additions and 1560 deletions
+1 -1
View File
@@ -38,7 +38,7 @@ jobs:
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v7 uses: actions/checkout@v6
# Initializes the CodeQL tools for scanning. # Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL - name: Initialize CodeQL
+1 -1
View File
@@ -21,7 +21,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v7 uses: actions/checkout@v6
- name: Login to GitHub Container Registry - name: Login to GitHub Container Registry
uses: docker/login-action@v4 uses: docker/login-action@v4
+1 -1
View File
@@ -25,7 +25,7 @@ jobs:
build: build:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
steps: steps:
- uses: actions/checkout@v7 - uses: actions/checkout@v6
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
with: with:
+1 -1
View File
@@ -30,7 +30,7 @@ jobs:
env: env:
MACOSX_DEPLOYMENT_TARGET: '14.0' MACOSX_DEPLOYMENT_TARGET: '14.0'
steps: steps:
- uses: actions/checkout@v7 - uses: actions/checkout@v6
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
with: with:
+1 -1
View File
@@ -31,7 +31,7 @@ jobs:
PYTHON_VERSION: 3.11.9 PYTHON_VERSION: 3.11.9
MACOSX_DEPLOYMENT_TARGET: '10.14' MACOSX_DEPLOYMENT_TARGET: '10.14'
steps: steps:
- uses: actions/checkout@v7 - uses: actions/checkout@v6
- name: Get Python - name: Get Python
run: curl https://www.python.org/ftp/python/3.11.9/python-3.11.9-macos11.pkg -o "python.pkg" run: curl https://www.python.org/ftp/python/3.11.9/python-3.11.9-macos11.pkg -o "python.pkg"
- name: Install Python - name: Install Python
+1 -1
View File
@@ -35,7 +35,7 @@ jobs:
command: build_c2p command: build_c2p
runs-on: windows-latest runs-on: windows-latest
steps: steps:
- uses: actions/checkout@v7 - uses: actions/checkout@v6
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
with: with:
+1 -1
View File
@@ -27,7 +27,7 @@ jobs:
env: env:
WINDOWS_7: 1 WINDOWS_7: 1
steps: steps:
- uses: actions/checkout@v7 - uses: actions/checkout@v6
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
with: with:
-5
View File
@@ -239,12 +239,8 @@ MAIN:
Device profile (Available options: K1, K2, K34, K578, KDX, KPW, KPW5, KV, KO, K11, KS, KoMT, KoG, KoGHD, KoA, KoAHD, KoAH2O, KoAO, KoN, KoC, KoCC, KoL, KoLC, KoF, KoS, KoE) Device profile (Available options: K1, K2, K34, K578, KDX, KPW, KPW5, KV, KO, K11, KS, KoMT, KoG, KoGHD, KoA, KoAHD, KoAH2O, KoAO, KoN, KoC, KoCC, KoL, KoLC, KoF, KoS, KoE)
[Default=KV] [Default=KV]
-m, --manga-style Manga style (right-to-left reading and splitting) -m, --manga-style Manga style (right-to-left reading and splitting)
--lightnovel Only resize images and preserve original file structure.
--ebok Force EBOK tag instead of PDOC for MOBI
--invertdirection Invert page turn direction
-q, --hq Try to increase the quality of magnification -q, --hq Try to increase the quality of magnification
-2, --two-panel Display two not four panels in Panel View mode -2, --two-panel Display two not four panels in Panel View mode
--vertical4panel Show side panels first in virtual panel view
-w, --webtoon Webtoon processing mode -w, --webtoon Webtoon processing mode
--ts TARGETSIZE, --targetsize TARGETSIZE --ts TARGETSIZE, --targetsize TARGETSIZE
the maximal size of output file in MB. [Default=100MB for webtoon and 400MB for others] the maximal size of output file in MB. [Default=100MB for webtoon and 400MB for others]
@@ -295,7 +291,6 @@ OUTPUT SETTINGS:
--metadatatitle Write title using ComicInfo.xml or other embedded metadata. 0: Don't use Title from metadata 1: Combine Title with default schema 2: Use Title only [Default=0] --metadatatitle Write title using ComicInfo.xml or other embedded metadata. 0: Don't use Title from metadata 1: Combine Title with default schema 2: Use Title only [Default=0]
-a AUTHOR, --author AUTHOR -a AUTHOR, --author AUTHOR
Author name [Default=KCC] Author name [Default=KCC]
--language EPUB language [Default=en-US]
-f FORMAT, --format FORMAT -f FORMAT, --format FORMAT
Output format (Available options: Auto, MOBI, EPUB, CBZ, PDF, KFX, MOBI+EPUB) [Default=Auto] Output format (Available options: Auto, MOBI, EPUB, CBZ, PDF, KFX, MOBI+EPUB) [Default=Auto]
--nokepub If format is EPUB, output file with '.epub' extension rather than '.kepub.epub' --nokepub If format is EPUB, output file with '.epub' extension rather than '.kepub.epub'
+739 -792
View File
File diff suppressed because it is too large Load Diff
+5 -24
View File
@@ -273,12 +273,6 @@ class WorkerThread(QThread):
options.format = gui_current_format options.format = gui_current_format
if GUI.mangaBox.isChecked(): if GUI.mangaBox.isChecked():
options.righttoleft = True options.righttoleft = True
if GUI.lightnovelBox.isChecked():
options.lightnovel = True
if GUI.ebokBox.isChecked():
options.ebok = True
if GUI.invertDirectionBox.isChecked():
options.invertdirection = True
if GUI.rotateBox.checkState() == Qt.CheckState.PartiallyChecked: if GUI.rotateBox.checkState() == Qt.CheckState.PartiallyChecked:
options.splitter = 2 options.splitter = 2
elif GUI.rotateBox.checkState() == Qt.CheckState.Checked: elif GUI.rotateBox.checkState() == Qt.CheckState.Checked:
@@ -287,8 +281,6 @@ class WorkerThread(QThread):
options.autoscale = True options.autoscale = True
elif GUI.qualityBox.checkState() == Qt.CheckState.Checked: elif GUI.qualityBox.checkState() == Qt.CheckState.Checked:
options.hq = True options.hq = True
if GUI.vertical4PanelBox.isChecked():
options.vertical4panel = True
if GUI.webtoonBox.isChecked(): if GUI.webtoonBox.isChecked():
options.webtoon = True options.webtoon = True
if GUI.upscaleBox.checkState() == Qt.CheckState.PartiallyChecked: if GUI.upscaleBox.checkState() == Qt.CheckState.PartiallyChecked:
@@ -387,8 +379,6 @@ class WorkerThread(QThread):
options.title = str(GUI.titleEdit.text()) options.title = str(GUI.titleEdit.text())
if GUI.authorEdit.text(): if GUI.authorEdit.text():
options.author = str(GUI.authorEdit.text()) options.author = str(GUI.authorEdit.text())
if GUI.languageEdit.text():
options.language = str(GUI.languageEdit.text())
if GUI.chunkSizeCheckBox.isChecked(): if GUI.chunkSizeCheckBox.isChecked():
options.targetsize = int(GUI.chunkSizeBox.value()) options.targetsize = int(GUI.chunkSizeBox.value())
@@ -478,7 +468,7 @@ class WorkerThread(QThread):
MW.addMessage.emit('Creating PDF files... <b>Done!</b>', 'info', True) MW.addMessage.emit('Creating PDF files... <b>Done!</b>', 'info', True)
else: else:
MW.addMessage.emit('Creating EPUB files... <b>Done!</b>', 'info', True) MW.addMessage.emit('Creating EPUB files... <b>Done!</b>', 'info', True)
if 'MOBI' in gui_current_format and not options.lightnovel: if 'MOBI' in gui_current_format:
MW.progressBarTick.emit(f'{job_progress_number}Creating MOBI files') MW.progressBarTick.emit(f'{job_progress_number}Creating MOBI files')
MW.progressBarTick.emit(str(len(outputPath) * 2 + 1)) MW.progressBarTick.emit(str(len(outputPath) * 2 + 1))
MW.progressBarTick.emit('tick') MW.progressBarTick.emit('tick')
@@ -519,6 +509,7 @@ class WorkerThread(QThread):
for item in outputPath: for item in outputPath:
GUI.progress.content = '' GUI.progress.content = ''
mobiPath = item.replace('.epub', '.mobi') mobiPath = item.replace('.epub', '.mobi')
os.remove(mobiPath + '_toclean')
if GUI.targetDirectory and GUI.targetDirectory != os.path.dirname(mobiPath): if GUI.targetDirectory and GUI.targetDirectory != os.path.dirname(mobiPath):
try: try:
move(mobiPath, GUI.targetDirectory) move(mobiPath, GUI.targetDirectory)
@@ -539,6 +530,8 @@ class WorkerThread(QThread):
mobiPath = item.replace('.epub', '.mobi') mobiPath = item.replace('.epub', '.mobi')
if os.path.exists(mobiPath): if os.path.exists(mobiPath):
os.remove(mobiPath) os.remove(mobiPath)
if os.path.exists(mobiPath + '_toclean'):
os.remove(mobiPath + '_toclean')
MW.addMessage.emit('Failed to process MOBI file!', 'error', False) MW.addMessage.emit('Failed to process MOBI file!', 'error', False)
MW.addTrayMessage.emit('Failed to process MOBI file!', 'Critical') MW.addTrayMessage.emit('Failed to process MOBI file!', 'Critical')
else: else:
@@ -1087,13 +1080,8 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
self.settings.setValue('startNumber', self.startNumber + 1) self.settings.setValue('startNumber', self.startNumber + 1)
self.settings.setValue('windowSize', str(MW.size().width()) + 'x' + str(MW.size().height())) self.settings.setValue('windowSize', str(MW.size().width()) + 'x' + str(MW.size().height()))
self.settings.setValue('options', {'mangaBox': GUI.mangaBox.checkState(), self.settings.setValue('options', {'mangaBox': GUI.mangaBox.checkState(),
'lightnovelBox': GUI.lightnovelBox.checkState(),
'ebokBox': GUI.ebokBox.checkState(),
'invertDirectionBox': GUI.invertDirectionBox.checkState(),
'languageEdit': GUI.languageEdit.text(),
'rotateBox': GUI.rotateBox.checkState(), 'rotateBox': GUI.rotateBox.checkState(),
'qualityBox': GUI.qualityBox.checkState(), 'qualityBox': GUI.qualityBox.checkState(),
'vertical4PanelBox': GUI.vertical4PanelBox.checkState(),
'gammaBox': GUI.gammaBox.checkState(), 'gammaBox': GUI.gammaBox.checkState(),
'autoLevelBox': GUI.autoLevelBox.checkState(), 'autoLevelBox': GUI.autoLevelBox.checkState(),
'autocontrastBox': GUI.autocontrastBox.checkState(), 'autocontrastBox': GUI.autocontrastBox.checkState(),
@@ -1201,11 +1189,6 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
self.kindleGen = False self.kindleGen = False
if startup: if startup:
self.display_kindlegen_missing() self.display_kindlegen_missing()
except OSError as e:
self.kindleGen = False
if startup:
error = f"kindlegen: {e.strerror}\n\n Re-install Rosetta/Kindle Previewer/other Intel app?\n\nPlease email Amazon to make Kindle Previewer Apple silicon native at amazon.com/kindle-help"
self.showDialog(error, 'error')
def __init__(self, kccapp, kccwindow): def __init__(self, kccapp, kccwindow):
global APP, MW, GUI global APP, MW, GUI
@@ -1215,7 +1198,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
self.setupUi(MW) self.setupUi(MW)
self.editor = KCCGUI_MetaEditor() self.editor = KCCGUI_MetaEditor()
self.icons = Icons() self.icons = Icons()
self.settings = QSettings('ciromattia', 'kcc10') self.settings = QSettings('ciromattia', 'kcc9')
self.settingsVersion = self.settings.value('settingsVersion', '', type=str) self.settingsVersion = self.settings.value('settingsVersion', '', type=str)
self.lastPath = self.settings.value('lastPath', '', type=str) self.lastPath = self.settings.value('lastPath', '', type=str)
self.defaultOutputFolder = str(self.settings.value('defaultOutputFolder', '', type=str)) self.defaultOutputFolder = str(self.settings.value('defaultOutputFolder', '', type=str))
@@ -1529,8 +1512,6 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
GUI.widthBox.setValue(int(self.options[option])) GUI.widthBox.setValue(int(self.options[option]))
elif str(option) == "heightBox": elif str(option) == "heightBox":
GUI.heightBox.setValue(int(self.options[option])) GUI.heightBox.setValue(int(self.options[option]))
elif str(option) == "languageEdit":
GUI.languageEdit.setText(str(self.options[option]))
elif str(option) == "gammaSlider": elif str(option) == "gammaSlider":
if GUI.gammaSlider.isEnabled(): if GUI.gammaSlider.isEnabled():
GUI.gammaSlider.setValue(int(self.options[option])) GUI.gammaSlider.setValue(int(self.options[option]))
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -1,4 +1,4 @@
__version__ = '10.3.0' __version__ = '10.2.0'
__license__ = 'ISC' __license__ = 'ISC'
__copyright__ = '2012-2022, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>, darodi' __copyright__ = '2012-2022, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>, darodi'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
+24 -89
View File
@@ -34,12 +34,12 @@ from stat import S_IWRITE, S_IREAD, S_IEXEC
from typing import List from typing import List
from zipfile import ZipFile, ZIP_STORED from zipfile import ZipFile, ZIP_STORED
from tempfile import mkdtemp, gettempdir from tempfile import mkdtemp, gettempdir
from shutil import move, copytree, rmtree from shutil import move, copytree, rmtree, copyfile
from multiprocessing import Pool, cpu_count from multiprocessing import Pool, cpu_count
from uuid import uuid4 from uuid import uuid4
from natsort import os_sort_keygen, os_sorted from natsort import os_sort_keygen, os_sorted
from slugify import slugify as slugify_ext from slugify import slugify as slugify_ext
from PIL import Image, ImageFile, ImageOps from PIL import Image, ImageFile
from pathlib import Path from pathlib import Path
from subprocess import STDOUT, PIPE, CalledProcessError from subprocess import STDOUT, PIPE, CalledProcessError
from psutil import virtual_memory, disk_usage from psutil import virtual_memory, disk_usage
@@ -226,7 +226,7 @@ def buildNCX(dstdir, title, chapters, chapternames):
ncxfile = os.path.join(dstdir, 'OEBPS', 'toc.ncx') ncxfile = os.path.join(dstdir, 'OEBPS', 'toc.ncx')
f = open(ncxfile, "w", encoding='UTF-8') f = open(ncxfile, "w", encoding='UTF-8')
f.writelines(["<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n", f.writelines(["<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n",
f"<ncx version=\"2005-1\" xml:lang=\"{options.language}\" xmlns=\"http://www.daisy.org/z3986/2005/ncx/\">\n", "<ncx version=\"2005-1\" xml:lang=\"en-US\" xmlns=\"http://www.daisy.org/z3986/2005/ncx/\">\n",
"<head>\n", "<head>\n",
"<meta name=\"dtb:uid\" content=\"urn:uuid:", options.uuid, "\"/>\n", "<meta name=\"dtb:uid\" content=\"urn:uuid:", options.uuid, "\"/>\n",
"<meta name=\"dtb:depth\" content=\"1\"/>\n", "<meta name=\"dtb:depth\" content=\"1\"/>\n",
@@ -292,22 +292,10 @@ def buildNAV(dstdir, title, chapters, chapternames):
def buildOPF(dstdir, title, filelist, originalpath, cover=None): def buildOPF(dstdir, title, filelist, originalpath, cover=None):
opffile = os.path.join(dstdir, 'OEBPS', 'content.opf') opffile = os.path.join(dstdir, 'OEBPS', 'content.opf')
deviceres = options.profileData[1] deviceres = options.profileData[1]
if options.vertical4panel:
writingmode = "vertical"
else:
writingmode = "horizontal"
if options.invertdirection:
if options.righttoleft: if options.righttoleft:
writingmode += "-lr" writingmode = "horizontal-rl"
else: else:
writingmode += "-rl" writingmode = "horizontal-lr"
else:
if options.righttoleft:
writingmode += "-rl"
else:
writingmode += "-lr"
f = open(opffile, "w", encoding='UTF-8') f = open(opffile, "w", encoding='UTF-8')
f.writelines(["<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n", f.writelines(["<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n",
"<package version=\"3.0\" unique-identifier=\"BookID\" ", "<package version=\"3.0\" unique-identifier=\"BookID\" ",
@@ -315,7 +303,7 @@ def buildOPF(dstdir, title, filelist, originalpath, cover=None):
"<metadata xmlns:opf=\"http://www.idpf.org/2007/opf\" ", "<metadata xmlns:opf=\"http://www.idpf.org/2007/opf\" ",
"xmlns:dc=\"http://purl.org/dc/elements/1.1/\">\n", "xmlns:dc=\"http://purl.org/dc/elements/1.1/\">\n",
"<dc:title>", hescape(title), "</dc:title>\n", "<dc:title>", hescape(title), "</dc:title>\n",
f"<dc:language>{options.language}</dc:language>\n", "<dc:language>en-US</dc:language>\n",
"<dc:identifier id=\"BookID\">urn:uuid:", options.uuid, "</dc:identifier>\n", "<dc:identifier id=\"BookID\">urn:uuid:", options.uuid, "</dc:identifier>\n",
"<dc:contributor id=\"contributor\">KindleComicConverter-" + __version__ + "</dc:contributor>\n"]) "<dc:contributor id=\"contributor\">KindleComicConverter-" + __version__ + "</dc:contributor>\n"])
if len(options.summary) > 0: if len(options.summary) > 0:
@@ -406,22 +394,13 @@ def buildOPF(dstdir, title, filelist, originalpath, cover=None):
else: else:
return "" return ""
if options.invertdirection:
if options.righttoleft:
f.write("</manifest>\n<spine page-progression-direction=\"ltr\" toc=\"ncx\">\n")
pageside = "left"
else:
f.write("</manifest>\n<spine page-progression-direction=\"rtl\" toc=\"ncx\">\n")
pageside = "right"
else:
if options.righttoleft: if options.righttoleft:
f.write("</manifest>\n<spine page-progression-direction=\"rtl\" toc=\"ncx\">\n") f.write("</manifest>\n<spine page-progression-direction=\"rtl\" toc=\"ncx\">\n")
pageside = "right" pageside = "right"
else: else:
f.write("</manifest>\n<spine page-progression-direction=\"ltr\" toc=\"ncx\">\n") f.write("</manifest>\n<spine page-progression-direction=\"ltr\" toc=\"ncx\">\n")
pageside = "left" pageside = "left"
if originalpath.lower().endswith('.pdf'):
if originalpath.lower().endswith('.pdf') or originalpath.lower().endswith('.epub'):
if pageside == "right": if pageside == "right":
pageside = "left" pageside = "left"
else: else:
@@ -965,8 +944,6 @@ def getWorkFolder(afile, workdir=None):
try: try:
cbx = comicarchive.ComicArchive(afile) cbx = comicarchive.ComicArchive(afile)
path = cbx.extract(fullPath) path = cbx.extract(fullPath)
if options.lightnovel:
return workdir
sanitizePermissions(path) sanitizePermissions(path)
tdir = os.listdir(fullPath) tdir = os.listdir(fullPath)
@@ -1408,6 +1385,7 @@ def slugify(value, is_natural_sorted):
def makeZIP(zipfilename, basedir, job_progress='', isepub=False): def makeZIP(zipfilename, basedir, job_progress='', isepub=False):
start = perf_counter() start = perf_counter()
zipfilename = os.path.abspath(zipfilename) + '.zip'
if SEVENZIP in available_archive_tools(): if SEVENZIP in available_archive_tools():
if isepub: if isepub:
mimetypeFile = open(os.path.join(basedir, '!mimetype'), 'w') mimetypeFile = open(os.path.join(basedir, '!mimetype'), 'w')
@@ -1450,18 +1428,10 @@ def makeParser():
" [Default=KV]") " [Default=KV]")
main_options.add_argument("-m", "--manga-style", action="store_true", dest="righttoleft", default=False, main_options.add_argument("-m", "--manga-style", action="store_true", dest="righttoleft", default=False,
help="Manga style (right-to-left reading and splitting)") help="Manga style (right-to-left reading and splitting)")
main_options.add_argument("--lightnovel", action="store_true", dest="lightnovel", default=False,
help="Only resize images and preserve original file structure.")
main_options.add_argument("--ebok", action="store_true", dest="ebok", default=False,
help="Force EBOK tag instead of PDOC for MOBI")
main_options.add_argument("--invertdirection", action="store_true", dest="invertdirection", default=False,
help="Invert page turn direction")
main_options.add_argument("-q", "--hq", action="store_true", dest="hq", default=False, main_options.add_argument("-q", "--hq", action="store_true", dest="hq", default=False,
help="Try to increase the quality of magnification") help="Try to increase the quality of magnification")
main_options.add_argument("-2", "--two-panel", action="store_true", dest="autoscale", default=False, main_options.add_argument("-2", "--two-panel", action="store_true", dest="autoscale", default=False,
help="Display two not four panels in Panel View mode") help="Display two not four panels in Panel View mode")
main_options.add_argument("--vertical4panel", action="store_true", dest="vertical4panel", default=False,
help="Display side panels first in virtual panel view")
main_options.add_argument("-w", "--webtoon", action="store_true", dest="webtoon", default=False, main_options.add_argument("-w", "--webtoon", action="store_true", dest="webtoon", default=False,
help="Webtoon processing mode"), help="Webtoon processing mode"),
main_options.add_argument("--ts", "--targetsize", type=int, dest="targetsize", default=None, main_options.add_argument("--ts", "--targetsize", type=int, dest="targetsize", default=None,
@@ -1477,8 +1447,6 @@ def makeParser():
"2: Use Title only") "2: Use Title only")
output_options.add_argument("-a", "--author", action="store", dest="author", default="defaultauthor", output_options.add_argument("-a", "--author", action="store", dest="author", default="defaultauthor",
help="Author name [Default=KCC]") help="Author name [Default=KCC]")
output_options.add_argument("--language", action="store", dest="language", default="en-US",
help="EPUB language [Default=en-US]")
output_options.add_argument("-f", "--format", action="store", dest="format", default="Auto", output_options.add_argument("-f", "--format", action="store", dest="format", default="Auto",
help="Output format (Available options: Auto, MOBI, EPUB, CBZ, KFX, MOBI+EPUB, PDF) " help="Output format (Available options: Auto, MOBI, EPUB, CBZ, KFX, MOBI+EPUB, PDF) "
"[Default=Auto]") "[Default=Auto]")
@@ -1586,9 +1554,6 @@ def checkOptions(options):
else: else:
options.isKobo = True options.isKobo = True
if options.lightnovel:
options.noKepub = True
if not options.iskindle and ('MOBI' in options.format or 'EPUB-200MB' in options.format or 'KFX' in options.format): if not options.iskindle and ('MOBI' in options.format or 'EPUB-200MB' in options.format or 'KFX' in options.format):
raise UserWarning('MOBI/Send to Kindle not supported for non-Kindle profiles') raise UserWarning('MOBI/Send to Kindle not supported for non-Kindle profiles')
@@ -1708,11 +1673,6 @@ def checkTools(source):
except (FileNotFoundError, CalledProcessError): except (FileNotFoundError, CalledProcessError):
print('ERROR: KindleGen is missing!') print('ERROR: KindleGen is missing!')
sys.exit(1) sys.exit(1)
except OSError as e:
print(f"kindlegen: {e.strerror}")
print('Re-install Rosetta/Kindle Previewer/other Intel app?')
print('Please email Amazon to make Kindle Previewer Apple silicon native at amazon.com/kindle-help')
sys.exit(1)
def checkPre(source='KCC-'): def checkPre(source='KCC-'):
@@ -1758,9 +1718,7 @@ def makeFusion(sources: List[str]):
else: else:
targetpath = fusion_path.joinpath(f'{prefix}{source_path.name}') targetpath = fusion_path.joinpath(f'{prefix}{source_path.name}')
path = getWorkFolder(source, str(targetpath)) getWorkFolder(source, str(targetpath))
if path != str(targetpath):
move(os.path.join(path, 'OEBPS', 'Images'), targetpath)
sanitizeTree(targetpath, prefix='fusion') sanitizeTree(targetpath, prefix='fusion')
# TODO: remove flattenTree when subchapters are supported # TODO: remove flattenTree when subchapters are supported
flattenTree(targetpath) flattenTree(targetpath)
@@ -1786,35 +1744,6 @@ def makeBook(source, qtgui=None, job_progress=''):
print(f"{job_progress}Preparing source images...") print(f"{job_progress}Preparing source images...")
path = getWorkFolder(source) path = getWorkFolder(source)
print(f"{job_progress}Checking images...") print(f"{job_progress}Checking images...")
if options.lightnovel:
for root, _, files in os.walk(os.path.join(path, 'OEBPS', 'Images')):
for file in files:
_, ext = os.path.splitext(file)
if ext.lower() in ('.jpg', '.jpeg', '.png', '.webp', '.gif'):
with Image.open(os.path.join(root, file)) as img:
# TODO: detect BW images saved as RGB
if not options.forcecolor:
if img.mode == 'RGB':
img = img.convert('L')
elif img.mode == 'RGBA':
img = img.convert('LA')
x, y = image.ProfileData.Profiles[options.profile][1]
if options.iskindle:
x = min(x, 1920)
y = min(y, 1920)
if img.size[0] > x or img.size[1] > y:
img = ImageOps.contain(img, (x, y))
img.save(os.path.join(root, file), quality=options.jpegquality)
_, ext = os.path.splitext(source)
if ext != '.epub':
ext = '.cbz'
output_file = getOutputFilename(source, options.output, ext, '')
makeZIP(output_file, os.path.join(path, 'OEBPS', 'Images'), job_progress)
rmtree(path, True)
return [output_file]
getMetadata(os.path.join(path, "OEBPS", "Images"), source) getMetadata(os.path.join(path, "OEBPS", "Images"), source)
removeNonImages(os.path.join(path, "OEBPS", "Images")) removeNonImages(os.path.join(path, "OEBPS", "Images"))
detectSuboptimalProcessing(os.path.join(path, "OEBPS", "Images"), source) detectSuboptimalProcessing(os.path.join(path, "OEBPS", "Images"), source)
@@ -1905,7 +1834,7 @@ def makeBook(source, qtgui=None, job_progress=''):
filepath.append(getOutputFilename(source, options.output, '.cbz', '')) filepath.append(getOutputFilename(source, options.output, '.cbz', ''))
if cover and cover.smartcover: if cover and cover.smartcover:
cover.save_to_folder(os.path.join(tome, 'OEBPS', 'Images', 'cover.jpg'), tomeNumber, len(tomes)) cover.save_to_folder(os.path.join(tome, 'OEBPS', 'Images', 'cover.jpg'), tomeNumber, len(tomes))
makeZIP(filepath[-1], os.path.join(tome, "OEBPS", "Images"), job_progress) makeZIP(tome + '_comic', os.path.join(tome, "OEBPS", "Images"), job_progress)
elif options.format == 'PDF': elif options.format == 'PDF':
print(f"{job_progress}Creating PDF file with PyMuPDF...") print(f"{job_progress}Creating PDF file with PyMuPDF...")
# determine output filename based on source and tome count # determine output filename based on source and tome count
@@ -1924,11 +1853,19 @@ def makeBook(source, qtgui=None, job_progress=''):
else: else:
buildEPUB(tome, chapterNames, tomeNumber, False, cover, source, job_progress) buildEPUB(tome, chapterNames, tomeNumber, False, cover, source, job_progress)
filepath.append(getOutputFilename(source, options.output, '.epub', '')) filepath.append(getOutputFilename(source, options.output, '.epub', ''))
makeZIP(filepath[-1], tome, job_progress, True) makeZIP(tome + '_comic', tome, job_progress, True)
# Copy files to final destination (PDF files are already saved directly)
if options.format != 'PDF':
copyfile(tome + '_comic.zip', filepath[-1])
try:
os.remove(tome + '_comic.zip')
except FileNotFoundError:
# newly temporary created file is not found. It might have been already deleted
pass
rmtree(tome, True) rmtree(tome, True)
if GUI: if GUI:
GUI.progressBarTick.emit('tick') GUI.progressBarTick.emit('tick')
if not GUI and options.format == 'MOBI' and not options.lightnovel: if not GUI and options.format == 'MOBI':
print(f"{job_progress}Creating MOBI files...") print(f"{job_progress}Creating MOBI files...")
work = [] work = []
for i in filepath: for i in filepath:
@@ -1947,6 +1884,8 @@ def makeBook(source, qtgui=None, job_progress=''):
if not output[0]: if not output[0]:
print(f'{job_progress}Error: Failed to tweak KindleGen output!') print(f'{job_progress}Error: Failed to tweak KindleGen output!')
return filepath return filepath
else:
os.remove(i.replace('.epub', '.mobi') + '_toclean')
if cover and k.path and k.coverSupport: if cover and k.path and k.coverSupport:
options.covers[filepath.index(i)][0].saveToKindle(k, options.covers[filepath.index(i)][1]) options.covers[filepath.index(i)][0].saveToKindle(k, options.covers[filepath.index(i)][1])
if options.delete: if options.delete:
@@ -1967,13 +1906,12 @@ def makeBook(source, qtgui=None, job_progress=''):
def makeMOBIFix(item, uuid): def makeMOBIFix(item, uuid):
is_pdoc = options.profile in image.ProfileData.ProfilesKindlePDOC.keys() is_pdoc = options.profile in image.ProfileData.ProfilesKindlePDOC.keys()
if options.ebok:
is_pdoc = False
if not options.keep_epub: if not options.keep_epub:
os.remove(item) os.remove(item)
mobiPath = item.replace('.epub', '.mobi') mobiPath = item.replace('.epub', '.mobi')
move(mobiPath, mobiPath + '_toclean')
try: try:
dualmetafix.DualMobiMetaFix(mobiPath, bytes(uuid, 'UTF-8'), is_pdoc) dualmetafix.DualMobiMetaFix(mobiPath + '_toclean', mobiPath, bytes(uuid, 'UTF-8'), is_pdoc)
return [True] return [True]
except Exception as err: except Exception as err:
return [False, format(err)] return [False, format(err)]
@@ -1995,11 +1933,8 @@ def makeMOBIWorker(item):
kindlegenError = '' kindlegenError = ''
try: try:
if os.path.getsize(item) < 629145600: if os.path.getsize(item) < 629145600:
start = perf_counter()
output = subprocess_run(['kindlegen', '-dont_append_source', '-locale', 'en', item], output = subprocess_run(['kindlegen', '-dont_append_source', '-locale', 'en', item],
stdout=PIPE, stderr=STDOUT, encoding='UTF-8', errors='ignore', check=True) stdout=PIPE, stderr=STDOUT, encoding='UTF-8', errors='ignore', check=True)
end = perf_counter()
print(f"kindlegen: {end - start} sec")
else: else:
# ERROR: EPUB too big # ERROR: EPUB too big
kindlegenErrorCode = 23026 kindlegenErrorCode = 23026
+1 -1
View File
@@ -62,7 +62,7 @@ def mergeDirectory(work):
imagesValid.append(i[0]) imagesValid.append(i[0])
# Silently drop directories that contain too many images # Silently drop directories that contain too many images
# 131072 = GIMP_MAX_IMAGE_SIZE / 4 # 131072 = GIMP_MAX_IMAGE_SIZE / 4
if targetHeight > 131072 * 4: if targetHeight > 131072 * 8:
raise RuntimeError(f'Image too tall at {targetHeight} pixels. {targetWidth} pixels wide. Try using separate chapter folders or file fusion.') raise RuntimeError(f'Image too tall at {targetHeight} pixels. {targetWidth} pixels wide. Try using separate chapter folders or file fusion.')
result = Image.new('RGB', (targetWidth, targetHeight)) result = Image.new('RGB', (targetWidth, targetHeight))
y = 0 y = 0
+2 -1
View File
@@ -136,11 +136,12 @@ def del_exth(rec0, exth_num):
class DualMobiMetaFix: class DualMobiMetaFix:
def __init__(self, outfile, asin, is_pdoc): def __init__(self, infile, outfile, asin, is_pdoc):
cdetype = b'EBOK' cdetype = b'EBOK'
if is_pdoc: if is_pdoc:
cdetype = b'PDOC' cdetype = b'PDOC'
shutil.copyfile(infile, outfile)
f = open(outfile, "r+b") f = open(outfile, "r+b")
self.datain = mmap.mmap(f.fileno(), 0) self.datain = mmap.mmap(f.fileno(), 0)
self.datain_rec0 = readsection(self.datain, 0) self.datain_rec0 = readsection(self.datain, 0)
+2 -2
View File
@@ -1,11 +1,11 @@
Pillow>=11.3.0 Pillow>=11.3.0
psutil>=7.2.2 psutil>=5.9.5
requests>=2.34.2 requests>=2.34.2
python-slugify>=8.0.4 python-slugify>=8.0.4
packaging>=26.2 packaging>=26.2
mozjpeg-lossless-optimization>=1.2.0 mozjpeg-lossless-optimization>=1.2.0
natsort>=8.4.0 natsort>=8.4.0
distro>=1.9.0 distro>=1.8.0
# Below requirements are compiled in Dockefile # Below requirements are compiled in Dockefile
# numpy==2.3.4 # numpy==2.3.4
# PyMuPDF==1.26.6 # PyMuPDF==1.26.6
+2 -2
View File
@@ -1,11 +1,11 @@
PySide6==6.4.3 PySide6==6.4.3
Pillow>=11.3.0 Pillow>=11.3.0
psutil>=7.2.2 psutil>=5.9.5
requests>=2.34.2 requests>=2.34.2
python-slugify>=8.0.4 python-slugify>=8.0.4
packaging>=26.2 packaging>=26.2
mozjpeg-lossless-optimization>=1.2.0 mozjpeg-lossless-optimization>=1.2.0
natsort>=8.4.0 natsort>=8.4.0
distro>=1.9.0 distro>=1.8.0
numpy<2 numpy<2
PyMuPDF==1.25.5 PyMuPDF==1.25.5
+3 -3
View File
@@ -1,11 +1,11 @@
PySide6==6.1.3 PySide6==6.1.3
Pillow>=9 Pillow>=9
psutil>=7.2.2 psutil>=5.9.5
requests>=2.32.4 requests>=2.32.4
python-slugify>=8.0.4 python-slugify>=8.0.4
packaging>=26.2 packaging>=26.2
mozjpeg-lossless-optimization>=1.2.0 mozjpeg-lossless-optimization>=1.2.0
natsort>=8.4.0 natsort>=8.4.0
distro>=1.9.0 distro>=1.8.0
numpy==1.23.5 numpy==1.23.0
PyMuPDF>=1.16 PyMuPDF>=1.16
+2 -2
View File
@@ -1,11 +1,11 @@
PySide6<6.10 PySide6<6.10
Pillow>=11.3.0 Pillow>=11.3.0
psutil>=7.2.2 psutil>=5.9.5
requests>=2.34.2 requests>=2.34.2
python-slugify>=8.0.4,<9.0.0 python-slugify>=8.0.4,<9.0.0
packaging>=26.2 packaging>=26.2
mozjpeg-lossless-optimization>=1.2.0 mozjpeg-lossless-optimization>=1.2.0
natsort>=8.4.0 natsort>=8.4.0
distro>=1.9.0 distro>=1.8.0
numpy>=1.22.4 numpy>=1.22.4
PyMuPDF>=1.18.0 PyMuPDF>=1.18.0