1
0
mirror of https://github.com/ciromattia/kcc synced 2026-04-17 22:48:53 +00:00

Compare commits

..

5 Commits

Author SHA1 Message Date
Alex Xu
2c9e7cfced --hidden-import=_cffi_backend (#754)
* --hidden-import=_cffi_backend

* Update setup.py
2024-10-15 10:09:57 -07:00
Alex Xu
9c71dc85ee build windows with docker 2024-10-14 23:11:52 -07:00
Alex Xu
57a0450026 Revert "remove GUI windows docker"
This reverts commit 4fc5cc9dfb.
2024-10-14 22:56:34 -07:00
Alex Xu
885ac227de bump windows to Python 3.12 2024-10-14 22:44:55 -07:00
Alex Xu
d3e4e859b1 Revert "Add de-dupe cover option for landscape alignment (#561)"
This reverts commit c35dd137ea.
2024-10-14 22:33:33 -07:00
31 changed files with 559 additions and 1033 deletions

View File

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

View File

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

View File

@@ -25,6 +25,11 @@ jobs:
# - name: build binary # - name: build binary
# run: | # run: |
# pyi-makespec -F -i icons\\comic2ebook.ico -n KCC_test -w --noupx kcc.py # pyi-makespec -F -i icons\\comic2ebook.ico -n KCC_test -w --noupx kcc.py
- name: Package Application
uses: JackMcKew/pyinstaller-action-windows@main
with:
path: .
spec: ./kcc.spec
- name: Package Application - name: Package Application
uses: JackMcKew/pyinstaller-action-windows@main uses: JackMcKew/pyinstaller-action-windows@main
with: with:
@@ -38,6 +43,7 @@ jobs:
- name: rename binaries - name: rename binaries
run: | run: |
version_built=$(cat kindlecomicconverter/__init__.py | grep version | awk '{print $3}' | sed "s/[^.0-9b]//g") version_built=$(cat kindlecomicconverter/__init__.py | grep version | awk '{print $3}' | sed "s/[^.0-9b]//g")
mv dist/windows/kcc.exe dist/windows/KCC_${version_built}.exe
mv dist/windows/kcc-c2e.exe dist/windows/KCC_c2e_${version_built}.exe mv dist/windows/kcc-c2e.exe dist/windows/KCC_c2e_${version_built}.exe
mv dist/windows/kcc-c2p.exe dist/windows/KCC_c2p_${version_built}.exe mv dist/windows/kcc-c2p.exe dist/windows/KCC_c2p_${version_built}.exe
- name: upload build - name: upload build

View File

@@ -29,7 +29,7 @@ jobs:
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v5 uses: actions/setup-python@v5
with: with:
python-version: 3.11 python-version: 3.12
cache: 'pip' cache: 'pip'
- name: Install dependencies - name: Install dependencies
env: env:
@@ -55,4 +55,4 @@ jobs:
files: | files: |
CHANGELOG.md CHANGELOG.md
LICENSE.txt LICENSE.txt
dist/*.exe dist/*.exe

View File

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

View File

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

View File

@@ -52,6 +52,8 @@ 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 For flatpak, Docker, and AppImage versions, refer to the wiki: https://github.com/ciromattia/kcc/wiki/Installation
## FAQ ## 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) - [Windows 7 support](https://github.com/ciromattia/kcc/issues/678)
- [Combine files/chapters](https://github.com/ciromattia/kcc/issues/612#issuecomment-2117985011) - [Combine files/chapters](https://github.com/ciromattia/kcc/issues/612#issuecomment-2117985011)
- [Flatpak mobi conversion stuck](https://github.com/ciromattia/kcc/wiki/Installation#linux) - [Flatpak mobi conversion stuck](https://github.com/ciromattia/kcc/wiki/Installation#linux)
@@ -68,11 +70,9 @@ If you have issues detecting it, get stuck on the MOBI conversion step, or use L
### 7-Zip ### 7-Zip
This is only required for certain files and advanced features. This is no longer required as of KCC 6.1.
KCC will ask you to install if needed. If you still need it, refer to the wiki: https://github.com/ciromattia/kcc/wiki/Installation#7-zip
Refer to the wiki to install: https://github.com/ciromattia/kcc/wiki/Installation#7-zip
## INPUT FORMATS ## INPUT FORMATS
**KCC** can understand and convert, at the moment, the following input types: **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), 'KPW': ("Kindle Paperwhite 1/2", (758, 1024), Palette16, 1.8),
'KV': ("Kindle Paperwhite 3/4/Voyage/Oasis", (1072, 1448), 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), 'KPW5': ("Kindle Paperwhite 5/Signature Edition", (1236, 1648), Palette16, 1.8),
'KO': ("Kindle Oasis 2/3/Paperwhite 12/Colorsoft 12", (1264, 1680), Palette16, 1.8), 'KO': ("Kindle Oasis 2/3", (1264, 1680), Palette16, 1.8),
'KS': ("Kindle Scribe", (1860, 2480), Palette16, 1.8), 'KS': ("Kindle Scribe", (1860, 2480), Palette16, 1.8),
'KoMT': ("Kobo Mini/Touch", (600, 800), Palette16, 1.8), 'KoMT': ("Kobo Mini/Touch", (600, 800), Palette16, 1.8),
'KoG': ("Kobo Glo", (768, 1024), Palette16, 1.8), 'KoG': ("Kobo Glo", (768, 1024), Palette16, 1.8),
@@ -124,9 +124,6 @@ sudo apt-get install python3 p7zip-full python3-pil python3-psutil python3-slugi
'KoF': ("Kobo Forma", (1440, 1920), Palette16, 1.8), 'KoF': ("Kobo Forma", (1440, 1920), Palette16, 1.8),
'KoS': ("Kobo Sage", (1440, 1920), Palette16, 1.8), 'KoS': ("Kobo Sage", (1440, 1920), Palette16, 1.8),
'KoE': ("Kobo Elipsa", (1404, 1872), 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), 'OTHER': ("Other", (0, 0), Palette16, 1.8),
``` ```
@@ -140,8 +137,7 @@ MANDATORY:
MAIN: MAIN:
-p PROFILE, --profile PROFILE -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, KoCC, KoL, KoLC, KoF, KoS, KoE) Device profile (Available options: K1, K2, K34, K578, KDX, KPW, KPW5, KV, KO, K11, KS, KoMT, KoG, KoGHD, KoA, KoAHD, KoAH2O, KoAO, KoN, KoC, KoL, KoF, KoS, KoE) [Default=KV]
[Default=KV]
-m, --manga-style Manga style (right-to-left reading and splitting) -m, --manga-style Manga style (right-to-left reading and splitting)
-q, --hq Try to increase the quality of magnification -q, --hq Try to increase the quality of magnification
-2, --two-panel Display two not four panels in Panel View mode -2, --two-panel Display two not four panels in Panel View mode
@@ -176,15 +172,10 @@ OUTPUT SETTINGS:
Output generated file to specified directory or file Output generated file to specified directory or file
-t TITLE, --title TITLE -t TITLE, --title TITLE
Comic title [Default=filename or directory name] Comic title [Default=filename or directory name]
-a AUTHOR, --author AUTHOR
Author name [Default=KCC]
-f FORMAT, --format FORMAT -f FORMAT, --format FORMAT
Output format (Available options: Auto, MOBI, EPUB, CBZ, KFX, MOBI+EPUB) [Default=Auto] 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 -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] 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: CUSTOM PROFILE:
--customwidth CUSTOMWIDTH --customwidth CUSTOMWIDTH
@@ -229,8 +220,6 @@ If you want to edit the code, a good code editor is [VS Code](https://code.visua
If you want to edit the `.ui` files, use [Qt Creator](https://www.qt.io/download-qt-installer-oss), included in **Qt for desktop development**. If you want to edit the `.ui` files, use [Qt Creator](https://www.qt.io/download-qt-installer-oss), included in **Qt for desktop development**.
Then use the `gen_ui_files` scripts to autogenerate the python UI. Then use the `gen_ui_files` scripts to autogenerate the python UI.
An example PR adding a new checkbox is here: https://github.com/ciromattia/kcc/pull/785
### Windows install from source ### Windows install from source

View File

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

View File

@@ -1,3 +1,3 @@
pyside6-uic gui/KCC.ui --from-imports > kindlecomicconverter/KCC_ui.py pyside6-uic gui/KCC.ui > kindlecomicconverter/KCC_ui.py
pyside6-uic gui/MetaEditor.ui --from-imports > kindlecomicconverter/KCC_ui_editor.py pyside6-uic gui/MetaEditor.ui > kindlecomicconverter/KCC_ui_editor.py
pyside6-rcc gui/KCC.qrc > kindlecomicconverter/KCC_rc.py pyside6-rcc gui/KCC.qrc > kindlecomicconverter/KCC_rc.py

View File

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

View File

@@ -6,8 +6,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>482</width> <width>450</width>
<height>448</height> <height>400</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@@ -37,63 +37,7 @@
<property name="bottomMargin"> <property name="bottomMargin">
<number>0</number> <number>0</number>
</property> </property>
<item row="5" column="1"> <item row="1" column="1">
<widget class="QCheckBox" name="deleteBox">
<property name="toolTip">
<string>Delete input file(s) or directory. It's not recoverable!</string>
</property>
<property name="text">
<string>Delete input</string>
</property>
</widget>
</item>
<item row="1" 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>
</property>
<property name="text">
<string>Manga mode</string>
</property>
</widget>
</item>
<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 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="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>
</property>
<property name="text">
<string>Cropping mode</string>
</property>
<property name="tristate">
<bool>true</bool>
</property>
</widget>
</item>
<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>
</property>
<property name="text">
<string>JPEG/PNG/mozJpeg</string>
</property>
<property name="tristate">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="upscaleBox"> <widget class="QCheckBox" name="upscaleBox">
<property name="toolTip"> <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> <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>
@@ -106,17 +50,7 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="2"> <item row="0" column="1">
<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>
</property>
<property name="text">
<string>Color mode</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="rotateBox"> <widget class="QCheckBox" name="rotateBox">
<property name="toolTip"> <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&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>
@@ -129,27 +63,17 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="2"> <item row="2" column="1">
<widget class="QCheckBox" name="gammaBox"> <widget class="QCheckBox" name="outputSplit">
<property name="toolTip"> <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> <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>
<property name="text"> <property name="text">
<string>Custom gamma</string> <string>Output split</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="5" column="0"> <item row="1" 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"> <widget class="QCheckBox" name="webtoonBox">
<property name="toolTip"> <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> <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>
@@ -159,17 +83,27 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="4" column="1"> <item row="2" column="2">
<widget class="QCheckBox" name="maximizeStrips"> <widget class="QCheckBox" name="colorBox">
<property name="toolTip"> <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> <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>
</property> </property>
<property name="text"> <property name="text">
<string>1x4 to 2x2 strips</string> <string>Color mode</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="0"> <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"> <widget class="QCheckBox" name="borderBox">
<property name="toolTip"> <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> <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>
@@ -182,17 +116,17 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="6" column="1"> <item row="0" column="0">
<widget class="QCheckBox" name="noRotateBox"> <widget class="QCheckBox" name="mangaBox">
<property name="toolTip"> <property name="toolTip">
<string>Do not rotate double page spreads in spread splitter option.</string> <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>
</property> </property>
<property name="text"> <property name="text">
<string>No rotate</string> <string>Manga mode</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="2"> <item row="0" column="2">
<widget class="QCheckBox" name="qualityBox"> <widget class="QCheckBox" name="qualityBox">
<property name="toolTip"> <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> <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>
@@ -205,7 +139,53 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="5" column="2"> <item row="3" 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>
</property>
<property name="text">
<string>JPEG/PNG/mozJpeg</string>
</property>
<property name="tristate">
<bool>true</bool>
</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="3" 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>
</property>
<property name="text">
<string>Cropping mode</string>
</property>
<property name="tristate">
<bool>true</bool>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QCheckBox" name="deleteBox">
<property name="toolTip">
<string>Delete input file(s) or directory. It's not recoverable!</string>
</property>
<property name="text">
<string>Delete input</string>
</property>
</widget>
</item>
<item row="4" column="2">
<widget class="QCheckBox" name="disableProcessingBox"> <widget class="QCheckBox" name="disableProcessingBox">
<property name="toolTip"> <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> <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>
@@ -215,28 +195,6 @@
</property> </property>
</widget> </widget>
</item> </item>
<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="focusPolicy">
<enum>Qt::FocusPolicy::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>
</layout> </layout>
</widget> </widget>
</item> </item>
@@ -274,7 +232,7 @@
<number>5</number> <number>5</number>
</property> </property>
<property name="orientation"> <property name="orientation">
<enum>Qt::Orientation::Horizontal</enum> <enum>Qt::Horizontal</enum>
</property> </property>
</widget> </widget>
</item> </item>
@@ -309,13 +267,13 @@
<item> <item>
<widget class="QSlider" name="croppingPowerSlider"> <widget class="QSlider" name="croppingPowerSlider">
<property name="maximum"> <property name="maximum">
<number>300</number> <number>200</number>
</property> </property>
<property name="singleStep"> <property name="singleStep">
<number>1</number> <number>1</number>
</property> </property>
<property name="orientation"> <property name="orientation">
<enum>Qt::Orientation::Horizontal</enum> <enum>Qt::Horizontal</enum>
</property> </property>
</widget> </widget>
</item> </item>
@@ -487,7 +445,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> <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>
<property name="text"> <property name="text">
<string>Metadata Editor</string> <string>Editor</string>
</property> </property>
<property name="icon"> <property name="icon">
<iconset resource="KCC.qrc"> <iconset resource="KCC.qrc">
@@ -518,16 +476,16 @@
<item row="2" column="0" colspan="2"> <item row="2" column="0" colspan="2">
<widget class="QListWidget" name="jobList"> <widget class="QListWidget" name="jobList">
<property name="styleSheet"> <property name="styleSheet">
<string notr="true"/> <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>
</property> </property>
<property name="selectionMode"> <property name="selectionMode">
<enum>QAbstractItemView::SelectionMode::NoSelection</enum> <enum>QAbstractItemView::NoSelection</enum>
</property> </property>
<property name="verticalScrollMode"> <property name="verticalScrollMode">
<enum>QAbstractItemView::ScrollMode::ScrollPerPixel</enum> <enum>QAbstractItemView::ScrollPerPixel</enum>
</property> </property>
<property name="horizontalScrollMode"> <property name="horizontalScrollMode">
<enum>QAbstractItemView::ScrollMode::ScrollPerPixel</enum> <enum>QAbstractItemView::ScrollPerPixel</enum>
</property> </property>
</widget> </widget>
</item> </item>
@@ -548,7 +506,7 @@
<bool>false</bool> <bool>false</bool>
</property> </property>
<property name="alignment"> <property name="alignment">
<set>Qt::AlignmentFlag::AlignJustify|Qt::AlignmentFlag::AlignVCenter</set> <set>Qt::AlignJustify|Qt::AlignVCenter</set>
</property> </property>
</widget> </widget>
</item> </item>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

View File

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

View File

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

View File

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

View File

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

105
kcc.py
View File

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

39
kcc.spec Normal file
View File

@@ -0,0 +1,39 @@
# -*- mode: python ; coding: utf-8 -*-
block_cipher = None
a = Analysis(['kcc.py'],
pathex=['.'],
binaries=[],
datas=[],
hiddenimports=[],
hookspath=[],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False)
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
exe = EXE(pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
[],
name='kcc',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=False,
upx_exclude=[],
runtime_tmpdir=None,
console=False,
disable_windowed_traceback=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None , icon='icons\\comic2ebook.ico')

View File

@@ -16,11 +16,6 @@
# OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER # OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE. # PERFORMANCE OF THIS SOFTWARE.
from PySide6.QtCore import (QSize, QUrl, Qt, Signal, QIODeviceBase, QEvent, QThread, QSettings)
from PySide6.QtGui import (QColor, QIcon, QPixmap, QDesktopServices)
from PySide6.QtWidgets import (QApplication, QLabel, QListWidgetItem, QMainWindow, QApplication, QSystemTrayIcon, QFileDialog, QMessageBox, QDialog)
from PySide6.QtNetwork import (QLocalSocket, QLocalServer)
import os import os
import re import re
import sys import sys
@@ -30,6 +25,9 @@ from shutil import move, rmtree
from subprocess import STDOUT, PIPE from subprocess import STDOUT, PIPE
import requests import requests
# noinspection PyUnresolvedReferences
from PySide6 import QtGui, QtCore, QtWidgets, QtNetwork
from PySide6.QtCore import Qt
from xml.sax.saxutils import escape from xml.sax.saxutils import escape
from psutil import Process from psutil import Process
from copy import copy from copy import copy
@@ -46,18 +44,18 @@ from . import KCC_ui
from . import KCC_ui_editor from . import KCC_ui_editor
class QApplicationMessaging(QApplication): class QApplicationMessaging(QtWidgets.QApplication):
messageFromOtherInstance = Signal(bytes) messageFromOtherInstance = QtCore.Signal(bytes)
def __init__(self, argv): def __init__(self, argv):
QApplication.__init__(self, argv) QtWidgets.QApplication.__init__(self, argv)
self._key = 'KCC' self._key = 'KCC'
self._timeout = 1000 self._timeout = 1000
self._locked = False self._locked = False
socket = QLocalSocket(self) socket = QtNetwork.QLocalSocket(self)
socket.connectToServer(self._key, QIODeviceBase.OpenModeFlag.WriteOnly) socket.connectToServer(self._key, QtCore.QIODeviceBase.OpenModeFlag.WriteOnly)
if not socket.waitForConnected(self._timeout): if not socket.waitForConnected(self._timeout):
self._server = QLocalServer(self) self._server = QtNetwork.QLocalServer(self)
self._server.newConnection.connect(self.handleMessage) self._server.newConnection.connect(self.handleMessage)
self._server.listen(self._key) self._server.listen(self._key)
else: else:
@@ -69,11 +67,11 @@ class QApplicationMessaging(QApplication):
self._server.close() self._server.close()
def event(self, e): def event(self, e):
if e.type() == QEvent.Type.FileOpen: if e.type() == QtCore.QEvent.Type.FileOpen:
self.messageFromOtherInstance.emit(bytes(e.file(), 'UTF-8')) self.messageFromOtherInstance.emit(bytes(e.file(), 'UTF-8'))
return True return True
else: else:
return QApplication.event(self, e) return QtWidgets.QApplication.event(self, e)
def isRunning(self): def isRunning(self):
return self._locked return self._locked
@@ -84,56 +82,54 @@ class QApplicationMessaging(QApplication):
self.messageFromOtherInstance.emit(socket.readAll().data()) self.messageFromOtherInstance.emit(socket.readAll().data())
def sendMessage(self, message): def sendMessage(self, message):
socket = QLocalSocket(self) socket = QtNetwork.QLocalSocket(self)
socket.connectToServer(self._key, QIODeviceBase.OpenModeFlag.WriteOnly) socket.connectToServer(self._key, QtCore.QIODeviceBase.OpenModeFlag.WriteOnly)
socket.waitForConnected(self._timeout) socket.waitForConnected(self._timeout)
socket.write(bytes(message, 'UTF-8')) socket.write(bytes(message, 'UTF-8'))
socket.waitForBytesWritten(self._timeout) socket.waitForBytesWritten(self._timeout)
socket.disconnectFromServer() socket.disconnectFromServer()
class QMainWindowKCC(QMainWindow): class QMainWindowKCC(QtWidgets.QMainWindow):
progressBarTick = Signal(str) progressBarTick = QtCore.Signal(str)
modeConvert = Signal(int) modeConvert = QtCore.Signal(int)
addMessage = Signal(str, str, bool) addMessage = QtCore.Signal(str, str, bool)
addTrayMessage = Signal(str, str) addTrayMessage = QtCore.Signal(str, str)
showDialog = Signal(str, str) showDialog = QtCore.Signal(str, str)
hideProgressBar = Signal() hideProgressBar = QtCore.Signal()
forceShutdown = Signal() forceShutdown = QtCore.Signal()
class Icons: class Icons:
def __init__(self): def __init__(self):
self.deviceKindle = QIcon() self.deviceKindle = QtGui.QIcon()
self.deviceKindle.addPixmap(QPixmap(":/Devices/icons/Kindle.png"), QIcon.Mode.Normal, QIcon.State.Off) self.deviceKindle.addPixmap(QtGui.QPixmap(":/Devices/icons/Kindle.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
self.deviceKobo = QIcon() self.deviceKobo = QtGui.QIcon()
self.deviceKobo.addPixmap(QPixmap(":/Devices/icons/Kobo.png"), QIcon.Mode.Normal, QIcon.State.Off) self.deviceKobo.addPixmap(QtGui.QPixmap(":/Devices/icons/Kobo.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
self.deviceRmk = QIcon() self.deviceOther = QtGui.QIcon()
self.deviceRmk.addPixmap(QPixmap(":/Devices/icons/Rmk.png"), QIcon.Mode.Normal, QIcon.State.Off) self.deviceOther.addPixmap(QtGui.QPixmap(":/Devices/icons/Other.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
self.deviceOther = QIcon()
self.deviceOther.addPixmap(QPixmap(":/Devices/icons/Other.png"), QIcon.Mode.Normal, QIcon.State.Off)
self.MOBIFormat = QIcon() self.MOBIFormat = QtGui.QIcon()
self.MOBIFormat.addPixmap(QPixmap(":/Formats/icons/MOBI.png"), QIcon.Mode.Normal, QIcon.State.Off) self.MOBIFormat.addPixmap(QtGui.QPixmap(":/Formats/icons/MOBI.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
self.CBZFormat = QIcon() self.CBZFormat = QtGui.QIcon()
self.CBZFormat.addPixmap(QPixmap(":/Formats/icons/CBZ.png"), QIcon.Mode.Normal, QIcon.State.Off) self.CBZFormat.addPixmap(QtGui.QPixmap(":/Formats/icons/CBZ.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
self.EPUBFormat = QIcon() self.EPUBFormat = QtGui.QIcon()
self.EPUBFormat.addPixmap(QPixmap(":/Formats/icons/EPUB.png"), QIcon.Mode.Normal, QIcon.State.Off) self.EPUBFormat.addPixmap(QtGui.QPixmap(":/Formats/icons/EPUB.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
self.info = QIcon() self.info = QtGui.QIcon()
self.info.addPixmap(QPixmap(":/Status/icons/info.png"), QIcon.Mode.Normal, QIcon.State.Off) self.info.addPixmap(QtGui.QPixmap(":/Status/icons/info.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
self.warning = QIcon() self.warning = QtGui.QIcon()
self.warning.addPixmap(QPixmap(":/Status/icons/warning.png"), QIcon.Mode.Normal, QIcon.State.Off) self.warning.addPixmap(QtGui.QPixmap(":/Status/icons/warning.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
self.error = QIcon() self.error = QtGui.QIcon()
self.error.addPixmap(QPixmap(":/Status/icons/error.png"), QIcon.Mode.Normal, QIcon.State.Off) self.error.addPixmap(QtGui.QPixmap(":/Status/icons/error.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
self.programIcon = QIcon() self.programIcon = QtGui.QIcon()
self.programIcon.addPixmap(QPixmap(":/Icon/icons/comic2ebook.png"), QIcon.Mode.Normal, QIcon.State.Off) self.programIcon.addPixmap(QtGui.QPixmap(":/Icon/icons/comic2ebook.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
class VersionThread(QThread): class VersionThread(QtCore.QThread):
def __init__(self): def __init__(self):
QThread.__init__(self) QtCore.QThread.__init__(self)
self.newVersion = '' self.newVersion = ''
self.md5 = '' self.md5 = ''
self.barProgress = 0 self.barProgress = 0
@@ -162,9 +158,9 @@ class VersionThread(QThread):
self.answer = dialoganswer self.answer = dialoganswer
class ProgressThread(QThread): class ProgressThread(QtCore.QThread):
def __init__(self): def __init__(self):
QThread.__init__(self) QtCore.QThread.__init__(self)
self.running = False self.running = False
self.content = None self.content = None
self.progress = 0 self.progress = 0
@@ -186,9 +182,9 @@ class ProgressThread(QThread):
self.running = False self.running = False
class WorkerThread(QThread): class WorkerThread(QtCore.QThread):
def __init__(self): def __init__(self):
QThread.__init__(self) QtCore.QThread.__init__(self)
self.conversionAlive = False self.conversionAlive = False
self.errors = False self.errors = False
self.kindlegenErrorCode = [0] self.kindlegenErrorCode = [0]
@@ -258,10 +254,6 @@ class WorkerThread(QThread):
options.noprocessing = True options.noprocessing = True
if GUI.deleteBox.isChecked(): if GUI.deleteBox.isChecked():
options.delete = True options.delete = True
if GUI.spreadShiftBox.isChecked():
options.spreadshift = True
if GUI.noRotateBox.isChecked():
options.norotate = True
if GUI.mozJpegBox.checkState() == Qt.CheckState.PartiallyChecked: if GUI.mozJpegBox.checkState() == Qt.CheckState.PartiallyChecked:
options.forcepng = True options.forcepng = True
elif GUI.mozJpegBox.checkState() == Qt.CheckState.Checked: elif GUI.mozJpegBox.checkState() == Qt.CheckState.Checked:
@@ -271,8 +263,6 @@ class WorkerThread(QThread):
options.customheight = str(GUI.heightBox.value()) options.customheight = str(GUI.heightBox.value())
if GUI.targetDirectory != '': if GUI.targetDirectory != '':
options.output = GUI.targetDirectory options.output = GUI.targetDirectory
if GUI.authorEdit.text():
options.author = str(GUI.authorEdit.text())
for i in range(GUI.jobList.count()): for i in range(GUI.jobList.count()):
# Make sure that we don't consider any system message as job to do # Make sure that we don't consider any system message as job to do
@@ -388,7 +378,7 @@ class WorkerThread(QThread):
except Exception: except Exception:
pass pass
MW.addMessage.emit('Processing MOBI files... <b>Done!</b>', 'info', True) MW.addMessage.emit('Processing MOBI files... <b>Done!</b>', 'info', True)
k = kindle.Kindle(options.profile) k = kindle.Kindle()
if k.path and k.coverSupport: if k.path and k.coverSupport:
for item in outputPath: for item in outputPath:
comic2ebook.options.covers[outputPath.index(item)][0].saveToKindle( comic2ebook.options.covers[outputPath.index(item)][0].saveToKindle(
@@ -437,7 +427,7 @@ class WorkerThread(QThread):
MW.modeConvert.emit(1) MW.modeConvert.emit(1)
class SystemTrayIcon(QSystemTrayIcon): class SystemTrayIcon(QtWidgets.QSystemTrayIcon):
def __init__(self): def __init__(self):
super().__init__() super().__init__()
if self.isSystemTrayAvailable(): if self.isSystemTrayAvailable():
@@ -450,7 +440,7 @@ class SystemTrayIcon(QSystemTrayIcon):
MW.activateWindow() MW.activateWindow()
def addTrayMessage(self, message, icon): def addTrayMessage(self, message, icon):
icon = getattr(QSystemTrayIcon.MessageIcon, icon) icon = getattr(QtWidgets.QSystemTrayIcon.MessageIcon, icon)
if self.supportsMessages() and not MW.isActiveWindow(): if self.supportsMessages() and not MW.isActiveWindow():
self.showMessage('Kindle Comic Converter', message, icon) self.showMessage('Kindle Comic Converter', message, icon)
@@ -460,7 +450,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
if self.needClean: if self.needClean:
self.needClean = False self.needClean = False
GUI.jobList.clear() GUI.jobList.clear()
dname = QFileDialog.getExistingDirectory(MW, 'Select directory', self.lastPath) dname = QtWidgets.QFileDialog.getExistingDirectory(MW, 'Select directory', self.lastPath)
if dname != '': if dname != '':
if sys.platform.startswith('win'): if sys.platform.startswith('win'):
dname = dname.replace('/', '\\') dname = dname.replace('/', '\\')
@@ -473,10 +463,10 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
self.needClean = False self.needClean = False
GUI.jobList.clear() GUI.jobList.clear()
if self.tar or self.sevenzip: if self.tar or self.sevenzip:
fnames = QFileDialog.getOpenFileNames(MW, 'Select file', self.lastPath, fnames = QtWidgets.QFileDialog.getOpenFileNames(MW, 'Select file', self.lastPath,
'Comic (*.cbz *.cbr *.cb7 *.zip *.rar *.7z *.pdf);;All (*.*)') 'Comic (*.cbz *.cbr *.cb7 *.zip *.rar *.7z *.pdf);;All (*.*)')
else: else:
fnames = QFileDialog.getOpenFileNames(MW, 'Select file', self.lastPath, fnames = QtWidgets.QFileDialog.getOpenFileNames(MW, 'Select file', self.lastPath,
'Comic (*.pdf);;All (*.*)') 'Comic (*.pdf);;All (*.*)')
for fname in fnames[0]: for fname in fnames[0]:
if fname != '': if fname != '':
@@ -488,8 +478,8 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
def selectFileMetaEditor(self): def selectFileMetaEditor(self):
sname = '' sname = ''
if QApplication.keyboardModifiers() == Qt.ShiftModifier: if QtWidgets.QApplication.keyboardModifiers() == QtCore.Qt.ShiftModifier:
dname = QFileDialog.getExistingDirectory(MW, 'Select directory', self.lastPath) dname = QtWidgets.QFileDialog.getExistingDirectory(MW, 'Select directory', self.lastPath)
if dname != '': if dname != '':
sname = os.path.join(dname, 'ComicInfo.xml') sname = os.path.join(dname, 'ComicInfo.xml')
if sys.platform.startswith('win'): if sys.platform.startswith('win'):
@@ -497,7 +487,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
self.lastPath = os.path.abspath(sname) self.lastPath = os.path.abspath(sname)
else: else:
if self.sevenzip: if self.sevenzip:
fname = QFileDialog.getOpenFileName(MW, 'Select file', self.lastPath, fname = QtWidgets.QFileDialog.getOpenFileName(MW, 'Select file', self.lastPath,
'Comic (*.cbz *.cbr *.cb7)') 'Comic (*.cbz *.cbr *.cb7)')
else: else:
fname = [''] fname = ['']
@@ -526,7 +516,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
def openWiki(self): def openWiki(self):
# noinspection PyCallByClass # noinspection PyCallByClass
QDesktopServices.openUrl(QUrl('https://github.com/ciromattia/kcc/wiki')) QtGui.QDesktopServices.openUrl(QtCore.QUrl('https://github.com/ciromattia/kcc/wiki'))
def modeChange(self, mode): def modeChange(self, mode):
if mode == 1: if mode == 1:
@@ -561,16 +551,16 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
if enable == 1: if enable == 1:
self.conversionAlive = False self.conversionAlive = False
self.worker.sync() self.worker.sync()
icon = QIcon() icon = QtGui.QIcon()
icon.addPixmap(QPixmap(":/Other/icons/convert.png"), QIcon.Mode.Normal, QIcon.State.Off) icon.addPixmap(QtGui.QPixmap(":/Other/icons/convert.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
GUI.convertButton.setIcon(icon) GUI.convertButton.setIcon(icon)
GUI.convertButton.setText('Convert') GUI.convertButton.setText('Convert')
GUI.centralWidget.setAcceptDrops(True) GUI.centralWidget.setAcceptDrops(True)
elif enable == 0: elif enable == 0:
self.conversionAlive = True self.conversionAlive = True
self.worker.sync() self.worker.sync()
icon = QIcon() icon = QtGui.QIcon()
icon.addPixmap(QPixmap(":/Other/icons/clear.png"), QIcon.Mode.Normal, QIcon.State.Off) icon.addPixmap(QtGui.QPixmap(":/Other/icons/clear.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
GUI.convertButton.setIcon(icon) GUI.convertButton.setIcon(icon)
GUI.convertButton.setText('Abort') GUI.convertButton.setText('Abort')
GUI.centralWidget.setAcceptDrops(False) GUI.centralWidget.setAcceptDrops(False)
@@ -686,15 +676,16 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
def addMessage(self, message, icon, replace=False): def addMessage(self, message, icon, replace=False):
if icon != '': if icon != '':
icon = getattr(self.icons, icon) icon = getattr(self.icons, icon)
item = QListWidgetItem(icon, ' ' + self.stripTags(message)) item = QtWidgets.QListWidgetItem(icon, ' ' + self.stripTags(message))
else: else:
item = QListWidgetItem(' ' + self.stripTags(message)) item = QtWidgets.QListWidgetItem(' ' + self.stripTags(message))
if replace: if replace:
GUI.jobList.takeItem(GUI.jobList.count() - 1) GUI.jobList.takeItem(GUI.jobList.count() - 1)
# Due to lack of HTML support in QListWidgetItem we overlay text field with QLabel # Due to lack of HTML support in QListWidgetItem we overlay text field with QLabel
# We still fill original text field with transparent content to trigger creation of horizontal scrollbar # We still fill original text field with transparent content to trigger creation of horizontal scrollbar
item.setForeground(QColor('transparent')) item.setForeground(QtGui.QColor('transparent'))
label = QLabel(message) label = QtWidgets.QLabel(message)
label.setStyleSheet('background-image:url('');background-color:rgba(0,0,0,0);color:rgb(0,0,0);')
label.setOpenExternalLinks(True) label.setOpenExternalLinks(True)
GUI.jobList.addItem(item) GUI.jobList.addItem(item)
GUI.jobList.setItemWidget(item, label) GUI.jobList.setItemWidget(item, label)
@@ -702,11 +693,11 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
def showDialog(self, message, kind): def showDialog(self, message, kind):
if kind == 'error': if kind == 'error':
QMessageBox.critical(MW, 'KCC - Error', message, QMessageBox.StandardButton.Ok) QtWidgets.QMessageBox.critical(MW, 'KCC - Error', message, QtWidgets.QMessageBox.StandardButton.Ok)
elif kind == 'question': elif kind == 'question':
GUI.versionCheck.setAnswer(QMessageBox.question(MW, 'KCC - Question', message, GUI.versionCheck.setAnswer(QtWidgets.QMessageBox.question(MW, 'KCC - Question', message,
QMessageBox.Yes, QtWidgets.QMessageBox.Yes,
QMessageBox.No)) QtWidgets.QMessageBox.No))
def updateProgressbar(self, command): def updateProgressbar(self, command):
if command == 'tick': if command == 'tick':
@@ -730,8 +721,8 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
self.conversionAlive = False self.conversionAlive = False
self.worker.sync() self.worker.sync()
else: else:
if QApplication.keyboardModifiers() == Qt.KeyboardModifier.ShiftModifier: if QtWidgets.QApplication.keyboardModifiers() == QtCore.Qt.KeyboardModifier.ShiftModifier:
dname = QFileDialog.getExistingDirectory(MW, 'Select output directory', self.lastPath) dname = QtWidgets.QFileDialog.getExistingDirectory(MW, 'Select output directory', self.lastPath)
if dname != '': if dname != '':
if sys.platform.startswith('win'): if sys.platform.startswith('win'):
dname = dname.replace('/', '\\') dname = dname.replace('/', '\\')
@@ -799,8 +790,6 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
'widthBox': GUI.widthBox.value(), 'widthBox': GUI.widthBox.value(),
'heightBox': GUI.heightBox.value(), 'heightBox': GUI.heightBox.value(),
'deleteBox': GUI.deleteBox.checkState().value, 'deleteBox': GUI.deleteBox.checkState().value,
'spreadShiftBox': GUI.spreadShiftBox.checkState().value,
'noRotateBox': GUI.noRotateBox.checkState().value,
'maximizeStrips': GUI.maximizeStrips.checkState().value, 'maximizeStrips': GUI.maximizeStrips.checkState().value,
'gammaSlider': float(self.gammaValue) * 100}) 'gammaSlider': float(self.gammaValue) * 100})
self.settings.sync() self.settings.sync()
@@ -876,7 +865,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
self.setupUi(MW) self.setupUi(MW)
self.editor = KCCGUI_MetaEditor() self.editor = KCCGUI_MetaEditor()
self.icons = Icons() self.icons = Icons()
self.settings = QSettings('ciromattia', 'kcc') self.settings = QtCore.QSettings('ciromattia', 'kcc')
self.settingsVersion = self.settings.value('settingsVersion', '', type=str) self.settingsVersion = self.settings.value('settingsVersion', '', type=str)
self.lastPath = self.settings.value('lastPath', '', type=str) self.lastPath = self.settings.value('lastPath', '', type=str)
self.lastDevice = self.settings.value('lastDevice', 0, type=int) self.lastDevice = self.settings.value('lastDevice', 0, type=int)
@@ -909,7 +898,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
elif sys.platform.startswith('darwin'): elif sys.platform.startswith('darwin'):
for element in ['editorButton', 'wikiButton', 'directoryButton', 'clearButton', 'fileButton', 'deviceBox', for element in ['editorButton', 'wikiButton', 'directoryButton', 'clearButton', 'fileButton', 'deviceBox',
'convertButton', 'formatBox']: 'convertButton', 'formatBox']:
getattr(GUI, element).setMinimumSize(QSize(0, 0)) getattr(GUI, element).setMinimumSize(QtCore.QSize(0, 0))
GUI.gridLayout.setContentsMargins(-1, -1, -1, -1) GUI.gridLayout.setContentsMargins(-1, -1, -1, -1)
for element in ['gridLayout_2', 'gridLayout_3', 'gridLayout_4', 'horizontalLayout', 'horizontalLayout_2']: for element in ['gridLayout_2', 'gridLayout_3', 'gridLayout_4', 'horizontalLayout', 'horizontalLayout_2']:
getattr(GUI, element).setContentsMargins(-1, 0, -1, 0) getattr(GUI, element).setContentsMargins(-1, 0, -1, 0)
@@ -942,12 +931,6 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
"Kindle PW 11": { "Kindle PW 11": {
'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': True, 'ForceColor': False, 'Label': 'KPW5', '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, "Kindle PW 7/10": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': True, 'ForceColor': False, 'Label': 'KV'}, 'DefaultUpscale': True, 'ForceColor': False, 'Label': 'KV'},
"Kindle PW 5/6": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, "Kindle PW 5/6": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
@@ -998,21 +981,13 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
'Label': 'KoS'}, 'Label': 'KoS'},
"Kobo Elipsa": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, 'DefaultUpscale': True, 'ForceColor': False, "Kobo Elipsa": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, 'DefaultUpscale': True, 'ForceColor': False,
'Label': 'KoE'}, '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, "Other": {'PVOptions': False, 'ForceExpert': True, 'DefaultFormat': 1, 'DefaultUpscale': False, 'ForceColor': False,
'Label': 'OTHER'}, 'Label': 'OTHER'},
} }
profilesGUI = [ profilesGUI = [
"Kindle CS 12",
"Kindle PW 12",
"Kindle Scribe", "Kindle Scribe",
"Kindle PW 11",
"Kindle 11", "Kindle 11",
"Kindle PW 11",
"Kindle Oasis 9/10", "Kindle Oasis 9/10",
"Separator", "Separator",
"Kobo Clara 2E", "Kobo Clara 2E",
@@ -1023,10 +998,6 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
"Kobo Elipsa", "Kobo Elipsa",
"Kobo Nia", "Kobo Nia",
"Separator", "Separator",
"reMarkable 1",
"reMarkable 2",
"reMarkable Paper Pro",
"Separator",
"Other", "Other",
"Separator", "Separator",
"Kindle Oasis 8", "Kindle Oasis 8",
@@ -1052,11 +1023,11 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
"Kobo Mini/Touch", "Kobo Mini/Touch",
] ]
statusBarLabel = QLabel('<b><a href="https://kcc.iosphe.re/">HOMEPAGE</a> - <a href="https://github.' statusBarLabel = QtWidgets.QLabel('<b><a href="https://kcc.iosphe.re/">HOMEPAGE</a> - <a href="https://github.'
'com/ciromattia/kcc/blob/master/README.md#issues--new-features--donations">DO' 'com/ciromattia/kcc/blob/master/README.md#issues--new-features--donations">DO'
'NATE</a> - <a href="http://www.mobileread.com/forums/showthread.php?t=207461' 'NATE</a> - <a href="http://www.mobileread.com/forums/showthread.php?t=207461'
'">FORUM</a></b>') '">FORUM</a></b>')
statusBarLabel.setAlignment(Qt.AlignmentFlag.AlignCenter) statusBarLabel.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
statusBarLabel.setOpenExternalLinks(True) statusBarLabel.setOpenExternalLinks(True)
GUI.statusBar.addPermanentWidget(statusBarLabel, 1) GUI.statusBar.addPermanentWidget(statusBarLabel, 1)
@@ -1115,8 +1086,6 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
GUI.deviceBox.addItem(self.icons.deviceOther, profile) GUI.deviceBox.addItem(self.icons.deviceOther, profile)
elif profile == "Separator": elif profile == "Separator":
GUI.deviceBox.insertSeparator(GUI.deviceBox.count() + 1) GUI.deviceBox.insertSeparator(GUI.deviceBox.count() + 1)
elif 'reM' in profile:
GUI.deviceBox.addItem(self.icons.deviceRmk, profile)
elif 'Ko' in profile: elif 'Ko' in profile:
GUI.deviceBox.addItem(self.icons.deviceKobo, profile) GUI.deviceBox.addItem(self.icons.deviceKobo, profile)
else: else:
@@ -1220,15 +1189,15 @@ class KCCGUI_MetaEditor(KCC_ui_editor.Ui_editorDialog):
return escape(s.strip()) return escape(s.strip())
def __init__(self): def __init__(self):
self.ui = QDialog() self.ui = QtWidgets.QDialog()
self.parser = None self.parser = None
self.setupUi(self.ui) self.setupUi(self.ui)
self.ui.setWindowFlags(self.ui.windowFlags() & ~Qt.WindowType.WindowContextHelpButtonHint) self.ui.setWindowFlags(self.ui.windowFlags() & ~QtCore.Qt.WindowType.WindowContextHelpButtonHint)
self.okButton.clicked.connect(self.saveData) self.okButton.clicked.connect(self.saveData)
self.cancelButton.clicked.connect(self.ui.close) self.cancelButton.clicked.connect(self.ui.close)
if sys.platform.startswith('linux'): if sys.platform.startswith('linux'):
self.ui.resize(450, 260) self.ui.resize(450, 260)
self.ui.setMinimumSize(QSize(450, 260)) self.ui.setMinimumSize(QtCore.QSize(450, 260))
elif sys.platform.startswith('darwin'): elif sys.platform.startswith('darwin'):
self.ui.resize(450, 310) self.ui.resize(450, 310)
self.ui.setMinimumSize(QSize(450, 310)) self.ui.setMinimumSize(QtCore.QSize(450, 310))

View File

@@ -1,6 +1,6 @@
# Resource object code (Python 3) # Resource object code (Python 3)
# Created by: object code # Created by: object code
# Created by: The Resource Compiler for Qt version 6.8.1 # Created by: The Resource Compiler for Qt version 6.5.1
# WARNING! All changes made in this file will be lost! # WARNING! All changes made in this file will be lost!
from PySide6 import QtCore from PySide6 import QtCore
@@ -4908,138 +4908,6 @@ D-\xbea6\x9bu\xd3\xe9\xf4+@\x03\xb0\xa2V\
$\x12\x89D\x22\x91H$\x12\x89D\x15\xd1\xff\x00V\ $\x12\x89D\x22\x91H$\x12\x89D\x15\xd1\xff\x00V\
\x1c\x01\xcd\xc9\x01\xf3\xd5\x00\x00\x00\x00IEND\xae\ \x1c\x01\xcd\xc9\x01\xf3\xd5\x00\x00\x00\x00IEND\xae\
B`\x82\ 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\ \x00\x00\x05\xe0\
\x89\ \x89\
PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
@@ -11529,10 +11397,6 @@ qt_resource_name = b"\
\x05\x92]\x07\ \x05\x92]\x07\
\x00K\ \x00K\
\x00o\x00b\x00o\x00.\x00p\x00n\x00g\ \x00o\x00b\x00o\x00.\x00p\x00n\x00g\
\x00\x07\
\x09>W\xe7\
\x00R\
\x00m\x00k\x00.\x00p\x00n\x00g\
\x00\x09\ \x00\x09\
\x0e\xc5\xfa\x07\ \x0e\xc5\xfa\x07\
\x00O\ \x00O\
@@ -11599,11 +11463,11 @@ qt_resource_name = b"\
qt_resource_struct = b"\ qt_resource_struct = b"\
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x05\x00\x00\x00\x01\ \x00\x00\x00\x00\x00\x02\x00\x00\x00\x05\x00\x00\x00\x01\
\x00\x00\x00\x00\x00\x00\x00\x00\ \x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x00J\x00\x02\x00\x00\x00\x01\x00\x00\x00\x1c\ \x00\x00\x00J\x00\x02\x00\x00\x00\x01\x00\x00\x00\x1b\
\x00\x00\x00\x00\x00\x00\x00\x00\ \x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x00&\x00\x02\x00\x00\x00\x01\x00\x00\x00\x14\ \x00\x00\x00&\x00\x02\x00\x00\x00\x01\x00\x00\x00\x13\
\x00\x00\x00\x00\x00\x00\x00\x00\ \x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x10\ \x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x0f\
\x00\x00\x00\x00\x00\x00\x00\x00\ \x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x006\x00\x02\x00\x00\x00\x01\x00\x00\x00\x0b\ \x00\x00\x006\x00\x02\x00\x00\x00\x01\x00\x00\x00\x0b\
\x00\x00\x00\x00\x00\x00\x00\x00\ \x00\x00\x00\x00\x00\x00\x00\x00\
@@ -11611,52 +11475,50 @@ qt_resource_struct = b"\
\x00\x00\x00\x00\x00\x00\x00\x00\ \x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x00X\x00\x02\x00\x00\x00\x04\x00\x00\x00\x07\ \x00\x00\x00X\x00\x02\x00\x00\x00\x04\x00\x00\x00\x07\
\x00\x00\x00\x00\x00\x00\x00\x00\ \x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x01\xc0\x00\x00\x00\x00\x00\x01\x00\x02.\xed\ \x00\x00\x01\xac\x00\x00\x00\x00\x00\x01\x00\x02&\xd7\
\x00\x00\x01\x90(\xef\xc4\x03\ \x00\x00\x01\x89\x0c\xe8\xc1\x86\
\x00\x00\x01\xfe\x00\x00\x00\x00\x00\x01\x00\x02\x83\x87\ \x00\x00\x01\xea\x00\x00\x00\x00\x00\x01\x00\x02{q\
\x00\x00\x01\x90(\xef\xc4\x00\ \x00\x00\x01\x89\x0c\xe8\xc1\x85\
\x00\x00\x01\xea\x00\x00\x00\x00\x00\x01\x00\x02Y\x8c\ \x00\x00\x01\xd6\x00\x00\x00\x00\x00\x01\x00\x02Qv\
\x00\x00\x01\x90(\xef\xc3\xff\ \x00\x00\x01\x89\x0c\xe8\xc1\x84\
\x00\x00\x01\xd6\x00\x00\x00\x00\x00\x01\x00\x02N)\ \x00\x00\x01\xc2\x00\x00\x00\x00\x00\x01\x00\x02F\x13\
\x00\x00\x01\x90(\xef\xc4\x01\ \x00\x00\x01\x89\x0c\xe8\xc1\x85\
\x00\x00\x00X\x00\x02\x00\x00\x00\x04\x00\x00\x00\x0c\ \x00\x00\x00X\x00\x02\x00\x00\x00\x03\x00\x00\x00\x0c\
\x00\x00\x00\x00\x00\x00\x00\x00\ \x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x00\xa6\x00\x00\x00\x00\x00\x01\x00\x01(\x97\ \x00\x00\x00\xa6\x00\x00\x00\x00\x00\x01\x00\x01(\x97\
\x00\x00\x01\x90(\xef\xc4\x03\ \x00\x00\x01\x89\x0c\xe8\xc1\x86\
\x00\x00\x00\xbc\x00\x00\x00\x00\x00\x01\x00\x011\xef\
\x00\x00\x01\x94o>\xe74\
\x00\x00\x00\x8c\x00\x00\x00\x00\x00\x01\x00\x01\x1d\x90\ \x00\x00\x00\x8c\x00\x00\x00\x00\x00\x01\x00\x01\x1d\x90\
\x00\x00\x01\x90(\xef\xc4\x02\ \x00\x00\x01\x89\x0c\xe8\xc1\x86\
\x00\x00\x00\xd0\x00\x00\x00\x00\x00\x01\x00\x01:\x05\ \x00\x00\x00\xbc\x00\x00\x00\x00\x00\x01\x00\x011\xef\
\x00\x00\x01\x90(\xef\xc4\x04\ \x00\x00\x01\x89\x0c\xe8\xc1\x87\
\x00\x00\x00X\x00\x02\x00\x00\x00\x03\x00\x00\x00\x11\ \x00\x00\x00X\x00\x02\x00\x00\x00\x03\x00\x00\x00\x10\
\x00\x00\x00\x00\x00\x00\x00\x00\ \x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x02B\x00\x00\x00\x00\x00\x01\x00\x02\xb5\xd3\ \x00\x00\x02.\x00\x00\x00\x00\x00\x01\x00\x02\xad\xbd\
\x00\x00\x01\x90(\xef\xc4!\ \x00\x00\x01\x89\x0c\xe8\xc1\x9a\
\x00\x00\x02\x14\x00\x00\x00\x00\x00\x01\x00\x02\x9f\xd6\ \x00\x00\x02\x00\x00\x00\x00\x00\x00\x01\x00\x02\x97\xc0\
\x00\x00\x01\x90(\xef\xc4\x1d\ \x00\x00\x01\x89\x0c\xe8\xc1\x98\
\x00\x00\x02*\x00\x00\x00\x00\x00\x01\x00\x02\xa93\ \x00\x00\x02\x16\x00\x00\x00\x00\x00\x01\x00\x02\xa1\x1d\
\x00\x00\x01\x90(\xef\xc4\x19\ \x00\x00\x01\x89\x0c\xe8\xc1\x97\
\x00\x00\x00X\x00\x02\x00\x00\x00\x07\x00\x00\x00\x15\ \x00\x00\x00X\x00\x02\x00\x00\x00\x07\x00\x00\x00\x14\
\x00\x00\x00\x00\x00\x00\x00\x00\ \x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x01\x1c\x00\x00\x00\x00\x00\x01\x00\x01P\xb1\ \x00\x00\x01\x08\x00\x00\x00\x00\x00\x01\x00\x01H\x9b\
\x00\x00\x01\x90(\xef\xc4\x22\ \x00\x00\x01\x89\x0c\xe8\xc1\x9b\
\x00\x00\x012\x00\x00\x00\x00\x00\x01\x00\x01yY\ \x00\x00\x01\x1e\x00\x00\x00\x00\x00\x01\x00\x01qC\
\x00\x00\x01\x90(\xef\xc4\x1c\ \x00\x00\x01\x89\x0c\xe8\xc1\x97\
\x00\x00\x01\x94\x00\x00\x00\x00\x00\x01\x00\x01\xd2-\ \x00\x00\x01\x80\x00\x00\x00\x00\x00\x01\x00\x01\xca\x17\
\x00\x00\x01\x90(\xef\xc4\x1e\ \x00\x00\x01\x89\x0c\xe8\xc1\x98\
\x00\x00\x01z\x00\x00\x00\x00\x00\x01\x00\x01\x8c\xe6\ \x00\x00\x01f\x00\x00\x00\x00\x00\x01\x00\x01\x84\xd0\
\x00\x00\x01\x90(\xef\xc4\x18\ \x00\x00\x01\x89\x0c\xe8\xc1\x97\
\x00\x00\x01\x04\x00\x00\x00\x00\x00\x01\x00\x01LR\ \x00\x00\x00\xf0\x00\x00\x00\x00\x00\x01\x00\x01D<\
\x00\x00\x01\x90(\xef\xc4\x0e\ \x00\x00\x01\x89\x0c\xe8\xc1\x8f\
\x00\x00\x00\xe8\x00\x00\x00\x00\x00\x01\x00\x01?\xe9\ \x00\x00\x00\xd4\x00\x00\x00\x00\x00\x01\x00\x017\xd3\
\x00\x00\x01\x90(\xef\xc4\x17\ \x00\x00\x01\x89\x0c\xe8\xc1\x96\
\x00\x00\x01T\x00\x00\x00\x00\x00\x01\x00\x01\x82\xb0\ \x00\x00\x01@\x00\x00\x00\x00\x00\x01\x00\x01z\x9a\
\x00\x00\x01\x90(\xef\xc4\x18\ \x00\x00\x01\x89\x0c\xe8\xc1\x96\
\x00\x00\x00X\x00\x02\x00\x00\x00\x01\x00\x00\x00\x1d\ \x00\x00\x00X\x00\x02\x00\x00\x00\x01\x00\x00\x00\x1c\
\x00\x00\x00\x00\x00\x00\x00\x00\ \x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x00h\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\ \x00\x00\x00h\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
\x00\x00\x01\x90(\xef\xc4\x16\ \x00\x00\x01\x89\x0c\xe8\xc1\x96\
" "
def qInitResources(): def qInitResources():

View File

@@ -3,7 +3,7 @@
################################################################################ ################################################################################
## Form generated from reading UI file 'KCC.ui' ## Form generated from reading UI file 'KCC.ui'
## ##
## Created by: Qt User Interface Compiler version 6.8.1 ## Created by: Qt User Interface Compiler version 6.5.1
## ##
## WARNING! All changes made in this file will be lost when recompiling UI file! ## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################ ################################################################################
@@ -16,19 +16,19 @@ from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
QImage, QKeySequence, QLinearGradient, QPainter, QImage, QKeySequence, QLinearGradient, QPainter,
QPalette, QPixmap, QRadialGradient, QTransform) QPalette, QPixmap, QRadialGradient, QTransform)
from PySide6.QtWidgets import (QAbstractItemView, QApplication, QCheckBox, QComboBox, from PySide6.QtWidgets import (QAbstractItemView, QApplication, QCheckBox, QComboBox,
QGridLayout, QHBoxLayout, QLabel, QLineEdit, QGridLayout, QHBoxLayout, QLabel, QListWidget,
QListWidget, QListWidgetItem, QMainWindow, QProgressBar, QListWidgetItem, QMainWindow, QProgressBar, QPushButton,
QPushButton, QSizePolicy, QSlider, QSpinBox, QSizePolicy, QSlider, QSpinBox, QStatusBar,
QStatusBar, QWidget) QWidget)
from . import KCC_rc from . import KCC_rc
class Ui_mainWindow(object): class Ui_mainWindow(object):
def setupUi(self, mainWindow): def setupUi(self, mainWindow):
if not mainWindow.objectName(): if not mainWindow.objectName():
mainWindow.setObjectName(u"mainWindow") mainWindow.setObjectName(u"mainWindow")
mainWindow.resize(482, 448) mainWindow.resize(450, 400)
icon = QIcon() icon = QIcon()
icon.addFile(u":/Icon/icons/comic2ebook.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off) icon.addFile(u":/Icon/icons/comic2ebook.png", QSize(), QIcon.Normal, QIcon.Off)
mainWindow.setWindowIcon(icon) mainWindow.setWindowIcon(icon)
self.centralWidget = QWidget(mainWindow) self.centralWidget = QWidget(mainWindow)
self.centralWidget.setObjectName(u"centralWidget") self.centralWidget.setObjectName(u"centralWidget")
@@ -40,103 +40,81 @@ class Ui_mainWindow(object):
self.gridLayout_2 = QGridLayout(self.optionWidget) self.gridLayout_2 = QGridLayout(self.optionWidget)
self.gridLayout_2.setObjectName(u"gridLayout_2") self.gridLayout_2.setObjectName(u"gridLayout_2")
self.gridLayout_2.setContentsMargins(0, 0, 0, 0) self.gridLayout_2.setContentsMargins(0, 0, 0, 0)
self.deleteBox = QCheckBox(self.optionWidget)
self.deleteBox.setObjectName(u"deleteBox")
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, 1, 0, 1, 1)
self.outputSplit = QCheckBox(self.optionWidget)
self.outputSplit.setObjectName(u"outputSplit")
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, 4, 2, 1, 1)
self.mozJpegBox = QCheckBox(self.optionWidget)
self.mozJpegBox.setObjectName(u"mozJpegBox")
self.mozJpegBox.setTristate(True)
self.gridLayout_2.addWidget(self.mozJpegBox, 4, 0, 1, 1)
self.upscaleBox = QCheckBox(self.optionWidget) self.upscaleBox = QCheckBox(self.optionWidget)
self.upscaleBox.setObjectName(u"upscaleBox") self.upscaleBox.setObjectName(u"upscaleBox")
self.upscaleBox.setTristate(True) self.upscaleBox.setTristate(True)
self.gridLayout_2.addWidget(self.upscaleBox, 2, 1, 1, 1) self.gridLayout_2.addWidget(self.upscaleBox, 1, 1, 1, 1)
self.colorBox = QCheckBox(self.optionWidget)
self.colorBox.setObjectName(u"colorBox")
self.gridLayout_2.addWidget(self.colorBox, 3, 2, 1, 1)
self.rotateBox = QCheckBox(self.optionWidget) self.rotateBox = QCheckBox(self.optionWidget)
self.rotateBox.setObjectName(u"rotateBox") self.rotateBox.setObjectName(u"rotateBox")
self.rotateBox.setTristate(True) self.rotateBox.setTristate(True)
self.gridLayout_2.addWidget(self.rotateBox, 1, 1, 1, 1) self.gridLayout_2.addWidget(self.rotateBox, 0, 1, 1, 1)
self.gammaBox = QCheckBox(self.optionWidget) self.outputSplit = QCheckBox(self.optionWidget)
self.gammaBox.setObjectName(u"gammaBox") self.outputSplit.setObjectName(u"outputSplit")
self.gridLayout_2.addWidget(self.gammaBox, 2, 2, 1, 1) self.gridLayout_2.addWidget(self.outputSplit, 2, 1, 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 = QCheckBox(self.optionWidget)
self.webtoonBox.setObjectName(u"webtoonBox") self.webtoonBox.setObjectName(u"webtoonBox")
self.gridLayout_2.addWidget(self.webtoonBox, 2, 0, 1, 1) self.gridLayout_2.addWidget(self.webtoonBox, 1, 0, 1, 1)
self.maximizeStrips = QCheckBox(self.optionWidget) self.colorBox = QCheckBox(self.optionWidget)
self.maximizeStrips.setObjectName(u"maximizeStrips") self.colorBox.setObjectName(u"colorBox")
self.gridLayout_2.addWidget(self.maximizeStrips, 4, 1, 1, 1) self.gridLayout_2.addWidget(self.colorBox, 2, 2, 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 = QCheckBox(self.optionWidget)
self.borderBox.setObjectName(u"borderBox") self.borderBox.setObjectName(u"borderBox")
self.borderBox.setTristate(True) self.borderBox.setTristate(True)
self.gridLayout_2.addWidget(self.borderBox, 3, 0, 1, 1) self.gridLayout_2.addWidget(self.borderBox, 2, 0, 1, 1)
self.noRotateBox = QCheckBox(self.optionWidget) self.mangaBox = QCheckBox(self.optionWidget)
self.noRotateBox.setObjectName(u"noRotateBox") self.mangaBox.setObjectName(u"mangaBox")
self.gridLayout_2.addWidget(self.noRotateBox, 6, 1, 1, 1) self.gridLayout_2.addWidget(self.mangaBox, 0, 0, 1, 1)
self.qualityBox = QCheckBox(self.optionWidget) self.qualityBox = QCheckBox(self.optionWidget)
self.qualityBox.setObjectName(u"qualityBox") self.qualityBox.setObjectName(u"qualityBox")
self.qualityBox.setTristate(True) self.qualityBox.setTristate(True)
self.gridLayout_2.addWidget(self.qualityBox, 1, 2, 1, 1) self.gridLayout_2.addWidget(self.qualityBox, 0, 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.maximizeStrips = QCheckBox(self.optionWidget)
self.maximizeStrips.setObjectName(u"maximizeStrips")
self.gridLayout_2.addWidget(self.maximizeStrips, 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.deleteBox = QCheckBox(self.optionWidget)
self.deleteBox.setObjectName(u"deleteBox")
self.gridLayout_2.addWidget(self.deleteBox, 4, 1, 1, 1)
self.disableProcessingBox = QCheckBox(self.optionWidget) self.disableProcessingBox = QCheckBox(self.optionWidget)
self.disableProcessingBox.setObjectName(u"disableProcessingBox") self.disableProcessingBox.setObjectName(u"disableProcessingBox")
self.gridLayout_2.addWidget(self.disableProcessingBox, 5, 2, 1, 1) self.gridLayout_2.addWidget(self.disableProcessingBox, 4, 2, 1, 1)
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.FocusPolicy.ClickFocus)
self.authorEdit.setClearButtonEnabled(False)
self.gridLayout_2.addWidget(self.authorEdit, 0, 0, 1, 1)
self.gridLayout.addWidget(self.optionWidget, 5, 0, 1, 2) self.gridLayout.addWidget(self.optionWidget, 5, 0, 1, 2)
@@ -156,7 +134,7 @@ class Ui_mainWindow(object):
self.gammaSlider.setObjectName(u"gammaSlider") self.gammaSlider.setObjectName(u"gammaSlider")
self.gammaSlider.setMaximum(250) self.gammaSlider.setMaximum(250)
self.gammaSlider.setSingleStep(5) self.gammaSlider.setSingleStep(5)
self.gammaSlider.setOrientation(Qt.Orientation.Horizontal) self.gammaSlider.setOrientation(Qt.Horizontal)
self.horizontalLayout_2.addWidget(self.gammaSlider) self.horizontalLayout_2.addWidget(self.gammaSlider)
@@ -176,9 +154,9 @@ class Ui_mainWindow(object):
self.croppingPowerSlider = QSlider(self.croppingWidget) self.croppingPowerSlider = QSlider(self.croppingWidget)
self.croppingPowerSlider.setObjectName(u"croppingPowerSlider") self.croppingPowerSlider.setObjectName(u"croppingPowerSlider")
self.croppingPowerSlider.setMaximum(300) self.croppingPowerSlider.setMaximum(200)
self.croppingPowerSlider.setSingleStep(1) self.croppingPowerSlider.setSingleStep(1)
self.croppingPowerSlider.setOrientation(Qt.Orientation.Horizontal) self.croppingPowerSlider.setOrientation(Qt.Horizontal)
self.horizontalLayout_3.addWidget(self.croppingPowerSlider) self.horizontalLayout_3.addWidget(self.croppingPowerSlider)
@@ -187,11 +165,11 @@ class Ui_mainWindow(object):
self.buttonWidget = QWidget(self.centralWidget) self.buttonWidget = QWidget(self.centralWidget)
self.buttonWidget.setObjectName(u"buttonWidget") self.buttonWidget.setObjectName(u"buttonWidget")
sizePolicy1 = QSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Fixed) sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed)
sizePolicy1.setHorizontalStretch(0) sizePolicy.setHorizontalStretch(0)
sizePolicy1.setVerticalStretch(0) sizePolicy.setVerticalStretch(0)
sizePolicy1.setHeightForWidth(self.buttonWidget.sizePolicy().hasHeightForWidth()) sizePolicy.setHeightForWidth(self.buttonWidget.sizePolicy().hasHeightForWidth())
self.buttonWidget.setSizePolicy(sizePolicy1) self.buttonWidget.setSizePolicy(sizePolicy)
self.gridLayout_4 = QGridLayout(self.buttonWidget) self.gridLayout_4 = QGridLayout(self.buttonWidget)
self.gridLayout_4.setObjectName(u"gridLayout_4") self.gridLayout_4.setObjectName(u"gridLayout_4")
self.gridLayout_4.setContentsMargins(0, 0, 0, 0) self.gridLayout_4.setContentsMargins(0, 0, 0, 0)
@@ -199,7 +177,7 @@ class Ui_mainWindow(object):
self.directoryButton.setObjectName(u"directoryButton") self.directoryButton.setObjectName(u"directoryButton")
self.directoryButton.setMinimumSize(QSize(0, 30)) self.directoryButton.setMinimumSize(QSize(0, 30))
icon1 = QIcon() icon1 = QIcon()
icon1.addFile(u":/Other/icons/folder_new.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off) icon1.addFile(u":/Other/icons/folder_new.png", QSize(), QIcon.Normal, QIcon.Off)
self.directoryButton.setIcon(icon1) self.directoryButton.setIcon(icon1)
self.gridLayout_4.addWidget(self.directoryButton, 0, 0, 1, 1) self.gridLayout_4.addWidget(self.directoryButton, 0, 0, 1, 1)
@@ -208,7 +186,7 @@ class Ui_mainWindow(object):
self.fileButton.setObjectName(u"fileButton") self.fileButton.setObjectName(u"fileButton")
self.fileButton.setMinimumSize(QSize(0, 30)) self.fileButton.setMinimumSize(QSize(0, 30))
icon2 = QIcon() icon2 = QIcon()
icon2.addFile(u":/Other/icons/document_new.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off) icon2.addFile(u":/Other/icons/document_new.png", QSize(), QIcon.Normal, QIcon.Off)
self.fileButton.setIcon(icon2) self.fileButton.setIcon(icon2)
self.gridLayout_4.addWidget(self.fileButton, 0, 3, 1, 1) self.gridLayout_4.addWidget(self.fileButton, 0, 3, 1, 1)
@@ -232,7 +210,7 @@ class Ui_mainWindow(object):
font.setBold(True) font.setBold(True)
self.convertButton.setFont(font) self.convertButton.setFont(font)
icon3 = QIcon() icon3 = QIcon()
icon3.addFile(u":/Other/icons/convert.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off) icon3.addFile(u":/Other/icons/convert.png", QSize(), QIcon.Normal, QIcon.Off)
self.convertButton.setIcon(icon3) self.convertButton.setIcon(icon3)
self.gridLayout_4.addWidget(self.convertButton, 1, 2, 1, 1) self.gridLayout_4.addWidget(self.convertButton, 1, 2, 1, 1)
@@ -241,7 +219,7 @@ class Ui_mainWindow(object):
self.clearButton.setObjectName(u"clearButton") self.clearButton.setObjectName(u"clearButton")
self.clearButton.setMinimumSize(QSize(0, 30)) self.clearButton.setMinimumSize(QSize(0, 30))
icon4 = QIcon() icon4 = QIcon()
icon4.addFile(u":/Other/icons/clear.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off) icon4.addFile(u":/Other/icons/clear.png", QSize(), QIcon.Normal, QIcon.Off)
self.clearButton.setIcon(icon4) self.clearButton.setIcon(icon4)
self.gridLayout_4.addWidget(self.clearButton, 0, 2, 1, 1) self.gridLayout_4.addWidget(self.clearButton, 0, 2, 1, 1)
@@ -264,7 +242,7 @@ class Ui_mainWindow(object):
self.editorButton.setObjectName(u"editorButton") self.editorButton.setObjectName(u"editorButton")
self.editorButton.setMinimumSize(QSize(0, 30)) self.editorButton.setMinimumSize(QSize(0, 30))
icon5 = QIcon() icon5 = QIcon()
icon5.addFile(u":/Other/icons/editor.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off) icon5.addFile(u":/Other/icons/editor.png", QSize(), QIcon.Normal, QIcon.Off)
self.editorButton.setIcon(icon5) self.editorButton.setIcon(icon5)
self.horizontalLayout.addWidget(self.editorButton) self.horizontalLayout.addWidget(self.editorButton)
@@ -273,7 +251,7 @@ class Ui_mainWindow(object):
self.wikiButton.setObjectName(u"wikiButton") self.wikiButton.setObjectName(u"wikiButton")
self.wikiButton.setMinimumSize(QSize(0, 30)) self.wikiButton.setMinimumSize(QSize(0, 30))
icon6 = QIcon() icon6 = QIcon()
icon6.addFile(u":/Other/icons/wiki.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off) icon6.addFile(u":/Other/icons/wiki.png", QSize(), QIcon.Normal, QIcon.Off)
self.wikiButton.setIcon(icon6) self.wikiButton.setIcon(icon6)
self.horizontalLayout.addWidget(self.wikiButton) self.horizontalLayout.addWidget(self.wikiButton)
@@ -283,10 +261,10 @@ class Ui_mainWindow(object):
self.jobList = QListWidget(self.centralWidget) self.jobList = QListWidget(self.centralWidget)
self.jobList.setObjectName(u"jobList") self.jobList.setObjectName(u"jobList")
self.jobList.setStyleSheet(u"") 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.setSelectionMode(QAbstractItemView.SelectionMode.NoSelection) self.jobList.setSelectionMode(QAbstractItemView.NoSelection)
self.jobList.setVerticalScrollMode(QAbstractItemView.ScrollMode.ScrollPerPixel) self.jobList.setVerticalScrollMode(QAbstractItemView.ScrollPerPixel)
self.jobList.setHorizontalScrollMode(QAbstractItemView.ScrollMode.ScrollPerPixel) self.jobList.setHorizontalScrollMode(QAbstractItemView.ScrollPerPixel)
self.gridLayout.addWidget(self.jobList, 2, 0, 1, 2) self.gridLayout.addWidget(self.jobList, 2, 0, 1, 2)
@@ -295,7 +273,7 @@ class Ui_mainWindow(object):
self.progressBar.setMinimumSize(QSize(0, 30)) self.progressBar.setMinimumSize(QSize(0, 30))
self.progressBar.setFont(font) self.progressBar.setFont(font)
self.progressBar.setVisible(False) self.progressBar.setVisible(False)
self.progressBar.setAlignment(Qt.AlignmentFlag.AlignJustify|Qt.AlignmentFlag.AlignVCenter) self.progressBar.setAlignment(Qt.AlignJustify|Qt.AlignVCenter)
self.gridLayout.addWidget(self.progressBar, 1, 0, 1, 2) self.gridLayout.addWidget(self.progressBar, 1, 0, 1, 2)
@@ -307,11 +285,11 @@ class Ui_mainWindow(object):
self.gridLayout_3.setContentsMargins(0, 0, 0, 0) self.gridLayout_3.setContentsMargins(0, 0, 0, 0)
self.hLabel = QLabel(self.customWidget) self.hLabel = QLabel(self.customWidget)
self.hLabel.setObjectName(u"hLabel") self.hLabel.setObjectName(u"hLabel")
sizePolicy2 = QSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Preferred) sizePolicy1 = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred)
sizePolicy2.setHorizontalStretch(0) sizePolicy1.setHorizontalStretch(0)
sizePolicy2.setVerticalStretch(0) sizePolicy1.setVerticalStretch(0)
sizePolicy2.setHeightForWidth(self.hLabel.sizePolicy().hasHeightForWidth()) sizePolicy1.setHeightForWidth(self.hLabel.sizePolicy().hasHeightForWidth())
self.hLabel.setSizePolicy(sizePolicy2) self.hLabel.setSizePolicy(sizePolicy1)
self.gridLayout_3.addWidget(self.hLabel, 0, 2, 1, 1) self.gridLayout_3.addWidget(self.hLabel, 0, 2, 1, 1)
@@ -323,8 +301,8 @@ class Ui_mainWindow(object):
self.wLabel = QLabel(self.customWidget) self.wLabel = QLabel(self.customWidget)
self.wLabel.setObjectName(u"wLabel") self.wLabel.setObjectName(u"wLabel")
sizePolicy2.setHeightForWidth(self.wLabel.sizePolicy().hasHeightForWidth()) sizePolicy1.setHeightForWidth(self.wLabel.sizePolicy().hasHeightForWidth())
self.wLabel.setSizePolicy(sizePolicy2) self.wLabel.setSizePolicy(sizePolicy1)
self.gridLayout_3.addWidget(self.wLabel, 0, 0, 1, 1) self.gridLayout_3.addWidget(self.wLabel, 0, 0, 1, 1)
@@ -376,74 +354,62 @@ class Ui_mainWindow(object):
def retranslateUi(self, mainWindow): def retranslateUi(self, mainWindow):
mainWindow.setWindowTitle(QCoreApplication.translate("mainWindow", u"Kindle Comic Converter", None)) mainWindow.setWindowTitle(QCoreApplication.translate("mainWindow", u"Kindle Comic Converter", None))
#if QT_CONFIG(tooltip)
self.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.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.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.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.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) #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)) 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) #endif // QT_CONFIG(tooltip)
self.upscaleBox.setText(QCoreApplication.translate("mainWindow", u"Stretch/Upscale", None)) 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) #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.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) #endif // QT_CONFIG(tooltip)
self.rotateBox.setText(QCoreApplication.translate("mainWindow", u"Spread splitter", None)) self.rotateBox.setText(QCoreApplication.translate("mainWindow", u"Spread splitter", None))
#if QT_CONFIG(tooltip) #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)) 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) #endif // QT_CONFIG(tooltip)
self.gammaBox.setText(QCoreApplication.translate("mainWindow", u"Custom gamma", None)) self.outputSplit.setText(QCoreApplication.translate("mainWindow", u"Output split", 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) #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)) 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) #endif // QT_CONFIG(tooltip)
self.webtoonBox.setText(QCoreApplication.translate("mainWindow", u"Webtoon mode", None)) self.webtoonBox.setText(QCoreApplication.translate("mainWindow", u"Webtoon mode", None))
#if QT_CONFIG(tooltip) #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)) 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) #endif // QT_CONFIG(tooltip)
self.maximizeStrips.setText(QCoreApplication.translate("mainWindow", u"1x4 to 2x2 strips", None)) self.colorBox.setText(QCoreApplication.translate("mainWindow", u"Color mode", 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) #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)) 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) #endif // QT_CONFIG(tooltip)
self.borderBox.setText(QCoreApplication.translate("mainWindow", u"W/B margins", None)) self.borderBox.setText(QCoreApplication.translate("mainWindow", u"W/B margins", None))
#if QT_CONFIG(tooltip) #if QT_CONFIG(tooltip)
self.noRotateBox.setToolTip(QCoreApplication.translate("mainWindow", u"Do not rotate double page spreads in spread splitter option.", None)) 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) #endif // QT_CONFIG(tooltip)
self.noRotateBox.setText(QCoreApplication.translate("mainWindow", u"No rotate", None)) self.mangaBox.setText(QCoreApplication.translate("mainWindow", u"Manga mode", None))
#if QT_CONFIG(tooltip) #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)) 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) #endif // QT_CONFIG(tooltip)
self.qualityBox.setText(QCoreApplication.translate("mainWindow", u"Panel View 4/2/HQ", None)) self.qualityBox.setText(QCoreApplication.translate("mainWindow", u"Panel View 4/2/HQ", 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.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.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.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) #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)) 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) #endif // QT_CONFIG(tooltip)
self.disableProcessingBox.setText(QCoreApplication.translate("mainWindow", u"Disable processing", None)) self.disableProcessingBox.setText(QCoreApplication.translate("mainWindow", u"Disable processing", None))
#if QT_CONFIG(tooltip)
self.authorEdit.setToolTip(QCoreApplication.translate("mainWindow", u"Default Author is KCC", None))
#endif // QT_CONFIG(tooltip)
self.authorEdit.setPlaceholderText(QCoreApplication.translate("mainWindow", u"Default Author", None))
self.gammaLabel.setText(QCoreApplication.translate("mainWindow", u"Gamma: Auto", None)) self.gammaLabel.setText(QCoreApplication.translate("mainWindow", u"Gamma: Auto", None))
self.croppingPowerLabel.setText(QCoreApplication.translate("mainWindow", u"Cropping power:", None)) self.croppingPowerLabel.setText(QCoreApplication.translate("mainWindow", u"Cropping power:", None))
#if QT_CONFIG(tooltip) #if QT_CONFIG(tooltip)
@@ -468,7 +434,7 @@ class Ui_mainWindow(object):
#if QT_CONFIG(tooltip) #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)) 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) #endif // QT_CONFIG(tooltip)
self.editorButton.setText(QCoreApplication.translate("mainWindow", u"Metadata Editor", None)) self.editorButton.setText(QCoreApplication.translate("mainWindow", u"Editor", None))
self.wikiButton.setText(QCoreApplication.translate("mainWindow", u"Wiki", None)) self.wikiButton.setText(QCoreApplication.translate("mainWindow", u"Wiki", None))
#if QT_CONFIG(tooltip) #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)) 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' ## Form generated from reading UI file 'MetaEditor.ui'
## ##
## Created by: Qt User Interface Compiler version 6.8.1 ## Created by: Qt User Interface Compiler version 6.5.1
## ##
## WARNING! All changes made in this file will be lost when recompiling UI file! ## 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.resize(400, 260)
editorDialog.setMinimumSize(QSize(400, 260)) editorDialog.setMinimumSize(QSize(400, 260))
icon = QIcon() icon = QIcon()
icon.addFile(u":/Icon/icons/comic2ebook.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off) icon.addFile(u":/Icon/icons/comic2ebook.png", QSize(), QIcon.Normal, QIcon.Off)
editorDialog.setWindowIcon(icon) editorDialog.setWindowIcon(icon)
self.verticalLayout = QVBoxLayout(editorDialog) self.verticalLayout = QVBoxLayout(editorDialog)
self.verticalLayout.setObjectName(u"verticalLayout") self.verticalLayout.setObjectName(u"verticalLayout")
@@ -117,7 +117,7 @@ class Ui_editorDialog(object):
self.horizontalLayout.setContentsMargins(0, 0, 0, 0) self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
self.statusLabel = QLabel(self.optionWidget) self.statusLabel = QLabel(self.optionWidget)
self.statusLabel.setObjectName(u"statusLabel") self.statusLabel.setObjectName(u"statusLabel")
sizePolicy = QSizePolicy(QSizePolicy.Policy.MinimumExpanding, QSizePolicy.Policy.MinimumExpanding) sizePolicy = QSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding)
sizePolicy.setHorizontalStretch(0) sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0) sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.statusLabel.sizePolicy().hasHeightForWidth()) sizePolicy.setHeightForWidth(self.statusLabel.sizePolicy().hasHeightForWidth())
@@ -129,7 +129,7 @@ class Ui_editorDialog(object):
self.okButton.setObjectName(u"okButton") self.okButton.setObjectName(u"okButton")
self.okButton.setMinimumSize(QSize(0, 30)) self.okButton.setMinimumSize(QSize(0, 30))
icon1 = QIcon() icon1 = QIcon()
icon1.addFile(u":/Other/icons/convert.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off) icon1.addFile(u":/Other/icons/convert.png", QSize(), QIcon.Normal, QIcon.Off)
self.okButton.setIcon(icon1) self.okButton.setIcon(icon1)
self.horizontalLayout.addWidget(self.okButton) self.horizontalLayout.addWidget(self.okButton)
@@ -138,7 +138,7 @@ class Ui_editorDialog(object):
self.cancelButton.setObjectName(u"cancelButton") self.cancelButton.setObjectName(u"cancelButton")
self.cancelButton.setMinimumSize(QSize(0, 30)) self.cancelButton.setMinimumSize(QSize(0, 30))
icon2 = QIcon() icon2 = QIcon()
icon2.addFile(u":/Other/icons/clear.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off) icon2.addFile(u":/Other/icons/clear.png", QSize(), QIcon.Normal, QIcon.Off)
self.cancelButton.setIcon(icon2) self.cancelButton.setIcon(icon2)
self.horizontalLayout.addWidget(self.cancelButton) self.horizontalLayout.addWidget(self.cancelButton)

View File

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

View File

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

View File

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

View File

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

View File

@@ -23,7 +23,6 @@ import os
import mozjpeg_lossless_optimization import mozjpeg_lossless_optimization
from PIL import Image, ImageOps, ImageStat, ImageChops, ImageFilter from PIL import Image, ImageOps, ImageStat, ImageChops, ImageFilter
from .shared import md5Checksum 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 AUTO_CROP_THRESHOLD = 0.015
@@ -79,29 +78,18 @@ class ProfileData:
PalleteNull = [ PalleteNull = [
] ]
ProfilesKindleEBOK = { Profiles = {
'K1': ("Kindle 1", (600, 670), Palette4, 1.8), 'K1': ("Kindle 1", (600, 670), Palette4, 1.8),
'K11': ("Kindle 11", (1072, 1448), Palette16, 1.8),
'K2': ("Kindle 2", (600, 670), Palette15, 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), 'K34': ("Kindle Keyboard/Touch", (600, 800), Palette16, 1.8),
'K578': ("Kindle", (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), 'KPW': ("Kindle Paperwhite 1/2", (758, 1024), Palette16, 1.8),
'KV': ("Kindle Paperwhite 3/4/Voyage/Oasis", (1072, 1448), 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), '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), 'KS': ("Kindle Scribe", (1860, 2480), Palette16, 1.8),
}
ProfilesKindle = {
**ProfilesKindleEBOK,
**ProfilesKindlePDOC
}
ProfilesKobo = {
'KoMT': ("Kobo Mini/Touch", (600, 800), Palette16, 1.8), 'KoMT': ("Kobo Mini/Touch", (600, 800), Palette16, 1.8),
'KoG': ("Kobo Glo", (768, 1024), Palette16, 1.8), 'KoG': ("Kobo Glo", (768, 1024), Palette16, 1.8),
'KoGHD': ("Kobo Glo HD", (1072, 1448), Palette16, 1.8), 'KoGHD': ("Kobo Glo HD", (1072, 1448), Palette16, 1.8),
@@ -117,18 +105,6 @@ class ProfileData:
'KoF': ("Kobo Forma", (1440, 1920), Palette16, 1.8), 'KoF': ("Kobo Forma", (1440, 1920), Palette16, 1.8),
'KoS': ("Kobo Sage", (1440, 1920), Palette16, 1.8), 'KoS': ("Kobo Sage", (1440, 1920), Palette16, 1.8),
'KoE': ("Kobo Elipsa", (1404, 1872), 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), 'OTHER': ("Other", (0, 0), Palette16, 1.8),
} }
@@ -175,10 +151,7 @@ class ComicPageParser:
self.payload.append(['N', self.source, new_image, self.color, self.fill]) self.payload.append(['N', self.source, new_image, self.color, self.fill])
elif (width > height) != (dstwidth > dstheight) and width <= dstheight and height <= dstwidth \ elif (width > height) != (dstwidth > dstheight) and width <= dstheight and height <= dstwidth \
and not self.opt.webtoon and self.opt.splitter == 1: and not self.opt.webtoon and self.opt.splitter == 1:
spread = self.image self.payload.append(['R', self.source, self.image.rotate(90, Image.Resampling.BICUBIC, True), self.color, self.fill])
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: elif (width > height) != (dstwidth > dstheight) and not self.opt.webtoon:
if self.opt.splitter != 1: if self.opt.splitter != 1:
if width > height: if width > height:
@@ -196,10 +169,7 @@ class ComicPageParser:
self.payload.append(['S1', self.source, pageone, self.color, self.fill]) self.payload.append(['S1', self.source, pageone, self.color, self.fill])
self.payload.append(['S2', self.source, pagetwo, self.color, self.fill]) self.payload.append(['S2', self.source, pagetwo, self.color, self.fill])
if self.opt.splitter > 0: if self.opt.splitter > 0:
spread = self.image self.payload.append(['R', self.source, self.image.rotate(90, Image.Resampling.BICUBIC, True),
if not self.opt.norotate:
spread = spread.rotate(90, Image.Resampling.BICUBIC, True)
self.payload.append(['R', self.source, spread,
self.color, self.fill]) self.color, self.fill])
else: else:
self.payload.append(['N', self.source, self.image, self.color, self.fill]) self.payload.append(['N', self.source, self.image, self.color, self.fill])
@@ -372,6 +342,20 @@ class ComicPage:
else: else:
return Image.Resampling.LANCZOS 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): def maybeCrop(self, box, minimum):
box_area = (box[2] - box[0]) * (box[3] - box[1]) box_area = (box[2] - box[0]) * (box[3] - box[1])
image_area = self.image.size[0] * self.image.size[1] image_area = self.image.size[0] * self.image.size[1]
@@ -379,16 +363,26 @@ class ComicPage:
self.image = self.image.crop(box) self.image = self.image.crop(box)
def cropPageNumber(self, power, minimum): def cropPageNumber(self, power, minimum):
bbox = get_bbox_crop_margin_page_number(self.image, power, self.fill) if self.fill != 'white':
tmptmg = self.image.convert(mode='L')
if bbox: else:
self.maybeCrop(bbox, minimum) 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)
def cropMargin(self, power, minimum): def cropMargin(self, power, minimum):
bbox = get_bbox_crop_margin(self.image, power, self.fill) if self.fill != 'white':
tmptmg = self.image.convert(mode='L')
if bbox: else:
self.maybeCrop(bbox, minimum) 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)
class Cover: class Cover:

View File

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

View File

@@ -1,215 +0,0 @@
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,6 +6,5 @@ python-slugify>=1.2.1
raven>=6.0.0 raven>=6.0.0
packaging>=23.2 packaging>=23.2
mozjpeg-lossless-optimization>=1.1.2 mozjpeg-lossless-optimization>=1.1.2
natsort>=8.4.0 natsort[fast]>=8.4.0
distro>=1.8.0 distro>=1.8.0
numpy>=1.22.4,<2.0.0

View File

@@ -36,7 +36,7 @@ class BuildBinaryCommand(setuptools.Command):
def run(self): def run(self):
VERSION = __version__ VERSION = __version__
if sys.platform == 'darwin': if sys.platform == 'darwin':
os.system('pyinstaller --hidden-import=_cffi_backend -y -D -i icons/comic2ebook.icns -n "Kindle Comic Converter" -w -s kcc.py') os.system('pyinstaller -y -D -i icons/comic2ebook.icns -n "Kindle Comic Converter" -w -s kcc.py')
# TODO /usr/bin/codesign --force -s "$MACOS_CERTIFICATE_NAME" --options runtime dist/Applications/Kindle\ Comic\ Converter.app -v # TODO /usr/bin/codesign --force -s "$MACOS_CERTIFICATE_NAME" --options runtime dist/Applications/Kindle\ Comic\ Converter.app -v
os.system(f'appdmg kcc.json dist/kcc_macos_{platform.processor()}_{VERSION}.dmg') os.system(f'appdmg kcc.json dist/kcc_macos_{platform.processor()}_{VERSION}.dmg')
sys.exit(0) sys.exit(0)
@@ -45,7 +45,7 @@ class BuildBinaryCommand(setuptools.Command):
sys.exit(0) sys.exit(0)
elif sys.platform == 'linux': elif sys.platform == 'linux':
os.system( os.system(
'pyinstaller --hidden-import=_cffi_backend --hidden-import=queue -y -F -i icons/comic2ebook.ico -n kcc_linux_' + VERSION + ' kcc.py') 'pyinstaller --hidden-import=queue -y -F -i icons/comic2ebook.ico -n kcc_linux_' + VERSION + ' kcc.py')
sys.exit(0) sys.exit(0)
else: else:
sys.exit(0) sys.exit(0)
@@ -81,9 +81,8 @@ setuptools.setup(
'raven>=6.0.0', 'raven>=6.0.0',
'requests>=2.31.0', 'requests>=2.31.0',
'mozjpeg-lossless-optimization>=1.1.2', 'mozjpeg-lossless-optimization>=1.1.2',
'natsort>=8.4.0', 'natsort[fast]>=8.4.0',
'distro', 'distro',
'numpy>=1.22.4,<2.0.0'
], ],
classifiers=[], classifiers=[],
zip_safe=False, zip_safe=False,