mirror of
https://github.com/ciromattia/kcc
synced 2026-04-15 13:38:46 +00:00
Compare commits
49 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
35bba68a72 | ||
|
|
0b056a8fa8 | ||
|
|
a6f9e84251 | ||
|
|
259800e48b | ||
|
|
28e170f1d2 | ||
|
|
7120c76025 | ||
|
|
4891913b5c | ||
|
|
cd83b2899c | ||
|
|
535c2c220b | ||
|
|
409f077c3e | ||
|
|
3ecb2ba877 | ||
|
|
c07a9657ef | ||
|
|
7f719a22ad | ||
|
|
332d3d455e | ||
|
|
2070a977ae | ||
|
|
8f8d0d68a3 | ||
|
|
5a8deb4623 | ||
|
|
a7ea795df5 | ||
|
|
a2ffd259b8 | ||
|
|
93e6b51466 | ||
|
|
7904662f25 | ||
|
|
6792c2d366 | ||
|
|
ef4a91e44d | ||
|
|
a2a405e5f5 | ||
|
|
a63a46a741 | ||
|
|
2591b53a09 | ||
|
|
1c615ffc20 | ||
|
|
6eb05b3a8f | ||
|
|
968b083fb2 | ||
|
|
205907ef1e | ||
|
|
33ef8275c3 | ||
|
|
eec2099515 | ||
|
|
9027265b7c | ||
|
|
34e2af3389 | ||
|
|
5ecddaceab | ||
|
|
fe554c20aa | ||
|
|
6acf1a1802 | ||
|
|
ac81a6be4b | ||
|
|
829a5f25e7 | ||
|
|
a695a4c151 | ||
|
|
7c0b78350d | ||
|
|
aa978ab246 | ||
|
|
7524c50657 | ||
|
|
9d8663a925 | ||
|
|
08ed304f8e | ||
|
|
658d2f3281 | ||
|
|
f44bf5993e | ||
|
|
458c28281f | ||
|
|
968a1afa1d |
7
.gitignore
vendored
7
.gitignore
vendored
@@ -1,16 +1,15 @@
|
|||||||
*.pyc
|
*.pyc
|
||||||
*.cbz
|
*.cbz
|
||||||
*.cbr
|
*.cbr
|
||||||
|
*.spec
|
||||||
.idea
|
.idea
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
.python-version
|
||||||
Thumbs.db
|
Thumbs.db
|
||||||
dist
|
dist
|
||||||
Output
|
Output
|
||||||
test
|
|
||||||
solaio
|
|
||||||
kindlegen*
|
kindlegen*
|
||||||
*.spec
|
setup.bat
|
||||||
kindlecomicconverter/sentry.py
|
kindlecomicconverter/sentry.py
|
||||||
build/
|
build/
|
||||||
.python-version
|
|
||||||
KindleComicConverter.egg-info/
|
KindleComicConverter.egg-info/
|
||||||
|
|||||||
31
.travis.yml
31
.travis.yml
@@ -1,31 +1,22 @@
|
|||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- os: linux
|
|
||||||
language: python
|
|
||||||
python: 3.6
|
|
||||||
dist: trusty
|
|
||||||
sudo: required
|
|
||||||
- os: osx
|
- os: osx
|
||||||
language: generic
|
language: generic
|
||||||
osx_image: xcode6.4
|
osx_image: xcode9.2
|
||||||
|
|
||||||
before_install:
|
before_install:
|
||||||
- if [ "$TRAVIS_OS_NAME" == "linux" ] ; then sudo apt-get -y install ruby ruby-dev ; fi
|
- brew update
|
||||||
- if [ "$TRAVIS_OS_NAME" == "linux" ] ; then pip install --upgrade pip setuptools wheel ; fi
|
- brew upgrade python3
|
||||||
- if [ "$TRAVIS_OS_NAME" == "linux" ] ; then openssl aes-256-cbc -K $encrypted_a95564d8ff0d_key -iv $encrypted_a95564d8ff0d_iv -in other/linux/sentry.py.enc -out kindlecomicconverter/sentry.py -d ; fi
|
- brew uninstall node
|
||||||
- if [ "$TRAVIS_OS_NAME" == "osx" ] ; then brew update ; fi
|
- travis_wait 30 brew install node@6
|
||||||
- if [ "$TRAVIS_OS_NAME" == "osx" ] ; then brew install python3 ; fi
|
- brew link node@6 --force --overwrite
|
||||||
- if [ "$TRAVIS_OS_NAME" == "osx" ] ; then brew upgrade node ; fi
|
- pip3 install --upgrade pip setuptools wheel
|
||||||
- if [ "$TRAVIS_OS_NAME" == "osx" ] ; then pip3 install --upgrade pip setuptools wheel ; fi
|
- openssl aes-256-cbc -K $encrypted_a95564d8ff0d_key -iv $encrypted_a95564d8ff0d_iv -in other/osx/sentry.py.enc -out kindlecomicconverter/sentry.py -d
|
||||||
- if [ "$TRAVIS_OS_NAME" == "osx" ] ; then openssl aes-256-cbc -K $encrypted_a95564d8ff0d_key -iv $encrypted_a95564d8ff0d_iv -in other/osx/sentry.py.enc -out kindlecomicconverter/sentry.py -d ; fi
|
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- if [ "$TRAVIS_OS_NAME" == "linux" ] ; then pip install -r requirements.txt ; fi
|
- pip3 install -r requirements.txt
|
||||||
- if [ "$TRAVIS_OS_NAME" == "linux" ] ; then pip install certifi https://github.com/pyinstaller/pyinstaller/archive/develop.zip ; fi
|
- pip3 install certifi https://github.com/pyinstaller/pyinstaller/archive/develop.zip
|
||||||
- if [ "$TRAVIS_OS_NAME" == "linux" ] ; then gem install fpm ; fi
|
- npm install -g appdmg
|
||||||
- if [ "$TRAVIS_OS_NAME" == "osx" ] ; then pip3 install -r requirements.txt ; fi
|
|
||||||
- if [ "$TRAVIS_OS_NAME" == "osx" ] ; then pip3 install certifi https://github.com/pyinstaller/pyinstaller/archive/develop.zip ; fi
|
|
||||||
- if [ "$TRAVIS_OS_NAME" == "osx" ] ; then npm install -g appdmg ; fi
|
|
||||||
|
|
||||||
script: python3 setup.py build_binary
|
script: python3 setup.py build_binary
|
||||||
|
|
||||||
|
|||||||
25
CHANGELOG.md
25
CHANGELOG.md
@@ -1,4 +1,29 @@
|
|||||||
# CHANGELOG
|
# CHANGELOG
|
||||||
|
#### 5.5.1:
|
||||||
|
* Fixes some stability issues
|
||||||
|
|
||||||
|
#### 5.5.0:
|
||||||
|
* Added support for WebP format
|
||||||
|
* Added profiles for Kindle Paperwhite 4 and Kobo Forma
|
||||||
|
* All archives are now handled by 7z
|
||||||
|
* Removed MCD support
|
||||||
|
* Fixed multiple smaller issues
|
||||||
|
|
||||||
|
#### 5.4.5:
|
||||||
|
* Fixed EPUB output for non-Kindle devices
|
||||||
|
|
||||||
|
#### 5.4.4:
|
||||||
|
* Minor bug fixes
|
||||||
|
|
||||||
|
#### 5.4.3:
|
||||||
|
* Fixed conversion crash on Windows
|
||||||
|
|
||||||
|
#### 5.4.2:
|
||||||
|
* Added Kindle Oasis 2 profile
|
||||||
|
* Allowed metadata editor to edit directories
|
||||||
|
* Fixed image stretching when HQ Panel View option was enabled
|
||||||
|
* Fixed possible problem with directory sort order
|
||||||
|
|
||||||
#### 5.4.1:
|
#### 5.4.1:
|
||||||
* Minor bug fixes and tweaks
|
* Minor bug fixes and tweaks
|
||||||
* Implemented new binary build pipeline
|
* Implemented new binary build pipeline
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
ISC LICENSE
|
ISC LICENSE
|
||||||
|
|
||||||
Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
||||||
Copyright (c) 2013-2017 Paweł Jastrzębski <pawelj@iosphe.re>
|
Copyright (c) 2013-2019 Paweł Jastrzębski <pawelj@iosphe.re>
|
||||||
|
|
||||||
Permission to use, copy, modify, and/or distribute this software for
|
Permission to use, copy, modify, and/or distribute this software for
|
||||||
any purpose with or without fee is hereby granted, provided that the
|
any purpose with or without fee is hereby granted, provided that the
|
||||||
|
|||||||
41
README.md
41
README.md
@@ -27,9 +27,9 @@ If you find **KCC** valuable you can consider donating to the authors:
|
|||||||
|
|
||||||
## BINARY RELEASES
|
## BINARY RELEASES
|
||||||
You can find the latest released binary at the following links:
|
You can find the latest released binary at the following links:
|
||||||
- **Windows (64-bit only):** [http://kcc.iosphe.re/Windows/](http://kcc.iosphe.re/Windows/)
|
- **[Windows](http://kcc.iosphe.re/Windows/) (64-bit only)**
|
||||||
- **Linux (Glibc 2.19+):** [http://kcc.iosphe.re/Linux/](http://kcc.iosphe.re/Linux/)
|
- **[macOS](http://kcc.iosphe.re/OSX/) (10.12+)**
|
||||||
- **OS X (10.10+):** [http://kcc.iosphe.re/OSX/](http://kcc.iosphe.re/OSX/)
|
- **Linux:** Currently unavailable.
|
||||||
|
|
||||||
## PYPI
|
## PYPI
|
||||||
**KCC** is also available on PyPI.
|
**KCC** is also available on PyPI.
|
||||||
@@ -48,21 +48,20 @@ Following software is required to run Linux version of **KCC** and/or bare sourc
|
|||||||
|
|
||||||
On Debian based distributions these two commands should install all needed dependencies:
|
On Debian based distributions these two commands should install all needed dependencies:
|
||||||
```
|
```
|
||||||
sudo apt-get install python3 python3-dev python3-pip libpng-dev libjpeg-dev p7zip-full unrar
|
sudo apt-get install python3 python3-dev python3-pip libpng-dev libjpeg-dev p7zip-full
|
||||||
sudo pip3 install --upgrade pillow python-slugify psutil pyqt5 raven
|
sudo pip3 install --upgrade pillow python-slugify psutil pyqt5 raven
|
||||||
```
|
```
|
||||||
|
|
||||||
### Optional dependencies
|
### Optional dependencies
|
||||||
- [KindleGen](http://www.amazon.com/gp/feature.html?ie=UTF8&docId=1000765211) v2.9+ in a directory reachable by your _PATH_ or in _KCC_ directory *(For MOBI generation)*
|
- [KindleGen](http://www.amazon.com/gp/feature.html?ie=UTF8&docId=1000765211) v2.9+ in a directory reachable by your _PATH_ or in _KCC_ directory *(For MOBI generation)*
|
||||||
- [UnRAR](http://www.rarlab.com/download.htm) *(For CBR/RAR support)*
|
- [7z](http://www.7-zip.org/download.html) *(For CBZ/ZIP, CBR/RAR, 7z/CB7 support)*
|
||||||
- [7za](http://www.7-zip.org/download.html) *(For 7z/CB7 support)*
|
|
||||||
|
|
||||||
## INPUT FORMATS
|
## INPUT FORMATS
|
||||||
**KCC** can understand and convert, at the moment, the following input types:
|
**KCC** can understand and convert, at the moment, the following input types:
|
||||||
- Folders containing: PNG, JPG or GIF files
|
- Folders containing: PNG, JPG, GIF or WebP files
|
||||||
- CBZ, ZIP
|
- CBZ, ZIP *(With `7z` executable)*
|
||||||
- CBR, RAR *(With `unrar` executable)*
|
- CBR, RAR *(With `7z` executable)*
|
||||||
- CB7, 7Z *(With `7za` executable)*
|
- CB7, 7Z *(With `7z` executable)*
|
||||||
- PDF *(Only extracting JPG images)*
|
- PDF *(Only extracting JPG images)*
|
||||||
|
|
||||||
## USAGE
|
## USAGE
|
||||||
@@ -83,8 +82,8 @@ Options:
|
|||||||
MAIN:
|
MAIN:
|
||||||
-p PROFILE, --profile=PROFILE
|
-p PROFILE, --profile=PROFILE
|
||||||
Device profile (Available options: K1, K2, K34, K578,
|
Device profile (Available options: K1, K2, K34, K578,
|
||||||
KDX, KPW, KV, KoMT, KoG, KoGHD, KoA, KoAHD, KoAH2O,
|
KDX, KPW, KV, KO, KoMT, KoG, KoGHD, KoA, KoAHD, KoAH2O,
|
||||||
KoAO) [Default=KV]
|
KoAO, KoF) [Default=KV]
|
||||||
-m, --manga-style Manga style (right-to-left reading and splitting)
|
-m, --manga-style Manga style (right-to-left reading and splitting)
|
||||||
-q, --hq Try to increase the quality of magnification
|
-q, --hq Try to increase the quality of magnification
|
||||||
-2, --two-panel Display two not four panels in Panel View mode
|
-2, --two-panel Display two not four panels in Panel View mode
|
||||||
@@ -97,7 +96,7 @@ Options:
|
|||||||
Comic title [Default=filename or directory name]
|
Comic title [Default=filename or directory name]
|
||||||
-f FORMAT, --format=FORMAT
|
-f FORMAT, --format=FORMAT
|
||||||
Output format (Available options: Auto, MOBI, EPUB,
|
Output format (Available options: Auto, MOBI, EPUB,
|
||||||
CBZ) [Default=Auto]
|
CBZ, KFX) [Default=Auto]
|
||||||
-b BATCHSPLIT, --batchsplit=BATCHSPLIT
|
-b BATCHSPLIT, --batchsplit=BATCHSPLIT
|
||||||
Split output into multiple files. 0: Don't split 1:
|
Split output into multiple files. 0: Don't split 1:
|
||||||
Automatic mode 2: Consider every subdirectory as
|
Automatic mode 2: Consider every subdirectory as
|
||||||
@@ -157,28 +156,28 @@ This script born as a cross-platform alternative to `KindleComicParser` by **Dc5
|
|||||||
The app relies and includes the following scripts:
|
The app relies and includes the following scripts:
|
||||||
|
|
||||||
- `DualMetaFix` script by **K. Hendricks**. Released with GPL-3 License.
|
- `DualMetaFix` script by **K. Hendricks**. Released with GPL-3 License.
|
||||||
- `rarfile.py` script © 2005-2014 **Marko Kreen** <markokr@gmail.com>. Released with ISC License.
|
|
||||||
- `image.py` class from **Alex Yatskov**'s [Mangle](https://github.com/FooSoft/mangle/) with subsequent [proDOOMman](https://github.com/proDOOMman/Mangle)'s and [Birua](https://github.com/Birua/Mangle)'s patches.
|
- `image.py` class from **Alex Yatskov**'s [Mangle](https://github.com/FooSoft/mangle/) with subsequent [proDOOMman](https://github.com/proDOOMman/Mangle)'s and [Birua](https://github.com/Birua/Mangle)'s patches.
|
||||||
- Icon is by **Nikolay Verin** ([http://ncrow.deviantart.com/](http://ncrow.deviantart.com/)) and released under [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/) License.
|
- Icon is by **Nikolay Verin** ([http://ncrow.deviantart.com/](http://ncrow.deviantart.com/)) and released under [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/) License.
|
||||||
|
|
||||||
## SAMPLE FILES CREATED BY KCC
|
## SAMPLE FILES CREATED BY KCC
|
||||||
* [Kindle Paperwhite 3 / Voyage / Oasis](http://kcc.iosphe.re/Samples/Ubunchu!-KV.mobi)
|
* [Kindle Oasis 2](http://kcc.iosphe.re/Samples/Ubunchu!-KO.mobi)
|
||||||
|
* [Kindle Paperwhite 3 / 4 / Voyage / Oasis](http://kcc.iosphe.re/Samples/Ubunchu!-KV.mobi)
|
||||||
* [Kindle Paperwhite 1 / 2](http://kcc.iosphe.re/Samples/Ubunchu!-KPW.mobi)
|
* [Kindle Paperwhite 1 / 2](http://kcc.iosphe.re/Samples/Ubunchu!-KPW.mobi)
|
||||||
* [Kindle](http://kcc.iosphe.re/Samples/Ubunchu!-K45.mobi)
|
* [Kindle](http://kcc.iosphe.re/Samples/Ubunchu!-K578.mobi)
|
||||||
* [Kobo Aura](http://kcc.iosphe.re/Samples/Ubunchu-KoA.kepub.epub)
|
* [Kobo Aura](http://kcc.iosphe.re/Samples/Ubunchu-KoA.kepub.epub)
|
||||||
* [Kobo Aura HD](http://kcc.iosphe.re/Samples/Ubunchu-KoAHD.kepub.epub)
|
* [Kobo Aura HD](http://kcc.iosphe.re/Samples/Ubunchu-KoAHD.kepub.epub)
|
||||||
* [Kobo Aura H2O](http://kcc.iosphe.re/Samples/Ubunchu-KoAH2O.kepub.epub)
|
* [Kobo Aura H2O](http://kcc.iosphe.re/Samples/Ubunchu-KoAH2O.kepub.epub)
|
||||||
* [Kobo Aura ONE](http://kcc.iosphe.re/Samples/Ubunchu-KoAO.kepub.epub)
|
* [Kobo Aura ONE](http://kcc.iosphe.re/Samples/Ubunchu-KoAO.kepub.epub)
|
||||||
|
* [Kobo Forma](http://kcc.iosphe.re/Samples/Ubunchu-KoF.kepub.epub)
|
||||||
|
|
||||||
## PRIVACY
|
## PRIVACY
|
||||||
**KCC** is initiating internet connections in three cases:
|
**KCC** is initiating internet connections in two cases:
|
||||||
* During startup - Version check
|
* During startup - Version check.
|
||||||
* When MCD metadata are used - Cover download
|
* When error occurs - Automatic reporting on Windows and MacOS.
|
||||||
* When error occurs - Automatic reporting
|
|
||||||
|
|
||||||
## KNOWN ISSUES
|
## KNOWN ISSUES
|
||||||
Please check [wiki page](https://github.com/ciromattia/kcc/wiki/Known-issues).
|
Please check [wiki page](https://github.com/ciromattia/kcc/wiki/Known-issues).
|
||||||
|
|
||||||
## COPYRIGHT
|
## COPYRIGHT
|
||||||
Copyright (c) 2012-2017 Ciro Mattia Gonano and Paweł Jastrzębski.
|
Copyright (c) 2012-2019 Ciro Mattia Gonano and Paweł Jastrzębski.
|
||||||
**KCC** is released under ISC LICENSE; see LICENSE.txt for further details.
|
**KCC** is released under ISC LICENSE; see LICENSE.txt for further details.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
environment:
|
environment:
|
||||||
PYTHON: "C:\\Python36-x64"
|
PYTHON: "C:\\Python37-x64"
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- set PATH="%PYTHON%\\Scripts";"C:\\Program Files (x86)\\Inno Setup 5";%PATH%
|
- set PATH="%PYTHON%\\Scripts";"C:\\Program Files (x86)\\Inno Setup 5";%PATH%
|
||||||
@@ -7,15 +7,13 @@ install:
|
|||||||
- "%PYTHON%\\python.exe -m pip install -r requirements.txt"
|
- "%PYTHON%\\python.exe -m pip install -r requirements.txt"
|
||||||
- "%PYTHON%\\python.exe -m pip install certifi https://github.com/pyinstaller/pyinstaller/archive/develop.zip"
|
- "%PYTHON%\\python.exe -m pip install certifi https://github.com/pyinstaller/pyinstaller/archive/develop.zip"
|
||||||
- nuget install secure-file -ExcludeVersion
|
- nuget install secure-file -ExcludeVersion
|
||||||
- nuget install verpatch -ExcludeVersion
|
|
||||||
- secure-file\tools\secure-file -decrypt other\windows\Cert.pfx.enc -secret %ENCRYPTION%
|
|
||||||
- secure-file\tools\secure-file -decrypt other\windows\sentry.py.enc -out kindlecomicconverter\sentry.py -secret %ENCRYPTION%
|
- secure-file\tools\secure-file -decrypt other\windows\sentry.py.enc -out kindlecomicconverter\sentry.py -secret %ENCRYPTION%
|
||||||
|
|
||||||
build_script:
|
build_script:
|
||||||
- "%PYTHON%\\python.exe setup.py build_binary"
|
- "%PYTHON%\\python.exe setup.py build_binary"
|
||||||
|
|
||||||
after_build:
|
after_build:
|
||||||
- ps: Get-ChildItem .\dist\KindleComicConverter_win_* | % { Push-AppveyorArtifact $_.FullName -FileName $_.Name }
|
- ps: Get-ChildItem .\dist\KCC* | % { Push-AppveyorArtifact $_.FullName -FileName $_.Name }
|
||||||
|
|
||||||
deploy:
|
deploy:
|
||||||
provider: S3
|
provider: S3
|
||||||
|
|||||||
@@ -320,6 +320,9 @@
|
|||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Editor</string>
|
<string>Editor</string>
|
||||||
</property>
|
</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="icon">
|
<property name="icon">
|
||||||
<iconset resource="KCC.qrc">
|
<iconset resource="KCC.qrc">
|
||||||
<normaloff>:/Other/icons/editor.png</normaloff>:/Other/icons/editor.png</iconset>
|
<normaloff>:/Other/icons/editor.png</normaloff>:/Other/icons/editor.png</iconset>
|
||||||
@@ -454,7 +457,7 @@
|
|||||||
</font>
|
</font>
|
||||||
</property>
|
</property>
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string><html><head/><body><p style='white-space:pre'>Shift+Click to select the output directory.</p></body></html></string>
|
<string><html><head/><body><p style='white-space:pre'>Shift+Click to select the output directory.</p></body></html></string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Convert</string>
|
<string>Convert</string>
|
||||||
|
|||||||
@@ -112,19 +112,6 @@
|
|||||||
<item row="6" column="1">
|
<item row="6" column="1">
|
||||||
<widget class="QLineEdit" name="coloristLine"/>
|
<widget class="QLineEdit" name="coloristLine"/>
|
||||||
</item>
|
</item>
|
||||||
<item row="7" column="0">
|
|
||||||
<widget class="QLabel" name="label_8">
|
|
||||||
<property name="text">
|
|
||||||
<string><html><head/><body><p><a href="https://github.com/ciromattia/kcc/wiki/Manga-Cover-Database-support"><span style=" text-decoration: underline; color:#0000ff;">MUid:</span></a></p></body></html></string>
|
|
||||||
</property>
|
|
||||||
<property name="openExternalLinks">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="7" column="1">
|
|
||||||
<widget class="QLineEdit" name="muidLine"/>
|
|
||||||
</item>
|
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
||||||
# Copyright (c) 2013-2017 Pawel Jastrzebski <pawelj@iosphe.re>
|
# Copyright (c) 2013-2019 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and/or distribute this software for
|
# Permission to use, copy, modify, and/or distribute this software for
|
||||||
# any purpose with or without fee is hereby granted, provided that the
|
# any purpose with or without fee is hereby granted, provided that the
|
||||||
@@ -23,9 +23,10 @@ if sys.version_info[0] != 3:
|
|||||||
print('ERROR: This is Python 3 script!')
|
print('ERROR: This is Python 3 script!')
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
from multiprocessing import freeze_support
|
from multiprocessing import freeze_support, set_start_method
|
||||||
from kindlecomicconverter.startup import startC2E
|
from kindlecomicconverter.startup import startC2E
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
set_start_method('spawn')
|
||||||
freeze_support()
|
freeze_support()
|
||||||
startC2E()
|
startC2E()
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
||||||
# Copyright (c) 2013-2017 Pawel Jastrzebski <pawelj@iosphe.re>
|
# Copyright (c) 2013-2019 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and/or distribute this software for
|
# Permission to use, copy, modify, and/or distribute this software for
|
||||||
# any purpose with or without fee is hereby granted, provided that the
|
# any purpose with or without fee is hereby granted, provided that the
|
||||||
@@ -23,9 +23,10 @@ if sys.version_info[0] != 3:
|
|||||||
print('ERROR: This is Python 3 script!')
|
print('ERROR: This is Python 3 script!')
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
from multiprocessing import freeze_support
|
from multiprocessing import freeze_support, set_start_method
|
||||||
from kindlecomicconverter.startup import startC2P
|
from kindlecomicconverter.startup import startC2P
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
set_start_method('spawn')
|
||||||
freeze_support()
|
freeze_support()
|
||||||
startC2P()
|
startC2P()
|
||||||
|
|||||||
10
kcc.iss
10
kcc.iss
@@ -1,5 +1,5 @@
|
|||||||
#define MyAppName "Kindle Comic Converter"
|
#define MyAppName "Kindle Comic Converter"
|
||||||
#define MyAppVersion "5.4.1"
|
#define MyAppVersion "5.5.1"
|
||||||
#define MyAppPublisher "Ciro Mattia Gonano, Paweł Jastrzębski"
|
#define MyAppPublisher "Ciro Mattia Gonano, Paweł Jastrzębski"
|
||||||
#define MyAppURL "http://kcc.iosphe.re/"
|
#define MyAppURL "http://kcc.iosphe.re/"
|
||||||
#define MyAppExeName "KCC.exe"
|
#define MyAppExeName "KCC.exe"
|
||||||
@@ -12,7 +12,7 @@ AppPublisher={#MyAppPublisher}
|
|||||||
AppPublisherURL={#MyAppURL}
|
AppPublisherURL={#MyAppURL}
|
||||||
AppSupportURL={#MyAppURL}
|
AppSupportURL={#MyAppURL}
|
||||||
AppUpdatesURL={#MyAppURL}
|
AppUpdatesURL={#MyAppURL}
|
||||||
AppCopyright=Copyright (C) 2012-2017 Ciro Mattia Gonano and Paweł Jastrzębski
|
AppCopyright=Copyright (C) 2012-2019 Ciro Mattia Gonano and Paweł Jastrzębski
|
||||||
ArchitecturesAllowed=x64
|
ArchitecturesAllowed=x64
|
||||||
DefaultDirName={pf}\{#MyAppName}
|
DefaultDirName={pf}\{#MyAppName}
|
||||||
DefaultGroupName={#MyAppName}
|
DefaultGroupName={#MyAppName}
|
||||||
@@ -47,9 +47,8 @@ Name: "CB7association"; Description: "CB7"; GroupDescription: "File associations
|
|||||||
Source: "dist\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion
|
Source: "dist\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion
|
||||||
Source: "LICENSE.txt"; DestDir: "{app}"; Flags: ignoreversion solidbreak
|
Source: "LICENSE.txt"; DestDir: "{app}"; Flags: ignoreversion solidbreak
|
||||||
Source: "other\windows\Additional-LICENSE.txt"; DestDir: "{app}"; Flags: ignoreversion
|
Source: "other\windows\Additional-LICENSE.txt"; DestDir: "{app}"; Flags: ignoreversion
|
||||||
Source: "other\windows\UnRAR.exe"; DestDir: "{app}"; Flags: ignoreversion
|
Source: "other\windows\7z.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||||
Source: "other\windows\7za.exe"; DestDir: "{app}"; Flags: ignoreversion
|
Source: "other\windows\7z.dll"; DestDir: "{app}"; Flags: ignoreversion
|
||||||
Source: "other\windows\vc_redist.x64.exe"; DestDir: "{tmp}"; Flags: ignoreversion deleteafterinstall
|
|
||||||
|
|
||||||
[Icons]
|
[Icons]
|
||||||
Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
|
Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
|
||||||
@@ -57,7 +56,6 @@ Name: "{group}\Readme"; Filename: "https://github.com/ciromattia/kcc#kcc"
|
|||||||
Name: "{commondesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon
|
Name: "{commondesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon
|
||||||
|
|
||||||
[Run]
|
[Run]
|
||||||
Filename: "{tmp}\vc_redist.x64.exe"; Parameters: "/install /passive /norestart"; StatusMsg: "Installing Microsoft Visual C++ 2015 Redistributable Package..."
|
|
||||||
Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall
|
Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall
|
||||||
|
|
||||||
[Messages]
|
[Messages]
|
||||||
|
|||||||
29
kcc.py
29
kcc.py
@@ -2,7 +2,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
||||||
# Copyright (c) 2013-2017 Pawel Jastrzebski <pawelj@iosphe.re>
|
# Copyright (c) 2013-2019 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and/or distribute this software for
|
# Permission to use, copy, modify, and/or distribute this software for
|
||||||
# any purpose with or without fee is hereby granted, provided that the
|
# any purpose with or without fee is hereby granted, provided that the
|
||||||
@@ -28,29 +28,13 @@ import os
|
|||||||
if sys.platform.startswith('darwin'):
|
if sys.platform.startswith('darwin'):
|
||||||
if getattr(sys, 'frozen', False):
|
if getattr(sys, 'frozen', False):
|
||||||
os.environ['PATH'] = os.path.dirname(os.path.abspath(sys.executable)) + \
|
os.environ['PATH'] = os.path.dirname(os.path.abspath(sys.executable)) + \
|
||||||
'/../Resources:/usr/local/bin:/usr/bin:/bin'
|
'/../Resources:/usr/local/bin:/usr/bin:/bin'
|
||||||
|
os.chdir(os.path.dirname(os.path.abspath(sys.executable)) + '/../Resources')
|
||||||
os.system('defaults write com.kindlecomicconverter.KindleComicConverter ApplePersistenceIgnoreState YES')
|
os.system('defaults write com.kindlecomicconverter.KindleComicConverter ApplePersistenceIgnoreState YES')
|
||||||
os.system('defaults write com.kindlecomicconverter.KindleComicConverter NSInitialToolTipDelay -int 1000')
|
os.system('defaults write com.kindlecomicconverter.KindleComicConverter NSInitialToolTipDelay -int 1000')
|
||||||
else:
|
else:
|
||||||
os.environ['PATH'] = os.path.dirname(os.path.abspath(__file__)) + '/other/osx/:' + os.environ['PATH']
|
os.chdir(os.path.dirname(os.path.abspath(__file__)))
|
||||||
elif sys.platform.startswith('win'):
|
elif sys.platform.startswith('win'):
|
||||||
import multiprocessing.popen_spawn_win32 as forking
|
|
||||||
|
|
||||||
class _Popen(forking.Popen):
|
|
||||||
def __init__(self, *args, **kw):
|
|
||||||
if hasattr(sys, 'frozen'):
|
|
||||||
# noinspection PyUnresolvedReferences,PyProtectedMember
|
|
||||||
os.putenv('_MEIPASS2', sys._MEIPASS)
|
|
||||||
try:
|
|
||||||
super(_Popen, self).__init__(*args, **kw)
|
|
||||||
finally:
|
|
||||||
if hasattr(sys, 'frozen'):
|
|
||||||
if hasattr(os, 'unsetenv'):
|
|
||||||
os.unsetenv('_MEIPASS2')
|
|
||||||
else:
|
|
||||||
os.putenv('_MEIPASS2', '')
|
|
||||||
forking.Popen = _Popen
|
|
||||||
|
|
||||||
if getattr(sys, 'frozen', False):
|
if getattr(sys, 'frozen', False):
|
||||||
os.chdir(os.path.dirname(os.path.abspath(sys.executable)))
|
os.chdir(os.path.dirname(os.path.abspath(sys.executable)))
|
||||||
else:
|
else:
|
||||||
@@ -60,13 +44,14 @@ elif sys.platform.startswith('win'):
|
|||||||
if getattr(sys, 'frozen', False):
|
if getattr(sys, 'frozen', False):
|
||||||
try:
|
try:
|
||||||
import kindlecomicconverter.sentry
|
import kindlecomicconverter.sentry
|
||||||
except:
|
except ImportError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
from multiprocessing import freeze_support
|
from multiprocessing import freeze_support, set_start_method
|
||||||
from kindlecomicconverter.startup import start
|
from kindlecomicconverter.startup import start
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
set_start_method('spawn')
|
||||||
freeze_support()
|
freeze_support()
|
||||||
start()
|
start()
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
||||||
# Copyright (c) 2013-2017 Pawel Jastrzebski <pawelj@iosphe.re>
|
# Copyright (c) 2013-2019 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and/or distribute this software for
|
# Permission to use, copy, modify, and/or distribute this software for
|
||||||
# any purpose with or without fee is hereby granted, provided that the
|
# any purpose with or without fee is hereby granted, provided that the
|
||||||
@@ -22,8 +22,9 @@ import sys
|
|||||||
from urllib.parse import unquote
|
from urllib.parse import unquote
|
||||||
from urllib.request import urlopen, urlretrieve, Request
|
from urllib.request import urlopen, urlretrieve, Request
|
||||||
from time import sleep
|
from time import sleep
|
||||||
from shutil import move
|
from shutil import move, rmtree
|
||||||
from subprocess import STDOUT, PIPE
|
from subprocess import STDOUT, PIPE
|
||||||
|
# noinspection PyUnresolvedReferences
|
||||||
from PyQt5 import QtGui, QtCore, QtWidgets, QtNetwork
|
from PyQt5 import QtGui, QtCore, QtWidgets, QtNetwork
|
||||||
from xml.dom.minidom import parse
|
from xml.dom.minidom import parse
|
||||||
from xml.sax.saxutils import escape
|
from xml.sax.saxutils import escape
|
||||||
@@ -31,7 +32,8 @@ from psutil import Popen, Process
|
|||||||
from copy import copy
|
from copy import copy
|
||||||
from distutils.version import StrictVersion
|
from distutils.version import StrictVersion
|
||||||
from raven import Client
|
from raven import Client
|
||||||
from .shared import md5Checksum, HTMLStripper, sanitizeTrace
|
from tempfile import gettempdir
|
||||||
|
from .shared import md5Checksum, HTMLStripper, sanitizeTrace, walkLevel
|
||||||
from . import __version__
|
from . import __version__
|
||||||
from . import comic2ebook
|
from . import comic2ebook
|
||||||
from . import metadata
|
from . import metadata
|
||||||
@@ -156,8 +158,8 @@ class VersionThread(QtCore.QThread):
|
|||||||
'(<a href="https://github.com/ciromattia/kcc/releases/">'
|
'(<a href="https://github.com/ciromattia/kcc/releases/">'
|
||||||
'Changelog</a>)', 'warning', False)
|
'Changelog</a>)', 'warning', False)
|
||||||
|
|
||||||
def setAnswer(self, dialogAnswer):
|
def setAnswer(self, dialoganswer):
|
||||||
self.answer = dialogAnswer
|
self.answer = dialoganswer
|
||||||
|
|
||||||
def getNewVersion(self):
|
def getNewVersion(self):
|
||||||
while self.answer is None:
|
while self.answer is None:
|
||||||
@@ -173,15 +175,16 @@ class VersionThread(QtCore.QThread):
|
|||||||
move(path[0], path[0] + '.exe')
|
move(path[0], path[0] + '.exe')
|
||||||
MW.hideProgressBar.emit()
|
MW.hideProgressBar.emit()
|
||||||
MW.modeConvert.emit(1)
|
MW.modeConvert.emit(1)
|
||||||
Popen(path[0] + '.exe /SP- /silent /noicons', stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
|
Popen(path[0] + '.exe /SP- /silent /noicons', stdout=PIPE, stderr=STDOUT, stdin=PIPE, close_fds=True,
|
||||||
|
shell=True)
|
||||||
MW.forceShutdown.emit()
|
MW.forceShutdown.emit()
|
||||||
except Exception:
|
except Exception:
|
||||||
MW.addMessage.emit('Failed to download the update!', 'warning', False)
|
MW.addMessage.emit('Failed to download the update!', 'warning', False)
|
||||||
MW.hideProgressBar.emit()
|
MW.hideProgressBar.emit()
|
||||||
MW.modeConvert.emit(1)
|
MW.modeConvert.emit(1)
|
||||||
|
|
||||||
def getNewVersionTick(self, size, blockSize, totalSize):
|
def getNewVersionTick(self, size, blocksize, totalsize):
|
||||||
progress = int((size / (totalSize // blockSize)) * 100)
|
progress = int((size / (totalsize // blocksize)) * 100)
|
||||||
if size == 0:
|
if size == 0:
|
||||||
MW.progressBarTick.emit('100')
|
MW.progressBarTick.emit('100')
|
||||||
if progress > self.barProgress:
|
if progress > self.barProgress:
|
||||||
@@ -238,6 +241,7 @@ class WorkerThread(QtCore.QThread):
|
|||||||
MW.addTrayMessage.emit('Conversion interrupted.', 'Critical')
|
MW.addTrayMessage.emit('Conversion interrupted.', 'Critical')
|
||||||
MW.modeConvert.emit(1)
|
MW.modeConvert.emit(1)
|
||||||
|
|
||||||
|
# noinspection PyUnboundLocalVariable
|
||||||
def run(self):
|
def run(self):
|
||||||
MW.modeConvert.emit(0)
|
MW.modeConvert.emit(0)
|
||||||
|
|
||||||
@@ -477,20 +481,11 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
|||||||
if self.needClean:
|
if self.needClean:
|
||||||
self.needClean = False
|
self.needClean = False
|
||||||
GUI.jobList.clear()
|
GUI.jobList.clear()
|
||||||
if self.UnRAR:
|
if self.sevenzip:
|
||||||
if self.sevenza:
|
fnames = QtWidgets.QFileDialog.getOpenFileNames(MW, 'Select file', self.lastPath,
|
||||||
fnames = QtWidgets.QFileDialog.getOpenFileNames(MW, 'Select file', self.lastPath,
|
'Comic (*.cbz *.cbr *.cb7 *.zip *.rar *.7z *.pdf)')
|
||||||
'Comic (*.cbz *.cbr *.cb7 *.zip *.rar *.7z *.pdf)')
|
|
||||||
else:
|
|
||||||
fnames = QtWidgets.QFileDialog.getOpenFileNames(MW, 'Select file', self.lastPath,
|
|
||||||
'Comic (*.cbz *.cbr *.zip *.rar *.pdf)')
|
|
||||||
else:
|
else:
|
||||||
if self.sevenza:
|
fnames = QtWidgets.QFileDialog.getOpenFileNames(MW, 'Select file', self.lastPath, 'Comic (*.pdf)')
|
||||||
fnames = QtWidgets.QFileDialog.getOpenFileNames(MW, 'Select file', self.lastPath,
|
|
||||||
'Comic (*.cbz *.cb7 *.zip *.7z *.pdf)')
|
|
||||||
else:
|
|
||||||
fnames = QtWidgets.QFileDialog.getOpenFileNames(MW, 'Select file', self.lastPath,
|
|
||||||
'Comic (*.cbz *.zip *.pdf)')
|
|
||||||
for fname in fnames[0]:
|
for fname in fnames[0]:
|
||||||
if fname != '':
|
if fname != '':
|
||||||
if sys.platform.startswith('win'):
|
if sys.platform.startswith('win'):
|
||||||
@@ -500,28 +495,30 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
|||||||
GUI.jobList.scrollToBottom()
|
GUI.jobList.scrollToBottom()
|
||||||
|
|
||||||
def selectFileMetaEditor(self):
|
def selectFileMetaEditor(self):
|
||||||
if self.UnRAR:
|
sname = ''
|
||||||
if self.sevenza:
|
if QtWidgets.QApplication.keyboardModifiers() == QtCore.Qt.ShiftModifier:
|
||||||
|
dname = QtWidgets.QFileDialog.getExistingDirectory(MW, 'Select directory', self.lastPath)
|
||||||
|
if dname != '':
|
||||||
|
sname = os.path.join(dname, 'ComicInfo.xml')
|
||||||
|
if sys.platform.startswith('win'):
|
||||||
|
sname = sname.replace('/', '\\')
|
||||||
|
self.lastPath = os.path.abspath(sname)
|
||||||
|
else:
|
||||||
|
if self.sevenzip:
|
||||||
fname = QtWidgets.QFileDialog.getOpenFileName(MW, 'Select file', self.lastPath,
|
fname = QtWidgets.QFileDialog.getOpenFileName(MW, 'Select file', self.lastPath,
|
||||||
'Comic (*.cbz *.cbr *.cb7)')
|
'Comic (*.cbz *.cbr *.cb7)')
|
||||||
else:
|
else:
|
||||||
fname = QtWidgets.QFileDialog.getOpenFileName(MW, 'Select file', self.lastPath,
|
fname = ['']
|
||||||
'Comic (*.cbz *.cbr)')
|
self.showDialog("Editor is disabled due to a lack of 7z.", 'error')
|
||||||
else:
|
if fname[0] != '':
|
||||||
if self.sevenza:
|
if sys.platform.startswith('win'):
|
||||||
fname = QtWidgets.QFileDialog.getOpenFileName(MW, 'Select file', self.lastPath,
|
sname = fname[0].replace('/', '\\')
|
||||||
'Comic (*.cbz *.cb7)')
|
else:
|
||||||
else:
|
sname = fname[0]
|
||||||
fname = QtWidgets.QFileDialog.getOpenFileName(MW, 'Select file', self.lastPath,
|
self.lastPath = os.path.abspath(os.path.join(sname, os.pardir))
|
||||||
'Comic (*.cbz)')
|
if sname != '':
|
||||||
if fname[0] != '':
|
|
||||||
if sys.platform.startswith('win'):
|
|
||||||
fname = fname[0].replace('/', '\\')
|
|
||||||
else:
|
|
||||||
fname = fname[0]
|
|
||||||
self.lastPath = os.path.abspath(os.path.join(fname, os.pardir))
|
|
||||||
try:
|
try:
|
||||||
self.editor.loadData(fname)
|
self.editor.loadData(sname)
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
_, _, traceback = sys.exc_info()
|
_, _, traceback = sys.exc_info()
|
||||||
GUI.sentry.captureException()
|
GUI.sentry.captureException()
|
||||||
@@ -618,9 +615,9 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
|||||||
def togglequalityBox(self, value):
|
def togglequalityBox(self, value):
|
||||||
profile = GUI.profiles[str(GUI.deviceBox.currentText())]
|
profile = GUI.profiles[str(GUI.deviceBox.currentText())]
|
||||||
if value == 2:
|
if value == 2:
|
||||||
if profile['Label'] in ['KV']:
|
if profile['Label'] in ['KV', 'KO']:
|
||||||
self.addMessage('This option is intended for older Kindle models.', 'warning')
|
self.addMessage('This option is intended for older Kindle models.', 'warning')
|
||||||
self.addMessage('It will not increase quality on a device with 300 ppi screen.', 'warning')
|
self.addMessage('On this device, quality improvement will be negligible.', 'warning')
|
||||||
GUI.upscaleBox.setEnabled(False)
|
GUI.upscaleBox.setEnabled(False)
|
||||||
GUI.upscaleBox.setChecked(True)
|
GUI.upscaleBox.setChecked(True)
|
||||||
else:
|
else:
|
||||||
@@ -657,10 +654,10 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
|||||||
self.addMessage('<a href="https://github.com/ciromattia/kcc/wiki/NonKindle-devices">'
|
self.addMessage('<a href="https://github.com/ciromattia/kcc/wiki/NonKindle-devices">'
|
||||||
'List of supported Non-Kindle devices.</a>', 'info')
|
'List of supported Non-Kindle devices.</a>', 'info')
|
||||||
|
|
||||||
def changeFormat(self, outputFormat=None):
|
def changeFormat(self, outputformat=None):
|
||||||
profile = GUI.profiles[str(GUI.deviceBox.currentText())]
|
profile = GUI.profiles[str(GUI.deviceBox.currentText())]
|
||||||
if outputFormat is not None:
|
if outputformat is not None:
|
||||||
GUI.formatBox.setCurrentIndex(outputFormat)
|
GUI.formatBox.setCurrentIndex(outputformat)
|
||||||
else:
|
else:
|
||||||
GUI.formatBox.setCurrentIndex(profile['DefaultFormat'])
|
GUI.formatBox.setCurrentIndex(profile['DefaultFormat'])
|
||||||
if not GUI.webtoonBox.isChecked():
|
if not GUI.webtoonBox.isChecked():
|
||||||
@@ -802,16 +799,9 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
|||||||
if self.needClean:
|
if self.needClean:
|
||||||
self.needClean = False
|
self.needClean = False
|
||||||
GUI.jobList.clear()
|
GUI.jobList.clear()
|
||||||
if self.UnRAR:
|
formats = ['.pdf']
|
||||||
if self.sevenza:
|
if self.sevenzip:
|
||||||
formats = ['.cbz', '.cbr', '.cb7', '.zip', '.rar', '.7z', '.pdf']
|
formats.extend(['.cb7', '.7z', '.cbz', '.zip', '.cbr', '.rar'])
|
||||||
else:
|
|
||||||
formats = ['.cbz', '.cbr', '.zip', '.rar', '.pdf']
|
|
||||||
else:
|
|
||||||
if self.sevenza:
|
|
||||||
formats = ['.cbz', '.cb7', '.zip', '.7z', '.pdf']
|
|
||||||
else:
|
|
||||||
formats = ['.cbz', '.zip', '.pdf']
|
|
||||||
if os.path.isdir(message):
|
if os.path.isdir(message):
|
||||||
GUI.jobList.addItem(message)
|
GUI.jobList.addItem(message)
|
||||||
GUI.jobList.scrollToBottom()
|
GUI.jobList.scrollToBottom()
|
||||||
@@ -821,7 +811,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
|||||||
GUI.jobList.addItem(message)
|
GUI.jobList.addItem(message)
|
||||||
GUI.jobList.scrollToBottom()
|
GUI.jobList.scrollToBottom()
|
||||||
else:
|
else:
|
||||||
self.addMessage('This file type is unsupported!', 'error')
|
self.addMessage('Unsupported file type for ' + message, 'error')
|
||||||
|
|
||||||
def dragAndDrop(self, e):
|
def dragAndDrop(self, e):
|
||||||
e.accept()
|
e.accept()
|
||||||
@@ -848,7 +838,8 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
|||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
kindleGenExitCode = Popen('kindlegen -locale en', stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
|
kindleGenExitCode = Popen('kindlegen -locale en', stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
|
||||||
if kindleGenExitCode.wait() == 0:
|
kindleGenExitCode.communicate()
|
||||||
|
if kindleGenExitCode.returncode == 0:
|
||||||
self.kindleGen = True
|
self.kindleGen = True
|
||||||
versionCheck = Popen('kindlegen -locale en', stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
|
versionCheck = Popen('kindlegen -locale en', stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
|
||||||
for line in versionCheck.stdout:
|
for line in versionCheck.stdout:
|
||||||
@@ -871,10 +862,10 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
|||||||
else:
|
else:
|
||||||
self.addMessage('Download it and place executable in /usr/local/bin directory.', 'error')
|
self.addMessage('Download it and place executable in /usr/local/bin directory.', 'error')
|
||||||
|
|
||||||
def __init__(self, KCCAplication, KCCWindow):
|
def __init__(self, kccapp, kccwindow):
|
||||||
global APP, MW, GUI
|
global APP, MW, GUI
|
||||||
APP = KCCAplication
|
APP = kccapp
|
||||||
MW = KCCWindow
|
MW = kccwindow
|
||||||
GUI = self
|
GUI = self
|
||||||
self.setupUi(MW)
|
self.setupUi(MW)
|
||||||
self.editor = KCCGUI_MetaEditor()
|
self.editor = KCCGUI_MetaEditor()
|
||||||
@@ -899,6 +890,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
|||||||
self.targetDirectory = ''
|
self.targetDirectory = ''
|
||||||
self.sentry = Client(release=__version__)
|
self.sentry = Client(release=__version__)
|
||||||
if sys.platform.startswith('win'):
|
if sys.platform.startswith('win'):
|
||||||
|
# noinspection PyUnresolvedReferences
|
||||||
from psutil import BELOW_NORMAL_PRIORITY_CLASS
|
from psutil import BELOW_NORMAL_PRIORITY_CLASS
|
||||||
self.p = Process(os.getpid())
|
self.p = Process(os.getpid())
|
||||||
self.p.nice(BELOW_NORMAL_PRIORITY_CLASS)
|
self.p.nice(BELOW_NORMAL_PRIORITY_CLASS)
|
||||||
@@ -918,12 +910,14 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
|||||||
MW.resize(500, 500)
|
MW.resize(500, 500)
|
||||||
|
|
||||||
self.profiles = {
|
self.profiles = {
|
||||||
|
"Kindle Oasis 2": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
|
||||||
|
'DefaultUpscale': True, 'Label': 'KO'},
|
||||||
"Kindle Oasis": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
|
"Kindle Oasis": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
|
||||||
'DefaultUpscale': True, 'Label': 'KV'},
|
'DefaultUpscale': True, 'Label': 'KV'},
|
||||||
"Kindle Voyage": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
|
"Kindle Voyage": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
|
||||||
'DefaultUpscale': True, 'Label': 'KV'},
|
'DefaultUpscale': True, 'Label': 'KV'},
|
||||||
"Kindle PW 3": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
|
"Kindle PW 3/4": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
|
||||||
'DefaultUpscale': True, 'Label': 'KV'},
|
'DefaultUpscale': True, 'Label': 'KV'},
|
||||||
"Kindle PW 1/2": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
|
"Kindle PW 1/2": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
|
||||||
'DefaultUpscale': False, 'Label': 'KPW'},
|
'DefaultUpscale': False, 'Label': 'KPW'},
|
||||||
"Kindle": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
|
"Kindle": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
|
||||||
@@ -944,6 +938,8 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
|||||||
'DefaultUpscale': True, 'Label': 'KoAH2O'},
|
'DefaultUpscale': True, 'Label': 'KoAH2O'},
|
||||||
"Kobo Aura ONE": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1,
|
"Kobo Aura ONE": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1,
|
||||||
'DefaultUpscale': True, 'Label': 'KoAO'},
|
'DefaultUpscale': True, 'Label': 'KoAO'},
|
||||||
|
"Kobo Forma": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1,
|
||||||
|
'DefaultUpscale': True, 'Label': 'KoF'},
|
||||||
"Other": {'PVOptions': False, 'ForceExpert': True, 'DefaultFormat': 1,
|
"Other": {'PVOptions': False, 'ForceExpert': True, 'DefaultFormat': 1,
|
||||||
'DefaultUpscale': False, 'Label': 'OTHER'},
|
'DefaultUpscale': False, 'Label': 'OTHER'},
|
||||||
"Kindle 1": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 0,
|
"Kindle 1": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 0,
|
||||||
@@ -956,12 +952,14 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
|||||||
'DefaultUpscale': False, 'Label': 'K34'},
|
'DefaultUpscale': False, 'Label': 'K34'},
|
||||||
}
|
}
|
||||||
profilesGUI = [
|
profilesGUI = [
|
||||||
|
"Kindle Oasis 2",
|
||||||
"Kindle Oasis",
|
"Kindle Oasis",
|
||||||
"Kindle Voyage",
|
"Kindle Voyage",
|
||||||
"Kindle PW 3",
|
"Kindle PW 3/4",
|
||||||
"Kindle PW 1/2",
|
"Kindle PW 1/2",
|
||||||
"Kindle",
|
"Kindle",
|
||||||
"Separator",
|
"Separator",
|
||||||
|
"Kobo Forma",
|
||||||
"Kobo Aura ONE",
|
"Kobo Aura ONE",
|
||||||
"Kobo Aura H2O",
|
"Kobo Aura H2O",
|
||||||
"Kobo Aura HD",
|
"Kobo Aura HD",
|
||||||
@@ -994,22 +992,14 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
|||||||
self.addMessage('Since you are a new user of <b>KCC</b> please see few '
|
self.addMessage('Since you are a new user of <b>KCC</b> please see few '
|
||||||
'<a href="https://github.com/ciromattia/kcc/wiki/Important-tips">important tips</a>.',
|
'<a href="https://github.com/ciromattia/kcc/wiki/Important-tips">important tips</a>.',
|
||||||
'info')
|
'info')
|
||||||
rarExitCode = Popen('unrar', stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
|
process = Popen('7z', stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
|
||||||
rarExitCode = rarExitCode.wait()
|
process.communicate()
|
||||||
if rarExitCode == 0 or rarExitCode == 7:
|
if process.returncode == 0 or process.returncode == 7:
|
||||||
self.UnRAR = True
|
self.sevenzip = True
|
||||||
else:
|
else:
|
||||||
self.UnRAR = False
|
self.sevenzip = False
|
||||||
self.addMessage('Cannot find <a href="http://www.rarlab.com/rar_add.htm">UnRAR</a>!'
|
self.addMessage('Cannot find <a href="http://www.7-zip.org/download.html">7z</a>!'
|
||||||
' Processing of CBR/RAR files will be disabled.', 'warning')
|
' Processing of archives will be disabled.', 'warning')
|
||||||
sevenzaExitCode = Popen('7za', stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
|
|
||||||
sevenzaExitCode = sevenzaExitCode.wait()
|
|
||||||
if sevenzaExitCode == 0 or sevenzaExitCode == 7:
|
|
||||||
self.sevenza = True
|
|
||||||
else:
|
|
||||||
self.sevenza = False
|
|
||||||
self.addMessage('Cannot find <a href="http://www.7-zip.org/download.html">7za</a>!'
|
|
||||||
' Processing of CB7/7Z files will be disabled.', 'warning')
|
|
||||||
self.detectKindleGen(True)
|
self.detectKindleGen(True)
|
||||||
|
|
||||||
APP.messageFromOtherInstance.connect(self.handleMessage)
|
APP.messageFromOtherInstance.connect(self.handleMessage)
|
||||||
@@ -1079,6 +1069,12 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
|||||||
self.versionCheck.start()
|
self.versionCheck.start()
|
||||||
self.tray.show()
|
self.tray.show()
|
||||||
|
|
||||||
|
# Cleanup unfisnished conversion
|
||||||
|
for root, dirs, _ in walkLevel(gettempdir(), 0):
|
||||||
|
for tempdir in dirs:
|
||||||
|
if tempdir.startswith('KCC-'):
|
||||||
|
rmtree(os.path.join(root, tempdir), True)
|
||||||
|
|
||||||
if self.windowSize != '0x0':
|
if self.windowSize != '0x0':
|
||||||
x, y = self.windowSize.split('x')
|
x, y = self.windowSize.split('x')
|
||||||
MW.resize(int(x), int(y))
|
MW.resize(int(x), int(y))
|
||||||
@@ -1090,7 +1086,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
|||||||
class KCCGUI_MetaEditor(KCC_ui_editor.Ui_editorDialog):
|
class KCCGUI_MetaEditor(KCC_ui_editor.Ui_editorDialog):
|
||||||
def loadData(self, file):
|
def loadData(self, file):
|
||||||
self.parser = metadata.MetadataParser(file)
|
self.parser = metadata.MetadataParser(file)
|
||||||
if self.parser.compressor == 'rar':
|
if self.parser.format in ['RAR', 'RAR5']:
|
||||||
self.editorWidget.setEnabled(False)
|
self.editorWidget.setEnabled(False)
|
||||||
self.okButton.setEnabled(False)
|
self.okButton.setEnabled(False)
|
||||||
self.statusLabel.setText('CBR metadata are read-only.')
|
self.statusLabel.setText('CBR metadata are read-only.')
|
||||||
@@ -1098,23 +1094,20 @@ class KCCGUI_MetaEditor(KCC_ui_editor.Ui_editorDialog):
|
|||||||
self.editorWidget.setEnabled(True)
|
self.editorWidget.setEnabled(True)
|
||||||
self.okButton.setEnabled(True)
|
self.okButton.setEnabled(True)
|
||||||
self.statusLabel.setText('Separate authors with a comma.')
|
self.statusLabel.setText('Separate authors with a comma.')
|
||||||
for field in (self.seriesLine, self.volumeLine, self.numberLine, self.muidLine):
|
for field in (self.seriesLine, self.volumeLine, self.numberLine):
|
||||||
if field.objectName() == 'muidLine':
|
field.setText(self.parser.data[field.objectName().capitalize()[:-4]])
|
||||||
field.setText(self.parser.data['MUid'])
|
|
||||||
else:
|
|
||||||
field.setText(self.parser.data[field.objectName().capitalize()[:-4]])
|
|
||||||
for field in (self.writerLine, self.pencillerLine, self.inkerLine, self.coloristLine):
|
for field in (self.writerLine, self.pencillerLine, self.inkerLine, self.coloristLine):
|
||||||
field.setText(', '.join(self.parser.data[field.objectName().capitalize()[:-4] + 's']))
|
field.setText(', '.join(self.parser.data[field.objectName().capitalize()[:-4] + 's']))
|
||||||
if self.seriesLine.text() == '':
|
if self.seriesLine.text() == '':
|
||||||
self.seriesLine.setText(file.split('\\')[-1].split('/')[-1].split('.')[0])
|
if file.endswith('.xml'):
|
||||||
|
self.seriesLine.setText(file.split('\\')[-2])
|
||||||
|
else:
|
||||||
|
self.seriesLine.setText(file.split('\\')[-1].split('/')[-1].split('.')[0])
|
||||||
|
|
||||||
def saveData(self):
|
def saveData(self):
|
||||||
for field in (self.volumeLine, self.numberLine, self.muidLine):
|
for field in (self.volumeLine, self.numberLine):
|
||||||
if field.text().isnumeric() or self.cleanData(field.text()) == '':
|
if field.text().isnumeric() or self.cleanData(field.text()) == '':
|
||||||
if field.objectName() == 'muidLine':
|
self.parser.data[field.objectName().capitalize()[:-4]] = self.cleanData(field.text())
|
||||||
self.parser.data['MUid'] = self.cleanData(field.text())
|
|
||||||
else:
|
|
||||||
self.parser.data[field.objectName().capitalize()[:-4]] = self.cleanData(field.text())
|
|
||||||
else:
|
else:
|
||||||
self.statusLabel.setText(field.objectName().capitalize()[:-4] + ' field must be a number.')
|
self.statusLabel.setText(field.objectName().capitalize()[:-4] + ' field must be a number.')
|
||||||
break
|
break
|
||||||
|
|||||||
@@ -258,6 +258,7 @@ class Ui_mainWindow(object):
|
|||||||
self.colorBox.setText(_translate("mainWindow", "Color mode"))
|
self.colorBox.setText(_translate("mainWindow", "Color mode"))
|
||||||
self.gammaLabel.setText(_translate("mainWindow", "Gamma: Auto"))
|
self.gammaLabel.setText(_translate("mainWindow", "Gamma: Auto"))
|
||||||
self.editorButton.setText(_translate("mainWindow", "Editor"))
|
self.editorButton.setText(_translate("mainWindow", "Editor"))
|
||||||
|
self.editorButton.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Shift+Click to edit directory.</p></body></html>"))
|
||||||
self.wikiButton.setText(_translate("mainWindow", "Wiki"))
|
self.wikiButton.setText(_translate("mainWindow", "Wiki"))
|
||||||
self.directoryButton.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Add directory containing JPG, PNG or GIF files to queue.<br/><span style=\" font-weight:600;\">CBR, CBZ and CB7 files inside will not be processed!</span></p></body></html>"))
|
self.directoryButton.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Add directory containing JPG, PNG or GIF files to queue.<br/><span style=\" font-weight:600;\">CBR, CBZ and CB7 files inside will not be processed!</span></p></body></html>"))
|
||||||
self.directoryButton.setText(_translate("mainWindow", "Add directory"))
|
self.directoryButton.setText(_translate("mainWindow", "Add directory"))
|
||||||
|
|||||||
@@ -66,13 +66,6 @@ class Ui_editorDialog(object):
|
|||||||
self.coloristLine = QtWidgets.QLineEdit(self.editorWidget)
|
self.coloristLine = QtWidgets.QLineEdit(self.editorWidget)
|
||||||
self.coloristLine.setObjectName("coloristLine")
|
self.coloristLine.setObjectName("coloristLine")
|
||||||
self.gridLayout.addWidget(self.coloristLine, 6, 1, 1, 1)
|
self.gridLayout.addWidget(self.coloristLine, 6, 1, 1, 1)
|
||||||
self.label_8 = QtWidgets.QLabel(self.editorWidget)
|
|
||||||
self.label_8.setOpenExternalLinks(True)
|
|
||||||
self.label_8.setObjectName("label_8")
|
|
||||||
self.gridLayout.addWidget(self.label_8, 7, 0, 1, 1)
|
|
||||||
self.muidLine = QtWidgets.QLineEdit(self.editorWidget)
|
|
||||||
self.muidLine.setObjectName("muidLine")
|
|
||||||
self.gridLayout.addWidget(self.muidLine, 7, 1, 1, 1)
|
|
||||||
self.verticalLayout.addWidget(self.editorWidget)
|
self.verticalLayout.addWidget(self.editorWidget)
|
||||||
self.optionWidget = QtWidgets.QWidget(editorDialog)
|
self.optionWidget = QtWidgets.QWidget(editorDialog)
|
||||||
self.optionWidget.setObjectName("optionWidget")
|
self.optionWidget.setObjectName("optionWidget")
|
||||||
@@ -117,7 +110,6 @@ class Ui_editorDialog(object):
|
|||||||
self.label_5.setText(_translate("editorDialog", "Penciller:"))
|
self.label_5.setText(_translate("editorDialog", "Penciller:"))
|
||||||
self.label_6.setText(_translate("editorDialog", "Inker:"))
|
self.label_6.setText(_translate("editorDialog", "Inker:"))
|
||||||
self.label_7.setText(_translate("editorDialog", "Colorist:"))
|
self.label_7.setText(_translate("editorDialog", "Colorist:"))
|
||||||
self.label_8.setText(_translate("editorDialog", "<html><head/><body><p><a href=\"https://github.com/ciromattia/kcc/wiki/Manga-Cover-Database-support\"><span style=\" text-decoration: underline; color:#0000ff;\">MUid:</span></a></p></body></html>"))
|
|
||||||
self.okButton.setText(_translate("editorDialog", "Save"))
|
self.okButton.setText(_translate("editorDialog", "Save"))
|
||||||
self.cancelButton.setText(_translate("editorDialog", "Cancel"))
|
self.cancelButton.setText(_translate("editorDialog", "Cancel"))
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
__version__ = '5.4.1'
|
__version__ = '5.5.1'
|
||||||
__license__ = 'ISC'
|
__license__ = 'ISC'
|
||||||
__copyright__ = '2012-2017, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>'
|
__copyright__ = '2012-2019, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|||||||
@@ -1,90 +0,0 @@
|
|||||||
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
|
||||||
# Copyright (c) 2013-2017 Pawel Jastrzebski <pawelj@iosphe.re>
|
|
||||||
#
|
|
||||||
# Permission to use, copy, modify, and/or distribute this software for
|
|
||||||
# any purpose with or without fee is hereby granted, provided that the
|
|
||||||
# above copyright notice and this permission notice appear in all
|
|
||||||
# copies.
|
|
||||||
#
|
|
||||||
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
|
||||||
# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
|
||||||
# WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
|
|
||||||
# AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
|
||||||
# DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
|
|
||||||
# OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
|
||||||
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
||||||
# PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
#
|
|
||||||
|
|
||||||
import os
|
|
||||||
from zipfile import is_zipfile, ZipFile
|
|
||||||
from subprocess import STDOUT, PIPE
|
|
||||||
from psutil import Popen
|
|
||||||
from shutil import move
|
|
||||||
from . import rarfile
|
|
||||||
from .shared import check7ZFile as is_7zfile
|
|
||||||
|
|
||||||
|
|
||||||
class CBxArchive:
|
|
||||||
def __init__(self, origFileName):
|
|
||||||
self.origFileName = origFileName
|
|
||||||
if is_zipfile(origFileName):
|
|
||||||
self.compressor = 'zip'
|
|
||||||
elif rarfile.is_rarfile(origFileName):
|
|
||||||
self.compressor = 'rar'
|
|
||||||
elif is_7zfile(origFileName):
|
|
||||||
self.compressor = '7z'
|
|
||||||
else:
|
|
||||||
self.compressor = None
|
|
||||||
|
|
||||||
def isCbxFile(self):
|
|
||||||
return self.compressor is not None
|
|
||||||
|
|
||||||
def extractCBZ(self, targetdir):
|
|
||||||
cbzFile = ZipFile(self.origFileName)
|
|
||||||
filelist = []
|
|
||||||
for f in cbzFile.namelist():
|
|
||||||
if f.startswith('__MACOSX') or f.endswith('.DS_Store') or f.endswith('humbs.db'):
|
|
||||||
pass
|
|
||||||
elif f.endswith('/'):
|
|
||||||
try:
|
|
||||||
os.makedirs(os.path.join(targetdir, f))
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
filelist.append(f)
|
|
||||||
cbzFile.extractall(targetdir, filelist)
|
|
||||||
|
|
||||||
def extractCBR(self, targetdir):
|
|
||||||
cbrFile = rarfile.RarFile(self.origFileName)
|
|
||||||
cbrFile.extractall(targetdir)
|
|
||||||
for root, _, filenames in os.walk(targetdir):
|
|
||||||
for filename in filenames:
|
|
||||||
if filename.startswith('__MACOSX') or filename.endswith('.DS_Store') or filename.endswith('humbs.db'):
|
|
||||||
os.remove(os.path.join(root, filename))
|
|
||||||
|
|
||||||
def extractCB7(self, targetdir):
|
|
||||||
output = Popen('7za x "' + self.origFileName + '" -xr!__MACOSX -xr!.DS_Store -xr!thumbs.db -xr!Thumbs.db -o"' +
|
|
||||||
targetdir + '"', stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
|
|
||||||
extracted = False
|
|
||||||
for line in output.stdout:
|
|
||||||
if b"Everything is Ok" in line:
|
|
||||||
extracted = True
|
|
||||||
if not extracted:
|
|
||||||
raise OSError
|
|
||||||
|
|
||||||
def extract(self, targetdir):
|
|
||||||
if self.compressor == 'rar':
|
|
||||||
self.extractCBR(targetdir)
|
|
||||||
elif self.compressor == 'zip':
|
|
||||||
self.extractCBZ(targetdir)
|
|
||||||
elif self.compressor == '7z':
|
|
||||||
self.extractCB7(targetdir)
|
|
||||||
adir = os.listdir(targetdir)
|
|
||||||
if 'ComicInfo.xml' in adir:
|
|
||||||
adir.remove('ComicInfo.xml')
|
|
||||||
if len(adir) == 1 and os.path.isdir(os.path.join(targetdir, adir[0])):
|
|
||||||
for f in os.listdir(os.path.join(targetdir, adir[0])):
|
|
||||||
move(os.path.join(targetdir, adir[0], f), targetdir)
|
|
||||||
os.rmdir(os.path.join(targetdir, adir[0]))
|
|
||||||
return targetdir
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
||||||
# Copyright (c) 2013-2017 Pawel Jastrzebski <pawelj@iosphe.re>
|
# Copyright (c) 2013-2019 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and/or distribute this software for
|
# Permission to use, copy, modify, and/or distribute this software for
|
||||||
# any purpose with or without fee is hereby granted, provided that the
|
# any purpose with or without fee is hereby granted, provided that the
|
||||||
@@ -22,9 +22,7 @@ import os
|
|||||||
import sys
|
import sys
|
||||||
from time import strftime, gmtime
|
from time import strftime, gmtime
|
||||||
from copy import copy
|
from copy import copy
|
||||||
from glob import glob
|
from glob import glob, escape
|
||||||
from json import loads
|
|
||||||
from urllib.request import Request, urlopen
|
|
||||||
from re import sub
|
from re import sub
|
||||||
from stat import S_IWRITE, S_IREAD, S_IEXEC
|
from stat import S_IWRITE, S_IREAD, S_IEXEC
|
||||||
from zipfile import ZipFile, ZIP_STORED, ZIP_DEFLATED
|
from zipfile import ZipFile, ZIP_STORED, ZIP_DEFLATED
|
||||||
@@ -37,7 +35,7 @@ from slugify import slugify as slugifyExt
|
|||||||
from PIL import Image
|
from PIL import Image
|
||||||
from subprocess import STDOUT, PIPE
|
from subprocess import STDOUT, PIPE
|
||||||
from psutil import Popen, virtual_memory, disk_usage
|
from psutil import Popen, virtual_memory, disk_usage
|
||||||
from html import escape
|
from html import escape as hescape
|
||||||
try:
|
try:
|
||||||
from PyQt5 import QtCore
|
from PyQt5 import QtCore
|
||||||
except ImportError:
|
except ImportError:
|
||||||
@@ -45,7 +43,7 @@ except ImportError:
|
|||||||
from .shared import md5Checksum, getImageFileName, walkSort, walkLevel, sanitizeTrace
|
from .shared import md5Checksum, getImageFileName, walkSort, walkLevel, sanitizeTrace
|
||||||
from . import comic2panel
|
from . import comic2panel
|
||||||
from . import image
|
from . import image
|
||||||
from . import cbxarchive
|
from . import comicarchive
|
||||||
from . import pdfjpgextract
|
from . import pdfjpgextract
|
||||||
from . import dualmetafix
|
from . import dualmetafix
|
||||||
from . import metadata
|
from . import metadata
|
||||||
@@ -61,7 +59,7 @@ def main(argv=None):
|
|||||||
parser.print_help()
|
parser.print_help()
|
||||||
return 0
|
return 0
|
||||||
if sys.platform.startswith('win'):
|
if sys.platform.startswith('win'):
|
||||||
sources = set([source for arg in args for source in glob(arg)])
|
sources = set([source for arg in args for source in glob(escape(arg))])
|
||||||
else:
|
else:
|
||||||
sources = set(args)
|
sources = set(args)
|
||||||
if len(sources) == 0:
|
if len(sources) == 0:
|
||||||
@@ -88,7 +86,7 @@ def buildHTML(path, imgfile, imgfilepath):
|
|||||||
if "BlackBackground" in options.imgMetadata[imgfilepath]:
|
if "BlackBackground" in options.imgMetadata[imgfilepath]:
|
||||||
additionalStyle = 'background-color:#000000;'
|
additionalStyle = 'background-color:#000000;'
|
||||||
else:
|
else:
|
||||||
additionalStyle = 'background-color:#FFFFFF;'
|
additionalStyle = ''
|
||||||
postfix = ''
|
postfix = ''
|
||||||
backref = 1
|
backref = 1
|
||||||
head = path
|
head = path
|
||||||
@@ -104,7 +102,7 @@ def buildHTML(path, imgfile, imgfilepath):
|
|||||||
htmlfile = os.path.join(htmlpath, filename[0] + '.xhtml')
|
htmlfile = os.path.join(htmlpath, filename[0] + '.xhtml')
|
||||||
imgsize = Image.open(os.path.join(head, "Images", postfix, imgfile)).size
|
imgsize = Image.open(os.path.join(head, "Images", postfix, imgfile)).size
|
||||||
if options.hq:
|
if options.hq:
|
||||||
imgsizeframe = deviceres
|
imgsizeframe = (int(imgsize[0] // 1.5), int(imgsize[1] // 1.5))
|
||||||
else:
|
else:
|
||||||
imgsizeframe = imgsize
|
imgsizeframe = imgsize
|
||||||
f = open(htmlfile, "w", encoding='UTF-8')
|
f = open(htmlfile, "w", encoding='UTF-8')
|
||||||
@@ -112,13 +110,13 @@ def buildHTML(path, imgfile, imgfilepath):
|
|||||||
"<!DOCTYPE html>\n",
|
"<!DOCTYPE html>\n",
|
||||||
"<html xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:epub=\"http://www.idpf.org/2007/ops\">\n",
|
"<html xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:epub=\"http://www.idpf.org/2007/ops\">\n",
|
||||||
"<head>\n",
|
"<head>\n",
|
||||||
"<title>", escape(filename[0]), "</title>\n",
|
"<title>", hescape(filename[0]), "</title>\n",
|
||||||
"<link href=\"", "../" * (backref - 1), "style.css\" type=\"text/css\" rel=\"stylesheet\"/>\n",
|
"<link href=\"", "../" * (backref - 1), "style.css\" type=\"text/css\" rel=\"stylesheet\"/>\n",
|
||||||
"<meta name=\"viewport\" "
|
"<meta name=\"viewport\" "
|
||||||
"content=\"width=" + str(imgsize[0]) + ", height=" + str(imgsize[1]) + "\"/>\n"
|
"content=\"width=" + str(imgsize[0]) + ", height=" + str(imgsize[1]) + "\"/>\n"
|
||||||
"</head>\n",
|
"</head>\n",
|
||||||
"<body style=\"" + additionalStyle + "\">\n",
|
"<body style=\"" + additionalStyle + "\">\n",
|
||||||
"<div style=\"text-align:center;top:" + getTopMargin(deviceres, imgsize) + "%;\">\n",
|
"<div style=\"text-align:center;top:" + getTopMargin(deviceres, imgsizeframe) + "%;\">\n",
|
||||||
"<img width=\"" + str(imgsizeframe[0]) + "\" height=\"" + str(imgsizeframe[1]) + "\" ",
|
"<img width=\"" + str(imgsizeframe[0]) + "\" height=\"" + str(imgsizeframe[1]) + "\" ",
|
||||||
"src=\"", "../" * backref, "Images/", postfix, imgfile, "\"/>\n</div>\n"])
|
"src=\"", "../" * backref, "Images/", postfix, imgfile, "\"/>\n</div>\n"])
|
||||||
if options.iskindle and options.panelview:
|
if options.iskindle and options.panelview:
|
||||||
@@ -198,7 +196,7 @@ def buildHTML(path, imgfile, imgfilepath):
|
|||||||
return path, imgfile
|
return path, imgfile
|
||||||
|
|
||||||
|
|
||||||
def buildNCX(dstdir, title, chapters, chapterNames):
|
def buildNCX(dstdir, title, chapters, chapternames):
|
||||||
ncxfile = os.path.join(dstdir, 'OEBPS', 'toc.ncx')
|
ncxfile = os.path.join(dstdir, 'OEBPS', 'toc.ncx')
|
||||||
f = open(ncxfile, "w", encoding='UTF-8')
|
f = open(ncxfile, "w", encoding='UTF-8')
|
||||||
f.writelines(["<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n",
|
f.writelines(["<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n",
|
||||||
@@ -210,32 +208,32 @@ def buildNCX(dstdir, title, chapters, chapterNames):
|
|||||||
"<meta name=\"dtb:maxPageNumber\" content=\"0\"/>\n",
|
"<meta name=\"dtb:maxPageNumber\" content=\"0\"/>\n",
|
||||||
"<meta name=\"generated\" content=\"true\"/>\n",
|
"<meta name=\"generated\" content=\"true\"/>\n",
|
||||||
"</head>\n",
|
"</head>\n",
|
||||||
"<docTitle><text>", escape(title), "</text></docTitle>\n",
|
"<docTitle><text>", hescape(title), "</text></docTitle>\n",
|
||||||
"<navMap>\n"])
|
"<navMap>\n"])
|
||||||
for chapter in chapters:
|
for chapter in chapters:
|
||||||
folder = chapter[0].replace(os.path.join(dstdir, 'OEBPS'), '').lstrip('/').lstrip('\\\\')
|
folder = chapter[0].replace(os.path.join(dstdir, 'OEBPS'), '').lstrip('/').lstrip('\\\\')
|
||||||
filename = getImageFileName(os.path.join(folder, chapter[1]))
|
filename = getImageFileName(os.path.join(folder, chapter[1]))
|
||||||
navID = folder.replace('/', '_').replace('\\', '_')
|
navID = folder.replace('/', '_').replace('\\', '_')
|
||||||
if options.chapters:
|
if options.chapters:
|
||||||
title = chapterNames[chapter[1]]
|
title = chapternames[chapter[1]]
|
||||||
navID = filename[0].replace('/', '_').replace('\\', '_')
|
navID = filename[0].replace('/', '_').replace('\\', '_')
|
||||||
elif os.path.basename(folder) != "Text":
|
elif os.path.basename(folder) != "Text":
|
||||||
title = chapterNames[os.path.basename(folder)]
|
title = chapternames[os.path.basename(folder)]
|
||||||
f.write("<navPoint id=\"" + navID + "\"><navLabel><text>" +
|
f.write("<navPoint id=\"" + navID + "\"><navLabel><text>" +
|
||||||
escape(title) + "</text></navLabel><content src=\"" + filename[0].replace("\\", "/") +
|
hescape(title) + "</text></navLabel><content src=\"" + filename[0].replace("\\", "/") +
|
||||||
".xhtml\"/></navPoint>\n")
|
".xhtml\"/></navPoint>\n")
|
||||||
f.write("</navMap>\n</ncx>")
|
f.write("</navMap>\n</ncx>")
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
|
|
||||||
def buildNAV(dstdir, title, chapters, chapterNames):
|
def buildNAV(dstdir, title, chapters, chapternames):
|
||||||
navfile = os.path.join(dstdir, 'OEBPS', 'nav.xhtml')
|
navfile = os.path.join(dstdir, 'OEBPS', 'nav.xhtml')
|
||||||
f = open(navfile, "w", encoding='UTF-8')
|
f = open(navfile, "w", encoding='UTF-8')
|
||||||
f.writelines(["<?xml version=\"1.0\" encoding=\"utf-8\"?>\n",
|
f.writelines(["<?xml version=\"1.0\" encoding=\"utf-8\"?>\n",
|
||||||
"<!DOCTYPE html>\n",
|
"<!DOCTYPE html>\n",
|
||||||
"<html xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:epub=\"http://www.idpf.org/2007/ops\">\n",
|
"<html xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:epub=\"http://www.idpf.org/2007/ops\">\n",
|
||||||
"<head>\n",
|
"<head>\n",
|
||||||
"<title>" + escape(title) + "</title>\n",
|
"<title>" + hescape(title) + "</title>\n",
|
||||||
"<meta charset=\"utf-8\"/>\n",
|
"<meta charset=\"utf-8\"/>\n",
|
||||||
"</head>\n",
|
"</head>\n",
|
||||||
"<body>\n",
|
"<body>\n",
|
||||||
@@ -245,10 +243,10 @@ def buildNAV(dstdir, title, chapters, chapterNames):
|
|||||||
folder = chapter[0].replace(os.path.join(dstdir, 'OEBPS'), '').lstrip('/').lstrip('\\\\')
|
folder = chapter[0].replace(os.path.join(dstdir, 'OEBPS'), '').lstrip('/').lstrip('\\\\')
|
||||||
filename = getImageFileName(os.path.join(folder, chapter[1]))
|
filename = getImageFileName(os.path.join(folder, chapter[1]))
|
||||||
if options.chapters:
|
if options.chapters:
|
||||||
title = chapterNames[chapter[1]]
|
title = chapternames[chapter[1]]
|
||||||
elif os.path.basename(folder) != "Text":
|
elif os.path.basename(folder) != "Text":
|
||||||
title = chapterNames[os.path.basename(folder)]
|
title = chapternames[os.path.basename(folder)]
|
||||||
f.write("<li><a href=\"" + filename[0].replace("\\", "/") + ".xhtml\">" + escape(title) + "</a></li>\n")
|
f.write("<li><a href=\"" + filename[0].replace("\\", "/") + ".xhtml\">" + hescape(title) + "</a></li>\n")
|
||||||
f.writelines(["</ol>\n",
|
f.writelines(["</ol>\n",
|
||||||
"</nav>\n",
|
"</nav>\n",
|
||||||
"<nav epub:type=\"page-list\">\n",
|
"<nav epub:type=\"page-list\">\n",
|
||||||
@@ -257,10 +255,10 @@ def buildNAV(dstdir, title, chapters, chapterNames):
|
|||||||
folder = chapter[0].replace(os.path.join(dstdir, 'OEBPS'), '').lstrip('/').lstrip('\\\\')
|
folder = chapter[0].replace(os.path.join(dstdir, 'OEBPS'), '').lstrip('/').lstrip('\\\\')
|
||||||
filename = getImageFileName(os.path.join(folder, chapter[1]))
|
filename = getImageFileName(os.path.join(folder, chapter[1]))
|
||||||
if options.chapters:
|
if options.chapters:
|
||||||
title = chapterNames[chapter[1]]
|
title = chapternames[chapter[1]]
|
||||||
elif os.path.basename(folder) != "Text":
|
elif os.path.basename(folder) != "Text":
|
||||||
title = chapterNames[os.path.basename(folder)]
|
title = chapternames[os.path.basename(folder)]
|
||||||
f.write("<li><a href=\"" + filename[0].replace("\\", "/") + ".xhtml\">" + escape(title) + "</a></li>\n")
|
f.write("<li><a href=\"" + filename[0].replace("\\", "/") + ".xhtml\">" + hescape(title) + "</a></li>\n")
|
||||||
f.write("</ol>\n</nav>\n</body>\n</html>")
|
f.write("</ol>\n</nav>\n</body>\n</html>")
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
@@ -278,7 +276,7 @@ def buildOPF(dstdir, title, filelist, cover=None):
|
|||||||
"xmlns=\"http://www.idpf.org/2007/opf\">\n",
|
"xmlns=\"http://www.idpf.org/2007/opf\">\n",
|
||||||
"<metadata xmlns:opf=\"http://www.idpf.org/2007/opf\" ",
|
"<metadata xmlns:opf=\"http://www.idpf.org/2007/opf\" ",
|
||||||
"xmlns:dc=\"http://purl.org/dc/elements/1.1/\">\n",
|
"xmlns:dc=\"http://purl.org/dc/elements/1.1/\">\n",
|
||||||
"<dc:title>", title, "</dc:title>\n",
|
"<dc:title>", hescape(title), "</dc:title>\n",
|
||||||
"<dc:language>en-US</dc:language>\n",
|
"<dc:language>en-US</dc:language>\n",
|
||||||
"<dc:identifier id=\"BookID\">urn:uuid:", options.uuid, "</dc:identifier>\n",
|
"<dc:identifier id=\"BookID\">urn:uuid:", options.uuid, "</dc:identifier>\n",
|
||||||
"<dc:contributor id=\"contributor\">KindleComicConverter-" + __version__ + "</dc:contributor>\n"])
|
"<dc:contributor id=\"contributor\">KindleComicConverter-" + __version__ + "</dc:contributor>\n"])
|
||||||
@@ -287,20 +285,27 @@ def buildOPF(dstdir, title, filelist, cover=None):
|
|||||||
for author in options.authors:
|
for author in options.authors:
|
||||||
f.writelines(["<dc:creator>", author, "</dc:creator>\n"])
|
f.writelines(["<dc:creator>", author, "</dc:creator>\n"])
|
||||||
f.writelines(["<meta property=\"dcterms:modified\">" + strftime("%Y-%m-%dT%H:%M:%SZ", gmtime()) + "</meta>\n",
|
f.writelines(["<meta property=\"dcterms:modified\">" + strftime("%Y-%m-%dT%H:%M:%SZ", gmtime()) + "</meta>\n",
|
||||||
"<meta name=\"cover\" content=\"cover\"/>\n",
|
"<meta name=\"cover\" content=\"cover\"/>\n"])
|
||||||
"<meta property=\"rendition:orientation\">portrait</meta>\n",
|
|
||||||
"<meta property=\"rendition:spread\">portrait</meta>\n",
|
|
||||||
"<meta property=\"rendition:layout\">pre-paginated</meta>\n"])
|
|
||||||
if options.iskindle and options.profile != 'Custom':
|
if options.iskindle and options.profile != 'Custom':
|
||||||
f.writelines(["<meta name=\"original-resolution\" content=\"",
|
f.writelines(["<meta name=\"fixed-layout\" content=\"true\"/>\n",
|
||||||
|
"<meta name=\"original-resolution\" content=\"",
|
||||||
str(deviceres[0]) + "x" + str(deviceres[1]) + "\"/>\n",
|
str(deviceres[0]) + "x" + str(deviceres[1]) + "\"/>\n",
|
||||||
"<meta name=\"book-type\" content=\"comic\"/>\n",
|
"<meta name=\"book-type\" content=\"comic\"/>\n",
|
||||||
"<meta name=\"RegionMagnification\" content=\"true\"/>\n",
|
|
||||||
"<meta name=\"primary-writing-mode\" content=\"" + writingmode + "\"/>\n",
|
"<meta name=\"primary-writing-mode\" content=\"" + writingmode + "\"/>\n",
|
||||||
"<meta name=\"zero-gutter\" content=\"true\"/>\n",
|
"<meta name=\"zero-gutter\" content=\"true\"/>\n",
|
||||||
"<meta name=\"zero-margin\" content=\"true\"/>\n",
|
"<meta name=\"zero-margin\" content=\"true\"/>\n",
|
||||||
"<meta name=\"ke-border-color\" content=\"#ffffff\"/>\n",
|
"<meta name=\"ke-border-color\" content=\"#FFFFFF\"/>\n",
|
||||||
"<meta name=\"ke-border-width\" content=\"0\"/>\n"])
|
"<meta name=\"ke-border-width\" content=\"0\"/>\n"])
|
||||||
|
if options.kfx:
|
||||||
|
f.writelines(["<meta name=\"orientation-lock\" content=\"none\"/>\n",
|
||||||
|
"<meta name=\"region-mag\" content=\"false\"/>\n"])
|
||||||
|
else:
|
||||||
|
f.writelines(["<meta name=\"orientation-lock\" content=\"portrait\"/>\n",
|
||||||
|
"<meta name=\"region-mag\" content=\"true\"/>\n"])
|
||||||
|
else:
|
||||||
|
f.writelines(["<meta property=\"rendition:orientation\">portrait</meta>\n",
|
||||||
|
"<meta property=\"rendition:spread\">portrait</meta>\n",
|
||||||
|
"<meta property=\"rendition:layout\">pre-paginated</meta>\n"])
|
||||||
f.writelines(["</metadata>\n<manifest>\n<item id=\"ncx\" href=\"toc.ncx\" ",
|
f.writelines(["</metadata>\n<manifest>\n<item id=\"ncx\" href=\"toc.ncx\" ",
|
||||||
"media-type=\"application/x-dtbncx+xml\"/>\n",
|
"media-type=\"application/x-dtbncx+xml\"/>\n",
|
||||||
"<item id=\"nav\" href=\"nav.xhtml\" ",
|
"<item id=\"nav\" href=\"nav.xhtml\" ",
|
||||||
@@ -331,10 +336,43 @@ def buildOPF(dstdir, title, filelist, cover=None):
|
|||||||
f.write("<item id=\"css\" href=\"Text/style.css\" media-type=\"text/css\"/>\n")
|
f.write("<item id=\"css\" href=\"Text/style.css\" media-type=\"text/css\"/>\n")
|
||||||
if options.righttoleft:
|
if options.righttoleft:
|
||||||
f.write("</manifest>\n<spine page-progression-direction=\"rtl\" toc=\"ncx\">\n")
|
f.write("</manifest>\n<spine page-progression-direction=\"rtl\" toc=\"ncx\">\n")
|
||||||
|
pageside = "right"
|
||||||
else:
|
else:
|
||||||
f.write("</manifest>\n<spine page-progression-direction=\"ltr\" toc=\"ncx\">\n")
|
f.write("</manifest>\n<spine page-progression-direction=\"ltr\" toc=\"ncx\">\n")
|
||||||
for entry in reflist:
|
pageside = "left"
|
||||||
f.write("<itemref idref=\"page_" + entry + "\"/>\n")
|
if options.iskindle:
|
||||||
|
for entry in reflist:
|
||||||
|
if options.righttoleft:
|
||||||
|
if entry.endswith("-b"):
|
||||||
|
f.write("<itemref idref=\"page_" + entry + "\" linear=\"yes\" properties=\"page-spread-right\"/>\n")
|
||||||
|
pageside = "right"
|
||||||
|
elif entry.endswith("-c"):
|
||||||
|
f.write("<itemref idref=\"page_" + entry + "\" linear=\"yes\" properties=\"page-spread-left\"/>\n")
|
||||||
|
pageside = "right"
|
||||||
|
else:
|
||||||
|
f.write("<itemref idref=\"page_" + entry + "\" linear=\"yes\" properties=\"page-spread-" +
|
||||||
|
pageside + "\"/>\n")
|
||||||
|
if pageside == "right":
|
||||||
|
pageside = "left"
|
||||||
|
else:
|
||||||
|
pageside = "right"
|
||||||
|
else:
|
||||||
|
if entry.endswith("-b"):
|
||||||
|
f.write("<itemref idref=\"page_" + entry + "\" linear=\"yes\" properties=\"page-spread-left\"/>\n")
|
||||||
|
pageside = "left"
|
||||||
|
elif entry.endswith("-c"):
|
||||||
|
f.write("<itemref idref=\"page_" + entry + "\" linear=\"yes\" properties=\"page-spread-right\"/>\n")
|
||||||
|
pageside = "left"
|
||||||
|
else:
|
||||||
|
f.write("<itemref idref=\"page_" + entry + "\" linear=\"yes\" properties=\"page-spread-" +
|
||||||
|
pageside + "\"/>\n")
|
||||||
|
if pageside == "right":
|
||||||
|
pageside = "left"
|
||||||
|
else:
|
||||||
|
pageside = "right"
|
||||||
|
else:
|
||||||
|
for entry in reflist:
|
||||||
|
f.write("<itemref idref=\"page_" + entry + "\"/>\n")
|
||||||
f.write("</spine>\n</package>\n")
|
f.write("</spine>\n</package>\n")
|
||||||
f.close()
|
f.close()
|
||||||
os.mkdir(os.path.join(dstdir, 'META-INF'))
|
os.mkdir(os.path.join(dstdir, 'META-INF'))
|
||||||
@@ -348,7 +386,7 @@ def buildOPF(dstdir, title, filelist, cover=None):
|
|||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
|
|
||||||
def buildEPUB(path, chapterNames, tomeNumber):
|
def buildEPUB(path, chapternames, tomenumber):
|
||||||
filelist = []
|
filelist = []
|
||||||
chapterlist = []
|
chapterlist = []
|
||||||
cover = None
|
cover = None
|
||||||
@@ -361,71 +399,72 @@ def buildEPUB(path, chapterNames, tomeNumber):
|
|||||||
"display: block;\n",
|
"display: block;\n",
|
||||||
"margin: 0;\n",
|
"margin: 0;\n",
|
||||||
"padding: 0;\n",
|
"padding: 0;\n",
|
||||||
"}\n",
|
|
||||||
"#PV {\n",
|
|
||||||
"position: absolute;\n",
|
|
||||||
"width: 100%;\n",
|
|
||||||
"height: 100%;\n",
|
|
||||||
"top: 0;\n",
|
|
||||||
"left: 0;\n",
|
|
||||||
"}\n",
|
|
||||||
"#PV-T {\n",
|
|
||||||
"top: 0;\n",
|
|
||||||
"width: 100%;\n",
|
|
||||||
"height: 50%;\n",
|
|
||||||
"}\n",
|
|
||||||
"#PV-B {\n",
|
|
||||||
"bottom: 0;\n",
|
|
||||||
"width: 100%;\n",
|
|
||||||
"height: 50%;\n",
|
|
||||||
"}\n",
|
|
||||||
"#PV-L {\n",
|
|
||||||
"left: 0;\n",
|
|
||||||
"width: 49.5%;\n",
|
|
||||||
"height: 100%;\n",
|
|
||||||
"float: left;\n",
|
|
||||||
"}\n",
|
|
||||||
"#PV-R {\n",
|
|
||||||
"right: 0;\n",
|
|
||||||
"width: 49.5%;\n",
|
|
||||||
"height: 100%;\n",
|
|
||||||
"float: right;\n",
|
|
||||||
"}\n",
|
|
||||||
"#PV-TL {\n",
|
|
||||||
"top: 0;\n",
|
|
||||||
"left: 0;\n",
|
|
||||||
"width: 49.5%;\n",
|
|
||||||
"height: 50%;\n",
|
|
||||||
"float: left;\n",
|
|
||||||
"}\n",
|
|
||||||
"#PV-TR {\n",
|
|
||||||
"top: 0;\n",
|
|
||||||
"right: 0;\n",
|
|
||||||
"width: 49.5%;\n",
|
|
||||||
"height: 50%;\n",
|
|
||||||
"float: right;\n",
|
|
||||||
"}\n",
|
|
||||||
"#PV-BL {\n",
|
|
||||||
"bottom: 0;\n",
|
|
||||||
"left: 0;\n",
|
|
||||||
"width: 49.5%;\n",
|
|
||||||
"height: 50%;\n",
|
|
||||||
"float: left;\n",
|
|
||||||
"}\n",
|
|
||||||
"#PV-BR {\n",
|
|
||||||
"bottom: 0;\n",
|
|
||||||
"right: 0;\n",
|
|
||||||
"width: 49.5%;\n",
|
|
||||||
"height: 50%;\n",
|
|
||||||
"float: right;\n",
|
|
||||||
"}\n",
|
|
||||||
".PV-P {\n",
|
|
||||||
"width: 100%;\n",
|
|
||||||
"height: 100%;\n",
|
|
||||||
"top: 0;\n",
|
|
||||||
"position: absolute;\n",
|
|
||||||
"display: none;\n",
|
|
||||||
"}\n"])
|
"}\n"])
|
||||||
|
if options.iskindle and options.panelview:
|
||||||
|
f.writelines(["#PV {\n",
|
||||||
|
"position: absolute;\n",
|
||||||
|
"width: 100%;\n",
|
||||||
|
"height: 100%;\n",
|
||||||
|
"top: 0;\n",
|
||||||
|
"left: 0;\n",
|
||||||
|
"}\n",
|
||||||
|
"#PV-T {\n",
|
||||||
|
"top: 0;\n",
|
||||||
|
"width: 100%;\n",
|
||||||
|
"height: 50%;\n",
|
||||||
|
"}\n",
|
||||||
|
"#PV-B {\n",
|
||||||
|
"bottom: 0;\n",
|
||||||
|
"width: 100%;\n",
|
||||||
|
"height: 50%;\n",
|
||||||
|
"}\n",
|
||||||
|
"#PV-L {\n",
|
||||||
|
"left: 0;\n",
|
||||||
|
"width: 49.5%;\n",
|
||||||
|
"height: 100%;\n",
|
||||||
|
"float: left;\n",
|
||||||
|
"}\n",
|
||||||
|
"#PV-R {\n",
|
||||||
|
"right: 0;\n",
|
||||||
|
"width: 49.5%;\n",
|
||||||
|
"height: 100%;\n",
|
||||||
|
"float: right;\n",
|
||||||
|
"}\n",
|
||||||
|
"#PV-TL {\n",
|
||||||
|
"top: 0;\n",
|
||||||
|
"left: 0;\n",
|
||||||
|
"width: 49.5%;\n",
|
||||||
|
"height: 50%;\n",
|
||||||
|
"float: left;\n",
|
||||||
|
"}\n",
|
||||||
|
"#PV-TR {\n",
|
||||||
|
"top: 0;\n",
|
||||||
|
"right: 0;\n",
|
||||||
|
"width: 49.5%;\n",
|
||||||
|
"height: 50%;\n",
|
||||||
|
"float: right;\n",
|
||||||
|
"}\n",
|
||||||
|
"#PV-BL {\n",
|
||||||
|
"bottom: 0;\n",
|
||||||
|
"left: 0;\n",
|
||||||
|
"width: 49.5%;\n",
|
||||||
|
"height: 50%;\n",
|
||||||
|
"float: left;\n",
|
||||||
|
"}\n",
|
||||||
|
"#PV-BR {\n",
|
||||||
|
"bottom: 0;\n",
|
||||||
|
"right: 0;\n",
|
||||||
|
"width: 49.5%;\n",
|
||||||
|
"height: 50%;\n",
|
||||||
|
"float: right;\n",
|
||||||
|
"}\n",
|
||||||
|
".PV-P {\n",
|
||||||
|
"width: 100%;\n",
|
||||||
|
"height: 100%;\n",
|
||||||
|
"top: 0;\n",
|
||||||
|
"position: absolute;\n",
|
||||||
|
"display: none;\n",
|
||||||
|
"}\n"])
|
||||||
f.close()
|
f.close()
|
||||||
for dirpath, dirnames, filenames in os.walk(os.path.join(path, 'OEBPS', 'Images')):
|
for dirpath, dirnames, filenames in os.walk(os.path.join(path, 'OEBPS', 'Images')):
|
||||||
chapter = False
|
chapter = False
|
||||||
@@ -439,9 +478,9 @@ def buildEPUB(path, chapterNames, tomeNumber):
|
|||||||
cover = os.path.join(os.path.join(path, 'OEBPS', 'Images'),
|
cover = os.path.join(os.path.join(path, 'OEBPS', 'Images'),
|
||||||
'cover' + getImageFileName(filelist[-1][1])[1])
|
'cover' + getImageFileName(filelist[-1][1])[1])
|
||||||
options.covers.append((image.Cover(os.path.join(filelist[-1][0], filelist[-1][1]), cover, options,
|
options.covers.append((image.Cover(os.path.join(filelist[-1][0], filelist[-1][1]), cover, options,
|
||||||
tomeNumber), options.uuid))
|
tomenumber), options.uuid))
|
||||||
# Overwrite chapternames if tree is flat and ComicInfo.xml has bookmarks
|
# Overwrite chapternames if tree is flat and ComicInfo.xml has bookmarks
|
||||||
if not chapterNames and options.chapters:
|
if not chapternames and options.chapters:
|
||||||
chapterlist = []
|
chapterlist = []
|
||||||
globaldiff = 0
|
globaldiff = 0
|
||||||
for aChapter in options.chapters:
|
for aChapter in options.chapters:
|
||||||
@@ -453,10 +492,10 @@ def buildEPUB(path, chapterNames, tomeNumber):
|
|||||||
pageid -= 1
|
pageid -= 1
|
||||||
filename = filelist[pageid][1]
|
filename = filelist[pageid][1]
|
||||||
chapterlist.append((filelist[pageid][0].replace('Images', 'Text'), filename))
|
chapterlist.append((filelist[pageid][0].replace('Images', 'Text'), filename))
|
||||||
chapterNames[filename] = aChapter[1]
|
chapternames[filename] = aChapter[1]
|
||||||
globaldiff = pageid - (aChapter[0] + globaldiff)
|
globaldiff = pageid - (aChapter[0] + globaldiff)
|
||||||
buildNCX(path, options.title, chapterlist, chapterNames)
|
buildNCX(path, options.title, chapterlist, chapternames)
|
||||||
buildNAV(path, options.title, chapterlist, chapterNames)
|
buildNAV(path, options.title, chapterlist, chapternames)
|
||||||
buildOPF(path, options.title, filelist, cover)
|
buildOPF(path, options.title, filelist, cover)
|
||||||
|
|
||||||
|
|
||||||
@@ -542,7 +581,7 @@ def getWorkFolder(afile):
|
|||||||
copytree(afile, fullPath)
|
copytree(afile, fullPath)
|
||||||
sanitizePermissions(fullPath)
|
sanitizePermissions(fullPath)
|
||||||
return workdir
|
return workdir
|
||||||
except:
|
except Exception:
|
||||||
rmtree(workdir, True)
|
rmtree(workdir, True)
|
||||||
raise UserWarning("Failed to prepare a workspace.")
|
raise UserWarning("Failed to prepare a workspace.")
|
||||||
elif os.path.isfile(afile):
|
elif os.path.isfile(afile):
|
||||||
@@ -556,16 +595,12 @@ def getWorkFolder(afile):
|
|||||||
raise UserWarning("Failed to extract images from PDF file.")
|
raise UserWarning("Failed to extract images from PDF file.")
|
||||||
else:
|
else:
|
||||||
workdir = mkdtemp('', 'KCC-')
|
workdir = mkdtemp('', 'KCC-')
|
||||||
cbx = cbxarchive.CBxArchive(afile)
|
try:
|
||||||
if cbx.isCbxFile():
|
cbx = comicarchive.ComicArchive(afile)
|
||||||
try:
|
path = cbx.extract(workdir)
|
||||||
path = cbx.extract(workdir)
|
except OSError as e:
|
||||||
except:
|
|
||||||
rmtree(workdir, True)
|
|
||||||
raise UserWarning("Failed to extract archive.")
|
|
||||||
else:
|
|
||||||
rmtree(workdir, True)
|
rmtree(workdir, True)
|
||||||
raise UserWarning("Failed to detect archive format.")
|
raise UserWarning(e.strerror)
|
||||||
else:
|
else:
|
||||||
raise UserWarning("Failed to open source file/directory.")
|
raise UserWarning("Failed to open source file/directory.")
|
||||||
sanitizePermissions(path)
|
sanitizePermissions(path)
|
||||||
@@ -575,7 +610,7 @@ def getWorkFolder(afile):
|
|||||||
return newpath
|
return newpath
|
||||||
|
|
||||||
|
|
||||||
def getOutputFilename(srcpath, wantedname, ext, tomeNumber):
|
def getOutputFilename(srcpath, wantedname, ext, tomenumber):
|
||||||
if srcpath[-1] == os.path.sep:
|
if srcpath[-1] == os.path.sep:
|
||||||
srcpath = srcpath[:-1]
|
srcpath = srcpath[:-1]
|
||||||
if 'Ko' in options.profile and options.format == 'EPUB':
|
if 'Ko' in options.profile and options.format == 'EPUB':
|
||||||
@@ -589,16 +624,16 @@ def getOutputFilename(srcpath, wantedname, ext, tomeNumber):
|
|||||||
filename = os.path.join(os.path.abspath(options.output),
|
filename = os.path.join(os.path.abspath(options.output),
|
||||||
os.path.basename(os.path.splitext(srcpath)[0]) + ext)
|
os.path.basename(os.path.splitext(srcpath)[0]) + ext)
|
||||||
elif os.path.isdir(srcpath):
|
elif os.path.isdir(srcpath):
|
||||||
filename = srcpath + tomeNumber + ext
|
filename = srcpath + tomenumber + ext
|
||||||
else:
|
else:
|
||||||
if 'Ko' in options.profile and options.format == 'EPUB':
|
if 'Ko' in options.profile and options.format == 'EPUB':
|
||||||
path = srcpath.split(os.path.sep)
|
path = srcpath.split(os.path.sep)
|
||||||
path[-1] = ''.join(e for e in path[-1].split('.')[0] if e.isalnum()) + tomeNumber + ext
|
path[-1] = ''.join(e for e in path[-1].split('.')[0] if e.isalnum()) + tomenumber + ext
|
||||||
if not path[-1].split('.')[0]:
|
if not path[-1].split('.')[0]:
|
||||||
path[-1] = 'KCCPlaceholder' + tomeNumber + ext
|
path[-1] = 'KCCPlaceholder' + tomenumber + ext
|
||||||
filename = os.path.sep.join(path)
|
filename = os.path.sep.join(path)
|
||||||
else:
|
else:
|
||||||
filename = os.path.splitext(srcpath)[0] + tomeNumber + ext
|
filename = os.path.splitext(srcpath)[0] + tomenumber + ext
|
||||||
if os.path.isfile(filename):
|
if os.path.isfile(filename):
|
||||||
counter = 0
|
counter = 0
|
||||||
basename = os.path.splitext(filename)[0]
|
basename = os.path.splitext(filename)[0]
|
||||||
@@ -608,19 +643,18 @@ def getOutputFilename(srcpath, wantedname, ext, tomeNumber):
|
|||||||
return filename
|
return filename
|
||||||
|
|
||||||
|
|
||||||
def getComicInfo(path, originalPath):
|
def getComicInfo(path, originalpath):
|
||||||
xmlPath = os.path.join(path, 'ComicInfo.xml')
|
xmlPath = os.path.join(path, 'ComicInfo.xml')
|
||||||
options.authors = ['KCC']
|
options.authors = ['KCC']
|
||||||
options.remoteCovers = {}
|
|
||||||
options.chapters = []
|
options.chapters = []
|
||||||
options.summary = ''
|
options.summary = ''
|
||||||
titleSuffix = ''
|
titleSuffix = ''
|
||||||
if options.title == 'defaulttitle':
|
if options.title == 'defaulttitle':
|
||||||
defaultTitle = True
|
defaultTitle = True
|
||||||
if os.path.isdir(originalPath):
|
if os.path.isdir(originalpath):
|
||||||
options.title = os.path.basename(originalPath)
|
options.title = os.path.basename(originalpath)
|
||||||
else:
|
else:
|
||||||
options.title = os.path.splitext(os.path.basename(originalPath))[0]
|
options.title = os.path.splitext(os.path.basename(originalpath))[0]
|
||||||
else:
|
else:
|
||||||
defaultTitle = False
|
defaultTitle = False
|
||||||
if os.path.exists(xmlPath):
|
if os.path.exists(xmlPath):
|
||||||
@@ -632,7 +666,7 @@ def getComicInfo(path, originalPath):
|
|||||||
options.authors = []
|
options.authors = []
|
||||||
if defaultTitle:
|
if defaultTitle:
|
||||||
if xml.data['Series']:
|
if xml.data['Series']:
|
||||||
options.title = escape(xml.data['Series'])
|
options.title = hescape(xml.data['Series'])
|
||||||
if xml.data['Volume']:
|
if xml.data['Volume']:
|
||||||
titleSuffix += ' V' + xml.data['Volume'].zfill(2)
|
titleSuffix += ' V' + xml.data['Volume'].zfill(2)
|
||||||
if xml.data['Number']:
|
if xml.data['Number']:
|
||||||
@@ -640,35 +674,19 @@ def getComicInfo(path, originalPath):
|
|||||||
options.title += titleSuffix
|
options.title += titleSuffix
|
||||||
for field in ['Writers', 'Pencillers', 'Inkers', 'Colorists']:
|
for field in ['Writers', 'Pencillers', 'Inkers', 'Colorists']:
|
||||||
for person in xml.data[field]:
|
for person in xml.data[field]:
|
||||||
options.authors.append(escape(person))
|
options.authors.append(hescape(person))
|
||||||
if len(options.authors) > 0:
|
if len(options.authors) > 0:
|
||||||
options.authors = list(set(options.authors))
|
options.authors = list(set(options.authors))
|
||||||
options.authors.sort()
|
options.authors.sort()
|
||||||
else:
|
else:
|
||||||
options.authors = ['KCC']
|
options.authors = ['KCC']
|
||||||
if xml.data['MUid']:
|
|
||||||
options.remoteCovers = getCoversFromMCB(xml.data['MUid'])
|
|
||||||
if xml.data['Bookmarks']:
|
if xml.data['Bookmarks']:
|
||||||
options.chapters = xml.data['Bookmarks']
|
options.chapters = xml.data['Bookmarks']
|
||||||
if xml.data['Summary']:
|
if xml.data['Summary']:
|
||||||
options.summary = escape(xml.data['Summary'])
|
options.summary = hescape(xml.data['Summary'])
|
||||||
os.remove(xmlPath)
|
os.remove(xmlPath)
|
||||||
|
|
||||||
|
|
||||||
def getCoversFromMCB(mangaID):
|
|
||||||
covers = {}
|
|
||||||
try:
|
|
||||||
jsonRaw = urlopen(Request('http://mcd.iosphe.re/api/v1/series/' + mangaID + '/',
|
|
||||||
headers={'User-Agent': 'KindleComicConverter/' + __version__}))
|
|
||||||
jsonData = loads(jsonRaw.read().decode('utf-8'))
|
|
||||||
for volume in jsonData['Covers']['a']:
|
|
||||||
if volume['Side'] == 'front':
|
|
||||||
covers[int(volume['Volume'])] = volume['Raw']
|
|
||||||
except Exception:
|
|
||||||
return {}
|
|
||||||
return covers
|
|
||||||
|
|
||||||
|
|
||||||
def getDirectorySize(start_path='.'):
|
def getDirectorySize(start_path='.'):
|
||||||
total_size = 0
|
total_size = 0
|
||||||
for dirpath, _, filenames in os.walk(start_path):
|
for dirpath, _, filenames in os.walk(start_path):
|
||||||
@@ -683,9 +701,9 @@ def getTopMargin(deviceres, size):
|
|||||||
return str(round(y, 1))
|
return str(round(y, 1))
|
||||||
|
|
||||||
|
|
||||||
def getPanelViewResolution(imageSize, deviceRes):
|
def getPanelViewResolution(imagesize, deviceres):
|
||||||
scale = float(deviceRes[0]) / float(imageSize[0])
|
scale = float(deviceres[0]) / float(imagesize[0])
|
||||||
return int(deviceRes[0]), int(scale * imageSize[1])
|
return int(deviceres[0]), int(scale * imagesize[1])
|
||||||
|
|
||||||
|
|
||||||
def getPanelViewSize(deviceres, size):
|
def getPanelViewSize(deviceres, size):
|
||||||
@@ -699,7 +717,7 @@ def sanitizeTree(filetree):
|
|||||||
for root, dirs, files in os.walk(filetree, False):
|
for root, dirs, files in os.walk(filetree, False):
|
||||||
for name in files:
|
for name in files:
|
||||||
splitname = os.path.splitext(name)
|
splitname = os.path.splitext(name)
|
||||||
slugified = slugify(splitname[0])
|
slugified = slugify(splitname[0], False)
|
||||||
while os.path.exists(os.path.join(root, slugified + splitname[1])) and splitname[0].upper()\
|
while os.path.exists(os.path.join(root, slugified + splitname[1])) and splitname[0].upper()\
|
||||||
!= slugified.upper():
|
!= slugified.upper():
|
||||||
slugified += "A"
|
slugified += "A"
|
||||||
@@ -709,7 +727,7 @@ def sanitizeTree(filetree):
|
|||||||
os.replace(key, newKey)
|
os.replace(key, newKey)
|
||||||
for name in dirs:
|
for name in dirs:
|
||||||
tmpName = name
|
tmpName = name
|
||||||
slugified = slugify(name)
|
slugified = slugify(name, True)
|
||||||
while os.path.exists(os.path.join(root, slugified)) and name.upper() != slugified.upper():
|
while os.path.exists(os.path.join(root, slugified)) and name.upper() != slugified.upper():
|
||||||
slugified += "A"
|
slugified += "A"
|
||||||
chapterNames[slugified] = tmpName
|
chapterNames[slugified] = tmpName
|
||||||
@@ -749,7 +767,8 @@ def splitDirectory(path):
|
|||||||
level = -1
|
level = -1
|
||||||
for root, _, files in os.walk(os.path.join(path, 'OEBPS', 'Images')):
|
for root, _, files in os.walk(os.path.join(path, 'OEBPS', 'Images')):
|
||||||
for f in files:
|
for f in files:
|
||||||
if f.endswith('.jpg') or f.endswith('.jpeg') or f.endswith('.png') or f.endswith('.gif'):
|
if f.endswith('.jpg') or f.endswith('.jpeg') or f.endswith('.png') or f.endswith('.gif') or \
|
||||||
|
f.endswith('.webp'):
|
||||||
newLevel = os.path.join(root, f).replace(os.path.join(path, 'OEBPS', 'Images'), '').count(os.sep)
|
newLevel = os.path.join(root, f).replace(os.path.join(path, 'OEBPS', 'Images'), '').count(os.sep)
|
||||||
if level != -1 and level != newLevel:
|
if level != -1 and level != newLevel:
|
||||||
level = 0
|
level = 0
|
||||||
@@ -804,19 +823,19 @@ def splitProcess(path, mode):
|
|||||||
return output
|
return output
|
||||||
|
|
||||||
|
|
||||||
def detectCorruption(tmpPath, orgPath):
|
def detectCorruption(tmppath, orgpath):
|
||||||
imageNumber = 0
|
imageNumber = 0
|
||||||
imageSmaller = 0
|
imageSmaller = 0
|
||||||
alreadyProcessed = False
|
alreadyProcessed = False
|
||||||
for root, _, files in os.walk(tmpPath, False):
|
for root, _, files in os.walk(tmppath, False):
|
||||||
for name in files:
|
for name in files:
|
||||||
if getImageFileName(name) is not None:
|
if getImageFileName(name) is not None:
|
||||||
if not alreadyProcessed and getImageFileName(name)[0].endswith('-kcc'):
|
if not alreadyProcessed and getImageFileName(name)[0].endswith('-kcc'):
|
||||||
alreadyProcessed = True
|
alreadyProcessed = True
|
||||||
path = os.path.join(root, name)
|
path = os.path.join(root, name)
|
||||||
pathOrg = orgPath + path.split('OEBPS' + os.path.sep + 'Images')[1]
|
pathOrg = orgpath + path.split('OEBPS' + os.path.sep + 'Images')[1]
|
||||||
if os.path.getsize(path) == 0:
|
if os.path.getsize(path) == 0:
|
||||||
rmtree(os.path.join(tmpPath, '..', '..'), True)
|
rmtree(os.path.join(tmppath, '..', '..'), True)
|
||||||
raise RuntimeError('Image file %s is corrupted.' % pathOrg)
|
raise RuntimeError('Image file %s is corrupted.' % pathOrg)
|
||||||
try:
|
try:
|
||||||
img = Image.open(path)
|
img = Image.open(path)
|
||||||
@@ -827,11 +846,11 @@ def detectCorruption(tmpPath, orgPath):
|
|||||||
if options.profileData[1][0] > img.size[0] and options.profileData[1][1] > img.size[1]:
|
if options.profileData[1][0] > img.size[0] and options.profileData[1][1] > img.size[1]:
|
||||||
imageSmaller += 1
|
imageSmaller += 1
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
rmtree(os.path.join(tmpPath, '..', '..'), True)
|
rmtree(os.path.join(tmppath, '..', '..'), True)
|
||||||
if 'decoder' in str(err) and 'not available' in str(err):
|
if 'decoder' in str(err) and 'not available' in str(err):
|
||||||
raise RuntimeError('Pillow was compiled without JPG and/or PNG decoder.')
|
raise RuntimeError('Pillow was compiled without JPG and/or PNG decoder.')
|
||||||
else:
|
else:
|
||||||
raise RuntimeError('Image file %s is corrupted.' % pathOrg)
|
raise RuntimeError('Image file %s is corrupted. Error: %s' % (pathOrg, str(err)))
|
||||||
else:
|
else:
|
||||||
os.remove(os.path.join(root, name))
|
os.remove(os.path.join(root, name))
|
||||||
if alreadyProcessed:
|
if alreadyProcessed:
|
||||||
@@ -856,25 +875,28 @@ def createNewTome():
|
|||||||
return tomePath, tomePathRoot
|
return tomePath, tomePathRoot
|
||||||
|
|
||||||
|
|
||||||
def slugify(value):
|
def slugify(value, isdir):
|
||||||
value = slugifyExt(value)
|
if isdir:
|
||||||
|
value = slugifyExt(value, regex_pattern=r'[^-a-z0-9_\.]+').strip('.')
|
||||||
|
else:
|
||||||
|
value = slugifyExt(value).strip('.')
|
||||||
value = sub(r'0*([0-9]{4,})', r'\1', sub(r'([0-9]+)', r'0000\1', value, count=2))
|
value = sub(r'0*([0-9]{4,})', r'\1', sub(r'([0-9]+)', r'0000\1', value, count=2))
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
def makeZIP(zipFilename, baseDir, isEPUB=False):
|
def makeZIP(zipfilename, basedir, isepub=False):
|
||||||
zipFilename = os.path.abspath(zipFilename) + '.zip'
|
zipfilename = os.path.abspath(zipfilename) + '.zip'
|
||||||
zipOutput = ZipFile(zipFilename, 'w', ZIP_DEFLATED)
|
zipOutput = ZipFile(zipfilename, 'w', ZIP_DEFLATED)
|
||||||
if isEPUB:
|
if isepub:
|
||||||
zipOutput.writestr('mimetype', 'application/epub+zip', ZIP_STORED)
|
zipOutput.writestr('mimetype', 'application/epub+zip', ZIP_STORED)
|
||||||
for dirpath, _, filenames in os.walk(baseDir):
|
for dirpath, _, filenames in os.walk(basedir):
|
||||||
for name in filenames:
|
for name in filenames:
|
||||||
path = os.path.normpath(os.path.join(dirpath, name))
|
path = os.path.normpath(os.path.join(dirpath, name))
|
||||||
aPath = os.path.normpath(os.path.join(dirpath.replace(baseDir, ''), name))
|
aPath = os.path.normpath(os.path.join(dirpath.replace(basedir, ''), name))
|
||||||
if os.path.isfile(path):
|
if os.path.isfile(path):
|
||||||
zipOutput.write(path, aPath)
|
zipOutput.write(path, aPath)
|
||||||
zipOutput.close()
|
zipOutput.close()
|
||||||
return zipFilename
|
return zipfilename
|
||||||
|
|
||||||
|
|
||||||
def makeParser():
|
def makeParser():
|
||||||
@@ -887,8 +909,8 @@ def makeParser():
|
|||||||
otherOptions = OptionGroup(psr, "OTHER")
|
otherOptions = OptionGroup(psr, "OTHER")
|
||||||
|
|
||||||
mainOptions.add_option("-p", "--profile", action="store", dest="profile", default="KV",
|
mainOptions.add_option("-p", "--profile", action="store", dest="profile", default="KV",
|
||||||
help="Device profile (Available options: K1, K2, K34, K578, KDX, KPW, KV, KoMT, KoG, KoGHD,"
|
help="Device profile (Available options: K1, K2, K34, K578, KDX, KPW, KV, KO, KoMT, KoG,"
|
||||||
" KoA, KoAHD, KoAH2O, KoAO) [Default=KV]")
|
" KoGHD, KoA, KoAHD, KoAH2O, KoAO, KoF) [Default=KV]")
|
||||||
mainOptions.add_option("-m", "--manga-style", action="store_true", dest="righttoleft", default=False,
|
mainOptions.add_option("-m", "--manga-style", action="store_true", dest="righttoleft", default=False,
|
||||||
help="Manga style (right-to-left reading and splitting)")
|
help="Manga style (right-to-left reading and splitting)")
|
||||||
mainOptions.add_option("-q", "--hq", action="store_true", dest="hq", default=False,
|
mainOptions.add_option("-q", "--hq", action="store_true", dest="hq", default=False,
|
||||||
@@ -903,7 +925,7 @@ def makeParser():
|
|||||||
outputOptions.add_option("-t", "--title", action="store", dest="title", default="defaulttitle",
|
outputOptions.add_option("-t", "--title", action="store", dest="title", default="defaulttitle",
|
||||||
help="Comic title [Default=filename or directory name]")
|
help="Comic title [Default=filename or directory name]")
|
||||||
outputOptions.add_option("-f", "--format", action="store", dest="format", default="Auto",
|
outputOptions.add_option("-f", "--format", action="store", dest="format", default="Auto",
|
||||||
help="Output format (Available options: Auto, MOBI, EPUB, CBZ) [Default=Auto]")
|
help="Output format (Available options: Auto, MOBI, EPUB, CBZ, KFX) [Default=Auto]")
|
||||||
outputOptions.add_option("-b", "--batchsplit", type="int", dest="batchsplit", default="0",
|
outputOptions.add_option("-b", "--batchsplit", type="int", dest="batchsplit", default="0",
|
||||||
help="Split output into multiple files. 0: Don't split 1: Automatic mode "
|
help="Split output into multiple files. 0: Don't split 1: Automatic mode "
|
||||||
"2: Consider every subdirectory as separate volume [Default=0]")
|
"2: Consider every subdirectory as separate volume [Default=0]")
|
||||||
@@ -950,21 +972,22 @@ def checkOptions():
|
|||||||
options.panelview = True
|
options.panelview = True
|
||||||
options.iskindle = False
|
options.iskindle = False
|
||||||
options.bordersColor = None
|
options.bordersColor = None
|
||||||
|
options.kfx = False
|
||||||
if options.format == 'Auto':
|
if options.format == 'Auto':
|
||||||
if options.profile in ['K1', 'K2', 'K34', 'K578', 'KPW', 'KV']:
|
if options.profile in ['K1', 'K2', 'K34', 'K578', 'KPW', 'KV', 'KO']:
|
||||||
options.format = 'MOBI'
|
options.format = 'MOBI'
|
||||||
elif options.profile in ['OTHER', 'KoMT', 'KoG', 'KoGHD', 'KoA', 'KoAHD', 'KoAH2O', 'KoAO']:
|
elif options.profile in ['OTHER', 'KoMT', 'KoG', 'KoGHD', 'KoA', 'KoAHD', 'KoAH2O', 'KoAO']:
|
||||||
options.format = 'EPUB'
|
options.format = 'EPUB'
|
||||||
elif options.profile in ['KDX']:
|
elif options.profile in ['KDX']:
|
||||||
options.format = 'CBZ'
|
options.format = 'CBZ'
|
||||||
if options.profile in ['K1', 'K2', 'K34', 'K578', 'KPW', 'KV']:
|
if options.profile in ['K1', 'K2', 'K34', 'K578', 'KPW', 'KV', 'KO']:
|
||||||
options.iskindle = True
|
options.iskindle = True
|
||||||
if options.white_borders:
|
if options.white_borders:
|
||||||
options.bordersColor = 'white'
|
options.bordersColor = 'white'
|
||||||
if options.black_borders:
|
if options.black_borders:
|
||||||
options.bordersColor = 'black'
|
options.bordersColor = 'black'
|
||||||
# Splitting MOBI is not optional
|
# Splitting MOBI is not optional
|
||||||
if options.format == 'MOBI' and options.batchsplit != 2:
|
if (options.format == 'MOBI' or options.format == 'KFX') and options.batchsplit != 2:
|
||||||
options.batchsplit = 1
|
options.batchsplit = 1
|
||||||
# Older Kindle models don't support Panel View.
|
# Older Kindle models don't support Panel View.
|
||||||
if options.profile == 'K1' or options.profile == 'K2' or options.profile == 'K34' or options.profile == 'KDX':
|
if options.profile == 'K1' or options.profile == 'K2' or options.profile == 'K34' or options.profile == 'KDX':
|
||||||
@@ -986,6 +1009,11 @@ def checkOptions():
|
|||||||
# CBZ files on Kindle DX/DXG support higher resolution
|
# CBZ files on Kindle DX/DXG support higher resolution
|
||||||
if options.profile == 'KDX' and options.format == 'CBZ':
|
if options.profile == 'KDX' and options.format == 'CBZ':
|
||||||
options.customheight = 1200
|
options.customheight = 1200
|
||||||
|
# KFX output create EPUB that might be can be by jhowell KFX Output Calibre plugin
|
||||||
|
if options.format == 'KFX':
|
||||||
|
options.format = 'EPUB'
|
||||||
|
options.kfx = True
|
||||||
|
options.panelview = False
|
||||||
# Override profile data
|
# Override profile data
|
||||||
if options.customwidth != 0 or options.customheight != 0:
|
if options.customwidth != 0 or options.customheight != 0:
|
||||||
X = image.ProfileData.Profiles[options.profile][1][0]
|
X = image.ProfileData.Profiles[options.profile][1][0]
|
||||||
@@ -1003,21 +1031,17 @@ def checkOptions():
|
|||||||
|
|
||||||
def checkTools(source):
|
def checkTools(source):
|
||||||
source = source.upper()
|
source = source.upper()
|
||||||
if source.endswith('.CBR') or source.endswith('.RAR'):
|
if source.endswith('.CB7') or source.endswith('.7Z') or source.endswith('.RAR') or source.endswith('.CBR') or \
|
||||||
rarExitCode = Popen('unrar', stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
|
source.endswith('.ZIP') or source.endswith('.CBZ'):
|
||||||
rarExitCode = rarExitCode.wait()
|
process = Popen('7z', stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
|
||||||
if rarExitCode != 0 and rarExitCode != 7:
|
process.communicate()
|
||||||
print('ERROR: UnRAR is missing!')
|
if process.returncode != 0 and process.returncode != 7:
|
||||||
exit(1)
|
print('ERROR: 7z is missing!')
|
||||||
elif source.endswith('.CB7') or source.endswith('.7Z'):
|
|
||||||
sevenzaExitCode = Popen('7za', stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
|
|
||||||
sevenzaExitCode = sevenzaExitCode.wait()
|
|
||||||
if sevenzaExitCode != 0 and sevenzaExitCode != 7:
|
|
||||||
print('ERROR: 7za is missing!')
|
|
||||||
exit(1)
|
exit(1)
|
||||||
if options.format == 'MOBI':
|
if options.format == 'MOBI':
|
||||||
kindleGenExitCode = Popen('kindlegen -locale en', stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
|
kindleGenExitCode = Popen('kindlegen -locale en', stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
|
||||||
if kindleGenExitCode.wait() != 0:
|
kindleGenExitCode.communicate()
|
||||||
|
if kindleGenExitCode.returncode != 0:
|
||||||
print('ERROR: KindleGen is missing!')
|
print('ERROR: KindleGen is missing!')
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
@@ -1036,13 +1060,13 @@ def checkPre(source):
|
|||||||
try:
|
try:
|
||||||
with TemporaryFile(prefix='KCC-', dir=src):
|
with TemporaryFile(prefix='KCC-', dir=src):
|
||||||
pass
|
pass
|
||||||
except:
|
except Exception:
|
||||||
raise UserWarning("Target directory is not writable.")
|
raise UserWarning("Target directory is not writable.")
|
||||||
|
|
||||||
|
|
||||||
def makeBook(source, qtGUI=None):
|
def makeBook(source, qtgui=None):
|
||||||
global GUI
|
global GUI
|
||||||
GUI = qtGUI
|
GUI = qtgui
|
||||||
if GUI:
|
if GUI:
|
||||||
GUI.progressBarTick.emit('1')
|
GUI.progressBarTick.emit('1')
|
||||||
else:
|
else:
|
||||||
@@ -1058,7 +1082,7 @@ def makeBook(source, qtGUI=None):
|
|||||||
y = 1024
|
y = 1024
|
||||||
else:
|
else:
|
||||||
y = image.ProfileData.Profiles[options.profile][1][1]
|
y = image.ProfileData.Profiles[options.profile][1][1]
|
||||||
comic2panel.main(['-y ' + str(y), '-i', '-m', path], qtGUI)
|
comic2panel.main(['-y ' + str(y), '-i', '-m', path], qtgui)
|
||||||
print("Processing images...")
|
print("Processing images...")
|
||||||
if GUI:
|
if GUI:
|
||||||
GUI.progressBarTick.emit('Processing images')
|
GUI.progressBarTick.emit('Processing images')
|
||||||
@@ -1189,9 +1213,9 @@ def makeMOBIWorker(item):
|
|||||||
return [kindlegenErrorCode, kindlegenError, item]
|
return [kindlegenErrorCode, kindlegenError, item]
|
||||||
|
|
||||||
|
|
||||||
def makeMOBI(work, qtGUI=None):
|
def makeMOBI(work, qtgui=None):
|
||||||
global GUI, makeMOBIWorkerPool, makeMOBIWorkerOutput
|
global GUI, makeMOBIWorkerPool, makeMOBIWorkerOutput
|
||||||
GUI = qtGUI
|
GUI = qtgui
|
||||||
makeMOBIWorkerOutput = []
|
makeMOBIWorkerOutput = []
|
||||||
availableMemory = virtual_memory().total / 1000000000
|
availableMemory = virtual_memory().total / 1000000000
|
||||||
if availableMemory <= 2:
|
if availableMemory <= 2:
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
||||||
# Copyright (c) 2013-2017 Pawel Jastrzebski <pawelj@iosphe.re>
|
# Copyright (c) 2013-2019 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and/or distribute this software for
|
# Permission to use, copy, modify, and/or distribute this software for
|
||||||
# any purpose with or without fee is hereby granted, provided that the
|
# any purpose with or without fee is hereby granted, provided that the
|
||||||
@@ -93,6 +93,7 @@ def splitImageTick(output):
|
|||||||
splitWorkerPool.terminate()
|
splitWorkerPool.terminate()
|
||||||
|
|
||||||
|
|
||||||
|
# noinspection PyUnboundLocalVariable
|
||||||
def splitImage(work):
|
def splitImage(work):
|
||||||
try:
|
try:
|
||||||
path = work[0]
|
path = work[0]
|
||||||
@@ -140,9 +141,7 @@ def splitImage(work):
|
|||||||
|
|
||||||
if opt.debug:
|
if opt.debug:
|
||||||
for panel in panelsProcessed:
|
for panel in panelsProcessed:
|
||||||
# noinspection PyUnboundLocalVariable
|
|
||||||
draw.rectangle([(0, panel[0]), (widthImg, panel[1])], (0, 255, 0, 128), (0, 0, 255, 255))
|
draw.rectangle([(0, panel[0]), (widthImg, panel[1])], (0, 255, 0, 128), (0, 0, 255, 255))
|
||||||
# noinspection PyUnboundLocalVariable
|
|
||||||
debugImage = Image.alpha_composite(imgOrg.convert(mode='RGBA'), drawImg)
|
debugImage = Image.alpha_composite(imgOrg.convert(mode='RGBA'), drawImg)
|
||||||
debugImage.save(os.path.join(path, os.path.splitext(name)[0] + '-debug.png'), 'PNG')
|
debugImage.save(os.path.join(path, os.path.splitext(name)[0] + '-debug.png'), 'PNG')
|
||||||
|
|
||||||
@@ -185,7 +184,7 @@ def splitImage(work):
|
|||||||
return str(sys.exc_info()[1]), sanitizeTrace(sys.exc_info()[2])
|
return str(sys.exc_info()[1]), sanitizeTrace(sys.exc_info()[2])
|
||||||
|
|
||||||
|
|
||||||
def main(argv=None, qtGUI=None):
|
def main(argv=None, qtgui=None):
|
||||||
global options, GUI, splitWorkerPool, splitWorkerOutput, mergeWorkerPool, mergeWorkerOutput
|
global options, GUI, splitWorkerPool, splitWorkerOutput, mergeWorkerPool, mergeWorkerOutput
|
||||||
parser = OptionParser(usage="Usage: kcc-c2p [options] comic_folder", add_help_option=False)
|
parser = OptionParser(usage="Usage: kcc-c2p [options] comic_folder", add_help_option=False)
|
||||||
mainOptions = OptionGroup(parser, "MANDATORY")
|
mainOptions = OptionGroup(parser, "MANDATORY")
|
||||||
@@ -203,8 +202,8 @@ def main(argv=None, qtGUI=None):
|
|||||||
parser.add_option_group(mainOptions)
|
parser.add_option_group(mainOptions)
|
||||||
parser.add_option_group(otherOptions)
|
parser.add_option_group(otherOptions)
|
||||||
options, args = parser.parse_args(argv)
|
options, args = parser.parse_args(argv)
|
||||||
if qtGUI:
|
if qtgui:
|
||||||
GUI = qtGUI
|
GUI = qtgui
|
||||||
else:
|
else:
|
||||||
GUI = None
|
GUI = None
|
||||||
if len(args) != 1:
|
if len(args) != 1:
|
||||||
|
|||||||
81
kindlecomicconverter/comicarchive.py
Normal file
81
kindlecomicconverter/comicarchive.py
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
||||||
|
# Copyright (c) 2013-2019 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||||
|
#
|
||||||
|
# Permission to use, copy, modify, and/or distribute this software for
|
||||||
|
# any purpose with or without fee is hereby granted, provided that the
|
||||||
|
# above copyright notice and this permission notice appear in all
|
||||||
|
# copies.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
||||||
|
# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
||||||
|
# WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
|
||||||
|
# AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||||
|
# DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
|
||||||
|
# OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||||
|
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
# PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
#
|
||||||
|
|
||||||
|
import os
|
||||||
|
from psutil import Popen
|
||||||
|
from shutil import move
|
||||||
|
from subprocess import STDOUT, PIPE
|
||||||
|
from xml.dom.minidom import parseString
|
||||||
|
from xml.parsers.expat import ExpatError
|
||||||
|
|
||||||
|
|
||||||
|
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 = Popen('7z l -y -p1 "' + self.filepath + '"', stderr=STDOUT, stdout=PIPE, stdin=PIPE, shell=True)
|
||||||
|
for line in process.stdout:
|
||||||
|
if b'Type =' in line:
|
||||||
|
self.type = line.rstrip().decode().split(' = ')[1].upper()
|
||||||
|
break
|
||||||
|
process.communicate()
|
||||||
|
if process.returncode != 0:
|
||||||
|
raise OSError('Archive is corrupted or encrypted.')
|
||||||
|
elif self.type not in ['7Z', 'RAR', 'RAR5', 'ZIP']:
|
||||||
|
raise OSError('Unsupported archive format.')
|
||||||
|
|
||||||
|
def extract(self, targetdir):
|
||||||
|
if not os.path.isdir(targetdir):
|
||||||
|
raise OSError('Target directory don\'t exist.')
|
||||||
|
process = Popen('7z x -y -xr!__MACOSX -xr!.DS_Store -xr!thumbs.db -xr!Thumbs.db -o"' + targetdir + '" "' +
|
||||||
|
self.filepath + '"', stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
|
||||||
|
process.communicate()
|
||||||
|
if process.returncode != 0:
|
||||||
|
raise OSError('Failed to extract archive.')
|
||||||
|
tdir = os.listdir(targetdir)
|
||||||
|
if 'ComicInfo.xml' in tdir:
|
||||||
|
tdir.remove('ComicInfo.xml')
|
||||||
|
if len(tdir) == 1 and os.path.isdir(os.path.join(targetdir, tdir[0])):
|
||||||
|
for f in os.listdir(os.path.join(targetdir, tdir[0])):
|
||||||
|
move(os.path.join(targetdir, tdir[0], f), targetdir)
|
||||||
|
os.rmdir(os.path.join(targetdir, tdir[0]))
|
||||||
|
return targetdir
|
||||||
|
|
||||||
|
def addFile(self, sourcefile):
|
||||||
|
if self.type in ['RAR', 'RAR5']:
|
||||||
|
raise NotImplementedError
|
||||||
|
process = Popen('7z a -y "' + self.filepath + '" "' + sourcefile + '"',
|
||||||
|
stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
|
||||||
|
process.communicate()
|
||||||
|
if process.returncode != 0:
|
||||||
|
raise OSError('Failed to add the file.')
|
||||||
|
|
||||||
|
def extractMetadata(self):
|
||||||
|
process = Popen('7z x -y -so "' + self.filepath + '" ComicInfo.xml',
|
||||||
|
stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
|
||||||
|
xml = process.communicate()
|
||||||
|
if process.returncode != 0:
|
||||||
|
raise OSError('Failed to extract archive.')
|
||||||
|
try:
|
||||||
|
return parseString(xml[0])
|
||||||
|
except ExpatError:
|
||||||
|
return None
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# Based on initial version of DualMetaFix. Copyright (C) 2013 Kevin Hendricks
|
# Based on initial version of DualMetaFix. Copyright (C) 2013 Kevin Hendricks
|
||||||
# Changes for KCC Copyright (C) 2014-2017 Pawel Jastrzebski <pawelj@iosphe.re>
|
# Changes for KCC Copyright (C) 2014-2019 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -24,6 +24,7 @@ import shutil
|
|||||||
class DualMetaFixException(Exception):
|
class DualMetaFixException(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
# palm database offset constants
|
# palm database offset constants
|
||||||
number_of_pdb_records = 76
|
number_of_pdb_records = 76
|
||||||
first_pdb_record = 78
|
first_pdb_record = 78
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
# Copyright (C) 2010 Alex Yatskov
|
# Copyright (C) 2010 Alex Yatskov
|
||||||
# Copyright (C) 2011 Stanislav (proDOOMman) Kosolapov <prodoomman@gmail.com>
|
# Copyright (C) 2011 Stanislav (proDOOMman) Kosolapov <prodoomman@gmail.com>
|
||||||
# Copyright (c) 2016 Alberto Planas <aplanas@gmail.com>
|
# Copyright (c) 2016 Alberto Planas <aplanas@gmail.com>
|
||||||
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
||||||
# Copyright (c) 2013-2017 Pawel Jastrzebski <pawelj@iosphe.re>
|
# Copyright (c) 2013-2019 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -18,12 +20,8 @@
|
|||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import os
|
import os
|
||||||
from io import BytesIO
|
|
||||||
from urllib.request import Request, urlopen
|
|
||||||
from urllib.parse import quote
|
|
||||||
from PIL import Image, ImageOps, ImageStat, ImageChops, ImageFilter
|
from PIL import Image, ImageOps, ImageStat, ImageChops, ImageFilter
|
||||||
from .shared import md5Checksum
|
from .shared import md5Checksum
|
||||||
from . import __version__
|
|
||||||
|
|
||||||
|
|
||||||
class ProfileData:
|
class ProfileData:
|
||||||
@@ -84,7 +82,8 @@ class ProfileData:
|
|||||||
'K578': ("Kindle", (600, 800), Palette16, 1.8),
|
'K578': ("Kindle", (600, 800), Palette16, 1.8),
|
||||||
'KDX': ("Kindle DX/DXG", (824, 1000), Palette16, 1.8),
|
'KDX': ("Kindle DX/DXG", (824, 1000), Palette16, 1.8),
|
||||||
'KPW': ("Kindle Paperwhite 1/2", (758, 1024), Palette16, 1.8),
|
'KPW': ("Kindle Paperwhite 1/2", (758, 1024), Palette16, 1.8),
|
||||||
'KV': ("Kindle Paperwhite 3/Voyage/Oasis", (1072, 1448), Palette16, 1.8),
|
'KV': ("Kindle Paperwhite 3/4/Voyage/Oasis", (1072, 1448), Palette16, 1.8),
|
||||||
|
'KO': ("Kindle Oasis 2", (1264, 1680), Palette16, 1.8),
|
||||||
'KoMT': ("Kobo Mini/Touch", (600, 800), Palette16, 1.8),
|
'KoMT': ("Kobo Mini/Touch", (600, 800), Palette16, 1.8),
|
||||||
'KoG': ("Kobo Glo", (768, 1024), Palette16, 1.8),
|
'KoG': ("Kobo Glo", (768, 1024), Palette16, 1.8),
|
||||||
'KoGHD': ("Kobo Glo HD", (1072, 1448), Palette16, 1.8),
|
'KoGHD': ("Kobo Glo HD", (1072, 1448), Palette16, 1.8),
|
||||||
@@ -92,12 +91,14 @@ class ProfileData:
|
|||||||
'KoAHD': ("Kobo Aura HD", (1080, 1440), Palette16, 1.8),
|
'KoAHD': ("Kobo Aura HD", (1080, 1440), Palette16, 1.8),
|
||||||
'KoAH2O': ("Kobo Aura H2O", (1080, 1430), Palette16, 1.8),
|
'KoAH2O': ("Kobo Aura H2O", (1080, 1430), Palette16, 1.8),
|
||||||
'KoAO': ("Kobo Aura ONE", (1404, 1872), Palette16, 1.8),
|
'KoAO': ("Kobo Aura ONE", (1404, 1872), Palette16, 1.8),
|
||||||
|
'KoF': ("Kobo Forma", (1440, 1920), Palette16, 1.8),
|
||||||
'OTHER': ("Other", (0, 0), Palette16, 1.8),
|
'OTHER': ("Other", (0, 0), Palette16, 1.8),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class ComicPageParser:
|
class ComicPageParser:
|
||||||
def __init__(self, source, options):
|
def __init__(self, source, options):
|
||||||
|
Image.MAX_IMAGE_PIXELS = int(2048 * 2048 * 2048 // 4 // 3)
|
||||||
self.opt = options
|
self.opt = options
|
||||||
self.source = source
|
self.source = source
|
||||||
self.size = self.opt.profileData[1]
|
self.size = self.opt.profileData[1]
|
||||||
@@ -272,17 +273,17 @@ class ComicPage:
|
|||||||
method = Image.BICUBIC
|
method = Image.BICUBIC
|
||||||
else:
|
else:
|
||||||
method = Image.LANCZOS
|
method = Image.LANCZOS
|
||||||
if self.opt.stretch:
|
if self.opt.stretch or (self.opt.kfx and ('-KCC-B' in self.targetPath or '-KCC-C' in self.targetPath)):
|
||||||
self.image = self.image.resize(self.size, method)
|
self.image = self.image.resize(self.size, method)
|
||||||
elif self.image.size[0] <= self.size[0] and self.image.size[1] <= self.size[1] and not self.opt.upscale:
|
elif self.image.size[0] <= self.size[0] and self.image.size[1] <= self.size[1] and not self.opt.upscale:
|
||||||
if self.opt.format == 'CBZ':
|
if self.opt.format == 'CBZ' or self.opt.kfx:
|
||||||
borderw = int((self.size[0] - self.image.size[0]) / 2)
|
borderw = int((self.size[0] - self.image.size[0]) / 2)
|
||||||
borderh = int((self.size[1] - self.image.size[1]) / 2)
|
borderh = int((self.size[1] - self.image.size[1]) / 2)
|
||||||
self.image = ImageOps.expand(self.image, border=(borderw, borderh), fill=self.fill)
|
self.image = ImageOps.expand(self.image, border=(borderw, borderh), fill=self.fill)
|
||||||
if self.image.size[0] != self.size[0] or self.image.size[1] != self.size[1]:
|
if self.image.size[0] != self.size[0] or self.image.size[1] != self.size[1]:
|
||||||
self.image = ImageOps.fit(self.image, self.size, method=Image.BICUBIC, centering=(0.5, 0.5))
|
self.image = ImageOps.fit(self.image, self.size, method=Image.BICUBIC, centering=(0.5, 0.5))
|
||||||
else:
|
else:
|
||||||
if self.opt.format == 'CBZ':
|
if self.opt.format == 'CBZ' or self.opt.kfx:
|
||||||
ratioDev = float(self.size[0]) / float(self.size[1])
|
ratioDev = float(self.size[0]) / float(self.size[1])
|
||||||
if (float(self.image.size[0]) / float(self.image.size[1])) < ratioDev:
|
if (float(self.image.size[0]) / float(self.image.size[1])) < ratioDev:
|
||||||
diff = int(self.image.size[1] * ratioDev) - self.image.size[0]
|
diff = int(self.image.size[1] * ratioDev) - self.image.size[0]
|
||||||
@@ -298,59 +299,51 @@ class ComicPage:
|
|||||||
if self.image.size[0] > self.size[0] or self.image.size[1] > self.size[1]:
|
if self.image.size[0] > self.size[0] or self.image.size[1] > self.size[1]:
|
||||||
self.image.thumbnail(self.size, Image.LANCZOS)
|
self.image.thumbnail(self.size, Image.LANCZOS)
|
||||||
|
|
||||||
def getBoundingBox(self, tmpImg):
|
def getBoundingBox(self, tmptmg):
|
||||||
min_margin = [int(0.005 * i + 0.5) for i in tmpImg.size]
|
min_margin = [int(0.005 * i + 0.5) for i in tmptmg.size]
|
||||||
max_margin = [int(0.1 * i + 0.5) for i in tmpImg.size]
|
max_margin = [int(0.1 * i + 0.5) for i in tmptmg.size]
|
||||||
bbox = tmpImg.getbbox()
|
bbox = tmptmg.getbbox()
|
||||||
bbox = (
|
bbox = (
|
||||||
max(0, min(max_margin[0], bbox[0] - min_margin[0])),
|
max(0, min(max_margin[0], bbox[0] - min_margin[0])),
|
||||||
max(0, min(max_margin[1], bbox[1] - min_margin[1])),
|
max(0, min(max_margin[1], bbox[1] - min_margin[1])),
|
||||||
min(tmpImg.size[0],
|
min(tmptmg.size[0],
|
||||||
max(tmpImg.size[0] - max_margin[0], bbox[2] + min_margin[0])),
|
max(tmptmg.size[0] - max_margin[0], bbox[2] + min_margin[0])),
|
||||||
min(tmpImg.size[1],
|
min(tmptmg.size[1],
|
||||||
max(tmpImg.size[1] - max_margin[1], bbox[3] + min_margin[1])),
|
max(tmptmg.size[1] - max_margin[1], bbox[3] + min_margin[1])),
|
||||||
)
|
)
|
||||||
return bbox
|
return bbox
|
||||||
|
|
||||||
def cropPageNumber(self, power):
|
def cropPageNumber(self, power):
|
||||||
if self.fill != 'white':
|
if self.fill != 'white':
|
||||||
tmpImg = self.image.convert(mode='L')
|
tmptmg = self.image.convert(mode='L')
|
||||||
else:
|
else:
|
||||||
tmpImg = ImageOps.invert(self.image.convert(mode='L'))
|
tmptmg = ImageOps.invert(self.image.convert(mode='L'))
|
||||||
tmpImg = tmpImg.point(lambda x: x and 255)
|
tmptmg = tmptmg.point(lambda x: x and 255)
|
||||||
tmpImg = tmpImg.filter(ImageFilter.MinFilter(size=3))
|
tmptmg = tmptmg.filter(ImageFilter.MinFilter(size=3))
|
||||||
tmpImg = tmpImg.filter(ImageFilter.GaussianBlur(radius=5))
|
tmptmg = tmptmg.filter(ImageFilter.GaussianBlur(radius=5))
|
||||||
tmpImg = tmpImg.point(lambda x: (x >= 16 * power) and x)
|
tmptmg = tmptmg.point(lambda x: (x >= 16 * power) and x)
|
||||||
self.image = self.image.crop(tmpImg.getbbox()) if tmpImg.getbbox() else self.image
|
self.image = self.image.crop(tmptmg.getbbox()) if tmptmg.getbbox() else self.image
|
||||||
|
|
||||||
def cropMargin(self, power):
|
def cropMargin(self, power):
|
||||||
if self.fill != 'white':
|
if self.fill != 'white':
|
||||||
tmpImg = self.image.convert(mode='L')
|
tmptmg = self.image.convert(mode='L')
|
||||||
else:
|
else:
|
||||||
tmpImg = ImageOps.invert(self.image.convert(mode='L'))
|
tmptmg = ImageOps.invert(self.image.convert(mode='L'))
|
||||||
tmpImg = tmpImg.filter(ImageFilter.GaussianBlur(radius=3))
|
tmptmg = tmptmg.filter(ImageFilter.GaussianBlur(radius=3))
|
||||||
tmpImg = tmpImg.point(lambda x: (x >= 16 * power) and x)
|
tmptmg = tmptmg.point(lambda x: (x >= 16 * power) and x)
|
||||||
self.image = self.image.crop(self.getBoundingBox(tmpImg)) if tmpImg.getbbox() else self.image
|
self.image = self.image.crop(self.getBoundingBox(tmptmg)) if tmptmg.getbbox() else self.image
|
||||||
|
|
||||||
|
|
||||||
class Cover:
|
class Cover:
|
||||||
def __init__(self, source, target, opt, tomeNumber):
|
def __init__(self, source, target, opt, tomeid):
|
||||||
self.options = opt
|
self.options = opt
|
||||||
self.source = source
|
self.source = source
|
||||||
self.target = target
|
self.target = target
|
||||||
if tomeNumber == 0:
|
if tomeid == 0:
|
||||||
self.tomeNumber = 1
|
self.tomeid = 1
|
||||||
else:
|
else:
|
||||||
self.tomeNumber = tomeNumber
|
self.tomeid = tomeid
|
||||||
if self.tomeNumber in self.options.remoteCovers:
|
self.image = Image.open(source)
|
||||||
try:
|
|
||||||
source = urlopen(Request(quote(self.options.remoteCovers[self.tomeNumber]).replace('%3A', ':', 1),
|
|
||||||
headers={'User-Agent': 'KindleComicConverter/' + __version__})).read()
|
|
||||||
self.image = Image.open(BytesIO(source))
|
|
||||||
except Exception:
|
|
||||||
self.image = Image.open(source)
|
|
||||||
else:
|
|
||||||
self.image = Image.open(source)
|
|
||||||
self.process()
|
self.process()
|
||||||
|
|
||||||
def process(self):
|
def process(self):
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (c) 2013-2017 Pawel Jastrzebski <pawelj@iosphe.re>
|
# Copyright (c) 2013-2019 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and/or distribute this software for
|
# Permission to use, copy, modify, and/or distribute this software for
|
||||||
# any purpose with or without fee is hereby granted, provided that the
|
# any purpose with or without fee is hereby granted, provided that the
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (c) 2013-2017 Pawel Jastrzebski <pawelj@iosphe.re>
|
# Copyright (c) 2013-2019 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and/or distribute this software for
|
# Permission to use, copy, modify, and/or distribute this software for
|
||||||
# any purpose with or without fee is hereby granted, provided that the
|
# any purpose with or without fee is hereby granted, provided that the
|
||||||
@@ -18,14 +18,9 @@
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
from xml.dom.minidom import parse, Document
|
from xml.dom.minidom import parse, Document
|
||||||
from re import compile
|
|
||||||
from zipfile import is_zipfile, ZipFile, ZIP_DEFLATED
|
|
||||||
from subprocess import STDOUT, PIPE
|
|
||||||
from psutil import Popen
|
|
||||||
from tempfile import mkdtemp
|
from tempfile import mkdtemp
|
||||||
from shutil import rmtree
|
from shutil import rmtree
|
||||||
from .shared import removeFromZIP, check7ZFile as is_7zfile
|
from . import comicarchive
|
||||||
from . import rarfile
|
|
||||||
|
|
||||||
|
|
||||||
class MetadataParser:
|
class MetadataParser:
|
||||||
@@ -39,50 +34,20 @@ class MetadataParser:
|
|||||||
'Inkers': [],
|
'Inkers': [],
|
||||||
'Colorists': [],
|
'Colorists': [],
|
||||||
'Summary': '',
|
'Summary': '',
|
||||||
'MUid': '',
|
|
||||||
'Bookmarks': []}
|
'Bookmarks': []}
|
||||||
self.rawdata = None
|
self.rawdata = None
|
||||||
self.compressor = None
|
self.format = None
|
||||||
if self.source.endswith('.xml'):
|
if self.source.endswith('.xml') and os.path.exists(self.source):
|
||||||
self.rawdata = parse(self.source)
|
self.rawdata = parse(self.source)
|
||||||
|
elif not self.source.endswith('.xml'):
|
||||||
|
try:
|
||||||
|
cbx = comicarchive.ComicArchive(self.source)
|
||||||
|
self.rawdata = cbx.extractMetadata()
|
||||||
|
self.format = cbx.type
|
||||||
|
except OSError as e:
|
||||||
|
raise UserWarning(e.strerror)
|
||||||
|
if self.rawdata:
|
||||||
self.parseXML()
|
self.parseXML()
|
||||||
else:
|
|
||||||
if is_zipfile(self.source):
|
|
||||||
self.compressor = 'zip'
|
|
||||||
with ZipFile(self.source) as zip_file:
|
|
||||||
for member in zip_file.namelist():
|
|
||||||
if member != 'ComicInfo.xml':
|
|
||||||
continue
|
|
||||||
with zip_file.open(member) as xml_file:
|
|
||||||
self.rawdata = parse(xml_file)
|
|
||||||
elif rarfile.is_rarfile(self.source):
|
|
||||||
self.compressor = 'rar'
|
|
||||||
with rarfile.RarFile(self.source) as rar_file:
|
|
||||||
for member in rar_file.namelist():
|
|
||||||
if member != 'ComicInfo.xml':
|
|
||||||
continue
|
|
||||||
with rar_file.open(member) as xml_file:
|
|
||||||
self.rawdata = parse(xml_file)
|
|
||||||
elif is_7zfile(self.source):
|
|
||||||
self.compressor = '7z'
|
|
||||||
workdir = mkdtemp('', 'KCC-')
|
|
||||||
tmpXML = os.path.join(workdir, 'ComicInfo.xml')
|
|
||||||
output = Popen('7za e "' + self.source + '" ComicInfo.xml -o"' + workdir + '"',
|
|
||||||
stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
|
|
||||||
extracted = False
|
|
||||||
for line in output.stdout:
|
|
||||||
if b"Everything is Ok" in line or b"No files to process" in line:
|
|
||||||
extracted = True
|
|
||||||
if not extracted:
|
|
||||||
rmtree(workdir)
|
|
||||||
raise OSError('Failed to extract 7ZIP file.')
|
|
||||||
if os.path.isfile(tmpXML):
|
|
||||||
self.rawdata = parse(tmpXML)
|
|
||||||
rmtree(workdir)
|
|
||||||
else:
|
|
||||||
raise OSError('Failed to detect archive format.')
|
|
||||||
if self.rawdata:
|
|
||||||
self.parseXML()
|
|
||||||
|
|
||||||
def parseXML(self):
|
def parseXML(self):
|
||||||
if len(self.rawdata.getElementsByTagName('Series')) != 0:
|
if len(self.rawdata.getElementsByTagName('Series')) != 0:
|
||||||
@@ -99,11 +64,6 @@ class MetadataParser:
|
|||||||
self.data[field + 's'].append(person)
|
self.data[field + 's'].append(person)
|
||||||
self.data[field + 's'] = list(set(self.data[field + 's']))
|
self.data[field + 's'] = list(set(self.data[field + 's']))
|
||||||
self.data[field + 's'].sort()
|
self.data[field + 's'].sort()
|
||||||
if len(self.rawdata.getElementsByTagName('ScanInformation')) != 0:
|
|
||||||
coverId = compile('(MCD\\()(\\d+)(\\))')\
|
|
||||||
.search(self.rawdata.getElementsByTagName('ScanInformation')[0].firstChild.nodeValue)
|
|
||||||
if coverId:
|
|
||||||
self.data['MUid'] = coverId.group(2)
|
|
||||||
if len(self.rawdata.getElementsByTagName('Page')) != 0:
|
if len(self.rawdata.getElementsByTagName('Page')) != 0:
|
||||||
for page in self.rawdata.getElementsByTagName('Page'):
|
for page in self.rawdata.getElementsByTagName('Page'):
|
||||||
if 'Bookmark' in page.attributes and 'Image' in page.attributes:
|
if 'Bookmark' in page.attributes and 'Image' in page.attributes:
|
||||||
@@ -116,8 +76,7 @@ class MetadataParser:
|
|||||||
for row in (['Series', self.data['Series']], ['Volume', self.data['Volume']],
|
for row in (['Series', self.data['Series']], ['Volume', self.data['Volume']],
|
||||||
['Number', self.data['Number']], ['Writer', ', '.join(self.data['Writers'])],
|
['Number', self.data['Number']], ['Writer', ', '.join(self.data['Writers'])],
|
||||||
['Penciller', ', '.join(self.data['Pencillers'])], ['Inker', ', '.join(self.data['Inkers'])],
|
['Penciller', ', '.join(self.data['Pencillers'])], ['Inker', ', '.join(self.data['Inkers'])],
|
||||||
['Colorist', ', '.join(self.data['Colorists'])], ['Summary', self.data['Summary']],
|
['Colorist', ', '.join(self.data['Colorists'])], ['Summary', self.data['Summary']]):
|
||||||
['ScanInformation', 'MCD(' + self.data['MUid'] + ')' if self.data['MUid'] else '']):
|
|
||||||
if self.rawdata.getElementsByTagName(row[0]):
|
if self.rawdata.getElementsByTagName(row[0]):
|
||||||
node = self.rawdata.getElementsByTagName(row[0])[0]
|
node = self.rawdata.getElementsByTagName(row[0])[0]
|
||||||
if row[1]:
|
if row[1]:
|
||||||
@@ -138,8 +97,7 @@ class MetadataParser:
|
|||||||
for row in (['Series', self.data['Series']], ['Volume', self.data['Volume']],
|
for row in (['Series', self.data['Series']], ['Volume', self.data['Volume']],
|
||||||
['Number', self.data['Number']], ['Writer', ', '.join(self.data['Writers'])],
|
['Number', self.data['Number']], ['Writer', ', '.join(self.data['Writers'])],
|
||||||
['Penciller', ', '.join(self.data['Pencillers'])], ['Inker', ', '.join(self.data['Inkers'])],
|
['Penciller', ', '.join(self.data['Pencillers'])], ['Inker', ', '.join(self.data['Inkers'])],
|
||||||
['Colorist', ', '.join(self.data['Colorists'])], ['Summary', self.data['Summary']],
|
['Colorist', ', '.join(self.data['Colorists'])], ['Summary', self.data['Summary']]):
|
||||||
['ScanInformation', 'MCD(' + self.data['MUid'] + ')' if self.data['MUid'] else '']):
|
|
||||||
if row[1]:
|
if row[1]:
|
||||||
main = doc.createElement(row[0])
|
main = doc.createElement(row[0])
|
||||||
root.appendChild(main)
|
root.appendChild(main)
|
||||||
@@ -154,20 +112,9 @@ class MetadataParser:
|
|||||||
tmpXML = os.path.join(workdir, 'ComicInfo.xml')
|
tmpXML = os.path.join(workdir, 'ComicInfo.xml')
|
||||||
with open(tmpXML, 'w', encoding='utf-8') as f:
|
with open(tmpXML, 'w', encoding='utf-8') as f:
|
||||||
self.rawdata.writexml(f, encoding='utf-8')
|
self.rawdata.writexml(f, encoding='utf-8')
|
||||||
if is_zipfile(self.source):
|
try:
|
||||||
removeFromZIP(self.source, 'ComicInfo.xml')
|
cbx = comicarchive.ComicArchive(self.source)
|
||||||
with ZipFile(self.source, mode='a', compression=ZIP_DEFLATED) as zip_file:
|
cbx.addFile(tmpXML)
|
||||||
zip_file.write(tmpXML, arcname=tmpXML.split(os.sep)[-1])
|
except OSError as e:
|
||||||
elif rarfile.is_rarfile(self.source):
|
raise UserWarning(e.strerror)
|
||||||
raise NotImplementedError
|
|
||||||
elif is_7zfile(self.source):
|
|
||||||
output = Popen('7za a "' + self.source + '" "' + tmpXML + '"',
|
|
||||||
stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
|
|
||||||
extracted = False
|
|
||||||
for line in output.stdout:
|
|
||||||
if b"Everything is Ok" in line:
|
|
||||||
extracted = True
|
|
||||||
if not extracted:
|
|
||||||
rmtree(workdir)
|
|
||||||
raise OSError('Failed to modify 7ZIP file.')
|
|
||||||
rmtree(workdir)
|
rmtree(workdir)
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
||||||
# Copyright (c) 2013-2017 Pawel Jastrzebski <pawelj@iosphe.re>
|
# Copyright (c) 2013-2019 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||||
#
|
#
|
||||||
# Based upon the code snippet by Ned Batchelder
|
# Based upon the code snippet by Ned Batchelder
|
||||||
# (http://nedbatchelder.com/blog/200712/extracting_jpgs_from_pdfs.html)
|
# (http://nedbatchelder.com/blog/200712/extracting_jpgs_from_pdfs.html)
|
||||||
@@ -25,17 +27,16 @@ from string import ascii_uppercase, digits
|
|||||||
|
|
||||||
|
|
||||||
class PdfJpgExtract:
|
class PdfJpgExtract:
|
||||||
def __init__(self, origFileName):
|
def __init__(self, fname):
|
||||||
self.origFileName = origFileName
|
self.fname = fname
|
||||||
self.filename = os.path.splitext(origFileName)
|
self.filename = os.path.splitext(fname)
|
||||||
# noinspection PyUnusedLocal
|
self.path = self.filename[0] + "-KCC-" + ''.join(choice(ascii_uppercase + digits) for _ in range(3))
|
||||||
self.path = self.filename[0] + "-KCC-" + ''.join(choice(ascii_uppercase + digits) for x in range(3))
|
|
||||||
|
|
||||||
def getPath(self):
|
def getPath(self):
|
||||||
return self.path
|
return self.path
|
||||||
|
|
||||||
def extract(self):
|
def extract(self):
|
||||||
pdf = open(self.origFileName, "rb").read()
|
pdf = open(self.fname, "rb").read()
|
||||||
startmark = b"\xff\xd8"
|
startmark = b"\xff\xd8"
|
||||||
startfix = 0
|
startfix = 0
|
||||||
endmark = b"\xff\xd9"
|
endmark = b"\xff\xd9"
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,7 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
||||||
# Copyright (c) 2013-2017 Pawel Jastrzebski <pawelj@iosphe.re>
|
# Copyright (c) 2013-2019 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and/or distribute this software for
|
# Permission to use, copy, modify, and/or distribute this software for
|
||||||
# any purpose with or without fee is hereby granted, provided that the
|
# any purpose with or without fee is hereby granted, provided that the
|
||||||
@@ -20,9 +22,6 @@ import os
|
|||||||
from hashlib import md5
|
from hashlib import md5
|
||||||
from html.parser import HTMLParser
|
from html.parser import HTMLParser
|
||||||
from distutils.version import StrictVersion
|
from distutils.version import StrictVersion
|
||||||
from shutil import rmtree, copy
|
|
||||||
from tempfile import mkdtemp
|
|
||||||
from zipfile import ZipFile, ZIP_DEFLATED
|
|
||||||
from re import split
|
from re import split
|
||||||
from traceback import format_tb
|
from traceback import format_tb
|
||||||
|
|
||||||
@@ -48,7 +47,7 @@ class HTMLStripper(HTMLParser):
|
|||||||
def getImageFileName(imgfile):
|
def getImageFileName(imgfile):
|
||||||
name, ext = os.path.splitext(imgfile)
|
name, ext = os.path.splitext(imgfile)
|
||||||
ext = ext.lower()
|
ext = ext.lower()
|
||||||
if name.startswith('.') or (ext != '.png' and ext != '.jpg' and ext != '.jpeg' and ext != '.gif'):
|
if name.startswith('.') or ext not in ['.png', '.jpg', '.jpeg', '.gif', '.webp']:
|
||||||
return None
|
return None
|
||||||
return [name, ext]
|
return [name, ext]
|
||||||
|
|
||||||
@@ -73,8 +72,8 @@ def walkLevel(some_dir, level=1):
|
|||||||
del dirs[:]
|
del dirs[:]
|
||||||
|
|
||||||
|
|
||||||
def md5Checksum(filePath):
|
def md5Checksum(fpath):
|
||||||
with open(filePath, 'rb') as fh:
|
with open(fpath, 'rb') as fh:
|
||||||
m = md5()
|
m = md5()
|
||||||
while True:
|
while True:
|
||||||
data = fh.read(8192)
|
data = fh.read(8192)
|
||||||
@@ -84,38 +83,19 @@ def md5Checksum(filePath):
|
|||||||
return m.hexdigest()
|
return m.hexdigest()
|
||||||
|
|
||||||
|
|
||||||
def check7ZFile(filePath):
|
|
||||||
with open(filePath, 'rb') as fh:
|
|
||||||
header = fh.read(6)
|
|
||||||
return header == b"7z\xbc\xaf'\x1c"
|
|
||||||
|
|
||||||
|
|
||||||
def removeFromZIP(zipfname, *filenames):
|
|
||||||
tempdir = mkdtemp('', 'KCC-')
|
|
||||||
try:
|
|
||||||
tempname = os.path.join(tempdir, 'KCC.zip')
|
|
||||||
with ZipFile(zipfname, 'r') as zipread:
|
|
||||||
with ZipFile(tempname, 'w', compression=ZIP_DEFLATED) as zipwrite:
|
|
||||||
for item in zipread.infolist():
|
|
||||||
if item.filename not in filenames:
|
|
||||||
zipwrite.writestr(item, zipread.read(item.filename))
|
|
||||||
copy(tempname, zipfname)
|
|
||||||
finally:
|
|
||||||
rmtree(tempdir, True)
|
|
||||||
|
|
||||||
|
|
||||||
def sanitizeTrace(traceback):
|
def sanitizeTrace(traceback):
|
||||||
return ''.join(format_tb(traceback))\
|
return ''.join(format_tb(traceback))\
|
||||||
.replace('C:/projects/kcc/', '') \
|
.replace('C:/projects/kcc/', '')\
|
||||||
.replace('c:/projects/kcc/', '') \
|
.replace('c:/projects/kcc/', '')\
|
||||||
.replace('C:/python36-x64/', '')\
|
.replace('C:/python37-x64/', '')\
|
||||||
.replace('c:/python36-x64/', '')\
|
.replace('c:/python37-x64/', '')\
|
||||||
.replace('C:\\projects\\kcc\\', '') \
|
.replace('C:\\projects\\kcc\\', '')\
|
||||||
.replace('c:\\projects\\kcc\\', '') \
|
.replace('c:\\projects\\kcc\\', '')\
|
||||||
.replace('C:\\python36-x64\\', '')\
|
.replace('C:\\python37-x64\\', '')\
|
||||||
.replace('c:\\python36-x64\\', '')
|
.replace('c:\\python37-x64\\', '')
|
||||||
|
|
||||||
|
|
||||||
|
# noinspection PyUnresolvedReferences
|
||||||
def dependencyCheck(level):
|
def dependencyCheck(level):
|
||||||
missing = []
|
missing = []
|
||||||
if level > 2:
|
if level > 2:
|
||||||
@@ -143,11 +123,11 @@ def dependencyCheck(level):
|
|||||||
except ImportError:
|
except ImportError:
|
||||||
missing.append('python-slugify 1.2.1+')
|
missing.append('python-slugify 1.2.1+')
|
||||||
try:
|
try:
|
||||||
from PIL import PILLOW_VERSION as pillowVersion
|
from PIL import __version__ as pillowVersion
|
||||||
if StrictVersion('4.0.0') > StrictVersion(pillowVersion):
|
if StrictVersion('5.2.0') > StrictVersion(pillowVersion):
|
||||||
missing.append('Pillow 4.0.0+')
|
missing.append('Pillow 5.2.0+')
|
||||||
except ImportError:
|
except ImportError:
|
||||||
missing.append('Pillow 4.0.0+')
|
missing.append('Pillow 5.2.0+')
|
||||||
if len(missing) > 0:
|
if len(missing) > 0:
|
||||||
print('ERROR: ' + ', '.join(missing) + ' is not installed!')
|
print('ERROR: ' + ', '.join(missing) + ' is not installed!')
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
||||||
# Copyright (c) 2013-2017 Pawel Jastrzebski <pawelj@iosphe.re>
|
# Copyright (c) 2013-2019 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and/or distribute this software for
|
# Permission to use, copy, modify, and/or distribute this software for
|
||||||
# any purpose with or without fee is hereby granted, provided that the
|
# any purpose with or without fee is hereby granted, provided that the
|
||||||
@@ -30,15 +30,15 @@ def start():
|
|||||||
os.environ['QT_AUTO_SCREEN_SCALE_FACTOR'] = "1"
|
os.environ['QT_AUTO_SCREEN_SCALE_FACTOR'] = "1"
|
||||||
KCCAplication = KCC_gui.QApplicationMessaging(sys.argv)
|
KCCAplication = KCC_gui.QApplicationMessaging(sys.argv)
|
||||||
if KCCAplication.isRunning():
|
if KCCAplication.isRunning():
|
||||||
if len(sys.argv) > 1:
|
for i in range(1, len(sys.argv)):
|
||||||
KCCAplication.sendMessage(sys.argv[1])
|
KCCAplication.sendMessage(sys.argv[i])
|
||||||
else:
|
else:
|
||||||
KCCAplication.sendMessage('ARISE')
|
KCCAplication.sendMessage('ARISE')
|
||||||
else:
|
else:
|
||||||
KCCWindow = KCC_gui.QMainWindowKCC()
|
KCCWindow = KCC_gui.QMainWindowKCC()
|
||||||
KCCUI = KCC_gui.KCCGUI(KCCAplication, KCCWindow)
|
KCCUI = KCC_gui.KCCGUI(KCCAplication, KCCWindow)
|
||||||
if len(sys.argv) > 1:
|
for i in range(1, len(sys.argv)):
|
||||||
KCCUI.handleMessage(sys.argv[1])
|
KCCUI.handleMessage(sys.argv[i])
|
||||||
sys.exit(KCCAplication.exec_())
|
sys.exit(KCCAplication.exec_())
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
kindlecomicconverter: binary-without-manpage usr/bin/kcc
|
|
||||||
kindlecomicconverter: wrong-name-for-changelog-of-native-package usr/share/doc/kindlecomicconverter/changelog.Debian.gz
|
|
||||||
kindlecomicconverter: file-missing-in-md5sums usr/share/doc/kindlecomicconverter/changelog.Debian.gz
|
|
||||||
kindlecomicconverter: hardening-no-relro usr/bin/kcc
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
[Desktop Entry]
|
|
||||||
Type=Application
|
|
||||||
Version=1.0
|
|
||||||
Name=Kindle Comic Converter
|
|
||||||
GenericName=Kindle Comic Converter
|
|
||||||
Comment=Comic and Manga converter for e-book readers
|
|
||||||
Icon=/usr/share/kindlecomicconverter/comic2ebook.png
|
|
||||||
Exec=/usr/bin/kcc %f
|
|
||||||
Terminal=false
|
|
||||||
Categories=Graphics;
|
|
||||||
MimeType=application/zip;application/x-rar;application/x-7z-compressed;
|
|
||||||
Binary file not shown.
BIN
other/osx/7z
Executable file
BIN
other/osx/7z
Executable file
Binary file not shown.
BIN
other/osx/7z.so
Normal file
BIN
other/osx/7z.so
Normal file
Binary file not shown.
BIN
other/osx/7za
BIN
other/osx/7za
Binary file not shown.
@@ -30,7 +30,7 @@
|
|||||||
<key>CFBundleExecutable</key>
|
<key>CFBundleExecutable</key>
|
||||||
<string>MacOS/Kindle Comic Converter</string>
|
<string>MacOS/Kindle Comic Converter</string>
|
||||||
<key>CFBundleGetInfoString</key>
|
<key>CFBundleGetInfoString</key>
|
||||||
<string>KindleComicConverter 5.4.1, written 2012-2017 by Ciro Mattia Gonano and Pawel Jastrzebski</string>
|
<string>KindleComicConverter 5.5.1, written 2012-2019 by Ciro Mattia Gonano and Pawel Jastrzebski</string>
|
||||||
<key>CFBundleIconFile</key>
|
<key>CFBundleIconFile</key>
|
||||||
<string>comic2ebook.icns</string>
|
<string>comic2ebook.icns</string>
|
||||||
<key>CFBundleIdentifier</key>
|
<key>CFBundleIdentifier</key>
|
||||||
@@ -42,11 +42,11 @@
|
|||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>APPL</string>
|
<string>APPL</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>5.4.1</string>
|
<string>5.5.1</string>
|
||||||
<key>CFBundleSignature</key>
|
<key>CFBundleSignature</key>
|
||||||
<string>????</string>
|
<string>????</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>5.4.1</string>
|
<string>5.5.1</string>
|
||||||
<key>LSEnvironment</key>
|
<key>LSEnvironment</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>PATH</key>
|
<key>PATH</key>
|
||||||
@@ -55,7 +55,7 @@
|
|||||||
<key>LSHasLocalizedDisplayName</key>
|
<key>LSHasLocalizedDisplayName</key>
|
||||||
<false/>
|
<false/>
|
||||||
<key>LSMinimumSystemVersion</key>
|
<key>LSMinimumSystemVersion</key>
|
||||||
<string>10.10.0</string>
|
<string>10.12.0</string>
|
||||||
<key>NSAppleScriptEnabled</key>
|
<key>NSAppleScriptEnabled</key>
|
||||||
<false/>
|
<false/>
|
||||||
<key>NSHumanReadableCopyright</key>
|
<key>NSHumanReadableCopyright</key>
|
||||||
|
|||||||
BIN
other/osx/Rar.so
Normal file
BIN
other/osx/Rar.so
Normal file
Binary file not shown.
BIN
other/osx/unrar
BIN
other/osx/unrar
Binary file not shown.
BIN
other/windows/7z.dll
Normal file
BIN
other/windows/7z.dll
Normal file
Binary file not shown.
BIN
other/windows/7z.exe
Normal file
BIN
other/windows/7z.exe
Normal file
Binary file not shown.
Binary file not shown.
@@ -1,56 +1,22 @@
|
|||||||
****** ***** ****** UnRAR - free utility for RAR archives
|
|
||||||
** ** ** ** ** ** ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
****** ******* ****** License for use and distribution of
|
|
||||||
** ** ** ** ** ** ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
** ** ** ** ** ** FREEWARE version
|
|
||||||
~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
The UnRAR utility is freeware. This means:
|
|
||||||
|
|
||||||
1. All copyrights to RAR and the utility UnRAR are exclusively
|
|
||||||
owned by the author - Alexander Roshal.
|
|
||||||
|
|
||||||
2. The UnRAR utility may be freely distributed. It is allowed
|
|
||||||
to distribute UnRAR inside of other software packages.
|
|
||||||
|
|
||||||
3. THE RAR ARCHIVER AND THE UnRAR UTILITY ARE DISTRIBUTED "AS IS".
|
|
||||||
NO WARRANTY OF ANY KIND IS EXPRESSED OR IMPLIED. YOU USE AT
|
|
||||||
YOUR OWN RISK. THE AUTHOR WILL NOT BE LIABLE FOR DATA LOSS,
|
|
||||||
DAMAGES, LOSS OF PROFITS OR ANY OTHER KIND OF LOSS WHILE USING
|
|
||||||
OR MISUSING THIS SOFTWARE.
|
|
||||||
|
|
||||||
4. Neither RAR binary code, WinRAR binary code, UnRAR source or UnRAR
|
|
||||||
binary code may be used or reverse engineered to re-create the RAR
|
|
||||||
compression algorithm, which is proprietary, without written
|
|
||||||
permission of the author.
|
|
||||||
|
|
||||||
5. If you don't agree with terms of the license you must remove
|
|
||||||
UnRAR files from your storage devices and cease to use the
|
|
||||||
utility.
|
|
||||||
|
|
||||||
Thank you for your interest in RAR and UnRAR.
|
|
||||||
|
|
||||||
|
|
||||||
Alexander L. Roshal
|
|
||||||
|
|
||||||
7-Zip
|
7-Zip
|
||||||
~~~~~
|
~~~~~
|
||||||
License for use and distribution
|
License for use and distribution
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
7-Zip Copyright (C) 1999-2012 Igor Pavlov.
|
7-Zip Copyright (C) 1999-2018 Igor Pavlov.
|
||||||
|
|
||||||
Licenses for files are:
|
The licenses for files are:
|
||||||
|
|
||||||
1) 7z.dll: GNU LGPL + unRAR restriction
|
1) 7z.dll:
|
||||||
2) All other files: GNU LGPL
|
- The "GNU LGPL" as main license for most of the code
|
||||||
|
- The "GNU LGPL" with "unRAR license restriction" for some code
|
||||||
|
- The "BSD 3-clause License" for some code
|
||||||
|
2) All other files: the "GNU LGPL".
|
||||||
|
|
||||||
The GNU LGPL + unRAR restriction means that you must follow both
|
Redistributions in binary form must reproduce related license information from this file.
|
||||||
GNU LGPL rules and unRAR restriction rules.
|
|
||||||
|
|
||||||
|
Note:
|
||||||
Note:
|
You can use 7-Zip on any computer, including a computer in a commercial
|
||||||
You can use 7-Zip on any computer, including a computer in a commercial
|
|
||||||
organization. You don't need to register or pay for 7-Zip.
|
organization. You don't need to register or pay for 7-Zip.
|
||||||
|
|
||||||
|
|
||||||
@@ -67,21 +33,54 @@
|
|||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
Lesser General Public License for more details.
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
You can receive a copy of the GNU Lesser General Public License from
|
You can receive a copy of the GNU Lesser General Public License from
|
||||||
http://www.gnu.org/
|
http://www.gnu.org/
|
||||||
|
|
||||||
|
|
||||||
unRAR restriction
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
The decompression engine for RAR archives was developed using source
|
|
||||||
|
BSD 3-clause License
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
The "BSD 3-clause License" is used for the code in 7z.dll that implements LZFSE data decompression.
|
||||||
|
That code was derived from the code in the "LZFSE compression library" developed by Apple Inc,
|
||||||
|
that also uses the "BSD 3-clause License":
|
||||||
|
|
||||||
|
----
|
||||||
|
Copyright (c) 2015-2016, Apple Inc. All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
|
||||||
|
in the documentation and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
3. Neither the name of the copyright holder(s) nor the names of any contributors may be used to endorse or promote products derived
|
||||||
|
from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||||
|
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
unRAR license restriction
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
The decompression engine for RAR archives was developed using source
|
||||||
code of unRAR program.
|
code of unRAR program.
|
||||||
All copyrights to original unRAR code are owned by Alexander Roshal.
|
All copyrights to original unRAR code are owned by Alexander Roshal.
|
||||||
|
|
||||||
The license for original unRAR code has the following restriction:
|
The license for original unRAR code has the following restriction:
|
||||||
|
|
||||||
The unRAR sources cannot be used to re-create the RAR compression algorithm,
|
The unRAR sources cannot be used to re-create the RAR compression algorithm,
|
||||||
which is proprietary. Distribution of modified unRAR sources in separate form
|
which is proprietary. Distribution of modified unRAR sources in separate form
|
||||||
or as a part of other software is permitted, provided that it is clearly
|
or as a part of other software is permitted, provided that it is clearly
|
||||||
stated in the documentation and source comments that the code may
|
stated in the documentation and source comments that the code may
|
||||||
not be used to develop a RAR (WinRAR) compatible archiver.
|
not be used to develop a RAR (WinRAR) compatible archiver.
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,5 +1,5 @@
|
|||||||
PyQt5>=5.6.0
|
PyQt5>=5.6.0
|
||||||
Pillow>=4.0.0
|
Pillow>=5.2.0
|
||||||
psutil>=5.0.0
|
psutil>=5.0.0
|
||||||
python-slugify>=1.2.1
|
python-slugify>=1.2.1,<3.0.0
|
||||||
raven>=6.0.0
|
raven>=6.0.0
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
@echo off
|
|
||||||
verpatch\lib\win\verpatch dist\KCC.exe %1 /va /pv %1 /s product "Kindle Comic Converter" /s description "Kindle Comic Converter" /s copyright "Copyright (C) 2012-2017 Ciro Mattia Gonano and Pawel Jastrzebski"
|
|
||||||
"C:\Program Files (x86)\Windows Kits\8.1\bin\x64\signtool.exe" sign /f "%APPVEYOR_BUILD_FOLDER%\other\windows\Cert.pfx" /p "%CERT_PASS%" /t http://time.certum.pl /d "Kindle Comic Converter" /du "http://kcc.iosphe.re/" dist/KCC.exe
|
|
||||||
iscc /SSignTool="""C:\Program Files (x86)\Windows Kits\8.1\bin\x64\signtool.exe"" sign /f ""%APPVEYOR_BUILD_FOLDER%\other\windows\Cert.pfx"" /p ""%CERT_PASS%"" /t http://time.certum.pl $p" kcc.iss >nul 2>&1
|
|
||||||
37
setup.py
37
setup.py
@@ -1,11 +1,12 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
"""
|
"""
|
||||||
pip/pyinstaller build script for KCC.
|
pip/pyinstaller build script for KCC.
|
||||||
|
|
||||||
Install as Python package:
|
Install as Python package:
|
||||||
python3 setup.py install
|
python3 setup.py install
|
||||||
|
|
||||||
Create EXE/APP/DEB:
|
Create EXE/APP:
|
||||||
python3 setup.py build_binary
|
python3 setup.py build_binary
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -36,41 +37,23 @@ class BuildBinaryCommand(distutils.cmd.Command):
|
|||||||
VERSION = __version__
|
VERSION = __version__
|
||||||
if sys.platform == 'darwin':
|
if sys.platform == 'darwin':
|
||||||
os.system('pyinstaller -y -F -i icons/comic2ebook.icns -n "Kindle Comic Converter" -w -s kcc.py')
|
os.system('pyinstaller -y -F -i icons/comic2ebook.icns -n "Kindle Comic Converter" -w -s kcc.py')
|
||||||
shutil.copy('other/osx/7za', 'dist/Kindle Comic Converter.app/Contents/Resources')
|
os.makedirs('dist/Kindle Comic Converter.app/Contents/Resources/Codecs')
|
||||||
shutil.copy('other/osx/unrar', 'dist/Kindle Comic Converter.app/Contents/Resources')
|
shutil.copy('other/osx/7z', 'dist/Kindle Comic Converter.app/Contents/Resources')
|
||||||
|
shutil.copy('other/osx/7z.so', 'dist/Kindle Comic Converter.app/Contents/Resources')
|
||||||
|
shutil.copy('other/osx/Rar.so', 'dist/Kindle Comic Converter.app/Contents/Resources/Codecs')
|
||||||
shutil.copy('other/osx/Info.plist', 'dist/Kindle Comic Converter.app/Contents')
|
shutil.copy('other/osx/Info.plist', 'dist/Kindle Comic Converter.app/Contents')
|
||||||
shutil.copy('LICENSE.txt', 'dist/Kindle Comic Converter.app/Contents/Resources')
|
shutil.copy('LICENSE.txt', 'dist/Kindle Comic Converter.app/Contents/Resources')
|
||||||
shutil.copy('other/windows/Additional-LICENSE.txt', 'dist/Kindle Comic Converter.app/Contents/Resources')
|
shutil.copy('other/windows/Additional-LICENSE.txt', 'dist/Kindle Comic Converter.app/Contents/Resources')
|
||||||
os.chmod('dist/Kindle Comic Converter.app/Contents/Resources/unrar', 0o777)
|
os.chmod('dist/Kindle Comic Converter.app/Contents/Resources/7z', 0o777)
|
||||||
os.chmod('dist/Kindle Comic Converter.app/Contents/Resources/7za', 0o777)
|
|
||||||
os.system('appdmg kcc.json dist/KindleComicConverter_osx_' + VERSION + '.dmg')
|
os.system('appdmg kcc.json dist/KindleComicConverter_osx_' + VERSION + '.dmg')
|
||||||
exit(0)
|
exit(0)
|
||||||
elif sys.platform == 'win32':
|
elif sys.platform == 'win32':
|
||||||
os.system('pyinstaller -y -F -i icons\comic2ebook.ico -n KCC -w --noupx kcc.py')
|
os.system('pyinstaller -y -F -i icons\\comic2ebook.ico -n KCC -w --noupx kcc.py')
|
||||||
if os.getenv('APPVEYOR'):
|
|
||||||
if len(VERSION) == 3:
|
|
||||||
VERSION = VERSION + '.0'
|
|
||||||
os.system('setup.bat ' + VERSION)
|
|
||||||
exit(0)
|
exit(0)
|
||||||
else:
|
else:
|
||||||
os.system('pyinstaller -y -F kcc.py')
|
|
||||||
os.system('mkdir -p dist/usr/bin dist/usr/share/applications dist/usr/share/doc/kindlecomicconverter '
|
|
||||||
'dist/usr/share/kindlecomicconverter dist/usr/share/lintian/overrides')
|
|
||||||
os.system('mv dist/kcc dist/usr/bin')
|
|
||||||
os.system('cp icons/comic2ebook.png dist/usr/share/kindlecomicconverter')
|
|
||||||
os.system('cp LICENSE.txt dist/usr/share/doc/kindlecomicconverter/copyright')
|
|
||||||
os.system('cp other/linux/kindlecomicconverter.desktop dist/usr/share/applications')
|
|
||||||
os.system('cp other/linux/kindlecomicconverter dist/usr/share/lintian/overrides')
|
|
||||||
os.chdir('dist')
|
|
||||||
os.system('fpm -f -s dir -t deb -n kindlecomicconverter -v ' + VERSION +
|
|
||||||
' -m "Paweł Jastrzębski <pawelj@iosphe.re>" --license "ISC" '
|
|
||||||
'--description "$(printf "Comic and Manga converter for e-book '
|
|
||||||
'readers.\nThis app allows you to transform your PNG, JPG, GIF, '
|
|
||||||
'CBZ, CBR and CB7 files\ninto EPUB or MOBI format e-books.")" '
|
|
||||||
'--url "https://kcc.iosphe.re/" --deb-priority "optional" --vendor "" '
|
|
||||||
'--category "graphics" -d "unrar | unrar-free" -d "p7zip-full" -d "libc6" usr')
|
|
||||||
exit(0)
|
exit(0)
|
||||||
|
|
||||||
|
|
||||||
setuptools.setup(
|
setuptools.setup(
|
||||||
cmdclass={
|
cmdclass={
|
||||||
'build_binary': BuildBinaryCommand,
|
'build_binary': BuildBinaryCommand,
|
||||||
@@ -95,7 +78,7 @@ setuptools.setup(
|
|||||||
packages=['kindlecomicconverter'],
|
packages=['kindlecomicconverter'],
|
||||||
install_requires=[
|
install_requires=[
|
||||||
'PyQt5>=5.6.0',
|
'PyQt5>=5.6.0',
|
||||||
'Pillow>=4.0.0',
|
'Pillow>=5.2.0',
|
||||||
'psutil>=5.0.0',
|
'psutil>=5.0.0',
|
||||||
'python-slugify>=1.2.1',
|
'python-slugify>=1.2.1',
|
||||||
'raven>=6.0.0',
|
'raven>=6.0.0',
|
||||||
|
|||||||
Reference in New Issue
Block a user