1
0
mirror of https://github.com/ciromattia/kcc synced 2026-04-17 14:38:47 +00:00

Compare commits

..

1 Commits
v6.2.0 ... win7

Author SHA1 Message Date
Alex Xu
b2aaa9e8b0 maybe restore win7 2024-04-14 05:10:27 -07:00
28 changed files with 12251 additions and 12465 deletions

View File

@@ -34,8 +34,8 @@ jobs:
- name: Install python dependencies - name: Install python dependencies
run: | run: |
sudo apt-get update sudo apt-get update
sudo apt-get install -y libpng-dev libjpeg-dev p7zip-full p7zip-rar python3-pip squashfs-tools libfuse2 libxcb-cursor0 sudo apt-get install -y libpng-dev libjpeg-dev p7zip-full p7zip-rar python3-pyqt5 python3-pip squashfs-tools libfuse2
python -m pip install --upgrade pip setuptools wheel certifi pyinstaller --no-binary pyinstaller python -m pip install --upgrade pip setuptools wheel certifi pyinstaller PyQt6 --no-binary pyinstaller
python -m pip install -r requirements.txt python -m pip install -r requirements.txt
- name: build binary - name: build binary
run: | run: |
@@ -64,7 +64,7 @@ jobs:
name: AppImage name: AppImage
path: './*.AppImage*' path: './*.AppImage*'
- name: Release - name: Release
uses: softprops/action-gh-release@v2 uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/') if: startsWith(github.ref, 'refs/tags/')
with: with:
prerelease: true prerelease: true

View File

@@ -23,10 +23,7 @@ on:
jobs: jobs:
build: build:
strategy: runs-on: macos-latest
matrix:
os: [ macos-12, macos-14 ]
runs-on: ${{ matrix.os }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Set up Python - name: Set up Python
@@ -80,10 +77,10 @@ jobs:
- name: upload build - name: upload build
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: mac-os-build-${{ runner.arch }} name: mac-os-build
path: dist/*.dmg path: dist/*.dmg
- name: Release - name: Release
uses: softprops/action-gh-release@v2 uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/') if: startsWith(github.ref, 'refs/tags/')
with: with:
prerelease: true prerelease: true
@@ -98,4 +95,4 @@ jobs:
# if: ${{ always() }} # if: ${{ always() }}
run: | run: |
security delete-keychain $RUNNER_TEMP/app-signing.keychain-db security delete-keychain $RUNNER_TEMP/app-signing.keychain-db
rm ~/Library/MobileDevice/Provisioning\ Profiles/build_pp.mobileprovision rm ~/Library/MobileDevice/Provisioning\ Profiles/build_pp.mobileprovision

View File

@@ -26,27 +26,33 @@ jobs:
# run: | # run: |
# pyi-makespec -F -i icons\\comic2ebook.ico -n KCC_test -w --noupx kcc.py # pyi-makespec -F -i icons\\comic2ebook.ico -n KCC_test -w --noupx kcc.py
- name: Package Application - name: Package Application
uses: JackMcKew/pyinstaller-action-windows@main uses: JackMcKew/pyinstaller-action-windows@python3-10-pyinstaller-5-3
with:
path: .
spec: ./kcc.spec
- name: Package Application
uses: JackMcKew/pyinstaller-action-windows@python3-10-pyinstaller-5-3
with: with:
path: . path: .
spec: ./kcc-c2e.spec spec: ./kcc-c2e.spec
- name: Package Application - name: Package Application
uses: JackMcKew/pyinstaller-action-windows@main uses: JackMcKew/pyinstaller-action-windows@python3-10-pyinstaller-5-3
with: with:
path: . path: .
spec: ./kcc-c2p.spec spec: ./kcc-c2p.spec
- name: rename binaries - name: rename binaries
run: | run: |
version_built=$(cat kindlecomicconverter/__init__.py | grep version | awk '{print $3}' | sed "s/[^.0-9b]//g") version_built=$(cat kindlecomicconverter/__init__.py | grep version | awk '{print $3}' | sed "s/[^.0-9b]//g")
mv dist/windows/kcc-c2e.exe dist/windows/KCC_c2e_${version_built}.exe mv dist/windows/kcc.exe dist/windows/kcc_${version_built}.exe
mv dist/windows/kcc-c2p.exe dist/windows/KCC_c2p_${version_built}.exe mv dist/windows/kcc-c2e.exe dist/windows/kcc_c2e_${version_built}.exe
mv dist/windows/kcc-c2p.exe dist/windows/kcc_c2p_${version_built}.exe
- name: upload build - name: upload build
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: windows-build name: windows-build
path: dist/windows/*.exe path: dist/windows/*.exe
- name: Release - name: Release
uses: softprops/action-gh-release@v2 uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/') if: startsWith(github.ref, 'refs/tags/')
with: with:
prerelease: true prerelease: true

View File

@@ -29,7 +29,7 @@ jobs:
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v5 uses: actions/setup-python@v5
with: with:
python-version: 3.11 python-version: 3.8
cache: 'pip' cache: 'pip'
- name: Install dependencies - name: Install dependencies
env: env:
@@ -47,7 +47,7 @@ jobs:
name: windows-build name: windows-build
path: dist/*.exe path: dist/*.exe
- name: Release - name: Release
uses: softprops/action-gh-release@v2 uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/') if: startsWith(github.ref, 'refs/tags/')
with: with:
prerelease: true prerelease: true
@@ -55,4 +55,4 @@ jobs:
files: | files: |
CHANGELOG.md CHANGELOG.md
LICENSE.txt LICENSE.txt
dist/*.exe dist/*.exe

View File

@@ -1,5 +1,5 @@
# Select final stage based on TARGETARCH ARG # Select final stage based on TARGETARCH ARG
FROM ghcr.io/ciromattia/kcc:docker-base-20240928 FROM ghcr.io/ciromattia/kcc:docker-base-20230514
LABEL com.kcc.name="Kindle Comic Converter" LABEL com.kcc.name="Kindle Comic Converter"
LABEL com.kcc.author="Ciro Mattia Gonano, Paweł Jastrzębski and Darodi" LABEL com.kcc.author="Ciro Mattia Gonano, Paweł Jastrzębski and Darodi"
LABEL org.opencontainers.image.description='Kindle Comic Converter' LABEL org.opencontainers.image.description='Kindle Comic Converter'
@@ -16,4 +16,4 @@ COPY . /opt/kcc
RUN cat /opt/kcc/kindlecomicconverter/__init__.py | grep version | awk '{print $3}' | sed "s/'//g" > /IMAGE_VERSION RUN cat /opt/kcc/kindlecomicconverter/__init__.py | grep version | awk '{print $3}' | sed "s/'//g" > /IMAGE_VERSION
ENTRYPOINT ["/opt/kcc/kcc-c2e.py"] ENTRYPOINT ["/opt/kcc/kcc-c2e.py"]
CMD ["-h"] CMD ["-h"]

View File

@@ -1,4 +1,4 @@
FROM --platform=linux/amd64 python:3.12-slim-bullseye as compile-amd64 FROM --platform=linux/amd64 python:3.11-slim-bullseye as compile-amd64
ARG TARGETOS ARG TARGETOS
ARG TARGETARCH ARG TARGETARCH
ARG TARGETVARIANT ARG TARGETVARIANT
@@ -8,7 +8,7 @@ RUN echo "I'm building for $TARGETOS/$TARGETARCH/$TARGETVARIANT"
COPY requirements.txt /opt/kcc/ COPY requirements.txt /opt/kcc/
ENV PATH="/opt/venv/bin:$PATH" ENV PATH="/opt/venv/bin:$PATH"
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 libpng-dev libjpeg-dev p7zip-full unrar-free libgl1 && \ apt-get install -y libpng-dev libjpeg-dev p7zip-full unrar-free libgl1 python3-pyqt5 && \
python -m pip install --upgrade pip && \ python -m pip install --upgrade pip && \
python -m venv /opt/venv && \ python -m venv /opt/venv && \
python -m pip install -r /opt/kcc/requirements.txt python -m pip install -r /opt/kcc/requirements.txt
@@ -16,7 +16,7 @@ RUN DEBIAN_FRONTEND=noninteractive apt-get update -y && apt-get -yq upgrade && \
###################################################################################### ######################################################################################
FROM --platform=linux/arm64 python:3.12-slim-bullseye as compile-arm64 FROM --platform=linux/arm64 python:3.11-slim-bullseye as compile-arm64
ARG TARGETOS ARG TARGETOS
ARG TARGETARCH ARG TARGETARCH
ARG TARGETVARIANT ARG TARGETVARIANT
@@ -28,9 +28,6 @@ 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=() && \
@@ -58,6 +55,7 @@ RUN set -x && \
KEPT_PACKAGES+=(p7zip-full) && \ KEPT_PACKAGES+=(p7zip-full) && \
KEPT_PACKAGES+=(python3) && \ KEPT_PACKAGES+=(python3) && \
KEPT_PACKAGES+=(python3-pip) && \ KEPT_PACKAGES+=(python3-pip) && \
KEPT_PACKAGES+=(python3-pyqt5) && \
KEPT_PACKAGES+=(unrar-free) && \ KEPT_PACKAGES+=(unrar-free) && \
# Install packages # Install packages
DEBIAN_FRONTEND=noninteractive apt-get update -y && apt-get -yq upgrade && \ DEBIAN_FRONTEND=noninteractive apt-get update -y && apt-get -yq upgrade && \
@@ -67,13 +65,14 @@ 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 -r /opt/kcc/requirements.txt python -m pip install --upgrade pillow python-slugify psutil raven mozjpeg-lossless-optimization
###################################################################################### ######################################################################################
FROM --platform=linux/arm/v7 python:3.12-slim-bullseye as compile-armv7 FROM --platform=linux/arm/v7 python:3.11-slim-bullseye as compile-armv7
ARG TARGETOS ARG TARGETOS
ARG TARGETARCH ARG TARGETARCH
ARG TARGETVARIANT ARG TARGETVARIANT
@@ -85,9 +84,6 @@ 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=() && \
@@ -116,6 +112,7 @@ RUN set -x && \
KEPT_PACKAGES+=(p7zip-full) && \ KEPT_PACKAGES+=(p7zip-full) && \
KEPT_PACKAGES+=(python3) && \ KEPT_PACKAGES+=(python3) && \
KEPT_PACKAGES+=(python3-pip) && \ KEPT_PACKAGES+=(python3-pip) && \
KEPT_PACKAGES+=(python3-pyqt5) && \
KEPT_PACKAGES+=(unrar-free) && \ KEPT_PACKAGES+=(unrar-free) && \
# Install packages # Install packages
DEBIAN_FRONTEND=noninteractive apt-get update -y && apt-get -yq upgrade && \ DEBIAN_FRONTEND=noninteractive apt-get update -y && apt-get -yq upgrade && \
@@ -125,18 +122,19 @@ 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 psutil requests python-slugify raven packaging mozjpeg-lossless-optimization natsort distro python -m pip install --upgrade pillow python-slugify psutil raven mozjpeg-lossless-optimization
###################################################################################### ######################################################################################
FROM --platform=linux/amd64 python:3.12-slim-bullseye as build-amd64 FROM --platform=linux/amd64 python:3.11-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.12-slim-bullseye as build-arm64 FROM --platform=linux/arm64 python:3.11-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.12-slim-bullseye as build-armv7 FROM --platform=linux/arm/v7 python:3.11-slim-bullseye as build-armv7
COPY --from=compile-armv7 /opt/venv /opt/venv COPY --from=compile-armv7 /opt/venv /opt/venv
###################################################################################### ######################################################################################
@@ -160,5 +158,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-20240928 > /IMAGE_VERSION echo docker-base-20230514 > /IMAGE_VERSION

109
README.md
View File

@@ -2,8 +2,8 @@
[![GitHub release](https://img.shields.io/github/release/ciromattia/kcc.svg)](https://github.com/ciromattia/kcc/releases) [![GitHub release](https://img.shields.io/github/release/ciromattia/kcc.svg)](https://github.com/ciromattia/kcc/releases)
[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/ciromattia/kcc/docker-publish.yml?label=docker%20build)](https://github.com/ciromattia/kcc/pkgs/container/kcc) [![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/ciromattia/kcc/docker-publish.yml?label=docker%20build)](https://github.com/ciromattia/kcc/pkgs/container/kcc)
**Kindle Comic Converter** is a Python app to convert comic/manga files or folders to EPUB, Panel View MOBI or E-Ink optimized CBZ. **Kindle Comic Converter** is a Python app to convert comic/manga files or folders to EPUB, Panel View MOBI or E-Ink optimized CBZ.
@@ -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 (founder, active 2013-2014): - Ciro Mattia Gonano:
- [![Donate PayPal](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=D8WNYNPBGDAS2) - [![Donate PayPal](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=D8WNYNPBGDAS2)
- [![Donate Flattr](https://img.shields.io/badge/Donate-Flattr-green.svg)](http://flattr.com/thing/2260449/ciromattiakcc-on-GitHub) - [![Donate Flattr](https://img.shields.io/badge/Donate-Flattr-green.svg)](http://flattr.com/thing/2260449/ciromattiakcc-on-GitHub)
- Paweł Jastrzębski (active 2013-2019): - Paweł Jastrzębski:
- [![Donate PayPal](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YTTJ4LK2JDHPS) - [![Donate PayPal](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YTTJ4LK2JDHPS)
- [![Donate Bitcoin](https://img.shields.io/badge/Donate-Bitcoin-green.svg)](https://jastrzeb.ski/donate/) - [![Donate Bitcoin](https://img.shields.io/badge/Donate-Bitcoin-green.svg)](https://jastrzeb.ski/donate/)
- Alex Xu (active 2023-Present) - Alex Xu
- [![Donate PayPal](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/donate/?business=QFJVE7A6LCP6U&no_recurring=0&item_name=Kindle+Comic+Converter&currency_code=USD) - [![Donate PayPal](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/donate/?business=QFJVE7A6LCP6U&no_recurring=0&item_name=Kindle+Comic+Converter&currency_code=USD)
@@ -36,12 +36,11 @@ If you find **KCC** valuable you can consider donating to the authors:
- **https://github.com/ciromattia/kcc/releases** - **https://github.com/ciromattia/kcc/releases**
Click on **Assets** of the latest release. Click on **Assets** of the latest release.
You probably want either You probably want either
- `KCC_*.*.*.exe` (Windows) - `kcc_*.*.*.exe` (Windows)
- `kcc_macos_arm_*.*.*.dmg` (recent Mac with Apple Silicon M1 chip or later) - `KindleComicConverter_osx_*.*.*.dmg` (Mac)
- `kcc_macos_i386_*.*.*.dmg` (older Mac with Intel chip)
The `c2e` and `c2p` versions are command line tools for power users. The `c2e` and `c2p` versions are command line tools for power users.
@@ -51,28 +50,33 @@ 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
- [Kindle Scribe cover guide](https://github.com/ciromattia/kcc/issues/508) (also works for older Kindles)
- [Windows 7 support](https://github.com/ciromattia/kcc/issues/678)
- [Combine files/chapters](https://github.com/ciromattia/kcc/issues/612#issuecomment-2117985011)
- [Flatpak mobi conversion stuck](https://github.com/ciromattia/kcc/wiki/Installation#linux)
## PREREQUISITES ## PREREQUISITES
You'll need to install various tools to access important but optional features. Close and re-open KCC to get KCC to detect them. You'll need to install various tools to access important but optional features.
### KindleGen The installation process has been greatly streamlined. No need to add 7z to PATH or locate KindleGen from the internet and put it in a special folder with KCC. Just run it and KCC will tell you what to install.
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.
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
### 7-Zip ### 7-Zip
This is no longer required as of KCC 6.1. #### Windows 7-Zip
If you still need it, refer to the wiki: https://github.com/ciromattia/kcc/wiki/Installation#7-zip First install 7z from https://www.7-zip.org/ or with command line:
```
winget install --id 7zip.7zip
```
#### macOS 7-Zip/Unar
with [Homebrew](https://brew.sh/) installed
```
brew install p7zip
brew install unar
```
### KindleGen
#### Windows / macOS KindleGen
Install [Kindle Previewer 3 (KP3)](https://www.amazon.com/Kindle-Previewer/b?ie=UTF8&node=21381691011). KCC will automatically detect KindleGen from it.
## 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:
@@ -118,15 +122,14 @@ sudo apt-get install python3 p7zip-full python3-pil python3-psutil python3-slugi
'KoAO': ("Kobo Aura ONE", (1404, 1872), Palette16, 1.8), 'KoAO': ("Kobo Aura ONE", (1404, 1872), Palette16, 1.8),
'KoN': ("Kobo Nia", (758, 1024), Palette16, 1.8), 'KoN': ("Kobo Nia", (758, 1024), Palette16, 1.8),
'KoC': ("Kobo Clara HD/Kobo Clara 2E", (1072, 1448), Palette16, 1.8), 'KoC': ("Kobo Clara HD/Kobo Clara 2E", (1072, 1448), Palette16, 1.8),
'KoCC': ("Kobo Clara Colour", (1072, 1448), Palette16, 1.8),
'KoL': ("Kobo Libra H2O/Kobo Libra 2", (1264, 1680), Palette16, 1.8), 'KoL': ("Kobo Libra H2O/Kobo Libra 2", (1264, 1680), Palette16, 1.8),
'KoLC': ("Kobo Libra Colour", (1264, 1680), Palette16, 1.8),
'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),
'OTHER': ("Other", (0, 0), Palette16, 1.8), 'OTHER': ("Other", (0, 0), Palette16, 1.8),
``` ```
### Standalone `kcc-c2e.py` usage: ### Standalone `kcc-c2e.py` usage:
``` ```
@@ -207,56 +210,8 @@ OTHER:
-h, --help Show this help message and exit -h, --help Show this help message and exit
``` ```
## INSTALL FROM SOURCE
This section is for developers who want to contribute to KCC or power users who want to run the latest code without waiting for an official release.
Easiest to use [GitHub Desktop](https://desktop.github.com) to clone the KCC repo. From GitHub Desktop, click on `Repository` in the toolbar, then `Command Prompt` (Windows)/`Terminal` (Mac) to open a window in the KCC repo.
Depending on your system [Python](https://www.python.org) may be called either `python` or `python3`. We use virtual environments (venv) to manage dependencies.
If you want to edit the code, a good code editor is [VS Code](https://code.visualstudio.com).
If you want to edit the `.ui` files, use [Qt Creator](https://www.qt.io/download-qt-installer-oss), included in **Qt for desktop development**.
Then use the `gen_ui_files` scripts to autogenerate the python UI.
### Windows install from source
One time setup and running for the first time:
```
python -m venv venv
venv\Scripts\activate.bat
pip install -r requirements.txt
python kcc.py
```
Every time you close Command Prompt, you will need to re-activate the virtual environment and re-run:
```
venv\Scripts\activate.bat
python kcc.py
```
### macOS install from source
One time setup and running for the first time:
```
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
python kcc.py
```
Every time you close Terminal, you will need to reactivate the virtual environment and re-run:
```
source venv/bin/activate
python kcc.py
```
## CREDITS ## CREDITS
**KCC** is made by **KCC** is made by
- [Ciro Mattia Gonano](http://github.com/ciromattia) - [Ciro Mattia Gonano](http://github.com/ciromattia)
- [Paweł Jastrzębski](http://github.com/AcidWeb) - [Paweł Jastrzębski](http://github.com/AcidWeb)
@@ -291,5 +246,5 @@ The app relies and includes the following scripts:
Please check [wiki page](https://github.com/ciromattia/kcc/wiki/Known-issues). Please check [wiki page](https://github.com/ciromattia/kcc/wiki/Known-issues).
## COPYRIGHT ## COPYRIGHT
Copyright (c) 2012-2023 Ciro Mattia Gonano, Paweł Jastrzębski and Darodi. Copyright (c) 2012-2023 Ciro Mattia Gonano, Paweł Jastrzębski and Darodi.
**KCC** is released under ISC LICENSE; see [LICENSE.txt](./LICENSE.txt) for further details. **KCC** is released under ISC LICENSE; see [LICENSE.txt](./LICENSE.txt) for further details.

View File

@@ -5,7 +5,7 @@ channels:
dependencies: dependencies:
- python=3.11 - python=3.11
- Pillow>=5.2.0 - Pillow>=5.2.0
- psutil>=5.9.5 - psutil>=5.0.0
- python-slugify>=1.2.1 - python-slugify>=1.2.1
- raven>=6.0.0 - raven>=6.0.0
- distro - distro
@@ -13,4 +13,4 @@ dependencies:
- pip - pip
- pip: - pip:
- mozjpeg-lossless-optimization>=1.1.2 - mozjpeg-lossless-optimization>=1.1.2
- pyside6>=6.5.1 - PyQt5>=5.6.0

View File

@@ -1,3 +1,11 @@
pyside6-uic gui/KCC.ui > kindlecomicconverter/KCC_ui.py
pyside6-uic gui/MetaEditor.ui > kindlecomicconverter/KCC_ui_editor.py REM install qt creator
pyside6-rcc gui/KCC.qrc > kindlecomicconverter/KCC_rc.py REM conda create -n qtenv python=3.7
REM conda activate qtenv
REM pip install PyQt5
pyuic5 gui/KCC.ui > kindlecomicconverter/KCC_ui.py
pyuic5 gui/MetaEditor.ui > kindlecomicconverter/KCC_ui_editor.py
pyrcc5 gui/KCC.qrc > kindlecomicconverter/KCC_rc.py

View File

@@ -1,5 +1,10 @@
#!/bin/sh #!/bin/sh
pyside6-uic gui/KCC.ui --from-imports > kindlecomicconverter/KCC_ui.py # PREPARE PYTHON ENV
pyside6-uic gui/MetaEditor.ui --from-imports > kindlecomicconverter/KCC_ui_editor.py # conda create -n pyqt5 python=3.7
pyside6-rcc gui/KCC.qrc > kindlecomicconverter/KCC_rc.py # source activate pyqt5
# pip install pyqt5
pyuic5 gui/KCC.ui --from-imports > kindlecomicconverter/KCC_ui.py
pyuic5 gui/MetaEditor.ui --from-imports > kindlecomicconverter/KCC_ui_editor.py
pyrcc5 gui/KCC.qrc > kindlecomicconverter/KCC_rc.py

View File

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

View File

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

View File

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

6
kcc.py
View File

@@ -50,13 +50,7 @@ elif sys.platform.startswith('win'):
win_paths = [ win_paths = [
os.path.expandvars('%LOCALAPPDATA%\\Amazon\\KC2'), os.path.expandvars('%LOCALAPPDATA%\\Amazon\\KC2'),
os.path.expandvars('%LOCALAPPDATA%\\Amazon\\Kindle Previewer 3\\lib\\fc\\bin\\'), os.path.expandvars('%LOCALAPPDATA%\\Amazon\\Kindle Previewer 3\\lib\\fc\\bin\\'),
os.path.expandvars('%UserProfile%\\Kindle Previewer 3\\lib\\fc\\bin\\'),
'C:\\Apps\\Kindle Previewer 3\\lib\\fc\\bin',
'D:\\Apps\\Kindle Previewer 3\\lib\\fc\\bin',
'E:\\Apps\\Kindle Previewer 3\\lib\\fc\\bin',
'C:\\Program Files\\7-Zip', 'C:\\Program Files\\7-Zip',
'D:\\Program Files\\7-Zip',
'E:\\Program Files\\7-Zip',
] ]
if getattr(sys, 'frozen', False): if getattr(sys, 'frozen', False):
os.environ['PATH'] += os.pathsep + os.pathsep.join(win_paths) os.environ['PATH'] += os.pathsep + os.pathsep.join(win_paths)

39
kcc.spec Normal file
View File

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

View File

@@ -18,6 +18,7 @@
# PERFORMANCE OF THIS SOFTWARE. # PERFORMANCE OF THIS SOFTWARE.
import os import os
import re import re
import subprocess
import sys import sys
from urllib.parse import unquote from urllib.parse import unquote
from time import sleep from time import sleep
@@ -26,16 +27,14 @@ from subprocess import STDOUT, PIPE
import requests import requests
# noinspection PyUnresolvedReferences # noinspection PyUnresolvedReferences
from PySide6 import QtGui, QtCore, QtWidgets, QtNetwork from PyQt5 import QtGui, QtCore, QtWidgets, QtNetwork
from PySide6.QtCore import Qt
from xml.sax.saxutils import escape from xml.sax.saxutils import escape
from psutil import Process from psutil import Process
from copy import copy from copy import copy
from packaging.version import Version from distutils.version import StrictVersion
from raven import Client from raven import Client
from tempfile import gettempdir from tempfile import gettempdir
from .shared import md5Checksum, 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
@@ -45,7 +44,7 @@ from . import KCC_ui_editor
class QApplicationMessaging(QtWidgets.QApplication): class QApplicationMessaging(QtWidgets.QApplication):
messageFromOtherInstance = QtCore.Signal(bytes) messageFromOtherInstance = QtCore.pyqtSignal(bytes)
def __init__(self, argv): def __init__(self, argv):
QtWidgets.QApplication.__init__(self, argv) QtWidgets.QApplication.__init__(self, argv)
@@ -53,7 +52,7 @@ class QApplicationMessaging(QtWidgets.QApplication):
self._timeout = 1000 self._timeout = 1000
self._locked = False self._locked = False
socket = QtNetwork.QLocalSocket(self) socket = QtNetwork.QLocalSocket(self)
socket.connectToServer(self._key, QtCore.QIODeviceBase.OpenModeFlag.WriteOnly) socket.connectToServer(self._key, QtCore.QIODevice.WriteOnly)
if not socket.waitForConnected(self._timeout): if not socket.waitForConnected(self._timeout):
self._server = QtNetwork.QLocalServer(self) self._server = QtNetwork.QLocalServer(self)
self._server.newConnection.connect(self.handleMessage) self._server.newConnection.connect(self.handleMessage)
@@ -67,7 +66,7 @@ class QApplicationMessaging(QtWidgets.QApplication):
self._server.close() self._server.close()
def event(self, e): def event(self, e):
if e.type() == QtCore.QEvent.Type.FileOpen: if e.type() == QtCore.QEvent.FileOpen:
self.messageFromOtherInstance.emit(bytes(e.file(), 'UTF-8')) self.messageFromOtherInstance.emit(bytes(e.file(), 'UTF-8'))
return True return True
else: else:
@@ -83,7 +82,7 @@ class QApplicationMessaging(QtWidgets.QApplication):
def sendMessage(self, message): def sendMessage(self, message):
socket = QtNetwork.QLocalSocket(self) socket = QtNetwork.QLocalSocket(self)
socket.connectToServer(self._key, QtCore.QIODeviceBase.OpenModeFlag.WriteOnly) socket.connectToServer(self._key, QtCore.QIODevice.WriteOnly)
socket.waitForConnected(self._timeout) socket.waitForConnected(self._timeout)
socket.write(bytes(message, 'UTF-8')) socket.write(bytes(message, 'UTF-8'))
socket.waitForBytesWritten(self._timeout) socket.waitForBytesWritten(self._timeout)
@@ -91,40 +90,40 @@ class QApplicationMessaging(QtWidgets.QApplication):
class QMainWindowKCC(QtWidgets.QMainWindow): class QMainWindowKCC(QtWidgets.QMainWindow):
progressBarTick = QtCore.Signal(str) progressBarTick = QtCore.pyqtSignal(str)
modeConvert = QtCore.Signal(int) modeConvert = QtCore.pyqtSignal(int)
addMessage = QtCore.Signal(str, str, bool) addMessage = QtCore.pyqtSignal(str, str, bool)
addTrayMessage = QtCore.Signal(str, str) addTrayMessage = QtCore.pyqtSignal(str, str)
showDialog = QtCore.Signal(str, str) showDialog = QtCore.pyqtSignal(str, str)
hideProgressBar = QtCore.Signal() hideProgressBar = QtCore.pyqtSignal()
forceShutdown = QtCore.Signal() forceShutdown = QtCore.pyqtSignal()
class Icons: class Icons:
def __init__(self): def __init__(self):
self.deviceKindle = QtGui.QIcon() self.deviceKindle = QtGui.QIcon()
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.Normal, QtGui.QIcon.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.Normal, QtGui.QIcon.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.Normal, QtGui.QIcon.Off)
self.MOBIFormat = QtGui.QIcon() self.MOBIFormat = QtGui.QIcon()
self.MOBIFormat.addPixmap(QtGui.QPixmap(":/Formats/icons/MOBI.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off) self.MOBIFormat.addPixmap(QtGui.QPixmap(":/Formats/icons/MOBI.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.CBZFormat = QtGui.QIcon() self.CBZFormat = QtGui.QIcon()
self.CBZFormat.addPixmap(QtGui.QPixmap(":/Formats/icons/CBZ.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off) self.CBZFormat.addPixmap(QtGui.QPixmap(":/Formats/icons/CBZ.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.EPUBFormat = QtGui.QIcon() self.EPUBFormat = QtGui.QIcon()
self.EPUBFormat.addPixmap(QtGui.QPixmap(":/Formats/icons/EPUB.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off) self.EPUBFormat.addPixmap(QtGui.QPixmap(":/Formats/icons/EPUB.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.info = QtGui.QIcon() self.info = QtGui.QIcon()
self.info.addPixmap(QtGui.QPixmap(":/Status/icons/info.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off) self.info.addPixmap(QtGui.QPixmap(":/Status/icons/info.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.warning = QtGui.QIcon() self.warning = QtGui.QIcon()
self.warning.addPixmap(QtGui.QPixmap(":/Status/icons/warning.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off) self.warning.addPixmap(QtGui.QPixmap(":/Status/icons/warning.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.error = QtGui.QIcon() self.error = QtGui.QIcon()
self.error.addPixmap(QtGui.QPixmap(":/Status/icons/error.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off) self.error.addPixmap(QtGui.QPixmap(":/Status/icons/error.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.programIcon = QtGui.QIcon() self.programIcon = QtGui.QIcon()
self.programIcon.addPixmap(QtGui.QPixmap(":/Icon/icons/comic2ebook.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off) self.programIcon.addPixmap(QtGui.QPixmap(":/Icon/icons/comic2ebook.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
class VersionThread(QtCore.QThread): class VersionThread(QtCore.QThread):
@@ -146,9 +145,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 Version(latest_version) > Version(__version__)) \ if ("b" not in __version__ and StrictVersion(latest_version) > StrictVersion(__version__)) \
or ("b" in __version__ or ("b" in __version__
and Version(latest_version) >= Version(re.sub(r'b.*', '', __version__))): and StrictVersion(latest_version) >= StrictVersion(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:
@@ -221,28 +220,28 @@ class WorkerThread(QtCore.QThread):
options.format = gui_current_format options.format = gui_current_format
if GUI.mangaBox.isChecked(): if GUI.mangaBox.isChecked():
options.righttoleft = True options.righttoleft = True
if GUI.rotateBox.checkState() == Qt.CheckState.PartiallyChecked: if GUI.rotateBox.checkState() == 1:
options.splitter = 2 options.splitter = 2
elif GUI.rotateBox.checkState() == Qt.CheckState.Checked: elif GUI.rotateBox.checkState() == 2:
options.splitter = 1 options.splitter = 1
if GUI.qualityBox.checkState() == Qt.CheckState.PartiallyChecked: if GUI.qualityBox.checkState() == 1:
options.autoscale = True options.autoscale = True
elif GUI.qualityBox.checkState() == Qt.CheckState.Checked: elif GUI.qualityBox.checkState() == 2:
options.hq = True options.hq = True
if GUI.webtoonBox.isChecked(): if GUI.webtoonBox.isChecked():
options.webtoon = True options.webtoon = True
if GUI.upscaleBox.checkState() == Qt.CheckState.PartiallyChecked: if GUI.upscaleBox.checkState() == 1:
options.stretch = True options.stretch = True
elif GUI.upscaleBox.checkState() == Qt.CheckState.Checked: elif GUI.upscaleBox.checkState() == 2:
options.upscale = True options.upscale = True
if GUI.gammaBox.isChecked() and float(GUI.gammaValue) > 0.09: if GUI.gammaBox.isChecked() and float(GUI.gammaValue) > 0.09:
options.gamma = float(GUI.gammaValue) options.gamma = float(GUI.gammaValue)
options.cropping = GUI.croppingBox.checkState().value options.cropping = GUI.croppingBox.checkState()
if GUI.croppingBox.checkState() != Qt.CheckState.Unchecked: if GUI.croppingBox.checkState() >= 1:
options.croppingp = float(GUI.croppingPowerValue) options.croppingp = float(GUI.croppingPowerValue)
if GUI.borderBox.checkState() == Qt.CheckState.PartiallyChecked: if GUI.borderBox.checkState() == 1:
options.white_borders = True options.white_borders = True
elif GUI.borderBox.checkState() == Qt.CheckState.Checked: elif GUI.borderBox.checkState() == 2:
options.black_borders = True options.black_borders = True
if GUI.outputSplit.isChecked(): if GUI.outputSplit.isChecked():
options.batchsplit = 2 options.batchsplit = 2
@@ -254,17 +253,13 @@ 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.mozJpegBox.checkState() == 1:
options.dedupecover = True
if GUI.mozJpegBox.checkState() == Qt.CheckState.PartiallyChecked:
options.forcepng = True options.forcepng = True
elif GUI.mozJpegBox.checkState() == Qt.CheckState.Checked: elif GUI.mozJpegBox.checkState() == 2:
options.mozjpeg = True options.mozjpeg = True
if GUI.currentMode > 2: if GUI.currentMode > 2:
options.customwidth = str(GUI.widthBox.value()) options.customwidth = str(GUI.widthBox.value())
options.customheight = str(GUI.heightBox.value()) options.customheight = str(GUI.heightBox.value())
if GUI.targetDirectory != '':
options.output = GUI.targetDirectory
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
@@ -433,7 +428,7 @@ class SystemTrayIcon(QtWidgets.QSystemTrayIcon):
def __init__(self): def __init__(self):
super().__init__() super().__init__()
if self.isSystemTrayAvailable(): if self.isSystemTrayAvailable():
self.setIcon(GUI.icons.programIcon) QtWidgets.QSystemTrayIcon.__init__(self, GUI.icons.programIcon, MW)
self.activated.connect(self.catchClicks) self.activated.connect(self.catchClicks)
def catchClicks(self): def catchClicks(self):
@@ -442,7 +437,7 @@ class SystemTrayIcon(QtWidgets.QSystemTrayIcon):
MW.activateWindow() MW.activateWindow()
def addTrayMessage(self, message, icon): def addTrayMessage(self, message, icon):
icon = getattr(QtWidgets.QSystemTrayIcon.MessageIcon, icon) icon = eval('QtWidgets.QSystemTrayIcon.' + icon)
if self.supportsMessages() and not MW.isActiveWindow(): if self.supportsMessages() and not MW.isActiveWindow():
self.showMessage('Kindle Comic Converter', message, icon) self.showMessage('Kindle Comic Converter', message, icon)
@@ -464,7 +459,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
if self.needClean: if self.needClean:
self.needClean = False self.needClean = False
GUI.jobList.clear() GUI.jobList.clear()
if self.tar or self.sevenzip: if self.sevenzip:
fnames = QtWidgets.QFileDialog.getOpenFileNames(MW, 'Select file', self.lastPath, fnames = QtWidgets.QFileDialog.getOpenFileNames(MW, 'Select file', self.lastPath,
'Comic (*.cbz *.cbr *.cb7 *.zip *.rar *.7z *.pdf);;All (*.*)') 'Comic (*.cbz *.cbr *.cb7 *.zip *.rar *.7z *.pdf);;All (*.*)')
else: else:
@@ -494,8 +489,6 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
else: else:
fname = [''] fname = ['']
self.showDialog("Editor is disabled due to a lack of 7z.", 'error') self.showDialog("Editor is disabled due to a lack of 7z.", 'error')
self.addMessage('<a href="https://github.com/ciromattia/kcc#7-zip">Install 7z (link)</a>'
' to enable metadata editing.', 'warning')
if fname[0] != '': if fname[0] != '':
if sys.platform.startswith('win'): if sys.platform.startswith('win'):
sname = fname[0].replace('/', '\\') sname = fname[0].replace('/', '\\')
@@ -554,7 +547,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
self.conversionAlive = False self.conversionAlive = False
self.worker.sync() self.worker.sync()
icon = QtGui.QIcon() icon = QtGui.QIcon()
icon.addPixmap(QtGui.QPixmap(":/Other/icons/convert.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off) icon.addPixmap(QtGui.QPixmap(":/Other/icons/convert.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
GUI.convertButton.setIcon(icon) GUI.convertButton.setIcon(icon)
GUI.convertButton.setText('Convert') GUI.convertButton.setText('Convert')
GUI.centralWidget.setAcceptDrops(True) GUI.centralWidget.setAcceptDrops(True)
@@ -562,7 +555,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
self.conversionAlive = True self.conversionAlive = True
self.worker.sync() self.worker.sync()
icon = QtGui.QIcon() icon = QtGui.QIcon()
icon.addPixmap(QtGui.QPixmap(":/Other/icons/clear.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off) icon.addPixmap(QtGui.QPixmap(":/Other/icons/clear.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
GUI.convertButton.setIcon(icon) GUI.convertButton.setIcon(icon)
GUI.convertButton.setText('Abort') GUI.convertButton.setText('Abort')
GUI.centralWidget.setAcceptDrops(False) GUI.centralWidget.setAcceptDrops(False)
@@ -642,7 +635,6 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
self.modeChange(2) self.modeChange(2)
else: else:
self.modeChange(1) self.modeChange(1)
GUI.colorBox.setChecked(profile['ForceColor'])
self.changeFormat() self.changeFormat()
GUI.gammaSlider.setValue(0) GUI.gammaSlider.setValue(0)
self.changeGamma(0) self.changeGamma(0)
@@ -677,7 +669,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
def addMessage(self, message, icon, replace=False): def addMessage(self, message, icon, replace=False):
if icon != '': if icon != '':
icon = getattr(self.icons, icon) icon = eval('self.icons.' + icon)
item = QtWidgets.QListWidgetItem(icon, ' ' + self.stripTags(message)) item = QtWidgets.QListWidgetItem(icon, ' ' + self.stripTags(message))
else: else:
item = QtWidgets.QListWidgetItem(' ' + self.stripTags(message)) item = QtWidgets.QListWidgetItem(' ' + self.stripTags(message))
@@ -695,7 +687,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
def showDialog(self, message, kind): def showDialog(self, message, kind):
if kind == 'error': if kind == 'error':
QtWidgets.QMessageBox.critical(MW, 'KCC - Error', message, QtWidgets.QMessageBox.StandardButton.Ok) QtWidgets.QMessageBox.critical(MW, 'KCC - Error', message, QtWidgets.QMessageBox.Ok)
elif kind == 'question': elif kind == 'question':
GUI.versionCheck.setAnswer(QtWidgets.QMessageBox.question(MW, 'KCC - Question', message, GUI.versionCheck.setAnswer(QtWidgets.QMessageBox.question(MW, 'KCC - Question', message,
QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.Yes,
@@ -723,7 +715,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
self.conversionAlive = False self.conversionAlive = False
self.worker.sync() self.worker.sync()
else: else:
if QtWidgets.QApplication.keyboardModifiers() == QtCore.Qt.KeyboardModifier.ShiftModifier: if QtWidgets.QApplication.keyboardModifiers() == QtCore.Qt.ShiftModifier:
dname = QtWidgets.QFileDialog.getExistingDirectory(MW, 'Select output directory', self.lastPath) dname = QtWidgets.QFileDialog.getExistingDirectory(MW, 'Select output directory', self.lastPath)
if dname != '': if dname != '':
if sys.platform.startswith('win'): if sys.platform.startswith('win'):
@@ -757,7 +749,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
def display_kindlegen_missing(self): def display_kindlegen_missing(self):
self.addMessage( self.addMessage(
'<a href="https://github.com/ciromattia/kcc#kindlegen"><b>Install KindleGen (link)</b></a> to enable MOBI conversion for Kindles!', '<a href="https://github.com/ciromattia/kcc#kindlegen"><b>Install KindleGen (link)</b></a> to enable MOBI conversion for Kindles!',
'error' 'error'
) )
@@ -776,24 +768,23 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
self.settings.setValue('currentFormat', GUI.formatBox.currentIndex()) self.settings.setValue('currentFormat', GUI.formatBox.currentIndex())
self.settings.setValue('startNumber', self.startNumber + 1) self.settings.setValue('startNumber', self.startNumber + 1)
self.settings.setValue('windowSize', str(MW.size().width()) + 'x' + str(MW.size().height())) self.settings.setValue('windowSize', str(MW.size().width()) + 'x' + str(MW.size().height()))
self.settings.setValue('options', {'mangaBox': GUI.mangaBox.checkState().value, self.settings.setValue('options', {'mangaBox': GUI.mangaBox.checkState(),
'rotateBox': GUI.rotateBox.checkState().value, 'rotateBox': GUI.rotateBox.checkState(),
'qualityBox': GUI.qualityBox.checkState().value, 'qualityBox': GUI.qualityBox.checkState(),
'gammaBox': GUI.gammaBox.checkState().value, 'gammaBox': GUI.gammaBox.checkState(),
'croppingBox': GUI.croppingBox.checkState().value, 'croppingBox': GUI.croppingBox.checkState(),
'croppingPowerSlider': float(self.croppingPowerValue) * 100, 'croppingPowerSlider': float(self.croppingPowerValue) * 100,
'upscaleBox': GUI.upscaleBox.checkState().value, 'upscaleBox': GUI.upscaleBox.checkState(),
'borderBox': GUI.borderBox.checkState().value, 'borderBox': GUI.borderBox.checkState(),
'webtoonBox': GUI.webtoonBox.checkState().value, 'webtoonBox': GUI.webtoonBox.checkState(),
'outputSplit': GUI.outputSplit.checkState().value, 'outputSplit': GUI.outputSplit.checkState(),
'colorBox': GUI.colorBox.checkState().value, 'colorBox': GUI.colorBox.checkState(),
'disableProcessingBox': GUI.disableProcessingBox.checkState().value, 'disableProcessingBox': GUI.disableProcessingBox.checkState(),
'mozJpegBox': GUI.mozJpegBox.checkState().value, 'mozJpegBox': GUI.mozJpegBox.checkState(),
'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(),
'dedupeCoverBox': GUI.dedupeCoverBox.checkState().value, 'maximizeStrips': GUI.maximizeStrips.checkState(),
'maximizeStrips': GUI.maximizeStrips.checkState().value,
'gammaSlider': float(self.gammaValue) * 100}) 'gammaSlider': float(self.gammaValue) * 100})
self.settings.sync() self.settings.sync()
self.tray.hide() self.tray.hide()
@@ -808,7 +799,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
self.needClean = False self.needClean = False
GUI.jobList.clear() GUI.jobList.clear()
formats = ['.pdf'] formats = ['.pdf']
if self.tar or self.sevenzip: if self.sevenzip:
formats.extend(['.cb7', '.7z', '.cbz', '.zip', '.cbr', '.rar']) formats.extend(['.cb7', '.7z', '.cbz', '.zip', '.cbr', '.rar'])
if os.path.isdir(message): if os.path.isdir(message):
GUI.jobList.addItem(message) GUI.jobList.addItem(message)
@@ -846,12 +837,12 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
except Exception: except Exception:
pass pass
try: try:
versionCheck = subprocess_run(['kindlegen', '-locale', 'en'], stdout=PIPE, stderr=STDOUT, encoding='UTF-8') versionCheck = subprocess_run_silent(['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 Version(versionCheck) < Version('2.9'): if StrictVersion(versionCheck) < StrictVersion('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
@@ -868,7 +859,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
self.setupUi(MW) self.setupUi(MW)
self.editor = KCCGUI_MetaEditor() self.editor = KCCGUI_MetaEditor()
self.icons = Icons() self.icons = Icons()
self.settings = QtCore.QSettings('ciromattia', 'kcc') self.settings = QtCore.QSettings('KindleComicConverter', 'KindleComicConverter')
self.settingsVersion = self.settings.value('settingsVersion', '', type=str) self.settingsVersion = self.settings.value('settingsVersion', '', type=str)
self.lastPath = self.settings.value('lastPath', '', type=str) self.lastPath = self.settings.value('lastPath', '', type=str)
self.lastDevice = self.settings.value('lastDevice', 0, type=int) self.lastDevice = self.settings.value('lastDevice', 0, type=int)
@@ -901,10 +892,10 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
elif sys.platform.startswith('darwin'): elif sys.platform.startswith('darwin'):
for element in ['editorButton', 'wikiButton', 'directoryButton', 'clearButton', 'fileButton', 'deviceBox', for element in ['editorButton', 'wikiButton', 'directoryButton', 'clearButton', 'fileButton', 'deviceBox',
'convertButton', 'formatBox']: 'convertButton', 'formatBox']:
getattr(GUI, element).setMinimumSize(QtCore.QSize(0, 0)) eval('GUI.' + element).setMinimumSize(QtCore.QSize(0, 0))
GUI.gridLayout.setContentsMargins(-1, -1, -1, -1) GUI.gridLayout.setContentsMargins(-1, -1, -1, -1)
for element in ['gridLayout_2', 'gridLayout_3', 'gridLayout_4', 'horizontalLayout', 'horizontalLayout_2']: for element in ['gridLayout_2', 'gridLayout_3', 'gridLayout_4', 'horizontalLayout', 'horizontalLayout_2']:
getattr(GUI, element).setContentsMargins(-1, 0, -1, 0) eval('GUI.' + element).setContentsMargins(-1, 0, -1, 0)
if self.windowSize == '0x0': if self.windowSize == '0x0':
MW.resize(500, 500) MW.resize(500, 500)
@@ -919,100 +910,94 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
self.profiles = { self.profiles = {
"Kindle Oasis 9/10": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, "Kindle Oasis 2/3": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': True, 'ForceColor': False, 'Label': 'KO'}, 'DefaultUpscale': True, 'Label': 'KO'},
"Kindle Oasis 8": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, "Kindle Oasis": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': True, 'ForceColor': False, 'Label': 'KV'}, 'DefaultUpscale': True, 'Label': 'KV'},
"Kindle Voyage": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, "Kindle Voyage": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': True, 'ForceColor': False, 'Label': 'KV'}, 'DefaultUpscale': True, 'Label': 'KV'},
"Kindle Scribe": { "Kindle Scribe": {
'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': False, 'ForceColor': False, 'Label': 'KS', 'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': False, 'Label': 'KS',
}, },
"Kindle 11": { "Kindle 11": {
'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': True, 'ForceColor': False, 'Label': 'K11', 'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': True, 'Label': 'K11',
}, },
"Kindle PW 11": { "Kindle PW 5": {
'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': True, 'ForceColor': False, 'Label': 'KPW5', 'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': True, 'Label': 'KPW5',
}, },
"Kindle PW 7/10": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, "Kindle PW 3/4": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': True, 'ForceColor': False, 'Label': 'KV'}, 'DefaultUpscale': True, 'Label': 'KV'},
"Kindle PW 5/6": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, "Kindle PW 1/2": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': False, 'ForceColor': False, 'Label': 'KPW'}, 'DefaultUpscale': False, 'Label': 'KPW'},
"Kindle 4/5/7/8/10": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, "Kindle 4/5/7/8/10": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': False, 'ForceColor': False, 'Label': 'K578'}, 'DefaultUpscale': False, 'Label': 'K578'},
"Kindle DX": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 2, "Kindle DX/DXG": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 2,
'DefaultUpscale': False, 'ForceColor': False, 'Label': 'KDX'}, 'DefaultUpscale': False, 'Label': 'KDX'},
"Kobo Mini/Touch": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, "Kobo Mini/Touch": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1,
'DefaultUpscale': False, 'ForceColor': False, 'Label': 'KoMT'}, 'DefaultUpscale': False, 'Label': 'KoMT'},
"Kobo Glo": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, "Kobo Glo": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1,
'DefaultUpscale': False, 'ForceColor': False, 'Label': 'KoG'}, 'DefaultUpscale': False, 'Label': 'KoG'},
"Kobo Glo HD": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, "Kobo Glo HD": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1,
'DefaultUpscale': False, 'ForceColor': False, 'Label': 'KoGHD'}, 'DefaultUpscale': False, 'Label': 'KoGHD'},
"Kobo Aura": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, "Kobo Aura": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1,
'DefaultUpscale': False, 'ForceColor': False, 'Label': 'KoA'}, 'DefaultUpscale': False, 'Label': 'KoA'},
"Kobo Aura HD": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, "Kobo Aura HD": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1,
'DefaultUpscale': True, 'ForceColor': False, 'Label': 'KoAHD'}, 'DefaultUpscale': True, 'Label': 'KoAHD'},
"Kobo Aura H2O": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, "Kobo Aura H2O": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1,
'DefaultUpscale': True, 'ForceColor': False, 'Label': 'KoAH2O'}, 'DefaultUpscale': True, 'Label': 'KoAH2O'},
"Kobo Aura ONE": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, "Kobo Aura ONE": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1,
'DefaultUpscale': True, 'ForceColor': False, 'Label': 'KoAO'}, 'DefaultUpscale': True, 'Label': 'KoAO'},
"Kobo Clara HD": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, "Kobo Clara HD": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1,
'DefaultUpscale': True, 'ForceColor': False, 'Label': 'KoC'}, 'DefaultUpscale': True, 'Label': 'KoC'},
"Kobo Libra H2O": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, "Kobo Libra H2O": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1,
'DefaultUpscale': True, 'ForceColor': False, 'Label': 'KoL'}, 'DefaultUpscale': True, 'Label': 'KoL'},
"Kobo Forma": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, "Kobo Forma": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1,
'DefaultUpscale': True, 'ForceColor': False, 'Label': 'KoF'}, 'DefaultUpscale': True, 'Label': 'KoF'},
"Kindle 1": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 0, "Kindle 1": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': False, 'ForceColor': False, 'Label': 'K1'}, 'DefaultUpscale': False, 'Label': 'K1'},
"Kindle 2": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 0, "Kindle 2": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': False, 'ForceColor': False, 'Label': 'K2'}, 'DefaultUpscale': False, 'Label': 'K2'},
"Kindle Keyboard": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 0, "Kindle Keyboard": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': False, 'ForceColor': False, 'Label': 'K34'}, 'DefaultUpscale': False, 'Label': 'K34'},
"Kindle Touch": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 0, "Kindle Touch": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': False, 'ForceColor': False, 'Label': 'K34'}, 'DefaultUpscale': False, 'Label': 'K34'},
"Kobo Nia": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, 'DefaultUpscale': True, 'ForceColor': False, "Kobo Nia": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, 'DefaultUpscale': True,
'Label': 'KoN'}, 'Label': 'KoN'},
"Kobo Clara 2E": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, 'DefaultUpscale': True, 'ForceColor': False, "Kobo Clara 2E": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, 'DefaultUpscale': True,
'Label': 'KoC'}, 'Label': 'KoC'},
"Kobo Clara Colour": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, 'DefaultUpscale': True, 'ForceColor': True, "Kobo Libra 2": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, 'DefaultUpscale': True,
'Label': 'KoCC'},
"Kobo Libra 2": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, 'DefaultUpscale': True, 'ForceColor': False,
'Label': 'KoL'}, 'Label': 'KoL'},
"Kobo Libra Colour": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, 'DefaultUpscale': True, 'ForceColor': True, "Kobo Sage": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, 'DefaultUpscale': True,
'Label': 'KoLC'},
"Kobo Sage": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, 'DefaultUpscale': True, 'ForceColor': False,
'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,
'Label': 'KoE'}, 'Label': 'KoE'},
"Other": {'PVOptions': False, 'ForceExpert': True, 'DefaultFormat': 1, 'DefaultUpscale': False, 'ForceColor': False, "Other": {'PVOptions': False, 'ForceExpert': True, 'DefaultFormat': 1, 'DefaultUpscale': False,
'Label': 'OTHER'}, 'Label': 'OTHER'},
} }
profilesGUI = [ profilesGUI = [
"Kindle Scribe", "Kindle Oasis 2/3",
"Kindle PW 5",
"Kindle 11", "Kindle 11",
"Kindle PW 11", "Kindle Scribe",
"Kindle Oasis 9/10",
"Separator", "Separator",
"Kobo Clara 2E", "Kobo Clara 2E",
"Kobo Clara Colour",
"Kobo Sage", "Kobo Sage",
"Kobo Libra 2", "Kobo Libra 2",
"Kobo Libra Colour",
"Kobo Elipsa", "Kobo Elipsa",
"Kobo Nia", "Kobo Nia",
"Separator", "Separator",
"Other", "Other",
"Separator", "Separator",
"Kindle Oasis 8", "Kindle Oasis",
"Kindle PW 7/10",
"Kindle Voyage",
"Kindle PW 5/6",
"Kindle 4/5/7/8/10",
"Kindle Touch", "Kindle Touch",
"Kindle Keyboard", "Kindle Keyboard",
"Kindle DX", "Kindle DX/DXG",
"Kindle PW 3/4",
"Kindle PW 1/2",
"Kindle Voyage",
"Kindle 2", "Kindle 2",
"Kindle 1", "Kindle 1",
"Kindle 4/5/7/8/10",
"Separator", "Separator",
"Kobo Aura", "Kobo Aura",
"Kobo Aura ONE", "Kobo Aura ONE",
@@ -1030,7 +1015,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
'com/ciromattia/kcc/blob/master/README.md#issues--new-features--donations">DO' 'com/ciromattia/kcc/blob/master/README.md#issues--new-features--donations">DO'
'NATE</a> - <a href="http://www.mobileread.com/forums/showthread.php?t=207461' 'NATE</a> - <a href="http://www.mobileread.com/forums/showthread.php?t=207461'
'">FORUM</a></b>') '">FORUM</a></b>')
statusBarLabel.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) statusBarLabel.setAlignment(QtCore.Qt.AlignCenter)
statusBarLabel.setOpenExternalLinks(True) statusBarLabel.setOpenExternalLinks(True)
GUI.statusBar.addPermanentWidget(statusBarLabel, 1) GUI.statusBar.addPermanentWidget(statusBarLabel, 1)
@@ -1041,18 +1026,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(['tar'], stdout=PIPE, stderr=STDOUT) subprocess_run_silent(['7z'], stdout=PIPE, stderr=STDOUT)
self.tar = True
except FileNotFoundError:
self.tar = False
try:
subprocess_run(['7z'], stdout=PIPE, stderr=STDOUT)
self.sevenzip = True self.sevenzip = True
except FileNotFoundError: except FileNotFoundError:
self.sevenzip = False self.sevenzip = False
if not self.tar: self.addMessage('<a href="https://github.com/ciromattia/kcc#7-zip">Install 7z (link)</a>'
self.addMessage('<a href="https://github.com/ciromattia/kcc#7-zip">Install 7z (link)</a>' ' to enable CBZ/CBR/ZIP/etc processing.', 'warning')
' to enable CBZ/CBR/ZIP/etc processing.', 'warning')
self.detectKindleGen(True) self.detectKindleGen(True)
APP.messageFromOtherInstance.connect(self.handleMessage) APP.messageFromOtherInstance.connect(self.handleMessage)
@@ -1094,7 +1073,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
else: else:
GUI.deviceBox.addItem(self.icons.deviceKindle, profile) GUI.deviceBox.addItem(self.icons.deviceKindle, profile)
for f in self.formats: for f in self.formats:
GUI.formatBox.addItem(getattr(self.icons, self.formats[f]['icon'] + 'Format'), f) GUI.formatBox.addItem(eval('self.icons.' + self.formats[f]['icon'] + 'Format'), f)
if self.lastDevice > GUI.deviceBox.count(): if self.lastDevice > GUI.deviceBox.count():
self.lastDevice = 0 self.lastDevice = 0
if profilesGUI[self.lastDevice] == "Separator": if profilesGUI[self.lastDevice] == "Separator":
@@ -1120,8 +1099,8 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
self.changeCroppingPower(int(self.options[option])) self.changeCroppingPower(int(self.options[option]))
else: else:
try: try:
if getattr(GUI, option).isEnabled(): if eval('GUI.' + str(option)).isEnabled():
getattr(GUI, option).setCheckState(Qt.CheckState(self.options[option])) eval('GUI.' + str(option)).setCheckState(self.options[option])
except AttributeError: except AttributeError:
pass pass
self.worker.sync() self.worker.sync()
@@ -1195,7 +1174,7 @@ class KCCGUI_MetaEditor(KCC_ui_editor.Ui_editorDialog):
self.ui = QtWidgets.QDialog() self.ui = QtWidgets.QDialog()
self.parser = None self.parser = None
self.setupUi(self.ui) self.setupUi(self.ui)
self.ui.setWindowFlags(self.ui.windowFlags() & ~QtCore.Qt.WindowType.WindowContextHelpButtonHint) self.ui.setWindowFlags(self.ui.windowFlags() & ~QtCore.Qt.WindowContextHelpButtonHint)
self.okButton.clicked.connect(self.saveData) self.okButton.clicked.connect(self.saveData)
self.cancelButton.clicked.connect(self.ui.close) self.cancelButton.clicked.connect(self.ui.close)
if sys.platform.startswith('linux'): if sys.platform.startswith('linux'):

File diff suppressed because it is too large Load Diff

View File

@@ -1,463 +1,321 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
################################################################################ # Form implementation generated from reading ui file 'gui/KCC.ui'
## Form generated from reading UI file 'KCC.ui' #
## # Created by: PyQt5 UI code generator 5.15.7
## Created by: Qt User Interface Compiler version 6.5.2 #
## # WARNING: Any manual changes made to this file will be lost when pyuic5 is
## WARNING! All changes made in this file will be lost when recompiling UI file! # run again. Do not edit this file unless you know what you are doing.
################################################################################
from PyQt5 import QtCore, QtGui, QtWidgets
from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
QMetaObject, QObject, QPoint, QRect,
QSize, QTime, QUrl, Qt)
from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
QFont, QFontDatabase, QGradient, QIcon,
QImage, QKeySequence, QLinearGradient, QPainter,
QPalette, QPixmap, QRadialGradient, QTransform)
from PySide6.QtWidgets import (QAbstractItemView, QApplication, QCheckBox, QComboBox,
QGridLayout, QHBoxLayout, QLabel, QListWidget,
QListWidgetItem, QMainWindow, QProgressBar, QPushButton,
QSizePolicy, QSlider, QSpinBox, QStatusBar,
QWidget)
from . import KCC_rc
class Ui_mainWindow(object): class Ui_mainWindow(object):
def setupUi(self, mainWindow): def setupUi(self, mainWindow):
if not mainWindow.objectName(): mainWindow.setObjectName("mainWindow")
mainWindow.setObjectName(u"mainWindow")
mainWindow.resize(450, 400) mainWindow.resize(450, 400)
icon = QIcon() icon = QtGui.QIcon()
icon.addFile(u":/Icon/icons/comic2ebook.png", QSize(), QIcon.Normal, QIcon.Off) icon.addPixmap(QtGui.QPixmap(":/Icon/icons/comic2ebook.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
mainWindow.setWindowIcon(icon) mainWindow.setWindowIcon(icon)
self.centralWidget = QWidget(mainWindow) self.centralWidget = QtWidgets.QWidget(mainWindow)
self.centralWidget.setObjectName(u"centralWidget") self.centralWidget.setObjectName("centralWidget")
self.gridLayout = QGridLayout(self.centralWidget) self.gridLayout = QtWidgets.QGridLayout(self.centralWidget)
self.gridLayout.setObjectName(u"gridLayout")
self.gridLayout.setContentsMargins(-1, -1, -1, 5) self.gridLayout.setContentsMargins(-1, -1, -1, 5)
self.optionWidget = QWidget(self.centralWidget) self.gridLayout.setObjectName("gridLayout")
self.optionWidget.setObjectName(u"optionWidget") self.optionWidget = QtWidgets.QWidget(self.centralWidget)
self.gridLayout_2 = QGridLayout(self.optionWidget) self.optionWidget.setObjectName("optionWidget")
self.gridLayout_2.setObjectName(u"gridLayout_2") self.gridLayout_2 = QtWidgets.QGridLayout(self.optionWidget)
self.gridLayout_2.setContentsMargins(0, 0, 0, 0) self.gridLayout_2.setContentsMargins(0, 0, 0, 0)
self.qualityBox = QCheckBox(self.optionWidget) self.gridLayout_2.setObjectName("gridLayout_2")
self.qualityBox.setObjectName(u"qualityBox") self.upscaleBox = QtWidgets.QCheckBox(self.optionWidget)
self.qualityBox.setTristate(True)
self.gridLayout_2.addWidget(self.qualityBox, 0, 2, 1, 1)
self.deleteBox = QCheckBox(self.optionWidget)
self.deleteBox.setObjectName(u"deleteBox")
self.gridLayout_2.addWidget(self.deleteBox, 4, 1, 1, 1)
self.maximizeStrips = QCheckBox(self.optionWidget)
self.maximizeStrips.setObjectName(u"maximizeStrips")
self.gridLayout_2.addWidget(self.maximizeStrips, 3, 1, 1, 1)
self.gammaBox = QCheckBox(self.optionWidget)
self.gammaBox.setObjectName(u"gammaBox")
self.gridLayout_2.addWidget(self.gammaBox, 1, 2, 1, 1)
self.borderBox = QCheckBox(self.optionWidget)
self.borderBox.setObjectName(u"borderBox")
self.borderBox.setTristate(True)
self.gridLayout_2.addWidget(self.borderBox, 2, 0, 1, 1)
self.webtoonBox = QCheckBox(self.optionWidget)
self.webtoonBox.setObjectName(u"webtoonBox")
self.gridLayout_2.addWidget(self.webtoonBox, 1, 0, 1, 1)
self.upscaleBox = QCheckBox(self.optionWidget)
self.upscaleBox.setObjectName(u"upscaleBox")
self.upscaleBox.setTristate(True) self.upscaleBox.setTristate(True)
self.upscaleBox.setObjectName("upscaleBox")
self.gridLayout_2.addWidget(self.upscaleBox, 1, 1, 1, 1) self.gridLayout_2.addWidget(self.upscaleBox, 1, 1, 1, 1)
self.rotateBox = QtWidgets.QCheckBox(self.optionWidget)
self.mangaBox = QCheckBox(self.optionWidget)
self.mangaBox.setObjectName(u"mangaBox")
self.gridLayout_2.addWidget(self.mangaBox, 0, 0, 1, 1)
self.rotateBox = QCheckBox(self.optionWidget)
self.rotateBox.setObjectName(u"rotateBox")
self.rotateBox.setTristate(True) self.rotateBox.setTristate(True)
self.rotateBox.setObjectName("rotateBox")
self.gridLayout_2.addWidget(self.rotateBox, 0, 1, 1, 1) self.gridLayout_2.addWidget(self.rotateBox, 0, 1, 1, 1)
self.outputSplit = QtWidgets.QCheckBox(self.optionWidget)
self.croppingBox = QCheckBox(self.optionWidget) self.outputSplit.setObjectName("outputSplit")
self.croppingBox.setObjectName(u"croppingBox")
self.croppingBox.setTristate(True)
self.gridLayout_2.addWidget(self.croppingBox, 3, 2, 1, 1)
self.outputSplit = QCheckBox(self.optionWidget)
self.outputSplit.setObjectName(u"outputSplit")
self.gridLayout_2.addWidget(self.outputSplit, 2, 1, 1, 1) self.gridLayout_2.addWidget(self.outputSplit, 2, 1, 1, 1)
self.webtoonBox = QtWidgets.QCheckBox(self.optionWidget)
self.mozJpegBox = QCheckBox(self.optionWidget) self.webtoonBox.setObjectName("webtoonBox")
self.mozJpegBox.setObjectName(u"mozJpegBox") self.gridLayout_2.addWidget(self.webtoonBox, 1, 0, 1, 1)
self.mozJpegBox.setTristate(True) self.colorBox = QtWidgets.QCheckBox(self.optionWidget)
self.colorBox.setObjectName("colorBox")
self.gridLayout_2.addWidget(self.mozJpegBox, 3, 0, 1, 1)
self.colorBox = QCheckBox(self.optionWidget)
self.colorBox.setObjectName(u"colorBox")
self.gridLayout_2.addWidget(self.colorBox, 2, 2, 1, 1) self.gridLayout_2.addWidget(self.colorBox, 2, 2, 1, 1)
self.gammaBox = QtWidgets.QCheckBox(self.optionWidget)
self.disableProcessingBox = QCheckBox(self.optionWidget) self.gammaBox.setObjectName("gammaBox")
self.disableProcessingBox.setObjectName(u"disableProcessingBox") self.gridLayout_2.addWidget(self.gammaBox, 1, 2, 1, 1)
self.borderBox = QtWidgets.QCheckBox(self.optionWidget)
self.borderBox.setTristate(True)
self.borderBox.setObjectName("borderBox")
self.gridLayout_2.addWidget(self.borderBox, 2, 0, 1, 1)
self.mangaBox = QtWidgets.QCheckBox(self.optionWidget)
self.mangaBox.setObjectName("mangaBox")
self.gridLayout_2.addWidget(self.mangaBox, 0, 0, 1, 1)
self.qualityBox = QtWidgets.QCheckBox(self.optionWidget)
self.qualityBox.setTristate(True)
self.qualityBox.setObjectName("qualityBox")
self.gridLayout_2.addWidget(self.qualityBox, 0, 2, 1, 1)
self.mozJpegBox = QtWidgets.QCheckBox(self.optionWidget)
self.mozJpegBox.setTristate(True)
self.mozJpegBox.setObjectName("mozJpegBox")
self.gridLayout_2.addWidget(self.mozJpegBox, 3, 0, 1, 1)
self.maximizeStrips = QtWidgets.QCheckBox(self.optionWidget)
self.maximizeStrips.setObjectName("maximizeStrips")
self.gridLayout_2.addWidget(self.maximizeStrips, 3, 1, 1, 1)
self.croppingBox = QtWidgets.QCheckBox(self.optionWidget)
self.croppingBox.setTristate(True)
self.croppingBox.setObjectName("croppingBox")
self.gridLayout_2.addWidget(self.croppingBox, 3, 2, 1, 1)
self.deleteBox = QtWidgets.QCheckBox(self.optionWidget)
self.deleteBox.setObjectName("deleteBox")
self.gridLayout_2.addWidget(self.deleteBox, 4, 1, 1, 1)
self.disableProcessingBox = QtWidgets.QCheckBox(self.optionWidget)
self.disableProcessingBox.setObjectName("disableProcessingBox")
self.gridLayout_2.addWidget(self.disableProcessingBox, 4, 2, 1, 1) self.gridLayout_2.addWidget(self.disableProcessingBox, 4, 2, 1, 1)
self.dedupeCoverBox = QCheckBox(self.optionWidget)
self.dedupeCoverBox.setObjectName(u"dedupeCoverBox")
self.gridLayout_2.addWidget(self.dedupeCoverBox, 4, 0, 1, 1)
self.gridLayout.addWidget(self.optionWidget, 5, 0, 1, 2) self.gridLayout.addWidget(self.optionWidget, 5, 0, 1, 2)
self.gammaWidget = QtWidgets.QWidget(self.centralWidget)
self.gammaWidget = QWidget(self.centralWidget)
self.gammaWidget.setObjectName(u"gammaWidget")
self.gammaWidget.setVisible(False) self.gammaWidget.setVisible(False)
self.horizontalLayout_2 = QHBoxLayout(self.gammaWidget) self.gammaWidget.setObjectName("gammaWidget")
self.horizontalLayout_2.setObjectName(u"horizontalLayout_2") self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.gammaWidget)
self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0) self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0)
self.gammaLabel = QLabel(self.gammaWidget) self.horizontalLayout_2.setObjectName("horizontalLayout_2")
self.gammaLabel.setObjectName(u"gammaLabel") self.gammaLabel = QtWidgets.QLabel(self.gammaWidget)
self.gammaLabel.setObjectName("gammaLabel")
self.horizontalLayout_2.addWidget(self.gammaLabel) self.horizontalLayout_2.addWidget(self.gammaLabel)
self.gammaSlider = QtWidgets.QSlider(self.gammaWidget)
self.gammaSlider = QSlider(self.gammaWidget)
self.gammaSlider.setObjectName(u"gammaSlider")
self.gammaSlider.setMaximum(250) self.gammaSlider.setMaximum(250)
self.gammaSlider.setSingleStep(5) self.gammaSlider.setSingleStep(5)
self.gammaSlider.setOrientation(Qt.Horizontal) self.gammaSlider.setOrientation(QtCore.Qt.Horizontal)
self.gammaSlider.setObjectName("gammaSlider")
self.horizontalLayout_2.addWidget(self.gammaSlider) self.horizontalLayout_2.addWidget(self.gammaSlider)
self.gridLayout.addWidget(self.gammaWidget, 6, 0, 1, 2) self.gridLayout.addWidget(self.gammaWidget, 6, 0, 1, 2)
self.croppingWidget = QtWidgets.QWidget(self.centralWidget)
self.croppingWidget = QWidget(self.centralWidget)
self.croppingWidget.setObjectName(u"croppingWidget")
self.croppingWidget.setVisible(False) self.croppingWidget.setVisible(False)
self.horizontalLayout_3 = QHBoxLayout(self.croppingWidget) self.croppingWidget.setObjectName("croppingWidget")
self.horizontalLayout_3.setObjectName(u"horizontalLayout_3") self.horizontalLayout_3 = QtWidgets.QHBoxLayout(self.croppingWidget)
self.horizontalLayout_3.setContentsMargins(0, 0, 0, 0) self.horizontalLayout_3.setContentsMargins(0, 0, 0, 0)
self.croppingPowerLabel = QLabel(self.croppingWidget) self.horizontalLayout_3.setObjectName("horizontalLayout_3")
self.croppingPowerLabel.setObjectName(u"croppingPowerLabel") self.croppingPowerLabel = QtWidgets.QLabel(self.croppingWidget)
self.croppingPowerLabel.setObjectName("croppingPowerLabel")
self.horizontalLayout_3.addWidget(self.croppingPowerLabel) self.horizontalLayout_3.addWidget(self.croppingPowerLabel)
self.croppingPowerSlider = QtWidgets.QSlider(self.croppingWidget)
self.croppingPowerSlider = QSlider(self.croppingWidget)
self.croppingPowerSlider.setObjectName(u"croppingPowerSlider")
self.croppingPowerSlider.setMaximum(200) self.croppingPowerSlider.setMaximum(200)
self.croppingPowerSlider.setSingleStep(1) self.croppingPowerSlider.setSingleStep(1)
self.croppingPowerSlider.setOrientation(Qt.Horizontal) self.croppingPowerSlider.setOrientation(QtCore.Qt.Horizontal)
self.croppingPowerSlider.setObjectName("croppingPowerSlider")
self.horizontalLayout_3.addWidget(self.croppingPowerSlider) self.horizontalLayout_3.addWidget(self.croppingPowerSlider)
self.gridLayout.addWidget(self.croppingWidget, 8, 0, 1, 2) self.gridLayout.addWidget(self.croppingWidget, 8, 0, 1, 2)
self.buttonWidget = QtWidgets.QWidget(self.centralWidget)
self.buttonWidget = QWidget(self.centralWidget) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
self.buttonWidget.setObjectName(u"buttonWidget")
sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0) sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0) sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.buttonWidget.sizePolicy().hasHeightForWidth()) sizePolicy.setHeightForWidth(self.buttonWidget.sizePolicy().hasHeightForWidth())
self.buttonWidget.setSizePolicy(sizePolicy) self.buttonWidget.setSizePolicy(sizePolicy)
self.gridLayout_4 = QGridLayout(self.buttonWidget) self.buttonWidget.setObjectName("buttonWidget")
self.gridLayout_4.setObjectName(u"gridLayout_4") self.gridLayout_4 = QtWidgets.QGridLayout(self.buttonWidget)
self.gridLayout_4.setContentsMargins(0, 0, 0, 0) self.gridLayout_4.setContentsMargins(0, 0, 0, 0)
self.directoryButton = QPushButton(self.buttonWidget) self.gridLayout_4.setObjectName("gridLayout_4")
self.directoryButton.setObjectName(u"directoryButton") self.directoryButton = QtWidgets.QPushButton(self.buttonWidget)
self.directoryButton.setMinimumSize(QSize(0, 30)) self.directoryButton.setMinimumSize(QtCore.QSize(0, 30))
icon1 = QIcon() icon1 = QtGui.QIcon()
icon1.addFile(u":/Other/icons/folder_new.png", QSize(), QIcon.Normal, QIcon.Off) icon1.addPixmap(QtGui.QPixmap(":/Other/icons/folder_new.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.directoryButton.setIcon(icon1) self.directoryButton.setIcon(icon1)
self.directoryButton.setObjectName("directoryButton")
self.gridLayout_4.addWidget(self.directoryButton, 0, 0, 1, 1) self.gridLayout_4.addWidget(self.directoryButton, 0, 0, 1, 1)
self.fileButton = QtWidgets.QPushButton(self.buttonWidget)
self.fileButton = QPushButton(self.buttonWidget) self.fileButton.setMinimumSize(QtCore.QSize(0, 30))
self.fileButton.setObjectName(u"fileButton") icon2 = QtGui.QIcon()
self.fileButton.setMinimumSize(QSize(0, 30)) icon2.addPixmap(QtGui.QPixmap(":/Other/icons/document_new.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
icon2 = QIcon()
icon2.addFile(u":/Other/icons/document_new.png", QSize(), QIcon.Normal, QIcon.Off)
self.fileButton.setIcon(icon2) self.fileButton.setIcon(icon2)
self.fileButton.setObjectName("fileButton")
self.gridLayout_4.addWidget(self.fileButton, 0, 3, 1, 1) self.gridLayout_4.addWidget(self.fileButton, 0, 3, 1, 1)
self.deviceBox = QtWidgets.QComboBox(self.buttonWidget)
self.deviceBox = QComboBox(self.buttonWidget) self.deviceBox.setMinimumSize(QtCore.QSize(0, 28))
self.deviceBox.setObjectName(u"deviceBox") self.deviceBox.setObjectName("deviceBox")
self.deviceBox.setMinimumSize(QSize(0, 28))
self.gridLayout_4.addWidget(self.deviceBox, 1, 0, 1, 1) self.gridLayout_4.addWidget(self.deviceBox, 1, 0, 1, 1)
self.formatBox = QtWidgets.QComboBox(self.buttonWidget)
self.formatBox = QComboBox(self.buttonWidget) self.formatBox.setMinimumSize(QtCore.QSize(0, 28))
self.formatBox.setObjectName(u"formatBox") self.formatBox.setObjectName("formatBox")
self.formatBox.setMinimumSize(QSize(0, 28))
self.gridLayout_4.addWidget(self.formatBox, 1, 3, 1, 1) self.gridLayout_4.addWidget(self.formatBox, 1, 3, 1, 1)
self.convertButton = QtWidgets.QPushButton(self.buttonWidget)
self.convertButton = QPushButton(self.buttonWidget) self.convertButton.setMinimumSize(QtCore.QSize(0, 30))
self.convertButton.setObjectName(u"convertButton") font = QtGui.QFont()
self.convertButton.setMinimumSize(QSize(0, 30))
font = QFont()
font.setBold(True) font.setBold(True)
self.convertButton.setFont(font) self.convertButton.setFont(font)
icon3 = QIcon() icon3 = QtGui.QIcon()
icon3.addFile(u":/Other/icons/convert.png", QSize(), QIcon.Normal, QIcon.Off) icon3.addPixmap(QtGui.QPixmap(":/Other/icons/convert.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.convertButton.setIcon(icon3) self.convertButton.setIcon(icon3)
self.convertButton.setObjectName("convertButton")
self.gridLayout_4.addWidget(self.convertButton, 1, 2, 1, 1) self.gridLayout_4.addWidget(self.convertButton, 1, 2, 1, 1)
self.clearButton = QtWidgets.QPushButton(self.buttonWidget)
self.clearButton = QPushButton(self.buttonWidget) self.clearButton.setMinimumSize(QtCore.QSize(0, 30))
self.clearButton.setObjectName(u"clearButton") icon4 = QtGui.QIcon()
self.clearButton.setMinimumSize(QSize(0, 30)) icon4.addPixmap(QtGui.QPixmap(":/Other/icons/clear.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
icon4 = QIcon()
icon4.addFile(u":/Other/icons/clear.png", QSize(), QIcon.Normal, QIcon.Off)
self.clearButton.setIcon(icon4) self.clearButton.setIcon(icon4)
self.clearButton.setObjectName("clearButton")
self.gridLayout_4.addWidget(self.clearButton, 0, 2, 1, 1) self.gridLayout_4.addWidget(self.clearButton, 0, 2, 1, 1)
self.directoryButton.raise_() self.directoryButton.raise_()
self.clearButton.raise_() self.clearButton.raise_()
self.fileButton.raise_() self.fileButton.raise_()
self.deviceBox.raise_() self.deviceBox.raise_()
self.convertButton.raise_() self.convertButton.raise_()
self.formatBox.raise_() self.formatBox.raise_()
self.gridLayout.addWidget(self.buttonWidget, 3, 0, 1, 2) self.gridLayout.addWidget(self.buttonWidget, 3, 0, 1, 2)
self.toolWidget = QtWidgets.QWidget(self.centralWidget)
self.toolWidget = QWidget(self.centralWidget) self.toolWidget.setObjectName("toolWidget")
self.toolWidget.setObjectName(u"toolWidget") self.horizontalLayout = QtWidgets.QHBoxLayout(self.toolWidget)
self.horizontalLayout = QHBoxLayout(self.toolWidget)
self.horizontalLayout.setObjectName(u"horizontalLayout")
self.horizontalLayout.setContentsMargins(0, 0, 0, 0) self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
self.editorButton = QPushButton(self.toolWidget) self.horizontalLayout.setObjectName("horizontalLayout")
self.editorButton.setObjectName(u"editorButton") self.editorButton = QtWidgets.QPushButton(self.toolWidget)
self.editorButton.setMinimumSize(QSize(0, 30)) self.editorButton.setMinimumSize(QtCore.QSize(0, 30))
icon5 = QIcon() icon5 = QtGui.QIcon()
icon5.addFile(u":/Other/icons/editor.png", QSize(), QIcon.Normal, QIcon.Off) icon5.addPixmap(QtGui.QPixmap(":/Other/icons/editor.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.editorButton.setIcon(icon5) self.editorButton.setIcon(icon5)
self.editorButton.setObjectName("editorButton")
self.horizontalLayout.addWidget(self.editorButton) self.horizontalLayout.addWidget(self.editorButton)
self.wikiButton = QtWidgets.QPushButton(self.toolWidget)
self.wikiButton = QPushButton(self.toolWidget) self.wikiButton.setMinimumSize(QtCore.QSize(0, 30))
self.wikiButton.setObjectName(u"wikiButton") icon6 = QtGui.QIcon()
self.wikiButton.setMinimumSize(QSize(0, 30)) icon6.addPixmap(QtGui.QPixmap(":/Other/icons/wiki.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
icon6 = QIcon()
icon6.addFile(u":/Other/icons/wiki.png", QSize(), QIcon.Normal, QIcon.Off)
self.wikiButton.setIcon(icon6) self.wikiButton.setIcon(icon6)
self.wikiButton.setObjectName("wikiButton")
self.horizontalLayout.addWidget(self.wikiButton) self.horizontalLayout.addWidget(self.wikiButton)
self.gridLayout.addWidget(self.toolWidget, 0, 0, 1, 2) self.gridLayout.addWidget(self.toolWidget, 0, 0, 1, 2)
self.jobList = QtWidgets.QListWidget(self.centralWidget)
self.jobList = QListWidget(self.centralWidget) self.jobList.setStyleSheet("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.setObjectName(u"jobList") self.jobList.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection)
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.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel)
self.jobList.setSelectionMode(QAbstractItemView.NoSelection) self.jobList.setHorizontalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel)
self.jobList.setVerticalScrollMode(QAbstractItemView.ScrollPerPixel) self.jobList.setObjectName("jobList")
self.jobList.setHorizontalScrollMode(QAbstractItemView.ScrollPerPixel)
self.gridLayout.addWidget(self.jobList, 2, 0, 1, 2) self.gridLayout.addWidget(self.jobList, 2, 0, 1, 2)
self.progressBar = QtWidgets.QProgressBar(self.centralWidget)
self.progressBar = QProgressBar(self.centralWidget) self.progressBar.setMinimumSize(QtCore.QSize(0, 30))
self.progressBar.setObjectName(u"progressBar") font = QtGui.QFont()
self.progressBar.setMinimumSize(QSize(0, 30)) font.setBold(True)
self.progressBar.setFont(font) self.progressBar.setFont(font)
self.progressBar.setVisible(False) self.progressBar.setVisible(False)
self.progressBar.setAlignment(Qt.AlignJustify|Qt.AlignVCenter) self.progressBar.setAlignment(QtCore.Qt.AlignJustify|QtCore.Qt.AlignVCenter)
self.progressBar.setObjectName("progressBar")
self.gridLayout.addWidget(self.progressBar, 1, 0, 1, 2) self.gridLayout.addWidget(self.progressBar, 1, 0, 1, 2)
self.customWidget = QtWidgets.QWidget(self.centralWidget)
self.customWidget = QWidget(self.centralWidget)
self.customWidget.setObjectName(u"customWidget")
self.customWidget.setVisible(False) self.customWidget.setVisible(False)
self.gridLayout_3 = QGridLayout(self.customWidget) self.customWidget.setObjectName("customWidget")
self.gridLayout_3.setObjectName(u"gridLayout_3") self.gridLayout_3 = QtWidgets.QGridLayout(self.customWidget)
self.gridLayout_3.setContentsMargins(0, 0, 0, 0) self.gridLayout_3.setContentsMargins(0, 0, 0, 0)
self.hLabel = QLabel(self.customWidget) self.gridLayout_3.setObjectName("gridLayout_3")
self.hLabel.setObjectName(u"hLabel") self.hLabel = QtWidgets.QLabel(self.customWidget)
sizePolicy1 = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Preferred)
sizePolicy1.setHorizontalStretch(0) sizePolicy.setHorizontalStretch(0)
sizePolicy1.setVerticalStretch(0) sizePolicy.setVerticalStretch(0)
sizePolicy1.setHeightForWidth(self.hLabel.sizePolicy().hasHeightForWidth()) sizePolicy.setHeightForWidth(self.hLabel.sizePolicy().hasHeightForWidth())
self.hLabel.setSizePolicy(sizePolicy1) self.hLabel.setSizePolicy(sizePolicy)
self.hLabel.setObjectName("hLabel")
self.gridLayout_3.addWidget(self.hLabel, 0, 2, 1, 1) self.gridLayout_3.addWidget(self.hLabel, 0, 2, 1, 1)
self.widthBox = QtWidgets.QSpinBox(self.customWidget)
self.widthBox = QSpinBox(self.customWidget)
self.widthBox.setObjectName(u"widthBox")
self.widthBox.setMaximum(2160) self.widthBox.setMaximum(2160)
self.widthBox.setObjectName("widthBox")
self.gridLayout_3.addWidget(self.widthBox, 0, 1, 1, 1) self.gridLayout_3.addWidget(self.widthBox, 0, 1, 1, 1)
self.wLabel = QtWidgets.QLabel(self.customWidget)
self.wLabel = QLabel(self.customWidget) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Preferred)
self.wLabel.setObjectName(u"wLabel") sizePolicy.setHorizontalStretch(0)
sizePolicy1.setHeightForWidth(self.wLabel.sizePolicy().hasHeightForWidth()) sizePolicy.setVerticalStretch(0)
self.wLabel.setSizePolicy(sizePolicy1) sizePolicy.setHeightForWidth(self.wLabel.sizePolicy().hasHeightForWidth())
self.wLabel.setSizePolicy(sizePolicy)
self.wLabel.setObjectName("wLabel")
self.gridLayout_3.addWidget(self.wLabel, 0, 0, 1, 1) self.gridLayout_3.addWidget(self.wLabel, 0, 0, 1, 1)
self.heightBox = QtWidgets.QSpinBox(self.customWidget)
self.heightBox = QSpinBox(self.customWidget)
self.heightBox.setObjectName(u"heightBox")
self.heightBox.setMaximum(3840) self.heightBox.setMaximum(3840)
self.heightBox.setObjectName("heightBox")
self.gridLayout_3.addWidget(self.heightBox, 0, 3, 1, 1) self.gridLayout_3.addWidget(self.heightBox, 0, 3, 1, 1)
self.gridLayout.addWidget(self.customWidget, 7, 0, 1, 2) self.gridLayout.addWidget(self.customWidget, 7, 0, 1, 2)
mainWindow.setCentralWidget(self.centralWidget) mainWindow.setCentralWidget(self.centralWidget)
self.statusBar = QStatusBar(mainWindow) self.statusBar = QtWidgets.QStatusBar(mainWindow)
self.statusBar.setObjectName(u"statusBar")
self.statusBar.setSizeGripEnabled(False) self.statusBar.setSizeGripEnabled(False)
self.statusBar.setObjectName("statusBar")
mainWindow.setStatusBar(self.statusBar) mainWindow.setStatusBar(self.statusBar)
QWidget.setTabOrder(self.convertButton, self.clearButton)
QWidget.setTabOrder(self.clearButton, self.directoryButton)
QWidget.setTabOrder(self.directoryButton, self.fileButton)
QWidget.setTabOrder(self.fileButton, self.deviceBox)
QWidget.setTabOrder(self.deviceBox, self.formatBox)
QWidget.setTabOrder(self.formatBox, self.mangaBox)
QWidget.setTabOrder(self.mangaBox, self.rotateBox)
QWidget.setTabOrder(self.rotateBox, self.qualityBox)
QWidget.setTabOrder(self.qualityBox, self.webtoonBox)
QWidget.setTabOrder(self.webtoonBox, self.upscaleBox)
QWidget.setTabOrder(self.upscaleBox, self.gammaBox)
QWidget.setTabOrder(self.gammaBox, self.borderBox)
QWidget.setTabOrder(self.borderBox, self.outputSplit)
QWidget.setTabOrder(self.outputSplit, self.colorBox)
QWidget.setTabOrder(self.colorBox, self.croppingBox)
QWidget.setTabOrder(self.croppingBox, self.mozJpegBox)
QWidget.setTabOrder(self.mozJpegBox, self.maximizeStrips)
QWidget.setTabOrder(self.maximizeStrips, self.deleteBox)
QWidget.setTabOrder(self.deleteBox, self.disableProcessingBox)
QWidget.setTabOrder(self.disableProcessingBox, self.editorButton)
QWidget.setTabOrder(self.editorButton, self.wikiButton)
QWidget.setTabOrder(self.wikiButton, self.jobList)
QWidget.setTabOrder(self.jobList, self.gammaSlider)
QWidget.setTabOrder(self.gammaSlider, self.widthBox)
QWidget.setTabOrder(self.widthBox, self.heightBox)
QWidget.setTabOrder(self.heightBox, self.croppingPowerSlider)
self.retranslateUi(mainWindow) self.retranslateUi(mainWindow)
QtCore.QMetaObject.connectSlotsByName(mainWindow)
QMetaObject.connectSlotsByName(mainWindow) mainWindow.setTabOrder(self.convertButton, self.clearButton)
# setupUi mainWindow.setTabOrder(self.clearButton, self.directoryButton)
mainWindow.setTabOrder(self.directoryButton, self.fileButton)
mainWindow.setTabOrder(self.fileButton, self.deviceBox)
mainWindow.setTabOrder(self.deviceBox, self.formatBox)
mainWindow.setTabOrder(self.formatBox, self.mangaBox)
mainWindow.setTabOrder(self.mangaBox, self.rotateBox)
mainWindow.setTabOrder(self.rotateBox, self.qualityBox)
mainWindow.setTabOrder(self.qualityBox, self.webtoonBox)
mainWindow.setTabOrder(self.webtoonBox, self.upscaleBox)
mainWindow.setTabOrder(self.upscaleBox, self.gammaBox)
mainWindow.setTabOrder(self.gammaBox, self.borderBox)
mainWindow.setTabOrder(self.borderBox, self.outputSplit)
mainWindow.setTabOrder(self.outputSplit, self.colorBox)
mainWindow.setTabOrder(self.colorBox, self.croppingBox)
mainWindow.setTabOrder(self.croppingBox, self.mozJpegBox)
mainWindow.setTabOrder(self.mozJpegBox, self.maximizeStrips)
mainWindow.setTabOrder(self.maximizeStrips, self.deleteBox)
mainWindow.setTabOrder(self.deleteBox, self.disableProcessingBox)
mainWindow.setTabOrder(self.disableProcessingBox, self.editorButton)
mainWindow.setTabOrder(self.editorButton, self.wikiButton)
mainWindow.setTabOrder(self.wikiButton, self.jobList)
mainWindow.setTabOrder(self.jobList, self.gammaSlider)
mainWindow.setTabOrder(self.gammaSlider, self.widthBox)
mainWindow.setTabOrder(self.widthBox, self.heightBox)
mainWindow.setTabOrder(self.heightBox, self.croppingPowerSlider)
def retranslateUi(self, mainWindow): def retranslateUi(self, mainWindow):
mainWindow.setWindowTitle(QCoreApplication.translate("mainWindow", u"Kindle Comic Converter", None)) _translate = QtCore.QCoreApplication.translate
#if QT_CONFIG(tooltip) mainWindow.setWindowTitle(_translate("mainWindow", "Kindle Comic Converter"))
self.qualityBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - 4 panels<br/></span>Zoom each corner separately.</p><p style='white-space:pre'><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - 2 panels<br/></span>Zoom only the top and bottom of the page.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - 4 high-quality panels<br/></span>Zoom each corner separately. Try to increase the quality of magnification. Check wiki for more details.</p></body></html>", None)) self.upscaleBox.setToolTip(_translate("mainWindow", "<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>"))
#endif // QT_CONFIG(tooltip) self.upscaleBox.setText(_translate("mainWindow", "Stretch/Upscale"))
self.qualityBox.setText(QCoreApplication.translate("mainWindow", u"Panel View 4/2/HQ", None)) self.rotateBox.setToolTip(_translate("mainWindow", "<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>"))
#if QT_CONFIG(tooltip) self.rotateBox.setText(_translate("mainWindow", "Spread splitter"))
self.deleteBox.setToolTip(QCoreApplication.translate("mainWindow", u"Delete input file(s) or directory. It's not recoverable!", None)) self.outputSplit.setToolTip(_translate("mainWindow", "<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>"))
#endif // QT_CONFIG(tooltip) self.outputSplit.setText(_translate("mainWindow", "Output split"))
self.deleteBox.setText(QCoreApplication.translate("mainWindow", u"Delete input", None)) self.webtoonBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Enable special parsing mode for Korean Webtoons.</p></body></html>"))
#if QT_CONFIG(tooltip) self.webtoonBox.setText(_translate("mainWindow", "Webtoon mode"))
self.maximizeStrips.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - 1x4<br/></span>Keep format 1x4 panels strips.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - 2x2<br/></span>Turn 1x4 strips to 2x2 to maximize screen usage.</p></body></html>", None)) self.colorBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Disable conversion to grayscale.</p></body></html>"))
#endif // QT_CONFIG(tooltip) self.colorBox.setText(_translate("mainWindow", "Color mode"))
self.maximizeStrips.setText(QCoreApplication.translate("mainWindow", u"1x4 to 2x2 strips", None)) self.gammaBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Disable automatic gamma correction.</p></body></html>"))
#if QT_CONFIG(tooltip) self.gammaBox.setText(_translate("mainWindow", "Custom gamma"))
self.gammaBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Disable automatic gamma correction.</p></body></html>", None)) self.borderBox.setToolTip(_translate("mainWindow", "<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>"))
#endif // QT_CONFIG(tooltip) self.borderBox.setText(_translate("mainWindow", "W/B margins"))
self.gammaBox.setText(QCoreApplication.translate("mainWindow", u"Custom gamma", None)) self.mangaBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Enable right-to-left reading.</p></body></html>"))
#if QT_CONFIG(tooltip) self.mangaBox.setText(_translate("mainWindow", "Manga mode"))
self.borderBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Autodetection<br/></span>The color of margins fill will be detected automatically.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - White<br/></span>Margins will be filled with white color.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Black<br/></span>Margins will be filled with black color.</p></body></html>", None)) self.qualityBox.setToolTip(_translate("mainWindow", "<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>"))
#endif // QT_CONFIG(tooltip) self.qualityBox.setText(_translate("mainWindow", "Panel View 4/2/HQ"))
self.borderBox.setText(QCoreApplication.translate("mainWindow", u"W/B margins", None)) self.mozJpegBox.setToolTip(_translate("mainWindow", "<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>"))
#if QT_CONFIG(tooltip) self.mozJpegBox.setText(_translate("mainWindow", "JPEG/PNG/mozJpeg"))
self.webtoonBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Enable special parsing mode for Korean Webtoons.</p></body></html>", None)) self.maximizeStrips.setToolTip(_translate("mainWindow", "<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>"))
#endif // QT_CONFIG(tooltip) self.maximizeStrips.setText(_translate("mainWindow", "1x4 to 2x2 strips"))
self.webtoonBox.setText(QCoreApplication.translate("mainWindow", u"Webtoon mode", None)) self.croppingBox.setToolTip(_translate("mainWindow", "<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>"))
#if QT_CONFIG(tooltip) self.croppingBox.setText(_translate("mainWindow", "Cropping mode"))
self.upscaleBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Nothing<br/></span>Images smaller than device resolution will not be resized.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - Stretching<br/></span>Images smaller than device resolution will be resized. Aspect ratio will be not preserved.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Upscaling<br/></span>Images smaller than device resolution will be resized. Aspect ratio will be preserved.</p></body></html>", None)) self.deleteBox.setToolTip(_translate("mainWindow", "Delete input file(s) or directory. It\'s not recoverable!"))
#endif // QT_CONFIG(tooltip) self.deleteBox.setText(_translate("mainWindow", "Delete input"))
self.upscaleBox.setText(QCoreApplication.translate("mainWindow", u"Stretch/Upscale", None)) self.disableProcessingBox.setToolTip(_translate("mainWindow", "<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>"))
#if QT_CONFIG(tooltip) self.disableProcessingBox.setText(_translate("mainWindow", "Disable processing"))
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.gammaLabel.setText(_translate("mainWindow", "Gamma: Auto"))
#endif // QT_CONFIG(tooltip) self.croppingPowerLabel.setText(_translate("mainWindow", "Cropping power:"))
self.mangaBox.setText(QCoreApplication.translate("mainWindow", u"Manga mode", None)) self.directoryButton.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Add directory containing JPG, PNG or GIF files to queue.<br/><span style=\" font-weight:600;\">CBR, CBZ and CB7 files inside will not be processed!</span></p></body></html>"))
#if QT_CONFIG(tooltip) self.directoryButton.setText(_translate("mainWindow", "Add directory"))
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.fileButton.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Add CBR, CBZ, CB7 or PDF file to queue.</p></body></html>"))
#endif // QT_CONFIG(tooltip) self.fileButton.setText(_translate("mainWindow", "Add file"))
self.rotateBox.setText(QCoreApplication.translate("mainWindow", u"Spread splitter", None)) self.deviceBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Target device.</p></body></html>"))
#if QT_CONFIG(tooltip) self.formatBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Output format.</p></body></html>"))
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.convertButton.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Shift+Click to select the output directory.</p></body></html>"))
#endif // QT_CONFIG(tooltip) self.convertButton.setText(_translate("mainWindow", "Convert"))
self.croppingBox.setText(QCoreApplication.translate("mainWindow", u"Cropping mode", None)) self.clearButton.setText(_translate("mainWindow", "Clear list"))
#if QT_CONFIG(tooltip) self.editorButton.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Shift+Click to edit directory.</p></body></html>"))
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)) self.editorButton.setText(_translate("mainWindow", "Editor"))
#endif // QT_CONFIG(tooltip) self.wikiButton.setText(_translate("mainWindow", "Wiki"))
self.outputSplit.setText(QCoreApplication.translate("mainWindow", u"Output split", None)) self.hLabel.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Resolution of the target device.</p></body></html>"))
#if QT_CONFIG(tooltip) self.hLabel.setText(_translate("mainWindow", "Custom height:"))
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.widthBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Resolution of the target device.</p></body></html>"))
#endif // QT_CONFIG(tooltip) self.wLabel.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Resolution of the target device.</p></body></html>"))
self.mozJpegBox.setText(QCoreApplication.translate("mainWindow", u"JPEG/PNG/mozJpeg", None)) self.wLabel.setText(_translate("mainWindow", "Custom width:"))
#if QT_CONFIG(tooltip) self.heightBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Resolution of the target device.</p></body></html>"))
self.colorBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Disable conversion to grayscale.</p></body></html>", None)) from . import KCC_rc
#endif // QT_CONFIG(tooltip)
self.colorBox.setText(QCoreApplication.translate("mainWindow", u"Color mode", None))
#if QT_CONFIG(tooltip)
self.disableProcessingBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><pre style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">Do not process any image, ignore profile and processing options</pre></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.disableProcessingBox.setText(QCoreApplication.translate("mainWindow", u"Disable processing", None))
#if QT_CONFIG(tooltip)
self.dedupeCoverBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p>Don't duplicate the first page as the cover. Useful for 2 page spread alignment.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.dedupeCoverBox.setText(QCoreApplication.translate("mainWindow", u"De-dupe cover", None))
self.gammaLabel.setText(QCoreApplication.translate("mainWindow", u"Gamma: Auto", None))
self.croppingPowerLabel.setText(QCoreApplication.translate("mainWindow", u"Cropping power:", None))
#if QT_CONFIG(tooltip)
self.directoryButton.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Add directory containing JPG, PNG or GIF files to queue.<br/><span style=\" font-weight:600;\">CBR, CBZ and CB7 files inside will not be processed!</span></p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.directoryButton.setText(QCoreApplication.translate("mainWindow", u"Add directory", None))
#if QT_CONFIG(tooltip)
self.fileButton.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Add CBR, CBZ, CB7 or PDF file to queue.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.fileButton.setText(QCoreApplication.translate("mainWindow", u"Add file", None))
#if QT_CONFIG(tooltip)
self.deviceBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Target device.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
#if QT_CONFIG(tooltip)
self.formatBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Output format.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
#if QT_CONFIG(tooltip)
self.convertButton.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Shift+Click to select the output directory.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.convertButton.setText(QCoreApplication.translate("mainWindow", u"Convert", None))
self.clearButton.setText(QCoreApplication.translate("mainWindow", u"Clear list", None))
#if QT_CONFIG(tooltip)
self.editorButton.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Shift+Click to edit directory.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.editorButton.setText(QCoreApplication.translate("mainWindow", u"Editor", None))
self.wikiButton.setText(QCoreApplication.translate("mainWindow", u"Wiki", None))
#if QT_CONFIG(tooltip)
self.hLabel.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Resolution of the target device.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.hLabel.setText(QCoreApplication.translate("mainWindow", u"Custom height:", None))
#if QT_CONFIG(tooltip)
self.widthBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Resolution of the target device.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
#if QT_CONFIG(tooltip)
self.wLabel.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Resolution of the target device.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.wLabel.setText(QCoreApplication.translate("mainWindow", u"Custom width:", None))
#if QT_CONFIG(tooltip)
self.heightBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Resolution of the target device.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
# retranslateUi

View File

@@ -1,168 +1,118 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
################################################################################ # Form implementation generated from reading ui file 'gui/MetaEditor.ui'
## Form generated from reading UI file 'MetaEditor.ui' #
## # Created by: PyQt5 UI code generator 5.15.2
## Created by: Qt User Interface Compiler version 6.5.2 #
## # WARNING: Any manual changes made to this file will be lost when pyuic5 is
## WARNING! All changes made in this file will be lost when recompiling UI file! # run again. Do not edit this file unless you know what you are doing.
################################################################################
from PyQt5 import QtCore, QtGui, QtWidgets
from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
QMetaObject, QObject, QPoint, QRect,
QSize, QTime, QUrl, Qt)
from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
QFont, QFontDatabase, QGradient, QIcon,
QImage, QKeySequence, QLinearGradient, QPainter,
QPalette, QPixmap, QRadialGradient, QTransform)
from PySide6.QtWidgets import (QApplication, QDialog, QGridLayout, QHBoxLayout,
QLabel, QLineEdit, QPushButton, QSizePolicy,
QVBoxLayout, QWidget)
from . import KCC_rc
class Ui_editorDialog(object): class Ui_editorDialog(object):
def setupUi(self, editorDialog): def setupUi(self, editorDialog):
if not editorDialog.objectName(): editorDialog.setObjectName("editorDialog")
editorDialog.setObjectName(u"editorDialog")
editorDialog.resize(400, 260) editorDialog.resize(400, 260)
editorDialog.setMinimumSize(QSize(400, 260)) editorDialog.setMinimumSize(QtCore.QSize(400, 260))
icon = QIcon() icon = QtGui.QIcon()
icon.addFile(u":/Icon/icons/comic2ebook.png", QSize(), QIcon.Normal, QIcon.Off) icon.addPixmap(QtGui.QPixmap(":/Icon/icons/comic2ebook.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
editorDialog.setWindowIcon(icon) editorDialog.setWindowIcon(icon)
self.verticalLayout = QVBoxLayout(editorDialog) self.verticalLayout = QtWidgets.QVBoxLayout(editorDialog)
self.verticalLayout.setObjectName(u"verticalLayout")
self.verticalLayout.setContentsMargins(-1, -1, -1, 5) self.verticalLayout.setContentsMargins(-1, -1, -1, 5)
self.editorWidget = QWidget(editorDialog) self.verticalLayout.setObjectName("verticalLayout")
self.editorWidget.setObjectName(u"editorWidget") self.editorWidget = QtWidgets.QWidget(editorDialog)
self.gridLayout = QGridLayout(self.editorWidget) self.editorWidget.setObjectName("editorWidget")
self.gridLayout.setObjectName(u"gridLayout") self.gridLayout = QtWidgets.QGridLayout(self.editorWidget)
self.gridLayout.setContentsMargins(0, 0, 0, 0) self.gridLayout.setContentsMargins(0, 0, 0, 0)
self.label_1 = QLabel(self.editorWidget) self.gridLayout.setObjectName("gridLayout")
self.label_1.setObjectName(u"label_1") self.label_1 = QtWidgets.QLabel(self.editorWidget)
self.label_1.setObjectName("label_1")
self.gridLayout.addWidget(self.label_1, 0, 0, 1, 1) self.gridLayout.addWidget(self.label_1, 0, 0, 1, 1)
self.seriesLine = QtWidgets.QLineEdit(self.editorWidget)
self.seriesLine = QLineEdit(self.editorWidget) self.seriesLine.setObjectName("seriesLine")
self.seriesLine.setObjectName(u"seriesLine")
self.gridLayout.addWidget(self.seriesLine, 0, 1, 1, 1) self.gridLayout.addWidget(self.seriesLine, 0, 1, 1, 1)
self.label_2 = QtWidgets.QLabel(self.editorWidget)
self.label_2 = QLabel(self.editorWidget) self.label_2.setObjectName("label_2")
self.label_2.setObjectName(u"label_2")
self.gridLayout.addWidget(self.label_2, 1, 0, 1, 1) self.gridLayout.addWidget(self.label_2, 1, 0, 1, 1)
self.volumeLine = QtWidgets.QLineEdit(self.editorWidget)
self.volumeLine = QLineEdit(self.editorWidget) self.volumeLine.setObjectName("volumeLine")
self.volumeLine.setObjectName(u"volumeLine")
self.gridLayout.addWidget(self.volumeLine, 1, 1, 1, 1) self.gridLayout.addWidget(self.volumeLine, 1, 1, 1, 1)
self.label_3 = QtWidgets.QLabel(self.editorWidget)
self.label_3 = QLabel(self.editorWidget) self.label_3.setObjectName("label_3")
self.label_3.setObjectName(u"label_3")
self.gridLayout.addWidget(self.label_3, 2, 0, 1, 1) self.gridLayout.addWidget(self.label_3, 2, 0, 1, 1)
self.numberLine = QtWidgets.QLineEdit(self.editorWidget)
self.numberLine = QLineEdit(self.editorWidget) self.numberLine.setObjectName("numberLine")
self.numberLine.setObjectName(u"numberLine")
self.gridLayout.addWidget(self.numberLine, 2, 1, 1, 1) self.gridLayout.addWidget(self.numberLine, 2, 1, 1, 1)
self.label_4 = QtWidgets.QLabel(self.editorWidget)
self.label_4 = QLabel(self.editorWidget) self.label_4.setObjectName("label_4")
self.label_4.setObjectName(u"label_4")
self.gridLayout.addWidget(self.label_4, 3, 0, 1, 1) self.gridLayout.addWidget(self.label_4, 3, 0, 1, 1)
self.writerLine = QtWidgets.QLineEdit(self.editorWidget)
self.writerLine = QLineEdit(self.editorWidget) self.writerLine.setObjectName("writerLine")
self.writerLine.setObjectName(u"writerLine")
self.gridLayout.addWidget(self.writerLine, 3, 1, 1, 1) self.gridLayout.addWidget(self.writerLine, 3, 1, 1, 1)
self.label_5 = QtWidgets.QLabel(self.editorWidget)
self.label_5 = QLabel(self.editorWidget) self.label_5.setObjectName("label_5")
self.label_5.setObjectName(u"label_5")
self.gridLayout.addWidget(self.label_5, 4, 0, 1, 1) self.gridLayout.addWidget(self.label_5, 4, 0, 1, 1)
self.pencillerLine = QtWidgets.QLineEdit(self.editorWidget)
self.pencillerLine = QLineEdit(self.editorWidget) self.pencillerLine.setObjectName("pencillerLine")
self.pencillerLine.setObjectName(u"pencillerLine")
self.gridLayout.addWidget(self.pencillerLine, 4, 1, 1, 1) self.gridLayout.addWidget(self.pencillerLine, 4, 1, 1, 1)
self.label_6 = QtWidgets.QLabel(self.editorWidget)
self.label_6 = QLabel(self.editorWidget) self.label_6.setObjectName("label_6")
self.label_6.setObjectName(u"label_6")
self.gridLayout.addWidget(self.label_6, 5, 0, 1, 1) self.gridLayout.addWidget(self.label_6, 5, 0, 1, 1)
self.inkerLine = QtWidgets.QLineEdit(self.editorWidget)
self.inkerLine = QLineEdit(self.editorWidget) self.inkerLine.setObjectName("inkerLine")
self.inkerLine.setObjectName(u"inkerLine")
self.gridLayout.addWidget(self.inkerLine, 5, 1, 1, 1) self.gridLayout.addWidget(self.inkerLine, 5, 1, 1, 1)
self.label_7 = QtWidgets.QLabel(self.editorWidget)
self.label_7 = QLabel(self.editorWidget) self.label_7.setObjectName("label_7")
self.label_7.setObjectName(u"label_7")
self.gridLayout.addWidget(self.label_7, 6, 0, 1, 1) self.gridLayout.addWidget(self.label_7, 6, 0, 1, 1)
self.coloristLine = QtWidgets.QLineEdit(self.editorWidget)
self.coloristLine = QLineEdit(self.editorWidget) self.coloristLine.setObjectName("coloristLine")
self.coloristLine.setObjectName(u"coloristLine")
self.gridLayout.addWidget(self.coloristLine, 6, 1, 1, 1) self.gridLayout.addWidget(self.coloristLine, 6, 1, 1, 1)
self.verticalLayout.addWidget(self.editorWidget) self.verticalLayout.addWidget(self.editorWidget)
self.optionWidget = QtWidgets.QWidget(editorDialog)
self.optionWidget = QWidget(editorDialog) self.optionWidget.setObjectName("optionWidget")
self.optionWidget.setObjectName(u"optionWidget") self.horizontalLayout = QtWidgets.QHBoxLayout(self.optionWidget)
self.horizontalLayout = QHBoxLayout(self.optionWidget)
self.horizontalLayout.setObjectName(u"horizontalLayout")
self.horizontalLayout.setContentsMargins(0, 0, 0, 0) self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
self.statusLabel = QLabel(self.optionWidget) self.horizontalLayout.setObjectName("horizontalLayout")
self.statusLabel.setObjectName(u"statusLabel") self.statusLabel = QtWidgets.QLabel(self.optionWidget)
sizePolicy = QSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding)
sizePolicy.setHorizontalStretch(0) sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0) sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.statusLabel.sizePolicy().hasHeightForWidth()) sizePolicy.setHeightForWidth(self.statusLabel.sizePolicy().hasHeightForWidth())
self.statusLabel.setSizePolicy(sizePolicy) self.statusLabel.setSizePolicy(sizePolicy)
self.statusLabel.setText("")
self.statusLabel.setObjectName("statusLabel")
self.horizontalLayout.addWidget(self.statusLabel) self.horizontalLayout.addWidget(self.statusLabel)
self.okButton = QtWidgets.QPushButton(self.optionWidget)
self.okButton = QPushButton(self.optionWidget) self.okButton.setMinimumSize(QtCore.QSize(0, 30))
self.okButton.setObjectName(u"okButton") icon1 = QtGui.QIcon()
self.okButton.setMinimumSize(QSize(0, 30)) icon1.addPixmap(QtGui.QPixmap(":/Other/icons/convert.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
icon1 = QIcon()
icon1.addFile(u":/Other/icons/convert.png", QSize(), QIcon.Normal, QIcon.Off)
self.okButton.setIcon(icon1) self.okButton.setIcon(icon1)
self.okButton.setObjectName("okButton")
self.horizontalLayout.addWidget(self.okButton) self.horizontalLayout.addWidget(self.okButton)
self.cancelButton = QtWidgets.QPushButton(self.optionWidget)
self.cancelButton = QPushButton(self.optionWidget) self.cancelButton.setMinimumSize(QtCore.QSize(0, 30))
self.cancelButton.setObjectName(u"cancelButton") icon2 = QtGui.QIcon()
self.cancelButton.setMinimumSize(QSize(0, 30)) icon2.addPixmap(QtGui.QPixmap(":/Other/icons/clear.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
icon2 = QIcon()
icon2.addFile(u":/Other/icons/clear.png", QSize(), QIcon.Normal, QIcon.Off)
self.cancelButton.setIcon(icon2) self.cancelButton.setIcon(icon2)
self.cancelButton.setObjectName("cancelButton")
self.horizontalLayout.addWidget(self.cancelButton) self.horizontalLayout.addWidget(self.cancelButton)
self.verticalLayout.addWidget(self.optionWidget) self.verticalLayout.addWidget(self.optionWidget)
self.retranslateUi(editorDialog) self.retranslateUi(editorDialog)
QtCore.QMetaObject.connectSlotsByName(editorDialog)
QMetaObject.connectSlotsByName(editorDialog)
# setupUi
def retranslateUi(self, editorDialog): def retranslateUi(self, editorDialog):
editorDialog.setWindowTitle(QCoreApplication.translate("editorDialog", u"Metadata editor", None)) _translate = QtCore.QCoreApplication.translate
self.label_1.setText(QCoreApplication.translate("editorDialog", u"Series:", None)) editorDialog.setWindowTitle(_translate("editorDialog", "Metadata editor"))
self.label_2.setText(QCoreApplication.translate("editorDialog", u"Volume:", None)) self.label_1.setText(_translate("editorDialog", "Series:"))
self.label_3.setText(QCoreApplication.translate("editorDialog", u"Number:", None)) self.label_2.setText(_translate("editorDialog", "Volume:"))
self.label_4.setText(QCoreApplication.translate("editorDialog", u"Writer:", None)) self.label_3.setText(_translate("editorDialog", "Number:"))
self.label_5.setText(QCoreApplication.translate("editorDialog", u"Penciller:", None)) self.label_4.setText(_translate("editorDialog", "Writer:"))
self.label_6.setText(QCoreApplication.translate("editorDialog", u"Inker:", None)) self.label_5.setText(_translate("editorDialog", "Penciller:"))
self.label_7.setText(QCoreApplication.translate("editorDialog", u"Colorist:", None)) self.label_6.setText(_translate("editorDialog", "Inker:"))
self.statusLabel.setText("") self.label_7.setText(_translate("editorDialog", "Colorist:"))
self.okButton.setText(QCoreApplication.translate("editorDialog", u"Save", None)) self.okButton.setText(_translate("editorDialog", "Save"))
self.cancelButton.setText(QCoreApplication.translate("editorDialog", u"Cancel", None)) self.cancelButton.setText(_translate("editorDialog", "Cancel"))
# retranslateUi from . import KCC_rc

View File

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

View File

@@ -21,6 +21,7 @@
import os import os
import pathlib import pathlib
import re import re
import subprocess
import sys import sys
from argparse import ArgumentParser from argparse import ArgumentParser
from time import strftime, gmtime from time import strftime, gmtime
@@ -35,12 +36,15 @@ from multiprocessing import Pool
from uuid import uuid4 from uuid import uuid4
from natsort import os_sorted from natsort import os_sorted
from slugify import slugify as slugify_ext from slugify import slugify as slugify_ext
from PIL import Image, ImageFile from PIL import Image
from subprocess import STDOUT, PIPE 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
try:
from .shared import md5Checksum, getImageFileName, walkSort, walkLevel, sanitizeTrace, subprocess_run from PyQt5 import QtCore
except ImportError:
QtCore = None
from .shared import md5Checksum, getImageFileName, walkSort, walkLevel, sanitizeTrace, subprocess_run_silent
from . import comic2panel from . import comic2panel
from . import image from . import image
from . import comicarchive from . import comicarchive
@@ -50,8 +54,6 @@ from . import metadata
from . import kindle from . import kindle
from . import __version__ from . import __version__
ImageFile.LOAD_TRUNCATED_IMAGES = True
def main(argv=None): def main(argv=None):
global options global options
@@ -504,18 +506,15 @@ def buildEPUB(path, chapternames, tomenumber):
chapter = False chapter = False
dirnames, filenames = walkSort(dirnames, filenames) dirnames, filenames = walkSort(dirnames, filenames)
for afile in filenames: for afile in filenames:
if cover is None:
cover = os.path.join(os.path.join(path, 'OEBPS', 'Images'),
'cover' + getImageFileName(afile)[1])
options.covers.append((image.Cover(os.path.join(dirpath, afile), cover, options,
tomenumber), options.uuid))
if options.dedupecover:
os.remove(os.path.join(dirpath, afile))
continue
filelist.append(buildHTML(dirpath, afile, os.path.join(dirpath, afile))) filelist.append(buildHTML(dirpath, afile, os.path.join(dirpath, afile)))
if not chapter: if not chapter:
chapterlist.append((dirpath.replace('Images', 'Text'), filelist[-1][1])) chapterlist.append((dirpath.replace('Images', 'Text'), filelist[-1][1]))
chapter = True chapter = True
if cover is None:
cover = os.path.join(os.path.join(path, 'OEBPS', 'Images'),
'cover' + getImageFileName(filelist[-1][1])[1])
options.covers.append((image.Cover(os.path.join(filelist[-1][0], filelist[-1][1]), cover, options,
tomenumber), options.uuid))
# Overwrite chapternames if tree is flat and ComicInfo.xml has bookmarks # Overwrite chapternames if tree is flat and ComicInfo.xml has bookmarks
if not chapternames and options.chapters: if not chapternames and options.chapters:
chapterlist = [] chapterlist = []
@@ -647,9 +646,6 @@ def getWorkFolder(afile):
try: try:
cbx = comicarchive.ComicArchive(afile) cbx = comicarchive.ComicArchive(afile)
path = cbx.extract(workdir) path = cbx.extract(workdir)
tdir = os.listdir(workdir)
if 'ComicInfo.xml' in tdir:
tdir.remove('ComicInfo.xml')
except OSError as e: except OSError as e:
rmtree(workdir, True) rmtree(workdir, True)
raise UserWarning(e) raise UserWarning(e)
@@ -695,6 +691,7 @@ def getOutputFilename(srcpath, wantedname, ext, tomenumber):
def getComicInfo(path, originalpath): def getComicInfo(path, originalpath):
xmlPath = os.path.join(path, 'ComicInfo.xml') xmlPath = os.path.join(path, 'ComicInfo.xml')
options.authors = ['KCC']
options.chapters = [] options.chapters = []
options.summary = '' options.summary = ''
titleSuffix = '' titleSuffix = ''
@@ -706,18 +703,13 @@ def getComicInfo(path, originalpath):
options.title = os.path.splitext(os.path.basename(originalpath))[0] options.title = os.path.splitext(os.path.basename(originalpath))[0]
else: else:
defaultTitle = False defaultTitle = False
if options.author == 'defaultauthor':
defaultAuthor = True
options.authors = ['KCC']
else:
defaultAuthor = False
options.authors = [options.author]
if os.path.exists(xmlPath): if os.path.exists(xmlPath):
try: try:
xml = metadata.MetadataParser(xmlPath) xml = metadata.MetadataParser(xmlPath)
except Exception: except Exception:
os.remove(xmlPath) os.remove(xmlPath)
return return
options.authors = []
if xml.data['Title']: if xml.data['Title']:
options.title = hescape(xml.data['Title']) options.title = hescape(xml.data['Title'])
elif defaultTitle: elif defaultTitle:
@@ -728,16 +720,14 @@ def getComicInfo(path, originalpath):
if xml.data['Number']: if xml.data['Number']:
titleSuffix += ' #' + xml.data['Number'].zfill(3) titleSuffix += ' #' + xml.data['Number'].zfill(3)
options.title += titleSuffix options.title += titleSuffix
if defaultAuthor: for field in ['Writers', 'Pencillers', 'Inkers', 'Colorists']:
options.authors = [] for person in xml.data[field]:
for field in ['Writers', 'Pencillers', 'Inkers', 'Colorists']: options.authors.append(hescape(person))
for person in xml.data[field]: if len(options.authors) > 0:
options.authors.append(hescape(person)) options.authors = list(set(options.authors))
if len(options.authors) > 0: options.authors.sort()
options.authors = list(set(options.authors)) else:
options.authors.sort() options.authors = ['KCC']
else:
options.authors = ['KCC']
if xml.data['Bookmarks'] and options.batchsplit == 0: if xml.data['Bookmarks'] and options.batchsplit == 0:
options.chapters = xml.data['Bookmarks'] options.chapters = xml.data['Bookmarks']
if xml.data['Summary']: if xml.data['Summary']:
@@ -906,7 +896,7 @@ def detectCorruption(tmppath, orgpath):
GUI.addMessage.emit('Source files are probably created by KCC. The second conversion will decrease quality.' GUI.addMessage.emit('Source files are probably created by KCC. The second conversion will decrease quality.'
, 'warning', False) , 'warning', False)
GUI.addMessage.emit('', '', False) GUI.addMessage.emit('', '', False)
if imageSmaller > imageNumber * 0.25 and not options.upscale and not options.stretch and options.profile != 'KS': if imageSmaller > imageNumber * 0.25 and not options.upscale and not options.stretch:
print("WARNING: More than 25% of images are smaller than target device resolution. " print("WARNING: More than 25% of images are smaller than target device resolution. "
"Consider enabling stretching or upscaling to improve readability.") "Consider enabling stretching or upscaling to improve readability.")
if GUI: if GUI:
@@ -958,7 +948,7 @@ def makeParser():
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="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)" "K11, KS, KoMT, KoG, KoGHD, KoA, KoAHD, KoAH2O, KoAO, KoN, KoC, KoL, 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)")
@@ -976,16 +966,12 @@ def makeParser():
help="Output generated file to specified directory or file") help="Output generated file to specified directory or file")
output_options.add_argument("-t", "--title", action="store", dest="title", default="defaulttitle", output_options.add_argument("-t", "--title", action="store", dest="title", default="defaulttitle",
help="Comic title [Default=filename or directory name]") help="Comic title [Default=filename or directory name]")
output_options.add_argument("-a", "--author", action="store", dest="author", default="defaultauthor",
help="Author name [Default=KCC]")
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("-b", "--batchsplit", type=int, dest="batchsplit", default="0", output_options.add_argument("-b", "--batchsplit", type=int, dest="batchsplit", default="0",
help="Split output into multiple files. 0: Don't split 1: Automatic mode " help="Split output into multiple files. 0: Don't split 1: Automatic mode "
"2: Consider every subdirectory as separate volume [Default=0]") "2: Consider every subdirectory as separate volume [Default=0]")
output_options.add_argument("--dedupecover", action="store_true", dest="dedupecover", default=False,
help="De-duplicate the cover as the first page in the book")
processing_options.add_argument("-n", "--noprocessing", action="store_true", dest="noprocessing", default=False, processing_options.add_argument("-n", "--noprocessing", action="store_true", dest="noprocessing", default=False,
help="Do not modify image and ignore any profil or processing option") help="Do not modify image and ignore any profil or processing option")
@@ -1049,13 +1035,13 @@ def checkOptions(options):
if options.profile in ['K1', 'K2', 'K34', 'K578', 'KPW', 'KPW5', 'KV', 'KO', 'K11', 'KS']: if options.profile in ['K1', 'K2', 'K34', 'K578', 'KPW', 'KPW5', 'KV', 'KO', 'K11', 'KS']:
options.format = 'MOBI' options.format = 'MOBI'
elif options.profile in ['OTHER', 'KoMT', 'KoG', 'KoGHD', 'KoA', 'KoAHD', 'KoAH2O', 'KoAO', elif options.profile in ['OTHER', 'KoMT', 'KoG', 'KoGHD', 'KoA', 'KoAHD', 'KoAH2O', 'KoAO',
'KoN', 'KoC', 'KoCC', 'KoL', 'KoLC', 'KoF', 'KoS', 'KoE']: 'KoN', 'KoC', 'KoL', 'KoF', 'KoS', 'KoE']:
options.format = 'EPUB' options.format = 'EPUB'
elif options.profile in ['KDX']: 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']: if options.profile in ['K1', 'K2', 'K34', 'K578', 'KPW', 'KPW5', 'KV', 'KO', 'K11', 'KS']:
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']: elif options.profile in ['OTHER', 'KoMT', 'KoG', 'KoGHD', 'KoA', 'KoAHD', 'KoAH2O', 'KoAO', 'KoN', 'KoC', 'KoL', 'KoF', 'KoS', 'KoE']:
options.isKobo = True options.isKobo = True
# Other Kobo devices probably support synthetic spreads as well, but # Other Kobo devices probably support synthetic spreads as well, but
# they haven't been tested. # they haven't been tested.
@@ -1106,6 +1092,10 @@ def checkOptions(options):
image.ProfileData.Profiles["Custom"] = newProfile image.ProfileData.Profiles["Custom"] = newProfile
options.profile = "Custom" options.profile = "Custom"
options.profileData = image.ProfileData.Profiles[options.profile] options.profileData = image.ProfileData.Profiles[options.profile]
# kindle scribe conversion to mobi is limited in resolution by kindlegen, same with send to kindle and epub
if options.profile == 'KS' and (options.format == 'MOBI' or options.format == 'EPUB'):
options.profileData = list(options.profileData)
options.profileData[1] = (1440, 1920)
return options return options
@@ -1114,13 +1104,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(['7z'], stdout=PIPE, stderr=STDOUT) subprocess_run_silent(['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(['kindlegen', '-locale', 'en'], stdout=PIPE, stderr=STDOUT) subprocess_run_silent(['kindlegen', '-locale', 'en'], stdout=PIPE, stderr=STDOUT)
except FileNotFoundError: except FileNotFoundError:
print('ERROR: KindleGen is missing!') print('ERROR: KindleGen is missing!')
sys.exit(1) sys.exit(1)
@@ -1277,7 +1267,7 @@ def makeMOBIWorker(item):
kindlegenError = '' kindlegenError = ''
try: try:
if os.path.getsize(item) < 629145600: if os.path.getsize(item) < 629145600:
output = subprocess_run(['kindlegen', '-dont_append_source', '-locale', 'en', item], output = subprocess_run_silent(['kindlegen', '-dont_append_source', '-locale', 'en', item],
stdout=PIPE, stderr=STDOUT, encoding='UTF-8') stdout=PIPE, stderr=STDOUT, encoding='UTF-8')
for line in output.stdout.splitlines(): for line in output.stdout.splitlines():
# ERROR: Generic error # ERROR: Generic error

View File

@@ -25,6 +25,10 @@ from shutil import rmtree, copytree, move
from multiprocessing import Pool from multiprocessing import Pool
from PIL import Image, ImageChops, ImageOps, ImageDraw from PIL import Image, ImageChops, ImageOps, ImageDraw
from .shared import getImageFileName, walkLevel, walkSort, sanitizeTrace from .shared import getImageFileName, walkLevel, walkSort, sanitizeTrace
try:
from PyQt5 import QtCore
except ImportError:
QtCore = None
def mergeDirectoryTick(output): def mergeDirectoryTick(output):

View File

@@ -18,14 +18,15 @@
# 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 subprocess import STDOUT, PIPE, CalledProcessError from shutil import move
from subprocess import STDOUT, PIPE
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 from .shared import subprocess_run_silent
EXTRACTION_ERROR = 'Failed to extract archive. Try extracting file outside of KCC.' EXTRACTION_ERROR = 'Failed to extract archive. Try extracting file outside of KCC.'
@@ -33,78 +34,53 @@ 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.')
process = subprocess_run_silent(['7z', 'l', '-y', '-p1', self.filepath], stderr=STDOUT, stdout=PIPE)
@cached_property for line in process.stdout.splitlines():
def type(self): if b'Type =' in line:
extraction_commands = [ self.type = line.rstrip().decode().split(' = ')[1].upper()
['7z', 'l', '-y', '-p1', self.filepath], break
] if process.returncode != 0 and distro.id() == 'fedora':
process = subprocess_run_silent(['unrar', 'l', '-y', '-p1', self.filepath], stderr=STDOUT, stdout=PIPE)
if distro.id() == 'fedora': for line in process.stdout.splitlines():
extraction_commands.append( if b'Details: ' in line:
['unrar', 'l', '-y', '-p1', self.filepath], self.type = line.rstrip().decode().split(' ')[1].upper()
) break
if process.returncode != 0:
for cmd in extraction_commands: raise OSError(EXTRACTION_ERROR)
try:
process = subprocess_run(cmd, capture_output=True, check=True)
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.')
process = subprocess_run_silent(['7z', 'x', '-y', '-xr!__MACOSX', '-xr!.DS_Store', '-xr!thumbs.db', '-xr!Thumbs.db', '-o' + targetdir, self.filepath],
missing = [] stdout=PIPE, stderr=STDOUT)
if process.returncode != 0 and distro.id() == 'fedora':
extraction_commands = [ process = subprocess_run_silent(['unrar', 'x', '-y', '-x__MACOSX', '-x.DS_Store', '-xthumbs.db', '-xThumbs.db', self.filepath, targetdir]
['tar', '--exclude', '__MACOSX', '--exclude', '.DS_Store', '--exclude', 'thumbs.db', '--exclude', 'Thumbs.db', '-xf', self.filepath, '-C', targetdir], , stdout=PIPE, stderr=STDOUT)
['7z', 'x', '-y', '-xr!__MACOSX', '-xr!.DS_Store', '-xr!thumbs.db', '-xr!Thumbs.db', '-o' + targetdir, self.filepath], if process.returncode != 0:
] raise OSError(EXTRACTION_ERROR)
elif process.returncode != 0 and platform.system() == 'Darwin':
if platform.system() == 'Darwin': process = subprocess_run_silent(['unar', self.filepath, '-f', '-o', targetdir],
extraction_commands.append( stdout=PIPE, stderr=STDOUT)
['unar', self.filepath, '-f', '-o', targetdir] elif process.returncode != 0:
)
if distro.id() == 'fedora':
extraction_commands.append(
['unrar', 'x', '-y', '-x__MACOSX', '-x.DS_Store', '-xthumbs.db', '-xThumbs.db', self.filepath, targetdir]
)
for cmd in extraction_commands:
try:
subprocess_run(cmd, capture_output=True, check=True)
return targetdir
except FileNotFoundError:
missing.append(cmd[0])
except CalledProcessError:
pass
if missing:
raise OSError(f'Extraction failed, install <a href="https://github.com/ciromattia/kcc#7-zip">specialized extraction software.</a> ')
else:
raise OSError(EXTRACTION_ERROR) raise OSError(EXTRACTION_ERROR)
tdir = os.listdir(targetdir)
if 'ComicInfo.xml' in tdir:
tdir.remove('ComicInfo.xml')
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(['7z', 'a', '-y', self.filepath, sourcefile], process = subprocess_run_silent(['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(['7z', 'x', '-y', '-so', self.filepath, 'ComicInfo.xml'], process = subprocess_run_silent(['7z', 'x', '-y', '-so', self.filepath, 'ComicInfo.xml'],
stdout=PIPE, stderr=STDOUT) stdout=PIPE, stderr=STDOUT)
if process.returncode != 0: if process.returncode != 0:
raise OSError(EXTRACTION_ERROR) raise OSError(EXTRACTION_ERROR)

View File

@@ -99,9 +99,7 @@ class ProfileData:
'KoAO': ("Kobo Aura ONE", (1404, 1872), Palette16, 1.8), 'KoAO': ("Kobo Aura ONE", (1404, 1872), Palette16, 1.8),
'KoN': ("Kobo Nia", (758, 1024), Palette16, 1.8), 'KoN': ("Kobo Nia", (758, 1024), Palette16, 1.8),
'KoC': ("Kobo Clara HD/Kobo Clara 2E", (1072, 1448), Palette16, 1.8), 'KoC': ("Kobo Clara HD/Kobo Clara 2E", (1072, 1448), Palette16, 1.8),
'KoCC': ("Kobo Clara Colour", (1072, 1448), Palette16, 1.8),
'KoL': ("Kobo Libra H2O/Kobo Libra 2", (1264, 1680), Palette16, 1.8), 'KoL': ("Kobo Libra H2O/Kobo Libra 2", (1264, 1680), Palette16, 1.8),
'KoLC': ("Kobo Libra Colour", (1264, 1680), Palette16, 1.8),
'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),
@@ -241,7 +239,6 @@ class ComicPage:
_, self.size, self.palette, self.gamma = self.opt.profileData _, self.size, self.palette, self.gamma = self.opt.profileData
if self.opt.hq: if self.opt.hq:
self.size = (int(self.size[0] * 1.5), int(self.size[1] * 1.5)) self.size = (int(self.size[0] * 1.5), int(self.size[1] * 1.5))
self.kindle_scribe_azw3 = (options.profile == 'KS') and (options.format in ('MOBI', 'EPUB'))
self.image = image self.image = image
self.color = color self.color = color
self.fill = fill self.fill = fill
@@ -311,9 +308,6 @@ class ComicPage:
self.image = self.image.quantize(palette=palImg) self.image = self.image.quantize(palette=palImg)
def resizeImage(self): def resizeImage(self):
# kindle scribe conversion to mobi is limited in resolution by kindlegen, same with send to kindle and epub
if self.kindle_scribe_azw3:
self.size = (1440, 1920)
ratio_device = float(self.size[1]) / float(self.size[0]) ratio_device = float(self.size[1]) / float(self.size[0])
ratio_image = float(self.image.size[1]) / float(self.image.size[0]) ratio_image = float(self.image.size[1]) / float(self.image.size[0])
method = self.resize_method() method = self.resize_method()
@@ -332,15 +326,14 @@ class ComicPage:
elif self.opt.format == 'CBZ' or self.opt.kfx: elif self.opt.format == 'CBZ' or self.opt.kfx:
self.image = ImageOps.pad(self.image, self.size, method=method, color=self.fill) self.image = ImageOps.pad(self.image, self.size, method=method, color=self.fill)
else: else:
if self.kindle_scribe_azw3:
self.size = (1860, 1920)
self.image = ImageOps.contain(self.image, self.size, method=method) self.image = ImageOps.contain(self.image, self.size, method=method)
def resize_method(self): def resize_method(self):
if self.image.size[0] <= self.size[0] and self.image.size[1] <= self.size[1]: if self.image.size[0] <= self.size[0] and self.image.size[1] <= self.size[1]:
return Image.Resampling.BICUBIC method = Image.Resampling.BICUBIC
else: else:
return Image.Resampling.LANCZOS method = Image.Resampling.LANCZOS
return method
def getBoundingBox(self, tmptmg): def getBoundingBox(self, tmptmg):
min_margin = [int(0.005 * i + 0.5) for i in tmptmg.size] min_margin = [int(0.005 * i + 0.5) for i in tmptmg.size]

View File

@@ -31,7 +31,7 @@ class Kindle:
def findDevice(self): def findDevice(self):
for drive in reversed(psutil.disk_partitions(False)): for drive in reversed(psutil.disk_partitions(False)):
if (drive[2] == 'FAT32' and drive[3] == 'rw,removable') or \ if (drive[2] == 'FAT32' and drive[3] == 'rw,removable') or \
(drive[2] in ('vfat', 'msdos', 'FAT', 'apfs') and 'rw' in drive[3]): (drive[2] in ('vfat', 'msdos', 'FAT') and 'rw' in drive[3]):
if os.path.isdir(os.path.join(drive[1], 'system')) and \ if os.path.isdir(os.path.join(drive[1], 'system')) and \
os.path.isdir(os.path.join(drive[1], 'documents')): os.path.isdir(os.path.join(drive[1], 'documents')):
return drive[1] return drive[1]

View File

@@ -22,7 +22,7 @@ import os
from hashlib import md5 from hashlib import md5
from html.parser import HTMLParser from html.parser import HTMLParser
import subprocess import subprocess
from packaging.version import Version from distutils.version import StrictVersion
from re import split from re import split
import sys import sys
from traceback import format_tb from traceback import format_tb
@@ -102,11 +102,11 @@ def dependencyCheck(level):
missing = [] missing = []
if level > 2: if level > 2:
try: try:
from PySide6.QtCore import qVersion as qtVersion from PyQt5.QtCore import qVersion as qtVersion
if Version('6.5.1') > Version(qtVersion()): if StrictVersion('5.6.0') > StrictVersion(qtVersion()):
missing.append('PySide 6.5.1+') missing.append('PyQt 5.6.0+')
except ImportError: except ImportError:
missing.append('PySide 6.5.1+') missing.append('PyQt 5.6.0+')
try: try:
import raven import raven
except ImportError: except ImportError:
@@ -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 Version('5.0.0') > Version(psutilVersion): if StrictVersion('5.0.0') > StrictVersion(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 Version('1.2.1') > Version(slugifyVersion): if StrictVersion('1.2.1') > StrictVersion(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 Version('5.2.0') > Version(pillowVersion): if StrictVersion('5.2.0') > StrictVersion(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(command, **kwargs): def subprocess_run_silent(command, **kwargs):
if (os.name == 'nt'): if (os.name == 'nt'):
kwargs.setdefault('creationflags', subprocess.CREATE_NO_WINDOW) kwargs.setdefault('creationflags', subprocess.CREATE_NO_WINDOW)
return subprocess.run(command, **kwargs) return subprocess.run(command, **kwargs)

View File

@@ -1,10 +1,10 @@
PySide6>=6.5.1 PyQt5>=5.6.0
Pillow>=5.2.0 Pillow>=5.2.0
psutil>=5.9.5 psutil>=5.0.0
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 # PyQt5-tools
mozjpeg-lossless-optimization>=1.1.2 mozjpeg-lossless-optimization>=1.1.2
natsort[fast]>=8.4.0 natsort[fast]>=8.4.0
distro>=1.8.0 distro

View File

@@ -11,9 +11,10 @@ Create EXE/APP:
""" """
import os import os
import platform
import sys import sys
import shutil
import setuptools import setuptools
import distutils.cmd
from kindlecomicconverter import __version__ from kindlecomicconverter import __version__
NAME = 'KindleComicConverter' NAME = 'KindleComicConverter'
@@ -22,7 +23,7 @@ VERSION = __version__
# noinspection PyUnresolvedReferences # noinspection PyUnresolvedReferences
class BuildBinaryCommand(setuptools.Command): class BuildBinaryCommand(distutils.cmd.Command):
description = 'build binary release' description = 'build binary release'
user_options = [] user_options = []
@@ -36,16 +37,16 @@ class BuildBinaryCommand(setuptools.Command):
def run(self): def run(self):
VERSION = __version__ VERSION = __version__
if sys.platform == 'darwin': if sys.platform == 'darwin':
os.system('pyinstaller --hidden-import=_cffi_backend -y -D -i icons/comic2ebook.icns -n "Kindle Comic Converter" -w -s kcc.py') os.system('pyinstaller -y -D -i icons/comic2ebook.icns -n "Kindle Comic Converter" -w -s kcc.py')
# TODO /usr/bin/codesign --force -s "$MACOS_CERTIFICATE_NAME" --options runtime dist/Applications/Kindle\ Comic\ Converter.app -v # TODO /usr/bin/codesign --force -s "$MACOS_CERTIFICATE_NAME" --options runtime dist/Applications/Kindle\ Comic\ Converter.app -v
os.system(f'appdmg kcc.json dist/kcc_macos_{platform.processor()}_{VERSION}.dmg') os.system('appdmg kcc.json dist/KindleComicConverter_osx_' + VERSION + '.dmg')
sys.exit(0) sys.exit(0)
elif sys.platform == 'win32': elif sys.platform == 'win32':
os.system('pyinstaller --hidden-import=_cffi_backend -y -F -i icons\\comic2ebook.ico -n KCC_' + VERSION + ' -w --noupx kcc.py') os.system('pyinstaller -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=_cffi_backend --hidden-import=queue -y -F -i icons/comic2ebook.ico -n kcc_linux_' + VERSION + ' kcc.py') 'pyinstaller --hidden-import=queue -y -F -i icons/comic2ebook.ico -n kcc_linux_' + VERSION + ' kcc.py')
sys.exit(0) sys.exit(0)
else: else:
sys.exit(0) sys.exit(0)
@@ -74,9 +75,9 @@ setuptools.setup(
}, },
packages=['kindlecomicconverter'], packages=['kindlecomicconverter'],
install_requires=[ install_requires=[
'pyside6>=6.5.1', 'PyQt5>=5.6.0',
'Pillow>=5.2.0', 'Pillow>=5.2.0',
'psutil>=5.9.5', 'psutil>=5.0.0',
'python-slugify>=1.2.1,<9.0.0', 'python-slugify>=1.2.1,<9.0.0',
'raven>=6.0.0', 'raven>=6.0.0',
'requests>=2.31.0', 'requests>=2.31.0',