mirror of
https://github.com/ciromattia/kcc
synced 2026-04-15 13:38:46 +00:00
Compare commits
29 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6c6f591e45 | ||
|
|
78c014bf22 | ||
|
|
421e6bcb64 | ||
|
|
5168cd73c4 | ||
|
|
f7f19b99da | ||
|
|
1131bab41f | ||
|
|
8d204668a7 | ||
|
|
99d94ceaa7 | ||
|
|
8ff401cc3a | ||
|
|
fb7d92d737 | ||
|
|
1ca8b2c11b | ||
|
|
40e0b4853b | ||
|
|
005313f978 | ||
|
|
add2ef9faa | ||
|
|
0c98acd606 | ||
|
|
fe902ec213 | ||
|
|
4e9714e6f8 | ||
|
|
1337ad7fe3 | ||
|
|
511c7c1580 | ||
|
|
16dd034af4 | ||
|
|
d22794555f | ||
|
|
ab089adbca | ||
|
|
2c770f4562 | ||
|
|
4c73006bd8 | ||
|
|
1093dbf65a | ||
|
|
df9990c692 | ||
|
|
114f4c9e57 | ||
|
|
c2c477475d | ||
|
|
a0f8d0b5cf |
@@ -1,5 +1,5 @@
|
|||||||
# Select final stage based on TARGETARCH ARG
|
# Select final stage based on TARGETARCH ARG
|
||||||
FROM ghcr.io/ciromattia/kcc:docker-base-20230809
|
FROM ghcr.io/ciromattia/kcc:docker-base-20240928
|
||||||
LABEL com.kcc.name="Kindle Comic Converter"
|
LABEL com.kcc.name="Kindle Comic Converter"
|
||||||
LABEL com.kcc.author="Ciro Mattia Gonano, Paweł Jastrzębski and Darodi"
|
LABEL com.kcc.author="Ciro Mattia Gonano, Paweł Jastrzębski and Darodi"
|
||||||
LABEL org.opencontainers.image.description='Kindle Comic Converter'
|
LABEL org.opencontainers.image.description='Kindle Comic Converter'
|
||||||
@@ -16,4 +16,4 @@ COPY . /opt/kcc
|
|||||||
RUN cat /opt/kcc/kindlecomicconverter/__init__.py | grep version | awk '{print $3}' | sed "s/'//g" > /IMAGE_VERSION
|
RUN cat /opt/kcc/kindlecomicconverter/__init__.py | grep version | awk '{print $3}' | sed "s/'//g" > /IMAGE_VERSION
|
||||||
|
|
||||||
ENTRYPOINT ["/opt/kcc/kcc-c2e.py"]
|
ENTRYPOINT ["/opt/kcc/kcc-c2e.py"]
|
||||||
CMD ["-h"]
|
CMD ["-h"]
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM --platform=linux/amd64 python:3.11-slim-bullseye as compile-amd64
|
FROM --platform=linux/amd64 python:3.12-slim-bullseye as compile-amd64
|
||||||
ARG TARGETOS
|
ARG TARGETOS
|
||||||
ARG TARGETARCH
|
ARG TARGETARCH
|
||||||
ARG TARGETVARIANT
|
ARG TARGETVARIANT
|
||||||
@@ -16,7 +16,7 @@ RUN DEBIAN_FRONTEND=noninteractive apt-get update -y && apt-get -yq upgrade && \
|
|||||||
|
|
||||||
######################################################################################
|
######################################################################################
|
||||||
|
|
||||||
FROM --platform=linux/arm64 python:3.11-slim-bullseye as compile-arm64
|
FROM --platform=linux/arm64 python:3.12-slim-bullseye as compile-arm64
|
||||||
ARG TARGETOS
|
ARG TARGETOS
|
||||||
ARG TARGETARCH
|
ARG TARGETARCH
|
||||||
ARG TARGETVARIANT
|
ARG TARGETVARIANT
|
||||||
@@ -28,6 +28,9 @@ ENV LC_ALL=C.UTF-8 \
|
|||||||
|
|
||||||
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
|
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
|
||||||
|
|
||||||
|
COPY requirements.txt /opt/kcc/
|
||||||
|
ENV PATH="/opt/venv/bin:$PATH"
|
||||||
|
|
||||||
RUN set -x && \
|
RUN set -x && \
|
||||||
TEMP_PACKAGES=() && \
|
TEMP_PACKAGES=() && \
|
||||||
KEPT_PACKAGES=() && \
|
KEPT_PACKAGES=() && \
|
||||||
@@ -64,14 +67,13 @@ RUN set -x && \
|
|||||||
&& \
|
&& \
|
||||||
# Install required python modules
|
# Install required python modules
|
||||||
python -m pip install --upgrade pip && \
|
python -m pip install --upgrade pip && \
|
||||||
# python -m pip install -r /opt/kcc/requirements.txt && \
|
|
||||||
python -m venv /opt/venv && \
|
python -m venv /opt/venv && \
|
||||||
python -m pip install --upgrade pillow python-slugify psutil raven mozjpeg-lossless-optimization
|
python -m pip install -r /opt/kcc/requirements.txt
|
||||||
|
|
||||||
|
|
||||||
######################################################################################
|
######################################################################################
|
||||||
|
|
||||||
FROM --platform=linux/arm/v7 python:3.11-slim-bullseye as compile-armv7
|
FROM --platform=linux/arm/v7 python:3.12-slim-bullseye as compile-armv7
|
||||||
ARG TARGETOS
|
ARG TARGETOS
|
||||||
ARG TARGETARCH
|
ARG TARGETARCH
|
||||||
ARG TARGETVARIANT
|
ARG TARGETVARIANT
|
||||||
@@ -83,6 +85,9 @@ ENV LC_ALL=C.UTF-8 \
|
|||||||
|
|
||||||
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
|
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
|
||||||
|
|
||||||
|
COPY requirements.txt /opt/kcc/
|
||||||
|
ENV PATH="/opt/venv/bin:$PATH"
|
||||||
|
|
||||||
RUN set -x && \
|
RUN set -x && \
|
||||||
TEMP_PACKAGES=() && \
|
TEMP_PACKAGES=() && \
|
||||||
KEPT_PACKAGES=() && \
|
KEPT_PACKAGES=() && \
|
||||||
@@ -120,19 +125,18 @@ RUN set -x && \
|
|||||||
&& \
|
&& \
|
||||||
# Install required python modules
|
# Install required python modules
|
||||||
python -m pip install --upgrade pip && \
|
python -m pip install --upgrade pip && \
|
||||||
# python -m pip install -r /opt/kcc/requirements.txt && \
|
|
||||||
python -m venv /opt/venv && \
|
python -m venv /opt/venv && \
|
||||||
python -m pip install --upgrade pillow python-slugify psutil raven mozjpeg-lossless-optimization
|
python -m pip install --upgrade pillow psutil requests python-slugify raven packaging mozjpeg-lossless-optimization natsort distro
|
||||||
|
|
||||||
|
|
||||||
######################################################################################
|
######################################################################################
|
||||||
FROM --platform=linux/amd64 python:3.11-slim-bullseye as build-amd64
|
FROM --platform=linux/amd64 python:3.12-slim-bullseye as build-amd64
|
||||||
COPY --from=compile-amd64 /opt/venv /opt/venv
|
COPY --from=compile-amd64 /opt/venv /opt/venv
|
||||||
|
|
||||||
FROM --platform=linux/arm64 python:3.11-slim-bullseye as build-arm64
|
FROM --platform=linux/arm64 python:3.12-slim-bullseye as build-arm64
|
||||||
COPY --from=compile-arm64 /opt/venv /opt/venv
|
COPY --from=compile-arm64 /opt/venv /opt/venv
|
||||||
|
|
||||||
FROM --platform=linux/arm/v7 python:3.11-slim-bullseye as build-armv7
|
FROM --platform=linux/arm/v7 python:3.12-slim-bullseye as build-armv7
|
||||||
COPY --from=compile-armv7 /opt/venv /opt/venv
|
COPY --from=compile-armv7 /opt/venv /opt/venv
|
||||||
######################################################################################
|
######################################################################################
|
||||||
|
|
||||||
@@ -156,5 +160,5 @@ WORKDIR /app
|
|||||||
RUN DEBIAN_FRONTEND=noninteractive apt-get update -y && apt-get -yq upgrade && \
|
RUN DEBIAN_FRONTEND=noninteractive apt-get update -y && apt-get -yq upgrade && \
|
||||||
apt-get install -y p7zip-full unrar-free && \
|
apt-get install -y p7zip-full unrar-free && \
|
||||||
ln -s /app/kindlegen /bin/kindlegen && \
|
ln -s /app/kindlegen /bin/kindlegen && \
|
||||||
echo docker-base-20230809 > /IMAGE_VERSION
|
echo docker-base-20240928 > /IMAGE_VERSION
|
||||||
|
|
||||||
|
|||||||
20
README.md
20
README.md
@@ -22,13 +22,13 @@ If you have some **technical** problems using KCC please [file an issue here](ht
|
|||||||
If you can fix an open issue, fork & make a pull request.
|
If you can fix an open issue, fork & make a pull request.
|
||||||
|
|
||||||
If you find **KCC** valuable you can consider donating to the authors:
|
If you find **KCC** valuable you can consider donating to the authors:
|
||||||
- Ciro Mattia Gonano:
|
- Ciro Mattia Gonano (founder, active 2013-2014):
|
||||||
- [](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=D8WNYNPBGDAS2)
|
- [](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=D8WNYNPBGDAS2)
|
||||||
- [](http://flattr.com/thing/2260449/ciromattiakcc-on-GitHub)
|
- [](http://flattr.com/thing/2260449/ciromattiakcc-on-GitHub)
|
||||||
- Paweł Jastrzębski:
|
- Paweł Jastrzębski (active 2013-2019):
|
||||||
- [](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YTTJ4LK2JDHPS)
|
- [](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YTTJ4LK2JDHPS)
|
||||||
- [](https://jastrzeb.ski/donate/)
|
- [](https://jastrzeb.ski/donate/)
|
||||||
- Alex Xu
|
- 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)
|
||||||
|
|
||||||
|
|
||||||
@@ -55,6 +55,8 @@ For flatpak, Docker, and AppImage versions, refer to the wiki: https://github.co
|
|||||||
|
|
||||||
- [Kindle Scribe cover guide](https://github.com/ciromattia/kcc/issues/508) (also works for older Kindles)
|
- [Kindle Scribe cover guide](https://github.com/ciromattia/kcc/issues/508) (also works for older Kindles)
|
||||||
- [Windows 7 support](https://github.com/ciromattia/kcc/issues/678)
|
- [Windows 7 support](https://github.com/ciromattia/kcc/issues/678)
|
||||||
|
- [Combine files/chapters](https://github.com/ciromattia/kcc/issues/612#issuecomment-2117985011)
|
||||||
|
- [Flatpak mobi conversion stuck](https://github.com/ciromattia/kcc/wiki/Installation#linux)
|
||||||
|
|
||||||
## PREREQUISITES
|
## PREREQUISITES
|
||||||
|
|
||||||
@@ -62,17 +64,15 @@ You'll need to install various tools to access important but optional features.
|
|||||||
|
|
||||||
### KindleGen
|
### KindleGen
|
||||||
|
|
||||||
#### Windows / macOS KindleGen
|
On Windows and macOS, install [Kindle Previewer](https://www.amazon.com/Kindle-Previewer/b?ie=UTF8&node=21381691011) and `kindlegen` will be autodetected from it.
|
||||||
|
|
||||||
Install [Kindle Previewer](https://www.amazon.com/Kindle-Previewer/b?ie=UTF8&node=21381691011) and `kindlegen` will be autodetected from it.
|
If you have issues detecting it, get stuck on the MOBI conversion step, or use Linux AppImage or Flatpak, refer to the wiki: https://github.com/ciromattia/kcc/wiki/Installation#kindlegen
|
||||||
|
|
||||||
If you have issues detecting it, or use another OS, refer to the wiki: https://github.com/ciromattia/kcc/wiki/Installation#kindlegen
|
|
||||||
|
|
||||||
### 7-Zip
|
### 7-Zip
|
||||||
|
|
||||||
This is an optional requirement as of KCC 6.1.0. You only need to install it if 1) you are using advanced features, 2) are using Windows 10 (2017) or earlier, or 3) need to use an older KCC version.
|
This is no longer required as of KCC 6.1.
|
||||||
|
|
||||||
If you need to install it, refer to the wiki: https://github.com/ciromattia/kcc/wiki/Installation#7-zip
|
If you still need it, refer to the wiki: https://github.com/ciromattia/kcc/wiki/Installation#7-zip
|
||||||
|
|
||||||
## INPUT FORMATS
|
## INPUT FORMATS
|
||||||
**KCC** can understand and convert, at the moment, the following input types:
|
**KCC** can understand and convert, at the moment, the following input types:
|
||||||
@@ -107,7 +107,7 @@ sudo apt-get install python3 p7zip-full python3-pil python3-psutil python3-slugi
|
|||||||
'KPW': ("Kindle Paperwhite 1/2", (758, 1024), Palette16, 1.8),
|
'KPW': ("Kindle Paperwhite 1/2", (758, 1024), Palette16, 1.8),
|
||||||
'KV': ("Kindle Paperwhite 3/4/Voyage/Oasis", (1072, 1448), Palette16, 1.8),
|
'KV': ("Kindle Paperwhite 3/4/Voyage/Oasis", (1072, 1448), Palette16, 1.8),
|
||||||
'KPW5': ("Kindle Paperwhite 5/Signature Edition", (1236, 1648), Palette16, 1.8),
|
'KPW5': ("Kindle Paperwhite 5/Signature Edition", (1236, 1648), Palette16, 1.8),
|
||||||
'KO': ("Kindle Oasis 2/3", (1264, 1680), Palette16, 1.8),
|
'KO': ("Kindle Oasis 2/3/Paperwhite 12/Colorsoft 12", (1264, 1680), Palette16, 1.8),
|
||||||
'KS': ("Kindle Scribe", (1860, 2480), Palette16, 1.8),
|
'KS': ("Kindle Scribe", (1860, 2480), Palette16, 1.8),
|
||||||
'KoMT': ("Kobo Mini/Touch", (600, 800), Palette16, 1.8),
|
'KoMT': ("Kobo Mini/Touch", (600, 800), Palette16, 1.8),
|
||||||
'KoG': ("Kobo Glo", (768, 1024), Palette16, 1.8),
|
'KoG': ("Kobo Glo", (768, 1024), Palette16, 1.8),
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ a = Analysis(['kcc-c2e.py'],
|
|||||||
pathex=['.'],
|
pathex=['.'],
|
||||||
binaries=[],
|
binaries=[],
|
||||||
datas=[],
|
datas=[],
|
||||||
hiddenimports=[],
|
hiddenimports=['_cffi_backend'],
|
||||||
hookspath=[],
|
hookspath=[],
|
||||||
runtime_hooks=[],
|
runtime_hooks=[],
|
||||||
excludes=[],
|
excludes=[],
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ a = Analysis(['kcc-c2p.py'],
|
|||||||
pathex=['.'],
|
pathex=['.'],
|
||||||
binaries=[],
|
binaries=[],
|
||||||
datas=[],
|
datas=[],
|
||||||
hiddenimports=[],
|
hiddenimports=['_cffi_backend'],
|
||||||
hookspath=[],
|
hookspath=[],
|
||||||
runtime_hooks=[],
|
runtime_hooks=[],
|
||||||
excludes=[],
|
excludes=[],
|
||||||
|
|||||||
1
kcc.py
1
kcc.py
@@ -50,6 +50,7 @@ elif sys.platform.startswith('win'):
|
|||||||
win_paths = [
|
win_paths = [
|
||||||
os.path.expandvars('%LOCALAPPDATA%\\Amazon\\KC2'),
|
os.path.expandvars('%LOCALAPPDATA%\\Amazon\\KC2'),
|
||||||
os.path.expandvars('%LOCALAPPDATA%\\Amazon\\Kindle Previewer 3\\lib\\fc\\bin\\'),
|
os.path.expandvars('%LOCALAPPDATA%\\Amazon\\Kindle Previewer 3\\lib\\fc\\bin\\'),
|
||||||
|
os.path.expandvars('%UserProfile%\\Kindle Previewer 3\\lib\\fc\\bin\\'),
|
||||||
'C:\\Apps\\Kindle Previewer 3\\lib\\fc\\bin',
|
'C:\\Apps\\Kindle Previewer 3\\lib\\fc\\bin',
|
||||||
'D:\\Apps\\Kindle Previewer 3\\lib\\fc\\bin',
|
'D:\\Apps\\Kindle Previewer 3\\lib\\fc\\bin',
|
||||||
'E:\\Apps\\Kindle Previewer 3\\lib\\fc\\bin',
|
'E:\\Apps\\Kindle Previewer 3\\lib\\fc\\bin',
|
||||||
|
|||||||
@@ -31,11 +31,11 @@ from PySide6.QtCore import Qt
|
|||||||
from xml.sax.saxutils import escape
|
from xml.sax.saxutils import escape
|
||||||
from psutil import Process
|
from psutil import Process
|
||||||
from copy import copy
|
from copy import copy
|
||||||
from distutils.version import StrictVersion
|
from packaging.version import Version
|
||||||
from raven import Client
|
from raven import Client
|
||||||
from tempfile import gettempdir
|
from tempfile import gettempdir
|
||||||
|
|
||||||
from .shared import HTMLStripper, sanitizeTrace, walkLevel, subprocess_run_silent
|
from .shared import HTMLStripper, sanitizeTrace, walkLevel, subprocess_run
|
||||||
from . import __version__
|
from . import __version__
|
||||||
from . import comic2ebook
|
from . import comic2ebook
|
||||||
from . import metadata
|
from . import metadata
|
||||||
@@ -146,9 +146,9 @@ class VersionThread(QtCore.QThread):
|
|||||||
latest_version = json_parser["tag_name"]
|
latest_version = json_parser["tag_name"]
|
||||||
latest_version = re.sub(r'^v', "", latest_version)
|
latest_version = re.sub(r'^v', "", latest_version)
|
||||||
|
|
||||||
if ("b" not in __version__ and StrictVersion(latest_version) > StrictVersion(__version__)) \
|
if ("b" not in __version__ and Version(latest_version) > Version(__version__)) \
|
||||||
or ("b" in __version__
|
or ("b" in __version__
|
||||||
and StrictVersion(latest_version) >= StrictVersion(re.sub(r'b.*', '', __version__))):
|
and Version(latest_version) >= Version(re.sub(r'b.*', '', __version__))):
|
||||||
MW.addMessage.emit('<a href="' + html_url + '"><b>The new version is available!</b></a>', 'warning',
|
MW.addMessage.emit('<a href="' + html_url + '"><b>The new version is available!</b></a>', 'warning',
|
||||||
False)
|
False)
|
||||||
except Exception:
|
except Exception:
|
||||||
@@ -380,7 +380,7 @@ class WorkerThread(QtCore.QThread):
|
|||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
MW.addMessage.emit('Processing MOBI files... <b>Done!</b>', 'info', True)
|
MW.addMessage.emit('Processing MOBI files... <b>Done!</b>', 'info', True)
|
||||||
k = kindle.Kindle()
|
k = kindle.Kindle(options.profile)
|
||||||
if k.path and k.coverSupport:
|
if k.path and k.coverSupport:
|
||||||
for item in outputPath:
|
for item in outputPath:
|
||||||
comic2ebook.options.covers[outputPath.index(item)][0].saveToKindle(
|
comic2ebook.options.covers[outputPath.index(item)][0].saveToKindle(
|
||||||
@@ -846,12 +846,12 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
|||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
try:
|
try:
|
||||||
versionCheck = subprocess_run_silent(['kindlegen', '-locale', 'en'], stdout=PIPE, stderr=STDOUT, encoding='UTF-8')
|
versionCheck = subprocess_run(['kindlegen', '-locale', 'en'], stdout=PIPE, stderr=STDOUT, encoding='UTF-8')
|
||||||
self.kindleGen = True
|
self.kindleGen = True
|
||||||
for line in versionCheck.stdout.splitlines():
|
for line in versionCheck.stdout.splitlines():
|
||||||
if 'Amazon kindlegen' in line:
|
if 'Amazon kindlegen' in line:
|
||||||
versionCheck = line.split('V')[1].split(' ')[0]
|
versionCheck = line.split('V')[1].split(' ')[0]
|
||||||
if StrictVersion(versionCheck) < StrictVersion('2.9'):
|
if Version(versionCheck) < Version('2.9'):
|
||||||
self.addMessage('Your <a href="https://www.amazon.com/b?node=23496309011">KindleGen</a>'
|
self.addMessage('Your <a href="https://www.amazon.com/b?node=23496309011">KindleGen</a>'
|
||||||
' is outdated! MOBI conversion might fail.', 'warning')
|
' is outdated! MOBI conversion might fail.', 'warning')
|
||||||
break
|
break
|
||||||
@@ -934,6 +934,12 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
|||||||
"Kindle PW 11": {
|
"Kindle PW 11": {
|
||||||
'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': True, 'ForceColor': False, 'Label': 'KPW5',
|
'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': True, 'ForceColor': False, 'Label': 'KPW5',
|
||||||
},
|
},
|
||||||
|
"Kindle PW 12": {
|
||||||
|
'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': True, 'ForceColor': False, 'Label': 'KO',
|
||||||
|
},
|
||||||
|
"Kindle CS 12": {
|
||||||
|
'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': True, 'ForceColor': True, 'Label': 'KO',
|
||||||
|
},
|
||||||
"Kindle PW 7/10": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
|
"Kindle PW 7/10": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
|
||||||
'DefaultUpscale': True, 'ForceColor': False, 'Label': 'KV'},
|
'DefaultUpscale': True, 'ForceColor': False, 'Label': 'KV'},
|
||||||
"Kindle PW 5/6": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
|
"Kindle PW 5/6": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
|
||||||
@@ -988,9 +994,11 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
|||||||
'Label': 'OTHER'},
|
'Label': 'OTHER'},
|
||||||
}
|
}
|
||||||
profilesGUI = [
|
profilesGUI = [
|
||||||
|
"Kindle CS 12",
|
||||||
|
"Kindle PW 12",
|
||||||
"Kindle Scribe",
|
"Kindle Scribe",
|
||||||
"Kindle 11",
|
|
||||||
"Kindle PW 11",
|
"Kindle PW 11",
|
||||||
|
"Kindle 11",
|
||||||
"Kindle Oasis 9/10",
|
"Kindle Oasis 9/10",
|
||||||
"Separator",
|
"Separator",
|
||||||
"Kobo Clara 2E",
|
"Kobo Clara 2E",
|
||||||
@@ -1041,12 +1049,12 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
|||||||
'<a href="https://github.com/ciromattia/kcc/wiki/Important-tips">important tips</a>.',
|
'<a href="https://github.com/ciromattia/kcc/wiki/Important-tips">important tips</a>.',
|
||||||
'info')
|
'info')
|
||||||
try:
|
try:
|
||||||
subprocess_run_silent(['tar'], stdout=PIPE, stderr=STDOUT)
|
subprocess_run(['tar'], stdout=PIPE, stderr=STDOUT)
|
||||||
self.tar = True
|
self.tar = True
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
self.tar = False
|
self.tar = False
|
||||||
try:
|
try:
|
||||||
subprocess_run_silent(['7z'], stdout=PIPE, stderr=STDOUT)
|
subprocess_run(['7z'], stdout=PIPE, stderr=STDOUT)
|
||||||
self.sevenzip = True
|
self.sevenzip = True
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
self.sevenzip = False
|
self.sevenzip = False
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
__version__ = '6.1.0'
|
__version__ = '6.3.0'
|
||||||
__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'
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ from subprocess import STDOUT, PIPE
|
|||||||
from psutil import virtual_memory, disk_usage
|
from psutil import virtual_memory, disk_usage
|
||||||
from html import escape as hescape
|
from html import escape as hescape
|
||||||
|
|
||||||
from .shared import md5Checksum, getImageFileName, walkSort, walkLevel, sanitizeTrace, subprocess_run_silent
|
from .shared import md5Checksum, getImageFileName, walkSort, walkLevel, sanitizeTrace, subprocess_run
|
||||||
from . import comic2panel
|
from . import comic2panel
|
||||||
from . import image
|
from . import image
|
||||||
from . import comicarchive
|
from . import comicarchive
|
||||||
@@ -420,7 +420,7 @@ def buildOPF(dstdir, title, filelist, cover=None):
|
|||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
|
|
||||||
def buildEPUB(path, chapternames, tomenumber):
|
def buildEPUB(path, chapternames, tomenumber, ischunked):
|
||||||
filelist = []
|
filelist = []
|
||||||
chapterlist = []
|
chapterlist = []
|
||||||
cover = None
|
cover = None
|
||||||
@@ -517,7 +517,7 @@ def buildEPUB(path, chapternames, tomenumber):
|
|||||||
chapterlist.append((dirpath.replace('Images', 'Text'), filelist[-1][1]))
|
chapterlist.append((dirpath.replace('Images', 'Text'), filelist[-1][1]))
|
||||||
chapter = True
|
chapter = True
|
||||||
# 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:
|
if not chapternames and options.chapters and not ischunked:
|
||||||
chapterlist = []
|
chapterlist = []
|
||||||
|
|
||||||
global_diff = 0
|
global_diff = 0
|
||||||
@@ -738,7 +738,7 @@ def getComicInfo(path, originalpath):
|
|||||||
options.authors.sort()
|
options.authors.sort()
|
||||||
else:
|
else:
|
||||||
options.authors = ['KCC']
|
options.authors = ['KCC']
|
||||||
if xml.data['Bookmarks'] and options.batchsplit == 0:
|
if xml.data['Bookmarks']:
|
||||||
options.chapters = xml.data['Bookmarks']
|
options.chapters = xml.data['Bookmarks']
|
||||||
if xml.data['Summary']:
|
if xml.data['Summary']:
|
||||||
options.summary = hescape(xml.data['Summary'])
|
options.summary = hescape(xml.data['Summary'])
|
||||||
@@ -957,8 +957,7 @@ def makeParser():
|
|||||||
help="Full path to comic folder or file(s) to be processed.")
|
help="Full path to comic folder or file(s) to be processed.")
|
||||||
|
|
||||||
main_options.add_argument("-p", "--profile", action="store", dest="profile", default="KV",
|
main_options.add_argument("-p", "--profile", action="store", dest="profile", default="KV",
|
||||||
help="Device profile (Available options: K1, K2, K34, K578, KDX, KPW, KPW5, KV, KO, "
|
help=f"Device profile (Available options: {', '.join(image.ProfileData.Profiles.keys())})"
|
||||||
"K11, KS, KoMT, KoG, KoGHD, KoA, KoAHD, KoAH2O, KoAO, KoN, KoC, KoCC, KoL, KoLC, KoF, KoS, KoE)"
|
|
||||||
" [Default=KV]")
|
" [Default=KV]")
|
||||||
main_options.add_argument("-m", "--manga-style", action="store_true", dest="righttoleft", default=False,
|
main_options.add_argument("-m", "--manga-style", action="store_true", dest="righttoleft", default=False,
|
||||||
help="Manga style (right-to-left reading and splitting)")
|
help="Manga style (right-to-left reading and splitting)")
|
||||||
@@ -1046,16 +1045,15 @@ def checkOptions(options):
|
|||||||
options.kfx = False
|
options.kfx = False
|
||||||
options.supportSyntheticSpread = False
|
options.supportSyntheticSpread = False
|
||||||
if options.format == 'Auto':
|
if options.format == 'Auto':
|
||||||
if options.profile in ['K1', 'K2', 'K34', 'K578', 'KPW', 'KPW5', 'KV', 'KO', 'K11', 'KS']:
|
if options.profile in ['KDX']:
|
||||||
options.format = 'MOBI'
|
|
||||||
elif options.profile in ['OTHER', 'KoMT', 'KoG', 'KoGHD', 'KoA', 'KoAHD', 'KoAH2O', 'KoAO',
|
|
||||||
'KoN', 'KoC', 'KoCC', 'KoL', 'KoLC', 'KoF', 'KoS', 'KoE']:
|
|
||||||
options.format = 'EPUB'
|
|
||||||
elif options.profile in ['KDX']:
|
|
||||||
options.format = 'CBZ'
|
options.format = 'CBZ'
|
||||||
if options.profile in ['K1', 'K2', 'K34', 'K578', 'KPW', 'KPW5', 'KV', 'KO', 'K11', 'KS']:
|
elif options.profile in image.ProfileData.ProfilesKindle.keys():
|
||||||
|
options.format = 'MOBI'
|
||||||
|
else:
|
||||||
|
options.format = 'EPUB'
|
||||||
|
if options.profile in image.ProfileData.ProfilesKindle.keys():
|
||||||
options.iskindle = True
|
options.iskindle = True
|
||||||
elif options.profile in ['OTHER', 'KoMT', 'KoG', 'KoGHD', 'KoA', 'KoAHD', 'KoAH2O', 'KoAO', 'KoN', 'KoC', 'KoCC', 'KoL', 'KoLC', 'KoF', 'KoS', 'KoE']:
|
else:
|
||||||
options.isKobo = True
|
options.isKobo = True
|
||||||
# Other Kobo devices probably support synthetic spreads as well, but
|
# Other Kobo devices probably support synthetic spreads as well, but
|
||||||
# they haven't been tested.
|
# they haven't been tested.
|
||||||
@@ -1114,13 +1112,13 @@ def checkTools(source):
|
|||||||
if source.endswith('.CB7') or source.endswith('.7Z') or source.endswith('.RAR') or source.endswith('.CBR') or \
|
if source.endswith('.CB7') or source.endswith('.7Z') or source.endswith('.RAR') or source.endswith('.CBR') or \
|
||||||
source.endswith('.ZIP') or source.endswith('.CBZ'):
|
source.endswith('.ZIP') or source.endswith('.CBZ'):
|
||||||
try:
|
try:
|
||||||
subprocess_run_silent(['7z'], stdout=PIPE, stderr=STDOUT)
|
subprocess_run(['7z'], stdout=PIPE, stderr=STDOUT)
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
print('ERROR: 7z is missing!')
|
print('ERROR: 7z is missing!')
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
if options.format == 'MOBI':
|
if options.format == 'MOBI':
|
||||||
try:
|
try:
|
||||||
subprocess_run_silent(['kindlegen', '-locale', 'en'], stdout=PIPE, stderr=STDOUT)
|
subprocess_run(['kindlegen', '-locale', 'en'], stdout=PIPE, stderr=STDOUT)
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
print('ERROR: KindleGen is missing!')
|
print('ERROR: KindleGen is missing!')
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
@@ -1202,10 +1200,11 @@ def makeBook(source, qtgui=None):
|
|||||||
makeZIP(tome + '_comic', os.path.join(tome, "OEBPS", "Images"))
|
makeZIP(tome + '_comic', os.path.join(tome, "OEBPS", "Images"))
|
||||||
else:
|
else:
|
||||||
print("Creating EPUB file...")
|
print("Creating EPUB file...")
|
||||||
buildEPUB(tome, chapterNames, tomeNumber)
|
|
||||||
if len(tomes) > 1:
|
if len(tomes) > 1:
|
||||||
|
buildEPUB(tome, chapterNames, tomeNumber, True)
|
||||||
filepath.append(getOutputFilename(source, options.output, '.epub', ' ' + str(tomeNumber)))
|
filepath.append(getOutputFilename(source, options.output, '.epub', ' ' + str(tomeNumber)))
|
||||||
else:
|
else:
|
||||||
|
buildEPUB(tome, chapterNames, tomeNumber, False)
|
||||||
filepath.append(getOutputFilename(source, options.output, '.epub', ''))
|
filepath.append(getOutputFilename(source, options.output, '.epub', ''))
|
||||||
makeZIP(tome + '_comic', tome, True)
|
makeZIP(tome + '_comic', tome, True)
|
||||||
copyfile(tome + '_comic.zip', filepath[-1])
|
copyfile(tome + '_comic.zip', filepath[-1])
|
||||||
@@ -1228,7 +1227,7 @@ def makeBook(source, qtgui=None):
|
|||||||
print('Error: KindleGen failed to create MOBI!')
|
print('Error: KindleGen failed to create MOBI!')
|
||||||
print(errors)
|
print(errors)
|
||||||
return filepath
|
return filepath
|
||||||
k = kindle.Kindle()
|
k = kindle.Kindle(options.profile)
|
||||||
if k.path and k.coverSupport:
|
if k.path and k.coverSupport:
|
||||||
print("Kindle detected. Uploading covers...")
|
print("Kindle detected. Uploading covers...")
|
||||||
for i in filepath:
|
for i in filepath:
|
||||||
@@ -1250,12 +1249,13 @@ def makeBook(source, qtgui=None):
|
|||||||
|
|
||||||
|
|
||||||
def makeMOBIFix(item, uuid):
|
def makeMOBIFix(item, uuid):
|
||||||
|
is_pdoc = options.profile in image.ProfileData.ProfilesKindlePDOC.keys()
|
||||||
if not options.keep_epub:
|
if not options.keep_epub:
|
||||||
os.remove(item)
|
os.remove(item)
|
||||||
mobiPath = item.replace('.epub', '.mobi')
|
mobiPath = item.replace('.epub', '.mobi')
|
||||||
move(mobiPath, mobiPath + '_toclean')
|
move(mobiPath, mobiPath + '_toclean')
|
||||||
try:
|
try:
|
||||||
dualmetafix.DualMobiMetaFix(mobiPath + '_toclean', mobiPath, bytes(uuid, 'UTF-8'))
|
dualmetafix.DualMobiMetaFix(mobiPath + '_toclean', mobiPath, bytes(uuid, 'UTF-8'), is_pdoc)
|
||||||
return [True]
|
return [True]
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
return [False, format(err)]
|
return [False, format(err)]
|
||||||
@@ -1277,7 +1277,7 @@ def makeMOBIWorker(item):
|
|||||||
kindlegenError = ''
|
kindlegenError = ''
|
||||||
try:
|
try:
|
||||||
if os.path.getsize(item) < 629145600:
|
if os.path.getsize(item) < 629145600:
|
||||||
output = subprocess_run_silent(['kindlegen', '-dont_append_source', '-locale', 'en', item],
|
output = subprocess_run(['kindlegen', '-dont_append_source', '-locale', 'en', item],
|
||||||
stdout=PIPE, stderr=STDOUT, encoding='UTF-8')
|
stdout=PIPE, stderr=STDOUT, encoding='UTF-8')
|
||||||
for line in output.stdout.splitlines():
|
for line in output.stdout.splitlines():
|
||||||
# ERROR: Generic error
|
# ERROR: Generic error
|
||||||
|
|||||||
@@ -18,15 +18,14 @@
|
|||||||
# PERFORMANCE OF THIS SOFTWARE.
|
# PERFORMANCE OF THIS SOFTWARE.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
from functools import cached_property
|
||||||
import os
|
import os
|
||||||
import platform
|
import platform
|
||||||
import subprocess
|
|
||||||
import distro
|
import distro
|
||||||
from shutil import move
|
|
||||||
from subprocess import STDOUT, PIPE, CalledProcessError
|
from subprocess import STDOUT, PIPE, CalledProcessError
|
||||||
from xml.dom.minidom import parseString
|
from xml.dom.minidom import parseString
|
||||||
from xml.parsers.expat import ExpatError
|
from xml.parsers.expat import ExpatError
|
||||||
from .shared import subprocess_run_silent
|
from .shared import subprocess_run
|
||||||
|
|
||||||
EXTRACTION_ERROR = 'Failed to extract archive. Try extracting file outside of KCC.'
|
EXTRACTION_ERROR = 'Failed to extract archive. Try extracting file outside of KCC.'
|
||||||
|
|
||||||
@@ -34,56 +33,78 @@ EXTRACTION_ERROR = 'Failed to extract archive. Try extracting file outside of KC
|
|||||||
class ComicArchive:
|
class ComicArchive:
|
||||||
def __init__(self, filepath):
|
def __init__(self, filepath):
|
||||||
self.filepath = filepath
|
self.filepath = filepath
|
||||||
self.type = None
|
|
||||||
if not os.path.isfile(self.filepath):
|
if not os.path.isfile(self.filepath):
|
||||||
raise OSError('File not found.')
|
raise OSError('File not found.')
|
||||||
try:
|
|
||||||
process = subprocess_run_silent(['7z', 'l', '-y', '-p1', self.filepath], stderr=STDOUT, stdout=PIPE)
|
@cached_property
|
||||||
except FileNotFoundError:
|
def type(self):
|
||||||
return
|
extraction_commands = [
|
||||||
for line in process.stdout.splitlines():
|
['7z', 'l', '-y', '-p1', self.filepath],
|
||||||
if b'Type =' in line:
|
]
|
||||||
self.type = line.rstrip().decode().split(' = ')[1].upper()
|
|
||||||
break
|
if distro.id() == 'fedora':
|
||||||
if process.returncode != 0 and distro.id() == 'fedora':
|
extraction_commands.append(
|
||||||
process = subprocess_run_silent(['unrar', 'l', '-y', '-p1', self.filepath], stderr=STDOUT, stdout=PIPE)
|
['unrar', 'l', '-y', '-p1', self.filepath],
|
||||||
for line in process.stdout.splitlines():
|
)
|
||||||
if b'Details: ' in line:
|
|
||||||
self.type = line.rstrip().decode().split(' ')[1].upper()
|
for cmd in extraction_commands:
|
||||||
break
|
try:
|
||||||
if process.returncode != 0:
|
process = subprocess_run(cmd, capture_output=True, check=True)
|
||||||
raise OSError(EXTRACTION_ERROR)
|
for line in process.stdout.splitlines():
|
||||||
|
if b'Type =' in line:
|
||||||
|
return line.rstrip().decode().split(' = ')[1].upper()
|
||||||
|
except FileNotFoundError:
|
||||||
|
pass
|
||||||
|
except CalledProcessError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
raise OSError(EXTRACTION_ERROR)
|
||||||
|
|
||||||
def extract(self, targetdir):
|
def extract(self, targetdir):
|
||||||
if not os.path.isdir(targetdir):
|
if not os.path.isdir(targetdir):
|
||||||
raise OSError('Target directory doesn\'t exist.')
|
raise OSError('Target directory doesn\'t exist.')
|
||||||
try:
|
|
||||||
process = subprocess_run_silent(['tar', '-xf', self.filepath, '-C', targetdir],
|
missing = []
|
||||||
stdout=PIPE, stderr=STDOUT, check=True)
|
|
||||||
return targetdir
|
extraction_commands = [
|
||||||
except (FileNotFoundError, CalledProcessError):
|
['tar', '--exclude', '__MACOSX', '--exclude', '.DS_Store', '--exclude', 'thumbs.db', '--exclude', 'Thumbs.db', '-xf', self.filepath, '-C', targetdir],
|
||||||
pass
|
['7z', 'x', '-y', '-xr!__MACOSX', '-xr!.DS_Store', '-xr!thumbs.db', '-xr!Thumbs.db', '-o' + targetdir, self.filepath],
|
||||||
process = subprocess_run_silent(['7z', 'x', '-y', '-xr!__MACOSX', '-xr!.DS_Store', '-xr!thumbs.db', '-xr!Thumbs.db', '-o' + targetdir, self.filepath],
|
]
|
||||||
stdout=PIPE, stderr=STDOUT)
|
|
||||||
if process.returncode != 0 and distro.id() == 'fedora':
|
if platform.system() == 'Darwin':
|
||||||
process = subprocess_run_silent(['unrar', 'x', '-y', '-x__MACOSX', '-x.DS_Store', '-xthumbs.db', '-xThumbs.db', self.filepath, targetdir]
|
extraction_commands.append(
|
||||||
, stdout=PIPE, stderr=STDOUT)
|
['unar', self.filepath, '-f', '-o', targetdir]
|
||||||
if process.returncode != 0:
|
)
|
||||||
raise OSError(EXTRACTION_ERROR)
|
|
||||||
elif process.returncode != 0:
|
if distro.id() == 'fedora':
|
||||||
|
extraction_commands.append(
|
||||||
|
['unrar', 'x', '-y', '-x__MACOSX', '-x.DS_Store', '-xthumbs.db', '-xThumbs.db', self.filepath, targetdir]
|
||||||
|
)
|
||||||
|
|
||||||
|
for cmd in extraction_commands:
|
||||||
|
try:
|
||||||
|
subprocess_run(cmd, capture_output=True, check=True)
|
||||||
|
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:
|
||||||
raise OSError(EXTRACTION_ERROR)
|
raise OSError(EXTRACTION_ERROR)
|
||||||
return targetdir
|
|
||||||
|
|
||||||
def addFile(self, sourcefile):
|
def addFile(self, sourcefile):
|
||||||
if self.type in ['RAR', 'RAR5']:
|
if self.type in ['RAR', 'RAR5']:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
process = subprocess_run_silent(['7z', 'a', '-y', self.filepath, sourcefile],
|
process = subprocess_run(['7z', 'a', '-y', self.filepath, sourcefile],
|
||||||
stdout=PIPE, stderr=STDOUT)
|
stdout=PIPE, stderr=STDOUT)
|
||||||
if process.returncode != 0:
|
if process.returncode != 0:
|
||||||
raise OSError('Failed to add the file.')
|
raise OSError('Failed to add the file.')
|
||||||
|
|
||||||
def extractMetadata(self):
|
def extractMetadata(self):
|
||||||
process = subprocess_run_silent(['7z', 'x', '-y', '-so', self.filepath, 'ComicInfo.xml'],
|
process = subprocess_run(['7z', 'x', '-y', '-so', self.filepath, 'ComicInfo.xml'],
|
||||||
stdout=PIPE, stderr=STDOUT)
|
stdout=PIPE, stderr=STDOUT)
|
||||||
if process.returncode != 0:
|
if process.returncode != 0:
|
||||||
raise OSError(EXTRACTION_ERROR)
|
raise OSError(EXTRACTION_ERROR)
|
||||||
|
|||||||
@@ -136,7 +136,11 @@ def del_exth(rec0, exth_num):
|
|||||||
|
|
||||||
|
|
||||||
class DualMobiMetaFix:
|
class DualMobiMetaFix:
|
||||||
def __init__(self, infile, outfile, asin):
|
def __init__(self, infile, outfile, asin, is_pdoc):
|
||||||
|
cdetype = b'EBOK'
|
||||||
|
if is_pdoc:
|
||||||
|
cdetype = b'PDOC'
|
||||||
|
|
||||||
shutil.copyfile(infile, outfile)
|
shutil.copyfile(infile, outfile)
|
||||||
f = open(outfile, "r+b")
|
f = open(outfile, "r+b")
|
||||||
self.datain = mmap.mmap(f.fileno(), 0)
|
self.datain = mmap.mmap(f.fileno(), 0)
|
||||||
@@ -147,7 +151,7 @@ class DualMobiMetaFix:
|
|||||||
rec0 = self.datain_rec0
|
rec0 = self.datain_rec0
|
||||||
rec0 = del_exth(rec0, 501)
|
rec0 = del_exth(rec0, 501)
|
||||||
rec0 = del_exth(rec0, 113)
|
rec0 = del_exth(rec0, 113)
|
||||||
rec0 = add_exth(rec0, 501, b'EBOK')
|
rec0 = add_exth(rec0, 501, cdetype)
|
||||||
rec0 = add_exth(rec0, 113, asin)
|
rec0 = add_exth(rec0, 113, asin)
|
||||||
replacesection(self.datain, 0, rec0)
|
replacesection(self.datain, 0, rec0)
|
||||||
|
|
||||||
@@ -174,7 +178,7 @@ class DualMobiMetaFix:
|
|||||||
rec0 = self.datain_kfrec0
|
rec0 = self.datain_kfrec0
|
||||||
rec0 = del_exth(rec0, 501)
|
rec0 = del_exth(rec0, 501)
|
||||||
rec0 = del_exth(rec0, 113)
|
rec0 = del_exth(rec0, 113)
|
||||||
rec0 = add_exth(rec0, 501, b'EBOK')
|
rec0 = add_exth(rec0, 501, cdetype)
|
||||||
rec0 = add_exth(rec0, 113, asin)
|
rec0 = add_exth(rec0, 113, asin)
|
||||||
replacesection(self.datain, datain_kf8, rec0)
|
replacesection(self.datain, datain_kf8, rec0)
|
||||||
|
|
||||||
|
|||||||
@@ -78,18 +78,29 @@ class ProfileData:
|
|||||||
PalleteNull = [
|
PalleteNull = [
|
||||||
]
|
]
|
||||||
|
|
||||||
Profiles = {
|
ProfilesKindleEBOK = {
|
||||||
'K1': ("Kindle 1", (600, 670), Palette4, 1.8),
|
'K1': ("Kindle 1", (600, 670), Palette4, 1.8),
|
||||||
'K11': ("Kindle 11", (1072, 1448), Palette16, 1.8),
|
|
||||||
'K2': ("Kindle 2", (600, 670), Palette15, 1.8),
|
'K2': ("Kindle 2", (600, 670), Palette15, 1.8),
|
||||||
|
'KDX': ("Kindle DX/DXG", (824, 1000), Palette16, 1.8),
|
||||||
'K34': ("Kindle Keyboard/Touch", (600, 800), Palette16, 1.8),
|
'K34': ("Kindle Keyboard/Touch", (600, 800), Palette16, 1.8),
|
||||||
'K578': ("Kindle", (600, 800), Palette16, 1.8),
|
'K578': ("Kindle", (600, 800), Palette16, 1.8),
|
||||||
'KDX': ("Kindle DX/DXG", (824, 1000), Palette16, 1.8),
|
|
||||||
'KPW': ("Kindle Paperwhite 1/2", (758, 1024), Palette16, 1.8),
|
'KPW': ("Kindle Paperwhite 1/2", (758, 1024), Palette16, 1.8),
|
||||||
'KV': ("Kindle Paperwhite 3/4/Voyage/Oasis", (1072, 1448), Palette16, 1.8),
|
'KV': ("Kindle Paperwhite 3/4/Voyage/Oasis", (1072, 1448), Palette16, 1.8),
|
||||||
|
}
|
||||||
|
|
||||||
|
ProfilesKindlePDOC = {
|
||||||
|
'KO': ("Kindle Oasis 2/3/Paperwhite 12/Colorsoft 12", (1264, 1680), Palette16, 1.8),
|
||||||
|
'K11': ("Kindle 11", (1072, 1448), Palette16, 1.8),
|
||||||
'KPW5': ("Kindle Paperwhite 5/Signature Edition", (1236, 1648), Palette16, 1.8),
|
'KPW5': ("Kindle Paperwhite 5/Signature Edition", (1236, 1648), Palette16, 1.8),
|
||||||
'KO': ("Kindle Oasis 2/3", (1264, 1680), Palette16, 1.8),
|
|
||||||
'KS': ("Kindle Scribe", (1860, 2480), Palette16, 1.8),
|
'KS': ("Kindle Scribe", (1860, 2480), Palette16, 1.8),
|
||||||
|
}
|
||||||
|
|
||||||
|
ProfilesKindle = {
|
||||||
|
**ProfilesKindleEBOK,
|
||||||
|
**ProfilesKindlePDOC
|
||||||
|
}
|
||||||
|
|
||||||
|
ProfilesKobo = {
|
||||||
'KoMT': ("Kobo Mini/Touch", (600, 800), Palette16, 1.8),
|
'KoMT': ("Kobo Mini/Touch", (600, 800), Palette16, 1.8),
|
||||||
'KoG': ("Kobo Glo", (768, 1024), Palette16, 1.8),
|
'KoG': ("Kobo Glo", (768, 1024), Palette16, 1.8),
|
||||||
'KoGHD': ("Kobo Glo HD", (1072, 1448), Palette16, 1.8),
|
'KoGHD': ("Kobo Glo HD", (1072, 1448), Palette16, 1.8),
|
||||||
@@ -105,6 +116,11 @@ class ProfileData:
|
|||||||
'KoF': ("Kobo Forma", (1440, 1920), Palette16, 1.8),
|
'KoF': ("Kobo Forma", (1440, 1920), Palette16, 1.8),
|
||||||
'KoS': ("Kobo Sage", (1440, 1920), Palette16, 1.8),
|
'KoS': ("Kobo Sage", (1440, 1920), Palette16, 1.8),
|
||||||
'KoE': ("Kobo Elipsa", (1404, 1872), Palette16, 1.8),
|
'KoE': ("Kobo Elipsa", (1404, 1872), Palette16, 1.8),
|
||||||
|
}
|
||||||
|
|
||||||
|
Profiles = {
|
||||||
|
**ProfilesKindle,
|
||||||
|
**ProfilesKobo,
|
||||||
'OTHER': ("Other", (0, 0), Palette16, 1.8),
|
'OTHER': ("Other", (0, 0), Palette16, 1.8),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,9 +19,12 @@
|
|||||||
import os.path
|
import os.path
|
||||||
import psutil
|
import psutil
|
||||||
|
|
||||||
|
from . import image
|
||||||
|
|
||||||
|
|
||||||
class Kindle:
|
class Kindle:
|
||||||
def __init__(self):
|
def __init__(self, profile):
|
||||||
|
self.profile = profile
|
||||||
self.path = self.findDevice()
|
self.path = self.findDevice()
|
||||||
if self.path:
|
if self.path:
|
||||||
self.coverSupport = self.checkThumbnails()
|
self.coverSupport = self.checkThumbnails()
|
||||||
@@ -29,9 +32,11 @@ class Kindle:
|
|||||||
self.coverSupport = False
|
self.coverSupport = False
|
||||||
|
|
||||||
def findDevice(self):
|
def findDevice(self):
|
||||||
|
if self.profile in image.ProfileData.ProfilesKindlePDOC.keys():
|
||||||
|
return False
|
||||||
for drive in reversed(psutil.disk_partitions(False)):
|
for drive in reversed(psutil.disk_partitions(False)):
|
||||||
if (drive[2] == 'FAT32' and drive[3] == 'rw,removable') or \
|
if (drive[2] == 'FAT32' and drive[3] == 'rw,removable') or \
|
||||||
(drive[2] in ('vfat', 'msdos', 'FAT') and 'rw' in drive[3]):
|
(drive[2] in ('vfat', 'msdos', 'FAT', 'apfs') and 'rw' in drive[3]):
|
||||||
if os.path.isdir(os.path.join(drive[1], 'system')) and \
|
if os.path.isdir(os.path.join(drive[1], 'system')) and \
|
||||||
os.path.isdir(os.path.join(drive[1], 'documents')):
|
os.path.isdir(os.path.join(drive[1], 'documents')):
|
||||||
return drive[1]
|
return drive[1]
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import os
|
|||||||
from hashlib import md5
|
from hashlib import md5
|
||||||
from html.parser import HTMLParser
|
from html.parser import HTMLParser
|
||||||
import subprocess
|
import subprocess
|
||||||
from distutils.version import StrictVersion
|
from packaging.version import Version
|
||||||
from re import split
|
from re import split
|
||||||
import sys
|
import sys
|
||||||
from traceback import format_tb
|
from traceback import format_tb
|
||||||
@@ -103,7 +103,7 @@ def dependencyCheck(level):
|
|||||||
if level > 2:
|
if level > 2:
|
||||||
try:
|
try:
|
||||||
from PySide6.QtCore import qVersion as qtVersion
|
from PySide6.QtCore import qVersion as qtVersion
|
||||||
if StrictVersion('6.5.1') > StrictVersion(qtVersion()):
|
if Version('6.5.1') > Version(qtVersion()):
|
||||||
missing.append('PySide 6.5.1+')
|
missing.append('PySide 6.5.1+')
|
||||||
except ImportError:
|
except ImportError:
|
||||||
missing.append('PySide 6.5.1+')
|
missing.append('PySide 6.5.1+')
|
||||||
@@ -114,7 +114,7 @@ def dependencyCheck(level):
|
|||||||
if level > 1:
|
if level > 1:
|
||||||
try:
|
try:
|
||||||
from psutil import __version__ as psutilVersion
|
from psutil import __version__ as psutilVersion
|
||||||
if StrictVersion('5.0.0') > StrictVersion(psutilVersion):
|
if Version('5.0.0') > Version(psutilVersion):
|
||||||
missing.append('psutil 5.0.0+')
|
missing.append('psutil 5.0.0+')
|
||||||
except ImportError:
|
except ImportError:
|
||||||
missing.append('psutil 5.0.0+')
|
missing.append('psutil 5.0.0+')
|
||||||
@@ -123,13 +123,13 @@ def dependencyCheck(level):
|
|||||||
from slugify import __version__ as slugifyVersion
|
from slugify import __version__ as slugifyVersion
|
||||||
if isinstance(slugifyVersion, ModuleType):
|
if isinstance(slugifyVersion, ModuleType):
|
||||||
slugifyVersion = slugifyVersion.__version__
|
slugifyVersion = slugifyVersion.__version__
|
||||||
if StrictVersion('1.2.1') > StrictVersion(slugifyVersion):
|
if Version('1.2.1') > Version(slugifyVersion):
|
||||||
missing.append('python-slugify 1.2.1+')
|
missing.append('python-slugify 1.2.1+')
|
||||||
except ImportError:
|
except ImportError:
|
||||||
missing.append('python-slugify 1.2.1+')
|
missing.append('python-slugify 1.2.1+')
|
||||||
try:
|
try:
|
||||||
from PIL import __version__ as pillowVersion
|
from PIL import __version__ as pillowVersion
|
||||||
if StrictVersion('5.2.0') > StrictVersion(pillowVersion):
|
if Version('5.2.0') > Version(pillowVersion):
|
||||||
missing.append('Pillow 5.2.0+')
|
missing.append('Pillow 5.2.0+')
|
||||||
except ImportError:
|
except ImportError:
|
||||||
missing.append('Pillow 5.2.0+')
|
missing.append('Pillow 5.2.0+')
|
||||||
@@ -137,7 +137,7 @@ def dependencyCheck(level):
|
|||||||
print('ERROR: ' + ', '.join(missing) + ' is not installed!')
|
print('ERROR: ' + ', '.join(missing) + ' is not installed!')
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
def subprocess_run_silent(command, **kwargs):
|
def subprocess_run(command, **kwargs):
|
||||||
if (os.name == 'nt'):
|
if (os.name == 'nt'):
|
||||||
kwargs.setdefault('creationflags', subprocess.CREATE_NO_WINDOW)
|
kwargs.setdefault('creationflags', subprocess.CREATE_NO_WINDOW)
|
||||||
return subprocess.run(command, **kwargs)
|
return subprocess.run(command, **kwargs)
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ psutil>=5.9.5
|
|||||||
requests>=2.31.0
|
requests>=2.31.0
|
||||||
python-slugify>=1.2.1
|
python-slugify>=1.2.1
|
||||||
raven>=6.0.0
|
raven>=6.0.0
|
||||||
|
packaging>=23.2
|
||||||
mozjpeg-lossless-optimization>=1.1.2
|
mozjpeg-lossless-optimization>=1.1.2
|
||||||
natsort[fast]>=8.4.0
|
natsort[fast]>=8.4.0
|
||||||
distro>=1.8.0
|
distro>=1.8.0
|
||||||
|
|||||||
9
setup.py
9
setup.py
@@ -14,7 +14,6 @@ import os
|
|||||||
import platform
|
import platform
|
||||||
import sys
|
import sys
|
||||||
import setuptools
|
import setuptools
|
||||||
import distutils.cmd
|
|
||||||
from kindlecomicconverter import __version__
|
from kindlecomicconverter import __version__
|
||||||
|
|
||||||
NAME = 'KindleComicConverter'
|
NAME = 'KindleComicConverter'
|
||||||
@@ -23,7 +22,7 @@ VERSION = __version__
|
|||||||
|
|
||||||
|
|
||||||
# noinspection PyUnresolvedReferences
|
# noinspection PyUnresolvedReferences
|
||||||
class BuildBinaryCommand(distutils.cmd.Command):
|
class BuildBinaryCommand(setuptools.Command):
|
||||||
description = 'build binary release'
|
description = 'build binary release'
|
||||||
user_options = []
|
user_options = []
|
||||||
|
|
||||||
@@ -37,16 +36,16 @@ class BuildBinaryCommand(distutils.cmd.Command):
|
|||||||
def run(self):
|
def run(self):
|
||||||
VERSION = __version__
|
VERSION = __version__
|
||||||
if sys.platform == 'darwin':
|
if sys.platform == 'darwin':
|
||||||
os.system('pyinstaller -y -D -i icons/comic2ebook.icns -n "Kindle Comic Converter" -w -s kcc.py')
|
os.system('pyinstaller --hidden-import=_cffi_backend -y -D -i icons/comic2ebook.icns -n "Kindle Comic Converter" -w -s kcc.py')
|
||||||
# TODO /usr/bin/codesign --force -s "$MACOS_CERTIFICATE_NAME" --options runtime dist/Applications/Kindle\ Comic\ Converter.app -v
|
# TODO /usr/bin/codesign --force -s "$MACOS_CERTIFICATE_NAME" --options runtime dist/Applications/Kindle\ Comic\ Converter.app -v
|
||||||
os.system(f'appdmg kcc.json dist/kcc_macos_{platform.processor()}_{VERSION}.dmg')
|
os.system(f'appdmg kcc.json dist/kcc_macos_{platform.processor()}_{VERSION}.dmg')
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
elif sys.platform == 'win32':
|
elif sys.platform == 'win32':
|
||||||
os.system('pyinstaller -y -F -i icons\\comic2ebook.ico -n KCC_' + VERSION + ' -w --noupx kcc.py')
|
os.system('pyinstaller --hidden-import=_cffi_backend -y -F -i icons\\comic2ebook.ico -n KCC_' + VERSION + ' -w --noupx kcc.py')
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
elif sys.platform == 'linux':
|
elif sys.platform == 'linux':
|
||||||
os.system(
|
os.system(
|
||||||
'pyinstaller --hidden-import=queue -y -F -i icons/comic2ebook.ico -n kcc_linux_' + VERSION + ' kcc.py')
|
'pyinstaller --hidden-import=_cffi_backend --hidden-import=queue -y -F -i icons/comic2ebook.ico -n kcc_linux_' + VERSION + ' kcc.py')
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
else:
|
else:
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|||||||
Reference in New Issue
Block a user