1
0
mirror of https://github.com/ciromattia/kcc synced 2026-04-18 15:08:48 +00:00

Compare commits

..

10 Commits

Author SHA1 Message Date
Alex Xu
c6f491d27e bump 7.2.3 2025-02-28 20:44:45 -08:00
Alex Xu
c9ed3feef1 deprioritize tar (#842) 2025-02-28 20:17:41 -08:00
Alex Xu
be147fe7e5 saves several seconds per file (#841) 2025-02-28 20:11:11 -08:00
utopiafallen
62ffa2bc80 Improve processing performance by partially undoing "Refactored detection of corrupted files"
Commit f952634971 moved image corruption detection out from the ComicPage constructor and into a standalone detectCorruption() function. This led to a performance regression because now corruption detection happens in a single thread when it used to be distributed across worker threads, and because a source image is now loaded twice in memory: once during corruption detection and once when actually going to process the image.

Image file corruption detection is now back inside the ComicPage constructor and the extra load() has been removed because the convert() call will automatically invoke load() and most likely throw the same exceptions.
2025-02-28 19:49:13 -08:00
Alex Xu
11186d07c0 fix file splitting/chunking for real in certain situations (#839)
* fix file splitting without ComicInfo.xml

* remove dead var
2025-02-28 19:19:11 -08:00
Alex Xu
4b3cd6882a Revert "Build windows GUI version normally without docker" (#835)
* Revert "remove GUI windows docker"

This reverts commit 4fc5cc9dfb.

* build windows gui version with docker

* cffi

* bump 7.2.2
2025-02-25 08:57:40 -08:00
Alex Xu
b35a2baf05 bump 7.2.1 2025-02-20 18:24:53 -08:00
Alex Xu
11a395e983 fix pdf workdir (#830) 2025-02-20 18:24:06 -08:00
Alex Xu
2e39a8c227 add jpeg2000 (#828) 2025-02-19 16:29:26 -08:00
Alex Xu
02535421a0 rename batch to chunk (#826) 2025-02-12 15:31:40 -08:00
7 changed files with 68 additions and 22 deletions

View File

@@ -25,6 +25,11 @@ jobs:
# - name: build binary # - name: build binary
# run: | # run: |
# pyi-makespec -F -i icons\\comic2ebook.ico -n KCC_test -w --noupx kcc.py # pyi-makespec -F -i icons\\comic2ebook.ico -n KCC_test -w --noupx kcc.py
- name: Package Application
uses: JackMcKew/pyinstaller-action-windows@main
with:
path: .
spec: ./kcc.spec
- name: Package Application - name: Package Application
uses: JackMcKew/pyinstaller-action-windows@main uses: JackMcKew/pyinstaller-action-windows@main
with: with:
@@ -38,6 +43,7 @@ jobs:
- name: rename binaries - name: rename binaries
run: | run: |
version_built=$(cat kindlecomicconverter/__init__.py | grep version | awk '{print $3}' | sed "s/[^.0-9b]//g") version_built=$(cat kindlecomicconverter/__init__.py | grep version | awk '{print $3}' | sed "s/[^.0-9b]//g")
mv dist/windows/kcc.exe dist/windows/KCC_${version_built}.exe
mv dist/windows/kcc-c2e.exe dist/windows/KCC_c2e_${version_built}.exe mv dist/windows/kcc-c2e.exe dist/windows/KCC_c2e_${version_built}.exe
mv dist/windows/kcc-c2p.exe dist/windows/KCC_c2p_${version_built}.exe mv dist/windows/kcc-c2p.exe dist/windows/KCC_c2p_${version_built}.exe
- name: upload build - name: upload build

39
kcc.spec Normal file
View File

@@ -0,0 +1,39 @@
# -*- mode: python ; coding: utf-8 -*-
block_cipher = None
a = Analysis(['kcc.py'],
pathex=['.'],
binaries=[],
datas=[],
hiddenimports=['_cffi_backend'],
hookspath=[],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False)
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
exe = EXE(pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
[],
name='kcc',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=False,
upx_exclude=[],
runtime_tmpdir=None,
console=False,
disable_windowed_traceback=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None , icon='icons\\comic2ebook.ico')

View File

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

View File

@@ -645,6 +645,7 @@ def getWorkFolder(afile):
if afile.lower().endswith('.pdf'): if afile.lower().endswith('.pdf'):
pdf = pdfjpgextract.PdfJpgExtract(afile) pdf = pdfjpgextract.PdfJpgExtract(afile)
path, njpg = pdf.extract() path, njpg = pdf.extract()
workdir = path
sanitizePermissions(path) sanitizePermissions(path)
if njpg == 0: if njpg == 0:
rmtree(path, True) rmtree(path, True)
@@ -656,16 +657,14 @@ def getWorkFolder(afile):
path = cbx.extract(workdir) path = cbx.extract(workdir)
sanitizePermissions(path) sanitizePermissions(path)
tdir = os.listdir(workdir) tdir = os.listdir(workdir)
is_nested_single_dir = False
if len(tdir) == 2 and 'ComicInfo.xml' in tdir: if len(tdir) == 2 and 'ComicInfo.xml' in tdir:
tdir.remove('ComicInfo.xml') tdir.remove('ComicInfo.xml')
is_nested_single_dir = os.path.isdir(os.path.join(workdir, tdir[0])) if os.path.isdir(os.path.join(workdir, tdir[0])):
if is_nested_single_dir:
os.replace( os.replace(
os.path.join(workdir, 'ComicInfo.xml'), os.path.join(workdir, 'ComicInfo.xml'),
os.path.join(workdir, tdir[0], 'ComicInfo.xml') os.path.join(workdir, tdir[0], 'ComicInfo.xml')
) )
if len(tdir) == 1 and is_nested_single_dir: if len(tdir) == 1 and os.path.isdir(os.path.join(workdir, tdir[0])):
path = os.path.join(workdir, tdir[0]) path = os.path.join(workdir, tdir[0])
except OSError as e: except OSError as e:
rmtree(workdir, True) rmtree(workdir, True)
@@ -673,8 +672,7 @@ def getWorkFolder(afile):
else: else:
raise UserWarning("Failed to open source file/directory.") raise UserWarning("Failed to open source file/directory.")
newpath = mkdtemp('', 'KCC-', os.path.dirname(afile)) newpath = mkdtemp('', 'KCC-', os.path.dirname(afile))
copytree(path, os.path.join(newpath, 'OEBPS', 'Images')) os.renames(path, os.path.join(newpath, 'OEBPS', 'Images'))
rmtree(workdir, True)
return newpath return newpath
@@ -828,12 +826,11 @@ def sanitizePermissions(filetree):
os.chmod(os.path.join(root, name), S_IWRITE | S_IREAD | S_IEXEC) os.chmod(os.path.join(root, name), S_IWRITE | S_IREAD | S_IEXEC)
def batch_directory(path): def chunk_directory(path):
level = -1 level = -1
for root, _, files in os.walk(os.path.join(path, 'OEBPS', 'Images')): for root, _, files in os.walk(os.path.join(path, 'OEBPS', 'Images')):
for f in files: for f in files:
if f.endswith('.jpg') or f.endswith('.jpeg') or f.endswith('.png') or f.endswith('.gif') or \ if getImageFileName(f):
f.endswith('.webp'):
newLevel = os.path.join(root, f).replace(os.path.join(path, 'OEBPS', 'Images'), '').count(os.sep) newLevel = os.path.join(root, f).replace(os.path.join(path, 'OEBPS', 'Images'), '').count(os.sep)
if level != -1 and level != newLevel: if level != -1 and level != newLevel:
level = 0 level = 0
@@ -842,16 +839,16 @@ def batch_directory(path):
level = newLevel level = newLevel
if level > 0: if level > 0:
parent = pathlib.Path(path).parent parent = pathlib.Path(path).parent
batcher = batch_process(os.path.join(path, 'OEBPS', 'Images'), level, parent) chunker = chunk_process(os.path.join(path, 'OEBPS', 'Images'), level, parent)
path = [path] path = [path]
for tome in batcher: for tome in chunker:
path.append(tome) path.append(tome)
return path return path
else: else:
raise UserWarning('Unsupported directory structure.') raise UserWarning('Unsupported directory structure.')
def batch_process(path, mode, parent): def chunk_process(path, mode, parent):
output = [] output = []
currentSize = 0 currentSize = 0
currentTarget = path currentTarget = path
@@ -890,8 +887,7 @@ def batch_process(path, mode, parent):
firstTome = False firstTome = False
return output return output
def detectSuboptimalProcessing(tmppath, orgpath):
def detectCorruption(tmppath, orgpath):
imageNumber = 0 imageNumber = 0
imageSmaller = 0 imageSmaller = 0
alreadyProcessed = False alreadyProcessed = False
@@ -907,9 +903,6 @@ def detectCorruption(tmppath, orgpath):
raise RuntimeError('Image file %s is corrupted.' % pathOrg) raise RuntimeError('Image file %s is corrupted.' % pathOrg)
try: try:
img = Image.open(path) img = Image.open(path)
img.verify()
img = Image.open(path)
img.load()
imageNumber += 1 imageNumber += 1
if options.profileData[1][0] > img.size[0] and options.profileData[1][1] > img.size[1]: if options.profileData[1][0] > img.size[0] and options.profileData[1][1] > img.size[1]:
imageSmaller += 1 imageSmaller += 1
@@ -1181,7 +1174,7 @@ def makeBook(source, qtgui=None):
path = getWorkFolder(source) path = getWorkFolder(source)
print("Checking images...") print("Checking images...")
getComicInfo(os.path.join(path, "OEBPS", "Images"), source) getComicInfo(os.path.join(path, "OEBPS", "Images"), source)
detectCorruption(os.path.join(path, "OEBPS", "Images"), source) detectSuboptimalProcessing(os.path.join(path, "OEBPS", "Images"), source)
if options.webtoon: if options.webtoon:
y = image.ProfileData.Profiles[options.profile][1][1] y = image.ProfileData.Profiles[options.profile][1][1]
comic2panel.main(['-y ' + str(y), '-i', '-m', path], qtgui) comic2panel.main(['-y ' + str(y), '-i', '-m', path], qtgui)
@@ -1196,7 +1189,7 @@ def makeBook(source, qtgui=None):
GUI.progressBarTick.emit('1') GUI.progressBarTick.emit('1')
chapterNames = sanitizeTree(os.path.join(path, 'OEBPS', 'Images')) chapterNames = sanitizeTree(os.path.join(path, 'OEBPS', 'Images'))
if options.batchsplit > 0: if options.batchsplit > 0:
tomes = batch_directory(path) tomes = chunk_directory(path)
else: else:
tomes = [path] tomes = [path]
filepath = [] filepath = []

View File

@@ -76,6 +76,8 @@ class ComicArchive:
['unar', self.filepath, '-f', '-o', targetdir] ['unar', self.filepath, '-f', '-o', targetdir]
) )
extraction_commands.reverse()
if distro.id() == 'fedora' or distro.like() == 'fedora': if distro.id() == 'fedora' or distro.like() == 'fedora':
extraction_commands.append( extraction_commands.append(
['unrar', 'x', '-y', '-x__MACOSX', '-x.DS_Store', '-xthumbs.db', '-xThumbs.db', self.filepath, targetdir] ['unrar', 'x', '-y', '-x__MACOSX', '-x.DS_Store', '-xthumbs.db', '-xThumbs.db', self.filepath, targetdir]

View File

@@ -141,7 +141,13 @@ class ComicPageParser:
self.source = source self.source = source
self.size = self.opt.profileData[1] self.size = self.opt.profileData[1]
self.payload = [] self.payload = []
self.image = Image.open(os.path.join(source[0], source[1])).convert('RGB')
# Detect corruption in source image, let caller catch any exceptions triggered.
srcImgPath = os.path.join(source[0], source[1])
self.image = Image.open(srcImgPath)
self.image.verify()
self.image = Image.open(srcImgPath).convert('RGB')
self.color = self.colorCheck() self.color = self.colorCheck()
self.fill = self.fillCheck() self.fill = self.fillCheck()
# backwards compatibility for Pillow >9.1.0 # backwards compatibility for Pillow >9.1.0

View File

@@ -49,7 +49,7 @@ class HTMLStripper(HTMLParser):
def getImageFileName(imgfile): def getImageFileName(imgfile):
name, ext = os.path.splitext(imgfile) name, ext = os.path.splitext(imgfile)
ext = ext.lower() ext = ext.lower()
if (name.startswith('.') and len(name) == 1) or ext not in ['.png', '.jpg', '.jpeg', '.gif', '.webp']: if (name.startswith('.') and len(name) == 1) or ext not in ['.png', '.jpg', '.jpeg', '.gif', '.webp', '.jp2', '.j2k', '.jpx']:
return None return None
return [name, ext] return [name, ext]