mirror of
https://github.com/ciromattia/kcc
synced 2026-04-30 21:09:04 +00:00
Compare commits
13 Commits
revert-845
...
v7.3.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
187475a424 | ||
|
|
88fd54e2ba | ||
|
|
b23b67bbbe | ||
|
|
9992d895cf | ||
|
|
561951a349 | ||
|
|
b8b7926366 | ||
|
|
92f3308e1c | ||
|
|
9e87ccef4e | ||
|
|
9a2a09eab9 | ||
|
|
88cf2fd21f | ||
|
|
7a3ed262b1 | ||
|
|
9e204aad76 | ||
|
|
e1f9d12676 |
@@ -25,11 +25,6 @@ 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:
|
||||||
@@ -43,14 +38,27 @@ 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-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 +68,4 @@ jobs:
|
|||||||
files: |
|
files: |
|
||||||
CHANGELOG.md
|
CHANGELOG.md
|
||||||
LICENSE.txt
|
LICENSE.txt
|
||||||
dist/windows/*.exe
|
dist/windows/signed/*.exe
|
||||||
|
|||||||
15
.github/workflows/package-windows.yml
vendored
15
.github/workflows/package-windows.yml
vendored
@@ -41,11 +41,22 @@ jobs:
|
|||||||
- name: build binary
|
- name: build binary
|
||||||
run: |
|
run: |
|
||||||
python setup.py build_binary
|
python setup.py build_binary
|
||||||
- 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/*.exe
|
path: dist/*.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/')
|
||||||
@@ -55,4 +66,4 @@ jobs:
|
|||||||
files: |
|
files: |
|
||||||
CHANGELOG.md
|
CHANGELOG.md
|
||||||
LICENSE.txt
|
LICENSE.txt
|
||||||
dist/*.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)
|
- Alex Xu (active 2023-Present)
|
||||||
- [](https://www.paypal.com/donate/?business=QFJVE7A6LCP6U&no_recurring=0&item_name=Kindle+Comic+Converter¤cy_code=USD)
|
- [](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
|
## DOWNLOADS
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
__version__ = '7.3.0'
|
__version__ = '7.3.2'
|
||||||
__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'
|
||||||
|
|||||||
@@ -78,13 +78,14 @@ def main(argv=None):
|
|||||||
|
|
||||||
|
|
||||||
def buildHTML(path, imgfile, imgfilepath):
|
def buildHTML(path, imgfile, imgfilepath):
|
||||||
|
key = pathlib.Path(imgfilepath).name
|
||||||
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 options.imgMetadata[key]:
|
||||||
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 options.imgMetadata[key]:
|
||||||
additionalStyle = 'background-color:#000000;'
|
additionalStyle = 'background-color:#000000;'
|
||||||
else:
|
else:
|
||||||
additionalStyle = ''
|
additionalStyle = ''
|
||||||
@@ -215,7 +216,7 @@ def buildNCX(dstdir, title, chapters, chapternames):
|
|||||||
folder = chapter[0].replace(os.path.join(dstdir, 'OEBPS'), '').lstrip('/').lstrip('\\\\')
|
folder = chapter[0].replace(os.path.join(dstdir, 'OEBPS'), '').lstrip('/').lstrip('\\\\')
|
||||||
filename = getImageFileName(os.path.join(folder, chapter[1]))
|
filename = getImageFileName(os.path.join(folder, chapter[1]))
|
||||||
navID = folder.replace('/', '_').replace('\\', '_')
|
navID = folder.replace('/', '_').replace('\\', '_')
|
||||||
if options.chapters:
|
if options.comicinfo_chapters:
|
||||||
title = chapternames[chapter[1]]
|
title = chapternames[chapter[1]]
|
||||||
navID = filename[0].replace('/', '_').replace('\\', '_')
|
navID = filename[0].replace('/', '_').replace('\\', '_')
|
||||||
elif os.path.basename(folder) != "Text":
|
elif os.path.basename(folder) != "Text":
|
||||||
@@ -243,7 +244,7 @@ def buildNAV(dstdir, title, chapters, chapternames):
|
|||||||
for chapter in chapters:
|
for chapter in chapters:
|
||||||
folder = chapter[0].replace(os.path.join(dstdir, 'OEBPS'), '').lstrip('/').lstrip('\\\\')
|
folder = chapter[0].replace(os.path.join(dstdir, 'OEBPS'), '').lstrip('/').lstrip('\\\\')
|
||||||
filename = getImageFileName(os.path.join(folder, chapter[1]))
|
filename = getImageFileName(os.path.join(folder, chapter[1]))
|
||||||
if options.chapters:
|
if options.comicinfo_chapters:
|
||||||
title = chapternames[chapter[1]]
|
title = chapternames[chapter[1]]
|
||||||
elif os.path.basename(folder) != "Text":
|
elif os.path.basename(folder) != "Text":
|
||||||
title = chapternames[os.path.basename(folder)]
|
title = chapternames[os.path.basename(folder)]
|
||||||
@@ -255,7 +256,7 @@ def buildNAV(dstdir, title, chapters, chapternames):
|
|||||||
for chapter in chapters:
|
for chapter in chapters:
|
||||||
folder = chapter[0].replace(os.path.join(dstdir, 'OEBPS'), '').lstrip('/').lstrip('\\\\')
|
folder = chapter[0].replace(os.path.join(dstdir, 'OEBPS'), '').lstrip('/').lstrip('\\\\')
|
||||||
filename = getImageFileName(os.path.join(folder, chapter[1]))
|
filename = getImageFileName(os.path.join(folder, chapter[1]))
|
||||||
if options.chapters:
|
if options.comicinfo_chapters:
|
||||||
title = chapternames[chapter[1]]
|
title = chapternames[chapter[1]]
|
||||||
elif os.path.basename(folder) != "Text":
|
elif os.path.basename(folder) != "Text":
|
||||||
title = chapternames[os.path.basename(folder)]
|
title = chapternames[os.path.basename(folder)]
|
||||||
@@ -521,7 +522,10 @@ def buildEPUB(path, chapternames, tomenumber, ischunked):
|
|||||||
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
|
||||||
if not chapternames and options.chapters and not ischunked:
|
if ischunked:
|
||||||
|
options.comicinfo_chapters = []
|
||||||
|
|
||||||
|
if not chapternames and options.comicinfo_chapters:
|
||||||
chapterlist = []
|
chapterlist = []
|
||||||
|
|
||||||
global_diff = 0
|
global_diff = 0
|
||||||
@@ -534,13 +538,13 @@ def buildEPUB(path, chapternames, tomenumber, ischunked):
|
|||||||
elif options.splitter == 2:
|
elif options.splitter == 2:
|
||||||
diff_delta = 2
|
diff_delta = 2
|
||||||
|
|
||||||
for aChapter in options.chapters:
|
for aChapter in options.comicinfo_chapters:
|
||||||
pageid = aChapter[0]
|
pageid = aChapter[0]
|
||||||
cur_diff = global_diff
|
cur_diff = global_diff
|
||||||
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
|
||||||
|
|
||||||
@@ -557,7 +561,6 @@ def imgDirectoryProcessing(path):
|
|||||||
workerPool = Pool(maxtasksperchild=100)
|
workerPool = Pool(maxtasksperchild=100)
|
||||||
workerOutput = []
|
workerOutput = []
|
||||||
options.imgMetadata = {}
|
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 +583,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.")
|
||||||
@@ -596,7 +596,6 @@ def imgFileProcessingTick(output):
|
|||||||
for page in output:
|
for page in output:
|
||||||
if page is not None:
|
if page is not None:
|
||||||
options.imgMetadata[page[0]] = page[1]
|
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:
|
||||||
@@ -717,7 +716,7 @@ def getOutputFilename(srcpath, wantedname, ext, tomenumber):
|
|||||||
|
|
||||||
def getComicInfo(path, originalpath):
|
def getComicInfo(path, originalpath):
|
||||||
xmlPath = os.path.join(path, 'ComicInfo.xml')
|
xmlPath = os.path.join(path, 'ComicInfo.xml')
|
||||||
options.chapters = []
|
options.comicinfo_chapters = []
|
||||||
options.summary = ''
|
options.summary = ''
|
||||||
titleSuffix = ''
|
titleSuffix = ''
|
||||||
if options.title == 'defaulttitle':
|
if options.title == 'defaulttitle':
|
||||||
@@ -740,9 +739,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']:
|
||||||
@@ -761,7 +758,7 @@ def getComicInfo(path, originalpath):
|
|||||||
else:
|
else:
|
||||||
options.authors = ['KCC']
|
options.authors = ['KCC']
|
||||||
if xml.data['Bookmarks']:
|
if xml.data['Bookmarks']:
|
||||||
options.chapters = xml.data['Bookmarks']
|
options.comicinfo_chapters = xml.data['Bookmarks']
|
||||||
if xml.data['Summary']:
|
if xml.data['Summary']:
|
||||||
options.summary = hescape(xml.data['Summary'])
|
options.summary = hescape(xml.data['Summary'])
|
||||||
os.remove(xmlPath)
|
os.remove(xmlPath)
|
||||||
@@ -801,19 +798,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 +817,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 +1186,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 +1199,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:
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -320,7 +321,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, flags]
|
||||||
except IOError as err:
|
except IOError as err:
|
||||||
raise RuntimeError('Cannot save image. ' + str(err))
|
raise RuntimeError('Cannot save image. ' + str(err))
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user