1
0
mirror of https://github.com/ciromattia/kcc synced 2026-04-18 23:19:00 +00:00

Compare commits

..

15 Commits

Author SHA1 Message Date
Alex Xu
3f4ef3e21e Bump version to 9.3.7 2025-12-08 20:59:26 -08:00
Alex Xu
4733c6348b specify macOS 12 minimum for standard builds (#1188) 2025-12-08 20:58:15 -08:00
Alex Xu
5ad23d9629 mention color 2025-12-08 19:50:34 -08:00
Alex Xu
db4eb78963 Bump version to 9.3.6 2025-12-08 19:15:47 -08:00
Alex Xu
988fc93dc5 Fix macOS 10.14+ legacy compatibility (#1187)
* Update requirements-osx-legacy.txt

* upgrade macos-13 to macos-15-intel

* upgrade macos-13 to macos-15-intel
2025-12-08 18:08:16 -08:00
Alex Xu
74fee9346c Bump version to 9.3.5 2025-12-03 19:15:45 -08:00
Alex Xu
9fcacd7ae6 fix comicinfo detection in corner case (9.3.4 regression) (#1185) 2025-12-03 19:13:19 -08:00
Alex Xu
8ac58e361f Bump version to 9.3.4 2025-12-03 10:23:00 -08:00
kiryl
61d6972e22 Sync setup install_requires with requirements.txt (#1176)
* remove duplicated PyMuPDF entry and change packages order for easier comparison with requirements.txt

* update packages versions to be synced to each other (requirements.txt vs install_requires in setuptools.setup()

* add missing pyinstaller package which is required to build exe/app

* clarify minimums

* fix typo

* remove pyinstaller

Remove pyinstaller from the requirements.

---------

Co-authored-by: Alex Xu <alexkurosakimh3@gmail.com>
2025-12-02 21:13:26 -08:00
Alex Xu
c7c1557e72 add tomenumber when output folder checked (#1183)
* add tomenumber when output checked

* fix all cases
2025-12-02 20:54:46 -08:00
kiryl
cb93704e08 Mention tabulation order in README.md (#1181) 2025-12-02 15:20:52 -08:00
kiryl
62c5183609 set tabulation order for KCC fields (#1178)
* set tabulation order for KCC fields

- loop through fields in more organized order including fields which visibility depends on some checkboxes state

* don't change rc

---------

Co-authored-by: Alex Xu <alexkurosakimh3@gmail.com>
2025-12-02 12:36:17 -08:00
kiryl
a629f267a1 set tabulation order for metadata editor fields (#1177)
* set tabulation order for metadata editor fields

- loop through fields in from top to down order

* don't change rc

---------

Co-authored-by: Alex Xu <alexkurosakimh3@gmail.com>
2025-12-02 12:33:38 -08:00
Alex Xu
aeec4dd294 fix output folder with period (#1180) 2025-12-02 12:04:15 -08:00
Alex Xu
0d3076465b use less file operations (#1174) 2025-12-01 19:27:17 -08:00
12 changed files with 110 additions and 53 deletions

View File

@@ -25,8 +25,10 @@ jobs:
build: build:
strategy: strategy:
matrix: matrix:
os: [ macos-13, macos-14 ] os: [ macos-15-intel, macos-14 ]
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
env:
MACOSX_DEPLOYMENT_TARGET: '12'
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v6
- name: Set up Python - name: Set up Python

View File

@@ -23,7 +23,7 @@ jobs:
build: build:
strategy: strategy:
matrix: matrix:
os: [ macos-13 ] os: [ macos-15-intel ]
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
env: env:
# We need the official Python, because the GA ones only support newer macOS versions # We need the official Python, because the GA ones only support newer macOS versions

View File

@@ -7,7 +7,7 @@
[![Github All Releases](https://img.shields.io/github/downloads/ciromattia/kcc/total.svg)](https://github.com/ciromattia/kcc/releases) [![Github All Releases](https://img.shields.io/github/downloads/ciromattia/kcc/total.svg)](https://github.com/ciromattia/kcc/releases)
**Kindle Comic Converter** optimizes black & white comics and manga for E-ink ereaders **Kindle Comic Converter** optimizes black & white or color comics and manga for E-ink ereaders
like Kindle, Kobo, ReMarkable, and more. like Kindle, Kobo, ReMarkable, and more.
Pages display in fullscreen without margins, Pages display in fullscreen without margins,
with proper fixed layout support. with proper fixed layout support.
@@ -318,6 +318,7 @@ Depending on your system [Python](https://www.python.org) may be called either `
If you want to edit the code, a good code editor is [VS Code](https://code.visualstudio.com). If you want to edit the code, a good code editor is [VS Code](https://code.visualstudio.com).
If you want to edit the `.ui` files, use `pyside6-designer` which is included in the `pip install pyside6`. If you want to edit the `.ui` files, use `pyside6-designer` which is included in the `pip install pyside6`.
If new objects have been added, verify that correct tab order has been applied by using [Tab Order Editing Mode](https://doc.qt.io/qt-6/designer-tab-order.html).
Then use the `gen_ui_files` scripts to autogenerate the python UI. Then use the `gen_ui_files` scripts to autogenerate the python UI.
An example PR adding a new checkbox is here: https://github.com/ciromattia/kcc/pull/785 An example PR adding a new checkbox is here: https://github.com/ciromattia/kcc/pull/785

View File

@@ -898,36 +898,47 @@
</widget> </widget>
</widget> </widget>
<tabstops> <tabstops>
<tabstop>convertButton</tabstop> <tabstop>jobList</tabstop>
<tabstop>fileButton</tabstop>
<tabstop>clearButton</tabstop> <tabstop>clearButton</tabstop>
<tabstop>defaultOutputFolderButton</tabstop>
<tabstop>defaultOutputFolderBox</tabstop>
<tabstop>deviceBox</tabstop> <tabstop>deviceBox</tabstop>
<tabstop>widthBox</tabstop>
<tabstop>heightBox</tabstop>
<tabstop>formatBox</tabstop> <tabstop>formatBox</tabstop>
<tabstop>convertButton</tabstop>
<tabstop>mangaBox</tabstop> <tabstop>mangaBox</tabstop>
<tabstop>rotateBox</tabstop> <tabstop>rotateBox</tabstop>
<tabstop>qualityBox</tabstop> <tabstop>qualityBox</tabstop>
<tabstop>webtoonBox</tabstop> <tabstop>webtoonBox</tabstop>
<tabstop>upscaleBox</tabstop> <tabstop>upscaleBox</tabstop>
<tabstop>gammaBox</tabstop> <tabstop>gammaBox</tabstop>
<tabstop>gammaSlider</tabstop>
<tabstop>borderBox</tabstop> <tabstop>borderBox</tabstop>
<tabstop>outputSplit</tabstop> <tabstop>outputSplit</tabstop>
<tabstop>colorBox</tabstop> <tabstop>colorBox</tabstop>
<tabstop>mozJpegBox</tabstop> <tabstop>mozJpegBox</tabstop>
<tabstop>maximizeStrips</tabstop> <tabstop>maximizeStrips</tabstop>
<tabstop>croppingBox</tabstop> <tabstop>croppingBox</tabstop>
<tabstop>croppingPowerSlider</tabstop>
<tabstop>preserveMarginBox</tabstop>
<tabstop>spreadShiftBox</tabstop> <tabstop>spreadShiftBox</tabstop>
<tabstop>deleteBox</tabstop> <tabstop>deleteBox</tabstop>
<tabstop>disableProcessingBox</tabstop> <tabstop>disableProcessingBox</tabstop>
<tabstop>chunkSizeBox</tabstop> <tabstop>fileFusionBox</tabstop>
<tabstop>noRotateBox</tabstop> <tabstop>noRotateBox</tabstop>
<tabstop>interPanelCropBox</tabstop> <tabstop>interPanelCropBox</tabstop>
<tabstop>metadataTitleBox</tabstop>
<tabstop>chunkSizeCheckBox</tabstop>
<tabstop>chunkSizeBox</tabstop>
<tabstop>eraseRainbowBox</tabstop> <tabstop>eraseRainbowBox</tabstop>
<tabstop>heightBox</tabstop> <tabstop>rotateFirstBox</tabstop>
<tabstop>croppingPowerSlider</tabstop> <tabstop>autoLevelBox</tabstop>
<tabstop>autocontrastBox</tabstop>
<tabstop>editorButton</tabstop> <tabstop>editorButton</tabstop>
<tabstop>kofiButton</tabstop>
<tabstop>wikiButton</tabstop> <tabstop>wikiButton</tabstop>
<tabstop>jobList</tabstop>
<tabstop>gammaSlider</tabstop>
<tabstop>widthBox</tabstop>
</tabstops> </tabstops>
<resources> <resources>
<include location="KCC.qrc"/> <include location="KCC.qrc"/>

View File

@@ -192,6 +192,18 @@
</item> </item>
</layout> </layout>
</widget> </widget>
<tabstops>
<tabstop>seriesLine</tabstop>
<tabstop>volumeLine</tabstop>
<tabstop>titleLine</tabstop>
<tabstop>numberLine</tabstop>
<tabstop>writerLine</tabstop>
<tabstop>pencillerLine</tabstop>
<tabstop>inkerLine</tabstop>
<tabstop>coloristLine</tabstop>
<tabstop>okButton</tabstop>
<tabstop>cancelButton</tabstop>
</tabstops>
<resources> <resources>
<include location="KCC.qrc"/> <include location="KCC.qrc"/>
</resources> </resources>

View File

@@ -469,35 +469,46 @@ class Ui_mainWindow(object):
self.statusBar.setObjectName(u"statusBar") self.statusBar.setObjectName(u"statusBar")
self.statusBar.setSizeGripEnabled(False) self.statusBar.setSizeGripEnabled(False)
mainWindow.setStatusBar(self.statusBar) mainWindow.setStatusBar(self.statusBar)
QWidget.setTabOrder(self.convertButton, self.clearButton) QWidget.setTabOrder(self.jobList, self.fileButton)
QWidget.setTabOrder(self.clearButton, self.deviceBox) QWidget.setTabOrder(self.fileButton, self.clearButton)
QWidget.setTabOrder(self.deviceBox, self.formatBox) QWidget.setTabOrder(self.clearButton, self.defaultOutputFolderButton)
QWidget.setTabOrder(self.formatBox, self.mangaBox) QWidget.setTabOrder(self.defaultOutputFolderButton, self.defaultOutputFolderBox)
QWidget.setTabOrder(self.defaultOutputFolderBox, self.deviceBox)
QWidget.setTabOrder(self.deviceBox, self.widthBox)
QWidget.setTabOrder(self.widthBox, self.heightBox)
QWidget.setTabOrder(self.heightBox, self.formatBox)
QWidget.setTabOrder(self.formatBox, self.convertButton)
QWidget.setTabOrder(self.convertButton, self.mangaBox)
QWidget.setTabOrder(self.mangaBox, self.rotateBox) QWidget.setTabOrder(self.mangaBox, self.rotateBox)
QWidget.setTabOrder(self.rotateBox, self.qualityBox) QWidget.setTabOrder(self.rotateBox, self.qualityBox)
QWidget.setTabOrder(self.qualityBox, self.webtoonBox) QWidget.setTabOrder(self.qualityBox, self.webtoonBox)
QWidget.setTabOrder(self.webtoonBox, self.upscaleBox) QWidget.setTabOrder(self.webtoonBox, self.upscaleBox)
QWidget.setTabOrder(self.upscaleBox, self.gammaBox) QWidget.setTabOrder(self.upscaleBox, self.gammaBox)
QWidget.setTabOrder(self.gammaBox, self.borderBox) QWidget.setTabOrder(self.gammaBox, self.gammaSlider)
QWidget.setTabOrder(self.gammaSlider, self.borderBox)
QWidget.setTabOrder(self.borderBox, self.outputSplit) QWidget.setTabOrder(self.borderBox, self.outputSplit)
QWidget.setTabOrder(self.outputSplit, self.colorBox) QWidget.setTabOrder(self.outputSplit, self.colorBox)
QWidget.setTabOrder(self.colorBox, self.mozJpegBox) QWidget.setTabOrder(self.colorBox, self.mozJpegBox)
QWidget.setTabOrder(self.mozJpegBox, self.maximizeStrips) QWidget.setTabOrder(self.mozJpegBox, self.maximizeStrips)
QWidget.setTabOrder(self.maximizeStrips, self.croppingBox) QWidget.setTabOrder(self.maximizeStrips, self.croppingBox)
QWidget.setTabOrder(self.croppingBox, self.spreadShiftBox) QWidget.setTabOrder(self.croppingBox, self.croppingPowerSlider)
QWidget.setTabOrder(self.croppingPowerSlider, self.preserveMarginBox)
QWidget.setTabOrder(self.preserveMarginBox, self.spreadShiftBox)
QWidget.setTabOrder(self.spreadShiftBox, self.deleteBox) QWidget.setTabOrder(self.spreadShiftBox, self.deleteBox)
QWidget.setTabOrder(self.deleteBox, self.disableProcessingBox) QWidget.setTabOrder(self.deleteBox, self.disableProcessingBox)
QWidget.setTabOrder(self.disableProcessingBox, self.chunkSizeBox) QWidget.setTabOrder(self.disableProcessingBox, self.fileFusionBox)
QWidget.setTabOrder(self.chunkSizeBox, self.noRotateBox) QWidget.setTabOrder(self.fileFusionBox, self.noRotateBox)
QWidget.setTabOrder(self.noRotateBox, self.interPanelCropBox) QWidget.setTabOrder(self.noRotateBox, self.interPanelCropBox)
QWidget.setTabOrder(self.interPanelCropBox, self.eraseRainbowBox) QWidget.setTabOrder(self.interPanelCropBox, self.metadataTitleBox)
QWidget.setTabOrder(self.eraseRainbowBox, self.heightBox) QWidget.setTabOrder(self.metadataTitleBox, self.chunkSizeCheckBox)
QWidget.setTabOrder(self.heightBox, self.croppingPowerSlider) QWidget.setTabOrder(self.chunkSizeCheckBox, self.chunkSizeBox)
QWidget.setTabOrder(self.croppingPowerSlider, self.editorButton) QWidget.setTabOrder(self.chunkSizeBox, self.eraseRainbowBox)
QWidget.setTabOrder(self.editorButton, self.wikiButton) QWidget.setTabOrder(self.eraseRainbowBox, self.rotateFirstBox)
QWidget.setTabOrder(self.wikiButton, self.jobList) QWidget.setTabOrder(self.rotateFirstBox, self.autoLevelBox)
QWidget.setTabOrder(self.jobList, self.gammaSlider) QWidget.setTabOrder(self.autoLevelBox, self.autocontrastBox)
QWidget.setTabOrder(self.gammaSlider, self.widthBox) QWidget.setTabOrder(self.autocontrastBox, self.editorButton)
QWidget.setTabOrder(self.editorButton, self.kofiButton)
QWidget.setTabOrder(self.kofiButton, self.wikiButton)
self.retranslateUi(mainWindow) self.retranslateUi(mainWindow)

View File

@@ -156,6 +156,15 @@ class Ui_editorDialog(object):
self.verticalLayout.addWidget(self.optionWidget) self.verticalLayout.addWidget(self.optionWidget)
QWidget.setTabOrder(self.seriesLine, self.volumeLine)
QWidget.setTabOrder(self.volumeLine, self.titleLine)
QWidget.setTabOrder(self.titleLine, self.numberLine)
QWidget.setTabOrder(self.numberLine, self.writerLine)
QWidget.setTabOrder(self.writerLine, self.pencillerLine)
QWidget.setTabOrder(self.pencillerLine, self.inkerLine)
QWidget.setTabOrder(self.inkerLine, self.coloristLine)
QWidget.setTabOrder(self.coloristLine, self.okButton)
QWidget.setTabOrder(self.okButton, self.cancelButton)
self.retranslateUi(editorDialog) self.retranslateUi(editorDialog)

View File

@@ -1,4 +1,4 @@
__version__ = '9.3.3' __version__ = '9.3.7'
__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'

View File

@@ -870,6 +870,9 @@ def getWorkFolder(afile):
raise UserWarning("Not enough disk space to perform conversion.") raise UserWarning("Not enough disk space to perform conversion.")
if afile.lower().endswith('.pdf'): if afile.lower().endswith('.pdf'):
workdir = mkdtemp('', 'KCC-', os.path.dirname(afile)) workdir = mkdtemp('', 'KCC-', os.path.dirname(afile))
fullPath = os.path.join(workdir, 'OEBPS', 'Images')
if not os.path.exists(fullPath):
os.makedirs(fullPath)
path = workdir path = workdir
sanitizePermissions(path) sanitizePermissions(path)
target_height = options.profileData[1][1] target_height = options.profileData[1][1]
@@ -878,39 +881,44 @@ def getWorkFolder(afile):
elif options.cropping == 2: elif options.cropping == 2:
target_height = target_height + target_height*0.25 #Account for possible margin at the top and bottom with page number target_height = target_height + target_height*0.25 #Account for possible margin at the top and bottom with page number
try: try:
mupdf_pdf_process_pages_parallel(afile, workdir, target_height) mupdf_pdf_process_pages_parallel(afile, fullPath, target_height)
except Exception as e: except Exception as e:
rmtree(path, True) rmtree(path, True)
raise UserWarning(f"Failed to extract images from PDF file. {e}") raise UserWarning(f"Failed to extract images from PDF file. {e}")
return workdir
else: else:
workdir = mkdtemp('', 'KCC-', os.path.dirname(afile)) workdir = mkdtemp('', 'KCC-', os.path.dirname(afile))
fullPath = os.path.join(workdir, 'OEBPS', 'Images')
if not os.path.exists(fullPath):
os.makedirs(fullPath)
try: try:
cbx = comicarchive.ComicArchive(afile) cbx = comicarchive.ComicArchive(afile)
path = cbx.extract(workdir) path = cbx.extract(fullPath)
sanitizePermissions(path) sanitizePermissions(path)
tdir = os.listdir(workdir) tdir = os.listdir(fullPath)
if len(tdir) == 2 and 'ComicInfo.xml' in tdir: if len(tdir) == 2 and 'ComicInfo.xml' in tdir:
tdir.remove('ComicInfo.xml') tdir.remove('ComicInfo.xml')
if os.path.isdir(os.path.join(workdir, tdir[0])): if os.path.isdir(os.path.join(fullPath, tdir[0])):
os.replace( os.replace(
os.path.join(workdir, 'ComicInfo.xml'), os.path.join(fullPath, 'ComicInfo.xml'),
os.path.join(workdir, tdir[0], 'ComicInfo.xml') os.path.join(fullPath, tdir[0], 'ComicInfo.xml')
) )
if len(tdir) == 1 and os.path.isdir(os.path.join(workdir, tdir[0])): if len(tdir) == 1 and os.path.isdir(os.path.join(fullPath, tdir[0])):
path = os.path.join(workdir, tdir[0]) for file in os.listdir(os.path.join(fullPath, tdir[0])):
move(os.path.join(fullPath, tdir[0], file), fullPath)
os.rmdir(os.path.join(fullPath, tdir[0]))
return workdir
except OSError as e: except OSError as e:
rmtree(workdir, True) rmtree(workdir, True)
raise UserWarning(e) raise UserWarning(e)
else: else:
raise UserWarning("Failed to open source file/directory.") raise UserWarning("Failed to open source file/directory.")
newpath = mkdtemp('', 'KCC-', os.path.dirname(afile))
os.renames(path, os.path.join(newpath, 'OEBPS', 'Images'))
return newpath
def getOutputFilename(srcpath, wantedname, ext, tomenumber): def getOutputFilename(srcpath, wantedname, ext, tomenumber):
source_path = Path(srcpath)
if srcpath[-1] == os.path.sep: if srcpath[-1] == os.path.sep:
srcpath = srcpath[:-1] srcpath = srcpath[:-1]
if 'Ko' in options.profile and options.format == 'EPUB': if 'Ko' in options.profile and options.format == 'EPUB':
@@ -923,22 +931,26 @@ def getOutputFilename(srcpath, wantedname, ext, tomenumber):
wanted_root, wanted_ext = os.path.splitext(wantedname) wanted_root, wanted_ext = os.path.splitext(wantedname)
if wantedname.endswith(ext): if wantedname.endswith(ext):
filename = os.path.abspath(wantedname) filename = os.path.abspath(wantedname)
elif wanted_ext == '.mobi' and ext == '.epub':
filename = os.path.abspath(wanted_root + ext)
# output directory # output directory
elif not wanted_ext: else:
abs_path = os.path.abspath(options.output) abs_path = os.path.abspath(options.output)
if not os.path.exists(abs_path): if not os.path.exists(abs_path):
os.mkdir(abs_path) os.mkdir(abs_path)
filename = os.path.join(os.path.abspath(options.output), Path(srcpath).stem + ext) if source_path.is_file():
# output file filename = os.path.join(os.path.abspath(options.output), source_path.stem + tomenumber + ext)
else: else:
filename = os.path.abspath(wanted_root) + ext filename = os.path.join(os.path.abspath(options.output), source_path.name + tomenumber + ext)
elif os.path.isdir(srcpath): elif os.path.isdir(srcpath):
filename = srcpath + tomenumber + ext filename = srcpath + tomenumber + ext
else: else:
if 'Ko' in options.profile and options.format == 'EPUB': if 'Ko' in options.profile and options.format == 'EPUB':
src = pathlib.Path(srcpath) if source_path.is_file():
name = re.sub(r'\W+', '_', src.stem) + tomenumber + ext name = re.sub(r'\W+', '_', source_path.stem) + tomenumber + ext
filename = src.with_name(name) else:
name = re.sub(r'\W+', '_', source_path.name) + tomenumber + ext
filename = source_path.with_name(name)
else: else:
filename = os.path.splitext(srcpath)[0] + tomenumber + ext filename = os.path.splitext(srcpath)[0] + tomenumber + ext
if os.path.isfile(filename): if os.path.isfile(filename):

View File

@@ -1,4 +1,4 @@
PySide6==6.5.2 PySide6==6.4.3
Pillow>=11.3.0 Pillow>=11.3.0
psutil>=5.9.5 psutil>=5.9.5
requests>=2.31.0 requests>=2.31.0

View File

@@ -2,7 +2,7 @@ PySide6<6.10
Pillow>=11.3.0 Pillow>=11.3.0
psutil>=5.9.5 psutil>=5.9.5
requests>=2.31.0 requests>=2.31.0
python-slugify>=1.2.1 python-slugify>=1.2.1,<9.0.0
raven>=6.0.0 raven>=6.0.0
packaging>=23.2 packaging>=23.2
mozjpeg-lossless-optimization>=1.2.0 mozjpeg-lossless-optimization>=1.2.0

View File

@@ -148,16 +148,15 @@ setuptools.setup(
}, },
packages=['kindlecomicconverter'], packages=['kindlecomicconverter'],
install_requires=[ install_requires=[
'pyside6>=6.0.0', 'PySide6>=6.0.0',
'Pillow>=9.3.0', 'Pillow>=9.3.0',
'PyMuPDF>=1.18.0',
'psutil>=5.9.5', 'psutil>=5.9.5',
'requests>=2.31.0',
'python-slugify>=1.2.1,<9.0.0', 'python-slugify>=1.2.1,<9.0.0',
'raven>=6.0.0', 'raven>=6.0.0',
'requests>=2.31.0', 'mozjpeg-lossless-optimization>=1.2.0',
'mozjpeg-lossless-optimization>=1.1.2',
'natsort>=8.4.0', 'natsort>=8.4.0',
'distro', 'distro>=1.8.0',
'numpy>=1.22.4', 'numpy>=1.22.4',
'PyMuPDF>=1.16.1', 'PyMuPDF>=1.16.1',
], ],