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

Compare commits

...

88 Commits
4.0.1 ... 4.3.1

Author SHA1 Message Date
Paweł Jastrzębski
bd665c3261 Merge pull request #119 from ciromattia/4.x
4.3.1
2014-11-24 20:13:17 +01:00
Paweł Jastrzębski
94586fa590 Version bump 2014-11-24 19:07:50 +01:00
Paweł Jastrzębski
89806dfcfc Disabled MCD integration 2014-11-24 18:56:24 +01:00
Paweł Jastrzębski
c9eb73ab90 OS X: Miscellaneous fixes (close #115) 2014-11-24 18:47:53 +01:00
Paweł Jastrzębski
2edcc0369a Updated UnRAR 2014-11-22 15:22:31 +01:00
Paweł Jastrzębski
bc0a52b848 PEP 8 2014-11-21 22:46:43 +01:00
Paweł Jastrzębski
5ae72bf06d Fixed Kindle Voyage profile 2014-11-21 22:41:51 +01:00
Paweł Jastrzębski
24c32643c1 Tweaked glob support 2014-11-10 19:33:03 +01:00
Paweł Jastrzębski
40b988f964 Merge branch 'multifile' of git://github.com/theaquamarine/kcc into 4.x 2014-10-29 07:14:39 +01:00
blue
ac794eff85 Use glob to resolve file globs
For systems where the shell doesn't do glob expansion, eg Windows.
2014-10-28 18:34:14 +00:00
blue
eaa387a9d6 Add basic support for converting more >1 file 2014-10-28 16:51:31 +00:00
Paweł Jastrzębski
d0f5d6dac4 Tweaked title sorting 2014-10-17 14:15:35 +02:00
Paweł Jastrzębski
b174534c1c Merge pull request #113 from ciromattia/4.x
4.3
2014-09-29 18:05:32 +02:00
Paweł Jastrzębski
19d486a4ee Updated README 2014-09-28 22:01:22 +02:00
Paweł Jastrzębski
652762b709 Updated README + version bump 2014-09-28 13:23:40 +02:00
Paweł Jastrzębski
e6df87f8fd CLI: Added tool detection 2014-09-28 13:16:13 +02:00
Paweł Jastrzębski
1a9cd0beb5 Moved MOBI creation to main code (close #111) 2014-09-28 13:01:42 +02:00
Paweł Jastrzębski
108e351126 Refactored locking mechanism (close #112) 2014-09-27 11:44:42 +02:00
Paweł Jastrzębski
dfe3e10470 Updated setup 2014-09-22 18:35:53 +02:00
Paweł Jastrzębski
787ad9fa66 Updated multiple device profiles 2014-09-18 19:05:24 +02:00
Paweł Jastrzębski
f10e8869a9 Added Kobo Aura H2O profile 2014-09-03 07:16:26 +02:00
Paweł Jastrzębski
715ada328f Tweaked error reporting (close #109) 2014-08-24 09:56:59 +02:00
Paweł Jastrzębski
56f23ab488 Merge pull request #108 from ciromattia/4.x
4.2.1
2014-08-03 11:22:28 +02:00
Paweł Jastrzębski
996af59e00 Updated README + version bump 2014-08-03 11:21:43 +02:00
Paweł Jastrzębski
37aa84c4aa Fixed MOBI processing anomalies 2014-08-02 07:54:15 +02:00
Paweł Jastrzębski
50574632e6 Replaced margin color detection algorithm 2014-08-01 07:23:55 +02:00
Paweł Jastrzębski
0afb9e8c0b Kindle: Fixed high quality mode (close #106) 2014-07-31 21:49:03 +02:00
Paweł Jastrzębski
7511c7eed6 Kindle DX: Changed default output format to CBZ 2014-07-29 19:59:21 +02:00
Paweł Jastrzębski
836a4146f9 MCD: Fixed small bug 2014-07-23 20:55:31 +02:00
Paweł Jastrzębski
15a240ccea Merge pull request #103 from ciromattia/4.x
Version 4.2
2014-07-18 10:29:14 +02:00
Paweł Jastrzębski
0722ddf8b0 Updated README + version bump 2014-07-18 08:45:57 +02:00
Paweł Jastrzębski
b3159b94e7 Prevented hypothetical problems with batch processing 2014-07-18 08:24:12 +02:00
Paweł Jastrzębski
ef5207c990 Dropped Windows XP support 2014-07-10 19:11:04 +02:00
Paweł Jastrzębski
db77d89817 Fixed Other profile 2014-07-05 09:38:10 +02:00
Paweł Jastrzębski
4571fadadb Resolved problems with page order on Kobo 2014-07-04 09:59:21 +02:00
Paweł Jastrzębski
94f56238ae Corruption detection now also delete non-image files (close #99) 2014-07-03 18:16:57 +02:00
Paweł Jastrzębski
5efb5d6dbb Updated Pillow (close #95) 2014-07-03 18:16:57 +02:00
Paweł Jastrzębski
623f615dd9 Added Manga Cover Database support 2014-07-03 18:16:56 +02:00
Paweł Jastrzębski
39fbbc42b3 Made KindleGen detection more foolproof 2014-07-03 18:16:56 +02:00
Paweł Jastrzębski
99405ab8a6 Disabled ultra quality mode for CBZ format 2014-07-03 18:16:55 +02:00
Paweł Jastrzębski
aadfca8306 Code cleanup 2014-07-03 18:16:54 +02:00
Paweł Jastrzębski
5450502c2a Updated README 2014-06-08 07:52:19 +02:00
Paweł Jastrzębski
c976b06413 Make sure that KindleGen have execute permissions 2014-06-05 07:37:32 +02:00
Paweł Jastrzębski
b6facda95b Updated OS X libraries 2014-06-04 10:00:45 +02:00
Paweł Jastrzębski
3f608eb602 Version bump 2014-06-03 19:26:24 +02:00
Paweł Jastrzębski
104cd04994 Fixed title parsing 2014-06-02 20:31:48 +02:00
Paweł Jastrzębski
b323204628 Added additional cover processing 2014-06-02 19:55:20 +02:00
Paweł Jastrzębski
56195d301d Tweaked merge 2014-05-29 22:01:21 +02:00
Paweł Jastrzębski
287723ca6f Updated UnRAR 2014-05-29 21:56:14 +02:00
Paweł Jastrzębski
90490149c7 Merge branch 'theaquamarine-kindlegen' 2014-05-29 18:54:38 +02:00
blue
2210f484df Use makeBook to create book from GUI
Call comic2ebook.makeBook() rather than comic2ebook.main()
2014-05-29 18:28:38 +02:00
blue
d5502e85b0 Use makeParser() for options in KCC_gui
KCC_gui now uses comic2ebook.makeParser() and setting global variable
in comic2ebook to handle options for ebook creation.
2014-05-29 18:28:33 +02:00
blue
59b26cfc8b Split comic2ebook.py main in two again
Now has main() which initialises the program and makeParser() which
sets up the option parser.
2014-05-29 18:28:27 +02:00
blue
a722e5fa49 Split comic2ebook.py main in two
Now has main() which initialises program and makeBook which does the
actual work.
2014-05-29 18:28:14 +02:00
Paweł Jastrzębski
fce3072dca Updated to Python 3.4 and PyQt 5.3 (close #94) 2014-05-29 18:23:28 +02:00
Paweł Jastrzębski
f53cdf9cd7 Implemented new DualMetaFix version 2014-05-28 06:57:37 +02:00
Paweł Jastrzębski
de74318c69 Replaced cx_Freeze with py2exe 2014-05-25 11:08:28 +02:00
Paweł Jastrzębski
b137e69510 Dependency tweak 2014-05-25 07:29:03 +02:00
Paweł Jastrzębski
888663fa4c Miscellaneous processing tweaks 2014-05-18 09:09:38 +02:00
Paweł Jastrzębski
8a2ba96ac5 Tiny tweaks for Linux 2014-05-17 19:12:54 +02:00
Paweł Jastrzębski
cb6b0e0a7b Updated tooltips 2014-05-15 19:38:34 +02:00
Paweł Jastrzębski
49d2a7fbab Changed domain 2014-05-14 20:23:12 +02:00
Paweł Jastrzębski
3d84b27d58 Fixed tiny bugs 2014-05-05 18:32:46 +02:00
Paweł Jastrzębski
e98a23d0c0 Updated README 2014-05-05 12:08:15 +02:00
Paweł Jastrzębski
7f80dacea8 Finished implementation of DualMetaFix 2014-05-05 10:42:16 +02:00
Paweł Jastrzębski
6efb3dcef3 Preliminary implementation of DualMetaFix 2014-05-04 21:45:03 +02:00
Paweł Jastrzębski
942a828b7e Revert "Changed output extension to AZW3"
This reverts commit 64b199ef74.
2014-05-04 21:37:26 +02:00
Paweł Jastrzębski
df5ee1badf Overhauled dependency check 2014-04-30 20:38:23 +02:00
Paweł Jastrzębski
e3ab28642d Re-enabled tray icon on Linux 2014-04-30 20:11:25 +02:00
Ciro Mattia Gonano
a1831c45d5 Revert to ISC License 2014-04-30 03:00:03 +02:00
Paweł Jastrzębski
52bea9957a Updated README 2014-04-29 13:12:27 +02:00
Paweł Jastrzębski
a71339ec34 Changed license to GNU General Public License v3 (GPL-3) 2014-04-29 13:00:32 +02:00
Paweł Jastrzębski
c5f09c44b8 Bumped a little MOBI file size limit 2014-04-24 22:11:37 +02:00
Paweł Jastrzębski
64b199ef74 Changed output extension to AZW3
This is cosmetic change. All profiles for new Kindle models already created KF8/AZW3 files but used generic extension.
2014-04-21 08:39:16 +02:00
Paweł Jastrzębski
8dd0b0e694 Tweaked whitespace cropping + other small tweaks 2014-04-18 11:47:51 +02:00
Paweł Jastrzębski
181a2e8ab4 GUI tweak: Fixed rounding 2014-04-17 18:46:25 +02:00
Paweł Jastrzębski
3fdff845b7 Tiny code cleanup 2014-04-10 13:42:29 +02:00
Paweł Jastrzębski
2ee5dc310b Fixed No optimization mode (close #88) 2014-04-08 13:10:34 +02:00
Paweł Jastrzębski
f32e9560b5 Fixed Linux/OSX crash 2014-03-26 10:23:20 +01:00
Paweł Jastrzębski
621827c1c2 Performance tweaks for Windows 2014-03-22 11:16:25 +01:00
Paweł Jastrzębski
dc312f36c2 Updated OSX libs 2014-03-19 10:14:56 +01:00
Paweł Jastrzębski
4573ff6ec2 README update 2014-03-19 09:25:45 +01:00
Paweł Jastrzębski
d77498405b Updated to psutil 2.0 2014-03-13 21:58:15 +01:00
Paweł Jastrzębski
e491fca445 Version bump + README update 2014-03-13 21:26:26 +01:00
Paweł Jastrzębski
d22ee1a488 Tweaked HQ mode 2014-02-18 12:56:27 +01:00
Paweł Jastrzębski
7ebcccd8a2 Replaced os.rename with atomic os.replace
This should eliminate last problems with file locks on Windows
2014-02-13 18:35:17 +01:00
Paweł Jastrzębski
9a691c3c63 Windows: Fixed sys.stdout and sys.stderr 2014-02-06 11:45:37 +01:00
Paweł Jastrzębski
2b04a0298e Windows: Fixed abnormalities with version check
CX_Freeze working very, very strange with Python 3.3.3
2014-02-02 08:56:50 +01:00
27 changed files with 1436 additions and 1354 deletions

View File

@@ -110,7 +110,7 @@
<enum>Qt::NoFocus</enum>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&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;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&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>
</property>
<property name="text">
<string>Webtoon mode</string>
@@ -386,7 +386,7 @@
<enum>Qt::NoFocus</enum>
</property>
<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;br/&gt;&lt;/span&gt;&lt;span style=&quot; font-style:italic;&quot;&gt;Use it when Panel View support is not needed.&lt;br/&gt;&lt;/span&gt;- Maximum quality when zoom is not enabled.&lt;br/&gt;- Poor quality when zoom is enabled.&lt;br/&gt;- Lowest file size.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Indeterminate - High quality mode&lt;br/&gt;&lt;/span&gt;&lt;span style=&quot; font-style:italic;&quot;&gt;Not zoomed images &lt;/span&gt;&lt;span style=&quot; font-weight:600; font-style:italic;&quot;&gt;might&lt;/span&gt;&lt;span style=&quot; font-style:italic;&quot;&gt; be blurry.&lt;br/&gt;&lt;/span&gt;- High quality when zoom is not enabled.&lt;br/&gt;- Maximum quality when zoom is enabled.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Checked - Ultra quality mode&lt;br/&gt;&lt;/span&gt;&lt;span style=&quot; font-style:italic;&quot;&gt;Maximum possible quality.&lt;br/&gt;&lt;/span&gt;- Maximum quality when zoom is not enabled.&lt;br/&gt;- Maximum quality when zoom is enabled.&lt;br/&gt;- Very high file size.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<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>
</property>
<property name="text">
<string>High/Ultra quality</string>

View File

@@ -110,7 +110,7 @@
<enum>Qt::NoFocus</enum>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&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;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&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>
</property>
<property name="text">
<string>Webtoon mode</string>
@@ -391,7 +391,7 @@
<enum>Qt::NoFocus</enum>
</property>
<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;br/&gt;&lt;/span&gt;&lt;span style=&quot; font-style:italic;&quot;&gt;Use it when Panel View support is not needed.&lt;br/&gt;&lt;/span&gt;- Maximum quality when zoom is not enabled.&lt;br/&gt;- Poor quality when zoom is enabled.&lt;br/&gt;- Lowest file size.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Indeterminate - High quality mode&lt;br/&gt;&lt;/span&gt;&lt;span style=&quot; font-style:italic;&quot;&gt;Not zoomed image &lt;/span&gt;&lt;span style=&quot; font-weight:600; font-style:italic;&quot;&gt;might&lt;/span&gt;&lt;span style=&quot; font-style:italic;&quot;&gt; be a little blurry.&lt;br/&gt;&lt;/span&gt;- High quality when zoom is not enabled.&lt;br/&gt;- Maximum quality when zoom is enabled.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Checked - Ultra quality mode&lt;br/&gt;&lt;/span&gt;&lt;span style=&quot; font-style:italic;&quot;&gt;Maximum possible quality.&lt;br/&gt;&lt;/span&gt;- Maximum quality when zoom is not enabled.&lt;br/&gt;- Maximum quality when zoom is enabled.&lt;br/&gt;- Very high file size.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<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>
</property>
<property name="text">
<string>High/Ultra quality</string>

4
KCC.ui
View File

@@ -94,7 +94,7 @@
<enum>Qt::NoFocus</enum>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&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;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=&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>
</property>
<property name="text">
<string>Webtoon mode</string>
@@ -338,7 +338,7 @@
<enum>Qt::NoFocus</enum>
</property>
<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;br/&gt;&lt;/span&gt;&lt;span style=&quot; font-style:italic;&quot;&gt;Use it when Panel View support is not needed.&lt;/span&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;&lt;br/&gt;&lt;/span&gt;- Maximum quality when zoom is not enabled.&lt;br/&gt;- Poor quality when zoom is enabled.&lt;br/&gt;- Lowest file size.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Indeterminate - High quality mode&lt;br/&gt;&lt;/span&gt;&lt;span style=&quot; font-style:italic;&quot;&gt;Not zoomed images &lt;/span&gt;&lt;span style=&quot; font-weight:600; font-style:italic;&quot;&gt;might &lt;/span&gt;&lt;span style=&quot; font-style:italic;&quot;&gt;be blurry.&lt;/span&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;&lt;br/&gt;&lt;/span&gt;- High quality when zoom is not enabled.&lt;br/&gt;- Maximum quality when zoom is enabled.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Checked - Ultra quality mode&lt;br/&gt;&lt;/span&gt;&lt;span style=&quot; font-style:italic;&quot;&gt;Maximum possible quality.&lt;/span&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;&lt;br/&gt;&lt;/span&gt;- Maximum quality when zoom is not enabled.&lt;br/&gt;- Maximum quality when zoom is enabled.&lt;br/&gt;- Very high file size.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<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>
</property>
<property name="text">
<string>High/Ultra quality</string>

View File

@@ -1,7 +1,7 @@
ISC LICENSE
Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
Copyright (c) 2013-2014 Paweł Jastrzębski <pawelj@vulturis.eu>
Copyright (c) 2013-2014 Paweł Jastrzębski <pawelj@iosphe.re>
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the

139
README.md
View File

@@ -1,13 +1,13 @@
# KCC
**Kindle Comic Converter** is a Python app to convert comic files or folders to ePub, Panel View MOBI or E-Ink optimized CBZ.
It was initally developed for Kindle but since v2.2 it outputs valid ePub 2.0 so _**despite its name, KCC is
actually a comic to EPUB converter that every e-reader owner can happily use**_.
**Kindle Comic Converter** is a Python app to convert comic/manga files or folders to EPUB, Panel View MOBI or E-Ink optimized CBZ.
It was initially developed for Kindle but since version 2.2 it outputs valid EPUB 2.0 so _**despite its name, KCC is
actually a comic/manga to EPUB converter that every e-reader owner can happily use**_.
It can also optionally optimize images by applying a number of transformations.
### A word of warning
**KCC** _is not_ [Amazon's Kindle Comic Creator](http://www.amazon.com/gp/feature.html?ie=UTF8&docId=1001103761) nor is in any way endorsed by Amazon.
Amazon's tool is for comic publishers and involves a lot of manual effort, while **KCC** is for comic readers.
Amazon's tool is for comic publishers and involves a lot of manual effort, while **KCC** is for comic/manga readers.
_KC2_ in no way is a replacement for **KCC** so you can be quite confident we'll going to carry on developing our little monster ;-)
### Issues / new features / donations
@@ -17,14 +17,18 @@ 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:
* Ciro Mattia Gonano: [![Donate](https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=D8WNYNPBGDAS2) [![Flattr this](http://api.flattr.com/button/flattr-badge-large.png)](http://flattr.com/thing/2260449/ciromattiakcc-on-GitHub)
* Paweł Jastrzębski: [![Donate](https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YTTJ4LK2JDHPS) [![1W15wwqsfd7wbaZ6wvSJf1LW1bz6q5L8b](http://s30.postimg.org/6z3kwvdlp/BC_Rnd.png)](bitcoin:1W15wwqsfd7wbaZ6wvSJf1LW1bz6q5L8b?label=KCC) [1W15wwqsfd7wbaZ6wvSJf1LW1bz6q5L8b](bitcoin:1W15wwqsfd7wbaZ6wvSJf1LW1bz6q5L8b?label=KCC)
- Ciro Mattia Gonano:
- [![Donate](https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=D8WNYNPBGDAS2)
- [![Flattr this](http://api.flattr.com/button/flattr-badge-large.png)](http://flattr.com/thing/2260449/ciromattiakcc-on-GitHub)
- Paweł Jastrzębski:
- [![Donate](https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YTTJ4LK2JDHPS)
- Bitcoin: 1W15wwqsfd7wbaZ6wvSJf1LW1bz6q5L8b
## BINARY RELEASES
You can find the latest released binary at the following links:
- **Windows:** [http://kcc.vulturis.eu/Windows/](http://kcc.vulturis.eu/Windows/)
- **Linux:** [http://kcc.vulturis.eu/Linux/](http://kcc.vulturis.eu/Linux/)
- **OS X (10.8+):** [http://kcc.vulturis.eu/OSX/](http://kcc.vulturis.eu/OSX/)
- **Windows (Vista or newer):** [http://kcc.iosphe.re/Windows/](http://kcc.iosphe.re/Windows/)
- **Linux:** [http://kcc.iosphe.re/Linux/](http://kcc.iosphe.re/Linux/)
- **OS X (10.8+):** [http://kcc.iosphe.re/OSX/](http://kcc.iosphe.re/OSX/)
## INPUT FORMATS
**KCC** can understand and convert, at the moment, the following input types:
@@ -32,7 +36,7 @@ You can find the latest released binary at the following links:
- CBZ, ZIP
- CBR, RAR *(With `unrar` executable)*
- CB7, 7Z *(With `7za` executable)*
- PDF *(Extracting only contained JPG images)*
- PDF *(Only extracting JPG images)*
## OPTIONAL REQUIREMENTS
- [KindleGen](http://www.amazon.com/gp/feature.html?ie=UTF8&docId=1000765211) v2.9+ in a directory reachable by your _PATH_ or in _KCC_ directory *(For MOBI generation)*
@@ -40,10 +44,10 @@ You can find the latest released binary at the following links:
- [7za](http://www.7-zip.org/download.html) *(For 7z/CB7 support)*
### For running from source:
- Python 3.3
- [PyQt5](http://www.riverbankcomputing.co.uk/software/pyqt/download5)
- [Pillow](http://pypi.python.org/pypi/Pillow/) 2.3.0+
- [psutil](https://pypi.python.org/pypi/psutil)
- Python 3.3+
- [PyQt5](http://www.riverbankcomputing.co.uk/software/pyqt/download5) 5.2.0+
- [Pillow](http://pypi.python.org/pypi/Pillow/) 2.5.0+
- [psutil](https://pypi.python.org/pypi/psutil) 2.0+
- [python-slugify](http://pypi.python.org/pypi/python-slugify)
On Debian based distributions these two commands should install all dependencies:
@@ -53,8 +57,8 @@ sudo pip3 install pillow python-slugify psutil
```
### For freezing code:
- Windows - [cx_Freeze](https://bitbucket.org/anthony_tuininga/cx_freeze) version 4.3.2 with [this](https://bitbucket.org/anthony_tuininga/cx_freeze/pull-request/29/conversions-to-support-untranslated-wide) patchset.
- OS X - [py2app](https://bitbucket.org/ronaldoussoren/py2app) HEAD version.
- Windows - [py2exe](https://pypi.python.org/pypi/py2exe) 0.9.2+
- OS X - [py2app](https://bitbucket.org/ronaldoussoren/py2app) 0.8.0+
## USAGE
@@ -63,15 +67,19 @@ After completed conversion you should find ready file alongside the original inp
Please check [our wiki](https://github.com/ciromattia/kcc/wiki/) for more details.
### Standalone `comic2ebook.py` usage:
CLI version of **KCC** is intended for power users. It is not idiot-proof like GUI :-)
### Standalone `kcc-c2e.py` usage:
```
Usage: comic2ebook.py [options] comic_file|comic_folder
Usage: kcc-c2e [options] comic_file|comic_folder
Options:
MAIN:
-p PROFILE, --profile=PROFILE
Device profile (Choose one among K1, K2, K345, KDX, KHD, KF, KFHD, KFHD8, KFHDX, KFHDX8, KFA, KoMT, KoG, KoA, KoAHD) [Default=KHD]
Device profile (Available options: K1, K2, K345, KDX,
KPW, KV, KFHD, KFHDX, KFHDX8, KFA, KoMT, KoG, KoA,
KoAHD, KoAH2O) [Default=KV]
-q QUALITY, --quality=QUALITY
Quality of Panel View. 0 - Normal 1 - High 2 - Ultra [Default=0]
-m, --manga-style Manga style (Right-to-left reading and splitting)
@@ -82,7 +90,9 @@ Options:
Output generated file to specified directory or file
-t TITLE, --title=TITLE
Comic title [Default=filename or directory name]
--cbz-output Outputs a CBZ archive and does not generate EPUB
-f FORMAT, --format=FORMAT
Output format (Available options: Auto, MOBI,
EPUB, CBZ) [Default=Auto]
--batchsplit Split output into multiple files
PROCESSING:
@@ -108,10 +118,10 @@ Options:
-h, --help Show this help message and exit
```
### Standalone `comic2panel.py` usage:
### Standalone `kcc-c2p.py` usage:
```
Usage: comic2panel.py [options] comic_folder
Usage: kcc-c2p [options] comic_folder
Options:
MANDATORY:
@@ -126,29 +136,27 @@ Options:
```
## CREDITS
**KCC** is made by [Ciro Mattia Gonano](http://github.com/ciromattia) and [Paweł Jastrzębski](http://github.com/AcidWeb)
**KCC** is made by [Ciro Mattia Gonano](http://github.com/ciromattia) and [Paweł Jastrzębski](http://github.com/AcidWeb).
This script born as a cross-platform alternative to `KindleComicParser` by **Dc5e** (published [here](http://www.mobileread.com/forums/showthread.php?t=192783))
This script born as a cross-platform alternative to `KindleComicParser` by **Dc5e** (published [here](http://www.mobileread.com/forums/showthread.php?t=192783)).
The app relies and includes the following scripts/binaries:
The app relies and includes the following scripts:
- `KindleUnpack` script by Charles **M. Hannum, P. Durrant, K. Hendricks, S. Siebert, fandrieu, DiapDealer, nickredding**. Released with GPLv3 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.
- `image.py` class from **Alex Yatskov**'s [Mangle](http://foosoft.net/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.
## SAMPLE FILES CREATED BY KCC
* [Kindle Paperwhite](http://kcc.vulturis.eu/Samples/Ubunchu!-KPW.mobi)
* [Kindle](http://kcc.vulturis.eu/Samples/Ubunchu!-K345.mobi)
* [Kindle DX/DXG](http://kcc.vulturis.eu/Samples/Ubunchu!-KDX.mobi)
* [Kindle Fire HD](http://kcc.vulturis.eu/Samples/Ubunchu!-KFHD.mobi)
* [Kindle Fire HD 8.9"](http://kcc.vulturis.eu/Samples/Ubunchu!-KFHD8.mobi)
* [Kindle Fire HDX](http://kcc.vulturis.eu/Samples/Ubunchu!-KFHDX.mobi)
* [Kindle Fire HDX 8.9"](http://kcc.vulturis.eu/Samples/Ubunchu!-KFHDX8.mobi)
* [Kobo Mini/Touch](http://kcc.vulturis.eu/Samples/Ubunchu!-KoMT.cbz)
* [Kobo Glow](http://kcc.vulturis.eu/Samples/Ubunchu!-KoG.cbz)
* [Kobo Aura](http://kcc.vulturis.eu/Samples/Ubunchu!-KoA.cbz)
* [Kobo Aura HD](http://kcc.vulturis.eu/Samples/Ubunchu!-KoAHD.cbz)
* [Kindle Voyage](http://kcc.iosphe.re/Samples/Ubunchu!-KV.mobi)
* [Kindle Paperwhite](http://kcc.iosphe.re/Samples/Ubunchu!-KPW.mobi)
* [Kindle](http://kcc.iosphe.re/Samples/Ubunchu!-K345.mobi)
* [Kindle DX/DXG](http://kcc.iosphe.re/Samples/Ubunchu!-KDX.cbz)
* [Kobo Mini/Touch](http://kcc.iosphe.re/Samples/Ubunchu!-KoMT.cbz)
* [Kobo Glow](http://kcc.iosphe.re/Samples/Ubunchu!-KoG.cbz)
* [Kobo Aura](http://kcc.iosphe.re/Samples/Ubunchu!-KoA.cbz)
* [Kobo Aura HD](http://kcc.iosphe.re/Samples/Ubunchu!-KoAHD.cbz)
* [Kobo Aura H2O](http://kcc.iosphe.re/Samples/Ubunchu!-KoAH2O.cbz)
## CHANGELOG
####1.0
@@ -184,11 +192,11 @@ The app relies and includes the following scripts/binaries:
* Added basic error reporting
####2.2:
* Added (valid!) ePub 2.0 output
* Added (valid!) EPUB 2.0 output
* Rename .zip files to .cbz to avoid overwriting
####2.3
* Fixed win32 ePub generation, folder handling, filenames with spaces and subfolders
* Fixed win32 EPUB generation, folder handling, filenames with spaces and subfolders
####2.4
* Use temporary directory as workdir (fixes converting from external volumes and zipfiles renaming)
@@ -196,22 +204,22 @@ The app relies and includes the following scripts/binaries:
####2.5
* Added --black-borders option to set added borders black when page's ratio is not the device's one (#11).
* Fixes epub containing zipped itself (#10)
* Fixes EPUB containing zipped itself (#10)
####2.6
* Added --rotate option to rotate landscape images instead of splitting them (#16, #24)
* Added --output option to customize ePub output dir/file (#22)
* Add rendition:layout and rendition:orientation ePub meta tags (supported by new kindlegen 2.8)
* 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
* 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)
* 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)
* Do not call kindlegen if source EPUB is bigger than 320MB (#17)
* Get filetype from magic number (#14)
* PDF conversion works again
@@ -225,7 +233,7 @@ The app relies and includes the following scripts/binaries:
* Optimized archive extraction for zip/rar files (#40)
####2.9
* Added support for generating a plain CBZ (skipping all the EPUB/Mobi generation) (#45)
* Added support for generating a plain CBZ (skipping all the EPUB/MOBI generation) (#45)
* Prevent output file overwriting the source one: if a duplicate name is detected, append _kcc to the name
* Rarfile library updated to 2.6
* Added GIF, TIFF and BMP to supported formats (#42)
@@ -233,7 +241,7 @@ The app relies and includes the following scripts/binaries:
####2.10:
* Multiprocessing support
* Kindle Fire support (color ePub/Mobi)
* 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
@@ -339,6 +347,43 @@ The app relies and includes the following scripts/binaries:
* Improved performance of WebToon splitter
* Tweaked margin color detection
####4.0.2:
* Fixed some Windows and OSX specific bugs
* Fixed problem with marigns when using HQ mode
####4.1:
* Thanks to code contributed by Kevin Hendricks speed of MOBI creation was greatly increased
* Improved performance on Windows
* Improved MOBI splitting and changed maximal size of output file
* Fixed _No optimization_ mode
* Multiple small tweaks nad minor bug fixes
####4.2:
* Added [Manga Cover Database](http://manga.joentjuh.nl/) support
* Officially dropped Windows XP support
* Fixed _Other_ profile
* Fixed problems with page order on stock KOBO CBZ reader
* Many other small bug fixes and tweaks
####4.2.1:
* Improved margin color detection
* Fixed random crashes of MOBI processing step
* Fixed resizing problems in high quality mode
* Fixed some MCD support bugs
* Default output format for Kindle DX is now CBZ
####4.3:
* Added profiles for Kindle Voyage and Kobo Aura H2O
* Added missing features to CLI version
* Other minor bug fixes
####4.3.1:
* Fixed Kindle Voyage profile
* Fixed some bugs in OS X release
* CLI version now support multiple input files at once
* Disabled MCB support
* Other minor tweaks
## KNOWN ISSUES
Please check [wiki page](https://github.com/ciromattia/kcc/wiki/Known-issues).

View File

@@ -2,7 +2,7 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
# Copyright (c) 2013-2014 Pawel Jastrzebski <pawelj@vulturis.eu>
# Copyright (c) 2013-2014 Pawel Jastrzebski <pawelj@iosphe.re>
#
# Permission to use, copy, modify, and/or distribute this software for
# any purpose with or without fee is hereby granted, provided that the
@@ -18,9 +18,9 @@
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
__version__ = '4.0.1'
__version__ = '4.3.1'
__license__ = 'ISC'
__copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@vulturis.eu>'
__copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>'
__docformat__ = 'restructuredtext en'
import sys
@@ -28,25 +28,27 @@ if sys.version_info[0] != 3:
print('ERROR: This is Python 3 script!')
exit(1)
# Dependiences check
# Dependency check
missing = []
try:
# noinspection PyUnresolvedReferences
from psutil import TOTAL_PHYMEM, Popen
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')
missing.append('psutil 2.0.0+')
try:
# noinspection PyUnresolvedReferences
from slugify import slugify
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')
try:
# noinspection PyUnresolvedReferences
from PIL import Image, ImageOps, ImageStat, ImageChops
if tuple(map(int, ('2.3.0'.split(".")))) > tuple(map(int, (Image.PILLOW_VERSION.split(".")))):
missing.append('Pillow 2.3.0+')
except ImportError:
missing.append('Pillow 2.3.0+')
if len(missing) > 0:
try:
# noinspection PyUnresolvedReferences
@@ -61,10 +63,10 @@ if len(missing) > 0:
exit(1)
from multiprocessing import freeze_support
from kcc.comic2ebook import main, Copyright
from kcc.comic2ebook import main
if __name__ == "__main__":
freeze_support()
Copyright()
print(('comic2ebook v%(__version__)s. Written by Ciro Mattia Gonano and Pawel Jastrzebski.' % globals()))
main(sys.argv[1:])
sys.exit(0)

View File

@@ -2,7 +2,7 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
# Copyright (c) 2013-2014 Pawel Jastrzebski <pawelj@vulturis.eu>
# Copyright (c) 2013-2014 Pawel Jastrzebski <pawelj@iosphe.re>
#
# Permission to use, copy, modify, and/or distribute this software for
# any purpose with or without fee is hereby granted, provided that the
@@ -18,25 +18,25 @@
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
__version__ = '4.3.1'
__license__ = 'ISC'
__copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>'
__docformat__ = 'restructuredtext en'
import sys
if sys.version_info[0] != 3:
print('ERROR: This is Python 3 script!')
exit(1)
__version__ = '4.0.1'
__license__ = 'ISC'
__copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@vulturis.eu>'
__docformat__ = 'restructuredtext en'
# Dependiences check
# Dependency check
missing = []
try:
# noinspection PyUnresolvedReferences
from PIL import Image, ImageOps, ImageStat, ImageChops
if tuple(map(int, ('2.3.0'.split(".")))) > tuple(map(int, (Image.PILLOW_VERSION.split(".")))):
missing.append('Pillow 2.3.0+')
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.3.0+')
missing.append('Pillow 2.5.0+')
if len(missing) > 0:
try:
# noinspection PyUnresolvedReferences
@@ -51,10 +51,10 @@ if len(missing) > 0:
exit(1)
from multiprocessing import freeze_support
from kcc.comic2panel import main, Copyright
from kcc.comic2panel import main
if __name__ == "__main__":
freeze_support()
Copyright()
print(('comic2ebook v%(__version__)s. Written by Ciro Mattia Gonano and Pawel Jastrzebski.' % globals()))
main(sys.argv[1:])
sys.exit(0)

21
kcc.iss
View File

@@ -1,7 +1,7 @@
#define MyAppName "Kindle Comic Converter"
#define MyAppVersion "4.0.1"
#define MyAppVersion "4.3.1"
#define MyAppPublisher "Ciro Mattia Gonano, Paweł Jastrzębski"
#define MyAppURL "http://kcc.vulturis.eu/"
#define MyAppURL "http://kcc.iosphe.re/"
#define MyAppExeName "KCC.exe"
[Setup]
@@ -30,6 +30,7 @@ UninstallDisplayIcon={app}\{#MyAppExeName}
ChangesAssociations=True
InfoAfterFile=other\InstallWarning.rtf
SignTool=SignTool /d $q{#MyAppName}$q /du $q{#MyAppURL}$q $f
MinVersion=0,6.0
[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"
@@ -42,18 +43,14 @@ Name: "CB7association"; Description: "CB7"; GroupDescription: "File associations
[Files]
; x64 files
Source: "build\exe.win-amd64-3.3\imageformats\*"; DestDir: "{app}\imageformats\"; Flags: ignoreversion; Check: Is64BitInstallMode
Source: "build\exe.win-amd64-3.3\platforms\*"; DestDir: "{app}\platforms\"; Flags: ignoreversion; Check: Is64BitInstallMode
Source: "build\exe.win-amd64-3.3\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion; Check: Is64BitInstallMode
Source: "build\exe.win-amd64-3.3\*.pyd"; DestDir: "{app}"; Flags: ignoreversion; Check: Is64BitInstallMode
Source: "build\exe.win-amd64-3.3\*.dll"; DestDir: "{app}"; Flags: ignoreversion; Check: Is64BitInstallMode
Source: "dist_64\platforms\*"; DestDir: "{app}\platforms\"; Flags: ignoreversion; Check: Is64BitInstallMode
Source: "dist_64\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion; Check: Is64BitInstallMode
Source: "dist_64\*.dll"; DestDir: "{app}"; Flags: ignoreversion; Check: Is64BitInstallMode
Source: "other\vcredist_x64.exe"; DestDir: "{tmp}"; Flags: ignoreversion deleteafterinstall; Check: Is64BitInstallMode
; x86 files
Source: "build\exe.win32-3.3\imageformats\*"; DestDir: "{app}\imageformats\"; Flags: ignoreversion; Check: not Is64BitInstallMode
Source: "build\exe.win32-3.3\platforms\*"; DestDir: "{app}\platforms\"; Flags: ignoreversion; Check: not Is64BitInstallMode
Source: "build\exe.win32-3.3\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion; Check: not Is64BitInstallMode
Source: "build\exe.win32-3.3\*.pyd"; DestDir: "{app}"; Flags: ignoreversion; Check: not Is64BitInstallMode
Source: "build\exe.win32-3.3\*.dll"; DestDir: "{app}"; Flags: ignoreversion; Check: not Is64BitInstallMode
Source: "dist\platforms\*"; DestDir: "{app}\platforms\"; Flags: ignoreversion; Check: not Is64BitInstallMode
Source: "dist\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion; Check: not Is64BitInstallMode
Source: "dist\*.dll"; DestDir: "{app}"; Flags: ignoreversion; Check: not Is64BitInstallMode
Source: "other\vcredist_x86.exe"; DestDir: "{tmp}"; Flags: ignoreversion deleteafterinstall; Check: not Is64BitInstallMode
; Common files
Source: "LICENSE.txt"; DestDir: "{app}"; Flags: ignoreversion solidbreak

95
kcc.py
View File

@@ -2,7 +2,7 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
# Copyright (c) 2013-2014 Pawel Jastrzebski <pawelj@vulturis.eu>
# Copyright (c) 2013-2014 Pawel Jastrzebski <pawelj@iosphe.re>
#
# Permission to use, copy, modify, and/or distribute this software for
# any purpose with or without fee is hereby granted, provided that the
@@ -18,9 +18,9 @@
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
__version__ = '4.0.1'
__version__ = '4.3.1'
__license__ = 'ISC'
__copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@vulturis.eu>'
__copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>'
__docformat__ = 'restructuredtext en'
import sys
@@ -28,30 +28,34 @@ if sys.version_info[0] != 3:
print('ERROR: This is Python 3 script!')
exit(1)
# Dependiences check
# Dependency check
missing = []
try:
# noinspection PyUnresolvedReferences
from PyQt5 import QtCore, QtGui, QtNetwork, QtWidgets
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')
missing.append('PyQt5 5.2.0+')
try:
# noinspection PyUnresolvedReferences
from psutil import TOTAL_PHYMEM, Popen
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')
missing.append('psutil 2.0.0+')
try:
# noinspection PyUnresolvedReferences
from slugify import slugify
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')
try:
# noinspection PyUnresolvedReferences
from PIL import Image, ImageOps, ImageStat, ImageChops
if tuple(map(int, ('2.3.0'.split(".")))) > tuple(map(int, (Image.PILLOW_VERSION.split(".")))):
missing.append('Pillow 2.3.0+')
except ImportError:
missing.append('Pillow 2.3.0+')
if len(missing) > 0:
try:
# noinspection PyUnresolvedReferences
@@ -70,14 +74,21 @@ from multiprocessing import freeze_support
from kcc import KCC_gui
# OS specific PATH variable workarounds
if sys.platform.startswith('darwin'):
if 'RESOURCEPATH' 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']
if sys.platform.startswith('darwin') and 'RESOURCEPATH' not in os.environ:
os.environ['PATH'] = os.path.dirname(os.path.abspath(__file__)) + '/other/:' + os.environ['PATH']
elif sys.platform.startswith('win'):
if getattr(sys, 'frozen', False):
os.chdir(os.path.dirname(os.path.abspath(sys.executable)))
# Implementing dummy stdout and stderr for frozen Windows release
class FakeSTD(object):
def write(self, string):
pass
def flush(self):
pass
sys.stdout = FakeSTD()
sys.stderr = FakeSTD()
else:
os.environ['PATH'] = os.path.dirname(os.path.abspath(__file__)) + '/other/;' + os.environ['PATH']
os.chdir(os.path.dirname(os.path.abspath(__file__)))
@@ -89,35 +100,33 @@ class QApplicationMessaging(QtWidgets.QApplication):
def __init__(self, argv):
QtWidgets.QApplication.__init__(self, argv)
self._memory = QtCore.QSharedMemory(self)
self._memory.setKey('KCC')
if self._memory.attach():
self._running = True
else:
self._running = False
self._memory.create(1)
self._key = 'KCC'
self._timeout = 1000
self._server = QtNetwork.QLocalServer(self)
if not self.isRunning():
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 self._memory.isAttached():
self._memory.detach()
if not self._locked:
self._server.close()
def event(self, e):
if e.type() == QtCore.QEvent.FileOpen:
# noinspection PyArgumentList
self.messageFromOtherInstance.emit(bytes(e.file(), 'UTF-8'))
return True
else:
return QtWidgets.QApplication.event(self, e)
def isRunning(self):
return self._running
return self._locked
def handleMessage(self):
socket = self._server.nextPendingConnection()
@@ -125,18 +134,12 @@ class QApplicationMessaging(QtWidgets.QApplication):
self.messageFromOtherInstance.emit(socket.readAll().data())
def sendMessage(self, message):
if self.isRunning():
socket = QtNetwork.QLocalSocket(self)
socket.connectToServer(self._key, QtCore.QIODevice.WriteOnly)
if not socket.waitForConnected(self._timeout):
return False
# noinspection PyArgumentList
socket.write(bytes(message, 'UTF-8'))
if not socket.waitForBytesWritten(self._timeout):
return False
socket.disconnectFromServer()
return True
return False
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

View File

@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
# Copyright (c) 2013-2014 Pawel Jastrzebski <pawelj@vulturis.eu>
# Copyright (c) 2013-2014 Pawel Jastrzebski <pawelj@iosphe.re>
#
# Permission to use, copy, modify, and/or distribute this software for
# any purpose with or without fee is hereby granted, provided that the
@@ -17,9 +17,9 @@
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
__version__ = '4.0.1'
__version__ = '4.3.1'
__license__ = 'ISC'
__copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@vulturis.eu>'
__copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>'
__docformat__ = 'restructuredtext en'
import os
@@ -36,10 +36,10 @@ from subprocess import STDOUT, PIPE
from PyQt5 import QtGui, QtCore, QtWidgets
from xml.dom.minidom import parse
from html.parser import HTMLParser
from psutil import TOTAL_PHYMEM, Popen
from psutil import Popen, Process
from copy import copy
from .shared import md5Checksum
from . import comic2ebook
from . import kindlesplit
from . import KCC_rc_web
if sys.platform.startswith('darwin'):
from . import KCC_ui_osx as KCC_ui
@@ -111,15 +111,15 @@ class WebServerHandler(BaseHTTPRequestHandler):
self.send_header('Content-type', 'text/html')
self.end_headers()
self.wfile.write(bytes('<!DOCTYPE html>\n'
'<html lang="en">\n'
'<head><meta charset="utf-8">\n'
'<link href="' + GUI.webContent.favicon + '" rel="icon" type="image/x-icon" />\n'
'<title>Kindle Comic Converter</title>\n'
'</head>\n'
'<body>\n'
'<div style="text-align: center; font-size:25px">\n'
'<p style="font-size:50px">- <img style="vertical-align: middle" '
'alt="KCC Logo" src="' + GUI.webContent.logo + '" /> -</p>\n', 'UTF-8'))
'<html lang="en">\n'
'<head><meta charset="utf-8">\n'
'<link href="' + GUI.webContent.favicon + '" rel="icon" type="image/x-icon" />\n'
'<title>Kindle Comic Converter</title>\n'
'</head>\n'
'<body>\n'
'<div style="text-align: center; font-size:25px">\n'
'<p style="font-size:50px">- <img style="vertical-align: middle" '
'alt="KCC Logo" src="' + GUI.webContent.logo + '" /> -</p>\n', 'UTF-8'))
if len(GUI.completedWork) > 0 and not GUI.conversionAlive:
for key in sorted(GUI.completedWork.keys()):
self.wfile.write(bytes('<p><a href="' + key + '">' + key.split('.')[0] + '</a></p>\n', 'UTF-8'))
@@ -127,8 +127,8 @@ class WebServerHandler(BaseHTTPRequestHandler):
self.wfile.write(bytes('<p style="font-weight: bold">No downloads are available.<br/>'
'Convert some files and refresh this page.</p>\n', 'UTF-8'))
self.wfile.write(bytes('</div>\n'
'</body>\n'
'</html>\n', 'UTF-8'))
'</body>\n'
'</html>\n', 'UTF-8'))
elif sendReply:
outputFile = GUI.completedWork[unquote(self.path[1:])]
fp = open(outputFile, 'rb')
@@ -195,7 +195,7 @@ class VersionThread(QtCore.QThread):
def run(self):
try:
XML = urlopen('http://kcc.vulturis.eu/Version.php')
XML = urlopen('http://kcc.iosphe.re/Version.php')
XML = parse(XML)
except Exception:
return
@@ -209,7 +209,7 @@ class VersionThread(QtCore.QThread):
'<br/>Current version: ' + latestVersion +
'<br/><br/>Would you like to start automatic update?', 'question')
else:
MW.addMessage.emit('<a href="http://kcc.vulturis.eu/">'
MW.addMessage.emit('<a href="http://kcc.iosphe.re/">'
'<b>New version is available!</b></a> '
'(<a href="https://github.com/ciromattia/kcc/releases/">'
'Changelog</a>)', 'warning', False)
@@ -219,7 +219,7 @@ class VersionThread(QtCore.QThread):
try:
MW.modeConvert.emit(-1)
MW.progressBarTick.emit('Downloading update')
path = urlretrieve('http://kcc.vulturis.eu/Windows/KindleComicConverter_win_'
path = urlretrieve('http://kcc.iosphe.re/Windows/KindleComicConverter_win_'
+ self.newVersion + '.exe', reporthook=self.getNewVersionTick)
if self.md5 != md5Checksum(path[0]):
raise Exception
@@ -263,90 +263,14 @@ class ProgressThread(QtCore.QThread):
self.running = False
class WorkerSignals(QtCore.QObject):
result = QtCore.pyqtSignal(list)
class KindleGenThread(QtCore.QRunnable):
def __init__(self, batch):
super(KindleGenThread, self).__init__()
self.signals = WorkerSignals()
self.work = batch
def run(self):
kindlegenErrorCode = 0
kindlegenError = ''
try:
if os.path.getsize(self.work) < 367001600:
output = Popen('kindlegen -locale en "' + self.work + '"', stdout=PIPE, stderr=STDOUT, shell=True)
for line in output.stdout:
line = line.decode('utf-8')
# ERROR: Generic error
if "Error(" in line:
kindlegenErrorCode = 1
kindlegenError = line
# ERROR: EPUB too big
if ":E23026:" in line:
kindlegenErrorCode = 23026
if kindlegenErrorCode > 0:
break
else:
# ERROR: EPUB too big
kindlegenErrorCode = 23026
self.signals.result.emit([kindlegenErrorCode, kindlegenError, self.work])
except Exception as err:
# ERROR: KCC unknown generic error
kindlegenErrorCode = 1
kindlegenError = format(err)
self.signals.result.emit([kindlegenErrorCode, kindlegenError, self.work])
class KindleUnpackThread(QtCore.QRunnable):
def __init__(self, batch):
super(KindleUnpackThread, self).__init__()
self.signals = WorkerSignals()
self.work = batch
def run(self):
item = self.work[0]
profile = self.work[1]
os.remove(item)
mobiPath = item.replace('.epub', '.mobi')
move(mobiPath, mobiPath + '_toclean')
try:
# MOBI file produced by KindleGen is hybrid. KF8 + M7 + Source header
# KindleSplit is removing redundant data as we need only KF8 part for new Kindle models
if profile in ['K345', 'KHD', 'KF', 'KFHD', 'KFHD8', 'KFHDX', 'KFHDX8', 'KFA']:
newKindle = True
else:
newKindle = False
mobisplit = kindlesplit.mobi_split(mobiPath + '_toclean', newKindle)
open(mobiPath, 'wb').write(mobisplit.getResult())
self.signals.result.emit([True])
except Exception as err:
self.signals.result.emit([False, format(err)])
class WorkerThread(QtCore.QThread):
#noinspection PyArgumentList
# noinspection PyArgumentList
def __init__(self):
QtCore.QThread.__init__(self)
self.pool = QtCore.QThreadPool()
self.conversionAlive = False
self.errors = False
self.kindlegenErrorCode = [0]
self.workerOutput = []
# Let's make sure that we don't fill the memory
availableMemory = TOTAL_PHYMEM/1000000000
if availableMemory <= 2:
self.threadNumber = 1
elif 2 < availableMemory <= 4:
self.threadNumber = 2
else:
self.threadNumber = 4
# Let's make sure that we don't use too many threads
if self.threadNumber > QtCore.QThread.idealThreadCount():
self.threadNumber = QtCore.QThread.idealThreadCount()
self.progressBarTick = MW.progressBarTick
self.addMessage = MW.addMessage
@@ -365,55 +289,65 @@ class WorkerThread(QtCore.QThread):
MW.addTrayMessage.emit('Conversion interrupted.', 'Critical')
MW.modeConvert.emit(1)
def addResult(self, output):
MW.progressBarTick.emit('tick')
self.workerOutput.append(output)
def sanitizeTrace(self, traceback):
return ''.join(format_tb(traceback))\
.replace('C:\\Users\\AcidWeb\\Documents\\Projekty\\KCC\\', '')\
.replace('C:\\Python34\\', '')\
.replace('C:\\Python34_64\\', '')
def run(self):
MW.modeConvert.emit(0)
parser = comic2ebook.makeParser()
options, _ = parser.parse_args()
profile = GUI.profiles[str(GUI.DeviceBox.currentText())]['Label']
argv = ["--profile=" + profile]
options.profile = profile
argv = ''
currentJobs = []
# Basic mode settings
if GUI.MangaBox.isChecked():
argv.append("--manga-style")
options.righttoleft = True
if GUI.RotateBox.isChecked():
argv.append("--rotate")
options.rotate = True
if GUI.QualityBox.checkState() == 1:
argv.append("--quality=1")
options.quality = 1
elif GUI.QualityBox.checkState() == 2:
argv.append("--quality=2")
if str(GUI.FormatBox.currentText()) == 'CBZ':
argv.append("--cbz-output")
options.quality = 2
options.format = str(GUI.FormatBox.currentText())
if GUI.currentMode == 1:
if profile in ['KFHD', 'KFHD8', 'KFHDX', 'KFHDX8']:
argv.append("--upscale")
if 'KFH' in profile:
options.upscale = True
# Advanced mode settings
if GUI.currentMode > 1:
if GUI.ProcessingBox.isChecked():
argv.append("--noprocessing")
options.imgproc = False
if GUI.NoRotateBox.isChecked():
argv.append("--nosplitrotate")
options.nosplitrotate = True
if GUI.UpscaleBox.checkState() == 1:
argv.append("--stretch")
options.stretch = True
elif GUI.UpscaleBox.checkState() == 2:
argv.append("--upscale")
options.upscale = True
if GUI.BorderBox.checkState() == 1:
argv.append("--whiteborders")
options.white_borders = True
elif GUI.BorderBox.checkState() == 2:
argv.append("--blackborders")
options.black_borders = True
if GUI.NoDitheringBox.isChecked():
argv.append("--forcepng")
options.forcepng = True
if GUI.WebtoonBox.isChecked():
argv.append("--webtoon")
options.webtoon = True
if float(GUI.GammaValue) > 0.09:
# noinspection PyTypeChecker
argv.append("--gamma=" + GUI.GammaValue)
if str(GUI.FormatBox.currentText()) == 'MOBI':
argv.append("--batchsplit")
options.gamma = float(GUI.GammaValue)
# Other/custom settings.
if GUI.currentMode > 2:
argv.append("--customwidth=" + str(GUI.customWidth.text()))
argv.append("--customheight=" + str(GUI.customHeight.text()))
options.customwidth = str(GUI.customWidth.text())
options.customheight = str(GUI.customHeight.text())
if GUI.ColorBox.isChecked():
argv.append("--forcecolor")
options.forcecolor = True
for i in range(GUI.JobList.count()):
# Make sure that we don't consider any system message as job to do
if GUI.JobList.item(i).icon().isNull():
@@ -435,7 +369,9 @@ class WorkerThread(QtCore.QThread):
jobargv = list(argv)
jobargv.append(job)
try:
outputPath = comic2ebook.main(jobargv, self)
comic2ebook.options = copy(options)
comic2ebook.checkOptions()
outputPath = comic2ebook.makeBook(job, self)
MW.hideProgressBar.emit()
except UserWarning as warn:
if not self.conversionAlive:
@@ -452,7 +388,7 @@ class WorkerThread(QtCore.QThread):
self.errors = True
_, _, traceback = sys.exc_info()
MW.showDialog.emit("Error during conversion %s:\n\n%s\n\nTraceback:\n%s"
% (jobargv[-1], str(err), "".join(format_tb(traceback))), 'error')
% (jobargv[-1], str(err), self.sanitizeTrace(traceback)), 'error')
MW.addMessage.emit('Failed to create EPUB!', 'error', False)
MW.addTrayMessage.emit('Failed to create EPUB!', 'Critical')
if not self.conversionAlive:
@@ -473,15 +409,10 @@ class WorkerThread(QtCore.QThread):
MW.progressBarTick.emit('tick')
MW.addMessage.emit('Creating MOBI files', 'info', False)
GUI.progress.content = 'Creating MOBI files'
self.workerOutput = []
# Number of KindleGen threads depends on the size of RAM
self.pool.setMaxThreadCount(self.threadNumber)
work = []
for item in outputPath:
worker = KindleGenThread(item)
worker.signals.result.connect(self.addResult)
self.pool.start(worker)
self.pool.waitForDone()
sleep(0.5)
work.append([item])
self.workerOutput = comic2ebook.makeMOBI(work, self)
self.kindlegenErrorCode = [0]
for errors in self.workerOutput:
if errors[0] != 0:
@@ -498,20 +429,14 @@ class WorkerThread(QtCore.QThread):
if self.kindlegenErrorCode[0] == 0:
GUI.progress.content = ''
MW.addMessage.emit('Creating MOBI files... <b>Done!</b>', 'info', True)
MW.addMessage.emit('Cleaning MOBI files', 'info', False)
GUI.progress.content = 'Cleaning MOBI files'
MW.addMessage.emit('Processing MOBI files', 'info', False)
GUI.progress.content = 'Processing MOBI files'
self.workerOutput = []
# Multithreading KindleUnpack in current form is a waste of resources.
# Unless we higly optimise KindleUnpack or drop 32bit support this will not change.
self.pool.setMaxThreadCount(1)
for item in outputPath:
worker = KindleUnpackThread([item, profile])
worker.signals.result.connect(self.addResult)
self.pool.start(worker)
self.pool.waitForDone()
sleep(0.5)
self.workerOutput.append(comic2ebook.makeMOBIFix(item))
MW.progressBarTick.emit('tick')
for success in self.workerOutput:
if not success:
if not success[0]:
self.errors = True
break
if not self.errors:
@@ -526,7 +451,7 @@ class WorkerThread(QtCore.QThread):
except Exception:
pass
GUI.completedWork[os.path.basename(mobiPath)] = mobiPath
MW.addMessage.emit('Cleaning MOBI files... <b>Done!</b>', 'info', True)
MW.addMessage.emit('Processing MOBI files... <b>Done!</b>', 'info', True)
else:
GUI.progress.content = ''
for item in outputPath:
@@ -535,11 +460,11 @@ class WorkerThread(QtCore.QThread):
os.remove(mobiPath)
if os.path.exists(mobiPath + '_toclean'):
os.remove(mobiPath + '_toclean')
MW.addMessage.emit('KindleUnpack failed to clean MOBI file!', 'error', False)
MW.addTrayMessage.emit('KindleUnpack failed to clean MOBI file!', 'Critical')
MW.addMessage.emit('Failed to process MOBI file!', 'error', False)
MW.addTrayMessage.emit('Failed to process MOBI file!', 'Critical')
else:
GUI.progress.content = ''
epubSize = (os.path.getsize(self.kindlegenErrorCode[2]))/1024/1024
epubSize = (os.path.getsize(self.kindlegenErrorCode[2]))//1024//1024
for item in outputPath:
if os.path.exists(item):
os.remove(item)
@@ -551,7 +476,7 @@ class WorkerThread(QtCore.QThread):
MW.showDialog.emit("KindleGen error:\n\n" + self.kindlegenErrorCode[1], 'error')
if self.kindlegenErrorCode[0] == 23026:
MW.addMessage.emit('Created EPUB file was too big.', 'error', False)
MW.addMessage.emit('EPUB file: ' + str(epubSize) + 'MB. Supported size: ~300MB.', 'error',
MW.addMessage.emit('EPUB file: ' + str(epubSize) + 'MB. Supported size: ~350MB.', 'error',
False)
else:
for item in outputPath:
@@ -575,6 +500,7 @@ class SystemTrayIcon(QtWidgets.QSystemTrayIcon):
def __init__(self):
if self.isSystemTrayAvailable():
QtWidgets.QSystemTrayIcon.__init__(self, GUI.icons.programIcon, MW)
# noinspection PyUnresolvedReferences
self.activated.connect(self.catchClicks)
def catchClicks(self):
@@ -820,6 +746,9 @@ class KCCGUI(KCC_ui.Ui_KCC):
if value == 2 and 'Kobo' in str(GUI.DeviceBox.currentText()):
self.addMessage('Kobo devices can\'t use ultra quality mode!', 'warning')
GUI.QualityBox.setCheckState(0)
elif value == 2 and 'CBZ' in str(GUI.FormatBox.currentText()):
self.addMessage('CBZ format don\'t support ultra quality mode!', 'warning')
GUI.QualityBox.setCheckState(0)
def changeGamma(self, value):
value = float(value)
@@ -847,7 +776,7 @@ class KCCGUI(KCC_ui.Ui_KCC):
GUI.AdvModeButton.setEnabled(True)
if self.currentMode == 3:
self.modeBasic()
self.changeFormat()
self.changeFormat(event=False)
GUI.GammaSlider.setValue(0)
self.changeGamma(0)
if profile['DefaultUpscale']:
@@ -856,19 +785,12 @@ class KCCGUI(KCC_ui.Ui_KCC):
self.addMessage('<a href="https://github.com/ciromattia/kcc/wiki/NonKindle-devices">'
'List of supported Non-Kindle devices.</a>', 'info')
def changeFormat(self, outputFormat=None):
def changeFormat(self, outputFormat=None, event=True):
profile = GUI.profiles[str(GUI.DeviceBox.currentText())]
if outputFormat is not None:
GUI.FormatBox.setCurrentIndex(outputFormat)
else:
if GUI.FormatBox.count() == 3:
GUI.FormatBox.setCurrentIndex(profile['DefaultFormat'])
else:
if profile['DefaultFormat'] != 0:
tmpFormat = profile['DefaultFormat'] - 1
else:
tmpFormat = 0
GUI.FormatBox.setCurrentIndex(tmpFormat)
GUI.FormatBox.setCurrentIndex(profile['DefaultFormat'])
if GUI.WebtoonBox.isChecked():
GUI.MangaBox.setEnabled(False)
GUI.QualityBox.setEnabled(False)
@@ -881,6 +803,10 @@ class KCCGUI(KCC_ui.Ui_KCC):
if GUI.ProcessingBox.isChecked():
GUI.QualityBox.setEnabled(False)
GUI.QualityBox.setChecked(False)
if event and GUI.QualityBox.isEnabled() and 'CBZ' in str(GUI.FormatBox.currentText()) and\
GUI.QualityBox.checkState() == 2:
self.addMessage('CBZ format don\'t support ultra quality mode!', 'warning')
GUI.QualityBox.setCheckState(0)
def stripTags(self, html):
s = HTMLStripper()
@@ -959,6 +885,15 @@ class KCCGUI(KCC_ui.Ui_KCC):
self.addMessage('Target resolution is not set!', 'error')
self.needClean = True
return
if str(GUI.FormatBox.currentText()) == 'MOBI' and not GUI.KindleGen:
self.addMessage('Cannot find <a href="http://www.amazon.com/gp/feature.html?ie=UTF8&docId=1000765211">'
'<b>KindleGen</b></a>! MOBI conversion is not possible!', 'error')
if sys.platform.startswith('win'):
self.addMessage('Download it and place EXE in KCC directory.', 'error')
else:
self.addMessage('Download it, and place executable in /usr/local/bin directory.', 'error')
self.needClean = True
return
self.worker.start()
def hideProgressBar(self):
@@ -996,8 +931,7 @@ class KCCGUI(KCC_ui.Ui_KCC):
'customHeight': GUI.customHeight.text(),
'GammaSlider': float(self.GammaValue)*100})
self.settings.sync()
if not sys.platform.startswith('linux'):
self.tray.hide()
self.tray.hide()
def handleMessage(self, message):
MW.raise_()
@@ -1072,6 +1006,7 @@ class KCCGUI(KCC_ui.Ui_KCC):
self.versionCheck = VersionThread()
self.contentServer = WebServerThread()
self.progress = ProgressThread()
self.tray = SystemTrayIcon()
self.conversionAlive = False
self.needClean = True
self.GammaValue = 1.0
@@ -1081,10 +1016,9 @@ class KCCGUI(KCC_ui.Ui_KCC):
self.listFontSize = 11
self.statusBarFontSize = 10
self.statusBarStyle = 'QLabel{padding-top:2px;padding-bottom:3px;}'
self.ProgressBar.setStyleSheet('QProgressBar{padding-top:5px;text-align:center;}')
self.tray = SystemTrayIcon()
self.tray.show()
MW.addTrayMessage.connect(self.tray.addTrayMessage)
self.ProgressBar.setStyleSheet('QProgressBar{font-size:13px;text-align:center;'
'border:2px solid grey;border-radius:5px;}'
'QProgressBar::chunk{background-color:steelblue;width:20px;}')
elif sys.platform.startswith('linux'):
self.listFontSize = 8
self.statusBarFontSize = 8
@@ -1095,27 +1029,27 @@ class KCCGUI(KCC_ui.Ui_KCC):
self.statusBarFontSize = 8
self.statusBarStyle = 'QLabel{padding-top:3px;padding-bottom:3px}'
self.statusBar.setStyleSheet('QStatusBar::item{border:0px;border-top:2px solid #C2C7CB;}')
self.tray = SystemTrayIcon()
self.tray.show()
MW.addTrayMessage.connect(self.tray.addTrayMessage)
# Decrease priority to increase system responsiveness during conversion
from psutil import BELOW_NORMAL_PRIORITY_CLASS
self.p = Process(os.getpid())
self.p.nice(BELOW_NORMAL_PRIORITY_CLASS)
self.p.ionice(1)
self.profiles = {
"Kindle Voyage": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': False, 'Label': 'KV'},
"Kindle Paperwhite": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': False, 'Label': 'KHD'},
'DefaultUpscale': False, 'Label': 'KPW'},
"Kindle": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': False, 'Label': 'K345'},
"Kindle DX/DXG": {'Quality': False, 'ForceExpert': False, 'DefaultFormat': 0,
"Kindle DX/DXG": {'Quality': False, 'ForceExpert': False, 'DefaultFormat': 2,
'DefaultUpscale': False, 'Label': 'KDX'},
"Kindle Fire": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': False, 'Label': 'KF'},
"K. Fire HD 7\"": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': True, 'Label': 'KFHD'},
"K. Fire HD 8.9\"": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': True, 'Label': 'KFHD8'},
"K. Fire HDX 7\"": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': True, 'Label': 'KFHDX'},
"K. Fire HDX 8.9\"": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': True, 'Label': 'KFHDX8'},
"K. Fire HD": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': True, 'Label': 'KFHD'},
"K. Fire HDX": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': True, 'Label': 'KFHDX'},
"K. Fire HDX 8.9": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': True, 'Label': 'KFHDX8'},
"Kobo Mini/Touch": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 2,
'DefaultUpscale': False, 'Label': 'KoMT'},
"Kobo Glow": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 2,
@@ -1124,6 +1058,8 @@ class KCCGUI(KCC_ui.Ui_KCC):
'DefaultUpscale': False, 'Label': 'KoA'},
"Kobo Aura HD": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 2,
'DefaultUpscale': False, 'Label': 'KoAHD'},
"Kobo Aura H2O": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 2,
'DefaultUpscale': False, 'Label': 'KoAH2O'},
"Other": {'Quality': False, 'ForceExpert': True, 'DefaultFormat': 1,
'DefaultUpscale': False, 'Label': 'OTHER'},
"Kindle for Android": {'Quality': False, 'ForceExpert': True, 'DefaultFormat': 0,
@@ -1134,29 +1070,29 @@ class KCCGUI(KCC_ui.Ui_KCC):
'DefaultUpscale': False, 'Label': 'K2'}
}
profilesGUI = [
"Kindle Voyage",
"Kindle Paperwhite",
"Kindle",
"Kindle DX/DXG",
"Separator",
"Kindle Fire",
"K. Fire HD 7\"",
"K. Fire HD 8.9\"",
"K. Fire HDX 7\"",
"K. Fire HDX 8.9\"",
"K. Fire HD",
"K. Fire HDX",
"K. Fire HDX 8.9",
"Separator",
"Kobo Mini/Touch",
"Kobo Glow",
"Kobo Aura",
"Kobo Aura HD",
"Kobo Aura H2O",
"Separator",
"Other",
"Separator",
"Kindle for Android",
"Kindle 1",
"Kindle 2",
"Kindle DX/DXG",
]
statusBarLabel = QtWidgets.QLabel('<b><a href="http://kcc.vulturis.eu/">HOMEPAGE</a> - <a href="https://github.'
statusBarLabel = QtWidgets.QLabel('<b><a href="http://kcc.iosphe.re/">HOMEPAGE</a> - <a href="https://github.'
'com/ciromattia/kcc/blob/master/README.md#issues--new-features--donations">DO'
'NATE</a> - <a href="https://github.com/ciromattia/kcc/wiki">WIKI</a> - <a hr'
'ef="http://www.mobileread.com/forums/showthread.php?t=207461">FORUM</a></b>')
@@ -1174,10 +1110,14 @@ class KCCGUI(KCC_ui.Ui_KCC):
self.addMessage('Since you are new user of <b>KCC</b> please see few '
'<a href="https://github.com/ciromattia/kcc/wiki/Important-tips">important tips</a>.',
'info')
if not sys.platform.startswith('win'):
try:
os.chmod('/usr/local/bin/kindlegen', 0o755)
except Exception:
pass
kindleGenExitCode = Popen('kindlegen -locale en', stdout=PIPE, stderr=STDOUT, shell=True)
if kindleGenExitCode.wait() == 0:
self.KindleGen = True
formats = ['MOBI', 'EPUB', 'CBZ']
versionCheck = Popen('kindlegen -locale en', stdout=PIPE, stderr=STDOUT, shell=True)
for line in versionCheck.stdout:
line = line.decode("utf-8")
@@ -1191,13 +1131,6 @@ class KCCGUI(KCC_ui.Ui_KCC):
break
else:
self.KindleGen = False
formats = ['EPUB', 'CBZ']
if sys.platform.startswith('win'):
self.addMessage('Cannot find <a href="http://www.amazon.com/gp/feature.html?ie=UTF8&docId=1000765211">'
'kindlegen</a> in KCC directory! MOBI creation will be disabled.', 'warning')
else:
self.addMessage('Cannot find <a href="http://www.amazon.com/gp/feature.html?ie=UTF8&docId=1000765211">'
'kindlegen</a> in PATH! MOBI creation will be disabled.', 'warning')
rarExitCode = Popen('unrar', stdout=PIPE, stderr=STDOUT, shell=True)
rarExitCode = rarExitCode.wait()
if rarExitCode == 0 or rarExitCode == 7:
@@ -1237,6 +1170,7 @@ class KCCGUI(KCC_ui.Ui_KCC):
MW.forceShutdown.connect(self.forceShutdown)
MW.dialogAnswer.connect(self.versionCheck.getNewVersion)
MW.closeEvent = self.saveSettings
MW.addTrayMessage.connect(self.tray.addTrayMessage)
GUI.Form.setAcceptDrops(True)
GUI.Form.dragEnterEvent = self.dragAndDrop
@@ -1251,7 +1185,7 @@ class KCCGUI(KCC_ui.Ui_KCC):
GUI.DeviceBox.addItem(self.icons.deviceKobo, profile)
else:
GUI.DeviceBox.addItem(self.icons.deviceKindle, profile)
for f in formats:
for f in ['MOBI', 'EPUB', 'CBZ']:
GUI.FormatBox.addItem(eval('self.icons.' + f + 'Format'), f)
if self.lastDevice > GUI.DeviceBox.count():
self.lastDevice = 0
@@ -1262,7 +1196,7 @@ class KCCGUI(KCC_ui.Ui_KCC):
GUI.DeviceBox.setCurrentIndex(self.lastDevice)
self.changeDevice()
if self.currentFormat != self.profiles[str(GUI.DeviceBox.currentText())]['DefaultFormat']:
self.changeFormat(self.currentFormat)
self.changeFormat(self.currentFormat, False)
for option in self.options:
if str(option) == "customWidth":
GUI.customWidth.setText(str(self.options[option]))
@@ -1279,6 +1213,7 @@ class KCCGUI(KCC_ui.Ui_KCC):
self.worker.sync()
self.versionCheck.start()
self.contentServer.start()
self.tray.show()
MW.setWindowTitle("Kindle Comic Converter " + __version__)
MW.show()
MW.raise_()

View File

@@ -2,8 +2,8 @@
# Form implementation generated from reading ui file 'KCC.ui'
#
# Created: Sat Jan 25 17:36:53 2014
# by: PyQt5 UI code generator 5.2
# Created: Sun May 18 09:08:27 2014
# by: PyQt5 UI code generator 5.2.1
#
# WARNING! All changes made in this file will be lost!
@@ -264,7 +264,7 @@ class Ui_KCC(object):
self.ProcessingBox.setText(_translate("KCC", "No optimisation"))
self.UpscaleBox.setToolTip(_translate("KCC", "<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Nothing<br/></span>Images smaller than device resolution will not be resized.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - Stretching<br/></span>Images smaller than device resolution will be resized. Aspect ratio will be not preserved.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Upscaling<br/></span>Images smaller than device resolution will be resized. Aspect ratio will be preserved.</p></body></html>"))
self.UpscaleBox.setText(_translate("KCC", "Stretch/Upscale"))
self.WebtoonBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Enable auto-splitting of webtoons like <span style=\" font-style:italic;\">Tower of God</span> or <span style=\" font-style:italic;\">Noblesse</span>.<br/>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 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.setText(_translate("KCC", "Webtoon mode"))
self.NoDitheringBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Create PNG files instead JPEG.<br/>Quality increase is not noticeable on most of devices.<br/>Output files <span style=\" font-weight:600;\">might</span> be smaller.<br/><span style=\" font-weight:600;\">MOBI conversion will be much slower.</span></p></body></html>"))
self.NoDitheringBox.setText(_translate("KCC", "PNG output"))
@@ -281,7 +281,7 @@ class Ui_KCC(object):
self.ClearButton.setText(_translate("KCC", "Clear list"))
self.MangaBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Enable right-to-left reading.</p></body></html>"))
self.MangaBox.setText(_translate("KCC", "Manga mode"))
self.QualityBox.setToolTip(_translate("KCC", "<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Normal quality mode<br/></span><span style=\" font-style:italic;\">Use it when Panel View support is not needed.</span><span style=\" font-weight:600; text-decoration: underline;\"><br/></span>- Maximum quality when zoom is not enabled.<br/>- Poor quality when zoom is enabled.<br/>- Lowest file size.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - High quality mode<br/></span><span style=\" font-style:italic;\">Not zoomed images </span><span style=\" font-weight:600; font-style:italic;\">might </span><span style=\" font-style:italic;\">be blurry.</span><span style=\" font-weight:600; text-decoration: underline;\"><br/></span>- High quality when zoom is not enabled.<br/>- Maximum quality when zoom is enabled.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Ultra quality mode<br/></span><span style=\" font-style:italic;\">Maximum possible quality.</span><span style=\" font-weight:600; text-decoration: underline;\"><br/></span>- Maximum quality when zoom is not enabled.<br/>- Maximum quality when zoom is enabled.<br/>- Very high file size.</p></body></html>"))
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.setText(_translate("KCC", "High/Ultra quality"))
self.RotateBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Disable splitting of two-page spreads.<br/>They will be rotated instead.</p></body></html>"))
self.RotateBox.setText(_translate("KCC", "Horizontal mode"))

View File

@@ -2,8 +2,8 @@
# Form implementation generated from reading ui file 'KCC-Linux.ui'
#
# Created: Sat Jan 25 17:37:02 2014
# by: PyQt5 UI code generator 5.2
# Created: Sun May 18 09:08:37 2014
# by: PyQt5 UI code generator 5.2.1
#
# WARNING! All changes made in this file will be lost!
@@ -333,7 +333,7 @@ class Ui_KCC(object):
self.ProcessingBox.setText(_translate("KCC", "No optimisation"))
self.UpscaleBox.setToolTip(_translate("KCC", "<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Nothing<br/></span>Images smaller than device resolution will not be resized.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - Stretching<br/></span>Images smaller than device resolution will be resized. Aspect ratio will be not preserved.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Upscaling<br/></span>Images smaller than device resolution will be resized. Aspect ratio will be preserved.</p></body></html>"))
self.UpscaleBox.setText(_translate("KCC", "Stretch/Upscale"))
self.WebtoonBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Enable auto-splitting of webtoons like <span style=\" font-style:italic;\">Tower of God</span> or <span style=\" font-style:italic;\">Noblesse</span>.<br/>Pages with a low width, high height and vertical panel flow.</p></body></html>"))
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.setText(_translate("KCC", "Webtoon mode"))
self.NoDitheringBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Create PNG files instead JPEG.<br/>Quality increase is not noticeable on most of devices.<br/>Output files <span style=\" font-weight:600;\">might</span> be smaller.<br/><span style=\" font-weight:600;\">MOBI conversion will be much slower.</span></p></body></html>"))
self.NoDitheringBox.setText(_translate("KCC", "PNG output"))
@@ -350,7 +350,7 @@ class Ui_KCC(object):
self.ClearButton.setText(_translate("KCC", "Clear list"))
self.MangaBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Enable right-to-left reading.</p></body></html>"))
self.MangaBox.setText(_translate("KCC", "Manga mode"))
self.QualityBox.setToolTip(_translate("KCC", "<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Normal quality mode<br/></span><span style=\" font-style:italic;\">Use it when Panel View support is not needed.<br/></span>- Maximum quality when zoom is not enabled.<br/>- Poor quality when zoom is enabled.<br/>- Lowest file size.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - High quality mode<br/></span><span style=\" font-style:italic;\">Not zoomed images </span><span style=\" font-weight:600; font-style:italic;\">might</span><span style=\" font-style:italic;\"> be blurry.<br/></span>- High quality when zoom is not enabled.<br/>- Maximum quality when zoom is enabled.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Ultra quality mode<br/></span><span style=\" font-style:italic;\">Maximum possible quality.<br/></span>- Maximum quality when zoom is not enabled.<br/>- Maximum quality when zoom is enabled.<br/>- Very high file size.</p></body></html>"))
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.setText(_translate("KCC", "High/Ultra quality"))
self.RotateBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Disable splitting of two-page spreads.<br/>They will be rotated instead.</p></body></html>"))
self.RotateBox.setText(_translate("KCC", "Horizontal mode"))

View File

@@ -2,8 +2,8 @@
# Form implementation generated from reading ui file 'KCC-OSX.ui'
#
# Created: Sat Jan 25 17:37:10 2014
# by: PyQt5 UI code generator 5.2
# Created: Sun May 18 09:08:44 2014
# by: PyQt5 UI code generator 5.2.1
#
# WARNING! All changes made in this file will be lost!
@@ -356,7 +356,7 @@ class Ui_KCC(object):
self.ProcessingBox.setText(_translate("KCC", "No optimisation"))
self.UpscaleBox.setToolTip(_translate("KCC", "<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Nothing<br/></span>Images smaller than device resolution will not be resized.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - Stretching<br/></span>Images smaller than device resolution will be resized. Aspect ratio will be not preserved.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Upscaling<br/></span>Images smaller than device resolution will be resized. Aspect ratio will be preserved.</p></body></html>"))
self.UpscaleBox.setText(_translate("KCC", "Stretch/Upscale"))
self.WebtoonBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Enable auto-splitting of webtoons like <span style=\" font-style:italic;\">Tower of God</span> or <span style=\" font-style:italic;\">Noblesse</span>.<br/>Pages with a low width, high height and vertical panel flow.</p></body></html>"))
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.setText(_translate("KCC", "Webtoon mode"))
self.NoDitheringBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Create PNG files instead JPEG.<br/>Quality increase is not noticeable on most of devices.<br/>Output files <span style=\" font-weight:600;\">might</span> be smaller.<br/><span style=\" font-weight:600;\">MOBI conversion will be much slower.</span></p></body></html>"))
self.NoDitheringBox.setText(_translate("KCC", "PNG output"))
@@ -373,7 +373,7 @@ class Ui_KCC(object):
self.ClearButton.setText(_translate("KCC", "Clear list"))
self.MangaBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Enable right-to-left reading.</p></body></html>"))
self.MangaBox.setText(_translate("KCC", "Manga mode"))
self.QualityBox.setToolTip(_translate("KCC", "<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Normal quality mode<br/></span><span style=\" font-style:italic;\">Use it when Panel View support is not needed.<br/></span>- Maximum quality when zoom is not enabled.<br/>- Poor quality when zoom is enabled.<br/>- Lowest file size.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - High quality mode<br/></span><span style=\" font-style:italic;\">Not zoomed image </span><span style=\" font-weight:600; font-style:italic;\">might</span><span style=\" font-style:italic;\"> be a little blurry.<br/></span>- High quality when zoom is not enabled.<br/>- Maximum quality when zoom is enabled.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Ultra quality mode<br/></span><span style=\" font-style:italic;\">Maximum possible quality.<br/></span>- Maximum quality when zoom is not enabled.<br/>- Maximum quality when zoom is enabled.<br/>- Very high file size.</p></body></html>"))
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.setText(_translate("KCC", "High/Ultra quality"))
self.RotateBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Disable splitting of two-page spreads.<br/>They will be rotated instead.</p></body></html>"))
self.RotateBox.setText(_translate("KCC", "Horizontal mode"))

View File

@@ -1,4 +1,4 @@
__version__ = '4.0.1'
__version__ = '4.3.1'
__license__ = 'ISC'
__copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@vulturis.eu>'
__copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>'
__docformat__ = 'restructuredtext en'

View File

@@ -1,5 +1,5 @@
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
# Copyright (c) 2013-2014 Pawel Jastrzebski <pawelj@vulturis.eu>
# Copyright (c) 2013-2014 Pawel Jastrzebski <pawelj@iosphe.re>
#
# Permission to use, copy, modify, and/or distribute this software for
# any purpose with or without fee is hereby granted, provided that the
@@ -17,7 +17,7 @@
#
__license__ = 'ISC'
__copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@vulturis.eu>'
__copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>'
__docformat__ = 'restructuredtext en'
import sys
@@ -104,7 +104,7 @@ class CBxArchive:
for f in os.listdir(os.path.join(targetdir, adir[0])):
# If directory names contain UTF-8 chars shutil.move can't clean up the mess alone
if os.path.isdir(os.path.join(targetdir, f)):
os.rename(os.path.join(targetdir, adir[0], f), os.path.join(targetdir, adir[0], f + '-A'))
os.replace(os.path.join(targetdir, adir[0], f), os.path.join(targetdir, adir[0], f + '-A'))
f += '-A'
move(os.path.join(targetdir, adir[0], f), targetdir)
os.rmdir(os.path.join(targetdir, adir[0]))

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
# Copyright (c) 2013-2014 Pawel Jastrzebski <pawelj@vulturis.eu>
# Copyright (c) 2013-2014 Pawel Jastrzebski <pawelj@iosphe.re>
#
# Permission to use, copy, modify, and/or distribute this software for
# any purpose with or without fee is hereby granted, provided that the
@@ -18,9 +18,9 @@
# PERFORMANCE OF THIS SOFTWARE.
#
__version__ = '4.0.1'
__version__ = '4.3.1'
__license__ = 'ISC'
__copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@vulturis.eu>'
__copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>'
__docformat__ = 'restructuredtext en'
import os
@@ -36,7 +36,7 @@ except ImportError:
QtCore = None
def mergeDirectory_tick(output):
def mergeDirectoryTick(output):
if output:
mergeWorkerOutput.append(output)
mergeWorkerPool.terminate()
@@ -108,7 +108,7 @@ def sanitizePanelSize(panel, opt):
return newPanels
def splitImage_tick(output):
def splitImageTick(output):
if output:
splitWorkerOutput.append(output)
splitWorkerPool.terminate()
@@ -118,13 +118,12 @@ def splitImage_tick(output):
splitWorkerPool.terminate()
#noinspection PyUnboundLocalVariable
def splitImage(work):
try:
path = work[0]
name = work[1]
opt = work[2]
# Harcoded opttions
# Hardcoded options
threshold = 1.0
delta = 15
fileExpanded = os.path.splitext(name)
@@ -165,6 +164,7 @@ def splitImage(work):
for panel in panelsCleaned:
panels.append(panel)
if opt.debug:
# noinspection PyUnboundLocalVariable
debugImage.save(os.path.join(path, fileExpanded[0] + '-debug.png'), 'PNG')
# Create virtual pages
@@ -207,13 +207,9 @@ def splitImage(work):
return str(sys.exc_info()[1])
def Copyright():
print(('comic2panel v%(__version__)s. Written by Ciro Mattia Gonano and Pawel Jastrzebski.' % globals()))
def main(argv=None, qtGUI=None):
global options, GUI, splitWorkerPool, splitWorkerOutput, mergeWorkerPool, mergeWorkerOutput
parser = OptionParser(usage="Usage: %prog [options] comic_folder", add_help_option=False)
parser = OptionParser(usage="Usage: kcc-c2p [options] comic_folder", add_help_option=False)
mainOptions = OptionGroup(parser, "MANDATORY")
otherOptions = OptionGroup(parser, "OTHER")
mainOptions.add_option("-y", "--height", type="int", dest="height", default=0,
@@ -261,7 +257,7 @@ def main(argv=None, qtGUI=None):
GUI.progressBarTick.emit('Combining images')
GUI.progressBarTick.emit(str(directoryNumer))
for i in mergeWork:
mergeWorkerPool.apply_async(func=mergeDirectory, args=(i, ), callback=mergeDirectory_tick)
mergeWorkerPool.apply_async(func=mergeDirectory, args=(i, ), callback=mergeDirectoryTick)
mergeWorkerPool.close()
mergeWorkerPool.join()
if GUI and not GUI.conversionAlive:
@@ -284,7 +280,7 @@ def main(argv=None, qtGUI=None):
GUI.progressBarTick.emit('tick')
if len(work) > 0:
for i in work:
splitWorkerPool.apply_async(func=splitImage, args=(i, ), callback=splitImage_tick)
splitWorkerPool.apply_async(func=splitImage, args=(i, ), callback=splitImageTick)
splitWorkerPool.close()
splitWorkerPool.join()
if GUI and not GUI.conversionAlive:

184
kcc/dualmetafix.py Normal file
View File

@@ -0,0 +1,184 @@
# -*- coding: utf-8 -*-
#
# Based on initial version of DualMetaFix. Copyright (C) 2013 Kevin Hendricks
# Changes for KCC Copyright (C) 2014 Pawel Jastrzebski <pawelj@iosphe.re>
#
# 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
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import struct
import mmap
import shutil
class DualMetaFixException(Exception):
pass
# palm database offset constants
number_of_pdb_records = 76
first_pdb_record = 78
# important rec0 offsets
mobi_header_base = 16
mobi_header_length = 20
mobi_version = 36
title_offset = 84
def getint(data, ofs, sz='L'):
i, = struct.unpack_from('>'+sz, data, ofs)
return i
def writeint(data, ofs, n, slen='L'):
if slen == 'L':
return data[:ofs]+struct.pack('>L', n)+data[ofs+4:]
else:
return data[:ofs]+struct.pack('>H', n)+data[ofs+2:]
def getsecaddr(datain, secno):
nsec = getint(datain, number_of_pdb_records, 'H')
if (secno < 0) | (secno >= nsec):
emsg = 'requested section number %d out of range (nsec=%d)' % (secno, nsec)
raise DualMetaFixException(emsg)
secstart = getint(datain, first_pdb_record+secno*8)
if secno == nsec-1:
secend = len(datain)
else:
secend = getint(datain, first_pdb_record+(secno+1)*8)
return secstart, secend
def readsection(datain, secno):
secstart, secend = getsecaddr(datain, secno)
return datain[secstart:secend]
# overwrite section - must be exact same length as original
def replacesection(datain, secno, secdata):
secstart, secend = getsecaddr(datain, secno)
seclen = secend - secstart
if len(secdata) != seclen:
raise DualMetaFixException('section length change in replacesection')
datain[secstart:secstart+seclen] = secdata
def get_exth_params(rec0):
ebase = mobi_header_base + getint(rec0, mobi_header_length)
if rec0[ebase:ebase+4] != b'EXTH':
raise DualMetaFixException('EXTH tag not found where expected')
elen = getint(rec0, ebase+4)
enum = getint(rec0, ebase+8)
rlen = len(rec0)
return ebase, elen, enum, rlen
def add_exth(rec0, exth_num, exth_bytes):
ebase, elen, enum, rlen = get_exth_params(rec0)
newrecsize = 8+len(exth_bytes)
newrec0 = rec0[0:ebase+4]+struct.pack('>L', elen+newrecsize)+struct.pack('>L', enum+1)+struct.pack('>L', exth_num)\
+ struct.pack('>L', newrecsize)+exth_bytes+rec0[ebase+12:]
newrec0 = writeint(newrec0, title_offset, getint(newrec0, title_offset)+newrecsize)
# keep constant record length by removing newrecsize null bytes from end
sectail = newrec0[-newrecsize:]
if sectail != b'\0'*newrecsize:
raise DualMetaFixException('add_exth: trimmed non-null bytes at end of section')
newrec0 = newrec0[0:rlen]
return newrec0
def read_exth(rec0, exth_num):
exth_values = []
ebase, elen, enum, rlen = get_exth_params(rec0)
ebase += 12
while enum > 0:
exth_id = getint(rec0, ebase)
if exth_id == exth_num:
# We might have multiple exths, so build a list.
exth_values.append(rec0[ebase+8:ebase+getint(rec0, ebase+4)])
enum -= 1
ebase = ebase+getint(rec0, ebase+4)
return exth_values
def del_exth(rec0, exth_num):
ebase, elen, enum, rlen = get_exth_params(rec0)
ebase_idx = ebase+12
enum_idx = 0
while enum_idx < enum:
exth_id = getint(rec0, ebase_idx)
exth_size = getint(rec0, ebase_idx+4)
if exth_id == exth_num:
newrec0 = rec0
newrec0 = writeint(newrec0, title_offset, getint(newrec0, title_offset)-exth_size)
newrec0 = newrec0[:ebase_idx]+newrec0[ebase_idx+exth_size:]
newrec0 = newrec0[0:ebase+4]+struct.pack('>L', elen-exth_size)+struct.pack('>L', enum-1)+newrec0[ebase+12:]
newrec0 += b'\0'*exth_size
if rlen != len(newrec0):
raise DualMetaFixException('del_exth: incorrect section size change')
return newrec0
enum_idx += 1
ebase_idx = ebase_idx+exth_size
return rec0
class DualMobiMetaFix:
def __init__(self, infile, outfile, asin):
shutil.copyfile(infile, outfile)
f = open(outfile, "r+b")
self.datain = mmap.mmap(f.fileno(), 0)
self.datain_rec0 = readsection(self.datain, 0)
# in the first mobi header
# add 501 to "EBOK", add 113 as asin, add 504 as asin
rec0 = self.datain_rec0
rec0 = del_exth(rec0, 501)
rec0 = del_exth(rec0, 113)
rec0 = del_exth(rec0, 504)
rec0 = add_exth(rec0, 501, b'EBOK')
rec0 = add_exth(rec0, 113, asin)
rec0 = add_exth(rec0, 504, asin)
replacesection(self.datain, 0, rec0)
ver = getint(self.datain_rec0, mobi_version)
self.combo = (ver != 8)
if not self.combo:
return
exth121 = read_exth(self.datain_rec0, 121)
if len(exth121) == 0:
self.combo = False
return
else:
# only pay attention to first exth121
# (there should only be one)
datain_kf8, = struct.unpack_from('>L', exth121[0], 0)
if datain_kf8 == 0xffffffff:
self.combo = False
return
self.datain_kfrec0 = readsection(self.datain, datain_kf8)
# in the second header
# add 501 to "EBOK", add 113 as asin, add 504 as asin
rec0 = self.datain_kfrec0
rec0 = del_exth(rec0, 501)
rec0 = del_exth(rec0, 113)
rec0 = del_exth(rec0, 504)
rec0 = add_exth(rec0, 501, b'EBOK')
rec0 = add_exth(rec0, 113, asin)
rec0 = add_exth(rec0, 504, asin)
replacesection(self.datain, datain_kf8, rec0)
self.datain.flush()
self.datain.close()

View File

@@ -1,7 +1,7 @@
# Copyright (C) 2010 Alex Yatskov
# Copyright (C) 2011 Stanislav (proDOOMman) Kosolapov <prodoomman@gmail.com>
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
# Copyright (c) 2013-2014 Pawel Jastrzebski <pawelj@vulturis.eu>
# Copyright (c) 2013-2014 Pawel Jastrzebski <pawelj@iosphe.re>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -16,11 +16,15 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
__version__ = '4.3.1'
__license__ = 'ISC'
__copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@vulturis.eu>'
__copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>'
__docformat__ = 'restructuredtext en'
import os
from io import BytesIO
from urllib.request import Request, urlopen
from urllib.parse import quote
from functools import reduce
from PIL import Image, ImageOps, ImageStat, ImageChops
from .shared import md5Checksum
@@ -81,18 +85,18 @@ class ProfileData:
'K1': ("Kindle 1", (600, 670), Palette4, 1.8, (900, 1005)),
'K2': ("Kindle 2", (600, 670), Palette15, 1.8, (900, 1005)),
'K345': ("Kindle", (600, 800), Palette16, 1.8, (900, 1200)),
'KHD': ("Kindle Paperwhite", (758, 1024), Palette16, 1.8, (1137, 1536)),
'KDX': ("Kindle DX/DXG", (824, 1000), Palette16, 1.8, (1236, 1500)),
'KF': ("Kindle Fire", (600, 1024), PalleteNull, 1.0, (900, 1536)),
'KFHD': ("K. Fire HD 7\"", (800, 1280), PalleteNull, 1.0, (1200, 1920)),
'KFHD8': ("K. Fire HD 8.9\"", (1200, 1920), PalleteNull, 1.0, (1800, 2880)),
'KFHDX': ("K. Fire HDX 7\"", (1200, 1920), PalleteNull, 1.0, (1800, 2880)),
'KFHDX8': ("K. Fire HDX 8.9\"", (1600, 2560), PalleteNull, 1.0, (2400, 3840)),
'KPW': ("Kindle Paperwhite", (758, 1024), Palette16, 1.8, (1137, 1536)),
'KV': ("Kindle Voyage", (1072, 1448), Palette16, 1.8, (1608, 2172)),
'KFHD': ("K. Fire HD", (800, 1280), PalleteNull, 1.0, (1200, 1920)),
'KFHDX': ("K. Fire HDX", (1200, 1920), PalleteNull, 1.0, (1800, 2880)),
'KFHDX8': ("K. Fire HDX 8.9", (1600, 2560), PalleteNull, 1.0, (2400, 3840)),
'KFA': ("Kindle for Android", (0, 0), PalleteNull, 1.0, (0, 0)),
'KoMT': ("Kobo Mini/Touch", (600, 800), Palette16, 1.8, (900, 1200)),
'KoG': ("Kobo Glow", (768, 1024), Palette16, 1.8, (1152, 1536)),
'KoA': ("Kobo Aura", (758, 1024), Palette16, 1.8, (1137, 1536)),
'KoAHD': ("Kobo Aura HD", (1080, 1440), Palette16, 1.8, (1620, 2160)),
'KFA': ("Kindle for Android", (0, 0), PalleteNull, 1.0, (0, 0)),
'KoAH2O': ("Kobo Aura H2O", (1080, 1430), Palette16, 1.8, (1620, 2145)),
'OTHER': ("Other", (0, 0), Palette16, 1.8, (0, 0)),
}
@@ -144,7 +148,7 @@ class ComicPage:
+ str(self.border[2]) + "-" + str(self.border[3]))
if forcepng:
filename += ".png"
self.image.save(filename, "PNG", optimize=1)
self.image.save(filename, "PNG", optimize=1)
else:
filename += ".jpg"
self.image.save(filename, "JPEG", optimize=1)
@@ -223,6 +227,9 @@ class ComicPage:
self.size[0] and self.image.size[1] <= self.size[1]:
size = (self.size[0], self.size[1])
elif qualityMode == 1:
# Forcing upscale to make sure that margins will be not too big
if not stretch:
upscale = True
size = (self.panelviewsize[0], self.panelviewsize[1])
elif qualityMode == 2 and not stretch and not upscale and self.image.size[0] <=\
self.size[0] and self.image.size[1] <= self.size[1]:
@@ -359,27 +366,28 @@ class ComicPage:
self.image = self.image.crop((0, 0, widthImg, heightImg - diff))
return self.image
def cropWhiteSpace(self, threshold):
def cropWhiteSpace(self):
if ImageChops.invert(self.image).getbbox() is not None:
widthImg, heightImg = self.image.size
delta = 10
diff = delta
fixedThreshold = 0.1
# top
while ImageStat.Stat(self.image.crop((0, 0, widthImg, diff))).var[0] < threshold and diff < heightImg:
while ImageStat.Stat(self.image.crop((0, 0, widthImg, diff))).var[0] < fixedThreshold and diff < heightImg:
diff += delta
diff -= delta
self.image = self.image.crop((0, diff, widthImg, heightImg))
widthImg, heightImg = self.image.size
diff = delta
# left
while ImageStat.Stat(self.image.crop((0, 0, diff, heightImg))).var[0] < threshold and diff < widthImg:
while ImageStat.Stat(self.image.crop((0, 0, diff, heightImg))).var[0] < fixedThreshold and diff < widthImg:
diff += delta
diff -= delta
self.image = self.image.crop((diff, 0, widthImg, heightImg))
widthImg, heightImg = self.image.size
diff = delta
# down
while ImageStat.Stat(self.image.crop((0, heightImg - diff, widthImg, heightImg))).var[0] < threshold\
while ImageStat.Stat(self.image.crop((0, heightImg - diff, widthImg, heightImg))).var[0] < fixedThreshold\
and diff < heightImg:
diff += delta
diff -= delta
@@ -387,7 +395,7 @@ class ComicPage:
widthImg, heightImg = self.image.size
diff = delta
# right
while ImageStat.Stat(self.image.crop((widthImg - diff, 0, widthImg, heightImg))).var[0] < threshold\
while ImageStat.Stat(self.image.crop((widthImg - diff, 0, widthImg, heightImg))).var[0] < fixedThreshold\
and diff < widthImg:
diff += delta
diff -= delta
@@ -396,50 +404,44 @@ class ComicPage:
def getImageHistogram(self, image):
histogram = image.histogram()
RBGW = []
pixelCount = 0
for i in range(256):
pixelCount += histogram[i] + histogram[256 + i] + histogram[512 + i]
RBGW.append(histogram[i] + histogram[256 + i] + histogram[512 + i])
white = 0
black = 0
for i in range(251, 256):
white += RBGW[i]
for i in range(5):
black += RBGW[i]
if black > pixelCount*0.8 and white == 0:
return 1
elif white > pixelCount*0.8 and black == 0:
if histogram[0] == 0:
return -1
elif histogram[255] == 0:
return 1
else:
return False
return 0
def getImageFill(self, webtoon):
fill = 0
if not webtoon and not self.rotated:
# Search for horizontal solid lines
startY = 0
while startY < self.image.size[1]:
if startY + 5 > self.image.size[1]:
startY = self.image.size[1] - 5
checkSolid = self.getImageHistogram(self.image.crop((0, startY, self.image.size[0], startY+5)))
if checkSolid:
fill += checkSolid
startY += 5
def getImageFill(self):
bw = self.image.convert('L').point(lambda x: 0 if x < 128 else 255, '1')
imageBoxA = bw.getbbox()
imageBoxB = ImageChops.invert(bw).getbbox()
if imageBoxA is None or imageBoxB is None:
surfaceB, surfaceW = 0, 0
else:
# Search for vertical solid lines
startX = 0
while startX < self.image.size[0]:
if startX + 5 > self.image.size[0]:
startX = self.image.size[0] - 5
checkSolid = self.getImageHistogram(self.image.crop((startX, 0, startX+5, self.image.size[1])))
if checkSolid:
fill += checkSolid
startX += 5
if fill > 0:
surfaceB = (imageBoxA[2] - imageBoxA[0]) * (imageBoxA[3] - imageBoxA[1])
surfaceW = (imageBoxB[2] - imageBoxB[0]) * (imageBoxB[3] - imageBoxB[1])
if surfaceW < surfaceB:
self.fill = 'white'
elif surfaceW > surfaceB:
self.fill = 'black'
else:
self.fill = 'white'
fill = 0
startY = 0
while startY < bw.size[1]:
if startY + 5 > bw.size[1]:
startY = bw.size[1] - 5
fill += self.getImageHistogram(bw.crop((0, startY, bw.size[0], startY+5)))
startY += 5
startX = 0
while startX < bw.size[0]:
if startX + 5 > bw.size[0]:
startX = bw.size[0] - 5
fill += self.getImageHistogram(bw.crop((startX, 0, startX+5, bw.size[1])))
startX += 5
if fill > 0:
self.fill = 'black'
else:
self.fill = 'white'
def isImageColor(self):
v = ImageStat.Stat(self.image).var
@@ -465,3 +467,59 @@ class ComicPage:
else:
# Detection failed
return False
class Cover:
def __init__(self, source, target, opt, tomeNumber):
self.options = opt
self.source = source
self.target = target
if tomeNumber == 0:
self.tomeNumber = 1
else:
self.tomeNumber = tomeNumber
if self.tomeNumber in self.options.remoteCovers:
try:
source = urlopen(Request(quote(self.options.remoteCovers[self.tomeNumber]).replace('%3A', ':', 1),
headers={'User-Agent': 'KindleComicConverter/' + __version__})).read()
self.image = Image.open(BytesIO(source))
self.processExternal()
except Exception:
self.image = Image.open(source)
self.processInternal()
else:
self.image = Image.open(source)
self.processInternal()
def processInternal(self):
self.image = self.image.convert('RGB')
self.image = self.trim()
self.save()
def processExternal(self):
self.image = self.image.convert('RGB')
self.image.thumbnail(self.options.profileData[1], Image.ANTIALIAS)
self.save(True)
def trim(self):
bg = Image.new(self.image.mode, self.image.size, self.image.getpixel((0, 0)))
diff = ImageChops.difference(self.image, bg)
diff = ImageChops.add(diff, diff, 2.0, -100)
bbox = diff.getbbox()
if bbox:
return self.image.crop(bbox)
else:
return self.image
def save(self, external=False):
if external:
source = self.options.remoteCovers[self.tomeNumber].split('/')[-1]
else:
source = self.source
try:
if os.path.splitext(source)[1].lower() == '.png':
self.image.save(self.target, "PNG", optimize=1)
else:
self.image.save(self.target, "JPEG", optimize=1)
except IOError:
raise RuntimeError('Failed to save cover')

View File

@@ -1,382 +0,0 @@
# -*- coding: utf-8 -*-
#
# Based on initial version of KindleUnpack. Copyright (C) 2009 Charles M. Hannum <root@ihack.net>
# Improvements Copyright (C) 2009-2012 P. Durrant, K. Hendricks, S. Siebert, fandrieu, DiapDealer, nickredding
# Changes for KCC Copyright (C) 2013-2014 Pawel Jastrzebski <pawelj@vulturis.eu>
#
# 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
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import struct
# from uuid import uuid4
# important pdb header offsets
unique_id_seed = 68
number_of_pdb_records = 76
# important palmdoc header offsets
book_length = 4
book_record_count = 8
first_pdb_record = 78
# important rec0 offsets
length_of_book = 4
mobi_header_base = 16
mobi_header_length = 20
mobi_type = 24
mobi_version = 36
first_non_text = 80
title_offset = 84
first_image_record = 108
first_content_index = 192
last_content_index = 194
kf8_last_content_index = 192 # for KF8 mobi headers
fcis_index = 200
flis_index = 208
srcs_index = 224
srcs_count = 228
primary_index = 244
datp_index = 256
huffoff = 112
hufftbloff = 120
def getint(datain, ofs, sz='L'):
i, = struct.unpack_from('>'+sz, datain, ofs)
return i
def writeint(datain, ofs, n, length='L'):
if length == 'L':
return datain[:ofs]+struct.pack('>L', n)+datain[ofs+4:]
else:
return datain[:ofs]+struct.pack('>H', n)+datain[ofs+2:]
def getsecaddr(datain, secno):
nsec = getint(datain, number_of_pdb_records, 'H')
assert secno >= 0 & secno < nsec, 'secno %d out of range (nsec=%d)' % (secno, nsec)
secstart = getint(datain, first_pdb_record+secno*8)
if secno == nsec-1:
secend = len(datain)
else:
secend = getint(datain, first_pdb_record+(secno+1)*8)
return secstart, secend
def readsection(datain, secno):
secstart, secend = getsecaddr(datain, secno)
return datain[secstart:secend]
def writesection(datain, secno, secdata): # overwrite, accounting for different length
dataout = deletesectionrange(datain, secno, secno)
return insertsection(dataout, secno, secdata)
def nullsection(datain, secno): # make it zero-length without deleting it
datalst = []
nsec = getint(datain, number_of_pdb_records, 'H')
secstart, secend = getsecaddr(datain, secno)
zerosecstart, zerosecend = getsecaddr(datain, 0)
dif = secend-secstart
datalst.append(datain[:first_pdb_record])
for i in range(0, secno+1):
ofs, flgval = struct.unpack_from('>2L', datain, first_pdb_record+i*8)
datalst.append(struct.pack('>L', ofs) + struct.pack('>L', flgval))
for i in range(secno+1, nsec):
ofs, flgval = struct.unpack_from('>2L', datain, first_pdb_record+i*8)
ofs -= dif
datalst.append(struct.pack('>L', ofs) + struct.pack('>L', flgval))
lpad = zerosecstart - (first_pdb_record + 8*nsec)
if lpad > 0:
datalst.append(b'\0' * lpad)
datalst.append(datain[zerosecstart: secstart])
datalst.append(datain[secend:])
dataout = b"".join(datalst)
return dataout
def deletesectionrange(datain, firstsec, lastsec): # delete a range of sections
datalst = []
firstsecstart, firstsecend = getsecaddr(datain, firstsec)
lastsecstart, lastsecend = getsecaddr(datain, lastsec)
zerosecstart, zerosecend = getsecaddr(datain, 0)
dif = lastsecend - firstsecstart + 8*(lastsec-firstsec+1)
nsec = getint(datain, number_of_pdb_records, 'H')
datalst.append(datain[:unique_id_seed])
datalst.append(struct.pack('>L', 2*(nsec-(lastsec-firstsec+1))+1))
datalst.append(datain[unique_id_seed+4:number_of_pdb_records])
datalst.append(struct.pack('>H', nsec-(lastsec-firstsec+1)))
newstart = zerosecstart - 8*(lastsec-firstsec+1)
for i in range(0, firstsec):
ofs, flgval = struct.unpack_from('>2L', datain, first_pdb_record+i*8)
ofs -= 8 * (lastsec - firstsec + 1)
datalst.append(struct.pack('>L', ofs) + struct.pack('>L', flgval))
for i in range(lastsec+1, nsec):
ofs, flgval = struct.unpack_from('>2L', datain, first_pdb_record+i*8)
ofs -= dif
flgval = 2*(i-(lastsec-firstsec+1))
datalst.append(struct.pack('>L', ofs) + struct.pack('>L', flgval))
lpad = newstart - (first_pdb_record + 8*(nsec - (lastsec - firstsec + 1)))
if lpad > 0:
datalst.append(b'\0' * lpad)
datalst.append(datain[zerosecstart:firstsecstart])
datalst.append(datain[lastsecend:])
dataout = b"".join(datalst)
return dataout
def insertsection(datain, secno, secdata): # insert a new section
datalst = []
nsec = getint(datain, number_of_pdb_records, 'H')
secstart, secend = getsecaddr(datain, secno)
zerosecstart, zerosecend = getsecaddr(datain, 0)
dif = len(secdata)
datalst.append(datain[:unique_id_seed])
datalst.append(struct.pack('>L', 2*(nsec+1)+1))
datalst.append(datain[unique_id_seed+4:number_of_pdb_records])
datalst.append(struct.pack('>H', nsec+1))
newstart = zerosecstart + 8
for i in range(0, secno):
ofs, flgval = struct.unpack_from('>2L', datain, first_pdb_record+i*8)
ofs += 8
datalst.append(struct.pack('>L', ofs) + struct.pack('>L', flgval))
datalst.append(struct.pack('>L', secstart + 8) + struct.pack('>L', (2*secno)))
for i in range(secno, nsec):
ofs, flgval = struct.unpack_from('>2L', datain, first_pdb_record+i*8)
ofs = ofs + dif + 8
flgval = 2*(i+1)
datalst.append(struct.pack('>L', ofs) + struct.pack('>L', flgval))
lpad = newstart - (first_pdb_record + 8*(nsec + 1))
if lpad > 0:
datalst.append(b'\0' * lpad)
datalst.append(datain[zerosecstart:secstart])
datalst.append(secdata)
datalst.append(datain[secstart:])
dataout = b"".join(datalst)
return dataout
def insertsectionrange(sectionsource, firstsec, lastsec, sectiontarget, targetsec): # insert a range of sections
dataout = sectiontarget
for idx in range(lastsec, firstsec-1, -1):
dataout = insertsection(dataout, targetsec, readsection(sectionsource, idx))
return dataout
def get_exth_params(rec0):
ebase = mobi_header_base + getint(rec0, mobi_header_length)
elen = getint(rec0, ebase+4)
enum = getint(rec0, ebase+8)
return ebase, elen, enum
def add_exth(rec0, exth_num, exth_bytes):
ebase, elen, enum = get_exth_params(rec0)
newrecsize = 8+len(exth_bytes)
newrec0 = rec0[0:ebase+4]+struct.pack('>L', elen+newrecsize)+struct.pack('>L', enum+1) +\
struct.pack('>L', exth_num) + struct.pack('>L', newrecsize)+exth_bytes+rec0[ebase+12:]
newrec0 = writeint(newrec0, title_offset, getint(newrec0, title_offset)+newrecsize)
return newrec0
def read_exth(rec0, exth_num):
exth_values = []
ebase, elen, enum = get_exth_params(rec0)
ebase += 12
while enum > 0:
exth_id = getint(rec0, ebase)
if exth_id == exth_num:
# We might have multiple exths, so build a list.
exth_values.append(rec0[ebase+8:ebase+getint(rec0, ebase+4)])
enum -= 1
ebase = ebase+getint(rec0, ebase+4)
return exth_values
def write_exth(rec0, exth_num, exth_bytes):
ebase, elen, enum = get_exth_params(rec0)
ebase_idx = ebase+12
enum_idx = enum
while enum_idx > 0:
exth_id = getint(rec0, ebase_idx)
if exth_id == exth_num:
dif = len(exth_bytes)+8-getint(rec0, ebase_idx+4)
newrec0 = rec0
if dif != 0:
newrec0 = writeint(newrec0, title_offset, getint(newrec0, title_offset)+dif)
return newrec0[:ebase+4]+struct.pack('>L', elen+len(exth_bytes)+8-getint(rec0, ebase_idx+4)) +\
struct.pack('>L', enum)+rec0[ebase+12:ebase_idx+4] +\
struct.pack('>L', len(exth_bytes)+8)+exth_bytes +\
rec0[ebase_idx+getint(rec0, ebase_idx+4):]
enum_idx -= 1
ebase_idx = ebase_idx+getint(rec0, ebase_idx+4)
return rec0
def del_exth(rec0, exth_num):
ebase, elen, enum = get_exth_params(rec0)
ebase_idx = ebase+12
enum_idx = 0
while enum_idx < enum:
exth_id = getint(rec0, ebase_idx)
exth_size = getint(rec0, ebase_idx+4)
if exth_id == exth_num:
newrec0 = rec0
newrec0 = writeint(newrec0, title_offset, getint(newrec0, title_offset)-exth_size)
newrec0 = newrec0[:ebase_idx]+newrec0[ebase_idx+exth_size:]
newrec0 = newrec0[0:ebase+4]+struct.pack('>L', elen-exth_size)+struct.pack('>L', enum-1)+newrec0[ebase+12:]
return newrec0
enum_idx += 1
ebase_idx = ebase_idx+exth_size
return rec0
class mobi_split:
def __init__(self, infile, newKindle):
try:
datain = open(infile, 'rb').read()
datain_rec0 = readsection(datain, 0)
ver = getint(datain_rec0, mobi_version)
# fake_asin = str(uuid4())
self.combo = (ver != 8)
if not self.combo:
return
exth121 = read_exth(datain_rec0, 121)
if len(exth121) == 0:
self.combo = False
return
else:
# only pay attention to first exth121
# (there should only be one)
datain_kf8, = struct.unpack_from('>L', exth121[0], 0)
if datain_kf8 == 0xffffffff:
self.combo = False
return
datain_kfrec0 = readsection(datain, datain_kf8)
firstimage = getint(datain_rec0, first_image_record)
lastimage = getint(datain_rec0, last_content_index, 'H')
if not newKindle:
# create the standalone mobi7
num_sec = getint(datain, number_of_pdb_records, 'H')
# remove BOUNDARY up to but not including ELF record
self.result_file = deletesectionrange(datain, datain_kf8-1, num_sec-2)
# check if there are SRCS records and delete them
srcs = getint(datain_rec0, srcs_index)
num_srcs = getint(datain_rec0, srcs_count)
if srcs != 0xffffffff and num_srcs > 0:
self.result_file = deletesectionrange(self.result_file, srcs, srcs+num_srcs-1)
datain_rec0 = writeint(datain_rec0, srcs_index, 0xffffffff)
datain_rec0 = writeint(datain_rec0, srcs_count, 0)
# reset the EXTH 121 KF8 Boundary meta data to 0xffffffff
datain_rec0 = write_exth(datain_rec0, 121, struct.pack('>L', 0xffffffff))
# datain_rec0 = del_exth(datain_rec0,121)
# datain_rec0 = del_exth(datain_rec0,534)
# don't remove the EXTH 125 KF8 Count of Resources, seems to be present in mobi6 files as well
# set the EXTH 129 KF8 Masthead / Cover Image string to the null string
datain_rec0 = write_exth(datain_rec0, 129, b'')
# don't remove the EXTH 131 KF8 Unidentified Count, seems to be present in mobi6 files as well
# Make sure we have an ASIN & cdeType set...
# if len(read_exth(datain_rec0, 113)) == 0:
# datain_rec0 = add_exth(datain_rec0, 113, fake_asin)
# if len(read_exth(datain_rec0, 504)) == 0:
# datain_rec0 = add_exth(datain_rec0, 504, fake_asin)
if len(read_exth(datain_rec0, 501)) == 0:
datain_rec0 = add_exth(datain_rec0, 501, b'EBOK')
# need to reset flags stored in 0x80-0x83
# old mobi with exth: 0x50, mobi7 part with exth: 0x1850, mobi8 part with exth: 0x1050
# Bit Flags
# 0x1000 = Bit 12 indicates if embedded fonts are used or not
# 0x0800 = means this Header points to *shared* images/resource/fonts ??
# 0x0080 = unknown new flag, why is this now being set by Kindlegen 2.8?
# 0x0040 = exth exists
# 0x0010 = Not sure but this is always set so far
fval, = struct.unpack_from('>L', datain_rec0, 0x80)
# need to remove flag 0x0800 for KindlePreviewer 2.8 and unset Bit 12 for embedded fonts
fval &= 0x07FF
datain_rec0 = datain_rec0[:0x80] + struct.pack('>L', fval) + datain_rec0[0x84:]
self.result_file = writesection(self.result_file, 0, datain_rec0)
if lastimage == 0xffff:
# find the lowest of the next sections and copy up to that.
ofs_list = [(kf8_last_content_index, 'L'), (fcis_index, 'L'), (flis_index, 'L'), (datp_index, 'L'),
(hufftbloff, 'L')]
for ofs, sz in ofs_list:
n = getint(datain_kfrec0, ofs, sz)
if 0 < n < lastimage:
lastimage = n-1
# Try to null out FONT and RES, but leave the (empty) PDB record so image refs remain valid
for i in range(firstimage, lastimage):
imgsec = readsection(self.result_file, i)
if imgsec[0:4] in ['RESC', 'FONT']:
self.result_file = nullsection(self.result_file, i)
# mobi7 finished
else:
# create standalone mobi8
self.result_file = deletesectionrange(datain, 0, datain_kf8-1)
target = getint(datain_kfrec0, first_image_record)
self.result_file = insertsectionrange(datain, firstimage, lastimage, self.result_file, target)
datain_kfrec0 = readsection(self.result_file, 0)
# Only keep the correct EXTH 116 StartOffset, KG 2.5 carries over the one from the mobi7 part,
# which then points at garbage in the mobi8 part, and confuses FW 3.4
kf8starts = read_exth(datain_kfrec0, 116)
# If we have multiple StartOffset, keep only the last one
kf8start_count = len(kf8starts)
while kf8start_count > 1:
kf8start_count -= 1
datain_kfrec0 = del_exth(datain_kfrec0, 116)
# update the EXTH 125 KF8 Count of Images/Fonts/Resources
datain_kfrec0 = write_exth(datain_kfrec0, 125, struct.pack('>L', lastimage-firstimage+1))
# Same dance for the KF8, we want an ASIN & cdeType :)
# if len(read_exth(datain_kfrec0, 113)) == 0:
# datain_kfrec0 = add_exth(datain_kfrec0, 113, fake_asin)
# if len(read_exth(datain_kfrec0, 504)) == 0:
# datain_kfrec0 = add_exth(datain_kfrec0, 504, fake_asin)
if len(read_exth(datain_kfrec0, 501)) == 0:
datain_kfrec0 = add_exth(datain_kfrec0, 501, b'EBOK')
# need to reset flags stored in 0x80-0x83
# old mobi with exth: 0x50, mobi7 part with exth: 0x1850, mobi8 part with exth: 0x1050
# standalone mobi8 with exth: 0x0050
# Bit Flags
# 0x1000 = Bit 12 indicates if embedded fonts are used or not
# 0x0800 = means this Header points to *shared* images/resource/fonts ??
# 0x0080 = unknown new flag, why is this now being set by Kindlegen 2.8?
# 0x0040 = exth exists
# 0x0010 = Not sure but this is always set so far
fval, = struct.unpack_from('>L', datain_kfrec0, 0x80)
fval &= 0x1FFF
fval |= 0x0800
datain_kfrec0 = datain_kfrec0[:0x80] + struct.pack('>L', fval) + datain_kfrec0[0x84:]
# properly update other index pointers that have been shifted by the insertion of images
ofs_list = [(kf8_last_content_index, 'L'), (fcis_index, 'L'), (flis_index, 'L'), (datp_index, 'L'),
(hufftbloff, 'L')]
for ofs, sz in ofs_list:
n = getint(datain_kfrec0, ofs, sz)
if n != 0xffffffff:
datain_kfrec0 = writeint(datain_kfrec0, ofs, n+lastimage-firstimage+1, sz)
self.result_file = writesection(self.result_file, 0, datain_kfrec0)
# mobi8 finished
except Exception:
raise
def getResult(self):
return self.result_file

View File

@@ -1,5 +1,5 @@
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
# Copyright (c) 2013-2014 Pawel Jastrzebski <pawelj@vulturis.eu>
# Copyright (c) 2013-2014 Pawel Jastrzebski <pawelj@iosphe.re>
#
# Based upon the code snippet by Ned Batchelder
# (http://nedbatchelder.com/blog/200712/extracting_jpgs_from_pdfs.html)
@@ -20,7 +20,7 @@
#
__license__ = 'ISC'
__copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@vulturis.eu>'
__copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>'
__docformat__ = 'restructuredtext en'
import os

View File

@@ -178,6 +178,23 @@ EXTRACT_ARGS = ('x', '-y', '-idq')
#: args for testrar()
TEST_ARGS = ('t', '-idq')
#
# Allow use of tool that is not compatible with unrar.
#
# By default use 'bsdtar' which is 'tar' program that
# sits on top of libarchive.
#
# Problems with libarchive RAR backend:
# - Does not support solid archives.
# - Does not support password-protected archives.
#
ALT_TOOL = 'bsdtar'
ALT_OPEN_ARGS = ('-x', '--to-stdout', '-f')
ALT_EXTRACT_ARGS = ('-x', '-f')
ALT_TEST_ARGS = ('-t', '-f')
ALT_CHECK_ARGS = ('--help',)
#: whether to speed up decompression by using tmp archive
USE_EXTRACT_HACK = 1
@@ -336,6 +353,8 @@ class RarUnknownError(RarExecError):
"""Unknown exit code"""
class RarSignalExit(RarExecError):
"""Unrar exited with signal"""
class RarCannotExec(RarExecError):
"""Executable not found."""
def is_rarfile(xfile):
@@ -693,10 +712,7 @@ class RarFile(object):
"""Let 'unrar' test the archive.
"""
cmd = [UNRAR_TOOL] + list(TEST_ARGS)
if self._password is not None:
cmd.append('-p' + self._password)
else:
cmd.append('-p-')
add_password_arg(cmd, self._password)
cmd.append(self.rarfile)
p = custom_popen(cmd)
output = p.communicate()[0]
@@ -1172,8 +1188,7 @@ class RarFile(object):
if is_filelike(rarfile):
raise ValueError("Cannot use unrar directly on memory buffer")
cmd = [UNRAR_TOOL] + list(OPEN_ARGS)
if psw is not None:
cmd.append("-p" + psw)
add_password_arg(cmd, psw)
cmd.append(rarfile)
# not giving filename avoids encoding related problems
@@ -1205,10 +1220,7 @@ class RarFile(object):
# pasoword
psw = psw or self._password
if psw is not None:
cmd.append('-p' + psw)
else:
cmd.append('-p-')
add_password_arg(cmd, psw)
# rar file
cmd.append(self.rarfile)
@@ -1830,10 +1842,7 @@ def rar_decompress(vers, meth, data, declen=0, flags=0, crc=0, psw=None, salt=No
tmpf.close()
cmd = [UNRAR_TOOL] + list(OPEN_ARGS)
if psw is not None and (flags & RAR_FILE_PASSWORD):
cmd.append("-p" + psw)
else:
cmd.append("-p-")
add_password_arg(cmd, psw, (flags & RAR_FILE_PASSWORD))
cmd.append(tmpname)
p = custom_popen(cmd)
@@ -1902,10 +1911,27 @@ def custom_popen(cmd):
except OSError:
ex = sys.exc_info()[1]
if ex.errno == errno.ENOENT:
raise RarExecError("Unrar not installed? (rarfile.UNRAR_TOOL=%r)" % UNRAR_TOOL)
raise RarCannotExec("Unrar not installed? (rarfile.UNRAR_TOOL=%r)" % UNRAR_TOOL)
raise
return p
def custom_check(cmd, ignore_retcode=False):
"""Run command, collect output, raise error if needed."""
p = custom_popen(cmd)
out, err = p.communicate()
if p.returncode and not ignore_retcode:
raise RarExecError("Check-run failed")
return out
def add_password_arg(cmd, psw, required=False):
"""Append password switch to commandline."""
if UNRAR_TOOL == ALT_TOOL:
return
if psw is not None:
cmd.append('-p' + psw)
else:
cmd.append('-p-')
def check_returncode(p, out):
"""Raise exception according to unrar exit code"""
@@ -1920,6 +1946,8 @@ def check_returncode(p, out):
RarWarning, RarFatalError, RarCRCError, RarLockedArchiveError,
RarWriteError, RarOpenError, RarUserError, RarMemoryError,
RarCreateError, RarNoFilesError] # codes from rar.txt
if UNRAR_TOOL == ALT_TOOL:
errmap = [None]
if code > 0 and code < len(errmap):
exc = errmap[code]
elif code == 255:
@@ -1936,3 +1964,24 @@ def check_returncode(p, out):
msg = "%s [%d]" % (exc.__doc__, p.returncode)
raise exc(msg)
#
# Check if unrar works
#
try:
# does UNRAR_TOOL work?
custom_check([UNRAR_TOOL], True)
except RarCannotExec:
try:
# does ALT_TOOL work?
custom_check([ALT_TOOL] + list(ALT_CHECK_ARGS), True)
# replace config
UNRAR_TOOL = ALT_TOOL
OPEN_ARGS = ALT_OPEN_ARGS
EXTRACT_ARGS = ALT_EXTRACT_ARGS
TEST_ARGS = ALT_TEST_ARGS
except RarCannotExec:
# no usable tool, only uncompressed archives work
pass

View File

@@ -1,5 +1,5 @@
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
# Copyright (c) 2013-2014 Pawel Jastrzebski <pawelj@vulturis.eu>
# Copyright (c) 2013-2014 Pawel Jastrzebski <pawelj@iosphe.re>
#
# Permission to use, copy, modify, and/or distribute this software for
# any purpose with or without fee is hereby granted, provided that the
@@ -17,7 +17,7 @@
#
__license__ = 'ISC'
__copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@vulturis.eu>'
__copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>'
__docformat__ = 'restructuredtext en'
import os

Binary file not shown.

Binary file not shown.

View File

@@ -1,12 +1,12 @@
#!/usr/bin/env python3
"""
cx_Freeze build script for KCC.
py2exe/py2app build script for KCC.
Usage (Windows):
python setup.py py2exe
Usage (Mac OS X):
python setup.py py2app
Usage (Windows):
python setup.py build
"""
from sys import platform, version_info
if version_info[0] != 3:
@@ -14,7 +14,7 @@ if version_info[0] != 3:
exit(1)
NAME = "KindleComicConverter"
VERSION = "4.0.1"
VERSION = "4.3.1"
MAIN = "kcc.py"
if platform == "darwin":
@@ -47,7 +47,7 @@ if platform == "darwin":
],
LSMinimumSystemVersion='10.8.0',
LSEnvironment=dict(
PATH='/usr/local/bin:/usr/bin:/bin'
PATH='./../Resources:/usr/local/bin:/usr/bin:/bin'
),
NSHumanReadableCopyright='ISC License (ISCL)'
)
@@ -55,42 +55,51 @@ if platform == "darwin":
)
)
elif platform == "win32":
# noinspection PyUnresolvedReferences
import py2exe
import platform as arch
from cx_Freeze import setup, Executable
from distutils.core import setup
if arch.architecture()[0] == '64bit':
library = 'libEGL64.dll'
suffix = '_64'
else:
library = 'libEGL32.dll'
base = "Win32GUI"
suffix = ''
additional_files = [('platforms', ['C:\Python34' + suffix +
'\Lib\site-packages\PyQt5\plugins\platforms\qwindows.dll']),
('', ['LICENSE.txt',
'other\\7za.exe',
'other\\UnRAR.exe',
'other\\Additional-LICENSE.txt',
'C:\Python34' + suffix + '\Lib\site-packages\PyQt5\libEGL.dll'])]
extra_options = dict(
options={"build_exe": {"optimize": 2,
"include_files": ['LICENSE.txt',
['other/UnRAR.exe', 'UnRAR.exe'],
['other/7za.exe', '7za.exe'],
['other/Additional-LICENSE.txt', 'Additional-LICENSE.txt'],
['other/' + library, 'libEGL.dll']
],
"copy_dependent_files": True,
"create_shared_zip": False,
"append_script_to_exe": True,
"replace_paths": '*=',
"excludes": ['tkinter']}},
executables=[Executable(MAIN,
base=base,
targetName="KCC.exe",
icon="icons/comic2ebook.ico",
compress=False)])
options={'py2exe': {"bundle_files": 1,
"dll_excludes": ["tcl85.dll", "tk85.dll"],
"dist_dir": "dist" + suffix,
"compressed": True,
"includes": ["sip"],
"excludes": ["tkinter"],
"optimize": 2}},
windows=[{"script": MAIN,
"dest_base": "KCC",
"version": VERSION,
"copyright": "Ciro Mattia Gonano, Pawel Jastrzebski © 2014",
"legal_copyright": "ISC License (ISCL)",
"product_version": VERSION,
"product_name": "Kindle Comic Converter",
"file_description": "Kindle Comic Converter",
"icon_resources": [(1, "icons\comic2ebook.ico")]}],
zipfile=None,
data_files=additional_files)
else:
print('Please use setup.sh to build Linux package.')
exit()
#noinspection PyUnboundLocalVariable
# noinspection PyUnboundLocalVariable
setup(
name=NAME,
version=VERSION,
author="Ciro Mattia Gonano, Pawel Jastrzebski",
author_email="ciromattia@gmail.com, pawelj@vulturis.eu",
description="A tool to convert comics (CBR/CBZ/PDFs/image folders) to MOBI.",
author_email="ciromattia@gmail.com, pawelj@iosphe.re",
description="Kindle Comic Converter",
license="ISC License (ISCL)",
keywords="kindle comic mobipocket mobi cbz cbr manga",
url="http://github.com/ciromattia/kcc",

View File

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