mirror of
https://github.com/ciromattia/kcc
synced 2026-04-15 13:38:46 +00:00
Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9d267a6cc4 | ||
|
|
462f24149b | ||
|
|
4744b62f91 | ||
|
|
08244e7fdc | ||
|
|
f64fb1bee1 | ||
|
|
743c3b2466 | ||
|
|
aefa36fef8 | ||
|
|
d7f6503196 | ||
|
|
3375b8c898 | ||
|
|
52e5919ccd | ||
|
|
1c2e57adb6 | ||
|
|
2e99d6ee0a | ||
|
|
6744815f77 | ||
|
|
0ba44ab2d3 | ||
|
|
a6006450de | ||
|
|
c20e2ba451 | ||
|
|
7a0b387c1c | ||
|
|
fdfe5fbe39 | ||
|
|
35751efad5 | ||
|
|
7005c9e40a | ||
|
|
118cf25ff7 |
3
KCC.ui
3
KCC.ui
@@ -59,6 +59,9 @@
|
||||
</font>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<property name="leftMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="ProcessingBox">
|
||||
<property name="focusPolicy">
|
||||
|
||||
11
README.md
11
README.md
@@ -24,9 +24,6 @@ You can find the latest released binary at the following links:
|
||||
- **OS X:** [http://kcc.vulturis.eu/OSX/](http://kcc.vulturis.eu/OSX/)
|
||||
- **Linux:** Just download sourcecode and launch: `python kcc.py`
|
||||
|
||||
_It has been reported by a couple of users that version 2.10 crashing on OSX at start. We don't know if that issue still exist in version 3.0.
|
||||
If it happens to you please append your message to [Issue #52](https://github.com/ciromattia/kcc/issues/52)._
|
||||
|
||||
## INPUT FORMATS
|
||||
**KCC** can understand and convert, at the moment, the following file types:
|
||||
- PNG, JPG, GIF, TIFF, BMP
|
||||
@@ -70,7 +67,7 @@ Options:
|
||||
--version show program's version number and exit
|
||||
-h, --help show this help message and exit
|
||||
-p PROFILE, --profile=PROFILE
|
||||
Device profile (Choose one among K1, K2, K3, K4NT, K4T, KDX, KDXG, KHD, KF, KFHD, KFHD8) [Default=KHD]
|
||||
Device profile (Choose one among K1, K2, K3, K4NT, K4T, KDX, KDXG, KHD, KF, KFHD, KFHD8, KFA) [Default=KHD]
|
||||
-t TITLE, --title=TITLE
|
||||
Comic title [Default=filename]
|
||||
-m, --manga-style Manga style (Right-to-left reading and splitting) [Default=False]
|
||||
@@ -213,8 +210,12 @@ The app relies and includes the following scripts/binaries:
|
||||
* Added support for custom width/height
|
||||
* Added option to disable color conversion
|
||||
|
||||
####3.1:
|
||||
* Added profile: Kindle for Android
|
||||
* Add file/directory dialogs now support multiselect
|
||||
* Many small fixes and tweaks
|
||||
|
||||
## KNOWN ISSUES
|
||||
* _Add directory_ dialog allow to select multiple directories but they are not added to job list. [QT bug.](https://bugreports.qt-project.org/browse/QTBUG-21372)
|
||||
* Removing SRCS headers sometimes fail in 32bit enviroments. Due to memory limitations.
|
||||
|
||||
## COPYRIGHT
|
||||
|
||||
2
kcc.py
2
kcc.py
@@ -17,7 +17,7 @@
|
||||
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
# PERFORMANCE OF THIS SOFTWARE.
|
||||
#
|
||||
__version__ = '3.0'
|
||||
__version__ = '3.1'
|
||||
__license__ = 'ISC'
|
||||
__copyright__ = '2012-2013, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@vulturis.eu>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
338
kcc/KCC_gui.py
338
kcc/KCC_gui.py
@@ -17,7 +17,7 @@
|
||||
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
# PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
__version__ = '3.0'
|
||||
__version__ = '3.1'
|
||||
__license__ = 'ISC'
|
||||
__copyright__ = '2012-2013, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@vulturis.eu>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
@@ -27,12 +27,14 @@ import sys
|
||||
import shutil
|
||||
import traceback
|
||||
import urllib2
|
||||
import time
|
||||
import comic2ebook
|
||||
import kindlestrip
|
||||
from image import ProfileData
|
||||
from subprocess import call, Popen, STDOUT, PIPE
|
||||
from PyQt4 import QtGui, QtCore
|
||||
from xml.dom.minidom import parse
|
||||
from HTMLParser import HTMLParser
|
||||
|
||||
|
||||
class Icons:
|
||||
@@ -57,6 +59,19 @@ class Icons:
|
||||
self.error.addPixmap(QtGui.QPixmap(":/Status/icons/error.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||
|
||||
|
||||
class HTMLStripper(HTMLParser):
|
||||
def __init__(self):
|
||||
HTMLParser.__init__(self)
|
||||
self.reset()
|
||||
self.fed = []
|
||||
|
||||
def handle_data(self, d):
|
||||
self.fed.append(d)
|
||||
|
||||
def get_data(self):
|
||||
return ''.join(self.fed)
|
||||
|
||||
|
||||
# noinspection PyBroadException
|
||||
class VersionThread(QtCore.QThread):
|
||||
def __init__(self, parent):
|
||||
@@ -74,7 +89,8 @@ class VersionThread(QtCore.QThread):
|
||||
return
|
||||
latestVersion = XML.childNodes[0].getElementsByTagName('latest')[0].childNodes[0].toxml()
|
||||
if tuple(map(int, (latestVersion.split(".")))) > tuple(map(int, (__version__.split(".")))):
|
||||
self.emit(QtCore.SIGNAL("addMessage"), 'New version is available!', 'warning')
|
||||
self.emit(QtCore.SIGNAL("addMessage"), '<a href="http://kcc.vulturis.eu/">'
|
||||
'<b>New version is available!</b></a>', 'warning')
|
||||
|
||||
|
||||
# noinspection PyBroadException
|
||||
@@ -86,6 +102,15 @@ class WorkerThread(QtCore.QThread):
|
||||
def __del__(self):
|
||||
self.wait()
|
||||
|
||||
def sync(self):
|
||||
self.conversionAlive = self.parent.conversionAlive
|
||||
|
||||
def clean(self):
|
||||
self.parent.needClean = True
|
||||
self.emit(QtCore.SIGNAL("hideProgressBar"))
|
||||
self.emit(QtCore.SIGNAL("addMessage"), '<b>Conversion interrupted.</b>', 'error')
|
||||
self.emit(QtCore.SIGNAL("modeConvert"), True)
|
||||
|
||||
def run(self):
|
||||
self.emit(QtCore.SIGNAL("modeConvert"), False)
|
||||
profile = ProfileData.ProfileLabels[str(GUI.DeviceBox.currentText())]
|
||||
@@ -102,7 +127,7 @@ class WorkerThread(QtCore.QThread):
|
||||
if self.parent.currentMode > 1:
|
||||
if GUI.ProcessingBox.isChecked():
|
||||
argv.append("--noprocessing")
|
||||
if GUI.UpscaleBox.isChecked() and not GUI.StretchBox.isChecked():
|
||||
if GUI.UpscaleBox.isChecked():
|
||||
argv.append("--upscale")
|
||||
if GUI.NoRotateBox.isChecked():
|
||||
argv.append("--nosplitrotate")
|
||||
@@ -125,8 +150,12 @@ class WorkerThread(QtCore.QThread):
|
||||
currentJobs.append(str(GUI.JobList.item(i).text()))
|
||||
GUI.JobList.clear()
|
||||
for job in currentJobs:
|
||||
time.sleep(0.5)
|
||||
if not self.conversionAlive:
|
||||
self.clean()
|
||||
return
|
||||
self.errors = False
|
||||
self.emit(QtCore.SIGNAL("addMessage"), 'Source: ' + job, 'info')
|
||||
self.emit(QtCore.SIGNAL("addMessage"), '<b>Source:</b> ' + job, 'info')
|
||||
if str(GUI.FormatBox.currentText()) == 'CBZ':
|
||||
self.emit(QtCore.SIGNAL("addMessage"), 'Creating CBZ file...', 'info')
|
||||
else:
|
||||
@@ -136,101 +165,150 @@ class WorkerThread(QtCore.QThread):
|
||||
try:
|
||||
outputPath = comic2ebook.main(jobargv, self)
|
||||
self.emit(QtCore.SIGNAL("hideProgressBar"))
|
||||
except UserWarning as warn:
|
||||
if not self.conversionAlive:
|
||||
self.clean()
|
||||
return
|
||||
else:
|
||||
self.errors = True
|
||||
self.emit(QtCore.SIGNAL("addMessage"), str(warn), 'warning')
|
||||
self.emit(QtCore.SIGNAL("addMessage"), 'KCC failed to create output file!', 'warning')
|
||||
except Exception as err:
|
||||
self.errors = True
|
||||
type_, value_, traceback_ = sys.exc_info()
|
||||
self.emit(QtCore.SIGNAL("showDialog"), "Error during conversion %s:\n\n%s\n\nTraceback:\n%s"
|
||||
% (jobargv[-1], str(err), traceback.format_tb(traceback_)))
|
||||
self.emit(QtCore.SIGNAL("addMessage"), 'KCC failed to create EPUB!', 'error')
|
||||
if not self.conversionAlive:
|
||||
os.remove(outputPath)
|
||||
self.clean()
|
||||
return
|
||||
if not self.errors:
|
||||
if str(GUI.FormatBox.currentText()) == 'CBZ':
|
||||
self.emit(QtCore.SIGNAL("addMessage"), 'Creating CBZ file... Done!', 'info', True)
|
||||
else:
|
||||
self.emit(QtCore.SIGNAL("addMessage"), 'Creating EPUB file... Done!', 'info', True)
|
||||
if str(GUI.FormatBox.currentText()) == 'MOBI':
|
||||
if not os.path.getsize(outputPath) > 314572800:
|
||||
self.emit(QtCore.SIGNAL("addMessage"), 'Creating MOBI file...', 'info')
|
||||
self.emit(QtCore.SIGNAL("progressBarTick"), 1)
|
||||
try:
|
||||
retcode = call('kindlegen -verbose "' + outputPath + '"', shell=True)
|
||||
except:
|
||||
continue
|
||||
if retcode == 0:
|
||||
self.emit(QtCore.SIGNAL("addMessage"), 'Creating MOBI file... Done!', 'info', True)
|
||||
self.emit(QtCore.SIGNAL("addMessage"), 'Removing SRCS header...', 'info')
|
||||
os.remove(outputPath)
|
||||
mobiPath = outputPath.replace('.epub', '.mobi')
|
||||
shutil.move(mobiPath, mobiPath + '_tostrip')
|
||||
try:
|
||||
kindlestrip.main((mobiPath + '_tostrip', mobiPath))
|
||||
except Exception:
|
||||
self.errors = True
|
||||
if not self.errors:
|
||||
os.remove(mobiPath + '_tostrip')
|
||||
self.emit(QtCore.SIGNAL("addMessage"), 'Removing SRCS header... Done!', 'info', True)
|
||||
else:
|
||||
shutil.move(mobiPath + '_tostrip', mobiPath)
|
||||
self.emit(QtCore.SIGNAL("addMessage"),
|
||||
'KindleStrip failed to remove SRCS header!', 'warning')
|
||||
self.emit(QtCore.SIGNAL("addMessage"),
|
||||
'MOBI file will work correctly but it will be highly oversized.', 'warning')
|
||||
self.emit(QtCore.SIGNAL("addMessage"), 'Creating MOBI file...', 'info')
|
||||
self.emit(QtCore.SIGNAL("progressBarTick"), 1)
|
||||
try:
|
||||
self.kindlegenErrorCode = 0
|
||||
if os.path.getsize(outputPath) < 367001600:
|
||||
output = Popen('kindlegen "' + outputPath + '"', stdout=PIPE, stderr=STDOUT, shell=True)
|
||||
for line in output.stdout:
|
||||
# ERROR: Generic error
|
||||
if "Error(" in line:
|
||||
self.kindlegenErrorCode = 1
|
||||
self.kindlegenError = line
|
||||
# ERROR: EPUB too big
|
||||
if ":E23026:" in line:
|
||||
self.kindlegenErrorCode = 23026
|
||||
else:
|
||||
os.remove(outputPath)
|
||||
if os.path.exists(outputPath.replace('.epub', '.mobi')):
|
||||
os.remove(outputPath.replace('.epub', '.mobi'))
|
||||
self.emit(QtCore.SIGNAL("addMessage"), 'KindleGen failed to create MOBI!', 'error')
|
||||
self.emit(QtCore.SIGNAL("addMessage"), 'Try converting a smaller batch.', 'error')
|
||||
else:
|
||||
excess = (os.path.getsize(outputPath) - 314572800)/1024/1024
|
||||
# ERROR: EPUB too big
|
||||
self.kindlegenErrorCode = 23026
|
||||
except:
|
||||
# ERROR: Unknown generic error
|
||||
self.kindlegenErrorCode = 1
|
||||
continue
|
||||
if not self.conversionAlive:
|
||||
os.remove(outputPath)
|
||||
self.emit(QtCore.SIGNAL("addMessage"), 'Created EPUB file is too big for KindleGen!', 'error')
|
||||
self.emit(QtCore.SIGNAL("addMessage"), 'Limit exceeded by ' + str(excess) +
|
||||
' MB. Try converting a smaller batch.', 'error')
|
||||
os.remove(outputPath.replace('.epub', '.mobi'))
|
||||
self.clean()
|
||||
return
|
||||
if self.kindlegenErrorCode == 0:
|
||||
self.emit(QtCore.SIGNAL("addMessage"), 'Creating MOBI file... Done!', 'info', True)
|
||||
self.emit(QtCore.SIGNAL("addMessage"), 'Removing SRCS header...', 'info')
|
||||
os.remove(outputPath)
|
||||
mobiPath = outputPath.replace('.epub', '.mobi')
|
||||
shutil.move(mobiPath, mobiPath + '_tostrip')
|
||||
try:
|
||||
kindlestrip.main((mobiPath + '_tostrip', mobiPath))
|
||||
except Exception:
|
||||
self.errors = True
|
||||
if not self.errors:
|
||||
os.remove(mobiPath + '_tostrip')
|
||||
self.emit(QtCore.SIGNAL("addMessage"), 'Removing SRCS header... Done!', 'info', True)
|
||||
else:
|
||||
shutil.move(mobiPath + '_tostrip', mobiPath)
|
||||
self.emit(QtCore.SIGNAL("addMessage"),
|
||||
'KindleStrip failed to remove SRCS header!', 'warning')
|
||||
self.emit(QtCore.SIGNAL("addMessage"),
|
||||
'MOBI file will work correctly but it will be highly oversized.', 'warning')
|
||||
else:
|
||||
epubSize = (os.path.getsize(outputPath))/1024/1024
|
||||
os.remove(outputPath)
|
||||
if os.path.exists(outputPath.replace('.epub', '.mobi')):
|
||||
os.remove(outputPath.replace('.epub', '.mobi'))
|
||||
self.emit(QtCore.SIGNAL("addMessage"), 'KindleGen failed to create MOBI!', 'error')
|
||||
if self.kindlegenErrorCode == 1 and self.kindlegenError:
|
||||
self.emit(QtCore.SIGNAL("showDialog"), "KindleGen error:\n\n" + self.kindlegenError)
|
||||
if self.kindlegenErrorCode == 23026:
|
||||
self.emit(QtCore.SIGNAL("addMessage"), 'Created EPUB file was too big.',
|
||||
'error')
|
||||
self.emit(QtCore.SIGNAL("addMessage"), 'EPUB file: ' + str(epubSize) + 'MB.'
|
||||
' Supported size: ~300MB.', 'error')
|
||||
self.emit(QtCore.SIGNAL("hideProgressBar"))
|
||||
self.parent.needClean = True
|
||||
self.emit(QtCore.SIGNAL("addMessage"), 'All jobs completed.', 'info')
|
||||
self.emit(QtCore.SIGNAL("addMessage"), '<b>All jobs completed.</b>', 'info')
|
||||
self.emit(QtCore.SIGNAL("modeConvert"), True)
|
||||
|
||||
|
||||
# noinspection PyBroadException
|
||||
class Ui_KCC(object):
|
||||
def selectDir(self):
|
||||
# Dialog allow to select multiple directories but we can't parse that. QT Bug.
|
||||
if self.needClean:
|
||||
self.needClean = False
|
||||
GUI.JobList.clear()
|
||||
dname = QtGui.QFileDialog.getExistingDirectory(MainWindow, 'Select directory', self.lastPath)
|
||||
# Dirty, dirty way but OS native QFileDialogs don't support directory multiselect
|
||||
dirDialog = QtGui.QFileDialog(MainWindow, 'Select directory', self.lastPath)
|
||||
dirDialog.setFileMode(dirDialog.Directory)
|
||||
dirDialog.setOption(dirDialog.ShowDirsOnly, True)
|
||||
dirDialog.setOption(dirDialog.DontUseNativeDialog, True)
|
||||
l = dirDialog.findChild(QtGui.QListView, "listView")
|
||||
t = dirDialog.findChild(QtGui.QTreeView)
|
||||
if l:
|
||||
l.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
|
||||
if t:
|
||||
t.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
|
||||
if dirDialog.exec_() == 1:
|
||||
dnames = dirDialog.selectedFiles()
|
||||
else:
|
||||
dnames = ""
|
||||
# Lame UTF-8 security measure
|
||||
try:
|
||||
str(dname)
|
||||
except Exception:
|
||||
QtGui.QMessageBox.critical(MainWindow, 'KCC Error', "Path cannot contain non-ASCII characters.",
|
||||
QtGui.QMessageBox.Ok)
|
||||
return
|
||||
if str(dname) != "":
|
||||
self.lastPath = os.path.abspath(os.path.join(str(dname), os.pardir))
|
||||
GUI.JobList.addItem(dname)
|
||||
for dname in dnames:
|
||||
try:
|
||||
str(dname)
|
||||
except Exception:
|
||||
QtGui.QMessageBox.critical(MainWindow, 'KCC Error', "Path cannot contain non-ASCII characters.",
|
||||
QtGui.QMessageBox.Ok)
|
||||
return
|
||||
if str(dname) != "":
|
||||
if sys.platform == 'win32':
|
||||
dname = dname.replace('/', '\\')
|
||||
self.lastPath = os.path.abspath(os.path.join(str(dname), os.pardir))
|
||||
GUI.JobList.addItem(dname)
|
||||
|
||||
def selectFile(self):
|
||||
if self.needClean:
|
||||
self.needClean = False
|
||||
GUI.JobList.clear()
|
||||
if self.UnRAR:
|
||||
fname = QtGui.QFileDialog.getOpenFileName(MainWindow, 'Select file', self.lastPath,
|
||||
'*.cbz *.cbr *.zip *.rar *.pdf')
|
||||
fnames = QtGui.QFileDialog.getOpenFileNames(MainWindow, 'Select file', self.lastPath,
|
||||
'*.cbz *.cbr *.zip *.rar *.pdf')
|
||||
else:
|
||||
fname = QtGui.QFileDialog.getOpenFileName(MainWindow, 'Select file', self.lastPath,
|
||||
'*.cbz *.zip *.pdf')
|
||||
fnames = QtGui.QFileDialog.getOpenFileNames(MainWindow, 'Select file', self.lastPath,
|
||||
'*.cbz *.zip *.pdf')
|
||||
# Lame UTF-8 security measure
|
||||
try:
|
||||
str(fname)
|
||||
except Exception:
|
||||
QtGui.QMessageBox.critical(MainWindow, 'KCC Error', "Path cannot contain non-ASCII characters.",
|
||||
QtGui.QMessageBox.Ok)
|
||||
return
|
||||
if str(fname) != "":
|
||||
self.lastPath = os.path.abspath(os.path.join(str(fname), os.pardir))
|
||||
GUI.JobList.addItem(fname)
|
||||
for fname in fnames:
|
||||
try:
|
||||
str(fname)
|
||||
except Exception:
|
||||
QtGui.QMessageBox.critical(MainWindow, 'KCC Error', "Path cannot contain non-ASCII characters.",
|
||||
QtGui.QMessageBox.Ok)
|
||||
return
|
||||
if str(fname) != "":
|
||||
self.lastPath = os.path.abspath(os.path.join(str(fname), os.pardir))
|
||||
GUI.JobList.addItem(fname)
|
||||
|
||||
def clearJobs(self):
|
||||
GUI.JobList.clear()
|
||||
@@ -268,15 +346,21 @@ class Ui_KCC(object):
|
||||
GUI.OptionsExpert.setEnabled(False)
|
||||
GUI.MangaBox.setEnabled(True)
|
||||
|
||||
def modeExpert(self):
|
||||
def modeExpert(self, KFA=False):
|
||||
self.modeAdvanced()
|
||||
self.currentMode = 3
|
||||
MainWindow.setMinimumSize(QtCore.QSize(420, 380))
|
||||
MainWindow.setMaximumSize(QtCore.QSize(420, 380))
|
||||
MainWindow.resize(420, 380)
|
||||
GUI.OptionsExpert.setEnabled(True)
|
||||
GUI.MangaBox.setCheckState(0)
|
||||
GUI.MangaBox.setEnabled(False)
|
||||
if KFA:
|
||||
GUI.ColorBox.setCheckState(2)
|
||||
GUI.FormatBox.setCurrentIndex(0)
|
||||
GUI.FormatBox.setEnabled(False)
|
||||
else:
|
||||
GUI.FormatBox.setEnabled(True)
|
||||
GUI.MangaBox.setCheckState(0)
|
||||
GUI.MangaBox.setEnabled(False)
|
||||
|
||||
def modeConvert(self, enable):
|
||||
if self.currentMode != 3:
|
||||
@@ -286,19 +370,33 @@ class Ui_KCC(object):
|
||||
GUI.ClearButton.setEnabled(enable)
|
||||
GUI.FileButton.setEnabled(enable)
|
||||
GUI.DeviceBox.setEnabled(enable)
|
||||
GUI.ConvertButton.setEnabled(enable)
|
||||
GUI.FormatBox.setEnabled(enable)
|
||||
GUI.OptionsBasic.setEnabled(enable)
|
||||
GUI.OptionsAdvanced.setEnabled(enable)
|
||||
GUI.OptionsAdvancedGamma.setEnabled(enable)
|
||||
GUI.OptionsExpert.setEnabled(enable)
|
||||
if enable:
|
||||
self.conversionAlive = False
|
||||
self.worker.sync()
|
||||
icon = QtGui.QIcon()
|
||||
icon.addPixmap(QtGui.QPixmap(":/Other/icons/convert.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||
GUI.ConvertButton.setIcon(icon)
|
||||
GUI.ConvertButton.setText('Convert')
|
||||
GUI.ConvertButton.setEnabled(True)
|
||||
if self.currentMode == 1:
|
||||
self.modeBasic()
|
||||
elif self.currentMode == 2:
|
||||
self.modeAdvanced()
|
||||
elif self.currentMode == 3:
|
||||
self.modeExpert()
|
||||
else:
|
||||
self.conversionAlive = True
|
||||
self.worker.sync()
|
||||
icon = QtGui.QIcon()
|
||||
icon.addPixmap(QtGui.QPixmap(":/Other/icons/clear.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||
GUI.ConvertButton.setIcon(icon)
|
||||
GUI.ConvertButton.setText('Abort')
|
||||
GUI.ConvertButton.setEnabled(True)
|
||||
|
||||
def changeGamma(self, value):
|
||||
value = float(value)
|
||||
@@ -309,30 +407,64 @@ class Ui_KCC(object):
|
||||
GUI.GammaLabel.setText('Gamma: ' + str(value))
|
||||
self.GammaValue = value
|
||||
|
||||
def changeDevice(self, value, start=False):
|
||||
if value == 11 and (start or self.currentMode != 3):
|
||||
def toggleNoSplitRotate(self, value):
|
||||
if value:
|
||||
GUI.RotateBox.setEnabled(False)
|
||||
GUI.RotateBox.setChecked(False)
|
||||
else:
|
||||
GUI.RotateBox.setEnabled(True)
|
||||
|
||||
def toggleUpscale(self, value):
|
||||
if value:
|
||||
GUI.StretchBox.setChecked(False)
|
||||
|
||||
def toggleStretch(self, value):
|
||||
if value:
|
||||
GUI.UpscaleBox.setChecked(False)
|
||||
|
||||
def changeDevice(self, value):
|
||||
if value == 12:
|
||||
GUI.BasicModeButton.setEnabled(False)
|
||||
GUI.AdvModeButton.setEnabled(False)
|
||||
self.addMessage('<a href="https://github.com/ciromattia/kcc/wiki/NonKindle-devices">'
|
||||
'List of supported Non-Kindle devices</a>', 'info')
|
||||
self.modeExpert()
|
||||
elif value == 11:
|
||||
GUI.BasicModeButton.setEnabled(False)
|
||||
GUI.AdvModeButton.setEnabled(False)
|
||||
self.modeExpert(True)
|
||||
elif self.currentMode == 3:
|
||||
GUI.BasicModeButton.setEnabled(True)
|
||||
GUI.AdvModeButton.setEnabled(True)
|
||||
self.modeBasic()
|
||||
if value in [0, 1, 5, 6, 7, 8, 9, 11]:
|
||||
if value in [0, 1, 5, 6, 7, 8, 9, 12]:
|
||||
GUI.QualityBox.setCheckState(0)
|
||||
GUI.QualityBox.setEnabled(False)
|
||||
else:
|
||||
GUI.QualityBox.setEnabled(True)
|
||||
|
||||
def stripTags(self, html):
|
||||
s = HTMLStripper()
|
||||
s.feed(html)
|
||||
return s.get_data()
|
||||
|
||||
def addMessage(self, message, icon=None, replace=False):
|
||||
if icon:
|
||||
icon = eval('self.icons.' + icon)
|
||||
item = QtGui.QListWidgetItem(icon, message)
|
||||
item = QtGui.QListWidgetItem(icon, ' ' + self.stripTags(message))
|
||||
else:
|
||||
item = QtGui.QListWidgetItem(message)
|
||||
item = QtGui.QListWidgetItem(' ' + self.stripTags(message))
|
||||
if replace:
|
||||
GUI.JobList.takeItem(GUI.JobList.count()-1)
|
||||
label = QtGui.QLabel(message)
|
||||
label.setOpenExternalLinks(True)
|
||||
if sys.platform == 'darwin':
|
||||
font = QtGui.QFont()
|
||||
font.setPointSize(11)
|
||||
label.setFont(font)
|
||||
item.setTextColor(QtGui.QColor("white"))
|
||||
GUI.JobList.addItem(item)
|
||||
GUI.JobList.setItemWidget(item, label)
|
||||
GUI.JobList.scrollToBottom()
|
||||
|
||||
def showDialog(self, message):
|
||||
@@ -350,19 +482,25 @@ class Ui_KCC(object):
|
||||
GUI.ProgressBar.setValue(GUI.ProgressBar.value() + 1)
|
||||
|
||||
def convertStart(self):
|
||||
if self.needClean:
|
||||
self.needClean = False
|
||||
GUI.JobList.clear()
|
||||
if GUI.JobList.count() == 0:
|
||||
self.addMessage('No files selected! Please choose files to convert.', 'error')
|
||||
self.needClean = True
|
||||
return
|
||||
if self.currentMode > 2 and (str(GUI.customWidth.text()) == '' or str(GUI.customHeight.text()) == ''):
|
||||
GUI.JobList.clear()
|
||||
self.addMessage('Target resolution is not set!', 'error')
|
||||
self.needClean = True
|
||||
return
|
||||
self.worker.start()
|
||||
if self.conversionAlive:
|
||||
GUI.ConvertButton.setEnabled(False)
|
||||
self.addMessage('Process will be interrupted. Please wait.', 'warning')
|
||||
self.conversionAlive = False
|
||||
self.worker.sync()
|
||||
else:
|
||||
if self.needClean:
|
||||
self.needClean = False
|
||||
GUI.JobList.clear()
|
||||
if GUI.JobList.count() == 0:
|
||||
self.addMessage('No files selected! Please choose files to convert.', 'error')
|
||||
self.needClean = True
|
||||
return
|
||||
if self.currentMode > 2 and (str(GUI.customWidth.text()) == '' or str(GUI.customHeight.text()) == ''):
|
||||
GUI.JobList.clear()
|
||||
self.addMessage('Target resolution is not set!', 'error')
|
||||
self.needClean = True
|
||||
return
|
||||
self.worker.start()
|
||||
|
||||
def hideProgressBar(self):
|
||||
GUI.ProgressBar.hide()
|
||||
@@ -403,10 +541,11 @@ class Ui_KCC(object):
|
||||
self.options = self.options.toPyObject()
|
||||
self.worker = WorkerThread(self)
|
||||
self.versionCheck = VersionThread(self)
|
||||
self.conversionAlive = False
|
||||
self.needClean = True
|
||||
|
||||
self.addMessage('Welcome!', 'info')
|
||||
self.addMessage('Remember: all options have additional informations in tooltips.', 'info')
|
||||
self.addMessage('<b>Welcome!</b>', 'info')
|
||||
self.addMessage('<b>Remember:</b> All options have additional informations in tooltips.', 'info')
|
||||
if call('kindlegen', stdout=PIPE, stderr=STDOUT, shell=True) == 0:
|
||||
self.KindleGen = True
|
||||
formats = ['MOBI', 'EPUB', 'CBZ']
|
||||
@@ -415,18 +554,23 @@ class Ui_KCC(object):
|
||||
if "Amazon kindlegen" in line:
|
||||
versionCheck = line.split('V')[1].split(' ')[0]
|
||||
if tuple(map(int, (versionCheck.split(".")))) < tuple(map(int, ('2.9'.split(".")))):
|
||||
self.addMessage('Your kindlegen is outdated! Creating MOBI might fail.'
|
||||
' Please update kindlegen from Amazon\'s website.', 'warning')
|
||||
self.addMessage('Your <a href="http://www.amazon.com/gp/feature.html?ie=UTF8&docId='
|
||||
'1000765211">kindlegen</a> is outdated! Creating MOBI might fail.'
|
||||
' Please update <a href="http://www.amazon.com/gp/feature.html?ie=UTF8&docId='
|
||||
'1000765211">kindlegen</a> from Amazon\'s website.', 'warning')
|
||||
break
|
||||
else:
|
||||
self.KindleGen = False
|
||||
formats = ['EPUB', 'CBZ']
|
||||
self.addMessage('Cannot find kindlegen in PATH! MOBI creation will be disabled.', 'warning')
|
||||
if call('unrar', stdout=PIPE, stderr=STDOUT, shell=True) == 0:
|
||||
self.addMessage('Cannot find <a href="http://www.amazon.com/gp/feature.html?ie=UTF8&docId='
|
||||
'1000765211">kindlegen</a> in PATH! MOBI creation will be disabled.', 'warning')
|
||||
rarExitCode = call('unrar', stdout=PIPE, stderr=STDOUT, shell=True)
|
||||
if rarExitCode == 0 or rarExitCode == 7:
|
||||
self.UnRAR = True
|
||||
else:
|
||||
self.UnRAR = False
|
||||
self.addMessage('Cannot find UnRAR! Processing of CBR/RAR files will be disabled.', 'warning')
|
||||
self.addMessage('Cannot find <a href="http://www.rarlab.com/rar_add.htm">UnRAR</a>!'
|
||||
' Processing of CBR/RAR files will be disabled.', 'warning')
|
||||
|
||||
GUI.BasicModeButton.clicked.connect(self.modeBasic)
|
||||
GUI.AdvModeButton.clicked.connect(self.modeAdvanced)
|
||||
@@ -435,6 +579,9 @@ class Ui_KCC(object):
|
||||
GUI.FileButton.clicked.connect(self.selectFile)
|
||||
GUI.ConvertButton.clicked.connect(self.convertStart)
|
||||
GUI.GammaSlider.valueChanged.connect(self.changeGamma)
|
||||
GUI.NoRotateBox.stateChanged.connect(self.toggleNoSplitRotate)
|
||||
GUI.UpscaleBox.stateChanged.connect(self.toggleUpscale)
|
||||
GUI.StretchBox.stateChanged.connect(self.toggleStretch)
|
||||
GUI.DeviceBox.activated.connect(self.changeDevice)
|
||||
KCC.connect(self.worker, QtCore.SIGNAL("progressBarTick"), self.updateProgressbar)
|
||||
KCC.connect(self.worker, QtCore.SIGNAL("modeConvert"), self.modeConvert)
|
||||
@@ -477,4 +624,5 @@ class Ui_KCC(object):
|
||||
self.modeExpert()
|
||||
self.versionCheck.start()
|
||||
self.hideProgressBar()
|
||||
self.changeDevice(self.lastDevice, True)
|
||||
self.changeDevice(self.lastDevice)
|
||||
self.worker.sync()
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
# Form implementation generated from reading ui file 'KCC.ui'
|
||||
#
|
||||
# Created: Fri Jun 21 18:23:19 2013
|
||||
# by: PyQt4 UI code generator 4.10.1
|
||||
# Created: Thu Jul 04 15:30:15 2013
|
||||
# by: PyQt4 UI code generator 4.10.2
|
||||
#
|
||||
# WARNING! All changes made in this file will be lost!
|
||||
|
||||
@@ -47,6 +47,7 @@ class Ui_KCC(object):
|
||||
self.OptionsAdvanced.setFont(font)
|
||||
self.OptionsAdvanced.setObjectName(_fromUtf8("OptionsAdvanced"))
|
||||
self.gridLayout = QtGui.QGridLayout(self.OptionsAdvanced)
|
||||
self.gridLayout.setContentsMargins(9, -1, -1, -1)
|
||||
self.gridLayout.setObjectName(_fromUtf8("gridLayout"))
|
||||
self.ProcessingBox = QtGui.QCheckBox(self.OptionsAdvanced)
|
||||
self.ProcessingBox.setFocusPolicy(QtCore.Qt.NoFocus)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
__version__ = '3.0'
|
||||
__version__ = '3.1'
|
||||
__license__ = 'ISC'
|
||||
__copyright__ = '2012-2013, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@vulturis.eu>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
@@ -17,7 +17,7 @@
|
||||
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
# PERFORMANCE OF THIS SOFTWARE.
|
||||
#
|
||||
__version__ = '3.0'
|
||||
__version__ = '3.1'
|
||||
__license__ = 'ISC'
|
||||
__copyright__ = '2012-2013, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@vulturis.eu>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
@@ -329,13 +329,13 @@ def applyImgOptimization(img, isSplit, toRight, options, overrideQuality=5):
|
||||
img.cropWhiteSpace(10.0)
|
||||
if options.cutpagenumbers:
|
||||
img.cutPageNumber()
|
||||
img.optimizeImage(options.gamma)
|
||||
if overrideQuality != 5:
|
||||
img.resizeImage(options.upscale, options.stretch, options.black_borders, isSplit, toRight,
|
||||
options.landscapemode, overrideQuality)
|
||||
else:
|
||||
img.resizeImage(options.upscale, options.stretch, options.black_borders, isSplit, toRight,
|
||||
options.landscapemode, options.quality)
|
||||
img.optimizeImage(options.gamma)
|
||||
if options.forcepng and not options.forcecolor:
|
||||
img.quantizeImage()
|
||||
|
||||
@@ -364,6 +364,10 @@ def dirImgProcess(path):
|
||||
queue.get(True, 1)
|
||||
except:
|
||||
pass
|
||||
if not GUI.conversionAlive:
|
||||
pool.terminate()
|
||||
rmtree(path)
|
||||
raise UserWarning("Conversion interrupted.")
|
||||
GUI.emit(QtCore.SIGNAL("progressBarTick"))
|
||||
pool.join()
|
||||
queue.close()
|
||||
@@ -379,6 +383,9 @@ def dirImgProcess(path):
|
||||
splitCount += 1
|
||||
pagenumbermodifier += 1
|
||||
pagenumbermodifier += 1
|
||||
else:
|
||||
rmtree(path)
|
||||
raise UserWarning("Source directory is empty.")
|
||||
|
||||
|
||||
def fileImgProcess_init(queue, options):
|
||||
@@ -682,8 +689,8 @@ def main(argv=None, qtGUI=None):
|
||||
usage = "Usage: %prog [options] comic_file|comic_folder"
|
||||
parser = OptionParser(usage=usage, version=__version__)
|
||||
parser.add_option("-p", "--profile", action="store", dest="profile", default="KHD",
|
||||
help="Device profile (Choose one among K1, K2, K3, K4NT, K4T, KDX, KDXG, KHD, KF, KFHD, KFHD8) "
|
||||
"[Default=KHD]")
|
||||
help="Device profile (Choose one among K1, K2, K3, K4NT, K4T, KDX, KDXG, KHD, KF, KFHD, KFHD8,"
|
||||
" KFA) [Default=KHD]")
|
||||
parser.add_option("-t", "--title", action="store", dest="title", default="defaulttitle",
|
||||
help="Comic title [Default=filename]")
|
||||
parser.add_option("-m", "--manga-style", action="store_true", dest="righttoleft", default=False,
|
||||
@@ -816,6 +823,10 @@ def checkOptions():
|
||||
options.landscapemode = False
|
||||
options.panelview = False
|
||||
options.quality = 0
|
||||
# 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)
|
||||
# Override profile data
|
||||
if options.customwidth != 0 or options.customheight != 0:
|
||||
X = image.ProfileData.Profiles[options.profile][1][0]
|
||||
|
||||
@@ -85,6 +85,7 @@ class ProfileData:
|
||||
'KF': ("Kindle Fire", (600, 1024), Palette16, 1.0, (900, 1536)),
|
||||
'KFHD': ("Kindle Fire HD 7\"", (800, 1280), Palette16, 1.0, (1200, 1920)),
|
||||
'KFHD8': ("Kindle Fire HD 8.9\"", (1200, 1920), Palette16, 1.0, (1800, 2880)),
|
||||
'KFA': ("Kindle for Android", (0, 0), Palette16, 1.0, (0, 0)),
|
||||
'OTHER': ("Other", (0, 0), Palette16, 1.8, (0, 0)),
|
||||
}
|
||||
|
||||
@@ -100,6 +101,7 @@ class ProfileData:
|
||||
"Kindle Fire": 'KF',
|
||||
"Kindle Fire HD 7\"": 'KFHD',
|
||||
"Kindle Fire HD 8.9\"": 'KFHD8',
|
||||
"Kindle for Android": 'KFA',
|
||||
"Other": 'OTHER'
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user