mirror of
https://github.com/ciromattia/kcc
synced 2026-04-18 23:19:00 +00:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9867f63d00 | ||
|
|
866f8898be | ||
|
|
9f2ac7a176 | ||
|
|
634213f380 | ||
|
|
ce82b5ec66 |
@@ -333,6 +333,12 @@ The app relies and includes the following scripts/binaries:
|
|||||||
* Fixed OSX file association support
|
* Fixed OSX file association support
|
||||||
* Many extensive internal changes and tweaks
|
* Many extensive internal changes and tweaks
|
||||||
|
|
||||||
|
####4.0.1:
|
||||||
|
* Fixed file lock problems that plagued some Windows users
|
||||||
|
* Fixed content server failing to start on Windows
|
||||||
|
* Improved performance of WebToon splitter
|
||||||
|
* Tweaked margin color detection
|
||||||
|
|
||||||
## KNOWN ISSUES
|
## KNOWN ISSUES
|
||||||
Please check [wiki page](https://github.com/ciromattia/kcc/wiki/Known-issues).
|
Please check [wiki page](https://github.com/ciromattia/kcc/wiki/Known-issues).
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
# PERFORMANCE OF THIS SOFTWARE.
|
# PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
__version__ = '4.0'
|
__version__ = '4.0.1'
|
||||||
__license__ = 'ISC'
|
__license__ = 'ISC'
|
||||||
__copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@vulturis.eu>'
|
__copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@vulturis.eu>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ if sys.version_info[0] != 3:
|
|||||||
print('ERROR: This is Python 3 script!')
|
print('ERROR: This is Python 3 script!')
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
__version__ = '4.0'
|
__version__ = '4.0.1'
|
||||||
__license__ = 'ISC'
|
__license__ = 'ISC'
|
||||||
__copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@vulturis.eu>'
|
__copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@vulturis.eu>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|||||||
2
kcc.iss
2
kcc.iss
@@ -1,5 +1,5 @@
|
|||||||
#define MyAppName "Kindle Comic Converter"
|
#define MyAppName "Kindle Comic Converter"
|
||||||
#define MyAppVersion "4.0"
|
#define MyAppVersion "4.0.1"
|
||||||
#define MyAppPublisher "Ciro Mattia Gonano, Paweł Jastrzębski"
|
#define MyAppPublisher "Ciro Mattia Gonano, Paweł Jastrzębski"
|
||||||
#define MyAppURL "http://kcc.vulturis.eu/"
|
#define MyAppURL "http://kcc.vulturis.eu/"
|
||||||
#define MyAppExeName "KCC.exe"
|
#define MyAppExeName "KCC.exe"
|
||||||
|
|||||||
2
kcc.py
2
kcc.py
@@ -18,7 +18,7 @@
|
|||||||
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
# PERFORMANCE OF THIS SOFTWARE.
|
# PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
__version__ = '4.0'
|
__version__ = '4.0.1'
|
||||||
__license__ = 'ISC'
|
__license__ = 'ISC'
|
||||||
__copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@vulturis.eu>'
|
__copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@vulturis.eu>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
# PERFORMANCE OF THIS SOFTWARE.
|
# PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
__version__ = '4.0'
|
__version__ = '4.0.1'
|
||||||
__license__ = 'ISC'
|
__license__ = 'ISC'
|
||||||
__copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@vulturis.eu>'
|
__copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@vulturis.eu>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
@@ -450,9 +450,9 @@ class WorkerThread(QtCore.QThread):
|
|||||||
except Exception as err:
|
except Exception as err:
|
||||||
GUI.progress.content = ''
|
GUI.progress.content = ''
|
||||||
self.errors = True
|
self.errors = True
|
||||||
type_, value_, traceback_ = sys.exc_info()
|
_, _, traceback = sys.exc_info()
|
||||||
MW.showDialog.emit("Error during conversion %s:\n\n%s\n\nTraceback:\n%s"
|
MW.showDialog.emit("Error during conversion %s:\n\n%s\n\nTraceback:\n%s"
|
||||||
% (jobargv[-1], str(err), format_tb(traceback_)), 'error')
|
% (jobargv[-1], str(err), "".join(format_tb(traceback))), 'error')
|
||||||
MW.addMessage.emit('Failed to create EPUB!', 'error', False)
|
MW.addMessage.emit('Failed to create EPUB!', 'error', False)
|
||||||
MW.addTrayMessage.emit('Failed to create EPUB!', 'Critical')
|
MW.addTrayMessage.emit('Failed to create EPUB!', 'Critical')
|
||||||
if not self.conversionAlive:
|
if not self.conversionAlive:
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
__version__ = '4.0'
|
__version__ = '4.0.1'
|
||||||
__license__ = 'ISC'
|
__license__ = 'ISC'
|
||||||
__copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@vulturis.eu>'
|
__copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@vulturis.eu>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
# PERFORMANCE OF THIS SOFTWARE.
|
# PERFORMANCE OF THIS SOFTWARE.
|
||||||
#
|
#
|
||||||
|
|
||||||
__version__ = '4.0'
|
__version__ = '4.0.1'
|
||||||
__license__ = 'ISC'
|
__license__ = 'ISC'
|
||||||
__copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@vulturis.eu>'
|
__copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@vulturis.eu>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
@@ -320,11 +320,12 @@ def applyImgOptimization(img, opt, hqImage=None):
|
|||||||
|
|
||||||
|
|
||||||
def dirImgProcess(path):
|
def dirImgProcess(path):
|
||||||
global workerPool, workerOutput, theGreatIndex
|
global workerPool, workerOutput, theGreatIndex, theGreatWipe
|
||||||
workerPool = Pool()
|
workerPool = Pool()
|
||||||
workerOutput = []
|
workerOutput = []
|
||||||
work = []
|
work = []
|
||||||
theGreatIndex = {}
|
theGreatIndex = {}
|
||||||
|
theGreatWipe = []
|
||||||
pagenumber = 0
|
pagenumber = 0
|
||||||
for (dirpath, dirnames, filenames) in os.walk(path):
|
for (dirpath, dirnames, filenames) in os.walk(path):
|
||||||
for afile in filenames:
|
for afile in filenames:
|
||||||
@@ -344,6 +345,9 @@ def dirImgProcess(path):
|
|||||||
if len(workerOutput) > 0:
|
if len(workerOutput) > 0:
|
||||||
rmtree(os.path.join(path, '..', '..'), True)
|
rmtree(os.path.join(path, '..', '..'), True)
|
||||||
raise RuntimeError("One of workers crashed. Cause: " + workerOutput[0])
|
raise RuntimeError("One of workers crashed. Cause: " + workerOutput[0])
|
||||||
|
for file in theGreatWipe:
|
||||||
|
if os.path.isfile(file):
|
||||||
|
os.remove(file)
|
||||||
else:
|
else:
|
||||||
rmtree(os.path.join(path, '..', '..'), True)
|
rmtree(os.path.join(path, '..', '..'), True)
|
||||||
raise UserWarning("Source directory is empty.")
|
raise UserWarning("Source directory is empty.")
|
||||||
@@ -356,9 +360,10 @@ def fileImgProcess_tick(output):
|
|||||||
else:
|
else:
|
||||||
for page in output:
|
for page in output:
|
||||||
if page is not None:
|
if page is not None:
|
||||||
if sys.platform.startswith('win'):
|
if isinstance(page, str):
|
||||||
page[0] = page[0].replace('/', '\\')
|
theGreatWipe.append(page)
|
||||||
theGreatIndex[page[0]] = page[1]
|
else:
|
||||||
|
theGreatIndex[page[0]] = page[1]
|
||||||
if GUI:
|
if GUI:
|
||||||
GUI.progressBarTick.emit('tick')
|
GUI.progressBarTick.emit('tick')
|
||||||
if not GUI.conversionAlive:
|
if not GUI.conversionAlive:
|
||||||
@@ -388,8 +393,8 @@ def fileImgProcess(work):
|
|||||||
applyImgOptimization(img1, opt)
|
applyImgOptimization(img1, opt)
|
||||||
output.append(img1.saveToDir(dirpath, opt.forcepng, opt.forcecolor))
|
output.append(img1.saveToDir(dirpath, opt.forcepng, opt.forcecolor))
|
||||||
if wipe:
|
if wipe:
|
||||||
os.remove(img0.origFileName)
|
output.append(img0.origFileName)
|
||||||
os.remove(img1.origFileName)
|
output.append(img1.origFileName)
|
||||||
if opt.quality == 2:
|
if opt.quality == 2:
|
||||||
img0b = image.ComicPage(splitter[0], opt.profileData, img0.fill)
|
img0b = image.ComicPage(splitter[0], opt.profileData, img0.fill)
|
||||||
applyImgOptimization(img0b, opt, img0)
|
applyImgOptimization(img0b, opt, img0)
|
||||||
@@ -397,14 +402,14 @@ def fileImgProcess(work):
|
|||||||
img1b = image.ComicPage(splitter[1], opt.profileData, img1.fill)
|
img1b = image.ComicPage(splitter[1], opt.profileData, img1.fill)
|
||||||
applyImgOptimization(img1b, opt, img1)
|
applyImgOptimization(img1b, opt, img1)
|
||||||
output.append(img1b.saveToDir(dirpath, opt.forcepng, opt.forcecolor))
|
output.append(img1b.saveToDir(dirpath, opt.forcepng, opt.forcecolor))
|
||||||
os.remove(img0.origFileName)
|
output.append(img0.origFileName)
|
||||||
os.remove(img1.origFileName)
|
output.append(img1.origFileName)
|
||||||
os.remove(img.origFileName)
|
output.append(img.origFileName)
|
||||||
else:
|
else:
|
||||||
applyImgOptimization(img, opt)
|
applyImgOptimization(img, opt)
|
||||||
output.append(img.saveToDir(dirpath, opt.forcepng, opt.forcecolor))
|
output.append(img.saveToDir(dirpath, opt.forcepng, opt.forcecolor))
|
||||||
if wipe:
|
if wipe:
|
||||||
os.remove(img.origFileName)
|
output.append(img.origFileName)
|
||||||
if opt.quality == 2:
|
if opt.quality == 2:
|
||||||
img2 = image.ComicPage(os.path.join(dirpath, afile), opt.profileData, img.fill)
|
img2 = image.ComicPage(os.path.join(dirpath, afile), opt.profileData, img.fill)
|
||||||
if img.rotated:
|
if img.rotated:
|
||||||
@@ -412,7 +417,7 @@ def fileImgProcess(work):
|
|||||||
img2.rotated = True
|
img2.rotated = True
|
||||||
applyImgOptimization(img2, opt, img)
|
applyImgOptimization(img2, opt, img)
|
||||||
output.append(img2.saveToDir(dirpath, opt.forcepng, opt.forcecolor))
|
output.append(img2.saveToDir(dirpath, opt.forcepng, opt.forcecolor))
|
||||||
os.remove(img.origFileName)
|
output.append(img.origFileName)
|
||||||
return output
|
return output
|
||||||
except Exception:
|
except Exception:
|
||||||
return str(sys.exc_info()[1])
|
return str(sys.exc_info()[1])
|
||||||
@@ -946,8 +951,8 @@ def main(argv=None, qtGUI=None):
|
|||||||
return
|
return
|
||||||
path = getWorkFolder(args[0])
|
path = getWorkFolder(args[0])
|
||||||
print("\nChecking images...")
|
print("\nChecking images...")
|
||||||
detectCorruption(path + "/OEBPS/Images/", args[0])
|
detectCorruption(os.path.join(path, "OEBPS", "Images"), args[0])
|
||||||
checkComicInfo(path + "/OEBPS/Images/", args[0])
|
checkComicInfo(os.path.join(path, "OEBPS", "Images"), args[0])
|
||||||
if options.webtoon:
|
if options.webtoon:
|
||||||
if options.customheight > 0:
|
if options.customheight > 0:
|
||||||
comic2panel.main(['-y ' + str(options.customheight), '-i', '-m', path], qtGUI)
|
comic2panel.main(['-y ' + str(options.customheight), '-i', '-m', path], qtGUI)
|
||||||
@@ -957,7 +962,7 @@ def main(argv=None, qtGUI=None):
|
|||||||
print("\nProcessing images...")
|
print("\nProcessing images...")
|
||||||
if GUI:
|
if GUI:
|
||||||
GUI.progressBarTick.emit('Processing images')
|
GUI.progressBarTick.emit('Processing images')
|
||||||
dirImgProcess(path + "/OEBPS/Images/")
|
dirImgProcess(os.path.join(path, "OEBPS", "Images"))
|
||||||
if GUI:
|
if GUI:
|
||||||
GUI.progressBarTick.emit('1')
|
GUI.progressBarTick.emit('1')
|
||||||
chapterNames = sanitizeTree(os.path.join(path, 'OEBPS', 'Images'))
|
chapterNames = sanitizeTree(os.path.join(path, 'OEBPS', 'Images'))
|
||||||
@@ -986,7 +991,7 @@ def main(argv=None, qtGUI=None):
|
|||||||
filepath.append(getOutputFilename(args[0], options.output, '.cbz', ' ' + str(tomeNumber)))
|
filepath.append(getOutputFilename(args[0], options.output, '.cbz', ' ' + str(tomeNumber)))
|
||||||
else:
|
else:
|
||||||
filepath.append(getOutputFilename(args[0], options.output, '.cbz', ''))
|
filepath.append(getOutputFilename(args[0], options.output, '.cbz', ''))
|
||||||
makeZIP(tome + '_comic', tome + '/OEBPS/Images')
|
makeZIP(tome + '_comic', os.path.join(tome, "OEBPS", "Images"))
|
||||||
else:
|
else:
|
||||||
print("\nCreating EPUB structure...")
|
print("\nCreating EPUB structure...")
|
||||||
genEpubStruct(tome, chapterNames)
|
genEpubStruct(tome, chapterNames)
|
||||||
@@ -1010,9 +1015,10 @@ def getOutputFilename(srcpath, wantedname, ext, tomeNumber):
|
|||||||
if wantedname.endswith(ext):
|
if wantedname.endswith(ext):
|
||||||
filename = os.path.abspath(wantedname)
|
filename = os.path.abspath(wantedname)
|
||||||
elif os.path.isdir(srcpath):
|
elif os.path.isdir(srcpath):
|
||||||
filename = os.path.abspath(options.output) + "/" + os.path.basename(srcpath) + ext
|
filename = os.path.join(os.path.abspath(options.output), os.path.basename(srcpath) + ext)
|
||||||
else:
|
else:
|
||||||
filename = os.path.abspath(options.output) + "/" + os.path.basename(os.path.splitext(srcpath)[0]) + ext
|
filename = os.path.join(os.path.abspath(options.output),
|
||||||
|
os.path.basename(os.path.splitext(srcpath)[0]) + ext)
|
||||||
elif os.path.isdir(srcpath):
|
elif os.path.isdir(srcpath):
|
||||||
filename = srcpath + tomeNumber + ext
|
filename = srcpath + tomeNumber + ext
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
# PERFORMANCE OF THIS SOFTWARE.
|
# PERFORMANCE OF THIS SOFTWARE.
|
||||||
#
|
#
|
||||||
|
|
||||||
__version__ = '4.0'
|
__version__ = '4.0.1'
|
||||||
__license__ = 'ISC'
|
__license__ = 'ISC'
|
||||||
__copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@vulturis.eu>'
|
__copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@vulturis.eu>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
@@ -52,27 +52,31 @@ def mergeDirectory(work):
|
|||||||
images = []
|
images = []
|
||||||
imagesClear = []
|
imagesClear = []
|
||||||
sizes = []
|
sizes = []
|
||||||
|
h = 0
|
||||||
for root, dirs, files in walkLevel(directory, 0):
|
for root, dirs, files in walkLevel(directory, 0):
|
||||||
for name in files:
|
for name in files:
|
||||||
if getImageFileName(name) is not None:
|
if getImageFileName(name) is not None:
|
||||||
images.append([Image.open(os.path.join(root, name)), os.path.join(root, name)])
|
i = Image.open(os.path.join(root, name))
|
||||||
|
images.append([os.path.join(root, name), i.size[0], i.size[1]])
|
||||||
|
sizes.append(i.size[0])
|
||||||
if len(images) > 0:
|
if len(images) > 0:
|
||||||
for i in images:
|
|
||||||
sizes.append(i[0].size[0])
|
|
||||||
mw = max(set(sizes), key=sizes.count)
|
mw = max(set(sizes), key=sizes.count)
|
||||||
for i in images:
|
for i in images:
|
||||||
if i[0].size[0] == mw:
|
if i[1] == mw:
|
||||||
i[0] = i[0].convert('RGB')
|
h += i[2]
|
||||||
imagesClear.append(i)
|
imagesClear.append(i[0])
|
||||||
h = sum(i[0].size[1] for i in imagesClear)
|
# Silently drop directories that contain too many images
|
||||||
|
if h > 262144:
|
||||||
|
return None
|
||||||
result = Image.new('RGB', (mw, h))
|
result = Image.new('RGB', (mw, h))
|
||||||
y = 0
|
y = 0
|
||||||
for i in imagesClear:
|
for i in imagesClear:
|
||||||
result.paste(i[0], (0, y))
|
img = Image.open(i)
|
||||||
y += i[0].size[1]
|
img = img.convert('RGB')
|
||||||
for i in imagesClear:
|
result.paste(img, (0, y))
|
||||||
os.remove(i[1])
|
y += img.size[1]
|
||||||
savePath = os.path.split(imagesClear[0][1])
|
os.remove(i)
|
||||||
|
savePath = os.path.split(imagesClear[0])
|
||||||
result.save(os.path.join(savePath[0], os.path.splitext(savePath[1])[0] + '.png'), 'PNG')
|
result.save(os.path.join(savePath[0], os.path.splitext(savePath[1])[0] + '.png'), 'PNG')
|
||||||
except Exception:
|
except Exception:
|
||||||
return str(sys.exc_info()[1])
|
return str(sys.exc_info()[1])
|
||||||
|
|||||||
@@ -415,9 +415,6 @@ class ComicPage:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def getImageFill(self, webtoon):
|
def getImageFill(self, webtoon):
|
||||||
if not webtoon and self.color:
|
|
||||||
self.fill = 'black'
|
|
||||||
return
|
|
||||||
fill = 0
|
fill = 0
|
||||||
if not webtoon and not self.rotated:
|
if not webtoon and not self.rotated:
|
||||||
# Search for horizontal solid lines
|
# Search for horizontal solid lines
|
||||||
|
|||||||
3
setup.py
3
setup.py
@@ -14,7 +14,7 @@ if version_info[0] != 3:
|
|||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
NAME = "KindleComicConverter"
|
NAME = "KindleComicConverter"
|
||||||
VERSION = "4.0"
|
VERSION = "4.0.1"
|
||||||
MAIN = "kcc.py"
|
MAIN = "kcc.py"
|
||||||
|
|
||||||
if platform == "darwin":
|
if platform == "darwin":
|
||||||
@@ -73,6 +73,7 @@ elif platform == "win32":
|
|||||||
"copy_dependent_files": True,
|
"copy_dependent_files": True,
|
||||||
"create_shared_zip": False,
|
"create_shared_zip": False,
|
||||||
"append_script_to_exe": True,
|
"append_script_to_exe": True,
|
||||||
|
"replace_paths": '*=',
|
||||||
"excludes": ['tkinter']}},
|
"excludes": ['tkinter']}},
|
||||||
executables=[Executable(MAIN,
|
executables=[Executable(MAIN,
|
||||||
base=base,
|
base=base,
|
||||||
|
|||||||
Reference in New Issue
Block a user