1
0
mirror of https://github.com/ciromattia/kcc synced 2025-12-15 18:56:28 +00:00

General refactoring and tweaks

This commit is contained in:
Paweł Jastrzębski
2015-02-09 17:19:33 +01:00
parent 7924c492b3
commit f5dd813c4c
5 changed files with 130 additions and 155 deletions

View File

@@ -1335,14 +1335,10 @@ class KCCGUI_MetaEditor(KCC_MetaEditor_ui.Ui_MetaEditorDialog):
self.EditorFrame.setEnabled(True) self.EditorFrame.setEnabled(True)
self.OKButton.setEnabled(True) self.OKButton.setEnabled(True)
self.StatusLabel.setText('Separate authors with a comma.') self.StatusLabel.setText('Separate authors with a comma.')
self.SeriesLine.setText(self.parser.data['Series']) for field in (self.SeriesLine, self.VolumeLine, self.NumberLine, self.MUidLine):
self.VolumeLine.setText(self.parser.data['Volume']) field.setText(self.parser.data[field.objectName()[:-4]])
self.NumberLine.setText(self.parser.data['Number']) for field in (self.WriterLine, self.PencillerLine, self.InkerLine, self.ColoristLine):
self.WriterLine.setText(', '.join(self.parser.data['Writers'])) field.setText(', '.join(self.parser.data[field.objectName()[:-4] + 's']))
self.PencillerLine.setText(', '.join(self.parser.data['Pencillers']))
self.InkerLine.setText(', '.join(self.parser.data['Inkers']))
self.ColoristLine.setText(', '.join(self.parser.data['Colorists']))
self.MUidLine.setText(self.parser.data['MUid'])
def saveData(self): def saveData(self):
for field in (self.VolumeLine, self.NumberLine, self.MUidLine): for field in (self.VolumeLine, self.NumberLine, self.MUidLine):

View File

@@ -75,6 +75,7 @@ def main(argv=None):
def buildHTML(path, imgfile, imgfilepath, forcePV=False): def buildHTML(path, imgfile, imgfilepath, forcePV=False):
imgfilepath = md5Checksum(imgfilepath) imgfilepath = md5Checksum(imgfilepath)
filename = getImageFileName(imgfile) filename = getImageFileName(imgfile)
additionalStyle = ''
if options.imgproc: if options.imgproc:
if "Rotated" in options.imgIndex[imgfilepath]: if "Rotated" in options.imgIndex[imgfilepath]:
rotatedPage = True rotatedPage = True
@@ -92,6 +93,8 @@ def buildHTML(path, imgfile, imgfilepath, forcePV=False):
noVerticalPV = True noVerticalPV = True
else: else:
noVerticalPV = False noVerticalPV = False
if "BlackFill" in options.imgIndex[imgfilepath]:
additionalStyle = ' style="background-color:#000000" '
else: else:
rotatedPage = False rotatedPage = False
noPV = False noPV = False
@@ -125,7 +128,7 @@ def buildHTML(path, imgfile, imgfilepath, forcePV=False):
"<link href=\"", "../" * (backref - 1), "<link href=\"", "../" * (backref - 1),
"style.css\" type=\"text/css\" rel=\"stylesheet\"/>\n", "style.css\" type=\"text/css\" rel=\"stylesheet\"/>\n",
"</head>\n", "</head>\n",
"<body>\n", "<body" + additionalStyle + ">\n",
"<div class=\"fs\">\n", "<div class=\"fs\">\n",
"<div><img src=\"", "../" * backref, "Images/", postfix, imgfile, "\" alt=\"", "<div><img src=\"", "../" * backref, "Images/", postfix, imgfile, "\" alt=\"",
imgfile, "\" class=\"singlePage\"/></div>\n" imgfile, "\" class=\"singlePage\"/></div>\n"
@@ -169,7 +172,7 @@ def buildHTML(path, imgfile, imgfilepath, forcePV=False):
f.writelines(["<div id=\"" + boxes[i] + "\"><a class=\"app-amzn-magnify\" data-app-amzn-magnify=", f.writelines(["<div id=\"" + boxes[i] + "\"><a class=\"app-amzn-magnify\" data-app-amzn-magnify=",
"'{\"targetId\":\"" + boxes[i] + "-Panel-Parent\", \"ordinal\":" + str(order[i]), "'{\"targetId\":\"" + boxes[i] + "-Panel-Parent\", \"ordinal\":" + str(order[i]),
"}'></a></div>\n"]) "}'></a></div>\n"])
if options.quality == 2: if options.quality == 2 and not forcePV:
imgfilepv = imgfile.split(".") imgfilepv = imgfile.split(".")
imgfilepv[0] += "-hq" imgfilepv[0] += "-hq"
imgfilepv = ".".join(imgfilepv) imgfilepv = ".".join(imgfilepv)
@@ -433,6 +436,7 @@ def buildEPUB(path, chapterNames, tomeNumber):
cover = os.path.join(os.path.join(path, 'OEBPS', 'Images'), cover = os.path.join(os.path.join(path, 'OEBPS', 'Images'),
'cover' + getImageFileName(filelist[-1][1])[1]) 'cover' + getImageFileName(filelist[-1][1])[1])
image.Cover(os.path.join(filelist[-1][0], filelist[-1][1]), cover, options, tomeNumber) image.Cover(os.path.join(filelist[-1][0], filelist[-1][1]), cover, options, tomeNumber)
# Hack that force Panel View on at last one page
if lastfile and not options.panelviewused and 'Ko' not in options.profile \ if lastfile and not options.panelviewused and 'Ko' not in options.profile \
and options.profile not in ['K1', 'K2', 'KDX', 'OTHER']: and options.profile not in ['K1', 'K2', 'KDX', 'OTHER']:
filelist[-1] = buildHTML(lastfile[0], lastfile[1], lastfile[2], True) filelist[-1] = buildHTML(lastfile[0], lastfile[1], lastfile[2], True)
@@ -444,24 +448,17 @@ def buildEPUB(path, chapterNames, tomeNumber):
buildOPF(path, options.title, filelist, cover) buildOPF(path, options.title, filelist, cover)
def imgOptimization(img, opt, hqImage=None): def imgOptimization(img, opt):
if not img.fill: if not img.fill:
img.getImageFill() img.getImageFill()
if not opt.webtoon: if not opt.webtoon:
img.cropWhiteSpace() img.cropWhiteSpace()
if opt.cutpagenumbers and not opt.webtoon: if opt.cutpagenumbers and not opt.webtoon:
img.cutPageNumber() img.cutPageNumber()
img.optimizeImage() img.autocontrastImage()
if hqImage: img.resizeImage()
img.resizeImage(0) if not img.second and opt.panelview:
img.calculateBorder(hqImage, True) img.calculateBorder()
else:
img.resizeImage()
if opt.panelview:
if opt.quality == 0:
img.calculateBorder(img)
elif opt.quality == 1:
img.calculateBorder(img, True)
if opt.forcepng and not opt.forcecolor: if opt.forcepng and not opt.forcecolor:
img.quantizeImage() img.quantizeImage()
@@ -523,10 +520,6 @@ def imgFileProcessing(work):
opt = work[2] opt = work[2]
output = [] output = []
img = image.ComicPage(os.path.join(dirpath, afile), opt) img = image.ComicPage(os.path.join(dirpath, afile), opt)
if opt.quality == 2:
wipe = False
else:
wipe = True
if opt.nosplitrotate: if opt.nosplitrotate:
splitter = None splitter = None
else: else:
@@ -534,36 +527,30 @@ def imgFileProcessing(work):
if splitter is not None: if splitter is not None:
img0 = image.ComicPage(splitter[0], opt) img0 = image.ComicPage(splitter[0], opt)
imgOptimization(img0, opt) imgOptimization(img0, opt)
output.append(img0.saveToDir(dirpath)) if not img0.noHQ:
output.append(img0.saveToDir(dirpath))
img1 = image.ComicPage(splitter[1], opt) img1 = image.ComicPage(splitter[1], opt)
imgOptimization(img1, opt) imgOptimization(img1, opt)
output.append(img1.saveToDir(dirpath)) if not img1.noHQ:
if wipe: output.append(img1.saveToDir(dirpath))
output.append(img0.origFileName) output.extend([img.origFileName, img0.origFileName, img1.origFileName])
output.append(img1.origFileName)
if opt.quality == 2: if opt.quality == 2:
img0b = image.ComicPage(splitter[0], opt, img0.fill) output.extend([img0.origFileName, img1.origFileName])
imgOptimization(img0b, opt, img0) img0b = image.ComicPage(splitter[0], opt, img0)
imgOptimization(img0b, opt)
output.append(img0b.saveToDir(dirpath)) output.append(img0b.saveToDir(dirpath))
img1b = image.ComicPage(splitter[1], opt, img1.fill) img1b = image.ComicPage(splitter[1], opt, img1)
imgOptimization(img1b, opt, img1) imgOptimization(img1b, opt)
output.append(img1b.saveToDir(dirpath)) output.append(img1b.saveToDir(dirpath))
output.append(img0.origFileName)
output.append(img1.origFileName)
output.append(img.origFileName)
else: else:
output.append(img.origFileName)
imgOptimization(img, opt) imgOptimization(img, opt)
output.append(img.saveToDir(dirpath)) if not img.noHQ:
if wipe: output.append(img.saveToDir(dirpath))
output.append(img.origFileName)
if opt.quality == 2: if opt.quality == 2:
img2 = image.ComicPage(os.path.join(dirpath, afile), opt, img.fill) img2 = image.ComicPage(os.path.join(dirpath, afile), opt, img)
if img.rotated: imgOptimization(img2, opt)
img2.image = img2.image.rotate(90, Image.BICUBIC, True)
img2.rotated = True
imgOptimization(img2, opt, img)
output.append(img2.saveToDir(dirpath)) output.append(img2.saveToDir(dirpath))
output.append(img.origFileName)
return output return output
except Exception: except Exception:
return str(sys.exc_info()[1]) return str(sys.exc_info()[1])
@@ -575,7 +562,7 @@ def getWorkFolder(afile):
if os.path.isdir(afile): if os.path.isdir(afile):
workdir = mkdtemp('', 'KCC-TMP-') workdir = mkdtemp('', 'KCC-TMP-')
try: try:
os.rmdir(workdir) # needed for copytree() fails if dst already exists os.rmdir(workdir)
fullPath = os.path.join(workdir, 'OEBPS', 'Images') fullPath = os.path.join(workdir, 'OEBPS', 'Images')
if len(fullPath) > 240: if len(fullPath) > 240:
raise UserWarning("Path is too long.") raise UserWarning("Path is too long.")
@@ -906,20 +893,20 @@ def detectMargins(path):
yu = flag[2] yu = flag[2]
xr = flag[3] xr = flag[3]
yd = flag[4] yd = flag[4]
if xl != "0": if xl != "0.0":
xl = "-" + str(float(xl)/100) + "%" xl = "-" + xl + "%"
else: else:
xl = "0%" xl = "0%"
if xr != "0": if xr != "0.0":
xr = "-" + str(float(xr)/100) + "%" xr = "-" + xr + "%"
else: else:
xr = "0%" xr = "0%"
if yu != "0": if yu != "0.0":
yu = "-" + str(float(yu)/100) + "%" yu = "-" + yu + "%"
else: else:
yu = "0%" yu = "0%"
if yd != "0": if yd != "0.0":
yd = "-" + str(float(yd)/100) + "%" yd = "-" + yd + "%"
else: else:
yd = "0%" yd = "0%"
return xl, yu, xr, yd return xl, yu, xr, yd
@@ -1123,10 +1110,7 @@ def makeBook(source, qtGUI=None):
getComicInfo(os.path.join(path, "OEBPS", "Images"), source) getComicInfo(os.path.join(path, "OEBPS", "Images"), source)
detectCorruption(os.path.join(path, "OEBPS", "Images"), source) detectCorruption(os.path.join(path, "OEBPS", "Images"), source)
if options.webtoon: if options.webtoon:
if options.customheight > 0: comic2panel.main(['-y ' + str(image.ProfileData.Profiles[options.profile][1][1]), '-i', '-m', path], qtGUI)
comic2panel.main(['-y ' + str(options.customheight), '-i', '-m', path], qtGUI)
else:
comic2panel.main(['-y ' + str(image.ProfileData.Profiles[options.profile][1][1]), '-i', '-m', path], qtGUI)
if options.imgproc: if options.imgproc:
print("\nProcessing images...") print("\nProcessing images...")
if GUI: if GUI:

View File

@@ -98,7 +98,7 @@ class ProfileData:
class ComicPage: class ComicPage:
def __init__(self, source, options, fill=None): def __init__(self, source, options, original=None):
try: try:
self.profile_label, self.size, self.palette, self.gamma, self.panelviewsize = options.profileData self.profile_label, self.size, self.palette, self.gamma, self.panelviewsize = options.profileData
except KeyError: except KeyError:
@@ -107,58 +107,67 @@ class ComicPage:
self.filename = os.path.basename(self.origFileName) self.filename = os.path.basename(self.origFileName)
self.image = Image.open(source) self.image = Image.open(source)
self.image = self.image.convert('RGB') self.image = self.image.convert('RGB')
self.rotated = None
self.border = None
self.noHPV = None
self.noVPV = None
self.noPV = None
self.purge = False
self.hq = False
self.opt = options self.opt = options
if fill: if original:
self.fill = fill self.second = True
self.rotated = original.rotated
self.border = original.border
self.noHPV = original.noHPV
self.noVPV = original.noVPV
self.noPV = original.noPV
self.noHQ = original.noHQ
self.fill = original.fill
self.color = original.color
if self.rotated:
self.image = self.image.rotate(90, Image.BICUBIC, True)
self.opt.quality = 0
else: else:
self.second = False
self.rotated = None
self.border = None
self.noHPV = None
self.noVPV = None
self.noPV = None
self.fill = None self.fill = None
if options.webtoon: self.noHQ = False
self.color = True if options.webtoon:
else: self.color = True
self.color = self.isImageColor() else:
self.color = self.isImageColor()
def saveToDir(self, targetdir): def saveToDir(self, targetdir):
try: try:
if not self.purge: flags = []
flags = [] filename = os.path.join(targetdir, os.path.splitext(self.filename)[0]) + '-KCC'
filename = os.path.join(targetdir, os.path.splitext(self.filename)[0]) + '-KCC' if not self.opt.forcecolor and not self.opt.forcepng:
if not self.opt.forcecolor and not self.opt.forcepng: self.image = self.image.convert('L')
self.image = self.image.convert('L') if self.rotated:
if self.rotated: flags.append('Rotated')
flags.append('Rotated') if self.noPV:
if self.hq: flags.append('NoPanelView')
flags.append('HighQuality')
filename += '-HQ'
if self.noPV:
flags.append('NoPanelView')
else:
if self.noHPV:
flags.append('NoHorizontalPanelView')
if self.noVPV:
flags.append('NoVerticalPanelView')
if self.border:
flags.append('Margins-' + str(self.border[0]) + '-' + str(self.border[1]) + '-'
+ str(self.border[2]) + '-' + str(self.border[3]))
if self.opt.forcepng:
filename += '.png'
self.image.save(filename, 'PNG', optimize=1)
else:
filename += '.jpg'
self.image.save(filename, 'JPEG', optimize=1, quality=80)
return [md5Checksum(filename), flags]
else: else:
return None if self.noHPV:
flags.append('NoHorizontalPanelView')
if self.noVPV:
flags.append('NoVerticalPanelView')
if self.border:
flags.append('Margins-' + str(self.border[0]) + '-' + str(self.border[1]) + '-'
+ str(self.border[2]) + '-' + str(self.border[3]))
if self.fill != 'white':
flags.append('BlackFill')
if self.opt.quality == 2:
filename += '-HQ'
if self.opt.forcepng:
filename += '.png'
self.image.save(filename, 'PNG', optimize=1)
else:
filename += '.jpg'
self.image.save(filename, 'JPEG', optimize=1, quality=80)
return [md5Checksum(filename), flags]
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))
def optimizeImage(self): def autocontrastImage(self):
gamma = self.opt.gamma gamma = self.opt.gamma
if gamma < 0.1: if gamma < 0.1:
gamma = self.gamma gamma = self.gamma
@@ -180,80 +189,65 @@ class ComicPage:
# Quantize is deprecated but new function call it internally anyway... # Quantize is deprecated but new function call it internally anyway...
self.image = self.image.quantize(palette=palImg) self.image = self.image.quantize(palette=palImg)
def calculateBorderPercent(self, x, img, isWidth): def calculateBorder(self):
if isWidth: if self.noPV:
return int(round(float(x)/float(img.image.size[0]), 4) * 10000 * 1.5) self.border = [0.0, 0.0, 0.0, 0.0]
else:
return int(round(float(x)/float(img.image.size[1]), 4) * 10000 * 1.5)
def calculateBorder(self, sourceImage, isHQ=False):
if (isHQ and sourceImage.purge) or self.noPV:
self.border = [0, 0, 0, 0]
self.noPV = True
return return
if self.fill == 'white': if self.fill == 'white':
# Only already saved files can have P mode. So we can break color quantization. border = ImageChops.invert(self.image).getbbox()
if sourceImage.image.mode == 'P':
sourceImage.image = sourceImage.image.convert('RGB')
border = ImageChops.invert(sourceImage.image).getbbox()
else: else:
border = sourceImage.image.getbbox() border = self.image.getbbox()
if self.opt.quality == 2:
multiplier = 1.0
else:
multiplier = 1.5
if border is not None: if border is not None:
if isHQ: self.border = [round(float(border[0])/float(self.image.size[0])*150, 3),
multiplier = 1.0 round(float(border[1])/float(self.image.size[1])*150, 3),
else: round(float(self.image.size[0]-border[2])/float(self.image.size[0])*150, 3),
multiplier = 1.5 round(float(self.image.size[1]-border[3])/float(self.image.size[1])*150, 3)]
self.border = [self.calculateBorderPercent(border[0], sourceImage, True), if int((border[2] - border[0]) * multiplier) < self.size[0] + 10:
self.calculateBorderPercent(border[1], sourceImage, False),
self.calculateBorderPercent((sourceImage.image.size[0] - border[2]), sourceImage, True),
self.calculateBorderPercent((sourceImage.image.size[1] - border[3]), sourceImage, False)]
if int((border[2] - border[0]) * multiplier) < self.size[0]:
self.noHPV = True self.noHPV = True
if int((border[3] - border[1]) * multiplier) < self.size[1]: if int((border[3] - border[1]) * multiplier) < self.size[1] + 10:
self.noVPV = True self.noVPV = True
else: else:
self.border = [0, 0, 0, 0] self.border = [0.0, 0.0, 0.0, 0.0]
self.noHPV = True self.noHPV = True
self.noVPV = True self.noVPV = True
def resizeImage(self, qualityMode=None): def resizeImage(self):
upscale = self.opt.upscale if self.opt.bordersColor:
stretch = self.opt.stretch fill = self.opt.bordersColor
bordersColor = self.opt.bordersColor
if qualityMode is None:
qualityMode = self.opt.quality
if bordersColor:
fill = bordersColor
else: else:
fill = self.fill fill = self.fill
# Set target size # Set target size
if qualityMode == 0: if self.opt.quality == 0:
size = (self.size[0], self.size[1]) size = (self.size[0], self.size[1])
elif qualityMode == 1 and not stretch and not upscale and self.image.size[0] <=\ elif self.opt.quality == 1 and not self.opt.stretch and not self.opt.upscale and self.image.size[0] <=\
self.size[0] and self.image.size[1] <= self.size[1]: self.size[0] and self.image.size[1] <= self.size[1]:
size = (self.size[0], self.size[1]) size = (self.size[0], self.size[1])
elif qualityMode == 1: elif self.opt.quality == 1:
# Forcing upscale to make sure that margins will be not too big # Forcing upscale to make sure that margins will be not too big
if not stretch: if not self.opt.stretch:
upscale = True self.opt.upscale = True
size = (self.panelviewsize[0], self.panelviewsize[1]) size = (self.panelviewsize[0], self.panelviewsize[1])
elif qualityMode == 2 and not stretch and not upscale and self.image.size[0] <=\ elif self.opt.quality == 2 and not self.opt.stretch and not self.opt.upscale and self.image.size[0] <=\
self.size[0] and self.image.size[1] <= self.size[1]: self.size[0] and self.image.size[1] <= self.size[1]:
self.purge = True # HQ version will not be needed
return self.image self.noHQ = True
return
else: else:
self.hq = True
size = (self.panelviewsize[0], self.panelviewsize[1]) size = (self.panelviewsize[0], self.panelviewsize[1])
# If stretching is on - Resize without other considerations # If stretching is on - Resize without other considerations
if stretch: if self.opt.stretch:
if self.image.size[0] <= size[0] and self.image.size[1] <= size[1]: if self.image.size[0] <= size[0] and self.image.size[1] <= size[1]:
method = Image.BICUBIC method = Image.BICUBIC
else: else:
method = Image.LANCZOS method = Image.LANCZOS
self.image = self.image.resize(size, method) self.image = self.image.resize(size, method)
return self.image return
# If image is smaller than target resolution and upscale is off - Just expand it by adding margins # If image is smaller than target resolution and upscale is off - Just expand it by adding margins
if self.image.size[0] <= size[0] and self.image.size[1] <= size[1] and not upscale: if self.image.size[0] <= size[0] and self.image.size[1] <= size[1] and not self.opt.upscale:
borderw = int((size[0] - self.image.size[0]) / 2) borderw = int((size[0] - self.image.size[0]) / 2)
borderh = int((size[1] - self.image.size[1]) / 2) borderh = int((size[1] - self.image.size[1]) / 2)
# PV is disabled when source image is smaller than device screen and upscale is off # PV is disabled when source image is smaller than device screen and upscale is off
@@ -263,7 +257,7 @@ class ComicPage:
# Border can't be float so sometimes image might be 1px too small/large # Border can't be float so sometimes image might be 1px too small/large
if self.image.size[0] != size[0] or self.image.size[1] != size[1]: 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)) self.image = ImageOps.fit(self.image, size, method=Image.BICUBIC, centering=(0.5, 0.5))
return self.image return
# Otherwise - Upscale/Downscale # Otherwise - Upscale/Downscale
ratioDev = float(size[0]) / float(size[1]) ratioDev = float(size[0]) / float(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:
@@ -277,7 +271,7 @@ class ComicPage:
else: else:
method = Image.LANCZOS method = Image.LANCZOS
self.image = ImageOps.fit(self.image, size, method=method, centering=(0.5, 0.5)) self.image = ImageOps.fit(self.image, size, method=method, centering=(0.5, 0.5))
return self.image return
def splitPage(self, targetdir): def splitPage(self, targetdir):
width, height = self.image.size width, height = self.image.size
@@ -370,7 +364,6 @@ class ComicPage:
else: else:
diff = pageNumberCut1 diff = pageNumberCut1
self.image = self.image.crop((0, 0, widthImg, heightImg - diff)) self.image = self.image.crop((0, 0, widthImg, heightImg - diff))
return self.image
def cropWhiteSpace(self): def cropWhiteSpace(self):
if ImageChops.invert(self.image).getbbox() is not None: if ImageChops.invert(self.image).getbbox() is not None:
@@ -406,7 +399,6 @@ class ComicPage:
diff += delta diff += delta
diff -= delta diff -= delta
self.image = self.image.crop((0, 0, widthImg - diff, heightImg)) self.image = self.image.crop((0, 0, widthImg - diff, heightImg))
return self.image
def getImageHistogram(self, image): def getImageHistogram(self, image):
histogram = image.histogram() histogram = image.histogram()

View File

@@ -69,7 +69,7 @@ class MetadataParser:
stdout=PIPE, stderr=STDOUT, shell=True) stdout=PIPE, stderr=STDOUT, shell=True)
extracted = False extracted = False
for line in output.stdout: for line in output.stdout:
if b"Everything is Ok" in line: if b"Everything is Ok" in line or b"No files to process" in line:
extracted = True extracted = True
if not extracted: if not extracted:
rmtree(workdir) rmtree(workdir)

View File

@@ -20,11 +20,14 @@ import os
from hashlib import md5 from hashlib import md5
from html.parser import HTMLParser from html.parser import HTMLParser
from distutils.version import StrictVersion from distutils.version import StrictVersion
from scandir import walk
from time import sleep from time import sleep
from shutil import rmtree, move from shutil import rmtree, move
from tempfile import mkdtemp from tempfile import mkdtemp
from zipfile import ZipFile, ZIP_DEFLATED from zipfile import ZipFile, ZIP_DEFLATED
try:
from scandir import walk
except ImportError:
walk = None
class HTMLStripper(HTMLParser): class HTMLStripper(HTMLParser):