mirror of
https://github.com/ciromattia/kcc
synced 2026-04-20 16:09:05 +00:00
Compare commits
8 Commits
revert-845
...
v7.3.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b8b7926366 | ||
|
|
92f3308e1c | ||
|
|
9e87ccef4e | ||
|
|
9a2a09eab9 | ||
|
|
88cf2fd21f | ||
|
|
7a3ed262b1 | ||
|
|
9e204aad76 | ||
|
|
e1f9d12676 |
@@ -46,11 +46,25 @@ jobs:
|
||||
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-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
|
||||
with:
|
||||
name: windows-build
|
||||
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
|
||||
uses: softprops/action-gh-release@v2
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
@@ -60,4 +74,4 @@ jobs:
|
||||
files: |
|
||||
CHANGELOG.md
|
||||
LICENSE.txt
|
||||
dist/windows/*.exe
|
||||
dist/windows/signed/*.exe
|
||||
|
||||
@@ -33,6 +33,9 @@ If you find **KCC** valuable you can consider donating to the authors:
|
||||
- Alex Xu (active 2023-Present)
|
||||
- [](https://www.paypal.com/donate/?business=QFJVE7A6LCP6U&no_recurring=0&item_name=Kindle+Comic+Converter¤cy_code=USD)
|
||||
|
||||
## Sponsors
|
||||
|
||||
- Free code signing on Windows provided by [SignPath.io](https://about.signpath.io/), certificate by [SignPath Foundation](https://signpath.org/)
|
||||
|
||||
## DOWNLOADS
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
__version__ = '7.3.0'
|
||||
__version__ = '7.3.1'
|
||||
__license__ = 'ISC'
|
||||
__copyright__ = '2012-2022, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>, darodi'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
@@ -40,7 +40,7 @@ from subprocess import STDOUT, PIPE
|
||||
from psutil import virtual_memory, disk_usage
|
||||
from html import escape as hescape
|
||||
|
||||
from .shared import available_archive_tools, md5Checksum, getImageFileName, walkSort, walkLevel, sanitizeTrace, subprocess_run
|
||||
from .shared import available_archive_tools, getImageFileName, walkSort, walkLevel, sanitizeTrace, subprocess_run
|
||||
from . import comic2panel
|
||||
from . import image
|
||||
from . import comicarchive
|
||||
@@ -77,15 +77,14 @@ def main(argv=None):
|
||||
return 0
|
||||
|
||||
|
||||
def buildHTML(path, imgfile, imgfilepath):
|
||||
imgfilepath = md5Checksum(imgfilepath)
|
||||
def buildHTML(path, imgfile):
|
||||
filename = getImageFileName(imgfile)
|
||||
deviceres = options.profileData[1]
|
||||
if not options.noprocessing and "Rotated" in options.imgMetadata[imgfilepath]:
|
||||
if not options.noprocessing and "Rotated" in imgfile:
|
||||
rotatedPage = True
|
||||
else:
|
||||
rotatedPage = False
|
||||
if not options.noprocessing and "BlackBackground" in options.imgMetadata[imgfilepath]:
|
||||
if not options.noprocessing and "BlackBackground" in imgfile:
|
||||
additionalStyle = 'background-color:#000000;'
|
||||
else:
|
||||
additionalStyle = ''
|
||||
@@ -425,7 +424,6 @@ def buildOPF(dstdir, title, filelist, cover=None):
|
||||
"</container>"])
|
||||
f.close()
|
||||
|
||||
|
||||
def buildEPUB(path, chapternames, tomenumber, ischunked):
|
||||
filelist = []
|
||||
chapterlist = []
|
||||
@@ -506,6 +504,7 @@ def buildEPUB(path, chapternames, tomenumber, ischunked):
|
||||
"display: none;\n",
|
||||
"}\n"])
|
||||
f.close()
|
||||
build_html_start = perf_counter()
|
||||
for dirpath, dirnames, filenames in os.walk(os.path.join(path, 'OEBPS', 'Images')):
|
||||
chapter = False
|
||||
dirnames, filenames = walkSort(dirnames, filenames)
|
||||
@@ -515,10 +514,12 @@ def buildEPUB(path, chapternames, tomenumber, ischunked):
|
||||
'cover' + getImageFileName(afile)[1])
|
||||
options.covers.append((image.Cover(os.path.join(dirpath, afile), cover, options,
|
||||
tomenumber), options.uuid))
|
||||
filelist.append(buildHTML(dirpath, afile, os.path.join(dirpath, afile)))
|
||||
if not chapter:
|
||||
chapterlist.append((dirpath.replace('Images', 'Text'), filelist[-1][1]))
|
||||
chapterlist.append((dirpath.replace('Images', 'Text'), afile))
|
||||
chapter = True
|
||||
filelist.append(buildHTML(dirpath, afile))
|
||||
build_html_end = perf_counter()
|
||||
print(f"buildHTML: {build_html_end - build_html_start} seconds")
|
||||
# Overwrite chapternames if tree is flat and ComicInfo.xml has bookmarks
|
||||
if not chapternames and options.chapters and not ischunked:
|
||||
chapterlist = []
|
||||
@@ -539,7 +540,7 @@ def buildEPUB(path, chapternames, tomenumber, ischunked):
|
||||
global_diff = 0
|
||||
|
||||
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
|
||||
global_diff += diff_delta
|
||||
|
||||
@@ -555,8 +556,6 @@ def imgDirectoryProcessing(path):
|
||||
global workerPool, workerOutput
|
||||
workerPool = Pool(maxtasksperchild=100)
|
||||
workerOutput = []
|
||||
options.imgMetadata = {}
|
||||
options.imgOld = []
|
||||
work = []
|
||||
pagenumber = 0
|
||||
for dirpath, _, filenames in os.walk(path):
|
||||
@@ -566,19 +565,19 @@ def imgDirectoryProcessing(path):
|
||||
if GUI:
|
||||
GUI.progressBarTick.emit(str(pagenumber))
|
||||
if len(work) > 0:
|
||||
img_processing_start = perf_counter()
|
||||
for i in work:
|
||||
workerPool.apply_async(func=imgFileProcessing, args=(i,), callback=imgFileProcessingTick)
|
||||
workerPool.close()
|
||||
workerPool.join()
|
||||
img_processing_end = perf_counter()
|
||||
print(f"imgFileProcessing: {img_processing_end - img_processing_start} seconds")
|
||||
if GUI and not GUI.conversionAlive:
|
||||
rmtree(os.path.join(path, '..', '..'), True)
|
||||
raise UserWarning("Conversion interrupted.")
|
||||
if len(workerOutput) > 0:
|
||||
rmtree(os.path.join(path, '..', '..'), True)
|
||||
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:
|
||||
rmtree(os.path.join(path, '..', '..'), True)
|
||||
raise UserWarning("Source directory is empty.")
|
||||
@@ -588,11 +587,7 @@ def imgFileProcessingTick(output):
|
||||
if isinstance(output, tuple):
|
||||
workerOutput.append(output)
|
||||
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:
|
||||
GUI.progressBarTick.emit('tick')
|
||||
if not GUI.conversionAlive:
|
||||
@@ -736,9 +731,7 @@ def getComicInfo(path, originalpath):
|
||||
except Exception:
|
||||
os.remove(xmlPath)
|
||||
return
|
||||
if xml.data['Title']:
|
||||
options.title = hescape(xml.data['Title'])
|
||||
elif defaultTitle:
|
||||
if defaultTitle:
|
||||
if xml.data['Series']:
|
||||
options.title = hescape(xml.data['Series'])
|
||||
if xml.data['Volume']:
|
||||
@@ -797,13 +790,9 @@ def sanitizeTree(filetree):
|
||||
for name in files:
|
||||
splitname = os.path.splitext(name)
|
||||
|
||||
# file needs kcc at front AND back to avoid renaming issues
|
||||
# 9999 page limit
|
||||
slugified = f'kcc-{page:04}'
|
||||
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])
|
||||
key = os.path.join(root, name)
|
||||
@@ -1189,6 +1178,7 @@ def makeBook(source, qtgui=None):
|
||||
print("Checking images...")
|
||||
getComicInfo(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:
|
||||
y = image.ProfileData.Profiles[options.profile][1][1]
|
||||
comic2panel.main(['-y ' + str(y), '-i', '-m', path], qtgui)
|
||||
@@ -1201,7 +1191,6 @@ def makeBook(source, qtgui=None):
|
||||
imgDirectoryProcessing(os.path.join(path, "OEBPS", "Images"))
|
||||
if GUI:
|
||||
GUI.progressBarTick.emit('1')
|
||||
chapterNames = sanitizeTree(os.path.join(path, 'OEBPS', 'Images'))
|
||||
if options.batchsplit > 0:
|
||||
tomes = chunk_directory(path)
|
||||
else:
|
||||
|
||||
@@ -20,9 +20,9 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import io
|
||||
import os
|
||||
from pathlib import Path
|
||||
import mozjpeg_lossless_optimization
|
||||
from PIL import Image, ImageOps, ImageStat, ImageChops, ImageFilter
|
||||
from .shared import md5Checksum
|
||||
from .page_number_crop_alg import get_bbox_crop_margin_page_number, get_bbox_crop_margin
|
||||
from .inter_panel_crop_alg import crop_empty_inter_panel
|
||||
|
||||
@@ -299,13 +299,12 @@ class ComicPage:
|
||||
|
||||
def saveToDir(self):
|
||||
try:
|
||||
flags = []
|
||||
if not self.opt.forcecolor and not self.opt.forcepng:
|
||||
self.image = self.image.convert('L')
|
||||
if self.rotated:
|
||||
flags.append('Rotated')
|
||||
self.targetPath += '-Rotated'
|
||||
if self.fill != 'white':
|
||||
flags.append('BlackBackground')
|
||||
self.targetPath += '-BlackBackground'
|
||||
if self.opt.forcepng:
|
||||
self.image.info["transparency"] = None
|
||||
self.targetPath += '.png'
|
||||
@@ -321,7 +320,9 @@ class ComicPage:
|
||||
output_jpeg_file.write(output_jpeg_bytes)
|
||||
else:
|
||||
self.image.save(self.targetPath, 'JPEG', optimize=1, quality=85)
|
||||
return [md5Checksum(self.targetPath), flags, self.orgPath]
|
||||
if os.path.isfile(self.orgPath):
|
||||
os.remove(self.orgPath)
|
||||
return Path(self.targetPath).name
|
||||
except IOError as err:
|
||||
raise RuntimeError('Cannot save image. ' + str(err))
|
||||
|
||||
|
||||
@@ -75,16 +75,6 @@ def walkLevel(some_dir, level=1):
|
||||
del dirs[:]
|
||||
|
||||
|
||||
def md5Checksum(fpath):
|
||||
with open(fpath, 'rb') as fh:
|
||||
m = md5()
|
||||
while True:
|
||||
data = fh.read(8192)
|
||||
if not data:
|
||||
break
|
||||
m.update(data)
|
||||
return m.hexdigest()
|
||||
|
||||
|
||||
def sanitizeTrace(traceback):
|
||||
return ''.join(format_tb(traceback))\
|
||||
|
||||
Reference in New Issue
Block a user