diff options
author | Paweł Jastrzębski <pawelj@iosphe.re> | 2015-06-21 08:44:37 +0200 |
---|---|---|
committer | Paweł Jastrzębski <pawelj@iosphe.re> | 2015-06-21 08:44:37 +0200 |
commit | 4c9857f14d9b929e6528224e81036e6f494b8ed3 (patch) | |
tree | 6e953ccfc6aae03effa5957794cc2c5a8fd50f95 | |
parent | Merge pull request #137 from ciromattia/dev (diff) | |
parent | Updated README + version bump (diff) | |
download | kcc-4c9857f14d9b929e6528224e81036e6f494b8ed3.tar.gz kcc-4c9857f14d9b929e6528224e81036e6f494b8ed3.tar.bz2 kcc-4c9857f14d9b929e6528224e81036e6f494b8ed3.zip |
Merge pull request #143 from ciromattia/dev
4.6
-rw-r--r-- | README.md | 36 | ||||
-rwxr-xr-x | kcc-c2e.py | 2 | ||||
-rwxr-xr-x | kcc-c2p.py | 2 | ||||
-rw-r--r-- | kcc.iss | 2 | ||||
-rw-r--r-- | kcc/KCC_gui.py | 41 | ||||
-rw-r--r-- | kcc/__init__.py | 4 | ||||
-rwxr-xr-x | kcc/comic2ebook.py | 502 | ||||
-rw-r--r-- | kcc/comic2panel.py | 2 | ||||
-rw-r--r-- | kcc/dualmetafix.py | 2 | ||||
-rwxr-xr-x | kcc/image.py | 8 | ||||
-rw-r--r-- | kcc/shared.py | 26 | ||||
-rwxr-xr-x | setup.py | 10 |
12 files changed, 343 insertions, 294 deletions
diff --git a/README.md b/README.md index ef85cfd..87b7151 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # KCC **Kindle Comic Converter** is a Python app to convert comic/manga files or folders to EPUB, Panel View MOBI or E-Ink optimized CBZ. -It was initially developed for Kindle but since version 4.0 it outputs valid EPUB 3.0 so _**despite its name, KCC is +It was initially developed for Kindle but since version 4.6 it outputs valid EPUB 3.0 so _**despite its name, KCC is actually a comic/manga to EPUB converter that every e-reader owner can happily use**_. It can also optionally optimize images by applying a number of transformations. @@ -33,10 +33,10 @@ You can find the latest released binary at the following links: Following software is required to run Linux version of **KCC** and/or bare sources: - Python 3.3+ - [PyQt](http://www.riverbankcomputing.co.uk/software/pyqt/download5) 5.2.0+ -- [Pillow](http://pypi.python.org/pypi/Pillow/) 2.7.0+ -- [psutil](https://pypi.python.org/pypi/psutil) 2.0+ -- [python-slugify](http://pypi.python.org/pypi/python-slugify) 0.1.0+ -- [scandir](https://pypi.python.org/pypi/scandir) 0.9+ +- [Pillow](http://pypi.python.org/pypi/Pillow/) 2.8.2+ +- [psutil](https://pypi.python.org/pypi/psutil) 3.0.0+ +- [python-slugify](http://pypi.python.org/pypi/python-slugify) 1.1.2+ +- [scandir](https://pypi.python.org/pypi/scandir) 1.1.0+ On Debian based distributions these two commands should install all needed dependencies: ``` @@ -75,8 +75,7 @@ Options: MAIN: -p PROFILE, --profile=PROFILE Device profile (Available options: K1, K2, K345, KDX, - KPW, KV, KFHD, KFHDX, KFHDX8, KFA, KoMT, KoG, KoGHD, KoA, - KoAHD, KoAH2O) [Default=KV] + KPW, KV, KoMT, KoG, KoGHD, KoA, KoAHD, KoAH2O) [Default=KV] -q QUALITY, --quality=QUALITY Quality of Panel View. 0 - Normal 1 - High 2 - Ultra [Default=0] -m, --manga-style Manga style (Right-to-left reading and splitting) @@ -145,18 +144,25 @@ The app relies and includes the following scripts: - Icon is by **Nikolay Verin** ([http://ncrow.deviantart.com/](http://ncrow.deviantart.com/)) and released under [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/) License. ## SAMPLE FILES CREATED BY KCC -* [Kindle Voyage](http://kcc.iosphe.re/Samples/Ubunchu!-KV.mobi) -* [Kindle Paperwhite](http://kcc.iosphe.re/Samples/Ubunchu!-KPW.mobi) +* [Kindle Paperwhite 3 / Voyage](http://kcc.iosphe.re/Samples/Ubunchu!-KV.mobi) +* [Kindle Paperwhite 1 / 2](http://kcc.iosphe.re/Samples/Ubunchu!-KPW.mobi) * [Kindle](http://kcc.iosphe.re/Samples/Ubunchu!-K345.mobi) * [Kindle DX/DXG](http://kcc.iosphe.re/Samples/Ubunchu!-KDX.cbz) -* [Kobo Mini/Touch](http://kcc.iosphe.re/Samples/Ubunchu!-KoMT.cbz) -* [Kobo Glo](http://kcc.iosphe.re/Samples/Ubunchu!-KoG.cbz) -* [Kobo Glo HD](http://kcc.iosphe.re/Samples/Ubunchu!-KoGHD.cbz) -* [Kobo Aura](http://kcc.iosphe.re/Samples/Ubunchu!-KoA.cbz) -* [Kobo Aura HD](http://kcc.iosphe.re/Samples/Ubunchu!-KoAHD.cbz) -* [Kobo Aura H2O](http://kcc.iosphe.re/Samples/Ubunchu!-KoAH2O.cbz) +* [Kobo Mini/Touch](http://kcc.iosphe.re/Samples/Ubunchu-KoMT.kepub.epub) +* [Kobo Glo](http://kcc.iosphe.re/Samples/Ubunchu-KoG.kepub.epub) +* [Kobo Glo HD](http://kcc.iosphe.re/Samples/Ubunchu-KoGHD.kepub.epub) +* [Kobo Aura](http://kcc.iosphe.re/Samples/Ubunchu-KoA.kepub.epub) +* [Kobo Aura HD](http://kcc.iosphe.re/Samples/Ubunchu-KoAHD.kepub.epub) +* [Kobo Aura H2O](http://kcc.iosphe.re/Samples/Ubunchu-KoAH2O.kepub.epub) ## CHANGELOG +####4.6: +* KEPUB is now default output for all Kobo profiles +* EPUB output now produce fully valid EPUB 3.0.1 +* Added profile for Kindle Paperwhite 3 +* Dropped official support of all Kindle Fire models and Kindle for Android +* Other minor tweaks + ####4.5.1: * Added Kobo Glo HD profile * Fixed RAR/CBR parsing anomalies diff --git a/kcc-c2e.py b/kcc-c2e.py index e46d133..0e0fb6a 100755 --- a/kcc-c2e.py +++ b/kcc-c2e.py @@ -34,4 +34,4 @@ if __name__ == "__main__": freeze_support() print('comic2ebook v' + __version__ + ' - Written by Ciro Mattia Gonano and Pawel Jastrzebski.') main(sys.argv[1:]) - sys.exit(0) \ No newline at end of file + sys.exit(0) diff --git a/kcc-c2p.py b/kcc-c2p.py index 4fe42fd..7709306 100755 --- a/kcc-c2p.py +++ b/kcc-c2p.py @@ -34,4 +34,4 @@ if __name__ == "__main__": freeze_support() print('comic2panel v' + __version__ + ' - Written by Ciro Mattia Gonano and Pawel Jastrzebski.') main(sys.argv[1:]) - sys.exit(0) \ No newline at end of file + sys.exit(0) diff --git a/kcc.iss b/kcc.iss index b06f53c..e65dcce 100644 --- a/kcc.iss +++ b/kcc.iss @@ -1,5 +1,5 @@ #define MyAppName "Kindle Comic Converter" -#define MyAppVersion "4.5.1" +#define MyAppVersion "4.6" #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 787239a..a020cf2 100644 --- a/kcc/KCC_gui.py +++ b/kcc/KCC_gui.py @@ -375,9 +375,6 @@ class WorkerThread(QtCore.QThread): elif GUI.QualityBox.checkState() == 2: options.quality = 2 options.format = str(GUI.FormatBox.currentText()) - if GUI.currentMode == 1: - if 'KFH' in profile: - options.upscale = True # Advanced mode settings if GUI.currentMode > 1: @@ -557,6 +554,7 @@ class WorkerThread(QtCore.QThread): class SystemTrayIcon(QtWidgets.QSystemTrayIcon): def __init__(self): + super().__init__() if self.isSystemTrayAvailable(): QtWidgets.QSystemTrayIcon.__init__(self, GUI.icons.programIcon, MW) # noinspection PyUnresolvedReferences @@ -1161,50 +1159,38 @@ class KCCGUI(KCC_ui.Ui_KCC): self.p.ionice(1) self.profiles = { - "Kindle Voyage": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0, - 'DefaultUpscale': False, 'Label': 'KV'}, - "Kindle Paperwhite": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0, - 'DefaultUpscale': False, 'Label': 'KPW'}, + "K. PW 3/Voyage": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0, + 'DefaultUpscale': False, 'Label': 'KV'}, + "Kindle PW 1/2": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0, + 'DefaultUpscale': False, 'Label': 'KPW'}, "Kindle": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': False, 'Label': 'K345'}, "Kindle DX/DXG": {'Quality': False, 'ForceExpert': False, 'DefaultFormat': 2, 'DefaultUpscale': False, 'Label': 'KDX'}, - "K. Fire HD": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0, - 'DefaultUpscale': True, 'Label': 'KFHD'}, - "K. Fire HDX": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0, - 'DefaultUpscale': True, 'Label': 'KFHDX'}, - "K. Fire HDX 8.9": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0, - 'DefaultUpscale': True, 'Label': 'KFHDX8'}, - "Kobo Mini/Touch": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 2, + "Kobo Mini/Touch": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 1, 'DefaultUpscale': False, 'Label': 'KoMT'}, - "Kobo Glo": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 2, + "Kobo Glo": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 1, 'DefaultUpscale': False, 'Label': 'KoG'}, - "Kobo Glo HD": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 2, + "Kobo Glo HD": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 1, 'DefaultUpscale': False, 'Label': 'KoGHD'}, - "Kobo Aura": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 2, + "Kobo Aura": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 1, 'DefaultUpscale': False, 'Label': 'KoA'}, - "Kobo Aura HD": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 2, + "Kobo Aura HD": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 1, 'DefaultUpscale': False, 'Label': 'KoAHD'}, - "Kobo Aura H2O": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 2, + "Kobo Aura H2O": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 1, 'DefaultUpscale': False, 'Label': 'KoAH2O'}, "Other": {'Quality': False, 'ForceExpert': True, 'DefaultFormat': 1, 'DefaultUpscale': False, 'Label': 'OTHER'}, - "Kindle for Android": {'Quality': False, 'ForceExpert': True, 'DefaultFormat': 0, - 'DefaultUpscale': False, 'Label': 'KFA'}, "Kindle 1": {'Quality': False, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': False, 'Label': 'K1'}, "Kindle 2": {'Quality': False, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': False, 'Label': 'K2'} } profilesGUI = [ - "Kindle Voyage", - "Kindle Paperwhite", + "K. PW 3/Voyage", + "Kindle PW 1/2", "Kindle", "Separator", - "K. Fire HD", - "K. Fire HDX", - "K. Fire HDX 8.9", - "Separator", "Kobo Mini/Touch", "Kobo Glo", "Kobo Glo HD", @@ -1214,7 +1200,6 @@ class KCCGUI(KCC_ui.Ui_KCC): "Separator", "Other", "Separator", - "Kindle for Android", "Kindle 1", "Kindle 2", "Kindle DX/DXG", diff --git a/kcc/__init__.py b/kcc/__init__.py index 183755c..cbfefda 100644 --- a/kcc/__init__.py +++ b/kcc/__init__.py @@ -1,4 +1,4 @@ -__version__ = '4.5.1' +__version__ = '4.6' __license__ = 'ISC' __copyright__ = '2012-2015, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>' -__docformat__ = 'restructuredtext en' \ No newline at end of file +__docformat__ = 'restructuredtext en' diff --git a/kcc/comic2ebook.py b/kcc/comic2ebook.py index fa6c49f..f795695 100755 --- a/kcc/comic2ebook.py +++ b/kcc/comic2ebook.py @@ -20,6 +20,7 @@ import os import sys +from time import strftime, gmtime from copy import copy from glob import glob from json import loads @@ -119,83 +120,98 @@ def buildHTML(path, imgfile, imgfilepath, forcePV=False): 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" + additionalStyle + ">\n", - "<div class=\"fs\">\n", - "<div><img src=\"", "../" * backref, "Images/", postfix, imgfile, "\" alt=\"", - imgfile, "\" class=\"singlePage\"/></div>\n" - ]) - if (options.panelview or forcePV) and not noPV: - options.panelviewused = True - if not noHorizontalPV and not noVerticalPV: - if rotatedPage: - if options.righttoleft: - order = [1, 3, 2, 4] + if options.iskindle: + f.writelines(["<?xml version=\"1.0\" encoding=\"utf-8\"?>\n", + "<!DOCTYPE html>\n", + "<html xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:epub=\"http://www.idpf.org/2007/ops\">\n", + "<head>\n", + "<title>", filename[0], "</title>\n", + "<link href=\"", "../" * (backref - 1), "style.css\" type=\"text/css\" rel=\"stylesheet\"/>\n", + "<meta charset=\"utf-8\"/>\n", + "</head>\n", + "<body" + additionalStyle + ">\n", + "<div class=\"fs\">\n", + "<div><img src=\"", "../" * backref, "Images/", postfix, imgfile, "\" alt=\"", + imgfile, "\" class=\"singlePage\"/></div>\n" + ]) + if (options.panelview or forcePV) and not noPV: + options.panelviewused = True + if not noHorizontalPV and not noVerticalPV: + if rotatedPage: + if options.righttoleft: + order = [1, 3, 2, 4] + else: + order = [2, 4, 1, 3] else: - order = [2, 4, 1, 3] - else: - if options.righttoleft: - order = [2, 1, 4, 3] + 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] else: - order = [1, 2, 3, 4] - boxes = ["BoxTL", "BoxTR", "BoxBL", "BoxBR"] - elif noHorizontalPV and not noVerticalPV: - if rotatedPage: - if options.righttoleft: + order = [1, 2] + boxes = ["BoxT", "BoxB"] + elif not noHorizontalPV and noVerticalPV: + if rotatedPage: order = [1, 2] else: - order = [2, 1] + if options.righttoleft: + order = [2, 1] + else: + order = [1, 2] + boxes = ["BoxL", "BoxR"] else: - order = [1, 2] - boxes = ["BoxT", "BoxB"] - elif not noHorizontalPV and noVerticalPV: - if rotatedPage: - order = [1, 2] + 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 and not forcePV: + imgfilepv = imgfile.split(".") + imgfilepv[0] += "-hq" + imgfilepv = ".".join(imgfilepv) else: - if options.righttoleft: - order = [2, 1] - else: - 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 and not forcePV: - imgfilepv = imgfile.split(".") - 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>"]) + 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>"]) + else: + f.writelines(["<?xml version=\"1.0\" encoding=\"utf-8\"?>\n", + "<!DOCTYPE html>\n", + "<html xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:epub=\"http://www.idpf.org/2007/ops\">\n", + "<head>\n", + "<title>", filename[0], "</title>\n", + "<link href=\"", "../" * (backref - 1), "style.css\" type=\"text/css\" rel=\"stylesheet\"/>\n", + "<meta charset=\"utf-8\"/>\n", + "</head>\n", + "<body" + additionalStyle + ">\n", + "<div class=\"epub:type=bodymatter\">\n", + "<img src=\"", "../" * backref, "Images/", postfix, imgfile, "\"/>\n", + "</div>\n", + "</body>\n</html>" + ]) f.close() return path, imgfile @@ -205,11 +221,9 @@ def buildNCX(dstdir, title, chapters, chapterNames): ncxfile = os.path.join(dstdir, 'OEBPS', 'toc.ncx') f = open(ncxfile, "w", encoding='UTF-8') f.writelines(["<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n", - "<!DOCTYPE ncx PUBLIC \"-//NISO//DTD ncx 2005-1//EN\" ", - "\"http://www.daisy.org/z3986/2005/ncx-2005-1.dtd\">\n", "<ncx version=\"2005-1\" xml:lang=\"en-US\" xmlns=\"http://www.daisy.org/z3986/2005/ncx/\">\n", "<head>\n", - "<meta name=\"dtb:uid\" content=\"", options.uuid, "\"/>\n", + "<meta name=\"dtb:uid\" content=\"urn:uuid:", options.uuid, "\"/>\n", "<meta name=\"dtb:depth\" content=\"1\"/>\n", "<meta name=\"dtb:totalPageCount\" content=\"0\"/>\n", "<meta name=\"dtb:maxPageNumber\" content=\"0\"/>\n", @@ -234,6 +248,35 @@ def buildNCX(dstdir, title, chapters, chapterNames): f.close() +def buildNAV(dstdir, title, chapters, chapterNames): + navfile = os.path.join(dstdir, 'OEBPS', 'nav.xhtml') + f = open(navfile, "w", encoding='UTF-8') + f.writelines(["<?xml version=\"1.0\" encoding=\"utf-8\"?>\n", + "<!DOCTYPE html>\n", + "<html xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:epub=\"http://www.idpf.org/2007/ops\">\n", + "<head>\n", + "<title>" + title + "</title>\n", + "<meta charset=\"utf-8\"/>\n", + "</head>\n", + "<body>\n", + "<nav xmlns:epub=\"http://www.idpf.org/2007/ops\" epub:type=\"toc\" id=\"toc\">\n", + "<ol></ol>\n", + "</nav>\n", + "<nav epub:type=\"page-list\">\n", + "<ol>\n" + ]) + for chapter in chapters: + folder = chapter[0].replace(os.path.join(dstdir, 'OEBPS'), '').lstrip('/').lstrip('\\\\') + filename = getImageFileName(os.path.join(folder, chapter[1])) + if options.chapters: + title = chapterNames[chapter[1]] + elif os.path.basename(folder) != "Text": + title = chapterNames[os.path.basename(folder)] + f.write("<li><a href=\"" + filename[0].replace("\\", "/") + ".html\">" + title + "</a></li>\n") + f.write("</ol>\n</nav>\n</body>\n</html>") + f.close() + + def buildOPF(dstdir, title, filelist, cover=None): opffile = os.path.join(dstdir, 'OEBPS', 'content.opf') profilelabel, deviceres, palette, gamma, panelviewsize = options.profileData @@ -243,40 +286,47 @@ def buildOPF(dstdir, title, filelist, cover=None): writingmode = "horizontal-lr" f = open(opffile, "w", encoding='UTF-8') f.writelines(["<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n", - "<package version=\"2.0\" unique-identifier=\"BookID\" ", + "<package version=\"3.0\" unique-identifier=\"BookID\" ", + "prefix=\"rendition: http://www.idpf.org/vocab/rendition/#\" ", "xmlns=\"http://www.idpf.org/2007/opf\">\n", "<metadata xmlns:opf=\"http://www.idpf.org/2007/opf\" ", "xmlns:dc=\"http://purl.org/dc/elements/1.1/\">\n", "<dc:title>", title, "</dc:title>\n", "<dc:language>en-US</dc:language>\n", - "<dc:identifier id=\"BookID\" opf:scheme=\"UUID\">", options.uuid, "</dc:identifier>\n"]) + "<dc:identifier id=\"BookID\">urn:uuid:", options.uuid, "</dc:identifier>\n", + "<dc:contributor id=\"contributor\">KindleComicConverter-" + __version__ + "</dc:contributor>\n"]) for author in options.authors: f.writelines(["<dc:creator>", author, "</dc:creator>\n"]) - f.writelines(["<meta name=\"generator\" content=\"KindleComicConverter-" + __version__ + "\"/>\n", - "<meta name=\"RegionMagnification\" content=\"true\"/>\n", - "<meta name=\"region-mag\" content=\"true\"/>\n", + f.writelines(["<meta property=\"dcterms:modified\">" + strftime("%Y-%m-%dT%H:%M:%SZ", gmtime()) + "</meta>\n", "<meta name=\"cover\" content=\"cover\"/>\n", - "<meta name=\"book-type\" content=\"comic\"/>\n", - "<meta name=\"rendition:layout\" content=\"pre-paginated\"/>\n", - "<meta name=\"zero-gutter\" content=\"true\"/>\n", - "<meta name=\"zero-margin\" content=\"true\"/>\n", - "<meta name=\"fixed-layout\" content=\"true\"/>\n" - "<meta name=\"rendition:orientation\" content=\"portrait\"/>\n", - "<meta name=\"orientation-lock\" content=\"portrait\"/>\n", - "<meta name=\"original-resolution\" content=\"", - str(deviceres[0]) + "x" + str(deviceres[1]), "\"/>\n", - "<meta name=\"primary-writing-mode\" content=\"", writingmode, "\"/>\n", - "<meta name=\"ke-border-color\" content=\"#ffffff\"/>\n", - "<meta name=\"ke-border-width\" content=\"0\"/>\n", - "</metadata>\n<manifest>\n<item id=\"ncx\" href=\"toc.ncx\" ", - "media-type=\"application/x-dtbncx+xml\"/>\n"]) + "<meta property=\"rendition:orientation\">portrait</meta>\n", + "<meta property=\"rendition:spread\">portrait</meta>\n", + "<meta property=\"rendition:layout\">pre-paginated</meta>\n"]) + if options.iskindle and options.profile != 'Custom': + f.writelines(["<meta property=\"RegionMagnification\">true</meta>\n", + "<meta property=\"region-mag\">true</meta>\n", + "<meta property=\"book-type\">comic</meta>\n", + "<meta property=\"zero-gutter\">true</meta>\n", + "<meta property=\"zero-margin\">true</meta>\n", + "<meta property=\"fixed-layout\">true</meta>\n", + "<meta property=\"orientation-lock\">portrait</meta>\n", + "<meta property=\"original-resolution\">", + str(deviceres[0]) + "x" + str(deviceres[1]) + "</meta>\n", + "<meta property=\"primary-writing-mode\">" + writingmode + "</meta>\n", + "<meta property=\"ke-border-color\">#ffffff</meta>\n", + "<meta property=\"ke-border-width\">0</meta>\n"]) + f.writelines(["</metadata>\n<manifest>\n<item id=\"ncx\" href=\"toc.ncx\" ", + "media-type=\"application/x-dtbncx+xml\"/>\n", + "<item id=\"nav\" href=\"nav.xhtml\" ", + "properties=\"nav\" media-type=\"application/xhtml+xml\"/>\n"]) if cover is not None: filename = getImageFileName(cover.replace(os.path.join(dstdir, 'OEBPS'), '').lstrip('/').lstrip('\\\\')) if '.png' == filename[1]: mt = 'image/png' else: mt = 'image/jpeg' - f.write("<item id=\"cover\" href=\"Images/cover" + filename[1] + "\" media-type=\"" + mt + "\"/>\n") + f.write("<item id=\"cover\" href=\"Images/cover" + filename[1] + "\" media-type=\"" + mt + + "\" properties=\"cover-image\"/>\n") reflist = [] for path in filelist: folder = path[0].replace(os.path.join(dstdir, 'OEBPS'), '').lstrip('/').lstrip('\\\\').replace("\\", "/") @@ -299,7 +349,7 @@ def buildOPF(dstdir, title, filelist, cover=None): f.write("</manifest>\n<spine page-progression-direction=\"ltr\" toc=\"ncx\">\n") for entry in reflist: f.write("<itemref idref=\"page_" + entry + "\"/>\n") - f.write("</spine>\n<guide>\n</guide>\n</package>\n") + f.write("</spine>\n</package>\n") f.close() os.mkdir(os.path.join(dstdir, 'META-INF')) f = open(os.path.join(dstdir, 'META-INF', 'container.xml'), 'w', encoding='UTF-8') @@ -320,114 +370,126 @@ def buildEPUB(path, chapterNames, tomeNumber): _, deviceres, _, _, panelviewsize = options.profileData os.mkdir(os.path.join(path, 'OEBPS', 'Text')) f = open(os.path.join(path, 'OEBPS', 'Text', 'style.css'), 'w', encoding='UTF-8') - # DON'T COMPRESS CSS. KINDLE WILL FAIL TO PARSE IT. - # Generic Panel View support + Margins fix for Non-Kindle devices. - f.writelines(["@page {\n", - "margin-bottom: 0;\n", - "margin-top: 0\n", - "}\n", - "body {\n", - "display: block;\n", - "margin-bottom: 0;\n", - "margin-left: 0;\n", - "margin-right: 0;\n", - "margin-top: 0;\n", - "padding-bottom: 0;\n", - "padding-left: 0;\n", - "padding-right: 0;\n", - "padding-top: 0;\n", - "text-align: left\n", - "}\n", - "div.fs {\n", - "height: ", str(deviceres[1]), "px;\n", - "width: ", str(deviceres[0]), "px;\n", - "position: relative;\n", - "display: block;\n", - "text-align: center\n", - "}\n", - "div.fs a {\n", - "display: block;\n", - "width : 100%;\n", - "height: 100%;\n", - "}\n", - "div.fs div {\n", - "position: absolute;\n", - "}\n", - "img.singlePage {\n", - "position: absolute;\n", - "height: ", str(deviceres[1]), "px;\n", - "width: ", str(deviceres[0]), "px;\n", - "}\n", - "div.target-mag-parent {\n", - "width:100%;\n", - "height:100%;\n", - "display:none;\n", - "}\n", - "div.target-mag {\n", - "position: absolute;\n", - "display: block;\n", - "overflow: hidden;\n", - "}\n", - "div.target-mag img {\n", - "position: absolute;\n", - "height: ", str(panelviewsize[1]), "px;\n", - "width: ", str(panelviewsize[0]), "px;\n", - "}\n", - "#Generic-Panel {\n", - "top: 0;\n", - "height: 100%;\n", - "width: 100%;\n", - "}\n", - "#BoxC {\n", - "top: 0;\n", - "height: 100%;\n", - "width: 100%;\n", - "}\n", - "#BoxT {\n", - "top: 0;\n", - "height: 50%;\n", - "width: 100%;\n", - "}\n", - "#BoxB {\n", - "bottom: 0;\n", - "height: 50%;\n", - "width: 100%;\n", - "}\n", - "#BoxL {\n", - "left: 0;\n", - "height: 100%;\n", - "width: 50%;\n", - "}\n", - "#BoxR {\n", - "right: 0;\n", - "height: 100%;\n", - "width: 50%;\n", - "}\n", - "#BoxTL {\n", - "top: 0;\n", - "left: 0;\n", - "height: 50%;\n", - "width: 50%;\n", - "}\n", - "#BoxTR {\n", - "top: 0;\n", - "right: 0;\n", - "height: 50%;\n", - "width: 50%;\n", - "}\n", - "#BoxBL {\n", - "bottom: 0;\n", - "left: 0;\n", - "height: 50%;\n", - "width: 50%;\n", - "}\n", - "#BoxBR {\n", - "bottom: 0;\n", - "right: 0;\n", - "height: 50%;\n", - "width: 50%;\n", - "}", - ]) + if options.iskindle: + f.writelines(["@page {\n", + "margin-bottom: 0;\n", + "margin-top: 0\n", + "}\n", + "body {\n", + "display: block;\n", + "margin-bottom: 0;\n", + "margin-left: 0;\n", + "margin-right: 0;\n", + "margin-top: 0;\n", + "padding-bottom: 0;\n", + "padding-left: 0;\n", + "padding-right: 0;\n", + "padding-top: 0;\n", + "text-align: left\n", + "}\n", + "div.fs {\n", + "height: ", str(deviceres[1]), "px;\n", + "width: ", str(deviceres[0]), "px;\n", + "position: relative;\n", + "display: block;\n", + "text-align: center\n", + "}\n", + "div.fs a {\n", + "display: block;\n", + "width : 100%;\n", + "height: 100%;\n", + "}\n", + "div.fs div {\n", + "position: absolute;\n", + "}\n", + "img.singlePage {\n", + "position: absolute;\n", + "height: ", str(deviceres[1]), "px;\n", + "width: ", str(deviceres[0]), "px;\n", + "}\n", + "div.target-mag-parent {\n", + "width:100%;\n", + "height:100%;\n", + "display:none;\n", + "}\n", + "div.target-mag {\n", + "position: absolute;\n", + "display: block;\n", + "overflow: hidden;\n", + "}\n", + "div.target-mag img {\n", + "position: absolute;\n", + "height: ", str(panelviewsize[1]), "px;\n", + "width: ", str(panelviewsize[0]), "px;\n", + "}\n", + "#Generic-Panel {\n", + "top: 0;\n", + "height: 100%;\n", + "width: 100%;\n", + "}\n", + "#BoxC {\n", + "top: 0;\n", + "height: 100%;\n", + "width: 100%;\n", + "}\n", + "#BoxT {\n", + "top: 0;\n", + "height: 50%;\n", + "width: 100%;\n", + "}\n", + "#BoxB {\n", + "bottom: 0;\n", + "height: 50%;\n", + "width: 100%;\n", + "}\n", + "#BoxL {\n", + "left: 0;\n", + "height: 100%;\n", + "width: 50%;\n", + "}\n", + "#BoxR {\n", + "right: 0;\n", + "height: 100%;\n", + "width: 50%;\n", + "}\n", + "#BoxTL {\n", + "top: 0;\n", + "left: 0;\n", + "height: 50%;\n", + "width: 50%;\n", + "}\n", + "#BoxTR {\n", + "top: 0;\n", + "right: 0;\n", + "height: 50%;\n", + "width: 50%;\n", + "}\n", + "#BoxBL {\n", + "bottom: 0;\n", + "left: 0;\n", + "height: 50%;\n", + "width: 50%;\n", + "}\n", + "#BoxBR {\n", + "bottom: 0;\n", + "right: 0;\n", + "height: 50%;\n", + "width: 50%;\n", + "}", + ]) + else: + f.writelines([ + "@namespace epub \"http://www.idpf.org/2007/ops\";\n", + "@charset \"UTF-8\";\n", + "body {\n", + "margin: 0;\n", + "}\n", + "img {\n", + "position: absolute;\n", + "margin: 0;\n", + "z-index: 0;\n", + "height: 100%;\n", + "}"]) f.close() for (dirpath, dirnames, filenames) in walk(os.path.join(path, 'OEBPS', 'Images')): chapter = False @@ -446,7 +508,7 @@ def buildEPUB(path, chapterNames, tomeNumber): image.Cover(os.path.join(filelist[-1][0], filelist[-1][1]), cover, options, tomeNumber) # Hack that force Panel View on at last one page if lastfile and not options.panelviewused and 'Ko' not in options.profile \ - and options.profile not in ['K1', 'K2', 'KDX', 'OTHER']: + and options.profile not in ['K1', 'K2', 'KDX', 'Custom']: filelist[-1] = buildHTML(lastfile[0], lastfile[1], lastfile[2], True) # Overwrite chapternames if tree is flat and ComicInfo.xml has bookmarks if not chapterNames and options.chapters: @@ -464,6 +526,7 @@ def buildEPUB(path, chapterNames, tomeNumber): chapterNames[filename] = aChapter[1] globaldiff = pageid - (aChapter[0] + globaldiff) buildNCX(path, options.title, chapterlist, chapterNames) + buildNAV(path, options.title, chapterlist, chapterNames) buildOPF(path, options.title, filelist, cover) @@ -608,7 +671,7 @@ def getWorkFolder(afile): raise UserWarning("Failed to extract file.") else: rmtree(workdir, True) - raise TypeError + raise TypeError("Failed to detect archive format.") if len(os.path.join(path, 'OEBPS', 'Images')) > 240: raise UserWarning("Path is too long.") move(path, path + "_temp") @@ -976,8 +1039,8 @@ def makeParser(): otherOptions = OptionGroup(psr, "OTHER") mainOptions.add_option("-p", "--profile", action="store", dest="profile", default="KV", - help="Device profile (Available options: K1, K2, K345, KDX, KPW, KV, KFHD, KFHDX, KFHDX8," - " KFA, KoMT, KoG, KoGHD, KoA, KoAHD, KoAH2O) [Default=KV]") + help="Device profile (Available options: K1, K2, K345, KDX, KPW, KV, KoMT, KoG, KoGHD," + " KoA, KoAHD, KoAH2O) [Default=KV]") mainOptions.add_option("-q", "--quality", type="int", dest="quality", default="0", help="Quality of Panel View. 0 - Normal 1 - High 2 - Ultra [Default=0]") mainOptions.add_option("-m", "--manga-style", action="store_true", dest="righttoleft", default=False, @@ -1037,14 +1100,17 @@ def checkOptions(): global options options.panelview = True options.panelviewused = False + options.iskindle = False options.bordersColor = None if options.format == 'Auto': - if options.profile in ['K1', 'K2', 'K345', 'KPW', 'KV', 'KFHD', 'KFHDX', 'KFHDX8', 'KFA']: + if options.profile in ['K1', 'K2', 'K345', 'KPW', 'KV']: options.format = 'MOBI' - elif options.profile in ['Other']: + elif options.profile in ['OTHER', 'KoMT', 'KoG', 'KoGHD', 'KoA', 'KoAHD', 'KoAH2O']: options.format = 'EPUB' - elif options.profile in ['KDX', 'KoMT', 'KoG', 'KoGHD', 'KoA', 'KoAHD', 'KoAH2O']: + elif options.profile in ['KDX']: options.format = 'CBZ' + if options.profile in ['K1', 'K2', 'K345', 'KPW', 'KV', 'OTHER']: + options.iskindle = True if options.white_borders: options.bordersColor = 'white' if options.black_borders: @@ -1052,11 +1118,6 @@ def checkOptions(): # Splitting MOBI is not optional if options.format == 'MOBI': options.batchsplit = True - # Disabling grayscale conversion for Kindle Fire family. - if 'KFH' in options.profile or options.forcecolor: - options.forcecolor = True - 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 @@ -1075,10 +1136,6 @@ def checkOptions(): # 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.format == 'CBZ': options.customheight = 1200 @@ -1182,6 +1239,11 @@ def makeBook(source, qtGUI=None): filepath.append(getOutputFilename(source, options.output, '.epub', '')) makeZIP(tome + '_comic', tome, True) move(tome + '_comic.zip', filepath[-1]) + if 'Ko' in options.profile: + filename = filepath[-1].split(os.path.sep) + filename[-1] = ''.join(e for e in filename[-1].split('.')[0] if e.isalnum()) + '.kepub.epub' + filename = os.path.sep.join(filename) + move(filepath[-1], filename) rmtree(tome, True) if GUI: GUI.progressBarTick.emit('tick') @@ -1211,7 +1273,7 @@ def makeMOBIFix(item): mobiPath = item.replace('.epub', '.mobi') move(mobiPath, mobiPath + '_toclean') try: - dualmetafix.DualMobiMetaFix(mobiPath + '_toclean', mobiPath, bytes(str(uuid4()), 'UTF-8')) + dualmetafix.DualMobiMetaFix(mobiPath + '_toclean', mobiPath, bytes(options.uuid, 'UTF-8')) return [True] except Exception as err: return [False, format(err)] diff --git a/kcc/comic2panel.py b/kcc/comic2panel.py index 920a071..c68103e 100644 --- a/kcc/comic2panel.py +++ b/kcc/comic2panel.py @@ -297,4 +297,4 @@ def main(argv=None, qtGUI=None): else: raise UserWarning("Provided path is not a directory.") else: - raise UserWarning("Target height is not set.") \ No newline at end of file + raise UserWarning("Target height is not set.") diff --git a/kcc/dualmetafix.py b/kcc/dualmetafix.py index 5889d68..0eec68b 100644 --- a/kcc/dualmetafix.py +++ b/kcc/dualmetafix.py @@ -181,4 +181,4 @@ class DualMobiMetaFix: replacesection(self.datain, datain_kf8, rec0) self.datain.flush() - self.datain.close() \ No newline at end of file + self.datain.close() diff --git a/kcc/image.py b/kcc/image.py index 3235abe..cd0213b 100755 --- a/kcc/image.py +++ b/kcc/image.py @@ -82,12 +82,8 @@ class ProfileData: 'K2': ("Kindle 2", (600, 670), Palette15, 1.8, (900, 1005)), 'K345': ("Kindle", (600, 800), Palette16, 1.8, (900, 1200)), 'KDX': ("Kindle DX/DXG", (824, 1000), Palette16, 1.8, (1236, 1500)), - 'KPW': ("Kindle Paperwhite", (758, 1024), Palette16, 1.8, (1137, 1536)), - 'KV': ("Kindle Voyage", (1072, 1448), Palette16, 1.8, (1608, 2172)), - 'KFHD': ("K. Fire HD", (800, 1280), PalleteNull, 1.0, (1200, 1920)), - 'KFHDX': ("K. Fire HDX", (1200, 1920), PalleteNull, 1.0, (1800, 2880)), - 'KFHDX8': ("K. Fire HDX 8.9", (1600, 2560), PalleteNull, 1.0, (2400, 3840)), - 'KFA': ("Kindle for Android", (0, 0), PalleteNull, 1.0, (0, 0)), + 'KPW': ("Kindle Paperwhite 1/2", (758, 1024), Palette16, 1.8, (1137, 1536)), + 'KV': ("Kindle Paperwhite 3/Voyage", (1072, 1448), Palette16, 1.8, (1608, 2172)), 'KoMT': ("Kobo Mini/Touch", (600, 800), Palette16, 1.8, (900, 1200)), 'KoG': ("Kobo Glo", (768, 1024), Palette16, 1.8, (1152, 1536)), 'KoGHD': ("Kobo Glo HD", (1072, 1448), Palette16, 1.8, (1608, 2172)), diff --git a/kcc/shared.py b/kcc/shared.py index eee3e51..941d9b5 100644 --- a/kcc/shared.py +++ b/kcc/shared.py @@ -130,28 +130,28 @@ def dependencyCheck(level): if level > 1: try: from psutil import __version__ as psutilVersion - if StrictVersion('2.0.0') > StrictVersion(psutilVersion): - missing.append('psutil 2.0.0+') + if StrictVersion('3.0.0') > StrictVersion(psutilVersion): + missing.append('psutil 3.0.0+') except ImportError: - missing.append('psutil 2.0.0+') + missing.append('psutil 3.0.0+') try: from slugify import __version__ as slugifyVersion - if StrictVersion('0.1.0') > StrictVersion(slugifyVersion): - missing.append('python-slugify 0.1.0+') + if StrictVersion('1.1.2') > StrictVersion(slugifyVersion): + missing.append('python-slugify 1.1.2+') except ImportError: - missing.append('python-slugify 0.1.0+') + missing.append('python-slugify 1.1.2+') try: from PIL import PILLOW_VERSION as pillowVersion - if StrictVersion('2.7.0') > StrictVersion(pillowVersion): - missing.append('Pillow 2.7.0+') + if StrictVersion('2.8.2') > StrictVersion(pillowVersion): + missing.append('Pillow 2.8.2+') except ImportError: - missing.append('Pillow 2.7.0+') + missing.append('Pillow 2.8.2+') try: from scandir import __version__ as scandirVersion - if StrictVersion('0.9') > StrictVersion(scandirVersion): - missing.append('scandir 0.9+') + if StrictVersion('1.1') > StrictVersion(scandirVersion): + missing.append('scandir 1.1+') except ImportError: - missing.append('scandir 0.9+') + missing.append('scandir 1.1+') if len(missing) > 0: print('ERROR: ' + ', '.join(missing) + ' is not installed!') - exit(1) \ No newline at end of file + exit(1) diff --git a/setup.py b/setup.py index c50a190..64774e5 100755 --- a/setup.py +++ b/setup.py @@ -135,10 +135,10 @@ else: scripts=['build/_scripts/kcc', 'build/_scripts/kcc-c2e', 'build/_scripts/kcc-c2p'], packages=['kcc'], install_requires=[ - 'Pillow>=2.7.0', - 'psutil>=2.0', - 'python-slugify>=0.1.0', - 'scandir>=0.9', + 'Pillow>=2.8.2', + 'psutil>=3.0.0', + 'python-slugify>=1.1.2', + 'scandir>=1.1.0', ], zip_safe=False, ) @@ -159,4 +159,4 @@ if platform == 'darwin': makedirs('dist/' + NAME + '.app/Contents/PlugIns/platforms', exist_ok=True) copyfile('other/libqcocoa.dylib', 'dist/' + NAME + '.app/Contents/PlugIns/platforms/libqcocoa.dylib') chmod('dist/' + NAME + '.app/Contents/Resources/unrar', 0o777) - chmod('dist/' + NAME + '.app/Contents/Resources/7za', 0o777) \ No newline at end of file + chmod('dist/' + NAME + '.app/Contents/Resources/7za', 0o777) |