mirror of
https://github.com/ciromattia/kcc
synced 2026-04-18 23:19:00 +00:00
Compare commits
64 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a9d0c57ba6 | ||
|
|
5598596650 | ||
|
|
4b7b6d1c58 | ||
|
|
075bc748d1 | ||
|
|
9e318ed33e | ||
|
|
6d21bfa6fa | ||
|
|
132574d57d | ||
|
|
317fb33fd0 | ||
|
|
2189f4b1cb | ||
|
|
462a3cebde | ||
|
|
2a414ef936 | ||
|
|
4adb998896 | ||
|
|
315b6e150d | ||
|
|
5875508597 | ||
|
|
0a4ef31daf | ||
|
|
c99df3308e | ||
|
|
e9482fbd6c | ||
|
|
434fe90b00 | ||
|
|
73a91ec0ae | ||
|
|
e0b1848e09 | ||
|
|
a9360e6bc3 | ||
|
|
ae475e709a | ||
|
|
4769f68265 | ||
|
|
dbe6043542 | ||
|
|
e1a318145d | ||
|
|
a71523b2d4 | ||
|
|
7a5473f530 | ||
|
|
f5a5624112 | ||
|
|
3b7e8dc9a5 | ||
|
|
e637f37ef0 | ||
|
|
6ba690659f | ||
|
|
50eb48fb9b | ||
|
|
4a661a1a17 | ||
|
|
c26383c4b5 | ||
|
|
4e6ee8b59b | ||
|
|
6c6f591e45 | ||
|
|
78c014bf22 | ||
|
|
421e6bcb64 | ||
|
|
5168cd73c4 | ||
|
|
f7f19b99da | ||
|
|
1131bab41f | ||
|
|
8d204668a7 | ||
|
|
99d94ceaa7 | ||
|
|
8ff401cc3a | ||
|
|
fb7d92d737 | ||
|
|
1ca8b2c11b | ||
|
|
40e0b4853b | ||
|
|
005313f978 | ||
|
|
add2ef9faa | ||
|
|
0c98acd606 | ||
|
|
fe902ec213 | ||
|
|
4e9714e6f8 | ||
|
|
1337ad7fe3 | ||
|
|
511c7c1580 | ||
|
|
16dd034af4 | ||
|
|
d22794555f | ||
|
|
ab089adbca | ||
|
|
2c770f4562 | ||
|
|
4c73006bd8 | ||
|
|
1093dbf65a | ||
|
|
df9990c692 | ||
|
|
114f4c9e57 | ||
|
|
c2c477475d | ||
|
|
a0f8d0b5cf |
2
.github/workflows/package-linux.yml
vendored
2
.github/workflows/package-linux.yml
vendored
@@ -23,7 +23,7 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
|
|||||||
2
.github/workflows/package-macos.yml
vendored
2
.github/workflows/package-macos.yml
vendored
@@ -25,7 +25,7 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [ macos-12, macos-14 ]
|
os: [ macos-13, macos-14 ]
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|||||||
@@ -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-20241116
|
||||||
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'
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM --platform=linux/amd64 python:3.11-slim-bullseye as compile-amd64
|
FROM --platform=linux/amd64 python:3.13-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.13-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.13-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 numpy
|
||||||
|
|
||||||
|
|
||||||
######################################################################################
|
######################################################################################
|
||||||
FROM --platform=linux/amd64 python:3.11-slim-bullseye as build-amd64
|
FROM --platform=linux/amd64 python:3.13-slim-bullseye as build-amd64
|
||||||
COPY --from=compile-amd64 /opt/venv /opt/venv
|
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.13-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.13-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-20241116 > /IMAGE_VERSION
|
||||||
|
|
||||||
|
|||||||
35
README.md
35
README.md
@@ -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):
|
||||||
- [](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=D8WNYNPBGDAS2)
|
- [](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=D8WNYNPBGDAS2)
|
||||||
- [](http://flattr.com/thing/2260449/ciromattiakcc-on-GitHub)
|
- [](http://flattr.com/thing/2260449/ciromattiakcc-on-GitHub)
|
||||||
- Paweł Jastrzębski:
|
- Paweł Jastrzębski (active 2013-2019):
|
||||||
- [](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YTTJ4LK2JDHPS)
|
- [](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YTTJ4LK2JDHPS)
|
||||||
- [](https://jastrzeb.ski/donate/)
|
- [](https://jastrzeb.ski/donate/)
|
||||||
- Alex Xu
|
- Alex Xu (active 2023-Present)
|
||||||
- [](https://www.paypal.com/donate/?business=QFJVE7A6LCP6U&no_recurring=0&item_name=Kindle+Comic+Converter¤cy_code=USD)
|
- [](https://www.paypal.com/donate/?business=QFJVE7A6LCP6U&no_recurring=0&item_name=Kindle+Comic+Converter¤cy_code=USD)
|
||||||
|
|
||||||
|
|
||||||
@@ -52,9 +52,9 @@ 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)
|
||||||
|
- [Flatpak mobi conversion stuck](https://github.com/ciromattia/kcc/wiki/Installation#linux)
|
||||||
|
|
||||||
## PREREQUISITES
|
## PREREQUISITES
|
||||||
|
|
||||||
@@ -62,17 +62,17 @@ 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 only required for certain files and advanced features.
|
||||||
|
|
||||||
If you need to install it, refer to the wiki: https://github.com/ciromattia/kcc/wiki/Installation#7-zip
|
KCC will ask you to install if needed.
|
||||||
|
|
||||||
|
Refer to the wiki to install: https://github.com/ciromattia/kcc/wiki/Installation#7-zip
|
||||||
|
|
||||||
## INPUT FORMATS
|
## 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", (1264, 1680), Palette16, 1.8),
|
'KO': ("Kindle Oasis 2/3/Paperwhite 12/Colorsoft 12", (1264, 1680), Palette16, 1.8),
|
||||||
'KS': ("Kindle Scribe", (1860, 2480), Palette16, 1.8),
|
'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,6 +124,9 @@ 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),
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -137,7 +140,8 @@ 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, KoL, KoF, KoS, KoE) [Default=KV]
|
Device profile (Available options: K1, K2, K34, K578, KDX, KPW, KPW5, KV, KO, K11, KS, KoMT, KoG, KoGHD, KoA, KoAHD, KoAH2O, KoAO, KoN, KoC, KoCC, KoL, KoLC, KoF, KoS, KoE)
|
||||||
|
[Default=KV]
|
||||||
-m, --manga-style Manga style (right-to-left reading and splitting)
|
-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
|
||||||
@@ -172,10 +176,15 @@ 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
|
||||||
|
|||||||
@@ -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[fast]>=8.4.0
|
- natsort>=8.4.0
|
||||||
- pip
|
- pip
|
||||||
- pip:
|
- pip:
|
||||||
- mozjpeg-lossless-optimization>=1.1.2
|
- mozjpeg-lossless-optimization>=1.1.2
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
<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>
|
||||||
|
|||||||
232
gui/KCC.ui
232
gui/KCC.ui
@@ -37,20 +37,7 @@
|
|||||||
<property name="bottomMargin">
|
<property name="bottomMargin">
|
||||||
<number>0</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<item row="0" column="2">
|
<item row="5" column="1">
|
||||||
<widget class="QCheckBox" name="qualityBox">
|
|
||||||
<property name="toolTip">
|
|
||||||
<string><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></string>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Panel View 4/2/HQ</string>
|
|
||||||
</property>
|
|
||||||
<property name="tristate">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="4" column="1">
|
|
||||||
<widget class="QCheckBox" name="deleteBox">
|
<widget class="QCheckBox" name="deleteBox">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string>Delete input file(s) or directory. It's not recoverable!</string>
|
<string>Delete input file(s) or directory. It's not recoverable!</string>
|
||||||
@@ -60,63 +47,7 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="1">
|
|
||||||
<widget class="QCheckBox" name="maximizeStrips">
|
|
||||||
<property name="toolTip">
|
|
||||||
<string><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></string>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>1x4 to 2x2 strips</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="2">
|
|
||||||
<widget class="QCheckBox" name="gammaBox">
|
|
||||||
<property name="toolTip">
|
|
||||||
<string><html><head/><body><p style='white-space:pre'>Disable automatic gamma correction.</p></body></html></string>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Custom gamma</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="0">
|
|
||||||
<widget class="QCheckBox" name="borderBox">
|
|
||||||
<property name="toolTip">
|
|
||||||
<string><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></string>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>W/B margins</string>
|
|
||||||
</property>
|
|
||||||
<property name="tristate">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="0">
|
<item row="1" column="0">
|
||||||
<widget class="QCheckBox" name="webtoonBox">
|
|
||||||
<property name="toolTip">
|
|
||||||
<string><html><head/><body><p style='white-space:pre'>Enable special parsing mode for Korean Webtoons.</p></body></html></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><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></string>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Stretch/Upscale</string>
|
|
||||||
</property>
|
|
||||||
<property name="tristate">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="0" column="0">
|
|
||||||
<widget class="QCheckBox" name="mangaBox">
|
<widget class="QCheckBox" name="mangaBox">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string><html><head/><body><p style='white-space:pre'>Enable right-to-left reading.</p></body></html></string>
|
<string><html><head/><body><p style='white-space:pre'>Enable right-to-left reading.</p></body></html></string>
|
||||||
@@ -126,20 +57,17 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="0" column="1">
|
<item row="3" column="1">
|
||||||
<widget class="QCheckBox" name="rotateBox">
|
<widget class="QCheckBox" name="outputSplit">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string><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></string>
|
<string><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></string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Spread splitter</string>
|
<string>Output split</string>
|
||||||
</property>
|
|
||||||
<property name="tristate">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="2">
|
<item row="4" column="2">
|
||||||
<widget class="QCheckBox" name="croppingBox">
|
<widget class="QCheckBox" name="croppingBox">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string><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></string>
|
<string><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></string>
|
||||||
@@ -152,17 +80,7 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="1">
|
<item row="4" column="0">
|
||||||
<widget class="QCheckBox" name="outputSplit">
|
|
||||||
<property name="toolTip">
|
|
||||||
<string><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></string>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Output split</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="3" column="0">
|
|
||||||
<widget class="QCheckBox" name="mozJpegBox">
|
<widget class="QCheckBox" name="mozJpegBox">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string><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></string>
|
<string><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></string>
|
||||||
@@ -175,7 +93,20 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="2">
|
<item row="2" column="1">
|
||||||
|
<widget class="QCheckBox" name="upscaleBox">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string><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></string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Stretch/Upscale</string>
|
||||||
|
</property>
|
||||||
|
<property name="tristate">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="2">
|
||||||
<widget class="QCheckBox" name="colorBox">
|
<widget class="QCheckBox" name="colorBox">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string><html><head/><body><p style='white-space:pre'>Disable conversion to grayscale.</p></body></html></string>
|
<string><html><head/><body><p style='white-space:pre'>Disable conversion to grayscale.</p></body></html></string>
|
||||||
@@ -185,7 +116,96 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="4" column="2">
|
<item row="1" column="1">
|
||||||
|
<widget class="QCheckBox" name="rotateBox">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string><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></string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Spread splitter</string>
|
||||||
|
</property>
|
||||||
|
<property name="tristate">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="2">
|
||||||
|
<widget class="QCheckBox" name="gammaBox">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string><html><head/><body><p style='white-space:pre'>Disable automatic gamma correction.</p></body></html></string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Custom gamma</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="5" column="0">
|
||||||
|
<widget class="QCheckBox" name="spreadShiftBox">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Shift first page to opposite side in landscape for two page spread alignment</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Spread shift</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QCheckBox" name="webtoonBox">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string><html><head/><body><p style='white-space:pre'>Enable special parsing mode for Korean Webtoons.</p></body></html></string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Webtoon mode</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="1">
|
||||||
|
<widget class="QCheckBox" name="maximizeStrips">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string><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></string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>1x4 to 2x2 strips</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QCheckBox" name="borderBox">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string><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></string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>W/B margins</string>
|
||||||
|
</property>
|
||||||
|
<property name="tristate">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="6" column="1">
|
||||||
|
<widget class="QCheckBox" name="noRotateBox">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Do not rotate double page spreads in spread splitter option.</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>No rotate</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="2">
|
||||||
|
<widget class="QCheckBox" name="qualityBox">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string><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></string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Panel View 4/2/HQ</string>
|
||||||
|
</property>
|
||||||
|
<property name="tristate">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="5" column="2">
|
||||||
<widget class="QCheckBox" name="disableProcessingBox">
|
<widget class="QCheckBox" name="disableProcessingBox">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string><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></string>
|
<string><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></string>
|
||||||
@@ -195,13 +215,25 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="4" column="0">
|
<item row="0" column="0">
|
||||||
<widget class="QCheckBox" name="dedupeCoverBox">
|
<widget class="QLineEdit" name="authorEdit">
|
||||||
<property name="toolTip">
|
<property name="sizePolicy">
|
||||||
<string><html><head/><body><p>Don't duplicate the first page as the cover. Useful for 2 page spread alignment.</p></body></html></string>
|
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="focusPolicy">
|
||||||
<string>De-dupe cover</string>
|
<enum>Qt::ClickFocus</enum>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Default Author is KCC</string>
|
||||||
|
</property>
|
||||||
|
<property name="placeholderText">
|
||||||
|
<string>Default Author</string>
|
||||||
|
</property>
|
||||||
|
<property name="clearButtonEnabled">
|
||||||
|
<bool>false</bool>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@@ -277,7 +309,7 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QSlider" name="croppingPowerSlider">
|
<widget class="QSlider" name="croppingPowerSlider">
|
||||||
<property name="maximum">
|
<property name="maximum">
|
||||||
<number>200</number>
|
<number>300</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="singleStep">
|
<property name="singleStep">
|
||||||
<number>1</number>
|
<number>1</number>
|
||||||
@@ -455,7 +487,7 @@
|
|||||||
<string><html><head/><body><p style='white-space:pre'>Shift+Click to edit directory.</p></body></html></string>
|
<string><html><head/><body><p style='white-space:pre'>Shift+Click to edit directory.</p></body></html></string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Editor</string>
|
<string>Metadata Editor</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="KCC.qrc">
|
<iconset resource="KCC.qrc">
|
||||||
@@ -486,7 +518,7 @@
|
|||||||
<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">QListWidget#jobList {background:#ffffff;background-image:url(:/Other/icons/list_background.png);background-position:center center;background-repeat:no-repeat;color:rgb(0,0,0);}</string>
|
<string notr="true"/>
|
||||||
</property>
|
</property>
|
||||||
<property name="selectionMode">
|
<property name="selectionMode">
|
||||||
<enum>QAbstractItemView::NoSelection</enum>
|
<enum>QAbstractItemView::NoSelection</enum>
|
||||||
|
|||||||
BIN
icons/Rmk.png
Normal file
BIN
icons/Rmk.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.0 KiB |
@@ -20,6 +20,8 @@
|
|||||||
|
|
||||||
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)
|
||||||
@@ -28,6 +30,7 @@ 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()
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ a = Analysis(['kcc-c2e.py'],
|
|||||||
pathex=['.'],
|
pathex=['.'],
|
||||||
binaries=[],
|
binaries=[],
|
||||||
datas=[],
|
datas=[],
|
||||||
hiddenimports=[],
|
hiddenimports=['_cffi_backend'],
|
||||||
hookspath=[],
|
hookspath=[],
|
||||||
runtime_hooks=[],
|
runtime_hooks=[],
|
||||||
excludes=[],
|
excludes=[],
|
||||||
|
|||||||
@@ -20,6 +20,8 @@
|
|||||||
|
|
||||||
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)
|
||||||
@@ -28,6 +30,7 @@ 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()
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ a = Analysis(['kcc-c2p.py'],
|
|||||||
pathex=['.'],
|
pathex=['.'],
|
||||||
binaries=[],
|
binaries=[],
|
||||||
datas=[],
|
datas=[],
|
||||||
hiddenimports=[],
|
hiddenimports=['_cffi_backend'],
|
||||||
hookspath=[],
|
hookspath=[],
|
||||||
runtime_hooks=[],
|
runtime_hooks=[],
|
||||||
excludes=[],
|
excludes=[],
|
||||||
|
|||||||
104
kcc.py
104
kcc.py
@@ -18,62 +18,76 @@
|
|||||||
# 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)
|
||||||
|
|
||||||
# OS specific workarounds
|
def modify_path():
|
||||||
import os
|
if platform.system() == 'Darwin':
|
||||||
if sys.platform.startswith('darwin'):
|
mac_paths = [
|
||||||
# prioritize KC2 since it optionally also installs KP3
|
'/Applications/Kindle Comic Creator/Kindle Comic Creator.app/Contents/MacOS',
|
||||||
mac_paths = [
|
'/Applications/Kindle Previewer 3.app/Contents/lib/fc/bin/',
|
||||||
'/Applications/Kindle Comic Creator/Kindle Comic Creator.app/Contents/MacOS',
|
]
|
||||||
'/Applications/Kindle Previewer 3.app/Contents/lib/fc/bin/',
|
if getattr(sys, 'frozen', False):
|
||||||
]
|
os.environ['PATH'] += os.pathsep + os.pathsep.join(mac_paths +
|
||||||
if getattr(sys, 'frozen', False):
|
[
|
||||||
os.environ['PATH'] += os.pathsep + os.pathsep.join(mac_paths +
|
'/opt/homebrew/bin',
|
||||||
[
|
'/usr/local/bin',
|
||||||
'/opt/homebrew/bin',
|
'/usr/bin',
|
||||||
'/usr/local/bin',
|
'/bin',
|
||||||
'/usr/bin',
|
]
|
||||||
'/bin',
|
)
|
||||||
]
|
os.chdir(os.path.dirname(os.path.abspath(sys.executable)))
|
||||||
)
|
else:
|
||||||
os.chdir(os.path.dirname(os.path.abspath(sys.executable)))
|
os.environ['PATH'] += os.pathsep + os.pathsep.join(mac_paths)
|
||||||
else:
|
os.chdir(os.path.dirname(os.path.abspath(__file__)))
|
||||||
os.environ['PATH'] += os.pathsep + os.pathsep.join(mac_paths)
|
|
||||||
os.chdir(os.path.dirname(os.path.abspath(__file__)))
|
elif platform.system() == 'Linux':
|
||||||
elif sys.platform.startswith('win'):
|
if getattr(sys, 'frozen', False):
|
||||||
# prioritize KC2 since it optionally also installs KP3
|
os.environ['PATH'] += os.pathsep + os.pathsep.join(
|
||||||
win_paths = [
|
[
|
||||||
os.path.expandvars('%LOCALAPPDATA%\\Amazon\\KC2'),
|
str(Path.home() / ".local" / "bin"),
|
||||||
os.path.expandvars('%LOCALAPPDATA%\\Amazon\\Kindle Previewer 3\\lib\\fc\\bin\\'),
|
'/opt/homebrew/bin',
|
||||||
'C:\\Apps\\Kindle Previewer 3\\lib\\fc\\bin',
|
'/usr/local/bin',
|
||||||
'D:\\Apps\\Kindle Previewer 3\\lib\\fc\\bin',
|
'/usr/bin',
|
||||||
'E:\\Apps\\Kindle Previewer 3\\lib\\fc\\bin',
|
'/bin',
|
||||||
'C:\\Program Files\\7-Zip',
|
]
|
||||||
'D:\\Program Files\\7-Zip',
|
)
|
||||||
'E:\\Program Files\\7-Zip',
|
os.chdir(os.path.dirname(os.path.abspath(sys.executable)))
|
||||||
]
|
else:
|
||||||
if getattr(sys, 'frozen', False):
|
os.chdir(os.path.dirname(os.path.abspath(__file__)))
|
||||||
os.environ['PATH'] += os.pathsep + os.pathsep.join(win_paths)
|
|
||||||
os.chdir(os.path.dirname(os.path.abspath(sys.executable)))
|
elif platform.system() == 'Windows':
|
||||||
else:
|
win_paths = [
|
||||||
os.environ['PATH'] += os.pathsep + os.pathsep.join(win_paths)
|
os.path.expandvars('%LOCALAPPDATA%\\Amazon\\KC2'),
|
||||||
os.chdir(os.path.dirname(os.path.abspath(__file__)))
|
os.path.expandvars('%LOCALAPPDATA%\\Amazon\\Kindle Previewer 3\\lib\\fc\\bin\\'),
|
||||||
# Load additional Sentry configuration
|
os.path.expandvars('%UserProfile%\\Kindle Previewer 3\\lib\\fc\\bin\\'),
|
||||||
# if getattr(sys, 'frozen', False):
|
'C:\\Apps\\Kindle Previewer 3\\lib\\fc\\bin',
|
||||||
# try:
|
'D:\\Apps\\Kindle Previewer 3\\lib\\fc\\bin',
|
||||||
# import kindlecomicconverter.sentry
|
'E:\\Apps\\Kindle Previewer 3\\lib\\fc\\bin',
|
||||||
# except ImportError:
|
'C:\\Program Files\\7-Zip',
|
||||||
# pass
|
'D:\\Program Files\\7-Zip',
|
||||||
|
'E:\\Program Files\\7-Zip',
|
||||||
|
]
|
||||||
|
if getattr(sys, 'frozen', False):
|
||||||
|
os.environ['PATH'] += os.pathsep + os.pathsep.join(win_paths)
|
||||||
|
os.chdir(os.path.dirname(os.path.abspath(sys.executable)))
|
||||||
|
else:
|
||||||
|
os.environ['PATH'] += os.pathsep + os.pathsep.join(win_paths)
|
||||||
|
os.chdir(os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
|
||||||
|
|
||||||
from multiprocessing import freeze_support, set_start_method
|
from 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()
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -106,6 +106,8 @@ class Icons:
|
|||||||
self.deviceKindle.addPixmap(QtGui.QPixmap(":/Devices/icons/Kindle.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
self.deviceKindle.addPixmap(QtGui.QPixmap(":/Devices/icons/Kindle.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
||||||
self.deviceKobo = QtGui.QIcon()
|
self.deviceKobo = QtGui.QIcon()
|
||||||
self.deviceKobo.addPixmap(QtGui.QPixmap(":/Devices/icons/Kobo.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
self.deviceKobo.addPixmap(QtGui.QPixmap(":/Devices/icons/Kobo.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
||||||
|
self.deviceRmk = QtGui.QIcon()
|
||||||
|
self.deviceRmk.addPixmap(QtGui.QPixmap(":/Devices/icons/Rmk.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
||||||
self.deviceOther = QtGui.QIcon()
|
self.deviceOther = QtGui.QIcon()
|
||||||
self.deviceOther.addPixmap(QtGui.QPixmap(":/Devices/icons/Other.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
self.deviceOther.addPixmap(QtGui.QPixmap(":/Devices/icons/Other.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
||||||
|
|
||||||
@@ -146,9 +148,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 +256,10 @@ 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():
|
if GUI.spreadShiftBox.isChecked():
|
||||||
options.dedupecover = True
|
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:
|
||||||
@@ -265,6 +269,8 @@ class WorkerThread(QtCore.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
|
||||||
@@ -380,7 +386,7 @@ class WorkerThread(QtCore.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()
|
k = kindle.Kindle(options.profile)
|
||||||
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(
|
||||||
@@ -687,7 +693,6 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
|||||||
# We still fill original text field with transparent content to trigger creation of horizontal scrollbar
|
# We still fill original text field with transparent content to trigger creation of horizontal scrollbar
|
||||||
item.setForeground(QtGui.QColor('transparent'))
|
item.setForeground(QtGui.QColor('transparent'))
|
||||||
label = QtWidgets.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)
|
||||||
@@ -792,7 +797,8 @@ 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,
|
'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()
|
||||||
@@ -846,12 +852,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
|
||||||
@@ -934,6 +940,12 @@ 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,
|
||||||
@@ -984,13 +996,21 @@ 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 11",
|
|
||||||
"Kindle PW 11",
|
"Kindle PW 11",
|
||||||
|
"Kindle 11",
|
||||||
"Kindle Oasis 9/10",
|
"Kindle Oasis 9/10",
|
||||||
"Separator",
|
"Separator",
|
||||||
"Kobo Clara 2E",
|
"Kobo Clara 2E",
|
||||||
@@ -1001,6 +1021,10 @@ 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",
|
||||||
@@ -1041,12 +1065,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
|
||||||
@@ -1089,6 +1113,8 @@ 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:
|
||||||
|
|||||||
@@ -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.8.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,6 +4908,138 @@ 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\
|
||||||
@@ -11397,6 +11529,10 @@ 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\
|
||||||
@@ -11463,11 +11599,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\x1b\
|
\x00\x00\x00J\x00\x02\x00\x00\x00\x01\x00\x00\x00\x1c\
|
||||||
\x00\x00\x00\x00\x00\x00\x00\x00\
|
\x00\x00\x00\x00\x00\x00\x00\x00\
|
||||||
\x00\x00\x00&\x00\x02\x00\x00\x00\x01\x00\x00\x00\x13\
|
\x00\x00\x00&\x00\x02\x00\x00\x00\x01\x00\x00\x00\x14\
|
||||||
\x00\x00\x00\x00\x00\x00\x00\x00\
|
\x00\x00\x00\x00\x00\x00\x00\x00\
|
||||||
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x0f\
|
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x10\
|
||||||
\x00\x00\x00\x00\x00\x00\x00\x00\
|
\x00\x00\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\
|
||||||
@@ -11475,47 +11611,49 @@ 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\xac\x00\x00\x00\x00\x00\x01\x00\x02&\xd7\
|
\x00\x00\x01\xc0\x00\x00\x00\x00\x00\x01\x00\x02.\xed\
|
||||||
\x00\x00\x01\x88;p\xbcB\
|
\x00\x00\x01\x88;p\xbcB\
|
||||||
\x00\x00\x01\xea\x00\x00\x00\x00\x00\x01\x00\x02{q\
|
\x00\x00\x01\xfe\x00\x00\x00\x00\x00\x01\x00\x02\x83\x87\
|
||||||
\x00\x00\x01\x88;p\xbcB\
|
\x00\x00\x01\x88;p\xbcB\
|
||||||
\x00\x00\x01\xd6\x00\x00\x00\x00\x00\x01\x00\x02Qv\
|
\x00\x00\x01\xea\x00\x00\x00\x00\x00\x01\x00\x02Y\x8c\
|
||||||
\x00\x00\x01\x88;p\xbcB\
|
\x00\x00\x01\x88;p\xbcB\
|
||||||
\x00\x00\x01\xc2\x00\x00\x00\x00\x00\x01\x00\x02F\x13\
|
\x00\x00\x01\xd6\x00\x00\x00\x00\x00\x01\x00\x02N)\
|
||||||
\x00\x00\x01\x89\x89D9.\
|
\x00\x00\x01\x89\x89D9.\
|
||||||
\x00\x00\x00X\x00\x02\x00\x00\x00\x03\x00\x00\x00\x0c\
|
\x00\x00\x00X\x00\x02\x00\x00\x00\x04\x00\x00\x00\x0c\
|
||||||
\x00\x00\x00\x00\x00\x00\x00\x00\
|
\x00\x00\x00\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\x88;p\xbcB\
|
||||||
|
\x00\x00\x00\xbc\x00\x00\x00\x00\x00\x01\x00\x011\xef\
|
||||||
|
\x00\x00\x01\x94\x1a\xa2\xa2\x92\
|
||||||
\x00\x00\x00\x8c\x00\x00\x00\x00\x00\x01\x00\x01\x1d\x90\
|
\x00\x00\x00\x8c\x00\x00\x00\x00\x00\x01\x00\x01\x1d\x90\
|
||||||
\x00\x00\x01\x88;p\xbcB\
|
\x00\x00\x01\x88;p\xbcB\
|
||||||
\x00\x00\x00\xbc\x00\x00\x00\x00\x00\x01\x00\x011\xef\
|
\x00\x00\x00\xd0\x00\x00\x00\x00\x00\x01\x00\x01:\x05\
|
||||||
\x00\x00\x01\x88;p\xbcB\
|
\x00\x00\x01\x88;p\xbcB\
|
||||||
\x00\x00\x00X\x00\x02\x00\x00\x00\x03\x00\x00\x00\x10\
|
\x00\x00\x00X\x00\x02\x00\x00\x00\x03\x00\x00\x00\x11\
|
||||||
\x00\x00\x00\x00\x00\x00\x00\x00\
|
\x00\x00\x00\x00\x00\x00\x00\x00\
|
||||||
\x00\x00\x02.\x00\x00\x00\x00\x00\x01\x00\x02\xad\xbd\
|
\x00\x00\x02B\x00\x00\x00\x00\x00\x01\x00\x02\xb5\xd3\
|
||||||
\x00\x00\x01\x88;p\xbcJ\
|
\x00\x00\x01\x88;p\xbcJ\
|
||||||
\x00\x00\x02\x00\x00\x00\x00\x00\x00\x01\x00\x02\x97\xc0\
|
\x00\x00\x02\x14\x00\x00\x00\x00\x00\x01\x00\x02\x9f\xd6\
|
||||||
\x00\x00\x01\x88;p\xbcI\
|
\x00\x00\x01\x88;p\xbcI\
|
||||||
\x00\x00\x02\x16\x00\x00\x00\x00\x00\x01\x00\x02\xa1\x1d\
|
\x00\x00\x02*\x00\x00\x00\x00\x00\x01\x00\x02\xa93\
|
||||||
\x00\x00\x01\x88;p\xbcI\
|
\x00\x00\x01\x88;p\xbcI\
|
||||||
\x00\x00\x00X\x00\x02\x00\x00\x00\x07\x00\x00\x00\x14\
|
\x00\x00\x00X\x00\x02\x00\x00\x00\x07\x00\x00\x00\x15\
|
||||||
\x00\x00\x00\x00\x00\x00\x00\x00\
|
\x00\x00\x00\x00\x00\x00\x00\x00\
|
||||||
\x00\x00\x01\x08\x00\x00\x00\x00\x00\x01\x00\x01H\x9b\
|
\x00\x00\x01\x1c\x00\x00\x00\x00\x00\x01\x00\x01P\xb1\
|
||||||
\x00\x00\x01\x88;p\xbcJ\
|
\x00\x00\x01\x88;p\xbcJ\
|
||||||
\x00\x00\x01\x1e\x00\x00\x00\x00\x00\x01\x00\x01qC\
|
\x00\x00\x012\x00\x00\x00\x00\x00\x01\x00\x01yY\
|
||||||
\x00\x00\x01\x88;p\xbcI\
|
\x00\x00\x01\x88;p\xbcI\
|
||||||
\x00\x00\x01\x80\x00\x00\x00\x00\x00\x01\x00\x01\xca\x17\
|
\x00\x00\x01\x94\x00\x00\x00\x00\x00\x01\x00\x01\xd2-\
|
||||||
\x00\x00\x01\x88;p\xbcI\
|
\x00\x00\x01\x94\x1a\xbb\xe8\xa7\
|
||||||
\x00\x00\x01f\x00\x00\x00\x00\x00\x01\x00\x01\x84\xd0\
|
\x00\x00\x01z\x00\x00\x00\x00\x00\x01\x00\x01\x8c\xe6\
|
||||||
\x00\x00\x01\x88;p\xbcH\
|
\x00\x00\x01\x88;p\xbcH\
|
||||||
\x00\x00\x00\xf0\x00\x00\x00\x00\x00\x01\x00\x01D<\
|
\x00\x00\x01\x04\x00\x00\x00\x00\x00\x01\x00\x01LR\
|
||||||
\x00\x00\x01\x88;p\xbcF\
|
\x00\x00\x01\x88;p\xbcF\
|
||||||
\x00\x00\x00\xd4\x00\x00\x00\x00\x00\x01\x00\x017\xd3\
|
\x00\x00\x00\xe8\x00\x00\x00\x00\x00\x01\x00\x01?\xe9\
|
||||||
\x00\x00\x01\x88;p\xbcH\
|
\x00\x00\x01\x88;p\xbcH\
|
||||||
\x00\x00\x01@\x00\x00\x00\x00\x00\x01\x00\x01z\x9a\
|
\x00\x00\x01T\x00\x00\x00\x00\x00\x01\x00\x01\x82\xb0\
|
||||||
\x00\x00\x01\x88;p\xbcH\
|
\x00\x00\x01\x88;p\xbcH\
|
||||||
\x00\x00\x00X\x00\x02\x00\x00\x00\x01\x00\x00\x00\x1c\
|
\x00\x00\x00X\x00\x02\x00\x00\x00\x01\x00\x00\x00\x1d\
|
||||||
\x00\x00\x00\x00\x00\x00\x00\x00\
|
\x00\x00\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\x88;p\xbcH\
|
||||||
|
|||||||
@@ -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.8.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,10 +16,10 @@ 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, QListWidget,
|
QGridLayout, QHBoxLayout, QLabel, QLineEdit,
|
||||||
QListWidgetItem, QMainWindow, QProgressBar, QPushButton,
|
QListWidget, QListWidgetItem, QMainWindow, QProgressBar,
|
||||||
QSizePolicy, QSlider, QSpinBox, QStatusBar,
|
QPushButton, QSizePolicy, QSlider, QSpinBox,
|
||||||
QWidget)
|
QStatusBar, QWidget)
|
||||||
from . import KCC_rc
|
from . import KCC_rc
|
||||||
|
|
||||||
class Ui_mainWindow(object):
|
class Ui_mainWindow(object):
|
||||||
@@ -28,7 +28,7 @@ class Ui_mainWindow(object):
|
|||||||
mainWindow.setObjectName(u"mainWindow")
|
mainWindow.setObjectName(u"mainWindow")
|
||||||
mainWindow.resize(450, 400)
|
mainWindow.resize(450, 400)
|
||||||
icon = QIcon()
|
icon = QIcon()
|
||||||
icon.addFile(u":/Icon/icons/comic2ebook.png", QSize(), QIcon.Normal, QIcon.Off)
|
icon.addFile(u":/Icon/icons/comic2ebook.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
|
||||||
mainWindow.setWindowIcon(icon)
|
mainWindow.setWindowIcon(icon)
|
||||||
self.centralWidget = QWidget(mainWindow)
|
self.centralWidget = QWidget(mainWindow)
|
||||||
self.centralWidget.setObjectName(u"centralWidget")
|
self.centralWidget.setObjectName(u"centralWidget")
|
||||||
@@ -40,86 +40,103 @@ 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.qualityBox.setObjectName(u"qualityBox")
|
|
||||||
self.qualityBox.setTristate(True)
|
|
||||||
|
|
||||||
self.gridLayout_2.addWidget(self.qualityBox, 0, 2, 1, 1)
|
|
||||||
|
|
||||||
self.deleteBox = QCheckBox(self.optionWidget)
|
self.deleteBox = QCheckBox(self.optionWidget)
|
||||||
self.deleteBox.setObjectName(u"deleteBox")
|
self.deleteBox.setObjectName(u"deleteBox")
|
||||||
|
|
||||||
self.gridLayout_2.addWidget(self.deleteBox, 4, 1, 1, 1)
|
self.gridLayout_2.addWidget(self.deleteBox, 5, 1, 1, 1)
|
||||||
|
|
||||||
self.maximizeStrips = QCheckBox(self.optionWidget)
|
|
||||||
self.maximizeStrips.setObjectName(u"maximizeStrips")
|
|
||||||
|
|
||||||
self.gridLayout_2.addWidget(self.maximizeStrips, 3, 1, 1, 1)
|
|
||||||
|
|
||||||
self.gammaBox = QCheckBox(self.optionWidget)
|
|
||||||
self.gammaBox.setObjectName(u"gammaBox")
|
|
||||||
|
|
||||||
self.gridLayout_2.addWidget(self.gammaBox, 1, 2, 1, 1)
|
|
||||||
|
|
||||||
self.borderBox = QCheckBox(self.optionWidget)
|
|
||||||
self.borderBox.setObjectName(u"borderBox")
|
|
||||||
self.borderBox.setTristate(True)
|
|
||||||
|
|
||||||
self.gridLayout_2.addWidget(self.borderBox, 2, 0, 1, 1)
|
|
||||||
|
|
||||||
self.webtoonBox = QCheckBox(self.optionWidget)
|
|
||||||
self.webtoonBox.setObjectName(u"webtoonBox")
|
|
||||||
|
|
||||||
self.gridLayout_2.addWidget(self.webtoonBox, 1, 0, 1, 1)
|
|
||||||
|
|
||||||
self.upscaleBox = QCheckBox(self.optionWidget)
|
|
||||||
self.upscaleBox.setObjectName(u"upscaleBox")
|
|
||||||
self.upscaleBox.setTristate(True)
|
|
||||||
|
|
||||||
self.gridLayout_2.addWidget(self.upscaleBox, 1, 1, 1, 1)
|
|
||||||
|
|
||||||
self.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, 1, 0, 1, 1)
|
||||||
|
|
||||||
self.rotateBox = QCheckBox(self.optionWidget)
|
self.outputSplit = QCheckBox(self.optionWidget)
|
||||||
self.rotateBox.setObjectName(u"rotateBox")
|
self.outputSplit.setObjectName(u"outputSplit")
|
||||||
self.rotateBox.setTristate(True)
|
|
||||||
|
|
||||||
self.gridLayout_2.addWidget(self.rotateBox, 0, 1, 1, 1)
|
self.gridLayout_2.addWidget(self.outputSplit, 3, 1, 1, 1)
|
||||||
|
|
||||||
self.croppingBox = QCheckBox(self.optionWidget)
|
self.croppingBox = QCheckBox(self.optionWidget)
|
||||||
self.croppingBox.setObjectName(u"croppingBox")
|
self.croppingBox.setObjectName(u"croppingBox")
|
||||||
self.croppingBox.setTristate(True)
|
self.croppingBox.setTristate(True)
|
||||||
|
|
||||||
self.gridLayout_2.addWidget(self.croppingBox, 3, 2, 1, 1)
|
self.gridLayout_2.addWidget(self.croppingBox, 4, 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")
|
||||||
self.mozJpegBox.setTristate(True)
|
self.mozJpegBox.setTristate(True)
|
||||||
|
|
||||||
self.gridLayout_2.addWidget(self.mozJpegBox, 3, 0, 1, 1)
|
self.gridLayout_2.addWidget(self.mozJpegBox, 4, 0, 1, 1)
|
||||||
|
|
||||||
|
self.upscaleBox = QCheckBox(self.optionWidget)
|
||||||
|
self.upscaleBox.setObjectName(u"upscaleBox")
|
||||||
|
self.upscaleBox.setTristate(True)
|
||||||
|
|
||||||
|
self.gridLayout_2.addWidget(self.upscaleBox, 2, 1, 1, 1)
|
||||||
|
|
||||||
self.colorBox = QCheckBox(self.optionWidget)
|
self.colorBox = QCheckBox(self.optionWidget)
|
||||||
self.colorBox.setObjectName(u"colorBox")
|
self.colorBox.setObjectName(u"colorBox")
|
||||||
|
|
||||||
self.gridLayout_2.addWidget(self.colorBox, 2, 2, 1, 1)
|
self.gridLayout_2.addWidget(self.colorBox, 3, 2, 1, 1)
|
||||||
|
|
||||||
|
self.rotateBox = QCheckBox(self.optionWidget)
|
||||||
|
self.rotateBox.setObjectName(u"rotateBox")
|
||||||
|
self.rotateBox.setTristate(True)
|
||||||
|
|
||||||
|
self.gridLayout_2.addWidget(self.rotateBox, 1, 1, 1, 1)
|
||||||
|
|
||||||
|
self.gammaBox = QCheckBox(self.optionWidget)
|
||||||
|
self.gammaBox.setObjectName(u"gammaBox")
|
||||||
|
|
||||||
|
self.gridLayout_2.addWidget(self.gammaBox, 2, 2, 1, 1)
|
||||||
|
|
||||||
|
self.spreadShiftBox = QCheckBox(self.optionWidget)
|
||||||
|
self.spreadShiftBox.setObjectName(u"spreadShiftBox")
|
||||||
|
|
||||||
|
self.gridLayout_2.addWidget(self.spreadShiftBox, 5, 0, 1, 1)
|
||||||
|
|
||||||
|
self.webtoonBox = QCheckBox(self.optionWidget)
|
||||||
|
self.webtoonBox.setObjectName(u"webtoonBox")
|
||||||
|
|
||||||
|
self.gridLayout_2.addWidget(self.webtoonBox, 2, 0, 1, 1)
|
||||||
|
|
||||||
|
self.maximizeStrips = QCheckBox(self.optionWidget)
|
||||||
|
self.maximizeStrips.setObjectName(u"maximizeStrips")
|
||||||
|
|
||||||
|
self.gridLayout_2.addWidget(self.maximizeStrips, 4, 1, 1, 1)
|
||||||
|
|
||||||
|
self.borderBox = QCheckBox(self.optionWidget)
|
||||||
|
self.borderBox.setObjectName(u"borderBox")
|
||||||
|
self.borderBox.setTristate(True)
|
||||||
|
|
||||||
|
self.gridLayout_2.addWidget(self.borderBox, 3, 0, 1, 1)
|
||||||
|
|
||||||
|
self.noRotateBox = QCheckBox(self.optionWidget)
|
||||||
|
self.noRotateBox.setObjectName(u"noRotateBox")
|
||||||
|
|
||||||
|
self.gridLayout_2.addWidget(self.noRotateBox, 6, 1, 1, 1)
|
||||||
|
|
||||||
|
self.qualityBox = QCheckBox(self.optionWidget)
|
||||||
|
self.qualityBox.setObjectName(u"qualityBox")
|
||||||
|
self.qualityBox.setTristate(True)
|
||||||
|
|
||||||
|
self.gridLayout_2.addWidget(self.qualityBox, 1, 2, 1, 1)
|
||||||
|
|
||||||
self.disableProcessingBox = QCheckBox(self.optionWidget)
|
self.disableProcessingBox = 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, 5, 2, 1, 1)
|
||||||
|
|
||||||
self.dedupeCoverBox = QCheckBox(self.optionWidget)
|
self.authorEdit = QLineEdit(self.optionWidget)
|
||||||
self.dedupeCoverBox.setObjectName(u"dedupeCoverBox")
|
self.authorEdit.setObjectName(u"authorEdit")
|
||||||
|
sizePolicy = QSizePolicy(QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Fixed)
|
||||||
|
sizePolicy.setHorizontalStretch(0)
|
||||||
|
sizePolicy.setVerticalStretch(0)
|
||||||
|
sizePolicy.setHeightForWidth(self.authorEdit.sizePolicy().hasHeightForWidth())
|
||||||
|
self.authorEdit.setSizePolicy(sizePolicy)
|
||||||
|
self.authorEdit.setFocusPolicy(Qt.ClickFocus)
|
||||||
|
self.authorEdit.setClearButtonEnabled(False)
|
||||||
|
|
||||||
self.gridLayout_2.addWidget(self.dedupeCoverBox, 4, 0, 1, 1)
|
self.gridLayout_2.addWidget(self.authorEdit, 0, 0, 1, 1)
|
||||||
|
|
||||||
|
|
||||||
self.gridLayout.addWidget(self.optionWidget, 5, 0, 1, 2)
|
self.gridLayout.addWidget(self.optionWidget, 5, 0, 1, 2)
|
||||||
@@ -159,7 +176,7 @@ 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(200)
|
self.croppingPowerSlider.setMaximum(300)
|
||||||
self.croppingPowerSlider.setSingleStep(1)
|
self.croppingPowerSlider.setSingleStep(1)
|
||||||
self.croppingPowerSlider.setOrientation(Qt.Horizontal)
|
self.croppingPowerSlider.setOrientation(Qt.Horizontal)
|
||||||
|
|
||||||
@@ -170,11 +187,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")
|
||||||
sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed)
|
sizePolicy1 = QSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Fixed)
|
||||||
sizePolicy.setHorizontalStretch(0)
|
sizePolicy1.setHorizontalStretch(0)
|
||||||
sizePolicy.setVerticalStretch(0)
|
sizePolicy1.setVerticalStretch(0)
|
||||||
sizePolicy.setHeightForWidth(self.buttonWidget.sizePolicy().hasHeightForWidth())
|
sizePolicy1.setHeightForWidth(self.buttonWidget.sizePolicy().hasHeightForWidth())
|
||||||
self.buttonWidget.setSizePolicy(sizePolicy)
|
self.buttonWidget.setSizePolicy(sizePolicy1)
|
||||||
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)
|
||||||
@@ -182,7 +199,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.Normal, QIcon.Off)
|
icon1.addFile(u":/Other/icons/folder_new.png", QSize(), QIcon.Mode.Normal, QIcon.State.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)
|
||||||
@@ -191,7 +208,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.Normal, QIcon.Off)
|
icon2.addFile(u":/Other/icons/document_new.png", QSize(), QIcon.Mode.Normal, QIcon.State.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)
|
||||||
@@ -215,7 +232,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.Normal, QIcon.Off)
|
icon3.addFile(u":/Other/icons/convert.png", QSize(), QIcon.Mode.Normal, QIcon.State.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)
|
||||||
@@ -224,7 +241,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.Normal, QIcon.Off)
|
icon4.addFile(u":/Other/icons/clear.png", QSize(), QIcon.Mode.Normal, QIcon.State.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)
|
||||||
@@ -247,7 +264,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.Normal, QIcon.Off)
|
icon5.addFile(u":/Other/icons/editor.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
|
||||||
self.editorButton.setIcon(icon5)
|
self.editorButton.setIcon(icon5)
|
||||||
|
|
||||||
self.horizontalLayout.addWidget(self.editorButton)
|
self.horizontalLayout.addWidget(self.editorButton)
|
||||||
@@ -256,7 +273,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.Normal, QIcon.Off)
|
icon6.addFile(u":/Other/icons/wiki.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
|
||||||
self.wikiButton.setIcon(icon6)
|
self.wikiButton.setIcon(icon6)
|
||||||
|
|
||||||
self.horizontalLayout.addWidget(self.wikiButton)
|
self.horizontalLayout.addWidget(self.wikiButton)
|
||||||
@@ -266,7 +283,7 @@ 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"QListWidget#jobList {background:#ffffff;background-image:url(:/Other/icons/list_background.png);background-position:center center;background-repeat:no-repeat;color:rgb(0,0,0);}")
|
self.jobList.setStyleSheet(u"")
|
||||||
self.jobList.setSelectionMode(QAbstractItemView.NoSelection)
|
self.jobList.setSelectionMode(QAbstractItemView.NoSelection)
|
||||||
self.jobList.setVerticalScrollMode(QAbstractItemView.ScrollPerPixel)
|
self.jobList.setVerticalScrollMode(QAbstractItemView.ScrollPerPixel)
|
||||||
self.jobList.setHorizontalScrollMode(QAbstractItemView.ScrollPerPixel)
|
self.jobList.setHorizontalScrollMode(QAbstractItemView.ScrollPerPixel)
|
||||||
@@ -290,11 +307,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")
|
||||||
sizePolicy1 = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred)
|
sizePolicy2 = QSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Preferred)
|
||||||
sizePolicy1.setHorizontalStretch(0)
|
sizePolicy2.setHorizontalStretch(0)
|
||||||
sizePolicy1.setVerticalStretch(0)
|
sizePolicy2.setVerticalStretch(0)
|
||||||
sizePolicy1.setHeightForWidth(self.hLabel.sizePolicy().hasHeightForWidth())
|
sizePolicy2.setHeightForWidth(self.hLabel.sizePolicy().hasHeightForWidth())
|
||||||
self.hLabel.setSizePolicy(sizePolicy1)
|
self.hLabel.setSizePolicy(sizePolicy2)
|
||||||
|
|
||||||
self.gridLayout_3.addWidget(self.hLabel, 0, 2, 1, 1)
|
self.gridLayout_3.addWidget(self.hLabel, 0, 2, 1, 1)
|
||||||
|
|
||||||
@@ -306,8 +323,8 @@ class Ui_mainWindow(object):
|
|||||||
|
|
||||||
self.wLabel = QLabel(self.customWidget)
|
self.wLabel = QLabel(self.customWidget)
|
||||||
self.wLabel.setObjectName(u"wLabel")
|
self.wLabel.setObjectName(u"wLabel")
|
||||||
sizePolicy1.setHeightForWidth(self.wLabel.sizePolicy().hasHeightForWidth())
|
sizePolicy2.setHeightForWidth(self.wLabel.sizePolicy().hasHeightForWidth())
|
||||||
self.wLabel.setSizePolicy(sizePolicy1)
|
self.wLabel.setSizePolicy(sizePolicy2)
|
||||||
|
|
||||||
self.gridLayout_3.addWidget(self.wLabel, 0, 0, 1, 1)
|
self.gridLayout_3.addWidget(self.wLabel, 0, 0, 1, 1)
|
||||||
|
|
||||||
@@ -359,66 +376,74 @@ 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.qualityBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - 4 panels<br/></span>Zoom each corner separately.</p><p style='white-space:pre'><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - 2 panels<br/></span>Zoom only the top and bottom of the page.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - 4 high-quality panels<br/></span>Zoom each corner separately. Try to increase the quality of magnification. Check wiki for more details.</p></body></html>", None))
|
|
||||||
#endif // QT_CONFIG(tooltip)
|
|
||||||
self.qualityBox.setText(QCoreApplication.translate("mainWindow", u"Panel View 4/2/HQ", None))
|
|
||||||
#if QT_CONFIG(tooltip)
|
#if QT_CONFIG(tooltip)
|
||||||
self.deleteBox.setToolTip(QCoreApplication.translate("mainWindow", u"Delete input file(s) or directory. It's not recoverable!", None))
|
self.deleteBox.setToolTip(QCoreApplication.translate("mainWindow", u"Delete input file(s) or directory. It's not recoverable!", None))
|
||||||
#endif // QT_CONFIG(tooltip)
|
#endif // QT_CONFIG(tooltip)
|
||||||
self.deleteBox.setText(QCoreApplication.translate("mainWindow", u"Delete input", None))
|
self.deleteBox.setText(QCoreApplication.translate("mainWindow", u"Delete input", None))
|
||||||
#if QT_CONFIG(tooltip)
|
|
||||||
self.maximizeStrips.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - 1x4<br/></span>Keep format 1x4 panels strips.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - 2x2<br/></span>Turn 1x4 strips to 2x2 to maximize screen usage.</p></body></html>", None))
|
|
||||||
#endif // QT_CONFIG(tooltip)
|
|
||||||
self.maximizeStrips.setText(QCoreApplication.translate("mainWindow", u"1x4 to 2x2 strips", None))
|
|
||||||
#if QT_CONFIG(tooltip)
|
|
||||||
self.gammaBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Disable automatic gamma correction.</p></body></html>", None))
|
|
||||||
#endif // QT_CONFIG(tooltip)
|
|
||||||
self.gammaBox.setText(QCoreApplication.translate("mainWindow", u"Custom gamma", None))
|
|
||||||
#if QT_CONFIG(tooltip)
|
|
||||||
self.borderBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Autodetection<br/></span>The color of margins fill will be detected automatically.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - White<br/></span>Margins will be filled with white color.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Black<br/></span>Margins will be filled with black color.</p></body></html>", None))
|
|
||||||
#endif // QT_CONFIG(tooltip)
|
|
||||||
self.borderBox.setText(QCoreApplication.translate("mainWindow", u"W/B margins", None))
|
|
||||||
#if QT_CONFIG(tooltip)
|
|
||||||
self.webtoonBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Enable special parsing mode for Korean Webtoons.</p></body></html>", None))
|
|
||||||
#endif // QT_CONFIG(tooltip)
|
|
||||||
self.webtoonBox.setText(QCoreApplication.translate("mainWindow", u"Webtoon mode", None))
|
|
||||||
#if QT_CONFIG(tooltip)
|
|
||||||
self.upscaleBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Nothing<br/></span>Images smaller than device resolution will not be resized.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - Stretching<br/></span>Images smaller than device resolution will be resized. Aspect ratio will be not preserved.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Upscaling<br/></span>Images smaller than device resolution will be resized. Aspect ratio will be preserved.</p></body></html>", None))
|
|
||||||
#endif // QT_CONFIG(tooltip)
|
|
||||||
self.upscaleBox.setText(QCoreApplication.translate("mainWindow", u"Stretch/Upscale", None))
|
|
||||||
#if QT_CONFIG(tooltip)
|
#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.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.rotateBox.setText(QCoreApplication.translate("mainWindow", u"Spread splitter", None))
|
self.outputSplit.setText(QCoreApplication.translate("mainWindow", u"Output split", None))
|
||||||
#if QT_CONFIG(tooltip)
|
#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))
|
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)
|
#endif // QT_CONFIG(tooltip)
|
||||||
self.croppingBox.setText(QCoreApplication.translate("mainWindow", u"Cropping mode", None))
|
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)
|
||||||
|
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.colorBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Disable conversion to grayscale.</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.colorBox.setText(QCoreApplication.translate("mainWindow", u"Color mode", None))
|
self.colorBox.setText(QCoreApplication.translate("mainWindow", u"Color mode", None))
|
||||||
|
#if QT_CONFIG(tooltip)
|
||||||
|
self.rotateBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Split<br/></span>Double page spreads will be cut into two separate pages.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - Rotate and split<br/></span>Double page spreads will be displayed twice. First rotated and then split. </p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Rotate<br/></span>Double page spreads will be rotated.</p></body></html>", None))
|
||||||
|
#endif // QT_CONFIG(tooltip)
|
||||||
|
self.rotateBox.setText(QCoreApplication.translate("mainWindow", u"Spread splitter", None))
|
||||||
|
#if QT_CONFIG(tooltip)
|
||||||
|
self.gammaBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Disable automatic gamma correction.</p></body></html>", None))
|
||||||
|
#endif // QT_CONFIG(tooltip)
|
||||||
|
self.gammaBox.setText(QCoreApplication.translate("mainWindow", u"Custom gamma", None))
|
||||||
|
#if QT_CONFIG(tooltip)
|
||||||
|
self.spreadShiftBox.setToolTip(QCoreApplication.translate("mainWindow", u"Shift first page to opposite side in landscape for two page spread alignment", None))
|
||||||
|
#endif // QT_CONFIG(tooltip)
|
||||||
|
self.spreadShiftBox.setText(QCoreApplication.translate("mainWindow", u"Spread shift", None))
|
||||||
|
#if QT_CONFIG(tooltip)
|
||||||
|
self.webtoonBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Enable special parsing mode for Korean Webtoons.</p></body></html>", None))
|
||||||
|
#endif // QT_CONFIG(tooltip)
|
||||||
|
self.webtoonBox.setText(QCoreApplication.translate("mainWindow", u"Webtoon mode", None))
|
||||||
|
#if QT_CONFIG(tooltip)
|
||||||
|
self.maximizeStrips.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - 1x4<br/></span>Keep format 1x4 panels strips.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - 2x2<br/></span>Turn 1x4 strips to 2x2 to maximize screen usage.</p></body></html>", None))
|
||||||
|
#endif // QT_CONFIG(tooltip)
|
||||||
|
self.maximizeStrips.setText(QCoreApplication.translate("mainWindow", u"1x4 to 2x2 strips", None))
|
||||||
|
#if QT_CONFIG(tooltip)
|
||||||
|
self.borderBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Autodetection<br/></span>The color of margins fill will be detected automatically.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - White<br/></span>Margins will be filled with white color.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Black<br/></span>Margins will be filled with black color.</p></body></html>", None))
|
||||||
|
#endif // QT_CONFIG(tooltip)
|
||||||
|
self.borderBox.setText(QCoreApplication.translate("mainWindow", u"W/B margins", None))
|
||||||
|
#if QT_CONFIG(tooltip)
|
||||||
|
self.noRotateBox.setToolTip(QCoreApplication.translate("mainWindow", u"Do not rotate double page spreads in spread splitter option.", None))
|
||||||
|
#endif // QT_CONFIG(tooltip)
|
||||||
|
self.noRotateBox.setText(QCoreApplication.translate("mainWindow", u"No rotate", None))
|
||||||
|
#if QT_CONFIG(tooltip)
|
||||||
|
self.qualityBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - 4 panels<br/></span>Zoom each corner separately.</p><p style='white-space:pre'><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - 2 panels<br/></span>Zoom only the top and bottom of the page.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - 4 high-quality panels<br/></span>Zoom each corner separately. Try to increase the quality of magnification. Check wiki for more details.</p></body></html>", None))
|
||||||
|
#endif // QT_CONFIG(tooltip)
|
||||||
|
self.qualityBox.setText(QCoreApplication.translate("mainWindow", u"Panel View 4/2/HQ", None))
|
||||||
#if QT_CONFIG(tooltip)
|
#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)
|
#if QT_CONFIG(tooltip)
|
||||||
self.dedupeCoverBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p>Don't duplicate the first page as the cover. Useful for 2 page spread alignment.</p></body></html>", None))
|
self.authorEdit.setToolTip(QCoreApplication.translate("mainWindow", u"Default Author is KCC", None))
|
||||||
#endif // QT_CONFIG(tooltip)
|
#endif // QT_CONFIG(tooltip)
|
||||||
self.dedupeCoverBox.setText(QCoreApplication.translate("mainWindow", u"De-dupe cover", None))
|
self.authorEdit.setPlaceholderText(QCoreApplication.translate("mainWindow", u"Default Author", None))
|
||||||
self.gammaLabel.setText(QCoreApplication.translate("mainWindow", u"Gamma: Auto", None))
|
self.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)
|
||||||
@@ -443,7 +468,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"Editor", None))
|
self.editorButton.setText(QCoreApplication.translate("mainWindow", u"Metadata 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))
|
||||||
|
|||||||
@@ -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.8.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.Normal, QIcon.Off)
|
icon.addFile(u":/Icon/icons/comic2ebook.png", QSize(), QIcon.Mode.Normal, QIcon.State.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.MinimumExpanding, QSizePolicy.MinimumExpanding)
|
sizePolicy = QSizePolicy(QSizePolicy.Policy.MinimumExpanding, QSizePolicy.Policy.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.Normal, QIcon.Off)
|
icon1.addFile(u":/Other/icons/convert.png", QSize(), QIcon.Mode.Normal, QIcon.State.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.Normal, QIcon.Off)
|
icon2.addFile(u":/Other/icons/clear.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
|
||||||
self.cancelButton.setIcon(icon2)
|
self.cancelButton.setIcon(icon2)
|
||||||
|
|
||||||
self.horizontalLayout.addWidget(self.cancelButton)
|
self.horizontalLayout.addWidget(self.cancelButton)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
__version__ = '6.1.0'
|
__version__ = '7.1.2'
|
||||||
__license__ = 'ISC'
|
__license__ = 'ISC'
|
||||||
__copyright__ = '2012-2022, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>, darodi'
|
__copyright__ = '2012-2022, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>, darodi'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -298,22 +298,15 @@ 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"])
|
||||||
elif options.supportSyntheticSpread:
|
f.writelines([
|
||||||
f.writelines([
|
"<meta property=\"rendition:spread\">landscape</meta>\n",
|
||||||
"<meta property=\"rendition:spread\">landscape</meta>\n",
|
"<meta property=\"rendition:layout\">pre-paginated</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\" ",
|
||||||
@@ -358,55 +351,68 @@ 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.iskindle or options.supportSyntheticSpread:
|
if options.spreadshift:
|
||||||
for entry in reflist:
|
if pageside == "right":
|
||||||
if options.righttoleft:
|
pageside = "left"
|
||||||
if entry.endswith("-b"):
|
else:
|
||||||
f.write(
|
pageside = "right"
|
||||||
"<itemref idref=\"page_%s\" %s/>\n" % (entry,
|
for entry in reflist:
|
||||||
pageSpreadProperty("right"))
|
if options.righttoleft:
|
||||||
)
|
if entry.endswith("-a"):
|
||||||
pageside = "right"
|
f.write(
|
||||||
elif entry.endswith("-c"):
|
"<itemref idref=\"page_%s\" %s/>\n" % (entry,
|
||||||
f.write(
|
pageSpreadProperty("center"))
|
||||||
"<itemref idref=\"page_%s\" %s/>\n" % (entry,
|
)
|
||||||
pageSpreadProperty("left"))
|
pageside = "right"
|
||||||
)
|
elif entry.endswith("-b"):
|
||||||
pageside = "right"
|
f.write(
|
||||||
else:
|
"<itemref idref=\"page_%s\" %s/>\n" % (entry,
|
||||||
f.write(
|
pageSpreadProperty("right"))
|
||||||
"<itemref idref=\"page_%s\" %s/>\n" % (entry,
|
)
|
||||||
pageSpreadProperty(pageside))
|
pageside = "right"
|
||||||
)
|
elif entry.endswith("-c"):
|
||||||
if pageside == "right":
|
f.write(
|
||||||
pageside = "left"
|
"<itemref idref=\"page_%s\" %s/>\n" % (entry,
|
||||||
else:
|
pageSpreadProperty("left"))
|
||||||
pageside = "right"
|
)
|
||||||
|
pageside = "right"
|
||||||
else:
|
else:
|
||||||
if entry.endswith("-b"):
|
f.write(
|
||||||
f.write(
|
"<itemref idref=\"page_%s\" %s/>\n" % (entry,
|
||||||
"<itemref idref=\"page_%s\" %s/>\n" % (entry,
|
pageSpreadProperty(pageside))
|
||||||
pageSpreadProperty("left"))
|
)
|
||||||
)
|
if pageside == "right":
|
||||||
pageside = "left"
|
pageside = "left"
|
||||||
elif entry.endswith("-c"):
|
else:
|
||||||
f.write(
|
pageside = "right"
|
||||||
"<itemref idref=\"page_%s\" %s/>\n" % (entry,
|
else:
|
||||||
pageSpreadProperty("right"))
|
if entry.endswith("-a"):
|
||||||
)
|
f.write(
|
||||||
pageside = "left"
|
"<itemref idref=\"page_%s\" %s/>\n" % (entry,
|
||||||
else:
|
pageSpreadProperty("center"))
|
||||||
f.write(
|
)
|
||||||
"<itemref idref=\"page_%s\" %s/>\n" % (entry,
|
pageside = "left"
|
||||||
pageSpreadProperty(pageside))
|
elif entry.endswith("-b"):
|
||||||
)
|
f.write(
|
||||||
|
"<itemref idref=\"page_%s\" %s/>\n" % (entry,
|
||||||
|
pageSpreadProperty("left"))
|
||||||
|
)
|
||||||
|
pageside = "left"
|
||||||
|
elif entry.endswith("-c"):
|
||||||
|
f.write(
|
||||||
|
"<itemref idref=\"page_%s\" %s/>\n" % (entry,
|
||||||
|
pageSpreadProperty("right"))
|
||||||
|
)
|
||||||
|
pageside = "left"
|
||||||
|
else:
|
||||||
|
f.write(
|
||||||
|
"<itemref idref=\"page_%s\" %s/>\n" % (entry,
|
||||||
|
pageSpreadProperty(pageside))
|
||||||
|
)
|
||||||
if pageside == "right":
|
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'))
|
||||||
@@ -420,7 +426,7 @@ def buildOPF(dstdir, title, filelist, cover=None):
|
|||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
|
|
||||||
def buildEPUB(path, chapternames, tomenumber):
|
def buildEPUB(path, chapternames, tomenumber, ischunked):
|
||||||
filelist = []
|
filelist = []
|
||||||
chapterlist = []
|
chapterlist = []
|
||||||
cover = None
|
cover = None
|
||||||
@@ -509,15 +515,12 @@ def buildEPUB(path, chapternames, tomenumber):
|
|||||||
'cover' + getImageFileName(afile)[1])
|
'cover' + getImageFileName(afile)[1])
|
||||||
options.covers.append((image.Cover(os.path.join(dirpath, afile), cover, options,
|
options.covers.append((image.Cover(os.path.join(dirpath, afile), cover, options,
|
||||||
tomenumber), options.uuid))
|
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
|
||||||
# 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 and not ischunked:
|
||||||
chapterlist = []
|
chapterlist = []
|
||||||
|
|
||||||
global_diff = 0
|
global_diff = 0
|
||||||
@@ -666,7 +669,11 @@ 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':
|
||||||
ext = '.kepub.epub'
|
if options.noKepub:
|
||||||
|
# Just use normal epub extension if no_kepub option is true
|
||||||
|
ext = '.epub'
|
||||||
|
else:
|
||||||
|
ext = '.kepub.epub'
|
||||||
if wantedname is not None:
|
if wantedname is not None:
|
||||||
if wantedname.endswith(ext):
|
if wantedname.endswith(ext):
|
||||||
filename = os.path.abspath(wantedname)
|
filename = os.path.abspath(wantedname)
|
||||||
@@ -738,7 +745,7 @@ def getComicInfo(path, originalpath):
|
|||||||
options.authors.sort()
|
options.authors.sort()
|
||||||
else:
|
else:
|
||||||
options.authors = ['KCC']
|
options.authors = ['KCC']
|
||||||
if xml.data['Bookmarks'] and options.batchsplit == 0:
|
if xml.data['Bookmarks']:
|
||||||
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'])
|
||||||
@@ -957,8 +964,7 @@ 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="Device profile (Available options: K1, K2, K34, K578, KDX, KPW, KPW5, KV, KO, "
|
help=f"Device profile (Available options: {', '.join(image.ProfileData.Profiles.keys())})"
|
||||||
"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)")
|
||||||
@@ -981,11 +987,15 @@ 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("--dedupecover", action="store_true", dest="dedupecover", default=False,
|
output_options.add_argument("--spreadshift", action="store_true", dest="spreadshift", default=False,
|
||||||
help="De-duplicate the cover as the first page in the book")
|
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")
|
||||||
@@ -1044,23 +1054,17 @@ 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 ['K1', 'K2', 'K34', 'K578', 'KPW', 'KPW5', 'KV', 'KO', 'K11', 'KS']:
|
if options.profile in ['KDX']:
|
||||||
options.format = 'MOBI'
|
|
||||||
elif options.profile in ['OTHER', 'KoMT', 'KoG', 'KoGHD', 'KoA', 'KoAHD', 'KoAH2O', 'KoAO',
|
|
||||||
'KoN', 'KoC', 'KoCC', 'KoL', 'KoLC', 'KoF', 'KoS', 'KoE']:
|
|
||||||
options.format = 'EPUB'
|
|
||||||
elif options.profile in ['KDX']:
|
|
||||||
options.format = 'CBZ'
|
options.format = 'CBZ'
|
||||||
if options.profile in ['K1', 'K2', 'K34', 'K578', 'KPW', 'KPW5', 'KV', 'KO', 'K11', 'KS']:
|
elif options.profile in image.ProfileData.ProfilesKindle.keys():
|
||||||
|
options.format = 'MOBI'
|
||||||
|
else:
|
||||||
|
options.format = 'EPUB'
|
||||||
|
if options.profile in image.ProfileData.ProfilesKindle.keys():
|
||||||
options.iskindle = True
|
options.iskindle = True
|
||||||
elif options.profile in ['OTHER', 'KoMT', 'KoG', 'KoGHD', 'KoA', 'KoAHD', 'KoAH2O', 'KoAO', 'KoN', 'KoC', 'KoCC', 'KoL', 'KoLC', 'KoF', 'KoS', 'KoE']:
|
else:
|
||||||
options.isKobo = True
|
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:
|
||||||
@@ -1114,13 +1118,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)
|
||||||
@@ -1202,10 +1206,11 @@ 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])
|
||||||
@@ -1228,7 +1233,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()
|
k = kindle.Kindle(options.profile)
|
||||||
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:
|
||||||
@@ -1250,12 +1255,13 @@ 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'))
|
dualmetafix.DualMobiMetaFix(mobiPath + '_toclean', mobiPath, bytes(uuid, 'UTF-8'), is_pdoc)
|
||||||
return [True]
|
return [True]
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
return [False, format(err)]
|
return [False, format(err)]
|
||||||
@@ -1277,7 +1283,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
|
||||||
|
|||||||
@@ -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' or distro.like() == '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' or distro.like() == '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)
|
||||||
|
|||||||
@@ -136,7 +136,11 @@ def del_exth(rec0, exth_num):
|
|||||||
|
|
||||||
|
|
||||||
class DualMobiMetaFix:
|
class DualMobiMetaFix:
|
||||||
def __init__(self, infile, outfile, asin):
|
def __init__(self, infile, outfile, asin, is_pdoc):
|
||||||
|
cdetype = b'EBOK'
|
||||||
|
if is_pdoc:
|
||||||
|
cdetype = b'PDOC'
|
||||||
|
|
||||||
shutil.copyfile(infile, outfile)
|
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)
|
||||||
@@ -147,7 +151,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, b'EBOK')
|
rec0 = add_exth(rec0, 501, cdetype)
|
||||||
rec0 = add_exth(rec0, 113, asin)
|
rec0 = add_exth(rec0, 113, asin)
|
||||||
replacesection(self.datain, 0, rec0)
|
replacesection(self.datain, 0, rec0)
|
||||||
|
|
||||||
@@ -174,7 +178,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, b'EBOK')
|
rec0 = add_exth(rec0, 501, cdetype)
|
||||||
rec0 = add_exth(rec0, 113, asin)
|
rec0 = add_exth(rec0, 113, asin)
|
||||||
replacesection(self.datain, datain_kf8, rec0)
|
replacesection(self.datain, datain_kf8, rec0)
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ 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
|
||||||
|
|
||||||
@@ -78,18 +79,29 @@ class ProfileData:
|
|||||||
PalleteNull = [
|
PalleteNull = [
|
||||||
]
|
]
|
||||||
|
|
||||||
Profiles = {
|
ProfilesKindleEBOK = {
|
||||||
'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),
|
||||||
@@ -105,6 +117,18 @@ 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),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,7 +175,10 @@ 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:
|
||||||
self.payload.append(['R', self.source, self.image.rotate(90, Image.Resampling.BICUBIC, True), self.color, self.fill])
|
spread = self.image
|
||||||
|
if not self.opt.norotate:
|
||||||
|
spread = spread.rotate(90, Image.Resampling.BICUBIC, True)
|
||||||
|
self.payload.append(['R', self.source, spread, self.color, self.fill])
|
||||||
elif (width > height) != (dstwidth > dstheight) and not self.opt.webtoon:
|
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:
|
||||||
@@ -169,7 +196,10 @@ 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:
|
||||||
self.payload.append(['R', self.source, self.image.rotate(90, Image.Resampling.BICUBIC, True),
|
spread = self.image
|
||||||
|
if not self.opt.norotate:
|
||||||
|
spread = spread.rotate(90, Image.Resampling.BICUBIC, True)
|
||||||
|
self.payload.append(['R', self.source, spread,
|
||||||
self.color, self.fill])
|
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])
|
||||||
@@ -342,20 +372,6 @@ 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]
|
||||||
@@ -363,26 +379,16 @@ class ComicPage:
|
|||||||
self.image = self.image.crop(box)
|
self.image = self.image.crop(box)
|
||||||
|
|
||||||
def cropPageNumber(self, power, minimum):
|
def cropPageNumber(self, power, minimum):
|
||||||
if self.fill != 'white':
|
bbox = get_bbox_crop_margin_page_number(self.image, power, self.fill)
|
||||||
tmptmg = self.image.convert(mode='L')
|
|
||||||
else:
|
if bbox:
|
||||||
tmptmg = ImageOps.invert(self.image.convert(mode='L'))
|
self.maybeCrop(bbox, minimum)
|
||||||
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):
|
||||||
if self.fill != 'white':
|
bbox = get_bbox_crop_margin(self.image, power, self.fill)
|
||||||
tmptmg = self.image.convert(mode='L')
|
|
||||||
else:
|
if bbox:
|
||||||
tmptmg = ImageOps.invert(self.image.convert(mode='L'))
|
self.maybeCrop(bbox, minimum)
|
||||||
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:
|
||||||
|
|||||||
@@ -19,9 +19,12 @@
|
|||||||
import os.path
|
import os.path
|
||||||
import psutil
|
import psutil
|
||||||
|
|
||||||
|
from . import image
|
||||||
|
|
||||||
|
|
||||||
class Kindle:
|
class Kindle:
|
||||||
def __init__(self):
|
def __init__(self, profile):
|
||||||
|
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()
|
||||||
@@ -29,9 +32,11 @@ 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') 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]
|
||||||
|
|||||||
215
kindlecomicconverter/page_number_crop_alg.py
Normal file
215
kindlecomicconverter/page_number_crop_alg.py
Normal file
@@ -0,0 +1,215 @@
|
|||||||
|
from PIL import ImageOps, ImageFilter
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
'''
|
||||||
|
Some assupmptions on the page number sizes
|
||||||
|
We assume that the size of the number (including all digits) is between
|
||||||
|
'min_shape_size_tolerated_size' and 'max_shape_size_tolerated_size' relative to the image size.
|
||||||
|
We assume the distance between the digit is no more than 'max_dist_size' (x,y), and no more than 3 digits.
|
||||||
|
'''
|
||||||
|
max_shape_size_tolerated_size = (0.015*3, 0.02) # percent
|
||||||
|
min_shape_size_tolerated_size = (0.003, 0.006) # percent
|
||||||
|
window_h_size = max_shape_size_tolerated_size[1]*1.25 # percent
|
||||||
|
max_dist_size = (0.01, 0.002) # percent
|
||||||
|
|
||||||
|
|
||||||
|
'''
|
||||||
|
E-reader screen real-estate is an important resource.
|
||||||
|
More available screensize means more details can be better seen, especially text.
|
||||||
|
Text is one of the most important elements that need to be clearly readable on e-readers,
|
||||||
|
which mostly are smaller devices where the need to zoom is unwanted.
|
||||||
|
|
||||||
|
By cropping the page number on the bottom of the page, 2%-5% of the page height can be regained
|
||||||
|
that allows us to upscale the image even more.
|
||||||
|
- Most of the times the screen height is the limiting factor in upscaling, rather than its width.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
img (PIL image): A PIL image.
|
||||||
|
power (float): The power to 'chop' through pixels matching the background. Values in range[0,3].
|
||||||
|
background_color (string): 'white' for white background, anything else for black.
|
||||||
|
Returns:
|
||||||
|
bbox (4-tuple, left|top|right|bot): The tightest bounding box calculated after trying to remove the bottom page number. Returns None if couldnt find anything satisfactory
|
||||||
|
'''
|
||||||
|
def get_bbox_crop_margin_page_number(img, power=1, background_color='white'):
|
||||||
|
if img.mode != 'L':
|
||||||
|
img = ImageOps.grayscale(img)
|
||||||
|
|
||||||
|
if background_color != 'white':
|
||||||
|
img = ImageOps.invert(img)
|
||||||
|
|
||||||
|
'''
|
||||||
|
Autocontrast: due to some threshold values, it's important that the blacks will be blacks and white will be whites.
|
||||||
|
Box/MeanFilter: Allows us to reduce noise like bad a page scan or compression artifacts.
|
||||||
|
Note: MedianFilter works better in my experience, but takes 2x-3x longer to perform.
|
||||||
|
'''
|
||||||
|
img = ImageOps.autocontrast(img, 1).filter(ImageFilter.BoxBlur(1))
|
||||||
|
|
||||||
|
'''
|
||||||
|
The 'power' parameters determines the threshold. The higher the power, the more "force" it can crop through black pixels (in case of white background)
|
||||||
|
and the lower the power, more sensitive to black pixels.
|
||||||
|
'''
|
||||||
|
threshold = threshold_from_power(power)
|
||||||
|
bw_img = img.point(lambda p: 255 if p <= threshold else 0)
|
||||||
|
bw_bbox = bw_img.getbbox()
|
||||||
|
|
||||||
|
if not bw_bbox: # bbox cannot be found in case that the entire resulted image is black.
|
||||||
|
return None
|
||||||
|
|
||||||
|
left, top_y_pos, right, bot_y_pos = bw_bbox
|
||||||
|
|
||||||
|
'''
|
||||||
|
We inspect the lower bottom part of the image where we suspect might be a page number.
|
||||||
|
We assume that page number consist of 1 to 3 digits and the total min and max size of the number
|
||||||
|
is between 'min_shape_size_tolerated_size' and 'max_shape_size_tolerated_size'.
|
||||||
|
'''
|
||||||
|
window_h = int(img.size[1] * window_h_size)
|
||||||
|
img_part = img.crop((left,bot_y_pos-window_h, right, bot_y_pos))
|
||||||
|
|
||||||
|
'''
|
||||||
|
We detect related-pixels by proximity, with max distance defined in 'max_dist_size'.
|
||||||
|
Related pixels (in x axis) for each image-row are then merged to boxes with adjacent rows (in y axis)
|
||||||
|
to form bounding boxes of the detected objects (which one of them could be the page number).
|
||||||
|
'''
|
||||||
|
img_part_mat = np.array(img_part)
|
||||||
|
window_groups = []
|
||||||
|
for i in range(img_part.size[1]):
|
||||||
|
row_groups = [(g[0], g[1], i, i) for g in group_pixels(img_part_mat[i], img.size[0]*max_dist_size[0], threshold)]
|
||||||
|
window_groups.extend(row_groups)
|
||||||
|
|
||||||
|
window_groups = np.array(window_groups)
|
||||||
|
|
||||||
|
boxes = merge_boxes(window_groups, (img.size[0]*max_dist_size[0], img.size[1]*max_dist_size[1]))
|
||||||
|
'''
|
||||||
|
We assume that the lowest part of the image that has black pixels on is the page number.
|
||||||
|
In case that there are more than one detected object in the loewst part, we assume that one of them is probably
|
||||||
|
manga-content and shouldn't be cropped.
|
||||||
|
'''
|
||||||
|
# filter all small objects
|
||||||
|
boxes = list(filter(lambda box: box[1]-box[0] >= img.size[0]*min_shape_size_tolerated_size[0]
|
||||||
|
and box[3]-box[2] >= img.size[1]*min_shape_size_tolerated_size[1], boxes))
|
||||||
|
lowest_boxes = list(filter(lambda box: box[3] == window_h-1, boxes))
|
||||||
|
|
||||||
|
min_y_of_lowest_boxes = 0
|
||||||
|
if len(lowest_boxes) > 0:
|
||||||
|
min_y_of_lowest_boxes = np.min(np.array(lowest_boxes)[:,2])
|
||||||
|
|
||||||
|
boxes_in_same_y_range = list(filter(lambda box: box[3] >= min_y_of_lowest_boxes, boxes))
|
||||||
|
|
||||||
|
max_shape_size_tolerated = (img.size[0] * max_shape_size_tolerated_size[0],
|
||||||
|
max(img.size[1] *max_shape_size_tolerated_size[1], 3))
|
||||||
|
|
||||||
|
should_force_crop = (
|
||||||
|
len(boxes_in_same_y_range) == 1
|
||||||
|
and (boxes_in_same_y_range[0][1] - boxes_in_same_y_range[0][0] <= max_shape_size_tolerated[0])
|
||||||
|
and (boxes_in_same_y_range[0][3] - boxes_in_same_y_range[0][2] <= max_shape_size_tolerated[1])
|
||||||
|
)
|
||||||
|
|
||||||
|
cropped_bbox = (0, 0, img.size[0], img.size[1])
|
||||||
|
if should_force_crop:
|
||||||
|
cropped_bbox = (0, 0, img.size[0], bot_y_pos-(window_h-boxes_in_same_y_range[0][2]+1))
|
||||||
|
|
||||||
|
cropped_bbox = bw_img.crop(cropped_bbox).getbbox()
|
||||||
|
|
||||||
|
return cropped_bbox
|
||||||
|
|
||||||
|
|
||||||
|
'''
|
||||||
|
Parameters:
|
||||||
|
img (PIL image): A PIL image.
|
||||||
|
power (float): The power to 'chop' through pixels matching the background. Values in range[0,3].
|
||||||
|
background_color (string): 'white' for white background, anything else for black.
|
||||||
|
Returns:
|
||||||
|
bbox (4-tuple, left|top|right|bot): The tightest bounding box calculated after trying to remove the bottom page number. Returns None if couldnt find anything satisfactory
|
||||||
|
'''
|
||||||
|
def get_bbox_crop_margin(img, power=1, background_color='white'):
|
||||||
|
if img.mode != 'L':
|
||||||
|
img = ImageOps.grayscale(img)
|
||||||
|
|
||||||
|
if background_color != 'white':
|
||||||
|
img = ImageOps.invert(img)
|
||||||
|
|
||||||
|
'''
|
||||||
|
Autocontrast: due to some threshold values, it's important that the blacks will be blacks and white will be whites.
|
||||||
|
Box/MeanFilter: Allows us to reduce noise like bad a page scan or compression artifacts.
|
||||||
|
Note: MedianFilter works better in my experience, but takes 2x-3x longer to perform.
|
||||||
|
'''
|
||||||
|
img = ImageOps.autocontrast(img, 1).filter(ImageFilter.BoxBlur(1))
|
||||||
|
|
||||||
|
'''
|
||||||
|
The 'power' parameters determines the threshold. The higher the power, the more "force" it can crop through black pixels (in case of white background)
|
||||||
|
and the lower the power, more sensitive to black pixels.
|
||||||
|
'''
|
||||||
|
threshold = threshold_from_power(power)
|
||||||
|
bw_img = img.point(lambda p: 255 if p <= threshold else 0)
|
||||||
|
|
||||||
|
return bw_img.getbbox()
|
||||||
|
|
||||||
|
|
||||||
|
'''
|
||||||
|
Groups close pixels together (x axis)
|
||||||
|
'''
|
||||||
|
def group_pixels(row, max_dist_tolerated, threshold):
|
||||||
|
groups = []
|
||||||
|
idx = np.where(row <= threshold)[0]
|
||||||
|
|
||||||
|
group_start = -1
|
||||||
|
group_end = 0
|
||||||
|
for i in range(len(idx)):
|
||||||
|
dist = idx[i] - group_end
|
||||||
|
if group_start == -1:
|
||||||
|
group_start = idx[i]
|
||||||
|
group_end = idx[i]
|
||||||
|
elif dist <= max_dist_tolerated:
|
||||||
|
group_end = idx[i]
|
||||||
|
else:
|
||||||
|
groups.append((group_start, group_end))
|
||||||
|
group_start = -1
|
||||||
|
group_end = -1
|
||||||
|
|
||||||
|
if group_start != -1:
|
||||||
|
groups.append((group_start, group_end))
|
||||||
|
|
||||||
|
return groups
|
||||||
|
|
||||||
|
|
||||||
|
def box_intersect(box1, box2, max_dist):
|
||||||
|
return not (box2[0]-max_dist[0] > box1[1]
|
||||||
|
or box2[1]+max_dist[0] < box1[0]
|
||||||
|
or box2[2]-max_dist[1] > box1[3]
|
||||||
|
or box2[3]+max_dist[1] < box1[2])
|
||||||
|
|
||||||
|
'''
|
||||||
|
Merge close bounding boxes (left,right, top,bot) (x axis) with distance threshold defined in
|
||||||
|
'max_dist_tolerated'. Boxes with less 'max_dist_tolerated' distance (Chebyshev distance).
|
||||||
|
'''
|
||||||
|
def merge_boxes(boxes, max_dist_tolerated):
|
||||||
|
j = 0
|
||||||
|
while j < len(boxes)-1:
|
||||||
|
g1 = boxes[j]
|
||||||
|
intersecting_boxes = []
|
||||||
|
other_boxes = []
|
||||||
|
for i in range(j+1,len(boxes)):
|
||||||
|
g2 = boxes[i]
|
||||||
|
if box_intersect(g1,g2, max_dist_tolerated):
|
||||||
|
intersecting_boxes.append(g2)
|
||||||
|
else:
|
||||||
|
other_boxes.append(g2)
|
||||||
|
|
||||||
|
if len(intersecting_boxes) > 0:
|
||||||
|
intersecting_boxes = np.array([g1, *intersecting_boxes])
|
||||||
|
merged_box = np.array([
|
||||||
|
np.min(intersecting_boxes[:,0]),
|
||||||
|
np.max(intersecting_boxes[:,1]),
|
||||||
|
np.min(intersecting_boxes[:,2]),
|
||||||
|
np.max(intersecting_boxes[:,3])
|
||||||
|
])
|
||||||
|
other_boxes.append(merged_box)
|
||||||
|
boxes = np.concatenate([boxes[:j], other_boxes])
|
||||||
|
j = 0
|
||||||
|
else:
|
||||||
|
j += 1
|
||||||
|
return boxes
|
||||||
|
|
||||||
|
|
||||||
|
def threshold_from_power(power):
|
||||||
|
return 240-(power*64)
|
||||||
@@ -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)
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ 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>=8.4.0
|
||||||
distro>=1.8.0
|
distro>=1.8.0
|
||||||
|
numpy>=1.22.4,<2.0.0
|
||||||
|
|||||||
12
setup.py
12
setup.py
@@ -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 = []
|
||||||
|
|
||||||
@@ -37,16 +36,16 @@ class BuildBinaryCommand(distutils.cmd.Command):
|
|||||||
def run(self):
|
def run(self):
|
||||||
VERSION = __version__
|
VERSION = __version__
|
||||||
if sys.platform == 'darwin':
|
if sys.platform == 'darwin':
|
||||||
os.system('pyinstaller -y -D -i icons/comic2ebook.icns -n "Kindle Comic Converter" -w -s kcc.py')
|
os.system('pyinstaller --hidden-import=_cffi_backend -y -D -i icons/comic2ebook.icns -n "Kindle Comic Converter" -w -s kcc.py')
|
||||||
# TODO /usr/bin/codesign --force -s "$MACOS_CERTIFICATE_NAME" --options runtime dist/Applications/Kindle\ Comic\ Converter.app -v
|
# 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)
|
||||||
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(
|
||||||
'pyinstaller --hidden-import=queue -y -F -i icons/comic2ebook.ico -n kcc_linux_' + VERSION + ' kcc.py')
|
'pyinstaller --hidden-import=_cffi_backend --hidden-import=queue -y -F -i icons/comic2ebook.ico -n kcc_linux_' + VERSION + ' kcc.py')
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
else:
|
else:
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
@@ -82,8 +81,9 @@ 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[fast]>=8.4.0',
|
'natsort>=8.4.0',
|
||||||
'distro',
|
'distro',
|
||||||
|
'numpy>=1.22.4,<2.0.0'
|
||||||
],
|
],
|
||||||
classifiers=[],
|
classifiers=[],
|
||||||
zip_safe=False,
|
zip_safe=False,
|
||||||
|
|||||||
Reference in New Issue
Block a user