about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--README.md9
-rw-r--r--kcc/KCC_gui.py106
-rwxr-xr-xkcc/comic2ebook.py129
3 files changed, 129 insertions, 115 deletions
diff --git a/README.md b/README.md
index d1fe1e3..828abd7 100644
--- a/README.md
+++ b/README.md
@@ -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, KPW, KV, KFHD, KFHDX, KFHDX8, KFA, KoMT, KoG, KoA, KoAHD, KoAH2O) [Default=KV]
+                        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,8 @@ 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:
diff --git a/kcc/KCC_gui.py b/kcc/KCC_gui.py
index 2220ff4..40f6e73 100644
--- a/kcc/KCC_gui.py
+++ b/kcc/KCC_gui.py
@@ -36,12 +36,10 @@ from subprocess import STDOUT, PIPE
 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 @@ class ProgressThread(QtCore.QThread):
         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
     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,10 +289,6 @@ class WorkerThread(QtCore.QThread):
         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\\', '')\
@@ -391,8 +315,7 @@ class WorkerThread(QtCore.QThread):
             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 'KFH' in profile:
                 options.upscale = True
@@ -416,10 +339,7 @@ class WorkerThread(QtCore.QThread):
             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:
@@ -489,16 +409,10 @@ class WorkerThread(QtCore.QThread):
                     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:
@@ -518,15 +432,9 @@ class WorkerThread(QtCore.QThread):
                         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
diff --git a/kcc/comic2ebook.py b/kcc/comic2ebook.py
index 0ee22e9..5bec740 100755
--- a/kcc/comic2ebook.py
+++ b/kcc/comic2ebook.py
@@ -38,6 +38,8 @@ from xml.dom.minidom import parse
 from uuid import uuid4
 from slugify import slugify as slugifyExt
 from PIL import Image
+from subprocess import STDOUT, PIPE
+from psutil import Popen, virtual_memory
 try:
     from PyQt5 import QtCore
 except ImportError:
@@ -47,6 +49,7 @@ from . import comic2panel
 from . import image
 from . import cbxarchive
 from . import pdfjpgextract
+from . import dualmetafix
 
 
 def main(argv=None):
@@ -955,8 +958,8 @@ def makeParser():
     otherOptions = OptionGroup(psr, "OTHER")
 
     mainOptions.add_option("-p", "--profile", action="store", dest="profile", default="KV",
-                           help="Device profile (Choose one among K1, K2, K345, KDX, KPW, KV, KFHD, KFHDX, KFHDX8, KFA,"
-                                " KoMT, KoG, KoA, KoAHD, KoAH2O) [Default=KV]")
+                           help="Device profile (Available options: K1, K2, K345, KDX, KPW, KV, KFHD, KFHDX, KFHDX8,"
+                                " KFA, KoMT, KoG, KoA, KoAHD, KoAH2O) [Default=KV]")
     mainOptions.add_option("-q", "--quality", type="int", dest="quality", 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,
@@ -968,8 +971,8 @@ def makeParser():
                              help="Output generated file to specified directory or file")
     outputOptions.add_option("-t", "--title", action="store", dest="title", default="defaulttitle",
                              help="Comic title [Default=filename or directory name]")
-    outputOptions.add_option("--cbz-output", action="store_true", dest="cbzoutput", default=False,
-                             help="Outputs a CBZ archive and does not generate EPUB")
+    outputOptions.add_option("-f", "--format", action="store", dest="format", default="Auto",
+                             help="Output format (Available options: Auto, MOBI, EPUB, CBZ) [Default=Auto]")
     outputOptions.add_option("--batchsplit", action="store_true", dest="batchsplit", default=False,
                              help="Split output into multiple files"),
 
@@ -1016,10 +1019,20 @@ def checkOptions():
     global options
     options.panelview = True
     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:
-        options.bordersColor = "white"
+        options.bordersColor = 'white'
     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.
     if 'KFH' in options.profile or options.forcecolor:
         options.forcecolor = True
@@ -1048,10 +1061,10 @@ def checkOptions():
         print("ERROR: Kindle for Android profile require --customwidth and --customheight options!")
         sys.exit(1)
     # 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
     # 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
     # Override profile data
     if options.customwidth != 0 or options.customheight != 0:
@@ -1069,7 +1082,7 @@ def checkOptions():
 
 
 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
     GUI = qtGUI
     if GUI:
@@ -1091,7 +1104,7 @@ def makeBook(source, qtGUI=None):
     if GUI:
         GUI.progressBarTick.emit('1')
     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'))
     if options.batchsplit:
         tomes = splitDirectory(path)
@@ -1100,7 +1113,7 @@ def makeBook(source, qtGUI=None):
     filepath = []
     tomeNumber = 0
     if GUI:
-        if options.cbzoutput:
+        if options.format == 'CBZ':
             GUI.progressBarTick.emit('Compressing CBZ files')
         else:
             GUI.progressBarTick.emit('Compressing EPUB files')
@@ -1111,7 +1124,7 @@ def makeBook(source, qtGUI=None):
         if len(tomes) > 1:
             tomeNumber += 1
             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...")
             if len(tomes) > 1:
@@ -1120,7 +1133,7 @@ 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:
@@ -1132,4 +1145,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