mirror of
https://github.com/ciromattia/kcc
synced 2026-04-16 14:08:45 +00:00
Compare commits
39 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b2aaa9e8b0 | ||
|
|
e8502f008a | ||
|
|
169a41e7d2 | ||
|
|
57cf669cdd | ||
|
|
0b4f089b8e | ||
|
|
7eb985337c | ||
|
|
d62690e8bf | ||
|
|
3988f2012f | ||
|
|
6cdd9d5909 | ||
|
|
c29a4beac9 | ||
|
|
5f7bdef325 | ||
|
|
54b5d698ee | ||
|
|
a99c63acea | ||
|
|
251df2e7ba | ||
|
|
b1379b7c59 | ||
|
|
983bde1691 | ||
|
|
e79e5a311c | ||
|
|
39c73dbc0f | ||
|
|
915c9389ef | ||
|
|
89d887710e | ||
|
|
2e9bc5381a | ||
|
|
ab1ce158c7 | ||
|
|
6278adfb25 | ||
|
|
6e6c13047e | ||
|
|
b528dab711 | ||
|
|
2ffefee928 | ||
|
|
a5e5407363 | ||
|
|
da1ba64bd2 | ||
|
|
6dcaf9a6d1 | ||
|
|
3090a47f20 | ||
|
|
1e537915d4 | ||
|
|
7273ca25b8 | ||
|
|
68da853e42 | ||
|
|
431862a2e9 | ||
|
|
65062f8984 | ||
|
|
8122fa1e45 | ||
|
|
13fedff77b | ||
|
|
61b1207a3e | ||
|
|
60e9f075b8 |
31
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
31
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
Add a screenshot of your KCC settings.
|
||||
|
||||
**Desktop (please complete the following information):**
|
||||
- OS: [e.g. macOS, Linux, Windows 11]
|
||||
- Device [e.g. Kindle Paperwhite 3rd gen, Kobo Libra 2]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
8
.github/workflows/codeql-analysis.yml
vendored
8
.github/workflows/codeql-analysis.yml
vendored
@@ -38,11 +38,11 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
@@ -56,7 +56,7 @@ jobs:
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
uses: github/codeql-action/autobuild@v3
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||
@@ -69,6 +69,6 @@ jobs:
|
||||
# ./location_of_script_within_repo/buildscript.sh
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
uses: github/codeql-action/analyze@v3
|
||||
with:
|
||||
category: "/language:${{matrix.language}}"
|
||||
|
||||
6
.github/workflows/package-linux.yml
vendored
6
.github/workflows/package-linux.yml
vendored
@@ -25,9 +25,9 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: 3.11
|
||||
cache: 'pip'
|
||||
@@ -59,7 +59,7 @@ jobs:
|
||||
env:
|
||||
UPDATE_INFO: gh-releases-zsync|ciromattia|kcc|latest|*x86_64.AppImage.zsync
|
||||
- name: upload artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: AppImage
|
||||
path: './*.AppImage*'
|
||||
|
||||
8
.github/workflows/package-macos.yml
vendored
8
.github/workflows/package-macos.yml
vendored
@@ -25,9 +25,9 @@ jobs:
|
||||
build:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: 3.11
|
||||
cache: 'pip'
|
||||
@@ -66,7 +66,7 @@ jobs:
|
||||
# apply provisioning profile
|
||||
mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
|
||||
cp $PP_PATH ~/Library/MobileDevice/Provisioning\ Profiles
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 16
|
||||
- run: npm install -g appdmg
|
||||
@@ -75,7 +75,7 @@ jobs:
|
||||
run: |
|
||||
python setup.py build_binary
|
||||
- name: upload build
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: mac-os-build
|
||||
path: dist/*.dmg
|
||||
|
||||
@@ -12,7 +12,7 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
# - name: Set up Python
|
||||
# uses: actions/setup-python@v4
|
||||
# with:
|
||||
@@ -44,10 +44,10 @@ jobs:
|
||||
run: |
|
||||
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-c2p.exe dist/windows/kcc-c2p_${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
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: windows-build
|
||||
path: dist/windows/*.exe
|
||||
@@ -60,4 +60,4 @@ jobs:
|
||||
files: |
|
||||
CHANGELOG.md
|
||||
LICENSE.txt
|
||||
dist/windows/*.exe
|
||||
dist/windows/*.exe
|
||||
|
||||
10
.github/workflows/package-windows.yml
vendored
10
.github/workflows/package-windows.yml
vendored
@@ -25,11 +25,11 @@ jobs:
|
||||
build:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: 3.11
|
||||
python-version: 3.8
|
||||
cache: 'pip'
|
||||
- name: Install dependencies
|
||||
env:
|
||||
@@ -42,7 +42,7 @@ jobs:
|
||||
run: |
|
||||
python setup.py build_binary
|
||||
- name: upload build
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: windows-build
|
||||
path: dist/*.exe
|
||||
@@ -55,4 +55,4 @@ jobs:
|
||||
files: |
|
||||
CHANGELOG.md
|
||||
LICENSE.txt
|
||||
dist/*.exe
|
||||
dist/*.exe
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -11,3 +11,4 @@ KindleComicConverter*.egg-info/
|
||||
/venv/
|
||||
/kindlegen*
|
||||
/kcc.bat
|
||||
.DS_Store
|
||||
|
||||
63
README.md
63
README.md
@@ -28,11 +28,11 @@ If you find **KCC** valuable you can consider donating to the authors:
|
||||
- Paweł Jastrzębski:
|
||||
- [](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YTTJ4LK2JDHPS)
|
||||
- [](https://jastrzeb.ski/donate/)
|
||||
- Alex Xu
|
||||
- [](https://www.paypal.com/donate/?business=QFJVE7A6LCP6U&no_recurring=0&item_name=Kindle+Comic+Converter¤cy_code=USD)
|
||||
|
||||
|
||||
## INSTALLATION
|
||||
|
||||
### DOWNLOADS
|
||||
## DOWNLOADS
|
||||
|
||||
- **https://github.com/ciromattia/kcc/releases**
|
||||
|
||||
@@ -42,45 +42,41 @@ You probably want either
|
||||
- `kcc_*.*.*.exe` (Windows)
|
||||
- `KindleComicConverter_osx_*.*.*.dmg` (Mac)
|
||||
|
||||
Installation Wiki: https://github.com/ciromattia/kcc/wiki/Installation
|
||||
The `c2e` and `c2p` versions are command line tools for power users.
|
||||
|
||||
- flatpak : https://flathub.org/apps/details/io.github.ciromattia.kcc
|
||||
- Docker: https://github.com/ciromattia/kcc/pkgs/container/kcc
|
||||
On Windows 11, you may need to run in compatibility mode for an older Windows version.
|
||||
|
||||
### DEPENDENCIES
|
||||
Following software is required to run Linux version of **KCC** and/or bare sources:
|
||||
- Python 3.3+
|
||||
- [PyQt5](https://pypi.python.org/pypi/PyQt5) 5.6.0+ (only needed for GUI)
|
||||
- [Pillow](https://pypi.python.org/pypi/Pillow/) 4.0.0+ (5.2.0+ needed for WebP support)
|
||||
- [psutil](https://pypi.python.org/pypi/psutil) 5.0.0+
|
||||
- [python-slugify](https://pypi.python.org/pypi/python-slugify) 1.2.1+, <8.0.0
|
||||
- [raven](https://pypi.python.org/pypi/raven) 6.0.0+ (only needed for GUI)
|
||||
On Mac, right click open to get past the security warning.
|
||||
|
||||
On Debian based distributions these two commands should install all needed dependencies:
|
||||
For flatpak, Docker, and AppImage versions, refer to the wiki: https://github.com/ciromattia/kcc/wiki/Installation
|
||||
|
||||
## PREREQUISITES
|
||||
|
||||
You'll need to install various tools to access important but optional features.
|
||||
|
||||
```bash
|
||||
$ sudo apt-get install -y python3 python3-dev libpng-dev libjpeg-dev p7zip-full p7zip-rar unrar-free libgl1 python3-pyqt5 && \
|
||||
python -m pip install --upgrade pip && \
|
||||
python -m pip install --upgrade -r requirements.txt
|
||||
The installation process has been greatly streamlined. No need to add 7z to PATH or locate KindleGen from the internet and put it in a special folder with KCC. Just run it and KCC will tell you what to install.
|
||||
|
||||
### 7-Zip
|
||||
|
||||
#### Windows 7-Zip
|
||||
|
||||
First install 7z from https://www.7-zip.org/ or with command line:
|
||||
```
|
||||
winget install --id 7zip.7zip
|
||||
```
|
||||
|
||||
|
||||
#### Optional dependencies
|
||||
- Qt platform integration plugin for Deepin Desktop Environment
|
||||
```bash
|
||||
$ sudo apt-get install qt5dxcb-plugin
|
||||
#### macOS 7-Zip/Unar
|
||||
with [Homebrew](https://brew.sh/) installed
|
||||
```
|
||||
brew install p7zip
|
||||
brew install unar
|
||||
```
|
||||
|
||||
- KindleGen ~~[(deprecated link)](http://www.amazon.com/gp/feature.html?ie=UTF8&docId=1000765211)~~ v2.9+ (For MOBI generation)
|
||||
- should be placed in a directory reachable by your _PATH_ or in _KCC_ directory
|
||||
- `KindleGen` can be found in [Kindle Previewer](https://www.amazon.com/Kindle-Previewer/b?ie=UTF8&node=21381691011)
|
||||
- `KindleGen` can be also be found in [Kindle Comic Creator](https://www.amazon.com/b?node=23496309011)
|
||||
- [7z](http://www.7-zip.org/download.html) *(For CBZ/ZIP, CBR/RAR, 7z/CB7 support)*
|
||||
- Unrar (no rar in 7z on Fedora)
|
||||
### KindleGen
|
||||
|
||||
#### Windows / macOS KindleGen
|
||||
|
||||
Install [Kindle Previewer 3 (KP3)](https://www.amazon.com/Kindle-Previewer/b?ie=UTF8&node=21381691011). KCC will automatically detect KindleGen from it.
|
||||
|
||||
## INPUT FORMATS
|
||||
**KCC** can understand and convert, at the moment, the following input types:
|
||||
@@ -215,7 +211,12 @@ OTHER:
|
||||
```
|
||||
|
||||
## CREDITS
|
||||
**KCC** is made by [Ciro Mattia Gonano](http://github.com/ciromattia), [Paweł Jastrzębski](http://github.com/AcidWeb) and [Darodi](http://github.com/darodi) .
|
||||
**KCC** is made by
|
||||
|
||||
- [Ciro Mattia Gonano](http://github.com/ciromattia)
|
||||
- [Paweł Jastrzębski](http://github.com/AcidWeb)
|
||||
- [Darodi](http://github.com/darodi)
|
||||
- [Alex Xu](http://github.com/axu2)
|
||||
|
||||
This script born as a cross-platform alternative to `KindleComicParser` by **Dc5e** (published [here](http://www.mobileread.com/forums/showthread.php?t=192783)).
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ dependencies:
|
||||
- python-slugify>=1.2.1
|
||||
- raven>=6.0.0
|
||||
- distro
|
||||
- natsort[fast]>=8.4.0
|
||||
- pip
|
||||
- pip:
|
||||
- mozjpeg-lossless-optimization>=1.1.2
|
||||
|
||||
@@ -22,7 +22,7 @@ import sys
|
||||
|
||||
if sys.version_info < (3, 8, 0):
|
||||
print('ERROR: This is a Python 3.8+ script!')
|
||||
exit(1)
|
||||
sys.exit(1)
|
||||
|
||||
from multiprocessing import freeze_support, set_start_method
|
||||
from kindlecomicconverter.startup import startC2E
|
||||
|
||||
@@ -22,7 +22,7 @@ import sys
|
||||
|
||||
if sys.version_info < (3, 8, 0):
|
||||
print('ERROR: This is a Python 3.8+ script!')
|
||||
exit(1)
|
||||
sys.exit(1)
|
||||
|
||||
from multiprocessing import freeze_support, set_start_method
|
||||
from kindlecomicconverter.startup import startC2P
|
||||
|
||||
2
kcc.py
2
kcc.py
@@ -22,7 +22,7 @@ import sys
|
||||
|
||||
if sys.version_info < (3, 8, 0):
|
||||
print('ERROR: This is a Python 3.8+ script!')
|
||||
exit(1)
|
||||
sys.exit(1)
|
||||
|
||||
# OS specific workarounds
|
||||
import os
|
||||
|
||||
@@ -16,25 +16,25 @@
|
||||
# OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
# PERFORMANCE OF THIS SOFTWARE.
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
from urllib.parse import unquote
|
||||
from urllib.request import urlretrieve, urlopen
|
||||
from time import sleep
|
||||
from shutil import move, rmtree
|
||||
from subprocess import STDOUT, PIPE
|
||||
|
||||
import requests
|
||||
# noinspection PyUnresolvedReferences
|
||||
from PyQt5 import QtGui, QtCore, QtWidgets, QtNetwork
|
||||
from xml.sax.saxutils import escape
|
||||
from psutil import Popen, Process
|
||||
from psutil import Process
|
||||
from copy import copy
|
||||
from distutils.version import StrictVersion
|
||||
from raven import Client
|
||||
from tempfile import gettempdir
|
||||
from .shared import md5Checksum, HTMLStripper, sanitizeTrace, walkLevel
|
||||
from .shared import md5Checksum, HTMLStripper, sanitizeTrace, walkLevel, subprocess_run_silent
|
||||
from . import __version__
|
||||
from . import comic2ebook
|
||||
from . import metadata
|
||||
@@ -139,10 +139,7 @@ class VersionThread(QtCore.QThread):
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
last_version_url = urlopen("https://api.github.com/repos/ciromattia/kcc/releases/latest")
|
||||
data = last_version_url.read()
|
||||
encoding = last_version_url.info().get_content_charset('utf-8')
|
||||
json_parser = json.loads(data.decode(encoding))
|
||||
json_parser = requests.get("https://api.github.com/repos/ciromattia/kcc/releases/latest").json()
|
||||
|
||||
html_url = json_parser["html_url"]
|
||||
latest_version = json_parser["tag_name"]
|
||||
@@ -752,7 +749,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
|
||||
def display_kindlegen_missing(self):
|
||||
self.addMessage(
|
||||
'<a href="https://github.com/ciromattia/kcc/wiki/Installation#kindlegen"><b>Cannot find KindleGen</b></a>: MOBI conversion is unavailable!',
|
||||
'<a href="https://github.com/ciromattia/kcc#kindlegen"><b>Install KindleGen (link)</b></a> to enable MOBI conversion for Kindles!',
|
||||
'error'
|
||||
)
|
||||
|
||||
@@ -839,32 +836,21 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
os.chmod('/usr/local/bin/kindlegen', 0o755)
|
||||
except Exception:
|
||||
pass
|
||||
kindleGenExitCode = Popen('kindlegen -locale en', stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
|
||||
kindleGenExitCode.communicate()
|
||||
if kindleGenExitCode.returncode == 0:
|
||||
try:
|
||||
versionCheck = subprocess_run_silent(['kindlegen', '-locale', 'en'], stdout=PIPE, stderr=STDOUT, encoding='UTF-8')
|
||||
self.kindleGen = True
|
||||
versionCheck = Popen('kindlegen -locale en', stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
|
||||
for line in versionCheck.stdout:
|
||||
line = line.decode("utf-8")
|
||||
for line in versionCheck.stdout.splitlines():
|
||||
if 'Amazon kindlegen' in line:
|
||||
versionCheck = line.split('V')[1].split(' ')[0]
|
||||
if StrictVersion(versionCheck) < StrictVersion('2.9'):
|
||||
self.addMessage('Your <a href="https://www.amazon.com/b?node=23496309011">KindleGen</a>'
|
||||
' is outdated! MOBI conversion might fail.', 'warning')
|
||||
break
|
||||
where_command = 'where kindlegen.exe'
|
||||
if os.name == 'posix':
|
||||
where_command = 'which kindlegen'
|
||||
process = subprocess.run(where_command, stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
|
||||
locations = process.stdout.decode('utf-8').split('\n')
|
||||
self.addMessage(f"<b>KindleGen Found:</b> {locations[0]}", 'info')
|
||||
else:
|
||||
except FileNotFoundError:
|
||||
self.kindleGen = False
|
||||
if startup:
|
||||
self.display_kindlegen_missing()
|
||||
|
||||
|
||||
|
||||
def __init__(self, kccapp, kccwindow):
|
||||
global APP, MW, GUI
|
||||
APP = kccapp
|
||||
@@ -943,7 +929,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
'DefaultUpscale': True, 'Label': 'KV'},
|
||||
"Kindle PW 1/2": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
|
||||
'DefaultUpscale': False, 'Label': 'KPW'},
|
||||
"Kindle": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
|
||||
"Kindle 4/5/7/8/10": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
|
||||
'DefaultUpscale': False, 'Label': 'K578'},
|
||||
"Kindle DX/DXG": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 2,
|
||||
'DefaultUpscale': False, 'Label': 'KDX'},
|
||||
@@ -1011,7 +997,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
"Kindle Voyage",
|
||||
"Kindle 2",
|
||||
"Kindle 1",
|
||||
"Kindle",
|
||||
"Kindle 4/5/7/8/10",
|
||||
"Separator",
|
||||
"Kobo Aura",
|
||||
"Kobo Aura ONE",
|
||||
@@ -1039,14 +1025,13 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
self.addMessage('Since you are a new user of <b>KCC</b> please see few '
|
||||
'<a href="https://github.com/ciromattia/kcc/wiki/Important-tips">important tips</a>.',
|
||||
'info')
|
||||
process = Popen('7z', stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
|
||||
process.communicate()
|
||||
if process.returncode == 0 or process.returncode == 7:
|
||||
try:
|
||||
subprocess_run_silent(['7z'], stdout=PIPE, stderr=STDOUT)
|
||||
self.sevenzip = True
|
||||
else:
|
||||
except FileNotFoundError:
|
||||
self.sevenzip = False
|
||||
self.addMessage('<a href="https://github.com/ciromattia/kcc/wiki/Installation#7-zip">Cannot find 7z</a>!'
|
||||
' CBZ/CBR/ZIP/etc processing disabled.', 'warning')
|
||||
self.addMessage('<a href="https://github.com/ciromattia/kcc#7-zip">Install 7z (link)</a>'
|
||||
' to enable CBZ/CBR/ZIP/etc processing.', 'warning')
|
||||
self.detectKindleGen(True)
|
||||
|
||||
APP.messageFromOtherInstance.connect(self.handleMessage)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
__version__ = '5.6.3'
|
||||
__version__ = '5.6.5'
|
||||
__license__ = 'ISC'
|
||||
__copyright__ = '2012-2022, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>, darodi'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
@@ -19,6 +19,9 @@
|
||||
#
|
||||
|
||||
import os
|
||||
import pathlib
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
from argparse import ArgumentParser
|
||||
from time import strftime, gmtime
|
||||
@@ -31,16 +34,17 @@ from tempfile import mkdtemp, gettempdir, TemporaryFile
|
||||
from shutil import move, copytree, rmtree, copyfile
|
||||
from multiprocessing import Pool
|
||||
from uuid import uuid4
|
||||
from natsort import os_sorted
|
||||
from slugify import slugify as slugify_ext
|
||||
from PIL import Image
|
||||
from subprocess import STDOUT, PIPE
|
||||
from psutil import Popen, virtual_memory, disk_usage
|
||||
from psutil import virtual_memory, disk_usage
|
||||
from html import escape as hescape
|
||||
try:
|
||||
from PyQt5 import QtCore
|
||||
except ImportError:
|
||||
QtCore = None
|
||||
from .shared import md5Checksum, getImageFileName, walkSort, walkLevel, sanitizeTrace
|
||||
from .shared import md5Checksum, getImageFileName, walkSort, walkLevel, sanitizeTrace, subprocess_run_silent
|
||||
from . import comic2panel
|
||||
from . import image
|
||||
from . import comicarchive
|
||||
@@ -671,11 +675,9 @@ def getOutputFilename(srcpath, wantedname, ext, tomenumber):
|
||||
filename = srcpath + tomenumber + ext
|
||||
else:
|
||||
if 'Ko' in options.profile and options.format == 'EPUB':
|
||||
path = srcpath.split(os.path.sep)
|
||||
path[-1] = ''.join(e for e in path[-1].split('.')[0] if e.isalnum()) + tomenumber + ext
|
||||
if not path[-1].split('.')[0]:
|
||||
path[-1] = 'KCCPlaceholder' + tomenumber + ext
|
||||
filename = os.path.sep.join(path)
|
||||
src = pathlib.Path(srcpath)
|
||||
name = re.sub(r'\W+', '_', src.stem) + tomenumber + ext
|
||||
filename = src.with_name(name)
|
||||
else:
|
||||
filename = os.path.splitext(srcpath)[0] + tomenumber + ext
|
||||
if os.path.isfile(filename):
|
||||
@@ -726,7 +728,7 @@ def getComicInfo(path, originalpath):
|
||||
options.authors.sort()
|
||||
else:
|
||||
options.authors = ['KCC']
|
||||
if xml.data['Bookmarks']:
|
||||
if xml.data['Bookmarks'] and options.batchsplit == 0:
|
||||
options.chapters = xml.data['Bookmarks']
|
||||
if xml.data['Summary']:
|
||||
options.summary = hescape(xml.data['Summary'])
|
||||
@@ -761,7 +763,7 @@ def getPanelViewSize(deviceres, size):
|
||||
def sanitizeTree(filetree):
|
||||
chapterNames = {}
|
||||
for root, dirs, files in os.walk(filetree, False):
|
||||
for i, name in enumerate(sorted(files)):
|
||||
for i, name in enumerate(os_sorted(files)):
|
||||
splitname = os.path.splitext(name)
|
||||
|
||||
# file needs kcc at front AND back to avoid renaming issues
|
||||
@@ -1101,17 +1103,17 @@ def checkTools(source):
|
||||
source = source.upper()
|
||||
if source.endswith('.CB7') or source.endswith('.7Z') or source.endswith('.RAR') or source.endswith('.CBR') or \
|
||||
source.endswith('.ZIP') or source.endswith('.CBZ'):
|
||||
process = Popen('7z', stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
|
||||
process.communicate()
|
||||
if process.returncode != 0 and process.returncode != 7:
|
||||
try:
|
||||
subprocess_run_silent(['7z'], stdout=PIPE, stderr=STDOUT)
|
||||
except FileNotFoundError:
|
||||
print('ERROR: 7z is missing!')
|
||||
exit(1)
|
||||
sys.exit(1)
|
||||
if options.format == 'MOBI':
|
||||
kindleGenExitCode = Popen('kindlegen -locale en', stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
|
||||
kindleGenExitCode.communicate()
|
||||
if kindleGenExitCode.returncode != 0:
|
||||
try:
|
||||
subprocess_run_silent(['kindlegen', '-locale', 'en'], stdout=PIPE, stderr=STDOUT)
|
||||
except FileNotFoundError:
|
||||
print('ERROR: KindleGen is missing!')
|
||||
exit(1)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def checkPre(source):
|
||||
@@ -1265,10 +1267,9 @@ def makeMOBIWorker(item):
|
||||
kindlegenError = ''
|
||||
try:
|
||||
if os.path.getsize(item) < 629145600:
|
||||
output = Popen('kindlegen -dont_append_source -locale en "' + item + '"',
|
||||
stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
|
||||
for line in output.stdout:
|
||||
line = line.decode('utf-8')
|
||||
output = subprocess_run_silent(['kindlegen', '-dont_append_source', '-locale', 'en', item],
|
||||
stdout=PIPE, stderr=STDOUT, encoding='UTF-8')
|
||||
for line in output.stdout.splitlines():
|
||||
# ERROR: Generic error
|
||||
if "Error(" in line:
|
||||
kindlegenErrorCode = 1
|
||||
@@ -1279,7 +1280,6 @@ def makeMOBIWorker(item):
|
||||
if kindlegenErrorCode > 0:
|
||||
break
|
||||
if ":I1036: Mobi file built successfully" in line:
|
||||
output.communicate()
|
||||
break
|
||||
else:
|
||||
# ERROR: EPUB too big
|
||||
|
||||
@@ -22,11 +22,13 @@ import os
|
||||
import platform
|
||||
import subprocess
|
||||
import distro
|
||||
from psutil import Popen
|
||||
from shutil import move
|
||||
from subprocess import STDOUT, PIPE
|
||||
from xml.dom.minidom import parseString
|
||||
from xml.parsers.expat import ExpatError
|
||||
from .shared import subprocess_run_silent
|
||||
|
||||
EXTRACTION_ERROR = 'Failed to extract archive. Try extracting file outside of KCC.'
|
||||
|
||||
|
||||
class ComicArchive:
|
||||
@@ -35,70 +37,54 @@ class ComicArchive:
|
||||
self.type = None
|
||||
if not os.path.isfile(self.filepath):
|
||||
raise OSError('File not found.')
|
||||
process = Popen('7z l -y -p1 "' + self.filepath + '"', stderr=STDOUT, stdout=PIPE, stdin=PIPE, shell=True)
|
||||
for line in process.stdout:
|
||||
process = subprocess_run_silent(['7z', 'l', '-y', '-p1', self.filepath], stderr=STDOUT, stdout=PIPE)
|
||||
for line in process.stdout.splitlines():
|
||||
if b'Type =' in line:
|
||||
self.type = line.rstrip().decode().split(' = ')[1].upper()
|
||||
break
|
||||
process.communicate()
|
||||
if process.returncode != 0 and distro.id() == 'fedora':
|
||||
process = Popen('unrar l -y -p1 "' + self.filepath + '"', stderr=STDOUT, stdout=PIPE, stdin=PIPE, shell=True)
|
||||
for line in process.stdout:
|
||||
process = subprocess_run_silent(['unrar', 'l', '-y', '-p1', self.filepath], stderr=STDOUT, stdout=PIPE)
|
||||
for line in process.stdout.splitlines():
|
||||
if b'Details: ' in line:
|
||||
self.type = line.rstrip().decode().split(' ')[1].upper()
|
||||
break
|
||||
process.communicate()
|
||||
if process.returncode != 0:
|
||||
raise OSError('Archive is corrupted or encrypted.')
|
||||
elif self.type not in ['7Z', 'RAR', 'RAR5', 'ZIP']:
|
||||
raise OSError('Unsupported archive format.')
|
||||
elif self.type not in ['7Z', 'RAR', 'RAR5', 'ZIP']:
|
||||
raise OSError('Unsupported archive format.')
|
||||
raise OSError(EXTRACTION_ERROR)
|
||||
|
||||
def extract(self, targetdir):
|
||||
if not os.path.isdir(targetdir):
|
||||
raise OSError('Target directory doesn\'t exist.')
|
||||
process = Popen('7z x -y -xr!__MACOSX -xr!.DS_Store -xr!thumbs.db -xr!Thumbs.db -o"' + targetdir + '" "' +
|
||||
self.filepath + '"', stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
|
||||
process.communicate()
|
||||
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':
|
||||
process = Popen('unrar x -y -x__MACOSX -x.DS_Store -xthumbs.db -xThumbs.db "' + self.filepath + '" "' +
|
||||
targetdir + '"', stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
|
||||
process.communicate()
|
||||
process = subprocess_run_silent(['unrar', 'x', '-y', '-x__MACOSX', '-x.DS_Store', '-xthumbs.db', '-xThumbs.db', self.filepath, targetdir]
|
||||
, stdout=PIPE, stderr=STDOUT)
|
||||
if process.returncode != 0:
|
||||
raise OSError('Failed to extract archive.')
|
||||
raise OSError(EXTRACTION_ERROR)
|
||||
elif process.returncode != 0 and platform.system() == 'Darwin':
|
||||
process = subprocess.run(f"unar '{self.filepath}' -f -o '{targetdir}'",
|
||||
stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
|
||||
if process.returncode != 0:
|
||||
raise Exception(process.stdout.decode("utf-8"))
|
||||
process = subprocess_run_silent(['unar', self.filepath, '-f', '-o', targetdir],
|
||||
stdout=PIPE, stderr=STDOUT)
|
||||
elif process.returncode != 0:
|
||||
raise OSError('Failed to extract archive. Check if p7zip-rar is installed.')
|
||||
raise OSError(EXTRACTION_ERROR)
|
||||
tdir = os.listdir(targetdir)
|
||||
if 'ComicInfo.xml' in tdir:
|
||||
tdir.remove('ComicInfo.xml')
|
||||
if len(tdir) == 1 and os.path.isdir(os.path.join(targetdir, tdir[0])):
|
||||
for f in os.listdir(os.path.join(targetdir, tdir[0])):
|
||||
move(os.path.join(targetdir, tdir[0], f), targetdir)
|
||||
os.rmdir(os.path.join(targetdir, tdir[0]))
|
||||
return targetdir
|
||||
|
||||
def addFile(self, sourcefile):
|
||||
if self.type in ['RAR', 'RAR5']:
|
||||
raise NotImplementedError
|
||||
process = Popen('7z a -y "' + self.filepath + '" "' + sourcefile + '"',
|
||||
stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
|
||||
process.communicate()
|
||||
process = subprocess_run_silent(['7z', 'a', '-y', self.filepath, sourcefile],
|
||||
stdout=PIPE, stderr=STDOUT)
|
||||
if process.returncode != 0:
|
||||
raise OSError('Failed to add the file.')
|
||||
|
||||
def extractMetadata(self):
|
||||
process = Popen('7z x -y -so "' + self.filepath + '" ComicInfo.xml',
|
||||
stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
|
||||
xml = process.communicate()
|
||||
process = subprocess_run_silent(['7z', 'x', '-y', '-so', self.filepath, 'ComicInfo.xml'],
|
||||
stdout=PIPE, stderr=STDOUT)
|
||||
if process.returncode != 0:
|
||||
raise OSError('Failed to extract archive.')
|
||||
raise OSError(EXTRACTION_ERROR)
|
||||
try:
|
||||
return parseString(xml[0])
|
||||
return parseString(process.stdout)
|
||||
except ExpatError:
|
||||
return None
|
||||
|
||||
@@ -24,12 +24,7 @@ import mozjpeg_lossless_optimization
|
||||
from PIL import Image, ImageOps, ImageStat, ImageChops, ImageFilter
|
||||
from .shared import md5Checksum
|
||||
|
||||
# 0.045 was determined by
|
||||
# 1200 / 824 = 1.456 (Kindle DX resolution)
|
||||
# 2250 / 1500 = 1.5 (Typical manga page resolution)
|
||||
# 1.5 - 1.456 < 0.045
|
||||
# 0.045 / 1.5 = 0.03 (So maximum 3% of is cropped)
|
||||
AUTO_CROP_THRESHOLD = 0.045
|
||||
AUTO_CROP_THRESHOLD = 0.015
|
||||
|
||||
|
||||
class ProfileData:
|
||||
|
||||
@@ -31,8 +31,7 @@ class Kindle:
|
||||
def findDevice(self):
|
||||
for drive in reversed(psutil.disk_partitions(False)):
|
||||
if (drive[2] == 'FAT32' and drive[3] == 'rw,removable') or \
|
||||
(drive[2] == 'vfat' and 'rw' in drive[3]) or \
|
||||
(drive[2] == 'msdos' and 'rw' in drive[3]):
|
||||
(drive[2] in ('vfat', 'msdos', 'FAT') and 'rw' in drive[3]):
|
||||
if os.path.isdir(os.path.join(drive[1], 'system')) and \
|
||||
os.path.isdir(os.path.join(drive[1], 'documents')):
|
||||
return drive[1]
|
||||
|
||||
@@ -21,8 +21,10 @@
|
||||
import os
|
||||
from hashlib import md5
|
||||
from html.parser import HTMLParser
|
||||
import subprocess
|
||||
from distutils.version import StrictVersion
|
||||
from re import split
|
||||
import sys
|
||||
from traceback import format_tb
|
||||
|
||||
|
||||
@@ -133,4 +135,9 @@ def dependencyCheck(level):
|
||||
missing.append('Pillow 5.2.0+')
|
||||
if len(missing) > 0:
|
||||
print('ERROR: ' + ', '.join(missing) + ' is not installed!')
|
||||
exit(1)
|
||||
sys.exit(1)
|
||||
|
||||
def subprocess_run_silent(command, **kwargs):
|
||||
if (os.name == 'nt'):
|
||||
kwargs.setdefault('creationflags', subprocess.CREATE_NO_WINDOW)
|
||||
return subprocess.run(command, **kwargs)
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
PyQt5>=5.6.0
|
||||
Pillow>=5.2.0
|
||||
psutil>=5.0.0
|
||||
requests>=2.31.0
|
||||
python-slugify>=1.2.1
|
||||
raven>=6.0.0
|
||||
# PyQt5-tools
|
||||
mozjpeg-lossless-optimization>=1.1.2
|
||||
natsort[fast]>=8.4.0
|
||||
distro
|
||||
|
||||
13
setup.py
13
setup.py
@@ -37,19 +37,19 @@ class BuildBinaryCommand(distutils.cmd.Command):
|
||||
def run(self):
|
||||
VERSION = __version__
|
||||
if sys.platform == 'darwin':
|
||||
os.system('pyinstaller -y -F -i icons/comic2ebook.icns -n "Kindle Comic Converter" -w -s kcc.py')
|
||||
os.system('pyinstaller -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
|
||||
os.system('appdmg kcc.json dist/KindleComicConverter_osx_' + VERSION + '.dmg')
|
||||
exit(0)
|
||||
sys.exit(0)
|
||||
elif sys.platform == 'win32':
|
||||
os.system('pyinstaller -y -F -i icons\\comic2ebook.ico -n KCC_' + VERSION + ' -w --noupx kcc.py')
|
||||
exit(0)
|
||||
sys.exit(0)
|
||||
elif sys.platform == 'linux':
|
||||
os.system(
|
||||
'pyinstaller --hidden-import=queue -y -F -i icons/comic2ebook.ico -n kcc_linux_' + VERSION + ' kcc.py')
|
||||
exit(0)
|
||||
sys.exit(0)
|
||||
else:
|
||||
exit(0)
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
setuptools.setup(
|
||||
@@ -80,7 +80,10 @@ setuptools.setup(
|
||||
'psutil>=5.0.0',
|
||||
'python-slugify>=1.2.1,<9.0.0',
|
||||
'raven>=6.0.0',
|
||||
'requests>=2.31.0',
|
||||
'mozjpeg-lossless-optimization>=1.1.2',
|
||||
'natsort[fast]>=8.4.0',
|
||||
'distro',
|
||||
],
|
||||
classifiers=[],
|
||||
zip_safe=False,
|
||||
|
||||
Reference in New Issue
Block a user