mirror of
https://github.com/ciromattia/kcc
synced 2026-07-11 15:33:17 +00:00
Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a52c0ce03a | |||
| 1c0506fe7d | |||
| 806c6232c3 | |||
| 6f9f5067d8 | |||
| 11be0dffcc | |||
| 4c10569ab1 | |||
| 2a5cd6718d | |||
| fd1468904f | |||
| feec40ffc8 | |||
| f6ae7661bc | |||
| ca14a4c813 | |||
| 0c1071a358 | |||
| 858251f509 | |||
| 3c66aa9a67 | |||
| 077957e665 | |||
| c3f86e29c8 | |||
| 72e1546c2c | |||
| 544e866b51 | |||
| 363a36a764 | |||
| bece5290a3 |
@@ -123,6 +123,8 @@ For flatpak, Docker, and AppImage versions, refer to the wiki: https://github.co
|
||||
Going back a few pages and exiting and re-entering book should fix it temporarily.
|
||||
- What output format should I use?
|
||||
- MOBI for Kindles. CBZ for Kindle DX. CBZ for Koreader. KEPUB for Kobo. PDF for ReMarkable or Kindle Scribe 2025.
|
||||
- Where is KEPUB option?
|
||||
- Choosing a Kobo profile and EPUB output will output KEPUB.
|
||||
- All options have additional information in tooltips if you hover over the option.
|
||||
- To get the converted book onto your Kindle/Kobo, just drag and drop the mobi/kepub into the documents folder on your Kindle/Kobo via USB
|
||||
- Kindle panel view not working?
|
||||
@@ -293,6 +295,7 @@ OUTPUT SETTINGS:
|
||||
-t TITLE, --title TITLE
|
||||
Comic title [Default=filename or directory name]
|
||||
--metadatatitle Write title using ComicInfo.xml or other embedded metadata. 0: Don't use Title from metadata 1: Combine Title with default schema 2: Use Title only [Default=0]
|
||||
--keepcomicinfo Keep any original ComicInfo.xml files [Default=0]
|
||||
-a AUTHOR, --author AUTHOR
|
||||
Author name [Default=KCC]
|
||||
--language EPUB language [Default=en-US]
|
||||
|
||||
+961
-993
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,61 @@
|
||||
# -*- mode: python ; coding: utf-8 -*-
|
||||
# macOS GUI build, used by setup.py build_binary
|
||||
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, SPECPATH)
|
||||
from kindlecomicconverter import __version__
|
||||
|
||||
|
||||
a = Analysis(
|
||||
['kcc.py'],
|
||||
pathex=[],
|
||||
binaries=[],
|
||||
datas=[],
|
||||
hiddenimports=['_cffi_backend'],
|
||||
hookspath=[],
|
||||
hooksconfig={},
|
||||
runtime_hooks=[],
|
||||
excludes=[],
|
||||
noarchive=False,
|
||||
optimize=0,
|
||||
)
|
||||
pyz = PYZ(a.pure)
|
||||
|
||||
exe = EXE(
|
||||
pyz,
|
||||
a.scripts,
|
||||
[],
|
||||
exclude_binaries=True,
|
||||
name='Kindle Comic Converter',
|
||||
debug=False,
|
||||
bootloader_ignore_signals=False,
|
||||
strip=True,
|
||||
upx=True,
|
||||
console=False,
|
||||
disable_windowed_traceback=False,
|
||||
argv_emulation=False,
|
||||
target_arch=None,
|
||||
codesign_identity=None,
|
||||
entitlements_file=None,
|
||||
icon=['icons/comic2ebook.icns'],
|
||||
)
|
||||
coll = COLLECT(
|
||||
exe,
|
||||
a.binaries,
|
||||
a.datas,
|
||||
strip=True,
|
||||
upx=True,
|
||||
upx_exclude=[],
|
||||
name='Kindle Comic Converter',
|
||||
)
|
||||
app = BUNDLE(
|
||||
coll,
|
||||
name='Kindle Comic Converter.app',
|
||||
icon='icons/comic2ebook.icns',
|
||||
bundle_identifier=None,
|
||||
info_plist={
|
||||
'CFBundleShortVersionString': __version__,
|
||||
'CFBundleVersion': __version__,
|
||||
},
|
||||
)
|
||||
@@ -346,6 +346,8 @@ class WorkerThread(QThread):
|
||||
options.metadatatitle = 1
|
||||
elif GUI.metadataTitleBox.checkState() == Qt.CheckState.Checked:
|
||||
options.metadatatitle = 2
|
||||
if GUI.keepComicInfoBox.isChecked():
|
||||
options.keepcomicinfo = True
|
||||
if GUI.deleteBox.isChecked():
|
||||
options.delete = True
|
||||
if GUI.tempDirBox.isChecked():
|
||||
@@ -558,8 +560,6 @@ class WorkerThread(QThread):
|
||||
MW.addMessage.emit('Created EPUB file was too big. Weird file structure?', 'error', False)
|
||||
MW.addMessage.emit('EPUB file: ' + str(epubSize) + 'MB. Supported size: ~350MB.', 'error',
|
||||
False)
|
||||
if self.kindlegenErrorCode[0] == 3221226505:
|
||||
MW.addMessage.emit('Unknown Windows error. Possibly filepath too long?', 'error', False)
|
||||
else:
|
||||
for item in outputPath:
|
||||
if GUI.targetDirectory and GUI.targetDirectory != os.path.dirname(item):
|
||||
@@ -750,7 +750,6 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
else:
|
||||
status = True
|
||||
GUI.editorButton.setEnabled(status)
|
||||
GUI.wikiButton.setEnabled(status)
|
||||
GUI.deviceBox.setEnabled(status)
|
||||
GUI.defaultOutputFolderButton.setEnabled(status)
|
||||
GUI.clearButton.setEnabled(status)
|
||||
@@ -839,7 +838,6 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
GUI.interPanelCropBox.setEnabled(True)
|
||||
GUI.autoLevelBox.setEnabled(True)
|
||||
GUI.autocontrastBox.setEnabled(True)
|
||||
GUI.autocontrastBox.setChecked(True)
|
||||
|
||||
|
||||
def togglequalityBox(self, value):
|
||||
@@ -1113,6 +1111,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
'smartCoverCropBox': GUI.smartCoverCropBox.checkState(),
|
||||
'coverFillBox': GUI.coverFillBox.checkState(),
|
||||
'metadataTitleBox': GUI.metadataTitleBox.checkState(),
|
||||
'keepComicInfoBox': GUI.keepComicInfoBox.checkState(),
|
||||
'mozJpegBox': GUI.mozJpegBox.checkState(),
|
||||
'forcePngRgbBox': GUI.forcePngRgbBox.checkState(),
|
||||
'webpBox': GUI.webpBox.checkState(),
|
||||
@@ -1221,11 +1220,14 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
self.defaultOutputFolder = str(self.settings.value('defaultOutputFolder', '', type=str))
|
||||
if not os.path.exists(self.defaultOutputFolder):
|
||||
self.defaultOutputFolder = ''
|
||||
self.lastDevice = self.settings.value('lastDevice', 0, type=int)
|
||||
|
||||
# default is Kindle Paperwhite 12th Gen
|
||||
self.lastDevice = self.settings.value('lastDevice', 3, type=int)
|
||||
|
||||
self.currentFormat = self.settings.value('currentFormat', 0, type=int)
|
||||
self.startNumber = self.settings.value('startNumber', 0, type=int)
|
||||
self.windowSize = self.settings.value('windowSize', '0x0', type=str)
|
||||
default_options = {'gammaSlider': 0, 'croppingBox': 2, 'croppingPowerSlider': 100}
|
||||
default_options = {'gammaSlider': 0, 'croppingBox': 2, 'croppingPowerSlider': 100, 'rotateBox': 1, 'mangaBox': 2}
|
||||
try:
|
||||
self.options = self.settings.value('options', default_options)
|
||||
except Exception:
|
||||
@@ -1252,7 +1254,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
if self.windowSize == '0x0':
|
||||
MW.resize(500, 500)
|
||||
elif sys.platform.startswith('darwin'):
|
||||
for element in ['editorButton', 'wikiButton', 'defaultOutputFolderButton', 'clearButton', 'fileButton', 'deviceBox',
|
||||
for element in ['editorButton', 'defaultOutputFolderButton', 'clearButton', 'fileButton', 'deviceBox',
|
||||
'convertButton', 'formatBox']:
|
||||
getattr(GUI, element).setMinimumSize(QSize(0, 0))
|
||||
GUI.gridLayout.setContentsMargins(-1, -1, -1, -1)
|
||||
@@ -1433,8 +1435,9 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
link_dict = {
|
||||
'README': "https://github.com/ciromattia/kcc?tab=readme-ov-file#kcc",
|
||||
'FAQ': "https://github.com/ciromattia/kcc/blob/master/README.md#faq",
|
||||
'YOUTUBE': "https://youtu.be/IR2Fhcm9658?si=Z-2zzLaUFjmaEbrj",
|
||||
'COMMISSIONS': "https://github.com/ciromattia/kcc?tab=readme-ov-file#commissions",
|
||||
'WIKI': "https://github.com/ciromattia/kcc/wiki",
|
||||
'TUTORIAL': "https://youtu.be/QQ6zJcMF2Iw?si=80rfm6DU6OUJdFqa",
|
||||
'EMAIL': "https://github.com/ciromattia/kcc?tab=readme-ov-file#commissions",
|
||||
'DONATE': "https://github.com/ciromattia/kcc/blob/master/README.md#issues--new-features--donations",
|
||||
'FORUM': "http://www.mobileread.com/forums/showthread.php?t=207461",
|
||||
'DISCORD': "https://discord.com/invite/qj7wpnUHav",
|
||||
@@ -1466,11 +1469,8 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
GUI.fileButton.clicked.connect(self.selectFile)
|
||||
GUI.directoryButton.clicked.connect(self.selectDir)
|
||||
GUI.editorButton.clicked.connect(self.selectFileMetaEditor)
|
||||
GUI.wikiButton.clicked.connect(self.openWiki)
|
||||
GUI.kofiButton.clicked.connect(self.openKofi)
|
||||
GUI.humbleButton.clicked.connect(self.openHumble)
|
||||
GUI.youtubeButton.clicked.connect(self.openYouTube)
|
||||
GUI.discordButton.clicked.connect(self.openDiscord)
|
||||
GUI.convertButton.clicked.connect(self.convertStart)
|
||||
GUI.gammaSlider.valueChanged.connect(self.changeGamma)
|
||||
GUI.gammaBox.stateChanged.connect(self.togglegammaBox)
|
||||
|
||||
+700
-714
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
__version__ = '10.3.0'
|
||||
__version__ = '10.4.0'
|
||||
__license__ = 'ISC'
|
||||
__copyright__ = '2012-2022, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>, darodi'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#
|
||||
|
||||
from collections import Counter
|
||||
from datetime import datetime
|
||||
import os
|
||||
import pathlib
|
||||
import re
|
||||
@@ -1103,6 +1104,7 @@ def getOutputFilename(srcpath, wantedname, ext, tomenumber):
|
||||
def getMetadata(path, originalpath):
|
||||
xmlPath = os.path.join(path, 'ComicInfo.xml')
|
||||
options.comicinfo_chapters = []
|
||||
options.comicinfo_xml = None
|
||||
options.summary = ''
|
||||
titleSuffix = ''
|
||||
options.volume = ''
|
||||
@@ -1159,6 +1161,10 @@ def getMetadata(path, originalpath):
|
||||
options.summary = xml.data['Summary']
|
||||
if xml.data['Series']:
|
||||
options.series = xml.data['Series']
|
||||
# ComicInfo.xml in output may break readers like the Kobo native CBZ reader
|
||||
if options.keepcomicinfo and options.format == 'CBZ':
|
||||
with open(xmlPath, 'rb') as f:
|
||||
options.comicinfo_xml = f.read()
|
||||
os.remove(xmlPath)
|
||||
|
||||
if originalpath.lower().endswith('.pdf'):
|
||||
@@ -1201,11 +1207,19 @@ def removeNonImages(filetree):
|
||||
os.remove(os.path.join(root, name))
|
||||
# remove empty nested folders
|
||||
for root, dirs, files in os.walk(filetree, False):
|
||||
if not files and not dirs:
|
||||
if not os.listdir(root):
|
||||
os.rmdir(root)
|
||||
|
||||
if not os.listdir(Path(filetree).parent):
|
||||
raise UserWarning('No images detected, nested archives are not supported.')
|
||||
warning = [
|
||||
'No images detected.',
|
||||
'',
|
||||
'Possible causes:',
|
||||
'',
|
||||
'1) Incompatible image file extension like .jxl. Convert to .png first outside of KCC.',
|
||||
'2) Nested archive: Either extract the nested archive outside of KCC or use File Fusion option.',
|
||||
]
|
||||
raise RuntimeError('\n'.join(warning))
|
||||
|
||||
|
||||
def sanitizeTree(filetree, prefix='kcc'):
|
||||
@@ -1254,6 +1268,7 @@ def flattenTree(filetree):
|
||||
|
||||
|
||||
def sanitizePermissions(filetree):
|
||||
os.chmod(filetree, S_IWRITE | S_IREAD | S_IEXEC)
|
||||
for root, dirs, files in os.walk(filetree, False):
|
||||
for name in files:
|
||||
os.chmod(os.path.join(root, name), S_IWRITE | S_IREAD)
|
||||
@@ -1475,6 +1490,8 @@ def makeParser():
|
||||
output_options.add_argument("--metadatatitle", type=int, dest="metadatatitle", default=0,
|
||||
help="Write title using ComicInfo.xml or other embedded metadata. 1: Combine Title with default schema "
|
||||
"2: Use Title only")
|
||||
output_options.add_argument("--keepcomicinfo", type=int, dest="keepcomicinfo", default=0,
|
||||
help="Keep any original ComicInfo.xml files")
|
||||
output_options.add_argument("-a", "--author", action="store", dest="author", default="defaultauthor",
|
||||
help="Author name [Default=KCC]")
|
||||
output_options.add_argument("--language", action="store", dest="language", default="en-US",
|
||||
@@ -1904,7 +1921,10 @@ def makeBook(source, qtgui=None, job_progress=''):
|
||||
else:
|
||||
filepath.append(getOutputFilename(source, options.output, '.cbz', ''))
|
||||
if cover and cover.smartcover:
|
||||
cover.save_to_folder(os.path.join(tome, 'OEBPS', 'Images', 'cover.jpg'), tomeNumber, len(tomes))
|
||||
cover.save_to_folder(os.path.join(tome, 'OEBPS', 'Images', '##cover.jpg'), tomeNumber, len(tomes))
|
||||
if options.comicinfo_xml:
|
||||
with open(os.path.join(tome, 'OEBPS', 'Images', 'ComicInfo.xml'), 'wb') as xmlOutput:
|
||||
xmlOutput.write(options.comicinfo_xml)
|
||||
makeZIP(filepath[-1], os.path.join(tome, "OEBPS", "Images"), job_progress)
|
||||
elif options.format == 'PDF':
|
||||
print(f"{job_progress}Creating PDF file with PyMuPDF...")
|
||||
@@ -1983,8 +2003,14 @@ def makeMOBIWorkerTick(output):
|
||||
makeMOBIWorkerOutput.append(output)
|
||||
if output[0] != 0:
|
||||
makeMOBIWorkerPool.terminate()
|
||||
for warning in output[3]:
|
||||
print(warning)
|
||||
if GUI:
|
||||
GUI.progressBarTick.emit('tick')
|
||||
if output[3]:
|
||||
for warning in output[3]:
|
||||
GUI.addMessage.emit(warning, 'warning', False)
|
||||
GUI.addMessage.emit('', '', False)
|
||||
if not GUI.conversionAlive:
|
||||
makeMOBIWorkerPool.terminate()
|
||||
|
||||
@@ -1994,8 +2020,10 @@ def makeMOBIWorker(item):
|
||||
kindlegenErrorCode = 0
|
||||
kindlegenError = ''
|
||||
try:
|
||||
# TODO: This size check is incorrect, I think kindlegen increased the limit
|
||||
if os.path.getsize(item) < 629145600:
|
||||
start = perf_counter()
|
||||
# TODO: should anything be done with the kindlegen output during successes?
|
||||
output = subprocess_run(['kindlegen', '-dont_append_source', '-locale', 'en', item],
|
||||
stdout=PIPE, stderr=STDOUT, encoding='UTF-8', errors='ignore', check=True)
|
||||
end = perf_counter()
|
||||
@@ -2003,27 +2031,47 @@ def makeMOBIWorker(item):
|
||||
else:
|
||||
# ERROR: EPUB too big
|
||||
kindlegenErrorCode = 23026
|
||||
return [kindlegenErrorCode, kindlegenError, item]
|
||||
return [kindlegenErrorCode, kindlegenError, item, []]
|
||||
except CalledProcessError as err:
|
||||
warnings = []
|
||||
for line in err.stdout.splitlines():
|
||||
# ERROR: Generic error
|
||||
if "Error(" in line:
|
||||
kindlegenErrorCode = 1
|
||||
kindlegenError = line
|
||||
kindlegenError = '\n\n'.join(warnings + [line, 'kindlegen logs dumped'])
|
||||
try:
|
||||
timestamp = datetime.now().isoformat(timespec='milliseconds').replace(':', '-').replace('.', '-')
|
||||
with open(os.path.join(os.path.dirname(item), f'kindlegen-log-{timestamp}.txt'), 'w') as f:
|
||||
f.write(err.stdout)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
# examples
|
||||
# Warning(prcgen):W14019: Cover is too small
|
||||
if "Warning(" in line:
|
||||
if ":W14016: Cover not specified" in line and options.webtoon:
|
||||
pass
|
||||
else:
|
||||
warnings.append(line)
|
||||
# ERROR: EPUB too big
|
||||
if ":E23026:" in line:
|
||||
kindlegenErrorCode = 23026
|
||||
if ":E23028:" in line:
|
||||
kindlegenErrorCode = 23028
|
||||
if kindlegenErrorCode > 0:
|
||||
break
|
||||
if ":I1036: Mobi file built successfully" in line:
|
||||
return [0, '', item]
|
||||
return [0, '', item, warnings]
|
||||
if ":I1037: Mobi file built with WARNINGS!" in line:
|
||||
return [0, '', item]
|
||||
return [0, '', item, warnings]
|
||||
# ERROR: KCC unknown generic error
|
||||
if kindlegenErrorCode == 0:
|
||||
kindlegenErrorCode = err.returncode
|
||||
kindlegenError = err.stdout
|
||||
return [kindlegenErrorCode, kindlegenError, item]
|
||||
kindlegenErrorCode = -1
|
||||
if err.returncode == 3221226505:
|
||||
kindlegenError = f'Error {err.returncode}: Unknown Windows error. Possibly filepath too long?'
|
||||
else:
|
||||
kindlegenError = f'Error {err.returncode}'
|
||||
return [kindlegenErrorCode, kindlegenError, item, warnings]
|
||||
|
||||
|
||||
def makeMOBI(work, qtgui=None):
|
||||
|
||||
@@ -22,7 +22,6 @@ from functools import cached_property, lru_cache
|
||||
import os
|
||||
from pathlib import Path
|
||||
import platform
|
||||
import distro
|
||||
from subprocess import STDOUT, PIPE, CalledProcessError
|
||||
from xml.dom.minidom import parseString
|
||||
from xml.parsers.expat import ExpatError
|
||||
@@ -41,12 +40,12 @@ class ComicArchive:
|
||||
self.dirname, self.basename = os.path.split(filepath)
|
||||
|
||||
@cached_property
|
||||
def type(self):
|
||||
def type(self):
|
||||
extraction_commands = [
|
||||
[SEVENZIP, 'l', '-y', '-p1', self.basename],
|
||||
]
|
||||
|
||||
if distro.id() == 'fedora' or distro.like() == 'fedora':
|
||||
if platform.system() == 'Linux':
|
||||
extraction_commands.append(
|
||||
['unrar', 'l', '-y', '-p1', self.basename],
|
||||
)
|
||||
@@ -85,20 +84,20 @@ class ComicArchive:
|
||||
|
||||
extraction_commands.reverse()
|
||||
|
||||
if distro.id() == 'fedora' or distro.like() == 'fedora':
|
||||
if platform.system() == 'Linux':
|
||||
extraction_commands.append(
|
||||
['unrar', 'x', '-y', '-x__MACOSX', '-x.DS_Store', '-xthumbs.db', '-xThumbs.db', self.basename, targetdir]
|
||||
)
|
||||
|
||||
|
||||
for cmd in extraction_commands:
|
||||
try:
|
||||
subprocess_run(cmd, capture_output=True, check=True, cwd=self.dirname)
|
||||
return targetdir
|
||||
return targetdir
|
||||
except FileNotFoundError:
|
||||
missing.append(cmd[0])
|
||||
except CalledProcessError:
|
||||
pass
|
||||
|
||||
|
||||
if missing:
|
||||
raise OSError(f'Extraction failed, install <a href="https://github.com/ciromattia/kcc#7-zip">specialized extraction software.</a> ')
|
||||
else:
|
||||
|
||||
@@ -648,7 +648,7 @@ class Cover:
|
||||
if self.options.righttoleft:
|
||||
self.image = self.image.crop((w * .36, 0, w, h))
|
||||
else:
|
||||
self.image = self.image.crop((w, 0, .64 * w, h))
|
||||
self.image = self.image.crop((0, 0, .64 * w, h))
|
||||
|
||||
def save_to_folder(self, target, tomeid, len_tomes=0):
|
||||
try:
|
||||
|
||||
@@ -5,7 +5,6 @@ python-slugify>=8.0.4
|
||||
packaging>=26.2
|
||||
mozjpeg-lossless-optimization>=1.2.0
|
||||
natsort>=8.4.0
|
||||
distro>=1.9.0
|
||||
# Below requirements are compiled in Dockefile
|
||||
# numpy==2.3.4
|
||||
# PyMuPDF==1.26.6
|
||||
@@ -6,6 +6,5 @@ python-slugify>=8.0.4
|
||||
packaging>=26.2
|
||||
mozjpeg-lossless-optimization>=1.2.0
|
||||
natsort>=8.4.0
|
||||
distro>=1.9.0
|
||||
numpy<2
|
||||
PyMuPDF==1.25.5
|
||||
|
||||
@@ -6,6 +6,5 @@ python-slugify>=8.0.4
|
||||
packaging>=26.2
|
||||
mozjpeg-lossless-optimization>=1.2.0
|
||||
natsort>=8.4.0
|
||||
distro>=1.9.0
|
||||
numpy==1.23.5
|
||||
PyMuPDF>=1.16
|
||||
|
||||
@@ -6,6 +6,5 @@ python-slugify>=8.0.4,<9.0.0
|
||||
packaging>=26.2
|
||||
mozjpeg-lossless-optimization>=1.2.0
|
||||
natsort>=8.4.0
|
||||
distro>=1.9.0
|
||||
numpy>=1.22.4
|
||||
PyMuPDF>=1.18.0
|
||||
|
||||
@@ -38,7 +38,7 @@ class BuildBinaryCommand(setuptools.Command):
|
||||
def run(self):
|
||||
VERSION = __version__
|
||||
if sys.platform == 'darwin':
|
||||
os.system('pyinstaller --hidden-import=_cffi_backend -y -D -i icons/comic2ebook.icns -n "Kindle Comic Converter" -w -s kcc.py')
|
||||
os.system('pyinstaller -y kcc-macos.spec')
|
||||
# TODO /usr/bin/codesign --force -s "$MACOS_CERTIFICATE_NAME" --options runtime dist/Applications/Kindle\ Comic\ Converter.app -v
|
||||
min_os = os.getenv('MACOSX_DEPLOYMENT_TARGET', '')
|
||||
if min_os.startswith('10.1'):
|
||||
@@ -155,7 +155,6 @@ setuptools.setup(
|
||||
'python-slugify>=1.2.1,<9.0.0',
|
||||
'mozjpeg-lossless-optimization>=1.2.0',
|
||||
'natsort>=8.4.0',
|
||||
'distro>=1.8.0',
|
||||
'numpy>=1.22.4',
|
||||
'packaging>=23.2',
|
||||
'PyMuPDF>=1.16.1',
|
||||
|
||||
Reference in New Issue
Block a user