mirror of
https://github.com/ciromattia/kcc
synced 2026-04-18 15:08:48 +00:00
Compare commits
285 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2fc32bae58 | ||
|
|
db25a0939c | ||
|
|
1bd7506140 | ||
|
|
4fc5cc9dfb | ||
|
|
89289412a3 | ||
|
|
193297c8fc | ||
|
|
367d71e4ad | ||
|
|
cdde0f18cd | ||
|
|
9fe82a9dd4 | ||
|
|
e958edd135 | ||
|
|
6738b70487 | ||
|
|
44094bdb21 | ||
|
|
a9a2f47e30 | ||
|
|
c35dd137ea | ||
|
|
1ea008c8f3 | ||
|
|
cbc1ed5db4 | ||
|
|
6bf662bea3 | ||
|
|
cfae306731 | ||
|
|
f6475f4c61 | ||
|
|
9646518575 | ||
|
|
937dd3984a | ||
|
|
4e6f6ec8e8 | ||
|
|
4b36fdbfa2 | ||
|
|
d8141af4eb | ||
|
|
cf38c4d445 | ||
|
|
283b2da1a0 | ||
|
|
4a9f693574 | ||
|
|
9a48887edc | ||
|
|
0981ec3c6d | ||
|
|
e0e6606736 | ||
|
|
518e67c132 | ||
|
|
6c593dac1f | ||
|
|
9d065676db | ||
|
|
9520e59a29 | ||
|
|
461a7fda88 | ||
|
|
5ccbb770a6 | ||
|
|
49bf80f5d8 | ||
|
|
33cc324381 | ||
|
|
6ba5539813 | ||
|
|
f725196106 | ||
|
|
c8ff88ed0f | ||
|
|
33fed662fe | ||
|
|
bc7cd17916 | ||
|
|
c5e1e18ac2 | ||
|
|
ee31b784cb | ||
|
|
48541404ee | ||
|
|
acbebcfd40 | ||
|
|
dd6273b864 | ||
|
|
e8502f008a | ||
|
|
169a41e7d2 | ||
|
|
57cf669cdd | ||
|
|
0b4f089b8e | ||
|
|
7eb985337c | ||
|
|
d62690e8bf | ||
|
|
3988f2012f | ||
|
|
6cdd9d5909 | ||
|
|
c29a4beac9 | ||
|
|
5f7bdef325 | ||
|
|
54b5d698ee | ||
|
|
a99c63acea | ||
|
|
251df2e7ba | ||
|
|
b1379b7c59 | ||
|
|
983bde1691 | ||
|
|
e79e5a311c | ||
|
|
39c73dbc0f | ||
|
|
915c9389ef | ||
|
|
89d887710e | ||
|
|
2e9bc5381a | ||
|
|
ab1ce158c7 | ||
|
|
6278adfb25 | ||
|
|
6e6c13047e | ||
|
|
b528dab711 | ||
|
|
2ffefee928 | ||
|
|
a5e5407363 | ||
|
|
da1ba64bd2 | ||
|
|
6dcaf9a6d1 | ||
|
|
3090a47f20 | ||
|
|
1e537915d4 | ||
|
|
7273ca25b8 | ||
|
|
68da853e42 | ||
|
|
431862a2e9 | ||
|
|
65062f8984 | ||
|
|
8122fa1e45 | ||
|
|
13fedff77b | ||
|
|
61b1207a3e | ||
|
|
60e9f075b8 | ||
|
|
424118b7cd | ||
|
|
4e45402b8f | ||
|
|
615790c278 | ||
|
|
0d417b8e11 | ||
|
|
6836c20377 | ||
|
|
f75ea6dfe8 | ||
|
|
77afa77d32 | ||
|
|
f73d889b6e | ||
|
|
8e04ccde18 | ||
|
|
cc1e5db0aa | ||
|
|
c5c88095ee | ||
|
|
1318b9c0f2 | ||
|
|
9339abb267 | ||
|
|
154707a412 | ||
|
|
217f571f3d | ||
|
|
531cea88e6 | ||
|
|
a5202458dc | ||
|
|
5902d88d98 | ||
|
|
e7e41715d0 | ||
|
|
62d1c7c488 | ||
|
|
95678adfd6 | ||
|
|
4923dac8f0 | ||
|
|
935727c1db | ||
|
|
b0e38a700a | ||
|
|
23961243b6 | ||
|
|
c98d6179c3 | ||
|
|
37200bdca0 | ||
|
|
0193bcd00a | ||
|
|
0bbe9348a2 | ||
|
|
d16628dc59 | ||
|
|
85c1801417 | ||
|
|
b28ee08d01 | ||
|
|
dba927c351 | ||
|
|
dd5fd621db | ||
|
|
21a167b3ee | ||
|
|
1c9eeee52d | ||
|
|
611ee31526 | ||
|
|
7c4fdf9d1a | ||
|
|
b14f59e77a | ||
|
|
b4ec0b4a74 | ||
|
|
a90b4c82c5 | ||
|
|
390d58bf08 | ||
|
|
e1aa6cd0af | ||
|
|
718fda2f0c | ||
|
|
9d7904f63b | ||
|
|
ec58964c7c | ||
|
|
0b687ebadc | ||
|
|
9bc1f92c8c | ||
|
|
56f6c6962f | ||
|
|
dfd15ab572 | ||
|
|
5588ad9250 | ||
|
|
0c2adb517e | ||
|
|
9ab1cd359c | ||
|
|
41d24e77e1 | ||
|
|
17206ddf8b | ||
|
|
896c05a72f | ||
|
|
f83106f35b | ||
|
|
dfca136a2d | ||
|
|
92ced5f415 | ||
|
|
d18275d525 | ||
|
|
c049adc3a1 | ||
|
|
a3ce26983e | ||
|
|
fe8195cfed | ||
|
|
3a1737e8d0 | ||
|
|
fb0c0231f3 | ||
|
|
2e807e23e1 | ||
|
|
27841abb83 | ||
|
|
b71d056559 | ||
|
|
a822dfa3ae | ||
|
|
70de379987 | ||
|
|
16e275bb1f | ||
|
|
b225de7b97 | ||
|
|
4a89446914 | ||
|
|
64521de577 | ||
|
|
38b14fd734 | ||
|
|
4fa72780a1 | ||
|
|
c979486e28 | ||
|
|
03bd67cf2f | ||
|
|
b44af66484 | ||
|
|
754395f1b3 | ||
|
|
95e73ea1dd | ||
|
|
f466701b5d | ||
|
|
bc6b26862f | ||
|
|
b4d86fed7f | ||
|
|
b5806ece0e | ||
|
|
85a99f0b05 | ||
|
|
4fea796763 | ||
|
|
ca8bdf7e1f | ||
|
|
33d57d5025 | ||
|
|
cceb9cf61f | ||
|
|
9ee375c6e4 | ||
|
|
a50345a26d | ||
|
|
be4b35b705 | ||
|
|
608e79a9fc | ||
|
|
7468512ec8 | ||
|
|
2256df0785 | ||
|
|
378a3caccc | ||
|
|
ab7d629ba9 | ||
|
|
b54299ff76 | ||
|
|
1d3fc9cc92 | ||
|
|
49313ce030 | ||
|
|
c0d17c1803 | ||
|
|
7f883f98ba | ||
|
|
2c19898952 | ||
|
|
12c663bc05 | ||
|
|
d8957dc4a6 | ||
|
|
9b45633279 | ||
|
|
b5a0126051 | ||
|
|
182a292f70 | ||
|
|
7ab96c5573 | ||
|
|
1fbfaeae01 | ||
|
|
2462d11b5e | ||
|
|
44e6ffe4e3 | ||
|
|
9be2a4a492 | ||
|
|
3ea9a486bf | ||
|
|
95f138887e | ||
|
|
a7a01f8269 | ||
|
|
6987c8b1cd | ||
|
|
6e10636356 | ||
|
|
63bd55313f | ||
|
|
324047bebc | ||
|
|
842a729c75 | ||
|
|
27d3eab8d5 | ||
|
|
8035dedae8 | ||
|
|
b39076a9f4 | ||
|
|
237f343e62 | ||
|
|
ab60d67ab7 | ||
|
|
79715c6a06 | ||
|
|
6cd073809e | ||
|
|
9dac000a04 | ||
|
|
674121620f | ||
|
|
f6e2ceae91 | ||
|
|
143f6eb0f9 | ||
|
|
7cac6d4008 | ||
|
|
aa4456bdb1 | ||
|
|
c91be77588 | ||
|
|
65a42c1063 | ||
|
|
6519eb0453 | ||
|
|
9fdbf095d3 | ||
|
|
4ec4c9966c | ||
|
|
5a51c5234e | ||
|
|
b7c6fd30e4 | ||
|
|
515b83637f | ||
|
|
c3dad087d3 | ||
|
|
67e913ed9e | ||
|
|
6ce0f76fe0 | ||
|
|
5f5157c1d4 | ||
|
|
4c13ef0f6c | ||
|
|
50dc7fbffe | ||
|
|
a060498ac7 | ||
|
|
35bba68a72 | ||
|
|
0b056a8fa8 | ||
|
|
a6f9e84251 | ||
|
|
259800e48b | ||
|
|
28e170f1d2 | ||
|
|
7120c76025 | ||
|
|
4891913b5c | ||
|
|
cd83b2899c | ||
|
|
535c2c220b | ||
|
|
409f077c3e | ||
|
|
3ecb2ba877 | ||
|
|
c07a9657ef | ||
|
|
7f719a22ad | ||
|
|
332d3d455e | ||
|
|
2070a977ae | ||
|
|
8f8d0d68a3 | ||
|
|
5a8deb4623 | ||
|
|
a7ea795df5 | ||
|
|
a2ffd259b8 | ||
|
|
93e6b51466 | ||
|
|
7904662f25 | ||
|
|
6792c2d366 | ||
|
|
ef4a91e44d | ||
|
|
a2a405e5f5 | ||
|
|
a63a46a741 | ||
|
|
2591b53a09 | ||
|
|
1c615ffc20 | ||
|
|
6eb05b3a8f | ||
|
|
968b083fb2 | ||
|
|
205907ef1e | ||
|
|
33ef8275c3 | ||
|
|
eec2099515 | ||
|
|
9027265b7c | ||
|
|
34e2af3389 | ||
|
|
5ecddaceab | ||
|
|
fe554c20aa | ||
|
|
6acf1a1802 | ||
|
|
ac81a6be4b | ||
|
|
829a5f25e7 | ||
|
|
a695a4c151 | ||
|
|
7c0b78350d | ||
|
|
aa978ab246 | ||
|
|
7524c50657 | ||
|
|
9d8663a925 | ||
|
|
08ed304f8e | ||
|
|
658d2f3281 | ||
|
|
f44bf5993e | ||
|
|
458c28281f | ||
|
|
968a1afa1d |
13
.dockerignore
Normal file
13
.dockerignore
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
.git
|
||||||
|
.github
|
||||||
|
build
|
||||||
|
dist
|
||||||
|
KindleComicConverter.egg-info
|
||||||
|
.dockerignore
|
||||||
|
.gitignore
|
||||||
|
.travis.yml
|
||||||
|
Dockerfile
|
||||||
|
venv
|
||||||
|
*.md
|
||||||
|
LICENSE.txt
|
||||||
|
MANIFEST.in
|
||||||
31
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
31
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
---
|
||||||
|
name: Bug report
|
||||||
|
about: Create a report to help us improve
|
||||||
|
title: ''
|
||||||
|
labels: ''
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Describe the bug**
|
||||||
|
A clear and concise description of what the bug is.
|
||||||
|
|
||||||
|
**To Reproduce**
|
||||||
|
Steps to reproduce the behavior:
|
||||||
|
1. Go to '...'
|
||||||
|
2. Click on '....'
|
||||||
|
3. Scroll down to '....'
|
||||||
|
4. See error
|
||||||
|
|
||||||
|
**Expected behavior**
|
||||||
|
A clear and concise description of what you expected to happen.
|
||||||
|
|
||||||
|
**Screenshots**
|
||||||
|
Add a screenshot of your KCC settings.
|
||||||
|
|
||||||
|
**Desktop (please complete the following information):**
|
||||||
|
- OS: [e.g. macOS, Linux, Windows 11]
|
||||||
|
- Device [e.g. Kindle Paperwhite 3rd gen, Kobo Libra 2]
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
Add any other context about the problem here.
|
||||||
25
.github/dependabot.yml
vendored
Normal file
25
.github/dependabot.yml
vendored
Normal 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
74
.github/workflows/codeql-analysis.yml
vendored
Normal 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@v4
|
||||||
|
|
||||||
|
# Initializes the CodeQL tools for scanning.
|
||||||
|
- name: Initialize CodeQL
|
||||||
|
uses: github/codeql-action/init@v3
|
||||||
|
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@v3
|
||||||
|
|
||||||
|
# ℹ️ 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@v3
|
||||||
|
with:
|
||||||
|
category: "/language:${{matrix.language}}"
|
||||||
34
.github/workflows/docker-base-publish.yml
vendored
Normal file
34
.github/workflows/docker-base-publish.yml
vendored
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
name: Docker base
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
push:
|
||||||
|
tags: [ 'docker-base-*' ]
|
||||||
|
|
||||||
|
# Don't trigger if it's just a documentation update
|
||||||
|
paths-ignore:
|
||||||
|
- '**.md'
|
||||||
|
- '**.MD'
|
||||||
|
- '**.yml'
|
||||||
|
- 'docs/**'
|
||||||
|
- 'LICENSE'
|
||||||
|
- '.gitattributes'
|
||||||
|
- '.gitignore'
|
||||||
|
- '.dockerignore'
|
||||||
|
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build_and_push:
|
||||||
|
uses: sdr-enthusiasts/common-github-workflows/.github/workflows/build_and_push_image.yml@main
|
||||||
|
with:
|
||||||
|
docker_build_file: ./Dockerfile-base
|
||||||
|
platform_linux_arm32v7_enabled: true
|
||||||
|
platform_linux_arm64v8_enabled: true
|
||||||
|
platform_linux_amd64_enabled: true
|
||||||
|
push_enabled: true
|
||||||
|
build_nohealthcheck: false
|
||||||
|
ghcr_repo_owner: ${{ github.repository_owner }}
|
||||||
|
ghcr_repo: ${{ github.repository }}
|
||||||
|
build_latest: false
|
||||||
|
secrets:
|
||||||
|
ghcr_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
33
.github/workflows/docker-publish.yml
vendored
Normal file
33
.github/workflows/docker-publish.yml
vendored
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
name: Docker
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
push:
|
||||||
|
# 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
75
.github/workflows/package-linux.yml
vendored
Normal 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@v4
|
||||||
|
- name: Set up Python
|
||||||
|
uses: actions/setup-python@v5
|
||||||
|
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 p7zip-rar python3-pip squashfs-tools libfuse2 libxcb-cursor0
|
||||||
|
python -m pip install --upgrade pip setuptools wheel certifi pyinstaller --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@v4
|
||||||
|
with:
|
||||||
|
name: AppImage
|
||||||
|
path: './*.AppImage*'
|
||||||
|
- name: Release
|
||||||
|
uses: softprops/action-gh-release@v2
|
||||||
|
if: startsWith(github.ref, 'refs/tags/')
|
||||||
|
with:
|
||||||
|
prerelease: true
|
||||||
|
generate_release_notes: true
|
||||||
|
files: |
|
||||||
|
CHANGELOG.md
|
||||||
|
LICENSE.txt
|
||||||
|
*.AppImage*
|
||||||
101
.github/workflows/package-macos.yml
vendored
Normal file
101
.github/workflows/package-macos.yml
vendored
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
# 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:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ macos-12, macos-14 ]
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Set up Python
|
||||||
|
uses: actions/setup-python@v5
|
||||||
|
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@v4
|
||||||
|
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@v4
|
||||||
|
with:
|
||||||
|
name: mac-os-build-${{ runner.arch }}
|
||||||
|
path: dist/*.dmg
|
||||||
|
- name: Release
|
||||||
|
uses: softprops/action-gh-release@v2
|
||||||
|
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
|
||||||
57
.github/workflows/package-windows-with-docker.yml
vendored
Normal file
57
.github/workflows/package-windows-with-docker.yml
vendored
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
# 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@v4
|
||||||
|
# - 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-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-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@v4
|
||||||
|
with:
|
||||||
|
name: windows-build
|
||||||
|
path: dist/windows/*.exe
|
||||||
|
- name: Release
|
||||||
|
uses: softprops/action-gh-release@v2
|
||||||
|
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
58
.github/workflows/package-windows.yml
vendored
Normal 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@v4
|
||||||
|
- name: Set up Python
|
||||||
|
uses: actions/setup-python@v5
|
||||||
|
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@v4
|
||||||
|
with:
|
||||||
|
name: windows-build
|
||||||
|
path: dist/*.exe
|
||||||
|
- name: Release
|
||||||
|
uses: softprops/action-gh-release@v2
|
||||||
|
if: startsWith(github.ref, 'refs/tags/')
|
||||||
|
with:
|
||||||
|
prerelease: true
|
||||||
|
generate_release_notes: true
|
||||||
|
files: |
|
||||||
|
CHANGELOG.md
|
||||||
|
LICENSE.txt
|
||||||
|
dist/*.exe
|
||||||
24
.gitignore
vendored
24
.gitignore
vendored
@@ -1,16 +1,14 @@
|
|||||||
*.pyc
|
*.pyc
|
||||||
*.cbz
|
Pipfile
|
||||||
*.cbr
|
Pipfile.lock
|
||||||
.idea
|
setup.bat
|
||||||
.DS_Store
|
|
||||||
Thumbs.db
|
|
||||||
dist
|
|
||||||
Output
|
|
||||||
test
|
|
||||||
solaio
|
|
||||||
kindlegen*
|
|
||||||
*.spec
|
|
||||||
kindlecomicconverter/sentry.py
|
kindlecomicconverter/sentry.py
|
||||||
|
other/windows/kindlegen.exe
|
||||||
|
dist/
|
||||||
build/
|
build/
|
||||||
.python-version
|
KindleComicConverter*.egg-info/
|
||||||
KindleComicConverter.egg-info/
|
.idea/
|
||||||
|
/venv/
|
||||||
|
/kindlegen*
|
||||||
|
/kcc.bat
|
||||||
|
.DS_Store
|
||||||
|
|||||||
38
.travis.yml
38
.travis.yml
@@ -1,31 +1,16 @@
|
|||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- os: linux
|
|
||||||
language: python
|
|
||||||
python: 3.6
|
|
||||||
dist: trusty
|
|
||||||
sudo: required
|
|
||||||
- os: osx
|
- os: osx
|
||||||
language: generic
|
language: generic
|
||||||
osx_image: xcode6.4
|
osx_image: xcode11.1
|
||||||
|
|
||||||
before_install:
|
before_install:
|
||||||
- if [ "$TRAVIS_OS_NAME" == "linux" ] ; then sudo apt-get -y install ruby ruby-dev ; fi
|
- pip3 install --upgrade pip setuptools wheel
|
||||||
- if [ "$TRAVIS_OS_NAME" == "linux" ] ; then pip install --upgrade pip setuptools wheel ; fi
|
|
||||||
- if [ "$TRAVIS_OS_NAME" == "linux" ] ; then openssl aes-256-cbc -K $encrypted_a95564d8ff0d_key -iv $encrypted_a95564d8ff0d_iv -in other/linux/sentry.py.enc -out kindlecomicconverter/sentry.py -d ; fi
|
|
||||||
- if [ "$TRAVIS_OS_NAME" == "osx" ] ; then brew update ; fi
|
|
||||||
- if [ "$TRAVIS_OS_NAME" == "osx" ] ; then brew install python3 ; fi
|
|
||||||
- if [ "$TRAVIS_OS_NAME" == "osx" ] ; then brew upgrade node ; fi
|
|
||||||
- if [ "$TRAVIS_OS_NAME" == "osx" ] ; then pip3 install --upgrade pip setuptools wheel ; fi
|
|
||||||
- if [ "$TRAVIS_OS_NAME" == "osx" ] ; then openssl aes-256-cbc -K $encrypted_a95564d8ff0d_key -iv $encrypted_a95564d8ff0d_iv -in other/osx/sentry.py.enc -out kindlecomicconverter/sentry.py -d ; fi
|
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- if [ "$TRAVIS_OS_NAME" == "linux" ] ; then pip install -r requirements.txt ; fi
|
- pip3 install -r requirements.txt
|
||||||
- if [ "$TRAVIS_OS_NAME" == "linux" ] ; then pip install certifi https://github.com/pyinstaller/pyinstaller/archive/develop.zip ; fi
|
- pip3 install certifi https://github.com/pyinstaller/pyinstaller/archive/develop.zip
|
||||||
- if [ "$TRAVIS_OS_NAME" == "linux" ] ; then gem install fpm ; fi
|
- npm install -g appdmg
|
||||||
- if [ "$TRAVIS_OS_NAME" == "osx" ] ; then pip3 install -r requirements.txt ; fi
|
|
||||||
- if [ "$TRAVIS_OS_NAME" == "osx" ] ; then pip3 install certifi https://github.com/pyinstaller/pyinstaller/archive/develop.zip ; fi
|
|
||||||
- if [ "$TRAVIS_OS_NAME" == "osx" ] ; then npm install -g appdmg ; fi
|
|
||||||
|
|
||||||
script: python3 setup.py build_binary
|
script: python3 setup.py build_binary
|
||||||
|
|
||||||
@@ -34,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
67
AppImageBuilder.yml
Normal 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
|
||||||
90
CHANGELOG.md
90
CHANGELOG.md
@@ -1,4 +1,94 @@
|
|||||||
# CHANGELOG
|
# CHANGELOG
|
||||||
|
|
||||||
|
|
||||||
|
#### 5.6.2:
|
||||||
|
* build pipeline : drop pypi by @darodi in [#465](https://github.com/ciromattia/kcc/pull/465)
|
||||||
|
* supporting Kindle Previewer by @darodi in [#466](https://github.com/ciromattia/kcc/pull/466)
|
||||||
|
* Bump actions/upload-artifact from 2 to 3 by @dependabot in [#468](https://github.com/ciromattia/kcc/pull/468)
|
||||||
|
* new appImage by @darodi in [#483](https://github.com/ciromattia/kcc/pull/483)
|
||||||
|
* Bump actions/checkout from 2 to 3 by @dependabot in [#484](https://github.com/ciromattia/kcc/pull/484)
|
||||||
|
* supporting Kindle Previewer by @darodi in [#486](https://github.com/ciromattia/kcc/pull/486)
|
||||||
|
* comic2ebook/func: Add a delete option (closes #458) by @Constantin1489 in [#485](https://github.com/ciromattia/kcc/pull/485)
|
||||||
|
* Add command line executables to CI/pipelines by @darodi in [#487](https://github.com/ciromattia/kcc/pull/487)
|
||||||
|
* gui/func: Add a 'delete after conversion' button (closes #458) by @Constantin1489 in [#488](https://github.com/ciromattia/kcc/pull/488)
|
||||||
|
* fix crashes on png transparency by @axu2 in [#494](https://github.com/ciromattia/kcc/pull/494)
|
||||||
|
* Update python-slugify requirement from <8.0.0,>=1.2.1 to >=1.2.1,<9.0.0 by @dependabot in [#473](https://github.com/ciromattia/kcc/pull/473)
|
||||||
|
* Updates GUI text for new Homebrew version by @thatrobotdev in [#491](https://github.com/ciromattia/kcc/pull/491)
|
||||||
|
* Even with EPUB-200MB option selected, created file is above 200MB by @darodi in [#503](https://github.com/ciromattia/kcc/pull/503)
|
||||||
|
* limit kindle scribe image size to (1440, 1920) when using kindlegen by @darodi in [#514](https://github.com/ciromattia/kcc/pull/514)
|
||||||
|
* add 7z to PATH by @axu2 in [#513](https://github.com/ciromattia/kcc/pull/513)
|
||||||
|
* use unrar for fedora only by @darodi in [#515](https://github.com/ciromattia/kcc/pull/515)
|
||||||
|
|
||||||
|
|
||||||
|
#### 5.6.1:
|
||||||
|
* 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:
|
||||||
|
* Fixes some stability issues
|
||||||
|
|
||||||
|
#### 5.5.0:
|
||||||
|
* Added support for WebP format
|
||||||
|
* Added profiles for Kindle Paperwhite 4 and Kobo Forma
|
||||||
|
* All archives are now handled by 7z
|
||||||
|
* Removed MCD support
|
||||||
|
* Fixed multiple smaller issues
|
||||||
|
|
||||||
|
#### 5.4.5:
|
||||||
|
* Fixed EPUB output for non-Kindle devices
|
||||||
|
|
||||||
|
#### 5.4.4:
|
||||||
|
* Minor bug fixes
|
||||||
|
|
||||||
|
#### 5.4.3:
|
||||||
|
* Fixed conversion crash on Windows
|
||||||
|
|
||||||
|
#### 5.4.2:
|
||||||
|
* Added Kindle Oasis 2 profile
|
||||||
|
* Allowed metadata editor to edit directories
|
||||||
|
* Fixed image stretching when HQ Panel View option was enabled
|
||||||
|
* Fixed possible problem with directory sort order
|
||||||
|
|
||||||
#### 5.4.1:
|
#### 5.4.1:
|
||||||
* Minor bug fixes and tweaks
|
* Minor bug fixes and tweaks
|
||||||
* Implemented new binary build pipeline
|
* Implemented new binary build pipeline
|
||||||
|
|||||||
19
Dockerfile
Normal file
19
Dockerfile
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# Select final stage based on TARGETARCH ARG
|
||||||
|
FROM ghcr.io/ciromattia/kcc:docker-base-20230809
|
||||||
|
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"
|
||||||
|
|
||||||
|
COPY . /opt/kcc
|
||||||
|
RUN cat /opt/kcc/kindlecomicconverter/__init__.py | grep version | awk '{print $3}' | sed "s/'//g" > /IMAGE_VERSION
|
||||||
|
|
||||||
|
ENTRYPOINT ["/opt/kcc/kcc-c2e.py"]
|
||||||
|
CMD ["-h"]
|
||||||
160
Dockerfile-base
Normal file
160
Dockerfile-base
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
FROM --platform=linux/amd64 python:3.11-slim-bullseye as compile-amd64
|
||||||
|
ARG TARGETOS
|
||||||
|
ARG TARGETARCH
|
||||||
|
ARG TARGETVARIANT
|
||||||
|
RUN echo "I'm building for $TARGETOS/$TARGETARCH/$TARGETVARIANT"
|
||||||
|
|
||||||
|
|
||||||
|
COPY requirements.txt /opt/kcc/
|
||||||
|
ENV PATH="/opt/venv/bin:$PATH"
|
||||||
|
RUN DEBIAN_FRONTEND=noninteractive apt-get update -y && apt-get -yq upgrade && \
|
||||||
|
apt-get install -y libpng-dev libjpeg-dev p7zip-full unrar-free libgl1 && \
|
||||||
|
python -m pip install --upgrade pip && \
|
||||||
|
python -m venv /opt/venv && \
|
||||||
|
python -m pip install -r /opt/kcc/requirements.txt
|
||||||
|
|
||||||
|
|
||||||
|
######################################################################################
|
||||||
|
|
||||||
|
FROM --platform=linux/arm64 python:3.11-slim-bullseye as compile-arm64
|
||||||
|
ARG TARGETOS
|
||||||
|
ARG TARGETARCH
|
||||||
|
ARG TARGETVARIANT
|
||||||
|
RUN echo "I'm building for $TARGETOS/$TARGETARCH/$TARGETVARIANT"
|
||||||
|
|
||||||
|
ENV LC_ALL=C.UTF-8 \
|
||||||
|
LANG=C.UTF-8 \
|
||||||
|
LANGUAGE=en_US:en
|
||||||
|
|
||||||
|
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
|
||||||
|
|
||||||
|
RUN set -x && \
|
||||||
|
TEMP_PACKAGES=() && \
|
||||||
|
KEPT_PACKAGES=() && \
|
||||||
|
# Packages only required during build
|
||||||
|
TEMP_PACKAGES+=(build-essential) && \
|
||||||
|
TEMP_PACKAGES+=(cmake) && \
|
||||||
|
TEMP_PACKAGES+=(libfreetype6-dev) && \
|
||||||
|
TEMP_PACKAGES+=(libfontconfig1-dev) && \
|
||||||
|
TEMP_PACKAGES+=(libpng-dev) && \
|
||||||
|
TEMP_PACKAGES+=(libjpeg-dev) && \
|
||||||
|
TEMP_PACKAGES+=(libssl-dev) && \
|
||||||
|
TEMP_PACKAGES+=(libxft-dev) && \
|
||||||
|
TEMP_PACKAGES+=(make) && \
|
||||||
|
TEMP_PACKAGES+=(python3-dev) && \
|
||||||
|
TEMP_PACKAGES+=(python3-setuptools) && \
|
||||||
|
TEMP_PACKAGES+=(python3-wheel) && \
|
||||||
|
# Packages kept in the image
|
||||||
|
KEPT_PACKAGES+=(bash) && \
|
||||||
|
KEPT_PACKAGES+=(ca-certificates) && \
|
||||||
|
KEPT_PACKAGES+=(chrpath) && \
|
||||||
|
KEPT_PACKAGES+=(locales) && \
|
||||||
|
KEPT_PACKAGES+=(locales-all) && \
|
||||||
|
KEPT_PACKAGES+=(libfreetype6) && \
|
||||||
|
KEPT_PACKAGES+=(libfontconfig1) && \
|
||||||
|
KEPT_PACKAGES+=(p7zip-full) && \
|
||||||
|
KEPT_PACKAGES+=(python3) && \
|
||||||
|
KEPT_PACKAGES+=(python3-pip) && \
|
||||||
|
KEPT_PACKAGES+=(unrar-free) && \
|
||||||
|
# Install packages
|
||||||
|
DEBIAN_FRONTEND=noninteractive apt-get update -y && apt-get -yq upgrade && \
|
||||||
|
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
|
||||||
|
${KEPT_PACKAGES[@]} \
|
||||||
|
${TEMP_PACKAGES[@]} \
|
||||||
|
&& \
|
||||||
|
# Install required python modules
|
||||||
|
python -m pip install --upgrade pip && \
|
||||||
|
# python -m pip install -r /opt/kcc/requirements.txt && \
|
||||||
|
python -m venv /opt/venv && \
|
||||||
|
python -m pip install --upgrade pillow python-slugify psutil raven mozjpeg-lossless-optimization
|
||||||
|
|
||||||
|
|
||||||
|
######################################################################################
|
||||||
|
|
||||||
|
FROM --platform=linux/arm/v7 python:3.11-slim-bullseye as compile-armv7
|
||||||
|
ARG TARGETOS
|
||||||
|
ARG TARGETARCH
|
||||||
|
ARG TARGETVARIANT
|
||||||
|
RUN echo "I'm building for $TARGETOS/$TARGETARCH/$TARGETVARIANT"
|
||||||
|
|
||||||
|
ENV LC_ALL=C.UTF-8 \
|
||||||
|
LANG=C.UTF-8 \
|
||||||
|
LANGUAGE=en_US:en
|
||||||
|
|
||||||
|
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
|
||||||
|
|
||||||
|
RUN set -x && \
|
||||||
|
TEMP_PACKAGES=() && \
|
||||||
|
KEPT_PACKAGES=() && \
|
||||||
|
# Packages only required during build
|
||||||
|
TEMP_PACKAGES+=(build-essential) && \
|
||||||
|
TEMP_PACKAGES+=(cmake) && \
|
||||||
|
TEMP_PACKAGES+=(libffi-dev) && \
|
||||||
|
TEMP_PACKAGES+=(libfreetype6-dev) && \
|
||||||
|
TEMP_PACKAGES+=(libfontconfig1-dev) && \
|
||||||
|
TEMP_PACKAGES+=(libpng-dev) && \
|
||||||
|
TEMP_PACKAGES+=(libjpeg-dev) && \
|
||||||
|
TEMP_PACKAGES+=(libssl-dev) && \
|
||||||
|
TEMP_PACKAGES+=(libxft-dev) && \
|
||||||
|
TEMP_PACKAGES+=(make) && \
|
||||||
|
TEMP_PACKAGES+=(python3-dev) && \
|
||||||
|
TEMP_PACKAGES+=(python3-setuptools) && \
|
||||||
|
TEMP_PACKAGES+=(python3-wheel) && \
|
||||||
|
# Packages kept in the image
|
||||||
|
KEPT_PACKAGES+=(bash) && \
|
||||||
|
KEPT_PACKAGES+=(ca-certificates) && \
|
||||||
|
KEPT_PACKAGES+=(chrpath) && \
|
||||||
|
KEPT_PACKAGES+=(locales) && \
|
||||||
|
KEPT_PACKAGES+=(locales-all) && \
|
||||||
|
KEPT_PACKAGES+=(libfreetype6) && \
|
||||||
|
KEPT_PACKAGES+=(libfontconfig1) && \
|
||||||
|
KEPT_PACKAGES+=(p7zip-full) && \
|
||||||
|
KEPT_PACKAGES+=(python3) && \
|
||||||
|
KEPT_PACKAGES+=(python3-pip) && \
|
||||||
|
KEPT_PACKAGES+=(unrar-free) && \
|
||||||
|
# Install packages
|
||||||
|
DEBIAN_FRONTEND=noninteractive apt-get update -y && apt-get -yq upgrade && \
|
||||||
|
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
|
||||||
|
${KEPT_PACKAGES[@]} \
|
||||||
|
${TEMP_PACKAGES[@]} \
|
||||||
|
&& \
|
||||||
|
# Install required python modules
|
||||||
|
python -m pip install --upgrade pip && \
|
||||||
|
# python -m pip install -r /opt/kcc/requirements.txt && \
|
||||||
|
python -m venv /opt/venv && \
|
||||||
|
python -m pip install --upgrade pillow python-slugify psutil raven mozjpeg-lossless-optimization
|
||||||
|
|
||||||
|
|
||||||
|
######################################################################################
|
||||||
|
FROM --platform=linux/amd64 python:3.11-slim-bullseye as build-amd64
|
||||||
|
COPY --from=compile-amd64 /opt/venv /opt/venv
|
||||||
|
|
||||||
|
FROM --platform=linux/arm64 python:3.11-slim-bullseye as build-arm64
|
||||||
|
COPY --from=compile-arm64 /opt/venv /opt/venv
|
||||||
|
|
||||||
|
FROM --platform=linux/arm/v7 python:3.11-slim-bullseye as build-armv7
|
||||||
|
COPY --from=compile-armv7 /opt/venv /opt/venv
|
||||||
|
######################################################################################
|
||||||
|
|
||||||
|
# Select final stage based on TARGETARCH ARG
|
||||||
|
FROM build-${TARGETARCH}${TARGETVARIANT}
|
||||||
|
LABEL com.kcc.name="Kindle Comic Converter base image"
|
||||||
|
LABEL com.kcc.author="Ciro Mattia Gonano, Paweł Jastrzębski and Darodi"
|
||||||
|
LABEL org.opencontainers.image.description='Kindle Comic Converter base image'
|
||||||
|
LABEL org.opencontainers.image.documentation='https://github.com/ciromattia/kcc'
|
||||||
|
LABEL org.opencontainers.image.source='https://github.com/ciromattia/kcc'
|
||||||
|
LABEL org.opencontainers.image.authors='darodi'
|
||||||
|
LABEL org.opencontainers.image.url='https://github.com/ciromattia/kcc'
|
||||||
|
LABEL org.opencontainers.image.documentation='https://github.com/ciromattia/kcc'
|
||||||
|
LABEL org.opencontainers.image.vendor='ciromattia'
|
||||||
|
LABEL org.opencontainers.image.licenses='ISC'
|
||||||
|
LABEL org.opencontainers.image.title="Kindle Comic Converter"
|
||||||
|
|
||||||
|
|
||||||
|
ENV PATH="/opt/venv/bin:$PATH"
|
||||||
|
WORKDIR /app
|
||||||
|
RUN DEBIAN_FRONTEND=noninteractive apt-get update -y && apt-get -yq upgrade && \
|
||||||
|
apt-get install -y p7zip-full unrar-free && \
|
||||||
|
ln -s /app/kindlegen /bin/kindlegen && \
|
||||||
|
echo docker-base-20230809 > /IMAGE_VERSION
|
||||||
|
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
ISC LICENSE
|
ISC LICENSE
|
||||||
|
|
||||||
Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
||||||
Copyright (c) 2013-2017 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
|
||||||
|
|||||||
305
README.md
305
README.md
@@ -1,6 +1,10 @@
|
|||||||
# KCC
|
# KCC
|
||||||
|
|
||||||
[](https://github.com/ciromattia/kcc/releases) [](https://pypi.python.org/pypi/KindleComicConverter) [](https://aur.archlinux.org/packages/kcc/)
|
|
||||||
|
|
||||||
|
[](https://github.com/ciromattia/kcc/releases)
|
||||||
|
[](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).
|
||||||
@@ -24,45 +28,58 @@ If you find **KCC** valuable you can consider donating to the authors:
|
|||||||
- Paweł Jastrzębski:
|
- Paweł Jastrzębski:
|
||||||
- [](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YTTJ4LK2JDHPS)
|
- [](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YTTJ4LK2JDHPS)
|
||||||
- [](https://jastrzeb.ski/donate/)
|
- [](https://jastrzeb.ski/donate/)
|
||||||
|
- Alex Xu
|
||||||
|
- [](https://www.paypal.com/donate/?business=QFJVE7A6LCP6U&no_recurring=0&item_name=Kindle+Comic+Converter¤cy_code=USD)
|
||||||
|
|
||||||
## BINARY RELEASES
|
|
||||||
You can find the latest released binary at the following links:
|
|
||||||
- **Windows (64-bit only):** [http://kcc.iosphe.re/Windows/](http://kcc.iosphe.re/Windows/)
|
|
||||||
- **Linux (Glibc 2.19+):** [http://kcc.iosphe.re/Linux/](http://kcc.iosphe.re/Linux/)
|
|
||||||
- **OS X (10.10+):** [http://kcc.iosphe.re/OSX/](http://kcc.iosphe.re/OSX/)
|
|
||||||
|
|
||||||
## PYPI
|
## DOWNLOADS
|
||||||
**KCC** is also available on PyPI.
|
|
||||||
```
|
|
||||||
pip install KindleComicConverter
|
|
||||||
```
|
|
||||||
|
|
||||||
## DEPENDENCIES
|
- **https://github.com/ciromattia/kcc/releases**
|
||||||
Following software is required to run Linux version of **KCC** and/or bare sources:
|
|
||||||
- Python 3.3+
|
|
||||||
- [PyQt5](https://pypi.python.org/pypi/PyQt5) 5.6.0+
|
|
||||||
- [Pillow](https://pypi.python.org/pypi/Pillow/) 4.0.0+
|
|
||||||
- [psutil](https://pypi.python.org/pypi/psutil) 5.0.0+
|
|
||||||
- [python-slugify](https://pypi.python.org/pypi/python-slugify) 1.2.1+
|
|
||||||
- [raven](https://pypi.python.org/pypi/raven) 6.0.0+
|
|
||||||
|
|
||||||
On Debian based distributions these two commands should install all needed dependencies:
|
Click on **Assets** of the latest release.
|
||||||
```
|
|
||||||
sudo apt-get install python3 python3-dev python3-pip libpng-dev libjpeg-dev p7zip-full unrar
|
|
||||||
sudo pip3 install --upgrade pillow python-slugify psutil pyqt5 raven
|
|
||||||
```
|
|
||||||
|
|
||||||
### Optional dependencies
|
You probably want either
|
||||||
- [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)*
|
- `KCC_*.*.*.exe` (Windows)
|
||||||
- [UnRAR](http://www.rarlab.com/download.htm) *(For CBR/RAR support)*
|
- `kcc_macos_arm_*.*.*.dmg` (recent Mac with Apple Silicon M1 chip or later)
|
||||||
- [7za](http://www.7-zip.org/download.html) *(For 7z/CB7 support)*
|
- `kcc_macos_i386_*.*.*.dmg` (older Mac with Intel chip)
|
||||||
|
|
||||||
|
The `c2e` and `c2p` versions are command line tools for power users.
|
||||||
|
|
||||||
|
On Windows 11, you may need to run in compatibility mode for an older Windows version.
|
||||||
|
|
||||||
|
On Mac, right click open to get past the security warning.
|
||||||
|
|
||||||
|
For flatpak, Docker, and AppImage versions, refer to the wiki: https://github.com/ciromattia/kcc/wiki/Installation
|
||||||
|
|
||||||
|
## FAQ
|
||||||
|
|
||||||
|
- [Kindle Scribe cover guide](https://github.com/ciromattia/kcc/issues/508) (also works for older Kindles)
|
||||||
|
- [Windows 7 support](https://github.com/ciromattia/kcc/issues/678)
|
||||||
|
|
||||||
|
## PREREQUISITES
|
||||||
|
|
||||||
|
You'll need to install various tools to access important but optional features. Close and re-open KCC to get KCC to detect them.
|
||||||
|
|
||||||
|
### KindleGen
|
||||||
|
|
||||||
|
#### Windows / macOS KindleGen
|
||||||
|
|
||||||
|
Install [Kindle Previewer](https://www.amazon.com/Kindle-Previewer/b?ie=UTF8&node=21381691011) and `kindlegen` will be autodetected from it.
|
||||||
|
|
||||||
|
If you have issues detecting it, or use another OS, refer to the wiki: https://github.com/ciromattia/kcc/wiki/Installation#kindlegen
|
||||||
|
|
||||||
|
### 7-Zip
|
||||||
|
|
||||||
|
This is an optional requirement as of KCC 6.1.0. You only need to install it if 1) you are using advanced features, 2) are using Windows 10 (2017) or earlier, or 3) need to use an older KCC version.
|
||||||
|
|
||||||
|
If you need to install it, refer to the wiki: https://github.com/ciromattia/kcc/wiki/Installation#7-zip
|
||||||
|
|
||||||
## INPUT FORMATS
|
## INPUT FORMATS
|
||||||
**KCC** can understand and convert, at the moment, the following input types:
|
**KCC** can understand and convert, at the moment, the following input types:
|
||||||
- Folders containing: PNG, JPG or GIF files
|
- Folders containing: PNG, JPG, GIF or WebP files
|
||||||
- CBZ, ZIP
|
- CBZ, ZIP *(With `7z` executable)*
|
||||||
- CBR, RAR *(With `unrar` executable)*
|
- CBR, RAR *(With `7z` executable)*
|
||||||
- CB7, 7Z *(With `7za` executable)*
|
- CB7, 7Z *(With `7z` executable)*
|
||||||
- PDF *(Only extracting JPG images)*
|
- PDF *(Only extracting JPG images)*
|
||||||
|
|
||||||
## USAGE
|
## USAGE
|
||||||
@@ -73,112 +90,206 @@ 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),
|
||||||
|
'KoCC': ("Kobo Clara Colour", (1072, 1448), Palette16, 1.8),
|
||||||
|
'KoL': ("Kobo Libra H2O/Kobo Libra 2", (1264, 1680), Palette16, 1.8),
|
||||||
|
'KoLC': ("Kobo Libra Colour", (1264, 1680), Palette16, 1.8),
|
||||||
|
'KoF': ("Kobo Forma", (1440, 1920), Palette16, 1.8),
|
||||||
|
'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:
|
||||||
|
|
||||||
```
|
```
|
||||||
Usage: kcc-c2e [options] comic_file|comic_folder
|
usage: kcc-c2e [options] [input]
|
||||||
|
|
||||||
Options:
|
MANDATORY:
|
||||||
MAIN:
|
input Full path to comic folder or file(s) to be processed.
|
||||||
-p PROFILE, --profile=PROFILE
|
|
||||||
Device profile (Available options: K1, K2, K34, K578,
|
|
||||||
KDX, KPW, KV, KoMT, KoG, KoGHD, KoA, KoAHD, KoAH2O,
|
|
||||||
KoAO) [Default=KV]
|
|
||||||
-m, --manga-style Manga style (right-to-left reading and splitting)
|
|
||||||
-q, --hq Try to increase the quality of magnification
|
|
||||||
-2, --two-panel Display two not four panels in Panel View mode
|
|
||||||
-w, --webtoon Webtoon processing mode
|
|
||||||
|
|
||||||
OUTPUT SETTINGS:
|
MAIN:
|
||||||
-o OUTPUT, --output=OUTPUT
|
-p PROFILE, --profile PROFILE
|
||||||
Output generated file to specified directory or file
|
Device profile (Available options: K1, K2, K34, K578, KDX, KPW, KPW5, KV, KO, K11, KS, KoMT, KoG, KoGHD, KoA, KoAHD, KoAH2O, KoAO, KoN, KoC, KoL, KoF, KoS, KoE) [Default=KV]
|
||||||
-t TITLE, --title=TITLE
|
-m, --manga-style Manga style (right-to-left reading and splitting)
|
||||||
Comic title [Default=filename or directory name]
|
-q, --hq Try to increase the quality of magnification
|
||||||
-f FORMAT, --format=FORMAT
|
-2, --two-panel Display two not four panels in Panel View mode
|
||||||
Output format (Available options: Auto, MOBI, EPUB,
|
-w, --webtoon Webtoon processing mode
|
||||||
CBZ) [Default=Auto]
|
--ts TARGETSIZE, --targetsize TARGETSIZE
|
||||||
-b BATCHSPLIT, --batchsplit=BATCHSPLIT
|
the maximal size of output file in MB. [Default=100MB for webtoon and 400MB for others]
|
||||||
Split output into multiple files. 0: Don't split 1:
|
|
||||||
Automatic mode 2: Consider every subdirectory as
|
|
||||||
separate volume [Default=0]
|
|
||||||
|
|
||||||
PROCESSING:
|
PROCESSING:
|
||||||
-u, --upscale Resize images smaller than device's resolution
|
-n, --noprocessing Do not modify image and ignore any profil or processing option
|
||||||
-s, --stretch Stretch images to device's resolution
|
-u, --upscale Resize images smaller than device's resolution
|
||||||
-r SPLITTER, --splitter=SPLITTER
|
-s, --stretch Stretch images to device's resolution
|
||||||
Double page parsing mode. 0: Split 1: Rotate 2: Both
|
-r SPLITTER, --splitter SPLITTER
|
||||||
[Default=0]
|
Double page parsing mode. 0: Split 1: Rotate 2: Both [Default=0]
|
||||||
-g GAMMA, --gamma=GAMMA
|
-g GAMMA, --gamma GAMMA
|
||||||
Apply gamma correction to linearize the image
|
Apply gamma correction to linearize the image [Default=Auto]
|
||||||
[Default=Auto]
|
-c CROPPING, --cropping CROPPING
|
||||||
-c CROPPING, --cropping=CROPPING
|
Set cropping mode. 0: Disabled 1: Margins 2: Margins + page numbers [Default=2]
|
||||||
Set cropping mode. 0: Disabled 1: Margins 2: Margins +
|
--cp CROPPINGP, --croppingpower CROPPINGP
|
||||||
page numbers [Default=2]
|
|
||||||
--cp=CROPPINGP, --croppingpower=CROPPINGP
|
|
||||||
Set cropping power [Default=1.0]
|
Set cropping power [Default=1.0]
|
||||||
--blackborders Disable autodetection and force black borders
|
--cm CROPPINGM, --croppingminimum CROPPINGM
|
||||||
--whiteborders Disable autodetection and force white borders
|
Set cropping minimum area ratio [Default=0.0]
|
||||||
--forcecolor Don't convert images to grayscale
|
--blackborders Disable autodetection and force black borders
|
||||||
--forcepng Create PNG files instead JPEG
|
--whiteborders Disable autodetection and force white borders
|
||||||
|
--forcecolor Don't convert images to grayscale
|
||||||
|
--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:
|
OUTPUT SETTINGS:
|
||||||
--customwidth=CUSTOMWIDTH
|
-o OUTPUT, --output OUTPUT
|
||||||
|
Output generated file to specified directory or file
|
||||||
|
-t TITLE, --title TITLE
|
||||||
|
Comic title [Default=filename or directory name]
|
||||||
|
-f FORMAT, --format FORMAT
|
||||||
|
Output format (Available options: Auto, MOBI, EPUB, CBZ, KFX, MOBI+EPUB) [Default=Auto]
|
||||||
|
-b BATCHSPLIT, --batchsplit BATCHSPLIT
|
||||||
|
Split output into multiple files. 0: Don't split 1: Automatic mode 2: Consider every subdirectory as separate volume [Default=0]
|
||||||
|
|
||||||
|
CUSTOM PROFILE:
|
||||||
|
--customwidth CUSTOMWIDTH
|
||||||
Replace screen width provided by device profile
|
Replace screen width provided by device profile
|
||||||
--customheight=CUSTOMHEIGHT
|
--customheight CUSTOMHEIGHT
|
||||||
Replace screen height provided by device profile
|
Replace screen height provided by device profile
|
||||||
|
|
||||||
OTHER:
|
OTHER:
|
||||||
-h, --help Show this help message and exit
|
-h, --help Show this help message and exit
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Standalone `kcc-c2p.py` usage:
|
### Standalone `kcc-c2p.py` usage:
|
||||||
|
|
||||||
```
|
```
|
||||||
Usage: kcc-c2p [options] comic_folder
|
usage: kcc-c2p [options] [input]
|
||||||
|
|
||||||
Options:
|
MANDATORY:
|
||||||
MANDATORY:
|
input Full path to comic folder(s) to be processed. Separate multiple inputs with spaces.
|
||||||
-y HEIGHT, --height=HEIGHT
|
|
||||||
|
MAIN:
|
||||||
|
-y HEIGHT, --height HEIGHT
|
||||||
Height of the target device screen
|
Height of the target device screen
|
||||||
-i, --in-place Overwrite source directory
|
-i, --in-place Overwrite source directory
|
||||||
-m, --merge Combine every directory into a single image before 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
|
||||||
-h, --help Show this help message and exit
|
-h, --help Show this help message and exit
|
||||||
|
```
|
||||||
|
|
||||||
|
## INSTALL FROM SOURCE
|
||||||
|
|
||||||
|
This section is for developers who want to contribute to KCC or power users who want to run the latest code without waiting for an official release.
|
||||||
|
|
||||||
|
Easiest to use [GitHub Desktop](https://desktop.github.com) to clone the KCC repo. From GitHub Desktop, click on `Repository` in the toolbar, then `Command Prompt` (Windows)/`Terminal` (Mac) to open a window in the KCC repo.
|
||||||
|
|
||||||
|
Depending on your system [Python](https://www.python.org) may be called either `python` or `python3`. We use virtual environments (venv) to manage dependencies.
|
||||||
|
|
||||||
|
If you want to edit the code, a good code editor is [VS Code](https://code.visualstudio.com).
|
||||||
|
|
||||||
|
If you want to edit the `.ui` files, use [Qt Creator](https://www.qt.io/download-qt-installer-oss), included in **Qt for desktop development**.
|
||||||
|
Then use the `gen_ui_files` scripts to autogenerate the python UI.
|
||||||
|
|
||||||
|
|
||||||
|
### Windows install from source
|
||||||
|
|
||||||
|
One time setup and running for the first time:
|
||||||
|
```
|
||||||
|
python -m venv venv
|
||||||
|
venv\Scripts\activate.bat
|
||||||
|
pip install -r requirements.txt
|
||||||
|
python kcc.py
|
||||||
|
```
|
||||||
|
|
||||||
|
Every time you close Command Prompt, you will need to re-activate the virtual environment and re-run:
|
||||||
|
|
||||||
|
```
|
||||||
|
venv\Scripts\activate.bat
|
||||||
|
python kcc.py
|
||||||
|
```
|
||||||
|
|
||||||
|
### macOS install from source
|
||||||
|
|
||||||
|
One time setup and running for the first time:
|
||||||
|
```
|
||||||
|
python3 -m venv venv
|
||||||
|
source venv/bin/activate
|
||||||
|
pip install -r requirements.txt
|
||||||
|
python kcc.py
|
||||||
|
```
|
||||||
|
|
||||||
|
Every time you close Terminal, you will need to reactivate the virtual environment and re-run:
|
||||||
|
|
||||||
|
```
|
||||||
|
source venv/bin/activate
|
||||||
|
python kcc.py
|
||||||
```
|
```
|
||||||
|
|
||||||
## CREDITS
|
## CREDITS
|
||||||
**KCC** is made by [Ciro Mattia Gonano](http://github.com/ciromattia) 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)
|
||||||
|
- [Darodi](http://github.com/darodi)
|
||||||
|
- [Alex Xu](http://github.com/axu2)
|
||||||
|
|
||||||
This script born as a cross-platform alternative to `KindleComicParser` by **Dc5e** (published [here](http://www.mobileread.com/forums/showthread.php?t=192783)).
|
This script born as a cross-platform alternative to `KindleComicParser` by **Dc5e** (published [here](http://www.mobileread.com/forums/showthread.php?t=192783)).
|
||||||
|
|
||||||
The app relies and includes the following scripts:
|
The app relies and includes the following scripts:
|
||||||
|
|
||||||
- `DualMetaFix` script by **K. Hendricks**. Released with GPL-3 License.
|
- `DualMetaFix` script by **K. Hendricks**. Released with GPL-3 License.
|
||||||
- `rarfile.py` script © 2005-2014 **Marko Kreen** <markokr@gmail.com>. Released with ISC License.
|
|
||||||
- `image.py` class from **Alex Yatskov**'s [Mangle](https://github.com/FooSoft/mangle/) with subsequent [proDOOMman](https://github.com/proDOOMman/Mangle)'s and [Birua](https://github.com/Birua/Mangle)'s patches.
|
- `image.py` class from **Alex Yatskov**'s [Mangle](https://github.com/FooSoft/mangle/) with subsequent [proDOOMman](https://github.com/proDOOMman/Mangle)'s and [Birua](https://github.com/Birua/Mangle)'s patches.
|
||||||
- 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 Paperwhite 3 / Voyage / Oasis](http://kcc.iosphe.re/Samples/Ubunchu!-KV.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 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!-K45.mobi)
|
* [Kindle](http://kcc.iosphe.re/Samples/Ubunchu!-K578.mobi)
|
||||||
* [Kobo Aura](http://kcc.iosphe.re/Samples/Ubunchu-KoA.kepub.epub)
|
* [Kobo Aura](http://kcc.iosphe.re/Samples/Ubunchu-KoA.kepub.epub)
|
||||||
* [Kobo Aura HD](http://kcc.iosphe.re/Samples/Ubunchu-KoAHD.kepub.epub)
|
* [Kobo Aura HD](http://kcc.iosphe.re/Samples/Ubunchu-KoAHD.kepub.epub)
|
||||||
* [Kobo Aura H2O](http://kcc.iosphe.re/Samples/Ubunchu-KoAH2O.kepub.epub)
|
* [Kobo Aura H2O](http://kcc.iosphe.re/Samples/Ubunchu-KoAH2O.kepub.epub)
|
||||||
* [Kobo Aura ONE](http://kcc.iosphe.re/Samples/Ubunchu-KoAO.kepub.epub)
|
* [Kobo Aura ONE](http://kcc.iosphe.re/Samples/Ubunchu-KoAO.kepub.epub)
|
||||||
|
* [Kobo Forma](http://kcc.iosphe.re/Samples/Ubunchu-KoF.kepub.epub)
|
||||||
|
|
||||||
## PRIVACY
|
## PRIVACY
|
||||||
**KCC** is initiating internet connections in three cases:
|
**KCC** is initiating internet connections in two cases:
|
||||||
* During startup - Version check
|
* During startup - Version check.
|
||||||
* When MCD metadata are used - Cover download
|
* When error occurs - Automatic reporting on Windows and macOS.
|
||||||
* When error occurs - Automatic reporting
|
|
||||||
|
|
||||||
## 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-2017 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.
|
||||||
|
|||||||
320
application-vnd.appimage.svg
Normal file
320
application-vnd.appimage.svg
Normal 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 |
21
appveyor.yml
21
appveyor.yml
@@ -1,27 +1,14 @@
|
|||||||
environment:
|
environment:
|
||||||
PYTHON: "C:\\Python36-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
|
|
||||||
- nuget install verpatch -ExcludeVersion
|
|
||||||
- secure-file\tools\secure-file -decrypt other\windows\Cert.pfx.enc -secret %ENCRYPTION%
|
|
||||||
- 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\KindleComicConverter_win_* | % { 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
|
|
||||||
|
|||||||
16
environment.yml
Normal file
16
environment.yml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
name: kcc
|
||||||
|
channels:
|
||||||
|
- conda-forge
|
||||||
|
- defaults
|
||||||
|
dependencies:
|
||||||
|
- python=3.11
|
||||||
|
- Pillow>=5.2.0
|
||||||
|
- psutil>=5.9.5
|
||||||
|
- python-slugify>=1.2.1
|
||||||
|
- raven>=6.0.0
|
||||||
|
- distro
|
||||||
|
- natsort[fast]>=8.4.0
|
||||||
|
- pip
|
||||||
|
- pip:
|
||||||
|
- mozjpeg-lossless-optimization>=1.1.2
|
||||||
|
- pyside6>=6.5.1
|
||||||
3
gen_ui_files.bat
Normal file
3
gen_ui_files.bat
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
pyside6-uic gui/KCC.ui > kindlecomicconverter/KCC_ui.py
|
||||||
|
pyside6-uic gui/MetaEditor.ui > kindlecomicconverter/KCC_ui_editor.py
|
||||||
|
pyside6-rcc gui/KCC.qrc > kindlecomicconverter/KCC_rc.py
|
||||||
5
gen_ui_files.sh
Executable file
5
gen_ui_files.sh
Executable file
@@ -0,0 +1,5 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
pyside6-uic gui/KCC.ui --from-imports > kindlecomicconverter/KCC_ui.py
|
||||||
|
pyside6-uic gui/MetaEditor.ui --from-imports > kindlecomicconverter/KCC_ui_editor.py
|
||||||
|
pyside6-rcc gui/KCC.qrc > kindlecomicconverter/KCC_rc.py
|
||||||
@@ -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>
|
||||||
|
|||||||
464
gui/KCC.ui
464
gui/KCC.ui
@@ -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><html><head/><body><p style='white-space:pre'>Resolution of the target device.</p></body></html></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><html><head/><body><p style='white-space:pre'>Resolution of the target device.</p></body></html></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><html><head/><body><p style='white-space:pre'>Resolution of the target device.</p></body></html></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><html><head/><body><p style='white-space:pre'>Resolution of the target device.</p></body></html></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,29 +37,6 @@
|
|||||||
<property name="bottomMargin">
|
<property name="bottomMargin">
|
||||||
<number>0</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<item row="0" column="0">
|
|
||||||
<widget class="QCheckBox" name="mangaBox">
|
|
||||||
<property name="toolTip">
|
|
||||||
<string><html><head/><body><p style='white-space:pre'>Enable right-to-left reading.</p></body></html></string>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Manga mode</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="0" column="1">
|
|
||||||
<widget class="QCheckBox" name="rotateBox">
|
|
||||||
<property name="toolTip">
|
|
||||||
<string><html><head/><body><p><span style=" font-weight:600; text-decoration: underline;">Unchecked - Split<br/></span>Double page spreads will be cut into two separate pages.</p><p><span style=" font-weight:600; text-decoration: underline;">Indeterminate - Rotate and split<br/></span>Double page spreads will be displayed twice. First rotated and then split. </p><p><span style=" font-weight:600; text-decoration: underline;">Checked - Rotate<br/></span>Double page spreads will be rotated.</p></body></html></string>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Spread splitter</string>
|
|
||||||
</property>
|
|
||||||
<property name="tristate">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="0" column="2">
|
<item row="0" column="2">
|
||||||
<widget class="QCheckBox" name="qualityBox">
|
<widget class="QCheckBox" name="qualityBox">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
@@ -184,6 +50,49 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</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="3" column="1">
|
||||||
|
<widget class="QCheckBox" name="maximizeStrips">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string><html><head/><body><p><span style=" font-weight:600; text-decoration: underline;">Unchecked - 1x4<br/></span>Keep format 1x4 panels strips.</p><p><span style=" font-weight:600; text-decoration: underline;">Checked - 2x2<br/></span>Turn 1x4 strips to 2x2 to maximize screen usage.</p></body></html></string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>1x4 to 2x2 strips</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="2">
|
||||||
|
<widget class="QCheckBox" name="gammaBox">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string><html><head/><body><p style='white-space:pre'>Disable automatic gamma correction.</p></body></html></string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Custom gamma</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QCheckBox" name="borderBox">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string><html><head/><body><p><span style=" font-weight:600; text-decoration: underline;">Unchecked - Autodetection<br/></span>The color of margins fill will be detected automatically.</p><p><span style=" font-weight:600; text-decoration: underline;">Indeterminate - White<br/></span>Margins will be filled with white color.</p><p><span style=" font-weight:600; text-decoration: underline;">Checked - Black<br/></span>Margins will be filled with black color.</p></body></html></string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>W/B margins</string>
|
||||||
|
</property>
|
||||||
|
<property name="tristate">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item row="1" column="0">
|
<item row="1" column="0">
|
||||||
<widget class="QCheckBox" name="webtoonBox">
|
<widget class="QCheckBox" name="webtoonBox">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
@@ -207,23 +116,36 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="2">
|
<item row="0" column="0">
|
||||||
<widget class="QCheckBox" name="gammaBox">
|
<widget class="QCheckBox" name="mangaBox">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string><html><head/><body><p style='white-space:pre'>Disable automatic gamma correction.</p></body></html></string>
|
<string><html><head/><body><p style='white-space:pre'>Enable right-to-left reading.</p></body></html></string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Custom gamma</string>
|
<string>Manga mode</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="0">
|
<item row="0" column="1">
|
||||||
<widget class="QCheckBox" name="borderBox">
|
<widget class="QCheckBox" name="rotateBox">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string><html><head/><body><p><span style=" font-weight:600; text-decoration: underline;">Unchecked - Autodetection<br/></span>The color of margins fill will be detected automatically.</p><p><span style=" font-weight:600; text-decoration: underline;">Indeterminate - White<br/></span>Margins will be filled with white color.</p><p><span style=" font-weight:600; text-decoration: underline;">Checked - Black<br/></span>Margins will be filled with black color.</p></body></html></string>
|
<string><html><head/><body><p><span style=" font-weight:600; text-decoration: underline;">Unchecked - Split<br/></span>Double page spreads will be cut into two separate pages.</p><p><span style=" font-weight:600; text-decoration: underline;">Indeterminate - Rotate and split<br/></span>Double page spreads will be displayed twice. First rotated and then split. </p><p><span style=" font-weight:600; text-decoration: underline;">Checked - Rotate<br/></span>Double page spreads will be rotated.</p></body></html></string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>W/B margins</string>
|
<string>Spread splitter</string>
|
||||||
|
</property>
|
||||||
|
<property name="tristate">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="2">
|
||||||
|
<widget class="QCheckBox" name="croppingBox">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string><html><head/><body><p><span style=" font-weight:600; text-decoration: underline;">Unchecked - Disabled</span></p><p>Disabled</p><p><span style=" font-weight:600; text-decoration: underline;">Indeterminate - Margins<br/></span>Margins</p><p><span style=" font-weight:600; text-decoration: underline;">Checked - Margins + page numbers<br/></span>Margins +page numbers</p></body></html></string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Cropping mode</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="tristate">
|
<property name="tristate">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
@@ -240,6 +162,19 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QCheckBox" name="mozJpegBox">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string><html><head/><body><p><span style=" font-weight:600; text-decoration: underline;">Unchecked - JPEG<br/></span>Use JPEG files</p><p><span style=" font-weight:600; text-decoration: underline;">Indeterminate - force PNG<br/></span>Create PNG files instead JPEG</p><p><span style=" font-weight:600; text-decoration: underline;">Checked - mozJpeg<br/></span>10-20% smaller JPEG file, with the same image quality, but processing time multiplied by 2</p></body></html></string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>JPEG/PNG/mozJpeg</string>
|
||||||
|
</property>
|
||||||
|
<property name="tristate">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item row="2" column="2">
|
<item row="2" column="2">
|
||||||
<widget class="QCheckBox" name="colorBox">
|
<widget class="QCheckBox" name="colorBox">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
@@ -250,10 +185,30 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="4" column="2">
|
||||||
|
<widget class="QCheckBox" name="disableProcessingBox">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string><html><head/><body><pre style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Do not process any image, ignore profile and processing options</pre></body></html></string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Disable processing</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="0">
|
||||||
|
<widget class="QCheckBox" name="dedupeCoverBox">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string><html><head/><body><p>Don't duplicate the first page as the cover. Useful for 2 page spread alignment.</p></body></html></string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>De-dupe cover</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</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 +249,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,36 +268,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="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>
|
||||||
@@ -418,9 +362,6 @@
|
|||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string><html><head/><body><p style='white-space:pre'>Target device.</p></body></html></string>
|
<string><html><head/><body><p style='white-space:pre'>Target device.</p></body></html></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">
|
||||||
@@ -434,9 +375,6 @@
|
|||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string><html><head/><body><p style='white-space:pre'>Output format.</p></body></html></string>
|
<string><html><head/><body><p style='white-space:pre'>Output format.</p></body></html></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">
|
||||||
@@ -449,12 +387,11 @@
|
|||||||
</property>
|
</property>
|
||||||
<property name="font">
|
<property name="font">
|
||||||
<font>
|
<font>
|
||||||
<weight>75</weight>
|
|
||||||
<bold>true</bold>
|
<bold>true</bold>
|
||||||
</font>
|
</font>
|
||||||
</property>
|
</property>
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string><html><head/><body><p style='white-space:pre'>Shift+Click to select the output directory.</p></body></html></string>
|
<string><html><head/><body><p style='white-space:pre'>Shift+Click to select the output directory.</p></body></html></string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Convert</string>
|
<string>Convert</string>
|
||||||
@@ -491,6 +428,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><html><head/><body><p style='white-space:pre'>Shift+Click to edit directory.</p></body></html></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><html><head/><body><p style='white-space:pre'>Resolution of the target device.</p></body></html></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><html><head/><body><p style='white-space:pre'>Resolution of the target device.</p></body></html></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><html><head/><body><p style='white-space:pre'>Resolution of the target device.</p></body></html></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><html><head/><body><p style='white-space:pre'>Resolution of the target device.</p></body></html></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">
|
||||||
@@ -515,12 +617,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"/>
|
||||||
|
|||||||
@@ -112,19 +112,6 @@
|
|||||||
<item row="6" column="1">
|
<item row="6" column="1">
|
||||||
<widget class="QLineEdit" name="coloristLine"/>
|
<widget class="QLineEdit" name="coloristLine"/>
|
||||||
</item>
|
</item>
|
||||||
<item row="7" column="0">
|
|
||||||
<widget class="QLabel" name="label_8">
|
|
||||||
<property name="text">
|
|
||||||
<string><html><head/><body><p><a href="https://github.com/ciromattia/kcc/wiki/Manga-Cover-Database-support"><span style=" text-decoration: underline; color:#0000ff;">MUid:</span></a></p></body></html></string>
|
|
||||||
</property>
|
|
||||||
<property name="openExternalLinks">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="7" column="1">
|
|
||||||
<widget class="QLineEdit" name="muidLine"/>
|
|
||||||
</item>
|
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
|||||||
BIN
icons/KFX.png
Normal file
BIN
icons/KFX.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.8 KiB |
12
kcc-c2e.py
12
kcc-c2e.py
@@ -2,7 +2,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
||||||
# Copyright (c) 2013-2017 Pawel Jastrzebski <pawelj@iosphe.re>
|
# Copyright (c) 2013-2019 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||||
#
|
#
|
||||||
# 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
|
||||||
@@ -19,13 +19,15 @@
|
|||||||
# PERFORMANCE OF THIS SOFTWARE.
|
# PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
if sys.version_info[0] != 3:
|
|
||||||
print('ERROR: This is Python 3 script!')
|
|
||||||
exit(1)
|
|
||||||
|
|
||||||
from multiprocessing import freeze_support
|
if sys.version_info < (3, 8, 0):
|
||||||
|
print('ERROR: This is a Python 3.8+ script!')
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
from multiprocessing import freeze_support, set_start_method
|
||||||
from kindlecomicconverter.startup import startC2E
|
from kindlecomicconverter.startup import startC2E
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
set_start_method('spawn')
|
||||||
freeze_support()
|
freeze_support()
|
||||||
startC2E()
|
startC2E()
|
||||||
|
|||||||
39
kcc-c2e.spec
Normal file
39
kcc-c2e.spec
Normal 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')
|
||||||
12
kcc-c2p.py
12
kcc-c2p.py
@@ -2,7 +2,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
||||||
# Copyright (c) 2013-2017 Pawel Jastrzebski <pawelj@iosphe.re>
|
# Copyright (c) 2013-2019 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||||
#
|
#
|
||||||
# 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
|
||||||
@@ -19,13 +19,15 @@
|
|||||||
# PERFORMANCE OF THIS SOFTWARE.
|
# PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
if sys.version_info[0] != 3:
|
|
||||||
print('ERROR: This is Python 3 script!')
|
|
||||||
exit(1)
|
|
||||||
|
|
||||||
from multiprocessing import freeze_support
|
if sys.version_info < (3, 8, 0):
|
||||||
|
print('ERROR: This is a Python 3.8+ script!')
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
from multiprocessing import freeze_support, set_start_method
|
||||||
from kindlecomicconverter.startup import startC2P
|
from kindlecomicconverter.startup import startC2P
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
set_start_method('spawn')
|
||||||
freeze_support()
|
freeze_support()
|
||||||
startC2P()
|
startC2P()
|
||||||
|
|||||||
39
kcc-c2p.spec
Normal file
39
kcc-c2p.spec
Normal 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')
|
||||||
125
kcc.iss
125
kcc.iss
@@ -1,125 +0,0 @@
|
|||||||
#define MyAppName "Kindle Comic Converter"
|
|
||||||
#define MyAppVersion "5.4.1"
|
|
||||||
#define MyAppPublisher "Ciro Mattia Gonano, Paweł Jastrzębski"
|
|
||||||
#define MyAppURL "http://kcc.iosphe.re/"
|
|
||||||
#define MyAppExeName "KCC.exe"
|
|
||||||
|
|
||||||
[Setup]
|
|
||||||
AppId={{7D279A59-C65E-4DA7-B165-56DD06596216}
|
|
||||||
AppName={#MyAppName}
|
|
||||||
AppVersion={#MyAppVersion}
|
|
||||||
AppPublisher={#MyAppPublisher}
|
|
||||||
AppPublisherURL={#MyAppURL}
|
|
||||||
AppSupportURL={#MyAppURL}
|
|
||||||
AppUpdatesURL={#MyAppURL}
|
|
||||||
AppCopyright=Copyright (C) 2012-2017 Ciro Mattia Gonano and Paweł Jastrzębski
|
|
||||||
ArchitecturesAllowed=x64
|
|
||||||
DefaultDirName={pf}\{#MyAppName}
|
|
||||||
DefaultGroupName={#MyAppName}
|
|
||||||
AllowNoIcons=yes
|
|
||||||
LicenseFile=LICENSE.txt
|
|
||||||
OutputBaseFilename=KindleComicConverter_win_{#MyAppVersion}
|
|
||||||
SetupIconFile=icons\comic2ebook.ico
|
|
||||||
SolidCompression=yes
|
|
||||||
ShowLanguageDialog=no
|
|
||||||
LanguageDetectionMethod=none
|
|
||||||
WizardImageFile=icons\Wizard.bmp
|
|
||||||
WizardSmallImageFile=icons\Wizard-Small.bmp
|
|
||||||
UninstallDisplayName={#MyAppName}
|
|
||||||
UninstallDisplayIcon={app}\{#MyAppExeName}
|
|
||||||
ChangesAssociations=True
|
|
||||||
InfoAfterFile=other\windows\InstallWarning.rtf
|
|
||||||
SignTool=SignTool /d $q{#MyAppName}$q /du $q{#MyAppURL}$q $f
|
|
||||||
MinVersion=0,6.0
|
|
||||||
OutputDir=dist
|
|
||||||
ArchitecturesInstallIn64BitMode=x64
|
|
||||||
|
|
||||||
[Languages]
|
|
||||||
Name: "english"; MessagesFile: "compiler:Default.isl"
|
|
||||||
|
|
||||||
[Tasks]
|
|
||||||
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
|
|
||||||
Name: "CBZassociation"; Description: "CBZ"; GroupDescription: "File associations:"
|
|
||||||
Name: "CBRassociation"; Description: "CBR"; GroupDescription: "File associations:"
|
|
||||||
Name: "CB7association"; Description: "CB7"; GroupDescription: "File associations:"
|
|
||||||
|
|
||||||
[Files]
|
|
||||||
Source: "dist\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion
|
|
||||||
Source: "LICENSE.txt"; DestDir: "{app}"; Flags: ignoreversion solidbreak
|
|
||||||
Source: "other\windows\Additional-LICENSE.txt"; DestDir: "{app}"; Flags: ignoreversion
|
|
||||||
Source: "other\windows\UnRAR.exe"; DestDir: "{app}"; Flags: ignoreversion
|
|
||||||
Source: "other\windows\7za.exe"; DestDir: "{app}"; Flags: ignoreversion
|
|
||||||
Source: "other\windows\vc_redist.x64.exe"; DestDir: "{tmp}"; Flags: ignoreversion deleteafterinstall
|
|
||||||
|
|
||||||
[Icons]
|
|
||||||
Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
|
|
||||||
Name: "{group}\Readme"; Filename: "https://github.com/ciromattia/kcc#kcc"
|
|
||||||
Name: "{commondesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon
|
|
||||||
|
|
||||||
[Run]
|
|
||||||
Filename: "{tmp}\vc_redist.x64.exe"; Parameters: "/install /passive /norestart"; StatusMsg: "Installing Microsoft Visual C++ 2015 Redistributable Package..."
|
|
||||||
Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall
|
|
||||||
|
|
||||||
[Messages]
|
|
||||||
WelcomeLabel1=Welcome to the KCC Setup Wizard
|
|
||||||
FinishedHeadingLabel=Completing the KCC Setup Wizard
|
|
||||||
|
|
||||||
[Registry]
|
|
||||||
Root: HKCR; SubKey: ".cbz"; ValueType: string; ValueData: "KCCZIP"; Flags: uninsdeletekey; Tasks: CBZassociation
|
|
||||||
Root: HKCR; SubKey: "KCCZIP"; ValueType: string; ValueData: "KCC ZIP Archive"; Flags: uninsdeletekey; Tasks: CBZassociation
|
|
||||||
Root: HKCR; SubKey: "KCCZIP\Shell\Open\Command"; ValueType: string; ValueData: """{app}\{#MyAppExeName}"" ""%1"""; Flags: uninsdeletekey; Tasks: CBZassociation
|
|
||||||
Root: HKCR; Subkey: "KCCZIP\DefaultIcon"; ValueType: string; ValueData: "{app}\{#MyAppExeName},0"; Flags: uninsdeletevalue; Tasks: CBZassociation
|
|
||||||
Root: HKCR; SubKey: ".cbr"; ValueType: string; ValueData: "KCCRAR"; Flags: uninsdeletekey; Tasks: CBRassociation
|
|
||||||
Root: HKCR; SubKey: "KCCRAR"; ValueType: string; ValueData: "KCC RAR Archive"; Flags: uninsdeletekey; Tasks: CBRassociation
|
|
||||||
Root: HKCR; SubKey: "KCCRAR\Shell\Open\Command"; ValueType: string; ValueData: """{app}\{#MyAppExeName}"" ""%1"""; Flags: uninsdeletekey; Tasks: CBRassociation
|
|
||||||
Root: HKCR; Subkey: "KCCRAR\DefaultIcon"; ValueType: string; ValueData: "{app}\{#MyAppExeName},0"; Flags: uninsdeletevalue; Tasks: CBRassociation
|
|
||||||
Root: HKCR; SubKey: ".cb7"; ValueType: string; ValueData: "KCCCB7"; Flags: uninsdeletekey; Tasks: CB7association
|
|
||||||
Root: HKCR; SubKey: "KCCCB7"; ValueType: string; ValueData: "KCC 7z Archive"; Flags: uninsdeletekey; Tasks: CB7association
|
|
||||||
Root: HKCR; SubKey: "KCCCB7\Shell\Open\Command"; ValueType: string; ValueData: """{app}\{#MyAppExeName}"" ""%1"""; Flags: uninsdeletekey; Tasks: CB7association
|
|
||||||
Root: HKCR; Subkey: "KCCCB7\DefaultIcon"; ValueType: string; ValueData: "{app}\{#MyAppExeName},0"; Flags: uninsdeletevalue; Tasks: CB7association
|
|
||||||
|
|
||||||
[Code]
|
|
||||||
function GetUninstallString(): String;
|
|
||||||
var
|
|
||||||
sUnInstPath: String;
|
|
||||||
sUnInstallString: String;
|
|
||||||
begin
|
|
||||||
sUnInstPath := ExpandConstant('Software\Microsoft\Windows\CurrentVersion\Uninstall\{#emit SetupSetting("AppId")}_is1');
|
|
||||||
sUnInstallString := '';
|
|
||||||
if not RegQueryStringValue(HKLM, sUnInstPath, 'UninstallString', sUnInstallString) then
|
|
||||||
RegQueryStringValue(HKCU, sUnInstPath, 'UninstallString', sUnInstallString);
|
|
||||||
Result := sUnInstallString;
|
|
||||||
end;
|
|
||||||
|
|
||||||
function IsUpgrade(): Boolean;
|
|
||||||
begin
|
|
||||||
Result := (GetUninstallString() <> '');
|
|
||||||
end;
|
|
||||||
|
|
||||||
function UnInstallOldVersion(): Integer;
|
|
||||||
var
|
|
||||||
sUnInstallString: String;
|
|
||||||
iResultCode: Integer;
|
|
||||||
begin
|
|
||||||
Result := 0;
|
|
||||||
sUnInstallString := GetUninstallString();
|
|
||||||
if sUnInstallString <> '' then begin
|
|
||||||
sUnInstallString := RemoveQuotes(sUnInstallString);
|
|
||||||
if Exec(sUnInstallString, '/SILENT /NORESTART /SUPPRESSMSGBOXES','', SW_HIDE, ewWaitUntilTerminated, iResultCode) then
|
|
||||||
Result := 3
|
|
||||||
else
|
|
||||||
Result := 2;
|
|
||||||
end else
|
|
||||||
Result := 1;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure CurStepChanged(CurStep: TSetupStep);
|
|
||||||
begin
|
|
||||||
if (CurStep=ssInstall) then
|
|
||||||
begin
|
|
||||||
if (IsUpgrade()) then
|
|
||||||
begin
|
|
||||||
UnInstallOldVersion();
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
80
kcc.py
80
kcc.py
@@ -2,7 +2,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
||||||
# Copyright (c) 2013-2017 Pawel Jastrzebski <pawelj@iosphe.re>
|
# Copyright (c) 2013-2019 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||||
#
|
#
|
||||||
# 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
|
||||||
@@ -19,54 +19,62 @@
|
|||||||
# PERFORMANCE OF THIS SOFTWARE.
|
# PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
if sys.version_info[0] != 3:
|
|
||||||
print('ERROR: This is Python 3 script!')
|
if sys.version_info < (3, 8, 0):
|
||||||
exit(1)
|
print('ERROR: This is a Python 3.8+ script!')
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
# OS specific workarounds
|
# OS specific workarounds
|
||||||
import os
|
import os
|
||||||
if sys.platform.startswith('darwin'):
|
if sys.platform.startswith('darwin'):
|
||||||
|
# prioritize KC2 since it optionally also installs KP3
|
||||||
|
mac_paths = [
|
||||||
|
'/Applications/Kindle Comic Creator/Kindle Comic Creator.app/Contents/MacOS',
|
||||||
|
'/Applications/Kindle Previewer 3.app/Contents/lib/fc/bin/',
|
||||||
|
]
|
||||||
if getattr(sys, 'frozen', False):
|
if getattr(sys, 'frozen', False):
|
||||||
os.environ['PATH'] = os.path.dirname(os.path.abspath(sys.executable)) + \
|
os.environ['PATH'] += os.pathsep + os.pathsep.join(mac_paths +
|
||||||
'/../Resources:/usr/local/bin:/usr/bin:/bin'
|
[
|
||||||
os.system('defaults write com.kindlecomicconverter.KindleComicConverter ApplePersistenceIgnoreState YES')
|
'/opt/homebrew/bin',
|
||||||
os.system('defaults write com.kindlecomicconverter.KindleComicConverter NSInitialToolTipDelay -int 1000')
|
'/usr/local/bin',
|
||||||
else:
|
'/usr/bin',
|
||||||
os.environ['PATH'] = os.path.dirname(os.path.abspath(__file__)) + '/other/osx/:' + os.environ['PATH']
|
'/bin',
|
||||||
elif sys.platform.startswith('win'):
|
]
|
||||||
import multiprocessing.popen_spawn_win32 as forking
|
)
|
||||||
|
|
||||||
class _Popen(forking.Popen):
|
|
||||||
def __init__(self, *args, **kw):
|
|
||||||
if hasattr(sys, 'frozen'):
|
|
||||||
# noinspection PyUnresolvedReferences,PyProtectedMember
|
|
||||||
os.putenv('_MEIPASS2', sys._MEIPASS)
|
|
||||||
try:
|
|
||||||
super(_Popen, self).__init__(*args, **kw)
|
|
||||||
finally:
|
|
||||||
if hasattr(sys, 'frozen'):
|
|
||||||
if hasattr(os, 'unsetenv'):
|
|
||||||
os.unsetenv('_MEIPASS2')
|
|
||||||
else:
|
|
||||||
os.putenv('_MEIPASS2', '')
|
|
||||||
forking.Popen = _Popen
|
|
||||||
|
|
||||||
if getattr(sys, 'frozen', False):
|
|
||||||
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.pathsep + os.pathsep.join(mac_paths)
|
||||||
|
os.chdir(os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
elif sys.platform.startswith('win'):
|
||||||
|
# prioritize KC2 since it optionally also installs KP3
|
||||||
|
win_paths = [
|
||||||
|
os.path.expandvars('%LOCALAPPDATA%\\Amazon\\KC2'),
|
||||||
|
os.path.expandvars('%LOCALAPPDATA%\\Amazon\\Kindle Previewer 3\\lib\\fc\\bin\\'),
|
||||||
|
'C:\\Apps\\Kindle Previewer 3\\lib\\fc\\bin',
|
||||||
|
'D:\\Apps\\Kindle Previewer 3\\lib\\fc\\bin',
|
||||||
|
'E:\\Apps\\Kindle Previewer 3\\lib\\fc\\bin',
|
||||||
|
'C:\\Program Files\\7-Zip',
|
||||||
|
'D:\\Program Files\\7-Zip',
|
||||||
|
'E:\\Program Files\\7-Zip',
|
||||||
|
]
|
||||||
|
if getattr(sys, 'frozen', False):
|
||||||
|
os.environ['PATH'] += os.pathsep + os.pathsep.join(win_paths)
|
||||||
|
os.chdir(os.path.dirname(os.path.abspath(sys.executable)))
|
||||||
|
else:
|
||||||
|
os.environ['PATH'] += os.pathsep + os.pathsep.join(win_paths)
|
||||||
os.chdir(os.path.dirname(os.path.abspath(__file__)))
|
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:
|
# except ImportError:
|
||||||
pass
|
# pass
|
||||||
|
|
||||||
from multiprocessing import freeze_support
|
from multiprocessing import freeze_support, set_start_method
|
||||||
from kindlecomicconverter.startup import start
|
from kindlecomicconverter.startup import start
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
set_start_method('spawn')
|
||||||
freeze_support()
|
freeze_support()
|
||||||
start()
|
start()
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
||||||
# Copyright (c) 2013-2017 Pawel Jastrzebski <pawelj@iosphe.re>
|
# Copyright (c) 2013-2019 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||||
#
|
#
|
||||||
# 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
|
||||||
@@ -16,22 +16,26 @@
|
|||||||
# OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
# OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||||
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
# PERFORMANCE OF THIS SOFTWARE.
|
# PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
import sys
|
import sys
|
||||||
from urllib.parse import unquote
|
from urllib.parse import unquote
|
||||||
from urllib.request import urlopen, urlretrieve, Request
|
|
||||||
from time import sleep
|
from time import sleep
|
||||||
from shutil import move
|
from shutil import move, rmtree
|
||||||
from subprocess import STDOUT, PIPE
|
from subprocess import STDOUT, PIPE
|
||||||
from PyQt5 import QtGui, QtCore, QtWidgets, QtNetwork
|
|
||||||
from xml.dom.minidom import parse
|
import requests
|
||||||
|
# noinspection PyUnresolvedReferences
|
||||||
|
from PySide6 import QtGui, QtCore, QtWidgets, QtNetwork
|
||||||
|
from PySide6.QtCore import Qt
|
||||||
from xml.sax.saxutils import escape
|
from xml.sax.saxutils import escape
|
||||||
from psutil import Popen, Process
|
from psutil import Process
|
||||||
from copy import copy
|
from copy import copy
|
||||||
from distutils.version import StrictVersion
|
from distutils.version import StrictVersion
|
||||||
from raven import Client
|
from raven import Client
|
||||||
from .shared import md5Checksum, HTMLStripper, sanitizeTrace
|
from tempfile import gettempdir
|
||||||
|
|
||||||
|
from .shared import HTMLStripper, sanitizeTrace, walkLevel, subprocess_run_silent
|
||||||
from . import __version__
|
from . import __version__
|
||||||
from . import comic2ebook
|
from . import comic2ebook
|
||||||
from . import metadata
|
from . import metadata
|
||||||
@@ -41,7 +45,7 @@ from . import KCC_ui_editor
|
|||||||
|
|
||||||
|
|
||||||
class QApplicationMessaging(QtWidgets.QApplication):
|
class QApplicationMessaging(QtWidgets.QApplication):
|
||||||
messageFromOtherInstance = QtCore.pyqtSignal(bytes)
|
messageFromOtherInstance = QtCore.Signal(bytes)
|
||||||
|
|
||||||
def __init__(self, argv):
|
def __init__(self, argv):
|
||||||
QtWidgets.QApplication.__init__(self, argv)
|
QtWidgets.QApplication.__init__(self, argv)
|
||||||
@@ -49,7 +53,7 @@ class QApplicationMessaging(QtWidgets.QApplication):
|
|||||||
self._timeout = 1000
|
self._timeout = 1000
|
||||||
self._locked = False
|
self._locked = False
|
||||||
socket = QtNetwork.QLocalSocket(self)
|
socket = QtNetwork.QLocalSocket(self)
|
||||||
socket.connectToServer(self._key, QtCore.QIODevice.WriteOnly)
|
socket.connectToServer(self._key, QtCore.QIODeviceBase.OpenModeFlag.WriteOnly)
|
||||||
if not socket.waitForConnected(self._timeout):
|
if not socket.waitForConnected(self._timeout):
|
||||||
self._server = QtNetwork.QLocalServer(self)
|
self._server = QtNetwork.QLocalServer(self)
|
||||||
self._server.newConnection.connect(self.handleMessage)
|
self._server.newConnection.connect(self.handleMessage)
|
||||||
@@ -63,7 +67,7 @@ class QApplicationMessaging(QtWidgets.QApplication):
|
|||||||
self._server.close()
|
self._server.close()
|
||||||
|
|
||||||
def event(self, e):
|
def event(self, e):
|
||||||
if e.type() == QtCore.QEvent.FileOpen:
|
if e.type() == QtCore.QEvent.Type.FileOpen:
|
||||||
self.messageFromOtherInstance.emit(bytes(e.file(), 'UTF-8'))
|
self.messageFromOtherInstance.emit(bytes(e.file(), 'UTF-8'))
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
@@ -79,7 +83,7 @@ class QApplicationMessaging(QtWidgets.QApplication):
|
|||||||
|
|
||||||
def sendMessage(self, message):
|
def sendMessage(self, message):
|
||||||
socket = QtNetwork.QLocalSocket(self)
|
socket = QtNetwork.QLocalSocket(self)
|
||||||
socket.connectToServer(self._key, QtCore.QIODevice.WriteOnly)
|
socket.connectToServer(self._key, QtCore.QIODeviceBase.OpenModeFlag.WriteOnly)
|
||||||
socket.waitForConnected(self._timeout)
|
socket.waitForConnected(self._timeout)
|
||||||
socket.write(bytes(message, 'UTF-8'))
|
socket.write(bytes(message, 'UTF-8'))
|
||||||
socket.waitForBytesWritten(self._timeout)
|
socket.waitForBytesWritten(self._timeout)
|
||||||
@@ -87,40 +91,40 @@ class QApplicationMessaging(QtWidgets.QApplication):
|
|||||||
|
|
||||||
|
|
||||||
class QMainWindowKCC(QtWidgets.QMainWindow):
|
class QMainWindowKCC(QtWidgets.QMainWindow):
|
||||||
progressBarTick = QtCore.pyqtSignal(str)
|
progressBarTick = QtCore.Signal(str)
|
||||||
modeConvert = QtCore.pyqtSignal(int)
|
modeConvert = QtCore.Signal(int)
|
||||||
addMessage = QtCore.pyqtSignal(str, str, bool)
|
addMessage = QtCore.Signal(str, str, bool)
|
||||||
addTrayMessage = QtCore.pyqtSignal(str, str)
|
addTrayMessage = QtCore.Signal(str, str)
|
||||||
showDialog = QtCore.pyqtSignal(str, str)
|
showDialog = QtCore.Signal(str, str)
|
||||||
hideProgressBar = QtCore.pyqtSignal()
|
hideProgressBar = QtCore.Signal()
|
||||||
forceShutdown = QtCore.pyqtSignal()
|
forceShutdown = QtCore.Signal()
|
||||||
|
|
||||||
|
|
||||||
class Icons:
|
class Icons:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.deviceKindle = QtGui.QIcon()
|
self.deviceKindle = QtGui.QIcon()
|
||||||
self.deviceKindle.addPixmap(QtGui.QPixmap(":/Devices/icons/Kindle.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
self.deviceKindle.addPixmap(QtGui.QPixmap(":/Devices/icons/Kindle.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
||||||
self.deviceKobo = QtGui.QIcon()
|
self.deviceKobo = QtGui.QIcon()
|
||||||
self.deviceKobo.addPixmap(QtGui.QPixmap(":/Devices/icons/Kobo.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
self.deviceKobo.addPixmap(QtGui.QPixmap(":/Devices/icons/Kobo.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
||||||
self.deviceOther = QtGui.QIcon()
|
self.deviceOther = QtGui.QIcon()
|
||||||
self.deviceOther.addPixmap(QtGui.QPixmap(":/Devices/icons/Other.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
self.deviceOther.addPixmap(QtGui.QPixmap(":/Devices/icons/Other.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
||||||
|
|
||||||
self.MOBIFormat = QtGui.QIcon()
|
self.MOBIFormat = QtGui.QIcon()
|
||||||
self.MOBIFormat.addPixmap(QtGui.QPixmap(":/Formats/icons/MOBI.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
self.MOBIFormat.addPixmap(QtGui.QPixmap(":/Formats/icons/MOBI.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
||||||
self.CBZFormat = QtGui.QIcon()
|
self.CBZFormat = QtGui.QIcon()
|
||||||
self.CBZFormat.addPixmap(QtGui.QPixmap(":/Formats/icons/CBZ.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
self.CBZFormat.addPixmap(QtGui.QPixmap(":/Formats/icons/CBZ.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
||||||
self.EPUBFormat = QtGui.QIcon()
|
self.EPUBFormat = QtGui.QIcon()
|
||||||
self.EPUBFormat.addPixmap(QtGui.QPixmap(":/Formats/icons/EPUB.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
self.EPUBFormat.addPixmap(QtGui.QPixmap(":/Formats/icons/EPUB.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
||||||
|
|
||||||
self.info = QtGui.QIcon()
|
self.info = QtGui.QIcon()
|
||||||
self.info.addPixmap(QtGui.QPixmap(":/Status/icons/info.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
self.info.addPixmap(QtGui.QPixmap(":/Status/icons/info.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
||||||
self.warning = QtGui.QIcon()
|
self.warning = QtGui.QIcon()
|
||||||
self.warning.addPixmap(QtGui.QPixmap(":/Status/icons/warning.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
self.warning.addPixmap(QtGui.QPixmap(":/Status/icons/warning.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
||||||
self.error = QtGui.QIcon()
|
self.error = QtGui.QIcon()
|
||||||
self.error.addPixmap(QtGui.QPixmap(":/Status/icons/error.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
self.error.addPixmap(QtGui.QPixmap(":/Status/icons/error.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
||||||
|
|
||||||
self.programIcon = QtGui.QIcon()
|
self.programIcon = QtGui.QIcon()
|
||||||
self.programIcon.addPixmap(QtGui.QPixmap(":/Icon/icons/comic2ebook.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
self.programIcon.addPixmap(QtGui.QPixmap(":/Icon/icons/comic2ebook.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
||||||
|
|
||||||
|
|
||||||
class VersionThread(QtCore.QThread):
|
class VersionThread(QtCore.QThread):
|
||||||
@@ -136,57 +140,22 @@ class VersionThread(QtCore.QThread):
|
|||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
try:
|
try:
|
||||||
XML = parse(urlopen(Request('https://kcc.iosphe.re/Version/',
|
json_parser = requests.get("https://api.github.com/repos/ciromattia/kcc/releases/latest").json()
|
||||||
headers={'User-Agent': 'KindleComicConverter/' + __version__})))
|
|
||||||
|
html_url = json_parser["html_url"]
|
||||||
|
latest_version = json_parser["tag_name"]
|
||||||
|
latest_version = re.sub(r'^v', "", latest_version)
|
||||||
|
|
||||||
|
if ("b" not in __version__ and StrictVersion(latest_version) > StrictVersion(__version__)) \
|
||||||
|
or ("b" in __version__
|
||||||
|
and StrictVersion(latest_version) >= StrictVersion(re.sub(r'b.*', '', __version__))):
|
||||||
|
MW.addMessage.emit('<a href="' + html_url + '"><b>The new version is available!</b></a>', 'warning',
|
||||||
|
False)
|
||||||
except Exception:
|
except Exception:
|
||||||
return
|
return
|
||||||
latestVersion = XML.childNodes[0].getElementsByTagName('LatestVersion')[0].childNodes[0].toxml()
|
|
||||||
if StrictVersion(latestVersion) > StrictVersion(__version__):
|
|
||||||
if sys.platform.startswith('win'):
|
|
||||||
self.newVersion = latestVersion
|
|
||||||
self.md5 = XML.childNodes[0].getElementsByTagName('MD5')[0].childNodes[0].toxml()
|
|
||||||
MW.showDialog.emit('<b>New version released!</b> <a href="https://github.com/ciromattia/kcc/releases/">'
|
|
||||||
'See changelog.</a><br/><br/>Installed version: ' + __version__ +
|
|
||||||
'<br/>Current version: ' + latestVersion +
|
|
||||||
'<br/><br/>Would you like to start automatic update?', 'question')
|
|
||||||
self.getNewVersion()
|
|
||||||
else:
|
|
||||||
MW.addMessage.emit('<a href="https://kcc.iosphe.re/">'
|
|
||||||
'<b>The new version is available!</b></a> '
|
|
||||||
'(<a href="https://github.com/ciromattia/kcc/releases/">'
|
|
||||||
'Changelog</a>)', 'warning', False)
|
|
||||||
|
|
||||||
def setAnswer(self, dialogAnswer):
|
def setAnswer(self, dialoganswer):
|
||||||
self.answer = dialogAnswer
|
self.answer = dialoganswer
|
||||||
|
|
||||||
def getNewVersion(self):
|
|
||||||
while self.answer is None:
|
|
||||||
sleep(1)
|
|
||||||
if self.answer == QtWidgets.QMessageBox.Yes:
|
|
||||||
try:
|
|
||||||
MW.modeConvert.emit(-1)
|
|
||||||
MW.progressBarTick.emit('Downloading update')
|
|
||||||
path = urlretrieve('https://kcc.iosphe.re/Windows/KindleComicConverter_win_' +
|
|
||||||
self.newVersion + '.exe', reporthook=self.getNewVersionTick)
|
|
||||||
if self.md5 != md5Checksum(path[0]):
|
|
||||||
raise Exception
|
|
||||||
move(path[0], path[0] + '.exe')
|
|
||||||
MW.hideProgressBar.emit()
|
|
||||||
MW.modeConvert.emit(1)
|
|
||||||
Popen(path[0] + '.exe /SP- /silent /noicons', stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
|
|
||||||
MW.forceShutdown.emit()
|
|
||||||
except Exception:
|
|
||||||
MW.addMessage.emit('Failed to download the update!', 'warning', False)
|
|
||||||
MW.hideProgressBar.emit()
|
|
||||||
MW.modeConvert.emit(1)
|
|
||||||
|
|
||||||
def getNewVersionTick(self, size, blockSize, totalSize):
|
|
||||||
progress = int((size / (totalSize // blockSize)) * 100)
|
|
||||||
if size == 0:
|
|
||||||
MW.progressBarTick.emit('100')
|
|
||||||
if progress > self.barProgress:
|
|
||||||
self.barProgress = progress
|
|
||||||
MW.progressBarTick.emit('tick')
|
|
||||||
|
|
||||||
|
|
||||||
class ProgressThread(QtCore.QThread):
|
class ProgressThread(QtCore.QThread):
|
||||||
@@ -238,45 +207,64 @@ class WorkerThread(QtCore.QThread):
|
|||||||
MW.addTrayMessage.emit('Conversion interrupted.', 'Critical')
|
MW.addTrayMessage.emit('Conversion interrupted.', 'Critical')
|
||||||
MW.modeConvert.emit(1)
|
MW.modeConvert.emit(1)
|
||||||
|
|
||||||
|
# noinspection PyUnboundLocalVariable
|
||||||
def run(self):
|
def run(self):
|
||||||
MW.modeConvert.emit(0)
|
MW.modeConvert.emit(0)
|
||||||
|
|
||||||
parser = comic2ebook.makeParser()
|
parser = comic2ebook.makeParser()
|
||||||
options, _ = parser.parse_args()
|
options = parser.parse_args()
|
||||||
argv = ''
|
argv = ''
|
||||||
currentJobs = []
|
currentJobs = []
|
||||||
|
|
||||||
options.profile = GUI.profiles[str(GUI.deviceBox.currentText())]['Label']
|
options.profile = GUI.profiles[str(GUI.deviceBox.currentText())]['Label']
|
||||||
options.format = str(GUI.formatBox.currentText()).replace('/AZW3', '')
|
gui_current_format = GUI.formats[str(GUI.formatBox.currentText())]['format']
|
||||||
|
options.format = gui_current_format
|
||||||
if GUI.mangaBox.isChecked():
|
if GUI.mangaBox.isChecked():
|
||||||
options.righttoleft = True
|
options.righttoleft = True
|
||||||
if GUI.rotateBox.checkState() == 1:
|
if GUI.rotateBox.checkState() == Qt.CheckState.PartiallyChecked:
|
||||||
options.splitter = 2
|
options.splitter = 2
|
||||||
elif GUI.rotateBox.checkState() == 2:
|
elif GUI.rotateBox.checkState() == Qt.CheckState.Checked:
|
||||||
options.splitter = 1
|
options.splitter = 1
|
||||||
if GUI.qualityBox.checkState() == 1:
|
if GUI.qualityBox.checkState() == Qt.CheckState.PartiallyChecked:
|
||||||
options.autoscale = True
|
options.autoscale = True
|
||||||
elif GUI.qualityBox.checkState() == 2:
|
elif GUI.qualityBox.checkState() == Qt.CheckState.Checked:
|
||||||
options.hq = True
|
options.hq = True
|
||||||
if GUI.webtoonBox.isChecked():
|
if GUI.webtoonBox.isChecked():
|
||||||
options.webtoon = True
|
options.webtoon = True
|
||||||
if GUI.upscaleBox.checkState() == 1:
|
if GUI.upscaleBox.checkState() == Qt.CheckState.PartiallyChecked:
|
||||||
options.stretch = True
|
options.stretch = True
|
||||||
elif GUI.upscaleBox.checkState() == 2:
|
elif GUI.upscaleBox.checkState() == Qt.CheckState.Checked:
|
||||||
options.upscale = True
|
options.upscale = True
|
||||||
if GUI.gammaBox.isChecked() and float(GUI.gammaValue) > 0.09:
|
if GUI.gammaBox.isChecked() and float(GUI.gammaValue) > 0.09:
|
||||||
options.gamma = float(GUI.gammaValue)
|
options.gamma = float(GUI.gammaValue)
|
||||||
if GUI.borderBox.checkState() == 1:
|
options.cropping = GUI.croppingBox.checkState().value
|
||||||
|
if GUI.croppingBox.checkState() != Qt.CheckState.Unchecked:
|
||||||
|
options.croppingp = float(GUI.croppingPowerValue)
|
||||||
|
if GUI.borderBox.checkState() == Qt.CheckState.PartiallyChecked:
|
||||||
options.white_borders = True
|
options.white_borders = True
|
||||||
elif GUI.borderBox.checkState() == 2:
|
elif GUI.borderBox.checkState() == Qt.CheckState.Checked:
|
||||||
options.black_borders = True
|
options.black_borders = True
|
||||||
if GUI.outputSplit.isChecked():
|
if GUI.outputSplit.isChecked():
|
||||||
options.batchsplit = 2
|
options.batchsplit = 2
|
||||||
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.dedupeCoverBox.isChecked():
|
||||||
|
options.dedupecover = True
|
||||||
|
if GUI.mozJpegBox.checkState() == Qt.CheckState.PartiallyChecked:
|
||||||
|
options.forcepng = True
|
||||||
|
elif GUI.mozJpegBox.checkState() == Qt.CheckState.Checked:
|
||||||
|
options.mozjpeg = True
|
||||||
if GUI.currentMode > 2:
|
if GUI.currentMode > 2:
|
||||||
options.customwidth = str(GUI.widthBox.value())
|
options.customwidth = str(GUI.widthBox.value())
|
||||||
options.customheight = str(GUI.heightBox.value())
|
options.customheight = str(GUI.heightBox.value())
|
||||||
|
if GUI.targetDirectory != '':
|
||||||
|
options.output = GUI.targetDirectory
|
||||||
|
|
||||||
for i in range(GUI.jobList.count()):
|
for i in range(GUI.jobList.count()):
|
||||||
# Make sure that we don't consider any system message as job to do
|
# Make sure that we don't consider any system message as job to do
|
||||||
@@ -290,7 +278,7 @@ class WorkerThread(QtCore.QThread):
|
|||||||
return
|
return
|
||||||
self.errors = False
|
self.errors = False
|
||||||
MW.addMessage.emit('<b>Source:</b> ' + job, 'info', False)
|
MW.addMessage.emit('<b>Source:</b> ' + job, 'info', False)
|
||||||
if str(GUI.formatBox.currentText()) == 'CBZ':
|
if gui_current_format == 'CBZ':
|
||||||
MW.addMessage.emit('Creating CBZ files', 'info', False)
|
MW.addMessage.emit('Creating CBZ files', 'info', False)
|
||||||
GUI.progress.content = 'Creating CBZ files'
|
GUI.progress.content = 'Creating CBZ files'
|
||||||
else:
|
else:
|
||||||
@@ -299,8 +287,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:
|
||||||
@@ -341,11 +328,11 @@ class WorkerThread(QtCore.QThread):
|
|||||||
return
|
return
|
||||||
if not self.errors:
|
if not self.errors:
|
||||||
GUI.progress.content = ''
|
GUI.progress.content = ''
|
||||||
if str(GUI.formatBox.currentText()) == 'CBZ':
|
if gui_current_format == 'CBZ':
|
||||||
MW.addMessage.emit('Creating CBZ files... <b>Done!</b>', 'info', True)
|
MW.addMessage.emit('Creating CBZ files... <b>Done!</b>', 'info', True)
|
||||||
else:
|
else:
|
||||||
MW.addMessage.emit('Creating EPUB files... <b>Done!</b>', 'info', True)
|
MW.addMessage.emit('Creating EPUB files... <b>Done!</b>', 'info', True)
|
||||||
if str(GUI.formatBox.currentText()) == 'MOBI/AZW3':
|
if 'MOBI' in gui_current_format:
|
||||||
MW.progressBarTick.emit('Creating MOBI files')
|
MW.progressBarTick.emit('Creating MOBI files')
|
||||||
MW.progressBarTick.emit(str(len(outputPath) * 2 + 1))
|
MW.progressBarTick.emit(str(len(outputPath) * 2 + 1))
|
||||||
MW.progressBarTick.emit('tick')
|
MW.progressBarTick.emit('tick')
|
||||||
@@ -446,7 +433,7 @@ class SystemTrayIcon(QtWidgets.QSystemTrayIcon):
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
if self.isSystemTrayAvailable():
|
if self.isSystemTrayAvailable():
|
||||||
QtWidgets.QSystemTrayIcon.__init__(self, GUI.icons.programIcon, MW)
|
self.setIcon(GUI.icons.programIcon)
|
||||||
self.activated.connect(self.catchClicks)
|
self.activated.connect(self.catchClicks)
|
||||||
|
|
||||||
def catchClicks(self):
|
def catchClicks(self):
|
||||||
@@ -455,7 +442,7 @@ class SystemTrayIcon(QtWidgets.QSystemTrayIcon):
|
|||||||
MW.activateWindow()
|
MW.activateWindow()
|
||||||
|
|
||||||
def addTrayMessage(self, message, icon):
|
def addTrayMessage(self, message, icon):
|
||||||
icon = eval('QtWidgets.QSystemTrayIcon.' + icon)
|
icon = getattr(QtWidgets.QSystemTrayIcon.MessageIcon, icon)
|
||||||
if self.supportsMessages() and not MW.isActiveWindow():
|
if self.supportsMessages() and not MW.isActiveWindow():
|
||||||
self.showMessage('Kindle Comic Converter', message, icon)
|
self.showMessage('Kindle Comic Converter', message, icon)
|
||||||
|
|
||||||
@@ -477,20 +464,12 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
|||||||
if self.needClean:
|
if self.needClean:
|
||||||
self.needClean = False
|
self.needClean = False
|
||||||
GUI.jobList.clear()
|
GUI.jobList.clear()
|
||||||
if self.UnRAR:
|
if self.tar or self.sevenzip:
|
||||||
if self.sevenza:
|
fnames = QtWidgets.QFileDialog.getOpenFileNames(MW, 'Select file', self.lastPath,
|
||||||
fnames = QtWidgets.QFileDialog.getOpenFileNames(MW, 'Select file', self.lastPath,
|
'Comic (*.cbz *.cbr *.cb7 *.zip *.rar *.7z *.pdf);;All (*.*)')
|
||||||
'Comic (*.cbz *.cbr *.cb7 *.zip *.rar *.7z *.pdf)')
|
|
||||||
else:
|
|
||||||
fnames = QtWidgets.QFileDialog.getOpenFileNames(MW, 'Select file', self.lastPath,
|
|
||||||
'Comic (*.cbz *.cbr *.zip *.rar *.pdf)')
|
|
||||||
else:
|
else:
|
||||||
if self.sevenza:
|
fnames = QtWidgets.QFileDialog.getOpenFileNames(MW, 'Select file', self.lastPath,
|
||||||
fnames = QtWidgets.QFileDialog.getOpenFileNames(MW, 'Select file', self.lastPath,
|
'Comic (*.pdf);;All (*.*)')
|
||||||
'Comic (*.cbz *.cb7 *.zip *.7z *.pdf)')
|
|
||||||
else:
|
|
||||||
fnames = QtWidgets.QFileDialog.getOpenFileNames(MW, 'Select file', self.lastPath,
|
|
||||||
'Comic (*.cbz *.zip *.pdf)')
|
|
||||||
for fname in fnames[0]:
|
for fname in fnames[0]:
|
||||||
if fname != '':
|
if fname != '':
|
||||||
if sys.platform.startswith('win'):
|
if sys.platform.startswith('win'):
|
||||||
@@ -500,28 +479,32 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
|||||||
GUI.jobList.scrollToBottom()
|
GUI.jobList.scrollToBottom()
|
||||||
|
|
||||||
def selectFileMetaEditor(self):
|
def selectFileMetaEditor(self):
|
||||||
if self.UnRAR:
|
sname = ''
|
||||||
if self.sevenza:
|
if QtWidgets.QApplication.keyboardModifiers() == QtCore.Qt.ShiftModifier:
|
||||||
|
dname = QtWidgets.QFileDialog.getExistingDirectory(MW, 'Select directory', self.lastPath)
|
||||||
|
if dname != '':
|
||||||
|
sname = os.path.join(dname, 'ComicInfo.xml')
|
||||||
|
if sys.platform.startswith('win'):
|
||||||
|
sname = sname.replace('/', '\\')
|
||||||
|
self.lastPath = os.path.abspath(sname)
|
||||||
|
else:
|
||||||
|
if self.sevenzip:
|
||||||
fname = QtWidgets.QFileDialog.getOpenFileName(MW, 'Select file', self.lastPath,
|
fname = QtWidgets.QFileDialog.getOpenFileName(MW, 'Select file', self.lastPath,
|
||||||
'Comic (*.cbz *.cbr *.cb7)')
|
'Comic (*.cbz *.cbr *.cb7)')
|
||||||
else:
|
else:
|
||||||
fname = QtWidgets.QFileDialog.getOpenFileName(MW, 'Select file', self.lastPath,
|
fname = ['']
|
||||||
'Comic (*.cbz *.cbr)')
|
self.showDialog("Editor is disabled due to a lack of 7z.", 'error')
|
||||||
else:
|
self.addMessage('<a href="https://github.com/ciromattia/kcc#7-zip">Install 7z (link)</a>'
|
||||||
if self.sevenza:
|
' to enable metadata editing.', 'warning')
|
||||||
fname = QtWidgets.QFileDialog.getOpenFileName(MW, 'Select file', self.lastPath,
|
if fname[0] != '':
|
||||||
'Comic (*.cbz *.cb7)')
|
if sys.platform.startswith('win'):
|
||||||
else:
|
sname = fname[0].replace('/', '\\')
|
||||||
fname = QtWidgets.QFileDialog.getOpenFileName(MW, 'Select file', self.lastPath,
|
else:
|
||||||
'Comic (*.cbz)')
|
sname = fname[0]
|
||||||
if fname[0] != '':
|
self.lastPath = os.path.abspath(os.path.join(sname, os.pardir))
|
||||||
if sys.platform.startswith('win'):
|
if sname != '':
|
||||||
fname = fname[0].replace('/', '\\')
|
|
||||||
else:
|
|
||||||
fname = fname[0]
|
|
||||||
self.lastPath = os.path.abspath(os.path.join(fname, os.pardir))
|
|
||||||
try:
|
try:
|
||||||
self.editor.loadData(fname)
|
self.editor.loadData(sname)
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
_, _, traceback = sys.exc_info()
|
_, _, traceback = sys.exc_info()
|
||||||
GUI.sentry.captureException()
|
GUI.sentry.captureException()
|
||||||
@@ -571,7 +554,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
|||||||
self.conversionAlive = False
|
self.conversionAlive = False
|
||||||
self.worker.sync()
|
self.worker.sync()
|
||||||
icon = QtGui.QIcon()
|
icon = QtGui.QIcon()
|
||||||
icon.addPixmap(QtGui.QPixmap(":/Other/icons/convert.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
icon.addPixmap(QtGui.QPixmap(":/Other/icons/convert.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
||||||
GUI.convertButton.setIcon(icon)
|
GUI.convertButton.setIcon(icon)
|
||||||
GUI.convertButton.setText('Convert')
|
GUI.convertButton.setText('Convert')
|
||||||
GUI.centralWidget.setAcceptDrops(True)
|
GUI.centralWidget.setAcceptDrops(True)
|
||||||
@@ -579,7 +562,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
|||||||
self.conversionAlive = True
|
self.conversionAlive = True
|
||||||
self.worker.sync()
|
self.worker.sync()
|
||||||
icon = QtGui.QIcon()
|
icon = QtGui.QIcon()
|
||||||
icon.addPixmap(QtGui.QPixmap(":/Other/icons/clear.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
icon.addPixmap(QtGui.QPixmap(":/Other/icons/clear.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
||||||
GUI.convertButton.setIcon(icon)
|
GUI.convertButton.setIcon(icon)
|
||||||
GUI.convertButton.setText('Abort')
|
GUI.convertButton.setText('Abort')
|
||||||
GUI.centralWidget.setAcceptDrops(False)
|
GUI.centralWidget.setAcceptDrops(False)
|
||||||
@@ -597,6 +580,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)
|
||||||
@@ -618,9 +608,9 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
|||||||
def togglequalityBox(self, value):
|
def togglequalityBox(self, value):
|
||||||
profile = GUI.profiles[str(GUI.deviceBox.currentText())]
|
profile = GUI.profiles[str(GUI.deviceBox.currentText())]
|
||||||
if value == 2:
|
if value == 2:
|
||||||
if profile['Label'] in ['KV']:
|
if profile['Label'] in ['KV', 'KO']:
|
||||||
self.addMessage('This option is intended for older Kindle models.', 'warning')
|
self.addMessage('This option is intended for older Kindle models.', 'warning')
|
||||||
self.addMessage('It will not increase quality on a device with 300 ppi screen.', 'warning')
|
self.addMessage('On this device, quality improvement will be negligible.', 'warning')
|
||||||
GUI.upscaleBox.setEnabled(False)
|
GUI.upscaleBox.setEnabled(False)
|
||||||
GUI.upscaleBox.setChecked(True)
|
GUI.upscaleBox.setChecked(True)
|
||||||
else:
|
else:
|
||||||
@@ -637,6 +627,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']:
|
||||||
@@ -645,27 +642,29 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
|||||||
self.modeChange(2)
|
self.modeChange(2)
|
||||||
else:
|
else:
|
||||||
self.modeChange(1)
|
self.modeChange(1)
|
||||||
|
GUI.colorBox.setChecked(profile['ForceColor'])
|
||||||
self.changeFormat()
|
self.changeFormat()
|
||||||
GUI.gammaSlider.setValue(0)
|
GUI.gammaSlider.setValue(0)
|
||||||
self.changeGamma(0)
|
self.changeGamma(0)
|
||||||
if not GUI.webtoonBox.isChecked():
|
if not GUI.webtoonBox.isChecked():
|
||||||
GUI.qualityBox.setEnabled(profile['PVOptions'])
|
GUI.qualityBox.setEnabled(profile['PVOptions'])
|
||||||
GUI.upscaleBox.setChecked(profile['DefaultUpscale'])
|
GUI.upscaleBox.setChecked(profile['DefaultUpscale'])
|
||||||
|
GUI.mangaBox.setChecked(True)
|
||||||
if not profile['PVOptions']:
|
if not profile['PVOptions']:
|
||||||
GUI.qualityBox.setChecked(False)
|
GUI.qualityBox.setChecked(False)
|
||||||
if str(GUI.deviceBox.currentText()) == 'Other':
|
if str(GUI.deviceBox.currentText()) == 'Other':
|
||||||
self.addMessage('<a href="https://github.com/ciromattia/kcc/wiki/NonKindle-devices">'
|
self.addMessage('<a href="https://github.com/ciromattia/kcc/wiki/NonKindle-devices">'
|
||||||
'List of supported Non-Kindle devices.</a>', 'info')
|
'List of supported Non-Kindle devices.</a>', 'info')
|
||||||
|
|
||||||
def changeFormat(self, outputFormat=None):
|
def changeFormat(self, outputformat=None):
|
||||||
profile = GUI.profiles[str(GUI.deviceBox.currentText())]
|
profile = GUI.profiles[str(GUI.deviceBox.currentText())]
|
||||||
if outputFormat is not None:
|
if outputformat is not None:
|
||||||
GUI.formatBox.setCurrentIndex(outputFormat)
|
GUI.formatBox.setCurrentIndex(outputformat)
|
||||||
else:
|
else:
|
||||||
GUI.formatBox.setCurrentIndex(profile['DefaultFormat'])
|
GUI.formatBox.setCurrentIndex(profile['DefaultFormat'])
|
||||||
if not GUI.webtoonBox.isChecked():
|
if not GUI.webtoonBox.isChecked():
|
||||||
GUI.qualityBox.setEnabled(profile['PVOptions'])
|
GUI.qualityBox.setEnabled(profile['PVOptions'])
|
||||||
if str(GUI.formatBox.currentText()) == 'MOBI/AZW3':
|
if GUI.formats[str(GUI.formatBox.currentText())]['format'] == 'MOBI':
|
||||||
GUI.outputSplit.setEnabled(True)
|
GUI.outputSplit.setEnabled(True)
|
||||||
else:
|
else:
|
||||||
GUI.outputSplit.setEnabled(False)
|
GUI.outputSplit.setEnabled(False)
|
||||||
@@ -678,7 +677,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
|||||||
|
|
||||||
def addMessage(self, message, icon, replace=False):
|
def addMessage(self, message, icon, replace=False):
|
||||||
if icon != '':
|
if icon != '':
|
||||||
icon = eval('self.icons.' + icon)
|
icon = getattr(self.icons, icon)
|
||||||
item = QtWidgets.QListWidgetItem(icon, ' ' + self.stripTags(message))
|
item = QtWidgets.QListWidgetItem(icon, ' ' + self.stripTags(message))
|
||||||
else:
|
else:
|
||||||
item = QtWidgets.QListWidgetItem(' ' + self.stripTags(message))
|
item = QtWidgets.QListWidgetItem(' ' + self.stripTags(message))
|
||||||
@@ -696,11 +695,11 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
|||||||
|
|
||||||
def showDialog(self, message, kind):
|
def showDialog(self, message, kind):
|
||||||
if kind == 'error':
|
if kind == 'error':
|
||||||
QtWidgets.QMessageBox.critical(MW, 'KCC - Error', message, QtWidgets.QMessageBox.Ok)
|
QtWidgets.QMessageBox.critical(MW, 'KCC - Error', message, QtWidgets.QMessageBox.StandardButton.Ok)
|
||||||
elif kind == 'question':
|
elif kind == 'question':
|
||||||
GUI.versionCheck.setAnswer(QtWidgets.QMessageBox.question(MW, 'KCC - Question', message,
|
GUI.versionCheck.setAnswer(QtWidgets.QMessageBox.question(MW, 'KCC - Question', message,
|
||||||
QtWidgets.QMessageBox.Yes,
|
QtWidgets.QMessageBox.Yes,
|
||||||
QtWidgets.QMessageBox.No))
|
QtWidgets.QMessageBox.No))
|
||||||
|
|
||||||
def updateProgressbar(self, command):
|
def updateProgressbar(self, command):
|
||||||
if command == 'tick':
|
if command == 'tick':
|
||||||
@@ -724,7 +723,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
|||||||
self.conversionAlive = False
|
self.conversionAlive = False
|
||||||
self.worker.sync()
|
self.worker.sync()
|
||||||
else:
|
else:
|
||||||
if QtWidgets.QApplication.keyboardModifiers() == QtCore.Qt.ShiftModifier:
|
if QtWidgets.QApplication.keyboardModifiers() == QtCore.Qt.KeyboardModifier.ShiftModifier:
|
||||||
dname = QtWidgets.QFileDialog.getExistingDirectory(MW, 'Select output directory', self.lastPath)
|
dname = QtWidgets.QFileDialog.getExistingDirectory(MW, 'Select output directory', self.lastPath)
|
||||||
if dname != '':
|
if dname != '':
|
||||||
if sys.platform.startswith('win'):
|
if sys.platform.startswith('win'):
|
||||||
@@ -747,22 +746,21 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
|||||||
self.addMessage('Target resolution is not set!', 'error')
|
self.addMessage('Target resolution is not set!', 'error')
|
||||||
self.needClean = True
|
self.needClean = True
|
||||||
return
|
return
|
||||||
if str(GUI.formatBox.currentText()) == 'MOBI/AZW3' and not self.kindleGen:
|
if 'MOBI' in GUI.formats[str(GUI.formatBox.currentText())]['format'] and not self.kindleGen:
|
||||||
self.detectKindleGen()
|
self.detectKindleGen()
|
||||||
if not self.kindleGen:
|
if not self.kindleGen:
|
||||||
GUI.jobList.clear()
|
GUI.jobList.clear()
|
||||||
self.addMessage('Cannot find <a href="http://www.amazon.com/gp/feature.html?ie=UTF8&docId='
|
self.display_kindlegen_missing()
|
||||||
'1000765211"><b>KindleGen</b></a>! MOBI conversion is unavailable!', 'error')
|
|
||||||
if sys.platform.startswith('win'):
|
|
||||||
self.addMessage('Download it and place EXE in KCC directory.', 'error')
|
|
||||||
elif sys.platform.startswith('darwin'):
|
|
||||||
self.addMessage('Install it using <a href="http://brew.sh/">Brew</a>.', 'error')
|
|
||||||
else:
|
|
||||||
self.addMessage('Download it and place executable in /usr/local/bin directory.', 'error')
|
|
||||||
self.needClean = True
|
self.needClean = True
|
||||||
return
|
return
|
||||||
self.worker.start()
|
self.worker.start()
|
||||||
|
|
||||||
|
def display_kindlegen_missing(self):
|
||||||
|
self.addMessage(
|
||||||
|
'<a href="https://github.com/ciromattia/kcc#kindlegen"><b>Install KindleGen (link)</b></a> to enable MOBI conversion for Kindles!',
|
||||||
|
'error'
|
||||||
|
)
|
||||||
|
|
||||||
def saveSettings(self, event):
|
def saveSettings(self, event):
|
||||||
if self.conversionAlive:
|
if self.conversionAlive:
|
||||||
GUI.convertButton.setEnabled(False)
|
GUI.convertButton.setEnabled(False)
|
||||||
@@ -778,17 +776,24 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
|||||||
self.settings.setValue('currentFormat', GUI.formatBox.currentIndex())
|
self.settings.setValue('currentFormat', GUI.formatBox.currentIndex())
|
||||||
self.settings.setValue('startNumber', self.startNumber + 1)
|
self.settings.setValue('startNumber', self.startNumber + 1)
|
||||||
self.settings.setValue('windowSize', str(MW.size().width()) + 'x' + str(MW.size().height()))
|
self.settings.setValue('windowSize', str(MW.size().width()) + 'x' + str(MW.size().height()))
|
||||||
self.settings.setValue('options', {'mangaBox': GUI.mangaBox.checkState(),
|
self.settings.setValue('options', {'mangaBox': GUI.mangaBox.checkState().value,
|
||||||
'rotateBox': GUI.rotateBox.checkState(),
|
'rotateBox': GUI.rotateBox.checkState().value,
|
||||||
'qualityBox': GUI.qualityBox.checkState(),
|
'qualityBox': GUI.qualityBox.checkState().value,
|
||||||
'gammaBox': GUI.gammaBox.checkState(),
|
'gammaBox': GUI.gammaBox.checkState().value,
|
||||||
'upscaleBox': GUI.upscaleBox.checkState(),
|
'croppingBox': GUI.croppingBox.checkState().value,
|
||||||
'borderBox': GUI.borderBox.checkState(),
|
'croppingPowerSlider': float(self.croppingPowerValue) * 100,
|
||||||
'webtoonBox': GUI.webtoonBox.checkState(),
|
'upscaleBox': GUI.upscaleBox.checkState().value,
|
||||||
'outputSplit': GUI.outputSplit.checkState(),
|
'borderBox': GUI.borderBox.checkState().value,
|
||||||
'colorBox': GUI.colorBox.checkState(),
|
'webtoonBox': GUI.webtoonBox.checkState().value,
|
||||||
|
'outputSplit': GUI.outputSplit.checkState().value,
|
||||||
|
'colorBox': GUI.colorBox.checkState().value,
|
||||||
|
'disableProcessingBox': GUI.disableProcessingBox.checkState().value,
|
||||||
|
'mozJpegBox': GUI.mozJpegBox.checkState().value,
|
||||||
'widthBox': GUI.widthBox.value(),
|
'widthBox': GUI.widthBox.value(),
|
||||||
'heightBox': GUI.heightBox.value(),
|
'heightBox': GUI.heightBox.value(),
|
||||||
|
'deleteBox': GUI.deleteBox.checkState().value,
|
||||||
|
'dedupeCoverBox': GUI.dedupeCoverBox.checkState().value,
|
||||||
|
'maximizeStrips': GUI.maximizeStrips.checkState().value,
|
||||||
'gammaSlider': float(self.gammaValue) * 100})
|
'gammaSlider': float(self.gammaValue) * 100})
|
||||||
self.settings.sync()
|
self.settings.sync()
|
||||||
self.tray.hide()
|
self.tray.hide()
|
||||||
@@ -802,16 +807,9 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
|||||||
if self.needClean:
|
if self.needClean:
|
||||||
self.needClean = False
|
self.needClean = False
|
||||||
GUI.jobList.clear()
|
GUI.jobList.clear()
|
||||||
if self.UnRAR:
|
formats = ['.pdf']
|
||||||
if self.sevenza:
|
if self.tar or self.sevenzip:
|
||||||
formats = ['.cbz', '.cbr', '.cb7', '.zip', '.rar', '.7z', '.pdf']
|
formats.extend(['.cb7', '.7z', '.cbz', '.zip', '.cbr', '.rar'])
|
||||||
else:
|
|
||||||
formats = ['.cbz', '.cbr', '.zip', '.rar', '.pdf']
|
|
||||||
else:
|
|
||||||
if self.sevenza:
|
|
||||||
formats = ['.cbz', '.cb7', '.zip', '.7z', '.pdf']
|
|
||||||
else:
|
|
||||||
formats = ['.cbz', '.zip', '.pdf']
|
|
||||||
if os.path.isdir(message):
|
if os.path.isdir(message):
|
||||||
GUI.jobList.addItem(message)
|
GUI.jobList.addItem(message)
|
||||||
GUI.jobList.scrollToBottom()
|
GUI.jobList.scrollToBottom()
|
||||||
@@ -821,7 +819,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
|||||||
GUI.jobList.addItem(message)
|
GUI.jobList.addItem(message)
|
||||||
GUI.jobList.scrollToBottom()
|
GUI.jobList.scrollToBottom()
|
||||||
else:
|
else:
|
||||||
self.addMessage('This file type is unsupported!', 'error')
|
self.addMessage('Unsupported file type for ' + message, 'error')
|
||||||
|
|
||||||
def dragAndDrop(self, e):
|
def dragAndDrop(self, e):
|
||||||
e.accept()
|
e.accept()
|
||||||
@@ -847,46 +845,37 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
|||||||
os.chmod('/usr/local/bin/kindlegen', 0o755)
|
os.chmod('/usr/local/bin/kindlegen', 0o755)
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
kindleGenExitCode = Popen('kindlegen -locale en', stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
|
try:
|
||||||
if kindleGenExitCode.wait() == 0:
|
versionCheck = subprocess_run_silent(['kindlegen', '-locale', 'en'], stdout=PIPE, stderr=STDOUT, encoding='UTF-8')
|
||||||
self.kindleGen = True
|
self.kindleGen = True
|
||||||
versionCheck = Popen('kindlegen -locale en', stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
|
for line in versionCheck.stdout.splitlines():
|
||||||
for line in versionCheck.stdout:
|
|
||||||
line = line.decode("utf-8")
|
|
||||||
if 'Amazon kindlegen' in line:
|
if 'Amazon kindlegen' in line:
|
||||||
versionCheck = line.split('V')[1].split(' ')[0]
|
versionCheck = line.split('V')[1].split(' ')[0]
|
||||||
if StrictVersion(versionCheck) < StrictVersion('2.9'):
|
if StrictVersion(versionCheck) < StrictVersion('2.9'):
|
||||||
self.addMessage('Your <a href="http://www.amazon.com/gp/feature.html?ie=UTF8&docId='
|
self.addMessage('Your <a href="https://www.amazon.com/b?node=23496309011">KindleGen</a>'
|
||||||
'1000765211">KindleGen</a> is outdated! MOBI conversion might fail.', 'warning')
|
' is outdated! MOBI conversion might fail.', 'warning')
|
||||||
break
|
break
|
||||||
else:
|
except FileNotFoundError:
|
||||||
self.kindleGen = False
|
self.kindleGen = False
|
||||||
if startup:
|
if startup:
|
||||||
self.addMessage('Cannot find <a href="http://www.amazon.com/gp/feature.html?ie=UTF8&docId=1000765211">'
|
self.display_kindlegen_missing()
|
||||||
'<b>KindleGen</b></a>! MOBI conversion will be unavailable!', 'error')
|
|
||||||
if sys.platform.startswith('win'):
|
|
||||||
self.addMessage('Download it and place EXE in KCC directory.', 'error')
|
|
||||||
elif sys.platform.startswith('darwin'):
|
|
||||||
self.addMessage('Install it using <a href="http://brew.sh/">Brew</a>.', 'error')
|
|
||||||
else:
|
|
||||||
self.addMessage('Download it and place executable in /usr/local/bin directory.', 'error')
|
|
||||||
|
|
||||||
def __init__(self, KCCAplication, KCCWindow):
|
def __init__(self, kccapp, kccwindow):
|
||||||
global APP, MW, GUI
|
global APP, MW, GUI
|
||||||
APP = KCCAplication
|
APP = kccapp
|
||||||
MW = KCCWindow
|
MW = kccwindow
|
||||||
GUI = self
|
GUI = self
|
||||||
self.setupUi(MW)
|
self.setupUi(MW)
|
||||||
self.editor = KCCGUI_MetaEditor()
|
self.editor = KCCGUI_MetaEditor()
|
||||||
self.icons = Icons()
|
self.icons = Icons()
|
||||||
self.settings = QtCore.QSettings('KindleComicConverter', 'KindleComicConverter')
|
self.settings = QtCore.QSettings('ciromattia', 'kcc')
|
||||||
self.settingsVersion = self.settings.value('settingsVersion', '', type=str)
|
self.settingsVersion = self.settings.value('settingsVersion', '', type=str)
|
||||||
self.lastPath = self.settings.value('lastPath', '', type=str)
|
self.lastPath = self.settings.value('lastPath', '', type=str)
|
||||||
self.lastDevice = self.settings.value('lastDevice', 0, type=int)
|
self.lastDevice = self.settings.value('lastDevice', 0, type=int)
|
||||||
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()
|
||||||
@@ -895,10 +884,12 @@ 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__)
|
||||||
if sys.platform.startswith('win'):
|
if sys.platform.startswith('win'):
|
||||||
|
# noinspection PyUnresolvedReferences
|
||||||
from psutil import BELOW_NORMAL_PRIORITY_CLASS
|
from psutil import BELOW_NORMAL_PRIORITY_CLASS
|
||||||
self.p = Process(os.getpid())
|
self.p = Process(os.getpid())
|
||||||
self.p.nice(BELOW_NORMAL_PRIORITY_CLASS)
|
self.p.nice(BELOW_NORMAL_PRIORITY_CLASS)
|
||||||
@@ -910,73 +901,128 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
|||||||
elif sys.platform.startswith('darwin'):
|
elif sys.platform.startswith('darwin'):
|
||||||
for element in ['editorButton', 'wikiButton', 'directoryButton', 'clearButton', 'fileButton', 'deviceBox',
|
for element in ['editorButton', 'wikiButton', 'directoryButton', 'clearButton', 'fileButton', 'deviceBox',
|
||||||
'convertButton', 'formatBox']:
|
'convertButton', 'formatBox']:
|
||||||
eval('GUI.' + element).setMinimumSize(QtCore.QSize(0, 0))
|
getattr(GUI, element).setMinimumSize(QtCore.QSize(0, 0))
|
||||||
GUI.gridLayout.setContentsMargins(-1, -1, -1, -1)
|
GUI.gridLayout.setContentsMargins(-1, -1, -1, -1)
|
||||||
for element in ['gridLayout_2', 'gridLayout_3', 'gridLayout_4', 'horizontalLayout', 'horizontalLayout_2']:
|
for element in ['gridLayout_2', 'gridLayout_3', 'gridLayout_4', 'horizontalLayout', 'horizontalLayout_2']:
|
||||||
eval('GUI.' + element).setContentsMargins(-1, 0, -1, 0)
|
getattr(GUI, element).setContentsMargins(-1, 0, -1, 0)
|
||||||
if self.windowSize == '0x0':
|
if self.windowSize == '0x0':
|
||||||
MW.resize(500, 500)
|
MW.resize(500, 500)
|
||||||
|
|
||||||
|
self.formats = { # text, icon, data/option_format
|
||||||
|
"MOBI/AZW3": {'icon': 'MOBI', 'format': 'MOBI'},
|
||||||
|
"EPUB": {'icon': 'EPUB', 'format': 'EPUB'},
|
||||||
|
"CBZ": {'icon': 'CBZ', 'format': 'CBZ'},
|
||||||
|
"EPUB (Calibre KFX)": {'icon': 'EPUB', 'format': 'KFX'},
|
||||||
|
"MOBI + EPUB": {'icon': 'MOBI', 'format': 'MOBI+EPUB'},
|
||||||
|
"EPUB (200MB limit)": {'icon': 'EPUB', 'format': 'EPUB-200MB'}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
self.profiles = {
|
self.profiles = {
|
||||||
"Kindle Oasis": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
|
"Kindle Oasis 9/10": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
|
||||||
'DefaultUpscale': True, 'Label': 'KV'},
|
'DefaultUpscale': True, 'ForceColor': False, 'Label': 'KO'},
|
||||||
|
"Kindle Oasis 8": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
|
||||||
|
'DefaultUpscale': True, 'ForceColor': False, 'Label': 'KV'},
|
||||||
"Kindle Voyage": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
|
"Kindle Voyage": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
|
||||||
'DefaultUpscale': True, 'Label': 'KV'},
|
'DefaultUpscale': True, 'ForceColor': False, 'Label': 'KV'},
|
||||||
"Kindle PW 3": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
|
"Kindle Scribe": {
|
||||||
'DefaultUpscale': True, 'Label': 'KV'},
|
'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': False, 'ForceColor': False, 'Label': 'KS',
|
||||||
"Kindle PW 1/2": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
|
},
|
||||||
'DefaultUpscale': False, 'Label': 'KPW'},
|
"Kindle 11": {
|
||||||
"Kindle": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
|
'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': True, 'ForceColor': False, 'Label': 'K11',
|
||||||
'DefaultUpscale': False, 'Label': 'K578'},
|
},
|
||||||
"Kindle DX/DXG": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 2,
|
"Kindle PW 11": {
|
||||||
'DefaultUpscale': False, 'Label': 'KDX'},
|
'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': True, 'ForceColor': False, 'Label': 'KPW5',
|
||||||
|
},
|
||||||
|
"Kindle PW 7/10": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
|
||||||
|
'DefaultUpscale': True, 'ForceColor': False, 'Label': 'KV'},
|
||||||
|
"Kindle PW 5/6": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
|
||||||
|
'DefaultUpscale': False, 'ForceColor': False, 'Label': 'KPW'},
|
||||||
|
"Kindle 4/5/7/8/10": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
|
||||||
|
'DefaultUpscale': False, 'ForceColor': False, 'Label': 'K578'},
|
||||||
|
"Kindle DX": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 2,
|
||||||
|
'DefaultUpscale': False, 'ForceColor': False, 'Label': 'KDX'},
|
||||||
"Kobo Mini/Touch": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1,
|
"Kobo Mini/Touch": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1,
|
||||||
'DefaultUpscale': False, 'Label': 'KoMT'},
|
'DefaultUpscale': False, 'ForceColor': False, 'Label': 'KoMT'},
|
||||||
"Kobo Glo": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1,
|
"Kobo Glo": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1,
|
||||||
'DefaultUpscale': False, 'Label': 'KoG'},
|
'DefaultUpscale': False, 'ForceColor': False, 'Label': 'KoG'},
|
||||||
"Kobo Glo HD": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1,
|
"Kobo Glo HD": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1,
|
||||||
'DefaultUpscale': False, 'Label': 'KoGHD'},
|
'DefaultUpscale': False, 'ForceColor': False, 'Label': 'KoGHD'},
|
||||||
"Kobo Aura": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1,
|
"Kobo Aura": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1,
|
||||||
'DefaultUpscale': False, 'Label': 'KoA'},
|
'DefaultUpscale': False, 'ForceColor': False, 'Label': 'KoA'},
|
||||||
"Kobo Aura HD": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1,
|
"Kobo Aura HD": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1,
|
||||||
'DefaultUpscale': True, 'Label': 'KoAHD'},
|
'DefaultUpscale': True, 'ForceColor': False, 'Label': 'KoAHD'},
|
||||||
"Kobo Aura H2O": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1,
|
"Kobo Aura H2O": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1,
|
||||||
'DefaultUpscale': True, 'Label': 'KoAH2O'},
|
'DefaultUpscale': True, 'ForceColor': False, 'Label': 'KoAH2O'},
|
||||||
"Kobo Aura ONE": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1,
|
"Kobo Aura ONE": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1,
|
||||||
'DefaultUpscale': True, 'Label': 'KoAO'},
|
'DefaultUpscale': True, 'ForceColor': False, 'Label': 'KoAO'},
|
||||||
"Other": {'PVOptions': False, 'ForceExpert': True, 'DefaultFormat': 1,
|
"Kobo Clara HD": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1,
|
||||||
'DefaultUpscale': False, 'Label': 'OTHER'},
|
'DefaultUpscale': True, 'ForceColor': False, 'Label': 'KoC'},
|
||||||
|
"Kobo Libra H2O": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1,
|
||||||
|
'DefaultUpscale': True, 'ForceColor': False, 'Label': 'KoL'},
|
||||||
|
"Kobo Forma": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1,
|
||||||
|
'DefaultUpscale': True, 'ForceColor': False, 'Label': 'KoF'},
|
||||||
"Kindle 1": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 0,
|
"Kindle 1": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 0,
|
||||||
'DefaultUpscale': False, 'Label': 'K1'},
|
'DefaultUpscale': False, 'ForceColor': False, 'Label': 'K1'},
|
||||||
"Kindle 2": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 0,
|
"Kindle 2": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 0,
|
||||||
'DefaultUpscale': False, 'Label': 'K2'},
|
'DefaultUpscale': False, 'ForceColor': False, 'Label': 'K2'},
|
||||||
"Kindle Keyboard": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 0,
|
"Kindle Keyboard": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 0,
|
||||||
'DefaultUpscale': False, 'Label': 'K34'},
|
'DefaultUpscale': False, 'ForceColor': False, 'Label': 'K34'},
|
||||||
"Kindle Touch": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 0,
|
"Kindle Touch": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 0,
|
||||||
'DefaultUpscale': False, 'Label': 'K34'},
|
'DefaultUpscale': False, 'ForceColor': False, 'Label': 'K34'},
|
||||||
|
"Kobo Nia": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, 'DefaultUpscale': True, 'ForceColor': False,
|
||||||
|
'Label': 'KoN'},
|
||||||
|
"Kobo Clara 2E": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, 'DefaultUpscale': True, 'ForceColor': False,
|
||||||
|
'Label': 'KoC'},
|
||||||
|
"Kobo Clara Colour": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, 'DefaultUpscale': True, 'ForceColor': True,
|
||||||
|
'Label': 'KoCC'},
|
||||||
|
"Kobo Libra 2": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, 'DefaultUpscale': True, 'ForceColor': False,
|
||||||
|
'Label': 'KoL'},
|
||||||
|
"Kobo Libra Colour": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, 'DefaultUpscale': True, 'ForceColor': True,
|
||||||
|
'Label': 'KoLC'},
|
||||||
|
"Kobo Sage": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, 'DefaultUpscale': True, 'ForceColor': False,
|
||||||
|
'Label': 'KoS'},
|
||||||
|
"Kobo Elipsa": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, 'DefaultUpscale': True, 'ForceColor': False,
|
||||||
|
'Label': 'KoE'},
|
||||||
|
"Other": {'PVOptions': False, 'ForceExpert': True, 'DefaultFormat': 1, 'DefaultUpscale': False, 'ForceColor': False,
|
||||||
|
'Label': 'OTHER'},
|
||||||
}
|
}
|
||||||
profilesGUI = [
|
profilesGUI = [
|
||||||
"Kindle Oasis",
|
"Kindle Scribe",
|
||||||
"Kindle Voyage",
|
"Kindle 11",
|
||||||
"Kindle PW 3",
|
"Kindle PW 11",
|
||||||
"Kindle PW 1/2",
|
"Kindle Oasis 9/10",
|
||||||
"Kindle",
|
|
||||||
"Separator",
|
"Separator",
|
||||||
"Kobo Aura ONE",
|
"Kobo Clara 2E",
|
||||||
"Kobo Aura H2O",
|
"Kobo Clara Colour",
|
||||||
"Kobo Aura HD",
|
"Kobo Sage",
|
||||||
"Kobo Aura",
|
"Kobo Libra 2",
|
||||||
|
"Kobo Libra Colour",
|
||||||
|
"Kobo Elipsa",
|
||||||
|
"Kobo Nia",
|
||||||
"Separator",
|
"Separator",
|
||||||
"Other",
|
"Other",
|
||||||
"Separator",
|
"Separator",
|
||||||
|
"Kindle Oasis 8",
|
||||||
|
"Kindle PW 7/10",
|
||||||
|
"Kindle Voyage",
|
||||||
|
"Kindle PW 5/6",
|
||||||
|
"Kindle 4/5/7/8/10",
|
||||||
"Kindle Touch",
|
"Kindle Touch",
|
||||||
"Kindle Keyboard",
|
"Kindle Keyboard",
|
||||||
"Kindle DX/DXG",
|
"Kindle DX",
|
||||||
"Kindle 2",
|
"Kindle 2",
|
||||||
"Kindle 1",
|
"Kindle 1",
|
||||||
"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",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -984,7 +1030,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
|||||||
'com/ciromattia/kcc/blob/master/README.md#issues--new-features--donations">DO'
|
'com/ciromattia/kcc/blob/master/README.md#issues--new-features--donations">DO'
|
||||||
'NATE</a> - <a href="http://www.mobileread.com/forums/showthread.php?t=207461'
|
'NATE</a> - <a href="http://www.mobileread.com/forums/showthread.php?t=207461'
|
||||||
'">FORUM</a></b>')
|
'">FORUM</a></b>')
|
||||||
statusBarLabel.setAlignment(QtCore.Qt.AlignCenter)
|
statusBarLabel.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
|
||||||
statusBarLabel.setOpenExternalLinks(True)
|
statusBarLabel.setOpenExternalLinks(True)
|
||||||
GUI.statusBar.addPermanentWidget(statusBarLabel, 1)
|
GUI.statusBar.addPermanentWidget(statusBarLabel, 1)
|
||||||
|
|
||||||
@@ -994,22 +1040,19 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
|||||||
self.addMessage('Since you are a new user of <b>KCC</b> please see few '
|
self.addMessage('Since you are a new user of <b>KCC</b> please see few '
|
||||||
'<a href="https://github.com/ciromattia/kcc/wiki/Important-tips">important tips</a>.',
|
'<a href="https://github.com/ciromattia/kcc/wiki/Important-tips">important tips</a>.',
|
||||||
'info')
|
'info')
|
||||||
rarExitCode = Popen('unrar', stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
|
try:
|
||||||
rarExitCode = rarExitCode.wait()
|
subprocess_run_silent(['tar'], stdout=PIPE, stderr=STDOUT)
|
||||||
if rarExitCode == 0 or rarExitCode == 7:
|
self.tar = True
|
||||||
self.UnRAR = True
|
except FileNotFoundError:
|
||||||
else:
|
self.tar = False
|
||||||
self.UnRAR = False
|
try:
|
||||||
self.addMessage('Cannot find <a href="http://www.rarlab.com/rar_add.htm">UnRAR</a>!'
|
subprocess_run_silent(['7z'], stdout=PIPE, stderr=STDOUT)
|
||||||
' Processing of CBR/RAR files will be disabled.', 'warning')
|
self.sevenzip = True
|
||||||
sevenzaExitCode = Popen('7za', stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
|
except FileNotFoundError:
|
||||||
sevenzaExitCode = sevenzaExitCode.wait()
|
self.sevenzip = False
|
||||||
if sevenzaExitCode == 0 or sevenzaExitCode == 7:
|
if not self.tar:
|
||||||
self.sevenza = True
|
self.addMessage('<a href="https://github.com/ciromattia/kcc#7-zip">Install 7z (link)</a>'
|
||||||
else:
|
' to enable CBZ/CBR/ZIP/etc processing.', 'warning')
|
||||||
self.sevenza = False
|
|
||||||
self.addMessage('Cannot find <a href="http://www.7-zip.org/download.html">7za</a>!'
|
|
||||||
' Processing of CB7/7Z files will be disabled.', 'warning')
|
|
||||||
self.detectKindleGen(True)
|
self.detectKindleGen(True)
|
||||||
|
|
||||||
APP.messageFromOtherInstance.connect(self.handleMessage)
|
APP.messageFromOtherInstance.connect(self.handleMessage)
|
||||||
@@ -1021,6 +1064,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)
|
||||||
@@ -1048,8 +1093,8 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
|||||||
GUI.deviceBox.addItem(self.icons.deviceKobo, profile)
|
GUI.deviceBox.addItem(self.icons.deviceKobo, profile)
|
||||||
else:
|
else:
|
||||||
GUI.deviceBox.addItem(self.icons.deviceKindle, profile)
|
GUI.deviceBox.addItem(self.icons.deviceKindle, profile)
|
||||||
for f in ['MOBI/AZW3', 'EPUB', 'CBZ']:
|
for f in self.formats:
|
||||||
GUI.formatBox.addItem(eval('self.icons.' + f.replace('/AZW3', '') + 'Format'), f)
|
GUI.formatBox.addItem(getattr(self.icons, self.formats[f]['icon'] + 'Format'), f)
|
||||||
if self.lastDevice > GUI.deviceBox.count():
|
if self.lastDevice > GUI.deviceBox.count():
|
||||||
self.lastDevice = 0
|
self.lastDevice = 0
|
||||||
if profilesGUI[self.lastDevice] == "Separator":
|
if profilesGUI[self.lastDevice] == "Separator":
|
||||||
@@ -1069,16 +1114,26 @@ 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 getattr(GUI, option).isEnabled():
|
||||||
eval('GUI.' + str(option)).setCheckState(self.options[option])
|
getattr(GUI, option).setCheckState(Qt.CheckState(self.options[option]))
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
pass
|
pass
|
||||||
self.worker.sync()
|
self.worker.sync()
|
||||||
self.versionCheck.start()
|
self.versionCheck.start()
|
||||||
self.tray.show()
|
self.tray.show()
|
||||||
|
|
||||||
|
# Cleanup unfinished conversion
|
||||||
|
for root, dirs, _ in walkLevel(gettempdir(), 0):
|
||||||
|
for tempdir in dirs:
|
||||||
|
if tempdir.startswith('KCC-'):
|
||||||
|
rmtree(os.path.join(root, tempdir), True)
|
||||||
|
|
||||||
if self.windowSize != '0x0':
|
if self.windowSize != '0x0':
|
||||||
x, y = self.windowSize.split('x')
|
x, y = self.windowSize.split('x')
|
||||||
MW.resize(int(x), int(y))
|
MW.resize(int(x), int(y))
|
||||||
@@ -1090,7 +1145,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
|||||||
class KCCGUI_MetaEditor(KCC_ui_editor.Ui_editorDialog):
|
class KCCGUI_MetaEditor(KCC_ui_editor.Ui_editorDialog):
|
||||||
def loadData(self, file):
|
def loadData(self, file):
|
||||||
self.parser = metadata.MetadataParser(file)
|
self.parser = metadata.MetadataParser(file)
|
||||||
if self.parser.compressor == 'rar':
|
if self.parser.format in ['RAR', 'RAR5']:
|
||||||
self.editorWidget.setEnabled(False)
|
self.editorWidget.setEnabled(False)
|
||||||
self.okButton.setEnabled(False)
|
self.okButton.setEnabled(False)
|
||||||
self.statusLabel.setText('CBR metadata are read-only.')
|
self.statusLabel.setText('CBR metadata are read-only.')
|
||||||
@@ -1098,23 +1153,20 @@ class KCCGUI_MetaEditor(KCC_ui_editor.Ui_editorDialog):
|
|||||||
self.editorWidget.setEnabled(True)
|
self.editorWidget.setEnabled(True)
|
||||||
self.okButton.setEnabled(True)
|
self.okButton.setEnabled(True)
|
||||||
self.statusLabel.setText('Separate authors with a comma.')
|
self.statusLabel.setText('Separate authors with a comma.')
|
||||||
for field in (self.seriesLine, self.volumeLine, self.numberLine, self.muidLine):
|
for field in (self.seriesLine, self.volumeLine, self.numberLine):
|
||||||
if field.objectName() == 'muidLine':
|
field.setText(self.parser.data[field.objectName().capitalize()[:-4]])
|
||||||
field.setText(self.parser.data['MUid'])
|
|
||||||
else:
|
|
||||||
field.setText(self.parser.data[field.objectName().capitalize()[:-4]])
|
|
||||||
for field in (self.writerLine, self.pencillerLine, self.inkerLine, self.coloristLine):
|
for field in (self.writerLine, self.pencillerLine, self.inkerLine, self.coloristLine):
|
||||||
field.setText(', '.join(self.parser.data[field.objectName().capitalize()[:-4] + 's']))
|
field.setText(', '.join(self.parser.data[field.objectName().capitalize()[:-4] + 's']))
|
||||||
if self.seriesLine.text() == '':
|
if self.seriesLine.text() == '':
|
||||||
self.seriesLine.setText(file.split('\\')[-1].split('/')[-1].split('.')[0])
|
if file.endswith('.xml'):
|
||||||
|
self.seriesLine.setText(file.split('\\')[-2])
|
||||||
|
else:
|
||||||
|
self.seriesLine.setText(file.split('\\')[-1].split('/')[-1].split('.')[0])
|
||||||
|
|
||||||
def saveData(self):
|
def saveData(self):
|
||||||
for field in (self.volumeLine, self.numberLine, self.muidLine):
|
for field in (self.volumeLine, self.numberLine):
|
||||||
if field.text().isnumeric() or self.cleanData(field.text()) == '':
|
if field.text().isnumeric() or self.cleanData(field.text()) == '':
|
||||||
if field.objectName() == 'muidLine':
|
self.parser.data[field.objectName().capitalize()[:-4]] = self.cleanData(field.text())
|
||||||
self.parser.data['MUid'] = self.cleanData(field.text())
|
|
||||||
else:
|
|
||||||
self.parser.data[field.objectName().capitalize()[:-4]] = self.cleanData(field.text())
|
|
||||||
else:
|
else:
|
||||||
self.statusLabel.setText(field.objectName().capitalize()[:-4] + ' field must be a number.')
|
self.statusLabel.setText(field.objectName().capitalize()[:-4] + ' field must be a number.')
|
||||||
break
|
break
|
||||||
@@ -1143,7 +1195,7 @@ class KCCGUI_MetaEditor(KCC_ui_editor.Ui_editorDialog):
|
|||||||
self.ui = QtWidgets.QDialog()
|
self.ui = QtWidgets.QDialog()
|
||||||
self.parser = None
|
self.parser = None
|
||||||
self.setupUi(self.ui)
|
self.setupUi(self.ui)
|
||||||
self.ui.setWindowFlags(self.ui.windowFlags() & ~QtCore.Qt.WindowContextHelpButtonHint)
|
self.ui.setWindowFlags(self.ui.windowFlags() & ~QtCore.Qt.WindowType.WindowContextHelpButtonHint)
|
||||||
self.okButton.clicked.connect(self.saveData)
|
self.okButton.clicked.connect(self.saveData)
|
||||||
self.cancelButton.clicked.connect(self.ui.close)
|
self.cancelButton.clicked.connect(self.ui.close)
|
||||||
if sys.platform.startswith('linux'):
|
if sys.platform.startswith('linux'):
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,272 +1,463 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Form implementation generated from reading ui file 'gui\KCC.ui'
|
################################################################################
|
||||||
#
|
## Form generated from reading UI file 'KCC.ui'
|
||||||
# Created by: PyQt5 UI code generator 5.8.1
|
##
|
||||||
#
|
## Created by: Qt User Interface Compiler version 6.5.2
|
||||||
# WARNING! All changes made in this file will be lost!
|
##
|
||||||
|
## WARNING! All changes made in this file will be lost when recompiling UI file!
|
||||||
|
################################################################################
|
||||||
|
|
||||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
|
||||||
|
QMetaObject, QObject, QPoint, QRect,
|
||||||
|
QSize, QTime, QUrl, Qt)
|
||||||
|
from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
|
||||||
|
QFont, QFontDatabase, QGradient, QIcon,
|
||||||
|
QImage, QKeySequence, QLinearGradient, QPainter,
|
||||||
|
QPalette, QPixmap, QRadialGradient, QTransform)
|
||||||
|
from PySide6.QtWidgets import (QAbstractItemView, QApplication, QCheckBox, QComboBox,
|
||||||
|
QGridLayout, QHBoxLayout, QLabel, QListWidget,
|
||||||
|
QListWidgetItem, QMainWindow, QProgressBar, QPushButton,
|
||||||
|
QSizePolicy, QSlider, QSpinBox, QStatusBar,
|
||||||
|
QWidget)
|
||||||
|
from . import KCC_rc
|
||||||
|
|
||||||
class Ui_mainWindow(object):
|
class Ui_mainWindow(object):
|
||||||
def setupUi(self, mainWindow):
|
def setupUi(self, mainWindow):
|
||||||
mainWindow.setObjectName("mainWindow")
|
if not mainWindow.objectName():
|
||||||
|
mainWindow.setObjectName(u"mainWindow")
|
||||||
mainWindow.resize(450, 400)
|
mainWindow.resize(450, 400)
|
||||||
icon = QtGui.QIcon()
|
icon = QIcon()
|
||||||
icon.addPixmap(QtGui.QPixmap(":/Icon/icons/comic2ebook.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
icon.addFile(u":/Icon/icons/comic2ebook.png", QSize(), QIcon.Normal, QIcon.Off)
|
||||||
mainWindow.setWindowIcon(icon)
|
mainWindow.setWindowIcon(icon)
|
||||||
self.centralWidget = QtWidgets.QWidget(mainWindow)
|
self.centralWidget = QWidget(mainWindow)
|
||||||
self.centralWidget.setObjectName("centralWidget")
|
self.centralWidget.setObjectName(u"centralWidget")
|
||||||
self.gridLayout = QtWidgets.QGridLayout(self.centralWidget)
|
self.gridLayout = QGridLayout(self.centralWidget)
|
||||||
|
self.gridLayout.setObjectName(u"gridLayout")
|
||||||
self.gridLayout.setContentsMargins(-1, -1, -1, 5)
|
self.gridLayout.setContentsMargins(-1, -1, -1, 5)
|
||||||
self.gridLayout.setObjectName("gridLayout")
|
self.optionWidget = QWidget(self.centralWidget)
|
||||||
self.progressBar = QtWidgets.QProgressBar(self.centralWidget)
|
self.optionWidget.setObjectName(u"optionWidget")
|
||||||
self.progressBar.setMinimumSize(QtCore.QSize(0, 30))
|
self.gridLayout_2 = QGridLayout(self.optionWidget)
|
||||||
font = QtGui.QFont()
|
self.gridLayout_2.setObjectName(u"gridLayout_2")
|
||||||
font.setBold(True)
|
|
||||||
font.setWeight(75)
|
|
||||||
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.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.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection)
|
|
||||||
self.jobList.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel)
|
|
||||||
self.jobList.setHorizontalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel)
|
|
||||||
self.jobList.setObjectName("jobList")
|
|
||||||
self.gridLayout.addWidget(self.jobList, 2, 0, 1, 2)
|
|
||||||
self.customWidget = QtWidgets.QWidget(self.centralWidget)
|
|
||||||
self.customWidget.setVisible(False)
|
|
||||||
self.customWidget.setObjectName("customWidget")
|
|
||||||
self.gridLayout_3 = QtWidgets.QGridLayout(self.customWidget)
|
|
||||||
self.gridLayout_3.setContentsMargins(0, 0, 0, 0)
|
|
||||||
self.gridLayout_3.setObjectName("gridLayout_3")
|
|
||||||
self.hLabel = QtWidgets.QLabel(self.customWidget)
|
|
||||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Preferred)
|
|
||||||
sizePolicy.setHorizontalStretch(0)
|
|
||||||
sizePolicy.setVerticalStretch(0)
|
|
||||||
sizePolicy.setHeightForWidth(self.hLabel.sizePolicy().hasHeightForWidth())
|
|
||||||
self.hLabel.setSizePolicy(sizePolicy)
|
|
||||||
self.hLabel.setObjectName("hLabel")
|
|
||||||
self.gridLayout_3.addWidget(self.hLabel, 0, 2, 1, 1)
|
|
||||||
self.widthBox = QtWidgets.QSpinBox(self.customWidget)
|
|
||||||
self.widthBox.setMaximum(2160)
|
|
||||||
self.widthBox.setObjectName("widthBox")
|
|
||||||
self.gridLayout_3.addWidget(self.widthBox, 0, 1, 1, 1)
|
|
||||||
self.wLabel = QtWidgets.QLabel(self.customWidget)
|
|
||||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Preferred)
|
|
||||||
sizePolicy.setHorizontalStretch(0)
|
|
||||||
sizePolicy.setVerticalStretch(0)
|
|
||||||
sizePolicy.setHeightForWidth(self.wLabel.sizePolicy().hasHeightForWidth())
|
|
||||||
self.wLabel.setSizePolicy(sizePolicy)
|
|
||||||
self.wLabel.setObjectName("wLabel")
|
|
||||||
self.gridLayout_3.addWidget(self.wLabel, 0, 0, 1, 1)
|
|
||||||
self.heightBox = QtWidgets.QSpinBox(self.customWidget)
|
|
||||||
self.heightBox.setMaximum(3840)
|
|
||||||
self.heightBox.setObjectName("heightBox")
|
|
||||||
self.gridLayout_3.addWidget(self.heightBox, 0, 3, 1, 1)
|
|
||||||
self.gridLayout.addWidget(self.customWidget, 6, 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.setContentsMargins(0, 0, 0, 0)
|
||||||
self.gridLayout_2.setObjectName("gridLayout_2")
|
self.qualityBox = QCheckBox(self.optionWidget)
|
||||||
self.mangaBox = QtWidgets.QCheckBox(self.optionWidget)
|
self.qualityBox.setObjectName(u"qualityBox")
|
||||||
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.setTristate(True)
|
||||||
self.qualityBox.setObjectName("qualityBox")
|
|
||||||
self.gridLayout_2.addWidget(self.qualityBox, 0, 2, 1, 1)
|
self.gridLayout_2.addWidget(self.qualityBox, 0, 2, 1, 1)
|
||||||
self.webtoonBox = QtWidgets.QCheckBox(self.optionWidget)
|
|
||||||
self.webtoonBox.setObjectName("webtoonBox")
|
self.deleteBox = QCheckBox(self.optionWidget)
|
||||||
self.gridLayout_2.addWidget(self.webtoonBox, 1, 0, 1, 1)
|
self.deleteBox.setObjectName(u"deleteBox")
|
||||||
self.upscaleBox = QtWidgets.QCheckBox(self.optionWidget)
|
|
||||||
self.upscaleBox.setTristate(True)
|
self.gridLayout_2.addWidget(self.deleteBox, 4, 1, 1, 1)
|
||||||
self.upscaleBox.setObjectName("upscaleBox")
|
|
||||||
self.gridLayout_2.addWidget(self.upscaleBox, 1, 1, 1, 1)
|
self.maximizeStrips = QCheckBox(self.optionWidget)
|
||||||
self.gammaBox = QtWidgets.QCheckBox(self.optionWidget)
|
self.maximizeStrips.setObjectName(u"maximizeStrips")
|
||||||
self.gammaBox.setObjectName("gammaBox")
|
|
||||||
|
self.gridLayout_2.addWidget(self.maximizeStrips, 3, 1, 1, 1)
|
||||||
|
|
||||||
|
self.gammaBox = QCheckBox(self.optionWidget)
|
||||||
|
self.gammaBox.setObjectName(u"gammaBox")
|
||||||
|
|
||||||
self.gridLayout_2.addWidget(self.gammaBox, 1, 2, 1, 1)
|
self.gridLayout_2.addWidget(self.gammaBox, 1, 2, 1, 1)
|
||||||
self.borderBox = QtWidgets.QCheckBox(self.optionWidget)
|
|
||||||
|
self.borderBox = QCheckBox(self.optionWidget)
|
||||||
|
self.borderBox.setObjectName(u"borderBox")
|
||||||
self.borderBox.setTristate(True)
|
self.borderBox.setTristate(True)
|
||||||
self.borderBox.setObjectName("borderBox")
|
|
||||||
self.gridLayout_2.addWidget(self.borderBox, 2, 0, 1, 1)
|
self.gridLayout_2.addWidget(self.borderBox, 2, 0, 1, 1)
|
||||||
self.outputSplit = QtWidgets.QCheckBox(self.optionWidget)
|
|
||||||
self.outputSplit.setObjectName("outputSplit")
|
self.webtoonBox = QCheckBox(self.optionWidget)
|
||||||
|
self.webtoonBox.setObjectName(u"webtoonBox")
|
||||||
|
|
||||||
|
self.gridLayout_2.addWidget(self.webtoonBox, 1, 0, 1, 1)
|
||||||
|
|
||||||
|
self.upscaleBox = QCheckBox(self.optionWidget)
|
||||||
|
self.upscaleBox.setObjectName(u"upscaleBox")
|
||||||
|
self.upscaleBox.setTristate(True)
|
||||||
|
|
||||||
|
self.gridLayout_2.addWidget(self.upscaleBox, 1, 1, 1, 1)
|
||||||
|
|
||||||
|
self.mangaBox = QCheckBox(self.optionWidget)
|
||||||
|
self.mangaBox.setObjectName(u"mangaBox")
|
||||||
|
|
||||||
|
self.gridLayout_2.addWidget(self.mangaBox, 0, 0, 1, 1)
|
||||||
|
|
||||||
|
self.rotateBox = QCheckBox(self.optionWidget)
|
||||||
|
self.rotateBox.setObjectName(u"rotateBox")
|
||||||
|
self.rotateBox.setTristate(True)
|
||||||
|
|
||||||
|
self.gridLayout_2.addWidget(self.rotateBox, 0, 1, 1, 1)
|
||||||
|
|
||||||
|
self.croppingBox = QCheckBox(self.optionWidget)
|
||||||
|
self.croppingBox.setObjectName(u"croppingBox")
|
||||||
|
self.croppingBox.setTristate(True)
|
||||||
|
|
||||||
|
self.gridLayout_2.addWidget(self.croppingBox, 3, 2, 1, 1)
|
||||||
|
|
||||||
|
self.outputSplit = QCheckBox(self.optionWidget)
|
||||||
|
self.outputSplit.setObjectName(u"outputSplit")
|
||||||
|
|
||||||
self.gridLayout_2.addWidget(self.outputSplit, 2, 1, 1, 1)
|
self.gridLayout_2.addWidget(self.outputSplit, 2, 1, 1, 1)
|
||||||
self.colorBox = QtWidgets.QCheckBox(self.optionWidget)
|
|
||||||
self.colorBox.setObjectName("colorBox")
|
self.mozJpegBox = QCheckBox(self.optionWidget)
|
||||||
|
self.mozJpegBox.setObjectName(u"mozJpegBox")
|
||||||
|
self.mozJpegBox.setTristate(True)
|
||||||
|
|
||||||
|
self.gridLayout_2.addWidget(self.mozJpegBox, 3, 0, 1, 1)
|
||||||
|
|
||||||
|
self.colorBox = QCheckBox(self.optionWidget)
|
||||||
|
self.colorBox.setObjectName(u"colorBox")
|
||||||
|
|
||||||
self.gridLayout_2.addWidget(self.colorBox, 2, 2, 1, 1)
|
self.gridLayout_2.addWidget(self.colorBox, 2, 2, 1, 1)
|
||||||
self.gridLayout.addWidget(self.optionWidget, 4, 0, 1, 2)
|
|
||||||
self.gammaWidget = QtWidgets.QWidget(self.centralWidget)
|
self.disableProcessingBox = QCheckBox(self.optionWidget)
|
||||||
|
self.disableProcessingBox.setObjectName(u"disableProcessingBox")
|
||||||
|
|
||||||
|
self.gridLayout_2.addWidget(self.disableProcessingBox, 4, 2, 1, 1)
|
||||||
|
|
||||||
|
self.dedupeCoverBox = QCheckBox(self.optionWidget)
|
||||||
|
self.dedupeCoverBox.setObjectName(u"dedupeCoverBox")
|
||||||
|
|
||||||
|
self.gridLayout_2.addWidget(self.dedupeCoverBox, 4, 0, 1, 1)
|
||||||
|
|
||||||
|
|
||||||
|
self.gridLayout.addWidget(self.optionWidget, 5, 0, 1, 2)
|
||||||
|
|
||||||
|
self.gammaWidget = QWidget(self.centralWidget)
|
||||||
|
self.gammaWidget.setObjectName(u"gammaWidget")
|
||||||
self.gammaWidget.setVisible(False)
|
self.gammaWidget.setVisible(False)
|
||||||
self.gammaWidget.setObjectName("gammaWidget")
|
self.horizontalLayout_2 = QHBoxLayout(self.gammaWidget)
|
||||||
self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.gammaWidget)
|
self.horizontalLayout_2.setObjectName(u"horizontalLayout_2")
|
||||||
self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0)
|
self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0)
|
||||||
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
|
self.gammaLabel = QLabel(self.gammaWidget)
|
||||||
self.gammaLabel = QtWidgets.QLabel(self.gammaWidget)
|
self.gammaLabel.setObjectName(u"gammaLabel")
|
||||||
self.gammaLabel.setObjectName("gammaLabel")
|
|
||||||
self.horizontalLayout_2.addWidget(self.gammaLabel)
|
self.horizontalLayout_2.addWidget(self.gammaLabel)
|
||||||
self.gammaSlider = QtWidgets.QSlider(self.gammaWidget)
|
|
||||||
|
self.gammaSlider = QSlider(self.gammaWidget)
|
||||||
|
self.gammaSlider.setObjectName(u"gammaSlider")
|
||||||
self.gammaSlider.setMaximum(250)
|
self.gammaSlider.setMaximum(250)
|
||||||
self.gammaSlider.setSingleStep(5)
|
self.gammaSlider.setSingleStep(5)
|
||||||
self.gammaSlider.setOrientation(QtCore.Qt.Horizontal)
|
self.gammaSlider.setOrientation(Qt.Horizontal)
|
||||||
self.gammaSlider.setObjectName("gammaSlider")
|
|
||||||
self.horizontalLayout_2.addWidget(self.gammaSlider)
|
self.horizontalLayout_2.addWidget(self.gammaSlider)
|
||||||
self.gridLayout.addWidget(self.gammaWidget, 5, 0, 1, 2)
|
|
||||||
self.toolWidget = QtWidgets.QWidget(self.centralWidget)
|
|
||||||
self.toolWidget.setObjectName("toolWidget")
|
self.gridLayout.addWidget(self.gammaWidget, 6, 0, 1, 2)
|
||||||
self.horizontalLayout = QtWidgets.QHBoxLayout(self.toolWidget)
|
|
||||||
self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
|
self.croppingWidget = QWidget(self.centralWidget)
|
||||||
self.horizontalLayout.setObjectName("horizontalLayout")
|
self.croppingWidget.setObjectName(u"croppingWidget")
|
||||||
self.editorButton = QtWidgets.QPushButton(self.toolWidget)
|
self.croppingWidget.setVisible(False)
|
||||||
self.editorButton.setMinimumSize(QtCore.QSize(0, 30))
|
self.horizontalLayout_3 = QHBoxLayout(self.croppingWidget)
|
||||||
icon1 = QtGui.QIcon()
|
self.horizontalLayout_3.setObjectName(u"horizontalLayout_3")
|
||||||
icon1.addPixmap(QtGui.QPixmap(":/Other/icons/editor.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
self.horizontalLayout_3.setContentsMargins(0, 0, 0, 0)
|
||||||
self.editorButton.setIcon(icon1)
|
self.croppingPowerLabel = QLabel(self.croppingWidget)
|
||||||
self.editorButton.setObjectName("editorButton")
|
self.croppingPowerLabel.setObjectName(u"croppingPowerLabel")
|
||||||
self.horizontalLayout.addWidget(self.editorButton)
|
|
||||||
self.wikiButton = QtWidgets.QPushButton(self.toolWidget)
|
self.horizontalLayout_3.addWidget(self.croppingPowerLabel)
|
||||||
self.wikiButton.setMinimumSize(QtCore.QSize(0, 30))
|
|
||||||
icon2 = QtGui.QIcon()
|
self.croppingPowerSlider = QSlider(self.croppingWidget)
|
||||||
icon2.addPixmap(QtGui.QPixmap(":/Other/icons/wiki.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
self.croppingPowerSlider.setObjectName(u"croppingPowerSlider")
|
||||||
self.wikiButton.setIcon(icon2)
|
self.croppingPowerSlider.setMaximum(200)
|
||||||
self.wikiButton.setObjectName("wikiButton")
|
self.croppingPowerSlider.setSingleStep(1)
|
||||||
self.horizontalLayout.addWidget(self.wikiButton)
|
self.croppingPowerSlider.setOrientation(Qt.Horizontal)
|
||||||
self.gridLayout.addWidget(self.toolWidget, 0, 0, 1, 2)
|
|
||||||
self.buttonWidget = QtWidgets.QWidget(self.centralWidget)
|
self.horizontalLayout_3.addWidget(self.croppingPowerSlider)
|
||||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
|
|
||||||
|
|
||||||
|
self.gridLayout.addWidget(self.croppingWidget, 8, 0, 1, 2)
|
||||||
|
|
||||||
|
self.buttonWidget = QWidget(self.centralWidget)
|
||||||
|
self.buttonWidget.setObjectName(u"buttonWidget")
|
||||||
|
sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed)
|
||||||
sizePolicy.setHorizontalStretch(0)
|
sizePolicy.setHorizontalStretch(0)
|
||||||
sizePolicy.setVerticalStretch(0)
|
sizePolicy.setVerticalStretch(0)
|
||||||
sizePolicy.setHeightForWidth(self.buttonWidget.sizePolicy().hasHeightForWidth())
|
sizePolicy.setHeightForWidth(self.buttonWidget.sizePolicy().hasHeightForWidth())
|
||||||
self.buttonWidget.setSizePolicy(sizePolicy)
|
self.buttonWidget.setSizePolicy(sizePolicy)
|
||||||
self.buttonWidget.setObjectName("buttonWidget")
|
self.gridLayout_4 = QGridLayout(self.buttonWidget)
|
||||||
self.gridLayout_4 = QtWidgets.QGridLayout(self.buttonWidget)
|
self.gridLayout_4.setObjectName(u"gridLayout_4")
|
||||||
self.gridLayout_4.setContentsMargins(0, 0, 0, 0)
|
self.gridLayout_4.setContentsMargins(0, 0, 0, 0)
|
||||||
self.gridLayout_4.setObjectName("gridLayout_4")
|
self.directoryButton = QPushButton(self.buttonWidget)
|
||||||
self.directoryButton = QtWidgets.QPushButton(self.buttonWidget)
|
self.directoryButton.setObjectName(u"directoryButton")
|
||||||
self.directoryButton.setMinimumSize(QtCore.QSize(0, 30))
|
self.directoryButton.setMinimumSize(QSize(0, 30))
|
||||||
icon3 = QtGui.QIcon()
|
icon1 = QIcon()
|
||||||
icon3.addPixmap(QtGui.QPixmap(":/Other/icons/folder_new.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
icon1.addFile(u":/Other/icons/folder_new.png", QSize(), QIcon.Normal, QIcon.Off)
|
||||||
self.directoryButton.setIcon(icon3)
|
self.directoryButton.setIcon(icon1)
|
||||||
self.directoryButton.setObjectName("directoryButton")
|
|
||||||
self.gridLayout_4.addWidget(self.directoryButton, 0, 0, 1, 1)
|
self.gridLayout_4.addWidget(self.directoryButton, 0, 0, 1, 1)
|
||||||
self.fileButton = QtWidgets.QPushButton(self.buttonWidget)
|
|
||||||
self.fileButton.setMinimumSize(QtCore.QSize(0, 30))
|
self.fileButton = QPushButton(self.buttonWidget)
|
||||||
icon4 = QtGui.QIcon()
|
self.fileButton.setObjectName(u"fileButton")
|
||||||
icon4.addPixmap(QtGui.QPixmap(":/Other/icons/document_new.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
self.fileButton.setMinimumSize(QSize(0, 30))
|
||||||
self.fileButton.setIcon(icon4)
|
icon2 = QIcon()
|
||||||
self.fileButton.setObjectName("fileButton")
|
icon2.addFile(u":/Other/icons/document_new.png", QSize(), QIcon.Normal, QIcon.Off)
|
||||||
|
self.fileButton.setIcon(icon2)
|
||||||
|
|
||||||
self.gridLayout_4.addWidget(self.fileButton, 0, 3, 1, 1)
|
self.gridLayout_4.addWidget(self.fileButton, 0, 3, 1, 1)
|
||||||
self.deviceBox = QtWidgets.QComboBox(self.buttonWidget)
|
|
||||||
self.deviceBox.setMinimumSize(QtCore.QSize(0, 28))
|
self.deviceBox = QComboBox(self.buttonWidget)
|
||||||
self.deviceBox.setSizeAdjustPolicy(QtWidgets.QComboBox.AdjustToMinimumContentsLength)
|
self.deviceBox.setObjectName(u"deviceBox")
|
||||||
self.deviceBox.setObjectName("deviceBox")
|
self.deviceBox.setMinimumSize(QSize(0, 28))
|
||||||
|
|
||||||
self.gridLayout_4.addWidget(self.deviceBox, 1, 0, 1, 1)
|
self.gridLayout_4.addWidget(self.deviceBox, 1, 0, 1, 1)
|
||||||
self.formatBox = QtWidgets.QComboBox(self.buttonWidget)
|
|
||||||
self.formatBox.setMinimumSize(QtCore.QSize(0, 28))
|
self.formatBox = QComboBox(self.buttonWidget)
|
||||||
self.formatBox.setSizeAdjustPolicy(QtWidgets.QComboBox.AdjustToMinimumContentsLength)
|
self.formatBox.setObjectName(u"formatBox")
|
||||||
self.formatBox.setObjectName("formatBox")
|
self.formatBox.setMinimumSize(QSize(0, 28))
|
||||||
|
|
||||||
self.gridLayout_4.addWidget(self.formatBox, 1, 3, 1, 1)
|
self.gridLayout_4.addWidget(self.formatBox, 1, 3, 1, 1)
|
||||||
self.convertButton = QtWidgets.QPushButton(self.buttonWidget)
|
|
||||||
self.convertButton.setMinimumSize(QtCore.QSize(0, 30))
|
self.convertButton = QPushButton(self.buttonWidget)
|
||||||
font = QtGui.QFont()
|
self.convertButton.setObjectName(u"convertButton")
|
||||||
|
self.convertButton.setMinimumSize(QSize(0, 30))
|
||||||
|
font = QFont()
|
||||||
font.setBold(True)
|
font.setBold(True)
|
||||||
font.setWeight(75)
|
|
||||||
self.convertButton.setFont(font)
|
self.convertButton.setFont(font)
|
||||||
icon5 = QtGui.QIcon()
|
icon3 = QIcon()
|
||||||
icon5.addPixmap(QtGui.QPixmap(":/Other/icons/convert.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
icon3.addFile(u":/Other/icons/convert.png", QSize(), QIcon.Normal, QIcon.Off)
|
||||||
self.convertButton.setIcon(icon5)
|
self.convertButton.setIcon(icon3)
|
||||||
self.convertButton.setObjectName("convertButton")
|
|
||||||
self.gridLayout_4.addWidget(self.convertButton, 1, 2, 1, 1)
|
self.gridLayout_4.addWidget(self.convertButton, 1, 2, 1, 1)
|
||||||
self.clearButton = QtWidgets.QPushButton(self.buttonWidget)
|
|
||||||
self.clearButton.setMinimumSize(QtCore.QSize(0, 30))
|
self.clearButton = QPushButton(self.buttonWidget)
|
||||||
icon6 = QtGui.QIcon()
|
self.clearButton.setObjectName(u"clearButton")
|
||||||
icon6.addPixmap(QtGui.QPixmap(":/Other/icons/clear.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
self.clearButton.setMinimumSize(QSize(0, 30))
|
||||||
self.clearButton.setIcon(icon6)
|
icon4 = QIcon()
|
||||||
self.clearButton.setObjectName("clearButton")
|
icon4.addFile(u":/Other/icons/clear.png", QSize(), QIcon.Normal, QIcon.Off)
|
||||||
|
self.clearButton.setIcon(icon4)
|
||||||
|
|
||||||
self.gridLayout_4.addWidget(self.clearButton, 0, 2, 1, 1)
|
self.gridLayout_4.addWidget(self.clearButton, 0, 2, 1, 1)
|
||||||
|
|
||||||
self.directoryButton.raise_()
|
self.directoryButton.raise_()
|
||||||
self.clearButton.raise_()
|
self.clearButton.raise_()
|
||||||
self.fileButton.raise_()
|
self.fileButton.raise_()
|
||||||
self.deviceBox.raise_()
|
self.deviceBox.raise_()
|
||||||
self.convertButton.raise_()
|
self.convertButton.raise_()
|
||||||
self.formatBox.raise_()
|
self.formatBox.raise_()
|
||||||
|
|
||||||
self.gridLayout.addWidget(self.buttonWidget, 3, 0, 1, 2)
|
self.gridLayout.addWidget(self.buttonWidget, 3, 0, 1, 2)
|
||||||
|
|
||||||
|
self.toolWidget = QWidget(self.centralWidget)
|
||||||
|
self.toolWidget.setObjectName(u"toolWidget")
|
||||||
|
self.horizontalLayout = QHBoxLayout(self.toolWidget)
|
||||||
|
self.horizontalLayout.setObjectName(u"horizontalLayout")
|
||||||
|
self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
|
||||||
|
self.editorButton = QPushButton(self.toolWidget)
|
||||||
|
self.editorButton.setObjectName(u"editorButton")
|
||||||
|
self.editorButton.setMinimumSize(QSize(0, 30))
|
||||||
|
icon5 = QIcon()
|
||||||
|
icon5.addFile(u":/Other/icons/editor.png", QSize(), QIcon.Normal, QIcon.Off)
|
||||||
|
self.editorButton.setIcon(icon5)
|
||||||
|
|
||||||
|
self.horizontalLayout.addWidget(self.editorButton)
|
||||||
|
|
||||||
|
self.wikiButton = QPushButton(self.toolWidget)
|
||||||
|
self.wikiButton.setObjectName(u"wikiButton")
|
||||||
|
self.wikiButton.setMinimumSize(QSize(0, 30))
|
||||||
|
icon6 = QIcon()
|
||||||
|
icon6.addFile(u":/Other/icons/wiki.png", QSize(), QIcon.Normal, QIcon.Off)
|
||||||
|
self.wikiButton.setIcon(icon6)
|
||||||
|
|
||||||
|
self.horizontalLayout.addWidget(self.wikiButton)
|
||||||
|
|
||||||
|
|
||||||
|
self.gridLayout.addWidget(self.toolWidget, 0, 0, 1, 2)
|
||||||
|
|
||||||
|
self.jobList = QListWidget(self.centralWidget)
|
||||||
|
self.jobList.setObjectName(u"jobList")
|
||||||
|
self.jobList.setStyleSheet(u"QListWidget#jobList {background:#ffffff;background-image:url(:/Other/icons/list_background.png);background-position:center center;background-repeat:no-repeat;color:rgb(0,0,0);}")
|
||||||
|
self.jobList.setSelectionMode(QAbstractItemView.NoSelection)
|
||||||
|
self.jobList.setVerticalScrollMode(QAbstractItemView.ScrollPerPixel)
|
||||||
|
self.jobList.setHorizontalScrollMode(QAbstractItemView.ScrollPerPixel)
|
||||||
|
|
||||||
|
self.gridLayout.addWidget(self.jobList, 2, 0, 1, 2)
|
||||||
|
|
||||||
|
self.progressBar = QProgressBar(self.centralWidget)
|
||||||
|
self.progressBar.setObjectName(u"progressBar")
|
||||||
|
self.progressBar.setMinimumSize(QSize(0, 30))
|
||||||
|
self.progressBar.setFont(font)
|
||||||
|
self.progressBar.setVisible(False)
|
||||||
|
self.progressBar.setAlignment(Qt.AlignJustify|Qt.AlignVCenter)
|
||||||
|
|
||||||
|
self.gridLayout.addWidget(self.progressBar, 1, 0, 1, 2)
|
||||||
|
|
||||||
|
self.customWidget = QWidget(self.centralWidget)
|
||||||
|
self.customWidget.setObjectName(u"customWidget")
|
||||||
|
self.customWidget.setVisible(False)
|
||||||
|
self.gridLayout_3 = QGridLayout(self.customWidget)
|
||||||
|
self.gridLayout_3.setObjectName(u"gridLayout_3")
|
||||||
|
self.gridLayout_3.setContentsMargins(0, 0, 0, 0)
|
||||||
|
self.hLabel = QLabel(self.customWidget)
|
||||||
|
self.hLabel.setObjectName(u"hLabel")
|
||||||
|
sizePolicy1 = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred)
|
||||||
|
sizePolicy1.setHorizontalStretch(0)
|
||||||
|
sizePolicy1.setVerticalStretch(0)
|
||||||
|
sizePolicy1.setHeightForWidth(self.hLabel.sizePolicy().hasHeightForWidth())
|
||||||
|
self.hLabel.setSizePolicy(sizePolicy1)
|
||||||
|
|
||||||
|
self.gridLayout_3.addWidget(self.hLabel, 0, 2, 1, 1)
|
||||||
|
|
||||||
|
self.widthBox = QSpinBox(self.customWidget)
|
||||||
|
self.widthBox.setObjectName(u"widthBox")
|
||||||
|
self.widthBox.setMaximum(2160)
|
||||||
|
|
||||||
|
self.gridLayout_3.addWidget(self.widthBox, 0, 1, 1, 1)
|
||||||
|
|
||||||
|
self.wLabel = QLabel(self.customWidget)
|
||||||
|
self.wLabel.setObjectName(u"wLabel")
|
||||||
|
sizePolicy1.setHeightForWidth(self.wLabel.sizePolicy().hasHeightForWidth())
|
||||||
|
self.wLabel.setSizePolicy(sizePolicy1)
|
||||||
|
|
||||||
|
self.gridLayout_3.addWidget(self.wLabel, 0, 0, 1, 1)
|
||||||
|
|
||||||
|
self.heightBox = QSpinBox(self.customWidget)
|
||||||
|
self.heightBox.setObjectName(u"heightBox")
|
||||||
|
self.heightBox.setMaximum(3840)
|
||||||
|
|
||||||
|
self.gridLayout_3.addWidget(self.heightBox, 0, 3, 1, 1)
|
||||||
|
|
||||||
|
|
||||||
|
self.gridLayout.addWidget(self.customWidget, 7, 0, 1, 2)
|
||||||
|
|
||||||
mainWindow.setCentralWidget(self.centralWidget)
|
mainWindow.setCentralWidget(self.centralWidget)
|
||||||
self.statusBar = QtWidgets.QStatusBar(mainWindow)
|
self.statusBar = QStatusBar(mainWindow)
|
||||||
|
self.statusBar.setObjectName(u"statusBar")
|
||||||
self.statusBar.setSizeGripEnabled(False)
|
self.statusBar.setSizeGripEnabled(False)
|
||||||
self.statusBar.setObjectName("statusBar")
|
|
||||||
mainWindow.setStatusBar(self.statusBar)
|
mainWindow.setStatusBar(self.statusBar)
|
||||||
|
QWidget.setTabOrder(self.convertButton, self.clearButton)
|
||||||
|
QWidget.setTabOrder(self.clearButton, self.directoryButton)
|
||||||
|
QWidget.setTabOrder(self.directoryButton, self.fileButton)
|
||||||
|
QWidget.setTabOrder(self.fileButton, self.deviceBox)
|
||||||
|
QWidget.setTabOrder(self.deviceBox, self.formatBox)
|
||||||
|
QWidget.setTabOrder(self.formatBox, self.mangaBox)
|
||||||
|
QWidget.setTabOrder(self.mangaBox, self.rotateBox)
|
||||||
|
QWidget.setTabOrder(self.rotateBox, self.qualityBox)
|
||||||
|
QWidget.setTabOrder(self.qualityBox, self.webtoonBox)
|
||||||
|
QWidget.setTabOrder(self.webtoonBox, self.upscaleBox)
|
||||||
|
QWidget.setTabOrder(self.upscaleBox, self.gammaBox)
|
||||||
|
QWidget.setTabOrder(self.gammaBox, self.borderBox)
|
||||||
|
QWidget.setTabOrder(self.borderBox, self.outputSplit)
|
||||||
|
QWidget.setTabOrder(self.outputSplit, self.colorBox)
|
||||||
|
QWidget.setTabOrder(self.colorBox, self.croppingBox)
|
||||||
|
QWidget.setTabOrder(self.croppingBox, self.mozJpegBox)
|
||||||
|
QWidget.setTabOrder(self.mozJpegBox, self.maximizeStrips)
|
||||||
|
QWidget.setTabOrder(self.maximizeStrips, self.deleteBox)
|
||||||
|
QWidget.setTabOrder(self.deleteBox, self.disableProcessingBox)
|
||||||
|
QWidget.setTabOrder(self.disableProcessingBox, self.editorButton)
|
||||||
|
QWidget.setTabOrder(self.editorButton, self.wikiButton)
|
||||||
|
QWidget.setTabOrder(self.wikiButton, self.jobList)
|
||||||
|
QWidget.setTabOrder(self.jobList, self.gammaSlider)
|
||||||
|
QWidget.setTabOrder(self.gammaSlider, self.widthBox)
|
||||||
|
QWidget.setTabOrder(self.widthBox, self.heightBox)
|
||||||
|
QWidget.setTabOrder(self.heightBox, self.croppingPowerSlider)
|
||||||
|
|
||||||
self.retranslateUi(mainWindow)
|
self.retranslateUi(mainWindow)
|
||||||
QtCore.QMetaObject.connectSlotsByName(mainWindow)
|
|
||||||
mainWindow.setTabOrder(self.convertButton, self.clearButton)
|
QMetaObject.connectSlotsByName(mainWindow)
|
||||||
mainWindow.setTabOrder(self.clearButton, self.directoryButton)
|
# setupUi
|
||||||
mainWindow.setTabOrder(self.directoryButton, self.fileButton)
|
|
||||||
mainWindow.setTabOrder(self.fileButton, self.deviceBox)
|
|
||||||
mainWindow.setTabOrder(self.deviceBox, self.formatBox)
|
|
||||||
mainWindow.setTabOrder(self.formatBox, self.mangaBox)
|
|
||||||
mainWindow.setTabOrder(self.mangaBox, self.rotateBox)
|
|
||||||
mainWindow.setTabOrder(self.rotateBox, self.qualityBox)
|
|
||||||
mainWindow.setTabOrder(self.qualityBox, self.webtoonBox)
|
|
||||||
mainWindow.setTabOrder(self.webtoonBox, self.upscaleBox)
|
|
||||||
mainWindow.setTabOrder(self.upscaleBox, self.gammaBox)
|
|
||||||
mainWindow.setTabOrder(self.gammaBox, self.borderBox)
|
|
||||||
mainWindow.setTabOrder(self.borderBox, self.outputSplit)
|
|
||||||
mainWindow.setTabOrder(self.outputSplit, self.colorBox)
|
|
||||||
mainWindow.setTabOrder(self.colorBox, self.editorButton)
|
|
||||||
mainWindow.setTabOrder(self.editorButton, self.wikiButton)
|
|
||||||
mainWindow.setTabOrder(self.wikiButton, self.jobList)
|
|
||||||
mainWindow.setTabOrder(self.jobList, self.gammaSlider)
|
|
||||||
mainWindow.setTabOrder(self.gammaSlider, self.widthBox)
|
|
||||||
mainWindow.setTabOrder(self.widthBox, self.heightBox)
|
|
||||||
|
|
||||||
def retranslateUi(self, mainWindow):
|
def retranslateUi(self, mainWindow):
|
||||||
_translate = QtCore.QCoreApplication.translate
|
mainWindow.setWindowTitle(QCoreApplication.translate("mainWindow", u"Kindle Comic Converter", None))
|
||||||
mainWindow.setWindowTitle(_translate("mainWindow", "Kindle Comic Converter"))
|
#if QT_CONFIG(tooltip)
|
||||||
self.hLabel.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Resolution of the target device.</p></body></html>"))
|
self.qualityBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - 4 panels<br/></span>Zoom each corner separately.</p><p style='white-space:pre'><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - 2 panels<br/></span>Zoom only the top and bottom of the page.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - 4 high-quality panels<br/></span>Zoom each corner separately. Try to increase the quality of magnification. Check wiki for more details.</p></body></html>", None))
|
||||||
self.hLabel.setText(_translate("mainWindow", "Custom height:"))
|
#endif // QT_CONFIG(tooltip)
|
||||||
self.widthBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Resolution of the target device.</p></body></html>"))
|
self.qualityBox.setText(QCoreApplication.translate("mainWindow", u"Panel View 4/2/HQ", None))
|
||||||
self.wLabel.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Resolution of the target device.</p></body></html>"))
|
#if QT_CONFIG(tooltip)
|
||||||
self.wLabel.setText(_translate("mainWindow", "Custom width:"))
|
self.deleteBox.setToolTip(QCoreApplication.translate("mainWindow", u"Delete input file(s) or directory. It's not recoverable!", None))
|
||||||
self.heightBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Resolution of the target device.</p></body></html>"))
|
#endif // QT_CONFIG(tooltip)
|
||||||
self.mangaBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Enable right-to-left reading.</p></body></html>"))
|
self.deleteBox.setText(QCoreApplication.translate("mainWindow", u"Delete input", None))
|
||||||
self.mangaBox.setText(_translate("mainWindow", "Manga mode"))
|
#if QT_CONFIG(tooltip)
|
||||||
self.rotateBox.setToolTip(_translate("mainWindow", "<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Split<br/></span>Double page spreads will be cut into two separate pages.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - Rotate and split<br/></span>Double page spreads will be displayed twice. First rotated and then split. </p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Rotate<br/></span>Double page spreads will be rotated.</p></body></html>"))
|
self.maximizeStrips.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - 1x4<br/></span>Keep format 1x4 panels strips.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - 2x2<br/></span>Turn 1x4 strips to 2x2 to maximize screen usage.</p></body></html>", None))
|
||||||
self.rotateBox.setText(_translate("mainWindow", "Spread splitter"))
|
#endif // QT_CONFIG(tooltip)
|
||||||
self.qualityBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - 4 panels<br/></span>Zoom each corner separately.</p><p style=\'white-space:pre\'><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - 2 panels<br/></span>Zoom only the top and bottom of the page.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - 4 high-quality panels<br/></span>Zoom each corner separately. Try to increase the quality of magnification. Check wiki for more details.</p></body></html>"))
|
self.maximizeStrips.setText(QCoreApplication.translate("mainWindow", u"1x4 to 2x2 strips", None))
|
||||||
self.qualityBox.setText(_translate("mainWindow", "Panel View 4/2/HQ"))
|
#if QT_CONFIG(tooltip)
|
||||||
self.webtoonBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Enable special parsing mode for Korean Webtoons.</p></body></html>"))
|
self.gammaBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Disable automatic gamma correction.</p></body></html>", None))
|
||||||
self.webtoonBox.setText(_translate("mainWindow", "Webtoon mode"))
|
#endif // QT_CONFIG(tooltip)
|
||||||
self.upscaleBox.setToolTip(_translate("mainWindow", "<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Nothing<br/></span>Images smaller than device resolution will not be resized.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - Stretching<br/></span>Images smaller than device resolution will be resized. Aspect ratio will be not preserved.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Upscaling<br/></span>Images smaller than device resolution will be resized. Aspect ratio will be preserved.</p></body></html>"))
|
self.gammaBox.setText(QCoreApplication.translate("mainWindow", u"Custom gamma", None))
|
||||||
self.upscaleBox.setText(_translate("mainWindow", "Stretch/Upscale"))
|
#if QT_CONFIG(tooltip)
|
||||||
self.gammaBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Disable automatic gamma correction.</p></body></html>"))
|
self.borderBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Autodetection<br/></span>The color of margins fill will be detected automatically.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - White<br/></span>Margins will be filled with white color.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Black<br/></span>Margins will be filled with black color.</p></body></html>", None))
|
||||||
self.gammaBox.setText(_translate("mainWindow", "Custom gamma"))
|
#endif // QT_CONFIG(tooltip)
|
||||||
self.borderBox.setToolTip(_translate("mainWindow", "<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Autodetection<br/></span>The color of margins fill will be detected automatically.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - White<br/></span>Margins will be filled with white color.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Black<br/></span>Margins will be filled with black color.</p></body></html>"))
|
self.borderBox.setText(QCoreApplication.translate("mainWindow", u"W/B margins", None))
|
||||||
self.borderBox.setText(_translate("mainWindow", "W/B margins"))
|
#if QT_CONFIG(tooltip)
|
||||||
self.outputSplit.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Automatic mode<br/></span>The output will be split automatically.</p><p style=\'white-space:pre\'><span style=\" font-weight:600; text-decoration: underline;\">Checked - Volume mode<br/></span>Every subdirectory will be considered as a separate volume.</p></body></html>"))
|
self.webtoonBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Enable special parsing mode for Korean Webtoons.</p></body></html>", None))
|
||||||
self.outputSplit.setText(_translate("mainWindow", "Output split"))
|
#endif // QT_CONFIG(tooltip)
|
||||||
self.colorBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Disable conversion to grayscale.</p></body></html>"))
|
self.webtoonBox.setText(QCoreApplication.translate("mainWindow", u"Webtoon mode", None))
|
||||||
self.colorBox.setText(_translate("mainWindow", "Color mode"))
|
#if QT_CONFIG(tooltip)
|
||||||
self.gammaLabel.setText(_translate("mainWindow", "Gamma: Auto"))
|
self.upscaleBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Nothing<br/></span>Images smaller than device resolution will not be resized.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - Stretching<br/></span>Images smaller than device resolution will be resized. Aspect ratio will be not preserved.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Upscaling<br/></span>Images smaller than device resolution will be resized. Aspect ratio will be preserved.</p></body></html>", None))
|
||||||
self.editorButton.setText(_translate("mainWindow", "Editor"))
|
#endif // QT_CONFIG(tooltip)
|
||||||
self.wikiButton.setText(_translate("mainWindow", "Wiki"))
|
self.upscaleBox.setText(QCoreApplication.translate("mainWindow", u"Stretch/Upscale", None))
|
||||||
self.directoryButton.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Add directory containing JPG, PNG or GIF files to queue.<br/><span style=\" font-weight:600;\">CBR, CBZ and CB7 files inside will not be processed!</span></p></body></html>"))
|
#if QT_CONFIG(tooltip)
|
||||||
self.directoryButton.setText(_translate("mainWindow", "Add directory"))
|
self.mangaBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Enable right-to-left reading.</p></body></html>", None))
|
||||||
self.fileButton.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Add CBR, CBZ, CB7 or PDF file to queue.</p></body></html>"))
|
#endif // QT_CONFIG(tooltip)
|
||||||
self.fileButton.setText(_translate("mainWindow", "Add file"))
|
self.mangaBox.setText(QCoreApplication.translate("mainWindow", u"Manga mode", None))
|
||||||
self.deviceBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Target device.</p></body></html>"))
|
#if QT_CONFIG(tooltip)
|
||||||
self.formatBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Output format.</p></body></html>"))
|
self.rotateBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Split<br/></span>Double page spreads will be cut into two separate pages.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - Rotate and split<br/></span>Double page spreads will be displayed twice. First rotated and then split. </p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Rotate<br/></span>Double page spreads will be rotated.</p></body></html>", None))
|
||||||
self.convertButton.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Shift+Click to select the output directory.</p></body></html>"))
|
#endif // QT_CONFIG(tooltip)
|
||||||
self.convertButton.setText(_translate("mainWindow", "Convert"))
|
self.rotateBox.setText(QCoreApplication.translate("mainWindow", u"Spread splitter", None))
|
||||||
self.clearButton.setText(_translate("mainWindow", "Clear list"))
|
#if QT_CONFIG(tooltip)
|
||||||
|
self.croppingBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Disabled</span></p><p>Disabled</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - Margins<br/></span>Margins</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Margins + page numbers<br/></span>Margins +page numbers</p></body></html>", None))
|
||||||
|
#endif // QT_CONFIG(tooltip)
|
||||||
|
self.croppingBox.setText(QCoreApplication.translate("mainWindow", u"Cropping mode", None))
|
||||||
|
#if QT_CONFIG(tooltip)
|
||||||
|
self.outputSplit.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Automatic mode<br/></span>The output will be split automatically.</p><p style='white-space:pre'><span style=\" font-weight:600; text-decoration: underline;\">Checked - Volume mode<br/></span>Every subdirectory will be considered as a separate volume.</p></body></html>", None))
|
||||||
|
#endif // QT_CONFIG(tooltip)
|
||||||
|
self.outputSplit.setText(QCoreApplication.translate("mainWindow", u"Output split", None))
|
||||||
|
#if QT_CONFIG(tooltip)
|
||||||
|
self.mozJpegBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - JPEG<br/></span>Use JPEG files</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - force PNG<br/></span>Create PNG files instead JPEG</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - mozJpeg<br/></span>10-20% smaller JPEG file, with the same image quality, but processing time multiplied by 2</p></body></html>", None))
|
||||||
|
#endif // QT_CONFIG(tooltip)
|
||||||
|
self.mozJpegBox.setText(QCoreApplication.translate("mainWindow", u"JPEG/PNG/mozJpeg", None))
|
||||||
|
#if QT_CONFIG(tooltip)
|
||||||
|
self.colorBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Disable conversion to grayscale.</p></body></html>", None))
|
||||||
|
#endif // QT_CONFIG(tooltip)
|
||||||
|
self.colorBox.setText(QCoreApplication.translate("mainWindow", u"Color mode", None))
|
||||||
|
#if QT_CONFIG(tooltip)
|
||||||
|
self.disableProcessingBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><pre style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">Do not process any image, ignore profile and processing options</pre></body></html>", None))
|
||||||
|
#endif // QT_CONFIG(tooltip)
|
||||||
|
self.disableProcessingBox.setText(QCoreApplication.translate("mainWindow", u"Disable processing", None))
|
||||||
|
#if QT_CONFIG(tooltip)
|
||||||
|
self.dedupeCoverBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p>Don't duplicate the first page as the cover. Useful for 2 page spread alignment.</p></body></html>", None))
|
||||||
|
#endif // QT_CONFIG(tooltip)
|
||||||
|
self.dedupeCoverBox.setText(QCoreApplication.translate("mainWindow", u"De-dupe cover", None))
|
||||||
|
self.gammaLabel.setText(QCoreApplication.translate("mainWindow", u"Gamma: Auto", None))
|
||||||
|
self.croppingPowerLabel.setText(QCoreApplication.translate("mainWindow", u"Cropping power:", None))
|
||||||
|
#if QT_CONFIG(tooltip)
|
||||||
|
self.directoryButton.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Add directory containing JPG, PNG or GIF files to queue.<br/><span style=\" font-weight:600;\">CBR, CBZ and CB7 files inside will not be processed!</span></p></body></html>", None))
|
||||||
|
#endif // QT_CONFIG(tooltip)
|
||||||
|
self.directoryButton.setText(QCoreApplication.translate("mainWindow", u"Add directory", None))
|
||||||
|
#if QT_CONFIG(tooltip)
|
||||||
|
self.fileButton.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Add CBR, CBZ, CB7 or PDF file to queue.</p></body></html>", None))
|
||||||
|
#endif // QT_CONFIG(tooltip)
|
||||||
|
self.fileButton.setText(QCoreApplication.translate("mainWindow", u"Add file", None))
|
||||||
|
#if QT_CONFIG(tooltip)
|
||||||
|
self.deviceBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Target device.</p></body></html>", None))
|
||||||
|
#endif // QT_CONFIG(tooltip)
|
||||||
|
#if QT_CONFIG(tooltip)
|
||||||
|
self.formatBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Output format.</p></body></html>", None))
|
||||||
|
#endif // QT_CONFIG(tooltip)
|
||||||
|
#if QT_CONFIG(tooltip)
|
||||||
|
self.convertButton.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Shift+Click to select the output directory.</p></body></html>", None))
|
||||||
|
#endif // QT_CONFIG(tooltip)
|
||||||
|
self.convertButton.setText(QCoreApplication.translate("mainWindow", u"Convert", None))
|
||||||
|
self.clearButton.setText(QCoreApplication.translate("mainWindow", u"Clear list", None))
|
||||||
|
#if QT_CONFIG(tooltip)
|
||||||
|
self.editorButton.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Shift+Click to edit directory.</p></body></html>", None))
|
||||||
|
#endif // QT_CONFIG(tooltip)
|
||||||
|
self.editorButton.setText(QCoreApplication.translate("mainWindow", u"Editor", None))
|
||||||
|
self.wikiButton.setText(QCoreApplication.translate("mainWindow", u"Wiki", None))
|
||||||
|
#if QT_CONFIG(tooltip)
|
||||||
|
self.hLabel.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Resolution of the target device.</p></body></html>", None))
|
||||||
|
#endif // QT_CONFIG(tooltip)
|
||||||
|
self.hLabel.setText(QCoreApplication.translate("mainWindow", u"Custom height:", None))
|
||||||
|
#if QT_CONFIG(tooltip)
|
||||||
|
self.widthBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Resolution of the target device.</p></body></html>", None))
|
||||||
|
#endif // QT_CONFIG(tooltip)
|
||||||
|
#if QT_CONFIG(tooltip)
|
||||||
|
self.wLabel.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Resolution of the target device.</p></body></html>", None))
|
||||||
|
#endif // QT_CONFIG(tooltip)
|
||||||
|
self.wLabel.setText(QCoreApplication.translate("mainWindow", u"Custom width:", None))
|
||||||
|
#if QT_CONFIG(tooltip)
|
||||||
|
self.heightBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Resolution of the target device.</p></body></html>", None))
|
||||||
|
#endif // QT_CONFIG(tooltip)
|
||||||
|
# retranslateUi
|
||||||
|
|
||||||
from . import KCC_rc
|
|
||||||
|
|||||||
@@ -1,124 +1,168 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Form implementation generated from reading ui file 'gui\MetaEditor.ui'
|
################################################################################
|
||||||
#
|
## Form generated from reading UI file 'MetaEditor.ui'
|
||||||
# Created by: PyQt5 UI code generator 5.6
|
##
|
||||||
#
|
## Created by: Qt User Interface Compiler version 6.5.2
|
||||||
# WARNING! All changes made in this file will be lost!
|
##
|
||||||
|
## WARNING! All changes made in this file will be lost when recompiling UI file!
|
||||||
|
################################################################################
|
||||||
|
|
||||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
|
||||||
|
QMetaObject, QObject, QPoint, QRect,
|
||||||
|
QSize, QTime, QUrl, Qt)
|
||||||
|
from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
|
||||||
|
QFont, QFontDatabase, QGradient, QIcon,
|
||||||
|
QImage, QKeySequence, QLinearGradient, QPainter,
|
||||||
|
QPalette, QPixmap, QRadialGradient, QTransform)
|
||||||
|
from PySide6.QtWidgets import (QApplication, QDialog, QGridLayout, QHBoxLayout,
|
||||||
|
QLabel, QLineEdit, QPushButton, QSizePolicy,
|
||||||
|
QVBoxLayout, QWidget)
|
||||||
|
from . import KCC_rc
|
||||||
|
|
||||||
class Ui_editorDialog(object):
|
class Ui_editorDialog(object):
|
||||||
def setupUi(self, editorDialog):
|
def setupUi(self, editorDialog):
|
||||||
editorDialog.setObjectName("editorDialog")
|
if not editorDialog.objectName():
|
||||||
|
editorDialog.setObjectName(u"editorDialog")
|
||||||
editorDialog.resize(400, 260)
|
editorDialog.resize(400, 260)
|
||||||
editorDialog.setMinimumSize(QtCore.QSize(400, 260))
|
editorDialog.setMinimumSize(QSize(400, 260))
|
||||||
icon = QtGui.QIcon()
|
icon = QIcon()
|
||||||
icon.addPixmap(QtGui.QPixmap(":/Icon/icons/comic2ebook.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
icon.addFile(u":/Icon/icons/comic2ebook.png", QSize(), QIcon.Normal, QIcon.Off)
|
||||||
editorDialog.setWindowIcon(icon)
|
editorDialog.setWindowIcon(icon)
|
||||||
self.verticalLayout = QtWidgets.QVBoxLayout(editorDialog)
|
self.verticalLayout = QVBoxLayout(editorDialog)
|
||||||
|
self.verticalLayout.setObjectName(u"verticalLayout")
|
||||||
self.verticalLayout.setContentsMargins(-1, -1, -1, 5)
|
self.verticalLayout.setContentsMargins(-1, -1, -1, 5)
|
||||||
self.verticalLayout.setObjectName("verticalLayout")
|
self.editorWidget = QWidget(editorDialog)
|
||||||
self.editorWidget = QtWidgets.QWidget(editorDialog)
|
self.editorWidget.setObjectName(u"editorWidget")
|
||||||
self.editorWidget.setObjectName("editorWidget")
|
self.gridLayout = QGridLayout(self.editorWidget)
|
||||||
self.gridLayout = QtWidgets.QGridLayout(self.editorWidget)
|
self.gridLayout.setObjectName(u"gridLayout")
|
||||||
self.gridLayout.setContentsMargins(0, 0, 0, 0)
|
self.gridLayout.setContentsMargins(0, 0, 0, 0)
|
||||||
self.gridLayout.setObjectName("gridLayout")
|
self.label_1 = QLabel(self.editorWidget)
|
||||||
self.label_1 = QtWidgets.QLabel(self.editorWidget)
|
self.label_1.setObjectName(u"label_1")
|
||||||
self.label_1.setObjectName("label_1")
|
|
||||||
self.gridLayout.addWidget(self.label_1, 0, 0, 1, 1)
|
self.gridLayout.addWidget(self.label_1, 0, 0, 1, 1)
|
||||||
self.seriesLine = QtWidgets.QLineEdit(self.editorWidget)
|
|
||||||
self.seriesLine.setObjectName("seriesLine")
|
self.seriesLine = QLineEdit(self.editorWidget)
|
||||||
|
self.seriesLine.setObjectName(u"seriesLine")
|
||||||
|
|
||||||
self.gridLayout.addWidget(self.seriesLine, 0, 1, 1, 1)
|
self.gridLayout.addWidget(self.seriesLine, 0, 1, 1, 1)
|
||||||
self.label_2 = QtWidgets.QLabel(self.editorWidget)
|
|
||||||
self.label_2.setObjectName("label_2")
|
self.label_2 = QLabel(self.editorWidget)
|
||||||
|
self.label_2.setObjectName(u"label_2")
|
||||||
|
|
||||||
self.gridLayout.addWidget(self.label_2, 1, 0, 1, 1)
|
self.gridLayout.addWidget(self.label_2, 1, 0, 1, 1)
|
||||||
self.volumeLine = QtWidgets.QLineEdit(self.editorWidget)
|
|
||||||
self.volumeLine.setObjectName("volumeLine")
|
self.volumeLine = QLineEdit(self.editorWidget)
|
||||||
|
self.volumeLine.setObjectName(u"volumeLine")
|
||||||
|
|
||||||
self.gridLayout.addWidget(self.volumeLine, 1, 1, 1, 1)
|
self.gridLayout.addWidget(self.volumeLine, 1, 1, 1, 1)
|
||||||
self.label_3 = QtWidgets.QLabel(self.editorWidget)
|
|
||||||
self.label_3.setObjectName("label_3")
|
self.label_3 = QLabel(self.editorWidget)
|
||||||
|
self.label_3.setObjectName(u"label_3")
|
||||||
|
|
||||||
self.gridLayout.addWidget(self.label_3, 2, 0, 1, 1)
|
self.gridLayout.addWidget(self.label_3, 2, 0, 1, 1)
|
||||||
self.numberLine = QtWidgets.QLineEdit(self.editorWidget)
|
|
||||||
self.numberLine.setObjectName("numberLine")
|
self.numberLine = QLineEdit(self.editorWidget)
|
||||||
|
self.numberLine.setObjectName(u"numberLine")
|
||||||
|
|
||||||
self.gridLayout.addWidget(self.numberLine, 2, 1, 1, 1)
|
self.gridLayout.addWidget(self.numberLine, 2, 1, 1, 1)
|
||||||
self.label_4 = QtWidgets.QLabel(self.editorWidget)
|
|
||||||
self.label_4.setObjectName("label_4")
|
self.label_4 = QLabel(self.editorWidget)
|
||||||
|
self.label_4.setObjectName(u"label_4")
|
||||||
|
|
||||||
self.gridLayout.addWidget(self.label_4, 3, 0, 1, 1)
|
self.gridLayout.addWidget(self.label_4, 3, 0, 1, 1)
|
||||||
self.writerLine = QtWidgets.QLineEdit(self.editorWidget)
|
|
||||||
self.writerLine.setObjectName("writerLine")
|
self.writerLine = QLineEdit(self.editorWidget)
|
||||||
|
self.writerLine.setObjectName(u"writerLine")
|
||||||
|
|
||||||
self.gridLayout.addWidget(self.writerLine, 3, 1, 1, 1)
|
self.gridLayout.addWidget(self.writerLine, 3, 1, 1, 1)
|
||||||
self.label_5 = QtWidgets.QLabel(self.editorWidget)
|
|
||||||
self.label_5.setObjectName("label_5")
|
self.label_5 = QLabel(self.editorWidget)
|
||||||
|
self.label_5.setObjectName(u"label_5")
|
||||||
|
|
||||||
self.gridLayout.addWidget(self.label_5, 4, 0, 1, 1)
|
self.gridLayout.addWidget(self.label_5, 4, 0, 1, 1)
|
||||||
self.pencillerLine = QtWidgets.QLineEdit(self.editorWidget)
|
|
||||||
self.pencillerLine.setObjectName("pencillerLine")
|
self.pencillerLine = QLineEdit(self.editorWidget)
|
||||||
|
self.pencillerLine.setObjectName(u"pencillerLine")
|
||||||
|
|
||||||
self.gridLayout.addWidget(self.pencillerLine, 4, 1, 1, 1)
|
self.gridLayout.addWidget(self.pencillerLine, 4, 1, 1, 1)
|
||||||
self.label_6 = QtWidgets.QLabel(self.editorWidget)
|
|
||||||
self.label_6.setObjectName("label_6")
|
self.label_6 = QLabel(self.editorWidget)
|
||||||
|
self.label_6.setObjectName(u"label_6")
|
||||||
|
|
||||||
self.gridLayout.addWidget(self.label_6, 5, 0, 1, 1)
|
self.gridLayout.addWidget(self.label_6, 5, 0, 1, 1)
|
||||||
self.inkerLine = QtWidgets.QLineEdit(self.editorWidget)
|
|
||||||
self.inkerLine.setObjectName("inkerLine")
|
self.inkerLine = QLineEdit(self.editorWidget)
|
||||||
|
self.inkerLine.setObjectName(u"inkerLine")
|
||||||
|
|
||||||
self.gridLayout.addWidget(self.inkerLine, 5, 1, 1, 1)
|
self.gridLayout.addWidget(self.inkerLine, 5, 1, 1, 1)
|
||||||
self.label_7 = QtWidgets.QLabel(self.editorWidget)
|
|
||||||
self.label_7.setObjectName("label_7")
|
self.label_7 = QLabel(self.editorWidget)
|
||||||
|
self.label_7.setObjectName(u"label_7")
|
||||||
|
|
||||||
self.gridLayout.addWidget(self.label_7, 6, 0, 1, 1)
|
self.gridLayout.addWidget(self.label_7, 6, 0, 1, 1)
|
||||||
self.coloristLine = QtWidgets.QLineEdit(self.editorWidget)
|
|
||||||
self.coloristLine.setObjectName("coloristLine")
|
self.coloristLine = QLineEdit(self.editorWidget)
|
||||||
|
self.coloristLine.setObjectName(u"coloristLine")
|
||||||
|
|
||||||
self.gridLayout.addWidget(self.coloristLine, 6, 1, 1, 1)
|
self.gridLayout.addWidget(self.coloristLine, 6, 1, 1, 1)
|
||||||
self.label_8 = QtWidgets.QLabel(self.editorWidget)
|
|
||||||
self.label_8.setOpenExternalLinks(True)
|
|
||||||
self.label_8.setObjectName("label_8")
|
|
||||||
self.gridLayout.addWidget(self.label_8, 7, 0, 1, 1)
|
|
||||||
self.muidLine = QtWidgets.QLineEdit(self.editorWidget)
|
|
||||||
self.muidLine.setObjectName("muidLine")
|
|
||||||
self.gridLayout.addWidget(self.muidLine, 7, 1, 1, 1)
|
|
||||||
self.verticalLayout.addWidget(self.editorWidget)
|
self.verticalLayout.addWidget(self.editorWidget)
|
||||||
self.optionWidget = QtWidgets.QWidget(editorDialog)
|
|
||||||
self.optionWidget.setObjectName("optionWidget")
|
self.optionWidget = QWidget(editorDialog)
|
||||||
self.horizontalLayout = QtWidgets.QHBoxLayout(self.optionWidget)
|
self.optionWidget.setObjectName(u"optionWidget")
|
||||||
|
self.horizontalLayout = QHBoxLayout(self.optionWidget)
|
||||||
|
self.horizontalLayout.setObjectName(u"horizontalLayout")
|
||||||
self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
|
self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
|
||||||
self.horizontalLayout.setObjectName("horizontalLayout")
|
self.statusLabel = QLabel(self.optionWidget)
|
||||||
self.statusLabel = QtWidgets.QLabel(self.optionWidget)
|
self.statusLabel.setObjectName(u"statusLabel")
|
||||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding)
|
sizePolicy = QSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding)
|
||||||
sizePolicy.setHorizontalStretch(0)
|
sizePolicy.setHorizontalStretch(0)
|
||||||
sizePolicy.setVerticalStretch(0)
|
sizePolicy.setVerticalStretch(0)
|
||||||
sizePolicy.setHeightForWidth(self.statusLabel.sizePolicy().hasHeightForWidth())
|
sizePolicy.setHeightForWidth(self.statusLabel.sizePolicy().hasHeightForWidth())
|
||||||
self.statusLabel.setSizePolicy(sizePolicy)
|
self.statusLabel.setSizePolicy(sizePolicy)
|
||||||
self.statusLabel.setText("")
|
|
||||||
self.statusLabel.setObjectName("statusLabel")
|
|
||||||
self.horizontalLayout.addWidget(self.statusLabel)
|
self.horizontalLayout.addWidget(self.statusLabel)
|
||||||
self.okButton = QtWidgets.QPushButton(self.optionWidget)
|
|
||||||
self.okButton.setMinimumSize(QtCore.QSize(0, 30))
|
self.okButton = QPushButton(self.optionWidget)
|
||||||
icon1 = QtGui.QIcon()
|
self.okButton.setObjectName(u"okButton")
|
||||||
icon1.addPixmap(QtGui.QPixmap(":/Other/icons/convert.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
self.okButton.setMinimumSize(QSize(0, 30))
|
||||||
|
icon1 = QIcon()
|
||||||
|
icon1.addFile(u":/Other/icons/convert.png", QSize(), QIcon.Normal, QIcon.Off)
|
||||||
self.okButton.setIcon(icon1)
|
self.okButton.setIcon(icon1)
|
||||||
self.okButton.setObjectName("okButton")
|
|
||||||
self.horizontalLayout.addWidget(self.okButton)
|
self.horizontalLayout.addWidget(self.okButton)
|
||||||
self.cancelButton = QtWidgets.QPushButton(self.optionWidget)
|
|
||||||
self.cancelButton.setMinimumSize(QtCore.QSize(0, 30))
|
self.cancelButton = QPushButton(self.optionWidget)
|
||||||
icon2 = QtGui.QIcon()
|
self.cancelButton.setObjectName(u"cancelButton")
|
||||||
icon2.addPixmap(QtGui.QPixmap(":/Other/icons/clear.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
self.cancelButton.setMinimumSize(QSize(0, 30))
|
||||||
|
icon2 = QIcon()
|
||||||
|
icon2.addFile(u":/Other/icons/clear.png", QSize(), QIcon.Normal, QIcon.Off)
|
||||||
self.cancelButton.setIcon(icon2)
|
self.cancelButton.setIcon(icon2)
|
||||||
self.cancelButton.setObjectName("cancelButton")
|
|
||||||
self.horizontalLayout.addWidget(self.cancelButton)
|
self.horizontalLayout.addWidget(self.cancelButton)
|
||||||
|
|
||||||
|
|
||||||
self.verticalLayout.addWidget(self.optionWidget)
|
self.verticalLayout.addWidget(self.optionWidget)
|
||||||
|
|
||||||
|
|
||||||
self.retranslateUi(editorDialog)
|
self.retranslateUi(editorDialog)
|
||||||
QtCore.QMetaObject.connectSlotsByName(editorDialog)
|
|
||||||
|
QMetaObject.connectSlotsByName(editorDialog)
|
||||||
|
# setupUi
|
||||||
|
|
||||||
def retranslateUi(self, editorDialog):
|
def retranslateUi(self, editorDialog):
|
||||||
_translate = QtCore.QCoreApplication.translate
|
editorDialog.setWindowTitle(QCoreApplication.translate("editorDialog", u"Metadata editor", None))
|
||||||
editorDialog.setWindowTitle(_translate("editorDialog", "Metadata editor"))
|
self.label_1.setText(QCoreApplication.translate("editorDialog", u"Series:", None))
|
||||||
self.label_1.setText(_translate("editorDialog", "Series:"))
|
self.label_2.setText(QCoreApplication.translate("editorDialog", u"Volume:", None))
|
||||||
self.label_2.setText(_translate("editorDialog", "Volume:"))
|
self.label_3.setText(QCoreApplication.translate("editorDialog", u"Number:", None))
|
||||||
self.label_3.setText(_translate("editorDialog", "Number:"))
|
self.label_4.setText(QCoreApplication.translate("editorDialog", u"Writer:", None))
|
||||||
self.label_4.setText(_translate("editorDialog", "Writer:"))
|
self.label_5.setText(QCoreApplication.translate("editorDialog", u"Penciller:", None))
|
||||||
self.label_5.setText(_translate("editorDialog", "Penciller:"))
|
self.label_6.setText(QCoreApplication.translate("editorDialog", u"Inker:", None))
|
||||||
self.label_6.setText(_translate("editorDialog", "Inker:"))
|
self.label_7.setText(QCoreApplication.translate("editorDialog", u"Colorist:", None))
|
||||||
self.label_7.setText(_translate("editorDialog", "Colorist:"))
|
self.statusLabel.setText("")
|
||||||
self.label_8.setText(_translate("editorDialog", "<html><head/><body><p><a href=\"https://github.com/ciromattia/kcc/wiki/Manga-Cover-Database-support\"><span style=\" text-decoration: underline; color:#0000ff;\">MUid:</span></a></p></body></html>"))
|
self.okButton.setText(QCoreApplication.translate("editorDialog", u"Save", None))
|
||||||
self.okButton.setText(_translate("editorDialog", "Save"))
|
self.cancelButton.setText(QCoreApplication.translate("editorDialog", u"Cancel", None))
|
||||||
self.cancelButton.setText(_translate("editorDialog", "Cancel"))
|
# retranslateUi
|
||||||
|
|
||||||
from . import KCC_rc
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
__version__ = '5.4.1'
|
__version__ = '6.1.0'
|
||||||
__license__ = 'ISC'
|
__license__ = 'ISC'
|
||||||
__copyright__ = '2012-2017, 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'
|
||||||
|
|||||||
@@ -1,90 +0,0 @@
|
|||||||
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
|
||||||
# Copyright (c) 2013-2017 Pawel Jastrzebski <pawelj@iosphe.re>
|
|
||||||
#
|
|
||||||
# Permission to use, copy, modify, and/or distribute this software for
|
|
||||||
# any purpose with or without fee is hereby granted, provided that the
|
|
||||||
# above copyright notice and this permission notice appear in all
|
|
||||||
# copies.
|
|
||||||
#
|
|
||||||
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
|
||||||
# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
|
||||||
# WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
|
|
||||||
# AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
|
||||||
# DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
|
|
||||||
# OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
|
||||||
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
||||||
# PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
#
|
|
||||||
|
|
||||||
import os
|
|
||||||
from zipfile import is_zipfile, ZipFile
|
|
||||||
from subprocess import STDOUT, PIPE
|
|
||||||
from psutil import Popen
|
|
||||||
from shutil import move
|
|
||||||
from . import rarfile
|
|
||||||
from .shared import check7ZFile as is_7zfile
|
|
||||||
|
|
||||||
|
|
||||||
class CBxArchive:
|
|
||||||
def __init__(self, origFileName):
|
|
||||||
self.origFileName = origFileName
|
|
||||||
if is_zipfile(origFileName):
|
|
||||||
self.compressor = 'zip'
|
|
||||||
elif rarfile.is_rarfile(origFileName):
|
|
||||||
self.compressor = 'rar'
|
|
||||||
elif is_7zfile(origFileName):
|
|
||||||
self.compressor = '7z'
|
|
||||||
else:
|
|
||||||
self.compressor = None
|
|
||||||
|
|
||||||
def isCbxFile(self):
|
|
||||||
return self.compressor is not None
|
|
||||||
|
|
||||||
def extractCBZ(self, targetdir):
|
|
||||||
cbzFile = ZipFile(self.origFileName)
|
|
||||||
filelist = []
|
|
||||||
for f in cbzFile.namelist():
|
|
||||||
if f.startswith('__MACOSX') or f.endswith('.DS_Store') or f.endswith('humbs.db'):
|
|
||||||
pass
|
|
||||||
elif f.endswith('/'):
|
|
||||||
try:
|
|
||||||
os.makedirs(os.path.join(targetdir, f))
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
filelist.append(f)
|
|
||||||
cbzFile.extractall(targetdir, filelist)
|
|
||||||
|
|
||||||
def extractCBR(self, targetdir):
|
|
||||||
cbrFile = rarfile.RarFile(self.origFileName)
|
|
||||||
cbrFile.extractall(targetdir)
|
|
||||||
for root, _, filenames in os.walk(targetdir):
|
|
||||||
for filename in filenames:
|
|
||||||
if filename.startswith('__MACOSX') or filename.endswith('.DS_Store') or filename.endswith('humbs.db'):
|
|
||||||
os.remove(os.path.join(root, filename))
|
|
||||||
|
|
||||||
def extractCB7(self, targetdir):
|
|
||||||
output = Popen('7za x "' + self.origFileName + '" -xr!__MACOSX -xr!.DS_Store -xr!thumbs.db -xr!Thumbs.db -o"' +
|
|
||||||
targetdir + '"', stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
|
|
||||||
extracted = False
|
|
||||||
for line in output.stdout:
|
|
||||||
if b"Everything is Ok" in line:
|
|
||||||
extracted = True
|
|
||||||
if not extracted:
|
|
||||||
raise OSError
|
|
||||||
|
|
||||||
def extract(self, targetdir):
|
|
||||||
if self.compressor == 'rar':
|
|
||||||
self.extractCBR(targetdir)
|
|
||||||
elif self.compressor == 'zip':
|
|
||||||
self.extractCBZ(targetdir)
|
|
||||||
elif self.compressor == '7z':
|
|
||||||
self.extractCB7(targetdir)
|
|
||||||
adir = os.listdir(targetdir)
|
|
||||||
if 'ComicInfo.xml' in adir:
|
|
||||||
adir.remove('ComicInfo.xml')
|
|
||||||
if len(adir) == 1 and os.path.isdir(os.path.join(targetdir, adir[0])):
|
|
||||||
for f in os.listdir(os.path.join(targetdir, adir[0])):
|
|
||||||
move(os.path.join(targetdir, adir[0], f), targetdir)
|
|
||||||
os.rmdir(os.path.join(targetdir, adir[0]))
|
|
||||||
return targetdir
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
||||||
# Copyright (c) 2013-2017 Pawel Jastrzebski <pawelj@iosphe.re>
|
# Copyright (c) 2013-2019 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||||
#
|
#
|
||||||
# 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
|
||||||
@@ -20,15 +20,11 @@
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
from argparse import ArgumentParser
|
||||||
from shutil import rmtree, copytree, move
|
from shutil import rmtree, copytree, move
|
||||||
from optparse import OptionParser, OptionGroup
|
|
||||||
from multiprocessing import Pool
|
from multiprocessing import Pool
|
||||||
from PIL import Image, ImageChops, ImageOps, ImageDraw
|
from PIL import Image, ImageChops, ImageOps, ImageDraw
|
||||||
from .shared import getImageFileName, walkLevel, walkSort, sanitizeTrace
|
from .shared import getImageFileName, walkLevel, walkSort, sanitizeTrace
|
||||||
try:
|
|
||||||
from PyQt5 import QtCore
|
|
||||||
except ImportError:
|
|
||||||
QtCore = None
|
|
||||||
|
|
||||||
|
|
||||||
def mergeDirectoryTick(output):
|
def mergeDirectoryTick(output):
|
||||||
@@ -57,9 +53,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 +63,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)
|
||||||
@@ -93,12 +90,15 @@ def splitImageTick(output):
|
|||||||
splitWorkerPool.terminate()
|
splitWorkerPool.terminate()
|
||||||
|
|
||||||
|
|
||||||
|
# noinspection PyUnboundLocalVariable
|
||||||
def splitImage(work):
|
def splitImage(work):
|
||||||
try:
|
try:
|
||||||
path = work[0]
|
path = work[0]
|
||||||
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
|
||||||
@@ -112,11 +112,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
|
||||||
@@ -140,9 +145,7 @@ def splitImage(work):
|
|||||||
|
|
||||||
if opt.debug:
|
if opt.debug:
|
||||||
for panel in panelsProcessed:
|
for panel in panelsProcessed:
|
||||||
# noinspection PyUnboundLocalVariable
|
draw.rectangle(((0, panel[0]), (widthImg, panel[1])), (0, 255, 0, 128), (0, 0, 255, 255))
|
||||||
draw.rectangle([(0, panel[0]), (widthImg, panel[1])], (0, 255, 0, 128), (0, 0, 255, 255))
|
|
||||||
# noinspection PyUnboundLocalVariable
|
|
||||||
debugImage = Image.alpha_composite(imgOrg.convert(mode='RGBA'), drawImg)
|
debugImage = Image.alpha_composite(imgOrg.convert(mode='RGBA'), drawImg)
|
||||||
debugImage.save(os.path.join(path, os.path.splitext(name)[0] + '-debug.png'), 'PNG')
|
debugImage.save(os.path.join(path, os.path.splitext(name)[0] + '-debug.png'), 'PNG')
|
||||||
|
|
||||||
@@ -175,7 +178,7 @@ def splitImage(work):
|
|||||||
if pageHeight > 15:
|
if pageHeight > 15:
|
||||||
newPage = Image.new('RGB', (widthImg, pageHeight))
|
newPage = Image.new('RGB', (widthImg, pageHeight))
|
||||||
for panel in page:
|
for panel in page:
|
||||||
panelImg = imgOrg.crop([0, panelsProcessed[panel][0], widthImg, panelsProcessed[panel][1]])
|
panelImg = imgOrg.crop((0, panelsProcessed[panel][0], widthImg, panelsProcessed[panel][1]))
|
||||||
newPage.paste(panelImg, (0, targetHeight))
|
newPage.paste(panelImg, (0, targetHeight))
|
||||||
targetHeight += panelsProcessed[panel][2]
|
targetHeight += panelsProcessed[panel][2]
|
||||||
newPage.save(os.path.join(path, os.path.splitext(name)[0] + '-' + str(pageNumber) + '.png'), 'PNG')
|
newPage.save(os.path.join(path, os.path.splitext(name)[0] + '-' + str(pageNumber) + '.png'), 'PNG')
|
||||||
@@ -185,98 +188,101 @@ def splitImage(work):
|
|||||||
return str(sys.exc_info()[1]), sanitizeTrace(sys.exc_info()[2])
|
return str(sys.exc_info()[1]), sanitizeTrace(sys.exc_info()[2])
|
||||||
|
|
||||||
|
|
||||||
def main(argv=None, qtGUI=None):
|
def main(argv=None, qtgui=None):
|
||||||
global options, GUI, splitWorkerPool, splitWorkerOutput, mergeWorkerPool, mergeWorkerOutput
|
global args, GUI, splitWorkerPool, splitWorkerOutput, mergeWorkerPool, mergeWorkerOutput
|
||||||
parser = OptionParser(usage="Usage: kcc-c2p [options] comic_folder", add_help_option=False)
|
parser = ArgumentParser(prog="kcc-c2p", usage="kcc-c2p [options] [input]", add_help=False)
|
||||||
mainOptions = OptionGroup(parser, "MANDATORY")
|
|
||||||
otherOptions = OptionGroup(parser, "OTHER")
|
mandatory_options = parser.add_argument_group("MANDATORY")
|
||||||
mainOptions.add_option("-y", "--height", type="int", dest="height", default=0,
|
main_options = parser.add_argument_group("MAIN")
|
||||||
help="Height of the target device screen")
|
other_options = parser.add_argument_group("OTHER")
|
||||||
mainOptions.add_option("-i", "--in-place", action="store_true", dest="inPlace", default=False,
|
mandatory_options.add_argument("input", action="extend", nargs="*", default=None,
|
||||||
help="Overwrite source directory")
|
help="Full path to comic folder(s) to be processed. Separate multiple inputs"
|
||||||
mainOptions.add_option("-m", "--merge", action="store_true", dest="merge", default=False,
|
" with spaces.")
|
||||||
help="Combine every directory into a single image before splitting")
|
main_options.add_argument("-y", "--height", type=int, dest="height", default=0,
|
||||||
otherOptions.add_option("-d", "--debug", action="store_true", dest="debug", default=False,
|
help="Height of the target device screen")
|
||||||
help="Create debug file for every split image")
|
main_options.add_argument("-i", "--in-place", action="store_true", dest="inPlace", default=False,
|
||||||
otherOptions.add_option("-h", "--help", action="help",
|
help="Overwrite source directory")
|
||||||
help="Show this help message and exit")
|
main_options.add_argument("-m", "--merge", action="store_true", dest="merge", default=False,
|
||||||
parser.add_option_group(mainOptions)
|
help="Combine every directory into a single image before splitting")
|
||||||
parser.add_option_group(otherOptions)
|
other_options.add_argument("-d", "--debug", action="store_true", dest="debug", default=False,
|
||||||
options, args = parser.parse_args(argv)
|
help="Create debug file for every split image")
|
||||||
if qtGUI:
|
other_options.add_argument("-h", "--help", action="help",
|
||||||
GUI = qtGUI
|
help="Show this help message and exit")
|
||||||
|
args = parser.parse_args(argv)
|
||||||
|
if qtgui:
|
||||||
|
GUI = qtgui
|
||||||
else:
|
else:
|
||||||
GUI = None
|
GUI = None
|
||||||
if len(args) != 1:
|
if not argv or args.input == []:
|
||||||
parser.print_help()
|
parser.print_help()
|
||||||
return 1
|
return 1
|
||||||
if options.height > 0:
|
if args.height > 0:
|
||||||
options.sourceDir = args[0]
|
for sourceDir in args.input:
|
||||||
options.targetDir = args[0] + "-Splitted"
|
targetDir = sourceDir + "-Splitted"
|
||||||
if os.path.isdir(options.sourceDir):
|
if os.path.isdir(sourceDir):
|
||||||
rmtree(options.targetDir, True)
|
rmtree(targetDir, True)
|
||||||
copytree(options.sourceDir, options.targetDir)
|
copytree(sourceDir, targetDir)
|
||||||
work = []
|
work = []
|
||||||
pagenumber = 1
|
pagenumber = 1
|
||||||
splitWorkerOutput = []
|
splitWorkerOutput = []
|
||||||
splitWorkerPool = Pool(maxtasksperchild=10)
|
splitWorkerPool = Pool(maxtasksperchild=10)
|
||||||
if options.merge:
|
if args.merge:
|
||||||
print("Merging images...")
|
print("Merging images...")
|
||||||
directoryNumer = 1
|
directoryNumer = 1
|
||||||
mergeWork = []
|
mergeWork = []
|
||||||
mergeWorkerOutput = []
|
mergeWorkerOutput = []
|
||||||
mergeWorkerPool = Pool(maxtasksperchild=10)
|
mergeWorkerPool = Pool(maxtasksperchild=10)
|
||||||
mergeWork.append([options.targetDir])
|
mergeWork.append([targetDir])
|
||||||
for root, dirs, files in os.walk(options.targetDir, False):
|
for root, dirs, files in os.walk(targetDir, False):
|
||||||
dirs, files = walkSort(dirs, files)
|
dirs, files = walkSort(dirs, files)
|
||||||
for directory in dirs:
|
for directory in dirs:
|
||||||
directoryNumer += 1
|
directoryNumer += 1
|
||||||
mergeWork.append([os.path.join(root, directory)])
|
mergeWork.append([os.path.join(root, directory)])
|
||||||
|
if GUI:
|
||||||
|
GUI.progressBarTick.emit('Combining images')
|
||||||
|
GUI.progressBarTick.emit(str(directoryNumer))
|
||||||
|
for i in mergeWork:
|
||||||
|
mergeWorkerPool.apply_async(func=mergeDirectory, args=(i, ), callback=mergeDirectoryTick)
|
||||||
|
mergeWorkerPool.close()
|
||||||
|
mergeWorkerPool.join()
|
||||||
|
if GUI and not GUI.conversionAlive:
|
||||||
|
rmtree(targetDir, True)
|
||||||
|
raise UserWarning("Conversion interrupted.")
|
||||||
|
if len(mergeWorkerOutput) > 0:
|
||||||
|
rmtree(targetDir, True)
|
||||||
|
raise RuntimeError("One of workers crashed. Cause: " + mergeWorkerOutput[0][0],
|
||||||
|
mergeWorkerOutput[0][1])
|
||||||
|
print("Splitting images...")
|
||||||
|
for root, _, files in os.walk(targetDir, False):
|
||||||
|
for name in files:
|
||||||
|
if getImageFileName(name) is not None:
|
||||||
|
pagenumber += 1
|
||||||
|
work.append([root, name, args])
|
||||||
|
else:
|
||||||
|
os.remove(os.path.join(root, name))
|
||||||
if GUI:
|
if GUI:
|
||||||
GUI.progressBarTick.emit('Combining images')
|
GUI.progressBarTick.emit('Splitting images')
|
||||||
GUI.progressBarTick.emit(str(directoryNumer))
|
GUI.progressBarTick.emit(str(pagenumber))
|
||||||
for i in mergeWork:
|
GUI.progressBarTick.emit('tick')
|
||||||
mergeWorkerPool.apply_async(func=mergeDirectory, args=(i, ), callback=mergeDirectoryTick)
|
if len(work) > 0:
|
||||||
mergeWorkerPool.close()
|
for i in work:
|
||||||
mergeWorkerPool.join()
|
splitWorkerPool.apply_async(func=splitImage, args=(i, ), callback=splitImageTick)
|
||||||
if GUI and not GUI.conversionAlive:
|
splitWorkerPool.close()
|
||||||
rmtree(options.targetDir, True)
|
splitWorkerPool.join()
|
||||||
raise UserWarning("Conversion interrupted.")
|
if GUI and not GUI.conversionAlive:
|
||||||
if len(mergeWorkerOutput) > 0:
|
rmtree(targetDir, True)
|
||||||
rmtree(options.targetDir, True)
|
raise UserWarning("Conversion interrupted.")
|
||||||
raise RuntimeError("One of workers crashed. Cause: " + mergeWorkerOutput[0][0],
|
if len(splitWorkerOutput) > 0:
|
||||||
mergeWorkerOutput[0][1])
|
rmtree(targetDir, True)
|
||||||
print("Splitting images...")
|
raise RuntimeError("One of workers crashed. Cause: " + splitWorkerOutput[0][0],
|
||||||
for root, _, files in os.walk(options.targetDir, False):
|
splitWorkerOutput[0][1])
|
||||||
for name in files:
|
if args.inPlace:
|
||||||
if getImageFileName(name) is not None:
|
rmtree(sourceDir)
|
||||||
pagenumber += 1
|
move(targetDir, sourceDir)
|
||||||
work.append([root, name, options])
|
else:
|
||||||
else:
|
rmtree(targetDir, True)
|
||||||
os.remove(os.path.join(root, name))
|
raise UserWarning("Source directory is empty.")
|
||||||
if GUI:
|
|
||||||
GUI.progressBarTick.emit('Splitting images')
|
|
||||||
GUI.progressBarTick.emit(str(pagenumber))
|
|
||||||
GUI.progressBarTick.emit('tick')
|
|
||||||
if len(work) > 0:
|
|
||||||
for i in work:
|
|
||||||
splitWorkerPool.apply_async(func=splitImage, args=(i, ), callback=splitImageTick)
|
|
||||||
splitWorkerPool.close()
|
|
||||||
splitWorkerPool.join()
|
|
||||||
if GUI and not GUI.conversionAlive:
|
|
||||||
rmtree(options.targetDir, True)
|
|
||||||
raise UserWarning("Conversion interrupted.")
|
|
||||||
if len(splitWorkerOutput) > 0:
|
|
||||||
rmtree(options.targetDir, True)
|
|
||||||
raise RuntimeError("One of workers crashed. Cause: " + splitWorkerOutput[0][0],
|
|
||||||
splitWorkerOutput[0][1])
|
|
||||||
if options.inPlace:
|
|
||||||
rmtree(options.sourceDir)
|
|
||||||
move(options.targetDir, options.sourceDir)
|
|
||||||
else:
|
else:
|
||||||
rmtree(options.targetDir, True)
|
raise UserWarning("Provided input is not a directory.")
|
||||||
raise UserWarning("Source directory is empty.")
|
|
||||||
else:
|
|
||||||
raise UserWarning("Provided path is not a directory.")
|
|
||||||
else:
|
else:
|
||||||
raise UserWarning("Target height is not set.")
|
raise UserWarning("Target height is not set.")
|
||||||
|
|||||||
93
kindlecomicconverter/comicarchive.py
Normal file
93
kindlecomicconverter/comicarchive.py
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
||||||
|
# Copyright (c) 2013-2019 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||||
|
#
|
||||||
|
# Permission to use, copy, modify, and/or distribute this software for
|
||||||
|
# any purpose with or without fee is hereby granted, provided that the
|
||||||
|
# above copyright notice and this permission notice appear in all
|
||||||
|
# copies.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
||||||
|
# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
||||||
|
# WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
|
||||||
|
# AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||||
|
# DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
|
||||||
|
# OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||||
|
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
# PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
#
|
||||||
|
|
||||||
|
import os
|
||||||
|
import platform
|
||||||
|
import subprocess
|
||||||
|
import distro
|
||||||
|
from shutil import move
|
||||||
|
from subprocess import STDOUT, PIPE, CalledProcessError
|
||||||
|
from xml.dom.minidom import parseString
|
||||||
|
from xml.parsers.expat import ExpatError
|
||||||
|
from .shared import subprocess_run_silent
|
||||||
|
|
||||||
|
EXTRACTION_ERROR = 'Failed to extract archive. Try extracting file outside of KCC.'
|
||||||
|
|
||||||
|
|
||||||
|
class ComicArchive:
|
||||||
|
def __init__(self, filepath):
|
||||||
|
self.filepath = filepath
|
||||||
|
self.type = None
|
||||||
|
if not os.path.isfile(self.filepath):
|
||||||
|
raise OSError('File not found.')
|
||||||
|
try:
|
||||||
|
process = subprocess_run_silent(['7z', 'l', '-y', '-p1', self.filepath], stderr=STDOUT, stdout=PIPE)
|
||||||
|
except FileNotFoundError:
|
||||||
|
return
|
||||||
|
for line in process.stdout.splitlines():
|
||||||
|
if b'Type =' in line:
|
||||||
|
self.type = line.rstrip().decode().split(' = ')[1].upper()
|
||||||
|
break
|
||||||
|
if process.returncode != 0 and distro.id() == 'fedora':
|
||||||
|
process = subprocess_run_silent(['unrar', 'l', '-y', '-p1', self.filepath], stderr=STDOUT, stdout=PIPE)
|
||||||
|
for line in process.stdout.splitlines():
|
||||||
|
if b'Details: ' in line:
|
||||||
|
self.type = line.rstrip().decode().split(' ')[1].upper()
|
||||||
|
break
|
||||||
|
if process.returncode != 0:
|
||||||
|
raise OSError(EXTRACTION_ERROR)
|
||||||
|
|
||||||
|
def extract(self, targetdir):
|
||||||
|
if not os.path.isdir(targetdir):
|
||||||
|
raise OSError('Target directory doesn\'t exist.')
|
||||||
|
try:
|
||||||
|
process = subprocess_run_silent(['tar', '-xf', self.filepath, '-C', targetdir],
|
||||||
|
stdout=PIPE, stderr=STDOUT, check=True)
|
||||||
|
return targetdir
|
||||||
|
except (FileNotFoundError, CalledProcessError):
|
||||||
|
pass
|
||||||
|
process = subprocess_run_silent(['7z', 'x', '-y', '-xr!__MACOSX', '-xr!.DS_Store', '-xr!thumbs.db', '-xr!Thumbs.db', '-o' + targetdir, self.filepath],
|
||||||
|
stdout=PIPE, stderr=STDOUT)
|
||||||
|
if process.returncode != 0 and distro.id() == 'fedora':
|
||||||
|
process = subprocess_run_silent(['unrar', 'x', '-y', '-x__MACOSX', '-x.DS_Store', '-xthumbs.db', '-xThumbs.db', self.filepath, targetdir]
|
||||||
|
, stdout=PIPE, stderr=STDOUT)
|
||||||
|
if process.returncode != 0:
|
||||||
|
raise OSError(EXTRACTION_ERROR)
|
||||||
|
elif process.returncode != 0:
|
||||||
|
raise OSError(EXTRACTION_ERROR)
|
||||||
|
return targetdir
|
||||||
|
|
||||||
|
def addFile(self, sourcefile):
|
||||||
|
if self.type in ['RAR', 'RAR5']:
|
||||||
|
raise NotImplementedError
|
||||||
|
process = subprocess_run_silent(['7z', 'a', '-y', self.filepath, sourcefile],
|
||||||
|
stdout=PIPE, stderr=STDOUT)
|
||||||
|
if process.returncode != 0:
|
||||||
|
raise OSError('Failed to add the file.')
|
||||||
|
|
||||||
|
def extractMetadata(self):
|
||||||
|
process = subprocess_run_silent(['7z', 'x', '-y', '-so', self.filepath, 'ComicInfo.xml'],
|
||||||
|
stdout=PIPE, stderr=STDOUT)
|
||||||
|
if process.returncode != 0:
|
||||||
|
raise OSError(EXTRACTION_ERROR)
|
||||||
|
try:
|
||||||
|
return parseString(process.stdout)
|
||||||
|
except ExpatError:
|
||||||
|
return None
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# Based on initial version of DualMetaFix. Copyright (C) 2013 Kevin Hendricks
|
# Based on initial version of DualMetaFix. Copyright (C) 2013 Kevin Hendricks
|
||||||
# Changes for KCC Copyright (C) 2014-2017 Pawel Jastrzebski <pawelj@iosphe.re>
|
# Changes for KCC Copyright (C) 2014-2019 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -24,6 +24,7 @@ import shutil
|
|||||||
class DualMetaFixException(Exception):
|
class DualMetaFixException(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
# palm database offset constants
|
# palm database offset constants
|
||||||
number_of_pdb_records = 76
|
number_of_pdb_records = 76
|
||||||
first_pdb_record = 78
|
first_pdb_record = 78
|
||||||
@@ -142,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)
|
||||||
@@ -171,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()
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
# Copyright (C) 2010 Alex Yatskov
|
# Copyright (C) 2010 Alex Yatskov
|
||||||
# Copyright (C) 2011 Stanislav (proDOOMman) Kosolapov <prodoomman@gmail.com>
|
# Copyright (C) 2011 Stanislav (proDOOMman) Kosolapov <prodoomman@gmail.com>
|
||||||
# Copyright (c) 2016 Alberto Planas <aplanas@gmail.com>
|
# Copyright (c) 2016 Alberto Planas <aplanas@gmail.com>
|
||||||
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
||||||
# Copyright (c) 2013-2017 Pawel Jastrzebski <pawelj@iosphe.re>
|
# Copyright (c) 2013-2019 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -16,14 +18,13 @@
|
|||||||
#
|
#
|
||||||
# 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
|
||||||
from io import BytesIO
|
import mozjpeg_lossless_optimization
|
||||||
from urllib.request import Request, urlopen
|
|
||||||
from urllib.parse import quote
|
|
||||||
from PIL import Image, ImageOps, ImageStat, ImageChops, ImageFilter
|
from PIL import Image, ImageOps, ImageStat, ImageChops, ImageFilter
|
||||||
from .shared import md5Checksum
|
from .shared import md5Checksum
|
||||||
from . import __version__
|
|
||||||
|
AUTO_CROP_THRESHOLD = 0.015
|
||||||
|
|
||||||
|
|
||||||
class ProfileData:
|
class ProfileData:
|
||||||
@@ -79,12 +80,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/Voyage/Oasis", (1072, 1448), Palette16, 1.8),
|
'KV': ("Kindle Paperwhite 3/4/Voyage/Oasis", (1072, 1448), Palette16, 1.8),
|
||||||
|
'KPW5': ("Kindle Paperwhite 5/Signature Edition", (1236, 1648), Palette16, 1.8),
|
||||||
|
'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),
|
||||||
@@ -92,12 +97,21 @@ 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),
|
||||||
|
'KoCC': ("Kobo Clara Colour", (1072, 1448), Palette16, 1.8),
|
||||||
|
'KoL': ("Kobo Libra H2O/Kobo Libra 2", (1264, 1680), Palette16, 1.8),
|
||||||
|
'KoLC': ("Kobo Libra Colour", (1264, 1680), Palette16, 1.8),
|
||||||
|
'KoF': ("Kobo Forma", (1440, 1920), Palette16, 1.8),
|
||||||
|
'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),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class ComicPageParser:
|
class ComicPageParser:
|
||||||
def __init__(self, source, options):
|
def __init__(self, source, options):
|
||||||
|
Image.MAX_IMAGE_PIXELS = int(2048 * 2048 * 2048 // 4 // 3)
|
||||||
self.opt = options
|
self.opt = options
|
||||||
self.source = source
|
self.source = source
|
||||||
self.size = self.opt.profileData[1]
|
self.size = self.opt.profileData[1]
|
||||||
@@ -105,6 +119,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):
|
||||||
@@ -119,9 +136,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:
|
||||||
@@ -139,7 +169,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])
|
||||||
@@ -211,6 +241,7 @@ class ComicPage:
|
|||||||
_, self.size, self.palette, self.gamma = self.opt.profileData
|
_, self.size, self.palette, self.gamma = self.opt.profileData
|
||||||
if self.opt.hq:
|
if self.opt.hq:
|
||||||
self.size = (int(self.size[0] * 1.5), int(self.size[1] * 1.5))
|
self.size = (int(self.size[0] * 1.5), int(self.size[1] * 1.5))
|
||||||
|
self.kindle_scribe_azw3 = (options.profile == 'KS') and (options.format in ('MOBI', 'EPUB'))
|
||||||
self.image = image
|
self.image = image
|
||||||
self.color = color
|
self.color = color
|
||||||
self.fill = fill
|
self.fill = fill
|
||||||
@@ -225,6 +256,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:
|
||||||
@@ -236,11 +270,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))
|
||||||
@@ -254,7 +297,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
|
||||||
@@ -268,89 +311,93 @@ class ComicPage:
|
|||||||
self.image = self.image.quantize(palette=palImg)
|
self.image = self.image.quantize(palette=palImg)
|
||||||
|
|
||||||
def resizeImage(self):
|
def resizeImage(self):
|
||||||
if self.image.size[0] <= self.size[0] and self.image.size[1] <= self.size[1]:
|
# kindle scribe conversion to mobi is limited in resolution by kindlegen, same with send to kindle and epub
|
||||||
method = Image.BICUBIC
|
if self.kindle_scribe_azw3:
|
||||||
else:
|
self.size = (1440, 1920)
|
||||||
method = Image.LANCZOS
|
ratio_device = float(self.size[1]) / float(self.size[0])
|
||||||
|
ratio_image = float(self.image.size[1]) / float(self.image.size[0])
|
||||||
|
method = self.resize_method()
|
||||||
if self.opt.stretch:
|
if self.opt.stretch:
|
||||||
self.image = self.image.resize(self.size, method)
|
self.image = self.image.resize(self.size, method)
|
||||||
elif self.image.size[0] <= self.size[0] and self.image.size[1] <= self.size[1] and not self.opt.upscale:
|
elif method == Image.Resampling.BICUBIC and not self.opt.upscale:
|
||||||
if self.opt.format == 'CBZ':
|
if self.opt.format == 'CBZ' or self.opt.kfx:
|
||||||
borderw = int((self.size[0] - self.image.size[0]) / 2)
|
borderw = int((self.size[0] - self.image.size[0]) / 2)
|
||||||
borderh = int((self.size[1] - self.image.size[1]) / 2)
|
borderh = int((self.size[1] - self.image.size[1]) / 2)
|
||||||
self.image = ImageOps.expand(self.image, border=(borderw, borderh), fill=self.fill)
|
self.image = ImageOps.expand(self.image, border=(borderw, borderh), fill=self.fill)
|
||||||
if self.image.size[0] != self.size[0] or self.image.size[1] != self.size[1]:
|
if self.image.size[0] != self.size[0] or self.image.size[1] != self.size[1]:
|
||||||
self.image = ImageOps.fit(self.image, self.size, method=Image.BICUBIC, centering=(0.5, 0.5))
|
self.image = ImageOps.fit(self.image, self.size, method=method)
|
||||||
else:
|
else: # if image bigger than device resolution or smaller with upscaling
|
||||||
if self.opt.format == 'CBZ':
|
if abs(ratio_image - ratio_device) < AUTO_CROP_THRESHOLD:
|
||||||
ratioDev = float(self.size[0]) / float(self.size[1])
|
self.image = ImageOps.fit(self.image, self.size, method=method)
|
||||||
if (float(self.image.size[0]) / float(self.image.size[1])) < ratioDev:
|
elif self.opt.format == 'CBZ' or self.opt.kfx:
|
||||||
diff = int(self.image.size[1] * ratioDev) - self.image.size[0]
|
self.image = ImageOps.pad(self.image, self.size, method=method, color=self.fill)
|
||||||
self.image = ImageOps.expand(self.image, border=(int(diff / 2), 0), fill=self.fill)
|
|
||||||
elif (float(self.image.size[0]) / float(self.image.size[1])) > ratioDev:
|
|
||||||
diff = int(self.image.size[0] / ratioDev) - self.image.size[1]
|
|
||||||
self.image = ImageOps.expand(self.image, border=(0, int(diff / 2)), fill=self.fill)
|
|
||||||
self.image = ImageOps.fit(self.image, self.size, method=method, centering=(0.5, 0.5))
|
|
||||||
else:
|
else:
|
||||||
hpercent = self.size[1] / float(self.image.size[1])
|
if self.kindle_scribe_azw3:
|
||||||
wsize = int((float(self.image.size[0]) * float(hpercent)))
|
self.size = (1860, 1920)
|
||||||
self.image = self.image.resize((wsize, self.size[1]), method)
|
self.image = ImageOps.contain(self.image, self.size, method=method)
|
||||||
if self.image.size[0] > self.size[0] or self.image.size[1] > self.size[1]:
|
|
||||||
self.image.thumbnail(self.size, Image.LANCZOS)
|
|
||||||
|
|
||||||
def getBoundingBox(self, tmpImg):
|
def resize_method(self):
|
||||||
min_margin = [int(0.005 * i + 0.5) for i in tmpImg.size]
|
if self.image.size[0] <= self.size[0] and self.image.size[1] <= self.size[1]:
|
||||||
max_margin = [int(0.1 * i + 0.5) for i in tmpImg.size]
|
return Image.Resampling.BICUBIC
|
||||||
bbox = tmpImg.getbbox()
|
else:
|
||||||
|
return Image.Resampling.LANCZOS
|
||||||
|
|
||||||
|
def getBoundingBox(self, tmptmg):
|
||||||
|
min_margin = [int(0.005 * i + 0.5) for i in tmptmg.size]
|
||||||
|
max_margin = [int(0.1 * i + 0.5) for i in tmptmg.size]
|
||||||
|
bbox = tmptmg.getbbox()
|
||||||
bbox = (
|
bbox = (
|
||||||
max(0, min(max_margin[0], bbox[0] - min_margin[0])),
|
max(0, min(max_margin[0], bbox[0] - min_margin[0])),
|
||||||
max(0, min(max_margin[1], bbox[1] - min_margin[1])),
|
max(0, min(max_margin[1], bbox[1] - min_margin[1])),
|
||||||
min(tmpImg.size[0],
|
min(tmptmg.size[0],
|
||||||
max(tmpImg.size[0] - max_margin[0], bbox[2] + min_margin[0])),
|
max(tmptmg.size[0] - max_margin[0], bbox[2] + min_margin[0])),
|
||||||
min(tmpImg.size[1],
|
min(tmptmg.size[1],
|
||||||
max(tmpImg.size[1] - max_margin[1], bbox[3] + min_margin[1])),
|
max(tmptmg.size[1] - max_margin[1], bbox[3] + min_margin[1])),
|
||||||
)
|
)
|
||||||
return bbox
|
return bbox
|
||||||
|
|
||||||
def cropPageNumber(self, power):
|
def maybeCrop(self, box, minimum):
|
||||||
if self.fill != 'white':
|
box_area = (box[2] - box[0]) * (box[3] - box[1])
|
||||||
tmpImg = self.image.convert(mode='L')
|
image_area = self.image.size[0] * self.image.size[1]
|
||||||
else:
|
if (box_area / image_area) >= minimum:
|
||||||
tmpImg = ImageOps.invert(self.image.convert(mode='L'))
|
self.image = self.image.crop(box)
|
||||||
tmpImg = tmpImg.point(lambda x: x and 255)
|
|
||||||
tmpImg = tmpImg.filter(ImageFilter.MinFilter(size=3))
|
|
||||||
tmpImg = tmpImg.filter(ImageFilter.GaussianBlur(radius=5))
|
|
||||||
tmpImg = tmpImg.point(lambda x: (x >= 16 * power) and x)
|
|
||||||
self.image = self.image.crop(tmpImg.getbbox()) if tmpImg.getbbox() else self.image
|
|
||||||
|
|
||||||
def cropMargin(self, power):
|
def cropPageNumber(self, power, minimum):
|
||||||
if self.fill != 'white':
|
if self.fill != 'white':
|
||||||
tmpImg = self.image.convert(mode='L')
|
tmptmg = self.image.convert(mode='L')
|
||||||
else:
|
else:
|
||||||
tmpImg = ImageOps.invert(self.image.convert(mode='L'))
|
tmptmg = ImageOps.invert(self.image.convert(mode='L'))
|
||||||
tmpImg = tmpImg.filter(ImageFilter.GaussianBlur(radius=3))
|
tmptmg = tmptmg.point(lambda x: x and 255)
|
||||||
tmpImg = tmpImg.point(lambda x: (x >= 16 * power) and x)
|
tmptmg = tmptmg.filter(ImageFilter.MinFilter(size=3))
|
||||||
self.image = self.image.crop(self.getBoundingBox(tmpImg)) if tmpImg.getbbox() else self.image
|
tmptmg = tmptmg.filter(ImageFilter.GaussianBlur(radius=5))
|
||||||
|
tmptmg = tmptmg.point(lambda x: (x >= 16 * power) and x)
|
||||||
|
if tmptmg.getbbox():
|
||||||
|
self.maybeCrop(tmptmg.getbbox(), minimum)
|
||||||
|
|
||||||
|
def cropMargin(self, power, minimum):
|
||||||
|
if self.fill != 'white':
|
||||||
|
tmptmg = self.image.convert(mode='L')
|
||||||
|
else:
|
||||||
|
tmptmg = ImageOps.invert(self.image.convert(mode='L'))
|
||||||
|
tmptmg = tmptmg.filter(ImageFilter.GaussianBlur(radius=3))
|
||||||
|
tmptmg = tmptmg.point(lambda x: (x >= 16 * power) and x)
|
||||||
|
if tmptmg.getbbox():
|
||||||
|
self.maybeCrop(self.getBoundingBox(tmptmg), minimum)
|
||||||
|
|
||||||
|
|
||||||
class Cover:
|
class Cover:
|
||||||
def __init__(self, source, target, opt, tomeNumber):
|
def __init__(self, source, target, opt, tomeid):
|
||||||
self.options = opt
|
self.options = opt
|
||||||
self.source = source
|
self.source = source
|
||||||
self.target = target
|
self.target = target
|
||||||
if tomeNumber == 0:
|
if tomeid == 0:
|
||||||
self.tomeNumber = 1
|
self.tomeid = 1
|
||||||
else:
|
else:
|
||||||
self.tomeNumber = tomeNumber
|
self.tomeid = tomeid
|
||||||
if self.tomeNumber in self.options.remoteCovers:
|
self.image = Image.open(source)
|
||||||
try:
|
# backwards compatibility for Pillow >9.1.0
|
||||||
source = urlopen(Request(quote(self.options.remoteCovers[self.tomeNumber]).replace('%3A', ':', 1),
|
if not hasattr(Image, 'Resampling'):
|
||||||
headers={'User-Agent': 'KindleComicConverter/' + __version__})).read()
|
Image.Resampling = Image
|
||||||
self.image = Image.open(BytesIO(source))
|
|
||||||
except Exception:
|
|
||||||
self.image = Image.open(source)
|
|
||||||
else:
|
|
||||||
self.image = Image.open(source)
|
|
||||||
self.process()
|
self.process()
|
||||||
|
|
||||||
def process(self):
|
def process(self):
|
||||||
@@ -358,17 +405,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)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (c) 2013-2017 Pawel Jastrzebski <pawelj@iosphe.re>
|
# Copyright (c) 2013-2019 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||||
#
|
#
|
||||||
# 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
|
||||||
@@ -31,8 +31,7 @@ class Kindle:
|
|||||||
def findDevice(self):
|
def findDevice(self):
|
||||||
for drive in reversed(psutil.disk_partitions(False)):
|
for drive in reversed(psutil.disk_partitions(False)):
|
||||||
if (drive[2] == 'FAT32' and drive[3] == 'rw,removable') or \
|
if (drive[2] == 'FAT32' and drive[3] == 'rw,removable') or \
|
||||||
(drive[2] == 'vfat' and 'rw' in drive[3]) or \
|
(drive[2] in ('vfat', 'msdos', 'FAT') and 'rw' in drive[3]):
|
||||||
(drive[2] == 'msdos' and 'rw' in drive[3]):
|
|
||||||
if os.path.isdir(os.path.join(drive[1], 'system')) and \
|
if os.path.isdir(os.path.join(drive[1], 'system')) and \
|
||||||
os.path.isdir(os.path.join(drive[1], 'documents')):
|
os.path.isdir(os.path.join(drive[1], 'documents')):
|
||||||
return drive[1]
|
return drive[1]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (c) 2013-2017 Pawel Jastrzebski <pawelj@iosphe.re>
|
# Copyright (c) 2013-2019 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||||
#
|
#
|
||||||
# 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
|
||||||
@@ -18,14 +18,9 @@
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
from xml.dom.minidom import parse, Document
|
from xml.dom.minidom import parse, Document
|
||||||
from re import compile
|
|
||||||
from zipfile import is_zipfile, ZipFile, ZIP_DEFLATED
|
|
||||||
from subprocess import STDOUT, PIPE
|
|
||||||
from psutil import Popen
|
|
||||||
from tempfile import mkdtemp
|
from tempfile import mkdtemp
|
||||||
from shutil import rmtree
|
from shutil import rmtree
|
||||||
from .shared import removeFromZIP, check7ZFile as is_7zfile
|
from . import comicarchive
|
||||||
from . import rarfile
|
|
||||||
|
|
||||||
|
|
||||||
class MetadataParser:
|
class MetadataParser:
|
||||||
@@ -39,50 +34,21 @@ class MetadataParser:
|
|||||||
'Inkers': [],
|
'Inkers': [],
|
||||||
'Colorists': [],
|
'Colorists': [],
|
||||||
'Summary': '',
|
'Summary': '',
|
||||||
'MUid': '',
|
'Bookmarks': [],
|
||||||
'Bookmarks': []}
|
'Title': ''}
|
||||||
self.rawdata = None
|
self.rawdata = None
|
||||||
self.compressor = None
|
self.format = None
|
||||||
if self.source.endswith('.xml'):
|
if self.source.endswith('.xml') and os.path.exists(self.source):
|
||||||
self.rawdata = parse(self.source)
|
self.rawdata = parse(self.source)
|
||||||
|
elif not self.source.endswith('.xml'):
|
||||||
|
try:
|
||||||
|
cbx = comicarchive.ComicArchive(self.source)
|
||||||
|
self.rawdata = cbx.extractMetadata()
|
||||||
|
self.format = cbx.type
|
||||||
|
except OSError as e:
|
||||||
|
raise UserWarning(e)
|
||||||
|
if self.rawdata:
|
||||||
self.parseXML()
|
self.parseXML()
|
||||||
else:
|
|
||||||
if is_zipfile(self.source):
|
|
||||||
self.compressor = 'zip'
|
|
||||||
with ZipFile(self.source) as zip_file:
|
|
||||||
for member in zip_file.namelist():
|
|
||||||
if member != 'ComicInfo.xml':
|
|
||||||
continue
|
|
||||||
with zip_file.open(member) as xml_file:
|
|
||||||
self.rawdata = parse(xml_file)
|
|
||||||
elif rarfile.is_rarfile(self.source):
|
|
||||||
self.compressor = 'rar'
|
|
||||||
with rarfile.RarFile(self.source) as rar_file:
|
|
||||||
for member in rar_file.namelist():
|
|
||||||
if member != 'ComicInfo.xml':
|
|
||||||
continue
|
|
||||||
with rar_file.open(member) as xml_file:
|
|
||||||
self.rawdata = parse(xml_file)
|
|
||||||
elif is_7zfile(self.source):
|
|
||||||
self.compressor = '7z'
|
|
||||||
workdir = mkdtemp('', 'KCC-')
|
|
||||||
tmpXML = os.path.join(workdir, 'ComicInfo.xml')
|
|
||||||
output = Popen('7za e "' + self.source + '" ComicInfo.xml -o"' + workdir + '"',
|
|
||||||
stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
|
|
||||||
extracted = False
|
|
||||||
for line in output.stdout:
|
|
||||||
if b"Everything is Ok" in line or b"No files to process" in line:
|
|
||||||
extracted = True
|
|
||||||
if not extracted:
|
|
||||||
rmtree(workdir)
|
|
||||||
raise OSError('Failed to extract 7ZIP file.')
|
|
||||||
if os.path.isfile(tmpXML):
|
|
||||||
self.rawdata = parse(tmpXML)
|
|
||||||
rmtree(workdir)
|
|
||||||
else:
|
|
||||||
raise OSError('Failed to detect archive format.')
|
|
||||||
if self.rawdata:
|
|
||||||
self.parseXML()
|
|
||||||
|
|
||||||
def parseXML(self):
|
def parseXML(self):
|
||||||
if len(self.rawdata.getElementsByTagName('Series')) != 0:
|
if len(self.rawdata.getElementsByTagName('Series')) != 0:
|
||||||
@@ -93,17 +59,14 @@ class MetadataParser:
|
|||||||
self.data['Number'] = self.rawdata.getElementsByTagName('Number')[0].firstChild.nodeValue
|
self.data['Number'] = self.rawdata.getElementsByTagName('Number')[0].firstChild.nodeValue
|
||||||
if len(self.rawdata.getElementsByTagName('Summary')) != 0:
|
if len(self.rawdata.getElementsByTagName('Summary')) != 0:
|
||||||
self.data['Summary'] = self.rawdata.getElementsByTagName('Summary')[0].firstChild.nodeValue
|
self.data['Summary'] = self.rawdata.getElementsByTagName('Summary')[0].firstChild.nodeValue
|
||||||
|
if len(self.rawdata.getElementsByTagName('Title')) != 0:
|
||||||
|
self.data['Title'] = self.rawdata.getElementsByTagName('Title')[0].firstChild.nodeValue
|
||||||
for field in ['Writer', 'Penciller', 'Inker', 'Colorist']:
|
for field in ['Writer', 'Penciller', 'Inker', 'Colorist']:
|
||||||
if len(self.rawdata.getElementsByTagName(field)) != 0:
|
if len(self.rawdata.getElementsByTagName(field)) != 0:
|
||||||
for person in self.rawdata.getElementsByTagName(field)[0].firstChild.nodeValue.split(', '):
|
for person in self.rawdata.getElementsByTagName(field)[0].firstChild.nodeValue.split(', '):
|
||||||
self.data[field + 's'].append(person)
|
self.data[field + 's'].append(person)
|
||||||
self.data[field + 's'] = list(set(self.data[field + 's']))
|
self.data[field + 's'] = list(set(self.data[field + 's']))
|
||||||
self.data[field + 's'].sort()
|
self.data[field + 's'].sort()
|
||||||
if len(self.rawdata.getElementsByTagName('ScanInformation')) != 0:
|
|
||||||
coverId = compile('(MCD\\()(\\d+)(\\))')\
|
|
||||||
.search(self.rawdata.getElementsByTagName('ScanInformation')[0].firstChild.nodeValue)
|
|
||||||
if coverId:
|
|
||||||
self.data['MUid'] = coverId.group(2)
|
|
||||||
if len(self.rawdata.getElementsByTagName('Page')) != 0:
|
if len(self.rawdata.getElementsByTagName('Page')) != 0:
|
||||||
for page in self.rawdata.getElementsByTagName('Page'):
|
for page in self.rawdata.getElementsByTagName('Page'):
|
||||||
if 'Bookmark' in page.attributes and 'Image' in page.attributes:
|
if 'Bookmark' in page.attributes and 'Image' in page.attributes:
|
||||||
@@ -117,7 +80,7 @@ class MetadataParser:
|
|||||||
['Number', self.data['Number']], ['Writer', ', '.join(self.data['Writers'])],
|
['Number', self.data['Number']], ['Writer', ', '.join(self.data['Writers'])],
|
||||||
['Penciller', ', '.join(self.data['Pencillers'])], ['Inker', ', '.join(self.data['Inkers'])],
|
['Penciller', ', '.join(self.data['Pencillers'])], ['Inker', ', '.join(self.data['Inkers'])],
|
||||||
['Colorist', ', '.join(self.data['Colorists'])], ['Summary', self.data['Summary']],
|
['Colorist', ', '.join(self.data['Colorists'])], ['Summary', self.data['Summary']],
|
||||||
['ScanInformation', 'MCD(' + self.data['MUid'] + ')' if self.data['MUid'] else '']):
|
['Title', self.data['Title']]):
|
||||||
if self.rawdata.getElementsByTagName(row[0]):
|
if self.rawdata.getElementsByTagName(row[0]):
|
||||||
node = self.rawdata.getElementsByTagName(row[0])[0]
|
node = self.rawdata.getElementsByTagName(row[0])[0]
|
||||||
if row[1]:
|
if row[1]:
|
||||||
@@ -139,7 +102,7 @@ class MetadataParser:
|
|||||||
['Number', self.data['Number']], ['Writer', ', '.join(self.data['Writers'])],
|
['Number', self.data['Number']], ['Writer', ', '.join(self.data['Writers'])],
|
||||||
['Penciller', ', '.join(self.data['Pencillers'])], ['Inker', ', '.join(self.data['Inkers'])],
|
['Penciller', ', '.join(self.data['Pencillers'])], ['Inker', ', '.join(self.data['Inkers'])],
|
||||||
['Colorist', ', '.join(self.data['Colorists'])], ['Summary', self.data['Summary']],
|
['Colorist', ', '.join(self.data['Colorists'])], ['Summary', self.data['Summary']],
|
||||||
['ScanInformation', 'MCD(' + self.data['MUid'] + ')' if self.data['MUid'] else '']):
|
['Title', self.data['Title']]):
|
||||||
if row[1]:
|
if row[1]:
|
||||||
main = doc.createElement(row[0])
|
main = doc.createElement(row[0])
|
||||||
root.appendChild(main)
|
root.appendChild(main)
|
||||||
@@ -154,20 +117,9 @@ class MetadataParser:
|
|||||||
tmpXML = os.path.join(workdir, 'ComicInfo.xml')
|
tmpXML = os.path.join(workdir, 'ComicInfo.xml')
|
||||||
with open(tmpXML, 'w', encoding='utf-8') as f:
|
with open(tmpXML, 'w', encoding='utf-8') as f:
|
||||||
self.rawdata.writexml(f, encoding='utf-8')
|
self.rawdata.writexml(f, encoding='utf-8')
|
||||||
if is_zipfile(self.source):
|
try:
|
||||||
removeFromZIP(self.source, 'ComicInfo.xml')
|
cbx = comicarchive.ComicArchive(self.source)
|
||||||
with ZipFile(self.source, mode='a', compression=ZIP_DEFLATED) as zip_file:
|
cbx.addFile(tmpXML)
|
||||||
zip_file.write(tmpXML, arcname=tmpXML.split(os.sep)[-1])
|
except OSError as e:
|
||||||
elif rarfile.is_rarfile(self.source):
|
raise UserWarning(e)
|
||||||
raise NotImplementedError
|
|
||||||
elif is_7zfile(self.source):
|
|
||||||
output = Popen('7za a "' + self.source + '" "' + tmpXML + '"',
|
|
||||||
stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
|
|
||||||
extracted = False
|
|
||||||
for line in output.stdout:
|
|
||||||
if b"Everything is Ok" in line:
|
|
||||||
extracted = True
|
|
||||||
if not extracted:
|
|
||||||
rmtree(workdir)
|
|
||||||
raise OSError('Failed to modify 7ZIP file.')
|
|
||||||
rmtree(workdir)
|
rmtree(workdir)
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
||||||
# Copyright (c) 2013-2017 Pawel Jastrzebski <pawelj@iosphe.re>
|
# Copyright (c) 2013-2019 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||||
#
|
#
|
||||||
# Based upon the code snippet by Ned Batchelder
|
# Based upon the code snippet by Ned Batchelder
|
||||||
# (http://nedbatchelder.com/blog/200712/extracting_jpgs_from_pdfs.html)
|
# (http://nedbatchelder.com/blog/200712/extracting_jpgs_from_pdfs.html)
|
||||||
@@ -23,19 +25,23 @@ import os
|
|||||||
from random import choice
|
from random import choice
|
||||||
from string import ascii_uppercase, digits
|
from string import ascii_uppercase, digits
|
||||||
|
|
||||||
|
# skip stray images a few pixels in size in some PDFs
|
||||||
|
# typical images are many thousands in length
|
||||||
|
# https://github.com/ciromattia/kcc/pull/546
|
||||||
|
STRAY_IMAGE_LENGTH_THRESHOLD = 300
|
||||||
|
|
||||||
|
|
||||||
class PdfJpgExtract:
|
class PdfJpgExtract:
|
||||||
def __init__(self, origFileName):
|
def __init__(self, fname):
|
||||||
self.origFileName = origFileName
|
self.fname = fname
|
||||||
self.filename = os.path.splitext(origFileName)
|
self.filename = os.path.splitext(fname)
|
||||||
# noinspection PyUnusedLocal
|
self.path = self.filename[0] + "-KCC-" + ''.join(choice(ascii_uppercase + digits) for _ in range(3))
|
||||||
self.path = self.filename[0] + "-KCC-" + ''.join(choice(ascii_uppercase + digits) for x in range(3))
|
|
||||||
|
|
||||||
def getPath(self):
|
def getPath(self):
|
||||||
return self.path
|
return self.path
|
||||||
|
|
||||||
def extract(self):
|
def extract(self):
|
||||||
pdf = open(self.origFileName, "rb").read()
|
pdf = open(self.fname, "rb").read()
|
||||||
startmark = b"\xff\xd8"
|
startmark = b"\xff\xd8"
|
||||||
startfix = 0
|
startfix = 0
|
||||||
endmark = b"\xff\xd9"
|
endmark = b"\xff\xd9"
|
||||||
@@ -59,10 +65,15 @@ class PdfJpgExtract:
|
|||||||
raise Exception("Didn't find end of JPG!")
|
raise Exception("Didn't find end of JPG!")
|
||||||
istart += startfix
|
istart += startfix
|
||||||
iend += endfix
|
iend += endfix
|
||||||
|
i = iend
|
||||||
|
|
||||||
|
if iend - istart < STRAY_IMAGE_LENGTH_THRESHOLD:
|
||||||
|
continue
|
||||||
|
|
||||||
jpg = pdf[istart:iend]
|
jpg = pdf[istart:iend]
|
||||||
jpgfile = open(self.path + "/jpg%d.jpg" % njpg, "wb")
|
jpgfile = open(self.path + "/jpg%d.jpg" % njpg, "wb")
|
||||||
jpgfile.write(jpg)
|
jpgfile.write(jpg)
|
||||||
jpgfile.close()
|
jpgfile.close()
|
||||||
njpg += 1
|
njpg += 1
|
||||||
i = iend
|
|
||||||
return self.path, njpg
|
return self.path, njpg
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,7 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
||||||
# Copyright (c) 2013-2017 Pawel Jastrzebski <pawelj@iosphe.re>
|
# Copyright (c) 2013-2019 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||||
#
|
#
|
||||||
# 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
|
||||||
@@ -19,11 +21,10 @@
|
|||||||
import os
|
import os
|
||||||
from hashlib import md5
|
from hashlib import md5
|
||||||
from html.parser import HTMLParser
|
from html.parser import HTMLParser
|
||||||
|
import subprocess
|
||||||
from distutils.version import StrictVersion
|
from distutils.version import StrictVersion
|
||||||
from shutil import rmtree, copy
|
|
||||||
from tempfile import mkdtemp
|
|
||||||
from zipfile import ZipFile, ZIP_DEFLATED
|
|
||||||
from re import split
|
from re import split
|
||||||
|
import sys
|
||||||
from traceback import format_tb
|
from traceback import format_tb
|
||||||
|
|
||||||
|
|
||||||
@@ -48,7 +49,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 != '.png' and ext != '.jpg' and ext != '.jpeg' and ext != '.gif'):
|
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]
|
||||||
|
|
||||||
@@ -73,8 +74,8 @@ def walkLevel(some_dir, level=1):
|
|||||||
del dirs[:]
|
del dirs[:]
|
||||||
|
|
||||||
|
|
||||||
def md5Checksum(filePath):
|
def md5Checksum(fpath):
|
||||||
with open(filePath, 'rb') as fh:
|
with open(fpath, 'rb') as fh:
|
||||||
m = md5()
|
m = md5()
|
||||||
while True:
|
while True:
|
||||||
data = fh.read(8192)
|
data = fh.read(8192)
|
||||||
@@ -84,47 +85,28 @@ def md5Checksum(filePath):
|
|||||||
return m.hexdigest()
|
return m.hexdigest()
|
||||||
|
|
||||||
|
|
||||||
def check7ZFile(filePath):
|
|
||||||
with open(filePath, 'rb') as fh:
|
|
||||||
header = fh.read(6)
|
|
||||||
return header == b"7z\xbc\xaf'\x1c"
|
|
||||||
|
|
||||||
|
|
||||||
def removeFromZIP(zipfname, *filenames):
|
|
||||||
tempdir = mkdtemp('', 'KCC-')
|
|
||||||
try:
|
|
||||||
tempname = os.path.join(tempdir, 'KCC.zip')
|
|
||||||
with ZipFile(zipfname, 'r') as zipread:
|
|
||||||
with ZipFile(tempname, 'w', compression=ZIP_DEFLATED) as zipwrite:
|
|
||||||
for item in zipread.infolist():
|
|
||||||
if item.filename not in filenames:
|
|
||||||
zipwrite.writestr(item, zipread.read(item.filename))
|
|
||||||
copy(tempname, zipfname)
|
|
||||||
finally:
|
|
||||||
rmtree(tempdir, True)
|
|
||||||
|
|
||||||
|
|
||||||
def sanitizeTrace(traceback):
|
def sanitizeTrace(traceback):
|
||||||
return ''.join(format_tb(traceback))\
|
return ''.join(format_tb(traceback))\
|
||||||
.replace('C:/projects/kcc/', '') \
|
.replace('C:/projects/kcc/', '')\
|
||||||
.replace('c:/projects/kcc/', '') \
|
.replace('c:/projects/kcc/', '')\
|
||||||
.replace('C:/python36-x64/', '')\
|
.replace('C:/python37-x64/', '')\
|
||||||
.replace('c:/python36-x64/', '')\
|
.replace('c:/python37-x64/', '')\
|
||||||
.replace('C:\\projects\\kcc\\', '') \
|
.replace('C:\\projects\\kcc\\', '')\
|
||||||
.replace('c:\\projects\\kcc\\', '') \
|
.replace('c:\\projects\\kcc\\', '')\
|
||||||
.replace('C:\\python36-x64\\', '')\
|
.replace('C:\\python37-x64\\', '')\
|
||||||
.replace('c:\\python36-x64\\', '')
|
.replace('c:\\python37-x64\\', '')
|
||||||
|
|
||||||
|
|
||||||
|
# noinspection PyUnresolvedReferences
|
||||||
def dependencyCheck(level):
|
def dependencyCheck(level):
|
||||||
missing = []
|
missing = []
|
||||||
if level > 2:
|
if level > 2:
|
||||||
try:
|
try:
|
||||||
from PyQt5.QtCore import qVersion as qtVersion
|
from PySide6.QtCore import qVersion as qtVersion
|
||||||
if StrictVersion('5.6.0') > StrictVersion(qtVersion()):
|
if StrictVersion('6.5.1') > StrictVersion(qtVersion()):
|
||||||
missing.append('PyQt 5.6.0+')
|
missing.append('PySide 6.5.1+')
|
||||||
except ImportError:
|
except ImportError:
|
||||||
missing.append('PyQt 5.6.0+')
|
missing.append('PySide 6.5.1+')
|
||||||
try:
|
try:
|
||||||
import raven
|
import raven
|
||||||
except ImportError:
|
except ImportError:
|
||||||
@@ -137,17 +119,25 @@ 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:
|
||||||
missing.append('python-slugify 1.2.1+')
|
missing.append('python-slugify 1.2.1+')
|
||||||
try:
|
try:
|
||||||
from PIL import PILLOW_VERSION as pillowVersion
|
from PIL import __version__ as pillowVersion
|
||||||
if StrictVersion('4.0.0') > StrictVersion(pillowVersion):
|
if StrictVersion('5.2.0') > StrictVersion(pillowVersion):
|
||||||
missing.append('Pillow 4.0.0+')
|
missing.append('Pillow 5.2.0+')
|
||||||
except ImportError:
|
except ImportError:
|
||||||
missing.append('Pillow 4.0.0+')
|
missing.append('Pillow 5.2.0+')
|
||||||
if len(missing) > 0:
|
if len(missing) > 0:
|
||||||
print('ERROR: ' + ', '.join(missing) + ' is not installed!')
|
print('ERROR: ' + ', '.join(missing) + ' is not installed!')
|
||||||
exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
def subprocess_run_silent(command, **kwargs):
|
||||||
|
if (os.name == 'nt'):
|
||||||
|
kwargs.setdefault('creationflags', subprocess.CREATE_NO_WINDOW)
|
||||||
|
return subprocess.run(command, **kwargs)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
||||||
# Copyright (c) 2013-2017 Pawel Jastrzebski <pawelj@iosphe.re>
|
# Copyright (c) 2013-2019 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||||
#
|
#
|
||||||
# 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
|
||||||
@@ -30,15 +30,15 @@ def start():
|
|||||||
os.environ['QT_AUTO_SCREEN_SCALE_FACTOR'] = "1"
|
os.environ['QT_AUTO_SCREEN_SCALE_FACTOR'] = "1"
|
||||||
KCCAplication = KCC_gui.QApplicationMessaging(sys.argv)
|
KCCAplication = KCC_gui.QApplicationMessaging(sys.argv)
|
||||||
if KCCAplication.isRunning():
|
if KCCAplication.isRunning():
|
||||||
if len(sys.argv) > 1:
|
for i in range(1, len(sys.argv)):
|
||||||
KCCAplication.sendMessage(sys.argv[1])
|
KCCAplication.sendMessage(sys.argv[i])
|
||||||
else:
|
else:
|
||||||
KCCAplication.sendMessage('ARISE')
|
KCCAplication.sendMessage('ARISE')
|
||||||
else:
|
else:
|
||||||
KCCWindow = KCC_gui.QMainWindowKCC()
|
KCCWindow = KCC_gui.QMainWindowKCC()
|
||||||
KCCUI = KCC_gui.KCCGUI(KCCAplication, KCCWindow)
|
KCCUI = KCC_gui.KCCGUI(KCCAplication, KCCWindow)
|
||||||
if len(sys.argv) > 1:
|
for i in range(1, len(sys.argv)):
|
||||||
KCCUI.handleMessage(sys.argv[1])
|
KCCUI.handleMessage(sys.argv[i])
|
||||||
sys.exit(KCCAplication.exec_())
|
sys.exit(KCCAplication.exec_())
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
kindlecomicconverter: binary-without-manpage usr/bin/kcc
|
|
||||||
kindlecomicconverter: wrong-name-for-changelog-of-native-package usr/share/doc/kindlecomicconverter/changelog.Debian.gz
|
|
||||||
kindlecomicconverter: file-missing-in-md5sums usr/share/doc/kindlecomicconverter/changelog.Debian.gz
|
|
||||||
kindlecomicconverter: hardening-no-relro usr/bin/kcc
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
[Desktop Entry]
|
|
||||||
Type=Application
|
|
||||||
Version=1.0
|
|
||||||
Name=Kindle Comic Converter
|
|
||||||
GenericName=Kindle Comic Converter
|
|
||||||
Comment=Comic and Manga converter for e-book readers
|
|
||||||
Icon=/usr/share/kindlecomicconverter/comic2ebook.png
|
|
||||||
Exec=/usr/bin/kcc %f
|
|
||||||
Terminal=false
|
|
||||||
Categories=Graphics;
|
|
||||||
MimeType=application/zip;application/x-rar;application/x-7z-compressed;
|
|
||||||
Binary file not shown.
BIN
other/osx/7za
BIN
other/osx/7za
Binary file not shown.
@@ -1,68 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
||||||
<plist version="1.0">
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleDevelopmentRegion</key>
|
|
||||||
<string>English</string>
|
|
||||||
<key>CFBundleDisplayName</key>
|
|
||||||
<string>Kindle Comic Converter</string>
|
|
||||||
<key>CFBundleDocumentTypes</key>
|
|
||||||
<array>
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleTypeExtensions</key>
|
|
||||||
<array>
|
|
||||||
<string>cbz</string>
|
|
||||||
<string>cbr</string>
|
|
||||||
<string>cb7</string>
|
|
||||||
<string>zip</string>
|
|
||||||
<string>rar</string>
|
|
||||||
<string>7z</string>
|
|
||||||
<string>pdf</string>
|
|
||||||
</array>
|
|
||||||
<key>CFBundleTypeIconFile</key>
|
|
||||||
<string>comic2ebook.icns</string>
|
|
||||||
<key>CFBundleTypeName</key>
|
|
||||||
<string>Comics</string>
|
|
||||||
<key>CFBundleTypeRole</key>
|
|
||||||
<string>Editor</string>
|
|
||||||
</dict>
|
|
||||||
</array>
|
|
||||||
<key>CFBundleExecutable</key>
|
|
||||||
<string>MacOS/Kindle Comic Converter</string>
|
|
||||||
<key>CFBundleGetInfoString</key>
|
|
||||||
<string>KindleComicConverter 5.4.1, written 2012-2017 by Ciro Mattia Gonano and Pawel Jastrzebski</string>
|
|
||||||
<key>CFBundleIconFile</key>
|
|
||||||
<string>comic2ebook.icns</string>
|
|
||||||
<key>CFBundleIdentifier</key>
|
|
||||||
<string>com.kindlecomicconverter.KindleComicConverter</string>
|
|
||||||
<key>CFBundleInfoDictionaryVersion</key>
|
|
||||||
<string>6.0</string>
|
|
||||||
<key>CFBundleName</key>
|
|
||||||
<string>Kindle Comic Converter</string>
|
|
||||||
<key>CFBundlePackageType</key>
|
|
||||||
<string>APPL</string>
|
|
||||||
<key>CFBundleShortVersionString</key>
|
|
||||||
<string>5.4.1</string>
|
|
||||||
<key>CFBundleSignature</key>
|
|
||||||
<string>????</string>
|
|
||||||
<key>CFBundleVersion</key>
|
|
||||||
<string>5.4.1</string>
|
|
||||||
<key>LSEnvironment</key>
|
|
||||||
<dict>
|
|
||||||
<key>PATH</key>
|
|
||||||
<string>./../Resources:/usr/local/bin:/usr/bin:/bin</string>
|
|
||||||
</dict>
|
|
||||||
<key>LSHasLocalizedDisplayName</key>
|
|
||||||
<false/>
|
|
||||||
<key>LSMinimumSystemVersion</key>
|
|
||||||
<string>10.10.0</string>
|
|
||||||
<key>NSAppleScriptEnabled</key>
|
|
||||||
<false/>
|
|
||||||
<key>NSHumanReadableCopyright</key>
|
|
||||||
<string>ISC License (ISCL)</string>
|
|
||||||
<key>NSMainNibFile</key>
|
|
||||||
<string>MainMenu</string>
|
|
||||||
<key>NSPrincipalClass</key>
|
|
||||||
<string>NSApplication</string>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
||||||
Binary file not shown.
BIN
other/osx/unrar
BIN
other/osx/unrar
Binary file not shown.
Binary file not shown.
@@ -1,91 +0,0 @@
|
|||||||
****** ***** ****** UnRAR - free utility for RAR archives
|
|
||||||
** ** ** ** ** ** ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
****** ******* ****** License for use and distribution of
|
|
||||||
** ** ** ** ** ** ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
** ** ** ** ** ** FREEWARE version
|
|
||||||
~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
The UnRAR utility is freeware. This means:
|
|
||||||
|
|
||||||
1. All copyrights to RAR and the utility UnRAR are exclusively
|
|
||||||
owned by the author - Alexander Roshal.
|
|
||||||
|
|
||||||
2. The UnRAR utility may be freely distributed. It is allowed
|
|
||||||
to distribute UnRAR inside of other software packages.
|
|
||||||
|
|
||||||
3. THE RAR ARCHIVER AND THE UnRAR UTILITY ARE DISTRIBUTED "AS IS".
|
|
||||||
NO WARRANTY OF ANY KIND IS EXPRESSED OR IMPLIED. YOU USE AT
|
|
||||||
YOUR OWN RISK. THE AUTHOR WILL NOT BE LIABLE FOR DATA LOSS,
|
|
||||||
DAMAGES, LOSS OF PROFITS OR ANY OTHER KIND OF LOSS WHILE USING
|
|
||||||
OR MISUSING THIS SOFTWARE.
|
|
||||||
|
|
||||||
4. Neither RAR binary code, WinRAR binary code, UnRAR source or UnRAR
|
|
||||||
binary code may be used or reverse engineered to re-create the RAR
|
|
||||||
compression algorithm, which is proprietary, without written
|
|
||||||
permission of the author.
|
|
||||||
|
|
||||||
5. If you don't agree with terms of the license you must remove
|
|
||||||
UnRAR files from your storage devices and cease to use the
|
|
||||||
utility.
|
|
||||||
|
|
||||||
Thank you for your interest in RAR and UnRAR.
|
|
||||||
|
|
||||||
|
|
||||||
Alexander L. Roshal
|
|
||||||
|
|
||||||
7-Zip
|
|
||||||
~~~~~
|
|
||||||
License for use and distribution
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
7-Zip Copyright (C) 1999-2012 Igor Pavlov.
|
|
||||||
|
|
||||||
Licenses for files are:
|
|
||||||
|
|
||||||
1) 7z.dll: GNU LGPL + unRAR restriction
|
|
||||||
2) All other files: GNU LGPL
|
|
||||||
|
|
||||||
The GNU LGPL + unRAR restriction means that you must follow both
|
|
||||||
GNU LGPL rules and unRAR restriction rules.
|
|
||||||
|
|
||||||
|
|
||||||
Note:
|
|
||||||
You can use 7-Zip on any computer, including a computer in a commercial
|
|
||||||
organization. You don't need to register or pay for 7-Zip.
|
|
||||||
|
|
||||||
|
|
||||||
GNU LGPL information
|
|
||||||
--------------------
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Lesser General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You can receive a copy of the GNU Lesser General Public License from
|
|
||||||
http://www.gnu.org/
|
|
||||||
|
|
||||||
|
|
||||||
unRAR restriction
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
The decompression engine for RAR archives was developed using source
|
|
||||||
code of unRAR program.
|
|
||||||
All copyrights to original unRAR code are owned by Alexander Roshal.
|
|
||||||
|
|
||||||
The license for original unRAR code has the following restriction:
|
|
||||||
|
|
||||||
The unRAR sources cannot be used to re-create the RAR compression algorithm,
|
|
||||||
which is proprietary. Distribution of modified unRAR sources in separate form
|
|
||||||
or as a part of other software is permitted, provided that it is clearly
|
|
||||||
stated in the documentation and source comments that the code may
|
|
||||||
not be used to develop a RAR (WinRAR) compatible archiver.
|
|
||||||
|
|
||||||
|
|
||||||
--
|
|
||||||
Igor Pavlov
|
|
||||||
Binary file not shown.
@@ -1,233 +0,0 @@
|
|||||||
{\rtf1\adeflang1025\ansi\ansicpg1250\uc1\adeff0\deff0\stshfdbch0\stshfloch31506\stshfhich31506\stshfbi31506\deflang1045\deflangfe1045\themelang1045\themelangfe0\themelangcs0{\fonttbl{\f0\fbidi \froman\fcharset238\fprq2{\*\panose 02020603050405020304}Times New Roman;}
|
|
||||||
{\f0\fbidi \froman\fcharset238\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f37\fbidi \fswiss\fcharset238\fprq2{\*\panose 020f0502020204030204}Calibri;}
|
|
||||||
{\flomajor\f31500\fbidi \froman\fcharset238\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\fdbmajor\f31501\fbidi \froman\fcharset238\fprq2{\*\panose 02020603050405020304}Times New Roman;}
|
|
||||||
{\fhimajor\f31502\fbidi \fswiss\fcharset238\fprq2{\*\panose 020f0302020204030204}Calibri Light;}{\fbimajor\f31503\fbidi \froman\fcharset238\fprq2{\*\panose 02020603050405020304}Times New Roman;}
|
|
||||||
{\flominor\f31504\fbidi \froman\fcharset238\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\fdbminor\f31505\fbidi \froman\fcharset238\fprq2{\*\panose 02020603050405020304}Times New Roman;}
|
|
||||||
{\fhiminor\f31506\fbidi \fswiss\fcharset238\fprq2{\*\panose 020f0502020204030204}Calibri;}{\fbiminor\f31507\fbidi \froman\fcharset238\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f44\fbidi \froman\fcharset0\fprq2 Times New Roman;}
|
|
||||||
{\f43\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\f45\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\f46\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\f47\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}
|
|
||||||
{\f48\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\f49\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\f50\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\f44\fbidi \froman\fcharset0\fprq2 Times New Roman;}
|
|
||||||
{\f43\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\f45\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\f46\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\f47\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}
|
|
||||||
{\f48\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\f49\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\f50\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\f414\fbidi \fswiss\fcharset0\fprq2 Calibri;}
|
|
||||||
{\f413\fbidi \fswiss\fcharset204\fprq2 Calibri Cyr;}{\f415\fbidi \fswiss\fcharset161\fprq2 Calibri Greek;}{\f416\fbidi \fswiss\fcharset162\fprq2 Calibri Tur;}{\f417\fbidi \fswiss\fcharset177\fprq2 Calibri (Hebrew);}
|
|
||||||
{\f418\fbidi \fswiss\fcharset178\fprq2 Calibri (Arabic);}{\f419\fbidi \fswiss\fcharset186\fprq2 Calibri Baltic;}{\f420\fbidi \fswiss\fcharset163\fprq2 Calibri (Vietnamese);}{\flomajor\f31510\fbidi \froman\fcharset0\fprq2 Times New Roman;}
|
|
||||||
{\flomajor\f31509\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\flomajor\f31511\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\flomajor\f31512\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}
|
|
||||||
{\flomajor\f31513\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\flomajor\f31514\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\flomajor\f31515\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}
|
|
||||||
{\flomajor\f31516\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fdbmajor\f31520\fbidi \froman\fcharset0\fprq2 Times New Roman;}{\fdbmajor\f31519\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}
|
|
||||||
{\fdbmajor\f31521\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fdbmajor\f31522\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fdbmajor\f31523\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}
|
|
||||||
{\fdbmajor\f31524\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fdbmajor\f31525\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fdbmajor\f31526\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}
|
|
||||||
{\fhimajor\f31530\fbidi \fswiss\fcharset0\fprq2 Calibri Light;}{\fhimajor\f31529\fbidi \fswiss\fcharset204\fprq2 Calibri Light Cyr;}{\fhimajor\f31531\fbidi \fswiss\fcharset161\fprq2 Calibri Light Greek;}
|
|
||||||
{\fhimajor\f31532\fbidi \fswiss\fcharset162\fprq2 Calibri Light Tur;}{\fhimajor\f31533\fbidi \fswiss\fcharset177\fprq2 Calibri Light (Hebrew);}{\fhimajor\f31534\fbidi \fswiss\fcharset178\fprq2 Calibri Light (Arabic);}
|
|
||||||
{\fhimajor\f31535\fbidi \fswiss\fcharset186\fprq2 Calibri Light Baltic;}{\fhimajor\f31536\fbidi \fswiss\fcharset163\fprq2 Calibri Light (Vietnamese);}{\fbimajor\f31540\fbidi \froman\fcharset0\fprq2 Times New Roman;}
|
|
||||||
{\fbimajor\f31539\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\fbimajor\f31541\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fbimajor\f31542\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}
|
|
||||||
{\fbimajor\f31543\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\fbimajor\f31544\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fbimajor\f31545\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}
|
|
||||||
{\fbimajor\f31546\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\flominor\f31550\fbidi \froman\fcharset0\fprq2 Times New Roman;}{\flominor\f31549\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}
|
|
||||||
{\flominor\f31551\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\flominor\f31552\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\flominor\f31553\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}
|
|
||||||
{\flominor\f31554\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\flominor\f31555\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\flominor\f31556\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}
|
|
||||||
{\fdbminor\f31560\fbidi \froman\fcharset0\fprq2 Times New Roman;}{\fdbminor\f31559\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\fdbminor\f31561\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}
|
|
||||||
{\fdbminor\f31562\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fdbminor\f31563\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\fdbminor\f31564\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}
|
|
||||||
{\fdbminor\f31565\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fdbminor\f31566\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fhiminor\f31570\fbidi \fswiss\fcharset0\fprq2 Calibri;}
|
|
||||||
{\fhiminor\f31569\fbidi \fswiss\fcharset204\fprq2 Calibri Cyr;}{\fhiminor\f31571\fbidi \fswiss\fcharset161\fprq2 Calibri Greek;}{\fhiminor\f31572\fbidi \fswiss\fcharset162\fprq2 Calibri Tur;}
|
|
||||||
{\fhiminor\f31573\fbidi \fswiss\fcharset177\fprq2 Calibri (Hebrew);}{\fhiminor\f31574\fbidi \fswiss\fcharset178\fprq2 Calibri (Arabic);}{\fhiminor\f31575\fbidi \fswiss\fcharset186\fprq2 Calibri Baltic;}
|
|
||||||
{\fhiminor\f31576\fbidi \fswiss\fcharset163\fprq2 Calibri (Vietnamese);}{\fbiminor\f31580\fbidi \froman\fcharset0\fprq2 Times New Roman;}{\fbiminor\f31579\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}
|
|
||||||
{\fbiminor\f31581\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fbiminor\f31582\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fbiminor\f31583\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}
|
|
||||||
{\fbiminor\f31584\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fbiminor\f31585\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fbiminor\f31586\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}}
|
|
||||||
{\colortbl;\red0\green0\blue0;\red0\green0\blue255;\red0\green255\blue255;\red0\green255\blue0;\red255\green0\blue255;\red255\green0\blue0;\red255\green255\blue0;\red255\green255\blue255;\red0\green0\blue128;\red0\green128\blue128;\red0\green128\blue0;
|
|
||||||
\red128\green0\blue128;\red128\green0\blue0;\red128\green128\blue0;\red128\green128\blue128;\red192\green192\blue192;\chyperlink\ctint255\cshade255\red5\green99\blue193;\cfollowedhyperlink\ctint255\cshade255\red149\green79\blue114;}{\*\defchp
|
|
||||||
\f31506\fs22\lang1045\langfe1033\langfenp1033 }{\*\defpap \ql \li0\ri0\sa160\sl259\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 }\noqfpromote {\stylesheet{\ql \li0\ri0\sa160\sl259\slmult1
|
|
||||||
\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af0\afs22\alang1025 \ltrch\fcs0 \f31506\fs22\lang1045\langfe1033\cgrid\langnp1045\langfenp1033 \snext0 \sqformat \spriority0 Normal;}{\*\cs10 \additive
|
|
||||||
\ssemihidden \sunhideused \spriority1 Default Paragraph Font;}{\*
|
|
||||||
\ts11\tsrowd\trftsWidthB3\trpaddl108\trpaddr108\trpaddfl3\trpaddft3\trpaddfb3\trpaddfr3\trcbpat1\trcfpat1\tblind0\tblindtype3\tsvertalt\tsbrdrt\tsbrdrl\tsbrdrb\tsbrdrr\tsbrdrdgl\tsbrdrdgr\tsbrdrh\tsbrdrv \ql \li0\ri0\sa160\sl259\slmult1
|
|
||||||
\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af31506\afs22\alang1025 \ltrch\fcs0 \f31506\fs22\lang1045\langfe1033\cgrid\langnp1045\langfenp1033 \snext11 \ssemihidden \sunhideused Normal Table;}{\*\cs15 \additive
|
|
||||||
\rtlch\fcs1 \af0 \ltrch\fcs0 \ul\cf17 \sbasedon10 \sunhideused \styrsid3562894 Hyperlink;}{\*\cs16 \additive \rtlch\fcs1 \af0 \ltrch\fcs0 \ul\cf18 \sbasedon10 \ssemihidden \sunhideused \styrsid7678248 FollowedHyperlink;}}{\*\rsidtbl \rsid1081196
|
|
||||||
\rsid3146412\rsid3562894\rsid5731975\rsid7678248\rsid9265883\rsid11107340\rsid11629590\rsid12600926\rsid13187577}{\mmathPr\mmathFont34\mbrkBin0\mbrkBinSub0\msmallFrac0\mdispDef1\mlMargin0\mrMargin0\mdefJc1\mwrapIndent1440\mintLim0\mnaryLim1}{\info
|
|
||||||
{\author Pawe\'b3 Jastrz\'eabski}{\operator Pawe\'b3 Jastrz\'eabski}{\creatim\yr2013\mo10\dy29\hr15\min17}{\revtim\yr2017\mo8\dy20\hr17\min40}{\version9}{\edmins8}{\nofpages1}{\nofwords33}{\nofchars201}{\nofcharsws233}{\vern39}}{\*\xmlnstbl {\xmlns1 http:
|
|
||||||
//schemas.microsoft.com/office/word/2003/wordml}}\paperw11906\paperh16838\margl1417\margr1417\margt1417\margb1417\gutter0\ltrsect
|
|
||||||
\deftab708\widowctrl\ftnbj\aenddoc\hyphhotz425\trackmoves0\trackformatting1\donotembedsysfont1\relyonvml0\donotembedlingdata0\grfdocevents0\validatexml1\showplaceholdtext0\ignoremixedcontent0\saveinvalidxml0
|
|
||||||
\showxmlerrors1\noxlattoyen\expshrtn\noultrlspc\dntblnsbdb\nospaceforul\formshade\horzdoc\dgmargin\dghspace180\dgvspace180\dghorigin1417\dgvorigin1417\dghshow1\dgvshow1
|
|
||||||
\jexpand\viewkind1\viewscale100\pgbrdrhead\pgbrdrfoot\splytwnine\ftnlytwnine\htmautsp\nolnhtadjtbl\useltbaln\alntblind\lytcalctblwd\lyttblrtgr\lnbrkrule\nobrkwrptbl\snaptogridincell\allowfieldendsel\wrppunct
|
|
||||||
\asianbrkrule\rsidroot11107340\newtblstyruls\nogrowautofit\usenormstyforlist\noindnmbrts\felnbrelev\nocxsptable\indrlsweleven\noafcnsttbl\afelev\utinl\hwelev\spltpgpar\notcvasp\notbrkcnstfrctbl\notvatxbx\krnprsnet\cachedcolbal \nouicompat \fet0
|
|
||||||
{\*\wgrffmtfilter 2450}\nofeaturethrottle1\ilfomacatclnup0\ltrpar \sectd \ltrsect\linex0\headery708\footery708\colsx708\endnhere\sectlinegrid360\sectdefaultcl\sftnbj {\*\pnseclvl1\pnucrm\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl2
|
|
||||||
\pnucltr\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl3\pndec\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl4\pnlcltr\pnstart1\pnindent720\pnhang {\pntxta )}}{\*\pnseclvl5\pndec\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl6
|
|
||||||
\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl7\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl8\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl9\pnlcrm\pnstart1\pnindent720\pnhang
|
|
||||||
{\pntxtb (}{\pntxta )}}\pard\plain \ltrpar\ql \li0\ri0\sa160\sl259\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af0\afs22\alang1025 \ltrch\fcs0
|
|
||||||
\f31506\fs22\lang1045\langfe1033\cgrid\langnp1045\langfenp1033 {\rtlch\fcs1 \af0 \ltrch\fcs0 \b\fs52\cf6\lang2057\langfe1033\langnp2057\insrsid3562894\charrsid3562894 Warning!}{\rtlch\fcs1 \af0 \ltrch\fcs0
|
|
||||||
\b\fs52\cf6\lang2057\langfe1033\langnp2057\insrsid13187577\charrsid3562894
|
|
||||||
\par }{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \fs28\lang2057\langfe1033\langnp2057\insrsid1081196 Creation of}{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \fs28\lang2057\langfe1033\langnp2057\insrsid3562894\charrsid12600926 }{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0
|
|
||||||
\b\fs28\lang2057\langfe1033\langnp2057\insrsid3562894\charrsid12600926 MOBI}{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \fs28\lang2057\langfe1033\langnp2057\insrsid3562894\charrsid12600926 files }{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0
|
|
||||||
\fs28\lang2057\langfe1033\langnp2057\insrsid5731975\charrsid12600926 require}{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \fs28\lang2057\langfe1033\langnp2057\insrsid11629590 s}{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0
|
|
||||||
\fs28\lang2057\langfe1033\langnp2057\insrsid5731975\charrsid12600926 additional software.}{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \fs28\lang2057\langfe1033\langnp2057\insrsid3562894\charrsid12600926
|
|
||||||
\par }{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \b\fs28\lang2057\langfe1033\langnp2057\insrsid3562894\charrsid12600926 Please download: }{\field\flddirty{\*\fldinst {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0
|
|
||||||
\b\fs28\lang2057\langfe1033\langnp2057\insrsid3562894\charrsid12600926 HYPERLINK "http://www.amazon.com/gp/feature.html?ie=UTF8&docId=1000765211" }{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \b\fs28\lang2057\langfe1033\langnp2057\insrsid3562894\charrsid12600926
|
|
||||||
{\*\datafield
|
|
||||||
00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90b9600000068007400740070003a002f002f007700770077002e0061006d0061007a006f006e002e0063006f006d002f00670070002f0066006500610074007500720065002e00680074006d006c003f00690065003d00
|
|
||||||
5500540046003800260064006f006300490064003d0031003000300030003700360035003200310031000000795881f43b1d7f48af2c825dc485276300000000a5ab00000000}}}{\fldrslt {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0
|
|
||||||
\cs15\b\fs28\ul\cf17\lang2057\langfe1033\langnp2057\insrsid3562894\charrsid12600926 KindleGen}}}\sectd \ltrsect\linex0\headery708\footery708\colsx708\endnhere\sectlinegrid360\sectdefaultcl\sftnbj {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0
|
|
||||||
\b\fs28\lang2057\langfe1033\langnp2057\insrsid3562894\charrsid12600926
|
|
||||||
\par }{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \fs28\lang2057\langfe1033\langnp2057\insrsid3562894\charrsid12600926 And place }{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \i\fs28\lang2057\langfe1033\langnp2057\insrsid5731975\charrsid12600926 kindlegen.exe}{\rtlch\fcs1
|
|
||||||
\af0\afs24 \ltrch\fcs0 \fs28\lang2057\langfe1033\langnp2057\insrsid5731975\charrsid12600926 }{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \fs28\lang2057\langfe1033\langnp2057\insrsid3562894\charrsid12600926 inside }{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0
|
|
||||||
\b\fs28\lang2057\langfe1033\langnp2057\insrsid3146412\charrsid3146412 Kindle}{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \b\fs28\lang2057\langfe1033\langnp2057\insrsid3146412 }{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0
|
|
||||||
\b\fs28\lang2057\langfe1033\langnp2057\insrsid3146412\charrsid3146412 Comic}{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \b\fs28\lang2057\langfe1033\langnp2057\insrsid3146412 }{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0
|
|
||||||
\b\fs28\lang2057\langfe1033\langnp2057\insrsid3146412\charrsid3146412 Converter}{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \b\fs28\lang2057\langfe1033\langnp2057\insrsid3146412 }{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0
|
|
||||||
\fs28\lang2057\langfe1033\langnp2057\insrsid3562894\charrsid12600926 directory.
|
|
||||||
\par }{\*\themedata 504b030414000600080000002100e9de0fbfff0000001c020000130000005b436f6e74656e745f54797065735d2e786d6cac91cb4ec3301045f748fc83e52d4a
|
|
||||||
9cb2400825e982c78ec7a27cc0c8992416c9d8b2a755fbf74cd25442a820166c2cd933f79e3be372bd1f07b5c3989ca74aaff2422b24eb1b475da5df374fd9ad
|
|
||||||
5689811a183c61a50f98f4babebc2837878049899a52a57be670674cb23d8e90721f90a4d2fa3802cb35762680fd800ecd7551dc18eb899138e3c943d7e503b6
|
|
||||||
b01d583deee5f99824e290b4ba3f364eac4a430883b3c092d4eca8f946c916422ecab927f52ea42b89a1cd59c254f919b0e85e6535d135a8de20f20b8c12c3b0
|
|
||||||
0c895fcf6720192de6bf3b9e89ecdbd6596cbcdd8eb28e7c365ecc4ec1ff1460f53fe813d3cc7f5b7f020000ffff0300504b030414000600080000002100a5d6
|
|
||||||
a7e7c0000000360100000b0000005f72656c732f2e72656c73848fcf6ac3300c87ef85bd83d17d51d2c31825762fa590432fa37d00e1287f68221bdb1bebdb4f
|
|
||||||
c7060abb0884a4eff7a93dfeae8bf9e194e720169aaa06c3e2433fcb68e1763dbf7f82c985a4a725085b787086a37bdbb55fbc50d1a33ccd311ba548b6309512
|
|
||||||
0f88d94fbc52ae4264d1c910d24a45db3462247fa791715fd71f989e19e0364cd3f51652d73760ae8fa8c9ffb3c330cc9e4fc17faf2ce545046e37944c69e462
|
|
||||||
a1a82fe353bd90a865aad41ed0b5b8f9d6fd010000ffff0300504b0304140006000800000021006b799616830000008a0000001c0000007468656d652f746865
|
|
||||||
6d652f7468656d654d616e616765722e786d6c0ccc4d0ac3201040e17da17790d93763bb284562b2cbaebbf600439c1a41c7a0d29fdbd7e5e38337cedf14d59b
|
|
||||||
4b0d592c9c070d8a65cd2e88b7f07c2ca71ba8da481cc52c6ce1c715e6e97818c9b48d13df49c873517d23d59085adb5dd20d6b52bd521ef2cdd5eb9246a3d8b
|
|
||||||
4757e8d3f729e245eb2b260a0238fd010000ffff0300504b030414000600080000002100b7e72e45da060000a81a0000160000007468656d652f7468656d652f
|
|
||||||
7468656d65312e786d6cec595d8b1b37147d2ff43f0cf3eef86bc61f4bbcc11edbd936bbc9123b2979d4dab24759cdc88ce4dd981028c963a1509a963e34d0b7
|
|
||||||
3e94b68104fa92fe9a6d53da14f2177aa5198f255bdb4d96149692352c63f9dcaba37bef9cab195dbe722fa2ce114e386171cb2d5f2ab90e8e476c4ce269cbbd
|
|
||||||
35ec171aaec3058ac788b218b7dc05e6ee95ed0f3fb88cb6448823ec807dccb750cb0d85986d158b7c04c3885f62331cc36f13964448c0d7645a1c27e818fc46
|
|
||||||
b45829956ac50891d875621481db3d2616c7ce0c1d122ce6ce8dc9848cb0bbbd9ca44761a658703930a2c9404e8133cb7d65a39b8c0fcb12c8173ca089738468
|
|
||||||
cb8569c7ec7888ef09d7a1880bf8a1e596d49f5bdcbe5c445b991115a7d86a767df597d96506e3c38a9a33991ee4937a9eefd5dab97f05a06213d7abf76abd5a
|
|
||||||
ee4f01d068040b4eb9e83efd4eb3d3f533ac064a2f2dbebbf56eb56ce035ffd50dce6d5f7e0cbc02a5febd0d7cbf1f40140dbc02a5787f03ef79f54ae0197805
|
|
||||||
4af1b50d7cbdd4ee7a7503af402125f1e106bae4d7aac172b53964c2e88e15def4bd7ebd92395fa1a01af22293534c582cce28b908dd65491f70124f9120b123
|
|
||||||
16333c4123a8ed005172901067974c43a8bf198a1987e152a5d42f55e1bffc78ea4a05066d61a4594b7a40886f0c495a0e1f2564265aeec7e0d5d520af5ffcf8
|
|
||||||
fac533e7e4e1f39387bf9c3c7a74f2f0e7d49161b583e2a96ef5eafb2ffe7ef2a9f3d7b3ef5e3dfeca8ee73afef79f3efbedd72fed4058e92a042fbf7efac7f3
|
|
||||||
a72fbff9fccf1f1e5be0ed041de8f021893077aee363e7268b60612a0426737c90bc9dc5304444b768c7538e622467b1f8ef89d0405f5f208a2cb80e3623783b
|
|
||||||
01a5b101afceef1a8407613217c4e2f15a1819c03dc6688725d6285c937369611ecee3a97df264aee36e2274649b3b40b191dfde7c064a4b6c2e83101b34f729
|
|
||||||
8a059ae2180b47fec60e31b6acee0e21465cf7c828619c4d847387381d44ac21199203a39a56463b2482bc2c6c0421df466cf66e3b1d466dabeee22313097705
|
|
||||||
a216f2434c8d305e457381229bcb218aa81ef05d24421bc9c12219e9b81e1790e929a6cce98d31e7369b1b09ac574bfa3590177bdaf7e8223291892087369fbb
|
|
||||||
88311dd965874188a2990d3b2071a8633fe28750a2c8d967c206df63e61d22bf431e507c6aba6fc31e409fe06c35b805caaa5bac0a44fe324f2cb9bc8a9951bf
|
|
||||||
83059d20aca406f4dfd0f388c4678afb9aacfbffadac8390befcf68965551755d0db09b1de513b6b327e1a6e5dbc03968cc9c5d7ee2e9ac7fb186e97cd06f65e
|
|
||||||
badf4bb7fbbf97eed3eee7772fd82b8d06f9965bc574c7aef6efd159dbf709a174201614ef72b583e7d0a0c67d1894e6ea8116e74f75b3102ee50d0df318b869
|
|
||||||
82948d9330f10911e1204433d8e6975de964ca33d753eecc1887ddbf1ab6fa96783a8ff6d8387d782d97e5836aaa211c89d578c9cfc7e18943a4e85a7df54096
|
|
||||||
bb576ca7eaf9794940dabe0d096d329344d542a2be1c9441524feb10340b09b5b277c2a26961d190ee97a9da6001d4f2acc00eca817d57cbf53d30012378b042
|
|
||||||
148f659ed2542fb3ab92f92e337d5a308d0a80edc4b20256996e4aaea72e4fae2e2db537c8b441422b3793848a8c6a653c44639c55a71c7d131a6f9bebe62aa5
|
|
||||||
063d190a351f94d68a46bdf16f2cce9b6bb05bd7061aeb4a4163e7b8e5d6aa3e94cc08cd5aee049efee1329a41ed70b9f345740a2fd64622496ff8f328cb2ce1
|
|
||||||
a28b7898065c894eaa061111387128895aae5c7e9e061a2b0d51dcca1510840b4bae09b272d1c841d2cd24e3c9048f849e766d44463afd0a0a9f6a85f557657e
|
|
||||||
7eb0b4647348f7201c1f3b07749edc4450627ebd2c0338261c5e0295d3688e09bcdccc856c557f6b8d29935dfdeda2aaa1741cd15988b28ea28b790a57529ed3
|
|
||||||
51dff21868dfb2354340b590648df0602a1bac1e54a39be65d23e5706ad73ddb48464e13cd55cf345445764dbb8a19332cdbc05a2ccfd7e43556cb1083a6e91d
|
|
||||||
3e95ee75c96d2eb56e6d9f90770908781e3f4bd77d8386a0515b4d6650938c3765586a76366af68ee502cfa0f6264d4253fddad2ed5adcf21e619d0e06cfd5f9
|
|
||||||
c16ebd6a6168b2dc5eaa48ab4311fdbc821ddc05f1e8c2bbe039155ca5128e2112041ba281da93a4b201b7c83d91dd1a70e5cc13d272ef97fcb61754fca0506a
|
|
||||||
f8bd8257f54a8586dfae16dabe5f2df7fc72a9dba93c80c622c2a8eca707327d78234517d9b18c1adf389a89962fdd2e8d585464eab0a5a888aba39972c5389a
|
|
||||||
490f639ca13c73711d02a273bf56e937abcd4eadd0acb6fb05afdb69149a41ad53e8d6827ab7df0dfc46b3ffc0758e14d86b5703afd66b146ae5202878b592a4
|
|
||||||
df6816ea5ea5d2f6eaed46cf6b3fc8b631b0f2543eb258407815afed7f000000ffff0300504b0304140006000800000021000dd1909fb60000001b0100002700
|
|
||||||
00007468656d652f7468656d652f5f72656c732f7468656d654d616e616765722e786d6c2e72656c73848f4d0ac2301484f78277086f6fd3ba109126dd88d0ad
|
|
||||||
d40384e4350d363f2451eced0dae2c082e8761be9969bb979dc9136332de3168aa1a083ae995719ac16db8ec8e4052164e89d93b64b060828e6f37ed1567914b
|
|
||||||
284d262452282e3198720e274a939cd08a54f980ae38a38f56e422a3a641c8bbd048f7757da0f19b017cc524bd62107bd5001996509affb3fd381a89672f1f16
|
|
||||||
5dfe514173d9850528a2c6cce0239baa4c04ca5bbabac4df000000ffff0300504b01022d0014000600080000002100e9de0fbfff0000001c0200001300000000
|
|
||||||
000000000000000000000000005b436f6e74656e745f54797065735d2e786d6c504b01022d0014000600080000002100a5d6a7e7c0000000360100000b000000
|
|
||||||
00000000000000000000300100005f72656c732f2e72656c73504b01022d00140006000800000021006b799616830000008a0000001c00000000000000000000
|
|
||||||
000000190200007468656d652f7468656d652f7468656d654d616e616765722e786d6c504b01022d0014000600080000002100b7e72e45da060000a81a000016
|
|
||||||
00000000000000000000000000d60200007468656d652f7468656d652f7468656d65312e786d6c504b01022d00140006000800000021000dd1909fb60000001b
|
|
||||||
0100002700000000000000000000000000e40900007468656d652f7468656d652f5f72656c732f7468656d654d616e616765722e786d6c2e72656c73504b050600000000050005005d010000df0a00000000}
|
|
||||||
{\*\colorschememapping 3c3f786d6c2076657273696f6e3d22312e302220656e636f64696e673d225554462d3822207374616e64616c6f6e653d22796573223f3e0d0a3c613a636c724d
|
|
||||||
617020786d6c6e733a613d22687474703a2f2f736368656d61732e6f70656e786d6c666f726d6174732e6f72672f64726177696e676d6c2f323030362f6d6169
|
|
||||||
6e22206267313d226c743122207478313d22646b3122206267323d226c743222207478323d22646b322220616363656e74313d22616363656e74312220616363
|
|
||||||
656e74323d22616363656e74322220616363656e74333d22616363656e74332220616363656e74343d22616363656e74342220616363656e74353d22616363656e74352220616363656e74363d22616363656e74362220686c696e6b3d22686c696e6b2220666f6c486c696e6b3d22666f6c486c696e6b222f3e}
|
|
||||||
{\*\latentstyles\lsdstimax375\lsdlockeddef0\lsdsemihiddendef0\lsdunhideuseddef0\lsdqformatdef0\lsdprioritydef99{\lsdlockedexcept \lsdqformat1 \lsdpriority0 \lsdlocked0 Normal;\lsdqformat1 \lsdpriority9 \lsdlocked0 heading 1;
|
|
||||||
\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 2;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 3;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 4;
|
|
||||||
\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 5;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 6;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 7;
|
|
||||||
\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 8;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 9;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 1;
|
|
||||||
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 5;
|
|
||||||
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 6;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 7;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 8;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 9;
|
|
||||||
\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 1;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 2;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 3;
|
|
||||||
\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 4;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 5;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 6;
|
|
||||||
\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 7;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 8;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 9;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Normal Indent;
|
|
||||||
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 footnote text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 annotation text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 header;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 footer;
|
|
||||||
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index heading;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority35 \lsdlocked0 caption;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 table of figures;
|
|
||||||
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 envelope address;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 envelope return;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 footnote reference;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 annotation reference;
|
|
||||||
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 line number;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 page number;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 endnote reference;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 endnote text;
|
|
||||||
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 table of authorities;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 macro;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 toa heading;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List;
|
|
||||||
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 3;
|
|
||||||
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 3;
|
|
||||||
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 3;
|
|
||||||
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 5;\lsdqformat1 \lsdpriority10 \lsdlocked0 Title;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Closing;
|
|
||||||
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Signature;\lsdsemihidden1 \lsdunhideused1 \lsdpriority1 \lsdlocked0 Default Paragraph Font;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text Indent;
|
|
||||||
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 4;
|
|
||||||
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Message Header;\lsdqformat1 \lsdpriority11 \lsdlocked0 Subtitle;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Salutation;
|
|
||||||
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Date;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text First Indent;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text First Indent 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Note Heading;
|
|
||||||
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text Indent 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text Indent 3;
|
|
||||||
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Block Text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Hyperlink;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 FollowedHyperlink;\lsdqformat1 \lsdpriority22 \lsdlocked0 Strong;
|
|
||||||
\lsdqformat1 \lsdpriority20 \lsdlocked0 Emphasis;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Document Map;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Plain Text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 E-mail Signature;
|
|
||||||
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Top of Form;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Bottom of Form;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Normal (Web);\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Acronym;
|
|
||||||
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Address;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Cite;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Code;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Definition;
|
|
||||||
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Keyboard;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Preformatted;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Sample;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Typewriter;
|
|
||||||
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Variable;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 annotation subject;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 No List;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 1;
|
|
||||||
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Balloon Text;\lsdpriority39 \lsdlocked0 Table Grid;
|
|
||||||
\lsdsemihidden1 \lsdlocked0 Placeholder Text;\lsdqformat1 \lsdpriority1 \lsdlocked0 No Spacing;\lsdpriority60 \lsdlocked0 Light Shading;\lsdpriority61 \lsdlocked0 Light List;\lsdpriority62 \lsdlocked0 Light Grid;
|
|
||||||
\lsdpriority63 \lsdlocked0 Medium Shading 1;\lsdpriority64 \lsdlocked0 Medium Shading 2;\lsdpriority65 \lsdlocked0 Medium List 1;\lsdpriority66 \lsdlocked0 Medium List 2;\lsdpriority67 \lsdlocked0 Medium Grid 1;\lsdpriority68 \lsdlocked0 Medium Grid 2;
|
|
||||||
\lsdpriority69 \lsdlocked0 Medium Grid 3;\lsdpriority70 \lsdlocked0 Dark List;\lsdpriority71 \lsdlocked0 Colorful Shading;\lsdpriority72 \lsdlocked0 Colorful List;\lsdpriority73 \lsdlocked0 Colorful Grid;\lsdpriority60 \lsdlocked0 Light Shading Accent 1;
|
|
||||||
\lsdpriority61 \lsdlocked0 Light List Accent 1;\lsdpriority62 \lsdlocked0 Light Grid Accent 1;\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 1;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 1;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 1;
|
|
||||||
\lsdsemihidden1 \lsdlocked0 Revision;\lsdqformat1 \lsdpriority34 \lsdlocked0 List Paragraph;\lsdqformat1 \lsdpriority29 \lsdlocked0 Quote;\lsdqformat1 \lsdpriority30 \lsdlocked0 Intense Quote;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 1;
|
|
||||||
\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 1;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 1;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 1;\lsdpriority70 \lsdlocked0 Dark List Accent 1;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 1;
|
|
||||||
\lsdpriority72 \lsdlocked0 Colorful List Accent 1;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 1;\lsdpriority60 \lsdlocked0 Light Shading Accent 2;\lsdpriority61 \lsdlocked0 Light List Accent 2;\lsdpriority62 \lsdlocked0 Light Grid Accent 2;
|
|
||||||
\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 2;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 2;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 2;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 2;
|
|
||||||
\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 2;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 2;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 2;\lsdpriority70 \lsdlocked0 Dark List Accent 2;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 2;
|
|
||||||
\lsdpriority72 \lsdlocked0 Colorful List Accent 2;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 2;\lsdpriority60 \lsdlocked0 Light Shading Accent 3;\lsdpriority61 \lsdlocked0 Light List Accent 3;\lsdpriority62 \lsdlocked0 Light Grid Accent 3;
|
|
||||||
\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 3;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 3;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 3;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 3;
|
|
||||||
\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 3;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 3;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 3;\lsdpriority70 \lsdlocked0 Dark List Accent 3;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 3;
|
|
||||||
\lsdpriority72 \lsdlocked0 Colorful List Accent 3;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 3;\lsdpriority60 \lsdlocked0 Light Shading Accent 4;\lsdpriority61 \lsdlocked0 Light List Accent 4;\lsdpriority62 \lsdlocked0 Light Grid Accent 4;
|
|
||||||
\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 4;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 4;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 4;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 4;
|
|
||||||
\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 4;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 4;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 4;\lsdpriority70 \lsdlocked0 Dark List Accent 4;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 4;
|
|
||||||
\lsdpriority72 \lsdlocked0 Colorful List Accent 4;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 4;\lsdpriority60 \lsdlocked0 Light Shading Accent 5;\lsdpriority61 \lsdlocked0 Light List Accent 5;\lsdpriority62 \lsdlocked0 Light Grid Accent 5;
|
|
||||||
\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 5;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 5;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 5;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 5;
|
|
||||||
\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 5;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 5;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 5;\lsdpriority70 \lsdlocked0 Dark List Accent 5;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 5;
|
|
||||||
\lsdpriority72 \lsdlocked0 Colorful List Accent 5;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 5;\lsdpriority60 \lsdlocked0 Light Shading Accent 6;\lsdpriority61 \lsdlocked0 Light List Accent 6;\lsdpriority62 \lsdlocked0 Light Grid Accent 6;
|
|
||||||
\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 6;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 6;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 6;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 6;
|
|
||||||
\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 6;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 6;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 6;\lsdpriority70 \lsdlocked0 Dark List Accent 6;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 6;
|
|
||||||
\lsdpriority72 \lsdlocked0 Colorful List Accent 6;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 6;\lsdqformat1 \lsdpriority19 \lsdlocked0 Subtle Emphasis;\lsdqformat1 \lsdpriority21 \lsdlocked0 Intense Emphasis;
|
|
||||||
\lsdqformat1 \lsdpriority31 \lsdlocked0 Subtle Reference;\lsdqformat1 \lsdpriority32 \lsdlocked0 Intense Reference;\lsdqformat1 \lsdpriority33 \lsdlocked0 Book Title;\lsdsemihidden1 \lsdunhideused1 \lsdpriority37 \lsdlocked0 Bibliography;
|
|
||||||
\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority39 \lsdlocked0 TOC Heading;\lsdpriority41 \lsdlocked0 Plain Table 1;\lsdpriority42 \lsdlocked0 Plain Table 2;\lsdpriority43 \lsdlocked0 Plain Table 3;\lsdpriority44 \lsdlocked0 Plain Table 4;
|
|
||||||
\lsdpriority45 \lsdlocked0 Plain Table 5;\lsdpriority40 \lsdlocked0 Grid Table Light;\lsdpriority46 \lsdlocked0 Grid Table 1 Light;\lsdpriority47 \lsdlocked0 Grid Table 2;\lsdpriority48 \lsdlocked0 Grid Table 3;\lsdpriority49 \lsdlocked0 Grid Table 4;
|
|
||||||
\lsdpriority50 \lsdlocked0 Grid Table 5 Dark;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 1;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 1;
|
|
||||||
\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 1;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 1;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 1;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 1;
|
|
||||||
\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 1;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 2;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 2;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 2;
|
|
||||||
\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 2;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 2;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 2;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 2;
|
|
||||||
\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 3;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 3;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 3;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 3;
|
|
||||||
\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 3;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 3;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 3;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 4;
|
|
||||||
\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 4;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 4;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 4;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 4;
|
|
||||||
\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 4;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 4;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 5;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 5;
|
|
||||||
\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 5;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 5;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 5;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 5;
|
|
||||||
\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 5;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 6;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 6;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 6;
|
|
||||||
\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 6;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 6;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 6;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 6;
|
|
||||||
\lsdpriority46 \lsdlocked0 List Table 1 Light;\lsdpriority47 \lsdlocked0 List Table 2;\lsdpriority48 \lsdlocked0 List Table 3;\lsdpriority49 \lsdlocked0 List Table 4;\lsdpriority50 \lsdlocked0 List Table 5 Dark;
|
|
||||||
\lsdpriority51 \lsdlocked0 List Table 6 Colorful;\lsdpriority52 \lsdlocked0 List Table 7 Colorful;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 1;\lsdpriority47 \lsdlocked0 List Table 2 Accent 1;\lsdpriority48 \lsdlocked0 List Table 3 Accent 1;
|
|
||||||
\lsdpriority49 \lsdlocked0 List Table 4 Accent 1;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 1;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 1;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 1;
|
|
||||||
\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 2;\lsdpriority47 \lsdlocked0 List Table 2 Accent 2;\lsdpriority48 \lsdlocked0 List Table 3 Accent 2;\lsdpriority49 \lsdlocked0 List Table 4 Accent 2;
|
|
||||||
\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 2;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 2;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 2;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 3;
|
|
||||||
\lsdpriority47 \lsdlocked0 List Table 2 Accent 3;\lsdpriority48 \lsdlocked0 List Table 3 Accent 3;\lsdpriority49 \lsdlocked0 List Table 4 Accent 3;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 3;
|
|
||||||
\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 3;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 3;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 4;\lsdpriority47 \lsdlocked0 List Table 2 Accent 4;
|
|
||||||
\lsdpriority48 \lsdlocked0 List Table 3 Accent 4;\lsdpriority49 \lsdlocked0 List Table 4 Accent 4;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 4;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 4;
|
|
||||||
\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 4;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 5;\lsdpriority47 \lsdlocked0 List Table 2 Accent 5;\lsdpriority48 \lsdlocked0 List Table 3 Accent 5;
|
|
||||||
\lsdpriority49 \lsdlocked0 List Table 4 Accent 5;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 5;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 5;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 5;
|
|
||||||
\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 6;\lsdpriority47 \lsdlocked0 List Table 2 Accent 6;\lsdpriority48 \lsdlocked0 List Table 3 Accent 6;\lsdpriority49 \lsdlocked0 List Table 4 Accent 6;
|
|
||||||
\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 6;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 6;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 6;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Mention;
|
|
||||||
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Smart Hyperlink;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Hashtag;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Unresolved Mention;}}{\*\datastore 010500000200000018000000
|
|
||||||
4d73786d6c322e534158584d4c5265616465722e362e30000000000000000000000e0000
|
|
||||||
d0cf11e0a1b11ae1000000000000000000000000000000003e000300feff0900060000000000000000000000010000000100000000000000001000000200000001000000feffffff0000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
|
||||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
|
||||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
|
||||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
|
||||||
fffffffffffffffffdffffff04000000feffffff05000000fefffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
|
||||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
|
||||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
|
||||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
|
||||||
ffffffffffffffffffffffffffffffff52006f006f007400200045006e00740072007900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016000500ffffffffffffffff010000000c6ad98892f1d411a65f0040963251e5000000000000000000000000b00f
|
|
||||||
9da8ca19d30103000000c0020000000000004d0073006f004400610074006100530074006f0072006500000000000000000000000000000000000000000000000000000000000000000000000000000000001a000101ffffffffffffffff020000000000000000000000000000000000000000000000b00f9da8ca19d301
|
|
||||||
b00f9da8ca19d301000000000000000000000000ca0041004300c300d300d300c70058004d00d4003000c9004d00c200590043003100320055004a00300051003d003d000000000000000000000000000000000032000101ffffffffffffffff030000000000000000000000000000000000000000000000b00f9da8ca19
|
|
||||||
d301b00f9da8ca19d3010000000000000000000000004900740065006d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000201ffffffff04000000ffffffff000000000000000000000000000000000000000000000000
|
|
||||||
00000000000000000000000000000000320100000000000001000000020000000300000004000000feffffff060000000700000008000000090000000a000000feffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
|
||||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
|
||||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
|
||||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
|
||||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff3c3f786d6c2076657273696f6e3d22312e302220656e636f64696e673d225554462d3822207374616e64616c6f6e653d226e6f223f3e3c623a536f75726365732053656c65637465645374796c653d225c41504153697874684564697469
|
|
||||||
6f6e4f66666963654f6e6c696e652e78736c22205374796c654e616d653d22415041222056657273696f6e3d22362220786d6c6e733a623d22687474703a2f2f736368656d61732e6f70656e786d6c666f726d6174732e6f72672f6f6666696365446f63756d656e742f323030362f6269626c696f677261706879222078
|
|
||||||
6d6c6e733d22687474703a2f2f736368656d61732e6f70656e786d6c666f726d6174732e6f72672f6f6666696365446f63756d656e742f323030362f6269626c696f677261706879223e3c2f623a536f75726365733e00000000000000000000000000003c3f786d6c2076657273696f6e3d22312e302220656e636f6469
|
|
||||||
6e673d225554462d3822207374616e64616c6f6e653d226e6f223f3e0d0a3c64733a6461746173746f72654974656d2064733a6974656d49443d227b43464133303041382d443733392d343633332d413933322d3236303236444335303936397d2220786d6c6e733a64733d22687474703a2f2f736368656d61732e6f70
|
|
||||||
656e786d6c666f726d6174732e6f72672f6f6666696365446f63756d656e742f323030362f637573500072006f007000650072007400690065007300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016000200ffffffffffffffffffffffff000000000000
|
|
||||||
0000000000000000000000000000000000000000000000000000000000000500000055010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff00000000
|
|
||||||
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff0000
|
|
||||||
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff
|
|
||||||
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000746f6d586d6c223e3c64733a736368656d61526566733e3c64733a736368656d615265662064733a7572693d22687474703a2f2f736368656d61732e6f70656e786d6c666f726d6174732e6f7267
|
|
||||||
2f6f6666696365446f63756d656e742f323030362f6269626c696f677261706879222f3e3c2f64733a736368656d61526566733e3c2f64733a6461746173746f72654974656d3e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
|
||||||
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
|
||||||
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
|
||||||
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000105000000000000}}
|
|
||||||
Binary file not shown.
@@ -1,3 +0,0 @@
|
|||||||
éd¼7¶ÍÑ<>ßñ|l“z6¯n¸I_œ—Åž:£ê-ˆ!ën‚BCDÇ}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
|
|
||||||
Binary file not shown.
@@ -1,5 +1,9 @@
|
|||||||
PyQt5>=5.6.0
|
PySide6>=6.5.1
|
||||||
Pillow>=4.0.0
|
Pillow>=5.2.0
|
||||||
psutil>=5.0.0
|
psutil>=5.9.5
|
||||||
|
requests>=2.31.0
|
||||||
python-slugify>=1.2.1
|
python-slugify>=1.2.1
|
||||||
raven>=6.0.0
|
raven>=6.0.0
|
||||||
|
mozjpeg-lossless-optimization>=1.1.2
|
||||||
|
natsort[fast]>=8.4.0
|
||||||
|
distro>=1.8.0
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
@echo off
|
|
||||||
verpatch\lib\win\verpatch dist\KCC.exe %1 /va /pv %1 /s product "Kindle Comic Converter" /s description "Kindle Comic Converter" /s copyright "Copyright (C) 2012-2017 Ciro Mattia Gonano and Pawel Jastrzebski"
|
|
||||||
"C:\Program Files (x86)\Windows Kits\8.1\bin\x64\signtool.exe" sign /f "%APPVEYOR_BUILD_FOLDER%\other\windows\Cert.pfx" /p "%CERT_PASS%" /t http://time.certum.pl /d "Kindle Comic Converter" /du "http://kcc.iosphe.re/" dist/KCC.exe
|
|
||||||
iscc /SSignTool="""C:\Program Files (x86)\Windows Kits\8.1\bin\x64\signtool.exe"" sign /f ""%APPVEYOR_BUILD_FOLDER%\other\windows\Cert.pfx"" /p ""%CERT_PASS%"" /t http://time.certum.pl $p" kcc.iss >nul 2>&1
|
|
||||||
63
setup.py
Executable file → Normal file
63
setup.py
Executable file → Normal file
@@ -1,17 +1,18 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
"""
|
"""
|
||||||
pip/pyinstaller build script for KCC.
|
pip/pyinstaller build script for KCC.
|
||||||
|
|
||||||
Install as Python package:
|
Install as Python package:
|
||||||
python3 setup.py install
|
python3 setup.py install
|
||||||
|
|
||||||
Create EXE/APP/DEB:
|
Create EXE/APP:
|
||||||
python3 setup.py build_binary
|
python3 setup.py build_binary
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import platform
|
||||||
import sys
|
import sys
|
||||||
import shutil
|
|
||||||
import setuptools
|
import setuptools
|
||||||
import distutils.cmd
|
import distutils.cmd
|
||||||
from kindlecomicconverter import __version__
|
from kindlecomicconverter import __version__
|
||||||
@@ -21,6 +22,7 @@ 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 = []
|
||||||
@@ -35,41 +37,20 @@ class BuildBinaryCommand(distutils.cmd.Command):
|
|||||||
def run(self):
|
def run(self):
|
||||||
VERSION = __version__
|
VERSION = __version__
|
||||||
if sys.platform == 'darwin':
|
if sys.platform == 'darwin':
|
||||||
os.system('pyinstaller -y -F -i icons/comic2ebook.icns -n "Kindle Comic Converter" -w -s kcc.py')
|
os.system('pyinstaller -y -D -i icons/comic2ebook.icns -n "Kindle Comic Converter" -w -s kcc.py')
|
||||||
shutil.copy('other/osx/7za', 'dist/Kindle Comic Converter.app/Contents/Resources')
|
# TODO /usr/bin/codesign --force -s "$MACOS_CERTIFICATE_NAME" --options runtime dist/Applications/Kindle\ Comic\ Converter.app -v
|
||||||
shutil.copy('other/osx/unrar', 'dist/Kindle Comic Converter.app/Contents/Resources')
|
os.system(f'appdmg kcc.json dist/kcc_macos_{platform.processor()}_{VERSION}.dmg')
|
||||||
shutil.copy('other/osx/Info.plist', 'dist/Kindle Comic Converter.app/Contents')
|
sys.exit(0)
|
||||||
shutil.copy('LICENSE.txt', 'dist/Kindle Comic Converter.app/Contents/Resources')
|
|
||||||
shutil.copy('other/windows/Additional-LICENSE.txt', 'dist/Kindle Comic Converter.app/Contents/Resources')
|
|
||||||
os.chmod('dist/Kindle Comic Converter.app/Contents/Resources/unrar', 0o777)
|
|
||||||
os.chmod('dist/Kindle Comic Converter.app/Contents/Resources/7za', 0o777)
|
|
||||||
os.system('appdmg kcc.json dist/KindleComicConverter_osx_' + VERSION + '.dmg')
|
|
||||||
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')
|
||||||
if os.getenv('APPVEYOR'):
|
sys.exit(0)
|
||||||
if len(VERSION) == 3:
|
elif sys.platform == 'linux':
|
||||||
VERSION = VERSION + '.0'
|
os.system(
|
||||||
os.system('setup.bat ' + VERSION)
|
'pyinstaller --hidden-import=queue -y -F -i icons/comic2ebook.ico -n kcc_linux_' + VERSION + ' kcc.py')
|
||||||
exit(0)
|
sys.exit(0)
|
||||||
else:
|
else:
|
||||||
os.system('pyinstaller -y -F kcc.py')
|
sys.exit(0)
|
||||||
os.system('mkdir -p dist/usr/bin dist/usr/share/applications dist/usr/share/doc/kindlecomicconverter '
|
|
||||||
'dist/usr/share/kindlecomicconverter dist/usr/share/lintian/overrides')
|
|
||||||
os.system('mv dist/kcc dist/usr/bin')
|
|
||||||
os.system('cp icons/comic2ebook.png dist/usr/share/kindlecomicconverter')
|
|
||||||
os.system('cp LICENSE.txt dist/usr/share/doc/kindlecomicconverter/copyright')
|
|
||||||
os.system('cp other/linux/kindlecomicconverter.desktop dist/usr/share/applications')
|
|
||||||
os.system('cp other/linux/kindlecomicconverter dist/usr/share/lintian/overrides')
|
|
||||||
os.chdir('dist')
|
|
||||||
os.system('fpm -f -s dir -t deb -n kindlecomicconverter -v ' + VERSION +
|
|
||||||
' -m "Paweł Jastrzębski <pawelj@iosphe.re>" --license "ISC" '
|
|
||||||
'--description "$(printf "Comic and Manga converter for e-book '
|
|
||||||
'readers.\nThis app allows you to transform your PNG, JPG, GIF, '
|
|
||||||
'CBZ, CBR and CB7 files\ninto EPUB or MOBI format e-books.")" '
|
|
||||||
'--url "https://kcc.iosphe.re/" --deb-priority "optional" --vendor "" '
|
|
||||||
'--category "graphics" -d "unrar | unrar-free" -d "p7zip-full" -d "libc6" usr')
|
|
||||||
exit(0)
|
|
||||||
|
|
||||||
setuptools.setup(
|
setuptools.setup(
|
||||||
cmdclass={
|
cmdclass={
|
||||||
@@ -94,11 +75,15 @@ setuptools.setup(
|
|||||||
},
|
},
|
||||||
packages=['kindlecomicconverter'],
|
packages=['kindlecomicconverter'],
|
||||||
install_requires=[
|
install_requires=[
|
||||||
'PyQt5>=5.6.0',
|
'pyside6>=6.5.1',
|
||||||
'Pillow>=4.0.0',
|
'Pillow>=5.2.0',
|
||||||
'psutil>=5.0.0',
|
'psutil>=5.9.5',
|
||||||
'python-slugify>=1.2.1',
|
'python-slugify>=1.2.1,<9.0.0',
|
||||||
'raven>=6.0.0',
|
'raven>=6.0.0',
|
||||||
|
'requests>=2.31.0',
|
||||||
|
'mozjpeg-lossless-optimization>=1.1.2',
|
||||||
|
'natsort[fast]>=8.4.0',
|
||||||
|
'distro',
|
||||||
],
|
],
|
||||||
classifiers=[],
|
classifiers=[],
|
||||||
zip_safe=False,
|
zip_safe=False,
|
||||||
|
|||||||
Reference in New Issue
Block a user