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

Compare commits

...

50 Commits
3.5 ... 3.6

Author SHA1 Message Date
Paweł Jastrzębski
0cf92fc48f Fixed psutil detection 2013-12-06 17:58:39 +01:00
Paweł Jastrzębski
953942ca00 Fixed tray icon owner 2013-12-06 17:42:11 +01:00
Paweł Jastrzębski
c46ca8b507 Updated README + Minor tweaks 2013-12-06 17:18:20 +01:00
Paweł Jastrzębski
48d3bee225 Upscaling is now default on Kindle Fire HD/HDX models 2013-12-05 19:49:29 +01:00
Paweł Jastrzębski
3b0e5cc309 Panel View support overhaul - Round 2 2013-12-05 15:41:47 +01:00
Paweł Jastrzębski
e5be31f9d5 TrayIcon: Clicking it now properly unminimize window 2013-12-04 19:04:54 +01:00
Paweł Jastrzębski
17ea85c31f Overhauled Panel View support 2013-12-04 18:32:32 +01:00
Paweł Jastrzębski
572e1422bf Gamma auto mode is now even more automatic 2013-12-04 18:30:19 +01:00
Ciro Mattia Gonano
af263073b5 Update README.md 2013-12-04 11:42:45 +01:00
Ciro Mattia Gonano
7facf2d620 Re-add text link for bitcoin 2013-12-04 11:42:26 +01:00
Ciro Mattia Gonano
eef3ff434b Added BountySource link and reformatted Donations section 2013-12-04 11:08:04 +01:00
Ciro Mattia Gonano
c680cfd5c5 Update README.md 2013-11-27 12:40:01 +01:00
Paweł Jastrzębski
3e8469611d Updated README 2013-11-25 10:41:30 +01:00
Paweł Jastrzębski
39ab475156 Updated README 2013-11-25 10:38:20 +01:00
Ciro Mattia Gonano
636de67a17 Update README.md 2013-11-25 10:14:56 +01:00
Ciro Mattia Gonano
d80c18f652 Add OS X 10.7 build download link 2013-11-25 10:14:23 +01:00
Paweł Jastrzębski
557bd2bbbf Added separate resolution for Kindle DX/DXG CBZ output (close #71) 2013-11-19 08:46:13 +01:00
Paweł Jastrzębski
ddd223c2ec Code cleanup 2013-11-13 11:27:26 +01:00
Paweł Jastrzębski
50f5b600b1 OS specific tweaks to status bar style 2013-11-12 14:46:32 +01:00
Paweł Jastrzębski
d94df8390a Added status bar with links 2013-11-12 14:32:38 +01:00
Paweł Jastrzębski
8b33331929 Disabled systray icon on OSX (close #70) 2013-11-12 13:18:27 +01:00
Paweł Jastrzębski
86a9dde1eb Added simple tray icon (close #69) 2013-11-12 12:13:57 +01:00
Ciro Mattia Gonano
33dec77063 Merge remote-tracking branch 'origin/master' 2013-11-11 11:52:04 +01:00
Ciro Mattia Gonano
fe06e2fa19 Version bump 2013-11-11 11:51:46 +01:00
Paweł Jastrzębski
7b5e3eaafd Updated README 2013-11-11 11:30:00 +01:00
Paweł Jastrzębski
0a30f1ffb9 Implemented OSX PATH change to Windows code too + minor tweaks 2013-11-09 20:09:34 +01:00
Paweł Jastrzębski
8687604d26 Tweak for Windows development environment 2013-11-08 18:19:58 +01:00
Ciro Mattia Gonano
0a9fd6c439 Merge branch 'master' of github.com:ciromattia/kcc 2013-11-08 17:21:42 +01:00
Paweł Jastrzębski
c95a9395de Optimization of ProgressThread 2013-11-08 17:13:41 +01:00
Paweł Jastrzębski
77066d7a9f Optimization of ProgressThread 2013-11-08 17:06:06 +01:00
Paweł Jastrzębski
c8e5b7de9a Implemented new method to detect border color in non-webtoon comics 2013-11-08 16:55:43 +01:00
Ciro Mattia Gonano
3e11a88a7c Bundle 7za and unrar for OSX too. 2013-11-08 15:32:22 +01:00
Paweł Jastrzębski
a7e4968836 GUI tweaks 2013-11-08 15:11:33 +01:00
Paweł Jastrzębski
6d9e2d3c03 Added Linux build script 2013-11-07 22:53:28 +01:00
Paweł Jastrzębski
0789e7a353 Updated README 2013-11-07 13:53:37 +01:00
Ciro Mattia Gonano
ff97a85552 KCC available from 10.6+ 2013-11-07 12:57:39 +01:00
Ciro Mattia Gonano
33cfd92cef Remove 10.8 limit 2013-11-07 12:14:03 +01:00
Paweł Jastrzębski
58513ef59f Updated Inno Setup script 2013-11-07 07:58:03 +01:00
Paweł Jastrzębski
6056e3e767 Updated OSX setup 2013-11-06 18:44:14 +01:00
Paweł Jastrzębski
1b1ed7c4ab Improved error messages about missing dependencies 2013-11-06 13:49:12 +01:00
Paweł Jastrzębski
5b44e4bddd Moved to psutil Popen 2013-11-06 11:41:19 +01:00
Paweł Jastrzębski
54592969a4 Optimized imports 2013-11-06 11:14:01 +01:00
Paweł Jastrzębski
38007ab3d5 Number of KindleGen threads is now dynamic 2013-11-06 11:08:14 +01:00
Paweł Jastrzębski
bdd10c7617 Tweaked KindleGen/KindleUnpack multiprocessing 2013-11-05 16:11:14 +01:00
Paweł Jastrzębski
c0f4bc021a Tweaked ComicRack metadata parser 2013-11-04 20:08:54 +01:00
Paweł Jastrzębski
34d6af93a6 Refactored KindleGen/KindleUnpack handling 2013-11-04 17:07:10 +01:00
Paweł Jastrzębski
0df481dabb Added ComicRack metadata parser 2013-11-03 09:29:13 +01:00
Paweł Jastrzębski
55c5b91411 README update 2013-10-31 14:04:06 +01:00
Paweł Jastrzębski
be745f4602 README update 2013-10-31 13:50:40 +01:00
Paweł Jastrzębski
8bf5ad0f12 Fixed headers 2013-10-30 11:50:32 +01:00
18 changed files with 850 additions and 423 deletions

8
.gitignore vendored
View File

@@ -2,11 +2,11 @@
*.cbz
*.cbr
.idea
.DS_Store
Thumbs.db
build
dist
Output
test
solaio
kindlegen*
UnRAR*
7za*
.DS_Store
Thumbs.db

View File

@@ -7,19 +7,19 @@
<x>0</x>
<y>0</y>
<width>420</width>
<height>380</height>
<height>397</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>420</width>
<height>380</height>
<height>397</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>420</width>
<height>380</height>
<height>397</height>
</size>
</property>
<property name="font">
@@ -27,9 +27,6 @@
<pointsize>9</pointsize>
</font>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="windowTitle">
<string>Kindle Comic Converter</string>
</property>
@@ -546,9 +543,6 @@ p, li { white-space: pre-wrap; }
<family>DejaVu Sans</family>
</font>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;When converting color images setting this option to 1.0 &lt;span style=&quot; font-weight:600;&quot;&gt;might&lt;/span&gt; improve readability.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Gamma: Auto</string>
</property>
@@ -570,9 +564,6 @@ p, li { white-space: pre-wrap; }
<property name="focusPolicy">
<enum>Qt::ClickFocus</enum>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;When converting color images setting this option to 1.0 &lt;span style=&quot; font-weight:600;&quot;&gt;might&lt;/span&gt; improve readability.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="maximum">
<number>500</number>
</property>
@@ -785,6 +776,17 @@ p, li { white-space: pre-wrap; }
<zorder>OptionsExpert</zorder>
<zorder>ProgressBar</zorder>
</widget>
<widget class="QStatusBar" name="statusBar">
<property name="font">
<font>
<family>DejaVu Sans</family>
<pointsize>8</pointsize>
</font>
</property>
<property name="sizeGripEnabled">
<bool>false</bool>
</property>
</widget>
<action name="ActionBasic">
<property name="checkable">
<bool>true</bool>

View File

@@ -7,19 +7,19 @@
<x>0</x>
<y>0</y>
<width>420</width>
<height>380</height>
<height>397</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>420</width>
<height>380</height>
<height>397</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>420</width>
<height>380</height>
<height>397</height>
</size>
</property>
<property name="font">
@@ -27,9 +27,6 @@
<pointsize>9</pointsize>
</font>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="windowTitle">
<string>Kindle Comic Converter</string>
</property>
@@ -546,9 +543,6 @@
<bold>false</bold>
</font>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:12pt;&quot;&gt;When converting color images setting this option to 1.0 &lt;/span&gt;&lt;span style=&quot; font-size:12pt; font-weight:600;&quot;&gt;might&lt;/span&gt;&lt;span style=&quot; font-size:12pt;&quot;&gt; improve readability.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Gamma: Auto</string>
</property>
@@ -570,9 +564,6 @@
<property name="focusPolicy">
<enum>Qt::ClickFocus</enum>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:12pt;&quot;&gt;When converting color images setting this option to 1.0 &lt;/span&gt;&lt;span style=&quot; font-size:12pt; font-weight:600;&quot;&gt;might&lt;/span&gt;&lt;span style=&quot; font-size:12pt;&quot;&gt; improve readability.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="maximum">
<number>500</number>
</property>
@@ -797,6 +788,17 @@
<zorder>OptionsAdvancedGamma</zorder>
<zorder>OptionsExpert</zorder>
</widget>
<widget class="QStatusBar" name="statusBar">
<property name="font">
<font>
<family>Aharoni</family>
<pointsize>8</pointsize>
</font>
</property>
<property name="sizeGripEnabled">
<bool>false</bool>
</property>
</widget>
<action name="ActionBasic">
<property name="checkable">
<bool>true</bool>

26
KCC.ui
View File

@@ -7,19 +7,19 @@
<x>0</x>
<y>0</y>
<width>420</width>
<height>380</height>
<height>397</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>420</width>
<height>380</height>
<height>397</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>420</width>
<height>380</height>
<height>397</height>
</size>
</property>
<property name="font">
@@ -27,9 +27,6 @@
<pointsize>9</pointsize>
</font>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="windowTitle">
<string>Kindle Comic Converter</string>
</property>
@@ -472,9 +469,6 @@ p, li { white-space: pre-wrap; }
<height>40</height>
</rect>
</property>
<property name="toolTip">
<string>When converting color images setting this option to 1.0 MIGHT improve readability.</string>
</property>
<property name="text">
<string>Gamma: Auto</string>
</property>
@@ -491,9 +485,6 @@ p, li { white-space: pre-wrap; }
<property name="focusPolicy">
<enum>Qt::ClickFocus</enum>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;When converting color images setting this option to 1.0 &lt;span style=&quot; font-weight:600;&quot;&gt;might&lt;/span&gt; improve readability.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="maximum">
<number>500</number>
</property>
@@ -674,6 +665,17 @@ p, li { white-space: pre-wrap; }
<zorder>OptionsExpert</zorder>
<zorder>ProgressBar</zorder>
</widget>
<widget class="QStatusBar" name="statusBar">
<property name="font">
<font>
<family>MS Shell Dlg 2</family>
<pointsize>8</pointsize>
</font>
</property>
<property name="sizeGripEnabled">
<bool>false</bool>
</property>
</widget>
<action name="ActionBasic">
<property name="checkable">
<bool>true</bool>

View File

@@ -1,6 +1,6 @@
# KCC
**KindleComicConverter** is a Python app to convert comic files or folders to ePub or Panel View MOBI.
**Kindle Comic Converter** is a Python app to convert comic files or folders to ePub or Panel View MOBI.
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**_.
It can also optionally optimize images by applying a number of transformations.
@@ -8,21 +8,23 @@ 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.
If you want to read some comments over *Amazon's KC2* you can take a look at [this](http://www.mobileread.com/forums/showthread.php?t=207461&page=7#96) and [that](http://www.mobileread.com/forums/showthread.php?t=211047) threads on Mobileread.
_KC2_ in no way is a replacement for **KCC** so you can be quite confident we'll going to carry on developing our little monster ;)
_KC2_ in no way is a replacement for **KCC** so you can be quite confident we'll going to carry on developing our little monster ;-)
### Issues / new features / donations
If you have some problems using KCC please [file an issue here](https://github.com/ciromattia/kcc/issues/new).
If you can fix an open issue, fork & make a pull request.
If you want more chances an issue is fixes or your wanted feature added, consider [placing a bounty](https://www.bountysource.com/trackers/65571-ciromattia-kcc)!
### Donations
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)
* 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)
* 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)
## BINARY RELEASES
You can find the latest released binary at the following links:
- **Win64:** [http://kcc.vulturis.eu/Win64/](http://kcc.vulturis.eu/Win64/)
- **Win32:** [http://kcc.vulturis.eu/Win32/](http://kcc.vulturis.eu/Win32/)
- **OS X:** [http://kcc.vulturis.eu/OSX/](http://kcc.vulturis.eu/OSX/)
- **Linux:** Just download sourcecode and launch: `python kcc.py`
- **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 or later):** [http://kcc.vulturis.eu/OSX/](http://kcc.vulturis.eu/OSX/)
- **OS X (10.7 or earlier):** Soon™
## INPUT FORMATS
**KCC** can understand and convert, at the moment, the following file types:
@@ -40,9 +42,10 @@ You can find the latest released binary at the following links:
### For compiling/running from source:
- Python 2.7 - Included in MacOS and Linux, follow the [official documentation](http://www.python.org/getit/windows/) to install on Windows.
- PyQt4 - Please refer to official documentation for installing into your system.
- [PyQt4](http://www.riverbankcomputing.co.uk/software/pyqt/download) - Please refer to official documentation for installing into your system.
- [Pillow](http://pypi.python.org/pypi/Pillow/) 2.2.1+ - For comic optimizations. Please refer to official documentation for installing into your system.
- **To build OS X release a modified QT is required:** [Patch](https://github.com/ciromattia/kcc/blob/master/other/QT-4.8.5-QListWidget.patch)
- [Psutil](https://code.google.com/p/psutil/) - Please refer to official documentation for installing into your system.
- **To build OS X release you need a modified QT:** [patch](https://github.com/ciromattia/kcc/blob/master/other/QT-4.8.5-QListWidget.patch)
## USAGE
@@ -51,11 +54,17 @@ You can find the latest released binary at the following links:
* Read tooltip of _High/Ultra quality_ option. There are many important informations there.
* When converting images smaller than device resolution remember to enable upscaling.
* Panel View (auto zooming every part of page) can be disabled directly on Kindle. There is no KCC option to do that.
* If you're converting color images and the end result is not satisfactory, experiment with gamma correction option (check 1.0 setting first).
* Check our [wiki](https://github.com/ciromattia/kcc/wiki/Other-devices) for a list of tested Non-Kindle E-Readers.
* The first image found will be set as the comic's cover.
* All files/directories will be added to EPUB in alphabetical order.
* Output MOBI file should be uploaded via USB. Other methods might corrupt it.
* Using high/ultra quality output option with Kindle Fire HD/HDX in most cases is just waste of space.
* ComicRack metadata will be parsed only if they are saved in *ComicInfo.xml* file.
### Calibre:
* Calibre can be used to upload files created by KCC.
* Uploading KCC output with Calibre will remove *Personal* tag from cover.
* **Don't convert files created by KCC with Calibre!** Any conversion process will corrupt the file!
* Don't use Calibre reader to preview files created by KCC. It can't parse them correctly.
### GUI
@@ -147,30 +156,30 @@ The app relies and includes the following scripts/binaries:
* [Kindle Fire HDX 8.9"](http://kcc.vulturis.eu/Samples/Ubunchu!-KFHDX8.mobi)
## CHANGELOG
####1.00
####1.0
* Initial version
####1.10
####1.1
* Added support for CBZ/CBR files in comic2ebook.py
####1.11
* Added support for CBZ/CBR files in KindleComicConverter
####1.1.1
* Added support for CBZ/CBR files in Kindle Comic Converter
####1.20
####1.2
* Comic optimizations! Split pages not target-oriented (landscape with portrait target or portrait with landscape target), add palette and other image optimizations from Mangle. WARNING: PIL is required for all image mangling!
####1.30
####1.3
* Fixed an issue in OPF generation for device resolution
* Reworked options system (call with -h option to get the inline help)
####1.40
####1.4
* Added some options for controlling image optimization
* Further optimization (ImageOps, page numbering cut, autocontrast)
####1.41
####1.4.1
* Fixed a serious bug on resizing when img ratio was bigger than device one
####1.50
####1.5
* Added subfolder support for multiple chapters.
####2.0
@@ -179,7 +188,7 @@ The app relies and includes the following scripts/binaries:
####2.1
* Added basic error reporting
#### 2.2:
####2.2:
* Added (valid!) ePub 2.0 output
* Rename .zip files to .cbz to avoid overwriting
@@ -284,6 +293,17 @@ The app relies and includes the following scripts/binaries:
* Improved multiprocessing speed
* GUI tweaks and minor bug fixes
####3.6:
* Increased quality of Panel View zoom
* Creation of multipart MOBI output is now faster on machines with 4GB+ RAM
* Automatic gamma correction now distinguishes color and grayscale images
* Added ComicRack metadata parser
* Implemented new method to detect border color in non-webtoon comics
* Upscaling is now enabled by default for Kindle Fire HD/HDX
* Windows nad Linux releases now have tray icon
* Fixed Kindle Fire HDX 7" output
* Increased target resolution for Kindle DX/DXG CBZ output
## COPYRIGHT
Copyright (c) 2012-2013 Ciro Mattia Gonano and Paweł Jastrzębski.

View File

@@ -1,5 +1,5 @@
#define MyAppName "Kindle Comic Converter"
#define MyAppVersion "3.5"
#define MyAppVersion "3.6"
#define MyAppPublisher "Ciro Mattia Gonano, Paweł Jastrzębski"
#define MyAppURL "http://kcc.vulturis.eu/"
#define MyAppExeName "KCC.exe"
@@ -12,6 +12,7 @@ AppPublisher={#MyAppPublisher}
AppPublisherURL={#MyAppURL}
AppSupportURL={#MyAppURL}
AppUpdatesURL={#MyAppURL}
AppCopyright=Copyright (C) 2012-2013 Ciro Mattia Gonano and Paweł Jastrzębski
DefaultDirName={pf}\{#MyAppName}
DefaultGroupName={#MyAppName}
AllowNoIcons=yes
@@ -28,6 +29,7 @@ UninstallDisplayName={#MyAppName}
UninstallDisplayIcon={app}\{#MyAppExeName}
ChangesAssociations=True
InfoAfterFile=other\InstallWarning.rtf
SignTool=SignTool /d $q{#MyAppName}$q /du $q{#MyAppURL}$q $f
[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"
@@ -63,6 +65,7 @@ Source: "build\exe.win-amd64-2.7\select.pyd"; DestDir: "{app}"; Flags: ignorever
Source: "build\exe.win-amd64-2.7\sip.pyd"; DestDir: "{app}"; Flags: ignoreversion; Check: Is64BitInstallMode
Source: "build\exe.win-amd64-2.7\SSLEAY32.dll"; DestDir: "{app}"; Flags: ignoreversion; Check: Is64BitInstallMode
Source: "build\exe.win-amd64-2.7\unicodedata.pyd"; DestDir: "{app}"; Flags: ignoreversion; Check: Is64BitInstallMode
Source: "build\exe.win-amd64-2.7\_psutil_mswindows.pyd"; DestDir: "{app}"; Flags: ignoreversion; Check: Is64BitInstallMode
; x86 files
Source: "build\exe.win32-2.7\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion solidbreak; Check: not Is64BitInstallMode
Source: "build\exe.win32-2.7\_ctypes.pyd"; DestDir: "{app}"; Flags: ignoreversion; Check: not Is64BitInstallMode
@@ -87,6 +90,7 @@ Source: "build\exe.win32-2.7\select.pyd"; DestDir: "{app}"; Flags: ignoreversion
Source: "build\exe.win32-2.7\sip.pyd"; DestDir: "{app}"; Flags: ignoreversion; Check: not Is64BitInstallMode
Source: "build\exe.win32-2.7\SSLEAY32.dll"; DestDir: "{app}"; Flags: ignoreversion; Check: not Is64BitInstallMode
Source: "build\exe.win32-2.7\unicodedata.pyd"; DestDir: "{app}"; Flags: ignoreversion; Check: not Is64BitInstallMode
Source: "build\exe.win32-2.7\_psutil_mswindows.pyd"; DestDir: "{app}"; Flags: ignoreversion; Check: not Is64BitInstallMode
; Common files
Source: "LICENSE.txt"; DestDir: "{app}"; Flags: ignoreversion solidbreak
Source: "other\Additional-LICENSE.txt"; DestDir: "{app}"; Flags: ignoreversion

45
kcc.py
View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
#
# Copyright (c) 2012-2013 Ciro Mattia Gonano <ciromattia@gmail.com>
@@ -18,7 +18,7 @@
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
__version__ = '3.5'
__version__ = '3.6'
__license__ = 'ISC'
__copyright__ = '2012-2013, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@vulturis.eu>'
__docformat__ = 'restructuredtext en'
@@ -30,22 +30,28 @@ try:
from PyQt4 import QtCore, QtGui, QtNetwork
except ImportError:
print "ERROR: PyQT4 is not installed!"
if sys.platform.startswith('linux'):
import Tkinter
import tkMessageBox
importRoot = Tkinter.Tk()
importRoot.withdraw()
tkMessageBox.showerror("KCC - Error", "PyQT4 is not installed!")
exit(1)
from kcc import KCC_gui
from multiprocessing import freeze_support
# OS specific PATH variable workarounds
if sys.platform.startswith('darwin'):
# Workaround Finder-launched app PATH evaluation
os.environ['PATH'] = '/usr/local/bin:' + os.environ['PATH']
from kcc import KCC_ui_osx as KCC_ui
elif sys.platform.startswith('linux'):
from kcc import KCC_ui_linux as KCC_ui
else:
# Workaround for Windows file association mechanism
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']
elif sys.platform.startswith('win'):
if getattr(sys, 'frozen', False):
os.chdir(os.path.dirname(os.path.abspath(sys.executable)))
else:
os.environ['PATH'] = os.path.dirname(os.path.abspath(__file__)) + '/other/;' + os.environ['PATH']
os.chdir(os.path.dirname(os.path.abspath(__file__)))
from kcc import KCC_ui
# Implementing detection of already running KCC instance and forwarding argv to it
@@ -89,10 +95,10 @@ class QApplicationMessaging(QtGui.QApplication):
return False
freeze_support()
APP = QApplicationMessaging(sys.argv)
if APP.isRunning():
KCCAplication = QApplicationMessaging(sys.argv)
if KCCAplication.isRunning():
if len(sys.argv) > 1:
APP.sendMessage(sys.argv[1].decode(sys.getfilesystemencoding()))
KCCAplication.sendMessage(sys.argv[1].decode(sys.getfilesystemencoding()))
sys.exit(0)
else:
messageBox = QtGui.QMessageBox()
@@ -101,13 +107,8 @@ if APP.isRunning():
messageBox.setWindowIcon(icon)
QtGui.QMessageBox.critical(messageBox, 'KCC - Error', 'KCC is already running!', QtGui.QMessageBox.Ok)
sys.exit(1)
KCC = QtGui.QMainWindow()
UI = KCC_ui.Ui_KCC()
UI.setupUi(KCC)
GUI = KCC_gui.Ui_KCC(UI, KCC, APP)
KCC.setWindowTitle("Kindle Comic Converter " + __version__)
KCC.show()
KCC.raise_()
KCCWindow = QtGui.QMainWindow()
KCCUI = KCC_gui.KCCGUI(KCCAplication, KCCWindow)
if len(sys.argv) > 1:
GUI.handleMessage(sys.argv[1].decode(sys.getfilesystemencoding()))
sys.exit(APP.exec_())
KCCUI.handleMessage(sys.argv[1].decode(sys.getfilesystemencoding()))
sys.exit(KCCAplication.exec_())

View File

@@ -1,4 +1,3 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright (c) 2012-2013 Ciro Mattia Gonano <ciromattia@gmail.com>
@@ -18,29 +17,47 @@
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
__version__ = '3.5'
__version__ = '3.6'
__license__ = 'ISC'
__copyright__ = '2012-2013, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@vulturis.eu>'
__docformat__ = 'restructuredtext en'
import os
import sys
import shutil
import traceback
import urllib2
import time
import socket
import comic2ebook
import kindlesplit
import socket
import string
from KCC_rc_web import WebContent
from string import split
from time import sleep
from shutil import move
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
from SocketServer import ThreadingMixIn
from image import ProfileData
from subprocess import call, Popen, STDOUT, PIPE
from subprocess import STDOUT, PIPE
from PyQt4 import QtGui, QtCore
from xml.dom.minidom import parse
from HTMLParser import HTMLParser
from KCC_rc_web import WebContent
try:
#noinspection PyUnresolvedReferences
from psutil import TOTAL_PHYMEM, Popen
except ImportError:
print "ERROR: Psutil is not installed!"
if sys.platform.startswith('linux'):
import Tkinter
import tkMessageBox
importRoot = Tkinter.Tk()
importRoot.withdraw()
tkMessageBox.showerror("KCC - Error", "Psutil is not installed!")
exit(1)
if sys.platform.startswith('darwin'):
import KCC_ui_osx as KCC_ui
elif sys.platform.startswith('linux'):
import KCC_ui_linux as KCC_ui
else:
import KCC_ui
class Icons:
@@ -64,6 +81,9 @@ class Icons:
self.error = QtGui.QIcon()
self.error.addPixmap(QtGui.QPixmap(":/Status/icons/error.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.programIcon = QtGui.QIcon()
self.programIcon.addPixmap(QtGui.QPixmap(":/Icon/icons/comic2ebook.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
class HTMLStripper(HTMLParser):
def __init__(self):
@@ -102,16 +122,16 @@ class WebServerHandler(BaseHTTPRequestHandler):
self.wfile.write('<!DOCTYPE html>\n'
'<html lang="en">\n'
'<head><meta charset="utf-8">\n'
'<link href="' + GUIMain.webContent.favicon + '" rel="icon" type="image/x-icon" />\n'
'<title>KindleComicConverter</title>\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="' + GUIMain.webContent.logo + '" /> -</p>\n')
if len(GUIMain.completedWork) > 0 and not GUIMain.conversionAlive:
for key in sorted(GUIMain.completedWork.iterkeys()):
self.wfile.write('<p><a href="' + key + '">' + string.split(key, '.')[0] + '</a></p>\n')
'alt="KCC Logo" src="' + GUI.webContent.logo + '" /> -</p>\n')
if len(GUI.completedWork) > 0 and not GUI.conversionAlive:
for key in sorted(GUI.completedWork.iterkeys()):
self.wfile.write('<p><a href="' + key + '">' + split(key, '.')[0] + '</a></p>\n')
else:
self.wfile.write('<p style="font-weight: bold">No downloads are available.<br/>'
'Convert some files and refresh this page.</p>\n')
@@ -119,7 +139,7 @@ class WebServerHandler(BaseHTTPRequestHandler):
'</body>\n'
'</html>\n')
elif sendReply:
outputFile = GUIMain.completedWork[urllib2.unquote(self.path[1:])].decode('utf-8')
outputFile = GUI.completedWork[urllib2.unquote(self.path[1:])].decode('utf-8')
fp = open(outputFile, 'rb')
self.send_response(200)
self.send_header('Content-type', mimetype)
@@ -194,27 +214,133 @@ class VersionThread(QtCore.QThread):
'Changelog</a>)', 'warning')
# noinspection PyBroadException
class WorkerThread(QtCore.QThread):
class ProgressThread(QtCore.QThread):
def __init__(self):
QtCore.QThread.__init__(self)
self.running = False
self.content = None
self.progress = 0
def __del__(self):
self.wait()
def run(self):
self.running = True
while self.running:
sleep(1)
if self.content:
self.emit(QtCore.SIGNAL("addMessage"), self.content + self.progress * '.', 'info', True)
self.progress += 1
if self.progress == 4:
self.progress = 0
def stop(self):
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.encode(sys.getfilesystemencoding()) + '"',
stdout=PIPE, stderr=STDOUT, shell=True)
for line in output.stdout:
# 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 StandardError:
# ERROR: Unknown generic error
kindlegenErrorCode = 1
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 StandardError:
self.signals.result.emit([False])
class WorkerThread(QtCore.QThread):
#noinspection PyArgumentList
def __init__(self):
QtCore.QThread.__init__(self)
self.pool = QtCore.QThreadPool()
self.conversionAlive = False
self.errors = False
self.kindlegenErrorCode = 0
self.kindlegenError = None
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()
def __del__(self):
self.wait()
def sync(self):
self.conversionAlive = GUIMain.conversionAlive
self.conversionAlive = GUI.conversionAlive
def clean(self):
GUIMain.needClean = True
GUI.progress.content = ''
GUI.progress.stop()
GUI.needClean = True
self.emit(QtCore.SIGNAL("hideProgressBar"))
self.emit(QtCore.SIGNAL("addMessage"), '<b>Conversion interrupted.</b>', 'error')
self.emit(QtCore.SIGNAL("addTrayMessage"), 'Conversion interrupted.', 'Critical')
self.emit(QtCore.SIGNAL("modeConvert"), True)
def addResult(self, output):
self.emit(QtCore.SIGNAL("progressBarTick"))
self.workerOutput.append(output)
def run(self):
self.emit(QtCore.SIGNAL("modeConvert"), False)
profile = ProfileData.ProfileLabels[str(GUI.DeviceBox.currentText())]
@@ -228,7 +354,10 @@ class WorkerThread(QtCore.QThread):
argv.append("--quality=1")
elif GUI.QualityBox.checkState() == 2:
argv.append("--quality=2")
if GUIMain.currentMode > 1:
if GUI.currentMode == 1:
if profile in ['KFHD', 'KFHD8', 'KFHDX', 'KFHDX8']:
argv.append("--upscale")
if GUI.currentMode > 1:
if GUI.ProcessingBox.isChecked():
argv.append("--noprocessing")
if GUI.NoRotateBox.isChecked():
@@ -245,13 +374,14 @@ class WorkerThread(QtCore.QThread):
argv.append("--forcepng")
if GUI.WebtoonBox.isChecked():
argv.append("--webtoon")
if float(GUIMain.GammaValue) > 0.09:
argv.append("--gamma=" + GUIMain.GammaValue)
if float(GUI.GammaValue) > 0.09:
# noinspection PyTypeChecker
argv.append("--gamma=" + GUI.GammaValue)
if str(GUI.FormatBox.currentText()) == 'CBZ':
argv.append("--cbz-output")
if str(GUI.FormatBox.currentText()) == 'MOBI':
argv.append("--batchsplit")
if GUIMain.currentMode > 2:
if GUI.currentMode > 2:
argv.append("--customwidth=" + str(GUI.customWidth.text()))
argv.append("--customheight=" + str(GUI.customHeight.text()))
if GUI.ColorBox.isChecked():
@@ -262,16 +392,18 @@ class WorkerThread(QtCore.QThread):
currentJobs.append(unicode(GUI.JobList.item(i).text()))
GUI.JobList.clear()
for job in currentJobs:
time.sleep(0.5)
sleep(0.5)
if not self.conversionAlive:
self.clean()
return
self.errors = False
self.emit(QtCore.SIGNAL("addMessage"), '<b>Source:</b> ' + job, 'info')
if str(GUI.FormatBox.currentText()) == 'CBZ':
self.emit(QtCore.SIGNAL("addMessage"), 'Creating CBZ file...', 'info')
self.emit(QtCore.SIGNAL("addMessage"), 'Creating CBZ files', 'info')
GUI.progress.content = 'Creating CBZ files'
else:
self.emit(QtCore.SIGNAL("addMessage"), 'Creating EPUB file...', 'info')
self.emit(QtCore.SIGNAL("addMessage"), 'Creating EPUB files', 'info')
GUI.progress.content = 'Creating EPUB files'
jobargv = list(argv)
jobargv.append(job)
try:
@@ -282,15 +414,19 @@ class WorkerThread(QtCore.QThread):
self.clean()
return
else:
GUI.progress.content = ''
self.errors = True
self.emit(QtCore.SIGNAL("addMessage"), str(warn), 'warning')
self.emit(QtCore.SIGNAL("addMessage"), 'Failed to create output file!', 'warning')
self.emit(QtCore.SIGNAL("addTrayMessage"), 'Failed to create output file!', 'Critical')
except Exception as err:
GUI.progress.content = ''
self.errors = True
type_, value_, traceback_ = sys.exc_info()
self.emit(QtCore.SIGNAL("showDialog"), "Error during conversion %s:\n\n%s\n\nTraceback:\n%s"
% (jobargv[-1], str(err), traceback.format_tb(traceback_)))
self.emit(QtCore.SIGNAL("addMessage"), 'Failed to create EPUB!', 'error')
self.emit(QtCore.SIGNAL("addTrayMessage"), 'Failed to create EPUB!', 'Critical')
if not self.conversionAlive:
for item in outputPath:
if os.path.exists(item):
@@ -298,117 +434,133 @@ class WorkerThread(QtCore.QThread):
self.clean()
return
if not self.errors:
GUI.progress.content = ''
if str(GUI.FormatBox.currentText()) == 'CBZ':
self.emit(QtCore.SIGNAL("addMessage"), 'Creating CBZ file... <b>Done!</b>', 'info', True)
self.emit(QtCore.SIGNAL("addMessage"), 'Creating CBZ files... <b>Done!</b>', 'info', True)
else:
self.emit(QtCore.SIGNAL("addMessage"), 'Creating EPUB file... <b>Done!</b>', 'info', True)
self.emit(QtCore.SIGNAL("addMessage"), 'Creating EPUB files... <b>Done!</b>', 'info', True)
if str(GUI.FormatBox.currentText()) == 'MOBI':
tomeNumber = 0
self.emit(QtCore.SIGNAL("progressBarTick"), 'status', 'Creating MOBI files')
self.emit(QtCore.SIGNAL("progressBarTick"), len(outputPath)*2)
self.emit(QtCore.SIGNAL("progressBarTick"), len(outputPath)*2+1)
self.emit(QtCore.SIGNAL("progressBarTick"))
self.emit(QtCore.SIGNAL("addMessage"), 'Creating MOBI files', 'info')
GUI.progress.content = 'Creating MOBI files'
self.workerOutput = []
# Number of KindleGen threads depends on the size of RAM
self.pool.setMaxThreadCount(self.threadNumber)
for item in outputPath:
tomeNumber += 1
if len(outputPath) > 1:
self.emit(QtCore.SIGNAL("addMessage"), 'Creating MOBI file (' + str(tomeNumber)
+ '/' + str(len(outputPath)) + ')...', 'info')
else:
self.emit(QtCore.SIGNAL("addMessage"), 'Creating MOBI file...', 'info')
self.emit(QtCore.SIGNAL("progressBarTick"))
try:
self.kindlegenErrorCode = 0
if os.path.getsize(item) < 367001600:
output = Popen('kindlegen -locale en "' + item.encode(sys.getfilesystemencoding()) +
'"', stdout=PIPE, stderr=STDOUT, shell=True)
for line in output.stdout:
# ERROR: Generic error
if "Error(" in line:
self.kindlegenErrorCode = 1
self.kindlegenError = line
# ERROR: EPUB too big
if ":E23026:" in line:
self.kindlegenErrorCode = 23026
if self.kindlegenErrorCode > 0:
break
else:
# ERROR: EPUB too big
self.kindlegenErrorCode = 23026
except:
# ERROR: Unknown generic error
self.kindlegenErrorCode = 1
continue
if not self.conversionAlive:
for item in outputPath:
if os.path.exists(item):
os.remove(item)
if os.path.exists(item.replace('.epub', '.mobi')):
os.remove(item.replace('.epub', '.mobi'))
self.clean()
return
if self.kindlegenErrorCode == 0:
if len(outputPath) > 1:
self.emit(QtCore.SIGNAL("addMessage"), 'Creating MOBI file (' + str(tomeNumber) + '/'
+ str(len(outputPath)) + ')... <b>Done!</b>',
'info', True)
else:
self.emit(QtCore.SIGNAL("addMessage"), 'Creating MOBI file... <b>Done!</b>', 'info',
True)
self.emit(QtCore.SIGNAL("addMessage"), 'Cleaning MOBI file...', 'info')
self.emit(QtCore.SIGNAL("progressBarTick"))
os.remove(item)
mobiPath = item.replace('.epub', '.mobi')
shutil.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', 'KFHDX8', 'KFA']:
newKindle = True
else:
newKindle = False
mobisplit = kindlesplit.mobi_split(mobiPath + '_toclean', newKindle)
open(mobiPath, 'wb').write(mobisplit.getResult())
except Exception:
self.errors = True
if not self.errors:
os.remove(mobiPath + '_toclean')
self.emit(QtCore.SIGNAL("addMessage"), 'Cleaning MOBI file... <b>Done!</b>', 'info',
True)
GUIMain.completedWork[os.path.basename(mobiPath).encode('utf-8')] = \
mobiPath.encode('utf-8')
else:
os.remove(mobiPath + '_toclean')
os.remove(mobiPath)
self.emit(QtCore.SIGNAL("addMessage"),
'KindleUnpack failed to clean MOBI file!', 'error')
else:
epubSize = (os.path.getsize(item))/1024/1024
os.remove(item)
worker = KindleGenThread(item)
worker.signals.result.connect(self.addResult)
self.pool.start(worker)
self.pool.waitForDone()
sleep(0.5)
self.kindlegenErrorCode = [0]
for errors in self.workerOutput:
if errors[0] != 0:
self.kindlegenErrorCode = errors
break
if not self.conversionAlive:
for item in outputPath:
if os.path.exists(item):
os.remove(item)
if os.path.exists(item.replace('.epub', '.mobi')):
os.remove(item.replace('.epub', '.mobi'))
self.emit(QtCore.SIGNAL("addMessage"), 'KindleGen failed to create MOBI!', 'error')
if self.kindlegenErrorCode == 1 and self.kindlegenError:
self.emit(QtCore.SIGNAL("showDialog"), "KindleGen error:\n\n" + self.kindlegenError)
if self.kindlegenErrorCode == 23026:
self.emit(QtCore.SIGNAL("addMessage"), 'Created EPUB file was too big.',
'error')
self.emit(QtCore.SIGNAL("addMessage"), 'EPUB file: ' + str(epubSize) + 'MB.'
' Supported size: ~300MB.', 'error')
self.clean()
return
if self.kindlegenErrorCode[0] == 0:
GUI.progress.content = ''
self.emit(QtCore.SIGNAL("addMessage"), 'Creating MOBI files... <b>Done!</b>', 'info', True)
self.emit(QtCore.SIGNAL("addMessage"), 'Cleaning MOBI files', 'info')
GUI.progress.content = 'Cleaning 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)
for success in self.workerOutput:
if not success:
self.errors = True
break
if not self.errors:
for item in outputPath:
GUI.progress.content = ''
mobiPath = item.replace('.epub', '.mobi')
os.remove(mobiPath + '_toclean')
GUI.completedWork[os.path.basename(mobiPath).encode('utf-8')] = \
mobiPath.encode('utf-8')
self.emit(QtCore.SIGNAL("addMessage"), 'Cleaning MOBI files... <b>Done!</b>', 'info',
True)
else:
GUI.progress.content = ''
for item in outputPath:
mobiPath = item.replace('.epub', '.mobi')
if os.path.exists(mobiPath):
os.remove(mobiPath)
if os.path.exists(mobiPath + '_toclean'):
os.remove(mobiPath + '_toclean')
self.emit(QtCore.SIGNAL("addMessage"), 'KindleUnpack failed to clean MOBI file!', 'error')
self.emit(QtCore.SIGNAL("addTrayMessage"), 'KindleUnpack failed to clean MOBI file!',
'Critical')
else:
GUI.progress.content = ''
epubSize = (os.path.getsize(self.kindlegenErrorCode[2]))/1024/1024
for item in outputPath:
if os.path.exists(item):
os.remove(item)
if os.path.exists(item.replace('.epub', '.mobi')):
os.remove(item.replace('.epub', '.mobi'))
self.emit(QtCore.SIGNAL("addMessage"), 'KindleGen failed to create MOBI!', 'error')
self.emit(QtCore.SIGNAL("addTrayMessage"), 'KindleGen failed to create MOBI!', 'Critical')
if self.kindlegenErrorCode[0] == 1 and self.kindlegenErrorCode[1] != '':
self.emit(QtCore.SIGNAL("showDialog"), "KindleGen error:\n\n" +
self.self.kindlegenErrorCode[1])
if self.kindlegenErrorCode[0] == 23026:
self.emit(QtCore.SIGNAL("addMessage"), 'Created EPUB file was too big.',
'error')
self.emit(QtCore.SIGNAL("addMessage"), 'EPUB file: ' + str(epubSize) + 'MB.'
' Supported size: ~300MB.', 'error')
else:
for item in outputPath:
GUIMain.completedWork[os.path.basename(item).encode('utf-8')] = item.encode('utf-8')
GUI.completedWork[os.path.basename(item).encode('utf-8')] = item.encode('utf-8')
GUI.progress.content = ''
GUI.progress.stop()
self.emit(QtCore.SIGNAL("hideProgressBar"))
GUIMain.needClean = True
GUI.needClean = True
self.emit(QtCore.SIGNAL("addMessage"), '<b>All jobs completed.</b>', 'info')
self.emit(QtCore.SIGNAL("addTrayMessage"), 'All jobs completed.', 'Information')
self.emit(QtCore.SIGNAL("modeConvert"), True)
# noinspection PyBroadException
class Ui_KCC(object):
class SystemTrayIcon(QtGui.QSystemTrayIcon):
def __init__(self):
if not sys.platform.startswith('darwin') and self.isSystemTrayAvailable():
QtGui.QSystemTrayIcon.__init__(self, GUI.icons.programIcon, MW)
self.activated.connect(self.catchClicks)
def catchClicks(self):
MW.showNormal()
MW.raise_()
MW.activateWindow()
def addTrayMessage(self, message, icon):
if not sys.platform.startswith('darwin'):
icon = eval('QtGui.QSystemTrayIcon.' + icon)
if self.supportsMessages() and not MW.isActiveWindow():
self.showMessage('Kindle Comic Converter', message, icon)
class KCCGUI(KCC_ui.Ui_KCC):
def selectDir(self):
if self.needClean:
self.needClean = False
GUI.JobList.clear()
# Dirty, dirty way but OS native QFileDialogs don't support directory multiselect
dirDialog = QtGui.QFileDialog(MainWindow, 'Select directory', self.lastPath)
dirDialog = QtGui.QFileDialog(MW, 'Select directory', self.lastPath)
dirDialog.setFileMode(dirDialog.Directory)
dirDialog.setOption(dirDialog.ShowDirsOnly, True)
dirDialog.setOption(dirDialog.DontUseNativeDialog, True)
@@ -428,6 +580,7 @@ class Ui_KCC(object):
dname = dname.replace('/', '\\')
self.lastPath = os.path.abspath(os.path.join(unicode(dname), os.pardir))
GUI.JobList.addItem(dname)
MW.setFocus()
def selectFile(self):
if self.needClean:
@@ -435,17 +588,17 @@ class Ui_KCC(object):
GUI.JobList.clear()
if self.UnRAR:
if self.sevenza:
fnames = QtGui.QFileDialog.getOpenFileNames(MainWindow, 'Select file', self.lastPath,
fnames = QtGui.QFileDialog.getOpenFileNames(MW, 'Select file', self.lastPath,
'*.cbz *.cbr *.cb7 *.zip *.rar *.7z *.pdf')
else:
fnames = QtGui.QFileDialog.getOpenFileNames(MainWindow, 'Select file', self.lastPath,
fnames = QtGui.QFileDialog.getOpenFileNames(MW, 'Select file', self.lastPath,
'*.cbz *.cbr *.zip *.rar *.pdf')
else:
if self.sevenza:
fnames = QtGui.QFileDialog.getOpenFileNames(MainWindow, 'Select file', self.lastPath,
fnames = QtGui.QFileDialog.getOpenFileNames(MW, 'Select file', self.lastPath,
'*.cbz *.cb7 *.zip *.7z *.pdf')
else:
fnames = QtGui.QFileDialog.getOpenFileNames(MainWindow, 'Select file', self.lastPath,
fnames = QtGui.QFileDialog.getOpenFileNames(MW, 'Select file', self.lastPath,
'*.cbz *.zip *.pdf')
for fname in fnames:
if unicode(fname) != "":
@@ -457,9 +610,14 @@ class Ui_KCC(object):
def modeBasic(self):
self.currentMode = 1
MainWindow.setMinimumSize(QtCore.QSize(420, 270))
MainWindow.setMaximumSize(QtCore.QSize(420, 270))
MainWindow.resize(420, 270)
if sys.platform.startswith('darwin'):
MW.setMinimumSize(QtCore.QSize(420, 291))
MW.setMaximumSize(QtCore.QSize(420, 291))
MW.resize(420, 291)
else:
MW.setMinimumSize(QtCore.QSize(420, 287))
MW.setMaximumSize(QtCore.QSize(420, 287))
MW.resize(420, 287)
GUI.BasicModeButton.setStyleSheet('font-weight:Bold;')
GUI.AdvModeButton.setStyleSheet('font-weight:Normal;')
GUI.FormatBox.setCurrentIndex(0)
@@ -478,9 +636,9 @@ class Ui_KCC(object):
def modeAdvanced(self):
self.currentMode = 2
MainWindow.setMinimumSize(QtCore.QSize(420, 345))
MainWindow.setMaximumSize(QtCore.QSize(420, 345))
MainWindow.resize(420, 345)
MW.setMinimumSize(QtCore.QSize(420, 365))
MW.setMaximumSize(QtCore.QSize(420, 365))
MW.resize(420, 365)
GUI.BasicModeButton.setStyleSheet('font-weight:Normal;')
GUI.AdvModeButton.setStyleSheet('font-weight:Bold;')
GUI.FormatBox.setEnabled(True)
@@ -495,9 +653,9 @@ class Ui_KCC(object):
def modeExpert(self, KFA=False):
self.modeAdvanced()
self.currentMode = 3
MainWindow.setMinimumSize(QtCore.QSize(420, 380))
MainWindow.setMaximumSize(QtCore.QSize(420, 380))
MainWindow.resize(420, 380)
MW.setMinimumSize(QtCore.QSize(420, 397))
MW.setMaximumSize(QtCore.QSize(420, 397))
MW.resize(420, 397)
GUI.OptionsExpert.setEnabled(True)
if KFA:
GUI.ColorBox.setChecked(True)
@@ -561,7 +719,6 @@ class Ui_KCC(object):
GUI.QualityBox.setChecked(False)
GUI.MangaBox.setEnabled(False)
GUI.MangaBox.setChecked(False)
self.addMessage('If images will be too dark after conversion: Set <i>Gamma</i> to 1.0.', 'info')
else:
if not GUI.ProcessingBox.isChecked():
GUI.NoRotateBox.setEnabled(True)
@@ -634,9 +791,12 @@ class Ui_KCC(object):
GUI.QualityBox.setChecked(False)
GUI.QualityBox.setEnabled(False)
self.QualityBoxDisabled = True
if value in [4, 5, 6, 7]:
if GUI.UpscaleBox.isEnabled():
GUI.UpscaleBox.setChecked(True)
else:
if not GUI.WebtoonBox.isChecked() and not GUI.ProcessingBox.isChecked() \
and str(GUI.FormatBox.currentText()) != 'CBZ':
and str(GUI.FormatBox.currentText()) != 'CBZ' and value not in [9, 11, 12, 13]:
GUI.QualityBox.setEnabled(True)
self.QualityBoxDisabled = False
@@ -675,7 +835,7 @@ class Ui_KCC(object):
GUI.JobList.scrollToBottom()
def showDialog(self, message):
QtGui.QMessageBox.critical(MainWindow, 'KCC - Error', message, QtGui.QMessageBox.Ok)
QtGui.QMessageBox.critical(MW, 'KCC - Error', message, QtGui.QMessageBox.Ok)
def updateProgressbar(self, new=False, status=False):
if new == "status":
@@ -694,6 +854,7 @@ class Ui_KCC(object):
self.conversionAlive = False
self.worker.sync()
else:
self.progress.start()
if self.needClean:
self.needClean = False
GUI.JobList.clear()
@@ -743,8 +904,8 @@ class Ui_KCC(object):
self.settings.sync()
def handleMessage(self, message):
MainWindow.raise_()
MainWindow.activateWindow()
MW.raise_()
MW.activateWindow()
if not self.conversionAlive:
if self.needClean:
self.needClean = False
@@ -768,16 +929,18 @@ class Ui_KCC(object):
else:
self.addMessage('This file type is unsupported!', 'error')
def __init__(self, UI, KCC, APP):
global GUI, GUIMain, MainWindow
GUI = UI
GUIMain = self
MainWindow = KCC
def __init__(self, KCCAplication, KCCWindow):
global APP, MW, GUI
APP = KCCAplication
MW = KCCWindow
GUI = self
self.setupUi(MW)
# User settings will be reverted to default ones if were created in one of the following versions
# Empty string cover all versions before this system was implemented
purgeSettingsVersions = ['']
self.icons = Icons()
self.webContent = WebContent()
self.tray = SystemTrayIcon()
self.settings = QtCore.QSettings('KindleComicConverter', 'KindleComicConverter')
self.settingsVersion = self.settings.value('settingsVersion', '', type=str)
if self.settingsVersion in purgeSettingsVersions:
@@ -793,6 +956,7 @@ class Ui_KCC(object):
self.worker = WorkerThread()
self.versionCheck = VersionThread()
self.contentServer = WebServerThread()
self.progress = ProgressThread()
self.conversionAlive = False
self.needClean = True
self.QualityBoxDisabled = False
@@ -800,17 +964,38 @@ class Ui_KCC(object):
self.completedWork = {}
if sys.platform.startswith('darwin'):
self.listFontSize = 11
self.statusBarFontSize = 10
self.statusBarStyle = 'QLabel{padding-top:5px;padding-bottom:5px;border-top:2px solid #C2C7CB}'
elif sys.platform.startswith('linux'):
self.listFontSize = 8
self.statusBarFontSize = 8
self.statusBarStyle = 'QLabel{padding-top:5px;padding-bottom:3px;border-top:2px solid #C2C7CB}'
self.tray.show()
else:
self.listFontSize = 9
self.statusBarFontSize = 8
self.statusBarStyle = 'QLabel{padding-top:3px;padding-bottom:3px;border-top:2px solid #C2C7CB}'
self.tray.show()
statusBarLabel = QtGui.QLabel('<b><a href="http://kcc.vulturis.eu/">HOMEPAGE</a> - <a href="https://github.com/'
'ciromattia/kcc/blob/master/README.md#issues--new-features--donations">DONATE</a>'
' - <a href="https://github.com/ciromattia/kcc/blob/master/README.md#kcc">README<'
'/a> - <a href="https://github.com/ciromattia/kcc/wiki">WIKI</a></b>')
statusBarLabel.setAlignment(QtCore.Qt.AlignCenter)
statusBarLabel.setStyleSheet(self.statusBarStyle)
statusBarLabel.setOpenExternalLinks(True)
statusBarLabelFont = QtGui.QFont()
statusBarLabelFont.setPointSize(self.statusBarFontSize)
statusBarLabel.setFont(statusBarLabelFont)
GUI.statusBar.addPermanentWidget(statusBarLabel, 1)
self.addMessage('<b>Welcome!</b>', 'info')
self.addMessage('<b>Remember:</b> All options have additional informations in tooltips.', 'info')
if self.firstStart:
self.addMessage('Since you are using <b>KCC</b> for first time please see few '
'<a href="https://github.com/ciromattia/kcc#important-tips">important tips</a>.', 'info')
if call('kindlegen -locale en', stdout=PIPE, stderr=STDOUT, shell=True) == 0:
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)
@@ -828,14 +1013,16 @@ class Ui_KCC(object):
formats = ['EPUB', 'CBZ']
self.addMessage('Cannot find <a href="http://www.amazon.com/gp/feature.html?ie=UTF8&docId='
'1000765211">kindlegen</a> in PATH! MOBI creation will be disabled.', 'warning')
rarExitCode = call('unrar', stdout=PIPE, stderr=STDOUT, shell=True)
rarExitCode = Popen('unrar', stdout=PIPE, stderr=STDOUT, shell=True)
rarExitCode = rarExitCode.wait()
if rarExitCode == 0 or rarExitCode == 7:
self.UnRAR = True
else:
self.UnRAR = False
self.addMessage('Cannot find <a href="http://www.rarlab.com/rar_add.htm">UnRAR</a>!'
' Processing of CBR/RAR files will be disabled.', 'warning')
sevenzaExitCode = call('7za', stdout=PIPE, stderr=STDOUT, shell=True)
sevenzaExitCode = Popen('7za', stdout=PIPE, stderr=STDOUT, shell=True)
sevenzaExitCode = sevenzaExitCode.wait()
if sevenzaExitCode == 0 or sevenzaExitCode == 7:
self.sevenza = True
else:
@@ -856,14 +1043,16 @@ class Ui_KCC(object):
GUI.ProcessingBox.stateChanged.connect(self.toggleProcessingBox)
GUI.DeviceBox.activated.connect(self.changeDevice)
GUI.FormatBox.activated.connect(self.changeFormat)
KCC.connect(self.worker, QtCore.SIGNAL("progressBarTick"), self.updateProgressbar)
KCC.connect(self.worker, QtCore.SIGNAL("modeConvert"), self.modeConvert)
KCC.connect(self.worker, QtCore.SIGNAL("addMessage"), self.addMessage)
KCC.connect(self.worker, QtCore.SIGNAL("showDialog"), self.showDialog)
KCC.connect(self.worker, QtCore.SIGNAL("hideProgressBar"), self.hideProgressBar)
KCC.connect(self.versionCheck, QtCore.SIGNAL("addMessage"), self.addMessage)
KCC.connect(self.contentServer, QtCore.SIGNAL("addMessage"), self.addMessage)
KCC.closeEvent = self.saveSettings
MW.connect(self.worker, QtCore.SIGNAL("progressBarTick"), self.updateProgressbar)
MW.connect(self.worker, QtCore.SIGNAL("modeConvert"), self.modeConvert)
MW.connect(self.worker, QtCore.SIGNAL("addMessage"), self.addMessage)
MW.connect(self.worker, QtCore.SIGNAL("addTrayMessage"), self.tray.addTrayMessage)
MW.connect(self.worker, QtCore.SIGNAL("showDialog"), self.showDialog)
MW.connect(self.worker, QtCore.SIGNAL("hideProgressBar"), self.hideProgressBar)
MW.connect(self.versionCheck, QtCore.SIGNAL("addMessage"), self.addMessage)
MW.connect(self.contentServer, QtCore.SIGNAL("addMessage"), self.addMessage)
MW.connect(self.progress, QtCore.SIGNAL("addMessage"), self.addMessage)
MW.closeEvent = self.saveSettings
for f in formats:
GUI.FormatBox.addItem(eval('self.icons.' + f + 'Format'), f)
@@ -907,3 +1096,6 @@ class Ui_KCC(object):
self.contentServer.start()
self.hideProgressBar()
self.worker.sync()
MW.setWindowTitle("Kindle Comic Converter " + __version__)
MW.show()
MW.raise_()

View File

@@ -2,7 +2,7 @@
# Form implementation generated from reading ui file 'KCC.ui'
#
# Created: Sat Oct 12 11:28:00 2013
# Created: Wed Dec 04 18:28:13 2013
# by: PyQt4 UI code generator 4.10.3
#
# WARNING! All changes made in this file will be lost!
@@ -26,13 +26,12 @@ except AttributeError:
class Ui_KCC(object):
def setupUi(self, KCC):
KCC.setObjectName(_fromUtf8("KCC"))
KCC.resize(420, 380)
KCC.setMinimumSize(QtCore.QSize(420, 380))
KCC.setMaximumSize(QtCore.QSize(420, 380))
KCC.resize(420, 397)
KCC.setMinimumSize(QtCore.QSize(420, 397))
KCC.setMaximumSize(QtCore.QSize(420, 397))
font = QtGui.QFont()
font.setPointSize(9)
KCC.setFont(font)
KCC.setFocusPolicy(QtCore.Qt.NoFocus)
icon = QtGui.QIcon()
icon.addPixmap(QtGui.QPixmap(_fromUtf8(":/Icon/icons/comic2ebook.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
KCC.setWindowIcon(icon)
@@ -247,6 +246,14 @@ class Ui_KCC(object):
self.customHeight.setObjectName(_fromUtf8("customHeight"))
self.gridLayout_2.addWidget(self.customHeight, 0, 3, 1, 1)
KCC.setCentralWidget(self.Form)
self.statusBar = QtGui.QStatusBar(KCC)
font = QtGui.QFont()
font.setFamily(_fromUtf8("MS Shell Dlg 2"))
font.setPointSize(8)
self.statusBar.setFont(font)
self.statusBar.setSizeGripEnabled(False)
self.statusBar.setObjectName(_fromUtf8("statusBar"))
KCC.setStatusBar(self.statusBar)
self.ActionBasic = QtGui.QAction(KCC)
self.ActionBasic.setCheckable(True)
self.ActionBasic.setChecked(False)
@@ -298,9 +305,7 @@ class Ui_KCC(object):
self.RotateBox.setText(_translate("KCC", "Horizontal mode", None))
self.BasicModeButton.setText(_translate("KCC", "Basic", None))
self.AdvModeButton.setText(_translate("KCC", "Advanced", None))
self.GammaLabel.setToolTip(_translate("KCC", "When converting color images setting this option to 1.0 MIGHT improve readability.", None))
self.GammaLabel.setText(_translate("KCC", "Gamma: Auto", None))
self.GammaSlider.setToolTip(_translate("KCC", "<html><head/><body><p>When converting color images setting this option to 1.0 <span style=\" font-weight:600;\">might</span> improve readability.</p></body></html>", None))
self.ColorBox.setToolTip(_translate("KCC", "Do not convert images to grayscale.", None))
self.ColorBox.setText(_translate("KCC", "Color mode", None))
self.wLabel.setToolTip(_translate("KCC", "Resolution of target device.", None))

View File

@@ -2,7 +2,7 @@
# Form implementation generated from reading ui file 'KCC-Linux.ui'
#
# Created: Sat Oct 12 11:28:11 2013
# Created: Wed Dec 04 18:27:52 2013
# by: PyQt4 UI code generator 4.10.3
#
# WARNING! All changes made in this file will be lost!
@@ -26,13 +26,12 @@ except AttributeError:
class Ui_KCC(object):
def setupUi(self, KCC):
KCC.setObjectName(_fromUtf8("KCC"))
KCC.resize(420, 380)
KCC.setMinimumSize(QtCore.QSize(420, 380))
KCC.setMaximumSize(QtCore.QSize(420, 380))
KCC.resize(420, 397)
KCC.setMinimumSize(QtCore.QSize(420, 397))
KCC.setMaximumSize(QtCore.QSize(420, 397))
font = QtGui.QFont()
font.setPointSize(9)
KCC.setFont(font)
KCC.setFocusPolicy(QtCore.Qt.NoFocus)
icon = QtGui.QIcon()
icon.addPixmap(QtGui.QPixmap(_fromUtf8(":/Icon/icons/comic2ebook.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
KCC.setWindowIcon(icon)
@@ -317,6 +316,14 @@ class Ui_KCC(object):
self.customHeight.setObjectName(_fromUtf8("customHeight"))
self.gridLayout_2.addWidget(self.customHeight, 0, 3, 1, 1)
KCC.setCentralWidget(self.Form)
self.statusBar = QtGui.QStatusBar(KCC)
font = QtGui.QFont()
font.setFamily(_fromUtf8("DejaVu Sans"))
font.setPointSize(8)
self.statusBar.setFont(font)
self.statusBar.setSizeGripEnabled(False)
self.statusBar.setObjectName(_fromUtf8("statusBar"))
KCC.setStatusBar(self.statusBar)
self.ActionBasic = QtGui.QAction(KCC)
self.ActionBasic.setCheckable(True)
self.ActionBasic.setChecked(False)
@@ -367,9 +374,7 @@ class Ui_KCC(object):
self.RotateBox.setText(_translate("KCC", "Horizontal mode", None))
self.BasicModeButton.setText(_translate("KCC", "Basic", None))
self.AdvModeButton.setText(_translate("KCC", "Advanced", None))
self.GammaLabel.setToolTip(_translate("KCC", "<html><head/><body><p>When converting color images setting this option to 1.0 <span style=\" font-weight:600;\">might</span> improve readability.</p></body></html>", None))
self.GammaLabel.setText(_translate("KCC", "Gamma: Auto", None))
self.GammaSlider.setToolTip(_translate("KCC", "<html><head/><body><p>When converting color images setting this option to 1.0 <span style=\" font-weight:600;\">might</span> improve readability.</p></body></html>", None))
self.ColorBox.setToolTip(_translate("KCC", "Do not convert images to grayscale.", None))
self.ColorBox.setText(_translate("KCC", "Color mode", None))
self.wLabel.setToolTip(_translate("KCC", "Resolution of target device.", None))

View File

@@ -2,7 +2,7 @@
# Form implementation generated from reading ui file 'KCC-OSX.ui'
#
# Created: Sat Oct 12 11:28:19 2013
# Created: Wed Dec 04 18:28:04 2013
# by: PyQt4 UI code generator 4.10.3
#
# WARNING! All changes made in this file will be lost!
@@ -26,13 +26,12 @@ except AttributeError:
class Ui_KCC(object):
def setupUi(self, KCC):
KCC.setObjectName(_fromUtf8("KCC"))
KCC.resize(420, 380)
KCC.setMinimumSize(QtCore.QSize(420, 380))
KCC.setMaximumSize(QtCore.QSize(420, 380))
KCC.resize(420, 397)
KCC.setMinimumSize(QtCore.QSize(420, 397))
KCC.setMaximumSize(QtCore.QSize(420, 397))
font = QtGui.QFont()
font.setPointSize(9)
KCC.setFont(font)
KCC.setFocusPolicy(QtCore.Qt.NoFocus)
icon = QtGui.QIcon()
icon.addPixmap(QtGui.QPixmap(_fromUtf8(":/Icon/icons/comic2ebook.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
KCC.setWindowIcon(icon)
@@ -340,6 +339,14 @@ class Ui_KCC(object):
self.customHeight.setObjectName(_fromUtf8("customHeight"))
self.gridLayout_2.addWidget(self.customHeight, 0, 3, 1, 1)
KCC.setCentralWidget(self.Form)
self.statusBar = QtGui.QStatusBar(KCC)
font = QtGui.QFont()
font.setFamily(_fromUtf8("Aharoni"))
font.setPointSize(8)
self.statusBar.setFont(font)
self.statusBar.setSizeGripEnabled(False)
self.statusBar.setObjectName(_fromUtf8("statusBar"))
KCC.setStatusBar(self.statusBar)
self.ActionBasic = QtGui.QAction(KCC)
self.ActionBasic.setCheckable(True)
self.ActionBasic.setChecked(False)
@@ -385,9 +392,7 @@ class Ui_KCC(object):
self.RotateBox.setText(_translate("KCC", "Horizontal mode", None))
self.BasicModeButton.setText(_translate("KCC", "Basic", None))
self.AdvModeButton.setText(_translate("KCC", "Advanced", None))
self.GammaLabel.setToolTip(_translate("KCC", "<html><head/><body><p><span style=\" font-size:12pt;\">When converting color images setting this option to 1.0 </span><span style=\" font-size:12pt; font-weight:600;\">might</span><span style=\" font-size:12pt;\"> improve readability.</span></p></body></html>", None))
self.GammaLabel.setText(_translate("KCC", "Gamma: Auto", None))
self.GammaSlider.setToolTip(_translate("KCC", "<html><head/><body><p><span style=\" font-size:12pt;\">When converting color images setting this option to 1.0 </span><span style=\" font-size:12pt; font-weight:600;\">might</span><span style=\" font-size:12pt;\"> improve readability.</span></p></body></html>", None))
self.ColorBox.setToolTip(_translate("KCC", "<html><head/><body><p><span style=\" font-size:12pt;\">Do not convert images to grayscale.</span></p></body></html>", None))
self.ColorBox.setText(_translate("KCC", "Color mode", None))
self.wLabel.setToolTip(_translate("KCC", "<html><head/><body><p><span style=\" font-size:12pt;\">Resolution of target device.</span></p></body></html>", None))

View File

@@ -1,4 +1,4 @@
__version__ = '3.5'
__version__ = '3.6'
__license__ = 'ISC'
__copyright__ = '2012-2013, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@vulturis.eu>'
__docformat__ = 'restructuredtext en'

View File

@@ -23,7 +23,21 @@ import os
import zipfile
import rarfile
import locale
from subprocess import Popen, STDOUT, PIPE
from sys import platform
from subprocess import STDOUT, PIPE
try:
#noinspection PyUnresolvedReferences
from psutil import Popen
except ImportError:
print "ERROR: Psutil is not installed!"
if platform.startswith('linux'):
import Tkinter
import tkMessageBox
importRoot = Tkinter.Tk()
importRoot.withdraw()
tkMessageBox.showerror("KCC - Error", "Psutil is not installed!")
exit(1)
from shutil import move
class CBxArchive:
@@ -91,9 +105,10 @@ class CBxArchive:
elif self.compressor == '7z':
self.extractCB7(targetdir)
adir = os.listdir(targetdir)
if 'ComicInfo.xml' in adir:
adir.remove('ComicInfo.xml')
if len(adir) == 1 and os.path.isdir(os.path.join(targetdir, adir[0])):
import shutil
for f in os.listdir(os.path.join(targetdir, adir[0])):
shutil.move(os.path.join(targetdir, adir[0], f), targetdir)
move(os.path.join(targetdir, adir[0], f), targetdir)
os.rmdir(os.path.join(targetdir, adir[0]))
return targetdir

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
#
# Copyright (c) 2012-2013 Ciro Mattia Gonano <ciromattia@gmail.com>
@@ -18,20 +18,23 @@
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
#
__version__ = '3.5'
__version__ = '3.6'
__license__ = 'ISC'
__copyright__ = '2012-2013, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@vulturis.eu>'
__docformat__ = 'restructuredtext en'
import os
import sys
import tempfile
import re
import stat
import string
import unicodedata
from tempfile import mkdtemp
from shutil import move, copyfile, copytree, rmtree, make_archive
from optparse import OptionParser, OptionGroup
from multiprocessing import Pool, freeze_support
from xml.dom.minidom import parse
from uuid import uuid4
try:
from PyQt4 import QtCore
except ImportError:
@@ -126,35 +129,48 @@ def buildHTML(path, imgfile):
"}'></a></div>\n"])
if options.quality == 2:
imgfilepv = string.split(imgfile, ".")
imgfilepv[0] = imgfilepv[0].split("_kccx")[0].replace("_kccnh", "").replace("_kccnv", "")
imgfilepv[0] = imgfilepv[0].split("_kccxl")[0].replace("_kccnh", "").replace("_kccnv", "")
imgfilepv[0] += "_kcchq"
imgfilepv = string.join(imgfilepv, ".")
else:
imgfilepv = imgfile
if "_kccx" in filename[0]:
xy = string.split(filename[0], "_kccx")[1]
x = string.split(xy, "_kccy")[0].lstrip("0")
y = string.split(xy, "_kccy")[1].lstrip("0")
if x != "":
x = "-" + str(float(x)/100) + "%"
if "_kccxl" in filename[0]:
borders = filename[0].split('_kccxl')[1]
borders = re.findall('[0-9]{1,6}', borders)
xl = borders[0].lstrip("0")
yu = borders[1].lstrip("0")
xr = borders[2].lstrip("0")
yd = borders[3].lstrip("0")
if xl != "":
xl = "-" + str(float(xl)/100) + "%"
else:
x = "0%"
if y != "":
y = "-" + str(float(y)/100) + "%"
xl = "0%"
if xr != "":
xr = "-" + str(float(xr)/100) + "%"
else:
y = "0%"
xr = "0%"
if yu != "":
yu = "-" + str(float(yu)/100) + "%"
else:
yu = "0%"
if yd != "":
yd = "-" + str(float(yd)/100) + "%"
else:
yd = "0%"
else:
x = "0%"
y = "0%"
boxStyles = {"BoxTL": "left:" + x + ";top:" + y + ";",
"BoxTR": "right:" + x + ";top:" + y + ";",
"BoxBL": "left:" + x + ";bottom:" + y + ";",
"BoxBR": "right:" + x + ";bottom:" + y + ";",
"BoxT": "left:-25%;top:" + y + ";",
"BoxB": "left:-25%;bottom:" + y + ";",
"BoxL": "left:" + x + ";top:-25%;",
"BoxR": "right:" + x + ";top:-25%;",
"BoxC": "right:-25%;top:-25%;"
xl = "0%"
yu = "0%"
xr = "0%"
yd = "0%"
boxStyles = {"BoxTL": "left:" + xl + ";top:" + yu + ";",
"BoxTR": "right:" + xr + ";top:" + yu + ";",
"BoxBL": "left:" + xl + ";bottom:" + yd + ";",
"BoxBR": "right:" + xr + ";bottom:" + yd + ";",
"BoxT": "left:" + xl + ";top:" + yu + ";",
"BoxB": "left:" + xl + ";bottom:" + yd + ";",
"BoxL": "left:" + xl + ";top:" + yu + ";",
"BoxR": "right:" + xr + ";top:" + yu + ";",
"BoxC": "left:" + xl + ";top:" + yu + ";"
}
for box in boxes:
f.writelines(["<div id=\"" + box + "-Panel-Parent\" class=\"target-mag-parent\"><div id=\"",
@@ -168,7 +184,6 @@ def buildHTML(path, imgfile):
def buildNCX(dstdir, title, chapters):
from uuid import uuid4
options.uuid = str(uuid4())
options.uuid = options.uuid.encode('utf-8')
ncxfile = os.path.join(dstdir, 'OEBPS', 'toc.ncx')
@@ -216,9 +231,10 @@ def buildOPF(dstdir, title, filelist, cover=None):
"xmlns:dc=\"http://purl.org/dc/elements/1.1/\">\n",
"<dc:title>", title.encode('utf-8'), "</dc:title>\n",
"<dc:language>en-US</dc:language>\n",
"<dc:identifier id=\"BookID\" opf:scheme=\"UUID\">", options.uuid, "</dc:identifier>\n",
"<dc:Creator>KCC</dc:Creator>\n",
"<meta name=\"generator\" content=\"KindleComicConverter-" + __version__ + "\"/>\n",
"<dc:identifier id=\"BookID\" opf:scheme=\"UUID\">", options.uuid, "</dc:identifier>\n"])
for author in options.authors:
f.writelines(["<dc:Creator>", author.encode('utf-8'), "</dc:Creator>\n"])
f.writelines(["<meta name=\"generator\" content=\"KindleComicConverter-" + __version__ + "\"/>\n",
"<meta name=\"RegionMagnification\" content=\"true\"/>\n",
"<meta name=\"region-mag\" content=\"true\"/>\n",
"<meta name=\"cover\" content=\"cover\"/>\n",
@@ -292,17 +308,21 @@ def getImageFileName(imgfile):
return filename
def applyImgOptimization(img, opt, overrideQuality=5):
img.getImageFill(opt.webtoon)
def applyImgOptimization(img, opt, hqImage=None):
if not img.fill:
img.getImageFill(opt.webtoon)
if not opt.webtoon:
img.cropWhiteSpace(10.0)
if opt.cutpagenumbers and not opt.webtoon:
img.cutPageNumber()
img.optimizeImage(opt.gamma)
if overrideQuality != 5:
img.resizeImage(opt.upscale, opt.stretch, opt.bordersColor, overrideQuality)
if hqImage:
img.resizeImage(opt.upscale, opt.stretch, opt.bordersColor, 0)
img.calculateBorder(hqImage)
else:
img.resizeImage(opt.upscale, opt.stretch, opt.bordersColor, opt.quality)
if opt.panelview and opt.quality != 2:
img.calculateBorder(img)
if opt.forcepng and not opt.forcecolor:
img.quantizeImage()
@@ -374,21 +394,21 @@ def fileImgProcess(work):
applyImgOptimization(img1, opt)
img1.saveToDir(dirpath, opt.forcepng, opt.forcecolor, wipe)
if opt.quality == 2:
img3 = image.ComicPage(split[0], opt.profileData)
applyImgOptimization(img3, opt, 0)
img3.saveToDir(dirpath, opt.forcepng, opt.forcecolor, True)
img4 = image.ComicPage(split[1], opt.profileData)
applyImgOptimization(img4, opt, 0)
img4.saveToDir(dirpath, opt.forcepng, opt.forcecolor, True)
img0b = image.ComicPage(split[0], opt.profileData, img0.fill)
applyImgOptimization(img0b, opt, img0)
img0b.saveToDir(dirpath, opt.forcepng, opt.forcecolor, True)
img1b = image.ComicPage(split[1], opt.profileData, img1.fill)
applyImgOptimization(img1b, opt, img1)
img1b.saveToDir(dirpath, opt.forcepng, opt.forcecolor, True)
else:
applyImgOptimization(img, opt)
img.saveToDir(dirpath, opt.forcepng, opt.forcecolor, wipe)
if opt.quality == 2:
img2 = image.ComicPage(os.path.join(dirpath, afile), opt.profileData)
img2 = image.ComicPage(os.path.join(dirpath, afile), opt.profileData, img.fill)
if img.rotated:
img2.image = img2.image.rotate(90)
img2.rotated = True
applyImgOptimization(img2, opt, 0)
applyImgOptimization(img2, opt, img)
img2.saveToDir(dirpath, opt.forcepng, opt.forcecolor, True)
except StandardError:
return str(sys.exc_info()[1])
@@ -535,7 +555,7 @@ def getWorkFolder(afile):
if len(afile) > 240:
raise UserWarning("Path is too long.")
if os.path.isdir(afile):
workdir = tempfile.mkdtemp('', 'KCC-TMP-')
workdir = mkdtemp('', 'KCC-TMP-')
try:
os.rmdir(workdir) # needed for copytree() fails if dst already exists
fullPath = os.path.join(workdir, 'OEBPS', 'Images')
@@ -554,7 +574,7 @@ def getWorkFolder(afile):
rmtree(path, True)
raise UserWarning("Failed to extract images.")
else:
workdir = tempfile.mkdtemp('', 'KCC-TMP-')
workdir = mkdtemp('', 'KCC-TMP-')
cbx = cbxarchive.CBxArchive(afile)
if cbx.isCbxFile():
try:
@@ -573,9 +593,59 @@ def getWorkFolder(afile):
return path
def checkComicInfo(path, originalPath):
xmlPath = os.path.join(path, 'ComicInfo.xml')
options.authors = ['KCC']
titleSuffix = ''
if options.title == 'defaulttitle':
defaultTitle = True
if os.path.isdir(originalPath):
options.title = os.path.basename(originalPath)
else:
options.title = os.path.splitext(os.path.basename(originalPath))[0]
else:
defaultTitle = False
if os.path.exists(xmlPath):
try:
xml = parse(xmlPath)
except StandardError:
os.remove(xmlPath)
return
options.authors = []
if defaultTitle:
if len(xml.getElementsByTagName('Series')) != 0:
options.title = xml.getElementsByTagName('Series')[0].firstChild.nodeValue
if len(xml.getElementsByTagName('Volume')) != 0:
titleSuffix += ' V' + xml.getElementsByTagName('Volume')[0].firstChild.nodeValue
if len(xml.getElementsByTagName('Number')) != 0:
titleSuffix += ' #' + xml.getElementsByTagName('Number')[0].firstChild.nodeValue
options.title += titleSuffix
if len(xml.getElementsByTagName('Writer')) != 0:
authorsTemp = string.split(xml.getElementsByTagName('Writer')[0].firstChild.nodeValue, ', ')
for author in authorsTemp:
options.authors.append(author)
if len(xml.getElementsByTagName('Penciller')) != 0:
authorsTemp = string.split(xml.getElementsByTagName('Penciller')[0].firstChild.nodeValue, ', ')
for author in authorsTemp:
options.authors.append(author)
if len(xml.getElementsByTagName('Inker')) != 0:
authorsTemp = string.split(xml.getElementsByTagName('Inker')[0].firstChild.nodeValue, ', ')
for author in authorsTemp:
options.authors.append(author)
if len(xml.getElementsByTagName('Colorist')) != 0:
authorsTemp = string.split(xml.getElementsByTagName('Colorist')[0].firstChild.nodeValue, ', ')
for author in authorsTemp:
options.authors.append(author)
if len(options.authors) > 0:
options.authors = list(set(options.authors))
options.authors.sort()
else:
options.authors = ['KCC']
os.remove(xmlPath)
def slugify(value):
# Normalizes string, converts to lowercase, removes non-alpha characters and converts spaces to hyphens.
import unicodedata
if isinstance(value, str):
#noinspection PyArgumentList
value = unicodedata.normalize('NFKD', unicode(value, 'latin1')).encode('ascii', 'ignore')
@@ -631,7 +701,7 @@ def getDirectorySize(start_path='.'):
def createNewTome():
tomePathRoot = tempfile.mkdtemp('', 'KCC-TMP-')
tomePathRoot = mkdtemp('', 'KCC-TMP-')
tomePath = os.path.join(tomePathRoot, 'OEBPS', 'Images')
os.makedirs(tomePath)
return tomePath, tomePathRoot
@@ -853,6 +923,7 @@ def main(argv=None, qtGUI=None):
parser.print_help()
return
path = getWorkFolder(args[0])
checkComicInfo(path + "/OEBPS/Images/", args[0])
if options.webtoon:
if GUI:
GUI.emit(QtCore.SIGNAL("progressBarTick"), 'status', 'Splitting images')
@@ -879,19 +950,13 @@ def main(argv=None, qtGUI=None):
GUI.emit(QtCore.SIGNAL("progressBarTick"), 'status', 'Compressing CBZ files')
else:
GUI.emit(QtCore.SIGNAL("progressBarTick"), 'status', 'Compressing EPUB files')
GUI.emit(QtCore.SIGNAL("progressBarTick"), len(tomes))
GUI.emit(QtCore.SIGNAL("progressBarTick"), len(tomes) + 1)
GUI.emit(QtCore.SIGNAL("progressBarTick"))
options.baseTitle = options.title
for tome in tomes:
if GUI:
GUI.emit(QtCore.SIGNAL("progressBarTick"))
if os.path.isdir(args[0]):
barePath = os.path.basename(args[0])
else:
barePath = os.path.splitext(os.path.basename(args[0]))[0]
if len(tomes) > 1:
tomeNumber += 1
options.title = barePath + ' ' + str(tomeNumber)
elif options.title == 'defaulttitle':
options.title = barePath
options.title = options.baseTitle + ' [' + str(tomeNumber) + '/' + str(len(tomes)) + ']'
if options.cbzoutput:
# if CBZ output wanted, compress all images and return filepath
print "\nCreating CBZ file..."
@@ -911,6 +976,8 @@ def main(argv=None, qtGUI=None):
make_archive(tome + '_comic', 'zip', tome)
move(tome + '_comic.zip', filepath[-1])
rmtree(tome, True)
if GUI:
GUI.emit(QtCore.SIGNAL("progressBarTick"))
return filepath
@@ -968,6 +1035,9 @@ def checkOptions():
if options.profile == 'KFA' and (options.customwidth == 0 or options.customheight == 0):
print "ERROR: Kindle for Android profile require --customwidth and --customheight options!"
sys.exit(1)
# CBZ files on Kindle DX/DXG support higher resolution
if options.profile == 'KDX' and options.cbzoutput:
options.customheight = 1200
# Override profile data
if options.customwidth != 0 or options.customheight != 0:
X = image.ProfileData.Profiles[options.profile][1][0]

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
#
# Copyright (c) 2012-2013 Ciro Mattia Gonano <ciromattia@gmail.com>
@@ -18,7 +18,7 @@
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
#
__version__ = '3.5'
__version__ = '3.6'
__license__ = 'ISC'
__copyright__ = '2012-2013, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@vulturis.eu>'
__docformat__ = 'restructuredtext en'
@@ -33,9 +33,25 @@ try:
from PIL import Image, ImageStat
if tuple(map(int, ('2.2.1'.split(".")))) > tuple(map(int, (Image.PILLOW_VERSION.split(".")))):
print "ERROR: Pillow 2.2.1 or newer is required!"
if sys.platform.startswith('linux'):
#noinspection PyUnresolvedReferences
import Tkinter
#noinspection PyUnresolvedReferences
import tkMessageBox
importRoot = Tkinter.Tk()
importRoot.withdraw()
tkMessageBox.showerror("KCC - Error", "Pillow 2.2.1 or newer is required!")
exit(1)
except ImportError:
print "ERROR: Pillow is not installed!"
if sys.platform.startswith('linux'):
#noinspection PyUnresolvedReferences
import Tkinter
#noinspection PyUnresolvedReferences
import tkMessageBox
importRoot = Tkinter.Tk()
importRoot.withdraw()
tkMessageBox.showerror("KCC - Error", "Pillow 2.2.1 or newer is required!")
exit(1)
try:
from PyQt4 import QtCore

View File

@@ -21,14 +21,31 @@ __copyright__ = '2012-2013, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jas
__docformat__ = 'restructuredtext en'
import os
from sys import platform
try:
# noinspection PyUnresolvedReferences
from PIL import Image, ImageOps, ImageStat, ImageChops
if tuple(map(int, ('2.2.1'.split(".")))) > tuple(map(int, (Image.PILLOW_VERSION.split(".")))):
print "ERROR: Pillow 2.2.1 or newer is required!"
if platform.startswith('linux'):
#noinspection PyUnresolvedReferences
import Tkinter
#noinspection PyUnresolvedReferences
import tkMessageBox
importRoot = Tkinter.Tk()
importRoot.withdraw()
tkMessageBox.showerror("KCC - Error", "Pillow 2.2.1 or newer is required!")
exit(1)
except ImportError:
print "ERROR: Pillow is not installed!"
if platform.startswith('linux'):
#noinspection PyUnresolvedReferences
import Tkinter
#noinspection PyUnresolvedReferences
import tkMessageBox
importRoot = Tkinter.Tk()
importRoot.withdraw()
tkMessageBox.showerror("KCC - Error", "Pillow 2.2.1 or newer is required!")
exit(1)
@@ -133,7 +150,7 @@ class ProfileData:
class ComicPage:
def __init__(self, source, device):
def __init__(self, source, device, fill=None):
try:
self.profile_label, self.size, self.palette, self.gamma, self.panelviewsize = device
except KeyError:
@@ -163,7 +180,10 @@ class ComicPage:
self.border = None
self.noHPV = None
self.noVPV = None
self.fill = None
if fill:
self.fill = fill
else:
self.fill = None
def saveToDir(self, targetdir, forcepng, color, wipe):
try:
@@ -181,7 +201,8 @@ class ComicPage:
if self.noVPV:
suffix += "_kccnv"
if self.border:
suffix += "_kccx" + str(self.border[0]) + "_kccy" + str(self.border[1])
suffix += "_kccxl" + str(self.border[0]) + "_kccyu" + str(self.border[1]) + "_kccxr" +\
str(self.border[2]) + "_kccyd" + str(self.border[3])
if forcepng:
self.image.save(os.path.join(targetdir, os.path.splitext(self.filename)[0] + suffix + ".png"), "PNG",
optimize=1)
@@ -194,6 +215,8 @@ class ComicPage:
def optimizeImage(self, gamma):
if gamma < 0.1:
gamma = self.gamma
if self.gamma != 1.0 and self.isImageColor(self.image):
gamma = 1.0
if gamma == 1.0:
self.image = ImageOps.autocontrast(self.image)
else:
@@ -210,58 +233,56 @@ class ComicPage:
# Quantize is deprecated but new function call it internally anyway...
self.image = self.image.quantize(palette=palImg)
def calculateBorderPercent(self, x, img, isWidth):
if isWidth:
return int(round(float(x)/float(img.image.size[0]), 4) * 10000 * 1.5)
else:
return int(round(float(x)/float(img.image.size[1]), 4) * 10000 * 1.5)
def calculateBorder(self, sourceImage):
if self.fill == 'white':
border = ImageOps.invert(sourceImage.image).getbbox()
else:
border = sourceImage.image.getbbox()
if border is not None:
self.border = [self.calculateBorderPercent(border[0], sourceImage, True),
self.calculateBorderPercent(border[1], sourceImage, False),
self.calculateBorderPercent((sourceImage.image.size[0] - border[2]), sourceImage, True),
self.calculateBorderPercent((sourceImage.image.size[1] - border[3]), sourceImage, False)]
if int((border[2] - border[0]) * 1.5) < self.size[0]:
self.noHPV = True
self.border[0] /= 2
if int((border[3] - border[1]) * 1.5) < self.size[1]:
self.noVPV = True
self.border[1] /= 2
else:
self.border = [0, 0, 0, 0]
self.noHPV = True
self.noVPV = True
def resizeImage(self, upscale=False, stretch=False, bordersColor=None, qualityMode=0):
# High-quality downscaling filter
method = Image.ANTIALIAS
if bordersColor:
fill = bordersColor
else:
fill = self.fill
# Set target size
if qualityMode == 0:
size = (self.size[0], self.size[1])
generateBorder = True
elif qualityMode == 1:
size = (self.panelviewsize[0], self.panelviewsize[1])
generateBorder = True
else:
size = (self.panelviewsize[0], self.panelviewsize[1])
generateBorder = False
# If image is smaller than screen and upscale is off - Just expand it
if self.image.size[0] <= self.size[0] and self.image.size[1] <= self.size[1]:
if not upscale:
borderw = (self.size[0] - self.image.size[0]) / 2
borderh = (self.size[1] - self.image.size[1]) / 2
self.image = ImageOps.expand(self.image, border=(borderw, borderh), fill=fill)
if generateBorder:
if (self.image.size[0]-(2*borderw))*1.5 < self.size[0]:
self.noHPV = True
if (self.image.size[1]-(2*borderh))*1.5 < self.size[1]:
self.noVPV = True
self.border = [int(round(float(borderw)/float(self.image.size[0])*100, 2)*100*1.5),
int(round(float(borderh)/float(self.image.size[1])*100, 2)*100*1.5)]
return self.image
else:
# Cubic spline interpolation in a 4x4 environment
method = Image.BICUBIC
# If image is smaller than device resolution and upscale is off - Just expand it by adding margins
if self.image.size[0] <= self.size[0] and self.image.size[1] <= self.size[1] and not upscale:
borderw = (self.size[0] - self.image.size[0]) / 2
borderh = (self.size[1] - self.image.size[1]) / 2
self.image = ImageOps.expand(self.image, border=(borderw, borderh), fill=fill)
return self.image
# If stretching is on - Resize without other considerations
if stretch:
if self.image.size[0] <= size[0] and self.image.size[1] <= size[1]:
method = Image.BICUBIC
else:
method = Image.ANTIALIAS
self.image = self.image.resize(size, method)
if generateBorder:
if fill == 'white':
border = ImageOps.invert(self.image).getbbox()
else:
border = self.image.getbbox()
if border is not None:
if (border[2]-border[0])*1.5 < self.size[0]:
self.noHPV = True
if (border[3]-border[1])*1.5 < self.size[1]:
self.noVPV = True
self.border = [int(round(float(border[0])/float(self.image.size[0])*100, 2)*100*1.5),
int(round(float(border[1])/float(self.image.size[1])*100, 2)*100*1.5)]
else:
self.border = [0, 0]
self.noHPV = True
self.noVPV = True
return self.image
# Otherwise - Upscale/Downscale
ratioDev = float(self.size[0]) / float(self.size[1])
@@ -271,23 +292,11 @@ class ComicPage:
elif (float(self.image.size[0]) / float(self.image.size[1])) > ratioDev:
diff = int(self.image.size[0] / ratioDev) - self.image.size[1]
self.image = ImageOps.expand(self.image, border=(0, diff / 2), fill=fill)
if self.image.size[0] <= size[0] and self.image.size[1] <= size[1]:
method = Image.BICUBIC
else:
method = Image.ANTIALIAS
self.image = ImageOps.fit(self.image, size, method=method, centering=(0.5, 0.5))
if generateBorder:
if fill == 'white':
border = ImageOps.invert(self.image).getbbox()
else:
border = self.image.getbbox()
if border is not None:
if (border[2]-border[0])*1.5 < self.size[0]:
self.noHPV = True
if (border[3]-border[1])*1.5 < self.size[1]:
self.noVPV = True
self.border = [int(round(float(border[0])/float(self.image.size[0])*100, 2)*100*1.5),
int(round(float(border[1])/float(self.image.size[1])*100, 2)*100*1.5)]
else:
self.border = [0, 0]
self.noHPV = True
self.noVPV = True
return self.image
def splitPage(self, targetdir, righttoleft=False, rotate=False):
@@ -419,7 +428,7 @@ class ComicPage:
self.image = self.image.crop((0, 0, widthImg - diff, heightImg))
return self.image
def getImageHistogram(self, image):
def getImageHistogram(self, image, new=True):
histogram = image.histogram()
RBGW = []
pixelCount = 0
@@ -428,37 +437,100 @@ class ComicPage:
RBGW.append(histogram[i] + histogram[256 + i] + histogram[512 + i])
white = 0
black = 0
for i in range(245, 256):
for i in range(251, 256):
white += RBGW[i]
for i in range(11):
for i in range(5):
black += RBGW[i]
if black > white and black > pixelCount*0.5:
return True
if new:
if black > 0 and white == 0:
return 1
elif white > 0 and black == 0:
return -1
else:
return False
else:
return False
if black > white and black > pixelCount*0.5:
return True
else:
return False
def getImageFill(self, isWebToon):
fill = 0
if isWebToon or self.rotated:
fill += self.getImageHistogram(self.image.crop((0, 0, self.image.size[0], 5)))
if isWebToon:
fill = 0
fill += self.getImageHistogram(self.image.crop((0, 0, self.image.size[0], 5)), False)
fill += self.getImageHistogram(self.image.crop((0, self.image.size[1]-5, self.image.size[0],
self.image.size[1])))
else:
fill += self.getImageHistogram(self.image.crop((0, 0, 5, self.image.size[1])))
fill += self.getImageHistogram(self.image.crop((self.image.size[0]-5, 0, self.image.size[0],
self.image.size[1])))
if fill == 2:
self.fill = 'black'
elif fill == 0:
self.fill = 'white'
self.image.size[1])), False)
if fill == 2:
self.fill = 'black'
elif fill == 0:
self.fill = 'white'
else:
fill = 0
fill += self.getImageHistogram(self.image.crop((0, 0, 5, 5)), False)
fill += self.getImageHistogram(self.image.crop((self.image.size[0]-5, 0, self.image.size[0], 5)), False)
fill += self.getImageHistogram(self.image.crop((0, self.image.size[1]-5, 5, self.image.size[1])), False)
fill += self.getImageHistogram(self.image.crop((self.image.size[0]-5, self.image.size[1]-5,
self.image.size[0], self.image.size[1])), False)
if fill > 1:
self.fill = 'black'
else:
self.fill = 'white'
else:
fill = 0
fill += self.getImageHistogram(self.image.crop((0, 0, 5, 5)))
fill += self.getImageHistogram(self.image.crop((self.image.size[0]-5, 0, self.image.size[0], 5)))
fill += self.getImageHistogram(self.image.crop((0, self.image.size[1]-5, 5, self.image.size[1])))
fill += self.getImageHistogram(self.image.crop((self.image.size[0]-5, self.image.size[1]-5,
self.image.size[0], self.image.size[1])))
if fill > 1:
# Search fom horizontal solid lines
startY = 0
stopY = 3
searching = True
while stopY <= self.image.size[1]:
checkSolid = self.getImageHistogram(self.image.crop((0, startY, self.image.size[0], stopY)))
if checkSolid:
fill += checkSolid
startY = stopY + 1
stopY = startY + 3
if stopY > self.image.size[1] and searching:
startY = self.image.size[1] - 3
stopY = self.image.size[1]
searching = False
# Search fom vertical solid lines
startX = 0
stopX = 3
searching = True
while stopX <= self.image.size[0]:
checkSolid = self.getImageHistogram(self.image.crop((startX, 0, stopX, self.image.size[1])))
if checkSolid:
fill += checkSolid
startX = stopX + 1
stopX = startX + 3
if stopX > self.image.size[0] and searching:
startX = self.image.size[0] - 3
stopX = self.image.size[0]
searching = False
if fill > 0:
self.fill = 'black'
else:
self.fill = 'white'
self.fill = 'white'
def isImageColor(self, image):
v = ImageStat.Stat(image).var
isMonochromatic = reduce(lambda x, y: x and y < 0.005, v, True)
if isMonochromatic:
# Monochromatic
return False
else:
if len(v) == 3:
maxmin = abs(max(v) - min(v))
if maxmin > 1000:
# Color
return True
elif maxmin > 100:
# Probably color
return True
else:
# Grayscale
return False
elif len(v) == 1:
# Black and white
return False
else:
# Detection failed
return False

View File

@@ -1,3 +1,4 @@
#!/usr/bin/env python2
"""
cx_Freeze build script for KCC.
@@ -10,7 +11,7 @@ Usage (Windows):
from sys import platform
NAME = "KindleComicConverter"
VERSION = "3.5"
VERSION = "3.6"
MAIN = "kcc.py"
if platform == "darwin":
@@ -23,10 +24,11 @@ if platform == "darwin":
argv_emulation=True,
iconfile='icons/comic2ebook.icns',
includes=['PIL', 'sip', 'PyQt4', 'PyQt4.QtCore', 'PyQt4.QtGui', 'PyQt4.QtNetwork'],
qt_plugins=[],
excludes=['PyQt4.QtDeclarative', 'PyQt4.QtDesigner', 'PyQt4.QtHelp', 'PyQt4.QtMultimedia',
'PyQt4.QtOpenGL', 'PyQt4.QtScript', 'PyQt4.QtScriptTools', 'PyQt4.QtSql', 'PyQt4.QtSvg',
'PyQt4.QtXmlPatterns', 'PyQt4.QtXml', 'PyQt4.QtWebKit', 'PyQt4.QtTest'],
resources=['LICENSE.txt', 'other/Additional-LICENSE.txt'],
'PyQt4.QtXmlPatterns', 'PyQt4.QtXml', 'PyQt4.QtWebKit', 'PyQt4.QtTest', 'Tkinter'],
resources=['LICENSE.txt', 'other/Additional-LICENSE.txt', 'other/unrar', 'other/7za'],
plist=dict(
CFBundleName=NAME,
CFBundleShortVersionString=VERSION,
@@ -42,6 +44,10 @@ if platform == "darwin":
CFBundleTypeRole='Viewer',
)
],
LSMinimumSystemVersion='10.6.0',
LSEnvironment=dict(
PATH='/usr/local/bin:/usr/bin:/bin'
),
NSHumanReadableCopyright='ISC License (ISCL)'
)
)
@@ -55,7 +61,8 @@ elif platform == "win32":
['other/UnRAR.exe', 'UnRAR.exe'],
['other/7za.exe', '7za.exe'],
['other/Additional-LICENSE.txt', 'Additional-LICENSE.txt']
], "compressed": True}},
], "compressed": True,
"excludes": ['Tkinter']}},
executables=[Executable(MAIN,
base=base,
targetName="KCC.exe",
@@ -65,18 +72,10 @@ elif platform == "win32":
appendScriptToLibrary=False,
compress=True)])
else:
from cx_Freeze import setup, Executable
extra_options = dict(
options={"build_exe": {"include_files": ['LICENSE.txt',
['other/Additional-LICENSE.txt', 'Additional-LICENSE.txt']
], "compressed": True}},
executables=[Executable(MAIN,
icon="icons/comic2ebook.png",
copyDependentFiles=True,
appendScriptToExe=True,
appendScriptToLibrary=False,
compress=True)])
print 'Please use setup.sh to build Linux package.'
exit()
#noinspection PyUnboundLocalVariable
setup(
name=NAME,
version=VERSION,
@@ -89,3 +88,8 @@ setup(
packages=['kcc'], requires=['Pillow'],
**extra_options
)
if platform == "darwin":
from os import chmod
chmod('dist/' + NAME + '.app/Contents/Resources/unrar', 0777)
chmod('dist/' + NAME + '.app/Contents/Resources/7za', 0777)

12
setup.sh Normal file
View File

@@ -0,0 +1,12 @@
#!/bin/bash
# Linux Python package build script
VERSION="3.6"
cp kcc.py __main__.py
zip kcc.zip __main__.py kcc/*.py
echo "#!/usr/bin/env python2" > kcc-bin
cat kcc.zip >> kcc-bin
chmod +x kcc-bin
tar --xform s:^.*/:: --xform s/kcc-bin/kcc/ --xform s/comic2ebook/kcc/ -czf KindleComicConverter_linux_$VERSION.tar.gz kcc-bin LICENSE.txt icons/comic2ebook.png
rm __main__.py kcc.zip kcc-bin