1
0
mirror of https://github.com/ciromattia/kcc synced 2026-05-10 09:42:25 +00:00

Compare commits

...

8 Commits

Author SHA1 Message Date
Alex Xu
b8b7926366 use release-signing 2025-03-10 09:04:55 -07:00
Alex Xu
92f3308e1c bump to 7.3.1 2025-03-10 08:59:18 -07:00
Alex Xu
9e87ccef4e Free code signing on Windows provided by SignPath.io, certificate by SignPath Foundation 2025-03-10 08:58:37 -07:00
Alex Xu
9a2a09eab9 memory optimization: store metadata in filenames, not a global dict (#853)
* remove options.imgMetadata and store metadata in filename

* make kcc much more memory efficient by removing imgMetadata and imgOld dicts
2025-03-09 16:52:07 -07:00
Alex Xu
88cf2fd21f fix sanitize-first bug 2025-03-09 16:50:15 -07:00
Vinh Quang Tran
7a3ed262b1 fix: disabled overriding title (#857) 2025-03-09 16:22:34 -07:00
Alex Xu
9e204aad76 rename images before processing to fix splitting issues (#852)
* rename images before processing to fix splitting issues

* import pathlib

* 9999 page limit
2025-03-07 07:59:04 -08:00
Alex Xu
e1f9d12676 add test-signing 2025-03-06 14:08:48 -08:00
5 changed files with 35 additions and 36 deletions

View File

@@ -46,11 +46,25 @@ jobs:
mv dist/windows/kcc.exe dist/windows/KCC_${version_built}.exe 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-unsigned-artifact
id: upload-unsigned-artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: windows-build name: windows-build
path: dist/windows/*.exe path: dist/windows/*.exe
- id: optional_step_id
uses: signpath/github-action-submit-signing-request@v1.1
with:
api-token: '${{ secrets.SIGNPATH_API_TOKEN }}'
organization-id: '1dc1bad6-4a8c-4f85-af30-5c5d3d392ea6'
project-slug: 'kcc'
signing-policy-slug: 'release-signing'
github-artifact-id: '${{ steps.upload-unsigned-artifact.outputs.artifact-id }}'
wait-for-completion: true
output-artifact-directory: 'dist/windows/signed/'
- name: Release - name: Release
uses: softprops/action-gh-release@v2 uses: softprops/action-gh-release@v2
if: startsWith(github.ref, 'refs/tags/') if: startsWith(github.ref, 'refs/tags/')
@@ -60,4 +74,4 @@ jobs:
files: | files: |
CHANGELOG.md CHANGELOG.md
LICENSE.txt LICENSE.txt
dist/windows/*.exe dist/windows/signed/*.exe

View File

@@ -33,6 +33,9 @@ If you find **KCC** valuable you can consider donating to the authors:
- Alex Xu (active 2023-Present) - Alex Xu (active 2023-Present)
- [![Donate PayPal](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/donate/?business=QFJVE7A6LCP6U&no_recurring=0&item_name=Kindle+Comic+Converter&currency_code=USD) - [![Donate PayPal](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/donate/?business=QFJVE7A6LCP6U&no_recurring=0&item_name=Kindle+Comic+Converter&currency_code=USD)
## Sponsors
- Free code signing on Windows provided by [SignPath.io](https://about.signpath.io/), certificate by [SignPath Foundation](https://signpath.org/)
## DOWNLOADS ## DOWNLOADS

View File

@@ -1,4 +1,4 @@
__version__ = '7.3.0' __version__ = '7.3.1'
__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

@@ -77,14 +77,14 @@ def main(argv=None):
return 0 return 0
def buildHTML(path, imgfile, imgfilepath): def buildHTML(path, imgfile):
filename = getImageFileName(imgfile) filename = getImageFileName(imgfile)
deviceres = options.profileData[1] deviceres = options.profileData[1]
if not options.noprocessing and "Rotated" in options.imgMetadata[imgfilepath]: if not options.noprocessing and "Rotated" in imgfile:
rotatedPage = True rotatedPage = True
else: else:
rotatedPage = False rotatedPage = False
if not options.noprocessing and "BlackBackground" in options.imgMetadata[imgfilepath]: if not options.noprocessing and "BlackBackground" in imgfile:
additionalStyle = 'background-color:#000000;' additionalStyle = 'background-color:#000000;'
else: else:
additionalStyle = '' additionalStyle = ''
@@ -517,7 +517,7 @@ def buildEPUB(path, chapternames, tomenumber, ischunked):
if not chapter: if not chapter:
chapterlist.append((dirpath.replace('Images', 'Text'), afile)) chapterlist.append((dirpath.replace('Images', 'Text'), afile))
chapter = True chapter = True
filelist.append(buildHTML(dirpath, afile, os.path.join(dirpath, afile))) filelist.append(buildHTML(dirpath, afile))
build_html_end = perf_counter() build_html_end = perf_counter()
print(f"buildHTML: {build_html_end - build_html_start} seconds") print(f"buildHTML: {build_html_end - build_html_start} seconds")
# Overwrite chapternames if tree is flat and ComicInfo.xml has bookmarks # Overwrite chapternames if tree is flat and ComicInfo.xml has bookmarks
@@ -540,7 +540,7 @@ def buildEPUB(path, chapternames, tomenumber, ischunked):
global_diff = 0 global_diff = 0
for x in range(0, pageid + cur_diff + 1): for x in range(0, pageid + cur_diff + 1):
if '-kcc-b' in filelist[x][1]: if '-KCC-B' in filelist[x][1]:
pageid += diff_delta pageid += diff_delta
global_diff += diff_delta global_diff += diff_delta
@@ -556,8 +556,6 @@ def imgDirectoryProcessing(path):
global workerPool, workerOutput global workerPool, workerOutput
workerPool = Pool(maxtasksperchild=100) workerPool = Pool(maxtasksperchild=100)
workerOutput = [] workerOutput = []
options.imgMetadata = {}
options.imgOld = []
work = [] work = []
pagenumber = 0 pagenumber = 0
for dirpath, _, filenames in os.walk(path): for dirpath, _, filenames in os.walk(path):
@@ -580,9 +578,6 @@ def imgDirectoryProcessing(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][0], workerOutput[0][1]) raise RuntimeError("One of workers crashed. Cause: " + workerOutput[0][0], workerOutput[0][1])
for file in options.imgOld:
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.")
@@ -592,11 +587,7 @@ def imgFileProcessingTick(output):
if isinstance(output, tuple): if isinstance(output, tuple):
workerOutput.append(output) workerOutput.append(output)
workerPool.terminate() workerPool.terminate()
else:
for page in output:
if page is not None:
options.imgMetadata[page[0]] = page[1]
options.imgOld.append(page[2])
if GUI: if GUI:
GUI.progressBarTick.emit('tick') GUI.progressBarTick.emit('tick')
if not GUI.conversionAlive: if not GUI.conversionAlive:
@@ -740,9 +731,7 @@ def getComicInfo(path, originalpath):
except Exception: except Exception:
os.remove(xmlPath) os.remove(xmlPath)
return return
if xml.data['Title']: if defaultTitle:
options.title = hescape(xml.data['Title'])
elif defaultTitle:
if xml.data['Series']: if xml.data['Series']:
options.title = hescape(xml.data['Series']) options.title = hescape(xml.data['Series'])
if xml.data['Volume']: if xml.data['Volume']:
@@ -801,19 +790,14 @@ def sanitizeTree(filetree):
for name in files: for name in files:
splitname = os.path.splitext(name) splitname = os.path.splitext(name)
# file needs kcc at front AND back to avoid renaming issues # 9999 page limit
slugified = f'kcc-{page:04}' slugified = f'kcc-{page:04}'
page += 1 page += 1
for suffix in '-KCC', '-KCC-A', '-KCC-B', '-KCC-C':
if splitname[0].endswith(suffix):
slugified += suffix.lower()
break
newKey = os.path.join(root, slugified + splitname[1]) newKey = os.path.join(root, slugified + splitname[1])
key = os.path.join(root, name) key = os.path.join(root, name)
if key != newKey: if key != newKey:
os.replace(key, newKey) os.replace(key, newKey)
options.imgMetadata[newKey] = options.imgMetadata.pop(key)
for i, name in enumerate(dirs): for i, name in enumerate(dirs):
tmpName = name tmpName = name
slugified = slugify(name) slugified = slugify(name)
@@ -825,10 +809,6 @@ def sanitizeTree(filetree):
if key != newKey: if key != newKey:
os.replace(key, newKey) os.replace(key, newKey)
dirs[i] = newKey dirs[i] = newKey
existingImgPathKeys = list(options.imgMetadata.keys())
for imgPath in existingImgPathKeys:
if imgPath.startswith(key):
options.imgMetadata[newKey + imgPath.removeprefix(key)] = options.imgMetadata.pop(imgPath)
return chapterNames return chapterNames
@@ -1198,6 +1178,7 @@ def makeBook(source, qtgui=None):
print("Checking images...") print("Checking images...")
getComicInfo(os.path.join(path, "OEBPS", "Images"), source) getComicInfo(os.path.join(path, "OEBPS", "Images"), source)
detectSuboptimalProcessing(os.path.join(path, "OEBPS", "Images"), source) detectSuboptimalProcessing(os.path.join(path, "OEBPS", "Images"), source)
chapterNames = sanitizeTree(os.path.join(path, 'OEBPS', 'Images'))
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)
@@ -1210,7 +1191,6 @@ def makeBook(source, qtgui=None):
imgDirectoryProcessing(os.path.join(path, "OEBPS", "Images")) imgDirectoryProcessing(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'))
if options.batchsplit > 0: if options.batchsplit > 0:
tomes = chunk_directory(path) tomes = chunk_directory(path)
else: else:

View File

@@ -20,6 +20,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import io import io
import os import os
from pathlib import Path
import mozjpeg_lossless_optimization import mozjpeg_lossless_optimization
from PIL import Image, ImageOps, ImageStat, ImageChops, ImageFilter from PIL import Image, ImageOps, ImageStat, ImageChops, ImageFilter
from .page_number_crop_alg import get_bbox_crop_margin_page_number, get_bbox_crop_margin from .page_number_crop_alg import get_bbox_crop_margin_page_number, get_bbox_crop_margin
@@ -298,13 +299,12 @@ class ComicPage:
def saveToDir(self): def saveToDir(self):
try: try:
flags = []
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') self.targetPath += '-Rotated'
if self.fill != 'white': if self.fill != 'white':
flags.append('BlackBackground') self.targetPath += '-BlackBackground'
if self.opt.forcepng: if self.opt.forcepng:
self.image.info["transparency"] = None self.image.info["transparency"] = None
self.targetPath += '.png' self.targetPath += '.png'
@@ -320,7 +320,9 @@ class ComicPage:
output_jpeg_file.write(output_jpeg_bytes) output_jpeg_file.write(output_jpeg_bytes)
else: else:
self.image.save(self.targetPath, 'JPEG', optimize=1, quality=85) self.image.save(self.targetPath, 'JPEG', optimize=1, quality=85)
return [self.targetPath, flags, self.orgPath] if os.path.isfile(self.orgPath):
os.remove(self.orgPath)
return Path(self.targetPath).name
except IOError as err: except IOError as err:
raise RuntimeError('Cannot save image. ' + str(err)) raise RuntimeError('Cannot save image. ' + str(err))