diff --git a/README.md b/README.md index f4534c8..ef85cfd 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 2.2 it outputs valid EPUB 2.0 so _**despite its name, KCC is +It was initially developed for Kindle but since version 4.0 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. @@ -75,7 +75,7 @@ Options: MAIN: -p PROFILE, --profile=PROFILE Device profile (Available options: K1, K2, K345, KDX, - KPW, KV, KFHD, KFHDX, KFHDX8, KFA, KoMT, KoG, KoA, + KPW, KV, KFHD, KFHDX, KFHDX8, KFA, KoMT, KoG, KoGHD, KoA, KoAHD, KoAH2O) [Default=KV] -q QUALITY, --quality=QUALITY Quality of Panel View. 0 - Normal 1 - High 2 - Ultra [Default=0] @@ -150,12 +150,18 @@ The app relies and includes the following scripts: * [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 Glow](http://kcc.iosphe.re/Samples/Ubunchu!-KoG.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) ## CHANGELOG +####4.5.1: +* Added Kobo Glo HD profile +* Fixed RAR/CBR parsing anomalies +* Minor bug fixes and tweaks + ####4.5: * Added simple ComicRack metadata editor * Re-enabled Manga Cover Database support diff --git a/kcc.iss b/kcc.iss index f1adaaf..b06f53c 100644 --- a/kcc.iss +++ b/kcc.iss @@ -1,5 +1,5 @@ #define MyAppName "Kindle Comic Converter" -#define MyAppVersion "4.5" +#define MyAppVersion "4.5.1" #define MyAppPublisher "Ciro Mattia Gonano, Paweł Jastrzębski" #define MyAppURL "http://kcc.iosphe.re/" #define MyAppExeName "KCC.exe" diff --git a/kcc/KCC_gui.py b/kcc/KCC_gui.py index fe5954c..787239a 100644 --- a/kcc/KCC_gui.py +++ b/kcc/KCC_gui.py @@ -1177,8 +1177,10 @@ class KCCGUI(KCC_ui.Ui_KCC): 'DefaultUpscale': True, 'Label': 'KFHDX8'}, "Kobo Mini/Touch": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 2, 'DefaultUpscale': False, 'Label': 'KoMT'}, - "Kobo Glow": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 2, - 'DefaultUpscale': False, 'Label': 'KoG'}, + "Kobo Glo": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 2, + 'DefaultUpscale': False, 'Label': 'KoG'}, + "Kobo Glo HD": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 2, + 'DefaultUpscale': False, 'Label': 'KoGHD'}, "Kobo Aura": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 2, 'DefaultUpscale': False, 'Label': 'KoA'}, "Kobo Aura HD": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 2, @@ -1204,7 +1206,8 @@ class KCCGUI(KCC_ui.Ui_KCC): "K. Fire HDX 8.9", "Separator", "Kobo Mini/Touch", - "Kobo Glow", + "Kobo Glo", + "Kobo Glo HD", "Kobo Aura", "Kobo Aura HD", "Kobo Aura H2O", @@ -1339,6 +1342,8 @@ class KCCGUI_MetaEditor(KCC_MetaEditor_ui.Ui_MetaEditorDialog): field.setText(self.parser.data[field.objectName()[:-4]]) for field in (self.WriterLine, self.PencillerLine, self.InkerLine, self.ColoristLine): field.setText(', '.join(self.parser.data[field.objectName()[:-4] + 's'])) + if self.SeriesLine.text() == '': + self.SeriesLine.setText(file.split('\\')[-1].split('.')[0]) def saveData(self): for field in (self.VolumeLine, self.NumberLine, self.MUidLine): diff --git a/kcc/__init__.py b/kcc/__init__.py index 1a4fe8c..183755c 100644 --- a/kcc/__init__.py +++ b/kcc/__init__.py @@ -1,4 +1,4 @@ -__version__ = '4.5' +__version__ = '4.5.1' __license__ = 'ISC' __copyright__ = '2012-2015, Ciro Mattia Gonano , Pawel Jastrzebski ' __docformat__ = 'restructuredtext en' \ No newline at end of file diff --git a/kcc/cbxarchive.py b/kcc/cbxarchive.py index 0a10b92..823a4c8 100644 --- a/kcc/cbxarchive.py +++ b/kcc/cbxarchive.py @@ -22,6 +22,7 @@ from zipfile import is_zipfile, ZipFile from subprocess import STDOUT, PIPE from psutil import Popen from shutil import move, copy +from scandir import walk from . import rarfile from .shared import check7ZFile as is_7zfile, saferReplace @@ -45,7 +46,7 @@ class CBxArchive: cbzFile = ZipFile(self.origFileName) filelist = [] for f in cbzFile.namelist(): - if f.startswith('__MACOSX') or f.endswith('.DS_Store') or f.endswith('thumbs.db'): + if f.startswith('__MACOSX') or f.endswith('.DS_Store') or f.endswith('humbs.db'): pass # skip MacOS special files elif f.endswith('/'): try: @@ -58,25 +59,18 @@ class CBxArchive: def extractCBR(self, targetdir): cbrFile = rarfile.RarFile(self.origFileName) - filelist = [] - for f in cbrFile.namelist(): - if f.startswith('__MACOSX') or f.endswith('.DS_Store') or f.endswith('thumbs.db'): - pass # skip MacOS special files - elif f.endswith('/'): - try: - os.makedirs(os.path.join(targetdir, f)) - except Exception: - pass # the dir exists so we are going to extract the images only. - else: - filelist.append(f) - cbrFile.extractall(targetdir, filelist) + cbrFile.extractall(targetdir) + for root, dirnames, filenames in walk(targetdir): + for filename in filenames: + if filename.startswith('__MACOSX') or filename.endswith('.DS_Store') or filename.endswith('humbs.db'): + os.remove(os.path.join(root, filename)) def extractCB7(self, targetdir): # Workaround for some wide UTF-8 + Popen abnormalities if sys.platform.startswith('darwin'): copy(self.origFileName, os.path.join(os.path.dirname(self.origFileName), 'TMP_KCC_TMP')) self.origFileName = os.path.join(os.path.dirname(self.origFileName), 'TMP_KCC_TMP') - output = Popen('7za x "' + self.origFileName + '" -xr!__MACOSX -xr!.DS_Store -xr!thumbs.db -o"' + output = Popen('7za x "' + self.origFileName + '" -xr!__MACOSX -xr!.DS_Store -xr!thumbs.db -xr!Thumbs.db -o"' + targetdir + '"', stdout=PIPE, stderr=STDOUT, shell=True) extracted = False for line in output.stdout: diff --git a/kcc/comic2ebook.py b/kcc/comic2ebook.py index 7b5f725..fa6c49f 100755 --- a/kcc/comic2ebook.py +++ b/kcc/comic2ebook.py @@ -293,7 +293,10 @@ def buildOPF(dstdir, title, filelist, cover=None): f.write("\n") f.write("\n") - f.write("\n\n") + if options.righttoleft: + f.write("\n\n") + else: + f.write("\n\n") for entry in reflist: f.write("\n") f.write("\n\n\n\n") @@ -576,7 +579,7 @@ def getWorkFolder(afile): if len(afile) > 240: raise UserWarning("Path is too long.") if os.path.isdir(afile): - workdir = mkdtemp('', 'KCC-TMP-') + workdir = mkdtemp('', 'KCC-') try: os.rmdir(workdir) fullPath = os.path.join(workdir, 'OEBPS', 'Images') @@ -595,7 +598,7 @@ def getWorkFolder(afile): rmtree(path, True) raise UserWarning("Failed to extract images.") else: - workdir = mkdtemp('', 'KCC-TMP-') + workdir = mkdtemp('', 'KCC-') cbx = cbxarchive.CBxArchive(afile) if cbx.isCbxFile(): try: @@ -895,9 +898,12 @@ def detectCorruption(tmpPath, orgPath): img.verify() img = Image.open(path) img.load() - except Exception: + except Exception as err: rmtree(os.path.join(tmpPath, '..', '..'), True) - raise RuntimeError('Image file %s is corrupted.' % pathOrg) + if 'decoder' in err and 'not available' in err: + raise RuntimeError('Pillow was compiled without JPG and/or PNG decoder.') + else: + raise RuntimeError('Image file %s is corrupted.' % pathOrg) else: os.remove(os.path.join(root, name)) @@ -932,7 +938,7 @@ def detectMargins(path): def createNewTome(): - tomePathRoot = mkdtemp('', 'KCC-TMP-') + tomePathRoot = mkdtemp('', 'KCC-') tomePath = os.path.join(tomePathRoot, 'OEBPS', 'Images') os.makedirs(tomePath) return tomePath, tomePathRoot @@ -971,7 +977,7 @@ def makeParser(): 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, KoA, KoAHD, KoAH2O) [Default=KV]") + " KFA, 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,7 +1043,7 @@ def checkOptions(): options.format = 'MOBI' elif options.profile in ['Other']: options.format = 'EPUB' - elif options.profile in ['KDX', 'KoMT', 'KoG', 'KoA', 'KoAHD', 'KoAH2O']: + elif options.profile in ['KDX', 'KoMT', 'KoG', 'KoGHD', 'KoA', 'KoAHD', 'KoAH2O']: options.format = 'CBZ' if options.white_borders: options.bordersColor = 'white' diff --git a/kcc/image.py b/kcc/image.py index d830e79..3235abe 100755 --- a/kcc/image.py +++ b/kcc/image.py @@ -89,7 +89,8 @@ class ProfileData: 'KFHDX8': ("K. Fire HDX 8.9", (1600, 2560), PalleteNull, 1.0, (2400, 3840)), 'KFA': ("Kindle for Android", (0, 0), PalleteNull, 1.0, (0, 0)), 'KoMT': ("Kobo Mini/Touch", (600, 800), Palette16, 1.8, (900, 1200)), - 'KoG': ("Kobo Glow", (768, 1024), Palette16, 1.8, (1152, 1536)), + 'KoG': ("Kobo Glo", (768, 1024), Palette16, 1.8, (1152, 1536)), + 'KoGHD': ("Kobo Glo HD", (1072, 1448), Palette16, 1.8, (1608, 2172)), 'KoA': ("Kobo Aura", (758, 1024), Palette16, 1.8, (1137, 1536)), 'KoAHD': ("Kobo Aura HD", (1080, 1440), Palette16, 1.8, (1620, 2160)), 'KoAH2O': ("Kobo Aura H2O", (1080, 1430), Palette16, 1.8, (1620, 2145)), diff --git a/kcc/metadata.py b/kcc/metadata.py index 697a554..5e08d43 100644 --- a/kcc/metadata.py +++ b/kcc/metadata.py @@ -64,7 +64,7 @@ class MetadataParser: self.rawdata = parse(xml_file) elif is_7zfile(self.source): self.compressor = '7z' - workdir = mkdtemp('', 'KCC-TMP-') + workdir = mkdtemp('', 'KCC-') tmpXML = os.path.join(workdir, 'ComicInfo.xml') output = Popen('7za e "' + self.source + '" ComicInfo.xml -o"' + workdir + '"', stdout=PIPE, stderr=STDOUT, shell=True) @@ -147,7 +147,7 @@ class MetadataParser: with open(self.source, 'w', encoding='utf-8') as f: self.rawdata.writexml(f, encoding='utf-8') else: - workdir = mkdtemp('', 'KCC-TMP-') + workdir = mkdtemp('', 'KCC-') tmpXML = os.path.join(workdir, 'ComicInfo.xml') with open(tmpXML, 'w', encoding='utf-8') as f: self.rawdata.writexml(f, encoding='utf-8') diff --git a/kcc/pdfjpgextract.py b/kcc/pdfjpgextract.py index a4c0203..8cd9d6d 100644 --- a/kcc/pdfjpgextract.py +++ b/kcc/pdfjpgextract.py @@ -29,7 +29,7 @@ class PdfJpgExtract: self.origFileName = origFileName self.filename = os.path.splitext(origFileName) # noinspection PyUnusedLocal - self.path = self.filename[0] + "-KCC-TMP-" + ''.join(choice(ascii_uppercase + digits) for x in range(3)) + self.path = self.filename[0] + "-KCC-" + ''.join(choice(ascii_uppercase + digits) for x in range(3)) def getPath(self): return self.path diff --git a/kcc/shared.py b/kcc/shared.py index 1789749..eee3e51 100644 --- a/kcc/shared.py +++ b/kcc/shared.py @@ -104,9 +104,9 @@ def saferReplace(old, new): def removeFromZIP(zipfname, *filenames): - tempdir = mkdtemp('', 'KCC-TMP-') + tempdir = mkdtemp('', 'KCC-') try: - tempname = os.path.join(tempdir, 'KCC-TMP.zip') + tempname = os.path.join(tempdir, 'KCC.zip') with ZipFile(zipfname, 'r') as zipread: with ZipFile(tempname, 'w', compression=ZIP_DEFLATED) as zipwrite: for item in zipread.infolist():