about summary refs log tree commit diff
diff options
context:
space:
mode:
authorPaweł Jastrzębski <[email protected]>2016-11-26 18:13:08 +0100
committerGitHub <[email protected]>2016-11-26 18:13:08 +0100
commitd76eea9f4342aa2ef9b9bf80705aa34da7a625d5 (patch)
treeedf436fcb39dc152fac7f71ecbf6947a97401b7d
parentMerge pull request #215 from ciromattia/dev (diff)
parentUpdated README + version bump (diff)
downloadkcc-d76eea9f4342aa2ef9b9bf80705aa34da7a625d5.tar.gz
kcc-d76eea9f4342aa2ef9b9bf80705aa34da7a625d5.tar.bz2
kcc-d76eea9f4342aa2ef9b9bf80705aa34da7a625d5.zip
Merge pull request #216 from ciromattia/dev
5.2.1
-rw-r--r--README.md10
-rw-r--r--gui/KCC.ui8
-rw-r--r--kcc.iss2
-rw-r--r--kcc/KCC_gui.py30
-rw-r--r--kcc/KCC_ui.py14
-rw-r--r--kcc/__init__.py2
-rwxr-xr-xkcc/comic2ebook.py147
-rw-r--r--kcc/comic2panel.py10
-rwxr-xr-xkcc/image.py2
-rw-r--r--kcc/pdfjpgextract.py1
-rw-r--r--kcc/shared.py5
-rw-r--r--other/osx/Info.plist6
12 files changed, 94 insertions, 143 deletions
diff --git a/README.md b/README.md
index cfaf214..3f8b676 100644
--- a/README.md
+++ b/README.md
@@ -90,7 +90,10 @@ Options:
     -f FORMAT, --format=FORMAT
                         Output format (Available options: Auto, MOBI, EPUB,
                         CBZ) [Default=Auto]
-    -b, --batchsplit    Split output into multiple files
+    -b BATCHSPLIT, --batchsplit=BATCHSPLIT
+                        Split output into multiple files. 0: Don't split 1:
+                        Automatic mode 2: Consider every subdirectory as
+                        separate volume [Default=0]
 
   PROCESSING:
     -u, --upscale       Resize images smaller than device's resolution
@@ -160,6 +163,11 @@ The app relies and includes the following scripts:
 * [Kobo Aura ONE](http://kcc.iosphe.re/Samples/Ubunchu-KoAO.kepub.epub)
 
 ## CHANGELOG
+####5.2.1:
+* Improved directory parsing
+* Tweaked margin detection algorithm
+* Improved error reporting
+
 ####5.2:
 * Added new Panel View options
 * Implemented new margin detection algorithm
diff --git a/gui/KCC.ui b/gui/KCC.ui
index 3bdd917..0b94d42 100644
--- a/gui/KCC.ui
+++ b/gui/KCC.ui
@@ -228,12 +228,12 @@
         </widget>
        </item>
        <item row="2" column="1">
-        <widget class="QCheckBox" name="noDitheringBox">
+        <widget class="QCheckBox" name="outputSplit">
          <property name="toolTip">
-          <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Create PNG files instead JPEG.&lt;br/&gt;Quality increase is not noticeable on most of devices.&lt;br/&gt;Output files &lt;span style=&quot; font-weight:600;&quot;&gt;might&lt;/span&gt; be smaller.&lt;br/&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;MOBI conversion will be much slower.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+          <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Unchecked - Automatic mode&lt;br/&gt;&lt;/span&gt;Output will be splitted automatically.&lt;/p&gt;&lt;p style='white-space:pre'&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Checked - Volume mode&lt;br/&gt;&lt;/span&gt;Every subdirectory will be considered as separate volume.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
          </property>
          <property name="text">
-          <string>PNG output</string>
+          <string>Output split</string>
          </property>
         </widget>
        </item>
@@ -510,7 +510,7 @@
   <tabstop>upscaleBox</tabstop>
   <tabstop>gammaBox</tabstop>
   <tabstop>borderBox</tabstop>
-  <tabstop>noDitheringBox</tabstop>
+  <tabstop>outputSplit</tabstop>
   <tabstop>colorBox</tabstop>
   <tabstop>editorButton</tabstop>
   <tabstop>wikiButton</tabstop>
diff --git a/kcc.iss b/kcc.iss
index 28a7317..9f0e8e6 100644
--- a/kcc.iss
+++ b/kcc.iss
@@ -1,5 +1,5 @@
 #define MyAppName "Kindle Comic Converter"
-#define MyAppVersion "5.2"
+#define MyAppVersion "5.2.1"
 #define MyAppPublisher "Ciro Mattia Gonano, Paweł Jastrzębski"
 #define MyAppURL "http://kcc.iosphe.re/"
 #define MyAppExeName "KCC.exe"
diff --git a/kcc/KCC_gui.py b/kcc/KCC_gui.py
index dcc9e1b..b60c2be 100644
--- a/kcc/KCC_gui.py
+++ b/kcc/KCC_gui.py
@@ -21,17 +21,15 @@ import os
 import sys
 from urllib.parse import unquote
 from urllib.request import urlopen, urlretrieve, Request
-from time import sleep, time
-from datetime import datetime
+from time import sleep
 from shutil import move
 from subprocess import STDOUT, PIPE
 from PyQt5 import QtGui, QtCore, QtWidgets, QtNetwork
-from xml.dom.minidom import parse, Document
+from xml.dom.minidom import parse
 from psutil import Popen, Process
 from copy import copy
 from distutils.version import StrictVersion
 from xml.sax.saxutils import escape
-from platform import platform
 from raven import Client
 from .shared import md5Checksum, HTMLStripper, sanitizeTrace, saferRemove
 from . import __version__
@@ -270,8 +268,8 @@ class WorkerThread(QtCore.QThread):
             options.white_borders = True
         elif GUI.borderBox.checkState() == 2:
             options.black_borders = True
-        if GUI.noDitheringBox.isChecked():
-            options.forcepng = True
+        if GUI.outputSplit.isChecked():
+            options.batchsplit = 2
         if GUI.colorBox.isChecked():
             options.forcecolor = True
         if GUI.currentMode > 2:
@@ -319,10 +317,15 @@ class WorkerThread(QtCore.QThread):
                 GUI.progress.content = ''
                 self.errors = True
                 _, _, traceback = sys.exc_info()
+                if len(err.args) == 1:
+                    MW.showDialog.emit("Error during conversion %s:\n\n%s\n\nTraceback:\n%s"
+                                       % (jobargv[-1], str(err), sanitizeTrace(traceback)), 'error')
+                else:
+                    MW.showDialog.emit("Error during conversion %s:\n\n%s\n\nTraceback:\n%s"
+                                       % (jobargv[-1], str(err.args[0]), err.args[1]), 'error')
+                    GUI.sentry.extra_context({'realTraceback': err.args[1]})
                 if ' is corrupted.' not in str(err):
                     GUI.sentry.captureException()
-                MW.showDialog.emit("Error during conversion %s:\n\n%s\n\nTraceback:\n%s"
-                                   % (jobargv[-1], str(err), sanitizeTrace(traceback)), 'error')
                 MW.addMessage.emit('Error during conversion! Please consult '
                                    '<a href="https://github.com/ciromattia/kcc/wiki/Error-messages">wiki</a> '
                                    'for more details.', 'error', False)
@@ -528,7 +531,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
     def clearJobs(self):
         GUI.jobList.clear()
 
-    # noinspection PyCallByClass,PyTypeChecker,PyArgumentList
+    # noinspection PyCallByClass,PyTypeChecker
     def openWiki(self):
         QtGui.QDesktopServices.openUrl(QtCore.QUrl('https://github.com/ciromattia/kcc/wiki'))
 
@@ -646,6 +649,11 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
         else:
             GUI.formatBox.setCurrentIndex(profile['DefaultFormat'])
         GUI.qualityBox.setEnabled(profile['PVOptions'])
+        if str(GUI.formatBox.currentText()) == 'MOBI/AZW3':
+            GUI.outputSplit.setEnabled(True)
+        else:
+            GUI.outputSplit.setEnabled(False)
+            GUI.outputSplit.setChecked(False)
 
     def stripTags(self, html):
         s = HTMLStripper()
@@ -700,7 +708,6 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
             self.conversionAlive = False
             self.worker.sync()
         else:
-            # noinspection PyArgumentList
             if QtWidgets.QApplication.keyboardModifiers() == QtCore.Qt.ShiftModifier:
                 dname = QtWidgets.QFileDialog.getExistingDirectory(MW, 'Select output directory', self.lastPath)
                 if dname != '':
@@ -762,7 +769,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
                                            'upscaleBox': GUI.upscaleBox.checkState(),
                                            'borderBox': GUI.borderBox.checkState(),
                                            'webtoonBox': GUI.webtoonBox.checkState(),
-                                           'noDitheringBox': GUI.noDitheringBox.checkState(),
+                                           'outputSplit': GUI.outputSplit.checkState(),
                                            'colorBox': GUI.colorBox.checkState(),
                                            'widthBox': GUI.widthBox.value(),
                                            'heightBox': GUI.heightBox.value(),
@@ -848,7 +855,6 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
                 else:
                     self.addMessage('Download it and place executable in /usr/local/bin directory.', 'error')
 
-    # noinspection PyArgumentList
     def __init__(self, KCCAplication, KCCWindow):
         global APP, MW, GUI
         APP = KCCAplication
diff --git a/kcc/KCC_ui.py b/kcc/KCC_ui.py
index 5e718ef..6e2ed50 100644
--- a/kcc/KCC_ui.py
+++ b/kcc/KCC_ui.py
@@ -97,9 +97,9 @@ class Ui_mainWindow(object):
         self.borderBox.setTristate(True)
         self.borderBox.setObjectName("borderBox")
         self.gridLayout_2.addWidget(self.borderBox, 2, 0, 1, 1)
-        self.noDitheringBox = QtWidgets.QCheckBox(self.optionWidget)
-        self.noDitheringBox.setObjectName("noDitheringBox")
-        self.gridLayout_2.addWidget(self.noDitheringBox, 2, 1, 1, 1)
+        self.outputSplit = QtWidgets.QCheckBox(self.optionWidget)
+        self.outputSplit.setObjectName("outputSplit")
+        self.gridLayout_2.addWidget(self.outputSplit, 2, 1, 1, 1)
         self.colorBox = QtWidgets.QCheckBox(self.optionWidget)
         self.colorBox.setObjectName("colorBox")
         self.gridLayout_2.addWidget(self.colorBox, 2, 2, 1, 1)
@@ -219,8 +219,8 @@ class Ui_mainWindow(object):
         mainWindow.setTabOrder(self.webtoonBox, self.upscaleBox)
         mainWindow.setTabOrder(self.upscaleBox, self.gammaBox)
         mainWindow.setTabOrder(self.gammaBox, self.borderBox)
-        mainWindow.setTabOrder(self.borderBox, self.noDitheringBox)
-        mainWindow.setTabOrder(self.noDitheringBox, self.colorBox)
+        mainWindow.setTabOrder(self.borderBox, self.outputSplit)
+        mainWindow.setTabOrder(self.outputSplit, self.colorBox)
         mainWindow.setTabOrder(self.colorBox, self.editorButton)
         mainWindow.setTabOrder(self.editorButton, self.wikiButton)
         mainWindow.setTabOrder(self.wikiButton, self.jobList)
@@ -251,8 +251,8 @@ class Ui_mainWindow(object):
         self.gammaBox.setText(_translate("mainWindow", "Custom gamma"))
         self.borderBox.setToolTip(_translate("mainWindow", "<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Autodetection<br/></span>Color of margins fill will be detected automatically.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - White<br/></span>Margins will be filled with white color.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Black<br/></span>Margins will be filled with black color.</p></body></html>"))
         self.borderBox.setText(_translate("mainWindow", "W/B margins"))
-        self.noDitheringBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Create PNG files instead JPEG.<br/>Quality increase is not noticeable on most of devices.<br/>Output files <span style=\" font-weight:600;\">might</span> be smaller.<br/><span style=\" font-weight:600;\">MOBI conversion will be much slower.</span></p></body></html>"))
-        self.noDitheringBox.setText(_translate("mainWindow", "PNG output"))
+        self.outputSplit.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Automatic mode<br/></span>Output will be splitted automatically.</p><p style=\'white-space:pre\'><span style=\" font-weight:600; text-decoration: underline;\">Checked - Volume mode<br/></span>Every subdirectory will be considered as separate volume.</p></body></html>"))
+        self.outputSplit.setText(_translate("mainWindow", "Output split"))
         self.colorBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Disable conversion to grayscale.</p></body></html>"))
         self.colorBox.setText(_translate("mainWindow", "Color mode"))
         self.gammaLabel.setText(_translate("mainWindow", "Gamma: Auto"))
diff --git a/kcc/__init__.py b/kcc/__init__.py
index d4f4bd0..8fd5a24 100644
--- a/kcc/__init__.py
+++ b/kcc/__init__.py
@@ -1,4 +1,4 @@
-__version__ = '5.2'
+__version__ = '5.2.1'
 __license__ = 'ISC'
 __copyright__ = '2012-2016, Ciro Mattia Gonano <[email protected]>, Pawel Jastrzebski <[email protected]>'
 __docformat__ = 'restructuredtext en'
diff --git a/kcc/comic2ebook.py b/kcc/comic2ebook.py
index da9c906..e16ea4f 100755
--- a/kcc/comic2ebook.py
+++ b/kcc/comic2ebook.py
@@ -46,7 +46,7 @@ try:
     from scandir import walk
 except ImportError:
     walk = os.walk
-from .shared import md5Checksum, getImageFileName, walkSort, walkLevel, saferReplace, saferRemove
+from .shared import md5Checksum, getImageFileName, walkSort, walkLevel, saferReplace, saferRemove, sanitizeTrace
 from . import comic2panel
 from . import image
 from . import cbxarchive
@@ -93,7 +93,6 @@ def buildHTML(path, imgfile, imgfilepath):
         additionalStyle = 'background-color:#000000;'
     else:
         additionalStyle = 'background-color:#FFFFFF;'
-    htmlpath = ''
     postfix = ''
     backref = 1
     head = path
@@ -483,7 +482,7 @@ def imgDirectoryProcessing(path):
             raise UserWarning("Conversion interrupted.")
         if len(workerOutput) > 0:
             rmtree(os.path.join(path, '..', '..'), True)
-            raise RuntimeError("One of workers crashed. Cause: " + workerOutput[0])
+            raise RuntimeError("One of workers crashed. Cause: " + workerOutput[0][0], workerOutput[0][1])
         for file in options.imgPurgeIndex:
             if os.path.isfile(file):
                 saferRemove(file)
@@ -493,7 +492,7 @@ def imgDirectoryProcessing(path):
 
 
 def imgFileProcessingTick(output):
-    if isinstance(output, str):
+    if isinstance(output, tuple):
         workerOutput.append(output)
         workerPool.terminate()
     else:
@@ -527,7 +526,7 @@ def imgFileProcessing(work):
             output.append(img.saveToDir())
         return output
     except Exception:
-        return str(sys.exc_info()[:2])
+        return str(sys.exc_info()[1]), sanitizeTrace(sys.exc_info()[2])
 
 
 def getWorkFolder(afile):
@@ -734,63 +733,25 @@ def sanitizePermissions(filetree):
             os.chmod(os.path.join(root, name), S_IWRITE | S_IREAD | S_IEXEC)
 
 
-# noinspection PyUnboundLocalVariable
 def splitDirectory(path):
-    # Detect directory stucture
-    for root, dirs, files in walkLevel(os.path.join(path, 'OEBPS', 'Images'), 0):
-        subdirectoryNumber = len(dirs)
-        filesNumber = len(files)
-    if subdirectoryNumber == 0:
-        # No subdirectories
-        mode = 0
+    level = -1
+    for root, _, files in os.walk(os.path.join(path, 'OEBPS', 'Images')):
+        for f in files:
+            if f.endswith('.jpg') or f.endswith('.jpeg') or f.endswith('.png') or f.endswith('.gif'):
+                newLevel = os.path.join(root, f).replace(os.path.join(path, 'OEBPS', 'Images'), '').count(os.sep)
+                if level != -1 and level != newLevel:
+                    level = 0
+                    break
+                else:
+                    level = newLevel
+    if level > 0:
+        splitter = splitProcess(os.path.join(path, 'OEBPS', 'Images'), level)
+        path = [path]
+        for tome in splitter:
+            path.append(tome)
+        return path
     else:
-        if filesNumber > 0:
-            print('WARNING: Automatic output splitting failed.')
-            if GUI:
-                GUI.addMessage.emit('Automatic output splitting failed. <a href='
-                                    '"https://github.com/ciromattia/kcc/wiki'
-                                    '/Automatic-output-splitting">'
-                                    'More details.</a>', 'warning', False)
-                GUI.addMessage.emit('', '', False)
-            return [path]
-        detectedSubSubdirectories = False
-        detectedFilesInSubdirectories = False
-        for root, dirs, files in walkLevel(os.path.join(path, 'OEBPS', 'Images'), 1):
-            if root != os.path.join(path, 'OEBPS', 'Images'):
-                if len(dirs) != 0:
-                    detectedSubSubdirectories = True
-                elif len(dirs) == 0 and detectedSubSubdirectories:
-                    print('WARNING: Automatic output splitting failed.')
-                    if GUI:
-                        GUI.addMessage.emit('Automatic output splitting failed. <a href='
-                                            '"https://github.com/ciromattia/kcc/wiki'
-                                            '/Automatic-output-splitting">'
-                                            'More details.</a>', 'warning', False)
-                        GUI.addMessage.emit('', '', False)
-                    return [path]
-                if len(files) != 0:
-                    detectedFilesInSubdirectories = True
-        if detectedSubSubdirectories:
-            # Two levels of subdirectories
-            mode = 2
-        else:
-            # One level of subdirectories
-            mode = 1
-        if detectedFilesInSubdirectories and detectedSubSubdirectories:
-            print('WARNING: Automatic output splitting failed.')
-            if GUI:
-                GUI.addMessage.emit('Automatic output splitting failed. <a href='
-                                    '"https://github.com/ciromattia/kcc/wiki'
-                                    '/Automatic-output-splitting">'
-                                    'More details.</a>', 'warning', False)
-                GUI.addMessage.emit('', '', False)
-            return [path]
-    # Split directories
-    splitter = splitProcess(os.path.join(path, 'OEBPS', 'Images'), mode)
-    path = [path]
-    for tome in splitter:
-        path.append(tome)
-    return path
+        raise UserWarning('Unsupported directory structure.')
 
 
 def splitProcess(path, mode):
@@ -801,10 +762,15 @@ def splitProcess(path, mode):
         targetSize = 104857600
     else:
         targetSize = 419430400
-    if mode == 0:
+    if options.batchsplit == 2 and mode == 2:
+        mode = 3
+    if mode < 3:
         for root, dirs, files in walkLevel(path, 0):
-            for name in files:
-                size = os.path.getsize(os.path.join(root, name))
+            for name in files if mode == 1 else dirs:
+                if mode == 1:
+                    size = os.path.getsize(os.path.join(root, name))
+                else:
+                    size = getDirectorySize(os.path.join(root, name))
                 if currentSize + size > targetSize:
                     currentTarget, pathRoot = createNewTome()
                     output.append(pathRoot)
@@ -813,48 +779,16 @@ def splitProcess(path, mode):
                     currentSize += size
                 if path != currentTarget:
                     move(os.path.join(root, name), os.path.join(currentTarget, name))
-    elif mode == 1:
+    else:
+        firstTome = True
         for root, dirs, files in walkLevel(path, 0):
             for name in dirs:
-                size = getDirectorySize(os.path.join(root, name))
-                if currentSize + size > targetSize:
+                if not firstTome:
                     currentTarget, pathRoot = createNewTome()
                     output.append(pathRoot)
-                    currentSize = size
-                else:
-                    currentSize += size
-                if path != currentTarget:
                     move(os.path.join(root, name), os.path.join(currentTarget, name))
-    elif mode == 2:
-        firstTome = True
-        for root, dirs, files in walkLevel(path, 0):
-            for name in dirs:
-                size = getDirectorySize(os.path.join(root, name))
-                currentSize = 0
-                if size > targetSize:
-                    if not firstTome:
-                        currentTarget, pathRoot = createNewTome()
-                        output.append(pathRoot)
-                    else:
-                        firstTome = False
-                    for rootInside, dirsInside, filesInside in walkLevel(os.path.join(root, name), 0):
-                        for nameInside in dirsInside:
-                            size = getDirectorySize(os.path.join(rootInside, nameInside))
-                            if currentSize + size > targetSize:
-                                currentTarget, pathRoot = createNewTome()
-                                output.append(pathRoot)
-                                currentSize = size
-                            else:
-                                currentSize += size
-                            if path != currentTarget:
-                                move(os.path.join(rootInside, nameInside), os.path.join(currentTarget, nameInside))
                 else:
-                    if not firstTome:
-                        currentTarget, pathRoot = createNewTome()
-                        output.append(pathRoot)
-                        move(os.path.join(root, name), os.path.join(currentTarget, name))
-                    else:
-                        firstTome = False
+                    firstTome = False
     return output
 
 
@@ -947,8 +881,9 @@ def makeParser():
                              help="Comic title [Default=filename or directory name]")
     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("-b", "--batchsplit", action="store_true", dest="batchsplit", default=False,
-                             help="Split output into multiple files"),
+    outputOptions.add_option("-b", "--batchsplit", type="int", dest="batchsplit", default="0",
+                             help="Split output into multiple files. 0: Don't split 1: Automatic mode "
+                                  "2: Consider every subdirectory as separate volume [Default=0]")
 
     processingOptions.add_option("-u", "--upscale", action="store_true", dest="upscale", default=False,
                                  help="Resize images smaller than device's resolution")
@@ -1006,8 +941,8 @@ def checkOptions():
     if options.black_borders:
         options.bordersColor = 'black'
     # Splitting MOBI is not optional
-    if options.format == 'MOBI':
-        options.batchsplit = True
+    if options.format == 'MOBI' and options.batchsplit != 2:
+        options.batchsplit = 1
     # Older Kindle don't need higher resolution files due lack of Panel View.
     if options.profile == 'K1' or options.profile == 'K2' or options.profile == 'K3' or options.profile == 'KDX':
         options.panelview = False
@@ -1092,8 +1027,8 @@ def makeBook(source, qtGUI=None):
     getComicInfo(os.path.join(path, "OEBPS", "Images"), source)
     detectCorruption(os.path.join(path, "OEBPS", "Images"), source)
     if options.webtoon:
-        if image.ProfileData.Profiles[options.profile][1][1] > 1000:
-            y = 1000
+        if image.ProfileData.Profiles[options.profile][1][1] > 1024:
+            y = 1024
         else:
             y = image.ProfileData.Profiles[options.profile][1][1]
         comic2panel.main(['-y ' + str(y), '-i', '-m', path], qtGUI)
@@ -1106,7 +1041,7 @@ def makeBook(source, qtGUI=None):
     chapterNames = sanitizeTree(os.path.join(path, 'OEBPS', 'Images'))
     if 'Ko' in options.profile and options.format == 'CBZ':
         sanitizeTreeKobo(os.path.join(path, 'OEBPS', 'Images'))
-    if options.batchsplit:
+    if options.batchsplit > 0:
         tomes = splitDirectory(path)
     else:
         tomes = [path]
diff --git a/kcc/comic2panel.py b/kcc/comic2panel.py
index def8250..5e2b4f9 100644
--- a/kcc/comic2panel.py
+++ b/kcc/comic2panel.py
@@ -24,7 +24,7 @@ from shutil import rmtree, copytree, move
 from optparse import OptionParser, OptionGroup
 from multiprocessing import Pool
 from PIL import Image, ImageStat, ImageOps
-from .shared import getImageFileName, walkLevel, walkSort, saferRemove
+from .shared import getImageFileName, walkLevel, walkSort, saferRemove, sanitizeTrace
 try:
     from PyQt5 import QtCore
 except ImportError:
@@ -81,7 +81,7 @@ def mergeDirectory(work):
             savePath = os.path.split(imagesValid[0])
             result.save(os.path.join(savePath[0], os.path.splitext(savePath[1])[0] + '.png'), 'PNG')
     except Exception:
-        return str(sys.exc_info()[1])
+        return str(sys.exc_info()[1]), sanitizeTrace(sys.exc_info()[2])
 
 
 def sanitizePanelSize(panel, opt):
@@ -205,7 +205,7 @@ def splitImage(work):
                     pageNumber += 1
             saferRemove(filePath)
     except Exception:
-        return str(sys.exc_info()[1])
+        return str(sys.exc_info()[1]), sanitizeTrace(sys.exc_info()[2])
 
 
 def main(argv=None, qtGUI=None):
@@ -267,7 +267,7 @@ def main(argv=None, qtGUI=None):
                     raise UserWarning("Conversion interrupted.")
                 if len(mergeWorkerOutput) > 0:
                     rmtree(options.targetDir, True)
-                    raise RuntimeError("One of workers crashed. Cause: " + mergeWorkerOutput[0])
+                    raise RuntimeError("One of workers crashed. Cause: " + mergeWorkerOutput[0][0], mergeWorkerOutput[0][1])
             print("Splitting images...")
             for root, dirs, files in walk(options.targetDir, False):
                 for name in files:
@@ -290,7 +290,7 @@ def main(argv=None, qtGUI=None):
                     raise UserWarning("Conversion interrupted.")
                 if len(splitWorkerOutput) > 0:
                     rmtree(options.targetDir, True)
-                    raise RuntimeError("One of workers crashed. Cause: " + splitWorkerOutput[0])
+                    raise RuntimeError("One of workers crashed. Cause: " + splitWorkerOutput[0][0], splitWorkerOutput[0][1])
                 if options.inPlace:
                     rmtree(options.sourceDir)
                     move(options.targetDir, options.sourceDir)
diff --git a/kcc/image.py b/kcc/image.py
index 009b2bb..682ae45 100755
--- a/kcc/image.py
+++ b/kcc/image.py
@@ -318,7 +318,7 @@ class ComicPage:
         tmpImg = tmpImg.point(lambda x: x and 255)
         tmpImg = tmpImg.filter(ImageFilter.MinFilter(size=3))
         tmpImg = tmpImg.filter(ImageFilter.GaussianBlur(radius=5))
-        tmpImg = tmpImg.point(lambda x: (x >= 48 * power) and x)
+        tmpImg = tmpImg.point(lambda x: (x >= 16 * power) and x)
         self.image = self.image.crop(tmpImg.getbbox()) if tmpImg.getbbox() else self.image
 
     def cropMargin(self, power):
diff --git a/kcc/pdfjpgextract.py b/kcc/pdfjpgextract.py
index f4d5813..06403ad 100644
--- a/kcc/pdfjpgextract.py
+++ b/kcc/pdfjpgextract.py
@@ -61,7 +61,6 @@ class PdfJpgExtract:
             iend += endfix
             jpg = pdf[istart:iend]
             jpgfile = open(self.path + "/jpg%d.jpg" % njpg, "wb")
-            # noinspection PyTypeChecker
             jpgfile.write(jpg)
             jpgfile.close()
             njpg += 1
diff --git a/kcc/shared.py b/kcc/shared.py
index 88d1782..c228d30 100644
--- a/kcc/shared.py
+++ b/kcc/shared.py
@@ -144,7 +144,10 @@ def removeFromZIP(zipfname, *filenames):
 
 def sanitizeTrace(traceback):
     return ''.join(format_tb(traceback))\
-        .replace('C:\\Users\\Pawel\\Documents\\Projekty\\KCC\\', '') \
+        .replace('C:/Users/Pawel/Documents/Projekty/KCC/', '')\
+        .replace('C:/Python35/', '')\
+        .replace('c:/python35/', '')\
+        .replace('C:\\Users\\Pawel\\Documents\\Projekty\\KCC\\', '')\
         .replace('C:\\Python35\\', '')\
         .replace('c:\\python35\\', '')
 
diff --git a/other/osx/Info.plist b/other/osx/Info.plist
index d883b4d..0eb9c94 100644
--- a/other/osx/Info.plist
+++ b/other/osx/Info.plist
@@ -9,7 +9,7 @@
 	<key>CFBundleExecutable</key>
 	<string>MacOS/Kindle Comic Converter</string>
 	<key>CFBundleGetInfoString</key>
-	<string>KindleComicConverter 5.2, written 2012-2016 by Ciro Mattia Gonano and Pawel Jastrzebski</string>
+	<string>KindleComicConverter 5.2.1, written 2012-2016 by Ciro Mattia Gonano and Pawel Jastrzebski</string>
 	<key>CFBundleIconFile</key>
 	<string>comic2ebook.icns</string>
 	<key>CFBundleIdentifier</key>
@@ -21,11 +21,11 @@
 	<key>CFBundlePackageType</key>
 	<string>APPL</string>
 	<key>CFBundleShortVersionString</key>
-	<string>5.2.0</string>
+	<string>5.2.1</string>
 	<key>CFBundleSignature</key>
 	<string>????</string>
 	<key>CFBundleVersion</key>
-	<string>5.2.0</string>
+	<string>5.2.1</string>
 	<key>LSEnvironment</key>
 	<dict>
 		<key>PATH</key>