mirror of
https://github.com/ciromattia/kcc
synced 2026-04-18 06:58:58 +00:00
Compare commits
59 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b8a5582431 | ||
|
|
9e73f065ae | ||
|
|
0d417b8e11 | ||
|
|
b40cfcd9a6 | ||
|
|
6836c20377 | ||
|
|
f75ea6dfe8 | ||
|
|
77afa77d32 | ||
|
|
f73d889b6e | ||
|
|
8e04ccde18 | ||
|
|
cc1e5db0aa | ||
|
|
c5c88095ee | ||
|
|
1318b9c0f2 | ||
|
|
9339abb267 | ||
|
|
154707a412 | ||
|
|
42c79b326f | ||
|
|
4cb986c302 | ||
|
|
69856c7f48 | ||
|
|
a3dd23ed40 | ||
|
|
217f571f3d | ||
|
|
531cea88e6 | ||
|
|
5a21ff0bcf | ||
|
|
17e8ffb7c0 | ||
|
|
a5202458dc | ||
|
|
4499e8faa0 | ||
|
|
6ee685e64c | ||
|
|
9f34df1414 | ||
|
|
5902d88d98 | ||
|
|
6a6a363c47 | ||
|
|
6ba2ef66ca | ||
|
|
264184c7e0 | ||
|
|
7e62329a51 | ||
|
|
7e82f492c6 | ||
|
|
829334556f | ||
|
|
5c1408e7b7 | ||
|
|
b51c87e3bc | ||
|
|
ada3232a0a | ||
|
|
44682156c9 | ||
|
|
242fd70b54 | ||
|
|
3f2365c677 | ||
|
|
f4e45e6052 | ||
|
|
fe81af831e | ||
|
|
eeec0501f5 | ||
|
|
3d0d615879 | ||
|
|
82bc405f6f | ||
|
|
b2a079c958 | ||
|
|
b37ea52c7d | ||
|
|
e7e41715d0 | ||
|
|
62d1c7c488 | ||
|
|
95678adfd6 | ||
|
|
4923dac8f0 | ||
|
|
935727c1db | ||
|
|
b0e38a700a | ||
|
|
d668883b6f | ||
|
|
23961243b6 | ||
|
|
c98d6179c3 | ||
|
|
37200bdca0 | ||
|
|
0193bcd00a | ||
|
|
0bbe9348a2 | ||
|
|
d16628dc59 |
34
.github/workflows/docker-base-publish.yml
vendored
Normal file
34
.github/workflows/docker-base-publish.yml
vendored
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
name: Docker base
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
push:
|
||||||
|
tags: [ 'docker-base-*' ]
|
||||||
|
|
||||||
|
# Don't trigger if it's just a documentation update
|
||||||
|
paths-ignore:
|
||||||
|
- '**.md'
|
||||||
|
- '**.MD'
|
||||||
|
- '**.yml'
|
||||||
|
- 'docs/**'
|
||||||
|
- 'LICENSE'
|
||||||
|
- '.gitattributes'
|
||||||
|
- '.gitignore'
|
||||||
|
- '.dockerignore'
|
||||||
|
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build_and_push:
|
||||||
|
uses: sdr-enthusiasts/common-github-workflows/.github/workflows/build_and_push_image.yml@main
|
||||||
|
with:
|
||||||
|
docker_build_file: ./Dockerfile-base
|
||||||
|
platform_linux_arm32v7_enabled: true
|
||||||
|
platform_linux_arm64v8_enabled: true
|
||||||
|
platform_linux_amd64_enabled: true
|
||||||
|
push_enabled: true
|
||||||
|
build_nohealthcheck: false
|
||||||
|
ghcr_repo_owner: ${{ github.repository_owner }}
|
||||||
|
ghcr_repo: ${{ github.repository }}
|
||||||
|
build_latest: false
|
||||||
|
secrets:
|
||||||
|
ghcr_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
3
.github/workflows/docker-publish.yml
vendored
3
.github/workflows/docker-publish.yml
vendored
@@ -2,10 +2,7 @@ name: Docker
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
#schedule:
|
|
||||||
# - cron: '39 5 * * *'
|
|
||||||
push:
|
push:
|
||||||
# branches: [ master, pipeline_test, docker_test ]
|
|
||||||
# Publish semver tags as releases.
|
# Publish semver tags as releases.
|
||||||
tags: [ 'v*.*.*' ]
|
tags: [ 'v*.*.*' ]
|
||||||
|
|
||||||
|
|||||||
4
.github/workflows/package-linux.yml
vendored
4
.github/workflows/package-linux.yml
vendored
@@ -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 python3-pyqt5 python3-pip squashfs-tools libfuse2
|
sudo apt-get install -y libpng-dev libjpeg-dev p7zip-full p7zip-rar python3-pip squashfs-tools libfuse2
|
||||||
python -m pip install --upgrade pip setuptools wheel certifi pyinstaller PyQt6 --no-binary pyinstaller
|
python -m pip install --upgrade pip setuptools wheel certifi pyinstaller --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: |
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -11,3 +11,4 @@ KindleComicConverter*.egg-info/
|
|||||||
/venv/
|
/venv/
|
||||||
/kindlegen*
|
/kindlegen*
|
||||||
/kcc.bat
|
/kcc.bat
|
||||||
|
.DS_Store
|
||||||
|
|||||||
20
CHANGELOG.md
20
CHANGELOG.md
@@ -1,5 +1,25 @@
|
|||||||
# CHANGELOG
|
# CHANGELOG
|
||||||
|
|
||||||
|
|
||||||
|
#### 5.6.2:
|
||||||
|
* build pipeline : drop pypi by @darodi in [#465](https://github.com/ciromattia/kcc/pull/465)
|
||||||
|
* supporting Kindle Previewer by @darodi in [#466](https://github.com/ciromattia/kcc/pull/466)
|
||||||
|
* Bump actions/upload-artifact from 2 to 3 by @dependabot in [#468](https://github.com/ciromattia/kcc/pull/468)
|
||||||
|
* new appImage by @darodi in [#483](https://github.com/ciromattia/kcc/pull/483)
|
||||||
|
* Bump actions/checkout from 2 to 3 by @dependabot in [#484](https://github.com/ciromattia/kcc/pull/484)
|
||||||
|
* supporting Kindle Previewer by @darodi in [#486](https://github.com/ciromattia/kcc/pull/486)
|
||||||
|
* comic2ebook/func: Add a delete option (closes #458) by @Constantin1489 in [#485](https://github.com/ciromattia/kcc/pull/485)
|
||||||
|
* Add command line executables to CI/pipelines by @darodi in [#487](https://github.com/ciromattia/kcc/pull/487)
|
||||||
|
* gui/func: Add a 'delete after conversion' button (closes #458) by @Constantin1489 in [#488](https://github.com/ciromattia/kcc/pull/488)
|
||||||
|
* fix crashes on png transparency by @axu2 in [#494](https://github.com/ciromattia/kcc/pull/494)
|
||||||
|
* Update python-slugify requirement from <8.0.0,>=1.2.1 to >=1.2.1,<9.0.0 by @dependabot in [#473](https://github.com/ciromattia/kcc/pull/473)
|
||||||
|
* Updates GUI text for new Homebrew version by @thatrobotdev in [#491](https://github.com/ciromattia/kcc/pull/491)
|
||||||
|
* Even with EPUB-200MB option selected, created file is above 200MB by @darodi in [#503](https://github.com/ciromattia/kcc/pull/503)
|
||||||
|
* limit kindle scribe image size to (1440, 1920) when using kindlegen by @darodi in [#514](https://github.com/ciromattia/kcc/pull/514)
|
||||||
|
* add 7z to PATH by @axu2 in [#513](https://github.com/ciromattia/kcc/pull/513)
|
||||||
|
* use unrar for fedora only by @darodi in [#515](https://github.com/ciromattia/kcc/pull/515)
|
||||||
|
|
||||||
|
|
||||||
#### 5.6.1:
|
#### 5.6.1:
|
||||||
* Fix pillow backwards compatibility, add mozjpeg-lossless-optimization to setup.py by @corylk in #461
|
* Fix pillow backwards compatibility, add mozjpeg-lossless-optimization to setup.py by @corylk in #461
|
||||||
* fix in fedora: 7z doesn't support rar archives, use unrar by @AlicesReflexion in #370
|
* fix in fedora: 7z doesn't support rar archives, use unrar by @AlicesReflexion in #370
|
||||||
|
|||||||
152
Dockerfile
152
Dockerfile
@@ -1,147 +1,5 @@
|
|||||||
FROM --platform=linux/amd64 python:3.11-slim-buster as compile-amd64
|
|
||||||
ARG TARGETOS
|
|
||||||
ARG TARGETARCH
|
|
||||||
ARG TARGETVARIANT
|
|
||||||
RUN echo "I'm building for $TARGETOS/$TARGETARCH/$TARGETVARIANT"
|
|
||||||
|
|
||||||
|
|
||||||
COPY requirements.txt /opt/kcc/
|
|
||||||
ENV PATH="/opt/venv/bin:$PATH"
|
|
||||||
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 python3-pyqt5 && \
|
|
||||||
python -m pip install --upgrade pip && \
|
|
||||||
python -m venv /opt/venv && \
|
|
||||||
python -m pip install -r /opt/kcc/requirements.txt
|
|
||||||
|
|
||||||
|
|
||||||
######################################################################################
|
|
||||||
|
|
||||||
FROM --platform=linux/arm64 python:3.11-slim-buster as compile-arm64
|
|
||||||
ARG TARGETOS
|
|
||||||
ARG TARGETARCH
|
|
||||||
ARG TARGETVARIANT
|
|
||||||
RUN echo "I'm building for $TARGETOS/$TARGETARCH/$TARGETVARIANT"
|
|
||||||
|
|
||||||
ENV LC_ALL=C.UTF-8 \
|
|
||||||
LANG=C.UTF-8 \
|
|
||||||
LANGUAGE=en_US:en
|
|
||||||
|
|
||||||
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
|
|
||||||
|
|
||||||
RUN set -x && \
|
|
||||||
TEMP_PACKAGES=() && \
|
|
||||||
KEPT_PACKAGES=() && \
|
|
||||||
# Packages only required during build
|
|
||||||
TEMP_PACKAGES+=(build-essential) && \
|
|
||||||
TEMP_PACKAGES+=(cmake) && \
|
|
||||||
TEMP_PACKAGES+=(libfreetype6-dev) && \
|
|
||||||
TEMP_PACKAGES+=(libfontconfig1-dev) && \
|
|
||||||
TEMP_PACKAGES+=(libpng-dev) && \
|
|
||||||
TEMP_PACKAGES+=(libjpeg-dev) && \
|
|
||||||
TEMP_PACKAGES+=(libssl-dev) && \
|
|
||||||
TEMP_PACKAGES+=(libxft-dev) && \
|
|
||||||
TEMP_PACKAGES+=(make) && \
|
|
||||||
TEMP_PACKAGES+=(python3-dev) && \
|
|
||||||
TEMP_PACKAGES+=(python3-setuptools) && \
|
|
||||||
TEMP_PACKAGES+=(python3-wheel) && \
|
|
||||||
# Packages kept in the image
|
|
||||||
KEPT_PACKAGES+=(bash) && \
|
|
||||||
KEPT_PACKAGES+=(ca-certificates) && \
|
|
||||||
KEPT_PACKAGES+=(chrpath) && \
|
|
||||||
KEPT_PACKAGES+=(locales) && \
|
|
||||||
KEPT_PACKAGES+=(locales-all) && \
|
|
||||||
KEPT_PACKAGES+=(libfreetype6) && \
|
|
||||||
KEPT_PACKAGES+=(libfontconfig1) && \
|
|
||||||
KEPT_PACKAGES+=(p7zip-full) && \
|
|
||||||
KEPT_PACKAGES+=(python3) && \
|
|
||||||
KEPT_PACKAGES+=(python3-pip) && \
|
|
||||||
KEPT_PACKAGES+=(python-pyqt5) && \
|
|
||||||
KEPT_PACKAGES+=(qt5-default) && \
|
|
||||||
KEPT_PACKAGES+=(unrar-free) && \
|
|
||||||
# Install packages
|
|
||||||
DEBIAN_FRONTEND=noninteractive apt-get update -y && apt-get -yq upgrade && \
|
|
||||||
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
|
|
||||||
${KEPT_PACKAGES[@]} \
|
|
||||||
${TEMP_PACKAGES[@]} \
|
|
||||||
&& \
|
|
||||||
# Install required python modules
|
|
||||||
python -m pip install --upgrade pip && \
|
|
||||||
# python -m pip install -r /opt/kcc/requirements.txt && \
|
|
||||||
python -m venv /opt/venv && \
|
|
||||||
python -m pip install --upgrade pillow python-slugify psutil raven mozjpeg-lossless-optimization
|
|
||||||
|
|
||||||
|
|
||||||
######################################################################################
|
|
||||||
|
|
||||||
FROM --platform=linux/arm/v7 python:3.11-slim-buster as compile-armv7
|
|
||||||
ARG TARGETOS
|
|
||||||
ARG TARGETARCH
|
|
||||||
ARG TARGETVARIANT
|
|
||||||
RUN echo "I'm building for $TARGETOS/$TARGETARCH/$TARGETVARIANT"
|
|
||||||
|
|
||||||
ENV LC_ALL=C.UTF-8 \
|
|
||||||
LANG=C.UTF-8 \
|
|
||||||
LANGUAGE=en_US:en
|
|
||||||
|
|
||||||
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
|
|
||||||
|
|
||||||
RUN set -x && \
|
|
||||||
TEMP_PACKAGES=() && \
|
|
||||||
KEPT_PACKAGES=() && \
|
|
||||||
# Packages only required during build
|
|
||||||
TEMP_PACKAGES+=(build-essential) && \
|
|
||||||
TEMP_PACKAGES+=(cmake) && \
|
|
||||||
TEMP_PACKAGES+=(libffi-dev) && \
|
|
||||||
TEMP_PACKAGES+=(libfreetype6-dev) && \
|
|
||||||
TEMP_PACKAGES+=(libfontconfig1-dev) && \
|
|
||||||
TEMP_PACKAGES+=(libpng-dev) && \
|
|
||||||
TEMP_PACKAGES+=(libjpeg-dev) && \
|
|
||||||
TEMP_PACKAGES+=(libssl-dev) && \
|
|
||||||
TEMP_PACKAGES+=(libxft-dev) && \
|
|
||||||
TEMP_PACKAGES+=(make) && \
|
|
||||||
TEMP_PACKAGES+=(python3-dev) && \
|
|
||||||
TEMP_PACKAGES+=(python3-setuptools) && \
|
|
||||||
TEMP_PACKAGES+=(python3-wheel) && \
|
|
||||||
# Packages kept in the image
|
|
||||||
KEPT_PACKAGES+=(bash) && \
|
|
||||||
KEPT_PACKAGES+=(ca-certificates) && \
|
|
||||||
KEPT_PACKAGES+=(chrpath) && \
|
|
||||||
KEPT_PACKAGES+=(locales) && \
|
|
||||||
KEPT_PACKAGES+=(locales-all) && \
|
|
||||||
KEPT_PACKAGES+=(libfreetype6) && \
|
|
||||||
KEPT_PACKAGES+=(libfontconfig1) && \
|
|
||||||
KEPT_PACKAGES+=(p7zip-full) && \
|
|
||||||
KEPT_PACKAGES+=(python3) && \
|
|
||||||
KEPT_PACKAGES+=(python3-pip) && \
|
|
||||||
KEPT_PACKAGES+=(python-pyqt5) && \
|
|
||||||
KEPT_PACKAGES+=(qt5-default) && \
|
|
||||||
KEPT_PACKAGES+=(unrar-free) && \
|
|
||||||
# Install packages
|
|
||||||
DEBIAN_FRONTEND=noninteractive apt-get update -y && apt-get -yq upgrade && \
|
|
||||||
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
|
|
||||||
${KEPT_PACKAGES[@]} \
|
|
||||||
${TEMP_PACKAGES[@]} \
|
|
||||||
&& \
|
|
||||||
# Install required python modules
|
|
||||||
python -m pip install --upgrade pip && \
|
|
||||||
# python -m pip install -r /opt/kcc/requirements.txt && \
|
|
||||||
python -m venv /opt/venv && \
|
|
||||||
python -m pip install --upgrade pillow python-slugify psutil raven mozjpeg-lossless-optimization
|
|
||||||
|
|
||||||
|
|
||||||
######################################################################################
|
|
||||||
FROM --platform=linux/amd64 python:3.11-slim-buster as build-amd64
|
|
||||||
COPY --from=compile-amd64 /opt/venv /opt/venv
|
|
||||||
|
|
||||||
FROM --platform=linux/arm64 python:3.11-slim-buster as build-arm64
|
|
||||||
COPY --from=compile-arm64 /opt/venv /opt/venv
|
|
||||||
|
|
||||||
FROM --platform=linux/arm/v7 python:3.11-slim-buster as build-armv7
|
|
||||||
COPY --from=compile-armv7 /opt/venv /opt/venv
|
|
||||||
######################################################################################
|
|
||||||
|
|
||||||
# Select final stage based on TARGETARCH ARG
|
# Select final stage based on TARGETARCH ARG
|
||||||
FROM build-${TARGETARCH}${TARGETVARIANT}
|
FROM ghcr.io/ciromattia/kcc:docker-base-20230809
|
||||||
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'
|
||||||
@@ -154,14 +12,8 @@ LABEL org.opencontainers.image.vendor='ciromattia'
|
|||||||
LABEL org.opencontainers.image.licenses='ISC'
|
LABEL org.opencontainers.image.licenses='ISC'
|
||||||
LABEL org.opencontainers.image.title="Kindle Comic Converter"
|
LABEL org.opencontainers.image.title="Kindle Comic Converter"
|
||||||
|
|
||||||
|
|
||||||
ENV PATH="/opt/venv/bin:$PATH"
|
|
||||||
WORKDIR /app
|
|
||||||
COPY . /opt/kcc
|
COPY . /opt/kcc
|
||||||
RUN DEBIAN_FRONTEND=noninteractive apt-get update -y && apt-get -yq upgrade && \
|
RUN cat /opt/kcc/kindlecomicconverter/__init__.py | grep version | awk '{print $3}' | sed "s/'//g" > /IMAGE_VERSION
|
||||||
apt-get install -y p7zip-full unrar-free && \
|
|
||||||
ln -s /app/kindlegen /bin/kindlegen && \
|
|
||||||
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"]
|
||||||
160
Dockerfile-base
Normal file
160
Dockerfile-base
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
FROM --platform=linux/amd64 python:3.11-slim-bullseye as compile-amd64
|
||||||
|
ARG TARGETOS
|
||||||
|
ARG TARGETARCH
|
||||||
|
ARG TARGETVARIANT
|
||||||
|
RUN echo "I'm building for $TARGETOS/$TARGETARCH/$TARGETVARIANT"
|
||||||
|
|
||||||
|
|
||||||
|
COPY requirements.txt /opt/kcc/
|
||||||
|
ENV PATH="/opt/venv/bin:$PATH"
|
||||||
|
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 && \
|
||||||
|
python -m pip install --upgrade pip && \
|
||||||
|
python -m venv /opt/venv && \
|
||||||
|
python -m pip install -r /opt/kcc/requirements.txt
|
||||||
|
|
||||||
|
|
||||||
|
######################################################################################
|
||||||
|
|
||||||
|
FROM --platform=linux/arm64 python:3.11-slim-bullseye as compile-arm64
|
||||||
|
ARG TARGETOS
|
||||||
|
ARG TARGETARCH
|
||||||
|
ARG TARGETVARIANT
|
||||||
|
RUN echo "I'm building for $TARGETOS/$TARGETARCH/$TARGETVARIANT"
|
||||||
|
|
||||||
|
ENV LC_ALL=C.UTF-8 \
|
||||||
|
LANG=C.UTF-8 \
|
||||||
|
LANGUAGE=en_US:en
|
||||||
|
|
||||||
|
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
|
||||||
|
|
||||||
|
RUN set -x && \
|
||||||
|
TEMP_PACKAGES=() && \
|
||||||
|
KEPT_PACKAGES=() && \
|
||||||
|
# Packages only required during build
|
||||||
|
TEMP_PACKAGES+=(build-essential) && \
|
||||||
|
TEMP_PACKAGES+=(cmake) && \
|
||||||
|
TEMP_PACKAGES+=(libfreetype6-dev) && \
|
||||||
|
TEMP_PACKAGES+=(libfontconfig1-dev) && \
|
||||||
|
TEMP_PACKAGES+=(libpng-dev) && \
|
||||||
|
TEMP_PACKAGES+=(libjpeg-dev) && \
|
||||||
|
TEMP_PACKAGES+=(libssl-dev) && \
|
||||||
|
TEMP_PACKAGES+=(libxft-dev) && \
|
||||||
|
TEMP_PACKAGES+=(make) && \
|
||||||
|
TEMP_PACKAGES+=(python3-dev) && \
|
||||||
|
TEMP_PACKAGES+=(python3-setuptools) && \
|
||||||
|
TEMP_PACKAGES+=(python3-wheel) && \
|
||||||
|
# Packages kept in the image
|
||||||
|
KEPT_PACKAGES+=(bash) && \
|
||||||
|
KEPT_PACKAGES+=(ca-certificates) && \
|
||||||
|
KEPT_PACKAGES+=(chrpath) && \
|
||||||
|
KEPT_PACKAGES+=(locales) && \
|
||||||
|
KEPT_PACKAGES+=(locales-all) && \
|
||||||
|
KEPT_PACKAGES+=(libfreetype6) && \
|
||||||
|
KEPT_PACKAGES+=(libfontconfig1) && \
|
||||||
|
KEPT_PACKAGES+=(p7zip-full) && \
|
||||||
|
KEPT_PACKAGES+=(python3) && \
|
||||||
|
KEPT_PACKAGES+=(python3-pip) && \
|
||||||
|
KEPT_PACKAGES+=(unrar-free) && \
|
||||||
|
# Install packages
|
||||||
|
DEBIAN_FRONTEND=noninteractive apt-get update -y && apt-get -yq upgrade && \
|
||||||
|
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
|
||||||
|
${KEPT_PACKAGES[@]} \
|
||||||
|
${TEMP_PACKAGES[@]} \
|
||||||
|
&& \
|
||||||
|
# Install required python modules
|
||||||
|
python -m pip install --upgrade pip && \
|
||||||
|
# python -m pip install -r /opt/kcc/requirements.txt && \
|
||||||
|
python -m venv /opt/venv && \
|
||||||
|
python -m pip install --upgrade pillow python-slugify psutil raven mozjpeg-lossless-optimization
|
||||||
|
|
||||||
|
|
||||||
|
######################################################################################
|
||||||
|
|
||||||
|
FROM --platform=linux/arm/v7 python:3.11-slim-bullseye as compile-armv7
|
||||||
|
ARG TARGETOS
|
||||||
|
ARG TARGETARCH
|
||||||
|
ARG TARGETVARIANT
|
||||||
|
RUN echo "I'm building for $TARGETOS/$TARGETARCH/$TARGETVARIANT"
|
||||||
|
|
||||||
|
ENV LC_ALL=C.UTF-8 \
|
||||||
|
LANG=C.UTF-8 \
|
||||||
|
LANGUAGE=en_US:en
|
||||||
|
|
||||||
|
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
|
||||||
|
|
||||||
|
RUN set -x && \
|
||||||
|
TEMP_PACKAGES=() && \
|
||||||
|
KEPT_PACKAGES=() && \
|
||||||
|
# Packages only required during build
|
||||||
|
TEMP_PACKAGES+=(build-essential) && \
|
||||||
|
TEMP_PACKAGES+=(cmake) && \
|
||||||
|
TEMP_PACKAGES+=(libffi-dev) && \
|
||||||
|
TEMP_PACKAGES+=(libfreetype6-dev) && \
|
||||||
|
TEMP_PACKAGES+=(libfontconfig1-dev) && \
|
||||||
|
TEMP_PACKAGES+=(libpng-dev) && \
|
||||||
|
TEMP_PACKAGES+=(libjpeg-dev) && \
|
||||||
|
TEMP_PACKAGES+=(libssl-dev) && \
|
||||||
|
TEMP_PACKAGES+=(libxft-dev) && \
|
||||||
|
TEMP_PACKAGES+=(make) && \
|
||||||
|
TEMP_PACKAGES+=(python3-dev) && \
|
||||||
|
TEMP_PACKAGES+=(python3-setuptools) && \
|
||||||
|
TEMP_PACKAGES+=(python3-wheel) && \
|
||||||
|
# Packages kept in the image
|
||||||
|
KEPT_PACKAGES+=(bash) && \
|
||||||
|
KEPT_PACKAGES+=(ca-certificates) && \
|
||||||
|
KEPT_PACKAGES+=(chrpath) && \
|
||||||
|
KEPT_PACKAGES+=(locales) && \
|
||||||
|
KEPT_PACKAGES+=(locales-all) && \
|
||||||
|
KEPT_PACKAGES+=(libfreetype6) && \
|
||||||
|
KEPT_PACKAGES+=(libfontconfig1) && \
|
||||||
|
KEPT_PACKAGES+=(p7zip-full) && \
|
||||||
|
KEPT_PACKAGES+=(python3) && \
|
||||||
|
KEPT_PACKAGES+=(python3-pip) && \
|
||||||
|
KEPT_PACKAGES+=(unrar-free) && \
|
||||||
|
# Install packages
|
||||||
|
DEBIAN_FRONTEND=noninteractive apt-get update -y && apt-get -yq upgrade && \
|
||||||
|
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
|
||||||
|
${KEPT_PACKAGES[@]} \
|
||||||
|
${TEMP_PACKAGES[@]} \
|
||||||
|
&& \
|
||||||
|
# Install required python modules
|
||||||
|
python -m pip install --upgrade pip && \
|
||||||
|
# python -m pip install -r /opt/kcc/requirements.txt && \
|
||||||
|
python -m venv /opt/venv && \
|
||||||
|
python -m pip install --upgrade pillow python-slugify psutil raven mozjpeg-lossless-optimization
|
||||||
|
|
||||||
|
|
||||||
|
######################################################################################
|
||||||
|
FROM --platform=linux/amd64 python:3.11-slim-bullseye as build-amd64
|
||||||
|
COPY --from=compile-amd64 /opt/venv /opt/venv
|
||||||
|
|
||||||
|
FROM --platform=linux/arm64 python:3.11-slim-bullseye as build-arm64
|
||||||
|
COPY --from=compile-arm64 /opt/venv /opt/venv
|
||||||
|
|
||||||
|
FROM --platform=linux/arm/v7 python:3.11-slim-bullseye as build-armv7
|
||||||
|
COPY --from=compile-armv7 /opt/venv /opt/venv
|
||||||
|
######################################################################################
|
||||||
|
|
||||||
|
# Select final stage based on TARGETARCH ARG
|
||||||
|
FROM build-${TARGETARCH}${TARGETVARIANT}
|
||||||
|
LABEL com.kcc.name="Kindle Comic Converter base image"
|
||||||
|
LABEL com.kcc.author="Ciro Mattia Gonano, Paweł Jastrzębski and Darodi"
|
||||||
|
LABEL org.opencontainers.image.description='Kindle Comic Converter base image'
|
||||||
|
LABEL org.opencontainers.image.documentation='https://github.com/ciromattia/kcc'
|
||||||
|
LABEL org.opencontainers.image.source='https://github.com/ciromattia/kcc'
|
||||||
|
LABEL org.opencontainers.image.authors='darodi'
|
||||||
|
LABEL org.opencontainers.image.url='https://github.com/ciromattia/kcc'
|
||||||
|
LABEL org.opencontainers.image.documentation='https://github.com/ciromattia/kcc'
|
||||||
|
LABEL org.opencontainers.image.vendor='ciromattia'
|
||||||
|
LABEL org.opencontainers.image.licenses='ISC'
|
||||||
|
LABEL org.opencontainers.image.title="Kindle Comic Converter"
|
||||||
|
|
||||||
|
|
||||||
|
ENV PATH="/opt/venv/bin:$PATH"
|
||||||
|
WORKDIR /app
|
||||||
|
RUN DEBIAN_FRONTEND=noninteractive apt-get update -y && apt-get -yq upgrade && \
|
||||||
|
apt-get install -y p7zip-full unrar-free && \
|
||||||
|
ln -s /app/kindlegen /bin/kindlegen && \
|
||||||
|
echo docker-base-20230809 > /IMAGE_VERSION
|
||||||
|
|
||||||
150
README.md
150
README.md
@@ -32,25 +32,21 @@ If you find **KCC** valuable you can consider donating to the authors:
|
|||||||
|
|
||||||
## INSTALLATION
|
## INSTALLATION
|
||||||
|
|
||||||
### BINARY RELEASES
|
### DOWNLOADS
|
||||||
You can find the latest binary at the following link:
|
You can find the latest binary at the following link:
|
||||||
|
|
||||||
- **https://github.com/ciromattia/kcc/releases**
|
- **https://github.com/ciromattia/kcc/releases**
|
||||||
- flatpak : https://flathub.org/apps/details/io.github.ciromattia.kcc
|
- flatpak : https://flathub.org/apps/details/io.github.ciromattia.kcc
|
||||||
- Docker: https://github.com/ciromattia/kcc/pkgs/container/kcc
|
- Docker: https://github.com/ciromattia/kcc/pkgs/container/kcc
|
||||||
|
|
||||||
~~- **[Windows](http://kcc.iosphe.re/Windows/) (64-bit only)**~~
|
|
||||||
~~- **[macOS](http://kcc.iosphe.re/OSX/) (10.14+)**~~
|
|
||||||
~~- **Linux:** Currently unavailable.~~
|
|
||||||
|
|
||||||
more information on [installation](https://github.com/ciromattia/kcc/wiki/Installation)
|
more information on [installation](https://github.com/ciromattia/kcc/wiki/Installation)
|
||||||
|
|
||||||
### DEPENDENCIES
|
### DEPENDENCIES
|
||||||
Following software is required to run Linux version of **KCC** and/or bare sources:
|
Following software is required to run Linux version of **KCC** and/or bare sources:
|
||||||
- Python 3.3+
|
- Python 3.3+
|
||||||
- [PyQt5](https://pypi.python.org/pypi/PyQt5) 5.6.0+ (only needed for GUI)
|
- [PySide6](https://pypi.org/project/PySide6/) 6.5.1+ (only needed for GUI)
|
||||||
- [Pillow](https://pypi.python.org/pypi/Pillow/) 4.0.0+ (5.2.0+ needed for WebP support)
|
- [Pillow](https://pypi.python.org/pypi/Pillow/) 4.0.0+ (5.2.0+ needed for WebP support)
|
||||||
- [psutil](https://pypi.python.org/pypi/psutil) 5.0.0+
|
- [psutil](https://pypi.python.org/pypi/psutil) 5.9.5+
|
||||||
- [python-slugify](https://pypi.python.org/pypi/python-slugify) 1.2.1+, <8.0.0
|
- [python-slugify](https://pypi.python.org/pypi/python-slugify) 1.2.1+, <8.0.0
|
||||||
- [raven](https://pypi.python.org/pypi/raven) 6.0.0+ (only needed for GUI)
|
- [raven](https://pypi.python.org/pypi/raven) 6.0.0+ (only needed for GUI)
|
||||||
|
|
||||||
@@ -59,21 +55,17 @@ On Debian based distributions these two commands should install all needed depen
|
|||||||
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ sudo apt-get install -y python3 python3-dev libpng-dev libjpeg-dev p7zip-full p7zip-rar unrar-free libgl1 python3-pyqt5 && \
|
$ sudo apt-get install -y python3 python3-dev libpng-dev libjpeg-dev p7zip-full p7zip-rar unrar-free libgl1 && \
|
||||||
python -m pip install --upgrade pip && \
|
python -m pip install --upgrade pip && \
|
||||||
python -m pip install --upgrade -r requirements.txt
|
python -m pip install --upgrade -r requirements.txt
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
#### Optional dependencies
|
#### Optional dependencies
|
||||||
- Qt platform integration plugin for Deepin Desktop Environment
|
- KindleGen ~~[(deprecated link)](http://www.amazon.com/gp/feature.html?ie=UTF8&docId=1000765211)~~ v2.9+ (For MOBI generation)
|
||||||
```bash
|
- should be placed in a directory reachable by your _PATH_ or in _KCC_ directory
|
||||||
$ sudo apt-get install qt5dxcb-plugin
|
- `KindleGen` can be found in [Kindle Previewer](https://www.amazon.com/Kindle-Previewer/b?ie=UTF8&node=21381691011)
|
||||||
```
|
- `KindleGen` can be also be found in [Kindle Comic Creator](https://www.amazon.com/b?node=23496309011)
|
||||||
|
|
||||||
- KindleGen ~~[deprecated link](http://www.amazon.com/gp/feature.html?ie=UTF8&docId=1000765211)~~ v2.9+ in a directory reachable by your _PATH_ or in _KCC_ directory *(For MOBI generation)
|
|
||||||
- It can be found in [Kindle Previewer](https://www.amazon.com/Kindle-Previewer/b?ie=UTF8&node=21381691011)
|
|
||||||
`Amazon Kindle Previewer 3 Folder\lib\fc\bin`, the usual location in windows is in windows is `C:\Users\user\AppData\Local\Amazon\Kindle Previewer 3\lib\fc\bin\`
|
|
||||||
- [7z](http://www.7-zip.org/download.html) *(For CBZ/ZIP, CBR/RAR, 7z/CB7 support)*
|
- [7z](http://www.7-zip.org/download.html) *(For CBZ/ZIP, CBR/RAR, 7z/CB7 support)*
|
||||||
- Unrar (no rar in 7z on Fedora)
|
- Unrar (no rar in 7z on Fedora)
|
||||||
|
|
||||||
@@ -87,8 +79,6 @@ $ sudo apt-get install qt5dxcb-plugin
|
|||||||
- CB7, 7Z *(With `7z` executable)*
|
- CB7, 7Z *(With `7z` executable)*
|
||||||
- PDF *(Only extracting JPG images)*
|
- PDF *(Only extracting JPG images)*
|
||||||
|
|
||||||
Add 7z to PATH via `setx path "%path%;C:\Program Files\7-Zip"`
|
|
||||||
|
|
||||||
## USAGE
|
## USAGE
|
||||||
|
|
||||||
Should be pretty self-explanatory. All options have detailed information in tooltips.
|
Should be pretty self-explanatory. All options have detailed information in tooltips.
|
||||||
@@ -136,89 +126,81 @@ sudo apt-get install python3 p7zip-full python3-pil python3-psutil python3-slugi
|
|||||||
### Standalone `kcc-c2e.py` usage:
|
### Standalone `kcc-c2e.py` usage:
|
||||||
|
|
||||||
```
|
```
|
||||||
Usage: kcc-c2e [options] comic_file|comic_folder
|
usage: kcc-c2e [options] [input]
|
||||||
|
|
||||||
Options:
|
MANDATORY:
|
||||||
MAIN:
|
input Full path to comic folder or file(s) to be processed.
|
||||||
-p PROFILE, --profile=PROFILE
|
|
||||||
Device profile (Available options: K1, K2, K34, K578,
|
|
||||||
KDX, KPW, KPW5, KV, KO, K11, KS, KoMT, KoG, KoGHD,
|
|
||||||
KoA, KoAHD, KoAH2O, KoAO, KoN, KoC, KoL, KoF, KoS,
|
|
||||||
KoE) [Default=KV]
|
|
||||||
-m, --manga-style Manga style (right-to-left reading and splitting)
|
|
||||||
-q, --hq Try to increase the quality of magnification
|
|
||||||
-2, --two-panel Display two not four panels in Panel View mode
|
|
||||||
-w, --webtoon Webtoon processing mode
|
|
||||||
--targetsize=TARGETSIZE
|
|
||||||
the maximal size of output file in MB. [Default=100MB
|
|
||||||
for webtoon and 400MB for others]
|
|
||||||
|
|
||||||
OUTPUT SETTINGS:
|
MAIN:
|
||||||
-o OUTPUT, --output=OUTPUT
|
-p PROFILE, --profile PROFILE
|
||||||
Output generated file to specified directory or file
|
Device profile (Available options: K1, K2, K34, K578, KDX, KPW, KPW5, KV, KO, K11, KS, KoMT, KoG, KoGHD, KoA, KoAHD, KoAH2O, KoAO, KoN, KoC, KoL, KoF, KoS, KoE) [Default=KV]
|
||||||
-t TITLE, --title=TITLE
|
-m, --manga-style Manga style (right-to-left reading and splitting)
|
||||||
Comic title [Default=filename or directory name]
|
-q, --hq Try to increase the quality of magnification
|
||||||
-f FORMAT, --format=FORMAT
|
-2, --two-panel Display two not four panels in Panel View mode
|
||||||
Output format (Available options: Auto, MOBI, EPUB,
|
-w, --webtoon Webtoon processing mode
|
||||||
CBZ, KFX, MOBI+EPUB) [Default=Auto]
|
--ts TARGETSIZE, --targetsize TARGETSIZE
|
||||||
-b BATCHSPLIT, --batchsplit=BATCHSPLIT
|
the maximal size of output file in MB. [Default=100MB for webtoon and 400MB for others]
|
||||||
Split output into multiple files. 0: Don't split 1:
|
|
||||||
Automatic mode 2: Consider every subdirectory as
|
|
||||||
separate volume [Default=0]
|
|
||||||
|
|
||||||
PROCESSING:
|
PROCESSING:
|
||||||
-n, --noprocessing Do not modify image and ignore any profil or
|
-n, --noprocessing Do not modify image and ignore any profil or processing option
|
||||||
processing option
|
-u, --upscale Resize images smaller than device's resolution
|
||||||
-u, --upscale Resize images smaller than device's resolution
|
-s, --stretch Stretch images to device's resolution
|
||||||
-s, --stretch Stretch images to device's resolution
|
-r SPLITTER, --splitter SPLITTER
|
||||||
-r SPLITTER, --splitter=SPLITTER
|
Double page parsing mode. 0: Split 1: Rotate 2: Both [Default=0]
|
||||||
Double page parsing mode. 0: Split 1: Rotate 2: Both
|
-g GAMMA, --gamma GAMMA
|
||||||
[Default=0]
|
Apply gamma correction to linearize the image [Default=Auto]
|
||||||
-g GAMMA, --gamma=GAMMA
|
-c CROPPING, --cropping CROPPING
|
||||||
Apply gamma correction to linearize the image
|
Set cropping mode. 0: Disabled 1: Margins 2: Margins + page numbers [Default=2]
|
||||||
[Default=Auto]
|
--cp CROPPINGP, --croppingpower CROPPINGP
|
||||||
-c CROPPING, --cropping=CROPPING
|
|
||||||
Set cropping mode. 0: Disabled 1: Margins 2: Margins +
|
|
||||||
page numbers [Default=2]
|
|
||||||
--cp=CROPPINGP, --croppingpower=CROPPINGP
|
|
||||||
Set cropping power [Default=1.0]
|
Set cropping power [Default=1.0]
|
||||||
--cm=CROPPINGM, --croppingminimum=CROPPINGM
|
--cm CROPPINGM, --croppingminimum CROPPINGM
|
||||||
Set cropping minimum area ratio [Default=0.0]
|
Set cropping minimum area ratio [Default=0.0]
|
||||||
--blackborders Disable autodetection and force black borders
|
--blackborders Disable autodetection and force black borders
|
||||||
--whiteborders Disable autodetection and force white borders
|
--whiteborders Disable autodetection and force white borders
|
||||||
--forcecolor Don't convert images to grayscale
|
--forcecolor Don't convert images to grayscale
|
||||||
--forcepng Create PNG files instead JPEG
|
--forcepng Create PNG files instead JPEG
|
||||||
--mozjpeg Create JPEG files using mozJpeg
|
--mozjpeg Create JPEG files using mozJpeg
|
||||||
--maximizestrips Turn 1x4 strips to 2x2 strips
|
--maximizestrips Turn 1x4 strips to 2x2 strips
|
||||||
-d, --delete Delete source file(s) or a directory. It's not
|
-d, --delete Delete source file(s) or a directory. It's not recoverable.
|
||||||
recoverable.
|
|
||||||
|
|
||||||
CUSTOM PROFILE:
|
OUTPUT SETTINGS:
|
||||||
--customwidth=CUSTOMWIDTH
|
-o OUTPUT, --output OUTPUT
|
||||||
|
Output generated file to specified directory or file
|
||||||
|
-t TITLE, --title TITLE
|
||||||
|
Comic title [Default=filename or directory name]
|
||||||
|
-f FORMAT, --format FORMAT
|
||||||
|
Output format (Available options: Auto, MOBI, EPUB, CBZ, KFX, MOBI+EPUB) [Default=Auto]
|
||||||
|
-b BATCHSPLIT, --batchsplit BATCHSPLIT
|
||||||
|
Split output into multiple files. 0: Don't split 1: Automatic mode 2: Consider every subdirectory as separate volume [Default=0]
|
||||||
|
|
||||||
|
CUSTOM PROFILE:
|
||||||
|
--customwidth CUSTOMWIDTH
|
||||||
Replace screen width provided by device profile
|
Replace screen width provided by device profile
|
||||||
--customheight=CUSTOMHEIGHT
|
--customheight CUSTOMHEIGHT
|
||||||
Replace screen height provided by device profile
|
Replace screen height provided by device profile
|
||||||
|
|
||||||
OTHER:
|
OTHER:
|
||||||
-h, --help Show this help message and exit
|
-h, --help Show this help message and exit
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Standalone `kcc-c2p.py` usage:
|
### Standalone `kcc-c2p.py` usage:
|
||||||
|
|
||||||
```
|
```
|
||||||
Usage: kcc-c2p [options] comic_folder
|
usage: kcc-c2p [options] [input]
|
||||||
|
|
||||||
Options:
|
MANDATORY:
|
||||||
MANDATORY:
|
input Full path to comic folder(s) to be processed. Separate multiple inputs with spaces.
|
||||||
-y HEIGHT, --height=HEIGHT
|
|
||||||
|
MAIN:
|
||||||
|
-y HEIGHT, --height HEIGHT
|
||||||
Height of the target device screen
|
Height of the target device screen
|
||||||
-i, --in-place Overwrite source directory
|
-i, --in-place Overwrite source directory
|
||||||
-m, --merge Combine every directory into a single image before
|
-m, --merge Combine every directory into a single image before splitting
|
||||||
splitting
|
|
||||||
|
|
||||||
OTHER:
|
OTHER:
|
||||||
-d, --debug Create debug file for every split image
|
-d, --debug Create debug file for every split image
|
||||||
-h, --help Show this help message and exit
|
-h, --help Show this help message and exit
|
||||||
```
|
```
|
||||||
|
|
||||||
## CREDITS
|
## CREDITS
|
||||||
|
|||||||
15
environment.yml
Normal file
15
environment.yml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
name: kcc
|
||||||
|
channels:
|
||||||
|
- conda-forge
|
||||||
|
- defaults
|
||||||
|
dependencies:
|
||||||
|
- python=3.11
|
||||||
|
- Pillow>=5.2.0
|
||||||
|
- psutil>=5.9.5
|
||||||
|
- python-slugify>=1.2.1
|
||||||
|
- raven>=6.0.0
|
||||||
|
- distro
|
||||||
|
- pip
|
||||||
|
- pip:
|
||||||
|
- mozjpeg-lossless-optimization>=1.1.2
|
||||||
|
- pyside6>=6.5.1
|
||||||
@@ -1,11 +1,3 @@
|
|||||||
|
pyside6-uic gui/KCC.ui > kindlecomicconverter/KCC_ui.py
|
||||||
REM install qt creator
|
pyside6-uic gui/MetaEditor.ui > kindlecomicconverter/KCC_ui_editor.py
|
||||||
REM conda create -n qtenv python=3.7
|
pyside6-rcc gui/KCC.qrc > kindlecomicconverter/KCC_rc.py
|
||||||
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
|
|
||||||
|
|||||||
@@ -1,10 +1,5 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
# PREPARE PYTHON ENV
|
pyside6-uic gui/KCC.ui --from-imports > kindlecomicconverter/KCC_ui.py
|
||||||
# conda create -n pyqt5 python=3.7
|
pyside6-uic gui/MetaEditor.ui --from-imports > kindlecomicconverter/KCC_ui_editor.py
|
||||||
# source activate pyqt5
|
pyside6-rcc gui/KCC.qrc > kindlecomicconverter/KCC_rc.py
|
||||||
# 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
|
|
||||||
|
|||||||
@@ -19,8 +19,9 @@
|
|||||||
# PERFORMANCE OF THIS SOFTWARE.
|
# PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
if sys.version_info[0] != 3:
|
|
||||||
print('ERROR: This is Python 3 script!')
|
if sys.version_info < (3, 8, 0):
|
||||||
|
print('ERROR: This is a Python 3.8+ script!')
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
from multiprocessing import freeze_support, set_start_method
|
from multiprocessing import freeze_support, set_start_method
|
||||||
|
|||||||
@@ -19,8 +19,9 @@
|
|||||||
# PERFORMANCE OF THIS SOFTWARE.
|
# PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
if sys.version_info[0] != 3:
|
|
||||||
print('ERROR: This is Python 3 script!')
|
if sys.version_info < (3, 8, 0):
|
||||||
|
print('ERROR: This is a Python 3.8+ script!')
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
from multiprocessing import freeze_support, set_start_method
|
from multiprocessing import freeze_support, set_start_method
|
||||||
|
|||||||
49
kcc.py
49
kcc.py
@@ -19,29 +19,48 @@
|
|||||||
# PERFORMANCE OF THIS SOFTWARE.
|
# PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
if sys.version_info[0] != 3:
|
|
||||||
print('ERROR: This is Python 3 script!')
|
if sys.version_info < (3, 8, 0):
|
||||||
|
print('ERROR: This is a Python 3.8+ script!')
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
# OS specific workarounds
|
# OS specific workarounds
|
||||||
import os
|
import os
|
||||||
if sys.platform.startswith('darwin'):
|
if sys.platform.startswith('darwin'):
|
||||||
|
# prioritize KC2 since it optionally also installs KP3
|
||||||
|
mac_paths = [
|
||||||
|
'/Applications/Kindle Comic Creator/Kindle Comic Creator.app/Contents/MacOS',
|
||||||
|
'/Applications/Kindle Previewer 3.app/Contents/lib/fc/bin/',
|
||||||
|
]
|
||||||
if getattr(sys, 'frozen', False):
|
if getattr(sys, 'frozen', False):
|
||||||
os.environ['PATH'] = os.path.dirname(os.path.abspath(sys.executable)) + \
|
os.environ['PATH'] += os.pathsep + os.pathsep.join(mac_paths +
|
||||||
'/../Resources:/Applications/Kindle Comic Creator/Kindle Comic Creator.app/Contents/MacOS:' \
|
[
|
||||||
'/Applications/Kindle Previewer 3.app/Contents/lib/fc/bin/:/usr/local/bin:/usr/bin:/bin'
|
'/opt/homebrew/bin',
|
||||||
os.chdir(os.path.dirname(os.path.abspath(sys.executable)) + '/../Resources')
|
'/usr/local/bin',
|
||||||
else:
|
'/usr/bin',
|
||||||
os.chdir(os.path.dirname(os.path.abspath(__file__)))
|
'/bin',
|
||||||
elif sys.platform.startswith('win'):
|
]
|
||||||
if getattr(sys, 'frozen', False):
|
)
|
||||||
os.environ['PATH'] = '%LOCALAPPDATA%\\Amazon\\Kindle Previewer 3\\lib\\fc\\bin\\;' + \
|
|
||||||
os.environ['PATH']
|
|
||||||
os.chdir(os.path.dirname(os.path.abspath(sys.executable)))
|
os.chdir(os.path.dirname(os.path.abspath(sys.executable)))
|
||||||
else:
|
else:
|
||||||
os.environ['PATH'] = os.path.dirname(os.path.abspath(__file__)) + '/other/windows/;' \
|
os.environ['PATH'] += os.pathsep + os.pathsep.join(mac_paths)
|
||||||
'%LOCALAPPDATA%\\Amazon\\Kindle Previewer 3\\lib\\fc\\bin\\;' + \
|
os.chdir(os.path.dirname(os.path.abspath(__file__)))
|
||||||
os.environ['PATH']
|
elif sys.platform.startswith('win'):
|
||||||
|
# prioritize KC2 since it optionally also installs KP3
|
||||||
|
win_paths = [
|
||||||
|
os.path.expandvars('%LOCALAPPDATA%\\Amazon\\KC2'),
|
||||||
|
os.path.expandvars('%LOCALAPPDATA%\\Amazon\\Kindle Previewer 3\\lib\\fc\\bin\\'),
|
||||||
|
'C:\\Program Files\\7-Zip',
|
||||||
|
]
|
||||||
|
if getattr(sys, 'frozen', False):
|
||||||
|
os.environ['PATH'] += os.pathsep + os.pathsep.join(win_paths)
|
||||||
|
os.chdir(os.path.dirname(os.path.abspath(sys.executable)))
|
||||||
|
else:
|
||||||
|
os.environ['PATH'] += os.pathsep + os.pathsep.join(win_paths +
|
||||||
|
[
|
||||||
|
os.path.dirname(os.path.abspath(__file__)) + '/other/windows/',
|
||||||
|
]
|
||||||
|
)
|
||||||
os.chdir(os.path.dirname(os.path.abspath(__file__)))
|
os.chdir(os.path.dirname(os.path.abspath(__file__)))
|
||||||
# Load additional Sentry configuration
|
# Load additional Sentry configuration
|
||||||
# if getattr(sys, 'frozen', False):
|
# if getattr(sys, 'frozen', False):
|
||||||
|
|||||||
@@ -16,23 +16,26 @@
|
|||||||
# OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
# OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||||
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
# PERFORMANCE OF THIS SOFTWARE.
|
# PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
import json
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
from urllib.parse import unquote
|
from urllib.parse import unquote
|
||||||
from urllib.request import urlretrieve
|
from urllib.request import urlopen
|
||||||
from time import sleep
|
from time import sleep
|
||||||
from shutil import move, rmtree
|
from shutil import move, rmtree
|
||||||
from subprocess import STDOUT, PIPE
|
from subprocess import STDOUT, PIPE
|
||||||
# noinspection PyUnresolvedReferences
|
# noinspection PyUnresolvedReferences
|
||||||
from PyQt5 import QtGui, QtCore, QtWidgets, QtNetwork
|
from PySide6 import QtGui, QtCore, QtWidgets, QtNetwork
|
||||||
|
from PySide6.QtCore import Qt
|
||||||
from xml.sax.saxutils import escape
|
from xml.sax.saxutils import escape
|
||||||
from psutil import Popen, Process
|
from psutil import Popen, Process
|
||||||
from copy import copy
|
from copy import copy
|
||||||
from distutils.version import StrictVersion
|
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
|
from .shared import HTMLStripper, sanitizeTrace, walkLevel
|
||||||
from . import __version__
|
from . import __version__
|
||||||
from . import comic2ebook
|
from . import comic2ebook
|
||||||
from . import metadata
|
from . import metadata
|
||||||
@@ -42,7 +45,7 @@ from . import KCC_ui_editor
|
|||||||
|
|
||||||
|
|
||||||
class QApplicationMessaging(QtWidgets.QApplication):
|
class QApplicationMessaging(QtWidgets.QApplication):
|
||||||
messageFromOtherInstance = QtCore.pyqtSignal(bytes)
|
messageFromOtherInstance = QtCore.Signal(bytes)
|
||||||
|
|
||||||
def __init__(self, argv):
|
def __init__(self, argv):
|
||||||
QtWidgets.QApplication.__init__(self, argv)
|
QtWidgets.QApplication.__init__(self, argv)
|
||||||
@@ -50,7 +53,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.QIODevice.WriteOnly)
|
socket.connectToServer(self._key, QtCore.QIODeviceBase.OpenModeFlag.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)
|
||||||
@@ -64,7 +67,7 @@ class QApplicationMessaging(QtWidgets.QApplication):
|
|||||||
self._server.close()
|
self._server.close()
|
||||||
|
|
||||||
def event(self, e):
|
def event(self, e):
|
||||||
if e.type() == QtCore.QEvent.FileOpen:
|
if e.type() == QtCore.QEvent.Type.FileOpen:
|
||||||
self.messageFromOtherInstance.emit(bytes(e.file(), 'UTF-8'))
|
self.messageFromOtherInstance.emit(bytes(e.file(), 'UTF-8'))
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
@@ -80,7 +83,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.QIODevice.WriteOnly)
|
socket.connectToServer(self._key, QtCore.QIODeviceBase.OpenModeFlag.WriteOnly)
|
||||||
socket.waitForConnected(self._timeout)
|
socket.waitForConnected(self._timeout)
|
||||||
socket.write(bytes(message, 'UTF-8'))
|
socket.write(bytes(message, 'UTF-8'))
|
||||||
socket.waitForBytesWritten(self._timeout)
|
socket.waitForBytesWritten(self._timeout)
|
||||||
@@ -88,46 +91,40 @@ class QApplicationMessaging(QtWidgets.QApplication):
|
|||||||
|
|
||||||
|
|
||||||
class QMainWindowKCC(QtWidgets.QMainWindow):
|
class QMainWindowKCC(QtWidgets.QMainWindow):
|
||||||
progressBarTick = QtCore.pyqtSignal(str)
|
progressBarTick = QtCore.Signal(str)
|
||||||
modeConvert = QtCore.pyqtSignal(int)
|
modeConvert = QtCore.Signal(int)
|
||||||
addMessage = QtCore.pyqtSignal(str, str, bool)
|
addMessage = QtCore.Signal(str, str, bool)
|
||||||
addTrayMessage = QtCore.pyqtSignal(str, str)
|
addTrayMessage = QtCore.Signal(str, str)
|
||||||
showDialog = QtCore.pyqtSignal(str, str)
|
showDialog = QtCore.Signal(str, str)
|
||||||
hideProgressBar = QtCore.pyqtSignal()
|
hideProgressBar = QtCore.Signal()
|
||||||
forceShutdown = QtCore.pyqtSignal()
|
forceShutdown = QtCore.Signal()
|
||||||
|
|
||||||
|
|
||||||
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.Normal, QtGui.QIcon.Off)
|
self.deviceKindle.addPixmap(QtGui.QPixmap(":/Devices/icons/Kindle.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
||||||
self.deviceKobo = QtGui.QIcon()
|
self.deviceKobo = QtGui.QIcon()
|
||||||
self.deviceKobo.addPixmap(QtGui.QPixmap(":/Devices/icons/Kobo.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
self.deviceKobo.addPixmap(QtGui.QPixmap(":/Devices/icons/Kobo.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
||||||
self.deviceOther = QtGui.QIcon()
|
self.deviceOther = QtGui.QIcon()
|
||||||
self.deviceOther.addPixmap(QtGui.QPixmap(":/Devices/icons/Other.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
self.deviceOther.addPixmap(QtGui.QPixmap(":/Devices/icons/Other.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
||||||
|
|
||||||
self.MOBIFormat = QtGui.QIcon()
|
self.MOBIFormat = QtGui.QIcon()
|
||||||
self.MOBIFormat.addPixmap(QtGui.QPixmap(":/Formats/icons/MOBI.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
self.MOBIFormat.addPixmap(QtGui.QPixmap(":/Formats/icons/MOBI.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
||||||
self.CBZFormat = QtGui.QIcon()
|
self.CBZFormat = QtGui.QIcon()
|
||||||
self.CBZFormat.addPixmap(QtGui.QPixmap(":/Formats/icons/CBZ.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
self.CBZFormat.addPixmap(QtGui.QPixmap(":/Formats/icons/CBZ.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
||||||
self.EPUBFormat = QtGui.QIcon()
|
self.EPUBFormat = QtGui.QIcon()
|
||||||
self.EPUBFormat.addPixmap(QtGui.QPixmap(":/Formats/icons/EPUB.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
self.EPUBFormat.addPixmap(QtGui.QPixmap(":/Formats/icons/EPUB.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
||||||
self.KFXFormat = QtGui.QIcon()
|
|
||||||
self.KFXFormat.addPixmap(QtGui.QPixmap(":/Formats/icons/KFX.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
|
||||||
self.MOBIEPUBFormat = QtGui.QIcon()
|
|
||||||
self.MOBIEPUBFormat.addPixmap(QtGui.QPixmap(":/Formats/icons/MOBI.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
|
||||||
self.EPUB200MBFormat = QtGui.QIcon()
|
|
||||||
self.EPUB200MBFormat.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.Normal, QtGui.QIcon.Off)
|
self.info.addPixmap(QtGui.QPixmap(":/Status/icons/info.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
||||||
self.warning = QtGui.QIcon()
|
self.warning = QtGui.QIcon()
|
||||||
self.warning.addPixmap(QtGui.QPixmap(":/Status/icons/warning.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
self.warning.addPixmap(QtGui.QPixmap(":/Status/icons/warning.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
||||||
self.error = QtGui.QIcon()
|
self.error = QtGui.QIcon()
|
||||||
self.error.addPixmap(QtGui.QPixmap(":/Status/icons/error.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
self.error.addPixmap(QtGui.QPixmap(":/Status/icons/error.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
||||||
|
|
||||||
self.programIcon = QtGui.QIcon()
|
self.programIcon = QtGui.QIcon()
|
||||||
self.programIcon.addPixmap(QtGui.QPixmap(":/Icon/icons/comic2ebook.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
self.programIcon.addPixmap(QtGui.QPixmap(":/Icon/icons/comic2ebook.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
||||||
|
|
||||||
|
|
||||||
class VersionThread(QtCore.QThread):
|
class VersionThread(QtCore.QThread):
|
||||||
@@ -142,64 +139,27 @@ class VersionThread(QtCore.QThread):
|
|||||||
self.wait()
|
self.wait()
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
# TODO adapt with github releases
|
try:
|
||||||
pass
|
last_version_url = urlopen("https://api.github.com/repos/ciromattia/kcc/releases/latest")
|
||||||
|
data = last_version_url.read()
|
||||||
|
encoding = last_version_url.info().get_content_charset('utf-8')
|
||||||
|
json_parser = json.loads(data.decode(encoding))
|
||||||
|
|
||||||
# try:
|
html_url = json_parser["html_url"]
|
||||||
# XML = parse(urlopen(Request('https://kcc.iosphe.re/Version/',
|
latest_version = json_parser["tag_name"]
|
||||||
# headers={'User-Agent': 'KindleComicConverter/' + __version__})))
|
latest_version = re.sub(r'^v', "", latest_version)
|
||||||
# except Exception:
|
|
||||||
# return
|
if ("b" not in __version__ and StrictVersion(latest_version) > StrictVersion(__version__)) \
|
||||||
# latestVersion = XML.childNodes[0].getElementsByTagName('LatestVersion')[0].childNodes[0].toxml()
|
or ("b" in __version__
|
||||||
# if ("beta" not in __version__ and StrictVersion(latestVersion) > StrictVersion(__version__)) \
|
and StrictVersion(latest_version) >= StrictVersion(re.sub(r'b.*', '', __version__))):
|
||||||
# or ("beta" in __version__
|
MW.addMessage.emit('<a href="' + html_url + '"><b>The new version is available!</b></a>', 'warning',
|
||||||
# and StrictVersion(latestVersion) >= StrictVersion(re.sub(r'-beta.*', '', __version__))):
|
False)
|
||||||
# if sys.platform.startswith('win'):
|
except Exception:
|
||||||
# self.newVersion = latestVersion
|
return
|
||||||
# self.md5 = XML.childNodes[0].getElementsByTagName('MD5')[0].childNodes[0].toxml()
|
|
||||||
# MW.showDialog.emit('<b>New version released!</b> <a href="https://github.com/ciromattia/kcc/releases/">'
|
|
||||||
# 'See changelog.</a><br/><br/>Installed version: ' + __version__ +
|
|
||||||
# '<br/>Current version: ' + latestVersion +
|
|
||||||
# '<br/><br/>Would you like to start automatic update?', 'question')
|
|
||||||
# self.getNewVersion()
|
|
||||||
# else:
|
|
||||||
# MW.addMessage.emit('<a href="https://kcc.iosphe.re/">'
|
|
||||||
# '<b>The new version is available!</b></a> '
|
|
||||||
# '(<a href="https://github.com/ciromattia/kcc/releases/">'
|
|
||||||
# 'Changelog</a>)', 'warning', False)
|
|
||||||
|
|
||||||
def setAnswer(self, dialoganswer):
|
def setAnswer(self, dialoganswer):
|
||||||
self.answer = dialoganswer
|
self.answer = dialoganswer
|
||||||
|
|
||||||
def getNewVersion(self):
|
|
||||||
while self.answer is None:
|
|
||||||
sleep(1)
|
|
||||||
if self.answer == QtWidgets.QMessageBox.Yes:
|
|
||||||
try:
|
|
||||||
MW.modeConvert.emit(-1)
|
|
||||||
MW.progressBarTick.emit('Downloading update')
|
|
||||||
path = urlretrieve('https://kcc.iosphe.re/Windows/KindleComicConverter_win_' +
|
|
||||||
self.newVersion + '.exe', reporthook=self.getNewVersionTick)
|
|
||||||
if self.md5 != md5Checksum(path[0]):
|
|
||||||
raise Exception
|
|
||||||
move(path[0], path[0] + '.exe')
|
|
||||||
MW.hideProgressBar.emit()
|
|
||||||
MW.modeConvert.emit(1)
|
|
||||||
Popen(path[0] + '.exe /SP- /silent /noicons', stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
|
|
||||||
MW.forceShutdown.emit()
|
|
||||||
except Exception:
|
|
||||||
MW.addMessage.emit('Failed to download the update!', 'warning', False)
|
|
||||||
MW.hideProgressBar.emit()
|
|
||||||
MW.modeConvert.emit(1)
|
|
||||||
|
|
||||||
def getNewVersionTick(self, size, blocksize, totalsize):
|
|
||||||
progress = int((size / (totalsize // blocksize)) * 100)
|
|
||||||
if size == 0:
|
|
||||||
MW.progressBarTick.emit('100')
|
|
||||||
if progress > self.barProgress:
|
|
||||||
self.barProgress = progress
|
|
||||||
MW.progressBarTick.emit('tick')
|
|
||||||
|
|
||||||
|
|
||||||
class ProgressThread(QtCore.QThread):
|
class ProgressThread(QtCore.QThread):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@@ -255,36 +215,37 @@ class WorkerThread(QtCore.QThread):
|
|||||||
MW.modeConvert.emit(0)
|
MW.modeConvert.emit(0)
|
||||||
|
|
||||||
parser = comic2ebook.makeParser()
|
parser = comic2ebook.makeParser()
|
||||||
options, _ = parser.parse_args()
|
options = parser.parse_args()
|
||||||
argv = ''
|
argv = ''
|
||||||
currentJobs = []
|
currentJobs = []
|
||||||
|
|
||||||
options.profile = GUI.profiles[str(GUI.deviceBox.currentText())]['Label']
|
options.profile = GUI.profiles[str(GUI.deviceBox.currentText())]['Label']
|
||||||
options.format = str(GUI.formatBox.currentText()).replace('/AZW3', '')
|
gui_current_format = GUI.formats[str(GUI.formatBox.currentText())]['format']
|
||||||
|
options.format = gui_current_format
|
||||||
if GUI.mangaBox.isChecked():
|
if GUI.mangaBox.isChecked():
|
||||||
options.righttoleft = True
|
options.righttoleft = True
|
||||||
if GUI.rotateBox.checkState() == 1:
|
if GUI.rotateBox.checkState() == Qt.CheckState.PartiallyChecked:
|
||||||
options.splitter = 2
|
options.splitter = 2
|
||||||
elif GUI.rotateBox.checkState() == 2:
|
elif GUI.rotateBox.checkState() == Qt.CheckState.Checked:
|
||||||
options.splitter = 1
|
options.splitter = 1
|
||||||
if GUI.qualityBox.checkState() == 1:
|
if GUI.qualityBox.checkState() == Qt.CheckState.PartiallyChecked:
|
||||||
options.autoscale = True
|
options.autoscale = True
|
||||||
elif GUI.qualityBox.checkState() == 2:
|
elif GUI.qualityBox.checkState() == Qt.CheckState.Checked:
|
||||||
options.hq = True
|
options.hq = True
|
||||||
if GUI.webtoonBox.isChecked():
|
if GUI.webtoonBox.isChecked():
|
||||||
options.webtoon = True
|
options.webtoon = True
|
||||||
if GUI.upscaleBox.checkState() == 1:
|
if GUI.upscaleBox.checkState() == Qt.CheckState.PartiallyChecked:
|
||||||
options.stretch = True
|
options.stretch = True
|
||||||
elif GUI.upscaleBox.checkState() == 2:
|
elif GUI.upscaleBox.checkState() == Qt.CheckState.Checked:
|
||||||
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()
|
options.cropping = GUI.croppingBox.checkState().value
|
||||||
if GUI.croppingBox.checkState() >= 1:
|
if GUI.croppingBox.checkState() != Qt.CheckState.Unchecked:
|
||||||
options.croppingp = float(GUI.croppingPowerValue)
|
options.croppingp = float(GUI.croppingPowerValue)
|
||||||
if GUI.borderBox.checkState() == 1:
|
if GUI.borderBox.checkState() == Qt.CheckState.PartiallyChecked:
|
||||||
options.white_borders = True
|
options.white_borders = True
|
||||||
elif GUI.borderBox.checkState() == 2:
|
elif GUI.borderBox.checkState() == Qt.CheckState.Checked:
|
||||||
options.black_borders = True
|
options.black_borders = True
|
||||||
if GUI.outputSplit.isChecked():
|
if GUI.outputSplit.isChecked():
|
||||||
options.batchsplit = 2
|
options.batchsplit = 2
|
||||||
@@ -296,9 +257,9 @@ 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.mozJpegBox.checkState() == 1:
|
if GUI.mozJpegBox.checkState() == Qt.CheckState.PartiallyChecked:
|
||||||
options.forcepng = True
|
options.forcepng = True
|
||||||
elif GUI.mozJpegBox.checkState() == 2:
|
elif GUI.mozJpegBox.checkState() == Qt.CheckState.Checked:
|
||||||
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())
|
||||||
@@ -316,7 +277,7 @@ class WorkerThread(QtCore.QThread):
|
|||||||
return
|
return
|
||||||
self.errors = False
|
self.errors = False
|
||||||
MW.addMessage.emit('<b>Source:</b> ' + job, 'info', False)
|
MW.addMessage.emit('<b>Source:</b> ' + job, 'info', False)
|
||||||
if str(GUI.formatBox.currentText()) == 'CBZ':
|
if gui_current_format == 'CBZ':
|
||||||
MW.addMessage.emit('Creating CBZ files', 'info', False)
|
MW.addMessage.emit('Creating CBZ files', 'info', False)
|
||||||
GUI.progress.content = 'Creating CBZ files'
|
GUI.progress.content = 'Creating CBZ files'
|
||||||
else:
|
else:
|
||||||
@@ -366,11 +327,11 @@ class WorkerThread(QtCore.QThread):
|
|||||||
return
|
return
|
||||||
if not self.errors:
|
if not self.errors:
|
||||||
GUI.progress.content = ''
|
GUI.progress.content = ''
|
||||||
if str(GUI.formatBox.currentText()) == 'CBZ':
|
if gui_current_format == 'CBZ':
|
||||||
MW.addMessage.emit('Creating CBZ files... <b>Done!</b>', 'info', True)
|
MW.addMessage.emit('Creating CBZ files... <b>Done!</b>', 'info', True)
|
||||||
else:
|
else:
|
||||||
MW.addMessage.emit('Creating EPUB files... <b>Done!</b>', 'info', True)
|
MW.addMessage.emit('Creating EPUB files... <b>Done!</b>', 'info', True)
|
||||||
if str(GUI.formatBox.currentText()) == 'MOBI/AZW3' or str(GUI.formatBox.currentText()) == 'MOBI+EPUB':
|
if 'MOBI' in gui_current_format:
|
||||||
MW.progressBarTick.emit('Creating MOBI files')
|
MW.progressBarTick.emit('Creating MOBI files')
|
||||||
MW.progressBarTick.emit(str(len(outputPath) * 2 + 1))
|
MW.progressBarTick.emit(str(len(outputPath) * 2 + 1))
|
||||||
MW.progressBarTick.emit('tick')
|
MW.progressBarTick.emit('tick')
|
||||||
@@ -471,7 +432,7 @@ class SystemTrayIcon(QtWidgets.QSystemTrayIcon):
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
if self.isSystemTrayAvailable():
|
if self.isSystemTrayAvailable():
|
||||||
QtWidgets.QSystemTrayIcon.__init__(self, GUI.icons.programIcon, MW)
|
self.setIcon(GUI.icons.programIcon)
|
||||||
self.activated.connect(self.catchClicks)
|
self.activated.connect(self.catchClicks)
|
||||||
|
|
||||||
def catchClicks(self):
|
def catchClicks(self):
|
||||||
@@ -480,7 +441,7 @@ class SystemTrayIcon(QtWidgets.QSystemTrayIcon):
|
|||||||
MW.activateWindow()
|
MW.activateWindow()
|
||||||
|
|
||||||
def addTrayMessage(self, message, icon):
|
def addTrayMessage(self, message, icon):
|
||||||
icon = eval('QtWidgets.QSystemTrayIcon.' + icon)
|
icon = eval('QtWidgets.QSystemTrayIcon.MessageIcon.' + icon)
|
||||||
if self.supportsMessages() and not MW.isActiveWindow():
|
if self.supportsMessages() and not MW.isActiveWindow():
|
||||||
self.showMessage('Kindle Comic Converter', message, icon)
|
self.showMessage('Kindle Comic Converter', message, icon)
|
||||||
|
|
||||||
@@ -506,7 +467,8 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
|||||||
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:
|
||||||
fnames = QtWidgets.QFileDialog.getOpenFileNames(MW, 'Select file', self.lastPath, 'Comic (*.pdf);;All (*.*)')
|
fnames = QtWidgets.QFileDialog.getOpenFileNames(MW, 'Select file', self.lastPath,
|
||||||
|
'Comic (*.pdf);;All (*.*)')
|
||||||
for fname in fnames[0]:
|
for fname in fnames[0]:
|
||||||
if fname != '':
|
if fname != '':
|
||||||
if sys.platform.startswith('win'):
|
if sys.platform.startswith('win'):
|
||||||
@@ -589,7 +551,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.Normal, QtGui.QIcon.Off)
|
icon.addPixmap(QtGui.QPixmap(":/Other/icons/convert.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
||||||
GUI.convertButton.setIcon(icon)
|
GUI.convertButton.setIcon(icon)
|
||||||
GUI.convertButton.setText('Convert')
|
GUI.convertButton.setText('Convert')
|
||||||
GUI.centralWidget.setAcceptDrops(True)
|
GUI.centralWidget.setAcceptDrops(True)
|
||||||
@@ -597,7 +559,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.Normal, QtGui.QIcon.Off)
|
icon.addPixmap(QtGui.QPixmap(":/Other/icons/clear.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
||||||
GUI.convertButton.setIcon(icon)
|
GUI.convertButton.setIcon(icon)
|
||||||
GUI.convertButton.setText('Abort')
|
GUI.convertButton.setText('Abort')
|
||||||
GUI.centralWidget.setAcceptDrops(False)
|
GUI.centralWidget.setAcceptDrops(False)
|
||||||
@@ -617,7 +579,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
|||||||
|
|
||||||
def togglecroppingBox(self, value):
|
def togglecroppingBox(self, value):
|
||||||
if value:
|
if value:
|
||||||
GUI.croppingWidget.setVisible(True)
|
GUI.croppingWidget.setVisible(True)
|
||||||
else:
|
else:
|
||||||
GUI.croppingWidget.setVisible(False)
|
GUI.croppingWidget.setVisible(False)
|
||||||
self.changeCroppingPower(100) # 1.0
|
self.changeCroppingPower(100) # 1.0
|
||||||
@@ -683,6 +645,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
|||||||
if not GUI.webtoonBox.isChecked():
|
if not GUI.webtoonBox.isChecked():
|
||||||
GUI.qualityBox.setEnabled(profile['PVOptions'])
|
GUI.qualityBox.setEnabled(profile['PVOptions'])
|
||||||
GUI.upscaleBox.setChecked(profile['DefaultUpscale'])
|
GUI.upscaleBox.setChecked(profile['DefaultUpscale'])
|
||||||
|
GUI.mangaBox.setChecked(True)
|
||||||
if not profile['PVOptions']:
|
if not profile['PVOptions']:
|
||||||
GUI.qualityBox.setChecked(False)
|
GUI.qualityBox.setChecked(False)
|
||||||
if str(GUI.deviceBox.currentText()) == 'Other':
|
if str(GUI.deviceBox.currentText()) == 'Other':
|
||||||
@@ -697,7 +660,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
|||||||
GUI.formatBox.setCurrentIndex(profile['DefaultFormat'])
|
GUI.formatBox.setCurrentIndex(profile['DefaultFormat'])
|
||||||
if not GUI.webtoonBox.isChecked():
|
if not GUI.webtoonBox.isChecked():
|
||||||
GUI.qualityBox.setEnabled(profile['PVOptions'])
|
GUI.qualityBox.setEnabled(profile['PVOptions'])
|
||||||
if str(GUI.formatBox.currentText()) == 'MOBI/AZW3':
|
if GUI.formats[str(GUI.formatBox.currentText())]['format'] == 'MOBI':
|
||||||
GUI.outputSplit.setEnabled(True)
|
GUI.outputSplit.setEnabled(True)
|
||||||
else:
|
else:
|
||||||
GUI.outputSplit.setEnabled(False)
|
GUI.outputSplit.setEnabled(False)
|
||||||
@@ -728,11 +691,11 @@ 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.Ok)
|
QtWidgets.QMessageBox.critical(MW, 'KCC - Error', message, QtWidgets.QMessageBox.StandardButton.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,
|
||||||
QtWidgets.QMessageBox.No))
|
QtWidgets.QMessageBox.No))
|
||||||
|
|
||||||
def updateProgressbar(self, command):
|
def updateProgressbar(self, command):
|
||||||
if command == 'tick':
|
if command == 'tick':
|
||||||
@@ -756,7 +719,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.ShiftModifier:
|
if QtWidgets.QApplication.keyboardModifiers() == QtCore.Qt.KeyboardModifier.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'):
|
||||||
@@ -779,22 +742,21 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
|||||||
self.addMessage('Target resolution is not set!', 'error')
|
self.addMessage('Target resolution is not set!', 'error')
|
||||||
self.needClean = True
|
self.needClean = True
|
||||||
return
|
return
|
||||||
if str(GUI.formatBox.currentText()) == 'MOBI/AZW3' and not self.kindleGen:
|
if 'MOBI' in GUI.formats[str(GUI.formatBox.currentText())]['format'] and not self.kindleGen:
|
||||||
self.detectKindleGen()
|
self.detectKindleGen()
|
||||||
if not self.kindleGen:
|
if not self.kindleGen:
|
||||||
GUI.jobList.clear()
|
GUI.jobList.clear()
|
||||||
self.addMessage('Cannot find <a href="http://www.amazon.com/gp/feature.html?ie=UTF8&docId='
|
self.display_kindlegen_missing()
|
||||||
'1000765211"><b>KindleGen</b></a>! MOBI conversion is unavailable!', 'error')
|
|
||||||
if sys.platform.startswith('win'):
|
|
||||||
self.addMessage('Download it and place EXE in KCC directory.', 'error')
|
|
||||||
elif sys.platform.startswith('darwin'):
|
|
||||||
self.addMessage('Install it using <a href="http://brew.sh/">Homebrew</a>.', 'error')
|
|
||||||
else:
|
|
||||||
self.addMessage('Download it and place executable in /usr/local/bin directory.', 'error')
|
|
||||||
self.needClean = True
|
self.needClean = True
|
||||||
return
|
return
|
||||||
self.worker.start()
|
self.worker.start()
|
||||||
|
|
||||||
|
def display_kindlegen_missing(self):
|
||||||
|
self.addMessage(
|
||||||
|
'<a href="https://github.com/ciromattia/kcc/wiki/Installation#kindlegen"><b>Cannot find KindleGen</b></a>: MOBI conversion is unavailable!',
|
||||||
|
'error'
|
||||||
|
)
|
||||||
|
|
||||||
def saveSettings(self, event):
|
def saveSettings(self, event):
|
||||||
if self.conversionAlive:
|
if self.conversionAlive:
|
||||||
GUI.convertButton.setEnabled(False)
|
GUI.convertButton.setEnabled(False)
|
||||||
@@ -810,23 +772,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(),
|
self.settings.setValue('options', {'mangaBox': GUI.mangaBox.checkState().value,
|
||||||
'rotateBox': GUI.rotateBox.checkState(),
|
'rotateBox': GUI.rotateBox.checkState().value,
|
||||||
'qualityBox': GUI.qualityBox.checkState(),
|
'qualityBox': GUI.qualityBox.checkState().value,
|
||||||
'gammaBox': GUI.gammaBox.checkState(),
|
'gammaBox': GUI.gammaBox.checkState().value,
|
||||||
'croppingBox': GUI.croppingBox.checkState(),
|
'croppingBox': GUI.croppingBox.checkState().value,
|
||||||
'croppingPowerSlider': float(self.croppingPowerValue) * 100,
|
'croppingPowerSlider': float(self.croppingPowerValue) * 100,
|
||||||
'upscaleBox': GUI.upscaleBox.checkState(),
|
'upscaleBox': GUI.upscaleBox.checkState().value,
|
||||||
'borderBox': GUI.borderBox.checkState(),
|
'borderBox': GUI.borderBox.checkState().value,
|
||||||
'webtoonBox': GUI.webtoonBox.checkState(),
|
'webtoonBox': GUI.webtoonBox.checkState().value,
|
||||||
'outputSplit': GUI.outputSplit.checkState(),
|
'outputSplit': GUI.outputSplit.checkState().value,
|
||||||
'colorBox': GUI.colorBox.checkState(),
|
'colorBox': GUI.colorBox.checkState().value,
|
||||||
'disableProcessingBox': GUI.disableProcessingBox.checkState(),
|
'disableProcessingBox': GUI.disableProcessingBox.checkState().value,
|
||||||
'mozJpegBox': GUI.mozJpegBox.checkState(),
|
'mozJpegBox': GUI.mozJpegBox.checkState().value,
|
||||||
'widthBox': GUI.widthBox.value(),
|
'widthBox': GUI.widthBox.value(),
|
||||||
'heightBox': GUI.heightBox.value(),
|
'heightBox': GUI.heightBox.value(),
|
||||||
'deleteBox': GUI.deleteBox.checkState(),
|
'deleteBox': GUI.deleteBox.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()
|
||||||
@@ -888,22 +850,21 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
|||||||
if 'Amazon kindlegen' in line:
|
if 'Amazon kindlegen' in line:
|
||||||
versionCheck = line.split('V')[1].split(' ')[0]
|
versionCheck = line.split('V')[1].split(' ')[0]
|
||||||
if StrictVersion(versionCheck) < StrictVersion('2.9'):
|
if StrictVersion(versionCheck) < StrictVersion('2.9'):
|
||||||
self.addMessage('Your <a href="http://www.amazon.com/gp/feature.html?ie=UTF8&docId='
|
self.addMessage('Your <a href="https://www.amazon.com/b?node=23496309011">KindleGen</a>'
|
||||||
'1000765211">KindleGen</a> is outdated! MOBI conversion might fail.', 'warning')
|
' is outdated! MOBI conversion might fail.', 'warning')
|
||||||
break
|
break
|
||||||
|
where_command = 'where kindlegen.exe'
|
||||||
|
if os.name == 'posix':
|
||||||
|
where_command = 'which kindlegen'
|
||||||
|
process = subprocess.run(where_command, stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
|
||||||
|
locations = process.stdout.decode('utf-8').split('\n')
|
||||||
|
self.addMessage(f"<b>KindleGen Found:</b> {locations[0]}", 'info')
|
||||||
else:
|
else:
|
||||||
self.kindleGen = False
|
self.kindleGen = False
|
||||||
if startup:
|
if startup:
|
||||||
self.addMessage('Cannot find <a href="http://www.amazon.com/gp/feature.html?ie=UTF8&docId=1000765211">'
|
self.display_kindlegen_missing()
|
||||||
'<b>KindleGen</b></a>! MOBI conversion will be unavailable!', 'error')
|
|
||||||
if sys.platform.startswith('win'):
|
|
||||||
self.addMessage('Download it and place EXE in KCC directory.', 'error')
|
|
||||||
elif sys.platform.startswith('darwin'):
|
|
||||||
self.addMessage('Install it using <a href="http://brew.sh/">Homebrew</a>: '
|
|
||||||
'<i>brew install --cask kindle-comic-creator</i> or '
|
|
||||||
'<i>brew install --cask kindle-previewer</i>', 'error')
|
|
||||||
else:
|
|
||||||
self.addMessage('Download it and place executable in /usr/local/bin directory.', 'error')
|
|
||||||
|
|
||||||
def __init__(self, kccapp, kccwindow):
|
def __init__(self, kccapp, kccwindow):
|
||||||
global APP, MW, GUI
|
global APP, MW, GUI
|
||||||
@@ -953,6 +914,16 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
|||||||
if self.windowSize == '0x0':
|
if self.windowSize == '0x0':
|
||||||
MW.resize(500, 500)
|
MW.resize(500, 500)
|
||||||
|
|
||||||
|
self.formats = { # text, icon, data/option_format
|
||||||
|
"MOBI/AZW3": {'icon': 'MOBI', 'format': 'MOBI'},
|
||||||
|
"EPUB": {'icon': 'EPUB', 'format': 'EPUB'},
|
||||||
|
"CBZ": {'icon': 'CBZ', 'format': 'CBZ'},
|
||||||
|
"EPUB (Calibre KFX)": {'icon': 'EPUB', 'format': 'KFX'},
|
||||||
|
"MOBI + EPUB": {'icon': 'MOBI', 'format': 'MOBI+EPUB'},
|
||||||
|
"EPUB (200MB limit)": {'icon': 'EPUB', 'format': 'EPUB-200MB'}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
self.profiles = {
|
self.profiles = {
|
||||||
"Kindle Oasis 2/3": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
|
"Kindle Oasis 2/3": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
|
||||||
'DefaultUpscale': True, 'Label': 'KO'},
|
'DefaultUpscale': True, 'Label': 'KO'},
|
||||||
@@ -961,7 +932,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
|||||||
"Kindle Voyage": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
|
"Kindle Voyage": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
|
||||||
'DefaultUpscale': True, 'Label': 'KV'},
|
'DefaultUpscale': True, 'Label': 'KV'},
|
||||||
"Kindle Scribe": {
|
"Kindle Scribe": {
|
||||||
'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': True, 'Label': 'KS',
|
'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': False, 'Label': 'KS',
|
||||||
},
|
},
|
||||||
"Kindle 11": {
|
"Kindle 11": {
|
||||||
'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': True, 'Label': 'K11',
|
'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': True, 'Label': 'K11',
|
||||||
@@ -1059,7 +1030,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.AlignCenter)
|
statusBarLabel.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
|
||||||
statusBarLabel.setOpenExternalLinks(True)
|
statusBarLabel.setOpenExternalLinks(True)
|
||||||
GUI.statusBar.addPermanentWidget(statusBarLabel, 1)
|
GUI.statusBar.addPermanentWidget(statusBarLabel, 1)
|
||||||
|
|
||||||
@@ -1075,8 +1046,8 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
|||||||
self.sevenzip = True
|
self.sevenzip = True
|
||||||
else:
|
else:
|
||||||
self.sevenzip = False
|
self.sevenzip = False
|
||||||
self.addMessage('Add <a href="http://www.7-zip.org/download.html">7z</a> to PATH!'
|
self.addMessage('<a href="https://github.com/ciromattia/kcc/wiki/Installation#7-zip">Cannot find 7z</a>!'
|
||||||
' Processing of archives will be disabled.', 'warning')
|
' CBZ/CBR/ZIP/etc processing disabled.', 'warning')
|
||||||
self.detectKindleGen(True)
|
self.detectKindleGen(True)
|
||||||
|
|
||||||
APP.messageFromOtherInstance.connect(self.handleMessage)
|
APP.messageFromOtherInstance.connect(self.handleMessage)
|
||||||
@@ -1117,9 +1088,8 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
|||||||
GUI.deviceBox.addItem(self.icons.deviceKobo, profile)
|
GUI.deviceBox.addItem(self.icons.deviceKobo, profile)
|
||||||
else:
|
else:
|
||||||
GUI.deviceBox.addItem(self.icons.deviceKindle, profile)
|
GUI.deviceBox.addItem(self.icons.deviceKindle, profile)
|
||||||
for f in ['MOBI/AZW3', 'EPUB', 'CBZ', 'KFX', 'MOBI+EPUB', 'EPUB-200MB']:
|
for f in self.formats:
|
||||||
format_prefix = f.replace('/AZW3', '').replace('+', '').replace('-', '')
|
GUI.formatBox.addItem(eval('self.icons.' + self.formats[f]['icon'] + 'Format'), f)
|
||||||
GUI.formatBox.addItem(eval('self.icons.' + format_prefix + '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":
|
||||||
@@ -1146,7 +1116,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
|||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
if eval('GUI.' + str(option)).isEnabled():
|
if eval('GUI.' + str(option)).isEnabled():
|
||||||
eval('GUI.' + str(option)).setCheckState(self.options[option])
|
eval('GUI.' + str(option)).setCheckState(Qt.CheckState(self.options[option]))
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
pass
|
pass
|
||||||
self.worker.sync()
|
self.worker.sync()
|
||||||
@@ -1220,7 +1190,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.WindowContextHelpButtonHint)
|
self.ui.setWindowFlags(self.ui.windowFlags() & ~QtCore.Qt.WindowType.WindowContextHelpButtonHint)
|
||||||
self.okButton.clicked.connect(self.saveData)
|
self.okButton.clicked.connect(self.saveData)
|
||||||
self.cancelButton.clicked.connect(self.ui.close)
|
self.cancelButton.clicked.connect(self.ui.close)
|
||||||
if sys.platform.startswith('linux'):
|
if sys.platform.startswith('linux'):
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,321 +1,454 @@
|
|||||||
# -*- 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.1
|
||||||
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
|
##
|
||||||
# run again. Do not edit this file unless you know what you are doing.
|
## WARNING! All changes made in this file will be lost when recompiling UI file!
|
||||||
|
################################################################################
|
||||||
|
|
||||||
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):
|
||||||
mainWindow.setObjectName("mainWindow")
|
if not mainWindow.objectName():
|
||||||
|
mainWindow.setObjectName(u"mainWindow")
|
||||||
mainWindow.resize(450, 400)
|
mainWindow.resize(450, 400)
|
||||||
icon = QtGui.QIcon()
|
icon = QIcon()
|
||||||
icon.addPixmap(QtGui.QPixmap(":/Icon/icons/comic2ebook.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
icon.addFile(u":/Icon/icons/comic2ebook.png", QSize(), QIcon.Normal, QIcon.Off)
|
||||||
mainWindow.setWindowIcon(icon)
|
mainWindow.setWindowIcon(icon)
|
||||||
self.centralWidget = QtWidgets.QWidget(mainWindow)
|
self.centralWidget = QWidget(mainWindow)
|
||||||
self.centralWidget.setObjectName("centralWidget")
|
self.centralWidget.setObjectName(u"centralWidget")
|
||||||
self.gridLayout = QtWidgets.QGridLayout(self.centralWidget)
|
self.gridLayout = QGridLayout(self.centralWidget)
|
||||||
|
self.gridLayout.setObjectName(u"gridLayout")
|
||||||
self.gridLayout.setContentsMargins(-1, -1, -1, 5)
|
self.gridLayout.setContentsMargins(-1, -1, -1, 5)
|
||||||
self.gridLayout.setObjectName("gridLayout")
|
self.optionWidget = QWidget(self.centralWidget)
|
||||||
self.optionWidget = QtWidgets.QWidget(self.centralWidget)
|
self.optionWidget.setObjectName(u"optionWidget")
|
||||||
self.optionWidget.setObjectName("optionWidget")
|
self.gridLayout_2 = QGridLayout(self.optionWidget)
|
||||||
self.gridLayout_2 = QtWidgets.QGridLayout(self.optionWidget)
|
self.gridLayout_2.setObjectName(u"gridLayout_2")
|
||||||
self.gridLayout_2.setContentsMargins(0, 0, 0, 0)
|
self.gridLayout_2.setContentsMargins(0, 0, 0, 0)
|
||||||
self.gridLayout_2.setObjectName("gridLayout_2")
|
self.upscaleBox = QCheckBox(self.optionWidget)
|
||||||
self.upscaleBox = QtWidgets.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.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.outputSplit.setObjectName("outputSplit")
|
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.webtoonBox.setObjectName("webtoonBox")
|
self.webtoonBox = QCheckBox(self.optionWidget)
|
||||||
|
self.webtoonBox.setObjectName(u"webtoonBox")
|
||||||
|
|
||||||
self.gridLayout_2.addWidget(self.webtoonBox, 1, 0, 1, 1)
|
self.gridLayout_2.addWidget(self.webtoonBox, 1, 0, 1, 1)
|
||||||
self.colorBox = QtWidgets.QCheckBox(self.optionWidget)
|
|
||||||
self.colorBox.setObjectName("colorBox")
|
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.gammaBox.setObjectName("gammaBox")
|
self.gammaBox = QCheckBox(self.optionWidget)
|
||||||
|
self.gammaBox.setObjectName(u"gammaBox")
|
||||||
|
|
||||||
self.gridLayout_2.addWidget(self.gammaBox, 1, 2, 1, 1)
|
self.gridLayout_2.addWidget(self.gammaBox, 1, 2, 1, 1)
|
||||||
self.borderBox = QtWidgets.QCheckBox(self.optionWidget)
|
|
||||||
|
self.borderBox = QCheckBox(self.optionWidget)
|
||||||
|
self.borderBox.setObjectName(u"borderBox")
|
||||||
self.borderBox.setTristate(True)
|
self.borderBox.setTristate(True)
|
||||||
self.borderBox.setObjectName("borderBox")
|
|
||||||
self.gridLayout_2.addWidget(self.borderBox, 2, 0, 1, 1)
|
self.gridLayout_2.addWidget(self.borderBox, 2, 0, 1, 1)
|
||||||
self.mangaBox = QtWidgets.QCheckBox(self.optionWidget)
|
|
||||||
self.mangaBox.setObjectName("mangaBox")
|
self.mangaBox = QCheckBox(self.optionWidget)
|
||||||
|
self.mangaBox.setObjectName(u"mangaBox")
|
||||||
|
|
||||||
self.gridLayout_2.addWidget(self.mangaBox, 0, 0, 1, 1)
|
self.gridLayout_2.addWidget(self.mangaBox, 0, 0, 1, 1)
|
||||||
self.qualityBox = QtWidgets.QCheckBox(self.optionWidget)
|
|
||||||
|
self.qualityBox = QCheckBox(self.optionWidget)
|
||||||
|
self.qualityBox.setObjectName(u"qualityBox")
|
||||||
self.qualityBox.setTristate(True)
|
self.qualityBox.setTristate(True)
|
||||||
self.qualityBox.setObjectName("qualityBox")
|
|
||||||
self.gridLayout_2.addWidget(self.qualityBox, 0, 2, 1, 1)
|
self.gridLayout_2.addWidget(self.qualityBox, 0, 2, 1, 1)
|
||||||
self.mozJpegBox = QtWidgets.QCheckBox(self.optionWidget)
|
|
||||||
|
self.mozJpegBox = QCheckBox(self.optionWidget)
|
||||||
|
self.mozJpegBox.setObjectName(u"mozJpegBox")
|
||||||
self.mozJpegBox.setTristate(True)
|
self.mozJpegBox.setTristate(True)
|
||||||
self.mozJpegBox.setObjectName("mozJpegBox")
|
|
||||||
self.gridLayout_2.addWidget(self.mozJpegBox, 3, 0, 1, 1)
|
self.gridLayout_2.addWidget(self.mozJpegBox, 3, 0, 1, 1)
|
||||||
self.maximizeStrips = QtWidgets.QCheckBox(self.optionWidget)
|
|
||||||
self.maximizeStrips.setObjectName("maximizeStrips")
|
self.maximizeStrips = QCheckBox(self.optionWidget)
|
||||||
|
self.maximizeStrips.setObjectName(u"maximizeStrips")
|
||||||
|
|
||||||
self.gridLayout_2.addWidget(self.maximizeStrips, 3, 1, 1, 1)
|
self.gridLayout_2.addWidget(self.maximizeStrips, 3, 1, 1, 1)
|
||||||
self.croppingBox = QtWidgets.QCheckBox(self.optionWidget)
|
|
||||||
|
self.croppingBox = QCheckBox(self.optionWidget)
|
||||||
|
self.croppingBox.setObjectName(u"croppingBox")
|
||||||
self.croppingBox.setTristate(True)
|
self.croppingBox.setTristate(True)
|
||||||
self.croppingBox.setObjectName("croppingBox")
|
|
||||||
self.gridLayout_2.addWidget(self.croppingBox, 3, 2, 1, 1)
|
self.gridLayout_2.addWidget(self.croppingBox, 3, 2, 1, 1)
|
||||||
self.deleteBox = QtWidgets.QCheckBox(self.optionWidget)
|
|
||||||
self.deleteBox.setObjectName("deleteBox")
|
self.deleteBox = QCheckBox(self.optionWidget)
|
||||||
|
self.deleteBox.setObjectName(u"deleteBox")
|
||||||
|
|
||||||
self.gridLayout_2.addWidget(self.deleteBox, 4, 1, 1, 1)
|
self.gridLayout_2.addWidget(self.deleteBox, 4, 1, 1, 1)
|
||||||
self.disableProcessingBox = QtWidgets.QCheckBox(self.optionWidget)
|
|
||||||
self.disableProcessingBox.setObjectName("disableProcessingBox")
|
self.disableProcessingBox = QCheckBox(self.optionWidget)
|
||||||
|
self.disableProcessingBox.setObjectName(u"disableProcessingBox")
|
||||||
|
|
||||||
self.gridLayout_2.addWidget(self.disableProcessingBox, 4, 2, 1, 1)
|
self.gridLayout_2.addWidget(self.disableProcessingBox, 4, 2, 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.gammaWidget.setObjectName("gammaWidget")
|
self.horizontalLayout_2 = QHBoxLayout(self.gammaWidget)
|
||||||
self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.gammaWidget)
|
self.horizontalLayout_2.setObjectName(u"horizontalLayout_2")
|
||||||
self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0)
|
self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0)
|
||||||
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
|
self.gammaLabel = QLabel(self.gammaWidget)
|
||||||
self.gammaLabel = QtWidgets.QLabel(self.gammaWidget)
|
self.gammaLabel.setObjectName(u"gammaLabel")
|
||||||
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(QtCore.Qt.Horizontal)
|
self.gammaSlider.setOrientation(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.croppingWidget.setObjectName("croppingWidget")
|
self.horizontalLayout_3 = QHBoxLayout(self.croppingWidget)
|
||||||
self.horizontalLayout_3 = QtWidgets.QHBoxLayout(self.croppingWidget)
|
self.horizontalLayout_3.setObjectName(u"horizontalLayout_3")
|
||||||
self.horizontalLayout_3.setContentsMargins(0, 0, 0, 0)
|
self.horizontalLayout_3.setContentsMargins(0, 0, 0, 0)
|
||||||
self.horizontalLayout_3.setObjectName("horizontalLayout_3")
|
self.croppingPowerLabel = QLabel(self.croppingWidget)
|
||||||
self.croppingPowerLabel = QtWidgets.QLabel(self.croppingWidget)
|
self.croppingPowerLabel.setObjectName(u"croppingPowerLabel")
|
||||||
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(QtCore.Qt.Horizontal)
|
self.croppingPowerSlider.setOrientation(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)
|
|
||||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
|
self.buttonWidget = QWidget(self.centralWidget)
|
||||||
|
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.buttonWidget.setObjectName("buttonWidget")
|
self.gridLayout_4 = QGridLayout(self.buttonWidget)
|
||||||
self.gridLayout_4 = QtWidgets.QGridLayout(self.buttonWidget)
|
self.gridLayout_4.setObjectName(u"gridLayout_4")
|
||||||
self.gridLayout_4.setContentsMargins(0, 0, 0, 0)
|
self.gridLayout_4.setContentsMargins(0, 0, 0, 0)
|
||||||
self.gridLayout_4.setObjectName("gridLayout_4")
|
self.directoryButton = QPushButton(self.buttonWidget)
|
||||||
self.directoryButton = QtWidgets.QPushButton(self.buttonWidget)
|
self.directoryButton.setObjectName(u"directoryButton")
|
||||||
self.directoryButton.setMinimumSize(QtCore.QSize(0, 30))
|
self.directoryButton.setMinimumSize(QSize(0, 30))
|
||||||
icon1 = QtGui.QIcon()
|
icon1 = QIcon()
|
||||||
icon1.addPixmap(QtGui.QPixmap(":/Other/icons/folder_new.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
icon1.addFile(u":/Other/icons/folder_new.png", QSize(), QIcon.Normal, 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.setMinimumSize(QtCore.QSize(0, 30))
|
self.fileButton = QPushButton(self.buttonWidget)
|
||||||
icon2 = QtGui.QIcon()
|
self.fileButton.setObjectName(u"fileButton")
|
||||||
icon2.addPixmap(QtGui.QPixmap(":/Other/icons/document_new.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
self.fileButton.setMinimumSize(QSize(0, 30))
|
||||||
|
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.setMinimumSize(QtCore.QSize(0, 28))
|
self.deviceBox = QComboBox(self.buttonWidget)
|
||||||
self.deviceBox.setObjectName("deviceBox")
|
self.deviceBox.setObjectName(u"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.setMinimumSize(QtCore.QSize(0, 28))
|
self.formatBox = QComboBox(self.buttonWidget)
|
||||||
self.formatBox.setObjectName("formatBox")
|
self.formatBox.setObjectName(u"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.setMinimumSize(QtCore.QSize(0, 30))
|
self.convertButton = QPushButton(self.buttonWidget)
|
||||||
font = QtGui.QFont()
|
self.convertButton.setObjectName(u"convertButton")
|
||||||
|
self.convertButton.setMinimumSize(QSize(0, 30))
|
||||||
|
font = QFont()
|
||||||
font.setBold(True)
|
font.setBold(True)
|
||||||
self.convertButton.setFont(font)
|
self.convertButton.setFont(font)
|
||||||
icon3 = QtGui.QIcon()
|
icon3 = QIcon()
|
||||||
icon3.addPixmap(QtGui.QPixmap(":/Other/icons/convert.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
icon3.addFile(u":/Other/icons/convert.png", QSize(), QIcon.Normal, 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.setMinimumSize(QtCore.QSize(0, 30))
|
self.clearButton = QPushButton(self.buttonWidget)
|
||||||
icon4 = QtGui.QIcon()
|
self.clearButton.setObjectName(u"clearButton")
|
||||||
icon4.addPixmap(QtGui.QPixmap(":/Other/icons/clear.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
self.clearButton.setMinimumSize(QSize(0, 30))
|
||||||
|
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.setObjectName("toolWidget")
|
self.toolWidget = QWidget(self.centralWidget)
|
||||||
self.horizontalLayout = QtWidgets.QHBoxLayout(self.toolWidget)
|
self.toolWidget.setObjectName(u"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.horizontalLayout.setObjectName("horizontalLayout")
|
self.editorButton = QPushButton(self.toolWidget)
|
||||||
self.editorButton = QtWidgets.QPushButton(self.toolWidget)
|
self.editorButton.setObjectName(u"editorButton")
|
||||||
self.editorButton.setMinimumSize(QtCore.QSize(0, 30))
|
self.editorButton.setMinimumSize(QSize(0, 30))
|
||||||
icon5 = QtGui.QIcon()
|
icon5 = QIcon()
|
||||||
icon5.addPixmap(QtGui.QPixmap(":/Other/icons/editor.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
icon5.addFile(u":/Other/icons/editor.png", QSize(), QIcon.Normal, 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.setMinimumSize(QtCore.QSize(0, 30))
|
self.wikiButton = QPushButton(self.toolWidget)
|
||||||
icon6 = QtGui.QIcon()
|
self.wikiButton.setObjectName(u"wikiButton")
|
||||||
icon6.addPixmap(QtGui.QPixmap(":/Other/icons/wiki.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
self.wikiButton.setMinimumSize(QSize(0, 30))
|
||||||
|
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.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 = QListWidget(self.centralWidget)
|
||||||
self.jobList.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection)
|
self.jobList.setObjectName(u"jobList")
|
||||||
self.jobList.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel)
|
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.setHorizontalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel)
|
self.jobList.setSelectionMode(QAbstractItemView.NoSelection)
|
||||||
self.jobList.setObjectName("jobList")
|
self.jobList.setVerticalScrollMode(QAbstractItemView.ScrollPerPixel)
|
||||||
|
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.setMinimumSize(QtCore.QSize(0, 30))
|
self.progressBar = QProgressBar(self.centralWidget)
|
||||||
font = QtGui.QFont()
|
self.progressBar.setObjectName(u"progressBar")
|
||||||
font.setBold(True)
|
self.progressBar.setMinimumSize(QSize(0, 30))
|
||||||
self.progressBar.setFont(font)
|
self.progressBar.setFont(font)
|
||||||
self.progressBar.setVisible(False)
|
self.progressBar.setVisible(False)
|
||||||
self.progressBar.setAlignment(QtCore.Qt.AlignJustify|QtCore.Qt.AlignVCenter)
|
self.progressBar.setAlignment(Qt.AlignJustify|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.customWidget.setObjectName("customWidget")
|
self.gridLayout_3 = QGridLayout(self.customWidget)
|
||||||
self.gridLayout_3 = QtWidgets.QGridLayout(self.customWidget)
|
self.gridLayout_3.setObjectName(u"gridLayout_3")
|
||||||
self.gridLayout_3.setContentsMargins(0, 0, 0, 0)
|
self.gridLayout_3.setContentsMargins(0, 0, 0, 0)
|
||||||
self.gridLayout_3.setObjectName("gridLayout_3")
|
self.hLabel = QLabel(self.customWidget)
|
||||||
self.hLabel = QtWidgets.QLabel(self.customWidget)
|
self.hLabel.setObjectName(u"hLabel")
|
||||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Preferred)
|
sizePolicy1 = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred)
|
||||||
sizePolicy.setHorizontalStretch(0)
|
sizePolicy1.setHorizontalStretch(0)
|
||||||
sizePolicy.setVerticalStretch(0)
|
sizePolicy1.setVerticalStretch(0)
|
||||||
sizePolicy.setHeightForWidth(self.hLabel.sizePolicy().hasHeightForWidth())
|
sizePolicy1.setHeightForWidth(self.hLabel.sizePolicy().hasHeightForWidth())
|
||||||
self.hLabel.setSizePolicy(sizePolicy)
|
self.hLabel.setSizePolicy(sizePolicy1)
|
||||||
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)
|
|
||||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Preferred)
|
self.wLabel = QLabel(self.customWidget)
|
||||||
sizePolicy.setHorizontalStretch(0)
|
self.wLabel.setObjectName(u"wLabel")
|
||||||
sizePolicy.setVerticalStretch(0)
|
sizePolicy1.setHeightForWidth(self.wLabel.sizePolicy().hasHeightForWidth())
|
||||||
sizePolicy.setHeightForWidth(self.wLabel.sizePolicy().hasHeightForWidth())
|
self.wLabel.setSizePolicy(sizePolicy1)
|
||||||
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 = QtWidgets.QStatusBar(mainWindow)
|
self.statusBar = 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)
|
|
||||||
mainWindow.setTabOrder(self.convertButton, self.clearButton)
|
QMetaObject.connectSlotsByName(mainWindow)
|
||||||
mainWindow.setTabOrder(self.clearButton, self.directoryButton)
|
# setupUi
|
||||||
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):
|
||||||
_translate = QtCore.QCoreApplication.translate
|
mainWindow.setWindowTitle(QCoreApplication.translate("mainWindow", u"Kindle Comic Converter", None))
|
||||||
mainWindow.setWindowTitle(_translate("mainWindow", "Kindle Comic Converter"))
|
#if QT_CONFIG(tooltip)
|
||||||
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>"))
|
self.upscaleBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Nothing<br/></span>Images smaller than device resolution will not be resized.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - Stretching<br/></span>Images smaller than device resolution will be resized. Aspect ratio will be not preserved.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Upscaling<br/></span>Images smaller than device resolution will be resized. Aspect ratio will be preserved.</p></body></html>", None))
|
||||||
self.upscaleBox.setText(_translate("mainWindow", "Stretch/Upscale"))
|
#endif // QT_CONFIG(tooltip)
|
||||||
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>"))
|
self.upscaleBox.setText(QCoreApplication.translate("mainWindow", u"Stretch/Upscale", None))
|
||||||
self.rotateBox.setText(_translate("mainWindow", "Spread splitter"))
|
#if QT_CONFIG(tooltip)
|
||||||
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>"))
|
self.rotateBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Split<br/></span>Double page spreads will be cut into two separate pages.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - Rotate and split<br/></span>Double page spreads will be displayed twice. First rotated and then split. </p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Rotate<br/></span>Double page spreads will be rotated.</p></body></html>", None))
|
||||||
self.outputSplit.setText(_translate("mainWindow", "Output split"))
|
#endif // QT_CONFIG(tooltip)
|
||||||
self.webtoonBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Enable special parsing mode for Korean Webtoons.</p></body></html>"))
|
self.rotateBox.setText(QCoreApplication.translate("mainWindow", u"Spread splitter", None))
|
||||||
self.webtoonBox.setText(_translate("mainWindow", "Webtoon mode"))
|
#if QT_CONFIG(tooltip)
|
||||||
self.colorBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Disable conversion to grayscale.</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.colorBox.setText(_translate("mainWindow", "Color mode"))
|
#endif // QT_CONFIG(tooltip)
|
||||||
self.gammaBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Disable automatic gamma correction.</p></body></html>"))
|
self.outputSplit.setText(QCoreApplication.translate("mainWindow", u"Output split", None))
|
||||||
self.gammaBox.setText(_translate("mainWindow", "Custom gamma"))
|
#if QT_CONFIG(tooltip)
|
||||||
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>"))
|
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.borderBox.setText(_translate("mainWindow", "W/B margins"))
|
#endif // QT_CONFIG(tooltip)
|
||||||
self.mangaBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Enable right-to-left reading.</p></body></html>"))
|
self.webtoonBox.setText(QCoreApplication.translate("mainWindow", u"Webtoon mode", None))
|
||||||
self.mangaBox.setText(_translate("mainWindow", "Manga mode"))
|
#if QT_CONFIG(tooltip)
|
||||||
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>"))
|
self.colorBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Disable conversion to grayscale.</p></body></html>", None))
|
||||||
self.qualityBox.setText(_translate("mainWindow", "Panel View 4/2/HQ"))
|
#endif // QT_CONFIG(tooltip)
|
||||||
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>"))
|
self.colorBox.setText(QCoreApplication.translate("mainWindow", u"Color mode", None))
|
||||||
self.mozJpegBox.setText(_translate("mainWindow", "JPEG/PNG/mozJpeg"))
|
#if QT_CONFIG(tooltip)
|
||||||
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>"))
|
self.gammaBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Disable automatic gamma correction.</p></body></html>", None))
|
||||||
self.maximizeStrips.setText(_translate("mainWindow", "1x4 to 2x2 strips"))
|
#endif // QT_CONFIG(tooltip)
|
||||||
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>"))
|
self.gammaBox.setText(QCoreApplication.translate("mainWindow", u"Custom gamma", None))
|
||||||
self.croppingBox.setText(_translate("mainWindow", "Cropping mode"))
|
#if QT_CONFIG(tooltip)
|
||||||
self.deleteBox.setToolTip(_translate("mainWindow", "Delete input file(s) or directory. It\'s not recoverable!"))
|
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.deleteBox.setText(_translate("mainWindow", "Delete input"))
|
#endif // QT_CONFIG(tooltip)
|
||||||
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>"))
|
self.borderBox.setText(QCoreApplication.translate("mainWindow", u"W/B margins", None))
|
||||||
self.disableProcessingBox.setText(_translate("mainWindow", "Disable processing"))
|
#if QT_CONFIG(tooltip)
|
||||||
self.gammaLabel.setText(_translate("mainWindow", "Gamma: Auto"))
|
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.croppingPowerLabel.setText(_translate("mainWindow", "Cropping power:"))
|
#endif // QT_CONFIG(tooltip)
|
||||||
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>"))
|
self.mangaBox.setText(QCoreApplication.translate("mainWindow", u"Manga mode", None))
|
||||||
self.directoryButton.setText(_translate("mainWindow", "Add directory"))
|
#if QT_CONFIG(tooltip)
|
||||||
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>"))
|
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.fileButton.setText(_translate("mainWindow", "Add file"))
|
#endif // QT_CONFIG(tooltip)
|
||||||
self.deviceBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Target device.</p></body></html>"))
|
self.qualityBox.setText(QCoreApplication.translate("mainWindow", u"Panel View 4/2/HQ", None))
|
||||||
self.formatBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Output format.</p></body></html>"))
|
#if QT_CONFIG(tooltip)
|
||||||
self.convertButton.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Shift+Click to select the output directory.</p></body></html>"))
|
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.convertButton.setText(_translate("mainWindow", "Convert"))
|
#endif // QT_CONFIG(tooltip)
|
||||||
self.clearButton.setText(_translate("mainWindow", "Clear list"))
|
self.mozJpegBox.setText(QCoreApplication.translate("mainWindow", u"JPEG/PNG/mozJpeg", None))
|
||||||
self.editorButton.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Shift+Click to edit directory.</p></body></html>"))
|
#if QT_CONFIG(tooltip)
|
||||||
self.editorButton.setText(_translate("mainWindow", "Editor"))
|
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.wikiButton.setText(_translate("mainWindow", "Wiki"))
|
#endif // QT_CONFIG(tooltip)
|
||||||
self.hLabel.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Resolution of the target device.</p></body></html>"))
|
self.maximizeStrips.setText(QCoreApplication.translate("mainWindow", u"1x4 to 2x2 strips", None))
|
||||||
self.hLabel.setText(_translate("mainWindow", "Custom height:"))
|
#if QT_CONFIG(tooltip)
|
||||||
self.widthBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Resolution of the target device.</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.wLabel.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.setText(_translate("mainWindow", "Custom width:"))
|
self.croppingBox.setText(QCoreApplication.translate("mainWindow", u"Cropping mode", None))
|
||||||
self.heightBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Resolution of the target device.</p></body></html>"))
|
#if QT_CONFIG(tooltip)
|
||||||
from . import KCC_rc
|
self.deleteBox.setToolTip(QCoreApplication.translate("mainWindow", u"Delete input file(s) or directory. It's not recoverable!", None))
|
||||||
|
#endif // QT_CONFIG(tooltip)
|
||||||
|
self.deleteBox.setText(QCoreApplication.translate("mainWindow", u"Delete input", None))
|
||||||
|
#if QT_CONFIG(tooltip)
|
||||||
|
self.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))
|
||||||
|
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
|
||||||
|
|
||||||
|
|||||||
@@ -1,118 +1,168 @@
|
|||||||
# -*- 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.1
|
||||||
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
|
##
|
||||||
# run again. Do not edit this file unless you know what you are doing.
|
## WARNING! All changes made in this file will be lost when recompiling UI file!
|
||||||
|
################################################################################
|
||||||
|
|
||||||
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):
|
||||||
editorDialog.setObjectName("editorDialog")
|
if not editorDialog.objectName():
|
||||||
|
editorDialog.setObjectName(u"editorDialog")
|
||||||
editorDialog.resize(400, 260)
|
editorDialog.resize(400, 260)
|
||||||
editorDialog.setMinimumSize(QtCore.QSize(400, 260))
|
editorDialog.setMinimumSize(QSize(400, 260))
|
||||||
icon = QtGui.QIcon()
|
icon = QIcon()
|
||||||
icon.addPixmap(QtGui.QPixmap(":/Icon/icons/comic2ebook.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
icon.addFile(u":/Icon/icons/comic2ebook.png", QSize(), QIcon.Normal, QIcon.Off)
|
||||||
editorDialog.setWindowIcon(icon)
|
editorDialog.setWindowIcon(icon)
|
||||||
self.verticalLayout = QtWidgets.QVBoxLayout(editorDialog)
|
self.verticalLayout = QVBoxLayout(editorDialog)
|
||||||
|
self.verticalLayout.setObjectName(u"verticalLayout")
|
||||||
self.verticalLayout.setContentsMargins(-1, -1, -1, 5)
|
self.verticalLayout.setContentsMargins(-1, -1, -1, 5)
|
||||||
self.verticalLayout.setObjectName("verticalLayout")
|
self.editorWidget = QWidget(editorDialog)
|
||||||
self.editorWidget = QtWidgets.QWidget(editorDialog)
|
self.editorWidget.setObjectName(u"editorWidget")
|
||||||
self.editorWidget.setObjectName("editorWidget")
|
self.gridLayout = QGridLayout(self.editorWidget)
|
||||||
self.gridLayout = QtWidgets.QGridLayout(self.editorWidget)
|
self.gridLayout.setObjectName(u"gridLayout")
|
||||||
self.gridLayout.setContentsMargins(0, 0, 0, 0)
|
self.gridLayout.setContentsMargins(0, 0, 0, 0)
|
||||||
self.gridLayout.setObjectName("gridLayout")
|
self.label_1 = QLabel(self.editorWidget)
|
||||||
self.label_1 = QtWidgets.QLabel(self.editorWidget)
|
self.label_1.setObjectName(u"label_1")
|
||||||
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.setObjectName("seriesLine")
|
self.seriesLine = QLineEdit(self.editorWidget)
|
||||||
|
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.setObjectName("label_2")
|
self.label_2 = QLabel(self.editorWidget)
|
||||||
|
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.setObjectName("volumeLine")
|
self.volumeLine = QLineEdit(self.editorWidget)
|
||||||
|
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.setObjectName("label_3")
|
self.label_3 = QLabel(self.editorWidget)
|
||||||
|
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.setObjectName("numberLine")
|
self.numberLine = QLineEdit(self.editorWidget)
|
||||||
|
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.setObjectName("label_4")
|
self.label_4 = QLabel(self.editorWidget)
|
||||||
|
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.setObjectName("writerLine")
|
self.writerLine = QLineEdit(self.editorWidget)
|
||||||
|
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.setObjectName("label_5")
|
self.label_5 = QLabel(self.editorWidget)
|
||||||
|
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.setObjectName("pencillerLine")
|
self.pencillerLine = QLineEdit(self.editorWidget)
|
||||||
|
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.setObjectName("label_6")
|
self.label_6 = QLabel(self.editorWidget)
|
||||||
|
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.setObjectName("inkerLine")
|
self.inkerLine = QLineEdit(self.editorWidget)
|
||||||
|
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.setObjectName("label_7")
|
self.label_7 = QLabel(self.editorWidget)
|
||||||
|
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.setObjectName("coloristLine")
|
self.coloristLine = QLineEdit(self.editorWidget)
|
||||||
|
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.setObjectName("optionWidget")
|
self.optionWidget = QWidget(editorDialog)
|
||||||
self.horizontalLayout = QtWidgets.QHBoxLayout(self.optionWidget)
|
self.optionWidget.setObjectName(u"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.horizontalLayout.setObjectName("horizontalLayout")
|
self.statusLabel = QLabel(self.optionWidget)
|
||||||
self.statusLabel = QtWidgets.QLabel(self.optionWidget)
|
self.statusLabel.setObjectName(u"statusLabel")
|
||||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding)
|
sizePolicy = QSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding)
|
||||||
sizePolicy.setHorizontalStretch(0)
|
sizePolicy.setHorizontalStretch(0)
|
||||||
sizePolicy.setVerticalStretch(0)
|
sizePolicy.setVerticalStretch(0)
|
||||||
sizePolicy.setHeightForWidth(self.statusLabel.sizePolicy().hasHeightForWidth())
|
sizePolicy.setHeightForWidth(self.statusLabel.sizePolicy().hasHeightForWidth())
|
||||||
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.setMinimumSize(QtCore.QSize(0, 30))
|
self.okButton = QPushButton(self.optionWidget)
|
||||||
icon1 = QtGui.QIcon()
|
self.okButton.setObjectName(u"okButton")
|
||||||
icon1.addPixmap(QtGui.QPixmap(":/Other/icons/convert.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
self.okButton.setMinimumSize(QSize(0, 30))
|
||||||
|
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.setMinimumSize(QtCore.QSize(0, 30))
|
self.cancelButton = QPushButton(self.optionWidget)
|
||||||
icon2 = QtGui.QIcon()
|
self.cancelButton.setObjectName(u"cancelButton")
|
||||||
icon2.addPixmap(QtGui.QPixmap(":/Other/icons/clear.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
self.cancelButton.setMinimumSize(QSize(0, 30))
|
||||||
|
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):
|
||||||
_translate = QtCore.QCoreApplication.translate
|
editorDialog.setWindowTitle(QCoreApplication.translate("editorDialog", u"Metadata editor", None))
|
||||||
editorDialog.setWindowTitle(_translate("editorDialog", "Metadata editor"))
|
self.label_1.setText(QCoreApplication.translate("editorDialog", u"Series:", None))
|
||||||
self.label_1.setText(_translate("editorDialog", "Series:"))
|
self.label_2.setText(QCoreApplication.translate("editorDialog", u"Volume:", None))
|
||||||
self.label_2.setText(_translate("editorDialog", "Volume:"))
|
self.label_3.setText(QCoreApplication.translate("editorDialog", u"Number:", None))
|
||||||
self.label_3.setText(_translate("editorDialog", "Number:"))
|
self.label_4.setText(QCoreApplication.translate("editorDialog", u"Writer:", None))
|
||||||
self.label_4.setText(_translate("editorDialog", "Writer:"))
|
self.label_5.setText(QCoreApplication.translate("editorDialog", u"Penciller:", None))
|
||||||
self.label_5.setText(_translate("editorDialog", "Penciller:"))
|
self.label_6.setText(QCoreApplication.translate("editorDialog", u"Inker:", None))
|
||||||
self.label_6.setText(_translate("editorDialog", "Inker:"))
|
self.label_7.setText(QCoreApplication.translate("editorDialog", u"Colorist:", None))
|
||||||
self.label_7.setText(_translate("editorDialog", "Colorist:"))
|
self.statusLabel.setText("")
|
||||||
self.okButton.setText(_translate("editorDialog", "Save"))
|
self.okButton.setText(QCoreApplication.translate("editorDialog", u"Save", None))
|
||||||
self.cancelButton.setText(_translate("editorDialog", "Cancel"))
|
self.cancelButton.setText(QCoreApplication.translate("editorDialog", u"Cancel", None))
|
||||||
from . import KCC_rc
|
# retranslateUi
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
__version__ = '5.6.2'
|
__version__ = '5.6.3'
|
||||||
__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'
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
from argparse import ArgumentParser
|
||||||
from time import strftime, gmtime
|
from time import strftime, gmtime
|
||||||
from copy import copy
|
from copy import copy
|
||||||
from glob import glob, escape
|
from glob import glob, escape
|
||||||
@@ -28,18 +29,13 @@ from stat import S_IWRITE, S_IREAD, S_IEXEC
|
|||||||
from zipfile import ZipFile, ZIP_STORED, ZIP_DEFLATED
|
from zipfile import ZipFile, ZIP_STORED, ZIP_DEFLATED
|
||||||
from tempfile import mkdtemp, gettempdir, TemporaryFile
|
from tempfile import mkdtemp, gettempdir, TemporaryFile
|
||||||
from shutil import move, copytree, rmtree, copyfile
|
from shutil import move, copytree, rmtree, copyfile
|
||||||
from optparse import OptionParser, OptionGroup
|
|
||||||
from multiprocessing import Pool
|
from multiprocessing import Pool
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
from slugify import slugify as slugifyExt
|
from slugify import slugify as slugify_ext
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
from subprocess import STDOUT, PIPE
|
from subprocess import STDOUT, PIPE
|
||||||
from psutil import Popen, virtual_memory, disk_usage
|
from psutil import Popen, virtual_memory, disk_usage
|
||||||
from html import escape as hescape
|
from html import escape as hescape
|
||||||
try:
|
|
||||||
from PyQt5 import QtCore
|
|
||||||
except ImportError:
|
|
||||||
QtCore = None
|
|
||||||
from .shared import md5Checksum, getImageFileName, walkSort, walkLevel, sanitizeTrace
|
from .shared import md5Checksum, getImageFileName, walkSort, walkLevel, sanitizeTrace
|
||||||
from . import comic2panel
|
from . import comic2panel
|
||||||
from . import image
|
from . import image
|
||||||
@@ -54,23 +50,23 @@ from . import __version__
|
|||||||
def main(argv=None):
|
def main(argv=None):
|
||||||
global options
|
global options
|
||||||
parser = makeParser()
|
parser = makeParser()
|
||||||
optionstemplate, args = parser.parse_args(argv)
|
args = parser.parse_args(argv)
|
||||||
if len(args) == 0:
|
options = copy(args)
|
||||||
|
if not argv or options.input == []:
|
||||||
parser.print_help()
|
parser.print_help()
|
||||||
return 0
|
return 0
|
||||||
if sys.platform.startswith('win'):
|
if sys.platform.startswith('win'):
|
||||||
sources = set([source for arg in args for source in glob(escape(arg))])
|
sources = set([source for option in options.input for source in glob(escape(option))])
|
||||||
else:
|
else:
|
||||||
sources = set(args)
|
sources = set(options.input)
|
||||||
if len(sources) == 0:
|
if len(sources) == 0:
|
||||||
print('No matching files found.')
|
print('No matching files found.')
|
||||||
return 1
|
return 1
|
||||||
for source in sources:
|
for source in sources:
|
||||||
source = source.rstrip('\\').rstrip('/')
|
source = source.rstrip('\\').rstrip('/')
|
||||||
options = copy(optionstemplate)
|
options = copy(args)
|
||||||
options = checkOptions(options)
|
options = checkOptions(options)
|
||||||
if len(sources) > 1:
|
print('Working on ' + source + '...')
|
||||||
print('Working on ' + source + '...')
|
|
||||||
makeBook(source)
|
makeBook(source)
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
@@ -295,13 +291,14 @@ def buildOPF(dstdir, title, filelist, cover=None):
|
|||||||
"<meta name=\"zero-gutter\" content=\"true\"/>\n",
|
"<meta name=\"zero-gutter\" content=\"true\"/>\n",
|
||||||
"<meta name=\"zero-margin\" content=\"true\"/>\n",
|
"<meta name=\"zero-margin\" content=\"true\"/>\n",
|
||||||
"<meta name=\"ke-border-color\" content=\"#FFFFFF\"/>\n",
|
"<meta name=\"ke-border-color\" content=\"#FFFFFF\"/>\n",
|
||||||
"<meta name=\"ke-border-width\" content=\"0\"/>\n"])
|
"<meta name=\"ke-border-width\" content=\"0\"/>\n",
|
||||||
|
"<meta property=\"rendition:spread\">landscape</meta>\n",
|
||||||
|
"<meta property=\"rendition:layout\">pre-paginated</meta>\n",
|
||||||
|
"<meta name=\"orientation-lock\" content=\"none\"/>\n"])
|
||||||
if options.kfx:
|
if options.kfx:
|
||||||
f.writelines(["<meta name=\"orientation-lock\" content=\"none\"/>\n",
|
f.writelines(["<meta name=\"region-mag\" content=\"false\"/>\n"])
|
||||||
"<meta name=\"region-mag\" content=\"false\"/>\n"])
|
|
||||||
else:
|
else:
|
||||||
f.writelines(["<meta name=\"orientation-lock\" content=\"portrait\"/>\n",
|
f.writelines(["<meta name=\"region-mag\" content=\"true\"/>\n"])
|
||||||
"<meta name=\"region-mag\" content=\"true\"/>\n"])
|
|
||||||
elif options.supportSyntheticSpread:
|
elif options.supportSyntheticSpread:
|
||||||
f.writelines([
|
f.writelines([
|
||||||
"<meta property=\"rendition:spread\">landscape</meta>\n",
|
"<meta property=\"rendition:spread\">landscape</meta>\n",
|
||||||
@@ -513,18 +510,30 @@ def buildEPUB(path, chapternames, tomenumber):
|
|||||||
# 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 = []
|
||||||
globaldiff = 0
|
|
||||||
|
global_diff = 0
|
||||||
|
diff_delta = 0
|
||||||
|
|
||||||
|
# if split
|
||||||
|
if options.splitter == 0:
|
||||||
|
diff_delta = 1
|
||||||
|
# if rotate and split
|
||||||
|
elif options.splitter == 2:
|
||||||
|
diff_delta = 2
|
||||||
|
|
||||||
for aChapter in options.chapters:
|
for aChapter in options.chapters:
|
||||||
pageid = aChapter[0]
|
pageid = aChapter[0]
|
||||||
for x in range(0, pageid + globaldiff + 1):
|
cur_diff = global_diff
|
||||||
|
global_diff = 0
|
||||||
|
|
||||||
|
for x in range(0, pageid + cur_diff + 1):
|
||||||
if '-kcc-b' in filelist[x][1]:
|
if '-kcc-b' in filelist[x][1]:
|
||||||
pageid += 1
|
pageid += diff_delta
|
||||||
if '-kcc-c' in filelist[pageid][1]:
|
global_diff += diff_delta
|
||||||
pageid -= 1
|
|
||||||
filename = filelist[pageid][1]
|
filename = filelist[pageid][1]
|
||||||
chapterlist.append((filelist[pageid][0].replace('Images', 'Text'), filename))
|
chapterlist.append((filelist[pageid][0].replace('Images', 'Text'), filename))
|
||||||
chapternames[filename] = aChapter[1]
|
chapternames[filename] = aChapter[1]
|
||||||
globaldiff = pageid - (aChapter[0] + globaldiff)
|
|
||||||
buildNCX(path, options.title, chapterlist, chapternames)
|
buildNCX(path, options.title, chapterlist, chapternames)
|
||||||
buildNAV(path, options.title, chapterlist, chapternames)
|
buildNAV(path, options.title, chapterlist, chapternames)
|
||||||
buildOPF(path, options.title, filelist, cover)
|
buildOPF(path, options.title, filelist, cover)
|
||||||
@@ -546,7 +555,7 @@ def imgDirectoryProcessing(path):
|
|||||||
GUI.progressBarTick.emit(str(pagenumber))
|
GUI.progressBarTick.emit(str(pagenumber))
|
||||||
if len(work) > 0:
|
if len(work) > 0:
|
||||||
for i in work:
|
for i in work:
|
||||||
workerPool.apply_async(func=imgFileProcessing, args=(i, ), callback=imgFileProcessingTick)
|
workerPool.apply_async(func=imgFileProcessing, args=(i,), callback=imgFileProcessingTick)
|
||||||
workerPool.close()
|
workerPool.close()
|
||||||
workerPool.join()
|
workerPool.join()
|
||||||
if GUI and not GUI.conversionAlive:
|
if GUI and not GUI.conversionAlive:
|
||||||
@@ -631,7 +640,7 @@ def getWorkFolder(afile):
|
|||||||
path = cbx.extract(workdir)
|
path = cbx.extract(workdir)
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
rmtree(workdir, True)
|
rmtree(workdir, True)
|
||||||
raise UserWarning(e.strerror)
|
raise UserWarning(e)
|
||||||
else:
|
else:
|
||||||
raise UserWarning("Failed to open source file/directory.")
|
raise UserWarning("Failed to open source file/directory.")
|
||||||
sanitizePermissions(path)
|
sanitizePermissions(path)
|
||||||
@@ -695,7 +704,9 @@ def getComicInfo(path, originalpath):
|
|||||||
os.remove(xmlPath)
|
os.remove(xmlPath)
|
||||||
return
|
return
|
||||||
options.authors = []
|
options.authors = []
|
||||||
if defaultTitle:
|
if xml.data['Title']:
|
||||||
|
options.title = hescape(xml.data['Title'])
|
||||||
|
elif defaultTitle:
|
||||||
if xml.data['Series']:
|
if xml.data['Series']:
|
||||||
options.title = hescape(xml.data['Series'])
|
options.title = hescape(xml.data['Series'])
|
||||||
if xml.data['Volume']:
|
if xml.data['Volume']:
|
||||||
@@ -746,19 +757,23 @@ def getPanelViewSize(deviceres, size):
|
|||||||
def sanitizeTree(filetree):
|
def sanitizeTree(filetree):
|
||||||
chapterNames = {}
|
chapterNames = {}
|
||||||
for root, dirs, files in os.walk(filetree, False):
|
for root, dirs, files in os.walk(filetree, False):
|
||||||
for name in files:
|
for i, name in enumerate(sorted(files)):
|
||||||
splitname = os.path.splitext(name)
|
splitname = os.path.splitext(name)
|
||||||
slugified = slugify(splitname[0], False)
|
|
||||||
while os.path.exists(os.path.join(root, slugified + splitname[1])) and splitname[0].upper()\
|
# file needs kcc at front AND back to avoid renaming issues
|
||||||
!= slugified.upper():
|
slugified = f'kcc-{i:04}'
|
||||||
slugified += "A"
|
for suffix in '-KCC', '-KCC-A', '-KCC-B', '-KCC-C':
|
||||||
|
if splitname[0].endswith(suffix):
|
||||||
|
slugified += suffix.lower()
|
||||||
|
break
|
||||||
|
|
||||||
newKey = os.path.join(root, slugified + splitname[1])
|
newKey = os.path.join(root, slugified + splitname[1])
|
||||||
key = os.path.join(root, name)
|
key = os.path.join(root, name)
|
||||||
if key != newKey:
|
if key != newKey:
|
||||||
os.replace(key, newKey)
|
os.replace(key, newKey)
|
||||||
for name in dirs:
|
for name in dirs:
|
||||||
tmpName = name
|
tmpName = name
|
||||||
slugified = slugify(name, True)
|
slugified = slugify(name)
|
||||||
while os.path.exists(os.path.join(root, slugified)) and name.upper() != slugified.upper():
|
while os.path.exists(os.path.join(root, slugified)) and name.upper() != slugified.upper():
|
||||||
slugified += "A"
|
slugified += "A"
|
||||||
chapterNames[slugified] = tmpName
|
chapterNames[slugified] = tmpName
|
||||||
@@ -769,23 +784,6 @@ def sanitizeTree(filetree):
|
|||||||
return chapterNames
|
return chapterNames
|
||||||
|
|
||||||
|
|
||||||
def sanitizeTreeKobo(filetree):
|
|
||||||
pageNumber = 0
|
|
||||||
for root, dirs, files in os.walk(filetree):
|
|
||||||
dirs, files = walkSort(dirs, files)
|
|
||||||
for name in files:
|
|
||||||
splitname = os.path.splitext(name)
|
|
||||||
slugified = str(pageNumber).zfill(5)
|
|
||||||
pageNumber += 1
|
|
||||||
while os.path.exists(os.path.join(root, slugified + splitname[1])) and splitname[0].upper()\
|
|
||||||
!= slugified.upper():
|
|
||||||
slugified += "A"
|
|
||||||
newKey = os.path.join(root, slugified + splitname[1])
|
|
||||||
key = os.path.join(root, name)
|
|
||||||
if key != newKey:
|
|
||||||
os.replace(key, newKey)
|
|
||||||
|
|
||||||
|
|
||||||
def sanitizePermissions(filetree):
|
def sanitizePermissions(filetree):
|
||||||
for root, dirs, files in os.walk(filetree, False):
|
for root, dirs, files in os.walk(filetree, False):
|
||||||
for name in files:
|
for name in files:
|
||||||
@@ -908,11 +906,8 @@ def createNewTome():
|
|||||||
return tomePath, tomePathRoot
|
return tomePath, tomePathRoot
|
||||||
|
|
||||||
|
|
||||||
def slugify(value, isdir):
|
def slugify(value):
|
||||||
if isdir:
|
value = slugify_ext(value, regex_pattern=r'[^-a-z0-9_\.]+').strip('.')
|
||||||
value = slugifyExt(value, regex_pattern=r'[^-a-z0-9_\.]+').strip('.')
|
|
||||||
else:
|
|
||||||
value = slugifyExt(value).strip('.')
|
|
||||||
value = sub(r'0*([0-9]{4,})', r'\1', sub(r'([0-9]+)', r'0000\1', value, count=2))
|
value = sub(r'0*([0-9]{4,})', r'\1', sub(r'([0-9]+)', r'0000\1', value, count=2))
|
||||||
return value
|
return value
|
||||||
|
|
||||||
@@ -933,85 +928,84 @@ def makeZIP(zipfilename, basedir, isepub=False):
|
|||||||
|
|
||||||
|
|
||||||
def makeParser():
|
def makeParser():
|
||||||
psr = OptionParser(usage="Usage: kcc-c2e [options] comic_file|comic_folder", add_help_option=False)
|
psr = ArgumentParser(prog="kcc-c2e", usage="kcc-c2e [options] [input]", add_help=False)
|
||||||
|
|
||||||
mainOptions = OptionGroup(psr, "MAIN")
|
mandatory_options = psr.add_argument_group("MANDATORY")
|
||||||
processingOptions = OptionGroup(psr, "PROCESSING")
|
main_options = psr.add_argument_group("MAIN")
|
||||||
outputOptions = OptionGroup(psr, "OUTPUT SETTINGS")
|
processing_options = psr.add_argument_group("PROCESSING")
|
||||||
customProfileOptions = OptionGroup(psr, "CUSTOM PROFILE")
|
output_options = psr.add_argument_group("OUTPUT SETTINGS")
|
||||||
otherOptions = OptionGroup(psr, "OTHER")
|
custom_profile_options = psr.add_argument_group("CUSTOM PROFILE")
|
||||||
|
other_options = psr.add_argument_group("OTHER")
|
||||||
|
|
||||||
mainOptions.add_option("-p", "--profile", action="store", dest="profile", default="KV",
|
mandatory_options.add_argument("input", action="extend", nargs="*", default=None,
|
||||||
help="Device profile (Available options: K1, K2, K34, K578, KDX, KPW, KPW5, KV, KO, "
|
help="Full path to comic folder or file(s) to be processed.")
|
||||||
"K11, KS, KoMT, KoG, KoGHD, KoA, KoAHD, KoAH2O, KoAO, KoN, KoC, KoL, KoF, KoS, KoE)"
|
|
||||||
" [Default=KV]")
|
|
||||||
mainOptions.add_option("-m", "--manga-style", action="store_true", dest="righttoleft", default=False,
|
|
||||||
help="Manga style (right-to-left reading and splitting)")
|
|
||||||
mainOptions.add_option("-q", "--hq", action="store_true", dest="hq", default=False,
|
|
||||||
help="Try to increase the quality of magnification")
|
|
||||||
mainOptions.add_option("-2", "--two-panel", action="store_true", dest="autoscale", default=False,
|
|
||||||
help="Display two not four panels in Panel View mode")
|
|
||||||
mainOptions.add_option("-w", "--webtoon", action="store_true", dest="webtoon", default=False,
|
|
||||||
help="Webtoon processing mode"),
|
|
||||||
mainOptions.add_option("--targetsize", type="int", dest="targetsize", default=None,
|
|
||||||
help="the maximal size of output file in MB."
|
|
||||||
" [Default=100MB for webtoon and 400MB for others]")
|
|
||||||
|
|
||||||
outputOptions.add_option("-o", "--output", action="store", dest="output", default=None,
|
main_options.add_argument("-p", "--profile", action="store", dest="profile", default="KV",
|
||||||
help="Output generated file to specified directory or file")
|
help="Device profile (Available options: K1, K2, K34, K578, KDX, KPW, KPW5, KV, KO, "
|
||||||
outputOptions.add_option("-t", "--title", action="store", dest="title", default="defaulttitle",
|
"K11, KS, KoMT, KoG, KoGHD, KoA, KoAHD, KoAH2O, KoAO, KoN, KoC, KoL, KoF, KoS, KoE)"
|
||||||
help="Comic title [Default=filename or directory name]")
|
" [Default=KV]")
|
||||||
outputOptions.add_option("-f", "--format", action="store", dest="format", default="Auto",
|
main_options.add_argument("-m", "--manga-style", action="store_true", dest="righttoleft", default=False,
|
||||||
help="Output format (Available options: Auto, MOBI, EPUB, CBZ, KFX, MOBI+EPUB) "
|
help="Manga style (right-to-left reading and splitting)")
|
||||||
"[Default=Auto]")
|
main_options.add_argument("-q", "--hq", action="store_true", dest="hq", default=False,
|
||||||
outputOptions.add_option("-b", "--batchsplit", type="int", dest="batchsplit", default="0",
|
help="Try to increase the quality of magnification")
|
||||||
help="Split output into multiple files. 0: Don't split 1: Automatic mode "
|
main_options.add_argument("-2", "--two-panel", action="store_true", dest="autoscale", default=False,
|
||||||
"2: Consider every subdirectory as separate volume [Default=0]")
|
help="Display two not four panels in Panel View mode")
|
||||||
|
main_options.add_argument("-w", "--webtoon", action="store_true", dest="webtoon", default=False,
|
||||||
|
help="Webtoon processing mode"),
|
||||||
|
main_options.add_argument("--ts", "--targetsize", type=int, dest="targetsize", default=None,
|
||||||
|
help="the maximal size of output file in MB."
|
||||||
|
" [Default=100MB for webtoon and 400MB for others]")
|
||||||
|
|
||||||
processingOptions.add_option("-n", "--noprocessing", action="store_true", dest="noprocessing", default=False,
|
output_options.add_argument("-o", "--output", action="store", dest="output", default=None,
|
||||||
help="Do not modify image and ignore any profil or processing option")
|
help="Output generated file to specified directory or file")
|
||||||
processingOptions.add_option("-u", "--upscale", action="store_true", dest="upscale", default=False,
|
output_options.add_argument("-t", "--title", action="store", dest="title", default="defaulttitle",
|
||||||
help="Resize images smaller than device's resolution")
|
help="Comic title [Default=filename or directory name]")
|
||||||
processingOptions.add_option("-s", "--stretch", action="store_true", dest="stretch", default=False,
|
output_options.add_argument("-f", "--format", action="store", dest="format", default="Auto",
|
||||||
help="Stretch images to device's resolution")
|
help="Output format (Available options: Auto, MOBI, EPUB, CBZ, KFX, MOBI+EPUB) "
|
||||||
processingOptions.add_option("-r", "--splitter", type="int", dest="splitter", default="0",
|
"[Default=Auto]")
|
||||||
help="Double page parsing mode. 0: Split 1: Rotate 2: Both [Default=0]")
|
output_options.add_argument("-b", "--batchsplit", type=int, dest="batchsplit", default="0",
|
||||||
processingOptions.add_option("-g", "--gamma", type="float", dest="gamma", default="0.0",
|
help="Split output into multiple files. 0: Don't split 1: Automatic mode "
|
||||||
help="Apply gamma correction to linearize the image [Default=Auto]")
|
"2: Consider every subdirectory as separate volume [Default=0]")
|
||||||
processingOptions.add_option("-c", "--cropping", type="int", dest="cropping", default="2",
|
|
||||||
help="Set cropping mode. 0: Disabled 1: Margins 2: Margins + page numbers [Default=2]")
|
|
||||||
processingOptions.add_option("--cp", "--croppingpower", type="float", dest="croppingp", default="1.0",
|
|
||||||
help="Set cropping power [Default=1.0]")
|
|
||||||
processingOptions.add_option("--cm", "--croppingminimum", type="float", dest="croppingm", default="0.0",
|
|
||||||
help="Set cropping minimum area ratio [Default=0.0]")
|
|
||||||
processingOptions.add_option("--blackborders", action="store_true", dest="black_borders", default=False,
|
|
||||||
help="Disable autodetection and force black borders")
|
|
||||||
processingOptions.add_option("--whiteborders", action="store_true", dest="white_borders", default=False,
|
|
||||||
help="Disable autodetection and force white borders")
|
|
||||||
processingOptions.add_option("--forcecolor", action="store_true", dest="forcecolor", default=False,
|
|
||||||
help="Don't convert images to grayscale")
|
|
||||||
processingOptions.add_option("--forcepng", action="store_true", dest="forcepng", default=False,
|
|
||||||
help="Create PNG files instead JPEG")
|
|
||||||
processingOptions.add_option("--mozjpeg", action="store_true", dest="mozjpeg", default=False,
|
|
||||||
help="Create JPEG files using mozJpeg")
|
|
||||||
processingOptions.add_option("--maximizestrips", action="store_true", dest="maximizestrips", default=False,
|
|
||||||
help="Turn 1x4 strips to 2x2 strips")
|
|
||||||
processingOptions.add_option("-d", "--delete", action="store_true", dest="delete", default=False,
|
|
||||||
help="Delete source file(s) or a directory. It's not recoverable.")
|
|
||||||
|
|
||||||
customProfileOptions.add_option("--customwidth", type="int", dest="customwidth", default=0,
|
processing_options.add_argument("-n", "--noprocessing", action="store_true", dest="noprocessing", default=False,
|
||||||
help="Replace screen width provided by device profile")
|
help="Do not modify image and ignore any profil or processing option")
|
||||||
customProfileOptions.add_option("--customheight", type="int", dest="customheight", default=0,
|
processing_options.add_argument("-u", "--upscale", action="store_true", dest="upscale", default=False,
|
||||||
help="Replace screen height provided by device profile")
|
help="Resize images smaller than device's resolution")
|
||||||
|
processing_options.add_argument("-s", "--stretch", action="store_true", dest="stretch", default=False,
|
||||||
|
help="Stretch images to device's resolution")
|
||||||
|
processing_options.add_argument("-r", "--splitter", type=int, dest="splitter", default="0",
|
||||||
|
help="Double page parsing mode. 0: Split 1: Rotate 2: Both [Default=0]")
|
||||||
|
processing_options.add_argument("-g", "--gamma", type=float, dest="gamma", default="0.0",
|
||||||
|
help="Apply gamma correction to linearize the image [Default=Auto]")
|
||||||
|
processing_options.add_argument("-c", "--cropping", type=int, dest="cropping", default="2",
|
||||||
|
help="Set cropping mode. 0: Disabled 1: Margins 2: Margins + page numbers [Default=2]")
|
||||||
|
processing_options.add_argument("--cp", "--croppingpower", type=float, dest="croppingp", default="1.0",
|
||||||
|
help="Set cropping power [Default=1.0]")
|
||||||
|
processing_options.add_argument("--cm", "--croppingminimum", type=float, dest="croppingm", default="0.0",
|
||||||
|
help="Set cropping minimum area ratio [Default=0.0]")
|
||||||
|
processing_options.add_argument("--blackborders", action="store_true", dest="black_borders", default=False,
|
||||||
|
help="Disable autodetection and force black borders")
|
||||||
|
processing_options.add_argument("--whiteborders", action="store_true", dest="white_borders", default=False,
|
||||||
|
help="Disable autodetection and force white borders")
|
||||||
|
processing_options.add_argument("--forcecolor", action="store_true", dest="forcecolor", default=False,
|
||||||
|
help="Don't convert images to grayscale")
|
||||||
|
processing_options.add_argument("--forcepng", action="store_true", dest="forcepng", default=False,
|
||||||
|
help="Create PNG files instead JPEG")
|
||||||
|
processing_options.add_argument("--mozjpeg", action="store_true", dest="mozjpeg", default=False,
|
||||||
|
help="Create JPEG files using mozJpeg")
|
||||||
|
processing_options.add_argument("--maximizestrips", action="store_true", dest="maximizestrips", default=False,
|
||||||
|
help="Turn 1x4 strips to 2x2 strips")
|
||||||
|
processing_options.add_argument("-d", "--delete", action="store_true", dest="delete", default=False,
|
||||||
|
help="Delete source file(s) or a directory. It's not recoverable.")
|
||||||
|
|
||||||
otherOptions.add_option("-h", "--help", action="help",
|
custom_profile_options.add_argument("--customwidth", type=int, dest="customwidth", default=0,
|
||||||
help="Show this help message and exit")
|
help="Replace screen width provided by device profile")
|
||||||
|
custom_profile_options.add_argument("--customheight", type=int, dest="customheight", default=0,
|
||||||
|
help="Replace screen height provided by device profile")
|
||||||
|
|
||||||
|
other_options.add_argument("-h", "--help", action="help",
|
||||||
|
help="Show this help message and exit")
|
||||||
|
|
||||||
psr.add_option_group(mainOptions)
|
|
||||||
psr.add_option_group(outputOptions)
|
|
||||||
psr.add_option_group(processingOptions)
|
|
||||||
psr.add_option_group(customProfileOptions)
|
|
||||||
psr.add_option_group(otherOptions)
|
|
||||||
return psr
|
return psr
|
||||||
|
|
||||||
|
|
||||||
@@ -1160,8 +1154,6 @@ def makeBook(source, qtgui=None):
|
|||||||
if GUI:
|
if GUI:
|
||||||
GUI.progressBarTick.emit('1')
|
GUI.progressBarTick.emit('1')
|
||||||
chapterNames = sanitizeTree(os.path.join(path, 'OEBPS', 'Images'))
|
chapterNames = sanitizeTree(os.path.join(path, 'OEBPS', 'Images'))
|
||||||
if 'Ko' in options.profile and options.format == 'CBZ':
|
|
||||||
sanitizeTreeKobo(os.path.join(path, 'OEBPS', 'Images'))
|
|
||||||
if options.batchsplit > 0:
|
if options.batchsplit > 0:
|
||||||
tomes = splitDirectory(path)
|
tomes = splitDirectory(path)
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -20,15 +20,11 @@
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
from argparse import ArgumentParser
|
||||||
from shutil import rmtree, copytree, move
|
from shutil import rmtree, copytree, move
|
||||||
from optparse import OptionParser, OptionGroup
|
|
||||||
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):
|
||||||
@@ -102,7 +98,7 @@ def splitImage(work):
|
|||||||
opt = work[2]
|
opt = work[2]
|
||||||
filePath = os.path.join(path, name)
|
filePath = os.path.join(path, name)
|
||||||
Image.warnings.simplefilter('error', Image.DecompressionBombWarning)
|
Image.warnings.simplefilter('error', Image.DecompressionBombWarning)
|
||||||
Image.MAX_IMAGE_PIXELS = 1000000000
|
Image.MAX_IMAGE_PIXELS = 1000000000
|
||||||
imgOrg = Image.open(filePath).convert('RGB')
|
imgOrg = Image.open(filePath).convert('RGB')
|
||||||
imgProcess = Image.open(filePath).convert('1')
|
imgProcess = Image.open(filePath).convert('1')
|
||||||
widthImg, heightImg = imgOrg.size
|
widthImg, heightImg = imgOrg.size
|
||||||
@@ -116,7 +112,7 @@ def splitImage(work):
|
|||||||
panelDetected = False
|
panelDetected = False
|
||||||
panels = []
|
panels = []
|
||||||
while yWork < heightImg:
|
while yWork < heightImg:
|
||||||
tmpImg = imgProcess.crop([4, yWork, widthImg-4, yWork + 4])
|
tmpImg = imgProcess.crop((4, yWork, widthImg-4, yWork + 4))
|
||||||
solid = detectSolid(tmpImg)
|
solid = detectSolid(tmpImg)
|
||||||
if not solid and not panelDetected:
|
if not solid and not panelDetected:
|
||||||
panelDetected = True
|
panelDetected = True
|
||||||
@@ -149,7 +145,7 @@ def splitImage(work):
|
|||||||
|
|
||||||
if opt.debug:
|
if opt.debug:
|
||||||
for panel in panelsProcessed:
|
for panel in panelsProcessed:
|
||||||
draw.rectangle([(0, panel[0]), (widthImg, panel[1])], (0, 255, 0, 128), (0, 0, 255, 255))
|
draw.rectangle(((0, panel[0]), (widthImg, panel[1])), (0, 255, 0, 128), (0, 0, 255, 255))
|
||||||
debugImage = Image.alpha_composite(imgOrg.convert(mode='RGBA'), drawImg)
|
debugImage = Image.alpha_composite(imgOrg.convert(mode='RGBA'), drawImg)
|
||||||
debugImage.save(os.path.join(path, os.path.splitext(name)[0] + '-debug.png'), 'PNG')
|
debugImage.save(os.path.join(path, os.path.splitext(name)[0] + '-debug.png'), 'PNG')
|
||||||
|
|
||||||
@@ -182,7 +178,7 @@ def splitImage(work):
|
|||||||
if pageHeight > 15:
|
if pageHeight > 15:
|
||||||
newPage = Image.new('RGB', (widthImg, pageHeight))
|
newPage = Image.new('RGB', (widthImg, pageHeight))
|
||||||
for panel in page:
|
for panel in page:
|
||||||
panelImg = imgOrg.crop([0, panelsProcessed[panel][0], widthImg, panelsProcessed[panel][1]])
|
panelImg = imgOrg.crop((0, panelsProcessed[panel][0], widthImg, panelsProcessed[panel][1]))
|
||||||
newPage.paste(panelImg, (0, targetHeight))
|
newPage.paste(panelImg, (0, targetHeight))
|
||||||
targetHeight += panelsProcessed[panel][2]
|
targetHeight += panelsProcessed[panel][2]
|
||||||
newPage.save(os.path.join(path, os.path.splitext(name)[0] + '-' + str(pageNumber) + '.png'), 'PNG')
|
newPage.save(os.path.join(path, os.path.splitext(name)[0] + '-' + str(pageNumber) + '.png'), 'PNG')
|
||||||
@@ -193,97 +189,100 @@ def splitImage(work):
|
|||||||
|
|
||||||
|
|
||||||
def main(argv=None, qtgui=None):
|
def main(argv=None, qtgui=None):
|
||||||
global options, GUI, splitWorkerPool, splitWorkerOutput, mergeWorkerPool, mergeWorkerOutput
|
global args, GUI, splitWorkerPool, splitWorkerOutput, mergeWorkerPool, mergeWorkerOutput
|
||||||
parser = OptionParser(usage="Usage: kcc-c2p [options] comic_folder", add_help_option=False)
|
parser = ArgumentParser(prog="kcc-c2p", usage="kcc-c2p [options] [input]", add_help=False)
|
||||||
mainOptions = OptionGroup(parser, "MANDATORY")
|
|
||||||
otherOptions = OptionGroup(parser, "OTHER")
|
mandatory_options = parser.add_argument_group("MANDATORY")
|
||||||
mainOptions.add_option("-y", "--height", type="int", dest="height", default=0,
|
main_options = parser.add_argument_group("MAIN")
|
||||||
help="Height of the target device screen")
|
other_options = parser.add_argument_group("OTHER")
|
||||||
mainOptions.add_option("-i", "--in-place", action="store_true", dest="inPlace", default=False,
|
mandatory_options.add_argument("input", action="extend", nargs="*", default=None,
|
||||||
help="Overwrite source directory")
|
help="Full path to comic folder(s) to be processed. Separate multiple inputs"
|
||||||
mainOptions.add_option("-m", "--merge", action="store_true", dest="merge", default=False,
|
" with spaces.")
|
||||||
help="Combine every directory into a single image before splitting")
|
main_options.add_argument("-y", "--height", type=int, dest="height", default=0,
|
||||||
otherOptions.add_option("-d", "--debug", action="store_true", dest="debug", default=False,
|
help="Height of the target device screen")
|
||||||
help="Create debug file for every split image")
|
main_options.add_argument("-i", "--in-place", action="store_true", dest="inPlace", default=False,
|
||||||
otherOptions.add_option("-h", "--help", action="help",
|
help="Overwrite source directory")
|
||||||
help="Show this help message and exit")
|
main_options.add_argument("-m", "--merge", action="store_true", dest="merge", default=False,
|
||||||
parser.add_option_group(mainOptions)
|
help="Combine every directory into a single image before splitting")
|
||||||
parser.add_option_group(otherOptions)
|
other_options.add_argument("-d", "--debug", action="store_true", dest="debug", default=False,
|
||||||
options, args = parser.parse_args(argv)
|
help="Create debug file for every split image")
|
||||||
|
other_options.add_argument("-h", "--help", action="help",
|
||||||
|
help="Show this help message and exit")
|
||||||
|
args = parser.parse_args(argv)
|
||||||
if qtgui:
|
if qtgui:
|
||||||
GUI = qtgui
|
GUI = qtgui
|
||||||
else:
|
else:
|
||||||
GUI = None
|
GUI = None
|
||||||
if len(args) != 1:
|
if not argv or args.input == []:
|
||||||
parser.print_help()
|
parser.print_help()
|
||||||
return 1
|
return 1
|
||||||
if options.height > 0:
|
if args.height > 0:
|
||||||
options.sourceDir = args[0]
|
for sourceDir in args.input:
|
||||||
options.targetDir = args[0] + "-Splitted"
|
targetDir = sourceDir + "-Splitted"
|
||||||
if os.path.isdir(options.sourceDir):
|
if os.path.isdir(sourceDir):
|
||||||
rmtree(options.targetDir, True)
|
rmtree(targetDir, True)
|
||||||
copytree(options.sourceDir, options.targetDir)
|
copytree(sourceDir, targetDir)
|
||||||
work = []
|
work = []
|
||||||
pagenumber = 1
|
pagenumber = 1
|
||||||
splitWorkerOutput = []
|
splitWorkerOutput = []
|
||||||
splitWorkerPool = Pool(maxtasksperchild=10)
|
splitWorkerPool = Pool(maxtasksperchild=10)
|
||||||
if options.merge:
|
if args.merge:
|
||||||
print("Merging images...")
|
print("Merging images...")
|
||||||
directoryNumer = 1
|
directoryNumer = 1
|
||||||
mergeWork = []
|
mergeWork = []
|
||||||
mergeWorkerOutput = []
|
mergeWorkerOutput = []
|
||||||
mergeWorkerPool = Pool(maxtasksperchild=10)
|
mergeWorkerPool = Pool(maxtasksperchild=10)
|
||||||
mergeWork.append([options.targetDir])
|
mergeWork.append([targetDir])
|
||||||
for root, dirs, files in os.walk(options.targetDir, False):
|
for root, dirs, files in os.walk(targetDir, False):
|
||||||
dirs, files = walkSort(dirs, files)
|
dirs, files = walkSort(dirs, files)
|
||||||
for directory in dirs:
|
for directory in dirs:
|
||||||
directoryNumer += 1
|
directoryNumer += 1
|
||||||
mergeWork.append([os.path.join(root, directory)])
|
mergeWork.append([os.path.join(root, directory)])
|
||||||
|
if GUI:
|
||||||
|
GUI.progressBarTick.emit('Combining images')
|
||||||
|
GUI.progressBarTick.emit(str(directoryNumer))
|
||||||
|
for i in mergeWork:
|
||||||
|
mergeWorkerPool.apply_async(func=mergeDirectory, args=(i, ), callback=mergeDirectoryTick)
|
||||||
|
mergeWorkerPool.close()
|
||||||
|
mergeWorkerPool.join()
|
||||||
|
if GUI and not GUI.conversionAlive:
|
||||||
|
rmtree(targetDir, True)
|
||||||
|
raise UserWarning("Conversion interrupted.")
|
||||||
|
if len(mergeWorkerOutput) > 0:
|
||||||
|
rmtree(targetDir, True)
|
||||||
|
raise RuntimeError("One of workers crashed. Cause: " + mergeWorkerOutput[0][0],
|
||||||
|
mergeWorkerOutput[0][1])
|
||||||
|
print("Splitting images...")
|
||||||
|
for root, _, files in os.walk(targetDir, False):
|
||||||
|
for name in files:
|
||||||
|
if getImageFileName(name) is not None:
|
||||||
|
pagenumber += 1
|
||||||
|
work.append([root, name, args])
|
||||||
|
else:
|
||||||
|
os.remove(os.path.join(root, name))
|
||||||
if GUI:
|
if GUI:
|
||||||
GUI.progressBarTick.emit('Combining images')
|
GUI.progressBarTick.emit('Splitting images')
|
||||||
GUI.progressBarTick.emit(str(directoryNumer))
|
GUI.progressBarTick.emit(str(pagenumber))
|
||||||
for i in mergeWork:
|
GUI.progressBarTick.emit('tick')
|
||||||
mergeWorkerPool.apply_async(func=mergeDirectory, args=(i, ), callback=mergeDirectoryTick)
|
if len(work) > 0:
|
||||||
mergeWorkerPool.close()
|
for i in work:
|
||||||
mergeWorkerPool.join()
|
splitWorkerPool.apply_async(func=splitImage, args=(i, ), callback=splitImageTick)
|
||||||
if GUI and not GUI.conversionAlive:
|
splitWorkerPool.close()
|
||||||
rmtree(options.targetDir, True)
|
splitWorkerPool.join()
|
||||||
raise UserWarning("Conversion interrupted.")
|
if GUI and not GUI.conversionAlive:
|
||||||
if len(mergeWorkerOutput) > 0:
|
rmtree(targetDir, True)
|
||||||
rmtree(options.targetDir, True)
|
raise UserWarning("Conversion interrupted.")
|
||||||
raise RuntimeError("One of workers crashed. Cause: " + mergeWorkerOutput[0][0],
|
if len(splitWorkerOutput) > 0:
|
||||||
mergeWorkerOutput[0][1])
|
rmtree(targetDir, True)
|
||||||
print("Splitting images...")
|
raise RuntimeError("One of workers crashed. Cause: " + splitWorkerOutput[0][0],
|
||||||
for root, _, files in os.walk(options.targetDir, False):
|
splitWorkerOutput[0][1])
|
||||||
for name in files:
|
if args.inPlace:
|
||||||
if getImageFileName(name) is not None:
|
rmtree(sourceDir)
|
||||||
pagenumber += 1
|
move(targetDir, sourceDir)
|
||||||
work.append([root, name, options])
|
else:
|
||||||
else:
|
rmtree(targetDir, True)
|
||||||
os.remove(os.path.join(root, name))
|
raise UserWarning("Source directory is empty.")
|
||||||
if GUI:
|
|
||||||
GUI.progressBarTick.emit('Splitting images')
|
|
||||||
GUI.progressBarTick.emit(str(pagenumber))
|
|
||||||
GUI.progressBarTick.emit('tick')
|
|
||||||
if len(work) > 0:
|
|
||||||
for i in work:
|
|
||||||
splitWorkerPool.apply_async(func=splitImage, args=(i, ), callback=splitImageTick)
|
|
||||||
splitWorkerPool.close()
|
|
||||||
splitWorkerPool.join()
|
|
||||||
if GUI and not GUI.conversionAlive:
|
|
||||||
rmtree(options.targetDir, True)
|
|
||||||
raise UserWarning("Conversion interrupted.")
|
|
||||||
if len(splitWorkerOutput) > 0:
|
|
||||||
rmtree(options.targetDir, True)
|
|
||||||
raise RuntimeError("One of workers crashed. Cause: " + splitWorkerOutput[0][0],
|
|
||||||
splitWorkerOutput[0][1])
|
|
||||||
if options.inPlace:
|
|
||||||
rmtree(options.sourceDir)
|
|
||||||
move(options.targetDir, options.sourceDir)
|
|
||||||
else:
|
else:
|
||||||
rmtree(options.targetDir, True)
|
raise UserWarning("Provided input is not a directory.")
|
||||||
raise UserWarning("Source directory is empty.")
|
|
||||||
else:
|
|
||||||
raise UserWarning("Provided path is not a directory.")
|
|
||||||
else:
|
else:
|
||||||
raise UserWarning("Target height is not set.")
|
raise UserWarning("Target height is not set.")
|
||||||
|
|||||||
@@ -19,6 +19,8 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import platform
|
||||||
|
import subprocess
|
||||||
import distro
|
import distro
|
||||||
from psutil import Popen
|
from psutil import Popen
|
||||||
from shutil import move
|
from shutil import move
|
||||||
@@ -55,7 +57,7 @@ class ComicArchive:
|
|||||||
|
|
||||||
def extract(self, targetdir):
|
def extract(self, targetdir):
|
||||||
if not os.path.isdir(targetdir):
|
if not os.path.isdir(targetdir):
|
||||||
raise OSError('Target directory don\'t exist.')
|
raise OSError('Target directory doesn\'t exist.')
|
||||||
process = Popen('7z x -y -xr!__MACOSX -xr!.DS_Store -xr!thumbs.db -xr!Thumbs.db -o"' + targetdir + '" "' +
|
process = Popen('7z x -y -xr!__MACOSX -xr!.DS_Store -xr!thumbs.db -xr!Thumbs.db -o"' + targetdir + '" "' +
|
||||||
self.filepath + '"', stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
|
self.filepath + '"', stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
|
||||||
process.communicate()
|
process.communicate()
|
||||||
@@ -65,6 +67,11 @@ class ComicArchive:
|
|||||||
process.communicate()
|
process.communicate()
|
||||||
if process.returncode != 0:
|
if process.returncode != 0:
|
||||||
raise OSError('Failed to extract archive.')
|
raise OSError('Failed to extract archive.')
|
||||||
|
elif process.returncode != 0 and platform.system() == 'Darwin':
|
||||||
|
process = subprocess.run(f"unar '{self.filepath}' -f -o '{targetdir}'",
|
||||||
|
stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
|
||||||
|
if process.returncode != 0:
|
||||||
|
raise Exception(process.stdout.decode("utf-8"))
|
||||||
elif process.returncode != 0:
|
elif process.returncode != 0:
|
||||||
raise OSError('Failed to extract archive. Check if p7zip-rar is installed.')
|
raise OSError('Failed to extract archive. Check if p7zip-rar is installed.')
|
||||||
tdir = os.listdir(targetdir)
|
tdir = os.listdir(targetdir)
|
||||||
|
|||||||
@@ -24,6 +24,13 @@ import mozjpeg_lossless_optimization
|
|||||||
from PIL import Image, ImageOps, ImageStat, ImageChops, ImageFilter
|
from PIL import Image, ImageOps, ImageStat, ImageChops, ImageFilter
|
||||||
from .shared import md5Checksum
|
from .shared import md5Checksum
|
||||||
|
|
||||||
|
# 0.045 was determined by
|
||||||
|
# 1200 / 824 = 1.456 (Kindle DX resolution)
|
||||||
|
# 2250 / 1500 = 1.5 (Typical manga page resolution)
|
||||||
|
# 1.5 - 1.456 < 0.045
|
||||||
|
# 0.045 / 1.5 = 0.03 (So maximum 3% of is cropped)
|
||||||
|
AUTO_CROP_THRESHOLD = 0.045
|
||||||
|
|
||||||
|
|
||||||
class ProfileData:
|
class ProfileData:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@@ -306,36 +313,32 @@ class ComicPage:
|
|||||||
self.image = self.image.quantize(palette=palImg)
|
self.image = self.image.quantize(palette=palImg)
|
||||||
|
|
||||||
def resizeImage(self):
|
def resizeImage(self):
|
||||||
if self.image.size[0] <= self.size[0] and self.image.size[1] <= self.size[1]:
|
ratio_device = float(self.size[1]) / float(self.size[0])
|
||||||
method = Image.Resampling.BICUBIC
|
ratio_image = float(self.image.size[1]) / float(self.image.size[0])
|
||||||
else:
|
method = self.resize_method()
|
||||||
method = Image.Resampling.LANCZOS
|
|
||||||
if self.opt.stretch:
|
if self.opt.stretch:
|
||||||
# if self.opt.stretch or (self.opt.kfx and ('-KCC-B' in self.targetPath or '-KCC-C' in self.targetPath)):
|
|
||||||
self.image = self.image.resize(self.size, method)
|
self.image = self.image.resize(self.size, method)
|
||||||
elif self.image.size[0] <= self.size[0] and self.image.size[1] <= self.size[1] and not self.opt.upscale:
|
elif method == Image.Resampling.BICUBIC and not self.opt.upscale:
|
||||||
if self.opt.format == 'CBZ' or self.opt.kfx:
|
if self.opt.format == 'CBZ' or self.opt.kfx:
|
||||||
borderw = int((self.size[0] - self.image.size[0]) / 2)
|
borderw = int((self.size[0] - self.image.size[0]) / 2)
|
||||||
borderh = int((self.size[1] - self.image.size[1]) / 2)
|
borderh = int((self.size[1] - self.image.size[1]) / 2)
|
||||||
self.image = ImageOps.expand(self.image, border=(borderw, borderh), fill=self.fill)
|
self.image = ImageOps.expand(self.image, border=(borderw, borderh), fill=self.fill)
|
||||||
if self.image.size[0] != self.size[0] or self.image.size[1] != self.size[1]:
|
if self.image.size[0] != self.size[0] or self.image.size[1] != self.size[1]:
|
||||||
self.image = ImageOps.fit(self.image, self.size, method=Image.Resampling.BICUBIC, centering=(0.5, 0.5))
|
self.image = ImageOps.fit(self.image, self.size, method=method)
|
||||||
else:
|
else: # if image bigger than device resolution or smaller with upscaling
|
||||||
if self.opt.format == 'CBZ' or self.opt.kfx:
|
if abs(ratio_image - ratio_device) < AUTO_CROP_THRESHOLD:
|
||||||
ratioDev = float(self.size[0]) / float(self.size[1])
|
self.image = ImageOps.fit(self.image, self.size, method=method)
|
||||||
if (float(self.image.size[0]) / float(self.image.size[1])) < ratioDev:
|
elif self.opt.format == 'CBZ' or self.opt.kfx:
|
||||||
diff = int(self.image.size[1] * ratioDev) - self.image.size[0]
|
self.image = ImageOps.pad(self.image, self.size, method=method, color=self.fill)
|
||||||
self.image = ImageOps.expand(self.image, border=(int(diff / 2), 0), fill=self.fill)
|
|
||||||
elif (float(self.image.size[0]) / float(self.image.size[1])) > ratioDev:
|
|
||||||
diff = int(self.image.size[0] / ratioDev) - self.image.size[1]
|
|
||||||
self.image = ImageOps.expand(self.image, border=(0, int(diff / 2)), fill=self.fill)
|
|
||||||
self.image = ImageOps.fit(self.image, self.size, method=method, centering=(0.5, 0.5))
|
|
||||||
else:
|
else:
|
||||||
hpercent = self.size[1] / float(self.image.size[1])
|
self.image = ImageOps.contain(self.image, self.size, method=method)
|
||||||
wsize = int((float(self.image.size[0]) * float(hpercent)))
|
|
||||||
self.image = self.image.resize((wsize, self.size[1]), method)
|
def resize_method(self):
|
||||||
if self.image.size[0] > self.size[0] or self.image.size[1] > self.size[1]:
|
if self.image.size[0] <= self.size[0] and self.image.size[1] <= self.size[1]:
|
||||||
self.image.thumbnail(self.size, Image.Resampling.LANCZOS)
|
method = Image.Resampling.BICUBIC
|
||||||
|
else:
|
||||||
|
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]
|
||||||
|
|||||||
@@ -34,7 +34,8 @@ class MetadataParser:
|
|||||||
'Inkers': [],
|
'Inkers': [],
|
||||||
'Colorists': [],
|
'Colorists': [],
|
||||||
'Summary': '',
|
'Summary': '',
|
||||||
'Bookmarks': []}
|
'Bookmarks': [],
|
||||||
|
'Title': ''}
|
||||||
self.rawdata = None
|
self.rawdata = None
|
||||||
self.format = None
|
self.format = None
|
||||||
if self.source.endswith('.xml') and os.path.exists(self.source):
|
if self.source.endswith('.xml') and os.path.exists(self.source):
|
||||||
@@ -45,7 +46,7 @@ class MetadataParser:
|
|||||||
self.rawdata = cbx.extractMetadata()
|
self.rawdata = cbx.extractMetadata()
|
||||||
self.format = cbx.type
|
self.format = cbx.type
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
raise UserWarning(e.strerror)
|
raise UserWarning(e)
|
||||||
if self.rawdata:
|
if self.rawdata:
|
||||||
self.parseXML()
|
self.parseXML()
|
||||||
|
|
||||||
@@ -58,6 +59,8 @@ class MetadataParser:
|
|||||||
self.data['Number'] = self.rawdata.getElementsByTagName('Number')[0].firstChild.nodeValue
|
self.data['Number'] = self.rawdata.getElementsByTagName('Number')[0].firstChild.nodeValue
|
||||||
if len(self.rawdata.getElementsByTagName('Summary')) != 0:
|
if len(self.rawdata.getElementsByTagName('Summary')) != 0:
|
||||||
self.data['Summary'] = self.rawdata.getElementsByTagName('Summary')[0].firstChild.nodeValue
|
self.data['Summary'] = self.rawdata.getElementsByTagName('Summary')[0].firstChild.nodeValue
|
||||||
|
if len(self.rawdata.getElementsByTagName('Title')) != 0:
|
||||||
|
self.data['Title'] = self.rawdata.getElementsByTagName('Title')[0].firstChild.nodeValue
|
||||||
for field in ['Writer', 'Penciller', 'Inker', 'Colorist']:
|
for field in ['Writer', 'Penciller', 'Inker', 'Colorist']:
|
||||||
if len(self.rawdata.getElementsByTagName(field)) != 0:
|
if len(self.rawdata.getElementsByTagName(field)) != 0:
|
||||||
for person in self.rawdata.getElementsByTagName(field)[0].firstChild.nodeValue.split(', '):
|
for person in self.rawdata.getElementsByTagName(field)[0].firstChild.nodeValue.split(', '):
|
||||||
@@ -76,7 +79,8 @@ class MetadataParser:
|
|||||||
for row in (['Series', self.data['Series']], ['Volume', self.data['Volume']],
|
for row in (['Series', self.data['Series']], ['Volume', self.data['Volume']],
|
||||||
['Number', self.data['Number']], ['Writer', ', '.join(self.data['Writers'])],
|
['Number', self.data['Number']], ['Writer', ', '.join(self.data['Writers'])],
|
||||||
['Penciller', ', '.join(self.data['Pencillers'])], ['Inker', ', '.join(self.data['Inkers'])],
|
['Penciller', ', '.join(self.data['Pencillers'])], ['Inker', ', '.join(self.data['Inkers'])],
|
||||||
['Colorist', ', '.join(self.data['Colorists'])], ['Summary', self.data['Summary']]):
|
['Colorist', ', '.join(self.data['Colorists'])], ['Summary', self.data['Summary']],
|
||||||
|
['Title', self.data['Title']]):
|
||||||
if self.rawdata.getElementsByTagName(row[0]):
|
if self.rawdata.getElementsByTagName(row[0]):
|
||||||
node = self.rawdata.getElementsByTagName(row[0])[0]
|
node = self.rawdata.getElementsByTagName(row[0])[0]
|
||||||
if row[1]:
|
if row[1]:
|
||||||
@@ -97,7 +101,8 @@ class MetadataParser:
|
|||||||
for row in (['Series', self.data['Series']], ['Volume', self.data['Volume']],
|
for row in (['Series', self.data['Series']], ['Volume', self.data['Volume']],
|
||||||
['Number', self.data['Number']], ['Writer', ', '.join(self.data['Writers'])],
|
['Number', self.data['Number']], ['Writer', ', '.join(self.data['Writers'])],
|
||||||
['Penciller', ', '.join(self.data['Pencillers'])], ['Inker', ', '.join(self.data['Inkers'])],
|
['Penciller', ', '.join(self.data['Pencillers'])], ['Inker', ', '.join(self.data['Inkers'])],
|
||||||
['Colorist', ', '.join(self.data['Colorists'])], ['Summary', self.data['Summary']]):
|
['Colorist', ', '.join(self.data['Colorists'])], ['Summary', self.data['Summary']],
|
||||||
|
['Title', self.data['Title']]):
|
||||||
if row[1]:
|
if row[1]:
|
||||||
main = doc.createElement(row[0])
|
main = doc.createElement(row[0])
|
||||||
root.appendChild(main)
|
root.appendChild(main)
|
||||||
@@ -116,5 +121,5 @@ class MetadataParser:
|
|||||||
cbx = comicarchive.ComicArchive(self.source)
|
cbx = comicarchive.ComicArchive(self.source)
|
||||||
cbx.addFile(tmpXML)
|
cbx.addFile(tmpXML)
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
raise UserWarning(e.strerror)
|
raise UserWarning(e)
|
||||||
rmtree(workdir)
|
rmtree(workdir)
|
||||||
|
|||||||
@@ -25,6 +25,11 @@ import os
|
|||||||
from random import choice
|
from random import choice
|
||||||
from string import ascii_uppercase, digits
|
from string import ascii_uppercase, digits
|
||||||
|
|
||||||
|
# skip stray images a few pixels in size in some PDFs
|
||||||
|
# typical images are many thousands in length
|
||||||
|
# https://github.com/ciromattia/kcc/pull/546
|
||||||
|
STRAY_IMAGE_LENGTH_THRESHOLD = 300
|
||||||
|
|
||||||
|
|
||||||
class PdfJpgExtract:
|
class PdfJpgExtract:
|
||||||
def __init__(self, fname):
|
def __init__(self, fname):
|
||||||
@@ -60,10 +65,15 @@ class PdfJpgExtract:
|
|||||||
raise Exception("Didn't find end of JPG!")
|
raise Exception("Didn't find end of JPG!")
|
||||||
istart += startfix
|
istart += startfix
|
||||||
iend += endfix
|
iend += endfix
|
||||||
|
i = iend
|
||||||
|
|
||||||
|
if iend - istart < STRAY_IMAGE_LENGTH_THRESHOLD:
|
||||||
|
continue
|
||||||
|
|
||||||
jpg = pdf[istart:iend]
|
jpg = pdf[istart:iend]
|
||||||
jpgfile = open(self.path + "/jpg%d.jpg" % njpg, "wb")
|
jpgfile = open(self.path + "/jpg%d.jpg" % njpg, "wb")
|
||||||
jpgfile.write(jpg)
|
jpgfile.write(jpg)
|
||||||
jpgfile.close()
|
jpgfile.close()
|
||||||
njpg += 1
|
njpg += 1
|
||||||
i = iend
|
|
||||||
return self.path, njpg
|
return self.path, njpg
|
||||||
|
|||||||
@@ -100,11 +100,11 @@ def dependencyCheck(level):
|
|||||||
missing = []
|
missing = []
|
||||||
if level > 2:
|
if level > 2:
|
||||||
try:
|
try:
|
||||||
from PyQt5.QtCore import qVersion as qtVersion
|
from PySide6.QtCore import qVersion as qtVersion
|
||||||
if StrictVersion('5.6.0') > StrictVersion(qtVersion()):
|
if StrictVersion('6.5.1') > StrictVersion(qtVersion()):
|
||||||
missing.append('PyQt 5.6.0+')
|
missing.append('PySide 6.5.1+')
|
||||||
except ImportError:
|
except ImportError:
|
||||||
missing.append('PyQt 5.6.0+')
|
missing.append('PySide 6.5.1+')
|
||||||
try:
|
try:
|
||||||
import raven
|
import raven
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
|||||||
BIN
other/osx/7z
BIN
other/osx/7z
Binary file not shown.
BIN
other/osx/7z.so
BIN
other/osx/7z.so
Binary file not shown.
@@ -1,72 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
||||||
<plist version="1.0">
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleDevelopmentRegion</key>
|
|
||||||
<string>English</string>
|
|
||||||
<key>CFBundleDisplayName</key>
|
|
||||||
<string>Kindle Comic Converter</string>
|
|
||||||
<key>CFBundleDocumentTypes</key>
|
|
||||||
<array>
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleTypeExtensions</key>
|
|
||||||
<array>
|
|
||||||
<string>cbz</string>
|
|
||||||
<string>cbr</string>
|
|
||||||
<string>cb7</string>
|
|
||||||
<string>zip</string>
|
|
||||||
<string>rar</string>
|
|
||||||
<string>7z</string>
|
|
||||||
<string>pdf</string>
|
|
||||||
</array>
|
|
||||||
<key>CFBundleTypeIconFile</key>
|
|
||||||
<string>comic2ebook.icns</string>
|
|
||||||
<key>CFBundleTypeName</key>
|
|
||||||
<string>Comics</string>
|
|
||||||
<key>CFBundleTypeRole</key>
|
|
||||||
<string>Editor</string>
|
|
||||||
</dict>
|
|
||||||
</array>
|
|
||||||
<key>CFBundleExecutable</key>
|
|
||||||
<string>MacOS/Kindle Comic Converter</string>
|
|
||||||
<key>CFBundleGetInfoString</key>
|
|
||||||
<string>KindleComicConverter 5.5.2, written 2012-2019 by Ciro Mattia Gonano and Pawel Jastrzebski</string>
|
|
||||||
<key>CFBundleIconFile</key>
|
|
||||||
<string>comic2ebook.icns</string>
|
|
||||||
<key>CFBundleIdentifier</key>
|
|
||||||
<string>com.kindlecomicconverter.KindleComicConverter</string>
|
|
||||||
<key>CFBundleInfoDictionaryVersion</key>
|
|
||||||
<string>6.0</string>
|
|
||||||
<key>CFBundleName</key>
|
|
||||||
<string>Kindle Comic Converter</string>
|
|
||||||
<key>CFBundlePackageType</key>
|
|
||||||
<string>APPL</string>
|
|
||||||
<key>CFBundleShortVersionString</key>
|
|
||||||
<string>5.5.2</string>
|
|
||||||
<key>CFBundleSignature</key>
|
|
||||||
<string>????</string>
|
|
||||||
<key>CFBundleVersion</key>
|
|
||||||
<string>5.5.2</string>
|
|
||||||
<key>LSEnvironment</key>
|
|
||||||
<dict>
|
|
||||||
<key>PATH</key>
|
|
||||||
<string>./../Resources:/Applications/Kindle Comic Creator/Kindle Comic Creator.app/Contents/MacOS:/usr/local/bin:/usr/bin:/bin</string>
|
|
||||||
</dict>
|
|
||||||
<key>LSHasLocalizedDisplayName</key>
|
|
||||||
<false/>
|
|
||||||
<key>LSMinimumSystemVersion</key>
|
|
||||||
<string>10.14.0</string>
|
|
||||||
<key>NSAppleScriptEnabled</key>
|
|
||||||
<false/>
|
|
||||||
<key>NSHumanReadableCopyright</key>
|
|
||||||
<string>ISC License (ISCL)</string>
|
|
||||||
<key>NSMainNibFile</key>
|
|
||||||
<string>MainMenu</string>
|
|
||||||
<key>NSPrincipalClass</key>
|
|
||||||
<string>NSApplication</string>
|
|
||||||
<key>NSRequiresAquaSystemAppearance</key>
|
|
||||||
<string>false</string>
|
|
||||||
<key>NSInitialToolTipDelay</key>
|
|
||||||
<integer>1000</integer>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
||||||
BIN
other/osx/Rar.so
BIN
other/osx/Rar.so
Binary file not shown.
@@ -1,8 +1,7 @@
|
|||||||
PyQt5>=5.6.0
|
PySide6>=6.5.1
|
||||||
Pillow>=5.2.0
|
Pillow>=5.2.0
|
||||||
psutil>=5.0.0
|
psutil>=5.9.5
|
||||||
python-slugify>=1.2.1
|
python-slugify>=1.2.1
|
||||||
raven>=6.0.0
|
raven>=6.0.0
|
||||||
# PyQt5-tools
|
|
||||||
mozjpeg-lossless-optimization>=1.1.2
|
mozjpeg-lossless-optimization>=1.1.2
|
||||||
distro
|
distro
|
||||||
|
|||||||
21
setup.py
21
setup.py
@@ -17,8 +17,6 @@ import setuptools
|
|||||||
import distutils.cmd
|
import distutils.cmd
|
||||||
from kindlecomicconverter import __version__
|
from kindlecomicconverter import __version__
|
||||||
|
|
||||||
OSX_INFO_PLIST = "other/osx/Info.plist"
|
|
||||||
|
|
||||||
NAME = 'KindleComicConverter'
|
NAME = 'KindleComicConverter'
|
||||||
MAIN = 'kcc.py'
|
MAIN = 'kcc.py'
|
||||||
VERSION = __version__
|
VERSION = __version__
|
||||||
@@ -39,22 +37,7 @@ class BuildBinaryCommand(distutils.cmd.Command):
|
|||||||
def run(self):
|
def run(self):
|
||||||
VERSION = __version__
|
VERSION = __version__
|
||||||
if sys.platform == 'darwin':
|
if sys.platform == 'darwin':
|
||||||
|
|
||||||
with open(OSX_INFO_PLIST, 'r') as file:
|
|
||||||
filedata = file.read()
|
|
||||||
filedata = filedata.replace('5.5.2', VERSION)
|
|
||||||
with open(OSX_INFO_PLIST, 'w') as file:
|
|
||||||
file.write(filedata)
|
|
||||||
|
|
||||||
os.system('pyinstaller -y -F -i icons/comic2ebook.icns -n "Kindle Comic Converter" -w -s kcc.py')
|
os.system('pyinstaller -y -F -i icons/comic2ebook.icns -n "Kindle Comic Converter" -w -s kcc.py')
|
||||||
os.makedirs('dist/Kindle Comic Converter.app/Contents/Resources/Codecs')
|
|
||||||
shutil.copy('other/osx/7z', 'dist/Kindle Comic Converter.app/Contents/Resources')
|
|
||||||
shutil.copy('other/osx/7z.so', 'dist/Kindle Comic Converter.app/Contents/Resources')
|
|
||||||
shutil.copy('other/osx/Rar.so', 'dist/Kindle Comic Converter.app/Contents/Resources/Codecs')
|
|
||||||
shutil.copy('other/osx/Info.plist', 'dist/Kindle Comic Converter.app/Contents')
|
|
||||||
shutil.copy('LICENSE.txt', 'dist/Kindle Comic Converter.app/Contents/Resources')
|
|
||||||
shutil.copy('other/windows/Additional-LICENSE.txt', 'dist/Kindle Comic Converter.app/Contents/Resources')
|
|
||||||
os.chmod('dist/Kindle Comic Converter.app/Contents/Resources/7z', 0o777)
|
|
||||||
# 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('appdmg kcc.json dist/KindleComicConverter_osx_' + VERSION + '.dmg')
|
os.system('appdmg kcc.json dist/KindleComicConverter_osx_' + VERSION + '.dmg')
|
||||||
exit(0)
|
exit(0)
|
||||||
@@ -92,9 +75,9 @@ setuptools.setup(
|
|||||||
},
|
},
|
||||||
packages=['kindlecomicconverter'],
|
packages=['kindlecomicconverter'],
|
||||||
install_requires=[
|
install_requires=[
|
||||||
'PyQt5>=5.6.0',
|
'pyside6>=6.5.1',
|
||||||
'Pillow>=5.2.0',
|
'Pillow>=5.2.0',
|
||||||
'psutil>=5.0.0',
|
'psutil>=5.9.5',
|
||||||
'python-slugify>=1.2.1,<9.0.0',
|
'python-slugify>=1.2.1,<9.0.0',
|
||||||
'raven>=6.0.0',
|
'raven>=6.0.0',
|
||||||
'mozjpeg-lossless-optimization>=1.1.2',
|
'mozjpeg-lossless-optimization>=1.1.2',
|
||||||
|
|||||||
Reference in New Issue
Block a user