1
0
mirror of https://github.com/ciromattia/kcc synced 2026-04-18 23:19:00 +00:00

Merge pull request #137 from ciromattia/dev

4.5.1
This commit is contained in:
Paweł Jastrzębski
2015-05-09 09:29:42 +02:00
10 changed files with 48 additions and 36 deletions

View File

@@ -1,7 +1,7 @@
# KCC # 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. **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**_. 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. It can also optionally optimize images by applying a number of transformations.
@@ -75,7 +75,7 @@ Options:
MAIN: MAIN:
-p PROFILE, --profile=PROFILE -p PROFILE, --profile=PROFILE
Device profile (Available options: K1, K2, K345, KDX, 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] KoAHD, KoAH2O) [Default=KV]
-q QUALITY, --quality=QUALITY -q QUALITY, --quality=QUALITY
Quality of Panel View. 0 - Normal 1 - High 2 - Ultra [Default=0] 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](http://kcc.iosphe.re/Samples/Ubunchu!-K345.mobi)
* [Kindle DX/DXG](http://kcc.iosphe.re/Samples/Ubunchu!-KDX.cbz) * [Kindle DX/DXG](http://kcc.iosphe.re/Samples/Ubunchu!-KDX.cbz)
* [Kobo Mini/Touch](http://kcc.iosphe.re/Samples/Ubunchu!-KoMT.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](http://kcc.iosphe.re/Samples/Ubunchu!-KoA.cbz)
* [Kobo Aura HD](http://kcc.iosphe.re/Samples/Ubunchu!-KoAHD.cbz) * [Kobo Aura HD](http://kcc.iosphe.re/Samples/Ubunchu!-KoAHD.cbz)
* [Kobo Aura H2O](http://kcc.iosphe.re/Samples/Ubunchu!-KoAH2O.cbz) * [Kobo Aura H2O](http://kcc.iosphe.re/Samples/Ubunchu!-KoAH2O.cbz)
## CHANGELOG ## CHANGELOG
####4.5.1:
* Added Kobo Glo HD profile
* Fixed RAR/CBR parsing anomalies
* Minor bug fixes and tweaks
####4.5: ####4.5:
* Added simple ComicRack metadata editor * Added simple ComicRack metadata editor
* Re-enabled Manga Cover Database support * Re-enabled Manga Cover Database support

View File

@@ -1,5 +1,5 @@
#define MyAppName "Kindle Comic Converter" #define MyAppName "Kindle Comic Converter"
#define MyAppVersion "4.5" #define MyAppVersion "4.5.1"
#define MyAppPublisher "Ciro Mattia Gonano, Paweł Jastrzębski" #define MyAppPublisher "Ciro Mattia Gonano, Paweł Jastrzębski"
#define MyAppURL "http://kcc.iosphe.re/" #define MyAppURL "http://kcc.iosphe.re/"
#define MyAppExeName "KCC.exe" #define MyAppExeName "KCC.exe"

View File

@@ -1177,8 +1177,10 @@ class KCCGUI(KCC_ui.Ui_KCC):
'DefaultUpscale': True, 'Label': 'KFHDX8'}, 'DefaultUpscale': True, 'Label': 'KFHDX8'},
"Kobo Mini/Touch": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 2, "Kobo Mini/Touch": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 2,
'DefaultUpscale': False, 'Label': 'KoMT'}, 'DefaultUpscale': False, 'Label': 'KoMT'},
"Kobo Glow": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 2, "Kobo Glo": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 2,
'DefaultUpscale': False, 'Label': 'KoG'}, '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, "Kobo Aura": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 2,
'DefaultUpscale': False, 'Label': 'KoA'}, 'DefaultUpscale': False, 'Label': 'KoA'},
"Kobo Aura HD": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 2, "Kobo Aura HD": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 2,
@@ -1204,7 +1206,8 @@ class KCCGUI(KCC_ui.Ui_KCC):
"K. Fire HDX 8.9", "K. Fire HDX 8.9",
"Separator", "Separator",
"Kobo Mini/Touch", "Kobo Mini/Touch",
"Kobo Glow", "Kobo Glo",
"Kobo Glo HD",
"Kobo Aura", "Kobo Aura",
"Kobo Aura HD", "Kobo Aura HD",
"Kobo Aura H2O", "Kobo Aura H2O",
@@ -1339,6 +1342,8 @@ class KCCGUI_MetaEditor(KCC_MetaEditor_ui.Ui_MetaEditorDialog):
field.setText(self.parser.data[field.objectName()[:-4]]) field.setText(self.parser.data[field.objectName()[:-4]])
for field in (self.WriterLine, self.PencillerLine, self.InkerLine, self.ColoristLine): for field in (self.WriterLine, self.PencillerLine, self.InkerLine, self.ColoristLine):
field.setText(', '.join(self.parser.data[field.objectName()[:-4] + 's'])) 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): def saveData(self):
for field in (self.VolumeLine, self.NumberLine, self.MUidLine): for field in (self.VolumeLine, self.NumberLine, self.MUidLine):

View File

@@ -1,4 +1,4 @@
__version__ = '4.5' __version__ = '4.5.1'
__license__ = 'ISC' __license__ = 'ISC'
__copyright__ = '2012-2015, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>' __copyright__ = '2012-2015, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'

View File

@@ -22,6 +22,7 @@ from zipfile import is_zipfile, ZipFile
from subprocess import STDOUT, PIPE from subprocess import STDOUT, PIPE
from psutil import Popen from psutil import Popen
from shutil import move, copy from shutil import move, copy
from scandir import walk
from . import rarfile from . import rarfile
from .shared import check7ZFile as is_7zfile, saferReplace from .shared import check7ZFile as is_7zfile, saferReplace
@@ -45,7 +46,7 @@ class CBxArchive:
cbzFile = ZipFile(self.origFileName) cbzFile = ZipFile(self.origFileName)
filelist = [] filelist = []
for f in cbzFile.namelist(): 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 pass # skip MacOS special files
elif f.endswith('/'): elif f.endswith('/'):
try: try:
@@ -58,25 +59,18 @@ class CBxArchive:
def extractCBR(self, targetdir): def extractCBR(self, targetdir):
cbrFile = rarfile.RarFile(self.origFileName) cbrFile = rarfile.RarFile(self.origFileName)
filelist = [] cbrFile.extractall(targetdir)
for f in cbrFile.namelist(): for root, dirnames, filenames in walk(targetdir):
if f.startswith('__MACOSX') or f.endswith('.DS_Store') or f.endswith('thumbs.db'): for filename in filenames:
pass # skip MacOS special files if filename.startswith('__MACOSX') or filename.endswith('.DS_Store') or filename.endswith('humbs.db'):
elif f.endswith('/'): os.remove(os.path.join(root, filename))
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)
def extractCB7(self, targetdir): def extractCB7(self, targetdir):
# Workaround for some wide UTF-8 + Popen abnormalities # Workaround for some wide UTF-8 + Popen abnormalities
if sys.platform.startswith('darwin'): if sys.platform.startswith('darwin'):
copy(self.origFileName, os.path.join(os.path.dirname(self.origFileName), 'TMP_KCC_TMP')) 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') 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) + targetdir + '"', stdout=PIPE, stderr=STDOUT, shell=True)
extracted = False extracted = False
for line in output.stdout: for line in output.stdout:

View File

@@ -293,7 +293,10 @@ def buildOPF(dstdir, title, filelist, cover=None):
f.write("<item id=\"img_" + str(uniqueid) + "\" href=\"" + folder + "/" + path[1] + "\" media-type=\"" f.write("<item id=\"img_" + str(uniqueid) + "\" href=\"" + folder + "/" + path[1] + "\" media-type=\""
+ mt + "\"/>\n") + mt + "\"/>\n")
f.write("<item id=\"css\" href=\"Text/style.css\" media-type=\"text/css\"/>\n") f.write("<item id=\"css\" href=\"Text/style.css\" media-type=\"text/css\"/>\n")
f.write("</manifest>\n<spine toc=\"ncx\">\n") if options.righttoleft:
f.write("</manifest>\n<spine page-progression-direction=\"rtl\" toc=\"ncx\">\n")
else:
f.write("</manifest>\n<spine page-progression-direction=\"ltr\" toc=\"ncx\">\n")
for entry in reflist: for entry in reflist:
f.write("<itemref idref=\"page_" + entry + "\"/>\n") f.write("<itemref idref=\"page_" + entry + "\"/>\n")
f.write("</spine>\n<guide>\n</guide>\n</package>\n") f.write("</spine>\n<guide>\n</guide>\n</package>\n")
@@ -576,7 +579,7 @@ def getWorkFolder(afile):
if len(afile) > 240: if len(afile) > 240:
raise UserWarning("Path is too long.") raise UserWarning("Path is too long.")
if os.path.isdir(afile): if os.path.isdir(afile):
workdir = mkdtemp('', 'KCC-TMP-') workdir = mkdtemp('', 'KCC-')
try: try:
os.rmdir(workdir) os.rmdir(workdir)
fullPath = os.path.join(workdir, 'OEBPS', 'Images') fullPath = os.path.join(workdir, 'OEBPS', 'Images')
@@ -595,7 +598,7 @@ def getWorkFolder(afile):
rmtree(path, True) rmtree(path, True)
raise UserWarning("Failed to extract images.") raise UserWarning("Failed to extract images.")
else: else:
workdir = mkdtemp('', 'KCC-TMP-') workdir = mkdtemp('', 'KCC-')
cbx = cbxarchive.CBxArchive(afile) cbx = cbxarchive.CBxArchive(afile)
if cbx.isCbxFile(): if cbx.isCbxFile():
try: try:
@@ -895,9 +898,12 @@ def detectCorruption(tmpPath, orgPath):
img.verify() img.verify()
img = Image.open(path) img = Image.open(path)
img.load() img.load()
except Exception: except Exception as err:
rmtree(os.path.join(tmpPath, '..', '..'), True) 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: else:
os.remove(os.path.join(root, name)) os.remove(os.path.join(root, name))
@@ -932,7 +938,7 @@ def detectMargins(path):
def createNewTome(): def createNewTome():
tomePathRoot = mkdtemp('', 'KCC-TMP-') tomePathRoot = mkdtemp('', 'KCC-')
tomePath = os.path.join(tomePathRoot, 'OEBPS', 'Images') tomePath = os.path.join(tomePathRoot, 'OEBPS', 'Images')
os.makedirs(tomePath) os.makedirs(tomePath)
return tomePath, tomePathRoot return tomePath, tomePathRoot
@@ -971,7 +977,7 @@ def makeParser():
mainOptions.add_option("-p", "--profile", action="store", dest="profile", default="KV", 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," 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", mainOptions.add_option("-q", "--quality", type="int", dest="quality", default="0",
help="Quality of Panel View. 0 - Normal 1 - High 2 - Ultra [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, mainOptions.add_option("-m", "--manga-style", action="store_true", dest="righttoleft", default=False,
@@ -1037,7 +1043,7 @@ def checkOptions():
options.format = 'MOBI' options.format = 'MOBI'
elif options.profile in ['Other']: elif options.profile in ['Other']:
options.format = 'EPUB' 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' options.format = 'CBZ'
if options.white_borders: if options.white_borders:
options.bordersColor = 'white' options.bordersColor = 'white'

View File

@@ -89,7 +89,8 @@ class ProfileData:
'KFHDX8': ("K. Fire HDX 8.9", (1600, 2560), PalleteNull, 1.0, (2400, 3840)), 'KFHDX8': ("K. Fire HDX 8.9", (1600, 2560), PalleteNull, 1.0, (2400, 3840)),
'KFA': ("Kindle for Android", (0, 0), PalleteNull, 1.0, (0, 0)), 'KFA': ("Kindle for Android", (0, 0), PalleteNull, 1.0, (0, 0)),
'KoMT': ("Kobo Mini/Touch", (600, 800), Palette16, 1.8, (900, 1200)), '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)), 'KoA': ("Kobo Aura", (758, 1024), Palette16, 1.8, (1137, 1536)),
'KoAHD': ("Kobo Aura HD", (1080, 1440), Palette16, 1.8, (1620, 2160)), 'KoAHD': ("Kobo Aura HD", (1080, 1440), Palette16, 1.8, (1620, 2160)),
'KoAH2O': ("Kobo Aura H2O", (1080, 1430), Palette16, 1.8, (1620, 2145)), 'KoAH2O': ("Kobo Aura H2O", (1080, 1430), Palette16, 1.8, (1620, 2145)),

View File

@@ -64,7 +64,7 @@ class MetadataParser:
self.rawdata = parse(xml_file) self.rawdata = parse(xml_file)
elif is_7zfile(self.source): elif is_7zfile(self.source):
self.compressor = '7z' self.compressor = '7z'
workdir = mkdtemp('', 'KCC-TMP-') workdir = mkdtemp('', 'KCC-')
tmpXML = os.path.join(workdir, 'ComicInfo.xml') tmpXML = os.path.join(workdir, 'ComicInfo.xml')
output = Popen('7za e "' + self.source + '" ComicInfo.xml -o"' + workdir + '"', output = Popen('7za e "' + self.source + '" ComicInfo.xml -o"' + workdir + '"',
stdout=PIPE, stderr=STDOUT, shell=True) stdout=PIPE, stderr=STDOUT, shell=True)
@@ -147,7 +147,7 @@ class MetadataParser:
with open(self.source, 'w', encoding='utf-8') as f: with open(self.source, 'w', encoding='utf-8') as f:
self.rawdata.writexml(f, encoding='utf-8') self.rawdata.writexml(f, encoding='utf-8')
else: else:
workdir = mkdtemp('', 'KCC-TMP-') workdir = mkdtemp('', 'KCC-')
tmpXML = os.path.join(workdir, 'ComicInfo.xml') tmpXML = os.path.join(workdir, 'ComicInfo.xml')
with open(tmpXML, 'w', encoding='utf-8') as f: with open(tmpXML, 'w', encoding='utf-8') as f:
self.rawdata.writexml(f, encoding='utf-8') self.rawdata.writexml(f, encoding='utf-8')

View File

@@ -29,7 +29,7 @@ class PdfJpgExtract:
self.origFileName = origFileName self.origFileName = origFileName
self.filename = os.path.splitext(origFileName) self.filename = os.path.splitext(origFileName)
# noinspection PyUnusedLocal # 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): def getPath(self):
return self.path return self.path

View File

@@ -104,9 +104,9 @@ def saferReplace(old, new):
def removeFromZIP(zipfname, *filenames): def removeFromZIP(zipfname, *filenames):
tempdir = mkdtemp('', 'KCC-TMP-') tempdir = mkdtemp('', 'KCC-')
try: try:
tempname = os.path.join(tempdir, 'KCC-TMP.zip') tempname = os.path.join(tempdir, 'KCC.zip')
with ZipFile(zipfname, 'r') as zipread: with ZipFile(zipfname, 'r') as zipread:
with ZipFile(tempname, 'w', compression=ZIP_DEFLATED) as zipwrite: with ZipFile(tempname, 'w', compression=ZIP_DEFLATED) as zipwrite:
for item in zipread.infolist(): for item in zipread.infolist():