mirror of
https://github.com/ciromattia/kcc
synced 2026-06-28 09:14:07 +00:00
Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| cab3703217 | |||
| 3723bf9d52 | |||
| 2a6d61530f | |||
| 5396f1f9c4 | |||
| f1b58c83d6 | |||
| 0f009755b1 | |||
| ee5bd150e5 | |||
| a55c0ddb08 | |||
| 799961407e | |||
| bc28df1f53 | |||
| a8b2c055bf | |||
| afa9c7e7e6 | |||
| 1f1b9a37fa | |||
| c4d9512cbc | |||
| 89fc6437dd | |||
| 1e57da08a9 | |||
| b4ef37dbb9 | |||
| b95cf6e179 | |||
| 08070cdd97 | |||
| df3d174437 |
@@ -38,7 +38,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
|
||||
@@ -21,7 +21,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v4
|
||||
|
||||
@@ -25,7 +25,7 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
|
||||
@@ -30,7 +30,7 @@ jobs:
|
||||
env:
|
||||
MACOSX_DEPLOYMENT_TARGET: '14.0'
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
|
||||
@@ -31,7 +31,7 @@ jobs:
|
||||
PYTHON_VERSION: 3.11.9
|
||||
MACOSX_DEPLOYMENT_TARGET: '10.14'
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
- name: Get Python
|
||||
run: curl https://www.python.org/ftp/python/3.11.9/python-3.11.9-macos11.pkg -o "python.pkg"
|
||||
- name: Install Python
|
||||
|
||||
@@ -35,7 +35,7 @@ jobs:
|
||||
command: build_c2p
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
|
||||
@@ -27,7 +27,7 @@ jobs:
|
||||
env:
|
||||
WINDOWS_7: 1
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
|
||||
@@ -239,8 +239,12 @@ 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)
|
||||
[Default=KV]
|
||||
-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
|
||||
-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
|
||||
--ts TARGETSIZE, --targetsize TARGETSIZE
|
||||
the maximal size of output file in MB. [Default=100MB for webtoon and 400MB for others]
|
||||
@@ -291,6 +295,7 @@ 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]
|
||||
-a AUTHOR, --author AUTHOR
|
||||
Author name [Default=KCC]
|
||||
--language EPUB language [Default=en-US]
|
||||
-f FORMAT, --format FORMAT
|
||||
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'
|
||||
|
||||
+787
-734
File diff suppressed because it is too large
Load Diff
@@ -273,6 +273,12 @@ class WorkerThread(QThread):
|
||||
options.format = gui_current_format
|
||||
if GUI.mangaBox.isChecked():
|
||||
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:
|
||||
options.splitter = 2
|
||||
elif GUI.rotateBox.checkState() == Qt.CheckState.Checked:
|
||||
@@ -281,6 +287,8 @@ class WorkerThread(QThread):
|
||||
options.autoscale = True
|
||||
elif GUI.qualityBox.checkState() == Qt.CheckState.Checked:
|
||||
options.hq = True
|
||||
if GUI.vertical4PanelBox.isChecked():
|
||||
options.vertical4panel = True
|
||||
if GUI.webtoonBox.isChecked():
|
||||
options.webtoon = True
|
||||
if GUI.upscaleBox.checkState() == Qt.CheckState.PartiallyChecked:
|
||||
@@ -379,6 +387,8 @@ class WorkerThread(QThread):
|
||||
options.title = str(GUI.titleEdit.text())
|
||||
if GUI.authorEdit.text():
|
||||
options.author = str(GUI.authorEdit.text())
|
||||
if GUI.languageEdit.text():
|
||||
options.language = str(GUI.languageEdit.text())
|
||||
if GUI.chunkSizeCheckBox.isChecked():
|
||||
options.targetsize = int(GUI.chunkSizeBox.value())
|
||||
|
||||
@@ -393,7 +403,10 @@ class WorkerThread(QThread):
|
||||
for job in currentJobs:
|
||||
bookDir.append(job)
|
||||
try:
|
||||
fusion_source_parent = str(Path(bookDir[0]).parent)
|
||||
comic2ebook.options = comic2ebook.checkOptions(copy(options))
|
||||
if options.output is None:
|
||||
options.output = fusion_source_parent
|
||||
currentJobs.clear()
|
||||
currentJobs.append(comic2ebook.makeFusion(bookDir))
|
||||
MW.addMessage.emit('Created fusion at ' + currentJobs[0], 'info', False)
|
||||
@@ -465,7 +478,7 @@ class WorkerThread(QThread):
|
||||
MW.addMessage.emit('Creating PDF files... <b>Done!</b>', 'info', True)
|
||||
else:
|
||||
MW.addMessage.emit('Creating EPUB files... <b>Done!</b>', 'info', True)
|
||||
if 'MOBI' in gui_current_format:
|
||||
if 'MOBI' in gui_current_format and not options.lightnovel:
|
||||
MW.progressBarTick.emit(f'{job_progress_number}Creating MOBI files')
|
||||
MW.progressBarTick.emit(str(len(outputPath) * 2 + 1))
|
||||
MW.progressBarTick.emit('tick')
|
||||
@@ -506,7 +519,6 @@ class WorkerThread(QThread):
|
||||
for item in outputPath:
|
||||
GUI.progress.content = ''
|
||||
mobiPath = item.replace('.epub', '.mobi')
|
||||
os.remove(mobiPath + '_toclean')
|
||||
if GUI.targetDirectory and GUI.targetDirectory != os.path.dirname(mobiPath):
|
||||
try:
|
||||
move(mobiPath, GUI.targetDirectory)
|
||||
@@ -527,8 +539,6 @@ class WorkerThread(QThread):
|
||||
mobiPath = item.replace('.epub', '.mobi')
|
||||
if os.path.exists(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.addTrayMessage.emit('Failed to process MOBI file!', 'Critical')
|
||||
else:
|
||||
@@ -557,13 +567,6 @@ class WorkerThread(QThread):
|
||||
move(item, GUI.targetDirectory)
|
||||
except Exception:
|
||||
pass
|
||||
if options.filefusion:
|
||||
for path in currentJobs:
|
||||
if os.path.isfile(path):
|
||||
os.remove(path)
|
||||
elif os.path.isdir(path):
|
||||
rmtree(path, True)
|
||||
comic2ebook.checkPre('LLL-')
|
||||
GUI.progress.content = ''
|
||||
GUI.progress.stop()
|
||||
MW.hideProgressBar.emit()
|
||||
@@ -1084,8 +1087,13 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
self.settings.setValue('startNumber', self.startNumber + 1)
|
||||
self.settings.setValue('windowSize', str(MW.size().width()) + 'x' + str(MW.size().height()))
|
||||
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(),
|
||||
'qualityBox': GUI.qualityBox.checkState(),
|
||||
'vertical4PanelBox': GUI.vertical4PanelBox.checkState(),
|
||||
'gammaBox': GUI.gammaBox.checkState(),
|
||||
'autoLevelBox': GUI.autoLevelBox.checkState(),
|
||||
'autocontrastBox': GUI.autocontrastBox.checkState(),
|
||||
@@ -1193,6 +1201,11 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
self.kindleGen = False
|
||||
if startup:
|
||||
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):
|
||||
global APP, MW, GUI
|
||||
@@ -1202,7 +1215,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
self.setupUi(MW)
|
||||
self.editor = KCCGUI_MetaEditor()
|
||||
self.icons = Icons()
|
||||
self.settings = QSettings('ciromattia', 'kcc9')
|
||||
self.settings = QSettings('ciromattia', 'kcc10')
|
||||
self.settingsVersion = self.settings.value('settingsVersion', '', type=str)
|
||||
self.lastPath = self.settings.value('lastPath', '', type=str)
|
||||
self.defaultOutputFolder = str(self.settings.value('defaultOutputFolder', '', type=str))
|
||||
@@ -1516,6 +1529,8 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
GUI.widthBox.setValue(int(self.options[option]))
|
||||
elif str(option) == "heightBox":
|
||||
GUI.heightBox.setValue(int(self.options[option]))
|
||||
elif str(option) == "languageEdit":
|
||||
GUI.languageEdit.setText(str(self.options[option]))
|
||||
elif str(option) == "gammaSlider":
|
||||
if GUI.gammaSlider.isEnabled():
|
||||
GUI.gammaSlider.setValue(int(self.options[option]))
|
||||
|
||||
+625
-585
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
__version__ = '10.2.0'
|
||||
__version__ = '10.3.0'
|
||||
__license__ = 'ISC'
|
||||
__copyright__ = '2012-2022, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>, darodi'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
@@ -34,12 +34,12 @@ from stat import S_IWRITE, S_IREAD, S_IEXEC
|
||||
from typing import List
|
||||
from zipfile import ZipFile, ZIP_STORED
|
||||
from tempfile import mkdtemp, gettempdir
|
||||
from shutil import move, copytree, rmtree, copyfile
|
||||
from shutil import move, copytree, rmtree
|
||||
from multiprocessing import Pool, cpu_count
|
||||
from uuid import uuid4
|
||||
from natsort import os_sort_keygen, os_sorted
|
||||
from slugify import slugify as slugify_ext
|
||||
from PIL import Image, ImageFile
|
||||
from PIL import Image, ImageFile, ImageOps
|
||||
from pathlib import Path
|
||||
from subprocess import STDOUT, PIPE, CalledProcessError
|
||||
from psutil import virtual_memory, disk_usage
|
||||
@@ -76,23 +76,19 @@ def main(argv=None):
|
||||
print('No matching files found.')
|
||||
return 1
|
||||
if options.filefusion:
|
||||
fusion_source_parent = str(Path(sources[0]).parent)
|
||||
fusion_path = makeFusion(list(sources))
|
||||
sources.clear()
|
||||
sources.append(fusion_path)
|
||||
for source in sources:
|
||||
source = source.rstrip('\\').rstrip('/')
|
||||
options = copy(args)
|
||||
if options.filefusion and options.output is None:
|
||||
options.output = fusion_source_parent
|
||||
options = checkOptions(options)
|
||||
print('Working on ' + source + '...')
|
||||
makeBook(source)
|
||||
|
||||
if options.filefusion:
|
||||
for path in sources:
|
||||
if os.path.isfile(path):
|
||||
os.remove(path)
|
||||
elif os.path.isdir(path):
|
||||
rmtree(path, True)
|
||||
checkPre('LLL-')
|
||||
return 0
|
||||
|
||||
|
||||
@@ -230,7 +226,7 @@ def buildNCX(dstdir, title, chapters, chapternames):
|
||||
ncxfile = os.path.join(dstdir, 'OEBPS', 'toc.ncx')
|
||||
f = open(ncxfile, "w", encoding='UTF-8')
|
||||
f.writelines(["<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n",
|
||||
"<ncx version=\"2005-1\" xml:lang=\"en-US\" xmlns=\"http://www.daisy.org/z3986/2005/ncx/\">\n",
|
||||
f"<ncx version=\"2005-1\" xml:lang=\"{options.language}\" xmlns=\"http://www.daisy.org/z3986/2005/ncx/\">\n",
|
||||
"<head>\n",
|
||||
"<meta name=\"dtb:uid\" content=\"urn:uuid:", options.uuid, "\"/>\n",
|
||||
"<meta name=\"dtb:depth\" content=\"1\"/>\n",
|
||||
@@ -296,10 +292,22 @@ def buildNAV(dstdir, title, chapters, chapternames):
|
||||
def buildOPF(dstdir, title, filelist, originalpath, cover=None):
|
||||
opffile = os.path.join(dstdir, 'OEBPS', 'content.opf')
|
||||
deviceres = options.profileData[1]
|
||||
if options.righttoleft:
|
||||
writingmode = "horizontal-rl"
|
||||
|
||||
if options.vertical4panel:
|
||||
writingmode = "vertical"
|
||||
else:
|
||||
writingmode = "horizontal-lr"
|
||||
writingmode = "horizontal"
|
||||
if options.invertdirection:
|
||||
if options.righttoleft:
|
||||
writingmode += "-lr"
|
||||
else:
|
||||
writingmode += "-rl"
|
||||
else:
|
||||
if options.righttoleft:
|
||||
writingmode += "-rl"
|
||||
else:
|
||||
writingmode += "-lr"
|
||||
|
||||
f = open(opffile, "w", encoding='UTF-8')
|
||||
f.writelines(["<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n",
|
||||
"<package version=\"3.0\" unique-identifier=\"BookID\" ",
|
||||
@@ -307,7 +315,7 @@ def buildOPF(dstdir, title, filelist, originalpath, cover=None):
|
||||
"<metadata xmlns:opf=\"http://www.idpf.org/2007/opf\" ",
|
||||
"xmlns:dc=\"http://purl.org/dc/elements/1.1/\">\n",
|
||||
"<dc:title>", hescape(title), "</dc:title>\n",
|
||||
"<dc:language>en-US</dc:language>\n",
|
||||
f"<dc:language>{options.language}</dc:language>\n",
|
||||
"<dc:identifier id=\"BookID\">urn:uuid:", options.uuid, "</dc:identifier>\n",
|
||||
"<dc:contributor id=\"contributor\">KindleComicConverter-" + __version__ + "</dc:contributor>\n"])
|
||||
if len(options.summary) > 0:
|
||||
@@ -398,13 +406,22 @@ def buildOPF(dstdir, title, filelist, originalpath, cover=None):
|
||||
else:
|
||||
return ""
|
||||
|
||||
if options.righttoleft:
|
||||
f.write("</manifest>\n<spine page-progression-direction=\"rtl\" toc=\"ncx\">\n")
|
||||
pageside = "right"
|
||||
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:
|
||||
f.write("</manifest>\n<spine page-progression-direction=\"ltr\" toc=\"ncx\">\n")
|
||||
pageside = "left"
|
||||
if originalpath.lower().endswith('.pdf'):
|
||||
if options.righttoleft:
|
||||
f.write("</manifest>\n<spine page-progression-direction=\"rtl\" toc=\"ncx\">\n")
|
||||
pageside = "right"
|
||||
else:
|
||||
f.write("</manifest>\n<spine page-progression-direction=\"ltr\" toc=\"ncx\">\n")
|
||||
pageside = "left"
|
||||
|
||||
if originalpath.lower().endswith('.pdf') or originalpath.lower().endswith('.epub'):
|
||||
if pageside == "right":
|
||||
pageside = "left"
|
||||
else:
|
||||
@@ -948,6 +965,8 @@ def getWorkFolder(afile, workdir=None):
|
||||
try:
|
||||
cbx = comicarchive.ComicArchive(afile)
|
||||
path = cbx.extract(fullPath)
|
||||
if options.lightnovel:
|
||||
return workdir
|
||||
sanitizePermissions(path)
|
||||
|
||||
tdir = os.listdir(fullPath)
|
||||
@@ -979,10 +998,11 @@ def getWorkFolder(afile, workdir=None):
|
||||
manifest_dict[manifest_item.attrib.get('id')] = manifest_item.attrib.get('href')
|
||||
ordered_image_paths = []
|
||||
for i, spine_item in enumerate(spine):
|
||||
if spine_item not in manifest_dict:
|
||||
try:
|
||||
page_path = os.path.join(os.path.dirname(opf_path), manifest_dict[spine_item])
|
||||
page = ET.parse(page_path)
|
||||
except Exception:
|
||||
continue
|
||||
page_path = os.path.join(os.path.dirname(opf_path), manifest_dict[spine_item])
|
||||
page = ET.parse(page_path)
|
||||
imgs = page.findall(r'.//{*}img') + page.findall(r'.//{*}image')
|
||||
|
||||
largest_size = 0
|
||||
@@ -1388,7 +1408,6 @@ def slugify(value, is_natural_sorted):
|
||||
|
||||
def makeZIP(zipfilename, basedir, job_progress='', isepub=False):
|
||||
start = perf_counter()
|
||||
zipfilename = os.path.abspath(zipfilename) + '.zip'
|
||||
if SEVENZIP in available_archive_tools():
|
||||
if isepub:
|
||||
mimetypeFile = open(os.path.join(basedir, '!mimetype'), 'w')
|
||||
@@ -1431,10 +1450,18 @@ def makeParser():
|
||||
" [Default=KV]")
|
||||
main_options.add_argument("-m", "--manga-style", action="store_true", dest="righttoleft", default=False,
|
||||
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,
|
||||
help="Try to increase the quality of magnification")
|
||||
main_options.add_argument("-2", "--two-panel", action="store_true", dest="autoscale", default=False,
|
||||
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,
|
||||
help="Webtoon processing mode"),
|
||||
main_options.add_argument("--ts", "--targetsize", type=int, dest="targetsize", default=None,
|
||||
@@ -1450,6 +1477,8 @@ def makeParser():
|
||||
"2: Use Title only")
|
||||
output_options.add_argument("-a", "--author", action="store", dest="author", default="defaultauthor",
|
||||
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",
|
||||
help="Output format (Available options: Auto, MOBI, EPUB, CBZ, KFX, MOBI+EPUB, PDF) "
|
||||
"[Default=Auto]")
|
||||
@@ -1557,6 +1586,9 @@ def checkOptions(options):
|
||||
else:
|
||||
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):
|
||||
raise UserWarning('MOBI/Send to Kindle not supported for non-Kindle profiles')
|
||||
|
||||
@@ -1676,6 +1708,11 @@ def checkTools(source):
|
||||
except (FileNotFoundError, CalledProcessError):
|
||||
print('ERROR: KindleGen is missing!')
|
||||
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-'):
|
||||
@@ -1690,8 +1727,8 @@ def makeFusion(sources: List[str]):
|
||||
raise UserWarning('Fusion requires at least 2 sources. Did you forget to uncheck fusion?')
|
||||
start = perf_counter()
|
||||
first_path = Path(sources[0])
|
||||
|
||||
if True:
|
||||
|
||||
if options.tempdir:
|
||||
fusion_parent = first_path.parent
|
||||
else:
|
||||
# LLL is after KCC
|
||||
@@ -1721,7 +1758,9 @@ def makeFusion(sources: List[str]):
|
||||
else:
|
||||
targetpath = fusion_path.joinpath(f'{prefix}{source_path.name}')
|
||||
|
||||
getWorkFolder(source, str(targetpath))
|
||||
path = getWorkFolder(source, str(targetpath))
|
||||
if path != str(targetpath):
|
||||
move(os.path.join(path, 'OEBPS', 'Images'), targetpath)
|
||||
sanitizeTree(targetpath, prefix='fusion')
|
||||
# TODO: remove flattenTree when subchapters are supported
|
||||
flattenTree(targetpath)
|
||||
@@ -1747,6 +1786,35 @@ def makeBook(source, qtgui=None, job_progress=''):
|
||||
print(f"{job_progress}Preparing source images...")
|
||||
path = getWorkFolder(source)
|
||||
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)
|
||||
removeNonImages(os.path.join(path, "OEBPS", "Images"))
|
||||
detectSuboptimalProcessing(os.path.join(path, "OEBPS", "Images"), source)
|
||||
@@ -1837,7 +1905,7 @@ def makeBook(source, qtgui=None, job_progress=''):
|
||||
filepath.append(getOutputFilename(source, options.output, '.cbz', ''))
|
||||
if cover and cover.smartcover:
|
||||
cover.save_to_folder(os.path.join(tome, 'OEBPS', 'Images', 'cover.jpg'), tomeNumber, len(tomes))
|
||||
makeZIP(tome + '_comic', os.path.join(tome, "OEBPS", "Images"), job_progress)
|
||||
makeZIP(filepath[-1], os.path.join(tome, "OEBPS", "Images"), job_progress)
|
||||
elif options.format == 'PDF':
|
||||
print(f"{job_progress}Creating PDF file with PyMuPDF...")
|
||||
# determine output filename based on source and tome count
|
||||
@@ -1856,19 +1924,11 @@ def makeBook(source, qtgui=None, job_progress=''):
|
||||
else:
|
||||
buildEPUB(tome, chapterNames, tomeNumber, False, cover, source, job_progress)
|
||||
filepath.append(getOutputFilename(source, options.output, '.epub', ''))
|
||||
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
|
||||
makeZIP(filepath[-1], tome, job_progress, True)
|
||||
rmtree(tome, True)
|
||||
if GUI:
|
||||
GUI.progressBarTick.emit('tick')
|
||||
if not GUI and options.format == 'MOBI':
|
||||
if not GUI and options.format == 'MOBI' and not options.lightnovel:
|
||||
print(f"{job_progress}Creating MOBI files...")
|
||||
work = []
|
||||
for i in filepath:
|
||||
@@ -1887,8 +1947,6 @@ def makeBook(source, qtgui=None, job_progress=''):
|
||||
if not output[0]:
|
||||
print(f'{job_progress}Error: Failed to tweak KindleGen output!')
|
||||
return filepath
|
||||
else:
|
||||
os.remove(i.replace('.epub', '.mobi') + '_toclean')
|
||||
if cover and k.path and k.coverSupport:
|
||||
options.covers[filepath.index(i)][0].saveToKindle(k, options.covers[filepath.index(i)][1])
|
||||
if options.delete:
|
||||
@@ -1899,22 +1957,23 @@ def makeBook(source, qtgui=None, job_progress=''):
|
||||
|
||||
end = perf_counter()
|
||||
print(f"{job_progress}makeBook: {end - start} seconds")
|
||||
# Clean up temporary workspace
|
||||
try:
|
||||
rmtree(path, True)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if options.filefusion:
|
||||
rmtree(source, True)
|
||||
checkPre('LLL-')
|
||||
|
||||
return filepath
|
||||
|
||||
|
||||
def makeMOBIFix(item, uuid):
|
||||
is_pdoc = options.profile in image.ProfileData.ProfilesKindlePDOC.keys()
|
||||
if options.ebok:
|
||||
is_pdoc = False
|
||||
if not options.keep_epub:
|
||||
os.remove(item)
|
||||
mobiPath = item.replace('.epub', '.mobi')
|
||||
move(mobiPath, mobiPath + '_toclean')
|
||||
try:
|
||||
dualmetafix.DualMobiMetaFix(mobiPath + '_toclean', mobiPath, bytes(uuid, 'UTF-8'), is_pdoc)
|
||||
dualmetafix.DualMobiMetaFix(mobiPath, bytes(uuid, 'UTF-8'), is_pdoc)
|
||||
return [True]
|
||||
except Exception as err:
|
||||
return [False, format(err)]
|
||||
@@ -1936,8 +1995,11 @@ def makeMOBIWorker(item):
|
||||
kindlegenError = ''
|
||||
try:
|
||||
if os.path.getsize(item) < 629145600:
|
||||
start = perf_counter()
|
||||
output = subprocess_run(['kindlegen', '-dont_append_source', '-locale', 'en', item],
|
||||
stdout=PIPE, stderr=STDOUT, encoding='UTF-8', errors='ignore', check=True)
|
||||
end = perf_counter()
|
||||
print(f"kindlegen: {end - start} sec")
|
||||
else:
|
||||
# ERROR: EPUB too big
|
||||
kindlegenErrorCode = 23026
|
||||
|
||||
@@ -136,12 +136,11 @@ def del_exth(rec0, exth_num):
|
||||
|
||||
|
||||
class DualMobiMetaFix:
|
||||
def __init__(self, infile, outfile, asin, is_pdoc):
|
||||
def __init__(self, outfile, asin, is_pdoc):
|
||||
cdetype = b'EBOK'
|
||||
if is_pdoc:
|
||||
cdetype = b'PDOC'
|
||||
|
||||
shutil.copyfile(infile, outfile)
|
||||
f = open(outfile, "r+b")
|
||||
self.datain = mmap.mmap(f.fileno(), 0)
|
||||
self.datain_rec0 = readsection(self.datain, 0)
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
Pillow>=11.3.0
|
||||
psutil>=5.9.5
|
||||
psutil>=7.2.2
|
||||
requests>=2.34.2
|
||||
python-slugify>=8.0.4
|
||||
packaging>=26.2
|
||||
mozjpeg-lossless-optimization>=1.2.0
|
||||
natsort>=8.4.0
|
||||
distro>=1.8.0
|
||||
distro>=1.9.0
|
||||
# Below requirements are compiled in Dockefile
|
||||
# numpy==2.3.4
|
||||
# PyMuPDF==1.26.6
|
||||
@@ -1,11 +1,11 @@
|
||||
PySide6==6.4.3
|
||||
Pillow>=11.3.0
|
||||
psutil>=5.9.5
|
||||
psutil>=7.2.2
|
||||
requests>=2.34.2
|
||||
python-slugify>=8.0.4
|
||||
packaging>=26.2
|
||||
mozjpeg-lossless-optimization>=1.2.0
|
||||
natsort>=8.4.0
|
||||
distro>=1.8.0
|
||||
distro>=1.9.0
|
||||
numpy<2
|
||||
PyMuPDF==1.25.5
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
PySide6==6.1.3
|
||||
Pillow>=9
|
||||
psutil>=5.9.5
|
||||
psutil>=7.2.2
|
||||
requests>=2.32.4
|
||||
python-slugify>=8.0.4
|
||||
packaging>=26.2
|
||||
mozjpeg-lossless-optimization>=1.2.0
|
||||
natsort>=8.4.0
|
||||
distro>=1.8.0
|
||||
numpy==1.23.0
|
||||
distro>=1.9.0
|
||||
numpy==1.23.5
|
||||
PyMuPDF>=1.16
|
||||
|
||||
+2
-2
@@ -1,11 +1,11 @@
|
||||
PySide6<6.10
|
||||
Pillow>=11.3.0
|
||||
psutil>=5.9.5
|
||||
psutil>=7.2.2
|
||||
requests>=2.34.2
|
||||
python-slugify>=8.0.4,<9.0.0
|
||||
packaging>=26.2
|
||||
mozjpeg-lossless-optimization>=1.2.0
|
||||
natsort>=8.4.0
|
||||
distro>=1.8.0
|
||||
distro>=1.9.0
|
||||
numpy>=1.22.4
|
||||
PyMuPDF>=1.18.0
|
||||
|
||||
Reference in New Issue
Block a user