1
0
mirror of https://github.com/ciromattia/kcc synced 2026-04-18 06:58:58 +00:00

Compare commits

..

120 Commits

Author SHA1 Message Date
darodi
85c1801417 change version 2023-05-13 18:40:24 +02:00
darodi
b28ee08d01 Merge pull request #515 from darodi/unrar_for_fedora
use unrar for fedora only
2023-05-13 16:25:06 +00:00
darodi
dba927c351 use unrar for fedora only 2023-05-13 18:24:18 +02:00
cookie99999
dd5fd621db Update README.md
Recommend to install p7zip-rar on Debian based systems, as it is needed to properly decompress rar files (As explained under "supported formats" on https://packages.debian.org/sid/p7zip-full).

(cherry picked from commit f3ee9b770b)
2023-05-13 18:20:01 +02:00
darodi
21a167b3ee limit kindle scribe image size to (1440, 1920) when using kindlegen (#516) 2023-05-13 15:50:27 +00:00
darodi
1c9eeee52d use unrar for fedora only 2023-05-13 14:11:50 +02:00
darodi
611ee31526 Merge pull request #513 from axu2/patch-3
add 7z to PATH
2023-05-13 01:46:01 +00:00
darodi
7c4fdf9d1a Merge pull request #514 from darodi/limit_kindle_scribe_size_with_kindlegen
limit kindle scribe image size to (1440, 1920) when using kindlegen
2023-05-13 01:44:12 +00:00
darodi
b14f59e77a limit kindle scribe image size to (1440, 1920) when using kindlegen 2023-05-13 03:40:52 +02:00
Alex Xu
b4ec0b4a74 update wording 2023-05-12 14:17:18 -07:00
Alex Xu
a90b4c82c5 add UI 7z path 2023-05-12 13:06:09 -07:00
Alex Xu
390d58bf08 add 7z to PATH 2023-05-12 12:20:37 -07:00
darodi
e1aa6cd0af Merge pull request #503 from darodi/epub_200MB_above_200MB
Even with EPUB-200MB option selected, created file is above 200MB
2023-04-17 17:50:42 +00:00
darodi
718fda2f0c Even with EPUB-200MB option selected, created file is above 200MB 2023-04-17 19:49:14 +02:00
darodi
9d7904f63b Merge pull request #502 from darodi/master_revert_499
revert #499
2023-04-17 17:46:57 +00:00
darodi
ec58964c7c Revert "Disable Panel View for kindle scribe"
This reverts commit 9bc1f92c8c.
2023-04-17 19:45:20 +02:00
darodi
0b687ebadc Merge pull request #499 from darodi/disable_panel_view_kindle_scribe
Disable Panel View for kindle scribe
2023-04-06 17:21:31 +00:00
darodi
9bc1f92c8c Disable Panel View for kindle scribe 2023-04-06 19:13:56 +02:00
darodi
56f6c6962f Merge pull request #491 from thatrobotdev/brew-gui-change
Updates GUI text for new Homebrew version
2023-03-27 22:48:27 +00:00
darodi
dfd15ab572 Updates GUI text for new Homebrew version 2023-03-28 00:46:25 +02:00
James Kerrane
5588ad9250 Updates GUI text for new Homebrew version 2023-03-28 00:46:25 +02:00
dependabot[bot]
0c2adb517e Update python-slugify requirement from <8.0.0,>=1.2.1 to >=1.2.1,<9.0.0 (#473)
Updates the requirements on [python-slugify](https://github.com/un33k/python-slugify) to permit the latest version.
- [Release notes](https://github.com/un33k/python-slugify/releases)
- [Changelog](https://github.com/un33k/python-slugify/blob/master/CHANGELOG.md)
- [Commits](https://github.com/un33k/python-slugify/compare/1.2.1...v8.0.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-27 22:16:07 +00:00
Alex Xu
9ab1cd359c remove transparency from image info 2023-03-27 22:04:35 +00:00
darodi
41d24e77e1 GUI delete option checkBox 2023-03-02 23:42:11 +01:00
Constantin Hong
17206ddf8b GUI delete option checkBox (closes #458) (#488)
* gui/func: add delete button (closes ciromattia#458)

* Update KCC_ui.py

fix typo

* GUI delete option checkBox

---------

Co-authored-by: darodi <4682830+darodi@users.noreply.github.com>
2023-03-02 23:38:27 +01:00
darodi
896c05a72f Add command line executables to CI/pipelines (#487) 2023-03-02 21:07:45 +01:00
darodi
f83106f35b Merge pull request #485 from Constantin1489/master
comic2ebook/func: Add a delete option
2023-03-02 17:34:16 +01:00
darodi
dfca136a2d comic2ebook/func: Add a delete option and function (closes #458) 2023-03-02 17:31:33 +01:00
Constantin Hong
92ced5f415 comic2ebook/func: Add a delete option and function (closes #458) 2023-03-02 17:12:46 +01:00
darodi
d18275d525 supporting Kindle Previewer (#486) 2023-03-02 17:09:45 +01:00
dependabot[bot]
c049adc3a1 Bump actions/checkout from 2 to 3 (#484)
Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 3.
- [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/v2...v3)

---
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-02-27 21:31:42 +01:00
darodi
a3ce26983e new AppImage (#483) 2023-02-23 18:26:05 +01:00
dependabot[bot]
fe8195cfed Bump actions/upload-artifact from 2 to 3 (#468)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 2 to 3.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v2...v3)

---
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>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-15 20:38:25 +01:00
darodi
3a1737e8d0 supporting Kindle Previewer 2023-01-22 18:27:50 +01:00
darodi
fb0c0231f3 build pipeline : drop pypi 2023-01-22 17:42:09 +01:00
darodi
2e807e23e1 update README.md 2023-01-22 14:25:37 +01:00
darodi
27841abb83 Update README.md 2023-01-22 14:08:44 +01:00
darodi
b71d056559 prepare pipeline for signing 2023-01-22 10:30:06 +01:00
darodi
a822dfa3ae Version bump 2023-01-21 11:57:55 +01:00
clach04
70de379987 Document CLI tools debian quick install
Command Line Tools can be used with pre-built dependencies.
2023-01-21 11:36:52 +01:00
Frédéric Brière
16e275bb1f Added --croppingminimum option to set a cropping minimum area ratio
Closes #342
2023-01-21 11:30:30 +01:00
darodi
b225de7b97 Enable Synthetic Spreads for the Kobo Forma. 2023-01-21 11:20:14 +01:00
Michael Shavit
4a89446914 Enable Synthetic Spreads for the Kobo Forma.
Rotating the device into landscape mode will cause it to display two
pages side by side. This is particularly useful for input comics whose
spreads are already split into two files.

See https://github.com/kobolabs/epub-spec#synthetic-spreads for
additional information on synthetic spreads.

(Tiny drive-by: Add Kobo format to list of profiles for which the AUTO
format is EPUB)
2023-01-21 11:20:14 +01:00
StudioEtrange
64521de577 replace move with copyfile 2023-01-21 11:03:39 +01:00
catsout
38b14fd734 Using communicate instead of terminate (#459)
Fix kindlegen not clear temp files
2023-01-21 11:00:04 +01:00
Alice Charatonik
4fa72780a1 fix in fedora: 7z doesn't support rar archives, use unrar (#370)
see 
https://github.com/ttys3/fedora-rpm-p7zip
https://sourceforge.net/p/sevenzip/discussion/45798/thread/dc2d0438/#8f9f
2023-01-21 10:54:11 +01:00
Cory Kleinschmidt
c979486e28 Fix pillow backwards compatibility, add mozjpeg-lossless-optimization to setup.py (#461)
- Fix pillow backwards compatibility
- Add mozjpeg-lossless-optimization to setup.py
2023-01-21 00:35:59 +01:00
darodi
03bd67cf2f Update README.md 2023-01-20 13:08:20 +01:00
darodi
b44af66484 pipeline python-package.yml python-package-test.yml 2023-01-19 21:15:54 +01:00
darodi
754395f1b3 pipeline python-package.yml python-package-test.yml 2023-01-19 21:08:34 +01:00
darodi
95e73ea1dd update README.md 2023-01-19 01:54:32 +01:00
darodi
f466701b5d Version bump 2023-01-19 01:28:21 +01:00
darodi
bc6b26862f update README.md 2023-01-19 01:04:45 +01:00
darodi
b4d86fed7f update README.md 2023-01-19 00:39:54 +01:00
darodi
b5806ece0e Update README.md 2023-01-19 00:33:54 +01:00
darodi
85a99f0b05 update README.md 2023-01-19 00:28:37 +01:00
Busindre
4fea796763 Update README
Dependency qt5dxcb-plugin required for KCC added. The kindlegen tool is no longer available through amazon, the link was replaced by a link to archive.org.
2023-01-14 20:31:19 +01:00
darodi
ca8bdf7e1f Update README.md 2023-01-14 17:38:59 +01:00
darodi
33d57d5025 Merge pull request #457 from ciromattia/beta_release_merge_rebased
Beta release merge rebased
2023-01-14 16:45:23 +01:00
darodi
cceb9cf61f change pipeline to publish to PyPi 2023-01-14 01:44:16 +01:00
darodi
9ee375c6e4 change repository to ciromattia 2023-01-14 01:44:16 +01:00
darodi
a50345a26d update version 2023-01-14 01:44:16 +01:00
darodi
be4b35b705 update to python 3.11 2023-01-14 00:31:12 +01:00
dependabot[bot]
608e79a9fc Bump python from 3.8-slim-buster to 3.11-slim-buster
Bumps python from 3.8-slim-buster to 3.11-slim-buster.

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-14 00:31:12 +01:00
Fabrizio Pietrucci
7468512ec8 More precise type in slugify dependency check 2023-01-14 00:31:12 +01:00
Fabrizio Pietrucci
2256df0785 Fix 'slugify' dependency check
New versions of slugify moved version informations to a standalone
module (__verrsion__.py). Added a check to see if the imported version
is indeed a string (as previously expected). If not, get the
`__version__` variable in the `__version__` module.
2023-01-14 00:31:12 +01:00
dependabot[bot]
378a3caccc Update python-slugify requirement from <3.0.0,>=1.2.1 to >=1.2.1,<8.0.0
Updates the requirements on [python-slugify](https://github.com/un33k/python-slugify) to permit the latest version.
- [Release notes](https://github.com/un33k/python-slugify/releases)
- [Changelog](https://github.com/un33k/python-slugify/blob/master/CHANGELOG.md)
- [Commits](https://github.com/un33k/python-slugify/compare/1.2.1...v7.0.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-14 00:31:12 +01:00
dependabot[bot]
ab7d629ba9 Bump actions/setup-python from 3 to 4
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 3 to 4.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v3...v4)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-14 00:31:12 +01:00
dependabot[bot]
b54299ff76 Bump actions/setup-node from 2 to 3
* Bump actions/setup-node from 2 to 3

Bumps [actions/setup-node](https://github.com/actions/setup-node) from 2 to 3.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](https://github.com/actions/setup-node/compare/v2...v3)

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

* Bump actions/setup-node from 2 to 3

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: darodi <4682830+darodi@users.noreply.github.com>
2023-01-14 00:31:12 +01:00
darodi
1d3fc9cc92 Update dependabot.yml 2023-01-14 00:31:11 +01:00
darodi
49313ce030 Create dependabot.yml 2023-01-14 00:31:11 +01:00
darodi
c0d17c1803 Update README.md 2023-01-14 00:31:11 +01:00
darodi
7f883f98ba Fix Docker 7z missing 2023-01-14 00:31:11 +01:00
darodi
2c19898952 Fix spread splitter not keeping aspect ratio 2023-01-14 00:31:11 +01:00
darodi
12c663bc05 Update README.md 2023-01-14 00:31:11 +01:00
darodi
d8957dc4a6 replace version in Info.plist 2023-01-14 00:31:11 +01:00
darodi
9b45633279 activate batchsplit only for EPUB-200 2023-01-14 00:31:11 +01:00
darodi
b5a0126051 targetsize and EPUB-200MB 2023-01-14 00:31:11 +01:00
rourien
182a292f70 Merge 0c152cf targetsize option 2023-01-14 00:31:11 +01:00
rourien
7ab96c5573 Add backwards compatibility for Pillow >9.1.0 from 4b127b1 2023-01-14 00:31:11 +01:00
rourien
1fbfaeae01 Update deprecated pillow filters
See https://pillow.readthedocs.io/en/stable/deprecations.html#constants
2023-01-14 00:31:11 +01:00
darodi
2462d11b5e appImage with ubuntu-20.04 2023-01-14 00:31:10 +01:00
darodi
44e6ffe4e3 Adding a switch on GUI interface in order to control cropping options 2023-01-14 00:31:10 +01:00
darodi
9be2a4a492 profiles: add Kindle11 and Kindle Scribe - update README.md and CHANGELOG.md 2023-01-14 00:31:10 +01:00
darodi
3ea9a486bf profiles: add Kindle11 and Kindle Scribe 2023-01-14 00:31:10 +01:00
lennie420
95f138887e Update with new Kobo models
* Update Kobo profile list

Update the Kobo models to the current models available from Kobo. I have kept Kobo Forma as this is a popular device amongst manga readers even though it's currently unlisted from Kobo UK's site.

Co-authored-by: darodi <4682830+darodi@users.noreply.github.com>
2023-01-14 00:31:10 +01:00
darodi
a7a01f8269 keep epub file when selecting another type of output file
* Keep epub file when selecting another type of output file
2023-01-14 00:31:10 +01:00
darodi
6987c8b1cd KFX Output in GUI 2023-01-14 00:31:09 +01:00
darodi
6e10636356 Adding Dockerfile 2023-01-14 00:27:53 +01:00
darodi
63bd55313f Update README.md 2023-01-14 00:27:53 +01:00
darodi
324047bebc Fix type error in autocontrastImage 2023-01-14 00:27:53 +01:00
darodi
842a729c75 turn 1x4 strips into 2x2 2023-01-14 00:27:53 +01:00
darodi
27d3eab8d5 Update README.md
clarify requirements for pillow
2023-01-14 00:27:53 +01:00
darodi
8035dedae8 force PNG checkbox 2023-01-14 00:27:53 +01:00
darodi
b39076a9f4 pipelines 2023-01-14 00:27:52 +01:00
darodi
237f343e62 * fix Using 'Disable processing' option using my processed image get an error 2023-01-13 22:48:53 +01:00
darodi
ab60d67ab7 add --mozjpeg option and GUI checkBox 2023-01-13 22:48:53 +01:00
darodi
79715c6a06 add --mozjpeg option and GUI checkBox 2023-01-13 22:48:53 +01:00
StudioEtrange
6cd073809e add GUI option to disable processing 2023-01-13 22:48:53 +01:00
StudioEtrange
9dac000a04 add --noprocessing option 2023-01-13 22:48:53 +01:00
StudioEtrange
674121620f file selector add All *.* 2023-01-13 22:22:11 +01:00
clach04
f6e2ceae91 Clarify Pillow version requirement
Pillow version requirement in code is 5.2.0+ since support for WebP was added for issue #240 
Clarify optional GUI dependencies, kcc-c2p.py/kcc-c2e.py does not need pyqt nor raven.
2023-01-13 22:20:12 +01:00
StudioEtrange
143f6eb0f9 sync requirements between setup.py and requirements.txt 2023-01-13 22:17:40 +01:00
darodi
7cac6d4008 Merge pull request #405 from darodi/kindle_signature
Add profile for Kindle Paperwhite 5 (Signature Edition) - fixed for panel view 4/2/HQ
2023-01-13 21:55:30 +01:00
darodi
aa4456bdb1 Add profile for Kindle PW5/Signature 2021-11-27 23:57:45 +01:00
Einlar
c91be77588 Add profile for Kindle PW5/Signature 2021-11-13 13:50:56 +01:00
Einlar
65a42c1063 Merge branch 'add_profiles_kobo' 2021-11-13 13:32:06 +01:00
Fulya
6519eb0453 Fixed the skipped/missed images and/or panels 2021-05-15 14:08:36 -04:00
Frédéric Brière
9fdbf095d3 Add profiles for the Kobo Clara HD and Libra H2O
Note: I omitted the "HD" and "H2O" suffixes from the labels to keep
things simple, since they do not denote a variation of a preexisting
model, as was the case with the Aura.
2019-11-12 11:59:22 -05:00
Paweł Jastrzębski
4ec4c9966c Merge pull request #329 from C0rn3j/master
Do not recommend running pip as root
2019-10-29 11:25:48 +01:00
C0rn3j
5a51c5234e Recommend running pip as user, not root 2019-10-28 14:38:48 +01:00
C0rn3j
b7c6fd30e4 Fix typo 2019-10-28 14:37:56 +01:00
Paweł Jastrzębski
515b83637f Merge pull request #326 from ciromattia/dev
5.5.2
2019-10-21 17:27:38 +02:00
Paweł Jastrzębski
c3dad087d3 Update build scripts 2019-10-21 16:12:03 +02:00
Paweł Jastrzębski
67e913ed9e Version bump 2019-10-21 13:50:53 +02:00
Paweł Jastrzębski
6ce0f76fe0 Fixed KindleGen detection on macOS 2019-10-21 13:44:49 +02:00
Paweł Jastrzębski
5f5157c1d4 No longer modify header field 504 2019-08-04 09:29:03 +02:00
Paweł Jastrzębski
4c13ef0f6c Fixed handling filenames that start with dot (close #310) 2019-08-04 09:25:32 +02:00
Paweł Jastrzębski
50dc7fbffe Added profile label for Oasis 3
New profile is unnecessary.
2019-08-04 08:40:48 +02:00
Paweł Jastrzębski
a060498ac7 Fixed Windows autoupdater 2019-03-08 09:04:12 +01:00
43 changed files with 7912 additions and 6012 deletions

14
.dockerignore Normal file
View File

@@ -0,0 +1,14 @@
.git
.github
build
dist
KindleComicConverter.egg-info
.dockerignore
.gitignore
.travis.yml
Dockerfile
other
venv
*.md
LICENSE.txt
MANIFEST.in

25
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,25 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
- package-ecosystem: "pip"
directory: "/"
schedule:
interval: "weekly"
# Enable version updates for Docker
- package-ecosystem: "docker"
# Look for a `Dockerfile` in the `root` directory
directory: "/"
# Check for updates once a week
schedule:
interval: "weekly"
# Maintain dependencies for GitHub Actions
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"

74
.github/workflows/codeql-analysis.yml vendored Normal file
View File

@@ -0,0 +1,74 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ "beta_release" ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ "beta_release" ]
schedule:
- cron: '42 22 * * 3'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'python' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
steps:
- name: Checkout repository
uses: actions/checkout@v3
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended,security-and-quality
# 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)
- name: Autobuild
uses: github/codeql-action/autobuild@v2
# 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
# If the Autobuild fails above, remove it and uncomment the following three lines.
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
# - run: |
# echo "Run, Build Application using script"
# ./location_of_script_within_repo/buildscript.sh
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
with:
category: "/language:${{matrix.language}}"

36
.github/workflows/docker-publish.yml vendored Normal file
View File

@@ -0,0 +1,36 @@
name: Docker
on:
workflow_dispatch:
#schedule:
# - cron: '39 5 * * *'
push:
# branches: [ master, pipeline_test, docker_test ]
# Publish semver tags as releases.
tags: [ 'v*.*.*' ]
# Don't trigger if it's just a documentation update
paths-ignore:
- '**.md'
- '**.MD'
- '**.yml'
- 'docs/**'
- 'LICENSE'
- '.gitattributes'
- '.gitignore'
- '.dockerignore'
jobs:
build_and_push:
uses: sdr-enthusiasts/common-github-workflows/.github/workflows/build_and_push_image.yml@main
with:
platform_linux_arm32v7_enabled: true
platform_linux_arm64v8_enabled: true
platform_linux_amd64_enabled: true
push_enabled: true
build_nohealthcheck: false
ghcr_repo_owner: ${{ github.repository_owner }}
ghcr_repo: ${{ github.repository }}
secrets:
ghcr_token: ${{ secrets.GITHUB_TOKEN }}

75
.github/workflows/package-linux.yml vendored Normal file
View File

@@ -0,0 +1,75 @@
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
name: build KCC for Linux
on:
workflow_dispatch:
push:
tags:
- "v*.*.*"
# Don't trigger if it's just a documentation update
paths-ignore:
- '**.md'
- '**.MD'
- '**.yml'
- '**.sh'
- 'docs/**'
- 'Dockerfile'
- 'LICENSE'
- '.gitattributes'
- '.gitignore'
- '.dockerignore'
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: 3.11
cache: 'pip'
- name: Install python dependencies
run: |
sudo apt-get update
sudo apt-get install -y libpng-dev libjpeg-dev p7zip-full python3-pyqt5 python3-pip squashfs-tools libfuse2
python -m pip install --upgrade pip setuptools wheel certifi pyinstaller PyQt6 --no-binary pyinstaller
python -m pip install -r requirements.txt
- name: build binary
run: |
python setup.py build_binary
chmod +x dist/kcc_linux*
# issue with this action, disabled and commented out
# see https://github.com/AppImageCrafters/build-appimage/issues/5
# see https://appimage-builder.readthedocs.io/en/latest/intro/install.html#install-appimagetool
# - name: Build AppImage
# uses: AppImageCrafters/build-appimage-action@master
# env:
# UPDATE_INFO: gh-releases-zsync|ciromattia|kcc|latest|*x86_64.AppImage.zsync
# with:
# recipe: AppImageBuilder.yml
- name: Build AppImage
run: |
wget -O appimage-builder-x86_64.AppImage https://github.com/AppImageCrafters/appimage-builder/releases/download/v1.1.0/appimage-builder-1.1.0-x86_64.AppImage
chmod +x appimage-builder-x86_64.AppImage
sudo mv appimage-builder-x86_64.AppImage /usr/local/bin/appimage-builder
appimage-builder --recipe AppImageBuilder.yml --skip-test
env:
UPDATE_INFO: gh-releases-zsync|ciromattia|kcc|latest|*x86_64.AppImage.zsync
- name: upload artifact
uses: actions/upload-artifact@v3
with:
name: AppImage
path: './*.AppImage*'
- name: Release
uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/')
with:
prerelease: true
generate_release_notes: true
files: |
CHANGELOG.md
LICENSE.txt
*.AppImage*

98
.github/workflows/package-macos.yml vendored Normal file
View File

@@ -0,0 +1,98 @@
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
name: build KCC for mac os
on:
workflow_dispatch:
push:
tags:
- "v*.*.*"
# Don't trigger if it's just a documentation update
paths-ignore:
- '**.md'
- '**.MD'
- '**.yml'
- '**.sh'
- 'docs/**'
- 'Dockerfile'
- 'LICENSE'
- '.gitattributes'
- '.gitignore'
- '.dockerignore'
jobs:
build:
runs-on: macos-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: 3.11
cache: 'pip'
- name: Install python dependencies
run: |
python -m pip install --upgrade pip setuptools wheel pyinstaller certifi
pip install -r requirements.txt
- name: Install the Apple certificate and provisioning profile
# TODO signing
# https://federicoterzi.com/blog/automatic-code-signing-and-notarization-for-macos-apps-using-github-actions/
if: ${{ false }}
env:
BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }}
P12_PASSWORD: ${{ secrets.P12_PASSWORD }}
BUILD_PROVISION_PROFILE_BASE64: ${{ secrets.BUILD_PROVISION_PROFILE_BASE64 }}
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
run: |
# create variables
CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12
PP_PATH=$RUNNER_TEMP/build_pp.mobileprovision
KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
# import certificate and provisioning profile from secrets
echo -n "$BUILD_CERTIFICATE_BASE64" | base64 --decode -o $CERTIFICATE_PATH
echo -n "$BUILD_PROVISION_PROFILE_BASE64" | base64 --decode -o $PP_PATH
# create temporary keychain
security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
security set-keychain-settings -lut 21600 $KEYCHAIN_PATH
security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
# import certificate to keychain
security import $CERTIFICATE_PATH -P "$P12_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
security list-keychain -d user -s $KEYCHAIN_PATH
# apply provisioning profile
mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
cp $PP_PATH ~/Library/MobileDevice/Provisioning\ Profiles
- uses: actions/setup-node@v3
with:
node-version: 16
- run: npm install -g appdmg
- name: build binary
# TODO /usr/bin/codesign --force -s "$MACOS_CERTIFICATE_NAME" --options runtime dist/Applications/Kindle\ Comic\ Converter.app -v
run: |
python setup.py build_binary
- name: upload build
uses: actions/upload-artifact@v3
with:
name: mac-os-build
path: dist/*.dmg
- name: Release
uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/')
with:
prerelease: true
generate_release_notes: true
files: |
CHANGELOG.md
LICENSE.txt
dist/*.dmg
- name: Clean up keychain and provisioning profile
# TODO signing
if: ${{ false }}
# if: ${{ always() }}
run: |
security delete-keychain $RUNNER_TEMP/app-signing.keychain-db
rm ~/Library/MobileDevice/Provisioning\ Profiles/build_pp.mobileprovision

View File

@@ -0,0 +1,63 @@
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
name: build KCC for windows with docker
on:
workflow_dispatch:
push:
tags:
- "v*.*.*"
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
# - name: Set up Python
# uses: actions/setup-python@v4
# with:
# python-version: 3.11
# cache: 'pip'
# - name: Install python dependencies
# run: |
# python -m pip install --upgrade pip setuptools wheel pyinstaller
# pip install -r requirements.txt
# - name: build binary
# run: |
# pyi-makespec -F -i icons\\comic2ebook.ico -n KCC_test -w --noupx kcc.py
- name: Package Application
uses: JackMcKew/pyinstaller-action-windows@main
with:
path: .
spec: ./kcc.spec
- name: Package Application
uses: JackMcKew/pyinstaller-action-windows@main
with:
path: .
spec: ./kcc-c2e.spec
- name: Package Application
uses: JackMcKew/pyinstaller-action-windows@main
with:
path: .
spec: ./kcc-c2p.spec
- name: rename binaries
run: |
version_built=$(cat kindlecomicconverter/__init__.py | grep version | awk '{print $3}' | sed "s/[^.0-9b]//g")
mv dist/windows/kcc.exe dist/windows/kcc_${version_built}.exe
mv dist/windows/kcc-c2e.exe dist/windows/kcc-c2e_${version_built}.exe
mv dist/windows/kcc-c2p.exe dist/windows/kcc-c2p_${version_built}.exe
- name: upload build
uses: actions/upload-artifact@v3
with:
name: windows-build
path: dist/windows/*.exe
- name: Release
uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/')
with:
prerelease: true
generate_release_notes: true
files: |
CHANGELOG.md
LICENSE.txt
dist/windows/*.exe

58
.github/workflows/package-windows.yml vendored Normal file
View File

@@ -0,0 +1,58 @@
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
name: build KCC for windows
on:
workflow_dispatch:
push:
tags:
- "v*.*.*"
# Don't trigger if it's just a documentation update
paths-ignore:
- '**.md'
- '**.MD'
- '**.yml'
- '**.sh'
- 'docs/**'
- 'Dockerfile'
- 'LICENSE'
- '.gitattributes'
- '.gitignore'
- '.dockerignore'
jobs:
build:
runs-on: windows-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: 3.11
cache: 'pip'
- name: Install dependencies
env:
PYINSTALLER_COMPILE_BOOTLOADER: 1
run: |
python -m pip install --upgrade pip setuptools wheel
pip install -r requirements.txt
pip install certifi pyinstaller --no-binary pyinstaller
- name: build binary
run: |
python setup.py build_binary
- name: upload build
uses: actions/upload-artifact@v3
with:
name: windows-build
path: dist/*.exe
- name: Release
uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/')
with:
prerelease: true
generate_release_notes: true
files: |
CHANGELOG.md
LICENSE.txt
dist/*.exe

20
.gitignore vendored
View File

@@ -1,15 +1,13 @@
*.pyc *.pyc
*.cbz Pipfile
*.cbr Pipfile.lock
*.spec
.idea
.DS_Store
.python-version
Thumbs.db
dist
Output
kindlegen*
setup.bat setup.bat
kindlecomicconverter/sentry.py kindlecomicconverter/sentry.py
other/windows/kindlegen.exe
dist/
build/ build/
KindleComicConverter.egg-info/ KindleComicConverter*.egg-info/
.idea/
/venv/
/kindlegen*
/kcc.bat

View File

@@ -2,16 +2,10 @@ matrix:
include: include:
- os: osx - os: osx
language: generic language: generic
osx_image: xcode9.2 osx_image: xcode11.1
before_install: before_install:
- brew update
- brew upgrade python3
- brew uninstall node
- travis_wait 30 brew install node@6
- brew link node@6 --force --overwrite
- pip3 install --upgrade pip setuptools wheel - pip3 install --upgrade pip setuptools wheel
- openssl aes-256-cbc -K $encrypted_a95564d8ff0d_key -iv $encrypted_a95564d8ff0d_iv -in other/osx/sentry.py.enc -out kindlecomicconverter/sentry.py -d
install: install:
- pip3 install -r requirements.txt - pip3 install -r requirements.txt
@@ -25,13 +19,12 @@ before_deploy:
- rm -r dist/!(*.deb|*.dmg) - rm -r dist/!(*.deb|*.dmg)
deploy: deploy:
provider: s3 provider: gcs
skip_cleanup: true access_key_id: GOOG1EC62457RKUYFR2TIZUWV4EFSV2EP5LVLPPFXUAKADWJFDYPFW63BQSLA
access_key_id: AKIAIQNL5R4FI4C4NJYQ
secret_access_key: secret_access_key:
secure: X66hYplxB4QSueljwvDfamNH/MQmHjo3mCofBcaTHAr7n2fp+yd2NzD2yy9h8NbsL0LWwx9wtJa/jpkIE02ZDfi9NrMLvKKFazzdpiyTMN5Yh85lHHyD1XIOCZRd4igaZ+O8975tJAEaEOPS+PE9XGZcRBh+y/eSJ+fMEgohaJ1MtDFbQR7X1cEw3iqbjrV2rlghZNCk/9mZEfObzAEjQiSDpv5G0IuIPRvYg/BgZt8chHVAe03B6oqcBa7uCBCTlfHIiNh1MWtP0B3NNBq3dcu9QHOFri1YqoZKuaPVCf6TFQL/NW5dFihegev2t9IwFyaBxytiT8fBkgQhP0VX8cuCwBAfnQGIogAu0eLSPp+E6dB/7Cpt2GDCk39+As8WKqt9hCRHmrvYhPA1Mq9QyEgKy/TKKKfDby3qVTIqYOQYpuQ1B7sIU651L5A+hBvZ1dqWIUz25h0zqjjeSFrcfNnf1e4tkk0QJvvnKqz0xsVaJxA2p07VJMRn8SlZQIJ2GEbMDeB5jxYtf5JzXywChP9adlPNjLna9G8ScnGSU1f7ZhsBQUEgY5jBlnX1lveyl3DUe6NP+qOTyljLWYwjx3AF4Zg10LYSecRS6hnqAUrGRmibDCIYclUzlJkVyjKGJ9uEyrUiCp0P0IsAzE1XhPVAWEyGUcWWGJG+jgmohSk= secure: sxYjeho7U3im0Ezf6cz6TjYDiLvf0kAM2ETQHYoFNbD1VVvhJJyymDCnPH80zpFKmhc1MWTB6ndwsrPfcyZDLR2meSdWGPjZfFPY3RcrfImndKi7ln+mYQDBQ7W1lGit4YcH3Ju7LHceaTbRA7fVTX8pWKOcbXL2oM+lQxTJHH32+crVma+ChhbjzTWsSLRoakt3Nhiveec5p/qSW7AFe4Zq+b3C85IgwjSJI/xVwzaWrs6p915h1zZi7KL7YCMIxfQFrvRPFR2KTbh/DoLCCrqfbD4qh0PVy1li51Ac3hd/u3foiNnTNchzgE3Nv/nbKmtFU6huuLNgzkQGuLA+yn7mKYzBwA3ZmFgoimdH9+yRCMkZ8B5VHpvfN1hgpJcyEl1T98Kv4cdtRYNB4w9iAMy1qSVxhjeI+2rjuWGoXro0lU6L4LIRCOruY3AuLCAKG8Qw5Ak9ksmIKBhZ9soxpoIwu/TYDUQkFj29IrUQucg9TEp7uAoxu8/7EHxB7hWnBRaBAAQbMuIRg7yysT3FT0Os6SB0t9+RBsVMSPuIti9JJZ2Lu0uRI1+Se+g7ItzYtJoPhBJAzAa+J9OONj0RNj2z8Vq2oIBhH4z6b6zTRMVroos3cdfYl5qIKs9SQ7rmeHoPRROcqpCznsUZ/ESa4f2MewFU/7AYcEnCesZV4xg=
bucket: kcc-deploy bucket: kcc-deploy
region: eu-central-1 local-dir: dist
local_dir: dist skip_cleanup: true
on: on:
repo: AcidWeb/kcc repo: AcidWeb/KCC

67
AppImageBuilder.yml Normal file
View File

@@ -0,0 +1,67 @@
# appimage-builder recipe see https://appimage-builder.readthedocs.io for details
version: 1
script:
- rm -rf AppDir || true
- mkdir -p AppDir/usr/share/icons/hicolor/64x64/apps/
- cp -a dist/kcc_linux* AppDir/ && mv AppDir/kcc_linux* AppDir/kcc_linux
- cp icons/comic2ebook.png AppDir/usr/share/icons/hicolor/64x64/apps/
AppDir:
path: AppDir
app_info:
id: com.github.ciromattia.kcc
name: kindleComicConverter
icon: comic2ebook
version: latest
exec: ./kcc_linux
exec_args: $@
apt:
arch:
- amd64
allow_unauthenticated: true
sources:
- sourceline: deb http://archive.ubuntu.com/ubuntu jammy main restricted
- sourceline: deb http://archive.ubuntu.com/ubuntu jammy-updates main restricted
- sourceline: deb http://archive.ubuntu.com/ubuntu jammy universe
- sourceline: deb http://archive.ubuntu.com/ubuntu jammy-updates universe
- sourceline: deb http://archive.ubuntu.com/ubuntu jammy multiverse
- sourceline: deb http://archive.ubuntu.com/ubuntu jammy-updates multiverse
- sourceline: deb http://archive.ubuntu.com/ubuntu jammy-backports main restricted
universe multiverse
- sourceline: deb http://security.ubuntu.com/ubuntu jammy-security main restricted
- sourceline: deb http://security.ubuntu.com/ubuntu jammy-security universe
- sourceline: deb http://security.ubuntu.com/ubuntu jammy-security multiverse
include:
- libc6:amd64
files:
include: []
exclude:
- usr/share/man
- usr/share/doc/*/README.*
- usr/share/doc/*/changelog.*
- usr/share/doc/*/NEWS.*
- usr/share/doc/*/TODO.*
test:
fedora-30:
image: appimagecrafters/tests-env:fedora-30
command: ./AppRun
use_host_x: true
debian-stable:
image: appimagecrafters/tests-env:debian-stable
command: ./AppRun
use_host_x: true
archlinux-latest:
image: appimagecrafters/tests-env:archlinux-latest
command: ./AppRun
use_host_x: true
centos-7:
image: appimagecrafters/tests-env:centos-7
command: ./AppRun
use_host_x: true
ubuntu-xenial:
image: appimagecrafters/tests-env:ubuntu-xenial
command: ./AppRun
use_host_x: true
AppImage:
arch: x86_64
update-information: !ENV ${UPDATE_INFO}
sign-key: None

View File

@@ -1,4 +1,49 @@
# CHANGELOG # CHANGELOG
#### 5.6.1:
* Fix pillow backwards compatibility, add mozjpeg-lossless-optimization to setup.py by @corylk in #461
* fix in fedora: 7z doesn't support rar archives, use unrar by @AlicesReflexion in #370
* Using communicate instead of terminate by @catsout in #459
* use copyfile and delete instead of shutil.move fix #386 by @StudioEtrange in #387
#### 5.6.0:
* Fix Docker 7z missing [darodi/kcc#31](https://github.com/darodi/kcc/issues/31), thanks [@darodi](https://github.com/darodi)
* update to python 3.11, thanks [@darodi](https://github.com/darodi)
* Bump python from 3.8-slim-buster to 3.11-slim-buster dependabot[bot]
* More precise type in slugify dependency check, thanks [@bamless](https://github.com/bamless)
* Fix 'slugify' dependency check, thanks [@bamless](https://github.com/bamless)
* Update python-slugify requirement from <3.0.0,>=1.2.1 to >=1.2.1,<8.0.0 dependabot[bot]
* Bump actions/setup-python from 3 to 4 [darodi/kcc#32](https://github.com/darodi/kcc/issues/32) dependabot[bot]
* Bump actions/setup-node from 2 to 3 [darodi/kcc#34](https://github.com/darodi/kcc/issues/34) dependabot[bot]
* Fix Docker 7z missing [darodi/kcc#31](https://github.com/darodi/kcc/issues/31), thanks [@darodi](https://github.com/darodi)
* Spread splitter not keeping aspect ratio [darodi/kcc#27](https://github.com/darodi/kcc/issues/27), thanks [@darodi](https://github.com/darodi)
* activate batchsplit only for EPUB-200 [darodi/kcc#24](https://github.com/darodi/kcc/issues/24), thanks [@darodi](https://github.com/darodi)
* Feature Request: allow split for epub and set target (email, web upload) [darodi/kcc#21](https://github.com/darodi/kcc/issues/21), thanks [@darodi](https://github.com/darodi)
* Adding a switch on GUI interface in order to control cropping options [darodi/kcc#18](https://github.com/darodi/kcc/issues/18), thanks [@darodi](https://github.com/darodi)
* profiles: add Kindle11 and Kindle Scribe [darodi/kcc#16](https://github.com/darodi/kcc/issues/16), thanks [@darodi](https://github.com/darodi)
* Update with new Kobo models [darodi/kcc#15](https://github.com/darodi/kcc/issues/15), thanks [@lennie420](https://github.com/lennie420)
* Keep epub file when selecting another type of output file [darodi/kcc#12](https://github.com/darodi/kcc/issues/12), thanks [@darodi](https://github.com/darodi)
* fix Using 'Disable processing' option using my processed image get an error [darodi/kcc#1](https://github.com/darodi/kcc/issues/1), thanks [@darodi](https://github.com/darodi)
* KFX Output in GUI [darodi/kcc#9](https://github.com/darodi/kcc/issues/9), thanks [@darodi](https://github.com/darodi)
* Linux version and appImage [darodi/kcc#6](https://github.com/darodi/kcc/issues/6), thanks [@darodi](https://github.com/darodi)
* docker image for command line [darodi/kcc#5](https://github.com/darodi/kcc/issues/5), thanks [@darodi](https://github.com/darodi)
* Fix type error in autocontrastImage [ciromattia/kcc#432](https://github.com/ciromattia/kcc/issues/432), thanks [@darodi](https://github.com/darodi)
* Option to turn 1x4 strips into 2x2 strips [ciromattia/kcc#439](https://github.com/ciromattia/kcc/issues/439), thanks [@darodi](https://github.com/darodi)
* Option in GUI to have PNG instead of jpg images [darodi/kcc#3](https://github.com/darodi/kcc/issues/3), thanks [@darodi](https://github.com/darodi)
* Use MozJPEG as the JPEG encoder : cli and GUI option [ciromattia/kcc#416](https://github.com/ciromattia/kcc/pull/416), thanks [@darodi](https://github.com/darodi)
* Disable all image transformation : cli and GUI option [ciromattia/kcc#388](https://github.com/ciromattia/kcc/pull/388), thanks [@StudioEtrange](https://github.com/StudioEtrange)
* file selector add All `*.*` [ciromattia/kcc#412](https://github.com/ciromattia/kcc/pull/412), thanks [@StudioEtrange](https://github.com/StudioEtrange)
* Clarify Pillow version requirement [ciromattia/kcc#366](https://github.com/ciromattia/kcc/pull/366), thanks [@clach04](https://github.com/clach04)
* sync requirements between setup.py and requirements.txt [ciromattia/kcc#411](https://github.com/ciromattia/kcc/pull/411), thanks [@StudioEtrange](https://github.com/StudioEtrange)
* Add profile for Kindle PW5/Signature [ciromattia/kcc#405](https://github.com/ciromattia/kcc/pull/405), thanks [@Einlar](https://github.com/Einlar), [@darodi](https://github.com/darodi)
* Fixed the skipped/missed images and/or panels [ciromattia/kcc#393](https://github.com/ciromattia/kcc/pull/393), thanks [@FulyaDemirkan](https://github.com/FulyaDemirkan)
* Add profiles for the Kobo Clara HD and Libra H2O [ciromattia/kcc#331](https://github.com/ciromattia/kcc/pull/331), thanks [@fbriere](https://github.com/fbriere)
#### 5.5.2:
* Fixed KindleGen detection on macOS 10.15
* Fixed multiple smaller issues
#### 5.5.1: #### 5.5.1:
* Fixes some stability issues * Fixes some stability issues

167
Dockerfile Normal file
View File

@@ -0,0 +1,167 @@
FROM --platform=linux/amd64 python:3.11-slim-buster as compile-amd64
ARG TARGETOS
ARG TARGETARCH
ARG TARGETVARIANT
RUN echo "I'm building for $TARGETOS/$TARGETARCH/$TARGETVARIANT"
COPY requirements.txt /opt/kcc/
ENV PATH="/opt/venv/bin:$PATH"
RUN DEBIAN_FRONTEND=noninteractive apt-get update -y && apt-get -yq upgrade && \
apt-get install -y libpng-dev libjpeg-dev p7zip-full unrar-free libgl1 python3-pyqt5 && \
python -m pip install --upgrade pip && \
python -m venv /opt/venv && \
python -m pip install -r /opt/kcc/requirements.txt
######################################################################################
FROM --platform=linux/arm64 python:3.11-slim-buster as compile-arm64
ARG TARGETOS
ARG TARGETARCH
ARG TARGETVARIANT
RUN echo "I'm building for $TARGETOS/$TARGETARCH/$TARGETVARIANT"
ENV LC_ALL=C.UTF-8 \
LANG=C.UTF-8 \
LANGUAGE=en_US:en
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
RUN set -x && \
TEMP_PACKAGES=() && \
KEPT_PACKAGES=() && \
# Packages only required during build
TEMP_PACKAGES+=(build-essential) && \
TEMP_PACKAGES+=(cmake) && \
TEMP_PACKAGES+=(libfreetype6-dev) && \
TEMP_PACKAGES+=(libfontconfig1-dev) && \
TEMP_PACKAGES+=(libpng-dev) && \
TEMP_PACKAGES+=(libjpeg-dev) && \
TEMP_PACKAGES+=(libssl-dev) && \
TEMP_PACKAGES+=(libxft-dev) && \
TEMP_PACKAGES+=(make) && \
TEMP_PACKAGES+=(python3-dev) && \
TEMP_PACKAGES+=(python3-setuptools) && \
TEMP_PACKAGES+=(python3-wheel) && \
# Packages kept in the image
KEPT_PACKAGES+=(bash) && \
KEPT_PACKAGES+=(ca-certificates) && \
KEPT_PACKAGES+=(chrpath) && \
KEPT_PACKAGES+=(locales) && \
KEPT_PACKAGES+=(locales-all) && \
KEPT_PACKAGES+=(libfreetype6) && \
KEPT_PACKAGES+=(libfontconfig1) && \
KEPT_PACKAGES+=(p7zip-full) && \
KEPT_PACKAGES+=(python3) && \
KEPT_PACKAGES+=(python3-pip) && \
KEPT_PACKAGES+=(python-pyqt5) && \
KEPT_PACKAGES+=(qt5-default) && \
KEPT_PACKAGES+=(unrar-free) && \
# Install packages
DEBIAN_FRONTEND=noninteractive apt-get update -y && apt-get -yq upgrade && \
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
${KEPT_PACKAGES[@]} \
${TEMP_PACKAGES[@]} \
&& \
# Install required python modules
python -m pip install --upgrade pip && \
# python -m pip install -r /opt/kcc/requirements.txt && \
python -m venv /opt/venv && \
python -m pip install --upgrade pillow python-slugify psutil raven mozjpeg-lossless-optimization
######################################################################################
FROM --platform=linux/arm/v7 python:3.11-slim-buster as compile-armv7
ARG TARGETOS
ARG TARGETARCH
ARG TARGETVARIANT
RUN echo "I'm building for $TARGETOS/$TARGETARCH/$TARGETVARIANT"
ENV LC_ALL=C.UTF-8 \
LANG=C.UTF-8 \
LANGUAGE=en_US:en
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
RUN set -x && \
TEMP_PACKAGES=() && \
KEPT_PACKAGES=() && \
# Packages only required during build
TEMP_PACKAGES+=(build-essential) && \
TEMP_PACKAGES+=(cmake) && \
TEMP_PACKAGES+=(libffi-dev) && \
TEMP_PACKAGES+=(libfreetype6-dev) && \
TEMP_PACKAGES+=(libfontconfig1-dev) && \
TEMP_PACKAGES+=(libpng-dev) && \
TEMP_PACKAGES+=(libjpeg-dev) && \
TEMP_PACKAGES+=(libssl-dev) && \
TEMP_PACKAGES+=(libxft-dev) && \
TEMP_PACKAGES+=(make) && \
TEMP_PACKAGES+=(python3-dev) && \
TEMP_PACKAGES+=(python3-setuptools) && \
TEMP_PACKAGES+=(python3-wheel) && \
# Packages kept in the image
KEPT_PACKAGES+=(bash) && \
KEPT_PACKAGES+=(ca-certificates) && \
KEPT_PACKAGES+=(chrpath) && \
KEPT_PACKAGES+=(locales) && \
KEPT_PACKAGES+=(locales-all) && \
KEPT_PACKAGES+=(libfreetype6) && \
KEPT_PACKAGES+=(libfontconfig1) && \
KEPT_PACKAGES+=(p7zip-full) && \
KEPT_PACKAGES+=(python3) && \
KEPT_PACKAGES+=(python3-pip) && \
KEPT_PACKAGES+=(python-pyqt5) && \
KEPT_PACKAGES+=(qt5-default) && \
KEPT_PACKAGES+=(unrar-free) && \
# Install packages
DEBIAN_FRONTEND=noninteractive apt-get update -y && apt-get -yq upgrade && \
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
${KEPT_PACKAGES[@]} \
${TEMP_PACKAGES[@]} \
&& \
# Install required python modules
python -m pip install --upgrade pip && \
# python -m pip install -r /opt/kcc/requirements.txt && \
python -m venv /opt/venv && \
python -m pip install --upgrade pillow python-slugify psutil raven mozjpeg-lossless-optimization
######################################################################################
FROM --platform=linux/amd64 python:3.11-slim-buster as build-amd64
COPY --from=compile-amd64 /opt/venv /opt/venv
FROM --platform=linux/arm64 python:3.11-slim-buster as build-arm64
COPY --from=compile-arm64 /opt/venv /opt/venv
FROM --platform=linux/arm/v7 python:3.11-slim-buster as build-armv7
COPY --from=compile-armv7 /opt/venv /opt/venv
######################################################################################
# Select final stage based on TARGETARCH ARG
FROM build-${TARGETARCH}${TARGETVARIANT}
LABEL com.kcc.name="Kindle Comic Converter"
LABEL com.kcc.author="Ciro Mattia Gonano, Paweł Jastrzębski and Darodi"
LABEL org.opencontainers.image.description='Kindle Comic Converter'
LABEL org.opencontainers.image.documentation='https://github.com/ciromattia/kcc'
LABEL org.opencontainers.image.source='https://github.com/ciromattia/kcc'
LABEL org.opencontainers.image.authors='darodi'
LABEL org.opencontainers.image.url='https://github.com/ciromattia/kcc'
LABEL org.opencontainers.image.documentation='https://github.com/ciromattia/kcc'
LABEL org.opencontainers.image.vendor='ciromattia'
LABEL org.opencontainers.image.licenses='ISC'
LABEL org.opencontainers.image.title="Kindle Comic Converter"
ENV PATH="/opt/venv/bin:$PATH"
WORKDIR /app
COPY . /opt/kcc
RUN DEBIAN_FRONTEND=noninteractive apt-get update -y && apt-get -yq upgrade && \
apt-get install -y p7zip-full unrar-free && \
ln -s /app/kindlegen /bin/kindlegen && \
cat /opt/kcc/kindlecomicconverter/__init__.py | grep version | awk '{print $3}' | sed "s/'//g" > /IMAGE_VERSION
ENTRYPOINT ["/opt/kcc/kcc-c2e.py"]
CMD ["-h"]

View File

@@ -2,6 +2,7 @@
Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com> Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
Copyright (c) 2013-2019 Paweł Jastrzębski <pawelj@iosphe.re> Copyright (c) 2013-2019 Paweł Jastrzębski <pawelj@iosphe.re>
Copyright (c) 2021-2023 Darodi
Permission to use, copy, modify, and/or distribute this software for Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the any purpose with or without fee is hereby granted, provided that the

135
README.md
View File

@@ -1,6 +1,10 @@
# KCC # KCC
[![GitHub release](https://img.shields.io/github/release/ciromattia/kcc.svg)](https://github.com/ciromattia/kcc/releases) [![PyPI](https://img.shields.io/pypi/v/KindleComicConverter.svg)](https://pypi.python.org/pypi/KindleComicConverter) [![AUR](https://img.shields.io/aur/version/kcc.svg)](https://aur.archlinux.org/packages/kcc/)
[![GitHub release](https://img.shields.io/github/release/ciromattia/kcc.svg)](https://github.com/ciromattia/kcc/releases)
[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/ciromattia/kcc/docker-publish.yml?label=docker%20build)](https://github.com/ciromattia/kcc/pkgs/container/kcc)
**Kindle Comic Converter** is a Python app to convert comic/manga files or folders to EPUB, Panel View MOBI or E-Ink optimized CBZ. **Kindle Comic Converter** is a Python app to convert comic/manga files or folders to EPUB, Panel View MOBI or E-Ink optimized CBZ.
It was initially developed for Kindle but since version 4.6 it outputs valid EPUB 3.0 so _**despite its name, KCC is It was initially developed for Kindle but since version 4.6 it outputs valid EPUB 3.0 so _**despite its name, KCC is
@@ -10,7 +14,7 @@ It can also optionally optimize images by applying a number of transformations.
### A word of warning ### A word of warning
**KCC** _is not_ [Amazon's Kindle Comic Creator](http://www.amazon.com/gp/feature.html?ie=UTF8&docId=1001103761) nor is in any way endorsed by Amazon. **KCC** _is not_ [Amazon's Kindle Comic Creator](http://www.amazon.com/gp/feature.html?ie=UTF8&docId=1001103761) nor is in any way endorsed by Amazon.
Amazon's tool is for comic publishers and involves a lot of manual effort, while **KCC** is for comic/manga readers. Amazon's tool is for comic publishers and involves a lot of manual effort, while **KCC** is for comic/manga readers.
_KC2_ in no way is a replacement for **KCC** so you can be quite confident we'll going to carry on developing our little monster ;-) _KC2_ in no way is a replacement for **KCC** so you can be quite confident we are going to carry on developing our little monster ;-)
### Issues / new features / donations ### Issues / new features / donations
If you have general questions about usage, feedback etc. please [post it here](http://www.mobileread.com/forums/showthread.php?t=207461). If you have general questions about usage, feedback etc. please [post it here](http://www.mobileread.com/forums/showthread.php?t=207461).
@@ -25,36 +29,55 @@ If you find **KCC** valuable you can consider donating to the authors:
- [![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/)
## BINARY RELEASES
You can find the latest released binary at the following links:
- **[Windows](http://kcc.iosphe.re/Windows/) (64-bit only)**
- **[macOS](http://kcc.iosphe.re/OSX/) (10.12+)**
- **Linux:** Currently unavailable.
## PYPI ## INSTALLATION
**KCC** is also available on PyPI.
```
pip install KindleComicConverter
```
## DEPENDENCIES ### BINARY RELEASES
You can find the latest binary at the following link:
- **https://github.com/ciromattia/kcc/releases**
- flatpak : https://flathub.org/apps/details/io.github.ciromattia.kcc
- Docker: https://github.com/ciromattia/kcc/pkgs/container/kcc
~~- **[Windows](http://kcc.iosphe.re/Windows/) (64-bit only)**~~
~~- **[macOS](http://kcc.iosphe.re/OSX/) (10.14+)**~~
~~- **Linux:** Currently unavailable.~~
more information on [installation](https://github.com/ciromattia/kcc/wiki/Installation)
### DEPENDENCIES
Following software is required to run Linux version of **KCC** and/or bare sources: Following software is required to run Linux version of **KCC** and/or bare sources:
- Python 3.3+ - Python 3.3+
- [PyQt5](https://pypi.python.org/pypi/PyQt5) 5.6.0+ - [PyQt5](https://pypi.python.org/pypi/PyQt5) 5.6.0+ (only needed for GUI)
- [Pillow](https://pypi.python.org/pypi/Pillow/) 4.0.0+ - [Pillow](https://pypi.python.org/pypi/Pillow/) 4.0.0+ (5.2.0+ needed for WebP support)
- [psutil](https://pypi.python.org/pypi/psutil) 5.0.0+ - [psutil](https://pypi.python.org/pypi/psutil) 5.0.0+
- [python-slugify](https://pypi.python.org/pypi/python-slugify) 1.2.1+ - [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+ - [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: On Debian based distributions these two commands should install all needed dependencies:
```
sudo apt-get install python3 python3-dev python3-pip libpng-dev libjpeg-dev p7zip-full
sudo pip3 install --upgrade pillow python-slugify psutil pyqt5 raven
```bash
$ 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
``` ```
### Optional dependencies
- [KindleGen](http://www.amazon.com/gp/feature.html?ie=UTF8&docId=1000765211) v2.9+ in a directory reachable by your _PATH_ or in _KCC_ directory *(For MOBI generation)* #### Optional dependencies
- 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+ in a directory reachable by your _PATH_ or in _KCC_ directory *(For MOBI generation)
- It can be found in [Kindle Previewer](https://www.amazon.com/Kindle-Previewer/b?ie=UTF8&node=21381691011)
`Amazon Kindle Previewer 3 Folder\lib\fc\bin`, the usual location in windows is in windows is `C:\Users\user\AppData\Local\Amazon\Kindle Previewer 3\lib\fc\bin\`
- [7z](http://www.7-zip.org/download.html) *(For CBZ/ZIP, CBR/RAR, 7z/CB7 support)* - [7z](http://www.7-zip.org/download.html) *(For CBZ/ZIP, CBR/RAR, 7z/CB7 support)*
- Unrar (no rar in 7z on Fedora)
## 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:
@@ -64,6 +87,8 @@ sudo pip3 install --upgrade pillow python-slugify psutil pyqt5 raven
- CB7, 7Z *(With `7z` executable)* - CB7, 7Z *(With `7z` executable)*
- PDF *(Only extracting JPG images)* - PDF *(Only extracting JPG images)*
Add 7z to PATH via `setx path "%path%;C:\Program Files\7-Zip"`
## USAGE ## USAGE
Should be pretty self-explanatory. All options have detailed information in tooltips. Should be pretty self-explanatory. All options have detailed information in tooltips.
@@ -72,6 +97,41 @@ After completed conversion, you should find ready file alongside the original in
Please check [our wiki](https://github.com/ciromattia/kcc/wiki/) for more details. Please check [our wiki](https://github.com/ciromattia/kcc/wiki/) for more details.
CLI version of **KCC** is intended for power users. It allows using options that might not be compatible and decrease the quality of output. CLI version of **KCC** is intended for power users. It allows using options that might not be compatible and decrease the quality of output.
CLI version has reduced dependencies, on Debian based distributions this commands should install all needed dependencies:
```
sudo apt-get install python3 p7zip-full python3-pil python3-psutil python3-slugify
```
### Profiles:
```
'K1': ("Kindle 1", (600, 670), Palette4, 1.8),
'K11': ("Kindle 11", (1072, 1448), Palette16, 1.8),
'K2': ("Kindle 2", (600, 670), Palette15, 1.8),
'K34': ("Kindle Keyboard/Touch", (600, 800), Palette16, 1.8),
'K578': ("Kindle", (600, 800), Palette16, 1.8),
'KDX': ("Kindle DX/DXG", (824, 1000), Palette16, 1.8),
'KPW': ("Kindle Paperwhite 1/2", (758, 1024), Palette16, 1.8),
'KV': ("Kindle Paperwhite 3/4/Voyage/Oasis", (1072, 1448), Palette16, 1.8),
'KPW5': ("Kindle Paperwhite 5/Signature Edition", (1236, 1648), Palette16, 1.8),
'KO': ("Kindle Oasis 2/3", (1264, 1680), Palette16, 1.8),
'KS': ("Kindle Scribe", (1860, 2480), Palette16, 1.8),
'KoMT': ("Kobo Mini/Touch", (600, 800), Palette16, 1.8),
'KoG': ("Kobo Glo", (768, 1024), Palette16, 1.8),
'KoGHD': ("Kobo Glo HD", (1072, 1448), Palette16, 1.8),
'KoA': ("Kobo Aura", (758, 1024), Palette16, 1.8),
'KoAHD': ("Kobo Aura HD", (1080, 1440), Palette16, 1.8),
'KoAH2O': ("Kobo Aura H2O", (1080, 1430), Palette16, 1.8),
'KoAO': ("Kobo Aura ONE", (1404, 1872), Palette16, 1.8),
'KoN': ("Kobo Nia", (758, 1024), Palette16, 1.8),
'KoC': ("Kobo Clara HD/Kobo Clara 2E", (1072, 1448), Palette16, 1.8),
'KoL': ("Kobo Libra H2O/Kobo Libra 2", (1264, 1680), Palette16, 1.8),
'KoF': ("Kobo Forma", (1440, 1920), Palette16, 1.8),
'KoS': ("Kobo Sage", (1440, 1920), Palette16, 1.8),
'KoE': ("Kobo Elipsa", (1404, 1872), Palette16, 1.8),
'OTHER': ("Other", (0, 0), Palette16, 1.8),
```
### Standalone `kcc-c2e.py` usage: ### Standalone `kcc-c2e.py` usage:
@@ -82,12 +142,16 @@ Options:
MAIN: MAIN:
-p PROFILE, --profile=PROFILE -p PROFILE, --profile=PROFILE
Device profile (Available options: K1, K2, K34, K578, Device profile (Available options: K1, K2, K34, K578,
KDX, KPW, KV, KO, KoMT, KoG, KoGHD, KoA, KoAHD, KoAH2O, KDX, KPW, KPW5, KV, KO, K11, KS, KoMT, KoG, KoGHD,
KoAO, KoF) [Default=KV] KoA, KoAHD, KoAH2O, KoAO, KoN, KoC, KoL, KoF, KoS,
KoE) [Default=KV]
-m, --manga-style Manga style (right-to-left reading and splitting) -m, --manga-style Manga style (right-to-left reading and splitting)
-q, --hq Try to increase the quality of magnification -q, --hq Try to increase the quality of magnification
-2, --two-panel Display two not four panels in Panel View mode -2, --two-panel Display two not four panels in Panel View mode
-w, --webtoon Webtoon processing mode -w, --webtoon Webtoon processing mode
--targetsize=TARGETSIZE
the maximal size of output file in MB. [Default=100MB
for webtoon and 400MB for others]
OUTPUT SETTINGS: OUTPUT SETTINGS:
-o OUTPUT, --output=OUTPUT -o OUTPUT, --output=OUTPUT
@@ -96,13 +160,15 @@ Options:
Comic title [Default=filename or directory name] Comic title [Default=filename or directory name]
-f FORMAT, --format=FORMAT -f FORMAT, --format=FORMAT
Output format (Available options: Auto, MOBI, EPUB, Output format (Available options: Auto, MOBI, EPUB,
CBZ, KFX) [Default=Auto] CBZ, KFX, MOBI+EPUB) [Default=Auto]
-b BATCHSPLIT, --batchsplit=BATCHSPLIT -b BATCHSPLIT, --batchsplit=BATCHSPLIT
Split output into multiple files. 0: Don't split 1: Split output into multiple files. 0: Don't split 1:
Automatic mode 2: Consider every subdirectory as Automatic mode 2: Consider every subdirectory as
separate volume [Default=0] separate volume [Default=0]
PROCESSING: PROCESSING:
-n, --noprocessing Do not modify image and ignore any profil or
processing option
-u, --upscale Resize images smaller than device's resolution -u, --upscale Resize images smaller than device's resolution
-s, --stretch Stretch images to device's resolution -s, --stretch Stretch images to device's resolution
-r SPLITTER, --splitter=SPLITTER -r SPLITTER, --splitter=SPLITTER
@@ -116,10 +182,16 @@ Options:
page numbers [Default=2] page numbers [Default=2]
--cp=CROPPINGP, --croppingpower=CROPPINGP --cp=CROPPINGP, --croppingpower=CROPPINGP
Set cropping power [Default=1.0] Set cropping power [Default=1.0]
--cm=CROPPINGM, --croppingminimum=CROPPINGM
Set cropping minimum area ratio [Default=0.0]
--blackborders Disable autodetection and force black borders --blackborders Disable autodetection and force black borders
--whiteborders Disable autodetection and force white borders --whiteborders Disable autodetection and force white borders
--forcecolor Don't convert images to grayscale --forcecolor Don't convert images to grayscale
--forcepng Create PNG files instead JPEG --forcepng Create PNG files instead JPEG
--mozjpeg Create JPEG files using mozJpeg
--maximizestrips Turn 1x4 strips to 2x2 strips
-d, --delete Delete source file(s) or a directory. It's not
recoverable.
CUSTOM PROFILE: CUSTOM PROFILE:
--customwidth=CUSTOMWIDTH --customwidth=CUSTOMWIDTH
@@ -141,7 +213,8 @@ Options:
-y HEIGHT, --height=HEIGHT -y HEIGHT, --height=HEIGHT
Height of the target device screen Height of the target device screen
-i, --in-place Overwrite source directory -i, --in-place Overwrite source directory
-m, --merge Combine every directory into a single image before splitting -m, --merge Combine every directory into a single image before
splitting
OTHER: OTHER:
-d, --debug Create debug file for every split image -d, --debug Create debug file for every split image
@@ -149,7 +222,7 @@ Options:
``` ```
## CREDITS ## CREDITS
**KCC** is made by [Ciro Mattia Gonano](http://github.com/ciromattia) and [Paweł Jastrzębski](http://github.com/AcidWeb). **KCC** is made by [Ciro Mattia Gonano](http://github.com/ciromattia), [Paweł Jastrzębski](http://github.com/AcidWeb) and [Darodi](http://github.com/darodi) .
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)).
@@ -160,7 +233,7 @@ The app relies and includes the following scripts:
- Icon is by **Nikolay Verin** ([http://ncrow.deviantart.com/](http://ncrow.deviantart.com/)) and released under [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/) License. - Icon is by **Nikolay Verin** ([http://ncrow.deviantart.com/](http://ncrow.deviantart.com/)) and released under [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/) License.
## SAMPLE FILES CREATED BY KCC ## SAMPLE FILES CREATED BY KCC
* [Kindle Oasis 2](http://kcc.iosphe.re/Samples/Ubunchu!-KO.mobi) * [Kindle Oasis 2 / 3](http://kcc.iosphe.re/Samples/Ubunchu!-KO.mobi)
* [Kindle Paperwhite 3 / 4 / Voyage / Oasis](http://kcc.iosphe.re/Samples/Ubunchu!-KV.mobi) * [Kindle Paperwhite 3 / 4 / Voyage / Oasis](http://kcc.iosphe.re/Samples/Ubunchu!-KV.mobi)
* [Kindle Paperwhite 1 / 2](http://kcc.iosphe.re/Samples/Ubunchu!-KPW.mobi) * [Kindle Paperwhite 1 / 2](http://kcc.iosphe.re/Samples/Ubunchu!-KPW.mobi)
* [Kindle](http://kcc.iosphe.re/Samples/Ubunchu!-K578.mobi) * [Kindle](http://kcc.iosphe.re/Samples/Ubunchu!-K578.mobi)
@@ -173,11 +246,11 @@ The app relies and includes the following scripts:
## PRIVACY ## PRIVACY
**KCC** is initiating internet connections in two cases: **KCC** is initiating internet connections in two cases:
* During startup - Version check. * During startup - Version check.
* When error occurs - Automatic reporting on Windows and MacOS. * When error occurs - Automatic reporting on Windows and macOS.
## KNOWN ISSUES ## KNOWN ISSUES
Please check [wiki page](https://github.com/ciromattia/kcc/wiki/Known-issues). Please check [wiki page](https://github.com/ciromattia/kcc/wiki/Known-issues).
## COPYRIGHT ## COPYRIGHT
Copyright (c) 2012-2019 Ciro Mattia Gonano and Paweł Jastrzębski. Copyright (c) 2012-2023 Ciro Mattia Gonano, Paweł Jastrzębski and Darodi.
**KCC** is released under ISC LICENSE; see LICENSE.txt for further details. **KCC** is released under ISC LICENSE; see [LICENSE.txt](./LICENSE.txt) for further details.

View File

@@ -0,0 +1,320 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="48px"
height="48px"
id="svg3832"
version="1.1"
inkscape:version="0.47 r22583"
sodipodi:docname="appimage-assistant_alt3.svg">
<defs
id="defs3834">
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3308-4-6-931-761-0"
id="linearGradient2975"
gradientUnits="userSpaceOnUse"
x1="24.3125"
y1="22.96875"
x2="24.3125"
y2="41.03125" />
<linearGradient
id="linearGradient3308-4-6-931-761-0">
<stop
id="stop2919-2"
style="stop-color:#ffffff;stop-opacity:1"
offset="0" />
<stop
id="stop2921-76"
style="stop-color:#ffffff;stop-opacity:0"
offset="1" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4222"
id="linearGradient2979"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0,0.3704967,-0.3617496,0,33.508315,6.1670925)"
x1="7.6485429"
y1="26.437023"
x2="41.861729"
y2="26.437023" />
<linearGradient
id="linearGradient4222">
<stop
id="stop4224"
style="stop-color:#ffffff;stop-opacity:1"
offset="0" />
<stop
id="stop4226"
style="stop-color:#ffffff;stop-opacity:0"
offset="1" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3308-4-6-931-761"
id="linearGradient2982"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(0,0.9999987)"
x1="23.99999"
y1="4.999989"
x2="23.99999"
y2="43" />
<linearGradient
id="linearGradient3308-4-6-931-761">
<stop
id="stop2919"
style="stop-color:#ffffff;stop-opacity:1"
offset="0" />
<stop
id="stop2921"
style="stop-color:#ffffff;stop-opacity:0"
offset="1" />
</linearGradient>
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3575"
id="radialGradient2985"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0,1.0262008,-1.6561124,9.4072203e-4,-56.097482,-45.332325)"
cx="48.42384"
cy="-48.027504"
fx="48.42384"
fy="-48.027504"
r="38.212933" />
<linearGradient
id="linearGradient3575">
<stop
id="stop3577"
style="stop-color:#fafafa;stop-opacity:1"
offset="0" />
<stop
id="stop3579"
style="stop-color:#e6e6e6;stop-opacity:1"
offset="1" />
</linearGradient>
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3993"
id="radialGradient2990"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0,2.0478765,-2.7410544,-8.6412258e-8,47.161382,-8.837436)"
cx="9.3330879"
cy="8.4497671"
fx="9.3330879"
fy="8.4497671"
r="19.99999" />
<linearGradient
id="linearGradient3993">
<stop
offset="0"
style="stop-color:#a3c0d0;stop-opacity:1"
id="stop3995" />
<stop
offset="1"
style="stop-color:#427da1;stop-opacity:1"
id="stop4001" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient2508"
id="linearGradient2992"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(0,0.9674382)"
x1="14.048676"
y1="44.137306"
x2="14.048676"
y2="4.0000005" />
<linearGradient
id="linearGradient2508">
<stop
offset="0"
style="stop-color:#2e4a5a;stop-opacity:1"
id="stop2510" />
<stop
offset="1"
style="stop-color:#6e8796;stop-opacity:1"
id="stop2512" />
</linearGradient>
<radialGradient
cx="4.9929786"
cy="43.5"
r="2.5"
fx="4.9929786"
fy="43.5"
id="radialGradient2873-966-168"
xlink:href="#linearGradient3688-166-749"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)" />
<linearGradient
id="linearGradient3688-166-749">
<stop
id="stop2883"
style="stop-color:#181818;stop-opacity:1"
offset="0" />
<stop
id="stop2885"
style="stop-color:#181818;stop-opacity:0"
offset="1" />
</linearGradient>
<radialGradient
cx="4.9929786"
cy="43.5"
r="2.5"
fx="4.9929786"
fy="43.5"
id="radialGradient2875-742-326"
xlink:href="#linearGradient3688-464-309"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)" />
<linearGradient
id="linearGradient3688-464-309">
<stop
id="stop2889"
style="stop-color:#181818;stop-opacity:1"
offset="0" />
<stop
id="stop2891"
style="stop-color:#181818;stop-opacity:0"
offset="1" />
</linearGradient>
<linearGradient
x1="25.058096"
y1="47.027729"
x2="25.058096"
y2="39.999443"
id="linearGradient2877-634-617"
xlink:href="#linearGradient3702-501-757"
gradientUnits="userSpaceOnUse" />
<linearGradient
id="linearGradient3702-501-757">
<stop
id="stop2895"
style="stop-color:#181818;stop-opacity:0"
offset="0" />
<stop
id="stop2897"
style="stop-color:#181818;stop-opacity:1"
offset="0.5" />
<stop
id="stop2899"
style="stop-color:#181818;stop-opacity:0"
offset="1" />
</linearGradient>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="7"
inkscape:cx="24"
inkscape:cy="24"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:grid-bbox="true"
inkscape:document-units="px"
inkscape:window-width="603"
inkscape:window-height="484"
inkscape:window-x="417"
inkscape:window-y="162"
inkscape:window-maximized="0" />
<metadata
id="metadata3837">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer">
<g
style="display:inline"
id="g2036"
transform="matrix(1.1,0,0,0.4444449,-2.4000022,25.11107)">
<g
style="opacity:0.4"
id="g3712"
transform="matrix(1.052632,0,0,1.285713,-1.263158,-13.42854)">
<rect
style="fill:url(#radialGradient2873-966-168);fill-opacity:1;stroke:none"
id="rect2801"
y="40"
x="38"
height="7"
width="5" />
<rect
style="fill:url(#radialGradient2875-742-326);fill-opacity:1;stroke:none"
id="rect3696"
transform="scale(-1,-1)"
y="-47"
x="-10"
height="7"
width="5" />
<rect
style="fill:url(#linearGradient2877-634-617);fill-opacity:1;stroke:none"
id="rect3700"
y="40"
x="10"
height="7.0000005"
width="28" />
</g>
</g>
<rect
style="fill:url(#radialGradient2990);fill-opacity:1;stroke:url(#linearGradient2992);stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect5505"
y="5.4674392"
x="4.5"
ry="2.2322156"
rx="2.2322156"
height="39"
width="39" />
<path
style="opacity:0.05;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.00178742;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="path4294-1"
d="m 21,6.9687498 a 2.0165107,2.0165107 0 0 0 -2.03125,2.03125 l 0,3.9687502 -1.15625,0 a 2.0165107,2.0165107 0 0 0 -1.5,3.375 l 5.0625,5.75 c -0.06312,0.110777 -0.178724,0.246032 -0.21875,0.34375 -0.195898,0.478256 -0.25,0.83653 -0.25,1.21875 l 0,0.125 L 20.8125,23.6875 C 20.534322,23.409323 20.213169,23.162739 19.71875,22.96875 19.47154,22.87176 19.185456,22.791748 18.75,22.8125 c -0.435456,0.02075 -1.054055,0.210302 -1.46875,0.625 L 15.75,24.96875 c -0.414689,0.414689 -0.604245,1.033294 -0.625,1.46875 -0.02075,0.435456 0.05925,0.721537 0.15625,0.96875 C 15.475241,27.900677 15.721817,28.221821 16,28.5 l 0.09375,0.09375 -0.125,0 c -0.382218,0 -0.740493,0.0541 -1.21875,0.25 -0.239128,0.09795 -0.538285,0.214988 -0.84375,0.53125 -0.305465,0.316262 -0.625,0.914788 -0.625,1.53125 l 0,2.1875 c 0,0.616465 0.319536,1.214989 0.625,1.53125 0.305464,0.316261 0.604622,0.433301 0.84375,0.53125 0.478256,0.195898 0.83653,0.25 1.21875,0.25 l 0.125,0 L 16,35.5 c -0.278175,0.278176 -0.52476,0.599329 -0.71875,1.09375 -0.09699,0.24721 -0.177003,0.533292 -0.15625,0.96875 0.02075,0.435458 0.210304,1.054058 0.625,1.46875 l 1.53125,1.53125 c 0.414691,0.414697 1.033292,0.604245 1.46875,0.625 0.435458,0.02076 0.721537,-0.05926 0.96875,-0.15625 0.494425,-0.19399 0.81557,-0.440568 1.09375,-0.71875 l 0.09375,-0.09375 0,0.125 c 0,0.38222 0.0541,0.740495 0.25,1.21875 0.09795,0.239127 0.214989,0.538285 0.53125,0.84375 0.316261,0.305465 0.914783,0.625 1.53125,0.625 l 2.1875,0 c 0.616466,0 1.214989,-0.319534 1.53125,-0.625 0.316261,-0.305466 0.433302,-0.604622 0.53125,-0.84375 0.195896,-0.478255 0.25,-0.836532 0.25,-1.21875 l 0,-0.125 0.09375,0.09375 c 0.278176,0.278175 0.599329,0.52476 1.09375,0.71875 0.24721,0.09699 0.533292,0.177003 0.96875,0.15625 0.435458,-0.02075 1.054058,-0.210304 1.46875,-0.625 L 32.875,39.03125 C 33.289697,38.616559 33.479245,37.997958 33.5,37.5625 33.52076,37.127042 33.44074,36.840963 33.34375,36.59375 33.14976,36.099325 32.903182,35.77818 32.625,35.5 l -0.09375,-0.09375 0.125,0 c 0.38222,0 0.740494,-0.0541 1.21875,-0.25 0.239128,-0.09795 0.538286,-0.214988 0.84375,-0.53125 0.305464,-0.316262 0.625,-0.914787 0.625,-1.53125 l 0,-2.1875 c 0,-0.61646 -0.319535,-1.214987 -0.625,-1.53125 -0.305465,-0.316263 -0.604621,-0.433301 -0.84375,-0.53125 -0.478257,-0.195898 -0.836532,-0.25 -1.21875,-0.25 l -0.125,0 L 32.625,28.5 c 0.278177,-0.278177 0.52476,-0.599329 0.71875,-1.09375 C 33.44074,27.15904 33.520753,26.872957 33.5,26.4375 33.47925,26.002043 33.289697,25.383443 32.875,24.96875 L 31.34375,23.4375 c -0.414688,-0.414694 -1.03329,-0.604245 -1.46875,-0.625 -0.43546,-0.02076 -0.721537,0.05925 -0.96875,0.15625 -0.494426,0.193991 -0.815572,0.44057 -1.09375,0.71875 l -0.09375,0.09375 0,-0.125 c 0,-0.382218 -0.0541,-0.740493 -0.25,-1.21875 -0.09112,-0.22245 -0.228127,-0.500183 -0.5,-0.78125 l 4.71875,-5.3125 a 2.0165107,2.0165107 0 0 0 -1.5,-3.375 l -1.15625,0 0,-3.9687502 A 2.0165107,2.0165107 0 0 0 27,6.9687498 l -6,0 z M 24.3125,31.25 c 0.427097,0 0.75,0.322904 0.75,0.75 0,0.427096 -0.322903,0.75 -0.75,0.75 -0.427094,0 -0.75,-0.322906 -0.75,-0.75 0,-0.427094 0.322906,-0.75 0.75,-0.75 z" />
<path
style="opacity:0.05;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.00178742;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="path4294"
d="m 20.90625,8.0312498 a 0.96385067,0.96385067 0 0 0 -0.875,0.96875 l 0,5.0312502 -2.21875,0 A 0.96385067,0.96385067 0 0 0 17.09375,15.625 l 5.78125,6.53125 c -0.158814,0.0616 -0.341836,0.0951 -0.4375,0.1875 -0.169161,0.163386 -0.252971,0.323419 -0.3125,0.46875 -0.119058,0.290663 -0.15625,0.566746 -0.15625,0.84375 l 0,1.65625 C 21.718163,25.40233 21.485871,25.509772 21.25,25.625 l -1.1875,-1.1875 c -0.199651,-0.19965 -0.421433,-0.352095 -0.71875,-0.46875 -0.148659,-0.05833 -0.329673,-0.104846 -0.5625,-0.09375 -0.232827,0.0111 -0.53583,0.09833 -0.75,0.3125 L 16.5,25.71875 c -0.214168,0.214168 -0.301403,0.517173 -0.3125,0.75 -0.0111,0.232827 0.03542,0.41384 0.09375,0.5625 0.116655,0.297321 0.269096,0.519099 0.46875,0.71875 l 1.1875,1.1875 c -0.115228,0.235871 -0.222668,0.468163 -0.3125,0.71875 l -1.65625,0 c -0.277003,0 -0.553087,0.03719 -0.84375,0.15625 -0.145332,0.05953 -0.305363,0.143338 -0.46875,0.3125 -0.163387,0.169162 -0.3125,0.46403 -0.3125,0.78125 l 0,2.1875 c 0,0.317221 0.149114,0.612089 0.3125,0.78125 0.163386,0.169161 0.323419,0.252971 0.46875,0.3125 0.290663,0.119058 0.566746,0.15625 0.84375,0.15625 l 1.65625,0 c 0.08983,0.250587 0.197272,0.482879 0.3125,0.71875 L 16.75,36.25 c -0.199649,0.19965 -0.352095,0.421432 -0.46875,0.71875 -0.05833,0.148659 -0.104846,0.329672 -0.09375,0.5625 0.0111,0.232828 0.09833,0.535831 0.3125,0.75 l 1.53125,1.53125 c 0.214168,0.214172 0.517172,0.301403 0.75,0.3125 0.232828,0.0111 0.41384,-0.03542 0.5625,-0.09375 0.29732,-0.116655 0.519098,-0.269096 0.71875,-0.46875 L 21.25,38.375 c 0.235871,0.115228 0.468164,0.222668 0.71875,0.3125 l 0,1.65625 c 0,0.277003 0.03719,0.553087 0.15625,0.84375 0.05953,0.145331 0.143339,0.305364 0.3125,0.46875 0.169161,0.163386 0.464028,0.3125 0.78125,0.3125 l 2.1875,0 c 0.317221,0 0.612089,-0.149113 0.78125,-0.3125 0.169161,-0.163387 0.252971,-0.323419 0.3125,-0.46875 0.119057,-0.290663 0.15625,-0.566748 0.15625,-0.84375 l 0,-1.65625 c 0.250586,-0.08983 0.482879,-0.197272 0.71875,-0.3125 l 1.1875,1.1875 c 0.19965,0.199649 0.421432,0.352095 0.71875,0.46875 0.148659,0.05833 0.329672,0.104846 0.5625,0.09375 0.232828,-0.0111 0.535831,-0.09833 0.75,-0.3125 L 32.125,38.28125 c 0.214172,-0.214168 0.301403,-0.517172 0.3125,-0.75 0.0111,-0.232828 -0.03542,-0.41384 -0.09375,-0.5625 C 32.227095,36.67143 32.074654,36.449652 31.875,36.25 L 30.6875,35.0625 C 30.802728,34.82663 30.910168,34.594337 31,34.34375 l 1.65625,0 c 0.277004,0 0.553087,-0.03719 0.84375,-0.15625 0.145332,-0.05953 0.305364,-0.143339 0.46875,-0.3125 0.163386,-0.169161 0.3125,-0.46403 0.3125,-0.78125 l 0,-2.1875 c 0,-0.317219 -0.149114,-0.612088 -0.3125,-0.78125 C 33.805364,29.955838 33.645332,29.872029 33.5,29.8125 33.209336,29.693442 32.933253,29.65625 32.65625,29.65625 l -1.65625,0 C 30.91017,29.405663 30.802728,29.17337 30.6875,28.9375 L 31.875,27.75 c 0.19965,-0.19965 0.352095,-0.421432 0.46875,-0.71875 0.05833,-0.148659 0.104846,-0.329672 0.09375,-0.5625 -0.0111,-0.232828 -0.09833,-0.535831 -0.3125,-0.75 L 30.59375,24.1875 c -0.214167,-0.21417 -0.517171,-0.301403 -0.75,-0.3125 -0.232829,-0.0111 -0.41384,0.03542 -0.5625,0.09375 -0.29732,0.116656 -0.519099,0.269097 -0.71875,0.46875 L 27.375,25.625 c -0.235871,-0.115228 -0.468163,-0.222668 -0.71875,-0.3125 l 0,-1.65625 c 0,-0.277003 -0.03719,-0.553087 -0.15625,-0.84375 -0.05953,-0.145332 -0.143338,-0.305363 -0.3125,-0.46875 -0.169162,-0.163387 -0.46403,-0.3125 -0.78125,-0.3125 l -0.15625,0 5.65625,-6.40625 A 0.96385067,0.96385067 0 0 0 30.1875,14.03125 l -2.21875,0 0,-5.0312502 A 0.96385067,0.96385067 0 0 0 27,8.0312498 l -6,0 a 0.96385067,0.96385067 0 0 0 -0.09375,0 z M 24.3125,30.1875 c 1.002113,0 1.8125,0.810388 1.8125,1.8125 0,1.002112 -0.810387,1.8125 -1.8125,1.8125 C 23.31039,33.8125 22.5,33.002111 22.5,32 c 0,-1.002111 0.81039,-1.8125 1.8125,-1.8125 z" />
<path
style="fill:url(#radialGradient2985);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.00178742;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="path2317"
d="M 21,8.9999996 21,15 17.8125,15 24,22 30.1875,15 27,15 l 0,-6.0000004 -6,0 z M 23.21875,23 c -0.172892,0 -0.28125,0.294922 -0.28125,0.65625 l 0,2.28125 C 22.24145,26.095996 21.585954,26.379869 21,26.75 l -1.625,-1.625 c -0.255498,-0.255497 -0.533998,-0.372253 -0.65625,-0.25 l -1.53125,1.53125 c -0.122254,0.122254 -0.0055,0.400753 0.25,0.65625 l 1.625,1.625 c -0.37013,0.585953 -0.654003,1.24145 -0.8125,1.9375 l -2.28125,0 c -0.361328,0 -0.65625,0.108357 -0.65625,0.28125 l 0,2.1875 c 0,0.172892 0.294922,0.28125 0.65625,0.28125 l 2.28125,0 c 0.158497,0.69605 0.44237,1.351546 0.8125,1.9375 l -1.625,1.625 c -0.255497,0.255498 -0.372254,0.533997 -0.25,0.65625 l 1.53125,1.53125 c 0.122252,0.122254 0.400752,0.0055 0.65625,-0.25 L 21,37.25 c 0.585954,0.37013 1.24145,0.654002 1.9375,0.8125 l 0,2.28125 C 22.9375,40.705077 23.045858,41 23.21875,41 l 2.1875,0 c 0.172893,0 0.28125,-0.294924 0.28125,-0.65625 l 0,-2.28125 c 0.69605,-0.158498 1.351546,-0.44237 1.9375,-0.8125 l 1.625,1.625 c 0.255498,0.255497 0.533997,0.372254 0.65625,0.25 l 1.53125,-1.53125 c 0.122254,-0.122252 0.0055,-0.400752 -0.25,-0.65625 l -1.625,-1.625 c 0.370129,-0.585954 0.654003,-1.24145 0.8125,-1.9375 l 2.28125,0 c 0.361329,0 0.65625,-0.108358 0.65625,-0.28125 l 0,-2.1875 c 0,-0.172893 -0.294921,-0.28125 -0.65625,-0.28125 l -2.28125,0 c -0.158497,-0.69605 -0.442371,-1.351547 -0.8125,-1.9375 l 1.625,-1.625 c 0.255497,-0.255497 0.372254,-0.533997 0.25,-0.65625 L 29.90625,24.875 C 29.783997,24.752745 29.505498,24.8695 29.25,25.125 l -1.625,1.625 c -0.585954,-0.370131 -1.24145,-0.654004 -1.9375,-0.8125 l 0,-2.28125 C 25.6875,23.294922 25.579143,23 25.40625,23 l -2.1875,0 z m 1.09375,6.21875 c 1.528616,0 2.78125,1.252635 2.78125,2.78125 0,1.528615 -1.252634,2.78125 -2.78125,2.78125 -1.528614,0 -2.78125,-1.252635 -2.78125,-2.78125 0,-1.528615 1.252636,-2.78125 2.78125,-2.78125 z" />
<rect
style="opacity:0.4;fill:none;stroke:url(#linearGradient2982);stroke-width:0.99999976;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect6741"
y="6.4999886"
x="5.4999981"
ry="1.365193"
rx="1.365193"
height="37.000011"
width="36.999985" />
<path
style="fill:none;stroke:url(#linearGradient2979);stroke-width:0.99829447;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
id="path2777"
d="M 28.926376,15.466668 24,21.177578 18.963089,15.5 21.5,15.5 l 0,-6.0000004 5,0 0,6.0000004 2.426376,-0.03333 z" />
<path
style="fill:none;stroke:url(#linearGradient2975);stroke-width:1;stroke-opacity:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="path4243"
d="m 23.4375,23.46875 c -0.01166,0.05381 -0.03125,0.100205 -0.03125,0.1875 l 0,2.28125 a 0.48185467,0.48185467 0 0 1 -0.375,0.46875 c -0.638467,0.145384 -1.238423,0.407111 -1.78125,0.75 a 0.48185467,0.48185467 0 0 1 -0.59375,-0.0625 l -1.625,-1.625 C 18.9779,25.4154 18.9477,25.40242 18.90625,25.375 l -1.21875,1.21875 c 0.02742,0.04145 0.0404,0.07165 0.09375,0.125 l 1.625,1.625 a 0.48185467,0.48185467 0 0 1 0.0625,0.59375 c -0.342888,0.542826 -0.604615,1.142782 -0.75,1.78125 a 0.48185467,0.48185467 0 0 1 -0.46875,0.375 l -2.28125,0 c -0.08729,0 -0.133695,0.01959 -0.1875,0.03125 l 0,1.75 c 0.05381,0.01166 0.100205,0.03125 0.1875,0.03125 l 2.28125,0 a 0.48185467,0.48185467 0 0 1 0.46875,0.375 c 0.145385,0.638468 0.407112,1.238423 0.75,1.78125 a 0.48185467,0.48185467 0 0 1 -0.0625,0.59375 l -1.625,1.625 c -0.05335,0.05335 -0.06633,0.08355 -0.09375,0.125 l 1.21875,1.21875 c 0.04145,-0.02742 0.07165,-0.0404 0.125,-0.09375 l 1.625,-1.625 A 0.48185467,0.48185467 0 0 1 21.25,36.84375 c 0.542827,0.342888 1.142781,0.604614 1.78125,0.75 a 0.48185467,0.48185467 0 0 1 0.375,0.46875 l 0,2.28125 c 0,0.08729 0.01959,0.133695 0.03125,0.1875 l 1.75,0 c 0.01166,-0.0538 0.03125,-0.100206 0.03125,-0.1875 l 0,-2.28125 a 0.48185467,0.48185467 0 0 1 0.375,-0.46875 c 0.638469,-0.145386 1.238423,-0.407112 1.78125,-0.75 a 0.48185467,0.48185467 0 0 1 0.59375,0.0625 l 1.625,1.625 c 0.05335,0.05335 0.08355,0.06633 0.125,0.09375 l 1.21875,-1.21875 c -0.02742,-0.04145 -0.0404,-0.07165 -0.09375,-0.125 l -1.625,-1.625 a 0.48185467,0.48185467 0 0 1 -0.0625,-0.59375 c 0.342888,-0.542828 0.604615,-1.142783 0.75,-1.78125 a 0.48185467,0.48185467 0 0 1 0.46875,-0.375 l 2.28125,0 c 0.08729,0 0.133695,-0.01959 0.1875,-0.03125 l 0,-1.75 c -0.0538,-0.01166 -0.100204,-0.03125 -0.1875,-0.03125 l -2.28125,0 a 0.48185467,0.48185467 0 0 1 -0.46875,-0.375 c -0.145385,-0.638467 -0.407113,-1.238424 -0.75,-1.78125 a 0.48185467,0.48185467 0 0 1 0.0625,-0.59375 l 1.625,-1.625 c 0.05335,-0.05335 0.06633,-0.08355 0.09375,-0.125 L 29.71875,25.375 c -0.04145,0.02742 -0.07165,0.0404 -0.125,0.09375 l -1.625,1.625 a 0.48185467,0.48185467 0 0 1 -0.59375,0.0625 c -0.542827,-0.342889 -1.142783,-0.604616 -1.78125,-0.75 a 0.48185467,0.48185467 0 0 1 -0.375,-0.46875 l 0,-2.28125 c 0,-0.0873 -0.01959,-0.133695 -0.03125,-0.1875 l -1.75,0 z m 0.875,5.28125 c 1.791829,0 3.25,1.458172 3.25,3.25 0,1.791828 -1.458171,3.25 -3.25,3.25 -1.791827,0 -3.25,-1.458172 -3.25,-3.25 0,-1.791828 1.458173,-3.25 3.25,-3.25 z" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 21 KiB

View File

@@ -2,24 +2,13 @@ environment:
PYTHON: "C:\\Python37-x64" PYTHON: "C:\\Python37-x64"
install: install:
- set PATH="%PYTHON%\\Scripts";"C:\\Program Files (x86)\\Inno Setup 5";%PATH% - set PATH="%PYTHON%\\Scripts";%PATH%
- "%PYTHON%\\python.exe -m pip install --upgrade pip setuptools wheel" - "%PYTHON%\\python.exe -m pip install --upgrade pip setuptools wheel"
- "%PYTHON%\\python.exe -m pip install -r requirements.txt" - "%PYTHON%\\python.exe -m pip install -r requirements.txt"
- "%PYTHON%\\python.exe -m pip install certifi https://github.com/pyinstaller/pyinstaller/archive/develop.zip" - "%PYTHON%\\python.exe -m pip install certifi https://github.com/pyinstaller/pyinstaller/archive/develop.zip"
- nuget install secure-file -ExcludeVersion
- secure-file\tools\secure-file -decrypt other\windows\sentry.py.enc -out kindlecomicconverter\sentry.py -secret %ENCRYPTION%
build_script: build_script:
- "%PYTHON%\\python.exe setup.py build_binary" - "%PYTHON%\\python.exe setup.py build_binary"
after_build: artifacts:
- ps: Get-ChildItem .\dist\KCC* | % { Push-AppveyorArtifact $_.FullName -FileName $_.Name } - path: dist\KCC*
deploy:
provider: S3
access_key_id:
secure: pWfyU8wtAHt354mBILwM41TemOjb+My9n3CRMnrpLzI=
secret_access_key:
secure: G0Xpxe355LMqV3s8v+TsdJYdmhFoKKA+mxK37Tlu8yNwKXKJgcnY7pcFKSdX5xS5
bucket: kcc-deploy
region: eu-central-1

11
gen_ui_files.bat Normal file
View File

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

10
gen_ui_files.sh Executable file
View File

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

View File

@@ -11,6 +11,7 @@
<file>../icons/CBZ.png</file> <file>../icons/CBZ.png</file>
<file>../icons/EPUB.png</file> <file>../icons/EPUB.png</file>
<file>../icons/MOBI.png</file> <file>../icons/MOBI.png</file>
<file>../icons/KFX.png</file>
</qresource> </qresource>
<qresource prefix="Status"> <qresource prefix="Status">
<file>../icons/error.png</file> <file>../icons/error.png</file>

View File

@@ -22,118 +22,7 @@
<property name="bottomMargin"> <property name="bottomMargin">
<number>5</number> <number>5</number>
</property> </property>
<item row="1" column="0" colspan="2"> <item row="5" column="0" colspan="2">
<widget class="QProgressBar" name="progressBar">
<property name="minimumSize">
<size>
<width>0</width>
<height>30</height>
</size>
</property>
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="visible">
<bool>false</bool>
</property>
<property name="alignment">
<set>Qt::AlignJustify|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<widget class="QListWidget" name="jobList">
<property name="styleSheet">
<string notr="true">QListWidget#jobList {background:#ffffff;background-image:url(:/Other/icons/list_background.png);background-position:center center;background-repeat:no-repeat;color:rgb(0,0,0);}</string>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::NoSelection</enum>
</property>
<property name="verticalScrollMode">
<enum>QAbstractItemView::ScrollPerPixel</enum>
</property>
<property name="horizontalScrollMode">
<enum>QAbstractItemView::ScrollPerPixel</enum>
</property>
</widget>
</item>
<item row="6" column="0" colspan="2">
<widget class="QWidget" name="customWidget" native="true">
<property name="visible">
<bool>false</bool>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="2">
<widget class="QLabel" name="hLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Resolution of the target device.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Custom height:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QSpinBox" name="widthBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Resolution of the target device.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="maximum">
<number>2160</number>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="wLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Resolution of the target device.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Custom width:</string>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QSpinBox" name="heightBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Resolution of the target device.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="maximum">
<number>3840</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="4" column="0" colspan="2">
<widget class="QWidget" name="optionWidget" native="true"> <widget class="QWidget" name="optionWidget" native="true">
<layout class="QGridLayout" name="gridLayout_2"> <layout class="QGridLayout" name="gridLayout_2">
<property name="leftMargin"> <property name="leftMargin">
@@ -148,13 +37,16 @@
<property name="bottomMargin"> <property name="bottomMargin">
<number>0</number> <number>0</number>
</property> </property>
<item row="0" column="0"> <item row="1" column="1">
<widget class="QCheckBox" name="mangaBox"> <widget class="QCheckBox" name="upscaleBox">
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Enable right-to-left reading.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Unchecked - Nothing&lt;br/&gt;&lt;/span&gt;Images smaller than device resolution will not be resized.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Indeterminate - Stretching&lt;br/&gt;&lt;/span&gt;Images smaller than device resolution will be resized. Aspect ratio will be not preserved.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Checked - Upscaling&lt;br/&gt;&lt;/span&gt;Images smaller than device resolution will be resized. Aspect ratio will be preserved.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="text"> <property name="text">
<string>Manga mode</string> <string>Stretch/Upscale</string>
</property>
<property name="tristate">
<bool>true</bool>
</property> </property>
</widget> </widget>
</item> </item>
@@ -171,16 +63,13 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="0" column="2"> <item row="2" column="1">
<widget class="QCheckBox" name="qualityBox"> <widget class="QCheckBox" name="outputSplit">
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Unchecked - 4 panels&lt;br/&gt;&lt;/span&gt;Zoom each corner separately.&lt;/p&gt;&lt;p style='white-space:pre'&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Indeterminate - 2 panels&lt;br/&gt;&lt;/span&gt;Zoom only the top and bottom of the page.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Checked - 4 high-quality panels&lt;br/&gt;&lt;/span&gt;Zoom each corner separately. Try to increase the quality of magnification. Check wiki for more details.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Unchecked - Automatic mode&lt;br/&gt;&lt;/span&gt;The output will be split automatically.&lt;/p&gt;&lt;p style='white-space:pre'&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Checked - Volume mode&lt;br/&gt;&lt;/span&gt;Every subdirectory will be considered as a separate volume.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="text"> <property name="text">
<string>Panel View 4/2/HQ</string> <string>Output split</string>
</property>
<property name="tristate">
<bool>true</bool>
</property> </property>
</widget> </widget>
</item> </item>
@@ -194,16 +83,13 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="1"> <item row="2" column="2">
<widget class="QCheckBox" name="upscaleBox"> <widget class="QCheckBox" name="colorBox">
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Unchecked - Nothing&lt;br/&gt;&lt;/span&gt;Images smaller than device resolution will not be resized.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Indeterminate - Stretching&lt;br/&gt;&lt;/span&gt;Images smaller than device resolution will be resized. Aspect ratio will be not preserved.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Checked - Upscaling&lt;br/&gt;&lt;/span&gt;Images smaller than device resolution will be resized. Aspect ratio will be preserved.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Disable conversion to grayscale.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="text"> <property name="text">
<string>Stretch/Upscale</string> <string>Color mode</string>
</property>
<property name="tristate">
<bool>true</bool>
</property> </property>
</widget> </widget>
</item> </item>
@@ -230,30 +116,89 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="1"> <item row="0" column="0">
<widget class="QCheckBox" name="outputSplit"> <widget class="QCheckBox" name="mangaBox">
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Unchecked - Automatic mode&lt;br/&gt;&lt;/span&gt;The output will be split automatically.&lt;/p&gt;&lt;p style='white-space:pre'&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Checked - Volume mode&lt;br/&gt;&lt;/span&gt;Every subdirectory will be considered as a separate volume.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Enable right-to-left reading.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="text"> <property name="text">
<string>Output split</string> <string>Manga mode</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="2"> <item row="0" column="2">
<widget class="QCheckBox" name="colorBox"> <widget class="QCheckBox" name="qualityBox">
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Disable conversion to grayscale.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Unchecked - 4 panels&lt;br/&gt;&lt;/span&gt;Zoom each corner separately.&lt;/p&gt;&lt;p style='white-space:pre'&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Indeterminate - 2 panels&lt;br/&gt;&lt;/span&gt;Zoom only the top and bottom of the page.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Checked - 4 high-quality panels&lt;br/&gt;&lt;/span&gt;Zoom each corner separately. Try to increase the quality of magnification. Check wiki for more details.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="text"> <property name="text">
<string>Color mode</string> <string>Panel View 4/2/HQ</string>
</property>
<property name="tristate">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="mozJpegBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Unchecked - JPEG&lt;br/&gt;&lt;/span&gt;Use JPEG files&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Indeterminate - force PNG&lt;br/&gt;&lt;/span&gt;Create PNG files instead JPEG&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Checked - mozJpeg&lt;br/&gt;&lt;/span&gt;10-20% smaller JPEG file, with the same image quality, but processing time multiplied by 2&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>JPEG/PNG/mozJpeg</string>
</property>
<property name="tristate">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QCheckBox" name="maximizeStrips">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Unchecked - 1x4&lt;br/&gt;&lt;/span&gt;Keep format 1x4 panels strips.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Checked - 2x2&lt;br/&gt;&lt;/span&gt;Turn 1x4 strips to 2x2 to maximize screen usage.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>1x4 to 2x2 strips</string>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QCheckBox" name="croppingBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Unchecked - Disabled&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Disabled&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Indeterminate - Margins&lt;br/&gt;&lt;/span&gt;Margins&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Checked - Margins + page numbers&lt;br/&gt;&lt;/span&gt;Margins +page numbers&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Cropping mode</string>
</property>
<property name="tristate">
<bool>true</bool>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QCheckBox" name="deleteBox">
<property name="toolTip">
<string>Delete input file(s) or directory. It's not recoverable!</string>
</property>
<property name="text">
<string>Delete input</string>
</property>
</widget>
</item>
<item row="4" column="2">
<widget class="QCheckBox" name="disableProcessingBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;pre style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Do not process any image, ignore profile and processing options&lt;/pre&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Disable processing</string>
</property> </property>
</widget> </widget>
</item> </item>
</layout> </layout>
</widget> </widget>
</item> </item>
<item row="5" column="0" colspan="2"> <item row="6" column="0" colspan="2">
<widget class="QWidget" name="gammaWidget" native="true"> <widget class="QWidget" name="gammaWidget" native="true">
<property name="visible"> <property name="visible">
<bool>false</bool> <bool>false</bool>
@@ -294,9 +239,12 @@
</layout> </layout>
</widget> </widget>
</item> </item>
<item row="0" column="0" colspan="2"> <item row="8" column="0" colspan="2">
<widget class="QWidget" name="toolWidget" native="true"> <widget class="QWidget" name="croppingWidget" native="true">
<layout class="QHBoxLayout" name="horizontalLayout"> <property name="visible">
<bool>false</bool>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="leftMargin"> <property name="leftMargin">
<number>0</number> <number>0</number>
</property> </property>
@@ -310,39 +258,22 @@
<number>0</number> <number>0</number>
</property> </property>
<item> <item>
<widget class="QPushButton" name="editorButton"> <widget class="QLabel" name="croppingPowerLabel">
<property name="minimumSize">
<size>
<width>0</width>
<height>30</height>
</size>
</property>
<property name="text"> <property name="text">
<string>Editor</string> <string>Cropping power:</string>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Shift+Click to edit directory.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="icon">
<iconset resource="KCC.qrc">
<normaloff>:/Other/icons/editor.png</normaloff>:/Other/icons/editor.png</iconset>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QPushButton" name="wikiButton"> <widget class="QSlider" name="croppingPowerSlider">
<property name="minimumSize"> <property name="maximum">
<size> <number>200</number>
<width>0</width>
<height>30</height>
</size>
</property> </property>
<property name="text"> <property name="singleStep">
<string>Wiki</string> <number>1</number>
</property> </property>
<property name="icon"> <property name="orientation">
<iconset resource="KCC.qrc"> <enum>Qt::Horizontal</enum>
<normaloff>:/Other/icons/wiki.png</normaloff>:/Other/icons/wiki.png</iconset>
</property> </property>
</widget> </widget>
</item> </item>
@@ -421,9 +352,6 @@
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Target device.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Target device.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToMinimumContentsLength</enum>
</property>
</widget> </widget>
</item> </item>
<item row="1" column="3"> <item row="1" column="3">
@@ -437,9 +365,6 @@
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Output format.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Output format.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToMinimumContentsLength</enum>
</property>
</widget> </widget>
</item> </item>
<item row="1" column="2"> <item row="1" column="2">
@@ -452,7 +377,6 @@
</property> </property>
<property name="font"> <property name="font">
<font> <font>
<weight>75</weight>
<bold>true</bold> <bold>true</bold>
</font> </font>
</property> </property>
@@ -494,6 +418,171 @@
<zorder>formatBox</zorder> <zorder>formatBox</zorder>
</widget> </widget>
</item> </item>
<item row="0" column="0" colspan="2">
<widget class="QWidget" name="toolWidget" native="true">
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QPushButton" name="editorButton">
<property name="minimumSize">
<size>
<width>0</width>
<height>30</height>
</size>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Shift+Click to edit directory.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Editor</string>
</property>
<property name="icon">
<iconset resource="KCC.qrc">
<normaloff>:/Other/icons/editor.png</normaloff>:/Other/icons/editor.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="wikiButton">
<property name="minimumSize">
<size>
<width>0</width>
<height>30</height>
</size>
</property>
<property name="text">
<string>Wiki</string>
</property>
<property name="icon">
<iconset resource="KCC.qrc">
<normaloff>:/Other/icons/wiki.png</normaloff>:/Other/icons/wiki.png</iconset>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="2" column="0" colspan="2">
<widget class="QListWidget" name="jobList">
<property name="styleSheet">
<string notr="true">QListWidget#jobList {background:#ffffff;background-image:url(:/Other/icons/list_background.png);background-position:center center;background-repeat:no-repeat;color:rgb(0,0,0);}</string>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::NoSelection</enum>
</property>
<property name="verticalScrollMode">
<enum>QAbstractItemView::ScrollPerPixel</enum>
</property>
<property name="horizontalScrollMode">
<enum>QAbstractItemView::ScrollPerPixel</enum>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QProgressBar" name="progressBar">
<property name="minimumSize">
<size>
<width>0</width>
<height>30</height>
</size>
</property>
<property name="font">
<font>
<bold>true</bold>
</font>
</property>
<property name="visible">
<bool>false</bool>
</property>
<property name="alignment">
<set>Qt::AlignJustify|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="7" column="0" colspan="2">
<widget class="QWidget" name="customWidget" native="true">
<property name="visible">
<bool>false</bool>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="2">
<widget class="QLabel" name="hLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Resolution of the target device.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Custom height:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QSpinBox" name="widthBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Resolution of the target device.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="maximum">
<number>2160</number>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="wLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Resolution of the target device.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Custom width:</string>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QSpinBox" name="heightBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Resolution of the target device.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="maximum">
<number>3840</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout> </layout>
</widget> </widget>
<widget class="QStatusBar" name="statusBar"> <widget class="QStatusBar" name="statusBar">
@@ -518,12 +607,18 @@
<tabstop>borderBox</tabstop> <tabstop>borderBox</tabstop>
<tabstop>outputSplit</tabstop> <tabstop>outputSplit</tabstop>
<tabstop>colorBox</tabstop> <tabstop>colorBox</tabstop>
<tabstop>croppingBox</tabstop>
<tabstop>mozJpegBox</tabstop>
<tabstop>maximizeStrips</tabstop>
<tabstop>deleteBox</tabstop>
<tabstop>disableProcessingBox</tabstop>
<tabstop>editorButton</tabstop> <tabstop>editorButton</tabstop>
<tabstop>wikiButton</tabstop> <tabstop>wikiButton</tabstop>
<tabstop>jobList</tabstop> <tabstop>jobList</tabstop>
<tabstop>gammaSlider</tabstop> <tabstop>gammaSlider</tabstop>
<tabstop>widthBox</tabstop> <tabstop>widthBox</tabstop>
<tabstop>heightBox</tabstop> <tabstop>heightBox</tabstop>
<tabstop>croppingPowerSlider</tabstop>
</tabstops> </tabstops>
<resources> <resources>
<include location="KCC.qrc"/> <include location="KCC.qrc"/>

BIN
icons/KFX.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

39
kcc-c2e.spec Normal file
View File

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

39
kcc-c2p.spec Normal file
View File

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

View File

@@ -1,5 +1,5 @@
#define MyAppName "Kindle Comic Converter" #define MyAppName "Kindle Comic Converter"
#define MyAppVersion "5.5.1" #define MyAppVersion "5.5.2"
#define MyAppPublisher "Ciro Mattia Gonano, Paweł Jastrzębski" #define MyAppPublisher "Ciro Mattia Gonano, Paweł Jastrzębski"
#define MyAppURL "http://kcc.iosphe.re/" #define MyAppURL "http://kcc.iosphe.re/"
#define MyAppExeName "KCC.exe" #define MyAppExeName "KCC.exe"

21
kcc.py
View File

@@ -28,24 +28,27 @@ import os
if sys.platform.startswith('darwin'): if sys.platform.startswith('darwin'):
if getattr(sys, 'frozen', False): if getattr(sys, 'frozen', False):
os.environ['PATH'] = os.path.dirname(os.path.abspath(sys.executable)) + \ os.environ['PATH'] = os.path.dirname(os.path.abspath(sys.executable)) + \
'/../Resources:/usr/local/bin:/usr/bin:/bin' '/../Resources:/Applications/Kindle Comic Creator/Kindle Comic Creator.app/Contents/MacOS:' \
'/Applications/Kindle Previewer 3.app/Contents/lib/fc/bin/:/usr/local/bin:/usr/bin:/bin'
os.chdir(os.path.dirname(os.path.abspath(sys.executable)) + '/../Resources') os.chdir(os.path.dirname(os.path.abspath(sys.executable)) + '/../Resources')
os.system('defaults write com.kindlecomicconverter.KindleComicConverter ApplePersistenceIgnoreState YES')
os.system('defaults write com.kindlecomicconverter.KindleComicConverter NSInitialToolTipDelay -int 1000')
else: else:
os.chdir(os.path.dirname(os.path.abspath(__file__))) os.chdir(os.path.dirname(os.path.abspath(__file__)))
elif sys.platform.startswith('win'): elif sys.platform.startswith('win'):
if getattr(sys, 'frozen', False): if getattr(sys, 'frozen', False):
os.environ['PATH'] = '%LOCALAPPDATA%\\Amazon\\Kindle Previewer 3\\lib\\fc\\bin\\;' + \
os.environ['PATH']
os.chdir(os.path.dirname(os.path.abspath(sys.executable))) os.chdir(os.path.dirname(os.path.abspath(sys.executable)))
else: else:
os.environ['PATH'] = os.path.dirname(os.path.abspath(__file__)) + '/other/windows/;' + os.environ['PATH'] os.environ['PATH'] = os.path.dirname(os.path.abspath(__file__)) + '/other/windows/;' \
'%LOCALAPPDATA%\\Amazon\\Kindle Previewer 3\\lib\\fc\\bin\\;' + \
os.environ['PATH']
os.chdir(os.path.dirname(os.path.abspath(__file__))) os.chdir(os.path.dirname(os.path.abspath(__file__)))
# Load additional Sentry configuration # Load additional Sentry configuration
if getattr(sys, 'frozen', False): # if getattr(sys, 'frozen', False):
try: # try:
import kindlecomicconverter.sentry # import kindlecomicconverter.sentry
except ImportError: # except ImportError:
pass # pass
from multiprocessing import freeze_support, set_start_method from multiprocessing import freeze_support, set_start_method
from kindlecomicconverter.startup import start from kindlecomicconverter.startup import start

39
kcc.spec Normal file
View File

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

View File

@@ -20,13 +20,12 @@
import os import os
import sys import sys
from urllib.parse import unquote from urllib.parse import unquote
from urllib.request import urlopen, urlretrieve, Request from urllib.request import urlretrieve
from time import sleep from time import sleep
from shutil import move, rmtree from shutil import move, rmtree
from subprocess import STDOUT, PIPE from subprocess import STDOUT, PIPE
# noinspection PyUnresolvedReferences # noinspection PyUnresolvedReferences
from PyQt5 import QtGui, QtCore, QtWidgets, QtNetwork from PyQt5 import QtGui, QtCore, QtWidgets, QtNetwork
from xml.dom.minidom import parse
from xml.sax.saxutils import escape from xml.sax.saxutils import escape
from psutil import Popen, Process from psutil import Popen, Process
from copy import copy from copy import copy
@@ -113,6 +112,12 @@ class Icons:
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.Normal, QtGui.QIcon.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.Normal, QtGui.QIcon.Off)
self.KFXFormat = QtGui.QIcon()
self.KFXFormat.addPixmap(QtGui.QPixmap(":/Formats/icons/KFX.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.MOBIEPUBFormat = QtGui.QIcon()
self.MOBIEPUBFormat.addPixmap(QtGui.QPixmap(":/Formats/icons/MOBI.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.EPUB200MBFormat = QtGui.QIcon()
self.EPUB200MBFormat.addPixmap(QtGui.QPixmap(":/Formats/icons/EPUB.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.info = QtGui.QIcon() self.info = QtGui.QIcon()
self.info.addPixmap(QtGui.QPixmap(":/Status/icons/info.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.info.addPixmap(QtGui.QPixmap(":/Status/icons/info.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
@@ -137,26 +142,31 @@ class VersionThread(QtCore.QThread):
self.wait() self.wait()
def run(self): def run(self):
try: # TODO adapt with github releases
XML = parse(urlopen(Request('https://kcc.iosphe.re/Version/', pass
headers={'User-Agent': 'KindleComicConverter/' + __version__})))
except Exception: # try:
return # XML = parse(urlopen(Request('https://kcc.iosphe.re/Version/',
latestVersion = XML.childNodes[0].getElementsByTagName('LatestVersion')[0].childNodes[0].toxml() # headers={'User-Agent': 'KindleComicConverter/' + __version__})))
if StrictVersion(latestVersion) > StrictVersion(__version__): # except Exception:
if sys.platform.startswith('win'): # return
self.newVersion = latestVersion # latestVersion = XML.childNodes[0].getElementsByTagName('LatestVersion')[0].childNodes[0].toxml()
self.md5 = XML.childNodes[0].getElementsByTagName('MD5')[0].childNodes[0].toxml() # if ("beta" not in __version__ and StrictVersion(latestVersion) > StrictVersion(__version__)) \
MW.showDialog.emit('<b>New version released!</b> <a href="https://github.com/ciromattia/kcc/releases/">' # or ("beta" in __version__
'See changelog.</a><br/><br/>Installed version: ' + __version__ + # and StrictVersion(latestVersion) >= StrictVersion(re.sub(r'-beta.*', '', __version__))):
'<br/>Current version: ' + latestVersion + # if sys.platform.startswith('win'):
'<br/><br/>Would you like to start automatic update?', 'question') # self.newVersion = latestVersion
self.getNewVersion() # self.md5 = XML.childNodes[0].getElementsByTagName('MD5')[0].childNodes[0].toxml()
else: # MW.showDialog.emit('<b>New version released!</b> <a href="https://github.com/ciromattia/kcc/releases/">'
MW.addMessage.emit('<a href="https://kcc.iosphe.re/">' # 'See changelog.</a><br/><br/>Installed version: ' + __version__ +
'<b>The new version is available!</b></a> ' # '<br/>Current version: ' + latestVersion +
'(<a href="https://github.com/ciromattia/kcc/releases/">' # '<br/><br/>Would you like to start automatic update?', 'question')
'Changelog</a>)', 'warning', False) # self.getNewVersion()
# else:
# MW.addMessage.emit('<a href="https://kcc.iosphe.re/">'
# '<b>The new version is available!</b></a> '
# '(<a href="https://github.com/ciromattia/kcc/releases/">'
# 'Changelog</a>)', 'warning', False)
def setAnswer(self, dialoganswer): def setAnswer(self, dialoganswer):
self.answer = dialoganswer self.answer = dialoganswer
@@ -175,8 +185,7 @@ class VersionThread(QtCore.QThread):
move(path[0], path[0] + '.exe') move(path[0], path[0] + '.exe')
MW.hideProgressBar.emit() MW.hideProgressBar.emit()
MW.modeConvert.emit(1) MW.modeConvert.emit(1)
Popen(path[0] + '.exe /SP- /silent /noicons', stdout=PIPE, stderr=STDOUT, stdin=PIPE, close_fds=True, Popen(path[0] + '.exe /SP- /silent /noicons', stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
shell=True)
MW.forceShutdown.emit() MW.forceShutdown.emit()
except Exception: except Exception:
MW.addMessage.emit('Failed to download the update!', 'warning', False) MW.addMessage.emit('Failed to download the update!', 'warning', False)
@@ -270,6 +279,9 @@ class WorkerThread(QtCore.QThread):
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()
if GUI.croppingBox.checkState() >= 1:
options.croppingp = float(GUI.croppingPowerValue)
if GUI.borderBox.checkState() == 1: if GUI.borderBox.checkState() == 1:
options.white_borders = True options.white_borders = True
elif GUI.borderBox.checkState() == 2: elif GUI.borderBox.checkState() == 2:
@@ -278,6 +290,16 @@ class WorkerThread(QtCore.QThread):
options.batchsplit = 2 options.batchsplit = 2
if GUI.colorBox.isChecked(): if GUI.colorBox.isChecked():
options.forcecolor = True options.forcecolor = True
if GUI.maximizeStrips.isChecked():
options.maximizestrips = True
if GUI.disableProcessingBox.isChecked():
options.noprocessing = True
if GUI.deleteBox.isChecked():
options.delete = True
if GUI.mozJpegBox.checkState() == 1:
options.forcepng = True
elif GUI.mozJpegBox.checkState() == 2:
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())
@@ -303,8 +325,7 @@ class WorkerThread(QtCore.QThread):
jobargv = list(argv) jobargv = list(argv)
jobargv.append(job) jobargv.append(job)
try: try:
comic2ebook.options = copy(options) comic2ebook.options = comic2ebook.checkOptions(copy(options))
comic2ebook.checkOptions()
outputPath = comic2ebook.makeBook(job, self) outputPath = comic2ebook.makeBook(job, self)
MW.hideProgressBar.emit() MW.hideProgressBar.emit()
except UserWarning as warn: except UserWarning as warn:
@@ -349,7 +370,7 @@ class WorkerThread(QtCore.QThread):
MW.addMessage.emit('Creating CBZ files... <b>Done!</b>', 'info', True) MW.addMessage.emit('Creating CBZ files... <b>Done!</b>', 'info', True)
else: else:
MW.addMessage.emit('Creating EPUB files... <b>Done!</b>', 'info', True) MW.addMessage.emit('Creating EPUB files... <b>Done!</b>', 'info', True)
if str(GUI.formatBox.currentText()) == 'MOBI/AZW3': if str(GUI.formatBox.currentText()) == 'MOBI/AZW3' or str(GUI.formatBox.currentText()) == 'MOBI+EPUB':
MW.progressBarTick.emit('Creating MOBI files') MW.progressBarTick.emit('Creating MOBI files')
MW.progressBarTick.emit(str(len(outputPath) * 2 + 1)) MW.progressBarTick.emit(str(len(outputPath) * 2 + 1))
MW.progressBarTick.emit('tick') MW.progressBarTick.emit('tick')
@@ -483,9 +504,9 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
GUI.jobList.clear() GUI.jobList.clear()
if self.sevenzip: if self.sevenzip:
fnames = QtWidgets.QFileDialog.getOpenFileNames(MW, 'Select file', self.lastPath, fnames = QtWidgets.QFileDialog.getOpenFileNames(MW, 'Select file', self.lastPath,
'Comic (*.cbz *.cbr *.cb7 *.zip *.rar *.7z *.pdf)') 'Comic (*.cbz *.cbr *.cb7 *.zip *.rar *.7z *.pdf);;All (*.*)')
else: else:
fnames = QtWidgets.QFileDialog.getOpenFileNames(MW, 'Select file', self.lastPath, 'Comic (*.pdf)') fnames = QtWidgets.QFileDialog.getOpenFileNames(MW, 'Select file', self.lastPath, 'Comic (*.pdf);;All (*.*)')
for fname in fnames[0]: for fname in fnames[0]:
if fname != '': if fname != '':
if sys.platform.startswith('win'): if sys.platform.startswith('win'):
@@ -594,6 +615,13 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
if self.currentMode != 3: if self.currentMode != 3:
self.modeChange(1) self.modeChange(1)
def togglecroppingBox(self, value):
if value:
GUI.croppingWidget.setVisible(True)
else:
GUI.croppingWidget.setVisible(False)
self.changeCroppingPower(100) # 1.0
def togglewebtoonBox(self, value): def togglewebtoonBox(self, value):
if value: if value:
GUI.qualityBox.setEnabled(False) GUI.qualityBox.setEnabled(False)
@@ -634,6 +662,13 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
GUI.gammaSlider.setValue(valueRaw) GUI.gammaSlider.setValue(valueRaw)
self.gammaValue = value self.gammaValue = value
def changeCroppingPower(self, value):
valueRaw = int(5 * round(float(value) / 5))
value = '%.2f' % (float(valueRaw) / 100)
GUI.croppingPowerLabel.setText('Cropping Power: ' + str(value))
GUI.croppingPowerSlider.setValue(valueRaw)
self.croppingPowerValue = value
def changeDevice(self): def changeDevice(self):
profile = GUI.profiles[str(GUI.deviceBox.currentText())] profile = GUI.profiles[str(GUI.deviceBox.currentText())]
if profile['ForceExpert']: if profile['ForceExpert']:
@@ -753,7 +788,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
if sys.platform.startswith('win'): if sys.platform.startswith('win'):
self.addMessage('Download it and place EXE in KCC directory.', 'error') self.addMessage('Download it and place EXE in KCC directory.', 'error')
elif sys.platform.startswith('darwin'): elif sys.platform.startswith('darwin'):
self.addMessage('Install it using <a href="http://brew.sh/">Brew</a>.', 'error') self.addMessage('Install it using <a href="http://brew.sh/">Homebrew</a>.', 'error')
else: else:
self.addMessage('Download it and place executable in /usr/local/bin directory.', 'error') self.addMessage('Download it and place executable in /usr/local/bin directory.', 'error')
self.needClean = True self.needClean = True
@@ -779,13 +814,19 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
'rotateBox': GUI.rotateBox.checkState(), 'rotateBox': GUI.rotateBox.checkState(),
'qualityBox': GUI.qualityBox.checkState(), 'qualityBox': GUI.qualityBox.checkState(),
'gammaBox': GUI.gammaBox.checkState(), 'gammaBox': GUI.gammaBox.checkState(),
'croppingBox': GUI.croppingBox.checkState(),
'croppingPowerSlider': float(self.croppingPowerValue) * 100,
'upscaleBox': GUI.upscaleBox.checkState(), 'upscaleBox': GUI.upscaleBox.checkState(),
'borderBox': GUI.borderBox.checkState(), 'borderBox': GUI.borderBox.checkState(),
'webtoonBox': GUI.webtoonBox.checkState(), 'webtoonBox': GUI.webtoonBox.checkState(),
'outputSplit': GUI.outputSplit.checkState(), 'outputSplit': GUI.outputSplit.checkState(),
'colorBox': GUI.colorBox.checkState(), 'colorBox': GUI.colorBox.checkState(),
'disableProcessingBox': GUI.disableProcessingBox.checkState(),
'mozJpegBox': GUI.mozJpegBox.checkState(),
'widthBox': GUI.widthBox.value(), 'widthBox': GUI.widthBox.value(),
'heightBox': GUI.heightBox.value(), 'heightBox': GUI.heightBox.value(),
'deleteBox': GUI.deleteBox.checkState(),
'maximizeStrips': GUI.maximizeStrips.checkState(),
'gammaSlider': float(self.gammaValue) * 100}) 'gammaSlider': float(self.gammaValue) * 100})
self.settings.sync() self.settings.sync()
self.tray.hide() self.tray.hide()
@@ -858,7 +899,9 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
if sys.platform.startswith('win'): if sys.platform.startswith('win'):
self.addMessage('Download it and place EXE in KCC directory.', 'error') self.addMessage('Download it and place EXE in KCC directory.', 'error')
elif sys.platform.startswith('darwin'): elif sys.platform.startswith('darwin'):
self.addMessage('Install it using <a href="http://brew.sh/">Brew</a>.', 'error') self.addMessage('Install it using <a href="http://brew.sh/">Homebrew</a>: '
'<i>brew install --cask kindle-comic-creator</i> or '
'<i>brew install --cask kindle-previewer</i>', 'error')
else: else:
self.addMessage('Download it and place executable in /usr/local/bin directory.', 'error') self.addMessage('Download it and place executable in /usr/local/bin directory.', 'error')
@@ -877,7 +920,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
self.currentFormat = self.settings.value('currentFormat', 0, type=int) self.currentFormat = self.settings.value('currentFormat', 0, type=int)
self.startNumber = self.settings.value('startNumber', 0, type=int) self.startNumber = self.settings.value('startNumber', 0, type=int)
self.windowSize = self.settings.value('windowSize', '0x0', type=str) self.windowSize = self.settings.value('windowSize', '0x0', type=str)
self.options = self.settings.value('options', {'gammaSlider': 0}) self.options = self.settings.value('options', {'gammaSlider': 0, 'croppingBox': 2, 'croppingPowerSlider': 100})
self.worker = WorkerThread() self.worker = WorkerThread()
self.versionCheck = VersionThread() self.versionCheck = VersionThread()
self.progress = ProgressThread() self.progress = ProgressThread()
@@ -886,6 +929,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
self.needClean = True self.needClean = True
self.kindleGen = False self.kindleGen = False
self.gammaValue = 1.0 self.gammaValue = 1.0
self.croppingPowerValue = 1.0
self.currentMode = 1 self.currentMode = 1
self.targetDirectory = '' self.targetDirectory = ''
self.sentry = Client(release=__version__) self.sentry = Client(release=__version__)
@@ -910,12 +954,21 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
MW.resize(500, 500) MW.resize(500, 500)
self.profiles = { self.profiles = {
"Kindle Oasis 2": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, "Kindle Oasis 2/3": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': True, 'Label': 'KO'}, 'DefaultUpscale': True, 'Label': 'KO'},
"Kindle Oasis": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, "Kindle Oasis": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': True, 'Label': 'KV'}, 'DefaultUpscale': True, 'Label': 'KV'},
"Kindle Voyage": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, "Kindle Voyage": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': True, 'Label': 'KV'}, 'DefaultUpscale': True, 'Label': 'KV'},
"Kindle Scribe": {
'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': True, 'Label': 'KS',
},
"Kindle 11": {
'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': True, 'Label': 'K11',
},
"Kindle PW 5": {
'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': True, 'Label': 'KPW5',
},
"Kindle PW 3/4": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, "Kindle PW 3/4": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': True, 'Label': 'KV'}, 'DefaultUpscale': True, 'Label': 'KV'},
"Kindle PW 1/2": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, "Kindle PW 1/2": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
@@ -938,10 +991,12 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
'DefaultUpscale': True, 'Label': 'KoAH2O'}, 'DefaultUpscale': True, 'Label': 'KoAH2O'},
"Kobo Aura ONE": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, "Kobo Aura ONE": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1,
'DefaultUpscale': True, 'Label': 'KoAO'}, 'DefaultUpscale': True, 'Label': 'KoAO'},
"Kobo Clara HD": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1,
'DefaultUpscale': True, 'Label': 'KoC'},
"Kobo Libra H2O": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1,
'DefaultUpscale': True, 'Label': 'KoL'},
"Kobo Forma": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, "Kobo Forma": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1,
'DefaultUpscale': True, 'Label': 'KoF'}, 'DefaultUpscale': True, 'Label': 'KoF'},
"Other": {'PVOptions': False, 'ForceExpert': True, 'DefaultFormat': 1,
'DefaultUpscale': False, 'Label': 'OTHER'},
"Kindle 1": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 0, "Kindle 1": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': False, 'Label': 'K1'}, 'DefaultUpscale': False, 'Label': 'K1'},
"Kindle 2": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 0, "Kindle 2": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 0,
@@ -950,31 +1005,53 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
'DefaultUpscale': False, 'Label': 'K34'}, 'DefaultUpscale': False, 'Label': 'K34'},
"Kindle Touch": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 0, "Kindle Touch": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': False, 'Label': 'K34'}, 'DefaultUpscale': False, 'Label': 'K34'},
"Kobo Nia": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, 'DefaultUpscale': True,
'Label': 'KoN'},
"Kobo Clara 2E": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, 'DefaultUpscale': True,
'Label': 'KoC'},
"Kobo Libra 2": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, 'DefaultUpscale': True,
'Label': 'KoL'},
"Kobo Sage": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, 'DefaultUpscale': True,
'Label': 'KoS'},
"Kobo Elipsa": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, 'DefaultUpscale': True,
'Label': 'KoE'},
"Other": {'PVOptions': False, 'ForceExpert': True, 'DefaultFormat': 1, 'DefaultUpscale': False,
'Label': 'OTHER'},
} }
profilesGUI = [ profilesGUI = [
"Kindle Oasis 2", "Kindle Oasis 2/3",
"Kindle Oasis", "Kindle PW 5",
"Kindle Voyage", "Kindle 11",
"Kindle PW 3/4", "Kindle Scribe",
"Kindle PW 1/2",
"Kindle",
"Separator", "Separator",
"Kobo Forma", "Kobo Clara 2E",
"Kobo Aura ONE", "Kobo Sage",
"Kobo Aura H2O", "Kobo Libra 2",
"Kobo Aura HD", "Kobo Elipsa",
"Kobo Aura", "Kobo Nia",
"Separator", "Separator",
"Other", "Other",
"Separator", "Separator",
"Kindle Oasis",
"Kindle Touch", "Kindle Touch",
"Kindle Keyboard", "Kindle Keyboard",
"Kindle DX/DXG", "Kindle DX/DXG",
"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 ONE",
"Kobo Aura H2O",
"Kobo Aura HD",
"Kobo Clara HD",
"Kobo Forma",
"Kobo Glo HD", "Kobo Glo HD",
"Kobo Glo", "Kobo Glo",
"Kobo Libra H2O",
"Kobo Mini/Touch", "Kobo Mini/Touch",
] ]
@@ -998,7 +1075,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
self.sevenzip = True self.sevenzip = True
else: else:
self.sevenzip = False self.sevenzip = False
self.addMessage('Cannot find <a href="http://www.7-zip.org/download.html">7z</a>!' self.addMessage('Add <a href="http://www.7-zip.org/download.html">7z</a> to PATH!'
' Processing of archives will be disabled.', 'warning') ' Processing of archives will be disabled.', 'warning')
self.detectKindleGen(True) self.detectKindleGen(True)
@@ -1011,6 +1088,8 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
GUI.convertButton.clicked.connect(self.convertStart) GUI.convertButton.clicked.connect(self.convertStart)
GUI.gammaSlider.valueChanged.connect(self.changeGamma) GUI.gammaSlider.valueChanged.connect(self.changeGamma)
GUI.gammaBox.stateChanged.connect(self.togglegammaBox) GUI.gammaBox.stateChanged.connect(self.togglegammaBox)
GUI.croppingBox.stateChanged.connect(self.togglecroppingBox)
GUI.croppingPowerSlider.valueChanged.connect(self.changeCroppingPower)
GUI.webtoonBox.stateChanged.connect(self.togglewebtoonBox) GUI.webtoonBox.stateChanged.connect(self.togglewebtoonBox)
GUI.qualityBox.stateChanged.connect(self.togglequalityBox) GUI.qualityBox.stateChanged.connect(self.togglequalityBox)
GUI.deviceBox.activated.connect(self.changeDevice) GUI.deviceBox.activated.connect(self.changeDevice)
@@ -1038,8 +1117,9 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
GUI.deviceBox.addItem(self.icons.deviceKobo, profile) GUI.deviceBox.addItem(self.icons.deviceKobo, profile)
else: else:
GUI.deviceBox.addItem(self.icons.deviceKindle, profile) GUI.deviceBox.addItem(self.icons.deviceKindle, profile)
for f in ['MOBI/AZW3', 'EPUB', 'CBZ']: for f in ['MOBI/AZW3', 'EPUB', 'CBZ', 'KFX', 'MOBI+EPUB', 'EPUB-200MB']:
GUI.formatBox.addItem(eval('self.icons.' + f.replace('/AZW3', '') + 'Format'), f) format_prefix = f.replace('/AZW3', '').replace('+', '').replace('-', '')
GUI.formatBox.addItem(eval('self.icons.' + format_prefix + 'Format'), f)
if self.lastDevice > GUI.deviceBox.count(): if self.lastDevice > GUI.deviceBox.count():
self.lastDevice = 0 self.lastDevice = 0
if profilesGUI[self.lastDevice] == "Separator": if profilesGUI[self.lastDevice] == "Separator":
@@ -1059,6 +1139,10 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
if GUI.gammaSlider.isEnabled(): if GUI.gammaSlider.isEnabled():
GUI.gammaSlider.setValue(int(self.options[option])) GUI.gammaSlider.setValue(int(self.options[option]))
self.changeGamma(int(self.options[option])) self.changeGamma(int(self.options[option]))
elif str(option) == "croppingPowerSlider":
if GUI.croppingPowerSlider.isEnabled():
GUI.croppingPowerSlider.setValue(int(self.options[option]))
self.changeCroppingPower(int(self.options[option]))
else: else:
try: try:
if eval('GUI.' + str(option)).isEnabled(): if eval('GUI.' + str(option)).isEnabled():
@@ -1069,7 +1153,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
self.versionCheck.start() self.versionCheck.start()
self.tray.show() self.tray.show()
# Cleanup unfisnished conversion # Cleanup unfinished conversion
for root, dirs, _ in walkLevel(gettempdir(), 0): for root, dirs, _ in walkLevel(gettempdir(), 0):
for tempdir in dirs: for tempdir in dirs:
if tempdir.startswith('KCC-'): if tempdir.startswith('KCC-'):

File diff suppressed because it is too large Load Diff

View File

@@ -1,13 +1,16 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'gui\KCC.ui' # Form implementation generated from reading ui file 'gui/KCC.ui'
# #
# Created by: PyQt5 UI code generator 5.8.1 # Created by: PyQt5 UI code generator 5.15.7
# #
# WARNING! All changes made in this file will be lost! # 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.
from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_mainWindow(object): class Ui_mainWindow(object):
def setupUi(self, mainWindow): def setupUi(self, mainWindow):
mainWindow.setObjectName("mainWindow") mainWindow.setObjectName("mainWindow")
@@ -20,16 +23,168 @@ class Ui_mainWindow(object):
self.gridLayout = QtWidgets.QGridLayout(self.centralWidget) self.gridLayout = QtWidgets.QGridLayout(self.centralWidget)
self.gridLayout.setContentsMargins(-1, -1, -1, 5) self.gridLayout.setContentsMargins(-1, -1, -1, 5)
self.gridLayout.setObjectName("gridLayout") self.gridLayout.setObjectName("gridLayout")
self.progressBar = QtWidgets.QProgressBar(self.centralWidget) self.optionWidget = QtWidgets.QWidget(self.centralWidget)
self.progressBar.setMinimumSize(QtCore.QSize(0, 30)) self.optionWidget.setObjectName("optionWidget")
self.gridLayout_2 = QtWidgets.QGridLayout(self.optionWidget)
self.gridLayout_2.setContentsMargins(0, 0, 0, 0)
self.gridLayout_2.setObjectName("gridLayout_2")
self.upscaleBox = QtWidgets.QCheckBox(self.optionWidget)
self.upscaleBox.setTristate(True)
self.upscaleBox.setObjectName("upscaleBox")
self.gridLayout_2.addWidget(self.upscaleBox, 1, 1, 1, 1)
self.rotateBox = QtWidgets.QCheckBox(self.optionWidget)
self.rotateBox.setTristate(True)
self.rotateBox.setObjectName("rotateBox")
self.gridLayout_2.addWidget(self.rotateBox, 0, 1, 1, 1)
self.outputSplit = QtWidgets.QCheckBox(self.optionWidget)
self.outputSplit.setObjectName("outputSplit")
self.gridLayout_2.addWidget(self.outputSplit, 2, 1, 1, 1)
self.webtoonBox = QtWidgets.QCheckBox(self.optionWidget)
self.webtoonBox.setObjectName("webtoonBox")
self.gridLayout_2.addWidget(self.webtoonBox, 1, 0, 1, 1)
self.colorBox = QtWidgets.QCheckBox(self.optionWidget)
self.colorBox.setObjectName("colorBox")
self.gridLayout_2.addWidget(self.colorBox, 2, 2, 1, 1)
self.gammaBox = QtWidgets.QCheckBox(self.optionWidget)
self.gammaBox.setObjectName("gammaBox")
self.gridLayout_2.addWidget(self.gammaBox, 1, 2, 1, 1)
self.borderBox = QtWidgets.QCheckBox(self.optionWidget)
self.borderBox.setTristate(True)
self.borderBox.setObjectName("borderBox")
self.gridLayout_2.addWidget(self.borderBox, 2, 0, 1, 1)
self.mangaBox = QtWidgets.QCheckBox(self.optionWidget)
self.mangaBox.setObjectName("mangaBox")
self.gridLayout_2.addWidget(self.mangaBox, 0, 0, 1, 1)
self.qualityBox = QtWidgets.QCheckBox(self.optionWidget)
self.qualityBox.setTristate(True)
self.qualityBox.setObjectName("qualityBox")
self.gridLayout_2.addWidget(self.qualityBox, 0, 2, 1, 1)
self.mozJpegBox = QtWidgets.QCheckBox(self.optionWidget)
self.mozJpegBox.setTristate(True)
self.mozJpegBox.setObjectName("mozJpegBox")
self.gridLayout_2.addWidget(self.mozJpegBox, 3, 0, 1, 1)
self.maximizeStrips = QtWidgets.QCheckBox(self.optionWidget)
self.maximizeStrips.setObjectName("maximizeStrips")
self.gridLayout_2.addWidget(self.maximizeStrips, 3, 1, 1, 1)
self.croppingBox = QtWidgets.QCheckBox(self.optionWidget)
self.croppingBox.setTristate(True)
self.croppingBox.setObjectName("croppingBox")
self.gridLayout_2.addWidget(self.croppingBox, 3, 2, 1, 1)
self.deleteBox = QtWidgets.QCheckBox(self.optionWidget)
self.deleteBox.setObjectName("deleteBox")
self.gridLayout_2.addWidget(self.deleteBox, 4, 1, 1, 1)
self.disableProcessingBox = QtWidgets.QCheckBox(self.optionWidget)
self.disableProcessingBox.setObjectName("disableProcessingBox")
self.gridLayout_2.addWidget(self.disableProcessingBox, 4, 2, 1, 1)
self.gridLayout.addWidget(self.optionWidget, 5, 0, 1, 2)
self.gammaWidget = QtWidgets.QWidget(self.centralWidget)
self.gammaWidget.setVisible(False)
self.gammaWidget.setObjectName("gammaWidget")
self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.gammaWidget)
self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0)
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
self.gammaLabel = QtWidgets.QLabel(self.gammaWidget)
self.gammaLabel.setObjectName("gammaLabel")
self.horizontalLayout_2.addWidget(self.gammaLabel)
self.gammaSlider = QtWidgets.QSlider(self.gammaWidget)
self.gammaSlider.setMaximum(250)
self.gammaSlider.setSingleStep(5)
self.gammaSlider.setOrientation(QtCore.Qt.Horizontal)
self.gammaSlider.setObjectName("gammaSlider")
self.horizontalLayout_2.addWidget(self.gammaSlider)
self.gridLayout.addWidget(self.gammaWidget, 6, 0, 1, 2)
self.croppingWidget = QtWidgets.QWidget(self.centralWidget)
self.croppingWidget.setVisible(False)
self.croppingWidget.setObjectName("croppingWidget")
self.horizontalLayout_3 = QtWidgets.QHBoxLayout(self.croppingWidget)
self.horizontalLayout_3.setContentsMargins(0, 0, 0, 0)
self.horizontalLayout_3.setObjectName("horizontalLayout_3")
self.croppingPowerLabel = QtWidgets.QLabel(self.croppingWidget)
self.croppingPowerLabel.setObjectName("croppingPowerLabel")
self.horizontalLayout_3.addWidget(self.croppingPowerLabel)
self.croppingPowerSlider = QtWidgets.QSlider(self.croppingWidget)
self.croppingPowerSlider.setMaximum(200)
self.croppingPowerSlider.setSingleStep(1)
self.croppingPowerSlider.setOrientation(QtCore.Qt.Horizontal)
self.croppingPowerSlider.setObjectName("croppingPowerSlider")
self.horizontalLayout_3.addWidget(self.croppingPowerSlider)
self.gridLayout.addWidget(self.croppingWidget, 8, 0, 1, 2)
self.buttonWidget = QtWidgets.QWidget(self.centralWidget)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.buttonWidget.sizePolicy().hasHeightForWidth())
self.buttonWidget.setSizePolicy(sizePolicy)
self.buttonWidget.setObjectName("buttonWidget")
self.gridLayout_4 = QtWidgets.QGridLayout(self.buttonWidget)
self.gridLayout_4.setContentsMargins(0, 0, 0, 0)
self.gridLayout_4.setObjectName("gridLayout_4")
self.directoryButton = QtWidgets.QPushButton(self.buttonWidget)
self.directoryButton.setMinimumSize(QtCore.QSize(0, 30))
icon1 = QtGui.QIcon()
icon1.addPixmap(QtGui.QPixmap(":/Other/icons/folder_new.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.directoryButton.setIcon(icon1)
self.directoryButton.setObjectName("directoryButton")
self.gridLayout_4.addWidget(self.directoryButton, 0, 0, 1, 1)
self.fileButton = QtWidgets.QPushButton(self.buttonWidget)
self.fileButton.setMinimumSize(QtCore.QSize(0, 30))
icon2 = QtGui.QIcon()
icon2.addPixmap(QtGui.QPixmap(":/Other/icons/document_new.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.fileButton.setIcon(icon2)
self.fileButton.setObjectName("fileButton")
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.setObjectName("deviceBox")
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.setObjectName("formatBox")
self.gridLayout_4.addWidget(self.formatBox, 1, 3, 1, 1)
self.convertButton = QtWidgets.QPushButton(self.buttonWidget)
self.convertButton.setMinimumSize(QtCore.QSize(0, 30))
font = QtGui.QFont() font = QtGui.QFont()
font.setBold(True) font.setBold(True)
font.setWeight(75) self.convertButton.setFont(font)
self.progressBar.setFont(font) icon3 = QtGui.QIcon()
self.progressBar.setVisible(False) icon3.addPixmap(QtGui.QPixmap(":/Other/icons/convert.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.progressBar.setAlignment(QtCore.Qt.AlignJustify|QtCore.Qt.AlignVCenter) self.convertButton.setIcon(icon3)
self.progressBar.setObjectName("progressBar") self.convertButton.setObjectName("convertButton")
self.gridLayout.addWidget(self.progressBar, 1, 0, 1, 2) self.gridLayout_4.addWidget(self.convertButton, 1, 2, 1, 1)
self.clearButton = QtWidgets.QPushButton(self.buttonWidget)
self.clearButton.setMinimumSize(QtCore.QSize(0, 30))
icon4 = QtGui.QIcon()
icon4.addPixmap(QtGui.QPixmap(":/Other/icons/clear.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.clearButton.setIcon(icon4)
self.clearButton.setObjectName("clearButton")
self.gridLayout_4.addWidget(self.clearButton, 0, 2, 1, 1)
self.directoryButton.raise_()
self.clearButton.raise_()
self.fileButton.raise_()
self.deviceBox.raise_()
self.convertButton.raise_()
self.formatBox.raise_()
self.gridLayout.addWidget(self.buttonWidget, 3, 0, 1, 2)
self.toolWidget = QtWidgets.QWidget(self.centralWidget)
self.toolWidget.setObjectName("toolWidget")
self.horizontalLayout = QtWidgets.QHBoxLayout(self.toolWidget)
self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
self.horizontalLayout.setObjectName("horizontalLayout")
self.editorButton = QtWidgets.QPushButton(self.toolWidget)
self.editorButton.setMinimumSize(QtCore.QSize(0, 30))
icon5 = QtGui.QIcon()
icon5.addPixmap(QtGui.QPixmap(":/Other/icons/editor.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.editorButton.setIcon(icon5)
self.editorButton.setObjectName("editorButton")
self.horizontalLayout.addWidget(self.editorButton)
self.wikiButton = QtWidgets.QPushButton(self.toolWidget)
self.wikiButton.setMinimumSize(QtCore.QSize(0, 30))
icon6 = QtGui.QIcon()
icon6.addPixmap(QtGui.QPixmap(":/Other/icons/wiki.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.wikiButton.setIcon(icon6)
self.wikiButton.setObjectName("wikiButton")
self.horizontalLayout.addWidget(self.wikiButton)
self.gridLayout.addWidget(self.toolWidget, 0, 0, 1, 2)
self.jobList = QtWidgets.QListWidget(self.centralWidget) 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.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.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection) self.jobList.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection)
@@ -37,6 +192,15 @@ class Ui_mainWindow(object):
self.jobList.setHorizontalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel) self.jobList.setHorizontalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel)
self.jobList.setObjectName("jobList") self.jobList.setObjectName("jobList")
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))
font = QtGui.QFont()
font.setBold(True)
self.progressBar.setFont(font)
self.progressBar.setVisible(False)
self.progressBar.setAlignment(QtCore.Qt.AlignJustify|QtCore.Qt.AlignVCenter)
self.progressBar.setObjectName("progressBar")
self.gridLayout.addWidget(self.progressBar, 1, 0, 1, 2)
self.customWidget = QtWidgets.QWidget(self.centralWidget) self.customWidget = QtWidgets.QWidget(self.centralWidget)
self.customWidget.setVisible(False) self.customWidget.setVisible(False)
self.customWidget.setObjectName("customWidget") self.customWidget.setObjectName("customWidget")
@@ -67,139 +231,7 @@ class Ui_mainWindow(object):
self.heightBox.setMaximum(3840) self.heightBox.setMaximum(3840)
self.heightBox.setObjectName("heightBox") 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, 6, 0, 1, 2) self.gridLayout.addWidget(self.customWidget, 7, 0, 1, 2)
self.optionWidget = QtWidgets.QWidget(self.centralWidget)
self.optionWidget.setObjectName("optionWidget")
self.gridLayout_2 = QtWidgets.QGridLayout(self.optionWidget)
self.gridLayout_2.setContentsMargins(0, 0, 0, 0)
self.gridLayout_2.setObjectName("gridLayout_2")
self.mangaBox = QtWidgets.QCheckBox(self.optionWidget)
self.mangaBox.setObjectName("mangaBox")
self.gridLayout_2.addWidget(self.mangaBox, 0, 0, 1, 1)
self.rotateBox = QtWidgets.QCheckBox(self.optionWidget)
self.rotateBox.setTristate(True)
self.rotateBox.setObjectName("rotateBox")
self.gridLayout_2.addWidget(self.rotateBox, 0, 1, 1, 1)
self.qualityBox = QtWidgets.QCheckBox(self.optionWidget)
self.qualityBox.setTristate(True)
self.qualityBox.setObjectName("qualityBox")
self.gridLayout_2.addWidget(self.qualityBox, 0, 2, 1, 1)
self.webtoonBox = QtWidgets.QCheckBox(self.optionWidget)
self.webtoonBox.setObjectName("webtoonBox")
self.gridLayout_2.addWidget(self.webtoonBox, 1, 0, 1, 1)
self.upscaleBox = QtWidgets.QCheckBox(self.optionWidget)
self.upscaleBox.setTristate(True)
self.upscaleBox.setObjectName("upscaleBox")
self.gridLayout_2.addWidget(self.upscaleBox, 1, 1, 1, 1)
self.gammaBox = QtWidgets.QCheckBox(self.optionWidget)
self.gammaBox.setObjectName("gammaBox")
self.gridLayout_2.addWidget(self.gammaBox, 1, 2, 1, 1)
self.borderBox = QtWidgets.QCheckBox(self.optionWidget)
self.borderBox.setTristate(True)
self.borderBox.setObjectName("borderBox")
self.gridLayout_2.addWidget(self.borderBox, 2, 0, 1, 1)
self.outputSplit = QtWidgets.QCheckBox(self.optionWidget)
self.outputSplit.setObjectName("outputSplit")
self.gridLayout_2.addWidget(self.outputSplit, 2, 1, 1, 1)
self.colorBox = QtWidgets.QCheckBox(self.optionWidget)
self.colorBox.setObjectName("colorBox")
self.gridLayout_2.addWidget(self.colorBox, 2, 2, 1, 1)
self.gridLayout.addWidget(self.optionWidget, 4, 0, 1, 2)
self.gammaWidget = QtWidgets.QWidget(self.centralWidget)
self.gammaWidget.setVisible(False)
self.gammaWidget.setObjectName("gammaWidget")
self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.gammaWidget)
self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0)
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
self.gammaLabel = QtWidgets.QLabel(self.gammaWidget)
self.gammaLabel.setObjectName("gammaLabel")
self.horizontalLayout_2.addWidget(self.gammaLabel)
self.gammaSlider = QtWidgets.QSlider(self.gammaWidget)
self.gammaSlider.setMaximum(250)
self.gammaSlider.setSingleStep(5)
self.gammaSlider.setOrientation(QtCore.Qt.Horizontal)
self.gammaSlider.setObjectName("gammaSlider")
self.horizontalLayout_2.addWidget(self.gammaSlider)
self.gridLayout.addWidget(self.gammaWidget, 5, 0, 1, 2)
self.toolWidget = QtWidgets.QWidget(self.centralWidget)
self.toolWidget.setObjectName("toolWidget")
self.horizontalLayout = QtWidgets.QHBoxLayout(self.toolWidget)
self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
self.horizontalLayout.setObjectName("horizontalLayout")
self.editorButton = QtWidgets.QPushButton(self.toolWidget)
self.editorButton.setMinimumSize(QtCore.QSize(0, 30))
icon1 = QtGui.QIcon()
icon1.addPixmap(QtGui.QPixmap(":/Other/icons/editor.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.editorButton.setIcon(icon1)
self.editorButton.setObjectName("editorButton")
self.horizontalLayout.addWidget(self.editorButton)
self.wikiButton = QtWidgets.QPushButton(self.toolWidget)
self.wikiButton.setMinimumSize(QtCore.QSize(0, 30))
icon2 = QtGui.QIcon()
icon2.addPixmap(QtGui.QPixmap(":/Other/icons/wiki.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.wikiButton.setIcon(icon2)
self.wikiButton.setObjectName("wikiButton")
self.horizontalLayout.addWidget(self.wikiButton)
self.gridLayout.addWidget(self.toolWidget, 0, 0, 1, 2)
self.buttonWidget = QtWidgets.QWidget(self.centralWidget)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.buttonWidget.sizePolicy().hasHeightForWidth())
self.buttonWidget.setSizePolicy(sizePolicy)
self.buttonWidget.setObjectName("buttonWidget")
self.gridLayout_4 = QtWidgets.QGridLayout(self.buttonWidget)
self.gridLayout_4.setContentsMargins(0, 0, 0, 0)
self.gridLayout_4.setObjectName("gridLayout_4")
self.directoryButton = QtWidgets.QPushButton(self.buttonWidget)
self.directoryButton.setMinimumSize(QtCore.QSize(0, 30))
icon3 = QtGui.QIcon()
icon3.addPixmap(QtGui.QPixmap(":/Other/icons/folder_new.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.directoryButton.setIcon(icon3)
self.directoryButton.setObjectName("directoryButton")
self.gridLayout_4.addWidget(self.directoryButton, 0, 0, 1, 1)
self.fileButton = QtWidgets.QPushButton(self.buttonWidget)
self.fileButton.setMinimumSize(QtCore.QSize(0, 30))
icon4 = QtGui.QIcon()
icon4.addPixmap(QtGui.QPixmap(":/Other/icons/document_new.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.fileButton.setIcon(icon4)
self.fileButton.setObjectName("fileButton")
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.setSizeAdjustPolicy(QtWidgets.QComboBox.AdjustToMinimumContentsLength)
self.deviceBox.setObjectName("deviceBox")
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.setSizeAdjustPolicy(QtWidgets.QComboBox.AdjustToMinimumContentsLength)
self.formatBox.setObjectName("formatBox")
self.gridLayout_4.addWidget(self.formatBox, 1, 3, 1, 1)
self.convertButton = QtWidgets.QPushButton(self.buttonWidget)
self.convertButton.setMinimumSize(QtCore.QSize(0, 30))
font = QtGui.QFont()
font.setBold(True)
font.setWeight(75)
self.convertButton.setFont(font)
icon5 = QtGui.QIcon()
icon5.addPixmap(QtGui.QPixmap(":/Other/icons/convert.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.convertButton.setIcon(icon5)
self.convertButton.setObjectName("convertButton")
self.gridLayout_4.addWidget(self.convertButton, 1, 2, 1, 1)
self.clearButton = QtWidgets.QPushButton(self.buttonWidget)
self.clearButton.setMinimumSize(QtCore.QSize(0, 30))
icon6 = QtGui.QIcon()
icon6.addPixmap(QtGui.QPixmap(":/Other/icons/clear.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.clearButton.setIcon(icon6)
self.clearButton.setObjectName("clearButton")
self.gridLayout_4.addWidget(self.clearButton, 0, 2, 1, 1)
self.directoryButton.raise_()
self.clearButton.raise_()
self.fileButton.raise_()
self.deviceBox.raise_()
self.convertButton.raise_()
self.formatBox.raise_()
self.gridLayout.addWidget(self.buttonWidget, 3, 0, 1, 2)
mainWindow.setCentralWidget(self.centralWidget) mainWindow.setCentralWidget(self.centralWidget)
self.statusBar = QtWidgets.QStatusBar(mainWindow) self.statusBar = QtWidgets.QStatusBar(mainWindow)
self.statusBar.setSizeGripEnabled(False) self.statusBar.setSizeGripEnabled(False)
@@ -222,52 +254,68 @@ class Ui_mainWindow(object):
mainWindow.setTabOrder(self.gammaBox, self.borderBox) mainWindow.setTabOrder(self.gammaBox, self.borderBox)
mainWindow.setTabOrder(self.borderBox, self.outputSplit) mainWindow.setTabOrder(self.borderBox, self.outputSplit)
mainWindow.setTabOrder(self.outputSplit, self.colorBox) mainWindow.setTabOrder(self.outputSplit, self.colorBox)
mainWindow.setTabOrder(self.colorBox, self.editorButton) 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.editorButton, self.wikiButton)
mainWindow.setTabOrder(self.wikiButton, self.jobList) mainWindow.setTabOrder(self.wikiButton, self.jobList)
mainWindow.setTabOrder(self.jobList, self.gammaSlider) mainWindow.setTabOrder(self.jobList, self.gammaSlider)
mainWindow.setTabOrder(self.gammaSlider, self.widthBox) mainWindow.setTabOrder(self.gammaSlider, self.widthBox)
mainWindow.setTabOrder(self.widthBox, self.heightBox) mainWindow.setTabOrder(self.widthBox, self.heightBox)
mainWindow.setTabOrder(self.heightBox, self.croppingPowerSlider)
def retranslateUi(self, mainWindow): def retranslateUi(self, mainWindow):
_translate = QtCore.QCoreApplication.translate _translate = QtCore.QCoreApplication.translate
mainWindow.setWindowTitle(_translate("mainWindow", "Kindle Comic Converter")) mainWindow.setWindowTitle(_translate("mainWindow", "Kindle Comic Converter"))
self.hLabel.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Resolution of the target device.</p></body></html>"))
self.hLabel.setText(_translate("mainWindow", "Custom height:"))
self.widthBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Resolution of the target device.</p></body></html>"))
self.wLabel.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Resolution of the target device.</p></body></html>"))
self.wLabel.setText(_translate("mainWindow", "Custom width:"))
self.heightBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Resolution of the target device.</p></body></html>"))
self.mangaBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Enable right-to-left reading.</p></body></html>"))
self.mangaBox.setText(_translate("mainWindow", "Manga mode"))
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.rotateBox.setText(_translate("mainWindow", "Spread splitter"))
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.qualityBox.setText(_translate("mainWindow", "Panel View 4/2/HQ"))
self.webtoonBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Enable special parsing mode for Korean Webtoons.</p></body></html>"))
self.webtoonBox.setText(_translate("mainWindow", "Webtoon mode"))
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(_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.setText(_translate("mainWindow", "Stretch/Upscale")) self.upscaleBox.setText(_translate("mainWindow", "Stretch/Upscale"))
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.rotateBox.setText(_translate("mainWindow", "Spread splitter"))
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.outputSplit.setText(_translate("mainWindow", "Output split"))
self.webtoonBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Enable special parsing mode for Korean Webtoons.</p></body></html>"))
self.webtoonBox.setText(_translate("mainWindow", "Webtoon mode"))
self.colorBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Disable conversion to grayscale.</p></body></html>"))
self.colorBox.setText(_translate("mainWindow", "Color mode"))
self.gammaBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Disable automatic gamma correction.</p></body></html>")) self.gammaBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Disable automatic gamma correction.</p></body></html>"))
self.gammaBox.setText(_translate("mainWindow", "Custom gamma")) self.gammaBox.setText(_translate("mainWindow", "Custom gamma"))
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.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.borderBox.setText(_translate("mainWindow", "W/B margins")) self.borderBox.setText(_translate("mainWindow", "W/B margins"))
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.mangaBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Enable right-to-left reading.</p></body></html>"))
self.outputSplit.setText(_translate("mainWindow", "Output split")) self.mangaBox.setText(_translate("mainWindow", "Manga mode"))
self.colorBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Disable conversion to grayscale.</p></body></html>")) 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.setText(_translate("mainWindow", "Color mode")) self.qualityBox.setText(_translate("mainWindow", "Panel View 4/2/HQ"))
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.mozJpegBox.setText(_translate("mainWindow", "JPEG/PNG/mozJpeg"))
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.maximizeStrips.setText(_translate("mainWindow", "1x4 to 2x2 strips"))
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.croppingBox.setText(_translate("mainWindow", "Cropping mode"))
self.deleteBox.setToolTip(_translate("mainWindow", "Delete input file(s) or directory. It\'s not recoverable!"))
self.deleteBox.setText(_translate("mainWindow", "Delete input"))
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.disableProcessingBox.setText(_translate("mainWindow", "Disable processing"))
self.gammaLabel.setText(_translate("mainWindow", "Gamma: Auto")) self.gammaLabel.setText(_translate("mainWindow", "Gamma: Auto"))
self.editorButton.setText(_translate("mainWindow", "Editor")) self.croppingPowerLabel.setText(_translate("mainWindow", "Cropping power:"))
self.editorButton.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Shift+Click to edit directory.</p></body></html>"))
self.wikiButton.setText(_translate("mainWindow", "Wiki"))
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.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.directoryButton.setText(_translate("mainWindow", "Add directory")) self.directoryButton.setText(_translate("mainWindow", "Add directory"))
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.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.fileButton.setText(_translate("mainWindow", "Add file")) self.fileButton.setText(_translate("mainWindow", "Add file"))
self.deviceBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Target device.</p></body></html>")) self.deviceBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Target device.</p></body></html>"))
self.formatBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Output format.</p></body></html>")) self.formatBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Output format.</p></body></html>"))
self.convertButton.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Shift+Click to select the output directory.</p></body></html>")) self.convertButton.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Shift+Click to select the output directory.</p></body></html>"))
self.convertButton.setText(_translate("mainWindow", "Convert")) self.convertButton.setText(_translate("mainWindow", "Convert"))
self.clearButton.setText(_translate("mainWindow", "Clear list")) self.clearButton.setText(_translate("mainWindow", "Clear list"))
self.editorButton.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Shift+Click to edit directory.</p></body></html>"))
self.editorButton.setText(_translate("mainWindow", "Editor"))
self.wikiButton.setText(_translate("mainWindow", "Wiki"))
self.hLabel.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Resolution of the target device.</p></body></html>"))
self.hLabel.setText(_translate("mainWindow", "Custom height:"))
self.widthBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Resolution of the target device.</p></body></html>"))
self.wLabel.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Resolution of the target device.</p></body></html>"))
self.wLabel.setText(_translate("mainWindow", "Custom width:"))
self.heightBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Resolution of the target device.</p></body></html>"))
from . import KCC_rc from . import KCC_rc

View File

@@ -1,13 +1,16 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'gui\MetaEditor.ui' # Form implementation generated from reading ui file 'gui/MetaEditor.ui'
# #
# Created by: PyQt5 UI code generator 5.6 # Created by: PyQt5 UI code generator 5.15.2
# #
# WARNING! All changes made in this file will be lost! # 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.
from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_editorDialog(object): class Ui_editorDialog(object):
def setupUi(self, editorDialog): def setupUi(self, editorDialog):
editorDialog.setObjectName("editorDialog") editorDialog.setObjectName("editorDialog")
@@ -112,5 +115,4 @@ class Ui_editorDialog(object):
self.label_7.setText(_translate("editorDialog", "Colorist:")) self.label_7.setText(_translate("editorDialog", "Colorist:"))
self.okButton.setText(_translate("editorDialog", "Save")) self.okButton.setText(_translate("editorDialog", "Save"))
self.cancelButton.setText(_translate("editorDialog", "Cancel")) self.cancelButton.setText(_translate("editorDialog", "Cancel"))
from . import KCC_rc from . import KCC_rc

View File

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

View File

@@ -27,7 +27,7 @@ from re import sub
from stat import S_IWRITE, S_IREAD, S_IEXEC from stat import S_IWRITE, S_IREAD, S_IEXEC
from zipfile import ZipFile, ZIP_STORED, ZIP_DEFLATED from zipfile import ZipFile, ZIP_STORED, ZIP_DEFLATED
from tempfile import mkdtemp, gettempdir, TemporaryFile from tempfile import mkdtemp, gettempdir, TemporaryFile
from shutil import move, copytree, rmtree from shutil import move, copytree, rmtree, copyfile
from optparse import OptionParser, OptionGroup from optparse import OptionParser, OptionGroup
from multiprocessing import Pool from multiprocessing import Pool
from uuid import uuid4 from uuid import uuid4
@@ -68,7 +68,7 @@ def main(argv=None):
for source in sources: for source in sources:
source = source.rstrip('\\').rstrip('/') source = source.rstrip('\\').rstrip('/')
options = copy(optionstemplate) options = copy(optionstemplate)
checkOptions() options = checkOptions(options)
if len(sources) > 1: if len(sources) > 1:
print('Working on ' + source + '...') print('Working on ' + source + '...')
makeBook(source) makeBook(source)
@@ -79,11 +79,11 @@ def buildHTML(path, imgfile, imgfilepath):
imgfilepath = md5Checksum(imgfilepath) imgfilepath = md5Checksum(imgfilepath)
filename = getImageFileName(imgfile) filename = getImageFileName(imgfile)
deviceres = options.profileData[1] deviceres = options.profileData[1]
if "Rotated" in options.imgMetadata[imgfilepath]: if not options.noprocessing and "Rotated" in options.imgMetadata[imgfilepath]:
rotatedPage = True rotatedPage = True
else: else:
rotatedPage = False rotatedPage = False
if "BlackBackground" in options.imgMetadata[imgfilepath]: if not options.noprocessing and "BlackBackground" in options.imgMetadata[imgfilepath]:
additionalStyle = 'background-color:#000000;' additionalStyle = 'background-color:#000000;'
else: else:
additionalStyle = '' additionalStyle = ''
@@ -302,6 +302,11 @@ def buildOPF(dstdir, title, filelist, cover=None):
else: else:
f.writelines(["<meta name=\"orientation-lock\" content=\"portrait\"/>\n", f.writelines(["<meta name=\"orientation-lock\" content=\"portrait\"/>\n",
"<meta name=\"region-mag\" content=\"true\"/>\n"]) "<meta name=\"region-mag\" content=\"true\"/>\n"])
elif options.supportSyntheticSpread:
f.writelines([
"<meta property=\"rendition:spread\">landscape</meta>\n",
"<meta property=\"rendition:layout\">pre-paginated</meta>\n"
])
else: else:
f.writelines(["<meta property=\"rendition:orientation\">portrait</meta>\n", f.writelines(["<meta property=\"rendition:orientation\">portrait</meta>\n",
"<meta property=\"rendition:spread\">portrait</meta>\n", "<meta property=\"rendition:spread\">portrait</meta>\n",
@@ -334,38 +339,64 @@ def buildOPF(dstdir, title, filelist, cover=None):
f.write("<item id=\"img_" + str(uniqueid) + "\" href=\"" + folder + "/" + path[1] + "\" media-type=\"" + f.write("<item id=\"img_" + str(uniqueid) + "\" href=\"" + folder + "/" + path[1] + "\" media-type=\"" +
mt + "\"/>\n") mt + "\"/>\n")
f.write("<item id=\"css\" href=\"Text/style.css\" media-type=\"text/css\"/>\n") f.write("<item id=\"css\" href=\"Text/style.css\" media-type=\"text/css\"/>\n")
def pageSpreadProperty(pageside):
if options.iskindle:
return "linear=\"yes\" properties=\"page-spread-%s\"" % pageside
elif options.isKobo:
return "properties=\"rendition:page-spread-%s\"" % pageside
else:
return ""
if options.righttoleft: if options.righttoleft:
f.write("</manifest>\n<spine page-progression-direction=\"rtl\" toc=\"ncx\">\n") f.write("</manifest>\n<spine page-progression-direction=\"rtl\" toc=\"ncx\">\n")
pageside = "right" pageside = "right"
else: else:
f.write("</manifest>\n<spine page-progression-direction=\"ltr\" toc=\"ncx\">\n") f.write("</manifest>\n<spine page-progression-direction=\"ltr\" toc=\"ncx\">\n")
pageside = "left" pageside = "left"
if options.iskindle: if options.iskindle or options.supportSyntheticSpread:
for entry in reflist: for entry in reflist:
if options.righttoleft: if options.righttoleft:
if entry.endswith("-b"): if entry.endswith("-b"):
f.write("<itemref idref=\"page_" + entry + "\" linear=\"yes\" properties=\"page-spread-right\"/>\n") f.write(
"<itemref idref=\"page_%s\" %s/>\n" % (entry,
pageSpreadProperty("right"))
)
pageside = "right" pageside = "right"
elif entry.endswith("-c"): elif entry.endswith("-c"):
f.write("<itemref idref=\"page_" + entry + "\" linear=\"yes\" properties=\"page-spread-left\"/>\n") f.write(
"<itemref idref=\"page_%s\" %s/>\n" % (entry,
pageSpreadProperty("left"))
)
pageside = "right" pageside = "right"
else: else:
f.write("<itemref idref=\"page_" + entry + "\" linear=\"yes\" properties=\"page-spread-" + f.write(
pageside + "\"/>\n") "<itemref idref=\"page_%s\" %s/>\n" % (entry,
pageSpreadProperty(pageside))
)
if pageside == "right": if pageside == "right":
pageside = "left" pageside = "left"
else: else:
pageside = "right" pageside = "right"
else: else:
if entry.endswith("-b"): if entry.endswith("-b"):
f.write("<itemref idref=\"page_" + entry + "\" linear=\"yes\" properties=\"page-spread-left\"/>\n") f.write(
"<itemref idref=\"page_%s\" %s/>\n" % (entry,
pageSpreadProperty("left"))
)
pageside = "left" pageside = "left"
elif entry.endswith("-c"): elif entry.endswith("-c"):
f.write("<itemref idref=\"page_" + entry + "\" linear=\"yes\" properties=\"page-spread-right\"/>\n") f.write(
"<itemref idref=\"page_%s\" %s/>\n" % (entry,
pageSpreadProperty("right"))
)
pageside = "left" pageside = "left"
else: else:
f.write("<itemref idref=\"page_" + entry + "\" linear=\"yes\" properties=\"page-spread-" + f.write(
pageside + "\"/>\n") "<itemref idref=\"page_%s\" %s/>\n" % (entry,
pageSpreadProperty(pageside))
)
if pageside == "right": if pageside == "right":
pageside = "left" pageside = "left"
else: else:
@@ -557,9 +588,9 @@ def imgFileProcessing(work):
for i in workImg.payload: for i in workImg.payload:
img = image.ComicPage(opt, *i) img = image.ComicPage(opt, *i)
if opt.cropping == 2 and not opt.webtoon: if opt.cropping == 2 and not opt.webtoon:
img.cropPageNumber(opt.croppingp) img.cropPageNumber(opt.croppingp, opt.croppingm)
if opt.cropping > 0 and not opt.webtoon: if opt.cropping > 0 and not opt.webtoon:
img.cropMargin(opt.croppingp) img.cropMargin(opt.croppingp, opt.croppingm)
img.autocontrastImage() img.autocontrastImage()
img.resizeImage() img.resizeImage()
if opt.forcepng and not opt.forcecolor: if opt.forcepng and not opt.forcecolor:
@@ -789,7 +820,9 @@ def splitProcess(path, mode):
output = [] output = []
currentSize = 0 currentSize = 0
currentTarget = path currentTarget = path
if options.webtoon: if options.targetsize:
targetSize = options.targetsize * 1048576
elif options.webtoon:
targetSize = 104857600 targetSize = 104857600
else: else:
targetSize = 419430400 targetSize = 419430400
@@ -909,8 +942,9 @@ def makeParser():
otherOptions = OptionGroup(psr, "OTHER") otherOptions = OptionGroup(psr, "OTHER")
mainOptions.add_option("-p", "--profile", action="store", dest="profile", default="KV", mainOptions.add_option("-p", "--profile", action="store", dest="profile", default="KV",
help="Device profile (Available options: K1, K2, K34, K578, KDX, KPW, KV, KO, KoMT, KoG," help="Device profile (Available options: K1, K2, K34, K578, KDX, KPW, KPW5, KV, KO, "
" KoGHD, KoA, KoAHD, KoAH2O, KoAO, KoF) [Default=KV]") "K11, KS, KoMT, KoG, KoGHD, KoA, KoAHD, KoAH2O, KoAO, KoN, KoC, KoL, KoF, KoS, KoE)"
" [Default=KV]")
mainOptions.add_option("-m", "--manga-style", action="store_true", dest="righttoleft", default=False, mainOptions.add_option("-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)")
mainOptions.add_option("-q", "--hq", action="store_true", dest="hq", default=False, mainOptions.add_option("-q", "--hq", action="store_true", dest="hq", default=False,
@@ -919,17 +953,23 @@ def makeParser():
help="Display two not four panels in Panel View mode") help="Display two not four panels in Panel View mode")
mainOptions.add_option("-w", "--webtoon", action="store_true", dest="webtoon", default=False, mainOptions.add_option("-w", "--webtoon", action="store_true", dest="webtoon", default=False,
help="Webtoon processing mode"), help="Webtoon processing mode"),
mainOptions.add_option("--targetsize", type="int", dest="targetsize", default=None,
help="the maximal size of output file in MB."
" [Default=100MB for webtoon and 400MB for others]")
outputOptions.add_option("-o", "--output", action="store", dest="output", default=None, outputOptions.add_option("-o", "--output", action="store", dest="output", default=None,
help="Output generated file to specified directory or file") help="Output generated file to specified directory or file")
outputOptions.add_option("-t", "--title", action="store", dest="title", default="defaulttitle", outputOptions.add_option("-t", "--title", action="store", dest="title", default="defaulttitle",
help="Comic title [Default=filename or directory name]") help="Comic title [Default=filename or directory name]")
outputOptions.add_option("-f", "--format", action="store", dest="format", default="Auto", outputOptions.add_option("-f", "--format", action="store", dest="format", default="Auto",
help="Output format (Available options: Auto, MOBI, EPUB, CBZ, KFX) [Default=Auto]") help="Output format (Available options: Auto, MOBI, EPUB, CBZ, KFX, MOBI+EPUB) "
"[Default=Auto]")
outputOptions.add_option("-b", "--batchsplit", type="int", dest="batchsplit", default="0", outputOptions.add_option("-b", "--batchsplit", type="int", dest="batchsplit", default="0",
help="Split output into multiple files. 0: Don't split 1: Automatic mode " help="Split output into multiple files. 0: Don't split 1: Automatic mode "
"2: Consider every subdirectory as separate volume [Default=0]") "2: Consider every subdirectory as separate volume [Default=0]")
processingOptions.add_option("-n", "--noprocessing", action="store_true", dest="noprocessing", default=False,
help="Do not modify image and ignore any profil or processing option")
processingOptions.add_option("-u", "--upscale", action="store_true", dest="upscale", default=False, processingOptions.add_option("-u", "--upscale", action="store_true", dest="upscale", default=False,
help="Resize images smaller than device's resolution") help="Resize images smaller than device's resolution")
processingOptions.add_option("-s", "--stretch", action="store_true", dest="stretch", default=False, processingOptions.add_option("-s", "--stretch", action="store_true", dest="stretch", default=False,
@@ -942,6 +982,8 @@ def makeParser():
help="Set cropping mode. 0: Disabled 1: Margins 2: Margins + page numbers [Default=2]") help="Set cropping mode. 0: Disabled 1: Margins 2: Margins + page numbers [Default=2]")
processingOptions.add_option("--cp", "--croppingpower", type="float", dest="croppingp", default="1.0", processingOptions.add_option("--cp", "--croppingpower", type="float", dest="croppingp", default="1.0",
help="Set cropping power [Default=1.0]") help="Set cropping power [Default=1.0]")
processingOptions.add_option("--cm", "--croppingminimum", type="float", dest="croppingm", default="0.0",
help="Set cropping minimum area ratio [Default=0.0]")
processingOptions.add_option("--blackborders", action="store_true", dest="black_borders", default=False, processingOptions.add_option("--blackborders", action="store_true", dest="black_borders", default=False,
help="Disable autodetection and force black borders") help="Disable autodetection and force black borders")
processingOptions.add_option("--whiteborders", action="store_true", dest="white_borders", default=False, processingOptions.add_option("--whiteborders", action="store_true", dest="white_borders", default=False,
@@ -950,6 +992,12 @@ def makeParser():
help="Don't convert images to grayscale") help="Don't convert images to grayscale")
processingOptions.add_option("--forcepng", action="store_true", dest="forcepng", default=False, processingOptions.add_option("--forcepng", action="store_true", dest="forcepng", default=False,
help="Create PNG files instead JPEG") help="Create PNG files instead JPEG")
processingOptions.add_option("--mozjpeg", action="store_true", dest="mozjpeg", default=False,
help="Create JPEG files using mozJpeg")
processingOptions.add_option("--maximizestrips", action="store_true", dest="maximizestrips", default=False,
help="Turn 1x4 strips to 2x2 strips")
processingOptions.add_option("-d", "--delete", action="store_true", dest="delete", default=False,
help="Delete source file(s) or a directory. It's not recoverable.")
customProfileOptions.add_option("--customwidth", type="int", dest="customwidth", default=0, customProfileOptions.add_option("--customwidth", type="int", dest="customwidth", default=0,
help="Replace screen width provided by device profile") help="Replace screen width provided by device profile")
@@ -967,21 +1015,38 @@ def makeParser():
return psr return psr
def checkOptions(): def checkOptions(options):
global options
options.panelview = True options.panelview = True
options.iskindle = False options.iskindle = False
options.isKobo = False
options.bordersColor = None options.bordersColor = None
options.keep_epub = False
if options.format == 'EPUB-200MB':
options.targetsize = 195
options.format = 'EPUB'
if options.batchsplit != 2:
options.batchsplit = 1
if options.format == 'MOBI+EPUB':
options.keep_epub = True
options.format = 'MOBI'
options.kfx = False options.kfx = False
options.supportSyntheticSpread = False
if options.format == 'Auto': if options.format == 'Auto':
if options.profile in ['K1', 'K2', 'K34', 'K578', 'KPW', 'KV', 'KO']: 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']:
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', 'KV', 'KO']: 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']:
options.isKobo = True
# Other Kobo devices probably support synthetic spreads as well, but
# they haven't been tested.
if options.profile in ['KoF']:
options.supportSyntheticSpread = True
if options.white_borders: if options.white_borders:
options.bordersColor = 'white' options.bordersColor = 'white'
if options.black_borders: if options.black_borders:
@@ -1027,6 +1092,11 @@ def checkOptions():
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
def checkTools(source): def checkTools(source):
@@ -1078,15 +1148,15 @@ def makeBook(source, qtgui=None):
getComicInfo(os.path.join(path, "OEBPS", "Images"), source) getComicInfo(os.path.join(path, "OEBPS", "Images"), source)
detectCorruption(os.path.join(path, "OEBPS", "Images"), source) detectCorruption(os.path.join(path, "OEBPS", "Images"), source)
if options.webtoon: if options.webtoon:
if image.ProfileData.Profiles[options.profile][1][1] > 1024: y = image.ProfileData.Profiles[options.profile][1][1]
y = 1024
else:
y = image.ProfileData.Profiles[options.profile][1][1]
comic2panel.main(['-y ' + str(y), '-i', '-m', path], qtgui) comic2panel.main(['-y ' + str(y), '-i', '-m', path], qtgui)
print("Processing images...") if options.noprocessing:
if GUI: print("Do not process image, ignore any profile or processing option")
GUI.progressBarTick.emit('Processing images') else:
imgDirectoryProcessing(os.path.join(path, "OEBPS", "Images")) print("Processing images...")
if GUI:
GUI.progressBarTick.emit('Processing images')
imgDirectoryProcessing(os.path.join(path, "OEBPS", "Images"))
if GUI: if GUI:
GUI.progressBarTick.emit('1') GUI.progressBarTick.emit('1')
chapterNames = sanitizeTree(os.path.join(path, 'OEBPS', 'Images')) chapterNames = sanitizeTree(os.path.join(path, 'OEBPS', 'Images'))
@@ -1130,7 +1200,12 @@ def makeBook(source, qtgui=None):
else: else:
filepath.append(getOutputFilename(source, options.output, '.epub', '')) filepath.append(getOutputFilename(source, options.output, '.epub', ''))
makeZIP(tome + '_comic', tome, True) makeZIP(tome + '_comic', tome, True)
move(tome + '_comic.zip', filepath[-1]) copyfile(tome + '_comic.zip', filepath[-1])
try:
os.remove(tome + '_comic.zip')
except FileNotFoundError:
# newly temporary created file is not found. It might have been already deleted
pass
rmtree(tome, True) rmtree(tome, True)
if GUI: if GUI:
GUI.progressBarTick.emit('tick') GUI.progressBarTick.emit('tick')
@@ -1157,11 +1232,18 @@ def makeBook(source, qtgui=None):
os.remove(i.replace('.epub', '.mobi') + '_toclean') os.remove(i.replace('.epub', '.mobi') + '_toclean')
if k.path and k.coverSupport: if k.path and k.coverSupport:
options.covers[filepath.index(i)][0].saveToKindle(k, options.covers[filepath.index(i)][1]) options.covers[filepath.index(i)][0].saveToKindle(k, options.covers[filepath.index(i)][1])
if options.delete:
if os.path.isfile(source):
os.remove(source)
elif os.path.isdir(source):
rmtree(source)
return filepath return filepath
def makeMOBIFix(item, uuid): def makeMOBIFix(item, uuid):
os.remove(item) if not options.keep_epub:
os.remove(item)
mobiPath = item.replace('.epub', '.mobi') mobiPath = item.replace('.epub', '.mobi')
move(mobiPath, mobiPath + '_toclean') move(mobiPath, mobiPath + '_toclean')
try: try:
@@ -1201,7 +1283,8 @@ 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.terminate() output.communicate()
break
else: else:
# ERROR: EPUB too big # ERROR: EPUB too big
kindlegenErrorCode = 23026 kindlegenErrorCode = 23026
@@ -1222,7 +1305,7 @@ def makeMOBI(work, qtgui=None):
threadNumber = 1 threadNumber = 1
elif 2 < availableMemory <= 4: elif 2 < availableMemory <= 4:
threadNumber = 2 threadNumber = 2
elif 4 < availableMemory <= 8: elif 4 < availableMemory:
threadNumber = 4 threadNumber = 4
else: else:
threadNumber = None threadNumber = None

View File

@@ -57,9 +57,8 @@ def mergeDirectory(work):
if len(images) > 0: if len(images) > 0:
targetWidth = max(set(sizes), key=sizes.count) targetWidth = max(set(sizes), key=sizes.count)
for i in images: for i in images:
if i[1] <= targetWidth: targetHeight += i[2]
targetHeight += i[2] imagesValid.append(i[0])
imagesValid.append(i[0])
# Silently drop directories that contain too many images # Silently drop directories that contain too many images
# 131072 = GIMP_MAX_IMAGE_SIZE / 4 # 131072 = GIMP_MAX_IMAGE_SIZE / 4
if targetHeight > 131072: if targetHeight > 131072:
@@ -68,8 +67,10 @@ def mergeDirectory(work):
y = 0 y = 0
for i in imagesValid: for i in imagesValid:
img = Image.open(i).convert('RGB') img = Image.open(i).convert('RGB')
if img.size[0] < targetWidth: if img.size[0] < targetWidth or img.size[0] > targetWidth:
img = ImageOps.fit(img, (targetWidth, img.size[1]), method=Image.BICUBIC, centering=(0.5, 0.5)) widthPercent = (targetWidth / float(img.size[0]))
heightSize = int((float(img.size[1]) * float(widthPercent)))
img = ImageOps.fit(img, (targetWidth, heightSize), method=Image.BICUBIC, centering=(0.5, 0.5))
result.paste(img, (0, y)) result.paste(img, (0, y))
y += img.size[1] y += img.size[1]
os.remove(i) os.remove(i)
@@ -100,6 +101,8 @@ def splitImage(work):
name = work[1] name = work[1]
opt = work[2] opt = work[2]
filePath = os.path.join(path, name) filePath = os.path.join(path, name)
Image.warnings.simplefilter('error', Image.DecompressionBombWarning)
Image.MAX_IMAGE_PIXELS = 1000000000
imgOrg = Image.open(filePath).convert('RGB') imgOrg = Image.open(filePath).convert('RGB')
imgProcess = Image.open(filePath).convert('1') imgProcess = Image.open(filePath).convert('1')
widthImg, heightImg = imgOrg.size widthImg, heightImg = imgOrg.size
@@ -113,11 +116,16 @@ def splitImage(work):
panelDetected = False panelDetected = False
panels = [] panels = []
while yWork < heightImg: while yWork < heightImg:
tmpImg = imgProcess.crop([0, yWork, widthImg, yWork + 4]) tmpImg = imgProcess.crop([4, yWork, widthImg-4, yWork + 4])
solid = detectSolid(tmpImg) solid = detectSolid(tmpImg)
if not solid and not panelDetected: if not solid and not panelDetected:
panelDetected = True panelDetected = True
panelY1 = yWork - 2 panelY1 = yWork - 2
if heightImg - yWork <= 5:
if not solid and panelDetected:
panelY2 = heightImg
panelDetected = False
panels.append((panelY1, panelY2, panelY2 - panelY1))
if solid and panelDetected: if solid and panelDetected:
panelDetected = False panelDetected = False
panelY2 = yWork + 6 panelY2 = yWork + 6

View File

@@ -19,6 +19,7 @@
# #
import os import os
import distro
from psutil import Popen from psutil import Popen
from shutil import move from shutil import move
from subprocess import STDOUT, PIPE from subprocess import STDOUT, PIPE
@@ -38,8 +39,17 @@ class ComicArchive:
self.type = line.rstrip().decode().split(' = ')[1].upper() self.type = line.rstrip().decode().split(' = ')[1].upper()
break break
process.communicate() process.communicate()
if process.returncode != 0: if process.returncode != 0 and distro.id() == 'fedora':
raise OSError('Archive is corrupted or encrypted.') process = Popen('unrar l -y -p1 "' + self.filepath + '"', stderr=STDOUT, stdout=PIPE, stdin=PIPE, shell=True)
for line in process.stdout:
if b'Details: ' in line:
self.type = line.rstrip().decode().split(' ')[1].upper()
break
process.communicate()
if process.returncode != 0:
raise OSError('Archive is corrupted or encrypted.')
elif self.type not in ['7Z', 'RAR', 'RAR5', 'ZIP']:
raise OSError('Unsupported archive format.')
elif self.type not in ['7Z', 'RAR', 'RAR5', 'ZIP']: elif self.type not in ['7Z', 'RAR', 'RAR5', 'ZIP']:
raise OSError('Unsupported archive format.') raise OSError('Unsupported archive format.')
@@ -49,8 +59,14 @@ class ComicArchive:
process = Popen('7z x -y -xr!__MACOSX -xr!.DS_Store -xr!thumbs.db -xr!Thumbs.db -o"' + targetdir + '" "' + process = Popen('7z x -y -xr!__MACOSX -xr!.DS_Store -xr!thumbs.db -xr!Thumbs.db -o"' + targetdir + '" "' +
self.filepath + '"', stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True) self.filepath + '"', stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
process.communicate() process.communicate()
if process.returncode != 0: if process.returncode != 0 and distro.id() == 'fedora':
raise OSError('Failed to extract archive.') process = Popen('unrar x -y -x__MACOSX -x.DS_Store -xthumbs.db -xThumbs.db "' + self.filepath + '" "' +
targetdir + '"', stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
process.communicate()
if process.returncode != 0:
raise OSError('Failed to extract archive.')
elif process.returncode != 0:
raise OSError('Failed to extract archive. Check if p7zip-rar is installed.')
tdir = os.listdir(targetdir) tdir = os.listdir(targetdir)
if 'ComicInfo.xml' in tdir: if 'ComicInfo.xml' in tdir:
tdir.remove('ComicInfo.xml') tdir.remove('ComicInfo.xml')

View File

@@ -143,14 +143,12 @@ class DualMobiMetaFix:
self.datain_rec0 = readsection(self.datain, 0) self.datain_rec0 = readsection(self.datain, 0)
# in the first mobi header # in the first mobi header
# add 501 to "EBOK", add 113 as asin, add 504 as asin # add 501 to "EBOK", add 113 as asin
rec0 = self.datain_rec0 rec0 = self.datain_rec0
rec0 = del_exth(rec0, 501) rec0 = del_exth(rec0, 501)
rec0 = del_exth(rec0, 113) rec0 = del_exth(rec0, 113)
rec0 = del_exth(rec0, 504)
rec0 = add_exth(rec0, 501, b'EBOK') rec0 = add_exth(rec0, 501, b'EBOK')
rec0 = add_exth(rec0, 113, asin) rec0 = add_exth(rec0, 113, asin)
rec0 = add_exth(rec0, 504, asin)
replacesection(self.datain, 0, rec0) replacesection(self.datain, 0, rec0)
ver = getint(self.datain_rec0, mobi_version) ver = getint(self.datain_rec0, mobi_version)
@@ -172,14 +170,12 @@ class DualMobiMetaFix:
self.datain_kfrec0 = readsection(self.datain, datain_kf8) self.datain_kfrec0 = readsection(self.datain, datain_kf8)
# in the second header # in the second header
# add 501 to "EBOK", add 113 as asin, add 504 as asin # add 501 to "EBOK", add 113 as asin
rec0 = self.datain_kfrec0 rec0 = self.datain_kfrec0
rec0 = del_exth(rec0, 501) rec0 = del_exth(rec0, 501)
rec0 = del_exth(rec0, 113) rec0 = del_exth(rec0, 113)
rec0 = del_exth(rec0, 504)
rec0 = add_exth(rec0, 501, b'EBOK') rec0 = add_exth(rec0, 501, b'EBOK')
rec0 = add_exth(rec0, 113, asin) rec0 = add_exth(rec0, 113, asin)
rec0 = add_exth(rec0, 504, asin)
replacesection(self.datain, datain_kf8, rec0) replacesection(self.datain, datain_kf8, rec0)
self.datain.flush() self.datain.flush()

View File

@@ -18,8 +18,9 @@
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import io
import os import os
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
@@ -77,13 +78,16 @@ class ProfileData:
Profiles = { Profiles = {
'K1': ("Kindle 1", (600, 670), Palette4, 1.8), 'K1': ("Kindle 1", (600, 670), Palette4, 1.8),
'K11': ("Kindle 11", (1072, 1448), Palette16, 1.8),
'K2': ("Kindle 2", (600, 670), Palette15, 1.8), 'K2': ("Kindle 2", (600, 670), Palette15, 1.8),
'K34': ("Kindle Keyboard/Touch", (600, 800), Palette16, 1.8), 'K34': ("Kindle Keyboard/Touch", (600, 800), Palette16, 1.8),
'K578': ("Kindle", (600, 800), Palette16, 1.8), 'K578': ("Kindle", (600, 800), Palette16, 1.8),
'KDX': ("Kindle DX/DXG", (824, 1000), Palette16, 1.8), 'KDX': ("Kindle DX/DXG", (824, 1000), Palette16, 1.8),
'KPW': ("Kindle Paperwhite 1/2", (758, 1024), Palette16, 1.8), 'KPW': ("Kindle Paperwhite 1/2", (758, 1024), Palette16, 1.8),
'KV': ("Kindle Paperwhite 3/4/Voyage/Oasis", (1072, 1448), Palette16, 1.8), 'KV': ("Kindle Paperwhite 3/4/Voyage/Oasis", (1072, 1448), Palette16, 1.8),
'KO': ("Kindle Oasis 2", (1264, 1680), Palette16, 1.8), 'KPW5': ("Kindle Paperwhite 5/Signature Edition", (1236, 1648), Palette16, 1.8),
'KO': ("Kindle Oasis 2/3", (1264, 1680), Palette16, 1.8),
'KS': ("Kindle Scribe", (1860, 2480), Palette16, 1.8),
'KoMT': ("Kobo Mini/Touch", (600, 800), Palette16, 1.8), 'KoMT': ("Kobo Mini/Touch", (600, 800), Palette16, 1.8),
'KoG': ("Kobo Glo", (768, 1024), Palette16, 1.8), 'KoG': ("Kobo Glo", (768, 1024), Palette16, 1.8),
'KoGHD': ("Kobo Glo HD", (1072, 1448), Palette16, 1.8), 'KoGHD': ("Kobo Glo HD", (1072, 1448), Palette16, 1.8),
@@ -91,7 +95,12 @@ class ProfileData:
'KoAHD': ("Kobo Aura HD", (1080, 1440), Palette16, 1.8), 'KoAHD': ("Kobo Aura HD", (1080, 1440), Palette16, 1.8),
'KoAH2O': ("Kobo Aura H2O", (1080, 1430), Palette16, 1.8), 'KoAH2O': ("Kobo Aura H2O", (1080, 1430), Palette16, 1.8),
'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),
'KoC': ("Kobo Clara HD/Kobo Clara 2E", (1072, 1448), Palette16, 1.8),
'KoL': ("Kobo Libra H2O/Kobo Libra 2", (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),
'KoE': ("Kobo Elipsa", (1404, 1872), Palette16, 1.8),
'OTHER': ("Other", (0, 0), Palette16, 1.8), 'OTHER': ("Other", (0, 0), Palette16, 1.8),
} }
@@ -106,6 +115,9 @@ class ComicPageParser:
self.image = Image.open(os.path.join(source[0], source[1])).convert('RGB') self.image = Image.open(os.path.join(source[0], source[1])).convert('RGB')
self.color = self.colorCheck() self.color = self.colorCheck()
self.fill = self.fillCheck() self.fill = self.fillCheck()
# backwards compatibility for Pillow >9.1.0
if not hasattr(Image, 'Resampling'):
Image.Resampling = Image
self.splitCheck() self.splitCheck()
def getImageHistogram(self, image): def getImageHistogram(self, image):
@@ -120,9 +132,22 @@ class ComicPageParser:
def splitCheck(self): def splitCheck(self):
width, height = self.image.size width, height = self.image.size
dstwidth, dstheight = self.size dstwidth, dstheight = self.size
if (width > height) != (dstwidth > dstheight) and width <= dstheight and height <= dstwidth \ if self.opt.maximizestrips:
leftbox = (0, 0, int(width / 2), height)
rightbox = (int(width / 2), 0, width, height)
if self.opt.righttoleft:
pageone = self.image.crop(rightbox)
pagetwo = self.image.crop(leftbox)
else:
pageone = self.image.crop(leftbox)
pagetwo = self.image.crop(rightbox)
new_image = Image.new("RGB", (int(width / 2), int(height*2)))
new_image.paste(pageone, (0, 0))
new_image.paste(pagetwo, (0, height))
self.payload.append(['N', self.source, new_image, self.color, self.fill])
elif (width > height) != (dstwidth > dstheight) and width <= dstheight and height <= dstwidth \
and not self.opt.webtoon and self.opt.splitter == 1: and not self.opt.webtoon and self.opt.splitter == 1:
self.payload.append(['R', self.source, self.image.rotate(90, Image.BICUBIC, True), self.color, self.fill]) self.payload.append(['R', self.source, self.image.rotate(90, Image.Resampling.BICUBIC, True), self.color, self.fill])
elif (width > height) != (dstwidth > dstheight) and not self.opt.webtoon: elif (width > height) != (dstwidth > dstheight) and not self.opt.webtoon:
if self.opt.splitter != 1: if self.opt.splitter != 1:
if width > height: if width > height:
@@ -140,7 +165,7 @@ class ComicPageParser:
self.payload.append(['S1', self.source, pageone, self.color, self.fill]) self.payload.append(['S1', self.source, pageone, self.color, self.fill])
self.payload.append(['S2', self.source, pagetwo, self.color, self.fill]) self.payload.append(['S2', self.source, pagetwo, self.color, self.fill])
if self.opt.splitter > 0: if self.opt.splitter > 0:
self.payload.append(['R', self.source, self.image.rotate(90, Image.BICUBIC, True), self.payload.append(['R', self.source, self.image.rotate(90, Image.Resampling.BICUBIC, True),
self.color, self.fill]) self.color, self.fill])
else: else:
self.payload.append(['N', self.source, self.image, self.color, self.fill]) self.payload.append(['N', self.source, self.image, self.color, self.fill])
@@ -226,6 +251,9 @@ class ComicPage:
self.targetPath = os.path.join(path[0], os.path.splitext(path[1])[0]) + '-KCC-B' self.targetPath = os.path.join(path[0], os.path.splitext(path[1])[0]) + '-KCC-B'
elif 'S2' in mode: elif 'S2' in mode:
self.targetPath = os.path.join(path[0], os.path.splitext(path[1])[0]) + '-KCC-C' self.targetPath = os.path.join(path[0], os.path.splitext(path[1])[0]) + '-KCC-C'
# backwards compatibility for Pillow >9.1.0
if not hasattr(Image, 'Resampling'):
Image.Resampling = Image
def saveToDir(self): def saveToDir(self):
try: try:
@@ -237,11 +265,20 @@ class ComicPage:
if self.fill != 'white': if self.fill != 'white':
flags.append('BlackBackground') flags.append('BlackBackground')
if self.opt.forcepng: if self.opt.forcepng:
self.image.info["transparency"] = None
self.targetPath += '.png' self.targetPath += '.png'
self.image.save(self.targetPath, 'PNG', optimize=1) self.image.save(self.targetPath, 'PNG', optimize=1)
else: else:
self.targetPath += '.jpg' self.targetPath += '.jpg'
self.image.save(self.targetPath, 'JPEG', optimize=1, quality=85) if self.opt.mozjpeg:
with io.BytesIO() as output:
self.image.save(output, format="JPEG", optimize=1, quality=85)
input_jpeg_bytes = output.getvalue()
output_jpeg_bytes = mozjpeg_lossless_optimization.optimize(input_jpeg_bytes)
with open(self.targetPath, "wb") as output_jpeg_file:
output_jpeg_file.write(output_jpeg_bytes)
else:
self.image.save(self.targetPath, 'JPEG', optimize=1, quality=85)
return [md5Checksum(self.targetPath), flags, self.orgPath] return [md5Checksum(self.targetPath), flags, self.orgPath]
except IOError as err: except IOError as err:
raise RuntimeError('Cannot save image. ' + str(err)) raise RuntimeError('Cannot save image. ' + str(err))
@@ -255,7 +292,7 @@ class ComicPage:
if gamma == 1.0: if gamma == 1.0:
self.image = ImageOps.autocontrast(self.image) self.image = ImageOps.autocontrast(self.image)
else: else:
self.image = ImageOps.autocontrast(Image.eval(self.image, lambda a: 255 * (a / 255.) ** gamma)) self.image = ImageOps.autocontrast(Image.eval(self.image, lambda a: int(255 * (a / 255.) ** gamma)))
def quantizeImage(self): def quantizeImage(self):
colors = len(self.palette) // 3 colors = len(self.palette) // 3
@@ -270,10 +307,11 @@ class ComicPage:
def resizeImage(self): def resizeImage(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.BICUBIC method = Image.Resampling.BICUBIC
else: else:
method = Image.LANCZOS method = Image.Resampling.LANCZOS
if self.opt.stretch or (self.opt.kfx and ('-KCC-B' in self.targetPath or '-KCC-C' in self.targetPath)): if self.opt.stretch:
# if self.opt.stretch or (self.opt.kfx and ('-KCC-B' in self.targetPath or '-KCC-C' in self.targetPath)):
self.image = self.image.resize(self.size, method) self.image = self.image.resize(self.size, method)
elif self.image.size[0] <= self.size[0] and self.image.size[1] <= self.size[1] and not self.opt.upscale: elif self.image.size[0] <= self.size[0] and self.image.size[1] <= self.size[1] and not self.opt.upscale:
if self.opt.format == 'CBZ' or self.opt.kfx: if self.opt.format == 'CBZ' or self.opt.kfx:
@@ -281,7 +319,7 @@ class ComicPage:
borderh = int((self.size[1] - self.image.size[1]) / 2) borderh = int((self.size[1] - self.image.size[1]) / 2)
self.image = ImageOps.expand(self.image, border=(borderw, borderh), fill=self.fill) self.image = ImageOps.expand(self.image, border=(borderw, borderh), fill=self.fill)
if self.image.size[0] != self.size[0] or self.image.size[1] != self.size[1]: if self.image.size[0] != self.size[0] or self.image.size[1] != self.size[1]:
self.image = ImageOps.fit(self.image, self.size, method=Image.BICUBIC, centering=(0.5, 0.5)) self.image = ImageOps.fit(self.image, self.size, method=Image.Resampling.BICUBIC, centering=(0.5, 0.5))
else: else:
if self.opt.format == 'CBZ' or self.opt.kfx: if self.opt.format == 'CBZ' or self.opt.kfx:
ratioDev = float(self.size[0]) / float(self.size[1]) ratioDev = float(self.size[0]) / float(self.size[1])
@@ -297,7 +335,7 @@ class ComicPage:
wsize = int((float(self.image.size[0]) * float(hpercent))) wsize = int((float(self.image.size[0]) * float(hpercent)))
self.image = self.image.resize((wsize, self.size[1]), method) self.image = self.image.resize((wsize, self.size[1]), method)
if self.image.size[0] > self.size[0] or self.image.size[1] > self.size[1]: if self.image.size[0] > self.size[0] or self.image.size[1] > self.size[1]:
self.image.thumbnail(self.size, Image.LANCZOS) self.image.thumbnail(self.size, Image.Resampling.LANCZOS)
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]
@@ -313,7 +351,13 @@ class ComicPage:
) )
return bbox return bbox
def cropPageNumber(self, power): def maybeCrop(self, box, minimum):
box_area = (box[2] - box[0]) * (box[3] - box[1])
image_area = self.image.size[0] * self.image.size[1]
if (box_area / image_area) >= minimum:
self.image = self.image.crop(box)
def cropPageNumber(self, power, minimum):
if self.fill != 'white': if self.fill != 'white':
tmptmg = self.image.convert(mode='L') tmptmg = self.image.convert(mode='L')
else: else:
@@ -322,16 +366,18 @@ class ComicPage:
tmptmg = tmptmg.filter(ImageFilter.MinFilter(size=3)) tmptmg = tmptmg.filter(ImageFilter.MinFilter(size=3))
tmptmg = tmptmg.filter(ImageFilter.GaussianBlur(radius=5)) tmptmg = tmptmg.filter(ImageFilter.GaussianBlur(radius=5))
tmptmg = tmptmg.point(lambda x: (x >= 16 * power) and x) tmptmg = tmptmg.point(lambda x: (x >= 16 * power) and x)
self.image = self.image.crop(tmptmg.getbbox()) if tmptmg.getbbox() else self.image if tmptmg.getbbox():
self.maybeCrop(tmptmg.getbbox(), minimum)
def cropMargin(self, power): def cropMargin(self, power, minimum):
if self.fill != 'white': if self.fill != 'white':
tmptmg = self.image.convert(mode='L') tmptmg = self.image.convert(mode='L')
else: else:
tmptmg = ImageOps.invert(self.image.convert(mode='L')) tmptmg = ImageOps.invert(self.image.convert(mode='L'))
tmptmg = tmptmg.filter(ImageFilter.GaussianBlur(radius=3)) tmptmg = tmptmg.filter(ImageFilter.GaussianBlur(radius=3))
tmptmg = tmptmg.point(lambda x: (x >= 16 * power) and x) tmptmg = tmptmg.point(lambda x: (x >= 16 * power) and x)
self.image = self.image.crop(self.getBoundingBox(tmptmg)) if tmptmg.getbbox() else self.image if tmptmg.getbbox():
self.maybeCrop(self.getBoundingBox(tmptmg), minimum)
class Cover: class Cover:
@@ -344,6 +390,9 @@ class Cover:
else: else:
self.tomeid = tomeid self.tomeid = tomeid
self.image = Image.open(source) self.image = Image.open(source)
# backwards compatibility for Pillow >9.1.0
if not hasattr(Image, 'Resampling'):
Image.Resampling = Image
self.process() self.process()
def process(self): def process(self):
@@ -351,17 +400,17 @@ class Cover:
self.image = ImageOps.autocontrast(self.image) self.image = ImageOps.autocontrast(self.image)
if not self.options.forcecolor: if not self.options.forcecolor:
self.image = self.image.convert('L') self.image = self.image.convert('L')
self.image.thumbnail(self.options.profileData[1], Image.LANCZOS) self.image.thumbnail(self.options.profileData[1], Image.Resampling.LANCZOS)
self.save() self.save()
def save(self): def save(self):
try: try:
self.image.save(self.target, "JPEG", optimize=1, quality=85) self.image.save(self.target, "JPEG", optimize=1, quality=85)
except IOError: except IOError:
raise RuntimeError('Failed to process downloaded cover.') raise RuntimeError('Failed to save cover.')
def saveToKindle(self, kindle, asin): def saveToKindle(self, kindle, asin):
self.image = self.image.resize((300, 470), Image.ANTIALIAS) self.image = self.image.resize((300, 470), Image.Resampling.LANCZOS)
try: try:
self.image.save(os.path.join(kindle.path.split('documents')[0], 'system', 'thumbnails', self.image.save(os.path.join(kindle.path.split('documents')[0], 'system', 'thumbnails',
'thumbnail_' + asin + '_EBOK_portrait.jpg'), 'JPEG', optimize=1, quality=85) 'thumbnail_' + asin + '_EBOK_portrait.jpg'), 'JPEG', optimize=1, quality=85)

View File

@@ -47,7 +47,7 @@ class HTMLStripper(HTMLParser):
def getImageFileName(imgfile): def getImageFileName(imgfile):
name, ext = os.path.splitext(imgfile) name, ext = os.path.splitext(imgfile)
ext = ext.lower() ext = ext.lower()
if name.startswith('.') or ext not in ['.png', '.jpg', '.jpeg', '.gif', '.webp']: if (name.startswith('.') and len(name) == 1) or ext not in ['.png', '.jpg', '.jpeg', '.gif', '.webp']:
return None return None
return [name, ext] return [name, ext]
@@ -117,7 +117,10 @@ def dependencyCheck(level):
except ImportError: except ImportError:
missing.append('psutil 5.0.0+') missing.append('psutil 5.0.0+')
try: try:
from types import ModuleType
from slugify import __version__ as slugifyVersion from slugify import __version__ as slugifyVersion
if isinstance(slugifyVersion, ModuleType):
slugifyVersion = slugifyVersion.__version__
if StrictVersion('1.2.1') > StrictVersion(slugifyVersion): if StrictVersion('1.2.1') > StrictVersion(slugifyVersion):
missing.append('python-slugify 1.2.1+') missing.append('python-slugify 1.2.1+')
except ImportError: except ImportError:

View File

@@ -30,7 +30,7 @@
<key>CFBundleExecutable</key> <key>CFBundleExecutable</key>
<string>MacOS/Kindle Comic Converter</string> <string>MacOS/Kindle Comic Converter</string>
<key>CFBundleGetInfoString</key> <key>CFBundleGetInfoString</key>
<string>KindleComicConverter 5.5.1, written 2012-2019 by Ciro Mattia Gonano and Pawel Jastrzebski</string> <string>KindleComicConverter 5.5.2, written 2012-2019 by Ciro Mattia Gonano and Pawel Jastrzebski</string>
<key>CFBundleIconFile</key> <key>CFBundleIconFile</key>
<string>comic2ebook.icns</string> <string>comic2ebook.icns</string>
<key>CFBundleIdentifier</key> <key>CFBundleIdentifier</key>
@@ -42,20 +42,20 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>5.5.1</string> <string>5.5.2</string>
<key>CFBundleSignature</key> <key>CFBundleSignature</key>
<string>????</string> <string>????</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>5.5.1</string> <string>5.5.2</string>
<key>LSEnvironment</key> <key>LSEnvironment</key>
<dict> <dict>
<key>PATH</key> <key>PATH</key>
<string>./../Resources:/usr/local/bin:/usr/bin:/bin</string> <string>./../Resources:/Applications/Kindle Comic Creator/Kindle Comic Creator.app/Contents/MacOS:/usr/local/bin:/usr/bin:/bin</string>
</dict> </dict>
<key>LSHasLocalizedDisplayName</key> <key>LSHasLocalizedDisplayName</key>
<false/> <false/>
<key>LSMinimumSystemVersion</key> <key>LSMinimumSystemVersion</key>
<string>10.12.0</string> <string>10.14.0</string>
<key>NSAppleScriptEnabled</key> <key>NSAppleScriptEnabled</key>
<false/> <false/>
<key>NSHumanReadableCopyright</key> <key>NSHumanReadableCopyright</key>
@@ -64,5 +64,9 @@
<string>MainMenu</string> <string>MainMenu</string>
<key>NSPrincipalClass</key> <key>NSPrincipalClass</key>
<string>NSApplication</string> <string>NSApplication</string>
<key>NSRequiresAquaSystemAppearance</key>
<string>false</string>
<key>NSInitialToolTipDelay</key>
<integer>1000</integer>
</dict> </dict>
</plist> </plist>

Binary file not shown.

View File

@@ -1,3 +0,0 @@
éd¼7¶ÍÑ<>ßñ|l“z6¯n¸I_œ—Åž:£ê-ˆ!ënBCDÇ}fðzIe|¦ÜÖà\9KW°f½H[íY¶LYô7^Ï@mª*<2A>YmÎ_z`3©WSD{Ö"áˆa@>®;}\¥—,Dš˜Ý·Ý!yX<79>±è<C3A8>¯~ÿ~Y_æxdÕï BøŠ T
B”𢯣
uô|91u~¿Pa¸¸LDÜjh

View File

@@ -1,5 +1,8 @@
PyQt5>=5.6.0 PyQt5>=5.6.0
Pillow>=5.2.0 Pillow>=5.2.0
psutil>=5.0.0 psutil>=5.0.0
python-slugify>=1.2.1,<3.0.0 python-slugify>=1.2.1
raven>=6.0.0 raven>=6.0.0
# PyQt5-tools
mozjpeg-lossless-optimization>=1.1.2
distro

20
setup.py Executable file → Normal file
View File

@@ -17,11 +17,14 @@ import setuptools
import distutils.cmd import distutils.cmd
from kindlecomicconverter import __version__ from kindlecomicconverter import __version__
OSX_INFO_PLIST = "other/osx/Info.plist"
NAME = 'KindleComicConverter' NAME = 'KindleComicConverter'
MAIN = 'kcc.py' MAIN = 'kcc.py'
VERSION = __version__ VERSION = __version__
# noinspection PyUnresolvedReferences
class BuildBinaryCommand(distutils.cmd.Command): class BuildBinaryCommand(distutils.cmd.Command):
description = 'build binary release' description = 'build binary release'
user_options = [] user_options = []
@@ -36,6 +39,13 @@ class BuildBinaryCommand(distutils.cmd.Command):
def run(self): def run(self):
VERSION = __version__ VERSION = __version__
if sys.platform == 'darwin': if sys.platform == 'darwin':
with open(OSX_INFO_PLIST, 'r') as file:
filedata = file.read()
filedata = filedata.replace('5.5.2', VERSION)
with open(OSX_INFO_PLIST, 'w') as file:
file.write(filedata)
os.system('pyinstaller -y -F -i icons/comic2ebook.icns -n "Kindle Comic Converter" -w -s kcc.py') os.system('pyinstaller -y -F -i icons/comic2ebook.icns -n "Kindle Comic Converter" -w -s kcc.py')
os.makedirs('dist/Kindle Comic Converter.app/Contents/Resources/Codecs') os.makedirs('dist/Kindle Comic Converter.app/Contents/Resources/Codecs')
shutil.copy('other/osx/7z', 'dist/Kindle Comic Converter.app/Contents/Resources') shutil.copy('other/osx/7z', 'dist/Kindle Comic Converter.app/Contents/Resources')
@@ -45,10 +55,15 @@ class BuildBinaryCommand(distutils.cmd.Command):
shutil.copy('LICENSE.txt', 'dist/Kindle Comic Converter.app/Contents/Resources') shutil.copy('LICENSE.txt', 'dist/Kindle Comic Converter.app/Contents/Resources')
shutil.copy('other/windows/Additional-LICENSE.txt', 'dist/Kindle Comic Converter.app/Contents/Resources') shutil.copy('other/windows/Additional-LICENSE.txt', 'dist/Kindle Comic Converter.app/Contents/Resources')
os.chmod('dist/Kindle Comic Converter.app/Contents/Resources/7z', 0o777) os.chmod('dist/Kindle Comic Converter.app/Contents/Resources/7z', 0o777)
# TODO /usr/bin/codesign --force -s "$MACOS_CERTIFICATE_NAME" --options runtime dist/Applications/Kindle\ Comic\ Converter.app -v
os.system('appdmg kcc.json dist/KindleComicConverter_osx_' + VERSION + '.dmg') os.system('appdmg kcc.json dist/KindleComicConverter_osx_' + VERSION + '.dmg')
exit(0) exit(0)
elif sys.platform == 'win32': elif sys.platform == 'win32':
os.system('pyinstaller -y -F -i icons\\comic2ebook.ico -n KCC -w --noupx kcc.py') os.system('pyinstaller -y -F -i icons\\comic2ebook.ico -n KCC_' + VERSION + ' -w --noupx kcc.py')
exit(0)
elif sys.platform == 'linux':
os.system(
'pyinstaller --hidden-import=queue -y -F -i icons/comic2ebook.ico -n kcc_linux_' + VERSION + ' kcc.py')
exit(0) exit(0)
else: else:
exit(0) exit(0)
@@ -80,8 +95,9 @@ setuptools.setup(
'PyQt5>=5.6.0', 'PyQt5>=5.6.0',
'Pillow>=5.2.0', 'Pillow>=5.2.0',
'psutil>=5.0.0', 'psutil>=5.0.0',
'python-slugify>=1.2.1', 'python-slugify>=1.2.1,<9.0.0',
'raven>=6.0.0', 'raven>=6.0.0',
'mozjpeg-lossless-optimization>=1.1.2',
], ],
classifiers=[], classifiers=[],
zip_safe=False, zip_safe=False,