1
0
mirror of https://github.com/ciromattia/kcc synced 2026-04-15 13:38:46 +00:00

Compare commits

...

43 Commits
dev ... v7.1.2

Author SHA1 Message Date
Alex Xu
a9d0c57ba6 build appimage on ubuntu-22.04 instead of 24.04 (#803) 2025-01-06 18:05:47 -08:00
Alex Xu
5598596650 bump to 7.1.2 2025-01-05 08:59:37 -08:00
Alex Xu
4b7b6d1c58 Revert "qt6-wayland (#800)" (#801)
This reverts commit 075bc748d1.
2025-01-04 22:45:00 -08:00
Alex Xu
075bc748d1 qt6-wayland (#800) 2025-01-04 22:35:22 -08:00
Alex Xu
9e318ed33e add author gui (#799) 2025-01-04 21:49:51 -08:00
Alex Xu
6d21bfa6fa bump to 7.1.1 2025-01-03 20:08:13 -08:00
Alex Xu
132574d57d remove fastnumbers (#798) 2025-01-03 20:06:44 -08:00
Alex Xu
317fb33fd0 No Rotate option (#785)
* no rotate

* Revert "no rotate"

This reverts commit b6f1fe8882.

* implement norotate
2025-01-03 19:43:20 -08:00
Alex Xu
2189f4b1cb Revert "build appimage on ubuntu-20.04 instead of 22.04 (#795)" (#797)
This reverts commit 462a3cebde.
2024-12-31 18:41:10 -08:00
Alex Xu
462a3cebde build appimage on ubuntu-20.04 instead of 22.04 (#795) 2024-12-31 09:05:09 -08:00
Alex Xu
2a414ef936 bump to 7.1.0 2024-12-30 22:23:18 -08:00
Alex Xu
4adb998896 modify path with c2e and c2p (#790) 2024-12-30 22:19:23 -08:00
Alex Xu
315b6e150d enable synthetic spreads for all devices (#789) 2024-12-30 22:15:41 -08:00
Alex Xu
5875508597 fix dark mode (#794) 2024-12-30 20:16:11 -08:00
Alex Xu
0a4ef31daf Fix cropping power sliders (#793)
* undo gui changes

* fix gui
2024-12-30 19:15:02 -08:00
Alex Xu
c99df3308e macos-12 is deprecated on GitHub, switch to macos-13 intel (#791) 2024-12-25 12:49:46 -08:00
Alex Xu
e9482fbd6c linux search for kindlegen in ~/.local/bin for steam deck and more (#786)
* on steam deck, search for kindlegen in ~/.local/bin

* remove comment
2024-12-25 12:24:12 -08:00
Alex Xu
434fe90b00 don't delete/dedupe covers, just change initial alignment (#784)
* don't delete covers, just change initial alignment

* replace dedupecover with spreadshift
2024-12-19 09:06:59 -08:00
dependabot[bot]
73a91ec0ae Bump python from 3.12-slim-bullseye to 3.13-slim-bullseye
Bumps python from 3.12-slim-bullseye to 3.13-slim-bullseye.

---
updated-dependencies:
- dependency-name: python
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-13 15:43:51 -08:00
Bradley Newman
e0b1848e09 Update README to include the latest usage options for kcc-c2e.py 2024-12-13 15:42:19 -08:00
Bradley Newman
a9360e6bc3 Add --nokepub option
By setting this to true, '.epub' format will be output rather than
'.kepub.epub'
2024-12-13 15:42:19 -08:00
termdisc
ae475e709a Add additional check for differently named Fedora-based distros (#780)
Bazzite is Fedora-based but fails the `distro.id() == 'fedora'` check because `distro.id()` resolves to "bazzite." I added an additional check to include `distro.like() == 'fedora'` for situations like this.

This needs to be formatted as an or statement because plain Fedora resolves `distro.like()` as a null string.
2024-12-13 14:08:40 -08:00
Matthias Witte
4769f68265 Remarkable profiles (#781)
* Add profiles for remarkable 1, 2 and Paper Pro

* Add remarkable icons based on Other.png

* Mention remarkable profile in README.md
2024-12-13 14:08:08 -08:00
Alex Xu
dbe6043542 add center spread property 2024-12-13 10:13:18 -08:00
Alex Xu
e1a318145d fix left to right comic spread alignment 2024-12-13 09:59:33 -08:00
Alex Xu
a71523b2d4 Remove scribe cover guide 2024-12-03 16:52:43 -08:00
Alex Xu
7a5473f530 Update 7z readme 2024-12-03 09:42:11 -08:00
Alex Xu
f5a5624112 add numpy to armv7 2024-11-16 20:09:36 -08:00
Alex Xu
3b7e8dc9a5 Docker numpy (#775)
* docker-base-20241116

* docker-base-20241116
2024-11-16 20:01:50 -08:00
Alex Xu
e637f37ef0 bump to 7.0.0 2024-11-11 15:52:29 -08:00
neyney10
6ba690659f Fix a missing line from previous commit. 2024-11-11 15:51:41 -08:00
neyney10
50eb48fb9b Fix crash when trying to crop blank images. 2024-11-11 15:51:41 -08:00
neyney10
4a661a1a17 [A new image cropping algorithm]
1. Replaced both crop margins and crop margins & page num with newer algorithm.
2. Crop max power level increased to 3.0
3. Adds NumPy as a new dependency.
2024-11-11 15:51:41 -08:00
Alex Xu
c26383c4b5 bump 6.3.1 2024-11-11 11:17:29 -08:00
Alex Xu
4e6ee8b59b fix bookmark drift when dedupecover is checked (#772)
* remove cover chapter as needed

* just skip cover chapter

* fix space
2024-11-11 08:19:04 -08:00
Alex Xu
6c6f591e45 bump 6.3.0 2024-11-07 21:54:37 -08:00
Alex Xu
78c014bf22 simplify cover upload on newer MTP based Kindles by replacing EBOK with PDOC (#767)
* simplify covers by replacing ebok with pdoc

* refactor

* fix order
2024-11-07 21:53:59 -08:00
Alex Xu
421e6bcb64 bump 6.2.2 2024-11-06 14:55:00 -08:00
Alex Xu
5168cd73c4 Make it easier to add new profiles (#764)
* refactor

* put pw12 under oasis profile

* remove kindle 12 tag

* separate ebok and pdoc

* put cs 12 on top
2024-11-04 20:24:12 -08:00
Alex Xu
f7f19b99da fix ComicInfo.xml chapters 2024-11-04 09:04:17 -08:00
Alex Xu
1131bab41f bump 6.2.1 2024-10-23 21:21:27 -07:00
Alex Xu
8d204668a7 add kindle 12th gen, including colorsoft (#762)
* add kindle colorsoft

* fix typo
2024-10-23 21:17:22 -07:00
Alex Xu
99d94ceaa7 fix _cffi_backend 2024-10-16 19:24:01 -07:00
27 changed files with 931 additions and 443 deletions

View File

@@ -23,7 +23,7 @@ on:
jobs:
build:
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- name: Set up Python

View File

@@ -25,7 +25,7 @@ jobs:
build:
strategy:
matrix:
os: [ macos-12, macos-14 ]
os: [ macos-13, macos-14 ]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4

View File

@@ -1,5 +1,5 @@
# Select final stage based on TARGETARCH ARG
FROM ghcr.io/ciromattia/kcc:docker-base-20240928
FROM ghcr.io/ciromattia/kcc:docker-base-20241116
LABEL com.kcc.name="Kindle Comic Converter"
LABEL com.kcc.author="Ciro Mattia Gonano, Paweł Jastrzębski and Darodi"
LABEL org.opencontainers.image.description='Kindle Comic Converter'

View File

@@ -1,4 +1,4 @@
FROM --platform=linux/amd64 python:3.12-slim-bullseye as compile-amd64
FROM --platform=linux/amd64 python:3.13-slim-bullseye as compile-amd64
ARG TARGETOS
ARG TARGETARCH
ARG TARGETVARIANT
@@ -16,7 +16,7 @@ RUN DEBIAN_FRONTEND=noninteractive apt-get update -y && apt-get -yq upgrade && \
######################################################################################
FROM --platform=linux/arm64 python:3.12-slim-bullseye as compile-arm64
FROM --platform=linux/arm64 python:3.13-slim-bullseye as compile-arm64
ARG TARGETOS
ARG TARGETARCH
ARG TARGETVARIANT
@@ -73,7 +73,7 @@ RUN set -x && \
######################################################################################
FROM --platform=linux/arm/v7 python:3.12-slim-bullseye as compile-armv7
FROM --platform=linux/arm/v7 python:3.13-slim-bullseye as compile-armv7
ARG TARGETOS
ARG TARGETARCH
ARG TARGETVARIANT
@@ -126,17 +126,17 @@ RUN set -x && \
# Install required python modules
python -m pip install --upgrade pip && \
python -m venv /opt/venv && \
python -m pip install --upgrade pillow psutil requests python-slugify raven packaging mozjpeg-lossless-optimization natsort distro
python -m pip install --upgrade pillow psutil requests python-slugify raven packaging mozjpeg-lossless-optimization natsort distro numpy
######################################################################################
FROM --platform=linux/amd64 python:3.12-slim-bullseye as build-amd64
FROM --platform=linux/amd64 python:3.13-slim-bullseye as build-amd64
COPY --from=compile-amd64 /opt/venv /opt/venv
FROM --platform=linux/arm64 python:3.12-slim-bullseye as build-arm64
FROM --platform=linux/arm64 python:3.13-slim-bullseye as build-arm64
COPY --from=compile-arm64 /opt/venv /opt/venv
FROM --platform=linux/arm/v7 python:3.12-slim-bullseye as build-armv7
FROM --platform=linux/arm/v7 python:3.13-slim-bullseye as build-armv7
COPY --from=compile-armv7 /opt/venv /opt/venv
######################################################################################
@@ -160,5 +160,5 @@ WORKDIR /app
RUN DEBIAN_FRONTEND=noninteractive apt-get update -y && apt-get -yq upgrade && \
apt-get install -y p7zip-full unrar-free && \
ln -s /app/kindlegen /bin/kindlegen && \
echo docker-base-20240928 > /IMAGE_VERSION
echo docker-base-20241116 > /IMAGE_VERSION

View File

@@ -52,8 +52,6 @@ On Mac, right click open to get past the security warning.
For flatpak, Docker, and AppImage versions, refer to the wiki: https://github.com/ciromattia/kcc/wiki/Installation
## FAQ
- [Kindle Scribe cover guide](https://github.com/ciromattia/kcc/issues/508) (also works for older Kindles)
- [Windows 7 support](https://github.com/ciromattia/kcc/issues/678)
- [Combine files/chapters](https://github.com/ciromattia/kcc/issues/612#issuecomment-2117985011)
- [Flatpak mobi conversion stuck](https://github.com/ciromattia/kcc/wiki/Installation#linux)
@@ -70,9 +68,11 @@ If you have issues detecting it, get stuck on the MOBI conversion step, or use L
### 7-Zip
This is no longer required as of KCC 6.1.
This is only required for certain files and advanced features.
If you still need it, refer to the wiki: https://github.com/ciromattia/kcc/wiki/Installation#7-zip
KCC will ask you to install if needed.
Refer to the wiki to install: https://github.com/ciromattia/kcc/wiki/Installation#7-zip
## INPUT FORMATS
**KCC** can understand and convert, at the moment, the following input types:
@@ -107,7 +107,7 @@ sudo apt-get install python3 p7zip-full python3-pil python3-psutil python3-slugi
'KPW': ("Kindle Paperwhite 1/2", (758, 1024), Palette16, 1.8),
'KV': ("Kindle Paperwhite 3/4/Voyage/Oasis", (1072, 1448), Palette16, 1.8),
'KPW5': ("Kindle Paperwhite 5/Signature Edition", (1236, 1648), Palette16, 1.8),
'KO': ("Kindle Oasis 2/3", (1264, 1680), Palette16, 1.8),
'KO': ("Kindle Oasis 2/3/Paperwhite 12/Colorsoft 12", (1264, 1680), Palette16, 1.8),
'KS': ("Kindle Scribe", (1860, 2480), Palette16, 1.8),
'KoMT': ("Kobo Mini/Touch", (600, 800), Palette16, 1.8),
'KoG': ("Kobo Glo", (768, 1024), Palette16, 1.8),
@@ -124,6 +124,9 @@ sudo apt-get install python3 p7zip-full python3-pil python3-psutil python3-slugi
'KoF': ("Kobo Forma", (1440, 1920), Palette16, 1.8),
'KoS': ("Kobo Sage", (1440, 1920), Palette16, 1.8),
'KoE': ("Kobo Elipsa", (1404, 1872), Palette16, 1.8),
'Rmk1': ("reMarkable 1", (1404, 1872), Palette16, 1.8),
'Rmk2': ("reMarkable 2", (1404, 1872), Palette16, 1.8),
'RmkPP': ("reMarkable Paper Pro", (1620, 2160), Palette16, 1.8),
'OTHER': ("Other", (0, 0), Palette16, 1.8),
```
@@ -137,7 +140,8 @@ MANDATORY:
MAIN:
-p PROFILE, --profile PROFILE
Device profile (Available options: K1, K2, K34, K578, KDX, KPW, KPW5, KV, KO, K11, KS, KoMT, KoG, KoGHD, KoA, KoAHD, KoAH2O, KoAO, KoN, KoC, KoL, KoF, KoS, KoE) [Default=KV]
Device profile (Available options: K1, K2, K34, K578, KDX, KPW, KPW5, KV, KO, K11, KS, KoMT, KoG, KoGHD, KoA, KoAHD, KoAH2O, KoAO, KoN, KoC, KoCC, KoL, KoLC, KoF, KoS, KoE)
[Default=KV]
-m, --manga-style Manga style (right-to-left reading and splitting)
-q, --hq Try to increase the quality of magnification
-2, --two-panel Display two not four panels in Panel View mode
@@ -172,10 +176,15 @@ OUTPUT SETTINGS:
Output generated file to specified directory or file
-t TITLE, --title TITLE
Comic title [Default=filename or directory name]
-a AUTHOR, --author AUTHOR
Author name [Default=KCC]
-f FORMAT, --format FORMAT
Output format (Available options: Auto, MOBI, EPUB, CBZ, KFX, MOBI+EPUB) [Default=Auto]
--nokepub If format is EPUB, output file with '.epub' extension rather than '.kepub.epub'
-b BATCHSPLIT, --batchsplit BATCHSPLIT
Split output into multiple files. 0: Don't split 1: Automatic mode 2: Consider every subdirectory as separate volume [Default=0]
--spreadshift Shift first page to opposite side in landscape for two page spread alignment
--norotate Do not rotate double page spreads in spread splitter option.
CUSTOM PROFILE:
--customwidth CUSTOMWIDTH

View File

@@ -9,7 +9,7 @@ dependencies:
- python-slugify>=1.2.1
- raven>=6.0.0
- distro
- natsort[fast]>=8.4.0
- natsort>=8.4.0
- pip
- pip:
- mozjpeg-lossless-optimization>=1.1.2

View File

@@ -6,6 +6,7 @@
<file>../icons/Kobo.png</file>
<file>../icons/Other.png</file>
<file>../icons/Kindle.png</file>
<file>../icons/Rmk.png</file>
</qresource>
<qresource prefix="Formats">
<file>../icons/CBZ.png</file>

View File

@@ -37,20 +37,7 @@
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="2">
<widget class="QCheckBox" name="qualityBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Unchecked - 4 panels&lt;br/&gt;&lt;/span&gt;Zoom each corner separately.&lt;/p&gt;&lt;p style='white-space:pre'&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Indeterminate - 2 panels&lt;br/&gt;&lt;/span&gt;Zoom only the top and bottom of the page.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Checked - 4 high-quality panels&lt;br/&gt;&lt;/span&gt;Zoom each corner separately. Try to increase the quality of magnification. Check wiki for more details.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Panel View 4/2/HQ</string>
</property>
<property name="tristate">
<bool>true</bool>
</property>
</widget>
</item>
<item row="4" column="1">
<item row="5" column="1">
<widget class="QCheckBox" name="deleteBox">
<property name="toolTip">
<string>Delete input file(s) or directory. It's not recoverable!</string>
@@ -60,63 +47,7 @@
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QCheckBox" name="maximizeStrips">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Unchecked - 1x4&lt;br/&gt;&lt;/span&gt;Keep format 1x4 panels strips.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Checked - 2x2&lt;br/&gt;&lt;/span&gt;Turn 1x4 strips to 2x2 to maximize screen usage.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>1x4 to 2x2 strips</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QCheckBox" name="gammaBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Disable automatic gamma correction.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Custom gamma</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="borderBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Unchecked - Autodetection&lt;br/&gt;&lt;/span&gt;The color of margins fill will be detected automatically.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Indeterminate - White&lt;br/&gt;&lt;/span&gt;Margins will be filled with white color.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Checked - Black&lt;br/&gt;&lt;/span&gt;Margins will be filled with black color.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>W/B margins</string>
</property>
<property name="tristate">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="webtoonBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Enable special parsing mode for Korean Webtoons.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Webtoon mode</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="upscaleBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Unchecked - Nothing&lt;br/&gt;&lt;/span&gt;Images smaller than device resolution will not be resized.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Indeterminate - Stretching&lt;br/&gt;&lt;/span&gt;Images smaller than device resolution will be resized. Aspect ratio will be not preserved.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Checked - Upscaling&lt;br/&gt;&lt;/span&gt;Images smaller than device resolution will be resized. Aspect ratio will be preserved.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Stretch/Upscale</string>
</property>
<property name="tristate">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="mangaBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Enable right-to-left reading.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
@@ -126,20 +57,17 @@
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="rotateBox">
<item row="3" column="1">
<widget class="QCheckBox" name="outputSplit">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Unchecked - Split&lt;br/&gt;&lt;/span&gt;Double page spreads will be cut into two separate pages.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Indeterminate - Rotate and split&lt;br/&gt;&lt;/span&gt;Double page spreads will be displayed twice. First rotated and then split. &lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Checked - Rotate&lt;br/&gt;&lt;/span&gt;Double page spreads will be rotated.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Unchecked - Automatic mode&lt;br/&gt;&lt;/span&gt;The output will be split automatically.&lt;/p&gt;&lt;p style='white-space:pre'&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Checked - Volume mode&lt;br/&gt;&lt;/span&gt;Every subdirectory will be considered as a separate volume.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Spread splitter</string>
</property>
<property name="tristate">
<bool>true</bool>
<string>Output split</string>
</property>
</widget>
</item>
<item row="3" column="2">
<item row="4" column="2">
<widget class="QCheckBox" name="croppingBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Unchecked - Disabled&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Disabled&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Indeterminate - Margins&lt;br/&gt;&lt;/span&gt;Margins&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Checked - Margins + page numbers&lt;br/&gt;&lt;/span&gt;Margins +page numbers&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
@@ -152,17 +80,7 @@
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="outputSplit">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Unchecked - Automatic mode&lt;br/&gt;&lt;/span&gt;The output will be split automatically.&lt;/p&gt;&lt;p style='white-space:pre'&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Checked - Volume mode&lt;br/&gt;&lt;/span&gt;Every subdirectory will be considered as a separate volume.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Output split</string>
</property>
</widget>
</item>
<item row="3" column="0">
<item row="4" column="0">
<widget class="QCheckBox" name="mozJpegBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Unchecked - JPEG&lt;br/&gt;&lt;/span&gt;Use JPEG files&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Indeterminate - force PNG&lt;br/&gt;&lt;/span&gt;Create PNG files instead JPEG&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Checked - mozJpeg&lt;br/&gt;&lt;/span&gt;10-20% smaller JPEG file, with the same image quality, but processing time multiplied by 2&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
@@ -175,7 +93,20 @@
</property>
</widget>
</item>
<item row="2" column="2">
<item row="2" column="1">
<widget class="QCheckBox" name="upscaleBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Unchecked - Nothing&lt;br/&gt;&lt;/span&gt;Images smaller than device resolution will not be resized.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Indeterminate - Stretching&lt;br/&gt;&lt;/span&gt;Images smaller than device resolution will be resized. Aspect ratio will be not preserved.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Checked - Upscaling&lt;br/&gt;&lt;/span&gt;Images smaller than device resolution will be resized. Aspect ratio will be preserved.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Stretch/Upscale</string>
</property>
<property name="tristate">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QCheckBox" name="colorBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Disable conversion to grayscale.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
@@ -185,7 +116,96 @@
</property>
</widget>
</item>
<item row="4" column="2">
<item row="1" column="1">
<widget class="QCheckBox" name="rotateBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Unchecked - Split&lt;br/&gt;&lt;/span&gt;Double page spreads will be cut into two separate pages.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Indeterminate - Rotate and split&lt;br/&gt;&lt;/span&gt;Double page spreads will be displayed twice. First rotated and then split. &lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Checked - Rotate&lt;br/&gt;&lt;/span&gt;Double page spreads will be rotated.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Spread splitter</string>
</property>
<property name="tristate">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QCheckBox" name="gammaBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Disable automatic gamma correction.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Custom gamma</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QCheckBox" name="spreadShiftBox">
<property name="toolTip">
<string>Shift first page to opposite side in landscape for two page spread alignment</string>
</property>
<property name="text">
<string>Spread shift</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="webtoonBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Enable special parsing mode for Korean Webtoons.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Webtoon mode</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QCheckBox" name="maximizeStrips">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Unchecked - 1x4&lt;br/&gt;&lt;/span&gt;Keep format 1x4 panels strips.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Checked - 2x2&lt;br/&gt;&lt;/span&gt;Turn 1x4 strips to 2x2 to maximize screen usage.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>1x4 to 2x2 strips</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="borderBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Unchecked - Autodetection&lt;br/&gt;&lt;/span&gt;The color of margins fill will be detected automatically.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Indeterminate - White&lt;br/&gt;&lt;/span&gt;Margins will be filled with white color.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Checked - Black&lt;br/&gt;&lt;/span&gt;Margins will be filled with black color.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>W/B margins</string>
</property>
<property name="tristate">
<bool>true</bool>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QCheckBox" name="noRotateBox">
<property name="toolTip">
<string>Do not rotate double page spreads in spread splitter option.</string>
</property>
<property name="text">
<string>No rotate</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QCheckBox" name="qualityBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Unchecked - 4 panels&lt;br/&gt;&lt;/span&gt;Zoom each corner separately.&lt;/p&gt;&lt;p style='white-space:pre'&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Indeterminate - 2 panels&lt;br/&gt;&lt;/span&gt;Zoom only the top and bottom of the page.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Checked - 4 high-quality panels&lt;br/&gt;&lt;/span&gt;Zoom each corner separately. Try to increase the quality of magnification. Check wiki for more details.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Panel View 4/2/HQ</string>
</property>
<property name="tristate">
<bool>true</bool>
</property>
</widget>
</item>
<item row="5" column="2">
<widget class="QCheckBox" name="disableProcessingBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;pre style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Do not process any image, ignore profile and processing options&lt;/pre&gt;&lt;/body&gt;&lt;/html&gt;</string>
@@ -195,13 +215,25 @@
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QCheckBox" name="dedupeCoverBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Don't duplicate the first page as the cover. Useful for 2 page spread alignment.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<item row="0" column="0">
<widget class="QLineEdit" name="authorEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>De-dupe cover</string>
<property name="focusPolicy">
<enum>Qt::ClickFocus</enum>
</property>
<property name="toolTip">
<string>Default Author is KCC</string>
</property>
<property name="placeholderText">
<string>Default Author</string>
</property>
<property name="clearButtonEnabled">
<bool>false</bool>
</property>
</widget>
</item>
@@ -277,7 +309,7 @@
<item>
<widget class="QSlider" name="croppingPowerSlider">
<property name="maximum">
<number>200</number>
<number>300</number>
</property>
<property name="singleStep">
<number>1</number>
@@ -455,7 +487,7 @@
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Shift+Click to edit directory.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Editor</string>
<string>Metadata Editor</string>
</property>
<property name="icon">
<iconset resource="KCC.qrc">
@@ -486,7 +518,7 @@
<item row="2" column="0" colspan="2">
<widget class="QListWidget" name="jobList">
<property name="styleSheet">
<string notr="true">QListWidget#jobList {background:#ffffff;background-image:url(:/Other/icons/list_background.png);background-position:center center;background-repeat:no-repeat;color:rgb(0,0,0);}</string>
<string notr="true"/>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::NoSelection</enum>

BIN
icons/Rmk.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@@ -20,6 +20,8 @@
import sys
from kcc import modify_path
if sys.version_info < (3, 8, 0):
print('ERROR: This is a Python 3.8+ script!')
sys.exit(1)
@@ -28,6 +30,7 @@ from multiprocessing import freeze_support, set_start_method
from kindlecomicconverter.startup import startC2E
if __name__ == "__main__":
modify_path()
set_start_method('spawn')
freeze_support()
startC2E()

View File

@@ -8,7 +8,7 @@ a = Analysis(['kcc-c2e.py'],
pathex=['.'],
binaries=[],
datas=[],
hiddenimports=[],
hiddenimports=['_cffi_backend'],
hookspath=[],
runtime_hooks=[],
excludes=[],

View File

@@ -20,6 +20,8 @@
import sys
from kcc import modify_path
if sys.version_info < (3, 8, 0):
print('ERROR: This is a Python 3.8+ script!')
sys.exit(1)
@@ -28,6 +30,7 @@ from multiprocessing import freeze_support, set_start_method
from kindlecomicconverter.startup import startC2P
if __name__ == "__main__":
modify_path()
set_start_method('spawn')
freeze_support()
startC2P()

View File

@@ -8,7 +8,7 @@ a = Analysis(['kcc-c2p.py'],
pathex=['.'],
binaries=[],
datas=[],
hiddenimports=[],
hiddenimports=['_cffi_backend'],
hookspath=[],
runtime_hooks=[],
excludes=[],

105
kcc.py
View File

@@ -18,63 +18,76 @@
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
import os
import sys
import platform
from pathlib import Path
if sys.version_info < (3, 8, 0):
print('ERROR: This is a Python 3.8+ script!')
sys.exit(1)
# OS specific workarounds
import os
if sys.platform.startswith('darwin'):
# prioritize KC2 since it optionally also installs KP3
mac_paths = [
'/Applications/Kindle Comic Creator/Kindle Comic Creator.app/Contents/MacOS',
'/Applications/Kindle Previewer 3.app/Contents/lib/fc/bin/',
]
if getattr(sys, 'frozen', False):
os.environ['PATH'] += os.pathsep + os.pathsep.join(mac_paths +
[
'/opt/homebrew/bin',
'/usr/local/bin',
'/usr/bin',
'/bin',
]
)
os.chdir(os.path.dirname(os.path.abspath(sys.executable)))
else:
os.environ['PATH'] += os.pathsep + os.pathsep.join(mac_paths)
os.chdir(os.path.dirname(os.path.abspath(__file__)))
elif sys.platform.startswith('win'):
# prioritize KC2 since it optionally also installs KP3
win_paths = [
os.path.expandvars('%LOCALAPPDATA%\\Amazon\\KC2'),
os.path.expandvars('%LOCALAPPDATA%\\Amazon\\Kindle Previewer 3\\lib\\fc\\bin\\'),
os.path.expandvars('%UserProfile%\\Kindle Previewer 3\\lib\\fc\\bin\\'),
'C:\\Apps\\Kindle Previewer 3\\lib\\fc\\bin',
'D:\\Apps\\Kindle Previewer 3\\lib\\fc\\bin',
'E:\\Apps\\Kindle Previewer 3\\lib\\fc\\bin',
'C:\\Program Files\\7-Zip',
'D:\\Program Files\\7-Zip',
'E:\\Program Files\\7-Zip',
]
if getattr(sys, 'frozen', False):
os.environ['PATH'] += os.pathsep + os.pathsep.join(win_paths)
os.chdir(os.path.dirname(os.path.abspath(sys.executable)))
else:
os.environ['PATH'] += os.pathsep + os.pathsep.join(win_paths)
os.chdir(os.path.dirname(os.path.abspath(__file__)))
# Load additional Sentry configuration
# if getattr(sys, 'frozen', False):
# try:
# import kindlecomicconverter.sentry
# except ImportError:
# pass
def modify_path():
if platform.system() == 'Darwin':
mac_paths = [
'/Applications/Kindle Comic Creator/Kindle Comic Creator.app/Contents/MacOS',
'/Applications/Kindle Previewer 3.app/Contents/lib/fc/bin/',
]
if getattr(sys, 'frozen', False):
os.environ['PATH'] += os.pathsep + os.pathsep.join(mac_paths +
[
'/opt/homebrew/bin',
'/usr/local/bin',
'/usr/bin',
'/bin',
]
)
os.chdir(os.path.dirname(os.path.abspath(sys.executable)))
else:
os.environ['PATH'] += os.pathsep + os.pathsep.join(mac_paths)
os.chdir(os.path.dirname(os.path.abspath(__file__)))
elif platform.system() == 'Linux':
if getattr(sys, 'frozen', False):
os.environ['PATH'] += os.pathsep + os.pathsep.join(
[
str(Path.home() / ".local" / "bin"),
'/opt/homebrew/bin',
'/usr/local/bin',
'/usr/bin',
'/bin',
]
)
os.chdir(os.path.dirname(os.path.abspath(sys.executable)))
else:
os.chdir(os.path.dirname(os.path.abspath(__file__)))
elif platform.system() == 'Windows':
win_paths = [
os.path.expandvars('%LOCALAPPDATA%\\Amazon\\KC2'),
os.path.expandvars('%LOCALAPPDATA%\\Amazon\\Kindle Previewer 3\\lib\\fc\\bin\\'),
os.path.expandvars('%UserProfile%\\Kindle Previewer 3\\lib\\fc\\bin\\'),
'C:\\Apps\\Kindle Previewer 3\\lib\\fc\\bin',
'D:\\Apps\\Kindle Previewer 3\\lib\\fc\\bin',
'E:\\Apps\\Kindle Previewer 3\\lib\\fc\\bin',
'C:\\Program Files\\7-Zip',
'D:\\Program Files\\7-Zip',
'E:\\Program Files\\7-Zip',
]
if getattr(sys, 'frozen', False):
os.environ['PATH'] += os.pathsep + os.pathsep.join(win_paths)
os.chdir(os.path.dirname(os.path.abspath(sys.executable)))
else:
os.environ['PATH'] += os.pathsep + os.pathsep.join(win_paths)
os.chdir(os.path.dirname(os.path.abspath(__file__)))
from multiprocessing import freeze_support, set_start_method
from kindlecomicconverter.startup import start
if __name__ == "__main__":
modify_path()
set_start_method('spawn')
freeze_support()
start()

View File

@@ -106,6 +106,8 @@ class Icons:
self.deviceKindle.addPixmap(QtGui.QPixmap(":/Devices/icons/Kindle.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
self.deviceKobo = QtGui.QIcon()
self.deviceKobo.addPixmap(QtGui.QPixmap(":/Devices/icons/Kobo.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
self.deviceRmk = QtGui.QIcon()
self.deviceRmk.addPixmap(QtGui.QPixmap(":/Devices/icons/Rmk.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
self.deviceOther = QtGui.QIcon()
self.deviceOther.addPixmap(QtGui.QPixmap(":/Devices/icons/Other.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
@@ -254,8 +256,10 @@ class WorkerThread(QtCore.QThread):
options.noprocessing = True
if GUI.deleteBox.isChecked():
options.delete = True
if GUI.dedupeCoverBox.isChecked():
options.dedupecover = True
if GUI.spreadShiftBox.isChecked():
options.spreadshift = True
if GUI.noRotateBox.isChecked():
options.norotate = True
if GUI.mozJpegBox.checkState() == Qt.CheckState.PartiallyChecked:
options.forcepng = True
elif GUI.mozJpegBox.checkState() == Qt.CheckState.Checked:
@@ -265,6 +269,8 @@ class WorkerThread(QtCore.QThread):
options.customheight = str(GUI.heightBox.value())
if GUI.targetDirectory != '':
options.output = GUI.targetDirectory
if GUI.authorEdit.text():
options.author = str(GUI.authorEdit.text())
for i in range(GUI.jobList.count()):
# Make sure that we don't consider any system message as job to do
@@ -380,7 +386,7 @@ class WorkerThread(QtCore.QThread):
except Exception:
pass
MW.addMessage.emit('Processing MOBI files... <b>Done!</b>', 'info', True)
k = kindle.Kindle()
k = kindle.Kindle(options.profile)
if k.path and k.coverSupport:
for item in outputPath:
comic2ebook.options.covers[outputPath.index(item)][0].saveToKindle(
@@ -687,7 +693,6 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
# We still fill original text field with transparent content to trigger creation of horizontal scrollbar
item.setForeground(QtGui.QColor('transparent'))
label = QtWidgets.QLabel(message)
label.setStyleSheet('background-image:url('');background-color:rgba(0,0,0,0);color:rgb(0,0,0);')
label.setOpenExternalLinks(True)
GUI.jobList.addItem(item)
GUI.jobList.setItemWidget(item, label)
@@ -792,7 +797,8 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
'widthBox': GUI.widthBox.value(),
'heightBox': GUI.heightBox.value(),
'deleteBox': GUI.deleteBox.checkState().value,
'dedupeCoverBox': GUI.dedupeCoverBox.checkState().value,
'spreadShiftBox': GUI.spreadShiftBox.checkState().value,
'noRotateBox': GUI.noRotateBox.checkState().value,
'maximizeStrips': GUI.maximizeStrips.checkState().value,
'gammaSlider': float(self.gammaValue) * 100})
self.settings.sync()
@@ -934,6 +940,12 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
"Kindle PW 11": {
'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': True, 'ForceColor': False, 'Label': 'KPW5',
},
"Kindle PW 12": {
'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': True, 'ForceColor': False, 'Label': 'KO',
},
"Kindle CS 12": {
'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': True, 'ForceColor': True, 'Label': 'KO',
},
"Kindle PW 7/10": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': True, 'ForceColor': False, 'Label': 'KV'},
"Kindle PW 5/6": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
@@ -984,13 +996,21 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
'Label': 'KoS'},
"Kobo Elipsa": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, 'DefaultUpscale': True, 'ForceColor': False,
'Label': 'KoE'},
"reMarkable 1": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, 'DefaultUpscale': True, 'ForceColor': False,
'Label': 'Rmk1'},
"reMarkable 2": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, 'DefaultUpscale': True, 'ForceColor': False,
'Label': 'Rmk2'},
"reMarkable Paper Pro": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, 'DefaultUpscale': True, 'ForceColor': True,
'Label': 'RmkPP'},
"Other": {'PVOptions': False, 'ForceExpert': True, 'DefaultFormat': 1, 'DefaultUpscale': False, 'ForceColor': False,
'Label': 'OTHER'},
}
profilesGUI = [
"Kindle CS 12",
"Kindle PW 12",
"Kindle Scribe",
"Kindle 11",
"Kindle PW 11",
"Kindle 11",
"Kindle Oasis 9/10",
"Separator",
"Kobo Clara 2E",
@@ -1001,6 +1021,10 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
"Kobo Elipsa",
"Kobo Nia",
"Separator",
"reMarkable 1",
"reMarkable 2",
"reMarkable Paper Pro",
"Separator",
"Other",
"Separator",
"Kindle Oasis 8",
@@ -1089,6 +1113,8 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
GUI.deviceBox.addItem(self.icons.deviceOther, profile)
elif profile == "Separator":
GUI.deviceBox.insertSeparator(GUI.deviceBox.count() + 1)
elif 'reM' in profile:
GUI.deviceBox.addItem(self.icons.deviceRmk, profile)
elif 'Ko' in profile:
GUI.deviceBox.addItem(self.icons.deviceKobo, profile)
else:

View File

@@ -1,6 +1,6 @@
# Resource object code (Python 3)
# Created by: object code
# Created by: The Resource Compiler for Qt version 6.5.2
# Created by: The Resource Compiler for Qt version 6.8.1
# WARNING! All changes made in this file will be lost!
from PySide6 import QtCore
@@ -4908,6 +4908,138 @@ D-\xbea6\x9bu\xd3\xe9\xf4+@\x03\xb0\xa2V\
$\x12\x89D\x22\x91H$\x12\x89D\x15\xd1\xff\x00V\
\x1c\x01\xcd\xc9\x01\xf3\xd5\x00\x00\x00\x00IEND\xae\
B`\x82\
\x00\x00\x08\x12\
\x89\
PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
\x00\x00\x80\x00\x00\x00\x80\x08\x03\x00\x00\x00\xf4\xe0\x91\xf9\
\x00\x00\x00 cHRM\x00\x00z&\x00\x00\x80\x84\
\x00\x00\xfa\x00\x00\x00\x80\xe8\x00\x00u0\x00\x00\xea`\
\x00\x00:\x98\x00\x00\x17p\x9c\xbaQ<\x00\x00\x027\
PLTE\x00\x00\x00333333333\
3333333333333333\
3333333333333333\
3333333333333333\
3333333333333333\
3333333333333333\
3333333333333333\
3333333333333333\
3333333333333333\
3333333333333333\
3333333333333333\
33333333333333<<\
<[[[\x5c\x5c\x5c^^^___]]]\
RRRaaa\xf9\xf9\xf9\xca\xca\xca\xfa\xfa\xfa\xfb\
\xfb\xfb\xfc\xfc\xfc\xfd\xfd\xfd\xfe\xfe\xfe\xff\xff\xff\xfe\xfc\
\xfbbbbdddeeeggg444\
iiiccc\xff\xfe\xfe\xf6\xf6\xf6\x00\x00\x00\xaa\
\xaa\xaa222\xbd\xbd\xbdKKK\xd2\xd2\xd2\xf3\xf3\
\xf3\xb6\xb6\xb6\xd9\xd9\xd9\x95\x95\x95qqq\xe9\xe9\xe9\
\xf5\xf5\xf5\xf8\xf8\xf8&&&\x9d\x9d\x9d***\xbe\
\xbe\xbe\x96\x96\x96555\xcf\xcf\xcf\x8f\x8f\x8f\xc8\xc8\
\xc8\xe5\xe5\xe5\xb2\xb2\xb2fff@@@sss\
\xc1\xc1\xc1\xc0\xc0\xc0hhh\xd0\xd0\xd0\x91\x91\x91\x94\
\x94\x94\x9e\x9e\x9eppp\x80\x80\x80\xc2\xc2\xc2\xbb\xbb\
\xbb\x8e\x8e\x8e\x22\x22\x22\x0c\x0c\x0c\x0d\x0d\x0d\xaf\xaf\xaf\
\x90\x90\x90\xee\xee\xee\x1c\x1c\x1c\xf4\xf4\xf4\xcc\xcc\xcc}\
}}\x98\x98\x98\xa7\xa7\xa7\x7f\x7f\x7f\xc5\xc5\xc5\x8c\x8c\
\x8c\x9b\x9b\x9b\xc7\xc7\xc7\xb4\xb4\xb4\xf0\xf0\xf0\xd7\xd7\xd7\
\xda\xda\xda\xad\xad\xad\xcd\xcd\xcd\xc9\xc9\xc9\x87\x87\x87\xa3\
\xa3\xa3\xd4\xd4\xd4\x16\x16\x16\xdb\xdb\xdb\xd8\xd8\xd8\xc6\xc6\
\xc6MMM\x92\x92\x92\xa4\xa4\xa4\x82\x82\x82\xde\xde\xde\
EEE\xe0\xe0\xe0XXX\x93\x93\x93vvvu\
uu\xeb\xeb\xeb\xba\xba\xba\xdf\xdf\xdf\xb9\xb9\xb9SS\
S\xdc\xdc\xdc\xf2\xf2\xf2\xe8\xe8\xe8\x97\x97\x97\xbc\xbc\xbc\
\xd3\xd3\xd3\x8a\x8a\x8ammm\xfe\xfe\xfd\xfe\xfd\xfb\xfe\
\xfd\xfc\xfe\xfc\xfaVVV\xa5\xa5\xa5\xecOs\x00\x00\
\x00\x00=tRNS\x00\x1ba\x80\x8f\x85i)\xb1\
\xfd\xc9?S\xf4xA\xfak\x09\xe3\xf8\x22n\x9b\xc2\
\xec\x03\xf3\x0b9\x10>\x0f=\x02\xfb-\xdd\xfc\x94+\
V\x90\xbd\x01\x05\xb7\xd6\x14\x04\x8c\xfe\xaa%\x87\xcd\xed\
\xf2\xd5\x968\x82\x10\xbfn\x00\x00\x00\x01bKGD\
M\x80h e\x00\x00\x00\x09pHYs\x00\x00\x0b\
\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\x07tI\
ME\x07\xe8\x0c\x0a\x0c\x07#x\xb2 \xfb\x00\x00\x04\
\xecIDATx\xda\xed\x9b\x87w\x14E\x1c\xc7\xcf\
\x8a\xb1w\xb1\x12\x1b\xf6\xde\xcbI\x88zn\x99!\xa0\
\x06F\x10PS,(\xc1\x18\x90\xa8\x04\x05BDP\
\xac\xa0\xa0X\xc0\x02\xb6(\x88\xf5\x8fs\xca\xce\x96\xbb\
\xcb\xee\xec\xdcws\x8f\xf7\xf6\x9b\x97w7sy\xf7\
\xfd\xbc\xdf\xcc\xfcfvfR\xa9\x94*U\xaaT\xa9\
,\x1du\xf41\xc7\xda\xeb\xb8\xe3g\xb4f?\xe3\x84\
\x8ej\x8b:\xf1\xa4\x16\xfcO>\xa5U{\xae\x8eS\
\xad\xfdO;\x1d\xe0\xcfu\x86\xa5\xff\x99ga\xfc\xab\
g\x9fc\x07p.\xc8\xbfZ=\xcf\x0e`&\x0c\xe0\
\xfc\x0b\xac\x00.\x84\x01T\xad\xda\xe0\x22\x9c\x7f\xf5b\
\x1b\x80K\x80\x00\xb3\x8eh\x80\x07\xe6\x84\xea\xea\x9a\xdb\
=\xf7\xc1\xae9\xa6z\x08\x02\xf0p\xcdZ\x8f\xc0\x01\
\x1c\xa9\xb6\x018\x8e\xeb\xba\x1e\xff5E\x00\x03p{\
\xcf\x17\xf2L\x11\xb0\x00\x8e\xb0'B\x02\xc1\x88\x00\x0c\
\xe0){\x85`D\x00\x05p\x5c\xe9O\xb9$\x82g\
@\x80\x04\xe0\x0d\xa0\xfd5Av\x10\x80\x00\xa2\x03\x84\
\xfe\x0a\xc1\x80\x00\x07\x10\xf8G\x00\x94\xcc\x93\x04\xce\xf4\
\x004\xfaG\xcd\xe0\x14\x0e\xd0\x13v\xc0F\x82\x18B\
\xb3$\x89\x01\x98\xdf4\x00!\x81/S\xa3\x1b(\xc9\
\x80\x01X\x10\x04\x806H\xe7\x04\xdf\x93\x92/\x89$\
\x89\x01x4HA\xb4\x99\xc2\xcc\xa4a\xbc\xf8\xd8\x80\
\x00<\xf6xM\xb6\x00\x9dB\xa4N\xf1\xd1\x09\x02\xa8\
\xcf\x01\x19\x081\x02L\x13\x04\x00\xda\x8d\xa6\xa9\x8e\x00\
\xd5\x07\xe2\x11 \xe9\x04*=`\x01\x16$:a\x16\
@\x22Ic\x00z\x13\xc3\x90\x90\x85&\x04H\x80\x9e\
8\x00\xc9\x8c@\x10\x02\xec\x5c\x10u\x82l\xffx\x08\
p\x93Q\x94\x8a\xcd\x00\x88\x0a\x01r6\xcc\x01\x10\x85\
\x00\xb7\x1e\x88\xb5\x81\x01\x00\xd5!@.H|\x83\xee\
\x97\x08\x81\x18\x89\xf0\x15Q\xdcd\x11\xd3z\x22\xacs\
\x16\xeb\xba%\xb2\x0d\x90k\xc2\x14\x80'=]\xb74\
\xac[B\xd0\x00\x8dk\x92E\xcb\x96\x07nO\xe9\xaa\
\xa7U\xf9\x99\xbe~\x07\x0d\xd0tM0\xc0\xd8\xa00\
|V\x97\x9fS\xe5\xe7i0\x0c\xf0\xcf\x05u\x9d\x8d\
\xb1\x17\x84\xe1\x8a\x9a*\xbe\xc8\x06_\x12\xe5\x95E\x00\
\x0c\xe9\x0eW[\xf5\xf2\xe0\xf0+#D\x01\xac^#\
j_U\x00k\xd9\xe8k\x12\x00?\x0aB\x80\xd7\xbd\
7\xd4\x9bu\x01\xc0\x98(\xacW\x9d\xe2M\xb6\xb48\
\x80\x81\xbe\x91\xb7\xf8wo\xd8\x18t\xbcM\x01\xc0\xb8\
(l\x9e\x10\xa5\xb7\xd9\x0a'\x04\x00'\x22\xd9\x0b\xb6\
H\xe7w\xfa\xc7\xbb\xf9\xcb\xd6\x00\x80n\x13\x95\xef\x8a\
\xd2{l;\x95\x00\xef\xe3S\xb1\xd4Z\xf1\xe5\xc3\xbc\
\xc3}\xc0X\xf7\x87\x1a`\xa5\xa8\xdd\xc2\x0b\xfd\x9b\xd9\
GTG\xa08\x80\xed\xdc\xea\xe3Ov\xb8T\x03\xf0\
\x96\xe7\x1a\xa7t'\x1bv\xa3\x08\xb8E\x01\xacK\x0e\
\xc3\xd5\x94\x8e\x8a\xea1J?e\x9f\xd1i\x00\xd8\xd5\
\x00\xb0KT\xaf\xa1\xcbv\xb3\xcf\x8b\x05p$\xc0\x17\
\x0d\x00\xee\xb0\xa8\x1f\xda\xc3\xbe\xf4\x0b\x06\xf8\xaa9\x00\
\xfdZ\xd4o\xfcF\xce\x8am\x01\xd8+\xea\xf7m`\
\xdf\x16\x0b\xe0\xb8S\x01\x90.\x99 \xbe#\xc5\x02\xb8\
\xde\xf7S\x00\x88\xbc \x1a\x81\x16\x0a\xc0\xa7\xe3\xa9\x22\
@\xfb$\xc0P\xa1\x00bQ\xf8\x83\xf8\xf2\x1d1\xff\
\x1ac\xab\xe4\x9b\xfd\xfc\x83^\xf9nT-\x87\x88\x07\
\x06p\xdc\xda\x81\x1f\xd5t\xf8\xd3DM\xaf\x88~f\
\xec\x97_'y\xd3\x8f\xf0\x0f\x96\xf3\xaa\x89\xbd\xfb\xe4\
\xdf\xfc61\x80\x06\xf0\xa2\xe5\x1e\xfb=\xb9&\x9c\xa4\
tr7c}r\x85\xa4u\x10\xfcd\xe4z\x87\xd2\
\x00\xe8\x1f\xec0M\x00\x8c\xa1\x01\xf2<\x16\x10\xdd\x05\
\xd0\xcf\x86\xe6\x00\xd8\xc7\xf3\xc4\xd3q;\x00z\x9a\xaf\
\x88\xd3Z\x00\x1d\x81\x5c\x00\xf1m\xaa\xb6E@oS\
\x01#\x90\xa3\x0f\x90h\x8f\xa6m\x9d\x10\x9e\x8ac\xe7\
E\xc6!\x00\x03\xe4\x0d\x81_\xc8\x0eI\x9e\x10x\x85\
\xec\x90\xe4 @o\xd1\xc4N-\xb3vJc\x03\x11\
\x1f\x01\x09`DP(\x80\x10\x99\xbe\x08\xf4\xc4\xfd\xc5\
\xf7g\x87\x00\x0b\xd0\x9b\xd8\xaa5\x1d\x05@\x80\xf9y\
\xd3\x00\xa5\x7f\x06\xd3\x11\xee\xd8\xceh2\x0a[f\xe1\
_\xd0Dd\x0c\x10\xb6\xc0\xdf\xff@\x9f\x0b\xf26\x01\
\xf1kzA0m\x9d\x90D?\xc1Y*vA\x12\
\x1f\x86D\xf9\xa5\xca\xc7\xae\x88\xea\x13Q\xa6\xa2\xe3[\
\xf0f\xb5o\xc2 o\xf98\xd0sC\xbdW\xecz\
\xea\x16Q\xaa\xd4\xf99\xf6\xe8VoP8\xe1%\x81\
T\xc5o\x10\xe0\xef\x92\x05\xf7$\xd2\x84\xbfA\xd1\xf6\
\xdbt\xff\xee\xb4\xd6\x7f\x10\x00\x84J\x80\x12\xa0\x048\
2\x01:\x81\x00\x97\xda\x00\x5cv9\x0e\xe0\x0a\x1b\x80\
\xca\x950\xff\xd9\x9dV\x00W\xc1\x00fZ\xf9W\xae\
\x9e\x8d\x02\xb8\xc6\x0e\xa0r-\xc8\xff\xba\xeb-\x01n\
\xb8\x11\xe2\x7f\xd3\xcd\x96\xfe\x95\xca-\xb7\xb6\xfe\x9fF\
\xb7\xdd\xdei\xed\xcfu\xc7\x9dw\xdd\xdda\xaf{\xee\
\xbd\xef\xfeV\xecK\x95*U\xaax\xfd\x0f\xf4\x94\xdc\
\x07\xeb\xfb\xc9m\x00\x00\x00\x00IEND\xaeB`\
\x82\
\x00\x00\x05\xe0\
\x89\
PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
@@ -11397,6 +11529,10 @@ qt_resource_name = b"\
\x05\x92]\x07\
\x00K\
\x00o\x00b\x00o\x00.\x00p\x00n\x00g\
\x00\x07\
\x09>W\xe7\
\x00R\
\x00m\x00k\x00.\x00p\x00n\x00g\
\x00\x09\
\x0e\xc5\xfa\x07\
\x00O\
@@ -11463,11 +11599,11 @@ qt_resource_name = b"\
qt_resource_struct = b"\
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x05\x00\x00\x00\x01\
\x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x00J\x00\x02\x00\x00\x00\x01\x00\x00\x00\x1b\
\x00\x00\x00J\x00\x02\x00\x00\x00\x01\x00\x00\x00\x1c\
\x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x00&\x00\x02\x00\x00\x00\x01\x00\x00\x00\x13\
\x00\x00\x00&\x00\x02\x00\x00\x00\x01\x00\x00\x00\x14\
\x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x0f\
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x10\
\x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x006\x00\x02\x00\x00\x00\x01\x00\x00\x00\x0b\
\x00\x00\x00\x00\x00\x00\x00\x00\
@@ -11475,47 +11611,49 @@ qt_resource_struct = b"\
\x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x00X\x00\x02\x00\x00\x00\x04\x00\x00\x00\x07\
\x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x01\xac\x00\x00\x00\x00\x00\x01\x00\x02&\xd7\
\x00\x00\x01\xc0\x00\x00\x00\x00\x00\x01\x00\x02.\xed\
\x00\x00\x01\x88;p\xbcB\
\x00\x00\x01\xea\x00\x00\x00\x00\x00\x01\x00\x02{q\
\x00\x00\x01\xfe\x00\x00\x00\x00\x00\x01\x00\x02\x83\x87\
\x00\x00\x01\x88;p\xbcB\
\x00\x00\x01\xd6\x00\x00\x00\x00\x00\x01\x00\x02Qv\
\x00\x00\x01\xea\x00\x00\x00\x00\x00\x01\x00\x02Y\x8c\
\x00\x00\x01\x88;p\xbcB\
\x00\x00\x01\xc2\x00\x00\x00\x00\x00\x01\x00\x02F\x13\
\x00\x00\x01\xd6\x00\x00\x00\x00\x00\x01\x00\x02N)\
\x00\x00\x01\x89\x89D9.\
\x00\x00\x00X\x00\x02\x00\x00\x00\x03\x00\x00\x00\x0c\
\x00\x00\x00X\x00\x02\x00\x00\x00\x04\x00\x00\x00\x0c\
\x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x00\xa6\x00\x00\x00\x00\x00\x01\x00\x01(\x97\
\x00\x00\x01\x88;p\xbcB\
\x00\x00\x00\xbc\x00\x00\x00\x00\x00\x01\x00\x011\xef\
\x00\x00\x01\x94\x1a\xa2\xa2\x92\
\x00\x00\x00\x8c\x00\x00\x00\x00\x00\x01\x00\x01\x1d\x90\
\x00\x00\x01\x88;p\xbcB\
\x00\x00\x00\xbc\x00\x00\x00\x00\x00\x01\x00\x011\xef\
\x00\x00\x00\xd0\x00\x00\x00\x00\x00\x01\x00\x01:\x05\
\x00\x00\x01\x88;p\xbcB\
\x00\x00\x00X\x00\x02\x00\x00\x00\x03\x00\x00\x00\x10\
\x00\x00\x00X\x00\x02\x00\x00\x00\x03\x00\x00\x00\x11\
\x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x02.\x00\x00\x00\x00\x00\x01\x00\x02\xad\xbd\
\x00\x00\x02B\x00\x00\x00\x00\x00\x01\x00\x02\xb5\xd3\
\x00\x00\x01\x88;p\xbcJ\
\x00\x00\x02\x00\x00\x00\x00\x00\x00\x01\x00\x02\x97\xc0\
\x00\x00\x02\x14\x00\x00\x00\x00\x00\x01\x00\x02\x9f\xd6\
\x00\x00\x01\x88;p\xbcI\
\x00\x00\x02\x16\x00\x00\x00\x00\x00\x01\x00\x02\xa1\x1d\
\x00\x00\x02*\x00\x00\x00\x00\x00\x01\x00\x02\xa93\
\x00\x00\x01\x88;p\xbcI\
\x00\x00\x00X\x00\x02\x00\x00\x00\x07\x00\x00\x00\x14\
\x00\x00\x00X\x00\x02\x00\x00\x00\x07\x00\x00\x00\x15\
\x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x01\x08\x00\x00\x00\x00\x00\x01\x00\x01H\x9b\
\x00\x00\x01\x1c\x00\x00\x00\x00\x00\x01\x00\x01P\xb1\
\x00\x00\x01\x88;p\xbcJ\
\x00\x00\x01\x1e\x00\x00\x00\x00\x00\x01\x00\x01qC\
\x00\x00\x012\x00\x00\x00\x00\x00\x01\x00\x01yY\
\x00\x00\x01\x88;p\xbcI\
\x00\x00\x01\x80\x00\x00\x00\x00\x00\x01\x00\x01\xca\x17\
\x00\x00\x01\x88;p\xbcI\
\x00\x00\x01f\x00\x00\x00\x00\x00\x01\x00\x01\x84\xd0\
\x00\x00\x01\x94\x00\x00\x00\x00\x00\x01\x00\x01\xd2-\
\x00\x00\x01\x94\x1a\xbb\xe8\xa7\
\x00\x00\x01z\x00\x00\x00\x00\x00\x01\x00\x01\x8c\xe6\
\x00\x00\x01\x88;p\xbcH\
\x00\x00\x00\xf0\x00\x00\x00\x00\x00\x01\x00\x01D<\
\x00\x00\x01\x04\x00\x00\x00\x00\x00\x01\x00\x01LR\
\x00\x00\x01\x88;p\xbcF\
\x00\x00\x00\xd4\x00\x00\x00\x00\x00\x01\x00\x017\xd3\
\x00\x00\x00\xe8\x00\x00\x00\x00\x00\x01\x00\x01?\xe9\
\x00\x00\x01\x88;p\xbcH\
\x00\x00\x01@\x00\x00\x00\x00\x00\x01\x00\x01z\x9a\
\x00\x00\x01T\x00\x00\x00\x00\x00\x01\x00\x01\x82\xb0\
\x00\x00\x01\x88;p\xbcH\
\x00\x00\x00X\x00\x02\x00\x00\x00\x01\x00\x00\x00\x1c\
\x00\x00\x00X\x00\x02\x00\x00\x00\x01\x00\x00\x00\x1d\
\x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x00h\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
\x00\x00\x01\x88;p\xbcH\

View File

@@ -3,7 +3,7 @@
################################################################################
## Form generated from reading UI file 'KCC.ui'
##
## Created by: Qt User Interface Compiler version 6.5.2
## Created by: Qt User Interface Compiler version 6.8.1
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
@@ -16,10 +16,10 @@ from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
QImage, QKeySequence, QLinearGradient, QPainter,
QPalette, QPixmap, QRadialGradient, QTransform)
from PySide6.QtWidgets import (QAbstractItemView, QApplication, QCheckBox, QComboBox,
QGridLayout, QHBoxLayout, QLabel, QListWidget,
QListWidgetItem, QMainWindow, QProgressBar, QPushButton,
QSizePolicy, QSlider, QSpinBox, QStatusBar,
QWidget)
QGridLayout, QHBoxLayout, QLabel, QLineEdit,
QListWidget, QListWidgetItem, QMainWindow, QProgressBar,
QPushButton, QSizePolicy, QSlider, QSpinBox,
QStatusBar, QWidget)
from . import KCC_rc
class Ui_mainWindow(object):
@@ -28,7 +28,7 @@ class Ui_mainWindow(object):
mainWindow.setObjectName(u"mainWindow")
mainWindow.resize(450, 400)
icon = QIcon()
icon.addFile(u":/Icon/icons/comic2ebook.png", QSize(), QIcon.Normal, QIcon.Off)
icon.addFile(u":/Icon/icons/comic2ebook.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
mainWindow.setWindowIcon(icon)
self.centralWidget = QWidget(mainWindow)
self.centralWidget.setObjectName(u"centralWidget")
@@ -40,86 +40,103 @@ class Ui_mainWindow(object):
self.gridLayout_2 = QGridLayout(self.optionWidget)
self.gridLayout_2.setObjectName(u"gridLayout_2")
self.gridLayout_2.setContentsMargins(0, 0, 0, 0)
self.qualityBox = QCheckBox(self.optionWidget)
self.qualityBox.setObjectName(u"qualityBox")
self.qualityBox.setTristate(True)
self.gridLayout_2.addWidget(self.qualityBox, 0, 2, 1, 1)
self.deleteBox = QCheckBox(self.optionWidget)
self.deleteBox.setObjectName(u"deleteBox")
self.gridLayout_2.addWidget(self.deleteBox, 4, 1, 1, 1)
self.maximizeStrips = QCheckBox(self.optionWidget)
self.maximizeStrips.setObjectName(u"maximizeStrips")
self.gridLayout_2.addWidget(self.maximizeStrips, 3, 1, 1, 1)
self.gammaBox = QCheckBox(self.optionWidget)
self.gammaBox.setObjectName(u"gammaBox")
self.gridLayout_2.addWidget(self.gammaBox, 1, 2, 1, 1)
self.borderBox = QCheckBox(self.optionWidget)
self.borderBox.setObjectName(u"borderBox")
self.borderBox.setTristate(True)
self.gridLayout_2.addWidget(self.borderBox, 2, 0, 1, 1)
self.webtoonBox = QCheckBox(self.optionWidget)
self.webtoonBox.setObjectName(u"webtoonBox")
self.gridLayout_2.addWidget(self.webtoonBox, 1, 0, 1, 1)
self.upscaleBox = QCheckBox(self.optionWidget)
self.upscaleBox.setObjectName(u"upscaleBox")
self.upscaleBox.setTristate(True)
self.gridLayout_2.addWidget(self.upscaleBox, 1, 1, 1, 1)
self.gridLayout_2.addWidget(self.deleteBox, 5, 1, 1, 1)
self.mangaBox = QCheckBox(self.optionWidget)
self.mangaBox.setObjectName(u"mangaBox")
self.gridLayout_2.addWidget(self.mangaBox, 0, 0, 1, 1)
self.gridLayout_2.addWidget(self.mangaBox, 1, 0, 1, 1)
self.rotateBox = QCheckBox(self.optionWidget)
self.rotateBox.setObjectName(u"rotateBox")
self.rotateBox.setTristate(True)
self.outputSplit = QCheckBox(self.optionWidget)
self.outputSplit.setObjectName(u"outputSplit")
self.gridLayout_2.addWidget(self.rotateBox, 0, 1, 1, 1)
self.gridLayout_2.addWidget(self.outputSplit, 3, 1, 1, 1)
self.croppingBox = QCheckBox(self.optionWidget)
self.croppingBox.setObjectName(u"croppingBox")
self.croppingBox.setTristate(True)
self.gridLayout_2.addWidget(self.croppingBox, 3, 2, 1, 1)
self.outputSplit = QCheckBox(self.optionWidget)
self.outputSplit.setObjectName(u"outputSplit")
self.gridLayout_2.addWidget(self.outputSplit, 2, 1, 1, 1)
self.gridLayout_2.addWidget(self.croppingBox, 4, 2, 1, 1)
self.mozJpegBox = QCheckBox(self.optionWidget)
self.mozJpegBox.setObjectName(u"mozJpegBox")
self.mozJpegBox.setTristate(True)
self.gridLayout_2.addWidget(self.mozJpegBox, 3, 0, 1, 1)
self.gridLayout_2.addWidget(self.mozJpegBox, 4, 0, 1, 1)
self.upscaleBox = QCheckBox(self.optionWidget)
self.upscaleBox.setObjectName(u"upscaleBox")
self.upscaleBox.setTristate(True)
self.gridLayout_2.addWidget(self.upscaleBox, 2, 1, 1, 1)
self.colorBox = QCheckBox(self.optionWidget)
self.colorBox.setObjectName(u"colorBox")
self.gridLayout_2.addWidget(self.colorBox, 2, 2, 1, 1)
self.gridLayout_2.addWidget(self.colorBox, 3, 2, 1, 1)
self.rotateBox = QCheckBox(self.optionWidget)
self.rotateBox.setObjectName(u"rotateBox")
self.rotateBox.setTristate(True)
self.gridLayout_2.addWidget(self.rotateBox, 1, 1, 1, 1)
self.gammaBox = QCheckBox(self.optionWidget)
self.gammaBox.setObjectName(u"gammaBox")
self.gridLayout_2.addWidget(self.gammaBox, 2, 2, 1, 1)
self.spreadShiftBox = QCheckBox(self.optionWidget)
self.spreadShiftBox.setObjectName(u"spreadShiftBox")
self.gridLayout_2.addWidget(self.spreadShiftBox, 5, 0, 1, 1)
self.webtoonBox = QCheckBox(self.optionWidget)
self.webtoonBox.setObjectName(u"webtoonBox")
self.gridLayout_2.addWidget(self.webtoonBox, 2, 0, 1, 1)
self.maximizeStrips = QCheckBox(self.optionWidget)
self.maximizeStrips.setObjectName(u"maximizeStrips")
self.gridLayout_2.addWidget(self.maximizeStrips, 4, 1, 1, 1)
self.borderBox = QCheckBox(self.optionWidget)
self.borderBox.setObjectName(u"borderBox")
self.borderBox.setTristate(True)
self.gridLayout_2.addWidget(self.borderBox, 3, 0, 1, 1)
self.noRotateBox = QCheckBox(self.optionWidget)
self.noRotateBox.setObjectName(u"noRotateBox")
self.gridLayout_2.addWidget(self.noRotateBox, 6, 1, 1, 1)
self.qualityBox = QCheckBox(self.optionWidget)
self.qualityBox.setObjectName(u"qualityBox")
self.qualityBox.setTristate(True)
self.gridLayout_2.addWidget(self.qualityBox, 1, 2, 1, 1)
self.disableProcessingBox = QCheckBox(self.optionWidget)
self.disableProcessingBox.setObjectName(u"disableProcessingBox")
self.gridLayout_2.addWidget(self.disableProcessingBox, 4, 2, 1, 1)
self.gridLayout_2.addWidget(self.disableProcessingBox, 5, 2, 1, 1)
self.dedupeCoverBox = QCheckBox(self.optionWidget)
self.dedupeCoverBox.setObjectName(u"dedupeCoverBox")
self.authorEdit = QLineEdit(self.optionWidget)
self.authorEdit.setObjectName(u"authorEdit")
sizePolicy = QSizePolicy(QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.authorEdit.sizePolicy().hasHeightForWidth())
self.authorEdit.setSizePolicy(sizePolicy)
self.authorEdit.setFocusPolicy(Qt.ClickFocus)
self.authorEdit.setClearButtonEnabled(False)
self.gridLayout_2.addWidget(self.dedupeCoverBox, 4, 0, 1, 1)
self.gridLayout_2.addWidget(self.authorEdit, 0, 0, 1, 1)
self.gridLayout.addWidget(self.optionWidget, 5, 0, 1, 2)
@@ -159,7 +176,7 @@ class Ui_mainWindow(object):
self.croppingPowerSlider = QSlider(self.croppingWidget)
self.croppingPowerSlider.setObjectName(u"croppingPowerSlider")
self.croppingPowerSlider.setMaximum(200)
self.croppingPowerSlider.setMaximum(300)
self.croppingPowerSlider.setSingleStep(1)
self.croppingPowerSlider.setOrientation(Qt.Horizontal)
@@ -170,11 +187,11 @@ class Ui_mainWindow(object):
self.buttonWidget = QWidget(self.centralWidget)
self.buttonWidget.setObjectName(u"buttonWidget")
sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.buttonWidget.sizePolicy().hasHeightForWidth())
self.buttonWidget.setSizePolicy(sizePolicy)
sizePolicy1 = QSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Fixed)
sizePolicy1.setHorizontalStretch(0)
sizePolicy1.setVerticalStretch(0)
sizePolicy1.setHeightForWidth(self.buttonWidget.sizePolicy().hasHeightForWidth())
self.buttonWidget.setSizePolicy(sizePolicy1)
self.gridLayout_4 = QGridLayout(self.buttonWidget)
self.gridLayout_4.setObjectName(u"gridLayout_4")
self.gridLayout_4.setContentsMargins(0, 0, 0, 0)
@@ -182,7 +199,7 @@ class Ui_mainWindow(object):
self.directoryButton.setObjectName(u"directoryButton")
self.directoryButton.setMinimumSize(QSize(0, 30))
icon1 = QIcon()
icon1.addFile(u":/Other/icons/folder_new.png", QSize(), QIcon.Normal, QIcon.Off)
icon1.addFile(u":/Other/icons/folder_new.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.directoryButton.setIcon(icon1)
self.gridLayout_4.addWidget(self.directoryButton, 0, 0, 1, 1)
@@ -191,7 +208,7 @@ class Ui_mainWindow(object):
self.fileButton.setObjectName(u"fileButton")
self.fileButton.setMinimumSize(QSize(0, 30))
icon2 = QIcon()
icon2.addFile(u":/Other/icons/document_new.png", QSize(), QIcon.Normal, QIcon.Off)
icon2.addFile(u":/Other/icons/document_new.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.fileButton.setIcon(icon2)
self.gridLayout_4.addWidget(self.fileButton, 0, 3, 1, 1)
@@ -215,7 +232,7 @@ class Ui_mainWindow(object):
font.setBold(True)
self.convertButton.setFont(font)
icon3 = QIcon()
icon3.addFile(u":/Other/icons/convert.png", QSize(), QIcon.Normal, QIcon.Off)
icon3.addFile(u":/Other/icons/convert.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.convertButton.setIcon(icon3)
self.gridLayout_4.addWidget(self.convertButton, 1, 2, 1, 1)
@@ -224,7 +241,7 @@ class Ui_mainWindow(object):
self.clearButton.setObjectName(u"clearButton")
self.clearButton.setMinimumSize(QSize(0, 30))
icon4 = QIcon()
icon4.addFile(u":/Other/icons/clear.png", QSize(), QIcon.Normal, QIcon.Off)
icon4.addFile(u":/Other/icons/clear.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.clearButton.setIcon(icon4)
self.gridLayout_4.addWidget(self.clearButton, 0, 2, 1, 1)
@@ -247,7 +264,7 @@ class Ui_mainWindow(object):
self.editorButton.setObjectName(u"editorButton")
self.editorButton.setMinimumSize(QSize(0, 30))
icon5 = QIcon()
icon5.addFile(u":/Other/icons/editor.png", QSize(), QIcon.Normal, QIcon.Off)
icon5.addFile(u":/Other/icons/editor.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.editorButton.setIcon(icon5)
self.horizontalLayout.addWidget(self.editorButton)
@@ -256,7 +273,7 @@ class Ui_mainWindow(object):
self.wikiButton.setObjectName(u"wikiButton")
self.wikiButton.setMinimumSize(QSize(0, 30))
icon6 = QIcon()
icon6.addFile(u":/Other/icons/wiki.png", QSize(), QIcon.Normal, QIcon.Off)
icon6.addFile(u":/Other/icons/wiki.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.wikiButton.setIcon(icon6)
self.horizontalLayout.addWidget(self.wikiButton)
@@ -266,7 +283,7 @@ class Ui_mainWindow(object):
self.jobList = QListWidget(self.centralWidget)
self.jobList.setObjectName(u"jobList")
self.jobList.setStyleSheet(u"QListWidget#jobList {background:#ffffff;background-image:url(:/Other/icons/list_background.png);background-position:center center;background-repeat:no-repeat;color:rgb(0,0,0);}")
self.jobList.setStyleSheet(u"")
self.jobList.setSelectionMode(QAbstractItemView.NoSelection)
self.jobList.setVerticalScrollMode(QAbstractItemView.ScrollPerPixel)
self.jobList.setHorizontalScrollMode(QAbstractItemView.ScrollPerPixel)
@@ -290,11 +307,11 @@ class Ui_mainWindow(object):
self.gridLayout_3.setContentsMargins(0, 0, 0, 0)
self.hLabel = QLabel(self.customWidget)
self.hLabel.setObjectName(u"hLabel")
sizePolicy1 = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred)
sizePolicy1.setHorizontalStretch(0)
sizePolicy1.setVerticalStretch(0)
sizePolicy1.setHeightForWidth(self.hLabel.sizePolicy().hasHeightForWidth())
self.hLabel.setSizePolicy(sizePolicy1)
sizePolicy2 = QSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Preferred)
sizePolicy2.setHorizontalStretch(0)
sizePolicy2.setVerticalStretch(0)
sizePolicy2.setHeightForWidth(self.hLabel.sizePolicy().hasHeightForWidth())
self.hLabel.setSizePolicy(sizePolicy2)
self.gridLayout_3.addWidget(self.hLabel, 0, 2, 1, 1)
@@ -306,8 +323,8 @@ class Ui_mainWindow(object):
self.wLabel = QLabel(self.customWidget)
self.wLabel.setObjectName(u"wLabel")
sizePolicy1.setHeightForWidth(self.wLabel.sizePolicy().hasHeightForWidth())
self.wLabel.setSizePolicy(sizePolicy1)
sizePolicy2.setHeightForWidth(self.wLabel.sizePolicy().hasHeightForWidth())
self.wLabel.setSizePolicy(sizePolicy2)
self.gridLayout_3.addWidget(self.wLabel, 0, 0, 1, 1)
@@ -359,66 +376,74 @@ class Ui_mainWindow(object):
def retranslateUi(self, mainWindow):
mainWindow.setWindowTitle(QCoreApplication.translate("mainWindow", u"Kindle Comic Converter", None))
#if QT_CONFIG(tooltip)
self.qualityBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - 4 panels<br/></span>Zoom each corner separately.</p><p style='white-space:pre'><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - 2 panels<br/></span>Zoom only the top and bottom of the page.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - 4 high-quality panels<br/></span>Zoom each corner separately. Try to increase the quality of magnification. Check wiki for more details.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.qualityBox.setText(QCoreApplication.translate("mainWindow", u"Panel View 4/2/HQ", None))
#if QT_CONFIG(tooltip)
self.deleteBox.setToolTip(QCoreApplication.translate("mainWindow", u"Delete input file(s) or directory. It's not recoverable!", None))
#endif // QT_CONFIG(tooltip)
self.deleteBox.setText(QCoreApplication.translate("mainWindow", u"Delete input", None))
#if QT_CONFIG(tooltip)
self.maximizeStrips.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - 1x4<br/></span>Keep format 1x4 panels strips.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - 2x2<br/></span>Turn 1x4 strips to 2x2 to maximize screen usage.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.maximizeStrips.setText(QCoreApplication.translate("mainWindow", u"1x4 to 2x2 strips", None))
#if QT_CONFIG(tooltip)
self.gammaBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Disable automatic gamma correction.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.gammaBox.setText(QCoreApplication.translate("mainWindow", u"Custom gamma", None))
#if QT_CONFIG(tooltip)
self.borderBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Autodetection<br/></span>The color of margins fill will be detected automatically.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - White<br/></span>Margins will be filled with white color.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Black<br/></span>Margins will be filled with black color.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.borderBox.setText(QCoreApplication.translate("mainWindow", u"W/B margins", None))
#if QT_CONFIG(tooltip)
self.webtoonBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Enable special parsing mode for Korean Webtoons.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.webtoonBox.setText(QCoreApplication.translate("mainWindow", u"Webtoon mode", None))
#if QT_CONFIG(tooltip)
self.upscaleBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Nothing<br/></span>Images smaller than device resolution will not be resized.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - Stretching<br/></span>Images smaller than device resolution will be resized. Aspect ratio will be not preserved.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Upscaling<br/></span>Images smaller than device resolution will be resized. Aspect ratio will be preserved.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.upscaleBox.setText(QCoreApplication.translate("mainWindow", u"Stretch/Upscale", None))
#if QT_CONFIG(tooltip)
self.mangaBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Enable right-to-left reading.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.mangaBox.setText(QCoreApplication.translate("mainWindow", u"Manga mode", None))
#if QT_CONFIG(tooltip)
self.rotateBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Split<br/></span>Double page spreads will be cut into two separate pages.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - Rotate and split<br/></span>Double page spreads will be displayed twice. First rotated and then split. </p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Rotate<br/></span>Double page spreads will be rotated.</p></body></html>", None))
self.outputSplit.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Automatic mode<br/></span>The output will be split automatically.</p><p style='white-space:pre'><span style=\" font-weight:600; text-decoration: underline;\">Checked - Volume mode<br/></span>Every subdirectory will be considered as a separate volume.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.rotateBox.setText(QCoreApplication.translate("mainWindow", u"Spread splitter", None))
self.outputSplit.setText(QCoreApplication.translate("mainWindow", u"Output split", None))
#if QT_CONFIG(tooltip)
self.croppingBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Disabled</span></p><p>Disabled</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - Margins<br/></span>Margins</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Margins + page numbers<br/></span>Margins +page numbers</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.croppingBox.setText(QCoreApplication.translate("mainWindow", u"Cropping mode", None))
#if QT_CONFIG(tooltip)
self.outputSplit.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Automatic mode<br/></span>The output will be split automatically.</p><p style='white-space:pre'><span style=\" font-weight:600; text-decoration: underline;\">Checked - Volume mode<br/></span>Every subdirectory will be considered as a separate volume.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.outputSplit.setText(QCoreApplication.translate("mainWindow", u"Output split", None))
#if QT_CONFIG(tooltip)
self.mozJpegBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - JPEG<br/></span>Use JPEG files</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - force PNG<br/></span>Create PNG files instead JPEG</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - mozJpeg<br/></span>10-20% smaller JPEG file, with the same image quality, but processing time multiplied by 2</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.mozJpegBox.setText(QCoreApplication.translate("mainWindow", u"JPEG/PNG/mozJpeg", None))
#if QT_CONFIG(tooltip)
self.upscaleBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Nothing<br/></span>Images smaller than device resolution will not be resized.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - Stretching<br/></span>Images smaller than device resolution will be resized. Aspect ratio will be not preserved.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Upscaling<br/></span>Images smaller than device resolution will be resized. Aspect ratio will be preserved.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.upscaleBox.setText(QCoreApplication.translate("mainWindow", u"Stretch/Upscale", None))
#if QT_CONFIG(tooltip)
self.colorBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Disable conversion to grayscale.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.colorBox.setText(QCoreApplication.translate("mainWindow", u"Color mode", None))
#if QT_CONFIG(tooltip)
self.rotateBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Split<br/></span>Double page spreads will be cut into two separate pages.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - Rotate and split<br/></span>Double page spreads will be displayed twice. First rotated and then split. </p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Rotate<br/></span>Double page spreads will be rotated.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.rotateBox.setText(QCoreApplication.translate("mainWindow", u"Spread splitter", None))
#if QT_CONFIG(tooltip)
self.gammaBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Disable automatic gamma correction.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.gammaBox.setText(QCoreApplication.translate("mainWindow", u"Custom gamma", None))
#if QT_CONFIG(tooltip)
self.spreadShiftBox.setToolTip(QCoreApplication.translate("mainWindow", u"Shift first page to opposite side in landscape for two page spread alignment", None))
#endif // QT_CONFIG(tooltip)
self.spreadShiftBox.setText(QCoreApplication.translate("mainWindow", u"Spread shift", None))
#if QT_CONFIG(tooltip)
self.webtoonBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Enable special parsing mode for Korean Webtoons.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.webtoonBox.setText(QCoreApplication.translate("mainWindow", u"Webtoon mode", None))
#if QT_CONFIG(tooltip)
self.maximizeStrips.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - 1x4<br/></span>Keep format 1x4 panels strips.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - 2x2<br/></span>Turn 1x4 strips to 2x2 to maximize screen usage.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.maximizeStrips.setText(QCoreApplication.translate("mainWindow", u"1x4 to 2x2 strips", None))
#if QT_CONFIG(tooltip)
self.borderBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Autodetection<br/></span>The color of margins fill will be detected automatically.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - White<br/></span>Margins will be filled with white color.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Black<br/></span>Margins will be filled with black color.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.borderBox.setText(QCoreApplication.translate("mainWindow", u"W/B margins", None))
#if QT_CONFIG(tooltip)
self.noRotateBox.setToolTip(QCoreApplication.translate("mainWindow", u"Do not rotate double page spreads in spread splitter option.", None))
#endif // QT_CONFIG(tooltip)
self.noRotateBox.setText(QCoreApplication.translate("mainWindow", u"No rotate", None))
#if QT_CONFIG(tooltip)
self.qualityBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - 4 panels<br/></span>Zoom each corner separately.</p><p style='white-space:pre'><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - 2 panels<br/></span>Zoom only the top and bottom of the page.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - 4 high-quality panels<br/></span>Zoom each corner separately. Try to increase the quality of magnification. Check wiki for more details.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.qualityBox.setText(QCoreApplication.translate("mainWindow", u"Panel View 4/2/HQ", None))
#if QT_CONFIG(tooltip)
self.disableProcessingBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><pre style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">Do not process any image, ignore profile and processing options</pre></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.disableProcessingBox.setText(QCoreApplication.translate("mainWindow", u"Disable processing", None))
#if QT_CONFIG(tooltip)
self.dedupeCoverBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p>Don't duplicate the first page as the cover. Useful for 2 page spread alignment.</p></body></html>", None))
self.authorEdit.setToolTip(QCoreApplication.translate("mainWindow", u"Default Author is KCC", None))
#endif // QT_CONFIG(tooltip)
self.dedupeCoverBox.setText(QCoreApplication.translate("mainWindow", u"De-dupe cover", None))
self.authorEdit.setPlaceholderText(QCoreApplication.translate("mainWindow", u"Default Author", None))
self.gammaLabel.setText(QCoreApplication.translate("mainWindow", u"Gamma: Auto", None))
self.croppingPowerLabel.setText(QCoreApplication.translate("mainWindow", u"Cropping power:", None))
#if QT_CONFIG(tooltip)
@@ -443,7 +468,7 @@ class Ui_mainWindow(object):
#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"Editor", None))
self.editorButton.setText(QCoreApplication.translate("mainWindow", u"Metadata Editor", None))
self.wikiButton.setText(QCoreApplication.translate("mainWindow", u"Wiki", 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))

View File

@@ -3,7 +3,7 @@
################################################################################
## Form generated from reading UI file 'MetaEditor.ui'
##
## Created by: Qt User Interface Compiler version 6.5.2
## Created by: Qt User Interface Compiler version 6.8.1
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
@@ -27,7 +27,7 @@ class Ui_editorDialog(object):
editorDialog.resize(400, 260)
editorDialog.setMinimumSize(QSize(400, 260))
icon = QIcon()
icon.addFile(u":/Icon/icons/comic2ebook.png", QSize(), QIcon.Normal, QIcon.Off)
icon.addFile(u":/Icon/icons/comic2ebook.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
editorDialog.setWindowIcon(icon)
self.verticalLayout = QVBoxLayout(editorDialog)
self.verticalLayout.setObjectName(u"verticalLayout")
@@ -117,7 +117,7 @@ class Ui_editorDialog(object):
self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
self.statusLabel = QLabel(self.optionWidget)
self.statusLabel.setObjectName(u"statusLabel")
sizePolicy = QSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding)
sizePolicy = QSizePolicy(QSizePolicy.Policy.MinimumExpanding, QSizePolicy.Policy.MinimumExpanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.statusLabel.sizePolicy().hasHeightForWidth())
@@ -129,7 +129,7 @@ class Ui_editorDialog(object):
self.okButton.setObjectName(u"okButton")
self.okButton.setMinimumSize(QSize(0, 30))
icon1 = QIcon()
icon1.addFile(u":/Other/icons/convert.png", QSize(), QIcon.Normal, QIcon.Off)
icon1.addFile(u":/Other/icons/convert.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.okButton.setIcon(icon1)
self.horizontalLayout.addWidget(self.okButton)
@@ -138,7 +138,7 @@ class Ui_editorDialog(object):
self.cancelButton.setObjectName(u"cancelButton")
self.cancelButton.setMinimumSize(QSize(0, 30))
icon2 = QIcon()
icon2.addFile(u":/Other/icons/clear.png", QSize(), QIcon.Normal, QIcon.Off)
icon2.addFile(u":/Other/icons/clear.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.cancelButton.setIcon(icon2)
self.horizontalLayout.addWidget(self.cancelButton)

View File

@@ -1,4 +1,4 @@
__version__ = '6.2.0'
__version__ = '7.1.2'
__license__ = 'ISC'
__copyright__ = '2012-2022, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>, darodi'
__docformat__ = 'restructuredtext en'

View File

@@ -298,22 +298,15 @@ def buildOPF(dstdir, title, filelist, cover=None):
"<meta name=\"zero-margin\" content=\"true\"/>\n",
"<meta name=\"ke-border-color\" content=\"#FFFFFF\"/>\n",
"<meta name=\"ke-border-width\" content=\"0\"/>\n",
"<meta property=\"rendition:spread\">landscape</meta>\n",
"<meta property=\"rendition:layout\">pre-paginated</meta>\n",
"<meta name=\"orientation-lock\" content=\"none\"/>\n"])
if options.kfx:
f.writelines(["<meta name=\"region-mag\" content=\"false\"/>\n"])
else:
f.writelines(["<meta name=\"region-mag\" content=\"true\"/>\n"])
elif options.supportSyntheticSpread:
f.writelines([
"<meta property=\"rendition:spread\">landscape</meta>\n",
"<meta property=\"rendition:layout\">pre-paginated</meta>\n"
])
else:
f.writelines(["<meta property=\"rendition:orientation\">portrait</meta>\n",
"<meta property=\"rendition:spread\">portrait</meta>\n",
"<meta property=\"rendition:layout\">pre-paginated</meta>\n"])
f.writelines([
"<meta property=\"rendition:spread\">landscape</meta>\n",
"<meta property=\"rendition:layout\">pre-paginated</meta>\n"
])
f.writelines(["</metadata>\n<manifest>\n<item id=\"ncx\" href=\"toc.ncx\" ",
"media-type=\"application/x-dtbncx+xml\"/>\n",
"<item id=\"nav\" href=\"nav.xhtml\" ",
@@ -358,55 +351,68 @@ def buildOPF(dstdir, title, filelist, cover=None):
else:
f.write("</manifest>\n<spine page-progression-direction=\"ltr\" toc=\"ncx\">\n")
pageside = "left"
if options.iskindle or options.supportSyntheticSpread:
for entry in reflist:
if options.righttoleft:
if entry.endswith("-b"):
f.write(
"<itemref idref=\"page_%s\" %s/>\n" % (entry,
pageSpreadProperty("right"))
)
pageside = "right"
elif entry.endswith("-c"):
f.write(
"<itemref idref=\"page_%s\" %s/>\n" % (entry,
pageSpreadProperty("left"))
)
pageside = "right"
else:
f.write(
"<itemref idref=\"page_%s\" %s/>\n" % (entry,
pageSpreadProperty(pageside))
)
if pageside == "right":
pageside = "left"
else:
pageside = "right"
if options.spreadshift:
if pageside == "right":
pageside = "left"
else:
pageside = "right"
for entry in reflist:
if options.righttoleft:
if entry.endswith("-a"):
f.write(
"<itemref idref=\"page_%s\" %s/>\n" % (entry,
pageSpreadProperty("center"))
)
pageside = "right"
elif entry.endswith("-b"):
f.write(
"<itemref idref=\"page_%s\" %s/>\n" % (entry,
pageSpreadProperty("right"))
)
pageside = "right"
elif entry.endswith("-c"):
f.write(
"<itemref idref=\"page_%s\" %s/>\n" % (entry,
pageSpreadProperty("left"))
)
pageside = "right"
else:
if entry.endswith("-b"):
f.write(
"<itemref idref=\"page_%s\" %s/>\n" % (entry,
pageSpreadProperty("left"))
)
pageside = "left"
elif entry.endswith("-c"):
f.write(
"<itemref idref=\"page_%s\" %s/>\n" % (entry,
pageSpreadProperty("right"))
)
pageside = "left"
else:
f.write(
"<itemref idref=\"page_%s\" %s/>\n" % (entry,
pageSpreadProperty(pageside))
)
f.write(
"<itemref idref=\"page_%s\" %s/>\n" % (entry,
pageSpreadProperty(pageside))
)
if pageside == "right":
pageside = "left"
else:
pageside = "right"
else:
if entry.endswith("-a"):
f.write(
"<itemref idref=\"page_%s\" %s/>\n" % (entry,
pageSpreadProperty("center"))
)
pageside = "left"
elif entry.endswith("-b"):
f.write(
"<itemref idref=\"page_%s\" %s/>\n" % (entry,
pageSpreadProperty("left"))
)
pageside = "left"
elif entry.endswith("-c"):
f.write(
"<itemref idref=\"page_%s\" %s/>\n" % (entry,
pageSpreadProperty("right"))
)
pageside = "left"
else:
f.write(
"<itemref idref=\"page_%s\" %s/>\n" % (entry,
pageSpreadProperty(pageside))
)
if pageside == "right":
pageside = "left"
else:
pageside = "right"
else:
for entry in reflist:
f.write("<itemref idref=\"page_" + entry + "\"/>\n")
f.write("</spine>\n</package>\n")
f.close()
os.mkdir(os.path.join(dstdir, 'META-INF'))
@@ -420,7 +426,7 @@ def buildOPF(dstdir, title, filelist, cover=None):
f.close()
def buildEPUB(path, chapternames, tomenumber):
def buildEPUB(path, chapternames, tomenumber, ischunked):
filelist = []
chapterlist = []
cover = None
@@ -509,15 +515,12 @@ def buildEPUB(path, chapternames, tomenumber):
'cover' + getImageFileName(afile)[1])
options.covers.append((image.Cover(os.path.join(dirpath, afile), cover, options,
tomenumber), options.uuid))
if options.dedupecover:
os.remove(os.path.join(dirpath, afile))
continue
filelist.append(buildHTML(dirpath, afile, os.path.join(dirpath, afile)))
if not chapter:
chapterlist.append((dirpath.replace('Images', 'Text'), filelist[-1][1]))
chapter = True
# Overwrite chapternames if tree is flat and ComicInfo.xml has bookmarks
if not chapternames and options.chapters:
if not chapternames and options.chapters and not ischunked:
chapterlist = []
global_diff = 0
@@ -666,7 +669,11 @@ def getOutputFilename(srcpath, wantedname, ext, tomenumber):
if srcpath[-1] == os.path.sep:
srcpath = srcpath[:-1]
if 'Ko' in options.profile and options.format == 'EPUB':
ext = '.kepub.epub'
if options.noKepub:
# Just use normal epub extension if no_kepub option is true
ext = '.epub'
else:
ext = '.kepub.epub'
if wantedname is not None:
if wantedname.endswith(ext):
filename = os.path.abspath(wantedname)
@@ -738,7 +745,7 @@ def getComicInfo(path, originalpath):
options.authors.sort()
else:
options.authors = ['KCC']
if xml.data['Bookmarks'] and options.batchsplit == 0:
if xml.data['Bookmarks']:
options.chapters = xml.data['Bookmarks']
if xml.data['Summary']:
options.summary = hescape(xml.data['Summary'])
@@ -957,8 +964,7 @@ def makeParser():
help="Full path to comic folder or file(s) to be processed.")
main_options.add_argument("-p", "--profile", action="store", dest="profile", default="KV",
help="Device profile (Available options: K1, K2, K34, K578, KDX, KPW, KPW5, KV, KO, "
"K11, KS, KoMT, KoG, KoGHD, KoA, KoAHD, KoAH2O, KoAO, KoN, KoC, KoCC, KoL, KoLC, KoF, KoS, KoE)"
help=f"Device profile (Available options: {', '.join(image.ProfileData.Profiles.keys())})"
" [Default=KV]")
main_options.add_argument("-m", "--manga-style", action="store_true", dest="righttoleft", default=False,
help="Manga style (right-to-left reading and splitting)")
@@ -981,11 +987,15 @@ def makeParser():
output_options.add_argument("-f", "--format", action="store", dest="format", default="Auto",
help="Output format (Available options: Auto, MOBI, EPUB, CBZ, KFX, MOBI+EPUB) "
"[Default=Auto]")
output_options.add_argument("--nokepub", action="store_true", dest="noKepub", default=False,
help="If format is EPUB, output file with '.epub' extension rather than '.kepub.epub'")
output_options.add_argument("-b", "--batchsplit", type=int, dest="batchsplit", default="0",
help="Split output into multiple files. 0: Don't split 1: Automatic mode "
"2: Consider every subdirectory as separate volume [Default=0]")
output_options.add_argument("--dedupecover", action="store_true", dest="dedupecover", default=False,
help="De-duplicate the cover as the first page in the book")
output_options.add_argument("--spreadshift", action="store_true", dest="spreadshift", default=False,
help="Shift first page to opposite side in landscape for spread alignment")
output_options.add_argument("--norotate", action="store_true", dest="norotate", default=False,
help="Do not rotate double page spreads in spread splitter option.")
processing_options.add_argument("-n", "--noprocessing", action="store_true", dest="noprocessing", default=False,
help="Do not modify image and ignore any profil or processing option")
@@ -1044,23 +1054,17 @@ def checkOptions(options):
options.keep_epub = True
options.format = 'MOBI'
options.kfx = False
options.supportSyntheticSpread = False
if options.format == 'Auto':
if options.profile in ['K1', 'K2', 'K34', 'K578', 'KPW', 'KPW5', 'KV', 'KO', 'K11', 'KS']:
options.format = 'MOBI'
elif options.profile in ['OTHER', 'KoMT', 'KoG', 'KoGHD', 'KoA', 'KoAHD', 'KoAH2O', 'KoAO',
'KoN', 'KoC', 'KoCC', 'KoL', 'KoLC', 'KoF', 'KoS', 'KoE']:
options.format = 'EPUB'
elif options.profile in ['KDX']:
if options.profile in ['KDX']:
options.format = 'CBZ'
if options.profile in ['K1', 'K2', 'K34', 'K578', 'KPW', 'KPW5', 'KV', 'KO', 'K11', 'KS']:
elif options.profile in image.ProfileData.ProfilesKindle.keys():
options.format = 'MOBI'
else:
options.format = 'EPUB'
if options.profile in image.ProfileData.ProfilesKindle.keys():
options.iskindle = True
elif options.profile in ['OTHER', 'KoMT', 'KoG', 'KoGHD', 'KoA', 'KoAHD', 'KoAH2O', 'KoAO', 'KoN', 'KoC', 'KoCC', 'KoL', 'KoLC', 'KoF', 'KoS', 'KoE']:
else:
options.isKobo = True
# Other Kobo devices probably support synthetic spreads as well, but
# they haven't been tested.
if options.profile in ['KoF']:
options.supportSyntheticSpread = True
if options.white_borders:
options.bordersColor = 'white'
if options.black_borders:
@@ -1202,10 +1206,11 @@ def makeBook(source, qtgui=None):
makeZIP(tome + '_comic', os.path.join(tome, "OEBPS", "Images"))
else:
print("Creating EPUB file...")
buildEPUB(tome, chapterNames, tomeNumber)
if len(tomes) > 1:
buildEPUB(tome, chapterNames, tomeNumber, True)
filepath.append(getOutputFilename(source, options.output, '.epub', ' ' + str(tomeNumber)))
else:
buildEPUB(tome, chapterNames, tomeNumber, False)
filepath.append(getOutputFilename(source, options.output, '.epub', ''))
makeZIP(tome + '_comic', tome, True)
copyfile(tome + '_comic.zip', filepath[-1])
@@ -1228,7 +1233,7 @@ def makeBook(source, qtgui=None):
print('Error: KindleGen failed to create MOBI!')
print(errors)
return filepath
k = kindle.Kindle()
k = kindle.Kindle(options.profile)
if k.path and k.coverSupport:
print("Kindle detected. Uploading covers...")
for i in filepath:
@@ -1250,12 +1255,13 @@ def makeBook(source, qtgui=None):
def makeMOBIFix(item, uuid):
is_pdoc = options.profile in image.ProfileData.ProfilesKindlePDOC.keys()
if not options.keep_epub:
os.remove(item)
mobiPath = item.replace('.epub', '.mobi')
move(mobiPath, mobiPath + '_toclean')
try:
dualmetafix.DualMobiMetaFix(mobiPath + '_toclean', mobiPath, bytes(uuid, 'UTF-8'))
dualmetafix.DualMobiMetaFix(mobiPath + '_toclean', mobiPath, bytes(uuid, 'UTF-8'), is_pdoc)
return [True]
except Exception as err:
return [False, format(err)]

View File

@@ -42,7 +42,7 @@ class ComicArchive:
['7z', 'l', '-y', '-p1', self.filepath],
]
if distro.id() == 'fedora':
if distro.id() == 'fedora' or distro.like() == 'fedora':
extraction_commands.append(
['unrar', 'l', '-y', '-p1', self.filepath],
)
@@ -76,7 +76,7 @@ class ComicArchive:
['unar', self.filepath, '-f', '-o', targetdir]
)
if distro.id() == 'fedora':
if distro.id() == 'fedora' or distro.like() == 'fedora':
extraction_commands.append(
['unrar', 'x', '-y', '-x__MACOSX', '-x.DS_Store', '-xthumbs.db', '-xThumbs.db', self.filepath, targetdir]
)

View File

@@ -136,7 +136,11 @@ def del_exth(rec0, exth_num):
class DualMobiMetaFix:
def __init__(self, infile, outfile, asin):
def __init__(self, infile, outfile, asin, is_pdoc):
cdetype = b'EBOK'
if is_pdoc:
cdetype = b'PDOC'
shutil.copyfile(infile, outfile)
f = open(outfile, "r+b")
self.datain = mmap.mmap(f.fileno(), 0)
@@ -147,7 +151,7 @@ class DualMobiMetaFix:
rec0 = self.datain_rec0
rec0 = del_exth(rec0, 501)
rec0 = del_exth(rec0, 113)
rec0 = add_exth(rec0, 501, b'EBOK')
rec0 = add_exth(rec0, 501, cdetype)
rec0 = add_exth(rec0, 113, asin)
replacesection(self.datain, 0, rec0)
@@ -174,7 +178,7 @@ class DualMobiMetaFix:
rec0 = self.datain_kfrec0
rec0 = del_exth(rec0, 501)
rec0 = del_exth(rec0, 113)
rec0 = add_exth(rec0, 501, b'EBOK')
rec0 = add_exth(rec0, 501, cdetype)
rec0 = add_exth(rec0, 113, asin)
replacesection(self.datain, datain_kf8, rec0)

View File

@@ -23,6 +23,7 @@ import os
import mozjpeg_lossless_optimization
from PIL import Image, ImageOps, ImageStat, ImageChops, ImageFilter
from .shared import md5Checksum
from .page_number_crop_alg import get_bbox_crop_margin_page_number, get_bbox_crop_margin
AUTO_CROP_THRESHOLD = 0.015
@@ -78,18 +79,29 @@ class ProfileData:
PalleteNull = [
]
Profiles = {
ProfilesKindleEBOK = {
'K1': ("Kindle 1", (600, 670), Palette4, 1.8),
'K11': ("Kindle 11", (1072, 1448), Palette16, 1.8),
'K2': ("Kindle 2", (600, 670), Palette15, 1.8),
'KDX': ("Kindle DX/DXG", (824, 1000), Palette16, 1.8),
'K34': ("Kindle Keyboard/Touch", (600, 800), Palette16, 1.8),
'K578': ("Kindle", (600, 800), Palette16, 1.8),
'KDX': ("Kindle DX/DXG", (824, 1000), Palette16, 1.8),
'KPW': ("Kindle Paperwhite 1/2", (758, 1024), Palette16, 1.8),
'KV': ("Kindle Paperwhite 3/4/Voyage/Oasis", (1072, 1448), Palette16, 1.8),
}
ProfilesKindlePDOC = {
'KO': ("Kindle Oasis 2/3/Paperwhite 12/Colorsoft 12", (1264, 1680), Palette16, 1.8),
'K11': ("Kindle 11", (1072, 1448), Palette16, 1.8),
'KPW5': ("Kindle Paperwhite 5/Signature Edition", (1236, 1648), Palette16, 1.8),
'KO': ("Kindle Oasis 2/3", (1264, 1680), Palette16, 1.8),
'KS': ("Kindle Scribe", (1860, 2480), Palette16, 1.8),
}
ProfilesKindle = {
**ProfilesKindleEBOK,
**ProfilesKindlePDOC
}
ProfilesKobo = {
'KoMT': ("Kobo Mini/Touch", (600, 800), Palette16, 1.8),
'KoG': ("Kobo Glo", (768, 1024), Palette16, 1.8),
'KoGHD': ("Kobo Glo HD", (1072, 1448), Palette16, 1.8),
@@ -105,6 +117,18 @@ class ProfileData:
'KoF': ("Kobo Forma", (1440, 1920), Palette16, 1.8),
'KoS': ("Kobo Sage", (1440, 1920), Palette16, 1.8),
'KoE': ("Kobo Elipsa", (1404, 1872), Palette16, 1.8),
}
ProfilesRemarkable = {
'Rmk1': ("reMarkable 1", (1404, 1872), Palette16, 1.8),
'Rmk2': ("reMarkable 2", (1404, 1872), Palette16, 1.8),
'RmkPP': ("reMarkable Paper Pro", (1620, 2160), Palette16, 1.8),
}
Profiles = {
**ProfilesKindle,
**ProfilesKobo,
**ProfilesRemarkable,
'OTHER': ("Other", (0, 0), Palette16, 1.8),
}
@@ -151,7 +175,10 @@ class ComicPageParser:
self.payload.append(['N', self.source, new_image, self.color, self.fill])
elif (width > height) != (dstwidth > dstheight) and width <= dstheight and height <= dstwidth \
and not self.opt.webtoon and self.opt.splitter == 1:
self.payload.append(['R', self.source, self.image.rotate(90, Image.Resampling.BICUBIC, True), self.color, self.fill])
spread = self.image
if not self.opt.norotate:
spread = spread.rotate(90, Image.Resampling.BICUBIC, True)
self.payload.append(['R', self.source, spread, self.color, self.fill])
elif (width > height) != (dstwidth > dstheight) and not self.opt.webtoon:
if self.opt.splitter != 1:
if width > height:
@@ -169,7 +196,10 @@ class ComicPageParser:
self.payload.append(['S1', self.source, pageone, self.color, self.fill])
self.payload.append(['S2', self.source, pagetwo, self.color, self.fill])
if self.opt.splitter > 0:
self.payload.append(['R', self.source, self.image.rotate(90, Image.Resampling.BICUBIC, True),
spread = self.image
if not self.opt.norotate:
spread = spread.rotate(90, Image.Resampling.BICUBIC, True)
self.payload.append(['R', self.source, spread,
self.color, self.fill])
else:
self.payload.append(['N', self.source, self.image, self.color, self.fill])
@@ -342,20 +372,6 @@ class ComicPage:
else:
return Image.Resampling.LANCZOS
def getBoundingBox(self, tmptmg):
min_margin = [int(0.005 * i + 0.5) for i in tmptmg.size]
max_margin = [int(0.1 * i + 0.5) for i in tmptmg.size]
bbox = tmptmg.getbbox()
bbox = (
max(0, min(max_margin[0], bbox[0] - min_margin[0])),
max(0, min(max_margin[1], bbox[1] - min_margin[1])),
min(tmptmg.size[0],
max(tmptmg.size[0] - max_margin[0], bbox[2] + min_margin[0])),
min(tmptmg.size[1],
max(tmptmg.size[1] - max_margin[1], bbox[3] + min_margin[1])),
)
return bbox
def maybeCrop(self, box, minimum):
box_area = (box[2] - box[0]) * (box[3] - box[1])
image_area = self.image.size[0] * self.image.size[1]
@@ -363,26 +379,16 @@ class ComicPage:
self.image = self.image.crop(box)
def cropPageNumber(self, power, minimum):
if self.fill != 'white':
tmptmg = self.image.convert(mode='L')
else:
tmptmg = ImageOps.invert(self.image.convert(mode='L'))
tmptmg = tmptmg.point(lambda x: x and 255)
tmptmg = tmptmg.filter(ImageFilter.MinFilter(size=3))
tmptmg = tmptmg.filter(ImageFilter.GaussianBlur(radius=5))
tmptmg = tmptmg.point(lambda x: (x >= 16 * power) and x)
if tmptmg.getbbox():
self.maybeCrop(tmptmg.getbbox(), minimum)
bbox = get_bbox_crop_margin_page_number(self.image, power, self.fill)
if bbox:
self.maybeCrop(bbox, minimum)
def cropMargin(self, power, minimum):
if self.fill != 'white':
tmptmg = self.image.convert(mode='L')
else:
tmptmg = ImageOps.invert(self.image.convert(mode='L'))
tmptmg = tmptmg.filter(ImageFilter.GaussianBlur(radius=3))
tmptmg = tmptmg.point(lambda x: (x >= 16 * power) and x)
if tmptmg.getbbox():
self.maybeCrop(self.getBoundingBox(tmptmg), minimum)
bbox = get_bbox_crop_margin(self.image, power, self.fill)
if bbox:
self.maybeCrop(bbox, minimum)
class Cover:

View File

@@ -19,9 +19,12 @@
import os.path
import psutil
from . import image
class Kindle:
def __init__(self):
def __init__(self, profile):
self.profile = profile
self.path = self.findDevice()
if self.path:
self.coverSupport = self.checkThumbnails()
@@ -29,6 +32,8 @@ class Kindle:
self.coverSupport = False
def findDevice(self):
if self.profile in image.ProfileData.ProfilesKindlePDOC.keys():
return False
for drive in reversed(psutil.disk_partitions(False)):
if (drive[2] == 'FAT32' and drive[3] == 'rw,removable') or \
(drive[2] in ('vfat', 'msdos', 'FAT', 'apfs') and 'rw' in drive[3]):

View File

@@ -0,0 +1,215 @@
from PIL import ImageOps, ImageFilter
import numpy as np
'''
Some assupmptions on the page number sizes
We assume that the size of the number (including all digits) is between
'min_shape_size_tolerated_size' and 'max_shape_size_tolerated_size' relative to the image size.
We assume the distance between the digit is no more than 'max_dist_size' (x,y), and no more than 3 digits.
'''
max_shape_size_tolerated_size = (0.015*3, 0.02) # percent
min_shape_size_tolerated_size = (0.003, 0.006) # percent
window_h_size = max_shape_size_tolerated_size[1]*1.25 # percent
max_dist_size = (0.01, 0.002) # percent
'''
E-reader screen real-estate is an important resource.
More available screensize means more details can be better seen, especially text.
Text is one of the most important elements that need to be clearly readable on e-readers,
which mostly are smaller devices where the need to zoom is unwanted.
By cropping the page number on the bottom of the page, 2%-5% of the page height can be regained
that allows us to upscale the image even more.
- Most of the times the screen height is the limiting factor in upscaling, rather than its width.
Parameters:
img (PIL image): A PIL image.
power (float): The power to 'chop' through pixels matching the background. Values in range[0,3].
background_color (string): 'white' for white background, anything else for black.
Returns:
bbox (4-tuple, left|top|right|bot): The tightest bounding box calculated after trying to remove the bottom page number. Returns None if couldnt find anything satisfactory
'''
def get_bbox_crop_margin_page_number(img, power=1, background_color='white'):
if img.mode != 'L':
img = ImageOps.grayscale(img)
if background_color != 'white':
img = ImageOps.invert(img)
'''
Autocontrast: due to some threshold values, it's important that the blacks will be blacks and white will be whites.
Box/MeanFilter: Allows us to reduce noise like bad a page scan or compression artifacts.
Note: MedianFilter works better in my experience, but takes 2x-3x longer to perform.
'''
img = ImageOps.autocontrast(img, 1).filter(ImageFilter.BoxBlur(1))
'''
The 'power' parameters determines the threshold. The higher the power, the more "force" it can crop through black pixels (in case of white background)
and the lower the power, more sensitive to black pixels.
'''
threshold = threshold_from_power(power)
bw_img = img.point(lambda p: 255 if p <= threshold else 0)
bw_bbox = bw_img.getbbox()
if not bw_bbox: # bbox cannot be found in case that the entire resulted image is black.
return None
left, top_y_pos, right, bot_y_pos = bw_bbox
'''
We inspect the lower bottom part of the image where we suspect might be a page number.
We assume that page number consist of 1 to 3 digits and the total min and max size of the number
is between 'min_shape_size_tolerated_size' and 'max_shape_size_tolerated_size'.
'''
window_h = int(img.size[1] * window_h_size)
img_part = img.crop((left,bot_y_pos-window_h, right, bot_y_pos))
'''
We detect related-pixels by proximity, with max distance defined in 'max_dist_size'.
Related pixels (in x axis) for each image-row are then merged to boxes with adjacent rows (in y axis)
to form bounding boxes of the detected objects (which one of them could be the page number).
'''
img_part_mat = np.array(img_part)
window_groups = []
for i in range(img_part.size[1]):
row_groups = [(g[0], g[1], i, i) for g in group_pixels(img_part_mat[i], img.size[0]*max_dist_size[0], threshold)]
window_groups.extend(row_groups)
window_groups = np.array(window_groups)
boxes = merge_boxes(window_groups, (img.size[0]*max_dist_size[0], img.size[1]*max_dist_size[1]))
'''
We assume that the lowest part of the image that has black pixels on is the page number.
In case that there are more than one detected object in the loewst part, we assume that one of them is probably
manga-content and shouldn't be cropped.
'''
# filter all small objects
boxes = list(filter(lambda box: box[1]-box[0] >= img.size[0]*min_shape_size_tolerated_size[0]
and box[3]-box[2] >= img.size[1]*min_shape_size_tolerated_size[1], boxes))
lowest_boxes = list(filter(lambda box: box[3] == window_h-1, boxes))
min_y_of_lowest_boxes = 0
if len(lowest_boxes) > 0:
min_y_of_lowest_boxes = np.min(np.array(lowest_boxes)[:,2])
boxes_in_same_y_range = list(filter(lambda box: box[3] >= min_y_of_lowest_boxes, boxes))
max_shape_size_tolerated = (img.size[0] * max_shape_size_tolerated_size[0],
max(img.size[1] *max_shape_size_tolerated_size[1], 3))
should_force_crop = (
len(boxes_in_same_y_range) == 1
and (boxes_in_same_y_range[0][1] - boxes_in_same_y_range[0][0] <= max_shape_size_tolerated[0])
and (boxes_in_same_y_range[0][3] - boxes_in_same_y_range[0][2] <= max_shape_size_tolerated[1])
)
cropped_bbox = (0, 0, img.size[0], img.size[1])
if should_force_crop:
cropped_bbox = (0, 0, img.size[0], bot_y_pos-(window_h-boxes_in_same_y_range[0][2]+1))
cropped_bbox = bw_img.crop(cropped_bbox).getbbox()
return cropped_bbox
'''
Parameters:
img (PIL image): A PIL image.
power (float): The power to 'chop' through pixels matching the background. Values in range[0,3].
background_color (string): 'white' for white background, anything else for black.
Returns:
bbox (4-tuple, left|top|right|bot): The tightest bounding box calculated after trying to remove the bottom page number. Returns None if couldnt find anything satisfactory
'''
def get_bbox_crop_margin(img, power=1, background_color='white'):
if img.mode != 'L':
img = ImageOps.grayscale(img)
if background_color != 'white':
img = ImageOps.invert(img)
'''
Autocontrast: due to some threshold values, it's important that the blacks will be blacks and white will be whites.
Box/MeanFilter: Allows us to reduce noise like bad a page scan or compression artifacts.
Note: MedianFilter works better in my experience, but takes 2x-3x longer to perform.
'''
img = ImageOps.autocontrast(img, 1).filter(ImageFilter.BoxBlur(1))
'''
The 'power' parameters determines the threshold. The higher the power, the more "force" it can crop through black pixels (in case of white background)
and the lower the power, more sensitive to black pixels.
'''
threshold = threshold_from_power(power)
bw_img = img.point(lambda p: 255 if p <= threshold else 0)
return bw_img.getbbox()
'''
Groups close pixels together (x axis)
'''
def group_pixels(row, max_dist_tolerated, threshold):
groups = []
idx = np.where(row <= threshold)[0]
group_start = -1
group_end = 0
for i in range(len(idx)):
dist = idx[i] - group_end
if group_start == -1:
group_start = idx[i]
group_end = idx[i]
elif dist <= max_dist_tolerated:
group_end = idx[i]
else:
groups.append((group_start, group_end))
group_start = -1
group_end = -1
if group_start != -1:
groups.append((group_start, group_end))
return groups
def box_intersect(box1, box2, max_dist):
return not (box2[0]-max_dist[0] > box1[1]
or box2[1]+max_dist[0] < box1[0]
or box2[2]-max_dist[1] > box1[3]
or box2[3]+max_dist[1] < box1[2])
'''
Merge close bounding boxes (left,right, top,bot) (x axis) with distance threshold defined in
'max_dist_tolerated'. Boxes with less 'max_dist_tolerated' distance (Chebyshev distance).
'''
def merge_boxes(boxes, max_dist_tolerated):
j = 0
while j < len(boxes)-1:
g1 = boxes[j]
intersecting_boxes = []
other_boxes = []
for i in range(j+1,len(boxes)):
g2 = boxes[i]
if box_intersect(g1,g2, max_dist_tolerated):
intersecting_boxes.append(g2)
else:
other_boxes.append(g2)
if len(intersecting_boxes) > 0:
intersecting_boxes = np.array([g1, *intersecting_boxes])
merged_box = np.array([
np.min(intersecting_boxes[:,0]),
np.max(intersecting_boxes[:,1]),
np.min(intersecting_boxes[:,2]),
np.max(intersecting_boxes[:,3])
])
other_boxes.append(merged_box)
boxes = np.concatenate([boxes[:j], other_boxes])
j = 0
else:
j += 1
return boxes
def threshold_from_power(power):
return 240-(power*64)

View File

@@ -6,5 +6,6 @@ python-slugify>=1.2.1
raven>=6.0.0
packaging>=23.2
mozjpeg-lossless-optimization>=1.1.2
natsort[fast]>=8.4.0
natsort>=8.4.0
distro>=1.8.0
numpy>=1.22.4,<2.0.0

View File

@@ -36,16 +36,16 @@ class BuildBinaryCommand(setuptools.Command):
def run(self):
VERSION = __version__
if sys.platform == 'darwin':
os.system('pyinstaller -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
os.system(f'appdmg kcc.json dist/kcc_macos_{platform.processor()}_{VERSION}.dmg')
sys.exit(0)
elif sys.platform == 'win32':
os.system('pyinstaller -y -F -i icons\\comic2ebook.ico -n KCC_' + VERSION + ' -w --noupx kcc.py')
os.system('pyinstaller --hidden-import=_cffi_backend -y -F -i icons\\comic2ebook.ico -n KCC_' + VERSION + ' -w --noupx kcc.py')
sys.exit(0)
elif sys.platform == 'linux':
os.system(
'pyinstaller --hidden-import=queue -y -F -i icons/comic2ebook.ico -n kcc_linux_' + VERSION + ' kcc.py')
'pyinstaller --hidden-import=_cffi_backend --hidden-import=queue -y -F -i icons/comic2ebook.ico -n kcc_linux_' + VERSION + ' kcc.py')
sys.exit(0)
else:
sys.exit(0)
@@ -81,8 +81,9 @@ setuptools.setup(
'raven>=6.0.0',
'requests>=2.31.0',
'mozjpeg-lossless-optimization>=1.1.2',
'natsort[fast]>=8.4.0',
'natsort>=8.4.0',
'distro',
'numpy>=1.22.4,<2.0.0'
],
classifiers=[],
zip_safe=False,