1
0
mirror of https://github.com/ciromattia/kcc synced 2025-12-13 09:46:25 +00:00

Merge pull request #113 from ciromattia/4.x

4.3
This commit is contained in:
Paweł Jastrzębski
2014-09-29 18:05:32 +02:00
12 changed files with 253 additions and 242 deletions

View File

@@ -1,13 +1,13 @@
# KCC # KCC
**Kindle Comic Converter** is a Python app to convert comic 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 initally developed for Kindle but since v2.2 it outputs valid ePub 2.0 so _**despite its name, KCC is It was initially developed for Kindle but since version 2.2 it outputs valid EPUB 2.0 so _**despite its name, KCC is
actually a comic to EPUB converter that every e-reader owner can happily use**_. actually a comic/manga to EPUB converter that every e-reader owner can happily use**_.
It can also optionally optimize images by applying a number of transformations. 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 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 ;-)
### Issues / new features / donations ### Issues / new features / donations
@@ -36,7 +36,7 @@ You can find the latest released binary at the following links:
- CBZ, ZIP - CBZ, ZIP
- CBR, RAR *(With `unrar` executable)* - CBR, RAR *(With `unrar` executable)*
- CB7, 7Z *(With `7za` executable)* - CB7, 7Z *(With `7za` executable)*
- PDF *(Extracting only contained JPG images)* - PDF *(Only extracting JPG images)*
## OPTIONAL REQUIREMENTS ## OPTIONAL REQUIREMENTS
- [KindleGen](http://www.amazon.com/gp/feature.html?ie=UTF8&docId=1000765211) v2.9+ in a directory reachable by your _PATH_ or in _KCC_ directory *(For MOBI generation)* - [KindleGen](http://www.amazon.com/gp/feature.html?ie=UTF8&docId=1000765211) v2.9+ in a directory reachable by your _PATH_ or in _KCC_ directory *(For MOBI generation)*
@@ -67,6 +67,8 @@ After completed conversion you should find ready file alongside the original inp
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 :-)
### Standalone `kcc-c2e.py` usage: ### Standalone `kcc-c2e.py` usage:
``` ```
@@ -75,7 +77,9 @@ Usage: kcc-c2e [options] comic_file|comic_folder
Options: Options:
MAIN: MAIN:
-p PROFILE, --profile=PROFILE -p PROFILE, --profile=PROFILE
Device profile (Choose one among K1, K2, K345, KDX, KHD, KF, KFHD, KFHD8, KFHDX, KFHDX8, KFA, KoMT, KoG, KoA, KoAHD) [Default=KHD] Device profile (Available options: K1, K2, K345, KDX,
KPW, KV, KFHD, KFHDX, KFHDX8, KFA, KoMT, KoG, KoA,
KoAHD, KoAH2O) [Default=KV]
-q QUALITY, --quality=QUALITY -q QUALITY, --quality=QUALITY
Quality of Panel View. 0 - Normal 1 - High 2 - Ultra [Default=0] Quality of Panel View. 0 - Normal 1 - High 2 - Ultra [Default=0]
-m, --manga-style Manga style (Right-to-left reading and splitting) -m, --manga-style Manga style (Right-to-left reading and splitting)
@@ -86,7 +90,9 @@ Options:
Output generated file to specified directory or file Output generated file to specified directory or file
-t TITLE, --title=TITLE -t TITLE, --title=TITLE
Comic title [Default=filename or directory name] Comic title [Default=filename or directory name]
--cbz-output Outputs a CBZ archive and does not generate EPUB -f FORMAT, --format=FORMAT
Output format (Available options: Auto, MOBI,
EPUB, CBZ) [Default=Auto]
--batchsplit Split output into multiple files --batchsplit Split output into multiple files
PROCESSING: PROCESSING:
@@ -134,7 +140,7 @@ Options:
This script born as a cross-platform alternative to `KindleComicParser` by **Dc5e** (published [here](http://www.mobileread.com/forums/showthread.php?t=192783)). This script born as a cross-platform alternative to `KindleComicParser` by **Dc5e** (published [here](http://www.mobileread.com/forums/showthread.php?t=192783)).
The app relies and includes the following scripts/binaries: The app relies and includes the following scripts:
- `DualMetaFix` script by **K. Hendricks**. Released with GPL-3 License. - `DualMetaFix` script by **K. Hendricks**. Released with GPL-3 License.
- `rarfile.py` script &copy; 2005-2011 **Marko Kreen** <markokr@gmail.com>. Released with ISC License. - `rarfile.py` script &copy; 2005-2011 **Marko Kreen** <markokr@gmail.com>. Released with ISC License.
@@ -142,17 +148,15 @@ The app relies and includes the following scripts/binaries:
- Icon is by **Nikolay Verin** ([http://ncrow.deviantart.com/](http://ncrow.deviantart.com/)) and released under [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/) License. - Icon is by **Nikolay Verin** ([http://ncrow.deviantart.com/](http://ncrow.deviantart.com/)) and released under [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/) License.
## SAMPLE FILES CREATED BY KCC ## SAMPLE FILES CREATED BY KCC
* [Kindle Voyage](http://kcc.iosphe.re/Samples/Ubunchu!-KV.mobi)
* [Kindle Paperwhite](http://kcc.iosphe.re/Samples/Ubunchu!-KPW.mobi) * [Kindle Paperwhite](http://kcc.iosphe.re/Samples/Ubunchu!-KPW.mobi)
* [Kindle](http://kcc.iosphe.re/Samples/Ubunchu!-K345.mobi) * [Kindle](http://kcc.iosphe.re/Samples/Ubunchu!-K345.mobi)
* [Kindle DX/DXG](http://kcc.iosphe.re/Samples/Ubunchu!-KDX.mobi) * [Kindle DX/DXG](http://kcc.iosphe.re/Samples/Ubunchu!-KDX.cbz)
* [Kindle Fire HD](http://kcc.iosphe.re/Samples/Ubunchu!-KFHD.mobi)
* [Kindle Fire HD 8.9"](http://kcc.iosphe.re/Samples/Ubunchu!-KFHD8.mobi)
* [Kindle Fire HDX](http://kcc.iosphe.re/Samples/Ubunchu!-KFHDX.mobi)
* [Kindle Fire HDX 8.9"](http://kcc.iosphe.re/Samples/Ubunchu!-KFHDX8.mobi)
* [Kobo Mini/Touch](http://kcc.iosphe.re/Samples/Ubunchu!-KoMT.cbz) * [Kobo Mini/Touch](http://kcc.iosphe.re/Samples/Ubunchu!-KoMT.cbz)
* [Kobo Glow](http://kcc.iosphe.re/Samples/Ubunchu!-KoG.cbz) * [Kobo Glow](http://kcc.iosphe.re/Samples/Ubunchu!-KoG.cbz)
* [Kobo Aura](http://kcc.iosphe.re/Samples/Ubunchu!-KoA.cbz) * [Kobo Aura](http://kcc.iosphe.re/Samples/Ubunchu!-KoA.cbz)
* [Kobo Aura HD](http://kcc.iosphe.re/Samples/Ubunchu!-KoAHD.cbz) * [Kobo Aura HD](http://kcc.iosphe.re/Samples/Ubunchu!-KoAHD.cbz)
* [Kobo Aura H2O](http://kcc.iosphe.re/Samples/Ubunchu!-KoAH2O.cbz)
## CHANGELOG ## CHANGELOG
####1.0 ####1.0
@@ -188,11 +192,11 @@ The app relies and includes the following scripts/binaries:
* Added basic error reporting * Added basic error reporting
####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.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.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)
@@ -200,22 +204,22 @@ The app relies and includes the following scripts/binaries:
####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.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.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)
* Epub margins support (#30) * EPUB margins support (#30)
* Fixed no file added if file has no spaces on Windows (#25) * Fixed no file added if file has no spaces on Windows (#25)
* Gracefully exit if unrar missing (#15) * Gracefully exit if unrar missing (#15)
* Do not call kindlegen if source epub is bigger than 320MB (#17) * Do not call kindlegen if source EPUB is bigger than 320MB (#17)
* Get filetype from magic number (#14) * Get filetype from magic number (#14)
* PDF conversion works again * PDF conversion works again
@@ -229,7 +233,7 @@ The app relies and includes the following scripts/binaries:
* Optimized archive extraction for zip/rar files (#40) * Optimized archive extraction for zip/rar files (#40)
####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)
@@ -237,7 +241,7 @@ The app relies and includes the following scripts/binaries:
####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
* Fixed panel order for horizontal pages when --rotate is enabled * Fixed panel order for horizontal pages when --rotate is enabled
* Disabled cropping and page number cutting for blank pages * Disabled cropping and page number cutting for blank pages
@@ -368,6 +372,11 @@ The app relies and includes the following scripts/binaries:
* 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.3:
* Added profiles for Kindle Voyage and Kobo Aura H2O
* Added missing features to CLI version
* Other minor bug fixes
## KNOWN ISSUES ## KNOWN ISSUES
Please check [wiki page](https://github.com/ciromattia/kcc/wiki/Known-issues). Please check [wiki page](https://github.com/ciromattia/kcc/wiki/Known-issues).

View File

@@ -18,7 +18,7 @@
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE. # PERFORMANCE OF THIS SOFTWARE.
__version__ = '4.2.1' __version__ = '4.3'
__license__ = 'ISC' __license__ = 'ISC'
__copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>' __copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'

View File

@@ -18,7 +18,7 @@
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE. # PERFORMANCE OF THIS SOFTWARE.
__version__ = '4.2.1' __version__ = '4.3'
__license__ = 'ISC' __license__ = 'ISC'
__copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>' __copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'

View File

@@ -1,5 +1,5 @@
#define MyAppName "Kindle Comic Converter" #define MyAppName "Kindle Comic Converter"
#define MyAppVersion "4.2.1" #define MyAppVersion "4.3"
#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"
@@ -43,13 +43,11 @@ Name: "CB7association"; Description: "CB7"; GroupDescription: "File associations
[Files] [Files]
; x64 files ; x64 files
Source: "dist_64\imageformats\*"; DestDir: "{app}\imageformats\"; Flags: ignoreversion; Check: Is64BitInstallMode
Source: "dist_64\platforms\*"; DestDir: "{app}\platforms\"; Flags: ignoreversion; Check: Is64BitInstallMode Source: "dist_64\platforms\*"; DestDir: "{app}\platforms\"; Flags: ignoreversion; Check: Is64BitInstallMode
Source: "dist_64\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion; Check: Is64BitInstallMode Source: "dist_64\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion; Check: Is64BitInstallMode
Source: "dist_64\*.dll"; DestDir: "{app}"; Flags: ignoreversion; Check: Is64BitInstallMode Source: "dist_64\*.dll"; DestDir: "{app}"; Flags: ignoreversion; Check: Is64BitInstallMode
Source: "other\vcredist_x64.exe"; DestDir: "{tmp}"; Flags: ignoreversion deleteafterinstall; Check: Is64BitInstallMode Source: "other\vcredist_x64.exe"; DestDir: "{tmp}"; Flags: ignoreversion deleteafterinstall; Check: Is64BitInstallMode
; x86 files ; x86 files
Source: "dist\imageformats\*"; DestDir: "{app}\imageformats\"; Flags: ignoreversion; Check: not Is64BitInstallMode
Source: "dist\platforms\*"; DestDir: "{app}\platforms\"; Flags: ignoreversion; Check: not Is64BitInstallMode Source: "dist\platforms\*"; DestDir: "{app}\platforms\"; Flags: ignoreversion; Check: not Is64BitInstallMode
Source: "dist\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion; Check: not Is64BitInstallMode Source: "dist\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion; Check: not Is64BitInstallMode
Source: "dist\*.dll"; DestDir: "{app}"; Flags: ignoreversion; Check: not Is64BitInstallMode Source: "dist\*.dll"; DestDir: "{app}"; Flags: ignoreversion; Check: not Is64BitInstallMode

51
kcc.py
View File

@@ -18,7 +18,7 @@
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE. # PERFORMANCE OF THIS SOFTWARE.
__version__ = '4.2.1' __version__ = '4.3'
__license__ = 'ISC' __license__ = 'ISC'
__copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>' __copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
@@ -84,14 +84,14 @@ elif sys.platform.startswith('win'):
os.chdir(os.path.dirname(os.path.abspath(sys.executable))) os.chdir(os.path.dirname(os.path.abspath(sys.executable)))
# Implementing dummy stdout and stderr for frozen Windows release # Implementing dummy stdout and stderr for frozen Windows release
class fakestd(object): class FakeSTD(object):
def write(self, string): def write(self, string):
pass pass
def flush(self): def flush(self):
pass pass
sys.stdout = fakestd() sys.stdout = FakeSTD()
sys.stderr = fakestd() sys.stderr = FakeSTD()
else: else:
os.environ['PATH'] = os.path.dirname(os.path.abspath(__file__)) + '/other/;' + os.environ['PATH'] os.environ['PATH'] = os.path.dirname(os.path.abspath(__file__)) + '/other/;' + os.environ['PATH']
os.chdir(os.path.dirname(os.path.abspath(__file__))) os.chdir(os.path.dirname(os.path.abspath(__file__)))
@@ -103,36 +103,33 @@ class QApplicationMessaging(QtWidgets.QApplication):
def __init__(self, argv): def __init__(self, argv):
QtWidgets.QApplication.__init__(self, argv) QtWidgets.QApplication.__init__(self, argv)
self._memory = QtCore.QSharedMemory(self)
self._memory.setKey('KCC')
if self._memory.attach():
self._running = True
else:
self._running = False
self._memory.create(1)
self._key = 'KCC' self._key = 'KCC'
self._timeout = 1000 self._timeout = 1000
self._server = QtNetwork.QLocalServer(self) self._locked = False
if not self.isRunning(): socket = QtNetwork.QLocalSocket(self)
socket.connectToServer(self._key, QtCore.QIODevice.WriteOnly)
if not socket.waitForConnected(self._timeout):
self._server = QtNetwork.QLocalServer(self)
# noinspection PyUnresolvedReferences # noinspection PyUnresolvedReferences
self._server.newConnection.connect(self.handleMessage) self._server.newConnection.connect(self.handleMessage)
self._server.listen(self._key) self._server.listen(self._key)
else:
self._locked = True
socket.disconnectFromServer()
def shutdown(self): def __del__(self):
if self._memory.isAttached(): if not self._locked:
self._memory.detach()
self._server.close() self._server.close()
def event(self, e): def event(self, e):
if e.type() == QtCore.QEvent.FileOpen: if e.type() == QtCore.QEvent.FileOpen:
# noinspection PyArgumentList
self.messageFromOtherInstance.emit(bytes(e.file(), 'UTF-8')) self.messageFromOtherInstance.emit(bytes(e.file(), 'UTF-8'))
return True return True
else: else:
return QtWidgets.QApplication.event(self, e) return QtWidgets.QApplication.event(self, e)
def isRunning(self): def isRunning(self):
return self._running return self._locked
def handleMessage(self): def handleMessage(self):
socket = self._server.nextPendingConnection() socket = self._server.nextPendingConnection()
@@ -140,18 +137,12 @@ class QApplicationMessaging(QtWidgets.QApplication):
self.messageFromOtherInstance.emit(socket.readAll().data()) self.messageFromOtherInstance.emit(socket.readAll().data())
def sendMessage(self, message): def sendMessage(self, message):
if self.isRunning(): socket = QtNetwork.QLocalSocket(self)
socket = QtNetwork.QLocalSocket(self) socket.connectToServer(self._key, QtCore.QIODevice.WriteOnly)
socket.connectToServer(self._key, QtCore.QIODevice.WriteOnly) socket.waitForConnected(self._timeout)
if not socket.waitForConnected(self._timeout): socket.write(bytes(message, 'UTF-8'))
return False socket.waitForBytesWritten(self._timeout)
# noinspection PyArgumentList socket.disconnectFromServer()
socket.write(bytes(message, 'UTF-8'))
if not socket.waitForBytesWritten(self._timeout):
return False
socket.disconnectFromServer()
return True
return False
# Adding signals to QMainWindow # Adding signals to QMainWindow

View File

@@ -17,7 +17,7 @@
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE. # PERFORMANCE OF THIS SOFTWARE.
__version__ = '4.2.1' __version__ = '4.3'
__license__ = 'ISC' __license__ = 'ISC'
__copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>' __copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
@@ -36,12 +36,10 @@ from subprocess import STDOUT, PIPE
from PyQt5 import QtGui, QtCore, QtWidgets from PyQt5 import QtGui, QtCore, QtWidgets
from xml.dom.minidom import parse from xml.dom.minidom import parse
from html.parser import HTMLParser from html.parser import HTMLParser
from psutil import virtual_memory, Popen, Process from psutil import Popen, Process
from uuid import uuid4
from copy import copy from copy import copy
from .shared import md5Checksum from .shared import md5Checksum
from . import comic2ebook from . import comic2ebook
from . import dualmetafix
from . import KCC_rc_web from . import KCC_rc_web
if sys.platform.startswith('darwin'): if sys.platform.startswith('darwin'):
from . import KCC_ui_osx as KCC_ui from . import KCC_ui_osx as KCC_ui
@@ -265,84 +263,14 @@ class ProgressThread(QtCore.QThread):
self.running = False self.running = False
class WorkerSignals(QtCore.QObject):
result = QtCore.pyqtSignal(list)
class KindleGenThread(QtCore.QRunnable):
def __init__(self, batch):
super(KindleGenThread, self).__init__()
self.signals = WorkerSignals()
self.work = batch
def run(self):
kindlegenErrorCode = 0
kindlegenError = ''
try:
if os.path.getsize(self.work) < 629145600:
output = Popen('kindlegen -dont_append_source -locale en "' + self.work + '"', stdout=PIPE,
stderr=STDOUT, shell=True)
for line in output.stdout:
line = line.decode('utf-8')
# ERROR: Generic error
if "Error(" in line:
kindlegenErrorCode = 1
kindlegenError = line
# ERROR: EPUB too big
if ":E23026:" in line:
kindlegenErrorCode = 23026
if kindlegenErrorCode > 0:
break
else:
# ERROR: EPUB too big
kindlegenErrorCode = 23026
self.signals.result.emit([kindlegenErrorCode, kindlegenError, self.work])
except Exception as err:
# ERROR: KCC unknown generic error
kindlegenErrorCode = 1
kindlegenError = format(err)
self.signals.result.emit([kindlegenErrorCode, kindlegenError, self.work])
class DualMetaFixThread(QtCore.QRunnable):
def __init__(self, batch):
super(DualMetaFixThread, self).__init__()
self.signals = WorkerSignals()
self.work = batch
def run(self):
item = self.work
os.remove(item)
mobiPath = item.replace('.epub', '.mobi')
move(mobiPath, mobiPath + '_toclean')
try:
# noinspection PyArgumentList
dualmetafix.DualMobiMetaFix(mobiPath + '_toclean', mobiPath, bytes(str(uuid4()), 'UTF-8'))
self.signals.result.emit([True])
except Exception as err:
self.signals.result.emit([False, format(err)])
class WorkerThread(QtCore.QThread): class WorkerThread(QtCore.QThread):
#noinspection PyArgumentList # noinspection PyArgumentList
def __init__(self): def __init__(self):
QtCore.QThread.__init__(self) QtCore.QThread.__init__(self)
self.pool = QtCore.QThreadPool()
self.conversionAlive = False self.conversionAlive = False
self.errors = False self.errors = False
self.kindlegenErrorCode = [0] self.kindlegenErrorCode = [0]
self.workerOutput = [] self.workerOutput = []
# Let's make sure that we don't fill the memory
availableMemory = virtual_memory().total/1000000000
if availableMemory <= 2:
self.threadNumber = 1
elif 2 < availableMemory <= 4:
self.threadNumber = 2
else:
self.threadNumber = 4
# Let's make sure that we don't use too many threads
if self.threadNumber > QtCore.QThread.idealThreadCount():
self.threadNumber = QtCore.QThread.idealThreadCount()
self.progressBarTick = MW.progressBarTick self.progressBarTick = MW.progressBarTick
self.addMessage = MW.addMessage self.addMessage = MW.addMessage
@@ -361,9 +289,11 @@ class WorkerThread(QtCore.QThread):
MW.addTrayMessage.emit('Conversion interrupted.', 'Critical') MW.addTrayMessage.emit('Conversion interrupted.', 'Critical')
MW.modeConvert.emit(1) MW.modeConvert.emit(1)
def addResult(self, output): def sanitizeTrace(self, traceback):
MW.progressBarTick.emit('tick') return ''.join(format_tb(traceback))\
self.workerOutput.append(output) .replace('C:\\Users\\AcidWeb\\Documents\\Projekty\\KCC\\', '')\
.replace('C:\\Python34\\', '')\
.replace('C:\\Python34_64\\', '')
def run(self): def run(self):
MW.modeConvert.emit(0) MW.modeConvert.emit(0)
@@ -385,10 +315,9 @@ class WorkerThread(QtCore.QThread):
options.quality = 1 options.quality = 1
elif GUI.QualityBox.checkState() == 2: elif GUI.QualityBox.checkState() == 2:
options.quality = 2 options.quality = 2
if str(GUI.FormatBox.currentText()) == 'CBZ': options.format = str(GUI.FormatBox.currentText())
options.cbzoutput = True
if GUI.currentMode == 1: if GUI.currentMode == 1:
if profile in ['KFHD', 'KFHD8', 'KFHDX', 'KFHDX8']: if 'KFH' in profile:
options.upscale = True options.upscale = True
# Advanced mode settings # Advanced mode settings
@@ -410,10 +339,7 @@ class WorkerThread(QtCore.QThread):
if GUI.WebtoonBox.isChecked(): if GUI.WebtoonBox.isChecked():
options.webtoon = True options.webtoon = True
if float(GUI.GammaValue) > 0.09: if float(GUI.GammaValue) > 0.09:
# noinspection PyTypeChecker
options.gamma = float(GUI.GammaValue) options.gamma = float(GUI.GammaValue)
if str(GUI.FormatBox.currentText()) == 'MOBI':
options.batchsplit = True
# Other/custom settings. # Other/custom settings.
if GUI.currentMode > 2: if GUI.currentMode > 2:
@@ -462,7 +388,7 @@ class WorkerThread(QtCore.QThread):
self.errors = True self.errors = True
_, _, traceback = sys.exc_info() _, _, traceback = sys.exc_info()
MW.showDialog.emit("Error during conversion %s:\n\n%s\n\nTraceback:\n%s" MW.showDialog.emit("Error during conversion %s:\n\n%s\n\nTraceback:\n%s"
% (jobargv[-1], str(err), "".join(format_tb(traceback))), 'error') % (jobargv[-1], str(err), self.sanitizeTrace(traceback)), 'error')
MW.addMessage.emit('Failed to create EPUB!', 'error', False) MW.addMessage.emit('Failed to create EPUB!', 'error', False)
MW.addTrayMessage.emit('Failed to create EPUB!', 'Critical') MW.addTrayMessage.emit('Failed to create EPUB!', 'Critical')
if not self.conversionAlive: if not self.conversionAlive:
@@ -483,16 +409,10 @@ class WorkerThread(QtCore.QThread):
MW.progressBarTick.emit('tick') MW.progressBarTick.emit('tick')
MW.addMessage.emit('Creating MOBI files', 'info', False) MW.addMessage.emit('Creating MOBI files', 'info', False)
GUI.progress.content = 'Creating MOBI files' GUI.progress.content = 'Creating MOBI files'
self.workerOutput = [] work = []
# Number of KindleGen threads depends on the size of RAM
self.pool.setMaxThreadCount(self.threadNumber)
for item in outputPath: for item in outputPath:
worker = KindleGenThread(item) work.append([item])
worker.signals.result.connect(self.addResult) self.workerOutput = comic2ebook.makeMOBI(work, self)
self.pool.start(worker)
self.pool.waitForDone()
while len(self.workerOutput) != len(outputPath):
sleep(0.1)
self.kindlegenErrorCode = [0] self.kindlegenErrorCode = [0]
for errors in self.workerOutput: for errors in self.workerOutput:
if errors[0] != 0: if errors[0] != 0:
@@ -512,15 +432,9 @@ class WorkerThread(QtCore.QThread):
MW.addMessage.emit('Processing MOBI files', 'info', False) MW.addMessage.emit('Processing MOBI files', 'info', False)
GUI.progress.content = 'Processing MOBI files' GUI.progress.content = 'Processing MOBI files'
self.workerOutput = [] self.workerOutput = []
# DualMetaFix is very fast and there is not reason to use multithreading.
self.pool.setMaxThreadCount(1)
for item in outputPath: for item in outputPath:
worker = DualMetaFixThread(item) self.workerOutput.append(comic2ebook.makeMOBIFix(item))
worker.signals.result.connect(self.addResult) MW.progressBarTick.emit('tick')
self.pool.start(worker)
self.pool.waitForDone()
while len(self.workerOutput) != len(outputPath):
sleep(0.1)
for success in self.workerOutput: for success in self.workerOutput:
if not success[0]: if not success[0]:
self.errors = True self.errors = True
@@ -1018,7 +932,6 @@ class KCCGUI(KCC_ui.Ui_KCC):
'GammaSlider': float(self.GammaValue)*100}) 'GammaSlider': float(self.GammaValue)*100})
self.settings.sync() self.settings.sync()
self.tray.hide() self.tray.hide()
APP.shutdown()
def handleMessage(self, message): def handleMessage(self, message):
MW.raise_() MW.raise_()
@@ -1121,22 +1034,20 @@ class KCCGUI(KCC_ui.Ui_KCC):
self.p.ionice(1) self.p.ionice(1)
self.profiles = { self.profiles = {
"Kindle Voyage": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': False, 'Label': 'KV'},
"Kindle Paperwhite": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0, "Kindle Paperwhite": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': False, 'Label': 'KHD'}, 'DefaultUpscale': False, 'Label': 'KPW'},
"Kindle": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0, "Kindle": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': False, 'Label': 'K345'}, 'DefaultUpscale': False, 'Label': 'K345'},
"Kindle DX/DXG": {'Quality': False, 'ForceExpert': False, 'DefaultFormat': 2, "Kindle DX/DXG": {'Quality': False, 'ForceExpert': False, 'DefaultFormat': 2,
'DefaultUpscale': False, 'Label': 'KDX'}, 'DefaultUpscale': False, 'Label': 'KDX'},
"Kindle Fire": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0, "K. Fire HD": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': False, 'Label': 'KF'}, 'DefaultUpscale': True, 'Label': 'KFHD'},
"K. Fire HD 7\"": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0, "K. Fire HDX": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': True, 'Label': 'KFHD'}, 'DefaultUpscale': True, 'Label': 'KFHDX'},
"K. Fire HD 8.9\"": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0, "K. Fire HDX 8.9": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': True, 'Label': 'KFHD8'}, 'DefaultUpscale': True, 'Label': 'KFHDX8'},
"K. Fire HDX 7\"": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': True, 'Label': 'KFHDX'},
"K. Fire HDX 8.9\"": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': True, 'Label': 'KFHDX8'},
"Kobo Mini/Touch": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 2, "Kobo Mini/Touch": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 2,
'DefaultUpscale': False, 'Label': 'KoMT'}, 'DefaultUpscale': False, 'Label': 'KoMT'},
"Kobo Glow": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 2, "Kobo Glow": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 2,
@@ -1145,6 +1056,8 @@ class KCCGUI(KCC_ui.Ui_KCC):
'DefaultUpscale': False, 'Label': 'KoA'}, 'DefaultUpscale': False, 'Label': 'KoA'},
"Kobo Aura HD": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 2, "Kobo Aura HD": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 2,
'DefaultUpscale': False, 'Label': 'KoAHD'}, 'DefaultUpscale': False, 'Label': 'KoAHD'},
"Kobo Aura H2O": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 2,
'DefaultUpscale': False, 'Label': 'KoAH2O'},
"Other": {'Quality': False, 'ForceExpert': True, 'DefaultFormat': 1, "Other": {'Quality': False, 'ForceExpert': True, 'DefaultFormat': 1,
'DefaultUpscale': False, 'Label': 'OTHER'}, 'DefaultUpscale': False, 'Label': 'OTHER'},
"Kindle for Android": {'Quality': False, 'ForceExpert': True, 'DefaultFormat': 0, "Kindle for Android": {'Quality': False, 'ForceExpert': True, 'DefaultFormat': 0,
@@ -1155,26 +1068,26 @@ class KCCGUI(KCC_ui.Ui_KCC):
'DefaultUpscale': False, 'Label': 'K2'} 'DefaultUpscale': False, 'Label': 'K2'}
} }
profilesGUI = [ profilesGUI = [
"Kindle Voyage",
"Kindle Paperwhite", "Kindle Paperwhite",
"Kindle", "Kindle",
"Kindle DX/DXG",
"Separator", "Separator",
"Kindle Fire", "K. Fire HD",
"K. Fire HD 7\"", "K. Fire HDX",
"K. Fire HD 8.9\"", "K. Fire HDX 8.9",
"K. Fire HDX 7\"",
"K. Fire HDX 8.9\"",
"Separator", "Separator",
"Kobo Mini/Touch", "Kobo Mini/Touch",
"Kobo Glow", "Kobo Glow",
"Kobo Aura", "Kobo Aura",
"Kobo Aura HD", "Kobo Aura HD",
"Kobo Aura H2O",
"Separator", "Separator",
"Other", "Other",
"Separator", "Separator",
"Kindle for Android", "Kindle for Android",
"Kindle 1", "Kindle 1",
"Kindle 2", "Kindle 2",
"Kindle DX/DXG",
] ]
statusBarLabel = QtWidgets.QLabel('<b><a href="http://kcc.iosphe.re/">HOMEPAGE</a> - <a href="https://github.' statusBarLabel = QtWidgets.QLabel('<b><a href="http://kcc.iosphe.re/">HOMEPAGE</a> - <a href="https://github.'

View File

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

View File

@@ -18,7 +18,7 @@
# PERFORMANCE OF THIS SOFTWARE. # PERFORMANCE OF THIS SOFTWARE.
# #
__version__ = '4.2.1' __version__ = '4.3'
__license__ = 'ISC' __license__ = 'ISC'
__copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>' __copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
@@ -38,6 +38,8 @@ from xml.dom.minidom import parse
from uuid import uuid4 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 psutil import Popen, virtual_memory
try: try:
from PyQt5 import QtCore from PyQt5 import QtCore
except ImportError: except ImportError:
@@ -47,6 +49,7 @@ from . import comic2panel
from . import image from . import image
from . import cbxarchive from . import cbxarchive
from . import pdfjpgextract from . import pdfjpgextract
from . import dualmetafix
def main(argv=None): def main(argv=None):
@@ -746,7 +749,7 @@ def sanitizePermissions(filetree):
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 # noinspection PyUnboundLocalVariable
def splitDirectory(path): def splitDirectory(path):
# Detect directory stucture # Detect directory stucture
for root, dirs, files in walkLevel(os.path.join(path, 'OEBPS', 'Images'), 0): for root, dirs, files in walkLevel(os.path.join(path, 'OEBPS', 'Images'), 0):
@@ -871,7 +874,7 @@ def detectCorruption(tmpPath, orgPath):
for name in files: for name in files:
if getImageFileName(name) is not None: if getImageFileName(name) is not None:
path = os.path.join(root, name) path = os.path.join(root, name)
pathOrg = os.path.join(orgPath, name) pathOrg = orgPath + path.split('OEBPS' + os.path.sep + 'Images')[1]
if os.path.getsize(path) == 0: if os.path.getsize(path) == 0:
rmtree(os.path.join(tmpPath, '..', '..'), True) rmtree(os.path.join(tmpPath, '..', '..'), True)
raise RuntimeError('Image file %s is corrupted.' % pathOrg) raise RuntimeError('Image file %s is corrupted.' % pathOrg)
@@ -945,7 +948,7 @@ def makeZIP(zipFilename, baseDir, isEPUB=False):
def makeParser(): def makeParser():
"""Create and return an option parser set up with kcc's options.""" """Create and return an option parser set up with KCC options."""
psr = OptionParser(usage="Usage: kcc-c2e [options] comic_file|comic_folder", add_help_option=False) psr = OptionParser(usage="Usage: kcc-c2e [options] comic_file|comic_folder", add_help_option=False)
mainOptions = OptionGroup(psr, "MAIN") mainOptions = OptionGroup(psr, "MAIN")
@@ -954,9 +957,9 @@ def makeParser():
customProfileOptions = OptionGroup(psr, "CUSTOM PROFILE") customProfileOptions = OptionGroup(psr, "CUSTOM PROFILE")
otherOptions = OptionGroup(psr, "OTHER") otherOptions = OptionGroup(psr, "OTHER")
mainOptions.add_option("-p", "--profile", action="store", dest="profile", default="KHD", mainOptions.add_option("-p", "--profile", action="store", dest="profile", default="KV",
help="Device profile (Choose one among K1, K2, K345, KDX, KHD, KF, KFHD, KFHD8, KFHDX," help="Device profile (Available options: K1, K2, K345, KDX, KPW, KV, KFHD, KFHDX, KFHDX8,"
" KFHDX8, KFA, KoMT, KoG, KoA, KoAHD) [Default=KHD]") " KFA, KoMT, KoG, KoA, KoAHD, KoAH2O) [Default=KV]")
mainOptions.add_option("-q", "--quality", type="int", dest="quality", default="0", mainOptions.add_option("-q", "--quality", type="int", dest="quality", default="0",
help="Quality of Panel View. 0 - Normal 1 - High 2 - Ultra [Default=0]") help="Quality of Panel View. 0 - Normal 1 - High 2 - Ultra [Default=0]")
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,
@@ -968,8 +971,8 @@ def makeParser():
help="Output generated file to specified directory or file") help="Output generated file to specified directory or file")
outputOptions.add_option("-t", "--title", action="store", dest="title", default="defaulttitle", outputOptions.add_option("-t", "--title", action="store", dest="title", default="defaulttitle",
help="Comic title [Default=filename or directory name]") help="Comic title [Default=filename or directory name]")
outputOptions.add_option("--cbz-output", action="store_true", dest="cbzoutput", default=False, outputOptions.add_option("-f", "--format", action="store", dest="format", default="Auto",
help="Outputs a CBZ archive and does not generate EPUB") help="Output format (Available options: Auto, MOBI, EPUB, CBZ) [Default=Auto]")
outputOptions.add_option("--batchsplit", action="store_true", dest="batchsplit", default=False, outputOptions.add_option("--batchsplit", action="store_true", dest="batchsplit", default=False,
help="Split output into multiple files"), help="Split output into multiple files"),
@@ -1016,13 +1019,22 @@ def checkOptions():
global options global options
options.panelview = True options.panelview = True
options.bordersColor = None options.bordersColor = None
if options.format == 'Auto':
if options.profile in ['K1', 'K2', 'K345', 'KPW', 'KV', 'KFHD', 'KFHDX', 'KFHDX8', 'KFA']:
options.format = 'MOBI'
elif options.profile in ['Other']:
options.format = 'EPUB'
elif options.profile in ['KDX', 'KoMT', 'KoG', 'KoA', 'KoAHD', 'KoAH2O']:
options.format = 'CBZ'
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
if options.format == 'MOBI':
options.batchsplit = True
# Disabling grayscale conversion for Kindle Fire family. # Disabling grayscale conversion for Kindle Fire family.
if options.profile == 'KF' or options.profile == 'KFHD' or options.profile == 'KFHD8' or options.profile == 'KFHDX'\ if 'KFH' in options.profile or options.forcecolor:
or options.profile == 'KFHDX8' or options.forcecolor:
options.forcecolor = True options.forcecolor = True
else: else:
options.forcecolor = False options.forcecolor = False
@@ -1049,10 +1061,10 @@ def checkOptions():
print("ERROR: Kindle for Android profile require --customwidth and --customheight options!") print("ERROR: Kindle for Android profile require --customwidth and --customheight options!")
sys.exit(1) sys.exit(1)
# CBZ files on Kindle DX/DXG support higher resolution # CBZ files on Kindle DX/DXG support higher resolution
if options.profile == 'KDX' and options.cbzoutput: if options.profile == 'KDX' and options.format == 'CBZ':
options.customheight = 1200 options.customheight = 1200
# Ultra mode don't work with CBZ format # Ultra mode don't work with CBZ format
if options.quality == 2 and options.cbzoutput: if options.quality == 2 and options.format == 'CBZ':
options.quality = 1 options.quality = 1
# Override profile data # Override profile data
if options.customwidth != 0 or options.customheight != 0: if options.customwidth != 0 or options.customheight != 0:
@@ -1069,12 +1081,35 @@ def checkOptions():
options.profileData = image.ProfileData.Profiles[options.profile] options.profileData = image.ProfileData.Profiles[options.profile]
def checkTools(source):
source = source.upper()
if source.endswith('.CBR') or source.endswith('.RAR'):
rarExitCode = Popen('unrar', stdout=PIPE, stderr=STDOUT, shell=True)
rarExitCode = rarExitCode.wait()
if rarExitCode != 0 and rarExitCode != 7:
print('\nUnRAR is missing!')
exit(1)
elif source.endswith('.CB7') or source.endswith('.7Z'):
sevenzaExitCode = Popen('7za', stdout=PIPE, stderr=STDOUT, shell=True)
sevenzaExitCode = sevenzaExitCode.wait()
if sevenzaExitCode != 0 and sevenzaExitCode != 7:
print('\n7za is missing!')
exit(1)
if options.format == 'MOBI':
kindleGenExitCode = Popen('kindlegen -locale en', stdout=PIPE, stderr=STDOUT, shell=True)
if kindleGenExitCode.wait() != 0:
print('\nKindleGen is missing!')
exit(1)
def makeBook(source, qtGUI=None): def makeBook(source, qtGUI=None):
"""Generates EPUB/CBZ comic ebook from a bunch of images.""" """Generates MOBI/EPUB/CBZ comic ebook from a bunch of images."""
global GUI global GUI
GUI = qtGUI GUI = qtGUI
if GUI: if GUI:
GUI.progressBarTick.emit('1') GUI.progressBarTick.emit('1')
else:
checkTools(source)
path = getWorkFolder(source) path = getWorkFolder(source)
print("\nChecking images...") print("\nChecking images...")
getComicInfo(os.path.join(path, "OEBPS", "Images"), source) getComicInfo(os.path.join(path, "OEBPS", "Images"), source)
@@ -1092,7 +1127,7 @@ def makeBook(source, qtGUI=None):
if GUI: if GUI:
GUI.progressBarTick.emit('1') GUI.progressBarTick.emit('1')
chapterNames = sanitizeTree(os.path.join(path, 'OEBPS', 'Images')) chapterNames = sanitizeTree(os.path.join(path, 'OEBPS', 'Images'))
if 'Ko' in options.profile and options.cbzoutput: 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:
tomes = splitDirectory(path) tomes = splitDirectory(path)
@@ -1101,7 +1136,7 @@ def makeBook(source, qtGUI=None):
filepath = [] filepath = []
tomeNumber = 0 tomeNumber = 0
if GUI: if GUI:
if options.cbzoutput: if options.format == 'CBZ':
GUI.progressBarTick.emit('Compressing CBZ files') GUI.progressBarTick.emit('Compressing CBZ files')
else: else:
GUI.progressBarTick.emit('Compressing EPUB files') GUI.progressBarTick.emit('Compressing EPUB files')
@@ -1112,8 +1147,7 @@ def makeBook(source, qtGUI=None):
if len(tomes) > 1: if len(tomes) > 1:
tomeNumber += 1 tomeNumber += 1
options.title = options.baseTitle + ' [' + str(tomeNumber) + '/' + str(len(tomes)) + ']' options.title = options.baseTitle + ' [' + str(tomeNumber) + '/' + str(len(tomes)) + ']'
if options.cbzoutput: if options.format == 'CBZ':
# if CBZ output wanted, compress all images and return filepath
print("\nCreating CBZ file...") print("\nCreating CBZ file...")
if len(tomes) > 1: if len(tomes) > 1:
filepath.append(getOutputFilename(source, options.output, '.cbz', ' ' + str(tomeNumber))) filepath.append(getOutputFilename(source, options.output, '.cbz', ' ' + str(tomeNumber)))
@@ -1121,9 +1155,8 @@ def makeBook(source, qtGUI=None):
filepath.append(getOutputFilename(source, options.output, '.cbz', '')) filepath.append(getOutputFilename(source, options.output, '.cbz', ''))
makeZIP(tome + '_comic', os.path.join(tome, "OEBPS", "Images")) makeZIP(tome + '_comic', os.path.join(tome, "OEBPS", "Images"))
else: else:
print("\nCreating EPUB structure...") print("\nCreating EPUB file...")
buildEPUB(tome, chapterNames, tomeNumber) buildEPUB(tome, chapterNames, tomeNumber)
# actually zip the ePub
if len(tomes) > 1: if len(tomes) > 1:
filepath.append(getOutputFilename(source, options.output, '.epub', ' ' + str(tomeNumber))) filepath.append(getOutputFilename(source, options.output, '.epub', ' ' + str(tomeNumber)))
else: else:
@@ -1133,4 +1166,92 @@ def makeBook(source, qtGUI=None):
rmtree(tome, True) rmtree(tome, True)
if GUI: if GUI:
GUI.progressBarTick.emit('tick') GUI.progressBarTick.emit('tick')
return filepath if not GUI and options.format == 'MOBI':
print("\nCreating MOBI file...")
work = []
for i in filepath:
work.append([i])
output = makeMOBI(work, GUI)
for errors in output:
if errors[0] != 0:
print('KINDLEGEN ERROR!')
print(errors)
return filepath
for i in filepath:
output = makeMOBIFix(i)
if not output[0]:
print('DUALMETAFIX ERROR!')
return filepath
else:
os.remove(i.replace('.epub', '.mobi') + '_toclean')
return filepath
def makeMOBIFix(item):
os.remove(item)
mobiPath = item.replace('.epub', '.mobi')
move(mobiPath, mobiPath + '_toclean')
try:
dualmetafix.DualMobiMetaFix(mobiPath + '_toclean', mobiPath, bytes(str(uuid4()), 'UTF-8'))
return [True]
except Exception as err:
return [False, format(err)]
def makeMOBIWorkerTick(output):
makeMOBIWorkerOutput.append(output)
if output[0] != 0:
makeMOBIWorkerPool.terminate()
if GUI:
GUI.progressBarTick.emit('tick')
if not GUI.conversionAlive:
makeMOBIWorkerPool.terminate()
def makeMOBIWorker(item):
item = item[0]
kindlegenErrorCode = 0
kindlegenError = ''
try:
if os.path.getsize(item) < 629145600:
output = Popen('kindlegen -dont_append_source -locale en "' + item + '"',
stdout=PIPE, stderr=STDOUT, shell=True)
for line in output.stdout:
line = line.decode('utf-8')
# ERROR: Generic error
if "Error(" in line:
kindlegenErrorCode = 1
kindlegenError = line
# ERROR: EPUB too big
if ":E23026:" in line:
kindlegenErrorCode = 23026
if kindlegenErrorCode > 0:
break
else:
# ERROR: EPUB too big
kindlegenErrorCode = 23026
return [kindlegenErrorCode, kindlegenError, item]
except Exception as err:
# ERROR: KCC unknown generic error
kindlegenErrorCode = 1
kindlegenError = format(err)
return [kindlegenErrorCode, kindlegenError, item]
def makeMOBI(work, qtGUI=None):
global GUI, makeMOBIWorkerPool, makeMOBIWorkerOutput
GUI = qtGUI
makeMOBIWorkerOutput = []
availableMemory = virtual_memory().total/1000000000
if availableMemory <= 2:
threadNumber = 1
elif 2 < availableMemory <= 4:
threadNumber = 2
else:
threadNumber = 4
makeMOBIWorkerPool = Pool(threadNumber)
for i in work:
makeMOBIWorkerPool.apply_async(func=makeMOBIWorker, args=(i, ), callback=makeMOBIWorkerTick)
makeMOBIWorkerPool.close()
makeMOBIWorkerPool.join()
return makeMOBIWorkerOutput

View File

@@ -18,7 +18,7 @@
# PERFORMANCE OF THIS SOFTWARE. # PERFORMANCE OF THIS SOFTWARE.
# #
__version__ = '4.2.1' __version__ = '4.3'
__license__ = 'ISC' __license__ = 'ISC'
__copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>' __copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
@@ -118,7 +118,6 @@ def splitImageTick(output):
splitWorkerPool.terminate() splitWorkerPool.terminate()
#noinspection PyUnboundLocalVariable
def splitImage(work): def splitImage(work):
try: try:
path = work[0] path = work[0]
@@ -165,6 +164,7 @@ def splitImage(work):
for panel in panelsCleaned: for panel in panelsCleaned:
panels.append(panel) panels.append(panel)
if opt.debug: if opt.debug:
# noinspection PyUnboundLocalVariable
debugImage.save(os.path.join(path, fileExpanded[0] + '-debug.png'), 'PNG') debugImage.save(os.path.join(path, fileExpanded[0] + '-debug.png'), 'PNG')
# Create virtual pages # Create virtual pages

View File

@@ -16,7 +16,7 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
__version__ = '4.2.1' __version__ = '4.3'
__license__ = 'ISC' __license__ = 'ISC'
__copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>' __copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
@@ -85,18 +85,18 @@ class ProfileData:
'K1': ("Kindle 1", (600, 670), Palette4, 1.8, (900, 1005)), 'K1': ("Kindle 1", (600, 670), Palette4, 1.8, (900, 1005)),
'K2': ("Kindle 2", (600, 670), Palette15, 1.8, (900, 1005)), 'K2': ("Kindle 2", (600, 670), Palette15, 1.8, (900, 1005)),
'K345': ("Kindle", (600, 800), Palette16, 1.8, (900, 1200)), 'K345': ("Kindle", (600, 800), Palette16, 1.8, (900, 1200)),
'KHD': ("Kindle Paperwhite", (758, 1024), Palette16, 1.8, (1137, 1536)),
'KDX': ("Kindle DX/DXG", (824, 1000), Palette16, 1.8, (1236, 1500)), 'KDX': ("Kindle DX/DXG", (824, 1000), Palette16, 1.8, (1236, 1500)),
'KF': ("Kindle Fire", (600, 1024), PalleteNull, 1.0, (900, 1536)), 'KPW': ("Kindle Paperwhite", (758, 1024), Palette16, 1.8, (1137, 1536)),
'KFHD': ("K. Fire HD 7\"", (800, 1280), PalleteNull, 1.0, (1200, 1920)), 'KV': ("Kindle Voyage", (1080, 1440), Palette16, 1.8, (1620, 2160)),
'KFHD8': ("K. Fire HD 8.9\"", (1200, 1920), PalleteNull, 1.0, (1800, 2880)), 'KFHD': ("K. Fire HD", (800, 1280), PalleteNull, 1.0, (1200, 1920)),
'KFHDX': ("K. Fire HDX 7\"", (1200, 1920), PalleteNull, 1.0, (1800, 2880)), 'KFHDX': ("K. Fire HDX", (1200, 1920), PalleteNull, 1.0, (1800, 2880)),
'KFHDX8': ("K. Fire HDX 8.9\"", (1600, 2560), PalleteNull, 1.0, (2400, 3840)), 'KFHDX8': ("K. Fire HDX 8.9", (1600, 2560), PalleteNull, 1.0, (2400, 3840)),
'KFA': ("Kindle for Android", (0, 0), PalleteNull, 1.0, (0, 0)),
'KoMT': ("Kobo Mini/Touch", (600, 800), Palette16, 1.8, (900, 1200)), 'KoMT': ("Kobo Mini/Touch", (600, 800), Palette16, 1.8, (900, 1200)),
'KoG': ("Kobo Glow", (768, 1024), Palette16, 1.8, (1152, 1536)), 'KoG': ("Kobo Glow", (768, 1024), Palette16, 1.8, (1152, 1536)),
'KoA': ("Kobo Aura", (758, 1024), Palette16, 1.8, (1137, 1536)), 'KoA': ("Kobo Aura", (758, 1024), Palette16, 1.8, (1137, 1536)),
'KoAHD': ("Kobo Aura HD", (1080, 1440), Palette16, 1.8, (1620, 2160)), 'KoAHD': ("Kobo Aura HD", (1080, 1440), Palette16, 1.8, (1620, 2160)),
'KFA': ("Kindle for Android", (0, 0), PalleteNull, 1.0, (0, 0)), 'KoAH2O': ("Kobo Aura H2O", (1080, 1430), Palette16, 1.8, (1620, 2145)),
'OTHER': ("Other", (0, 0), Palette16, 1.8, (0, 0)), 'OTHER': ("Other", (0, 0), Palette16, 1.8, (0, 0)),
} }

View File

@@ -1,12 +1,12 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
""" """
cx_Freeze/py2app build script for KCC. py2exe/py2app build script for KCC.
Usage (Mac OS X):
python setup.py py2app
Usage (Windows): Usage (Windows):
python setup.py py2exe python setup.py py2exe
Usage (Mac OS X):
python setup.py py2app
""" """
from sys import platform, version_info from sys import platform, version_info
if version_info[0] != 3: if version_info[0] != 3:
@@ -14,7 +14,7 @@ if version_info[0] != 3:
exit(1) exit(1)
NAME = "KindleComicConverter" NAME = "KindleComicConverter"
VERSION = "4.2.1" VERSION = "4.3"
MAIN = "kcc.py" MAIN = "kcc.py"
if platform == "darwin": if platform == "darwin":
@@ -63,43 +63,22 @@ elif platform == "win32":
suffix = '_64' suffix = '_64'
else: else:
suffix = '' suffix = ''
additional_files = [('imageformats', ['C:\Python34' + suffix + additional_files = [('platforms', ['C:\Python34' + suffix +
'\Lib\site-packages\PyQt5\plugins\imageformats\qgif.dll',
'C:\Python34' + suffix +
'\Lib\site-packages\PyQt5\plugins\imageformats\qico.dll',
'C:\Python34' + suffix +
'\Lib\site-packages\PyQt5\plugins\imageformats\qjpeg.dll',
'C:\Python34' + suffix +
'\Lib\site-packages\PyQt5\plugins\imageformats\qmng.dll',
'C:\Python34' + suffix +
'\Lib\site-packages\PyQt5\plugins\imageformats\qsvg.dll',
'C:\Python34' + suffix +
'\Lib\site-packages\PyQt5\plugins\imageformats\qtga.dll',
'C:\Python34' + suffix +
'\Lib\site-packages\PyQt5\plugins\imageformats\qtiff.dll',
'C:\Python34' + suffix +
'\Lib\site-packages\PyQt5\plugins\imageformats\qwbmp.dll']),
('platforms', ['C:\Python34' + suffix +
'\Lib\site-packages\PyQt5\plugins\platforms\qminimal.dll',
'C:\Python34' + suffix +
'\Lib\site-packages\PyQt5\plugins\platforms\qoffscreen.dll',
'C:\Python34' + suffix +
'\Lib\site-packages\PyQt5\plugins\platforms\qwindows.dll']), '\Lib\site-packages\PyQt5\plugins\platforms\qwindows.dll']),
('', ['LICENSE.txt', ('', ['LICENSE.txt',
'other\\7za.exe', 'other\\7za.exe',
'other\\UnRAR.exe', 'other\\UnRAR.exe',
'other\\Additional-LICENSE.txt', 'other\\Additional-LICENSE.txt',
'other\\7za.exe',
'C:\Python34' + suffix + '\Lib\site-packages\PyQt5\libEGL.dll'])] 'C:\Python34' + suffix + '\Lib\site-packages\PyQt5\libEGL.dll'])]
extra_options = dict( extra_options = dict(
options={'py2exe': {"bundle_files": 2, options={'py2exe': {"bundle_files": 1,
"dll_excludes": ["tcl85.dll", "tk85.dll"], "dll_excludes": ["tcl85.dll", "tk85.dll"],
"dist_dir": "dist" + suffix, "dist_dir": "dist" + suffix,
"compressed": True, "compressed": True,
"includes": ["sip"], "includes": ["sip"],
"excludes": ["tkinter"], "excludes": ["tkinter"],
"optimize": 2}}, "optimize": 2}},
windows=[{"script": "kcc.py", windows=[{"script": MAIN,
"dest_base": "KCC", "dest_base": "KCC",
"version": VERSION, "version": VERSION,
"copyright": "Ciro Mattia Gonano, Pawel Jastrzebski © 2014", "copyright": "Ciro Mattia Gonano, Pawel Jastrzebski © 2014",
@@ -114,7 +93,7 @@ else:
print('Please use setup.sh to build Linux package.') print('Please use setup.sh to build Linux package.')
exit() exit()
#noinspection PyUnboundLocalVariable # noinspection PyUnboundLocalVariable
setup( setup(
name=NAME, name=NAME,
version=VERSION, version=VERSION,

View File

@@ -1,7 +1,7 @@
#!/bin/bash #!/bin/bash
# Linux Python package build script # Linux Python package build script
VERSION="4.2.1" VERSION="4.3"
cp kcc.py __main__.py cp kcc.py __main__.py
zip kcc.zip __main__.py kcc/*.py zip kcc.zip __main__.py kcc/*.py