mirror of
https://github.com/ciromattia/kcc
synced 2026-01-06 21:39:29 +00:00
@@ -2,11 +2,11 @@ matrix:
|
||||
include:
|
||||
- os: osx
|
||||
language: generic
|
||||
osx_image: xcode6.4
|
||||
osx_image: xcode9.2
|
||||
|
||||
before_install:
|
||||
- brew update
|
||||
- brew install python3
|
||||
- brew upgrade python3
|
||||
- brew uninstall node
|
||||
- travis_wait 30 brew install node@6
|
||||
- brew link node@6 --force --overwrite
|
||||
@@ -15,7 +15,7 @@ before_install:
|
||||
|
||||
install:
|
||||
- pip3 install -r requirements.txt
|
||||
- pip3 install certifi https://github.com/bjones1/pyinstaller/archive/pyqt5_fix.zip
|
||||
- pip3 install certifi https://github.com/pyinstaller/pyinstaller/archive/develop.zip
|
||||
- npm install -g appdmg
|
||||
|
||||
script: python3 setup.py build_binary
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
# CHANGELOG
|
||||
#### 5.5.0:
|
||||
* Added support for WebP format
|
||||
* Added profiles for Kindle Paperwhite 4 and Kobo Forma
|
||||
* All archives are now handled by 7z
|
||||
* Removed MCD support
|
||||
* Fixed multiple smaller issues
|
||||
|
||||
#### 5.4.5:
|
||||
* Fixed EPUB output for non-Kindle devices
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
ISC LICENSE
|
||||
|
||||
Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
||||
Copyright (c) 2013-2018 Paweł Jastrzębski <pawelj@iosphe.re>
|
||||
Copyright (c) 2013-2019 Paweł Jastrzębski <pawelj@iosphe.re>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for
|
||||
any purpose with or without fee is hereby granted, provided that the
|
||||
|
||||
35
README.md
35
README.md
@@ -28,11 +28,8 @@ If you find **KCC** valuable you can consider donating to the authors:
|
||||
## BINARY RELEASES
|
||||
You can find the latest released binary at the following links:
|
||||
- **[Windows](http://kcc.iosphe.re/Windows/) (64-bit only)**
|
||||
- **[macOS](http://kcc.iosphe.re/OSX/) (10.10+)**
|
||||
- **Linux:**
|
||||
- [Ubuntu 17.10](http://kcc.iosphe.re/LinuxArtful/)
|
||||
- [Ubuntu 16.04 / Debian 9](http://kcc.iosphe.re/LinuxXenial/)
|
||||
- [Ubuntu 14.04 / Debian 8](http://kcc.iosphe.re/LinuxTrusty/)
|
||||
- **[macOS](http://kcc.iosphe.re/OSX/) (10.12+)**
|
||||
- **Linux:** Currently unavailable.
|
||||
|
||||
## PYPI
|
||||
**KCC** is also available on PyPI.
|
||||
@@ -51,21 +48,20 @@ Following software is required to run Linux version of **KCC** and/or bare sourc
|
||||
|
||||
On Debian based distributions these two commands should install all needed dependencies:
|
||||
```
|
||||
sudo apt-get install python3 python3-dev python3-pip libpng-dev libjpeg-dev p7zip-full unrar
|
||||
sudo apt-get install python3 python3-dev python3-pip libpng-dev libjpeg-dev p7zip-full
|
||||
sudo pip3 install --upgrade pillow python-slugify psutil pyqt5 raven
|
||||
```
|
||||
|
||||
### Optional dependencies
|
||||
- [KindleGen](http://www.amazon.com/gp/feature.html?ie=UTF8&docId=1000765211) v2.9+ in a directory reachable by your _PATH_ or in _KCC_ directory *(For MOBI generation)*
|
||||
- [UnRAR](http://www.rarlab.com/download.htm) *(For CBR/RAR support)*
|
||||
- [7za](http://www.7-zip.org/download.html) *(For 7z/CB7 support)*
|
||||
- [7z](http://www.7-zip.org/download.html) *(For CBZ/ZIP, CBR/RAR, 7z/CB7 support)*
|
||||
|
||||
## INPUT FORMATS
|
||||
**KCC** can understand and convert, at the moment, the following input types:
|
||||
- Folders containing: PNG, JPG or GIF files
|
||||
- CBZ, ZIP
|
||||
- CBR, RAR *(With `unrar` executable)*
|
||||
- CB7, 7Z *(With `7za` executable)*
|
||||
- Folders containing: PNG, JPG, GIF or WebP files
|
||||
- CBZ, ZIP *(With `7z` executable)*
|
||||
- CBR, RAR *(With `7z` executable)*
|
||||
- CB7, 7Z *(With `7z` executable)*
|
||||
- PDF *(Only extracting JPG images)*
|
||||
|
||||
## USAGE
|
||||
@@ -87,7 +83,7 @@ Options:
|
||||
-p PROFILE, --profile=PROFILE
|
||||
Device profile (Available options: K1, K2, K34, K578,
|
||||
KDX, KPW, KV, KO, KoMT, KoG, KoGHD, KoA, KoAHD, KoAH2O,
|
||||
KoAO) [Default=KV]
|
||||
KoAO, KoF) [Default=KV]
|
||||
-m, --manga-style Manga style (right-to-left reading and splitting)
|
||||
-q, --hq Try to increase the quality of magnification
|
||||
-2, --two-panel Display two not four panels in Panel View mode
|
||||
@@ -160,29 +156,28 @@ This script born as a cross-platform alternative to `KindleComicParser` by **Dc5
|
||||
The app relies and includes the following scripts:
|
||||
|
||||
- `DualMetaFix` script by **K. Hendricks**. Released with GPL-3 License.
|
||||
- `rarfile.py` script © 2005-2014 **Marko Kreen** <markokr@gmail.com>. Released with ISC License.
|
||||
- `image.py` class from **Alex Yatskov**'s [Mangle](https://github.com/FooSoft/mangle/) with subsequent [proDOOMman](https://github.com/proDOOMman/Mangle)'s and [Birua](https://github.com/Birua/Mangle)'s patches.
|
||||
- Icon is by **Nikolay Verin** ([http://ncrow.deviantart.com/](http://ncrow.deviantart.com/)) and released under [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/) License.
|
||||
|
||||
## SAMPLE FILES CREATED BY KCC
|
||||
* [Kindle Oasis 2](http://kcc.iosphe.re/Samples/Ubunchu!-KO.mobi)
|
||||
* [Kindle Paperwhite 3 / Voyage / Oasis](http://kcc.iosphe.re/Samples/Ubunchu!-KV.mobi)
|
||||
* [Kindle Paperwhite 3 / 4 / Voyage / Oasis](http://kcc.iosphe.re/Samples/Ubunchu!-KV.mobi)
|
||||
* [Kindle Paperwhite 1 / 2](http://kcc.iosphe.re/Samples/Ubunchu!-KPW.mobi)
|
||||
* [Kindle](http://kcc.iosphe.re/Samples/Ubunchu!-K578.mobi)
|
||||
* [Kobo Aura](http://kcc.iosphe.re/Samples/Ubunchu-KoA.kepub.epub)
|
||||
* [Kobo Aura HD](http://kcc.iosphe.re/Samples/Ubunchu-KoAHD.kepub.epub)
|
||||
* [Kobo Aura H2O](http://kcc.iosphe.re/Samples/Ubunchu-KoAH2O.kepub.epub)
|
||||
* [Kobo Aura ONE](http://kcc.iosphe.re/Samples/Ubunchu-KoAO.kepub.epub)
|
||||
* [Kobo Forma](http://kcc.iosphe.re/Samples/Ubunchu-KoF.kepub.epub)
|
||||
|
||||
## PRIVACY
|
||||
**KCC** is initiating internet connections in three cases:
|
||||
* During startup - Version check
|
||||
* When MCD metadata are used - Cover download
|
||||
* When error occurs - Automatic reporting
|
||||
**KCC** is initiating internet connections in two cases:
|
||||
* During startup - Version check.
|
||||
* When error occurs - Automatic reporting on Windows and MacOS.
|
||||
|
||||
## KNOWN ISSUES
|
||||
Please check [wiki page](https://github.com/ciromattia/kcc/wiki/Known-issues).
|
||||
|
||||
## COPYRIGHT
|
||||
Copyright (c) 2012-2018 Ciro Mattia Gonano and Paweł Jastrzębski.
|
||||
Copyright (c) 2012-2019 Ciro Mattia Gonano and Paweł Jastrzębski.
|
||||
**KCC** is released under ISC LICENSE; see LICENSE.txt for further details.
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
environment:
|
||||
PYTHON: "C:\\Python36-x64"
|
||||
PYTHON: "C:\\Python37-x64"
|
||||
|
||||
install:
|
||||
- set PATH="%PYTHON%\\Scripts";"C:\\Program Files (x86)\\Inno Setup 5";%PATH%
|
||||
- "%PYTHON%\\python.exe -m pip install --upgrade pip setuptools wheel"
|
||||
- "%PYTHON%\\python.exe -m pip install -r requirements.txt"
|
||||
- "%PYTHON%\\python.exe -m pip install certifi https://github.com/bjones1/pyinstaller/archive/pyqt5_fix.zip"
|
||||
- "%PYTHON%\\python.exe -m pip install certifi https://github.com/pyinstaller/pyinstaller/archive/develop.zip"
|
||||
- nuget install secure-file -ExcludeVersion
|
||||
- secure-file\tools\secure-file -decrypt other\windows\sentry.py.enc -out kindlecomicconverter\sentry.py -secret %ENCRYPTION%
|
||||
|
||||
|
||||
@@ -112,19 +112,6 @@
|
||||
<item row="6" column="1">
|
||||
<widget class="QLineEdit" name="coloristLine"/>
|
||||
</item>
|
||||
<item row="7" column="0">
|
||||
<widget class="QLabel" name="label_8">
|
||||
<property name="text">
|
||||
<string><html><head/><body><p><a href="https://github.com/ciromattia/kcc/wiki/Manga-Cover-Database-support"><span style=" text-decoration: underline; color:#0000ff;">MUid:</span></a></p></body></html></string>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="1">
|
||||
<widget class="QLineEdit" name="muidLine"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
||||
# Copyright (c) 2013-2018 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||
# Copyright (c) 2013-2019 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||
#
|
||||
# Permission to use, copy, modify, and/or distribute this software for
|
||||
# any purpose with or without fee is hereby granted, provided that the
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
||||
# Copyright (c) 2013-2018 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||
# Copyright (c) 2013-2019 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||
#
|
||||
# Permission to use, copy, modify, and/or distribute this software for
|
||||
# any purpose with or without fee is hereby granted, provided that the
|
||||
|
||||
8
kcc.iss
8
kcc.iss
@@ -1,5 +1,5 @@
|
||||
#define MyAppName "Kindle Comic Converter"
|
||||
#define MyAppVersion "5.4.5"
|
||||
#define MyAppVersion "5.5.0"
|
||||
#define MyAppPublisher "Ciro Mattia Gonano, Paweł Jastrzębski"
|
||||
#define MyAppURL "http://kcc.iosphe.re/"
|
||||
#define MyAppExeName "KCC.exe"
|
||||
@@ -12,7 +12,7 @@ AppPublisher={#MyAppPublisher}
|
||||
AppPublisherURL={#MyAppURL}
|
||||
AppSupportURL={#MyAppURL}
|
||||
AppUpdatesURL={#MyAppURL}
|
||||
AppCopyright=Copyright (C) 2012-2018 Ciro Mattia Gonano and Paweł Jastrzębski
|
||||
AppCopyright=Copyright (C) 2012-2019 Ciro Mattia Gonano and Paweł Jastrzębski
|
||||
ArchitecturesAllowed=x64
|
||||
DefaultDirName={pf}\{#MyAppName}
|
||||
DefaultGroupName={#MyAppName}
|
||||
@@ -47,8 +47,8 @@ Name: "CB7association"; Description: "CB7"; GroupDescription: "File associations
|
||||
Source: "dist\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "LICENSE.txt"; DestDir: "{app}"; Flags: ignoreversion solidbreak
|
||||
Source: "other\windows\Additional-LICENSE.txt"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "other\windows\UnRAR.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "other\windows\7za.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "other\windows\7z.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "other\windows\7z.dll"; DestDir: "{app}"; Flags: ignoreversion
|
||||
|
||||
[Icons]
|
||||
Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
|
||||
|
||||
2
kcc.py
2
kcc.py
@@ -2,7 +2,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
||||
# Copyright (c) 2013-2018 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||
# Copyright (c) 2013-2019 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||
#
|
||||
# Permission to use, copy, modify, and/or distribute this software for
|
||||
# any purpose with or without fee is hereby granted, provided that the
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
||||
# Copyright (c) 2013-2018 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||
# Copyright (c) 2013-2019 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||
#
|
||||
# Permission to use, copy, modify, and/or distribute this software for
|
||||
# any purpose with or without fee is hereby granted, provided that the
|
||||
@@ -22,8 +22,9 @@ import sys
|
||||
from urllib.parse import unquote
|
||||
from urllib.request import urlopen, urlretrieve, Request
|
||||
from time import sleep
|
||||
from shutil import move
|
||||
from shutil import move, rmtree
|
||||
from subprocess import STDOUT, PIPE
|
||||
# noinspection PyUnresolvedReferences
|
||||
from PyQt5 import QtGui, QtCore, QtWidgets, QtNetwork
|
||||
from xml.dom.minidom import parse
|
||||
from xml.sax.saxutils import escape
|
||||
@@ -31,7 +32,8 @@ from psutil import Popen, Process
|
||||
from copy import copy
|
||||
from distutils.version import StrictVersion
|
||||
from raven import Client
|
||||
from .shared import md5Checksum, HTMLStripper, sanitizeTrace
|
||||
from tempfile import gettempdir
|
||||
from .shared import md5Checksum, HTMLStripper, sanitizeTrace, walkLevel
|
||||
from . import __version__
|
||||
from . import comic2ebook
|
||||
from . import metadata
|
||||
@@ -173,7 +175,7 @@ class VersionThread(QtCore.QThread):
|
||||
move(path[0], path[0] + '.exe')
|
||||
MW.hideProgressBar.emit()
|
||||
MW.modeConvert.emit(1)
|
||||
Popen(path[0] + '.exe /SP- /silent /noicons', stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
|
||||
Popen(path[0] + '.exe /SP- /silent /noicons', stdout=PIPE, stderr=STDOUT, shell=True)
|
||||
MW.forceShutdown.emit()
|
||||
except Exception:
|
||||
MW.addMessage.emit('Failed to download the update!', 'warning', False)
|
||||
@@ -238,6 +240,7 @@ class WorkerThread(QtCore.QThread):
|
||||
MW.addTrayMessage.emit('Conversion interrupted.', 'Critical')
|
||||
MW.modeConvert.emit(1)
|
||||
|
||||
# noinspection PyUnboundLocalVariable
|
||||
def run(self):
|
||||
MW.modeConvert.emit(0)
|
||||
|
||||
@@ -477,20 +480,11 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
if self.needClean:
|
||||
self.needClean = False
|
||||
GUI.jobList.clear()
|
||||
if self.UnRAR:
|
||||
if self.sevenza:
|
||||
fnames = QtWidgets.QFileDialog.getOpenFileNames(MW, 'Select file', self.lastPath,
|
||||
'Comic (*.cbz *.cbr *.cb7 *.zip *.rar *.7z *.pdf)')
|
||||
else:
|
||||
fnames = QtWidgets.QFileDialog.getOpenFileNames(MW, 'Select file', self.lastPath,
|
||||
'Comic (*.cbz *.cbr *.zip *.rar *.pdf)')
|
||||
if self.sevenzip:
|
||||
fnames = QtWidgets.QFileDialog.getOpenFileNames(MW, 'Select file', self.lastPath,
|
||||
'Comic (*.cbz *.cbr *.cb7 *.zip *.rar *.7z *.pdf)')
|
||||
else:
|
||||
if self.sevenza:
|
||||
fnames = QtWidgets.QFileDialog.getOpenFileNames(MW, 'Select file', self.lastPath,
|
||||
'Comic (*.cbz *.cb7 *.zip *.7z *.pdf)')
|
||||
else:
|
||||
fnames = QtWidgets.QFileDialog.getOpenFileNames(MW, 'Select file', self.lastPath,
|
||||
'Comic (*.cbz *.zip *.pdf)')
|
||||
fnames = QtWidgets.QFileDialog.getOpenFileNames(MW, 'Select file', self.lastPath, 'Comic (*.pdf)')
|
||||
for fname in fnames[0]:
|
||||
if fname != '':
|
||||
if sys.platform.startswith('win'):
|
||||
@@ -509,20 +503,12 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
sname = sname.replace('/', '\\')
|
||||
self.lastPath = os.path.abspath(sname)
|
||||
else:
|
||||
if self.UnRAR:
|
||||
if self.sevenza:
|
||||
fname = QtWidgets.QFileDialog.getOpenFileName(MW, 'Select file', self.lastPath,
|
||||
'Comic (*.cbz *.cbr *.cb7)')
|
||||
else:
|
||||
fname = QtWidgets.QFileDialog.getOpenFileName(MW, 'Select file', self.lastPath,
|
||||
'Comic (*.cbz *.cbr)')
|
||||
if self.sevenzip:
|
||||
fname = QtWidgets.QFileDialog.getOpenFileName(MW, 'Select file', self.lastPath,
|
||||
'Comic (*.cbz *.cbr *.cb7)')
|
||||
else:
|
||||
if self.sevenza:
|
||||
fname = QtWidgets.QFileDialog.getOpenFileName(MW, 'Select file', self.lastPath,
|
||||
'Comic (*.cbz *.cb7)')
|
||||
else:
|
||||
fname = QtWidgets.QFileDialog.getOpenFileName(MW, 'Select file', self.lastPath,
|
||||
'Comic (*.cbz)')
|
||||
fname = ['']
|
||||
self.showDialog("Editor is disabled due to a lack of 7z.", 'error')
|
||||
if fname[0] != '':
|
||||
if sys.platform.startswith('win'):
|
||||
sname = fname[0].replace('/', '\\')
|
||||
@@ -812,16 +798,9 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
if self.needClean:
|
||||
self.needClean = False
|
||||
GUI.jobList.clear()
|
||||
if self.UnRAR:
|
||||
if self.sevenza:
|
||||
formats = ['.cbz', '.cbr', '.cb7', '.zip', '.rar', '.7z', '.pdf']
|
||||
else:
|
||||
formats = ['.cbz', '.cbr', '.zip', '.rar', '.pdf']
|
||||
else:
|
||||
if self.sevenza:
|
||||
formats = ['.cbz', '.cb7', '.zip', '.7z', '.pdf']
|
||||
else:
|
||||
formats = ['.cbz', '.zip', '.pdf']
|
||||
formats = ['.pdf']
|
||||
if self.sevenzip:
|
||||
formats.extend(['.cb7', '.7z', '.cbz', '.zip', '.cbr', '.rar'])
|
||||
if os.path.isdir(message):
|
||||
GUI.jobList.addItem(message)
|
||||
GUI.jobList.scrollToBottom()
|
||||
@@ -831,7 +810,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
GUI.jobList.addItem(message)
|
||||
GUI.jobList.scrollToBottom()
|
||||
else:
|
||||
self.addMessage('This file type is unsupported!', 'error')
|
||||
self.addMessage('Unsupported file type for ' + message, 'error')
|
||||
|
||||
def dragAndDrop(self, e):
|
||||
e.accept()
|
||||
@@ -857,10 +836,11 @@ 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)
|
||||
if kindleGenExitCode.wait() == 0:
|
||||
kindleGenExitCode = Popen('kindlegen -locale en', stdout=PIPE, stderr=STDOUT, shell=True)
|
||||
kindleGenExitCode.communicate()
|
||||
if kindleGenExitCode.returncode == 0:
|
||||
self.kindleGen = True
|
||||
versionCheck = Popen('kindlegen -locale en', stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
|
||||
versionCheck = Popen('kindlegen -locale en', stdout=PIPE, stderr=STDOUT, shell=True)
|
||||
for line in versionCheck.stdout:
|
||||
line = line.decode("utf-8")
|
||||
if 'Amazon kindlegen' in line:
|
||||
@@ -909,6 +889,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
self.targetDirectory = ''
|
||||
self.sentry = Client(release=__version__)
|
||||
if sys.platform.startswith('win'):
|
||||
# noinspection PyUnresolvedReferences
|
||||
from psutil import BELOW_NORMAL_PRIORITY_CLASS
|
||||
self.p = Process(os.getpid())
|
||||
self.p.nice(BELOW_NORMAL_PRIORITY_CLASS)
|
||||
@@ -934,8 +915,8 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
'DefaultUpscale': True, 'Label': 'KV'},
|
||||
"Kindle Voyage": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
|
||||
'DefaultUpscale': True, 'Label': 'KV'},
|
||||
"Kindle PW 3": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
|
||||
'DefaultUpscale': True, 'Label': 'KV'},
|
||||
"Kindle PW 3/4": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
|
||||
'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,
|
||||
@@ -956,6 +937,8 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
'DefaultUpscale': True, 'Label': 'KoAH2O'},
|
||||
"Kobo Aura ONE": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1,
|
||||
'DefaultUpscale': True, 'Label': 'KoAO'},
|
||||
"Kobo Forma": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1,
|
||||
'DefaultUpscale': True, 'Label': 'KoF'},
|
||||
"Other": {'PVOptions': False, 'ForceExpert': True, 'DefaultFormat': 1,
|
||||
'DefaultUpscale': False, 'Label': 'OTHER'},
|
||||
"Kindle 1": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 0,
|
||||
@@ -971,10 +954,11 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
"Kindle Oasis 2",
|
||||
"Kindle Oasis",
|
||||
"Kindle Voyage",
|
||||
"Kindle PW 3",
|
||||
"Kindle PW 3/4",
|
||||
"Kindle PW 1/2",
|
||||
"Kindle",
|
||||
"Separator",
|
||||
"Kobo Forma",
|
||||
"Kobo Aura ONE",
|
||||
"Kobo Aura H2O",
|
||||
"Kobo Aura HD",
|
||||
@@ -1007,22 +991,14 @@ 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')
|
||||
rarExitCode = Popen('unrar', stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
|
||||
rarExitCode = rarExitCode.wait()
|
||||
if rarExitCode == 0 or rarExitCode == 1 or rarExitCode == 7:
|
||||
self.UnRAR = True
|
||||
process = Popen('7z', stdout=PIPE, stderr=STDOUT, shell=True)
|
||||
process.communicate()
|
||||
if process.returncode == 0 or process.returncode == 7:
|
||||
self.sevenzip = True
|
||||
else:
|
||||
self.UnRAR = False
|
||||
self.addMessage('Cannot find <a href="http://www.rarlab.com/rar_add.htm">UnRAR</a>!'
|
||||
' Processing of CBR/RAR files will be disabled.', 'warning')
|
||||
sevenzaExitCode = Popen('7za', stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
|
||||
sevenzaExitCode = sevenzaExitCode.wait()
|
||||
if sevenzaExitCode == 0 or sevenzaExitCode == 7:
|
||||
self.sevenza = True
|
||||
else:
|
||||
self.sevenza = False
|
||||
self.addMessage('Cannot find <a href="http://www.7-zip.org/download.html">7za</a>!'
|
||||
' Processing of CB7/7Z files will be disabled.', 'warning')
|
||||
self.sevenzip = False
|
||||
self.addMessage('Cannot find <a href="http://www.7-zip.org/download.html">7z</a>!'
|
||||
' Processing of archives will be disabled.', 'warning')
|
||||
self.detectKindleGen(True)
|
||||
|
||||
APP.messageFromOtherInstance.connect(self.handleMessage)
|
||||
@@ -1092,6 +1068,12 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
self.versionCheck.start()
|
||||
self.tray.show()
|
||||
|
||||
# Cleanup unfisnished conversion
|
||||
for root, dirs, _ in walkLevel(gettempdir(), 0):
|
||||
for tempdir in dirs:
|
||||
if tempdir.startswith('KCC-'):
|
||||
rmtree(os.path.join(root, tempdir), True)
|
||||
|
||||
if self.windowSize != '0x0':
|
||||
x, y = self.windowSize.split('x')
|
||||
MW.resize(int(x), int(y))
|
||||
@@ -1103,7 +1085,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
class KCCGUI_MetaEditor(KCC_ui_editor.Ui_editorDialog):
|
||||
def loadData(self, file):
|
||||
self.parser = metadata.MetadataParser(file)
|
||||
if self.parser.compressor == 'rar':
|
||||
if self.parser.format in ['RAR', 'RAR5']:
|
||||
self.editorWidget.setEnabled(False)
|
||||
self.okButton.setEnabled(False)
|
||||
self.statusLabel.setText('CBR metadata are read-only.')
|
||||
@@ -1111,11 +1093,8 @@ class KCCGUI_MetaEditor(KCC_ui_editor.Ui_editorDialog):
|
||||
self.editorWidget.setEnabled(True)
|
||||
self.okButton.setEnabled(True)
|
||||
self.statusLabel.setText('Separate authors with a comma.')
|
||||
for field in (self.seriesLine, self.volumeLine, self.numberLine, self.muidLine):
|
||||
if field.objectName() == 'muidLine':
|
||||
field.setText(self.parser.data['MUid'])
|
||||
else:
|
||||
field.setText(self.parser.data[field.objectName().capitalize()[:-4]])
|
||||
for field in (self.seriesLine, self.volumeLine, self.numberLine):
|
||||
field.setText(self.parser.data[field.objectName().capitalize()[:-4]])
|
||||
for field in (self.writerLine, self.pencillerLine, self.inkerLine, self.coloristLine):
|
||||
field.setText(', '.join(self.parser.data[field.objectName().capitalize()[:-4] + 's']))
|
||||
if self.seriesLine.text() == '':
|
||||
@@ -1125,12 +1104,9 @@ class KCCGUI_MetaEditor(KCC_ui_editor.Ui_editorDialog):
|
||||
self.seriesLine.setText(file.split('\\')[-1].split('/')[-1].split('.')[0])
|
||||
|
||||
def saveData(self):
|
||||
for field in (self.volumeLine, self.numberLine, self.muidLine):
|
||||
for field in (self.volumeLine, self.numberLine):
|
||||
if field.text().isnumeric() or self.cleanData(field.text()) == '':
|
||||
if field.objectName() == 'muidLine':
|
||||
self.parser.data['MUid'] = self.cleanData(field.text())
|
||||
else:
|
||||
self.parser.data[field.objectName().capitalize()[:-4]] = self.cleanData(field.text())
|
||||
self.parser.data[field.objectName().capitalize()[:-4]] = self.cleanData(field.text())
|
||||
else:
|
||||
self.statusLabel.setText(field.objectName().capitalize()[:-4] + ' field must be a number.')
|
||||
break
|
||||
|
||||
@@ -66,13 +66,6 @@ class Ui_editorDialog(object):
|
||||
self.coloristLine = QtWidgets.QLineEdit(self.editorWidget)
|
||||
self.coloristLine.setObjectName("coloristLine")
|
||||
self.gridLayout.addWidget(self.coloristLine, 6, 1, 1, 1)
|
||||
self.label_8 = QtWidgets.QLabel(self.editorWidget)
|
||||
self.label_8.setOpenExternalLinks(True)
|
||||
self.label_8.setObjectName("label_8")
|
||||
self.gridLayout.addWidget(self.label_8, 7, 0, 1, 1)
|
||||
self.muidLine = QtWidgets.QLineEdit(self.editorWidget)
|
||||
self.muidLine.setObjectName("muidLine")
|
||||
self.gridLayout.addWidget(self.muidLine, 7, 1, 1, 1)
|
||||
self.verticalLayout.addWidget(self.editorWidget)
|
||||
self.optionWidget = QtWidgets.QWidget(editorDialog)
|
||||
self.optionWidget.setObjectName("optionWidget")
|
||||
@@ -117,7 +110,6 @@ class Ui_editorDialog(object):
|
||||
self.label_5.setText(_translate("editorDialog", "Penciller:"))
|
||||
self.label_6.setText(_translate("editorDialog", "Inker:"))
|
||||
self.label_7.setText(_translate("editorDialog", "Colorist:"))
|
||||
self.label_8.setText(_translate("editorDialog", "<html><head/><body><p><a href=\"https://github.com/ciromattia/kcc/wiki/Manga-Cover-Database-support\"><span style=\" text-decoration: underline; color:#0000ff;\">MUid:</span></a></p></body></html>"))
|
||||
self.okButton.setText(_translate("editorDialog", "Save"))
|
||||
self.cancelButton.setText(_translate("editorDialog", "Cancel"))
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
__version__ = '5.4.5'
|
||||
__version__ = '5.5.0'
|
||||
__license__ = 'ISC'
|
||||
__copyright__ = '2012-2018, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>'
|
||||
__copyright__ = '2012-2019, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
||||
# Copyright (c) 2013-2018 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||
#
|
||||
# Permission to use, copy, modify, and/or distribute this software for
|
||||
# any purpose with or without fee is hereby granted, provided that the
|
||||
# above copyright notice and this permission notice appear in all
|
||||
# copies.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
||||
# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
||||
# WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
|
||||
# AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||
# DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
|
||||
# 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 os
|
||||
from zipfile import is_zipfile, ZipFile
|
||||
from subprocess import STDOUT, PIPE
|
||||
from psutil import Popen
|
||||
from shutil import move
|
||||
from . import rarfile
|
||||
from .shared import check7ZFile as is_7zfile
|
||||
|
||||
|
||||
class CBxArchive:
|
||||
def __init__(self, fname):
|
||||
self.fname = fname
|
||||
if is_zipfile(fname):
|
||||
self.compressor = 'zip'
|
||||
elif rarfile.is_rarfile(fname):
|
||||
self.compressor = 'rar'
|
||||
elif is_7zfile(fname):
|
||||
self.compressor = '7z'
|
||||
else:
|
||||
self.compressor = None
|
||||
|
||||
def isCbxFile(self):
|
||||
return self.compressor is not None
|
||||
|
||||
def extractCBZ(self, targetdir):
|
||||
cbzFile = ZipFile(self.fname)
|
||||
filelist = []
|
||||
for f in cbzFile.namelist():
|
||||
if f.startswith('__MACOSX') or f.endswith('.DS_Store') or f.endswith('humbs.db'):
|
||||
pass
|
||||
elif f.endswith('/'):
|
||||
os.makedirs(os.path.join(targetdir, f), exist_ok=True)
|
||||
else:
|
||||
filelist.append(f)
|
||||
cbzFile.extractall(targetdir, filelist)
|
||||
|
||||
def extractCBR(self, targetdir):
|
||||
cbrFile = rarfile.RarFile(self.fname)
|
||||
cbrFile.extractall(targetdir)
|
||||
for root, _, filenames in os.walk(targetdir):
|
||||
for filename in filenames:
|
||||
if filename.startswith('__MACOSX') or filename.endswith('.DS_Store') or filename.endswith('humbs.db'):
|
||||
os.remove(os.path.join(root, filename))
|
||||
|
||||
def extractCB7(self, targetdir):
|
||||
output = Popen('7za x "' + self.fname + '" -xr!__MACOSX -xr!.DS_Store -xr!thumbs.db -xr!Thumbs.db -o"' +
|
||||
targetdir + '"', stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
|
||||
extracted = False
|
||||
for line in output.stdout:
|
||||
if b"Everything is Ok" in line:
|
||||
extracted = True
|
||||
if not extracted:
|
||||
raise OSError
|
||||
|
||||
def extract(self, targetdir):
|
||||
if self.compressor == 'rar':
|
||||
self.extractCBR(targetdir)
|
||||
elif self.compressor == 'zip':
|
||||
self.extractCBZ(targetdir)
|
||||
elif self.compressor == '7z':
|
||||
self.extractCB7(targetdir)
|
||||
adir = os.listdir(targetdir)
|
||||
if 'ComicInfo.xml' in adir:
|
||||
adir.remove('ComicInfo.xml')
|
||||
if len(adir) == 1 and os.path.isdir(os.path.join(targetdir, adir[0])):
|
||||
for f in os.listdir(os.path.join(targetdir, adir[0])):
|
||||
move(os.path.join(targetdir, adir[0], f), targetdir)
|
||||
os.rmdir(os.path.join(targetdir, adir[0]))
|
||||
return targetdir
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
||||
# Copyright (c) 2013-2018 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||
# Copyright (c) 2013-2019 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||
#
|
||||
# Permission to use, copy, modify, and/or distribute this software for
|
||||
# any purpose with or without fee is hereby granted, provided that the
|
||||
@@ -22,9 +22,7 @@ import os
|
||||
import sys
|
||||
from time import strftime, gmtime
|
||||
from copy import copy
|
||||
from glob import glob
|
||||
from json import loads
|
||||
from urllib.request import Request, urlopen
|
||||
from glob import glob, escape
|
||||
from re import sub
|
||||
from stat import S_IWRITE, S_IREAD, S_IEXEC
|
||||
from zipfile import ZipFile, ZIP_STORED, ZIP_DEFLATED
|
||||
@@ -37,7 +35,7 @@ from slugify import slugify as slugifyExt
|
||||
from PIL import Image
|
||||
from subprocess import STDOUT, PIPE
|
||||
from psutil import Popen, virtual_memory, disk_usage
|
||||
from html import escape
|
||||
from html import escape as hescape
|
||||
try:
|
||||
from PyQt5 import QtCore
|
||||
except ImportError:
|
||||
@@ -45,7 +43,7 @@ except ImportError:
|
||||
from .shared import md5Checksum, getImageFileName, walkSort, walkLevel, sanitizeTrace
|
||||
from . import comic2panel
|
||||
from . import image
|
||||
from . import cbxarchive
|
||||
from . import comicarchive
|
||||
from . import pdfjpgextract
|
||||
from . import dualmetafix
|
||||
from . import metadata
|
||||
@@ -61,7 +59,7 @@ def main(argv=None):
|
||||
parser.print_help()
|
||||
return 0
|
||||
if sys.platform.startswith('win'):
|
||||
sources = set([source for arg in args for source in glob(arg)])
|
||||
sources = set([source for arg in args for source in glob(escape(arg))])
|
||||
else:
|
||||
sources = set(args)
|
||||
if len(sources) == 0:
|
||||
@@ -112,7 +110,7 @@ def buildHTML(path, imgfile, imgfilepath):
|
||||
"<!DOCTYPE html>\n",
|
||||
"<html xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:epub=\"http://www.idpf.org/2007/ops\">\n",
|
||||
"<head>\n",
|
||||
"<title>", escape(filename[0]), "</title>\n",
|
||||
"<title>", hescape(filename[0]), "</title>\n",
|
||||
"<link href=\"", "../" * (backref - 1), "style.css\" type=\"text/css\" rel=\"stylesheet\"/>\n",
|
||||
"<meta name=\"viewport\" "
|
||||
"content=\"width=" + str(imgsize[0]) + ", height=" + str(imgsize[1]) + "\"/>\n"
|
||||
@@ -210,7 +208,7 @@ def buildNCX(dstdir, title, chapters, chapternames):
|
||||
"<meta name=\"dtb:maxPageNumber\" content=\"0\"/>\n",
|
||||
"<meta name=\"generated\" content=\"true\"/>\n",
|
||||
"</head>\n",
|
||||
"<docTitle><text>", escape(title), "</text></docTitle>\n",
|
||||
"<docTitle><text>", hescape(title), "</text></docTitle>\n",
|
||||
"<navMap>\n"])
|
||||
for chapter in chapters:
|
||||
folder = chapter[0].replace(os.path.join(dstdir, 'OEBPS'), '').lstrip('/').lstrip('\\\\')
|
||||
@@ -222,7 +220,7 @@ def buildNCX(dstdir, title, chapters, chapternames):
|
||||
elif os.path.basename(folder) != "Text":
|
||||
title = chapternames[os.path.basename(folder)]
|
||||
f.write("<navPoint id=\"" + navID + "\"><navLabel><text>" +
|
||||
escape(title) + "</text></navLabel><content src=\"" + filename[0].replace("\\", "/") +
|
||||
hescape(title) + "</text></navLabel><content src=\"" + filename[0].replace("\\", "/") +
|
||||
".xhtml\"/></navPoint>\n")
|
||||
f.write("</navMap>\n</ncx>")
|
||||
f.close()
|
||||
@@ -235,7 +233,7 @@ def buildNAV(dstdir, title, chapters, chapternames):
|
||||
"<!DOCTYPE html>\n",
|
||||
"<html xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:epub=\"http://www.idpf.org/2007/ops\">\n",
|
||||
"<head>\n",
|
||||
"<title>" + escape(title) + "</title>\n",
|
||||
"<title>" + hescape(title) + "</title>\n",
|
||||
"<meta charset=\"utf-8\"/>\n",
|
||||
"</head>\n",
|
||||
"<body>\n",
|
||||
@@ -248,7 +246,7 @@ def buildNAV(dstdir, title, chapters, chapternames):
|
||||
title = chapternames[chapter[1]]
|
||||
elif os.path.basename(folder) != "Text":
|
||||
title = chapternames[os.path.basename(folder)]
|
||||
f.write("<li><a href=\"" + filename[0].replace("\\", "/") + ".xhtml\">" + escape(title) + "</a></li>\n")
|
||||
f.write("<li><a href=\"" + filename[0].replace("\\", "/") + ".xhtml\">" + hescape(title) + "</a></li>\n")
|
||||
f.writelines(["</ol>\n",
|
||||
"</nav>\n",
|
||||
"<nav epub:type=\"page-list\">\n",
|
||||
@@ -260,7 +258,7 @@ def buildNAV(dstdir, title, chapters, chapternames):
|
||||
title = chapternames[chapter[1]]
|
||||
elif os.path.basename(folder) != "Text":
|
||||
title = chapternames[os.path.basename(folder)]
|
||||
f.write("<li><a href=\"" + filename[0].replace("\\", "/") + ".xhtml\">" + escape(title) + "</a></li>\n")
|
||||
f.write("<li><a href=\"" + filename[0].replace("\\", "/") + ".xhtml\">" + hescape(title) + "</a></li>\n")
|
||||
f.write("</ol>\n</nav>\n</body>\n</html>")
|
||||
f.close()
|
||||
|
||||
@@ -278,7 +276,7 @@ def buildOPF(dstdir, title, filelist, cover=None):
|
||||
"xmlns=\"http://www.idpf.org/2007/opf\">\n",
|
||||
"<metadata xmlns:opf=\"http://www.idpf.org/2007/opf\" ",
|
||||
"xmlns:dc=\"http://purl.org/dc/elements/1.1/\">\n",
|
||||
"<dc:title>", title, "</dc:title>\n",
|
||||
"<dc:title>", hescape(title), "</dc:title>\n",
|
||||
"<dc:language>en-US</dc:language>\n",
|
||||
"<dc:identifier id=\"BookID\">urn:uuid:", options.uuid, "</dc:identifier>\n",
|
||||
"<dc:contributor id=\"contributor\">KindleComicConverter-" + __version__ + "</dc:contributor>\n"])
|
||||
@@ -597,16 +595,12 @@ def getWorkFolder(afile):
|
||||
raise UserWarning("Failed to extract images from PDF file.")
|
||||
else:
|
||||
workdir = mkdtemp('', 'KCC-')
|
||||
cbx = cbxarchive.CBxArchive(afile)
|
||||
if cbx.isCbxFile():
|
||||
try:
|
||||
path = cbx.extract(workdir)
|
||||
except Exception:
|
||||
rmtree(workdir, True)
|
||||
raise UserWarning("Failed to extract archive.")
|
||||
else:
|
||||
try:
|
||||
cbx = comicarchive.ComicArchive(afile)
|
||||
path = cbx.extract(workdir)
|
||||
except OSError as e:
|
||||
rmtree(workdir, True)
|
||||
raise UserWarning("Failed to detect archive format.")
|
||||
raise UserWarning(e.strerror)
|
||||
else:
|
||||
raise UserWarning("Failed to open source file/directory.")
|
||||
sanitizePermissions(path)
|
||||
@@ -652,7 +646,6 @@ def getOutputFilename(srcpath, wantedname, ext, tomenumber):
|
||||
def getComicInfo(path, originalpath):
|
||||
xmlPath = os.path.join(path, 'ComicInfo.xml')
|
||||
options.authors = ['KCC']
|
||||
options.remoteCovers = {}
|
||||
options.chapters = []
|
||||
options.summary = ''
|
||||
titleSuffix = ''
|
||||
@@ -673,7 +666,7 @@ def getComicInfo(path, originalpath):
|
||||
options.authors = []
|
||||
if defaultTitle:
|
||||
if xml.data['Series']:
|
||||
options.title = escape(xml.data['Series'])
|
||||
options.title = hescape(xml.data['Series'])
|
||||
if xml.data['Volume']:
|
||||
titleSuffix += ' V' + xml.data['Volume'].zfill(2)
|
||||
if xml.data['Number']:
|
||||
@@ -681,35 +674,19 @@ def getComicInfo(path, originalpath):
|
||||
options.title += titleSuffix
|
||||
for field in ['Writers', 'Pencillers', 'Inkers', 'Colorists']:
|
||||
for person in xml.data[field]:
|
||||
options.authors.append(escape(person))
|
||||
options.authors.append(hescape(person))
|
||||
if len(options.authors) > 0:
|
||||
options.authors = list(set(options.authors))
|
||||
options.authors.sort()
|
||||
else:
|
||||
options.authors = ['KCC']
|
||||
if xml.data['MUid']:
|
||||
options.remoteCovers = getCoversFromMCB(xml.data['MUid'])
|
||||
if xml.data['Bookmarks']:
|
||||
options.chapters = xml.data['Bookmarks']
|
||||
if xml.data['Summary']:
|
||||
options.summary = escape(xml.data['Summary'])
|
||||
options.summary = hescape(xml.data['Summary'])
|
||||
os.remove(xmlPath)
|
||||
|
||||
|
||||
def getCoversFromMCB(mangaid):
|
||||
covers = {}
|
||||
try:
|
||||
jsonRaw = urlopen(Request('http://mcd.iosphe.re/api/v1/series/' + mangaid + '/',
|
||||
headers={'User-Agent': 'KindleComicConverter/' + __version__}))
|
||||
jsonData = loads(jsonRaw.read().decode('utf-8'))
|
||||
for volume in jsonData['Covers']['a']:
|
||||
if volume['Side'] == 'front':
|
||||
covers[int(volume['Volume'])] = volume['Raw']
|
||||
except Exception:
|
||||
return {}
|
||||
return covers
|
||||
|
||||
|
||||
def getDirectorySize(start_path='.'):
|
||||
total_size = 0
|
||||
for dirpath, _, filenames in os.walk(start_path):
|
||||
@@ -790,7 +767,8 @@ def splitDirectory(path):
|
||||
level = -1
|
||||
for root, _, files in os.walk(os.path.join(path, 'OEBPS', 'Images')):
|
||||
for f in files:
|
||||
if f.endswith('.jpg') or f.endswith('.jpeg') or f.endswith('.png') or f.endswith('.gif'):
|
||||
if f.endswith('.jpg') or f.endswith('.jpeg') or f.endswith('.png') or f.endswith('.gif') or \
|
||||
f.endswith('.webp'):
|
||||
newLevel = os.path.join(root, f).replace(os.path.join(path, 'OEBPS', 'Images'), '').count(os.sep)
|
||||
if level != -1 and level != newLevel:
|
||||
level = 0
|
||||
@@ -872,7 +850,7 @@ def detectCorruption(tmppath, orgpath):
|
||||
if 'decoder' in str(err) and 'not available' in str(err):
|
||||
raise RuntimeError('Pillow was compiled without JPG and/or PNG decoder.')
|
||||
else:
|
||||
raise RuntimeError('Image file %s is corrupted.' % pathOrg)
|
||||
raise RuntimeError('Image file %s is corrupted. Error: %s' % (pathOrg, str(err)))
|
||||
else:
|
||||
os.remove(os.path.join(root, name))
|
||||
if alreadyProcessed:
|
||||
@@ -932,7 +910,7 @@ def makeParser():
|
||||
|
||||
mainOptions.add_option("-p", "--profile", action="store", dest="profile", default="KV",
|
||||
help="Device profile (Available options: K1, K2, K34, K578, KDX, KPW, KV, KO, KoMT, KoG,"
|
||||
" KoGHD, KoA, KoAHD, KoAH2O, KoAO) [Default=KV]")
|
||||
" KoGHD, KoA, KoAHD, KoAH2O, KoAO, KoF) [Default=KV]")
|
||||
mainOptions.add_option("-m", "--manga-style", action="store_true", dest="righttoleft", default=False,
|
||||
help="Manga style (right-to-left reading and splitting)")
|
||||
mainOptions.add_option("-q", "--hq", action="store_true", dest="hq", default=False,
|
||||
@@ -1053,21 +1031,17 @@ def checkOptions():
|
||||
|
||||
def checkTools(source):
|
||||
source = source.upper()
|
||||
if source.endswith('.CBR') or source.endswith('.RAR'):
|
||||
rarExitCode = Popen('unrar', stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
|
||||
rarExitCode = rarExitCode.wait()
|
||||
if rarExitCode != 0 and rarExitCode != 1 and rarExitCode != 7:
|
||||
print('ERROR: UnRAR is missing!')
|
||||
exit(1)
|
||||
elif source.endswith('.CB7') or source.endswith('.7Z'):
|
||||
sevenzaExitCode = Popen('7za', stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
|
||||
sevenzaExitCode = sevenzaExitCode.wait()
|
||||
if sevenzaExitCode != 0 and sevenzaExitCode != 7:
|
||||
print('ERROR: 7za is missing!')
|
||||
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, shell=True)
|
||||
process.communicate()
|
||||
if process.returncode != 0 and process.returncode != 7:
|
||||
print('ERROR: 7z is missing!')
|
||||
exit(1)
|
||||
if options.format == 'MOBI':
|
||||
kindleGenExitCode = Popen('kindlegen -locale en', stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
|
||||
if kindleGenExitCode.wait() != 0:
|
||||
kindleGenExitCode = Popen('kindlegen -locale en', stdout=PIPE, stderr=STDOUT, shell=True)
|
||||
kindleGenExitCode.communicate()
|
||||
if kindleGenExitCode.returncode != 0:
|
||||
print('ERROR: KindleGen is missing!')
|
||||
exit(1)
|
||||
|
||||
@@ -1214,7 +1188,7 @@ def makeMOBIWorker(item):
|
||||
try:
|
||||
if os.path.getsize(item) < 629145600:
|
||||
output = Popen('kindlegen -dont_append_source -locale en "' + item + '"',
|
||||
stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
|
||||
stdout=PIPE, stderr=STDOUT, shell=True)
|
||||
for line in output.stdout:
|
||||
line = line.decode('utf-8')
|
||||
# ERROR: Generic error
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
||||
# Copyright (c) 2013-2018 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||
# Copyright (c) 2013-2019 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||
#
|
||||
# Permission to use, copy, modify, and/or distribute this software for
|
||||
# any purpose with or without fee is hereby granted, provided that the
|
||||
|
||||
81
kindlecomicconverter/comicarchive.py
Normal file
81
kindlecomicconverter/comicarchive.py
Normal file
@@ -0,0 +1,81 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
||||
# Copyright (c) 2013-2019 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||
#
|
||||
# Permission to use, copy, modify, and/or distribute this software for
|
||||
# any purpose with or without fee is hereby granted, provided that the
|
||||
# above copyright notice and this permission notice appear in all
|
||||
# copies.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
||||
# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
||||
# WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
|
||||
# AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||
# DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
|
||||
# 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 os
|
||||
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
|
||||
|
||||
|
||||
class ComicArchive:
|
||||
def __init__(self, filepath):
|
||||
self.filepath = filepath
|
||||
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, shell=True)
|
||||
for line in process.stdout:
|
||||
if b'Type =' 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.')
|
||||
|
||||
def extract(self, targetdir):
|
||||
if not os.path.isdir(targetdir):
|
||||
raise OSError('Target directory don\'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, shell=True)
|
||||
process.communicate()
|
||||
if process.returncode != 0:
|
||||
raise OSError('Failed to extract archive.')
|
||||
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, shell=True)
|
||||
process.communicate()
|
||||
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, shell=True)
|
||||
xml = process.communicate()
|
||||
if process.returncode != 0:
|
||||
raise OSError('Failed to extract archive.')
|
||||
try:
|
||||
return parseString(xml[0])
|
||||
except ExpatError:
|
||||
return None
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Based on initial version of DualMetaFix. Copyright (C) 2013 Kevin Hendricks
|
||||
# Changes for KCC Copyright (C) 2014-2018 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||
# Changes for KCC Copyright (C) 2014-2019 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
@@ -24,6 +24,7 @@ import shutil
|
||||
class DualMetaFixException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
# palm database offset constants
|
||||
number_of_pdb_records = 76
|
||||
first_pdb_record = 78
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
# Copyright (C) 2011 Stanislav (proDOOMman) Kosolapov <prodoomman@gmail.com>
|
||||
# Copyright (c) 2016 Alberto Planas <aplanas@gmail.com>
|
||||
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
||||
# Copyright (c) 2013-2018 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||
# Copyright (c) 2013-2019 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
@@ -20,12 +20,8 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import os
|
||||
from io import BytesIO
|
||||
from urllib.request import Request, urlopen
|
||||
from urllib.parse import quote
|
||||
from PIL import Image, ImageOps, ImageStat, ImageChops, ImageFilter
|
||||
from .shared import md5Checksum
|
||||
from . import __version__
|
||||
|
||||
|
||||
class ProfileData:
|
||||
@@ -86,7 +82,7 @@ class ProfileData:
|
||||
'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),
|
||||
'KV': ("Kindle Paperwhite 3/Voyage/Oasis", (1072, 1448), Palette16, 1.8),
|
||||
'KV': ("Kindle Paperwhite 3/4/Voyage/Oasis", (1072, 1448), Palette16, 1.8),
|
||||
'KO': ("Kindle Oasis 2", (1264, 1680), Palette16, 1.8),
|
||||
'KoMT': ("Kobo Mini/Touch", (600, 800), Palette16, 1.8),
|
||||
'KoG': ("Kobo Glo", (768, 1024), Palette16, 1.8),
|
||||
@@ -95,12 +91,14 @@ class ProfileData:
|
||||
'KoAHD': ("Kobo Aura HD", (1080, 1440), Palette16, 1.8),
|
||||
'KoAH2O': ("Kobo Aura H2O", (1080, 1430), Palette16, 1.8),
|
||||
'KoAO': ("Kobo Aura ONE", (1404, 1872), Palette16, 1.8),
|
||||
'KoF': ("Kobo Forma", (1440, 1920), Palette16, 1.8),
|
||||
'OTHER': ("Other", (0, 0), Palette16, 1.8),
|
||||
}
|
||||
|
||||
|
||||
class ComicPageParser:
|
||||
def __init__(self, source, options):
|
||||
Image.MAX_IMAGE_PIXELS = int(2048 * 2048 * 2048 // 4 // 3)
|
||||
self.opt = options
|
||||
self.source = source
|
||||
self.size = self.opt.profileData[1]
|
||||
@@ -345,15 +343,7 @@ class Cover:
|
||||
self.tomeid = 1
|
||||
else:
|
||||
self.tomeid = tomeid
|
||||
if self.tomeid in self.options.remoteCovers:
|
||||
try:
|
||||
source = urlopen(Request(quote(self.options.remoteCovers[self.tomeid]).replace('%3A', ':', 1),
|
||||
headers={'User-Agent': 'KindleComicConverter/' + __version__})).read()
|
||||
self.image = Image.open(BytesIO(source))
|
||||
except Exception:
|
||||
self.image = Image.open(source)
|
||||
else:
|
||||
self.image = Image.open(source)
|
||||
self.image = Image.open(source)
|
||||
self.process()
|
||||
|
||||
def process(self):
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2013-2018 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||
# Copyright (c) 2013-2019 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||
#
|
||||
# Permission to use, copy, modify, and/or distribute this software for
|
||||
# any purpose with or without fee is hereby granted, provided that the
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2013-2018 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||
# Copyright (c) 2013-2019 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||
#
|
||||
# Permission to use, copy, modify, and/or distribute this software for
|
||||
# any purpose with or without fee is hereby granted, provided that the
|
||||
@@ -18,14 +18,9 @@
|
||||
|
||||
import os
|
||||
from xml.dom.minidom import parse, Document
|
||||
from re import compile
|
||||
from zipfile import is_zipfile, ZipFile, ZIP_DEFLATED
|
||||
from subprocess import STDOUT, PIPE
|
||||
from psutil import Popen
|
||||
from tempfile import mkdtemp
|
||||
from shutil import rmtree
|
||||
from .shared import removeFromZIP, check7ZFile as is_7zfile
|
||||
from . import rarfile
|
||||
from . import comicarchive
|
||||
|
||||
|
||||
class MetadataParser:
|
||||
@@ -39,50 +34,20 @@ class MetadataParser:
|
||||
'Inkers': [],
|
||||
'Colorists': [],
|
||||
'Summary': '',
|
||||
'MUid': '',
|
||||
'Bookmarks': []}
|
||||
self.rawdata = None
|
||||
self.compressor = None
|
||||
self.format = None
|
||||
if self.source.endswith('.xml') and os.path.exists(self.source):
|
||||
self.rawdata = parse(self.source)
|
||||
self.parseXML()
|
||||
elif not self.source.endswith('.xml'):
|
||||
if is_zipfile(self.source):
|
||||
self.compressor = 'zip'
|
||||
with ZipFile(self.source) as zip_file:
|
||||
for member in zip_file.namelist():
|
||||
if member != 'ComicInfo.xml':
|
||||
continue
|
||||
with zip_file.open(member) as xml_file:
|
||||
self.rawdata = parse(xml_file)
|
||||
elif rarfile.is_rarfile(self.source):
|
||||
self.compressor = 'rar'
|
||||
with rarfile.RarFile(self.source) as rar_file:
|
||||
for member in rar_file.namelist():
|
||||
if member != 'ComicInfo.xml':
|
||||
continue
|
||||
with rar_file.open(member) as xml_file:
|
||||
self.rawdata = parse(xml_file)
|
||||
elif is_7zfile(self.source):
|
||||
self.compressor = '7z'
|
||||
workdir = mkdtemp('', 'KCC-')
|
||||
tmpXML = os.path.join(workdir, 'ComicInfo.xml')
|
||||
output = Popen('7za e "' + self.source + '" ComicInfo.xml -o"' + workdir + '"',
|
||||
stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
|
||||
extracted = False
|
||||
for line in output.stdout:
|
||||
if b"Everything is Ok" in line or b"No files to process" in line:
|
||||
extracted = True
|
||||
if not extracted:
|
||||
rmtree(workdir)
|
||||
raise OSError('Failed to extract 7ZIP file.')
|
||||
if os.path.isfile(tmpXML):
|
||||
self.rawdata = parse(tmpXML)
|
||||
rmtree(workdir)
|
||||
else:
|
||||
raise OSError('Failed to detect archive format.')
|
||||
if self.rawdata:
|
||||
self.parseXML()
|
||||
try:
|
||||
cbx = comicarchive.ComicArchive(self.source)
|
||||
self.rawdata = cbx.extractMetadata()
|
||||
self.format = cbx.type
|
||||
except OSError as e:
|
||||
raise UserWarning(e.strerror)
|
||||
if self.rawdata:
|
||||
self.parseXML()
|
||||
|
||||
def parseXML(self):
|
||||
if len(self.rawdata.getElementsByTagName('Series')) != 0:
|
||||
@@ -99,11 +64,6 @@ class MetadataParser:
|
||||
self.data[field + 's'].append(person)
|
||||
self.data[field + 's'] = list(set(self.data[field + 's']))
|
||||
self.data[field + 's'].sort()
|
||||
if len(self.rawdata.getElementsByTagName('ScanInformation')) != 0:
|
||||
coverId = compile('(MCD\\()(\\d+)(\\))')\
|
||||
.search(self.rawdata.getElementsByTagName('ScanInformation')[0].firstChild.nodeValue)
|
||||
if coverId:
|
||||
self.data['MUid'] = coverId.group(2)
|
||||
if len(self.rawdata.getElementsByTagName('Page')) != 0:
|
||||
for page in self.rawdata.getElementsByTagName('Page'):
|
||||
if 'Bookmark' in page.attributes and 'Image' in page.attributes:
|
||||
@@ -116,8 +76,7 @@ class MetadataParser:
|
||||
for row in (['Series', self.data['Series']], ['Volume', self.data['Volume']],
|
||||
['Number', self.data['Number']], ['Writer', ', '.join(self.data['Writers'])],
|
||||
['Penciller', ', '.join(self.data['Pencillers'])], ['Inker', ', '.join(self.data['Inkers'])],
|
||||
['Colorist', ', '.join(self.data['Colorists'])], ['Summary', self.data['Summary']],
|
||||
['ScanInformation', 'MCD(' + self.data['MUid'] + ')' if self.data['MUid'] else '']):
|
||||
['Colorist', ', '.join(self.data['Colorists'])], ['Summary', self.data['Summary']]):
|
||||
if self.rawdata.getElementsByTagName(row[0]):
|
||||
node = self.rawdata.getElementsByTagName(row[0])[0]
|
||||
if row[1]:
|
||||
@@ -138,8 +97,7 @@ class MetadataParser:
|
||||
for row in (['Series', self.data['Series']], ['Volume', self.data['Volume']],
|
||||
['Number', self.data['Number']], ['Writer', ', '.join(self.data['Writers'])],
|
||||
['Penciller', ', '.join(self.data['Pencillers'])], ['Inker', ', '.join(self.data['Inkers'])],
|
||||
['Colorist', ', '.join(self.data['Colorists'])], ['Summary', self.data['Summary']],
|
||||
['ScanInformation', 'MCD(' + self.data['MUid'] + ')' if self.data['MUid'] else '']):
|
||||
['Colorist', ', '.join(self.data['Colorists'])], ['Summary', self.data['Summary']]):
|
||||
if row[1]:
|
||||
main = doc.createElement(row[0])
|
||||
root.appendChild(main)
|
||||
@@ -154,20 +112,9 @@ class MetadataParser:
|
||||
tmpXML = os.path.join(workdir, 'ComicInfo.xml')
|
||||
with open(tmpXML, 'w', encoding='utf-8') as f:
|
||||
self.rawdata.writexml(f, encoding='utf-8')
|
||||
if is_zipfile(self.source):
|
||||
removeFromZIP(self.source, 'ComicInfo.xml')
|
||||
with ZipFile(self.source, mode='a', compression=ZIP_DEFLATED) as zip_file:
|
||||
zip_file.write(tmpXML, arcname=tmpXML.split(os.sep)[-1])
|
||||
elif rarfile.is_rarfile(self.source):
|
||||
raise NotImplementedError
|
||||
elif is_7zfile(self.source):
|
||||
output = Popen('7za a "' + self.source + '" "' + tmpXML + '"',
|
||||
stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
|
||||
extracted = False
|
||||
for line in output.stdout:
|
||||
if b"Everything is Ok" in line:
|
||||
extracted = True
|
||||
if not extracted:
|
||||
rmtree(workdir)
|
||||
raise OSError('Failed to modify 7ZIP file.')
|
||||
try:
|
||||
cbx = comicarchive.ComicArchive(self.source)
|
||||
cbx.addFile(tmpXML)
|
||||
except OSError as e:
|
||||
raise UserWarning(e.strerror)
|
||||
rmtree(workdir)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
||||
# Copyright (c) 2013-2018 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||
# Copyright (c) 2013-2019 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||
#
|
||||
# Based upon the code snippet by Ned Batchelder
|
||||
# (http://nedbatchelder.com/blog/200712/extracting_jpgs_from_pdfs.html)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
||||
# Copyright (c) 2013-2018 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||
# Copyright (c) 2013-2019 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||
#
|
||||
# Permission to use, copy, modify, and/or distribute this software for
|
||||
# any purpose with or without fee is hereby granted, provided that the
|
||||
@@ -22,9 +22,6 @@ import os
|
||||
from hashlib import md5
|
||||
from html.parser import HTMLParser
|
||||
from distutils.version import StrictVersion
|
||||
from shutil import rmtree, copy
|
||||
from tempfile import mkdtemp
|
||||
from zipfile import ZipFile, ZIP_DEFLATED
|
||||
from re import split
|
||||
from traceback import format_tb
|
||||
|
||||
@@ -50,7 +47,7 @@ class HTMLStripper(HTMLParser):
|
||||
def getImageFileName(imgfile):
|
||||
name, ext = os.path.splitext(imgfile)
|
||||
ext = ext.lower()
|
||||
if name.startswith('.') or (ext != '.png' and ext != '.jpg' and ext != '.jpeg' and ext != '.gif'):
|
||||
if name.startswith('.') or ext not in ['.png', '.jpg', '.jpeg', '.gif', '.webp']:
|
||||
return None
|
||||
return [name, ext]
|
||||
|
||||
@@ -86,38 +83,19 @@ def md5Checksum(fpath):
|
||||
return m.hexdigest()
|
||||
|
||||
|
||||
def check7ZFile(fpath):
|
||||
with open(fpath, 'rb') as fh:
|
||||
header = fh.read(6)
|
||||
return header == b"7z\xbc\xaf'\x1c"
|
||||
|
||||
|
||||
def removeFromZIP(zipfname, *filenames):
|
||||
tempdir = mkdtemp('', 'KCC-')
|
||||
try:
|
||||
tempname = os.path.join(tempdir, 'KCC.zip')
|
||||
with ZipFile(zipfname, 'r') as zipread:
|
||||
with ZipFile(tempname, 'w', compression=ZIP_DEFLATED) as zipwrite:
|
||||
for item in zipread.infolist():
|
||||
if item.filename not in filenames:
|
||||
zipwrite.writestr(item, zipread.read(item.filename))
|
||||
copy(tempname, zipfname)
|
||||
finally:
|
||||
rmtree(tempdir, True)
|
||||
|
||||
|
||||
def sanitizeTrace(traceback):
|
||||
return ''.join(format_tb(traceback))\
|
||||
.replace('C:/projects/kcc/', '') \
|
||||
.replace('c:/projects/kcc/', '') \
|
||||
.replace('C:/projects/kcc/', '')\
|
||||
.replace('c:/projects/kcc/', '')\
|
||||
.replace('C:/python36-x64/', '')\
|
||||
.replace('c:/python36-x64/', '')\
|
||||
.replace('C:\\projects\\kcc\\', '') \
|
||||
.replace('c:\\projects\\kcc\\', '') \
|
||||
.replace('C:\\projects\\kcc\\', '')\
|
||||
.replace('c:\\projects\\kcc\\', '')\
|
||||
.replace('C:\\python36-x64\\', '')\
|
||||
.replace('c:\\python36-x64\\', '')
|
||||
|
||||
|
||||
# noinspection PyUnresolvedReferences
|
||||
def dependencyCheck(level):
|
||||
missing = []
|
||||
if level > 2:
|
||||
@@ -145,11 +123,11 @@ def dependencyCheck(level):
|
||||
except ImportError:
|
||||
missing.append('python-slugify 1.2.1+')
|
||||
try:
|
||||
from PIL import PILLOW_VERSION as pillowVersion
|
||||
if StrictVersion('4.0.0') > StrictVersion(pillowVersion):
|
||||
missing.append('Pillow 4.0.0+')
|
||||
from PIL import __version__ as pillowVersion
|
||||
if StrictVersion('5.2.0') > StrictVersion(pillowVersion):
|
||||
missing.append('Pillow 5.2.0+')
|
||||
except ImportError:
|
||||
missing.append('Pillow 4.0.0+')
|
||||
missing.append('Pillow 5.2.0+')
|
||||
if len(missing) > 0:
|
||||
print('ERROR: ' + ', '.join(missing) + ' is not installed!')
|
||||
exit(1)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
||||
# Copyright (c) 2013-2018 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||
# Copyright (c) 2013-2019 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||
#
|
||||
# Permission to use, copy, modify, and/or distribute this software for
|
||||
# any purpose with or without fee is hereby granted, provided that the
|
||||
@@ -30,15 +30,15 @@ def start():
|
||||
os.environ['QT_AUTO_SCREEN_SCALE_FACTOR'] = "1"
|
||||
KCCAplication = KCC_gui.QApplicationMessaging(sys.argv)
|
||||
if KCCAplication.isRunning():
|
||||
if len(sys.argv) > 1:
|
||||
KCCAplication.sendMessage(sys.argv[1])
|
||||
for i in range(1, len(sys.argv)):
|
||||
KCCAplication.sendMessage(sys.argv[i])
|
||||
else:
|
||||
KCCAplication.sendMessage('ARISE')
|
||||
else:
|
||||
KCCWindow = KCC_gui.QMainWindowKCC()
|
||||
KCCUI = KCC_gui.KCCGUI(KCCAplication, KCCWindow)
|
||||
if len(sys.argv) > 1:
|
||||
KCCUI.handleMessage(sys.argv[1])
|
||||
for i in range(1, len(sys.argv)):
|
||||
KCCUI.handleMessage(sys.argv[i])
|
||||
sys.exit(KCCAplication.exec_())
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
kindlecomicconverter: binary-without-manpage usr/bin/kcc
|
||||
kindlecomicconverter: wrong-name-for-changelog-of-native-package usr/share/doc/kindlecomicconverter/changelog.Debian.gz
|
||||
kindlecomicconverter: file-missing-in-md5sums usr/share/doc/kindlecomicconverter/changelog.Debian.gz
|
||||
kindlecomicconverter: hardening-no-relro usr/bin/kcc
|
||||
@@ -1,11 +0,0 @@
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
Version=1.0
|
||||
Name=Kindle Comic Converter
|
||||
GenericName=Kindle Comic Converter
|
||||
Comment=Comic and Manga converter for e-book readers
|
||||
Icon=/usr/share/kindlecomicconverter/comic2ebook.png
|
||||
Exec=/usr/bin/kcc %f
|
||||
Terminal=false
|
||||
Categories=Graphics;
|
||||
MimeType=application/zip;application/x-rar;application/x-7z-compressed;
|
||||
Binary file not shown.
BIN
other/osx/7z
Normal file
BIN
other/osx/7z
Normal file
Binary file not shown.
BIN
other/osx/7z.so
Normal file
BIN
other/osx/7z.so
Normal file
Binary file not shown.
BIN
other/osx/7za
BIN
other/osx/7za
Binary file not shown.
@@ -30,7 +30,7 @@
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>MacOS/Kindle Comic Converter</string>
|
||||
<key>CFBundleGetInfoString</key>
|
||||
<string>KindleComicConverter 5.4.5, written 2012-2018 by Ciro Mattia Gonano and Pawel Jastrzebski</string>
|
||||
<string>KindleComicConverter 5.5.0, written 2012-2019 by Ciro Mattia Gonano and Pawel Jastrzebski</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>comic2ebook.icns</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
@@ -42,11 +42,11 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>5.4.5</string>
|
||||
<string>5.5.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>5.4.5</string>
|
||||
<string>5.5.0</string>
|
||||
<key>LSEnvironment</key>
|
||||
<dict>
|
||||
<key>PATH</key>
|
||||
@@ -55,7 +55,7 @@
|
||||
<key>LSHasLocalizedDisplayName</key>
|
||||
<false/>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>10.10.0</string>
|
||||
<string>10.12.0</string>
|
||||
<key>NSAppleScriptEnabled</key>
|
||||
<false/>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
|
||||
BIN
other/osx/unrar
BIN
other/osx/unrar
Binary file not shown.
BIN
other/windows/7z.dll
Normal file
BIN
other/windows/7z.dll
Normal file
Binary file not shown.
BIN
other/windows/7z.exe
Normal file
BIN
other/windows/7z.exe
Normal file
Binary file not shown.
Binary file not shown.
@@ -1,56 +1,22 @@
|
||||
****** ***** ****** UnRAR - free utility for RAR archives
|
||||
** ** ** ** ** ** ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
****** ******* ****** License for use and distribution of
|
||||
** ** ** ** ** ** ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
** ** ** ** ** ** FREEWARE version
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
The UnRAR utility is freeware. This means:
|
||||
|
||||
1. All copyrights to RAR and the utility UnRAR are exclusively
|
||||
owned by the author - Alexander Roshal.
|
||||
|
||||
2. The UnRAR utility may be freely distributed. It is allowed
|
||||
to distribute UnRAR inside of other software packages.
|
||||
|
||||
3. THE RAR ARCHIVER AND THE UnRAR UTILITY ARE DISTRIBUTED "AS IS".
|
||||
NO WARRANTY OF ANY KIND IS EXPRESSED OR IMPLIED. YOU USE AT
|
||||
YOUR OWN RISK. THE AUTHOR WILL NOT BE LIABLE FOR DATA LOSS,
|
||||
DAMAGES, LOSS OF PROFITS OR ANY OTHER KIND OF LOSS WHILE USING
|
||||
OR MISUSING THIS SOFTWARE.
|
||||
|
||||
4. Neither RAR binary code, WinRAR binary code, UnRAR source or UnRAR
|
||||
binary code may be used or reverse engineered to re-create the RAR
|
||||
compression algorithm, which is proprietary, without written
|
||||
permission of the author.
|
||||
|
||||
5. If you don't agree with terms of the license you must remove
|
||||
UnRAR files from your storage devices and cease to use the
|
||||
utility.
|
||||
|
||||
Thank you for your interest in RAR and UnRAR.
|
||||
|
||||
|
||||
Alexander L. Roshal
|
||||
|
||||
7-Zip
|
||||
~~~~~
|
||||
License for use and distribution
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
7-Zip Copyright (C) 1999-2012 Igor Pavlov.
|
||||
7-Zip Copyright (C) 1999-2018 Igor Pavlov.
|
||||
|
||||
Licenses for files are:
|
||||
The licenses for files are:
|
||||
|
||||
1) 7z.dll: GNU LGPL + unRAR restriction
|
||||
2) All other files: GNU LGPL
|
||||
1) 7z.dll:
|
||||
- The "GNU LGPL" as main license for most of the code
|
||||
- The "GNU LGPL" with "unRAR license restriction" for some code
|
||||
- The "BSD 3-clause License" for some code
|
||||
2) All other files: the "GNU LGPL".
|
||||
|
||||
The GNU LGPL + unRAR restriction means that you must follow both
|
||||
GNU LGPL rules and unRAR restriction rules.
|
||||
Redistributions in binary form must reproduce related license information from this file.
|
||||
|
||||
|
||||
Note:
|
||||
You can use 7-Zip on any computer, including a computer in a commercial
|
||||
Note:
|
||||
You can use 7-Zip on any computer, including a computer in a commercial
|
||||
organization. You don't need to register or pay for 7-Zip.
|
||||
|
||||
|
||||
@@ -67,21 +33,54 @@
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You can receive a copy of the GNU Lesser General Public License from
|
||||
You can receive a copy of the GNU Lesser General Public License from
|
||||
http://www.gnu.org/
|
||||
|
||||
|
||||
unRAR restriction
|
||||
-----------------
|
||||
|
||||
The decompression engine for RAR archives was developed using source
|
||||
|
||||
BSD 3-clause License
|
||||
--------------------
|
||||
|
||||
The "BSD 3-clause License" is used for the code in 7z.dll that implements LZFSE data decompression.
|
||||
That code was derived from the code in the "LZFSE compression library" developed by Apple Inc,
|
||||
that also uses the "BSD 3-clause License":
|
||||
|
||||
----
|
||||
Copyright (c) 2015-2016, Apple Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder(s) nor the names of any contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
----
|
||||
|
||||
|
||||
|
||||
|
||||
unRAR license restriction
|
||||
-------------------------
|
||||
|
||||
The decompression engine for RAR archives was developed using source
|
||||
code of unRAR program.
|
||||
All copyrights to original unRAR code are owned by Alexander Roshal.
|
||||
|
||||
The license for original unRAR code has the following restriction:
|
||||
|
||||
The unRAR sources cannot be used to re-create the RAR compression algorithm,
|
||||
which is proprietary. Distribution of modified unRAR sources in separate form
|
||||
The unRAR sources cannot be used to re-create the RAR compression algorithm,
|
||||
which is proprietary. Distribution of modified unRAR sources in separate form
|
||||
or as a part of other software is permitted, provided that it is clearly
|
||||
stated in the documentation and source comments that the code may
|
||||
not be used to develop a RAR (WinRAR) compatible archiver.
|
||||
|
||||
Binary file not shown.
@@ -1,5 +1,5 @@
|
||||
PyQt5>=5.6.0
|
||||
Pillow>=4.0.0
|
||||
Pillow>=5.2.0
|
||||
psutil>=5.0.0
|
||||
python-slugify>=1.2.1
|
||||
python-slugify>=1.2.1,<3.0.0
|
||||
raven>=6.0.0
|
||||
29
setup.py
29
setup.py
@@ -6,7 +6,7 @@ pip/pyinstaller build script for KCC.
|
||||
Install as Python package:
|
||||
python3 setup.py install
|
||||
|
||||
Create EXE/APP/DEB:
|
||||
Create EXE/APP:
|
||||
python3 setup.py build_binary
|
||||
"""
|
||||
|
||||
@@ -37,35 +37,18 @@ class BuildBinaryCommand(distutils.cmd.Command):
|
||||
VERSION = __version__
|
||||
if sys.platform == 'darwin':
|
||||
os.system('pyinstaller -y -F -i icons/comic2ebook.icns -n "Kindle Comic Converter" -w -s kcc.py')
|
||||
shutil.copy('other/osx/7za', 'dist/Kindle Comic Converter.app/Contents/Resources')
|
||||
shutil.copy('other/osx/unrar', 'dist/Kindle Comic Converter.app/Contents/Resources')
|
||||
shutil.copy('other/osx/7z', 'dist/Kindle Comic Converter.app/Contents/Resources')
|
||||
shutil.copy('other/osx/7z.so', 'dist/Kindle Comic Converter.app/Contents/Resources')
|
||||
shutil.copy('other/osx/Info.plist', 'dist/Kindle Comic Converter.app/Contents')
|
||||
shutil.copy('LICENSE.txt', 'dist/Kindle Comic Converter.app/Contents/Resources')
|
||||
shutil.copy('other/windows/Additional-LICENSE.txt', 'dist/Kindle Comic Converter.app/Contents/Resources')
|
||||
os.chmod('dist/Kindle Comic Converter.app/Contents/Resources/unrar', 0o777)
|
||||
os.chmod('dist/Kindle Comic Converter.app/Contents/Resources/7za', 0o777)
|
||||
os.chmod('dist/Kindle Comic Converter.app/Contents/Resources/7z', 0o777)
|
||||
os.system('appdmg kcc.json dist/KindleComicConverter_osx_' + VERSION + '.dmg')
|
||||
exit(0)
|
||||
elif sys.platform == 'win32':
|
||||
os.system('pyinstaller -y -F -i icons\comic2ebook.ico -n KCC -w --noupx kcc.py')
|
||||
os.system('pyinstaller -y -F -i icons\\comic2ebook.ico -n KCC -w --noupx kcc.py')
|
||||
exit(0)
|
||||
else:
|
||||
os.system('pyinstaller -y -F kcc.py')
|
||||
os.system('mkdir -p dist/usr/bin dist/usr/share/applications dist/usr/share/doc/kindlecomicconverter '
|
||||
'dist/usr/share/kindlecomicconverter dist/usr/share/lintian/overrides')
|
||||
os.system('mv dist/kcc dist/usr/bin')
|
||||
os.system('cp icons/comic2ebook.png dist/usr/share/kindlecomicconverter')
|
||||
os.system('cp LICENSE.txt dist/usr/share/doc/kindlecomicconverter/copyright')
|
||||
os.system('cp other/linux/kindlecomicconverter.desktop dist/usr/share/applications')
|
||||
os.system('cp other/linux/kindlecomicconverter dist/usr/share/lintian/overrides')
|
||||
os.chdir('dist')
|
||||
os.system('fpm -f -s dir -t deb -n kindlecomicconverter -v ' + VERSION +
|
||||
' -m "Pawel Jastrzebski <pawelj@iosphe.re>" --license "ISC" '
|
||||
'--description "$(printf "Comic and Manga converter for e-book '
|
||||
'readers.\nThis app allows you to transform your PNG, JPG, GIF, '
|
||||
'CBZ, CBR and CB7 files\ninto EPUB or MOBI format e-books.")" '
|
||||
'--url "https://kcc.iosphe.re/" --deb-priority "optional" --vendor "" '
|
||||
'--category "graphics" -d "unrar | unrar-free" -d "p7zip-full" -d "libc6" usr')
|
||||
exit(0)
|
||||
|
||||
|
||||
@@ -93,7 +76,7 @@ setuptools.setup(
|
||||
packages=['kindlecomicconverter'],
|
||||
install_requires=[
|
||||
'PyQt5>=5.6.0',
|
||||
'Pillow>=4.0.0',
|
||||
'Pillow>=5.2.0',
|
||||
'psutil>=5.0.0',
|
||||
'python-slugify>=1.2.1',
|
||||
'raven>=6.0.0',
|
||||
|
||||
Reference in New Issue
Block a user