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

Compare commits

..

28 Commits

Author SHA1 Message Date
Alex Xu
4baca03214 Bump version to 9.4.2 2026-01-11 13:19:41 -08:00
Alex Xu
7de212dca3 move fixed resolution Kindle profiles down 2026-01-10 20:40:09 -08:00
Carlos Lázaro Costa
c99444b96a docs: Update Profiles (#1218)
* Update README Profiles

Align the README Profiles section with the profile definitions
in image.py.

- Add missing Kindle Scribe 3, Colorsoft and reMarkable profiles
- Update gamma values to default (from 1.8 to 1.0)

* Update profile name KS1860

Fix typo (1920 -> 1860)
2026-01-10 16:53:40 -08:00
Alex Xu
6d7a635c3d Fix Kindle Scribe 2025 resolutions (#1217) 2026-01-09 13:46:16 -08:00
Alex Xu
be86bcbf6a Add macOS 15 unsigned instructions 2026-01-08 20:20:10 -08:00
Alex Xu
5cbc07e65d increase webtoon max height (#1213) 2026-01-08 18:21:45 -08:00
Alex Xu
42d94d8202 increase max Windows path length from 180 to 220 (#1211) 2026-01-08 18:21:32 -08:00
フィルターペーパー
7897627c43 Preserve input file order using list instead of set (#1209)
* Preserve input file order using list instead of set

* Simplify list comprehension and fix append for sources

* Remove redundant list() conversion
2026-01-05 20:52:53 -08:00
Alex Xu
8e42fc1162 remove padding from output folder GUI (#1207) 2026-01-01 22:55:54 -08:00
Alex Xu
d6b0e43d70 Bump version to 9.4.1 2026-01-01 22:26:23 -08:00
Alex Xu
af189ed265 add Kindle 1920 profiles (7.4 Scribe behavior) (#1206) 2026-01-01 22:26:04 -08:00
Alex Xu
aa5f4991dd Bump version to 9.4.0 2025-12-29 10:52:34 -08:00
jaroslawjanas
f9064ef0e4 configurable jpg quality (#1205) 2025-12-29 10:37:56 -08:00
Alex Xu
e14abe1787 default jpg quality of 90 for scribe colorsoft (#1204) 2025-12-28 14:54:12 -08:00
Alex Xu
c58387f4f4 partial support for Kindle Scribe 2025 models (#1203)
* partial Kindle Scribe 2025 support

* make variables better

* remove quad
2025-12-27 17:18:05 -08:00
Alex Xu
9b63b7af2c Bump version to 9.3.8 2025-12-27 12:23:39 -08:00
tokyis
f74e108a3e Fixed resizing bug
caused by misplaced closing parenthesis.
2025-12-27 12:22:10 -08:00
Alex Xu
f088ad732e default MACOSX_DEPLOYMENT_TARGET 2025-12-23 13:19:40 -08:00
jaroslawjanas
8e5d57364d Remove environment.yml 2025-12-15 09:48:49 -08:00
dependabot[bot]
b767d5dc2c Bump actions/upload-artifact from 5 to 6 (#1196)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 5 to 6.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-15 09:48:49 -08:00
Alex Xu
7228055bca reduce file operations in webtoon and file fusion (#1191)
* reduce file operations in webtoon

* reduce file operations of file fusion

* fix file fusion failed to prepare

* close webtoon image before remove

* use temp directory
2025-12-14 23:44:30 -08:00
Alex Xu
8c57fbf318 fix add folder button sizePolicy 2025-12-14 18:59:28 -08:00
Alex Xu
7e94861fa1 input folder multiselect (#1195) 2025-12-14 16:13:24 -08:00
Alex Xu
9992ca4d26 fix mac legacy build naming 2025-12-10 22:00:56 -08:00
Alex Xu
f47d1427f0 Update README.md
Clarified description of Kindle Comic Converter's capabilities.
2025-12-09 23:22:31 -08:00
Alex Xu
ce8998375c macOS 14 minimum for non legacy 2025-12-08 22:27:40 -08:00
Alex Xu
8870898a87 macOS 14 minimum for non legacy builds (#1189) 2025-12-08 22:25:59 -08:00
Alex Xu
a017cfd00d specify 12.0 instead of 12 2025-12-08 21:31:38 -08:00
15 changed files with 1077 additions and 880 deletions

View File

@@ -59,7 +59,7 @@ jobs:
env: env:
UPDATE_INFO: gh-releases-zsync|ciromattia|kcc|latest|*x86_64.AppImage.zsync UPDATE_INFO: gh-releases-zsync|ciromattia|kcc|latest|*x86_64.AppImage.zsync
- name: upload artifact - name: upload artifact
uses: actions/upload-artifact@v5 uses: actions/upload-artifact@v6
with: with:
name: AppImage name: AppImage
path: './*.AppImage*' path: './*.AppImage*'

View File

@@ -28,7 +28,7 @@ jobs:
os: [ macos-15-intel, macos-14 ] os: [ macos-15-intel, macos-14 ]
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
env: env:
MACOSX_DEPLOYMENT_TARGET: '12' MACOSX_DEPLOYMENT_TARGET: '14.0'
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v6
- name: Set up Python - name: Set up Python
@@ -80,7 +80,7 @@ jobs:
run: | run: |
python setup.py build_binary python setup.py build_binary
- name: upload build - name: upload build
uses: actions/upload-artifact@v5 uses: actions/upload-artifact@v6
with: with:
name: mac-os-build-${{ runner.arch }} name: mac-os-build-${{ runner.arch }}
path: dist/*.dmg path: dist/*.dmg

View File

@@ -51,7 +51,7 @@ jobs:
run: | run: |
python3 setup.py build_binary python3 setup.py build_binary
- name: upload build - name: upload build
uses: actions/upload-artifact@v5 uses: actions/upload-artifact@v6
with: with:
name: osx-build-${{ runner.arch }} name: osx-build-${{ runner.arch }}
path: dist/*.dmg path: dist/*.dmg

View File

@@ -53,7 +53,7 @@ jobs:
python setup.py ${{ matrix.command }} python setup.py ${{ matrix.command }}
- name: upload-unsigned-artifact - name: upload-unsigned-artifact
id: upload-unsigned-artifact id: upload-unsigned-artifact
uses: actions/upload-artifact@v5 uses: actions/upload-artifact@v6
with: with:
name: windows-build-${{ matrix.entry }} name: windows-build-${{ matrix.entry }}
path: dist/*.exe path: dist/*.exe

View File

@@ -46,7 +46,7 @@ jobs:
python setup.py build_binary python setup.py build_binary
- name: upload-unsigned-artifact - name: upload-unsigned-artifact
id: upload-unsigned-artifact id: upload-unsigned-artifact
uses: actions/upload-artifact@v5 uses: actions/upload-artifact@v6
with: with:
name: windows7-build name: windows7-build
path: dist/*.exe path: dist/*.exe

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 or color 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.
@@ -92,13 +92,13 @@ Click on **Assets** of the latest release.
You probably want either You probably want either
- `KCC_*.*.*.exe` (Windows) - `KCC_*.*.*.exe` (Windows)
- `kcc_macos_arm_*.*.*.dmg` (recent Mac with Apple Silicon M1 chip or later) - `kcc_macos_arm_*.*.*.dmg` (recent Mac with Apple Silicon M1 chip or later)
- `kcc_macos_i386_*.*.*.dmg` (older Mac with Intel chip macOS 12+) - `kcc_macos_i386_*.*.*.dmg` (older Mac with Intel chip macOS 14+)
There are also legacy macOS 10.14+ and Windows 7 experimental versions available. There are also legacy macOS 10.14+ and Windows 7 experimental versions available.
The `c2e` and `c2p` versions are command line tools for power users. The `c2e` and `c2p` versions are command line tools for power users.
On Mac, right click open to get past the security warning. On Mac, follow: https://support.apple.com/guide/mac-help/open-a-mac-app-from-an-unknown-developer-mh40616/mac
For flatpak, Docker, and AppImage versions, refer to the wiki: https://github.com/ciromattia/kcc/wiki/Installation For flatpak, Docker, and AppImage versions, refer to the wiki: https://github.com/ciromattia/kcc/wiki/Installation
@@ -177,38 +177,44 @@ sudo apt-get install python3 p7zip-full python3-pil python3-psutil python3-slugi
### Profiles: ### Profiles:
``` ```
'K1': ("Kindle 1", (600, 670), Palette4, 1.8), 'K1': ("Kindle 1", (600, 670), Palette4, 1.0),
'K11': ("Kindle 11", (1072, 1448), Palette16, 1.8), 'K2': ("Kindle 2", (600, 670), Palette15, 1.0),
'K2': ("Kindle 2", (600, 670), Palette15, 1.8), 'K11': ("Kindle 11", (1072, 1448), Palette16, 1.0),
'K34': ("Kindle Keyboard/Touch", (600, 800), Palette16, 1.8), 'K34': ("Kindle Keyboard/Touch", (600, 800), Palette16, 1.0),
'K57': ("Kindle 5/7", (600, 800), Palette16, 1.8), 'K57': ("Kindle 5/7", (600, 800), Palette16, 1.0),
'K810': ("Kindle 8/10", (600, 800), Palette16, 1.8), 'K810': ("Kindle 8/10", (600, 800), Palette16, 1.0),
'KDX': ("Kindle DX/DXG", (824, 1000), Palette16, 1.8), 'KDX': ("Kindle DX/DXG", (824, 1000), Palette16, 1.0),
'KPW': ("Kindle Paperwhite 1/2", (758, 1024), Palette16, 1.8), 'KPW': ("Kindle Paperwhite 1/2", (758, 1024), Palette16, 1.0),
'KV': ("Kindle Voyage, (1072, 1448), Palette16, 1.8), 'KV': ("Kindle Voyage", (1072, 1448), Palette16, 1.0),
'KPW34': ("Kindle Paperwhite 3/4/Oasis", (1072, 1448), Palette16, 1.8), 'KPW34': ("Kindle Paperwhite 3/4", (1072, 1448), Palette16, 1.0),
'KPW5': ("Kindle Paperwhite 5/Signature Edition", (1236, 1648), Palette16, 1.8), 'KPW5': ("Kindle Paperwhite 5/Signature Edition", (1236, 1648), Palette16, 1.0),
'KO': ("Kindle Oasis 2/3/Paperwhite 12/Colorsoft 12", (1264, 1680), Palette16, 1.8), 'KO': ("Kindle Oasis 2/3/Paperwhite 12", (1264, 1680), Palette16, 1.0),
'KS': ("Kindle Scribe", (1860, 2480), Palette16, 1.8), 'KCS': ("Kindle Colorsoft", (1264, 1680), Palette16, 1.0),
'KoMT': ("Kobo Mini/Touch", (600, 800), Palette16, 1.8), 'KS1860': ("Kindle 1860", (1860, 1920), Palette16, 1.0),
'KoG': ("Kobo Glo", (768, 1024), Palette16, 1.8), 'KS1920': ("Kindle 1920", (1920, 1920), Palette16, 1.0),
'KoGHD': ("Kobo Glo HD", (1072, 1448), Palette16, 1.8), 'KS': ("Kindle Scribe 1/2", (1860, 2480), Palette16, 1.0),
'KoA': ("Kobo Aura", (758, 1024), Palette16, 1.8), 'KS3': ("Kindle Scribe 3", (1986, 2648), Palette16, 1.0),
'KoAHD': ("Kobo Aura HD", (1080, 1440), Palette16, 1.8), 'KSCS': ("Kindle Scribe Colorsoft", (1986, 2648), Palette16, 1.0),
'KoAH2O': ("Kobo Aura H2O", (1080, 1430), Palette16, 1.8), 'KoMT': ("Kobo Mini/Touch", (600, 800), Palette16, 1.0),
'KoAO': ("Kobo Aura ONE", (1404, 1872), Palette16, 1.8), 'KoG': ("Kobo Glo", (768, 1024), Palette16, 1.0),
'KoN': ("Kobo Nia", (758, 1024), Palette16, 1.8), 'KoGHD': ("Kobo Glo HD", (1072, 1448), Palette16, 1.0),
'KoC': ("Kobo Clara HD/Kobo Clara 2E", (1072, 1448), Palette16, 1.8), 'KoA': ("Kobo Aura", (758, 1024), Palette16, 1.0),
'KoCC': ("Kobo Clara Colour", (1072, 1448), Palette16, 1.8), 'KoAHD': ("Kobo Aura HD", (1080, 1440), Palette16, 1.0),
'KoL': ("Kobo Libra H2O/Kobo Libra 2", (1264, 1680), Palette16, 1.8), 'KoAH2O': ("Kobo Aura H2O", (1080, 1430), Palette16, 1.0),
'KoLC': ("Kobo Libra Colour", (1264, 1680), Palette16, 1.8), 'KoAO': ("Kobo Aura ONE", (1404, 1872), Palette16, 1.0),
'KoF': ("Kobo Forma", (1440, 1920), Palette16, 1.8), 'KoN': ("Kobo Nia", (758, 1024), Palette16, 1.0),
'KoS': ("Kobo Sage", (1440, 1920), Palette16, 1.8), 'KoC': ("Kobo Clara HD/Kobo Clara 2E", (1072, 1448), Palette16, 1.0),
'KoE': ("Kobo Elipsa", (1404, 1872), Palette16, 1.8), 'KoCC': ("Kobo Clara Colour", (1072, 1448), Palette16, 1.0),
'Rmk1': ("reMarkable 1", (1404, 1872), Palette16, 1.8), 'KoL': ("Kobo Libra H2O/Kobo Libra 2", (1264, 1680), Palette16, 1.0),
'Rmk2': ("reMarkable 2", (1404, 1872), Palette16, 1.8), 'KoLC': ("Kobo Libra Colour", (1264, 1680), Palette16, 1.0),
'RmkPP': ("reMarkable Paper Pro", (1620, 2160), Palette16, 1.8), 'KoF': ("Kobo Forma", (1440, 1920), Palette16, 1.0),
'OTHER': ("Other", (0, 0), Palette16, 1.8), 'KoS': ("Kobo Sage", (1440, 1920), Palette16, 1.0),
'KoE': ("Kobo Elipsa", (1404, 1872), Palette16, 1.0),
'Rmk1': ("reMarkable 1", (1404, 1872), Palette16, 1.0),
'Rmk2': ("reMarkable 2", (1404, 1872), Palette16, 1.0),
'RmkPP': ("reMarkable Paper Pro", (1620, 2160), Palette16, 1.0),
'RmkPPMove': ("reMarkable Paper Pro Move", (954, 1696), Palette16, 1.0),
'OTHER': ("Other", (0, 0), Palette16, 1.0),
``` ```
### Standalone `kcc-c2e.py` usage: ### Standalone `kcc-c2e.py` usage:
@@ -255,6 +261,7 @@ PROCESSING:
--forcecolor Don't convert images to grayscale --forcecolor Don't convert images to grayscale
--forcepng Create PNG files instead JPEG --forcepng Create PNG files instead JPEG
--mozjpeg Create JPEG files using mozJpeg --mozjpeg Create JPEG files using mozJpeg
--jpeg-quality The JPEG quality, on a scale from 0 (worst) to 95 (best). Default 85 for most devices.
--maximizestrips Turn 1x4 strips to 2x2 strips --maximizestrips Turn 1x4 strips to 2x2 strips
-d, --delete Delete source file(s) or a directory. It's not recoverable. -d, --delete Delete source file(s) or a directory. It's not recoverable.

View File

@@ -1,16 +0,0 @@
name: kcc
channels:
- conda-forge
- defaults
dependencies:
- python=3.11
- Pillow>=11.3.0
- psutil>=5.9.5
- python-slugify>=1.2.1
- raven>=6.0.0
- distro
- natsort>=8.4.0
- pip
- pip:
- mozjpeg-lossless-optimization>=1.1.2
- pyside6>=6.5.1

1023
gui/KCC.ui

File diff suppressed because it is too large Load Diff

View File

@@ -22,7 +22,7 @@ import itertools
from pathlib import Path from pathlib import Path
from PySide6.QtCore import (QSize, QUrl, Qt, Signal, QIODeviceBase, QEvent, QThread, QSettings) from PySide6.QtCore import (QSize, QUrl, Qt, Signal, QIODeviceBase, QEvent, QThread, QSettings)
from PySide6.QtGui import (QColor, QIcon, QPixmap, QDesktopServices) from PySide6.QtGui import (QColor, QIcon, QPixmap, QDesktopServices)
from PySide6.QtWidgets import (QApplication, QLabel, QListWidgetItem, QMainWindow, QSystemTrayIcon, QFileDialog, QMessageBox, QDialog) from PySide6.QtWidgets import (QApplication, QLabel, QListWidgetItem, QMainWindow, QSystemTrayIcon, QFileDialog, QMessageBox, QDialog, QTreeView, QAbstractItemView)
from PySide6.QtNetwork import (QLocalSocket, QLocalServer) from PySide6.QtNetwork import (QLocalSocket, QLocalServer)
import os import os
@@ -347,6 +347,8 @@ class WorkerThread(QThread):
options.forcepng = True options.forcepng = True
elif GUI.mozJpegBox.checkState() == Qt.CheckState.Checked: elif GUI.mozJpegBox.checkState() == Qt.CheckState.Checked:
options.mozjpeg = True options.mozjpeg = True
if GUI.jpegQualityBox.isChecked():
options.jpegquality = GUI.jpegQualitySpinBox.value()
if GUI.currentMode > 2: if GUI.currentMode > 2:
options.customwidth = str(GUI.widthBox.value()) options.customwidth = str(GUI.widthBox.value())
options.customheight = str(GUI.heightBox.value()) options.customheight = str(GUI.heightBox.value())
@@ -610,12 +612,30 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
'Comic (*.pdf);;All (*.*)') 'Comic (*.pdf);;All (*.*)')
for fname in fnames[0]: for fname in fnames[0]:
if fname != '': if fname != '':
if sys.platform.startswith('win'):
fname = fname.replace('/', '\\')
self.lastPath = os.path.abspath(os.path.join(fname, os.pardir)) self.lastPath = os.path.abspath(os.path.join(fname, os.pardir))
GUI.jobList.addItem(fname) GUI.jobList.addItem(fname)
GUI.jobList.scrollToBottom() GUI.jobList.scrollToBottom()
def selectDir(self):
if self.needClean:
self.needClean = False
GUI.jobList.clear()
dialog = QFileDialog(MW, 'Select input folder(s)', self.lastPath)
dialog.setFileMode(QFileDialog.FileMode.Directory)
dialog.setOption(QFileDialog.Option.ShowDirsOnly, True)
dialog.setOption(QFileDialog.Option.DontUseNativeDialog, True)
dialog.findChild(QTreeView).setSelectionMode(QAbstractItemView.ExtendedSelection)
if dialog.exec():
dnames = dialog.selectedFiles()
for dname in dnames:
if dname != '':
self.lastPath = os.path.abspath(os.path.join(dname, os.pardir))
GUI.jobList.addItem(dname)
GUI.jobList.scrollToBottom()
def selectFileMetaEditor(self, sname): def selectFileMetaEditor(self, sname):
if not sname: if not sname:
if QApplication.keyboardModifiers() == Qt.ShiftModifier: if QApplication.keyboardModifiers() == Qt.ShiftModifier:
@@ -724,6 +744,12 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
GUI.croppingWidget.setVisible(False) GUI.croppingWidget.setVisible(False)
self.changeCroppingPower(100) # 1.0 self.changeCroppingPower(100) # 1.0
def togglejpegqualityBox(self, value):
if value:
GUI.jpegQualityWidget.setVisible(True)
else:
GUI.jpegQualityWidget.setVisible(False)
def togglewebtoonBox(self, value): def togglewebtoonBox(self, value):
if value: if value:
self.addMessage('You can choose a taller device profile to get taller cuts in webtoon mode.', 'info') self.addMessage('You can choose a taller device profile to get taller cuts in webtoon mode.', 'info')
@@ -754,7 +780,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
GUI.rotateBox.setEnabled(True) GUI.rotateBox.setEnabled(True)
GUI.borderBox.setEnabled(True) GUI.borderBox.setEnabled(True)
profile = GUI.profiles[str(GUI.deviceBox.currentText())] profile = GUI.profiles[str(GUI.deviceBox.currentText())]
if profile['Label'] != 'KS': if not profile['Label'].startswith('KS'):
GUI.upscaleBox.setEnabled(True) GUI.upscaleBox.setEnabled(True)
GUI.croppingBox.setEnabled(True) GUI.croppingBox.setEnabled(True)
GUI.interPanelCropBox.setEnabled(True) GUI.interPanelCropBox.setEnabled(True)
@@ -779,7 +805,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
def toggleImageFormatBox(self, value): def toggleImageFormatBox(self, value):
profile = GUI.profiles[str(GUI.deviceBox.currentText())] profile = GUI.profiles[str(GUI.deviceBox.currentText())]
if value == 1: if value == 1:
if profile['Label'] == 'KS': if profile['Label'].startswith('KS'):
current_format = GUI.formats[str(GUI.formatBox.currentText())]['format'] current_format = GUI.formats[str(GUI.formatBox.currentText())]['format']
for bad_format in ('MOBI', 'EPUB'): for bad_format in ('MOBI', 'EPUB'):
if bad_format in current_format: if bad_format in current_format:
@@ -841,7 +867,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
if not GUI.webtoonBox.isChecked(): if not GUI.webtoonBox.isChecked():
GUI.qualityBox.setEnabled(profile['PVOptions']) GUI.qualityBox.setEnabled(profile['PVOptions'])
GUI.upscaleBox.setChecked(profile['DefaultUpscale']) GUI.upscaleBox.setChecked(profile['DefaultUpscale'])
if profile['Label'] == 'KS': if profile['Label'].startswith('KS'):
GUI.upscaleBox.setDisabled(True) GUI.upscaleBox.setDisabled(True)
else: else:
if not GUI.webtoonBox.isChecked(): if not GUI.webtoonBox.isChecked():
@@ -1008,6 +1034,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
'disableProcessingBox': GUI.disableProcessingBox.checkState(), 'disableProcessingBox': GUI.disableProcessingBox.checkState(),
'metadataTitleBox': GUI.metadataTitleBox.checkState(), 'metadataTitleBox': GUI.metadataTitleBox.checkState(),
'mozJpegBox': GUI.mozJpegBox.checkState(), 'mozJpegBox': GUI.mozJpegBox.checkState(),
'jpegQualityBox': GUI.jpegQualityBox.checkState(),
'widthBox': GUI.widthBox.value(), 'widthBox': GUI.widthBox.value(),
'heightBox': GUI.heightBox.value(), 'heightBox': GUI.heightBox.value(),
'deleteBox': GUI.deleteBox.checkState(), 'deleteBox': GUI.deleteBox.checkState(),
@@ -1162,9 +1189,21 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
'DefaultUpscale': True, 'ForceColor': False, 'Label': 'KPW34'}, 'DefaultUpscale': True, 'ForceColor': False, 'Label': 'KPW34'},
"Kindle Voyage": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, "Kindle Voyage": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': True, 'ForceColor': False, 'Label': 'KV'}, 'DefaultUpscale': True, 'ForceColor': False, 'Label': 'KV'},
"Kindle Scribe": { "Kindle 1860x1920": {
'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': False, 'ForceColor': False, 'Label': 'KS1860',
},
"Kindle 1920x1920": {
'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': False, 'ForceColor': False, 'Label': 'KS1920',
},
"Kindle Scribe 1/2": {
'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': False, 'ForceColor': False, 'Label': 'KS', 'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': False, 'ForceColor': False, 'Label': 'KS',
}, },
"Kindle Scribe 3": {
'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': False, 'ForceColor': False, 'Label': 'KS3',
},
"Kindle Scribe Colorsoft": {
'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': False, 'ForceColor': True, 'Label': 'KSCS',
},
"Kindle 11": { "Kindle 11": {
'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': True, 'ForceColor': False, 'Label': 'K11', 'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': True, 'ForceColor': False, 'Label': 'K11',
}, },
@@ -1239,9 +1278,11 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
'Label': 'OTHER'}, 'Label': 'OTHER'},
} }
profilesGUI = [ profilesGUI = [
"Kindle Scribe Colorsoft",
"Kindle Scribe 3",
"Kindle Colorsoft", "Kindle Colorsoft",
"Kindle Paperwhite 12", "Kindle Paperwhite 12",
"Kindle Scribe", "Kindle Scribe 1/2",
"Kindle Paperwhite 11", "Kindle Paperwhite 11",
"Kindle 11", "Kindle 11",
"Kindle Oasis 9/10", "Kindle Oasis 9/10",
@@ -1261,6 +1302,8 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
"Separator", "Separator",
"Other", "Other",
"Separator", "Separator",
"Kindle 1920x1920",
"Kindle 1860x1920",
"Kindle 8/10", "Kindle 8/10",
"Kindle Oasis 8", "Kindle Oasis 8",
"Kindle Paperwhite 7/10", "Kindle Paperwhite 7/10",
@@ -1319,6 +1362,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
GUI.defaultOutputFolderButton.clicked.connect(self.selectDefaultOutputFolder) GUI.defaultOutputFolderButton.clicked.connect(self.selectDefaultOutputFolder)
GUI.clearButton.clicked.connect(self.clearJobs) GUI.clearButton.clicked.connect(self.clearJobs)
GUI.fileButton.clicked.connect(self.selectFile) GUI.fileButton.clicked.connect(self.selectFile)
GUI.directoryButton.clicked.connect(self.selectDir)
GUI.editorButton.clicked.connect(self.selectFileMetaEditor) GUI.editorButton.clicked.connect(self.selectFileMetaEditor)
GUI.wikiButton.clicked.connect(self.openWiki) GUI.wikiButton.clicked.connect(self.openWiki)
GUI.kofiButton.clicked.connect(self.openKofi) GUI.kofiButton.clicked.connect(self.openKofi)
@@ -1327,6 +1371,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
GUI.gammaBox.stateChanged.connect(self.togglegammaBox) GUI.gammaBox.stateChanged.connect(self.togglegammaBox)
GUI.croppingBox.stateChanged.connect(self.togglecroppingBox) GUI.croppingBox.stateChanged.connect(self.togglecroppingBox)
GUI.croppingPowerSlider.valueChanged.connect(self.changeCroppingPower) GUI.croppingPowerSlider.valueChanged.connect(self.changeCroppingPower)
GUI.jpegQualityBox.stateChanged.connect(self.togglejpegqualityBox)
GUI.webtoonBox.stateChanged.connect(self.togglewebtoonBox) GUI.webtoonBox.stateChanged.connect(self.togglewebtoonBox)
GUI.qualityBox.stateChanged.connect(self.togglequalityBox) GUI.qualityBox.stateChanged.connect(self.togglequalityBox)
GUI.mozJpegBox.stateChanged.connect(self.toggleImageFormatBox) GUI.mozJpegBox.stateChanged.connect(self.toggleImageFormatBox)

View File

@@ -26,7 +26,7 @@ class Ui_mainWindow(object):
def setupUi(self, mainWindow): def setupUi(self, mainWindow):
if not mainWindow.objectName(): if not mainWindow.objectName():
mainWindow.setObjectName(u"mainWindow") mainWindow.setObjectName(u"mainWindow")
mainWindow.resize(566, 573) mainWindow.resize(566, 658)
icon = QIcon() icon = QIcon()
icon.addFile(u":/Icon/icons/comic2ebook.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off) icon.addFile(u":/Icon/icons/comic2ebook.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
mainWindow.setWindowIcon(icon) mainWindow.setWindowIcon(icon)
@@ -35,6 +35,253 @@ class Ui_mainWindow(object):
self.gridLayout = QGridLayout(self.centralWidget) self.gridLayout = QGridLayout(self.centralWidget)
self.gridLayout.setObjectName(u"gridLayout") self.gridLayout.setObjectName(u"gridLayout")
self.gridLayout.setContentsMargins(-1, -1, -1, 5) self.gridLayout.setContentsMargins(-1, -1, -1, 5)
self.progressBar = QProgressBar(self.centralWidget)
self.progressBar.setObjectName(u"progressBar")
self.progressBar.setMinimumSize(QSize(0, 30))
font = QFont()
font.setBold(True)
self.progressBar.setFont(font)
self.progressBar.setVisible(False)
self.progressBar.setAlignment(Qt.AlignmentFlag.AlignJustify|Qt.AlignmentFlag.AlignVCenter)
self.gridLayout.addWidget(self.progressBar, 1, 0, 1, 2)
self.toolWidget = QWidget(self.centralWidget)
self.toolWidget.setObjectName(u"toolWidget")
self.horizontalLayout = QHBoxLayout(self.toolWidget)
self.horizontalLayout.setObjectName(u"horizontalLayout")
self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
self.editorButton = QPushButton(self.toolWidget)
self.editorButton.setObjectName(u"editorButton")
self.editorButton.setMinimumSize(QSize(0, 30))
icon1 = QIcon()
icon1.addFile(u":/Other/icons/editor.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.editorButton.setIcon(icon1)
self.horizontalLayout.addWidget(self.editorButton)
self.kofiButton = QPushButton(self.toolWidget)
self.kofiButton.setObjectName(u"kofiButton")
self.kofiButton.setMinimumSize(QSize(0, 30))
icon2 = QIcon()
icon2.addFile(u":/Brand/icons/kofi_symbol.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.kofiButton.setIcon(icon2)
self.kofiButton.setIconSize(QSize(19, 16))
self.horizontalLayout.addWidget(self.kofiButton)
self.wikiButton = QPushButton(self.toolWidget)
self.wikiButton.setObjectName(u"wikiButton")
self.wikiButton.setMinimumSize(QSize(0, 30))
icon3 = QIcon()
icon3.addFile(u":/Other/icons/wiki.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.wikiButton.setIcon(icon3)
self.horizontalLayout.addWidget(self.wikiButton)
self.gridLayout.addWidget(self.toolWidget, 0, 0, 1, 2)
self.croppingWidget = QWidget(self.centralWidget)
self.croppingWidget.setObjectName(u"croppingWidget")
self.croppingWidget.setVisible(False)
self.gridLayout_5 = QGridLayout(self.croppingWidget)
self.gridLayout_5.setObjectName(u"gridLayout_5")
self.gridLayout_5.setContentsMargins(0, 0, 0, 0)
self.croppingPowerSlider = QSlider(self.croppingWidget)
self.croppingPowerSlider.setObjectName(u"croppingPowerSlider")
self.croppingPowerSlider.setMaximum(300)
self.croppingPowerSlider.setSingleStep(1)
self.croppingPowerSlider.setOrientation(Qt.Orientation.Horizontal)
self.gridLayout_5.addWidget(self.croppingPowerSlider, 0, 1, 1, 1)
self.preserveMarginBox = QSpinBox(self.croppingWidget)
self.preserveMarginBox.setObjectName(u"preserveMarginBox")
sizePolicy = QSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.preserveMarginBox.sizePolicy().hasHeightForWidth())
self.preserveMarginBox.setSizePolicy(sizePolicy)
self.preserveMarginBox.setMaximum(99)
self.preserveMarginBox.setSingleStep(5)
self.preserveMarginBox.setValue(0)
self.gridLayout_5.addWidget(self.preserveMarginBox, 1, 1, 1, 1)
self.preserveMarginLabel = QLabel(self.croppingWidget)
self.preserveMarginLabel.setObjectName(u"preserveMarginLabel")
self.gridLayout_5.addWidget(self.preserveMarginLabel, 1, 0, 1, 1)
self.croppingPowerLabel = QLabel(self.croppingWidget)
self.croppingPowerLabel.setObjectName(u"croppingPowerLabel")
self.gridLayout_5.addWidget(self.croppingPowerLabel, 0, 0, 1, 1)
self.gridLayout.addWidget(self.croppingWidget, 9, 0, 1, 2)
self.customWidget = QWidget(self.centralWidget)
self.customWidget.setObjectName(u"customWidget")
self.customWidget.setVisible(False)
self.gridLayout_3 = QGridLayout(self.customWidget)
self.gridLayout_3.setObjectName(u"gridLayout_3")
self.gridLayout_3.setContentsMargins(0, 0, 0, 0)
self.hLabel = QLabel(self.customWidget)
self.hLabel.setObjectName(u"hLabel")
sizePolicy1 = QSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Preferred)
sizePolicy1.setHorizontalStretch(0)
sizePolicy1.setVerticalStretch(0)
sizePolicy1.setHeightForWidth(self.hLabel.sizePolicy().hasHeightForWidth())
self.hLabel.setSizePolicy(sizePolicy1)
self.gridLayout_3.addWidget(self.hLabel, 0, 2, 1, 1)
self.widthBox = QSpinBox(self.customWidget)
self.widthBox.setObjectName(u"widthBox")
self.widthBox.setMaximum(6000)
self.gridLayout_3.addWidget(self.widthBox, 0, 1, 1, 1)
self.wLabel = QLabel(self.customWidget)
self.wLabel.setObjectName(u"wLabel")
sizePolicy1.setHeightForWidth(self.wLabel.sizePolicy().hasHeightForWidth())
self.wLabel.setSizePolicy(sizePolicy1)
self.gridLayout_3.addWidget(self.wLabel, 0, 0, 1, 1)
self.heightBox = QSpinBox(self.customWidget)
self.heightBox.setObjectName(u"heightBox")
self.heightBox.setMaximum(8000)
self.gridLayout_3.addWidget(self.heightBox, 0, 3, 1, 1)
self.gridLayout.addWidget(self.customWidget, 8, 0, 1, 2)
self.buttonWidget = QWidget(self.centralWidget)
self.buttonWidget.setObjectName(u"buttonWidget")
sizePolicy2 = QSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Fixed)
sizePolicy2.setHorizontalStretch(0)
sizePolicy2.setVerticalStretch(0)
sizePolicy2.setHeightForWidth(self.buttonWidget.sizePolicy().hasHeightForWidth())
self.buttonWidget.setSizePolicy(sizePolicy2)
self.gridLayout_4 = QGridLayout(self.buttonWidget)
self.gridLayout_4.setObjectName(u"gridLayout_4")
self.gridLayout_4.setContentsMargins(0, 0, 0, 0)
self.convertButton = QPushButton(self.buttonWidget)
self.convertButton.setObjectName(u"convertButton")
self.convertButton.setMinimumSize(QSize(0, 30))
self.convertButton.setFont(font)
icon4 = QIcon()
icon4.addFile(u":/Other/icons/convert.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.convertButton.setIcon(icon4)
self.gridLayout_4.addWidget(self.convertButton, 1, 3, 1, 1)
self.clearButton = QPushButton(self.buttonWidget)
self.clearButton.setObjectName(u"clearButton")
self.clearButton.setMinimumSize(QSize(0, 30))
icon5 = QIcon()
icon5.addFile(u":/Other/icons/clear.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.clearButton.setIcon(icon5)
self.gridLayout_4.addWidget(self.clearButton, 0, 3, 1, 1)
self.deviceBox = QComboBox(self.buttonWidget)
self.deviceBox.setObjectName(u"deviceBox")
self.deviceBox.setMinimumSize(QSize(0, 28))
self.gridLayout_4.addWidget(self.deviceBox, 1, 1, 1, 1)
self.fileButton = QPushButton(self.buttonWidget)
self.fileButton.setObjectName(u"fileButton")
self.fileButton.setMinimumSize(QSize(0, 30))
icon6 = QIcon()
icon6.addFile(u":/Other/icons/document_new.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.fileButton.setIcon(icon6)
self.gridLayout_4.addWidget(self.fileButton, 0, 1, 1, 1)
self.directoryButton = QPushButton(self.buttonWidget)
self.directoryButton.setObjectName(u"directoryButton")
sizePolicy3 = QSizePolicy(QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Minimum)
sizePolicy3.setHorizontalStretch(0)
sizePolicy3.setVerticalStretch(0)
sizePolicy3.setHeightForWidth(self.directoryButton.sizePolicy().hasHeightForWidth())
self.directoryButton.setSizePolicy(sizePolicy3)
icon7 = QIcon()
icon7.addFile(u":/Other/icons/folder_new.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.directoryButton.setIcon(icon7)
self.gridLayout_4.addWidget(self.directoryButton, 0, 4, 1, 1)
self.formatBox = QComboBox(self.buttonWidget)
self.formatBox.setObjectName(u"formatBox")
self.formatBox.setMinimumSize(QSize(0, 28))
self.gridLayout_4.addWidget(self.formatBox, 1, 4, 1, 1)
self.clearButton.raise_()
self.deviceBox.raise_()
self.convertButton.raise_()
self.fileButton.raise_()
self.directoryButton.raise_()
self.formatBox.raise_()
self.gridLayout.addWidget(self.buttonWidget, 3, 0, 1, 2)
self.jobList = QListWidget(self.centralWidget)
self.jobList.setObjectName(u"jobList")
self.jobList.setMinimumSize(QSize(0, 150))
self.jobList.setStyleSheet(u"")
self.jobList.setSelectionMode(QAbstractItemView.SelectionMode.NoSelection)
self.jobList.setVerticalScrollMode(QAbstractItemView.ScrollMode.ScrollPerPixel)
self.jobList.setHorizontalScrollMode(QAbstractItemView.ScrollMode.ScrollPerPixel)
self.gridLayout.addWidget(self.jobList, 2, 0, 1, 2)
self.chunkSizeWidget = QWidget(self.centralWidget)
self.chunkSizeWidget.setObjectName(u"chunkSizeWidget")
sizePolicy4 = QSizePolicy(QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Fixed)
sizePolicy4.setHorizontalStretch(0)
sizePolicy4.setVerticalStretch(0)
sizePolicy4.setHeightForWidth(self.chunkSizeWidget.sizePolicy().hasHeightForWidth())
self.chunkSizeWidget.setSizePolicy(sizePolicy4)
self.chunkSizeWidget.setVisible(False)
self.horizontalLayout_4 = QHBoxLayout(self.chunkSizeWidget)
self.horizontalLayout_4.setSpacing(0)
self.horizontalLayout_4.setObjectName(u"horizontalLayout_4")
self.horizontalLayout_4.setContentsMargins(0, 0, 0, 0)
self.chunkSizeLabel = QLabel(self.chunkSizeWidget)
self.chunkSizeLabel.setObjectName(u"chunkSizeLabel")
sizePolicy5 = QSizePolicy(QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Preferred)
sizePolicy5.setHorizontalStretch(0)
sizePolicy5.setVerticalStretch(0)
sizePolicy5.setHeightForWidth(self.chunkSizeLabel.sizePolicy().hasHeightForWidth())
self.chunkSizeLabel.setSizePolicy(sizePolicy5)
self.horizontalLayout_4.addWidget(self.chunkSizeLabel)
self.chunkSizeBox = QSpinBox(self.chunkSizeWidget)
self.chunkSizeBox.setObjectName(u"chunkSizeBox")
self.chunkSizeBox.setMinimum(100)
self.chunkSizeBox.setMaximum(600)
self.chunkSizeBox.setValue(400)
self.horizontalLayout_4.addWidget(self.chunkSizeBox)
self.chunkSizeWarnLabel = QLabel(self.chunkSizeWidget)
self.chunkSizeWarnLabel.setObjectName(u"chunkSizeWarnLabel")
sizePolicy5.setHeightForWidth(self.chunkSizeWarnLabel.sizePolicy().hasHeightForWidth())
self.chunkSizeWarnLabel.setSizePolicy(sizePolicy5)
self.horizontalLayout_4.addWidget(self.chunkSizeWarnLabel)
self.gridLayout.addWidget(self.chunkSizeWidget, 6, 0, 1, 1)
self.optionWidget = QWidget(self.centralWidget) self.optionWidget = QWidget(self.centralWidget)
self.optionWidget.setObjectName(u"optionWidget") self.optionWidget.setObjectName(u"optionWidget")
self.gridLayout_2 = QGridLayout(self.optionWidget) self.gridLayout_2 = QGridLayout(self.optionWidget)
@@ -42,11 +289,8 @@ class Ui_mainWindow(object):
self.gridLayout_2.setContentsMargins(0, 0, 0, 0) self.gridLayout_2.setContentsMargins(0, 0, 0, 0)
self.titleEdit = QLineEdit(self.optionWidget) self.titleEdit = QLineEdit(self.optionWidget)
self.titleEdit.setObjectName(u"titleEdit") self.titleEdit.setObjectName(u"titleEdit")
sizePolicy = QSizePolicy(QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Fixed) sizePolicy4.setHeightForWidth(self.titleEdit.sizePolicy().hasHeightForWidth())
sizePolicy.setHorizontalStretch(0) self.titleEdit.setSizePolicy(sizePolicy4)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.titleEdit.sizePolicy().hasHeightForWidth())
self.titleEdit.setSizePolicy(sizePolicy)
self.titleEdit.setFocusPolicy(Qt.FocusPolicy.ClickFocus) self.titleEdit.setFocusPolicy(Qt.FocusPolicy.ClickFocus)
self.titleEdit.setClearButtonEnabled(False) self.titleEdit.setClearButtonEnabled(False)
@@ -148,8 +392,8 @@ class Ui_mainWindow(object):
self.authorEdit = QLineEdit(self.optionWidget) self.authorEdit = QLineEdit(self.optionWidget)
self.authorEdit.setObjectName(u"authorEdit") self.authorEdit.setObjectName(u"authorEdit")
sizePolicy.setHeightForWidth(self.authorEdit.sizePolicy().hasHeightForWidth()) sizePolicy4.setHeightForWidth(self.authorEdit.sizePolicy().hasHeightForWidth())
self.authorEdit.setSizePolicy(sizePolicy) self.authorEdit.setSizePolicy(sizePolicy4)
self.authorEdit.setFocusPolicy(Qt.FocusPolicy.ClickFocus) self.authorEdit.setFocusPolicy(Qt.FocusPolicy.ClickFocus)
self.authorEdit.setClearButtonEnabled(False) self.authorEdit.setClearButtonEnabled(False)
@@ -190,57 +434,37 @@ class Ui_mainWindow(object):
self.gridLayout_2.addWidget(self.autocontrastBox, 9, 2, 1, 1) self.gridLayout_2.addWidget(self.autocontrastBox, 9, 2, 1, 1)
self.outputFolderWidget = QWidget(self.optionWidget)
self.outputFolderWidget.setObjectName(u"outputFolderWidget")
self.horizontalLayout_3 = QHBoxLayout(self.outputFolderWidget)
self.horizontalLayout_3.setObjectName(u"horizontalLayout_3")
self.horizontalLayout_3.setContentsMargins(0, 0, 0, 0)
self.defaultOutputFolderBox = QCheckBox(self.outputFolderWidget)
self.defaultOutputFolderBox.setObjectName(u"defaultOutputFolderBox")
sizePolicy.setHeightForWidth(self.defaultOutputFolderBox.sizePolicy().hasHeightForWidth())
self.defaultOutputFolderBox.setSizePolicy(sizePolicy)
self.defaultOutputFolderBox.setTristate(True)
self.horizontalLayout_3.addWidget(self.defaultOutputFolderBox)
self.defaultOutputFolderButton = QPushButton(self.outputFolderWidget)
self.defaultOutputFolderButton.setObjectName(u"defaultOutputFolderButton")
self.defaultOutputFolderButton.setMinimumSize(QSize(0, 30))
self.defaultOutputFolderButton.setIcon(icon7)
self.horizontalLayout_3.addWidget(self.defaultOutputFolderButton)
self.gridLayout_2.addWidget(self.outputFolderWidget, 0, 2, 1, 1)
self.jpegQualityBox = QCheckBox(self.optionWidget)
self.jpegQualityBox.setObjectName(u"jpegQualityBox")
self.gridLayout_2.addWidget(self.jpegQualityBox, 8, 0, 1, 1)
self.gridLayout.addWidget(self.optionWidget, 5, 0, 1, 2) self.gridLayout.addWidget(self.optionWidget, 5, 0, 1, 2)
self.jobList = QListWidget(self.centralWidget)
self.jobList.setObjectName(u"jobList")
self.jobList.setMinimumSize(QSize(0, 150))
self.jobList.setStyleSheet(u"")
self.jobList.setSelectionMode(QAbstractItemView.SelectionMode.NoSelection)
self.jobList.setVerticalScrollMode(QAbstractItemView.ScrollMode.ScrollPerPixel)
self.jobList.setHorizontalScrollMode(QAbstractItemView.ScrollMode.ScrollPerPixel)
self.gridLayout.addWidget(self.jobList, 2, 0, 1, 2)
self.customWidget = QWidget(self.centralWidget)
self.customWidget.setObjectName(u"customWidget")
self.customWidget.setVisible(False)
self.gridLayout_3 = QGridLayout(self.customWidget)
self.gridLayout_3.setObjectName(u"gridLayout_3")
self.gridLayout_3.setContentsMargins(0, 0, 0, 0)
self.hLabel = QLabel(self.customWidget)
self.hLabel.setObjectName(u"hLabel")
sizePolicy1 = QSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Preferred)
sizePolicy1.setHorizontalStretch(0)
sizePolicy1.setVerticalStretch(0)
sizePolicy1.setHeightForWidth(self.hLabel.sizePolicy().hasHeightForWidth())
self.hLabel.setSizePolicy(sizePolicy1)
self.gridLayout_3.addWidget(self.hLabel, 0, 2, 1, 1)
self.widthBox = QSpinBox(self.customWidget)
self.widthBox.setObjectName(u"widthBox")
self.widthBox.setMaximum(6000)
self.gridLayout_3.addWidget(self.widthBox, 0, 1, 1, 1)
self.wLabel = QLabel(self.customWidget)
self.wLabel.setObjectName(u"wLabel")
sizePolicy1.setHeightForWidth(self.wLabel.sizePolicy().hasHeightForWidth())
self.wLabel.setSizePolicy(sizePolicy1)
self.gridLayout_3.addWidget(self.wLabel, 0, 0, 1, 1)
self.heightBox = QSpinBox(self.customWidget)
self.heightBox.setObjectName(u"heightBox")
self.heightBox.setMaximum(8000)
self.gridLayout_3.addWidget(self.heightBox, 0, 3, 1, 1)
self.gridLayout.addWidget(self.customWidget, 8, 0, 1, 2)
self.gammaWidget = QWidget(self.centralWidget) self.gammaWidget = QWidget(self.centralWidget)
self.gammaWidget.setObjectName(u"gammaWidget") self.gammaWidget.setObjectName(u"gammaWidget")
self.gammaWidget.setVisible(False) self.gammaWidget.setVisible(False)
@@ -263,206 +487,29 @@ class Ui_mainWindow(object):
self.gridLayout.addWidget(self.gammaWidget, 7, 0, 1, 2) self.gridLayout.addWidget(self.gammaWidget, 7, 0, 1, 2)
self.toolWidget = QWidget(self.centralWidget) self.jpegQualityWidget = QWidget(self.centralWidget)
self.toolWidget.setObjectName(u"toolWidget") self.jpegQualityWidget.setObjectName(u"jpegQualityWidget")
self.horizontalLayout = QHBoxLayout(self.toolWidget) sizePolicy1.setHeightForWidth(self.jpegQualityWidget.sizePolicy().hasHeightForWidth())
self.horizontalLayout.setObjectName(u"horizontalLayout") self.jpegQualityWidget.setSizePolicy(sizePolicy1)
self.horizontalLayout.setContentsMargins(0, 0, 0, 0) self.jpegQualityWidget.setVisible(False)
self.editorButton = QPushButton(self.toolWidget) self.horizontalLayout_12 = QHBoxLayout(self.jpegQualityWidget)
self.editorButton.setObjectName(u"editorButton") self.horizontalLayout_12.setObjectName(u"horizontalLayout_12")
self.editorButton.setMinimumSize(QSize(0, 30)) self.horizontalLayout_12.setContentsMargins(0, 0, 0, 0)
icon1 = QIcon() self.jpegQualityLabel = QLabel(self.jpegQualityWidget)
icon1.addFile(u":/Other/icons/editor.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off) self.jpegQualityLabel.setObjectName(u"jpegQualityLabel")
self.editorButton.setIcon(icon1)
self.horizontalLayout.addWidget(self.editorButton) self.horizontalLayout_12.addWidget(self.jpegQualityLabel)
self.kofiButton = QPushButton(self.toolWidget) self.jpegQualitySpinBox = QSpinBox(self.jpegQualityWidget)
self.kofiButton.setObjectName(u"kofiButton") self.jpegQualitySpinBox.setObjectName(u"jpegQualitySpinBox")
self.kofiButton.setMinimumSize(QSize(0, 30)) self.jpegQualitySpinBox.setMaximum(95)
icon2 = QIcon() self.jpegQualitySpinBox.setSingleStep(5)
icon2.addFile(u":/Brand/icons/kofi_symbol.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off) self.jpegQualitySpinBox.setValue(85)
self.kofiButton.setIcon(icon2)
self.kofiButton.setIconSize(QSize(19, 16))
self.horizontalLayout.addWidget(self.kofiButton) self.horizontalLayout_12.addWidget(self.jpegQualitySpinBox)
self.wikiButton = QPushButton(self.toolWidget)
self.wikiButton.setObjectName(u"wikiButton")
self.wikiButton.setMinimumSize(QSize(0, 30))
icon3 = QIcon()
icon3.addFile(u":/Other/icons/wiki.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.wikiButton.setIcon(icon3)
self.horizontalLayout.addWidget(self.wikiButton)
self.gridLayout.addWidget(self.toolWidget, 0, 0, 1, 2) self.gridLayout.addWidget(self.jpegQualityWidget, 10, 0, 1, 1)
self.progressBar = QProgressBar(self.centralWidget)
self.progressBar.setObjectName(u"progressBar")
self.progressBar.setMinimumSize(QSize(0, 30))
font = QFont()
font.setBold(True)
self.progressBar.setFont(font)
self.progressBar.setVisible(False)
self.progressBar.setAlignment(Qt.AlignmentFlag.AlignJustify|Qt.AlignmentFlag.AlignVCenter)
self.gridLayout.addWidget(self.progressBar, 1, 0, 1, 2)
self.croppingWidget = QWidget(self.centralWidget)
self.croppingWidget.setObjectName(u"croppingWidget")
self.croppingWidget.setVisible(False)
self.gridLayout_5 = QGridLayout(self.croppingWidget)
self.gridLayout_5.setObjectName(u"gridLayout_5")
self.gridLayout_5.setContentsMargins(0, 0, 0, 0)
self.preserveMarginLabel = QLabel(self.croppingWidget)
self.preserveMarginLabel.setObjectName(u"preserveMarginLabel")
self.gridLayout_5.addWidget(self.preserveMarginLabel, 1, 0, 1, 1)
self.croppingPowerLabel = QLabel(self.croppingWidget)
self.croppingPowerLabel.setObjectName(u"croppingPowerLabel")
self.gridLayout_5.addWidget(self.croppingPowerLabel, 0, 0, 1, 1)
self.croppingPowerSlider = QSlider(self.croppingWidget)
self.croppingPowerSlider.setObjectName(u"croppingPowerSlider")
self.croppingPowerSlider.setMaximum(300)
self.croppingPowerSlider.setSingleStep(1)
self.croppingPowerSlider.setOrientation(Qt.Orientation.Horizontal)
self.gridLayout_5.addWidget(self.croppingPowerSlider, 0, 1, 1, 1)
self.preserveMarginBox = QSpinBox(self.croppingWidget)
self.preserveMarginBox.setObjectName(u"preserveMarginBox")
sizePolicy2 = QSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed)
sizePolicy2.setHorizontalStretch(0)
sizePolicy2.setVerticalStretch(0)
sizePolicy2.setHeightForWidth(self.preserveMarginBox.sizePolicy().hasHeightForWidth())
self.preserveMarginBox.setSizePolicy(sizePolicy2)
self.preserveMarginBox.setMaximum(99)
self.preserveMarginBox.setSingleStep(5)
self.preserveMarginBox.setValue(0)
self.gridLayout_5.addWidget(self.preserveMarginBox, 1, 1, 1, 1)
self.gridLayout.addWidget(self.croppingWidget, 9, 0, 1, 2)
self.buttonWidget = QWidget(self.centralWidget)
self.buttonWidget.setObjectName(u"buttonWidget")
sizePolicy3 = QSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Fixed)
sizePolicy3.setHorizontalStretch(0)
sizePolicy3.setVerticalStretch(0)
sizePolicy3.setHeightForWidth(self.buttonWidget.sizePolicy().hasHeightForWidth())
self.buttonWidget.setSizePolicy(sizePolicy3)
self.gridLayout_4 = QGridLayout(self.buttonWidget)
self.gridLayout_4.setObjectName(u"gridLayout_4")
self.gridLayout_4.setContentsMargins(0, 0, 0, 0)
self.convertButton = QPushButton(self.buttonWidget)
self.convertButton.setObjectName(u"convertButton")
self.convertButton.setMinimumSize(QSize(0, 30))
self.convertButton.setFont(font)
icon4 = QIcon()
icon4.addFile(u":/Other/icons/convert.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.convertButton.setIcon(icon4)
self.gridLayout_4.addWidget(self.convertButton, 1, 3, 1, 1)
self.clearButton = QPushButton(self.buttonWidget)
self.clearButton.setObjectName(u"clearButton")
self.clearButton.setMinimumSize(QSize(0, 30))
icon5 = QIcon()
icon5.addFile(u":/Other/icons/clear.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.clearButton.setIcon(icon5)
self.gridLayout_4.addWidget(self.clearButton, 0, 3, 1, 1)
self.deviceBox = QComboBox(self.buttonWidget)
self.deviceBox.setObjectName(u"deviceBox")
self.deviceBox.setMinimumSize(QSize(0, 28))
self.gridLayout_4.addWidget(self.deviceBox, 1, 1, 1, 1)
self.fileButton = QPushButton(self.buttonWidget)
self.fileButton.setObjectName(u"fileButton")
self.fileButton.setMinimumSize(QSize(0, 30))
icon6 = QIcon()
icon6.addFile(u":/Other/icons/document_new.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.fileButton.setIcon(icon6)
self.gridLayout_4.addWidget(self.fileButton, 0, 1, 1, 1)
self.defaultOutputFolderButton = QPushButton(self.buttonWidget)
self.defaultOutputFolderButton.setObjectName(u"defaultOutputFolderButton")
self.defaultOutputFolderButton.setMinimumSize(QSize(0, 30))
icon7 = QIcon()
icon7.addFile(u":/Other/icons/folder_new.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.defaultOutputFolderButton.setIcon(icon7)
self.gridLayout_4.addWidget(self.defaultOutputFolderButton, 0, 5, 1, 1)
self.defaultOutputFolderBox = QCheckBox(self.buttonWidget)
self.defaultOutputFolderBox.setObjectName(u"defaultOutputFolderBox")
sizePolicy2.setHeightForWidth(self.defaultOutputFolderBox.sizePolicy().hasHeightForWidth())
self.defaultOutputFolderBox.setSizePolicy(sizePolicy2)
self.defaultOutputFolderBox.setTristate(True)
self.gridLayout_4.addWidget(self.defaultOutputFolderBox, 0, 4, 1, 1)
self.formatBox = QComboBox(self.buttonWidget)
self.formatBox.setObjectName(u"formatBox")
self.formatBox.setMinimumSize(QSize(0, 28))
self.gridLayout_4.addWidget(self.formatBox, 1, 4, 1, 2)
self.clearButton.raise_()
self.deviceBox.raise_()
self.convertButton.raise_()
self.formatBox.raise_()
self.defaultOutputFolderButton.raise_()
self.fileButton.raise_()
self.defaultOutputFolderBox.raise_()
self.gridLayout.addWidget(self.buttonWidget, 3, 0, 1, 2)
self.chunkSizeWidget = QWidget(self.centralWidget)
self.chunkSizeWidget.setObjectName(u"chunkSizeWidget")
sizePolicy.setHeightForWidth(self.chunkSizeWidget.sizePolicy().hasHeightForWidth())
self.chunkSizeWidget.setSizePolicy(sizePolicy)
self.chunkSizeWidget.setVisible(False)
self.horizontalLayout_4 = QHBoxLayout(self.chunkSizeWidget)
self.horizontalLayout_4.setSpacing(0)
self.horizontalLayout_4.setObjectName(u"horizontalLayout_4")
self.horizontalLayout_4.setContentsMargins(0, 0, 0, 0)
self.chunkSizeLabel = QLabel(self.chunkSizeWidget)
self.chunkSizeLabel.setObjectName(u"chunkSizeLabel")
sizePolicy4 = QSizePolicy(QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Preferred)
sizePolicy4.setHorizontalStretch(0)
sizePolicy4.setVerticalStretch(0)
sizePolicy4.setHeightForWidth(self.chunkSizeLabel.sizePolicy().hasHeightForWidth())
self.chunkSizeLabel.setSizePolicy(sizePolicy4)
self.horizontalLayout_4.addWidget(self.chunkSizeLabel)
self.chunkSizeBox = QSpinBox(self.chunkSizeWidget)
self.chunkSizeBox.setObjectName(u"chunkSizeBox")
self.chunkSizeBox.setMinimum(100)
self.chunkSizeBox.setMaximum(600)
self.chunkSizeBox.setValue(400)
self.horizontalLayout_4.addWidget(self.chunkSizeBox)
self.chunkSizeWarnLabel = QLabel(self.chunkSizeWidget)
self.chunkSizeWarnLabel.setObjectName(u"chunkSizeWarnLabel")
sizePolicy4.setHeightForWidth(self.chunkSizeWarnLabel.sizePolicy().hasHeightForWidth())
self.chunkSizeWarnLabel.setSizePolicy(sizePolicy4)
self.horizontalLayout_4.addWidget(self.chunkSizeWarnLabel)
self.gridLayout.addWidget(self.chunkSizeWidget, 6, 0, 1, 1)
mainWindow.setCentralWidget(self.centralWidget) mainWindow.setCentralWidget(self.centralWidget)
self.statusBar = QStatusBar(mainWindow) self.statusBar = QStatusBar(mainWindow)
@@ -471,13 +518,10 @@ class Ui_mainWindow(object):
mainWindow.setStatusBar(self.statusBar) mainWindow.setStatusBar(self.statusBar)
QWidget.setTabOrder(self.jobList, self.fileButton) QWidget.setTabOrder(self.jobList, self.fileButton)
QWidget.setTabOrder(self.fileButton, self.clearButton) QWidget.setTabOrder(self.fileButton, self.clearButton)
QWidget.setTabOrder(self.clearButton, self.defaultOutputFolderButton) QWidget.setTabOrder(self.clearButton, self.deviceBox)
QWidget.setTabOrder(self.defaultOutputFolderButton, self.defaultOutputFolderBox)
QWidget.setTabOrder(self.defaultOutputFolderBox, self.deviceBox)
QWidget.setTabOrder(self.deviceBox, self.widthBox) QWidget.setTabOrder(self.deviceBox, self.widthBox)
QWidget.setTabOrder(self.widthBox, self.heightBox) QWidget.setTabOrder(self.widthBox, self.heightBox)
QWidget.setTabOrder(self.heightBox, self.formatBox) QWidget.setTabOrder(self.heightBox, self.convertButton)
QWidget.setTabOrder(self.formatBox, self.convertButton)
QWidget.setTabOrder(self.convertButton, self.mangaBox) 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)
@@ -517,6 +561,58 @@ class Ui_mainWindow(object):
def retranslateUi(self, mainWindow): def retranslateUi(self, mainWindow):
mainWindow.setWindowTitle(QCoreApplication.translate("mainWindow", u"Kindle Comic Converter", None)) mainWindow.setWindowTitle(QCoreApplication.translate("mainWindow", u"Kindle Comic Converter", None))
#if QT_CONFIG(tooltip)
self.editorButton.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Shift+Click to edit directory.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.editorButton.setText(QCoreApplication.translate("mainWindow", u"Metadata Editor", None))
self.kofiButton.setText(QCoreApplication.translate("mainWindow", u"Support me on Ko-fi", None))
self.wikiButton.setText(QCoreApplication.translate("mainWindow", u"Wiki", None))
#if QT_CONFIG(tooltip)
self.preserveMarginLabel.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p>After calculating the cropping boundaries, &quot;back up&quot; a specified percentage amount.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.preserveMarginLabel.setText(QCoreApplication.translate("mainWindow", u"Preserve Margin %", None))
self.croppingPowerLabel.setText(QCoreApplication.translate("mainWindow", u"Cropping power:", None))
#if QT_CONFIG(tooltip)
self.hLabel.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Resolution of the target device.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.hLabel.setText(QCoreApplication.translate("mainWindow", u"Custom height:", None))
#if QT_CONFIG(tooltip)
self.widthBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Resolution of the target device.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
#if QT_CONFIG(tooltip)
self.wLabel.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Resolution of the target device.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.wLabel.setText(QCoreApplication.translate("mainWindow", u"Custom width:", None))
#if QT_CONFIG(tooltip)
self.heightBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Resolution of the target device.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
#if QT_CONFIG(tooltip)
self.convertButton.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Shift+Click to select the output directory for this list.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.convertButton.setText(QCoreApplication.translate("mainWindow", u"Convert", None))
self.clearButton.setText(QCoreApplication.translate("mainWindow", u"Clear list", None))
#if QT_CONFIG(tooltip)
self.deviceBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Target device.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
#if QT_CONFIG(tooltip)
self.fileButton.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Add CBR, CBZ, CB7 or PDF file to queue.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.fileButton.setText(QCoreApplication.translate("mainWindow", u"Add input file(s)", None))
#if QT_CONFIG(tooltip)
self.directoryButton.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Add directory containing JPG, PNG or GIF files to queue.<br/><span style=\" font-weight:600;\">CBR, CBZ and CB7 files inside will not be processed!</span></p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.directoryButton.setText(QCoreApplication.translate("mainWindow", u"Add input folder(s)", None))
#if QT_CONFIG(tooltip)
self.formatBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Output format.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
#if QT_CONFIG(tooltip)
self.jobList.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p>Double click on source to open it in metadata editor.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
#if QT_CONFIG(tooltip)
self.chunkSizeWidget.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p>Warning: chunk size greater than default may cause<br/>performance/battery issues, especially on older devices.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.chunkSizeLabel.setText(QCoreApplication.translate("mainWindow", u"Chunk size MB:", None))
self.chunkSizeWarnLabel.setText(QCoreApplication.translate("mainWindow", u"Greater than default may cause performance issues on older ereaders.", None))
#if QT_CONFIG(tooltip) #if QT_CONFIG(tooltip)
self.titleEdit.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p>Default Title</p></body></html>", None)) self.titleEdit.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p>Default Title</p></body></html>", None))
#endif // QT_CONFIG(tooltip) #endif // QT_CONFIG(tooltip)
@@ -622,61 +718,22 @@ class Ui_mainWindow(object):
#endif // QT_CONFIG(tooltip) #endif // QT_CONFIG(tooltip)
self.autocontrastBox.setText(QCoreApplication.translate("mainWindow", u"Autocontrast", None)) self.autocontrastBox.setText(QCoreApplication.translate("mainWindow", u"Autocontrast", None))
#if QT_CONFIG(tooltip) #if QT_CONFIG(tooltip)
self.jobList.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p>Double click on source to open it in metadata editor.</p></body></html>", None)) self.defaultOutputFolderBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - next to source<br/></span>Place output files next to source files</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - folder next to source<br/></span>Place output files in a folder next to source files</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Custom<br/></span>Place output files in custom directory specified by right button</p></body></html>", None))
#endif // QT_CONFIG(tooltip) #endif // QT_CONFIG(tooltip)
#if QT_CONFIG(tooltip) self.defaultOutputFolderBox.setText(QCoreApplication.translate("mainWindow", u"Output Folder", None))
self.hLabel.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Resolution of the target device.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.hLabel.setText(QCoreApplication.translate("mainWindow", u"Custom height:", None))
#if QT_CONFIG(tooltip)
self.widthBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Resolution of the target device.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
#if QT_CONFIG(tooltip)
self.wLabel.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Resolution of the target device.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.wLabel.setText(QCoreApplication.translate("mainWindow", u"Custom width:", None))
#if QT_CONFIG(tooltip)
self.heightBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Resolution of the target device.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.gammaLabel.setText(QCoreApplication.translate("mainWindow", u"Gamma: Auto", None))
#if QT_CONFIG(tooltip)
self.editorButton.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Shift+Click to edit directory.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.editorButton.setText(QCoreApplication.translate("mainWindow", u"Metadata Editor", None))
self.kofiButton.setText(QCoreApplication.translate("mainWindow", u"Support me on Ko-fi", None))
self.wikiButton.setText(QCoreApplication.translate("mainWindow", u"Wiki", None))
#if QT_CONFIG(tooltip)
self.preserveMarginLabel.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p>After calculating the cropping boundaries, &quot;back up&quot; a specified percentage amount.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.preserveMarginLabel.setText(QCoreApplication.translate("mainWindow", u"Preserve Margin %", None))
self.croppingPowerLabel.setText(QCoreApplication.translate("mainWindow", u"Cropping power:", None))
#if QT_CONFIG(tooltip)
self.convertButton.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Shift+Click to select the output directory for this list.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.convertButton.setText(QCoreApplication.translate("mainWindow", u"Convert", None))
self.clearButton.setText(QCoreApplication.translate("mainWindow", u"Clear list", None))
#if QT_CONFIG(tooltip)
self.deviceBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Target device.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
#if QT_CONFIG(tooltip)
self.fileButton.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Add CBR, CBZ, CB7 or PDF file to queue.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.fileButton.setText(QCoreApplication.translate("mainWindow", u"Add file(s)", None))
#if QT_CONFIG(tooltip) #if QT_CONFIG(tooltip)
self.defaultOutputFolderButton.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p>Use this to select the default output directory.</p></body></html>", None)) self.defaultOutputFolderButton.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p>Use this to select the default output directory.</p></body></html>", None))
#endif // QT_CONFIG(tooltip) #endif // QT_CONFIG(tooltip)
self.defaultOutputFolderButton.setText("") self.defaultOutputFolderButton.setText("")
#if QT_CONFIG(tooltip) #if QT_CONFIG(tooltip)
self.defaultOutputFolderBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - next to source<br/></span>Place output files next to source files</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - folder next to source<br/></span>Place output files in a folder next to source files</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Custom<br/></span>Place output files in custom directory specified by right button</p></body></html>", None)) self.jpegQualityBox.setToolTip(QCoreApplication.translate("mainWindow", u"The JPEG quality, on a scale from 0 (worst) to 95 (best). \n"
"\n"
"Default is 85 for most devices besides Kindle Scribe and Colorsoft, which are 90.\n"
"\n"
"Higher values are larger and higher quality, and may resolve blank page issues.", None))
#endif // QT_CONFIG(tooltip) #endif // QT_CONFIG(tooltip)
self.defaultOutputFolderBox.setText(QCoreApplication.translate("mainWindow", u"Output Folder", None)) self.jpegQualityBox.setText(QCoreApplication.translate("mainWindow", u"Custom JPEG Quality", None))
#if QT_CONFIG(tooltip) self.gammaLabel.setText(QCoreApplication.translate("mainWindow", u"Gamma: Auto", None))
self.formatBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Output format.</p></body></html>", None)) self.jpegQualityLabel.setText(QCoreApplication.translate("mainWindow", u"JPEG Quality:", None))
#endif // QT_CONFIG(tooltip)
#if QT_CONFIG(tooltip)
self.chunkSizeWidget.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p>Warning: chunk size greater than default may cause<br/>performance/battery issues, especially on older devices.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.chunkSizeLabel.setText(QCoreApplication.translate("mainWindow", u"Chunk size MB:", None))
self.chunkSizeWarnLabel.setText(QCoreApplication.translate("mainWindow", u"Greater than default may cause performance issues on older ereaders.", None))
# retranslateUi # retranslateUi

View File

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

@@ -65,16 +65,16 @@ def main(argv=None):
parser.print_help() parser.print_help()
return 0 return 0
if sys.platform.startswith('win'): if sys.platform.startswith('win'):
sources = set([source for option in options.input for source in glob(escape(option))]) sources = [source for option in options.input for source in glob(escape(option))]
else: else:
sources = set(options.input) sources = options.input
if len(sources) == 0: if len(sources) == 0:
print('No matching files found.') print('No matching files found.')
return 1 return 1
if options.filefusion: if options.filefusion:
fusion_path = makeFusion(list(sources)) fusion_path = makeFusion(list(sources))
sources.clear() sources.clear()
sources.add(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)
@@ -851,14 +851,16 @@ def mupdf_pdf_process_pages_parallel(filename, output_dir, target_height):
def getWorkFolder(afile): def getWorkFolder(afile, workdir=None):
if not workdir:
workdir = mkdtemp('', 'KCC-')
fullPath = os.path.join(workdir, 'OEBPS', 'Images')
else:
fullPath = workdir
if os.path.isdir(afile): if os.path.isdir(afile):
if disk_usage(gettempdir())[2] < getDirectorySize(afile) * 2.5: if disk_usage(gettempdir())[2] < getDirectorySize(afile) * 2.5:
raise UserWarning("Not enough disk space to perform conversion.") raise UserWarning("Not enough disk space to perform conversion.")
workdir = mkdtemp('', 'KCC-', os.path.dirname(afile))
try: try:
os.rmdir(workdir)
fullPath = os.path.join(workdir, 'OEBPS', 'Images')
copytree(afile, fullPath) copytree(afile, fullPath)
sanitizePermissions(fullPath) sanitizePermissions(fullPath)
return workdir return workdir
@@ -869,8 +871,6 @@ def getWorkFolder(afile):
if disk_usage(gettempdir())[2] < os.path.getsize(afile) * 2.5: if disk_usage(gettempdir())[2] < os.path.getsize(afile) * 2.5:
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))
fullPath = os.path.join(workdir, 'OEBPS', 'Images')
if not os.path.exists(fullPath): if not os.path.exists(fullPath):
os.makedirs(fullPath) os.makedirs(fullPath)
path = workdir path = workdir
@@ -887,8 +887,6 @@ def getWorkFolder(afile):
raise UserWarning(f"Failed to extract images from PDF file. {e}") raise UserWarning(f"Failed to extract images from PDF file. {e}")
return workdir return workdir
else: else:
workdir = mkdtemp('', 'KCC-', os.path.dirname(afile))
fullPath = os.path.join(workdir, 'OEBPS', 'Images')
if not os.path.exists(fullPath): if not os.path.exists(fullPath):
os.makedirs(fullPath) os.makedirs(fullPath)
try: try:
@@ -1127,7 +1125,7 @@ def chunk_directory(path):
for root, _, files in os.walk(os.path.join(path, 'OEBPS', 'Images')): for root, _, files in os.walk(os.path.join(path, 'OEBPS', 'Images')):
for f in files: for f in files:
# Windows MAX_LEN = 260 plus some buffer # Windows MAX_LEN = 260 plus some buffer
if os.name == 'nt' and len(os.path.join(root, f)) > 180: if os.name == 'nt' and len(os.path.join(root, f)) > 220:
flattenTree(os.path.join(path, 'OEBPS', 'Images')) flattenTree(os.path.join(path, 'OEBPS', 'Images'))
level = 1 level = 1
break break
@@ -1239,7 +1237,7 @@ def detectSuboptimalProcessing(tmppath, orgpath):
GUI.addMessage.emit('Source files are probably created by KCC. The second conversion will decrease quality.' GUI.addMessage.emit('Source files are probably created by KCC. The second conversion will decrease quality.'
, 'warning', False) , 'warning', False)
GUI.addMessage.emit('', '', False) GUI.addMessage.emit('', '', False)
if imageSmaller > imageNumber * 0.25 and not options.upscale and not options.stretch and options.profile != 'KS': if imageSmaller > imageNumber * 0.25 and not options.upscale and not options.stretch and not options.profile.startswith('KS'):
print("WARNING: More than 25% of images are smaller than target device resolution. " print("WARNING: More than 25% of images are smaller than target device resolution. "
"Consider enabling stretching or upscaling to improve readability.") "Consider enabling stretching or upscaling to improve readability.")
if GUI: if GUI:
@@ -1382,6 +1380,8 @@ def makeParser():
help="Create PNG files instead JPEG") help="Create PNG files instead JPEG")
processing_options.add_argument("--mozjpeg", action="store_true", dest="mozjpeg", default=False, processing_options.add_argument("--mozjpeg", action="store_true", dest="mozjpeg", default=False,
help="Create JPEG files using mozJpeg") help="Create JPEG files using mozJpeg")
processing_options.add_argument("--jpeg-quality", type=int, dest="jpegquality",
help="The JPEG quality, on a scale from 0 (worst) to 95 (best). Default 85 for most devices.")
processing_options.add_argument("--maximizestrips", action="store_true", dest="maximizestrips", default=False, processing_options.add_argument("--maximizestrips", action="store_true", dest="maximizestrips", default=False,
help="Turn 1x4 strips to 2x2 strips") help="Turn 1x4 strips to 2x2 strips")
processing_options.add_argument("-d", "--delete", action="store_true", dest="delete", default=False, processing_options.add_argument("-d", "--delete", action="store_true", dest="delete", default=False,
@@ -1481,6 +1481,17 @@ def checkOptions(options):
image.ProfileData.Profiles["Custom"] = newProfile image.ProfileData.Profiles["Custom"] = newProfile
options.profile = "Custom" options.profile = "Custom"
options.profileData = image.ProfileData.Profiles[options.profile] options.profileData = image.ProfileData.Profiles[options.profile]
if not options.jpegquality:
if options.profile.startswith('KS') or options.profile == 'KCS':
options.jpegquality = 90
else:
options.jpegquality = 85
options.kindle_azw3 = options.iskindle and ('MOBI' in options.format or 'EPUB' in options.format)
options.kindle_scribe_azw3 = options.profile.startswith('KS') and options.kindle_azw3
if options.kindle_scribe_azw3:
options.profileData = list(image.ProfileData.Profiles[options.profile])
options.profileData[1] = list(options.profileData[1])
options.profileData[1][0] = min(1920, options.profileData[1][0])
return options return options
@@ -1532,17 +1543,15 @@ def makeFusion(sources: List[str]):
print(f"Processing {source}...") print(f"Processing {source}...")
checkPre(source) checkPre(source)
print("Checking images...") print("Checking images...")
path = getWorkFolder(source)
pathfinder = os.path.join(path, "OEBPS", "Images")
sanitizeTree(pathfinder)
# TODO: remove flattenTree when subchapters are supported
flattenTree(pathfinder)
source_path = Path(source) source_path = Path(source)
if source_path.is_file(): if source_path.is_file():
os.renames(pathfinder, fusion_path.joinpath(source_path.stem)) targetpath = fusion_path.joinpath(source_path.stem)
else: else:
os.renames(pathfinder, fusion_path.joinpath(source_path.name)) targetpath = fusion_path.joinpath(source_path.name)
getWorkFolder(source, str(targetpath))
sanitizeTree(targetpath)
# TODO: remove flattenTree when subchapters are supported
flattenTree(targetpath)
end = perf_counter() end = perf_counter()
print(f"makefusion: {end - start} seconds") print(f"makefusion: {end - start} seconds")
@@ -1559,8 +1568,6 @@ def makeBook(source, qtgui=None, job_progress=''):
GUI.progressBarTick.emit('1') GUI.progressBarTick.emit('1')
else: else:
checkTools(source) checkTools(source)
options.kindle_azw3 = options.iskindle and ('MOBI' in options.format or 'EPUB' in options.format)
options.kindle_scribe_azw3 = options.profile == 'KS' and ('MOBI' in options.format or 'EPUB' in options.format)
checkPre(source) checkPre(source)
print(f"{job_progress}Preparing source images...") print(f"{job_progress}Preparing source images...")
path = getWorkFolder(source) path = getWorkFolder(source)

View File

@@ -62,18 +62,19 @@ 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 * 3: if targetHeight > 131072 * 4:
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
for i in imagesValid: for i in imagesValid:
img = Image.open(i).convert('RGB') with Image.open(i) as img:
if img.size[0] < targetWidth or img.size[0] > targetWidth: img = img.convert('RGB')
widthPercent = (targetWidth / float(img.size[0])) if img.size[0] < targetWidth or img.size[0] > targetWidth:
heightSize = int((float(img.size[1]) * float(widthPercent))) widthPercent = (targetWidth / float(img.size[0]))
img = ImageOps.fit(img, (targetWidth, heightSize), method=Image.BICUBIC, centering=(0.5, 0.5)) heightSize = int((float(img.size[1]) * float(widthPercent)))
result.paste(img, (0, y)) img = ImageOps.fit(img, (targetWidth, heightSize), method=Image.BICUBIC, centering=(0.5, 0.5))
y += img.size[1] result.paste(img, (0, y))
y += img.size[1]
os.remove(i) os.remove(i)
savePath = os.path.split(imagesValid[0]) savePath = os.path.split(imagesValid[0])
result.save(os.path.join(savePath[0], os.path.splitext(savePath[1])[0] + '.png'), 'PNG') result.save(os.path.join(savePath[0], os.path.splitext(savePath[1])[0] + '.png'), 'PNG')
@@ -253,10 +254,8 @@ def main(argv=None, job_progress='', qtgui=None):
return 1 return 1
if args.height > 0: if args.height > 0:
for sourceDir in args.input: for sourceDir in args.input:
targetDir = sourceDir + "-Splitted" targetDir = sourceDir
if os.path.isdir(sourceDir): if os.path.isdir(sourceDir):
rmtree(targetDir, True)
os.renames(sourceDir, targetDir)
work = [] work = []
pagenumber = 1 pagenumber = 1
splitWorkerOutput = [] splitWorkerOutput = []
@@ -313,8 +312,6 @@ def main(argv=None, job_progress='', qtgui=None):
rmtree(targetDir, True) rmtree(targetDir, True)
raise RuntimeError("One of workers crashed. Cause: " + splitWorkerOutput[0][0], raise RuntimeError("One of workers crashed. Cause: " + splitWorkerOutput[0][0],
splitWorkerOutput[0][1]) splitWorkerOutput[0][1])
if args.inPlace:
os.renames(targetDir, sourceDir)
else: else:
rmtree(targetDir, True) rmtree(targetDir, True)
raise UserWarning("C2P: Source directory is empty.") raise UserWarning("C2P: Source directory is empty.")

View File

@@ -101,8 +101,12 @@ class ProfileData:
'KO': ("Kindle Oasis 2/3/Paperwhite 12", (1264, 1680), Palette16, 1.0), 'KO': ("Kindle Oasis 2/3/Paperwhite 12", (1264, 1680), Palette16, 1.0),
'K11': ("Kindle 11", (1072, 1448), Palette16, 1.0), 'K11': ("Kindle 11", (1072, 1448), Palette16, 1.0),
'KPW5': ("Kindle Paperwhite 5/Signature Edition", (1236, 1648), Palette16, 1.0), 'KPW5': ("Kindle Paperwhite 5/Signature Edition", (1236, 1648), Palette16, 1.0),
'KS': ("Kindle Scribe", (1860, 2480), Palette16, 1.0), 'KS1860': ("Kindle 1860", (1860, 1920), Palette16, 1.0),
'KS1920': ("Kindle 1920", (1920, 1920), Palette16, 1.0),
'KS': ("Kindle Scribe 1/2", (1860, 2480), Palette16, 1.0),
'KCS': ("Kindle Colorsoft", (1264, 1680), Palette16, 1.0), 'KCS': ("Kindle Colorsoft", (1264, 1680), Palette16, 1.0),
'KS3': ("Kindle Scribe 3", (1986, 2648), Palette16, 1.0),
'KSCS': ("Kindle Scribe Colorsoft", (1986, 2648), Palette16, 1.0),
} }
ProfilesKindle = { ProfilesKindle = {
@@ -406,13 +410,13 @@ class ComicPage:
targetPath += '.jpg' targetPath += '.jpg'
if self.opt.mozjpeg: if self.opt.mozjpeg:
with io.BytesIO() as output: with io.BytesIO() as output:
image.save(output, format="JPEG", optimize=1, quality=85) image.save(output, format="JPEG", optimize=1, quality=self.opt.jpegquality)
input_jpeg_bytes = output.getvalue() input_jpeg_bytes = output.getvalue()
output_jpeg_bytes = mozjpeg_lossless_optimization.optimize(input_jpeg_bytes) output_jpeg_bytes = mozjpeg_lossless_optimization.optimize(input_jpeg_bytes)
with open(targetPath, "wb") as output_jpeg_file: with open(targetPath, "wb") as output_jpeg_file:
output_jpeg_file.write(output_jpeg_bytes) output_jpeg_file.write(output_jpeg_bytes)
else: else:
image.save(targetPath, 'JPEG', optimize=1, quality=85) image.save(targetPath, 'JPEG', optimize=1, quality=self.opt.jpegquality)
return targetPath return targetPath
def gammaCorrectImage(self): def gammaCorrectImage(self):
@@ -485,7 +489,7 @@ class ComicPage:
if self.opt.kindle_azw3 and any(dim > 1920 for dim in self.image.size): if self.opt.kindle_azw3 and any(dim > 1920 for dim in self.image.size):
self.image = ImageOps.contain(self.image, (1920, 1920), Image.Resampling.LANCZOS) self.image = ImageOps.contain(self.image, (1920, 1920), Image.Resampling.LANCZOS)
elif self.image.size[0] > self.size[0] * 2 or self.image.size[1] > self.size[1]: elif self.image.size[0] > self.size[0] * 2 or self.image.size[1] > self.size[1]:
self.image = ImageOps.contain(self.image, (self.size[0] * 2, self.size[1], Image.Resampling.LANCZOS)) self.image = ImageOps.contain(self.image, (self.size[0] * 2, self.size[1]), Image.Resampling.LANCZOS)
return return
ratio_device = float(self.size[1]) / float(self.size[0]) ratio_device = float(self.size[1]) / float(self.size[0])
@@ -562,6 +566,7 @@ class Cover:
size = list(self.options.profileData[1]) size = list(self.options.profileData[1])
if self.options.kindle_scribe_azw3: if self.options.kindle_scribe_azw3:
size[0] = min(size[0], 1920)
size[1] = min(size[1], 1920) size[1] = min(size[1], 1920)
self.image.thumbnail(tuple(size), Image.Resampling.LANCZOS) self.image.thumbnail(tuple(size), Image.Resampling.LANCZOS)
@@ -581,7 +586,7 @@ class Cover:
def save_to_epub(self, target, tomeid, len_tomes=0): def save_to_epub(self, target, tomeid, len_tomes=0):
try: try:
if tomeid == 0: if tomeid == 0:
self.image.save(target, "JPEG", optimize=1, quality=85) self.image.save(target, "JPEG", optimize=1, quality=self.options.jpegquality)
else: else:
copy = self.image.copy() copy = self.image.copy()
draw = ImageDraw.Draw(copy) draw = ImageDraw.Draw(copy)
@@ -595,7 +600,7 @@ class Cover:
stroke_fill=0, stroke_fill=0,
stroke_width=25 stroke_width=25
) )
copy.save(target, "JPEG", optimize=1, quality=85) copy.save(target, "JPEG", optimize=1, quality=self.options.jpegquality)
except IOError: except IOError:
raise RuntimeError('Failed to save cover.') raise RuntimeError('Failed to save cover.')
@@ -603,6 +608,6 @@ class Cover:
self.image = ImageOps.contain(self.image, (300, 470), Image.Resampling.LANCZOS) self.image = ImageOps.contain(self.image, (300, 470), Image.Resampling.LANCZOS)
try: try:
self.image.save(os.path.join(kindle.path.split('documents')[0], 'system', 'thumbnails', self.image.save(os.path.join(kindle.path.split('documents')[0], 'system', 'thumbnails',
'thumbnail_' + asin + '_EBOK_portrait.jpg'), 'JPEG', optimize=1, quality=85) 'thumbnail_' + asin + '_EBOK_portrait.jpg'), 'JPEG', optimize=1, quality=self.options.jpegquality)
except IOError: except IOError:
raise RuntimeError('Failed to upload cover.') raise RuntimeError('Failed to upload cover.')

View File

@@ -40,8 +40,8 @@ class BuildBinaryCommand(setuptools.Command):
if sys.platform == 'darwin': if sys.platform == 'darwin':
os.system('pyinstaller --hidden-import=_cffi_backend -y -D -i icons/comic2ebook.icns -n "Kindle Comic Converter" -w -s kcc.py') os.system('pyinstaller --hidden-import=_cffi_backend -y -D -i icons/comic2ebook.icns -n "Kindle Comic Converter" -w -s kcc.py')
# TODO /usr/bin/codesign --force -s "$MACOS_CERTIFICATE_NAME" --options runtime dist/Applications/Kindle\ Comic\ Converter.app -v # TODO /usr/bin/codesign --force -s "$MACOS_CERTIFICATE_NAME" --options runtime dist/Applications/Kindle\ Comic\ Converter.app -v
min_os = os.getenv('MACOSX_DEPLOYMENT_TARGET') min_os = os.getenv('MACOSX_DEPLOYMENT_TARGET', '')
if min_os: if min_os.startswith('10.1'):
os.system(f'appdmg kcc.json dist/kcc_osx_{min_os.replace(".", "_")}_legacy_{VERSION}.dmg') os.system(f'appdmg kcc.json dist/kcc_osx_{min_os.replace(".", "_")}_legacy_{VERSION}.dmg')
else: else:
os.system(f'appdmg kcc.json dist/kcc_macos_{platform.processor()}_{VERSION}.dmg') os.system(f'appdmg kcc.json dist/kcc_macos_{platform.processor()}_{VERSION}.dmg')