1
0
mirror of https://github.com/ciromattia/kcc synced 2026-04-21 08:28:59 +00:00

Compare commits

...

26 Commits
v6.1.0 ... dev

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
Alex Xu
8ff401cc3a remove fastnumbers from armv7 2024-10-04 09:04:56 -07:00
Alex Xu
fb7d92d737 fix Docker linux arm (#746)
* fix linux arm64

* Update Dockerfile-base

* Update Dockerfile

* Update Dockerfile-base

* ENV PATH="/opt/venv/bin:$PATH"

* fix docker armv7

* Update Dockerfile-base

* Update Dockerfile-base
2024-09-29 10:21:35 -07:00
Alex Xu
1ca8b2c11b Update README.md 2024-09-15 11:49:49 -07:00
Alex Xu
40e0b4853b add flatpak note 2024-09-08 22:23:23 -07:00
Alex Xu
005313f978 flatpak mobi conversion stuck 2024-09-08 21:22:56 -07:00
Alex Xu
add2ef9faa fix image file is corrupt for real 2024-09-07 22:17:30 -07:00
Alex Xu
0c98acd606 bump to 6.2.0 2024-09-06 23:05:02 -07:00
Alex Xu
fe902ec213 fix corrupt 2024-09-06 23:04:32 -07:00
Alex Xu
4e9714e6f8 add packaging for Python 3.12 2024-08-30 10:02:44 -07:00
Alex Xu
1337ad7fe3 Python 3.12 supported 2024-08-28 14:32:23 -07:00
dependabot[bot]
511c7c1580 Bump python from 3.11-slim-bullseye to 3.12-slim-bullseye
Bumps python from 3.11-slim-bullseye to 3.12-slim-bullseye.

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-28 14:28:16 -07:00
Alex Xu
16dd034af4 add Python 3.12 support 2024-08-28 14:26:54 -07:00
Alex Xu
d22794555f bump to 6.1.1 2024-08-09 17:13:02 -07:00
Alex Xu
ab089adbca fix no such file or directory 7z (#728)
* refactor archive code

* simplify code

* Revert "simplify code"

This reverts commit 3e90dd66c3.

* add link to missing extraction software

* fix tar

* fix wording
2024-08-09 17:08:13 -07:00
Alex Xu
2c770f4562 search for kindlegen in %UserProfile% 2024-08-09 17:05:44 -07:00
Alex Xu
4c73006bd8 add APFS to external drive 2024-08-06 16:04:45 -07:00
Alex Xu
1093dbf65a Update README.md 2024-07-27 08:26:44 -07:00
Alex Xu
df9990c692 add python 3.12 unsupported note 2024-07-25 13:54:31 -07:00
Alex Xu
114f4c9e57 add macOS minimums 2024-07-21 10:19:49 -07:00
Alex Xu
c2c477475d combine files/chapters in readme 2024-07-20 12:37:20 -07:00
Alex Xu
a0f8d0b5cf add kcc 6.1 7z note 2024-07-01 08:55:20 -07:00
19 changed files with 312 additions and 268 deletions

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-20230809 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'
@@ -16,4 +16,4 @@ COPY . /opt/kcc
RUN cat /opt/kcc/kindlecomicconverter/__init__.py | grep version | awk '{print $3}' | sed "s/'//g" > /IMAGE_VERSION RUN cat /opt/kcc/kindlecomicconverter/__init__.py | grep version | awk '{print $3}' | sed "s/'//g" > /IMAGE_VERSION
ENTRYPOINT ["/opt/kcc/kcc-c2e.py"] ENTRYPOINT ["/opt/kcc/kcc-c2e.py"]
CMD ["-h"] CMD ["-h"]

View File

@@ -1,4 +1,4 @@
FROM --platform=linux/amd64 python:3.11-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.11-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
@@ -28,6 +28,9 @@ ENV LC_ALL=C.UTF-8 \
SHELL ["/bin/bash", "-o", "pipefail", "-c"] SHELL ["/bin/bash", "-o", "pipefail", "-c"]
COPY requirements.txt /opt/kcc/
ENV PATH="/opt/venv/bin:$PATH"
RUN set -x && \ RUN set -x && \
TEMP_PACKAGES=() && \ TEMP_PACKAGES=() && \
KEPT_PACKAGES=() && \ KEPT_PACKAGES=() && \
@@ -64,14 +67,13 @@ 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 pip install -r /opt/kcc/requirements.txt && \
python -m venv /opt/venv && \ python -m venv /opt/venv && \
python -m pip install --upgrade pillow python-slugify psutil raven mozjpeg-lossless-optimization python -m pip install -r /opt/kcc/requirements.txt
###################################################################################### ######################################################################################
FROM --platform=linux/arm/v7 python:3.11-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
@@ -83,6 +85,9 @@ ENV LC_ALL=C.UTF-8 \
SHELL ["/bin/bash", "-o", "pipefail", "-c"] SHELL ["/bin/bash", "-o", "pipefail", "-c"]
COPY requirements.txt /opt/kcc/
ENV PATH="/opt/venv/bin:$PATH"
RUN set -x && \ RUN set -x && \
TEMP_PACKAGES=() && \ TEMP_PACKAGES=() && \
KEPT_PACKAGES=() && \ KEPT_PACKAGES=() && \
@@ -120,19 +125,18 @@ 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 pip install -r /opt/kcc/requirements.txt && \
python -m venv /opt/venv && \ python -m venv /opt/venv && \
python -m pip install --upgrade pillow python-slugify psutil raven mozjpeg-lossless-optimization python -m pip install --upgrade pillow psutil requests python-slugify raven packaging mozjpeg-lossless-optimization natsort distro
###################################################################################### ######################################################################################
FROM --platform=linux/amd64 python:3.11-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.11-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.11-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
###################################################################################### ######################################################################################
@@ -156,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-20230809 > /IMAGE_VERSION echo docker-base-20240928 > /IMAGE_VERSION

View File

@@ -22,13 +22,13 @@ If you have some **technical** problems using KCC please [file an issue here](ht
If you can fix an open issue, fork & make a pull request. If you can fix an open issue, fork & make a pull request.
If you find **KCC** valuable you can consider donating to the authors: If you find **KCC** valuable you can consider donating to the authors:
- Ciro Mattia Gonano: - Ciro Mattia Gonano (founder, active 2013-2014):
- [![Donate PayPal](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=D8WNYNPBGDAS2) - [![Donate PayPal](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=D8WNYNPBGDAS2)
- [![Donate Flattr](https://img.shields.io/badge/Donate-Flattr-green.svg)](http://flattr.com/thing/2260449/ciromattiakcc-on-GitHub) - [![Donate Flattr](https://img.shields.io/badge/Donate-Flattr-green.svg)](http://flattr.com/thing/2260449/ciromattiakcc-on-GitHub)
- Paweł Jastrzębski: - Paweł Jastrzębski (active 2013-2019):
- [![Donate PayPal](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YTTJ4LK2JDHPS) - [![Donate PayPal](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YTTJ4LK2JDHPS)
- [![Donate Bitcoin](https://img.shields.io/badge/Donate-Bitcoin-green.svg)](https://jastrzeb.ski/donate/) - [![Donate Bitcoin](https://img.shields.io/badge/Donate-Bitcoin-green.svg)](https://jastrzeb.ski/donate/)
- Alex Xu - Alex Xu (active 2023-Present)
- [![Donate PayPal](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/donate/?business=QFJVE7A6LCP6U&no_recurring=0&item_name=Kindle+Comic+Converter&currency_code=USD) - [![Donate PayPal](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/donate/?business=QFJVE7A6LCP6U&no_recurring=0&item_name=Kindle+Comic+Converter&currency_code=USD)
@@ -55,6 +55,8 @@ For flatpak, Docker, and AppImage versions, refer to the wiki: https://github.co
- [Kindle Scribe cover guide](https://github.com/ciromattia/kcc/issues/508) (also works for older Kindles) - [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)
- [Flatpak mobi conversion stuck](https://github.com/ciromattia/kcc/wiki/Installation#linux)
## PREREQUISITES ## PREREQUISITES
@@ -62,17 +64,15 @@ You'll need to install various tools to access important but optional features.
### KindleGen ### KindleGen
#### Windows / macOS KindleGen On Windows and macOS, install [Kindle Previewer](https://www.amazon.com/Kindle-Previewer/b?ie=UTF8&node=21381691011) and `kindlegen` will be autodetected from it.
Install [Kindle Previewer](https://www.amazon.com/Kindle-Previewer/b?ie=UTF8&node=21381691011) and `kindlegen` will be autodetected from it. If you have issues detecting it, get stuck on the MOBI conversion step, or use Linux AppImage or Flatpak, refer to the wiki: https://github.com/ciromattia/kcc/wiki/Installation#kindlegen
If you have issues detecting it, or use another OS, refer to the wiki: https://github.com/ciromattia/kcc/wiki/Installation#kindlegen
### 7-Zip ### 7-Zip
This is an optional requirement as of KCC 6.1.0. You only need to install it if 1) you are using advanced features, 2) are using Windows 10 (2017) or earlier, or 3) need to use an older KCC version. This is no longer required as of KCC 6.1.
If you need to install it, refer to the wiki: https://github.com/ciromattia/kcc/wiki/Installation#7-zip If you still need it, refer to the wiki: 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:

View File

@@ -37,36 +37,59 @@
<property name="bottomMargin"> <property name="bottomMargin">
<number>0</number> <number>0</number>
</property> </property>
<item row="0" column="2"> <item row="1" column="1">
<widget class="QCheckBox" name="qualityBox"> <widget class="QCheckBox" name="upscaleBox">
<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&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Unchecked - Nothing&lt;br/&gt;&lt;/span&gt;Images smaller than device resolution will not be resized.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Indeterminate - Stretching&lt;br/&gt;&lt;/span&gt;Images smaller than device resolution will be resized. Aspect ratio will be not preserved.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Checked - Upscaling&lt;br/&gt;&lt;/span&gt;Images smaller than device resolution will be resized. Aspect ratio will be preserved.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="text"> <property name="text">
<string>Panel View 4/2/HQ</string> <string>Stretch/Upscale</string>
</property> </property>
<property name="tristate"> <property name="tristate">
<bool>true</bool> <bool>true</bool>
</property> </property>
</widget> </widget>
</item> </item>
<item row="4" column="1"> <item row="0" column="1">
<widget class="QCheckBox" name="deleteBox"> <widget class="QCheckBox" name="rotateBox">
<property name="toolTip"> <property name="toolTip">
<string>Delete input file(s) or directory. It's not recoverable!</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>
</property> </property>
<property name="text"> <property name="text">
<string>Delete input</string> <string>Spread splitter</string>
</property>
<property name="tristate">
<bool>true</bool>
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="1"> <item row="2" column="1">
<widget class="QCheckBox" name="maximizeStrips"> <widget class="QCheckBox" name="outputSplit">
<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;&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>1x4 to 2x2 strips</string> <string>Output split</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="webtoonBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Enable special parsing mode for Korean Webtoons.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Webtoon mode</string>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QCheckBox" name="colorBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Disable conversion to grayscale.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Color mode</string>
</property> </property>
</widget> </widget>
</item> </item>
@@ -93,29 +116,6 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="0">
<widget class="QCheckBox" name="webtoonBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Enable special parsing mode for Korean Webtoons.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Webtoon mode</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="upscaleBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Unchecked - Nothing&lt;br/&gt;&lt;/span&gt;Images smaller than device resolution will not be resized.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Indeterminate - Stretching&lt;br/&gt;&lt;/span&gt;Images smaller than device resolution will be resized. Aspect ratio will be not preserved.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Checked - Upscaling&lt;br/&gt;&lt;/span&gt;Images smaller than device resolution will be resized. Aspect ratio will be preserved.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Stretch/Upscale</string>
</property>
<property name="tristate">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="0"> <item row="0" column="0">
<widget class="QCheckBox" name="mangaBox"> <widget class="QCheckBox" name="mangaBox">
<property name="toolTip"> <property name="toolTip">
@@ -126,42 +126,19 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="0" column="1"> <item row="0" column="2">
<widget class="QCheckBox" name="rotateBox"> <widget class="QCheckBox" name="qualityBox">
<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 style='white-space:pre'&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Unchecked - 4 panels&lt;br/&gt;&lt;/span&gt;Zoom each corner separately.&lt;/p&gt;&lt;p style='white-space:pre'&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Indeterminate - 2 panels&lt;br/&gt;&lt;/span&gt;Zoom only the top and bottom of the page.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Checked - 4 high-quality panels&lt;br/&gt;&lt;/span&gt;Zoom each corner separately. Try to increase the quality of magnification. Check wiki for more details.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="text"> <property name="text">
<string>Spread splitter</string> <string>Panel View 4/2/HQ</string>
</property> </property>
<property name="tristate"> <property name="tristate">
<bool>true</bool> <bool>true</bool>
</property> </property>
</widget> </widget>
</item> </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="2" column="1">
<widget class="QCheckBox" name="outputSplit">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Unchecked - Automatic mode&lt;br/&gt;&lt;/span&gt;The output will be split automatically.&lt;/p&gt;&lt;p style='white-space:pre'&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Checked - Volume mode&lt;br/&gt;&lt;/span&gt;Every subdirectory will be considered as a separate volume.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Output split</string>
</property>
</widget>
</item>
<item row="3" column="0"> <item row="3" column="0">
<widget class="QCheckBox" name="mozJpegBox"> <widget class="QCheckBox" name="mozJpegBox">
<property name="toolTip"> <property name="toolTip">
@@ -175,13 +152,36 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="2"> <item row="3" column="1">
<widget class="QCheckBox" name="colorBox"> <widget class="QCheckBox" name="maximizeStrips">
<property name="toolTip"> <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> <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>
<property name="text"> <property name="text">
<string>Color mode</string> <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> </property>
</widget> </widget>
</item> </item>
@@ -195,16 +195,6 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="4" column="0">
<widget class="QCheckBox" name="dedupeCoverBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Don't duplicate the first page as the cover. Useful for 2 page spread alignment.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>De-dupe cover</string>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
</item> </item>

1
kcc.py
View File

@@ -50,6 +50,7 @@ elif sys.platform.startswith('win'):
win_paths = [ win_paths = [
os.path.expandvars('%LOCALAPPDATA%\\Amazon\\KC2'), os.path.expandvars('%LOCALAPPDATA%\\Amazon\\KC2'),
os.path.expandvars('%LOCALAPPDATA%\\Amazon\\Kindle Previewer 3\\lib\\fc\\bin\\'), os.path.expandvars('%LOCALAPPDATA%\\Amazon\\Kindle Previewer 3\\lib\\fc\\bin\\'),
os.path.expandvars('%UserProfile%\\Kindle Previewer 3\\lib\\fc\\bin\\'),
'C:\\Apps\\Kindle Previewer 3\\lib\\fc\\bin', 'C:\\Apps\\Kindle Previewer 3\\lib\\fc\\bin',
'D:\\Apps\\Kindle Previewer 3\\lib\\fc\\bin', 'D:\\Apps\\Kindle Previewer 3\\lib\\fc\\bin',
'E:\\Apps\\Kindle Previewer 3\\lib\\fc\\bin', 'E:\\Apps\\Kindle Previewer 3\\lib\\fc\\bin',

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

@@ -31,11 +31,11 @@ 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
from distutils.version import StrictVersion from packaging.version import Version
from raven import Client from raven import Client
from tempfile import gettempdir from tempfile import gettempdir
from .shared import HTMLStripper, sanitizeTrace, walkLevel, subprocess_run_silent from .shared import HTMLStripper, sanitizeTrace, walkLevel, subprocess_run
from . import __version__ from . import __version__
from . import comic2ebook from . import comic2ebook
from . import metadata from . import metadata
@@ -146,9 +146,9 @@ class VersionThread(QtCore.QThread):
latest_version = json_parser["tag_name"] latest_version = json_parser["tag_name"]
latest_version = re.sub(r'^v', "", latest_version) latest_version = re.sub(r'^v', "", latest_version)
if ("b" not in __version__ and StrictVersion(latest_version) > StrictVersion(__version__)) \ if ("b" not in __version__ and Version(latest_version) > Version(__version__)) \
or ("b" in __version__ or ("b" in __version__
and StrictVersion(latest_version) >= StrictVersion(re.sub(r'b.*', '', __version__))): and Version(latest_version) >= Version(re.sub(r'b.*', '', __version__))):
MW.addMessage.emit('<a href="' + html_url + '"><b>The new version is available!</b></a>', 'warning', MW.addMessage.emit('<a href="' + html_url + '"><b>The new version is available!</b></a>', 'warning',
False) False)
except Exception: except Exception:
@@ -254,8 +254,6 @@ class WorkerThread(QtCore.QThread):
options.noprocessing = True options.noprocessing = True
if GUI.deleteBox.isChecked(): if GUI.deleteBox.isChecked():
options.delete = True options.delete = True
if GUI.dedupeCoverBox.isChecked():
options.dedupecover = 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:
@@ -792,7 +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,
'dedupeCoverBox': GUI.dedupeCoverBox.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()
@@ -846,12 +843,12 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
except Exception: except Exception:
pass pass
try: try:
versionCheck = subprocess_run_silent(['kindlegen', '-locale', 'en'], stdout=PIPE, stderr=STDOUT, encoding='UTF-8') versionCheck = subprocess_run(['kindlegen', '-locale', 'en'], stdout=PIPE, stderr=STDOUT, encoding='UTF-8')
self.kindleGen = True self.kindleGen = True
for line in versionCheck.stdout.splitlines(): for line in versionCheck.stdout.splitlines():
if 'Amazon kindlegen' in line: if 'Amazon kindlegen' in line:
versionCheck = line.split('V')[1].split(' ')[0] versionCheck = line.split('V')[1].split(' ')[0]
if StrictVersion(versionCheck) < StrictVersion('2.9'): if Version(versionCheck) < Version('2.9'):
self.addMessage('Your <a href="https://www.amazon.com/b?node=23496309011">KindleGen</a>' self.addMessage('Your <a href="https://www.amazon.com/b?node=23496309011">KindleGen</a>'
' is outdated! MOBI conversion might fail.', 'warning') ' is outdated! MOBI conversion might fail.', 'warning')
break break
@@ -1041,12 +1038,12 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
'<a href="https://github.com/ciromattia/kcc/wiki/Important-tips">important tips</a>.', '<a href="https://github.com/ciromattia/kcc/wiki/Important-tips">important tips</a>.',
'info') 'info')
try: try:
subprocess_run_silent(['tar'], stdout=PIPE, stderr=STDOUT) subprocess_run(['tar'], stdout=PIPE, stderr=STDOUT)
self.tar = True self.tar = True
except FileNotFoundError: except FileNotFoundError:
self.tar = False self.tar = False
try: try:
subprocess_run_silent(['7z'], stdout=PIPE, stderr=STDOUT) subprocess_run(['7z'], stdout=PIPE, stderr=STDOUT)
self.sevenzip = True self.sevenzip = True
except FileNotFoundError: except FileNotFoundError:
self.sevenzip = False self.sevenzip = False

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.5.2 # 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
@@ -11476,49 +11476,49 @@ qt_resource_struct = b"\
\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\xac\x00\x00\x00\x00\x00\x01\x00\x02&\xd7\ \x00\x00\x01\xac\x00\x00\x00\x00\x00\x01\x00\x02&\xd7\
\x00\x00\x01\x88;p\xbcB\ \x00\x00\x01\x89\x0c\xe8\xc1\x86\
\x00\x00\x01\xea\x00\x00\x00\x00\x00\x01\x00\x02{q\ \x00\x00\x01\xea\x00\x00\x00\x00\x00\x01\x00\x02{q\
\x00\x00\x01\x88;p\xbcB\ \x00\x00\x01\x89\x0c\xe8\xc1\x85\
\x00\x00\x01\xd6\x00\x00\x00\x00\x00\x01\x00\x02Qv\ \x00\x00\x01\xd6\x00\x00\x00\x00\x00\x01\x00\x02Qv\
\x00\x00\x01\x88;p\xbcB\ \x00\x00\x01\x89\x0c\xe8\xc1\x84\
\x00\x00\x01\xc2\x00\x00\x00\x00\x00\x01\x00\x02F\x13\ \x00\x00\x01\xc2\x00\x00\x00\x00\x00\x01\x00\x02F\x13\
\x00\x00\x01\x89\x89D9.\ \x00\x00\x01\x89\x0c\xe8\xc1\x85\
\x00\x00\x00X\x00\x02\x00\x00\x00\x03\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\x88;p\xbcB\ \x00\x00\x01\x89\x0c\xe8\xc1\x86\
\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\x88;p\xbcB\ \x00\x00\x01\x89\x0c\xe8\xc1\x86\
\x00\x00\x00\xbc\x00\x00\x00\x00\x00\x01\x00\x011\xef\ \x00\x00\x00\xbc\x00\x00\x00\x00\x00\x01\x00\x011\xef\
\x00\x00\x01\x88;p\xbcB\ \x00\x00\x01\x89\x0c\xe8\xc1\x87\
\x00\x00\x00X\x00\x02\x00\x00\x00\x03\x00\x00\x00\x10\ \x00\x00\x00X\x00\x02\x00\x00\x00\x03\x00\x00\x00\x10\
\x00\x00\x00\x00\x00\x00\x00\x00\ \x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x02.\x00\x00\x00\x00\x00\x01\x00\x02\xad\xbd\ \x00\x00\x02.\x00\x00\x00\x00\x00\x01\x00\x02\xad\xbd\
\x00\x00\x01\x88;p\xbcJ\ \x00\x00\x01\x89\x0c\xe8\xc1\x9a\
\x00\x00\x02\x00\x00\x00\x00\x00\x00\x01\x00\x02\x97\xc0\ \x00\x00\x02\x00\x00\x00\x00\x00\x00\x01\x00\x02\x97\xc0\
\x00\x00\x01\x88;p\xbcI\ \x00\x00\x01\x89\x0c\xe8\xc1\x98\
\x00\x00\x02\x16\x00\x00\x00\x00\x00\x01\x00\x02\xa1\x1d\ \x00\x00\x02\x16\x00\x00\x00\x00\x00\x01\x00\x02\xa1\x1d\
\x00\x00\x01\x88;p\xbcI\ \x00\x00\x01\x89\x0c\xe8\xc1\x97\
\x00\x00\x00X\x00\x02\x00\x00\x00\x07\x00\x00\x00\x14\ \x00\x00\x00X\x00\x02\x00\x00\x00\x07\x00\x00\x00\x14\
\x00\x00\x00\x00\x00\x00\x00\x00\ \x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x01\x08\x00\x00\x00\x00\x00\x01\x00\x01H\x9b\ \x00\x00\x01\x08\x00\x00\x00\x00\x00\x01\x00\x01H\x9b\
\x00\x00\x01\x88;p\xbcJ\ \x00\x00\x01\x89\x0c\xe8\xc1\x9b\
\x00\x00\x01\x1e\x00\x00\x00\x00\x00\x01\x00\x01qC\ \x00\x00\x01\x1e\x00\x00\x00\x00\x00\x01\x00\x01qC\
\x00\x00\x01\x88;p\xbcI\ \x00\x00\x01\x89\x0c\xe8\xc1\x97\
\x00\x00\x01\x80\x00\x00\x00\x00\x00\x01\x00\x01\xca\x17\ \x00\x00\x01\x80\x00\x00\x00\x00\x00\x01\x00\x01\xca\x17\
\x00\x00\x01\x88;p\xbcI\ \x00\x00\x01\x89\x0c\xe8\xc1\x98\
\x00\x00\x01f\x00\x00\x00\x00\x00\x01\x00\x01\x84\xd0\ \x00\x00\x01f\x00\x00\x00\x00\x00\x01\x00\x01\x84\xd0\
\x00\x00\x01\x88;p\xbcH\ \x00\x00\x01\x89\x0c\xe8\xc1\x97\
\x00\x00\x00\xf0\x00\x00\x00\x00\x00\x01\x00\x01D<\ \x00\x00\x00\xf0\x00\x00\x00\x00\x00\x01\x00\x01D<\
\x00\x00\x01\x88;p\xbcF\ \x00\x00\x01\x89\x0c\xe8\xc1\x8f\
\x00\x00\x00\xd4\x00\x00\x00\x00\x00\x01\x00\x017\xd3\ \x00\x00\x00\xd4\x00\x00\x00\x00\x00\x01\x00\x017\xd3\
\x00\x00\x01\x88;p\xbcH\ \x00\x00\x01\x89\x0c\xe8\xc1\x96\
\x00\x00\x01@\x00\x00\x00\x00\x00\x01\x00\x01z\x9a\ \x00\x00\x01@\x00\x00\x00\x00\x00\x01\x00\x01z\x9a\
\x00\x00\x01\x88;p\xbcH\ \x00\x00\x01\x89\x0c\xe8\xc1\x96\
\x00\x00\x00X\x00\x02\x00\x00\x00\x01\x00\x00\x00\x1c\ \x00\x00\x00X\x00\x02\x00\x00\x00\x01\x00\x00\x00\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\x88;p\xbcH\ \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.5.2 ## 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!
################################################################################ ################################################################################
@@ -40,21 +40,32 @@ 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.qualityBox = QCheckBox(self.optionWidget) self.upscaleBox = QCheckBox(self.optionWidget)
self.qualityBox.setObjectName(u"qualityBox") self.upscaleBox.setObjectName(u"upscaleBox")
self.qualityBox.setTristate(True) self.upscaleBox.setTristate(True)
self.gridLayout_2.addWidget(self.qualityBox, 0, 2, 1, 1) self.gridLayout_2.addWidget(self.upscaleBox, 1, 1, 1, 1)
self.deleteBox = QCheckBox(self.optionWidget) self.rotateBox = QCheckBox(self.optionWidget)
self.deleteBox.setObjectName(u"deleteBox") self.rotateBox.setObjectName(u"rotateBox")
self.rotateBox.setTristate(True)
self.gridLayout_2.addWidget(self.deleteBox, 4, 1, 1, 1) self.gridLayout_2.addWidget(self.rotateBox, 0, 1, 1, 1)
self.maximizeStrips = QCheckBox(self.optionWidget) self.outputSplit = QCheckBox(self.optionWidget)
self.maximizeStrips.setObjectName(u"maximizeStrips") self.outputSplit.setObjectName(u"outputSplit")
self.gridLayout_2.addWidget(self.maximizeStrips, 3, 1, 1, 1) self.gridLayout_2.addWidget(self.outputSplit, 2, 1, 1, 1)
self.webtoonBox = QCheckBox(self.optionWidget)
self.webtoonBox.setObjectName(u"webtoonBox")
self.gridLayout_2.addWidget(self.webtoonBox, 1, 0, 1, 1)
self.colorBox = QCheckBox(self.optionWidget)
self.colorBox.setObjectName(u"colorBox")
self.gridLayout_2.addWidget(self.colorBox, 2, 2, 1, 1)
self.gammaBox = QCheckBox(self.optionWidget) self.gammaBox = QCheckBox(self.optionWidget)
self.gammaBox.setObjectName(u"gammaBox") self.gammaBox.setObjectName(u"gammaBox")
@@ -67,38 +78,16 @@ class Ui_mainWindow(object):
self.gridLayout_2.addWidget(self.borderBox, 2, 0, 1, 1) self.gridLayout_2.addWidget(self.borderBox, 2, 0, 1, 1)
self.webtoonBox = QCheckBox(self.optionWidget)
self.webtoonBox.setObjectName(u"webtoonBox")
self.gridLayout_2.addWidget(self.webtoonBox, 1, 0, 1, 1)
self.upscaleBox = QCheckBox(self.optionWidget)
self.upscaleBox.setObjectName(u"upscaleBox")
self.upscaleBox.setTristate(True)
self.gridLayout_2.addWidget(self.upscaleBox, 1, 1, 1, 1)
self.mangaBox = QCheckBox(self.optionWidget) self.mangaBox = QCheckBox(self.optionWidget)
self.mangaBox.setObjectName(u"mangaBox") self.mangaBox.setObjectName(u"mangaBox")
self.gridLayout_2.addWidget(self.mangaBox, 0, 0, 1, 1) self.gridLayout_2.addWidget(self.mangaBox, 0, 0, 1, 1)
self.rotateBox = QCheckBox(self.optionWidget) self.qualityBox = QCheckBox(self.optionWidget)
self.rotateBox.setObjectName(u"rotateBox") self.qualityBox.setObjectName(u"qualityBox")
self.rotateBox.setTristate(True) self.qualityBox.setTristate(True)
self.gridLayout_2.addWidget(self.rotateBox, 0, 1, 1, 1) self.gridLayout_2.addWidget(self.qualityBox, 0, 2, 1, 1)
self.croppingBox = QCheckBox(self.optionWidget)
self.croppingBox.setObjectName(u"croppingBox")
self.croppingBox.setTristate(True)
self.gridLayout_2.addWidget(self.croppingBox, 3, 2, 1, 1)
self.outputSplit = QCheckBox(self.optionWidget)
self.outputSplit.setObjectName(u"outputSplit")
self.gridLayout_2.addWidget(self.outputSplit, 2, 1, 1, 1)
self.mozJpegBox = QCheckBox(self.optionWidget) self.mozJpegBox = QCheckBox(self.optionWidget)
self.mozJpegBox.setObjectName(u"mozJpegBox") self.mozJpegBox.setObjectName(u"mozJpegBox")
@@ -106,21 +95,27 @@ class Ui_mainWindow(object):
self.gridLayout_2.addWidget(self.mozJpegBox, 3, 0, 1, 1) self.gridLayout_2.addWidget(self.mozJpegBox, 3, 0, 1, 1)
self.colorBox = QCheckBox(self.optionWidget) self.maximizeStrips = QCheckBox(self.optionWidget)
self.colorBox.setObjectName(u"colorBox") self.maximizeStrips.setObjectName(u"maximizeStrips")
self.gridLayout_2.addWidget(self.colorBox, 2, 2, 1, 1) 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, 4, 2, 1, 1) self.gridLayout_2.addWidget(self.disableProcessingBox, 4, 2, 1, 1)
self.dedupeCoverBox = QCheckBox(self.optionWidget)
self.dedupeCoverBox.setObjectName(u"dedupeCoverBox")
self.gridLayout_2.addWidget(self.dedupeCoverBox, 4, 0, 1, 1)
self.gridLayout.addWidget(self.optionWidget, 5, 0, 1, 2) self.gridLayout.addWidget(self.optionWidget, 5, 0, 1, 2)
@@ -360,17 +355,25 @@ 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) #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.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.qualityBox.setText(QCoreApplication.translate("mainWindow", u"Panel View 4/2/HQ", None)) self.upscaleBox.setText(QCoreApplication.translate("mainWindow", u"Stretch/Upscale", None))
#if QT_CONFIG(tooltip) #if QT_CONFIG(tooltip)
self.deleteBox.setToolTip(QCoreApplication.translate("mainWindow", u"Delete input file(s) or directory. It's not recoverable!", 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.deleteBox.setText(QCoreApplication.translate("mainWindow", u"Delete input", None)) self.rotateBox.setText(QCoreApplication.translate("mainWindow", u"Spread splitter", 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.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.maximizeStrips.setText(QCoreApplication.translate("mainWindow", u"1x4 to 2x2 strips", None)) self.outputSplit.setText(QCoreApplication.translate("mainWindow", u"Output split", None))
#if QT_CONFIG(tooltip)
self.webtoonBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Enable special parsing mode for Korean Webtoons.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.webtoonBox.setText(QCoreApplication.translate("mainWindow", u"Webtoon mode", None))
#if QT_CONFIG(tooltip)
self.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.gammaBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Disable automatic gamma correction.</p></body></html>", None)) 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) #endif // QT_CONFIG(tooltip)
@@ -379,46 +382,34 @@ class Ui_mainWindow(object):
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)
self.webtoonBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Enable special parsing mode for Korean Webtoons.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.webtoonBox.setText(QCoreApplication.translate("mainWindow", u"Webtoon mode", None))
#if QT_CONFIG(tooltip)
self.upscaleBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Nothing<br/></span>Images smaller than device resolution will not be resized.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - Stretching<br/></span>Images smaller than device resolution will be resized. Aspect ratio will be not preserved.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Upscaling<br/></span>Images smaller than device resolution will be resized. Aspect ratio will be preserved.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.upscaleBox.setText(QCoreApplication.translate("mainWindow", u"Stretch/Upscale", None))
#if QT_CONFIG(tooltip) #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)) 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.mangaBox.setText(QCoreApplication.translate("mainWindow", u"Manga mode", None)) self.mangaBox.setText(QCoreApplication.translate("mainWindow", u"Manga 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.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.rotateBox.setText(QCoreApplication.translate("mainWindow", u"Spread splitter", None)) self.qualityBox.setText(QCoreApplication.translate("mainWindow", u"Panel View 4/2/HQ", None))
#if QT_CONFIG(tooltip)
self.croppingBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Disabled</span></p><p>Disabled</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - Margins<br/></span>Margins</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Margins + page numbers<br/></span>Margins +page numbers</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.croppingBox.setText(QCoreApplication.translate("mainWindow", u"Cropping mode", None))
#if QT_CONFIG(tooltip)
self.outputSplit.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Automatic mode<br/></span>The output will be split automatically.</p><p style='white-space:pre'><span style=\" font-weight:600; text-decoration: underline;\">Checked - Volume mode<br/></span>Every subdirectory will be considered as a separate volume.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.outputSplit.setText(QCoreApplication.translate("mainWindow", u"Output split", None))
#if QT_CONFIG(tooltip) #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)) 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) #endif // QT_CONFIG(tooltip)
self.mozJpegBox.setText(QCoreApplication.translate("mainWindow", u"JPEG/PNG/mozJpeg", None)) self.mozJpegBox.setText(QCoreApplication.translate("mainWindow", u"JPEG/PNG/mozJpeg", None))
#if QT_CONFIG(tooltip) #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)) 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) #endif // QT_CONFIG(tooltip)
self.colorBox.setText(QCoreApplication.translate("mainWindow", u"Color mode", None)) 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.dedupeCoverBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p>Don't duplicate the first page as the cover. Useful for 2 page spread alignment.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.dedupeCoverBox.setText(QCoreApplication.translate("mainWindow", u"De-dupe cover", 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)

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.5.2 ## 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!
################################################################################ ################################################################################

View File

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

@@ -40,7 +40,7 @@ from subprocess import STDOUT, PIPE
from psutil import virtual_memory, disk_usage from psutil import virtual_memory, disk_usage
from html import escape as hescape from html import escape as hescape
from .shared import md5Checksum, getImageFileName, walkSort, walkLevel, sanitizeTrace, subprocess_run_silent from .shared import md5Checksum, getImageFileName, walkSort, walkLevel, sanitizeTrace, subprocess_run
from . import comic2panel from . import comic2panel
from . import image from . import image
from . import comicarchive from . import comicarchive
@@ -504,18 +504,15 @@ def buildEPUB(path, chapternames, tomenumber):
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))
if options.dedupecover:
os.remove(os.path.join(dirpath, afile))
continue
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: if not chapternames and options.chapters:
chapterlist = [] chapterlist = []
@@ -984,8 +981,6 @@ def makeParser():
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("--dedupecover", action="store_true", dest="dedupecover", default=False,
help="De-duplicate the cover as the first page in the book")
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")
@@ -1114,13 +1109,13 @@ def checkTools(source):
if source.endswith('.CB7') or source.endswith('.7Z') or source.endswith('.RAR') or source.endswith('.CBR') or \ if source.endswith('.CB7') or source.endswith('.7Z') or source.endswith('.RAR') or source.endswith('.CBR') or \
source.endswith('.ZIP') or source.endswith('.CBZ'): source.endswith('.ZIP') or source.endswith('.CBZ'):
try: try:
subprocess_run_silent(['7z'], stdout=PIPE, stderr=STDOUT) subprocess_run(['7z'], stdout=PIPE, stderr=STDOUT)
except FileNotFoundError: except FileNotFoundError:
print('ERROR: 7z is missing!') print('ERROR: 7z is missing!')
sys.exit(1) sys.exit(1)
if options.format == 'MOBI': if options.format == 'MOBI':
try: try:
subprocess_run_silent(['kindlegen', '-locale', 'en'], stdout=PIPE, stderr=STDOUT) subprocess_run(['kindlegen', '-locale', 'en'], stdout=PIPE, stderr=STDOUT)
except FileNotFoundError: except FileNotFoundError:
print('ERROR: KindleGen is missing!') print('ERROR: KindleGen is missing!')
sys.exit(1) sys.exit(1)
@@ -1277,7 +1272,7 @@ def makeMOBIWorker(item):
kindlegenError = '' kindlegenError = ''
try: try:
if os.path.getsize(item) < 629145600: if os.path.getsize(item) < 629145600:
output = subprocess_run_silent(['kindlegen', '-dont_append_source', '-locale', 'en', item], output = subprocess_run(['kindlegen', '-dont_append_source', '-locale', 'en', item],
stdout=PIPE, stderr=STDOUT, encoding='UTF-8') stdout=PIPE, stderr=STDOUT, encoding='UTF-8')
for line in output.stdout.splitlines(): for line in output.stdout.splitlines():
# ERROR: Generic error # ERROR: Generic error

View File

@@ -18,15 +18,14 @@
# PERFORMANCE OF THIS SOFTWARE. # PERFORMANCE OF THIS SOFTWARE.
# #
from functools import cached_property
import os import os
import platform import platform
import subprocess
import distro import distro
from shutil import move
from subprocess import STDOUT, PIPE, CalledProcessError from subprocess import STDOUT, PIPE, CalledProcessError
from xml.dom.minidom import parseString from xml.dom.minidom import parseString
from xml.parsers.expat import ExpatError from xml.parsers.expat import ExpatError
from .shared import subprocess_run_silent from .shared import subprocess_run
EXTRACTION_ERROR = 'Failed to extract archive. Try extracting file outside of KCC.' EXTRACTION_ERROR = 'Failed to extract archive. Try extracting file outside of KCC.'
@@ -34,56 +33,78 @@ EXTRACTION_ERROR = 'Failed to extract archive. Try extracting file outside of KC
class ComicArchive: class ComicArchive:
def __init__(self, filepath): def __init__(self, filepath):
self.filepath = filepath self.filepath = filepath
self.type = None
if not os.path.isfile(self.filepath): if not os.path.isfile(self.filepath):
raise OSError('File not found.') raise OSError('File not found.')
try:
process = subprocess_run_silent(['7z', 'l', '-y', '-p1', self.filepath], stderr=STDOUT, stdout=PIPE) @cached_property
except FileNotFoundError: def type(self):
return extraction_commands = [
for line in process.stdout.splitlines(): ['7z', 'l', '-y', '-p1', self.filepath],
if b'Type =' in line: ]
self.type = line.rstrip().decode().split(' = ')[1].upper()
break if distro.id() == 'fedora':
if process.returncode != 0 and distro.id() == 'fedora': extraction_commands.append(
process = subprocess_run_silent(['unrar', 'l', '-y', '-p1', self.filepath], stderr=STDOUT, stdout=PIPE) ['unrar', 'l', '-y', '-p1', self.filepath],
for line in process.stdout.splitlines(): )
if b'Details: ' in line:
self.type = line.rstrip().decode().split(' ')[1].upper() for cmd in extraction_commands:
break try:
if process.returncode != 0: process = subprocess_run(cmd, capture_output=True, check=True)
raise OSError(EXTRACTION_ERROR) for line in process.stdout.splitlines():
if b'Type =' in line:
return line.rstrip().decode().split(' = ')[1].upper()
except FileNotFoundError:
pass
except CalledProcessError:
pass
raise OSError(EXTRACTION_ERROR)
def extract(self, targetdir): def extract(self, targetdir):
if not os.path.isdir(targetdir): if not os.path.isdir(targetdir):
raise OSError('Target directory doesn\'t exist.') raise OSError('Target directory doesn\'t exist.')
try:
process = subprocess_run_silent(['tar', '-xf', self.filepath, '-C', targetdir], missing = []
stdout=PIPE, stderr=STDOUT, check=True)
return targetdir extraction_commands = [
except (FileNotFoundError, CalledProcessError): ['tar', '--exclude', '__MACOSX', '--exclude', '.DS_Store', '--exclude', 'thumbs.db', '--exclude', 'Thumbs.db', '-xf', self.filepath, '-C', targetdir],
pass ['7z', 'x', '-y', '-xr!__MACOSX', '-xr!.DS_Store', '-xr!thumbs.db', '-xr!Thumbs.db', '-o' + targetdir, self.filepath],
process = subprocess_run_silent(['7z', 'x', '-y', '-xr!__MACOSX', '-xr!.DS_Store', '-xr!thumbs.db', '-xr!Thumbs.db', '-o' + targetdir, self.filepath], ]
stdout=PIPE, stderr=STDOUT)
if process.returncode != 0 and distro.id() == 'fedora': if platform.system() == 'Darwin':
process = subprocess_run_silent(['unrar', 'x', '-y', '-x__MACOSX', '-x.DS_Store', '-xthumbs.db', '-xThumbs.db', self.filepath, targetdir] extraction_commands.append(
, stdout=PIPE, stderr=STDOUT) ['unar', self.filepath, '-f', '-o', targetdir]
if process.returncode != 0: )
raise OSError(EXTRACTION_ERROR)
elif process.returncode != 0: if distro.id() == 'fedora':
extraction_commands.append(
['unrar', 'x', '-y', '-x__MACOSX', '-x.DS_Store', '-xthumbs.db', '-xThumbs.db', self.filepath, targetdir]
)
for cmd in extraction_commands:
try:
subprocess_run(cmd, capture_output=True, check=True)
return targetdir
except FileNotFoundError:
missing.append(cmd[0])
except CalledProcessError:
pass
if missing:
raise OSError(f'Extraction failed, install <a href="https://github.com/ciromattia/kcc#7-zip">specialized extraction software.</a> ')
else:
raise OSError(EXTRACTION_ERROR) raise OSError(EXTRACTION_ERROR)
return targetdir
def addFile(self, sourcefile): def addFile(self, sourcefile):
if self.type in ['RAR', 'RAR5']: if self.type in ['RAR', 'RAR5']:
raise NotImplementedError raise NotImplementedError
process = subprocess_run_silent(['7z', 'a', '-y', self.filepath, sourcefile], process = subprocess_run(['7z', 'a', '-y', self.filepath, sourcefile],
stdout=PIPE, stderr=STDOUT) stdout=PIPE, stderr=STDOUT)
if process.returncode != 0: if process.returncode != 0:
raise OSError('Failed to add the file.') raise OSError('Failed to add the file.')
def extractMetadata(self): def extractMetadata(self):
process = subprocess_run_silent(['7z', 'x', '-y', '-so', self.filepath, 'ComicInfo.xml'], process = subprocess_run(['7z', 'x', '-y', '-so', self.filepath, 'ComicInfo.xml'],
stdout=PIPE, stderr=STDOUT) stdout=PIPE, stderr=STDOUT)
if process.returncode != 0: if process.returncode != 0:
raise OSError(EXTRACTION_ERROR) raise OSError(EXTRACTION_ERROR)

View File

@@ -31,7 +31,7 @@ class Kindle:
def findDevice(self): def findDevice(self):
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') and 'rw' in drive[3]): (drive[2] in ('vfat', 'msdos', 'FAT', 'apfs') and 'rw' in drive[3]):
if os.path.isdir(os.path.join(drive[1], 'system')) and \ if os.path.isdir(os.path.join(drive[1], 'system')) and \
os.path.isdir(os.path.join(drive[1], 'documents')): os.path.isdir(os.path.join(drive[1], 'documents')):
return drive[1] return drive[1]

View File

@@ -22,7 +22,7 @@ import os
from hashlib import md5 from hashlib import md5
from html.parser import HTMLParser from html.parser import HTMLParser
import subprocess import subprocess
from distutils.version import StrictVersion from packaging.version import Version
from re import split from re import split
import sys import sys
from traceback import format_tb from traceback import format_tb
@@ -103,7 +103,7 @@ def dependencyCheck(level):
if level > 2: if level > 2:
try: try:
from PySide6.QtCore import qVersion as qtVersion from PySide6.QtCore import qVersion as qtVersion
if StrictVersion('6.5.1') > StrictVersion(qtVersion()): if Version('6.5.1') > Version(qtVersion()):
missing.append('PySide 6.5.1+') missing.append('PySide 6.5.1+')
except ImportError: except ImportError:
missing.append('PySide 6.5.1+') missing.append('PySide 6.5.1+')
@@ -114,7 +114,7 @@ def dependencyCheck(level):
if level > 1: if level > 1:
try: try:
from psutil import __version__ as psutilVersion from psutil import __version__ as psutilVersion
if StrictVersion('5.0.0') > StrictVersion(psutilVersion): if Version('5.0.0') > Version(psutilVersion):
missing.append('psutil 5.0.0+') missing.append('psutil 5.0.0+')
except ImportError: except ImportError:
missing.append('psutil 5.0.0+') missing.append('psutil 5.0.0+')
@@ -123,13 +123,13 @@ def dependencyCheck(level):
from slugify import __version__ as slugifyVersion from slugify import __version__ as slugifyVersion
if isinstance(slugifyVersion, ModuleType): if isinstance(slugifyVersion, ModuleType):
slugifyVersion = slugifyVersion.__version__ slugifyVersion = slugifyVersion.__version__
if StrictVersion('1.2.1') > StrictVersion(slugifyVersion): if Version('1.2.1') > Version(slugifyVersion):
missing.append('python-slugify 1.2.1+') missing.append('python-slugify 1.2.1+')
except ImportError: except ImportError:
missing.append('python-slugify 1.2.1+') missing.append('python-slugify 1.2.1+')
try: try:
from PIL import __version__ as pillowVersion from PIL import __version__ as pillowVersion
if StrictVersion('5.2.0') > StrictVersion(pillowVersion): if Version('5.2.0') > Version(pillowVersion):
missing.append('Pillow 5.2.0+') missing.append('Pillow 5.2.0+')
except ImportError: except ImportError:
missing.append('Pillow 5.2.0+') missing.append('Pillow 5.2.0+')
@@ -137,7 +137,7 @@ def dependencyCheck(level):
print('ERROR: ' + ', '.join(missing) + ' is not installed!') print('ERROR: ' + ', '.join(missing) + ' is not installed!')
sys.exit(1) sys.exit(1)
def subprocess_run_silent(command, **kwargs): def subprocess_run(command, **kwargs):
if (os.name == 'nt'): if (os.name == 'nt'):
kwargs.setdefault('creationflags', subprocess.CREATE_NO_WINDOW) kwargs.setdefault('creationflags', subprocess.CREATE_NO_WINDOW)
return subprocess.run(command, **kwargs) return subprocess.run(command, **kwargs)

View File

@@ -4,6 +4,7 @@ psutil>=5.9.5
requests>=2.31.0 requests>=2.31.0
python-slugify>=1.2.1 python-slugify>=1.2.1
raven>=6.0.0 raven>=6.0.0
packaging>=23.2
mozjpeg-lossless-optimization>=1.1.2 mozjpeg-lossless-optimization>=1.1.2
natsort[fast]>=8.4.0 natsort[fast]>=8.4.0
distro>=1.8.0 distro>=1.8.0

View File

@@ -14,7 +14,6 @@ import os
import platform import platform
import sys import sys
import setuptools import setuptools
import distutils.cmd
from kindlecomicconverter import __version__ from kindlecomicconverter import __version__
NAME = 'KindleComicConverter' NAME = 'KindleComicConverter'
@@ -23,7 +22,7 @@ VERSION = __version__
# noinspection PyUnresolvedReferences # noinspection PyUnresolvedReferences
class BuildBinaryCommand(distutils.cmd.Command): class BuildBinaryCommand(setuptools.Command):
description = 'build binary release' description = 'build binary release'
user_options = [] user_options = []
@@ -42,7 +41,7 @@ class BuildBinaryCommand(distutils.cmd.Command):
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)
elif sys.platform == 'win32': elif sys.platform == 'win32':
os.system('pyinstaller -y -F -i icons\\comic2ebook.ico -n KCC_' + VERSION + ' -w --noupx kcc.py') os.system('pyinstaller --hidden-import=_cffi_backend -y -F -i icons\\comic2ebook.ico -n KCC_' + VERSION + ' -w --noupx kcc.py')
sys.exit(0) sys.exit(0)
elif sys.platform == 'linux': elif sys.platform == 'linux':
os.system( os.system(