1
0
mirror of https://github.com/ciromattia/kcc synced 2026-04-15 13:38:46 +00:00

Compare commits

...

36 Commits
4.2.1 ... 4.4

Author SHA1 Message Date
Paweł Jastrzębski
5c2c6ed825 Tweaked OSX hack 2015-01-04 18:59:12 +01:00
Paweł Jastrzębski
c2730ab01c Merge pull request #122 from ciromattia/dev
4.4
2015-01-04 14:57:09 +01:00
Paweł Jastrzębski
bfba66d47d Improved KindleGen detection 2015-01-04 14:53:14 +01:00
Paweł Jastrzębski
b5bc2f8e00 Updated README + version bump 2015-01-04 10:49:59 +01:00
Paweł Jastrzębski
917eaef548 GUI tweaks 2015-01-04 10:27:13 +01:00
Paweł Jastrzębski
3187ebb054 Generic processing improvements 2015-01-04 09:13:20 +01:00
Paweł Jastrzębski
b9276e9ede WebToon processing improvements 2015-01-04 09:11:46 +01:00
Paweł Jastrzębski
147d815057 Code cleanup 2015-01-04 09:09:33 +01:00
Paweł Jastrzębski
180123fee2 Improved 7zip detection 2015-01-03 09:17:00 +01:00
Paweł Jastrzębski
2768e622f2 PyQT 5.4 workarounds 2015-01-02 11:51:14 +01:00
Paweł Jastrzębski
b629b45d46 Pillow update 2015-01-02 09:55:34 +01:00
Paweł Jastrzębski
f66c83425c PyQT 5.4 update 2015-01-01 15:24:59 +01:00
Paweł Jastrzębski
68b4b7114d Added RAR5 support 2014-12-30 11:07:11 +01:00
Paweł Jastrzębski
36f8c82eaf Fixed OSX race condition 2014-12-30 11:00:21 +01:00
Paweł Jastrzębski
bd665c3261 Merge pull request #119 from ciromattia/4.x
4.3.1
2014-11-24 20:13:17 +01:00
Paweł Jastrzębski
94586fa590 Version bump 2014-11-24 19:07:50 +01:00
Paweł Jastrzębski
89806dfcfc Disabled MCD integration 2014-11-24 18:56:24 +01:00
Paweł Jastrzębski
c9eb73ab90 OS X: Miscellaneous fixes (close #115) 2014-11-24 18:47:53 +01:00
Paweł Jastrzębski
2edcc0369a Updated UnRAR 2014-11-22 15:22:31 +01:00
Paweł Jastrzębski
bc0a52b848 PEP 8 2014-11-21 22:46:43 +01:00
Paweł Jastrzębski
5ae72bf06d Fixed Kindle Voyage profile 2014-11-21 22:41:51 +01:00
Paweł Jastrzębski
24c32643c1 Tweaked glob support 2014-11-10 19:33:03 +01:00
Paweł Jastrzębski
40b988f964 Merge branch 'multifile' of git://github.com/theaquamarine/kcc into 4.x 2014-10-29 07:14:39 +01:00
blue
ac794eff85 Use glob to resolve file globs
For systems where the shell doesn't do glob expansion, eg Windows.
2014-10-28 18:34:14 +00:00
blue
eaa387a9d6 Add basic support for converting more >1 file 2014-10-28 16:51:31 +00:00
Paweł Jastrzębski
d0f5d6dac4 Tweaked title sorting 2014-10-17 14:15:35 +02:00
Paweł Jastrzębski
b174534c1c Merge pull request #113 from ciromattia/4.x
4.3
2014-09-29 18:05:32 +02:00
Paweł Jastrzębski
19d486a4ee Updated README 2014-09-28 22:01:22 +02:00
Paweł Jastrzębski
652762b709 Updated README + version bump 2014-09-28 13:23:40 +02:00
Paweł Jastrzębski
e6df87f8fd CLI: Added tool detection 2014-09-28 13:16:13 +02:00
Paweł Jastrzębski
1a9cd0beb5 Moved MOBI creation to main code (close #111) 2014-09-28 13:01:42 +02:00
Paweł Jastrzębski
108e351126 Refactored locking mechanism (close #112) 2014-09-27 11:44:42 +02:00
Paweł Jastrzębski
dfe3e10470 Updated setup 2014-09-22 18:35:53 +02:00
Paweł Jastrzębski
787ad9fa66 Updated multiple device profiles 2014-09-18 19:05:24 +02:00
Paweł Jastrzębski
f10e8869a9 Added Kobo Aura H2O profile 2014-09-03 07:16:26 +02:00
Paweł Jastrzębski
715ada328f Tweaked error reporting (close #109) 2014-08-24 09:56:59 +02:00
24 changed files with 923 additions and 805 deletions

View File

@@ -71,7 +71,7 @@
<enum>Qt::NoFocus</enum> <enum>Qt::NoFocus</enum>
</property> </property>
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Disable image optimizations.&lt;br/&gt;Input images must be already resized.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Disable image optimizations.&lt;br/&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Input images must be already resized.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="text"> <property name="text">
<string>No optimisation</string> <string>No optimisation</string>
@@ -110,7 +110,7 @@
<enum>Qt::NoFocus</enum> <enum>Qt::NoFocus</enum>
</property> </property>
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Enable auto-splitting of webtoons like &lt;span style=&quot; font-style:italic;&quot;&gt;Tower of God&lt;/span&gt; or &lt;span style=&quot; font-style:italic;&quot;&gt;Noblesse&lt;/span&gt;.&lt;br/&gt;This mode was created for pages with a low width, high height and vertical panel flow.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Enable special parsing mode for WebToons.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="text"> <property name="text">
<string>Webtoon mode</string> <string>Webtoon mode</string>
@@ -167,7 +167,7 @@
<enum>Qt::NoFocus</enum> <enum>Qt::NoFocus</enum>
</property> </property>
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Disable splitting and rotation.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Disable page splitting and rotation.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="text"> <property name="text">
<string>No split/rotate</string> <string>No split/rotate</string>
@@ -241,7 +241,7 @@
<enum>Qt::NoFocus</enum> <enum>Qt::NoFocus</enum>
</property> </property>
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Shift+Click to select the output directory.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Shift+Click to select the output directory.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="text"> <property name="text">
<string>Convert</string> <string>Convert</string>
@@ -269,6 +269,9 @@
<property name="focusPolicy"> <property name="focusPolicy">
<enum>Qt::NoFocus</enum> <enum>Qt::NoFocus</enum>
</property> </property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Add directory containing JPG, PNG or GIF files to queue.&lt;br/&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;CBR, CBZ and CB7 files inside will not be processed!&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text"> <property name="text">
<string>Add directory</string> <string>Add directory</string>
</property> </property>
@@ -295,6 +298,9 @@
<property name="focusPolicy"> <property name="focusPolicy">
<enum>Qt::NoFocus</enum> <enum>Qt::NoFocus</enum>
</property> </property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Add CBR, CBZ, CB7 or PDF file to queue.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text"> <property name="text">
<string>Add file</string> <string>Add file</string>
</property> </property>
@@ -386,7 +392,7 @@
<enum>Qt::NoFocus</enum> <enum>Qt::NoFocus</enum>
</property> </property>
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Unchecked - Normal quality mode&lt;/span&gt;&lt;br/&gt;Maximal quality of images but very poor magnification quality.&lt;br/&gt;Use it only when zoom is not needed or output files needs to be small.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Indeterminate - High quality mode&lt;/span&gt;&lt;br/&gt;In most cases high quality of images and magnification.&lt;br/&gt;Overall quality highly depends on the resolution of source files.&lt;br/&gt;On Kindle models older than Paperwhite non-zoomed images might be a little blurred.&lt;br/&gt;&lt;br/&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Checked - Ultra quality mode&lt;/span&gt;&lt;br/&gt;Highest possible quality. Output files will be big.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Quality of Panel View/zoom. Highly impact size of output file.&lt;br/&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;This option control only quality of magnification!&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="text"> <property name="text">
<string>High/Ultra quality</string> <string>High/Ultra quality</string>
@@ -445,6 +451,9 @@
<property name="styleSheet"> <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> <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>
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOn</enum>
</property>
<property name="showDropIndicator" stdset="0"> <property name="showDropIndicator" stdset="0">
<bool>false</bool> <bool>false</bool>
</property> </property>
@@ -661,7 +670,7 @@
</font> </font>
</property> </property>
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Resolution of target device.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Resolution of target device.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="text"> <property name="text">
<string>Custom width: </string> <string>Custom width: </string>
@@ -694,7 +703,7 @@
<bool>false</bool> <bool>false</bool>
</property> </property>
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Resolution of target device.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Resolution of target device.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="inputMask"> <property name="inputMask">
<string>0000</string> <string>0000</string>
@@ -712,7 +721,7 @@
</font> </font>
</property> </property>
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Resolution of target device.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Resolution of target device.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="text"> <property name="text">
<string>Custom height: </string> <string>Custom height: </string>
@@ -745,7 +754,7 @@
<bool>false</bool> <bool>false</bool>
</property> </property>
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Resolution of target device.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Resolution of target device.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="inputMask"> <property name="inputMask">
<string>0000</string> <string>0000</string>

View File

@@ -69,7 +69,7 @@
<enum>Qt::NoFocus</enum> <enum>Qt::NoFocus</enum>
</property> </property>
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Disable image optimizations.&lt;br/&gt;Input images must be already resized.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Disable image optimizations.&lt;br/&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Input images must be already resized.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="text"> <property name="text">
<string>No optimisation</string> <string>No optimisation</string>
@@ -110,7 +110,7 @@
<enum>Qt::NoFocus</enum> <enum>Qt::NoFocus</enum>
</property> </property>
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Enable auto-splitting of webtoons like &lt;span style=&quot; font-style:italic;&quot;&gt;Tower of God &lt;/span&gt;or &lt;span style=&quot; font-style:italic;&quot;&gt;Noblesse&lt;/span&gt;.&lt;br/&gt;This mode was created for pages with a low width, high height and vertical panel flow.&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Enable special parsing mode for WebToons.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="text"> <property name="text">
<string>Webtoon mode</string> <string>Webtoon mode</string>
@@ -170,7 +170,7 @@
<enum>Qt::NoFocus</enum> <enum>Qt::NoFocus</enum>
</property> </property>
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Disable splitting and rotation.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Disable page splitting and rotation.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="text"> <property name="text">
<string>No split/rotate</string> <string>No split/rotate</string>
@@ -220,7 +220,7 @@
<enum>Qt::NoFocus</enum> <enum>Qt::NoFocus</enum>
</property> </property>
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Output format.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Output format.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
</widget> </widget>
<widget class="QPushButton" name="ConvertButton"> <widget class="QPushButton" name="ConvertButton">
@@ -244,7 +244,7 @@
<enum>Qt::NoFocus</enum> <enum>Qt::NoFocus</enum>
</property> </property>
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Shift+Click to select the output directory.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Shift+Click to select the output directory.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="text"> <property name="text">
<string>Convert</string> <string>Convert</string>
@@ -272,6 +272,9 @@
<property name="focusPolicy"> <property name="focusPolicy">
<enum>Qt::NoFocus</enum> <enum>Qt::NoFocus</enum>
</property> </property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Add directory containing JPG, PNG or GIF files to queue.&lt;br/&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;CBR, CBZ and CB7 files inside will not be processed!&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text"> <property name="text">
<string>Add directory</string> <string>Add directory</string>
</property> </property>
@@ -298,6 +301,9 @@
<property name="focusPolicy"> <property name="focusPolicy">
<enum>Qt::NoFocus</enum> <enum>Qt::NoFocus</enum>
</property> </property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Add CBR, CBZ, CB7 or PDF file to queue.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text"> <property name="text">
<string>Add file</string> <string>Add file</string>
</property> </property>
@@ -391,7 +397,7 @@
<enum>Qt::NoFocus</enum> <enum>Qt::NoFocus</enum>
</property> </property>
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Unchecked - Normal quality mode&lt;/span&gt;&lt;br/&gt;Maximal quality of images but very poor magnification quality.&lt;br/&gt;Use it only when zoom is not needed or output files needs to be small.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Indeterminate - High quality mode&lt;/span&gt;&lt;br/&gt;In most cases high quality of images and magnification.&lt;br/&gt;Overall quality highly depends on the resolution of source files.&lt;br/&gt;On Kindle models older than Paperwhite non-zoomed images might be a little blurred.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Checked - Ultra quality mode&lt;/span&gt;&lt;br/&gt;Highest possible quality. Output files will be big.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Quality of Panel View/zoom. Highly impact size of output file.&lt;br/&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;This option control only quality of magnification!&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="text"> <property name="text">
<string>High/Ultra quality</string> <string>High/Ultra quality</string>
@@ -450,6 +456,9 @@
<property name="styleSheet"> <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> <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>
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOn</enum>
</property>
<property name="showDropIndicator" stdset="0"> <property name="showDropIndicator" stdset="0">
<bool>false</bool> <bool>false</bool>
</property> </property>

17
KCC.ui
View File

@@ -65,7 +65,7 @@
<enum>Qt::NoFocus</enum> <enum>Qt::NoFocus</enum>
</property> </property>
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Disable image optimizations.&lt;br/&gt;Input images must be already resized.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Disable image optimizations.&lt;br/&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Input images must be already resized.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="text"> <property name="text">
<string>No optimisation</string> <string>No optimisation</string>
@@ -94,7 +94,7 @@
<enum>Qt::NoFocus</enum> <enum>Qt::NoFocus</enum>
</property> </property>
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style=&quot;white-space:pre&quot;&gt;Enable auto-splitting of webtoons like &lt;span style=&quot; font-style:italic;&quot;&gt;Tower of God&lt;/span&gt; or &lt;span style=&quot; font-style:italic;&quot;&gt;Noblesse&lt;/span&gt;.&lt;br/&gt;This mode is created for pages with a low width, high height and vertical panel flow.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Enable special parsing mode for WebToons.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="text"> <property name="text">
<string>Webtoon mode</string> <string>Webtoon mode</string>
@@ -136,7 +136,7 @@
<enum>Qt::NoFocus</enum> <enum>Qt::NoFocus</enum>
</property> </property>
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Disable splitting and rotation.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Disable page splitting and rotation.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="text"> <property name="text">
<string>No split/rotate</string> <string>No split/rotate</string>
@@ -234,6 +234,9 @@
<property name="focusPolicy"> <property name="focusPolicy">
<enum>Qt::NoFocus</enum> <enum>Qt::NoFocus</enum>
</property> </property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Add directory containing JPG, PNG or GIF files to queue.&lt;br/&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;CBR, CBZ and CB7 files inside will not be processed!&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text"> <property name="text">
<string>Add directory</string> <string>Add directory</string>
</property> </property>
@@ -259,6 +262,9 @@
<property name="focusPolicy"> <property name="focusPolicy">
<enum>Qt::NoFocus</enum> <enum>Qt::NoFocus</enum>
</property> </property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Add CBR, CBZ, CB7 or PDF file to queue.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text"> <property name="text">
<string>Add file</string> <string>Add file</string>
</property> </property>
@@ -338,7 +344,7 @@
<enum>Qt::NoFocus</enum> <enum>Qt::NoFocus</enum>
</property> </property>
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style=&quot;white-space:pre&quot;&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Unchecked - Normal quality mode&lt;br/&gt;&lt;/span&gt;Maximal quality of images but very poor magnification quality.&lt;br/&gt;Use it only when zoom is not needed or output files needs to be small.&lt;/p&gt;&lt;p style=&quot;white-space:pre&quot;&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Indeterminate - High quality mode&lt;br/&gt;&lt;/span&gt;In most cases high quality of images and magnification.&lt;br/&gt;Overall quality highly depends on the resolution of source files.&lt;br/&gt;On Kindle models older than Paperwhite non-zoomed images might be a little blurred.&lt;/p&gt;&lt;p style=&quot;white-space:pre&quot;&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Checked - Ultra quality mode&lt;br/&gt;&lt;/span&gt;Highest possible quality. Output files will be big.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;Quality of Panel View/zoom. Highly impact size of output file.&lt;br/&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;This option control only quality of magnification!&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="text"> <property name="text">
<string>High/Ultra quality</string> <string>High/Ultra quality</string>
@@ -385,6 +391,9 @@
<property name="styleSheet"> <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> <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>
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOn</enum>
</property>
<property name="showDropIndicator" stdset="0"> <property name="showDropIndicator" stdset="0">
<bool>false</bool> <bool>false</bool>
</property> </property>

View File

@@ -1,7 +1,7 @@
ISC LICENSE ISC LICENSE
Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com> 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 Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the any purpose with or without fee is hereby granted, provided that the

402
README.md
View File

@@ -1,20 +1,19 @@
# KCC # KCC
**Kindle Comic Converter** is a Python app to convert comic files or folders to ePub, Panel View MOBI or E-Ink optimized CBZ. **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 initally developed for Kindle but since v2.2 it outputs valid ePub 2.0 so _**despite its name, KCC is 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 to EPUB converter that every e-reader owner can happily use**_. 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. It can also optionally optimize images by applying a number of transformations.
### A word of warning ### 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. **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 ;-) _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 ### 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 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 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 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 find **KCC** valuable you can consider donating to the authors: If you find **KCC** valuable you can consider donating to the authors:
- Ciro Mattia Gonano: - Ciro Mattia Gonano:
@@ -32,11 +31,11 @@ You can find the latest released binary at the following links:
## INPUT FORMATS ## INPUT FORMATS
**KCC** can understand and convert, at the moment, the following input types: **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 - CBZ, ZIP
- CBR, RAR *(With `unrar` executable)* - CBR, RAR *(With `unrar` executable)*
- CB7, 7Z *(With `7za` executable)* - CB7, 7Z *(With `7za` executable)*
- PDF *(Extracting only contained JPG images)* - PDF *(Only extracting JPG images)*
## OPTIONAL REQUIREMENTS ## 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)* - [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: ### For running from source:
- Python 3.3+ - Python 3.3+
- [PyQt5](http://www.riverbankcomputing.co.uk/software/pyqt/download5) 5.2.0+ - [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+ - [psutil](https://pypi.python.org/pypi/psutil) 2.0+
- [python-slugify](http://pypi.python.org/pypi/python-slugify) - [python-slugify](http://pypi.python.org/pypi/python-slugify)
On Debian based distributions these two commands should install all dependencies: 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 sudo pip3 install pillow python-slugify psutil
``` ```
### For freezing code: ### For freezing code:
- Windows - [py2exe](https://pypi.python.org/pypi/py2exe) 0.9.2+ - Windows - [py2exe](https://pypi.python.org/pypi/py2exe) 0.9.2.2+
- OS X - [py2app](https://bitbucket.org/ronaldoussoren/py2app) 0.8.0+ - OS X - [py2app](https://bitbucket.org/ronaldoussoren/py2app) 0.9.0+
## USAGE ## 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. 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: ### Standalone `kcc-c2e.py` usage:
``` ```
@@ -75,7 +76,9 @@ Usage: kcc-c2e [options] comic_file|comic_folder
Options: Options:
MAIN: MAIN:
-p PROFILE, --profile=PROFILE -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 -q QUALITY, --quality=QUALITY
Quality of Panel View. 0 - Normal 1 - High 2 - Ultra [Default=0] Quality of Panel View. 0 - Normal 1 - High 2 - Ultra [Default=0]
-m, --manga-style Manga style (Right-to-left reading and splitting) -m, --manga-style Manga style (Right-to-left reading and splitting)
@@ -86,7 +89,9 @@ Options:
Output generated file to specified directory or file Output generated file to specified directory or file
-t TITLE, --title=TITLE -t TITLE, --title=TITLE
Comic title [Default=filename or directory name] 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 --batchsplit Split output into multiple files
PROCESSING: 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)). 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. - `DualMetaFix` script by **K. Hendricks**. Released with GPL-3 License.
- `rarfile.py` script &copy; 2005-2011 **Marko Kreen** <markokr@gmail.com>. Released with ISC License. - `rarfile.py` script &copy; 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. - `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. - 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 ## SAMPLE FILES CREATED BY KCC
* [Kindle Voyage](http://kcc.iosphe.re/Samples/Ubunchu!-KV.mobi)
* [Kindle Paperwhite](http://kcc.iosphe.re/Samples/Ubunchu!-KPW.mobi) * [Kindle Paperwhite](http://kcc.iosphe.re/Samples/Ubunchu!-KPW.mobi)
* [Kindle](http://kcc.iosphe.re/Samples/Ubunchu!-K345.mobi) * [Kindle](http://kcc.iosphe.re/Samples/Ubunchu!-K345.mobi)
* [Kindle DX/DXG](http://kcc.iosphe.re/Samples/Ubunchu!-KDX.mobi) * [Kindle DX/DXG](http://kcc.iosphe.re/Samples/Ubunchu!-KDX.cbz)
* [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)
* [Kobo Mini/Touch](http://kcc.iosphe.re/Samples/Ubunchu!-KoMT.cbz) * [Kobo Mini/Touch](http://kcc.iosphe.re/Samples/Ubunchu!-KoMT.cbz)
* [Kobo Glow](http://kcc.iosphe.re/Samples/Ubunchu!-KoG.cbz) * [Kobo Glow](http://kcc.iosphe.re/Samples/Ubunchu!-KoG.cbz)
* [Kobo Aura](http://kcc.iosphe.re/Samples/Ubunchu!-KoA.cbz) * [Kobo Aura](http://kcc.iosphe.re/Samples/Ubunchu!-KoA.cbz)
* [Kobo Aura HD](http://kcc.iosphe.re/Samples/Ubunchu!-KoAHD.cbz) * [Kobo Aura HD](http://kcc.iosphe.re/Samples/Ubunchu!-KoAHD.cbz)
* [Kobo Aura H2O](http://kcc.iosphe.re/Samples/Ubunchu!-KoAH2O.cbz)
## CHANGELOG ## CHANGELOG
####1.0 ####4.4:
* Initial version * 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 ####4.3.1:
* Added support for CBZ/CBR files in comic2ebook.py * 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 ####4.3:
* Added support for CBZ/CBR files in Kindle Comic Converter * Added profiles for Kindle Voyage and Kobo Aura H2O
* Added missing features to CLI version
* Other minor bug fixes
####1.2 ####4.2.1:
* 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! * 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 ####4.2:
* Fixed an issue in OPF generation for device resolution * Added [Manga Cover Database](http://manga.joentjuh.nl/) support
* Reworked options system (call with -h option to get the inline help) * 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 ####4.1:
* Added some options for controlling image optimization * Thanks to code contributed by Kevin Hendricks speed of MOBI creation was greatly increased
* Further optimization (ImageOps, page numbering cut, autocontrast) * 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 ####4.0.2:
* Fixed a serious bug on resizing when img ratio was bigger than device one * Fixed some Windows and OSX specific bugs
* Fixed problem with marigns when using HQ mode
####1.5 ####4.0.1:
* Added subfolder support for multiple chapters. * 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 ####4.0:
* GUI! AppleScript is gone and Tk is used to provide cross-platform GUI support. * 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 ####3.7.2:
* Added basic error reporting * Fixed problems with HQ mode
####2.2: ####3.7.1:
* Added (valid!) ePub 2.0 output * Hotfixed Kobo profiles
* Rename .zip files to .cbz to avoid overwriting
####2.3 ####3.7:
* Fixed win32 ePub generation, folder handling, filenames with spaces and subfolders * 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 ####3.6.2:
* Use temporary directory as workdir (fixes converting from external volumes and zipfiles renaming) * Fixed previous PNG output fix
* Fixed "add folders" from GUI. * Fixed Panel View anomalies
####2.5 ####3.6.1:
* Added --black-borders option to set added borders black when page's ratio is not the device's one (#11). * Fixed PNG output
* Fixes epub containing zipped itself (#10)
####2.6 ####3.6:
* Added --rotate option to rotate landscape images instead of splitting them (#16, #24) * Increased quality of Panel View zoom
* Added --output option to customize ePub output dir/file (#22) * Creation of multipart MOBI output is now faster on machines with 4GB+ RAM
* Add rendition:layout and rendition:orientation ePub meta tags (supported by new kindlegen 2.8) * Automatic gamma correction now distinguishes color and grayscale images
* Fixed natural sorting for files (#18) * 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 ####3.5:
* Lots of GUI improvements (#27, #13) * Added simple content server - Converted files can be now delivered wireless
* Added gamma support within --gamma option (defaults to profile-specified gamma) (#26, #27) * Added proper Windows installer
* Added --nodithering option to prevent dithering optimizations (#27) * Improved multiprocessing speed
* Epub margins support (#30) * GUI tweaks and minor bug fixes
* 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
####2.8 ####3.4:
* Updated rarfile library * Improved PNG output
* Panel View support + HQ support (#36) - new option: --nopanelviewhq * Increased quality of upscaling
* Split profiles for K4NT and K4T * Added support of file association - KCC can now open CBZ, CBR, CB7, ZIP, RAR, 7Z and PDF files directly
* Rewrite of Landscape Mode support (huge readability improvement for KPW) * Paths that contain UTF-8 characters are now supported
* Upscale use now BILINEAR method * Migrated to new version of Pillow library
* Added generic CSS file * Merged DX and DXG profiles
* Optimized archive extraction for zip/rar files (#40) * Many other minor bug fixes and GUI tweaks
####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.3: ####3.3:
* Margins are now automatically omitted in Panel View mode * 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 * Windows release is now bundled with UnRAR and 7za
* Small GUI tweaks * Small GUI tweaks
####3.4: ####3.2:
* Improved PNG output * Too big EPUB files are now splitted before conversion to MOBI
* Increased quality of upscaling * Added experimental parser of manga webtoons
* Added support of file association - KCC can now open CBZ, CBR, CB7, ZIP, RAR, 7Z and PDF files directly * Improved error handling
* 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.5: ####3.2.1:
* Added simple content server - Converted files can be now delivered wireless * Hotfixed crash occurring on OS with Russian locale
* Added proper Windows installer
* Improved multiprocessing speed
* GUI tweaks and minor bug fixes
####3.6: ####3.1:
* Increased quality of Panel View zoom * Added profile: Kindle for Android
* Creation of multipart MOBI output is now faster on machines with 4GB+ RAM * Add file/directory dialogs now support multiselect
* Automatic gamma correction now distinguishes color and grayscale images * Many small fixes and tweaks
* 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.6.1: ####3.0:
* Fixed PNG output * 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: ####2.10:
* Fixed previous PNG output fix * Multiprocessing support
* Fixed Panel View anomalies * 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: ####2.9
* Added profiles for KOBO devices * Added support for generating a plain CBZ (skipping all the EPUB/MOBI generation) (#45)
* Improved Panel View support * Prevent output file overwriting the source one: if a duplicate name is detected, append _kcc to the name
* Improved WebToon splitter * Rarfile library updated to 2.6
* Improved margin color autodetection * Added GIF, TIFF and BMP to supported formats (#42)
* Tweaked EPUB output * Filenames slugifications (#28, #31, #9, #8)
* Fixed stretching option
* GUI tweaks and minor bugfixes
####3.7.1: ####2.8
* Hotfixed Kobo profiles * 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: ####2.7
* Fixed problems with HQ mode * 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: ####2.6
* KCC now use Python 3.3 and Qt 5.2 * Added --rotate option to rotate landscape images instead of splitting them (#16, #24)
* Full UTF-8 awareness * Added --output option to customize EPUB output dir/file (#22)
* CBZ output now support Manga mode * Add rendition:layout and rendition:orientation EPUB meta tags (supported by new kindlegen 2.8)
* Improved Panel View support and margin color detection * Fixed natural sorting for files (#18)
* 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
####4.0.1: ####2.5
* Fixed file lock problems that plagued some Windows users * Added --black-borders option to set added borders black when page's ratio is not the device's one (#11).
* Fixed content server failing to start on Windows * Fixes EPUB containing zipped itself (#10)
* Improved performance of WebToon splitter
* Tweaked margin color detection
####4.0.2: ####2.4
* Fixed some Windows and OSX specific bugs * Use temporary directory as workdir (fixes converting from external volumes and zipfiles renaming)
* Fixed problem with marigns when using HQ mode * Fixed "add folders" from GUI.
####4.1: ####2.3
* Thanks to code contributed by Kevin Hendricks speed of MOBI creation was greatly increased * Fixed win32 EPUB generation, folder handling, filenames with spaces and subfolders
* 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
####4.2: ####2.2:
* Added [Manga Cover Database](http://manga.joentjuh.nl/) support * Added (valid!) EPUB 2.0 output
* Officially dropped Windows XP support * Rename .zip files to .cbz to avoid overwriting
* Fixed _Other_ profile
* Fixed problems with page order on stock KOBO CBZ reader
* Many other small bug fixes and tweaks
####4.2.1: ####2.1
* Improved margin color detection * Added basic error reporting
* Fixed random crashes of MOBI processing step
* Fixed resizing problems in high quality mode ####2.0
* Fixed some MCD support bugs * GUI! AppleScript is gone and Tk is used to provide cross-platform GUI support.
* Default output format for Kindle DX is now CBZ
####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 ## KNOWN ISSUES
Please check [wiki page](https://github.com/ciromattia/kcc/wiki/Known-issues). Please check [wiki page](https://github.com/ciromattia/kcc/wiki/Known-issues).
## COPYRIGHT ## 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. **KCC** is released under ISC LICENSE; see LICENSE.txt for further details.

View File

@@ -2,7 +2,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@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>
# #
# Permission to use, copy, modify, and/or distribute this software for # Permission to use, copy, modify, and/or distribute this software for
# any purpose with or without fee is hereby granted, provided that the # 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 # TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE. # PERFORMANCE OF THIS SOFTWARE.
__version__ = '4.2.1' __version__ = '4.4'
__license__ = 'ISC' __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' __docformat__ = 'restructuredtext en'
import sys import sys
@@ -28,39 +28,8 @@ if sys.version_info[0] != 3:
print('ERROR: This is Python 3 script!') print('ERROR: This is Python 3 script!')
exit(1) exit(1)
# Dependency check from kcc.shared import dependencyCheck
missing = [] dependencyCheck(2)
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 multiprocessing import freeze_support from multiprocessing import freeze_support
from kcc.comic2ebook import main from kcc.comic2ebook import main

View File

@@ -2,7 +2,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@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>
# #
# Permission to use, copy, modify, and/or distribute this software for # Permission to use, copy, modify, and/or distribute this software for
# any purpose with or without fee is hereby granted, provided that the # 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 # TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE. # PERFORMANCE OF THIS SOFTWARE.
__version__ = '4.2.1' __version__ = '4.4'
__license__ = 'ISC' __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' __docformat__ = 'restructuredtext en'
import sys import sys
@@ -28,33 +28,14 @@ if sys.version_info[0] != 3:
print('ERROR: This is Python 3 script!') print('ERROR: This is Python 3 script!')
exit(1) exit(1)
# Dependency check from kcc.shared import dependencyCheck
missing = [] dependencyCheck(1)
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 multiprocessing import freeze_support from multiprocessing import freeze_support
from kcc.comic2panel import main from kcc.comic2panel import main
if __name__ == "__main__": if __name__ == "__main__":
freeze_support() 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:]) main(sys.argv[1:])
sys.exit(0) sys.exit(0)

View File

@@ -1,5 +1,5 @@
#define MyAppName "Kindle Comic Converter" #define MyAppName "Kindle Comic Converter"
#define MyAppVersion "4.2.1" #define MyAppVersion "4.4"
#define MyAppPublisher "Ciro Mattia Gonano, Paweł Jastrzębski" #define MyAppPublisher "Ciro Mattia Gonano, Paweł Jastrzębski"
#define MyAppURL "http://kcc.iosphe.re/" #define MyAppURL "http://kcc.iosphe.re/"
#define MyAppExeName "KCC.exe" #define MyAppExeName "KCC.exe"
@@ -12,7 +12,7 @@ AppPublisher={#MyAppPublisher}
AppPublisherURL={#MyAppURL} AppPublisherURL={#MyAppURL}
AppSupportURL={#MyAppURL} AppSupportURL={#MyAppURL}
AppUpdatesURL={#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} DefaultDirName={pf}\{#MyAppName}
DefaultGroupName={#MyAppName} DefaultGroupName={#MyAppName}
AllowNoIcons=yes AllowNoIcons=yes
@@ -43,13 +43,11 @@ Name: "CB7association"; Description: "CB7"; GroupDescription: "File associations
[Files] [Files]
; x64 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\platforms\*"; DestDir: "{app}\platforms\"; Flags: ignoreversion; Check: Is64BitInstallMode
Source: "dist_64\{#MyAppExeName}"; DestDir: "{app}"; 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: "dist_64\*.dll"; DestDir: "{app}"; Flags: ignoreversion; Check: Is64BitInstallMode
Source: "other\vcredist_x64.exe"; DestDir: "{tmp}"; Flags: ignoreversion deleteafterinstall; Check: Is64BitInstallMode Source: "other\vcredist_x64.exe"; DestDir: "{tmp}"; Flags: ignoreversion deleteafterinstall; Check: Is64BitInstallMode
; x86 files ; 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\platforms\*"; DestDir: "{app}\platforms\"; Flags: ignoreversion; Check: not Is64BitInstallMode
Source: "dist\{#MyAppExeName}"; DestDir: "{app}"; 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 Source: "dist\*.dll"; DestDir: "{app}"; Flags: ignoreversion; Check: not Is64BitInstallMode

151
kcc.py
View File

@@ -2,7 +2,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@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>
# #
# Permission to use, copy, modify, and/or distribute this software for # Permission to use, copy, modify, and/or distribute this software for
# any purpose with or without fee is hereby granted, provided that the # 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 # TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE. # PERFORMANCE OF THIS SOFTWARE.
__version__ = '4.2.1' __version__ = '4.4'
__license__ = 'ISC' __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' __docformat__ = 'restructuredtext en'
import sys import sys
@@ -28,156 +28,47 @@ if sys.version_info[0] != 3:
print('ERROR: This is Python 3 script!') print('ERROR: This is Python 3 script!')
exit(1) 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 # OS specific PATH variable workarounds
import os
if sys.platform.startswith('darwin'): if sys.platform.startswith('darwin'):
if 'RESOURCEPATH' in os.environ: if 'RESOURCEPATH' not in os.environ:
os.environ['PATH'] = os.environ['RESOURCEPATH'] + ':' + os.environ['PATH']
else:
os.environ['PATH'] = os.path.dirname(os.path.abspath(__file__)) + '/other/:' + os.environ['PATH'] 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'): elif sys.platform.startswith('win'):
if getattr(sys, 'frozen', False): if getattr(sys, 'frozen', False):
os.chdir(os.path.dirname(os.path.abspath(sys.executable))) os.chdir(os.path.dirname(os.path.abspath(sys.executable)))
# Implementing dummy stdout and stderr for frozen Windows release # Implementing dummy stdout and stderr for frozen Windows release
class fakestd(object): class FakeSTD(object):
def write(self, string): def write(self, string):
pass pass
def flush(self): def flush(self):
pass pass
sys.stdout = fakestd() sys.stdout = FakeSTD()
sys.stderr = fakestd() sys.stderr = FakeSTD()
else: else:
os.environ['PATH'] = os.path.dirname(os.path.abspath(__file__)) + '/other/;' + os.environ['PATH'] os.environ['PATH'] = os.path.dirname(os.path.abspath(__file__)) + '/other/;' + os.environ['PATH']
os.chdir(os.path.dirname(os.path.abspath(__file__))) 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 from multiprocessing import freeze_support
class QApplicationMessaging(QtWidgets.QApplication): from kcc import KCC_gui
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)
if __name__ == "__main__": if __name__ == "__main__":
freeze_support() freeze_support()
KCCAplication = QApplicationMessaging(sys.argv) KCCAplication = KCC_gui.QApplicationMessaging(sys.argv)
if KCCAplication.isRunning(): if KCCAplication.isRunning():
if len(sys.argv) > 1: if len(sys.argv) > 1:
KCCAplication.sendMessage(sys.argv[1]) KCCAplication.sendMessage(sys.argv[1])
sys.exit(0)
else: else:
KCCAplication.sendMessage('ARISE') KCCAplication.sendMessage('ARISE')
sys.exit(0) else:
KCCWindow = QMainWindowKCC() KCCWindow = KCC_gui.QMainWindowKCC()
KCCUI = KCC_gui.KCCGUI(KCCAplication, KCCWindow) KCCUI = KCC_gui.KCCGUI(KCCAplication, KCCWindow)
if len(sys.argv) > 1: if len(sys.argv) > 1:
KCCUI.handleMessage(sys.argv[1]) KCCUI.handleMessage(sys.argv[1])
sys.exit(KCCAplication.exec_()) sys.exit(KCCAplication.exec_())

View File

@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@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>
# #
# Permission to use, copy, modify, and/or distribute this software for # Permission to use, copy, modify, and/or distribute this software for
# any purpose with or without fee is hereby granted, provided that the # 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 # TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE. # PERFORMANCE OF THIS SOFTWARE.
__version__ = '4.2.1' __version__ = '4.4'
__license__ = 'ISC' __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' __docformat__ = 'restructuredtext en'
import os import os
@@ -33,15 +33,12 @@ from shutil import move
from http.server import BaseHTTPRequestHandler, HTTPServer from http.server import BaseHTTPRequestHandler, HTTPServer
from socketserver import ThreadingMixIn from socketserver import ThreadingMixIn
from subprocess import STDOUT, PIPE from subprocess import STDOUT, PIPE
from PyQt5 import QtGui, QtCore, QtWidgets from PyQt5 import QtGui, QtCore, QtWidgets, QtNetwork
from xml.dom.minidom import parse from xml.dom.minidom import parse
from html.parser import HTMLParser from psutil import Popen, Process
from psutil import virtual_memory, Popen, Process
from uuid import uuid4
from copy import copy from copy import copy
from .shared import md5Checksum from .shared import md5Checksum, HTMLStripper
from . import comic2ebook from . import comic2ebook
from . import dualmetafix
from . import KCC_rc_web from . import KCC_rc_web
if sys.platform.startswith('darwin'): if sys.platform.startswith('darwin'):
from . import KCC_ui_osx as KCC_ui from . import KCC_ui_osx as KCC_ui
@@ -51,6 +48,64 @@ else:
from . import KCC_ui 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: class Icons:
def __init__(self): def __init__(self):
self.deviceKindle = QtGui.QIcon() 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) 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): class WebServerHandler(BaseHTTPRequestHandler):
# noinspection PyAttributeOutsideInit, PyArgumentList # noinspection PyAttributeOutsideInit, PyArgumentList
def do_GET(self): def do_GET(self):
@@ -113,15 +155,15 @@ class WebServerHandler(BaseHTTPRequestHandler):
self.send_header('Content-type', 'text/html') self.send_header('Content-type', 'text/html')
self.end_headers() self.end_headers()
self.wfile.write(bytes('<!DOCTYPE html>\n' self.wfile.write(bytes('<!DOCTYPE html>\n'
'<html lang="en">\n' '<html lang="en">\n'
'<head><meta charset="utf-8">\n' '<head><meta charset="utf-8">\n'
'<link href="' + GUI.webContent.favicon + '" rel="icon" type="image/x-icon" />\n' '<link href="' + GUI.webContent.favicon + '" rel="icon" type="image/x-icon" />\n'
'<title>Kindle Comic Converter</title>\n' '<title>Kindle Comic Converter</title>\n'
'</head>\n' '</head>\n'
'<body>\n' '<body>\n'
'<div style="text-align: center; font-size:25px">\n' '<div style="text-align: center; font-size:25px">\n'
'<p style="font-size:50px">- <img style="vertical-align: middle" ' '<p style="font-size:50px">- <img style="vertical-align: middle" '
'alt="KCC Logo" src="' + GUI.webContent.logo + '" /> -</p>\n', 'UTF-8')) 'alt="KCC Logo" src="' + GUI.webContent.logo + '" /> -</p>\n', 'UTF-8'))
if len(GUI.completedWork) > 0 and not GUI.conversionAlive: if len(GUI.completedWork) > 0 and not GUI.conversionAlive:
for key in sorted(GUI.completedWork.keys()): for key in sorted(GUI.completedWork.keys()):
self.wfile.write(bytes('<p><a href="' + key + '">' + key.split('.')[0] + '</a></p>\n', 'UTF-8')) 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/>' 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')) 'Convert some files and refresh this page.</p>\n', 'UTF-8'))
self.wfile.write(bytes('</div>\n' self.wfile.write(bytes('</div>\n'
'</body>\n' '</body>\n'
'</html>\n', 'UTF-8')) '</html>\n', 'UTF-8'))
elif sendReply: elif sendReply:
outputFile = GUI.completedWork[unquote(self.path[1:])] outputFile = GUI.completedWork[unquote(self.path[1:])]
fp = open(outputFile, 'rb') fp = open(outputFile, 'rb')
@@ -265,84 +307,14 @@ class ProgressThread(QtCore.QThread):
self.running = False 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): class WorkerThread(QtCore.QThread):
#noinspection PyArgumentList # noinspection PyArgumentList
def __init__(self): def __init__(self):
QtCore.QThread.__init__(self) QtCore.QThread.__init__(self)
self.pool = QtCore.QThreadPool()
self.conversionAlive = False self.conversionAlive = False
self.errors = False self.errors = False
self.kindlegenErrorCode = [0] self.kindlegenErrorCode = [0]
self.workerOutput = [] 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.progressBarTick = MW.progressBarTick
self.addMessage = MW.addMessage self.addMessage = MW.addMessage
@@ -361,9 +333,11 @@ class WorkerThread(QtCore.QThread):
MW.addTrayMessage.emit('Conversion interrupted.', 'Critical') MW.addTrayMessage.emit('Conversion interrupted.', 'Critical')
MW.modeConvert.emit(1) MW.modeConvert.emit(1)
def addResult(self, output): def sanitizeTrace(self, traceback):
MW.progressBarTick.emit('tick') return ''.join(format_tb(traceback))\
self.workerOutput.append(output) .replace('C:\\Users\\AcidWeb\\Documents\\Projekty\\KCC\\', '')\
.replace('C:\\Python34\\', '')\
.replace('C:\\Python34_64\\', '')
def run(self): def run(self):
MW.modeConvert.emit(0) MW.modeConvert.emit(0)
@@ -385,10 +359,9 @@ class WorkerThread(QtCore.QThread):
options.quality = 1 options.quality = 1
elif GUI.QualityBox.checkState() == 2: elif GUI.QualityBox.checkState() == 2:
options.quality = 2 options.quality = 2
if str(GUI.FormatBox.currentText()) == 'CBZ': options.format = str(GUI.FormatBox.currentText())
options.cbzoutput = True
if GUI.currentMode == 1: if GUI.currentMode == 1:
if profile in ['KFHD', 'KFHD8', 'KFHDX', 'KFHDX8']: if 'KFH' in profile:
options.upscale = True options.upscale = True
# Advanced mode settings # Advanced mode settings
@@ -410,10 +383,7 @@ class WorkerThread(QtCore.QThread):
if GUI.WebtoonBox.isChecked(): if GUI.WebtoonBox.isChecked():
options.webtoon = True options.webtoon = True
if float(GUI.GammaValue) > 0.09: if float(GUI.GammaValue) > 0.09:
# noinspection PyTypeChecker
options.gamma = float(GUI.GammaValue) options.gamma = float(GUI.GammaValue)
if str(GUI.FormatBox.currentText()) == 'MOBI':
options.batchsplit = True
# Other/custom settings. # Other/custom settings.
if GUI.currentMode > 2: if GUI.currentMode > 2:
@@ -462,7 +432,7 @@ class WorkerThread(QtCore.QThread):
self.errors = True self.errors = True
_, _, traceback = sys.exc_info() _, _, traceback = sys.exc_info()
MW.showDialog.emit("Error during conversion %s:\n\n%s\n\nTraceback:\n%s" 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.addMessage.emit('Failed to create EPUB!', 'error', False)
MW.addTrayMessage.emit('Failed to create EPUB!', 'Critical') MW.addTrayMessage.emit('Failed to create EPUB!', 'Critical')
if not self.conversionAlive: if not self.conversionAlive:
@@ -483,16 +453,10 @@ class WorkerThread(QtCore.QThread):
MW.progressBarTick.emit('tick') MW.progressBarTick.emit('tick')
MW.addMessage.emit('Creating MOBI files', 'info', False) MW.addMessage.emit('Creating MOBI files', 'info', False)
GUI.progress.content = 'Creating MOBI files' GUI.progress.content = 'Creating MOBI files'
self.workerOutput = [] work = []
# Number of KindleGen threads depends on the size of RAM
self.pool.setMaxThreadCount(self.threadNumber)
for item in outputPath: for item in outputPath:
worker = KindleGenThread(item) work.append([item])
worker.signals.result.connect(self.addResult) self.workerOutput = comic2ebook.makeMOBI(work, self)
self.pool.start(worker)
self.pool.waitForDone()
while len(self.workerOutput) != len(outputPath):
sleep(0.1)
self.kindlegenErrorCode = [0] self.kindlegenErrorCode = [0]
for errors in self.workerOutput: for errors in self.workerOutput:
if errors[0] != 0: if errors[0] != 0:
@@ -512,15 +476,9 @@ class WorkerThread(QtCore.QThread):
MW.addMessage.emit('Processing MOBI files', 'info', False) MW.addMessage.emit('Processing MOBI files', 'info', False)
GUI.progress.content = 'Processing MOBI files' GUI.progress.content = 'Processing MOBI files'
self.workerOutput = [] self.workerOutput = []
# DualMetaFix is very fast and there is not reason to use multithreading.
self.pool.setMaxThreadCount(1)
for item in outputPath: for item in outputPath:
worker = DualMetaFixThread(item) self.workerOutput.append(comic2ebook.makeMOBIFix(item))
worker.signals.result.connect(self.addResult) MW.progressBarTick.emit('tick')
self.pool.start(worker)
self.pool.waitForDone()
while len(self.workerOutput) != len(outputPath):
sleep(0.1)
for success in self.workerOutput: for success in self.workerOutput:
if not success[0]: if not success[0]:
self.errors = True self.errors = True
@@ -611,6 +569,7 @@ class KCCGUI(KCC_ui.Ui_KCC):
dname = dname.replace('/', '\\') dname = dname.replace('/', '\\')
self.lastPath = os.path.abspath(os.path.join(dname, os.pardir)) self.lastPath = os.path.abspath(os.path.join(dname, os.pardir))
GUI.JobList.addItem(dname) GUI.JobList.addItem(dname)
GUI.JobList.scrollToBottom()
def selectFile(self): def selectFile(self):
if self.needClean: if self.needClean:
@@ -636,6 +595,7 @@ class KCCGUI(KCC_ui.Ui_KCC):
fname = fname.replace('/', '\\') fname = fname.replace('/', '\\')
self.lastPath = os.path.abspath(os.path.join(fname, os.pardir)) self.lastPath = os.path.abspath(os.path.join(fname, os.pardir))
GUI.JobList.addItem(fname) GUI.JobList.addItem(fname)
GUI.JobList.scrollToBottom()
def clearJobs(self): def clearJobs(self):
GUI.JobList.clear() GUI.JobList.clear()
@@ -971,15 +931,19 @@ class KCCGUI(KCC_ui.Ui_KCC):
self.addMessage('Target resolution is not set!', 'error') self.addMessage('Target resolution is not set!', 'error')
self.needClean = True self.needClean = True
return return
if str(GUI.FormatBox.currentText()) == 'MOBI' and not GUI.KindleGen: if str(GUI.FormatBox.currentText()) == 'MOBI' and not self.KindleGen:
self.addMessage('Cannot find <a href="http://www.amazon.com/gp/feature.html?ie=UTF8&docId=1000765211">' self.detectKindleGen()
'<b>KindleGen</b></a>! MOBI conversion is not possible!', 'error') if not self.KindleGen:
if sys.platform.startswith('win'): GUI.JobList.clear()
self.addMessage('Download it and place EXE in KCC directory.', 'error') self.addMessage('Cannot find <a href="http://www.amazon.com/gp/feature.html'
else: '?ie=UTF8&docId=1000765211"><b>KindleGen</b></a>!'
self.addMessage('Download it, and place executable in /usr/local/bin directory.', 'error') ' MOBI conversion is unavailable!', 'error')
self.needClean = True if sys.platform.startswith('win'):
return 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() self.worker.start()
def hideProgressBar(self): def hideProgressBar(self):
@@ -1018,7 +982,6 @@ class KCCGUI(KCC_ui.Ui_KCC):
'GammaSlider': float(self.GammaValue)*100}) 'GammaSlider': float(self.GammaValue)*100})
self.settings.sync() self.settings.sync()
self.tray.hide() self.tray.hide()
APP.shutdown()
def handleMessage(self, message): def handleMessage(self, message):
MW.raise_() MW.raise_()
@@ -1041,10 +1004,12 @@ class KCCGUI(KCC_ui.Ui_KCC):
formats = ['.cbz', '.zip', '.pdf'] formats = ['.cbz', '.zip', '.pdf']
if os.path.isdir(message): if os.path.isdir(message):
GUI.JobList.addItem(message) GUI.JobList.addItem(message)
GUI.JobList.scrollToBottom()
elif os.path.isfile(message): elif os.path.isfile(message):
extension = os.path.splitext(message) extension = os.path.splitext(message)
if extension[1].lower() in formats: if extension[1].lower() in formats:
GUI.JobList.addItem(message) GUI.JobList.addItem(message)
GUI.JobList.scrollToBottom()
else: else:
self.addMessage('This file type is unsupported!', 'error') self.addMessage('This file type is unsupported!', 'error')
@@ -1066,6 +1031,36 @@ class KCCGUI(KCC_ui.Ui_KCC):
self.saveSettings(None) self.saveSettings(None)
sys.exit(0) 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 # noinspection PyArgumentList
def __init__(self, KCCAplication, KCCWindow): def __init__(self, KCCAplication, KCCWindow):
global APP, MW, GUI global APP, MW, GUI
@@ -1096,6 +1091,7 @@ class KCCGUI(KCC_ui.Ui_KCC):
self.tray = SystemTrayIcon() self.tray = SystemTrayIcon()
self.conversionAlive = False self.conversionAlive = False
self.needClean = True self.needClean = True
self.KindleGen = False
self.GammaValue = 1.0 self.GammaValue = 1.0
self.completedWork = {} self.completedWork = {}
self.targetDirectory = '' self.targetDirectory = ''
@@ -1103,11 +1099,13 @@ class KCCGUI(KCC_ui.Ui_KCC):
self.listFontSize = 11 self.listFontSize = 11
self.statusBarFontSize = 10 self.statusBarFontSize = 10
self.statusBarStyle = 'QLabel{padding-top:2px;padding-bottom:3px;}' 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'): elif sys.platform.startswith('linux'):
self.listFontSize = 8 self.listFontSize = 8
self.statusBarFontSize = 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;}') self.statusBar.setStyleSheet('QStatusBar::item{border:0px;border-top:2px solid #C2C7CB;}')
else: else:
self.listFontSize = 9 self.listFontSize = 9
@@ -1121,22 +1119,20 @@ class KCCGUI(KCC_ui.Ui_KCC):
self.p.ionice(1) self.p.ionice(1)
self.profiles = { self.profiles = {
"Kindle Voyage": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': False, 'Label': 'KV'},
"Kindle Paperwhite": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0, "Kindle Paperwhite": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': False, 'Label': 'KHD'}, 'DefaultUpscale': False, 'Label': 'KPW'},
"Kindle": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0, "Kindle": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': False, 'Label': 'K345'}, 'DefaultUpscale': False, 'Label': 'K345'},
"Kindle DX/DXG": {'Quality': False, 'ForceExpert': False, 'DefaultFormat': 2, "Kindle DX/DXG": {'Quality': False, 'ForceExpert': False, 'DefaultFormat': 2,
'DefaultUpscale': False, 'Label': 'KDX'}, 'DefaultUpscale': False, 'Label': 'KDX'},
"Kindle Fire": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0, "K. Fire HD": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': False, 'Label': 'KF'}, 'DefaultUpscale': True, 'Label': 'KFHD'},
"K. Fire HD 7\"": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0, "K. Fire HDX": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': True, 'Label': 'KFHD'}, 'DefaultUpscale': True, 'Label': 'KFHDX'},
"K. Fire HD 8.9\"": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0, "K. Fire HDX 8.9": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': True, 'Label': 'KFHD8'}, 'DefaultUpscale': True, 'Label': 'KFHDX8'},
"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'},
"Kobo Mini/Touch": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 2, "Kobo Mini/Touch": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 2,
'DefaultUpscale': False, 'Label': 'KoMT'}, 'DefaultUpscale': False, 'Label': 'KoMT'},
"Kobo Glow": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 2, "Kobo Glow": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 2,
@@ -1145,6 +1141,8 @@ class KCCGUI(KCC_ui.Ui_KCC):
'DefaultUpscale': False, 'Label': 'KoA'}, 'DefaultUpscale': False, 'Label': 'KoA'},
"Kobo Aura HD": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 2, "Kobo Aura HD": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 2,
'DefaultUpscale': False, 'Label': 'KoAHD'}, 'DefaultUpscale': False, 'Label': 'KoAHD'},
"Kobo Aura H2O": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 2,
'DefaultUpscale': False, 'Label': 'KoAH2O'},
"Other": {'Quality': False, 'ForceExpert': True, 'DefaultFormat': 1, "Other": {'Quality': False, 'ForceExpert': True, 'DefaultFormat': 1,
'DefaultUpscale': False, 'Label': 'OTHER'}, 'DefaultUpscale': False, 'Label': 'OTHER'},
"Kindle for Android": {'Quality': False, 'ForceExpert': True, 'DefaultFormat': 0, "Kindle for Android": {'Quality': False, 'ForceExpert': True, 'DefaultFormat': 0,
@@ -1155,26 +1153,26 @@ class KCCGUI(KCC_ui.Ui_KCC):
'DefaultUpscale': False, 'Label': 'K2'} 'DefaultUpscale': False, 'Label': 'K2'}
} }
profilesGUI = [ profilesGUI = [
"Kindle Voyage",
"Kindle Paperwhite", "Kindle Paperwhite",
"Kindle", "Kindle",
"Kindle DX/DXG",
"Separator", "Separator",
"Kindle Fire", "K. Fire HD",
"K. Fire HD 7\"", "K. Fire HDX",
"K. Fire HD 8.9\"", "K. Fire HDX 8.9",
"K. Fire HDX 7\"",
"K. Fire HDX 8.9\"",
"Separator", "Separator",
"Kobo Mini/Touch", "Kobo Mini/Touch",
"Kobo Glow", "Kobo Glow",
"Kobo Aura", "Kobo Aura",
"Kobo Aura HD", "Kobo Aura HD",
"Kobo Aura H2O",
"Separator", "Separator",
"Other", "Other",
"Separator", "Separator",
"Kindle for Android", "Kindle for Android",
"Kindle 1", "Kindle 1",
"Kindle 2", "Kindle 2",
"Kindle DX/DXG",
] ]
statusBarLabel = QtWidgets.QLabel('<b><a href="http://kcc.iosphe.re/">HOMEPAGE</a> - <a href="https://github.' statusBarLabel = QtWidgets.QLabel('<b><a href="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 ' 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>.', '<a href="https://github.com/ciromattia/kcc/wiki/Important-tips">important tips</a>.',
'info') '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 = Popen('unrar', stdout=PIPE, stderr=STDOUT, shell=True)
rarExitCode = rarExitCode.wait() rarExitCode = rarExitCode.wait()
if rarExitCode == 0 or rarExitCode == 7: if rarExitCode == 0 or rarExitCode == 7:
@@ -1232,6 +1209,7 @@ class KCCGUI(KCC_ui.Ui_KCC):
self.sevenza = False self.sevenza = False
self.addMessage('Cannot find <a href="http://www.7-zip.org/download.html">7za</a>!' self.addMessage('Cannot find <a href="http://www.7-zip.org/download.html">7za</a>!'
' Processing of CB7/7Z files will be disabled.', 'warning') ' Processing of CB7/7Z files will be disabled.', 'warning')
self.detectKindleGen(True)
APP.messageFromOtherInstance.connect(self.handleMessage) APP.messageFromOtherInstance.connect(self.handleMessage)
GUI.BasicModeButton.clicked.connect(self.modeBasic) GUI.BasicModeButton.clicked.connect(self.modeBasic)

View File

@@ -2,8 +2,8 @@
# Form implementation generated from reading ui file 'KCC.ui' # Form implementation generated from reading ui file 'KCC.ui'
# #
# Created: Sun May 18 09:08:27 2014 # Created: Sun Jan 4 09:58:25 2015
# by: PyQt5 UI code generator 5.2.1 # by: PyQt5 UI code generator 5.4
# #
# WARNING! All changes made in this file will be lost! # 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.setGeometry(QtCore.QRect(10, 50, 401, 101))
self.JobList.setFocusPolicy(QtCore.Qt.NoFocus) 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.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.setProperty("showDropIndicator", False)
self.JobList.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection) self.JobList.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection)
self.JobList.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel) self.JobList.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel)
@@ -260,28 +261,30 @@ class Ui_KCC(object):
def retranslateUi(self, KCC): def retranslateUi(self, KCC):
_translate = QtCore.QCoreApplication.translate _translate = QtCore.QCoreApplication.translate
KCC.setWindowTitle(_translate("KCC", "Kindle Comic Converter")) 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.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.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.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.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.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.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.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.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.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.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.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.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.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.FileButton.setText(_translate("KCC", "Add file"))
self.ClearButton.setText(_translate("KCC", "Clear list")) 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.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.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.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.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")) self.RotateBox.setText(_translate("KCC", "Horizontal mode"))

View File

@@ -2,8 +2,8 @@
# Form implementation generated from reading ui file 'KCC-Linux.ui' # Form implementation generated from reading ui file 'KCC-Linux.ui'
# #
# Created: Sun May 18 09:08:37 2014 # Created: Sun Jan 4 10:06:14 2015
# by: PyQt5 UI code generator 5.2.1 # by: PyQt5 UI code generator 5.4
# #
# WARNING! All changes made in this file will be lost! # WARNING! All changes made in this file will be lost!
@@ -179,6 +179,7 @@ class Ui_KCC(object):
self.JobList.setFont(font) self.JobList.setFont(font)
self.JobList.setFocusPolicy(QtCore.Qt.NoFocus) 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.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.setProperty("showDropIndicator", False)
self.JobList.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection) self.JobList.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection)
self.JobList.setIconSize(QtCore.QSize(18, 18)) self.JobList.setIconSize(QtCore.QSize(18, 18))
@@ -329,28 +330,30 @@ class Ui_KCC(object):
def retranslateUi(self, KCC): def retranslateUi(self, KCC):
_translate = QtCore.QCoreApplication.translate _translate = QtCore.QCoreApplication.translate
KCC.setWindowTitle(_translate("KCC", "Kindle Comic Converter")) 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.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.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.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.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.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.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.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.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.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.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.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.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.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.FileButton.setText(_translate("KCC", "Add file"))
self.ClearButton.setText(_translate("KCC", "Clear list")) 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.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.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.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.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")) self.RotateBox.setText(_translate("KCC", "Horizontal mode"))
@@ -359,13 +362,13 @@ class Ui_KCC(object):
self.GammaLabel.setText(_translate("KCC", "Gamma: Auto")) 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.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.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.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.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.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.customHeight.setInputMask(_translate("KCC", "0000"))
self.ActionBasic.setText(_translate("KCC", "Basic")) self.ActionBasic.setText(_translate("KCC", "Basic"))
self.ActionAdvanced.setText(_translate("KCC", "Advanced")) self.ActionAdvanced.setText(_translate("KCC", "Advanced"))

View File

@@ -2,8 +2,8 @@
# Form implementation generated from reading ui file 'KCC-OSX.ui' # Form implementation generated from reading ui file 'KCC-OSX.ui'
# #
# Created: Sun May 18 09:08:44 2014 # Created: Sun Jan 4 10:26:09 2015
# by: PyQt5 UI code generator 5.2.1 # by: PyQt5 UI code generator 5.4
# #
# WARNING! All changes made in this file will be lost! # WARNING! All changes made in this file will be lost!
@@ -186,6 +186,7 @@ class Ui_KCC(object):
self.JobList.setFont(font) self.JobList.setFont(font)
self.JobList.setFocusPolicy(QtCore.Qt.NoFocus) 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.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.setProperty("showDropIndicator", False)
self.JobList.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection) self.JobList.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection)
self.JobList.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel) self.JobList.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel)
@@ -352,28 +353,30 @@ class Ui_KCC(object):
def retranslateUi(self, KCC): def retranslateUi(self, KCC):
_translate = QtCore.QCoreApplication.translate _translate = QtCore.QCoreApplication.translate
KCC.setWindowTitle(_translate("KCC", "Kindle Comic Converter")) 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.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.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.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.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.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.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.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.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.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.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.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.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.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.FileButton.setText(_translate("KCC", "Add file"))
self.ClearButton.setText(_translate("KCC", "Clear list")) 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.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.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.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.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")) self.RotateBox.setText(_translate("KCC", "Horizontal mode"))

View File

@@ -1,4 +1,4 @@
__version__ = '4.2.1' __version__ = '4.4'
__license__ = 'ISC' __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' __docformat__ = 'restructuredtext en'

View File

@@ -1,5 +1,5 @@
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@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>
# #
# Permission to use, copy, modify, and/or distribute this software for # Permission to use, copy, modify, and/or distribute this software for
# any purpose with or without fee is hereby granted, provided that the # any purpose with or without fee is hereby granted, provided that the
@@ -17,7 +17,7 @@
# #
__license__ = 'ISC' __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' __docformat__ = 'restructuredtext en'
import sys import sys
@@ -27,6 +27,7 @@ from subprocess import STDOUT, PIPE
from psutil import Popen from psutil import Popen
from shutil import move, copy from shutil import move, copy
from . import rarfile from . import rarfile
from .shared import check7ZFile as is_7zfile
class CBxArchive: class CBxArchive:
@@ -36,7 +37,7 @@ class CBxArchive:
self.compressor = 'zip' self.compressor = 'zip'
elif rarfile.is_rarfile(origFileName): elif rarfile.is_rarfile(origFileName):
self.compressor = 'rar' self.compressor = 'rar'
elif origFileName.endswith('.7z') or origFileName.endswith('.cb7'): elif is_7zfile(origFileName):
self.compressor = '7z' self.compressor = '7z'
else: else:
self.compressor = None self.compressor = None

View File

@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@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>
# #
# Permission to use, copy, modify, and/or distribute this software for # Permission to use, copy, modify, and/or distribute this software for
# any purpose with or without fee is hereby granted, provided that the # any purpose with or without fee is hereby granted, provided that the
@@ -18,16 +18,18 @@
# PERFORMANCE OF THIS SOFTWARE. # PERFORMANCE OF THIS SOFTWARE.
# #
__version__ = '4.2.1' __version__ = '4.4'
__license__ = 'ISC' __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' __docformat__ = 'restructuredtext en'
import os import os
import sys import sys
from copy import copy
from glob import glob
from json import loads from json import loads
from urllib.request import Request, urlopen 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 stat import S_IWRITE, S_IREAD, S_IEXEC
from zipfile import ZipFile, ZIP_STORED, ZIP_DEFLATED from zipfile import ZipFile, ZIP_STORED, ZIP_DEFLATED
from tempfile import mkdtemp from tempfile import mkdtemp
@@ -38,6 +40,8 @@ from xml.dom.minidom import parse
from uuid import uuid4 from uuid import uuid4
from slugify import slugify as slugifyExt from slugify import slugify as slugifyExt
from PIL import Image from PIL import Image
from subprocess import STDOUT, PIPE
from psutil import Popen, virtual_memory
try: try:
from PyQt5 import QtCore from PyQt5 import QtCore
except ImportError: except ImportError:
@@ -47,17 +51,27 @@ from . import comic2panel
from . import image from . import image
from . import cbxarchive from . import cbxarchive
from . import pdfjpgextract from . import pdfjpgextract
from . import dualmetafix
def main(argv=None): def main(argv=None):
global options global options
parser = makeParser() parser = makeParser()
options, args = parser.parse_args(argv) optionstemplate, args = parser.parse_args(argv)
checkOptions() if len(args) == 0:
if len(args) != 1:
parser.print_help() parser.print_help()
return 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 return outputPath
@@ -406,7 +420,7 @@ def buildEPUB(path, chapterNames, tomeNumber):
chapter = False chapter = False
for afile in filenames: for afile in filenames:
filename = getImageFileName(afile) 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))) filelist.append(buildHTML(dirpath, afile, os.path.join(dirpath, afile)))
if not chapter: if not chapter:
chapterlist.append((dirpath.replace('Images', 'Text'), filelist[-1][1])) chapterlist.append((dirpath.replace('Images', 'Text'), filelist[-1][1]))
@@ -430,12 +444,12 @@ def imgOptimization(img, opt, hqImage=None):
img.cropWhiteSpace() img.cropWhiteSpace()
if opt.cutpagenumbers and not opt.webtoon: if opt.cutpagenumbers and not opt.webtoon:
img.cutPageNumber() img.cutPageNumber()
img.optimizeImage(opt.gamma) img.optimizeImage()
if hqImage: if hqImage:
img.resizeImage(opt.upscale, opt.stretch, opt.bordersColor, 0) img.resizeImage(0)
img.calculateBorder(hqImage, True) img.calculateBorder(hqImage, True)
else: else:
img.resizeImage(opt.upscale, opt.stretch, opt.bordersColor, opt.quality) img.resizeImage()
if opt.panelview: if opt.panelview:
if opt.quality == 0: if opt.quality == 0:
img.calculateBorder(img) img.calculateBorder(img)
@@ -501,7 +515,7 @@ def imgFileProcessing(work):
dirpath = work[1] dirpath = work[1]
opt = work[2] opt = work[2]
output = [] output = []
img = image.ComicPage(os.path.join(dirpath, afile), opt.profileData) img = image.ComicPage(os.path.join(dirpath, afile), opt)
if opt.quality == 2: if opt.quality == 2:
wipe = False wipe = False
else: else:
@@ -509,39 +523,39 @@ def imgFileProcessing(work):
if opt.nosplitrotate: if opt.nosplitrotate:
splitter = None splitter = None
else: else:
splitter = img.splitPage(dirpath, opt.righttoleft, opt.rotate) splitter = img.splitPage(dirpath)
if splitter is not None: if splitter is not None:
img0 = image.ComicPage(splitter[0], opt.profileData) img0 = image.ComicPage(splitter[0], opt)
imgOptimization(img0, opt) imgOptimization(img0, opt)
output.append(img0.saveToDir(dirpath, opt.forcepng, opt.forcecolor)) output.append(img0.saveToDir(dirpath))
img1 = image.ComicPage(splitter[1], opt.profileData) img1 = image.ComicPage(splitter[1], opt)
imgOptimization(img1, opt) imgOptimization(img1, opt)
output.append(img1.saveToDir(dirpath, opt.forcepng, opt.forcecolor)) output.append(img1.saveToDir(dirpath))
if wipe: if wipe:
output.append(img0.origFileName) output.append(img0.origFileName)
output.append(img1.origFileName) output.append(img1.origFileName)
if opt.quality == 2: 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) imgOptimization(img0b, opt, img0)
output.append(img0b.saveToDir(dirpath, opt.forcepng, opt.forcecolor)) output.append(img0b.saveToDir(dirpath))
img1b = image.ComicPage(splitter[1], opt.profileData, img1.fill) img1b = image.ComicPage(splitter[1], opt, img1.fill)
imgOptimization(img1b, opt, img1) imgOptimization(img1b, opt, img1)
output.append(img1b.saveToDir(dirpath, opt.forcepng, opt.forcecolor)) output.append(img1b.saveToDir(dirpath))
output.append(img0.origFileName) output.append(img0.origFileName)
output.append(img1.origFileName) output.append(img1.origFileName)
output.append(img.origFileName) output.append(img.origFileName)
else: else:
imgOptimization(img, opt) imgOptimization(img, opt)
output.append(img.saveToDir(dirpath, opt.forcepng, opt.forcecolor)) output.append(img.saveToDir(dirpath))
if wipe: if wipe:
output.append(img.origFileName) output.append(img.origFileName)
if opt.quality == 2: 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: if img.rotated:
img2.image = img2.image.rotate(90) img2.image = img2.image.rotate(90, Image.BICUBIC, True)
img2.rotated = True img2.rotated = True
imgOptimization(img2, opt, img) imgOptimization(img2, opt, img)
output.append(img2.saveToDir(dirpath, opt.forcepng, opt.forcecolor)) output.append(img2.saveToDir(dirpath))
output.append(img.origFileName) output.append(img.origFileName)
return output return output
except Exception: except Exception:
@@ -664,11 +678,12 @@ def getComicInfo(path, originalPath):
options.authors.sort() options.authors.sort()
else: else:
options.authors = ['KCC'] options.authors = ['KCC']
if len(xml.getElementsByTagName('ScanInformation')) != 0: # Disabled due to closure of MCD
coverId = xml.getElementsByTagName('ScanInformation')[0].firstChild.nodeValue # if len(xml.getElementsByTagName('ScanInformation')) != 0:
coverId = compile('(MCD\\()(\\d+)(\\))').search(coverId) # coverId = xml.getElementsByTagName('ScanInformation')[0].firstChild.nodeValue
if coverId: # coverId = compile('(MCD\\()(\\d+)(\\))').search(coverId)
options.remoteCovers = getCoversFromMCB(coverId.group(2)) # if coverId:
# options.remoteCovers = getCoversFromMCB(coverId.group(2))
os.remove(xmlPath) os.remove(xmlPath)
@@ -746,7 +761,7 @@ def sanitizePermissions(filetree):
os.chmod(os.path.join(root, name), S_IWRITE | S_IREAD | S_IEXEC) os.chmod(os.path.join(root, name), S_IWRITE | S_IREAD | S_IEXEC)
#noinspection PyUnboundLocalVariable # noinspection PyUnboundLocalVariable
def splitDirectory(path): def splitDirectory(path):
# Detect directory stucture # Detect directory stucture
for root, dirs, files in walkLevel(os.path.join(path, 'OEBPS', 'Images'), 0): for root, dirs, files in walkLevel(os.path.join(path, 'OEBPS', 'Images'), 0):
@@ -809,11 +824,15 @@ def splitProcess(path, mode):
output = [] output = []
currentSize = 0 currentSize = 0
currentTarget = path currentTarget = path
if options.webtoon:
targetSize = 104857600
else:
targetSize = 419430400
if mode == 0: if mode == 0:
for root, dirs, files in walkLevel(path, 0): for root, dirs, files in walkLevel(path, 0):
for name in files: for name in files:
size = os.path.getsize(os.path.join(root, name)) size = os.path.getsize(os.path.join(root, name))
if currentSize + size > 419430400: if currentSize + size > targetSize:
currentTarget, pathRoot = createNewTome() currentTarget, pathRoot = createNewTome()
output.append(pathRoot) output.append(pathRoot)
currentSize = size currentSize = size
@@ -825,7 +844,7 @@ def splitProcess(path, mode):
for root, dirs, files in walkLevel(path, 0): for root, dirs, files in walkLevel(path, 0):
for name in dirs: for name in dirs:
size = getDirectorySize(os.path.join(root, name)) size = getDirectorySize(os.path.join(root, name))
if currentSize + size > 419430400: if currentSize + size > targetSize:
currentTarget, pathRoot = createNewTome() currentTarget, pathRoot = createNewTome()
output.append(pathRoot) output.append(pathRoot)
currentSize = size currentSize = size
@@ -839,7 +858,7 @@ def splitProcess(path, mode):
for name in dirs: for name in dirs:
size = getDirectorySize(os.path.join(root, name)) size = getDirectorySize(os.path.join(root, name))
currentSize = 0 currentSize = 0
if size > 419430400: if size > targetSize:
if not firstTome: if not firstTome:
currentTarget, pathRoot = createNewTome() currentTarget, pathRoot = createNewTome()
output.append(pathRoot) output.append(pathRoot)
@@ -848,7 +867,7 @@ def splitProcess(path, mode):
for rootInside, dirsInside, filesInside in walkLevel(os.path.join(root, name), 0): for rootInside, dirsInside, filesInside in walkLevel(os.path.join(root, name), 0):
for nameInside in dirsInside: for nameInside in dirsInside:
size = getDirectorySize(os.path.join(rootInside, nameInside)) size = getDirectorySize(os.path.join(rootInside, nameInside))
if currentSize + size > 419430400: if currentSize + size > targetSize:
currentTarget, pathRoot = createNewTome() currentTarget, pathRoot = createNewTome()
output.append(pathRoot) output.append(pathRoot)
currentSize = size currentSize = size
@@ -871,7 +890,7 @@ def detectCorruption(tmpPath, orgPath):
for name in files: for name in files:
if getImageFileName(name) is not None: if getImageFileName(name) is not None:
path = os.path.join(root, name) 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: if os.path.getsize(path) == 0:
rmtree(os.path.join(tmpPath, '..', '..'), True) rmtree(os.path.join(tmpPath, '..', '..'), True)
raise RuntimeError('Image file %s is corrupted.' % pathOrg) raise RuntimeError('Image file %s is corrupted.' % pathOrg)
@@ -945,7 +964,7 @@ def makeZIP(zipFilename, baseDir, isEPUB=False):
def makeParser(): 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) psr = OptionParser(usage="Usage: kcc-c2e [options] comic_file|comic_folder", add_help_option=False)
mainOptions = OptionGroup(psr, "MAIN") mainOptions = OptionGroup(psr, "MAIN")
@@ -954,9 +973,9 @@ def makeParser():
customProfileOptions = OptionGroup(psr, "CUSTOM PROFILE") customProfileOptions = OptionGroup(psr, "CUSTOM PROFILE")
otherOptions = OptionGroup(psr, "OTHER") otherOptions = OptionGroup(psr, "OTHER")
mainOptions.add_option("-p", "--profile", action="store", dest="profile", default="KHD", mainOptions.add_option("-p", "--profile", action="store", dest="profile", default="KV",
help="Device profile (Choose one among K1, K2, K345, KDX, KHD, KF, KFHD, KFHD8, KFHDX," help="Device profile (Available options: K1, K2, K345, KDX, KPW, KV, KFHD, KFHDX, KFHDX8,"
" KFHDX8, KFA, KoMT, KoG, KoA, KoAHD) [Default=KHD]") " KFA, KoMT, KoG, KoA, KoAHD, KoAH2O) [Default=KV]")
mainOptions.add_option("-q", "--quality", type="int", dest="quality", default="0", mainOptions.add_option("-q", "--quality", type="int", dest="quality", default="0",
help="Quality of Panel View. 0 - Normal 1 - High 2 - Ultra [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, 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") help="Output generated file to specified directory or file")
outputOptions.add_option("-t", "--title", action="store", dest="title", default="defaulttitle", outputOptions.add_option("-t", "--title", action="store", dest="title", default="defaulttitle",
help="Comic title [Default=filename or directory name]") help="Comic title [Default=filename or directory name]")
outputOptions.add_option("--cbz-output", action="store_true", dest="cbzoutput", default=False, outputOptions.add_option("-f", "--format", action="store", dest="format", default="Auto",
help="Outputs a CBZ archive and does not generate EPUB") help="Output format (Available options: Auto, MOBI, EPUB, CBZ) [Default=Auto]")
outputOptions.add_option("--batchsplit", action="store_true", dest="batchsplit", default=False, outputOptions.add_option("--batchsplit", action="store_true", dest="batchsplit", default=False,
help="Split output into multiple files"), help="Split output into multiple files"),
@@ -1016,13 +1035,22 @@ def checkOptions():
global options global options
options.panelview = True options.panelview = True
options.bordersColor = None 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: if options.white_borders:
options.bordersColor = "white" options.bordersColor = 'white'
if options.black_borders: 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. # Disabling grayscale conversion for Kindle Fire family.
if options.profile == 'KF' or options.profile == 'KFHD' or options.profile == 'KFHD8' or options.profile == 'KFHDX'\ if 'KFH' in options.profile or options.forcecolor:
or options.profile == 'KFHDX8' or options.forcecolor:
options.forcecolor = True options.forcecolor = True
else: else:
options.forcecolor = False options.forcecolor = False
@@ -1049,10 +1077,10 @@ def checkOptions():
print("ERROR: Kindle for Android profile require --customwidth and --customheight options!") print("ERROR: Kindle for Android profile require --customwidth and --customheight options!")
sys.exit(1) sys.exit(1)
# CBZ files on Kindle DX/DXG support higher resolution # 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 options.customheight = 1200
# Ultra mode don't work with CBZ format # 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 options.quality = 1
# Override profile data # Override profile data
if options.customwidth != 0 or options.customheight != 0: if options.customwidth != 0 or options.customheight != 0:
@@ -1069,12 +1097,35 @@ def checkOptions():
options.profileData = image.ProfileData.Profiles[options.profile] 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): 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 global GUI
GUI = qtGUI GUI = qtGUI
if GUI: if GUI:
GUI.progressBarTick.emit('1') GUI.progressBarTick.emit('1')
else:
checkTools(source)
path = getWorkFolder(source) path = getWorkFolder(source)
print("\nChecking images...") print("\nChecking images...")
getComicInfo(os.path.join(path, "OEBPS", "Images"), source) getComicInfo(os.path.join(path, "OEBPS", "Images"), source)
@@ -1092,7 +1143,7 @@ def makeBook(source, qtGUI=None):
if GUI: if GUI:
GUI.progressBarTick.emit('1') GUI.progressBarTick.emit('1')
chapterNames = sanitizeTree(os.path.join(path, 'OEBPS', 'Images')) 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')) sanitizeTreeKobo(os.path.join(path, 'OEBPS', 'Images'))
if options.batchsplit: if options.batchsplit:
tomes = splitDirectory(path) tomes = splitDirectory(path)
@@ -1101,7 +1152,7 @@ def makeBook(source, qtGUI=None):
filepath = [] filepath = []
tomeNumber = 0 tomeNumber = 0
if GUI: if GUI:
if options.cbzoutput: if options.format == 'CBZ':
GUI.progressBarTick.emit('Compressing CBZ files') GUI.progressBarTick.emit('Compressing CBZ files')
else: else:
GUI.progressBarTick.emit('Compressing EPUB files') GUI.progressBarTick.emit('Compressing EPUB files')
@@ -1109,11 +1160,13 @@ def makeBook(source, qtGUI=None):
GUI.progressBarTick.emit('tick') GUI.progressBarTick.emit('tick')
options.baseTitle = options.title options.baseTitle = options.title
for tome in tomes: 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 tomeNumber += 1
options.title = options.baseTitle + ' [' + str(tomeNumber) + '/' + str(len(tomes)) + ']' options.title = options.baseTitle + ' [' + str(tomeNumber) + '/' + str(len(tomes)) + ']'
if options.cbzoutput: if options.format == 'CBZ':
# if CBZ output wanted, compress all images and return filepath
print("\nCreating CBZ file...") print("\nCreating CBZ file...")
if len(tomes) > 1: if len(tomes) > 1:
filepath.append(getOutputFilename(source, options.output, '.cbz', ' ' + str(tomeNumber))) 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', '')) filepath.append(getOutputFilename(source, options.output, '.cbz', ''))
makeZIP(tome + '_comic', os.path.join(tome, "OEBPS", "Images")) makeZIP(tome + '_comic', os.path.join(tome, "OEBPS", "Images"))
else: else:
print("\nCreating EPUB structure...") print("\nCreating EPUB file...")
buildEPUB(tome, chapterNames, tomeNumber) buildEPUB(tome, chapterNames, tomeNumber)
# actually zip the ePub
if len(tomes) > 1: if len(tomes) > 1:
filepath.append(getOutputFilename(source, options.output, '.epub', ' ' + str(tomeNumber))) filepath.append(getOutputFilename(source, options.output, '.epub', ' ' + str(tomeNumber)))
else: else:
@@ -1133,4 +1185,92 @@ def makeBook(source, qtGUI=None):
rmtree(tome, True) rmtree(tome, True)
if GUI: if GUI:
GUI.progressBarTick.emit('tick') 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

View File

@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@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>
# #
# Permission to use, copy, modify, and/or distribute this software for # Permission to use, copy, modify, and/or distribute this software for
# any purpose with or without fee is hereby granted, provided that the # any purpose with or without fee is hereby granted, provided that the
@@ -18,9 +18,9 @@
# PERFORMANCE OF THIS SOFTWARE. # PERFORMANCE OF THIS SOFTWARE.
# #
__version__ = '4.2.1' __version__ = '4.4'
__license__ = 'ISC' __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' __docformat__ = 'restructuredtext en'
import os import os
@@ -28,7 +28,7 @@ import sys
from shutil import rmtree, copytree, move from shutil import rmtree, copytree, move
from optparse import OptionParser, OptionGroup from optparse import OptionParser, OptionGroup
from multiprocessing import Pool from multiprocessing import Pool
from PIL import Image, ImageStat from PIL import Image, ImageStat, ImageOps
from .shared import getImageFileName, walkLevel from .shared import getImageFileName, walkLevel
try: try:
from PyQt5 import QtCore from PyQt5 import QtCore
@@ -50,9 +50,9 @@ def mergeDirectory(work):
try: try:
directory = work[0] directory = work[0]
images = [] images = []
imagesClear = [] imagesValid = []
sizes = [] sizes = []
h = 0 targetHeight = 0
for root, dirs, files in walkLevel(directory, 0): for root, dirs, files in walkLevel(directory, 0):
for name in files: for name in files:
if getImageFileName(name) is not None: 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]]) images.append([os.path.join(root, name), i.size[0], i.size[1]])
sizes.append(i.size[0]) sizes.append(i.size[0])
if len(images) > 0: if len(images) > 0:
mw = max(set(sizes), key=sizes.count) targetWidth = max(set(sizes), key=sizes.count)
for i in images: for i in images:
if i[1] == mw: if i[1] <= targetWidth:
h += i[2] targetHeight += i[2]
imagesClear.append(i[0]) imagesValid.append(i[0])
# Silently drop directories that contain too many images # Silently drop directories that contain too many images
if h > 262144: # 131072 = GIMP_MAX_IMAGE_SIZE / 4
if targetHeight > 131072:
return None return None
result = Image.new('RGB', (mw, h)) result = Image.new('RGB', (targetWidth, targetHeight))
y = 0 y = 0
for i in imagesClear: for i in imagesValid:
img = Image.open(i) img = Image.open(i)
img = img.convert('RGB') 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)) result.paste(img, (0, y))
y += img.size[1] y += img.size[1]
os.remove(i) 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') result.save(os.path.join(savePath[0], os.path.splitext(savePath[1])[0] + '.png'), 'PNG')
except Exception: except Exception:
return str(sys.exc_info()[1]) return str(sys.exc_info()[1])
@@ -84,7 +87,7 @@ def mergeDirectory(work):
def sanitizePanelSize(panel, opt): def sanitizePanelSize(panel, opt):
newPanels = [] newPanels = []
if panel[2] > 8 * opt.height: if panel[2] > 6 * opt.height:
diff = int(panel[2] / 8) diff = int(panel[2] / 8)
newPanels.append([panel[0], panel[1] - diff*7, diff]) newPanels.append([panel[0], panel[1] - diff*7, diff])
newPanels.append([panel[1] - diff*7, panel[1] - diff*6, 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*3, panel[1] - diff*2, diff])
newPanels.append([panel[1] - diff*2, panel[1] - diff, diff]) newPanels.append([panel[1] - diff*2, panel[1] - diff, diff])
newPanels.append([panel[1] - diff, panel[1], 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) diff = int(panel[2] / 4)
newPanels.append([panel[0], panel[1] - diff*3, diff]) 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*3, panel[1] - diff*2, diff])
newPanels.append([panel[1] - diff*2, panel[1] - diff, diff]) newPanels.append([panel[1] - diff*2, panel[1] - diff, diff])
newPanels.append([panel[1] - diff, panel[1], 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[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)]) newPanels.append([panel[1] - int(panel[2] / 2), panel[1], int(panel[2] / 2)])
else: else:
@@ -118,7 +121,6 @@ def splitImageTick(output):
splitWorkerPool.terminate() splitWorkerPool.terminate()
#noinspection PyUnboundLocalVariable
def splitImage(work): def splitImage(work):
try: try:
path = work[0] path = work[0]
@@ -165,6 +167,7 @@ def splitImage(work):
for panel in panelsCleaned: for panel in panelsCleaned:
panels.append(panel) panels.append(panel)
if opt.debug: if opt.debug:
# noinspection PyUnboundLocalVariable
debugImage.save(os.path.join(path, fileExpanded[0] + '-debug.png'), 'PNG') debugImage.save(os.path.join(path, fileExpanded[0] + '-debug.png'), 'PNG')
# Create virtual pages # Create virtual pages
@@ -199,8 +202,7 @@ def splitImage(work):
panelImg = image.crop([0, panels[panel][0], widthImg, panels[panel][1]]) panelImg = image.crop([0, panels[panel][0], widthImg, panels[panel][1]])
newPage.paste(panelImg, (0, targetHeight)) newPage.paste(panelImg, (0, targetHeight))
targetHeight += panels[panel][2] targetHeight += panels[panel][2]
newPage.save(os.path.join(path, fileExpanded[0] + '-' + newPage.save(os.path.join(path, fileExpanded[0] + '-' + str(pageNumber) + '.png'), 'PNG')
str(pageNumber) + '.png'), 'PNG')
pageNumber += 1 pageNumber += 1
os.remove(filePath) os.remove(filePath)
except Exception: except Exception:

View File

@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Based on initial version of DualMetaFix. Copyright (C) 2013 Kevin Hendricks # 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 # 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 # it under the terms of the GNU General Public License as published by

View File

@@ -1,7 +1,7 @@
# Copyright (C) 2010 Alex Yatskov # Copyright (C) 2010 Alex Yatskov
# Copyright (C) 2011 Stanislav (proDOOMman) Kosolapov <prodoomman@gmail.com> # Copyright (C) 2011 Stanislav (proDOOMman) Kosolapov <prodoomman@gmail.com>
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@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 # 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 # 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 # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
__version__ = '4.2.1' __version__ = '4.4'
__license__ = 'ISC' __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' __docformat__ = 'restructuredtext en'
import os import os
@@ -85,33 +85,32 @@ class ProfileData:
'K1': ("Kindle 1", (600, 670), Palette4, 1.8, (900, 1005)), 'K1': ("Kindle 1", (600, 670), Palette4, 1.8, (900, 1005)),
'K2': ("Kindle 2", (600, 670), Palette15, 1.8, (900, 1005)), 'K2': ("Kindle 2", (600, 670), Palette15, 1.8, (900, 1005)),
'K345': ("Kindle", (600, 800), Palette16, 1.8, (900, 1200)), '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)), 'KDX': ("Kindle DX/DXG", (824, 1000), Palette16, 1.8, (1236, 1500)),
'KF': ("Kindle Fire", (600, 1024), PalleteNull, 1.0, (900, 1536)), 'KPW': ("Kindle Paperwhite", (758, 1024), Palette16, 1.8, (1137, 1536)),
'KFHD': ("K. Fire HD 7\"", (800, 1280), PalleteNull, 1.0, (1200, 1920)), 'KV': ("Kindle Voyage", (1072, 1448), Palette16, 1.8, (1608, 2172)),
'KFHD8': ("K. Fire HD 8.9\"", (1200, 1920), PalleteNull, 1.0, (1800, 2880)), 'KFHD': ("K. Fire HD", (800, 1280), PalleteNull, 1.0, (1200, 1920)),
'KFHDX': ("K. Fire HDX 7\"", (1200, 1920), PalleteNull, 1.0, (1800, 2880)), 'KFHDX': ("K. Fire HDX", (1200, 1920), PalleteNull, 1.0, (1800, 2880)),
'KFHDX8': ("K. Fire HDX 8.9\"", (1600, 2560), PalleteNull, 1.0, (2400, 3840)), '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)), 'KoMT': ("Kobo Mini/Touch", (600, 800), Palette16, 1.8, (900, 1200)),
'KoG': ("Kobo Glow", (768, 1024), Palette16, 1.8, (1152, 1536)), 'KoG': ("Kobo Glow", (768, 1024), Palette16, 1.8, (1152, 1536)),
'KoA': ("Kobo Aura", (758, 1024), Palette16, 1.8, (1137, 1536)), 'KoA': ("Kobo Aura", (758, 1024), Palette16, 1.8, (1137, 1536)),
'KoAHD': ("Kobo Aura HD", (1080, 1440), Palette16, 1.8, (1620, 2160)), '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)), 'OTHER': ("Other", (0, 0), Palette16, 1.8, (0, 0)),
} }
class ComicPage: class ComicPage:
def __init__(self, source, device, fill=None): def __init__(self, source, options, fill=None):
try: 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: except KeyError:
raise RuntimeError('Unexpected output device %s' % device) raise RuntimeError('Unexpected output device %s' % options.profileData)
self.origFileName = source self.origFileName = source
self.filename = os.path.basename(self.origFileName) self.filename = os.path.basename(self.origFileName)
self.image = Image.open(source) self.image = Image.open(source)
self.image = self.image.convert('RGB') self.image = self.image.convert('RGB')
self.color = self.isImageColor()
self.rotated = None self.rotated = None
self.border = None self.border = None
self.noHPV = None self.noHPV = None
@@ -119,17 +118,22 @@ class ComicPage:
self.noPV = None self.noPV = None
self.purge = False self.purge = False
self.hq = False self.hq = False
self.opt = options
if fill: if fill:
self.fill = fill self.fill = fill
else: else:
self.fill = None 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: try:
if not self.purge: if not self.purge:
flags = [] flags = []
filename = os.path.join(targetdir, os.path.splitext(self.filename)[0]) + '-KCC' 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') self.image = self.image.convert('L')
if self.rotated: if self.rotated:
flags.append('Rotated') flags.append('Rotated')
@@ -144,21 +148,22 @@ class ComicPage:
if self.noVPV: if self.noVPV:
flags.append('NoVerticalPanelView') flags.append('NoVerticalPanelView')
if self.border: if self.border:
flags.append("Margins-" + str(self.border[0]) + "-" + str(self.border[1]) + "-" flags.append('Margins-' + str(self.border[0]) + '-' + str(self.border[1]) + '-'
+ str(self.border[2]) + "-" + str(self.border[3])) + str(self.border[2]) + '-' + str(self.border[3]))
if forcepng: if self.opt.forcepng:
filename += ".png" filename += '.png'
self.image.save(filename, "PNG", optimize=1) self.image.save(filename, 'PNG', optimize=1)
else: else:
filename += ".jpg" filename += '.jpg'
self.image.save(filename, "JPEG", optimize=1) self.image.save(filename, 'JPEG', optimize=1, quality=80)
return [md5Checksum(filename), flags] return [md5Checksum(filename), flags]
else: else:
return None return None
except IOError as e: except IOError as e:
raise RuntimeError('Cannot write image in directory %s: %s' % (targetdir, 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: if gamma < 0.1:
gamma = self.gamma gamma = self.gamma
if self.gamma != 1.0 and self.color: if self.gamma != 1.0 and self.color:
@@ -215,7 +220,12 @@ class ComicPage:
self.noHPV = True self.noHPV = True
self.noVPV = 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: if bordersColor:
fill = bordersColor fill = bordersColor
else: else:
@@ -243,7 +253,7 @@ class ComicPage:
if self.image.size[0] <= size[0] and self.image.size[1] <= size[1]: if self.image.size[0] <= size[0] and self.image.size[1] <= size[1]:
method = Image.BICUBIC method = Image.BICUBIC
else: else:
method = Image.ANTIALIAS method = Image.LANCZOS
self.image = self.image.resize(size, method) self.image = self.image.resize(size, method)
return self.image return self.image
# If image is smaller than target resolution and upscale is off - Just expand it by adding margins # 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]: if self.image.size[0] <= size[0] and self.image.size[1] <= size[1]:
method = Image.BICUBIC method = Image.BICUBIC
else: else:
method = Image.ANTIALIAS method = Image.LANCZOS
self.image = ImageOps.fit(self.image, size, method=method, centering=(0.5, 0.5)) self.image = ImageOps.fit(self.image, size, method=method, centering=(0.5, 0.5))
return self.image return self.image
def splitPage(self, targetdir, righttoleft=False, rotate=False): def splitPage(self, targetdir):
width, height = self.image.size width, height = self.image.size
dstwidth, dstheight = self.size dstwidth, dstheight = self.size
# Only split if origin is not oriented the same as target # Only split if origin is not oriented the same as target
if (width > height) != (dstwidth > dstheight): if (width > height) != (dstwidth > dstheight):
if rotate: if self.opt.rotate:
self.image = self.image.rotate(90) self.image = self.image.rotate(90, Image.BICUBIC, True)
self.rotated = True self.rotated = True
return None return None
else: else:
@@ -292,18 +302,18 @@ class ComicPage:
# Source is portrait and target is landscape, so split by the height # Source is portrait and target is landscape, so split by the height
leftbox = (0, 0, width, int(height / 2)) leftbox = (0, 0, width, int(height / 2))
rightbox = (0, int(height / 2), width, height) rightbox = (0, int(height / 2), width, height)
filename = os.path.splitext(self.filename) filename = os.path.splitext(self.filename)[0]
fileone = targetdir + '/' + filename[0] + '-A' + filename[1] fileone = targetdir + '/' + filename + '-AAA.png'
filetwo = targetdir + '/' + filename[0] + '-B' + filename[1] filetwo = targetdir + '/' + filename + '-BBB.png'
try: try:
if righttoleft: if self.opt.righttoleft:
pageone = self.image.crop(rightbox) pageone = self.image.crop(rightbox)
pagetwo = self.image.crop(leftbox) pagetwo = self.image.crop(leftbox)
else: else:
pageone = self.image.crop(leftbox) pageone = self.image.crop(leftbox)
pagetwo = self.image.crop(rightbox) pagetwo = self.image.crop(rightbox)
pageone.save(fileone) pageone.save(fileone, 'PNG', optimize=1)
pagetwo.save(filetwo) pagetwo.save(filetwo, 'PNG', optimize=1)
except IOError as e: except IOError as e:
raise RuntimeError('Cannot write image in directory %s: %s' % (targetdir, e)) raise RuntimeError('Cannot write image in directory %s: %s' % (targetdir, e))
return fileone, filetwo return fileone, filetwo
@@ -417,13 +427,16 @@ class ComicPage:
imageBoxB = ImageChops.invert(bw).getbbox() imageBoxB = ImageChops.invert(bw).getbbox()
if imageBoxA is None or imageBoxB is None: if imageBoxA is None or imageBoxB is None:
surfaceB, surfaceW = 0, 0 surfaceB, surfaceW = 0, 0
diff = 0
else: else:
surfaceB = (imageBoxA[2] - imageBoxA[0]) * (imageBoxA[3] - imageBoxA[1]) surfaceB = (imageBoxA[2] - imageBoxA[0]) * (imageBoxA[3] - imageBoxA[1])
surfaceW = (imageBoxB[2] - imageBoxB[0]) * (imageBoxB[3] - imageBoxB[1]) surfaceW = (imageBoxB[2] - imageBoxB[0]) * (imageBoxB[3] - imageBoxB[1])
if surfaceW < surfaceB: diff = ((max(surfaceB, surfaceW) - min(surfaceB, surfaceW)) / min(surfaceB, surfaceW)) * 100
self.fill = 'white' if diff > 0.5:
elif surfaceW > surfaceB: if surfaceW < surfaceB:
self.fill = 'black' self.fill = 'white'
elif surfaceW > surfaceB:
self.fill = 'black'
else: else:
fill = 0 fill = 0
startY = 0 startY = 0
@@ -498,7 +511,7 @@ class Cover:
def processExternal(self): def processExternal(self):
self.image = self.image.convert('RGB') 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) self.save(True)
def trim(self): def trim(self):
@@ -520,6 +533,6 @@ class Cover:
if os.path.splitext(source)[1].lower() == '.png': if os.path.splitext(source)[1].lower() == '.png':
self.image.save(self.target, "PNG", optimize=1) self.image.save(self.target, "PNG", optimize=1)
else: else:
self.image.save(self.target, "JPEG", optimize=1) self.image.save(self.target, "JPEG", optimize=1, quality=80)
except IOError: except IOError:
raise RuntimeError('Failed to save cover') raise RuntimeError('Failed to save cover')

View File

@@ -1,5 +1,5 @@
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@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>
# #
# Based upon the code snippet by Ned Batchelder # Based upon the code snippet by Ned Batchelder
# (http://nedbatchelder.com/blog/200712/extracting_jpgs_from_pdfs.html) # (http://nedbatchelder.com/blog/200712/extracting_jpgs_from_pdfs.html)
@@ -20,7 +20,7 @@
# #
__license__ = 'ISC' __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' __docformat__ = 'restructuredtext en'
import os import os

View File

@@ -1,6 +1,6 @@
# rarfile.py # 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 # Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above # 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 # export only interesting items
__all__ = ['is_rarfile', 'RarInfo', 'RarFile', 'RarExtFile'] __all__ = ['is_rarfile', 'RarInfo', 'RarFile', 'RarExtFile']
@@ -178,8 +178,25 @@ EXTRACT_ARGS = ('x', '-y', '-idq')
#: args for testrar() #: args for testrar()
TEST_ARGS = ('t', '-idq') 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 #: whether to speed up decompression by using tmp archive
USE_EXTRACT_HACK = 1 USE_EXTRACT_HACK = 0
#: limit the filesize for tmp archive usage #: limit the filesize for tmp archive usage
HACK_SIZE_LIMIT = 20*1024*1024 HACK_SIZE_LIMIT = 20*1024*1024
@@ -278,6 +295,7 @@ RAR_M5 = 0x35
## ##
RAR_ID = bytes("Rar!\x1a\x07\x00", 'ascii') RAR_ID = bytes("Rar!\x1a\x07\x00", 'ascii')
RAR5_ID = bytes("Rar!\x1a\x07\x01", 'ascii')
ZERO = bytes("\0", 'ascii') ZERO = bytes("\0", 'ascii')
EMPTY = bytes("", 'ascii') EMPTY = bytes("", 'ascii')
@@ -336,6 +354,8 @@ class RarUnknownError(RarExecError):
"""Unknown exit code""" """Unknown exit code"""
class RarSignalExit(RarExecError): class RarSignalExit(RarExecError):
"""Unrar exited with signal""" """Unrar exited with signal"""
class RarCannotExec(RarExecError):
"""Executable not found."""
def is_rarfile(xfile): def is_rarfile(xfile):
@@ -343,7 +363,10 @@ def is_rarfile(xfile):
fd = XFile(xfile) fd = XFile(xfile)
buf = fd.read(len(RAR_ID)) buf = fd.read(len(RAR_ID))
fd.close() fd.close()
return buf == RAR_ID if buf == RAR_ID or buf == RAR5_ID:
return True
else:
return False
class RarInfo(object): class RarInfo(object):
@@ -693,10 +716,7 @@ class RarFile(object):
"""Let 'unrar' test the archive. """Let 'unrar' test the archive.
""" """
cmd = [UNRAR_TOOL] + list(TEST_ARGS) cmd = [UNRAR_TOOL] + list(TEST_ARGS)
if self._password is not None: add_password_arg(cmd, self._password)
cmd.append('-p' + self._password)
else:
cmd.append('-p-')
cmd.append(self.rarfile) cmd.append(self.rarfile)
p = custom_popen(cmd) p = custom_popen(cmd)
output = p.communicate()[0] output = p.communicate()[0]
@@ -769,7 +789,7 @@ class RarFile(object):
fd = XFile(self.rarfile) fd = XFile(self.rarfile)
self._fd = fd self._fd = fd
id = fd.read(len(RAR_ID)) 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) raise NotRarFile("Not a Rar archive: "+self.rarfile)
volume = 0 # first vol (.rar) is 0 volume = 0 # first vol (.rar) is 0
@@ -1172,8 +1192,7 @@ class RarFile(object):
if is_filelike(rarfile): if is_filelike(rarfile):
raise ValueError("Cannot use unrar directly on memory buffer") raise ValueError("Cannot use unrar directly on memory buffer")
cmd = [UNRAR_TOOL] + list(OPEN_ARGS) cmd = [UNRAR_TOOL] + list(OPEN_ARGS)
if psw is not None: add_password_arg(cmd, psw)
cmd.append("-p" + psw)
cmd.append(rarfile) cmd.append(rarfile)
# not giving filename avoids encoding related problems # not giving filename avoids encoding related problems
@@ -1205,10 +1224,7 @@ class RarFile(object):
# pasoword # pasoword
psw = psw or self._password psw = psw or self._password
if psw is not None: add_password_arg(cmd, psw)
cmd.append('-p' + psw)
else:
cmd.append('-p-')
# rar file # rar file
cmd.append(self.rarfile) 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() tmpf.close()
cmd = [UNRAR_TOOL] + list(OPEN_ARGS) cmd = [UNRAR_TOOL] + list(OPEN_ARGS)
if psw is not None and (flags & RAR_FILE_PASSWORD): add_password_arg(cmd, psw, (flags & RAR_FILE_PASSWORD))
cmd.append("-p" + psw)
else:
cmd.append("-p-")
cmd.append(tmpname) cmd.append(tmpname)
p = custom_popen(cmd) p = custom_popen(cmd)
@@ -1902,10 +1915,27 @@ def custom_popen(cmd):
except OSError: except OSError:
ex = sys.exc_info()[1] ex = sys.exc_info()[1]
if ex.errno == errno.ENOENT: 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 raise
return p 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): def check_returncode(p, out):
"""Raise exception according to unrar exit code""" """Raise exception according to unrar exit code"""
@@ -1920,6 +1950,8 @@ def check_returncode(p, out):
RarWarning, RarFatalError, RarCRCError, RarLockedArchiveError, RarWarning, RarFatalError, RarCRCError, RarLockedArchiveError,
RarWriteError, RarOpenError, RarUserError, RarMemoryError, RarWriteError, RarOpenError, RarUserError, RarMemoryError,
RarCreateError, RarNoFilesError] # codes from rar.txt RarCreateError, RarNoFilesError] # codes from rar.txt
if UNRAR_TOOL == ALT_TOOL:
errmap = [None]
if code > 0 and code < len(errmap): if code > 0 and code < len(errmap):
exc = errmap[code] exc = errmap[code]
elif code == 255: elif code == 255:
@@ -1936,3 +1968,24 @@ def check_returncode(p, out):
msg = "%s [%d]" % (exc.__doc__, p.returncode) msg = "%s [%d]" % (exc.__doc__, p.returncode)
raise exc(msg) 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

View File

@@ -1,5 +1,5 @@
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@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>
# #
# Permission to use, copy, modify, and/or distribute this software for # Permission to use, copy, modify, and/or distribute this software for
# any purpose with or without fee is hereby granted, provided that the # any purpose with or without fee is hereby granted, provided that the
@@ -17,25 +17,35 @@
# #
__license__ = 'ISC' __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' __docformat__ = 'restructuredtext en'
import os import os
from hashlib import md5 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): def getImageFileName(imgfile):
filename = os.path.splitext(imgfile) name, ext = os.path.splitext(imgfile)
if filename[0].startswith('.') or\ ext = ext.lower()
(filename[1].lower() != '.png' and if name.startswith('.') or (ext != '.png' and ext != '.jpg' and ext != '.jpeg' and ext != '.gif'):
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'):
return None return None
return filename return [name, ext]
def walkLevel(some_dir, level=1): def walkLevel(some_dir, level=1):
@@ -58,3 +68,48 @@ def md5Checksum(filePath):
break break
m.update(data) m.update(data)
return m.hexdigest() 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)

View File

@@ -1,12 +1,12 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
""" """
cx_Freeze/py2app build script for KCC. py2exe/py2app build script for KCC.
Usage (Mac OS X):
python setup.py py2app
Usage (Windows): Usage (Windows):
python setup.py py2exe python setup.py py2exe
Usage (Mac OS X):
python setup.py py2app
""" """
from sys import platform, version_info from sys import platform, version_info
if version_info[0] != 3: if version_info[0] != 3:
@@ -14,7 +14,7 @@ if version_info[0] != 3:
exit(1) exit(1)
NAME = "KindleComicConverter" NAME = "KindleComicConverter"
VERSION = "4.2.1" VERSION = "4.4"
MAIN = "kcc.py" MAIN = "kcc.py"
if platform == "darwin": if platform == "darwin":
@@ -33,7 +33,7 @@ if platform == "darwin":
CFBundleName=NAME, CFBundleName=NAME,
CFBundleShortVersionString=VERSION, CFBundleShortVersionString=VERSION,
CFBundleGetInfoString=NAME + " " + 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, CFBundleExecutable=NAME,
CFBundleIdentifier='com.github.ciromattia.kcc', CFBundleIdentifier='com.github.ciromattia.kcc',
CFBundleSignature='dplt', CFBundleSignature='dplt',
@@ -47,7 +47,7 @@ if platform == "darwin":
], ],
LSMinimumSystemVersion='10.8.0', LSMinimumSystemVersion='10.8.0',
LSEnvironment=dict( LSEnvironment=dict(
PATH='/usr/local/bin:/usr/bin:/bin' PATH='./../Resources:/usr/local/bin:/usr/bin:/bin'
), ),
NSHumanReadableCopyright='ISC License (ISCL)' NSHumanReadableCopyright='ISC License (ISCL)'
) )
@@ -63,46 +63,25 @@ elif platform == "win32":
suffix = '_64' suffix = '_64'
else: else:
suffix = '' suffix = ''
additional_files = [('imageformats', ['C:\Python34' + suffix + additional_files = [('platforms', ['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 +
'\Lib\site-packages\PyQt5\plugins\platforms\qwindows.dll']), '\Lib\site-packages\PyQt5\plugins\platforms\qwindows.dll']),
('', ['LICENSE.txt', ('', ['LICENSE.txt',
'other\\7za.exe', 'other\\7za.exe',
'other\\UnRAR.exe', 'other\\UnRAR.exe',
'other\\Additional-LICENSE.txt', 'other\\Additional-LICENSE.txt',
'other\\7za.exe',
'C:\Python34' + suffix + '\Lib\site-packages\PyQt5\libEGL.dll'])] 'C:\Python34' + suffix + '\Lib\site-packages\PyQt5\libEGL.dll'])]
extra_options = dict( extra_options = dict(
options={'py2exe': {"bundle_files": 2, options={'py2exe': {"bundle_files": 1,
"dll_excludes": ["tcl85.dll", "tk85.dll"], "dll_excludes": ["tcl85.dll", "tk85.dll"],
"dist_dir": "dist" + suffix, "dist_dir": "dist" + suffix,
"compressed": True, "compressed": True,
"includes": ["sip"], "includes": ["sip"],
"excludes": ["tkinter"], "excludes": ["tkinter"],
"optimize": 2}}, "optimize": 2}},
windows=[{"script": "kcc.py", windows=[{"script": MAIN,
"dest_base": "KCC", "dest_base": "KCC",
"version": VERSION, "version": VERSION,
"copyright": "Ciro Mattia Gonano, Pawel Jastrzebski © 2014", "copyright": "Ciro Mattia Gonano, Pawel Jastrzebski © 2012-2015",
"legal_copyright": "ISC License (ISCL)", "legal_copyright": "ISC License (ISCL)",
"product_version": VERSION, "product_version": VERSION,
"product_name": "Kindle Comic Converter", "product_name": "Kindle Comic Converter",
@@ -114,7 +93,7 @@ else:
print('Please use setup.sh to build Linux package.') print('Please use setup.sh to build Linux package.')
exit() exit()
#noinspection PyUnboundLocalVariable # noinspection PyUnboundLocalVariable
setup( setup(
name=NAME, name=NAME,
version=VERSION, version=VERSION,

View File

@@ -1,7 +1,7 @@
#!/bin/bash #!/bin/bash
# Linux Python package build script # Linux Python package build script
VERSION="4.2.1" VERSION="4.4"
cp kcc.py __main__.py cp kcc.py __main__.py
zip kcc.zip __main__.py kcc/*.py zip kcc.zip __main__.py kcc/*.py