mirror of
https://github.com/ciromattia/kcc
synced 2026-04-16 14:08:45 +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
|
||||
*.cbz
|
||||
*.cbr
|
||||
.idea
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
dist
|
||||
Output
|
||||
test
|
||||
solaio
|
||||
kindlegen*
|
||||
*.spec
|
||||
Pipfile
|
||||
Pipfile.lock
|
||||
setup.bat
|
||||
kindlecomicconverter/sentry.py
|
||||
other/windows/kindlegen.exe
|
||||
dist/
|
||||
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:
|
||||
include:
|
||||
- os: linux
|
||||
language: python
|
||||
python: 3.6
|
||||
dist: trusty
|
||||
sudo: required
|
||||
- os: osx
|
||||
language: generic
|
||||
osx_image: xcode6.4
|
||||
osx_image: xcode11.1
|
||||
|
||||
before_install:
|
||||
- if [ "$TRAVIS_OS_NAME" == "linux" ] ; then sudo apt-get -y install ruby ruby-dev ; fi
|
||||
- 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
|
||||
- pip3 install --upgrade pip setuptools wheel
|
||||
|
||||
install:
|
||||
- if [ "$TRAVIS_OS_NAME" == "linux" ] ; then pip install -r requirements.txt ; fi
|
||||
- if [ "$TRAVIS_OS_NAME" == "linux" ] ; then pip install certifi https://github.com/pyinstaller/pyinstaller/archive/develop.zip ; fi
|
||||
- if [ "$TRAVIS_OS_NAME" == "linux" ] ; then gem install fpm ; fi
|
||||
- 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
|
||||
- pip3 install -r requirements.txt
|
||||
- pip3 install certifi https://github.com/pyinstaller/pyinstaller/archive/develop.zip
|
||||
- npm install -g appdmg
|
||||
|
||||
script: python3 setup.py build_binary
|
||||
|
||||
@@ -34,13 +19,12 @@ before_deploy:
|
||||
- rm -r dist/!(*.deb|*.dmg)
|
||||
|
||||
deploy:
|
||||
provider: s3
|
||||
skip_cleanup: true
|
||||
access_key_id: AKIAIQNL5R4FI4C4NJYQ
|
||||
provider: gcs
|
||||
access_key_id: GOOG1EC62457RKUYFR2TIZUWV4EFSV2EP5LVLPPFXUAKADWJFDYPFW63BQSLA
|
||||
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
|
||||
region: eu-central-1
|
||||
local_dir: dist
|
||||
local-dir: dist
|
||||
skip_cleanup: true
|
||||
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
|
||||
|
||||
|
||||
#### 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:
|
||||
* Minor bug fixes and tweaks
|
||||
* 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
|
||||
|
||||
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
|
||||
any purpose with or without fee is hereby granted, provided that the
|
||||
|
||||
305
README.md
305
README.md
@@ -1,6 +1,10 @@
|
||||
# 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.
|
||||
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
|
||||
**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.
|
||||
_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
|
||||
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:
|
||||
- [](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YTTJ4LK2JDHPS)
|
||||
- [](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
|
||||
**KCC** is also available on PyPI.
|
||||
```
|
||||
pip install KindleComicConverter
|
||||
```
|
||||
## DOWNLOADS
|
||||
|
||||
## DEPENDENCIES
|
||||
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+
|
||||
- **https://github.com/ciromattia/kcc/releases**
|
||||
|
||||
On Debian based distributions these two commands should install all needed dependencies:
|
||||
```
|
||||
sudo apt-get install python3 python3-dev python3-pip libpng-dev libjpeg-dev p7zip-full unrar
|
||||
sudo pip3 install --upgrade pillow python-slugify psutil pyqt5 raven
|
||||
```
|
||||
Click on **Assets** of the latest release.
|
||||
|
||||
### Optional dependencies
|
||||
- [KindleGen](http://www.amazon.com/gp/feature.html?ie=UTF8&docId=1000765211) v2.9+ in a directory reachable by your _PATH_ or in _KCC_ directory *(For MOBI generation)*
|
||||
- [UnRAR](http://www.rarlab.com/download.htm) *(For CBR/RAR support)*
|
||||
- [7za](http://www.7-zip.org/download.html) *(For 7z/CB7 support)*
|
||||
You probably want either
|
||||
- `KCC_*.*.*.exe` (Windows)
|
||||
- `kcc_macos_arm_*.*.*.dmg` (recent Mac with Apple Silicon M1 chip or later)
|
||||
- `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
|
||||
**KCC** can understand and convert, at the moment, the following input types:
|
||||
- Folders containing: PNG, JPG or GIF files
|
||||
- CBZ, ZIP
|
||||
- CBR, RAR *(With `unrar` executable)*
|
||||
- CB7, 7Z *(With `7za` executable)*
|
||||
- Folders containing: PNG, JPG, GIF or WebP files
|
||||
- CBZ, ZIP *(With `7z` executable)*
|
||||
- CBR, RAR *(With `7z` executable)*
|
||||
- CB7, 7Z *(With `7z` executable)*
|
||||
- PDF *(Only extracting JPG images)*
|
||||
|
||||
## 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.
|
||||
|
||||
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:
|
||||
|
||||
```
|
||||
Usage: kcc-c2e [options] comic_file|comic_folder
|
||||
usage: kcc-c2e [options] [input]
|
||||
|
||||
Options:
|
||||
MAIN:
|
||||
-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
|
||||
MANDATORY:
|
||||
input Full path to comic folder or file(s) to be processed.
|
||||
|
||||
OUTPUT SETTINGS:
|
||||
-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) [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]
|
||||
MAIN:
|
||||
-p PROFILE, --profile PROFILE
|
||||
Device profile (Available options: K1, K2, K34, K578, KDX, KPW, KPW5, KV, KO, K11, KS, KoMT, KoG, KoGHD, KoA, KoAHD, KoAH2O, KoAO, KoN, KoC, KoL, KoF, KoS, KoE) [Default=KV]
|
||||
-m, --manga-style Manga style (right-to-left reading and splitting)
|
||||
-q, --hq Try to increase the quality of magnification
|
||||
-2, --two-panel Display two not four panels in Panel View mode
|
||||
-w, --webtoon Webtoon processing mode
|
||||
--ts TARGETSIZE, --targetsize TARGETSIZE
|
||||
the maximal size of output file in MB. [Default=100MB for webtoon and 400MB for others]
|
||||
|
||||
PROCESSING:
|
||||
-u, --upscale Resize images smaller than device's resolution
|
||||
-s, --stretch Stretch images to device's resolution
|
||||
-r SPLITTER, --splitter=SPLITTER
|
||||
Double page parsing mode. 0: Split 1: Rotate 2: Both
|
||||
[Default=0]
|
||||
-g GAMMA, --gamma=GAMMA
|
||||
Apply gamma correction to linearize the image
|
||||
[Default=Auto]
|
||||
-c CROPPING, --cropping=CROPPING
|
||||
Set cropping mode. 0: Disabled 1: Margins 2: Margins +
|
||||
page numbers [Default=2]
|
||||
--cp=CROPPINGP, --croppingpower=CROPPINGP
|
||||
PROCESSING:
|
||||
-n, --noprocessing Do not modify image and ignore any profil or processing option
|
||||
-u, --upscale Resize images smaller than device's resolution
|
||||
-s, --stretch Stretch images to device's resolution
|
||||
-r SPLITTER, --splitter SPLITTER
|
||||
Double page parsing mode. 0: Split 1: Rotate 2: Both [Default=0]
|
||||
-g GAMMA, --gamma GAMMA
|
||||
Apply gamma correction to linearize the image [Default=Auto]
|
||||
-c CROPPING, --cropping CROPPING
|
||||
Set cropping mode. 0: Disabled 1: Margins 2: Margins + page numbers [Default=2]
|
||||
--cp CROPPINGP, --croppingpower CROPPINGP
|
||||
Set cropping power [Default=1.0]
|
||||
--blackborders Disable autodetection and force black borders
|
||||
--whiteborders Disable autodetection and force white borders
|
||||
--forcecolor Don't convert images to grayscale
|
||||
--forcepng Create PNG files instead JPEG
|
||||
--cm CROPPINGM, --croppingminimum CROPPINGM
|
||||
Set cropping minimum area ratio [Default=0.0]
|
||||
--blackborders Disable autodetection and force black borders
|
||||
--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:
|
||||
--customwidth=CUSTOMWIDTH
|
||||
OUTPUT SETTINGS:
|
||||
-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
|
||||
--customheight=CUSTOMHEIGHT
|
||||
--customheight CUSTOMHEIGHT
|
||||
Replace screen height provided by device profile
|
||||
|
||||
OTHER:
|
||||
-h, --help Show this help message and exit
|
||||
OTHER:
|
||||
-h, --help Show this help message and exit
|
||||
|
||||
```
|
||||
|
||||
### Standalone `kcc-c2p.py` usage:
|
||||
|
||||
```
|
||||
Usage: kcc-c2p [options] comic_folder
|
||||
usage: kcc-c2p [options] [input]
|
||||
|
||||
Options:
|
||||
MANDATORY:
|
||||
-y HEIGHT, --height=HEIGHT
|
||||
MANDATORY:
|
||||
input Full path to comic folder(s) to be processed. Separate multiple inputs with spaces.
|
||||
|
||||
MAIN:
|
||||
-y HEIGHT, --height HEIGHT
|
||||
Height of the target device screen
|
||||
-i, --in-place Overwrite source directory
|
||||
-m, --merge Combine every directory into a single image before splitting
|
||||
-i, --in-place Overwrite source directory
|
||||
-m, --merge Combine every directory into a single image before splitting
|
||||
|
||||
OTHER:
|
||||
-d, --debug Create debug file for every split image
|
||||
-h, --help Show this help message and exit
|
||||
OTHER:
|
||||
-d, --debug Create debug file for every split image
|
||||
-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
|
||||
**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)).
|
||||
|
||||
The app relies and includes the following scripts:
|
||||
|
||||
- `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.
|
||||
- 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
|
||||
* [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](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 HD](http://kcc.iosphe.re/Samples/Ubunchu-KoAHD.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 Forma](http://kcc.iosphe.re/Samples/Ubunchu-KoF.kepub.epub)
|
||||
|
||||
## PRIVACY
|
||||
**KCC** is initiating internet connections in three cases:
|
||||
* During startup - Version check
|
||||
* When MCD metadata are used - Cover download
|
||||
* When error occurs - Automatic reporting
|
||||
**KCC** is initiating internet connections in two cases:
|
||||
* During startup - Version check.
|
||||
* When error occurs - Automatic reporting on Windows and macOS.
|
||||
|
||||
## KNOWN ISSUES
|
||||
Please check [wiki page](https://github.com/ciromattia/kcc/wiki/Known-issues).
|
||||
|
||||
## COPYRIGHT
|
||||
Copyright (c) 2012-2017 Ciro Mattia Gonano and Paweł Jastrzębski.
|
||||
**KCC** is released under ISC LICENSE; see LICENSE.txt for further details.
|
||||
Copyright (c) 2012-2023 Ciro Mattia Gonano, Paweł Jastrzębski and Darodi.
|
||||
**KCC** is released under ISC LICENSE; see [LICENSE.txt](./LICENSE.txt) for further details.
|
||||
|
||||
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:
|
||||
PYTHON: "C:\\Python36-x64"
|
||||
PYTHON: "C:\\Python37-x64"
|
||||
|
||||
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 -r requirements.txt"
|
||||
- "%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:
|
||||
- "%PYTHON%\\python.exe setup.py build_binary"
|
||||
|
||||
after_build:
|
||||
- ps: Get-ChildItem .\dist\KindleComicConverter_win_* | % { Push-AppveyorArtifact $_.FullName -FileName $_.Name }
|
||||
|
||||
deploy:
|
||||
provider: S3
|
||||
access_key_id:
|
||||
secure: pWfyU8wtAHt354mBILwM41TemOjb+My9n3CRMnrpLzI=
|
||||
secret_access_key:
|
||||
secure: G0Xpxe355LMqV3s8v+TsdJYdmhFoKKA+mxK37Tlu8yNwKXKJgcnY7pcFKSdX5xS5
|
||||
bucket: kcc-deploy
|
||||
region: eu-central-1
|
||||
artifacts:
|
||||
- path: dist\KCC*
|
||||
|
||||
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/EPUB.png</file>
|
||||
<file>../icons/MOBI.png</file>
|
||||
<file>../icons/KFX.png</file>
|
||||
</qresource>
|
||||
<qresource prefix="Status">
|
||||
<file>../icons/error.png</file>
|
||||
|
||||
464
gui/KCC.ui
464
gui/KCC.ui
@@ -22,118 +22,7 @@
|
||||
<property name="bottomMargin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<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>
|
||||
<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">
|
||||
<item row="5" column="0" colspan="2">
|
||||
<widget class="QWidget" name="optionWidget" native="true">
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<property name="leftMargin">
|
||||
@@ -148,29 +37,6 @@
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</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">
|
||||
<widget class="QCheckBox" name="qualityBox">
|
||||
<property name="toolTip">
|
||||
@@ -184,6 +50,49 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QCheckBox" name="deleteBox">
|
||||
<property name="toolTip">
|
||||
<string>Delete input file(s) or directory. It's not recoverable!</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Delete input</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="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">
|
||||
<widget class="QCheckBox" name="webtoonBox">
|
||||
<property name="toolTip">
|
||||
@@ -207,23 +116,36 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QCheckBox" name="gammaBox">
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="mangaBox">
|
||||
<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 name="text">
|
||||
<string>Custom gamma</string>
|
||||
<string>Manga mode</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QCheckBox" name="borderBox">
|
||||
<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 - 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 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 name="tristate">
|
||||
<bool>true</bool>
|
||||
@@ -240,6 +162,19 @@
|
||||
</property>
|
||||
</widget>
|
||||
</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">
|
||||
<widget class="QCheckBox" name="colorBox">
|
||||
<property name="toolTip">
|
||||
@@ -250,10 +185,30 @@
|
||||
</property>
|
||||
</widget>
|
||||
</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>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0" colspan="2">
|
||||
<item row="6" column="0" colspan="2">
|
||||
<widget class="QWidget" name="gammaWidget" native="true">
|
||||
<property name="visible">
|
||||
<bool>false</bool>
|
||||
@@ -294,9 +249,12 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QWidget" name="toolWidget" native="true">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item row="8" column="0" colspan="2">
|
||||
<widget class="QWidget" name="croppingWidget" native="true">
|
||||
<property name="visible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
@@ -310,36 +268,22 @@
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QPushButton" name="editorButton">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>30</height>
|
||||
</size>
|
||||
</property>
|
||||
<widget class="QLabel" name="croppingPowerLabel">
|
||||
<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>
|
||||
<string>Cropping power:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="wikiButton">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>30</height>
|
||||
</size>
|
||||
<widget class="QSlider" name="croppingPowerSlider">
|
||||
<property name="maximum">
|
||||
<number>200</number>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Wiki</string>
|
||||
<property name="singleStep">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="KCC.qrc">
|
||||
<normaloff>:/Other/icons/wiki.png</normaloff>:/Other/icons/wiki.png</iconset>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -418,9 +362,6 @@
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p style='white-space:pre'>Target device.</p></body></html></string>
|
||||
</property>
|
||||
<property name="sizeAdjustPolicy">
|
||||
<enum>QComboBox::AdjustToMinimumContentsLength</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="3">
|
||||
@@ -434,9 +375,6 @@
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p style='white-space:pre'>Output format.</p></body></html></string>
|
||||
</property>
|
||||
<property name="sizeAdjustPolicy">
|
||||
<enum>QComboBox::AdjustToMinimumContentsLength</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
@@ -449,12 +387,11 @@
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<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 name="text">
|
||||
<string>Convert</string>
|
||||
@@ -491,6 +428,171 @@
|
||||
<zorder>formatBox</zorder>
|
||||
</widget>
|
||||
</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>
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="statusBar">
|
||||
@@ -515,12 +617,18 @@
|
||||
<tabstop>borderBox</tabstop>
|
||||
<tabstop>outputSplit</tabstop>
|
||||
<tabstop>colorBox</tabstop>
|
||||
<tabstop>croppingBox</tabstop>
|
||||
<tabstop>mozJpegBox</tabstop>
|
||||
<tabstop>maximizeStrips</tabstop>
|
||||
<tabstop>deleteBox</tabstop>
|
||||
<tabstop>disableProcessingBox</tabstop>
|
||||
<tabstop>editorButton</tabstop>
|
||||
<tabstop>wikiButton</tabstop>
|
||||
<tabstop>jobList</tabstop>
|
||||
<tabstop>gammaSlider</tabstop>
|
||||
<tabstop>widthBox</tabstop>
|
||||
<tabstop>heightBox</tabstop>
|
||||
<tabstop>croppingPowerSlider</tabstop>
|
||||
</tabstops>
|
||||
<resources>
|
||||
<include location="KCC.qrc"/>
|
||||
|
||||
@@ -112,19 +112,6 @@
|
||||
<item row="6" column="1">
|
||||
<widget class="QLineEdit" name="coloristLine"/>
|
||||
</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>
|
||||
</widget>
|
||||
</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 -*-
|
||||
#
|
||||
# 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
|
||||
# any purpose with or without fee is hereby granted, provided that the
|
||||
@@ -19,13 +19,15 @@
|
||||
# PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
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
|
||||
|
||||
if __name__ == "__main__":
|
||||
set_start_method('spawn')
|
||||
freeze_support()
|
||||
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 -*-
|
||||
#
|
||||
# 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
|
||||
# any purpose with or without fee is hereby granted, provided that the
|
||||
@@ -19,13 +19,15 @@
|
||||
# PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
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
|
||||
|
||||
if __name__ == "__main__":
|
||||
set_start_method('spawn')
|
||||
freeze_support()
|
||||
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 -*-
|
||||
#
|
||||
# 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
|
||||
# any purpose with or without fee is hereby granted, provided that the
|
||||
@@ -19,54 +19,62 @@
|
||||
# PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
import sys
|
||||
if sys.version_info[0] != 3:
|
||||
print('ERROR: This is Python 3 script!')
|
||||
exit(1)
|
||||
|
||||
if sys.version_info < (3, 8, 0):
|
||||
print('ERROR: This is a Python 3.8+ script!')
|
||||
sys.exit(1)
|
||||
|
||||
# OS specific workarounds
|
||||
import os
|
||||
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):
|
||||
os.environ['PATH'] = os.path.dirname(os.path.abspath(sys.executable)) + \
|
||||
'/../Resources:/usr/local/bin:/usr/bin:/bin'
|
||||
os.system('defaults write com.kindlecomicconverter.KindleComicConverter ApplePersistenceIgnoreState YES')
|
||||
os.system('defaults write com.kindlecomicconverter.KindleComicConverter NSInitialToolTipDelay -int 1000')
|
||||
else:
|
||||
os.environ['PATH'] = os.path.dirname(os.path.abspath(__file__)) + '/other/osx/:' + os.environ['PATH']
|
||||
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.environ['PATH'] += os.pathsep + os.pathsep.join(mac_paths +
|
||||
[
|
||||
'/opt/homebrew/bin',
|
||||
'/usr/local/bin',
|
||||
'/usr/bin',
|
||||
'/bin',
|
||||
]
|
||||
)
|
||||
os.chdir(os.path.dirname(os.path.abspath(sys.executable)))
|
||||
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__)))
|
||||
# Load additional Sentry configuration
|
||||
if getattr(sys, 'frozen', False):
|
||||
try:
|
||||
import kindlecomicconverter.sentry
|
||||
except:
|
||||
pass
|
||||
# if getattr(sys, 'frozen', False):
|
||||
# try:
|
||||
# import kindlecomicconverter.sentry
|
||||
# except ImportError:
|
||||
# pass
|
||||
|
||||
from multiprocessing import freeze_support
|
||||
from multiprocessing import freeze_support, set_start_method
|
||||
from kindlecomicconverter.startup import start
|
||||
|
||||
if __name__ == "__main__":
|
||||
set_start_method('spawn')
|
||||
freeze_support()
|
||||
start()
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# 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
|
||||
# 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
|
||||
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
# PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from urllib.parse import unquote
|
||||
from urllib.request import urlopen, urlretrieve, Request
|
||||
from time import sleep
|
||||
from shutil import move
|
||||
from shutil import move, rmtree
|
||||
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 psutil import Popen, Process
|
||||
from psutil import Process
|
||||
from copy import copy
|
||||
from distutils.version import StrictVersion
|
||||
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 comic2ebook
|
||||
from . import metadata
|
||||
@@ -41,7 +45,7 @@ from . import KCC_ui_editor
|
||||
|
||||
|
||||
class QApplicationMessaging(QtWidgets.QApplication):
|
||||
messageFromOtherInstance = QtCore.pyqtSignal(bytes)
|
||||
messageFromOtherInstance = QtCore.Signal(bytes)
|
||||
|
||||
def __init__(self, argv):
|
||||
QtWidgets.QApplication.__init__(self, argv)
|
||||
@@ -49,7 +53,7 @@ class QApplicationMessaging(QtWidgets.QApplication):
|
||||
self._timeout = 1000
|
||||
self._locked = False
|
||||
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):
|
||||
self._server = QtNetwork.QLocalServer(self)
|
||||
self._server.newConnection.connect(self.handleMessage)
|
||||
@@ -63,7 +67,7 @@ class QApplicationMessaging(QtWidgets.QApplication):
|
||||
self._server.close()
|
||||
|
||||
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'))
|
||||
return True
|
||||
else:
|
||||
@@ -79,7 +83,7 @@ class QApplicationMessaging(QtWidgets.QApplication):
|
||||
|
||||
def sendMessage(self, message):
|
||||
socket = QtNetwork.QLocalSocket(self)
|
||||
socket.connectToServer(self._key, QtCore.QIODevice.WriteOnly)
|
||||
socket.connectToServer(self._key, QtCore.QIODeviceBase.OpenModeFlag.WriteOnly)
|
||||
socket.waitForConnected(self._timeout)
|
||||
socket.write(bytes(message, 'UTF-8'))
|
||||
socket.waitForBytesWritten(self._timeout)
|
||||
@@ -87,40 +91,40 @@ class QApplicationMessaging(QtWidgets.QApplication):
|
||||
|
||||
|
||||
class QMainWindowKCC(QtWidgets.QMainWindow):
|
||||
progressBarTick = QtCore.pyqtSignal(str)
|
||||
modeConvert = QtCore.pyqtSignal(int)
|
||||
addMessage = QtCore.pyqtSignal(str, str, bool)
|
||||
addTrayMessage = QtCore.pyqtSignal(str, str)
|
||||
showDialog = QtCore.pyqtSignal(str, str)
|
||||
hideProgressBar = QtCore.pyqtSignal()
|
||||
forceShutdown = QtCore.pyqtSignal()
|
||||
progressBarTick = QtCore.Signal(str)
|
||||
modeConvert = QtCore.Signal(int)
|
||||
addMessage = QtCore.Signal(str, str, bool)
|
||||
addTrayMessage = QtCore.Signal(str, str)
|
||||
showDialog = QtCore.Signal(str, str)
|
||||
hideProgressBar = QtCore.Signal()
|
||||
forceShutdown = QtCore.Signal()
|
||||
|
||||
|
||||
class Icons:
|
||||
def __init__(self):
|
||||
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.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.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.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.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.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.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.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.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.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):
|
||||
@@ -136,57 +140,22 @@ class VersionThread(QtCore.QThread):
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
XML = parse(urlopen(Request('https://kcc.iosphe.re/Version/',
|
||||
headers={'User-Agent': 'KindleComicConverter/' + __version__})))
|
||||
json_parser = requests.get("https://api.github.com/repos/ciromattia/kcc/releases/latest").json()
|
||||
|
||||
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:
|
||||
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):
|
||||
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')
|
||||
def setAnswer(self, dialoganswer):
|
||||
self.answer = dialoganswer
|
||||
|
||||
|
||||
class ProgressThread(QtCore.QThread):
|
||||
@@ -238,45 +207,64 @@ class WorkerThread(QtCore.QThread):
|
||||
MW.addTrayMessage.emit('Conversion interrupted.', 'Critical')
|
||||
MW.modeConvert.emit(1)
|
||||
|
||||
# noinspection PyUnboundLocalVariable
|
||||
def run(self):
|
||||
MW.modeConvert.emit(0)
|
||||
|
||||
parser = comic2ebook.makeParser()
|
||||
options, _ = parser.parse_args()
|
||||
options = parser.parse_args()
|
||||
argv = ''
|
||||
currentJobs = []
|
||||
|
||||
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():
|
||||
options.righttoleft = True
|
||||
if GUI.rotateBox.checkState() == 1:
|
||||
if GUI.rotateBox.checkState() == Qt.CheckState.PartiallyChecked:
|
||||
options.splitter = 2
|
||||
elif GUI.rotateBox.checkState() == 2:
|
||||
elif GUI.rotateBox.checkState() == Qt.CheckState.Checked:
|
||||
options.splitter = 1
|
||||
if GUI.qualityBox.checkState() == 1:
|
||||
if GUI.qualityBox.checkState() == Qt.CheckState.PartiallyChecked:
|
||||
options.autoscale = True
|
||||
elif GUI.qualityBox.checkState() == 2:
|
||||
elif GUI.qualityBox.checkState() == Qt.CheckState.Checked:
|
||||
options.hq = True
|
||||
if GUI.webtoonBox.isChecked():
|
||||
options.webtoon = True
|
||||
if GUI.upscaleBox.checkState() == 1:
|
||||
if GUI.upscaleBox.checkState() == Qt.CheckState.PartiallyChecked:
|
||||
options.stretch = True
|
||||
elif GUI.upscaleBox.checkState() == 2:
|
||||
elif GUI.upscaleBox.checkState() == Qt.CheckState.Checked:
|
||||
options.upscale = True
|
||||
if GUI.gammaBox.isChecked() and float(GUI.gammaValue) > 0.09:
|
||||
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
|
||||
elif GUI.borderBox.checkState() == 2:
|
||||
elif GUI.borderBox.checkState() == Qt.CheckState.Checked:
|
||||
options.black_borders = True
|
||||
if GUI.outputSplit.isChecked():
|
||||
options.batchsplit = 2
|
||||
if GUI.colorBox.isChecked():
|
||||
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:
|
||||
options.customwidth = str(GUI.widthBox.value())
|
||||
options.customheight = str(GUI.heightBox.value())
|
||||
if GUI.targetDirectory != '':
|
||||
options.output = GUI.targetDirectory
|
||||
|
||||
for i in range(GUI.jobList.count()):
|
||||
# Make sure that we don't consider any system message as job to do
|
||||
@@ -290,7 +278,7 @@ class WorkerThread(QtCore.QThread):
|
||||
return
|
||||
self.errors = 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)
|
||||
GUI.progress.content = 'Creating CBZ files'
|
||||
else:
|
||||
@@ -299,8 +287,7 @@ class WorkerThread(QtCore.QThread):
|
||||
jobargv = list(argv)
|
||||
jobargv.append(job)
|
||||
try:
|
||||
comic2ebook.options = copy(options)
|
||||
comic2ebook.checkOptions()
|
||||
comic2ebook.options = comic2ebook.checkOptions(copy(options))
|
||||
outputPath = comic2ebook.makeBook(job, self)
|
||||
MW.hideProgressBar.emit()
|
||||
except UserWarning as warn:
|
||||
@@ -341,11 +328,11 @@ class WorkerThread(QtCore.QThread):
|
||||
return
|
||||
if not self.errors:
|
||||
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)
|
||||
else:
|
||||
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(str(len(outputPath) * 2 + 1))
|
||||
MW.progressBarTick.emit('tick')
|
||||
@@ -446,7 +433,7 @@ class SystemTrayIcon(QtWidgets.QSystemTrayIcon):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
if self.isSystemTrayAvailable():
|
||||
QtWidgets.QSystemTrayIcon.__init__(self, GUI.icons.programIcon, MW)
|
||||
self.setIcon(GUI.icons.programIcon)
|
||||
self.activated.connect(self.catchClicks)
|
||||
|
||||
def catchClicks(self):
|
||||
@@ -455,7 +442,7 @@ class SystemTrayIcon(QtWidgets.QSystemTrayIcon):
|
||||
MW.activateWindow()
|
||||
|
||||
def addTrayMessage(self, message, icon):
|
||||
icon = eval('QtWidgets.QSystemTrayIcon.' + icon)
|
||||
icon = getattr(QtWidgets.QSystemTrayIcon.MessageIcon, icon)
|
||||
if self.supportsMessages() and not MW.isActiveWindow():
|
||||
self.showMessage('Kindle Comic Converter', message, icon)
|
||||
|
||||
@@ -477,20 +464,12 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
if self.needClean:
|
||||
self.needClean = False
|
||||
GUI.jobList.clear()
|
||||
if self.UnRAR:
|
||||
if self.sevenza:
|
||||
fnames = QtWidgets.QFileDialog.getOpenFileNames(MW, 'Select file', self.lastPath,
|
||||
'Comic (*.cbz *.cbr *.cb7 *.zip *.rar *.7z *.pdf)')
|
||||
else:
|
||||
fnames = QtWidgets.QFileDialog.getOpenFileNames(MW, 'Select file', self.lastPath,
|
||||
'Comic (*.cbz *.cbr *.zip *.rar *.pdf)')
|
||||
if self.tar or self.sevenzip:
|
||||
fnames = QtWidgets.QFileDialog.getOpenFileNames(MW, 'Select file', self.lastPath,
|
||||
'Comic (*.cbz *.cbr *.cb7 *.zip *.rar *.7z *.pdf);;All (*.*)')
|
||||
else:
|
||||
if self.sevenza:
|
||||
fnames = QtWidgets.QFileDialog.getOpenFileNames(MW, 'Select file', self.lastPath,
|
||||
'Comic (*.cbz *.cb7 *.zip *.7z *.pdf)')
|
||||
else:
|
||||
fnames = QtWidgets.QFileDialog.getOpenFileNames(MW, 'Select file', self.lastPath,
|
||||
'Comic (*.cbz *.zip *.pdf)')
|
||||
fnames = QtWidgets.QFileDialog.getOpenFileNames(MW, 'Select file', self.lastPath,
|
||||
'Comic (*.pdf);;All (*.*)')
|
||||
for fname in fnames[0]:
|
||||
if fname != '':
|
||||
if sys.platform.startswith('win'):
|
||||
@@ -500,28 +479,32 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
GUI.jobList.scrollToBottom()
|
||||
|
||||
def selectFileMetaEditor(self):
|
||||
if self.UnRAR:
|
||||
if self.sevenza:
|
||||
sname = ''
|
||||
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,
|
||||
'Comic (*.cbz *.cbr *.cb7)')
|
||||
else:
|
||||
fname = QtWidgets.QFileDialog.getOpenFileName(MW, 'Select file', self.lastPath,
|
||||
'Comic (*.cbz *.cbr)')
|
||||
else:
|
||||
if self.sevenza:
|
||||
fname = QtWidgets.QFileDialog.getOpenFileName(MW, 'Select file', self.lastPath,
|
||||
'Comic (*.cbz *.cb7)')
|
||||
else:
|
||||
fname = QtWidgets.QFileDialog.getOpenFileName(MW, 'Select file', self.lastPath,
|
||||
'Comic (*.cbz)')
|
||||
if fname[0] != '':
|
||||
if sys.platform.startswith('win'):
|
||||
fname = fname[0].replace('/', '\\')
|
||||
else:
|
||||
fname = fname[0]
|
||||
self.lastPath = os.path.abspath(os.path.join(fname, os.pardir))
|
||||
fname = ['']
|
||||
self.showDialog("Editor is disabled due to a lack of 7z.", 'error')
|
||||
self.addMessage('<a href="https://github.com/ciromattia/kcc#7-zip">Install 7z (link)</a>'
|
||||
' to enable metadata editing.', 'warning')
|
||||
if fname[0] != '':
|
||||
if sys.platform.startswith('win'):
|
||||
sname = fname[0].replace('/', '\\')
|
||||
else:
|
||||
sname = fname[0]
|
||||
self.lastPath = os.path.abspath(os.path.join(sname, os.pardir))
|
||||
if sname != '':
|
||||
try:
|
||||
self.editor.loadData(fname)
|
||||
self.editor.loadData(sname)
|
||||
except Exception as err:
|
||||
_, _, traceback = sys.exc_info()
|
||||
GUI.sentry.captureException()
|
||||
@@ -571,7 +554,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
self.conversionAlive = False
|
||||
self.worker.sync()
|
||||
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.setText('Convert')
|
||||
GUI.centralWidget.setAcceptDrops(True)
|
||||
@@ -579,7 +562,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
self.conversionAlive = True
|
||||
self.worker.sync()
|
||||
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.setText('Abort')
|
||||
GUI.centralWidget.setAcceptDrops(False)
|
||||
@@ -597,6 +580,13 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
if self.currentMode != 3:
|
||||
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):
|
||||
if value:
|
||||
GUI.qualityBox.setEnabled(False)
|
||||
@@ -618,9 +608,9 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
def togglequalityBox(self, value):
|
||||
profile = GUI.profiles[str(GUI.deviceBox.currentText())]
|
||||
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('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.setChecked(True)
|
||||
else:
|
||||
@@ -637,6 +627,13 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
GUI.gammaSlider.setValue(valueRaw)
|
||||
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):
|
||||
profile = GUI.profiles[str(GUI.deviceBox.currentText())]
|
||||
if profile['ForceExpert']:
|
||||
@@ -645,27 +642,29 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
self.modeChange(2)
|
||||
else:
|
||||
self.modeChange(1)
|
||||
GUI.colorBox.setChecked(profile['ForceColor'])
|
||||
self.changeFormat()
|
||||
GUI.gammaSlider.setValue(0)
|
||||
self.changeGamma(0)
|
||||
if not GUI.webtoonBox.isChecked():
|
||||
GUI.qualityBox.setEnabled(profile['PVOptions'])
|
||||
GUI.upscaleBox.setChecked(profile['DefaultUpscale'])
|
||||
GUI.mangaBox.setChecked(True)
|
||||
if not profile['PVOptions']:
|
||||
GUI.qualityBox.setChecked(False)
|
||||
if str(GUI.deviceBox.currentText()) == 'Other':
|
||||
self.addMessage('<a href="https://github.com/ciromattia/kcc/wiki/NonKindle-devices">'
|
||||
'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())]
|
||||
if outputFormat is not None:
|
||||
GUI.formatBox.setCurrentIndex(outputFormat)
|
||||
if outputformat is not None:
|
||||
GUI.formatBox.setCurrentIndex(outputformat)
|
||||
else:
|
||||
GUI.formatBox.setCurrentIndex(profile['DefaultFormat'])
|
||||
if not GUI.webtoonBox.isChecked():
|
||||
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)
|
||||
else:
|
||||
GUI.outputSplit.setEnabled(False)
|
||||
@@ -678,7 +677,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
|
||||
def addMessage(self, message, icon, replace=False):
|
||||
if icon != '':
|
||||
icon = eval('self.icons.' + icon)
|
||||
icon = getattr(self.icons, icon)
|
||||
item = QtWidgets.QListWidgetItem(icon, ' ' + self.stripTags(message))
|
||||
else:
|
||||
item = QtWidgets.QListWidgetItem(' ' + self.stripTags(message))
|
||||
@@ -696,11 +695,11 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
|
||||
def showDialog(self, message, kind):
|
||||
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':
|
||||
GUI.versionCheck.setAnswer(QtWidgets.QMessageBox.question(MW, 'KCC - Question', message,
|
||||
QtWidgets.QMessageBox.Yes,
|
||||
QtWidgets.QMessageBox.No))
|
||||
QtWidgets.QMessageBox.Yes,
|
||||
QtWidgets.QMessageBox.No))
|
||||
|
||||
def updateProgressbar(self, command):
|
||||
if command == 'tick':
|
||||
@@ -724,7 +723,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
self.conversionAlive = False
|
||||
self.worker.sync()
|
||||
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)
|
||||
if dname != '':
|
||||
if sys.platform.startswith('win'):
|
||||
@@ -747,22 +746,21 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
self.addMessage('Target resolution is not set!', 'error')
|
||||
self.needClean = True
|
||||
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()
|
||||
if not self.kindleGen:
|
||||
GUI.jobList.clear()
|
||||
self.addMessage('Cannot find <a href="http://www.amazon.com/gp/feature.html?ie=UTF8&docId='
|
||||
'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.display_kindlegen_missing()
|
||||
self.needClean = True
|
||||
return
|
||||
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):
|
||||
if self.conversionAlive:
|
||||
GUI.convertButton.setEnabled(False)
|
||||
@@ -778,17 +776,24 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
self.settings.setValue('currentFormat', GUI.formatBox.currentIndex())
|
||||
self.settings.setValue('startNumber', self.startNumber + 1)
|
||||
self.settings.setValue('windowSize', str(MW.size().width()) + 'x' + str(MW.size().height()))
|
||||
self.settings.setValue('options', {'mangaBox': GUI.mangaBox.checkState(),
|
||||
'rotateBox': GUI.rotateBox.checkState(),
|
||||
'qualityBox': GUI.qualityBox.checkState(),
|
||||
'gammaBox': GUI.gammaBox.checkState(),
|
||||
'upscaleBox': GUI.upscaleBox.checkState(),
|
||||
'borderBox': GUI.borderBox.checkState(),
|
||||
'webtoonBox': GUI.webtoonBox.checkState(),
|
||||
'outputSplit': GUI.outputSplit.checkState(),
|
||||
'colorBox': GUI.colorBox.checkState(),
|
||||
self.settings.setValue('options', {'mangaBox': GUI.mangaBox.checkState().value,
|
||||
'rotateBox': GUI.rotateBox.checkState().value,
|
||||
'qualityBox': GUI.qualityBox.checkState().value,
|
||||
'gammaBox': GUI.gammaBox.checkState().value,
|
||||
'croppingBox': GUI.croppingBox.checkState().value,
|
||||
'croppingPowerSlider': float(self.croppingPowerValue) * 100,
|
||||
'upscaleBox': GUI.upscaleBox.checkState().value,
|
||||
'borderBox': GUI.borderBox.checkState().value,
|
||||
'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(),
|
||||
'heightBox': GUI.heightBox.value(),
|
||||
'deleteBox': GUI.deleteBox.checkState().value,
|
||||
'dedupeCoverBox': GUI.dedupeCoverBox.checkState().value,
|
||||
'maximizeStrips': GUI.maximizeStrips.checkState().value,
|
||||
'gammaSlider': float(self.gammaValue) * 100})
|
||||
self.settings.sync()
|
||||
self.tray.hide()
|
||||
@@ -802,16 +807,9 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
if self.needClean:
|
||||
self.needClean = False
|
||||
GUI.jobList.clear()
|
||||
if self.UnRAR:
|
||||
if self.sevenza:
|
||||
formats = ['.cbz', '.cbr', '.cb7', '.zip', '.rar', '.7z', '.pdf']
|
||||
else:
|
||||
formats = ['.cbz', '.cbr', '.zip', '.rar', '.pdf']
|
||||
else:
|
||||
if self.sevenza:
|
||||
formats = ['.cbz', '.cb7', '.zip', '.7z', '.pdf']
|
||||
else:
|
||||
formats = ['.cbz', '.zip', '.pdf']
|
||||
formats = ['.pdf']
|
||||
if self.tar or self.sevenzip:
|
||||
formats.extend(['.cb7', '.7z', '.cbz', '.zip', '.cbr', '.rar'])
|
||||
if os.path.isdir(message):
|
||||
GUI.jobList.addItem(message)
|
||||
GUI.jobList.scrollToBottom()
|
||||
@@ -821,7 +819,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
GUI.jobList.addItem(message)
|
||||
GUI.jobList.scrollToBottom()
|
||||
else:
|
||||
self.addMessage('This file type is unsupported!', 'error')
|
||||
self.addMessage('Unsupported file type for ' + message, 'error')
|
||||
|
||||
def dragAndDrop(self, e):
|
||||
e.accept()
|
||||
@@ -847,46 +845,37 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
os.chmod('/usr/local/bin/kindlegen', 0o755)
|
||||
except Exception:
|
||||
pass
|
||||
kindleGenExitCode = Popen('kindlegen -locale en', stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
|
||||
if kindleGenExitCode.wait() == 0:
|
||||
try:
|
||||
versionCheck = subprocess_run_silent(['kindlegen', '-locale', 'en'], stdout=PIPE, stderr=STDOUT, encoding='UTF-8')
|
||||
self.kindleGen = True
|
||||
versionCheck = Popen('kindlegen -locale en', stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
|
||||
for line in versionCheck.stdout:
|
||||
line = line.decode("utf-8")
|
||||
for line in versionCheck.stdout.splitlines():
|
||||
if 'Amazon kindlegen' in line:
|
||||
versionCheck = line.split('V')[1].split(' ')[0]
|
||||
if StrictVersion(versionCheck) < StrictVersion('2.9'):
|
||||
self.addMessage('Your <a href="http://www.amazon.com/gp/feature.html?ie=UTF8&docId='
|
||||
'1000765211">KindleGen</a> is outdated! MOBI conversion might fail.', 'warning')
|
||||
self.addMessage('Your <a href="https://www.amazon.com/b?node=23496309011">KindleGen</a>'
|
||||
' is outdated! MOBI conversion might fail.', 'warning')
|
||||
break
|
||||
else:
|
||||
except FileNotFoundError:
|
||||
self.kindleGen = False
|
||||
if startup:
|
||||
self.addMessage('Cannot find <a href="http://www.amazon.com/gp/feature.html?ie=UTF8&docId=1000765211">'
|
||||
'<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')
|
||||
self.display_kindlegen_missing()
|
||||
|
||||
def __init__(self, KCCAplication, KCCWindow):
|
||||
def __init__(self, kccapp, kccwindow):
|
||||
global APP, MW, GUI
|
||||
APP = KCCAplication
|
||||
MW = KCCWindow
|
||||
APP = kccapp
|
||||
MW = kccwindow
|
||||
GUI = self
|
||||
self.setupUi(MW)
|
||||
self.editor = KCCGUI_MetaEditor()
|
||||
self.icons = Icons()
|
||||
self.settings = QtCore.QSettings('KindleComicConverter', 'KindleComicConverter')
|
||||
self.settings = QtCore.QSettings('ciromattia', 'kcc')
|
||||
self.settingsVersion = self.settings.value('settingsVersion', '', type=str)
|
||||
self.lastPath = self.settings.value('lastPath', '', type=str)
|
||||
self.lastDevice = self.settings.value('lastDevice', 0, type=int)
|
||||
self.currentFormat = self.settings.value('currentFormat', 0, type=int)
|
||||
self.startNumber = self.settings.value('startNumber', 0, type=int)
|
||||
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.versionCheck = VersionThread()
|
||||
self.progress = ProgressThread()
|
||||
@@ -895,10 +884,12 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
self.needClean = True
|
||||
self.kindleGen = False
|
||||
self.gammaValue = 1.0
|
||||
self.croppingPowerValue = 1.0
|
||||
self.currentMode = 1
|
||||
self.targetDirectory = ''
|
||||
self.sentry = Client(release=__version__)
|
||||
if sys.platform.startswith('win'):
|
||||
# noinspection PyUnresolvedReferences
|
||||
from psutil import BELOW_NORMAL_PRIORITY_CLASS
|
||||
self.p = Process(os.getpid())
|
||||
self.p.nice(BELOW_NORMAL_PRIORITY_CLASS)
|
||||
@@ -910,73 +901,128 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
elif sys.platform.startswith('darwin'):
|
||||
for element in ['editorButton', 'wikiButton', 'directoryButton', 'clearButton', 'fileButton', 'deviceBox',
|
||||
'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)
|
||||
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':
|
||||
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 = {
|
||||
"Kindle Oasis": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
|
||||
'DefaultUpscale': True, 'Label': 'KV'},
|
||||
"Kindle Oasis 9/10": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
|
||||
'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,
|
||||
'DefaultUpscale': True, 'Label': 'KV'},
|
||||
"Kindle PW 3": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
|
||||
'DefaultUpscale': True, 'Label': 'KV'},
|
||||
"Kindle PW 1/2": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
|
||||
'DefaultUpscale': False, 'Label': 'KPW'},
|
||||
"Kindle": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
|
||||
'DefaultUpscale': False, 'Label': 'K578'},
|
||||
"Kindle DX/DXG": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 2,
|
||||
'DefaultUpscale': False, 'Label': 'KDX'},
|
||||
'DefaultUpscale': True, 'ForceColor': False, 'Label': 'KV'},
|
||||
"Kindle Scribe": {
|
||||
'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': False, 'ForceColor': False, 'Label': 'KS',
|
||||
},
|
||||
"Kindle 11": {
|
||||
'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': True, 'ForceColor': False, 'Label': 'K11',
|
||||
},
|
||||
"Kindle PW 11": {
|
||||
'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,
|
||||
'DefaultUpscale': False, 'Label': 'KoMT'},
|
||||
'DefaultUpscale': False, 'ForceColor': False, 'Label': 'KoMT'},
|
||||
"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,
|
||||
'DefaultUpscale': False, 'Label': 'KoGHD'},
|
||||
'DefaultUpscale': False, 'ForceColor': False, 'Label': 'KoGHD'},
|
||||
"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,
|
||||
'DefaultUpscale': True, 'Label': 'KoAHD'},
|
||||
'DefaultUpscale': True, 'ForceColor': False, 'Label': 'KoAHD'},
|
||||
"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,
|
||||
'DefaultUpscale': True, 'Label': 'KoAO'},
|
||||
"Other": {'PVOptions': False, 'ForceExpert': True, 'DefaultFormat': 1,
|
||||
'DefaultUpscale': False, 'Label': 'OTHER'},
|
||||
'DefaultUpscale': True, 'ForceColor': False, 'Label': 'KoAO'},
|
||||
"Kobo Clara HD": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1,
|
||||
'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,
|
||||
'DefaultUpscale': False, 'Label': 'K1'},
|
||||
'DefaultUpscale': False, 'ForceColor': False, 'Label': 'K1'},
|
||||
"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,
|
||||
'DefaultUpscale': False, 'Label': 'K34'},
|
||||
'DefaultUpscale': False, 'ForceColor': False, 'Label': 'K34'},
|
||||
"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 = [
|
||||
"Kindle Oasis",
|
||||
"Kindle Voyage",
|
||||
"Kindle PW 3",
|
||||
"Kindle PW 1/2",
|
||||
"Kindle",
|
||||
"Kindle Scribe",
|
||||
"Kindle 11",
|
||||
"Kindle PW 11",
|
||||
"Kindle Oasis 9/10",
|
||||
"Separator",
|
||||
"Kobo Aura ONE",
|
||||
"Kobo Aura H2O",
|
||||
"Kobo Aura HD",
|
||||
"Kobo Aura",
|
||||
"Kobo Clara 2E",
|
||||
"Kobo Clara Colour",
|
||||
"Kobo Sage",
|
||||
"Kobo Libra 2",
|
||||
"Kobo Libra Colour",
|
||||
"Kobo Elipsa",
|
||||
"Kobo Nia",
|
||||
"Separator",
|
||||
"Other",
|
||||
"Separator",
|
||||
"Kindle Oasis 8",
|
||||
"Kindle PW 7/10",
|
||||
"Kindle Voyage",
|
||||
"Kindle PW 5/6",
|
||||
"Kindle 4/5/7/8/10",
|
||||
"Kindle Touch",
|
||||
"Kindle Keyboard",
|
||||
"Kindle DX/DXG",
|
||||
"Kindle DX",
|
||||
"Kindle 2",
|
||||
"Kindle 1",
|
||||
"Separator",
|
||||
"Kobo Aura",
|
||||
"Kobo Aura ONE",
|
||||
"Kobo Aura H2O",
|
||||
"Kobo Aura HD",
|
||||
"Kobo Clara HD",
|
||||
"Kobo Forma",
|
||||
"Kobo Glo HD",
|
||||
"Kobo Glo",
|
||||
"Kobo Libra H2O",
|
||||
"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'
|
||||
'NATE</a> - <a href="http://www.mobileread.com/forums/showthread.php?t=207461'
|
||||
'">FORUM</a></b>')
|
||||
statusBarLabel.setAlignment(QtCore.Qt.AlignCenter)
|
||||
statusBarLabel.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
|
||||
statusBarLabel.setOpenExternalLinks(True)
|
||||
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 '
|
||||
'<a href="https://github.com/ciromattia/kcc/wiki/Important-tips">important tips</a>.',
|
||||
'info')
|
||||
rarExitCode = Popen('unrar', stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
|
||||
rarExitCode = rarExitCode.wait()
|
||||
if rarExitCode == 0 or rarExitCode == 7:
|
||||
self.UnRAR = True
|
||||
else:
|
||||
self.UnRAR = False
|
||||
self.addMessage('Cannot find <a href="http://www.rarlab.com/rar_add.htm">UnRAR</a>!'
|
||||
' Processing of CBR/RAR files will be disabled.', 'warning')
|
||||
sevenzaExitCode = Popen('7za', stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
|
||||
sevenzaExitCode = sevenzaExitCode.wait()
|
||||
if sevenzaExitCode == 0 or sevenzaExitCode == 7:
|
||||
self.sevenza = True
|
||||
else:
|
||||
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')
|
||||
try:
|
||||
subprocess_run_silent(['tar'], stdout=PIPE, stderr=STDOUT)
|
||||
self.tar = True
|
||||
except FileNotFoundError:
|
||||
self.tar = False
|
||||
try:
|
||||
subprocess_run_silent(['7z'], stdout=PIPE, stderr=STDOUT)
|
||||
self.sevenzip = True
|
||||
except FileNotFoundError:
|
||||
self.sevenzip = False
|
||||
if not self.tar:
|
||||
self.addMessage('<a href="https://github.com/ciromattia/kcc#7-zip">Install 7z (link)</a>'
|
||||
' to enable CBZ/CBR/ZIP/etc processing.', 'warning')
|
||||
self.detectKindleGen(True)
|
||||
|
||||
APP.messageFromOtherInstance.connect(self.handleMessage)
|
||||
@@ -1021,6 +1064,8 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
GUI.convertButton.clicked.connect(self.convertStart)
|
||||
GUI.gammaSlider.valueChanged.connect(self.changeGamma)
|
||||
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.qualityBox.stateChanged.connect(self.togglequalityBox)
|
||||
GUI.deviceBox.activated.connect(self.changeDevice)
|
||||
@@ -1048,8 +1093,8 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
GUI.deviceBox.addItem(self.icons.deviceKobo, profile)
|
||||
else:
|
||||
GUI.deviceBox.addItem(self.icons.deviceKindle, profile)
|
||||
for f in ['MOBI/AZW3', 'EPUB', 'CBZ']:
|
||||
GUI.formatBox.addItem(eval('self.icons.' + f.replace('/AZW3', '') + 'Format'), f)
|
||||
for f in self.formats:
|
||||
GUI.formatBox.addItem(getattr(self.icons, self.formats[f]['icon'] + 'Format'), f)
|
||||
if self.lastDevice > GUI.deviceBox.count():
|
||||
self.lastDevice = 0
|
||||
if profilesGUI[self.lastDevice] == "Separator":
|
||||
@@ -1069,16 +1114,26 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
if GUI.gammaSlider.isEnabled():
|
||||
GUI.gammaSlider.setValue(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:
|
||||
try:
|
||||
if eval('GUI.' + str(option)).isEnabled():
|
||||
eval('GUI.' + str(option)).setCheckState(self.options[option])
|
||||
if getattr(GUI, option).isEnabled():
|
||||
getattr(GUI, option).setCheckState(Qt.CheckState(self.options[option]))
|
||||
except AttributeError:
|
||||
pass
|
||||
self.worker.sync()
|
||||
self.versionCheck.start()
|
||||
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':
|
||||
x, y = self.windowSize.split('x')
|
||||
MW.resize(int(x), int(y))
|
||||
@@ -1090,7 +1145,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
class KCCGUI_MetaEditor(KCC_ui_editor.Ui_editorDialog):
|
||||
def loadData(self, file):
|
||||
self.parser = metadata.MetadataParser(file)
|
||||
if self.parser.compressor == 'rar':
|
||||
if self.parser.format in ['RAR', 'RAR5']:
|
||||
self.editorWidget.setEnabled(False)
|
||||
self.okButton.setEnabled(False)
|
||||
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.okButton.setEnabled(True)
|
||||
self.statusLabel.setText('Separate authors with a comma.')
|
||||
for field in (self.seriesLine, self.volumeLine, self.numberLine, self.muidLine):
|
||||
if field.objectName() == 'muidLine':
|
||||
field.setText(self.parser.data['MUid'])
|
||||
else:
|
||||
field.setText(self.parser.data[field.objectName().capitalize()[:-4]])
|
||||
for field in (self.seriesLine, self.volumeLine, self.numberLine):
|
||||
field.setText(self.parser.data[field.objectName().capitalize()[:-4]])
|
||||
for field in (self.writerLine, self.pencillerLine, self.inkerLine, self.coloristLine):
|
||||
field.setText(', '.join(self.parser.data[field.objectName().capitalize()[:-4] + 's']))
|
||||
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):
|
||||
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.objectName() == 'muidLine':
|
||||
self.parser.data['MUid'] = self.cleanData(field.text())
|
||||
else:
|
||||
self.parser.data[field.objectName().capitalize()[:-4]] = self.cleanData(field.text())
|
||||
self.parser.data[field.objectName().capitalize()[:-4]] = self.cleanData(field.text())
|
||||
else:
|
||||
self.statusLabel.setText(field.objectName().capitalize()[:-4] + ' field must be a number.')
|
||||
break
|
||||
@@ -1143,7 +1195,7 @@ class KCCGUI_MetaEditor(KCC_ui_editor.Ui_editorDialog):
|
||||
self.ui = QtWidgets.QDialog()
|
||||
self.parser = None
|
||||
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.cancelButton.clicked.connect(self.ui.close)
|
||||
if sys.platform.startswith('linux'):
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,272 +1,463 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Form implementation generated from reading ui file 'gui\KCC.ui'
|
||||
#
|
||||
# Created by: PyQt5 UI code generator 5.8.1
|
||||
#
|
||||
# WARNING! All changes made in this file will be lost!
|
||||
################################################################################
|
||||
## Form generated from reading UI file 'KCC.ui'
|
||||
##
|
||||
## Created by: Qt User Interface Compiler version 6.5.2
|
||||
##
|
||||
## 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):
|
||||
def setupUi(self, mainWindow):
|
||||
mainWindow.setObjectName("mainWindow")
|
||||
if not mainWindow.objectName():
|
||||
mainWindow.setObjectName(u"mainWindow")
|
||||
mainWindow.resize(450, 400)
|
||||
icon = QtGui.QIcon()
|
||||
icon.addPixmap(QtGui.QPixmap(":/Icon/icons/comic2ebook.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||
icon = QIcon()
|
||||
icon.addFile(u":/Icon/icons/comic2ebook.png", QSize(), QIcon.Normal, QIcon.Off)
|
||||
mainWindow.setWindowIcon(icon)
|
||||
self.centralWidget = QtWidgets.QWidget(mainWindow)
|
||||
self.centralWidget.setObjectName("centralWidget")
|
||||
self.gridLayout = QtWidgets.QGridLayout(self.centralWidget)
|
||||
self.centralWidget = QWidget(mainWindow)
|
||||
self.centralWidget.setObjectName(u"centralWidget")
|
||||
self.gridLayout = QGridLayout(self.centralWidget)
|
||||
self.gridLayout.setObjectName(u"gridLayout")
|
||||
self.gridLayout.setContentsMargins(-1, -1, -1, 5)
|
||||
self.gridLayout.setObjectName("gridLayout")
|
||||
self.progressBar = QtWidgets.QProgressBar(self.centralWidget)
|
||||
self.progressBar.setMinimumSize(QtCore.QSize(0, 30))
|
||||
font = QtGui.QFont()
|
||||
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.optionWidget = QWidget(self.centralWidget)
|
||||
self.optionWidget.setObjectName(u"optionWidget")
|
||||
self.gridLayout_2 = QGridLayout(self.optionWidget)
|
||||
self.gridLayout_2.setObjectName(u"gridLayout_2")
|
||||
self.gridLayout_2.setContentsMargins(0, 0, 0, 0)
|
||||
self.gridLayout_2.setObjectName("gridLayout_2")
|
||||
self.mangaBox = QtWidgets.QCheckBox(self.optionWidget)
|
||||
self.mangaBox.setObjectName("mangaBox")
|
||||
self.gridLayout_2.addWidget(self.mangaBox, 0, 0, 1, 1)
|
||||
self.rotateBox = QtWidgets.QCheckBox(self.optionWidget)
|
||||
self.rotateBox.setTristate(True)
|
||||
self.rotateBox.setObjectName("rotateBox")
|
||||
self.gridLayout_2.addWidget(self.rotateBox, 0, 1, 1, 1)
|
||||
self.qualityBox = QtWidgets.QCheckBox(self.optionWidget)
|
||||
self.qualityBox = QCheckBox(self.optionWidget)
|
||||
self.qualityBox.setObjectName(u"qualityBox")
|
||||
self.qualityBox.setTristate(True)
|
||||
self.qualityBox.setObjectName("qualityBox")
|
||||
|
||||
self.gridLayout_2.addWidget(self.qualityBox, 0, 2, 1, 1)
|
||||
self.webtoonBox = QtWidgets.QCheckBox(self.optionWidget)
|
||||
self.webtoonBox.setObjectName("webtoonBox")
|
||||
self.gridLayout_2.addWidget(self.webtoonBox, 1, 0, 1, 1)
|
||||
self.upscaleBox = QtWidgets.QCheckBox(self.optionWidget)
|
||||
self.upscaleBox.setTristate(True)
|
||||
self.upscaleBox.setObjectName("upscaleBox")
|
||||
self.gridLayout_2.addWidget(self.upscaleBox, 1, 1, 1, 1)
|
||||
self.gammaBox = QtWidgets.QCheckBox(self.optionWidget)
|
||||
self.gammaBox.setObjectName("gammaBox")
|
||||
|
||||
self.deleteBox = QCheckBox(self.optionWidget)
|
||||
self.deleteBox.setObjectName(u"deleteBox")
|
||||
|
||||
self.gridLayout_2.addWidget(self.deleteBox, 4, 1, 1, 1)
|
||||
|
||||
self.maximizeStrips = QCheckBox(self.optionWidget)
|
||||
self.maximizeStrips.setObjectName(u"maximizeStrips")
|
||||
|
||||
self.gridLayout_2.addWidget(self.maximizeStrips, 3, 1, 1, 1)
|
||||
|
||||
self.gammaBox = QCheckBox(self.optionWidget)
|
||||
self.gammaBox.setObjectName(u"gammaBox")
|
||||
|
||||
self.gridLayout_2.addWidget(self.gammaBox, 1, 2, 1, 1)
|
||||
self.borderBox = QtWidgets.QCheckBox(self.optionWidget)
|
||||
|
||||
self.borderBox = QCheckBox(self.optionWidget)
|
||||
self.borderBox.setObjectName(u"borderBox")
|
||||
self.borderBox.setTristate(True)
|
||||
self.borderBox.setObjectName("borderBox")
|
||||
|
||||
self.gridLayout_2.addWidget(self.borderBox, 2, 0, 1, 1)
|
||||
self.outputSplit = QtWidgets.QCheckBox(self.optionWidget)
|
||||
self.outputSplit.setObjectName("outputSplit")
|
||||
|
||||
self.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.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.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.setObjectName("gammaWidget")
|
||||
self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.gammaWidget)
|
||||
self.horizontalLayout_2 = QHBoxLayout(self.gammaWidget)
|
||||
self.horizontalLayout_2.setObjectName(u"horizontalLayout_2")
|
||||
self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0)
|
||||
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
|
||||
self.gammaLabel = QtWidgets.QLabel(self.gammaWidget)
|
||||
self.gammaLabel.setObjectName("gammaLabel")
|
||||
self.gammaLabel = QLabel(self.gammaWidget)
|
||||
self.gammaLabel.setObjectName(u"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.setSingleStep(5)
|
||||
self.gammaSlider.setOrientation(QtCore.Qt.Horizontal)
|
||||
self.gammaSlider.setObjectName("gammaSlider")
|
||||
self.gammaSlider.setOrientation(Qt.Horizontal)
|
||||
|
||||
self.horizontalLayout_2.addWidget(self.gammaSlider)
|
||||
self.gridLayout.addWidget(self.gammaWidget, 5, 0, 1, 2)
|
||||
self.toolWidget = QtWidgets.QWidget(self.centralWidget)
|
||||
self.toolWidget.setObjectName("toolWidget")
|
||||
self.horizontalLayout = QtWidgets.QHBoxLayout(self.toolWidget)
|
||||
self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
|
||||
self.horizontalLayout.setObjectName("horizontalLayout")
|
||||
self.editorButton = QtWidgets.QPushButton(self.toolWidget)
|
||||
self.editorButton.setMinimumSize(QtCore.QSize(0, 30))
|
||||
icon1 = QtGui.QIcon()
|
||||
icon1.addPixmap(QtGui.QPixmap(":/Other/icons/editor.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||
self.editorButton.setIcon(icon1)
|
||||
self.editorButton.setObjectName("editorButton")
|
||||
self.horizontalLayout.addWidget(self.editorButton)
|
||||
self.wikiButton = QtWidgets.QPushButton(self.toolWidget)
|
||||
self.wikiButton.setMinimumSize(QtCore.QSize(0, 30))
|
||||
icon2 = QtGui.QIcon()
|
||||
icon2.addPixmap(QtGui.QPixmap(":/Other/icons/wiki.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||
self.wikiButton.setIcon(icon2)
|
||||
self.wikiButton.setObjectName("wikiButton")
|
||||
self.horizontalLayout.addWidget(self.wikiButton)
|
||||
self.gridLayout.addWidget(self.toolWidget, 0, 0, 1, 2)
|
||||
self.buttonWidget = QtWidgets.QWidget(self.centralWidget)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
|
||||
|
||||
|
||||
self.gridLayout.addWidget(self.gammaWidget, 6, 0, 1, 2)
|
||||
|
||||
self.croppingWidget = QWidget(self.centralWidget)
|
||||
self.croppingWidget.setObjectName(u"croppingWidget")
|
||||
self.croppingWidget.setVisible(False)
|
||||
self.horizontalLayout_3 = QHBoxLayout(self.croppingWidget)
|
||||
self.horizontalLayout_3.setObjectName(u"horizontalLayout_3")
|
||||
self.horizontalLayout_3.setContentsMargins(0, 0, 0, 0)
|
||||
self.croppingPowerLabel = QLabel(self.croppingWidget)
|
||||
self.croppingPowerLabel.setObjectName(u"croppingPowerLabel")
|
||||
|
||||
self.horizontalLayout_3.addWidget(self.croppingPowerLabel)
|
||||
|
||||
self.croppingPowerSlider = QSlider(self.croppingWidget)
|
||||
self.croppingPowerSlider.setObjectName(u"croppingPowerSlider")
|
||||
self.croppingPowerSlider.setMaximum(200)
|
||||
self.croppingPowerSlider.setSingleStep(1)
|
||||
self.croppingPowerSlider.setOrientation(Qt.Horizontal)
|
||||
|
||||
self.horizontalLayout_3.addWidget(self.croppingPowerSlider)
|
||||
|
||||
|
||||
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.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.buttonWidget.sizePolicy().hasHeightForWidth())
|
||||
self.buttonWidget.setSizePolicy(sizePolicy)
|
||||
self.buttonWidget.setObjectName("buttonWidget")
|
||||
self.gridLayout_4 = QtWidgets.QGridLayout(self.buttonWidget)
|
||||
self.gridLayout_4 = QGridLayout(self.buttonWidget)
|
||||
self.gridLayout_4.setObjectName(u"gridLayout_4")
|
||||
self.gridLayout_4.setContentsMargins(0, 0, 0, 0)
|
||||
self.gridLayout_4.setObjectName("gridLayout_4")
|
||||
self.directoryButton = QtWidgets.QPushButton(self.buttonWidget)
|
||||
self.directoryButton.setMinimumSize(QtCore.QSize(0, 30))
|
||||
icon3 = QtGui.QIcon()
|
||||
icon3.addPixmap(QtGui.QPixmap(":/Other/icons/folder_new.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||
self.directoryButton.setIcon(icon3)
|
||||
self.directoryButton.setObjectName("directoryButton")
|
||||
self.directoryButton = QPushButton(self.buttonWidget)
|
||||
self.directoryButton.setObjectName(u"directoryButton")
|
||||
self.directoryButton.setMinimumSize(QSize(0, 30))
|
||||
icon1 = QIcon()
|
||||
icon1.addFile(u":/Other/icons/folder_new.png", QSize(), QIcon.Normal, QIcon.Off)
|
||||
self.directoryButton.setIcon(icon1)
|
||||
|
||||
self.gridLayout_4.addWidget(self.directoryButton, 0, 0, 1, 1)
|
||||
self.fileButton = QtWidgets.QPushButton(self.buttonWidget)
|
||||
self.fileButton.setMinimumSize(QtCore.QSize(0, 30))
|
||||
icon4 = QtGui.QIcon()
|
||||
icon4.addPixmap(QtGui.QPixmap(":/Other/icons/document_new.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||
self.fileButton.setIcon(icon4)
|
||||
self.fileButton.setObjectName("fileButton")
|
||||
|
||||
self.fileButton = QPushButton(self.buttonWidget)
|
||||
self.fileButton.setObjectName(u"fileButton")
|
||||
self.fileButton.setMinimumSize(QSize(0, 30))
|
||||
icon2 = QIcon()
|
||||
icon2.addFile(u":/Other/icons/document_new.png", QSize(), QIcon.Normal, QIcon.Off)
|
||||
self.fileButton.setIcon(icon2)
|
||||
|
||||
self.gridLayout_4.addWidget(self.fileButton, 0, 3, 1, 1)
|
||||
self.deviceBox = QtWidgets.QComboBox(self.buttonWidget)
|
||||
self.deviceBox.setMinimumSize(QtCore.QSize(0, 28))
|
||||
self.deviceBox.setSizeAdjustPolicy(QtWidgets.QComboBox.AdjustToMinimumContentsLength)
|
||||
self.deviceBox.setObjectName("deviceBox")
|
||||
|
||||
self.deviceBox = QComboBox(self.buttonWidget)
|
||||
self.deviceBox.setObjectName(u"deviceBox")
|
||||
self.deviceBox.setMinimumSize(QSize(0, 28))
|
||||
|
||||
self.gridLayout_4.addWidget(self.deviceBox, 1, 0, 1, 1)
|
||||
self.formatBox = QtWidgets.QComboBox(self.buttonWidget)
|
||||
self.formatBox.setMinimumSize(QtCore.QSize(0, 28))
|
||||
self.formatBox.setSizeAdjustPolicy(QtWidgets.QComboBox.AdjustToMinimumContentsLength)
|
||||
self.formatBox.setObjectName("formatBox")
|
||||
|
||||
self.formatBox = QComboBox(self.buttonWidget)
|
||||
self.formatBox.setObjectName(u"formatBox")
|
||||
self.formatBox.setMinimumSize(QSize(0, 28))
|
||||
|
||||
self.gridLayout_4.addWidget(self.formatBox, 1, 3, 1, 1)
|
||||
self.convertButton = QtWidgets.QPushButton(self.buttonWidget)
|
||||
self.convertButton.setMinimumSize(QtCore.QSize(0, 30))
|
||||
font = QtGui.QFont()
|
||||
|
||||
self.convertButton = QPushButton(self.buttonWidget)
|
||||
self.convertButton.setObjectName(u"convertButton")
|
||||
self.convertButton.setMinimumSize(QSize(0, 30))
|
||||
font = QFont()
|
||||
font.setBold(True)
|
||||
font.setWeight(75)
|
||||
self.convertButton.setFont(font)
|
||||
icon5 = QtGui.QIcon()
|
||||
icon5.addPixmap(QtGui.QPixmap(":/Other/icons/convert.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||
self.convertButton.setIcon(icon5)
|
||||
self.convertButton.setObjectName("convertButton")
|
||||
icon3 = QIcon()
|
||||
icon3.addFile(u":/Other/icons/convert.png", QSize(), QIcon.Normal, QIcon.Off)
|
||||
self.convertButton.setIcon(icon3)
|
||||
|
||||
self.gridLayout_4.addWidget(self.convertButton, 1, 2, 1, 1)
|
||||
self.clearButton = QtWidgets.QPushButton(self.buttonWidget)
|
||||
self.clearButton.setMinimumSize(QtCore.QSize(0, 30))
|
||||
icon6 = QtGui.QIcon()
|
||||
icon6.addPixmap(QtGui.QPixmap(":/Other/icons/clear.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||
self.clearButton.setIcon(icon6)
|
||||
self.clearButton.setObjectName("clearButton")
|
||||
|
||||
self.clearButton = QPushButton(self.buttonWidget)
|
||||
self.clearButton.setObjectName(u"clearButton")
|
||||
self.clearButton.setMinimumSize(QSize(0, 30))
|
||||
icon4 = QIcon()
|
||||
icon4.addFile(u":/Other/icons/clear.png", QSize(), QIcon.Normal, QIcon.Off)
|
||||
self.clearButton.setIcon(icon4)
|
||||
|
||||
self.gridLayout_4.addWidget(self.clearButton, 0, 2, 1, 1)
|
||||
|
||||
self.directoryButton.raise_()
|
||||
self.clearButton.raise_()
|
||||
self.fileButton.raise_()
|
||||
self.deviceBox.raise_()
|
||||
self.convertButton.raise_()
|
||||
self.formatBox.raise_()
|
||||
|
||||
self.gridLayout.addWidget(self.buttonWidget, 3, 0, 1, 2)
|
||||
|
||||
self.toolWidget = 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)
|
||||
self.statusBar = QtWidgets.QStatusBar(mainWindow)
|
||||
self.statusBar = QStatusBar(mainWindow)
|
||||
self.statusBar.setObjectName(u"statusBar")
|
||||
self.statusBar.setSizeGripEnabled(False)
|
||||
self.statusBar.setObjectName("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)
|
||||
QtCore.QMetaObject.connectSlotsByName(mainWindow)
|
||||
mainWindow.setTabOrder(self.convertButton, self.clearButton)
|
||||
mainWindow.setTabOrder(self.clearButton, self.directoryButton)
|
||||
mainWindow.setTabOrder(self.directoryButton, self.fileButton)
|
||||
mainWindow.setTabOrder(self.fileButton, self.deviceBox)
|
||||
mainWindow.setTabOrder(self.deviceBox, self.formatBox)
|
||||
mainWindow.setTabOrder(self.formatBox, self.mangaBox)
|
||||
mainWindow.setTabOrder(self.mangaBox, self.rotateBox)
|
||||
mainWindow.setTabOrder(self.rotateBox, self.qualityBox)
|
||||
mainWindow.setTabOrder(self.qualityBox, self.webtoonBox)
|
||||
mainWindow.setTabOrder(self.webtoonBox, self.upscaleBox)
|
||||
mainWindow.setTabOrder(self.upscaleBox, self.gammaBox)
|
||||
mainWindow.setTabOrder(self.gammaBox, self.borderBox)
|
||||
mainWindow.setTabOrder(self.borderBox, self.outputSplit)
|
||||
mainWindow.setTabOrder(self.outputSplit, self.colorBox)
|
||||
mainWindow.setTabOrder(self.colorBox, self.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)
|
||||
|
||||
QMetaObject.connectSlotsByName(mainWindow)
|
||||
# setupUi
|
||||
|
||||
def retranslateUi(self, mainWindow):
|
||||
_translate = QtCore.QCoreApplication.translate
|
||||
mainWindow.setWindowTitle(_translate("mainWindow", "Kindle Comic Converter"))
|
||||
self.hLabel.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Resolution of the target device.</p></body></html>"))
|
||||
self.hLabel.setText(_translate("mainWindow", "Custom height:"))
|
||||
self.widthBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Resolution of the target device.</p></body></html>"))
|
||||
self.wLabel.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Resolution of the target device.</p></body></html>"))
|
||||
self.wLabel.setText(_translate("mainWindow", "Custom width:"))
|
||||
self.heightBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Resolution of the target device.</p></body></html>"))
|
||||
self.mangaBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Enable right-to-left reading.</p></body></html>"))
|
||||
self.mangaBox.setText(_translate("mainWindow", "Manga mode"))
|
||||
self.rotateBox.setToolTip(_translate("mainWindow", "<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Split<br/></span>Double page spreads will be cut into two separate pages.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - Rotate and split<br/></span>Double page spreads will be displayed twice. First rotated and then split. </p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Rotate<br/></span>Double page spreads will be rotated.</p></body></html>"))
|
||||
self.rotateBox.setText(_translate("mainWindow", "Spread splitter"))
|
||||
self.qualityBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - 4 panels<br/></span>Zoom each corner separately.</p><p style=\'white-space:pre\'><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - 2 panels<br/></span>Zoom only the top and bottom of the page.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - 4 high-quality panels<br/></span>Zoom each corner separately. Try to increase the quality of magnification. Check wiki for more details.</p></body></html>"))
|
||||
self.qualityBox.setText(_translate("mainWindow", "Panel View 4/2/HQ"))
|
||||
self.webtoonBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Enable special parsing mode for Korean Webtoons.</p></body></html>"))
|
||||
self.webtoonBox.setText(_translate("mainWindow", "Webtoon mode"))
|
||||
self.upscaleBox.setToolTip(_translate("mainWindow", "<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Nothing<br/></span>Images smaller than device resolution will not be resized.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - Stretching<br/></span>Images smaller than device resolution will be resized. Aspect ratio will be not preserved.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Upscaling<br/></span>Images smaller than device resolution will be resized. Aspect ratio will be preserved.</p></body></html>"))
|
||||
self.upscaleBox.setText(_translate("mainWindow", "Stretch/Upscale"))
|
||||
self.gammaBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Disable automatic gamma correction.</p></body></html>"))
|
||||
self.gammaBox.setText(_translate("mainWindow", "Custom gamma"))
|
||||
self.borderBox.setToolTip(_translate("mainWindow", "<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Autodetection<br/></span>The color of margins fill will be detected automatically.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - White<br/></span>Margins will be filled with white color.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Black<br/></span>Margins will be filled with black color.</p></body></html>"))
|
||||
self.borderBox.setText(_translate("mainWindow", "W/B margins"))
|
||||
self.outputSplit.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Automatic mode<br/></span>The output will be split automatically.</p><p style=\'white-space:pre\'><span style=\" font-weight:600; text-decoration: underline;\">Checked - Volume mode<br/></span>Every subdirectory will be considered as a separate volume.</p></body></html>"))
|
||||
self.outputSplit.setText(_translate("mainWindow", "Output split"))
|
||||
self.colorBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Disable conversion to grayscale.</p></body></html>"))
|
||||
self.colorBox.setText(_translate("mainWindow", "Color mode"))
|
||||
self.gammaLabel.setText(_translate("mainWindow", "Gamma: Auto"))
|
||||
self.editorButton.setText(_translate("mainWindow", "Editor"))
|
||||
self.wikiButton.setText(_translate("mainWindow", "Wiki"))
|
||||
self.directoryButton.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Add directory containing JPG, PNG or GIF files to queue.<br/><span style=\" font-weight:600;\">CBR, CBZ and CB7 files inside will not be processed!</span></p></body></html>"))
|
||||
self.directoryButton.setText(_translate("mainWindow", "Add directory"))
|
||||
self.fileButton.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Add CBR, CBZ, CB7 or PDF file to queue.</p></body></html>"))
|
||||
self.fileButton.setText(_translate("mainWindow", "Add file"))
|
||||
self.deviceBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Target device.</p></body></html>"))
|
||||
self.formatBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Output format.</p></body></html>"))
|
||||
self.convertButton.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Shift+Click to select the output directory.</p></body></html>"))
|
||||
self.convertButton.setText(_translate("mainWindow", "Convert"))
|
||||
self.clearButton.setText(_translate("mainWindow", "Clear list"))
|
||||
mainWindow.setWindowTitle(QCoreApplication.translate("mainWindow", u"Kindle Comic Converter", None))
|
||||
#if QT_CONFIG(tooltip)
|
||||
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))
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
self.qualityBox.setText(QCoreApplication.translate("mainWindow", u"Panel View 4/2/HQ", None))
|
||||
#if QT_CONFIG(tooltip)
|
||||
self.deleteBox.setToolTip(QCoreApplication.translate("mainWindow", u"Delete input file(s) or directory. It's not recoverable!", None))
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
self.deleteBox.setText(QCoreApplication.translate("mainWindow", u"Delete input", None))
|
||||
#if QT_CONFIG(tooltip)
|
||||
self.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))
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
self.maximizeStrips.setText(QCoreApplication.translate("mainWindow", u"1x4 to 2x2 strips", None))
|
||||
#if QT_CONFIG(tooltip)
|
||||
self.gammaBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Disable automatic gamma correction.</p></body></html>", None))
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
self.gammaBox.setText(QCoreApplication.translate("mainWindow", u"Custom gamma", None))
|
||||
#if QT_CONFIG(tooltip)
|
||||
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))
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
self.borderBox.setText(QCoreApplication.translate("mainWindow", u"W/B margins", None))
|
||||
#if QT_CONFIG(tooltip)
|
||||
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))
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
self.webtoonBox.setText(QCoreApplication.translate("mainWindow", u"Webtoon mode", None))
|
||||
#if QT_CONFIG(tooltip)
|
||||
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))
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
self.upscaleBox.setText(QCoreApplication.translate("mainWindow", u"Stretch/Upscale", None))
|
||||
#if QT_CONFIG(tooltip)
|
||||
self.mangaBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Enable right-to-left reading.</p></body></html>", None))
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
self.mangaBox.setText(QCoreApplication.translate("mainWindow", u"Manga mode", None))
|
||||
#if QT_CONFIG(tooltip)
|
||||
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))
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
self.rotateBox.setText(QCoreApplication.translate("mainWindow", u"Spread splitter", None))
|
||||
#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 -*-
|
||||
|
||||
# Form implementation generated from reading ui file 'gui\MetaEditor.ui'
|
||||
#
|
||||
# Created by: PyQt5 UI code generator 5.6
|
||||
#
|
||||
# WARNING! All changes made in this file will be lost!
|
||||
################################################################################
|
||||
## Form generated from reading UI file 'MetaEditor.ui'
|
||||
##
|
||||
## Created by: Qt User Interface Compiler version 6.5.2
|
||||
##
|
||||
## 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):
|
||||
def setupUi(self, editorDialog):
|
||||
editorDialog.setObjectName("editorDialog")
|
||||
if not editorDialog.objectName():
|
||||
editorDialog.setObjectName(u"editorDialog")
|
||||
editorDialog.resize(400, 260)
|
||||
editorDialog.setMinimumSize(QtCore.QSize(400, 260))
|
||||
icon = QtGui.QIcon()
|
||||
icon.addPixmap(QtGui.QPixmap(":/Icon/icons/comic2ebook.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||
editorDialog.setMinimumSize(QSize(400, 260))
|
||||
icon = QIcon()
|
||||
icon.addFile(u":/Icon/icons/comic2ebook.png", QSize(), QIcon.Normal, QIcon.Off)
|
||||
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.setObjectName("verticalLayout")
|
||||
self.editorWidget = QtWidgets.QWidget(editorDialog)
|
||||
self.editorWidget.setObjectName("editorWidget")
|
||||
self.gridLayout = QtWidgets.QGridLayout(self.editorWidget)
|
||||
self.editorWidget = QWidget(editorDialog)
|
||||
self.editorWidget.setObjectName(u"editorWidget")
|
||||
self.gridLayout = QGridLayout(self.editorWidget)
|
||||
self.gridLayout.setObjectName(u"gridLayout")
|
||||
self.gridLayout.setContentsMargins(0, 0, 0, 0)
|
||||
self.gridLayout.setObjectName("gridLayout")
|
||||
self.label_1 = QtWidgets.QLabel(self.editorWidget)
|
||||
self.label_1.setObjectName("label_1")
|
||||
self.label_1 = QLabel(self.editorWidget)
|
||||
self.label_1.setObjectName(u"label_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.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.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.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.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.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.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.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.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.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.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.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.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.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.optionWidget = QtWidgets.QWidget(editorDialog)
|
||||
self.optionWidget.setObjectName("optionWidget")
|
||||
self.horizontalLayout = QtWidgets.QHBoxLayout(self.optionWidget)
|
||||
|
||||
self.optionWidget = QWidget(editorDialog)
|
||||
self.optionWidget.setObjectName(u"optionWidget")
|
||||
self.horizontalLayout = QHBoxLayout(self.optionWidget)
|
||||
self.horizontalLayout.setObjectName(u"horizontalLayout")
|
||||
self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
|
||||
self.horizontalLayout.setObjectName("horizontalLayout")
|
||||
self.statusLabel = QtWidgets.QLabel(self.optionWidget)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding)
|
||||
self.statusLabel = QLabel(self.optionWidget)
|
||||
self.statusLabel.setObjectName(u"statusLabel")
|
||||
sizePolicy = QSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.statusLabel.sizePolicy().hasHeightForWidth())
|
||||
self.statusLabel.setSizePolicy(sizePolicy)
|
||||
self.statusLabel.setText("")
|
||||
self.statusLabel.setObjectName("statusLabel")
|
||||
|
||||
self.horizontalLayout.addWidget(self.statusLabel)
|
||||
self.okButton = QtWidgets.QPushButton(self.optionWidget)
|
||||
self.okButton.setMinimumSize(QtCore.QSize(0, 30))
|
||||
icon1 = QtGui.QIcon()
|
||||
icon1.addPixmap(QtGui.QPixmap(":/Other/icons/convert.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||
|
||||
self.okButton = QPushButton(self.optionWidget)
|
||||
self.okButton.setObjectName(u"okButton")
|
||||
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.setObjectName("okButton")
|
||||
|
||||
self.horizontalLayout.addWidget(self.okButton)
|
||||
self.cancelButton = QtWidgets.QPushButton(self.optionWidget)
|
||||
self.cancelButton.setMinimumSize(QtCore.QSize(0, 30))
|
||||
icon2 = QtGui.QIcon()
|
||||
icon2.addPixmap(QtGui.QPixmap(":/Other/icons/clear.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||
|
||||
self.cancelButton = QPushButton(self.optionWidget)
|
||||
self.cancelButton.setObjectName(u"cancelButton")
|
||||
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.setObjectName("cancelButton")
|
||||
|
||||
self.horizontalLayout.addWidget(self.cancelButton)
|
||||
|
||||
|
||||
self.verticalLayout.addWidget(self.optionWidget)
|
||||
|
||||
|
||||
self.retranslateUi(editorDialog)
|
||||
QtCore.QMetaObject.connectSlotsByName(editorDialog)
|
||||
|
||||
QMetaObject.connectSlotsByName(editorDialog)
|
||||
# setupUi
|
||||
|
||||
def retranslateUi(self, editorDialog):
|
||||
_translate = QtCore.QCoreApplication.translate
|
||||
editorDialog.setWindowTitle(_translate("editorDialog", "Metadata editor"))
|
||||
self.label_1.setText(_translate("editorDialog", "Series:"))
|
||||
self.label_2.setText(_translate("editorDialog", "Volume:"))
|
||||
self.label_3.setText(_translate("editorDialog", "Number:"))
|
||||
self.label_4.setText(_translate("editorDialog", "Writer:"))
|
||||
self.label_5.setText(_translate("editorDialog", "Penciller:"))
|
||||
self.label_6.setText(_translate("editorDialog", "Inker:"))
|
||||
self.label_7.setText(_translate("editorDialog", "Colorist:"))
|
||||
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(_translate("editorDialog", "Save"))
|
||||
self.cancelButton.setText(_translate("editorDialog", "Cancel"))
|
||||
editorDialog.setWindowTitle(QCoreApplication.translate("editorDialog", u"Metadata editor", None))
|
||||
self.label_1.setText(QCoreApplication.translate("editorDialog", u"Series:", None))
|
||||
self.label_2.setText(QCoreApplication.translate("editorDialog", u"Volume:", None))
|
||||
self.label_3.setText(QCoreApplication.translate("editorDialog", u"Number:", None))
|
||||
self.label_4.setText(QCoreApplication.translate("editorDialog", u"Writer:", None))
|
||||
self.label_5.setText(QCoreApplication.translate("editorDialog", u"Penciller:", None))
|
||||
self.label_6.setText(QCoreApplication.translate("editorDialog", u"Inker:", None))
|
||||
self.label_7.setText(QCoreApplication.translate("editorDialog", u"Colorist:", None))
|
||||
self.statusLabel.setText("")
|
||||
self.okButton.setText(QCoreApplication.translate("editorDialog", u"Save", None))
|
||||
self.cancelButton.setText(QCoreApplication.translate("editorDialog", u"Cancel", None))
|
||||
# retranslateUi
|
||||
|
||||
from . import KCC_rc
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
__version__ = '5.4.1'
|
||||
__version__ = '6.1.0'
|
||||
__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'
|
||||
|
||||
@@ -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 -*-
|
||||
#
|
||||
# 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
|
||||
# any purpose with or without fee is hereby granted, provided that the
|
||||
@@ -20,15 +20,11 @@
|
||||
|
||||
import os
|
||||
import sys
|
||||
from argparse import ArgumentParser
|
||||
from shutil import rmtree, copytree, move
|
||||
from optparse import OptionParser, OptionGroup
|
||||
from multiprocessing import Pool
|
||||
from PIL import Image, ImageChops, ImageOps, ImageDraw
|
||||
from .shared import getImageFileName, walkLevel, walkSort, sanitizeTrace
|
||||
try:
|
||||
from PyQt5 import QtCore
|
||||
except ImportError:
|
||||
QtCore = None
|
||||
|
||||
|
||||
def mergeDirectoryTick(output):
|
||||
@@ -57,9 +53,8 @@ def mergeDirectory(work):
|
||||
if len(images) > 0:
|
||||
targetWidth = max(set(sizes), key=sizes.count)
|
||||
for i in images:
|
||||
if i[1] <= targetWidth:
|
||||
targetHeight += i[2]
|
||||
imagesValid.append(i[0])
|
||||
targetHeight += i[2]
|
||||
imagesValid.append(i[0])
|
||||
# Silently drop directories that contain too many images
|
||||
# 131072 = GIMP_MAX_IMAGE_SIZE / 4
|
||||
if targetHeight > 131072:
|
||||
@@ -68,8 +63,10 @@ def mergeDirectory(work):
|
||||
y = 0
|
||||
for i in imagesValid:
|
||||
img = Image.open(i).convert('RGB')
|
||||
if img.size[0] < targetWidth:
|
||||
img = ImageOps.fit(img, (targetWidth, img.size[1]), method=Image.BICUBIC, centering=(0.5, 0.5))
|
||||
if img.size[0] < targetWidth or img.size[0] > targetWidth:
|
||||
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))
|
||||
y += img.size[1]
|
||||
os.remove(i)
|
||||
@@ -93,12 +90,15 @@ def splitImageTick(output):
|
||||
splitWorkerPool.terminate()
|
||||
|
||||
|
||||
# noinspection PyUnboundLocalVariable
|
||||
def splitImage(work):
|
||||
try:
|
||||
path = work[0]
|
||||
name = work[1]
|
||||
opt = work[2]
|
||||
filePath = os.path.join(path, name)
|
||||
Image.warnings.simplefilter('error', Image.DecompressionBombWarning)
|
||||
Image.MAX_IMAGE_PIXELS = 1000000000
|
||||
imgOrg = Image.open(filePath).convert('RGB')
|
||||
imgProcess = Image.open(filePath).convert('1')
|
||||
widthImg, heightImg = imgOrg.size
|
||||
@@ -112,11 +112,16 @@ def splitImage(work):
|
||||
panelDetected = False
|
||||
panels = []
|
||||
while yWork < heightImg:
|
||||
tmpImg = imgProcess.crop([0, yWork, widthImg, yWork + 4])
|
||||
tmpImg = imgProcess.crop((4, yWork, widthImg-4, yWork + 4))
|
||||
solid = detectSolid(tmpImg)
|
||||
if not solid and not panelDetected:
|
||||
panelDetected = True
|
||||
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:
|
||||
panelDetected = False
|
||||
panelY2 = yWork + 6
|
||||
@@ -140,9 +145,7 @@ def splitImage(work):
|
||||
|
||||
if opt.debug:
|
||||
for panel in panelsProcessed:
|
||||
# noinspection PyUnboundLocalVariable
|
||||
draw.rectangle([(0, panel[0]), (widthImg, panel[1])], (0, 255, 0, 128), (0, 0, 255, 255))
|
||||
# noinspection PyUnboundLocalVariable
|
||||
draw.rectangle(((0, panel[0]), (widthImg, panel[1])), (0, 255, 0, 128), (0, 0, 255, 255))
|
||||
debugImage = Image.alpha_composite(imgOrg.convert(mode='RGBA'), drawImg)
|
||||
debugImage.save(os.path.join(path, os.path.splitext(name)[0] + '-debug.png'), 'PNG')
|
||||
|
||||
@@ -175,7 +178,7 @@ def splitImage(work):
|
||||
if pageHeight > 15:
|
||||
newPage = Image.new('RGB', (widthImg, pageHeight))
|
||||
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))
|
||||
targetHeight += panelsProcessed[panel][2]
|
||||
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])
|
||||
|
||||
|
||||
def main(argv=None, qtGUI=None):
|
||||
global options, GUI, splitWorkerPool, splitWorkerOutput, mergeWorkerPool, mergeWorkerOutput
|
||||
parser = OptionParser(usage="Usage: kcc-c2p [options] comic_folder", add_help_option=False)
|
||||
mainOptions = OptionGroup(parser, "MANDATORY")
|
||||
otherOptions = OptionGroup(parser, "OTHER")
|
||||
mainOptions.add_option("-y", "--height", type="int", dest="height", default=0,
|
||||
help="Height of the target device screen")
|
||||
mainOptions.add_option("-i", "--in-place", action="store_true", dest="inPlace", default=False,
|
||||
help="Overwrite source directory")
|
||||
mainOptions.add_option("-m", "--merge", action="store_true", dest="merge", default=False,
|
||||
help="Combine every directory into a single image before splitting")
|
||||
otherOptions.add_option("-d", "--debug", action="store_true", dest="debug", default=False,
|
||||
help="Create debug file for every split image")
|
||||
otherOptions.add_option("-h", "--help", action="help",
|
||||
help="Show this help message and exit")
|
||||
parser.add_option_group(mainOptions)
|
||||
parser.add_option_group(otherOptions)
|
||||
options, args = parser.parse_args(argv)
|
||||
if qtGUI:
|
||||
GUI = qtGUI
|
||||
def main(argv=None, qtgui=None):
|
||||
global args, GUI, splitWorkerPool, splitWorkerOutput, mergeWorkerPool, mergeWorkerOutput
|
||||
parser = ArgumentParser(prog="kcc-c2p", usage="kcc-c2p [options] [input]", add_help=False)
|
||||
|
||||
mandatory_options = parser.add_argument_group("MANDATORY")
|
||||
main_options = parser.add_argument_group("MAIN")
|
||||
other_options = parser.add_argument_group("OTHER")
|
||||
mandatory_options.add_argument("input", action="extend", nargs="*", default=None,
|
||||
help="Full path to comic folder(s) to be processed. Separate multiple inputs"
|
||||
" with spaces.")
|
||||
main_options.add_argument("-y", "--height", type=int, dest="height", default=0,
|
||||
help="Height of the target device screen")
|
||||
main_options.add_argument("-i", "--in-place", action="store_true", dest="inPlace", default=False,
|
||||
help="Overwrite source directory")
|
||||
main_options.add_argument("-m", "--merge", action="store_true", dest="merge", default=False,
|
||||
help="Combine every directory into a single image before splitting")
|
||||
other_options.add_argument("-d", "--debug", action="store_true", dest="debug", default=False,
|
||||
help="Create debug file for every split image")
|
||||
other_options.add_argument("-h", "--help", action="help",
|
||||
help="Show this help message and exit")
|
||||
args = parser.parse_args(argv)
|
||||
if qtgui:
|
||||
GUI = qtgui
|
||||
else:
|
||||
GUI = None
|
||||
if len(args) != 1:
|
||||
if not argv or args.input == []:
|
||||
parser.print_help()
|
||||
return 1
|
||||
if options.height > 0:
|
||||
options.sourceDir = args[0]
|
||||
options.targetDir = args[0] + "-Splitted"
|
||||
if os.path.isdir(options.sourceDir):
|
||||
rmtree(options.targetDir, True)
|
||||
copytree(options.sourceDir, options.targetDir)
|
||||
work = []
|
||||
pagenumber = 1
|
||||
splitWorkerOutput = []
|
||||
splitWorkerPool = Pool(maxtasksperchild=10)
|
||||
if options.merge:
|
||||
print("Merging images...")
|
||||
directoryNumer = 1
|
||||
mergeWork = []
|
||||
mergeWorkerOutput = []
|
||||
mergeWorkerPool = Pool(maxtasksperchild=10)
|
||||
mergeWork.append([options.targetDir])
|
||||
for root, dirs, files in os.walk(options.targetDir, False):
|
||||
dirs, files = walkSort(dirs, files)
|
||||
for directory in dirs:
|
||||
directoryNumer += 1
|
||||
mergeWork.append([os.path.join(root, directory)])
|
||||
if args.height > 0:
|
||||
for sourceDir in args.input:
|
||||
targetDir = sourceDir + "-Splitted"
|
||||
if os.path.isdir(sourceDir):
|
||||
rmtree(targetDir, True)
|
||||
copytree(sourceDir, targetDir)
|
||||
work = []
|
||||
pagenumber = 1
|
||||
splitWorkerOutput = []
|
||||
splitWorkerPool = Pool(maxtasksperchild=10)
|
||||
if args.merge:
|
||||
print("Merging images...")
|
||||
directoryNumer = 1
|
||||
mergeWork = []
|
||||
mergeWorkerOutput = []
|
||||
mergeWorkerPool = Pool(maxtasksperchild=10)
|
||||
mergeWork.append([targetDir])
|
||||
for root, dirs, files in os.walk(targetDir, False):
|
||||
dirs, files = walkSort(dirs, files)
|
||||
for directory in dirs:
|
||||
directoryNumer += 1
|
||||
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:
|
||||
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(options.targetDir, True)
|
||||
raise UserWarning("Conversion interrupted.")
|
||||
if len(mergeWorkerOutput) > 0:
|
||||
rmtree(options.targetDir, True)
|
||||
raise RuntimeError("One of workers crashed. Cause: " + mergeWorkerOutput[0][0],
|
||||
mergeWorkerOutput[0][1])
|
||||
print("Splitting images...")
|
||||
for root, _, files in os.walk(options.targetDir, False):
|
||||
for name in files:
|
||||
if getImageFileName(name) is not None:
|
||||
pagenumber += 1
|
||||
work.append([root, name, options])
|
||||
else:
|
||||
os.remove(os.path.join(root, name))
|
||||
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)
|
||||
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(targetDir, True)
|
||||
raise UserWarning("Conversion interrupted.")
|
||||
if len(splitWorkerOutput) > 0:
|
||||
rmtree(targetDir, True)
|
||||
raise RuntimeError("One of workers crashed. Cause: " + splitWorkerOutput[0][0],
|
||||
splitWorkerOutput[0][1])
|
||||
if args.inPlace:
|
||||
rmtree(sourceDir)
|
||||
move(targetDir, sourceDir)
|
||||
else:
|
||||
rmtree(targetDir, True)
|
||||
raise UserWarning("Source directory is empty.")
|
||||
else:
|
||||
rmtree(options.targetDir, True)
|
||||
raise UserWarning("Source directory is empty.")
|
||||
else:
|
||||
raise UserWarning("Provided path is not a directory.")
|
||||
raise UserWarning("Provided input is not a directory.")
|
||||
else:
|
||||
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 -*-
|
||||
#
|
||||
# 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
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
@@ -24,6 +24,7 @@ import shutil
|
||||
class DualMetaFixException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
# palm database offset constants
|
||||
number_of_pdb_records = 76
|
||||
first_pdb_record = 78
|
||||
@@ -142,14 +143,12 @@ class DualMobiMetaFix:
|
||||
self.datain_rec0 = readsection(self.datain, 0)
|
||||
|
||||
# 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 = del_exth(rec0, 501)
|
||||
rec0 = del_exth(rec0, 113)
|
||||
rec0 = del_exth(rec0, 504)
|
||||
rec0 = add_exth(rec0, 501, b'EBOK')
|
||||
rec0 = add_exth(rec0, 113, asin)
|
||||
rec0 = add_exth(rec0, 504, asin)
|
||||
replacesection(self.datain, 0, rec0)
|
||||
|
||||
ver = getint(self.datain_rec0, mobi_version)
|
||||
@@ -171,14 +170,12 @@ class DualMobiMetaFix:
|
||||
self.datain_kfrec0 = readsection(self.datain, datain_kf8)
|
||||
|
||||
# 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 = del_exth(rec0, 501)
|
||||
rec0 = del_exth(rec0, 113)
|
||||
rec0 = del_exth(rec0, 504)
|
||||
rec0 = add_exth(rec0, 501, b'EBOK')
|
||||
rec0 = add_exth(rec0, 113, asin)
|
||||
rec0 = add_exth(rec0, 504, asin)
|
||||
replacesection(self.datain, datain_kf8, rec0)
|
||||
|
||||
self.datain.flush()
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2010 Alex Yatskov
|
||||
# Copyright (C) 2011 Stanislav (proDOOMman) Kosolapov <prodoomman@gmail.com>
|
||||
# Copyright (c) 2016 Alberto Planas <aplanas@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
|
||||
# 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
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import io
|
||||
import os
|
||||
from io import BytesIO
|
||||
from urllib.request import Request, urlopen
|
||||
from urllib.parse import quote
|
||||
import mozjpeg_lossless_optimization
|
||||
from PIL import Image, ImageOps, ImageStat, ImageChops, ImageFilter
|
||||
from .shared import md5Checksum
|
||||
from . import __version__
|
||||
|
||||
AUTO_CROP_THRESHOLD = 0.015
|
||||
|
||||
|
||||
class ProfileData:
|
||||
@@ -79,12 +80,16 @@ class ProfileData:
|
||||
|
||||
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/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),
|
||||
'KoG': ("Kobo Glo", (768, 1024), 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),
|
||||
'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),
|
||||
}
|
||||
|
||||
|
||||
class ComicPageParser:
|
||||
def __init__(self, source, options):
|
||||
Image.MAX_IMAGE_PIXELS = int(2048 * 2048 * 2048 // 4 // 3)
|
||||
self.opt = options
|
||||
self.source = source
|
||||
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.color = self.colorCheck()
|
||||
self.fill = self.fillCheck()
|
||||
# backwards compatibility for Pillow >9.1.0
|
||||
if not hasattr(Image, 'Resampling'):
|
||||
Image.Resampling = Image
|
||||
self.splitCheck()
|
||||
|
||||
def getImageHistogram(self, image):
|
||||
@@ -119,9 +136,22 @@ class ComicPageParser:
|
||||
def splitCheck(self):
|
||||
width, height = self.image.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:
|
||||
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:
|
||||
if self.opt.splitter != 1:
|
||||
if width > height:
|
||||
@@ -139,7 +169,7 @@ class ComicPageParser:
|
||||
self.payload.append(['S1', self.source, pageone, self.color, self.fill])
|
||||
self.payload.append(['S2', self.source, pagetwo, self.color, self.fill])
|
||||
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])
|
||||
else:
|
||||
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
|
||||
if self.opt.hq:
|
||||
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.color = color
|
||||
self.fill = fill
|
||||
@@ -225,6 +256,9 @@ class ComicPage:
|
||||
self.targetPath = os.path.join(path[0], os.path.splitext(path[1])[0]) + '-KCC-B'
|
||||
elif 'S2' in mode:
|
||||
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):
|
||||
try:
|
||||
@@ -236,11 +270,20 @@ class ComicPage:
|
||||
if self.fill != 'white':
|
||||
flags.append('BlackBackground')
|
||||
if self.opt.forcepng:
|
||||
self.image.info["transparency"] = None
|
||||
self.targetPath += '.png'
|
||||
self.image.save(self.targetPath, 'PNG', optimize=1)
|
||||
else:
|
||||
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]
|
||||
except IOError as err:
|
||||
raise RuntimeError('Cannot save image. ' + str(err))
|
||||
@@ -254,7 +297,7 @@ class ComicPage:
|
||||
if gamma == 1.0:
|
||||
self.image = ImageOps.autocontrast(self.image)
|
||||
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):
|
||||
colors = len(self.palette) // 3
|
||||
@@ -268,89 +311,93 @@ class ComicPage:
|
||||
self.image = self.image.quantize(palette=palImg)
|
||||
|
||||
def resizeImage(self):
|
||||
if self.image.size[0] <= self.size[0] and self.image.size[1] <= self.size[1]:
|
||||
method = Image.BICUBIC
|
||||
else:
|
||||
method = Image.LANCZOS
|
||||
# kindle scribe conversion to mobi is limited in resolution by kindlegen, same with send to kindle and epub
|
||||
if self.kindle_scribe_azw3:
|
||||
self.size = (1440, 1920)
|
||||
ratio_device = float(self.size[1]) / float(self.size[0])
|
||||
ratio_image = float(self.image.size[1]) / float(self.image.size[0])
|
||||
method = self.resize_method()
|
||||
if self.opt.stretch:
|
||||
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:
|
||||
if self.opt.format == 'CBZ':
|
||||
elif method == Image.Resampling.BICUBIC and not self.opt.upscale:
|
||||
if self.opt.format == 'CBZ' or self.opt.kfx:
|
||||
borderw = int((self.size[0] - self.image.size[0]) / 2)
|
||||
borderh = int((self.size[1] - self.image.size[1]) / 2)
|
||||
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]:
|
||||
self.image = ImageOps.fit(self.image, self.size, method=Image.BICUBIC, centering=(0.5, 0.5))
|
||||
else:
|
||||
if self.opt.format == 'CBZ':
|
||||
ratioDev = float(self.size[0]) / float(self.size[1])
|
||||
if (float(self.image.size[0]) / float(self.image.size[1])) < ratioDev:
|
||||
diff = int(self.image.size[1] * ratioDev) - self.image.size[0]
|
||||
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))
|
||||
self.image = ImageOps.fit(self.image, self.size, method=method)
|
||||
else: # if image bigger than device resolution or smaller with upscaling
|
||||
if abs(ratio_image - ratio_device) < AUTO_CROP_THRESHOLD:
|
||||
self.image = ImageOps.fit(self.image, self.size, method=method)
|
||||
elif self.opt.format == 'CBZ' or self.opt.kfx:
|
||||
self.image = ImageOps.pad(self.image, self.size, method=method, color=self.fill)
|
||||
else:
|
||||
hpercent = self.size[1] / float(self.image.size[1])
|
||||
wsize = int((float(self.image.size[0]) * float(hpercent)))
|
||||
self.image = self.image.resize((wsize, self.size[1]), method)
|
||||
if self.image.size[0] > self.size[0] or self.image.size[1] > self.size[1]:
|
||||
self.image.thumbnail(self.size, Image.LANCZOS)
|
||||
if self.kindle_scribe_azw3:
|
||||
self.size = (1860, 1920)
|
||||
self.image = ImageOps.contain(self.image, self.size, method=method)
|
||||
|
||||
def getBoundingBox(self, tmpImg):
|
||||
min_margin = [int(0.005 * i + 0.5) for i in tmpImg.size]
|
||||
max_margin = [int(0.1 * i + 0.5) for i in tmpImg.size]
|
||||
bbox = tmpImg.getbbox()
|
||||
def resize_method(self):
|
||||
if self.image.size[0] <= self.size[0] and self.image.size[1] <= self.size[1]:
|
||||
return Image.Resampling.BICUBIC
|
||||
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 = (
|
||||
max(0, min(max_margin[0], bbox[0] - min_margin[0])),
|
||||
max(0, min(max_margin[1], bbox[1] - min_margin[1])),
|
||||
min(tmpImg.size[0],
|
||||
max(tmpImg.size[0] - max_margin[0], bbox[2] + min_margin[0])),
|
||||
min(tmpImg.size[1],
|
||||
max(tmpImg.size[1] - max_margin[1], bbox[3] + min_margin[1])),
|
||||
min(tmptmg.size[0],
|
||||
max(tmptmg.size[0] - max_margin[0], bbox[2] + min_margin[0])),
|
||||
min(tmptmg.size[1],
|
||||
max(tmptmg.size[1] - max_margin[1], bbox[3] + min_margin[1])),
|
||||
)
|
||||
return bbox
|
||||
|
||||
def cropPageNumber(self, power):
|
||||
if self.fill != 'white':
|
||||
tmpImg = self.image.convert(mode='L')
|
||||
else:
|
||||
tmpImg = ImageOps.invert(self.image.convert(mode='L'))
|
||||
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 maybeCrop(self, box, minimum):
|
||||
box_area = (box[2] - box[0]) * (box[3] - box[1])
|
||||
image_area = self.image.size[0] * self.image.size[1]
|
||||
if (box_area / image_area) >= minimum:
|
||||
self.image = self.image.crop(box)
|
||||
|
||||
def cropMargin(self, power):
|
||||
def cropPageNumber(self, power, minimum):
|
||||
if self.fill != 'white':
|
||||
tmpImg = self.image.convert(mode='L')
|
||||
tmptmg = self.image.convert(mode='L')
|
||||
else:
|
||||
tmpImg = ImageOps.invert(self.image.convert(mode='L'))
|
||||
tmpImg = tmpImg.filter(ImageFilter.GaussianBlur(radius=3))
|
||||
tmpImg = tmpImg.point(lambda x: (x >= 16 * power) and x)
|
||||
self.image = self.image.crop(self.getBoundingBox(tmpImg)) if tmpImg.getbbox() else self.image
|
||||
tmptmg = ImageOps.invert(self.image.convert(mode='L'))
|
||||
tmptmg = tmptmg.point(lambda x: x and 255)
|
||||
tmptmg = tmptmg.filter(ImageFilter.MinFilter(size=3))
|
||||
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:
|
||||
def __init__(self, source, target, opt, tomeNumber):
|
||||
def __init__(self, source, target, opt, tomeid):
|
||||
self.options = opt
|
||||
self.source = source
|
||||
self.target = target
|
||||
if tomeNumber == 0:
|
||||
self.tomeNumber = 1
|
||||
if tomeid == 0:
|
||||
self.tomeid = 1
|
||||
else:
|
||||
self.tomeNumber = tomeNumber
|
||||
if self.tomeNumber in self.options.remoteCovers:
|
||||
try:
|
||||
source = urlopen(Request(quote(self.options.remoteCovers[self.tomeNumber]).replace('%3A', ':', 1),
|
||||
headers={'User-Agent': 'KindleComicConverter/' + __version__})).read()
|
||||
self.image = Image.open(BytesIO(source))
|
||||
except Exception:
|
||||
self.image = Image.open(source)
|
||||
else:
|
||||
self.image = Image.open(source)
|
||||
self.tomeid = tomeid
|
||||
self.image = Image.open(source)
|
||||
# backwards compatibility for Pillow >9.1.0
|
||||
if not hasattr(Image, 'Resampling'):
|
||||
Image.Resampling = Image
|
||||
self.process()
|
||||
|
||||
def process(self):
|
||||
@@ -358,17 +405,17 @@ class Cover:
|
||||
self.image = ImageOps.autocontrast(self.image)
|
||||
if not self.options.forcecolor:
|
||||
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()
|
||||
|
||||
def save(self):
|
||||
try:
|
||||
self.image.save(self.target, "JPEG", optimize=1, quality=85)
|
||||
except IOError:
|
||||
raise RuntimeError('Failed to process downloaded cover.')
|
||||
raise RuntimeError('Failed to save cover.')
|
||||
|
||||
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:
|
||||
self.image.save(os.path.join(kindle.path.split('documents')[0], 'system', 'thumbnails',
|
||||
'thumbnail_' + asin + '_EBOK_portrait.jpg'), 'JPEG', optimize=1, quality=85)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# -*- 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
|
||||
# any purpose with or without fee is hereby granted, provided that the
|
||||
@@ -31,8 +31,7 @@ class Kindle:
|
||||
def findDevice(self):
|
||||
for drive in reversed(psutil.disk_partitions(False)):
|
||||
if (drive[2] == 'FAT32' and drive[3] == 'rw,removable') or \
|
||||
(drive[2] == 'vfat' and 'rw' in drive[3]) or \
|
||||
(drive[2] == 'msdos' and 'rw' in drive[3]):
|
||||
(drive[2] in ('vfat', 'msdos', 'FAT') and 'rw' in drive[3]):
|
||||
if os.path.isdir(os.path.join(drive[1], 'system')) and \
|
||||
os.path.isdir(os.path.join(drive[1], 'documents')):
|
||||
return drive[1]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# -*- 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
|
||||
# any purpose with or without fee is hereby granted, provided that the
|
||||
@@ -18,14 +18,9 @@
|
||||
|
||||
import os
|
||||
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 shutil import rmtree
|
||||
from .shared import removeFromZIP, check7ZFile as is_7zfile
|
||||
from . import rarfile
|
||||
from . import comicarchive
|
||||
|
||||
|
||||
class MetadataParser:
|
||||
@@ -39,50 +34,21 @@ class MetadataParser:
|
||||
'Inkers': [],
|
||||
'Colorists': [],
|
||||
'Summary': '',
|
||||
'MUid': '',
|
||||
'Bookmarks': []}
|
||||
'Bookmarks': [],
|
||||
'Title': ''}
|
||||
self.rawdata = None
|
||||
self.compressor = None
|
||||
if self.source.endswith('.xml'):
|
||||
self.format = None
|
||||
if self.source.endswith('.xml') and os.path.exists(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()
|
||||
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):
|
||||
if len(self.rawdata.getElementsByTagName('Series')) != 0:
|
||||
@@ -93,17 +59,14 @@ class MetadataParser:
|
||||
self.data['Number'] = self.rawdata.getElementsByTagName('Number')[0].firstChild.nodeValue
|
||||
if len(self.rawdata.getElementsByTagName('Summary')) != 0:
|
||||
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']:
|
||||
if len(self.rawdata.getElementsByTagName(field)) != 0:
|
||||
for person in self.rawdata.getElementsByTagName(field)[0].firstChild.nodeValue.split(', '):
|
||||
self.data[field + 's'].append(person)
|
||||
self.data[field + 's'] = list(set(self.data[field + 's']))
|
||||
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:
|
||||
for page in self.rawdata.getElementsByTagName('Page'):
|
||||
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'])],
|
||||
['Penciller', ', '.join(self.data['Pencillers'])], ['Inker', ', '.join(self.data['Inkers'])],
|
||||
['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]):
|
||||
node = self.rawdata.getElementsByTagName(row[0])[0]
|
||||
if row[1]:
|
||||
@@ -139,7 +102,7 @@ class MetadataParser:
|
||||
['Number', self.data['Number']], ['Writer', ', '.join(self.data['Writers'])],
|
||||
['Penciller', ', '.join(self.data['Pencillers'])], ['Inker', ', '.join(self.data['Inkers'])],
|
||||
['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]:
|
||||
main = doc.createElement(row[0])
|
||||
root.appendChild(main)
|
||||
@@ -154,20 +117,9 @@ class MetadataParser:
|
||||
tmpXML = os.path.join(workdir, 'ComicInfo.xml')
|
||||
with open(tmpXML, 'w', encoding='utf-8') as f:
|
||||
self.rawdata.writexml(f, encoding='utf-8')
|
||||
if is_zipfile(self.source):
|
||||
removeFromZIP(self.source, 'ComicInfo.xml')
|
||||
with ZipFile(self.source, mode='a', compression=ZIP_DEFLATED) as zip_file:
|
||||
zip_file.write(tmpXML, arcname=tmpXML.split(os.sep)[-1])
|
||||
elif rarfile.is_rarfile(self.source):
|
||||
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.')
|
||||
try:
|
||||
cbx = comicarchive.ComicArchive(self.source)
|
||||
cbx.addFile(tmpXML)
|
||||
except OSError as e:
|
||||
raise UserWarning(e)
|
||||
rmtree(workdir)
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# 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
|
||||
# (http://nedbatchelder.com/blog/200712/extracting_jpgs_from_pdfs.html)
|
||||
@@ -23,19 +25,23 @@ import os
|
||||
from random import choice
|
||||
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:
|
||||
def __init__(self, origFileName):
|
||||
self.origFileName = origFileName
|
||||
self.filename = os.path.splitext(origFileName)
|
||||
# noinspection PyUnusedLocal
|
||||
self.path = self.filename[0] + "-KCC-" + ''.join(choice(ascii_uppercase + digits) for x in range(3))
|
||||
def __init__(self, fname):
|
||||
self.fname = fname
|
||||
self.filename = os.path.splitext(fname)
|
||||
self.path = self.filename[0] + "-KCC-" + ''.join(choice(ascii_uppercase + digits) for _ in range(3))
|
||||
|
||||
def getPath(self):
|
||||
return self.path
|
||||
|
||||
def extract(self):
|
||||
pdf = open(self.origFileName, "rb").read()
|
||||
pdf = open(self.fname, "rb").read()
|
||||
startmark = b"\xff\xd8"
|
||||
startfix = 0
|
||||
endmark = b"\xff\xd9"
|
||||
@@ -59,10 +65,15 @@ class PdfJpgExtract:
|
||||
raise Exception("Didn't find end of JPG!")
|
||||
istart += startfix
|
||||
iend += endfix
|
||||
i = iend
|
||||
|
||||
if iend - istart < STRAY_IMAGE_LENGTH_THRESHOLD:
|
||||
continue
|
||||
|
||||
jpg = pdf[istart:iend]
|
||||
jpgfile = open(self.path + "/jpg%d.jpg" % njpg, "wb")
|
||||
jpgfile.write(jpg)
|
||||
jpgfile.close()
|
||||
njpg += 1
|
||||
i = iend
|
||||
|
||||
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) 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
|
||||
# any purpose with or without fee is hereby granted, provided that the
|
||||
@@ -19,11 +21,10 @@
|
||||
import os
|
||||
from hashlib import md5
|
||||
from html.parser import HTMLParser
|
||||
import subprocess
|
||||
from distutils.version import StrictVersion
|
||||
from shutil import rmtree, copy
|
||||
from tempfile import mkdtemp
|
||||
from zipfile import ZipFile, ZIP_DEFLATED
|
||||
from re import split
|
||||
import sys
|
||||
from traceback import format_tb
|
||||
|
||||
|
||||
@@ -48,7 +49,7 @@ class HTMLStripper(HTMLParser):
|
||||
def getImageFileName(imgfile):
|
||||
name, ext = os.path.splitext(imgfile)
|
||||
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 [name, ext]
|
||||
|
||||
@@ -73,8 +74,8 @@ def walkLevel(some_dir, level=1):
|
||||
del dirs[:]
|
||||
|
||||
|
||||
def md5Checksum(filePath):
|
||||
with open(filePath, 'rb') as fh:
|
||||
def md5Checksum(fpath):
|
||||
with open(fpath, 'rb') as fh:
|
||||
m = md5()
|
||||
while True:
|
||||
data = fh.read(8192)
|
||||
@@ -84,47 +85,28 @@ def md5Checksum(filePath):
|
||||
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):
|
||||
return ''.join(format_tb(traceback))\
|
||||
.replace('C:/projects/kcc/', '') \
|
||||
.replace('c:/projects/kcc/', '') \
|
||||
.replace('C:/python36-x64/', '')\
|
||||
.replace('c:/python36-x64/', '')\
|
||||
.replace('C:\\projects\\kcc\\', '') \
|
||||
.replace('c:\\projects\\kcc\\', '') \
|
||||
.replace('C:\\python36-x64\\', '')\
|
||||
.replace('c:\\python36-x64\\', '')
|
||||
.replace('C:/projects/kcc/', '')\
|
||||
.replace('c:/projects/kcc/', '')\
|
||||
.replace('C:/python37-x64/', '')\
|
||||
.replace('c:/python37-x64/', '')\
|
||||
.replace('C:\\projects\\kcc\\', '')\
|
||||
.replace('c:\\projects\\kcc\\', '')\
|
||||
.replace('C:\\python37-x64\\', '')\
|
||||
.replace('c:\\python37-x64\\', '')
|
||||
|
||||
|
||||
# noinspection PyUnresolvedReferences
|
||||
def dependencyCheck(level):
|
||||
missing = []
|
||||
if level > 2:
|
||||
try:
|
||||
from PyQt5.QtCore import qVersion as qtVersion
|
||||
if StrictVersion('5.6.0') > StrictVersion(qtVersion()):
|
||||
missing.append('PyQt 5.6.0+')
|
||||
from PySide6.QtCore import qVersion as qtVersion
|
||||
if StrictVersion('6.5.1') > StrictVersion(qtVersion()):
|
||||
missing.append('PySide 6.5.1+')
|
||||
except ImportError:
|
||||
missing.append('PyQt 5.6.0+')
|
||||
missing.append('PySide 6.5.1+')
|
||||
try:
|
||||
import raven
|
||||
except ImportError:
|
||||
@@ -137,17 +119,25 @@ def dependencyCheck(level):
|
||||
except ImportError:
|
||||
missing.append('psutil 5.0.0+')
|
||||
try:
|
||||
from types import ModuleType
|
||||
from slugify import __version__ as slugifyVersion
|
||||
if isinstance(slugifyVersion, ModuleType):
|
||||
slugifyVersion = slugifyVersion.__version__
|
||||
if StrictVersion('1.2.1') > StrictVersion(slugifyVersion):
|
||||
missing.append('python-slugify 1.2.1+')
|
||||
except ImportError:
|
||||
missing.append('python-slugify 1.2.1+')
|
||||
try:
|
||||
from PIL import PILLOW_VERSION as pillowVersion
|
||||
if StrictVersion('4.0.0') > StrictVersion(pillowVersion):
|
||||
missing.append('Pillow 4.0.0+')
|
||||
from PIL import __version__ as pillowVersion
|
||||
if StrictVersion('5.2.0') > StrictVersion(pillowVersion):
|
||||
missing.append('Pillow 5.2.0+')
|
||||
except ImportError:
|
||||
missing.append('Pillow 4.0.0+')
|
||||
missing.append('Pillow 5.2.0+')
|
||||
if len(missing) > 0:
|
||||
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 -*-
|
||||
#
|
||||
# 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
|
||||
# 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"
|
||||
KCCAplication = KCC_gui.QApplicationMessaging(sys.argv)
|
||||
if KCCAplication.isRunning():
|
||||
if len(sys.argv) > 1:
|
||||
KCCAplication.sendMessage(sys.argv[1])
|
||||
for i in range(1, len(sys.argv)):
|
||||
KCCAplication.sendMessage(sys.argv[i])
|
||||
else:
|
||||
KCCAplication.sendMessage('ARISE')
|
||||
else:
|
||||
KCCWindow = KCC_gui.QMainWindowKCC()
|
||||
KCCUI = KCC_gui.KCCGUI(KCCAplication, KCCWindow)
|
||||
if len(sys.argv) > 1:
|
||||
KCCUI.handleMessage(sys.argv[1])
|
||||
for i in range(1, len(sys.argv)):
|
||||
KCCUI.handleMessage(sys.argv[i])
|
||||
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
|
||||
Pillow>=4.0.0
|
||||
psutil>=5.0.0
|
||||
PySide6>=6.5.1
|
||||
Pillow>=5.2.0
|
||||
psutil>=5.9.5
|
||||
requests>=2.31.0
|
||||
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
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
pip/pyinstaller build script for KCC.
|
||||
|
||||
Install as Python package:
|
||||
python3 setup.py install
|
||||
|
||||
Create EXE/APP/DEB:
|
||||
Create EXE/APP:
|
||||
python3 setup.py build_binary
|
||||
"""
|
||||
|
||||
import os
|
||||
import platform
|
||||
import sys
|
||||
import shutil
|
||||
import setuptools
|
||||
import distutils.cmd
|
||||
from kindlecomicconverter import __version__
|
||||
@@ -21,6 +22,7 @@ MAIN = 'kcc.py'
|
||||
VERSION = __version__
|
||||
|
||||
|
||||
# noinspection PyUnresolvedReferences
|
||||
class BuildBinaryCommand(distutils.cmd.Command):
|
||||
description = 'build binary release'
|
||||
user_options = []
|
||||
@@ -35,41 +37,20 @@ class BuildBinaryCommand(distutils.cmd.Command):
|
||||
def run(self):
|
||||
VERSION = __version__
|
||||
if sys.platform == 'darwin':
|
||||
os.system('pyinstaller -y -F -i icons/comic2ebook.icns -n "Kindle Comic Converter" -w -s kcc.py')
|
||||
shutil.copy('other/osx/7za', 'dist/Kindle Comic Converter.app/Contents/Resources')
|
||||
shutil.copy('other/osx/unrar', 'dist/Kindle Comic Converter.app/Contents/Resources')
|
||||
shutil.copy('other/osx/Info.plist', 'dist/Kindle Comic Converter.app/Contents')
|
||||
shutil.copy('LICENSE.txt', 'dist/Kindle Comic Converter.app/Contents/Resources')
|
||||
shutil.copy('other/windows/Additional-LICENSE.txt', 'dist/Kindle Comic Converter.app/Contents/Resources')
|
||||
os.chmod('dist/Kindle Comic Converter.app/Contents/Resources/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)
|
||||
os.system('pyinstaller -y -D -i icons/comic2ebook.icns -n "Kindle Comic Converter" -w -s kcc.py')
|
||||
# TODO /usr/bin/codesign --force -s "$MACOS_CERTIFICATE_NAME" --options runtime dist/Applications/Kindle\ Comic\ Converter.app -v
|
||||
os.system(f'appdmg kcc.json dist/kcc_macos_{platform.processor()}_{VERSION}.dmg')
|
||||
sys.exit(0)
|
||||
elif sys.platform == 'win32':
|
||||
os.system('pyinstaller -y -F -i icons\comic2ebook.ico -n KCC -w --noupx kcc.py')
|
||||
if os.getenv('APPVEYOR'):
|
||||
if len(VERSION) == 3:
|
||||
VERSION = VERSION + '.0'
|
||||
os.system('setup.bat ' + VERSION)
|
||||
exit(0)
|
||||
os.system('pyinstaller -y -F -i icons\\comic2ebook.ico -n KCC_' + VERSION + ' -w --noupx kcc.py')
|
||||
sys.exit(0)
|
||||
elif sys.platform == 'linux':
|
||||
os.system(
|
||||
'pyinstaller --hidden-import=queue -y -F -i icons/comic2ebook.ico -n kcc_linux_' + VERSION + ' kcc.py')
|
||||
sys.exit(0)
|
||||
else:
|
||||
os.system('pyinstaller -y -F kcc.py')
|
||||
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)
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
setuptools.setup(
|
||||
cmdclass={
|
||||
@@ -94,11 +75,15 @@ setuptools.setup(
|
||||
},
|
||||
packages=['kindlecomicconverter'],
|
||||
install_requires=[
|
||||
'PyQt5>=5.6.0',
|
||||
'Pillow>=4.0.0',
|
||||
'psutil>=5.0.0',
|
||||
'python-slugify>=1.2.1',
|
||||
'pyside6>=6.5.1',
|
||||
'Pillow>=5.2.0',
|
||||
'psutil>=5.9.5',
|
||||
'python-slugify>=1.2.1,<9.0.0',
|
||||
'raven>=6.0.0',
|
||||
'requests>=2.31.0',
|
||||
'mozjpeg-lossless-optimization>=1.1.2',
|
||||
'natsort[fast]>=8.4.0',
|
||||
'distro',
|
||||
],
|
||||
classifiers=[],
|
||||
zip_safe=False,
|
||||
|
||||
Reference in New Issue
Block a user