1
0
mirror of https://github.com/ciromattia/kcc synced 2025-12-13 17:56:30 +00:00

Use temporary directory as workdir (fixes converting from external volumes and zipfiles renaming)

Fixed "add folders" from GUI.
This commit is contained in:
Ciro Mattia Gonano
2013-01-29 15:01:36 +01:00
parent dcb577d457
commit d136f8cc3a
6 changed files with 65 additions and 93 deletions

View File

@@ -12,8 +12,8 @@ It also optimizes comic images by:
## BINARY RELEASES ## BINARY RELEASES
You can find the latest released binary at the following links: You can find the latest released binary at the following links:
- OS X: [https://dl.dropbox.com/u/16806101/KindleComicConverter_osx_2.3.zip](https://dl.dropbox.com/u/16806101/KindleComicConverter_osx_2.3.zip) - OS X: [https://dl.dropbox.com/u/16806101/KindleComicConverter_osx_2.4.zip](https://dl.dropbox.com/u/16806101/KindleComicConverter_osx_2.4.zip)
- Win64: [https://dl.dropbox.com/u/16806101/KindleComicConverter_win-amd64_2.3.zip](https://dl.dropbox.com/u/16806101/KindleComicConverter_win-amd64_2.3.zip) - Win64: [https://dl.dropbox.com/u/16806101/KindleComicConverter_win-amd64_2.4.zip](https://dl.dropbox.com/u/16806101/KindleComicConverter_win-amd64_2.4.zip)
- Linux: just download sourcecode and launch `python kcc.py` *(provided you have Python and Pillow installed)* - Linux: just download sourcecode and launch `python kcc.py` *(provided you have Python and Pillow installed)*
## INPUT FORMATS ## INPUT FORMATS
@@ -40,6 +40,8 @@ As of v. 1.50, KCC supports subfolders!
### GUI ### GUI
Should be pretty self-explanatory, just keep in mind that it's still in development ;) Should be pretty self-explanatory, just keep in mind that it's still in development ;)
While working it seems frozen, I'll try to fix the aesthetics later.
Conversion being done, you should find an .epub and a .mobi files alongside the original input file (same directory)
### Standalone `comic2ebook.py` usage: ### Standalone `comic2ebook.py` usage:
@@ -104,7 +106,9 @@ and installed in a directory reachable by your PATH (e.g. `/usr/local/bin/` or `
- 2.1: Added basic error reporting - 2.1: Added basic error reporting
- 2.2: Added (valid!) ePub 2.0 output - 2.2: Added (valid!) ePub 2.0 output
Rename .zip files to .cbz to avoid overwriting Rename .zip files to .cbz to avoid overwriting
- 2.3: Fixed win32 ePub generation, folder handling, filenames with spaces and subfolders. - 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.
## TODO ## TODO
- Add gracefully exit for CBR if no rarfile.py and no unrar executable are found - Add gracefully exit for CBR if no rarfile.py and no unrar executable are found

2
kcc.py
View File

@@ -16,7 +16,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__ = '2.3' __version__ = '2.4'
__license__ = 'ISC' __license__ = 'ISC'
__copyright__ = '2012-2013, Ciro Mattia Gonano <ciromattia@gmail.com>' __copyright__ = '2012-2013, Ciro Mattia Gonano <ciromattia@gmail.com>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'

View File

@@ -25,35 +25,28 @@ class CBxArchive:
self.cbxexts = ['.zip','.cbz','.rar','.cbr'] self.cbxexts = ['.zip','.cbz','.rar','.cbr']
self.origFileName = origFileName self.origFileName = origFileName
self.filename = os.path.splitext(origFileName) self.filename = os.path.splitext(origFileName)
self.path = self.filename[0]
def isCbxFile(self): def isCbxFile(self):
result = (self.filename[1].lower() in self.cbxexts) return self.filename[1].lower() in self.cbxexts
if result == True:
return result
return False
def getPath(self): def extractCBZ(self,targetdir):
return self.path
def extractCBZ(self):
try: try:
from zipfile import ZipFile from zipfile import ZipFile
except ImportError: except ImportError:
self.cbzFile = None self.cbzFile = None
cbzFile = ZipFile(self.origFileName) cbzFile = ZipFile(self.origFileName)
for f in cbzFile.namelist(): for f in cbzFile.namelist():
if (f.startswith('__MACOSX') or f.endswith('.DS_Store')): if f.startswith('__MACOSX') or f.endswith('.DS_Store'):
pass # skip MacOS special files pass # skip MacOS special files
elif f.endswith('/'): elif f.endswith('/'):
try: try:
os.makedirs(self.path+'/'+f) os.makedirs(os.path.join(targetdir,f))
except: except:
pass #the dir exists so we are going to extract the images only. pass #the dir exists so we are going to extract the images only.
else: else:
cbzFile.extract(f, self.path) cbzFile.extract(f, targetdir)
def extractCBR(self): def extractCBR(self,targetdir):
try: try:
import rarfile import rarfile
except ImportError: except ImportError:
@@ -61,24 +54,25 @@ class CBxArchive:
return return
cbrFile = rarfile.RarFile(self.origFileName) cbrFile = rarfile.RarFile(self.origFileName)
for f in cbrFile.namelist(): for f in cbrFile.namelist():
if (f.startswith('__MACOSX') or f.endswith('.DS_Store')): if f.startswith('__MACOSX') or f.endswith('.DS_Store'):
pass # skip MacOS special files pass # skip MacOS special files
elif f.endswith('/'): elif f.endswith('/'):
try: try:
os.makedirs(self.path+'/'+f) os.makedirs(os.path.join(targetdir,f))
except: except:
pass #the dir exists so we are going to extract the images only. pass #the dir exists so we are going to extract the images only.
else: else:
cbrFile.extract(f, self.path) cbrFile.extract(f, targetdir)
def extract(self): def extract(self,targetdir):
if ('.cbr' == self.filename[1].lower() or '.rar' == self.filename[1].lower()): if '.cbr' == self.filename[1].lower() or '.rar' == self.filename[1].lower():
self.extractCBR() self.extractCBR(targetdir)
elif ('.cbz' == self.filename[1].lower() or '.zip' == self.filename[1].lower()): elif '.cbz' == self.filename[1].lower() or '.zip' == self.filename[1].lower():
self.extractCBZ() self.extractCBZ(targetdir)
dir = os.listdir(self.path) dir = os.listdir(targetdir)
if (len(dir) == 1): if len(dir) == 1 and os.path.isdir(os.path.join(targetdir,dir[0])):
import shutil import shutil
for f in os.listdir(self.path + "/" + dir[0]): for f in os.listdir(os.path.join(targetdir,dir[0])):
shutil.move(self.path + "/" + dir[0] + "/" + f,self.path) shutil.move(os.path.join(targetdir,dir[0],f),targetdir)
os.rmdir(self.path + "/" + dir[0]) os.rmdir(os.path.join(targetdir,dir[0]))
return targetdir

View File

@@ -16,39 +16,20 @@
# 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.
# #
# Changelog __version__ = '2.4'
# 1.00 - Initial version
# 1.10 - Added support for CBZ/CBR files
# 1.11 - Added support for ZIP/RAR extensions
# 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 - Support for subfolders
#
# Todo:
# - Add gracefully exit for CBR if no rarfile.py and no unrar
# executable are found
# - Improve error reporting
__version__ = '2.3'
__license__ = 'ISC' __license__ = 'ISC'
__copyright__ = '2012-2013, Ciro Mattia Gonano <ciromattia@gmail.com>' __copyright__ = '2012-2013, Ciro Mattia Gonano <ciromattia@gmail.com>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
import os, sys import os, sys, tempfile
from shutil import move,copyfile,make_archive,rmtree from shutil import move,copyfile,copytree,rmtree,make_archive
from optparse import OptionParser from optparse import OptionParser
import image, cbxarchive, pdfjpgextract import image, cbxarchive, pdfjpgextract
def buildHTML(path,file): def buildHTML(path,file):
filename = getImageFileName(file) filename = getImageFileName(file)
if filename is not None: if filename is not None:
htmlpath = ''
postfix = '' postfix = ''
backref = 1 backref = 1
head = path head = path
@@ -203,9 +184,9 @@ def dirImgProcess(path):
if options.verbose: if options.verbose:
print "Splitted " + file print "Splitted " + file
img0 = image.ComicPage(split[0],options.profile) img0 = image.ComicPage(split[0],options.profile)
img1 = image.ComicPage(split[1],options.profile)
applyImgOptimization(img0) applyImgOptimization(img0)
img0.saveToDir(dirpath) img0.saveToDir(dirpath)
img1 = image.ComicPage(split[1],options.profile)
applyImgOptimization(img1) applyImgOptimization(img1)
img1.saveToDir(dirpath) img1.saveToDir(dirpath)
else: else:
@@ -238,35 +219,31 @@ def genEpubStruct(path):
if cover is None: if cover is None:
cover = os.path.join(filelist[-1][0],'cover' + getImageFileName(filelist[-1][1])[1]) cover = os.path.join(filelist[-1][0],'cover' + getImageFileName(filelist[-1][1])[1])
copyfile(os.path.join(filelist[-1][0],filelist[-1][1]), cover) copyfile(os.path.join(filelist[-1][0],filelist[-1][1]), cover)
if options.title == 'defaulttitle':
options.title = os.path.basename(path)
buildNCX(path,options.title,chapterlist) buildNCX(path,options.title,chapterlist)
# ensure we're sorting files alphabetically # ensure we're sorting files alphabetically
filelist = sorted(filelist, key=lambda name: (name[0].lower(), name[1].lower())) filelist = sorted(filelist, key=lambda name: (name[0].lower(), name[1].lower()))
buildOPF(options.profile,path,options.title,filelist,cover) buildOPF(options.profile,path,options.title,filelist,cover)
def getWorkFolder(file): def getWorkFolder(file):
workdir = tempfile.mkdtemp()
fname = os.path.splitext(file) fname = os.path.splitext(file)
if fname[1].lower() == '.pdf': if os.path.isdir(file):
try:
import shutil
os.rmdir(workdir) # needed for copytree() fails if dst already exists
copytree(file, workdir)
path = workdir
except OSError:
raise
elif fname[1].lower() == '.pdf':
pdf = pdfjpgextract.PdfJpgExtract(file) pdf = pdfjpgextract.PdfJpgExtract(file)
pdf.extract() path = pdf.extract(workdir)
path = pdf.getPath()
else: else:
if fname[1].lower() == '.zip':
move(file,fname[0] + '.cbz')
file = fname[0] + '.cbz'
cbx = cbxarchive.CBxArchive(file) cbx = cbxarchive.CBxArchive(file)
if cbx.isCbxFile(): if cbx.isCbxFile():
cbx.extract() path = cbx.extract(workdir)
path = cbx.getPath()
else: else:
try: raise TypeError
import shutil
if not os.path.isdir(file + "_orig"):
shutil.copytree(file, file + "_orig")
path = file
except OSError:
raise
move(path,path + "_temp") move(path,path + "_temp")
move(path + "_temp",os.path.join(path,'OEBPS','Images')) move(path + "_temp",os.path.join(path,'OEBPS','Images'))
return path return path
@@ -304,18 +281,23 @@ def main(argv=None):
if len(args) != 1: if len(args) != 1:
parser.print_help() parser.print_help()
return return
path = args[0] path = getWorkFolder(args[0])
path = getWorkFolder(path) if options.title == 'defaulttitle':
options.title = os.path.splitext(os.path.basename(args[0]))[0]
if options.imgproc: if options.imgproc:
print "Processing images..." print "Processing images..."
dirImgProcess(path + "/OEBPS/Images/") dirImgProcess(path + "/OEBPS/Images/")
print "Creating ePub structure..." print "Creating ePub structure..."
genEpubStruct(path) genEpubStruct(path)
# actually zip the ePub # actually zip the ePub
make_archive(path,'zip',path) if os.path.isdir(args[0]):
move(path + '.zip', path + '.epub') epubpath = args[0] + '.epub'
else:
epubpath = os.path.splitext(args[0])[0] + '.epub'
make_archive(os.path.join(path,'comic'),'zip',path)
move(os.path.join(path,'comic') + '.zip', epubpath)
rmtree(path) rmtree(path)
epub_path = path + '.epub' return epubpath
def getEpubPath(): def getEpubPath():
global epub_path global epub_path

View File

@@ -51,7 +51,8 @@ class MainWindow:
self.refresh_list() self.refresh_list()
def open_folder(self): def open_folder(self):
self.filelist = tkFileDialog.askdirectory(title="Choose a folder...") f = tkFileDialog.askdirectory(title="Choose a folder...")
self.filelist.extend([f])
self.refresh_list() self.refresh_list()
def refresh_list(self): def refresh_list(self):
@@ -146,8 +147,7 @@ class MainWindow:
try: try:
subargv = list(argv) subargv = list(argv)
subargv.append(entry) subargv.append(entry)
comic2ebook.main(subargv) epub_path = comic2ebook.main(subargv)
epub_path = comic2ebook.getEpubPath()
except Exception, err: except Exception, err:
tkMessageBox.showerror('Error comic2ebook', "Error on file %s:\n%s" % (subargv[-1], str(err))) tkMessageBox.showerror('Error comic2ebook', "Error on file %s:\n%s" % (subargv[-1], str(err)))
errors = True errors = True

View File

@@ -110,10 +110,9 @@ class ComicPage:
def saveToDir(self,targetdir): def saveToDir(self,targetdir):
filename = os.path.basename(self.origFileName) filename = os.path.basename(self.origFileName)
#print "Saving to " + targetdir + '/' + filename
try: try:
self.image = self.image.convert('L') # convert to grayscale self.image = self.image.convert('L') # convert to grayscale
self.image.save(targetdir + '/' + filename,"JPEG") self.image.save(os.path.join(targetdir,filename),"JPEG")
except IOError as e: except IOError as e:
raise RuntimeError('Cannot write image in directory %s: %s' %(targetdir,e)) raise RuntimeError('Cannot write image in directory %s: %s' %(targetdir,e))
@@ -133,11 +132,9 @@ class ComicPage:
if self.image.size[0] <= self.size[0] and self.image.size[1] <= self.size[1]: if self.image.size[0] <= self.size[0] and self.image.size[1] <= self.size[1]:
if not upscale: if not upscale:
# do not upscale but center image in a device-sized image # do not upscale but center image in a device-sized image
newImage = Image.new('RGB', (self.size[0], self.size[1]), (255,255,255)) borderw = (self.size[0] - self.image.size[0]) / 2
newImage.paste(self.image, ( borderh = (self.size[1] - self.image.size[1]) / 2
(self.size[0] - self.image.size[0]) / 2, self.image = ImageOps.expand(self.image, border=(borderw,borderh), fill='white')
(self.size[1] - self.image.size[1]) / 2))
self.image = newImage
return self.image return self.image
else: else:
method = Image.NEAREST method = Image.NEAREST
@@ -149,14 +146,10 @@ class ComicPage:
ratioDev = float(self.size[0]) / float(self.size[1]) ratioDev = float(self.size[0]) / float(self.size[1])
if (float(self.image.size[0]) / float(self.image.size[1])) < ratioDev: if (float(self.image.size[0]) / float(self.image.size[1])) < ratioDev:
diff = int(self.image.size[1] * ratioDev) - self.image.size[0] diff = int(self.image.size[1] * ratioDev) - self.image.size[0]
newImage = Image.new('RGB', (self.image.size[0] + diff, self.image.size[1]), (255,255,255)) self.image = ImageOps.expand(self.image, border=(diff/2,0), fill='white')
newImage.paste(self.image, (diff / 2, 0, diff / 2 + self.image.size[0], self.image.size[1]))
self.image = newImage
elif (float(self.image.size[0]) / float(self.image.size[1])) > ratioDev: elif (float(self.image.size[0]) / float(self.image.size[1])) > ratioDev:
diff = int(self.image.size[0] / ratioDev) - self.image.size[1] diff = int(self.image.size[0] / ratioDev) - self.image.size[1]
newImage = Image.new('RGB', (self.image.size[0], self.image.size[1] + diff), (255,255,255)) self.image = ImageOps.expand(self.image, border=(0,diff/2), fill='white')
newImage.paste(self.image, (0, diff / 2, self.image.size[0], diff / 2 + self.image.size[1]))
self.image = newImage
self.image = ImageOps.fit(self.image, self.size, method = method, centering = (0.5,0.5)) self.image = ImageOps.fit(self.image, self.size, method = method, centering = (0.5,0.5))
return self.image return self.image
@@ -338,4 +331,3 @@ class ComicPage:
if i==5: if i==5:
draw.rectangle([(widthImg/2-1,heightImg-5), (widthImg/2+1,heightImg)],outline=black,fill=notch_colour) draw.rectangle([(widthImg/2-1,heightImg-5), (widthImg/2+1,heightImg)],outline=black,fill=notch_colour)
return self.image return self.image