1
0
mirror of https://github.com/ciromattia/kcc synced 2025-12-20 21:22:05 +00:00

Merge pull request #231 from ciromattia/dev

5.3.1
This commit is contained in:
Paweł Jastrzębski
2017-03-17 11:11:00 +01:00
committed by GitHub
19 changed files with 279 additions and 331 deletions

1
.gitignore vendored
View File

@@ -15,3 +15,4 @@ setup.sh
kindlecomicconverter/sentry.py kindlecomicconverter/sentry.py
build/ build/
.python-version .python-version
KindleComicConverter.egg-info/

1
MANIFEST.in Normal file
View File

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

154
README.md
View File

@@ -1,4 +1,6 @@
# KCC # KCC
[![GitHub release](https://img.shields.io/github/release/ciromattia/kcc.svg)]() [![PyPI](https://img.shields.io/pypi/v/KindleComicConverter.svg)]() [![AUR](https://img.shields.io/aur/version/kcc.svg)]()
**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
@@ -17,11 +19,11 @@ 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:
@@ -29,20 +31,25 @@ You can find the latest released binary at the following links:
- **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.9+):** [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
@@ -163,35 +170,40 @@ The app relies and includes the following scripts:
* [Kobo Aura ONE](http://kcc.iosphe.re/Samples/Ubunchu-KoAO.kepub.epub) * [Kobo Aura ONE](http://kcc.iosphe.re/Samples/Ubunchu-KoAO.kepub.epub)
## CHANGELOG ## CHANGELOG
####5.3: #### 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 * Vastly improved output compatibility for non-Kindle devices
* Enabled old pinch zoom for Kindle devices * Enabled old pinch zoom for Kindle devices
* Re-enabled Panel View support for Kindle Keyboard * Re-enabled Panel View support for Kindle Keyboard
* Partially re-enabled OS X file association mechanism * Partially re-enabled OS X file association mechanism
* Fixed multiple smaller issues * Fixed multiple smaller issues
####5.2.1: #### 5.2.1:
* Improved directory parsing * Improved directory parsing
* Tweaked margin detection algorithm * Tweaked margin detection algorithm
* Improved error reporting * Improved error reporting
####5.2: #### 5.2:
* Added new Panel View options * Added new Panel View options
* Implemented new margin detection algorithm * Implemented new margin detection algorithm
* Removed HQ Panel View mode * Removed HQ Panel View mode
* Fixed multiple smaller issues * Fixed multiple smaller issues
####5.1.3: #### 5.1.3:
* Added Kobo Aura ONE profile * Added Kobo Aura ONE profile
* Fixed few small bugs * Fixed few small bugs
####5.1.2: #### 5.1.2:
* Fixed error reporting * Fixed error reporting
####5.1.1: #### 5.1.1:
* Fixed multiple GUI bugs * Fixed multiple GUI bugs
####5.1: #### 5.1:
* GUI now can be resized and high DPI support was somewhat improved * GUI now can be resized and high DPI support was somewhat improved
* Added profile for Kindle Oasis * Added profile for Kindle Oasis
* Implemented new error reporting mechanism * Implemented new error reporting mechanism
@@ -199,55 +211,55 @@ The app relies and includes the following scripts:
* Fixed permission issues on Windows * Fixed permission issues on Windows
* Fixed multiple smaller issues * Fixed multiple smaller issues
####5.0.1: #### 5.0.1:
* Fixed Panel View placement issues * Fixed Panel View placement issues
* Decreased application startup time * Decreased application startup time
* Fixed multiple smaller issues * Fixed multiple smaller issues
####5.0: #### 5.0:
* Major overhaul of internal mechanisms and GUI * Major overhaul of internal mechanisms and GUI
* Added cover upload feature * Added cover upload feature
* Tweaked Webtoon parsing mode * Tweaked Webtoon parsing mode
* Fixed multiple smaller issues * Fixed multiple smaller issues
* Migrated build enviroment to PyInstaller * Migrated build enviroment to PyInstaller
####4.6.5: #### 4.6.5:
* Fixed multiple Windows and OS X issues * Fixed multiple Windows and OS X issues
* Allowed Linux release to use older PyQT5 version * Allowed Linux release to use older PyQT5 version
####4.6.4: #### 4.6.4:
* Fixed multiple Windows specific problems * Fixed multiple Windows specific problems
* Improved error handling * Improved error handling
* Improved color detection algorithm * Improved color detection algorithm
* New, slimmer OS X release * New, slimmer OS X release
####4.6.3: #### 4.6.3:
* Implemented remote bug reporting * Implemented remote bug reporting
* Minor bug fixes and GUI tweaks * Minor bug fixes and GUI tweaks
####4.6.2: #### 4.6.2:
* Fixed critical MOBI header bug * Fixed critical MOBI header bug
* Fixed metadata encoding error * Fixed metadata encoding error
####4.6.1: #### 4.6.1:
* Fixed KEPUB TOC generator * Fixed KEPUB TOC generator
* Added warning about too small input files * Added warning about too small input files
* ComicRack Summary metadata field is now parsed * ComicRack Summary metadata field is now parsed
* Small tweaks of KEPUB output * Small tweaks of KEPUB output
####4.6: #### 4.6:
* KEPUB is now default output for all Kobo profiles * KEPUB is now default output for all Kobo profiles
* EPUB output now produce fully valid EPUB 3.0.1 * EPUB output now produce fully valid EPUB 3.0.1
* Added profile for Kindle Paperwhite 3 * Added profile for Kindle Paperwhite 3
* Dropped official support of all Kindle Fire models and Kindle for Android * Dropped official support of all Kindle Fire models and Kindle for Android
* Other minor tweaks * Other minor tweaks
####4.5.1: #### 4.5.1:
* Added Kobo Glo HD profile * Added Kobo Glo HD profile
* Fixed RAR/CBR parsing anomalies * Fixed RAR/CBR parsing anomalies
* Minor bug fixes and tweaks * Minor bug fixes and tweaks
####4.5: #### 4.5:
* Added simple ComicRack metadata editor * Added simple ComicRack metadata editor
* Re-enabled Manga Cover Database support * Re-enabled Manga Cover Database support
* ComicRack bookmarks are now parsed * ComicRack bookmarks are now parsed
@@ -256,61 +268,61 @@ The app relies and includes the following scripts:
* Fixed sorting anomalies * Fixed sorting anomalies
* Improved conversion speed * Improved conversion speed
####4.4.1: #### 4.4.1:
* Fixed problems with OSX GUI * Fixed problems with OSX GUI
* Added one missing DLL to Windows installer * Added one missing DLL to Windows installer
####4.4: #### 4.4:
* Improved speed and quality of conversion * Improved speed and quality of conversion
* Added RAR5 support * Added RAR5 support
* Dropped BMP and TIFF support * Dropped BMP and TIFF support
* Fixed some WebToon mode bugs * Fixed some WebToon mode bugs
* Fixed CBR parsing on OSX * Fixed CBR parsing on OSX
####4.3.1: #### 4.3.1:
* Fixed Kindle Voyage profile * Fixed Kindle Voyage profile
* Fixed some bugs in OS X release * Fixed some bugs in OS X release
* CLI version now support multiple input files at once * CLI version now support multiple input files at once
* Disabled MCB support * Disabled MCB support
* Other minor tweaks * Other minor tweaks
####4.3: #### 4.3:
* Added profiles for Kindle Voyage and Kobo Aura H2O * Added profiles for Kindle Voyage and Kobo Aura H2O
* Added missing features to CLI version * Added missing features to CLI version
* Other minor bug fixes * Other minor bug fixes
####4.2.1: #### 4.2.1:
* Improved margin color detection * Improved margin color detection
* Fixed random crashes of MOBI processing step * Fixed random crashes of MOBI processing step
* Fixed resizing problems in high quality mode * Fixed resizing problems in high quality mode
* Fixed some MCD support bugs * Fixed some MCD support bugs
* Default output format for Kindle DX is now CBZ * Default output format for Kindle DX is now CBZ
####4.2: #### 4.2:
* Added [Manga Cover Database](http://manga.joentjuh.nl/) support * Added [Manga Cover Database](http://manga.joentjuh.nl/) support
* Officially dropped Windows XP support * Officially dropped Windows XP support
* Fixed _Other_ profile * Fixed _Other_ profile
* Fixed problems with page order on stock KOBO CBZ reader * Fixed problems with page order on stock KOBO CBZ reader
* Many other small bug fixes and tweaks * Many other small bug fixes and tweaks
####4.1: #### 4.1:
* Thanks to code contributed by Kevin Hendricks speed of MOBI creation was greatly increased * Thanks to code contributed by Kevin Hendricks speed of MOBI creation was greatly increased
* Improved performance on Windows * Improved performance on Windows
* Improved MOBI splitting and changed maximal size of output file * Improved MOBI splitting and changed maximal size of output file
* Fixed _No optimization_ mode * Fixed _No optimization_ mode
* Multiple small tweaks nad minor bug fixes * Multiple small tweaks nad minor bug fixes
####4.0.2: #### 4.0.2:
* Fixed some Windows and OSX specific bugs * Fixed some Windows and OSX specific bugs
* Fixed problem with marigns when using HQ mode * Fixed problem with marigns when using HQ mode
####4.0.1: #### 4.0.1:
* Fixed file lock problems that plagued some Windows users * Fixed file lock problems that plagued some Windows users
* Fixed content server failing to start on Windows * Fixed content server failing to start on Windows
* Improved performance of WebToon splitter * Improved performance of WebToon splitter
* Tweaked margin color detection * Tweaked margin color detection
####4.0: #### 4.0:
* KCC now use Python 3.3 and Qt 5.2 * KCC now use Python 3.3 and Qt 5.2
* Full UTF-8 awareness * Full UTF-8 awareness
* CBZ output now support Manga mode * CBZ output now support Manga mode
@@ -322,13 +334,13 @@ The app relies and includes the following scripts:
* Fixed OSX file association support * Fixed OSX file association support
* Many extensive internal changes and tweaks * Many extensive internal changes and tweaks
####3.7.2: #### 3.7.2:
* Fixed problems with HQ mode * Fixed problems with HQ mode
####3.7.1: #### 3.7.1:
* Hotfixed Kobo profiles * Hotfixed Kobo profiles
####3.7: #### 3.7:
* Added profiles for KOBO devices * Added profiles for KOBO devices
* Improved Panel View support * Improved Panel View support
* Improved WebToon splitter * Improved WebToon splitter
@@ -337,14 +349,14 @@ The app relies and includes the following scripts:
* Fixed stretching option * Fixed stretching option
* GUI tweaks and minor bugfixes * GUI tweaks and minor bugfixes
####3.6.2: #### 3.6.2:
* Fixed previous PNG output fix * Fixed previous PNG output fix
* Fixed Panel View anomalies * Fixed Panel View anomalies
####3.6.1: #### 3.6.1:
* Fixed PNG output * Fixed PNG output
####3.6: #### 3.6:
* Increased quality of Panel View zoom * Increased quality of Panel View zoom
* Creation of multipart MOBI output is now faster on machines with 4GB+ RAM * Creation of multipart MOBI output is now faster on machines with 4GB+ RAM
* Automatic gamma correction now distinguishes color and grayscale images * Automatic gamma correction now distinguishes color and grayscale images
@@ -355,13 +367,13 @@ The app relies and includes the following scripts:
* Fixed Kindle Fire HDX 7" output * Fixed Kindle Fire HDX 7" output
* Increased target resolution for Kindle DX/DXG CBZ output * Increased target resolution for Kindle DX/DXG CBZ output
####3.5: #### 3.5:
* Added simple content server - Converted files can be now delivered wireless * Added simple content server - Converted files can be now delivered wireless
* Added proper Windows installer * Added proper Windows installer
* Improved multiprocessing speed * Improved multiprocessing speed
* GUI tweaks and minor bug fixes * GUI tweaks and minor bug fixes
####3.4: #### 3.4:
* Improved PNG output * Improved PNG output
* Increased quality of upscaling * Increased quality of upscaling
* Added support of file association - KCC can now open CBZ, CBR, CB7, ZIP, RAR, 7Z and PDF files directly * Added support of file association - KCC can now open CBZ, CBR, CB7, ZIP, RAR, 7Z and PDF files directly
@@ -370,7 +382,7 @@ The app relies and includes the following scripts:
* Merged DX and DXG profiles * Merged DX and DXG profiles
* Many other minor bug fixes and GUI tweaks * Many other minor bug fixes and GUI tweaks
####3.3: #### 3.3:
* Margins are now automatically omitted in Panel View mode * Margins are now automatically omitted in Panel View mode
* Margin color fill is now autodetected * Margin color fill is now autodetected
* Created MOBI files are not longer marked as _Personal_ on newer Kindle models * Created MOBI files are not longer marked as _Personal_ on newer Kindle models
@@ -384,27 +396,27 @@ The app relies and includes the following scripts:
* Windows release is now bundled with UnRAR and 7za * Windows release is now bundled with UnRAR and 7za
* Small GUI tweaks * Small GUI tweaks
####3.2: #### 3.2:
* Too big EPUB files are now splitted before conversion to MOBI * Too big EPUB files are now splitted before conversion to MOBI
* Added experimental parser of manga webtoons * Added experimental parser of manga webtoons
* Improved error handling * Improved error handling
####3.2.1: #### 3.2.1:
* Hotfixed crash occurring on OS with Russian locale * Hotfixed crash occurring on OS with Russian locale
####3.1: #### 3.1:
* Added profile: Kindle for Android * Added profile: Kindle for Android
* Add file/directory dialogs now support multiselect * Add file/directory dialogs now support multiselect
* Many small fixes and tweaks * Many small fixes and tweaks
####3.0: #### 3.0:
* New QT GUI * New QT GUI
* Merge with AWKCC * Merge with AWKCC
* Added ultra quality mode * Added ultra quality mode
* Added support for custom width/height * Added support for custom width/height
* Added option to disable color conversion * Added option to disable color conversion
####2.10: #### 2.10:
* Multiprocessing support * Multiprocessing support
* Kindle Fire support (color EPUB/MOBI) * Kindle Fire support (color EPUB/MOBI)
* Panel View support for horizontal content * Panel View support for horizontal content
@@ -412,14 +424,14 @@ The app relies and includes the following scripts:
* Disabled cropping and page number cutting for blank pages * Disabled cropping and page number cutting for blank pages
* Fixed some slugify issues with specific file naming conventions (#50, #51) * Fixed some slugify issues with specific file naming conventions (#50, #51)
####2.9 #### 2.9
* Added support for generating a plain CBZ (skipping all the EPUB/MOBI generation) (#45) * 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 * Prevent output file overwriting the source one: if a duplicate name is detected, append _kcc to the name
* Rarfile library updated to 2.6 * Rarfile library updated to 2.6
* Added GIF, TIFF and BMP to supported formats (#42) * Added GIF, TIFF and BMP to supported formats (#42)
* Filenames slugifications (#28, #31, #9, #8) * Filenames slugifications (#28, #31, #9, #8)
####2.8 #### 2.8
* Updated rarfile library * Updated rarfile library
* Panel View support + HQ support (#36) - new option: --nopanelviewhq * Panel View support + HQ support (#36) - new option: --nopanelviewhq
* Split profiles for K4NT and K4T * Split profiles for K4NT and K4T
@@ -428,7 +440,7 @@ The app relies and includes the following scripts:
* Added generic CSS file * Added generic CSS file
* Optimized archive extraction for zip/rar files (#40) * Optimized archive extraction for zip/rar files (#40)
####2.7 #### 2.7
* Lots of GUI improvements (#27, #13) * Lots of GUI improvements (#27, #13)
* Added gamma support within --gamma option (defaults to profile-specified gamma) (#26, #27) * Added gamma support within --gamma option (defaults to profile-specified gamma) (#26, #27)
* Added --nodithering option to prevent dithering optimizations (#27) * Added --nodithering option to prevent dithering optimizations (#27)
@@ -439,57 +451,57 @@ The app relies and includes the following scripts:
* Get filetype from magic number (#14) * Get filetype from magic number (#14)
* PDF conversion works again * PDF conversion works again
####2.6 #### 2.6
* Added --rotate option to rotate landscape images instead of splitting them (#16, #24) * Added --rotate option to rotate landscape images instead of splitting them (#16, #24)
* Added --output option to customize EPUB output dir/file (#22) * 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) * Add rendition:layout and rendition:orientation EPUB meta tags (supported by new kindlegen 2.8)
* Fixed natural sorting for files (#18) * Fixed natural sorting for files (#18)
####2.5 #### 2.5
* Added --black-borders option to set added borders black when page's ratio is not the device's one (#11). * 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) * Fixes EPUB containing zipped itself (#10)
####2.4 #### 2.4
* Use temporary directory as workdir (fixes converting from external volumes and zipfiles renaming) * Use temporary directory as workdir (fixes converting from external volumes and zipfiles renaming)
* Fixed "add folders" from GUI. * Fixed "add folders" from GUI.
####2.3 #### 2.3
* Fixed win32 EPUB generation, folder handling, filenames with spaces and subfolders * Fixed win32 EPUB generation, folder handling, filenames with spaces and subfolders
####2.2: #### 2.2:
* Added (valid!) EPUB 2.0 output * Added (valid!) EPUB 2.0 output
* Rename .zip files to .cbz to avoid overwriting * Rename .zip files to .cbz to avoid overwriting
####2.1 #### 2.1
* Added basic error reporting * Added basic error reporting
####2.0 #### 2.0
* GUI! AppleScript is gone and Tk is used to provide cross-platform GUI support. * GUI! AppleScript is gone and Tk is used to provide cross-platform GUI support.
####1.5 #### 1.5
* Added subfolder support for multiple chapters. * Added subfolder support for multiple chapters.
####1.4.1 #### 1.4.1
* Fixed a serious bug on resizing when img ratio was bigger than device one * Fixed a serious bug on resizing when img ratio was bigger than device one
####1.4 #### 1.4
* Added some options for controlling image optimization * Added some options for controlling image optimization
* Further optimization (ImageOps, page numbering cut, autocontrast) * Further optimization (ImageOps, page numbering cut, autocontrast)
####1.3 #### 1.3
* Fixed an issue in OPF generation for device resolution * Fixed an issue in OPF generation for device resolution
* Reworked options system (call with -h option to get the inline help) * Reworked options system (call with -h option to get the inline help)
####1.2 #### 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! * 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 #### 1.1.1
* Added support for CBZ/CBR files in Kindle Comic Converter * Added support for CBZ/CBR files in Kindle Comic Converter
####1.1 #### 1.1
* Added support for CBZ/CBR files in comic2ebook.py * Added support for CBZ/CBR files in comic2ebook.py
####1.0 #### 1.0
* Initial version * Initial version
## PRIVACY ## PRIVACY

View File

@@ -1,14 +1,11 @@
# acidweb/kcc # acidweb/kcc
FROM debian:jessie FROM debian:stretch
MAINTAINER Paweł Jastrzębski <pawelj@iosphe.re> MAINTAINER Paweł Jastrzębski <pawelj@iosphe.re>
ADD ./Build /Build ADD ./Build /Build
RUN printf "deb http://httpredir.debian.org/debian stretch main" > /etc/apt/sources.list.d/stretch.list
RUN printf "Package: *\nPin: release a=testing\nPin-Priority: 400\n" > /etc/apt/preferences.d/stretch.pref
RUN apt-get update && apt-get -y dist-upgrade RUN apt-get update && apt-get -y dist-upgrade
RUN apt-get -y install build-essential curl ruby ruby-dev libpng-dev libjpeg-dev RUN apt-get -y install build-essential curl ruby ruby-dev libpng-dev libjpeg-dev python3 python3-dev python3-pyqt5
RUN apt-get -y -t testing install python3 python3-dev python3-pyqt5
RUN curl https://bootstrap.pypa.io/get-pip.py | python3 RUN curl https://bootstrap.pypa.io/get-pip.py | python3
RUN apt-get clean -y && apt-get autoclean -y && apt-get autoremove -y && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* RUN apt-get clean -y && apt-get autoclean -y && apt-get autoremove -y && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*

View File

@@ -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 kindlecomicconverter.shared import dependencyCheck
dependencyCheck(2)
from multiprocessing import freeze_support from multiprocessing import freeze_support
from kindlecomicconverter import __version__ from kindlecomicconverter.startup import startC2E
from kindlecomicconverter.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

@@ -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 kindlecomicconverter.shared import dependencyCheck
dependencyCheck(1)
from multiprocessing import freeze_support from multiprocessing import freeze_support
from kindlecomicconverter import __version__ from kindlecomicconverter.startup import startC2P
from kindlecomicconverter.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.3.0" #define MyAppVersion "5.3.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"

20
kcc.py
View File

@@ -63,24 +63,10 @@ if getattr(sys, 'frozen', False):
except: except:
pass pass
from kindlecomicconverter.shared import dependencyCheck
dependencyCheck(3)
from multiprocessing import freeze_support from multiprocessing import freeze_support
from kindlecomicconverter 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

@@ -26,12 +26,12 @@ 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 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 raven import Client from raven import Client
from .shared import md5Checksum, HTMLStripper, sanitizeTrace, saferRemove from .shared import md5Checksum, HTMLStripper, sanitizeTrace
from . import __version__ from . import __version__
from . import comic2ebook from . import comic2ebook
from . import metadata from . import metadata
@@ -334,7 +334,7 @@ class WorkerThread(QtCore.QThread):
if 'outputPath' in locals(): if 'outputPath' in locals():
for item in outputPath: for item in outputPath:
if os.path.exists(item): if os.path.exists(item):
saferRemove(item) os.remove(item)
self.clean() self.clean()
return return
if not self.errors: if not self.errors:
@@ -361,9 +361,9 @@ class WorkerThread(QtCore.QThread):
if not self.conversionAlive: if not self.conversionAlive:
for item in outputPath: for item in outputPath:
if os.path.exists(item): if os.path.exists(item):
saferRemove(item) os.remove(item)
if os.path.exists(item.replace('.epub', '.mobi')): if os.path.exists(item.replace('.epub', '.mobi')):
saferRemove(item.replace('.epub', '.mobi')) os.remove(item.replace('.epub', '.mobi'))
self.clean() self.clean()
return return
if self.kindlegenErrorCode[0] == 0: if self.kindlegenErrorCode[0] == 0:
@@ -384,7 +384,7 @@ class WorkerThread(QtCore.QThread):
for item in outputPath: for item in outputPath:
GUI.progress.content = '' GUI.progress.content = ''
mobiPath = item.replace('.epub', '.mobi') mobiPath = item.replace('.epub', '.mobi')
saferRemove(mobiPath + '_toclean') os.remove(mobiPath + '_toclean')
if GUI.targetDirectory and GUI.targetDirectory != os.path.dirname(mobiPath): if GUI.targetDirectory and GUI.targetDirectory != os.path.dirname(mobiPath):
try: try:
move(mobiPath, GUI.targetDirectory) move(mobiPath, GUI.targetDirectory)
@@ -402,9 +402,9 @@ class WorkerThread(QtCore.QThread):
for item in outputPath: for item in outputPath:
mobiPath = item.replace('.epub', '.mobi') mobiPath = item.replace('.epub', '.mobi')
if os.path.exists(mobiPath): if os.path.exists(mobiPath):
saferRemove(mobiPath) os.remove(mobiPath)
if os.path.exists(mobiPath + '_toclean'): if os.path.exists(mobiPath + '_toclean'):
saferRemove(mobiPath + '_toclean') os.remove(mobiPath + '_toclean')
MW.addMessage.emit('Failed to process MOBI file!', 'error', False) MW.addMessage.emit('Failed to process MOBI file!', 'error', False)
MW.addTrayMessage.emit('Failed to process MOBI file!', 'Critical') MW.addTrayMessage.emit('Failed to process MOBI file!', 'Critical')
else: else:
@@ -412,9 +412,9 @@ class WorkerThread(QtCore.QThread):
epubSize = (os.path.getsize(self.kindlegenErrorCode[2])) // 1024 // 1024 epubSize = (os.path.getsize(self.kindlegenErrorCode[2])) // 1024 // 1024
for item in outputPath: for item in outputPath:
if os.path.exists(item): if os.path.exists(item):
saferRemove(item) os.remove(item)
if os.path.exists(item.replace('.epub', '.mobi')): if os.path.exists(item.replace('.epub', '.mobi')):
saferRemove(item.replace('.epub', '.mobi')) os.remove(item.replace('.epub', '.mobi'))
MW.addMessage.emit('KindleGen failed to create MOBI!', 'error', False) MW.addMessage.emit('KindleGen failed to create MOBI!', 'error', False)
MW.addTrayMessage.emit('KindleGen failed to create MOBI!', 'Critical') MW.addTrayMessage.emit('KindleGen failed to create MOBI!', 'Critical')
if self.kindlegenErrorCode[0] == 1 and self.kindlegenErrorCode[1] != '': if self.kindlegenErrorCode[0] == 1 and self.kindlegenErrorCode[1] != '':

View File

@@ -1,4 +1,4 @@
__version__ = '5.3.0' __version__ = '5.3.1'
__license__ = 'ISC' __license__ = 'ISC'
__copyright__ = '2012-2017, 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

@@ -22,12 +22,8 @@ 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, copy
try:
from scandir import walk
except ImportError:
walk = os.walk
from . import rarfile from . import rarfile
from .shared import check7ZFile as is_7zfile, saferReplace, saferRemove from .shared import check7ZFile as is_7zfile
class CBxArchive: class CBxArchive:
@@ -50,12 +46,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 +59,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'):
saferRemove(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'):
saferRemove(self.origFileName)
if not extracted: if not extracted:
raise OSError raise OSError
@@ -96,10 +86,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

@@ -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, sanitizeTrace
from . import comic2panel from . import comic2panel
from . import image from . import image
from . import cbxarchive from . import cbxarchive
@@ -85,11 +81,11 @@ 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;'
@@ -424,7 +420,7 @@ 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:
@@ -461,11 +457,11 @@ def imgDirectoryProcessing(path):
global workerPool, workerOutput global workerPool, workerOutput
workerPool = Pool() workerPool = Pool()
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])
@@ -482,9 +478,9 @@ def imgDirectoryProcessing(path):
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][0], workerOutput[0][1]) 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.")
@@ -497,8 +493,8 @@ def imgFileProcessingTick(output):
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:
@@ -513,7 +509,7 @@ 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 == 2 and not opt.webtoon: if opt.cropping == 2 and not opt.webtoon:
img.cropPageNumber(opt.croppingp) img.cropPageNumber(opt.croppingp)
if opt.cropping > 0 and not opt.webtoon: if opt.cropping > 0 and not opt.webtoon:
@@ -530,6 +526,8 @@ def imgFileProcessing(work):
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)
@@ -540,24 +538,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)
@@ -619,7 +620,7 @@ def getComicInfo(path, originalPath):
try: try:
xml = metadata.MetadataParser(xmlPath) xml = metadata.MetadataParser(xmlPath)
except Exception: except Exception:
saferRemove(xmlPath) os.remove(xmlPath)
return return
options.authors = [] options.authors = []
if defaultTitle: if defaultTitle:
@@ -644,7 +645,7 @@ def getComicInfo(path, originalPath):
options.chapters = xml.data['Bookmarks'] options.chapters = xml.data['Bookmarks']
if xml.data['Summary']: if xml.data['Summary']:
options.summary = escape(xml.data['Summary']) options.summary = escape(xml.data['Summary'])
saferRemove(xmlPath) os.remove(xmlPath)
def getCoversFromMCB(mangaID): def getCoversFromMCB(mangaID):
@@ -663,7 +664,7 @@ 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)
@@ -688,7 +689,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])
@@ -698,7 +699,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)
@@ -708,13 +709,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)
@@ -726,11 +727,11 @@ 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:
@@ -785,7 +786,7 @@ def splitProcess(path, mode):
move(os.path.join(root, name), os.path.join(currentTarget, name)) move(os.path.join(root, name), os.path.join(currentTarget, name))
else: else:
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:
if not firstTome: if not firstTome:
currentTarget, pathRoot = createNewTome() currentTarget, pathRoot = createNewTome()
@@ -799,9 +800,12 @@ def splitProcess(path, mode):
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:
@@ -822,7 +826,13 @@ 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. Second conversion will decrease quality.")
if GUI:
GUI.addMessage.emit('Source files are probably created by KCC. 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 25% 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.")
@@ -850,7 +860,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))
@@ -1107,14 +1117,14 @@ def makeBook(source, qtGUI=None):
print('Error: Failed to tweak KindleGen output!') print('Error: Failed to tweak KindleGen output!')
return filepath return filepath
else: else:
saferRemove(i.replace('.epub', '.mobi') + '_toclean') os.remove(i.replace('.epub', '.mobi') + '_toclean')
if k.path and k.coverSupport: if k.path and k.coverSupport:
options.covers[filepath.index(i)][0].saveToKindle(k, options.covers[filepath.index(i)][1]) options.covers[filepath.index(i)][0].saveToKindle(k, options.covers[filepath.index(i)][1])
return filepath return filepath
def makeMOBIFix(item, uuid): def makeMOBIFix(item, uuid):
saferRemove(item) os.remove(item)
mobiPath = item.replace('.epub', '.mobi') mobiPath = item.replace('.epub', '.mobi')
move(mobiPath, mobiPath + '_toclean') move(mobiPath, mobiPath + '_toclean')
try: try:

View File

@@ -24,15 +24,11 @@ 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, ImageStat, ImageOps
from .shared import getImageFileName, walkLevel, walkSort, saferRemove, sanitizeTrace 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))
@@ -77,7 +73,7 @@ def mergeDirectory(work):
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))
y += img.size[1] y += img.size[1]
saferRemove(i) os.remove(i)
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:
@@ -203,7 +199,7 @@ def splitImage(work):
targetHeight += panels[panel][2] targetHeight += panels[panel][2]
newPage.save(os.path.join(path, fileExpanded[0] + '-' + str(pageNumber) + '.png'), 'PNG') newPage.save(os.path.join(path, fileExpanded[0] + '-' + str(pageNumber) + '.png'), 'PNG')
pageNumber += 1 pageNumber += 1
saferRemove(filePath) os.remove(filePath)
except Exception: except Exception:
return str(sys.exc_info()[1]), sanitizeTrace(sys.exc_info()[2]) return str(sys.exc_info()[1]), sanitizeTrace(sys.exc_info()[2])
@@ -250,7 +246,7 @@ def main(argv=None, qtGUI=None):
mergeWorkerOutput = [] mergeWorkerOutput = []
mergeWorkerPool = Pool() mergeWorkerPool = Pool()
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
@@ -269,13 +265,13 @@ def main(argv=None, qtGUI=None):
rmtree(options.targetDir, True) rmtree(options.targetDir, True)
raise RuntimeError("One of workers crashed. Cause: " + mergeWorkerOutput[0][0], mergeWorkerOutput[0][1]) 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
work.append([root, name, options]) work.append([root, name, options])
else: else:
saferRemove(os.path.join(root, name)) os.remove(os.path.join(root, name))
if GUI: if GUI:
GUI.progressBarTick.emit('Splitting images') GUI.progressBarTick.emit('Splitting images')
GUI.progressBarTick.emit(str(pagenumber)) GUI.progressBarTick.emit(str(pagenumber))

View File

@@ -206,7 +206,7 @@ class ComicPageParser:
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.opt.profileData _, self.size, self.palette, self.gamma = self.opt.profileData
self.image = image self.image = image
@@ -232,16 +232,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.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
@@ -361,7 +361,7 @@ class Cover:
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.')
@@ -369,6 +369,6 @@ class Cover:
self.image = self.image.resize((300, 470), Image.ANTIALIAS) 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

@@ -27,10 +27,6 @@ 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 +67,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 +92,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,15 +101,7 @@ 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)
@@ -164,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,53 @@
# -*- 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

@@ -30,7 +30,7 @@
<key>CFBundleExecutable</key> <key>CFBundleExecutable</key>
<string>MacOS/Kindle Comic Converter</string> <string>MacOS/Kindle Comic Converter</string>
<key>CFBundleGetInfoString</key> <key>CFBundleGetInfoString</key>
<string>KindleComicConverter 5.3.0, written 2012-2017 by Ciro Mattia Gonano and Pawel Jastrzebski</string> <string>KindleComicConverter 5.3.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>
@@ -42,11 +42,11 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>5.3.0</string> <string>5.3.1</string>
<key>CFBundleSignature</key> <key>CFBundleSignature</key>
<string>????</string> <string>????</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>5.3.0</string> <string>5.3.1</string>
<key>LSEnvironment</key> <key>LSEnvironment</key>
<dict> <dict>
<key>PATH</key> <key>PATH</key>

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

View File

@@ -6,7 +6,7 @@ Usage (Windows):
py -3 setup.py build_binary py -3 setup.py build_binary
Usage (Linux/OS X): Usage (Linux/OS X):
python3 setup.py build_binary or python3 setup.py build_binary --pyz python3 setup.py build_binary
""" """
import os import os
@@ -20,8 +20,6 @@ from kindlecomicconverter 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'
@@ -62,74 +60,12 @@ class BuildBinaryCommand(distutils.cmd.Command):
os.system('setup.bat') os.system('setup.bat')
exit(0) exit(0)
else: else:
if self.pyz: os.system('docker run --rm -v ' + os.getcwd() + ':/app -e KCCVER=' + VERSION + ' acidweb/kcc')
script = ''' exit(0)
cp kcc.py __main__.py
zip kcc.zip __main__.py kindlecomicconverter/*.py
echo "#!/usr/bin/env python3" > kcc-bin
cat kcc.zip >> kcc-bin
chmod +x kcc-bin
cp kcc-c2e.py __main__.py
zip kcc-c2e.zip __main__.py kindlecomicconverter/*.py
echo "#!/usr/bin/env python3" > kcc-c2e-bin
cat kcc-c2e.zip >> kcc-c2e-bin
chmod +x kcc-c2e-bin
cp kcc-c2p.py __main__.py
zip kcc-c2p.zip __main__.py kindlecomicconverter/*.py
echo "#!/usr/bin/env python3" > kcc-c2p-bin
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,
@@ -137,7 +73,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,
) )