diff --git a/README.md b/README.md index f0f59af3..a8c9efe6 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,13 @@ # KCC -**Kindle Comic Converter** is a Python app to convert comic 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 -actually a comic to EPUB converter that every e-reader owner can happily use**_. +**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 2.2 it outputs valid EPUB 2.0 so _**despite its name, KCC is +actually a comic/manga to EPUB converter that every e-reader owner can happily use**_. It can also optionally optimize images by applying a number of transformations. ### 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. -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 ;-) ### Issues / new features / donations @@ -36,7 +36,7 @@ You can find the latest released binary at the following links: - CBZ, ZIP - CBR, RAR *(With `unrar` executable)* - CB7, 7Z *(With `7za` executable)* -- PDF *(Extracting only contained JPG images)* +- PDF *(Only extracting JPG images)* ## 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)* @@ -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. +CLI version of **KCC** is intended for power users. It is not idiot-proof like GUI :-) + ### Standalone `kcc-c2e.py` usage: ``` @@ -75,7 +77,9 @@ Usage: kcc-c2e [options] comic_file|comic_folder Options: MAIN: -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 Quality of Panel View. 0 - Normal 1 - High 2 - Ultra [Default=0] -m, --manga-style Manga style (Right-to-left reading and splitting) @@ -86,7 +90,9 @@ Options: Output generated file to specified directory or file -t TITLE, --title=TITLE 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 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)). -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. - `rarfile.py` script © 2005-2011 **Marko Kreen** . 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. ## 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](http://kcc.iosphe.re/Samples/Ubunchu!-K345.mobi) -* [Kindle DX/DXG](http://kcc.iosphe.re/Samples/Ubunchu!-KDX.mobi) -* [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) +* [Kindle DX/DXG](http://kcc.iosphe.re/Samples/Ubunchu!-KDX.cbz) * [Kobo Mini/Touch](http://kcc.iosphe.re/Samples/Ubunchu!-KoMT.cbz) * [Kobo Glow](http://kcc.iosphe.re/Samples/Ubunchu!-KoG.cbz) * [Kobo Aura](http://kcc.iosphe.re/Samples/Ubunchu!-KoA.cbz) * [Kobo Aura HD](http://kcc.iosphe.re/Samples/Ubunchu!-KoAHD.cbz) +* [Kobo Aura H2O](http://kcc.iosphe.re/Samples/Ubunchu!-KoAH2O.cbz) ## CHANGELOG ####1.0 @@ -188,11 +192,11 @@ The app relies and includes the following scripts/binaries: * Added basic error reporting ####2.2: -* Added (valid!) ePub 2.0 output +* Added (valid!) EPUB 2.0 output * Rename .zip files to .cbz to avoid overwriting ####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 * 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 * 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 * Added --rotate option to rotate landscape images instead of splitting them (#16, #24) -* Added --output option to customize ePub output dir/file (#22) -* Add rendition:layout and rendition:orientation ePub meta tags (supported by new kindlegen 2.8) +* Added --output option to customize EPUB output dir/file (#22) +* Add rendition:layout and rendition:orientation EPUB meta tags (supported by new kindlegen 2.8) * Fixed natural sorting for files (#18) ####2.7 * Lots of GUI improvements (#27, #13) * Added gamma support within --gamma option (defaults to profile-specified gamma) (#26, #27) * Added --nodithering option to prevent dithering optimizations (#27) -* Epub margins support (#30) +* EPUB margins support (#30) * Fixed no file added if file has no spaces on Windows (#25) * Gracefully exit if unrar missing (#15) -* Do not call kindlegen if source epub is bigger than 320MB (#17) +* Do not call kindlegen if source EPUB is bigger than 320MB (#17) * Get filetype from magic number (#14) * 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) ####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 * Rarfile library updated to 2.6 * Added GIF, TIFF and BMP to supported formats (#42) @@ -237,7 +241,7 @@ The app relies and includes the following scripts/binaries: ####2.10: * Multiprocessing support -* Kindle Fire support (color ePub/Mobi) +* Kindle Fire support (color EPUB/MOBI) * Panel View support for horizontal content * Fixed panel order for horizontal pages when --rotate is enabled * Disabled cropping and page number cutting for blank pages @@ -368,6 +372,11 @@ The app relies and includes the following scripts/binaries: * Fixed some MCD support bugs * 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 Please check [wiki page](https://github.com/ciromattia/kcc/wiki/Known-issues). diff --git a/kcc-c2e.py b/kcc-c2e.py index 466624dc..fea68dd9 100755 --- a/kcc-c2e.py +++ b/kcc-c2e.py @@ -18,7 +18,7 @@ # TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # PERFORMANCE OF THIS SOFTWARE. -__version__ = '4.2.1' +__version__ = '4.3' __license__ = 'ISC' __copyright__ = '2012-2014, Ciro Mattia Gonano , Pawel Jastrzebski ' __docformat__ = 'restructuredtext en' diff --git a/kcc-c2p.py b/kcc-c2p.py index 17034130..2112a1c7 100755 --- a/kcc-c2p.py +++ b/kcc-c2p.py @@ -18,7 +18,7 @@ # TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # PERFORMANCE OF THIS SOFTWARE. -__version__ = '4.2.1' +__version__ = '4.3' __license__ = 'ISC' __copyright__ = '2012-2014, Ciro Mattia Gonano , Pawel Jastrzebski ' __docformat__ = 'restructuredtext en' diff --git a/kcc.iss b/kcc.iss index 3d63f082..3f6e6197 100644 --- a/kcc.iss +++ b/kcc.iss @@ -1,5 +1,5 @@ #define MyAppName "Kindle Comic Converter" -#define MyAppVersion "4.2.1" +#define MyAppVersion "4.3" #define MyAppPublisher "Ciro Mattia Gonano, Paweł Jastrzębski" #define MyAppURL "http://kcc.iosphe.re/" #define MyAppExeName "KCC.exe" @@ -43,13 +43,11 @@ Name: "CB7association"; Description: "CB7"; GroupDescription: "File associations [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\{#MyAppExeName}"; 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 ; 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\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion; Check: not Is64BitInstallMode Source: "dist\*.dll"; DestDir: "{app}"; Flags: ignoreversion; Check: not Is64BitInstallMode diff --git a/kcc.py b/kcc.py index a0d5fa2f..c59f2ad8 100755 --- a/kcc.py +++ b/kcc.py @@ -18,7 +18,7 @@ # TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # PERFORMANCE OF THIS SOFTWARE. -__version__ = '4.2.1' +__version__ = '4.3' __license__ = 'ISC' __copyright__ = '2012-2014, Ciro Mattia Gonano , Pawel Jastrzebski ' __docformat__ = 'restructuredtext en' @@ -84,14 +84,14 @@ os.chdir(os.path.dirname(os.path.abspath(sys.executable))) # Implementing dummy stdout and stderr for frozen Windows release - class fakestd(object): + class FakeSTD(object): def write(self, string): pass def flush(self): pass - sys.stdout = fakestd() - sys.stderr = fakestd() + sys.stdout = FakeSTD() + sys.stderr = FakeSTD() else: os.environ['PATH'] = os.path.dirname(os.path.abspath(__file__)) + '/other/;' + os.environ['PATH'] os.chdir(os.path.dirname(os.path.abspath(__file__))) @@ -103,36 +103,33 @@ class QApplicationMessaging(QtWidgets.QApplication): def __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._timeout = 1000 - self._server = QtNetwork.QLocalServer(self) - if not self.isRunning(): + self._locked = False + socket = QtNetwork.QLocalSocket(self) + socket.connectToServer(self._key, QtCore.QIODevice.WriteOnly) + if not socket.waitForConnected(self._timeout): + self._server = QtNetwork.QLocalServer(self) # noinspection PyUnresolvedReferences self._server.newConnection.connect(self.handleMessage) self._server.listen(self._key) + else: + self._locked = True + socket.disconnectFromServer() - def shutdown(self): - if self._memory.isAttached(): - self._memory.detach() + def __del__(self): + if not self._locked: self._server.close() def event(self, e): if e.type() == QtCore.QEvent.FileOpen: - # noinspection PyArgumentList self.messageFromOtherInstance.emit(bytes(e.file(), 'UTF-8')) return True else: return QtWidgets.QApplication.event(self, e) def isRunning(self): - return self._running + return self._locked def handleMessage(self): socket = self._server.nextPendingConnection() @@ -140,18 +137,12 @@ def handleMessage(self): self.messageFromOtherInstance.emit(socket.readAll().data()) def sendMessage(self, message): - if self.isRunning(): - socket = QtNetwork.QLocalSocket(self) - socket.connectToServer(self._key, QtCore.QIODevice.WriteOnly) - if not socket.waitForConnected(self._timeout): - return False - # noinspection PyArgumentList - socket.write(bytes(message, 'UTF-8')) - if not socket.waitForBytesWritten(self._timeout): - return False - socket.disconnectFromServer() - return True - return False + socket = QtNetwork.QLocalSocket(self) + socket.connectToServer(self._key, QtCore.QIODevice.WriteOnly) + socket.waitForConnected(self._timeout) + socket.write(bytes(message, 'UTF-8')) + socket.waitForBytesWritten(self._timeout) + socket.disconnectFromServer() # Adding signals to QMainWindow diff --git a/kcc/KCC_gui.py b/kcc/KCC_gui.py index 9ddace4b..1cb465f6 100644 --- a/kcc/KCC_gui.py +++ b/kcc/KCC_gui.py @@ -17,7 +17,7 @@ # TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # PERFORMANCE OF THIS SOFTWARE. -__version__ = '4.2.1' +__version__ = '4.3' __license__ = 'ISC' __copyright__ = '2012-2014, Ciro Mattia Gonano , Pawel Jastrzebski ' __docformat__ = 'restructuredtext en' @@ -36,12 +36,10 @@ from PyQt5 import QtGui, QtCore, QtWidgets from xml.dom.minidom import parse from html.parser import HTMLParser -from psutil import virtual_memory, Popen, Process -from uuid import uuid4 +from psutil import Popen, Process from copy import copy from .shared import md5Checksum from . import comic2ebook -from . import dualmetafix from . import KCC_rc_web if sys.platform.startswith('darwin'): from . import KCC_ui_osx as KCC_ui @@ -265,84 +263,14 @@ def stop(self): 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): - #noinspection PyArgumentList + # noinspection PyArgumentList def __init__(self): QtCore.QThread.__init__(self) - self.pool = QtCore.QThreadPool() self.conversionAlive = False self.errors = False self.kindlegenErrorCode = [0] 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.addMessage = MW.addMessage @@ -361,9 +289,11 @@ def clean(self): MW.addTrayMessage.emit('Conversion interrupted.', 'Critical') MW.modeConvert.emit(1) - def addResult(self, output): - MW.progressBarTick.emit('tick') - self.workerOutput.append(output) + def sanitizeTrace(self, traceback): + return ''.join(format_tb(traceback))\ + .replace('C:\\Users\\AcidWeb\\Documents\\Projekty\\KCC\\', '')\ + .replace('C:\\Python34\\', '')\ + .replace('C:\\Python34_64\\', '') def run(self): MW.modeConvert.emit(0) @@ -385,10 +315,9 @@ def run(self): options.quality = 1 elif GUI.QualityBox.checkState() == 2: options.quality = 2 - if str(GUI.FormatBox.currentText()) == 'CBZ': - options.cbzoutput = True + options.format = str(GUI.FormatBox.currentText()) if GUI.currentMode == 1: - if profile in ['KFHD', 'KFHD8', 'KFHDX', 'KFHDX8']: + if 'KFH' in profile: options.upscale = True # Advanced mode settings @@ -410,10 +339,7 @@ def run(self): if GUI.WebtoonBox.isChecked(): options.webtoon = True if float(GUI.GammaValue) > 0.09: - # noinspection PyTypeChecker options.gamma = float(GUI.GammaValue) - if str(GUI.FormatBox.currentText()) == 'MOBI': - options.batchsplit = True # Other/custom settings. if GUI.currentMode > 2: @@ -462,7 +388,7 @@ def run(self): self.errors = True _, _, traceback = sys.exc_info() 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.addTrayMessage.emit('Failed to create EPUB!', 'Critical') if not self.conversionAlive: @@ -483,16 +409,10 @@ def run(self): MW.progressBarTick.emit('tick') MW.addMessage.emit('Creating MOBI files', 'info', False) GUI.progress.content = 'Creating MOBI files' - self.workerOutput = [] - # Number of KindleGen threads depends on the size of RAM - self.pool.setMaxThreadCount(self.threadNumber) + work = [] for item in outputPath: - worker = KindleGenThread(item) - worker.signals.result.connect(self.addResult) - self.pool.start(worker) - self.pool.waitForDone() - while len(self.workerOutput) != len(outputPath): - sleep(0.1) + work.append([item]) + self.workerOutput = comic2ebook.makeMOBI(work, self) self.kindlegenErrorCode = [0] for errors in self.workerOutput: if errors[0] != 0: @@ -512,15 +432,9 @@ def run(self): MW.addMessage.emit('Processing MOBI files', 'info', False) GUI.progress.content = 'Processing MOBI files' self.workerOutput = [] - # DualMetaFix is very fast and there is not reason to use multithreading. - self.pool.setMaxThreadCount(1) for item in outputPath: - worker = DualMetaFixThread(item) - worker.signals.result.connect(self.addResult) - self.pool.start(worker) - self.pool.waitForDone() - while len(self.workerOutput) != len(outputPath): - sleep(0.1) + self.workerOutput.append(comic2ebook.makeMOBIFix(item)) + MW.progressBarTick.emit('tick') for success in self.workerOutput: if not success[0]: self.errors = True @@ -1018,7 +932,6 @@ def saveSettings(self, event): 'GammaSlider': float(self.GammaValue)*100}) self.settings.sync() self.tray.hide() - APP.shutdown() def handleMessage(self, message): MW.raise_() @@ -1121,22 +1034,20 @@ def __init__(self, KCCAplication, KCCWindow): self.p.ionice(1) self.profiles = { + "Kindle Voyage": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0, + 'DefaultUpscale': False, 'Label': 'KV'}, "Kindle Paperwhite": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0, - 'DefaultUpscale': False, 'Label': 'KHD'}, + 'DefaultUpscale': False, 'Label': 'KPW'}, "Kindle": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': False, 'Label': 'K345'}, "Kindle DX/DXG": {'Quality': False, 'ForceExpert': False, 'DefaultFormat': 2, 'DefaultUpscale': False, 'Label': 'KDX'}, - "Kindle Fire": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0, - 'DefaultUpscale': False, 'Label': 'KF'}, - "K. Fire HD 7\"": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0, - 'DefaultUpscale': True, 'Label': 'KFHD'}, - "K. Fire HD 8.9\"": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0, - 'DefaultUpscale': True, 'Label': 'KFHD8'}, - "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'}, + "K. Fire HD": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0, + 'DefaultUpscale': True, 'Label': 'KFHD'}, + "K. Fire HDX": {'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, 'DefaultUpscale': False, 'Label': 'KoMT'}, "Kobo Glow": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 2, @@ -1145,6 +1056,8 @@ def __init__(self, KCCAplication, KCCWindow): 'DefaultUpscale': False, 'Label': 'KoA'}, "Kobo Aura HD": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 2, 'DefaultUpscale': False, 'Label': 'KoAHD'}, + "Kobo Aura H2O": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 2, + 'DefaultUpscale': False, 'Label': 'KoAH2O'}, "Other": {'Quality': False, 'ForceExpert': True, 'DefaultFormat': 1, 'DefaultUpscale': False, 'Label': 'OTHER'}, "Kindle for Android": {'Quality': False, 'ForceExpert': True, 'DefaultFormat': 0, @@ -1155,26 +1068,26 @@ def __init__(self, KCCAplication, KCCWindow): 'DefaultUpscale': False, 'Label': 'K2'} } profilesGUI = [ + "Kindle Voyage", "Kindle Paperwhite", "Kindle", - "Kindle DX/DXG", "Separator", - "Kindle Fire", - "K. Fire HD 7\"", - "K. Fire HD 8.9\"", - "K. Fire HDX 7\"", - "K. Fire HDX 8.9\"", + "K. Fire HD", + "K. Fire HDX", + "K. Fire HDX 8.9", "Separator", "Kobo Mini/Touch", "Kobo Glow", "Kobo Aura", "Kobo Aura HD", + "Kobo Aura H2O", "Separator", "Other", "Separator", "Kindle for Android", "Kindle 1", "Kindle 2", + "Kindle DX/DXG", ] statusBarLabel = QtWidgets.QLabel('HOMEPAGE - 1: tomeNumber += 1 options.title = options.baseTitle + ' [' + str(tomeNumber) + '/' + str(len(tomes)) + ']' - if options.cbzoutput: - # if CBZ output wanted, compress all images and return filepath + if options.format == 'CBZ': print("\nCreating CBZ file...") if len(tomes) > 1: 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', '')) makeZIP(tome + '_comic', os.path.join(tome, "OEBPS", "Images")) else: - print("\nCreating EPUB structure...") + print("\nCreating EPUB file...") buildEPUB(tome, chapterNames, tomeNumber) - # actually zip the ePub if len(tomes) > 1: filepath.append(getOutputFilename(source, options.output, '.epub', ' ' + str(tomeNumber))) else: @@ -1133,4 +1166,92 @@ def makeBook(source, qtGUI=None): rmtree(tome, True) if GUI: GUI.progressBarTick.emit('tick') - return filepath \ No newline at end of file + 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 \ No newline at end of file diff --git a/kcc/comic2panel.py b/kcc/comic2panel.py index e0e91910..72d2f72d 100644 --- a/kcc/comic2panel.py +++ b/kcc/comic2panel.py @@ -18,7 +18,7 @@ # PERFORMANCE OF THIS SOFTWARE. # -__version__ = '4.2.1' +__version__ = '4.3' __license__ = 'ISC' __copyright__ = '2012-2014, Ciro Mattia Gonano , Pawel Jastrzebski ' __docformat__ = 'restructuredtext en' @@ -118,7 +118,6 @@ def splitImageTick(output): splitWorkerPool.terminate() -#noinspection PyUnboundLocalVariable def splitImage(work): try: path = work[0] @@ -165,6 +164,7 @@ def splitImage(work): for panel in panelsCleaned: panels.append(panel) if opt.debug: + # noinspection PyUnboundLocalVariable debugImage.save(os.path.join(path, fileExpanded[0] + '-debug.png'), 'PNG') # Create virtual pages diff --git a/kcc/image.py b/kcc/image.py index a04b2de8..6a0aad85 100755 --- a/kcc/image.py +++ b/kcc/image.py @@ -16,7 +16,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -__version__ = '4.2.1' +__version__ = '4.3' __license__ = 'ISC' __copyright__ = '2012-2014, Ciro Mattia Gonano , Pawel Jastrzebski ' __docformat__ = 'restructuredtext en' @@ -85,18 +85,18 @@ def __init__(self): 'K1': ("Kindle 1", (600, 670), Palette4, 1.8, (900, 1005)), 'K2': ("Kindle 2", (600, 670), Palette15, 1.8, (900, 1005)), '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)), - 'KF': ("Kindle Fire", (600, 1024), PalleteNull, 1.0, (900, 1536)), - 'KFHD': ("K. Fire HD 7\"", (800, 1280), PalleteNull, 1.0, (1200, 1920)), - 'KFHD8': ("K. Fire HD 8.9\"", (1200, 1920), PalleteNull, 1.0, (1800, 2880)), - 'KFHDX': ("K. Fire HDX 7\"", (1200, 1920), PalleteNull, 1.0, (1800, 2880)), - 'KFHDX8': ("K. Fire HDX 8.9\"", (1600, 2560), PalleteNull, 1.0, (2400, 3840)), + 'KPW': ("Kindle Paperwhite", (758, 1024), Palette16, 1.8, (1137, 1536)), + 'KV': ("Kindle Voyage", (1080, 1440), Palette16, 1.8, (1620, 2160)), + 'KFHD': ("K. Fire HD", (800, 1280), PalleteNull, 1.0, (1200, 1920)), + 'KFHDX': ("K. Fire HDX", (1200, 1920), PalleteNull, 1.0, (1800, 2880)), + '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)), 'KoG': ("Kobo Glow", (768, 1024), Palette16, 1.8, (1152, 1536)), 'KoA': ("Kobo Aura", (758, 1024), Palette16, 1.8, (1137, 1536)), '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)), } diff --git a/setup.py b/setup.py index f2a2828b..396d6d7b 100755 --- a/setup.py +++ b/setup.py @@ -1,12 +1,12 @@ #!/usr/bin/env python3 """ -cx_Freeze/py2app build script for KCC. - -Usage (Mac OS X): - python setup.py py2app +py2exe/py2app build script for KCC. Usage (Windows): python setup.py py2exe + +Usage (Mac OS X): + python setup.py py2app """ from sys import platform, version_info if version_info[0] != 3: @@ -14,7 +14,7 @@ exit(1) NAME = "KindleComicConverter" -VERSION = "4.2.1" +VERSION = "4.3" MAIN = "kcc.py" if platform == "darwin": @@ -63,43 +63,22 @@ suffix = '_64' else: suffix = '' - additional_files = [('imageformats', ['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 + + additional_files = [('platforms', ['C:\Python34' + suffix + '\Lib\site-packages\PyQt5\plugins\platforms\qwindows.dll']), ('', ['LICENSE.txt', 'other\\7za.exe', 'other\\UnRAR.exe', 'other\\Additional-LICENSE.txt', - 'other\\7za.exe', 'C:\Python34' + suffix + '\Lib\site-packages\PyQt5\libEGL.dll'])] extra_options = dict( - options={'py2exe': {"bundle_files": 2, + options={'py2exe': {"bundle_files": 1, "dll_excludes": ["tcl85.dll", "tk85.dll"], "dist_dir": "dist" + suffix, "compressed": True, "includes": ["sip"], "excludes": ["tkinter"], "optimize": 2}}, - windows=[{"script": "kcc.py", + windows=[{"script": MAIN, "dest_base": "KCC", "version": VERSION, "copyright": "Ciro Mattia Gonano, Pawel Jastrzebski © 2014", @@ -114,7 +93,7 @@ print('Please use setup.sh to build Linux package.') exit() -#noinspection PyUnboundLocalVariable +# noinspection PyUnboundLocalVariable setup( name=NAME, version=VERSION, diff --git a/setup.sh b/setup.sh index eacc5c4b..e1947dd1 100755 --- a/setup.sh +++ b/setup.sh @@ -1,7 +1,7 @@ #!/bin/bash # Linux Python package build script -VERSION="4.2.1" +VERSION="4.3" cp kcc.py __main__.py zip kcc.zip __main__.py kcc/*.py