1
0
mirror of https://github.com/ciromattia/kcc synced 2026-04-17 14:38:47 +00:00

Compare commits

...

31 Commits
4.3 ... 4.4.1

Author SHA1 Message Date
Paweł Jastrzębski
7bd9c766cc Version bump 2015-01-11 16:20:01 +01:00
Paweł Jastrzębski
c6b1417d9c Added one Windows DLL 2015-01-11 16:17:04 +01:00
Paweł Jastrzębski
98bf28a713 Fixed OSX GUI anomalies 2015-01-10 11:09:28 +01:00
Paweł Jastrzębski
f2d6d5b458 Fixed upgrade freeze (close #123) 2015-01-05 10:47:21 +01:00
Paweł Jastrzębski
5de492ffb6 Overhauled dependency check 2015-01-05 08:43:15 +01:00
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
24 changed files with 741 additions and 627 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

381
README.md
View File

@@ -13,8 +13,7 @@ _KC2_ in no way is a replacement for **KCC** so you can be quite confident we'll
### 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,7 +31,7 @@ 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)*
@@ -45,20 +44,20 @@ 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+ - [PyQt](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) 0.1.0+
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
@@ -143,7 +142,7 @@ This script born as a cross-platform alternative to `KindleComicParser` by **Dc5
The app relies and includes the following scripts: 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.
@@ -159,113 +158,119 @@ The app relies and includes the following scripts:
* [Kobo Aura H2O](http://kcc.iosphe.re/Samples/Ubunchu!-KoAH2O.cbz) * [Kobo Aura H2O](http://kcc.iosphe.re/Samples/Ubunchu!-KoAH2O.cbz)
## CHANGELOG ## CHANGELOG
####1.0 ####4.4.1:
* Initial version * Fixed problems with OSX GUI
* Added one missing DLL to Windows installer
####1.1 ####4.4:
* Added support for CBZ/CBR files in comic2ebook.py * 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.1 ####4.3.1:
* Added support for CBZ/CBR files in Kindle Comic Converter * 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.2 ####4.3:
* 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! * Added profiles for Kindle Voyage and Kobo Aura H2O
* Added missing features to CLI version
* Other minor bug fixes
####1.3 ####4.2.1:
* Fixed an issue in OPF generation for device resolution * Improved margin color detection
* Reworked options system (call with -h option to get the inline help) * 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.4 ####4.2:
* Added some options for controlling image optimization * Added [Manga Cover Database](http://manga.joentjuh.nl/) support
* Further optimization (ImageOps, page numbering cut, autocontrast) * 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.1 ####4.1:
* Fixed a serious bug on resizing when img ratio was bigger than device one * Thanks to code contributed by Kevin Hendricks speed of MOBI creation was greatly increased
* Improved performance on Windows
* Improved MOBI splitting and changed maximal size of output file
* Fixed _No optimization_ mode
* Multiple small tweaks nad minor bug fixes
####1.5 ####4.0.2:
* Added subfolder support for multiple chapters. * Fixed some Windows and OSX specific bugs
* Fixed problem with marigns when using HQ mode
####2.0 ####4.0.1:
* GUI! AppleScript is gone and Tk is used to provide cross-platform GUI support. * 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.1 ####4.0:
* Added basic error reporting * 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.2: ####3.7.2:
* Added (valid!) EPUB 2.0 output * Fixed problems with HQ mode
* Rename .zip files to .cbz to avoid overwriting
####2.3 ####3.7.1:
* Fixed win32 EPUB generation, folder handling, filenames with spaces and subfolders * Hotfixed Kobo profiles
####2.4 ####3.7:
* Use temporary directory as workdir (fixes converting from external volumes and zipfiles renaming) * Added profiles for KOBO devices
* Fixed "add folders" from GUI. * Improved Panel View support
* Improved WebToon splitter
* Improved margin color autodetection
* Tweaked EPUB output
* Fixed stretching option
* GUI tweaks and minor bugfixes
####2.5 ####3.6.2:
* Added --black-borders option to set added borders black when page's ratio is not the device's one (#11). * Fixed previous PNG output fix
* Fixes EPUB containing zipped itself (#10) * Fixed Panel View anomalies
####2.6 ####3.6.1:
* Added --rotate option to rotate landscape images instead of splitting them (#16, #24) * Fixed PNG output
* Added --output option to customize EPUB output dir/file (#22)
* Add rendition:layout and rendition:orientation EPUB meta tags (supported by new kindlegen 2.8)
* Fixed natural sorting for files (#18)
####2.7 ####3.6:
* Lots of GUI improvements (#27, #13) * Increased quality of Panel View zoom
* Added gamma support within --gamma option (defaults to profile-specified gamma) (#26, #27) * Creation of multipart MOBI output is now faster on machines with 4GB+ RAM
* Added --nodithering option to prevent dithering optimizations (#27) * Automatic gamma correction now distinguishes color and grayscale images
* EPUB margins support (#30) * Added ComicRack metadata parser
* Fixed no file added if file has no spaces on Windows (#25) * Implemented new method to detect border color in non-webtoon comics
* Gracefully exit if unrar missing (#15) * Upscaling is now enabled by default for Kindle Fire HD/HDX
* Do not call kindlegen if source EPUB is bigger than 320MB (#17) * Windows nad Linux releases now have tray icon
* Get filetype from magic number (#14) * Fixed Kindle Fire HDX 7" output
* PDF conversion works again * Increased target resolution for Kindle DX/DXG CBZ output
####2.8 ####3.5:
* Updated rarfile library * Added simple content server - Converted files can be now delivered wireless
* Panel View support + HQ support (#36) - new option: --nopanelviewhq * Added proper Windows installer
* Split profiles for K4NT and K4T * Improved multiprocessing speed
* Rewrite of Landscape Mode support (huge readability improvement for KPW) * GUI tweaks and minor bug fixes
* Upscale use now BILINEAR method
* Added generic CSS file
* Optimized archive extraction for zip/rar files (#40)
####2.9 ####3.4:
* Added support for generating a plain CBZ (skipping all the EPUB/MOBI generation) (#45) * Improved PNG output
* Prevent output file overwriting the source one: if a duplicate name is detected, append _kcc to the name * Increased quality of upscaling
* Rarfile library updated to 2.6 * Added support of file association - KCC can now open CBZ, CBR, CB7, ZIP, RAR, 7Z and PDF files directly
* Added GIF, TIFF and BMP to supported formats (#42) * Paths that contain UTF-8 characters are now supported
* Filenames slugifications (#28, #31, #9, #8) * Migrated to new version of Pillow library
* Merged DX and DXG profiles
####2.10: * Many other minor bug fixes and GUI tweaks
* 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
@@ -281,105 +286,117 @@ The app relies and includes the following scripts:
* 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
* Fixed some MCD support bugs
* Default output format for Kindle DX is now CBZ
####4.3: ####2.0
* Added profiles for Kindle Voyage and Kobo Aura H2O * GUI! AppleScript is gone and Tk is used to provide cross-platform GUI support.
* Added missing features to CLI version
* Other minor bug fixes ####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.3' __version__ = '4.4.1'
__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.3' __version__ = '4.4.1'
__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.3" #define MyAppVersion "4.4.1"
#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

136
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.3' __version__ = '4.4.1'
__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,57 +28,13 @@ 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)))
@@ -96,79 +52,23 @@ elif sys.platform.startswith('win'):
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._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()
# 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.3' __version__ = '4.4.1'
__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,12 +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 Popen, Process
from copy import copy from copy import copy
from .shared import md5Checksum from distutils.version import StrictVersion
from .shared import md5Checksum, HTMLStripper
from . import comic2ebook from . import comic2ebook
from . import KCC_rc_web from . import KCC_rc_web
if sys.platform.startswith('darwin'): if sys.platform.startswith('darwin'):
@@ -49,6 +49,63 @@ 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()
class Icons: class Icons:
def __init__(self): def __init__(self):
self.deviceKindle = QtGui.QIcon() self.deviceKindle = QtGui.QIcon()
@@ -76,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):
@@ -111,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'))
@@ -127,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')
@@ -189,6 +233,8 @@ class VersionThread(QtCore.QThread):
QtCore.QThread.__init__(self) QtCore.QThread.__init__(self)
self.newVersion = '' self.newVersion = ''
self.md5 = '' self.md5 = ''
self.barProgress = 0
self.answer = None
def __del__(self): def __del__(self):
self.wait() self.wait()
@@ -200,22 +246,28 @@ class VersionThread(QtCore.QThread):
except Exception: except Exception:
return return
latestVersion = XML.childNodes[0].getElementsByTagName('latest')[0].childNodes[0].toxml() latestVersion = XML.childNodes[0].getElementsByTagName('latest')[0].childNodes[0].toxml()
if tuple(map(int, (latestVersion.split(".")))) > tuple(map(int, (__version__.split(".")))): if StrictVersion(latestVersion) > StrictVersion(__version__):
if sys.platform.startswith('win'): if sys.platform.startswith('win'):
self.newVersion = latestVersion self.newVersion = latestVersion
self.md5 = XML.childNodes[0].getElementsByTagName('WindowsMD5')[0].childNodes[0].toxml() self.md5 = XML.childNodes[0].getElementsByTagName('WindowsMD5')[0].childNodes[0].toxml()
MW.showDialog.emit('<b>New version released!</b> <a href="https://github.com/ciromattia/kcc/releases/">' MW.showDialog.emit('<b>New version released!</b> <a href="https://github.com/ciromattia/kcc/releases/">'
'See changelog.</a><<br/><br/>Installed version: ' + __version__ + 'See changelog.</a><br/><br/>Installed version: ' + __version__ +
'<br/>Current version: ' + latestVersion + '<br/>Current version: ' + latestVersion +
'<br/><br/>Would you like to start automatic update?', 'question') '<br/><br/>Would you like to start automatic update?', 'question')
self.getNewVersion()
else: else:
MW.addMessage.emit('<a href="http://kcc.iosphe.re/">' MW.addMessage.emit('<a href="http://kcc.iosphe.re/">'
'<b>New version is available!</b></a> ' '<b>New version is available!</b></a> '
'(<a href="https://github.com/ciromattia/kcc/releases/">' '(<a href="https://github.com/ciromattia/kcc/releases/">'
'Changelog</a>)', 'warning', False) 'Changelog</a>)', 'warning', False)
def getNewVersion(self, dialogAnswer): def setAnswer(self, dialogAnswer):
if dialogAnswer == QtWidgets.QMessageBox.Yes: self.answer = dialogAnswer
def getNewVersion(self):
while self.answer is None:
sleep(1)
if self.answer == QtWidgets.QMessageBox.Yes:
try: try:
MW.modeConvert.emit(-1) MW.modeConvert.emit(-1)
MW.progressBarTick.emit('Downloading update') MW.progressBarTick.emit('Downloading update')
@@ -234,9 +286,12 @@ class VersionThread(QtCore.QThread):
MW.modeConvert.emit(1) MW.modeConvert.emit(1)
def getNewVersionTick(self, size, blockSize, totalSize): def getNewVersionTick(self, size, blockSize, totalSize):
progress = int((size / (totalSize // blockSize)) * 100)
if size == 0: if size == 0:
MW.progressBarTick.emit(str(int(totalSize / blockSize))) MW.progressBarTick.emit('100')
MW.progressBarTick.emit('tick') if progress > self.barProgress:
self.barProgress = progress
MW.progressBarTick.emit('tick')
class ProgressThread(QtCore.QThread): class ProgressThread(QtCore.QThread):
@@ -525,6 +580,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:
@@ -550,6 +606,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()
@@ -838,9 +895,9 @@ class KCCGUI(KCC_ui.Ui_KCC):
if kind == 'error': if kind == 'error':
QtWidgets.QMessageBox.critical(MW, 'KCC - Error', message, QtWidgets.QMessageBox.Ok) QtWidgets.QMessageBox.critical(MW, 'KCC - Error', message, QtWidgets.QMessageBox.Ok)
elif kind == 'question': elif kind == 'question':
dialogResponse = QtWidgets.QMessageBox.question(MW, 'KCC - Question', message, GUI.versionCheck.setAnswer(QtWidgets.QMessageBox.question(MW, 'KCC - Question', message,
QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No) QtWidgets.QMessageBox.Yes,
MW.dialogAnswer.emit(dialogResponse) QtWidgets.QMessageBox.No))
def updateProgressbar(self, command): def updateProgressbar(self, command):
if command == 'tick': if command == 'tick':
@@ -885,15 +942,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):
@@ -954,10 +1015,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')
@@ -979,6 +1042,34 @@ 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 StrictVersion(versionCheck) < StrictVersion('2.9'):
self.addMessage('Your <a href="http://www.amazon.com/gp/feature.html?ie=UTF8&docId='
'1000765211">KindleGen</a> is outdated! MOBI conversion might fail.', '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
@@ -986,16 +1077,10 @@ class KCCGUI(KCC_ui.Ui_KCC):
MW = KCCWindow MW = KCCWindow
GUI = self GUI = self
self.setupUi(MW) self.setupUi(MW)
# User settings will be reverted to default ones if were created in one of the following versions
# Empty string cover all versions before this system was implemented
purgeSettingsVersions = ['']
self.icons = Icons() self.icons = Icons()
self.webContent = KCC_rc_web.WebContent() self.webContent = KCC_rc_web.WebContent()
self.settings = QtCore.QSettings('KindleComicConverter', 'KindleComicConverter') self.settings = QtCore.QSettings('KindleComicConverter', 'KindleComicConverter')
self.settingsVersion = self.settings.value('settingsVersion', '', type=str) self.settingsVersion = self.settings.value('settingsVersion', '', type=str)
if self.settingsVersion in purgeSettingsVersions:
QtCore.QSettings.clear(self.settings)
self.settingsVersion = self.settings.value('settingsVersion', '', type=str)
self.lastPath = self.settings.value('lastPath', '', type=str) self.lastPath = self.settings.value('lastPath', '', type=str)
self.lastDevice = self.settings.value('lastDevice', 0, type=int) self.lastDevice = self.settings.value('lastDevice', 0, type=int)
self.currentMode = self.settings.value('currentMode', 1, type=int) self.currentMode = self.settings.value('currentMode', 1, type=int)
@@ -1009,6 +1094,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 = ''
@@ -1016,11 +1102,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
@@ -1108,27 +1196,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:
@@ -1145,6 +1212,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)
@@ -1166,7 +1234,6 @@ class KCCGUI(KCC_ui.Ui_KCC):
MW.showDialog.connect(self.showDialog) MW.showDialog.connect(self.showDialog)
MW.hideProgressBar.connect(self.hideProgressBar) MW.hideProgressBar.connect(self.hideProgressBar)
MW.forceShutdown.connect(self.forceShutdown) MW.forceShutdown.connect(self.forceShutdown)
MW.dialogAnswer.connect(self.versionCheck.getNewVersion)
MW.closeEvent = self.saveSettings MW.closeEvent = self.saveSettings
MW.addTrayMessage.connect(self.tray.addTrayMessage) MW.addTrayMessage.connect(self.tray.addTrayMessage)

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.3' __version__ = '4.4.1'
__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.3' __version__ = '4.4.1'
__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
@@ -55,12 +57,21 @@ 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
@@ -409,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]))
@@ -433,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)
@@ -504,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:
@@ -512,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:
@@ -667,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)
@@ -812,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
@@ -828,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
@@ -842,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)
@@ -851,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
@@ -1144,7 +1160,10 @@ 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.format == 'CBZ': if options.format == 'CBZ':
@@ -1254,4 +1273,4 @@ def makeMOBI(work, qtGUI=None):
makeMOBIWorkerPool.apply_async(func=makeMOBIWorker, args=(i, ), callback=makeMOBIWorkerTick) makeMOBIWorkerPool.apply_async(func=makeMOBIWorker, args=(i, ), callback=makeMOBIWorkerTick)
makeMOBIWorkerPool.close() makeMOBIWorkerPool.close()
makeMOBIWorkerPool.join() makeMOBIWorkerPool.join()
return makeMOBIWorkerOutput 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.3' __version__ = '4.4.1'
__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:
@@ -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.3' __version__ = '4.4.1'
__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
@@ -87,7 +87,7 @@ class ProfileData:
'K345': ("Kindle", (600, 800), Palette16, 1.8, (900, 1200)), 'K345': ("Kindle", (600, 800), Palette16, 1.8, (900, 1200)),
'KDX': ("Kindle DX/DXG", (824, 1000), Palette16, 1.8, (1236, 1500)), 'KDX': ("Kindle DX/DXG", (824, 1000), Palette16, 1.8, (1236, 1500)),
'KPW': ("Kindle Paperwhite", (758, 1024), Palette16, 1.8, (1137, 1536)), 'KPW': ("Kindle Paperwhite", (758, 1024), Palette16, 1.8, (1137, 1536)),
'KV': ("Kindle Voyage", (1080, 1440), Palette16, 1.8, (1620, 2160)), 'KV': ("Kindle Voyage", (1072, 1448), Palette16, 1.8, (1608, 2172)),
'KFHD': ("K. Fire HD", (800, 1280), PalleteNull, 1.0, (1200, 1920)), 'KFHD': ("K. Fire HD", (800, 1280), PalleteNull, 1.0, (1200, 1920)),
'KFHDX': ("K. Fire HDX", (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)),
@@ -102,16 +102,15 @@ class ProfileData:
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,36 @@
# #
__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
from distutils.version import StrictVersion
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 +69,50 @@ 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.QtCore import qVersion as qtVersion
if StrictVersion('5.2.0') > StrictVersion(qtVersion()):
missing.append('PyQt 5.2.0+')
except ImportError:
missing.append('PyQt 5.2.0+')
if level > 1:
try:
from psutil import __version__ as psutilVersion
if StrictVersion('2.0.0') > StrictVersion(psutilVersion):
missing.append('psutil 2.0.0+')
except ImportError:
missing.append('psutil 2.0.0+')
try:
from slugify import __version__ as slugifyVersion
if StrictVersion('0.1.0') > StrictVersion(slugifyVersion):
missing.append('python-slugify 0.1.0+')
except ImportError:
missing.append('python-slugify 0.1.0+')
try:
from PIL import PILLOW_VERSION as pillowVersion
if StrictVersion('2.7.0') > StrictVersion(pillowVersion):
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

@@ -14,7 +14,7 @@ if version_info[0] != 3:
exit(1) exit(1)
NAME = "KindleComicConverter" NAME = "KindleComicConverter"
VERSION = "4.3" VERSION = "4.4.1"
MAIN = "kcc.py" MAIN = "kcc.py"
if platform == "darwin": if platform == "darwin":
@@ -26,17 +26,14 @@ if platform == "darwin":
py2app=dict( py2app=dict(
argv_emulation=True, argv_emulation=True,
iconfile='icons/comic2ebook.icns', iconfile='icons/comic2ebook.icns',
includes=['PIL', 'sip', 'PyQt5', 'PyQt5.QtCore', 'PyQt5.QtGui', 'PyQt5.QtNetwork', 'PyQt5.QtWidgets', includes=['sip', 'PyQt5.QtPrintSupport'],
'PyQt5.QtPrintSupport'],
resources=['LICENSE.txt', 'other/qt.conf', 'other/Additional-LICENSE.txt', 'other/unrar', 'other/7za'], resources=['LICENSE.txt', 'other/qt.conf', 'other/Additional-LICENSE.txt', 'other/unrar', 'other/7za'],
plist=dict( plist=dict(
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',
CFBundleSignature='dplt',
CFBundleDocumentTypes=[ CFBundleDocumentTypes=[
dict( dict(
CFBundleTypeExtensions=['cbz', 'cbr', 'cb7', 'zip', 'rar', '7z', 'pdf'], CFBundleTypeExtensions=['cbz', 'cbr', 'cb7', 'zip', 'rar', '7z', 'pdf'],
@@ -47,7 +44,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)'
) )
@@ -69,6 +66,7 @@ elif platform == "win32":
'other\\7za.exe', 'other\\7za.exe',
'other\\UnRAR.exe', 'other\\UnRAR.exe',
'other\\Additional-LICENSE.txt', 'other\\Additional-LICENSE.txt',
'C:\Python34' + suffix + '\Lib\site-packages\PyQt5\libGLESv2.dll',
'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": 1, options={'py2exe': {"bundle_files": 1,
@@ -81,7 +79,7 @@ elif platform == "win32":
windows=[{"script": MAIN, 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",

View File

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