about summary refs log tree commit diff
diff options
context:
space:
mode:
authorPaweł Jastrzębski <pawelj@iosphe.re>2014-07-18 10:29:14 +0200
committerPaweł Jastrzębski <pawelj@iosphe.re>2014-07-18 10:29:14 +0200
commit15a240cceae92552e7d0a6e5d3529fd24dbd9434 (patch)
treef176a3435f33c16d48a6c7eb9685569ca29c2616
parentUpdated README (diff)
parentUpdated README + version bump (diff)
downloadkcc-15a240cceae92552e7d0a6e5d3529fd24dbd9434.tar.gz
kcc-15a240cceae92552e7d0a6e5d3529fd24dbd9434.tar.bz2
kcc-15a240cceae92552e7d0a6e5d3529fd24dbd9434.zip
Merge pull request #103 from ciromattia/4.x
Version 4.2
-rw-r--r--README.md11
-rwxr-xr-xkcc-c2e.py12
-rwxr-xr-xkcc-c2p.py12
-rw-r--r--kcc.iss3
-rwxr-xr-xkcc.py8
-rw-r--r--kcc/KCC_gui.py55
-rw-r--r--kcc/__init__.py2
-rwxr-xr-xkcc/comic2ebook.py992
-rw-r--r--kcc/comic2panel.py14
-rwxr-xr-xkcc/image.py43
-rwxr-xr-xsetup.py2
-rwxr-xr-xsetup.sh2
12 files changed, 598 insertions, 558 deletions
diff --git a/README.md b/README.md
index 814ba77..eef9924 100644
--- a/README.md
+++ b/README.md
@@ -26,7 +26,7 @@ If you find **KCC** valuable you can consider donating to the authors:
 
 ## BINARY RELEASES
 You can find the latest released binary at the following links:
-- **Windows:** [http://kcc.iosphe.re/Windows/](http://kcc.iosphe.re/Windows/)
+- **Windows (Vista or newer):** [http://kcc.iosphe.re/Windows/](http://kcc.iosphe.re/Windows/)
 - **Linux:** [http://kcc.iosphe.re/Linux/](http://kcc.iosphe.re/Linux/)
 - **OS X (10.8+):** [http://kcc.iosphe.re/OSX/](http://kcc.iosphe.re/OSX/)
 
@@ -46,7 +46,7 @@ You can find the latest released binary at the following links:
 ### For running from source:
 - Python 3.3+
 - [PyQt5](http://www.riverbankcomputing.co.uk/software/pyqt/download5) 5.2.0+
-- [Pillow](http://pypi.python.org/pypi/Pillow/) 2.3.0+
+- [Pillow](http://pypi.python.org/pypi/Pillow/) 2.5.0+
 - [psutil](https://pypi.python.org/pypi/psutil) 2.0+
 - [python-slugify](http://pypi.python.org/pypi/python-slugify)
 
@@ -354,6 +354,13 @@ The app relies and includes the following scripts/binaries:
 * Fixed _No optimization_ mode
 * Multiple small tweaks nad minor bug fixes
 
+####4.2:
+* Added [Manga Cover Database](http://manga.joentjuh.nl/) support
+* Officially dropped Windows XP support
+* Fixed _Other_ profile
+* Fixed problems with page order on stock KOBO CBZ reader
+* Many other small bug fixes and tweaks
+
 ## 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 4a6442f..a89fe1a 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.1'
+__version__ = '4.2'
 __license__ = 'ISC'
 __copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>'
 __docformat__ = 'restructuredtext en'
@@ -40,10 +40,10 @@ except ImportError:
 try:
     # noinspection PyUnresolvedReferences
     import PIL
-    if tuple(map(int, ('2.3.0'.split(".")))) > tuple(map(int, (PIL.PILLOW_VERSION.split(".")))):
-        missing.append('Pillow 2.3.0+')
+    if tuple(map(int, ('2.5.0'.split(".")))) > tuple(map(int, (PIL.PILLOW_VERSION.split(".")))):
+        missing.append('Pillow 2.5.0+')
 except ImportError:
-    missing.append('Pillow 2.3.0+')
+    missing.append('Pillow 2.5.0+')
 try:
     # noinspection PyUnresolvedReferences
     import slugify
@@ -63,10 +63,10 @@ if len(missing) > 0:
     exit(1)
 
 from multiprocessing import freeze_support
-from kcc.comic2ebook import main, Copyright
+from kcc.comic2ebook import main
 
 if __name__ == "__main__":
     freeze_support()
-    Copyright()
+    print(('comic2ebook v%(__version__)s. Written by Ciro Mattia Gonano and Pawel Jastrzebski.' % globals()))
     main(sys.argv[1:])
     sys.exit(0)
\ No newline at end of file
diff --git a/kcc-c2p.py b/kcc-c2p.py
index 3976e2e..a50500c 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.1'
+__version__ = '4.2'
 __license__ = 'ISC'
 __copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>'
 __docformat__ = 'restructuredtext en'
@@ -33,10 +33,10 @@ missing = []
 try:
     # noinspection PyUnresolvedReferences
     import PIL
-    if tuple(map(int, ('2.3.0'.split(".")))) > tuple(map(int, (PIL.PILLOW_VERSION.split(".")))):
-        missing.append('Pillow 2.3.0+')
+    if tuple(map(int, ('2.5.0'.split(".")))) > tuple(map(int, (PIL.PILLOW_VERSION.split(".")))):
+        missing.append('Pillow 2.5.0+')
 except ImportError:
-    missing.append('Pillow 2.3.0+')
+    missing.append('Pillow 2.5.0+')
 if len(missing) > 0:
     try:
         # noinspection PyUnresolvedReferences
@@ -51,10 +51,10 @@ if len(missing) > 0:
     exit(1)
 
 from multiprocessing import freeze_support
-from kcc.comic2panel import main, Copyright
+from kcc.comic2panel import main
 
 if __name__ == "__main__":
     freeze_support()
-    Copyright()
+    print(('comic2ebook v%(__version__)s. Written by Ciro Mattia Gonano and Pawel Jastrzebski.' % globals()))
     main(sys.argv[1:])
     sys.exit(0)
\ No newline at end of file
diff --git a/kcc.iss b/kcc.iss
index bd78427..5b7b067 100644
--- a/kcc.iss
+++ b/kcc.iss
@@ -1,5 +1,5 @@
 #define MyAppName "Kindle Comic Converter"
-#define MyAppVersion "4.1"
+#define MyAppVersion "4.2"
 #define MyAppPublisher "Ciro Mattia Gonano, Paweł Jastrzębski"
 #define MyAppURL "http://kcc.iosphe.re/"
 #define MyAppExeName "KCC.exe"
@@ -30,6 +30,7 @@ UninstallDisplayIcon={app}\{#MyAppExeName}
 ChangesAssociations=True
 InfoAfterFile=other\InstallWarning.rtf
 SignTool=SignTool /d $q{#MyAppName}$q /du $q{#MyAppURL}$q $f
+MinVersion=0,6.0
 
 [Languages]
 Name: "english"; MessagesFile: "compiler:Default.isl"
diff --git a/kcc.py b/kcc.py
index 1264841..9e546b9 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.1'
+__version__ = '4.2'
 __license__ = 'ISC'
 __copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>'
 __docformat__ = 'restructuredtext en'
@@ -47,10 +47,10 @@ except ImportError:
 try:
     # noinspection PyUnresolvedReferences
     import PIL
-    if tuple(map(int, ('2.3.0'.split(".")))) > tuple(map(int, (PIL.PILLOW_VERSION.split(".")))):
-        missing.append('Pillow 2.3.0+')
+    if tuple(map(int, ('2.5.0'.split(".")))) > tuple(map(int, (PIL.PILLOW_VERSION.split(".")))):
+        missing.append('Pillow 2.5.0+')
 except ImportError:
-    missing.append('Pillow 2.3.0+')
+    missing.append('Pillow 2.5.0+')
 try:
     # noinspection PyUnresolvedReferences
     import slugify
diff --git a/kcc/KCC_gui.py b/kcc/KCC_gui.py
index 1102067..30a4e91 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.1'
+__version__ = '4.2'
 __license__ = 'ISC'
 __copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>'
 __docformat__ = 'restructuredtext en'
@@ -38,6 +38,7 @@ from xml.dom.minidom import parse
 from html.parser import HTMLParser
 from psutil import virtual_memory, Popen, Process
 from uuid import uuid4
+from copy import copy
 from .shared import md5Checksum
 from . import comic2ebook
 from . import dualmetafix
@@ -196,7 +197,6 @@ class VersionThread(QtCore.QThread):
 
     def run(self):
         try:
-            sleep(1)
             XML = urlopen('http://kcc.iosphe.re/Version.php')
             XML = parse(XML)
         except Exception:
@@ -422,9 +422,6 @@ class WorkerThread(QtCore.QThread):
             if GUI.ColorBox.isChecked():
                 options.forcecolor = True
 
-        comic2ebook.options = options
-        comic2ebook.checkOptions()
-
         for i in range(GUI.JobList.count()):
             # Make sure that we don't consider any system message as job to do
             if GUI.JobList.item(i).icon().isNull():
@@ -446,7 +443,8 @@ class WorkerThread(QtCore.QThread):
             jobargv = list(argv)
             jobargv.append(job)
             try:
-                comic2ebook.options.title = 'defaulttitle'
+                comic2ebook.options = copy(options)
+                comic2ebook.checkOptions()
                 outputPath = comic2ebook.makeBook(job, self)
                 MW.hideProgressBar.emit()
             except UserWarning as warn:
@@ -493,7 +491,6 @@ class WorkerThread(QtCore.QThread):
                         worker.signals.result.connect(self.addResult)
                         self.pool.start(worker)
                     self.pool.waitForDone()
-                    sleep(0.5)
                     self.kindlegenErrorCode = [0]
                     for errors in self.workerOutput:
                         if errors[0] != 0:
@@ -503,7 +500,6 @@ class WorkerThread(QtCore.QThread):
                         for item in outputPath:
                             if os.path.exists(item):
                                 os.remove(item)
-                            sleep(1)
                             if os.path.exists(item.replace('.epub', '.mobi')):
                                 os.remove(item.replace('.epub', '.mobi'))
                         self.clean()
@@ -521,7 +517,6 @@ class WorkerThread(QtCore.QThread):
                             worker.signals.result.connect(self.addResult)
                             self.pool.start(worker)
                         self.pool.waitForDone()
-                        sleep(0.5)
                         for success in self.workerOutput:
                             if not success[0]:
                                 self.errors = True
@@ -555,7 +550,6 @@ class WorkerThread(QtCore.QThread):
                         for item in outputPath:
                             if os.path.exists(item):
                                 os.remove(item)
-                            sleep(1)
                             if os.path.exists(item.replace('.epub', '.mobi')):
                                 os.remove(item.replace('.epub', '.mobi'))
                         MW.addMessage.emit('KindleGen failed to create MOBI!', 'error', False)
@@ -834,6 +828,9 @@ class KCCGUI(KCC_ui.Ui_KCC):
         if value == 2 and 'Kobo' in str(GUI.DeviceBox.currentText()):
             self.addMessage('Kobo devices can\'t use ultra quality mode!', 'warning')
             GUI.QualityBox.setCheckState(0)
+        elif value == 2 and 'CBZ' in str(GUI.FormatBox.currentText()):
+            self.addMessage('CBZ format don\'t support ultra quality mode!', 'warning')
+            GUI.QualityBox.setCheckState(0)
 
     def changeGamma(self, value):
         value = float(value)
@@ -861,7 +858,7 @@ class KCCGUI(KCC_ui.Ui_KCC):
             GUI.AdvModeButton.setEnabled(True)
             if self.currentMode == 3:
                 self.modeBasic()
-        self.changeFormat()
+        self.changeFormat(event=False)
         GUI.GammaSlider.setValue(0)
         self.changeGamma(0)
         if profile['DefaultUpscale']:
@@ -870,19 +867,12 @@ class KCCGUI(KCC_ui.Ui_KCC):
             self.addMessage('<a href="https://github.com/ciromattia/kcc/wiki/NonKindle-devices">'
                             'List of supported Non-Kindle devices.</a>', 'info')
 
-    def changeFormat(self, outputFormat=None):
+    def changeFormat(self, outputFormat=None, event=True):
         profile = GUI.profiles[str(GUI.DeviceBox.currentText())]
         if outputFormat is not None:
             GUI.FormatBox.setCurrentIndex(outputFormat)
         else:
-            if GUI.FormatBox.count() == 3:
-                GUI.FormatBox.setCurrentIndex(profile['DefaultFormat'])
-            else:
-                if profile['DefaultFormat'] != 0:
-                    tmpFormat = profile['DefaultFormat'] - 1
-                else:
-                    tmpFormat = 0
-                GUI.FormatBox.setCurrentIndex(tmpFormat)
+            GUI.FormatBox.setCurrentIndex(profile['DefaultFormat'])
         if GUI.WebtoonBox.isChecked():
             GUI.MangaBox.setEnabled(False)
             GUI.QualityBox.setEnabled(False)
@@ -895,6 +885,10 @@ class KCCGUI(KCC_ui.Ui_KCC):
         if GUI.ProcessingBox.isChecked():
             GUI.QualityBox.setEnabled(False)
             GUI.QualityBox.setChecked(False)
+        if event and GUI.QualityBox.isEnabled() and 'CBZ' in str(GUI.FormatBox.currentText()) and\
+                GUI.QualityBox.checkState() == 2:
+            self.addMessage('CBZ format don\'t support ultra quality mode!', 'warning')
+            GUI.QualityBox.setCheckState(0)
 
     def stripTags(self, html):
         s = HTMLStripper()
@@ -973,6 +967,15 @@ class KCCGUI(KCC_ui.Ui_KCC):
                 self.addMessage('Target resolution is not set!', 'error')
                 self.needClean = True
                 return
+            if str(GUI.FormatBox.currentText()) == 'MOBI' and not GUI.KindleGen:
+                self.addMessage('Cannot find <a href="http://www.amazon.com/gp/feature.html?ie=UTF8&docId=1000765211">'
+                                '<b>KindleGen</b></a>! MOBI conversion is not possible!', 'error')
+                if sys.platform.startswith('win'):
+                    self.addMessage('Download it and place EXE in KCC directory.', 'error')
+                else:
+                    self.addMessage('Download it, and place executable in /usr/local/bin directory.', 'error')
+                self.needClean = True
+                return
             self.worker.start()
 
     def hideProgressBar(self):
@@ -1196,7 +1199,6 @@ class KCCGUI(KCC_ui.Ui_KCC):
         kindleGenExitCode = Popen('kindlegen -locale en', stdout=PIPE, stderr=STDOUT, shell=True)
         if kindleGenExitCode.wait() == 0:
             self.KindleGen = True
-            formats = ['MOBI', 'EPUB', 'CBZ']
             versionCheck = Popen('kindlegen -locale en', stdout=PIPE, stderr=STDOUT, shell=True)
             for line in versionCheck.stdout:
                 line = line.decode("utf-8")
@@ -1210,13 +1212,6 @@ class KCCGUI(KCC_ui.Ui_KCC):
                     break
         else:
             self.KindleGen = False
-            formats = ['EPUB', 'CBZ']
-            if sys.platform.startswith('win'):
-                self.addMessage('Cannot find <a href="http://www.amazon.com/gp/feature.html?ie=UTF8&docId=1000765211">'
-                                'kindlegen</a> in KCC directory! MOBI creation will be disabled.', 'warning')
-            else:
-                self.addMessage('Cannot find <a href="http://www.amazon.com/gp/feature.html?ie=UTF8&docId=1000765211">'
-                                'kindlegen</a> in PATH! MOBI creation will be disabled.', 'warning')
         rarExitCode = Popen('unrar', stdout=PIPE, stderr=STDOUT, shell=True)
         rarExitCode = rarExitCode.wait()
         if rarExitCode == 0 or rarExitCode == 7:
@@ -1271,7 +1266,7 @@ class KCCGUI(KCC_ui.Ui_KCC):
                 GUI.DeviceBox.addItem(self.icons.deviceKobo, profile)
             else:
                 GUI.DeviceBox.addItem(self.icons.deviceKindle, profile)
-        for f in formats:
+        for f in ['MOBI', 'EPUB', 'CBZ']:
             GUI.FormatBox.addItem(eval('self.icons.' + f + 'Format'), f)
         if self.lastDevice > GUI.DeviceBox.count():
             self.lastDevice = 0
@@ -1282,7 +1277,7 @@ class KCCGUI(KCC_ui.Ui_KCC):
         GUI.DeviceBox.setCurrentIndex(self.lastDevice)
         self.changeDevice()
         if self.currentFormat != self.profiles[str(GUI.DeviceBox.currentText())]['DefaultFormat']:
-            self.changeFormat(self.currentFormat)
+            self.changeFormat(self.currentFormat, False)
         for option in self.options:
             if str(option) == "customWidth":
                 GUI.customWidth.setText(str(self.options[option]))
diff --git a/kcc/__init__.py b/kcc/__init__.py
index 90c588a..34c10c7 100644
--- a/kcc/__init__.py
+++ b/kcc/__init__.py
@@ -1,4 +1,4 @@
-__version__ = '4.1'
+__version__ = '4.2'
 __license__ = 'ISC'
 __copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>'
 __docformat__ = 'restructuredtext en'
\ No newline at end of file
diff --git a/kcc/comic2ebook.py b/kcc/comic2ebook.py
index c654071..73b798b 100755
--- a/kcc/comic2ebook.py
+++ b/kcc/comic2ebook.py
@@ -18,14 +18,16 @@
 # PERFORMANCE OF THIS SOFTWARE.
 #
 
-__version__ = '4.1'
+__version__ = '4.2'
 __license__ = 'ISC'
 __copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>'
 __docformat__ = 'restructuredtext en'
 
 import os
 import sys
-from re import split, sub
+from json import loads
+from urllib.request import Request, urlopen
+from re import split, sub, compile
 from stat import S_IWRITE, S_IREAD, S_IEXEC
 from zipfile import ZipFile, ZIP_STORED, ZIP_DEFLATED
 from tempfile import mkdtemp
@@ -47,154 +49,136 @@ from . import cbxarchive
 from . import pdfjpgextract
 
 
+def main(argv=None):
+    global options
+    parser = makeParser()
+    options, args = parser.parse_args(argv)
+    checkOptions()
+    if len(args) != 1:
+        parser.print_help()
+        return
+    outputPath = makeBook(args[0])
+    return outputPath
+
+
 def buildHTML(path, imgfile, imgfilepath):
     imgfilepath = md5Checksum(imgfilepath)
     filename = getImageFileName(imgfile)
-    if filename is not None:
-        if options.imgproc:
-            if "Rotated" in theGreatIndex[imgfilepath]:
-                rotatedPage = True
-            else:
-                rotatedPage = False
-            if "NoPanelView" in theGreatIndex[imgfilepath]:
-                noPV = True
-            else:
-                noPV = False
-            if "NoHorizontalPanelView" in theGreatIndex[imgfilepath]:
-                noHorizontalPV = True
-            else:
-                noHorizontalPV = False
-            if "NoVerticalPanelView" in theGreatIndex[imgfilepath]:
-                noVerticalPV = True
-            else:
-                noVerticalPV = False
+    if options.imgproc:
+        if "Rotated" in options.imgIndex[imgfilepath]:
+            rotatedPage = True
         else:
             rotatedPage = False
+        if "NoPanelView" in options.imgIndex[imgfilepath]:
+            noPV = True
+        else:
             noPV = False
+        if "NoHorizontalPanelView" in options.imgIndex[imgfilepath]:
+            noHorizontalPV = True
+        else:
             noHorizontalPV = False
+        if "NoVerticalPanelView" in options.imgIndex[imgfilepath]:
+            noVerticalPV = True
+        else:
             noVerticalPV = False
-        htmlpath = ''
-        postfix = ''
-        backref = 1
-        head = path
-        while True:
-            head, tail = os.path.split(head)
-            if tail == 'Images':
-                htmlpath = os.path.join(head, 'Text', postfix)
-                break
-            postfix = tail + "/" + postfix
-            backref += 1
-        if not os.path.exists(htmlpath):
-            os.makedirs(htmlpath)
-        htmlfile = os.path.join(htmlpath, filename[0] + '.html')
-        f = open(htmlfile, "w", encoding='UTF-8')
-        f.writelines(["<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" ",
-                      "\"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n",
-                      "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n",
-                      "<head>\n",
-                      "<title>", filename[0], "</title>\n",
-                      "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>\n",
-                      "<link href=\"", "../" * (backref - 1),
-                      "style.css\" type=\"text/css\" rel=\"stylesheet\"/>\n",
-                      "</head>\n",
-                      "<body>\n",
-                      "<div class=\"fs\">\n",
-                      "<div><img src=\"", "../" * backref, "Images/", postfix, imgfile, "\" alt=\"",
-                      imgfile, "\" class=\"singlePage\"/></div>\n"
-                      ])
-        if options.panelview and not noPV:
-            if not noHorizontalPV and not noVerticalPV:
-                if rotatedPage:
-                    if options.righttoleft:
-                        order = [1, 3, 2, 4]
-                    else:
-                        order = [2, 4, 1, 3]
+    else:
+        rotatedPage = False
+        noPV = False
+        noHorizontalPV = False
+        noVerticalPV = False
+    htmlpath = ''
+    postfix = ''
+    backref = 1
+    head = path
+    while True:
+        head, tail = os.path.split(head)
+        if tail == 'Images':
+            htmlpath = os.path.join(head, 'Text', postfix)
+            break
+        postfix = tail + "/" + postfix
+        backref += 1
+    if not os.path.exists(htmlpath):
+        os.makedirs(htmlpath)
+    htmlfile = os.path.join(htmlpath, filename[0] + '.html')
+    f = open(htmlfile, "w", encoding='UTF-8')
+    f.writelines(["<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" ",
+                  "\"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n",
+                  "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n",
+                  "<head>\n",
+                  "<title>", filename[0], "</title>\n",
+                  "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>\n",
+                  "<link href=\"", "../" * (backref - 1),
+                  "style.css\" type=\"text/css\" rel=\"stylesheet\"/>\n",
+                  "</head>\n",
+                  "<body>\n",
+                  "<div class=\"fs\">\n",
+                  "<div><img src=\"", "../" * backref, "Images/", postfix, imgfile, "\" alt=\"",
+                  imgfile, "\" class=\"singlePage\"/></div>\n"
+                  ])
+    if options.panelview and not noPV:
+        if not noHorizontalPV and not noVerticalPV:
+            if rotatedPage:
+                if options.righttoleft:
+                    order = [1, 3, 2, 4]
                 else:
-                    if options.righttoleft:
-                        order = [2, 1, 4, 3]
-                    else:
-                        order = [1, 2, 3, 4]
-                boxes = ["BoxTL", "BoxTR", "BoxBL", "BoxBR"]
-            elif noHorizontalPV and not noVerticalPV:
-                if rotatedPage:
-                    if options.righttoleft:
-                        order = [1, 2]
-                    else:
-                        order = [2, 1]
+                    order = [2, 4, 1, 3]
+            else:
+                if options.righttoleft:
+                    order = [2, 1, 4, 3]
                 else:
-                    order = [1, 2]
-                boxes = ["BoxT", "BoxB"]
-            elif not noHorizontalPV and noVerticalPV:
-                if rotatedPage:
+                    order = [1, 2, 3, 4]
+            boxes = ["BoxTL", "BoxTR", "BoxBL", "BoxBR"]
+        elif noHorizontalPV and not noVerticalPV:
+            if rotatedPage:
+                if options.righttoleft:
                     order = [1, 2]
                 else:
-                    if options.righttoleft:
-                        order = [2, 1]
-                    else:
-                        order = [1, 2]
-                boxes = ["BoxL", "BoxR"]
+                    order = [2, 1]
             else:
-                order = [1]
-                boxes = ["BoxC"]
-            for i in range(0, len(boxes)):
-                f.writelines(["<div id=\"" + boxes[i] + "\"><a class=\"app-amzn-magnify\" data-app-amzn-magnify=",
-                              "'{\"targetId\":\"" + boxes[i] + "-Panel-Parent\", \"ordinal\":" + str(order[i]),
-                              "}'></a></div>\n"])
-            if options.quality == 2:
-                imgfilepv = str.split(imgfile, ".")
-                imgfilepv[0] += "-hq"
-                imgfilepv = ".".join(imgfilepv)
+                order = [1, 2]
+            boxes = ["BoxT", "BoxB"]
+        elif not noHorizontalPV and noVerticalPV:
+            if rotatedPage:
+                order = [1, 2]
             else:
-                imgfilepv = imgfile
-            xl, yu, xr, yd = checkMargins(imgfilepath)
-            boxStyles = {"BoxTL": "left:" + xl + ";top:" + yu + ";",
-                         "BoxTR": "right:" + xr + ";top:" + yu + ";",
-                         "BoxBL": "left:" + xl + ";bottom:" + yd + ";",
-                         "BoxBR": "right:" + xr + ";bottom:" + yd + ";",
-                         "BoxT": "left:-25%;top:" + yu + ";",
-                         "BoxB": "left:-25%;bottom:" + yd + ";",
-                         "BoxL": "left:" + xl + ";top:-25%;",
-                         "BoxR": "right:" + xr + ";top:-25%;",
-                         "BoxC": "left:-25%;top:-25%;"
-                         }
-            for box in boxes:
-                f.writelines(["<div id=\"" + box + "-Panel-Parent\" class=\"target-mag-parent\"><div id=\"",
-                              "Generic-Panel\" class=\"target-mag\"><img style=\"" + boxStyles[box] + "\" src=\"",
-                              "../" * backref, "Images/", postfix, imgfilepv, "\" alt=\"" + imgfilepv,
-                              "\"/></div></div>\n",
-                              ])
-        f.writelines(["</div>\n</body>\n</html>"])
-        f.close()
-        return path, imgfile
-
-
-def checkMargins(path):
-    if options.imgproc:
-        for flag in theGreatIndex[path]:
-            if "Margins-" in flag:
-                flag = flag.split('-')
-                xl = flag[1]
-                yu = flag[2]
-                xr = flag[3]
-                yd = flag[4]
-                if xl != "0":
-                    xl = "-" + str(float(xl)/100) + "%"
-                else:
-                    xl = "0%"
-                if xr != "0":
-                    xr = "-" + str(float(xr)/100) + "%"
-                else:
-                    xr = "0%"
-                if yu != "0":
-                    yu = "-" + str(float(yu)/100) + "%"
-                else:
-                    yu = "0%"
-                if yd != "0":
-                    yd = "-" + str(float(yd)/100) + "%"
+                if options.righttoleft:
+                    order = [2, 1]
                 else:
-                    yd = "0%"
-                return xl, yu, xr, yd
-    return '0%', '0%', '0%', '0%'
+                    order = [1, 2]
+            boxes = ["BoxL", "BoxR"]
+        else:
+            order = [1]
+            boxes = ["BoxC"]
+        for i in range(0, len(boxes)):
+            f.writelines(["<div id=\"" + boxes[i] + "\"><a class=\"app-amzn-magnify\" data-app-amzn-magnify=",
+                          "'{\"targetId\":\"" + boxes[i] + "-Panel-Parent\", \"ordinal\":" + str(order[i]),
+                          "}'></a></div>\n"])
+        if options.quality == 2:
+            imgfilepv = str.split(imgfile, ".")
+            imgfilepv[0] += "-hq"
+            imgfilepv = ".".join(imgfilepv)
+        else:
+            imgfilepv = imgfile
+        xl, yu, xr, yd = detectMargins(imgfilepath)
+        boxStyles = {"BoxTL": "left:" + xl + ";top:" + yu + ";",
+                     "BoxTR": "right:" + xr + ";top:" + yu + ";",
+                     "BoxBL": "left:" + xl + ";bottom:" + yd + ";",
+                     "BoxBR": "right:" + xr + ";bottom:" + yd + ";",
+                     "BoxT": "left:-25%;top:" + yu + ";",
+                     "BoxB": "left:-25%;bottom:" + yd + ";",
+                     "BoxL": "left:" + xl + ";top:-25%;",
+                     "BoxR": "right:" + xr + ";top:-25%;",
+                     "BoxC": "left:-25%;top:-25%;"
+                     }
+        for box in boxes:
+            f.writelines(["<div id=\"" + box + "-Panel-Parent\" class=\"target-mag-parent\"><div id=\"",
+                          "Generic-Panel\" class=\"target-mag\"><img style=\"" + boxStyles[box] + "\" src=\"",
+                          "../" * backref, "Images/", postfix, imgfilepv, "\" alt=\"" + imgfilepv,
+                          "\"/></div></div>\n",
+                          ])
+    f.writelines(["</div>\n</body>\n</html>"])
+    f.close()
+    return path, imgfile
 
 
 def buildNCX(dstdir, title, chapters, chapterNames):
@@ -305,133 +289,7 @@ def buildOPF(dstdir, title, filelist, cover=None):
     f.close()
 
 
-def applyImgOptimization(img, opt, hqImage=None):
-    if not img.fill:
-        img.getImageFill(opt.webtoon)
-    if not opt.webtoon:
-        img.cropWhiteSpace()
-    if opt.cutpagenumbers and not opt.webtoon:
-        img.cutPageNumber()
-    img.optimizeImage(opt.gamma)
-    if hqImage:
-        img.resizeImage(opt.upscale, opt.stretch, opt.bordersColor, 0)
-        img.calculateBorder(hqImage, True)
-    else:
-        img.resizeImage(opt.upscale, opt.stretch, opt.bordersColor, opt.quality)
-        if opt.panelview:
-            if opt.quality == 0:
-                img.calculateBorder(img)
-            elif opt.quality == 1:
-                img.calculateBorder(img, True)
-    if opt.forcepng and not opt.forcecolor:
-        img.quantizeImage()
-
-
-def dirImgProcess(path):
-    global workerPool, workerOutput, theGreatIndex, theGreatWipe
-    workerPool = Pool()
-    workerOutput = []
-    work = []
-    theGreatIndex = {}
-    theGreatWipe = []
-    pagenumber = 0
-    for (dirpath, dirnames, filenames) in os.walk(path):
-        for afile in filenames:
-            if getImageFileName(afile) is not None:
-                pagenumber += 1
-                work.append([afile, dirpath, options])
-    if GUI:
-        GUI.progressBarTick.emit(str(pagenumber))
-    if len(work) > 0:
-        for i in work:
-            workerPool.apply_async(func=fileImgProcess, args=(i, ), callback=fileImgProcess_tick)
-        workerPool.close()
-        workerPool.join()
-        if GUI and not GUI.conversionAlive:
-            rmtree(os.path.join(path, '..', '..'), True)
-            raise UserWarning("Conversion interrupted.")
-        if len(workerOutput) > 0:
-            rmtree(os.path.join(path, '..', '..'), True)
-            raise RuntimeError("One of workers crashed. Cause: " + workerOutput[0])
-        for file in theGreatWipe:
-            if os.path.isfile(file):
-                os.remove(file)
-    else:
-        rmtree(os.path.join(path, '..', '..'), True)
-        raise UserWarning("Source directory is empty.")
-
-
-def fileImgProcess_tick(output):
-    if isinstance(output, str):
-        workerOutput.append(output)
-        workerPool.terminate()
-    else:
-        for page in output:
-            if page is not None:
-                if isinstance(page, str):
-                    theGreatWipe.append(page)
-                else:
-                    theGreatIndex[page[0]] = page[1]
-    if GUI:
-        GUI.progressBarTick.emit('tick')
-        if not GUI.conversionAlive:
-            workerPool.terminate()
-
-
-def fileImgProcess(work):
-    try:
-        afile = work[0]
-        dirpath = work[1]
-        opt = work[2]
-        output = []
-        img = image.ComicPage(os.path.join(dirpath, afile), opt.profileData)
-        if opt.quality == 2:
-            wipe = False
-        else:
-            wipe = True
-        if opt.nosplitrotate:
-            splitter = None
-        else:
-            splitter = img.splitPage(dirpath, opt.righttoleft, opt.rotate)
-        if splitter is not None:
-            img0 = image.ComicPage(splitter[0], opt.profileData)
-            applyImgOptimization(img0, opt)
-            output.append(img0.saveToDir(dirpath, opt.forcepng, opt.forcecolor))
-            img1 = image.ComicPage(splitter[1], opt.profileData)
-            applyImgOptimization(img1, opt)
-            output.append(img1.saveToDir(dirpath, opt.forcepng, opt.forcecolor))
-            if wipe:
-                output.append(img0.origFileName)
-                output.append(img1.origFileName)
-            if opt.quality == 2:
-                img0b = image.ComicPage(splitter[0], opt.profileData, img0.fill)
-                applyImgOptimization(img0b, opt, img0)
-                output.append(img0b.saveToDir(dirpath, opt.forcepng, opt.forcecolor))
-                img1b = image.ComicPage(splitter[1], opt.profileData, img1.fill)
-                applyImgOptimization(img1b, opt, img1)
-                output.append(img1b.saveToDir(dirpath, opt.forcepng, opt.forcecolor))
-                output.append(img0.origFileName)
-                output.append(img1.origFileName)
-            output.append(img.origFileName)
-        else:
-            applyImgOptimization(img, opt)
-            output.append(img.saveToDir(dirpath, opt.forcepng, opt.forcecolor))
-            if wipe:
-                output.append(img.origFileName)
-            if opt.quality == 2:
-                img2 = image.ComicPage(os.path.join(dirpath, afile), opt.profileData, img.fill)
-                if img.rotated:
-                    img2.image = img2.image.rotate(90)
-                    img2.rotated = True
-                applyImgOptimization(img2, opt, img)
-                output.append(img2.saveToDir(dirpath, opt.forcepng, opt.forcecolor))
-                output.append(img.origFileName)
-        return output
-    except Exception:
-        return str(sys.exc_info()[1])
-
-
-def genEpubStruct(path, chapterNames):
+def buildEPUB(path, chapterNames, tomeNumber):
     filelist = []
     chapterlist = []
     cover = None
@@ -551,7 +409,7 @@ def genEpubStruct(path, chapterNames):
         chapter = False
         for afile in filenames:
             filename = getImageFileName(afile)
-            if filename is not None and not '-kcc-hq' in filename[0]:
+            if not '-kcc-hq' in filename[0]:
                 filelist.append(buildHTML(dirpath, afile, os.path.join(dirpath, afile)))
                 if not chapter:
                     chapterlist.append((dirpath.replace('Images', 'Text'), filelist[-1][1]))
@@ -559,7 +417,7 @@ def genEpubStruct(path, chapterNames):
                 if cover is None:
                     cover = os.path.join(os.path.join(path, 'OEBPS', 'Images'),
                                          'cover' + getImageFileName(filelist[-1][1])[1])
-                    image.Cover(os.path.join(filelist[-1][0], filelist[-1][1]), cover)
+                    image.Cover(os.path.join(filelist[-1][0], filelist[-1][1]), cover, options, tomeNumber)
     buildNCX(path, options.title, chapterlist, chapterNames)
     # Ensure we're sorting files alphabetically
     convert = lambda text: int(text) if text.isdigit() else text
@@ -568,6 +426,131 @@ def genEpubStruct(path, chapterNames):
     buildOPF(path, options.title, filelist, cover)
 
 
+def imgOptimization(img, opt, hqImage=None):
+    if not img.fill:
+        img.getImageFill(opt.webtoon)
+    if not opt.webtoon:
+        img.cropWhiteSpace()
+    if opt.cutpagenumbers and not opt.webtoon:
+        img.cutPageNumber()
+    img.optimizeImage(opt.gamma)
+    if hqImage:
+        img.resizeImage(opt.upscale, opt.stretch, opt.bordersColor, 0)
+        img.calculateBorder(hqImage, True)
+    else:
+        img.resizeImage(opt.upscale, opt.stretch, opt.bordersColor, opt.quality)
+        if opt.panelview:
+            if opt.quality == 0:
+                img.calculateBorder(img)
+            elif opt.quality == 1:
+                img.calculateBorder(img, True)
+    if opt.forcepng and not opt.forcecolor:
+        img.quantizeImage()
+
+
+def imgDirectoryProcessing(path):
+    global workerPool, workerOutput
+    workerPool = Pool()
+    workerOutput = []
+    options.imgIndex = {}
+    options.imgPurgeIndex = []
+    work = []
+    pagenumber = 0
+    for (dirpath, dirnames, filenames) in os.walk(path):
+        for afile in filenames:
+            pagenumber += 1
+            work.append([afile, dirpath, options])
+    if GUI:
+        GUI.progressBarTick.emit(str(pagenumber))
+    if len(work) > 0:
+        for i in work:
+            workerPool.apply_async(func=imgFileProcessing, args=(i, ), callback=imgFileProcessingTick)
+        workerPool.close()
+        workerPool.join()
+        if GUI and not GUI.conversionAlive:
+            rmtree(os.path.join(path, '..', '..'), True)
+            raise UserWarning("Conversion interrupted.")
+        if len(workerOutput) > 0:
+            rmtree(os.path.join(path, '..', '..'), True)
+            raise RuntimeError("One of workers crashed. Cause: " + workerOutput[0])
+        for file in options.imgPurgeIndex:
+            if os.path.isfile(file):
+                os.remove(file)
+    else:
+        rmtree(os.path.join(path, '..', '..'), True)
+        raise UserWarning("Source directory is empty.")
+
+
+def imgFileProcessingTick(output):
+    if isinstance(output, str):
+        workerOutput.append(output)
+        workerPool.terminate()
+    else:
+        for page in output:
+            if page is not None:
+                if isinstance(page, str):
+                    options.imgPurgeIndex.append(page)
+                else:
+                    options.imgIndex[page[0]] = page[1]
+    if GUI:
+        GUI.progressBarTick.emit('tick')
+        if not GUI.conversionAlive:
+            workerPool.terminate()
+
+
+def imgFileProcessing(work):
+    try:
+        afile = work[0]
+        dirpath = work[1]
+        opt = work[2]
+        output = []
+        img = image.ComicPage(os.path.join(dirpath, afile), opt.profileData)
+        if opt.quality == 2:
+            wipe = False
+        else:
+            wipe = True
+        if opt.nosplitrotate:
+            splitter = None
+        else:
+            splitter = img.splitPage(dirpath, opt.righttoleft, opt.rotate)
+        if splitter is not None:
+            img0 = image.ComicPage(splitter[0], opt.profileData)
+            imgOptimization(img0, opt)
+            output.append(img0.saveToDir(dirpath, opt.forcepng, opt.forcecolor))
+            img1 = image.ComicPage(splitter[1], opt.profileData)
+            imgOptimization(img1, opt)
+            output.append(img1.saveToDir(dirpath, opt.forcepng, opt.forcecolor))
+            if wipe:
+                output.append(img0.origFileName)
+                output.append(img1.origFileName)
+            if opt.quality == 2:
+                img0b = image.ComicPage(splitter[0], opt.profileData, img0.fill)
+                imgOptimization(img0b, opt, img0)
+                output.append(img0b.saveToDir(dirpath, opt.forcepng, opt.forcecolor))
+                img1b = image.ComicPage(splitter[1], opt.profileData, img1.fill)
+                imgOptimization(img1b, opt, img1)
+                output.append(img1b.saveToDir(dirpath, opt.forcepng, opt.forcecolor))
+                output.append(img0.origFileName)
+                output.append(img1.origFileName)
+            output.append(img.origFileName)
+        else:
+            imgOptimization(img, opt)
+            output.append(img.saveToDir(dirpath, opt.forcepng, opt.forcecolor))
+            if wipe:
+                output.append(img.origFileName)
+            if opt.quality == 2:
+                img2 = image.ComicPage(os.path.join(dirpath, afile), opt.profileData, img.fill)
+                if img.rotated:
+                    img2.image = img2.image.rotate(90)
+                    img2.rotated = True
+                imgOptimization(img2, opt, img)
+                output.append(img2.saveToDir(dirpath, opt.forcepng, opt.forcecolor))
+                output.append(img.origFileName)
+        return output
+    except Exception:
+        return str(sys.exc_info()[1])
+
+
 def getWorkFolder(afile):
     if len(afile) > 240:
         raise UserWarning("Path is too long.")
@@ -579,7 +562,7 @@ def getWorkFolder(afile):
             if len(fullPath) > 240:
                 raise UserWarning("Path is too long.")
             copytree(afile, fullPath)
-            sanitizeTreeBeforeConversion(fullPath)
+            sanitizePermissions(fullPath)
             return workdir
         except OSError:
             rmtree(workdir, True)
@@ -609,9 +592,36 @@ def getWorkFolder(afile):
     return path
 
 
-def checkComicInfo(path, originalPath):
+def getOutputFilename(srcpath, wantedname, ext, tomeNumber):
+    if srcpath[-1] == os.path.sep:
+        srcpath = srcpath[:-1]
+    if not ext.startswith('.'):
+        ext = '.' + ext
+    if wantedname is not None:
+        if wantedname.endswith(ext):
+            filename = os.path.abspath(wantedname)
+        elif os.path.isdir(srcpath):
+            filename = os.path.join(os.path.abspath(options.output), os.path.basename(srcpath) + ext)
+        else:
+            filename = os.path.join(os.path.abspath(options.output),
+                                    os.path.basename(os.path.splitext(srcpath)[0]) + ext)
+    elif os.path.isdir(srcpath):
+        filename = srcpath + tomeNumber + ext
+    else:
+        filename = os.path.splitext(srcpath)[0] + tomeNumber + ext
+    if os.path.isfile(filename):
+        counter = 0
+        basename = os.path.splitext(filename)[0]
+        while os.path.isfile(basename + '_kcc' + str(counter) + ext):
+            counter += 1
+        filename = basename + '_kcc' + str(counter) + ext
+    return filename
+
+
+def getComicInfo(path, originalPath):
     xmlPath = os.path.join(path, 'ComicInfo.xml')
     options.authors = ['KCC']
+    options.remoteCovers = {}
     titleSuffix = ''
     if options.title == 'defaulttitle':
         defaultTitle = True
@@ -657,48 +667,81 @@ def checkComicInfo(path, originalPath):
             options.authors.sort()
         else:
             options.authors = ['KCC']
+        if len(xml.getElementsByTagName('ScanInformation')) != 0:
+            coverId = xml.getElementsByTagName('ScanInformation')[0].firstChild.nodeValue
+            coverId = compile('(MCD\\()(\\d+)(\\))').search(coverId)
+            if coverId:
+                options.remoteCovers = getCoversFromMCB(coverId.group(2))
         os.remove(xmlPath)
 
 
-def slugify(value):
-    value = slugifyExt(value)
-    value = sub(r'0*([0-9]{4,})', r'\1', sub(r'([0-9]+)', r'0000\1', value))
-    return value
+def getCoversFromMCB(mangaID):
+    covers = {}
+    try:
+        jsonRaw = urlopen(Request('http://manga.joentjuh.nl/json/series/' + mangaID + '/',
+                                  headers={'User-Agent': 'KindleComicConverter/' + __version__}))
+        jsonData = loads(jsonRaw.readall().decode('utf-8'))
+        for volume in jsonData['volumes']:
+            covers[int(volume['volume'])] = volume['releases'][0]['files']['front']['url']
+    except Exception:
+        return {}
+    return covers
+
+
+def getDirectorySize(start_path='.'):
+    total_size = 0
+    for dirpath, dirnames, filenames in os.walk(start_path):
+        for f in filenames:
+            fp = os.path.join(dirpath, f)
+            total_size += os.path.getsize(fp)
+    return total_size
 
 
 def sanitizeTree(filetree):
     chapterNames = {}
     for root, dirs, files in os.walk(filetree, False):
         for name in files:
-            if name.startswith('.') or name.lower() == 'thumbs.db':
-                os.remove(os.path.join(root, name))
-            else:
-                splitname = os.path.splitext(name)
-                slugified = slugify(splitname[0])
-                while os.path.exists(os.path.join(root, slugified + splitname[1])) and splitname[0].upper()\
-                        != slugified.upper():
-                    slugified += "A"
-                newKey = os.path.join(root, slugified + splitname[1])
-                key = os.path.join(root, name)
-                if key != newKey:
-                    os.replace(key, newKey)
+            splitname = os.path.splitext(name)
+            slugified = slugify(splitname[0])
+            while os.path.exists(os.path.join(root, slugified + splitname[1])) and splitname[0].upper()\
+                    != slugified.upper():
+                slugified += "A"
+            newKey = os.path.join(root, slugified + splitname[1])
+            key = os.path.join(root, name)
+            if key != newKey:
+                os.replace(key, newKey)
         for name in dirs:
-            if name.startswith('.'):
-                os.remove(os.path.join(root, name))
-            else:
-                tmpName = name
-                slugified = slugify(name)
-                while os.path.exists(os.path.join(root, slugified)) and name.upper() != slugified.upper():
-                    slugified += "A"
-                chapterNames[slugified] = tmpName
-                newKey = os.path.join(root, slugified)
-                key = os.path.join(root, name)
-                if key != newKey:
-                    os.replace(key, newKey)
+            tmpName = name
+            slugified = slugify(name)
+            while os.path.exists(os.path.join(root, slugified)) and name.upper() != slugified.upper():
+                slugified += "A"
+            chapterNames[slugified] = tmpName
+            newKey = os.path.join(root, slugified)
+            key = os.path.join(root, name)
+            if key != newKey:
+                os.replace(key, newKey)
     return chapterNames
 
 
-def sanitizeTreeBeforeConversion(filetree):
+def sanitizeTreeKobo(filetree):
+    pageNumber = 0
+    for root, dirs, files in os.walk(filetree):
+        files.sort()
+        dirs.sort()
+        for name in files:
+            splitname = os.path.splitext(name)
+            slugified = str(pageNumber).zfill(5)
+            pageNumber += 1
+            while os.path.exists(os.path.join(root, slugified + splitname[1])) and splitname[0].upper()\
+                    != slugified.upper():
+                slugified += "A"
+            newKey = os.path.join(root, slugified + splitname[1])
+            key = os.path.join(root, name)
+            if key != newKey:
+                os.replace(key, newKey)
+
+
+def sanitizePermissions(filetree):
     for root, dirs, files in os.walk(filetree, False):
         for name in files:
             os.chmod(os.path.join(root, name), S_IWRITE | S_IREAD)
@@ -706,23 +749,66 @@ def sanitizeTreeBeforeConversion(filetree):
             os.chmod(os.path.join(root, name), S_IWRITE | S_IREAD | S_IEXEC)
 
 
-def getDirectorySize(start_path='.'):
-    total_size = 0
-    for dirpath, dirnames, filenames in os.walk(start_path):
-        for f in filenames:
-            fp = os.path.join(dirpath, f)
-            total_size += os.path.getsize(fp)
-    return total_size
-
-
-def createNewTome():
-    tomePathRoot = mkdtemp('', 'KCC-TMP-')
-    tomePath = os.path.join(tomePathRoot, 'OEBPS', 'Images')
-    os.makedirs(tomePath)
-    return tomePath, tomePathRoot
+#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
+    else:
+        if filesNumber > 0:
+            print('\nWARNING: 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('\nWARNING: 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('\nWARNING: 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
 
 
-def splitDirectory(path, mode):
+def splitProcess(path, mode):
     output = []
     currentSize = 0
     currentTarget = path
@@ -783,65 +869,6 @@ def splitDirectory(path, mode):
     return output
 
 
-#noinspection PyUnboundLocalVariable
-def preSplitDirectory(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
-    else:
-        if filesNumber > 0:
-            print('\nWARNING: 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('\nWARNING: 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('\nWARNING: 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 = splitDirectory(os.path.join(path, 'OEBPS', 'Images'), mode)
-    path = [path]
-    for tome in splitter:
-        path.append(tome)
-    return path
-
-
 def detectCorruption(tmpPath, orgPath):
     for root, dirs, files in os.walk(tmpPath, False):
         for name in files:
@@ -859,6 +886,50 @@ def detectCorruption(tmpPath, orgPath):
                 except Exception:
                     rmtree(os.path.join(tmpPath, '..', '..'), True)
                     raise RuntimeError('Image file %s is corrupted.' % pathOrg)
+            else:
+                os.remove(os.path.join(root, name))
+
+
+def detectMargins(path):
+    if options.imgproc:
+        for flag in options.imgIndex[path]:
+            if "Margins-" in flag:
+                flag = flag.split('-')
+                xl = flag[1]
+                yu = flag[2]
+                xr = flag[3]
+                yd = flag[4]
+                if xl != "0":
+                    xl = "-" + str(float(xl)/100) + "%"
+                else:
+                    xl = "0%"
+                if xr != "0":
+                    xr = "-" + str(float(xr)/100) + "%"
+                else:
+                    xr = "0%"
+                if yu != "0":
+                    yu = "-" + str(float(yu)/100) + "%"
+                else:
+                    yu = "0%"
+                if yd != "0":
+                    yd = "-" + str(float(yd)/100) + "%"
+                else:
+                    yd = "0%"
+                return xl, yu, xr, yd
+    return '0%', '0%', '0%', '0%'
+
+
+def createNewTome():
+    tomePathRoot = mkdtemp('', 'KCC-TMP-')
+    tomePath = os.path.join(tomePathRoot, 'OEBPS', 'Images')
+    os.makedirs(tomePath)
+    return tomePath, tomePathRoot
+
+
+def slugify(value):
+    value = slugifyExt(value)
+    value = sub(r'0*([0-9]{4,})', r'\1', sub(r'([0-9]+)', r'0000\1', value))
+    return value
 
 
 def makeZIP(zipFilename, baseDir, isEPUB=False):
@@ -876,15 +947,6 @@ def makeZIP(zipFilename, baseDir, isEPUB=False):
     return zipFilename
 
 
-def Copyright():
-    print(('comic2ebook v%(__version__)s. Written by Ciro Mattia Gonano and Pawel Jastrzebski.' % globals()))
-
-
-def Usage():
-    print("Generates EPUB/CBZ comic ebook from a bunch of images.")
-    parser.print_help()
-
-
 def makeParser():
     """Create and return an option parser set up with kcc's options."""
     psr = OptionParser(usage="Usage: kcc-c2e [options] comic_file|comic_folder", add_help_option=False)
@@ -953,57 +1015,94 @@ def makeParser():
     return psr
 
 
-def main(argv=None, qtGUI=None):
-    global parser, options, GUI
-    parser = makeParser()
-    options, args = parser.parse_args(argv)
-    checkOptions()
-    if qtGUI:
-        GUI = qtGUI
-        GUI.progressBarTick.emit('1')
+def checkOptions():
+    global options
+    options.panelview = True
+    options.bordersColor = None
+    if options.white_borders:
+        options.bordersColor = "white"
+    if options.black_borders:
+        options.bordersColor = "black"
+    # Disabling grayscale conversion for Kindle Fire family.
+    if options.profile == 'KF' or options.profile == 'KFHD' or options.profile == 'KFHD8' or options.profile == 'KFHDX'\
+       or options.profile == 'KFHDX8' or options.forcecolor:
+        options.forcecolor = True
     else:
-        GUI = None
-    if len(args) != 1:
-        parser.print_help()
-        return
-    outputPath = makeBook(args[0], qtGUI=qtGUI)
-    return outputPath
+        options.forcecolor = False
+    # Older Kindle don't need higher resolution files due lack of Panel View.
+    if options.profile == 'K1' or options.profile == 'K2' or options.profile == 'KDX':
+        options.quality = 0
+        options.panelview = False
+    # Webtoon mode mandatory options
+    if options.webtoon:
+        options.nosplitrotate = True
+        options.quality = 0
+        options.panelview = False
+    # Disable all Kindle features for other e-readers
+    if options.profile == 'OTHER':
+        options.panelview = False
+        options.quality = 0
+    if 'Ko' in options.profile:
+        options.panelview = False
+        # Kobo models can't use ultra quality mode
+        if options.quality == 2:
+            options.quality = 1
+    # Kindle for Android profile require target resolution.
+    if options.profile == 'KFA' and (options.customwidth == 0 or options.customheight == 0):
+        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:
+        options.customheight = 1200
+    # Ultra mode don't work with CBZ format
+    if options.quality == 2 and options.cbzoutput:
+        options.quality = 1
+    # Override profile data
+    if options.customwidth != 0 or options.customheight != 0:
+        X = image.ProfileData.Profiles[options.profile][1][0]
+        Y = image.ProfileData.Profiles[options.profile][1][1]
+        if options.customwidth != 0:
+            X = options.customwidth
+        if options.customheight != 0:
+            Y = options.customheight
+        newProfile = ("Custom", (int(X), int(Y)), image.ProfileData.Palette16,
+                      image.ProfileData.Profiles[options.profile][3], (int(int(X)*1.5), int(int(Y)*1.5)))
+        image.ProfileData.Profiles["Custom"] = newProfile
+        options.profile = "Custom"
+    options.profileData = image.ProfileData.Profiles[options.profile]
 
 
 def makeBook(source, qtGUI=None):
     """Generates EPUB/CBZ comic ebook from a bunch of images."""
     global GUI
     GUI = qtGUI
+    if GUI:
+        GUI.progressBarTick.emit('1')
     path = getWorkFolder(source)
     print("\nChecking images...")
+    getComicInfo(os.path.join(path, "OEBPS", "Images"), source)
     detectCorruption(os.path.join(path, "OEBPS", "Images"), source)
-    checkComicInfo(os.path.join(path, "OEBPS", "Images"), source)
-
     if options.webtoon:
         if options.customheight > 0:
             comic2panel.main(['-y ' + str(options.customheight), '-i', '-m', path], qtGUI)
         else:
             comic2panel.main(['-y ' + str(image.ProfileData.Profiles[options.profile][1][1]), '-i', '-m', path], qtGUI)
-
     if options.imgproc:
         print("\nProcessing images...")
         if GUI:
             GUI.progressBarTick.emit('Processing images')
-        dirImgProcess(os.path.join(path, "OEBPS", "Images"))
-
+        imgDirectoryProcessing(os.path.join(path, "OEBPS", "Images"))
     if GUI:
         GUI.progressBarTick.emit('1')
-
     chapterNames = sanitizeTree(os.path.join(path, 'OEBPS', 'Images'))
-
+    if 'Ko' in options.profile and options.cbzoutput:
+        sanitizeTreeKobo(os.path.join(path, 'OEBPS', 'Images'))
     if options.batchsplit:
-        tomes = preSplitDirectory(path)
+        tomes = splitDirectory(path)
     else:
         tomes = [path]
-
     filepath = []
     tomeNumber = 0
-
     if GUI:
         if options.cbzoutput:
             GUI.progressBarTick.emit('Compressing CBZ files')
@@ -1011,14 +1110,11 @@ def makeBook(source, qtGUI=None):
             GUI.progressBarTick.emit('Compressing EPUB files')
         GUI.progressBarTick.emit(str(len(tomes) + 1))
         GUI.progressBarTick.emit('tick')
-
     options.baseTitle = options.title
-
     for tome in tomes:
         if len(tomes) > 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
             print("\nCreating CBZ file...")
@@ -1029,97 +1125,15 @@ def makeBook(source, qtGUI=None):
             makeZIP(tome + '_comic', os.path.join(tome, "OEBPS", "Images"))
         else:
             print("\nCreating EPUB structure...")
-            genEpubStruct(tome, chapterNames)
+            buildEPUB(tome, chapterNames, tomeNumber)
             # actually zip the ePub
             if len(tomes) > 1:
                 filepath.append(getOutputFilename(source, options.output, '.epub', ' ' + str(tomeNumber)))
             else:
                 filepath.append(getOutputFilename(source, options.output, '.epub', ''))
             makeZIP(tome + '_comic', tome, True)
-
         move(tome + '_comic.zip', filepath[-1])
         rmtree(tome, True)
-
         if GUI:
             GUI.progressBarTick.emit('tick')
-    return filepath
-
-
-def getOutputFilename(srcpath, wantedname, ext, tomeNumber):
-    if srcpath[-1] == os.path.sep:
-        srcpath = srcpath[:-1]
-    if not ext.startswith('.'):
-        ext = '.' + ext
-    if wantedname is not None:
-        if wantedname.endswith(ext):
-            filename = os.path.abspath(wantedname)
-        elif os.path.isdir(srcpath):
-            filename = os.path.join(os.path.abspath(options.output), os.path.basename(srcpath) + ext)
-        else:
-            filename = os.path.join(os.path.abspath(options.output),
-                                    os.path.basename(os.path.splitext(srcpath)[0]) + ext)
-    elif os.path.isdir(srcpath):
-        filename = srcpath + tomeNumber + ext
-    else:
-        filename = os.path.splitext(srcpath)[0] + tomeNumber + ext
-    if os.path.isfile(filename):
-        counter = 0
-        basename = os.path.splitext(filename)[0]
-        while os.path.isfile(basename + '_kcc' + str(counter) + ext):
-            counter += 1
-        filename = basename + '_kcc' + str(counter) + ext
-    return filename
-
-
-def checkOptions():
-    global options
-    options.panelview = True
-    options.bordersColor = None
-    if options.white_borders:
-        options.bordersColor = "white"
-    if options.black_borders:
-        options.bordersColor = "black"
-    # Disabling grayscale conversion for Kindle Fire family.
-    if options.profile == 'KF' or options.profile == 'KFHD' or options.profile == 'KFHD8' or options.profile == 'KFHDX'\
-       or options.profile == 'KFHDX8' or options.forcecolor:
-        options.forcecolor = True
-    else:
-        options.forcecolor = False
-    # Older Kindle don't need higher resolution files due lack of Panel View.
-    if options.profile == 'K1' or options.profile == 'K2' or options.profile == 'KDX':
-        options.quality = 0
-        options.panelview = False
-    # Webtoon mode mandatory options
-    if options.webtoon:
-        options.nosplitrotate = True
-        options.quality = 0
-        options.panelview = False
-    # Disable all Kindle features for other e-readers
-    if options.profile == 'OTHER':
-        options.panelview = False
-        options.quality = 0
-    if 'Ko' in options.profile:
-        options.panelview = False
-        # Kobo models can't use ultra quality mode
-        if options.quality == 2:
-            options.quality = 1
-    # Kindle for Android profile require target resolution.
-    if options.profile == 'KFA' and (options.customwidth == 0 or options.customheight == 0):
-        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:
-        options.customheight = 1200
-    # Override profile data
-    if options.customwidth != 0 or options.customheight != 0:
-        X = image.ProfileData.Profiles[options.profile][1][0]
-        Y = image.ProfileData.Profiles[options.profile][1][1]
-        if options.customwidth != 0:
-            X = options.customwidth
-        if options.customheight != 0:
-            Y = options.customheight
-        newProfile = ("Custom", (X, Y), image.ProfileData.Palette16, image.ProfileData.Profiles[options.profile][3],
-                      (int(X*1.5), int(Y*1.5)))
-        image.ProfileData.Profiles["Custom"] = newProfile
-        options.profile = "Custom"
-    options.profileData = image.ProfileData.Profiles[options.profile]
+    return filepath
\ No newline at end of file
diff --git a/kcc/comic2panel.py b/kcc/comic2panel.py
index 3a36777..d506cac 100644
--- a/kcc/comic2panel.py
+++ b/kcc/comic2panel.py
@@ -18,7 +18,7 @@
 # PERFORMANCE OF THIS SOFTWARE.
 #
 
-__version__ = '4.1'
+__version__ = '4.2'
 __license__ = 'ISC'
 __copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>'
 __docformat__ = 'restructuredtext en'
@@ -36,7 +36,7 @@ except ImportError:
     QtCore = None
 
 
-def mergeDirectory_tick(output):
+def mergeDirectoryTick(output):
     if output:
         mergeWorkerOutput.append(output)
         mergeWorkerPool.terminate()
@@ -108,7 +108,7 @@ def sanitizePanelSize(panel, opt):
     return newPanels
 
 
-def splitImage_tick(output):
+def splitImageTick(output):
     if output:
         splitWorkerOutput.append(output)
         splitWorkerPool.terminate()
@@ -207,10 +207,6 @@ def splitImage(work):
         return str(sys.exc_info()[1])
 
 
-def Copyright():
-    print(('comic2panel v%(__version__)s. Written by Ciro Mattia Gonano and Pawel Jastrzebski.' % globals()))
-
-
 def main(argv=None, qtGUI=None):
     global options, GUI, splitWorkerPool, splitWorkerOutput, mergeWorkerPool, mergeWorkerOutput
     parser = OptionParser(usage="Usage: kcc-c2p [options] comic_folder", add_help_option=False)
@@ -261,7 +257,7 @@ def main(argv=None, qtGUI=None):
                     GUI.progressBarTick.emit('Combining images')
                     GUI.progressBarTick.emit(str(directoryNumer))
                 for i in mergeWork:
-                    mergeWorkerPool.apply_async(func=mergeDirectory, args=(i, ), callback=mergeDirectory_tick)
+                    mergeWorkerPool.apply_async(func=mergeDirectory, args=(i, ), callback=mergeDirectoryTick)
                 mergeWorkerPool.close()
                 mergeWorkerPool.join()
                 if GUI and not GUI.conversionAlive:
@@ -284,7 +280,7 @@ def main(argv=None, qtGUI=None):
                 GUI.progressBarTick.emit('tick')
             if len(work) > 0:
                 for i in work:
-                    splitWorkerPool.apply_async(func=splitImage, args=(i, ), callback=splitImage_tick)
+                    splitWorkerPool.apply_async(func=splitImage, args=(i, ), callback=splitImageTick)
                 splitWorkerPool.close()
                 splitWorkerPool.join()
                 if GUI and not GUI.conversionAlive:
diff --git a/kcc/image.py b/kcc/image.py
index 5b54bbd..f8abfe6 100755
--- a/kcc/image.py
+++ b/kcc/image.py
@@ -16,11 +16,14 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
+__version__ = '4.2'
 __license__ = 'ISC'
 __copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>'
 __docformat__ = 'restructuredtext en'
 
 import os
+from io import BytesIO
+from urllib.request import Request, urlopen
 from functools import reduce
 from PIL import Image, ImageOps, ImageStat, ImageChops
 from .shared import md5Checksum
@@ -472,14 +475,37 @@ class ComicPage:
 
 
 class Cover:
-    def __init__(self, source, target):
+    def __init__(self, source, target, opt, tomeNumber):
+        self.options = opt
         self.source = source
         self.target = target
-        self.image = Image.open(source)
+        if tomeNumber == 0:
+            self.tomeNumber = 1
+        else:
+            self.tomeNumber = tomeNumber
+        if self.tomeNumber in self.options.remoteCovers:
+            try:
+                source = urlopen(Request(self.options.remoteCovers[self.tomeNumber],
+                                         headers={'User-Agent': 'KindleComicConverter/' + __version__})).read()
+                self.image = Image.open(BytesIO(source))
+                self.processExternal()
+            except Exception:
+                self.image = Image.open(source)
+                self.processInternal()
+        else:
+            self.image = Image.open(source)
+            self.processInternal()
+
+    def processInternal(self):
         self.image = self.image.convert('RGB')
-        self.process()
+        self.image = self.trim()
         self.save()
 
+    def processExternal(self):
+        self.image = self.image.convert('RGB')
+        self.image.thumbnail(self.options.profileData[1], Image.ANTIALIAS)
+        self.save(True)
+
     def trim(self):
         bg = Image.new(self.image.mode, self.image.size, self.image.getpixel((0, 0)))
         diff = ImageChops.difference(self.image, bg)
@@ -490,12 +516,13 @@ class Cover:
         else:
             return self.image
 
-    def process(self):
-        self.image = self.trim()
-
-    def save(self):
+    def save(self, external=False):
+        if external:
+            source = self.options.remoteCovers[self.tomeNumber].split('/')[-1]
+        else:
+            source = self.source
         try:
-            if os.path.splitext(self.source)[1].lower() == '.png':
+            if os.path.splitext(source)[1].lower() == '.png':
                 self.image.save(self.target, "PNG", optimize=1)
             else:
                 self.image.save(self.target, "JPEG", optimize=1)
diff --git a/setup.py b/setup.py
index af87fa2..5f90bec 100755
--- a/setup.py
+++ b/setup.py
@@ -14,7 +14,7 @@ if version_info[0] != 3:
     exit(1)
 
 NAME = "KindleComicConverter"
-VERSION = "4.1"
+VERSION = "4.2"
 MAIN = "kcc.py"
 
 if platform == "darwin":
diff --git a/setup.sh b/setup.sh
index 0f9d0d2..bd69758 100755
--- a/setup.sh
+++ b/setup.sh
@@ -1,7 +1,7 @@
 #!/bin/bash
 # Linux Python package build script
 
-VERSION="4.1"
+VERSION="4.2"
 
 cp kcc.py __main__.py
 zip kcc.zip __main__.py kcc/*.py