1
0
mirror of https://github.com/ciromattia/kcc synced 2026-04-15 13:38:46 +00:00

Compare commits

...

69 Commits
5.1.1 ... 5.4.1

Author SHA1 Message Date
Paweł Jastrzębski
2c875673bd Merge pull request #244 from ciromattia/dev
5.4.1
2017-08-22 15:32:18 +02:00
Paweł Jastrzębski
1a0be83d63 Updated README + version bump 2017-08-22 15:31:45 +02:00
Paweł Jastrzębski
36a7dc49ec Fixed some typos 2017-08-20 18:06:09 +02:00
Paweł Jastrzębski
40d1ae63da Tweaked build process 2017-08-20 17:10:28 +02:00
Paweł Jastrzębski
3d3621c6ec Merge pull request #243 from AcidWeb/master
Implemented new build environments
2017-08-20 12:29:28 +02:00
Paweł Jastrzębski
d77f04a84e Implemented new build enviroments 2017-08-20 12:21:21 +02:00
Paweł Jastrzębski
ec51d6fc51 Merge pull request #242 from AcidWeb/master
Travis update
2017-08-19 20:58:38 +02:00
Paweł Jastrzębski
b7861d9d9e Travis update 2017-08-19 20:45:49 +02:00
Paweł Jastrzębski
0b4503af21 Merge pull request #241 from ciromattia/dev
5.4.1 preparation
2017-08-19 20:31:28 +02:00
Paweł Jastrzębski
437ffb9b10 Updated Docker recipe 2017-08-19 20:26:18 +02:00
Paweł Jastrzębski
066d1401bd Cleaned profile list 2017-08-17 10:11:24 +02:00
Paweł Jastrzębski
9babe68d2a Start using Travis CI as OS X binary builder 2017-08-16 18:55:20 +02:00
Paweł Jastrzębski
67de77538c Disable Panel View for Kindle Keyboard (#238) 2017-07-15 12:00:19 +02:00
Paweł Jastrzębski
c3e950f2ec Split README file 2017-04-09 16:50:11 +02:00
Paweł Jastrzębski
ac2934aba2 Fix setup.py 2017-04-09 15:41:12 +02:00
Paweł Jastrzębski
a5064a0c0a Merge pull request #233 from ciromattia/dev
5.4
2017-04-09 15:35:41 +02:00
Paweł Jastrzębski
2d712e796d Updated README + version bump 2017-04-09 15:34:59 +02:00
Paweł Jastrzębski
cc3da40fd7 Fixed page splitter 2017-04-06 15:24:03 +02:00
Paweł Jastrzębski
a53c272bd0 Tweaked webtoon splitter 2017-03-25 09:16:29 +01:00
Paweł Jastrzębski
6526b139fd Code cleanup 2017-03-25 08:05:28 +01:00
Paweł Jastrzębski
283d6101cd Reimplemented HQ Panel View (close #223) 2017-03-22 10:46:58 +01:00
Paweł Jastrzębski
02dab3c6ee Overhauled webtoon splitter 2017-03-22 07:56:23 +01:00
Paweł Jastrzębski
1895aa127d Decrease memory usage 2017-03-19 07:48:39 +01:00
Paweł Jastrzębski
c01ff83fce Merge pull request #231 from ciromattia/dev
5.3.1
2017-03-17 11:11:00 +01:00
Paweł Jastrzębski
4b670f3754 Update README.md 2017-03-17 11:09:29 +01:00
Paweł Jastrzębski
23b1560fa2 Updated README + version bump 2017-03-17 11:02:59 +01:00
Paweł Jastrzębski
62350608dc Added some additional checks 2017-03-17 10:58:48 +01:00
Paweł Jastrzębski
8048b91fa8 Overhauled startup functions for PyPI packaging 2017-03-17 10:55:56 +01:00
Paweł Jastrzębski
2e9b3389e4 Code cleanup 2017-03-15 18:30:32 +01:00
Paweł Jastrzębski
40e1ab4cf3 Updated build environment 2017-03-12 15:28:54 +01:00
Paweł Jastrzębski
d2c12c89e6 Updated dependencies 2017-03-12 13:29:10 +01:00
Paweł Jastrzębski
4647fd1f1d Merge pull request #224 from ciromattia/dev
5.3.0
2017-02-12 09:13:12 +01:00
Paweł Jastrzębski
010ad3c88c Updated README + version bump 2017-02-12 09:11:18 +01:00
Paweł Jastrzębski
4b0a94a8a0 Revert "Force admin rights for Windows version"
This reverts commit e1470cca15.
2017-02-06 19:21:33 +01:00
Paweł Jastrzębski
807a2d1dff Tweaked cover parsing 2017-02-05 08:53:09 +01:00
Paweł Jastrzębski
e1470cca15 Force admin rights for Windows version 2017-02-05 08:35:34 +01:00
Paweł Jastrzębski
02b9081e37 Improved compatibility with non-Kindle devices 2017-02-04 19:05:31 +01:00
Paweł Jastrzębski
495db88a9e Re-enabled Panel View support for Kindle Keyboard 2017-02-01 17:32:48 +01:00
Paweł Jastrzębski
2bea546a9d Re-enabled OS X file association mechanism 2017-01-21 22:34:39 +01:00
Paweł Jastrzębski
ee042ef98d Update build environment 2017-01-21 22:21:58 +01:00
bakatrouble
aea7c0fafb Fix unreadable text with dark qt themes
Fix unreadable text with dark qt themes #2 (file list)

Tweaks
2017-01-20 09:54:32 +01:00
Paweł Jastrzębski
45c1afcad4 Update build environment 2017-01-20 09:44:21 +01:00
Paweł Jastrzębski
b8e314f6ca Improved processing of credit pages 2016-12-08 10:36:05 +01:00
Paweł Jastrzębski
d76eea9f43 Merge pull request #216 from ciromattia/dev
5.2.1
2016-11-26 18:13:08 +01:00
Paweł Jastrzębski
2e55f22355 Updated README + version bump 2016-11-26 18:12:29 +01:00
Paweł Jastrzębski
30b8770e34 Improved error reporting 2016-11-26 17:59:40 +01:00
Paweł Jastrzębski
9ad161489f Decreased ferocity of margin cropping 2016-11-26 14:56:51 +01:00
Paweł Jastrzębski
bdb459cfab Code cleanup 2016-11-26 09:23:39 +01:00
Paweł Jastrzębski
2e85556543 GUI update 2016-11-25 18:57:42 +01:00
Paweł Jastrzębski
93ebbbd0af Refactored and improved output splitting 2016-11-25 18:05:05 +01:00
Paweł Jastrzębski
dd5c907bad Merge pull request #215 from ciromattia/dev
5.2
2016-11-22 08:53:56 +01:00
Paweł Jastrzębski
64fb4a9eca Updated README + version bump 2016-11-22 08:33:02 +01:00
Paweł Jastrzębski
284c577894 Fixed some file lock anomalies 2016-11-21 17:24:58 +01:00
Paweł Jastrzębski
c68c9892e4 GUI update 2016-11-21 16:36:46 +01:00
Paweł Jastrzębski
aa00ea3aa2 Expanded autoscale option 2016-11-21 15:59:14 +01:00
Paweł Jastrzębski
88f005824c Merge branch 'dev' of https://github.com/ciromattia/kcc into dev
# Conflicts:
#	kcc/comic2ebook.py
2016-11-21 14:06:04 +01:00
Paweł Jastrzębski
2a2bfae112 Dropped HQ PV option 2016-11-21 13:55:12 +01:00
Paweł Jastrzębski
583eec787f Merge pull request #214 from houcheng/autoscale
Add autoscale option
2016-11-21 13:51:51 +01:00
Houcheng Lin
9ce691aecb add autoscale option
Instead of fixed 1.5 scale ratio, the autoscale feature uses current page's
image width, and dynamically determine the needed scale ratio. The rendering
effects looks okay and speed is fine in my KPW1.

The generated panel view will have two view ports: (top and bottom).
2016-11-20 17:38:06 -05:00
Paweł Jastrzębski
d1a07d7ffa Improved cropping mechanism 2016-11-19 17:57:16 +01:00
Paweł Jastrzębski
b545f7ad48 Small tweaks 2016-11-18 08:39:16 +01:00
Paweł Jastrzębski
9e01797d28 Merge pull request #207 from ciromattia/dev
5.1.3
2016-09-17 08:24:22 +02:00
Paweł Jastrzębski
c68c5f25bf Updated README + version bump 2016-09-17 08:23:32 +02:00
Paweł Jastrzębski
a04bf5262f Added Kobo Aura ONE profile 2016-08-20 08:40:38 +02:00
Paweł Jastrzębski
b09b2527d9 Small bugfix 2016-08-20 08:14:39 +02:00
Paweł Jastrzębski
94b372f47d Tweaked glob (close #205) 2016-08-19 08:55:38 +02:00
Paweł Jastrzębski
b978adcc7c Updated README + version bump 2016-05-11 16:55:37 +02:00
Paweł Jastrzębski
9dee4432ad Updated Docker recipe 2016-05-11 12:15:56 +02:00
Paweł Jastrzębski
15055c6c0c Added missing Docker recipe 2016-05-01 09:42:12 +02:00
38 changed files with 1155 additions and 1191 deletions

8
.gitignore vendored
View File

@@ -4,13 +4,13 @@
.idea .idea
.DS_Store .DS_Store
Thumbs.db Thumbs.db
build
dist dist
Output Output
test test
solaio solaio
kindlegen* kindlegen*
*.spec *.spec
setup.bat kindlecomicconverter/sentry.py
setup.sh build/
kcc/sentry.py .python-version
KindleComicConverter.egg-info/

46
.travis.yml Normal file
View File

@@ -0,0 +1,46 @@
matrix:
include:
- os: linux
language: python
python: 3.6
dist: trusty
sudo: required
- os: osx
language: generic
osx_image: xcode6.4
before_install:
- if [ "$TRAVIS_OS_NAME" == "linux" ] ; then sudo apt-get -y install ruby ruby-dev ; fi
- if [ "$TRAVIS_OS_NAME" == "linux" ] ; then pip install --upgrade pip setuptools wheel ; fi
- if [ "$TRAVIS_OS_NAME" == "linux" ] ; then openssl aes-256-cbc -K $encrypted_a95564d8ff0d_key -iv $encrypted_a95564d8ff0d_iv -in other/linux/sentry.py.enc -out kindlecomicconverter/sentry.py -d ; fi
- if [ "$TRAVIS_OS_NAME" == "osx" ] ; then brew update ; fi
- if [ "$TRAVIS_OS_NAME" == "osx" ] ; then brew install python3 ; fi
- if [ "$TRAVIS_OS_NAME" == "osx" ] ; then brew upgrade node ; fi
- if [ "$TRAVIS_OS_NAME" == "osx" ] ; then pip3 install --upgrade pip setuptools wheel ; fi
- if [ "$TRAVIS_OS_NAME" == "osx" ] ; then openssl aes-256-cbc -K $encrypted_a95564d8ff0d_key -iv $encrypted_a95564d8ff0d_iv -in other/osx/sentry.py.enc -out kindlecomicconverter/sentry.py -d ; fi
install:
- if [ "$TRAVIS_OS_NAME" == "linux" ] ; then pip install -r requirements.txt ; fi
- if [ "$TRAVIS_OS_NAME" == "linux" ] ; then pip install certifi https://github.com/pyinstaller/pyinstaller/archive/develop.zip ; fi
- if [ "$TRAVIS_OS_NAME" == "linux" ] ; then gem install fpm ; fi
- if [ "$TRAVIS_OS_NAME" == "osx" ] ; then pip3 install -r requirements.txt ; fi
- if [ "$TRAVIS_OS_NAME" == "osx" ] ; then pip3 install certifi https://github.com/pyinstaller/pyinstaller/archive/develop.zip ; fi
- if [ "$TRAVIS_OS_NAME" == "osx" ] ; then npm install -g appdmg ; fi
script: python3 setup.py build_binary
before_deploy:
- shopt -s extglob
- rm -r dist/!(*.deb|*.dmg)
deploy:
provider: s3
skip_cleanup: true
access_key_id: AKIAIQNL5R4FI4C4NJYQ
secret_access_key:
secure: X66hYplxB4QSueljwvDfamNH/MQmHjo3mCofBcaTHAr7n2fp+yd2NzD2yy9h8NbsL0LWwx9wtJa/jpkIE02ZDfi9NrMLvKKFazzdpiyTMN5Yh85lHHyD1XIOCZRd4igaZ+O8975tJAEaEOPS+PE9XGZcRBh+y/eSJ+fMEgohaJ1MtDFbQR7X1cEw3iqbjrV2rlghZNCk/9mZEfObzAEjQiSDpv5G0IuIPRvYg/BgZt8chHVAe03B6oqcBa7uCBCTlfHIiNh1MWtP0B3NNBq3dcu9QHOFri1YqoZKuaPVCf6TFQL/NW5dFihegev2t9IwFyaBxytiT8fBkgQhP0VX8cuCwBAfnQGIogAu0eLSPp+E6dB/7Cpt2GDCk39+As8WKqt9hCRHmrvYhPA1Mq9QyEgKy/TKKKfDby3qVTIqYOQYpuQ1B7sIU651L5A+hBvZ1dqWIUz25h0zqjjeSFrcfNnf1e4tkk0QJvvnKqz0xsVaJxA2p07VJMRn8SlZQIJ2GEbMDeB5jxYtf5JzXywChP9adlPNjLna9G8ScnGSU1f7ZhsBQUEgY5jBlnX1lveyl3DUe6NP+qOTyljLWYwjx3AF4Zg10LYSecRS6hnqAUrGRmibDCIYclUzlJkVyjKGJ9uEyrUiCp0P0IsAzE1XhPVAWEyGUcWWGJG+jgmohSk=
bucket: kcc-deploy
region: eu-central-1
local_dir: dist
on:
repo: AcidWeb/kcc

343
CHANGELOG.md Normal file
View File

@@ -0,0 +1,343 @@
# CHANGELOG
#### 5.4.1:
* Minor bug fixes and tweaks
* Implemented new binary build pipeline
#### 5.4:
* Reimplemented high quality Panel View option
* Improved webtoon splitter
* Fixed page splitter
#### 5.3.1:
* Small increase of output quality
* Improved error reporting
* Internal changes and tweaks
#### 5.3:
* Vastly improved output compatibility for non-Kindle devices
* Enabled old pinch zoom for Kindle devices
* Re-enabled Panel View support for Kindle Keyboard
* Partially re-enabled OS X file association mechanism
* Fixed multiple smaller issues
#### 5.2.1:
* Improved directory parsing
* Tweaked margin detection algorithm
* Improved error reporting
#### 5.2:
* Added new Panel View options
* Implemented new margin detection algorithm
* Removed HQ Panel View mode
* Fixed multiple smaller issues
#### 5.1.3:
* Added Kobo Aura ONE profile
* Fixed few small bugs
#### 5.1.2:
* Fixed error reporting
#### 5.1.1:
* Fixed multiple GUI bugs
#### 5.1:
* GUI now can be resized and high DPI support was somewhat improved
* Added profile for Kindle Oasis
* Implemented new error reporting mechanism
* CLI version now support additional cropping options
* Fixed permission issues on Windows
* Fixed multiple smaller issues
#### 5.0.1:
* Fixed Panel View placement issues
* Decreased application startup time
* Fixed multiple smaller issues
#### 5.0:
* Major overhaul of internal mechanisms and GUI
* Added cover upload feature
* Tweaked Webtoon parsing mode
* Fixed multiple smaller issues
* Migrated build enviroment to PyInstaller
#### 4.6.5:
* Fixed multiple Windows and OS X issues
* Allowed Linux release to use older PyQT5 version
#### 4.6.4:
* Fixed multiple Windows specific problems
* Improved error handling
* Improved color detection algorithm
* New, slimmer OS X release
#### 4.6.3:
* Implemented remote bug reporting
* Minor bug fixes and GUI tweaks
#### 4.6.2:
* Fixed critical MOBI header bug
* Fixed metadata encoding error
#### 4.6.1:
* Fixed KEPUB TOC generator
* Added warning about too small input files
* ComicRack Summary metadata field is now parsed
* Small tweaks of KEPUB output
#### 4.6:
* KEPUB is now default output for all Kobo profiles
* EPUB output now produce fully valid EPUB 3.0.1
* Added profile for Kindle Paperwhite 3
* Dropped official support of all Kindle Fire models and Kindle for Android
* Other minor tweaks
#### 4.5.1:
* Added Kobo Glo HD profile
* Fixed RAR/CBR parsing anomalies
* Minor bug fixes and tweaks
#### 4.5:
* Added simple ComicRack metadata editor
* Re-enabled Manga Cover Database support
* ComicRack bookmarks are now parsed
* Fixed glitches in Kindle Voyage profile
* Fixed problems with directory locks on Windows
* Fixed sorting anomalies
* Improved conversion speed
#### 4.4.1:
* Fixed problems with OSX GUI
* Added one missing DLL to Windows installer
#### 4.4:
* Improved speed and quality of conversion
* Added RAR5 support
* Dropped BMP and TIFF support
* Fixed some WebToon mode bugs
* Fixed CBR parsing on OSX
#### 4.3.1:
* Fixed Kindle Voyage profile
* Fixed some bugs in OS X release
* CLI version now support multiple input files at once
* Disabled MCB support
* Other minor tweaks
#### 4.3:
* Added profiles for Kindle Voyage and Kobo Aura H2O
* Added missing features to CLI version
* Other minor bug fixes
#### 4.2.1:
* Improved margin color detection
* Fixed random crashes of MOBI processing step
* Fixed resizing problems in high quality mode
* Fixed some MCD support bugs
* Default output format for Kindle DX is now CBZ
#### 4.2:
* Added [Manga Cover Database](http://manga.joentjuh.nl/) support
* Officially dropped Windows XP support
* Fixed _Other_ profile
* Fixed problems with page order on stock KOBO CBZ reader
* Many other small bug fixes and tweaks
#### 4.1:
* Thanks to code contributed by Kevin Hendricks speed of MOBI creation was greatly increased
* Improved performance on Windows
* Improved MOBI splitting and changed maximal size of output file
* Fixed _No optimization_ mode
* Multiple small tweaks nad minor bug fixes
#### 4.0.2:
* Fixed some Windows and OSX specific bugs
* Fixed problem with marigns when using HQ mode
#### 4.0.1:
* Fixed file lock problems that plagued some Windows users
* Fixed content server failing to start on Windows
* Improved performance of WebToon splitter
* Tweaked margin color detection
#### 4.0:
* KCC now use Python 3.3 and Qt 5.2
* Full UTF-8 awareness
* CBZ output now support Manga mode
* Improved Panel View support and margin color detection
* Added drag&drop support
* Output directory can be now selected
* Windows release now have auto-updater
* Names of chapters on Kindle should be now more user friendly
* Fixed OSX file association support
* Many extensive internal changes and tweaks
#### 3.7.2:
* Fixed problems with HQ mode
#### 3.7.1:
* Hotfixed Kobo profiles
#### 3.7:
* Added profiles for KOBO devices
* Improved Panel View support
* Improved WebToon splitter
* Improved margin color autodetection
* Tweaked EPUB output
* Fixed stretching option
* GUI tweaks and minor bugfixes
#### 3.6.2:
* Fixed previous PNG output fix
* Fixed Panel View anomalies
#### 3.6.1:
* Fixed PNG output
#### 3.6:
* Increased quality of Panel View zoom
* Creation of multipart MOBI output is now faster on machines with 4GB+ RAM
* Automatic gamma correction now distinguishes color and grayscale images
* Added ComicRack metadata parser
* Implemented new method to detect border color in non-webtoon comics
* Upscaling is now enabled by default for Kindle Fire HD/HDX
* Windows nad Linux releases now have tray icon
* Fixed Kindle Fire HDX 7" output
* Increased target resolution for Kindle DX/DXG CBZ output
#### 3.5:
* Added simple content server - Converted files can be now delivered wireless
* Added proper Windows installer
* Improved multiprocessing speed
* GUI tweaks and minor bug fixes
#### 3.4:
* Improved PNG output
* Increased quality of upscaling
* Added support of file association - KCC can now open CBZ, CBR, CB7, ZIP, RAR, 7Z and PDF files directly
* Paths that contain UTF-8 characters are now supported
* Migrated to new version of Pillow library
* Merged DX and DXG profiles
* Many other minor bug fixes and GUI tweaks
#### 3.3:
* Margins are now automatically omitted in Panel View mode
* Margin color fill is now autodetected
* Created MOBI files are not longer marked as _Personal_ on newer Kindle models
* Layout of panels in Panel View mode is now automatically adjusted to content
* Fixed Kindle 2/DX/DXG profiles - no more blank pages
* All Kindle Fire profiles now support hiqh quality Panel View
* Added support of 7z/CB7 files
* Added Kindle Fire HDX profile
* Support for Virtual Panel View was removed
* Profiles for Kindle Keyboard, Touch and Non-Touch are now merged
* Windows release is now bundled with UnRAR and 7za
* Small GUI tweaks
#### 3.2:
* Too big EPUB files are now splitted before conversion to MOBI
* Added experimental parser of manga webtoons
* Improved error handling
#### 3.2.1:
* Hotfixed crash occurring on OS with Russian locale
#### 3.1:
* Added profile: Kindle for Android
* Add file/directory dialogs now support multiselect
* Many small fixes and tweaks
#### 3.0:
* New QT GUI
* Merge with AWKCC
* Added ultra quality mode
* Added support for custom width/height
* Added option to disable color conversion
#### 2.10:
* Multiprocessing support
* Kindle Fire support (color EPUB/MOBI)
* Panel View support for horizontal content
* Fixed panel order for horizontal pages when --rotate is enabled
* Disabled cropping and page number cutting for blank pages
* Fixed some slugify issues with specific file naming conventions (#50, #51)
#### 2.9
* Added support for generating a plain CBZ (skipping all the EPUB/MOBI generation) (#45)
* Prevent output file overwriting the source one: if a duplicate name is detected, append _kcc to the name
* Rarfile library updated to 2.6
* Added GIF, TIFF and BMP to supported formats (#42)
* Filenames slugifications (#28, #31, #9, #8)
#### 2.8
* Updated rarfile library
* Panel View support + HQ support (#36) - new option: --nopanelviewhq
* Split profiles for K4NT and K4T
* Rewrite of Landscape Mode support (huge readability improvement for KPW)
* Upscale use now BILINEAR method
* Added generic CSS file
* Optimized archive extraction for zip/rar files (#40)
#### 2.7
* Lots of GUI improvements (#27, #13)
* Added gamma support within --gamma option (defaults to profile-specified gamma) (#26, #27)
* Added --nodithering option to prevent dithering optimizations (#27)
* EPUB margins support (#30)
* Fixed no file added if file has no spaces on Windows (#25)
* Gracefully exit if unrar missing (#15)
* Do not call kindlegen if source EPUB is bigger than 320MB (#17)
* Get filetype from magic number (#14)
* PDF conversion works again
#### 2.6
* Added --rotate option to rotate landscape images instead of splitting them (#16, #24)
* Added --output option to customize EPUB output dir/file (#22)
* Add rendition:layout and rendition:orientation EPUB meta tags (supported by new kindlegen 2.8)
* Fixed natural sorting for files (#18)
#### 2.5
* Added --black-borders option to set added borders black when page's ratio is not the device's one (#11).
* Fixes EPUB containing zipped itself (#10)
#### 2.4
* Use temporary directory as workdir (fixes converting from external volumes and zipfiles renaming)
* Fixed "add folders" from GUI.
#### 2.3
* Fixed win32 EPUB generation, folder handling, filenames with spaces and subfolders
#### 2.2:
* Added (valid!) EPUB 2.0 output
* Rename .zip files to .cbz to avoid overwriting
#### 2.1
* Added basic error reporting
#### 2.0
* GUI! AppleScript is gone and Tk is used to provide cross-platform GUI support.
#### 1.5
* Added subfolder support for multiple chapters.
#### 1.4.1
* Fixed a serious bug on resizing when img ratio was bigger than device one
#### 1.4
* Added some options for controlling image optimization
* Further optimization (ImageOps, page numbering cut, autocontrast)
#### 1.3
* Fixed an issue in OPF generation for device resolution
* Reworked options system (call with -h option to get the inline help)
#### 1.2
* Comic optimizations! Split pages not target-oriented (landscape with portrait target or portrait with landscape target), add palette and other image optimizations from Mangle. WARNING: PIL is required for all image mangling!
#### 1.1.1
* Added support for CBZ/CBR files in Kindle Comic Converter
#### 1.1
* Added support for CBZ/CBR files in comic2ebook.py
#### 1.0
* Initial version

View File

@@ -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-2016 Paweł Jastrzębski <pawelj@iosphe.re> Copyright (c) 2013-2017 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

1
MANIFEST.in Normal file
View File

@@ -0,0 +1 @@
exclude kindlecomicconverter/sentry.py

391
README.md
View File

@@ -1,4 +1,6 @@
# KCC # KCC
[![GitHub release](https://img.shields.io/github/release/ciromattia/kcc.svg)](https://github.com/ciromattia/kcc/releases) [![PyPI](https://img.shields.io/pypi/v/KindleComicConverter.svg)](https://pypi.python.org/pypi/KindleComicConverter) [![AUR](https://img.shields.io/aur/version/kcc.svg)](https://aur.archlinux.org/packages/kcc/)
**Kindle Comic Converter** is a Python app to convert comic/manga files or folders to EPUB, Panel View MOBI or E-Ink optimized CBZ. **Kindle Comic Converter** is a Python app to convert comic/manga files or folders to EPUB, Panel View MOBI or E-Ink optimized CBZ.
It was initially developed for Kindle but since version 4.6 it outputs valid EPUB 3.0 so _**despite its name, KCC is It was initially developed for Kindle but since version 4.6 it outputs valid EPUB 3.0 so _**despite its name, KCC is
@@ -6,7 +8,7 @@ actually a comic/manga to EPUB converter that every e-reader owner can happily u
It can also optionally optimize images by applying a number of transformations. It can also optionally optimize images by applying a number of transformations.
### A word of warning ### A word of warning
**KCC** _is not_ [Amazon's Kindle Comic Creator](http://www.amazon.com/gp/feature.html?ie=UTF8&docId=1001103761) nor is in any way endorsed by Amazon. **KCC** _is not_ [Amazon's Kindle Comic Creator](http://www.amazon.com/gp/feature.html?ie=UTF8&docId=1001103761) nor is in any way endorsed by Amazon.
Amazon's tool is for comic publishers and involves a lot of manual effort, while **KCC** is for comic/manga readers. Amazon's tool is for comic publishers and involves a lot of manual effort, while **KCC** is for comic/manga readers.
_KC2_ in no way is a replacement for **KCC** so you can be quite confident we'll going to carry on developing our little monster ;-) _KC2_ in no way is a replacement for **KCC** so you can be quite confident we'll going to carry on developing our little monster ;-)
@@ -17,32 +19,37 @@ If you can fix an open issue, fork & make a pull request.
If you find **KCC** valuable you can consider donating to the authors: If you find **KCC** valuable you can consider donating to the authors:
- Ciro Mattia Gonano: - Ciro Mattia Gonano:
- [![Donate](https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=D8WNYNPBGDAS2) - [![Donate PayPal](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=D8WNYNPBGDAS2)
- [![Flattr this](http://api.flattr.com/button/flattr-badge-large.png)](http://flattr.com/thing/2260449/ciromattiakcc-on-GitHub) - [![Donate Flattr](https://img.shields.io/badge/Donate-Flattr-green.svg)](http://flattr.com/thing/2260449/ciromattiakcc-on-GitHub)
- Paweł Jastrzębski: - Paweł Jastrzębski:
- [![Donate](https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YTTJ4LK2JDHPS) - [![Donate PayPal](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YTTJ4LK2JDHPS)
- Bitcoin: 1W15wwqsfd7wbaZ6wvSJf1LW1bz6q5L8b - [![Donate Bitcoin](https://img.shields.io/badge/Donate-Bitcoin-green.svg)](https://jastrzeb.ski/donate/)
## 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 (64-bit only):** [http://kcc.iosphe.re/Windows/](http://kcc.iosphe.re/Windows/)
- **Linux (Glibc 2.19+):** [http://kcc.iosphe.re/Linux/](http://kcc.iosphe.re/Linux/) - **Linux (Glibc 2.19+):** [http://kcc.iosphe.re/Linux/](http://kcc.iosphe.re/Linux/)
- **OS X (10.9+):** [http://kcc.iosphe.re/OSX/](http://kcc.iosphe.re/OSX/) - **OS X (10.10+):** [http://kcc.iosphe.re/OSX/](http://kcc.iosphe.re/OSX/)
## PYPI
**KCC** is also available on PyPI.
```
pip install KindleComicConverter
```
## DEPENDENCIES ## DEPENDENCIES
Following software is required to run Linux version of **KCC** and/or bare sources: Following software is required to run Linux version of **KCC** and/or bare sources:
- Python 3.3+ - Python 3.3+
- [PyQt](https://pypi.python.org/pypi/PyQt5) 5.6.0+ - [PyQt5](https://pypi.python.org/pypi/PyQt5) 5.6.0+
- [Pillow](https://pypi.python.org/pypi/Pillow/) 3.2.0+ - [Pillow](https://pypi.python.org/pypi/Pillow/) 4.0.0+
- [psutil](https://pypi.python.org/pypi/psutil) 4.1.0+ - [psutil](https://pypi.python.org/pypi/psutil) 5.0.0+
- [python-slugify](https://pypi.python.org/pypi/python-slugify) 1.2.0+ - [python-slugify](https://pypi.python.org/pypi/python-slugify) 1.2.1+
- [raven](https://pypi.python.org/pypi/raven) 5.13.0+ - [raven](https://pypi.python.org/pypi/raven) 6.0.0+
- [scandir](https://pypi.python.org/pypi/scandir) 1.2.0+ _(needed only when using Python 3.3 or 3.4)_
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 unrar
sudo pip3 install --upgrade pillow python-slugify psutil scandir raven pyqt5 sudo pip3 install --upgrade pillow python-slugify psutil pyqt5 raven
``` ```
### Optional dependencies ### Optional dependencies
@@ -60,12 +67,12 @@ sudo pip3 install --upgrade pillow python-slugify psutil scandir raven pyqt5
## USAGE ## USAGE
Should be pretty self-explanatory. All options have detailed informations in tooltips. Should be pretty self-explanatory. All options have detailed information in tooltips.
After completed conversion you should find ready file alongside the original input file (same directory). After completed conversion, you should find ready file alongside the original input file (same directory).
Please check [our wiki](https://github.com/ciromattia/kcc/wiki/) for more details. Please check [our wiki](https://github.com/ciromattia/kcc/wiki/) for more details.
CLI version of **KCC** is intended for power users. It is not idiot-proof like GUI :-) CLI version of **KCC** is intended for power users. It allows using options that might not be compatible and decrease the quality of output.
### Standalone `kcc-c2e.py` usage: ### Standalone `kcc-c2e.py` usage:
@@ -75,9 +82,12 @@ Usage: kcc-c2e [options] comic_file|comic_folder
Options: Options:
MAIN: MAIN:
-p PROFILE, --profile=PROFILE -p PROFILE, --profile=PROFILE
Device profile (Available options: K1, K2, K3, K45, KDX, Device profile (Available options: K1, K2, K34, K578,
KPW, KV, KoMT, KoG, KoGHD, KoA, KoAHD, KoAH2O) [Default=KV] KDX, KPW, KV, KoMT, KoG, KoGHD, KoA, KoAHD, KoAH2O,
KoAO) [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
-2, --two-panel Display two not four panels in Panel View mode
-w, --webtoon Webtoon processing mode -w, --webtoon Webtoon processing mode
OUTPUT SETTINGS: OUTPUT SETTINGS:
@@ -86,29 +96,31 @@ Options:
-t TITLE, --title=TITLE -t TITLE, --title=TITLE
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, CBZ) Output format (Available options: Auto, MOBI, EPUB,
[Default=Auto] CBZ) [Default=Auto]
-b, --batchsplit Split output into multiple files -b BATCHSPLIT, --batchsplit=BATCHSPLIT
Split output into multiple files. 0: Don't split 1:
Automatic mode 2: Consider every subdirectory as
separate volume [Default=0]
PROCESSING: PROCESSING:
-u, --upscale Resize images smaller than device's resolution -u, --upscale Resize images smaller than device's resolution
-s, --stretch Stretch images to device's resolution -s, --stretch Stretch images to device's resolution
-r SPLITTER, --splitter=SPLITTER -r SPLITTER, --splitter=SPLITTER
Double page parsing mode. 0: Split 1: Rotate 2: Both [Default=0] Double page parsing mode. 0: Split 1: Rotate 2: Both
[Default=0]
-g GAMMA, --gamma=GAMMA -g GAMMA, --gamma=GAMMA
Apply gamma correction to linearize the image [Default=Auto] Apply gamma correction to linearize the image
--hq Enable high quality Panel View [Default=Auto]
-c CROPPING, --cropping=CROPPING
Set cropping mode. 0: Disabled 1: Margins 2: Margins +
page numbers [Default=2]
--cp=CROPPINGP, --croppingpower=CROPPINGP
Set cropping power [Default=1.0]
--blackborders Disable autodetection and force black borders --blackborders Disable autodetection and force black borders
--whiteborders Disable autodetection and force white borders --whiteborders Disable autodetection and force white borders
--forcecolor Don't convert images to grayscale --forcecolor Don't convert images to grayscale
--forcepng Create PNG files instead JPEG --forcepng Create PNG files instead JPEG
--cropping=CROPPING
Set cropping mode. 0: Disabled 1: Margins 2: Margins +
page numbers [Default=2]
--croppingpower=CROPPINGP
Set margin cropping threshold [Default=0.1]
--croppingpowerpage=CROPPINGPN
Set page number cropping threshold [Default=5.0]
CUSTOM PROFILE: CUSTOM PROFILE:
--customwidth=CUSTOMWIDTH --customwidth=CUSTOMWIDTH
@@ -133,7 +145,7 @@ Options:
-m, --merge Combine every directory into a single image before splitting -m, --merge Combine every directory into a single image before splitting
OTHER: OTHER:
-d, --debug Create debug file for every splitted image -d, --debug Create debug file for every split image
-h, --help Show this help message and exit -h, --help Show this help message and exit
``` ```
@@ -153,319 +165,10 @@ The app relies and includes the following scripts:
* [Kindle Paperwhite 3 / Voyage / Oasis](http://kcc.iosphe.re/Samples/Ubunchu!-KV.mobi) * [Kindle Paperwhite 3 / 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!-K45.mobi)
* [Kindle Keyboard](http://kcc.iosphe.re/Samples/Ubunchu!-K3.mobi)
* [Kindle DX/DXG](http://kcc.iosphe.re/Samples/Ubunchu!-KDX.cbz)
* [Kobo Mini/Touch](http://kcc.iosphe.re/Samples/Ubunchu-KoMT.kepub.epub)
* [Kobo Glo](http://kcc.iosphe.re/Samples/Ubunchu-KoG.kepub.epub)
* [Kobo Glo HD](http://kcc.iosphe.re/Samples/Ubunchu-KoGHD.kepub.epub)
* [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)
## CHANGELOG
####5.1.1:
* Fixed multiple GUI bugs
####5.1:
* GUI now can be resized and high DPI support was somewhat improved
* Added profile for Kindle Oasis
* Implemented new error reporting mechanism
* CLI version now support additional cropping options
* Fixed permission issues on Windows
* Fixed multiple smaller issues
####5.0.1:
* Fixed Panel View placement issues
* Decreased application startup time
* Fixed multiple smaller issues
####5.0:
* Major overhaul of internal mechanisms and GUI
* Added cover upload feature
* Tweaked Webtoon parsing mode
* Fixed multiple smaller issues
* Migrated build enviroment to PyInstaller
####4.6.5:
* Fixed multiple Windows and OS X issues
* Allowed Linux release to use older PyQT5 version
####4.6.4:
* Fixed multiple Windows specific problems
* Improved error handling
* Improved color detection algorithm
* New, slimmer OS X release
####4.6.3:
* Implemented remote bug reporting
* Minor bug fixes and GUI tweaks
####4.6.2:
* Fixed critical MOBI header bug
* Fixed metadata encoding error
####4.6.1:
* Fixed KEPUB TOC generator
* Added warning about too small input files
* ComicRack Summary metadata field is now parsed
* Small tweaks of KEPUB output
####4.6:
* KEPUB is now default output for all Kobo profiles
* EPUB output now produce fully valid EPUB 3.0.1
* Added profile for Kindle Paperwhite 3
* Dropped official support of all Kindle Fire models and Kindle for Android
* Other minor tweaks
####4.5.1:
* Added Kobo Glo HD profile
* Fixed RAR/CBR parsing anomalies
* Minor bug fixes and tweaks
####4.5:
* Added simple ComicRack metadata editor
* Re-enabled Manga Cover Database support
* ComicRack bookmarks are now parsed
* Fixed glitches in Kindle Voyage profile
* Fixed problems with directory locks on Windows
* Fixed sorting anomalies
* Improved conversion speed
####4.4.1:
* Fixed problems with OSX GUI
* Added one missing DLL to Windows installer
####4.4:
* Improved speed and quality of conversion
* Added RAR5 support
* Dropped BMP and TIFF support
* Fixed some WebToon mode bugs
* Fixed CBR parsing on OSX
####4.3.1:
* Fixed Kindle Voyage profile
* Fixed some bugs in OS X release
* CLI version now support multiple input files at once
* Disabled MCB support
* Other minor tweaks
####4.3:
* Added profiles for Kindle Voyage and Kobo Aura H2O
* Added missing features to CLI version
* Other minor bug fixes
####4.2.1:
* Improved margin color detection
* Fixed random crashes of MOBI processing step
* Fixed resizing problems in high quality mode
* Fixed some MCD support bugs
* Default output format for Kindle DX is now CBZ
####4.2:
* Added [Manga Cover Database](http://manga.joentjuh.nl/) support
* Officially dropped Windows XP support
* Fixed _Other_ profile
* Fixed problems with page order on stock KOBO CBZ reader
* Many other small bug fixes and tweaks
####4.1:
* Thanks to code contributed by Kevin Hendricks speed of MOBI creation was greatly increased
* Improved performance on Windows
* Improved MOBI splitting and changed maximal size of output file
* Fixed _No optimization_ mode
* Multiple small tweaks nad minor bug fixes
####4.0.2:
* Fixed some Windows and OSX specific bugs
* Fixed problem with marigns when using HQ mode
####4.0.1:
* Fixed file lock problems that plagued some Windows users
* Fixed content server failing to start on Windows
* Improved performance of WebToon splitter
* Tweaked margin color detection
####4.0:
* KCC now use Python 3.3 and Qt 5.2
* Full UTF-8 awareness
* CBZ output now support Manga mode
* Improved Panel View support and margin color detection
* Added drag&drop support
* Output directory can be now selected
* Windows release now have auto-updater
* Names of chapters on Kindle should be now more user friendly
* Fixed OSX file association support
* Many extensive internal changes and tweaks
####3.7.2:
* Fixed problems with HQ mode
####3.7.1:
* Hotfixed Kobo profiles
####3.7:
* Added profiles for KOBO devices
* Improved Panel View support
* Improved WebToon splitter
* Improved margin color autodetection
* Tweaked EPUB output
* Fixed stretching option
* GUI tweaks and minor bugfixes
####3.6.2:
* Fixed previous PNG output fix
* Fixed Panel View anomalies
####3.6.1:
* Fixed PNG output
####3.6:
* Increased quality of Panel View zoom
* Creation of multipart MOBI output is now faster on machines with 4GB+ RAM
* Automatic gamma correction now distinguishes color and grayscale images
* Added ComicRack metadata parser
* Implemented new method to detect border color in non-webtoon comics
* Upscaling is now enabled by default for Kindle Fire HD/HDX
* Windows nad Linux releases now have tray icon
* Fixed Kindle Fire HDX 7" output
* Increased target resolution for Kindle DX/DXG CBZ output
####3.5:
* Added simple content server - Converted files can be now delivered wireless
* Added proper Windows installer
* Improved multiprocessing speed
* GUI tweaks and minor bug fixes
####3.4:
* Improved PNG output
* Increased quality of upscaling
* Added support of file association - KCC can now open CBZ, CBR, CB7, ZIP, RAR, 7Z and PDF files directly
* Paths that contain UTF-8 characters are now supported
* Migrated to new version of Pillow library
* Merged DX and DXG profiles
* Many other minor bug fixes and GUI tweaks
####3.3:
* Margins are now automatically omitted in Panel View mode
* Margin color fill is now autodetected
* Created MOBI files are not longer marked as _Personal_ on newer Kindle models
* Layout of panels in Panel View mode is now automatically adjusted to content
* Fixed Kindle 2/DX/DXG profiles - no more blank pages
* All Kindle Fire profiles now support hiqh quality Panel View
* Added support of 7z/CB7 files
* Added Kindle Fire HDX profile
* Support for Virtual Panel View was removed
* Profiles for Kindle Keyboard, Touch and Non-Touch are now merged
* Windows release is now bundled with UnRAR and 7za
* Small GUI tweaks
####3.2:
* Too big EPUB files are now splitted before conversion to MOBI
* Added experimental parser of manga webtoons
* Improved error handling
####3.2.1:
* Hotfixed crash occurring on OS with Russian locale
####3.1:
* Added profile: Kindle for Android
* Add file/directory dialogs now support multiselect
* Many small fixes and tweaks
####3.0:
* New QT GUI
* Merge with AWKCC
* Added ultra quality mode
* Added support for custom width/height
* Added option to disable color conversion
####2.10:
* Multiprocessing support
* Kindle Fire support (color EPUB/MOBI)
* Panel View support for horizontal content
* Fixed panel order for horizontal pages when --rotate is enabled
* Disabled cropping and page number cutting for blank pages
* Fixed some slugify issues with specific file naming conventions (#50, #51)
####2.9
* Added support for generating a plain CBZ (skipping all the EPUB/MOBI generation) (#45)
* Prevent output file overwriting the source one: if a duplicate name is detected, append _kcc to the name
* Rarfile library updated to 2.6
* Added GIF, TIFF and BMP to supported formats (#42)
* Filenames slugifications (#28, #31, #9, #8)
####2.8
* Updated rarfile library
* Panel View support + HQ support (#36) - new option: --nopanelviewhq
* Split profiles for K4NT and K4T
* Rewrite of Landscape Mode support (huge readability improvement for KPW)
* Upscale use now BILINEAR method
* Added generic CSS file
* Optimized archive extraction for zip/rar files (#40)
####2.7
* Lots of GUI improvements (#27, #13)
* Added gamma support within --gamma option (defaults to profile-specified gamma) (#26, #27)
* Added --nodithering option to prevent dithering optimizations (#27)
* EPUB margins support (#30)
* Fixed no file added if file has no spaces on Windows (#25)
* Gracefully exit if unrar missing (#15)
* Do not call kindlegen if source EPUB is bigger than 320MB (#17)
* Get filetype from magic number (#14)
* PDF conversion works again
####2.6
* Added --rotate option to rotate landscape images instead of splitting them (#16, #24)
* Added --output option to customize EPUB output dir/file (#22)
* Add rendition:layout and rendition:orientation EPUB meta tags (supported by new kindlegen 2.8)
* Fixed natural sorting for files (#18)
####2.5
* Added --black-borders option to set added borders black when page's ratio is not the device's one (#11).
* Fixes EPUB containing zipped itself (#10)
####2.4
* Use temporary directory as workdir (fixes converting from external volumes and zipfiles renaming)
* Fixed "add folders" from GUI.
####2.3
* Fixed win32 EPUB generation, folder handling, filenames with spaces and subfolders
####2.2:
* Added (valid!) EPUB 2.0 output
* Rename .zip files to .cbz to avoid overwriting
####2.1
* Added basic error reporting
####2.0
* GUI! AppleScript is gone and Tk is used to provide cross-platform GUI support.
####1.5
* Added subfolder support for multiple chapters.
####1.4.1
* Fixed a serious bug on resizing when img ratio was bigger than device one
####1.4
* Added some options for controlling image optimization
* Further optimization (ImageOps, page numbering cut, autocontrast)
####1.3
* Fixed an issue in OPF generation for device resolution
* Reworked options system (call with -h option to get the inline help)
####1.2
* Comic optimizations! Split pages not target-oriented (landscape with portrait target or portrait with landscape target), add palette and other image optimizations from Mangle. WARNING: PIL is required for all image mangling!
####1.1.1
* Added support for CBZ/CBR files in Kindle Comic Converter
####1.1
* Added support for CBZ/CBR files in comic2ebook.py
####1.0
* Initial version
## PRIVACY ## PRIVACY
**KCC** is initiating internet connections in three cases: **KCC** is initiating internet connections in three cases:
@@ -477,5 +180,5 @@ The app relies and includes the following scripts:
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-2016 Ciro Mattia Gonano and Paweł Jastrzębski. Copyright (c) 2012-2017 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.

27
appveyor.yml Normal file
View File

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

View File

@@ -47,7 +47,7 @@
<item row="2" column="0" colspan="2"> <item row="2" column="0" colspan="2">
<widget class="QListWidget" name="jobList"> <widget class="QListWidget" name="jobList">
<property name="styleSheet"> <property name="styleSheet">
<string notr="true">QListWidget#jobList {background:#ffffff;background-image:url(:/Other/icons/list_background.png);background-position:center center;background-repeat:no-repeat;}</string> <string notr="true">QListWidget#jobList {background:#ffffff;background-image:url(:/Other/icons/list_background.png);background-position:center center;background-repeat:no-repeat;color:rgb(0,0,0);}</string>
</property> </property>
<property name="selectionMode"> <property name="selectionMode">
<enum>QAbstractItemView::NoSelection</enum> <enum>QAbstractItemView::NoSelection</enum>
@@ -87,7 +87,7 @@
</sizepolicy> </sizepolicy>
</property> </property>
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Resolution of target device.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Resolution of the target device.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="text"> <property name="text">
<string>Custom height:</string> <string>Custom height:</string>
@@ -97,7 +97,7 @@
<item row="0" column="1"> <item row="0" column="1">
<widget class="QSpinBox" name="widthBox"> <widget class="QSpinBox" name="widthBox">
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Resolution of target device.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Resolution of the target device.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="maximum"> <property name="maximum">
<number>2160</number> <number>2160</number>
@@ -113,7 +113,7 @@
</sizepolicy> </sizepolicy>
</property> </property>
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Resolution of target device.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Resolution of the target device.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="text"> <property name="text">
<string>Custom width:</string> <string>Custom width:</string>
@@ -123,7 +123,7 @@
<item row="0" column="3"> <item row="0" column="3">
<widget class="QSpinBox" name="heightBox"> <widget class="QSpinBox" name="heightBox">
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Resolution of target device.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Resolution of the target device.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="maximum"> <property name="maximum">
<number>3840</number> <number>3840</number>
@@ -174,10 +174,13 @@
<item row="0" column="2"> <item row="0" column="2">
<widget class="QCheckBox" name="qualityBox"> <widget class="QCheckBox" name="qualityBox">
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;High quality Panel View.&lt;br/&gt;Require source files with bigger resolution than target device.&lt;br/&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Highly impact size of output file!&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Unchecked - 4 panels&lt;br/&gt;&lt;/span&gt;Zoom each corner separately.&lt;/p&gt;&lt;p style='white-space:pre'&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Indeterminate - 2 panels&lt;br/&gt;&lt;/span&gt;Zoom only the top and bottom of the page.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Checked - 4 high-quality panels&lt;br/&gt;&lt;/span&gt;Zoom each corner separately. Try to increase the quality of magnification. Check wiki for more details.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="text"> <property name="text">
<string>HQ zoom</string> <string>Panel View 4/2/HQ</string>
</property>
<property name="tristate">
<bool>true</bool>
</property> </property>
</widget> </widget>
</item> </item>
@@ -217,7 +220,7 @@
<item row="2" column="0"> <item row="2" column="0">
<widget class="QCheckBox" name="borderBox"> <widget class="QCheckBox" name="borderBox">
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Unchecked - Autodetection&lt;br/&gt;&lt;/span&gt;Color of margins fill will be detected automatically.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Indeterminate - White&lt;br/&gt;&lt;/span&gt;Margins will be filled with white color.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Checked - Black&lt;br/&gt;&lt;/span&gt;Margins will be filled with black color.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Unchecked - Autodetection&lt;br/&gt;&lt;/span&gt;The color of margins fill will be detected automatically.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Indeterminate - White&lt;br/&gt;&lt;/span&gt;Margins will be filled with white color.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Checked - Black&lt;br/&gt;&lt;/span&gt;Margins will be filled with black color.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="text"> <property name="text">
<string>W/B margins</string> <string>W/B margins</string>
@@ -228,12 +231,12 @@
</widget> </widget>
</item> </item>
<item row="2" column="1"> <item row="2" column="1">
<widget class="QCheckBox" name="noDitheringBox"> <widget class="QCheckBox" name="outputSplit">
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Create PNG files instead JPEG.&lt;br/&gt;Quality increase is not noticeable on most of devices.&lt;br/&gt;Output files &lt;span style=&quot; font-weight:600;&quot;&gt;might&lt;/span&gt; be smaller.&lt;br/&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;MOBI conversion will be much slower.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Unchecked - Automatic mode&lt;br/&gt;&lt;/span&gt;The output will be split automatically.&lt;/p&gt;&lt;p style='white-space:pre'&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Checked - Volume mode&lt;br/&gt;&lt;/span&gt;Every subdirectory will be considered as a separate volume.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="text"> <property name="text">
<string>PNG output</string> <string>Output split</string>
</property> </property>
</widget> </widget>
</item> </item>
@@ -510,7 +513,7 @@
<tabstop>upscaleBox</tabstop> <tabstop>upscaleBox</tabstop>
<tabstop>gammaBox</tabstop> <tabstop>gammaBox</tabstop>
<tabstop>borderBox</tabstop> <tabstop>borderBox</tabstop>
<tabstop>noDitheringBox</tabstop> <tabstop>outputSplit</tabstop>
<tabstop>colorBox</tabstop> <tabstop>colorBox</tabstop>
<tabstop>editorButton</tabstop> <tabstop>editorButton</tabstop>
<tabstop>wikiButton</tabstop> <tabstop>wikiButton</tabstop>

View File

@@ -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-2016 Pawel Jastrzebski <pawelj@iosphe.re> # Copyright (c) 2013-2017 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,14 +23,9 @@ 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 kcc.shared import dependencyCheck
dependencyCheck(2)
from multiprocessing import freeze_support from multiprocessing import freeze_support
from kcc import __version__ from kindlecomicconverter.startup import startC2E
from kcc.comic2ebook import main
if __name__ == "__main__": if __name__ == "__main__":
freeze_support() freeze_support()
print('comic2ebook v' + __version__ + ' - Written by Ciro Mattia Gonano and Pawel Jastrzebski.') startC2E()
sys.exit(main(sys.argv[1:]))

View File

@@ -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-2016 Pawel Jastrzebski <pawelj@iosphe.re> # Copyright (c) 2013-2017 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,14 +23,9 @@ 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 kcc.shared import dependencyCheck
dependencyCheck(1)
from multiprocessing import freeze_support from multiprocessing import freeze_support
from kcc import __version__ from kindlecomicconverter.startup import startC2P
from kcc.comic2panel import main
if __name__ == "__main__": if __name__ == "__main__":
freeze_support() freeze_support()
print('comic2panel v' + __version__ + ' - Written by Ciro Mattia Gonano and Pawel Jastrzebski.') startC2P()
sys.exit(main(sys.argv[1:]))

View File

@@ -1,5 +1,5 @@
#define MyAppName "Kindle Comic Converter" #define MyAppName "Kindle Comic Converter"
#define MyAppVersion "5.1.1" #define MyAppVersion "5.4.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-2016 Ciro Mattia Gonano and Paweł Jastrzębski AppCopyright=Copyright (C) 2012-2017 Ciro Mattia Gonano and Paweł Jastrzębski
ArchitecturesAllowed=x64 ArchitecturesAllowed=x64
DefaultDirName={pf}\{#MyAppName} DefaultDirName={pf}\{#MyAppName}
DefaultGroupName={#MyAppName} DefaultGroupName={#MyAppName}

26
kcc.py
View File

@@ -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-2016 Pawel Jastrzebski <pawelj@iosphe.re> # Copyright (c) 2013-2017 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
@@ -39,7 +39,7 @@ elif sys.platform.startswith('win'):
class _Popen(forking.Popen): class _Popen(forking.Popen):
def __init__(self, *args, **kw): def __init__(self, *args, **kw):
if hasattr(sys, 'frozen'): if hasattr(sys, 'frozen'):
# noinspection PyProtectedMember # noinspection PyUnresolvedReferences,PyProtectedMember
os.putenv('_MEIPASS2', sys._MEIPASS) os.putenv('_MEIPASS2', sys._MEIPASS)
try: try:
super(_Popen, self).__init__(*args, **kw) super(_Popen, self).__init__(*args, **kw)
@@ -59,28 +59,14 @@ elif sys.platform.startswith('win'):
# Load additional Sentry configuration # Load additional Sentry configuration
if getattr(sys, 'frozen', False): if getattr(sys, 'frozen', False):
try: try:
import kcc.sentry import kindlecomicconverter.sentry
except: except:
pass pass
from kcc.shared import dependencyCheck
dependencyCheck(3)
from multiprocessing import freeze_support from multiprocessing import freeze_support
from kcc import KCC_gui from kindlecomicconverter.startup import start
if __name__ == "__main__": if __name__ == "__main__":
freeze_support() freeze_support()
os.environ['QT_AUTO_SCREEN_SCALE_FACTOR'] = "1" start()
KCCAplication = KCC_gui.QApplicationMessaging(sys.argv)
if KCCAplication.isRunning():
if len(sys.argv) > 1:
KCCAplication.sendMessage(sys.argv[1])
else:
KCCAplication.sendMessage('ARISE')
else:
KCCWindow = KCC_gui.QMainWindowKCC()
KCCUI = KCC_gui.KCCGUI(KCCAplication, KCCWindow)
if len(sys.argv) > 1:
KCCUI.handleMessage(sys.argv[1])
sys.exit(KCCAplication.exec_())

View File

@@ -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-2016 Pawel Jastrzebski <pawelj@iosphe.re> # Copyright (c) 2013-2017 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
@@ -21,17 +21,15 @@ import os
import sys import sys
from urllib.parse import unquote from urllib.parse import unquote
from urllib.request import urlopen, urlretrieve, Request from urllib.request import urlopen, urlretrieve, Request
from time import sleep, time from time import sleep
from datetime import datetime
from shutil import move from shutil import move
from subprocess import STDOUT, PIPE from subprocess import STDOUT, PIPE
from PyQt5 import QtGui, QtCore, QtWidgets, QtNetwork from PyQt5 import QtGui, QtCore, QtWidgets, QtNetwork
from xml.dom.minidom import parse, Document from xml.dom.minidom import parse
from xml.sax.saxutils import escape
from psutil import Popen, Process from psutil import Popen, Process
from copy import copy from copy import copy
from distutils.version import StrictVersion from distutils.version import StrictVersion
from xml.sax.saxutils import escape
from platform import platform
from raven import Client from raven import Client
from .shared import md5Checksum, HTMLStripper, sanitizeTrace from .shared import md5Checksum, HTMLStripper, sanitizeTrace
from . import __version__ from . import __version__
@@ -154,7 +152,7 @@ class VersionThread(QtCore.QThread):
self.getNewVersion() self.getNewVersion()
else: else:
MW.addMessage.emit('<a href="https://kcc.iosphe.re/">' MW.addMessage.emit('<a href="https://kcc.iosphe.re/">'
'<b>New version is available!</b></a> ' '<b>The new version is available!</b></a> '
'(<a href="https://github.com/ciromattia/kcc/releases/">' '(<a href="https://github.com/ciromattia/kcc/releases/">'
'Changelog</a>)', 'warning', False) 'Changelog</a>)', 'warning', False)
@@ -178,7 +176,7 @@ class VersionThread(QtCore.QThread):
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, shell=True)
MW.forceShutdown.emit() MW.forceShutdown.emit()
except Exception: except Exception:
MW.addMessage.emit('Failed to download 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)
@@ -256,8 +254,10 @@ class WorkerThread(QtCore.QThread):
options.splitter = 2 options.splitter = 2
elif GUI.rotateBox.checkState() == 2: elif GUI.rotateBox.checkState() == 2:
options.splitter = 1 options.splitter = 1
if GUI.qualityBox.isChecked(): if GUI.qualityBox.checkState() == 1:
options.hqmode = True options.autoscale = True
elif GUI.qualityBox.checkState() == 2:
options.hq = True
if GUI.webtoonBox.isChecked(): if GUI.webtoonBox.isChecked():
options.webtoon = True options.webtoon = True
if GUI.upscaleBox.checkState() == 1: if GUI.upscaleBox.checkState() == 1:
@@ -270,8 +270,8 @@ class WorkerThread(QtCore.QThread):
options.white_borders = True options.white_borders = True
elif GUI.borderBox.checkState() == 2: elif GUI.borderBox.checkState() == 2:
options.black_borders = True options.black_borders = True
if GUI.noDitheringBox.isChecked(): if GUI.outputSplit.isChecked():
options.forcepng = True options.batchsplit = 2
if GUI.colorBox.isChecked(): if GUI.colorBox.isChecked():
options.forcecolor = True options.forcecolor = True
if GUI.currentMode > 2: if GUI.currentMode > 2:
@@ -319,18 +319,24 @@ class WorkerThread(QtCore.QThread):
GUI.progress.content = '' GUI.progress.content = ''
self.errors = True self.errors = True
_, _, traceback = sys.exc_info() _, _, traceback = sys.exc_info()
if len(err.args) == 1:
MW.showDialog.emit("Error during conversion %s:\n\n%s\n\nTraceback:\n%s"
% (jobargv[-1], str(err), sanitizeTrace(traceback)), 'error')
else:
MW.showDialog.emit("Error during conversion %s:\n\n%s\n\nTraceback:\n%s"
% (jobargv[-1], str(err.args[0]), err.args[1]), 'error')
GUI.sentry.extra_context({'realTraceback': err.args[1]})
if ' is corrupted.' not in str(err): if ' is corrupted.' not in str(err):
GUI.sentry.captureException() GUI.sentry.captureException()
MW.showDialog.emit("Error during conversion %s:\n\n%s\n\nTraceback:\n%s"
% (jobargv[-1], str(err), sanitizeTrace(traceback)), 'error')
MW.addMessage.emit('Error during conversion! Please consult ' MW.addMessage.emit('Error during conversion! Please consult '
'<a href="https://github.com/ciromattia/kcc/wiki/Error-messages">wiki</a> ' '<a href="https://github.com/ciromattia/kcc/wiki/Error-messages">wiki</a> '
'for more details.', 'error', False) 'for more details.', 'error', False)
MW.addTrayMessage.emit('Error during conversion!', 'Critical') MW.addTrayMessage.emit('Error during conversion!', 'Critical')
if not self.conversionAlive: if not self.conversionAlive:
for item in outputPath: if 'outputPath' in locals():
if os.path.exists(item): for item in outputPath:
os.remove(item) if os.path.exists(item):
os.remove(item)
self.clean() self.clean()
return return
if not self.errors: if not self.errors:
@@ -392,7 +398,7 @@ class WorkerThread(QtCore.QThread):
for item in outputPath: for item in outputPath:
comic2ebook.options.covers[outputPath.index(item)][0].saveToKindle( comic2ebook.options.covers[outputPath.index(item)][0].saveToKindle(
k, comic2ebook.options.covers[outputPath.index(item)][1]) k, comic2ebook.options.covers[outputPath.index(item)][1])
MW.addMessage.emit('Kindle detected. Uploading covers...', 'info', False) MW.addMessage.emit('Kindle detected. Uploading covers... <b>Done!</b>', 'info', False)
else: else:
GUI.progress.content = '' GUI.progress.content = ''
for item in outputPath: for item in outputPath:
@@ -527,8 +533,8 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
def clearJobs(self): def clearJobs(self):
GUI.jobList.clear() GUI.jobList.clear()
# noinspection PyCallByClass,PyTypeChecker,PyArgumentList
def openWiki(self): def openWiki(self):
# noinspection PyCallByClass
QtGui.QDesktopServices.openUrl(QtCore.QUrl('https://github.com/ciromattia/kcc/wiki')) QtGui.QDesktopServices.openUrl(QtCore.QUrl('https://github.com/ciromattia/kcc/wiki'))
def modeChange(self, mode): def modeChange(self, mode):
@@ -602,11 +608,25 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
GUI.upscaleBox.setEnabled(False) GUI.upscaleBox.setEnabled(False)
GUI.upscaleBox.setChecked(True) GUI.upscaleBox.setChecked(True)
else: else:
GUI.qualityBox.setEnabled(True) profile = GUI.profiles[str(GUI.deviceBox.currentText())]
if profile['PVOptions']:
GUI.qualityBox.setEnabled(True)
GUI.mangaBox.setEnabled(True) GUI.mangaBox.setEnabled(True)
GUI.rotateBox.setEnabled(True) GUI.rotateBox.setEnabled(True)
GUI.upscaleBox.setEnabled(True) GUI.upscaleBox.setEnabled(True)
def togglequalityBox(self, value):
profile = GUI.profiles[str(GUI.deviceBox.currentText())]
if value == 2:
if profile['Label'] in ['KV']:
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')
GUI.upscaleBox.setEnabled(False)
GUI.upscaleBox.setChecked(True)
else:
GUI.upscaleBox.setEnabled(True)
GUI.upscaleBox.setChecked(profile['DefaultUpscale'])
def changeGamma(self, value): def changeGamma(self, value):
valueRaw = int(5 * round(float(value) / 5)) valueRaw = int(5 * round(float(value) / 5))
value = '%.2f' % (float(valueRaw) / 100) value = '%.2f' % (float(valueRaw) / 100)
@@ -628,9 +648,10 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
self.changeFormat() self.changeFormat()
GUI.gammaSlider.setValue(0) GUI.gammaSlider.setValue(0)
self.changeGamma(0) self.changeGamma(0)
GUI.qualityBox.setEnabled(profile['Quality']) if not GUI.webtoonBox.isChecked():
GUI.qualityBox.setEnabled(profile['PVOptions'])
GUI.upscaleBox.setChecked(profile['DefaultUpscale']) GUI.upscaleBox.setChecked(profile['DefaultUpscale'])
if not profile['Quality']: if not profile['PVOptions']:
GUI.qualityBox.setChecked(False) GUI.qualityBox.setChecked(False)
if str(GUI.deviceBox.currentText()) == 'Other': if str(GUI.deviceBox.currentText()) == 'Other':
self.addMessage('<a href="https://github.com/ciromattia/kcc/wiki/NonKindle-devices">' self.addMessage('<a href="https://github.com/ciromattia/kcc/wiki/NonKindle-devices">'
@@ -642,7 +663,13 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
GUI.formatBox.setCurrentIndex(outputFormat) GUI.formatBox.setCurrentIndex(outputFormat)
else: else:
GUI.formatBox.setCurrentIndex(profile['DefaultFormat']) GUI.formatBox.setCurrentIndex(profile['DefaultFormat'])
GUI.qualityBox.setEnabled(profile['Quality']) if not GUI.webtoonBox.isChecked():
GUI.qualityBox.setEnabled(profile['PVOptions'])
if str(GUI.formatBox.currentText()) == 'MOBI/AZW3':
GUI.outputSplit.setEnabled(True)
else:
GUI.outputSplit.setEnabled(False)
GUI.outputSplit.setChecked(False)
def stripTags(self, html): def stripTags(self, html):
s = HTMLStripper() s = HTMLStripper()
@@ -661,7 +688,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
# We still fill original text field with transparent content to trigger creation of horizontal scrollbar # We still fill original text field with transparent content to trigger creation of horizontal scrollbar
item.setForeground(QtGui.QColor('transparent')) item.setForeground(QtGui.QColor('transparent'))
label = QtWidgets.QLabel(message) label = QtWidgets.QLabel(message)
label.setStyleSheet('background-image:url('');background-color:rgba(0,0,0,0);') label.setStyleSheet('background-image:url('');background-color:rgba(0,0,0,0);color:rgb(0,0,0);')
label.setOpenExternalLinks(True) label.setOpenExternalLinks(True)
GUI.jobList.addItem(item) GUI.jobList.addItem(item)
GUI.jobList.setItemWidget(item, label) GUI.jobList.setItemWidget(item, label)
@@ -693,11 +720,10 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
def convertStart(self): def convertStart(self):
if self.conversionAlive: if self.conversionAlive:
GUI.convertButton.setEnabled(False) GUI.convertButton.setEnabled(False)
self.addMessage('Process will be interrupted. Please wait.', 'warning') self.addMessage('The process will be interrupted. Please wait.', 'warning')
self.conversionAlive = False self.conversionAlive = False
self.worker.sync() self.worker.sync()
else: else:
# noinspection PyArgumentList
if QtWidgets.QApplication.keyboardModifiers() == QtCore.Qt.ShiftModifier: if QtWidgets.QApplication.keyboardModifiers() == QtCore.Qt.ShiftModifier:
dname = QtWidgets.QFileDialog.getExistingDirectory(MW, 'Select output directory', self.lastPath) dname = QtWidgets.QFileDialog.getExistingDirectory(MW, 'Select output directory', self.lastPath)
if dname != '': if dname != '':
@@ -740,7 +766,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
def saveSettings(self, event): def saveSettings(self, event):
if self.conversionAlive: if self.conversionAlive:
GUI.convertButton.setEnabled(False) GUI.convertButton.setEnabled(False)
self.addMessage('Process will be interrupted. Please wait.', 'warning') self.addMessage('The process will be interrupted. Please wait.', 'warning')
self.conversionAlive = False self.conversionAlive = False
self.worker.sync() self.worker.sync()
event.ignore() event.ignore()
@@ -759,7 +785,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
'upscaleBox': GUI.upscaleBox.checkState(), 'upscaleBox': GUI.upscaleBox.checkState(),
'borderBox': GUI.borderBox.checkState(), 'borderBox': GUI.borderBox.checkState(),
'webtoonBox': GUI.webtoonBox.checkState(), 'webtoonBox': GUI.webtoonBox.checkState(),
'noDitheringBox': GUI.noDitheringBox.checkState(), 'outputSplit': GUI.outputSplit.checkState(),
'colorBox': GUI.colorBox.checkState(), 'colorBox': GUI.colorBox.checkState(),
'widthBox': GUI.widthBox.value(), 'widthBox': GUI.widthBox.value(),
'heightBox': GUI.heightBox.value(), 'heightBox': GUI.heightBox.value(),
@@ -845,7 +871,6 @@ 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')
# noinspection PyArgumentList
def __init__(self, KCCAplication, KCCWindow): def __init__(self, KCCAplication, KCCWindow):
global APP, MW, GUI global APP, MW, GUI
APP = KCCAplication APP = KCCAplication
@@ -893,38 +918,42 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
MW.resize(500, 500) MW.resize(500, 500)
self.profiles = { self.profiles = {
"Kindle Oasis": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0, "Kindle Oasis": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': True, 'Label': 'KV'}, 'DefaultUpscale': True, 'Label': 'KV'},
"Kindle Voyage": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0, "Kindle Voyage": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': True, 'Label': 'KV'}, 'DefaultUpscale': True, 'Label': 'KV'},
"Kindle PW 3": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0, "Kindle PW 3": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': True, 'Label': 'KV'}, 'DefaultUpscale': True, 'Label': 'KV'},
"Kindle PW 1/2": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0, "Kindle PW 1/2": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': False, 'Label': 'KPW'}, 'DefaultUpscale': False, 'Label': 'KPW'},
"Kindle": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0, "Kindle": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': False, 'Label': 'K45'}, 'DefaultUpscale': False, 'Label': 'K578'},
"Kindle DX/DXG": {'Quality': False, 'ForceExpert': False, 'DefaultFormat': 2, "Kindle DX/DXG": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 2,
'DefaultUpscale': False, 'Label': 'KDX'}, 'DefaultUpscale': False, 'Label': 'KDX'},
"Kobo Mini/Touch": {'Quality': False, 'ForceExpert': False, 'DefaultFormat': 1, "Kobo Mini/Touch": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1,
'DefaultUpscale': False, 'Label': 'KoMT'}, 'DefaultUpscale': False, 'Label': 'KoMT'},
"Kobo Glo": {'Quality': False, 'ForceExpert': False, 'DefaultFormat': 1, "Kobo Glo": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1,
'DefaultUpscale': False, 'Label': 'KoG'}, 'DefaultUpscale': False, 'Label': 'KoG'},
"Kobo Glo HD": {'Quality': False, 'ForceExpert': False, 'DefaultFormat': 1, "Kobo Glo HD": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1,
'DefaultUpscale': False, 'Label': 'KoGHD'}, 'DefaultUpscale': False, 'Label': 'KoGHD'},
"Kobo Aura": {'Quality': False, 'ForceExpert': False, 'DefaultFormat': 1, "Kobo Aura": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1,
'DefaultUpscale': False, 'Label': 'KoA'}, 'DefaultUpscale': False, 'Label': 'KoA'},
"Kobo Aura HD": {'Quality': False, 'ForceExpert': False, 'DefaultFormat': 1, "Kobo Aura HD": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1,
'DefaultUpscale': True, 'Label': 'KoAHD'}, 'DefaultUpscale': True, 'Label': 'KoAHD'},
"Kobo Aura H2O": {'Quality': False, 'ForceExpert': False, 'DefaultFormat': 1, "Kobo Aura H2O": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1,
'DefaultUpscale': True, 'Label': 'KoAH2O'}, 'DefaultUpscale': True, 'Label': 'KoAH2O'},
"Other": {'Quality': False, 'ForceExpert': True, 'DefaultFormat': 1, "Kobo Aura ONE": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1,
'DefaultUpscale': True, 'Label': 'KoAO'},
"Other": {'PVOptions': False, 'ForceExpert': True, 'DefaultFormat': 1,
'DefaultUpscale': False, 'Label': 'OTHER'}, 'DefaultUpscale': False, 'Label': 'OTHER'},
"Kindle 1": {'Quality': False, 'ForceExpert': False, 'DefaultFormat': 0, "Kindle 1": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': False, 'Label': 'K1'}, 'DefaultUpscale': False, 'Label': 'K1'},
"Kindle 2": {'Quality': False, 'ForceExpert': False, 'DefaultFormat': 0, "Kindle 2": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': False, 'Label': 'K2'}, 'DefaultUpscale': False, 'Label': 'K2'},
"Kindle 3": {'Quality': False, 'ForceExpert': False, 'DefaultFormat': 0, "Kindle Keyboard": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': False, 'Label': 'K3'}, 'DefaultUpscale': False, 'Label': 'K34'},
"Kindle Touch": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': False, 'Label': 'K34'},
} }
profilesGUI = [ profilesGUI = [
"Kindle Oasis", "Kindle Oasis",
@@ -933,19 +962,22 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
"Kindle PW 1/2", "Kindle PW 1/2",
"Kindle", "Kindle",
"Separator", "Separator",
"Kobo Aura ONE",
"Kobo Aura H2O", "Kobo Aura H2O",
"Kobo Aura HD", "Kobo Aura HD",
"Kobo Aura", "Kobo Aura",
"Kobo Glo HD",
"Kobo Glo",
"Kobo Mini/Touch",
"Separator", "Separator",
"Other", "Other",
"Separator", "Separator",
"Kindle Touch",
"Kindle Keyboard",
"Kindle DX/DXG", "Kindle DX/DXG",
"Kindle 3",
"Kindle 2", "Kindle 2",
"Kindle 1", "Kindle 1",
"Separator",
"Kobo Glo HD",
"Kobo Glo",
"Kobo Mini/Touch",
] ]
statusBarLabel = QtWidgets.QLabel('<b><a href="https://kcc.iosphe.re/">HOMEPAGE</a> - <a href="https://github.' statusBarLabel = QtWidgets.QLabel('<b><a href="https://kcc.iosphe.re/">HOMEPAGE</a> - <a href="https://github.'
@@ -957,9 +989,9 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
GUI.statusBar.addPermanentWidget(statusBarLabel, 1) GUI.statusBar.addPermanentWidget(statusBarLabel, 1)
self.addMessage('<b>Welcome!</b>', 'info') self.addMessage('<b>Welcome!</b>', 'info')
self.addMessage('<b>Remember:</b> All options have additional informations in tooltips.', 'info') self.addMessage('<b>Remember:</b> All options have additional information in tooltips.', 'info')
if self.startNumber < 5: if self.startNumber < 5:
self.addMessage('Since you are 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) rarExitCode = Popen('unrar', stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
@@ -990,6 +1022,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
GUI.gammaSlider.valueChanged.connect(self.changeGamma) GUI.gammaSlider.valueChanged.connect(self.changeGamma)
GUI.gammaBox.stateChanged.connect(self.togglegammaBox) GUI.gammaBox.stateChanged.connect(self.togglegammaBox)
GUI.webtoonBox.stateChanged.connect(self.togglewebtoonBox) GUI.webtoonBox.stateChanged.connect(self.togglewebtoonBox)
GUI.qualityBox.stateChanged.connect(self.togglequalityBox)
GUI.deviceBox.activated.connect(self.changeDevice) GUI.deviceBox.activated.connect(self.changeDevice)
GUI.formatBox.activated.connect(self.changeFormat) GUI.formatBox.activated.connect(self.changeFormat)
MW.progressBarTick.connect(self.updateProgressbar) MW.progressBarTick.connect(self.updateProgressbar)

View File

@@ -2,7 +2,7 @@
# Form implementation generated from reading ui file 'gui\KCC.ui' # Form implementation generated from reading ui file 'gui\KCC.ui'
# #
# Created by: PyQt5 UI code generator 5.6 # Created by: PyQt5 UI code generator 5.8.1
# #
# WARNING! All changes made in this file will be lost! # WARNING! All changes made in this file will be lost!
@@ -31,7 +31,7 @@ class Ui_mainWindow(object):
self.progressBar.setObjectName("progressBar") self.progressBar.setObjectName("progressBar")
self.gridLayout.addWidget(self.progressBar, 1, 0, 1, 2) self.gridLayout.addWidget(self.progressBar, 1, 0, 1, 2)
self.jobList = QtWidgets.QListWidget(self.centralWidget) self.jobList = QtWidgets.QListWidget(self.centralWidget)
self.jobList.setStyleSheet("QListWidget#jobList {background:#ffffff;background-image:url(:/Other/icons/list_background.png);background-position:center center;background-repeat:no-repeat;}") self.jobList.setStyleSheet("QListWidget#jobList {background:#ffffff;background-image:url(:/Other/icons/list_background.png);background-position:center center;background-repeat:no-repeat;color:rgb(0,0,0);}")
self.jobList.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection) self.jobList.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection)
self.jobList.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel) self.jobList.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel)
self.jobList.setHorizontalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel) self.jobList.setHorizontalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel)
@@ -81,6 +81,7 @@ class Ui_mainWindow(object):
self.rotateBox.setObjectName("rotateBox") self.rotateBox.setObjectName("rotateBox")
self.gridLayout_2.addWidget(self.rotateBox, 0, 1, 1, 1) self.gridLayout_2.addWidget(self.rotateBox, 0, 1, 1, 1)
self.qualityBox = QtWidgets.QCheckBox(self.optionWidget) self.qualityBox = QtWidgets.QCheckBox(self.optionWidget)
self.qualityBox.setTristate(True)
self.qualityBox.setObjectName("qualityBox") self.qualityBox.setObjectName("qualityBox")
self.gridLayout_2.addWidget(self.qualityBox, 0, 2, 1, 1) self.gridLayout_2.addWidget(self.qualityBox, 0, 2, 1, 1)
self.webtoonBox = QtWidgets.QCheckBox(self.optionWidget) self.webtoonBox = QtWidgets.QCheckBox(self.optionWidget)
@@ -97,9 +98,9 @@ class Ui_mainWindow(object):
self.borderBox.setTristate(True) self.borderBox.setTristate(True)
self.borderBox.setObjectName("borderBox") self.borderBox.setObjectName("borderBox")
self.gridLayout_2.addWidget(self.borderBox, 2, 0, 1, 1) self.gridLayout_2.addWidget(self.borderBox, 2, 0, 1, 1)
self.noDitheringBox = QtWidgets.QCheckBox(self.optionWidget) self.outputSplit = QtWidgets.QCheckBox(self.optionWidget)
self.noDitheringBox.setObjectName("noDitheringBox") self.outputSplit.setObjectName("outputSplit")
self.gridLayout_2.addWidget(self.noDitheringBox, 2, 1, 1, 1) self.gridLayout_2.addWidget(self.outputSplit, 2, 1, 1, 1)
self.colorBox = QtWidgets.QCheckBox(self.optionWidget) self.colorBox = QtWidgets.QCheckBox(self.optionWidget)
self.colorBox.setObjectName("colorBox") self.colorBox.setObjectName("colorBox")
self.gridLayout_2.addWidget(self.colorBox, 2, 2, 1, 1) self.gridLayout_2.addWidget(self.colorBox, 2, 2, 1, 1)
@@ -219,8 +220,8 @@ class Ui_mainWindow(object):
mainWindow.setTabOrder(self.webtoonBox, self.upscaleBox) mainWindow.setTabOrder(self.webtoonBox, self.upscaleBox)
mainWindow.setTabOrder(self.upscaleBox, self.gammaBox) mainWindow.setTabOrder(self.upscaleBox, self.gammaBox)
mainWindow.setTabOrder(self.gammaBox, self.borderBox) mainWindow.setTabOrder(self.gammaBox, self.borderBox)
mainWindow.setTabOrder(self.borderBox, self.noDitheringBox) mainWindow.setTabOrder(self.borderBox, self.outputSplit)
mainWindow.setTabOrder(self.noDitheringBox, self.colorBox) mainWindow.setTabOrder(self.outputSplit, self.colorBox)
mainWindow.setTabOrder(self.colorBox, self.editorButton) mainWindow.setTabOrder(self.colorBox, self.editorButton)
mainWindow.setTabOrder(self.editorButton, self.wikiButton) mainWindow.setTabOrder(self.editorButton, self.wikiButton)
mainWindow.setTabOrder(self.wikiButton, self.jobList) mainWindow.setTabOrder(self.wikiButton, self.jobList)
@@ -231,28 +232,28 @@ class Ui_mainWindow(object):
def retranslateUi(self, mainWindow): def retranslateUi(self, mainWindow):
_translate = QtCore.QCoreApplication.translate _translate = QtCore.QCoreApplication.translate
mainWindow.setWindowTitle(_translate("mainWindow", "Kindle Comic Converter")) mainWindow.setWindowTitle(_translate("mainWindow", "Kindle Comic Converter"))
self.hLabel.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Resolution of target device.</p></body></html>")) self.hLabel.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Resolution of the target device.</p></body></html>"))
self.hLabel.setText(_translate("mainWindow", "Custom height:")) self.hLabel.setText(_translate("mainWindow", "Custom height:"))
self.widthBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Resolution of target device.</p></body></html>")) self.widthBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Resolution of the target device.</p></body></html>"))
self.wLabel.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Resolution of target device.</p></body></html>")) self.wLabel.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Resolution of the target device.</p></body></html>"))
self.wLabel.setText(_translate("mainWindow", "Custom width:")) self.wLabel.setText(_translate("mainWindow", "Custom width:"))
self.heightBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Resolution of target device.</p></body></html>")) self.heightBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Resolution of the target device.</p></body></html>"))
self.mangaBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Enable right-to-left reading.</p></body></html>")) self.mangaBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Enable right-to-left reading.</p></body></html>"))
self.mangaBox.setText(_translate("mainWindow", "Manga mode")) self.mangaBox.setText(_translate("mainWindow", "Manga mode"))
self.rotateBox.setToolTip(_translate("mainWindow", "<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Split<br/></span>Double page spreads will be cut into two separate pages.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - Rotate and split<br/></span>Double page spreads will be displayed twice. First rotated and then split. </p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Rotate<br/></span>Double page spreads will be rotated.</p></body></html>")) self.rotateBox.setToolTip(_translate("mainWindow", "<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Split<br/></span>Double page spreads will be cut into two separate pages.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - Rotate and split<br/></span>Double page spreads will be displayed twice. First rotated and then split. </p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Rotate<br/></span>Double page spreads will be rotated.</p></body></html>"))
self.rotateBox.setText(_translate("mainWindow", "Spread splitter")) self.rotateBox.setText(_translate("mainWindow", "Spread splitter"))
self.qualityBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>High quality Panel View.<br/>Require source files with bigger resolution than target device.<br/><span style=\" font-weight:600;\">Highly impact size of output file!</span></p></body></html>")) self.qualityBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - 4 panels<br/></span>Zoom each corner separately.</p><p style=\'white-space:pre\'><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - 2 panels<br/></span>Zoom only the top and bottom of the page.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - 4 high-quality panels<br/></span>Zoom each corner separately. Try to increase the quality of magnification. Check wiki for more details.</p></body></html>"))
self.qualityBox.setText(_translate("mainWindow", "HQ zoom")) self.qualityBox.setText(_translate("mainWindow", "Panel View 4/2/HQ"))
self.webtoonBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Enable special parsing mode for Korean Webtoons.</p></body></html>")) self.webtoonBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Enable special parsing mode for Korean Webtoons.</p></body></html>"))
self.webtoonBox.setText(_translate("mainWindow", "Webtoon mode")) self.webtoonBox.setText(_translate("mainWindow", "Webtoon mode"))
self.upscaleBox.setToolTip(_translate("mainWindow", "<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Nothing<br/></span>Images smaller than device resolution will not be resized.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - Stretching<br/></span>Images smaller than device resolution will be resized. Aspect ratio will be not preserved.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Upscaling<br/></span>Images smaller than device resolution will be resized. Aspect ratio will be preserved.</p></body></html>")) self.upscaleBox.setToolTip(_translate("mainWindow", "<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Nothing<br/></span>Images smaller than device resolution will not be resized.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - Stretching<br/></span>Images smaller than device resolution will be resized. Aspect ratio will be not preserved.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Upscaling<br/></span>Images smaller than device resolution will be resized. Aspect ratio will be preserved.</p></body></html>"))
self.upscaleBox.setText(_translate("mainWindow", "Stretch/Upscale")) self.upscaleBox.setText(_translate("mainWindow", "Stretch/Upscale"))
self.gammaBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Disable automatic gamma correction.</p></body></html>")) self.gammaBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Disable automatic gamma correction.</p></body></html>"))
self.gammaBox.setText(_translate("mainWindow", "Custom gamma")) self.gammaBox.setText(_translate("mainWindow", "Custom gamma"))
self.borderBox.setToolTip(_translate("mainWindow", "<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Autodetection<br/></span>Color of margins fill will be detected automatically.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - White<br/></span>Margins will be filled with white color.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Black<br/></span>Margins will be filled with black color.</p></body></html>")) self.borderBox.setToolTip(_translate("mainWindow", "<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Autodetection<br/></span>The color of margins fill will be detected automatically.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - White<br/></span>Margins will be filled with white color.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Black<br/></span>Margins will be filled with black color.</p></body></html>"))
self.borderBox.setText(_translate("mainWindow", "W/B margins")) self.borderBox.setText(_translate("mainWindow", "W/B margins"))
self.noDitheringBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Create PNG files instead JPEG.<br/>Quality increase is not noticeable on most of devices.<br/>Output files <span style=\" font-weight:600;\">might</span> be smaller.<br/><span style=\" font-weight:600;\">MOBI conversion will be much slower.</span></p></body></html>")) self.outputSplit.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Automatic mode<br/></span>The output will be split automatically.</p><p style=\'white-space:pre\'><span style=\" font-weight:600; text-decoration: underline;\">Checked - Volume mode<br/></span>Every subdirectory will be considered as a separate volume.</p></body></html>"))
self.noDitheringBox.setText(_translate("mainWindow", "PNG output")) self.outputSplit.setText(_translate("mainWindow", "Output split"))
self.colorBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Disable conversion to grayscale.</p></body></html>")) self.colorBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Disable conversion to grayscale.</p></body></html>"))
self.colorBox.setText(_translate("mainWindow", "Color mode")) self.colorBox.setText(_translate("mainWindow", "Color mode"))
self.gammaLabel.setText(_translate("mainWindow", "Gamma: Auto")) self.gammaLabel.setText(_translate("mainWindow", "Gamma: Auto"))

View File

@@ -1,4 +1,4 @@
__version__ = '5.1.1' __version__ = '5.4.1'
__license__ = 'ISC' __license__ = 'ISC'
__copyright__ = '2012-2016, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>' __copyright__ = '2012-2017, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'

View File

@@ -1,5 +1,5 @@
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com> # Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
# Copyright (c) 2013-2016 Pawel Jastrzebski <pawelj@iosphe.re> # Copyright (c) 2013-2017 Pawel Jastrzebski <pawelj@iosphe.re>
# #
# Permission to use, copy, modify, and/or distribute this software for # Permission to use, copy, modify, and/or distribute this software for
# any purpose with or without fee is hereby granted, provided that the # any purpose with or without fee is hereby granted, provided that the
@@ -16,18 +16,13 @@
# PERFORMANCE OF THIS SOFTWARE. # PERFORMANCE OF THIS SOFTWARE.
# #
import sys
import os import os
from zipfile import is_zipfile, ZipFile from zipfile import is_zipfile, ZipFile
from subprocess import STDOUT, PIPE from subprocess import STDOUT, PIPE
from psutil import Popen from psutil import Popen
from shutil import move, copy from shutil import move
try:
from scandir import walk
except ImportError:
walk = os.walk
from . import rarfile from . import rarfile
from .shared import check7ZFile as is_7zfile, saferReplace from .shared import check7ZFile as is_7zfile
class CBxArchive: class CBxArchive:
@@ -50,12 +45,12 @@ class CBxArchive:
filelist = [] filelist = []
for f in cbzFile.namelist(): for f in cbzFile.namelist():
if f.startswith('__MACOSX') or f.endswith('.DS_Store') or f.endswith('humbs.db'): if f.startswith('__MACOSX') or f.endswith('.DS_Store') or f.endswith('humbs.db'):
pass # skip MacOS special files pass
elif f.endswith('/'): elif f.endswith('/'):
try: try:
os.makedirs(os.path.join(targetdir, f)) os.makedirs(os.path.join(targetdir, f))
except Exception: except Exception:
pass # the dir exists so we are going to extract the images only. pass
else: else:
filelist.append(f) filelist.append(f)
cbzFile.extractall(targetdir, filelist) cbzFile.extractall(targetdir, filelist)
@@ -63,24 +58,18 @@ class CBxArchive:
def extractCBR(self, targetdir): def extractCBR(self, targetdir):
cbrFile = rarfile.RarFile(self.origFileName) cbrFile = rarfile.RarFile(self.origFileName)
cbrFile.extractall(targetdir) cbrFile.extractall(targetdir)
for root, dirnames, filenames in walk(targetdir): for root, _, filenames in os.walk(targetdir):
for filename in filenames: for filename in filenames:
if filename.startswith('__MACOSX') or filename.endswith('.DS_Store') or filename.endswith('humbs.db'): if filename.startswith('__MACOSX') or filename.endswith('.DS_Store') or filename.endswith('humbs.db'):
os.remove(os.path.join(root, filename)) os.remove(os.path.join(root, filename))
def extractCB7(self, targetdir): def extractCB7(self, targetdir):
# Workaround for some wide UTF-8 + Popen abnormalities
if sys.platform.startswith('darwin'):
copy(self.origFileName, os.path.join(os.path.dirname(self.origFileName), 'TMP_KCC_TMP'))
self.origFileName = os.path.join(os.path.dirname(self.origFileName), 'TMP_KCC_TMP')
output = Popen('7za x "' + self.origFileName + '" -xr!__MACOSX -xr!.DS_Store -xr!thumbs.db -xr!Thumbs.db -o"' + 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) targetdir + '"', stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
extracted = False extracted = False
for line in output.stdout: for line in output.stdout:
if b"Everything is Ok" in line: if b"Everything is Ok" in line:
extracted = True extracted = True
if sys.platform.startswith('darwin'):
os.remove(self.origFileName)
if not extracted: if not extracted:
raise OSError raise OSError
@@ -96,10 +85,6 @@ class CBxArchive:
adir.remove('ComicInfo.xml') adir.remove('ComicInfo.xml')
if len(adir) == 1 and os.path.isdir(os.path.join(targetdir, adir[0])): 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])): for f in os.listdir(os.path.join(targetdir, adir[0])):
# If directory names contain UTF-8 chars shutil.move can't clean up the mess alone
if os.path.isdir(os.path.join(targetdir, f)):
saferReplace(os.path.join(targetdir, adir[0], f), os.path.join(targetdir, adir[0], f + '-A'))
f += '-A'
move(os.path.join(targetdir, adir[0], f), targetdir) move(os.path.join(targetdir, adir[0], f), targetdir)
os.rmdir(os.path.join(targetdir, adir[0])) os.rmdir(os.path.join(targetdir, adir[0]))
return targetdir return targetdir

View File

@@ -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-2016 Pawel Jastrzebski <pawelj@iosphe.re> # Copyright (c) 2013-2017 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
@@ -36,17 +36,13 @@ from uuid import uuid4
from slugify import slugify as slugifyExt 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 from psutil import Popen, virtual_memory, disk_usage
from html import escape from html import escape
try: try:
from PyQt5 import QtCore from PyQt5 import QtCore
except ImportError: except ImportError:
QtCore = None QtCore = None
try: from .shared import md5Checksum, getImageFileName, walkSort, walkLevel, sanitizeTrace
from scandir import walk
except ImportError:
walk = os.walk
from .shared import md5Checksum, getImageFileName, walkSort, walkLevel, saferReplace, saferRemove
from . import comic2panel from . import comic2panel
from . import image from . import image
from . import cbxarchive from . import cbxarchive
@@ -64,7 +60,10 @@ def main(argv=None):
if len(args) == 0: if len(args) == 0:
parser.print_help() parser.print_help()
return 0 return 0
sources = set([source for arg in args for source in glob(arg)]) if sys.platform.startswith('win'):
sources = set([source for arg in args for source in glob(arg)])
else:
sources = set(args)
if len(sources) == 0: if len(sources) == 0:
print('No matching files found.') print('No matching files found.')
return 1 return 1
@@ -82,18 +81,15 @@ def buildHTML(path, imgfile, imgfilepath):
imgfilepath = md5Checksum(imgfilepath) imgfilepath = md5Checksum(imgfilepath)
filename = getImageFileName(imgfile) filename = getImageFileName(imgfile)
deviceres = options.profileData[1] deviceres = options.profileData[1]
if "Rotated" in options.imgIndex[imgfilepath]: if "Rotated" in options.imgMetadata[imgfilepath]:
rotatedPage = True rotatedPage = True
else: else:
rotatedPage = False rotatedPage = False
if "BlackFill" in options.imgIndex[imgfilepath]: if "BlackBackground" in options.imgMetadata[imgfilepath]:
additionalStyle = 'background-color:#000000;' additionalStyle = 'background-color:#000000;'
else: else:
additionalStyle = 'background-color:#FFFFFF;' additionalStyle = 'background-color:#FFFFFF;'
htmlpath = ''
postfix = '' postfix = ''
size = ''
imgfilepv = ''
backref = 1 backref = 1
head = path head = path
while True: while True:
@@ -106,6 +102,11 @@ def buildHTML(path, imgfile, imgfilepath):
if not os.path.exists(htmlpath): if not os.path.exists(htmlpath):
os.makedirs(htmlpath) os.makedirs(htmlpath)
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
if options.hq:
imgsizeframe = deviceres
else:
imgsizeframe = imgsize
f = open(htmlfile, "w", encoding='UTF-8') f = open(htmlfile, "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",
@@ -114,26 +115,25 @@ def buildHTML(path, imgfile, imgfilepath):
"<title>", escape(filename[0]), "</title>\n", "<title>", escape(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(deviceres[0]) + ", height=" + str(deviceres[1]) + "\"/>\n" "content=\"width=" + str(imgsize[0]) + ", height=" + str(imgsize[1]) + "\"/>\n"
"</head>\n", "</head>\n",
"<body style=\"background-image: ", "<body style=\"" + additionalStyle + "\">\n",
"url('", "../" * backref, "Images/", postfix, imgfile, "'); " + additionalStyle + "\">\n"]) "<div style=\"text-align:center;top:" + getTopMargin(deviceres, imgsize) + "%;\">\n",
"<img width=\"" + str(imgsizeframe[0]) + "\" height=\"" + str(imgsizeframe[1]) + "\" ",
"src=\"", "../" * backref, "Images/", postfix, imgfile, "\"/>\n</div>\n"])
if options.iskindle and options.panelview: if options.iskindle and options.panelview:
if options.hqmode: if options.autoscale:
imgfilepv = list(os.path.splitext(imgfile)) size = (getPanelViewResolution(imgsize, deviceres))
imgfilepv[0] += "-hq" else:
imgfilepv = "".join(imgfilepv) if options.hq:
if os.path.isfile(os.path.join(head, "Images", postfix, imgfilepv)): size = imgsize
size = Image.open(os.path.join(head, "Images", postfix, imgfilepv)).size else:
if not options.hqmode or not size: size = (int(imgsize[0] * 1.5), int(imgsize[1] * 1.5))
imgfilepv = imgfile if size[0] - deviceres[0] < deviceres[0] * 0.01:
sizeTmp = Image.open(os.path.join(head, "Images", postfix, imgfilepv)).size
size = (int(sizeTmp[0] * 1.5), int(sizeTmp[1] * 1.5))
if size[0] <= deviceres[0]:
noHorizontalPV = True noHorizontalPV = True
else: else:
noHorizontalPV = False noHorizontalPV = False
if size[1] <= deviceres[1]: if size[1] - deviceres[1] < deviceres[1] * 0.01:
noVerticalPV = True noVerticalPV = True
else: else:
noVerticalPV = False noVerticalPV = False
@@ -190,7 +190,7 @@ def buildHTML(path, imgfile, imgfilepath):
for box in boxes: for box in boxes:
f.writelines(["<div class=\"PV-P\" id=\"" + box + "-P\" style=\"" + additionalStyle + "\">\n", f.writelines(["<div class=\"PV-P\" id=\"" + box + "-P\" style=\"" + additionalStyle + "\">\n",
"<img style=\"" + boxStyles[box] + "\" src=\"", "../" * backref, "Images/", postfix, "<img style=\"" + boxStyles[box] + "\" src=\"", "../" * backref, "Images/", postfix,
imgfilepv, "\" width=\"" + str(size[0]) + "\" height=\"" + str(size[1]) + "\"/>\n", imgfile, "\" width=\"" + str(size[0]) + "\" height=\"" + str(size[1]) + "\"/>\n",
"</div>\n"]) "</div>\n"])
f.writelines(["</body>\n", f.writelines(["</body>\n",
"</html>\n"]) "</html>\n"])
@@ -333,29 +333,6 @@ def buildOPF(dstdir, title, filelist, cover=None):
f.write("</manifest>\n<spine page-progression-direction=\"rtl\" toc=\"ncx\">\n") f.write("</manifest>\n<spine page-progression-direction=\"rtl\" toc=\"ncx\">\n")
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")
# if options.iskindle and options.profile != 'Custom':
# if options.righttoleft:
# nextflow = 'right'
# else:
# nextflow = 'left'
# for entry in reflist:
# if '-kcc-b' in entry:
# if options.righttoleft:
# f.write("<itemref idref=\"page_" + entry + "\" properties=\"page-spread-right\"/>\n")
# else:
# f.write("<itemref idref=\"page_" + entry + "\" properties=\"page-spread-left\"/>\n")
# elif '-kcc-c' in entry:
# if options.righttoleft:
# f.write("<itemref idref=\"page_" + entry + "\" properties=\"page-spread-left\"/>\n")
# else:
# f.write("<itemref idref=\"page_" + entry + "\" properties=\"page-spread-right\"/>\n")
# else:
# f.write("<itemref idref=\"page_" + entry + "\" properties=\"facing-page-" + nextflow + "\"/>\n")
# if nextflow == 'right':
# nextflow = 'left'
# else:
# nextflow = 'right'
# else:
for entry in reflist: for entry in reflist:
f.write("<itemref idref=\"page_" + entry + "\"/>\n") f.write("<itemref idref=\"page_" + entry + "\"/>\n")
f.write("</spine>\n</package>\n") f.write("</spine>\n</package>\n")
@@ -384,9 +361,6 @@ def buildEPUB(path, chapterNames, tomeNumber):
"display: block;\n", "display: block;\n",
"margin: 0;\n", "margin: 0;\n",
"padding: 0;\n", "padding: 0;\n",
"background-position: center center;\n",
"background-repeat: no-repeat;\n",
"background-size: auto auto;\n",
"}\n", "}\n",
"#PV {\n", "#PV {\n",
"position: absolute;\n", "position: absolute;\n",
@@ -453,21 +427,19 @@ def buildEPUB(path, chapterNames, tomeNumber):
"display: none;\n", "display: none;\n",
"}\n"]) "}\n"])
f.close() f.close()
for (dirpath, dirnames, filenames) in walk(os.path.join(path, 'OEBPS', 'Images')): for dirpath, dirnames, filenames in os.walk(os.path.join(path, 'OEBPS', 'Images')):
chapter = False chapter = False
dirnames, filenames = walkSort(dirnames, filenames) dirnames, filenames = walkSort(dirnames, filenames)
for afile in filenames: for afile in filenames:
filename = getImageFileName(afile) filelist.append(buildHTML(dirpath, afile, os.path.join(dirpath, afile)))
if not filename[0].endswith('-hq'): if not chapter:
filelist.append(buildHTML(dirpath, afile, os.path.join(dirpath, afile))) chapterlist.append((dirpath.replace('Images', 'Text'), filelist[-1][1]))
if not chapter: chapter = True
chapterlist.append((dirpath.replace('Images', 'Text'), filelist[-1][1])) if cover is None:
chapter = True cover = os.path.join(os.path.join(path, 'OEBPS', 'Images'),
if cover is None: 'cover' + getImageFileName(filelist[-1][1])[1])
cover = os.path.join(os.path.join(path, 'OEBPS', 'Images'), options.covers.append((image.Cover(os.path.join(filelist[-1][0], filelist[-1][1]), cover, options,
'cover' + getImageFileName(filelist[-1][1])[1]) tomeNumber), options.uuid))
options.covers.append((image.Cover(os.path.join(filelist[-1][0], filelist[-1][1]), cover, options,
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 = []
@@ -490,13 +462,13 @@ def buildEPUB(path, chapterNames, tomeNumber):
def imgDirectoryProcessing(path): def imgDirectoryProcessing(path):
global workerPool, workerOutput global workerPool, workerOutput
workerPool = Pool() workerPool = Pool(maxtasksperchild=100)
workerOutput = [] workerOutput = []
options.imgIndex = {} options.imgMetadata = {}
options.imgPurgeIndex = [] options.imgOld = []
work = [] work = []
pagenumber = 0 pagenumber = 0
for (dirpath, dirnames, filenames) in walk(path): for dirpath, _, filenames in os.walk(path):
for afile in filenames: for afile in filenames:
pagenumber += 1 pagenumber += 1
work.append([afile, dirpath, options]) work.append([afile, dirpath, options])
@@ -512,24 +484,24 @@ def imgDirectoryProcessing(path):
raise UserWarning("Conversion interrupted.") raise UserWarning("Conversion interrupted.")
if len(workerOutput) > 0: if len(workerOutput) > 0:
rmtree(os.path.join(path, '..', '..'), True) rmtree(os.path.join(path, '..', '..'), True)
raise RuntimeError("One of workers crashed. Cause: " + workerOutput[0]) raise RuntimeError("One of workers crashed. Cause: " + workerOutput[0][0], workerOutput[0][1])
for file in options.imgPurgeIndex: for file in options.imgOld:
if os.path.isfile(file): if os.path.isfile(file):
saferRemove(file) os.remove(file)
else: else:
rmtree(os.path.join(path, '..', '..'), True) rmtree(os.path.join(path, '..', '..'), True)
raise UserWarning("Source directory is empty.") raise UserWarning("Source directory is empty.")
def imgFileProcessingTick(output): def imgFileProcessingTick(output):
if isinstance(output, str): if isinstance(output, tuple):
workerOutput.append(output) workerOutput.append(output)
workerPool.terminate() workerPool.terminate()
else: else:
for page in output: for page in output:
if page is not None: if page is not None:
options.imgIndex[page[0]] = page[1] options.imgMetadata[page[0]] = page[1]
options.imgPurgeIndex.append(page[2]) options.imgOld.append(page[2])
if GUI: if GUI:
GUI.progressBarTick.emit('tick') GUI.progressBarTick.emit('tick')
if not GUI.conversionAlive: if not GUI.conversionAlive:
@@ -544,11 +516,11 @@ def imgFileProcessing(work):
output = [] output = []
workImg = image.ComicPageParser((dirpath, afile), opt) workImg = image.ComicPageParser((dirpath, afile), opt)
for i in workImg.payload: for i in workImg.payload:
img = image.ComicPage(i[0], i[1], i[2], i[3], i[4], opt) img = image.ComicPage(opt, *i)
if opt.cropping > 0 and not opt.webtoon:
img.cropWhiteSpace(opt.croppingp)
if opt.cropping == 2 and not opt.webtoon: if opt.cropping == 2 and not opt.webtoon:
img.cutPageNumber(opt.croppingpn) img.cropPageNumber(opt.croppingp)
if opt.cropping > 0 and not opt.webtoon:
img.cropMargin(opt.croppingp)
img.autocontrastImage() img.autocontrastImage()
img.resizeImage() img.resizeImage()
if opt.forcepng and not opt.forcecolor: if opt.forcepng and not opt.forcecolor:
@@ -556,11 +528,13 @@ def imgFileProcessing(work):
output.append(img.saveToDir()) output.append(img.saveToDir())
return output return output
except Exception: except Exception:
return str(sys.exc_info()[:2]) return str(sys.exc_info()[1]), sanitizeTrace(sys.exc_info()[2])
def getWorkFolder(afile): def getWorkFolder(afile):
if os.path.isdir(afile): if os.path.isdir(afile):
if disk_usage(gettempdir())[2] < getDirectorySize(afile) * 2.5:
raise UserWarning("Not enough disk space to perform conversion.")
workdir = mkdtemp('', 'KCC-') workdir = mkdtemp('', 'KCC-')
try: try:
os.rmdir(workdir) os.rmdir(workdir)
@@ -571,24 +545,27 @@ def getWorkFolder(afile):
except: except:
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) and afile.lower().endswith('.pdf'):
pdf = pdfjpgextract.PdfJpgExtract(afile)
path, njpg = pdf.extract()
if njpg == 0:
rmtree(path, True)
raise UserWarning("Failed to extract images from PDF file.")
elif os.path.isfile(afile): elif os.path.isfile(afile):
workdir = mkdtemp('', 'KCC-') if disk_usage(gettempdir())[2] < os.path.getsize(afile) * 2.5:
cbx = cbxarchive.CBxArchive(afile) raise UserWarning("Not enough disk space to perform conversion.")
if cbx.isCbxFile(): if afile.lower().endswith('.pdf'):
try: pdf = pdfjpgextract.PdfJpgExtract(afile)
path = cbx.extract(workdir) path, njpg = pdf.extract()
except: if njpg == 0:
rmtree(workdir, True) rmtree(path, True)
raise UserWarning("Failed to extract archive.") raise UserWarning("Failed to extract images from PDF file.")
else: else:
rmtree(workdir, True) workdir = mkdtemp('', 'KCC-')
raise UserWarning("Failed to detect archive format.") cbx = cbxarchive.CBxArchive(afile)
if cbx.isCbxFile():
try:
path = cbx.extract(workdir)
except:
rmtree(workdir, True)
raise UserWarning("Failed to extract archive.")
else:
rmtree(workdir, True)
raise UserWarning("Failed to detect archive format.")
else: else:
raise UserWarning("Failed to open source file/directory.") raise UserWarning("Failed to open source file/directory.")
sanitizePermissions(path) sanitizePermissions(path)
@@ -694,13 +671,23 @@ def getCoversFromMCB(mangaID):
def getDirectorySize(start_path='.'): def getDirectorySize(start_path='.'):
total_size = 0 total_size = 0
for dirpath, dirnames, filenames in walk(start_path): for dirpath, _, filenames in os.walk(start_path):
for f in filenames: for f in filenames:
fp = os.path.join(dirpath, f) fp = os.path.join(dirpath, f)
total_size += os.path.getsize(fp) total_size += os.path.getsize(fp)
return total_size return total_size
def getTopMargin(deviceres, size):
y = int((deviceres[1] - size[1]) / 2) / deviceres[1] * 100
return str(round(y, 1))
def getPanelViewResolution(imageSize, deviceRes):
scale = float(deviceRes[0]) / float(imageSize[0])
return int(deviceRes[0]), int(scale * imageSize[1])
def getPanelViewSize(deviceres, size): def getPanelViewSize(deviceres, size):
x = int(deviceres[0] / 2 - size[0] / 2) / deviceres[0] * 100 x = int(deviceres[0] / 2 - size[0] / 2) / deviceres[0] * 100
y = int(deviceres[1] / 2 - size[1] / 2) / deviceres[1] * 100 y = int(deviceres[1] / 2 - size[1] / 2) / deviceres[1] * 100
@@ -709,7 +696,7 @@ def getPanelViewSize(deviceres, size):
def sanitizeTree(filetree): def sanitizeTree(filetree):
chapterNames = {} chapterNames = {}
for root, dirs, files in 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])
@@ -719,7 +706,7 @@ def sanitizeTree(filetree):
newKey = os.path.join(root, slugified + splitname[1]) newKey = os.path.join(root, slugified + splitname[1])
key = os.path.join(root, name) key = os.path.join(root, name)
if key != newKey: if key != newKey:
saferReplace(key, newKey) os.replace(key, newKey)
for name in dirs: for name in dirs:
tmpName = name tmpName = name
slugified = slugify(name) slugified = slugify(name)
@@ -729,13 +716,13 @@ def sanitizeTree(filetree):
newKey = os.path.join(root, slugified) newKey = os.path.join(root, slugified)
key = os.path.join(root, name) key = os.path.join(root, name)
if key != newKey: if key != newKey:
saferReplace(key, newKey) os.replace(key, newKey)
return chapterNames return chapterNames
def sanitizeTreeKobo(filetree): def sanitizeTreeKobo(filetree):
pageNumber = 0 pageNumber = 0
for root, dirs, files in walk(filetree): for root, dirs, files in os.walk(filetree):
dirs, files = walkSort(dirs, files) dirs, files = walkSort(dirs, files)
for name in files: for name in files:
splitname = os.path.splitext(name) splitname = os.path.splitext(name)
@@ -747,74 +734,36 @@ def sanitizeTreeKobo(filetree):
newKey = os.path.join(root, slugified + splitname[1]) newKey = os.path.join(root, slugified + splitname[1])
key = os.path.join(root, name) key = os.path.join(root, name)
if key != newKey: if key != newKey:
saferReplace(key, newKey) os.replace(key, newKey)
def sanitizePermissions(filetree): def sanitizePermissions(filetree):
for root, dirs, files in walk(filetree, False): for root, dirs, files in os.walk(filetree, False):
for name in files: for name in files:
os.chmod(os.path.join(root, name), S_IWRITE | S_IREAD) os.chmod(os.path.join(root, name), S_IWRITE | S_IREAD)
for name in dirs: for name in dirs:
os.chmod(os.path.join(root, name), S_IWRITE | S_IREAD | S_IEXEC) os.chmod(os.path.join(root, name), S_IWRITE | S_IREAD | S_IEXEC)
# noinspection PyUnboundLocalVariable
def splitDirectory(path): def splitDirectory(path):
# Detect directory stucture level = -1
for root, dirs, files in walkLevel(os.path.join(path, 'OEBPS', 'Images'), 0): for root, _, files in os.walk(os.path.join(path, 'OEBPS', 'Images')):
subdirectoryNumber = len(dirs) for f in files:
filesNumber = len(files) if f.endswith('.jpg') or f.endswith('.jpeg') or f.endswith('.png') or f.endswith('.gif'):
if subdirectoryNumber == 0: newLevel = os.path.join(root, f).replace(os.path.join(path, 'OEBPS', 'Images'), '').count(os.sep)
# No subdirectories if level != -1 and level != newLevel:
mode = 0 level = 0
break
else:
level = newLevel
if level > 0:
splitter = splitProcess(os.path.join(path, 'OEBPS', 'Images'), level)
path = [path]
for tome in splitter:
path.append(tome)
return path
else: else:
if filesNumber > 0: raise UserWarning('Unsupported directory structure.')
print('WARNING: Automatic output splitting failed.')
if GUI:
GUI.addMessage.emit('Automatic output splitting failed. <a href='
'"https://github.com/ciromattia/kcc/wiki'
'/Automatic-output-splitting">'
'More details.</a>', 'warning', False)
GUI.addMessage.emit('', '', False)
return [path]
detectedSubSubdirectories = False
detectedFilesInSubdirectories = False
for root, dirs, files in walkLevel(os.path.join(path, 'OEBPS', 'Images'), 1):
if root != os.path.join(path, 'OEBPS', 'Images'):
if len(dirs) != 0:
detectedSubSubdirectories = True
elif len(dirs) == 0 and detectedSubSubdirectories:
print('WARNING: Automatic output splitting failed.')
if GUI:
GUI.addMessage.emit('Automatic output splitting failed. <a href='
'"https://github.com/ciromattia/kcc/wiki'
'/Automatic-output-splitting">'
'More details.</a>', 'warning', False)
GUI.addMessage.emit('', '', False)
return [path]
if len(files) != 0:
detectedFilesInSubdirectories = True
if detectedSubSubdirectories:
# Two levels of subdirectories
mode = 2
else:
# One level of subdirectories
mode = 1
if detectedFilesInSubdirectories and detectedSubSubdirectories:
print('WARNING: Automatic output splitting failed.')
if GUI:
GUI.addMessage.emit('Automatic output splitting failed. <a href='
'"https://github.com/ciromattia/kcc/wiki'
'/Automatic-output-splitting">'
'More details.</a>', 'warning', False)
GUI.addMessage.emit('', '', False)
return [path]
# Split directories
splitter = splitProcess(os.path.join(path, 'OEBPS', 'Images'), mode)
path = [path]
for tome in splitter:
path.append(tome)
return path
def splitProcess(path, mode): def splitProcess(path, mode):
@@ -825,10 +774,15 @@ def splitProcess(path, mode):
targetSize = 104857600 targetSize = 104857600
else: else:
targetSize = 419430400 targetSize = 419430400
if mode == 0: if options.batchsplit == 2 and mode == 2:
mode = 3
if mode < 3:
for root, dirs, files in walkLevel(path, 0): for root, dirs, files in walkLevel(path, 0):
for name in files: for name in files if mode == 1 else dirs:
size = os.path.getsize(os.path.join(root, name)) if mode == 1:
size = os.path.getsize(os.path.join(root, name))
else:
size = getDirectorySize(os.path.join(root, name))
if currentSize + size > targetSize: if currentSize + size > targetSize:
currentTarget, pathRoot = createNewTome() currentTarget, pathRoot = createNewTome()
output.append(pathRoot) output.append(pathRoot)
@@ -837,57 +791,28 @@ def splitProcess(path, mode):
currentSize += size currentSize += size
if path != currentTarget: if path != currentTarget:
move(os.path.join(root, name), os.path.join(currentTarget, name)) move(os.path.join(root, name), os.path.join(currentTarget, name))
elif mode == 1: else:
for root, dirs, files in walkLevel(path, 0):
for name in dirs:
size = getDirectorySize(os.path.join(root, name))
if currentSize + size > targetSize:
currentTarget, pathRoot = createNewTome()
output.append(pathRoot)
currentSize = size
else:
currentSize += size
if path != currentTarget:
move(os.path.join(root, name), os.path.join(currentTarget, name))
elif mode == 2:
firstTome = True firstTome = True
for root, dirs, files in walkLevel(path, 0): for root, dirs, _ in walkLevel(path, 0):
for name in dirs: for name in dirs:
size = getDirectorySize(os.path.join(root, name)) if not firstTome:
currentSize = 0 currentTarget, pathRoot = createNewTome()
if size > targetSize: output.append(pathRoot)
if not firstTome: move(os.path.join(root, name), os.path.join(currentTarget, name))
currentTarget, pathRoot = createNewTome()
output.append(pathRoot)
else:
firstTome = False
for rootInside, dirsInside, filesInside in walkLevel(os.path.join(root, name), 0):
for nameInside in dirsInside:
size = getDirectorySize(os.path.join(rootInside, nameInside))
if currentSize + size > targetSize:
currentTarget, pathRoot = createNewTome()
output.append(pathRoot)
currentSize = size
else:
currentSize += size
if path != currentTarget:
move(os.path.join(rootInside, nameInside), os.path.join(currentTarget, nameInside))
else: else:
if not firstTome: firstTome = False
currentTarget, pathRoot = createNewTome()
output.append(pathRoot)
move(os.path.join(root, name), os.path.join(currentTarget, name))
else:
firstTome = False
return output return output
def detectCorruption(tmpPath, orgPath): def detectCorruption(tmpPath, orgPath):
imageNumber = 0 imageNumber = 0
imageSmaller = 0 imageSmaller = 0
for root, dirs, files in walk(tmpPath, False): alreadyProcessed = 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'):
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:
@@ -908,12 +833,18 @@ def detectCorruption(tmpPath, orgPath):
else: else:
raise RuntimeError('Image file %s is corrupted.' % pathOrg) raise RuntimeError('Image file %s is corrupted.' % pathOrg)
else: else:
saferRemove(os.path.join(root, name)) os.remove(os.path.join(root, name))
if alreadyProcessed:
print("WARNING: Source files are probably created by KCC. The second conversion will decrease quality.")
if GUI:
GUI.addMessage.emit('Source files are probably created by KCC. The second conversion will decrease quality.'
, 'warning', False)
GUI.addMessage.emit('', '', False)
if imageSmaller > imageNumber * 0.25 and not options.upscale and not options.stretch: if imageSmaller > imageNumber * 0.25 and not options.upscale and not options.stretch:
print("WARNING: More than 1/4 of images are smaller than target device resolution. " print("WARNING: More than 25% of images are smaller than target device resolution. "
"Consider enabling stretching or upscaling to improve readability.") "Consider enabling stretching or upscaling to improve readability.")
if GUI: if GUI:
GUI.addMessage.emit('More than 1/4 of images are smaller than target device resolution.', 'warning', False) GUI.addMessage.emit('More than 25% of images are smaller than target device resolution.', 'warning', False)
GUI.addMessage.emit('Consider enabling stretching or upscaling to improve readability.', 'warning', False) GUI.addMessage.emit('Consider enabling stretching or upscaling to improve readability.', 'warning', False)
GUI.addMessage.emit('', '', False) GUI.addMessage.emit('', '', False)
@@ -936,7 +867,7 @@ def makeZIP(zipFilename, baseDir, isEPUB=False):
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, dirnames, filenames in 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))
@@ -956,10 +887,14 @@ 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, K3, K45, KDX, KPW, KV, KoMT, KoG, KoGHD," help="Device profile (Available options: K1, K2, K34, K578, KDX, KPW, KV, KoMT, KoG, KoGHD,"
" KoA, KoAHD, KoAH2O) [Default=KV]") " KoA, KoAHD, KoAH2O, KoAO) [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,
help="Try to increase the quality of magnification")
mainOptions.add_option("-2", "--two-panel", action="store_true", dest="autoscale", default=False,
help="Display two not four panels in Panel View mode")
mainOptions.add_option("-w", "--webtoon", action="store_true", dest="webtoon", default=False, mainOptions.add_option("-w", "--webtoon", action="store_true", dest="webtoon", default=False,
help="Webtoon processing mode"), help="Webtoon processing mode"),
@@ -969,8 +904,9 @@ def makeParser():
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) [Default=Auto]")
outputOptions.add_option("-b", "--batchsplit", action="store_true", dest="batchsplit", default=False, outputOptions.add_option("-b", "--batchsplit", type="int", dest="batchsplit", default="0",
help="Split output into multiple files"), help="Split output into multiple files. 0: Don't split 1: Automatic mode "
"2: Consider every subdirectory as separate volume [Default=0]")
processingOptions.add_option("-u", "--upscale", action="store_true", dest="upscale", default=False, processingOptions.add_option("-u", "--upscale", action="store_true", dest="upscale", default=False,
help="Resize images smaller than device's resolution") help="Resize images smaller than device's resolution")
@@ -980,8 +916,10 @@ def makeParser():
help="Double page parsing mode. 0: Split 1: Rotate 2: Both [Default=0]") help="Double page parsing mode. 0: Split 1: Rotate 2: Both [Default=0]")
processingOptions.add_option("-g", "--gamma", type="float", dest="gamma", default="0.0", processingOptions.add_option("-g", "--gamma", type="float", dest="gamma", default="0.0",
help="Apply gamma correction to linearize the image [Default=Auto]") help="Apply gamma correction to linearize the image [Default=Auto]")
processingOptions.add_option("--hq", action="store_true", dest="hqmode", default=False, processingOptions.add_option("-c", "--cropping", type="int", dest="cropping", default="2",
help="Enable high quality Panel View") help="Set cropping mode. 0: Disabled 1: Margins 2: Margins + page numbers [Default=2]")
processingOptions.add_option("--cp", "--croppingpower", type="float", dest="croppingp", default="1.0",
help="Set cropping power [Default=1.0]")
processingOptions.add_option("--blackborders", action="store_true", dest="black_borders", default=False, processingOptions.add_option("--blackborders", action="store_true", dest="black_borders", default=False,
help="Disable autodetection and force black borders") help="Disable autodetection and force black borders")
processingOptions.add_option("--whiteborders", action="store_true", dest="white_borders", default=False, processingOptions.add_option("--whiteborders", action="store_true", dest="white_borders", default=False,
@@ -990,12 +928,6 @@ def makeParser():
help="Don't convert images to grayscale") help="Don't convert images to grayscale")
processingOptions.add_option("--forcepng", action="store_true", dest="forcepng", default=False, processingOptions.add_option("--forcepng", action="store_true", dest="forcepng", default=False,
help="Create PNG files instead JPEG") help="Create PNG files instead JPEG")
processingOptions.add_option("--cropping", type="int", dest="cropping", default="2",
help="Set cropping mode. 0: Disabled 1: Margins 2: Margins + page numbers [Default=2]")
processingOptions.add_option("--croppingpower", type="float", dest="croppingp", default="0.1",
help="Set margin cropping threshold [Default=0.1]")
processingOptions.add_option("--croppingpowerpage", type="float", dest="croppingpn", default="5.0",
help="Set page number cropping threshold [Default=5.0]")
customProfileOptions.add_option("--customwidth", type="int", dest="customwidth", default=0, customProfileOptions.add_option("--customwidth", type="int", dest="customwidth", default=0,
help="Replace screen width provided by device profile") help="Replace screen width provided by device profile")
@@ -1019,38 +951,38 @@ def checkOptions():
options.iskindle = False options.iskindle = False
options.bordersColor = None options.bordersColor = None
if options.format == 'Auto': if options.format == 'Auto':
if options.profile in ['K1', 'K2', 'K3', 'K45', 'KPW', 'KV']: if options.profile in ['K1', 'K2', 'K34', 'K578', 'KPW', 'KV']:
options.format = 'MOBI' options.format = 'MOBI'
elif options.profile in ['OTHER', 'KoMT', 'KoG', 'KoGHD', 'KoA', 'KoAHD', 'KoAH2O']: 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', 'K3', 'K45', 'KPW', 'KV']: if options.profile in ['K1', 'K2', 'K34', 'K578', 'KPW', 'KV']:
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': if options.format == 'MOBI' and options.batchsplit != 2:
options.batchsplit = True options.batchsplit = 1
# Older Kindle don't need higher resolution files due lack of Panel View. # Older Kindle models don't support Panel View.
if options.profile == 'K1' or options.profile == 'K2' or options.profile == 'K3' or options.profile == 'KDX': if options.profile == 'K1' or options.profile == 'K2' or options.profile == 'K34' or options.profile == 'KDX':
options.panelview = False options.panelview = False
options.hqmode = False options.hq = False
# Webtoon mode mandatory options # Webtoon mode mandatory options
if options.webtoon: if options.webtoon:
options.panelview = False options.panelview = False
options.hqmode = False
options.righttoleft = False options.righttoleft = False
options.upscale = True options.upscale = True
options.hq = False
# Disable all Kindle features for other e-readers # Disable all Kindle features for other e-readers
if options.profile == 'OTHER': if options.profile == 'OTHER':
options.panelview = False options.panelview = False
options.hqmode = False options.hq = False
if 'Ko' in options.profile: if 'Ko' in options.profile:
options.panelview = False options.panelview = False
options.hqmode = False options.hq = False
# 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
@@ -1063,7 +995,7 @@ def checkOptions():
if options.customheight != 0: if options.customheight != 0:
Y = options.customheight Y = options.customheight
newProfile = ("Custom", (int(X), int(Y)), image.ProfileData.Palette16, newProfile = ("Custom", (int(X), int(Y)), image.ProfileData.Palette16,
image.ProfileData.Profiles[options.profile][3], (int(int(X) * 1.5), int(int(Y) * 1.5))) image.ProfileData.Profiles[options.profile][3])
image.ProfileData.Profiles["Custom"] = newProfile image.ProfileData.Profiles["Custom"] = newProfile
options.profile = "Custom" options.profile = "Custom"
options.profileData = image.ProfileData.Profiles[options.profile] options.profileData = image.ProfileData.Profiles[options.profile]
@@ -1122,8 +1054,8 @@ def makeBook(source, qtGUI=None):
getComicInfo(os.path.join(path, "OEBPS", "Images"), source) getComicInfo(os.path.join(path, "OEBPS", "Images"), source)
detectCorruption(os.path.join(path, "OEBPS", "Images"), source) detectCorruption(os.path.join(path, "OEBPS", "Images"), source)
if options.webtoon: if options.webtoon:
if image.ProfileData.Profiles[options.profile][1][1] > 1000: if image.ProfileData.Profiles[options.profile][1][1] > 1024:
y = 1000 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)
@@ -1136,7 +1068,7 @@ def makeBook(source, qtGUI=None):
chapterNames = sanitizeTree(os.path.join(path, 'OEBPS', 'Images')) chapterNames = sanitizeTree(os.path.join(path, 'OEBPS', 'Images'))
if 'Ko' in options.profile and options.format == 'CBZ': if 'Ko' in options.profile and options.format == 'CBZ':
sanitizeTreeKobo(os.path.join(path, 'OEBPS', 'Images')) sanitizeTreeKobo(os.path.join(path, 'OEBPS', 'Images'))
if options.batchsplit: if options.batchsplit > 0:
tomes = splitDirectory(path) tomes = splitDirectory(path)
else: else:
tomes = [path] tomes = [path]
@@ -1181,7 +1113,6 @@ def makeBook(source, qtGUI=None):
if not GUI and options.format == 'MOBI': if not GUI and options.format == 'MOBI':
print("Creating MOBI files...") print("Creating MOBI files...")
work = [] work = []
k = kindle.Kindle()
for i in filepath: for i in filepath:
work.append([i]) work.append([i])
output = makeMOBI(work, GUI) output = makeMOBI(work, GUI)
@@ -1190,6 +1121,7 @@ def makeBook(source, qtGUI=None):
print('Error: KindleGen failed to create MOBI!') print('Error: KindleGen failed to create MOBI!')
print(errors) print(errors)
return filepath return filepath
k = kindle.Kindle()
if k.path and k.coverSupport: if k.path and k.coverSupport:
print("Kindle detected. Uploading covers...") print("Kindle detected. Uploading covers...")
for i in filepath: for i in filepath:
@@ -1266,9 +1198,11 @@ def makeMOBI(work, qtGUI=None):
threadNumber = 1 threadNumber = 1
elif 2 < availableMemory <= 4: elif 2 < availableMemory <= 4:
threadNumber = 2 threadNumber = 2
else: elif 4 < availableMemory <= 8:
threadNumber = 4 threadNumber = 4
makeMOBIWorkerPool = Pool(threadNumber) else:
threadNumber = None
makeMOBIWorkerPool = Pool(threadNumber, maxtasksperchild=10)
for i in work: for i in work:
makeMOBIWorkerPool.apply_async(func=makeMOBIWorker, args=(i, ), callback=makeMOBIWorkerTick) makeMOBIWorkerPool.apply_async(func=makeMOBIWorker, args=(i, ), callback=makeMOBIWorkerTick)
makeMOBIWorkerPool.close() makeMOBIWorkerPool.close()

View File

@@ -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-2016 Pawel Jastrzebski <pawelj@iosphe.re> # Copyright (c) 2013-2017 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,16 +23,12 @@ import sys
from shutil import rmtree, copytree, move from shutil import rmtree, copytree, move
from optparse import OptionParser, OptionGroup from optparse import OptionParser, OptionGroup
from multiprocessing import Pool from multiprocessing import Pool
from PIL import Image, ImageStat, ImageOps from PIL import Image, ImageChops, ImageOps, ImageDraw
from .shared import getImageFileName, walkLevel, walkSort from .shared import getImageFileName, walkLevel, walkSort, sanitizeTrace
try: try:
from PyQt5 import QtCore from PyQt5 import QtCore
except ImportError: except ImportError:
QtCore = None QtCore = None
try:
from scandir import walk
except ImportError:
walk = os.walk
def mergeDirectoryTick(output): def mergeDirectoryTick(output):
@@ -52,7 +48,7 @@ def mergeDirectory(work):
imagesValid = [] imagesValid = []
sizes = [] sizes = []
targetHeight = 0 targetHeight = 0
for root, dirs, files in walkLevel(directory, 0): for root, _, files in walkLevel(directory, 0):
for name in files: for name in files:
if getImageFileName(name) is not None: if getImageFileName(name) is not None:
i = Image.open(os.path.join(root, name)) i = Image.open(os.path.join(root, name))
@@ -71,8 +67,7 @@ def mergeDirectory(work):
result = Image.new('RGB', (targetWidth, targetHeight)) result = Image.new('RGB', (targetWidth, targetHeight))
y = 0 y = 0
for i in imagesValid: for i in imagesValid:
img = Image.open(i) img = Image.open(i).convert('RGB')
img = img.convert('RGB')
if img.size[0] < targetWidth: if img.size[0] < targetWidth:
img = ImageOps.fit(img, (targetWidth, img.size[1]), method=Image.BICUBIC, centering=(0.5, 0.5)) img = ImageOps.fit(img, (targetWidth, img.size[1]), method=Image.BICUBIC, centering=(0.5, 0.5))
result.paste(img, (0, y)) result.paste(img, (0, y))
@@ -81,33 +76,11 @@ def mergeDirectory(work):
savePath = os.path.split(imagesValid[0]) savePath = os.path.split(imagesValid[0])
result.save(os.path.join(savePath[0], os.path.splitext(savePath[1])[0] + '.png'), 'PNG') result.save(os.path.join(savePath[0], os.path.splitext(savePath[1])[0] + '.png'), 'PNG')
except Exception: except Exception:
return str(sys.exc_info()[1]) return str(sys.exc_info()[1]), sanitizeTrace(sys.exc_info()[2])
def sanitizePanelSize(panel, opt): def detectSolid(img):
newPanels = [] return not ImageChops.invert(img).getbbox() or not img.getbbox()
if panel[2] > 6 * opt.height:
diff = int(panel[2] / 8)
newPanels.append([panel[0], panel[1] - diff * 7, diff])
newPanels.append([panel[1] - diff * 7, panel[1] - diff * 6, diff])
newPanels.append([panel[1] - diff * 6, panel[1] - diff * 5, diff])
newPanels.append([panel[1] - diff * 5, panel[1] - diff * 4, diff])
newPanels.append([panel[1] - diff * 4, panel[1] - diff * 3, diff])
newPanels.append([panel[1] - diff * 3, panel[1] - diff * 2, diff])
newPanels.append([panel[1] - diff * 2, panel[1] - diff, diff])
newPanels.append([panel[1] - diff, panel[1], diff])
elif panel[2] > 3 * opt.height:
diff = int(panel[2] / 4)
newPanels.append([panel[0], panel[1] - diff * 3, diff])
newPanels.append([panel[1] - diff * 3, panel[1] - diff * 2, diff])
newPanels.append([panel[1] - diff * 2, panel[1] - diff, diff])
newPanels.append([panel[1] - diff, panel[1], diff])
elif panel[2] > 1.5 * opt.height:
newPanels.append([panel[0], panel[1] - int(panel[2] / 2), int(panel[2] / 2)])
newPanels.append([panel[1] - int(panel[2] / 2), panel[1], int(panel[2] / 2)])
else:
newPanels = [panel]
return newPanels
def splitImageTick(output): def splitImageTick(output):
@@ -125,56 +98,60 @@ def splitImage(work):
path = work[0] path = work[0]
name = work[1] name = work[1]
opt = work[2] opt = work[2]
# Hardcoded options
threshold = 1.0
delta = 15
fileExpanded = os.path.splitext(name)
filePath = os.path.join(path, name) filePath = os.path.join(path, name)
image = Image.open(filePath) imgOrg = Image.open(filePath).convert('RGB')
image = image.convert('RGB') imgProcess = Image.open(filePath).convert('1')
widthImg, heightImg = image.size widthImg, heightImg = imgOrg.size
if heightImg > opt.height: if heightImg > opt.height:
if opt.debug: if opt.debug:
from PIL import ImageDraw drawImg = Image.open(filePath).convert(mode='RGBA')
debugImage = Image.open(filePath) draw = ImageDraw.Draw(drawImg)
draw = ImageDraw.Draw(debugImage)
# Find panels # Find panels
y1 = 0 yWork = 0
y2 = 15 panelDetected = False
panels = [] panels = []
while y2 < heightImg: while yWork < heightImg:
while ImageStat.Stat(image.crop([0, y1, widthImg, y2])).var[0] < threshold and y2 < heightImg: tmpImg = imgProcess.crop([0, yWork, widthImg, yWork + 4])
y2 += delta solid = detectSolid(tmpImg)
y2 -= delta if not solid and not panelDetected:
y1Temp = y2 panelDetected = True
y1 = y2 + delta panelY1 = yWork - 2
y2 = y1 + delta if solid and panelDetected:
while ImageStat.Stat(image.crop([0, y1, widthImg, y2])).var[0] >= threshold and y2 < heightImg: panelDetected = False
y1 += delta panelY2 = yWork + 6
y2 += delta panels.append((panelY1, panelY2, panelY2 - panelY1))
if y1 + delta >= heightImg: yWork += 5
y1 = heightImg - 1
y2Temp = y1 # Split too big panels
if opt.debug: panelsProcessed = []
draw.line([(0, y1Temp), (widthImg, y1Temp)], fill=(0, 255, 0)) for panel in panels:
draw.line([(0, y2Temp), (widthImg, y2Temp)], fill=(255, 0, 0)) if panel[2] <= opt.height * 1.5:
panelHeight = y2Temp - y1Temp panelsProcessed.append(panel)
if panelHeight > delta: elif panel[2] < opt.height * 2:
# Panels that can't be cut nicely will be forcefully splitted diff = panel[2] - opt.height
panelsCleaned = sanitizePanelSize([y1Temp, y2Temp, panelHeight], opt) panelsProcessed.append((panel[0], panel[1] - diff, opt.height))
for panel in panelsCleaned: panelsProcessed.append((panel[1] - opt.height, panel[1], opt.height))
panels.append(panel) else:
parts = round(panel[2] / opt.height)
diff = panel[2] // parts
for x in range(0, parts):
panelsProcessed.append((panel[0] + (x * diff), panel[1] - ((parts - x - 1) * diff), diff))
if opt.debug: if opt.debug:
for panel in panelsProcessed:
# noinspection PyUnboundLocalVariable
draw.rectangle([(0, panel[0]), (widthImg, panel[1])], (0, 255, 0, 128), (0, 0, 255, 255))
# noinspection PyUnboundLocalVariable # noinspection PyUnboundLocalVariable
debugImage.save(os.path.join(path, fileExpanded[0] + '-debug.png'), 'PNG') debugImage = Image.alpha_composite(imgOrg.convert(mode='RGBA'), drawImg)
debugImage.save(os.path.join(path, os.path.splitext(name)[0] + '-debug.png'), 'PNG')
# Create virtual pages # Create virtual pages
pages = [] pages = []
currentPage = [] currentPage = []
pageLeft = opt.height pageLeft = opt.height
panelNumber = 0 panelNumber = 0
for panel in panels: for panel in panelsProcessed:
if pageLeft - panel[2] > 0: if pageLeft - panel[2] > 0:
pageLeft -= panel[2] pageLeft -= panel[2]
currentPage.append(panelNumber) currentPage.append(panelNumber)
@@ -194,18 +171,18 @@ def splitImage(work):
pageHeight = 0 pageHeight = 0
targetHeight = 0 targetHeight = 0
for panel in page: for panel in page:
pageHeight += panels[panel][2] pageHeight += panelsProcessed[panel][2]
if pageHeight > delta: if pageHeight > 15:
newPage = Image.new('RGB', (widthImg, pageHeight)) newPage = Image.new('RGB', (widthImg, pageHeight))
for panel in page: for panel in page:
panelImg = image.crop([0, panels[panel][0], widthImg, panels[panel][1]]) panelImg = imgOrg.crop([0, panelsProcessed[panel][0], widthImg, panelsProcessed[panel][1]])
newPage.paste(panelImg, (0, targetHeight)) newPage.paste(panelImg, (0, targetHeight))
targetHeight += panels[panel][2] targetHeight += panelsProcessed[panel][2]
newPage.save(os.path.join(path, fileExpanded[0] + '-' + str(pageNumber) + '.png'), 'PNG') newPage.save(os.path.join(path, os.path.splitext(name)[0] + '-' + str(pageNumber) + '.png'), 'PNG')
pageNumber += 1 pageNumber += 1
os.remove(filePath) os.remove(filePath)
except Exception: except Exception:
return str(sys.exc_info()[1]) return str(sys.exc_info()[1]), sanitizeTrace(sys.exc_info()[2])
def main(argv=None, qtGUI=None): def main(argv=None, qtGUI=None):
@@ -220,7 +197,7 @@ def main(argv=None, qtGUI=None):
mainOptions.add_option("-m", "--merge", action="store_true", dest="merge", default=False, mainOptions.add_option("-m", "--merge", action="store_true", dest="merge", default=False,
help="Combine every directory into a single image before splitting") help="Combine every directory into a single image before splitting")
otherOptions.add_option("-d", "--debug", action="store_true", dest="debug", default=False, otherOptions.add_option("-d", "--debug", action="store_true", dest="debug", default=False,
help="Create debug file for every splitted image") help="Create debug file for every split image")
otherOptions.add_option("-h", "--help", action="help", otherOptions.add_option("-h", "--help", action="help",
help="Show this help message and exit") help="Show this help message and exit")
parser.add_option_group(mainOptions) parser.add_option_group(mainOptions)
@@ -242,15 +219,15 @@ def main(argv=None, qtGUI=None):
work = [] work = []
pagenumber = 1 pagenumber = 1
splitWorkerOutput = [] splitWorkerOutput = []
splitWorkerPool = Pool() splitWorkerPool = Pool(maxtasksperchild=10)
if options.merge: if options.merge:
print("Merging images...") print("Merging images...")
directoryNumer = 1 directoryNumer = 1
mergeWork = [] mergeWork = []
mergeWorkerOutput = [] mergeWorkerOutput = []
mergeWorkerPool = Pool() mergeWorkerPool = Pool(maxtasksperchild=10)
mergeWork.append([options.targetDir]) mergeWork.append([options.targetDir])
for root, dirs, files in walk(options.targetDir, False): for root, dirs, files in os.walk(options.targetDir, False):
dirs, files = walkSort(dirs, files) dirs, files = walkSort(dirs, files)
for directory in dirs: for directory in dirs:
directoryNumer += 1 directoryNumer += 1
@@ -267,9 +244,10 @@ def main(argv=None, qtGUI=None):
raise UserWarning("Conversion interrupted.") raise UserWarning("Conversion interrupted.")
if len(mergeWorkerOutput) > 0: if len(mergeWorkerOutput) > 0:
rmtree(options.targetDir, True) rmtree(options.targetDir, True)
raise RuntimeError("One of workers crashed. Cause: " + mergeWorkerOutput[0]) raise RuntimeError("One of workers crashed. Cause: " + mergeWorkerOutput[0][0],
mergeWorkerOutput[0][1])
print("Splitting images...") print("Splitting images...")
for root, dirs, files in walk(options.targetDir, False): for root, _, files in os.walk(options.targetDir, False):
for name in files: for name in files:
if getImageFileName(name) is not None: if getImageFileName(name) is not None:
pagenumber += 1 pagenumber += 1
@@ -290,7 +268,8 @@ def main(argv=None, qtGUI=None):
raise UserWarning("Conversion interrupted.") raise UserWarning("Conversion interrupted.")
if len(splitWorkerOutput) > 0: if len(splitWorkerOutput) > 0:
rmtree(options.targetDir, True) rmtree(options.targetDir, True)
raise RuntimeError("One of workers crashed. Cause: " + splitWorkerOutput[0]) raise RuntimeError("One of workers crashed. Cause: " + splitWorkerOutput[0][0],
splitWorkerOutput[0][1])
if options.inPlace: if options.inPlace:
rmtree(options.sourceDir) rmtree(options.sourceDir)
move(options.targetDir, options.sourceDir) move(options.targetDir, options.sourceDir)

View File

@@ -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-2016 Pawel Jastrzebski <pawelj@iosphe.re> # Changes for KCC Copyright (C) 2014-2017 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

View File

@@ -1,7 +1,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) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com> # Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
# Copyright (c) 2013-2016 Pawel Jastrzebski <pawelj@iosphe.re> # Copyright (c) 2013-2017 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
@@ -20,7 +21,7 @@ import os
from io import BytesIO from io import BytesIO
from urllib.request import Request, urlopen from urllib.request import Request, urlopen
from urllib.parse import quote from urllib.parse import quote
from PIL import Image, ImageOps, ImageStat, ImageChops from PIL import Image, ImageOps, ImageStat, ImageChops, ImageFilter
from .shared import md5Checksum from .shared import md5Checksum
from . import __version__ from . import __version__
@@ -77,20 +78,21 @@ class ProfileData:
] ]
Profiles = { Profiles = {
'K1': ("Kindle 1", (600, 670), Palette4, 1.8, (900, 1005)), 'K1': ("Kindle 1", (600, 670), Palette4, 1.8),
'K2': ("Kindle 2", (600, 670), Palette15, 1.8, (900, 1005)), 'K2': ("Kindle 2", (600, 670), Palette15, 1.8),
'K3': ("Kindle", (600, 800), Palette16, 1.8, (900, 1200)), 'K34': ("Kindle Keyboard/Touch", (600, 800), Palette16, 1.8),
'K45': ("Kindle", (600, 800), Palette16, 1.8, (900, 1200)), 'K578': ("Kindle", (600, 800), Palette16, 1.8),
'KDX': ("Kindle DX/DXG", (824, 1000), Palette16, 1.8, (1236, 1500)), 'KDX': ("Kindle DX/DXG", (824, 1000), Palette16, 1.8),
'KPW': ("Kindle Paperwhite 1/2", (758, 1024), Palette16, 1.8, (1137, 1536)), 'KPW': ("Kindle Paperwhite 1/2", (758, 1024), Palette16, 1.8),
'KV': ("Kindle Paperwhite 3/Voyage/Oasis", (1072, 1448), Palette16, 1.8, (1608, 2172)), 'KV': ("Kindle Paperwhite 3/Voyage/Oasis", (1072, 1448), Palette16, 1.8),
'KoMT': ("Kobo Mini/Touch", (600, 800), Palette16, 1.8, (900, 1200)), 'KoMT': ("Kobo Mini/Touch", (600, 800), Palette16, 1.8),
'KoG': ("Kobo Glo", (768, 1024), Palette16, 1.8, (1152, 1536)), 'KoG': ("Kobo Glo", (768, 1024), Palette16, 1.8),
'KoGHD': ("Kobo Glo HD", (1072, 1448), Palette16, 1.8, (1608, 2172)), 'KoGHD': ("Kobo Glo HD", (1072, 1448), Palette16, 1.8),
'KoA': ("Kobo Aura", (758, 1024), Palette16, 1.8, (1137, 1536)), 'KoA': ("Kobo Aura", (758, 1024), Palette16, 1.8),
'KoAHD': ("Kobo Aura HD", (1080, 1440), Palette16, 1.8, (1620, 2160)), 'KoAHD': ("Kobo Aura HD", (1080, 1440), Palette16, 1.8),
'KoAH2O': ("Kobo Aura H2O", (1080, 1430), Palette16, 1.8, (1620, 2145)), 'KoAH2O': ("Kobo Aura H2O", (1080, 1430), Palette16, 1.8),
'OTHER': ("Other", (0, 0), Palette16, 1.8, (0, 0)), 'KoAO': ("Kobo Aura ONE", (1404, 1872), Palette16, 1.8),
'OTHER': ("Other", (0, 0), Palette16, 1.8),
} }
@@ -104,8 +106,6 @@ class ComicPageParser:
self.color = self.colorCheck() self.color = self.colorCheck()
self.fill = self.fillCheck() self.fill = self.fillCheck()
self.splitCheck() self.splitCheck()
if self.opt.hqmode:
self.sizeCheck()
def getImageHistogram(self, image): def getImageHistogram(self, image):
histogram = image.histogram() histogram = image.histogram()
@@ -119,15 +119,15 @@ class ComicPageParser:
def splitCheck(self): def splitCheck(self):
width, height = self.image.size width, height = self.image.size
dstwidth, dstheight = self.size dstwidth, dstheight = self.size
# Only split if origin is not oriented the same as target if (width > height) != (dstwidth > dstheight) and width <= dstheight and height <= dstwidth \
if (width > height) != (dstwidth > dstheight) and not self.opt.webtoon: and not self.opt.webtoon and self.opt.splitter == 1:
self.payload.append(['R', self.source, self.image.rotate(90, Image.BICUBIC, True), self.color, self.fill])
elif (width > height) != (dstwidth > dstheight) and not self.opt.webtoon:
if self.opt.splitter != 1: if self.opt.splitter != 1:
if width > height: if width > height:
# Source is landscape, so split by the width
leftbox = (0, 0, int(width / 2), height) leftbox = (0, 0, int(width / 2), height)
rightbox = (int(width / 2), 0, width, height) rightbox = (int(width / 2), 0, width, height)
else: else:
# Source is portrait and target is landscape, so split by the height
leftbox = (0, 0, width, int(height / 2)) leftbox = (0, 0, width, int(height / 2))
rightbox = (0, int(height / 2), width, height) rightbox = (0, int(height / 2), width, height)
if self.opt.righttoleft: if self.opt.righttoleft:
@@ -204,29 +204,18 @@ class ComicPageParser:
else: else:
return 'white' return 'white'
def sizeCheck(self):
additionalPayload = []
width, height = self.image.size
dstwidth, dstheight = self.size
for work in self.payload:
if width > dstwidth and height > dstheight:
additionalPayload.append([work[0] + '+', work[1], work[2].copy(), work[3], work[4]])
self.payload = self.payload + additionalPayload
class ComicPage: class ComicPage:
def __init__(self, mode, path, image, color, fill, options): def __init__(self, options, mode, path, image, color, fill):
self.opt = options self.opt = options
_, self.size, self.palette, self.gamma, self.panelviewsize = self.opt.profileData _, self.size, self.palette, self.gamma = self.opt.profileData
if self.opt.hq:
self.size = (int(self.size[0] * 1.5), int(self.size[1] * 1.5))
self.image = image self.image = image
self.color = color self.color = color
self.fill = fill self.fill = fill
self.rotated = False self.rotated = False
self.orgPath = os.path.join(path[0], path[1]) self.orgPath = os.path.join(path[0], path[1])
if '+' in mode:
self.hqMode = True
else:
self.hqMode = False
if 'N' in mode: if 'N' in mode:
self.targetPath = os.path.join(path[0], os.path.splitext(path[1])[0]) + '-KCC' self.targetPath = os.path.join(path[0], os.path.splitext(path[1])[0]) + '-KCC'
elif 'R' in mode: elif 'R' in mode:
@@ -245,18 +234,16 @@ class ComicPage:
if self.rotated: if self.rotated:
flags.append('Rotated') flags.append('Rotated')
if self.fill != 'white': if self.fill != 'white':
flags.append('BlackFill') flags.append('BlackBackground')
if self.hqMode:
self.targetPath += '-HQ'
if self.opt.forcepng: if self.opt.forcepng:
self.targetPath += '.png' self.targetPath += '.png'
self.image.save(self.targetPath, 'PNG', optimize=1) self.image.save(self.targetPath, 'PNG', optimize=1)
else: else:
self.targetPath += '.jpg' self.targetPath += '.jpg'
self.image.save(self.targetPath, 'JPEG', optimize=1, quality=80) self.image.save(self.targetPath, 'JPEG', optimize=1, quality=85)
return [md5Checksum(self.targetPath), flags, self.orgPath] return [md5Checksum(self.targetPath), flags, self.orgPath]
except IOError: except IOError as err:
raise RuntimeError('Cannot save image.') raise RuntimeError('Cannot save image. ' + str(err))
def autocontrastImage(self): def autocontrastImage(self):
gamma = self.opt.gamma gamma = self.opt.gamma
@@ -281,128 +268,69 @@ class ComicPage:
self.image = self.image.quantize(palette=palImg) self.image = self.image.quantize(palette=palImg)
def resizeImage(self): def resizeImage(self):
if self.hqMode: if self.image.size[0] <= self.size[0] and self.image.size[1] <= self.size[1]:
size = (self.panelviewsize[0], self.panelviewsize[1]) method = Image.BICUBIC
if self.image.size[0] > size[0] or self.image.size[1] > size[1]:
self.image.thumbnail(size, Image.LANCZOS)
else: else:
size = (self.size[0], self.size[1]) method = Image.LANCZOS
if self.image.size[0] <= size[0] and self.image.size[1] <= size[1]: if self.opt.stretch:
method = Image.BICUBIC self.image = self.image.resize(self.size, method)
elif self.image.size[0] <= self.size[0] and self.image.size[1] <= self.size[1] and not self.opt.upscale:
if self.opt.format == 'CBZ':
borderw = int((self.size[0] - self.image.size[0]) / 2)
borderh = int((self.size[1] - self.image.size[1]) / 2)
self.image = ImageOps.expand(self.image, border=(borderw, borderh), fill=self.fill)
if self.image.size[0] != self.size[0] or self.image.size[1] != self.size[1]:
self.image = ImageOps.fit(self.image, self.size, method=Image.BICUBIC, centering=(0.5, 0.5))
else:
if self.opt.format == 'CBZ':
ratioDev = float(self.size[0]) / float(self.size[1])
if (float(self.image.size[0]) / float(self.image.size[1])) < ratioDev:
diff = int(self.image.size[1] * ratioDev) - self.image.size[0]
self.image = ImageOps.expand(self.image, border=(int(diff / 2), 0), fill=self.fill)
elif (float(self.image.size[0]) / float(self.image.size[1])) > ratioDev:
diff = int(self.image.size[0] / ratioDev) - self.image.size[1]
self.image = ImageOps.expand(self.image, border=(0, int(diff / 2)), fill=self.fill)
self.image = ImageOps.fit(self.image, self.size, method=method, centering=(0.5, 0.5))
else: else:
method = Image.LANCZOS hpercent = self.size[1] / float(self.image.size[1])
if self.opt.stretch: wsize = int((float(self.image.size[0]) * float(hpercent)))
self.image = self.image.resize(size, method) self.image = self.image.resize((wsize, self.size[1]), method)
elif self.image.size[0] <= size[0] and self.image.size[1] <= size[1] and not self.opt.upscale: if self.image.size[0] > self.size[0] or self.image.size[1] > self.size[1]:
if self.opt.format == 'CBZ': self.image.thumbnail(self.size, Image.LANCZOS)
borderw = int((size[0] - self.image.size[0]) / 2)
borderh = int((size[1] - self.image.size[1]) / 2)
self.image = ImageOps.expand(self.image, border=(borderw, borderh), fill=self.fill)
if self.image.size[0] != size[0] or self.image.size[1] != size[1]:
self.image = ImageOps.fit(self.image, size, method=Image.BICUBIC, centering=(0.5, 0.5))
else:
if self.opt.format == 'CBZ':
ratioDev = float(size[0]) / float(size[1])
if (float(self.image.size[0]) / float(self.image.size[1])) < ratioDev:
diff = int(self.image.size[1] * ratioDev) - self.image.size[0]
self.image = ImageOps.expand(self.image, border=(int(diff / 2), 0), fill=self.fill)
elif (float(self.image.size[0]) / float(self.image.size[1])) > ratioDev:
diff = int(self.image.size[0] / ratioDev) - self.image.size[1]
self.image = ImageOps.expand(self.image, border=(0, int(diff / 2)), fill=self.fill)
self.image = ImageOps.fit(self.image, size, method=method, centering=(0.5, 0.5))
else:
hpercent = size[1] / float(self.image.size[1])
wsize = int((float(self.image.size[0]) * float(hpercent)))
self.image = self.image.resize((wsize, size[1]), method)
if self.image.size[0] > size[0] or self.image.size[1] > size[1]:
self.image.thumbnail(size, Image.LANCZOS)
def cutPageNumber(self, fixedThreshold): def getBoundingBox(self, tmpImg):
if ImageChops.invert(self.image).getbbox() is not None: min_margin = [int(0.005 * i + 0.5) for i in tmpImg.size]
widthImg, heightImg = self.image.size max_margin = [int(0.1 * i + 0.5) for i in tmpImg.size]
delta = 2 bbox = tmpImg.getbbox()
diff = delta bbox = (
if ImageStat.Stat(self.image).var[0] < 2 * fixedThreshold: max(0, min(max_margin[0], bbox[0] - min_margin[0])),
return self.image max(0, min(max_margin[1], bbox[1] - min_margin[1])),
while ImageStat.Stat(self.image.crop((0, heightImg - diff, widthImg, heightImg))).var[0] < fixedThreshold\ min(tmpImg.size[0],
and diff < heightImg: max(tmpImg.size[0] - max_margin[0], bbox[2] + min_margin[0])),
diff += delta min(tmpImg.size[1],
diff -= delta max(tmpImg.size[1] - max_margin[1], bbox[3] + min_margin[1])),
pageNumberCut1 = diff )
if diff < delta: return bbox
diff = delta
oldStat = ImageStat.Stat(self.image.crop((0, heightImg - diff, widthImg, heightImg))).var[0]
diff += delta
while ImageStat.Stat(self.image.crop((0, heightImg - diff, widthImg, heightImg))).var[0] - oldStat > 0\
and diff < heightImg // 4:
oldStat = ImageStat.Stat(self.image.crop((0, heightImg - diff, widthImg, heightImg))).var[0]
diff += delta
diff -= delta
pageNumberCut2 = diff
diff += delta
oldStat = ImageStat.Stat(self.image.crop((0, heightImg - diff, widthImg,
heightImg - pageNumberCut2))).var[0]
while ImageStat.Stat(self.image.crop((0, heightImg - diff, widthImg, heightImg - pageNumberCut2))).var[0]\
< fixedThreshold + oldStat and diff < heightImg // 4:
diff += delta
diff -= delta
pageNumberCut3 = diff
delta = 5
diff = delta
while ImageStat.Stat(self.image.crop((0, heightImg - pageNumberCut2, diff, heightImg))).var[0]\
< fixedThreshold and diff < widthImg:
diff += delta
diff -= delta
pageNumberX1 = diff
diff = delta
while ImageStat.Stat(self.image.crop((widthImg - diff, heightImg - pageNumberCut2,
widthImg, heightImg))).var[0] < fixedThreshold and diff < widthImg:
diff += delta
diff -= delta
pageNumberX2 = widthImg - diff
if pageNumberCut3 - pageNumberCut1 > 2 * delta\
and float(pageNumberX2 - pageNumberX1) / float(pageNumberCut2 - pageNumberCut1) <= 9.0\
and ImageStat.Stat(self.image.crop((0, heightImg - pageNumberCut3, widthImg, heightImg))).var[0]\
/ ImageStat.Stat(self.image).var[0] < 0.1\
and pageNumberCut3 < heightImg / 4 - delta:
diff = pageNumberCut3
else:
diff = pageNumberCut1
self.image = self.image.crop((0, 0, widthImg, heightImg - diff))
def cropWhiteSpace(self, fixedThreshold): def cropPageNumber(self, power):
if ImageChops.invert(self.image).getbbox() is not None: if self.fill != 'white':
widthImg, heightImg = self.image.size tmpImg = self.image.convert(mode='L')
delta = 10 else:
diff = delta tmpImg = ImageOps.invert(self.image.convert(mode='L'))
# top tmpImg = tmpImg.point(lambda x: x and 255)
while ImageStat.Stat(self.image.crop((0, 0, widthImg, diff))).var[0] < fixedThreshold and diff < heightImg: tmpImg = tmpImg.filter(ImageFilter.MinFilter(size=3))
diff += delta tmpImg = tmpImg.filter(ImageFilter.GaussianBlur(radius=5))
diff -= delta tmpImg = tmpImg.point(lambda x: (x >= 16 * power) and x)
self.image = self.image.crop((0, diff, widthImg, heightImg)) self.image = self.image.crop(tmpImg.getbbox()) if tmpImg.getbbox() else self.image
widthImg, heightImg = self.image.size
diff = delta def cropMargin(self, power):
# left if self.fill != 'white':
while ImageStat.Stat(self.image.crop((0, 0, diff, heightImg))).var[0] < fixedThreshold and diff < widthImg: tmpImg = self.image.convert(mode='L')
diff += delta else:
diff -= delta tmpImg = ImageOps.invert(self.image.convert(mode='L'))
self.image = self.image.crop((diff, 0, widthImg, heightImg)) tmpImg = tmpImg.filter(ImageFilter.GaussianBlur(radius=3))
widthImg, heightImg = self.image.size tmpImg = tmpImg.point(lambda x: (x >= 16 * power) and x)
diff = delta self.image = self.image.crop(self.getBoundingBox(tmpImg)) if tmpImg.getbbox() else self.image
# down
while ImageStat.Stat(self.image.crop((0, heightImg - diff, widthImg, heightImg))).var[0] < fixedThreshold\
and diff < heightImg:
diff += delta
diff -= delta
self.image = self.image.crop((0, 0, widthImg, heightImg - diff))
widthImg, heightImg = self.image.size
diff = delta
# right
while ImageStat.Stat(self.image.crop((widthImg - diff, 0, widthImg, heightImg))).var[0] < fixedThreshold\
and diff < widthImg:
diff += delta
diff -= delta
self.image = self.image.crop((0, 0, widthImg - diff, heightImg))
class Cover: class Cover:
@@ -419,44 +347,30 @@ class Cover:
source = urlopen(Request(quote(self.options.remoteCovers[self.tomeNumber]).replace('%3A', ':', 1), source = urlopen(Request(quote(self.options.remoteCovers[self.tomeNumber]).replace('%3A', ':', 1),
headers={'User-Agent': 'KindleComicConverter/' + __version__})).read() headers={'User-Agent': 'KindleComicConverter/' + __version__})).read()
self.image = Image.open(BytesIO(source)) self.image = Image.open(BytesIO(source))
self.processExternal()
except Exception: except Exception:
self.image = Image.open(source) self.image = Image.open(source)
self.processInternal()
else: else:
self.image = Image.open(source) self.image = Image.open(source)
self.processInternal() self.process()
def processInternal(self): def process(self):
self.image = self.image.convert('RGB')
self.image = self.trim()
self.save()
def processExternal(self):
self.image = self.image.convert('RGB') self.image = self.image.convert('RGB')
self.image = ImageOps.autocontrast(self.image)
if not self.options.forcecolor:
self.image = self.image.convert('L')
self.image.thumbnail(self.options.profileData[1], Image.LANCZOS) self.image.thumbnail(self.options.profileData[1], Image.LANCZOS)
self.save() self.save()
def trim(self):
bg = Image.new(self.image.mode, self.image.size, self.image.getpixel((0, 0)))
diff = ImageChops.difference(self.image, bg)
diff = ImageChops.add(diff, diff, 2.0, -100)
bbox = diff.getbbox()
if bbox:
return self.image.crop(bbox)
else:
return self.image
def save(self): def save(self):
try: try:
self.image.save(self.target, "JPEG", optimize=1, quality=80) self.image.save(self.target, "JPEG", optimize=1, quality=85)
except IOError: except IOError:
raise RuntimeError('Failed to process downloaded cover.') raise RuntimeError('Failed to process downloaded cover.')
def saveToKindle(self, kindle, asin): def saveToKindle(self, kindle, asin):
self.image = self.image.resize((300, 470), Image.ANTIALIAS).convert('L') self.image = self.image.resize((300, 470), Image.ANTIALIAS)
try: try:
self.image.save(os.path.join(kindle.path.split('documents')[0], 'system', 'thumbnails', self.image.save(os.path.join(kindle.path.split('documents')[0], 'system', 'thumbnails',
'thumbnail_' + asin + '_EBOK_portrait.jpg'), 'JPEG') 'thumbnail_' + asin + '_EBOK_portrait.jpg'), 'JPEG', optimize=1, quality=85)
except IOError: except IOError:
raise RuntimeError('Failed to upload cover.') raise RuntimeError('Failed to upload cover.')

View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (c) 2013-2016 Pawel Jastrzebski <pawelj@iosphe.re> # Copyright (c) 2013-2017 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

View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (c) 2013-2016 Pawel Jastrzebski <pawelj@iosphe.re> # Copyright (c) 2013-2017 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

View File

@@ -1,5 +1,5 @@
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com> # Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
# Copyright (c) 2013-2016 Pawel Jastrzebski <pawelj@iosphe.re> # Copyright (c) 2013-2017 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)
@@ -61,7 +61,6 @@ class PdfJpgExtract:
iend += endfix iend += endfix
jpg = pdf[istart:iend] jpg = pdf[istart:iend]
jpgfile = open(self.path + "/jpg%d.jpg" % njpg, "wb") jpgfile = open(self.path + "/jpg%d.jpg" % njpg, "wb")
# noinspection PyTypeChecker
jpgfile.write(jpg) jpgfile.write(jpg)
jpgfile.close() jpgfile.close()
njpg += 1 njpg += 1

View File

@@ -1,5 +1,5 @@
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com> # Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
# Copyright (c) 2013-2016 Pawel Jastrzebski <pawelj@iosphe.re> # Copyright (c) 2013-2017 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
@@ -17,20 +17,14 @@
# #
import os import os
from sys import version_info
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 time import sleep
from shutil import rmtree, copy from shutil import rmtree, copy
from tempfile import mkdtemp from tempfile import mkdtemp
from zipfile import ZipFile, ZIP_DEFLATED from zipfile import ZipFile, ZIP_DEFLATED
from re import split from re import split
from traceback import format_tb from traceback import format_tb
try:
from scandir import walk
except ImportError:
walk = os.walk
class HTMLStripper(HTMLParser): class HTMLStripper(HTMLParser):
@@ -71,7 +65,7 @@ def walkLevel(some_dir, level=1):
some_dir = some_dir.rstrip(os.path.sep) some_dir = some_dir.rstrip(os.path.sep)
assert os.path.isdir(some_dir) assert os.path.isdir(some_dir)
num_sep = some_dir.count(os.path.sep) num_sep = some_dir.count(os.path.sep)
for root, dirs, files in walk(some_dir): for root, dirs, files in os.walk(some_dir):
dirs, files = walkSort(dirs, files) dirs, files = walkSort(dirs, files)
yield root, dirs, files yield root, dirs, files
num_sep_this = root.count(os.path.sep) num_sep_this = root.count(os.path.sep)
@@ -96,30 +90,6 @@ def check7ZFile(filePath):
return header == b"7z\xbc\xaf'\x1c" return header == b"7z\xbc\xaf'\x1c"
def saferReplace(old, new):
for x in range(30):
try:
os.replace(old, new)
except PermissionError:
sleep(1)
else:
break
else:
raise PermissionError("Failed to move the file.")
def saferRemove(target):
for x in range(30):
try:
os.remove(target)
except PermissionError:
sleep(1)
else:
break
else:
raise PermissionError("Failed to remove the file.")
def removeFromZIP(zipfname, *filenames): def removeFromZIP(zipfname, *filenames):
tempdir = mkdtemp('', 'KCC-') tempdir = mkdtemp('', 'KCC-')
try: try:
@@ -129,25 +99,21 @@ def removeFromZIP(zipfname, *filenames):
for item in zipread.infolist(): for item in zipread.infolist():
if item.filename not in filenames: if item.filename not in filenames:
zipwrite.writestr(item, zipread.read(item.filename)) zipwrite.writestr(item, zipread.read(item.filename))
for x in range(30): copy(tempname, zipfname)
try:
copy(tempname, zipfname)
except PermissionError:
sleep(1)
else:
break
else:
raise PermissionError
finally: finally:
rmtree(tempdir, True) rmtree(tempdir, True)
def sanitizeTrace(traceback): def sanitizeTrace(traceback):
return ''.join(format_tb(traceback))\ return ''.join(format_tb(traceback))\
.replace('C:\\Users\\pawel\\Documents\\Projekty\\KCC\\', '') \ .replace('C:/projects/kcc/', '') \
.replace('C:\\Users\\Paweł\\Documents\\Projekty\\KCC\\', '') \ .replace('c:/projects/kcc/', '') \
.replace('C:\\Python35\\', '')\ .replace('C:/python36-x64/', '')\
.replace('c:\\python35\\', '') .replace('c:/python36-x64/', '')\
.replace('C:\\projects\\kcc\\', '') \
.replace('c:\\projects\\kcc\\', '') \
.replace('C:\\python36-x64\\', '')\
.replace('c:\\python36-x64\\', '')
def dependencyCheck(level): def dependencyCheck(level):
@@ -162,33 +128,26 @@ def dependencyCheck(level):
try: try:
import raven import raven
except ImportError: except ImportError:
missing.append('raven 5.13.0+') missing.append('raven 6.0.0+')
if level > 1: if level > 1:
try: try:
from psutil import __version__ as psutilVersion from psutil import __version__ as psutilVersion
if StrictVersion('4.1.0') > StrictVersion(psutilVersion): if StrictVersion('5.0.0') > StrictVersion(psutilVersion):
missing.append('psutil 4.1.0+') missing.append('psutil 5.0.0+')
except ImportError: except ImportError:
missing.append('psutil 4.1.0+') missing.append('psutil 5.0.0+')
try: try:
from slugify import __version__ as slugifyVersion from slugify import __version__ as slugifyVersion
if StrictVersion('1.2.0') > StrictVersion(slugifyVersion): if StrictVersion('1.2.1') > StrictVersion(slugifyVersion):
missing.append('python-slugify 1.2.0+') missing.append('python-slugify 1.2.1+')
except ImportError: except ImportError:
missing.append('python-slugify 1.2.0+') missing.append('python-slugify 1.2.1+')
try: try:
from PIL import PILLOW_VERSION as pillowVersion from PIL import PILLOW_VERSION as pillowVersion
if StrictVersion('3.2.0') > StrictVersion(pillowVersion): if StrictVersion('4.0.0') > StrictVersion(pillowVersion):
missing.append('Pillow 3.2.0+') missing.append('Pillow 4.0.0+')
except ImportError: except ImportError:
missing.append('Pillow 3.2.0+') missing.append('Pillow 4.0.0+')
if version_info[1] < 5:
try:
from scandir import __version__ as scandirVersion
if StrictVersion('1.2') > StrictVersion(scandirVersion):
missing.append('scandir 1.2+')
except ImportError:
missing.append('scandir 1.2+')
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)

View File

@@ -0,0 +1,56 @@
# -*- coding: utf-8 -*-
#
# 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
import sys
from . import __version__
from .shared import dependencyCheck
def start():
dependencyCheck(3)
from . import KCC_gui
os.environ['QT_AUTO_SCREEN_SCALE_FACTOR'] = "1"
KCCAplication = KCC_gui.QApplicationMessaging(sys.argv)
if KCCAplication.isRunning():
if len(sys.argv) > 1:
KCCAplication.sendMessage(sys.argv[1])
else:
KCCAplication.sendMessage('ARISE')
else:
KCCWindow = KCC_gui.QMainWindowKCC()
KCCUI = KCC_gui.KCCGUI(KCCAplication, KCCWindow)
if len(sys.argv) > 1:
KCCUI.handleMessage(sys.argv[1])
sys.exit(KCCAplication.exec_())
def startC2E():
dependencyCheck(2)
from .comic2ebook import main
print('comic2ebook v' + __version__ + ' - Written by Ciro Mattia Gonano and Pawel Jastrzebski.')
sys.exit(main(sys.argv[1:]))
def startC2P():
dependencyCheck(1)
from .comic2panel import main
print('comic2panel v' + __version__ + ' - Written by Ciro Mattia Gonano and Pawel Jastrzebski.')
sys.exit(main(sys.argv[1:]))

View File

@@ -5,7 +5,7 @@ Name=Kindle Comic Converter
GenericName=Kindle Comic Converter GenericName=Kindle Comic Converter
Comment=Comic and Manga converter for e-book readers Comment=Comic and Manga converter for e-book readers
Icon=/usr/share/kindlecomicconverter/comic2ebook.png Icon=/usr/share/kindlecomicconverter/comic2ebook.png
Exec=/usr/bin/kcc Exec=/usr/bin/kcc %f
Terminal=false Terminal=false
Categories=Graphics; Categories=Graphics;
MimeType=application/zip;application/x-rar;application/x-7z-compressed; MimeType=application/zip;application/x-rar;application/x-7z-compressed;

BIN
other/linux/sentry.py.enc Normal file

Binary file not shown.

View File

@@ -6,10 +6,31 @@
<string>English</string> <string>English</string>
<key>CFBundleDisplayName</key> <key>CFBundleDisplayName</key>
<string>Kindle Comic Converter</string> <string>Kindle Comic Converter</string>
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>cbz</string>
<string>cbr</string>
<string>cb7</string>
<string>zip</string>
<string>rar</string>
<string>7z</string>
<string>pdf</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>comic2ebook.icns</string>
<key>CFBundleTypeName</key>
<string>Comics</string>
<key>CFBundleTypeRole</key>
<string>Editor</string>
</dict>
</array>
<key>CFBundleExecutable</key> <key>CFBundleExecutable</key>
<string>MacOS/Kindle Comic Converter</string> <string>MacOS/Kindle Comic Converter</string>
<key>CFBundleGetInfoString</key> <key>CFBundleGetInfoString</key>
<string>KindleComicConverter 5.1.1, written 2012-2016 by Ciro Mattia Gonano and Pawel Jastrzebski</string> <string>KindleComicConverter 5.4.1, written 2012-2017 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>
@@ -21,11 +42,11 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>5.1.1</string> <string>5.4.1</string>
<key>CFBundleSignature</key> <key>CFBundleSignature</key>
<string>????</string> <string>????</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>5.1.1</string> <string>5.4.1</string>
<key>LSEnvironment</key> <key>LSEnvironment</key>
<dict> <dict>
<key>PATH</key> <key>PATH</key>
@@ -34,7 +55,7 @@
<key>LSHasLocalizedDisplayName</key> <key>LSHasLocalizedDisplayName</key>
<false/> <false/>
<key>LSMinimumSystemVersion</key> <key>LSMinimumSystemVersion</key>
<string>10.9.0</string> <string>10.10.0</string>
<key>NSAppleScriptEnabled</key> <key>NSAppleScriptEnabled</key>
<false/> <false/>
<key>NSHumanReadableCopyright</key> <key>NSHumanReadableCopyright</key>

BIN
other/osx/sentry.py.enc Normal file

Binary file not shown.

BIN
other/windows/Cert.pfx.enc Normal file

Binary file not shown.

View File

@@ -3,19 +3,20 @@
{\flomajor\f31500\fbidi \froman\fcharset238\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\fdbmajor\f31501\fbidi \froman\fcharset238\fprq2{\*\panose 02020603050405020304}Times New Roman;} {\flomajor\f31500\fbidi \froman\fcharset238\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\fdbmajor\f31501\fbidi \froman\fcharset238\fprq2{\*\panose 02020603050405020304}Times New Roman;}
{\fhimajor\f31502\fbidi \fswiss\fcharset238\fprq2{\*\panose 020f0302020204030204}Calibri Light;}{\fbimajor\f31503\fbidi \froman\fcharset238\fprq2{\*\panose 02020603050405020304}Times New Roman;} {\fhimajor\f31502\fbidi \fswiss\fcharset238\fprq2{\*\panose 020f0302020204030204}Calibri Light;}{\fbimajor\f31503\fbidi \froman\fcharset238\fprq2{\*\panose 02020603050405020304}Times New Roman;}
{\flominor\f31504\fbidi \froman\fcharset238\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\fdbminor\f31505\fbidi \froman\fcharset238\fprq2{\*\panose 02020603050405020304}Times New Roman;} {\flominor\f31504\fbidi \froman\fcharset238\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\fdbminor\f31505\fbidi \froman\fcharset238\fprq2{\*\panose 02020603050405020304}Times New Roman;}
{\fhiminor\f31506\fbidi \fswiss\fcharset238\fprq2{\*\panose 020f0502020204030204}Calibri;}{\fbiminor\f31507\fbidi \froman\fcharset238\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f41\fbidi \froman\fcharset0\fprq2 Times New Roman;} {\fhiminor\f31506\fbidi \fswiss\fcharset238\fprq2{\*\panose 020f0502020204030204}Calibri;}{\fbiminor\f31507\fbidi \froman\fcharset238\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f44\fbidi \froman\fcharset0\fprq2 Times New Roman;}
{\f40\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\f42\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\f43\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\f44\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);} {\f43\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\f45\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\f46\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\f47\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}
{\f45\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\f46\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\f47\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\f41\fbidi \froman\fcharset0\fprq2 Times New Roman;} {\f48\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\f49\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\f50\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\f44\fbidi \froman\fcharset0\fprq2 Times New Roman;}
{\f40\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\f42\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\f43\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\f44\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);} {\f43\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\f45\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\f46\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\f47\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}
{\f45\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\f46\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\f47\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\f411\fbidi \fswiss\fcharset0\fprq2 Calibri;} {\f48\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\f49\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\f50\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\f414\fbidi \fswiss\fcharset0\fprq2 Calibri;}
{\f410\fbidi \fswiss\fcharset204\fprq2 Calibri Cyr;}{\f412\fbidi \fswiss\fcharset161\fprq2 Calibri Greek;}{\f413\fbidi \fswiss\fcharset162\fprq2 Calibri Tur;}{\f416\fbidi \fswiss\fcharset186\fprq2 Calibri Baltic;} {\f413\fbidi \fswiss\fcharset204\fprq2 Calibri Cyr;}{\f415\fbidi \fswiss\fcharset161\fprq2 Calibri Greek;}{\f416\fbidi \fswiss\fcharset162\fprq2 Calibri Tur;}{\f417\fbidi \fswiss\fcharset177\fprq2 Calibri (Hebrew);}
{\f417\fbidi \fswiss\fcharset163\fprq2 Calibri (Vietnamese);}{\flomajor\f31510\fbidi \froman\fcharset0\fprq2 Times New Roman;}{\flomajor\f31509\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;} {\f418\fbidi \fswiss\fcharset178\fprq2 Calibri (Arabic);}{\f419\fbidi \fswiss\fcharset186\fprq2 Calibri Baltic;}{\f420\fbidi \fswiss\fcharset163\fprq2 Calibri (Vietnamese);}{\flomajor\f31510\fbidi \froman\fcharset0\fprq2 Times New Roman;}
{\flomajor\f31511\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\flomajor\f31512\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\flomajor\f31513\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);} {\flomajor\f31509\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\flomajor\f31511\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\flomajor\f31512\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}
{\flomajor\f31514\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\flomajor\f31515\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\flomajor\f31516\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);} {\flomajor\f31513\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\flomajor\f31514\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\flomajor\f31515\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}
{\fdbmajor\f31520\fbidi \froman\fcharset0\fprq2 Times New Roman;}{\fdbmajor\f31519\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\fdbmajor\f31521\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;} {\flomajor\f31516\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fdbmajor\f31520\fbidi \froman\fcharset0\fprq2 Times New Roman;}{\fdbmajor\f31519\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}
{\fdbmajor\f31522\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fdbmajor\f31523\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\fdbmajor\f31524\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);} {\fdbmajor\f31521\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fdbmajor\f31522\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fdbmajor\f31523\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}
{\fdbmajor\f31525\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fdbmajor\f31526\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fhimajor\f31530\fbidi \fswiss\fcharset0\fprq2 Calibri Light;} {\fdbmajor\f31524\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fdbmajor\f31525\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fdbmajor\f31526\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}
{\fhimajor\f31529\fbidi \fswiss\fcharset204\fprq2 Calibri Light Cyr;}{\fhimajor\f31531\fbidi \fswiss\fcharset161\fprq2 Calibri Light Greek;}{\fhimajor\f31532\fbidi \fswiss\fcharset162\fprq2 Calibri Light Tur;} {\fhimajor\f31530\fbidi \fswiss\fcharset0\fprq2 Calibri Light;}{\fhimajor\f31529\fbidi \fswiss\fcharset204\fprq2 Calibri Light Cyr;}{\fhimajor\f31531\fbidi \fswiss\fcharset161\fprq2 Calibri Light Greek;}
{\fhimajor\f31532\fbidi \fswiss\fcharset162\fprq2 Calibri Light Tur;}{\fhimajor\f31533\fbidi \fswiss\fcharset177\fprq2 Calibri Light (Hebrew);}{\fhimajor\f31534\fbidi \fswiss\fcharset178\fprq2 Calibri Light (Arabic);}
{\fhimajor\f31535\fbidi \fswiss\fcharset186\fprq2 Calibri Light Baltic;}{\fhimajor\f31536\fbidi \fswiss\fcharset163\fprq2 Calibri Light (Vietnamese);}{\fbimajor\f31540\fbidi \froman\fcharset0\fprq2 Times New Roman;} {\fhimajor\f31535\fbidi \fswiss\fcharset186\fprq2 Calibri Light Baltic;}{\fhimajor\f31536\fbidi \fswiss\fcharset163\fprq2 Calibri Light (Vietnamese);}{\fbimajor\f31540\fbidi \froman\fcharset0\fprq2 Times New Roman;}
{\fbimajor\f31539\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\fbimajor\f31541\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fbimajor\f31542\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;} {\fbimajor\f31539\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\fbimajor\f31541\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fbimajor\f31542\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}
{\fbimajor\f31543\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\fbimajor\f31544\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fbimajor\f31545\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;} {\fbimajor\f31543\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\fbimajor\f31544\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fbimajor\f31545\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}
@@ -26,20 +27,21 @@
{\fdbminor\f31562\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fdbminor\f31563\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\fdbminor\f31564\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);} {\fdbminor\f31562\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fdbminor\f31563\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\fdbminor\f31564\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}
{\fdbminor\f31565\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fdbminor\f31566\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fhiminor\f31570\fbidi \fswiss\fcharset0\fprq2 Calibri;} {\fdbminor\f31565\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fdbminor\f31566\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fhiminor\f31570\fbidi \fswiss\fcharset0\fprq2 Calibri;}
{\fhiminor\f31569\fbidi \fswiss\fcharset204\fprq2 Calibri Cyr;}{\fhiminor\f31571\fbidi \fswiss\fcharset161\fprq2 Calibri Greek;}{\fhiminor\f31572\fbidi \fswiss\fcharset162\fprq2 Calibri Tur;} {\fhiminor\f31569\fbidi \fswiss\fcharset204\fprq2 Calibri Cyr;}{\fhiminor\f31571\fbidi \fswiss\fcharset161\fprq2 Calibri Greek;}{\fhiminor\f31572\fbidi \fswiss\fcharset162\fprq2 Calibri Tur;}
{\fhiminor\f31575\fbidi \fswiss\fcharset186\fprq2 Calibri Baltic;}{\fhiminor\f31576\fbidi \fswiss\fcharset163\fprq2 Calibri (Vietnamese);}{\fbiminor\f31580\fbidi \froman\fcharset0\fprq2 Times New Roman;} {\fhiminor\f31573\fbidi \fswiss\fcharset177\fprq2 Calibri (Hebrew);}{\fhiminor\f31574\fbidi \fswiss\fcharset178\fprq2 Calibri (Arabic);}{\fhiminor\f31575\fbidi \fswiss\fcharset186\fprq2 Calibri Baltic;}
{\fbiminor\f31579\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\fbiminor\f31581\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fbiminor\f31582\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;} {\fhiminor\f31576\fbidi \fswiss\fcharset163\fprq2 Calibri (Vietnamese);}{\fbiminor\f31580\fbidi \froman\fcharset0\fprq2 Times New Roman;}{\fbiminor\f31579\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}
{\fbiminor\f31583\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\fbiminor\f31584\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fbiminor\f31585\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;} {\fbiminor\f31581\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fbiminor\f31582\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fbiminor\f31583\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}
{\fbiminor\f31586\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}}{\colortbl;\red0\green0\blue0;\red0\green0\blue255;\red0\green255\blue255;\red0\green255\blue0;\red255\green0\blue255;\red255\green0\blue0;\red255\green255\blue0; {\fbiminor\f31584\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fbiminor\f31585\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fbiminor\f31586\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}}
\red255\green255\blue255;\red0\green0\blue128;\red0\green128\blue128;\red0\green128\blue0;\red128\green0\blue128;\red128\green0\blue0;\red128\green128\blue0;\red128\green128\blue128;\red192\green192\blue192; {\colortbl;\red0\green0\blue0;\red0\green0\blue255;\red0\green255\blue255;\red0\green255\blue0;\red255\green0\blue255;\red255\green0\blue0;\red255\green255\blue0;\red255\green255\blue255;\red0\green0\blue128;\red0\green128\blue128;\red0\green128\blue0;
\chyperlink\ctint255\cshade255\red5\green99\blue193;\cfollowedhyperlink\ctint255\cshade255\red149\green79\blue114;}{\*\defchp \f31506\fs22\lang1045\langfe1033\langfenp1033 }{\*\defpap \ql \li0\ri0\sa160\sl259\slmult1 \red128\green0\blue128;\red128\green0\blue0;\red128\green128\blue0;\red128\green128\blue128;\red192\green192\blue192;\chyperlink\ctint255\cshade255\red5\green99\blue193;\cfollowedhyperlink\ctint255\cshade255\red149\green79\blue114;}{\*\defchp
\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 }\noqfpromote {\stylesheet{\ql \li0\ri0\sa160\sl259\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af0\afs22\alang1025 \ltrch\fcs0 \f31506\fs22\lang1045\langfe1033\langfenp1033 }{\*\defpap \ql \li0\ri0\sa160\sl259\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 }\noqfpromote {\stylesheet{\ql \li0\ri0\sa160\sl259\slmult1
\f31506\fs22\lang1045\langfe1033\cgrid\langnp1045\langfenp1033 \snext0 \sqformat \spriority0 Normal;}{\*\cs10 \additive \ssemihidden \sunhideused \spriority1 Default Paragraph Font;}{\* \widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af0\afs22\alang1025 \ltrch\fcs0 \f31506\fs22\lang1045\langfe1033\cgrid\langnp1045\langfenp1033 \snext0 \sqformat \spriority0 Normal;}{\*\cs10 \additive
\ssemihidden \sunhideused \spriority1 Default Paragraph Font;}{\*
\ts11\tsrowd\trftsWidthB3\trpaddl108\trpaddr108\trpaddfl3\trpaddft3\trpaddfb3\trpaddfr3\trcbpat1\trcfpat1\tblind0\tblindtype3\tsvertalt\tsbrdrt\tsbrdrl\tsbrdrb\tsbrdrr\tsbrdrdgl\tsbrdrdgr\tsbrdrh\tsbrdrv \ql \li0\ri0\sa160\sl259\slmult1 \ts11\tsrowd\trftsWidthB3\trpaddl108\trpaddr108\trpaddfl3\trpaddft3\trpaddfb3\trpaddfr3\trcbpat1\trcfpat1\tblind0\tblindtype3\tsvertalt\tsbrdrt\tsbrdrl\tsbrdrb\tsbrdrr\tsbrdrdgl\tsbrdrdgr\tsbrdrh\tsbrdrv \ql \li0\ri0\sa160\sl259\slmult1
\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af31506\afs22\alang1025 \ltrch\fcs0 \f31506\fs22\lang1045\langfe1033\cgrid\langnp1045\langfenp1033 \snext11 \ssemihidden \sunhideused Normal Table;}{\*\cs15 \additive \widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af31506\afs22\alang1025 \ltrch\fcs0 \f31506\fs22\lang1045\langfe1033\cgrid\langnp1045\langfenp1033 \snext11 \ssemihidden \sunhideused Normal Table;}{\*\cs15 \additive
\rtlch\fcs1 \af0 \ltrch\fcs0 \ul\cf17 \sbasedon10 \sunhideused \styrsid3562894 Hyperlink;}{\*\cs16 \additive \rtlch\fcs1 \af0 \ltrch\fcs0 \ul\cf18 \sbasedon10 \ssemihidden \sunhideused \styrsid7678248 FollowedHyperlink;}}{\*\rsidtbl \rsid1081196 \rtlch\fcs1 \af0 \ltrch\fcs0 \ul\cf17 \sbasedon10 \sunhideused \styrsid3562894 Hyperlink;}{\*\cs16 \additive \rtlch\fcs1 \af0 \ltrch\fcs0 \ul\cf18 \sbasedon10 \ssemihidden \sunhideused \styrsid7678248 FollowedHyperlink;}}{\*\rsidtbl \rsid1081196
\rsid3146412\rsid3562894\rsid5731975\rsid7678248\rsid9265883\rsid11107340\rsid12600926\rsid13187577}{\mmathPr\mmathFont34\mbrkBin0\mbrkBinSub0\msmallFrac0\mdispDef1\mlMargin0\mrMargin0\mdefJc1\mwrapIndent1440\mintLim0\mnaryLim1}{\info \rsid3146412\rsid3562894\rsid5731975\rsid7678248\rsid9265883\rsid11107340\rsid11629590\rsid12600926\rsid13187577}{\mmathPr\mmathFont34\mbrkBin0\mbrkBinSub0\msmallFrac0\mdispDef1\mlMargin0\mrMargin0\mdefJc1\mwrapIndent1440\mintLim0\mnaryLim1}{\info
{\author Pawe\'b3 Jastrz\'eabski}{\operator Pawe\'b3 Jastrz\'eabski}{\creatim\yr2013\mo10\dy29\hr15\min17}{\revtim\yr2013\mo10\dy29\hr15\min28}{\version8}{\edmins8}{\nofpages1}{\nofwords33}{\nofchars200}{\nofcharsws232}{\vern57435}}{\*\xmlnstbl {\xmlns1 h {\author Pawe\'b3 Jastrz\'eabski}{\operator Pawe\'b3 Jastrz\'eabski}{\creatim\yr2013\mo10\dy29\hr15\min17}{\revtim\yr2017\mo8\dy20\hr17\min40}{\version9}{\edmins8}{\nofpages1}{\nofwords33}{\nofchars201}{\nofcharsws233}{\vern39}}{\*\xmlnstbl {\xmlns1 http:
ttp://schemas.microsoft.com/office/word/2003/wordml}}\paperw11906\paperh16838\margl1417\margr1417\margt1417\margb1417\gutter0\ltrsect //schemas.microsoft.com/office/word/2003/wordml}}\paperw11906\paperh16838\margl1417\margr1417\margt1417\margb1417\gutter0\ltrsect
\deftab708\widowctrl\ftnbj\aenddoc\hyphhotz425\trackmoves0\trackformatting1\donotembedsysfont1\relyonvml0\donotembedlingdata0\grfdocevents0\validatexml1\showplaceholdtext0\ignoremixedcontent0\saveinvalidxml0 \deftab708\widowctrl\ftnbj\aenddoc\hyphhotz425\trackmoves0\trackformatting1\donotembedsysfont1\relyonvml0\donotembedlingdata0\grfdocevents0\validatexml1\showplaceholdtext0\ignoremixedcontent0\saveinvalidxml0
\showxmlerrors1\noxlattoyen\expshrtn\noultrlspc\dntblnsbdb\nospaceforul\formshade\horzdoc\dgmargin\dghspace180\dgvspace180\dghorigin1417\dgvorigin1417\dghshow1\dgvshow1 \showxmlerrors1\noxlattoyen\expshrtn\noultrlspc\dntblnsbdb\nospaceforul\formshade\horzdoc\dgmargin\dghspace180\dgvspace180\dghorigin1417\dgvorigin1417\dghshow1\dgvshow1
\jexpand\viewkind1\viewscale100\pgbrdrhead\pgbrdrfoot\splytwnine\ftnlytwnine\htmautsp\nolnhtadjtbl\useltbaln\alntblind\lytcalctblwd\lyttblrtgr\lnbrkrule\nobrkwrptbl\snaptogridincell\allowfieldendsel\wrppunct \jexpand\viewkind1\viewscale100\pgbrdrhead\pgbrdrfoot\splytwnine\ftnlytwnine\htmautsp\nolnhtadjtbl\useltbaln\alntblind\lytcalctblwd\lyttblrtgr\lnbrkrule\nobrkwrptbl\snaptogridincell\allowfieldendsel\wrppunct
@@ -52,12 +54,13 @@ ttp://schemas.microsoft.com/office/word/2003/wordml}}\paperw11906\paperh16838\ma
\b\fs52\cf6\lang2057\langfe1033\langnp2057\insrsid13187577\charrsid3562894 \b\fs52\cf6\lang2057\langfe1033\langnp2057\insrsid13187577\charrsid3562894
\par }{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \fs28\lang2057\langfe1033\langnp2057\insrsid1081196 Creation of}{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \fs28\lang2057\langfe1033\langnp2057\insrsid3562894\charrsid12600926 }{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \par }{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \fs28\lang2057\langfe1033\langnp2057\insrsid1081196 Creation of}{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \fs28\lang2057\langfe1033\langnp2057\insrsid3562894\charrsid12600926 }{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0
\b\fs28\lang2057\langfe1033\langnp2057\insrsid3562894\charrsid12600926 MOBI}{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \fs28\lang2057\langfe1033\langnp2057\insrsid3562894\charrsid12600926 files }{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \b\fs28\lang2057\langfe1033\langnp2057\insrsid3562894\charrsid12600926 MOBI}{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \fs28\lang2057\langfe1033\langnp2057\insrsid3562894\charrsid12600926 files }{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0
\fs28\lang2057\langfe1033\langnp2057\insrsid5731975\charrsid12600926 require additional software.}{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \fs28\lang2057\langfe1033\langnp2057\insrsid3562894\charrsid12600926 \fs28\lang2057\langfe1033\langnp2057\insrsid5731975\charrsid12600926 require}{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \fs28\lang2057\langfe1033\langnp2057\insrsid11629590 s}{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0
\fs28\lang2057\langfe1033\langnp2057\insrsid5731975\charrsid12600926 additional software.}{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \fs28\lang2057\langfe1033\langnp2057\insrsid3562894\charrsid12600926
\par }{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \b\fs28\lang2057\langfe1033\langnp2057\insrsid3562894\charrsid12600926 Please download: }{\field\flddirty{\*\fldinst {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \par }{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \b\fs28\lang2057\langfe1033\langnp2057\insrsid3562894\charrsid12600926 Please download: }{\field\flddirty{\*\fldinst {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0
\b\fs28\lang2057\langfe1033\langnp2057\insrsid3562894\charrsid12600926 HYPERLINK "http://www.amazon.com/gp/feature.html?ie=UTF8&docId=1000765211" }{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \b\fs28\lang2057\langfe1033\langnp2057\insrsid3562894\charrsid12600926 \b\fs28\lang2057\langfe1033\langnp2057\insrsid3562894\charrsid12600926 HYPERLINK "http://www.amazon.com/gp/feature.html?ie=UTF8&docId=1000765211" }{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \b\fs28\lang2057\langfe1033\langnp2057\insrsid3562894\charrsid12600926
{\*\datafield {\*\datafield
00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90b9600000068007400740070003a002f002f007700770077002e0061006d0061007a006f006e002e0063006f006d002f00670070002f0066006500610074007500720065002e00680074006d006c003f00690065003d00 00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90b9600000068007400740070003a002f002f007700770077002e0061006d0061007a006f006e002e0063006f006d002f00670070002f0066006500610074007500720065002e00680074006d006c003f00690065003d00
5500540046003800260064006f006300490064003d0031003000300030003700360035003200310031000000795881f43b1d7f48af2c825dc485276300000000a5ab000000}}}{\fldrslt {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 5500540046003800260064006f006300490064003d0031003000300030003700360035003200310031000000795881f43b1d7f48af2c825dc485276300000000a5ab00000000}}}{\fldrslt {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0
\cs15\b\fs28\ul\cf17\lang2057\langfe1033\langnp2057\insrsid3562894\charrsid12600926 KindleGen}}}\sectd \ltrsect\linex0\headery708\footery708\colsx708\endnhere\sectlinegrid360\sectdefaultcl\sftnbj {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \cs15\b\fs28\ul\cf17\lang2057\langfe1033\langnp2057\insrsid3562894\charrsid12600926 KindleGen}}}\sectd \ltrsect\linex0\headery708\footery708\colsx708\endnhere\sectlinegrid360\sectdefaultcl\sftnbj {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0
\b\fs28\lang2057\langfe1033\langnp2057\insrsid3562894\charrsid12600926 \b\fs28\lang2057\langfe1033\langnp2057\insrsid3562894\charrsid12600926
\par }{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \fs28\lang2057\langfe1033\langnp2057\insrsid3562894\charrsid12600926 And place }{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \i\fs28\lang2057\langfe1033\langnp2057\insrsid5731975\charrsid12600926 kindlegen.exe}{\rtlch\fcs1 \par }{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \fs28\lang2057\langfe1033\langnp2057\insrsid3562894\charrsid12600926 And place }{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \i\fs28\lang2057\langfe1033\langnp2057\insrsid5731975\charrsid12600926 kindlegen.exe}{\rtlch\fcs1
@@ -119,7 +122,7 @@ d40384e4350d363f2451eced0dae2c082e8761be9969bb979dc9136332de3168aa1a083ae995719a
617020786d6c6e733a613d22687474703a2f2f736368656d61732e6f70656e786d6c666f726d6174732e6f72672f64726177696e676d6c2f323030362f6d6169 617020786d6c6e733a613d22687474703a2f2f736368656d61732e6f70656e786d6c666f726d6174732e6f72672f64726177696e676d6c2f323030362f6d6169
6e22206267313d226c743122207478313d22646b3122206267323d226c743222207478323d22646b322220616363656e74313d22616363656e74312220616363 6e22206267313d226c743122207478313d22646b3122206267323d226c743222207478323d22646b322220616363656e74313d22616363656e74312220616363
656e74323d22616363656e74322220616363656e74333d22616363656e74332220616363656e74343d22616363656e74342220616363656e74353d22616363656e74352220616363656e74363d22616363656e74362220686c696e6b3d22686c696e6b2220666f6c486c696e6b3d22666f6c486c696e6b222f3e} 656e74323d22616363656e74322220616363656e74333d22616363656e74332220616363656e74343d22616363656e74342220616363656e74353d22616363656e74352220616363656e74363d22616363656e74362220686c696e6b3d22686c696e6b2220666f6c486c696e6b3d22666f6c486c696e6b222f3e}
{\*\latentstyles\lsdstimax371\lsdlockeddef0\lsdsemihiddendef0\lsdunhideuseddef0\lsdqformatdef0\lsdprioritydef99{\lsdlockedexcept \lsdqformat1 \lsdpriority0 \lsdlocked0 Normal;\lsdqformat1 \lsdpriority9 \lsdlocked0 heading 1; {\*\latentstyles\lsdstimax375\lsdlockeddef0\lsdsemihiddendef0\lsdunhideuseddef0\lsdqformatdef0\lsdprioritydef99{\lsdlockedexcept \lsdqformat1 \lsdpriority0 \lsdlocked0 Normal;\lsdqformat1 \lsdpriority9 \lsdlocked0 heading 1;
\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 2;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 3;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 4; \lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 2;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 3;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 4;
\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 5;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 6;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 7; \lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 5;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 6;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 7;
\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 8;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 9;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 1; \lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 8;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 9;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 1;
@@ -196,7 +199,8 @@ d40384e4350d363f2451eced0dae2c082e8761be9969bb979dc9136332de3168aa1a083ae995719a
\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 4;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 5;\lsdpriority47 \lsdlocked0 List Table 2 Accent 5;\lsdpriority48 \lsdlocked0 List Table 3 Accent 5; \lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 4;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 5;\lsdpriority47 \lsdlocked0 List Table 2 Accent 5;\lsdpriority48 \lsdlocked0 List Table 3 Accent 5;
\lsdpriority49 \lsdlocked0 List Table 4 Accent 5;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 5;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 5;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 5; \lsdpriority49 \lsdlocked0 List Table 4 Accent 5;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 5;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 5;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 5;
\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 6;\lsdpriority47 \lsdlocked0 List Table 2 Accent 6;\lsdpriority48 \lsdlocked0 List Table 3 Accent 6;\lsdpriority49 \lsdlocked0 List Table 4 Accent 6; \lsdpriority46 \lsdlocked0 List Table 1 Light Accent 6;\lsdpriority47 \lsdlocked0 List Table 2 Accent 6;\lsdpriority48 \lsdlocked0 List Table 3 Accent 6;\lsdpriority49 \lsdlocked0 List Table 4 Accent 6;
\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 6;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 6;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 6;}}{\*\datastore 010500000200000018000000 \lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 6;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 6;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 6;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Mention;
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Smart Hyperlink;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Hashtag;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Unresolved Mention;}}{\*\datastore 010500000200000018000000
4d73786d6c322e534158584d4c5265616465722e362e30000000000000000000000e0000 4d73786d6c322e534158584d4c5265616465722e362e30000000000000000000000e0000
d0cf11e0a1b11ae1000000000000000000000000000000003e000300feff0900060000000000000000000000010000000100000000000000001000000200000001000000feffffff0000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff d0cf11e0a1b11ae1000000000000000000000000000000003e000300feff0900060000000000000000000000010000000100000000000000001000000200000001000000feffffff0000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
@@ -206,24 +210,24 @@ fffffffffffffffffdffffff04000000feffffff05000000fefffffffeffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffff52006f006f007400200045006e00740072007900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016000500ffffffffffffffff010000000c6ad98892f1d411a65f0040963251e50000000000000000000000006069 ffffffffffffffffffffffffffffffff52006f006f007400200045006e00740072007900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016000500ffffffffffffffff010000000c6ad98892f1d411a65f0040963251e5000000000000000000000000b00f
e214b3d4ce010300000080020000000000004d0073006f004400610074006100530074006f0072006500000000000000000000000000000000000000000000000000000000000000000000000000000000001a000101ffffffffffffffff0200000000000000000000000000000000000000000000006069e214b3d4ce01 9da8ca19d30103000000c0020000000000004d0073006f004400610074006100530074006f0072006500000000000000000000000000000000000000000000000000000000000000000000000000000000001a000101ffffffffffffffff020000000000000000000000000000000000000000000000b00f9da8ca19d301
6069e214b3d4ce010000000000000000000000003500d900ca00dd00ce004400cc00c8005a0045004700c400cd0057004900c500d400c900cb00ce00570051003d003d000000000000000000000000000000000032000101ffffffffffffffff0300000000000000000000000000000000000000000000006069e214b3d4 b00f9da8ca19d301000000000000000000000000ca0041004300c300d300d300c70058004d00d4003000c9004d00c200590043003100320055004a00300051003d003d000000000000000000000000000000000032000101ffffffffffffffff030000000000000000000000000000000000000000000000b00f9da8ca19
ce016069e214b3d4ce010000000000000000000000004900740065006d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000201ffffffff04000000ffffffff000000000000000000000000000000000000000000000000 d301b00f9da8ca19d3010000000000000000000000004900740065006d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000201ffffffff04000000ffffffff000000000000000000000000000000000000000000000000
00000000000000000000000000000000fc00000000000000010000000200000003000000feffffff0500000006000000070000000800000009000000feffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 00000000000000000000000000000000320100000000000001000000020000000300000004000000feffffff060000000700000008000000090000000a000000feffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff3c623a536f75726365732053656c65637465645374796c653d225c415041536978746845646974696f6e4f66666963654f6e6c696e652e78736c22205374796c654e616d653d22415041222056657273696f6e3d22362220786d6c6e733a ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff3c3f786d6c2076657273696f6e3d22312e302220656e636f64696e673d225554462d3822207374616e64616c6f6e653d226e6f223f3e3c623a536f75726365732053656c65637465645374796c653d225c41504153697874684564697469
623d22687474703a2f2f736368656d61732e6f70656e786d6c666f726d6174732e6f72672f6f6666696365446f63756d656e742f323030362f6269626c696f6772617068792220786d6c6e733d22687474703a2f2f736368656d61732e6f70656e786d6c666f726d6174732e6f72672f6f6666696365446f63756d656e74 6f6e4f66666963654f6e6c696e652e78736c22205374796c654e616d653d22415041222056657273696f6e3d22362220786d6c6e733a623d22687474703a2f2f736368656d61732e6f70656e786d6c666f726d6174732e6f72672f6f6666696365446f63756d656e742f323030362f6269626c696f677261706879222078
2f323030362f6269626c696f677261706879223e3c2f623a536f75726365733e000000003c3f786d6c2076657273696f6e3d22312e302220656e636f64696e673d225554462d3822207374616e64616c6f6e653d226e6f223f3e0d0a3c64733a6461746173746f72654974656d2064733a6974656d49443d227b42384244 6d6c6e733d22687474703a2f2f736368656d61732e6f70656e786d6c666f726d6174732e6f72672f6f6666696365446f63756d656e742f323030362f6269626c696f677261706879223e3c2f623a536f75726365733e00000000000000000000000000003c3f786d6c2076657273696f6e3d22312e302220656e636f6469
394137462d323833422d343136342d413442352d3632323544323941454535397d2220786d6c6e733a64733d22687474703a2f2f736368656d61732e6f70656e786d6c666f726d6174732e6f72672f6f6666696365446f63756d656e742f323030362f637573746f6d586d6c223e3c64733a736368656d61526566733e3c 6e673d225554462d3822207374616e64616c6f6e653d226e6f223f3e0d0a3c64733a6461746173746f72654974656d2064733a6974656d49443d227b43464133303041382d443733392d343633332d413933322d3236303236444335303936397d2220786d6c6e733a64733d22687474703a2f2f736368656d61732e6f70
64733a736368656d615265662064733a7572693d22687474703a2f2f736368656d61732e6f70656e500072006f007000650072007400690065007300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016000200ffffffffffffffffffffffff000000000000 656e786d6c666f726d6174732e6f72672f6f6666696365446f63756d656e742f323030362f637573500072006f007000650072007400690065007300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016000200ffffffffffffffffffffffff000000000000
0000000000000000000000000000000000000000000000000000000000000400000055010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff00000000 0000000000000000000000000000000000000000000000000000000000000500000055010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff00000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff0000 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff0000
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000786d6c666f726d6174732e6f72672f6f6666696365446f63756d656e742f323030362f6269626c696f677261706879222f3e3c2f64733a736368656d61526566733e3c2f64733a6461746173746f 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000746f6d586d6c223e3c64733a736368656d61526566733e3c64733a736368656d615265662064733a7572693d22687474703a2f2f736368656d61732e6f70656e786d6c666f726d6174732e6f7267
72654974656d3e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 2f6f6666696365446f63756d656e742f323030362f6269626c696f677261706879222f3e3c2f64733a736368656d61526566733e3c2f64733a6461746173746f72654974656d3e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000105000000000000}} 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000105000000000000}}

View File

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

5
requirements.txt Normal file
View File

@@ -0,0 +1,5 @@
PyQt5>=5.6.0
Pillow>=4.0.0
psutil>=5.0.0
python-slugify>=1.2.1
raven>=6.0.0

4
setup.bat Normal file
View File

@@ -0,0 +1,4 @@
@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

134
setup.py
View File

@@ -2,11 +2,11 @@
""" """
pip/pyinstaller build script for KCC. pip/pyinstaller build script for KCC.
Usage (Windows): Install as Python package:
py -3 setup.py build_binary python3 setup.py install
Usage (Linux/OS X): Create EXE/APP/DEB:
python3 setup.py build_binary or python3 setup.py build_binary --pyz python3 setup.py build_binary
""" """
import os import os
@@ -14,31 +14,28 @@ import sys
import shutil import shutil
import setuptools import setuptools
import distutils.cmd import distutils.cmd
from distutils.command.build import build from kindlecomicconverter import __version__
from kcc import __version__
NAME = 'KindleComicConverter' NAME = 'KindleComicConverter'
MAIN = 'kcc.py' MAIN = 'kcc.py'
VERSION = __version__ VERSION = __version__
OPTIONS = {}
class BuildBinaryCommand(distutils.cmd.Command): class BuildBinaryCommand(distutils.cmd.Command):
description = 'build binary release' description = 'build binary release'
user_options = [ user_options = []
('pyz', None, 'build PYZ package'),
]
def initialize_options(self): def initialize_options(self):
# noinspection PyAttributeOutsideInit pass
self.pyz = False
def finalize_options(self): def finalize_options(self):
pass pass
# noinspection PyShadowingNames
def run(self): def run(self):
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 --noupx 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') shutil.copy('other/osx/7za', 'dist/Kindle Comic Converter.app/Contents/Resources')
shutil.copy('other/osx/unrar', 'dist/Kindle Comic Converter.app/Contents/Resources') shutil.copy('other/osx/unrar', 'dist/Kindle Comic Converter.app/Contents/Resources')
shutil.copy('other/osx/Info.plist', 'dist/Kindle Comic Converter.app/Contents') shutil.copy('other/osx/Info.plist', 'dist/Kindle Comic Converter.app/Contents')
@@ -46,84 +43,37 @@ class BuildBinaryCommand(distutils.cmd.Command):
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/unrar', 0o777)
os.chmod('dist/Kindle Comic Converter.app/Contents/Resources/7za', 0o777) os.chmod('dist/Kindle Comic Converter.app/Contents/Resources/7za', 0o777)
if os.path.isfile('setup.sh'):
os.system('./setup.sh')
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.path.isfile('setup.bat'): if os.getenv('APPVEYOR'):
os.system('setup.bat') if len(VERSION) == 3:
VERSION = VERSION + '.0'
os.system('setup.bat ' + VERSION)
exit(0) exit(0)
else: else:
if self.pyz: os.system('pyinstaller -y -F kcc.py')
script = ''' os.system('mkdir -p dist/usr/bin dist/usr/share/applications dist/usr/share/doc/kindlecomicconverter '
cp kcc.py __main__.py 'dist/usr/share/kindlecomicconverter dist/usr/share/lintian/overrides')
zip kcc.zip __main__.py kcc/*.py os.system('mv dist/kcc dist/usr/bin')
echo "#!/usr/bin/env python3" > kcc-bin os.system('cp icons/comic2ebook.png dist/usr/share/kindlecomicconverter')
cat kcc.zip >> kcc-bin os.system('cp LICENSE.txt dist/usr/share/doc/kindlecomicconverter/copyright')
chmod +x kcc-bin os.system('cp other/linux/kindlecomicconverter.desktop dist/usr/share/applications')
os.system('cp other/linux/kindlecomicconverter dist/usr/share/lintian/overrides')
cp kcc-c2e.py __main__.py os.chdir('dist')
zip kcc-c2e.zip __main__.py kcc/*.py os.system('fpm -f -s dir -t deb -n kindlecomicconverter -v ' + VERSION +
echo "#!/usr/bin/env python3" > kcc-c2e-bin ' -m "Paweł Jastrzębski <pawelj@iosphe.re>" --license "ISC" '
cat kcc-c2e.zip >> kcc-c2e-bin '--description "$(printf "Comic and Manga converter for e-book '
chmod +x kcc-c2e-bin 'readers.\nThis app allows you to transform your PNG, JPG, GIF, '
'CBZ, CBR and CB7 files\ninto EPUB or MOBI format e-books.")" '
cp kcc-c2p.py __main__.py '--url "https://kcc.iosphe.re/" --deb-priority "optional" --vendor "" '
zip kcc-c2p.zip __main__.py kcc/*.py '--category "graphics" -d "unrar | unrar-free" -d "p7zip-full" -d "libc6" usr')
echo "#!/usr/bin/env python3" > kcc-c2p-bin exit(0)
cat kcc-c2p.zip >> kcc-c2p-bin
chmod +x kcc-c2p-bin
mkdir dist
tar --xform s:^.*/:: \
--xform s/LICENSE.txt/LICENSE/ \
--xform s/kcc-bin/kcc/ \
--xform s/kcc-c2p-bin/kcc-c2p/ \
--xform s/kcc-c2e-bin/kcc-c2e/ \
--xform s/comic2ebook/kcc/ \
-czf dist/KindleComicConverter_linux_''' + VERSION + '''.tar.gz \
kcc-bin kcc-c2e-bin kcc-c2p-bin LICENSE.txt README.md icons/comic2ebook.png
rm __main__.py kcc.zip kcc-c2e.zip kcc-c2p.zip kcc-bin kcc-c2e-bin kcc-c2p-bin
'''
os.system("bash -c '%s'" % script)
exit(0)
else:
os.system('docker run --rm -v ' + os.getcwd() + ':/app -e KCCVER=' + VERSION + ' acidweb/kcc')
exit(0)
class BuildCommand(build):
def run(self):
os.makedirs('build/_scripts/', exist_ok=True)
shutil.copyfile('kcc.py', 'build/_scripts/kcc')
shutil.copyfile('kcc-c2e.py', 'build/_scripts/kcc-c2e')
shutil.copyfile('kcc-c2p.py', 'build/_scripts/kcc-c2p')
# noinspection PyShadowingNames
OPTIONS = dict(
scripts=['build/_scripts/kcc',
'build/_scripts/kcc-c2e',
'build/_scripts/kcc-c2p'],
packages=['kcc'],
install_requires=[
'PyQt5>=5.6.0'
'Pillow>=3.2.0',
'psutil>=4.1.0',
'python-slugify>=1.2.0',
'raven>=5.13.0',
],
zip_safe=False,
)
if sys.version_info[1] < 5:
OPTIONS['install_requires'].append('scandir>=1.2.0')
build.run(self)
setuptools.setup( setuptools.setup(
cmdclass={ cmdclass={
'build_binary': BuildBinaryCommand, 'build_binary': BuildBinaryCommand,
'build': BuildCommand,
}, },
name=NAME, name=NAME,
version=VERSION, version=VERSION,
@@ -131,7 +81,25 @@ setuptools.setup(
author_email='ciromattia@gmail.com, pawelj@iosphe.re', author_email='ciromattia@gmail.com, pawelj@iosphe.re',
description='Comic and Manga converter for e-book readers.', description='Comic and Manga converter for e-book readers.',
license='ISC License (ISCL)', license='ISC License (ISCL)',
keywords='kindle comic mobipocket mobi cbz cbr manga', keywords=['kindle', 'kobo', 'comic', 'manga', 'mobi', 'epub', 'cbz'],
url='http://github.com/ciromattia/kcc', url='http://github.com/ciromattia/kcc',
**OPTIONS entry_points={
'console_scripts': [
'kcc-c2e = kindlecomicconverter.startup:startC2E',
'kcc-c2p = kindlecomicconverter.startup:startC2P',
],
'gui_scripts': [
'kcc = kindlecomicconverter.startup:start',
],
},
packages=['kindlecomicconverter'],
install_requires=[
'PyQt5>=5.6.0',
'Pillow>=4.0.0',
'psutil>=5.0.0',
'python-slugify>=1.2.1',
'raven>=6.0.0',
],
classifiers=[],
zip_safe=False,
) )