1
0
mirror of https://github.com/ciromattia/kcc synced 2026-04-15 21:48:44 +00:00

Compare commits

..

38 Commits
4.5 ... 4.6.3

Author SHA1 Message Date
Paweł Jastrzębski
9014ed53d4 Merge pull request #151 from ciromattia/dev
4.6.3
2015-08-28 19:53:59 +02:00
Paweł Jastrzębski
cad05904f3 Updated README + version bump 2015-08-28 19:53:16 +02:00
Paweł Jastrzębski
10386d8af3 Added detailed platform info to error report 2015-08-28 19:50:43 +02:00
Paweł Jastrzębski
c991feb9ce GUI tweaks 2015-08-28 19:33:40 +02:00
Paweł Jastrzębski
d26eb7cdcd Set proper User-Agent 2015-08-24 17:18:39 +02:00
Paweł Jastrzębski
351084b703 Implemented error reporting 2015-08-24 17:11:35 +02:00
Paweł Jastrzębski
e861e7f6e8 Updated page endpoints 2015-08-24 17:07:49 +02:00
Paweł Jastrzębski
370c9d4df7 Tweaked setup.py 2015-08-09 10:08:24 +02:00
Paweł Jastrzębski
8e5704683c Fixed detection of file corruption 2015-08-04 09:54:39 +02:00
Paweł Jastrzębski
c65e1c8dea Merge pull request #150 from ciromattia/dev
4.6.2
2015-07-14 18:01:51 +02:00
Paweł Jastrzębski
677622c103 Updated README + version bump 2015-07-14 18:01:02 +02:00
Paweł Jastrzębski
af0ebb85a0 Escape HTML in metadata (close #148) 2015-07-14 17:58:59 +02:00
Paweł Jastrzębski
8af029ac92 Fixed MOBI header (close #149) 2015-07-14 17:40:33 +02:00
Paweł Jastrzębski
a268e12a90 Merge pull request #147 from ciromattia/dev
4.6.1
2015-07-05 12:16:27 +02:00
Paweł Jastrzębski
d621335e6c Updated README + version bump 2015-07-05 12:15:35 +02:00
Paweł Jastrzębski
ec1d9c2d93 Added ComicRack Summary field parsing (close #146) 2015-07-05 07:49:48 +02:00
Paweł Jastrzębski
85b9dbbf83 Detect too small input images 2015-07-05 06:57:56 +02:00
Paweł Jastrzębski
feeced44bf Tweaked KEPUB renamer (close #144) 2015-06-28 19:20:38 +02:00
Paweł Jastrzębski
cbea18398b Fixed Kobo TOC (close #145) 2015-06-28 18:31:47 +02:00
Paweł Jastrzębski
4c9857f14d Merge pull request #143 from ciromattia/dev
4.6
2015-06-21 08:44:37 +02:00
Paweł Jastrzębski
6b58ef4557 Updated README + version bump 2015-06-21 08:29:22 +02:00
Paweł Jastrzębski
24d697c965 Dropped Kindle Fire support 2015-06-21 08:04:28 +02:00
Paweł Jastrzębski
8b07d4eb69 Added Kindle Paperwhite 3 profile 2015-06-18 16:21:48 +02:00
Paweł Jastrzębski
e6c5ac915f Changed Kobo default output to KEPUB (close #141) 2015-06-17 22:35:24 +02:00
Paweł Jastrzębski
b22e4757a3 EPUB 3.0 output 2015-06-17 20:22:04 +02:00
Paweł Jastrzębski
91b06016bb Dependency update 2015-06-15 22:19:08 +02:00
Paweł Jastrzębski
5631391245 Error handling tweak 2015-05-31 09:36:20 +02:00
Paweł Jastrzębski
c33887b7b7 Fixed yet another tray icon anomaly 2015-05-20 20:21:12 +02:00
Paweł Jastrzębski
8d82f58f09 Merge pull request #137 from ciromattia/dev
4.5.1
2015-05-09 09:29:42 +02:00
Paweł Jastrzębski
36985f5169 Detect broken Pillow (close #135) 2015-05-07 22:37:48 +02:00
Paweł Jastrzębski
9d190c1585 Updated README + version bump 2015-05-07 18:06:16 +02:00
Paweł Jastrzębski
3834850317 Tweaked metadata editor 2015-05-07 17:49:03 +02:00
Paweł Jastrzębski
84fc23b979 Fixed CBR parsing anomalies (close #133) 2015-04-27 18:50:14 +02:00
Paweł Jastrzębski
77748afdbd Fixed supid typo 2015-04-26 18:47:40 +02:00
Paweł Jastrzębski
431e2ffaf2 Binary blob cleanup 2015-04-26 15:59:43 +02:00
Paweł Jastrzębski
16df4cd083 Added Kobo Glow HD profile 2015-04-22 20:38:17 +02:00
Paweł Jastrzębski
1aa34347c1 Added page-progression-direction tag to EPUB spine 2015-03-17 11:02:28 +01:00
Paweł Jastrzębski
561af90b06 Updated README and Setup 2015-03-09 20:08:48 +01:00
26 changed files with 607 additions and 422 deletions

View File

@@ -1,7 +1,7 @@
# KCC
**Kindle Comic Converter** is a Python app to convert comic/manga files or folders to EPUB, Panel View MOBI or E-Ink optimized CBZ.
It was initially developed for Kindle but since version 2.2 it outputs valid EPUB 2.0 so _**despite its name, KCC is
It was initially developed for Kindle but since version 4.6 it outputs valid EPUB 3.0 so _**despite its name, KCC is
actually a comic/manga to EPUB converter that every e-reader owner can happily use**_.
It can also optionally optimize images by applying a number of transformations.
@@ -29,6 +29,26 @@ You can find the latest released binary at the following links:
- **Linux:** [http://kcc.iosphe.re/Linux/](http://kcc.iosphe.re/Linux/)
- **OS X (10.8+):** [http://kcc.iosphe.re/OSX/](http://kcc.iosphe.re/OSX/)
## DEPENDENCIES
Following software is required to run Linux version of **KCC** and/or bare sources:
- Python 3.3+
- [PyQt](http://www.riverbankcomputing.co.uk/software/pyqt/download5) 5.2.0+
- [Pillow](http://pypi.python.org/pypi/Pillow/) 2.8.2+
- [psutil](https://pypi.python.org/pypi/psutil) 3.0.0+
- [python-slugify](http://pypi.python.org/pypi/python-slugify) 1.1.2+
- [scandir](https://pypi.python.org/pypi/scandir) 1.1.0+
On Debian based distributions these two commands should install all needed dependencies:
```
sudo apt-get install python3 python3-dev python3-pip python3-pyqt5 libpng-dev libjpeg-dev p7zip-full unrar
sudo pip3 install pillow python-slugify psutil scandir
```
### 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)*
## INPUT FORMATS
**KCC** can understand and convert, at the moment, the following input types:
- Folders containing: PNG, JPG or GIF files
@@ -37,29 +57,6 @@ You can find the latest released binary at the following links:
- CB7, 7Z *(With `7za` executable)*
- PDF *(Only extracting JPG images)*
## OPTIONAL REQUIREMENTS
- [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)*
### For running from source:
- Python 3.3+
- [PyQt](http://www.riverbankcomputing.co.uk/software/pyqt/download5) 5.2.0+
- [Pillow](http://pypi.python.org/pypi/Pillow/) 2.7.0+
- [psutil](https://pypi.python.org/pypi/psutil) 2.0+
- [python-slugify](http://pypi.python.org/pypi/python-slugify) 0.1.0+
- [scandir](https://pypi.python.org/pypi/scandir) 0.9+
On Debian based distributions these two commands should install all dependencies:
```
sudo apt-get install python3 python3-dev python3-pip python3-pyqt5 libpng-dev libjpeg-dev p7zip-full unrar
sudo pip3 install pillow python-slugify psutil scandir
```
### For freezing code:
- Windows - [py2exe](https://pypi.python.org/pypi/py2exe) 0.9.2.2+
- OS X - [py2app](https://bitbucket.org/ronaldoussoren/py2app) 0.9.0+
## USAGE
Should be pretty self-explanatory. All options have detailed informations in tooltips.
@@ -78,8 +75,7 @@ Options:
MAIN:
-p PROFILE, --profile=PROFILE
Device profile (Available options: K1, K2, K345, KDX,
KPW, KV, KFHD, KFHDX, KFHDX8, KFA, KoMT, KoG, KoA,
KoAHD, KoAH2O) [Default=KV]
KPW, KV, KoMT, KoG, KoGHD, KoA, KoAHD, KoAH2O) [Default=KV]
-q QUALITY, --quality=QUALITY
Quality of Panel View. 0 - Normal 1 - High 2 - Ultra [Default=0]
-m, --manga-style Manga style (Right-to-left reading and splitting)
@@ -148,19 +144,46 @@ The app relies and includes the following scripts:
- 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 Voyage](http://kcc.iosphe.re/Samples/Ubunchu!-KV.mobi)
* [Kindle Paperwhite](http://kcc.iosphe.re/Samples/Ubunchu!-KPW.mobi)
* [Kindle Paperwhite 3 / Voyage](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!-K345.mobi)
* [Kindle DX/DXG](http://kcc.iosphe.re/Samples/Ubunchu!-KDX.cbz)
* [Kobo Mini/Touch](http://kcc.iosphe.re/Samples/Ubunchu!-KoMT.cbz)
* [Kobo Glow](http://kcc.iosphe.re/Samples/Ubunchu!-KoG.cbz)
* [Kobo Aura](http://kcc.iosphe.re/Samples/Ubunchu!-KoA.cbz)
* [Kobo Aura HD](http://kcc.iosphe.re/Samples/Ubunchu!-KoAHD.cbz)
* [Kobo Aura H2O](http://kcc.iosphe.re/Samples/Ubunchu!-KoAH2O.cbz)
* [Kobo Mini/Touch](http://kcc.iosphe.re/Samples/Ubunchu-KoMT.kepub.epub)
* [Kobo Glo](http://kcc.iosphe.re/Samples/Ubunchu-KoG.kepub.epub)
* [Kobo Glo HD](http://kcc.iosphe.re/Samples/Ubunchu-KoGHD.kepub.epub)
* [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)
## CHANGELOG
####4.6.3:
* Implemented remote bug reporting
* Minor bug fixes and GUI tweaks
####4.6.2:
* Fixed critical MOBI header bug
* Fixed metadata encoding error
####4.6.1:
* Fixed KEPUB TOC generator
* Added warning about too small input files
* ComicRack Summary metadata field is now parsed
* Small tweaks of KEPUB output
####4.6:
* KEPUB is now default output for all Kobo profiles
* EPUB output now produce fully valid EPUB 3.0.1
* Added profile for Kindle Paperwhite 3
* Dropped official support of all Kindle Fire models and Kindle for Android
* Other minor tweaks
####4.5.1:
* Added Kobo Glo HD profile
* Fixed RAR/CBR parsing anomalies
* Minor bug fixes and tweaks
####4.5:
* Added simple ComicRack medadata editor
* Added simple ComicRack metadata editor
* Re-enabled Manga Cover Database support
* ComicRack bookmarks are now parsed
* Fixed glitches in Kindle Voyage profile
@@ -404,6 +427,14 @@ The app relies and includes the following scripts:
####1.0
* Initial version
## 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
Error report include **KCC** version, OS version and content of error message.
## KNOWN ISSUES
Please check [wiki page](https://github.com/ciromattia/kcc/wiki/Known-issues).

View File

@@ -462,6 +462,12 @@
<property name="selectionMode">
<enum>QAbstractItemView::NoSelection</enum>
</property>
<property name="verticalScrollMode">
<enum>QAbstractItemView::ScrollPerPixel</enum>
</property>
<property name="horizontalScrollMode">
<enum>QAbstractItemView::ScrollPerPixel</enum>
</property>
</widget>
<widget class="QPushButton" name="BasicModeButton">
<property name="geometry">

View File

@@ -397,6 +397,12 @@
<property name="selectionMode">
<enum>QAbstractItemView::NoSelection</enum>
</property>
<property name="verticalScrollMode">
<enum>QAbstractItemView::ScrollPerPixel</enum>
</property>
<property name="horizontalScrollMode">
<enum>QAbstractItemView::ScrollPerPixel</enum>
</property>
</widget>
<widget class="QPushButton" name="BasicModeButton">
<property name="geometry">

View File

@@ -126,6 +126,9 @@
</rect>
</property>
<layout class="QFormLayout" name="formLayout">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::ExpandingFieldsGrow</enum>
</property>
<item row="1" column="0">
<widget class="QLabel" name="label">
<property name="text">

View File

@@ -34,4 +34,4 @@ if __name__ == "__main__":
freeze_support()
print('comic2ebook v' + __version__ + ' - Written by Ciro Mattia Gonano and Pawel Jastrzebski.')
main(sys.argv[1:])
sys.exit(0)
sys.exit(0)

View File

@@ -34,4 +34,4 @@ if __name__ == "__main__":
freeze_support()
print('comic2panel v' + __version__ + ' - Written by Ciro Mattia Gonano and Pawel Jastrzebski.')
main(sys.argv[1:])
sys.exit(0)
sys.exit(0)

View File

@@ -1,5 +1,5 @@
#define MyAppName "Kindle Comic Converter"
#define MyAppVersion "4.5"
#define MyAppVersion "4.6.3"
#define MyAppPublisher "Ciro Mattia Gonano, Paweł Jastrzębski"
#define MyAppURL "http://kcc.iosphe.re/"
#define MyAppExeName "KCC.exe"

View File

@@ -76,6 +76,7 @@ class Ui_MetaEditorDialog(object):
self.formLayoutWidget.setObjectName("formLayoutWidget")
self.formLayout = QtWidgets.QFormLayout(self.formLayoutWidget)
self.formLayout.setContentsMargins(0, 0, 0, 0)
self.formLayout.setFieldGrowthPolicy(QtWidgets.QFormLayout.ExpandingFieldsGrow)
self.formLayout.setObjectName("formLayout")
self.label = QtWidgets.QLabel(self.formLayoutWidget)
self.label.setObjectName("label")

View File

@@ -20,20 +20,22 @@
import os
import sys
from urllib.parse import unquote
from urllib.request import urlopen, urlretrieve
from urllib.request import urlopen, urlretrieve, Request
from socket import gethostbyname_ex, gethostname
from traceback import format_tb
from time import sleep
from time import sleep, time
from datetime import datetime
from shutil import move
from http.server import BaseHTTPRequestHandler, HTTPServer
from socketserver import ThreadingMixIn
from subprocess import STDOUT, PIPE
from PyQt5 import QtGui, QtCore, QtWidgets, QtNetwork
from xml.dom.minidom import parse
from xml.dom.minidom import parse, Document
from psutil import Popen, Process
from copy import copy
from distutils.version import StrictVersion
from xml.sax.saxutils import escape
from platform import platform
from .shared import md5Checksum, HTMLStripper
from . import __version__
from . import comic2ebook
@@ -245,22 +247,22 @@ class VersionThread(QtCore.QThread):
def run(self):
try:
XML = urlopen('http://kcc.iosphe.re/Version.php')
XML = parse(XML)
XML = parse(urlopen(Request('https://kcc.iosphe.re/Version/',
headers={'User-Agent': 'KindleComicConverter/' + __version__})))
except Exception:
return
latestVersion = XML.childNodes[0].getElementsByTagName('latest')[0].childNodes[0].toxml()
latestVersion = XML.childNodes[0].getElementsByTagName('LatestVersion')[0].childNodes[0].toxml()
if StrictVersion(latestVersion) > StrictVersion(__version__):
if sys.platform.startswith('win'):
self.newVersion = latestVersion
self.md5 = XML.childNodes[0].getElementsByTagName('WindowsMD5')[0].childNodes[0].toxml()
self.md5 = XML.childNodes[0].getElementsByTagName('MD5')[0].childNodes[0].toxml()
MW.showDialog.emit('<b>New version released!</b> <a href="https://github.com/ciromattia/kcc/releases/">'
'See changelog.</a><br/><br/>Installed version: ' + __version__ +
'<br/>Current version: ' + latestVersion +
'<br/><br/>Would you like to start automatic update?', 'question')
self.getNewVersion()
else:
MW.addMessage.emit('<a href="http://kcc.iosphe.re/">'
MW.addMessage.emit('<a href="https://kcc.iosphe.re/">'
'<b>New version is available!</b></a> '
'(<a href="https://github.com/ciromattia/kcc/releases/">'
'Changelog</a>)', 'warning', False)
@@ -275,7 +277,7 @@ class VersionThread(QtCore.QThread):
try:
MW.modeConvert.emit(-1)
MW.progressBarTick.emit('Downloading update')
path = urlretrieve('http://kcc.iosphe.re/Windows/KindleComicConverter_win_'
path = urlretrieve('https://kcc.iosphe.re/Windows/KindleComicConverter_win_'
+ self.newVersion + '.exe', reporthook=self.getNewVersionTick)
if self.md5 != md5Checksum(path[0]):
raise Exception
@@ -350,7 +352,7 @@ class WorkerThread(QtCore.QThread):
def sanitizeTrace(self, traceback):
return ''.join(format_tb(traceback))\
.replace('C:\\Users\\AcidWeb\\Documents\\Projekty\\KCC\\', '')\
.replace('C:\\Users\\pawel\\Documents\\Projekty\\KCC\\', '')\
.replace('C:\\Python34\\', '')\
.replace('C:\\Python34_64\\', '')
@@ -375,9 +377,6 @@ class WorkerThread(QtCore.QThread):
elif GUI.QualityBox.checkState() == 2:
options.quality = 2
options.format = str(GUI.FormatBox.currentText())
if GUI.currentMode == 1:
if 'KFH' in profile:
options.upscale = True
# Advanced mode settings
if GUI.currentMode > 1:
@@ -557,6 +556,7 @@ class WorkerThread(QtCore.QThread):
class SystemTrayIcon(QtWidgets.QSystemTrayIcon):
def __init__(self):
super().__init__()
if self.isSystemTrayAvailable():
QtWidgets.QSystemTrayIcon.__init__(self, GUI.icons.programIcon, MW)
# noinspection PyUnresolvedReferences
@@ -930,6 +930,31 @@ class KCCGUI(KCC_ui.Ui_KCC):
def showDialog(self, message, kind):
if kind == 'error':
QtWidgets.QMessageBox.critical(MW, 'KCC - Error', message, QtWidgets.QMessageBox.Ok)
try:
doc = Document()
root = doc.createElement('KCCErrorReport')
doc.appendChild(root)
main = doc.createElement('Timestamp')
root.appendChild(main)
text = doc.createTextNode(datetime.fromtimestamp(time()).strftime('%Y-%m-%d %H:%M:%S'))
main.appendChild(text)
main = doc.createElement('OS')
root.appendChild(main)
text = doc.createTextNode(platform())
main.appendChild(text)
main = doc.createElement('Version')
root.appendChild(main)
text = doc.createTextNode(__version__)
main.appendChild(text)
main = doc.createElement('Error')
root.appendChild(main)
text = doc.createTextNode(message)
main.appendChild(text)
urlopen(Request(url='https://kcc.iosphe.re/ErrorHandle/', data=doc.toxml(encoding='utf-8'),
headers={'Content-Type': 'application/xml',
'User-Agent': 'KindleComicConverter/' + __version__}))
except:
pass
elif kind == 'question':
GUI.versionCheck.setAnswer(QtWidgets.QMessageBox.question(MW, 'KCC - Question', message,
QtWidgets.QMessageBox.Yes,
@@ -1161,63 +1186,53 @@ class KCCGUI(KCC_ui.Ui_KCC):
self.p.ionice(1)
self.profiles = {
"Kindle Voyage": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': False, 'Label': 'KV'},
"Kindle Paperwhite": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': False, 'Label': 'KPW'},
"K. PW 3/Voyage": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': False, 'Label': 'KV'},
"Kindle PW 1/2": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': False, 'Label': 'KPW'},
"Kindle": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': False, 'Label': 'K345'},
"Kindle DX/DXG": {'Quality': False, 'ForceExpert': False, 'DefaultFormat': 2,
'DefaultUpscale': False, 'Label': 'KDX'},
"K. Fire HD": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': True, 'Label': 'KFHD'},
"K. Fire HDX": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': True, 'Label': 'KFHDX'},
"K. Fire HDX 8.9": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': True, 'Label': 'KFHDX8'},
"Kobo Mini/Touch": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 2,
"Kobo Mini/Touch": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 1,
'DefaultUpscale': False, 'Label': 'KoMT'},
"Kobo Glow": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 2,
'DefaultUpscale': False, 'Label': 'KoG'},
"Kobo Aura": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 2,
"Kobo Glo": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 1,
'DefaultUpscale': False, 'Label': 'KoG'},
"Kobo Glo HD": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 1,
'DefaultUpscale': False, 'Label': 'KoGHD'},
"Kobo Aura": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 1,
'DefaultUpscale': False, 'Label': 'KoA'},
"Kobo Aura HD": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 2,
"Kobo Aura HD": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 1,
'DefaultUpscale': False, 'Label': 'KoAHD'},
"Kobo Aura H2O": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 2,
"Kobo Aura H2O": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 1,
'DefaultUpscale': False, 'Label': 'KoAH2O'},
"Other": {'Quality': False, 'ForceExpert': True, 'DefaultFormat': 1,
'DefaultUpscale': False, 'Label': 'OTHER'},
"Kindle for Android": {'Quality': False, 'ForceExpert': True, 'DefaultFormat': 0,
'DefaultUpscale': False, 'Label': 'KFA'},
"Kindle 1": {'Quality': False, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': False, 'Label': 'K1'},
"Kindle 2": {'Quality': False, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': False, 'Label': 'K2'}
}
profilesGUI = [
"Kindle Voyage",
"Kindle Paperwhite",
"K. PW 3/Voyage",
"Kindle PW 1/2",
"Kindle",
"Separator",
"K. Fire HD",
"K. Fire HDX",
"K. Fire HDX 8.9",
"Separator",
"Kobo Mini/Touch",
"Kobo Glow",
"Kobo Glo",
"Kobo Glo HD",
"Kobo Aura",
"Kobo Aura HD",
"Kobo Aura H2O",
"Separator",
"Other",
"Separator",
"Kindle for Android",
"Kindle 1",
"Kindle 2",
"Kindle DX/DXG",
]
statusBarLabel = QtWidgets.QLabel('<b><a href="http://kcc.iosphe.re/">HOMEPAGE</a> - <a href="https://github.'
statusBarLabel = QtWidgets.QLabel('<b><a href="https://kcc.iosphe.re/">HOMEPAGE</a> - <a href="https://github.'
'com/ciromattia/kcc/blob/master/README.md#issues--new-features--donations">DO'
'NATE</a> - <a href="https://github.com/ciromattia/kcc/wiki">WIKI</a> - <a hr'
'ef="http://www.mobileread.com/forums/showthread.php?t=207461">FORUM</a></b>')
@@ -1319,6 +1334,12 @@ class KCCGUI(KCC_ui.Ui_KCC):
self.versionCheck.start()
self.contentServer.start()
self.tray.show()
# Linux hack as PyQt 5.5 not hit mainstream distributions yet
if sys.platform.startswith('linux') and StrictVersion(QtCore.qVersion()) > StrictVersion('5.4.9'):
self.JobList.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel)
self.JobList.setHorizontalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel)
MW.setWindowTitle("Kindle Comic Converter " + __version__)
MW.show()
MW.raise_()
@@ -1339,6 +1360,8 @@ class KCCGUI_MetaEditor(KCC_MetaEditor_ui.Ui_MetaEditorDialog):
field.setText(self.parser.data[field.objectName()[:-4]])
for field in (self.WriterLine, self.PencillerLine, self.InkerLine, self.ColoristLine):
field.setText(', '.join(self.parser.data[field.objectName()[:-4] + 's']))
if self.SeriesLine.text() == '':
self.SeriesLine.setText(file.split('\\')[-1].split('.')[0])
def saveData(self):
for field in (self.VolumeLine, self.NumberLine, self.MUidLine):

View File

@@ -141,6 +141,8 @@ class Ui_KCC(object):
self.JobList.setStyleSheet("QListWidget#JobList {background:#ffffff;background-image:url(:/Other/icons/list_background.png);background-position:center center;background-repeat:no-repeat;}QScrollBar:vertical{border:1px solid #999;background:#FFF;width:5px;margin:0}QScrollBar::handle:vertical{background:DarkGray;min-height:0}QScrollBar::add-line:vertical{height:0;background:DarkGray;subcontrol-position:bottom;subcontrol-origin:margin}QScrollBar::sub-line:vertical{height:0;background:DarkGray;subcontrol-position:top;subcontrol-origin:margin}QScrollBar:horizontal{border:1px solid #999;background:#FFF;height:5px;margin:0}QScrollBar::handle:horizontal{background:DarkGray;min-width:0}QScrollBar::add-line:horizontal{width:0;background:DarkGray;subcontrol-position:bottom;subcontrol-origin:margin}QScrollBar::sub-line:horizontal{width:0;background:DarkGray;subcontrol-position:top;subcontrol-origin:margin}")
self.JobList.setProperty("showDropIndicator", False)
self.JobList.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection)
self.JobList.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel)
self.JobList.setHorizontalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel)
self.JobList.setObjectName("JobList")
self.BasicModeButton = QtWidgets.QPushButton(self.Form)
self.BasicModeButton.setGeometry(QtCore.QRect(10, 10, 141, 32))

View File

@@ -188,6 +188,8 @@ class Ui_KCC(object):
self.JobList.setStyleSheet("QListWidget#JobList {background:#ffffff;background-image:url(:/Other/icons/list_background.png);background-position:center center;background-repeat:no-repeat;}QScrollBar:vertical{border:1px solid #999;background:#FFF;width:5px;margin:0}QScrollBar::handle:vertical{background:DarkGray;min-height:0}QScrollBar::add-line:vertical{height:0;background:DarkGray;subcontrol-position:bottom;subcontrol-origin:margin}QScrollBar::sub-line:vertical{height:0;background:DarkGray;subcontrol-position:top;subcontrol-origin:margin}QScrollBar:horizontal{border:1px solid #999;background:#FFF;height:5px;margin:0}QScrollBar::handle:horizontal{background:DarkGray;min-width:0}QScrollBar::add-line:horizontal{width:0;background:DarkGray;subcontrol-position:bottom;subcontrol-origin:margin}QScrollBar::sub-line:horizontal{width:0;background:DarkGray;subcontrol-position:top;subcontrol-origin:margin}")
self.JobList.setProperty("showDropIndicator", False)
self.JobList.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection)
self.JobList.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel)
self.JobList.setHorizontalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel)
self.JobList.setObjectName("JobList")
self.BasicModeButton = QtWidgets.QPushButton(self.Form)
self.BasicModeButton.setGeometry(QtCore.QRect(5, 10, 156, 41))

View File

@@ -1,4 +1,4 @@
__version__ = '4.5'
__version__ = '4.6.3'
__license__ = 'ISC'
__copyright__ = '2012-2015, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>'
__docformat__ = 'restructuredtext en'
__docformat__ = 'restructuredtext en'

View File

@@ -22,6 +22,7 @@ from zipfile import is_zipfile, ZipFile
from subprocess import STDOUT, PIPE
from psutil import Popen
from shutil import move, copy
from scandir import walk
from . import rarfile
from .shared import check7ZFile as is_7zfile, saferReplace
@@ -45,7 +46,7 @@ class CBxArchive:
cbzFile = ZipFile(self.origFileName)
filelist = []
for f in cbzFile.namelist():
if f.startswith('__MACOSX') or f.endswith('.DS_Store') or f.endswith('thumbs.db'):
if f.startswith('__MACOSX') or f.endswith('.DS_Store') or f.endswith('humbs.db'):
pass # skip MacOS special files
elif f.endswith('/'):
try:
@@ -58,25 +59,18 @@ class CBxArchive:
def extractCBR(self, targetdir):
cbrFile = rarfile.RarFile(self.origFileName)
filelist = []
for f in cbrFile.namelist():
if f.startswith('__MACOSX') or f.endswith('.DS_Store') or f.endswith('thumbs.db'):
pass # skip MacOS special files
elif f.endswith('/'):
try:
os.makedirs(os.path.join(targetdir, f))
except Exception:
pass # the dir exists so we are going to extract the images only.
else:
filelist.append(f)
cbrFile.extractall(targetdir, filelist)
cbrFile.extractall(targetdir)
for root, dirnames, filenames in 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):
# Workaround for some wide UTF-8 + Popen abnormalities
if sys.platform.startswith('darwin'):
copy(self.origFileName, os.path.join(os.path.dirname(self.origFileName), 'TMP_KCC_TMP'))
self.origFileName = os.path.join(os.path.dirname(self.origFileName), 'TMP_KCC_TMP')
output = Popen('7za x "' + self.origFileName + '" -xr!__MACOSX -xr!.DS_Store -xr!thumbs.db -o"'
output = Popen('7za x "' + self.origFileName + '" -xr!__MACOSX -xr!.DS_Store -xr!thumbs.db -xr!Thumbs.db -o"'
+ targetdir + '"', stdout=PIPE, stderr=STDOUT, shell=True)
extracted = False
for line in output.stdout:

View File

@@ -20,6 +20,7 @@
import os
import sys
from time import strftime, gmtime
from copy import copy
from glob import glob
from json import loads
@@ -37,6 +38,7 @@ from PIL import Image
from subprocess import STDOUT, PIPE
from psutil import Popen, virtual_memory
from scandir import walk
from html import escape
try:
from PyQt5 import QtCore
except ImportError:
@@ -119,83 +121,98 @@ def buildHTML(path, imgfile, imgfilepath, forcePV=False):
os.makedirs(htmlpath)
htmlfile = os.path.join(htmlpath, filename[0] + '.html')
f = open(htmlfile, "w", encoding='UTF-8')
f.writelines(["<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" ",
"\"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n",
"<html xmlns=\"http://www.w3.org/1999/xhtml\">\n",
"<head>\n",
"<title>", filename[0], "</title>\n",
"<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>\n",
"<link href=\"", "../" * (backref - 1),
"style.css\" type=\"text/css\" rel=\"stylesheet\"/>\n",
"</head>\n",
"<body" + additionalStyle + ">\n",
"<div class=\"fs\">\n",
"<div><img src=\"", "../" * backref, "Images/", postfix, imgfile, "\" alt=\"",
imgfile, "\" class=\"singlePage\"/></div>\n"
])
if (options.panelview or forcePV) and not noPV:
options.panelviewused = True
if not noHorizontalPV and not noVerticalPV:
if rotatedPage:
if options.righttoleft:
order = [1, 3, 2, 4]
if options.iskindle:
f.writelines(["<?xml version=\"1.0\" encoding=\"utf-8\"?>\n",
"<!DOCTYPE html>\n",
"<html xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:epub=\"http://www.idpf.org/2007/ops\">\n",
"<head>\n",
"<title>", filename[0], "</title>\n",
"<link href=\"", "../" * (backref - 1), "style.css\" type=\"text/css\" rel=\"stylesheet\"/>\n",
"<meta charset=\"utf-8\"/>\n",
"</head>\n",
"<body" + additionalStyle + ">\n",
"<div class=\"fs\">\n",
"<div><img src=\"", "../" * backref, "Images/", postfix, imgfile, "\" alt=\"",
imgfile, "\" class=\"singlePage\"/></div>\n"
])
if (options.panelview or forcePV) and not noPV:
options.panelviewused = True
if not noHorizontalPV and not noVerticalPV:
if rotatedPage:
if options.righttoleft:
order = [1, 3, 2, 4]
else:
order = [2, 4, 1, 3]
else:
order = [2, 4, 1, 3]
else:
if options.righttoleft:
order = [2, 1, 4, 3]
else:
order = [1, 2, 3, 4]
boxes = ["BoxTL", "BoxTR", "BoxBL", "BoxBR"]
elif noHorizontalPV and not noVerticalPV:
if rotatedPage:
if options.righttoleft:
order = [1, 2]
else:
order = [2, 1]
else:
order = [1, 2]
boxes = ["BoxT", "BoxB"]
elif not noHorizontalPV and noVerticalPV:
if rotatedPage:
order = [1, 2]
else:
if options.righttoleft:
order = [2, 1]
if options.righttoleft:
order = [2, 1, 4, 3]
else:
order = [1, 2, 3, 4]
boxes = ["BoxTL", "BoxTR", "BoxBL", "BoxBR"]
elif noHorizontalPV and not noVerticalPV:
if rotatedPage:
if options.righttoleft:
order = [1, 2]
else:
order = [2, 1]
else:
order = [1, 2]
boxes = ["BoxL", "BoxR"]
else:
order = [1]
boxes = ["BoxC"]
for i in range(0, len(boxes)):
f.writelines(["<div id=\"" + boxes[i] + "\"><a class=\"app-amzn-magnify\" data-app-amzn-magnify=",
"'{\"targetId\":\"" + boxes[i] + "-Panel-Parent\", \"ordinal\":" + str(order[i]),
"}'></a></div>\n"])
if options.quality == 2 and not forcePV:
imgfilepv = imgfile.split(".")
imgfilepv[0] += "-hq"
imgfilepv = ".".join(imgfilepv)
else:
imgfilepv = imgfile
xl, yu, xr, yd = detectMargins(imgfilepath)
boxStyles = {"BoxTL": "left:" + xl + ";top:" + yu + ";",
"BoxTR": "right:" + xr + ";top:" + yu + ";",
"BoxBL": "left:" + xl + ";bottom:" + yd + ";",
"BoxBR": "right:" + xr + ";bottom:" + yd + ";",
"BoxT": "left:-25%;top:" + yu + ";",
"BoxB": "left:-25%;bottom:" + yd + ";",
"BoxL": "left:" + xl + ";top:-25%;",
"BoxR": "right:" + xr + ";top:-25%;",
"BoxC": "left:-25%;top:-25%;"
}
for box in boxes:
f.writelines(["<div id=\"" + box + "-Panel-Parent\" class=\"target-mag-parent\"><div id=\"",
"Generic-Panel\" class=\"target-mag\"><img style=\"" + boxStyles[box] + "\" src=\"",
"../" * backref, "Images/", postfix, imgfilepv, "\" alt=\"" + imgfilepv,
"\"/></div></div>\n",
])
f.writelines(["</div>\n</body>\n</html>"])
boxes = ["BoxT", "BoxB"]
elif not noHorizontalPV and noVerticalPV:
if rotatedPage:
order = [1, 2]
else:
if options.righttoleft:
order = [2, 1]
else:
order = [1, 2]
boxes = ["BoxL", "BoxR"]
else:
order = [1]
boxes = ["BoxC"]
for i in range(0, len(boxes)):
f.writelines(["<div id=\"" + boxes[i] + "\"><a class=\"app-amzn-magnify\" data-app-amzn-magnify=",
"'{\"targetId\":\"" + boxes[i] + "-Panel-Parent\", \"ordinal\":" + str(order[i]),
"}'></a></div>\n"])
if options.quality == 2 and not forcePV:
imgfilepv = imgfile.split(".")
imgfilepv[0] += "-hq"
imgfilepv = ".".join(imgfilepv)
else:
imgfilepv = imgfile
xl, yu, xr, yd = detectMargins(imgfilepath)
boxStyles = {"BoxTL": "left:" + xl + ";top:" + yu + ";",
"BoxTR": "right:" + xr + ";top:" + yu + ";",
"BoxBL": "left:" + xl + ";bottom:" + yd + ";",
"BoxBR": "right:" + xr + ";bottom:" + yd + ";",
"BoxT": "left:-25%;top:" + yu + ";",
"BoxB": "left:-25%;bottom:" + yd + ";",
"BoxL": "left:" + xl + ";top:-25%;",
"BoxR": "right:" + xr + ";top:-25%;",
"BoxC": "left:-25%;top:-25%;"
}
for box in boxes:
f.writelines(["<div id=\"" + box + "-Panel-Parent\" class=\"target-mag-parent\"><div id=\"",
"Generic-Panel\" class=\"target-mag\"><img style=\"" + boxStyles[box] + "\" src=\"",
"../" * backref, "Images/", postfix, imgfilepv, "\" alt=\"" + imgfilepv,
"\"/></div></div>\n",
])
f.writelines(["</div>\n</body>\n</html>"])
else:
f.writelines(["<?xml version=\"1.0\" encoding=\"utf-8\"?>\n",
"<!DOCTYPE html>\n",
"<html xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:epub=\"http://www.idpf.org/2007/ops\">\n",
"<head>\n",
"<title>", filename[0], "</title>\n",
"<link href=\"", "../" * (backref - 1), "style.css\" type=\"text/css\" rel=\"stylesheet\"/>\n",
"<meta charset=\"utf-8\"/>\n",
"</head>\n",
"<body" + additionalStyle + ">\n",
"<div class=\"epub:type=bodymatter\">\n",
"<img src=\"", "../" * backref, "Images/", postfix, imgfile, "\"/>\n",
"</div>\n",
"</body>\n</html>"
])
f.close()
return path, imgfile
@@ -205,11 +222,9 @@ def buildNCX(dstdir, title, chapters, chapterNames):
ncxfile = os.path.join(dstdir, 'OEBPS', 'toc.ncx')
f = open(ncxfile, "w", encoding='UTF-8')
f.writelines(["<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n",
"<!DOCTYPE ncx PUBLIC \"-//NISO//DTD ncx 2005-1//EN\" ",
"\"http://www.daisy.org/z3986/2005/ncx-2005-1.dtd\">\n",
"<ncx version=\"2005-1\" xml:lang=\"en-US\" xmlns=\"http://www.daisy.org/z3986/2005/ncx/\">\n",
"<head>\n",
"<meta name=\"dtb:uid\" content=\"", options.uuid, "\"/>\n",
"<meta name=\"dtb:uid\" content=\"urn:uuid:", options.uuid, "\"/>\n",
"<meta name=\"dtb:depth\" content=\"1\"/>\n",
"<meta name=\"dtb:totalPageCount\" content=\"0\"/>\n",
"<meta name=\"dtb:maxPageNumber\" content=\"0\"/>\n",
@@ -234,6 +249,44 @@ def buildNCX(dstdir, title, chapters, chapterNames):
f.close()
def buildNAV(dstdir, title, chapters, chapterNames):
navfile = os.path.join(dstdir, 'OEBPS', 'nav.xhtml')
f = open(navfile, "w", encoding='UTF-8')
f.writelines(["<?xml version=\"1.0\" encoding=\"utf-8\"?>\n",
"<!DOCTYPE html>\n",
"<html xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:epub=\"http://www.idpf.org/2007/ops\">\n",
"<head>\n",
"<title>" + title + "</title>\n",
"<meta charset=\"utf-8\"/>\n",
"</head>\n",
"<body>\n",
"<nav xmlns:epub=\"http://www.idpf.org/2007/ops\" epub:type=\"toc\" id=\"toc\">\n",
"<ol>\n"])
for chapter in chapters:
folder = chapter[0].replace(os.path.join(dstdir, 'OEBPS'), '').lstrip('/').lstrip('\\\\')
filename = getImageFileName(os.path.join(folder, chapter[1]))
if options.chapters:
title = chapterNames[chapter[1]]
elif os.path.basename(folder) != "Text":
title = chapterNames[os.path.basename(folder)]
f.write("<li><a href=\"" + filename[0].replace("\\", "/") + ".html\">" + title + "</a></li>\n")
f.writelines(["</ol>\n",
"</nav>\n",
"<nav epub:type=\"page-list\">\n",
"<ol>\n"
])
for chapter in chapters:
folder = chapter[0].replace(os.path.join(dstdir, 'OEBPS'), '').lstrip('/').lstrip('\\\\')
filename = getImageFileName(os.path.join(folder, chapter[1]))
if options.chapters:
title = chapterNames[chapter[1]]
elif os.path.basename(folder) != "Text":
title = chapterNames[os.path.basename(folder)]
f.write("<li><a href=\"" + filename[0].replace("\\", "/") + ".html\">" + title + "</a></li>\n")
f.write("</ol>\n</nav>\n</body>\n</html>")
f.close()
def buildOPF(dstdir, title, filelist, cover=None):
opffile = os.path.join(dstdir, 'OEBPS', 'content.opf')
profilelabel, deviceres, palette, gamma, panelviewsize = options.profileData
@@ -243,40 +296,45 @@ def buildOPF(dstdir, title, filelist, cover=None):
writingmode = "horizontal-lr"
f = open(opffile, "w", encoding='UTF-8')
f.writelines(["<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n",
"<package version=\"2.0\" unique-identifier=\"BookID\" ",
"<package version=\"3.0\" unique-identifier=\"BookID\" ",
"prefix=\"rendition: http://www.idpf.org/vocab/rendition/#\" ",
"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:language>en-US</dc:language>\n",
"<dc:identifier id=\"BookID\" opf:scheme=\"UUID\">", options.uuid, "</dc:identifier>\n"])
"<dc:identifier id=\"BookID\">urn:uuid:", options.uuid, "</dc:identifier>\n",
"<dc:contributor id=\"contributor\">KindleComicConverter-" + __version__ + "</dc:contributor>\n",
"<dc:description>", options.summary, "</dc:description>\n"])
for author in options.authors:
f.writelines(["<dc:creator>", author, "</dc:creator>\n"])
f.writelines(["<meta name=\"generator\" content=\"KindleComicConverter-" + __version__ + "\"/>\n",
"<meta name=\"RegionMagnification\" content=\"true\"/>\n",
"<meta name=\"region-mag\" content=\"true\"/>\n",
f.writelines(["<meta property=\"dcterms:modified\">" + strftime("%Y-%m-%dT%H:%M:%SZ", gmtime()) + "</meta>\n",
"<meta name=\"cover\" content=\"cover\"/>\n",
"<meta name=\"book-type\" content=\"comic\"/>\n",
"<meta name=\"rendition:layout\" content=\"pre-paginated\"/>\n",
"<meta name=\"zero-gutter\" content=\"true\"/>\n",
"<meta name=\"zero-margin\" content=\"true\"/>\n",
"<meta name=\"fixed-layout\" content=\"true\"/>\n"
"<meta name=\"rendition:orientation\" content=\"portrait\"/>\n",
"<meta name=\"orientation-lock\" content=\"portrait\"/>\n",
"<meta name=\"original-resolution\" content=\"",
str(deviceres[0]) + "x" + str(deviceres[1]), "\"/>\n",
"<meta name=\"primary-writing-mode\" content=\"", writingmode, "\"/>\n",
"<meta name=\"ke-border-color\" content=\"#ffffff\"/>\n",
"<meta name=\"ke-border-width\" content=\"0\"/>\n",
"</metadata>\n<manifest>\n<item id=\"ncx\" href=\"toc.ncx\" ",
"media-type=\"application/x-dtbncx+xml\"/>\n"])
"<meta property=\"rendition:orientation\">portrait</meta>\n",
"<meta property=\"rendition:spread\">portrait</meta>\n",
"<meta property=\"rendition:layout\">pre-paginated</meta>\n"])
if options.iskindle and options.profile != 'Custom':
f.writelines(["<meta name=\"original-resolution\" content=\"",
str(deviceres[0]) + "x" + str(deviceres[1]) + "\"/>\n",
"<meta name=\"book-type\" content=\"comic\"/>\n",
"<meta name=\"RegionMagnification\" content=\"true\"/>\n",
"<meta name=\"primary-writing-mode\" content=\"" + writingmode + "\"/>\n",
"<meta name=\"zero-gutter\" content=\"true\"/>\n",
"<meta name=\"zero-margin\" content=\"true\"/>\n",
"<meta name=\"ke-border-color\" content=\"#ffffff\"/>\n",
"<meta name=\"ke-border-width\" content=\"0\"/>\n"])
f.writelines(["</metadata>\n<manifest>\n<item id=\"ncx\" href=\"toc.ncx\" ",
"media-type=\"application/x-dtbncx+xml\"/>\n",
"<item id=\"nav\" href=\"nav.xhtml\" ",
"properties=\"nav\" media-type=\"application/xhtml+xml\"/>\n"])
if cover is not None:
filename = getImageFileName(cover.replace(os.path.join(dstdir, 'OEBPS'), '').lstrip('/').lstrip('\\\\'))
if '.png' == filename[1]:
mt = 'image/png'
else:
mt = 'image/jpeg'
f.write("<item id=\"cover\" href=\"Images/cover" + filename[1] + "\" media-type=\"" + mt + "\"/>\n")
f.write("<item id=\"cover\" href=\"Images/cover" + filename[1] + "\" media-type=\"" + mt +
"\" properties=\"cover-image\"/>\n")
reflist = []
for path in filelist:
folder = path[0].replace(os.path.join(dstdir, 'OEBPS'), '').lstrip('/').lstrip('\\\\').replace("\\", "/")
@@ -293,10 +351,13 @@ def buildOPF(dstdir, title, filelist, cover=None):
f.write("<item id=\"img_" + str(uniqueid) + "\" href=\"" + folder + "/" + path[1] + "\" media-type=\""
+ mt + "\"/>\n")
f.write("<item id=\"css\" href=\"Text/style.css\" media-type=\"text/css\"/>\n")
f.write("</manifest>\n<spine toc=\"ncx\">\n")
if options.righttoleft:
f.write("</manifest>\n<spine page-progression-direction=\"rtl\" toc=\"ncx\">\n")
else:
f.write("</manifest>\n<spine page-progression-direction=\"ltr\" toc=\"ncx\">\n")
for entry in reflist:
f.write("<itemref idref=\"page_" + entry + "\"/>\n")
f.write("</spine>\n<guide>\n</guide>\n</package>\n")
f.write("</spine>\n</package>\n")
f.close()
os.mkdir(os.path.join(dstdir, 'META-INF'))
f = open(os.path.join(dstdir, 'META-INF', 'container.xml'), 'w', encoding='UTF-8')
@@ -317,114 +378,126 @@ def buildEPUB(path, chapterNames, tomeNumber):
_, deviceres, _, _, panelviewsize = options.profileData
os.mkdir(os.path.join(path, 'OEBPS', 'Text'))
f = open(os.path.join(path, 'OEBPS', 'Text', 'style.css'), 'w', encoding='UTF-8')
# DON'T COMPRESS CSS. KINDLE WILL FAIL TO PARSE IT.
# Generic Panel View support + Margins fix for Non-Kindle devices.
f.writelines(["@page {\n",
"margin-bottom: 0;\n",
"margin-top: 0\n",
"}\n",
"body {\n",
"display: block;\n",
"margin-bottom: 0;\n",
"margin-left: 0;\n",
"margin-right: 0;\n",
"margin-top: 0;\n",
"padding-bottom: 0;\n",
"padding-left: 0;\n",
"padding-right: 0;\n",
"padding-top: 0;\n",
"text-align: left\n",
"}\n",
"div.fs {\n",
"height: ", str(deviceres[1]), "px;\n",
"width: ", str(deviceres[0]), "px;\n",
"position: relative;\n",
"display: block;\n",
"text-align: center\n",
"}\n",
"div.fs a {\n",
"display: block;\n",
"width : 100%;\n",
"height: 100%;\n",
"}\n",
"div.fs div {\n",
"position: absolute;\n",
"}\n",
"img.singlePage {\n",
"position: absolute;\n",
"height: ", str(deviceres[1]), "px;\n",
"width: ", str(deviceres[0]), "px;\n",
"}\n",
"div.target-mag-parent {\n",
"width:100%;\n",
"height:100%;\n",
"display:none;\n",
"}\n",
"div.target-mag {\n",
"position: absolute;\n",
"display: block;\n",
"overflow: hidden;\n",
"}\n",
"div.target-mag img {\n",
"position: absolute;\n",
"height: ", str(panelviewsize[1]), "px;\n",
"width: ", str(panelviewsize[0]), "px;\n",
"}\n",
"#Generic-Panel {\n",
"top: 0;\n",
"height: 100%;\n",
"width: 100%;\n",
"}\n",
"#BoxC {\n",
"top: 0;\n",
"height: 100%;\n",
"width: 100%;\n",
"}\n",
"#BoxT {\n",
"top: 0;\n",
"height: 50%;\n",
"width: 100%;\n",
"}\n",
"#BoxB {\n",
"bottom: 0;\n",
"height: 50%;\n",
"width: 100%;\n",
"}\n",
"#BoxL {\n",
"left: 0;\n",
"height: 100%;\n",
"width: 50%;\n",
"}\n",
"#BoxR {\n",
"right: 0;\n",
"height: 100%;\n",
"width: 50%;\n",
"}\n",
"#BoxTL {\n",
"top: 0;\n",
"left: 0;\n",
"height: 50%;\n",
"width: 50%;\n",
"}\n",
"#BoxTR {\n",
"top: 0;\n",
"right: 0;\n",
"height: 50%;\n",
"width: 50%;\n",
"}\n",
"#BoxBL {\n",
"bottom: 0;\n",
"left: 0;\n",
"height: 50%;\n",
"width: 50%;\n",
"}\n",
"#BoxBR {\n",
"bottom: 0;\n",
"right: 0;\n",
"height: 50%;\n",
"width: 50%;\n",
"}",
])
if options.iskindle:
f.writelines(["@page {\n",
"margin-bottom: 0;\n",
"margin-top: 0\n",
"}\n",
"body {\n",
"display: block;\n",
"margin-bottom: 0;\n",
"margin-left: 0;\n",
"margin-right: 0;\n",
"margin-top: 0;\n",
"padding-bottom: 0;\n",
"padding-left: 0;\n",
"padding-right: 0;\n",
"padding-top: 0;\n",
"text-align: left\n",
"}\n",
"div.fs {\n",
"height: ", str(deviceres[1]), "px;\n",
"width: ", str(deviceres[0]), "px;\n",
"position: relative;\n",
"display: block;\n",
"text-align: center\n",
"}\n",
"div.fs a {\n",
"display: block;\n",
"width : 100%;\n",
"height: 100%;\n",
"}\n",
"div.fs div {\n",
"position: absolute;\n",
"}\n",
"img.singlePage {\n",
"position: absolute;\n",
"height: ", str(deviceres[1]), "px;\n",
"width: ", str(deviceres[0]), "px;\n",
"}\n",
"div.target-mag-parent {\n",
"width:100%;\n",
"height:100%;\n",
"display:none;\n",
"}\n",
"div.target-mag {\n",
"position: absolute;\n",
"display: block;\n",
"overflow: hidden;\n",
"}\n",
"div.target-mag img {\n",
"position: absolute;\n",
"height: ", str(panelviewsize[1]), "px;\n",
"width: ", str(panelviewsize[0]), "px;\n",
"}\n",
"#Generic-Panel {\n",
"top: 0;\n",
"height: 100%;\n",
"width: 100%;\n",
"}\n",
"#BoxC {\n",
"top: 0;\n",
"height: 100%;\n",
"width: 100%;\n",
"}\n",
"#BoxT {\n",
"top: 0;\n",
"height: 50%;\n",
"width: 100%;\n",
"}\n",
"#BoxB {\n",
"bottom: 0;\n",
"height: 50%;\n",
"width: 100%;\n",
"}\n",
"#BoxL {\n",
"left: 0;\n",
"height: 100%;\n",
"width: 50%;\n",
"}\n",
"#BoxR {\n",
"right: 0;\n",
"height: 100%;\n",
"width: 50%;\n",
"}\n",
"#BoxTL {\n",
"top: 0;\n",
"left: 0;\n",
"height: 50%;\n",
"width: 50%;\n",
"}\n",
"#BoxTR {\n",
"top: 0;\n",
"right: 0;\n",
"height: 50%;\n",
"width: 50%;\n",
"}\n",
"#BoxBL {\n",
"bottom: 0;\n",
"left: 0;\n",
"height: 50%;\n",
"width: 50%;\n",
"}\n",
"#BoxBR {\n",
"bottom: 0;\n",
"right: 0;\n",
"height: 50%;\n",
"width: 50%;\n",
"}",
])
else:
f.writelines([
"@namespace epub \"http://www.idpf.org/2007/ops\";\n",
"@charset \"UTF-8\";\n",
"body {\n",
"margin: 0;\n",
"}\n",
"img {\n",
"position: absolute;\n",
"margin: 0;\n",
"z-index: 0;\n",
"height: 100%;\n",
"}"])
f.close()
for (dirpath, dirnames, filenames) in walk(os.path.join(path, 'OEBPS', 'Images')):
chapter = False
@@ -443,7 +516,7 @@ def buildEPUB(path, chapterNames, tomeNumber):
image.Cover(os.path.join(filelist[-1][0], filelist[-1][1]), cover, options, tomeNumber)
# Hack that force Panel View on at last one page
if lastfile and not options.panelviewused and 'Ko' not in options.profile \
and options.profile not in ['K1', 'K2', 'KDX', 'OTHER']:
and options.profile not in ['K1', 'K2', 'KDX', 'Custom']:
filelist[-1] = buildHTML(lastfile[0], lastfile[1], lastfile[2], True)
# Overwrite chapternames if tree is flat and ComicInfo.xml has bookmarks
if not chapterNames and options.chapters:
@@ -461,6 +534,7 @@ def buildEPUB(path, chapterNames, tomeNumber):
chapterNames[filename] = aChapter[1]
globaldiff = pageid - (aChapter[0] + globaldiff)
buildNCX(path, options.title, chapterlist, chapterNames)
buildNAV(path, options.title, chapterlist, chapterNames)
buildOPF(path, options.title, filelist, cover)
@@ -576,7 +650,7 @@ def getWorkFolder(afile):
if len(afile) > 240:
raise UserWarning("Path is too long.")
if os.path.isdir(afile):
workdir = mkdtemp('', 'KCC-TMP-')
workdir = mkdtemp('', 'KCC-')
try:
os.rmdir(workdir)
fullPath = os.path.join(workdir, 'OEBPS', 'Images')
@@ -595,7 +669,7 @@ def getWorkFolder(afile):
rmtree(path, True)
raise UserWarning("Failed to extract images.")
else:
workdir = mkdtemp('', 'KCC-TMP-')
workdir = mkdtemp('', 'KCC-')
cbx = cbxarchive.CBxArchive(afile)
if cbx.isCbxFile():
try:
@@ -605,7 +679,7 @@ def getWorkFolder(afile):
raise UserWarning("Failed to extract file.")
else:
rmtree(workdir, True)
raise TypeError
raise TypeError("Failed to detect archive format.")
if len(os.path.join(path, 'OEBPS', 'Images')) > 240:
raise UserWarning("Path is too long.")
move(path, path + "_temp")
@@ -616,8 +690,8 @@ def getWorkFolder(afile):
def getOutputFilename(srcpath, wantedname, ext, tomeNumber):
if srcpath[-1] == os.path.sep:
srcpath = srcpath[:-1]
if not ext.startswith('.'):
ext = '.' + ext
if 'Ko' in options.profile and options.format == 'EPUB':
ext = '.kepub.epub'
if wantedname is not None:
if wantedname.endswith(ext):
filename = os.path.abspath(wantedname)
@@ -629,7 +703,14 @@ def getOutputFilename(srcpath, wantedname, ext, tomeNumber):
elif os.path.isdir(srcpath):
filename = srcpath + tomeNumber + ext
else:
filename = os.path.splitext(srcpath)[0] + tomeNumber + ext
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)
else:
filename = os.path.splitext(srcpath)[0] + tomeNumber + ext
if os.path.isfile(filename):
counter = 0
basename = os.path.splitext(filename)[0]
@@ -644,6 +725,7 @@ def getComicInfo(path, originalPath):
options.authors = ['KCC']
options.remoteCovers = {}
options.chapters = []
options.summary = ''
titleSuffix = ''
if options.title == 'defaulttitle':
defaultTitle = True
@@ -662,7 +744,7 @@ def getComicInfo(path, originalPath):
options.authors = []
if defaultTitle:
if xml.data['Series']:
options.title = xml.data['Series']
options.title = escape(xml.data['Series'])
if xml.data['Volume']:
titleSuffix += ' V' + xml.data['Volume']
if xml.data['Number']:
@@ -670,7 +752,7 @@ def getComicInfo(path, originalPath):
options.title += titleSuffix
for field in ['Writers', 'Pencillers', 'Inkers', 'Colorists']:
for person in xml.data[field]:
options.authors.append(person)
options.authors.append(escape(person))
if len(options.authors) > 0:
options.authors = list(set(options.authors))
options.authors.sort()
@@ -680,6 +762,8 @@ def getComicInfo(path, originalPath):
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'])
os.remove(xmlPath)
@@ -882,6 +966,8 @@ def splitProcess(path, mode):
def detectCorruption(tmpPath, orgPath):
imageNumber = 0
imageSmaller = 0
for root, dirs, files in walk(tmpPath, False):
for name in files:
if getImageFileName(name) is not None:
@@ -895,11 +981,24 @@ def detectCorruption(tmpPath, orgPath):
img.verify()
img = Image.open(path)
img.load()
except Exception:
imageNumber += 1
if options.profileData[1][0] > img.size[0] and options.profileData[1][1] > img.size[1]:
imageSmaller += 1
except Exception as err:
rmtree(os.path.join(tmpPath, '..', '..'), True)
raise RuntimeError('Image file %s is corrupted.' % pathOrg)
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)
else:
os.remove(os.path.join(root, name))
if imageSmaller > imageNumber * 0.5 and not options.upscale and not options.stretch:
print("\nMore than half of images are smaller than target device resolution. "
"Consider enabling stretching or upscaling to improve readability.")
if GUI:
GUI.addMessage.emit('More than half of images are smaller than target device resolution.', 'warning', False)
GUI.addMessage.emit('Consider enabling stretching or upscaling to improve readability.', 'warning', False)
GUI.addMessage.emit('', '', False)
def detectMargins(path):
@@ -932,7 +1031,7 @@ def detectMargins(path):
def createNewTome():
tomePathRoot = mkdtemp('', 'KCC-TMP-')
tomePathRoot = mkdtemp('', 'KCC-')
tomePath = os.path.join(tomePathRoot, 'OEBPS', 'Images')
os.makedirs(tomePath)
return tomePath, tomePathRoot
@@ -970,8 +1069,8 @@ def makeParser():
otherOptions = OptionGroup(psr, "OTHER")
mainOptions.add_option("-p", "--profile", action="store", dest="profile", default="KV",
help="Device profile (Available options: K1, K2, K345, KDX, KPW, KV, KFHD, KFHDX, KFHDX8,"
" KFA, KoMT, KoG, KoA, KoAHD, KoAH2O) [Default=KV]")
help="Device profile (Available options: K1, K2, K345, KDX, KPW, KV, KoMT, KoG, KoGHD,"
" KoA, KoAHD, KoAH2O) [Default=KV]")
mainOptions.add_option("-q", "--quality", type="int", dest="quality", default="0",
help="Quality of Panel View. 0 - Normal 1 - High 2 - Ultra [Default=0]")
mainOptions.add_option("-m", "--manga-style", action="store_true", dest="righttoleft", default=False,
@@ -1031,14 +1130,17 @@ def checkOptions():
global options
options.panelview = True
options.panelviewused = False
options.iskindle = False
options.bordersColor = None
if options.format == 'Auto':
if options.profile in ['K1', 'K2', 'K345', 'KPW', 'KV', 'KFHD', 'KFHDX', 'KFHDX8', 'KFA']:
if options.profile in ['K1', 'K2', 'K345', 'KPW', 'KV']:
options.format = 'MOBI'
elif options.profile in ['Other']:
elif options.profile in ['OTHER', 'KoMT', 'KoG', 'KoGHD', 'KoA', 'KoAHD', 'KoAH2O']:
options.format = 'EPUB'
elif options.profile in ['KDX', 'KoMT', 'KoG', 'KoA', 'KoAHD', 'KoAH2O']:
elif options.profile in ['KDX']:
options.format = 'CBZ'
if options.profile in ['K1', 'K2', 'K345', 'KPW', 'KV', 'OTHER']:
options.iskindle = True
if options.white_borders:
options.bordersColor = 'white'
if options.black_borders:
@@ -1046,11 +1148,6 @@ def checkOptions():
# Splitting MOBI is not optional
if options.format == 'MOBI':
options.batchsplit = True
# Disabling grayscale conversion for Kindle Fire family.
if 'KFH' in options.profile or options.forcecolor:
options.forcecolor = True
else:
options.forcecolor = False
# Older Kindle don't need higher resolution files due lack of Panel View.
if options.profile == 'K1' or options.profile == 'K2' or options.profile == 'KDX':
options.quality = 0
@@ -1069,10 +1166,6 @@ def checkOptions():
# Kobo models can't use ultra quality mode
if options.quality == 2:
options.quality = 1
# Kindle for Android profile require target resolution.
if options.profile == 'KFA' and (options.customwidth == 0 or options.customheight == 0):
print("ERROR: Kindle for Android profile require --customwidth and --customheight options!")
sys.exit(1)
# CBZ files on Kindle DX/DXG support higher resolution
if options.profile == 'KDX' and options.format == 'CBZ':
options.customheight = 1200
@@ -1205,7 +1298,7 @@ def makeMOBIFix(item):
mobiPath = item.replace('.epub', '.mobi')
move(mobiPath, mobiPath + '_toclean')
try:
dualmetafix.DualMobiMetaFix(mobiPath + '_toclean', mobiPath, bytes(str(uuid4()), 'UTF-8'))
dualmetafix.DualMobiMetaFix(mobiPath + '_toclean', mobiPath, bytes(options.uuid, 'UTF-8'))
return [True]
except Exception as err:
return [False, format(err)]

View File

@@ -297,4 +297,4 @@ def main(argv=None, qtGUI=None):
else:
raise UserWarning("Provided path is not a directory.")
else:
raise UserWarning("Target height is not set.")
raise UserWarning("Target height is not set.")

View File

@@ -181,4 +181,4 @@ class DualMobiMetaFix:
replacesection(self.datain, datain_kf8, rec0)
self.datain.flush()
self.datain.close()
self.datain.close()

View File

@@ -82,14 +82,11 @@ class ProfileData:
'K2': ("Kindle 2", (600, 670), Palette15, 1.8, (900, 1005)),
'K345': ("Kindle", (600, 800), Palette16, 1.8, (900, 1200)),
'KDX': ("Kindle DX/DXG", (824, 1000), Palette16, 1.8, (1236, 1500)),
'KPW': ("Kindle Paperwhite", (758, 1024), Palette16, 1.8, (1137, 1536)),
'KV': ("Kindle Voyage", (1072, 1448), Palette16, 1.8, (1608, 2172)),
'KFHD': ("K. Fire HD", (800, 1280), PalleteNull, 1.0, (1200, 1920)),
'KFHDX': ("K. Fire HDX", (1200, 1920), PalleteNull, 1.0, (1800, 2880)),
'KFHDX8': ("K. Fire HDX 8.9", (1600, 2560), PalleteNull, 1.0, (2400, 3840)),
'KFA': ("Kindle for Android", (0, 0), PalleteNull, 1.0, (0, 0)),
'KPW': ("Kindle Paperwhite 1/2", (758, 1024), Palette16, 1.8, (1137, 1536)),
'KV': ("Kindle Paperwhite 3/Voyage", (1072, 1448), Palette16, 1.8, (1608, 2172)),
'KoMT': ("Kobo Mini/Touch", (600, 800), Palette16, 1.8, (900, 1200)),
'KoG': ("Kobo Glow", (768, 1024), Palette16, 1.8, (1152, 1536)),
'KoG': ("Kobo Glo", (768, 1024), Palette16, 1.8, (1152, 1536)),
'KoGHD': ("Kobo Glo HD", (1072, 1448), Palette16, 1.8, (1608, 2172)),
'KoA': ("Kobo Aura", (758, 1024), Palette16, 1.8, (1137, 1536)),
'KoAHD': ("Kobo Aura HD", (1080, 1440), Palette16, 1.8, (1620, 2160)),
'KoAH2O': ("Kobo Aura H2O", (1080, 1430), Palette16, 1.8, (1620, 2145)),

View File

@@ -38,6 +38,7 @@ class MetadataParser:
'Pencillers': [],
'Inkers': [],
'Colorists': [],
'Summary': '',
'MUid': '',
'Bookmarks': []}
self.rawdata = None
@@ -64,7 +65,7 @@ class MetadataParser:
self.rawdata = parse(xml_file)
elif is_7zfile(self.source):
self.compressor = '7z'
workdir = mkdtemp('', 'KCC-TMP-')
workdir = mkdtemp('', 'KCC-')
tmpXML = os.path.join(workdir, 'ComicInfo.xml')
output = Popen('7za e "' + self.source + '" ComicInfo.xml -o"' + workdir + '"',
stdout=PIPE, stderr=STDOUT, shell=True)
@@ -90,6 +91,8 @@ class MetadataParser:
self.data['Volume'] = self.rawdata.getElementsByTagName('Volume')[0].firstChild.nodeValue
if len(self.rawdata.getElementsByTagName('Number')) != 0:
self.data['Number'] = self.rawdata.getElementsByTagName('Number')[0].firstChild.nodeValue
if len(self.rawdata.getElementsByTagName('Summary')) != 0:
self.data['Summary'] = self.rawdata.getElementsByTagName('Summary')[0].firstChild.nodeValue
for field in ['Writer', 'Penciller', 'Inker', 'Colorist']:
if len(self.rawdata.getElementsByTagName(field)) != 0:
for person in self.rawdata.getElementsByTagName(field)[0].firstChild.nodeValue.split(', '):
@@ -113,7 +116,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'])],
['Colorist', ', '.join(self.data['Colorists'])], ['Summary', self.data['Summary']],
['ScanInformation', 'MCD(' + self.data['MUid'] + ')' if self.data['MUid'] else '']):
if self.rawdata.getElementsByTagName(row[0]):
node = self.rawdata.getElementsByTagName(row[0])[0]
@@ -135,7 +138,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'])],
['Colorist', ', '.join(self.data['Colorists'])], ['Summary', self.data['Summary']],
['ScanInformation', 'MCD(' + self.data['MUid'] + ')' if self.data['MUid'] else '']):
if row[1]:
main = doc.createElement(row[0])
@@ -147,7 +150,7 @@ class MetadataParser:
with open(self.source, 'w', encoding='utf-8') as f:
self.rawdata.writexml(f, encoding='utf-8')
else:
workdir = mkdtemp('', 'KCC-TMP-')
workdir = mkdtemp('', 'KCC-')
tmpXML = os.path.join(workdir, 'ComicInfo.xml')
with open(tmpXML, 'w', encoding='utf-8') as f:
self.rawdata.writexml(f, encoding='utf-8')

View File

@@ -29,7 +29,7 @@ class PdfJpgExtract:
self.origFileName = origFileName
self.filename = os.path.splitext(origFileName)
# noinspection PyUnusedLocal
self.path = self.filename[0] + "-KCC-TMP-" + ''.join(choice(ascii_uppercase + digits) for x in range(3))
self.path = self.filename[0] + "-KCC-" + ''.join(choice(ascii_uppercase + digits) for x in range(3))
def getPath(self):
return self.path

View File

@@ -104,9 +104,9 @@ def saferReplace(old, new):
def removeFromZIP(zipfname, *filenames):
tempdir = mkdtemp('', 'KCC-TMP-')
tempdir = mkdtemp('', 'KCC-')
try:
tempname = os.path.join(tempdir, 'KCC-TMP.zip')
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():
@@ -130,28 +130,28 @@ def dependencyCheck(level):
if level > 1:
try:
from psutil import __version__ as psutilVersion
if StrictVersion('2.0.0') > StrictVersion(psutilVersion):
missing.append('psutil 2.0.0+')
if StrictVersion('3.0.0') > StrictVersion(psutilVersion):
missing.append('psutil 3.0.0+')
except ImportError:
missing.append('psutil 2.0.0+')
missing.append('psutil 3.0.0+')
try:
from slugify import __version__ as slugifyVersion
if StrictVersion('0.1.0') > StrictVersion(slugifyVersion):
missing.append('python-slugify 0.1.0+')
if StrictVersion('1.1.2') > StrictVersion(slugifyVersion):
missing.append('python-slugify 1.1.2+')
except ImportError:
missing.append('python-slugify 0.1.0+')
missing.append('python-slugify 1.1.2+')
try:
from PIL import PILLOW_VERSION as pillowVersion
if StrictVersion('2.7.0') > StrictVersion(pillowVersion):
missing.append('Pillow 2.7.0+')
if StrictVersion('2.8.2') > StrictVersion(pillowVersion):
missing.append('Pillow 2.8.2+')
except ImportError:
missing.append('Pillow 2.7.0+')
missing.append('Pillow 2.8.2+')
try:
from scandir import __version__ as scandirVersion
if StrictVersion('0.9') > StrictVersion(scandirVersion):
missing.append('scandir 0.9+')
if StrictVersion('1.1') > StrictVersion(scandirVersion):
missing.append('scandir 1.1+')
except ImportError:
missing.append('scandir 0.9+')
missing.append('scandir 1.1+')
if len(missing) > 0:
print('ERROR: ' + ', '.join(missing) + ' is not installed!')
exit(1)
exit(1)

BIN
other/7za Executable file

Binary file not shown.

BIN
other/7za.exe Normal file

Binary file not shown.

BIN
other/UnRAR.exe Normal file

Binary file not shown.

BIN
other/unrar Executable file

Binary file not shown.

131
setup.py
View File

@@ -1,25 +1,32 @@
#!/usr/bin/env python3
"""
py2exe/py2app build script for KCC.
pip/py2exe/py2app build script for KCC.
Usage (Windows):
python setup.py py2exe
py -3.4 setup.py py2exe
Usage (Linux):
python3 setup.py make_pyz or python3 setup.py install
Usage (Mac OS X):
python setup.py py2app
python3 setup.py py2app
"""
from sys import platform, version_info
from sys import platform, version_info, argv
from kcc import __version__
if version_info[0] != 3:
print('ERROR: This is Python 3 script!')
exit(1)
NAME = "KindleComicConverter"
NAME = 'KindleComicConverter'
VERSION = __version__
MAIN = "kcc.py"
MAIN = 'kcc.py'
extra_options = {}
if platform == "darwin":
# noinspection PyUnresolvedReferences
if platform == 'darwin':
from setuptools import setup
from os import chmod, makedirs
from shutil import copyfile
extra_options = dict(
setup_requires=['py2app'],
app=[MAIN],
@@ -32,8 +39,8 @@ if platform == "darwin":
plist=dict(
CFBundleName=NAME,
CFBundleShortVersionString=VERSION,
CFBundleGetInfoString=NAME + " " + VERSION +
", written 2012-2015 by Ciro Mattia Gonano and Pawel Jastrzebski",
CFBundleGetInfoString=NAME + ' ' + VERSION +
', written 2012-2015 by Ciro Mattia Gonano and Pawel Jastrzebski',
CFBundleExecutable=NAME,
CFBundleDocumentTypes=[
dict(
@@ -52,12 +59,12 @@ if platform == "darwin":
)
)
)
elif platform == "win32":
elif platform == 'win32':
# noinspection PyUnresolvedReferences
import py2exe
import platform
from platform import architecture
from distutils.core import setup
if platform.architecture()[0] == '64bit':
if architecture()[0] == '64bit':
suffix = '_64'
else:
suffix = ''
@@ -70,44 +77,86 @@ elif platform == "win32":
'C:\Python34' + suffix + '\Lib\site-packages\PyQt5\libGLESv2.dll',
'C:\Python34' + suffix + '\Lib\site-packages\PyQt5\libEGL.dll'])]
extra_options = dict(
options={'py2exe': {"bundle_files": 1,
"dist_dir": "dist" + suffix,
"compressed": True,
"includes": ["sip"],
"excludes": ["tkinter"],
"optimize": 2}},
windows=[{"script": MAIN,
"dest_base": "KCC",
"version": VERSION,
"copyright": "Ciro Mattia Gonano, Pawel Jastrzebski © 2012-2015",
"legal_copyright": "ISC License (ISCL)",
"product_version": VERSION,
"product_name": "Kindle Comic Converter",
"file_description": "Kindle Comic Converter",
"icon_resources": [(1, "icons\comic2ebook.ico")]}],
options={'py2exe': {'bundle_files': 1,
'dist_dir': 'dist' + suffix,
'compressed': True,
'includes': ['sip'],
'excludes': ['tkinter'],
'optimize': 2}},
windows=[{'script': MAIN,
'dest_base': 'KCC',
'version': VERSION,
'copyright': 'Ciro Mattia Gonano, Pawel Jastrzebski © 2012-2015',
'legal_copyright': 'ISC License (ISCL)',
'product_version': VERSION,
'product_name': 'Kindle Comic Converter',
'file_description': 'Kindle Comic Converter',
'icon_resources': [(1, 'icons\comic2ebook.ico')]}],
zipfile=None,
data_files=additional_files)
else:
print('Please use setup.sh to build Linux package.')
exit()
if len(argv) > 1 and argv[1] == 'make_pyz':
from os import system
script = '''
cp kcc.py __main__.py
zip kcc.zip __main__.py kcc/*.py
echo "#!/usr/bin/env python3" > kcc-bin
cat kcc.zip >> kcc-bin
chmod +x kcc-bin
cp kcc-c2e.py __main__.py
zip kcc-c2e.zip __main__.py kcc/*.py
echo "#!/usr/bin/env python3" > kcc-c2e-bin
cat kcc-c2e.zip >> kcc-c2e-bin
chmod +x kcc-c2e-bin
cp kcc-c2p.py __main__.py
zip kcc-c2p.zip __main__.py kcc/*.py
echo "#!/usr/bin/env python3" > kcc-c2p-bin
cat kcc-c2p.zip >> kcc-c2p-bin
chmod +x kcc-c2p-bin
tar --xform s:^.*/:: --xform s/LICENSE.txt/LICENSE/ --xform s/kcc-bin/kcc/ --xform s/kcc-c2p-bin/kcc-c2p/ \
--xform s/kcc-c2e-bin/kcc-c2e/ --xform s/comic2ebook/kcc/ -czf KindleComicConverter_linux_'''\
+ VERSION + '''.tar.gz kcc-bin kcc-c2e-bin kcc-c2p-bin LICENSE.txt README.md icons/comic2ebook.png
rm __main__.py kcc.zip kcc-c2e.zip kcc-c2p.zip kcc-bin kcc-c2e-bin kcc-c2p-bin
'''
system("bash -c '%s'" % script)
exit(0)
else:
from setuptools import setup
from os import makedirs
from shutil import copyfile
makedirs('build/_scripts/', exist_ok=True)
copyfile('kcc.py', 'build/_scripts/kcc')
copyfile('kcc-c2e.py', 'build/_scripts/kcc-c2e')
copyfile('kcc-c2p.py', 'build/_scripts/kcc-c2p')
extra_options = dict(
scripts=['build/_scripts/kcc', 'build/_scripts/kcc-c2e', 'build/_scripts/kcc-c2p'],
packages=['kcc'],
install_requires=[
'Pillow>=2.8.2',
'psutil>=3.0.0',
'python-slugify>=1.1.2',
'scandir>=1.1.0',
],
zip_safe=False,
)
# noinspection PyUnboundLocalVariable
setup(
name=NAME,
version=VERSION,
author="Ciro Mattia Gonano, Pawel Jastrzebski",
author_email="ciromattia@gmail.com, pawelj@iosphe.re",
description="Kindle Comic Converter",
license="ISC License (ISCL)",
keywords="kindle comic mobipocket mobi cbz cbr manga",
url="http://github.com/ciromattia/kcc",
author='Ciro Mattia Gonano, Pawel Jastrzebski',
author_email='ciromattia@gmail.com, pawelj@iosphe.re',
description='Comic and manga converter for E-Book readers.',
license='ISC License (ISCL)',
keywords='kindle comic mobipocket mobi cbz cbr manga',
url='http://github.com/ciromattia/kcc',
**extra_options
)
if platform == "darwin":
from os import chmod, makedirs
from shutil import copyfile
makedirs('dist/' + NAME + '.app/Contents/PlugIns/platforms')
if platform == 'darwin':
makedirs('dist/' + NAME + '.app/Contents/PlugIns/platforms', exist_ok=True)
copyfile('other/libqcocoa.dylib', 'dist/' + NAME + '.app/Contents/PlugIns/platforms/libqcocoa.dylib')
chmod('dist/' + NAME + '.app/Contents/Resources/unrar', 0o777)
chmod('dist/' + NAME + '.app/Contents/Resources/7za', 0o777)
chmod('dist/' + NAME + '.app/Contents/Resources/7za', 0o777)

View File

@@ -1,25 +0,0 @@
#!/bin/bash
# Linux Python package build script
VERSION="4.5"
cp kcc.py __main__.py
zip kcc.zip __main__.py kcc/*.py
echo "#!/usr/bin/env python3" > kcc-bin
cat kcc.zip >> kcc-bin
chmod +x kcc-bin
cp kcc-c2e.py __main__.py
zip kcc-c2e.zip __main__.py kcc/*.py
echo "#!/usr/bin/env python3" > kcc-c2e-bin
cat kcc-c2e.zip >> kcc-c2e-bin
chmod +x kcc-c2e-bin
cp kcc-c2p.py __main__.py
zip kcc-c2p.zip __main__.py kcc/*.py
echo "#!/usr/bin/env python3" > kcc-c2p-bin
cat kcc-c2p.zip >> kcc-c2p-bin
chmod +x kcc-c2p-bin
tar --xform s:^.*/:: --xform s/kcc-bin/kcc/ --xform s/kcc-c2p-bin/kcc-c2p/ --xform s/kcc-c2e-bin/kcc-c2e/ --xform s/comic2ebook/kcc/ -czf KindleComicConverter_linux_${VERSION}.tar.gz kcc-bin kcc-c2e-bin kcc-c2p-bin LICENSE.txt icons/comic2ebook.png
rm __main__.py kcc.zip kcc-c2e.zip kcc-c2p.zip kcc-bin kcc-c2e-bin kcc-c2p-bin