mirror of
https://github.com/ciromattia/kcc
synced 2026-04-15 13:38:46 +00:00
Compare commits
36 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5c2c6ed825 | ||
|
|
c2730ab01c | ||
|
|
bfba66d47d | ||
|
|
b5bc2f8e00 | ||
|
|
917eaef548 | ||
|
|
3187ebb054 | ||
|
|
b9276e9ede | ||
|
|
147d815057 | ||
|
|
180123fee2 | ||
|
|
2768e622f2 | ||
|
|
b629b45d46 | ||
|
|
f66c83425c | ||
|
|
68b4b7114d | ||
|
|
36f8c82eaf | ||
|
|
bd665c3261 | ||
|
|
94586fa590 | ||
|
|
89806dfcfc | ||
|
|
c9eb73ab90 | ||
|
|
2edcc0369a | ||
|
|
bc0a52b848 | ||
|
|
5ae72bf06d | ||
|
|
24c32643c1 | ||
|
|
40b988f964 | ||
|
|
ac794eff85 | ||
|
|
eaa387a9d6 | ||
|
|
d0f5d6dac4 | ||
|
|
b174534c1c | ||
|
|
19d486a4ee | ||
|
|
652762b709 | ||
|
|
e6df87f8fd | ||
|
|
1a9cd0beb5 | ||
|
|
108e351126 | ||
|
|
dfe3e10470 | ||
|
|
787ad9fa66 | ||
|
|
f10e8869a9 | ||
|
|
715ada328f |
27
KCC-Linux.ui
27
KCC-Linux.ui
@@ -71,7 +71,7 @@
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p style='white-space:pre'>Disable image optimizations.<br/>Input images must be already resized.</p></body></html></string>
|
||||
<string><html><head/><body><p style='white-space:pre'>Disable image optimizations.<br/><span style=" font-weight:600;">Input images must be already resized.</span></p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>No optimisation</string>
|
||||
@@ -110,7 +110,7 @@
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Enable auto-splitting of webtoons like <span style=" font-style:italic;">Tower of God</span> or <span style=" font-style:italic;">Noblesse</span>.<br/>This mode was created for pages with a low width, high height and vertical panel flow.</p></body></html></string>
|
||||
<string><html><head/><body><p style='white-space:pre'>Enable special parsing mode for WebToons.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Webtoon mode</string>
|
||||
@@ -167,7 +167,7 @@
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p style='white-space:pre'>Disable splitting and rotation.</p></body></html></string>
|
||||
<string><html><head/><body><p style='white-space:pre'>Disable page splitting and rotation.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>No split/rotate</string>
|
||||
@@ -241,7 +241,7 @@
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p style='white-space:pre'>Shift+Click to select the output directory.</p></body></html></string>
|
||||
<string><html><head/><body><p style='white-space:pre'>Shift+Click to select the output directory.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Convert</string>
|
||||
@@ -269,6 +269,9 @@
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p style='white-space:pre'>Add directory containing JPG, PNG or GIF files to queue.<br/><span style=" font-weight:600;">CBR, CBZ and CB7 files inside will not be processed!</span></p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Add directory</string>
|
||||
</property>
|
||||
@@ -295,6 +298,9 @@
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p style='white-space:pre'>Add CBR, CBZ, CB7 or PDF file to queue.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Add file</string>
|
||||
</property>
|
||||
@@ -386,7 +392,7 @@
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p><span style=" font-weight:600; text-decoration: underline;">Unchecked - Normal quality mode</span><br/>Maximal quality of images but very poor magnification quality.<br/>Use it only when zoom is not needed or output files needs to be small.</p><p><span style=" font-weight:600; text-decoration: underline;">Indeterminate - High quality mode</span><br/>In most cases high quality of images and magnification.<br/>Overall quality highly depends on the resolution of source files.<br/>On Kindle models older than Paperwhite non-zoomed images might be a little blurred.<br/><br/><span style=" font-weight:600; text-decoration: underline;">Checked - Ultra quality mode</span><br/>Highest possible quality. Output files will be big.</p></body></html></string>
|
||||
<string><html><head/><body><p style='white-space:pre'>Quality of Panel View/zoom. Highly impact size of output file.<br/><span style=" font-weight:600;">This option control only quality of magnification!</span></p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>High/Ultra quality</string>
|
||||
@@ -445,6 +451,9 @@
|
||||
<property name="styleSheet">
|
||||
<string notr="true">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}</string>
|
||||
</property>
|
||||
<property name="verticalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAlwaysOn</enum>
|
||||
</property>
|
||||
<property name="showDropIndicator" stdset="0">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
@@ -661,7 +670,7 @@
|
||||
</font>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Resolution of target device.</p></body></html></string>
|
||||
<string><html><head/><body><p style='white-space:pre'>Resolution of target device.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Custom width: </string>
|
||||
@@ -694,7 +703,7 @@
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Resolution of target device.</p></body></html></string>
|
||||
<string><html><head/><body><p style='white-space:pre'>Resolution of target device.</p></body></html></string>
|
||||
</property>
|
||||
<property name="inputMask">
|
||||
<string>0000</string>
|
||||
@@ -712,7 +721,7 @@
|
||||
</font>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Resolution of target device.</p></body></html></string>
|
||||
<string><html><head/><body><p style='white-space:pre'>Resolution of target device.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Custom height: </string>
|
||||
@@ -745,7 +754,7 @@
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Resolution of target device.</p></body></html></string>
|
||||
<string><html><head/><body><p style='white-space:pre'>Resolution of target device.</p></body></html></string>
|
||||
</property>
|
||||
<property name="inputMask">
|
||||
<string>0000</string>
|
||||
|
||||
21
KCC-OSX.ui
21
KCC-OSX.ui
@@ -69,7 +69,7 @@
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p style='white-space:pre'>Disable image optimizations.<br/>Input images must be already resized.</p></body></html></string>
|
||||
<string><html><head/><body><p style='white-space:pre'>Disable image optimizations.<br/><span style=" font-weight:600;">Input images must be already resized.</span></p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>No optimisation</string>
|
||||
@@ -110,7 +110,7 @@
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Enable auto-splitting of webtoons like <span style=" font-style:italic;">Tower of God </span>or <span style=" font-style:italic;">Noblesse</span>.<br/>This mode was created for pages with a low width, high height and vertical panel flow.</p><p><br/></p></body></html></string>
|
||||
<string><html><head/><body><p style='white-space:pre'>Enable special parsing mode for WebToons.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Webtoon mode</string>
|
||||
@@ -170,7 +170,7 @@
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p style='white-space:pre'>Disable splitting and rotation.</p></body></html></string>
|
||||
<string><html><head/><body><p style='white-space:pre'>Disable page splitting and rotation.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>No split/rotate</string>
|
||||
@@ -220,7 +220,7 @@
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p style='white-space:pre'>Output format.</p></body></html></string>
|
||||
<string><html><head/><body><p style='white-space:pre'>Output format.</p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="ConvertButton">
|
||||
@@ -244,7 +244,7 @@
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p style='white-space:pre'>Shift+Click to select the output directory.</p></body></html></string>
|
||||
<string><html><head/><body><p style='white-space:pre'>Shift+Click to select the output directory.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Convert</string>
|
||||
@@ -272,6 +272,9 @@
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p style='white-space:pre'>Add directory containing JPG, PNG or GIF files to queue.<br/><span style=" font-weight:600;">CBR, CBZ and CB7 files inside will not be processed!</span></p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Add directory</string>
|
||||
</property>
|
||||
@@ -298,6 +301,9 @@
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p style='white-space:pre'>Add CBR, CBZ, CB7 or PDF file to queue.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Add file</string>
|
||||
</property>
|
||||
@@ -391,7 +397,7 @@
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p><span style=" font-weight:600; text-decoration: underline;">Unchecked - Normal quality mode</span><br/>Maximal quality of images but very poor magnification quality.<br/>Use it only when zoom is not needed or output files needs to be small.</p><p><span style=" font-weight:600; text-decoration: underline;">Indeterminate - High quality mode</span><br/>In most cases high quality of images and magnification.<br/>Overall quality highly depends on the resolution of source files.<br/>On Kindle models older than Paperwhite non-zoomed images might be a little blurred.</p><p><span style=" font-weight:600; text-decoration: underline;">Checked - Ultra quality mode</span><br/>Highest possible quality. Output files will be big.</p></body></html></string>
|
||||
<string><html><head/><body><p style='white-space:pre'>Quality of Panel View/zoom. Highly impact size of output file.<br/><span style=" font-weight:600;">This option control only quality of magnification!</span></p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>High/Ultra quality</string>
|
||||
@@ -450,6 +456,9 @@
|
||||
<property name="styleSheet">
|
||||
<string notr="true">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}</string>
|
||||
</property>
|
||||
<property name="verticalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAlwaysOn</enum>
|
||||
</property>
|
||||
<property name="showDropIndicator" stdset="0">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
|
||||
17
KCC.ui
17
KCC.ui
@@ -65,7 +65,7 @@
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p style='white-space:pre'>Disable image optimizations.<br/>Input images must be already resized.</p></body></html></string>
|
||||
<string><html><head/><body><p style='white-space:pre'>Disable image optimizations.<br/><span style=" font-weight:600;">Input images must be already resized.</span></p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>No optimisation</string>
|
||||
@@ -94,7 +94,7 @@
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p style="white-space:pre">Enable auto-splitting of webtoons like <span style=" font-style:italic;">Tower of God</span> or <span style=" font-style:italic;">Noblesse</span>.<br/>This mode is created for pages with a low width, high height and vertical panel flow.</p></body></html></string>
|
||||
<string><html><head/><body><p style='white-space:pre'>Enable special parsing mode for WebToons.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Webtoon mode</string>
|
||||
@@ -136,7 +136,7 @@
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p style='white-space:pre'>Disable splitting and rotation.</p></body></html></string>
|
||||
<string><html><head/><body><p style='white-space:pre'>Disable page splitting and rotation.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>No split/rotate</string>
|
||||
@@ -234,6 +234,9 @@
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p style='white-space:pre'>Add directory containing JPG, PNG or GIF files to queue.<br/><span style=" font-weight:600;">CBR, CBZ and CB7 files inside will not be processed!</span></p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Add directory</string>
|
||||
</property>
|
||||
@@ -259,6 +262,9 @@
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p style='white-space:pre'>Add CBR, CBZ, CB7 or PDF file to queue.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Add file</string>
|
||||
</property>
|
||||
@@ -338,7 +344,7 @@
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p style="white-space:pre"><span style=" font-weight:600; text-decoration: underline;">Unchecked - Normal quality mode<br/></span>Maximal quality of images but very poor magnification quality.<br/>Use it only when zoom is not needed or output files needs to be small.</p><p style="white-space:pre"><span style=" font-weight:600; text-decoration: underline;">Indeterminate - High quality mode<br/></span>In most cases high quality of images and magnification.<br/>Overall quality highly depends on the resolution of source files.<br/>On Kindle models older than Paperwhite non-zoomed images might be a little blurred.</p><p style="white-space:pre"><span style=" font-weight:600; text-decoration: underline;">Checked - Ultra quality mode<br/></span>Highest possible quality. Output files will be big.</p></body></html></string>
|
||||
<string><html><head/><body><p style='white-space:pre'>Quality of Panel View/zoom. Highly impact size of output file.<br/><span style=" font-weight:600;">This option control only quality of magnification!</span></p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>High/Ultra quality</string>
|
||||
@@ -385,6 +391,9 @@
|
||||
<property name="styleSheet">
|
||||
<string notr="true">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}</string>
|
||||
</property>
|
||||
<property name="verticalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAlwaysOn</enum>
|
||||
</property>
|
||||
<property name="showDropIndicator" stdset="0">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
ISC LICENSE
|
||||
|
||||
Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
||||
Copyright (c) 2013-2014 Paweł Jastrzębski <pawelj@iosphe.re>
|
||||
Copyright (c) 2013-2015 Paweł Jastrzębski <pawelj@iosphe.re>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for
|
||||
any purpose with or without fee is hereby granted, provided that the
|
||||
|
||||
402
README.md
402
README.md
@@ -1,20 +1,19 @@
|
||||
# KCC
|
||||
|
||||
**Kindle Comic Converter** is a Python app to convert comic files or folders to ePub, Panel View MOBI or E-Ink optimized CBZ.
|
||||
It was initally developed for Kindle but since v2.2 it outputs valid ePub 2.0 so _**despite its name, KCC is
|
||||
actually a comic to EPUB converter that every e-reader owner can happily use**_.
|
||||
**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
|
||||
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.
|
||||
|
||||
### A word of warning
|
||||
**KCC** _is not_ [Amazon's Kindle Comic Creator](http://www.amazon.com/gp/feature.html?ie=UTF8&docId=1001103761) nor is in any way endorsed by Amazon.
|
||||
Amazon's tool is for comic publishers and involves a lot of manual effort, while **KCC** is for comic readers.
|
||||
Amazon's tool is for comic publishers and involves a lot of manual effort, while **KCC** is for comic/manga readers.
|
||||
_KC2_ in no way is a replacement for **KCC** so you can be quite confident we'll going to carry on developing our little monster ;-)
|
||||
|
||||
### Issues / new features / donations
|
||||
If you have general questions about usage, feedback etc. please [post it here](http://www.mobileread.com/forums/showthread.php?t=207461).
|
||||
If you have some **technical** problems using KCC please [file an issue here](https://github.com/ciromattia/kcc/issues/new).
|
||||
If you can fix an open issue, fork & make a pull request.
|
||||
If you want more chances an issue is fixes or your wanted feature added, consider [placing a bounty](https://www.bountysource.com/trackers/65571-ciromattia-kcc)!
|
||||
If you can fix an open issue, fork & make a pull request.
|
||||
|
||||
If you find **KCC** valuable you can consider donating to the authors:
|
||||
- Ciro Mattia Gonano:
|
||||
@@ -32,11 +31,11 @@ You can find the latest released binary at the following links:
|
||||
|
||||
## INPUT FORMATS
|
||||
**KCC** can understand and convert, at the moment, the following input types:
|
||||
- Folders containing: PNG, JPG, GIF, TIFF or BMP files
|
||||
- Folders containing: PNG, JPG or GIF files
|
||||
- CBZ, ZIP
|
||||
- CBR, RAR *(With `unrar` executable)*
|
||||
- CB7, 7Z *(With `7za` executable)*
|
||||
- PDF *(Extracting only contained JPG images)*
|
||||
- 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)*
|
||||
@@ -46,19 +45,19 @@ You can find the latest released binary at the following links:
|
||||
### For running from source:
|
||||
- Python 3.3+
|
||||
- [PyQt5](http://www.riverbankcomputing.co.uk/software/pyqt/download5) 5.2.0+
|
||||
- [Pillow](http://pypi.python.org/pypi/Pillow/) 2.5.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)
|
||||
|
||||
On Debian based distributions these two commands should install all dependencies:
|
||||
```
|
||||
sudo apt-get install python3 python3-dev python3-pip python3-pyqt5 libtiff-dev libpng-dev libjpeg-dev p7zip-full unrar
|
||||
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
|
||||
```
|
||||
|
||||
### For freezing code:
|
||||
- Windows - [py2exe](https://pypi.python.org/pypi/py2exe) 0.9.2+
|
||||
- OS X - [py2app](https://bitbucket.org/ronaldoussoren/py2app) 0.8.0+
|
||||
- Windows - [py2exe](https://pypi.python.org/pypi/py2exe) 0.9.2.2+
|
||||
- OS X - [py2app](https://bitbucket.org/ronaldoussoren/py2app) 0.9.0+
|
||||
|
||||
## USAGE
|
||||
|
||||
@@ -67,6 +66,8 @@ After completed conversion you should find ready file alongside the original inp
|
||||
|
||||
Please check [our wiki](https://github.com/ciromattia/kcc/wiki/) for more details.
|
||||
|
||||
CLI version of **KCC** is intended for power users. It is not idiot-proof like GUI :-)
|
||||
|
||||
### Standalone `kcc-c2e.py` usage:
|
||||
|
||||
```
|
||||
@@ -75,7 +76,9 @@ Usage: kcc-c2e [options] comic_file|comic_folder
|
||||
Options:
|
||||
MAIN:
|
||||
-p PROFILE, --profile=PROFILE
|
||||
Device profile (Choose one among K1, K2, K345, KDX, KHD, KF, KFHD, KFHD8, KFHDX, KFHDX8, KFA, KoMT, KoG, KoA, KoAHD) [Default=KHD]
|
||||
Device profile (Available options: K1, K2, K345, KDX,
|
||||
KPW, KV, KFHD, KFHDX, KFHDX8, KFA, KoMT, KoG, 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)
|
||||
@@ -86,7 +89,9 @@ Options:
|
||||
Output generated file to specified directory or file
|
||||
-t TITLE, --title=TITLE
|
||||
Comic title [Default=filename or directory name]
|
||||
--cbz-output Outputs a CBZ archive and does not generate EPUB
|
||||
-f FORMAT, --format=FORMAT
|
||||
Output format (Available options: Auto, MOBI,
|
||||
EPUB, CBZ) [Default=Auto]
|
||||
--batchsplit Split output into multiple files
|
||||
|
||||
PROCESSING:
|
||||
@@ -134,134 +139,134 @@ Options:
|
||||
|
||||
This script born as a cross-platform alternative to `KindleComicParser` by **Dc5e** (published [here](http://www.mobileread.com/forums/showthread.php?t=192783)).
|
||||
|
||||
The app relies and includes the following scripts/binaries:
|
||||
The app relies and includes the following scripts:
|
||||
|
||||
- `DualMetaFix` script by **K. Hendricks**. Released with GPL-3 License.
|
||||
- `rarfile.py` script © 2005-2011 **Marko Kreen** <markokr@gmail.com>. Released with ISC License.
|
||||
- `rarfile.py` script © 2005-2014 **Marko Kreen** <markokr@gmail.com>. Released with ISC License.
|
||||
- `image.py` class from **Alex Yatskov**'s [Mangle](https://github.com/FooSoft/mangle/) with subsequent [proDOOMman](https://github.com/proDOOMman/Mangle)'s and [Birua](https://github.com/Birua/Mangle)'s patches.
|
||||
- Icon is by **Nikolay Verin** ([http://ncrow.deviantart.com/](http://ncrow.deviantart.com/)) and released under [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/) License.
|
||||
|
||||
## SAMPLE FILES CREATED BY KCC
|
||||
* [Kindle Voyage](http://kcc.iosphe.re/Samples/Ubunchu!-KV.mobi)
|
||||
* [Kindle Paperwhite](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.mobi)
|
||||
* [Kindle Fire HD](http://kcc.iosphe.re/Samples/Ubunchu!-KFHD.mobi)
|
||||
* [Kindle Fire HD 8.9"](http://kcc.iosphe.re/Samples/Ubunchu!-KFHD8.mobi)
|
||||
* [Kindle Fire HDX](http://kcc.iosphe.re/Samples/Ubunchu!-KFHDX.mobi)
|
||||
* [Kindle Fire HDX 8.9"](http://kcc.iosphe.re/Samples/Ubunchu!-KFHDX8.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)
|
||||
|
||||
## CHANGELOG
|
||||
####1.0
|
||||
* Initial version
|
||||
####4.4:
|
||||
* Improved speed and quality of conversion
|
||||
* Added RAR5 support
|
||||
* Dropped BMP and TIFF support
|
||||
* Fixed some WebToon mode bugs
|
||||
* Fixed CBR parsing on OSX
|
||||
|
||||
####1.1
|
||||
* Added support for CBZ/CBR files in comic2ebook.py
|
||||
####4.3.1:
|
||||
* Fixed Kindle Voyage profile
|
||||
* Fixed some bugs in OS X release
|
||||
* CLI version now support multiple input files at once
|
||||
* Disabled MCB support
|
||||
* Other minor tweaks
|
||||
|
||||
####1.1.1
|
||||
* Added support for CBZ/CBR files in Kindle Comic Converter
|
||||
####4.3:
|
||||
* Added profiles for Kindle Voyage and Kobo Aura H2O
|
||||
* Added missing features to CLI version
|
||||
* Other minor bug fixes
|
||||
|
||||
####1.2
|
||||
* Comic optimizations! Split pages not target-oriented (landscape with portrait target or portrait with landscape target), add palette and other image optimizations from Mangle. WARNING: PIL is required for all image mangling!
|
||||
####4.2.1:
|
||||
* Improved margin color detection
|
||||
* Fixed random crashes of MOBI processing step
|
||||
* Fixed resizing problems in high quality mode
|
||||
* Fixed some MCD support bugs
|
||||
* Default output format for Kindle DX is now CBZ
|
||||
|
||||
####1.3
|
||||
* Fixed an issue in OPF generation for device resolution
|
||||
* Reworked options system (call with -h option to get the inline help)
|
||||
####4.2:
|
||||
* Added [Manga Cover Database](http://manga.joentjuh.nl/) support
|
||||
* Officially dropped Windows XP support
|
||||
* Fixed _Other_ profile
|
||||
* Fixed problems with page order on stock KOBO CBZ reader
|
||||
* Many other small bug fixes and tweaks
|
||||
|
||||
####1.4
|
||||
* Added some options for controlling image optimization
|
||||
* Further optimization (ImageOps, page numbering cut, autocontrast)
|
||||
####4.1:
|
||||
* Thanks to code contributed by Kevin Hendricks speed of MOBI creation was greatly increased
|
||||
* Improved performance on Windows
|
||||
* Improved MOBI splitting and changed maximal size of output file
|
||||
* Fixed _No optimization_ mode
|
||||
* Multiple small tweaks nad minor bug fixes
|
||||
|
||||
####1.4.1
|
||||
* Fixed a serious bug on resizing when img ratio was bigger than device one
|
||||
####4.0.2:
|
||||
* Fixed some Windows and OSX specific bugs
|
||||
* Fixed problem with marigns when using HQ mode
|
||||
|
||||
####1.5
|
||||
* Added subfolder support for multiple chapters.
|
||||
####4.0.1:
|
||||
* Fixed file lock problems that plagued some Windows users
|
||||
* Fixed content server failing to start on Windows
|
||||
* Improved performance of WebToon splitter
|
||||
* Tweaked margin color detection
|
||||
|
||||
####2.0
|
||||
* GUI! AppleScript is gone and Tk is used to provide cross-platform GUI support.
|
||||
####4.0:
|
||||
* KCC now use Python 3.3 and Qt 5.2
|
||||
* Full UTF-8 awareness
|
||||
* CBZ output now support Manga mode
|
||||
* Improved Panel View support and margin color detection
|
||||
* Added drag&drop support
|
||||
* Output directory can be now selected
|
||||
* Windows release now have auto-updater
|
||||
* Names of chapters on Kindle should be now more user friendly
|
||||
* Fixed OSX file association support
|
||||
* Many extensive internal changes and tweaks
|
||||
|
||||
####2.1
|
||||
* Added basic error reporting
|
||||
####3.7.2:
|
||||
* Fixed problems with HQ mode
|
||||
|
||||
####2.2:
|
||||
* Added (valid!) ePub 2.0 output
|
||||
* Rename .zip files to .cbz to avoid overwriting
|
||||
####3.7.1:
|
||||
* Hotfixed Kobo profiles
|
||||
|
||||
####2.3
|
||||
* Fixed win32 ePub generation, folder handling, filenames with spaces and subfolders
|
||||
####3.7:
|
||||
* Added profiles for KOBO devices
|
||||
* Improved Panel View support
|
||||
* Improved WebToon splitter
|
||||
* Improved margin color autodetection
|
||||
* Tweaked EPUB output
|
||||
* Fixed stretching option
|
||||
* GUI tweaks and minor bugfixes
|
||||
|
||||
####2.4
|
||||
* Use temporary directory as workdir (fixes converting from external volumes and zipfiles renaming)
|
||||
* Fixed "add folders" from GUI.
|
||||
####3.6.2:
|
||||
* Fixed previous PNG output fix
|
||||
* Fixed Panel View anomalies
|
||||
|
||||
####2.5
|
||||
* Added --black-borders option to set added borders black when page's ratio is not the device's one (#11).
|
||||
* Fixes epub containing zipped itself (#10)
|
||||
####3.6.1:
|
||||
* Fixed PNG output
|
||||
|
||||
####2.6
|
||||
* Added --rotate option to rotate landscape images instead of splitting them (#16, #24)
|
||||
* Added --output option to customize ePub output dir/file (#22)
|
||||
* Add rendition:layout and rendition:orientation ePub meta tags (supported by new kindlegen 2.8)
|
||||
* Fixed natural sorting for files (#18)
|
||||
####3.6:
|
||||
* Increased quality of Panel View zoom
|
||||
* Creation of multipart MOBI output is now faster on machines with 4GB+ RAM
|
||||
* Automatic gamma correction now distinguishes color and grayscale images
|
||||
* Added ComicRack metadata parser
|
||||
* Implemented new method to detect border color in non-webtoon comics
|
||||
* Upscaling is now enabled by default for Kindle Fire HD/HDX
|
||||
* Windows nad Linux releases now have tray icon
|
||||
* Fixed Kindle Fire HDX 7" output
|
||||
* Increased target resolution for Kindle DX/DXG CBZ output
|
||||
|
||||
####2.7
|
||||
* Lots of GUI improvements (#27, #13)
|
||||
* Added gamma support within --gamma option (defaults to profile-specified gamma) (#26, #27)
|
||||
* Added --nodithering option to prevent dithering optimizations (#27)
|
||||
* Epub margins support (#30)
|
||||
* Fixed no file added if file has no spaces on Windows (#25)
|
||||
* Gracefully exit if unrar missing (#15)
|
||||
* Do not call kindlegen if source epub is bigger than 320MB (#17)
|
||||
* Get filetype from magic number (#14)
|
||||
* PDF conversion works again
|
||||
####3.5:
|
||||
* Added simple content server - Converted files can be now delivered wireless
|
||||
* Added proper Windows installer
|
||||
* Improved multiprocessing speed
|
||||
* GUI tweaks and minor bug fixes
|
||||
|
||||
####2.8
|
||||
* Updated rarfile library
|
||||
* Panel View support + HQ support (#36) - new option: --nopanelviewhq
|
||||
* Split profiles for K4NT and K4T
|
||||
* Rewrite of Landscape Mode support (huge readability improvement for KPW)
|
||||
* Upscale use now BILINEAR method
|
||||
* Added generic CSS file
|
||||
* Optimized archive extraction for zip/rar files (#40)
|
||||
|
||||
####2.9
|
||||
* Added support for generating a plain CBZ (skipping all the EPUB/Mobi generation) (#45)
|
||||
* Prevent output file overwriting the source one: if a duplicate name is detected, append _kcc to the name
|
||||
* Rarfile library updated to 2.6
|
||||
* Added GIF, TIFF and BMP to supported formats (#42)
|
||||
* Filenames slugifications (#28, #31, #9, #8)
|
||||
|
||||
####2.10:
|
||||
* Multiprocessing support
|
||||
* Kindle Fire support (color ePub/Mobi)
|
||||
* Panel View support for horizontal content
|
||||
* Fixed panel order for horizontal pages when --rotate is enabled
|
||||
* Disabled cropping and page number cutting for blank pages
|
||||
* Fixed some slugify issues with specific file naming conventions (#50, #51)
|
||||
|
||||
####3.0:
|
||||
* New QT GUI
|
||||
* Merge with AWKCC
|
||||
* Added ultra quality mode
|
||||
* 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
|
||||
|
||||
####3.2:
|
||||
* Too big EPUB files are now splitted before conversion to MOBI
|
||||
* Added experimental parser of manga webtoons
|
||||
* Improved error handling
|
||||
|
||||
####3.2.1:
|
||||
* Hotfixed crash occurring on OS with Russian locale
|
||||
####3.4:
|
||||
* Improved PNG output
|
||||
* Increased quality of upscaling
|
||||
* Added support of file association - KCC can now open CBZ, CBR, CB7, ZIP, RAR, 7Z and PDF files directly
|
||||
* Paths that contain UTF-8 characters are now supported
|
||||
* Migrated to new version of Pillow library
|
||||
* Merged DX and DXG profiles
|
||||
* Many other minor bug fixes and GUI tweaks
|
||||
|
||||
####3.3:
|
||||
* Margins are now automatically omitted in Panel View mode
|
||||
@@ -277,100 +282,117 @@ The app relies and includes the following scripts/binaries:
|
||||
* Windows release is now bundled with UnRAR and 7za
|
||||
* Small GUI tweaks
|
||||
|
||||
####3.4:
|
||||
* Improved PNG output
|
||||
* Increased quality of upscaling
|
||||
* Added support of file association - KCC can now open CBZ, CBR, CB7, ZIP, RAR, 7Z and PDF files directly
|
||||
* Paths that contain UTF-8 characters are now supported
|
||||
* Migrated to new version of Pillow library
|
||||
* Merged DX and DXG profiles
|
||||
* Many other minor bug fixes and GUI tweaks
|
||||
####3.2:
|
||||
* Too big EPUB files are now splitted before conversion to MOBI
|
||||
* Added experimental parser of manga webtoons
|
||||
* Improved error handling
|
||||
|
||||
####3.5:
|
||||
* Added simple content server - Converted files can be now delivered wireless
|
||||
* Added proper Windows installer
|
||||
* Improved multiprocessing speed
|
||||
* GUI tweaks and minor bug fixes
|
||||
####3.2.1:
|
||||
* Hotfixed crash occurring on OS with Russian locale
|
||||
|
||||
####3.6:
|
||||
* Increased quality of Panel View zoom
|
||||
* Creation of multipart MOBI output is now faster on machines with 4GB+ RAM
|
||||
* Automatic gamma correction now distinguishes color and grayscale images
|
||||
* Added ComicRack metadata parser
|
||||
* Implemented new method to detect border color in non-webtoon comics
|
||||
* Upscaling is now enabled by default for Kindle Fire HD/HDX
|
||||
* Windows nad Linux releases now have tray icon
|
||||
* Fixed Kindle Fire HDX 7" output
|
||||
* Increased target resolution for Kindle DX/DXG CBZ output
|
||||
####3.1:
|
||||
* Added profile: Kindle for Android
|
||||
* Add file/directory dialogs now support multiselect
|
||||
* Many small fixes and tweaks
|
||||
|
||||
####3.6.1:
|
||||
* Fixed PNG output
|
||||
####3.0:
|
||||
* New QT GUI
|
||||
* Merge with AWKCC
|
||||
* Added ultra quality mode
|
||||
* Added support for custom width/height
|
||||
* Added option to disable color conversion
|
||||
|
||||
####3.6.2:
|
||||
* Fixed previous PNG output fix
|
||||
* Fixed Panel View anomalies
|
||||
####2.10:
|
||||
* Multiprocessing support
|
||||
* Kindle Fire support (color EPUB/MOBI)
|
||||
* Panel View support for horizontal content
|
||||
* Fixed panel order for horizontal pages when --rotate is enabled
|
||||
* Disabled cropping and page number cutting for blank pages
|
||||
* Fixed some slugify issues with specific file naming conventions (#50, #51)
|
||||
|
||||
####3.7:
|
||||
* Added profiles for KOBO devices
|
||||
* Improved Panel View support
|
||||
* Improved WebToon splitter
|
||||
* Improved margin color autodetection
|
||||
* Tweaked EPUB output
|
||||
* Fixed stretching option
|
||||
* GUI tweaks and minor bugfixes
|
||||
####2.9
|
||||
* Added support for generating a plain CBZ (skipping all the EPUB/MOBI generation) (#45)
|
||||
* Prevent output file overwriting the source one: if a duplicate name is detected, append _kcc to the name
|
||||
* Rarfile library updated to 2.6
|
||||
* Added GIF, TIFF and BMP to supported formats (#42)
|
||||
* Filenames slugifications (#28, #31, #9, #8)
|
||||
|
||||
####3.7.1:
|
||||
* Hotfixed Kobo profiles
|
||||
####2.8
|
||||
* Updated rarfile library
|
||||
* Panel View support + HQ support (#36) - new option: --nopanelviewhq
|
||||
* Split profiles for K4NT and K4T
|
||||
* Rewrite of Landscape Mode support (huge readability improvement for KPW)
|
||||
* Upscale use now BILINEAR method
|
||||
* Added generic CSS file
|
||||
* Optimized archive extraction for zip/rar files (#40)
|
||||
|
||||
####3.7.2:
|
||||
* Fixed problems with HQ mode
|
||||
####2.7
|
||||
* Lots of GUI improvements (#27, #13)
|
||||
* Added gamma support within --gamma option (defaults to profile-specified gamma) (#26, #27)
|
||||
* Added --nodithering option to prevent dithering optimizations (#27)
|
||||
* EPUB margins support (#30)
|
||||
* Fixed no file added if file has no spaces on Windows (#25)
|
||||
* Gracefully exit if unrar missing (#15)
|
||||
* Do not call kindlegen if source EPUB is bigger than 320MB (#17)
|
||||
* Get filetype from magic number (#14)
|
||||
* PDF conversion works again
|
||||
|
||||
####4.0:
|
||||
* KCC now use Python 3.3 and Qt 5.2
|
||||
* Full UTF-8 awareness
|
||||
* CBZ output now support Manga mode
|
||||
* Improved Panel View support and margin color detection
|
||||
* Added drag&drop support
|
||||
* Output directory can be now selected
|
||||
* Windows release now have auto-updater
|
||||
* Names of chapters on Kindle should be now more user friendly
|
||||
* Fixed OSX file association support
|
||||
* Many extensive internal changes and tweaks
|
||||
####2.6
|
||||
* Added --rotate option to rotate landscape images instead of splitting them (#16, #24)
|
||||
* Added --output option to customize EPUB output dir/file (#22)
|
||||
* Add rendition:layout and rendition:orientation EPUB meta tags (supported by new kindlegen 2.8)
|
||||
* Fixed natural sorting for files (#18)
|
||||
|
||||
####4.0.1:
|
||||
* Fixed file lock problems that plagued some Windows users
|
||||
* Fixed content server failing to start on Windows
|
||||
* Improved performance of WebToon splitter
|
||||
* Tweaked margin color detection
|
||||
####2.5
|
||||
* Added --black-borders option to set added borders black when page's ratio is not the device's one (#11).
|
||||
* Fixes EPUB containing zipped itself (#10)
|
||||
|
||||
####4.0.2:
|
||||
* Fixed some Windows and OSX specific bugs
|
||||
* Fixed problem with marigns when using HQ mode
|
||||
####2.4
|
||||
* Use temporary directory as workdir (fixes converting from external volumes and zipfiles renaming)
|
||||
* Fixed "add folders" from GUI.
|
||||
|
||||
####4.1:
|
||||
* Thanks to code contributed by Kevin Hendricks speed of MOBI creation was greatly increased
|
||||
* Improved performance on Windows
|
||||
* Improved MOBI splitting and changed maximal size of output file
|
||||
* Fixed _No optimization_ mode
|
||||
* Multiple small tweaks nad minor bug fixes
|
||||
####2.3
|
||||
* Fixed win32 EPUB generation, folder handling, filenames with spaces and subfolders
|
||||
|
||||
####4.2:
|
||||
* Added [Manga Cover Database](http://manga.joentjuh.nl/) support
|
||||
* Officially dropped Windows XP support
|
||||
* Fixed _Other_ profile
|
||||
* Fixed problems with page order on stock KOBO CBZ reader
|
||||
* Many other small bug fixes and tweaks
|
||||
####2.2:
|
||||
* Added (valid!) EPUB 2.0 output
|
||||
* Rename .zip files to .cbz to avoid overwriting
|
||||
|
||||
####4.2.1:
|
||||
* Improved margin color detection
|
||||
* Fixed random crashes of MOBI processing step
|
||||
* Fixed resizing problems in high quality mode
|
||||
* Fixed some MCD support bugs
|
||||
* Default output format for Kindle DX is now CBZ
|
||||
####2.1
|
||||
* Added basic error reporting
|
||||
|
||||
####2.0
|
||||
* GUI! AppleScript is gone and Tk is used to provide cross-platform GUI support.
|
||||
|
||||
####1.5
|
||||
* Added subfolder support for multiple chapters.
|
||||
|
||||
####1.4.1
|
||||
* Fixed a serious bug on resizing when img ratio was bigger than device one
|
||||
|
||||
####1.4
|
||||
* Added some options for controlling image optimization
|
||||
* Further optimization (ImageOps, page numbering cut, autocontrast)
|
||||
|
||||
####1.3
|
||||
* Fixed an issue in OPF generation for device resolution
|
||||
* Reworked options system (call with -h option to get the inline help)
|
||||
|
||||
####1.2
|
||||
* Comic optimizations! Split pages not target-oriented (landscape with portrait target or portrait with landscape target), add palette and other image optimizations from Mangle. WARNING: PIL is required for all image mangling!
|
||||
|
||||
####1.1.1
|
||||
* Added support for CBZ/CBR files in Kindle Comic Converter
|
||||
|
||||
####1.1
|
||||
* Added support for CBZ/CBR files in comic2ebook.py
|
||||
|
||||
####1.0
|
||||
* Initial version
|
||||
|
||||
## KNOWN ISSUES
|
||||
Please check [wiki page](https://github.com/ciromattia/kcc/wiki/Known-issues).
|
||||
|
||||
## COPYRIGHT
|
||||
Copyright (c) 2012-2014 Ciro Mattia Gonano and Paweł Jastrzębski.
|
||||
Copyright (c) 2012-2015 Ciro Mattia Gonano and Paweł Jastrzębski.
|
||||
**KCC** is released under ISC LICENSE; see LICENSE.txt for further details.
|
||||
|
||||
41
kcc-c2e.py
41
kcc-c2e.py
@@ -2,7 +2,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
||||
# Copyright (c) 2013-2014 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||
# Copyright (c) 2013-2015 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||
#
|
||||
# Permission to use, copy, modify, and/or distribute this software for
|
||||
# any purpose with or without fee is hereby granted, provided that the
|
||||
@@ -18,9 +18,9 @@
|
||||
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
# PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
__version__ = '4.2.1'
|
||||
__version__ = '4.4'
|
||||
__license__ = 'ISC'
|
||||
__copyright__ = '2012-2014, 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'
|
||||
|
||||
import sys
|
||||
@@ -28,39 +28,8 @@ if sys.version_info[0] != 3:
|
||||
print('ERROR: This is Python 3 script!')
|
||||
exit(1)
|
||||
|
||||
# Dependency check
|
||||
missing = []
|
||||
try:
|
||||
# noinspection PyUnresolvedReferences
|
||||
import psutil
|
||||
if tuple(map(int, ('2.0.0'.split(".")))) > tuple(map(int, psutil.version_info)):
|
||||
missing.append('psutil 2.0.0+')
|
||||
except ImportError:
|
||||
missing.append('psutil 2.0.0+')
|
||||
try:
|
||||
# noinspection PyUnresolvedReferences
|
||||
import PIL
|
||||
if tuple(map(int, ('2.5.0'.split(".")))) > tuple(map(int, (PIL.PILLOW_VERSION.split(".")))):
|
||||
missing.append('Pillow 2.5.0+')
|
||||
except ImportError:
|
||||
missing.append('Pillow 2.5.0+')
|
||||
try:
|
||||
# noinspection PyUnresolvedReferences
|
||||
import slugify
|
||||
except ImportError:
|
||||
missing.append('python-slugify')
|
||||
if len(missing) > 0:
|
||||
try:
|
||||
# noinspection PyUnresolvedReferences
|
||||
import tkinter
|
||||
# noinspection PyUnresolvedReferences
|
||||
import tkinter.messagebox
|
||||
importRoot = tkinter.Tk()
|
||||
importRoot.withdraw()
|
||||
tkinter.messagebox.showerror('KCC - Error', 'ERROR: ' + ', '.join(missing) + ' is not installed!')
|
||||
except ImportError:
|
||||
print('ERROR: ' + ', '.join(missing) + ' is not installed!')
|
||||
exit(1)
|
||||
from kcc.shared import dependencyCheck
|
||||
dependencyCheck(2)
|
||||
|
||||
from multiprocessing import freeze_support
|
||||
from kcc.comic2ebook import main
|
||||
|
||||
31
kcc-c2p.py
31
kcc-c2p.py
@@ -2,7 +2,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
||||
# Copyright (c) 2013-2014 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||
# Copyright (c) 2013-2015 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||
#
|
||||
# Permission to use, copy, modify, and/or distribute this software for
|
||||
# any purpose with or without fee is hereby granted, provided that the
|
||||
@@ -18,9 +18,9 @@
|
||||
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
# PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
__version__ = '4.2.1'
|
||||
__version__ = '4.4'
|
||||
__license__ = 'ISC'
|
||||
__copyright__ = '2012-2014, 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'
|
||||
|
||||
import sys
|
||||
@@ -28,33 +28,14 @@ if sys.version_info[0] != 3:
|
||||
print('ERROR: This is Python 3 script!')
|
||||
exit(1)
|
||||
|
||||
# Dependency check
|
||||
missing = []
|
||||
try:
|
||||
# noinspection PyUnresolvedReferences
|
||||
import PIL
|
||||
if tuple(map(int, ('2.5.0'.split(".")))) > tuple(map(int, (PIL.PILLOW_VERSION.split(".")))):
|
||||
missing.append('Pillow 2.5.0+')
|
||||
except ImportError:
|
||||
missing.append('Pillow 2.5.0+')
|
||||
if len(missing) > 0:
|
||||
try:
|
||||
# noinspection PyUnresolvedReferences
|
||||
import tkinter
|
||||
# noinspection PyUnresolvedReferences
|
||||
import tkinter.messagebox
|
||||
importRoot = tkinter.Tk()
|
||||
importRoot.withdraw()
|
||||
tkinter.messagebox.showerror('KCC - Error', 'ERROR: ' + ', '.join(missing) + ' is not installed!')
|
||||
except ImportError:
|
||||
print('ERROR: ' + ', '.join(missing) + ' is not installed!')
|
||||
exit(1)
|
||||
from kcc.shared import dependencyCheck
|
||||
dependencyCheck(1)
|
||||
|
||||
from multiprocessing import freeze_support
|
||||
from kcc.comic2panel import main
|
||||
|
||||
if __name__ == "__main__":
|
||||
freeze_support()
|
||||
print(('comic2ebook v%(__version__)s. Written by Ciro Mattia Gonano and Pawel Jastrzebski.' % globals()))
|
||||
print(('comic2panel v%(__version__)s. Written by Ciro Mattia Gonano and Pawel Jastrzebski.' % globals()))
|
||||
main(sys.argv[1:])
|
||||
sys.exit(0)
|
||||
6
kcc.iss
6
kcc.iss
@@ -1,5 +1,5 @@
|
||||
#define MyAppName "Kindle Comic Converter"
|
||||
#define MyAppVersion "4.2.1"
|
||||
#define MyAppVersion "4.4"
|
||||
#define MyAppPublisher "Ciro Mattia Gonano, Paweł Jastrzębski"
|
||||
#define MyAppURL "http://kcc.iosphe.re/"
|
||||
#define MyAppExeName "KCC.exe"
|
||||
@@ -12,7 +12,7 @@ AppPublisher={#MyAppPublisher}
|
||||
AppPublisherURL={#MyAppURL}
|
||||
AppSupportURL={#MyAppURL}
|
||||
AppUpdatesURL={#MyAppURL}
|
||||
AppCopyright=Copyright (C) 2012-2014 Ciro Mattia Gonano and Paweł Jastrzębski
|
||||
AppCopyright=Copyright (C) 2012-2015 Ciro Mattia Gonano and Paweł Jastrzębski
|
||||
DefaultDirName={pf}\{#MyAppName}
|
||||
DefaultGroupName={#MyAppName}
|
||||
AllowNoIcons=yes
|
||||
@@ -43,13 +43,11 @@ Name: "CB7association"; Description: "CB7"; GroupDescription: "File associations
|
||||
|
||||
[Files]
|
||||
; x64 files
|
||||
Source: "dist_64\imageformats\*"; DestDir: "{app}\imageformats\"; Flags: ignoreversion; Check: Is64BitInstallMode
|
||||
Source: "dist_64\platforms\*"; DestDir: "{app}\platforms\"; Flags: ignoreversion; Check: Is64BitInstallMode
|
||||
Source: "dist_64\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion; Check: Is64BitInstallMode
|
||||
Source: "dist_64\*.dll"; DestDir: "{app}"; Flags: ignoreversion; Check: Is64BitInstallMode
|
||||
Source: "other\vcredist_x64.exe"; DestDir: "{tmp}"; Flags: ignoreversion deleteafterinstall; Check: Is64BitInstallMode
|
||||
; x86 files
|
||||
Source: "dist\imageformats\*"; DestDir: "{app}\imageformats\"; Flags: ignoreversion; Check: not Is64BitInstallMode
|
||||
Source: "dist\platforms\*"; DestDir: "{app}\platforms\"; Flags: ignoreversion; Check: not Is64BitInstallMode
|
||||
Source: "dist\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion; Check: not Is64BitInstallMode
|
||||
Source: "dist\*.dll"; DestDir: "{app}"; Flags: ignoreversion; Check: not Is64BitInstallMode
|
||||
|
||||
151
kcc.py
151
kcc.py
@@ -2,7 +2,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
||||
# Copyright (c) 2013-2014 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||
# Copyright (c) 2013-2015 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||
#
|
||||
# Permission to use, copy, modify, and/or distribute this software for
|
||||
# any purpose with or without fee is hereby granted, provided that the
|
||||
@@ -18,9 +18,9 @@
|
||||
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
# PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
__version__ = '4.2.1'
|
||||
__version__ = '4.4'
|
||||
__license__ = 'ISC'
|
||||
__copyright__ = '2012-2014, 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'
|
||||
|
||||
import sys
|
||||
@@ -28,156 +28,47 @@ if sys.version_info[0] != 3:
|
||||
print('ERROR: This is Python 3 script!')
|
||||
exit(1)
|
||||
|
||||
# Dependency check
|
||||
missing = []
|
||||
try:
|
||||
# noinspection PyUnresolvedReferences
|
||||
from PyQt5 import QtCore, QtNetwork, QtWidgets
|
||||
if tuple(map(int, ('5.2.0'.split(".")))) > tuple(map(int, (QtCore.qVersion().split(".")))):
|
||||
missing.append('PyQt5 5.2.0+')
|
||||
except ImportError:
|
||||
missing.append('PyQt5 5.2.0+')
|
||||
try:
|
||||
# noinspection PyUnresolvedReferences
|
||||
import psutil
|
||||
if tuple(map(int, ('2.0.0'.split(".")))) > tuple(map(int, psutil.version_info)):
|
||||
missing.append('psutil 2.0.0+')
|
||||
except ImportError:
|
||||
missing.append('psutil 2.0.0+')
|
||||
try:
|
||||
# noinspection PyUnresolvedReferences
|
||||
import PIL
|
||||
if tuple(map(int, ('2.5.0'.split(".")))) > tuple(map(int, (PIL.PILLOW_VERSION.split(".")))):
|
||||
missing.append('Pillow 2.5.0+')
|
||||
except ImportError:
|
||||
missing.append('Pillow 2.5.0+')
|
||||
try:
|
||||
# noinspection PyUnresolvedReferences
|
||||
import slugify
|
||||
except ImportError:
|
||||
missing.append('python-slugify')
|
||||
if len(missing) > 0:
|
||||
try:
|
||||
# noinspection PyUnresolvedReferences
|
||||
import tkinter
|
||||
# noinspection PyUnresolvedReferences
|
||||
import tkinter.messagebox
|
||||
importRoot = tkinter.Tk()
|
||||
importRoot.withdraw()
|
||||
tkinter.messagebox.showerror('KCC - Error', 'ERROR: ' + ', '.join(missing) + ' is not installed!')
|
||||
except ImportError:
|
||||
print('ERROR: ' + ', '.join(missing) + ' is not installed!')
|
||||
exit(1)
|
||||
|
||||
import os
|
||||
from multiprocessing import freeze_support
|
||||
from kcc import KCC_gui
|
||||
|
||||
# OS specific PATH variable workarounds
|
||||
import os
|
||||
if sys.platform.startswith('darwin'):
|
||||
if 'RESOURCEPATH' in os.environ:
|
||||
os.environ['PATH'] = os.environ['RESOURCEPATH'] + ':' + os.environ['PATH']
|
||||
else:
|
||||
if 'RESOURCEPATH' not in os.environ:
|
||||
os.environ['PATH'] = os.path.dirname(os.path.abspath(__file__)) + '/other/:' + os.environ['PATH']
|
||||
else:
|
||||
os.environ['PATH'] = './../Resources:/usr/local/bin:/usr/bin:/bin'
|
||||
elif sys.platform.startswith('win'):
|
||||
if getattr(sys, 'frozen', False):
|
||||
os.chdir(os.path.dirname(os.path.abspath(sys.executable)))
|
||||
|
||||
# Implementing dummy stdout and stderr for frozen Windows release
|
||||
class fakestd(object):
|
||||
class FakeSTD(object):
|
||||
def write(self, string):
|
||||
pass
|
||||
|
||||
def flush(self):
|
||||
pass
|
||||
sys.stdout = fakestd()
|
||||
sys.stderr = fakestd()
|
||||
sys.stdout = FakeSTD()
|
||||
sys.stderr = FakeSTD()
|
||||
else:
|
||||
os.environ['PATH'] = os.path.dirname(os.path.abspath(__file__)) + '/other/;' + os.environ['PATH']
|
||||
os.chdir(os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
from kcc.shared import dependencyCheck
|
||||
dependencyCheck(3)
|
||||
|
||||
# Implementing detection of already running KCC instance and forwarding argv to it
|
||||
class QApplicationMessaging(QtWidgets.QApplication):
|
||||
messageFromOtherInstance = QtCore.pyqtSignal(bytes)
|
||||
|
||||
def __init__(self, argv):
|
||||
QtWidgets.QApplication.__init__(self, argv)
|
||||
self._memory = QtCore.QSharedMemory(self)
|
||||
self._memory.setKey('KCC')
|
||||
if self._memory.attach():
|
||||
self._running = True
|
||||
else:
|
||||
self._running = False
|
||||
self._memory.create(1)
|
||||
self._key = 'KCC'
|
||||
self._timeout = 1000
|
||||
self._server = QtNetwork.QLocalServer(self)
|
||||
if not self.isRunning():
|
||||
# noinspection PyUnresolvedReferences
|
||||
self._server.newConnection.connect(self.handleMessage)
|
||||
self._server.listen(self._key)
|
||||
|
||||
def shutdown(self):
|
||||
if self._memory.isAttached():
|
||||
self._memory.detach()
|
||||
self._server.close()
|
||||
|
||||
def event(self, e):
|
||||
if e.type() == QtCore.QEvent.FileOpen:
|
||||
# noinspection PyArgumentList
|
||||
self.messageFromOtherInstance.emit(bytes(e.file(), 'UTF-8'))
|
||||
return True
|
||||
else:
|
||||
return QtWidgets.QApplication.event(self, e)
|
||||
|
||||
def isRunning(self):
|
||||
return self._running
|
||||
|
||||
def handleMessage(self):
|
||||
socket = self._server.nextPendingConnection()
|
||||
if socket.waitForReadyRead(self._timeout):
|
||||
self.messageFromOtherInstance.emit(socket.readAll().data())
|
||||
|
||||
def sendMessage(self, message):
|
||||
if self.isRunning():
|
||||
socket = QtNetwork.QLocalSocket(self)
|
||||
socket.connectToServer(self._key, QtCore.QIODevice.WriteOnly)
|
||||
if not socket.waitForConnected(self._timeout):
|
||||
return False
|
||||
# noinspection PyArgumentList
|
||||
socket.write(bytes(message, 'UTF-8'))
|
||||
if not socket.waitForBytesWritten(self._timeout):
|
||||
return False
|
||||
socket.disconnectFromServer()
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
# Adding signals to QMainWindow
|
||||
class QMainWindowKCC(QtWidgets.QMainWindow):
|
||||
progressBarTick = QtCore.pyqtSignal(str)
|
||||
modeConvert = QtCore.pyqtSignal(int)
|
||||
addMessage = QtCore.pyqtSignal(str, str, bool)
|
||||
addTrayMessage = QtCore.pyqtSignal(str, str)
|
||||
showDialog = QtCore.pyqtSignal(str, str)
|
||||
hideProgressBar = QtCore.pyqtSignal()
|
||||
forceShutdown = QtCore.pyqtSignal()
|
||||
dialogAnswer = QtCore.pyqtSignal(int)
|
||||
|
||||
from multiprocessing import freeze_support
|
||||
from kcc import KCC_gui
|
||||
|
||||
if __name__ == "__main__":
|
||||
freeze_support()
|
||||
KCCAplication = QApplicationMessaging(sys.argv)
|
||||
KCCAplication = KCC_gui.QApplicationMessaging(sys.argv)
|
||||
if KCCAplication.isRunning():
|
||||
if len(sys.argv) > 1:
|
||||
KCCAplication.sendMessage(sys.argv[1])
|
||||
sys.exit(0)
|
||||
else:
|
||||
KCCAplication.sendMessage('ARISE')
|
||||
sys.exit(0)
|
||||
KCCWindow = QMainWindowKCC()
|
||||
KCCUI = KCC_gui.KCCGUI(KCCAplication, KCCWindow)
|
||||
if len(sys.argv) > 1:
|
||||
KCCUI.handleMessage(sys.argv[1])
|
||||
sys.exit(KCCAplication.exec_())
|
||||
else:
|
||||
KCCWindow = KCC_gui.QMainWindowKCC()
|
||||
KCCUI = KCC_gui.KCCGUI(KCCAplication, KCCWindow)
|
||||
if len(sys.argv) > 1:
|
||||
KCCUI.handleMessage(sys.argv[1])
|
||||
sys.exit(KCCAplication.exec_())
|
||||
|
||||
340
kcc/KCC_gui.py
340
kcc/KCC_gui.py
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
||||
# Copyright (c) 2013-2014 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||
# Copyright (c) 2013-2015 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||
#
|
||||
# Permission to use, copy, modify, and/or distribute this software for
|
||||
# any purpose with or without fee is hereby granted, provided that the
|
||||
@@ -17,9 +17,9 @@
|
||||
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
# PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
__version__ = '4.2.1'
|
||||
__version__ = '4.4'
|
||||
__license__ = 'ISC'
|
||||
__copyright__ = '2012-2014, 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'
|
||||
|
||||
import os
|
||||
@@ -33,15 +33,12 @@ 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
|
||||
from PyQt5 import QtGui, QtCore, QtWidgets, QtNetwork
|
||||
from xml.dom.minidom import parse
|
||||
from html.parser import HTMLParser
|
||||
from psutil import virtual_memory, Popen, Process
|
||||
from uuid import uuid4
|
||||
from psutil import Popen, Process
|
||||
from copy import copy
|
||||
from .shared import md5Checksum
|
||||
from .shared import md5Checksum, HTMLStripper
|
||||
from . import comic2ebook
|
||||
from . import dualmetafix
|
||||
from . import KCC_rc_web
|
||||
if sys.platform.startswith('darwin'):
|
||||
from . import KCC_ui_osx as KCC_ui
|
||||
@@ -51,6 +48,64 @@ else:
|
||||
from . import KCC_ui
|
||||
|
||||
|
||||
class QApplicationMessaging(QtWidgets.QApplication):
|
||||
messageFromOtherInstance = QtCore.pyqtSignal(bytes)
|
||||
|
||||
def __init__(self, argv):
|
||||
QtWidgets.QApplication.__init__(self, argv)
|
||||
self._key = 'KCC'
|
||||
self._timeout = 1000
|
||||
self._locked = False
|
||||
socket = QtNetwork.QLocalSocket(self)
|
||||
socket.connectToServer(self._key, QtCore.QIODevice.WriteOnly)
|
||||
if not socket.waitForConnected(self._timeout):
|
||||
self._server = QtNetwork.QLocalServer(self)
|
||||
# noinspection PyUnresolvedReferences
|
||||
self._server.newConnection.connect(self.handleMessage)
|
||||
self._server.listen(self._key)
|
||||
else:
|
||||
self._locked = True
|
||||
socket.disconnectFromServer()
|
||||
|
||||
def __del__(self):
|
||||
if not self._locked:
|
||||
self._server.close()
|
||||
|
||||
def event(self, e):
|
||||
if e.type() == QtCore.QEvent.FileOpen:
|
||||
self.messageFromOtherInstance.emit(bytes(e.file(), 'UTF-8'))
|
||||
return True
|
||||
else:
|
||||
return QtWidgets.QApplication.event(self, e)
|
||||
|
||||
def isRunning(self):
|
||||
return self._locked
|
||||
|
||||
def handleMessage(self):
|
||||
socket = self._server.nextPendingConnection()
|
||||
if socket.waitForReadyRead(self._timeout):
|
||||
self.messageFromOtherInstance.emit(socket.readAll().data())
|
||||
|
||||
def sendMessage(self, message):
|
||||
socket = QtNetwork.QLocalSocket(self)
|
||||
socket.connectToServer(self._key, QtCore.QIODevice.WriteOnly)
|
||||
socket.waitForConnected(self._timeout)
|
||||
socket.write(bytes(message, 'UTF-8'))
|
||||
socket.waitForBytesWritten(self._timeout)
|
||||
socket.disconnectFromServer()
|
||||
|
||||
|
||||
class QMainWindowKCC(QtWidgets.QMainWindow):
|
||||
progressBarTick = QtCore.pyqtSignal(str)
|
||||
modeConvert = QtCore.pyqtSignal(int)
|
||||
addMessage = QtCore.pyqtSignal(str, str, bool)
|
||||
addTrayMessage = QtCore.pyqtSignal(str, str)
|
||||
showDialog = QtCore.pyqtSignal(str, str)
|
||||
hideProgressBar = QtCore.pyqtSignal()
|
||||
forceShutdown = QtCore.pyqtSignal()
|
||||
dialogAnswer = QtCore.pyqtSignal(int)
|
||||
|
||||
|
||||
class Icons:
|
||||
def __init__(self):
|
||||
self.deviceKindle = QtGui.QIcon()
|
||||
@@ -78,19 +133,6 @@ class Icons:
|
||||
self.programIcon.addPixmap(QtGui.QPixmap(":/Icon/icons/comic2ebook.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)
|
||||
|
||||
|
||||
class WebServerHandler(BaseHTTPRequestHandler):
|
||||
# noinspection PyAttributeOutsideInit, PyArgumentList
|
||||
def do_GET(self):
|
||||
@@ -113,15 +155,15 @@ class WebServerHandler(BaseHTTPRequestHandler):
|
||||
self.send_header('Content-type', 'text/html')
|
||||
self.end_headers()
|
||||
self.wfile.write(bytes('<!DOCTYPE html>\n'
|
||||
'<html lang="en">\n'
|
||||
'<head><meta charset="utf-8">\n'
|
||||
'<link href="' + GUI.webContent.favicon + '" rel="icon" type="image/x-icon" />\n'
|
||||
'<title>Kindle Comic Converter</title>\n'
|
||||
'</head>\n'
|
||||
'<body>\n'
|
||||
'<div style="text-align: center; font-size:25px">\n'
|
||||
'<p style="font-size:50px">- <img style="vertical-align: middle" '
|
||||
'alt="KCC Logo" src="' + GUI.webContent.logo + '" /> -</p>\n', 'UTF-8'))
|
||||
'<html lang="en">\n'
|
||||
'<head><meta charset="utf-8">\n'
|
||||
'<link href="' + GUI.webContent.favicon + '" rel="icon" type="image/x-icon" />\n'
|
||||
'<title>Kindle Comic Converter</title>\n'
|
||||
'</head>\n'
|
||||
'<body>\n'
|
||||
'<div style="text-align: center; font-size:25px">\n'
|
||||
'<p style="font-size:50px">- <img style="vertical-align: middle" '
|
||||
'alt="KCC Logo" src="' + GUI.webContent.logo + '" /> -</p>\n', 'UTF-8'))
|
||||
if len(GUI.completedWork) > 0 and not GUI.conversionAlive:
|
||||
for key in sorted(GUI.completedWork.keys()):
|
||||
self.wfile.write(bytes('<p><a href="' + key + '">' + key.split('.')[0] + '</a></p>\n', 'UTF-8'))
|
||||
@@ -129,8 +171,8 @@ class WebServerHandler(BaseHTTPRequestHandler):
|
||||
self.wfile.write(bytes('<p style="font-weight: bold">No downloads are available.<br/>'
|
||||
'Convert some files and refresh this page.</p>\n', 'UTF-8'))
|
||||
self.wfile.write(bytes('</div>\n'
|
||||
'</body>\n'
|
||||
'</html>\n', 'UTF-8'))
|
||||
'</body>\n'
|
||||
'</html>\n', 'UTF-8'))
|
||||
elif sendReply:
|
||||
outputFile = GUI.completedWork[unquote(self.path[1:])]
|
||||
fp = open(outputFile, 'rb')
|
||||
@@ -265,84 +307,14 @@ class ProgressThread(QtCore.QThread):
|
||||
self.running = False
|
||||
|
||||
|
||||
class WorkerSignals(QtCore.QObject):
|
||||
result = QtCore.pyqtSignal(list)
|
||||
|
||||
|
||||
class KindleGenThread(QtCore.QRunnable):
|
||||
def __init__(self, batch):
|
||||
super(KindleGenThread, self).__init__()
|
||||
self.signals = WorkerSignals()
|
||||
self.work = batch
|
||||
|
||||
def run(self):
|
||||
kindlegenErrorCode = 0
|
||||
kindlegenError = ''
|
||||
try:
|
||||
if os.path.getsize(self.work) < 629145600:
|
||||
output = Popen('kindlegen -dont_append_source -locale en "' + self.work + '"', stdout=PIPE,
|
||||
stderr=STDOUT, shell=True)
|
||||
for line in output.stdout:
|
||||
line = line.decode('utf-8')
|
||||
# ERROR: Generic error
|
||||
if "Error(" in line:
|
||||
kindlegenErrorCode = 1
|
||||
kindlegenError = line
|
||||
# ERROR: EPUB too big
|
||||
if ":E23026:" in line:
|
||||
kindlegenErrorCode = 23026
|
||||
if kindlegenErrorCode > 0:
|
||||
break
|
||||
else:
|
||||
# ERROR: EPUB too big
|
||||
kindlegenErrorCode = 23026
|
||||
self.signals.result.emit([kindlegenErrorCode, kindlegenError, self.work])
|
||||
except Exception as err:
|
||||
# ERROR: KCC unknown generic error
|
||||
kindlegenErrorCode = 1
|
||||
kindlegenError = format(err)
|
||||
self.signals.result.emit([kindlegenErrorCode, kindlegenError, self.work])
|
||||
|
||||
|
||||
class DualMetaFixThread(QtCore.QRunnable):
|
||||
def __init__(self, batch):
|
||||
super(DualMetaFixThread, self).__init__()
|
||||
self.signals = WorkerSignals()
|
||||
self.work = batch
|
||||
|
||||
def run(self):
|
||||
item = self.work
|
||||
os.remove(item)
|
||||
mobiPath = item.replace('.epub', '.mobi')
|
||||
move(mobiPath, mobiPath + '_toclean')
|
||||
try:
|
||||
# noinspection PyArgumentList
|
||||
dualmetafix.DualMobiMetaFix(mobiPath + '_toclean', mobiPath, bytes(str(uuid4()), 'UTF-8'))
|
||||
self.signals.result.emit([True])
|
||||
except Exception as err:
|
||||
self.signals.result.emit([False, format(err)])
|
||||
|
||||
|
||||
class WorkerThread(QtCore.QThread):
|
||||
#noinspection PyArgumentList
|
||||
# noinspection PyArgumentList
|
||||
def __init__(self):
|
||||
QtCore.QThread.__init__(self)
|
||||
self.pool = QtCore.QThreadPool()
|
||||
self.conversionAlive = False
|
||||
self.errors = False
|
||||
self.kindlegenErrorCode = [0]
|
||||
self.workerOutput = []
|
||||
# Let's make sure that we don't fill the memory
|
||||
availableMemory = virtual_memory().total/1000000000
|
||||
if availableMemory <= 2:
|
||||
self.threadNumber = 1
|
||||
elif 2 < availableMemory <= 4:
|
||||
self.threadNumber = 2
|
||||
else:
|
||||
self.threadNumber = 4
|
||||
# Let's make sure that we don't use too many threads
|
||||
if self.threadNumber > QtCore.QThread.idealThreadCount():
|
||||
self.threadNumber = QtCore.QThread.idealThreadCount()
|
||||
self.progressBarTick = MW.progressBarTick
|
||||
self.addMessage = MW.addMessage
|
||||
|
||||
@@ -361,9 +333,11 @@ class WorkerThread(QtCore.QThread):
|
||||
MW.addTrayMessage.emit('Conversion interrupted.', 'Critical')
|
||||
MW.modeConvert.emit(1)
|
||||
|
||||
def addResult(self, output):
|
||||
MW.progressBarTick.emit('tick')
|
||||
self.workerOutput.append(output)
|
||||
def sanitizeTrace(self, traceback):
|
||||
return ''.join(format_tb(traceback))\
|
||||
.replace('C:\\Users\\AcidWeb\\Documents\\Projekty\\KCC\\', '')\
|
||||
.replace('C:\\Python34\\', '')\
|
||||
.replace('C:\\Python34_64\\', '')
|
||||
|
||||
def run(self):
|
||||
MW.modeConvert.emit(0)
|
||||
@@ -385,10 +359,9 @@ class WorkerThread(QtCore.QThread):
|
||||
options.quality = 1
|
||||
elif GUI.QualityBox.checkState() == 2:
|
||||
options.quality = 2
|
||||
if str(GUI.FormatBox.currentText()) == 'CBZ':
|
||||
options.cbzoutput = True
|
||||
options.format = str(GUI.FormatBox.currentText())
|
||||
if GUI.currentMode == 1:
|
||||
if profile in ['KFHD', 'KFHD8', 'KFHDX', 'KFHDX8']:
|
||||
if 'KFH' in profile:
|
||||
options.upscale = True
|
||||
|
||||
# Advanced mode settings
|
||||
@@ -410,10 +383,7 @@ class WorkerThread(QtCore.QThread):
|
||||
if GUI.WebtoonBox.isChecked():
|
||||
options.webtoon = True
|
||||
if float(GUI.GammaValue) > 0.09:
|
||||
# noinspection PyTypeChecker
|
||||
options.gamma = float(GUI.GammaValue)
|
||||
if str(GUI.FormatBox.currentText()) == 'MOBI':
|
||||
options.batchsplit = True
|
||||
|
||||
# Other/custom settings.
|
||||
if GUI.currentMode > 2:
|
||||
@@ -462,7 +432,7 @@ class WorkerThread(QtCore.QThread):
|
||||
self.errors = True
|
||||
_, _, traceback = sys.exc_info()
|
||||
MW.showDialog.emit("Error during conversion %s:\n\n%s\n\nTraceback:\n%s"
|
||||
% (jobargv[-1], str(err), "".join(format_tb(traceback))), 'error')
|
||||
% (jobargv[-1], str(err), self.sanitizeTrace(traceback)), 'error')
|
||||
MW.addMessage.emit('Failed to create EPUB!', 'error', False)
|
||||
MW.addTrayMessage.emit('Failed to create EPUB!', 'Critical')
|
||||
if not self.conversionAlive:
|
||||
@@ -483,16 +453,10 @@ class WorkerThread(QtCore.QThread):
|
||||
MW.progressBarTick.emit('tick')
|
||||
MW.addMessage.emit('Creating MOBI files', 'info', False)
|
||||
GUI.progress.content = 'Creating MOBI files'
|
||||
self.workerOutput = []
|
||||
# Number of KindleGen threads depends on the size of RAM
|
||||
self.pool.setMaxThreadCount(self.threadNumber)
|
||||
work = []
|
||||
for item in outputPath:
|
||||
worker = KindleGenThread(item)
|
||||
worker.signals.result.connect(self.addResult)
|
||||
self.pool.start(worker)
|
||||
self.pool.waitForDone()
|
||||
while len(self.workerOutput) != len(outputPath):
|
||||
sleep(0.1)
|
||||
work.append([item])
|
||||
self.workerOutput = comic2ebook.makeMOBI(work, self)
|
||||
self.kindlegenErrorCode = [0]
|
||||
for errors in self.workerOutput:
|
||||
if errors[0] != 0:
|
||||
@@ -512,15 +476,9 @@ class WorkerThread(QtCore.QThread):
|
||||
MW.addMessage.emit('Processing MOBI files', 'info', False)
|
||||
GUI.progress.content = 'Processing MOBI files'
|
||||
self.workerOutput = []
|
||||
# DualMetaFix is very fast and there is not reason to use multithreading.
|
||||
self.pool.setMaxThreadCount(1)
|
||||
for item in outputPath:
|
||||
worker = DualMetaFixThread(item)
|
||||
worker.signals.result.connect(self.addResult)
|
||||
self.pool.start(worker)
|
||||
self.pool.waitForDone()
|
||||
while len(self.workerOutput) != len(outputPath):
|
||||
sleep(0.1)
|
||||
self.workerOutput.append(comic2ebook.makeMOBIFix(item))
|
||||
MW.progressBarTick.emit('tick')
|
||||
for success in self.workerOutput:
|
||||
if not success[0]:
|
||||
self.errors = True
|
||||
@@ -611,6 +569,7 @@ class KCCGUI(KCC_ui.Ui_KCC):
|
||||
dname = dname.replace('/', '\\')
|
||||
self.lastPath = os.path.abspath(os.path.join(dname, os.pardir))
|
||||
GUI.JobList.addItem(dname)
|
||||
GUI.JobList.scrollToBottom()
|
||||
|
||||
def selectFile(self):
|
||||
if self.needClean:
|
||||
@@ -636,6 +595,7 @@ class KCCGUI(KCC_ui.Ui_KCC):
|
||||
fname = fname.replace('/', '\\')
|
||||
self.lastPath = os.path.abspath(os.path.join(fname, os.pardir))
|
||||
GUI.JobList.addItem(fname)
|
||||
GUI.JobList.scrollToBottom()
|
||||
|
||||
def clearJobs(self):
|
||||
GUI.JobList.clear()
|
||||
@@ -971,15 +931,19 @@ class KCCGUI(KCC_ui.Ui_KCC):
|
||||
self.addMessage('Target resolution is not set!', 'error')
|
||||
self.needClean = True
|
||||
return
|
||||
if str(GUI.FormatBox.currentText()) == 'MOBI' and not GUI.KindleGen:
|
||||
self.addMessage('Cannot find <a href="http://www.amazon.com/gp/feature.html?ie=UTF8&docId=1000765211">'
|
||||
'<b>KindleGen</b></a>! MOBI conversion is not possible!', 'error')
|
||||
if sys.platform.startswith('win'):
|
||||
self.addMessage('Download it and place EXE in KCC directory.', 'error')
|
||||
else:
|
||||
self.addMessage('Download it, and place executable in /usr/local/bin directory.', 'error')
|
||||
self.needClean = True
|
||||
return
|
||||
if str(GUI.FormatBox.currentText()) == 'MOBI' and not self.KindleGen:
|
||||
self.detectKindleGen()
|
||||
if not self.KindleGen:
|
||||
GUI.JobList.clear()
|
||||
self.addMessage('Cannot find <a href="http://www.amazon.com/gp/feature.html'
|
||||
'?ie=UTF8&docId=1000765211"><b>KindleGen</b></a>!'
|
||||
' MOBI conversion is unavailable!', 'error')
|
||||
if sys.platform.startswith('win'):
|
||||
self.addMessage('Download it and place EXE in KCC directory.', 'error')
|
||||
else:
|
||||
self.addMessage('Download it and place executable in /usr/local/bin directory.', 'error')
|
||||
self.needClean = True
|
||||
return
|
||||
self.worker.start()
|
||||
|
||||
def hideProgressBar(self):
|
||||
@@ -1018,7 +982,6 @@ class KCCGUI(KCC_ui.Ui_KCC):
|
||||
'GammaSlider': float(self.GammaValue)*100})
|
||||
self.settings.sync()
|
||||
self.tray.hide()
|
||||
APP.shutdown()
|
||||
|
||||
def handleMessage(self, message):
|
||||
MW.raise_()
|
||||
@@ -1041,10 +1004,12 @@ class KCCGUI(KCC_ui.Ui_KCC):
|
||||
formats = ['.cbz', '.zip', '.pdf']
|
||||
if os.path.isdir(message):
|
||||
GUI.JobList.addItem(message)
|
||||
GUI.JobList.scrollToBottom()
|
||||
elif os.path.isfile(message):
|
||||
extension = os.path.splitext(message)
|
||||
if extension[1].lower() in formats:
|
||||
GUI.JobList.addItem(message)
|
||||
GUI.JobList.scrollToBottom()
|
||||
else:
|
||||
self.addMessage('This file type is unsupported!', 'error')
|
||||
|
||||
@@ -1066,6 +1031,36 @@ class KCCGUI(KCC_ui.Ui_KCC):
|
||||
self.saveSettings(None)
|
||||
sys.exit(0)
|
||||
|
||||
def detectKindleGen(self, startup=False):
|
||||
if not sys.platform.startswith('win'):
|
||||
try:
|
||||
os.chmod('/usr/local/bin/kindlegen', 0o755)
|
||||
except Exception:
|
||||
pass
|
||||
kindleGenExitCode = Popen('kindlegen -locale en', stdout=PIPE, stderr=STDOUT, shell=True)
|
||||
if kindleGenExitCode.wait() == 0:
|
||||
self.KindleGen = True
|
||||
versionCheck = Popen('kindlegen -locale en', stdout=PIPE, stderr=STDOUT, shell=True)
|
||||
for line in versionCheck.stdout:
|
||||
line = line.decode("utf-8")
|
||||
if 'Amazon kindlegen' in line:
|
||||
versionCheck = line.split('V')[1].split(' ')[0]
|
||||
if tuple(map(int, (versionCheck.split(".")))) < tuple(map(int, ('2.9'.split(".")))):
|
||||
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
|
||||
if startup:
|
||||
self.addMessage('Cannot find <a href="http://www.amazon.com/gp/feature.html?ie=UTF8&docId=1000765211">'
|
||||
'<b>KindleGen</b></a>! MOBI conversion will be unavailable!', 'error')
|
||||
if sys.platform.startswith('win'):
|
||||
self.addMessage('Download it and place EXE in KCC directory.', 'error')
|
||||
else:
|
||||
self.addMessage('Download it and place executable in /usr/local/bin directory.', 'error')
|
||||
|
||||
# noinspection PyArgumentList
|
||||
def __init__(self, KCCAplication, KCCWindow):
|
||||
global APP, MW, GUI
|
||||
@@ -1096,6 +1091,7 @@ class KCCGUI(KCC_ui.Ui_KCC):
|
||||
self.tray = SystemTrayIcon()
|
||||
self.conversionAlive = False
|
||||
self.needClean = True
|
||||
self.KindleGen = False
|
||||
self.GammaValue = 1.0
|
||||
self.completedWork = {}
|
||||
self.targetDirectory = ''
|
||||
@@ -1103,11 +1099,13 @@ class KCCGUI(KCC_ui.Ui_KCC):
|
||||
self.listFontSize = 11
|
||||
self.statusBarFontSize = 10
|
||||
self.statusBarStyle = 'QLabel{padding-top:2px;padding-bottom:3px;}'
|
||||
self.ProgressBar.setStyleSheet('QProgressBar{padding-top:5px;text-align:center;}')
|
||||
self.ProgressBar.setStyleSheet('QProgressBar{font-size:13px;text-align:center;'
|
||||
'border:2px solid grey;border-radius:5px;}'
|
||||
'QProgressBar::chunk{background-color:steelblue;width:20px;}')
|
||||
elif sys.platform.startswith('linux'):
|
||||
self.listFontSize = 8
|
||||
self.statusBarFontSize = 8
|
||||
self.statusBarStyle = 'QLabel{padding-top:5px;padding-bottom:3px;}'
|
||||
self.statusBarStyle = 'QLabel{padding-top:3px;padding-bottom:3px;}'
|
||||
self.statusBar.setStyleSheet('QStatusBar::item{border:0px;border-top:2px solid #C2C7CB;}')
|
||||
else:
|
||||
self.listFontSize = 9
|
||||
@@ -1121,22 +1119,20 @@ 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': 'KHD'},
|
||||
'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'},
|
||||
"Kindle Fire": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0,
|
||||
'DefaultUpscale': False, 'Label': 'KF'},
|
||||
"K. Fire HD 7\"": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0,
|
||||
'DefaultUpscale': True, 'Label': 'KFHD'},
|
||||
"K. Fire HD 8.9\"": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0,
|
||||
'DefaultUpscale': True, 'Label': 'KFHD8'},
|
||||
"K. Fire HDX 7\"": {'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'},
|
||||
"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,
|
||||
'DefaultUpscale': False, 'Label': 'KoMT'},
|
||||
"Kobo Glow": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 2,
|
||||
@@ -1145,6 +1141,8 @@ class KCCGUI(KCC_ui.Ui_KCC):
|
||||
'DefaultUpscale': False, 'Label': 'KoA'},
|
||||
"Kobo Aura HD": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 2,
|
||||
'DefaultUpscale': False, 'Label': 'KoAHD'},
|
||||
"Kobo Aura H2O": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 2,
|
||||
'DefaultUpscale': False, 'Label': 'KoAH2O'},
|
||||
"Other": {'Quality': False, 'ForceExpert': True, 'DefaultFormat': 1,
|
||||
'DefaultUpscale': False, 'Label': 'OTHER'},
|
||||
"Kindle for Android": {'Quality': False, 'ForceExpert': True, 'DefaultFormat': 0,
|
||||
@@ -1155,26 +1153,26 @@ class KCCGUI(KCC_ui.Ui_KCC):
|
||||
'DefaultUpscale': False, 'Label': 'K2'}
|
||||
}
|
||||
profilesGUI = [
|
||||
"Kindle Voyage",
|
||||
"Kindle Paperwhite",
|
||||
"Kindle",
|
||||
"Kindle DX/DXG",
|
||||
"Separator",
|
||||
"Kindle Fire",
|
||||
"K. Fire HD 7\"",
|
||||
"K. Fire HD 8.9\"",
|
||||
"K. Fire HDX 7\"",
|
||||
"K. Fire HDX 8.9\"",
|
||||
"K. Fire HD",
|
||||
"K. Fire HDX",
|
||||
"K. Fire HDX 8.9",
|
||||
"Separator",
|
||||
"Kobo Mini/Touch",
|
||||
"Kobo Glow",
|
||||
"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.'
|
||||
@@ -1195,27 +1193,6 @@ class KCCGUI(KCC_ui.Ui_KCC):
|
||||
self.addMessage('Since you are new user of <b>KCC</b> please see few '
|
||||
'<a href="https://github.com/ciromattia/kcc/wiki/Important-tips">important tips</a>.',
|
||||
'info')
|
||||
if not sys.platform.startswith('win'):
|
||||
try:
|
||||
os.chmod('/usr/local/bin/kindlegen', 0o755)
|
||||
except Exception:
|
||||
pass
|
||||
kindleGenExitCode = Popen('kindlegen -locale en', stdout=PIPE, stderr=STDOUT, shell=True)
|
||||
if kindleGenExitCode.wait() == 0:
|
||||
self.KindleGen = True
|
||||
versionCheck = Popen('kindlegen -locale en', stdout=PIPE, stderr=STDOUT, shell=True)
|
||||
for line in versionCheck.stdout:
|
||||
line = line.decode("utf-8")
|
||||
if 'Amazon kindlegen' in line:
|
||||
versionCheck = line.split('V')[1].split(' ')[0]
|
||||
if tuple(map(int, (versionCheck.split(".")))) < tuple(map(int, ('2.9'.split(".")))):
|
||||
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
|
||||
rarExitCode = Popen('unrar', stdout=PIPE, stderr=STDOUT, shell=True)
|
||||
rarExitCode = rarExitCode.wait()
|
||||
if rarExitCode == 0 or rarExitCode == 7:
|
||||
@@ -1232,6 +1209,7 @@ class KCCGUI(KCC_ui.Ui_KCC):
|
||||
self.sevenza = False
|
||||
self.addMessage('Cannot find <a href="http://www.7-zip.org/download.html">7za</a>!'
|
||||
' Processing of CB7/7Z files will be disabled.', 'warning')
|
||||
self.detectKindleGen(True)
|
||||
|
||||
APP.messageFromOtherInstance.connect(self.handleMessage)
|
||||
GUI.BasicModeButton.clicked.connect(self.modeBasic)
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
# Form implementation generated from reading ui file 'KCC.ui'
|
||||
#
|
||||
# Created: Sun May 18 09:08:27 2014
|
||||
# by: PyQt5 UI code generator 5.2.1
|
||||
# Created: Sun Jan 4 09:58:25 2015
|
||||
# by: PyQt5 UI code generator 5.4
|
||||
#
|
||||
# WARNING! All changes made in this file will be lost!
|
||||
|
||||
@@ -139,6 +139,7 @@ class Ui_KCC(object):
|
||||
self.JobList.setGeometry(QtCore.QRect(10, 50, 401, 101))
|
||||
self.JobList.setFocusPolicy(QtCore.Qt.NoFocus)
|
||||
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.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
|
||||
self.JobList.setProperty("showDropIndicator", False)
|
||||
self.JobList.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection)
|
||||
self.JobList.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel)
|
||||
@@ -260,28 +261,30 @@ class Ui_KCC(object):
|
||||
def retranslateUi(self, KCC):
|
||||
_translate = QtCore.QCoreApplication.translate
|
||||
KCC.setWindowTitle(_translate("KCC", "Kindle Comic Converter"))
|
||||
self.ProcessingBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Disable image optimizations.<br/>Input images must be already resized.</p></body></html>"))
|
||||
self.ProcessingBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Disable image optimizations.<br/><span style=\" font-weight:600;\">Input images must be already resized.</span></p></body></html>"))
|
||||
self.ProcessingBox.setText(_translate("KCC", "No optimisation"))
|
||||
self.UpscaleBox.setToolTip(_translate("KCC", "<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Nothing<br/></span>Images smaller than device resolution will not be resized.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - Stretching<br/></span>Images smaller than device resolution will be resized. Aspect ratio will be not preserved.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Upscaling<br/></span>Images smaller than device resolution will be resized. Aspect ratio will be preserved.</p></body></html>"))
|
||||
self.UpscaleBox.setText(_translate("KCC", "Stretch/Upscale"))
|
||||
self.WebtoonBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\"white-space:pre\">Enable auto-splitting of webtoons like <span style=\" font-style:italic;\">Tower of God</span> or <span style=\" font-style:italic;\">Noblesse</span>.<br/>This mode is created for pages with a low width, high height and vertical panel flow.</p></body></html>"))
|
||||
self.WebtoonBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Enable special parsing mode for WebToons.</p></body></html>"))
|
||||
self.WebtoonBox.setText(_translate("KCC", "Webtoon mode"))
|
||||
self.NoDitheringBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Create PNG files instead JPEG.<br/>Quality increase is not noticeable on most of devices.<br/>Output files <span style=\" font-weight:600;\">might</span> be smaller.<br/><span style=\" font-weight:600;\">MOBI conversion will be much slower.</span></p></body></html>"))
|
||||
self.NoDitheringBox.setText(_translate("KCC", "PNG output"))
|
||||
self.BorderBox.setToolTip(_translate("KCC", "<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Autodetection<br/></span>Color of margins fill will be detected automatically.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - White<br/></span>Margins will be filled with white color.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Black<br/></span>Margins will be filled with black color.</p></body></html>"))
|
||||
self.BorderBox.setText(_translate("KCC", "W/B margins"))
|
||||
self.NoRotateBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Disable splitting and rotation.</p></body></html>"))
|
||||
self.NoRotateBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Disable page splitting and rotation.</p></body></html>"))
|
||||
self.NoRotateBox.setText(_translate("KCC", "No split/rotate"))
|
||||
self.DeviceBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Target device.</p></body></html>"))
|
||||
self.FormatBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Output format.</p></body></html>"))
|
||||
self.ConvertButton.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Shift+Click to select the output directory.</p></body></html>"))
|
||||
self.ConvertButton.setText(_translate("KCC", "Convert"))
|
||||
self.DirectoryButton.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Add directory containing JPG, PNG or GIF files to queue.<br/><span style=\" font-weight:600;\">CBR, CBZ and CB7 files inside will not be processed!</span></p></body></html>"))
|
||||
self.DirectoryButton.setText(_translate("KCC", "Add directory"))
|
||||
self.FileButton.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Add CBR, CBZ, CB7 or PDF file to queue.</p></body></html>"))
|
||||
self.FileButton.setText(_translate("KCC", "Add file"))
|
||||
self.ClearButton.setText(_translate("KCC", "Clear list"))
|
||||
self.MangaBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Enable right-to-left reading.</p></body></html>"))
|
||||
self.MangaBox.setText(_translate("KCC", "Manga mode"))
|
||||
self.QualityBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\"white-space:pre\"><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Normal quality mode<br/></span>Maximal quality of images but very poor magnification quality.<br/>Use it only when zoom is not needed or output files needs to be small.</p><p style=\"white-space:pre\"><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - High quality mode<br/></span>In most cases high quality of images and magnification.<br/>Overall quality highly depends on the resolution of source files.<br/>On Kindle models older than Paperwhite non-zoomed images might be a little blurred.</p><p style=\"white-space:pre\"><span style=\" font-weight:600; text-decoration: underline;\">Checked - Ultra quality mode<br/></span>Highest possible quality. Output files will be big.</p></body></html>"))
|
||||
self.QualityBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Quality of Panel View/zoom. Highly impact size of output file.<br/><span style=\" font-weight:600;\">This option control only quality of magnification!</span></p></body></html>"))
|
||||
self.QualityBox.setText(_translate("KCC", "High/Ultra quality"))
|
||||
self.RotateBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Disable splitting of two-page spreads.<br/>They will be rotated instead.</p></body></html>"))
|
||||
self.RotateBox.setText(_translate("KCC", "Horizontal mode"))
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
# Form implementation generated from reading ui file 'KCC-Linux.ui'
|
||||
#
|
||||
# Created: Sun May 18 09:08:37 2014
|
||||
# by: PyQt5 UI code generator 5.2.1
|
||||
# Created: Sun Jan 4 10:06:14 2015
|
||||
# by: PyQt5 UI code generator 5.4
|
||||
#
|
||||
# WARNING! All changes made in this file will be lost!
|
||||
|
||||
@@ -179,6 +179,7 @@ class Ui_KCC(object):
|
||||
self.JobList.setFont(font)
|
||||
self.JobList.setFocusPolicy(QtCore.Qt.NoFocus)
|
||||
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.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
|
||||
self.JobList.setProperty("showDropIndicator", False)
|
||||
self.JobList.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection)
|
||||
self.JobList.setIconSize(QtCore.QSize(18, 18))
|
||||
@@ -329,28 +330,30 @@ class Ui_KCC(object):
|
||||
def retranslateUi(self, KCC):
|
||||
_translate = QtCore.QCoreApplication.translate
|
||||
KCC.setWindowTitle(_translate("KCC", "Kindle Comic Converter"))
|
||||
self.ProcessingBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Disable image optimizations.<br/>Input images must be already resized.</p></body></html>"))
|
||||
self.ProcessingBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Disable image optimizations.<br/><span style=\" font-weight:600;\">Input images must be already resized.</span></p></body></html>"))
|
||||
self.ProcessingBox.setText(_translate("KCC", "No optimisation"))
|
||||
self.UpscaleBox.setToolTip(_translate("KCC", "<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Nothing<br/></span>Images smaller than device resolution will not be resized.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - Stretching<br/></span>Images smaller than device resolution will be resized. Aspect ratio will be not preserved.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Upscaling<br/></span>Images smaller than device resolution will be resized. Aspect ratio will be preserved.</p></body></html>"))
|
||||
self.UpscaleBox.setText(_translate("KCC", "Stretch/Upscale"))
|
||||
self.WebtoonBox.setToolTip(_translate("KCC", "<html><head/><body><p>Enable auto-splitting of webtoons like <span style=\" font-style:italic;\">Tower of God</span> or <span style=\" font-style:italic;\">Noblesse</span>.<br/>This mode was created for pages with a low width, high height and vertical panel flow.</p></body></html>"))
|
||||
self.WebtoonBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Enable special parsing mode for WebToons.</p></body></html>"))
|
||||
self.WebtoonBox.setText(_translate("KCC", "Webtoon mode"))
|
||||
self.NoDitheringBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Create PNG files instead JPEG.<br/>Quality increase is not noticeable on most of devices.<br/>Output files <span style=\" font-weight:600;\">might</span> be smaller.<br/><span style=\" font-weight:600;\">MOBI conversion will be much slower.</span></p></body></html>"))
|
||||
self.NoDitheringBox.setText(_translate("KCC", "PNG output"))
|
||||
self.BorderBox.setToolTip(_translate("KCC", "<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Autodetection<br/></span>Color of margins fill will be detected automatically.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - White<br/></span>Margins will be filled with white color.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Black<br/></span>Margins will be filled with black color.</p></body></html>"))
|
||||
self.BorderBox.setText(_translate("KCC", "W/B margins"))
|
||||
self.NoRotateBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Disable splitting and rotation.</p></body></html>"))
|
||||
self.NoRotateBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Disable page splitting and rotation.</p></body></html>"))
|
||||
self.NoRotateBox.setText(_translate("KCC", "No split/rotate"))
|
||||
self.DeviceBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Target device.</p></body></html>"))
|
||||
self.FormatBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Output format.</p></body></html>"))
|
||||
self.ConvertButton.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Shift+Click to select the output directory.</p></body></html>"))
|
||||
self.ConvertButton.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Shift+Click to select the output directory.</p></body></html>"))
|
||||
self.ConvertButton.setText(_translate("KCC", "Convert"))
|
||||
self.DirectoryButton.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Add directory containing JPG, PNG or GIF files to queue.<br/><span style=\" font-weight:600;\">CBR, CBZ and CB7 files inside will not be processed!</span></p></body></html>"))
|
||||
self.DirectoryButton.setText(_translate("KCC", "Add directory"))
|
||||
self.FileButton.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Add CBR, CBZ, CB7 or PDF file to queue.</p></body></html>"))
|
||||
self.FileButton.setText(_translate("KCC", "Add file"))
|
||||
self.ClearButton.setText(_translate("KCC", "Clear list"))
|
||||
self.MangaBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Enable right-to-left reading.</p></body></html>"))
|
||||
self.MangaBox.setText(_translate("KCC", "Manga mode"))
|
||||
self.QualityBox.setToolTip(_translate("KCC", "<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Normal quality mode</span><br/>Maximal quality of images but very poor magnification quality.<br/>Use it only when zoom is not needed or output files needs to be small.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - High quality mode</span><br/>In most cases high quality of images and magnification.<br/>Overall quality highly depends on the resolution of source files.<br/>On Kindle models older than Paperwhite non-zoomed images might be a little blurred.<br/><br/><span style=\" font-weight:600; text-decoration: underline;\">Checked - Ultra quality mode</span><br/>Highest possible quality. Output files will be big.</p></body></html>"))
|
||||
self.QualityBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Quality of Panel View/zoom. Highly impact size of output file.<br/><span style=\" font-weight:600;\">This option control only quality of magnification!</span></p></body></html>"))
|
||||
self.QualityBox.setText(_translate("KCC", "High/Ultra quality"))
|
||||
self.RotateBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Disable splitting of two-page spreads.<br/>They will be rotated instead.</p></body></html>"))
|
||||
self.RotateBox.setText(_translate("KCC", "Horizontal mode"))
|
||||
@@ -359,13 +362,13 @@ class Ui_KCC(object):
|
||||
self.GammaLabel.setText(_translate("KCC", "Gamma: Auto"))
|
||||
self.ColorBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Don\'t convert images to grayscale.</p></body></html>"))
|
||||
self.ColorBox.setText(_translate("KCC", "Color mode"))
|
||||
self.wLabel.setToolTip(_translate("KCC", "<html><head/><body><p>Resolution of target device.</p></body></html>"))
|
||||
self.wLabel.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Resolution of target device.</p></body></html>"))
|
||||
self.wLabel.setText(_translate("KCC", "Custom width: "))
|
||||
self.customWidth.setToolTip(_translate("KCC", "<html><head/><body><p>Resolution of target device.</p></body></html>"))
|
||||
self.customWidth.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Resolution of target device.</p></body></html>"))
|
||||
self.customWidth.setInputMask(_translate("KCC", "0000"))
|
||||
self.hLabel.setToolTip(_translate("KCC", "<html><head/><body><p>Resolution of target device.</p></body></html>"))
|
||||
self.hLabel.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Resolution of target device.</p></body></html>"))
|
||||
self.hLabel.setText(_translate("KCC", "Custom height: "))
|
||||
self.customHeight.setToolTip(_translate("KCC", "<html><head/><body><p>Resolution of target device.</p></body></html>"))
|
||||
self.customHeight.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Resolution of target device.</p></body></html>"))
|
||||
self.customHeight.setInputMask(_translate("KCC", "0000"))
|
||||
self.ActionBasic.setText(_translate("KCC", "Basic"))
|
||||
self.ActionAdvanced.setText(_translate("KCC", "Advanced"))
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
# Form implementation generated from reading ui file 'KCC-OSX.ui'
|
||||
#
|
||||
# Created: Sun May 18 09:08:44 2014
|
||||
# by: PyQt5 UI code generator 5.2.1
|
||||
# Created: Sun Jan 4 10:26:09 2015
|
||||
# by: PyQt5 UI code generator 5.4
|
||||
#
|
||||
# WARNING! All changes made in this file will be lost!
|
||||
|
||||
@@ -186,6 +186,7 @@ class Ui_KCC(object):
|
||||
self.JobList.setFont(font)
|
||||
self.JobList.setFocusPolicy(QtCore.Qt.NoFocus)
|
||||
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.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
|
||||
self.JobList.setProperty("showDropIndicator", False)
|
||||
self.JobList.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection)
|
||||
self.JobList.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel)
|
||||
@@ -352,28 +353,30 @@ class Ui_KCC(object):
|
||||
def retranslateUi(self, KCC):
|
||||
_translate = QtCore.QCoreApplication.translate
|
||||
KCC.setWindowTitle(_translate("KCC", "Kindle Comic Converter"))
|
||||
self.ProcessingBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Disable image optimizations.<br/>Input images must be already resized.</p></body></html>"))
|
||||
self.ProcessingBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Disable image optimizations.<br/><span style=\" font-weight:600;\">Input images must be already resized.</span></p></body></html>"))
|
||||
self.ProcessingBox.setText(_translate("KCC", "No optimisation"))
|
||||
self.UpscaleBox.setToolTip(_translate("KCC", "<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Nothing<br/></span>Images smaller than device resolution will not be resized.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - Stretching<br/></span>Images smaller than device resolution will be resized. Aspect ratio will be not preserved.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Upscaling<br/></span>Images smaller than device resolution will be resized. Aspect ratio will be preserved.</p></body></html>"))
|
||||
self.UpscaleBox.setText(_translate("KCC", "Stretch/Upscale"))
|
||||
self.WebtoonBox.setToolTip(_translate("KCC", "<html><head/><body><p>Enable auto-splitting of webtoons like <span style=\" font-style:italic;\">Tower of God </span>or <span style=\" font-style:italic;\">Noblesse</span>.<br/>This mode was created for pages with a low width, high height and vertical panel flow.</p><p><br/></p></body></html>"))
|
||||
self.WebtoonBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Enable special parsing mode for WebToons.</p></body></html>"))
|
||||
self.WebtoonBox.setText(_translate("KCC", "Webtoon mode"))
|
||||
self.NoDitheringBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Create PNG files instead JPEG.<br/>Quality increase is not noticeable on most of devices.<br/>Output files <span style=\" font-weight:600;\">might</span> be smaller.<br/><span style=\" font-weight:600;\">MOBI conversion will be much slower.</span></p></body></html>"))
|
||||
self.NoDitheringBox.setText(_translate("KCC", "PNG output"))
|
||||
self.BorderBox.setToolTip(_translate("KCC", "<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Autodetection<br/></span>Color of margins fill will be detected automatically.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - White<br/></span>Margins will be filled with white color.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Black<br/></span>Margins will be filled with black color.</p></body></html>"))
|
||||
self.BorderBox.setText(_translate("KCC", "W/B margins"))
|
||||
self.NoRotateBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Disable splitting and rotation.</p></body></html>"))
|
||||
self.NoRotateBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Disable page splitting and rotation.</p></body></html>"))
|
||||
self.NoRotateBox.setText(_translate("KCC", "No split/rotate"))
|
||||
self.DeviceBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Target device.</p></body></html>"))
|
||||
self.FormatBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Output format.</p></body></html>"))
|
||||
self.ConvertButton.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Shift+Click to select the output directory.</p></body></html>"))
|
||||
self.FormatBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Output format.</p></body></html>"))
|
||||
self.ConvertButton.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Shift+Click to select the output directory.</p></body></html>"))
|
||||
self.ConvertButton.setText(_translate("KCC", "Convert"))
|
||||
self.DirectoryButton.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Add directory containing JPG, PNG or GIF files to queue.<br/><span style=\" font-weight:600;\">CBR, CBZ and CB7 files inside will not be processed!</span></p></body></html>"))
|
||||
self.DirectoryButton.setText(_translate("KCC", "Add directory"))
|
||||
self.FileButton.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Add CBR, CBZ, CB7 or PDF file to queue.</p></body></html>"))
|
||||
self.FileButton.setText(_translate("KCC", "Add file"))
|
||||
self.ClearButton.setText(_translate("KCC", "Clear list"))
|
||||
self.MangaBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Enable right-to-left reading.</p></body></html>"))
|
||||
self.MangaBox.setText(_translate("KCC", "Manga mode"))
|
||||
self.QualityBox.setToolTip(_translate("KCC", "<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Normal quality mode</span><br/>Maximal quality of images but very poor magnification quality.<br/>Use it only when zoom is not needed or output files needs to be small.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - High quality mode</span><br/>In most cases high quality of images and magnification.<br/>Overall quality highly depends on the resolution of source files.<br/>On Kindle models older than Paperwhite non-zoomed images might be a little blurred.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Ultra quality mode</span><br/>Highest possible quality. Output files will be big.</p></body></html>"))
|
||||
self.QualityBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Quality of Panel View/zoom. Highly impact size of output file.<br/><span style=\" font-weight:600;\">This option control only quality of magnification!</span></p></body></html>"))
|
||||
self.QualityBox.setText(_translate("KCC", "High/Ultra quality"))
|
||||
self.RotateBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Disable splitting of two-page spreads.<br/>They will be rotated instead.</p></body></html>"))
|
||||
self.RotateBox.setText(_translate("KCC", "Horizontal mode"))
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
__version__ = '4.2.1'
|
||||
__version__ = '4.4'
|
||||
__license__ = 'ISC'
|
||||
__copyright__ = '2012-2014, 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'
|
||||
@@ -1,5 +1,5 @@
|
||||
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
||||
# Copyright (c) 2013-2014 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||
# Copyright (c) 2013-2015 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||
#
|
||||
# Permission to use, copy, modify, and/or distribute this software for
|
||||
# any purpose with or without fee is hereby granted, provided that the
|
||||
@@ -17,7 +17,7 @@
|
||||
#
|
||||
|
||||
__license__ = 'ISC'
|
||||
__copyright__ = '2012-2014, 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'
|
||||
|
||||
import sys
|
||||
@@ -27,6 +27,7 @@ from subprocess import STDOUT, PIPE
|
||||
from psutil import Popen
|
||||
from shutil import move, copy
|
||||
from . import rarfile
|
||||
from .shared import check7ZFile as is_7zfile
|
||||
|
||||
|
||||
class CBxArchive:
|
||||
@@ -36,7 +37,7 @@ class CBxArchive:
|
||||
self.compressor = 'zip'
|
||||
elif rarfile.is_rarfile(origFileName):
|
||||
self.compressor = 'rar'
|
||||
elif origFileName.endswith('.7z') or origFileName.endswith('.cb7'):
|
||||
elif is_7zfile(origFileName):
|
||||
self.compressor = '7z'
|
||||
else:
|
||||
self.compressor = None
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
||||
# Copyright (c) 2013-2014 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||
# Copyright (c) 2013-2015 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||
#
|
||||
# Permission to use, copy, modify, and/or distribute this software for
|
||||
# any purpose with or without fee is hereby granted, provided that the
|
||||
@@ -18,16 +18,18 @@
|
||||
# PERFORMANCE OF THIS SOFTWARE.
|
||||
#
|
||||
|
||||
__version__ = '4.2.1'
|
||||
__version__ = '4.4'
|
||||
__license__ = 'ISC'
|
||||
__copyright__ = '2012-2014, 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'
|
||||
|
||||
import os
|
||||
import sys
|
||||
from copy import copy
|
||||
from glob import glob
|
||||
from json import loads
|
||||
from urllib.request import Request, urlopen
|
||||
from re import split, sub, compile
|
||||
from re import split, sub
|
||||
from stat import S_IWRITE, S_IREAD, S_IEXEC
|
||||
from zipfile import ZipFile, ZIP_STORED, ZIP_DEFLATED
|
||||
from tempfile import mkdtemp
|
||||
@@ -38,6 +40,8 @@ from xml.dom.minidom import parse
|
||||
from uuid import uuid4
|
||||
from slugify import slugify as slugifyExt
|
||||
from PIL import Image
|
||||
from subprocess import STDOUT, PIPE
|
||||
from psutil import Popen, virtual_memory
|
||||
try:
|
||||
from PyQt5 import QtCore
|
||||
except ImportError:
|
||||
@@ -47,17 +51,27 @@ from . import comic2panel
|
||||
from . import image
|
||||
from . import cbxarchive
|
||||
from . import pdfjpgextract
|
||||
from . import dualmetafix
|
||||
|
||||
|
||||
def main(argv=None):
|
||||
global options
|
||||
parser = makeParser()
|
||||
options, args = parser.parse_args(argv)
|
||||
checkOptions()
|
||||
if len(args) != 1:
|
||||
optionstemplate, args = parser.parse_args(argv)
|
||||
if len(args) == 0:
|
||||
parser.print_help()
|
||||
return
|
||||
outputPath = makeBook(args[0])
|
||||
sources = set([source for arg in args for source in glob(arg)])
|
||||
outputPath = []
|
||||
if len(sources) == 0:
|
||||
print('No matching files found.')
|
||||
return
|
||||
for source in sources:
|
||||
options = copy(optionstemplate)
|
||||
checkOptions()
|
||||
if len(sources) > 1:
|
||||
print('\nWorking on ' + source)
|
||||
outputPath = makeBook(source)
|
||||
return outputPath
|
||||
|
||||
|
||||
@@ -406,7 +420,7 @@ def buildEPUB(path, chapterNames, tomeNumber):
|
||||
chapter = False
|
||||
for afile in filenames:
|
||||
filename = getImageFileName(afile)
|
||||
if not '-kcc-hq' in filename[0]:
|
||||
if '-kcc-hq' not in filename[0]:
|
||||
filelist.append(buildHTML(dirpath, afile, os.path.join(dirpath, afile)))
|
||||
if not chapter:
|
||||
chapterlist.append((dirpath.replace('Images', 'Text'), filelist[-1][1]))
|
||||
@@ -430,12 +444,12 @@ def imgOptimization(img, opt, hqImage=None):
|
||||
img.cropWhiteSpace()
|
||||
if opt.cutpagenumbers and not opt.webtoon:
|
||||
img.cutPageNumber()
|
||||
img.optimizeImage(opt.gamma)
|
||||
img.optimizeImage()
|
||||
if hqImage:
|
||||
img.resizeImage(opt.upscale, opt.stretch, opt.bordersColor, 0)
|
||||
img.resizeImage(0)
|
||||
img.calculateBorder(hqImage, True)
|
||||
else:
|
||||
img.resizeImage(opt.upscale, opt.stretch, opt.bordersColor, opt.quality)
|
||||
img.resizeImage()
|
||||
if opt.panelview:
|
||||
if opt.quality == 0:
|
||||
img.calculateBorder(img)
|
||||
@@ -501,7 +515,7 @@ def imgFileProcessing(work):
|
||||
dirpath = work[1]
|
||||
opt = work[2]
|
||||
output = []
|
||||
img = image.ComicPage(os.path.join(dirpath, afile), opt.profileData)
|
||||
img = image.ComicPage(os.path.join(dirpath, afile), opt)
|
||||
if opt.quality == 2:
|
||||
wipe = False
|
||||
else:
|
||||
@@ -509,39 +523,39 @@ def imgFileProcessing(work):
|
||||
if opt.nosplitrotate:
|
||||
splitter = None
|
||||
else:
|
||||
splitter = img.splitPage(dirpath, opt.righttoleft, opt.rotate)
|
||||
splitter = img.splitPage(dirpath)
|
||||
if splitter is not None:
|
||||
img0 = image.ComicPage(splitter[0], opt.profileData)
|
||||
img0 = image.ComicPage(splitter[0], opt)
|
||||
imgOptimization(img0, opt)
|
||||
output.append(img0.saveToDir(dirpath, opt.forcepng, opt.forcecolor))
|
||||
img1 = image.ComicPage(splitter[1], opt.profileData)
|
||||
output.append(img0.saveToDir(dirpath))
|
||||
img1 = image.ComicPage(splitter[1], opt)
|
||||
imgOptimization(img1, opt)
|
||||
output.append(img1.saveToDir(dirpath, opt.forcepng, opt.forcecolor))
|
||||
output.append(img1.saveToDir(dirpath))
|
||||
if wipe:
|
||||
output.append(img0.origFileName)
|
||||
output.append(img1.origFileName)
|
||||
if opt.quality == 2:
|
||||
img0b = image.ComicPage(splitter[0], opt.profileData, img0.fill)
|
||||
img0b = image.ComicPage(splitter[0], opt, img0.fill)
|
||||
imgOptimization(img0b, opt, img0)
|
||||
output.append(img0b.saveToDir(dirpath, opt.forcepng, opt.forcecolor))
|
||||
img1b = image.ComicPage(splitter[1], opt.profileData, img1.fill)
|
||||
output.append(img0b.saveToDir(dirpath))
|
||||
img1b = image.ComicPage(splitter[1], opt, img1.fill)
|
||||
imgOptimization(img1b, opt, img1)
|
||||
output.append(img1b.saveToDir(dirpath, opt.forcepng, opt.forcecolor))
|
||||
output.append(img1b.saveToDir(dirpath))
|
||||
output.append(img0.origFileName)
|
||||
output.append(img1.origFileName)
|
||||
output.append(img.origFileName)
|
||||
else:
|
||||
imgOptimization(img, opt)
|
||||
output.append(img.saveToDir(dirpath, opt.forcepng, opt.forcecolor))
|
||||
output.append(img.saveToDir(dirpath))
|
||||
if wipe:
|
||||
output.append(img.origFileName)
|
||||
if opt.quality == 2:
|
||||
img2 = image.ComicPage(os.path.join(dirpath, afile), opt.profileData, img.fill)
|
||||
img2 = image.ComicPage(os.path.join(dirpath, afile), opt, img.fill)
|
||||
if img.rotated:
|
||||
img2.image = img2.image.rotate(90)
|
||||
img2.image = img2.image.rotate(90, Image.BICUBIC, True)
|
||||
img2.rotated = True
|
||||
imgOptimization(img2, opt, img)
|
||||
output.append(img2.saveToDir(dirpath, opt.forcepng, opt.forcecolor))
|
||||
output.append(img2.saveToDir(dirpath))
|
||||
output.append(img.origFileName)
|
||||
return output
|
||||
except Exception:
|
||||
@@ -664,11 +678,12 @@ def getComicInfo(path, originalPath):
|
||||
options.authors.sort()
|
||||
else:
|
||||
options.authors = ['KCC']
|
||||
if len(xml.getElementsByTagName('ScanInformation')) != 0:
|
||||
coverId = xml.getElementsByTagName('ScanInformation')[0].firstChild.nodeValue
|
||||
coverId = compile('(MCD\\()(\\d+)(\\))').search(coverId)
|
||||
if coverId:
|
||||
options.remoteCovers = getCoversFromMCB(coverId.group(2))
|
||||
# Disabled due to closure of MCD
|
||||
# if len(xml.getElementsByTagName('ScanInformation')) != 0:
|
||||
# coverId = xml.getElementsByTagName('ScanInformation')[0].firstChild.nodeValue
|
||||
# coverId = compile('(MCD\\()(\\d+)(\\))').search(coverId)
|
||||
# if coverId:
|
||||
# options.remoteCovers = getCoversFromMCB(coverId.group(2))
|
||||
os.remove(xmlPath)
|
||||
|
||||
|
||||
@@ -746,7 +761,7 @@ def sanitizePermissions(filetree):
|
||||
os.chmod(os.path.join(root, name), S_IWRITE | S_IREAD | S_IEXEC)
|
||||
|
||||
|
||||
#noinspection PyUnboundLocalVariable
|
||||
# noinspection PyUnboundLocalVariable
|
||||
def splitDirectory(path):
|
||||
# Detect directory stucture
|
||||
for root, dirs, files in walkLevel(os.path.join(path, 'OEBPS', 'Images'), 0):
|
||||
@@ -809,11 +824,15 @@ def splitProcess(path, mode):
|
||||
output = []
|
||||
currentSize = 0
|
||||
currentTarget = path
|
||||
if options.webtoon:
|
||||
targetSize = 104857600
|
||||
else:
|
||||
targetSize = 419430400
|
||||
if mode == 0:
|
||||
for root, dirs, files in walkLevel(path, 0):
|
||||
for name in files:
|
||||
size = os.path.getsize(os.path.join(root, name))
|
||||
if currentSize + size > 419430400:
|
||||
if currentSize + size > targetSize:
|
||||
currentTarget, pathRoot = createNewTome()
|
||||
output.append(pathRoot)
|
||||
currentSize = size
|
||||
@@ -825,7 +844,7 @@ def splitProcess(path, mode):
|
||||
for root, dirs, files in walkLevel(path, 0):
|
||||
for name in dirs:
|
||||
size = getDirectorySize(os.path.join(root, name))
|
||||
if currentSize + size > 419430400:
|
||||
if currentSize + size > targetSize:
|
||||
currentTarget, pathRoot = createNewTome()
|
||||
output.append(pathRoot)
|
||||
currentSize = size
|
||||
@@ -839,7 +858,7 @@ def splitProcess(path, mode):
|
||||
for name in dirs:
|
||||
size = getDirectorySize(os.path.join(root, name))
|
||||
currentSize = 0
|
||||
if size > 419430400:
|
||||
if size > targetSize:
|
||||
if not firstTome:
|
||||
currentTarget, pathRoot = createNewTome()
|
||||
output.append(pathRoot)
|
||||
@@ -848,7 +867,7 @@ def splitProcess(path, mode):
|
||||
for rootInside, dirsInside, filesInside in walkLevel(os.path.join(root, name), 0):
|
||||
for nameInside in dirsInside:
|
||||
size = getDirectorySize(os.path.join(rootInside, nameInside))
|
||||
if currentSize + size > 419430400:
|
||||
if currentSize + size > targetSize:
|
||||
currentTarget, pathRoot = createNewTome()
|
||||
output.append(pathRoot)
|
||||
currentSize = size
|
||||
@@ -871,7 +890,7 @@ def detectCorruption(tmpPath, orgPath):
|
||||
for name in files:
|
||||
if getImageFileName(name) is not None:
|
||||
path = os.path.join(root, name)
|
||||
pathOrg = os.path.join(orgPath, name)
|
||||
pathOrg = orgPath + path.split('OEBPS' + os.path.sep + 'Images')[1]
|
||||
if os.path.getsize(path) == 0:
|
||||
rmtree(os.path.join(tmpPath, '..', '..'), True)
|
||||
raise RuntimeError('Image file %s is corrupted.' % pathOrg)
|
||||
@@ -945,7 +964,7 @@ def makeZIP(zipFilename, baseDir, isEPUB=False):
|
||||
|
||||
|
||||
def makeParser():
|
||||
"""Create and return an option parser set up with kcc's options."""
|
||||
"""Create and return an option parser set up with KCC options."""
|
||||
psr = OptionParser(usage="Usage: kcc-c2e [options] comic_file|comic_folder", add_help_option=False)
|
||||
|
||||
mainOptions = OptionGroup(psr, "MAIN")
|
||||
@@ -954,9 +973,9 @@ def makeParser():
|
||||
customProfileOptions = OptionGroup(psr, "CUSTOM PROFILE")
|
||||
otherOptions = OptionGroup(psr, "OTHER")
|
||||
|
||||
mainOptions.add_option("-p", "--profile", action="store", dest="profile", default="KHD",
|
||||
help="Device profile (Choose one among K1, K2, K345, KDX, KHD, KF, KFHD, KFHD8, KFHDX,"
|
||||
" KFHDX8, KFA, KoMT, KoG, KoA, KoAHD) [Default=KHD]")
|
||||
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]")
|
||||
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,
|
||||
@@ -968,8 +987,8 @@ def makeParser():
|
||||
help="Output generated file to specified directory or file")
|
||||
outputOptions.add_option("-t", "--title", action="store", dest="title", default="defaulttitle",
|
||||
help="Comic title [Default=filename or directory name]")
|
||||
outputOptions.add_option("--cbz-output", action="store_true", dest="cbzoutput", default=False,
|
||||
help="Outputs a CBZ archive and does not generate EPUB")
|
||||
outputOptions.add_option("-f", "--format", action="store", dest="format", default="Auto",
|
||||
help="Output format (Available options: Auto, MOBI, EPUB, CBZ) [Default=Auto]")
|
||||
outputOptions.add_option("--batchsplit", action="store_true", dest="batchsplit", default=False,
|
||||
help="Split output into multiple files"),
|
||||
|
||||
@@ -1016,13 +1035,22 @@ def checkOptions():
|
||||
global options
|
||||
options.panelview = True
|
||||
options.bordersColor = None
|
||||
if options.format == 'Auto':
|
||||
if options.profile in ['K1', 'K2', 'K345', 'KPW', 'KV', 'KFHD', 'KFHDX', 'KFHDX8', 'KFA']:
|
||||
options.format = 'MOBI'
|
||||
elif options.profile in ['Other']:
|
||||
options.format = 'EPUB'
|
||||
elif options.profile in ['KDX', 'KoMT', 'KoG', 'KoA', 'KoAHD', 'KoAH2O']:
|
||||
options.format = 'CBZ'
|
||||
if options.white_borders:
|
||||
options.bordersColor = "white"
|
||||
options.bordersColor = 'white'
|
||||
if options.black_borders:
|
||||
options.bordersColor = "black"
|
||||
options.bordersColor = 'black'
|
||||
# Splitting MOBI is not optional
|
||||
if options.format == 'MOBI':
|
||||
options.batchsplit = True
|
||||
# Disabling grayscale conversion for Kindle Fire family.
|
||||
if options.profile == 'KF' or options.profile == 'KFHD' or options.profile == 'KFHD8' or options.profile == 'KFHDX'\
|
||||
or options.profile == 'KFHDX8' or options.forcecolor:
|
||||
if 'KFH' in options.profile or options.forcecolor:
|
||||
options.forcecolor = True
|
||||
else:
|
||||
options.forcecolor = False
|
||||
@@ -1049,10 +1077,10 @@ def checkOptions():
|
||||
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.cbzoutput:
|
||||
if options.profile == 'KDX' and options.format == 'CBZ':
|
||||
options.customheight = 1200
|
||||
# Ultra mode don't work with CBZ format
|
||||
if options.quality == 2 and options.cbzoutput:
|
||||
if options.quality == 2 and options.format == 'CBZ':
|
||||
options.quality = 1
|
||||
# Override profile data
|
||||
if options.customwidth != 0 or options.customheight != 0:
|
||||
@@ -1069,12 +1097,35 @@ def checkOptions():
|
||||
options.profileData = image.ProfileData.Profiles[options.profile]
|
||||
|
||||
|
||||
def checkTools(source):
|
||||
source = source.upper()
|
||||
if source.endswith('.CBR') or source.endswith('.RAR'):
|
||||
rarExitCode = Popen('unrar', stdout=PIPE, stderr=STDOUT, shell=True)
|
||||
rarExitCode = rarExitCode.wait()
|
||||
if rarExitCode != 0 and rarExitCode != 7:
|
||||
print('\nUnRAR is missing!')
|
||||
exit(1)
|
||||
elif source.endswith('.CB7') or source.endswith('.7Z'):
|
||||
sevenzaExitCode = Popen('7za', stdout=PIPE, stderr=STDOUT, shell=True)
|
||||
sevenzaExitCode = sevenzaExitCode.wait()
|
||||
if sevenzaExitCode != 0 and sevenzaExitCode != 7:
|
||||
print('\n7za is missing!')
|
||||
exit(1)
|
||||
if options.format == 'MOBI':
|
||||
kindleGenExitCode = Popen('kindlegen -locale en', stdout=PIPE, stderr=STDOUT, shell=True)
|
||||
if kindleGenExitCode.wait() != 0:
|
||||
print('\nKindleGen is missing!')
|
||||
exit(1)
|
||||
|
||||
|
||||
def makeBook(source, qtGUI=None):
|
||||
"""Generates EPUB/CBZ comic ebook from a bunch of images."""
|
||||
"""Generates MOBI/EPUB/CBZ comic ebook from a bunch of images."""
|
||||
global GUI
|
||||
GUI = qtGUI
|
||||
if GUI:
|
||||
GUI.progressBarTick.emit('1')
|
||||
else:
|
||||
checkTools(source)
|
||||
path = getWorkFolder(source)
|
||||
print("\nChecking images...")
|
||||
getComicInfo(os.path.join(path, "OEBPS", "Images"), source)
|
||||
@@ -1092,7 +1143,7 @@ def makeBook(source, qtGUI=None):
|
||||
if GUI:
|
||||
GUI.progressBarTick.emit('1')
|
||||
chapterNames = sanitizeTree(os.path.join(path, 'OEBPS', 'Images'))
|
||||
if 'Ko' in options.profile and options.cbzoutput:
|
||||
if 'Ko' in options.profile and options.format == 'CBZ':
|
||||
sanitizeTreeKobo(os.path.join(path, 'OEBPS', 'Images'))
|
||||
if options.batchsplit:
|
||||
tomes = splitDirectory(path)
|
||||
@@ -1101,7 +1152,7 @@ def makeBook(source, qtGUI=None):
|
||||
filepath = []
|
||||
tomeNumber = 0
|
||||
if GUI:
|
||||
if options.cbzoutput:
|
||||
if options.format == 'CBZ':
|
||||
GUI.progressBarTick.emit('Compressing CBZ files')
|
||||
else:
|
||||
GUI.progressBarTick.emit('Compressing EPUB files')
|
||||
@@ -1109,11 +1160,13 @@ def makeBook(source, qtGUI=None):
|
||||
GUI.progressBarTick.emit('tick')
|
||||
options.baseTitle = options.title
|
||||
for tome in tomes:
|
||||
if len(tomes) > 1:
|
||||
if len(tomes) > 9:
|
||||
tomeNumber += 1
|
||||
options.title = options.baseTitle + ' [' + str(tomeNumber).zfill(2) + '/' + str(len(tomes)).zfill(2) + ']'
|
||||
elif len(tomes) > 1:
|
||||
tomeNumber += 1
|
||||
options.title = options.baseTitle + ' [' + str(tomeNumber) + '/' + str(len(tomes)) + ']'
|
||||
if options.cbzoutput:
|
||||
# if CBZ output wanted, compress all images and return filepath
|
||||
if options.format == 'CBZ':
|
||||
print("\nCreating CBZ file...")
|
||||
if len(tomes) > 1:
|
||||
filepath.append(getOutputFilename(source, options.output, '.cbz', ' ' + str(tomeNumber)))
|
||||
@@ -1121,9 +1174,8 @@ def makeBook(source, qtGUI=None):
|
||||
filepath.append(getOutputFilename(source, options.output, '.cbz', ''))
|
||||
makeZIP(tome + '_comic', os.path.join(tome, "OEBPS", "Images"))
|
||||
else:
|
||||
print("\nCreating EPUB structure...")
|
||||
print("\nCreating EPUB file...")
|
||||
buildEPUB(tome, chapterNames, tomeNumber)
|
||||
# actually zip the ePub
|
||||
if len(tomes) > 1:
|
||||
filepath.append(getOutputFilename(source, options.output, '.epub', ' ' + str(tomeNumber)))
|
||||
else:
|
||||
@@ -1133,4 +1185,92 @@ def makeBook(source, qtGUI=None):
|
||||
rmtree(tome, True)
|
||||
if GUI:
|
||||
GUI.progressBarTick.emit('tick')
|
||||
return filepath
|
||||
if not GUI and options.format == 'MOBI':
|
||||
print("\nCreating MOBI file...")
|
||||
work = []
|
||||
for i in filepath:
|
||||
work.append([i])
|
||||
output = makeMOBI(work, GUI)
|
||||
for errors in output:
|
||||
if errors[0] != 0:
|
||||
print('KINDLEGEN ERROR!')
|
||||
print(errors)
|
||||
return filepath
|
||||
for i in filepath:
|
||||
output = makeMOBIFix(i)
|
||||
if not output[0]:
|
||||
print('DUALMETAFIX ERROR!')
|
||||
return filepath
|
||||
else:
|
||||
os.remove(i.replace('.epub', '.mobi') + '_toclean')
|
||||
return filepath
|
||||
|
||||
|
||||
def makeMOBIFix(item):
|
||||
os.remove(item)
|
||||
mobiPath = item.replace('.epub', '.mobi')
|
||||
move(mobiPath, mobiPath + '_toclean')
|
||||
try:
|
||||
dualmetafix.DualMobiMetaFix(mobiPath + '_toclean', mobiPath, bytes(str(uuid4()), 'UTF-8'))
|
||||
return [True]
|
||||
except Exception as err:
|
||||
return [False, format(err)]
|
||||
|
||||
|
||||
def makeMOBIWorkerTick(output):
|
||||
makeMOBIWorkerOutput.append(output)
|
||||
if output[0] != 0:
|
||||
makeMOBIWorkerPool.terminate()
|
||||
if GUI:
|
||||
GUI.progressBarTick.emit('tick')
|
||||
if not GUI.conversionAlive:
|
||||
makeMOBIWorkerPool.terminate()
|
||||
|
||||
|
||||
def makeMOBIWorker(item):
|
||||
item = item[0]
|
||||
kindlegenErrorCode = 0
|
||||
kindlegenError = ''
|
||||
try:
|
||||
if os.path.getsize(item) < 629145600:
|
||||
output = Popen('kindlegen -dont_append_source -locale en "' + item + '"',
|
||||
stdout=PIPE, stderr=STDOUT, shell=True)
|
||||
for line in output.stdout:
|
||||
line = line.decode('utf-8')
|
||||
# ERROR: Generic error
|
||||
if "Error(" in line:
|
||||
kindlegenErrorCode = 1
|
||||
kindlegenError = line
|
||||
# ERROR: EPUB too big
|
||||
if ":E23026:" in line:
|
||||
kindlegenErrorCode = 23026
|
||||
if kindlegenErrorCode > 0:
|
||||
break
|
||||
else:
|
||||
# ERROR: EPUB too big
|
||||
kindlegenErrorCode = 23026
|
||||
return [kindlegenErrorCode, kindlegenError, item]
|
||||
except Exception as err:
|
||||
# ERROR: KCC unknown generic error
|
||||
kindlegenErrorCode = 1
|
||||
kindlegenError = format(err)
|
||||
return [kindlegenErrorCode, kindlegenError, item]
|
||||
|
||||
|
||||
def makeMOBI(work, qtGUI=None):
|
||||
global GUI, makeMOBIWorkerPool, makeMOBIWorkerOutput
|
||||
GUI = qtGUI
|
||||
makeMOBIWorkerOutput = []
|
||||
availableMemory = virtual_memory().total/1000000000
|
||||
if availableMemory <= 2:
|
||||
threadNumber = 1
|
||||
elif 2 < availableMemory <= 4:
|
||||
threadNumber = 2
|
||||
else:
|
||||
threadNumber = 4
|
||||
makeMOBIWorkerPool = Pool(threadNumber)
|
||||
for i in work:
|
||||
makeMOBIWorkerPool.apply_async(func=makeMOBIWorker, args=(i, ), callback=makeMOBIWorkerTick)
|
||||
makeMOBIWorkerPool.close()
|
||||
makeMOBIWorkerPool.join()
|
||||
return makeMOBIWorkerOutput
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
||||
# Copyright (c) 2013-2014 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||
# Copyright (c) 2013-2015 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||
#
|
||||
# Permission to use, copy, modify, and/or distribute this software for
|
||||
# any purpose with or without fee is hereby granted, provided that the
|
||||
@@ -18,9 +18,9 @@
|
||||
# PERFORMANCE OF THIS SOFTWARE.
|
||||
#
|
||||
|
||||
__version__ = '4.2.1'
|
||||
__version__ = '4.4'
|
||||
__license__ = 'ISC'
|
||||
__copyright__ = '2012-2014, 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'
|
||||
|
||||
import os
|
||||
@@ -28,7 +28,7 @@ import sys
|
||||
from shutil import rmtree, copytree, move
|
||||
from optparse import OptionParser, OptionGroup
|
||||
from multiprocessing import Pool
|
||||
from PIL import Image, ImageStat
|
||||
from PIL import Image, ImageStat, ImageOps
|
||||
from .shared import getImageFileName, walkLevel
|
||||
try:
|
||||
from PyQt5 import QtCore
|
||||
@@ -50,9 +50,9 @@ def mergeDirectory(work):
|
||||
try:
|
||||
directory = work[0]
|
||||
images = []
|
||||
imagesClear = []
|
||||
imagesValid = []
|
||||
sizes = []
|
||||
h = 0
|
||||
targetHeight = 0
|
||||
for root, dirs, files in walkLevel(directory, 0):
|
||||
for name in files:
|
||||
if getImageFileName(name) is not None:
|
||||
@@ -60,23 +60,26 @@ def mergeDirectory(work):
|
||||
images.append([os.path.join(root, name), i.size[0], i.size[1]])
|
||||
sizes.append(i.size[0])
|
||||
if len(images) > 0:
|
||||
mw = max(set(sizes), key=sizes.count)
|
||||
targetWidth = max(set(sizes), key=sizes.count)
|
||||
for i in images:
|
||||
if i[1] == mw:
|
||||
h += i[2]
|
||||
imagesClear.append(i[0])
|
||||
if i[1] <= targetWidth:
|
||||
targetHeight += i[2]
|
||||
imagesValid.append(i[0])
|
||||
# Silently drop directories that contain too many images
|
||||
if h > 262144:
|
||||
# 131072 = GIMP_MAX_IMAGE_SIZE / 4
|
||||
if targetHeight > 131072:
|
||||
return None
|
||||
result = Image.new('RGB', (mw, h))
|
||||
result = Image.new('RGB', (targetWidth, targetHeight))
|
||||
y = 0
|
||||
for i in imagesClear:
|
||||
for i in imagesValid:
|
||||
img = Image.open(i)
|
||||
img = img.convert('RGB')
|
||||
if img.size[0] < targetWidth:
|
||||
img = ImageOps.fit(img, (targetWidth, img.size[1]), method=Image.BICUBIC, centering=(0.5, 0.5))
|
||||
result.paste(img, (0, y))
|
||||
y += img.size[1]
|
||||
os.remove(i)
|
||||
savePath = os.path.split(imagesClear[0])
|
||||
savePath = os.path.split(imagesValid[0])
|
||||
result.save(os.path.join(savePath[0], os.path.splitext(savePath[1])[0] + '.png'), 'PNG')
|
||||
except Exception:
|
||||
return str(sys.exc_info()[1])
|
||||
@@ -84,7 +87,7 @@ def mergeDirectory(work):
|
||||
|
||||
def sanitizePanelSize(panel, opt):
|
||||
newPanels = []
|
||||
if panel[2] > 8 * opt.height:
|
||||
if panel[2] > 6 * opt.height:
|
||||
diff = int(panel[2] / 8)
|
||||
newPanels.append([panel[0], panel[1] - diff*7, diff])
|
||||
newPanels.append([panel[1] - diff*7, panel[1] - diff*6, diff])
|
||||
@@ -94,13 +97,13 @@ def sanitizePanelSize(panel, opt):
|
||||
newPanels.append([panel[1] - diff*3, panel[1] - diff*2, diff])
|
||||
newPanels.append([panel[1] - diff*2, panel[1] - diff, diff])
|
||||
newPanels.append([panel[1] - diff, panel[1], diff])
|
||||
elif panel[2] > 4 * opt.height:
|
||||
elif panel[2] > 3 * opt.height:
|
||||
diff = int(panel[2] / 4)
|
||||
newPanels.append([panel[0], panel[1] - diff*3, diff])
|
||||
newPanels.append([panel[1] - diff*3, panel[1] - diff*2, diff])
|
||||
newPanels.append([panel[1] - diff*2, panel[1] - diff, diff])
|
||||
newPanels.append([panel[1] - diff, panel[1], diff])
|
||||
elif panel[2] > 2 * opt.height:
|
||||
elif panel[2] > 1.5 * opt.height:
|
||||
newPanels.append([panel[0], panel[1] - int(panel[2] / 2), int(panel[2] / 2)])
|
||||
newPanels.append([panel[1] - int(panel[2] / 2), panel[1], int(panel[2] / 2)])
|
||||
else:
|
||||
@@ -118,7 +121,6 @@ def splitImageTick(output):
|
||||
splitWorkerPool.terminate()
|
||||
|
||||
|
||||
#noinspection PyUnboundLocalVariable
|
||||
def splitImage(work):
|
||||
try:
|
||||
path = work[0]
|
||||
@@ -165,6 +167,7 @@ def splitImage(work):
|
||||
for panel in panelsCleaned:
|
||||
panels.append(panel)
|
||||
if opt.debug:
|
||||
# noinspection PyUnboundLocalVariable
|
||||
debugImage.save(os.path.join(path, fileExpanded[0] + '-debug.png'), 'PNG')
|
||||
|
||||
# Create virtual pages
|
||||
@@ -199,8 +202,7 @@ def splitImage(work):
|
||||
panelImg = image.crop([0, panels[panel][0], widthImg, panels[panel][1]])
|
||||
newPage.paste(panelImg, (0, targetHeight))
|
||||
targetHeight += panels[panel][2]
|
||||
newPage.save(os.path.join(path, fileExpanded[0] + '-' +
|
||||
str(pageNumber) + '.png'), 'PNG')
|
||||
newPage.save(os.path.join(path, fileExpanded[0] + '-' + str(pageNumber) + '.png'), 'PNG')
|
||||
pageNumber += 1
|
||||
os.remove(filePath)
|
||||
except Exception:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Based on initial version of DualMetaFix. Copyright (C) 2013 Kevin Hendricks
|
||||
# Changes for KCC Copyright (C) 2014 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||
# Changes for KCC Copyright (C) 2014-2015 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
|
||||
97
kcc/image.py
97
kcc/image.py
@@ -1,7 +1,7 @@
|
||||
# Copyright (C) 2010 Alex Yatskov
|
||||
# Copyright (C) 2011 Stanislav (proDOOMman) Kosolapov <prodoomman@gmail.com>
|
||||
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
||||
# Copyright (c) 2013-2014 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||
# Copyright (c) 2013-2015 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
@@ -16,9 +16,9 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
__version__ = '4.2.1'
|
||||
__version__ = '4.4'
|
||||
__license__ = 'ISC'
|
||||
__copyright__ = '2012-2014, 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'
|
||||
|
||||
import os
|
||||
@@ -85,33 +85,32 @@ class ProfileData:
|
||||
'K1': ("Kindle 1", (600, 670), Palette4, 1.8, (900, 1005)),
|
||||
'K2': ("Kindle 2", (600, 670), Palette15, 1.8, (900, 1005)),
|
||||
'K345': ("Kindle", (600, 800), Palette16, 1.8, (900, 1200)),
|
||||
'KHD': ("Kindle Paperwhite", (758, 1024), Palette16, 1.8, (1137, 1536)),
|
||||
'KDX': ("Kindle DX/DXG", (824, 1000), Palette16, 1.8, (1236, 1500)),
|
||||
'KF': ("Kindle Fire", (600, 1024), PalleteNull, 1.0, (900, 1536)),
|
||||
'KFHD': ("K. Fire HD 7\"", (800, 1280), PalleteNull, 1.0, (1200, 1920)),
|
||||
'KFHD8': ("K. Fire HD 8.9\"", (1200, 1920), PalleteNull, 1.0, (1800, 2880)),
|
||||
'KFHDX': ("K. Fire HDX 7\"", (1200, 1920), PalleteNull, 1.0, (1800, 2880)),
|
||||
'KFHDX8': ("K. Fire HDX 8.9\"", (1600, 2560), PalleteNull, 1.0, (2400, 3840)),
|
||||
'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)),
|
||||
'KoMT': ("Kobo Mini/Touch", (600, 800), Palette16, 1.8, (900, 1200)),
|
||||
'KoG': ("Kobo Glow", (768, 1024), Palette16, 1.8, (1152, 1536)),
|
||||
'KoA': ("Kobo Aura", (758, 1024), Palette16, 1.8, (1137, 1536)),
|
||||
'KoAHD': ("Kobo Aura HD", (1080, 1440), Palette16, 1.8, (1620, 2160)),
|
||||
'KFA': ("Kindle for Android", (0, 0), PalleteNull, 1.0, (0, 0)),
|
||||
'KoAH2O': ("Kobo Aura H2O", (1080, 1430), Palette16, 1.8, (1620, 2145)),
|
||||
'OTHER': ("Other", (0, 0), Palette16, 1.8, (0, 0)),
|
||||
}
|
||||
|
||||
|
||||
class ComicPage:
|
||||
def __init__(self, source, device, fill=None):
|
||||
def __init__(self, source, options, fill=None):
|
||||
try:
|
||||
self.profile_label, self.size, self.palette, self.gamma, self.panelviewsize = device
|
||||
self.profile_label, self.size, self.palette, self.gamma, self.panelviewsize = options.profileData
|
||||
except KeyError:
|
||||
raise RuntimeError('Unexpected output device %s' % device)
|
||||
raise RuntimeError('Unexpected output device %s' % options.profileData)
|
||||
self.origFileName = source
|
||||
self.filename = os.path.basename(self.origFileName)
|
||||
self.image = Image.open(source)
|
||||
self.image = self.image.convert('RGB')
|
||||
self.color = self.isImageColor()
|
||||
self.rotated = None
|
||||
self.border = None
|
||||
self.noHPV = None
|
||||
@@ -119,17 +118,22 @@ class ComicPage:
|
||||
self.noPV = None
|
||||
self.purge = False
|
||||
self.hq = False
|
||||
self.opt = options
|
||||
if fill:
|
||||
self.fill = fill
|
||||
else:
|
||||
self.fill = None
|
||||
if options.webtoon:
|
||||
self.color = True
|
||||
else:
|
||||
self.color = self.isImageColor()
|
||||
|
||||
def saveToDir(self, targetdir, forcepng, color):
|
||||
def saveToDir(self, targetdir):
|
||||
try:
|
||||
if not self.purge:
|
||||
flags = []
|
||||
filename = os.path.join(targetdir, os.path.splitext(self.filename)[0]) + '-KCC'
|
||||
if not color and not forcepng:
|
||||
if not self.opt.forcecolor and not self.opt.forcepng:
|
||||
self.image = self.image.convert('L')
|
||||
if self.rotated:
|
||||
flags.append('Rotated')
|
||||
@@ -144,21 +148,22 @@ class ComicPage:
|
||||
if self.noVPV:
|
||||
flags.append('NoVerticalPanelView')
|
||||
if self.border:
|
||||
flags.append("Margins-" + str(self.border[0]) + "-" + str(self.border[1]) + "-"
|
||||
+ str(self.border[2]) + "-" + str(self.border[3]))
|
||||
if forcepng:
|
||||
filename += ".png"
|
||||
self.image.save(filename, "PNG", optimize=1)
|
||||
flags.append('Margins-' + str(self.border[0]) + '-' + str(self.border[1]) + '-'
|
||||
+ str(self.border[2]) + '-' + str(self.border[3]))
|
||||
if self.opt.forcepng:
|
||||
filename += '.png'
|
||||
self.image.save(filename, 'PNG', optimize=1)
|
||||
else:
|
||||
filename += ".jpg"
|
||||
self.image.save(filename, "JPEG", optimize=1)
|
||||
filename += '.jpg'
|
||||
self.image.save(filename, 'JPEG', optimize=1, quality=80)
|
||||
return [md5Checksum(filename), flags]
|
||||
else:
|
||||
return None
|
||||
except IOError as e:
|
||||
raise RuntimeError('Cannot write image in directory %s: %s' % (targetdir, e))
|
||||
|
||||
def optimizeImage(self, gamma):
|
||||
def optimizeImage(self):
|
||||
gamma = self.opt.gamma
|
||||
if gamma < 0.1:
|
||||
gamma = self.gamma
|
||||
if self.gamma != 1.0 and self.color:
|
||||
@@ -215,7 +220,12 @@ class ComicPage:
|
||||
self.noHPV = True
|
||||
self.noVPV = True
|
||||
|
||||
def resizeImage(self, upscale=False, stretch=False, bordersColor=None, qualityMode=0):
|
||||
def resizeImage(self, qualityMode=None):
|
||||
upscale = self.opt.upscale
|
||||
stretch = self.opt.stretch
|
||||
bordersColor = self.opt.bordersColor
|
||||
if qualityMode is None:
|
||||
qualityMode = self.opt.quality
|
||||
if bordersColor:
|
||||
fill = bordersColor
|
||||
else:
|
||||
@@ -243,7 +253,7 @@ class ComicPage:
|
||||
if self.image.size[0] <= size[0] and self.image.size[1] <= size[1]:
|
||||
method = Image.BICUBIC
|
||||
else:
|
||||
method = Image.ANTIALIAS
|
||||
method = Image.LANCZOS
|
||||
self.image = self.image.resize(size, method)
|
||||
return self.image
|
||||
# If image is smaller than target resolution and upscale is off - Just expand it by adding margins
|
||||
@@ -269,17 +279,17 @@ class ComicPage:
|
||||
if self.image.size[0] <= size[0] and self.image.size[1] <= size[1]:
|
||||
method = Image.BICUBIC
|
||||
else:
|
||||
method = Image.ANTIALIAS
|
||||
method = Image.LANCZOS
|
||||
self.image = ImageOps.fit(self.image, size, method=method, centering=(0.5, 0.5))
|
||||
return self.image
|
||||
|
||||
def splitPage(self, targetdir, righttoleft=False, rotate=False):
|
||||
def splitPage(self, targetdir):
|
||||
width, height = self.image.size
|
||||
dstwidth, dstheight = self.size
|
||||
# Only split if origin is not oriented the same as target
|
||||
if (width > height) != (dstwidth > dstheight):
|
||||
if rotate:
|
||||
self.image = self.image.rotate(90)
|
||||
if self.opt.rotate:
|
||||
self.image = self.image.rotate(90, Image.BICUBIC, True)
|
||||
self.rotated = True
|
||||
return None
|
||||
else:
|
||||
@@ -292,18 +302,18 @@ class ComicPage:
|
||||
# Source is portrait and target is landscape, so split by the height
|
||||
leftbox = (0, 0, width, int(height / 2))
|
||||
rightbox = (0, int(height / 2), width, height)
|
||||
filename = os.path.splitext(self.filename)
|
||||
fileone = targetdir + '/' + filename[0] + '-A' + filename[1]
|
||||
filetwo = targetdir + '/' + filename[0] + '-B' + filename[1]
|
||||
filename = os.path.splitext(self.filename)[0]
|
||||
fileone = targetdir + '/' + filename + '-AAA.png'
|
||||
filetwo = targetdir + '/' + filename + '-BBB.png'
|
||||
try:
|
||||
if righttoleft:
|
||||
if self.opt.righttoleft:
|
||||
pageone = self.image.crop(rightbox)
|
||||
pagetwo = self.image.crop(leftbox)
|
||||
else:
|
||||
pageone = self.image.crop(leftbox)
|
||||
pagetwo = self.image.crop(rightbox)
|
||||
pageone.save(fileone)
|
||||
pagetwo.save(filetwo)
|
||||
pageone.save(fileone, 'PNG', optimize=1)
|
||||
pagetwo.save(filetwo, 'PNG', optimize=1)
|
||||
except IOError as e:
|
||||
raise RuntimeError('Cannot write image in directory %s: %s' % (targetdir, e))
|
||||
return fileone, filetwo
|
||||
@@ -417,13 +427,16 @@ class ComicPage:
|
||||
imageBoxB = ImageChops.invert(bw).getbbox()
|
||||
if imageBoxA is None or imageBoxB is None:
|
||||
surfaceB, surfaceW = 0, 0
|
||||
diff = 0
|
||||
else:
|
||||
surfaceB = (imageBoxA[2] - imageBoxA[0]) * (imageBoxA[3] - imageBoxA[1])
|
||||
surfaceW = (imageBoxB[2] - imageBoxB[0]) * (imageBoxB[3] - imageBoxB[1])
|
||||
if surfaceW < surfaceB:
|
||||
self.fill = 'white'
|
||||
elif surfaceW > surfaceB:
|
||||
self.fill = 'black'
|
||||
diff = ((max(surfaceB, surfaceW) - min(surfaceB, surfaceW)) / min(surfaceB, surfaceW)) * 100
|
||||
if diff > 0.5:
|
||||
if surfaceW < surfaceB:
|
||||
self.fill = 'white'
|
||||
elif surfaceW > surfaceB:
|
||||
self.fill = 'black'
|
||||
else:
|
||||
fill = 0
|
||||
startY = 0
|
||||
@@ -498,7 +511,7 @@ class Cover:
|
||||
|
||||
def processExternal(self):
|
||||
self.image = self.image.convert('RGB')
|
||||
self.image.thumbnail(self.options.profileData[1], Image.ANTIALIAS)
|
||||
self.image.thumbnail(self.options.profileData[1], Image.LANCZOS)
|
||||
self.save(True)
|
||||
|
||||
def trim(self):
|
||||
@@ -520,6 +533,6 @@ class Cover:
|
||||
if os.path.splitext(source)[1].lower() == '.png':
|
||||
self.image.save(self.target, "PNG", optimize=1)
|
||||
else:
|
||||
self.image.save(self.target, "JPEG", optimize=1)
|
||||
self.image.save(self.target, "JPEG", optimize=1, quality=80)
|
||||
except IOError:
|
||||
raise RuntimeError('Failed to save cover')
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
||||
# Copyright (c) 2013-2014 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||
# Copyright (c) 2013-2015 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||
#
|
||||
# Based upon the code snippet by Ned Batchelder
|
||||
# (http://nedbatchelder.com/blog/200712/extracting_jpgs_from_pdfs.html)
|
||||
@@ -20,7 +20,7 @@
|
||||
#
|
||||
|
||||
__license__ = 'ISC'
|
||||
__copyright__ = '2012-2014, 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'
|
||||
|
||||
import os
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# rarfile.py
|
||||
#
|
||||
# Copyright (c) 2005-2013 Marko Kreen <markokr@gmail.com>
|
||||
# Copyright (c) 2005-2014 Marko Kreen <markokr@gmail.com>
|
||||
#
|
||||
# Permission to use, copy, modify, and/or distribute this software for any
|
||||
# purpose with or without fee is hereby granted, provided that the above
|
||||
@@ -74,7 +74,7 @@ For more details, refer to source.
|
||||
|
||||
"""
|
||||
|
||||
__version__ = '2.6'
|
||||
__version__ = '2.7-kcc'
|
||||
|
||||
# export only interesting items
|
||||
__all__ = ['is_rarfile', 'RarInfo', 'RarFile', 'RarExtFile']
|
||||
@@ -178,8 +178,25 @@ EXTRACT_ARGS = ('x', '-y', '-idq')
|
||||
#: args for testrar()
|
||||
TEST_ARGS = ('t', '-idq')
|
||||
|
||||
#
|
||||
# Allow use of tool that is not compatible with unrar.
|
||||
#
|
||||
# By default use 'bsdtar' which is 'tar' program that
|
||||
# sits on top of libarchive.
|
||||
#
|
||||
# Problems with libarchive RAR backend:
|
||||
# - Does not support solid archives.
|
||||
# - Does not support password-protected archives.
|
||||
#
|
||||
|
||||
ALT_TOOL = 'bsdtar'
|
||||
ALT_OPEN_ARGS = ('-x', '--to-stdout', '-f')
|
||||
ALT_EXTRACT_ARGS = ('-x', '-f')
|
||||
ALT_TEST_ARGS = ('-t', '-f')
|
||||
ALT_CHECK_ARGS = ('--help',)
|
||||
|
||||
#: whether to speed up decompression by using tmp archive
|
||||
USE_EXTRACT_HACK = 1
|
||||
USE_EXTRACT_HACK = 0
|
||||
|
||||
#: limit the filesize for tmp archive usage
|
||||
HACK_SIZE_LIMIT = 20*1024*1024
|
||||
@@ -278,6 +295,7 @@ RAR_M5 = 0x35
|
||||
##
|
||||
|
||||
RAR_ID = bytes("Rar!\x1a\x07\x00", 'ascii')
|
||||
RAR5_ID = bytes("Rar!\x1a\x07\x01", 'ascii')
|
||||
ZERO = bytes("\0", 'ascii')
|
||||
EMPTY = bytes("", 'ascii')
|
||||
|
||||
@@ -336,6 +354,8 @@ class RarUnknownError(RarExecError):
|
||||
"""Unknown exit code"""
|
||||
class RarSignalExit(RarExecError):
|
||||
"""Unrar exited with signal"""
|
||||
class RarCannotExec(RarExecError):
|
||||
"""Executable not found."""
|
||||
|
||||
|
||||
def is_rarfile(xfile):
|
||||
@@ -343,7 +363,10 @@ def is_rarfile(xfile):
|
||||
fd = XFile(xfile)
|
||||
buf = fd.read(len(RAR_ID))
|
||||
fd.close()
|
||||
return buf == RAR_ID
|
||||
if buf == RAR_ID or buf == RAR5_ID:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
class RarInfo(object):
|
||||
@@ -693,10 +716,7 @@ class RarFile(object):
|
||||
"""Let 'unrar' test the archive.
|
||||
"""
|
||||
cmd = [UNRAR_TOOL] + list(TEST_ARGS)
|
||||
if self._password is not None:
|
||||
cmd.append('-p' + self._password)
|
||||
else:
|
||||
cmd.append('-p-')
|
||||
add_password_arg(cmd, self._password)
|
||||
cmd.append(self.rarfile)
|
||||
p = custom_popen(cmd)
|
||||
output = p.communicate()[0]
|
||||
@@ -769,7 +789,7 @@ class RarFile(object):
|
||||
fd = XFile(self.rarfile)
|
||||
self._fd = fd
|
||||
id = fd.read(len(RAR_ID))
|
||||
if id != RAR_ID:
|
||||
if id != RAR_ID and id != RAR5_ID:
|
||||
raise NotRarFile("Not a Rar archive: "+self.rarfile)
|
||||
|
||||
volume = 0 # first vol (.rar) is 0
|
||||
@@ -1172,8 +1192,7 @@ class RarFile(object):
|
||||
if is_filelike(rarfile):
|
||||
raise ValueError("Cannot use unrar directly on memory buffer")
|
||||
cmd = [UNRAR_TOOL] + list(OPEN_ARGS)
|
||||
if psw is not None:
|
||||
cmd.append("-p" + psw)
|
||||
add_password_arg(cmd, psw)
|
||||
cmd.append(rarfile)
|
||||
|
||||
# not giving filename avoids encoding related problems
|
||||
@@ -1205,10 +1224,7 @@ class RarFile(object):
|
||||
|
||||
# pasoword
|
||||
psw = psw or self._password
|
||||
if psw is not None:
|
||||
cmd.append('-p' + psw)
|
||||
else:
|
||||
cmd.append('-p-')
|
||||
add_password_arg(cmd, psw)
|
||||
|
||||
# rar file
|
||||
cmd.append(self.rarfile)
|
||||
@@ -1830,10 +1846,7 @@ def rar_decompress(vers, meth, data, declen=0, flags=0, crc=0, psw=None, salt=No
|
||||
tmpf.close()
|
||||
|
||||
cmd = [UNRAR_TOOL] + list(OPEN_ARGS)
|
||||
if psw is not None and (flags & RAR_FILE_PASSWORD):
|
||||
cmd.append("-p" + psw)
|
||||
else:
|
||||
cmd.append("-p-")
|
||||
add_password_arg(cmd, psw, (flags & RAR_FILE_PASSWORD))
|
||||
cmd.append(tmpname)
|
||||
|
||||
p = custom_popen(cmd)
|
||||
@@ -1902,10 +1915,27 @@ def custom_popen(cmd):
|
||||
except OSError:
|
||||
ex = sys.exc_info()[1]
|
||||
if ex.errno == errno.ENOENT:
|
||||
raise RarExecError("Unrar not installed? (rarfile.UNRAR_TOOL=%r)" % UNRAR_TOOL)
|
||||
raise RarCannotExec("Unrar not installed? (rarfile.UNRAR_TOOL=%r)" % UNRAR_TOOL)
|
||||
raise
|
||||
return p
|
||||
|
||||
def custom_check(cmd, ignore_retcode=False):
|
||||
"""Run command, collect output, raise error if needed."""
|
||||
p = custom_popen(cmd)
|
||||
out, err = p.communicate()
|
||||
if p.returncode and not ignore_retcode:
|
||||
raise RarExecError("Check-run failed")
|
||||
return out
|
||||
|
||||
def add_password_arg(cmd, psw, required=False):
|
||||
"""Append password switch to commandline."""
|
||||
if UNRAR_TOOL == ALT_TOOL:
|
||||
return
|
||||
if psw is not None:
|
||||
cmd.append('-p' + psw)
|
||||
else:
|
||||
cmd.append('-p-')
|
||||
|
||||
def check_returncode(p, out):
|
||||
"""Raise exception according to unrar exit code"""
|
||||
|
||||
@@ -1920,6 +1950,8 @@ def check_returncode(p, out):
|
||||
RarWarning, RarFatalError, RarCRCError, RarLockedArchiveError,
|
||||
RarWriteError, RarOpenError, RarUserError, RarMemoryError,
|
||||
RarCreateError, RarNoFilesError] # codes from rar.txt
|
||||
if UNRAR_TOOL == ALT_TOOL:
|
||||
errmap = [None]
|
||||
if code > 0 and code < len(errmap):
|
||||
exc = errmap[code]
|
||||
elif code == 255:
|
||||
@@ -1936,3 +1968,24 @@ def check_returncode(p, out):
|
||||
msg = "%s [%d]" % (exc.__doc__, p.returncode)
|
||||
|
||||
raise exc(msg)
|
||||
|
||||
#
|
||||
# Check if unrar works
|
||||
#
|
||||
|
||||
try:
|
||||
# does UNRAR_TOOL work?
|
||||
custom_check([UNRAR_TOOL], True)
|
||||
except RarCannotExec:
|
||||
try:
|
||||
# does ALT_TOOL work?
|
||||
custom_check([ALT_TOOL] + list(ALT_CHECK_ARGS), True)
|
||||
# replace config
|
||||
UNRAR_TOOL = ALT_TOOL
|
||||
OPEN_ARGS = ALT_OPEN_ARGS
|
||||
EXTRACT_ARGS = ALT_EXTRACT_ARGS
|
||||
TEST_ARGS = ALT_TEST_ARGS
|
||||
except RarCannotExec:
|
||||
# no usable tool, only uncompressed archives work
|
||||
pass
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
||||
# Copyright (c) 2013-2014 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||
# Copyright (c) 2013-2015 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||
#
|
||||
# Permission to use, copy, modify, and/or distribute this software for
|
||||
# any purpose with or without fee is hereby granted, provided that the
|
||||
@@ -17,25 +17,35 @@
|
||||
#
|
||||
|
||||
__license__ = 'ISC'
|
||||
__copyright__ = '2012-2014, 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'
|
||||
|
||||
import os
|
||||
from hashlib import md5
|
||||
from html.parser import HTMLParser
|
||||
|
||||
|
||||
class HTMLStripper(HTMLParser):
|
||||
def __init__(self):
|
||||
HTMLParser.__init__(self)
|
||||
self.reset()
|
||||
self.strict = False
|
||||
self.convert_charrefs = True
|
||||
self.fed = []
|
||||
|
||||
def handle_data(self, d):
|
||||
self.fed.append(d)
|
||||
|
||||
def get_data(self):
|
||||
return ''.join(self.fed)
|
||||
|
||||
|
||||
def getImageFileName(imgfile):
|
||||
filename = os.path.splitext(imgfile)
|
||||
if filename[0].startswith('.') or\
|
||||
(filename[1].lower() != '.png' and
|
||||
filename[1].lower() != '.jpg' and
|
||||
filename[1].lower() != '.gif' and
|
||||
filename[1].lower() != '.tif' and
|
||||
filename[1].lower() != '.tiff' and
|
||||
filename[1].lower() != '.bmp' and
|
||||
filename[1].lower() != '.jpeg'):
|
||||
name, ext = os.path.splitext(imgfile)
|
||||
ext = ext.lower()
|
||||
if name.startswith('.') or (ext != '.png' and ext != '.jpg' and ext != '.jpeg' and ext != '.gif'):
|
||||
return None
|
||||
return filename
|
||||
return [name, ext]
|
||||
|
||||
|
||||
def walkLevel(some_dir, level=1):
|
||||
@@ -58,3 +68,48 @@ def md5Checksum(filePath):
|
||||
break
|
||||
m.update(data)
|
||||
return m.hexdigest()
|
||||
|
||||
|
||||
def check7ZFile(filePath):
|
||||
with open(filePath, 'rb') as fh:
|
||||
header = fh.read(6)
|
||||
return header == b"7z\xbc\xaf'\x1c"
|
||||
|
||||
|
||||
# noinspection PyUnresolvedReferences
|
||||
def dependencyCheck(level):
|
||||
missing = []
|
||||
if level > 2:
|
||||
try:
|
||||
from PyQt5 import QtCore, QtNetwork, QtWidgets
|
||||
if tuple(map(int, ('5.2.0'.split(".")))) > tuple(map(int, (QtCore.qVersion().split(".")))):
|
||||
missing.append('PyQt5 5.2.0+')
|
||||
except ImportError:
|
||||
missing.append('PyQt5 5.2.0+')
|
||||
if level > 1:
|
||||
try:
|
||||
import psutil
|
||||
if tuple(map(int, ('2.0.0'.split(".")))) > tuple(map(int, psutil.version_info)):
|
||||
missing.append('psutil 2.0.0+')
|
||||
except ImportError:
|
||||
missing.append('psutil 2.0.0+')
|
||||
try:
|
||||
import slugify
|
||||
except ImportError:
|
||||
missing.append('python-slugify')
|
||||
try:
|
||||
import PIL
|
||||
if tuple(map(int, ('2.7.0'.split(".")))) > tuple(map(int, (PIL.PILLOW_VERSION.split(".")))):
|
||||
missing.append('Pillow 2.7.0+')
|
||||
except ImportError:
|
||||
missing.append('Pillow 2.7.0+')
|
||||
if len(missing) > 0:
|
||||
try:
|
||||
import tkinter
|
||||
import tkinter.messagebox
|
||||
importRoot = tkinter.Tk()
|
||||
importRoot.withdraw()
|
||||
tkinter.messagebox.showerror('KCC - Error', 'ERROR: ' + ', '.join(missing) + ' is not installed!')
|
||||
except ImportError:
|
||||
print('ERROR: ' + ', '.join(missing) + ' is not installed!')
|
||||
exit(1)
|
||||
45
setup.py
45
setup.py
@@ -1,12 +1,12 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
cx_Freeze/py2app build script for KCC.
|
||||
|
||||
Usage (Mac OS X):
|
||||
python setup.py py2app
|
||||
py2exe/py2app build script for KCC.
|
||||
|
||||
Usage (Windows):
|
||||
python setup.py py2exe
|
||||
|
||||
Usage (Mac OS X):
|
||||
python setup.py py2app
|
||||
"""
|
||||
from sys import platform, version_info
|
||||
if version_info[0] != 3:
|
||||
@@ -14,7 +14,7 @@ if version_info[0] != 3:
|
||||
exit(1)
|
||||
|
||||
NAME = "KindleComicConverter"
|
||||
VERSION = "4.2.1"
|
||||
VERSION = "4.4"
|
||||
MAIN = "kcc.py"
|
||||
|
||||
if platform == "darwin":
|
||||
@@ -33,7 +33,7 @@ if platform == "darwin":
|
||||
CFBundleName=NAME,
|
||||
CFBundleShortVersionString=VERSION,
|
||||
CFBundleGetInfoString=NAME + " " + VERSION +
|
||||
", written 2012-2014 by Ciro Mattia Gonano and Pawel Jastrzebski",
|
||||
", written 2012-2015 by Ciro Mattia Gonano and Pawel Jastrzebski",
|
||||
CFBundleExecutable=NAME,
|
||||
CFBundleIdentifier='com.github.ciromattia.kcc',
|
||||
CFBundleSignature='dplt',
|
||||
@@ -47,7 +47,7 @@ if platform == "darwin":
|
||||
],
|
||||
LSMinimumSystemVersion='10.8.0',
|
||||
LSEnvironment=dict(
|
||||
PATH='/usr/local/bin:/usr/bin:/bin'
|
||||
PATH='./../Resources:/usr/local/bin:/usr/bin:/bin'
|
||||
),
|
||||
NSHumanReadableCopyright='ISC License (ISCL)'
|
||||
)
|
||||
@@ -63,46 +63,25 @@ elif platform == "win32":
|
||||
suffix = '_64'
|
||||
else:
|
||||
suffix = ''
|
||||
additional_files = [('imageformats', ['C:\Python34' + suffix +
|
||||
'\Lib\site-packages\PyQt5\plugins\imageformats\qgif.dll',
|
||||
'C:\Python34' + suffix +
|
||||
'\Lib\site-packages\PyQt5\plugins\imageformats\qico.dll',
|
||||
'C:\Python34' + suffix +
|
||||
'\Lib\site-packages\PyQt5\plugins\imageformats\qjpeg.dll',
|
||||
'C:\Python34' + suffix +
|
||||
'\Lib\site-packages\PyQt5\plugins\imageformats\qmng.dll',
|
||||
'C:\Python34' + suffix +
|
||||
'\Lib\site-packages\PyQt5\plugins\imageformats\qsvg.dll',
|
||||
'C:\Python34' + suffix +
|
||||
'\Lib\site-packages\PyQt5\plugins\imageformats\qtga.dll',
|
||||
'C:\Python34' + suffix +
|
||||
'\Lib\site-packages\PyQt5\plugins\imageformats\qtiff.dll',
|
||||
'C:\Python34' + suffix +
|
||||
'\Lib\site-packages\PyQt5\plugins\imageformats\qwbmp.dll']),
|
||||
('platforms', ['C:\Python34' + suffix +
|
||||
'\Lib\site-packages\PyQt5\plugins\platforms\qminimal.dll',
|
||||
'C:\Python34' + suffix +
|
||||
'\Lib\site-packages\PyQt5\plugins\platforms\qoffscreen.dll',
|
||||
'C:\Python34' + suffix +
|
||||
additional_files = [('platforms', ['C:\Python34' + suffix +
|
||||
'\Lib\site-packages\PyQt5\plugins\platforms\qwindows.dll']),
|
||||
('', ['LICENSE.txt',
|
||||
'other\\7za.exe',
|
||||
'other\\UnRAR.exe',
|
||||
'other\\Additional-LICENSE.txt',
|
||||
'other\\7za.exe',
|
||||
'C:\Python34' + suffix + '\Lib\site-packages\PyQt5\libEGL.dll'])]
|
||||
extra_options = dict(
|
||||
options={'py2exe': {"bundle_files": 2,
|
||||
options={'py2exe': {"bundle_files": 1,
|
||||
"dll_excludes": ["tcl85.dll", "tk85.dll"],
|
||||
"dist_dir": "dist" + suffix,
|
||||
"compressed": True,
|
||||
"includes": ["sip"],
|
||||
"excludes": ["tkinter"],
|
||||
"optimize": 2}},
|
||||
windows=[{"script": "kcc.py",
|
||||
windows=[{"script": MAIN,
|
||||
"dest_base": "KCC",
|
||||
"version": VERSION,
|
||||
"copyright": "Ciro Mattia Gonano, Pawel Jastrzebski © 2014",
|
||||
"copyright": "Ciro Mattia Gonano, Pawel Jastrzebski © 2012-2015",
|
||||
"legal_copyright": "ISC License (ISCL)",
|
||||
"product_version": VERSION,
|
||||
"product_name": "Kindle Comic Converter",
|
||||
@@ -114,7 +93,7 @@ else:
|
||||
print('Please use setup.sh to build Linux package.')
|
||||
exit()
|
||||
|
||||
#noinspection PyUnboundLocalVariable
|
||||
# noinspection PyUnboundLocalVariable
|
||||
setup(
|
||||
name=NAME,
|
||||
version=VERSION,
|
||||
|
||||
Reference in New Issue
Block a user