mirror of
https://github.com/ciromattia/kcc
synced 2026-04-15 13:38:46 +00:00
Compare commits
287 Commits
7z-kindleg
...
v7.5.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cb0520dcab | ||
|
|
623bce6ae3 | ||
|
|
ad60894d19 | ||
|
|
4229b79c42 | ||
|
|
5fa6a59672 | ||
|
|
f171314a49 | ||
|
|
6b28e313e6 | ||
|
|
5a17435f7d | ||
|
|
0d573eb0a1 | ||
|
|
30a3f90907 | ||
|
|
9a7de0f5d9 | ||
|
|
f1db31205b | ||
|
|
eef5a85fa6 | ||
|
|
271200d29f | ||
|
|
ee375abfc5 | ||
|
|
94257c396a | ||
|
|
a05111b64a | ||
|
|
96e3ba7482 | ||
|
|
eb0abb538c | ||
|
|
87c2ef8033 | ||
|
|
ae7f56c81b | ||
|
|
70c73a82eb | ||
|
|
dbf4e45d25 | ||
|
|
1c6fe0cb22 | ||
|
|
2f7f6ebf0a | ||
|
|
21159c4328 | ||
|
|
4bb6ba55d3 | ||
|
|
06ae4ec25f | ||
|
|
3ac5709e73 | ||
|
|
fe7255a2d9 | ||
|
|
4712eac3c2 | ||
|
|
8ef5bf14ac | ||
|
|
c7e69f5bdb | ||
|
|
51d0be4379 | ||
|
|
ddc0ca2ff5 | ||
|
|
bd6dfa1e33 | ||
|
|
a95dde4cba | ||
|
|
2882d0f707 | ||
|
|
a1fa8e0ec3 | ||
|
|
ae4e063e09 | ||
|
|
8e0deff5ae | ||
|
|
3bd752537d | ||
|
|
4319f64815 | ||
|
|
82d2f7f4bf | ||
|
|
5a1e614a5d | ||
|
|
75e05a0ef0 | ||
|
|
e0f5bff527 | ||
|
|
a8316737be | ||
|
|
04228d100b | ||
|
|
8cc44c99f7 | ||
|
|
60f7902edd | ||
|
|
34bea98ca0 | ||
|
|
734b179e8a | ||
|
|
dcaa7401e7 | ||
|
|
d4d71cdd05 | ||
|
|
ec613cce7b | ||
|
|
ada001eb41 | ||
|
|
c4f845c221 | ||
|
|
ebb59dbc2d | ||
|
|
8da2b4cb96 | ||
|
|
7b8858678f | ||
|
|
be07e0df6a | ||
|
|
dc711e671d | ||
|
|
581ecd0ec2 | ||
|
|
f3a32c6174 | ||
|
|
0ce5f7f186 | ||
|
|
c3d2f89471 | ||
|
|
b1c4cd36f1 | ||
|
|
b7c6281b55 | ||
|
|
559485184d | ||
|
|
75f5274449 | ||
|
|
dfc149d893 | ||
|
|
327b522080 | ||
|
|
7c029b4ba1 | ||
|
|
2db8f5c8fd | ||
|
|
ce72921289 | ||
|
|
f1bbc47798 | ||
|
|
5a39007db6 | ||
|
|
0d7487f8d4 | ||
|
|
8a78f82ff2 | ||
|
|
149f7e5921 | ||
|
|
40d219de4d | ||
|
|
370d1d7392 | ||
|
|
8295f163c2 | ||
|
|
9f9a97afaa | ||
|
|
d113cae154 | ||
|
|
bc4afeb69e | ||
|
|
afb32f6287 | ||
|
|
bc516634cf | ||
|
|
36180f7904 | ||
|
|
3517994d37 | ||
|
|
08acad10ea | ||
|
|
727df0c9d6 | ||
|
|
13e71df172 | ||
|
|
3e7646bbad | ||
|
|
1c81a9d5b3 | ||
|
|
efa831341c | ||
|
|
1dd36e08eb | ||
|
|
d9d5ce2423 | ||
|
|
83c6b7b2d5 | ||
|
|
55dfdd32f8 | ||
|
|
3e3710dd76 | ||
|
|
41f87273ca | ||
|
|
b959739b53 | ||
|
|
d1b66d16fd | ||
|
|
80b01d298f | ||
|
|
de2aad0b9c | ||
|
|
bc7ab0879c | ||
|
|
2ab0135815 | ||
|
|
3882b6ce0a | ||
|
|
5e23e2cac1 | ||
|
|
d36933cb9a | ||
|
|
e40cf29aa3 | ||
|
|
9d1802453c | ||
|
|
4337d6c10d | ||
|
|
9680ff24c2 | ||
|
|
48b5b4f397 | ||
|
|
c712dc12a2 | ||
|
|
d2be9138c4 | ||
|
|
3cb40772a4 | ||
|
|
ef3b756247 | ||
|
|
77af020abb | ||
|
|
1c9c6c13b4 | ||
|
|
85ce39b2c3 | ||
|
|
6a33aee241 | ||
|
|
271a129537 | ||
|
|
23099cee81 | ||
|
|
b957fcf3fe | ||
|
|
187475a424 | ||
|
|
88fd54e2ba | ||
|
|
b23b67bbbe | ||
|
|
9992d895cf | ||
|
|
561951a349 | ||
|
|
b8b7926366 | ||
|
|
92f3308e1c | ||
|
|
9e87ccef4e | ||
|
|
9a2a09eab9 | ||
|
|
88cf2fd21f | ||
|
|
7a3ed262b1 | ||
|
|
9e204aad76 | ||
|
|
e1f9d12676 | ||
|
|
24ab72fcbc | ||
|
|
4af6a75874 | ||
|
|
28b6188a3f | ||
|
|
f000af1207 | ||
|
|
299f916580 | ||
|
|
c39e403595 | ||
|
|
e787dd2897 | ||
|
|
01625904d1 | ||
|
|
5f8526da44 | ||
|
|
1159e737a0 | ||
|
|
5bbdb715e9 | ||
|
|
1a3cd6c916 | ||
|
|
e1e6d587f4 | ||
|
|
ca5c0bdd61 | ||
|
|
c6f491d27e | ||
|
|
c9ed3feef1 | ||
|
|
be147fe7e5 | ||
|
|
62ffa2bc80 | ||
|
|
11186d07c0 | ||
|
|
4b3cd6882a | ||
|
|
b35a2baf05 | ||
|
|
11a395e983 | ||
|
|
2e39a8c227 | ||
|
|
02535421a0 | ||
|
|
3d4fae62d8 | ||
|
|
2b550b8b98 | ||
|
|
ecee7cf6f5 | ||
|
|
b0a5558da1 | ||
|
|
1b487c18d6 | ||
|
|
a3546d19c3 | ||
|
|
2f703ef92c | ||
|
|
4fb993b38b | ||
|
|
1401f94c1f | ||
|
|
70d10204ee | ||
|
|
a9d0c57ba6 | ||
|
|
5598596650 | ||
|
|
4b7b6d1c58 | ||
|
|
075bc748d1 | ||
|
|
9e318ed33e | ||
|
|
6d21bfa6fa | ||
|
|
132574d57d | ||
|
|
317fb33fd0 | ||
|
|
2189f4b1cb | ||
|
|
462a3cebde | ||
|
|
2a414ef936 | ||
|
|
4adb998896 | ||
|
|
315b6e150d | ||
|
|
5875508597 | ||
|
|
0a4ef31daf | ||
|
|
c99df3308e | ||
|
|
e9482fbd6c | ||
|
|
434fe90b00 | ||
|
|
73a91ec0ae | ||
|
|
e0b1848e09 | ||
|
|
a9360e6bc3 | ||
|
|
ae475e709a | ||
|
|
4769f68265 | ||
|
|
dbe6043542 | ||
|
|
e1a318145d | ||
|
|
a71523b2d4 | ||
|
|
7a5473f530 | ||
|
|
f5a5624112 | ||
|
|
3b7e8dc9a5 | ||
|
|
e637f37ef0 | ||
|
|
6ba690659f | ||
|
|
50eb48fb9b | ||
|
|
4a661a1a17 | ||
|
|
c26383c4b5 | ||
|
|
4e6ee8b59b | ||
|
|
6c6f591e45 | ||
|
|
78c014bf22 | ||
|
|
421e6bcb64 | ||
|
|
5168cd73c4 | ||
|
|
f7f19b99da | ||
|
|
1131bab41f | ||
|
|
8d204668a7 | ||
|
|
99d94ceaa7 | ||
|
|
8ff401cc3a | ||
|
|
fb7d92d737 | ||
|
|
1ca8b2c11b | ||
|
|
40e0b4853b | ||
|
|
005313f978 | ||
|
|
add2ef9faa | ||
|
|
0c98acd606 | ||
|
|
fe902ec213 | ||
|
|
4e9714e6f8 | ||
|
|
1337ad7fe3 | ||
|
|
511c7c1580 | ||
|
|
16dd034af4 | ||
|
|
d22794555f | ||
|
|
ab089adbca | ||
|
|
2c770f4562 | ||
|
|
4c73006bd8 | ||
|
|
1093dbf65a | ||
|
|
df9990c692 | ||
|
|
114f4c9e57 | ||
|
|
c2c477475d | ||
|
|
a0f8d0b5cf | ||
|
|
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 |
9
.github/workflows/package-linux.yml
vendored
9
.github/workflows/package-linux.yml
vendored
@@ -23,7 +23,7 @@ on:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up Python
|
||||
@@ -34,8 +34,8 @@ jobs:
|
||||
- name: Install python dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libpng-dev libjpeg-dev p7zip-full p7zip-rar python3-pyqt5 python3-pip squashfs-tools libfuse2
|
||||
python -m pip install --upgrade pip setuptools wheel certifi pyinstaller PyQt6 --no-binary pyinstaller
|
||||
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: |
|
||||
@@ -64,12 +64,11 @@ jobs:
|
||||
name: AppImage
|
||||
path: './*.AppImage*'
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
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*
|
||||
|
||||
12
.github/workflows/package-macos.yml
vendored
12
.github/workflows/package-macos.yml
vendored
@@ -23,7 +23,10 @@ on:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: macos-latest
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ macos-13, macos-14 ]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up Python
|
||||
@@ -77,16 +80,15 @@ jobs:
|
||||
- name: upload build
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: mac-os-build
|
||||
name: mac-os-build-${{ runner.arch }}
|
||||
path: dist/*.dmg
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
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
|
||||
@@ -95,4 +97,4 @@ jobs:
|
||||
# if: ${{ always() }}
|
||||
run: |
|
||||
security delete-keychain $RUNNER_TEMP/app-signing.keychain-db
|
||||
rm ~/Library/MobileDevice/Provisioning\ Profiles/build_pp.mobileprovision
|
||||
rm ~/Library/MobileDevice/Provisioning\ Profiles/build_pp.mobileprovision
|
||||
|
||||
@@ -10,54 +10,53 @@ on:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
entry: [ kcc-c2e, kcc-c2p ]
|
||||
include:
|
||||
- entry: kcc-c2e
|
||||
capital: KCC_c2e
|
||||
- entry: kcc-c2p
|
||||
capital: KCC_c2p
|
||||
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@python3-10-pyinstaller-5-3
|
||||
uses: JackMcKew/pyinstaller-action-windows@main
|
||||
with:
|
||||
path: .
|
||||
spec: ./kcc.spec
|
||||
- name: Package Application
|
||||
uses: JackMcKew/pyinstaller-action-windows@python3-10-pyinstaller-5-3
|
||||
with:
|
||||
path: .
|
||||
spec: ./kcc-c2e.spec
|
||||
- name: Package Application
|
||||
uses: JackMcKew/pyinstaller-action-windows@python3-10-pyinstaller-5-3
|
||||
with:
|
||||
path: .
|
||||
spec: ./kcc-c2p.spec
|
||||
spec: ./${{ matrix.entry }}.spec
|
||||
- name: rename binaries
|
||||
run: |
|
||||
version_built=$(cat kindlecomicconverter/__init__.py | grep version | awk '{print $3}' | sed "s/[^.0-9b]//g")
|
||||
mv dist/windows/kcc.exe dist/windows/kcc_${version_built}.exe
|
||||
mv dist/windows/kcc-c2e.exe dist/windows/kcc_c2e_${version_built}.exe
|
||||
mv dist/windows/kcc-c2p.exe dist/windows/kcc_c2p_${version_built}.exe
|
||||
- name: upload build
|
||||
mv dist/windows/${{ matrix.entry }}.exe dist/windows/${{ matrix.capital }}_${version_built}.exe
|
||||
|
||||
- name: upload-unsigned-artifact
|
||||
id: upload-unsigned-artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: windows-build
|
||||
name: windows-build-${{ matrix.entry }}
|
||||
path: dist/windows/*.exe
|
||||
|
||||
- id: optional_step_id
|
||||
uses: signpath/github-action-submit-signing-request@v1.2
|
||||
if: ${{ github.repository == 'ciromattia/kcc' }}
|
||||
with:
|
||||
api-token: '${{ secrets.SIGNPATH_API_TOKEN }}'
|
||||
organization-id: '1dc1bad6-4a8c-4f85-af30-5c5d3d392ea6'
|
||||
project-slug: 'kcc'
|
||||
signing-policy-slug: 'release-signing'
|
||||
github-artifact-id: '${{ steps.upload-unsigned-artifact.outputs.artifact-id }}'
|
||||
wait-for-completion: true
|
||||
output-artifact-directory: 'dist/windows/'
|
||||
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
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
|
||||
|
||||
19
.github/workflows/package-windows.yml
vendored
19
.github/workflows/package-windows.yml
vendored
@@ -41,18 +41,29 @@ jobs:
|
||||
- name: build binary
|
||||
run: |
|
||||
python setup.py build_binary
|
||||
- name: upload build
|
||||
- name: upload-unsigned-artifact
|
||||
id: upload-unsigned-artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: windows-build
|
||||
path: dist/*.exe
|
||||
- id: optional_step_id
|
||||
uses: signpath/github-action-submit-signing-request@v1.2
|
||||
if: ${{ github.repository == 'ciromattia/kcc' }}
|
||||
with:
|
||||
api-token: '${{ secrets.SIGNPATH_API_TOKEN }}'
|
||||
organization-id: '1dc1bad6-4a8c-4f85-af30-5c5d3d392ea6'
|
||||
project-slug: 'kcc'
|
||||
signing-policy-slug: 'release-signing'
|
||||
github-artifact-id: '${{ steps.upload-unsigned-artifact.outputs.artifact-id }}'
|
||||
wait-for-completion: true
|
||||
output-artifact-directory: 'dist/'
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
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
|
||||
dist/*.exe
|
||||
|
||||
30
.travis.yml
30
.travis.yml
@@ -1,30 +0,0 @@
|
||||
matrix:
|
||||
include:
|
||||
- os: osx
|
||||
language: generic
|
||||
osx_image: xcode11.1
|
||||
|
||||
before_install:
|
||||
- pip3 install --upgrade pip setuptools wheel
|
||||
|
||||
install:
|
||||
- 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
|
||||
|
||||
before_deploy:
|
||||
- shopt -s extglob
|
||||
- rm -r dist/!(*.deb|*.dmg)
|
||||
|
||||
deploy:
|
||||
provider: gcs
|
||||
access_key_id: GOOG1EC62457RKUYFR2TIZUWV4EFSV2EP5LVLPPFXUAKADWJFDYPFW63BQSLA
|
||||
secret_access_key:
|
||||
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
|
||||
local-dir: dist
|
||||
skip_cleanup: true
|
||||
on:
|
||||
repo: AcidWeb/KCC
|
||||
@@ -1,5 +1,5 @@
|
||||
# Select final stage based on TARGETARCH ARG
|
||||
FROM ghcr.io/ciromattia/kcc:docker-base-20230514
|
||||
FROM ghcr.io/ciromattia/kcc:docker-base-20241116
|
||||
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'
|
||||
@@ -16,4 +16,4 @@ 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"]
|
||||
CMD ["-h"]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM --platform=linux/amd64 python:3.11-slim-bullseye as compile-amd64
|
||||
FROM --platform=linux/amd64 python:3.13-slim-bullseye as compile-amd64
|
||||
ARG TARGETOS
|
||||
ARG TARGETARCH
|
||||
ARG TARGETVARIANT
|
||||
@@ -8,7 +8,7 @@ RUN echo "I'm building for $TARGETOS/$TARGETARCH/$TARGETVARIANT"
|
||||
COPY requirements.txt /opt/kcc/
|
||||
ENV PATH="/opt/venv/bin:$PATH"
|
||||
RUN DEBIAN_FRONTEND=noninteractive apt-get update -y && apt-get -yq upgrade && \
|
||||
apt-get install -y libpng-dev libjpeg-dev p7zip-full unrar-free libgl1 python3-pyqt5 && \
|
||||
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
|
||||
@@ -16,7 +16,7 @@ RUN DEBIAN_FRONTEND=noninteractive apt-get update -y && apt-get -yq upgrade && \
|
||||
|
||||
######################################################################################
|
||||
|
||||
FROM --platform=linux/arm64 python:3.11-slim-bullseye as compile-arm64
|
||||
FROM --platform=linux/arm64 python:3.13-slim-bullseye as compile-arm64
|
||||
ARG TARGETOS
|
||||
ARG TARGETARCH
|
||||
ARG TARGETVARIANT
|
||||
@@ -28,6 +28,9 @@ ENV LC_ALL=C.UTF-8 \
|
||||
|
||||
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
|
||||
|
||||
COPY requirements.txt /opt/kcc/
|
||||
ENV PATH="/opt/venv/bin:$PATH"
|
||||
|
||||
RUN set -x && \
|
||||
TEMP_PACKAGES=() && \
|
||||
KEPT_PACKAGES=() && \
|
||||
@@ -55,7 +58,6 @@ RUN set -x && \
|
||||
KEPT_PACKAGES+=(p7zip-full) && \
|
||||
KEPT_PACKAGES+=(python3) && \
|
||||
KEPT_PACKAGES+=(python3-pip) && \
|
||||
KEPT_PACKAGES+=(python3-pyqt5) && \
|
||||
KEPT_PACKAGES+=(unrar-free) && \
|
||||
# Install packages
|
||||
DEBIAN_FRONTEND=noninteractive apt-get update -y && apt-get -yq upgrade && \
|
||||
@@ -65,14 +67,13 @@ RUN set -x && \
|
||||
&& \
|
||||
# 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
|
||||
python -m pip install -r /opt/kcc/requirements.txt
|
||||
|
||||
|
||||
######################################################################################
|
||||
|
||||
FROM --platform=linux/arm/v7 python:3.11-slim-bullseye as compile-armv7
|
||||
FROM --platform=linux/arm/v7 python:3.13-slim-bullseye as compile-armv7
|
||||
ARG TARGETOS
|
||||
ARG TARGETARCH
|
||||
ARG TARGETVARIANT
|
||||
@@ -84,6 +85,9 @@ ENV LC_ALL=C.UTF-8 \
|
||||
|
||||
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
|
||||
|
||||
COPY requirements.txt /opt/kcc/
|
||||
ENV PATH="/opt/venv/bin:$PATH"
|
||||
|
||||
RUN set -x && \
|
||||
TEMP_PACKAGES=() && \
|
||||
KEPT_PACKAGES=() && \
|
||||
@@ -112,7 +116,6 @@ RUN set -x && \
|
||||
KEPT_PACKAGES+=(p7zip-full) && \
|
||||
KEPT_PACKAGES+=(python3) && \
|
||||
KEPT_PACKAGES+=(python3-pip) && \
|
||||
KEPT_PACKAGES+=(python3-pyqt5) && \
|
||||
KEPT_PACKAGES+=(unrar-free) && \
|
||||
# Install packages
|
||||
DEBIAN_FRONTEND=noninteractive apt-get update -y && apt-get -yq upgrade && \
|
||||
@@ -122,19 +125,18 @@ RUN set -x && \
|
||||
&& \
|
||||
# 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
|
||||
python -m pip install --upgrade pillow psutil requests python-slugify raven packaging mozjpeg-lossless-optimization natsort distro numpy
|
||||
|
||||
|
||||
######################################################################################
|
||||
FROM --platform=linux/amd64 python:3.11-slim-bullseye as build-amd64
|
||||
FROM --platform=linux/amd64 python:3.13-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
|
||||
FROM --platform=linux/arm64 python:3.13-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
|
||||
FROM --platform=linux/arm/v7 python:3.13-slim-bullseye as build-armv7
|
||||
COPY --from=compile-armv7 /opt/venv /opt/venv
|
||||
######################################################################################
|
||||
|
||||
@@ -158,5 +160,5 @@ WORKDIR /app
|
||||
RUN DEBIAN_FRONTEND=noninteractive apt-get update -y && apt-get -yq upgrade && \
|
||||
apt-get install -y p7zip-full unrar-free && \
|
||||
ln -s /app/kindlegen /bin/kindlegen && \
|
||||
echo docker-base-20230514 > /IMAGE_VERSION
|
||||
echo docker-base-20241116 > /IMAGE_VERSION
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
ISC LICENSE
|
||||
|
||||
Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
||||
Copyright (c) 2012-2025 Ciro Mattia Gonano <ciromattia@gmail.com>
|
||||
Copyright (c) 2013-2019 Paweł Jastrzębski <pawelj@iosphe.re>
|
||||
Copyright (c) 2021-2023 Darodi
|
||||
Copyright (c) 2021-2023 Darodi (https://github.com/darodi)
|
||||
Copyright (c) 2023-2025 Alex Xu (https://github.com/axu2)
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for
|
||||
any purpose with or without fee is hereby granted, provided that the
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
exclude kindlecomicconverter/sentry.py
|
||||
242
README.md
242
README.md
@@ -1,15 +1,48 @@
|
||||
<img src="header.jpg" alt="Header Image" width="400">
|
||||
|
||||
# KCC
|
||||
|
||||
[](https://github.com/ciromattia/kcc/releases)
|
||||
[](https://github.com/ciromattia/kcc/pkgs/container/kcc)
|
||||
[](https://github.com/ciromattia/kcc/releases)
|
||||
|
||||
|
||||
[](https://github.com/ciromattia/kcc/releases)
|
||||
[](https://github.com/ciromattia/kcc/pkgs/container/kcc)
|
||||
**Kindle Comic Converter** optimizes black & white comics and manga for E-ink ereaders
|
||||
like Kindle, Kobo, ReMarkable, and more.
|
||||
Pages display in fullscreen without margins,
|
||||
with proper fixed layout support.
|
||||
Supported input formats include JPG/PNG/GIF image files in folders, archives, or PDFs.
|
||||
Supported output formats include MOBI/AZW3, EPUB, KEPUB, and CBZ.
|
||||
|
||||
If your source are super high resolution DRM-free PDFs from Kodansha/Humble Bundle/Fanatical,
|
||||
you'll need to first [convert the PDFs to CBZ](https://github.com/ciromattia/kcc/issues/680) for use in 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
|
||||
actually a comic/manga to EPUB converter that every e-reader owner can happily use**_.
|
||||
It can also optionally optimize images by applying a number of transformations.
|
||||
Its main feature is various optional image processing steps to look good on eink screens,
|
||||
which have different requirements than normal LCD screens.
|
||||
Combining that with downscaling to your specific device's screen resolution
|
||||
can result in filesize reductions of hundreds of MB per volume with no visible quality loss on eink.
|
||||
This can also improve battery life, page turn speed, and general performance
|
||||
on underpowered ereaders with small storage capacities.
|
||||
|
||||
KCC avoids many common formatting issues (some of which occur [even on the Kindle Store](https://github.com/ciromattia/kcc/wiki/Kindle-Store-bad-formatting)), such as:
|
||||
1) faded black levels causing unneccessarily low contrast, which is hard to see and can cause eyestrain.
|
||||
2) unneccessary margins at the bottom of the screen
|
||||
3) Not utilizing the full 1860x2480 resolution of the 10" Kindle Scribe (feature in progress)
|
||||
4) incorrect page turn direction for manga that's read right to left
|
||||
5) unaligned two page spreads in landscape, where pages are shifted over by 1
|
||||
|
||||
The GUI looks like this, built in Qt6, with my most commonly used settings:
|
||||
|
||||

|
||||
|
||||
Simply drag and drop your files/folders into the KCC window,
|
||||
adjust your settings (hover over each option to see details in a tooltip),
|
||||
and hit convert to create ereader optimized files.
|
||||
You can change the default output directory by holding `Shift` while clicking the convert button.
|
||||
Then just drag and drop the generated output files onto your device's documents folder via USB.
|
||||
If you are on macOS and use a 2022+ Kindle, you may need to use Amazon USB File Manager for Mac.
|
||||
|
||||
YouTube tutorial (please subscribe): https://www.youtube.com/watch?v=IR2Fhcm9658
|
||||
|
||||
### 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.
|
||||
@@ -22,25 +55,40 @@ If you have some **technical** problems using KCC please [file an issue here](ht
|
||||
If you can fix an open issue, fork & make a pull request.
|
||||
|
||||
If you find **KCC** valuable you can consider donating to the authors:
|
||||
- Ciro Mattia Gonano:
|
||||
- [](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=D8WNYNPBGDAS2)
|
||||
- [](http://flattr.com/thing/2260449/ciromattiakcc-on-GitHub)
|
||||
- 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)
|
||||
- Ciro Mattia Gonano (founder, active 2012-2014):
|
||||
|
||||
[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=D8WNYNPBGDAS2)
|
||||
|
||||
- Paweł Jastrzębski (active 2013-2019):
|
||||
|
||||
[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YTTJ4LK2JDHPS)
|
||||
[](https://jastrzeb.ski/donate/)
|
||||
|
||||
- Alex Xu (active 2023-Present)
|
||||
|
||||
[](https://ko-fi.com/Q5Q41BW8HS)
|
||||
|
||||
## Commissions
|
||||
|
||||
This section is subject to change:
|
||||
|
||||
Email (for commisions and inquiries): `kindle.comic.converter` gmail
|
||||
|
||||
|
||||
## Sponsors
|
||||
|
||||
- Free code signing on Windows provided by [SignPath.io](https://about.signpath.io/), certificate by [SignPath Foundation](https://signpath.org/)
|
||||
|
||||
## DOWNLOADS
|
||||
|
||||
- **https://github.com/ciromattia/kcc/releases**
|
||||
|
||||
Click on **Assets** of the latest release.
|
||||
Click on **Assets** of the latest release.
|
||||
|
||||
You probably want either
|
||||
- `kcc_*.*.*.exe` (Windows)
|
||||
- `KindleComicConverter_osx_*.*.*.dmg` (Mac)
|
||||
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.
|
||||
|
||||
@@ -50,33 +98,42 @@ 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
|
||||
- All options have additional information in tooltips if you hover over the option.
|
||||
- To get the converted book onto your Kindle/Kobo, just drag and drop the mobi/kepub into the documents folder on your Kindle/Kobo via USB
|
||||
- Cannot connect Kindle Scribe or 2024+ Kindle to macOS
|
||||
- Use official MTP [Amazon USB File Transfer app](https://www.amazon.com/gp/help/customer/display.html/ref=hp_Connect_USB_MTP?nodeId=TCUBEdEkbIhK07ysFu)
|
||||
(no login required). Works much better than previously recommended Android File Transfer. Cannot run simutaneously with other transfer apps.
|
||||
- How to make AZW3 instead of MOBI?
|
||||
- The `.mobi` file generated by KCC is a dual filetype, it's both MOBI and AZW3. The file extension is `.mobi` for compatibility reasons.
|
||||
- [Windows 7 support](https://github.com/ciromattia/kcc/issues/678)
|
||||
- [Combine files/chapters](https://github.com/ciromattia/kcc/issues/612#issuecomment-2117985011)
|
||||
- [Flatpak mobi conversion stuck](https://github.com/ciromattia/kcc/wiki/Installation#linux)
|
||||
- Image too dark?
|
||||
- The default gamma correction of 1.8 makes the image darker, and is useful for faded/gray artwork/text. Disable by setting gamma = 1.0
|
||||
- [Better PDF support (Humble Bundle, Fanatical, etc)](https://github.com/ciromattia/kcc/issues/680)
|
||||
- Huge margins / slow page turns?
|
||||
- You likely modified the file during transfer using a 3rd party app. Try simply dragging and dropping the final mobi/kepub file into the Kindle documents folder via USB.
|
||||
|
||||
## PREREQUISITES
|
||||
|
||||
You'll need to install various tools to access important but optional features.
|
||||
|
||||
The installation process has been greatly streamlined. No need to add 7z to PATH or locate KindleGen from the internet and put it in a special folder with KCC. Just run it and KCC will tell you what to install.
|
||||
|
||||
### 7-Zip
|
||||
|
||||
#### Windows 7-Zip
|
||||
|
||||
First install 7z from https://www.7-zip.org/ or with command line:
|
||||
```
|
||||
winget install --id 7zip.7zip
|
||||
```
|
||||
|
||||
#### macOS 7-Zip/Unar
|
||||
with [Homebrew](https://brew.sh/) installed
|
||||
```
|
||||
brew install p7zip
|
||||
brew install unar
|
||||
```
|
||||
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
|
||||
On Windows and macOS, install [Kindle Previewer](https://www.amazon.com/Kindle-Previewer/b?ie=UTF8&node=21381691011) and `kindlegen` will be autodetected from it.
|
||||
|
||||
Install [Kindle Previewer 3 (KP3)](https://www.amazon.com/Kindle-Previewer/b?ie=UTF8&node=21381691011). KCC will automatically detect KindleGen from it.
|
||||
If you have issues detecting it, get stuck on the MOBI conversion step, or use Linux AppImage or Flatpak, refer to the wiki: https://github.com/ciromattia/kcc/wiki/Installation#kindlegen
|
||||
|
||||
### 7-Zip
|
||||
|
||||
This is optional but will make conversions much faster.
|
||||
|
||||
This is required for certain files and advanced features.
|
||||
|
||||
KCC will ask you to install if needed.
|
||||
|
||||
Refer to the wiki to install: https://github.com/ciromattia/kcc/wiki/Installation#7-zip
|
||||
|
||||
## INPUT FORMATS
|
||||
**KCC** can understand and convert, at the moment, the following input types:
|
||||
@@ -106,12 +163,14 @@ sudo apt-get install python3 p7zip-full python3-pil python3-psutil python3-slugi
|
||||
'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),
|
||||
'K57': ("Kindle 5/7", (600, 800), Palette16, 1.8),
|
||||
'K810': ("Kindle 8/10", (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),
|
||||
'KV': ("Kindle Voyage, (1072, 1448), Palette16, 1.8),
|
||||
'KPW34': ("Kindle Paperwhite 3/4/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),
|
||||
'KO': ("Kindle Oasis 2/3/Paperwhite 12/Colorsoft 12", (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),
|
||||
@@ -122,14 +181,18 @@ sudo apt-get install python3 p7zip-full python3-pil python3-psutil python3-slugi
|
||||
'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),
|
||||
'Rmk1': ("reMarkable 1", (1404, 1872), Palette16, 1.8),
|
||||
'Rmk2': ("reMarkable 2", (1404, 1872), Palette16, 1.8),
|
||||
'RmkPP': ("reMarkable Paper Pro", (1620, 2160), Palette16, 1.8),
|
||||
'OTHER': ("Other", (0, 0), Palette16, 1.8),
|
||||
```
|
||||
|
||||
|
||||
### Standalone `kcc-c2e.py` usage:
|
||||
|
||||
```
|
||||
@@ -140,7 +203,8 @@ MANDATORY:
|
||||
|
||||
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]
|
||||
Device profile (Available options: K1, K2, K34, K578, KDX, KPW, KPW5, KV, KO, K11, KS, KoMT, KoG, KoGHD, KoA, KoAHD, KoAH2O, KoAO, KoN, KoC, KoCC, KoL, KoLC, 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
|
||||
@@ -160,8 +224,11 @@ PROCESSING:
|
||||
Set cropping mode. 0: Disabled 1: Margins 2: Margins + page numbers [Default=2]
|
||||
--cp CROPPINGP, --croppingpower CROPPINGP
|
||||
Set cropping power [Default=1.0]
|
||||
--preservemargin After calculating crop, "back up" a specified percentage amount [Default=0]
|
||||
--cm CROPPINGM, --croppingminimum CROPPINGM
|
||||
Set cropping minimum area ratio [Default=0.0]
|
||||
--ipc INTERPANELCROP, --interpanelcrop INTERPANELCROP
|
||||
Crop empty sections. 0: Disabled 1: Horizontally 2: Both [Default=0]
|
||||
--blackborders Disable autodetection and force black borders
|
||||
--whiteborders Disable autodetection and force white borders
|
||||
--forcecolor Don't convert images to grayscale
|
||||
@@ -175,10 +242,17 @@ OUTPUT SETTINGS:
|
||||
Output generated file to specified directory or file
|
||||
-t TITLE, --title TITLE
|
||||
Comic title [Default=filename or directory name]
|
||||
--comicinfotitle Write title from ComicInfo.xml
|
||||
-a AUTHOR, --author AUTHOR
|
||||
Author name [Default=KCC]
|
||||
-f FORMAT, --format FORMAT
|
||||
Output format (Available options: Auto, MOBI, EPUB, CBZ, KFX, MOBI+EPUB) [Default=Auto]
|
||||
--nokepub If format is EPUB, output file with '.epub' extension rather than '.kepub.epub'
|
||||
-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]
|
||||
--spreadshift Shift first page to opposite side in landscape for two page spread alignment
|
||||
--norotate Do not rotate double page spreads in spread splitter option.
|
||||
--reducerainbow Reduce rainbow effect on color eink by slightly blurring images
|
||||
|
||||
CUSTOM PROFILE:
|
||||
--customwidth CUSTOMWIDTH
|
||||
@@ -210,8 +284,78 @@ OTHER:
|
||||
-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 your fork of 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 `pyside6-designer` which is included in the `pip install pyside6`.
|
||||
Then use the `gen_ui_files` scripts to autogenerate the python UI.
|
||||
|
||||
An example PR adding a new checkbox is here: https://github.com/ciromattia/kcc/pull/785
|
||||
|
||||
Do not use `git merge` to merge master from upstream,
|
||||
use the "Sync fork" button on your fork on GitHub in your branch
|
||||
to avoid weird looking merges in pull requests.
|
||||
|
||||
When making changes, be aware of how your change might affect file splitting/chunking
|
||||
or chapter alignment.
|
||||
|
||||
### 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
|
||||
```
|
||||
|
||||
You can build a `.exe` of KCC like the downloads we offer with
|
||||
|
||||
```
|
||||
python setup.py build_binary
|
||||
```
|
||||
|
||||
### macOS install from source
|
||||
|
||||
If the system installed Python gives you issues, please install the latest Python from either brew or the official website.
|
||||
|
||||
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
|
||||
```
|
||||
|
||||
You can build a `.app` of KCC like the downloads we offer with
|
||||
|
||||
```
|
||||
python setup.py build_binary
|
||||
```
|
||||
|
||||
## CREDITS
|
||||
**KCC** is made by
|
||||
**KCC** is made by
|
||||
|
||||
- [Ciro Mattia Gonano](http://github.com/ciromattia)
|
||||
- [Paweł Jastrzębski](http://github.com/AcidWeb)
|
||||
@@ -227,6 +371,12 @@ The app relies and includes the following scripts:
|
||||
- Icon is by **Nikolay Verin** ([http://ncrow.deviantart.com/](http://ncrow.deviantart.com/)) and released under [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/) License.
|
||||
|
||||
## SAMPLE FILES CREATED BY KCC
|
||||
|
||||
https://www.mediafire.com/folder/ixh40veo6hrc5/kcc_samples
|
||||
|
||||
Older links (dead):
|
||||
|
||||
|
||||
* [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)
|
||||
@@ -246,5 +396,5 @@ The app relies and includes the following scripts:
|
||||
Please check [wiki page](https://github.com/ciromattia/kcc/wiki/Known-issues).
|
||||
|
||||
## COPYRIGHT
|
||||
Copyright (c) 2012-2023 Ciro Mattia Gonano, Paweł Jastrzębski and Darodi.
|
||||
Copyright (c) 2012-2025 Ciro Mattia Gonano, Paweł Jastrzębski, Darodi and Alex Xu.
|
||||
**KCC** is released under ISC LICENSE; see [LICENSE.txt](./LICENSE.txt) for further details.
|
||||
|
||||
14
appveyor.yml
14
appveyor.yml
@@ -1,14 +0,0 @@
|
||||
environment:
|
||||
PYTHON: "C:\\Python37-x64"
|
||||
|
||||
install:
|
||||
- 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"
|
||||
|
||||
build_script:
|
||||
- "%PYTHON%\\python.exe setup.py build_binary"
|
||||
|
||||
artifacts:
|
||||
- path: dist\KCC*
|
||||
@@ -5,12 +5,12 @@ channels:
|
||||
dependencies:
|
||||
- python=3.11
|
||||
- Pillow>=5.2.0
|
||||
- psutil>=5.0.0
|
||||
- psutil>=5.9.5
|
||||
- python-slugify>=1.2.1
|
||||
- raven>=6.0.0
|
||||
- distro
|
||||
- natsort[fast]>=8.4.0
|
||||
- natsort>=8.4.0
|
||||
- pip
|
||||
- pip:
|
||||
- mozjpeg-lossless-optimization>=1.1.2
|
||||
- PyQt5>=5.6.0
|
||||
- pyside6>=6.5.1
|
||||
|
||||
@@ -1,11 +1,3 @@
|
||||
|
||||
REM install qt creator
|
||||
REM conda create -n qtenv python=3.7
|
||||
REM conda activate qtenv
|
||||
REM pip install PyQt5
|
||||
|
||||
pyuic5 gui/KCC.ui > kindlecomicconverter/KCC_ui.py
|
||||
|
||||
pyuic5 gui/MetaEditor.ui > kindlecomicconverter/KCC_ui_editor.py
|
||||
|
||||
pyrcc5 gui/KCC.qrc > kindlecomicconverter/KCC_rc.py
|
||||
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
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
#!/bin/sh
|
||||
|
||||
# PREPARE PYTHON ENV
|
||||
# conda create -n pyqt5 python=3.7
|
||||
# source activate pyqt5
|
||||
# pip install pyqt5
|
||||
|
||||
pyuic5 gui/KCC.ui --from-imports > kindlecomicconverter/KCC_ui.py
|
||||
pyuic5 gui/MetaEditor.ui --from-imports > kindlecomicconverter/KCC_ui_editor.py
|
||||
pyrcc5 gui/KCC.qrc > kindlecomicconverter/KCC_rc.py
|
||||
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
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
<file>../icons/Kobo.png</file>
|
||||
<file>../icons/Other.png</file>
|
||||
<file>../icons/Kindle.png</file>
|
||||
<file>../icons/Rmk.png</file>
|
||||
</qresource>
|
||||
<qresource prefix="Formats">
|
||||
<file>../icons/CBZ.png</file>
|
||||
@@ -26,5 +27,6 @@
|
||||
<file>../icons/convert.png</file>
|
||||
<file>../icons/document_new.png</file>
|
||||
<file>../icons/folder_new.png</file>
|
||||
<file>../icons/kofi_symbol.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
834
gui/KCC.ui
834
gui/KCC.ui
@@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>450</width>
|
||||
<height>400</height>
|
||||
<width>519</width>
|
||||
<height>572</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@@ -22,188 +22,25 @@
|
||||
<property name="bottomMargin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<item row="5" column="0" colspan="2">
|
||||
<widget class="QWidget" name="optionWidget" native="true">
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<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="1" column="1">
|
||||
<widget class="QCheckBox" name="upscaleBox">
|
||||
<property name="toolTip">
|
||||
<string><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></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Stretch/Upscale</string>
|
||||
</property>
|
||||
<property name="tristate">
|
||||
<bool>true</bool>
|
||||
</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="2" column="1">
|
||||
<widget class="QCheckBox" name="outputSplit">
|
||||
<property name="toolTip">
|
||||
<string><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></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Output split</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="webtoonBox">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p style='white-space:pre'>Enable special parsing mode for Korean Webtoons.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Webtoon mode</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="2">
|
||||
<widget class="QCheckBox" name="colorBox">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p style='white-space:pre'>Disable conversion to grayscale.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Color mode</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="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="2">
|
||||
<widget class="QCheckBox" name="qualityBox">
|
||||
<property name="toolTip">
|
||||
<string><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></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Panel View 4/2/HQ</string>
|
||||
</property>
|
||||
<property name="tristate">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QCheckBox" name="mozJpegBox">
|
||||
<property name="toolTip">
|
||||
<string><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="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="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>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QCheckBox" name="deleteBox">
|
||||
<property name="toolTip">
|
||||
<string>Delete input file(s) or directory. It's not recoverable!</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Delete input</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="2">
|
||||
<widget class="QCheckBox" name="disableProcessingBox">
|
||||
<property name="toolTip">
|
||||
<string><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>
|
||||
</layout>
|
||||
<item row="2" column="0" colspan="2">
|
||||
<widget class="QListWidget" name="jobList">
|
||||
<property name="styleSheet">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::SelectionMode::NoSelection</enum>
|
||||
</property>
|
||||
<property name="verticalScrollMode">
|
||||
<enum>QAbstractItemView::ScrollMode::ScrollPerPixel</enum>
|
||||
</property>
|
||||
<property name="horizontalScrollMode">
|
||||
<enum>QAbstractItemView::ScrollMode::ScrollPerPixel</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0" colspan="2">
|
||||
<widget class="QWidget" name="gammaWidget" native="true">
|
||||
<property name="visible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<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>
|
||||
@@ -217,63 +54,62 @@
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="gammaLabel">
|
||||
<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>Gamma: Auto</string>
|
||||
<string>Metadata 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="QSlider" name="gammaSlider">
|
||||
<property name="maximum">
|
||||
<number>250</number>
|
||||
<widget class="QPushButton" name="kofiButton">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>30</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<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>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="croppingPowerLabel">
|
||||
<property name="text">
|
||||
<string>Cropping power:</string>
|
||||
<string>Support me on Ko-fi</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="KCC.qrc">
|
||||
<normaloff>:/Other/icons/kofi_symbol.png</normaloff>:/Other/icons/kofi_symbol.png</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>19</width>
|
||||
<height>16</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSlider" name="croppingPowerSlider">
|
||||
<property name="maximum">
|
||||
<number>200</number>
|
||||
<widget class="QPushButton" name="wikiButton">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>30</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<number>1</number>
|
||||
<property name="text">
|
||||
<string>Wiki</string>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
<property name="icon">
|
||||
<iconset resource="KCC.qrc">
|
||||
<normaloff>:/Other/icons/wiki.png</normaloff>:/Other/icons/wiki.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -313,7 +149,7 @@
|
||||
<string><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></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Add directory</string>
|
||||
<string>Add image folder</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="KCC.qrc">
|
||||
@@ -333,7 +169,7 @@
|
||||
<string><html><head/><body><p style='white-space:pre'>Add CBR, CBZ, CB7 or PDF file to queue.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Add file</string>
|
||||
<string>Add file(s)</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="KCC.qrc">
|
||||
@@ -418,77 +254,6 @@
|
||||
<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">
|
||||
@@ -506,11 +271,11 @@
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignJustify|Qt::AlignVCenter</set>
|
||||
<set>Qt::AlignmentFlag::AlignJustify|Qt::AlignmentFlag::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0" colspan="2">
|
||||
<item row="8" column="0" colspan="2">
|
||||
<widget class="QWidget" name="customWidget" native="true">
|
||||
<property name="visible">
|
||||
<bool>false</bool>
|
||||
@@ -550,7 +315,7 @@
|
||||
<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>
|
||||
<number>2400</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -583,6 +348,460 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="0" colspan="2">
|
||||
<widget class="QWidget" name="croppingWidget" native="true">
|
||||
<property name="visible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_5">
|
||||
<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="1" column="0">
|
||||
<widget class="QLabel" name="preserveMarginLabel">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>After calculating the cropping boundaries, &quot;back up&quot; a specified percentage amount.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Preserve Margin %</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="croppingPowerLabel">
|
||||
<property name="text">
|
||||
<string>Cropping power:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QSlider" name="croppingPowerSlider">
|
||||
<property name="maximum">
|
||||
<number>300</number>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QSpinBox" name="preserveMarginBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>99</number>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0" colspan="2">
|
||||
<widget class="QWidget" name="optionWidget" native="true">
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<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="4" 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>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" 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>Right-to-left mode</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QCheckBox" name="webtoonBox">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p style='white-space:pre'>Enable special parsing mode for Korean Webtoons.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Webtoon mode</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" 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 - Split and rotate<br/></span>Double page spreads will be displayed twice. First split and then rotated. </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="3" 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="2" 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="6" column="2">
|
||||
<widget class="QCheckBox" name="interPanelCropBox">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p><span style=" font-weight:600; text-decoration: underline;">Unchecked - Disabled<br/></span>Disabled</p><p><span style=" font-weight:600; text-decoration: underline;">Indeterminate - Horizontal<br/></span>Crop empty horizontal lines.</p><p><span style=" font-weight:600; text-decoration: underline;">Checked - Both<br/></span>Crop empty horizontal and vertical lines.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Inter-panel crop</string>
|
||||
</property>
|
||||
<property name="tristate">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="2">
|
||||
<widget class="QCheckBox" name="colorBox">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p style='white-space:pre'>Disable conversion to grayscale.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Color mode</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QCheckBox" name="qualityBox">
|
||||
<property name="toolTip">
|
||||
<string><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></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Panel View 4/2/HQ</string>
|
||||
</property>
|
||||
<property name="tristate">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="2">
|
||||
<widget class="QCheckBox" name="disableProcessingBox">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p style='white-space:pre'>Do not process any image, ignore profile and processing options.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Disable processing</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" 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="0" column="0">
|
||||
<widget class="QLineEdit" name="authorEdit">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::FocusPolicy::ClickFocus</enum>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Default Author is KCC</string>
|
||||
</property>
|
||||
<property name="placeholderText">
|
||||
<string>Default Author</string>
|
||||
</property>
|
||||
<property name="clearButtonEnabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<widget class="QCheckBox" name="deleteBox">
|
||||
<property name="toolTip">
|
||||
<string>Delete input file(s) or directory. It's not recoverable!</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Delete input</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="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="5" column="0">
|
||||
<widget class="QCheckBox" name="spreadShiftBox">
|
||||
<property name="toolTip">
|
||||
<string>Shift first page to opposite side in landscape for two page spread alignment</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Spread shift</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<widget class="QCheckBox" name="fileFusionBox">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Combines all selected files into a single file. (Helpful for combining chapters into volumes.)</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>File Fusion</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QCheckBox" name="upscaleBox">
|
||||
<property name="toolTip">
|
||||
<string><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></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Stretch/Upscale</string>
|
||||
</property>
|
||||
<property name="tristate">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QCheckBox" name="outputSplit">
|
||||
<property name="toolTip">
|
||||
<string><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></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Output split</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="1">
|
||||
<widget class="QCheckBox" name="noRotateBox">
|
||||
<property name="toolTip">
|
||||
<string>Do not rotate double page spreads in spread splitter option.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>No rotate</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="2">
|
||||
<widget class="QCheckBox" name="reduceRainbowBox">
|
||||
<property name="toolTip">
|
||||
<string>Reduce rainbow effect on color eink by slightly blurring images</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Rainbow blur</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="1">
|
||||
<widget class="QCheckBox" name="chunkSizeCheckBox">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p><span style=" font-weight:700; text-decoration: underline;">Unchecked<br/></span>Maximal output file size is 100 MB for Webtoon, 400 MB for others before split occurs.</p><p><span style=" font-weight:700; text-decoration: underline;">Checked</span><br/>Output file size specified in &quot;Chunk size MB&quot; before split occurs.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Chunk size</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0">
|
||||
<widget class="QCheckBox" name="comicinfoTitleBox">
|
||||
<property name="toolTip">
|
||||
<string>Write Title from ComicInfo.xml</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>ComicInfo Title</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0" colspan="2">
|
||||
<widget class="QWidget" name="gammaWidget" native="true">
|
||||
<property name="visible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<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="QLabel" name="gammaLabel">
|
||||
<property name="text">
|
||||
<string>Gamma: Auto</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSlider" name="gammaSlider">
|
||||
<property name="maximum">
|
||||
<number>250</number>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<widget class="QWidget" name="chunkSizeWidget" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="visible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Warning: chunk size greater than default may cause<br/>performance/battery issues, especially on older devices.</p></body></html></string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<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="QLabel" name="chunkSizeLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Chunk size MB:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="chunkSizeBox">
|
||||
<property name="minimum">
|
||||
<number>100</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>600</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>400</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="chunkSizeWarnLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Greater than default may cause performance issues on older ereaders.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="statusBar">
|
||||
@@ -607,18 +826,23 @@
|
||||
<tabstop>borderBox</tabstop>
|
||||
<tabstop>outputSplit</tabstop>
|
||||
<tabstop>colorBox</tabstop>
|
||||
<tabstop>croppingBox</tabstop>
|
||||
<tabstop>mozJpegBox</tabstop>
|
||||
<tabstop>maximizeStrips</tabstop>
|
||||
<tabstop>croppingBox</tabstop>
|
||||
<tabstop>spreadShiftBox</tabstop>
|
||||
<tabstop>deleteBox</tabstop>
|
||||
<tabstop>disableProcessingBox</tabstop>
|
||||
<tabstop>chunkSizeBox</tabstop>
|
||||
<tabstop>noRotateBox</tabstop>
|
||||
<tabstop>interPanelCropBox</tabstop>
|
||||
<tabstop>reduceRainbowBox</tabstop>
|
||||
<tabstop>heightBox</tabstop>
|
||||
<tabstop>croppingPowerSlider</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"/>
|
||||
|
||||
BIN
header.jpg
Normal file
BIN
header.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 921 KiB |
BIN
icons/Rmk.png
Normal file
BIN
icons/Rmk.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.0 KiB |
BIN
icons/kofi_symbol.png
Normal file
BIN
icons/kofi_symbol.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.1 KiB |
@@ -20,6 +20,8 @@
|
||||
|
||||
import sys
|
||||
|
||||
from kcc import modify_path
|
||||
|
||||
if sys.version_info < (3, 8, 0):
|
||||
print('ERROR: This is a Python 3.8+ script!')
|
||||
sys.exit(1)
|
||||
@@ -28,6 +30,7 @@ from multiprocessing import freeze_support, set_start_method
|
||||
from kindlecomicconverter.startup import startC2E
|
||||
|
||||
if __name__ == "__main__":
|
||||
modify_path()
|
||||
set_start_method('spawn')
|
||||
freeze_support()
|
||||
startC2E()
|
||||
|
||||
@@ -8,7 +8,7 @@ a = Analysis(['kcc-c2e.py'],
|
||||
pathex=['.'],
|
||||
binaries=[],
|
||||
datas=[],
|
||||
hiddenimports=[],
|
||||
hiddenimports=['_cffi_backend'],
|
||||
hookspath=[],
|
||||
runtime_hooks=[],
|
||||
excludes=[],
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
|
||||
import sys
|
||||
|
||||
from kcc import modify_path
|
||||
|
||||
if sys.version_info < (3, 8, 0):
|
||||
print('ERROR: This is a Python 3.8+ script!')
|
||||
sys.exit(1)
|
||||
@@ -28,6 +30,7 @@ from multiprocessing import freeze_support, set_start_method
|
||||
from kindlecomicconverter.startup import startC2P
|
||||
|
||||
if __name__ == "__main__":
|
||||
modify_path()
|
||||
set_start_method('spawn')
|
||||
freeze_support()
|
||||
startC2P()
|
||||
|
||||
@@ -8,7 +8,7 @@ a = Analysis(['kcc-c2p.py'],
|
||||
pathex=['.'],
|
||||
binaries=[],
|
||||
datas=[],
|
||||
hiddenimports=[],
|
||||
hiddenimports=['_cffi_backend'],
|
||||
hookspath=[],
|
||||
runtime_hooks=[],
|
||||
excludes=[],
|
||||
|
||||
99
kcc.py
99
kcc.py
@@ -18,57 +18,76 @@
|
||||
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
# PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
import os
|
||||
import sys
|
||||
import platform
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
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.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.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:\\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 ImportError:
|
||||
# pass
|
||||
def modify_path():
|
||||
if platform.system() == 'Darwin':
|
||||
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.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.pathsep + os.pathsep.join(mac_paths)
|
||||
os.chdir(os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
elif platform.system() == 'Linux':
|
||||
if getattr(sys, 'frozen', False):
|
||||
os.environ['PATH'] += os.pathsep + os.pathsep.join(
|
||||
[
|
||||
str(Path.home() / ".local" / "bin"),
|
||||
'/opt/homebrew/bin',
|
||||
'/usr/local/bin',
|
||||
'/usr/bin',
|
||||
'/bin',
|
||||
]
|
||||
)
|
||||
os.chdir(os.path.dirname(os.path.abspath(sys.executable)))
|
||||
else:
|
||||
os.chdir(os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
elif platform.system() == 'Windows':
|
||||
win_paths = [
|
||||
os.path.expandvars('%LOCALAPPDATA%\\Amazon\\KC2'),
|
||||
os.path.expandvars('%LOCALAPPDATA%\\Amazon\\Kindle Previewer 3\\lib\\fc\\bin\\'),
|
||||
os.path.expandvars('%UserProfile%\\Kindle Previewer 3\\lib\\fc\\bin\\'),
|
||||
'C:\\Apps\\Kindle Previewer 3\\lib\\fc\\bin',
|
||||
'D:\\Apps\\Kindle Previewer 3\\lib\\fc\\bin',
|
||||
'E:\\Apps\\Kindle Previewer 3\\lib\\fc\\bin',
|
||||
'C:\\Program Files\\7-Zip',
|
||||
'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__)))
|
||||
|
||||
|
||||
from multiprocessing import freeze_support, set_start_method
|
||||
from kindlecomicconverter.startup import start
|
||||
|
||||
if __name__ == "__main__":
|
||||
modify_path()
|
||||
set_start_method('spawn')
|
||||
freeze_support()
|
||||
start()
|
||||
|
||||
2
kcc.spec
2
kcc.spec
@@ -8,7 +8,7 @@ a = Analysis(['kcc.py'],
|
||||
pathex=['.'],
|
||||
binaries=[],
|
||||
datas=[],
|
||||
hiddenimports=[],
|
||||
hiddenimports=['_cffi_backend'],
|
||||
hookspath=[],
|
||||
runtime_hooks=[],
|
||||
excludes=[],
|
||||
|
||||
@@ -16,9 +16,13 @@
|
||||
# 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.
|
||||
from PySide6.QtCore import (QSize, QUrl, Qt, Signal, QIODeviceBase, QEvent, QThread, QSettings)
|
||||
from PySide6.QtGui import (QColor, QIcon, QPixmap, QDesktopServices)
|
||||
from PySide6.QtWidgets import (QApplication, QLabel, QListWidgetItem, QMainWindow, QApplication, QSystemTrayIcon, QFileDialog, QMessageBox, QDialog)
|
||||
from PySide6.QtNetwork import (QLocalSocket, QLocalServer)
|
||||
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
from urllib.parse import unquote
|
||||
from time import sleep
|
||||
@@ -26,35 +30,35 @@ from shutil import move, rmtree
|
||||
from subprocess import STDOUT, PIPE
|
||||
|
||||
import requests
|
||||
# noinspection PyUnresolvedReferences
|
||||
from PyQt5 import QtGui, QtCore, QtWidgets, QtNetwork
|
||||
from xml.sax.saxutils import escape
|
||||
from psutil import Process
|
||||
from copy import copy
|
||||
from distutils.version import StrictVersion
|
||||
from packaging.version import Version
|
||||
from raven import Client
|
||||
from tempfile import gettempdir
|
||||
from .shared import md5Checksum, HTMLStripper, sanitizeTrace, walkLevel, subprocess_run_silent
|
||||
|
||||
from .shared import HTMLStripper, available_archive_tools, sanitizeTrace, walkLevel, subprocess_run
|
||||
from . import __version__
|
||||
from . import comic2ebook
|
||||
from . import image
|
||||
from . import metadata
|
||||
from . import kindle
|
||||
from . import KCC_ui
|
||||
from . import KCC_ui_editor
|
||||
|
||||
|
||||
class QApplicationMessaging(QtWidgets.QApplication):
|
||||
messageFromOtherInstance = QtCore.pyqtSignal(bytes)
|
||||
class QApplicationMessaging(QApplication):
|
||||
messageFromOtherInstance = Signal(bytes)
|
||||
|
||||
def __init__(self, argv):
|
||||
QtWidgets.QApplication.__init__(self, argv)
|
||||
QApplication.__init__(self, argv)
|
||||
self._key = 'KCC'
|
||||
self._timeout = 1000
|
||||
self._locked = False
|
||||
socket = QtNetwork.QLocalSocket(self)
|
||||
socket.connectToServer(self._key, QtCore.QIODevice.WriteOnly)
|
||||
socket = QLocalSocket(self)
|
||||
socket.connectToServer(self._key, QIODeviceBase.OpenModeFlag.WriteOnly)
|
||||
if not socket.waitForConnected(self._timeout):
|
||||
self._server = QtNetwork.QLocalServer(self)
|
||||
self._server = QLocalServer(self)
|
||||
self._server.newConnection.connect(self.handleMessage)
|
||||
self._server.listen(self._key)
|
||||
else:
|
||||
@@ -66,11 +70,11 @@ class QApplicationMessaging(QtWidgets.QApplication):
|
||||
self._server.close()
|
||||
|
||||
def event(self, e):
|
||||
if e.type() == QtCore.QEvent.FileOpen:
|
||||
if e.type() == QEvent.Type.FileOpen:
|
||||
self.messageFromOtherInstance.emit(bytes(e.file(), 'UTF-8'))
|
||||
return True
|
||||
else:
|
||||
return QtWidgets.QApplication.event(self, e)
|
||||
return QApplication.event(self, e)
|
||||
|
||||
def isRunning(self):
|
||||
return self._locked
|
||||
@@ -81,54 +85,58 @@ class QApplicationMessaging(QtWidgets.QApplication):
|
||||
self.messageFromOtherInstance.emit(socket.readAll().data())
|
||||
|
||||
def sendMessage(self, message):
|
||||
socket = QtNetwork.QLocalSocket(self)
|
||||
socket.connectToServer(self._key, QtCore.QIODevice.WriteOnly)
|
||||
socket = QLocalSocket(self)
|
||||
socket.connectToServer(self._key, QIODeviceBase.OpenModeFlag.WriteOnly)
|
||||
socket.waitForConnected(self._timeout)
|
||||
socket.write(bytes(message, 'UTF-8'))
|
||||
socket.waitForBytesWritten(self._timeout)
|
||||
socket.disconnectFromServer()
|
||||
|
||||
|
||||
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()
|
||||
class QMainWindowKCC(QMainWindow):
|
||||
progressBarTick = Signal(str)
|
||||
modeConvert = Signal(int)
|
||||
addMessage = Signal(str, str, bool)
|
||||
addTrayMessage = Signal(str, str)
|
||||
showDialog = Signal(str, str)
|
||||
hideProgressBar = Signal()
|
||||
forceShutdown = 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.deviceKobo = QtGui.QIcon()
|
||||
self.deviceKobo.addPixmap(QtGui.QPixmap(":/Devices/icons/Kobo.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||
self.deviceOther = QtGui.QIcon()
|
||||
self.deviceOther.addPixmap(QtGui.QPixmap(":/Devices/icons/Other.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||
self.deviceKindle = QIcon()
|
||||
self.deviceKindle.addPixmap(QPixmap(":/Devices/icons/Kindle.png"), QIcon.Mode.Normal, QIcon.State.Off)
|
||||
self.deviceKobo = QIcon()
|
||||
self.deviceKobo.addPixmap(QPixmap(":/Devices/icons/Kobo.png"), QIcon.Mode.Normal, QIcon.State.Off)
|
||||
self.deviceRmk = QIcon()
|
||||
self.deviceRmk.addPixmap(QPixmap(":/Devices/icons/Rmk.png"), QIcon.Mode.Normal, QIcon.State.Off)
|
||||
self.deviceOther = QIcon()
|
||||
self.deviceOther.addPixmap(QPixmap(":/Devices/icons/Other.png"), QIcon.Mode.Normal, QIcon.State.Off)
|
||||
|
||||
self.MOBIFormat = QtGui.QIcon()
|
||||
self.MOBIFormat.addPixmap(QtGui.QPixmap(":/Formats/icons/MOBI.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||
self.CBZFormat = QtGui.QIcon()
|
||||
self.CBZFormat.addPixmap(QtGui.QPixmap(":/Formats/icons/CBZ.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||
self.EPUBFormat = QtGui.QIcon()
|
||||
self.EPUBFormat.addPixmap(QtGui.QPixmap(":/Formats/icons/EPUB.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||
self.MOBIFormat = QIcon()
|
||||
self.MOBIFormat.addPixmap(QPixmap(":/Formats/icons/MOBI.png"), QIcon.Mode.Normal, QIcon.State.Off)
|
||||
self.CBZFormat = QIcon()
|
||||
self.CBZFormat.addPixmap(QPixmap(":/Formats/icons/CBZ.png"), QIcon.Mode.Normal, QIcon.State.Off)
|
||||
self.EPUBFormat = QIcon()
|
||||
self.EPUBFormat.addPixmap(QPixmap(":/Formats/icons/EPUB.png"), QIcon.Mode.Normal, QIcon.State.Off)
|
||||
self.KFXFormat = QIcon()
|
||||
self.KFXFormat.addPixmap(QPixmap(":/Formats/icons/KFX.png"), QIcon.Normal, QIcon.Off)
|
||||
|
||||
self.info = QtGui.QIcon()
|
||||
self.info.addPixmap(QtGui.QPixmap(":/Status/icons/info.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||
self.warning = QtGui.QIcon()
|
||||
self.warning.addPixmap(QtGui.QPixmap(":/Status/icons/warning.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||
self.error = QtGui.QIcon()
|
||||
self.error.addPixmap(QtGui.QPixmap(":/Status/icons/error.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||
self.info = QIcon()
|
||||
self.info.addPixmap(QPixmap(":/Status/icons/info.png"), QIcon.Mode.Normal, QIcon.State.Off)
|
||||
self.warning = QIcon()
|
||||
self.warning.addPixmap(QPixmap(":/Status/icons/warning.png"), QIcon.Mode.Normal, QIcon.State.Off)
|
||||
self.error = QIcon()
|
||||
self.error.addPixmap(QPixmap(":/Status/icons/error.png"), QIcon.Mode.Normal, QIcon.State.Off)
|
||||
|
||||
self.programIcon = QtGui.QIcon()
|
||||
self.programIcon.addPixmap(QtGui.QPixmap(":/Icon/icons/comic2ebook.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||
self.programIcon = QIcon()
|
||||
self.programIcon.addPixmap(QPixmap(":/Icon/icons/comic2ebook.png"), QIcon.Mode.Normal, QIcon.State.Off)
|
||||
|
||||
|
||||
class VersionThread(QtCore.QThread):
|
||||
class VersionThread(QThread):
|
||||
def __init__(self):
|
||||
QtCore.QThread.__init__(self)
|
||||
QThread.__init__(self)
|
||||
self.newVersion = ''
|
||||
self.md5 = ''
|
||||
self.barProgress = 0
|
||||
@@ -145,9 +153,9 @@ class VersionThread(QtCore.QThread):
|
||||
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__)) \
|
||||
if ("b" not in __version__ and Version(latest_version) > Version(__version__)) \
|
||||
or ("b" in __version__
|
||||
and StrictVersion(latest_version) >= StrictVersion(re.sub(r'b.*', '', __version__))):
|
||||
and Version(latest_version) >= Version(re.sub(r'b.*', '', __version__))):
|
||||
MW.addMessage.emit('<a href="' + html_url + '"><b>The new version is available!</b></a>', 'warning',
|
||||
False)
|
||||
except Exception:
|
||||
@@ -157,9 +165,9 @@ class VersionThread(QtCore.QThread):
|
||||
self.answer = dialoganswer
|
||||
|
||||
|
||||
class ProgressThread(QtCore.QThread):
|
||||
class ProgressThread(QThread):
|
||||
def __init__(self):
|
||||
QtCore.QThread.__init__(self)
|
||||
QThread.__init__(self)
|
||||
self.running = False
|
||||
self.content = None
|
||||
self.progress = 0
|
||||
@@ -181,9 +189,9 @@ class ProgressThread(QtCore.QThread):
|
||||
self.running = False
|
||||
|
||||
|
||||
class WorkerThread(QtCore.QThread):
|
||||
class WorkerThread(QThread):
|
||||
def __init__(self):
|
||||
QtCore.QThread.__init__(self)
|
||||
QThread.__init__(self)
|
||||
self.conversionAlive = False
|
||||
self.errors = False
|
||||
self.kindlegenErrorCode = [0]
|
||||
@@ -220,52 +228,85 @@ class WorkerThread(QtCore.QThread):
|
||||
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)
|
||||
options.cropping = GUI.croppingBox.checkState()
|
||||
if GUI.croppingBox.checkState() >= 1:
|
||||
options.cropping = GUI.croppingBox.checkState().value
|
||||
if GUI.croppingBox.checkState() != Qt.CheckState.Unchecked:
|
||||
options.croppingp = float(GUI.croppingPowerValue)
|
||||
if GUI.borderBox.checkState() == 1:
|
||||
options.preservemargin = GUI.preserveMarginBox.value()
|
||||
options.interpanelcrop = GUI.interPanelCropBox.checkState().value
|
||||
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.reduceRainbowBox.isChecked():
|
||||
options.reducerainbow = True
|
||||
if GUI.maximizeStrips.isChecked():
|
||||
options.maximizestrips = True
|
||||
if GUI.disableProcessingBox.isChecked():
|
||||
options.noprocessing = True
|
||||
if GUI.comicinfoTitleBox.isChecked():
|
||||
options.comicinfotitle = True
|
||||
if GUI.deleteBox.isChecked():
|
||||
options.delete = True
|
||||
if GUI.mozJpegBox.checkState() == 1:
|
||||
if GUI.spreadShiftBox.isChecked():
|
||||
options.spreadshift = True
|
||||
if GUI.fileFusionBox.isChecked():
|
||||
options.filefusion = True
|
||||
else:
|
||||
options.filefusion = False
|
||||
if GUI.noRotateBox.isChecked():
|
||||
options.norotate = True
|
||||
if GUI.mozJpegBox.checkState() == Qt.CheckState.PartiallyChecked:
|
||||
options.forcepng = True
|
||||
elif GUI.mozJpegBox.checkState() == 2:
|
||||
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
|
||||
if GUI.authorEdit.text():
|
||||
options.author = str(GUI.authorEdit.text())
|
||||
if GUI.chunkSizeCheckBox.isChecked():
|
||||
options.targetsize = int(GUI.chunkSizeBox.value())
|
||||
|
||||
for i in range(GUI.jobList.count()):
|
||||
# Make sure that we don't consider any system message as job to do
|
||||
if GUI.jobList.item(i).icon().isNull():
|
||||
currentJobs.append(str(GUI.jobList.item(i).text()))
|
||||
GUI.jobList.clear()
|
||||
if options.filefusion:
|
||||
bookDir = []
|
||||
MW.addMessage.emit('Attempting file fusion', 'info', False)
|
||||
for job in currentJobs:
|
||||
bookDir.append(job)
|
||||
try:
|
||||
comic2ebook.options = comic2ebook.checkOptions(copy(options))
|
||||
currentJobs.clear()
|
||||
currentJobs.append(comic2ebook.makeFusion(bookDir))
|
||||
MW.addMessage.emit('Created fusion at ' + currentJobs[0], 'info', False)
|
||||
except Exception as e:
|
||||
print('Fusion Failed. ' + str(e))
|
||||
MW.addMessage.emit('Fusion Failed. ' + str(e), 'error', True)
|
||||
for job in currentJobs:
|
||||
sleep(0.5)
|
||||
if not self.conversionAlive:
|
||||
@@ -301,13 +342,8 @@ class WorkerThread(QtCore.QThread):
|
||||
GUI.progress.content = ''
|
||||
self.errors = True
|
||||
_, _, traceback = sys.exc_info()
|
||||
if len(err.args) == 1:
|
||||
MW.showDialog.emit("Error during conversion %s:\n\n%s\n\nTraceback:\n%s"
|
||||
% (jobargv[-1], str(err), sanitizeTrace(traceback)), 'error')
|
||||
else:
|
||||
MW.showDialog.emit("Error during conversion %s:\n\n%s\n\nTraceback:\n%s"
|
||||
% (jobargv[-1], str(err.args[0]), err.args[1]), 'error')
|
||||
GUI.sentry.extra_context({'realTraceback': err.args[1]})
|
||||
MW.showDialog.emit("Error during conversion %s:\n\n%s\n\nTraceback:\n%s"
|
||||
% (jobargv[-1], str(err), sanitizeTrace(traceback)), 'error')
|
||||
if ' is corrupted.' not in str(err):
|
||||
GUI.sentry.captureException()
|
||||
MW.addMessage.emit('Error during conversion! Please consult '
|
||||
@@ -375,7 +411,7 @@ class WorkerThread(QtCore.QThread):
|
||||
except Exception:
|
||||
pass
|
||||
MW.addMessage.emit('Processing MOBI files... <b>Done!</b>', 'info', True)
|
||||
k = kindle.Kindle()
|
||||
k = kindle.Kindle(options.profile)
|
||||
if k.path and k.coverSupport:
|
||||
for item in outputPath:
|
||||
comic2ebook.options.covers[outputPath.index(item)][0].saveToKindle(
|
||||
@@ -407,6 +443,8 @@ class WorkerThread(QtCore.QThread):
|
||||
MW.addMessage.emit('Created EPUB file was too big.', 'error', False)
|
||||
MW.addMessage.emit('EPUB file: ' + str(epubSize) + 'MB. Supported size: ~350MB.', 'error',
|
||||
False)
|
||||
if self.kindlegenErrorCode[0] == 3221226505:
|
||||
MW.addMessage.emit('Unknown Windows error. Possibly filepath too long?', 'error', False)
|
||||
else:
|
||||
for item in outputPath:
|
||||
if GUI.targetDirectory and GUI.targetDirectory != os.path.dirname(item):
|
||||
@@ -414,6 +452,12 @@ class WorkerThread(QtCore.QThread):
|
||||
move(item, GUI.targetDirectory)
|
||||
except Exception:
|
||||
pass
|
||||
if options.filefusion:
|
||||
for path in currentJobs:
|
||||
if os.path.isfile(path):
|
||||
os.remove(path)
|
||||
elif os.path.isdir(path):
|
||||
rmtree(path)
|
||||
GUI.progress.content = ''
|
||||
GUI.progress.stop()
|
||||
MW.hideProgressBar.emit()
|
||||
@@ -424,11 +468,11 @@ class WorkerThread(QtCore.QThread):
|
||||
MW.modeConvert.emit(1)
|
||||
|
||||
|
||||
class SystemTrayIcon(QtWidgets.QSystemTrayIcon):
|
||||
class SystemTrayIcon(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):
|
||||
@@ -437,7 +481,7 @@ class SystemTrayIcon(QtWidgets.QSystemTrayIcon):
|
||||
MW.activateWindow()
|
||||
|
||||
def addTrayMessage(self, message, icon):
|
||||
icon = eval('QtWidgets.QSystemTrayIcon.' + icon)
|
||||
icon = getattr(QSystemTrayIcon.MessageIcon, icon)
|
||||
if self.supportsMessages() and not MW.isActiveWindow():
|
||||
self.showMessage('Kindle Comic Converter', message, icon)
|
||||
|
||||
@@ -447,7 +491,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
if self.needClean:
|
||||
self.needClean = False
|
||||
GUI.jobList.clear()
|
||||
dname = QtWidgets.QFileDialog.getExistingDirectory(MW, 'Select directory', self.lastPath)
|
||||
dname = QFileDialog.getExistingDirectory(MW, 'Select directory', self.lastPath)
|
||||
if dname != '':
|
||||
if sys.platform.startswith('win'):
|
||||
dname = dname.replace('/', '\\')
|
||||
@@ -459,11 +503,11 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
if self.needClean:
|
||||
self.needClean = False
|
||||
GUI.jobList.clear()
|
||||
if self.sevenzip:
|
||||
fnames = QtWidgets.QFileDialog.getOpenFileNames(MW, 'Select file', self.lastPath,
|
||||
if self.tar or self.sevenzip:
|
||||
fnames = QFileDialog.getOpenFileNames(MW, 'Select file', self.lastPath,
|
||||
'Comic (*.cbz *.cbr *.cb7 *.zip *.rar *.7z *.pdf);;All (*.*)')
|
||||
else:
|
||||
fnames = QtWidgets.QFileDialog.getOpenFileNames(MW, 'Select file', self.lastPath,
|
||||
fnames = QFileDialog.getOpenFileNames(MW, 'Select file', self.lastPath,
|
||||
'Comic (*.pdf);;All (*.*)')
|
||||
for fname in fnames[0]:
|
||||
if fname != '':
|
||||
@@ -475,8 +519,8 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
|
||||
def selectFileMetaEditor(self):
|
||||
sname = ''
|
||||
if QtWidgets.QApplication.keyboardModifiers() == QtCore.Qt.ShiftModifier:
|
||||
dname = QtWidgets.QFileDialog.getExistingDirectory(MW, 'Select directory', self.lastPath)
|
||||
if QApplication.keyboardModifiers() == Qt.ShiftModifier:
|
||||
dname = QFileDialog.getExistingDirectory(MW, 'Select directory', self.lastPath)
|
||||
if dname != '':
|
||||
sname = os.path.join(dname, 'ComicInfo.xml')
|
||||
if sys.platform.startswith('win'):
|
||||
@@ -484,11 +528,13 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
self.lastPath = os.path.abspath(sname)
|
||||
else:
|
||||
if self.sevenzip:
|
||||
fname = QtWidgets.QFileDialog.getOpenFileName(MW, 'Select file', self.lastPath,
|
||||
fname = QFileDialog.getOpenFileName(MW, 'Select file', self.lastPath,
|
||||
'Comic (*.cbz *.cbr *.cb7)')
|
||||
else:
|
||||
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('/', '\\')
|
||||
@@ -511,7 +557,11 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
|
||||
def openWiki(self):
|
||||
# noinspection PyCallByClass
|
||||
QtGui.QDesktopServices.openUrl(QtCore.QUrl('https://github.com/ciromattia/kcc/wiki'))
|
||||
QDesktopServices.openUrl(QUrl('https://github.com/ciromattia/kcc/wiki'))
|
||||
|
||||
def openKofi(self):
|
||||
# noinspection PyCallByClass
|
||||
QDesktopServices.openUrl(QUrl('https://ko-fi.com/eink_dude'))
|
||||
|
||||
def modeChange(self, mode):
|
||||
if mode == 1:
|
||||
@@ -546,16 +596,16 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
if enable == 1:
|
||||
self.conversionAlive = False
|
||||
self.worker.sync()
|
||||
icon = QtGui.QIcon()
|
||||
icon.addPixmap(QtGui.QPixmap(":/Other/icons/convert.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||
icon = QIcon()
|
||||
icon.addPixmap(QPixmap(":/Other/icons/convert.png"), QIcon.Mode.Normal, QIcon.State.Off)
|
||||
GUI.convertButton.setIcon(icon)
|
||||
GUI.convertButton.setText('Convert')
|
||||
GUI.centralWidget.setAcceptDrops(True)
|
||||
elif enable == 0:
|
||||
self.conversionAlive = True
|
||||
self.worker.sync()
|
||||
icon = QtGui.QIcon()
|
||||
icon.addPixmap(QtGui.QPixmap(":/Other/icons/clear.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||
icon = QIcon()
|
||||
icon.addPixmap(QPixmap(":/Other/icons/clear.png"), QIcon.Mode.Normal, QIcon.State.Off)
|
||||
GUI.convertButton.setIcon(icon)
|
||||
GUI.convertButton.setText('Abort')
|
||||
GUI.centralWidget.setAcceptDrops(False)
|
||||
@@ -590,6 +640,8 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
GUI.rotateBox.setChecked(False)
|
||||
GUI.upscaleBox.setEnabled(False)
|
||||
GUI.upscaleBox.setChecked(True)
|
||||
GUI.chunkSizeCheckBox.setEnabled(False)
|
||||
GUI.chunkSizeCheckBox.setChecked(False)
|
||||
else:
|
||||
profile = GUI.profiles[str(GUI.deviceBox.currentText())]
|
||||
if profile['PVOptions']:
|
||||
@@ -597,18 +649,23 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
GUI.mangaBox.setEnabled(True)
|
||||
GUI.rotateBox.setEnabled(True)
|
||||
GUI.upscaleBox.setEnabled(True)
|
||||
GUI.chunkSizeCheckBox.setEnabled(True)
|
||||
|
||||
def togglequalityBox(self, value):
|
||||
profile = GUI.profiles[str(GUI.deviceBox.currentText())]
|
||||
if value == 2:
|
||||
if profile['Label'] in ['KV', 'KO']:
|
||||
if profile['Label'] not in ('K57', 'KPW', 'K810') :
|
||||
self.addMessage('This option is intended for older Kindle models.', 'warning')
|
||||
self.addMessage('On this device, quality improvement will be negligible.', 'warning')
|
||||
self.addMessage('On this device, there will be conversion speed and quality issues.', 'warning')
|
||||
self.addMessage('Use the Kindle Scribe profile if you want higher resolution when zooming.', 'warning')
|
||||
GUI.upscaleBox.setEnabled(False)
|
||||
GUI.upscaleBox.setChecked(True)
|
||||
else:
|
||||
GUI.upscaleBox.setEnabled(True)
|
||||
GUI.upscaleBox.setChecked(profile['DefaultUpscale'])
|
||||
|
||||
def togglechunkSizeCheckBox(self, value):
|
||||
GUI.chunkSizeWidget.setVisible(value)
|
||||
|
||||
def changeGamma(self, value):
|
||||
valueRaw = int(5 * round(float(value) / 5))
|
||||
@@ -635,13 +692,17 @@ 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 profile['Label'] == 'KS':
|
||||
GUI.upscaleBox.setDisabled(True)
|
||||
else:
|
||||
GUI.upscaleBox.setEnabled(True)
|
||||
if not profile['PVOptions']:
|
||||
GUI.qualityBox.setChecked(False)
|
||||
if str(GUI.deviceBox.currentText()) == 'Other':
|
||||
@@ -661,6 +722,12 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
else:
|
||||
GUI.outputSplit.setEnabled(False)
|
||||
GUI.outputSplit.setChecked(False)
|
||||
if (GUI.formats[str(GUI.formatBox.currentText())]['format'] == 'EPUB-200MB' or
|
||||
GUI.formats[str(GUI.formatBox.currentText())]['format'] == 'MOBI+EPUB-200MB'):
|
||||
GUI.chunkSizeCheckBox.setEnabled(False)
|
||||
GUI.chunkSizeCheckBox.setChecked(False)
|
||||
elif not GUI.webtoonBox.isChecked():
|
||||
GUI.chunkSizeCheckBox.setEnabled(True)
|
||||
|
||||
def stripTags(self, html):
|
||||
s = HTMLStripper()
|
||||
@@ -669,17 +736,16 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
|
||||
def addMessage(self, message, icon, replace=False):
|
||||
if icon != '':
|
||||
icon = eval('self.icons.' + icon)
|
||||
item = QtWidgets.QListWidgetItem(icon, ' ' + self.stripTags(message))
|
||||
icon = getattr(self.icons, icon)
|
||||
item = QListWidgetItem(icon, ' ' + self.stripTags(message))
|
||||
else:
|
||||
item = QtWidgets.QListWidgetItem(' ' + self.stripTags(message))
|
||||
item = QListWidgetItem(' ' + self.stripTags(message))
|
||||
if replace:
|
||||
GUI.jobList.takeItem(GUI.jobList.count() - 1)
|
||||
# Due to lack of HTML support in QListWidgetItem we overlay text field with QLabel
|
||||
# We still fill original text field with transparent content to trigger creation of horizontal scrollbar
|
||||
item.setForeground(QtGui.QColor('transparent'))
|
||||
label = QtWidgets.QLabel(message)
|
||||
label.setStyleSheet('background-image:url('');background-color:rgba(0,0,0,0);color:rgb(0,0,0);')
|
||||
item.setForeground(QColor('transparent'))
|
||||
label = QLabel(message)
|
||||
label.setOpenExternalLinks(True)
|
||||
GUI.jobList.addItem(item)
|
||||
GUI.jobList.setItemWidget(item, label)
|
||||
@@ -687,11 +753,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)
|
||||
QMessageBox.critical(MW, 'KCC - Error', message, QMessageBox.StandardButton.Ok)
|
||||
elif kind == 'question':
|
||||
GUI.versionCheck.setAnswer(QtWidgets.QMessageBox.question(MW, 'KCC - Question', message,
|
||||
QtWidgets.QMessageBox.Yes,
|
||||
QtWidgets.QMessageBox.No))
|
||||
GUI.versionCheck.setAnswer(QMessageBox.question(MW, 'KCC - Question', message,
|
||||
QMessageBox.Yes,
|
||||
QMessageBox.No))
|
||||
|
||||
def updateProgressbar(self, command):
|
||||
if command == 'tick':
|
||||
@@ -715,8 +781,8 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
self.conversionAlive = False
|
||||
self.worker.sync()
|
||||
else:
|
||||
if QtWidgets.QApplication.keyboardModifiers() == QtCore.Qt.ShiftModifier:
|
||||
dname = QtWidgets.QFileDialog.getExistingDirectory(MW, 'Select output directory', self.lastPath)
|
||||
if QApplication.keyboardModifiers() == Qt.KeyboardModifier.ShiftModifier:
|
||||
dname = QFileDialog.getExistingDirectory(MW, 'Select output directory', self.lastPath)
|
||||
if dname != '':
|
||||
if sys.platform.startswith('win'):
|
||||
dname = dname.replace('/', '\\')
|
||||
@@ -749,7 +815,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
|
||||
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!',
|
||||
'<a href="https://github.com/ciromattia/kcc#kindlegen"><b>Install KindleGen (link)</b></a> to enable MOBI conversion for Kindles!',
|
||||
'error'
|
||||
)
|
||||
|
||||
@@ -768,24 +834,33 @@ 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(),
|
||||
'croppingBox': GUI.croppingBox.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(),
|
||||
'borderBox': GUI.borderBox.checkState(),
|
||||
'webtoonBox': GUI.webtoonBox.checkState(),
|
||||
'outputSplit': GUI.outputSplit.checkState(),
|
||||
'colorBox': GUI.colorBox.checkState(),
|
||||
'disableProcessingBox': GUI.disableProcessingBox.checkState(),
|
||||
'mozJpegBox': GUI.mozJpegBox.checkState(),
|
||||
'preserveMarginBox': self.preserveMarginBox.value(),
|
||||
'interPanelCropBox': GUI.interPanelCropBox.checkState().value,
|
||||
'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,
|
||||
'reduceRainbowBox': GUI.reduceRainbowBox.checkState().value,
|
||||
'disableProcessingBox': GUI.disableProcessingBox.checkState().value,
|
||||
'comicinfoTitleBox': GUI.comicinfoTitleBox.checkState().value,
|
||||
'mozJpegBox': GUI.mozJpegBox.checkState().value,
|
||||
'widthBox': GUI.widthBox.value(),
|
||||
'heightBox': GUI.heightBox.value(),
|
||||
'deleteBox': GUI.deleteBox.checkState(),
|
||||
'maximizeStrips': GUI.maximizeStrips.checkState(),
|
||||
'gammaSlider': float(self.gammaValue) * 100})
|
||||
'deleteBox': GUI.deleteBox.checkState().value,
|
||||
'spreadShiftBox': GUI.spreadShiftBox.checkState().value,
|
||||
'fileFusionBox': GUI.fileFusionBox.checkState().value,
|
||||
'noRotateBox': GUI.noRotateBox.checkState().value,
|
||||
'maximizeStrips': GUI.maximizeStrips.checkState().value,
|
||||
'gammaSlider': float(self.gammaValue) * 100,
|
||||
'chunkSizeCheckBox': GUI.chunkSizeCheckBox.checkState().value,
|
||||
'chunkSizeBox': GUI.chunkSizeBox.value()})
|
||||
self.settings.sync()
|
||||
self.tray.hide()
|
||||
|
||||
@@ -799,7 +874,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
self.needClean = False
|
||||
GUI.jobList.clear()
|
||||
formats = ['.pdf']
|
||||
if self.sevenzip:
|
||||
if self.tar or self.sevenzip:
|
||||
formats.extend(['.cb7', '.7z', '.cbz', '.zip', '.cbr', '.rar'])
|
||||
if os.path.isdir(message):
|
||||
GUI.jobList.addItem(message)
|
||||
@@ -837,12 +912,12 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
versionCheck = subprocess_run_silent(['kindlegen', '-locale', 'en'], stdout=PIPE, stderr=STDOUT, encoding='UTF-8')
|
||||
versionCheck = subprocess_run(['kindlegen', '-locale', 'en'], stdout=PIPE, stderr=STDOUT, encoding='UTF-8', errors='ignore', check=True)
|
||||
self.kindleGen = True
|
||||
for line in versionCheck.stdout.splitlines():
|
||||
if 'Amazon kindlegen' in line:
|
||||
versionCheck = line.split('V')[1].split(' ')[0]
|
||||
if StrictVersion(versionCheck) < StrictVersion('2.9'):
|
||||
if Version(versionCheck) < Version('2.9'):
|
||||
self.addMessage('Your <a href="https://www.amazon.com/b?node=23496309011">KindleGen</a>'
|
||||
' is outdated! MOBI conversion might fail.', 'warning')
|
||||
break
|
||||
@@ -859,7 +934,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
self.setupUi(MW)
|
||||
self.editor = KCCGUI_MetaEditor()
|
||||
self.icons = Icons()
|
||||
self.settings = QtCore.QSettings('KindleComicConverter', 'KindleComicConverter')
|
||||
self.settings = 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)
|
||||
@@ -892,10 +967,10 @@ 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(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)
|
||||
|
||||
@@ -903,101 +978,129 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
"MOBI/AZW3": {'icon': 'MOBI', 'format': 'MOBI'},
|
||||
"EPUB": {'icon': 'EPUB', 'format': 'EPUB'},
|
||||
"CBZ": {'icon': 'CBZ', 'format': 'CBZ'},
|
||||
"EPUB (Calibre KFX)": {'icon': 'EPUB', 'format': 'KFX'},
|
||||
"KFX (does not work)": {'icon': 'KFX', 'format': 'KFX'},
|
||||
"MOBI + EPUB": {'icon': 'MOBI', 'format': 'MOBI+EPUB'},
|
||||
"EPUB (200MB limit)": {'icon': 'EPUB', 'format': 'EPUB-200MB'}
|
||||
"EPUB (200MB limit)": {'icon': 'EPUB', 'format': 'EPUB-200MB'},
|
||||
"MOBI + EPUB (200MB limit)": {'icon': 'MOBI', 'format': 'MOBI+EPUB-200MB'},
|
||||
}
|
||||
|
||||
|
||||
self.profiles = {
|
||||
"Kindle Oasis 2/3": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
|
||||
'DefaultUpscale': True, 'Label': 'KO'},
|
||||
"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 8/10": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
|
||||
'DefaultUpscale': False, 'ForceColor': False, 'Label': 'K810'},
|
||||
"Kindle Oasis 8": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
|
||||
'DefaultUpscale': True, 'ForceColor': False, 'Label': 'KPW34'},
|
||||
"Kindle Voyage": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
|
||||
'DefaultUpscale': True, 'Label': 'KV'},
|
||||
'DefaultUpscale': True, 'ForceColor': False, 'Label': 'KV'},
|
||||
"Kindle Scribe": {
|
||||
'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': False, 'Label': 'KS',
|
||||
'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': False, 'ForceColor': False, 'Label': 'KS',
|
||||
},
|
||||
"Kindle 11": {
|
||||
'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': True, 'Label': 'K11',
|
||||
'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': True, 'ForceColor': False, 'Label': 'K11',
|
||||
},
|
||||
"Kindle PW 5": {
|
||||
'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': True, 'Label': 'KPW5',
|
||||
"Kindle Paperwhite 11": {
|
||||
'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': True, 'ForceColor': False, 'Label': 'KPW5',
|
||||
},
|
||||
"Kindle PW 3/4": {'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 4/5/7/8/10": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
|
||||
'DefaultUpscale': False, 'Label': 'K578'},
|
||||
"Kindle DX/DXG": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 2,
|
||||
'DefaultUpscale': False, 'Label': 'KDX'},
|
||||
"Kindle Paperwhite 12": {
|
||||
'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': True, 'ForceColor': False, 'Label': 'KO',
|
||||
},
|
||||
"Kindle Colorsoft": {
|
||||
'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': True, 'ForceColor': True, 'Label': 'KO',
|
||||
},
|
||||
"Kindle Paperwhite 7/10": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
|
||||
'DefaultUpscale': True, 'ForceColor': False, 'Label': 'KPW34'},
|
||||
"Kindle Paperwhite 5/6": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
|
||||
'DefaultUpscale': False, 'ForceColor': False, 'Label': 'KPW'},
|
||||
"Kindle 4/5/7": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
|
||||
'DefaultUpscale': False, 'ForceColor': False, 'Label': 'K57'},
|
||||
"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'},
|
||||
'DefaultUpscale': True, 'ForceColor': False, 'Label': 'KoAO'},
|
||||
"Kobo Clara HD": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1,
|
||||
'DefaultUpscale': True, 'Label': 'KoC'},
|
||||
'DefaultUpscale': True, 'ForceColor': False, 'Label': 'KoC'},
|
||||
"Kobo Libra H2O": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1,
|
||||
'DefaultUpscale': True, 'Label': 'KoL'},
|
||||
'DefaultUpscale': True, 'ForceColor': False, 'Label': 'KoL'},
|
||||
"Kobo Forma": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1,
|
||||
'DefaultUpscale': True, 'Label': 'KoF'},
|
||||
'DefaultUpscale': True, 'ForceColor': False, 'Label': 'KoF'},
|
||||
"Kindle 1": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 0,
|
||||
'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'},
|
||||
"Kobo Nia": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, 'DefaultUpscale': True,
|
||||
'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,
|
||||
"Kobo Clara 2E": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, 'DefaultUpscale': True, 'ForceColor': False,
|
||||
'Label': 'KoC'},
|
||||
"Kobo Libra 2": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, 'DefaultUpscale': True,
|
||||
"Kobo Clara Colour": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, 'DefaultUpscale': True, 'ForceColor': True,
|
||||
'Label': 'KoCC'},
|
||||
"Kobo Libra 2": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, 'DefaultUpscale': True, 'ForceColor': False,
|
||||
'Label': 'KoL'},
|
||||
"Kobo Sage": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, 'DefaultUpscale': True,
|
||||
"Kobo Libra Colour": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, 'DefaultUpscale': True, 'ForceColor': True,
|
||||
'Label': 'KoLC'},
|
||||
"Kobo Sage": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, 'DefaultUpscale': True, 'ForceColor': False,
|
||||
'Label': 'KoS'},
|
||||
"Kobo Elipsa": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, 'DefaultUpscale': True,
|
||||
"Kobo Elipsa": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, 'DefaultUpscale': True, 'ForceColor': False,
|
||||
'Label': 'KoE'},
|
||||
"Other": {'PVOptions': False, 'ForceExpert': True, 'DefaultFormat': 1, 'DefaultUpscale': False,
|
||||
"reMarkable 1": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, 'DefaultUpscale': True, 'ForceColor': False,
|
||||
'Label': 'Rmk1'},
|
||||
"reMarkable 2": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, 'DefaultUpscale': True, 'ForceColor': False,
|
||||
'Label': 'Rmk2'},
|
||||
"reMarkable Paper Pro": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, 'DefaultUpscale': True, 'ForceColor': True,
|
||||
'Label': 'RmkPP'},
|
||||
"Other": {'PVOptions': False, 'ForceExpert': True, 'DefaultFormat': 1, 'DefaultUpscale': False, 'ForceColor': False,
|
||||
'Label': 'OTHER'},
|
||||
}
|
||||
profilesGUI = [
|
||||
"Kindle Oasis 2/3",
|
||||
"Kindle PW 5",
|
||||
"Kindle 11",
|
||||
"Kindle Colorsoft",
|
||||
"Kindle Paperwhite 12",
|
||||
"Kindle Scribe",
|
||||
"Kindle Paperwhite 11",
|
||||
"Kindle 11",
|
||||
"Kindle Oasis 9/10",
|
||||
"Separator",
|
||||
"Kobo Clara 2E",
|
||||
"Kobo Clara Colour",
|
||||
"Kobo Sage",
|
||||
"Kobo Libra 2",
|
||||
"Kobo Libra Colour",
|
||||
"Kobo Elipsa",
|
||||
"Kobo Nia",
|
||||
"Separator",
|
||||
"reMarkable 1",
|
||||
"reMarkable 2",
|
||||
"reMarkable Paper Pro",
|
||||
"Separator",
|
||||
"Other",
|
||||
"Separator",
|
||||
"Kindle Oasis",
|
||||
"Kindle 8/10",
|
||||
"Kindle Oasis 8",
|
||||
"Kindle Paperwhite 7/10",
|
||||
"Kindle Voyage",
|
||||
"Kindle Paperwhite 5/6",
|
||||
"Kindle 4/5/7",
|
||||
"Kindle Touch",
|
||||
"Kindle Keyboard",
|
||||
"Kindle DX/DXG",
|
||||
"Kindle PW 3/4",
|
||||
"Kindle PW 1/2",
|
||||
"Kindle Voyage",
|
||||
"Kindle DX",
|
||||
"Kindle 2",
|
||||
"Kindle 1",
|
||||
"Kindle 4/5/7/8/10",
|
||||
"Separator",
|
||||
"Kobo Aura",
|
||||
"Kobo Aura ONE",
|
||||
@@ -1011,25 +1114,34 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
"Kobo Mini/Touch",
|
||||
]
|
||||
|
||||
statusBarLabel = QtWidgets.QLabel('<b><a href="https://kcc.iosphe.re/">HOMEPAGE</a> - <a href="https://github.'
|
||||
'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)
|
||||
link_dict = {
|
||||
'README': "https://github.com/ciromattia/kcc?tab=readme-ov-file#kcc",
|
||||
'FAQ': "https://github.com/ciromattia/kcc/blob/master/README.md#faq",
|
||||
'YOUTUBE': "https://youtu.be/IR2Fhcm9658?si=Z-2zzLaUFjmaEbrj",
|
||||
'COMMISSIONS': "https://github.com/ciromattia/kcc?tab=readme-ov-file#commissions",
|
||||
'DONATE': "https://github.com/ciromattia/kcc/blob/master/README.md#issues--new-features--donations",
|
||||
'FORUM': "http://www.mobileread.com/forums/showthread.php?t=207461",
|
||||
'DISCORD': "https://discord.com/invite/qj7wpnUHav",
|
||||
}
|
||||
|
||||
link_html_list = [f'<a href="{v}">{k}</a>' for k, v in link_dict.items()]
|
||||
statusBarLabel = QLabel(f'<b>{" - ".join(link_html_list)}</b>')
|
||||
statusBarLabel.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
statusBarLabel.setOpenExternalLinks(True)
|
||||
GUI.statusBar.addPermanentWidget(statusBarLabel, 1)
|
||||
|
||||
self.addMessage('<b>Welcome!</b>', 'info')
|
||||
self.addMessage('<b>Remember:</b> All options have additional information in tooltips.', 'info')
|
||||
self.addMessage('<b>Tip:</b> Hover mouse over options to see additional information in tooltips.', 'info')
|
||||
self.addMessage('<b>Tip:</b> You can drag and drop image folders or comic files/archives into this window to convert.', 'info')
|
||||
self.addMessage('<b>Tip:</b> Shift clicking the Convert button lets you select a custom output directory', 'info')
|
||||
if self.startNumber < 5:
|
||||
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')
|
||||
try:
|
||||
subprocess_run_silent(['7z'], stdout=PIPE, stderr=STDOUT)
|
||||
self.sevenzip = True
|
||||
except FileNotFoundError:
|
||||
self.sevenzip = False
|
||||
|
||||
self.tar = 'tar' in available_archive_tools()
|
||||
self.sevenzip = '7z' in available_archive_tools()
|
||||
if not any([self.tar, self.sevenzip]):
|
||||
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)
|
||||
@@ -1040,6 +1152,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
GUI.fileButton.clicked.connect(self.selectFile)
|
||||
GUI.editorButton.clicked.connect(self.selectFileMetaEditor)
|
||||
GUI.wikiButton.clicked.connect(self.openWiki)
|
||||
GUI.kofiButton.clicked.connect(self.openKofi)
|
||||
GUI.convertButton.clicked.connect(self.convertStart)
|
||||
GUI.gammaSlider.valueChanged.connect(self.changeGamma)
|
||||
GUI.gammaBox.stateChanged.connect(self.togglegammaBox)
|
||||
@@ -1047,6 +1160,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
GUI.croppingPowerSlider.valueChanged.connect(self.changeCroppingPower)
|
||||
GUI.webtoonBox.stateChanged.connect(self.togglewebtoonBox)
|
||||
GUI.qualityBox.stateChanged.connect(self.togglequalityBox)
|
||||
GUI.chunkSizeCheckBox.stateChanged.connect(self.togglechunkSizeCheckBox)
|
||||
GUI.deviceBox.activated.connect(self.changeDevice)
|
||||
GUI.formatBox.activated.connect(self.changeFormat)
|
||||
MW.progressBarTick.connect(self.updateProgressbar)
|
||||
@@ -1068,12 +1182,14 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
GUI.deviceBox.addItem(self.icons.deviceOther, profile)
|
||||
elif profile == "Separator":
|
||||
GUI.deviceBox.insertSeparator(GUI.deviceBox.count() + 1)
|
||||
elif 'reM' in profile:
|
||||
GUI.deviceBox.addItem(self.icons.deviceRmk, profile)
|
||||
elif 'Ko' in profile:
|
||||
GUI.deviceBox.addItem(self.icons.deviceKobo, profile)
|
||||
else:
|
||||
GUI.deviceBox.addItem(self.icons.deviceKindle, profile)
|
||||
for f in self.formats:
|
||||
GUI.formatBox.addItem(eval('self.icons.' + self.formats[f]['icon'] + 'Format'), f)
|
||||
GUI.formatBox.addItem(getattr(self.icons, self.formats[f]['icon'] + 'Format'), f)
|
||||
if self.lastDevice > GUI.deviceBox.count():
|
||||
self.lastDevice = 0
|
||||
if profilesGUI[self.lastDevice] == "Separator":
|
||||
@@ -1097,10 +1213,13 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
if GUI.croppingPowerSlider.isEnabled():
|
||||
GUI.croppingPowerSlider.setValue(int(self.options[option]))
|
||||
self.changeCroppingPower(int(self.options[option]))
|
||||
GUI.preserveMarginBox.setValue(self.options.get('preserveMarginBox', 0))
|
||||
elif str(option) == "chunkSizeBox":
|
||||
GUI.chunkSizeBox.setValue(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()
|
||||
@@ -1171,15 +1290,15 @@ class KCCGUI_MetaEditor(KCC_ui_editor.Ui_editorDialog):
|
||||
return escape(s.strip())
|
||||
|
||||
def __init__(self):
|
||||
self.ui = QtWidgets.QDialog()
|
||||
self.ui = QDialog()
|
||||
self.parser = None
|
||||
self.setupUi(self.ui)
|
||||
self.ui.setWindowFlags(self.ui.windowFlags() & ~QtCore.Qt.WindowContextHelpButtonHint)
|
||||
self.ui.setWindowFlags(self.ui.windowFlags() & ~Qt.WindowType.WindowContextHelpButtonHint)
|
||||
self.okButton.clicked.connect(self.saveData)
|
||||
self.cancelButton.clicked.connect(self.ui.close)
|
||||
if sys.platform.startswith('linux'):
|
||||
self.ui.resize(450, 260)
|
||||
self.ui.setMinimumSize(QtCore.QSize(450, 260))
|
||||
self.ui.setMinimumSize(QSize(450, 260))
|
||||
elif sys.platform.startswith('darwin'):
|
||||
self.ui.resize(450, 310)
|
||||
self.ui.setMinimumSize(QtCore.QSize(450, 310))
|
||||
self.ui.setMinimumSize(QSize(450, 310))
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,321 +1,614 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Form implementation generated from reading ui file 'gui/KCC.ui'
|
||||
#
|
||||
# Created by: PyQt5 UI code generator 5.15.7
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
|
||||
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
################################################################################
|
||||
## Form generated from reading UI file 'KCC.ui'
|
||||
##
|
||||
## Created by: Qt User Interface Compiler version 6.9.1
|
||||
##
|
||||
## WARNING! All changes made in this file will be lost when recompiling UI file!
|
||||
################################################################################
|
||||
|
||||
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, QLineEdit,
|
||||
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")
|
||||
mainWindow.resize(450, 400)
|
||||
icon = QtGui.QIcon()
|
||||
icon.addPixmap(QtGui.QPixmap(":/Icon/icons/comic2ebook.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||
if not mainWindow.objectName():
|
||||
mainWindow.setObjectName(u"mainWindow")
|
||||
mainWindow.resize(519, 572)
|
||||
icon = QIcon()
|
||||
icon.addFile(u":/Icon/icons/comic2ebook.png", QSize(), QIcon.Mode.Normal, QIcon.State.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.optionWidget = QtWidgets.QWidget(self.centralWidget)
|
||||
self.optionWidget.setObjectName("optionWidget")
|
||||
self.gridLayout_2 = QtWidgets.QGridLayout(self.optionWidget)
|
||||
self.gridLayout_2.setContentsMargins(0, 0, 0, 0)
|
||||
self.gridLayout_2.setObjectName("gridLayout_2")
|
||||
self.upscaleBox = QtWidgets.QCheckBox(self.optionWidget)
|
||||
self.upscaleBox.setTristate(True)
|
||||
self.upscaleBox.setObjectName("upscaleBox")
|
||||
self.gridLayout_2.addWidget(self.upscaleBox, 1, 1, 1, 1)
|
||||
self.rotateBox = QtWidgets.QCheckBox(self.optionWidget)
|
||||
self.rotateBox.setTristate(True)
|
||||
self.rotateBox.setObjectName("rotateBox")
|
||||
self.gridLayout_2.addWidget(self.rotateBox, 0, 1, 1, 1)
|
||||
self.outputSplit = QtWidgets.QCheckBox(self.optionWidget)
|
||||
self.outputSplit.setObjectName("outputSplit")
|
||||
self.gridLayout_2.addWidget(self.outputSplit, 2, 1, 1, 1)
|
||||
self.webtoonBox = QtWidgets.QCheckBox(self.optionWidget)
|
||||
self.webtoonBox.setObjectName("webtoonBox")
|
||||
self.gridLayout_2.addWidget(self.webtoonBox, 1, 0, 1, 1)
|
||||
self.colorBox = QtWidgets.QCheckBox(self.optionWidget)
|
||||
self.colorBox.setObjectName("colorBox")
|
||||
self.gridLayout_2.addWidget(self.colorBox, 2, 2, 1, 1)
|
||||
self.gammaBox = QtWidgets.QCheckBox(self.optionWidget)
|
||||
self.gammaBox.setObjectName("gammaBox")
|
||||
self.gridLayout_2.addWidget(self.gammaBox, 1, 2, 1, 1)
|
||||
self.borderBox = QtWidgets.QCheckBox(self.optionWidget)
|
||||
self.borderBox.setTristate(True)
|
||||
self.borderBox.setObjectName("borderBox")
|
||||
self.gridLayout_2.addWidget(self.borderBox, 2, 0, 1, 1)
|
||||
self.mangaBox = QtWidgets.QCheckBox(self.optionWidget)
|
||||
self.mangaBox.setObjectName("mangaBox")
|
||||
self.gridLayout_2.addWidget(self.mangaBox, 0, 0, 1, 1)
|
||||
self.qualityBox = QtWidgets.QCheckBox(self.optionWidget)
|
||||
self.qualityBox.setTristate(True)
|
||||
self.qualityBox.setObjectName("qualityBox")
|
||||
self.gridLayout_2.addWidget(self.qualityBox, 0, 2, 1, 1)
|
||||
self.mozJpegBox = QtWidgets.QCheckBox(self.optionWidget)
|
||||
self.mozJpegBox.setTristate(True)
|
||||
self.mozJpegBox.setObjectName("mozJpegBox")
|
||||
self.gridLayout_2.addWidget(self.mozJpegBox, 3, 0, 1, 1)
|
||||
self.maximizeStrips = QtWidgets.QCheckBox(self.optionWidget)
|
||||
self.maximizeStrips.setObjectName("maximizeStrips")
|
||||
self.gridLayout_2.addWidget(self.maximizeStrips, 3, 1, 1, 1)
|
||||
self.croppingBox = QtWidgets.QCheckBox(self.optionWidget)
|
||||
self.croppingBox.setTristate(True)
|
||||
self.croppingBox.setObjectName("croppingBox")
|
||||
self.gridLayout_2.addWidget(self.croppingBox, 3, 2, 1, 1)
|
||||
self.deleteBox = QtWidgets.QCheckBox(self.optionWidget)
|
||||
self.deleteBox.setObjectName("deleteBox")
|
||||
self.gridLayout_2.addWidget(self.deleteBox, 4, 1, 1, 1)
|
||||
self.disableProcessingBox = QtWidgets.QCheckBox(self.optionWidget)
|
||||
self.disableProcessingBox.setObjectName("disableProcessingBox")
|
||||
self.gridLayout_2.addWidget(self.disableProcessingBox, 4, 2, 1, 1)
|
||||
self.gridLayout.addWidget(self.optionWidget, 5, 0, 1, 2)
|
||||
self.gammaWidget = QtWidgets.QWidget(self.centralWidget)
|
||||
self.gammaWidget.setVisible(False)
|
||||
self.gammaWidget.setObjectName("gammaWidget")
|
||||
self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.gammaWidget)
|
||||
self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0)
|
||||
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
|
||||
self.gammaLabel = QtWidgets.QLabel(self.gammaWidget)
|
||||
self.gammaLabel.setObjectName("gammaLabel")
|
||||
self.horizontalLayout_2.addWidget(self.gammaLabel)
|
||||
self.gammaSlider = QtWidgets.QSlider(self.gammaWidget)
|
||||
self.gammaSlider.setMaximum(250)
|
||||
self.gammaSlider.setSingleStep(5)
|
||||
self.gammaSlider.setOrientation(QtCore.Qt.Horizontal)
|
||||
self.gammaSlider.setObjectName("gammaSlider")
|
||||
self.horizontalLayout_2.addWidget(self.gammaSlider)
|
||||
self.gridLayout.addWidget(self.gammaWidget, 6, 0, 1, 2)
|
||||
self.croppingWidget = QtWidgets.QWidget(self.centralWidget)
|
||||
self.croppingWidget.setVisible(False)
|
||||
self.croppingWidget.setObjectName("croppingWidget")
|
||||
self.horizontalLayout_3 = QtWidgets.QHBoxLayout(self.croppingWidget)
|
||||
self.horizontalLayout_3.setContentsMargins(0, 0, 0, 0)
|
||||
self.horizontalLayout_3.setObjectName("horizontalLayout_3")
|
||||
self.croppingPowerLabel = QtWidgets.QLabel(self.croppingWidget)
|
||||
self.croppingPowerLabel.setObjectName("croppingPowerLabel")
|
||||
self.horizontalLayout_3.addWidget(self.croppingPowerLabel)
|
||||
self.croppingPowerSlider = QtWidgets.QSlider(self.croppingWidget)
|
||||
self.croppingPowerSlider.setMaximum(200)
|
||||
self.croppingPowerSlider.setSingleStep(1)
|
||||
self.croppingPowerSlider.setOrientation(QtCore.Qt.Horizontal)
|
||||
self.croppingPowerSlider.setObjectName("croppingPowerSlider")
|
||||
self.horizontalLayout_3.addWidget(self.croppingPowerSlider)
|
||||
self.gridLayout.addWidget(self.croppingWidget, 8, 0, 1, 2)
|
||||
self.buttonWidget = QtWidgets.QWidget(self.centralWidget)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
|
||||
self.jobList = QListWidget(self.centralWidget)
|
||||
self.jobList.setObjectName(u"jobList")
|
||||
self.jobList.setStyleSheet(u"")
|
||||
self.jobList.setSelectionMode(QAbstractItemView.SelectionMode.NoSelection)
|
||||
self.jobList.setVerticalScrollMode(QAbstractItemView.ScrollMode.ScrollPerPixel)
|
||||
self.jobList.setHorizontalScrollMode(QAbstractItemView.ScrollMode.ScrollPerPixel)
|
||||
|
||||
self.gridLayout.addWidget(self.jobList, 2, 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))
|
||||
icon1 = QIcon()
|
||||
icon1.addFile(u":/Other/icons/editor.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
|
||||
self.editorButton.setIcon(icon1)
|
||||
|
||||
self.horizontalLayout.addWidget(self.editorButton)
|
||||
|
||||
self.kofiButton = QPushButton(self.toolWidget)
|
||||
self.kofiButton.setObjectName(u"kofiButton")
|
||||
self.kofiButton.setMinimumSize(QSize(0, 30))
|
||||
icon2 = QIcon()
|
||||
icon2.addFile(u":/Other/icons/kofi_symbol.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
|
||||
self.kofiButton.setIcon(icon2)
|
||||
self.kofiButton.setIconSize(QSize(19, 16))
|
||||
|
||||
self.horizontalLayout.addWidget(self.kofiButton)
|
||||
|
||||
self.wikiButton = QPushButton(self.toolWidget)
|
||||
self.wikiButton.setObjectName(u"wikiButton")
|
||||
self.wikiButton.setMinimumSize(QSize(0, 30))
|
||||
icon3 = QIcon()
|
||||
icon3.addFile(u":/Other/icons/wiki.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
|
||||
self.wikiButton.setIcon(icon3)
|
||||
|
||||
self.horizontalLayout.addWidget(self.wikiButton)
|
||||
|
||||
|
||||
self.gridLayout.addWidget(self.toolWidget, 0, 0, 1, 2)
|
||||
|
||||
self.buttonWidget = QWidget(self.centralWidget)
|
||||
self.buttonWidget.setObjectName(u"buttonWidget")
|
||||
sizePolicy = QSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.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))
|
||||
icon1 = QtGui.QIcon()
|
||||
icon1.addPixmap(QtGui.QPixmap(":/Other/icons/folder_new.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||
self.directoryButton.setIcon(icon1)
|
||||
self.directoryButton.setObjectName("directoryButton")
|
||||
self.directoryButton = QPushButton(self.buttonWidget)
|
||||
self.directoryButton.setObjectName(u"directoryButton")
|
||||
self.directoryButton.setMinimumSize(QSize(0, 30))
|
||||
icon4 = QIcon()
|
||||
icon4.addFile(u":/Other/icons/folder_new.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
|
||||
self.directoryButton.setIcon(icon4)
|
||||
|
||||
self.gridLayout_4.addWidget(self.directoryButton, 0, 0, 1, 1)
|
||||
self.fileButton = QtWidgets.QPushButton(self.buttonWidget)
|
||||
self.fileButton.setMinimumSize(QtCore.QSize(0, 30))
|
||||
icon2 = QtGui.QIcon()
|
||||
icon2.addPixmap(QtGui.QPixmap(":/Other/icons/document_new.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||
self.fileButton.setIcon(icon2)
|
||||
self.fileButton.setObjectName("fileButton")
|
||||
|
||||
self.fileButton = QPushButton(self.buttonWidget)
|
||||
self.fileButton.setObjectName(u"fileButton")
|
||||
self.fileButton.setMinimumSize(QSize(0, 30))
|
||||
icon5 = QIcon()
|
||||
icon5.addFile(u":/Other/icons/document_new.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
|
||||
self.fileButton.setIcon(icon5)
|
||||
|
||||
self.gridLayout_4.addWidget(self.fileButton, 0, 3, 1, 1)
|
||||
self.deviceBox = QtWidgets.QComboBox(self.buttonWidget)
|
||||
self.deviceBox.setMinimumSize(QtCore.QSize(0, 28))
|
||||
self.deviceBox.setObjectName("deviceBox")
|
||||
|
||||
self.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.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)
|
||||
self.convertButton.setFont(font)
|
||||
icon3 = QtGui.QIcon()
|
||||
icon3.addPixmap(QtGui.QPixmap(":/Other/icons/convert.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||
self.convertButton.setIcon(icon3)
|
||||
self.convertButton.setObjectName("convertButton")
|
||||
icon6 = QIcon()
|
||||
icon6.addFile(u":/Other/icons/convert.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
|
||||
self.convertButton.setIcon(icon6)
|
||||
|
||||
self.gridLayout_4.addWidget(self.convertButton, 1, 2, 1, 1)
|
||||
self.clearButton = QtWidgets.QPushButton(self.buttonWidget)
|
||||
self.clearButton.setMinimumSize(QtCore.QSize(0, 30))
|
||||
icon4 = QtGui.QIcon()
|
||||
icon4.addPixmap(QtGui.QPixmap(":/Other/icons/clear.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||
self.clearButton.setIcon(icon4)
|
||||
self.clearButton.setObjectName("clearButton")
|
||||
|
||||
self.clearButton = QPushButton(self.buttonWidget)
|
||||
self.clearButton.setObjectName(u"clearButton")
|
||||
self.clearButton.setMinimumSize(QSize(0, 30))
|
||||
icon7 = QIcon()
|
||||
icon7.addFile(u":/Other/icons/clear.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
|
||||
self.clearButton.setIcon(icon7)
|
||||
|
||||
self.gridLayout_4.addWidget(self.clearButton, 0, 2, 1, 1)
|
||||
|
||||
self.directoryButton.raise_()
|
||||
self.clearButton.raise_()
|
||||
self.fileButton.raise_()
|
||||
self.deviceBox.raise_()
|
||||
self.convertButton.raise_()
|
||||
self.formatBox.raise_()
|
||||
|
||||
self.gridLayout.addWidget(self.buttonWidget, 3, 0, 1, 2)
|
||||
self.toolWidget = QtWidgets.QWidget(self.centralWidget)
|
||||
self.toolWidget.setObjectName("toolWidget")
|
||||
self.horizontalLayout = QtWidgets.QHBoxLayout(self.toolWidget)
|
||||
self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
|
||||
self.horizontalLayout.setObjectName("horizontalLayout")
|
||||
self.editorButton = QtWidgets.QPushButton(self.toolWidget)
|
||||
self.editorButton.setMinimumSize(QtCore.QSize(0, 30))
|
||||
icon5 = QtGui.QIcon()
|
||||
icon5.addPixmap(QtGui.QPixmap(":/Other/icons/editor.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||
self.editorButton.setIcon(icon5)
|
||||
self.editorButton.setObjectName("editorButton")
|
||||
self.horizontalLayout.addWidget(self.editorButton)
|
||||
self.wikiButton = QtWidgets.QPushButton(self.toolWidget)
|
||||
self.wikiButton.setMinimumSize(QtCore.QSize(0, 30))
|
||||
icon6 = QtGui.QIcon()
|
||||
icon6.addPixmap(QtGui.QPixmap(":/Other/icons/wiki.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||
self.wikiButton.setIcon(icon6)
|
||||
self.wikiButton.setObjectName("wikiButton")
|
||||
self.horizontalLayout.addWidget(self.wikiButton)
|
||||
self.gridLayout.addWidget(self.toolWidget, 0, 0, 1, 2)
|
||||
self.jobList = QtWidgets.QListWidget(self.centralWidget)
|
||||
self.jobList.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.progressBar = QtWidgets.QProgressBar(self.centralWidget)
|
||||
self.progressBar.setMinimumSize(QtCore.QSize(0, 30))
|
||||
font = QtGui.QFont()
|
||||
font.setBold(True)
|
||||
|
||||
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(QtCore.Qt.AlignJustify|QtCore.Qt.AlignVCenter)
|
||||
self.progressBar.setObjectName("progressBar")
|
||||
self.progressBar.setAlignment(Qt.AlignmentFlag.AlignJustify|Qt.AlignmentFlag.AlignVCenter)
|
||||
|
||||
self.gridLayout.addWidget(self.progressBar, 1, 0, 1, 2)
|
||||
self.customWidget = QtWidgets.QWidget(self.centralWidget)
|
||||
|
||||
self.customWidget = QWidget(self.centralWidget)
|
||||
self.customWidget.setObjectName(u"customWidget")
|
||||
self.customWidget.setVisible(False)
|
||||
self.customWidget.setObjectName("customWidget")
|
||||
self.gridLayout_3 = QtWidgets.QGridLayout(self.customWidget)
|
||||
self.gridLayout_3 = QGridLayout(self.customWidget)
|
||||
self.gridLayout_3.setObjectName(u"gridLayout_3")
|
||||
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.hLabel = QLabel(self.customWidget)
|
||||
self.hLabel.setObjectName(u"hLabel")
|
||||
sizePolicy1 = QSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.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 = QtWidgets.QSpinBox(self.customWidget)
|
||||
self.widthBox.setMaximum(2160)
|
||||
self.widthBox.setObjectName("widthBox")
|
||||
|
||||
self.widthBox = QSpinBox(self.customWidget)
|
||||
self.widthBox.setObjectName(u"widthBox")
|
||||
self.widthBox.setMaximum(2400)
|
||||
|
||||
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.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 = QtWidgets.QSpinBox(self.customWidget)
|
||||
|
||||
self.heightBox = QSpinBox(self.customWidget)
|
||||
self.heightBox.setObjectName(u"heightBox")
|
||||
self.heightBox.setMaximum(3840)
|
||||
self.heightBox.setObjectName("heightBox")
|
||||
|
||||
self.gridLayout_3.addWidget(self.heightBox, 0, 3, 1, 1)
|
||||
self.gridLayout.addWidget(self.customWidget, 7, 0, 1, 2)
|
||||
|
||||
|
||||
self.gridLayout.addWidget(self.customWidget, 8, 0, 1, 2)
|
||||
|
||||
self.croppingWidget = QWidget(self.centralWidget)
|
||||
self.croppingWidget.setObjectName(u"croppingWidget")
|
||||
self.croppingWidget.setVisible(False)
|
||||
self.gridLayout_5 = QGridLayout(self.croppingWidget)
|
||||
self.gridLayout_5.setObjectName(u"gridLayout_5")
|
||||
self.gridLayout_5.setContentsMargins(0, 0, 0, 0)
|
||||
self.preserveMarginLabel = QLabel(self.croppingWidget)
|
||||
self.preserveMarginLabel.setObjectName(u"preserveMarginLabel")
|
||||
|
||||
self.gridLayout_5.addWidget(self.preserveMarginLabel, 1, 0, 1, 1)
|
||||
|
||||
self.croppingPowerLabel = QLabel(self.croppingWidget)
|
||||
self.croppingPowerLabel.setObjectName(u"croppingPowerLabel")
|
||||
|
||||
self.gridLayout_5.addWidget(self.croppingPowerLabel, 0, 0, 1, 1)
|
||||
|
||||
self.croppingPowerSlider = QSlider(self.croppingWidget)
|
||||
self.croppingPowerSlider.setObjectName(u"croppingPowerSlider")
|
||||
self.croppingPowerSlider.setMaximum(300)
|
||||
self.croppingPowerSlider.setSingleStep(1)
|
||||
self.croppingPowerSlider.setOrientation(Qt.Orientation.Horizontal)
|
||||
|
||||
self.gridLayout_5.addWidget(self.croppingPowerSlider, 0, 1, 1, 1)
|
||||
|
||||
self.preserveMarginBox = QSpinBox(self.croppingWidget)
|
||||
self.preserveMarginBox.setObjectName(u"preserveMarginBox")
|
||||
sizePolicy2 = QSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed)
|
||||
sizePolicy2.setHorizontalStretch(0)
|
||||
sizePolicy2.setVerticalStretch(0)
|
||||
sizePolicy2.setHeightForWidth(self.preserveMarginBox.sizePolicy().hasHeightForWidth())
|
||||
self.preserveMarginBox.setSizePolicy(sizePolicy2)
|
||||
self.preserveMarginBox.setMaximum(99)
|
||||
self.preserveMarginBox.setSingleStep(5)
|
||||
self.preserveMarginBox.setValue(0)
|
||||
|
||||
self.gridLayout_5.addWidget(self.preserveMarginBox, 1, 1, 1, 1)
|
||||
|
||||
|
||||
self.gridLayout.addWidget(self.croppingWidget, 9, 0, 1, 2)
|
||||
|
||||
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.croppingBox = QCheckBox(self.optionWidget)
|
||||
self.croppingBox.setObjectName(u"croppingBox")
|
||||
self.croppingBox.setTristate(True)
|
||||
|
||||
self.gridLayout_2.addWidget(self.croppingBox, 4, 2, 1, 1)
|
||||
|
||||
self.mangaBox = QCheckBox(self.optionWidget)
|
||||
self.mangaBox.setObjectName(u"mangaBox")
|
||||
|
||||
self.gridLayout_2.addWidget(self.mangaBox, 1, 0, 1, 1)
|
||||
|
||||
self.webtoonBox = QCheckBox(self.optionWidget)
|
||||
self.webtoonBox.setObjectName(u"webtoonBox")
|
||||
|
||||
self.gridLayout_2.addWidget(self.webtoonBox, 2, 0, 1, 1)
|
||||
|
||||
self.rotateBox = QCheckBox(self.optionWidget)
|
||||
self.rotateBox.setObjectName(u"rotateBox")
|
||||
self.rotateBox.setTristate(True)
|
||||
|
||||
self.gridLayout_2.addWidget(self.rotateBox, 1, 1, 1, 1)
|
||||
|
||||
self.borderBox = QCheckBox(self.optionWidget)
|
||||
self.borderBox.setObjectName(u"borderBox")
|
||||
self.borderBox.setTristate(True)
|
||||
|
||||
self.gridLayout_2.addWidget(self.borderBox, 3, 0, 1, 1)
|
||||
|
||||
self.gammaBox = QCheckBox(self.optionWidget)
|
||||
self.gammaBox.setObjectName(u"gammaBox")
|
||||
|
||||
self.gridLayout_2.addWidget(self.gammaBox, 2, 2, 1, 1)
|
||||
|
||||
self.interPanelCropBox = QCheckBox(self.optionWidget)
|
||||
self.interPanelCropBox.setObjectName(u"interPanelCropBox")
|
||||
self.interPanelCropBox.setTristate(True)
|
||||
|
||||
self.gridLayout_2.addWidget(self.interPanelCropBox, 6, 2, 1, 1)
|
||||
|
||||
self.colorBox = QCheckBox(self.optionWidget)
|
||||
self.colorBox.setObjectName(u"colorBox")
|
||||
|
||||
self.gridLayout_2.addWidget(self.colorBox, 3, 2, 1, 1)
|
||||
|
||||
self.qualityBox = QCheckBox(self.optionWidget)
|
||||
self.qualityBox.setObjectName(u"qualityBox")
|
||||
self.qualityBox.setTristate(True)
|
||||
|
||||
self.gridLayout_2.addWidget(self.qualityBox, 1, 2, 1, 1)
|
||||
|
||||
self.disableProcessingBox = QCheckBox(self.optionWidget)
|
||||
self.disableProcessingBox.setObjectName(u"disableProcessingBox")
|
||||
|
||||
self.gridLayout_2.addWidget(self.disableProcessingBox, 5, 2, 1, 1)
|
||||
|
||||
self.maximizeStrips = QCheckBox(self.optionWidget)
|
||||
self.maximizeStrips.setObjectName(u"maximizeStrips")
|
||||
|
||||
self.gridLayout_2.addWidget(self.maximizeStrips, 4, 1, 1, 1)
|
||||
|
||||
self.authorEdit = QLineEdit(self.optionWidget)
|
||||
self.authorEdit.setObjectName(u"authorEdit")
|
||||
sizePolicy3 = QSizePolicy(QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Fixed)
|
||||
sizePolicy3.setHorizontalStretch(0)
|
||||
sizePolicy3.setVerticalStretch(0)
|
||||
sizePolicy3.setHeightForWidth(self.authorEdit.sizePolicy().hasHeightForWidth())
|
||||
self.authorEdit.setSizePolicy(sizePolicy3)
|
||||
self.authorEdit.setFocusPolicy(Qt.FocusPolicy.ClickFocus)
|
||||
self.authorEdit.setClearButtonEnabled(False)
|
||||
|
||||
self.gridLayout_2.addWidget(self.authorEdit, 0, 0, 1, 1)
|
||||
|
||||
self.deleteBox = QCheckBox(self.optionWidget)
|
||||
self.deleteBox.setObjectName(u"deleteBox")
|
||||
|
||||
self.gridLayout_2.addWidget(self.deleteBox, 5, 1, 1, 1)
|
||||
|
||||
self.mozJpegBox = QCheckBox(self.optionWidget)
|
||||
self.mozJpegBox.setObjectName(u"mozJpegBox")
|
||||
self.mozJpegBox.setTristate(True)
|
||||
|
||||
self.gridLayout_2.addWidget(self.mozJpegBox, 4, 0, 1, 1)
|
||||
|
||||
self.spreadShiftBox = QCheckBox(self.optionWidget)
|
||||
self.spreadShiftBox.setObjectName(u"spreadShiftBox")
|
||||
|
||||
self.gridLayout_2.addWidget(self.spreadShiftBox, 5, 0, 1, 1)
|
||||
|
||||
self.fileFusionBox = QCheckBox(self.optionWidget)
|
||||
self.fileFusionBox.setObjectName(u"fileFusionBox")
|
||||
|
||||
self.gridLayout_2.addWidget(self.fileFusionBox, 6, 0, 1, 1)
|
||||
|
||||
self.upscaleBox = QCheckBox(self.optionWidget)
|
||||
self.upscaleBox.setObjectName(u"upscaleBox")
|
||||
self.upscaleBox.setTristate(True)
|
||||
|
||||
self.gridLayout_2.addWidget(self.upscaleBox, 2, 1, 1, 1)
|
||||
|
||||
self.outputSplit = QCheckBox(self.optionWidget)
|
||||
self.outputSplit.setObjectName(u"outputSplit")
|
||||
|
||||
self.gridLayout_2.addWidget(self.outputSplit, 3, 1, 1, 1)
|
||||
|
||||
self.noRotateBox = QCheckBox(self.optionWidget)
|
||||
self.noRotateBox.setObjectName(u"noRotateBox")
|
||||
|
||||
self.gridLayout_2.addWidget(self.noRotateBox, 6, 1, 1, 1)
|
||||
|
||||
self.reduceRainbowBox = QCheckBox(self.optionWidget)
|
||||
self.reduceRainbowBox.setObjectName(u"reduceRainbowBox")
|
||||
|
||||
self.gridLayout_2.addWidget(self.reduceRainbowBox, 7, 2, 1, 1)
|
||||
|
||||
self.chunkSizeCheckBox = QCheckBox(self.optionWidget)
|
||||
self.chunkSizeCheckBox.setObjectName(u"chunkSizeCheckBox")
|
||||
|
||||
self.gridLayout_2.addWidget(self.chunkSizeCheckBox, 7, 1, 1, 1)
|
||||
|
||||
self.comicinfoTitleBox = QCheckBox(self.optionWidget)
|
||||
self.comicinfoTitleBox.setObjectName(u"comicinfoTitleBox")
|
||||
|
||||
self.gridLayout_2.addWidget(self.comicinfoTitleBox, 7, 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.horizontalLayout_2 = QHBoxLayout(self.gammaWidget)
|
||||
self.horizontalLayout_2.setObjectName(u"horizontalLayout_2")
|
||||
self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0)
|
||||
self.gammaLabel = QLabel(self.gammaWidget)
|
||||
self.gammaLabel.setObjectName(u"gammaLabel")
|
||||
|
||||
self.horizontalLayout_2.addWidget(self.gammaLabel)
|
||||
|
||||
self.gammaSlider = QSlider(self.gammaWidget)
|
||||
self.gammaSlider.setObjectName(u"gammaSlider")
|
||||
self.gammaSlider.setMaximum(250)
|
||||
self.gammaSlider.setSingleStep(5)
|
||||
self.gammaSlider.setOrientation(Qt.Orientation.Horizontal)
|
||||
|
||||
self.horizontalLayout_2.addWidget(self.gammaSlider)
|
||||
|
||||
|
||||
self.gridLayout.addWidget(self.gammaWidget, 7, 0, 1, 2)
|
||||
|
||||
self.chunkSizeWidget = QWidget(self.centralWidget)
|
||||
self.chunkSizeWidget.setObjectName(u"chunkSizeWidget")
|
||||
sizePolicy3.setHeightForWidth(self.chunkSizeWidget.sizePolicy().hasHeightForWidth())
|
||||
self.chunkSizeWidget.setSizePolicy(sizePolicy3)
|
||||
self.chunkSizeWidget.setVisible(False)
|
||||
self.horizontalLayout_4 = QHBoxLayout(self.chunkSizeWidget)
|
||||
self.horizontalLayout_4.setSpacing(0)
|
||||
self.horizontalLayout_4.setObjectName(u"horizontalLayout_4")
|
||||
self.horizontalLayout_4.setContentsMargins(0, 0, 0, 0)
|
||||
self.chunkSizeLabel = QLabel(self.chunkSizeWidget)
|
||||
self.chunkSizeLabel.setObjectName(u"chunkSizeLabel")
|
||||
sizePolicy4 = QSizePolicy(QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Preferred)
|
||||
sizePolicy4.setHorizontalStretch(0)
|
||||
sizePolicy4.setVerticalStretch(0)
|
||||
sizePolicy4.setHeightForWidth(self.chunkSizeLabel.sizePolicy().hasHeightForWidth())
|
||||
self.chunkSizeLabel.setSizePolicy(sizePolicy4)
|
||||
|
||||
self.horizontalLayout_4.addWidget(self.chunkSizeLabel)
|
||||
|
||||
self.chunkSizeBox = QSpinBox(self.chunkSizeWidget)
|
||||
self.chunkSizeBox.setObjectName(u"chunkSizeBox")
|
||||
self.chunkSizeBox.setMinimum(100)
|
||||
self.chunkSizeBox.setMaximum(600)
|
||||
self.chunkSizeBox.setValue(400)
|
||||
|
||||
self.horizontalLayout_4.addWidget(self.chunkSizeBox)
|
||||
|
||||
self.chunkSizeWarnLabel = QLabel(self.chunkSizeWidget)
|
||||
self.chunkSizeWarnLabel.setObjectName(u"chunkSizeWarnLabel")
|
||||
sizePolicy4.setHeightForWidth(self.chunkSizeWarnLabel.sizePolicy().hasHeightForWidth())
|
||||
self.chunkSizeWarnLabel.setSizePolicy(sizePolicy4)
|
||||
|
||||
self.horizontalLayout_4.addWidget(self.chunkSizeWarnLabel)
|
||||
|
||||
|
||||
self.gridLayout.addWidget(self.chunkSizeWidget, 6, 0, 1, 1)
|
||||
|
||||
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.mozJpegBox)
|
||||
QWidget.setTabOrder(self.mozJpegBox, self.maximizeStrips)
|
||||
QWidget.setTabOrder(self.maximizeStrips, self.croppingBox)
|
||||
QWidget.setTabOrder(self.croppingBox, self.spreadShiftBox)
|
||||
QWidget.setTabOrder(self.spreadShiftBox, self.deleteBox)
|
||||
QWidget.setTabOrder(self.deleteBox, self.disableProcessingBox)
|
||||
QWidget.setTabOrder(self.disableProcessingBox, self.chunkSizeBox)
|
||||
QWidget.setTabOrder(self.chunkSizeBox, self.noRotateBox)
|
||||
QWidget.setTabOrder(self.noRotateBox, self.interPanelCropBox)
|
||||
QWidget.setTabOrder(self.interPanelCropBox, self.reduceRainbowBox)
|
||||
QWidget.setTabOrder(self.reduceRainbowBox, self.heightBox)
|
||||
QWidget.setTabOrder(self.heightBox, self.croppingPowerSlider)
|
||||
QWidget.setTabOrder(self.croppingPowerSlider, 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)
|
||||
|
||||
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.croppingBox)
|
||||
mainWindow.setTabOrder(self.croppingBox, self.mozJpegBox)
|
||||
mainWindow.setTabOrder(self.mozJpegBox, self.maximizeStrips)
|
||||
mainWindow.setTabOrder(self.maximizeStrips, self.deleteBox)
|
||||
mainWindow.setTabOrder(self.deleteBox, self.disableProcessingBox)
|
||||
mainWindow.setTabOrder(self.disableProcessingBox, self.editorButton)
|
||||
mainWindow.setTabOrder(self.editorButton, self.wikiButton)
|
||||
mainWindow.setTabOrder(self.wikiButton, self.jobList)
|
||||
mainWindow.setTabOrder(self.jobList, self.gammaSlider)
|
||||
mainWindow.setTabOrder(self.gammaSlider, self.widthBox)
|
||||
mainWindow.setTabOrder(self.widthBox, self.heightBox)
|
||||
mainWindow.setTabOrder(self.heightBox, self.croppingPowerSlider)
|
||||
|
||||
QMetaObject.connectSlotsByName(mainWindow)
|
||||
# setupUi
|
||||
|
||||
def retranslateUi(self, mainWindow):
|
||||
_translate = QtCore.QCoreApplication.translate
|
||||
mainWindow.setWindowTitle(_translate("mainWindow", "Kindle Comic Converter"))
|
||||
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.rotateBox.setToolTip(_translate("mainWindow", "<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Split<br/></span>Double page spreads will be cut into two separate pages.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - Rotate and split<br/></span>Double page spreads will be displayed twice. First rotated and then split. </p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Rotate<br/></span>Double page spreads will be rotated.</p></body></html>"))
|
||||
self.rotateBox.setText(_translate("mainWindow", "Spread splitter"))
|
||||
self.outputSplit.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Automatic mode<br/></span>The output will be split automatically.</p><p style=\'white-space:pre\'><span style=\" font-weight:600; text-decoration: underline;\">Checked - Volume mode<br/></span>Every subdirectory will be considered as a separate volume.</p></body></html>"))
|
||||
self.outputSplit.setText(_translate("mainWindow", "Output split"))
|
||||
self.webtoonBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Enable special parsing mode for Korean Webtoons.</p></body></html>"))
|
||||
self.webtoonBox.setText(_translate("mainWindow", "Webtoon mode"))
|
||||
self.colorBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Disable conversion to grayscale.</p></body></html>"))
|
||||
self.colorBox.setText(_translate("mainWindow", "Color mode"))
|
||||
self.gammaBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Disable automatic gamma correction.</p></body></html>"))
|
||||
self.gammaBox.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.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.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.mozJpegBox.setToolTip(_translate("mainWindow", "<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - JPEG<br/></span>Use JPEG files</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - force PNG<br/></span>Create PNG files instead JPEG</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - mozJpeg<br/></span>10-20% smaller JPEG file, with the same image quality, but processing time multiplied by 2</p></body></html>"))
|
||||
self.mozJpegBox.setText(_translate("mainWindow", "JPEG/PNG/mozJpeg"))
|
||||
self.maximizeStrips.setToolTip(_translate("mainWindow", "<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - 1x4<br/></span>Keep format 1x4 panels strips.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - 2x2<br/></span>Turn 1x4 strips to 2x2 to maximize screen usage.</p></body></html>"))
|
||||
self.maximizeStrips.setText(_translate("mainWindow", "1x4 to 2x2 strips"))
|
||||
self.croppingBox.setToolTip(_translate("mainWindow", "<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Disabled</span></p><p>Disabled</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - Margins<br/></span>Margins</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Margins + page numbers<br/></span>Margins +page numbers</p></body></html>"))
|
||||
self.croppingBox.setText(_translate("mainWindow", "Cropping mode"))
|
||||
self.deleteBox.setToolTip(_translate("mainWindow", "Delete input file(s) or directory. It\'s not recoverable!"))
|
||||
self.deleteBox.setText(_translate("mainWindow", "Delete input"))
|
||||
self.disableProcessingBox.setToolTip(_translate("mainWindow", "<html><head/><body><pre style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">Do not process any image, ignore profile and processing options</pre></body></html>"))
|
||||
self.disableProcessingBox.setText(_translate("mainWindow", "Disable processing"))
|
||||
self.gammaLabel.setText(_translate("mainWindow", "Gamma: Auto"))
|
||||
self.croppingPowerLabel.setText(_translate("mainWindow", "Cropping power:"))
|
||||
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"))
|
||||
self.editorButton.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Shift+Click to edit directory.</p></body></html>"))
|
||||
self.editorButton.setText(_translate("mainWindow", "Editor"))
|
||||
self.wikiButton.setText(_translate("mainWindow", "Wiki"))
|
||||
self.hLabel.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Resolution of the target device.</p></body></html>"))
|
||||
self.hLabel.setText(_translate("mainWindow", "Custom height:"))
|
||||
self.widthBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Resolution of the target device.</p></body></html>"))
|
||||
self.wLabel.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Resolution of the target device.</p></body></html>"))
|
||||
self.wLabel.setText(_translate("mainWindow", "Custom width:"))
|
||||
self.heightBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Resolution of the target device.</p></body></html>"))
|
||||
from . import KCC_rc
|
||||
mainWindow.setWindowTitle(QCoreApplication.translate("mainWindow", u"Kindle Comic Converter", 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"Metadata Editor", None))
|
||||
self.kofiButton.setText(QCoreApplication.translate("mainWindow", u"Support me on Ko-fi", None))
|
||||
self.wikiButton.setText(QCoreApplication.translate("mainWindow", u"Wiki", 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 image folder", 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(s)", 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.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)
|
||||
#if QT_CONFIG(tooltip)
|
||||
self.preserveMarginLabel.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p>After calculating the cropping boundaries, "back up" a specified percentage amount.</p></body></html>", None))
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
self.preserveMarginLabel.setText(QCoreApplication.translate("mainWindow", u"Preserve Margin %", None))
|
||||
self.croppingPowerLabel.setText(QCoreApplication.translate("mainWindow", u"Cropping power:", 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.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"Right-to-left mode", 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.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 - Split and rotate<br/></span>Double page spreads will be displayed twice. First split and then rotated. </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.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.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.interPanelCropBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Disabled<br/></span>Disabled</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - Horizontal<br/></span>Crop empty horizontal lines.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Both<br/></span>Crop empty horizontal and vertical lines.</p></body></html>", None))
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
self.interPanelCropBox.setText(QCoreApplication.translate("mainWindow", u"Inter-panel crop", 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.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.disableProcessingBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Do not process any image, ignore profile and processing options.</p></body></html>", None))
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
self.disableProcessingBox.setText(QCoreApplication.translate("mainWindow", u"Disable processing", 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.authorEdit.setToolTip(QCoreApplication.translate("mainWindow", u"Default Author is KCC", None))
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
self.authorEdit.setPlaceholderText(QCoreApplication.translate("mainWindow", u"Default Author", 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.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.spreadShiftBox.setToolTip(QCoreApplication.translate("mainWindow", u"Shift first page to opposite side in landscape for two page spread alignment", None))
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
self.spreadShiftBox.setText(QCoreApplication.translate("mainWindow", u"Spread shift", None))
|
||||
#if QT_CONFIG(tooltip)
|
||||
self.fileFusionBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p>Combines all selected files into a single file. (Helpful for combining chapters into volumes.)</p></body></html>", None))
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
self.fileFusionBox.setText(QCoreApplication.translate("mainWindow", u"File Fusion", 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.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.noRotateBox.setToolTip(QCoreApplication.translate("mainWindow", u"Do not rotate double page spreads in spread splitter option.", None))
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
self.noRotateBox.setText(QCoreApplication.translate("mainWindow", u"No rotate", None))
|
||||
#if QT_CONFIG(tooltip)
|
||||
self.reduceRainbowBox.setToolTip(QCoreApplication.translate("mainWindow", u"Reduce rainbow effect on color eink by slightly blurring images", None))
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
self.reduceRainbowBox.setText(QCoreApplication.translate("mainWindow", u"Rainbow blur", None))
|
||||
#if QT_CONFIG(tooltip)
|
||||
self.chunkSizeCheckBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p><span style=\" font-weight:700; text-decoration: underline;\">Unchecked<br/></span>Maximal output file size is 100 MB for Webtoon, 400 MB for others before split occurs.</p><p><span style=\" font-weight:700; text-decoration: underline;\">Checked</span><br/>Output file size specified in "Chunk size MB" before split occurs.</p></body></html>", None))
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
self.chunkSizeCheckBox.setText(QCoreApplication.translate("mainWindow", u"Chunk size", None))
|
||||
#if QT_CONFIG(tooltip)
|
||||
self.comicinfoTitleBox.setToolTip(QCoreApplication.translate("mainWindow", u"Write Title from ComicInfo.xml", None))
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
self.comicinfoTitleBox.setText(QCoreApplication.translate("mainWindow", u"ComicInfo Title", None))
|
||||
self.gammaLabel.setText(QCoreApplication.translate("mainWindow", u"Gamma: Auto", None))
|
||||
#if QT_CONFIG(tooltip)
|
||||
self.chunkSizeWidget.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p>Warning: chunk size greater than default may cause<br/>performance/battery issues, especially on older devices.</p></body></html>", None))
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
self.chunkSizeLabel.setText(QCoreApplication.translate("mainWindow", u"Chunk size MB:", None))
|
||||
self.chunkSizeWarnLabel.setText(QCoreApplication.translate("mainWindow", u"Greater than default may cause performance issues on older ereaders.", None))
|
||||
# retranslateUi
|
||||
|
||||
|
||||
@@ -1,118 +1,168 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Form implementation generated from reading ui file 'gui/MetaEditor.ui'
|
||||
#
|
||||
# Created by: PyQt5 UI code generator 5.15.2
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
|
||||
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
################################################################################
|
||||
## Form generated from reading UI file 'MetaEditor.ui'
|
||||
##
|
||||
## Created by: Qt User Interface Compiler version 6.9.1
|
||||
##
|
||||
## WARNING! All changes made in this file will be lost when recompiling UI file!
|
||||
################################################################################
|
||||
|
||||
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.Mode.Normal, QIcon.State.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.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.Policy.MinimumExpanding, QSizePolicy.Policy.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.Mode.Normal, QIcon.State.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.Mode.Normal, QIcon.State.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.okButton.setText(_translate("editorDialog", "Save"))
|
||||
self.cancelButton.setText(_translate("editorDialog", "Cancel"))
|
||||
from . import KCC_rc
|
||||
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
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
__version__ = '5.6.5'
|
||||
__version__ = '7.5.1'
|
||||
__license__ = 'ISC'
|
||||
__copyright__ = '2012-2022, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>, darodi'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -25,10 +25,6 @@ from shutil import rmtree, copytree, move
|
||||
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):
|
||||
@@ -185,7 +181,7 @@ def splitImage(work):
|
||||
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')
|
||||
newPage.save(os.path.join(path, os.path.splitext(name)[0] + '-' + str(pageNumber).zfill(4) + '.png'), 'PNG')
|
||||
pageNumber += 1
|
||||
os.remove(filePath)
|
||||
except Exception:
|
||||
|
||||
@@ -18,15 +18,14 @@
|
||||
# PERFORMANCE OF THIS SOFTWARE.
|
||||
#
|
||||
|
||||
from functools import cached_property
|
||||
import os
|
||||
import platform
|
||||
import subprocess
|
||||
import distro
|
||||
from shutil import move
|
||||
from subprocess import STDOUT, PIPE
|
||||
from subprocess import STDOUT, PIPE, CalledProcessError
|
||||
from xml.dom.minidom import parseString
|
||||
from xml.parsers.expat import ExpatError
|
||||
from .shared import subprocess_run_silent
|
||||
from .shared import subprocess_run
|
||||
|
||||
EXTRACTION_ERROR = 'Failed to extract archive. Try extracting file outside of KCC.'
|
||||
|
||||
@@ -34,53 +33,80 @@ EXTRACTION_ERROR = 'Failed to extract archive. Try extracting file outside of KC
|
||||
class ComicArchive:
|
||||
def __init__(self, filepath):
|
||||
self.filepath = filepath
|
||||
self.type = None
|
||||
if not os.path.isfile(self.filepath):
|
||||
raise OSError('File not found.')
|
||||
process = subprocess_run_silent(['7z', 'l', '-y', '-p1', self.filepath], stderr=STDOUT, stdout=PIPE)
|
||||
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)
|
||||
|
||||
@cached_property
|
||||
def type(self):
|
||||
extraction_commands = [
|
||||
['7z', 'l', '-y', '-p1', self.filepath],
|
||||
]
|
||||
|
||||
if distro.id() == 'fedora' or distro.like() == 'fedora':
|
||||
extraction_commands.append(
|
||||
['unrar', 'l', '-y', '-p1', self.filepath],
|
||||
)
|
||||
|
||||
for cmd in extraction_commands:
|
||||
try:
|
||||
process = subprocess_run(cmd, capture_output=True, check=True)
|
||||
for line in process.stdout.splitlines():
|
||||
if b'Type =' in line:
|
||||
return line.rstrip().decode().split(' = ')[1].upper()
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
except CalledProcessError:
|
||||
pass
|
||||
|
||||
raise OSError(EXTRACTION_ERROR)
|
||||
|
||||
def extract(self, targetdir):
|
||||
if not os.path.isdir(targetdir):
|
||||
raise OSError('Target directory doesn\'t exist.')
|
||||
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 and platform.system() == 'Darwin':
|
||||
process = subprocess_run_silent(['unar', self.filepath, '-f', '-o', targetdir],
|
||||
stdout=PIPE, stderr=STDOUT)
|
||||
elif process.returncode != 0:
|
||||
|
||||
missing = []
|
||||
|
||||
extraction_commands = [
|
||||
['tar', '--exclude', '__MACOSX', '--exclude', '.DS_Store', '--exclude', 'thumbs.db', '--exclude', 'Thumbs.db', '-xf', self.filepath, '-C', targetdir],
|
||||
['7z', 'x', '-y', '-xr!__MACOSX', '-xr!.DS_Store', '-xr!thumbs.db', '-xr!Thumbs.db', '-o' + targetdir, self.filepath],
|
||||
]
|
||||
|
||||
if platform.system() == 'Darwin':
|
||||
extraction_commands.append(
|
||||
['unar', self.filepath, '-f', '-o', targetdir]
|
||||
)
|
||||
|
||||
extraction_commands.reverse()
|
||||
|
||||
if distro.id() == 'fedora' or distro.like() == 'fedora':
|
||||
extraction_commands.append(
|
||||
['unrar', 'x', '-y', '-x__MACOSX', '-x.DS_Store', '-xthumbs.db', '-xThumbs.db', self.filepath, targetdir]
|
||||
)
|
||||
|
||||
for cmd in extraction_commands:
|
||||
try:
|
||||
subprocess_run(cmd, capture_output=True, check=True)
|
||||
return targetdir
|
||||
except FileNotFoundError:
|
||||
missing.append(cmd[0])
|
||||
except CalledProcessError:
|
||||
pass
|
||||
|
||||
if missing:
|
||||
raise OSError(f'Extraction failed, install <a href="https://github.com/ciromattia/kcc#7-zip">specialized extraction software.</a> ')
|
||||
else:
|
||||
raise OSError(EXTRACTION_ERROR)
|
||||
tdir = os.listdir(targetdir)
|
||||
if 'ComicInfo.xml' in tdir:
|
||||
tdir.remove('ComicInfo.xml')
|
||||
return targetdir
|
||||
|
||||
def addFile(self, sourcefile):
|
||||
if self.type in ['RAR', 'RAR5']:
|
||||
raise NotImplementedError
|
||||
process = subprocess_run_silent(['7z', 'a', '-y', self.filepath, sourcefile],
|
||||
process = subprocess_run(['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'],
|
||||
process = subprocess_run(['7z', 'x', '-y', '-so', self.filepath, 'ComicInfo.xml'],
|
||||
stdout=PIPE, stderr=STDOUT)
|
||||
if process.returncode != 0:
|
||||
raise OSError(EXTRACTION_ERROR)
|
||||
|
||||
28
kindlecomicconverter/common_crop.py
Normal file
28
kindlecomicconverter/common_crop.py
Normal file
@@ -0,0 +1,28 @@
|
||||
def threshold_from_power(power):
|
||||
return 240-(power*64)
|
||||
|
||||
|
||||
'''
|
||||
Groups close values together
|
||||
'''
|
||||
def group_close_values(vals, max_dist_tolerated):
|
||||
groups = []
|
||||
|
||||
group_start = -1
|
||||
group_end = 0
|
||||
for i in range(len(vals)):
|
||||
dist = vals[i] - group_end
|
||||
if group_start == -1:
|
||||
group_start = vals[i]
|
||||
group_end = vals[i]
|
||||
elif dist <= max_dist_tolerated:
|
||||
group_end = vals[i]
|
||||
else:
|
||||
groups.append((group_start, group_end))
|
||||
group_start = -1
|
||||
group_end = -1
|
||||
|
||||
if group_start != -1:
|
||||
groups.append((group_start, group_end))
|
||||
|
||||
return groups
|
||||
@@ -136,7 +136,11 @@ def del_exth(rec0, exth_num):
|
||||
|
||||
|
||||
class DualMobiMetaFix:
|
||||
def __init__(self, infile, outfile, asin):
|
||||
def __init__(self, infile, outfile, asin, is_pdoc):
|
||||
cdetype = b'EBOK'
|
||||
if is_pdoc:
|
||||
cdetype = b'PDOC'
|
||||
|
||||
shutil.copyfile(infile, outfile)
|
||||
f = open(outfile, "r+b")
|
||||
self.datain = mmap.mmap(f.fileno(), 0)
|
||||
@@ -147,7 +151,7 @@ class DualMobiMetaFix:
|
||||
rec0 = self.datain_rec0
|
||||
rec0 = del_exth(rec0, 501)
|
||||
rec0 = del_exth(rec0, 113)
|
||||
rec0 = add_exth(rec0, 501, b'EBOK')
|
||||
rec0 = add_exth(rec0, 501, cdetype)
|
||||
rec0 = add_exth(rec0, 113, asin)
|
||||
replacesection(self.datain, 0, rec0)
|
||||
|
||||
@@ -174,7 +178,7 @@ class DualMobiMetaFix:
|
||||
rec0 = self.datain_kfrec0
|
||||
rec0 = del_exth(rec0, 501)
|
||||
rec0 = del_exth(rec0, 113)
|
||||
rec0 = add_exth(rec0, 501, b'EBOK')
|
||||
rec0 = add_exth(rec0, 501, cdetype)
|
||||
rec0 = add_exth(rec0, 113, asin)
|
||||
replacesection(self.datain, datain_kf8, rec0)
|
||||
|
||||
|
||||
@@ -20,9 +20,11 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import io
|
||||
import os
|
||||
from pathlib import Path
|
||||
import mozjpeg_lossless_optimization
|
||||
from PIL import Image, ImageOps, ImageStat, ImageChops, ImageFilter
|
||||
from .shared import md5Checksum
|
||||
from PIL import Image, ImageOps, ImageStat, ImageChops, ImageFilter, ImageDraw
|
||||
from .page_number_crop_alg import get_bbox_crop_margin_page_number, get_bbox_crop_margin
|
||||
from .inter_panel_crop_alg import crop_empty_inter_panel
|
||||
|
||||
AUTO_CROP_THRESHOLD = 0.015
|
||||
|
||||
@@ -78,18 +80,31 @@ class ProfileData:
|
||||
PalleteNull = [
|
||||
]
|
||||
|
||||
Profiles = {
|
||||
ProfilesKindleEBOK = {
|
||||
'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),
|
||||
'K34': ("Kindle Keyboard/Touch", (600, 800), Palette16, 1.8),
|
||||
'K57': ("Kindle 5/7", (600, 800), Palette16, 1.8),
|
||||
'KPW': ("Kindle Paperwhite 1/2", (758, 1024), Palette16, 1.8),
|
||||
'KV': ("Kindle Paperwhite 3/4/Voyage/Oasis", (1072, 1448), Palette16, 1.8),
|
||||
'KV': ("Kindle Voyage", (1072, 1448), Palette16, 1.8),
|
||||
}
|
||||
|
||||
ProfilesKindlePDOC = {
|
||||
'KPW34': ("Kindle Paperwhite 3/4/Oasis", (1072, 1448), Palette16, 1.8),
|
||||
'K810': ("Kindle 8/10", (600, 800), Palette16, 1.8),
|
||||
'KO': ("Kindle Oasis 2/3/Paperwhite 12/Colorsoft 12", (1264, 1680), Palette16, 1.8),
|
||||
'K11': ("Kindle 11", (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),
|
||||
}
|
||||
|
||||
ProfilesKindle = {
|
||||
**ProfilesKindleEBOK,
|
||||
**ProfilesKindlePDOC
|
||||
}
|
||||
|
||||
ProfilesKobo = {
|
||||
'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),
|
||||
@@ -99,10 +114,24 @@ class ProfileData:
|
||||
'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),
|
||||
}
|
||||
|
||||
ProfilesRemarkable = {
|
||||
'Rmk1': ("reMarkable 1", (1404, 1872), Palette16, 1.8),
|
||||
'Rmk2': ("reMarkable 2", (1404, 1872), Palette16, 1.8),
|
||||
'RmkPP': ("reMarkable Paper Pro", (1620, 2160), Palette16, 1.8),
|
||||
}
|
||||
|
||||
Profiles = {
|
||||
**ProfilesKindle,
|
||||
**ProfilesKobo,
|
||||
**ProfilesRemarkable,
|
||||
'OTHER': ("Other", (0, 0), Palette16, 1.8),
|
||||
}
|
||||
|
||||
@@ -114,7 +143,13 @@ class ComicPageParser:
|
||||
self.source = source
|
||||
self.size = self.opt.profileData[1]
|
||||
self.payload = []
|
||||
self.image = Image.open(os.path.join(source[0], source[1])).convert('RGB')
|
||||
|
||||
# Detect corruption in source image, let caller catch any exceptions triggered.
|
||||
srcImgPath = os.path.join(source[0], source[1])
|
||||
self.image = Image.open(srcImgPath)
|
||||
self.image.verify()
|
||||
self.image = Image.open(srcImgPath).convert('RGB')
|
||||
|
||||
self.color = self.colorCheck()
|
||||
self.fill = self.fillCheck()
|
||||
# backwards compatibility for Pillow >9.1.0
|
||||
@@ -149,7 +184,10 @@ class ComicPageParser:
|
||||
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.Resampling.BICUBIC, True), self.color, self.fill])
|
||||
spread = self.image
|
||||
if not self.opt.norotate:
|
||||
spread = spread.rotate(90, Image.Resampling.BICUBIC, True)
|
||||
self.payload.append(['R', self.source, spread, self.color, self.fill])
|
||||
elif (width > height) != (dstwidth > dstheight) and not self.opt.webtoon:
|
||||
if self.opt.splitter != 1:
|
||||
if width > height:
|
||||
@@ -167,7 +205,10 @@ 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.Resampling.BICUBIC, True),
|
||||
spread = self.image
|
||||
if not self.opt.norotate:
|
||||
spread = spread.rotate(90, Image.Resampling.BICUBIC, True)
|
||||
self.payload.append(['R', self.source, spread,
|
||||
self.color, self.fill])
|
||||
else:
|
||||
self.payload.append(['N', self.source, self.image, self.color, self.fill])
|
||||
@@ -239,20 +280,23 @@ 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
|
||||
self.rotated = False
|
||||
self.orgPath = os.path.join(path[0], path[1])
|
||||
self.targetPathStart = os.path.join(path[0], os.path.splitext(path[1])[0])
|
||||
if 'N' in mode:
|
||||
self.targetPath = os.path.join(path[0], os.path.splitext(path[1])[0]) + '-KCC'
|
||||
self.targetPathOrder = '-kcc-x'
|
||||
elif 'R' in mode:
|
||||
self.targetPath = os.path.join(path[0], os.path.splitext(path[1])[0]) + '-KCC-A'
|
||||
self.rotated = True
|
||||
self.targetPathOrder = '-kcc-d'
|
||||
if not options.norotate:
|
||||
self.rotated = True
|
||||
elif 'S1' in mode:
|
||||
self.targetPath = os.path.join(path[0], os.path.splitext(path[1])[0]) + '-KCC-B'
|
||||
self.targetPathOrder = '-kcc-b'
|
||||
elif 'S2' in mode:
|
||||
self.targetPath = os.path.join(path[0], os.path.splitext(path[1])[0]) + '-KCC-C'
|
||||
self.targetPathOrder = '-kcc-c'
|
||||
# backwards compatibility for Pillow >9.1.0
|
||||
if not hasattr(Image, 'Resampling'):
|
||||
Image.Resampling = Image
|
||||
@@ -266,25 +310,38 @@ class ComicPage:
|
||||
flags.append('Rotated')
|
||||
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)
|
||||
if self.opt.kindle_scribe_azw3 and self.image.size[1] > 1920:
|
||||
w, h = self.image.size
|
||||
targetPath = self.save_with_codec(self.image.crop((0, 0, w, 1920)), self.targetPathStart + self.targetPathOrder + '-above')
|
||||
self.save_with_codec(self.image.crop((0, 1920, w, h)), self.targetPathStart + self.targetPathOrder + '-below')
|
||||
elif self.opt.kindle_scribe_azw3:
|
||||
targetPath = self.save_with_codec(self.image, self.targetPathStart + self.targetPathOrder + '-whole')
|
||||
else:
|
||||
self.targetPath += '.jpg'
|
||||
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]
|
||||
targetPath = self.save_with_codec(self.image, self.targetPathStart + self.targetPathOrder)
|
||||
if os.path.isfile(self.orgPath):
|
||||
os.remove(self.orgPath)
|
||||
return [Path(targetPath).name, flags]
|
||||
except IOError as err:
|
||||
raise RuntimeError('Cannot save image. ' + str(err))
|
||||
|
||||
def save_with_codec(self, image, targetPath):
|
||||
if self.opt.forcepng:
|
||||
image.info["transparency"] = None
|
||||
targetPath += '.png'
|
||||
image.save(targetPath, 'PNG', optimize=1)
|
||||
else:
|
||||
targetPath += '.jpg'
|
||||
if self.opt.mozjpeg:
|
||||
with io.BytesIO() as output:
|
||||
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(targetPath, "wb") as output_jpeg_file:
|
||||
output_jpeg_file.write(output_jpeg_bytes)
|
||||
else:
|
||||
image.save(targetPath, 'JPEG', optimize=1, quality=85)
|
||||
return targetPath
|
||||
|
||||
def autocontrastImage(self):
|
||||
gamma = self.opt.gamma
|
||||
if gamma < 0.1:
|
||||
@@ -307,6 +364,14 @@ class ComicPage:
|
||||
# Quantize is deprecated but new function call it internally anyway...
|
||||
self.image = self.image.quantize(palette=palImg)
|
||||
|
||||
def optimizeForDisplay(self, reducerainbow):
|
||||
# Reduce rainbow artifacts for grayscale images by breaking up dither patterns that cause Moire interference with color filter array
|
||||
if reducerainbow and not self.color:
|
||||
unsharpFilter = ImageFilter.UnsharpMask(radius=1, percent=100)
|
||||
self.image = self.image.filter(unsharpFilter)
|
||||
self.image = self.image.filter(ImageFilter.BoxBlur(1.0))
|
||||
self.image = self.image.filter(unsharpFilter)
|
||||
|
||||
def resizeImage(self):
|
||||
ratio_device = float(self.size[1]) / float(self.size[0])
|
||||
ratio_image = float(self.image.size[1]) / float(self.image.size[0])
|
||||
@@ -314,79 +379,51 @@ class ComicPage:
|
||||
if self.opt.stretch:
|
||||
self.image = self.image.resize(self.size, method)
|
||||
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=method)
|
||||
pass
|
||||
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:
|
||||
elif (self.opt.format == 'CBZ' or self.opt.kfx) and not self.opt.white_borders:
|
||||
self.image = ImageOps.pad(self.image, self.size, method=method, color=self.fill)
|
||||
else:
|
||||
self.image = ImageOps.contain(self.image, self.size, method=method)
|
||||
|
||||
def resize_method(self):
|
||||
if self.image.size[0] <= self.size[0] and self.image.size[1] <= self.size[1]:
|
||||
method = Image.Resampling.BICUBIC
|
||||
if self.image.size[0] < self.size[0] and self.image.size[1] < self.size[1]:
|
||||
return Image.Resampling.BICUBIC
|
||||
else:
|
||||
method = Image.Resampling.LANCZOS
|
||||
return method
|
||||
|
||||
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(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
|
||||
return Image.Resampling.LANCZOS
|
||||
|
||||
def maybeCrop(self, box, minimum):
|
||||
w, h = self.image.size
|
||||
left, upper, right, lower = box
|
||||
if self.opt.preservemargin:
|
||||
ratio = 1 - self.opt.preservemargin / 100
|
||||
box = left * ratio, upper * ratio, right + (w - right) * (1 - ratio), lower + (h - lower) * (1 - ratio)
|
||||
box_area = (box[2] - box[0]) * (box[3] - box[1])
|
||||
image_area = self.image.size[0] * self.image.size[1]
|
||||
if (box_area / image_area) >= minimum:
|
||||
self.image = self.image.crop(box)
|
||||
|
||||
def cropPageNumber(self, power, minimum):
|
||||
if self.fill != 'white':
|
||||
tmptmg = self.image.convert(mode='L')
|
||||
else:
|
||||
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)
|
||||
bbox = get_bbox_crop_margin_page_number(self.image, power, self.fill)
|
||||
|
||||
if bbox:
|
||||
self.maybeCrop(bbox, 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)
|
||||
bbox = get_bbox_crop_margin(self.image, power, self.fill)
|
||||
|
||||
if bbox:
|
||||
self.maybeCrop(bbox, minimum)
|
||||
|
||||
def cropInterPanelEmptySections(self, direction):
|
||||
self.image = crop_empty_inter_panel(self.image, direction, background_color=self.fill)
|
||||
|
||||
class Cover:
|
||||
def __init__(self, source, target, opt, tomeid):
|
||||
def __init__(self, source, opt):
|
||||
self.options = opt
|
||||
self.source = source
|
||||
self.target = target
|
||||
if tomeid == 0:
|
||||
self.tomeid = 1
|
||||
else:
|
||||
self.tomeid = tomeid
|
||||
self.image = Image.open(source)
|
||||
# backwards compatibility for Pillow >9.1.0
|
||||
if not hasattr(Image, 'Resampling'):
|
||||
@@ -398,17 +435,49 @@ 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.Resampling.LANCZOS)
|
||||
self.save()
|
||||
self.crop_main_cover()
|
||||
|
||||
def save(self):
|
||||
size = list(self.options.profileData[1])
|
||||
if self.options.kindle_scribe_azw3:
|
||||
size[1] = min(size[1], 1920)
|
||||
self.image.thumbnail(tuple(size), Image.Resampling.LANCZOS)
|
||||
|
||||
def crop_main_cover(self):
|
||||
w, h = self.image.size
|
||||
if w / h > 2:
|
||||
if self.options.righttoleft:
|
||||
self.image = self.image.crop((w/6, 0, w/2 - w * 0.02, h))
|
||||
else:
|
||||
self.image = self.image.crop((w/2 + w * 0.02, 0, 5/6 * w, h))
|
||||
elif w / h > 1.3:
|
||||
if self.options.righttoleft:
|
||||
self.image = self.image.crop((0, 0, w/2 - w * 0.03, h))
|
||||
else:
|
||||
self.image = self.image.crop((w/2 + w * 0.03, 0, w, h))
|
||||
|
||||
def save_to_epub(self, target, tomeid, len_tomes=0):
|
||||
try:
|
||||
self.image.save(self.target, "JPEG", optimize=1, quality=85)
|
||||
if tomeid == 0:
|
||||
self.image.save(target, "JPEG", optimize=1, quality=85)
|
||||
else:
|
||||
copy = self.image.copy()
|
||||
draw = ImageDraw.Draw(copy)
|
||||
w, h = copy.size
|
||||
draw.text(
|
||||
xy=(w/2, h * .85),
|
||||
text=f'{tomeid}/{len_tomes}',
|
||||
anchor='ms',
|
||||
font_size=h//7,
|
||||
fill=255,
|
||||
stroke_fill=0,
|
||||
stroke_width=25
|
||||
)
|
||||
copy.save(target, "JPEG", optimize=1, quality=85)
|
||||
except IOError:
|
||||
raise RuntimeError('Failed to save cover.')
|
||||
|
||||
def saveToKindle(self, kindle, asin):
|
||||
self.image = self.image.resize((300, 470), Image.Resampling.LANCZOS)
|
||||
self.image = ImageOps.contain(self.image, (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)
|
||||
|
||||
76
kindlecomicconverter/inter_panel_crop_alg.py
Normal file
76
kindlecomicconverter/inter_panel_crop_alg.py
Normal file
@@ -0,0 +1,76 @@
|
||||
from PIL import Image, ImageFilter, ImageOps
|
||||
import numpy as np
|
||||
from typing import Literal
|
||||
from .common_crop import threshold_from_power, group_close_values
|
||||
|
||||
|
||||
'''
|
||||
Crops inter-panel empty spaces (ignores empty spaces near borders - for that use crop margins).
|
||||
|
||||
Parameters:
|
||||
img (PIL image): A PIL image.
|
||||
direction (horizontal or vertical or both): To crop rows (horizontal), cols (vertical) or both.
|
||||
keep (float): Distance to keep between panels after cropping (in percentage relative to the original distance).
|
||||
background_color (string): 'white' for white background, anything else for black.
|
||||
Returns:
|
||||
img (PIL image): A PIL image after cropping empty sections.
|
||||
'''
|
||||
def crop_empty_inter_panel(img, direction: Literal["horizontal", "vertical", "both"], keep=0.04, background_color='white'):
|
||||
img_temp = img
|
||||
|
||||
if img.mode != 'L':
|
||||
img_temp = ImageOps.grayscale(img)
|
||||
|
||||
if background_color != 'white':
|
||||
img_temp = ImageOps.invert(img)
|
||||
|
||||
img_mat = np.array(img)
|
||||
|
||||
power = 1
|
||||
img_temp = ImageOps.autocontrast(img_temp, 1).filter(ImageFilter.BoxBlur(1))
|
||||
img_temp = img_temp.point(lambda p: 255 if p <= threshold_from_power(power) else 0)
|
||||
|
||||
if direction in ["horizontal", "both"]:
|
||||
rows_idx_to_remove = empty_sections(img_temp, keep, horizontal=True)
|
||||
img_mat = np.delete(img_mat, rows_idx_to_remove, 0)
|
||||
|
||||
if direction in ["vertical", "both"]:
|
||||
cols_idx_to_remove = empty_sections(img_temp, keep, horizontal=False)
|
||||
img_mat = np.delete(img_mat, cols_idx_to_remove, 1)
|
||||
|
||||
return Image.fromarray(img_mat)
|
||||
|
||||
|
||||
'''
|
||||
Finds empty sections (excluding near borders).
|
||||
|
||||
Parameters:
|
||||
img (PIL image): A PIL image.
|
||||
keep (float): Distance to keep between panels after cropping (in percentage relative to the original distance).
|
||||
horizontal (boolean): True to find empty rows, False to find empty columns.
|
||||
Returns:
|
||||
Itertable (list or NumPy array): indices of rows or columns to remove.
|
||||
'''
|
||||
def empty_sections(img, keep, horizontal=True):
|
||||
axis = 1 if horizontal else 0
|
||||
|
||||
img_mat = np.array(img)
|
||||
img_mat_max = np.max(img_mat, axis=axis)
|
||||
img_mat_empty_idx = np.where(img_mat_max == 0)[0]
|
||||
|
||||
empty_sections = group_close_values(img_mat_empty_idx, 1)
|
||||
sections_to_remove = []
|
||||
for section in empty_sections:
|
||||
if section[1] < img.size[1] * 0.99 and section[0] > img.size[1] * 0.01: # if not near borders
|
||||
sections_to_remove.append(section)
|
||||
|
||||
if len(sections_to_remove) != 0:
|
||||
sections_to_remove_after_keep = [(int(x1+(keep/2)*(x2-x1)), int(x2-(keep/2)*(x2-x1))) for x1,x2 in sections_to_remove]
|
||||
idx_to_remove = np.concatenate([np.arange(x1, x2) for x1,x2 in sections_to_remove_after_keep])
|
||||
|
||||
return idx_to_remove
|
||||
|
||||
return []
|
||||
|
||||
|
||||
|
||||
@@ -19,9 +19,12 @@
|
||||
import os.path
|
||||
import psutil
|
||||
|
||||
from . import image
|
||||
|
||||
|
||||
class Kindle:
|
||||
def __init__(self):
|
||||
def __init__(self, profile):
|
||||
self.profile = profile
|
||||
self.path = self.findDevice()
|
||||
if self.path:
|
||||
self.coverSupport = self.checkThumbnails()
|
||||
@@ -29,9 +32,11 @@ class Kindle:
|
||||
self.coverSupport = False
|
||||
|
||||
def findDevice(self):
|
||||
if self.profile in image.ProfileData.ProfilesKindlePDOC.keys():
|
||||
return False
|
||||
for drive in reversed(psutil.disk_partitions(False)):
|
||||
if (drive[2] == 'FAT32' and drive[3] == 'rw,removable') or \
|
||||
(drive[2] in ('vfat', 'msdos', 'FAT') and 'rw' in drive[3]):
|
||||
(drive[2] in ('vfat', 'msdos', 'FAT', 'apfs') 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]
|
||||
|
||||
@@ -20,6 +20,7 @@ import os
|
||||
from xml.dom.minidom import parse, Document
|
||||
from tempfile import mkdtemp
|
||||
from shutil import rmtree
|
||||
from xml.sax.saxutils import unescape
|
||||
from . import comicarchive
|
||||
|
||||
|
||||
@@ -52,19 +53,19 @@ class MetadataParser:
|
||||
|
||||
def parseXML(self):
|
||||
if len(self.rawdata.getElementsByTagName('Series')) != 0:
|
||||
self.data['Series'] = self.rawdata.getElementsByTagName('Series')[0].firstChild.nodeValue
|
||||
self.data['Series'] = unescape(self.rawdata.getElementsByTagName('Series')[0].firstChild.nodeValue)
|
||||
if len(self.rawdata.getElementsByTagName('Volume')) != 0:
|
||||
self.data['Volume'] = self.rawdata.getElementsByTagName('Volume')[0].firstChild.nodeValue
|
||||
if len(self.rawdata.getElementsByTagName('Number')) != 0:
|
||||
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
|
||||
self.data['Summary'] = unescape(self.rawdata.getElementsByTagName('Summary')[0].firstChild.nodeValue)
|
||||
if len(self.rawdata.getElementsByTagName('Title')) != 0:
|
||||
self.data['Title'] = self.rawdata.getElementsByTagName('Title')[0].firstChild.nodeValue
|
||||
self.data['Title'] = unescape(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'].append(unescape(person))
|
||||
self.data[field + 's'] = list(set(self.data[field + 's']))
|
||||
self.data[field + 's'].sort()
|
||||
if len(self.rawdata.getElementsByTagName('Page')) != 0:
|
||||
|
||||
184
kindlecomicconverter/page_number_crop_alg.py
Normal file
184
kindlecomicconverter/page_number_crop_alg.py
Normal file
@@ -0,0 +1,184 @@
|
||||
from PIL import ImageOps, ImageFilter
|
||||
import numpy as np
|
||||
from .common_crop import threshold_from_power, group_close_values
|
||||
|
||||
|
||||
'''
|
||||
Some assupmptions on the page number sizes
|
||||
We assume that the size of the number (including all digits) is between
|
||||
'min_shape_size_tolerated_size' and 'max_shape_size_tolerated_size' relative to the image size.
|
||||
We assume the distance between the digit is no more than 'max_dist_size' (x,y), and no more than 3 digits.
|
||||
'''
|
||||
max_shape_size_tolerated_size = (0.015*3, 0.02) # percent
|
||||
min_shape_size_tolerated_size = (0.003, 0.006) # percent
|
||||
window_h_size = max_shape_size_tolerated_size[1]*1.25 # percent
|
||||
max_dist_size = (0.01, 0.002) # percent
|
||||
|
||||
|
||||
'''
|
||||
E-reader screen real-estate is an important resource.
|
||||
More available screensize means more details can be better seen, especially text.
|
||||
Text is one of the most important elements that need to be clearly readable on e-readers,
|
||||
which mostly are smaller devices where the need to zoom is unwanted.
|
||||
|
||||
By cropping the page number on the bottom of the page, 2%-5% of the page height can be regained
|
||||
that allows us to upscale the image even more.
|
||||
- Most of the times the screen height is the limiting factor in upscaling, rather than its width.
|
||||
|
||||
Parameters:
|
||||
img (PIL image): A PIL image.
|
||||
power (float): The power to 'chop' through pixels matching the background. Values in range[0,3].
|
||||
background_color (string): 'white' for white background, anything else for black.
|
||||
Returns:
|
||||
bbox (4-tuple, left|top|right|bot): The tightest bounding box calculated after trying to remove the bottom page number. Returns None if couldnt find anything satisfactory
|
||||
'''
|
||||
def get_bbox_crop_margin_page_number(img, power=1, background_color='white'):
|
||||
if img.mode != 'L':
|
||||
img = ImageOps.grayscale(img)
|
||||
|
||||
if background_color != 'white':
|
||||
img = ImageOps.invert(img)
|
||||
|
||||
'''
|
||||
Autocontrast: due to some threshold values, it's important that the blacks will be blacks and white will be whites.
|
||||
Box/MeanFilter: Allows us to reduce noise like bad a page scan or compression artifacts.
|
||||
Note: MedianFilter works better in my experience, but takes 2x-3x longer to perform.
|
||||
'''
|
||||
img = ImageOps.autocontrast(img, 1).filter(ImageFilter.BoxBlur(1))
|
||||
|
||||
'''
|
||||
The 'power' parameters determines the threshold. The higher the power, the more "force" it can crop through black pixels (in case of white background)
|
||||
and the lower the power, more sensitive to black pixels.
|
||||
'''
|
||||
threshold = threshold_from_power(power)
|
||||
bw_img = img.point(lambda p: 255 if p <= threshold else 0)
|
||||
bw_bbox = bw_img.getbbox()
|
||||
if not bw_bbox: # bbox cannot be found in case that the entire resulted image is black.
|
||||
return None
|
||||
|
||||
left, top_y_pos, right, bot_y_pos = bw_bbox
|
||||
|
||||
'''
|
||||
We inspect the lower bottom part of the image where we suspect might be a page number.
|
||||
We assume that page number consist of 1 to 3 digits and the total min and max size of the number
|
||||
is between 'min_shape_size_tolerated_size' and 'max_shape_size_tolerated_size'.
|
||||
'''
|
||||
window_h = int(img.size[1] * window_h_size)
|
||||
img_part = img.crop((left,bot_y_pos-window_h, right, bot_y_pos))
|
||||
|
||||
'''
|
||||
We detect related-pixels by proximity, with max distance defined in 'max_dist_size'.
|
||||
Related pixels (in x axis) for each image-row are then merged to boxes with adjacent rows (in y axis)
|
||||
to form bounding boxes of the detected objects (which one of them could be the page number).
|
||||
'''
|
||||
img_part_mat = np.array(img_part)
|
||||
window_groups = []
|
||||
for i in range(img_part.size[1]):
|
||||
row_groups = [(g[0], g[1], i, i) for g in group_close_values(np.where(img_part_mat[i] <= threshold)[0], img.size[0]*max_dist_size[0])]
|
||||
window_groups.extend(row_groups)
|
||||
|
||||
window_groups = np.array(window_groups)
|
||||
|
||||
boxes = merge_boxes(window_groups, (img.size[0]*max_dist_size[0], img.size[1]*max_dist_size[1]))
|
||||
'''
|
||||
We assume that the lowest part of the image that has black pixels on is the page number.
|
||||
In case that there are more than one detected object in the loewst part, we assume that one of them is probably
|
||||
manga-content and shouldn't be cropped.
|
||||
'''
|
||||
# filter all small objects
|
||||
boxes = list(filter(lambda box: box[1]-box[0] >= img.size[0]*min_shape_size_tolerated_size[0]
|
||||
and box[3]-box[2] >= img.size[1]*min_shape_size_tolerated_size[1], boxes))
|
||||
lowest_boxes = list(filter(lambda box: box[3] == window_h-1, boxes))
|
||||
|
||||
min_y_of_lowest_boxes = 0
|
||||
if len(lowest_boxes) > 0:
|
||||
min_y_of_lowest_boxes = np.min(np.array(lowest_boxes)[:,2])
|
||||
|
||||
boxes_in_same_y_range = list(filter(lambda box: box[3] >= min_y_of_lowest_boxes, boxes))
|
||||
|
||||
max_shape_size_tolerated = (img.size[0] * max_shape_size_tolerated_size[0],
|
||||
max(img.size[1] *max_shape_size_tolerated_size[1], 3))
|
||||
|
||||
should_force_crop = (
|
||||
len(boxes_in_same_y_range) == 1
|
||||
and (boxes_in_same_y_range[0][1] - boxes_in_same_y_range[0][0] <= max_shape_size_tolerated[0])
|
||||
and (boxes_in_same_y_range[0][3] - boxes_in_same_y_range[0][2] <= max_shape_size_tolerated[1])
|
||||
)
|
||||
|
||||
cropped_bbox = (0, 0, img.size[0], img.size[1])
|
||||
if should_force_crop:
|
||||
cropped_bbox = (0, 0, img.size[0], bot_y_pos-(window_h-boxes_in_same_y_range[0][2]+1))
|
||||
|
||||
cropped_bbox = bw_img.crop(cropped_bbox).getbbox()
|
||||
return cropped_bbox
|
||||
|
||||
|
||||
'''
|
||||
Parameters:
|
||||
img (PIL image): A PIL image.
|
||||
power (float): The power to 'chop' through pixels matching the background. Values in range[0,3].
|
||||
background_color (string): 'white' for white background, anything else for black.
|
||||
Returns:
|
||||
bbox (4-tuple, left|top|right|bot): The tightest bounding box calculated after trying to remove the bottom page number. Returns None if couldnt find anything satisfactory
|
||||
'''
|
||||
def get_bbox_crop_margin(img, power=1, background_color='white'):
|
||||
if img.mode != 'L':
|
||||
img = ImageOps.grayscale(img)
|
||||
|
||||
if background_color != 'white':
|
||||
img = ImageOps.invert(img)
|
||||
|
||||
'''
|
||||
Autocontrast: due to some threshold values, it's important that the blacks will be blacks and white will be whites.
|
||||
Box/MeanFilter: Allows us to reduce noise like bad a page scan or compression artifacts.
|
||||
Note: MedianFilter works better in my experience, but takes 2x-3x longer to perform.
|
||||
'''
|
||||
img = ImageOps.autocontrast(img, 1).filter(ImageFilter.BoxBlur(1))
|
||||
|
||||
'''
|
||||
The 'power' parameters determines the threshold. The higher the power, the more "force" it can crop through black pixels (in case of white background)
|
||||
and the lower the power, more sensitive to black pixels.
|
||||
'''
|
||||
threshold = threshold_from_power(power)
|
||||
bw_img = img.point(lambda p: 255 if p <= threshold else 0)
|
||||
|
||||
return bw_img.getbbox()
|
||||
|
||||
|
||||
def box_intersect(box1, box2, max_dist):
|
||||
return not (box2[0]-max_dist[0] > box1[1]
|
||||
or box2[1]+max_dist[0] < box1[0]
|
||||
or box2[2]-max_dist[1] > box1[3]
|
||||
or box2[3]+max_dist[1] < box1[2])
|
||||
|
||||
'''
|
||||
Merge close bounding boxes (left,right, top,bot) (x axis) with distance threshold defined in
|
||||
'max_dist_tolerated'. Boxes with less 'max_dist_tolerated' distance (Chebyshev distance).
|
||||
'''
|
||||
def merge_boxes(boxes, max_dist_tolerated):
|
||||
j = 0
|
||||
while j < len(boxes)-1:
|
||||
g1 = boxes[j]
|
||||
intersecting_boxes = []
|
||||
other_boxes = []
|
||||
for i in range(j+1,len(boxes)):
|
||||
g2 = boxes[i]
|
||||
if box_intersect(g1,g2, max_dist_tolerated):
|
||||
intersecting_boxes.append(g2)
|
||||
else:
|
||||
other_boxes.append(g2)
|
||||
|
||||
if len(intersecting_boxes) > 0:
|
||||
intersecting_boxes = np.array([g1, *intersecting_boxes])
|
||||
merged_box = np.array([
|
||||
np.min(intersecting_boxes[:,0]),
|
||||
np.max(intersecting_boxes[:,1]),
|
||||
np.min(intersecting_boxes[:,2]),
|
||||
np.max(intersecting_boxes[:,3])
|
||||
])
|
||||
other_boxes.append(merged_box)
|
||||
boxes = np.concatenate([boxes[:j], other_boxes])
|
||||
j = 0
|
||||
else:
|
||||
j += 1
|
||||
return boxes
|
||||
@@ -18,11 +18,12 @@
|
||||
# PERFORMANCE OF THIS SOFTWARE.
|
||||
#
|
||||
|
||||
from functools import lru_cache
|
||||
import os
|
||||
from hashlib import md5
|
||||
from html.parser import HTMLParser
|
||||
import subprocess
|
||||
from distutils.version import StrictVersion
|
||||
from packaging.version import Version
|
||||
from re import split
|
||||
import sys
|
||||
from traceback import format_tb
|
||||
@@ -49,7 +50,11 @@ class HTMLStripper(HTMLParser):
|
||||
def getImageFileName(imgfile):
|
||||
name, ext = os.path.splitext(imgfile)
|
||||
ext = ext.lower()
|
||||
if (name.startswith('.') and len(name) == 1) or ext not in ['.png', '.jpg', '.jpeg', '.gif', '.webp']:
|
||||
if (name.startswith('.') and len(name) == 1):
|
||||
return None
|
||||
if name.startswith('._'):
|
||||
return None
|
||||
if ext not in ['.png', '.jpg', '.jpeg', '.gif', '.webp', '.jp2', '.j2k', '.jpx']:
|
||||
return None
|
||||
return [name, ext]
|
||||
|
||||
@@ -74,16 +79,6 @@ def walkLevel(some_dir, level=1):
|
||||
del dirs[:]
|
||||
|
||||
|
||||
def md5Checksum(fpath):
|
||||
with open(fpath, 'rb') as fh:
|
||||
m = md5()
|
||||
while True:
|
||||
data = fh.read(8192)
|
||||
if not data:
|
||||
break
|
||||
m.update(data)
|
||||
return m.hexdigest()
|
||||
|
||||
|
||||
def sanitizeTrace(traceback):
|
||||
return ''.join(format_tb(traceback))\
|
||||
@@ -102,11 +97,11 @@ 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 Version('6.5.1') > Version(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:
|
||||
@@ -114,7 +109,7 @@ def dependencyCheck(level):
|
||||
if level > 1:
|
||||
try:
|
||||
from psutil import __version__ as psutilVersion
|
||||
if StrictVersion('5.0.0') > StrictVersion(psutilVersion):
|
||||
if Version('5.0.0') > Version(psutilVersion):
|
||||
missing.append('psutil 5.0.0+')
|
||||
except ImportError:
|
||||
missing.append('psutil 5.0.0+')
|
||||
@@ -123,13 +118,13 @@ def dependencyCheck(level):
|
||||
from slugify import __version__ as slugifyVersion
|
||||
if isinstance(slugifyVersion, ModuleType):
|
||||
slugifyVersion = slugifyVersion.__version__
|
||||
if StrictVersion('1.2.1') > StrictVersion(slugifyVersion):
|
||||
if Version('1.2.1') > Version(slugifyVersion):
|
||||
missing.append('python-slugify 1.2.1+')
|
||||
except ImportError:
|
||||
missing.append('python-slugify 1.2.1+')
|
||||
try:
|
||||
from PIL import __version__ as pillowVersion
|
||||
if StrictVersion('5.2.0') > StrictVersion(pillowVersion):
|
||||
if Version('5.2.0') > Version(pillowVersion):
|
||||
missing.append('Pillow 5.2.0+')
|
||||
except ImportError:
|
||||
missing.append('Pillow 5.2.0+')
|
||||
@@ -137,7 +132,20 @@ def dependencyCheck(level):
|
||||
print('ERROR: ' + ', '.join(missing) + ' is not installed!')
|
||||
sys.exit(1)
|
||||
|
||||
def subprocess_run_silent(command, **kwargs):
|
||||
@lru_cache
|
||||
def available_archive_tools():
|
||||
available = []
|
||||
|
||||
for tool in ['tar', '7z', 'unar', 'unrar']:
|
||||
try:
|
||||
subprocess_run([tool], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
available.append(tool)
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
|
||||
return available
|
||||
|
||||
def subprocess_run(command, **kwargs):
|
||||
if (os.name == 'nt'):
|
||||
kwargs.setdefault('creationflags', subprocess.CREATE_NO_WINDOW)
|
||||
return subprocess.run(command, **kwargs)
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
PyQt5>=5.6.0
|
||||
PySide6>=6.5.1
|
||||
Pillow>=5.2.0
|
||||
psutil>=5.0.0
|
||||
psutil>=5.9.5
|
||||
requests>=2.31.0
|
||||
python-slugify>=1.2.1
|
||||
raven>=6.0.0
|
||||
# PyQt5-tools
|
||||
mozjpeg-lossless-optimization>=1.1.2
|
||||
natsort[fast]>=8.4.0
|
||||
distro
|
||||
packaging>=23.2
|
||||
mozjpeg-lossless-optimization==1.2.0
|
||||
natsort>=8.4.0
|
||||
distro>=1.8.0
|
||||
numpy>=1.22.4
|
||||
|
||||
20
setup.py
20
setup.py
@@ -11,10 +11,9 @@ Create EXE/APP:
|
||||
"""
|
||||
|
||||
import os
|
||||
import platform
|
||||
import sys
|
||||
import shutil
|
||||
import setuptools
|
||||
import distutils.cmd
|
||||
from kindlecomicconverter import __version__
|
||||
|
||||
NAME = 'KindleComicConverter'
|
||||
@@ -23,7 +22,7 @@ VERSION = __version__
|
||||
|
||||
|
||||
# noinspection PyUnresolvedReferences
|
||||
class BuildBinaryCommand(distutils.cmd.Command):
|
||||
class BuildBinaryCommand(setuptools.Command):
|
||||
description = 'build binary release'
|
||||
user_options = []
|
||||
|
||||
@@ -37,16 +36,16 @@ class BuildBinaryCommand(distutils.cmd.Command):
|
||||
def run(self):
|
||||
VERSION = __version__
|
||||
if sys.platform == 'darwin':
|
||||
os.system('pyinstaller -y -D -i icons/comic2ebook.icns -n "Kindle Comic Converter" -w -s kcc.py')
|
||||
os.system('pyinstaller --hidden-import=_cffi_backend -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('appdmg kcc.json dist/KindleComicConverter_osx_' + VERSION + '.dmg')
|
||||
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_' + VERSION + ' -w --noupx kcc.py')
|
||||
os.system('pyinstaller --hidden-import=_cffi_backend -y -F -i icons\\comic2ebook.ico -n KCC_' + VERSION + ' -w --noupx kcc.py')
|
||||
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')
|
||||
'pyinstaller --hidden-import=_cffi_backend --hidden-import=queue -y -F -i icons/comic2ebook.ico -n kcc_linux_' + VERSION + ' kcc.py')
|
||||
sys.exit(0)
|
||||
else:
|
||||
sys.exit(0)
|
||||
@@ -75,15 +74,16 @@ setuptools.setup(
|
||||
},
|
||||
packages=['kindlecomicconverter'],
|
||||
install_requires=[
|
||||
'PyQt5>=5.6.0',
|
||||
'pyside6>=6.5.1',
|
||||
'Pillow>=5.2.0',
|
||||
'psutil>=5.0.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',
|
||||
'natsort>=8.4.0',
|
||||
'distro',
|
||||
'numpy>=1.22.4,<2.0.0'
|
||||
],
|
||||
classifiers=[],
|
||||
zip_safe=False,
|
||||
|
||||
Reference in New Issue
Block a user