mirror of
https://github.com/ciromattia/kcc
synced 2026-04-15 21:48:44 +00:00
Compare commits
32 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b068d82ccf | ||
|
|
43ca5ac5b9 | ||
|
|
18c3ab2340 | ||
|
|
87eaba184e | ||
|
|
97b5d8a2ee | ||
|
|
7c3a762107 | ||
|
|
0b90af77da | ||
|
|
28dcab8ee8 | ||
|
|
6c468a5291 | ||
|
|
d090d8c2e8 | ||
|
|
aba315866e | ||
|
|
e981aa4520 | ||
|
|
e603622021 | ||
|
|
3e007965b2 | ||
|
|
c0610360a3 | ||
|
|
ff8f6e073f | ||
|
|
aadb5407d2 | ||
|
|
2e4d5eb958 | ||
|
|
33fb13a66e | ||
|
|
4dc69aa1c4 | ||
|
|
b3681a3ceb | ||
|
|
348dcc4275 | ||
|
|
39e69119ac | ||
|
|
8c57926978 | ||
|
|
8b1965054f | ||
|
|
18993069e3 | ||
|
|
7e6c8cc768 | ||
|
|
3ae44d2fcb | ||
|
|
a6eb3936e4 | ||
|
|
bb24d3ca30 | ||
|
|
7e191c0be5 | ||
|
|
d9ea165bbb |
149
README.md
149
README.md
@@ -1,4 +1,4 @@
|
||||
# KCC
|
||||
# KCC
|
||||
|
||||
`KCC` (a.k.a. `KindleComicConverter`) is a Python app to convert comic files or folders to ePub or Panel View MOBI.
|
||||
It was initally developed for Kindle but since v2.2 it outputs valid ePub 2.0 so _**despite its name, KCC is
|
||||
@@ -14,11 +14,16 @@ _kc2_ in no way is a replacement for **KCC** so you can be quite confident we'll
|
||||
|
||||
## BINARY RELEASES
|
||||
You can find the latest released binary at the following links:
|
||||
- OS X: [https://dl.dropbox.com/u/16806101/KindleComicConverter_osx_2.9.zip](https://dl.dropbox.com/u/16806101/KindleComicConverter_osx_2.9.zip)
|
||||
- Win64: [https://dl.dropbox.com/u/16806101/KindleComicConverter_win-amd64_2.9.zip](https://dl.dropbox.com/u/16806101/KindleComicConverter_win-amd64_2.9.zip)
|
||||
- Win32: [http://pawelj.vulturis.eu/Shared/KindleComicConverter_win-x86_2.9.zip](http://pawelj.vulturis.eu/Shared/KindleComicConverter_win-x86_2.9.zip) *(thanks to [AcidWeb](https://github.com/AcidWeb))*
|
||||
- OS X: [https://dl.dropbox.com/u/16806101/KindleComicConverter_osx_2.10.zip](https://dl.dropbox.com/u/16806101/KindleComicConverter_osx_2.10.zip)
|
||||
- Win64: [https://dl.dropbox.com/u/16806101/KindleComicConverter_win-amd64_2.10.zip](https://dl.dropbox.com/u/16806101/KindleComicConverter_win-amd64_2.10.zip)
|
||||
- Win32: [http://pawelj.vulturis.eu/Shared/KindleComicConverter_win-x86_2.10.zip](http://pawelj.vulturis.eu/Shared/KindleComicConverter_win-x86_2.10.zip)
|
||||
- Linux: Just download sourcecode and launch `python kcc.py` *(Provided you have Python and Pillow installed)*
|
||||
|
||||
## AWKCC .NET GUI
|
||||

|
||||
|
||||
[All-in-one package for Windows users](http://www.mobileread.com/forums/showpost.php?p=2444957&postcount=3)
|
||||
|
||||
## INPUT FORMATS
|
||||
`kcc` can understand and convert, at the moment, the following file types:
|
||||
- PNG, JPG, GIF, TIFF, BMP
|
||||
@@ -53,7 +58,7 @@ Options:
|
||||
--version show program's version number and exit
|
||||
-h, --help show this help message and exit
|
||||
-p PROFILE, --profile=PROFILE
|
||||
Device profile (Choose one among K1, K2, K3, K4NT, K4T, KDX, KDXG or KHD) [Default=KHD]
|
||||
Device profile (Choose one among K1, K2, K3, K4NT, K4T, KDX, KDXG, KHD, KF, KFHD, KFHD8) [Default=KHD]
|
||||
-t TITLE, --title=TITLE
|
||||
Comic title [Default=filename]
|
||||
-m, --manga-style Manga style (Right-to-left reading and splitting) [Default=False]
|
||||
@@ -88,55 +93,95 @@ The app relies and includes the following scripts/binaries:
|
||||
- `magic.py` from [python-magic](https://github.com/ahupp/python-magic) library
|
||||
|
||||
## CHANGELOG
|
||||
- 1.00: Initial version
|
||||
- 1.10: Added support for CBZ/CBR files in comic2ebook.py
|
||||
- 1.11: Added support for CBZ/CBR files in KindleComicConverter
|
||||
- 1.20: Comic optimizations! Split pages not target-oriented (landscape with portrait target or portrait
|
||||
with landscape target), add palette and other image optimizations from Mangle.
|
||||
WARNING: PIL is required for all image mangling!
|
||||
- 1.30: Fixed an issue in OPF generation for device resolution
|
||||
Reworked options system (call with -h option to get the inline help)
|
||||
- 1.40: Added some options for controlling image optimization
|
||||
Further optimization (ImageOps, page numbering cut, autocontrast)
|
||||
- 1.41: Fixed a serious bug on resizing when img ratio was bigger than device one
|
||||
- 1.50: Added subfolder support for multiple chapters.
|
||||
- 2.0: GUI! AppleScript is gone and Tk is used to provide cross-platform GUI support.
|
||||
- 2.1: Added basic error reporting
|
||||
- 2.2: Added (valid!) ePub 2.0 output
|
||||
Rename .zip files to .cbz to avoid overwriting
|
||||
- 2.3: Fixed win32 ePub generation, folder handling, filenames with spaces and subfolders
|
||||
- 2.4: Use temporary directory as workdir (fixes converting from external volumes and zipfiles renaming)
|
||||
Fixed "add folders" from GUI.
|
||||
- 2.5: Added --black-borders option to set added borders black when page's ratio is not the device's one (#11).
|
||||
Fixes epub containing zipped itself (#10)
|
||||
- 2.6: Added --rotate option to rotate landscape images instead of splitting them (#16, #24)
|
||||
Added --output option to customize ePub output dir/file (#22)
|
||||
Add rendition:layout and rendition:orientation ePub meta tags (supported by new kindlegen 2.8)
|
||||
Fixed natural sorting for files (#18)
|
||||
- 2.7: Lots of GUI improvements (#27, #13)
|
||||
Added gamma support within --gamma option (defaults to profile-specified gamma) (#26, #27)
|
||||
Added --nodithering option to prevent dithering optimizations (#27)
|
||||
Epub margins support (#30)
|
||||
Fixed no file added if file has no spaces on Windows (#25)
|
||||
Gracefully exit if unrar missing (#15)
|
||||
Do not call kindlegen if source epub is bigger than 320MB (#17)
|
||||
Get filetype from magic number (#14)
|
||||
PDF conversion works again
|
||||
- 2.8: updated rarfile library
|
||||
Panel View support + HQ support (#36) - new option: --nopanelviewhq
|
||||
Split profiles for K4NT and K4T
|
||||
Rewrite of Landscape Mode support (huge readability improvement for KPW)
|
||||
Upscale use now BILINEAR method
|
||||
Added generic CSS file
|
||||
Optimized archive extraction for zip/rar files (#40)
|
||||
- 2.9: Added support for generating a plain CBZ (skipping all the EPUB/Mobi generation) (#45)
|
||||
Prevent output file overwriting the source one: if a duplicate name is detected, append _kcc to the name
|
||||
Rarfile library updated to 2.6
|
||||
Added GIF, TIFF and BMP to supported formats (#42)
|
||||
Filenames slugifications (#28, #31, #9, #8)
|
||||
####1.00
|
||||
* Initial version
|
||||
|
||||
####1.10
|
||||
* Added support for CBZ/CBR files in comic2ebook.py
|
||||
|
||||
####1.11
|
||||
* Added support for CBZ/CBR files in KindleComicConverter
|
||||
|
||||
####1.20
|
||||
* Comic optimizations! Split pages not target-oriented (landscape with portrait target or portrait with landscape target), add palette and other image optimizations from Mangle. WARNING: PIL is required for all image mangling!
|
||||
|
||||
####1.30
|
||||
* Fixed an issue in OPF generation for device resolution
|
||||
* Reworked options system (call with -h option to get the inline help)
|
||||
|
||||
####1.40
|
||||
* Added some options for controlling image optimization
|
||||
* Further optimization (ImageOps, page numbering cut, autocontrast)
|
||||
|
||||
####1.41
|
||||
* Fixed a serious bug on resizing when img ratio was bigger than device one
|
||||
|
||||
####1.50
|
||||
* Added subfolder support for multiple chapters.
|
||||
|
||||
####2.0
|
||||
* GUI! AppleScript is gone and Tk is used to provide cross-platform GUI support.
|
||||
|
||||
####2.1
|
||||
* Added basic error reporting
|
||||
|
||||
#### 2.2:
|
||||
* Added (valid!) ePub 2.0 output
|
||||
* Rename .zip files to .cbz to avoid overwriting
|
||||
|
||||
####2.3
|
||||
* Fixed win32 ePub generation, folder handling, filenames with spaces and subfolders
|
||||
|
||||
####2.4
|
||||
* Use temporary directory as workdir (fixes converting from external volumes and zipfiles renaming)
|
||||
* Fixed "add folders" from GUI.
|
||||
|
||||
####2.5
|
||||
* Added --black-borders option to set added borders black when page's ratio is not the device's one (#11).
|
||||
* Fixes epub containing zipped itself (#10)
|
||||
|
||||
####2.6
|
||||
* Added --rotate option to rotate landscape images instead of splitting them (#16, #24)
|
||||
* Added --output option to customize ePub output dir/file (#22)
|
||||
* Add rendition:layout and rendition:orientation ePub meta tags (supported by new kindlegen 2.8)
|
||||
* Fixed natural sorting for files (#18)
|
||||
|
||||
####2.7
|
||||
* Lots of GUI improvements (#27, #13)
|
||||
* Added gamma support within --gamma option (defaults to profile-specified gamma) (#26, #27)
|
||||
* Added --nodithering option to prevent dithering optimizations (#27)
|
||||
* Epub margins support (#30)
|
||||
* Fixed no file added if file has no spaces on Windows (#25)
|
||||
* Gracefully exit if unrar missing (#15)
|
||||
* Do not call kindlegen if source epub is bigger than 320MB (#17)
|
||||
* Get filetype from magic number (#14)
|
||||
* PDF conversion works again
|
||||
|
||||
####2.8
|
||||
* Updated rarfile library
|
||||
* Panel View support + HQ support (#36) - new option: --nopanelviewhq
|
||||
* Split profiles for K4NT and K4T
|
||||
* Rewrite of Landscape Mode support (huge readability improvement for KPW)
|
||||
* Upscale use now BILINEAR method
|
||||
* Added generic CSS file
|
||||
* Optimized archive extraction for zip/rar files (#40)
|
||||
|
||||
####2.9
|
||||
* Added support for generating a plain CBZ (skipping all the EPUB/Mobi generation) (#45)
|
||||
* Prevent output file overwriting the source one: if a duplicate name is detected, append _kcc to the name
|
||||
* Rarfile library updated to 2.6
|
||||
* Added GIF, TIFF and BMP to supported formats (#42)
|
||||
* Filenames slugifications (#28, #31, #9, #8)
|
||||
|
||||
####2.10:
|
||||
* Multiprocessing support
|
||||
* Kindle Fire support (color ePub/Mobi)
|
||||
* Panel View support for horizontal content
|
||||
* Fixed panel order for horizontal pages when --rotate is enabled
|
||||
* Disabled cropping and page number cutting for blank pages
|
||||
* Fixed some slugify issues with specific file naming conventions (#50, #51)
|
||||
|
||||
## COPYRIGHT
|
||||
|
||||
Copyright (c) 2012-2013 Ciro Mattia Gonano with further contributions by Paweł Jastrzębski.
|
||||
Copyright (c) 2012-2013 Ciro Mattia Gonano and Paweł Jastrzębski.
|
||||
KCC is released under ISC LICENSE; see LICENSE.txt for further details.
|
||||
|
||||
4
kcc.py
4
kcc.py
@@ -16,7 +16,7 @@
|
||||
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
# PERFORMANCE OF THIS SOFTWARE.
|
||||
#
|
||||
__version__ = '2.9'
|
||||
__version__ = '2.10'
|
||||
__license__ = 'ISC'
|
||||
__copyright__ = '2012-2013, Ciro Mattia Gonano <ciromattia@gmail.com>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
@@ -24,8 +24,10 @@ __docformat__ = 'restructuredtext en'
|
||||
from Tkinter import *
|
||||
from kcc import gui
|
||||
from sys import platform
|
||||
from multiprocessing import freeze_support
|
||||
import os
|
||||
|
||||
freeze_support()
|
||||
root = Tk()
|
||||
root.resizable(width=False, height=False)
|
||||
root.config(padx=5, pady=5, takefocus=True)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
__version__ = '2.0'
|
||||
__version__ = '2.10'
|
||||
__license__ = 'ISC'
|
||||
__copyright__ = '2012-2013, Ciro Mattia Gonano <ciromattia@gmail.com>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
@@ -17,7 +17,7 @@
|
||||
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
# PERFORMANCE OF THIS SOFTWARE.
|
||||
#
|
||||
__version__ = '2.9'
|
||||
__version__ = '2.10'
|
||||
__license__ = 'ISC'
|
||||
__copyright__ = '2012-2013, Ciro Mattia Gonano <ciromattia@gmail.com>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
@@ -32,6 +32,7 @@ from shutil import copytree
|
||||
from shutil import rmtree
|
||||
from shutil import make_archive
|
||||
from optparse import OptionParser
|
||||
from multiprocessing import Pool, freeze_support
|
||||
import image
|
||||
import cbxarchive
|
||||
import pdfjpgextract
|
||||
@@ -40,6 +41,11 @@ import pdfjpgextract
|
||||
def buildHTML(path, imgfile):
|
||||
filename = getImageFileName(imgfile)
|
||||
if filename is not None:
|
||||
# All files marked with this sufix need horizontal Panel View.
|
||||
if "_rotated" in str(filename):
|
||||
rotate = True
|
||||
else:
|
||||
rotate = False
|
||||
htmlpath = ''
|
||||
postfix = ''
|
||||
backref = 1
|
||||
@@ -70,26 +76,49 @@ def buildHTML(path, imgfile):
|
||||
imgfile, "\" class=\"singlePage\"/></div>\n"
|
||||
])
|
||||
if options.panelview:
|
||||
if options.righttoleft:
|
||||
f.writelines(["<div id=\"BoxTL\"><a class=\"app-amzn-magnify\" data-app-amzn-magnify='{\"targetId\":\"",
|
||||
"BoxTL-Panel-Parent\", \"ordinal\":2}'></a></div>\n",
|
||||
"<div id=\"BoxTR\"><a class=\"app-amzn-magnify\" data-app-amzn-magnify='{\"targetId\":\"",
|
||||
"BoxTR-Panel-Parent\", \"ordinal\":1}'></a></div>\n",
|
||||
"<div id=\"BoxBL\"><a class=\"app-amzn-magnify\" data-app-amzn-magnify='{\"targetId\":\"",
|
||||
"BoxBL-Panel-Parent\", \"ordinal\":4}'></a></div>\n",
|
||||
"<div id=\"BoxBR\"><a class=\"app-amzn-magnify\" data-app-amzn-magnify='{\"targetId\":\"",
|
||||
"BoxBR-Panel-Parent\", \"ordinal\":3}'></a></div>\n"
|
||||
])
|
||||
if rotate:
|
||||
if options.righttoleft:
|
||||
f.writelines(["<div id=\"BoxTL\"><a class=\"app-amzn-magnify\" data-app-amzn-magnify=",
|
||||
"'{\"targetId\":\"BoxTL-Panel-Parent\", \"ordinal\":1}'></a></div>\n",
|
||||
"<div id=\"BoxTR\"><a class=\"app-amzn-magnify\" data-app-amzn-magnify=",
|
||||
"'{\"targetId\":\"BoxTR-Panel-Parent\", \"ordinal\":3}'></a></div>\n",
|
||||
"<div id=\"BoxBL\"><a class=\"app-amzn-magnify\" data-app-amzn-magnify=",
|
||||
"'{\"targetId\":\"BoxBL-Panel-Parent\", \"ordinal\":2}'></a></div>\n",
|
||||
"<div id=\"BoxBR\"><a class=\"app-amzn-magnify\" data-app-amzn-magnify="
|
||||
"'{\"targetId\":\"BoxBR-Panel-Parent\", \"ordinal\":4}'></a></div>\n"
|
||||
])
|
||||
else:
|
||||
f.writelines(["<div id=\"BoxTL\"><a class=\"app-amzn-magnify\" data-app-amzn-magnify=",
|
||||
"'{\"targetId\":\"BoxTL-Panel-Parent\", \"ordinal\":2}'></a></div>\n",
|
||||
"<div id=\"BoxTR\"><a class=\"app-amzn-magnify\" data-app-amzn-magnify=",
|
||||
"'{\"targetId\":\"BoxTR-Panel-Parent\", \"ordinal\":4}'></a></div>\n",
|
||||
"<div id=\"BoxBL\"><a class=\"app-amzn-magnify\" data-app-amzn-magnify=",
|
||||
"'{\"targetId\":\"BoxBL-Panel-Parent\", \"ordinal\":1}'></a></div>\n",
|
||||
"<div id=\"BoxBR\"><a class=\"app-amzn-magnify\" data-app-amzn-magnify="
|
||||
"'{\"targetId\":\"BoxBR-Panel-Parent\", \"ordinal\":3}'></a></div>\n"
|
||||
])
|
||||
else:
|
||||
f.writelines(["<div id=\"BoxTL\"><a class=\"app-amzn-magnify\" data-app-amzn-magnify='{\"targetId\":\"",
|
||||
"BoxTL-Panel-Parent\", \"ordinal\":1}'></a></div>\n",
|
||||
"<div id=\"BoxTR\"><a class=\"app-amzn-magnify\" data-app-amzn-magnify='{\"targetId\":\"",
|
||||
"BoxTR-Panel-Parent\", \"ordinal\":2}'></a></div>\n",
|
||||
"<div id=\"BoxBL\"><a class=\"app-amzn-magnify\" data-app-amzn-magnify='{\"targetId\":\"",
|
||||
"BoxBL-Panel-Parent\", \"ordinal\":3}'></a></div>\n",
|
||||
"<div id=\"BoxBR\"><a class=\"app-amzn-magnify\" data-app-amzn-magnify='{\"targetId\":\"",
|
||||
"BoxBR-Panel-Parent\", \"ordinal\":4}'></a></div>\n"
|
||||
])
|
||||
if options.righttoleft:
|
||||
f.writelines(["<div id=\"BoxTL\"><a class=\"app-amzn-magnify\" data-app-amzn-magnify=",
|
||||
"'{\"targetId\":\"BoxTL-Panel-Parent\", \"ordinal\":2}'></a></div>\n",
|
||||
"<div id=\"BoxTR\"><a class=\"app-amzn-magnify\" data-app-amzn-magnify=",
|
||||
"'{\"targetId\":\"BoxTR-Panel-Parent\", \"ordinal\":1}'></a></div>\n",
|
||||
"<div id=\"BoxBL\"><a class=\"app-amzn-magnify\" data-app-amzn-magnify=",
|
||||
"'{\"targetId\":\"BoxBL-Panel-Parent\", \"ordinal\":4}'></a></div>\n",
|
||||
"<div id=\"BoxBR\"><a class=\"app-amzn-magnify\" data-app-amzn-magnify="
|
||||
"'{\"targetId\":\"BoxBR-Panel-Parent\", \"ordinal\":3}'></a></div>\n"
|
||||
])
|
||||
else:
|
||||
f.writelines(["<div id=\"BoxTL\"><a class=\"app-amzn-magnify\" data-app-amzn-magnify=",
|
||||
"'{\"targetId\":\"BoxTL-Panel-Parent\", \"ordinal\":1}'></a></div>\n",
|
||||
"<div id=\"BoxTR\"><a class=\"app-amzn-magnify\" data-app-amzn-magnify=",
|
||||
"'{\"targetId\":\"BoxTR-Panel-Parent\", \"ordinal\":2}'></a></div>\n",
|
||||
"<div id=\"BoxBL\"><a class=\"app-amzn-magnify\" data-app-amzn-magnify=",
|
||||
"'{\"targetId\":\"BoxBL-Panel-Parent\", \"ordinal\":3}'></a></div>\n",
|
||||
"<div id=\"BoxBR\"><a class=\"app-amzn-magnify\" data-app-amzn-magnify="
|
||||
"'{\"targetId\":\"BoxBR-Panel-Parent\", \"ordinal\":4}'></a></div>\n"
|
||||
])
|
||||
|
||||
f.writelines(["<div id=\"BoxTL-Panel-Parent\" class=\"target-mag-parent\"><div id=\"BoxTL-Panel\" class=\"",
|
||||
"target-mag\"><img src=\"", "../" * backref, "Images/", postfix, imgfile, "\" alt=\"",
|
||||
imgfile, "\"/></div></div>\n",
|
||||
@@ -227,7 +256,7 @@ def buildOPF(profile, dstdir, title, filelist, cover=None):
|
||||
f.write("</manifest>\n<spine toc=\"ncx\">\n")
|
||||
splitCountUsed = 1
|
||||
for entry in reflist:
|
||||
if entry.endswith("-1"):
|
||||
if entry.endswith("-kcca"):
|
||||
# noinspection PyRedundantParentheses
|
||||
if ((options.righttoleft and facing == 'left') or (not options.righttoleft and facing == 'right')) and\
|
||||
options.landscapemode:
|
||||
@@ -237,7 +266,7 @@ def buildOPF(profile, dstdir, title, filelist, cover=None):
|
||||
f.write("<itemref idref=\"page_" + entry + "\" properties=\"page-spread-" + facing1 + "\"/>\n")
|
||||
else:
|
||||
f.write("<itemref idref=\"page_" + entry + "\"/>\n")
|
||||
elif entry.endswith("-2"):
|
||||
elif entry.endswith("-kccb"):
|
||||
if options.landscapemode:
|
||||
f.write("<itemref idref=\"page_" + entry + "\" properties=\"page-spread-" + facing2 + "\"/>\n")
|
||||
else:
|
||||
@@ -286,16 +315,7 @@ def getImageFileName(imgfile):
|
||||
return filename
|
||||
|
||||
|
||||
def isInFilelist(filename, filelist):
|
||||
filename = os.path.splitext(filename)
|
||||
seen = False
|
||||
for item in filelist:
|
||||
if filename[0] == item[0]:
|
||||
seen = True
|
||||
return seen
|
||||
|
||||
|
||||
def applyImgOptimization(img, isSplit=False, toRight=False):
|
||||
def applyImgOptimization(img, isSplit, toRight, options):
|
||||
img.cropWhiteSpace(10.0)
|
||||
if options.cutpagenumbers:
|
||||
img.cutPageNumber()
|
||||
@@ -308,51 +328,62 @@ def applyImgOptimization(img, isSplit=False, toRight=False):
|
||||
|
||||
def dirImgProcess(path):
|
||||
global options, splitCount
|
||||
if options.righttoleft:
|
||||
facing = "right"
|
||||
else:
|
||||
facing = "left"
|
||||
|
||||
work = []
|
||||
pagenumber = 0
|
||||
pagenumbermodifier = 0
|
||||
pool = Pool()
|
||||
for (dirpath, dirnames, filenames) in os.walk(path):
|
||||
for afile in filenames:
|
||||
if getImageFileName(afile) is not None:
|
||||
if options.verbose:
|
||||
print "Optimizing " + afile + " for " + options.profile
|
||||
else:
|
||||
print ".",
|
||||
img = image.ComicPage(os.path.join(dirpath, afile), options.profile)
|
||||
if options.nosplitrotate:
|
||||
split = None
|
||||
else:
|
||||
split = img.splitPage(dirpath, options.righttoleft, options.rotate)
|
||||
if split is not None:
|
||||
if options.verbose:
|
||||
print "Splitted " + afile
|
||||
if options.righttoleft:
|
||||
toRight1 = False
|
||||
toRight2 = True
|
||||
if facing == "left":
|
||||
splitCount += 1
|
||||
facing = "right"
|
||||
else:
|
||||
toRight1 = True
|
||||
toRight2 = False
|
||||
if facing == "right":
|
||||
splitCount += 1
|
||||
facing = "left"
|
||||
img0 = image.ComicPage(split[0], options.profile)
|
||||
applyImgOptimization(img0, True, toRight1)
|
||||
img0.saveToDir(dirpath, options.forcepng)
|
||||
img1 = image.ComicPage(split[1], options.profile)
|
||||
applyImgOptimization(img1, True, toRight2)
|
||||
img1.saveToDir(dirpath, options.forcepng)
|
||||
else:
|
||||
if facing == "right":
|
||||
facing = "left"
|
||||
else:
|
||||
facing = "right"
|
||||
applyImgOptimization(img)
|
||||
img.saveToDir(dirpath, options.forcepng)
|
||||
pagenumber += 1
|
||||
work.append([afile, dirpath, pagenumber, options])
|
||||
splitpages = pool.map(fileImgProcess, work)
|
||||
pool.close()
|
||||
pool.join()
|
||||
splitpages = filter(None, splitpages)
|
||||
splitpages.sort()
|
||||
for page in splitpages:
|
||||
if (page + pagenumbermodifier) % 2 == 0:
|
||||
splitCount += 1
|
||||
pagenumbermodifier += 1
|
||||
pagenumbermodifier += 1
|
||||
|
||||
|
||||
def fileImgProcess(work):
|
||||
afile = work[0]
|
||||
dirpath = work[1]
|
||||
pagenumber = work[2]
|
||||
options = work[3]
|
||||
output = None
|
||||
if options.verbose:
|
||||
print "Optimizing " + afile + " for " + options.profile
|
||||
else:
|
||||
print ".",
|
||||
img = image.ComicPage(os.path.join(dirpath, afile), options.profile)
|
||||
if options.nosplitrotate:
|
||||
split = None
|
||||
else:
|
||||
split = img.splitPage(dirpath, options.righttoleft, options.rotate)
|
||||
if split is not None and split is not "R":
|
||||
if options.verbose:
|
||||
print "Splitted " + afile
|
||||
if options.righttoleft:
|
||||
toRight1 = False
|
||||
toRight2 = True
|
||||
else:
|
||||
toRight1 = True
|
||||
toRight2 = False
|
||||
output = pagenumber
|
||||
img0 = image.ComicPage(split[0], options.profile)
|
||||
applyImgOptimization(img0, True, toRight1, options)
|
||||
img0.saveToDir(dirpath, options.forcepng, options.forcecolor)
|
||||
img1 = image.ComicPage(split[1], options.profile)
|
||||
applyImgOptimization(img1, True, toRight2, options)
|
||||
img1.saveToDir(dirpath, options.forcepng, options.forcecolor)
|
||||
else:
|
||||
applyImgOptimization(img, False, False, options)
|
||||
img.saveToDir(dirpath, options.forcepng, options.forcecolor, split)
|
||||
return output
|
||||
|
||||
|
||||
def genEpubStruct(path):
|
||||
@@ -364,8 +395,8 @@ def genEpubStruct(path):
|
||||
sanitizeTree(os.path.join(path, 'OEBPS', 'Images'))
|
||||
os.mkdir(os.path.join(path, 'OEBPS', 'Text'))
|
||||
f = open(os.path.join(path, 'OEBPS', 'Text', 'style.css'), 'w')
|
||||
#DON'T COMPRESS CSS. KINDLE WILL FAIL TO PARSE IT.
|
||||
#Generic Panel View support + Margins fix for Non-Kindle devices.
|
||||
# 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",
|
||||
@@ -488,13 +519,6 @@ def genEpubStruct(path):
|
||||
for afile in filenames:
|
||||
filename = getImageFileName(afile)
|
||||
if filename is not None:
|
||||
if "credit" in afile.lower():
|
||||
os.rename(os.path.join(dirpath, afile), os.path.join(dirpath, 'ZZZ999_' + afile))
|
||||
afile = 'ZZZ999_' + afile
|
||||
if "+" in afile.lower() or "#" in afile.lower():
|
||||
newfilename = afile.replace('+', '_').replace('#', '_')
|
||||
os.rename(os.path.join(dirpath, afile), os.path.join(dirpath, newfilename))
|
||||
afile = newfilename
|
||||
filelist.append(buildHTML(dirpath, afile))
|
||||
if not chapter:
|
||||
chapterlist.append((dirpath.replace('Images', 'Text'), filelist[-1][1]))
|
||||
@@ -556,14 +580,16 @@ def slugify(value):
|
||||
|
||||
|
||||
def sanitizeTree(filetree):
|
||||
for root, dirs, files in os.walk(filetree):
|
||||
for root, dirs, files in os.walk(filetree, False):
|
||||
for name in files:
|
||||
if name.startswith('.') or name.lower() == 'thumbs.db':
|
||||
os.remove(os.path.join(root, name))
|
||||
else:
|
||||
splitname = os.path.splitext(name)
|
||||
os.rename(os.path.join(root, name),
|
||||
os.path.join(root, slugify(splitname[0]) + splitname[1]))
|
||||
slugified = slugify(splitname[0])
|
||||
while os.path.exists(os.path.join(root, slugified + splitname[1])):
|
||||
slugified += "1"
|
||||
os.rename(os.path.join(root, name), os.path.join(root, slugified + splitname[1]))
|
||||
for name in dirs:
|
||||
if name.startswith('.'):
|
||||
os.remove(os.path.join(root, name))
|
||||
@@ -573,11 +599,11 @@ def sanitizeTree(filetree):
|
||||
|
||||
def Copyright():
|
||||
print ('comic2ebook v%(__version__)s. '
|
||||
'Written 2012 by Ciro Mattia Gonano.' % globals())
|
||||
'Written 2013 by Ciro Mattia Gonano and Pawel Jastrzebski.' % globals())
|
||||
|
||||
|
||||
def Usage():
|
||||
print "Generates HTML, NCX and OPF for a Comic ebook from a bunch of images."
|
||||
print "Generates EPUB/CBZ comic ebook from a bunch of images."
|
||||
parser.print_help()
|
||||
|
||||
|
||||
@@ -586,7 +612,8 @@ def main(argv=None):
|
||||
usage = "Usage: %prog [options] comic_file|comic_folder"
|
||||
parser = OptionParser(usage=usage, version=__version__)
|
||||
parser.add_option("-p", "--profile", action="store", dest="profile", default="KHD",
|
||||
help="Device profile (Choose one among K1, K2, K3, K4NT, K4T, KDX, KDXG or KHD) [Default=KHD]")
|
||||
help="Device profile (Choose one among K1, K2, K3, K4NT, K4T, KDX, KDXG, KHD, KF, KFHD, KFHD8) "
|
||||
"[Default=KHD]")
|
||||
parser.add_option("-t", "--title", action="store", dest="title", default="defaulttitle",
|
||||
help="Comic title [Default=filename]")
|
||||
parser.add_option("-m", "--manga-style", action="store_true", dest="righttoleft", default=False,
|
||||
@@ -668,25 +695,44 @@ def getOutputFilename(srcpath, wantedname, ext):
|
||||
|
||||
def checkOptions():
|
||||
global options
|
||||
# Landscape mode is only supported by Kindle Touch and Paperwhite.
|
||||
if options.profile == 'K4T' or options.profile == 'KHD':
|
||||
options.landscapemode = True
|
||||
else:
|
||||
options.landscapemode = False
|
||||
# Older Kindle don't support Virtual Panel View. We providing them configuration that will fake that feature.
|
||||
if options.profile == 'K3' or options.profile == 'K4NT':
|
||||
#Real Panel View
|
||||
# Real Panel View
|
||||
options.panelview = True
|
||||
else:
|
||||
#Virtual Panel View
|
||||
# Virtual Panel View
|
||||
options.panelview = False
|
||||
if options.profile == 'K1' or options.profile == 'K2' or options.profile == 'KDX' or options.profile == 'KDXG':
|
||||
# Older Kindle don't need higher resolution files due lack of Panel View.
|
||||
# Kindle Fire family have very high resolution. Bigger images are not needed.
|
||||
if options.profile == 'K1' or options.profile == 'K2' or options.profile == 'KDX' or options.profile == 'KDXG'\
|
||||
or options.profile == 'KF' or options.profile == 'KFHD' or options.profile == 'KFHD8':
|
||||
options.nopanelviewhq = True
|
||||
# Disabling grayscale conversion for Kindle Fire family.
|
||||
# Forcing JPEG output. For now code can't provide color PNG files.
|
||||
if options.profile == 'KF' or options.profile == 'KFHD' or options.profile == 'KFHD8':
|
||||
options.forcecolor = True
|
||||
options.forcepng = False
|
||||
else:
|
||||
options.forcecolor = False
|
||||
# Mixing vertical and horizontal pages require real Panel View.
|
||||
# Landscape mode don't work correcly without Virtual Panel View.
|
||||
if options.rotate:
|
||||
options.panelview = True
|
||||
options.landscapemode = False
|
||||
|
||||
|
||||
def getEpubPath():
|
||||
global epub_path
|
||||
return epub_path
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
freeze_support()
|
||||
Copyright()
|
||||
main(sys.argv[1:])
|
||||
sys.exit(0)
|
||||
|
||||
198
kcc/image.py
198
kcc/image.py
@@ -20,7 +20,7 @@ __copyright__ = '2012-2013, Ciro Mattia Gonano <ciromattia@gmail.com>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
import os
|
||||
from PIL import Image, ImageOps, ImageStat
|
||||
from PIL import Image, ImageOps, ImageStat, ImageChops
|
||||
|
||||
|
||||
class ImageFlags:
|
||||
@@ -84,7 +84,10 @@ class ProfileData:
|
||||
'K4T': ("Kindle Touch", (600, 800), Palette16, 1.8, (900, 1200)),
|
||||
'KHD': ("Kindle Paperwhite", (758, 1024), Palette16, 1.8, (1137, 1536)),
|
||||
'KDX': ("Kindle DX", (824, 1200), Palette15, 1.8, (1236, 1800)),
|
||||
'KDXG': ("Kindle DXG", (824, 1200), Palette16, 1.8, (1236, 1800))
|
||||
'KDXG': ("Kindle DXG", (824, 1200), Palette16, 1.8, (1236, 1800)),
|
||||
'KF': ("Kindle Fire", (600, 1024), Palette16, 1.0, (900, 1536)),
|
||||
'KFHD': ("Kindle Fire HD 7\"", (800, 1280), Palette16, 1.0, (1200, 1920)),
|
||||
'KFHD8': ("Kindle Fire HD 8.9\"", (1200, 1920), Palette16, 1.0, (1800, 2880))
|
||||
}
|
||||
|
||||
ProfileLabels = {
|
||||
@@ -95,7 +98,10 @@ class ProfileData:
|
||||
"Kindle 4/Touch": 'K4T',
|
||||
"Kindle Paperwhite": 'KHD',
|
||||
"Kindle DX": 'KDX',
|
||||
"Kindle DXG": 'KDXG'
|
||||
"Kindle DXG": 'KDXG',
|
||||
"Kindle Fire": 'KF',
|
||||
"Kindle Fire HD 7\"": 'KFHD',
|
||||
"Kindle Fire HD 8.9\"": 'KFHD8'
|
||||
}
|
||||
|
||||
|
||||
@@ -113,15 +119,21 @@ class ComicPage:
|
||||
raise RuntimeError('Cannot read image file %s' % source)
|
||||
self.image = self.image.convert('RGB')
|
||||
|
||||
def saveToDir(self, targetdir, forcepng):
|
||||
def saveToDir(self, targetdir, forcepng, color, sufix=None):
|
||||
filename = os.path.basename(self.origFileName)
|
||||
try:
|
||||
self.image = self.image.convert('L') # convert to grayscale
|
||||
if not color:
|
||||
self.image = self.image.convert('L') # convert to grayscale
|
||||
# Sufix is used to recognise which files need horizontal Panel View.
|
||||
if sufix == "R":
|
||||
sufix = "_rotated"
|
||||
else:
|
||||
sufix = ""
|
||||
os.remove(os.path.join(targetdir, filename))
|
||||
if forcepng:
|
||||
self.image.save(os.path.join(targetdir, os.path.splitext(filename)[0] + ".png"), "PNG")
|
||||
self.image.save(os.path.join(targetdir, os.path.splitext(filename)[0] + sufix + ".png"), "PNG")
|
||||
else:
|
||||
self.image.save(os.path.join(targetdir, os.path.splitext(filename)[0] + ".jpg"), "JPEG")
|
||||
self.image.save(os.path.join(targetdir, os.path.splitext(filename)[0] + sufix + ".jpg"), "JPEG")
|
||||
except IOError as e:
|
||||
raise RuntimeError('Cannot write image in directory %s: %s' % (targetdir, e))
|
||||
|
||||
@@ -195,7 +207,7 @@ class ComicPage:
|
||||
if (width > height) != (dstwidth > dstheight):
|
||||
if rotate:
|
||||
self.image = self.image.rotate(90)
|
||||
return None
|
||||
return "R"
|
||||
else:
|
||||
if width > height:
|
||||
# source is landscape, so split by the width
|
||||
@@ -206,8 +218,8 @@ class ComicPage:
|
||||
leftbox = (0, 0, width, height / 2)
|
||||
rightbox = (0, height / 2, width, height)
|
||||
filename = os.path.splitext(os.path.basename(self.origFileName))
|
||||
fileone = targetdir + '/' + filename[0] + '-1' + filename[1]
|
||||
filetwo = targetdir + '/' + filename[0] + '-2' + filename[1]
|
||||
fileone = targetdir + '/' + filename[0] + '-kcca' + filename[1]
|
||||
filetwo = targetdir + '/' + filename[0] + '-kccb' + filename[1]
|
||||
try:
|
||||
if righttoleft:
|
||||
pageone = self.image.crop(rightbox)
|
||||
@@ -225,95 +237,97 @@ class ComicPage:
|
||||
return None
|
||||
|
||||
def cutPageNumber(self):
|
||||
widthImg, heightImg = self.image.size
|
||||
delta = 2
|
||||
diff = delta
|
||||
fixedThreshold = 5
|
||||
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:
|
||||
if ImageChops.invert(self.image).getbbox() is not None:
|
||||
widthImg, heightImg = self.image.size
|
||||
delta = 2
|
||||
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:
|
||||
fixedThreshold = 5
|
||||
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
|
||||
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:
|
||||
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
|
||||
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))
|
||||
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))
|
||||
return self.image
|
||||
|
||||
def cropWhiteSpace(self, threshold):
|
||||
widthImg, heightImg = self.image.size
|
||||
delta = 10
|
||||
diff = delta
|
||||
# top
|
||||
while ImageStat.Stat(self.image.crop((0, 0, widthImg, diff))).var[0] < threshold and diff < heightImg:
|
||||
diff += delta
|
||||
diff -= delta
|
||||
# print "Top crop: %s"%diff
|
||||
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] < threshold and diff < widthImg:
|
||||
diff += delta
|
||||
diff -= delta
|
||||
# print "Left crop: %s"%diff
|
||||
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] < threshold\
|
||||
and diff < heightImg:
|
||||
diff += delta
|
||||
diff -= delta
|
||||
# print "Down crop: %s"%diff
|
||||
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] < threshold\
|
||||
and diff < widthImg:
|
||||
diff += delta
|
||||
diff -= delta
|
||||
# print "Right crop: %s"%diff
|
||||
self.image = self.image.crop((0, 0, widthImg - diff, heightImg))
|
||||
# print "New size: %sx%s"%(self.image.size[0],self.image.size[1])
|
||||
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] < threshold and diff < heightImg:
|
||||
diff += delta
|
||||
diff -= delta
|
||||
# print "Top crop: %s"%diff
|
||||
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] < threshold and diff < widthImg:
|
||||
diff += delta
|
||||
diff -= delta
|
||||
# print "Left crop: %s"%diff
|
||||
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] < threshold\
|
||||
and diff < heightImg:
|
||||
diff += delta
|
||||
diff -= delta
|
||||
# print "Down crop: %s"%diff
|
||||
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] < threshold\
|
||||
and diff < widthImg:
|
||||
diff += delta
|
||||
diff -= delta
|
||||
# print "Right crop: %s"%diff
|
||||
self.image = self.image.crop((0, 0, widthImg - diff, heightImg))
|
||||
# print "New size: %sx%s"%(self.image.size[0],self.image.size[1])
|
||||
return self.image
|
||||
|
||||
# def addProgressbar(self, file_number, files_totalnumber, size, howoften):
|
||||
|
||||
2
setup.py
2
setup.py
@@ -15,7 +15,7 @@ use_setuptools()
|
||||
import sys
|
||||
|
||||
NAME = "KindleComicConverter"
|
||||
VERSION = "2.9"
|
||||
VERSION = "2.10"
|
||||
MAIN = "kcc.py"
|
||||
|
||||
includefiles = ['README.md', 'MANIFEST.in', 'LICENSE.txt', 'comic2ebook.ico', 'comic2ebook.icns']
|
||||
|
||||
@@ -10,7 +10,7 @@ sys.path.insert(0, 'kcc')
|
||||
|
||||
setup(
|
||||
name = "KindleComicConverter",
|
||||
version = "2.9",
|
||||
version = "2.10",
|
||||
author = "Ciro Mattia Gonano",
|
||||
author_email = "ciromattia@gmail.com",
|
||||
description = "A tool to convert comics (CBR/CBZ/PDFs/image folders) to MOBI.",
|
||||
|
||||
Reference in New Issue
Block a user