mirror of
https://github.com/ciromattia/kcc
synced 2026-06-28 17:15:27 +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:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v7
|
||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v7
|
||||||
|
|
||||||
- name: Login to GitHub Container Registry
|
- name: Login to GitHub Container Registry
|
||||||
uses: docker/login-action@v4
|
uses: docker/login-action@v4
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v7
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v6
|
uses: actions/setup-python@v6
|
||||||
with:
|
with:
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
MACOSX_DEPLOYMENT_TARGET: '14.0'
|
MACOSX_DEPLOYMENT_TARGET: '14.0'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v7
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v6
|
uses: actions/setup-python@v6
|
||||||
with:
|
with:
|
||||||
|
|||||||
@@ -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@v6
|
- uses: actions/checkout@v7
|
||||||
- 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
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ jobs:
|
|||||||
command: build_c2p
|
command: build_c2p
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v7
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v6
|
uses: actions/setup-python@v6
|
||||||
with:
|
with:
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
WINDOWS_7: 1
|
WINDOWS_7: 1
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v7
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v6
|
uses: actions/setup-python@v6
|
||||||
with:
|
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)
|
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]
|
||||||
@@ -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]
|
--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'
|
||||||
|
|||||||
+787
-734
File diff suppressed because it is too large
Load Diff
@@ -273,6 +273,12 @@ 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:
|
||||||
@@ -281,6 +287,8 @@ 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:
|
||||||
@@ -379,6 +387,8 @@ 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())
|
||||||
|
|
||||||
@@ -393,7 +403,10 @@ class WorkerThread(QThread):
|
|||||||
for job in currentJobs:
|
for job in currentJobs:
|
||||||
bookDir.append(job)
|
bookDir.append(job)
|
||||||
try:
|
try:
|
||||||
|
fusion_source_parent = str(Path(bookDir[0]).parent)
|
||||||
comic2ebook.options = comic2ebook.checkOptions(copy(options))
|
comic2ebook.options = comic2ebook.checkOptions(copy(options))
|
||||||
|
if options.output is None:
|
||||||
|
options.output = fusion_source_parent
|
||||||
currentJobs.clear()
|
currentJobs.clear()
|
||||||
currentJobs.append(comic2ebook.makeFusion(bookDir))
|
currentJobs.append(comic2ebook.makeFusion(bookDir))
|
||||||
MW.addMessage.emit('Created fusion at ' + currentJobs[0], 'info', False)
|
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)
|
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:
|
if 'MOBI' in gui_current_format and not options.lightnovel:
|
||||||
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')
|
||||||
@@ -506,7 +519,6 @@ 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)
|
||||||
@@ -527,8 +539,6 @@ 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:
|
||||||
@@ -557,13 +567,6 @@ class WorkerThread(QThread):
|
|||||||
move(item, GUI.targetDirectory)
|
move(item, GUI.targetDirectory)
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
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.content = ''
|
||||||
GUI.progress.stop()
|
GUI.progress.stop()
|
||||||
MW.hideProgressBar.emit()
|
MW.hideProgressBar.emit()
|
||||||
@@ -1084,8 +1087,13 @@ 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(),
|
||||||
@@ -1193,6 +1201,11 @@ 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
|
||||||
@@ -1202,7 +1215,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', 'kcc9')
|
self.settings = QSettings('ciromattia', 'kcc10')
|
||||||
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))
|
||||||
@@ -1516,6 +1529,8 @@ 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]))
|
||||||
|
|||||||
+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'
|
__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'
|
||||||
|
|||||||
@@ -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, copyfile
|
from shutil import move, copytree, rmtree
|
||||||
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
|
from PIL import Image, ImageFile, ImageOps
|
||||||
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
|
||||||
@@ -76,23 +76,19 @@ def main(argv=None):
|
|||||||
print('No matching files found.')
|
print('No matching files found.')
|
||||||
return 1
|
return 1
|
||||||
if options.filefusion:
|
if options.filefusion:
|
||||||
|
fusion_source_parent = str(Path(sources[0]).parent)
|
||||||
fusion_path = makeFusion(list(sources))
|
fusion_path = makeFusion(list(sources))
|
||||||
sources.clear()
|
sources.clear()
|
||||||
sources.append(fusion_path)
|
sources.append(fusion_path)
|
||||||
for source in sources:
|
for source in sources:
|
||||||
source = source.rstrip('\\').rstrip('/')
|
source = source.rstrip('\\').rstrip('/')
|
||||||
options = copy(args)
|
options = copy(args)
|
||||||
|
if options.filefusion and options.output is None:
|
||||||
|
options.output = fusion_source_parent
|
||||||
options = checkOptions(options)
|
options = checkOptions(options)
|
||||||
print('Working on ' + source + '...')
|
print('Working on ' + source + '...')
|
||||||
makeBook(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
|
return 0
|
||||||
|
|
||||||
|
|
||||||
@@ -230,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",
|
||||||
"<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",
|
"<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",
|
||||||
@@ -296,10 +292,22 @@ 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.righttoleft:
|
|
||||||
writingmode = "horizontal-rl"
|
if options.vertical4panel:
|
||||||
|
writingmode = "vertical"
|
||||||
else:
|
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 = 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\" ",
|
||||||
@@ -307,7 +315,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",
|
||||||
"<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: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:
|
||||||
@@ -398,13 +406,22 @@ 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:
|
||||||
@@ -948,6 +965,8 @@ 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)
|
||||||
@@ -979,10 +998,11 @@ def getWorkFolder(afile, workdir=None):
|
|||||||
manifest_dict[manifest_item.attrib.get('id')] = manifest_item.attrib.get('href')
|
manifest_dict[manifest_item.attrib.get('id')] = manifest_item.attrib.get('href')
|
||||||
ordered_image_paths = []
|
ordered_image_paths = []
|
||||||
for i, spine_item in enumerate(spine):
|
for i, spine_item in enumerate(spine):
|
||||||
if spine_item not in manifest_dict:
|
try:
|
||||||
continue
|
|
||||||
page_path = os.path.join(os.path.dirname(opf_path), manifest_dict[spine_item])
|
page_path = os.path.join(os.path.dirname(opf_path), manifest_dict[spine_item])
|
||||||
page = ET.parse(page_path)
|
page = ET.parse(page_path)
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
imgs = page.findall(r'.//{*}img') + page.findall(r'.//{*}image')
|
imgs = page.findall(r'.//{*}img') + page.findall(r'.//{*}image')
|
||||||
|
|
||||||
largest_size = 0
|
largest_size = 0
|
||||||
@@ -1388,7 +1408,6 @@ 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')
|
||||||
@@ -1431,10 +1450,18 @@ 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,
|
||||||
@@ -1450,6 +1477,8 @@ 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]")
|
||||||
@@ -1557,6 +1586,9 @@ 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')
|
||||||
|
|
||||||
@@ -1676,6 +1708,11 @@ 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-'):
|
||||||
@@ -1691,7 +1728,7 @@ def makeFusion(sources: List[str]):
|
|||||||
start = perf_counter()
|
start = perf_counter()
|
||||||
first_path = Path(sources[0])
|
first_path = Path(sources[0])
|
||||||
|
|
||||||
if True:
|
if options.tempdir:
|
||||||
fusion_parent = first_path.parent
|
fusion_parent = first_path.parent
|
||||||
else:
|
else:
|
||||||
# LLL is after KCC
|
# LLL is after KCC
|
||||||
@@ -1721,7 +1758,9 @@ 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}')
|
||||||
|
|
||||||
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')
|
sanitizeTree(targetpath, prefix='fusion')
|
||||||
# TODO: remove flattenTree when subchapters are supported
|
# TODO: remove flattenTree when subchapters are supported
|
||||||
flattenTree(targetpath)
|
flattenTree(targetpath)
|
||||||
@@ -1747,6 +1786,35 @@ 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)
|
||||||
@@ -1837,7 +1905,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(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':
|
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
|
||||||
@@ -1856,19 +1924,11 @@ 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(tome + '_comic', tome, job_progress, True)
|
makeZIP(filepath[-1], 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':
|
if not GUI and options.format == 'MOBI' and not options.lightnovel:
|
||||||
print(f"{job_progress}Creating MOBI files...")
|
print(f"{job_progress}Creating MOBI files...")
|
||||||
work = []
|
work = []
|
||||||
for i in filepath:
|
for i in filepath:
|
||||||
@@ -1887,8 +1947,6 @@ 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:
|
||||||
@@ -1899,22 +1957,23 @@ def makeBook(source, qtgui=None, job_progress=''):
|
|||||||
|
|
||||||
end = perf_counter()
|
end = perf_counter()
|
||||||
print(f"{job_progress}makeBook: {end - start} seconds")
|
print(f"{job_progress}makeBook: {end - start} seconds")
|
||||||
# Clean up temporary workspace
|
|
||||||
try:
|
if options.filefusion:
|
||||||
rmtree(path, True)
|
rmtree(source, True)
|
||||||
except Exception:
|
checkPre('LLL-')
|
||||||
pass
|
|
||||||
return filepath
|
return filepath
|
||||||
|
|
||||||
|
|
||||||
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 + '_toclean', mobiPath, bytes(uuid, 'UTF-8'), is_pdoc)
|
dualmetafix.DualMobiMetaFix(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)]
|
||||||
@@ -1936,8 +1995,11 @@ 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
|
||||||
|
|||||||
@@ -136,12 +136,11 @@ def del_exth(rec0, exth_num):
|
|||||||
|
|
||||||
|
|
||||||
class DualMobiMetaFix:
|
class DualMobiMetaFix:
|
||||||
def __init__(self, infile, outfile, asin, is_pdoc):
|
def __init__(self, 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)
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
Pillow>=11.3.0
|
Pillow>=11.3.0
|
||||||
psutil>=5.9.5
|
psutil>=7.2.2
|
||||||
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.8.0
|
distro>=1.9.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
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
PySide6==6.4.3
|
PySide6==6.4.3
|
||||||
Pillow>=11.3.0
|
Pillow>=11.3.0
|
||||||
psutil>=5.9.5
|
psutil>=7.2.2
|
||||||
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.8.0
|
distro>=1.9.0
|
||||||
numpy<2
|
numpy<2
|
||||||
PyMuPDF==1.25.5
|
PyMuPDF==1.25.5
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
PySide6==6.1.3
|
PySide6==6.1.3
|
||||||
Pillow>=9
|
Pillow>=9
|
||||||
psutil>=5.9.5
|
psutil>=7.2.2
|
||||||
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.8.0
|
distro>=1.9.0
|
||||||
numpy==1.23.0
|
numpy==1.23.5
|
||||||
PyMuPDF>=1.16
|
PyMuPDF>=1.16
|
||||||
|
|||||||
+2
-2
@@ -1,11 +1,11 @@
|
|||||||
PySide6<6.10
|
PySide6<6.10
|
||||||
Pillow>=11.3.0
|
Pillow>=11.3.0
|
||||||
psutil>=5.9.5
|
psutil>=7.2.2
|
||||||
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.8.0
|
distro>=1.9.0
|
||||||
numpy>=1.22.4
|
numpy>=1.22.4
|
||||||
PyMuPDF>=1.18.0
|
PyMuPDF>=1.18.0
|
||||||
|
|||||||
Reference in New Issue
Block a user