mirror of
https://github.com/ciromattia/kcc
synced 2026-04-18 15:08:48 +00:00
Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9014ed53d4 | ||
|
|
cad05904f3 | ||
|
|
10386d8af3 | ||
|
|
c991feb9ce | ||
|
|
d26eb7cdcd | ||
|
|
351084b703 | ||
|
|
e861e7f6e8 | ||
|
|
370c9d4df7 | ||
|
|
8e5704683c | ||
|
|
c65e1c8dea | ||
|
|
677622c103 | ||
|
|
af0ebb85a0 | ||
|
|
8af029ac92 | ||
|
|
a268e12a90 | ||
|
|
d621335e6c | ||
|
|
ec1d9c2d93 | ||
|
|
85b9dbbf83 | ||
|
|
feeced44bf | ||
|
|
cbea18398b |
22
README.md
22
README.md
@@ -156,6 +156,20 @@ The app relies and includes the following scripts:
|
|||||||
* [Kobo Aura H2O](http://kcc.iosphe.re/Samples/Ubunchu-KoAH2O.kepub.epub)
|
* [Kobo Aura H2O](http://kcc.iosphe.re/Samples/Ubunchu-KoAH2O.kepub.epub)
|
||||||
|
|
||||||
## CHANGELOG
|
## 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:
|
####4.6:
|
||||||
* KEPUB is now default output for all Kobo profiles
|
* KEPUB is now default output for all Kobo profiles
|
||||||
* EPUB output now produce fully valid EPUB 3.0.1
|
* EPUB output now produce fully valid EPUB 3.0.1
|
||||||
@@ -413,6 +427,14 @@ The app relies and includes the following scripts:
|
|||||||
####1.0
|
####1.0
|
||||||
* Initial version
|
* 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
|
## KNOWN ISSUES
|
||||||
Please check [wiki page](https://github.com/ciromattia/kcc/wiki/Known-issues).
|
Please check [wiki page](https://github.com/ciromattia/kcc/wiki/Known-issues).
|
||||||
|
|
||||||
|
|||||||
@@ -462,6 +462,12 @@
|
|||||||
<property name="selectionMode">
|
<property name="selectionMode">
|
||||||
<enum>QAbstractItemView::NoSelection</enum>
|
<enum>QAbstractItemView::NoSelection</enum>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="verticalScrollMode">
|
||||||
|
<enum>QAbstractItemView::ScrollPerPixel</enum>
|
||||||
|
</property>
|
||||||
|
<property name="horizontalScrollMode">
|
||||||
|
<enum>QAbstractItemView::ScrollPerPixel</enum>
|
||||||
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QPushButton" name="BasicModeButton">
|
<widget class="QPushButton" name="BasicModeButton">
|
||||||
<property name="geometry">
|
<property name="geometry">
|
||||||
|
|||||||
@@ -397,6 +397,12 @@
|
|||||||
<property name="selectionMode">
|
<property name="selectionMode">
|
||||||
<enum>QAbstractItemView::NoSelection</enum>
|
<enum>QAbstractItemView::NoSelection</enum>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="verticalScrollMode">
|
||||||
|
<enum>QAbstractItemView::ScrollPerPixel</enum>
|
||||||
|
</property>
|
||||||
|
<property name="horizontalScrollMode">
|
||||||
|
<enum>QAbstractItemView::ScrollPerPixel</enum>
|
||||||
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QPushButton" name="BasicModeButton">
|
<widget class="QPushButton" name="BasicModeButton">
|
||||||
<property name="geometry">
|
<property name="geometry">
|
||||||
|
|||||||
@@ -126,6 +126,9 @@
|
|||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QFormLayout" name="formLayout">
|
<layout class="QFormLayout" name="formLayout">
|
||||||
|
<property name="fieldGrowthPolicy">
|
||||||
|
<enum>QFormLayout::ExpandingFieldsGrow</enum>
|
||||||
|
</property>
|
||||||
<item row="1" column="0">
|
<item row="1" column="0">
|
||||||
<widget class="QLabel" name="label">
|
<widget class="QLabel" name="label">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
|
|||||||
2
kcc.iss
2
kcc.iss
@@ -1,5 +1,5 @@
|
|||||||
#define MyAppName "Kindle Comic Converter"
|
#define MyAppName "Kindle Comic Converter"
|
||||||
#define MyAppVersion "4.6"
|
#define MyAppVersion "4.6.3"
|
||||||
#define MyAppPublisher "Ciro Mattia Gonano, Paweł Jastrzębski"
|
#define MyAppPublisher "Ciro Mattia Gonano, Paweł Jastrzębski"
|
||||||
#define MyAppURL "http://kcc.iosphe.re/"
|
#define MyAppURL "http://kcc.iosphe.re/"
|
||||||
#define MyAppExeName "KCC.exe"
|
#define MyAppExeName "KCC.exe"
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ class Ui_MetaEditorDialog(object):
|
|||||||
self.formLayoutWidget.setObjectName("formLayoutWidget")
|
self.formLayoutWidget.setObjectName("formLayoutWidget")
|
||||||
self.formLayout = QtWidgets.QFormLayout(self.formLayoutWidget)
|
self.formLayout = QtWidgets.QFormLayout(self.formLayoutWidget)
|
||||||
self.formLayout.setContentsMargins(0, 0, 0, 0)
|
self.formLayout.setContentsMargins(0, 0, 0, 0)
|
||||||
|
self.formLayout.setFieldGrowthPolicy(QtWidgets.QFormLayout.ExpandingFieldsGrow)
|
||||||
self.formLayout.setObjectName("formLayout")
|
self.formLayout.setObjectName("formLayout")
|
||||||
self.label = QtWidgets.QLabel(self.formLayoutWidget)
|
self.label = QtWidgets.QLabel(self.formLayoutWidget)
|
||||||
self.label.setObjectName("label")
|
self.label.setObjectName("label")
|
||||||
|
|||||||
@@ -20,20 +20,22 @@
|
|||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
from urllib.parse import unquote
|
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 socket import gethostbyname_ex, gethostname
|
||||||
from traceback import format_tb
|
from traceback import format_tb
|
||||||
from time import sleep
|
from time import sleep, time
|
||||||
|
from datetime import datetime
|
||||||
from shutil import move
|
from shutil import move
|
||||||
from http.server import BaseHTTPRequestHandler, HTTPServer
|
from http.server import BaseHTTPRequestHandler, HTTPServer
|
||||||
from socketserver import ThreadingMixIn
|
from socketserver import ThreadingMixIn
|
||||||
from subprocess import STDOUT, PIPE
|
from subprocess import STDOUT, PIPE
|
||||||
from PyQt5 import QtGui, QtCore, QtWidgets, QtNetwork
|
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 psutil import Popen, Process
|
||||||
from copy import copy
|
from copy import copy
|
||||||
from distutils.version import StrictVersion
|
from distutils.version import StrictVersion
|
||||||
from xml.sax.saxutils import escape
|
from xml.sax.saxutils import escape
|
||||||
|
from platform import platform
|
||||||
from .shared import md5Checksum, HTMLStripper
|
from .shared import md5Checksum, HTMLStripper
|
||||||
from . import __version__
|
from . import __version__
|
||||||
from . import comic2ebook
|
from . import comic2ebook
|
||||||
@@ -245,22 +247,22 @@ class VersionThread(QtCore.QThread):
|
|||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
try:
|
try:
|
||||||
XML = urlopen('http://kcc.iosphe.re/Version.php')
|
XML = parse(urlopen(Request('https://kcc.iosphe.re/Version/',
|
||||||
XML = parse(XML)
|
headers={'User-Agent': 'KindleComicConverter/' + __version__})))
|
||||||
except Exception:
|
except Exception:
|
||||||
return
|
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 StrictVersion(latestVersion) > StrictVersion(__version__):
|
||||||
if sys.platform.startswith('win'):
|
if sys.platform.startswith('win'):
|
||||||
self.newVersion = latestVersion
|
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/">'
|
MW.showDialog.emit('<b>New version released!</b> <a href="https://github.com/ciromattia/kcc/releases/">'
|
||||||
'See changelog.</a><br/><br/>Installed version: ' + __version__ +
|
'See changelog.</a><br/><br/>Installed version: ' + __version__ +
|
||||||
'<br/>Current version: ' + latestVersion +
|
'<br/>Current version: ' + latestVersion +
|
||||||
'<br/><br/>Would you like to start automatic update?', 'question')
|
'<br/><br/>Would you like to start automatic update?', 'question')
|
||||||
self.getNewVersion()
|
self.getNewVersion()
|
||||||
else:
|
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> '
|
'<b>New version is available!</b></a> '
|
||||||
'(<a href="https://github.com/ciromattia/kcc/releases/">'
|
'(<a href="https://github.com/ciromattia/kcc/releases/">'
|
||||||
'Changelog</a>)', 'warning', False)
|
'Changelog</a>)', 'warning', False)
|
||||||
@@ -275,7 +277,7 @@ class VersionThread(QtCore.QThread):
|
|||||||
try:
|
try:
|
||||||
MW.modeConvert.emit(-1)
|
MW.modeConvert.emit(-1)
|
||||||
MW.progressBarTick.emit('Downloading update')
|
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)
|
+ self.newVersion + '.exe', reporthook=self.getNewVersionTick)
|
||||||
if self.md5 != md5Checksum(path[0]):
|
if self.md5 != md5Checksum(path[0]):
|
||||||
raise Exception
|
raise Exception
|
||||||
@@ -350,7 +352,7 @@ class WorkerThread(QtCore.QThread):
|
|||||||
|
|
||||||
def sanitizeTrace(self, traceback):
|
def sanitizeTrace(self, traceback):
|
||||||
return ''.join(format_tb(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\\', '')\
|
||||||
.replace('C:\\Python34_64\\', '')
|
.replace('C:\\Python34_64\\', '')
|
||||||
|
|
||||||
@@ -928,6 +930,31 @@ class KCCGUI(KCC_ui.Ui_KCC):
|
|||||||
def showDialog(self, message, kind):
|
def showDialog(self, message, kind):
|
||||||
if kind == 'error':
|
if kind == 'error':
|
||||||
QtWidgets.QMessageBox.critical(MW, 'KCC - Error', message, QtWidgets.QMessageBox.Ok)
|
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':
|
elif kind == 'question':
|
||||||
GUI.versionCheck.setAnswer(QtWidgets.QMessageBox.question(MW, 'KCC - Question', message,
|
GUI.versionCheck.setAnswer(QtWidgets.QMessageBox.question(MW, 'KCC - Question', message,
|
||||||
QtWidgets.QMessageBox.Yes,
|
QtWidgets.QMessageBox.Yes,
|
||||||
@@ -1205,7 +1232,7 @@ class KCCGUI(KCC_ui.Ui_KCC):
|
|||||||
"Kindle DX/DXG",
|
"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'
|
'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'
|
'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>')
|
'ef="http://www.mobileread.com/forums/showthread.php?t=207461">FORUM</a></b>')
|
||||||
@@ -1307,6 +1334,12 @@ class KCCGUI(KCC_ui.Ui_KCC):
|
|||||||
self.versionCheck.start()
|
self.versionCheck.start()
|
||||||
self.contentServer.start()
|
self.contentServer.start()
|
||||||
self.tray.show()
|
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.setWindowTitle("Kindle Comic Converter " + __version__)
|
||||||
MW.show()
|
MW.show()
|
||||||
MW.raise_()
|
MW.raise_()
|
||||||
|
|||||||
@@ -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.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.setProperty("showDropIndicator", False)
|
||||||
self.JobList.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection)
|
self.JobList.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection)
|
||||||
|
self.JobList.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel)
|
||||||
|
self.JobList.setHorizontalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel)
|
||||||
self.JobList.setObjectName("JobList")
|
self.JobList.setObjectName("JobList")
|
||||||
self.BasicModeButton = QtWidgets.QPushButton(self.Form)
|
self.BasicModeButton = QtWidgets.QPushButton(self.Form)
|
||||||
self.BasicModeButton.setGeometry(QtCore.QRect(10, 10, 141, 32))
|
self.BasicModeButton.setGeometry(QtCore.QRect(10, 10, 141, 32))
|
||||||
|
|||||||
@@ -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.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.setProperty("showDropIndicator", False)
|
||||||
self.JobList.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection)
|
self.JobList.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection)
|
||||||
|
self.JobList.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel)
|
||||||
|
self.JobList.setHorizontalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel)
|
||||||
self.JobList.setObjectName("JobList")
|
self.JobList.setObjectName("JobList")
|
||||||
self.BasicModeButton = QtWidgets.QPushButton(self.Form)
|
self.BasicModeButton = QtWidgets.QPushButton(self.Form)
|
||||||
self.BasicModeButton.setGeometry(QtCore.QRect(5, 10, 156, 41))
|
self.BasicModeButton.setGeometry(QtCore.QRect(5, 10, 156, 41))
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
__version__ = '4.6'
|
__version__ = '4.6.3'
|
||||||
__license__ = 'ISC'
|
__license__ = 'ISC'
|
||||||
__copyright__ = '2012-2015, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>'
|
__copyright__ = '2012-2015, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ from PIL import Image
|
|||||||
from subprocess import STDOUT, PIPE
|
from subprocess import STDOUT, PIPE
|
||||||
from psutil import Popen, virtual_memory
|
from psutil import Popen, virtual_memory
|
||||||
from scandir import walk
|
from scandir import walk
|
||||||
|
from html import escape
|
||||||
try:
|
try:
|
||||||
from PyQt5 import QtCore
|
from PyQt5 import QtCore
|
||||||
except ImportError:
|
except ImportError:
|
||||||
@@ -260,7 +261,16 @@ def buildNAV(dstdir, title, chapters, chapterNames):
|
|||||||
"</head>\n",
|
"</head>\n",
|
||||||
"<body>\n",
|
"<body>\n",
|
||||||
"<nav xmlns:epub=\"http://www.idpf.org/2007/ops\" epub:type=\"toc\" id=\"toc\">\n",
|
"<nav xmlns:epub=\"http://www.idpf.org/2007/ops\" epub:type=\"toc\" id=\"toc\">\n",
|
||||||
"<ol></ol>\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>\n",
|
||||||
"<nav epub:type=\"page-list\">\n",
|
"<nav epub:type=\"page-list\">\n",
|
||||||
"<ol>\n"
|
"<ol>\n"
|
||||||
@@ -294,7 +304,8 @@ def buildOPF(dstdir, title, filelist, cover=None):
|
|||||||
"<dc:title>", title, "</dc:title>\n",
|
"<dc:title>", title, "</dc:title>\n",
|
||||||
"<dc:language>en-US</dc:language>\n",
|
"<dc:language>en-US</dc:language>\n",
|
||||||
"<dc:identifier id=\"BookID\">urn: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:contributor id=\"contributor\">KindleComicConverter-" + __version__ + "</dc:contributor>\n",
|
||||||
|
"<dc:description>", options.summary, "</dc:description>\n"])
|
||||||
for author in options.authors:
|
for author in options.authors:
|
||||||
f.writelines(["<dc:creator>", author, "</dc:creator>\n"])
|
f.writelines(["<dc:creator>", author, "</dc:creator>\n"])
|
||||||
f.writelines(["<meta property=\"dcterms:modified\">" + strftime("%Y-%m-%dT%H:%M:%SZ", gmtime()) + "</meta>\n",
|
f.writelines(["<meta property=\"dcterms:modified\">" + strftime("%Y-%m-%dT%H:%M:%SZ", gmtime()) + "</meta>\n",
|
||||||
@@ -303,18 +314,15 @@ def buildOPF(dstdir, title, filelist, cover=None):
|
|||||||
"<meta property=\"rendition:spread\">portrait</meta>\n",
|
"<meta property=\"rendition:spread\">portrait</meta>\n",
|
||||||
"<meta property=\"rendition:layout\">pre-paginated</meta>\n"])
|
"<meta property=\"rendition:layout\">pre-paginated</meta>\n"])
|
||||||
if options.iskindle and options.profile != 'Custom':
|
if options.iskindle and options.profile != 'Custom':
|
||||||
f.writelines(["<meta property=\"RegionMagnification\">true</meta>\n",
|
f.writelines(["<meta name=\"original-resolution\" content=\"",
|
||||||
"<meta property=\"region-mag\">true</meta>\n",
|
str(deviceres[0]) + "x" + str(deviceres[1]) + "\"/>\n",
|
||||||
"<meta property=\"book-type\">comic</meta>\n",
|
"<meta name=\"book-type\" content=\"comic\"/>\n",
|
||||||
"<meta property=\"zero-gutter\">true</meta>\n",
|
"<meta name=\"RegionMagnification\" content=\"true\"/>\n",
|
||||||
"<meta property=\"zero-margin\">true</meta>\n",
|
"<meta name=\"primary-writing-mode\" content=\"" + writingmode + "\"/>\n",
|
||||||
"<meta property=\"fixed-layout\">true</meta>\n",
|
"<meta name=\"zero-gutter\" content=\"true\"/>\n",
|
||||||
"<meta property=\"orientation-lock\">portrait</meta>\n",
|
"<meta name=\"zero-margin\" content=\"true\"/>\n",
|
||||||
"<meta property=\"original-resolution\">",
|
"<meta name=\"ke-border-color\" content=\"#ffffff\"/>\n",
|
||||||
str(deviceres[0]) + "x" + str(deviceres[1]) + "</meta>\n",
|
"<meta name=\"ke-border-width\" content=\"0\"/>\n"])
|
||||||
"<meta property=\"primary-writing-mode\">" + writingmode + "</meta>\n",
|
|
||||||
"<meta property=\"ke-border-color\">#ffffff</meta>\n",
|
|
||||||
"<meta property=\"ke-border-width\">0</meta>\n"])
|
|
||||||
f.writelines(["</metadata>\n<manifest>\n<item id=\"ncx\" href=\"toc.ncx\" ",
|
f.writelines(["</metadata>\n<manifest>\n<item id=\"ncx\" href=\"toc.ncx\" ",
|
||||||
"media-type=\"application/x-dtbncx+xml\"/>\n",
|
"media-type=\"application/x-dtbncx+xml\"/>\n",
|
||||||
"<item id=\"nav\" href=\"nav.xhtml\" ",
|
"<item id=\"nav\" href=\"nav.xhtml\" ",
|
||||||
@@ -682,8 +690,8 @@ def getWorkFolder(afile):
|
|||||||
def getOutputFilename(srcpath, wantedname, ext, tomeNumber):
|
def getOutputFilename(srcpath, wantedname, ext, tomeNumber):
|
||||||
if srcpath[-1] == os.path.sep:
|
if srcpath[-1] == os.path.sep:
|
||||||
srcpath = srcpath[:-1]
|
srcpath = srcpath[:-1]
|
||||||
if not ext.startswith('.'):
|
if 'Ko' in options.profile and options.format == 'EPUB':
|
||||||
ext = '.' + ext
|
ext = '.kepub.epub'
|
||||||
if wantedname is not None:
|
if wantedname is not None:
|
||||||
if wantedname.endswith(ext):
|
if wantedname.endswith(ext):
|
||||||
filename = os.path.abspath(wantedname)
|
filename = os.path.abspath(wantedname)
|
||||||
@@ -695,7 +703,14 @@ def getOutputFilename(srcpath, wantedname, ext, tomeNumber):
|
|||||||
elif os.path.isdir(srcpath):
|
elif os.path.isdir(srcpath):
|
||||||
filename = srcpath + tomeNumber + ext
|
filename = srcpath + tomeNumber + ext
|
||||||
else:
|
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):
|
if os.path.isfile(filename):
|
||||||
counter = 0
|
counter = 0
|
||||||
basename = os.path.splitext(filename)[0]
|
basename = os.path.splitext(filename)[0]
|
||||||
@@ -710,6 +725,7 @@ def getComicInfo(path, originalPath):
|
|||||||
options.authors = ['KCC']
|
options.authors = ['KCC']
|
||||||
options.remoteCovers = {}
|
options.remoteCovers = {}
|
||||||
options.chapters = []
|
options.chapters = []
|
||||||
|
options.summary = ''
|
||||||
titleSuffix = ''
|
titleSuffix = ''
|
||||||
if options.title == 'defaulttitle':
|
if options.title == 'defaulttitle':
|
||||||
defaultTitle = True
|
defaultTitle = True
|
||||||
@@ -728,7 +744,7 @@ def getComicInfo(path, originalPath):
|
|||||||
options.authors = []
|
options.authors = []
|
||||||
if defaultTitle:
|
if defaultTitle:
|
||||||
if xml.data['Series']:
|
if xml.data['Series']:
|
||||||
options.title = xml.data['Series']
|
options.title = escape(xml.data['Series'])
|
||||||
if xml.data['Volume']:
|
if xml.data['Volume']:
|
||||||
titleSuffix += ' V' + xml.data['Volume']
|
titleSuffix += ' V' + xml.data['Volume']
|
||||||
if xml.data['Number']:
|
if xml.data['Number']:
|
||||||
@@ -736,7 +752,7 @@ def getComicInfo(path, originalPath):
|
|||||||
options.title += titleSuffix
|
options.title += titleSuffix
|
||||||
for field in ['Writers', 'Pencillers', 'Inkers', 'Colorists']:
|
for field in ['Writers', 'Pencillers', 'Inkers', 'Colorists']:
|
||||||
for person in xml.data[field]:
|
for person in xml.data[field]:
|
||||||
options.authors.append(person)
|
options.authors.append(escape(person))
|
||||||
if len(options.authors) > 0:
|
if len(options.authors) > 0:
|
||||||
options.authors = list(set(options.authors))
|
options.authors = list(set(options.authors))
|
||||||
options.authors.sort()
|
options.authors.sort()
|
||||||
@@ -746,6 +762,8 @@ def getComicInfo(path, originalPath):
|
|||||||
options.remoteCovers = getCoversFromMCB(xml.data['MUid'])
|
options.remoteCovers = getCoversFromMCB(xml.data['MUid'])
|
||||||
if xml.data['Bookmarks']:
|
if xml.data['Bookmarks']:
|
||||||
options.chapters = xml.data['Bookmarks']
|
options.chapters = xml.data['Bookmarks']
|
||||||
|
if xml.data['Summary']:
|
||||||
|
options.summary = escape(xml.data['Summary'])
|
||||||
os.remove(xmlPath)
|
os.remove(xmlPath)
|
||||||
|
|
||||||
|
|
||||||
@@ -948,6 +966,8 @@ def splitProcess(path, mode):
|
|||||||
|
|
||||||
|
|
||||||
def detectCorruption(tmpPath, orgPath):
|
def detectCorruption(tmpPath, orgPath):
|
||||||
|
imageNumber = 0
|
||||||
|
imageSmaller = 0
|
||||||
for root, dirs, files in walk(tmpPath, False):
|
for root, dirs, files in walk(tmpPath, False):
|
||||||
for name in files:
|
for name in files:
|
||||||
if getImageFileName(name) is not None:
|
if getImageFileName(name) is not None:
|
||||||
@@ -961,14 +981,24 @@ def detectCorruption(tmpPath, orgPath):
|
|||||||
img.verify()
|
img.verify()
|
||||||
img = Image.open(path)
|
img = Image.open(path)
|
||||||
img.load()
|
img.load()
|
||||||
|
imageNumber += 1
|
||||||
|
if options.profileData[1][0] > img.size[0] and options.profileData[1][1] > img.size[1]:
|
||||||
|
imageSmaller += 1
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
rmtree(os.path.join(tmpPath, '..', '..'), True)
|
rmtree(os.path.join(tmpPath, '..', '..'), True)
|
||||||
if 'decoder' in err and 'not available' in err:
|
if 'decoder' in str(err) and 'not available' in str(err):
|
||||||
raise RuntimeError('Pillow was compiled without JPG and/or PNG decoder.')
|
raise RuntimeError('Pillow was compiled without JPG and/or PNG decoder.')
|
||||||
else:
|
else:
|
||||||
raise RuntimeError('Image file %s is corrupted.' % pathOrg)
|
raise RuntimeError('Image file %s is corrupted.' % pathOrg)
|
||||||
else:
|
else:
|
||||||
os.remove(os.path.join(root, name))
|
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):
|
def detectMargins(path):
|
||||||
@@ -1239,11 +1269,6 @@ def makeBook(source, qtGUI=None):
|
|||||||
filepath.append(getOutputFilename(source, options.output, '.epub', ''))
|
filepath.append(getOutputFilename(source, options.output, '.epub', ''))
|
||||||
makeZIP(tome + '_comic', tome, True)
|
makeZIP(tome + '_comic', tome, True)
|
||||||
move(tome + '_comic.zip', filepath[-1])
|
move(tome + '_comic.zip', filepath[-1])
|
||||||
if 'Ko' in options.profile:
|
|
||||||
filename = filepath[-1].split(os.path.sep)
|
|
||||||
filename[-1] = ''.join(e for e in filename[-1].split('.')[0] if e.isalnum()) + '.kepub.epub'
|
|
||||||
filename = os.path.sep.join(filename)
|
|
||||||
move(filepath[-1], filename)
|
|
||||||
rmtree(tome, True)
|
rmtree(tome, True)
|
||||||
if GUI:
|
if GUI:
|
||||||
GUI.progressBarTick.emit('tick')
|
GUI.progressBarTick.emit('tick')
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ class MetadataParser:
|
|||||||
'Pencillers': [],
|
'Pencillers': [],
|
||||||
'Inkers': [],
|
'Inkers': [],
|
||||||
'Colorists': [],
|
'Colorists': [],
|
||||||
|
'Summary': '',
|
||||||
'MUid': '',
|
'MUid': '',
|
||||||
'Bookmarks': []}
|
'Bookmarks': []}
|
||||||
self.rawdata = None
|
self.rawdata = None
|
||||||
@@ -90,6 +91,8 @@ class MetadataParser:
|
|||||||
self.data['Volume'] = self.rawdata.getElementsByTagName('Volume')[0].firstChild.nodeValue
|
self.data['Volume'] = self.rawdata.getElementsByTagName('Volume')[0].firstChild.nodeValue
|
||||||
if len(self.rawdata.getElementsByTagName('Number')) != 0:
|
if len(self.rawdata.getElementsByTagName('Number')) != 0:
|
||||||
self.data['Number'] = self.rawdata.getElementsByTagName('Number')[0].firstChild.nodeValue
|
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']:
|
for field in ['Writer', 'Penciller', 'Inker', 'Colorist']:
|
||||||
if len(self.rawdata.getElementsByTagName(field)) != 0:
|
if len(self.rawdata.getElementsByTagName(field)) != 0:
|
||||||
for person in self.rawdata.getElementsByTagName(field)[0].firstChild.nodeValue.split(', '):
|
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']],
|
for row in (['Series', self.data['Series']], ['Volume', self.data['Volume']],
|
||||||
['Number', self.data['Number']], ['Writer', ', '.join(self.data['Writers'])],
|
['Number', self.data['Number']], ['Writer', ', '.join(self.data['Writers'])],
|
||||||
['Penciller', ', '.join(self.data['Pencillers'])], ['Inker', ', '.join(self.data['Inkers'])],
|
['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 '']):
|
['ScanInformation', 'MCD(' + self.data['MUid'] + ')' if self.data['MUid'] else '']):
|
||||||
if self.rawdata.getElementsByTagName(row[0]):
|
if self.rawdata.getElementsByTagName(row[0]):
|
||||||
node = self.rawdata.getElementsByTagName(row[0])[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']],
|
for row in (['Series', self.data['Series']], ['Volume', self.data['Volume']],
|
||||||
['Number', self.data['Number']], ['Writer', ', '.join(self.data['Writers'])],
|
['Number', self.data['Number']], ['Writer', ', '.join(self.data['Writers'])],
|
||||||
['Penciller', ', '.join(self.data['Pencillers'])], ['Inker', ', '.join(self.data['Inkers'])],
|
['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 '']):
|
['ScanInformation', 'MCD(' + self.data['MUid'] + ')' if self.data['MUid'] else '']):
|
||||||
if row[1]:
|
if row[1]:
|
||||||
main = doc.createElement(row[0])
|
main = doc.createElement(row[0])
|
||||||
|
|||||||
2
setup.py
2
setup.py
@@ -95,7 +95,7 @@ elif platform == 'win32':
|
|||||||
zipfile=None,
|
zipfile=None,
|
||||||
data_files=additional_files)
|
data_files=additional_files)
|
||||||
else:
|
else:
|
||||||
if argv[1] == 'make_pyz':
|
if len(argv) > 1 and argv[1] == 'make_pyz':
|
||||||
from os import system
|
from os import system
|
||||||
script = '''
|
script = '''
|
||||||
cp kcc.py __main__.py
|
cp kcc.py __main__.py
|
||||||
|
|||||||
Reference in New Issue
Block a user