1
0
mirror of https://github.com/ciromattia/kcc synced 2025-12-13 09:46:25 +00:00

Merge pull request #215 from ciromattia/dev

5.2
This commit is contained in:
Paweł Jastrzębski
2016-11-22 08:53:56 +01:00
committed by GitHub
12 changed files with 185 additions and 285 deletions

View File

@@ -75,9 +75,11 @@ Usage: kcc-c2e [options] comic_file|comic_folder
Options:
MAIN:
-p PROFILE, --profile=PROFILE
Device profile (Available options: K1, K2, K3, K45, KDX,
KPW, KV, KoMT, KoG, KoGHD, KoA, KoAHD, KoAH2O, KoAO) [Default=KV]
Device profile (Available options: K1, K2, K3, K45,
KDX, KPW, KV, KoMT, KoG, KoGHD, KoA, KoAHD, KoAH2O,
KoAO) [Default=KV]
-m, --manga-style Manga style (right-to-left reading and splitting)
-2, --two-panel Display two not four panels in Panel View mode
-w, --webtoon Webtoon processing mode
OUTPUT SETTINGS:
@@ -86,29 +88,28 @@ Options:
-t TITLE, --title=TITLE
Comic title [Default=filename or directory name]
-f FORMAT, --format=FORMAT
Output format (Available options: Auto, MOBI, EPUB, CBZ)
[Default=Auto]
Output format (Available options: Auto, MOBI, EPUB,
CBZ) [Default=Auto]
-b, --batchsplit Split output into multiple files
PROCESSING:
-u, --upscale Resize images smaller than device's resolution
-s, --stretch Stretch images to device's resolution
-r SPLITTER, --splitter=SPLITTER
Double page parsing mode. 0: Split 1: Rotate 2: Both [Default=0]
Double page parsing mode. 0: Split 1: Rotate 2: Both
[Default=0]
-g GAMMA, --gamma=GAMMA
Apply gamma correction to linearize the image [Default=Auto]
--hq Enable high quality Panel View
Apply gamma correction to linearize the image
[Default=Auto]
-c CROPPING, --cropping=CROPPING
Set cropping mode. 0: Disabled 1: Margins 2: Margins +
page numbers [Default=2]
--cp=CROPPINGP, --croppingpower=CROPPINGP
Set cropping power [Default=1.0]
--blackborders Disable autodetection and force black borders
--whiteborders Disable autodetection and force white borders
--forcecolor Don't convert images to grayscale
--forcepng Create PNG files instead JPEG
--cropping=CROPPING
Set cropping mode. 0: Disabled 1: Margins 2: Margins +
page numbers [Default=2]
--croppingpower=CROPPINGP
Set margin cropping threshold [Default=0.1]
--croppingpowerpage=CROPPINGPN
Set page number cropping threshold [Default=5.0]
CUSTOM PROFILE:
--customwidth=CUSTOMWIDTH
@@ -159,6 +160,12 @@ The app relies and includes the following scripts:
* [Kobo Aura ONE](http://kcc.iosphe.re/Samples/Ubunchu-KoAO.kepub.epub)
## CHANGELOG
####5.2:
* Added new Panel View options
* Implemented new margin detection algorithm
* Removed HQ Panel View mode
* Fixed multiple smaller issues
####5.1.3:
* Added Kobo Aura ONE profile
* Fixed few small bugs

View File

@@ -174,10 +174,10 @@
<item row="0" column="2">
<widget class="QCheckBox" name="qualityBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;High quality Panel View.&lt;br/&gt;Require source files with bigger resolution than target device.&lt;br/&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Highly impact size of output file!&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style=\'white-space:pre\'&gt;&lt;span style=\&quot; font-weight:600; text-decoration: underline;\&quot;&gt;Unchecked - 4 panels&lt;br/&gt;&lt;/span&gt;Zoom each corner separately.&lt;/p&gt;&lt;p style=\'white-space:pre\'&gt;&lt;span style=\&quot; font-weight:600; text-decoration: underline;\&quot;&gt;Checked - 2 panels&lt;br/&gt;&lt;/span&gt;Zoom only the top and bottom of the page.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>HQ zoom</string>
<string>Panel View 4/2</string>
</property>
</widget>
</item>

View File

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

View File

@@ -33,7 +33,7 @@ from distutils.version import StrictVersion
from xml.sax.saxutils import escape
from platform import platform
from raven import Client
from .shared import md5Checksum, HTMLStripper, sanitizeTrace
from .shared import md5Checksum, HTMLStripper, sanitizeTrace, saferRemove
from . import __version__
from . import comic2ebook
from . import metadata
@@ -257,7 +257,7 @@ class WorkerThread(QtCore.QThread):
elif GUI.rotateBox.checkState() == 2:
options.splitter = 1
if GUI.qualityBox.isChecked():
options.hqmode = True
options.autoscale = True
if GUI.webtoonBox.isChecked():
options.webtoon = True
if GUI.upscaleBox.checkState() == 1:
@@ -331,7 +331,7 @@ class WorkerThread(QtCore.QThread):
if 'outputPath' in locals():
for item in outputPath:
if os.path.exists(item):
os.remove(item)
saferRemove(item)
self.clean()
return
if not self.errors:
@@ -358,9 +358,9 @@ class WorkerThread(QtCore.QThread):
if not self.conversionAlive:
for item in outputPath:
if os.path.exists(item):
os.remove(item)
saferRemove(item)
if os.path.exists(item.replace('.epub', '.mobi')):
os.remove(item.replace('.epub', '.mobi'))
saferRemove(item.replace('.epub', '.mobi'))
self.clean()
return
if self.kindlegenErrorCode[0] == 0:
@@ -381,7 +381,7 @@ class WorkerThread(QtCore.QThread):
for item in outputPath:
GUI.progress.content = ''
mobiPath = item.replace('.epub', '.mobi')
os.remove(mobiPath + '_toclean')
saferRemove(mobiPath + '_toclean')
if GUI.targetDirectory and GUI.targetDirectory != os.path.dirname(mobiPath):
try:
move(mobiPath, GUI.targetDirectory)
@@ -393,15 +393,15 @@ class WorkerThread(QtCore.QThread):
for item in outputPath:
comic2ebook.options.covers[outputPath.index(item)][0].saveToKindle(
k, comic2ebook.options.covers[outputPath.index(item)][1])
MW.addMessage.emit('Kindle detected. Uploading covers...', 'info', False)
MW.addMessage.emit('Kindle detected. Uploading covers... <b>Done!</b>', 'info', False)
else:
GUI.progress.content = ''
for item in outputPath:
mobiPath = item.replace('.epub', '.mobi')
if os.path.exists(mobiPath):
os.remove(mobiPath)
saferRemove(mobiPath)
if os.path.exists(mobiPath + '_toclean'):
os.remove(mobiPath + '_toclean')
saferRemove(mobiPath + '_toclean')
MW.addMessage.emit('Failed to process MOBI file!', 'error', False)
MW.addTrayMessage.emit('Failed to process MOBI file!', 'Critical')
else:
@@ -409,9 +409,9 @@ class WorkerThread(QtCore.QThread):
epubSize = (os.path.getsize(self.kindlegenErrorCode[2])) // 1024 // 1024
for item in outputPath:
if os.path.exists(item):
os.remove(item)
saferRemove(item)
if os.path.exists(item.replace('.epub', '.mobi')):
os.remove(item.replace('.epub', '.mobi'))
saferRemove(item.replace('.epub', '.mobi'))
MW.addMessage.emit('KindleGen failed to create MOBI!', 'error', False)
MW.addTrayMessage.emit('KindleGen failed to create MOBI!', 'Critical')
if self.kindlegenErrorCode[0] == 1 and self.kindlegenErrorCode[1] != '':
@@ -603,7 +603,9 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
GUI.upscaleBox.setEnabled(False)
GUI.upscaleBox.setChecked(True)
else:
GUI.qualityBox.setEnabled(True)
profile = GUI.profiles[str(GUI.deviceBox.currentText())]
if profile['PVOptions']:
GUI.qualityBox.setEnabled(True)
GUI.mangaBox.setEnabled(True)
GUI.rotateBox.setEnabled(True)
GUI.upscaleBox.setEnabled(True)
@@ -629,9 +631,9 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
self.changeFormat()
GUI.gammaSlider.setValue(0)
self.changeGamma(0)
GUI.qualityBox.setEnabled(profile['Quality'])
GUI.qualityBox.setEnabled(profile['PVOptions'])
GUI.upscaleBox.setChecked(profile['DefaultUpscale'])
if not profile['Quality']:
if not profile['PVOptions']:
GUI.qualityBox.setChecked(False)
if str(GUI.deviceBox.currentText()) == 'Other':
self.addMessage('<a href="https://github.com/ciromattia/kcc/wiki/NonKindle-devices">'
@@ -643,7 +645,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
GUI.formatBox.setCurrentIndex(outputFormat)
else:
GUI.formatBox.setCurrentIndex(profile['DefaultFormat'])
GUI.qualityBox.setEnabled(profile['Quality'])
GUI.qualityBox.setEnabled(profile['PVOptions'])
def stripTags(self, html):
s = HTMLStripper()
@@ -894,39 +896,39 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
MW.resize(500, 500)
self.profiles = {
"Kindle Oasis": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0,
"Kindle Oasis": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': True, 'Label': 'KV'},
"Kindle Voyage": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0,
"Kindle Voyage": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': True, 'Label': 'KV'},
"Kindle PW 3": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0,
"Kindle PW 3": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': True, 'Label': 'KV'},
"Kindle PW 1/2": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0,
"Kindle PW 1/2": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': False, 'Label': 'KPW'},
"Kindle": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0,
"Kindle": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': False, 'Label': 'K45'},
"Kindle DX/DXG": {'Quality': False, 'ForceExpert': False, 'DefaultFormat': 2,
"Kindle DX/DXG": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 2,
'DefaultUpscale': False, 'Label': 'KDX'},
"Kobo Mini/Touch": {'Quality': False, 'ForceExpert': False, 'DefaultFormat': 1,
"Kobo Mini/Touch": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1,
'DefaultUpscale': False, 'Label': 'KoMT'},
"Kobo Glo": {'Quality': False, 'ForceExpert': False, 'DefaultFormat': 1,
"Kobo Glo": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1,
'DefaultUpscale': False, 'Label': 'KoG'},
"Kobo Glo HD": {'Quality': False, 'ForceExpert': False, 'DefaultFormat': 1,
"Kobo Glo HD": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1,
'DefaultUpscale': False, 'Label': 'KoGHD'},
"Kobo Aura": {'Quality': False, 'ForceExpert': False, 'DefaultFormat': 1,
"Kobo Aura": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1,
'DefaultUpscale': False, 'Label': 'KoA'},
"Kobo Aura HD": {'Quality': False, 'ForceExpert': False, 'DefaultFormat': 1,
"Kobo Aura HD": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1,
'DefaultUpscale': True, 'Label': 'KoAHD'},
"Kobo Aura H2O": {'Quality': False, 'ForceExpert': False, 'DefaultFormat': 1,
"Kobo Aura H2O": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1,
'DefaultUpscale': True, 'Label': 'KoAH2O'},
"Kobo Aura ONE": {'Quality': False, 'ForceExpert': False, 'DefaultFormat': 1,
"Kobo Aura ONE": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1,
'DefaultUpscale': True, 'Label': 'KoAO'},
"Other": {'Quality': False, 'ForceExpert': True, 'DefaultFormat': 1,
"Other": {'PVOptions': False, 'ForceExpert': True, 'DefaultFormat': 1,
'DefaultUpscale': False, 'Label': 'OTHER'},
"Kindle 1": {'Quality': False, 'ForceExpert': False, 'DefaultFormat': 0,
"Kindle 1": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': False, 'Label': 'K1'},
"Kindle 2": {'Quality': False, 'ForceExpert': False, 'DefaultFormat': 0,
"Kindle 2": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': False, 'Label': 'K2'},
"Kindle 3": {'Quality': False, 'ForceExpert': False, 'DefaultFormat': 0,
"Kindle 3": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': False, 'Label': 'K3'},
}
profilesGUI = [

View File

@@ -241,8 +241,8 @@ class Ui_mainWindow(object):
self.mangaBox.setText(_translate("mainWindow", "Manga mode"))
self.rotateBox.setToolTip(_translate("mainWindow", "<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Split<br/></span>Double page spreads will be cut into two separate pages.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - Rotate and split<br/></span>Double page spreads will be displayed twice. First rotated and then split. </p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Rotate<br/></span>Double page spreads will be rotated.</p></body></html>"))
self.rotateBox.setText(_translate("mainWindow", "Spread splitter"))
self.qualityBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>High quality Panel View.<br/>Require source files with bigger resolution than target device.<br/><span style=\" font-weight:600;\">Highly impact size of output file!</span></p></body></html>"))
self.qualityBox.setText(_translate("mainWindow", "HQ zoom"))
self.qualityBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - 4 panels<br/></span>Zoom each corner separately.</p><p style=\'white-space:pre\'><span style=\" font-weight:600; text-decoration: underline;\">Checked - 2 panels<br/></span>Zoom only the top and bottom of the page.</p></body></html>"))
self.qualityBox.setText(_translate("mainWindow", "Panel View 4/2"))
self.webtoonBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Enable special parsing mode for Korean Webtoons.</p></body></html>"))
self.webtoonBox.setText(_translate("mainWindow", "Webtoon mode"))
self.upscaleBox.setToolTip(_translate("mainWindow", "<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Nothing<br/></span>Images smaller than device resolution will not be resized.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - Stretching<br/></span>Images smaller than device resolution will be resized. Aspect ratio will be not preserved.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Upscaling<br/></span>Images smaller than device resolution will be resized. Aspect ratio will be preserved.</p></body></html>"))

View File

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

View File

@@ -27,7 +27,7 @@ try:
except ImportError:
walk = os.walk
from . import rarfile
from .shared import check7ZFile as is_7zfile, saferReplace
from .shared import check7ZFile as is_7zfile, saferReplace, saferRemove
class CBxArchive:
@@ -66,7 +66,7 @@ class CBxArchive:
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))
saferRemove(os.path.join(root, filename))
def extractCB7(self, targetdir):
# Workaround for some wide UTF-8 + Popen abnormalities
@@ -80,7 +80,7 @@ class CBxArchive:
if b"Everything is Ok" in line:
extracted = True
if sys.platform.startswith('darwin'):
os.remove(self.origFileName)
saferRemove(self.origFileName)
if not extracted:
raise OSError

View File

@@ -95,8 +95,6 @@ def buildHTML(path, imgfile, imgfilepath):
additionalStyle = 'background-color:#FFFFFF;'
htmlpath = ''
postfix = ''
size = ''
imgfilepv = ''
backref = 1
head = path
while True:
@@ -122,21 +120,16 @@ def buildHTML(path, imgfile, imgfilepath):
"<body style=\"background-image: ",
"url('", "../" * backref, "Images/", postfix, imgfile, "'); " + additionalStyle + "\">\n"])
if options.iskindle and options.panelview:
if options.hqmode:
imgfilepv = list(os.path.splitext(imgfile))
imgfilepv[0] += "-hq"
imgfilepv = "".join(imgfilepv)
if os.path.isfile(os.path.join(head, "Images", postfix, imgfilepv)):
size = Image.open(os.path.join(head, "Images", postfix, imgfilepv)).size
if not options.hqmode or not size:
imgfilepv = imgfile
sizeTmp = Image.open(os.path.join(head, "Images", postfix, imgfilepv)).size
sizeTmp = Image.open(os.path.join(head, "Images", postfix, imgfile)).size
if options.autoscale:
size = (getPanelViewResolution(sizeTmp, deviceres))
else:
size = (int(sizeTmp[0] * 1.5), int(sizeTmp[1] * 1.5))
if size[0] <= deviceres[0]:
if size[0] - deviceres[0] < deviceres[0] * 0.01:
noHorizontalPV = True
else:
noHorizontalPV = False
if size[1] <= deviceres[1]:
if size[1] - deviceres[1] < deviceres[1] * 0.01:
noVerticalPV = True
else:
noVerticalPV = False
@@ -193,7 +186,7 @@ def buildHTML(path, imgfile, imgfilepath):
for box in boxes:
f.writelines(["<div class=\"PV-P\" id=\"" + box + "-P\" style=\"" + additionalStyle + "\">\n",
"<img style=\"" + boxStyles[box] + "\" src=\"", "../" * backref, "Images/", postfix,
imgfilepv, "\" width=\"" + str(size[0]) + "\" height=\"" + str(size[1]) + "\"/>\n",
imgfile, "\" width=\"" + str(size[0]) + "\" height=\"" + str(size[1]) + "\"/>\n",
"</div>\n"])
f.writelines(["</body>\n",
"</html>\n"])
@@ -336,29 +329,6 @@ def buildOPF(dstdir, title, filelist, cover=None):
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")
# if options.iskindle and options.profile != 'Custom':
# if options.righttoleft:
# nextflow = 'right'
# else:
# nextflow = 'left'
# for entry in reflist:
# if '-kcc-b' in entry:
# if options.righttoleft:
# f.write("<itemref idref=\"page_" + entry + "\" properties=\"page-spread-right\"/>\n")
# else:
# f.write("<itemref idref=\"page_" + entry + "\" properties=\"page-spread-left\"/>\n")
# elif '-kcc-c' in entry:
# if options.righttoleft:
# f.write("<itemref idref=\"page_" + entry + "\" properties=\"page-spread-left\"/>\n")
# else:
# f.write("<itemref idref=\"page_" + entry + "\" properties=\"page-spread-right\"/>\n")
# else:
# f.write("<itemref idref=\"page_" + entry + "\" properties=\"facing-page-" + nextflow + "\"/>\n")
# if nextflow == 'right':
# nextflow = 'left'
# else:
# nextflow = 'right'
# else:
for entry in reflist:
f.write("<itemref idref=\"page_" + entry + "\"/>\n")
f.write("</spine>\n</package>\n")
@@ -460,17 +430,15 @@ def buildEPUB(path, chapterNames, tomeNumber):
chapter = False
dirnames, filenames = walkSort(dirnames, filenames)
for afile in filenames:
filename = getImageFileName(afile)
if not filename[0].endswith('-hq'):
filelist.append(buildHTML(dirpath, afile, os.path.join(dirpath, afile)))
if not chapter:
chapterlist.append((dirpath.replace('Images', 'Text'), filelist[-1][1]))
chapter = True
if cover is None:
cover = os.path.join(os.path.join(path, 'OEBPS', 'Images'),
'cover' + getImageFileName(filelist[-1][1])[1])
options.covers.append((image.Cover(os.path.join(filelist[-1][0], filelist[-1][1]), cover, options,
tomeNumber), options.uuid))
filelist.append(buildHTML(dirpath, afile, os.path.join(dirpath, afile)))
if not chapter:
chapterlist.append((dirpath.replace('Images', 'Text'), filelist[-1][1]))
chapter = True
if cover is None:
cover = os.path.join(os.path.join(path, 'OEBPS', 'Images'),
'cover' + getImageFileName(filelist[-1][1])[1])
options.covers.append((image.Cover(os.path.join(filelist[-1][0], filelist[-1][1]), cover, options,
tomeNumber), options.uuid))
# Overwrite chapternames if tree is flat and ComicInfo.xml has bookmarks
if not chapterNames and options.chapters:
chapterlist = []
@@ -548,10 +516,10 @@ def imgFileProcessing(work):
workImg = image.ComicPageParser((dirpath, afile), opt)
for i in workImg.payload:
img = image.ComicPage(i[0], i[1], i[2], i[3], i[4], opt)
if opt.cropping > 0 and not opt.webtoon:
img.cropWhiteSpace(opt.croppingp)
if opt.cropping == 2 and not opt.webtoon:
img.cutPageNumber(opt.croppingpn)
img.cropPageNumber(opt.croppingp)
if opt.cropping > 0 and not opt.webtoon:
img.cropMargin(opt.croppingp)
img.autocontrastImage()
img.resizeImage()
if opt.forcepng and not opt.forcecolor:
@@ -653,7 +621,7 @@ def getComicInfo(path, originalPath):
try:
xml = metadata.MetadataParser(xmlPath)
except Exception:
os.remove(xmlPath)
saferRemove(xmlPath)
return
options.authors = []
if defaultTitle:
@@ -678,7 +646,7 @@ def getComicInfo(path, originalPath):
options.chapters = xml.data['Bookmarks']
if xml.data['Summary']:
options.summary = escape(xml.data['Summary'])
os.remove(xmlPath)
saferRemove(xmlPath)
def getCoversFromMCB(mangaID):
@@ -704,6 +672,11 @@ def getDirectorySize(start_path='.'):
return total_size
def getPanelViewResolution(imageSize, deviceRes):
scale = float(deviceRes[0]) / float(imageSize[0])
return int(deviceRes[0]), int(scale * imageSize[1])
def getPanelViewSize(deviceres, size):
x = int(deviceres[0] / 2 - size[0] / 2) / deviceres[0] * 100
y = int(deviceres[1] / 2 - size[1] / 2) / deviceres[1] * 100
@@ -963,6 +936,8 @@ def makeParser():
" KoA, KoAHD, KoAH2O, KoAO) [Default=KV]")
mainOptions.add_option("-m", "--manga-style", action="store_true", dest="righttoleft", default=False,
help="Manga style (right-to-left reading and splitting)")
mainOptions.add_option("-2", "--two-panel", action="store_true", dest="autoscale", default=False,
help="Display two not four panels in Panel View mode")
mainOptions.add_option("-w", "--webtoon", action="store_true", dest="webtoon", default=False,
help="Webtoon processing mode"),
@@ -983,8 +958,10 @@ def makeParser():
help="Double page parsing mode. 0: Split 1: Rotate 2: Both [Default=0]")
processingOptions.add_option("-g", "--gamma", type="float", dest="gamma", default="0.0",
help="Apply gamma correction to linearize the image [Default=Auto]")
processingOptions.add_option("--hq", action="store_true", dest="hqmode", default=False,
help="Enable high quality Panel View")
processingOptions.add_option("-c", "--cropping", type="int", dest="cropping", default="2",
help="Set cropping mode. 0: Disabled 1: Margins 2: Margins + page numbers [Default=2]")
processingOptions.add_option("--cp", "--croppingpower", type="float", dest="croppingp", default="1.0",
help="Set cropping power [Default=1.0]")
processingOptions.add_option("--blackborders", action="store_true", dest="black_borders", default=False,
help="Disable autodetection and force black borders")
processingOptions.add_option("--whiteborders", action="store_true", dest="white_borders", default=False,
@@ -993,12 +970,6 @@ def makeParser():
help="Don't convert images to grayscale")
processingOptions.add_option("--forcepng", action="store_true", dest="forcepng", default=False,
help="Create PNG files instead JPEG")
processingOptions.add_option("--cropping", type="int", dest="cropping", default="2",
help="Set cropping mode. 0: Disabled 1: Margins 2: Margins + page numbers [Default=2]")
processingOptions.add_option("--croppingpower", type="float", dest="croppingp", default="0.1",
help="Set margin cropping threshold [Default=0.1]")
processingOptions.add_option("--croppingpowerpage", type="float", dest="croppingpn", default="5.0",
help="Set page number cropping threshold [Default=5.0]")
customProfileOptions.add_option("--customwidth", type="int", dest="customwidth", default=0,
help="Replace screen width provided by device profile")
@@ -1040,20 +1011,16 @@ def checkOptions():
# Older Kindle don't need higher resolution files due lack of Panel View.
if options.profile == 'K1' or options.profile == 'K2' or options.profile == 'K3' or options.profile == 'KDX':
options.panelview = False
options.hqmode = False
# Webtoon mode mandatory options
if options.webtoon:
options.panelview = False
options.hqmode = False
options.righttoleft = False
options.upscale = True
# Disable all Kindle features for other e-readers
if options.profile == 'OTHER':
options.panelview = False
options.hqmode = False
if 'Ko' in options.profile:
options.panelview = False
options.hqmode = False
# CBZ files on Kindle DX/DXG support higher resolution
if options.profile == 'KDX' and options.format == 'CBZ':
options.customheight = 1200
@@ -1066,7 +1033,7 @@ def checkOptions():
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[options.profile][3])
image.ProfileData.Profiles["Custom"] = newProfile
options.profile = "Custom"
options.profileData = image.ProfileData.Profiles[options.profile]
@@ -1184,7 +1151,6 @@ def makeBook(source, qtGUI=None):
if not GUI and options.format == 'MOBI':
print("Creating MOBI files...")
work = []
k = kindle.Kindle()
for i in filepath:
work.append([i])
output = makeMOBI(work, GUI)
@@ -1193,6 +1159,7 @@ def makeBook(source, qtGUI=None):
print('Error: KindleGen failed to create MOBI!')
print(errors)
return filepath
k = kindle.Kindle()
if k.path and k.coverSupport:
print("Kindle detected. Uploading covers...")
for i in filepath:
@@ -1201,14 +1168,14 @@ def makeBook(source, qtGUI=None):
print('Error: Failed to tweak KindleGen output!')
return filepath
else:
os.remove(i.replace('.epub', '.mobi') + '_toclean')
saferRemove(i.replace('.epub', '.mobi') + '_toclean')
if k.path and k.coverSupport:
options.covers[filepath.index(i)][0].saveToKindle(k, options.covers[filepath.index(i)][1])
return filepath
def makeMOBIFix(item, uuid):
os.remove(item)
saferRemove(item)
mobiPath = item.replace('.epub', '.mobi')
move(mobiPath, mobiPath + '_toclean')
try:

View File

@@ -24,7 +24,7 @@ from shutil import rmtree, copytree, move
from optparse import OptionParser, OptionGroup
from multiprocessing import Pool
from PIL import Image, ImageStat, ImageOps
from .shared import getImageFileName, walkLevel, walkSort
from .shared import getImageFileName, walkLevel, walkSort, saferRemove
try:
from PyQt5 import QtCore
except ImportError:
@@ -77,7 +77,7 @@ def mergeDirectory(work):
img = ImageOps.fit(img, (targetWidth, img.size[1]), method=Image.BICUBIC, centering=(0.5, 0.5))
result.paste(img, (0, y))
y += img.size[1]
os.remove(i)
saferRemove(i)
savePath = os.path.split(imagesValid[0])
result.save(os.path.join(savePath[0], os.path.splitext(savePath[1])[0] + '.png'), 'PNG')
except Exception:
@@ -203,7 +203,7 @@ def splitImage(work):
targetHeight += panels[panel][2]
newPage.save(os.path.join(path, fileExpanded[0] + '-' + str(pageNumber) + '.png'), 'PNG')
pageNumber += 1
os.remove(filePath)
saferRemove(filePath)
except Exception:
return str(sys.exc_info()[1])
@@ -275,7 +275,7 @@ def main(argv=None, qtGUI=None):
pagenumber += 1
work.append([root, name, options])
else:
os.remove(os.path.join(root, name))
saferRemove(os.path.join(root, name))
if GUI:
GUI.progressBarTick.emit('Splitting images')
GUI.progressBarTick.emit(str(pagenumber))

View File

@@ -1,5 +1,6 @@
# Copyright (C) 2010 Alex Yatskov
# Copyright (C) 2011 Stanislav (proDOOMman) Kosolapov <prodoomman@gmail.com>
# Copyright (c) 2016 Alberto Planas <aplanas@gmail.com>
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
# Copyright (c) 2013-2016 Pawel Jastrzebski <pawelj@iosphe.re>
#
@@ -20,7 +21,7 @@ import os
from io import BytesIO
from urllib.request import Request, urlopen
from urllib.parse import quote
from PIL import Image, ImageOps, ImageStat, ImageChops
from PIL import Image, ImageOps, ImageStat, ImageChops, ImageFilter
from .shared import md5Checksum
from . import __version__
@@ -77,21 +78,21 @@ class ProfileData:
]
Profiles = {
'K1': ("Kindle 1", (600, 670), Palette4, 1.8, (900, 1005)),
'K2': ("Kindle 2", (600, 670), Palette15, 1.8, (900, 1005)),
'K3': ("Kindle", (600, 800), Palette16, 1.8, (900, 1200)),
'K45': ("Kindle", (600, 800), Palette16, 1.8, (900, 1200)),
'KDX': ("Kindle DX/DXG", (824, 1000), Palette16, 1.8, (1236, 1500)),
'KPW': ("Kindle Paperwhite 1/2", (758, 1024), Palette16, 1.8, (1137, 1536)),
'KV': ("Kindle Paperwhite 3/Voyage/Oasis", (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)),
'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)),
'KoAO': ("Kobo Aura ONE", (1404, 1872), Palette16, 1.8, (2106, 2808)),
'OTHER': ("Other", (0, 0), Palette16, 1.8, (0, 0)),
'K1': ("Kindle 1", (600, 670), Palette4, 1.8),
'K2': ("Kindle 2", (600, 670), Palette15, 1.8),
'K3': ("Kindle", (600, 800), Palette16, 1.8),
'K45': ("Kindle", (600, 800), Palette16, 1.8),
'KDX': ("Kindle DX/DXG", (824, 1000), Palette16, 1.8),
'KPW': ("Kindle Paperwhite 1/2", (758, 1024), Palette16, 1.8),
'KV': ("Kindle Paperwhite 3/Voyage/Oasis", (1072, 1448), Palette16, 1.8),
'KoMT': ("Kobo Mini/Touch", (600, 800), Palette16, 1.8),
'KoG': ("Kobo Glo", (768, 1024), Palette16, 1.8),
'KoGHD': ("Kobo Glo HD", (1072, 1448), Palette16, 1.8),
'KoA': ("Kobo Aura", (758, 1024), Palette16, 1.8),
'KoAHD': ("Kobo Aura HD", (1080, 1440), Palette16, 1.8),
'KoAH2O': ("Kobo Aura H2O", (1080, 1430), Palette16, 1.8),
'KoAO': ("Kobo Aura ONE", (1404, 1872), Palette16, 1.8),
'OTHER': ("Other", (0, 0), Palette16, 1.8),
}
@@ -105,8 +106,6 @@ class ComicPageParser:
self.color = self.colorCheck()
self.fill = self.fillCheck()
self.splitCheck()
if self.opt.hqmode:
self.sizeCheck()
def getImageHistogram(self, image):
histogram = image.histogram()
@@ -205,29 +204,16 @@ class ComicPageParser:
else:
return 'white'
def sizeCheck(self):
additionalPayload = []
width, height = self.image.size
dstwidth, dstheight = self.size
for work in self.payload:
if width > dstwidth and height > dstheight:
additionalPayload.append([work[0] + '+', work[1], work[2].copy(), work[3], work[4]])
self.payload = self.payload + additionalPayload
class ComicPage:
def __init__(self, mode, path, image, color, fill, options):
self.opt = options
_, self.size, self.palette, self.gamma, self.panelviewsize = self.opt.profileData
_, self.size, self.palette, self.gamma = self.opt.profileData
self.image = image
self.color = color
self.fill = fill
self.rotated = False
self.orgPath = os.path.join(path[0], path[1])
if '+' in mode:
self.hqMode = True
else:
self.hqMode = False
if 'N' in mode:
self.targetPath = os.path.join(path[0], os.path.splitext(path[1])[0]) + '-KCC'
elif 'R' in mode:
@@ -247,8 +233,6 @@ class ComicPage:
flags.append('Rotated')
if self.fill != 'white':
flags.append('BlackFill')
if self.hqMode:
self.targetPath += '-HQ'
if self.opt.forcepng:
self.targetPath += '.png'
self.image.save(self.targetPath, 'PNG', optimize=1)
@@ -282,128 +266,69 @@ class ComicPage:
self.image = self.image.quantize(palette=palImg)
def resizeImage(self):
if self.hqMode:
size = (self.panelviewsize[0], self.panelviewsize[1])
if self.image.size[0] > size[0] or self.image.size[1] > size[1]:
self.image.thumbnail(size, Image.LANCZOS)
if self.image.size[0] <= self.size[0] and self.image.size[1] <= self.size[1]:
method = Image.BICUBIC
else:
size = (self.size[0], self.size[1])
if self.image.size[0] <= size[0] and self.image.size[1] <= size[1]:
method = Image.BICUBIC
method = Image.LANCZOS
if self.opt.stretch:
self.image = self.image.resize(self.size, method)
elif self.image.size[0] <= self.size[0] and self.image.size[1] <= self.size[1] and not self.opt.upscale:
if self.opt.format == 'CBZ':
borderw = int((self.size[0] - self.image.size[0]) / 2)
borderh = int((self.size[1] - self.image.size[1]) / 2)
self.image = ImageOps.expand(self.image, border=(borderw, borderh), fill=self.fill)
if self.image.size[0] != self.size[0] or self.image.size[1] != self.size[1]:
self.image = ImageOps.fit(self.image, self.size, method=Image.BICUBIC, centering=(0.5, 0.5))
else:
if self.opt.format == 'CBZ':
ratioDev = float(self.size[0]) / float(self.size[1])
if (float(self.image.size[0]) / float(self.image.size[1])) < ratioDev:
diff = int(self.image.size[1] * ratioDev) - self.image.size[0]
self.image = ImageOps.expand(self.image, border=(int(diff / 2), 0), fill=self.fill)
elif (float(self.image.size[0]) / float(self.image.size[1])) > ratioDev:
diff = int(self.image.size[0] / ratioDev) - self.image.size[1]
self.image = ImageOps.expand(self.image, border=(0, int(diff / 2)), fill=self.fill)
self.image = ImageOps.fit(self.image, self.size, method=method, centering=(0.5, 0.5))
else:
method = Image.LANCZOS
if self.opt.stretch:
self.image = self.image.resize(size, method)
elif self.image.size[0] <= size[0] and self.image.size[1] <= size[1] and not self.opt.upscale:
if self.opt.format == 'CBZ':
borderw = int((size[0] - self.image.size[0]) / 2)
borderh = int((size[1] - self.image.size[1]) / 2)
self.image = ImageOps.expand(self.image, border=(borderw, borderh), fill=self.fill)
if self.image.size[0] != size[0] or self.image.size[1] != size[1]:
self.image = ImageOps.fit(self.image, size, method=Image.BICUBIC, centering=(0.5, 0.5))
else:
if self.opt.format == 'CBZ':
ratioDev = float(size[0]) / float(size[1])
if (float(self.image.size[0]) / float(self.image.size[1])) < ratioDev:
diff = int(self.image.size[1] * ratioDev) - self.image.size[0]
self.image = ImageOps.expand(self.image, border=(int(diff / 2), 0), fill=self.fill)
elif (float(self.image.size[0]) / float(self.image.size[1])) > ratioDev:
diff = int(self.image.size[0] / ratioDev) - self.image.size[1]
self.image = ImageOps.expand(self.image, border=(0, int(diff / 2)), fill=self.fill)
self.image = ImageOps.fit(self.image, size, method=method, centering=(0.5, 0.5))
else:
hpercent = size[1] / float(self.image.size[1])
wsize = int((float(self.image.size[0]) * float(hpercent)))
self.image = self.image.resize((wsize, size[1]), method)
if self.image.size[0] > size[0] or self.image.size[1] > size[1]:
self.image.thumbnail(size, Image.LANCZOS)
hpercent = self.size[1] / float(self.image.size[1])
wsize = int((float(self.image.size[0]) * float(hpercent)))
self.image = self.image.resize((wsize, self.size[1]), method)
if self.image.size[0] > self.size[0] or self.image.size[1] > self.size[1]:
self.image.thumbnail(self.size, Image.LANCZOS)
def cutPageNumber(self, fixedThreshold):
if ImageChops.invert(self.image).getbbox() is not None:
widthImg, heightImg = self.image.size
delta = 2
diff = delta
if ImageStat.Stat(self.image).var[0] < 2 * fixedThreshold:
return self.image
while ImageStat.Stat(self.image.crop((0, heightImg - diff, widthImg, heightImg))).var[0] < fixedThreshold\
and diff < heightImg:
diff += delta
diff -= delta
pageNumberCut1 = diff
if diff < delta:
diff = delta
oldStat = ImageStat.Stat(self.image.crop((0, heightImg - diff, widthImg, heightImg))).var[0]
diff += delta
while ImageStat.Stat(self.image.crop((0, heightImg - diff, widthImg, heightImg))).var[0] - oldStat > 0\
and diff < heightImg // 4:
oldStat = ImageStat.Stat(self.image.crop((0, heightImg - diff, widthImg, heightImg))).var[0]
diff += delta
diff -= delta
pageNumberCut2 = diff
diff += delta
oldStat = ImageStat.Stat(self.image.crop((0, heightImg - diff, widthImg,
heightImg - pageNumberCut2))).var[0]
while ImageStat.Stat(self.image.crop((0, heightImg - diff, widthImg, heightImg - pageNumberCut2))).var[0]\
< fixedThreshold + oldStat and diff < heightImg // 4:
diff += delta
diff -= delta
pageNumberCut3 = diff
delta = 5
diff = delta
while ImageStat.Stat(self.image.crop((0, heightImg - pageNumberCut2, diff, heightImg))).var[0]\
< fixedThreshold and diff < widthImg:
diff += delta
diff -= delta
pageNumberX1 = diff
diff = delta
while ImageStat.Stat(self.image.crop((widthImg - diff, heightImg - pageNumberCut2,
widthImg, heightImg))).var[0] < fixedThreshold and diff < widthImg:
diff += delta
diff -= delta
pageNumberX2 = widthImg - diff
if pageNumberCut3 - pageNumberCut1 > 2 * delta\
and float(pageNumberX2 - pageNumberX1) / float(pageNumberCut2 - pageNumberCut1) <= 9.0\
and ImageStat.Stat(self.image.crop((0, heightImg - pageNumberCut3, widthImg, heightImg))).var[0]\
/ ImageStat.Stat(self.image).var[0] < 0.1\
and pageNumberCut3 < heightImg / 4 - delta:
diff = pageNumberCut3
else:
diff = pageNumberCut1
self.image = self.image.crop((0, 0, widthImg, heightImg - diff))
def getBoundingBox(self, tmpImg):
min_margin = [int(0.005 * i + 0.5) for i in tmpImg.size]
max_margin = [int(0.1 * i + 0.5) for i in tmpImg.size]
bbox = tmpImg.getbbox()
bbox = (
max(0, min(max_margin[0], bbox[0] - min_margin[0])),
max(0, min(max_margin[1], bbox[1] - min_margin[1])),
min(tmpImg.size[0],
max(tmpImg.size[0] - max_margin[0], bbox[2] + min_margin[0])),
min(tmpImg.size[1],
max(tmpImg.size[1] - max_margin[1], bbox[3] + min_margin[1])),
)
return bbox
def cropWhiteSpace(self, fixedThreshold):
if ImageChops.invert(self.image).getbbox() is not None:
widthImg, heightImg = self.image.size
delta = 10
diff = delta
# top
while ImageStat.Stat(self.image.crop((0, 0, widthImg, diff))).var[0] < fixedThreshold and diff < heightImg:
diff += delta
diff -= delta
self.image = self.image.crop((0, diff, widthImg, heightImg))
widthImg, heightImg = self.image.size
diff = delta
# left
while ImageStat.Stat(self.image.crop((0, 0, diff, heightImg))).var[0] < fixedThreshold and diff < widthImg:
diff += delta
diff -= delta
self.image = self.image.crop((diff, 0, widthImg, heightImg))
widthImg, heightImg = self.image.size
diff = delta
# down
while ImageStat.Stat(self.image.crop((0, heightImg - diff, widthImg, heightImg))).var[0] < fixedThreshold\
and diff < heightImg:
diff += delta
diff -= delta
self.image = self.image.crop((0, 0, widthImg, heightImg - diff))
widthImg, heightImg = self.image.size
diff = delta
# right
while ImageStat.Stat(self.image.crop((widthImg - diff, 0, widthImg, heightImg))).var[0] < fixedThreshold\
and diff < widthImg:
diff += delta
diff -= delta
self.image = self.image.crop((0, 0, widthImg - diff, heightImg))
def cropPageNumber(self, power):
if self.fill != 'white':
tmpImg = self.image.convert(mode='L')
else:
tmpImg = ImageOps.invert(self.image.convert(mode='L'))
tmpImg = tmpImg.point(lambda x: x and 255)
tmpImg = tmpImg.filter(ImageFilter.MinFilter(size=3))
tmpImg = tmpImg.filter(ImageFilter.GaussianBlur(radius=5))
tmpImg = tmpImg.point(lambda x: (x >= 48 * power) and x)
self.image = self.image.crop(tmpImg.getbbox()) if tmpImg.getbbox() else self.image
def cropMargin(self, power):
if self.fill != 'white':
tmpImg = self.image.convert(mode='L')
else:
tmpImg = ImageOps.invert(self.image.convert(mode='L'))
tmpImg = tmpImg.filter(ImageFilter.GaussianBlur(radius=3))
tmpImg = tmpImg.point(lambda x: (x >= 16 * power) and x)
self.image = self.image.crop(self.getBoundingBox(tmpImg)) if tmpImg.getbbox() else self.image
class Cover:

View File

@@ -144,8 +144,7 @@ def removeFromZIP(zipfname, *filenames):
def sanitizeTrace(traceback):
return ''.join(format_tb(traceback))\
.replace('C:\\Users\\pawel\\Documents\\Projekty\\KCC\\', '') \
.replace('C:\\Users\\Paweł\\Documents\\Projekty\\KCC\\', '') \
.replace('C:\\Users\\Pawel\\Documents\\Projekty\\KCC\\', '') \
.replace('C:\\Python35\\', '')\
.replace('c:\\python35\\', '')

View File

@@ -9,7 +9,7 @@
<key>CFBundleExecutable</key>
<string>MacOS/Kindle Comic Converter</string>
<key>CFBundleGetInfoString</key>
<string>KindleComicConverter 5.1.3, written 2012-2016 by Ciro Mattia Gonano and Pawel Jastrzebski</string>
<string>KindleComicConverter 5.2, written 2012-2016 by Ciro Mattia Gonano and Pawel Jastrzebski</string>
<key>CFBundleIconFile</key>
<string>comic2ebook.icns</string>
<key>CFBundleIdentifier</key>
@@ -21,11 +21,11 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>5.1.3</string>
<string>5.2.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>5.1.3</string>
<string>5.2.0</string>
<key>LSEnvironment</key>
<dict>
<key>PATH</key>