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

Compare commits

..

112 Commits
v5.6.3 ... dev

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

* Update setup.py
2024-10-15 10:09:57 -07:00
Alex Xu
9c71dc85ee build windows with docker 2024-10-14 23:11:52 -07:00
Alex Xu
57a0450026 Revert "remove GUI windows docker"
This reverts commit 4fc5cc9dfb.
2024-10-14 22:56:34 -07:00
Alex Xu
885ac227de bump windows to Python 3.12 2024-10-14 22:44:55 -07:00
Alex Xu
d3e4e859b1 Revert "Add de-dupe cover option for landscape alignment (#561)"
This reverts commit c35dd137ea.
2024-10-14 22:33:33 -07:00
Alex Xu
8ff401cc3a remove fastnumbers from armv7 2024-10-04 09:04:56 -07:00
Alex Xu
fb7d92d737 fix Docker linux arm (#746)
* fix linux arm64

* Update Dockerfile-base

* Update Dockerfile

* Update Dockerfile-base

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

* fix docker armv7

* Update Dockerfile-base

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

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

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

* simplify code

* Revert "simplify code"

This reverts commit 3e90dd66c3.

* add link to missing extraction software

* fix tar

* fix wording
2024-08-09 17:08:13 -07:00
Alex Xu
2c770f4562 search for kindlegen in %UserProfile% 2024-08-09 17:05:44 -07:00
Alex Xu
4c73006bd8 add APFS to external drive 2024-08-06 16:04:45 -07:00
Alex Xu
1093dbf65a Update README.md 2024-07-27 08:26:44 -07:00
Alex Xu
df9990c692 add python 3.12 unsupported note 2024-07-25 13:54:31 -07:00
Alex Xu
114f4c9e57 add macOS minimums 2024-07-21 10:19:49 -07:00
Alex Xu
c2c477475d combine files/chapters in readme 2024-07-20 12:37:20 -07:00
Alex Xu
a0f8d0b5cf add kcc 6.1 7z note 2024-07-01 08:55:20 -07:00
Alex Xu
2fc32bae58 Update __init__.py 2024-06-29 08:52:09 -07:00
Alex Xu
db25a0939c Build windows gui version normally. 2024-06-25 22:03:10 -07:00
Alex Xu
1bd7506140 remove unar macOS 2024-06-25 21:04:52 -07:00
Alex Xu
4fc5cc9dfb remove GUI windows docker 2024-06-25 20:45:32 -07:00
Alex Xu
89289412a3 Revert "add hiddenimports=['pkg_resources.extern']"
This reverts commit 193297c8fc.
2024-06-25 19:29:17 -07:00
Alex Xu
193297c8fc add hiddenimports=['pkg_resources.extern'] 2024-06-25 19:15:34 -07:00
Alex Xu
367d71e4ad fix typo 2024-06-25 12:52:41 -07:00
Alex Xu
cdde0f18cd update prereq readme (#714)
* update prereq readme

* Update README.md

* Update README.md

* Revert "Update README.md"

This reverts commit ceb35b03b6.

* Revert "Update README.md"

This reverts commit a24e818c0e.

* add other OS note
2024-06-25 12:42:38 -07:00
Alex Xu
9fe82a9dd4 improve kindlegen detection windows (#688)
* improve kindlegen detection windows

* add C:\Apps folders
2024-06-25 12:21:48 -07:00
detournemint
e958edd135 Update package-linux.yml to include libxcb-cursor0
Including libxcb-cursor0 for linux
2024-06-25 12:15:33 -07:00
Alex Xu
6738b70487 don't display upscale warning on kindle scribe 2024-06-09 08:00:55 -07:00
Dominik Gedon
44094bdb21 Add Kobo Libra/Clara colour (#703)
* fix: trailing whitespaces

* Add color toggle mode

* Add Kobo Libra Colour

Signed-off-by: Dominik Gedon <dominik@gedon.org>

* Add Kobo Clara Colour

Signed-off-by: Dominik Gedon <dominik@gedon.org>

* Address suggestions

* Address more suggestions

---------

Signed-off-by: Dominik Gedon <dominik@gedon.org>
2024-06-09 08:00:25 -07:00
Alex Xu
a9a2f47e30 fix 1860 width 2024-05-28 09:55:59 -07:00
Bruno Resende
c35dd137ea Add de-dupe cover option for landscape alignment (#561)
* Adds --author argument to CLI

* Add --prefercoverfile and --nocoveraspage arguments to CLI

* cover checks only run once

* Revert "cover checks only run once"

This reverts commit ad7b3cc106.

* split off author portion

* split off author

* remove prefercover

* whitespace fixes

* rename to duplicatecover

* whitespace fixes

* rename to dedupecover

* add dedupe to UI

---------

Co-authored-by: Alex Xu <alexkurosakimh3@gmail.com>
2024-05-17 09:56:22 -07:00
Alex Xu
1ea008c8f3 reduce dependency on 7z, unar, homebrew by using builtin tar (#693)
* reduce dependency on 7z, unar, homebrew by using builtin tar

* update readme

* fix typo

* add editor text

* remove unar

* Revert "remove unar"

This reverts commit 2c4b239d67.

* Revert "fix typo"

This reverts commit 79752c7f38.

* Revert "update readme"

This reverts commit 4f5c727a2d.
2024-05-17 09:56:04 -07:00
Alex Xu
cbc1ed5db4 Kindle Scribe 1860 max width (#691)
* Kindle Scribe 1860 max width

* create kindle_scribe_azw3 var

* clean up code
2024-05-17 09:55:43 -07:00
Alex Xu
6bf662bea3 add faq 2024-05-15 10:15:08 -07:00
Alex Xu
cfae306731 macos-latest is now macos-14 (m1) 2024-04-27 12:56:49 -07:00
Alex Xu
f6475f4c61 Close and re-open KCC note 2024-04-19 21:15:48 -07:00
Alex Xu
9646518575 shorten 7z/kindlegen notes 2024-04-18 20:58:12 -07:00
Alex Xu
937dd3984a kindlegen note 2024-04-18 18:07:06 -07:00
Alex Xu
4e6f6ec8e8 clarify 7z/kindlegen windows 2024-04-18 17:35:51 -07:00
Alex Xu
4b36fdbfa2 update C drive notes 2024-04-18 16:08:10 -07:00
Alex Xu
d8141af4eb update c drive notes 2024-04-18 15:45:34 -07:00
Alex Xu
cf38c4d445 Revert "fix package-windows-with-docker.yml (#579)"
This reverts commit 424118b7cd.
2024-04-16 15:46:44 -07:00
Alex Xu
283b2da1a0 bump to 6.0.0 2024-04-16 15:09:03 -07:00
Alex Xu
4a9f693574 Adds --author argument to CLI (#683) 2024-04-16 14:59:54 -07:00
Alex Xu
9a48887edc update C drive notes 2024-04-15 21:16:14 -07:00
Alex Xu
0981ec3c6d add more 7z Windows locations 2024-03-24 18:40:50 -07:00
dependabot[bot]
e0e6606736 Bump softprops/action-gh-release from 1 to 2
Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 1 to 2.
- [Release notes](https://github.com/softprops/action-gh-release/releases)
- [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md)
- [Commits](https://github.com/softprops/action-gh-release/compare/v1...v2)

---
updated-dependencies:
- dependency-name: softprops/action-gh-release
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-21 21:58:00 -07:00
Alex Xu
518e67c132 move install from source instructions 2024-03-16 20:12:30 -07:00
Alex Xu
6c593dac1f add gen_ui_files note 2024-03-16 14:04:35 -07:00
Alex Xu
9d065676db add install from source and qt creator instructions 2024-03-16 13:54:40 -07:00
Alex Xu
9520e59a29 Add C drive notes 2024-03-15 12:08:46 -07:00
Alex Xu
461a7fda88 add C drive note 2024-03-12 12:44:07 -07:00
Alex Xu
5ccbb770a6 do not change default install location for 7z/KP 2024-03-09 10:04:01 -08:00
Alex Xu
49bf80f5d8 LOAD_TRUNCATED_IMAGES = True 2024-03-04 21:36:07 -08:00
Alex Xu
33cc324381 prepare m1 build pipeline (#669) 2024-01-31 11:49:27 -08:00
Alex Xu
6ba5539813 Revert "docker arm install from requirements.txt" 2024-01-29 10:54:13 -08:00
Alex Xu
f725196106 reset saved settings 2024-01-29 10:29:45 -08:00
Alex Xu
c8ff88ed0f Update Dockerfile-base 2024-01-28 22:27:03 -08:00
darodi
33fed662fe Merge pull request #666
* fix dependencies
2024-01-12 23:22:09 +00:00
Alex Xu
bc7cd17916 rename kp3 2024-01-12 12:41:51 -08:00
Alex Xu
c5e1e18ac2 Revert "rename pw to paperwhite"
This reverts commit 48541404ee.
2024-01-02 14:26:53 -08:00
Alex Xu
ee31b784cb Add native Apple Silicon support by upgrading qt5 to qt6 (#523)
* initial upgrade

* fix epub icon

* pyside6

* fix tray icon

* add spaces

* add comment back

* change exec

* edit shared

* Add CheckState enums

* add mozJpeg

* fix batch

* import CheckedState

* remove references to qt5

* add mozJpeg warning

* Update package-linux.yml

* Update package-linux.yml

* Update Dockerfile-base

* Update README.md

* Update package-linux.yml

* Update README.md

* Update README.md

* add mozjpeg to gitignore

* add warning text

* fix state issue

* use same settings save location as qt5

* remove space

* remove mozJpeg

* update Dockerfile-base file version

* use getattr instead of eval

* undo readme changes

* undo conda

* undo gitignore

---------

Co-authored-by: Alexander Xu <alexanderx@qualtrics.com>
Co-authored-by: darodi <4682830+darodi@users.noreply.github.com>
2024-01-02 14:08:39 -08:00
Alex Xu
48541404ee rename pw to paperwhite 2024-01-02 12:14:51 -08:00
Alex Xu
acbebcfd40 rename kindle labels by gen 2024-01-02 12:14:51 -08:00
VampiroMedicado
dd6273b864 add missing parameter output to options 2024-01-02 10:11:38 -08:00
VampiroMedicado
e8502f008a hide subprocess consoles on Windows (#656) 2024-01-01 18:11:55 -08:00
inganault
169a41e7d2 Add missing dependencies to setup.py (#653) 2023-12-29 19:00:24 -08:00
Alex Xu
57cf669cdd add to credits 2023-12-28 18:55:08 -08:00
Alex Xu
0b4f089b8e add .DS_Store to .gitignore 2023-12-23 09:35:12 -08:00
Alex Xu
7eb985337c add requests to setup.py 2023-12-23 09:10:31 -08:00
Alex Xu
d62690e8bf import subprocess 2023-12-21 13:40:48 -08:00
Alex Xu
3988f2012f fix p7zip-rar error and files with ' or " by using arg lists instead of strings (#633)
* Revert "Revert "fix files with ' or " by using arg lists instead of strings (#581)" (#628)"

This reverts commit b528dab711.

* handle FileNotFoundError

* modify unar handling

* remove unneeded utf-8 encoding

* dont uft-8 encode 7z

* remove utf-8 encoding from 7z calls

* don't extract stderr

* add extraction error

* edit error message

* remove debug

* remove kindlegen location from GUI

* remove comment
2023-12-21 12:52:35 -08:00
Alex Xu
6cdd9d5909 change kobo epub filenames (#616) 2023-12-21 12:51:26 -08:00
Alex Xu
c29a4beac9 use requests library 2023-12-21 12:44:29 -08:00
Alex Xu
5f7bdef325 bump to 5.6.5 2023-12-21 09:50:34 -08:00
Alex Xu
54b5d698ee fix access denied 2023-12-21 09:43:33 -08:00
Alex Xu
a99c63acea use os_sorted over natsorted 2023-12-21 09:43:07 -08:00
dependabot[bot]
251df2e7ba Bump github/codeql-action from 2 to 3
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 2 to 3.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/v2...v3)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-21 09:42:20 -08:00
dependabot[bot]
b1379b7c59 Bump actions/upload-artifact from 3 to 4
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 3 to 4.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-21 09:42:11 -08:00
Alex Xu
983bde1691 add fat to list of drives 2023-12-21 09:41:18 -08:00
dependabot[bot]
e79e5a311c Bump actions/setup-python from 4 to 5
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 4 to 5.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-10 20:02:36 -08:00
jaroslawjanas
39c73dbc0f Added natsort to the dependency list in environment.yml 2023-12-10 19:59:33 -08:00
Alex Xu
915c9389ef remove kindlegen location UI 2023-12-09 17:39:01 -08:00
Alex Xu
89d887710e fix typo 2023-12-07 18:57:46 -08:00
Alex Xu
2e9bc5381a add PREREQUISITES 2023-12-07 18:56:57 -08:00
Alex Xu
ab1ce158c7 rename to prerequisites 2023-12-04 09:33:08 -08:00
Ciro Mattia Gonano
6278adfb25 Update issue templates 2023-11-29 12:40:11 +01:00
Alex Xu
6e6c13047e bump to 5.6.4 2023-11-28 08:32:34 -08:00
Alex Xu
b528dab711 Revert "fix files with ' or " by using arg lists instead of strings (#581)" (#628)
This reverts commit 431862a2e9.
2023-11-28 08:20:14 -08:00
Alex Xu
2ffefee928 link to readme not wiki for 7z kindlegen (#627)
* link to readme not wiki for 7z kindlegen

* update 7z link
2023-11-28 08:15:59 -08:00
Alex Xu
a5e5407363 streamline downloads and installation readme (#623)
* streamline downloads and installation

move linux stuff into wiki

* Update README.md

* update labels

* simplify
2023-11-28 07:38:46 -08:00
Alex Xu
da1ba64bd2 skip bookmarks if split (#620) 2023-11-28 06:54:47 -08:00
Alex Xu
6dcaf9a6d1 fix unsupported archive error (#619) 2023-11-28 06:53:58 -08:00
Alex Xu
3090a47f20 change mac pyinstaller -F to -D (#621) 2023-11-28 06:52:39 -08:00
Alex Xu
1e537915d4 make c2e and c2p versions first in file order 2023-11-28 06:50:55 -08:00
dependabot[bot]
7273ca25b8 Bump actions/setup-node from 3 to 4 (#609)
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 3 to 4.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](https://github.com/actions/setup-node/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-09 23:06:46 +00:00
Alex Xu
68da853e42 fix page order in pdf and more by using natural sort instead of python sort (#589)
* use natsorted

* Add fast
2023-11-09 23:00:44 +00:00
Alex Xu
431862a2e9 fix files with ' or " by using arg lists instead of strings (#581)
* replace popen with subprocess run

* add splitlines

* remove stdin

* fix xml

* fix error logging and 7zip
2023-11-09 22:27:09 +00:00
Alex Xu
65062f8984 add Kindle 4/5/7/8/10 label (#614) 2023-11-09 22:15:34 +00:00
Alex Xu
8122fa1e45 add c2e and c2p note (#606) 2023-10-26 19:29:16 +00:00
Alex Xu
13fedff77b lower auto crop threshold (#598) 2023-10-26 19:20:26 +00:00
dependabot[bot]
61b1207a3e Bump actions/checkout from 3 to 4 (#592)
Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-16 12:10:53 +00:00
Alex Xu
60e9f075b8 replace exit() with sys.exit() (#590) 2023-09-08 20:18:32 +00:00
29 changed files with 12477 additions and 12234 deletions

31
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@@ -0,0 +1,31 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
Add a screenshot of your KCC settings.
**Desktop (please complete the following information):**
- OS: [e.g. macOS, Linux, Windows 11]
- Device [e.g. Kindle Paperwhite 3rd gen, Kobo Libra 2]
**Additional context**
Add any other context about the problem here.

View File

@@ -38,11 +38,11 @@ jobs:
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v3 uses: actions/checkout@v4
# Initializes the CodeQL tools for scanning. # Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL - name: Initialize CodeQL
uses: github/codeql-action/init@v2 uses: github/codeql-action/init@v3
with: with:
languages: ${{ matrix.language }} languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file. # If you wish to specify custom queries, you can do so here or in a config file.
@@ -56,7 +56,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below) # If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild - name: Autobuild
uses: github/codeql-action/autobuild@v2 uses: github/codeql-action/autobuild@v3
# Command-line programs to run using the OS shell. # Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
@@ -69,6 +69,6 @@ jobs:
# ./location_of_script_within_repo/buildscript.sh # ./location_of_script_within_repo/buildscript.sh
- name: Perform CodeQL Analysis - name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2 uses: github/codeql-action/analyze@v3
with: with:
category: "/language:${{matrix.language}}" category: "/language:${{matrix.language}}"

View File

@@ -25,17 +25,17 @@ jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v4 uses: actions/setup-python@v5
with: with:
python-version: 3.11 python-version: 3.11
cache: 'pip' cache: 'pip'
- name: Install python dependencies - name: Install python dependencies
run: | run: |
sudo apt-get update sudo apt-get update
sudo apt-get install -y libpng-dev libjpeg-dev p7zip-full p7zip-rar python3-pyqt5 python3-pip squashfs-tools libfuse2 sudo apt-get install -y libpng-dev libjpeg-dev p7zip-full p7zip-rar python3-pip squashfs-tools libfuse2 libxcb-cursor0
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: |
@@ -59,12 +59,12 @@ jobs:
env: env:
UPDATE_INFO: gh-releases-zsync|ciromattia|kcc|latest|*x86_64.AppImage.zsync UPDATE_INFO: gh-releases-zsync|ciromattia|kcc|latest|*x86_64.AppImage.zsync
- name: upload artifact - name: upload artifact
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
name: AppImage name: AppImage
path: './*.AppImage*' path: './*.AppImage*'
- name: Release - name: Release
uses: softprops/action-gh-release@v1 uses: softprops/action-gh-release@v2
if: startsWith(github.ref, 'refs/tags/') if: startsWith(github.ref, 'refs/tags/')
with: with:
prerelease: true prerelease: true

View File

@@ -23,11 +23,14 @@ on:
jobs: jobs:
build: build:
runs-on: macos-latest strategy:
matrix:
os: [ macos-12, macos-14 ]
runs-on: ${{ matrix.os }}
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v4 uses: actions/setup-python@v5
with: with:
python-version: 3.11 python-version: 3.11
cache: 'pip' cache: 'pip'
@@ -66,7 +69,7 @@ jobs:
# apply provisioning profile # apply provisioning profile
mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
cp $PP_PATH ~/Library/MobileDevice/Provisioning\ Profiles cp $PP_PATH ~/Library/MobileDevice/Provisioning\ Profiles
- uses: actions/setup-node@v3 - uses: actions/setup-node@v4
with: with:
node-version: 16 node-version: 16
- run: npm install -g appdmg - run: npm install -g appdmg
@@ -75,12 +78,12 @@ jobs:
run: | run: |
python setup.py build_binary python setup.py build_binary
- name: upload build - name: upload build
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
name: mac-os-build name: mac-os-build-${{ runner.arch }}
path: dist/*.dmg path: dist/*.dmg
- name: Release - name: Release
uses: softprops/action-gh-release@v1 uses: softprops/action-gh-release@v2
if: startsWith(github.ref, 'refs/tags/') if: startsWith(github.ref, 'refs/tags/')
with: with:
prerelease: true prerelease: true

View File

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

View File

@@ -25,11 +25,11 @@ jobs:
build: build:
runs-on: windows-latest runs-on: windows-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v4 uses: actions/setup-python@v5
with: with:
python-version: 3.11 python-version: 3.12
cache: 'pip' cache: 'pip'
- name: Install dependencies - name: Install dependencies
env: env:
@@ -42,12 +42,12 @@ jobs:
run: | run: |
python setup.py build_binary python setup.py build_binary
- name: upload build - name: upload build
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
name: windows-build name: windows-build
path: dist/*.exe path: dist/*.exe
- name: Release - name: Release
uses: softprops/action-gh-release@v1 uses: softprops/action-gh-release@v2
if: startsWith(github.ref, 'refs/tags/') if: startsWith(github.ref, 'refs/tags/')
with: with:
prerelease: true prerelease: true

1
.gitignore vendored
View File

@@ -11,3 +11,4 @@ KindleComicConverter*.egg-info/
/venv/ /venv/
/kindlegen* /kindlegen*
/kcc.bat /kcc.bat
.DS_Store

View File

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

View File

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

120
README.md
View File

@@ -22,65 +22,57 @@ If you have some **technical** problems using KCC please [file an issue here](ht
If you can fix an open issue, fork & make a pull request. If you can fix an open issue, fork & make a pull request.
If you find **KCC** valuable you can consider donating to the authors: If you find **KCC** valuable you can consider donating to the authors:
- Ciro Mattia Gonano: - Ciro Mattia Gonano (founder, active 2013-2014):
- [![Donate PayPal](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=D8WNYNPBGDAS2) - [![Donate PayPal](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=D8WNYNPBGDAS2)
- [![Donate Flattr](https://img.shields.io/badge/Donate-Flattr-green.svg)](http://flattr.com/thing/2260449/ciromattiakcc-on-GitHub) - [![Donate Flattr](https://img.shields.io/badge/Donate-Flattr-green.svg)](http://flattr.com/thing/2260449/ciromattiakcc-on-GitHub)
- Paweł Jastrzębski: - Paweł Jastrzębski (active 2013-2019):
- [![Donate PayPal](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YTTJ4LK2JDHPS) - [![Donate PayPal](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YTTJ4LK2JDHPS)
- [![Donate Bitcoin](https://img.shields.io/badge/Donate-Bitcoin-green.svg)](https://jastrzeb.ski/donate/) - [![Donate Bitcoin](https://img.shields.io/badge/Donate-Bitcoin-green.svg)](https://jastrzeb.ski/donate/)
- Alex Xu (active 2023-Present)
- [![Donate PayPal](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/donate/?business=QFJVE7A6LCP6U&no_recurring=0&item_name=Kindle+Comic+Converter&currency_code=USD)
## INSTALLATION ## DOWNLOADS
### DOWNLOADS
- **https://github.com/ciromattia/kcc/releases** - **https://github.com/ciromattia/kcc/releases**
Click on **Assets** of the latest release. Click on **Assets** of the latest release.
You probably want either You probably want either
- `kcc_*.*.*.exe` (Windows) - `KCC_*.*.*.exe` (Windows)
- `KindleComicConverter_osx_*.*.*.dmg` (Mac) - `kcc_macos_arm_*.*.*.dmg` (recent Mac with Apple Silicon M1 chip or later)
- `kcc_macos_i386_*.*.*.dmg` (older Mac with Intel chip)
Installation Wiki: https://github.com/ciromattia/kcc/wiki/Installation The `c2e` and `c2p` versions are command line tools for power users.
- flatpak : https://flathub.org/apps/details/io.github.ciromattia.kcc On Windows 11, you may need to run in compatibility mode for an older Windows version.
- Docker: https://github.com/ciromattia/kcc/pkgs/container/kcc
### DEPENDENCIES On Mac, right click open to get past the security warning.
Following software is required to run Linux version of **KCC** and/or bare sources:
- Python 3.3+
- [PyQt5](https://pypi.python.org/pypi/PyQt5) 5.6.0+ (only needed for GUI)
- [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+
- [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)
On Debian based distributions these two commands should install all needed dependencies: For flatpak, Docker, and AppImage versions, refer to the wiki: https://github.com/ciromattia/kcc/wiki/Installation
## FAQ
- [Kindle Scribe cover guide](https://github.com/ciromattia/kcc/issues/508) (also works for older Kindles)
- [Windows 7 support](https://github.com/ciromattia/kcc/issues/678)
- [Combine files/chapters](https://github.com/ciromattia/kcc/issues/612#issuecomment-2117985011)
- [Flatpak mobi conversion stuck](https://github.com/ciromattia/kcc/wiki/Installation#linux)
```bash ## PREREQUISITES
$ sudo apt-get install -y python3 python3-dev libpng-dev libjpeg-dev p7zip-full p7zip-rar unrar-free libgl1 python3-pyqt5 && \
python -m pip install --upgrade pip && \
python -m pip install --upgrade -r requirements.txt
```
You'll need to install various tools to access important but optional features. Close and re-open KCC to get KCC to detect them.
#### Optional dependencies ### KindleGen
- Qt platform integration plugin for Deepin Desktop Environment
```bash
$ sudo apt-get install qt5dxcb-plugin
```
- KindleGen ~~[(deprecated link)](http://www.amazon.com/gp/feature.html?ie=UTF8&docId=1000765211)~~ v2.9+ (For MOBI generation) On Windows and macOS, install [Kindle Previewer](https://www.amazon.com/Kindle-Previewer/b?ie=UTF8&node=21381691011) and `kindlegen` will be autodetected from it.
- should be placed in a directory reachable by your _PATH_ or in _KCC_ directory
- `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)
- [7z](http://www.7-zip.org/download.html) *(For CBZ/ZIP, CBR/RAR, 7z/CB7 support)*
- Unrar (no rar in 7z on Fedora)
If you have issues detecting it, get stuck on the MOBI conversion step, or use Linux AppImage or Flatpak, refer to the wiki: https://github.com/ciromattia/kcc/wiki/Installation#kindlegen
### 7-Zip
This is no longer required as of KCC 6.1.
If you still need it, refer to the wiki: https://github.com/ciromattia/kcc/wiki/Installation#7-zip
## INPUT FORMATS ## INPUT FORMATS
**KCC** can understand and convert, at the moment, the following input types: **KCC** can understand and convert, at the moment, the following input types:
@@ -126,14 +118,15 @@ sudo apt-get install python3 p7zip-full python3-pil python3-psutil python3-slugi
'KoAO': ("Kobo Aura ONE", (1404, 1872), Palette16, 1.8), 'KoAO': ("Kobo Aura ONE", (1404, 1872), Palette16, 1.8),
'KoN': ("Kobo Nia", (758, 1024), Palette16, 1.8), 'KoN': ("Kobo Nia", (758, 1024), Palette16, 1.8),
'KoC': ("Kobo Clara HD/Kobo Clara 2E", (1072, 1448), Palette16, 1.8), 'KoC': ("Kobo Clara HD/Kobo Clara 2E", (1072, 1448), Palette16, 1.8),
'KoCC': ("Kobo Clara Colour", (1072, 1448), Palette16, 1.8),
'KoL': ("Kobo Libra H2O/Kobo Libra 2", (1264, 1680), Palette16, 1.8), 'KoL': ("Kobo Libra H2O/Kobo Libra 2", (1264, 1680), Palette16, 1.8),
'KoLC': ("Kobo Libra Colour", (1264, 1680), Palette16, 1.8),
'KoF': ("Kobo Forma", (1440, 1920), Palette16, 1.8), 'KoF': ("Kobo Forma", (1440, 1920), Palette16, 1.8),
'KoS': ("Kobo Sage", (1440, 1920), Palette16, 1.8), 'KoS': ("Kobo Sage", (1440, 1920), Palette16, 1.8),
'KoE': ("Kobo Elipsa", (1404, 1872), Palette16, 1.8), 'KoE': ("Kobo Elipsa", (1404, 1872), Palette16, 1.8),
'OTHER': ("Other", (0, 0), Palette16, 1.8), 'OTHER': ("Other", (0, 0), Palette16, 1.8),
``` ```
### Standalone `kcc-c2e.py` usage: ### Standalone `kcc-c2e.py` usage:
``` ```
@@ -214,8 +207,61 @@ OTHER:
-h, --help Show this help message and exit -h, --help Show this help message and exit
``` ```
## INSTALL FROM SOURCE
This section is for developers who want to contribute to KCC or power users who want to run the latest code without waiting for an official release.
Easiest to use [GitHub Desktop](https://desktop.github.com) to clone the KCC repo. From GitHub Desktop, click on `Repository` in the toolbar, then `Command Prompt` (Windows)/`Terminal` (Mac) to open a window in the KCC repo.
Depending on your system [Python](https://www.python.org) may be called either `python` or `python3`. We use virtual environments (venv) to manage dependencies.
If you want to edit the code, a good code editor is [VS Code](https://code.visualstudio.com).
If you want to edit the `.ui` files, use [Qt Creator](https://www.qt.io/download-qt-installer-oss), included in **Qt for desktop development**.
Then use the `gen_ui_files` scripts to autogenerate the python UI.
### Windows install from source
One time setup and running for the first time:
```
python -m venv venv
venv\Scripts\activate.bat
pip install -r requirements.txt
python kcc.py
```
Every time you close Command Prompt, you will need to re-activate the virtual environment and re-run:
```
venv\Scripts\activate.bat
python kcc.py
```
### macOS install from source
One time setup and running for the first time:
```
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
python kcc.py
```
Every time you close Terminal, you will need to reactivate the virtual environment and re-run:
```
source venv/bin/activate
python kcc.py
```
## CREDITS ## CREDITS
**KCC** is made by [Ciro Mattia Gonano](http://github.com/ciromattia), [Paweł Jastrzębski](http://github.com/AcidWeb) and [Darodi](http://github.com/darodi) . **KCC** is made by
- [Ciro Mattia Gonano](http://github.com/ciromattia)
- [Paweł Jastrzębski](http://github.com/AcidWeb)
- [Darodi](http://github.com/darodi)
- [Alex Xu](http://github.com/axu2)
This script born as a cross-platform alternative to `KindleComicParser` by **Dc5e** (published [here](http://www.mobileread.com/forums/showthread.php?t=192783)). This script born as a cross-platform alternative to `KindleComicParser` by **Dc5e** (published [here](http://www.mobileread.com/forums/showthread.php?t=192783)).

View File

@@ -5,11 +5,12 @@ channels:
dependencies: dependencies:
- python=3.11 - python=3.11
- 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
- distro - distro
- natsort[fast]>=8.4.0
- pip - pip
- pip: - pip:
- mozjpeg-lossless-optimization>=1.1.2 - mozjpeg-lossless-optimization>=1.1.2
- PyQt5>=5.6.0 - pyside6>=6.5.1

View File

@@ -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

View File

@@ -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

View File

@@ -22,7 +22,7 @@ import sys
if sys.version_info < (3, 8, 0): if sys.version_info < (3, 8, 0):
print('ERROR: This is a Python 3.8+ script!') print('ERROR: This is a Python 3.8+ script!')
exit(1) sys.exit(1)
from multiprocessing import freeze_support, set_start_method from multiprocessing import freeze_support, set_start_method
from kindlecomicconverter.startup import startC2E from kindlecomicconverter.startup import startC2E

View File

@@ -22,7 +22,7 @@ import sys
if sys.version_info < (3, 8, 0): if sys.version_info < (3, 8, 0):
print('ERROR: This is a Python 3.8+ script!') print('ERROR: This is a Python 3.8+ script!')
exit(1) sys.exit(1)
from multiprocessing import freeze_support, set_start_method from multiprocessing import freeze_support, set_start_method
from kindlecomicconverter.startup import startC2P from kindlecomicconverter.startup import startC2P

8
kcc.py
View File

@@ -22,7 +22,7 @@ import sys
if sys.version_info < (3, 8, 0): if sys.version_info < (3, 8, 0):
print('ERROR: This is a Python 3.8+ script!') print('ERROR: This is a Python 3.8+ script!')
exit(1) sys.exit(1)
# OS specific workarounds # OS specific workarounds
import os import os
@@ -50,7 +50,13 @@ elif sys.platform.startswith('win'):
win_paths = [ win_paths = [
os.path.expandvars('%LOCALAPPDATA%\\Amazon\\KC2'), os.path.expandvars('%LOCALAPPDATA%\\Amazon\\KC2'),
os.path.expandvars('%LOCALAPPDATA%\\Amazon\\Kindle Previewer 3\\lib\\fc\\bin\\'), os.path.expandvars('%LOCALAPPDATA%\\Amazon\\Kindle Previewer 3\\lib\\fc\\bin\\'),
os.path.expandvars('%UserProfile%\\Kindle Previewer 3\\lib\\fc\\bin\\'),
'C:\\Apps\\Kindle Previewer 3\\lib\\fc\\bin',
'D:\\Apps\\Kindle Previewer 3\\lib\\fc\\bin',
'E:\\Apps\\Kindle Previewer 3\\lib\\fc\\bin',
'C:\\Program Files\\7-Zip', 'C:\\Program Files\\7-Zip',
'D:\\Program Files\\7-Zip',
'E:\\Program Files\\7-Zip',
] ]
if getattr(sys, 'frozen', False): if getattr(sys, 'frozen', False):
os.environ['PATH'] += os.pathsep + os.pathsep.join(win_paths) os.environ['PATH'] += os.pathsep + os.pathsep.join(win_paths)

View File

@@ -16,25 +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 re
import subprocess
import sys import sys
from urllib.parse import unquote from urllib.parse import unquote
from urllib.request import urlretrieve, 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
import requests
# 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 Process
from copy import copy from copy import copy
from distutils.version import StrictVersion from packaging.version import Version
from raven import Client from raven import Client
from tempfile import gettempdir from tempfile import gettempdir
from .shared import md5Checksum, HTMLStripper, sanitizeTrace, walkLevel
from .shared import HTMLStripper, sanitizeTrace, walkLevel, subprocess_run
from . import __version__ from . import __version__
from . import comic2ebook from . import comic2ebook
from . import metadata from . import metadata
@@ -44,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)
@@ -52,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)
@@ -66,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:
@@ -82,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)
@@ -90,40 +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.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):
@@ -139,18 +140,15 @@ class VersionThread(QtCore.QThread):
def run(self): def run(self):
try: try:
last_version_url = urlopen("https://api.github.com/repos/ciromattia/kcc/releases/latest") json_parser = requests.get("https://api.github.com/repos/ciromattia/kcc/releases/latest").json()
data = last_version_url.read()
encoding = last_version_url.info().get_content_charset('utf-8')
json_parser = json.loads(data.decode(encoding))
html_url = json_parser["html_url"] html_url = json_parser["html_url"]
latest_version = json_parser["tag_name"] latest_version = json_parser["tag_name"]
latest_version = re.sub(r'^v', "", latest_version) latest_version = re.sub(r'^v', "", latest_version)
if ("b" not in __version__ and StrictVersion(latest_version) > StrictVersion(__version__)) \ if ("b" not in __version__ and Version(latest_version) > Version(__version__)) \
or ("b" in __version__ or ("b" in __version__
and StrictVersion(latest_version) >= StrictVersion(re.sub(r'b.*', '', __version__))): and Version(latest_version) >= Version(re.sub(r'b.*', '', __version__))):
MW.addMessage.emit('<a href="' + html_url + '"><b>The new version is available!</b></a>', 'warning', MW.addMessage.emit('<a href="' + html_url + '"><b>The new version is available!</b></a>', 'warning',
False) False)
except Exception: except Exception:
@@ -223,28 +221,28 @@ class WorkerThread(QtCore.QThread):
options.format = gui_current_format options.format = gui_current_format
if GUI.mangaBox.isChecked(): if GUI.mangaBox.isChecked():
options.righttoleft = True options.righttoleft = True
if GUI.rotateBox.checkState() == 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
@@ -256,13 +254,15 @@ 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())
options.customheight = str(GUI.heightBox.value()) options.customheight = str(GUI.heightBox.value())
if GUI.targetDirectory != '':
options.output = GUI.targetDirectory
for i in range(GUI.jobList.count()): for i in range(GUI.jobList.count()):
# Make sure that we don't consider any system message as job to do # Make sure that we don't consider any system message as job to do
@@ -431,7 +431,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):
@@ -440,7 +440,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 = getattr(QtWidgets.QSystemTrayIcon.MessageIcon, icon)
if self.supportsMessages() and not MW.isActiveWindow(): if self.supportsMessages() and not MW.isActiveWindow():
self.showMessage('Kindle Comic Converter', message, icon) self.showMessage('Kindle Comic Converter', message, icon)
@@ -462,7 +462,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
if self.needClean: if self.needClean:
self.needClean = False self.needClean = False
GUI.jobList.clear() GUI.jobList.clear()
if self.sevenzip: if self.tar or self.sevenzip:
fnames = QtWidgets.QFileDialog.getOpenFileNames(MW, 'Select file', self.lastPath, fnames = QtWidgets.QFileDialog.getOpenFileNames(MW, 'Select file', self.lastPath,
'Comic (*.cbz *.cbr *.cb7 *.zip *.rar *.7z *.pdf);;All (*.*)') 'Comic (*.cbz *.cbr *.cb7 *.zip *.rar *.7z *.pdf);;All (*.*)')
else: else:
@@ -492,6 +492,8 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
else: else:
fname = [''] fname = ['']
self.showDialog("Editor is disabled due to a lack of 7z.", 'error') self.showDialog("Editor is disabled due to a lack of 7z.", 'error')
self.addMessage('<a href="https://github.com/ciromattia/kcc#7-zip">Install 7z (link)</a>'
' to enable metadata editing.', 'warning')
if fname[0] != '': if fname[0] != '':
if sys.platform.startswith('win'): if sys.platform.startswith('win'):
sname = fname[0].replace('/', '\\') sname = fname[0].replace('/', '\\')
@@ -550,7 +552,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)
@@ -558,7 +560,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)
@@ -638,6 +640,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
self.modeChange(2) self.modeChange(2)
else: else:
self.modeChange(1) self.modeChange(1)
GUI.colorBox.setChecked(profile['ForceColor'])
self.changeFormat() self.changeFormat()
GUI.gammaSlider.setValue(0) GUI.gammaSlider.setValue(0)
self.changeGamma(0) self.changeGamma(0)
@@ -672,7 +675,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
def addMessage(self, message, icon, replace=False): def addMessage(self, message, icon, replace=False):
if icon != '': if icon != '':
icon = eval('self.icons.' + icon) icon = getattr(self.icons, icon)
item = QtWidgets.QListWidgetItem(icon, ' ' + self.stripTags(message)) item = QtWidgets.QListWidgetItem(icon, ' ' + self.stripTags(message))
else: else:
item = QtWidgets.QListWidgetItem(' ' + self.stripTags(message)) item = QtWidgets.QListWidgetItem(' ' + self.stripTags(message))
@@ -690,7 +693,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
def showDialog(self, message, kind): def showDialog(self, message, kind):
if kind == 'error': if kind == 'error':
QtWidgets.QMessageBox.critical(MW, 'KCC - Error', message, QtWidgets.QMessageBox.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,
@@ -718,7 +721,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'):
@@ -752,7 +755,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
def display_kindlegen_missing(self): def display_kindlegen_missing(self):
self.addMessage( self.addMessage(
'<a href="https://github.com/ciromattia/kcc/wiki/Installation#kindlegen"><b>Cannot find KindleGen</b></a>: MOBI conversion is unavailable!', '<a href="https://github.com/ciromattia/kcc#kindlegen"><b>Install KindleGen (link)</b></a> to enable MOBI conversion for Kindles!',
'error' 'error'
) )
@@ -771,23 +774,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()
@@ -802,7 +805,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
self.needClean = False self.needClean = False
GUI.jobList.clear() GUI.jobList.clear()
formats = ['.pdf'] formats = ['.pdf']
if self.sevenzip: if self.tar or self.sevenzip:
formats.extend(['.cb7', '.7z', '.cbz', '.zip', '.cbr', '.rar']) formats.extend(['.cb7', '.7z', '.cbz', '.zip', '.cbr', '.rar'])
if os.path.isdir(message): if os.path.isdir(message):
GUI.jobList.addItem(message) GUI.jobList.addItem(message)
@@ -839,32 +842,21 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
os.chmod('/usr/local/bin/kindlegen', 0o755) os.chmod('/usr/local/bin/kindlegen', 0o755)
except Exception: except Exception:
pass pass
kindleGenExitCode = Popen('kindlegen -locale en', stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True) try:
kindleGenExitCode.communicate() versionCheck = subprocess_run(['kindlegen', '-locale', 'en'], stdout=PIPE, stderr=STDOUT, encoding='UTF-8')
if kindleGenExitCode.returncode == 0:
self.kindleGen = True self.kindleGen = True
versionCheck = Popen('kindlegen -locale en', stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True) for line in versionCheck.stdout.splitlines():
for line in versionCheck.stdout:
line = line.decode("utf-8")
if 'Amazon kindlegen' in line: if 'Amazon kindlegen' in line:
versionCheck = line.split('V')[1].split(' ')[0] versionCheck = line.split('V')[1].split(' ')[0]
if StrictVersion(versionCheck) < StrictVersion('2.9'): if Version(versionCheck) < Version('2.9'):
self.addMessage('Your <a href="https://www.amazon.com/b?node=23496309011">KindleGen</a>' self.addMessage('Your <a href="https://www.amazon.com/b?node=23496309011">KindleGen</a>'
' is outdated! MOBI conversion might fail.', 'warning') ' is outdated! MOBI conversion might fail.', 'warning')
break break
where_command = 'where kindlegen.exe' except FileNotFoundError:
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:
self.kindleGen = False self.kindleGen = False
if startup: if startup:
self.display_kindlegen_missing() self.display_kindlegen_missing()
def __init__(self, kccapp, kccwindow): def __init__(self, kccapp, kccwindow):
global APP, MW, GUI global APP, MW, GUI
APP = kccapp APP = kccapp
@@ -873,7 +865,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
self.setupUi(MW) self.setupUi(MW)
self.editor = KCCGUI_MetaEditor() self.editor = KCCGUI_MetaEditor()
self.icons = Icons() self.icons = Icons()
self.settings = QtCore.QSettings('KindleComicConverter', 'KindleComicConverter') self.settings = QtCore.QSettings('ciromattia', 'kcc')
self.settingsVersion = self.settings.value('settingsVersion', '', type=str) self.settingsVersion = self.settings.value('settingsVersion', '', type=str)
self.lastPath = self.settings.value('lastPath', '', type=str) self.lastPath = self.settings.value('lastPath', '', type=str)
self.lastDevice = self.settings.value('lastDevice', 0, type=int) self.lastDevice = self.settings.value('lastDevice', 0, type=int)
@@ -906,10 +898,10 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
elif sys.platform.startswith('darwin'): elif sys.platform.startswith('darwin'):
for element in ['editorButton', 'wikiButton', 'directoryButton', 'clearButton', 'fileButton', 'deviceBox', for element in ['editorButton', 'wikiButton', 'directoryButton', 'clearButton', 'fileButton', 'deviceBox',
'convertButton', 'formatBox']: 'convertButton', 'formatBox']:
eval('GUI.' + element).setMinimumSize(QtCore.QSize(0, 0)) getattr(GUI, element).setMinimumSize(QtCore.QSize(0, 0))
GUI.gridLayout.setContentsMargins(-1, -1, -1, -1) GUI.gridLayout.setContentsMargins(-1, -1, -1, -1)
for element in ['gridLayout_2', 'gridLayout_3', 'gridLayout_4', 'horizontalLayout', 'horizontalLayout_2']: for element in ['gridLayout_2', 'gridLayout_3', 'gridLayout_4', 'horizontalLayout', 'horizontalLayout_2']:
eval('GUI.' + element).setContentsMargins(-1, 0, -1, 0) getattr(GUI, element).setContentsMargins(-1, 0, -1, 0)
if self.windowSize == '0x0': if self.windowSize == '0x0':
MW.resize(500, 500) MW.resize(500, 500)
@@ -924,94 +916,100 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
self.profiles = { self.profiles = {
"Kindle Oasis 2/3": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, "Kindle Oasis 9/10": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': True, 'Label': 'KO'}, 'DefaultUpscale': True, 'ForceColor': False, 'Label': 'KO'},
"Kindle Oasis": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, "Kindle Oasis 8": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': True, 'Label': 'KV'}, 'DefaultUpscale': True, 'ForceColor': False, 'Label': 'KV'},
"Kindle Voyage": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, "Kindle Voyage": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': True, 'Label': 'KV'}, 'DefaultUpscale': True, 'ForceColor': False, 'Label': 'KV'},
"Kindle Scribe": { "Kindle Scribe": {
'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': False, 'Label': 'KS', 'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': False, 'ForceColor': 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, 'ForceColor': False, 'Label': 'K11',
}, },
"Kindle PW 5": { "Kindle PW 11": {
'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': True, 'Label': 'KPW5', 'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': True, 'ForceColor': False, 'Label': 'KPW5',
}, },
"Kindle PW 3/4": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, "Kindle PW 7/10": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': True, 'Label': 'KV'}, 'DefaultUpscale': True, 'ForceColor': False, 'Label': 'KV'},
"Kindle PW 1/2": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, "Kindle PW 5/6": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': False, 'Label': 'KPW'}, 'DefaultUpscale': False, 'ForceColor': False, 'Label': 'KPW'},
"Kindle": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, "Kindle 4/5/7/8/10": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': False, 'Label': 'K578'}, 'DefaultUpscale': False, 'ForceColor': False, 'Label': 'K578'},
"Kindle DX/DXG": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 2, "Kindle DX": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 2,
'DefaultUpscale': False, 'Label': 'KDX'}, 'DefaultUpscale': False, 'ForceColor': False, 'Label': 'KDX'},
"Kobo Mini/Touch": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, "Kobo Mini/Touch": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1,
'DefaultUpscale': False, 'Label': 'KoMT'}, 'DefaultUpscale': False, 'ForceColor': False, 'Label': 'KoMT'},
"Kobo Glo": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, "Kobo Glo": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1,
'DefaultUpscale': False, 'Label': 'KoG'}, 'DefaultUpscale': False, 'ForceColor': False, 'Label': 'KoG'},
"Kobo Glo HD": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, "Kobo Glo HD": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1,
'DefaultUpscale': False, 'Label': 'KoGHD'}, 'DefaultUpscale': False, 'ForceColor': False, 'Label': 'KoGHD'},
"Kobo Aura": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, "Kobo Aura": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1,
'DefaultUpscale': False, 'Label': 'KoA'}, 'DefaultUpscale': False, 'ForceColor': False, 'Label': 'KoA'},
"Kobo Aura HD": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, "Kobo Aura HD": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1,
'DefaultUpscale': True, 'Label': 'KoAHD'}, 'DefaultUpscale': True, 'ForceColor': False, 'Label': 'KoAHD'},
"Kobo Aura H2O": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, "Kobo Aura H2O": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1,
'DefaultUpscale': True, 'Label': 'KoAH2O'}, 'DefaultUpscale': True, 'ForceColor': False, 'Label': 'KoAH2O'},
"Kobo Aura ONE": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, "Kobo Aura ONE": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1,
'DefaultUpscale': True, 'Label': 'KoAO'}, 'DefaultUpscale': True, 'ForceColor': False, 'Label': 'KoAO'},
"Kobo Clara HD": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, "Kobo Clara HD": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1,
'DefaultUpscale': True, 'Label': 'KoC'}, 'DefaultUpscale': True, 'ForceColor': False, 'Label': 'KoC'},
"Kobo Libra H2O": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, "Kobo Libra H2O": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1,
'DefaultUpscale': True, 'Label': 'KoL'}, 'DefaultUpscale': True, 'ForceColor': False, 'Label': 'KoL'},
"Kobo Forma": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, "Kobo Forma": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1,
'DefaultUpscale': True, 'Label': 'KoF'}, 'DefaultUpscale': True, 'ForceColor': False, 'Label': 'KoF'},
"Kindle 1": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 0, "Kindle 1": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': False, 'Label': 'K1'}, 'DefaultUpscale': False, 'ForceColor': False, 'Label': 'K1'},
"Kindle 2": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 0, "Kindle 2": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': False, 'Label': 'K2'}, 'DefaultUpscale': False, 'ForceColor': False, 'Label': 'K2'},
"Kindle Keyboard": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 0, "Kindle Keyboard": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': False, 'Label': 'K34'}, 'DefaultUpscale': False, 'ForceColor': False, 'Label': 'K34'},
"Kindle Touch": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 0, "Kindle Touch": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': False, 'Label': 'K34'}, 'DefaultUpscale': False, 'ForceColor': False, 'Label': 'K34'},
"Kobo Nia": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, 'DefaultUpscale': True, "Kobo Nia": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, 'DefaultUpscale': True, 'ForceColor': False,
'Label': 'KoN'}, 'Label': 'KoN'},
"Kobo Clara 2E": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, 'DefaultUpscale': True, "Kobo Clara 2E": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, 'DefaultUpscale': True, 'ForceColor': False,
'Label': 'KoC'}, 'Label': 'KoC'},
"Kobo Libra 2": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, 'DefaultUpscale': True, "Kobo Clara Colour": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, 'DefaultUpscale': True, 'ForceColor': True,
'Label': 'KoCC'},
"Kobo Libra 2": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, 'DefaultUpscale': True, 'ForceColor': False,
'Label': 'KoL'}, 'Label': 'KoL'},
"Kobo Sage": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, 'DefaultUpscale': True, "Kobo Libra Colour": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, 'DefaultUpscale': True, 'ForceColor': True,
'Label': 'KoLC'},
"Kobo Sage": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, 'DefaultUpscale': True, 'ForceColor': False,
'Label': 'KoS'}, 'Label': 'KoS'},
"Kobo Elipsa": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, 'DefaultUpscale': True, "Kobo Elipsa": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, 'DefaultUpscale': True, 'ForceColor': False,
'Label': 'KoE'}, 'Label': 'KoE'},
"Other": {'PVOptions': False, 'ForceExpert': True, 'DefaultFormat': 1, 'DefaultUpscale': False, "Other": {'PVOptions': False, 'ForceExpert': True, 'DefaultFormat': 1, 'DefaultUpscale': False, 'ForceColor': False,
'Label': 'OTHER'}, 'Label': 'OTHER'},
} }
profilesGUI = [ profilesGUI = [
"Kindle Oasis 2/3",
"Kindle PW 5",
"Kindle 11",
"Kindle Scribe", "Kindle Scribe",
"Kindle 11",
"Kindle PW 11",
"Kindle Oasis 9/10",
"Separator", "Separator",
"Kobo Clara 2E", "Kobo Clara 2E",
"Kobo Clara Colour",
"Kobo Sage", "Kobo Sage",
"Kobo Libra 2", "Kobo Libra 2",
"Kobo Libra Colour",
"Kobo Elipsa", "Kobo Elipsa",
"Kobo Nia", "Kobo Nia",
"Separator", "Separator",
"Other", "Other",
"Separator", "Separator",
"Kindle Oasis", "Kindle Oasis 8",
"Kindle PW 7/10",
"Kindle Voyage",
"Kindle PW 5/6",
"Kindle 4/5/7/8/10",
"Kindle Touch", "Kindle Touch",
"Kindle Keyboard", "Kindle Keyboard",
"Kindle DX/DXG", "Kindle DX",
"Kindle PW 3/4",
"Kindle PW 1/2",
"Kindle Voyage",
"Kindle 2", "Kindle 2",
"Kindle 1", "Kindle 1",
"Kindle",
"Separator", "Separator",
"Kobo Aura", "Kobo Aura",
"Kobo Aura ONE", "Kobo Aura ONE",
@@ -1029,7 +1027,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)
@@ -1039,14 +1037,19 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
self.addMessage('Since you are a new user of <b>KCC</b> please see few ' self.addMessage('Since you are a new user of <b>KCC</b> please see few '
'<a href="https://github.com/ciromattia/kcc/wiki/Important-tips">important tips</a>.', '<a href="https://github.com/ciromattia/kcc/wiki/Important-tips">important tips</a>.',
'info') 'info')
process = Popen('7z', stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True) try:
process.communicate() subprocess_run(['tar'], stdout=PIPE, stderr=STDOUT)
if process.returncode == 0 or process.returncode == 7: self.tar = True
except FileNotFoundError:
self.tar = False
try:
subprocess_run(['7z'], stdout=PIPE, stderr=STDOUT)
self.sevenzip = True self.sevenzip = True
else: except FileNotFoundError:
self.sevenzip = False self.sevenzip = False
self.addMessage('<a href="https://github.com/ciromattia/kcc/wiki/Installation#7-zip">Cannot find 7z</a>!' if not self.tar:
' CBZ/CBR/ZIP/etc processing disabled.', 'warning') self.addMessage('<a href="https://github.com/ciromattia/kcc#7-zip">Install 7z (link)</a>'
' to enable CBZ/CBR/ZIP/etc processing.', 'warning')
self.detectKindleGen(True) self.detectKindleGen(True)
APP.messageFromOtherInstance.connect(self.handleMessage) APP.messageFromOtherInstance.connect(self.handleMessage)
@@ -1088,7 +1091,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
else: else:
GUI.deviceBox.addItem(self.icons.deviceKindle, profile) GUI.deviceBox.addItem(self.icons.deviceKindle, profile)
for f in self.formats: for f in self.formats:
GUI.formatBox.addItem(eval('self.icons.' + self.formats[f]['icon'] + 'Format'), f) GUI.formatBox.addItem(getattr(self.icons, self.formats[f]['icon'] + 'Format'), f)
if self.lastDevice > GUI.deviceBox.count(): if self.lastDevice > GUI.deviceBox.count():
self.lastDevice = 0 self.lastDevice = 0
if profilesGUI[self.lastDevice] == "Separator": if profilesGUI[self.lastDevice] == "Separator":
@@ -1114,8 +1117,8 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
self.changeCroppingPower(int(self.options[option])) self.changeCroppingPower(int(self.options[option]))
else: else:
try: try:
if eval('GUI.' + str(option)).isEnabled(): if getattr(GUI, option).isEnabled():
eval('GUI.' + str(option)).setCheckState(self.options[option]) getattr(GUI, option).setCheckState(Qt.CheckState(self.options[option]))
except AttributeError: except AttributeError:
pass pass
self.worker.sync() self.worker.sync()
@@ -1189,7 +1192,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

View File

@@ -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

View File

@@ -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

View File

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

View File

@@ -19,6 +19,8 @@
# #
import os import os
import pathlib
import re
import sys import sys
from argparse import ArgumentParser from argparse import ArgumentParser
from time import strftime, gmtime from time import strftime, gmtime
@@ -31,16 +33,14 @@ from tempfile import mkdtemp, gettempdir, TemporaryFile
from shutil import move, copytree, rmtree, copyfile from shutil import move, copytree, rmtree, copyfile
from multiprocessing import Pool from multiprocessing import Pool
from uuid import uuid4 from uuid import uuid4
from natsort import os_sorted
from slugify import slugify as slugify_ext from slugify import slugify as slugify_ext
from PIL import Image from PIL import Image, ImageFile
from subprocess import STDOUT, PIPE from subprocess import STDOUT, PIPE
from psutil import Popen, virtual_memory, disk_usage from psutil import virtual_memory, disk_usage
from html import escape as hescape from html import escape as hescape
try:
from PyQt5 import QtCore from .shared import md5Checksum, getImageFileName, walkSort, walkLevel, sanitizeTrace, subprocess_run
except ImportError:
QtCore = None
from .shared import md5Checksum, getImageFileName, walkSort, walkLevel, sanitizeTrace
from . import comic2panel from . import comic2panel
from . import image from . import image
from . import comicarchive from . import comicarchive
@@ -50,6 +50,8 @@ from . import metadata
from . import kindle from . import kindle
from . import __version__ from . import __version__
ImageFile.LOAD_TRUNCATED_IMAGES = True
def main(argv=None): def main(argv=None):
global options global options
@@ -642,6 +644,9 @@ def getWorkFolder(afile):
try: try:
cbx = comicarchive.ComicArchive(afile) cbx = comicarchive.ComicArchive(afile)
path = cbx.extract(workdir) path = cbx.extract(workdir)
tdir = os.listdir(workdir)
if 'ComicInfo.xml' in tdir:
tdir.remove('ComicInfo.xml')
except OSError as e: except OSError as e:
rmtree(workdir, True) rmtree(workdir, True)
raise UserWarning(e) raise UserWarning(e)
@@ -671,11 +676,9 @@ def getOutputFilename(srcpath, wantedname, ext, tomenumber):
filename = srcpath + tomenumber + ext filename = srcpath + tomenumber + ext
else: else:
if 'Ko' in options.profile and options.format == 'EPUB': if 'Ko' in options.profile and options.format == 'EPUB':
path = srcpath.split(os.path.sep) src = pathlib.Path(srcpath)
path[-1] = ''.join(e for e in path[-1].split('.')[0] if e.isalnum()) + tomenumber + ext name = re.sub(r'\W+', '_', src.stem) + tomenumber + ext
if not path[-1].split('.')[0]: filename = src.with_name(name)
path[-1] = 'KCCPlaceholder' + tomenumber + ext
filename = os.path.sep.join(path)
else: else:
filename = os.path.splitext(srcpath)[0] + tomenumber + ext filename = os.path.splitext(srcpath)[0] + tomenumber + ext
if os.path.isfile(filename): if os.path.isfile(filename):
@@ -689,7 +692,6 @@ def getOutputFilename(srcpath, wantedname, ext, tomenumber):
def getComicInfo(path, originalpath): def getComicInfo(path, originalpath):
xmlPath = os.path.join(path, 'ComicInfo.xml') xmlPath = os.path.join(path, 'ComicInfo.xml')
options.authors = ['KCC']
options.chapters = [] options.chapters = []
options.summary = '' options.summary = ''
titleSuffix = '' titleSuffix = ''
@@ -701,13 +703,18 @@ def getComicInfo(path, originalpath):
options.title = os.path.splitext(os.path.basename(originalpath))[0] options.title = os.path.splitext(os.path.basename(originalpath))[0]
else: else:
defaultTitle = False defaultTitle = False
if options.author == 'defaultauthor':
defaultAuthor = True
options.authors = ['KCC']
else:
defaultAuthor = False
options.authors = [options.author]
if os.path.exists(xmlPath): if os.path.exists(xmlPath):
try: try:
xml = metadata.MetadataParser(xmlPath) xml = metadata.MetadataParser(xmlPath)
except Exception: except Exception:
os.remove(xmlPath) os.remove(xmlPath)
return return
options.authors = []
if xml.data['Title']: if xml.data['Title']:
options.title = hescape(xml.data['Title']) options.title = hescape(xml.data['Title'])
elif defaultTitle: elif defaultTitle:
@@ -718,15 +725,17 @@ def getComicInfo(path, originalpath):
if xml.data['Number']: if xml.data['Number']:
titleSuffix += ' #' + xml.data['Number'].zfill(3) titleSuffix += ' #' + xml.data['Number'].zfill(3)
options.title += titleSuffix options.title += titleSuffix
for field in ['Writers', 'Pencillers', 'Inkers', 'Colorists']: if defaultAuthor:
for person in xml.data[field]: options.authors = []
options.authors.append(hescape(person)) for field in ['Writers', 'Pencillers', 'Inkers', 'Colorists']:
if len(options.authors) > 0: for person in xml.data[field]:
options.authors = list(set(options.authors)) options.authors.append(hescape(person))
options.authors.sort() if len(options.authors) > 0:
else: options.authors = list(set(options.authors))
options.authors = ['KCC'] options.authors.sort()
if xml.data['Bookmarks']: else:
options.authors = ['KCC']
if xml.data['Bookmarks'] and options.batchsplit == 0:
options.chapters = xml.data['Bookmarks'] options.chapters = xml.data['Bookmarks']
if xml.data['Summary']: if xml.data['Summary']:
options.summary = hescape(xml.data['Summary']) options.summary = hescape(xml.data['Summary'])
@@ -761,7 +770,7 @@ 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 i, name in enumerate(sorted(files)): for i, name in enumerate(os_sorted(files)):
splitname = os.path.splitext(name) splitname = os.path.splitext(name)
# file needs kcc at front AND back to avoid renaming issues # file needs kcc at front AND back to avoid renaming issues
@@ -894,7 +903,7 @@ def detectCorruption(tmppath, orgpath):
GUI.addMessage.emit('Source files are probably created by KCC. The second conversion will decrease quality.' GUI.addMessage.emit('Source files are probably created by KCC. The second conversion will decrease quality.'
, 'warning', False) , 'warning', False)
GUI.addMessage.emit('', '', False) GUI.addMessage.emit('', '', False)
if imageSmaller > imageNumber * 0.25 and not options.upscale and not options.stretch: if imageSmaller > imageNumber * 0.25 and not options.upscale and not options.stretch and options.profile != 'KS':
print("WARNING: More than 25% of images are smaller than target device resolution. " print("WARNING: More than 25% of images are smaller than target device resolution. "
"Consider enabling stretching or upscaling to improve readability.") "Consider enabling stretching or upscaling to improve readability.")
if GUI: if GUI:
@@ -946,7 +955,7 @@ def makeParser():
main_options.add_argument("-p", "--profile", action="store", dest="profile", default="KV", main_options.add_argument("-p", "--profile", action="store", dest="profile", default="KV",
help="Device profile (Available options: K1, K2, K34, K578, KDX, KPW, KPW5, KV, KO, " help="Device profile (Available options: K1, K2, K34, K578, KDX, KPW, KPW5, KV, KO, "
"K11, KS, KoMT, KoG, KoGHD, KoA, KoAHD, KoAH2O, KoAO, KoN, KoC, KoL, KoF, KoS, KoE)" "K11, KS, KoMT, KoG, KoGHD, KoA, KoAHD, KoAH2O, KoAO, KoN, KoC, KoCC, KoL, KoLC, KoF, KoS, KoE)"
" [Default=KV]") " [Default=KV]")
main_options.add_argument("-m", "--manga-style", action="store_true", dest="righttoleft", default=False, main_options.add_argument("-m", "--manga-style", action="store_true", dest="righttoleft", default=False,
help="Manga style (right-to-left reading and splitting)") help="Manga style (right-to-left reading and splitting)")
@@ -964,6 +973,8 @@ def makeParser():
help="Output generated file to specified directory or file") help="Output generated file to specified directory or file")
output_options.add_argument("-t", "--title", action="store", dest="title", default="defaulttitle", output_options.add_argument("-t", "--title", action="store", dest="title", default="defaulttitle",
help="Comic title [Default=filename or directory name]") help="Comic title [Default=filename or directory name]")
output_options.add_argument("-a", "--author", action="store", dest="author", default="defaultauthor",
help="Author name [Default=KCC]")
output_options.add_argument("-f", "--format", action="store", dest="format", default="Auto", output_options.add_argument("-f", "--format", action="store", dest="format", default="Auto",
help="Output format (Available options: Auto, MOBI, EPUB, CBZ, KFX, MOBI+EPUB) " help="Output format (Available options: Auto, MOBI, EPUB, CBZ, KFX, MOBI+EPUB) "
"[Default=Auto]") "[Default=Auto]")
@@ -1033,13 +1044,13 @@ def checkOptions(options):
if options.profile in ['K1', 'K2', 'K34', 'K578', 'KPW', 'KPW5', 'KV', 'KO', 'K11', 'KS']: if options.profile in ['K1', 'K2', 'K34', 'K578', 'KPW', 'KPW5', 'KV', 'KO', 'K11', 'KS']:
options.format = 'MOBI' options.format = 'MOBI'
elif options.profile in ['OTHER', 'KoMT', 'KoG', 'KoGHD', 'KoA', 'KoAHD', 'KoAH2O', 'KoAO', elif options.profile in ['OTHER', 'KoMT', 'KoG', 'KoGHD', 'KoA', 'KoAHD', 'KoAH2O', 'KoAO',
'KoN', 'KoC', 'KoL', 'KoF', 'KoS', 'KoE']: 'KoN', 'KoC', 'KoCC', 'KoL', 'KoLC', 'KoF', 'KoS', 'KoE']:
options.format = 'EPUB' options.format = 'EPUB'
elif options.profile in ['KDX']: elif options.profile in ['KDX']:
options.format = 'CBZ' options.format = 'CBZ'
if options.profile in ['K1', 'K2', 'K34', 'K578', 'KPW', 'KPW5', 'KV', 'KO', 'K11', 'KS']: if options.profile in ['K1', 'K2', 'K34', 'K578', 'KPW', 'KPW5', 'KV', 'KO', 'K11', 'KS']:
options.iskindle = True options.iskindle = True
elif options.profile in ['OTHER', 'KoMT', 'KoG', 'KoGHD', 'KoA', 'KoAHD', 'KoAH2O', 'KoAO', 'KoN', 'KoC', 'KoL', 'KoF', 'KoS', 'KoE']: elif options.profile in ['OTHER', 'KoMT', 'KoG', 'KoGHD', 'KoA', 'KoAHD', 'KoAH2O', 'KoAO', 'KoN', 'KoC', 'KoCC', 'KoL', 'KoLC', 'KoF', 'KoS', 'KoE']:
options.isKobo = True options.isKobo = True
# Other Kobo devices probably support synthetic spreads as well, but # Other Kobo devices probably support synthetic spreads as well, but
# they haven't been tested. # they haven't been tested.
@@ -1090,10 +1101,6 @@ def checkOptions(options):
image.ProfileData.Profiles["Custom"] = newProfile image.ProfileData.Profiles["Custom"] = newProfile
options.profile = "Custom" options.profile = "Custom"
options.profileData = image.ProfileData.Profiles[options.profile] options.profileData = image.ProfileData.Profiles[options.profile]
# kindle scribe conversion to mobi is limited in resolution by kindlegen, same with send to kindle and epub
if options.profile == 'KS' and (options.format == 'MOBI' or options.format == 'EPUB'):
options.profileData = list(options.profileData)
options.profileData[1] = (1440, 1920)
return options return options
@@ -1101,17 +1108,17 @@ def checkTools(source):
source = source.upper() source = source.upper()
if source.endswith('.CB7') or source.endswith('.7Z') or source.endswith('.RAR') or source.endswith('.CBR') or \ if source.endswith('.CB7') or source.endswith('.7Z') or source.endswith('.RAR') or source.endswith('.CBR') or \
source.endswith('.ZIP') or source.endswith('.CBZ'): source.endswith('.ZIP') or source.endswith('.CBZ'):
process = Popen('7z', stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True) try:
process.communicate() subprocess_run(['7z'], stdout=PIPE, stderr=STDOUT)
if process.returncode != 0 and process.returncode != 7: except FileNotFoundError:
print('ERROR: 7z is missing!') print('ERROR: 7z is missing!')
exit(1) sys.exit(1)
if options.format == 'MOBI': if options.format == 'MOBI':
kindleGenExitCode = Popen('kindlegen -locale en', stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True) try:
kindleGenExitCode.communicate() subprocess_run(['kindlegen', '-locale', 'en'], stdout=PIPE, stderr=STDOUT)
if kindleGenExitCode.returncode != 0: except FileNotFoundError:
print('ERROR: KindleGen is missing!') print('ERROR: KindleGen is missing!')
exit(1) sys.exit(1)
def checkPre(source): def checkPre(source):
@@ -1265,10 +1272,9 @@ def makeMOBIWorker(item):
kindlegenError = '' kindlegenError = ''
try: try:
if os.path.getsize(item) < 629145600: if os.path.getsize(item) < 629145600:
output = Popen('kindlegen -dont_append_source -locale en "' + item + '"', output = subprocess_run(['kindlegen', '-dont_append_source', '-locale', 'en', item],
stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True) stdout=PIPE, stderr=STDOUT, encoding='UTF-8')
for line in output.stdout: for line in output.stdout.splitlines():
line = line.decode('utf-8')
# ERROR: Generic error # ERROR: Generic error
if "Error(" in line: if "Error(" in line:
kindlegenErrorCode = 1 kindlegenErrorCode = 1
@@ -1279,7 +1285,6 @@ def makeMOBIWorker(item):
if kindlegenErrorCode > 0: if kindlegenErrorCode > 0:
break break
if ":I1036: Mobi file built successfully" in line: if ":I1036: Mobi file built successfully" in line:
output.communicate()
break break
else: else:
# ERROR: EPUB too big # ERROR: EPUB too big

View File

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

View File

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

View File

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

View File

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

View File

@@ -21,8 +21,10 @@
import os import os
from hashlib import md5 from hashlib import md5
from html.parser import HTMLParser from html.parser import HTMLParser
from distutils.version import StrictVersion import subprocess
from packaging.version import Version
from re import split from re import split
import sys
from traceback import format_tb from traceback import format_tb
@@ -100,11 +102,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 Version('6.5.1') > Version(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:
@@ -112,7 +114,7 @@ def dependencyCheck(level):
if level > 1: if level > 1:
try: try:
from psutil import __version__ as psutilVersion from psutil import __version__ as psutilVersion
if StrictVersion('5.0.0') > StrictVersion(psutilVersion): if Version('5.0.0') > Version(psutilVersion):
missing.append('psutil 5.0.0+') missing.append('psutil 5.0.0+')
except ImportError: except ImportError:
missing.append('psutil 5.0.0+') missing.append('psutil 5.0.0+')
@@ -121,16 +123,21 @@ def dependencyCheck(level):
from slugify import __version__ as slugifyVersion from slugify import __version__ as slugifyVersion
if isinstance(slugifyVersion, ModuleType): if isinstance(slugifyVersion, ModuleType):
slugifyVersion = slugifyVersion.__version__ slugifyVersion = slugifyVersion.__version__
if StrictVersion('1.2.1') > StrictVersion(slugifyVersion): if Version('1.2.1') > Version(slugifyVersion):
missing.append('python-slugify 1.2.1+') missing.append('python-slugify 1.2.1+')
except ImportError: except ImportError:
missing.append('python-slugify 1.2.1+') missing.append('python-slugify 1.2.1+')
try: try:
from PIL import __version__ as pillowVersion from PIL import __version__ as pillowVersion
if StrictVersion('5.2.0') > StrictVersion(pillowVersion): if Version('5.2.0') > Version(pillowVersion):
missing.append('Pillow 5.2.0+') missing.append('Pillow 5.2.0+')
except ImportError: except ImportError:
missing.append('Pillow 5.2.0+') missing.append('Pillow 5.2.0+')
if len(missing) > 0: if len(missing) > 0:
print('ERROR: ' + ', '.join(missing) + ' is not installed!') print('ERROR: ' + ', '.join(missing) + ' is not installed!')
exit(1) sys.exit(1)
def subprocess_run(command, **kwargs):
if (os.name == 'nt'):
kwargs.setdefault('creationflags', subprocess.CREATE_NO_WINDOW)
return subprocess.run(command, **kwargs)

View File

@@ -1,8 +1,10 @@
PyQt5>=5.6.0 PySide6>=6.5.1
Pillow>=5.2.0 Pillow>=5.2.0
psutil>=5.0.0 psutil>=5.9.5
requests>=2.31.0
python-slugify>=1.2.1 python-slugify>=1.2.1
raven>=6.0.0 raven>=6.0.0
# PyQt5-tools packaging>=23.2
mozjpeg-lossless-optimization>=1.1.2 mozjpeg-lossless-optimization>=1.1.2
distro natsort[fast]>=8.4.0
distro>=1.8.0

View File

@@ -11,10 +11,9 @@ Create EXE/APP:
""" """
import os import os
import platform
import sys import sys
import shutil
import setuptools import setuptools
import distutils.cmd
from kindlecomicconverter import __version__ from kindlecomicconverter import __version__
NAME = 'KindleComicConverter' NAME = 'KindleComicConverter'
@@ -23,7 +22,7 @@ VERSION = __version__
# noinspection PyUnresolvedReferences # noinspection PyUnresolvedReferences
class BuildBinaryCommand(distutils.cmd.Command): class BuildBinaryCommand(setuptools.Command):
description = 'build binary release' description = 'build binary release'
user_options = [] user_options = []
@@ -37,19 +36,19 @@ class BuildBinaryCommand(distutils.cmd.Command):
def run(self): def run(self):
VERSION = __version__ VERSION = __version__
if sys.platform == 'darwin': if sys.platform == 'darwin':
os.system('pyinstaller -y -F -i icons/comic2ebook.icns -n "Kindle Comic Converter" -w -s kcc.py') os.system('pyinstaller -y -D -i icons/comic2ebook.icns -n "Kindle Comic Converter" -w -s kcc.py')
# TODO /usr/bin/codesign --force -s "$MACOS_CERTIFICATE_NAME" --options runtime dist/Applications/Kindle\ Comic\ Converter.app -v # TODO /usr/bin/codesign --force -s "$MACOS_CERTIFICATE_NAME" --options runtime dist/Applications/Kindle\ Comic\ Converter.app -v
os.system('appdmg kcc.json dist/KindleComicConverter_osx_' + VERSION + '.dmg') os.system(f'appdmg kcc.json dist/kcc_macos_{platform.processor()}_{VERSION}.dmg')
exit(0) sys.exit(0)
elif sys.platform == 'win32': elif sys.platform == 'win32':
os.system('pyinstaller -y -F -i icons\\comic2ebook.ico -n KCC_' + VERSION + ' -w --noupx kcc.py') os.system('pyinstaller --hidden-import=_cffi_backend -y -F -i icons\\comic2ebook.ico -n KCC_' + VERSION + ' -w --noupx kcc.py')
exit(0) sys.exit(0)
elif sys.platform == 'linux': elif sys.platform == 'linux':
os.system( os.system(
'pyinstaller --hidden-import=queue -y -F -i icons/comic2ebook.ico -n kcc_linux_' + VERSION + ' kcc.py') 'pyinstaller --hidden-import=queue -y -F -i icons/comic2ebook.ico -n kcc_linux_' + VERSION + ' kcc.py')
exit(0) sys.exit(0)
else: else:
exit(0) sys.exit(0)
setuptools.setup( setuptools.setup(
@@ -75,12 +74,15 @@ 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',
'requests>=2.31.0',
'mozjpeg-lossless-optimization>=1.1.2', 'mozjpeg-lossless-optimization>=1.1.2',
'natsort[fast]>=8.4.0',
'distro',
], ],
classifiers=[], classifiers=[],
zip_safe=False, zip_safe=False,