mirror of
https://github.com/ciromattia/kcc
synced 2026-04-16 22:18:51 +00:00
Compare commits
55 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e835502837 | ||
|
|
5bcdc78725 | ||
|
|
acb4dfad8f | ||
|
|
7f5de29174 | ||
|
|
11007402cd | ||
|
|
0cf92fc48f | ||
|
|
953942ca00 | ||
|
|
c46ca8b507 | ||
|
|
48d3bee225 | ||
|
|
3b0e5cc309 | ||
|
|
e5be31f9d5 | ||
|
|
17ea85c31f | ||
|
|
572e1422bf | ||
|
|
af263073b5 | ||
|
|
7facf2d620 | ||
|
|
eef3ff434b | ||
|
|
c680cfd5c5 | ||
|
|
3e8469611d | ||
|
|
39ab475156 | ||
|
|
636de67a17 | ||
|
|
d80c18f652 | ||
|
|
557bd2bbbf | ||
|
|
ddd223c2ec | ||
|
|
50f5b600b1 | ||
|
|
d94df8390a | ||
|
|
8b33331929 | ||
|
|
86a9dde1eb | ||
|
|
33dec77063 | ||
|
|
fe06e2fa19 | ||
|
|
7b5e3eaafd | ||
|
|
0a30f1ffb9 | ||
|
|
8687604d26 | ||
|
|
0a9fd6c439 | ||
|
|
c95a9395de | ||
|
|
77066d7a9f | ||
|
|
c8e5b7de9a | ||
|
|
3e11a88a7c | ||
|
|
a7e4968836 | ||
|
|
6d9e2d3c03 | ||
|
|
0789e7a353 | ||
|
|
ff97a85552 | ||
|
|
33cfd92cef | ||
|
|
58513ef59f | ||
|
|
6056e3e767 | ||
|
|
1b1ed7c4ab | ||
|
|
5b44e4bddd | ||
|
|
54592969a4 | ||
|
|
38007ab3d5 | ||
|
|
bdd10c7617 | ||
|
|
c0f4bc021a | ||
|
|
34d6af93a6 | ||
|
|
0df481dabb | ||
|
|
55c5b91411 | ||
|
|
be745f4602 | ||
|
|
8bf5ad0f12 |
8
.gitignore
vendored
8
.gitignore
vendored
@@ -2,11 +2,11 @@
|
|||||||
*.cbz
|
*.cbz
|
||||||
*.cbr
|
*.cbr
|
||||||
.idea
|
.idea
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
build
|
build
|
||||||
dist
|
dist
|
||||||
Output
|
Output
|
||||||
|
test
|
||||||
|
solaio
|
||||||
kindlegen*
|
kindlegen*
|
||||||
UnRAR*
|
|
||||||
7za*
|
|
||||||
.DS_Store
|
|
||||||
Thumbs.db
|
|
||||||
|
|||||||
26
KCC-Linux.ui
26
KCC-Linux.ui
@@ -7,19 +7,19 @@
|
|||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>420</width>
|
<width>420</width>
|
||||||
<height>380</height>
|
<height>397</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="minimumSize">
|
<property name="minimumSize">
|
||||||
<size>
|
<size>
|
||||||
<width>420</width>
|
<width>420</width>
|
||||||
<height>380</height>
|
<height>397</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="maximumSize">
|
<property name="maximumSize">
|
||||||
<size>
|
<size>
|
||||||
<width>420</width>
|
<width>420</width>
|
||||||
<height>380</height>
|
<height>397</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="font">
|
<property name="font">
|
||||||
@@ -27,9 +27,6 @@
|
|||||||
<pointsize>9</pointsize>
|
<pointsize>9</pointsize>
|
||||||
</font>
|
</font>
|
||||||
</property>
|
</property>
|
||||||
<property name="focusPolicy">
|
|
||||||
<enum>Qt::NoFocus</enum>
|
|
||||||
</property>
|
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
<string>Kindle Comic Converter</string>
|
<string>Kindle Comic Converter</string>
|
||||||
</property>
|
</property>
|
||||||
@@ -546,9 +543,6 @@ p, li { white-space: pre-wrap; }
|
|||||||
<family>DejaVu Sans</family>
|
<family>DejaVu Sans</family>
|
||||||
</font>
|
</font>
|
||||||
</property>
|
</property>
|
||||||
<property name="toolTip">
|
|
||||||
<string><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></string>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Gamma: Auto</string>
|
<string>Gamma: Auto</string>
|
||||||
</property>
|
</property>
|
||||||
@@ -570,9 +564,6 @@ p, li { white-space: pre-wrap; }
|
|||||||
<property name="focusPolicy">
|
<property name="focusPolicy">
|
||||||
<enum>Qt::ClickFocus</enum>
|
<enum>Qt::ClickFocus</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="toolTip">
|
|
||||||
<string><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></string>
|
|
||||||
</property>
|
|
||||||
<property name="maximum">
|
<property name="maximum">
|
||||||
<number>500</number>
|
<number>500</number>
|
||||||
</property>
|
</property>
|
||||||
@@ -785,6 +776,17 @@ p, li { white-space: pre-wrap; }
|
|||||||
<zorder>OptionsExpert</zorder>
|
<zorder>OptionsExpert</zorder>
|
||||||
<zorder>ProgressBar</zorder>
|
<zorder>ProgressBar</zorder>
|
||||||
</widget>
|
</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">
|
<action name="ActionBasic">
|
||||||
<property name="checkable">
|
<property name="checkable">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
|
|||||||
26
KCC-OSX.ui
26
KCC-OSX.ui
@@ -7,19 +7,19 @@
|
|||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>420</width>
|
<width>420</width>
|
||||||
<height>380</height>
|
<height>397</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="minimumSize">
|
<property name="minimumSize">
|
||||||
<size>
|
<size>
|
||||||
<width>420</width>
|
<width>420</width>
|
||||||
<height>380</height>
|
<height>397</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="maximumSize">
|
<property name="maximumSize">
|
||||||
<size>
|
<size>
|
||||||
<width>420</width>
|
<width>420</width>
|
||||||
<height>380</height>
|
<height>397</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="font">
|
<property name="font">
|
||||||
@@ -27,9 +27,6 @@
|
|||||||
<pointsize>9</pointsize>
|
<pointsize>9</pointsize>
|
||||||
</font>
|
</font>
|
||||||
</property>
|
</property>
|
||||||
<property name="focusPolicy">
|
|
||||||
<enum>Qt::NoFocus</enum>
|
|
||||||
</property>
|
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
<string>Kindle Comic Converter</string>
|
<string>Kindle Comic Converter</string>
|
||||||
</property>
|
</property>
|
||||||
@@ -546,9 +543,6 @@
|
|||||||
<bold>false</bold>
|
<bold>false</bold>
|
||||||
</font>
|
</font>
|
||||||
</property>
|
</property>
|
||||||
<property name="toolTip">
|
|
||||||
<string><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></string>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Gamma: Auto</string>
|
<string>Gamma: Auto</string>
|
||||||
</property>
|
</property>
|
||||||
@@ -570,9 +564,6 @@
|
|||||||
<property name="focusPolicy">
|
<property name="focusPolicy">
|
||||||
<enum>Qt::ClickFocus</enum>
|
<enum>Qt::ClickFocus</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="toolTip">
|
|
||||||
<string><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></string>
|
|
||||||
</property>
|
|
||||||
<property name="maximum">
|
<property name="maximum">
|
||||||
<number>500</number>
|
<number>500</number>
|
||||||
</property>
|
</property>
|
||||||
@@ -797,6 +788,17 @@
|
|||||||
<zorder>OptionsAdvancedGamma</zorder>
|
<zorder>OptionsAdvancedGamma</zorder>
|
||||||
<zorder>OptionsExpert</zorder>
|
<zorder>OptionsExpert</zorder>
|
||||||
</widget>
|
</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">
|
<action name="ActionBasic">
|
||||||
<property name="checkable">
|
<property name="checkable">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
|
|||||||
26
KCC.ui
26
KCC.ui
@@ -7,19 +7,19 @@
|
|||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>420</width>
|
<width>420</width>
|
||||||
<height>380</height>
|
<height>397</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="minimumSize">
|
<property name="minimumSize">
|
||||||
<size>
|
<size>
|
||||||
<width>420</width>
|
<width>420</width>
|
||||||
<height>380</height>
|
<height>397</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="maximumSize">
|
<property name="maximumSize">
|
||||||
<size>
|
<size>
|
||||||
<width>420</width>
|
<width>420</width>
|
||||||
<height>380</height>
|
<height>397</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="font">
|
<property name="font">
|
||||||
@@ -27,9 +27,6 @@
|
|||||||
<pointsize>9</pointsize>
|
<pointsize>9</pointsize>
|
||||||
</font>
|
</font>
|
||||||
</property>
|
</property>
|
||||||
<property name="focusPolicy">
|
|
||||||
<enum>Qt::NoFocus</enum>
|
|
||||||
</property>
|
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
<string>Kindle Comic Converter</string>
|
<string>Kindle Comic Converter</string>
|
||||||
</property>
|
</property>
|
||||||
@@ -472,9 +469,6 @@ p, li { white-space: pre-wrap; }
|
|||||||
<height>40</height>
|
<height>40</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="toolTip">
|
|
||||||
<string>When converting color images setting this option to 1.0 MIGHT improve readability.</string>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Gamma: Auto</string>
|
<string>Gamma: Auto</string>
|
||||||
</property>
|
</property>
|
||||||
@@ -491,9 +485,6 @@ p, li { white-space: pre-wrap; }
|
|||||||
<property name="focusPolicy">
|
<property name="focusPolicy">
|
||||||
<enum>Qt::ClickFocus</enum>
|
<enum>Qt::ClickFocus</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="toolTip">
|
|
||||||
<string><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></string>
|
|
||||||
</property>
|
|
||||||
<property name="maximum">
|
<property name="maximum">
|
||||||
<number>500</number>
|
<number>500</number>
|
||||||
</property>
|
</property>
|
||||||
@@ -674,6 +665,17 @@ p, li { white-space: pre-wrap; }
|
|||||||
<zorder>OptionsExpert</zorder>
|
<zorder>OptionsExpert</zorder>
|
||||||
<zorder>ProgressBar</zorder>
|
<zorder>ProgressBar</zorder>
|
||||||
</widget>
|
</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">
|
<action name="ActionBasic">
|
||||||
<property name="checkable">
|
<property name="checkable">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
|
|||||||
77
README.md
77
README.md
@@ -1,6 +1,6 @@
|
|||||||
# KCC
|
# 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
|
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**_.
|
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.
|
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
|
### A word of warning
|
||||||
**KCC** _is not_ [Amazon's Kindle Comic Creator](http://www.amazon.com/gp/feature.html?ie=UTF8&docId=1001103761) nor is in any way endorsed by Amazon.
|
**KCC** _is not_ [Amazon's Kindle Comic Creator](http://www.amazon.com/gp/feature.html?ie=UTF8&docId=1001103761) nor is in any way endorsed by Amazon.
|
||||||
Amazon's tool is for comic publishers and involves a lot of manual effort, while **KCC** is for comic readers.
|
Amazon's tool is for comic publishers and involves a lot of manual effort, while **KCC** is for comic 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:
|
If you find **KCC** valuable you can consider donating to the authors:
|
||||||
|
* Ciro Mattia Gonano: [](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=D8WNYNPBGDAS2) [](http://flattr.com/thing/2260449/ciromattiakcc-on-GitHub)
|
||||||
* Ciro Mattia Gonano [](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=D8WNYNPBGDAS2)
|
* Paweł Jastrzębski: [](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YTTJ4LK2JDHPS) [](bitcoin:1W15wwqsfd7wbaZ6wvSJf1LW1bz6q5L8b?label=KCC) [1W15wwqsfd7wbaZ6wvSJf1LW1bz6q5L8b](bitcoin:1W15wwqsfd7wbaZ6wvSJf1LW1bz6q5L8b?label=KCC)
|
||||||
* Paweł Jastrzębski [](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YTTJ4LK2JDHPS)
|
|
||||||
|
|
||||||
## BINARY RELEASES
|
## BINARY RELEASES
|
||||||
You can find the latest released binary at the following links:
|
You can find the latest released binary at the following links:
|
||||||
- **Win64:** [http://kcc.vulturis.eu/Win64/](http://kcc.vulturis.eu/Win64/)
|
- **Windows:** [http://kcc.vulturis.eu/Windows/](http://kcc.vulturis.eu/Windows/)
|
||||||
- **Win32:** [http://kcc.vulturis.eu/Win32/](http://kcc.vulturis.eu/Win32/)
|
- **Linux:** [http://kcc.vulturis.eu/Linux/](http://kcc.vulturis.eu/Linux/)
|
||||||
- **OS X:** [http://kcc.vulturis.eu/OSX/](http://kcc.vulturis.eu/OSX/)
|
- **OS X (10.8 or later):** [http://kcc.vulturis.eu/OSX/](http://kcc.vulturis.eu/OSX/)
|
||||||
- **Linux:** Just download sourcecode and launch: `python kcc.py`
|
- **OS X (10.7 or earlier):** Soon™
|
||||||
|
|
||||||
## INPUT FORMATS
|
## INPUT FORMATS
|
||||||
**KCC** can understand and convert, at the moment, the following file types:
|
**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:
|
### 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.
|
- 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.
|
- [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
|
## 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.
|
* Read tooltip of _High/Ultra quality_ option. There are many important informations there.
|
||||||
* When converting images smaller than device resolution remember to enable upscaling.
|
* 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.
|
* 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.
|
* 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.
|
* The first image found will be set as the comic's cover.
|
||||||
* All files/directories will be added to EPUB in alphabetical order.
|
* 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
|
### 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)
|
* [Kindle Fire HDX 8.9"](http://kcc.vulturis.eu/Samples/Ubunchu!-KFHDX8.mobi)
|
||||||
|
|
||||||
## CHANGELOG
|
## CHANGELOG
|
||||||
####1.00
|
####1.0
|
||||||
* Initial version
|
* Initial version
|
||||||
|
|
||||||
####1.10
|
####1.1
|
||||||
* Added support for CBZ/CBR files in comic2ebook.py
|
* Added support for CBZ/CBR files in comic2ebook.py
|
||||||
|
|
||||||
####1.11
|
####1.1.1
|
||||||
* Added support for CBZ/CBR files in KindleComicConverter
|
* 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!
|
* 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
|
* Fixed an issue in OPF generation for device resolution
|
||||||
* Reworked options system (call with -h option to get the inline help)
|
* Reworked options system (call with -h option to get the inline help)
|
||||||
|
|
||||||
####1.40
|
####1.4
|
||||||
* Added some options for controlling image optimization
|
* Added some options for controlling image optimization
|
||||||
* Further optimization (ImageOps, page numbering cut, autocontrast)
|
* 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
|
* Fixed a serious bug on resizing when img ratio was bigger than device one
|
||||||
|
|
||||||
####1.50
|
####1.5
|
||||||
* Added subfolder support for multiple chapters.
|
* Added subfolder support for multiple chapters.
|
||||||
|
|
||||||
####2.0
|
####2.0
|
||||||
@@ -179,7 +188,7 @@ The app relies and includes the following scripts/binaries:
|
|||||||
####2.1
|
####2.1
|
||||||
* Added basic error reporting
|
* Added basic error reporting
|
||||||
|
|
||||||
#### 2.2:
|
####2.2:
|
||||||
* Added (valid!) ePub 2.0 output
|
* Added (valid!) ePub 2.0 output
|
||||||
* Rename .zip files to .cbz to avoid overwriting
|
* Rename .zip files to .cbz to avoid overwriting
|
||||||
|
|
||||||
@@ -284,6 +293,24 @@ The app relies and includes the following scripts/binaries:
|
|||||||
* Improved multiprocessing speed
|
* Improved multiprocessing speed
|
||||||
* GUI tweaks and minor bug fixes
|
* 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
|
||||||
|
|
||||||
|
####3.6.1:
|
||||||
|
* Fixed PNG output
|
||||||
|
|
||||||
|
####3.6.2:
|
||||||
|
* Fixed previous PNG output fix
|
||||||
|
* Fixed Panel View anomalies
|
||||||
|
|
||||||
## COPYRIGHT
|
## COPYRIGHT
|
||||||
|
|
||||||
Copyright (c) 2012-2013 Ciro Mattia Gonano and Paweł Jastrzębski.
|
Copyright (c) 2012-2013 Ciro Mattia Gonano and Paweł Jastrzębski.
|
||||||
|
|||||||
6
kcc.iss
6
kcc.iss
@@ -1,5 +1,5 @@
|
|||||||
#define MyAppName "Kindle Comic Converter"
|
#define MyAppName "Kindle Comic Converter"
|
||||||
#define MyAppVersion "3.5"
|
#define MyAppVersion "3.6.2"
|
||||||
#define MyAppPublisher "Ciro Mattia Gonano, Paweł Jastrzębski"
|
#define MyAppPublisher "Ciro Mattia Gonano, Paweł Jastrzębski"
|
||||||
#define MyAppURL "http://kcc.vulturis.eu/"
|
#define MyAppURL "http://kcc.vulturis.eu/"
|
||||||
#define MyAppExeName "KCC.exe"
|
#define MyAppExeName "KCC.exe"
|
||||||
@@ -12,6 +12,7 @@ AppPublisher={#MyAppPublisher}
|
|||||||
AppPublisherURL={#MyAppURL}
|
AppPublisherURL={#MyAppURL}
|
||||||
AppSupportURL={#MyAppURL}
|
AppSupportURL={#MyAppURL}
|
||||||
AppUpdatesURL={#MyAppURL}
|
AppUpdatesURL={#MyAppURL}
|
||||||
|
AppCopyright=Copyright (C) 2012-2013 Ciro Mattia Gonano and Paweł Jastrzębski
|
||||||
DefaultDirName={pf}\{#MyAppName}
|
DefaultDirName={pf}\{#MyAppName}
|
||||||
DefaultGroupName={#MyAppName}
|
DefaultGroupName={#MyAppName}
|
||||||
AllowNoIcons=yes
|
AllowNoIcons=yes
|
||||||
@@ -28,6 +29,7 @@ UninstallDisplayName={#MyAppName}
|
|||||||
UninstallDisplayIcon={app}\{#MyAppExeName}
|
UninstallDisplayIcon={app}\{#MyAppExeName}
|
||||||
ChangesAssociations=True
|
ChangesAssociations=True
|
||||||
InfoAfterFile=other\InstallWarning.rtf
|
InfoAfterFile=other\InstallWarning.rtf
|
||||||
|
SignTool=SignTool /d $q{#MyAppName}$q /du $q{#MyAppURL}$q $f
|
||||||
|
|
||||||
[Languages]
|
[Languages]
|
||||||
Name: "english"; MessagesFile: "compiler:Default.isl"
|
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\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\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\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
|
; x86 files
|
||||||
Source: "build\exe.win32-2.7\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion solidbreak; Check: not Is64BitInstallMode
|
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
|
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\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\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\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
|
; Common files
|
||||||
Source: "LICENSE.txt"; DestDir: "{app}"; Flags: ignoreversion solidbreak
|
Source: "LICENSE.txt"; DestDir: "{app}"; Flags: ignoreversion solidbreak
|
||||||
Source: "other\Additional-LICENSE.txt"; DestDir: "{app}"; Flags: ignoreversion
|
Source: "other\Additional-LICENSE.txt"; DestDir: "{app}"; Flags: ignoreversion
|
||||||
|
|||||||
45
kcc.py
45
kcc.py
@@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python2
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (c) 2012-2013 Ciro Mattia Gonano <ciromattia@gmail.com>
|
# 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
|
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
# PERFORMANCE OF THIS SOFTWARE.
|
# PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
__version__ = '3.5'
|
__version__ = '3.6.2'
|
||||||
__license__ = 'ISC'
|
__license__ = 'ISC'
|
||||||
__copyright__ = '2012-2013, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@vulturis.eu>'
|
__copyright__ = '2012-2013, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@vulturis.eu>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
@@ -30,22 +30,28 @@ try:
|
|||||||
from PyQt4 import QtCore, QtGui, QtNetwork
|
from PyQt4 import QtCore, QtGui, QtNetwork
|
||||||
except ImportError:
|
except ImportError:
|
||||||
print "ERROR: PyQT4 is not installed!"
|
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)
|
exit(1)
|
||||||
from kcc import KCC_gui
|
from kcc import KCC_gui
|
||||||
from multiprocessing import freeze_support
|
from multiprocessing import freeze_support
|
||||||
|
|
||||||
|
# OS specific PATH variable workarounds
|
||||||
if sys.platform.startswith('darwin'):
|
if sys.platform.startswith('darwin'):
|
||||||
# Workaround Finder-launched app PATH evaluation
|
if 'RESOURCEPATH' in os.environ:
|
||||||
os.environ['PATH'] = '/usr/local/bin:' + os.environ['PATH']
|
os.environ['PATH'] = os.environ['RESOURCEPATH'] + ':' + os.environ['PATH']
|
||||||
from kcc import KCC_ui_osx as KCC_ui
|
else:
|
||||||
elif sys.platform.startswith('linux'):
|
os.environ['PATH'] = os.path.dirname(os.path.abspath(__file__)) + '/other/:' + os.environ['PATH']
|
||||||
from kcc import KCC_ui_linux as KCC_ui
|
elif sys.platform.startswith('win'):
|
||||||
else:
|
|
||||||
# Workaround for Windows file association mechanism
|
|
||||||
if getattr(sys, 'frozen', False):
|
if getattr(sys, 'frozen', False):
|
||||||
os.chdir(os.path.dirname(os.path.abspath(sys.executable)))
|
os.chdir(os.path.dirname(os.path.abspath(sys.executable)))
|
||||||
else:
|
else:
|
||||||
|
os.environ['PATH'] = os.path.dirname(os.path.abspath(__file__)) + '/other/;' + os.environ['PATH']
|
||||||
os.chdir(os.path.dirname(os.path.abspath(__file__)))
|
os.chdir(os.path.dirname(os.path.abspath(__file__)))
|
||||||
from kcc import KCC_ui
|
|
||||||
|
|
||||||
|
|
||||||
# Implementing detection of already running KCC instance and forwarding argv to it
|
# Implementing detection of already running KCC instance and forwarding argv to it
|
||||||
@@ -89,10 +95,10 @@ class QApplicationMessaging(QtGui.QApplication):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
freeze_support()
|
freeze_support()
|
||||||
APP = QApplicationMessaging(sys.argv)
|
KCCAplication = QApplicationMessaging(sys.argv)
|
||||||
if APP.isRunning():
|
if KCCAplication.isRunning():
|
||||||
if len(sys.argv) > 1:
|
if len(sys.argv) > 1:
|
||||||
APP.sendMessage(sys.argv[1].decode(sys.getfilesystemencoding()))
|
KCCAplication.sendMessage(sys.argv[1].decode(sys.getfilesystemencoding()))
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
else:
|
else:
|
||||||
messageBox = QtGui.QMessageBox()
|
messageBox = QtGui.QMessageBox()
|
||||||
@@ -101,13 +107,8 @@ if APP.isRunning():
|
|||||||
messageBox.setWindowIcon(icon)
|
messageBox.setWindowIcon(icon)
|
||||||
QtGui.QMessageBox.critical(messageBox, 'KCC - Error', 'KCC is already running!', QtGui.QMessageBox.Ok)
|
QtGui.QMessageBox.critical(messageBox, 'KCC - Error', 'KCC is already running!', QtGui.QMessageBox.Ok)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
KCC = QtGui.QMainWindow()
|
KCCWindow = QtGui.QMainWindow()
|
||||||
UI = KCC_ui.Ui_KCC()
|
KCCUI = KCC_gui.KCCGUI(KCCAplication, KCCWindow)
|
||||||
UI.setupUi(KCC)
|
|
||||||
GUI = KCC_gui.Ui_KCC(UI, KCC, APP)
|
|
||||||
KCC.setWindowTitle("Kindle Comic Converter " + __version__)
|
|
||||||
KCC.show()
|
|
||||||
KCC.raise_()
|
|
||||||
if len(sys.argv) > 1:
|
if len(sys.argv) > 1:
|
||||||
GUI.handleMessage(sys.argv[1].decode(sys.getfilesystemencoding()))
|
KCCUI.handleMessage(sys.argv[1].decode(sys.getfilesystemencoding()))
|
||||||
sys.exit(APP.exec_())
|
sys.exit(KCCAplication.exec_())
|
||||||
|
|||||||
500
kcc/KCC_gui.py
500
kcc/KCC_gui.py
@@ -1,4 +1,3 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (c) 2012-2013 Ciro Mattia Gonano <ciromattia@gmail.com>
|
# 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
|
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
# PERFORMANCE OF THIS SOFTWARE.
|
# PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
__version__ = '3.5'
|
__version__ = '3.6.2'
|
||||||
__license__ = 'ISC'
|
__license__ = 'ISC'
|
||||||
__copyright__ = '2012-2013, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@vulturis.eu>'
|
__copyright__ = '2012-2013, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@vulturis.eu>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import shutil
|
|
||||||
import traceback
|
import traceback
|
||||||
import urllib2
|
import urllib2
|
||||||
import time
|
import socket
|
||||||
import comic2ebook
|
import comic2ebook
|
||||||
import kindlesplit
|
import kindlesplit
|
||||||
import socket
|
from string import split
|
||||||
import string
|
from time import sleep
|
||||||
from KCC_rc_web import WebContent
|
from shutil import move
|
||||||
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
|
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
|
||||||
from SocketServer import ThreadingMixIn
|
from SocketServer import ThreadingMixIn
|
||||||
from image import ProfileData
|
from image import ProfileData
|
||||||
from subprocess import call, Popen, STDOUT, PIPE
|
from subprocess import STDOUT, PIPE
|
||||||
from PyQt4 import QtGui, QtCore
|
from PyQt4 import QtGui, QtCore
|
||||||
from xml.dom.minidom import parse
|
from xml.dom.minidom import parse
|
||||||
from HTMLParser import HTMLParser
|
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:
|
class Icons:
|
||||||
@@ -64,6 +81,9 @@ class Icons:
|
|||||||
self.error = QtGui.QIcon()
|
self.error = QtGui.QIcon()
|
||||||
self.error.addPixmap(QtGui.QPixmap(":/Status/icons/error.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
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):
|
class HTMLStripper(HTMLParser):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@@ -102,16 +122,16 @@ class WebServerHandler(BaseHTTPRequestHandler):
|
|||||||
self.wfile.write('<!DOCTYPE html>\n'
|
self.wfile.write('<!DOCTYPE html>\n'
|
||||||
'<html lang="en">\n'
|
'<html lang="en">\n'
|
||||||
'<head><meta charset="utf-8">\n'
|
'<head><meta charset="utf-8">\n'
|
||||||
'<link href="' + GUIMain.webContent.favicon + '" rel="icon" type="image/x-icon" />\n'
|
'<link href="' + GUI.webContent.favicon + '" rel="icon" type="image/x-icon" />\n'
|
||||||
'<title>KindleComicConverter</title>\n'
|
'<title>Kindle Comic Converter</title>\n'
|
||||||
'</head>\n'
|
'</head>\n'
|
||||||
'<body>\n'
|
'<body>\n'
|
||||||
'<div style="text-align: center; font-size:25px">\n'
|
'<div style="text-align: center; font-size:25px">\n'
|
||||||
'<p style="font-size:50px">- <img style="vertical-align: middle" '
|
'<p style="font-size:50px">- <img style="vertical-align: middle" '
|
||||||
'alt="KCC Logo" src="' + GUIMain.webContent.logo + '" /> -</p>\n')
|
'alt="KCC Logo" src="' + GUI.webContent.logo + '" /> -</p>\n')
|
||||||
if len(GUIMain.completedWork) > 0 and not GUIMain.conversionAlive:
|
if len(GUI.completedWork) > 0 and not GUI.conversionAlive:
|
||||||
for key in sorted(GUIMain.completedWork.iterkeys()):
|
for key in sorted(GUI.completedWork.iterkeys()):
|
||||||
self.wfile.write('<p><a href="' + key + '">' + string.split(key, '.')[0] + '</a></p>\n')
|
self.wfile.write('<p><a href="' + key + '">' + split(key, '.')[0] + '</a></p>\n')
|
||||||
else:
|
else:
|
||||||
self.wfile.write('<p style="font-weight: bold">No downloads are available.<br/>'
|
self.wfile.write('<p style="font-weight: bold">No downloads are available.<br/>'
|
||||||
'Convert some files and refresh this page.</p>\n')
|
'Convert some files and refresh this page.</p>\n')
|
||||||
@@ -119,7 +139,7 @@ class WebServerHandler(BaseHTTPRequestHandler):
|
|||||||
'</body>\n'
|
'</body>\n'
|
||||||
'</html>\n')
|
'</html>\n')
|
||||||
elif sendReply:
|
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')
|
fp = open(outputFile, 'rb')
|
||||||
self.send_response(200)
|
self.send_response(200)
|
||||||
self.send_header('Content-type', mimetype)
|
self.send_header('Content-type', mimetype)
|
||||||
@@ -194,27 +214,133 @@ class VersionThread(QtCore.QThread):
|
|||||||
'Changelog</a>)', 'warning')
|
'Changelog</a>)', 'warning')
|
||||||
|
|
||||||
|
|
||||||
# noinspection PyBroadException
|
class ProgressThread(QtCore.QThread):
|
||||||
class WorkerThread(QtCore.QThread):
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
QtCore.QThread.__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.conversionAlive = False
|
||||||
self.errors = False
|
self.errors = False
|
||||||
self.kindlegenErrorCode = 0
|
self.kindlegenErrorCode = [0]
|
||||||
self.kindlegenError = None
|
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):
|
def __del__(self):
|
||||||
self.wait()
|
self.wait()
|
||||||
|
|
||||||
def sync(self):
|
def sync(self):
|
||||||
self.conversionAlive = GUIMain.conversionAlive
|
self.conversionAlive = GUI.conversionAlive
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
GUIMain.needClean = True
|
GUI.progress.content = ''
|
||||||
|
GUI.progress.stop()
|
||||||
|
GUI.needClean = True
|
||||||
self.emit(QtCore.SIGNAL("hideProgressBar"))
|
self.emit(QtCore.SIGNAL("hideProgressBar"))
|
||||||
self.emit(QtCore.SIGNAL("addMessage"), '<b>Conversion interrupted.</b>', 'error')
|
self.emit(QtCore.SIGNAL("addMessage"), '<b>Conversion interrupted.</b>', 'error')
|
||||||
|
self.emit(QtCore.SIGNAL("addTrayMessage"), 'Conversion interrupted.', 'Critical')
|
||||||
self.emit(QtCore.SIGNAL("modeConvert"), True)
|
self.emit(QtCore.SIGNAL("modeConvert"), True)
|
||||||
|
|
||||||
|
def addResult(self, output):
|
||||||
|
self.emit(QtCore.SIGNAL("progressBarTick"))
|
||||||
|
self.workerOutput.append(output)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
self.emit(QtCore.SIGNAL("modeConvert"), False)
|
self.emit(QtCore.SIGNAL("modeConvert"), False)
|
||||||
profile = ProfileData.ProfileLabels[str(GUI.DeviceBox.currentText())]
|
profile = ProfileData.ProfileLabels[str(GUI.DeviceBox.currentText())]
|
||||||
@@ -228,7 +354,10 @@ class WorkerThread(QtCore.QThread):
|
|||||||
argv.append("--quality=1")
|
argv.append("--quality=1")
|
||||||
elif GUI.QualityBox.checkState() == 2:
|
elif GUI.QualityBox.checkState() == 2:
|
||||||
argv.append("--quality=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():
|
if GUI.ProcessingBox.isChecked():
|
||||||
argv.append("--noprocessing")
|
argv.append("--noprocessing")
|
||||||
if GUI.NoRotateBox.isChecked():
|
if GUI.NoRotateBox.isChecked():
|
||||||
@@ -245,13 +374,14 @@ class WorkerThread(QtCore.QThread):
|
|||||||
argv.append("--forcepng")
|
argv.append("--forcepng")
|
||||||
if GUI.WebtoonBox.isChecked():
|
if GUI.WebtoonBox.isChecked():
|
||||||
argv.append("--webtoon")
|
argv.append("--webtoon")
|
||||||
if float(GUIMain.GammaValue) > 0.09:
|
if float(GUI.GammaValue) > 0.09:
|
||||||
argv.append("--gamma=" + GUIMain.GammaValue)
|
# noinspection PyTypeChecker
|
||||||
|
argv.append("--gamma=" + GUI.GammaValue)
|
||||||
if str(GUI.FormatBox.currentText()) == 'CBZ':
|
if str(GUI.FormatBox.currentText()) == 'CBZ':
|
||||||
argv.append("--cbz-output")
|
argv.append("--cbz-output")
|
||||||
if str(GUI.FormatBox.currentText()) == 'MOBI':
|
if str(GUI.FormatBox.currentText()) == 'MOBI':
|
||||||
argv.append("--batchsplit")
|
argv.append("--batchsplit")
|
||||||
if GUIMain.currentMode > 2:
|
if GUI.currentMode > 2:
|
||||||
argv.append("--customwidth=" + str(GUI.customWidth.text()))
|
argv.append("--customwidth=" + str(GUI.customWidth.text()))
|
||||||
argv.append("--customheight=" + str(GUI.customHeight.text()))
|
argv.append("--customheight=" + str(GUI.customHeight.text()))
|
||||||
if GUI.ColorBox.isChecked():
|
if GUI.ColorBox.isChecked():
|
||||||
@@ -262,16 +392,18 @@ class WorkerThread(QtCore.QThread):
|
|||||||
currentJobs.append(unicode(GUI.JobList.item(i).text()))
|
currentJobs.append(unicode(GUI.JobList.item(i).text()))
|
||||||
GUI.JobList.clear()
|
GUI.JobList.clear()
|
||||||
for job in currentJobs:
|
for job in currentJobs:
|
||||||
time.sleep(0.5)
|
sleep(0.5)
|
||||||
if not self.conversionAlive:
|
if not self.conversionAlive:
|
||||||
self.clean()
|
self.clean()
|
||||||
return
|
return
|
||||||
self.errors = False
|
self.errors = False
|
||||||
self.emit(QtCore.SIGNAL("addMessage"), '<b>Source:</b> ' + job, 'info')
|
self.emit(QtCore.SIGNAL("addMessage"), '<b>Source:</b> ' + job, 'info')
|
||||||
if str(GUI.FormatBox.currentText()) == 'CBZ':
|
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:
|
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 = list(argv)
|
||||||
jobargv.append(job)
|
jobargv.append(job)
|
||||||
try:
|
try:
|
||||||
@@ -282,15 +414,19 @@ class WorkerThread(QtCore.QThread):
|
|||||||
self.clean()
|
self.clean()
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
|
GUI.progress.content = ''
|
||||||
self.errors = True
|
self.errors = True
|
||||||
self.emit(QtCore.SIGNAL("addMessage"), str(warn), 'warning')
|
self.emit(QtCore.SIGNAL("addMessage"), str(warn), 'warning')
|
||||||
self.emit(QtCore.SIGNAL("addMessage"), 'Failed to create output file!', '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:
|
except Exception as err:
|
||||||
|
GUI.progress.content = ''
|
||||||
self.errors = True
|
self.errors = True
|
||||||
type_, value_, traceback_ = sys.exc_info()
|
type_, value_, traceback_ = sys.exc_info()
|
||||||
self.emit(QtCore.SIGNAL("showDialog"), "Error during conversion %s:\n\n%s\n\nTraceback:\n%s"
|
self.emit(QtCore.SIGNAL("showDialog"), "Error during conversion %s:\n\n%s\n\nTraceback:\n%s"
|
||||||
% (jobargv[-1], str(err), traceback.format_tb(traceback_)))
|
% (jobargv[-1], str(err), traceback.format_tb(traceback_)))
|
||||||
self.emit(QtCore.SIGNAL("addMessage"), 'Failed to create EPUB!', 'error')
|
self.emit(QtCore.SIGNAL("addMessage"), 'Failed to create EPUB!', 'error')
|
||||||
|
self.emit(QtCore.SIGNAL("addTrayMessage"), 'Failed to create EPUB!', 'Critical')
|
||||||
if not self.conversionAlive:
|
if not self.conversionAlive:
|
||||||
for item in outputPath:
|
for item in outputPath:
|
||||||
if os.path.exists(item):
|
if os.path.exists(item):
|
||||||
@@ -298,117 +434,133 @@ class WorkerThread(QtCore.QThread):
|
|||||||
self.clean()
|
self.clean()
|
||||||
return
|
return
|
||||||
if not self.errors:
|
if not self.errors:
|
||||||
|
GUI.progress.content = ''
|
||||||
if str(GUI.FormatBox.currentText()) == 'CBZ':
|
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:
|
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':
|
if str(GUI.FormatBox.currentText()) == 'MOBI':
|
||||||
tomeNumber = 0
|
|
||||||
self.emit(QtCore.SIGNAL("progressBarTick"), 'status', 'Creating MOBI files')
|
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:
|
for item in outputPath:
|
||||||
tomeNumber += 1
|
worker = KindleGenThread(item)
|
||||||
if len(outputPath) > 1:
|
worker.signals.result.connect(self.addResult)
|
||||||
self.emit(QtCore.SIGNAL("addMessage"), 'Creating MOBI file (' + str(tomeNumber)
|
self.pool.start(worker)
|
||||||
+ '/' + str(len(outputPath)) + ')...', 'info')
|
self.pool.waitForDone()
|
||||||
else:
|
sleep(0.5)
|
||||||
self.emit(QtCore.SIGNAL("addMessage"), 'Creating MOBI file...', 'info')
|
self.kindlegenErrorCode = [0]
|
||||||
self.emit(QtCore.SIGNAL("progressBarTick"))
|
for errors in self.workerOutput:
|
||||||
try:
|
if errors[0] != 0:
|
||||||
self.kindlegenErrorCode = 0
|
self.kindlegenErrorCode = errors
|
||||||
if os.path.getsize(item) < 367001600:
|
break
|
||||||
output = Popen('kindlegen -locale en "' + item.encode(sys.getfilesystemencoding()) +
|
if not self.conversionAlive:
|
||||||
'"', stdout=PIPE, stderr=STDOUT, shell=True)
|
for item in outputPath:
|
||||||
for line in output.stdout:
|
if os.path.exists(item):
|
||||||
# ERROR: Generic error
|
os.remove(item)
|
||||||
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)
|
|
||||||
if os.path.exists(item.replace('.epub', '.mobi')):
|
if os.path.exists(item.replace('.epub', '.mobi')):
|
||||||
os.remove(item.replace('.epub', '.mobi'))
|
os.remove(item.replace('.epub', '.mobi'))
|
||||||
self.emit(QtCore.SIGNAL("addMessage"), 'KindleGen failed to create MOBI!', 'error')
|
self.clean()
|
||||||
if self.kindlegenErrorCode == 1 and self.kindlegenError:
|
return
|
||||||
self.emit(QtCore.SIGNAL("showDialog"), "KindleGen error:\n\n" + self.kindlegenError)
|
if self.kindlegenErrorCode[0] == 0:
|
||||||
if self.kindlegenErrorCode == 23026:
|
GUI.progress.content = ''
|
||||||
self.emit(QtCore.SIGNAL("addMessage"), 'Created EPUB file was too big.',
|
self.emit(QtCore.SIGNAL("addMessage"), 'Creating MOBI files... <b>Done!</b>', 'info', True)
|
||||||
'error')
|
self.emit(QtCore.SIGNAL("addMessage"), 'Cleaning MOBI files', 'info')
|
||||||
self.emit(QtCore.SIGNAL("addMessage"), 'EPUB file: ' + str(epubSize) + 'MB.'
|
GUI.progress.content = 'Cleaning MOBI files'
|
||||||
' Supported size: ~300MB.', 'error')
|
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:
|
else:
|
||||||
for item in outputPath:
|
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"))
|
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("addMessage"), '<b>All jobs completed.</b>', 'info')
|
||||||
|
self.emit(QtCore.SIGNAL("addTrayMessage"), 'All jobs completed.', 'Information')
|
||||||
self.emit(QtCore.SIGNAL("modeConvert"), True)
|
self.emit(QtCore.SIGNAL("modeConvert"), True)
|
||||||
|
|
||||||
|
|
||||||
# noinspection PyBroadException
|
class SystemTrayIcon(QtGui.QSystemTrayIcon):
|
||||||
class Ui_KCC(object):
|
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):
|
def selectDir(self):
|
||||||
if self.needClean:
|
if self.needClean:
|
||||||
self.needClean = False
|
self.needClean = False
|
||||||
GUI.JobList.clear()
|
GUI.JobList.clear()
|
||||||
# Dirty, dirty way but OS native QFileDialogs don't support directory multiselect
|
# 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.setFileMode(dirDialog.Directory)
|
||||||
dirDialog.setOption(dirDialog.ShowDirsOnly, True)
|
dirDialog.setOption(dirDialog.ShowDirsOnly, True)
|
||||||
dirDialog.setOption(dirDialog.DontUseNativeDialog, True)
|
dirDialog.setOption(dirDialog.DontUseNativeDialog, True)
|
||||||
@@ -428,6 +580,7 @@ class Ui_KCC(object):
|
|||||||
dname = dname.replace('/', '\\')
|
dname = dname.replace('/', '\\')
|
||||||
self.lastPath = os.path.abspath(os.path.join(unicode(dname), os.pardir))
|
self.lastPath = os.path.abspath(os.path.join(unicode(dname), os.pardir))
|
||||||
GUI.JobList.addItem(dname)
|
GUI.JobList.addItem(dname)
|
||||||
|
MW.setFocus()
|
||||||
|
|
||||||
def selectFile(self):
|
def selectFile(self):
|
||||||
if self.needClean:
|
if self.needClean:
|
||||||
@@ -435,17 +588,17 @@ class Ui_KCC(object):
|
|||||||
GUI.JobList.clear()
|
GUI.JobList.clear()
|
||||||
if self.UnRAR:
|
if self.UnRAR:
|
||||||
if self.sevenza:
|
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')
|
'*.cbz *.cbr *.cb7 *.zip *.rar *.7z *.pdf')
|
||||||
else:
|
else:
|
||||||
fnames = QtGui.QFileDialog.getOpenFileNames(MainWindow, 'Select file', self.lastPath,
|
fnames = QtGui.QFileDialog.getOpenFileNames(MW, 'Select file', self.lastPath,
|
||||||
'*.cbz *.cbr *.zip *.rar *.pdf')
|
'*.cbz *.cbr *.zip *.rar *.pdf')
|
||||||
else:
|
else:
|
||||||
if self.sevenza:
|
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')
|
'*.cbz *.cb7 *.zip *.7z *.pdf')
|
||||||
else:
|
else:
|
||||||
fnames = QtGui.QFileDialog.getOpenFileNames(MainWindow, 'Select file', self.lastPath,
|
fnames = QtGui.QFileDialog.getOpenFileNames(MW, 'Select file', self.lastPath,
|
||||||
'*.cbz *.zip *.pdf')
|
'*.cbz *.zip *.pdf')
|
||||||
for fname in fnames:
|
for fname in fnames:
|
||||||
if unicode(fname) != "":
|
if unicode(fname) != "":
|
||||||
@@ -457,9 +610,14 @@ class Ui_KCC(object):
|
|||||||
|
|
||||||
def modeBasic(self):
|
def modeBasic(self):
|
||||||
self.currentMode = 1
|
self.currentMode = 1
|
||||||
MainWindow.setMinimumSize(QtCore.QSize(420, 270))
|
if sys.platform.startswith('darwin'):
|
||||||
MainWindow.setMaximumSize(QtCore.QSize(420, 270))
|
MW.setMinimumSize(QtCore.QSize(420, 291))
|
||||||
MainWindow.resize(420, 270)
|
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.BasicModeButton.setStyleSheet('font-weight:Bold;')
|
||||||
GUI.AdvModeButton.setStyleSheet('font-weight:Normal;')
|
GUI.AdvModeButton.setStyleSheet('font-weight:Normal;')
|
||||||
GUI.FormatBox.setCurrentIndex(0)
|
GUI.FormatBox.setCurrentIndex(0)
|
||||||
@@ -478,9 +636,9 @@ class Ui_KCC(object):
|
|||||||
|
|
||||||
def modeAdvanced(self):
|
def modeAdvanced(self):
|
||||||
self.currentMode = 2
|
self.currentMode = 2
|
||||||
MainWindow.setMinimumSize(QtCore.QSize(420, 345))
|
MW.setMinimumSize(QtCore.QSize(420, 365))
|
||||||
MainWindow.setMaximumSize(QtCore.QSize(420, 345))
|
MW.setMaximumSize(QtCore.QSize(420, 365))
|
||||||
MainWindow.resize(420, 345)
|
MW.resize(420, 365)
|
||||||
GUI.BasicModeButton.setStyleSheet('font-weight:Normal;')
|
GUI.BasicModeButton.setStyleSheet('font-weight:Normal;')
|
||||||
GUI.AdvModeButton.setStyleSheet('font-weight:Bold;')
|
GUI.AdvModeButton.setStyleSheet('font-weight:Bold;')
|
||||||
GUI.FormatBox.setEnabled(True)
|
GUI.FormatBox.setEnabled(True)
|
||||||
@@ -495,9 +653,9 @@ class Ui_KCC(object):
|
|||||||
def modeExpert(self, KFA=False):
|
def modeExpert(self, KFA=False):
|
||||||
self.modeAdvanced()
|
self.modeAdvanced()
|
||||||
self.currentMode = 3
|
self.currentMode = 3
|
||||||
MainWindow.setMinimumSize(QtCore.QSize(420, 380))
|
MW.setMinimumSize(QtCore.QSize(420, 397))
|
||||||
MainWindow.setMaximumSize(QtCore.QSize(420, 380))
|
MW.setMaximumSize(QtCore.QSize(420, 397))
|
||||||
MainWindow.resize(420, 380)
|
MW.resize(420, 397)
|
||||||
GUI.OptionsExpert.setEnabled(True)
|
GUI.OptionsExpert.setEnabled(True)
|
||||||
if KFA:
|
if KFA:
|
||||||
GUI.ColorBox.setChecked(True)
|
GUI.ColorBox.setChecked(True)
|
||||||
@@ -561,7 +719,6 @@ class Ui_KCC(object):
|
|||||||
GUI.QualityBox.setChecked(False)
|
GUI.QualityBox.setChecked(False)
|
||||||
GUI.MangaBox.setEnabled(False)
|
GUI.MangaBox.setEnabled(False)
|
||||||
GUI.MangaBox.setChecked(False)
|
GUI.MangaBox.setChecked(False)
|
||||||
self.addMessage('If images will be too dark after conversion: Set <i>Gamma</i> to 1.0.', 'info')
|
|
||||||
else:
|
else:
|
||||||
if not GUI.ProcessingBox.isChecked():
|
if not GUI.ProcessingBox.isChecked():
|
||||||
GUI.NoRotateBox.setEnabled(True)
|
GUI.NoRotateBox.setEnabled(True)
|
||||||
@@ -634,9 +791,12 @@ class Ui_KCC(object):
|
|||||||
GUI.QualityBox.setChecked(False)
|
GUI.QualityBox.setChecked(False)
|
||||||
GUI.QualityBox.setEnabled(False)
|
GUI.QualityBox.setEnabled(False)
|
||||||
self.QualityBoxDisabled = True
|
self.QualityBoxDisabled = True
|
||||||
|
if value in [4, 5, 6, 7]:
|
||||||
|
if GUI.UpscaleBox.isEnabled():
|
||||||
|
GUI.UpscaleBox.setChecked(True)
|
||||||
else:
|
else:
|
||||||
if not GUI.WebtoonBox.isChecked() and not GUI.ProcessingBox.isChecked() \
|
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)
|
GUI.QualityBox.setEnabled(True)
|
||||||
self.QualityBoxDisabled = False
|
self.QualityBoxDisabled = False
|
||||||
|
|
||||||
@@ -675,7 +835,7 @@ class Ui_KCC(object):
|
|||||||
GUI.JobList.scrollToBottom()
|
GUI.JobList.scrollToBottom()
|
||||||
|
|
||||||
def showDialog(self, message):
|
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):
|
def updateProgressbar(self, new=False, status=False):
|
||||||
if new == "status":
|
if new == "status":
|
||||||
@@ -694,6 +854,7 @@ class Ui_KCC(object):
|
|||||||
self.conversionAlive = False
|
self.conversionAlive = False
|
||||||
self.worker.sync()
|
self.worker.sync()
|
||||||
else:
|
else:
|
||||||
|
self.progress.start()
|
||||||
if self.needClean:
|
if self.needClean:
|
||||||
self.needClean = False
|
self.needClean = False
|
||||||
GUI.JobList.clear()
|
GUI.JobList.clear()
|
||||||
@@ -743,8 +904,8 @@ class Ui_KCC(object):
|
|||||||
self.settings.sync()
|
self.settings.sync()
|
||||||
|
|
||||||
def handleMessage(self, message):
|
def handleMessage(self, message):
|
||||||
MainWindow.raise_()
|
MW.raise_()
|
||||||
MainWindow.activateWindow()
|
MW.activateWindow()
|
||||||
if not self.conversionAlive:
|
if not self.conversionAlive:
|
||||||
if self.needClean:
|
if self.needClean:
|
||||||
self.needClean = False
|
self.needClean = False
|
||||||
@@ -768,16 +929,18 @@ class Ui_KCC(object):
|
|||||||
else:
|
else:
|
||||||
self.addMessage('This file type is unsupported!', 'error')
|
self.addMessage('This file type is unsupported!', 'error')
|
||||||
|
|
||||||
def __init__(self, UI, KCC, APP):
|
def __init__(self, KCCAplication, KCCWindow):
|
||||||
global GUI, GUIMain, MainWindow
|
global APP, MW, GUI
|
||||||
GUI = UI
|
APP = KCCAplication
|
||||||
GUIMain = self
|
MW = KCCWindow
|
||||||
MainWindow = KCC
|
GUI = self
|
||||||
|
self.setupUi(MW)
|
||||||
# User settings will be reverted to default ones if were created in one of the following versions
|
# 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
|
# Empty string cover all versions before this system was implemented
|
||||||
purgeSettingsVersions = ['']
|
purgeSettingsVersions = ['']
|
||||||
self.icons = Icons()
|
self.icons = Icons()
|
||||||
self.webContent = WebContent()
|
self.webContent = WebContent()
|
||||||
|
self.tray = SystemTrayIcon()
|
||||||
self.settings = QtCore.QSettings('KindleComicConverter', 'KindleComicConverter')
|
self.settings = QtCore.QSettings('KindleComicConverter', 'KindleComicConverter')
|
||||||
self.settingsVersion = self.settings.value('settingsVersion', '', type=str)
|
self.settingsVersion = self.settings.value('settingsVersion', '', type=str)
|
||||||
if self.settingsVersion in purgeSettingsVersions:
|
if self.settingsVersion in purgeSettingsVersions:
|
||||||
@@ -793,6 +956,7 @@ class Ui_KCC(object):
|
|||||||
self.worker = WorkerThread()
|
self.worker = WorkerThread()
|
||||||
self.versionCheck = VersionThread()
|
self.versionCheck = VersionThread()
|
||||||
self.contentServer = WebServerThread()
|
self.contentServer = WebServerThread()
|
||||||
|
self.progress = ProgressThread()
|
||||||
self.conversionAlive = False
|
self.conversionAlive = False
|
||||||
self.needClean = True
|
self.needClean = True
|
||||||
self.QualityBoxDisabled = False
|
self.QualityBoxDisabled = False
|
||||||
@@ -800,17 +964,38 @@ class Ui_KCC(object):
|
|||||||
self.completedWork = {}
|
self.completedWork = {}
|
||||||
if sys.platform.startswith('darwin'):
|
if sys.platform.startswith('darwin'):
|
||||||
self.listFontSize = 11
|
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'):
|
elif sys.platform.startswith('linux'):
|
||||||
self.listFontSize = 8
|
self.listFontSize = 8
|
||||||
|
self.statusBarFontSize = 8
|
||||||
|
self.statusBarStyle = 'QLabel{padding-top:5px;padding-bottom:3px;border-top:2px solid #C2C7CB}'
|
||||||
|
self.tray.show()
|
||||||
else:
|
else:
|
||||||
self.listFontSize = 9
|
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>Welcome!</b>', 'info')
|
||||||
self.addMessage('<b>Remember:</b> All options have additional informations in tooltips.', 'info')
|
self.addMessage('<b>Remember:</b> All options have additional informations in tooltips.', 'info')
|
||||||
if self.firstStart:
|
if self.firstStart:
|
||||||
self.addMessage('Since you are using <b>KCC</b> for first time please see few '
|
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')
|
'<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
|
self.KindleGen = True
|
||||||
formats = ['MOBI', 'EPUB', 'CBZ']
|
formats = ['MOBI', 'EPUB', 'CBZ']
|
||||||
versionCheck = Popen('kindlegen -locale en', stdout=PIPE, stderr=STDOUT, shell=True)
|
versionCheck = Popen('kindlegen -locale en', stdout=PIPE, stderr=STDOUT, shell=True)
|
||||||
@@ -828,14 +1013,16 @@ class Ui_KCC(object):
|
|||||||
formats = ['EPUB', 'CBZ']
|
formats = ['EPUB', 'CBZ']
|
||||||
self.addMessage('Cannot find <a href="http://www.amazon.com/gp/feature.html?ie=UTF8&docId='
|
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')
|
'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:
|
if rarExitCode == 0 or rarExitCode == 7:
|
||||||
self.UnRAR = True
|
self.UnRAR = True
|
||||||
else:
|
else:
|
||||||
self.UnRAR = False
|
self.UnRAR = False
|
||||||
self.addMessage('Cannot find <a href="http://www.rarlab.com/rar_add.htm">UnRAR</a>!'
|
self.addMessage('Cannot find <a href="http://www.rarlab.com/rar_add.htm">UnRAR</a>!'
|
||||||
' Processing of CBR/RAR files will be disabled.', 'warning')
|
' 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:
|
if sevenzaExitCode == 0 or sevenzaExitCode == 7:
|
||||||
self.sevenza = True
|
self.sevenza = True
|
||||||
else:
|
else:
|
||||||
@@ -856,14 +1043,16 @@ class Ui_KCC(object):
|
|||||||
GUI.ProcessingBox.stateChanged.connect(self.toggleProcessingBox)
|
GUI.ProcessingBox.stateChanged.connect(self.toggleProcessingBox)
|
||||||
GUI.DeviceBox.activated.connect(self.changeDevice)
|
GUI.DeviceBox.activated.connect(self.changeDevice)
|
||||||
GUI.FormatBox.activated.connect(self.changeFormat)
|
GUI.FormatBox.activated.connect(self.changeFormat)
|
||||||
KCC.connect(self.worker, QtCore.SIGNAL("progressBarTick"), self.updateProgressbar)
|
MW.connect(self.worker, QtCore.SIGNAL("progressBarTick"), self.updateProgressbar)
|
||||||
KCC.connect(self.worker, QtCore.SIGNAL("modeConvert"), self.modeConvert)
|
MW.connect(self.worker, QtCore.SIGNAL("modeConvert"), self.modeConvert)
|
||||||
KCC.connect(self.worker, QtCore.SIGNAL("addMessage"), self.addMessage)
|
MW.connect(self.worker, QtCore.SIGNAL("addMessage"), self.addMessage)
|
||||||
KCC.connect(self.worker, QtCore.SIGNAL("showDialog"), self.showDialog)
|
MW.connect(self.worker, QtCore.SIGNAL("addTrayMessage"), self.tray.addTrayMessage)
|
||||||
KCC.connect(self.worker, QtCore.SIGNAL("hideProgressBar"), self.hideProgressBar)
|
MW.connect(self.worker, QtCore.SIGNAL("showDialog"), self.showDialog)
|
||||||
KCC.connect(self.versionCheck, QtCore.SIGNAL("addMessage"), self.addMessage)
|
MW.connect(self.worker, QtCore.SIGNAL("hideProgressBar"), self.hideProgressBar)
|
||||||
KCC.connect(self.contentServer, QtCore.SIGNAL("addMessage"), self.addMessage)
|
MW.connect(self.versionCheck, QtCore.SIGNAL("addMessage"), self.addMessage)
|
||||||
KCC.closeEvent = self.saveSettings
|
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:
|
for f in formats:
|
||||||
GUI.FormatBox.addItem(eval('self.icons.' + f + 'Format'), f)
|
GUI.FormatBox.addItem(eval('self.icons.' + f + 'Format'), f)
|
||||||
@@ -907,3 +1096,6 @@ class Ui_KCC(object):
|
|||||||
self.contentServer.start()
|
self.contentServer.start()
|
||||||
self.hideProgressBar()
|
self.hideProgressBar()
|
||||||
self.worker.sync()
|
self.worker.sync()
|
||||||
|
MW.setWindowTitle("Kindle Comic Converter " + __version__)
|
||||||
|
MW.show()
|
||||||
|
MW.raise_()
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
# Form implementation generated from reading ui file 'KCC.ui'
|
# 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
|
# by: PyQt4 UI code generator 4.10.3
|
||||||
#
|
#
|
||||||
# WARNING! All changes made in this file will be lost!
|
# WARNING! All changes made in this file will be lost!
|
||||||
@@ -26,13 +26,12 @@ except AttributeError:
|
|||||||
class Ui_KCC(object):
|
class Ui_KCC(object):
|
||||||
def setupUi(self, KCC):
|
def setupUi(self, KCC):
|
||||||
KCC.setObjectName(_fromUtf8("KCC"))
|
KCC.setObjectName(_fromUtf8("KCC"))
|
||||||
KCC.resize(420, 380)
|
KCC.resize(420, 397)
|
||||||
KCC.setMinimumSize(QtCore.QSize(420, 380))
|
KCC.setMinimumSize(QtCore.QSize(420, 397))
|
||||||
KCC.setMaximumSize(QtCore.QSize(420, 380))
|
KCC.setMaximumSize(QtCore.QSize(420, 397))
|
||||||
font = QtGui.QFont()
|
font = QtGui.QFont()
|
||||||
font.setPointSize(9)
|
font.setPointSize(9)
|
||||||
KCC.setFont(font)
|
KCC.setFont(font)
|
||||||
KCC.setFocusPolicy(QtCore.Qt.NoFocus)
|
|
||||||
icon = QtGui.QIcon()
|
icon = QtGui.QIcon()
|
||||||
icon.addPixmap(QtGui.QPixmap(_fromUtf8(":/Icon/icons/comic2ebook.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
icon.addPixmap(QtGui.QPixmap(_fromUtf8(":/Icon/icons/comic2ebook.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||||
KCC.setWindowIcon(icon)
|
KCC.setWindowIcon(icon)
|
||||||
@@ -247,6 +246,14 @@ class Ui_KCC(object):
|
|||||||
self.customHeight.setObjectName(_fromUtf8("customHeight"))
|
self.customHeight.setObjectName(_fromUtf8("customHeight"))
|
||||||
self.gridLayout_2.addWidget(self.customHeight, 0, 3, 1, 1)
|
self.gridLayout_2.addWidget(self.customHeight, 0, 3, 1, 1)
|
||||||
KCC.setCentralWidget(self.Form)
|
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 = QtGui.QAction(KCC)
|
||||||
self.ActionBasic.setCheckable(True)
|
self.ActionBasic.setCheckable(True)
|
||||||
self.ActionBasic.setChecked(False)
|
self.ActionBasic.setChecked(False)
|
||||||
@@ -298,9 +305,7 @@ class Ui_KCC(object):
|
|||||||
self.RotateBox.setText(_translate("KCC", "Horizontal mode", None))
|
self.RotateBox.setText(_translate("KCC", "Horizontal mode", None))
|
||||||
self.BasicModeButton.setText(_translate("KCC", "Basic", None))
|
self.BasicModeButton.setText(_translate("KCC", "Basic", None))
|
||||||
self.AdvModeButton.setText(_translate("KCC", "Advanced", 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.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.setToolTip(_translate("KCC", "Do not convert images to grayscale.", None))
|
||||||
self.ColorBox.setText(_translate("KCC", "Color mode", None))
|
self.ColorBox.setText(_translate("KCC", "Color mode", None))
|
||||||
self.wLabel.setToolTip(_translate("KCC", "Resolution of target device.", None))
|
self.wLabel.setToolTip(_translate("KCC", "Resolution of target device.", None))
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
# Form implementation generated from reading ui file 'KCC-Linux.ui'
|
# 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
|
# by: PyQt4 UI code generator 4.10.3
|
||||||
#
|
#
|
||||||
# WARNING! All changes made in this file will be lost!
|
# WARNING! All changes made in this file will be lost!
|
||||||
@@ -26,13 +26,12 @@ except AttributeError:
|
|||||||
class Ui_KCC(object):
|
class Ui_KCC(object):
|
||||||
def setupUi(self, KCC):
|
def setupUi(self, KCC):
|
||||||
KCC.setObjectName(_fromUtf8("KCC"))
|
KCC.setObjectName(_fromUtf8("KCC"))
|
||||||
KCC.resize(420, 380)
|
KCC.resize(420, 397)
|
||||||
KCC.setMinimumSize(QtCore.QSize(420, 380))
|
KCC.setMinimumSize(QtCore.QSize(420, 397))
|
||||||
KCC.setMaximumSize(QtCore.QSize(420, 380))
|
KCC.setMaximumSize(QtCore.QSize(420, 397))
|
||||||
font = QtGui.QFont()
|
font = QtGui.QFont()
|
||||||
font.setPointSize(9)
|
font.setPointSize(9)
|
||||||
KCC.setFont(font)
|
KCC.setFont(font)
|
||||||
KCC.setFocusPolicy(QtCore.Qt.NoFocus)
|
|
||||||
icon = QtGui.QIcon()
|
icon = QtGui.QIcon()
|
||||||
icon.addPixmap(QtGui.QPixmap(_fromUtf8(":/Icon/icons/comic2ebook.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
icon.addPixmap(QtGui.QPixmap(_fromUtf8(":/Icon/icons/comic2ebook.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||||
KCC.setWindowIcon(icon)
|
KCC.setWindowIcon(icon)
|
||||||
@@ -317,6 +316,14 @@ class Ui_KCC(object):
|
|||||||
self.customHeight.setObjectName(_fromUtf8("customHeight"))
|
self.customHeight.setObjectName(_fromUtf8("customHeight"))
|
||||||
self.gridLayout_2.addWidget(self.customHeight, 0, 3, 1, 1)
|
self.gridLayout_2.addWidget(self.customHeight, 0, 3, 1, 1)
|
||||||
KCC.setCentralWidget(self.Form)
|
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 = QtGui.QAction(KCC)
|
||||||
self.ActionBasic.setCheckable(True)
|
self.ActionBasic.setCheckable(True)
|
||||||
self.ActionBasic.setChecked(False)
|
self.ActionBasic.setChecked(False)
|
||||||
@@ -367,9 +374,7 @@ class Ui_KCC(object):
|
|||||||
self.RotateBox.setText(_translate("KCC", "Horizontal mode", None))
|
self.RotateBox.setText(_translate("KCC", "Horizontal mode", None))
|
||||||
self.BasicModeButton.setText(_translate("KCC", "Basic", None))
|
self.BasicModeButton.setText(_translate("KCC", "Basic", None))
|
||||||
self.AdvModeButton.setText(_translate("KCC", "Advanced", 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.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.setToolTip(_translate("KCC", "Do not convert images to grayscale.", None))
|
||||||
self.ColorBox.setText(_translate("KCC", "Color mode", None))
|
self.ColorBox.setText(_translate("KCC", "Color mode", None))
|
||||||
self.wLabel.setToolTip(_translate("KCC", "Resolution of target device.", None))
|
self.wLabel.setToolTip(_translate("KCC", "Resolution of target device.", None))
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
# Form implementation generated from reading ui file 'KCC-OSX.ui'
|
# 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
|
# by: PyQt4 UI code generator 4.10.3
|
||||||
#
|
#
|
||||||
# WARNING! All changes made in this file will be lost!
|
# WARNING! All changes made in this file will be lost!
|
||||||
@@ -26,13 +26,12 @@ except AttributeError:
|
|||||||
class Ui_KCC(object):
|
class Ui_KCC(object):
|
||||||
def setupUi(self, KCC):
|
def setupUi(self, KCC):
|
||||||
KCC.setObjectName(_fromUtf8("KCC"))
|
KCC.setObjectName(_fromUtf8("KCC"))
|
||||||
KCC.resize(420, 380)
|
KCC.resize(420, 397)
|
||||||
KCC.setMinimumSize(QtCore.QSize(420, 380))
|
KCC.setMinimumSize(QtCore.QSize(420, 397))
|
||||||
KCC.setMaximumSize(QtCore.QSize(420, 380))
|
KCC.setMaximumSize(QtCore.QSize(420, 397))
|
||||||
font = QtGui.QFont()
|
font = QtGui.QFont()
|
||||||
font.setPointSize(9)
|
font.setPointSize(9)
|
||||||
KCC.setFont(font)
|
KCC.setFont(font)
|
||||||
KCC.setFocusPolicy(QtCore.Qt.NoFocus)
|
|
||||||
icon = QtGui.QIcon()
|
icon = QtGui.QIcon()
|
||||||
icon.addPixmap(QtGui.QPixmap(_fromUtf8(":/Icon/icons/comic2ebook.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
icon.addPixmap(QtGui.QPixmap(_fromUtf8(":/Icon/icons/comic2ebook.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||||
KCC.setWindowIcon(icon)
|
KCC.setWindowIcon(icon)
|
||||||
@@ -340,6 +339,14 @@ class Ui_KCC(object):
|
|||||||
self.customHeight.setObjectName(_fromUtf8("customHeight"))
|
self.customHeight.setObjectName(_fromUtf8("customHeight"))
|
||||||
self.gridLayout_2.addWidget(self.customHeight, 0, 3, 1, 1)
|
self.gridLayout_2.addWidget(self.customHeight, 0, 3, 1, 1)
|
||||||
KCC.setCentralWidget(self.Form)
|
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 = QtGui.QAction(KCC)
|
||||||
self.ActionBasic.setCheckable(True)
|
self.ActionBasic.setCheckable(True)
|
||||||
self.ActionBasic.setChecked(False)
|
self.ActionBasic.setChecked(False)
|
||||||
@@ -385,9 +392,7 @@ class Ui_KCC(object):
|
|||||||
self.RotateBox.setText(_translate("KCC", "Horizontal mode", None))
|
self.RotateBox.setText(_translate("KCC", "Horizontal mode", None))
|
||||||
self.BasicModeButton.setText(_translate("KCC", "Basic", None))
|
self.BasicModeButton.setText(_translate("KCC", "Basic", None))
|
||||||
self.AdvModeButton.setText(_translate("KCC", "Advanced", 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.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.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.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))
|
self.wLabel.setToolTip(_translate("KCC", "<html><head/><body><p><span style=\" font-size:12pt;\">Resolution of target device.</span></p></body></html>", None))
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
__version__ = '3.5'
|
__version__ = '3.6.2'
|
||||||
__license__ = 'ISC'
|
__license__ = 'ISC'
|
||||||
__copyright__ = '2012-2013, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@vulturis.eu>'
|
__copyright__ = '2012-2013, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@vulturis.eu>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
@@ -23,7 +23,21 @@ import os
|
|||||||
import zipfile
|
import zipfile
|
||||||
import rarfile
|
import rarfile
|
||||||
import locale
|
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:
|
class CBxArchive:
|
||||||
@@ -91,9 +105,10 @@ class CBxArchive:
|
|||||||
elif self.compressor == '7z':
|
elif self.compressor == '7z':
|
||||||
self.extractCB7(targetdir)
|
self.extractCB7(targetdir)
|
||||||
adir = os.listdir(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])):
|
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])):
|
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]))
|
os.rmdir(os.path.join(targetdir, adir[0]))
|
||||||
return targetdir
|
return targetdir
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python2
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (c) 2012-2013 Ciro Mattia Gonano <ciromattia@gmail.com>
|
# 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
|
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
# PERFORMANCE OF THIS SOFTWARE.
|
# PERFORMANCE OF THIS SOFTWARE.
|
||||||
#
|
#
|
||||||
__version__ = '3.5'
|
__version__ = '3.6.2'
|
||||||
__license__ = 'ISC'
|
__license__ = 'ISC'
|
||||||
__copyright__ = '2012-2013, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@vulturis.eu>'
|
__copyright__ = '2012-2013, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@vulturis.eu>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import tempfile
|
|
||||||
import re
|
import re
|
||||||
import stat
|
import stat
|
||||||
import string
|
import string
|
||||||
|
import unicodedata
|
||||||
|
from tempfile import mkdtemp
|
||||||
from shutil import move, copyfile, copytree, rmtree, make_archive
|
from shutil import move, copyfile, copytree, rmtree, make_archive
|
||||||
from optparse import OptionParser, OptionGroup
|
from optparse import OptionParser, OptionGroup
|
||||||
from multiprocessing import Pool, freeze_support
|
from multiprocessing import Pool, freeze_support
|
||||||
|
from xml.dom.minidom import parse
|
||||||
|
from uuid import uuid4
|
||||||
try:
|
try:
|
||||||
from PyQt4 import QtCore
|
from PyQt4 import QtCore
|
||||||
except ImportError:
|
except ImportError:
|
||||||
@@ -102,9 +105,9 @@ def buildHTML(path, imgfile):
|
|||||||
elif noHorizontalPV and not noVerticalPV:
|
elif noHorizontalPV and not noVerticalPV:
|
||||||
if rotatedPage:
|
if rotatedPage:
|
||||||
if options.righttoleft:
|
if options.righttoleft:
|
||||||
order = [2, 1]
|
|
||||||
else:
|
|
||||||
order = [1, 2]
|
order = [1, 2]
|
||||||
|
else:
|
||||||
|
order = [2, 1]
|
||||||
else:
|
else:
|
||||||
order = [1, 2]
|
order = [1, 2]
|
||||||
boxes = ["BoxT", "BoxB"]
|
boxes = ["BoxT", "BoxB"]
|
||||||
@@ -126,35 +129,48 @@ def buildHTML(path, imgfile):
|
|||||||
"}'></a></div>\n"])
|
"}'></a></div>\n"])
|
||||||
if options.quality == 2:
|
if options.quality == 2:
|
||||||
imgfilepv = string.split(imgfile, ".")
|
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[0] += "_kcchq"
|
||||||
imgfilepv = string.join(imgfilepv, ".")
|
imgfilepv = string.join(imgfilepv, ".")
|
||||||
else:
|
else:
|
||||||
imgfilepv = imgfile
|
imgfilepv = imgfile
|
||||||
if "_kccx" in filename[0]:
|
if "_kccxl" in filename[0]:
|
||||||
xy = string.split(filename[0], "_kccx")[1]
|
borders = filename[0].split('_kccxl')[1]
|
||||||
x = string.split(xy, "_kccy")[0].lstrip("0")
|
borders = re.findall('[0-9]{1,6}', borders)
|
||||||
y = string.split(xy, "_kccy")[1].lstrip("0")
|
xl = borders[0].lstrip("0")
|
||||||
if x != "":
|
yu = borders[1].lstrip("0")
|
||||||
x = "-" + str(float(x)/100) + "%"
|
xr = borders[2].lstrip("0")
|
||||||
|
yd = borders[3].lstrip("0")
|
||||||
|
if xl != "":
|
||||||
|
xl = "-" + str(float(xl)/100) + "%"
|
||||||
else:
|
else:
|
||||||
x = "0%"
|
xl = "0%"
|
||||||
if y != "":
|
if xr != "":
|
||||||
y = "-" + str(float(y)/100) + "%"
|
xr = "-" + str(float(xr)/100) + "%"
|
||||||
else:
|
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:
|
else:
|
||||||
x = "0%"
|
xl = "0%"
|
||||||
y = "0%"
|
yu = "0%"
|
||||||
boxStyles = {"BoxTL": "left:" + x + ";top:" + y + ";",
|
xr = "0%"
|
||||||
"BoxTR": "right:" + x + ";top:" + y + ";",
|
yd = "0%"
|
||||||
"BoxBL": "left:" + x + ";bottom:" + y + ";",
|
boxStyles = {"BoxTL": "left:" + xl + ";top:" + yu + ";",
|
||||||
"BoxBR": "right:" + x + ";bottom:" + y + ";",
|
"BoxTR": "right:" + xr + ";top:" + yu + ";",
|
||||||
"BoxT": "left:-25%;top:" + y + ";",
|
"BoxBL": "left:" + xl + ";bottom:" + yd + ";",
|
||||||
"BoxB": "left:-25%;bottom:" + y + ";",
|
"BoxBR": "right:" + xr + ";bottom:" + yd + ";",
|
||||||
"BoxL": "left:" + x + ";top:-25%;",
|
"BoxT": "left:-25%;top:" + yu + ";",
|
||||||
"BoxR": "right:" + x + ";top:-25%;",
|
"BoxB": "left:-25%;bottom:" + yd + ";",
|
||||||
"BoxC": "right:-25%;top:-25%;"
|
"BoxL": "left:" + xl + ";top:-25%;",
|
||||||
|
"BoxR": "right:" + xr + ";top:-25%;",
|
||||||
|
"BoxC": "left:-25%;top:-25%;"
|
||||||
}
|
}
|
||||||
for box in boxes:
|
for box in boxes:
|
||||||
f.writelines(["<div id=\"" + box + "-Panel-Parent\" class=\"target-mag-parent\"><div id=\"",
|
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):
|
def buildNCX(dstdir, title, chapters):
|
||||||
from uuid import uuid4
|
|
||||||
options.uuid = str(uuid4())
|
options.uuid = str(uuid4())
|
||||||
options.uuid = options.uuid.encode('utf-8')
|
options.uuid = options.uuid.encode('utf-8')
|
||||||
ncxfile = os.path.join(dstdir, 'OEBPS', 'toc.ncx')
|
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",
|
"xmlns:dc=\"http://purl.org/dc/elements/1.1/\">\n",
|
||||||
"<dc:title>", title.encode('utf-8'), "</dc:title>\n",
|
"<dc:title>", title.encode('utf-8'), "</dc:title>\n",
|
||||||
"<dc:language>en-US</dc:language>\n",
|
"<dc:language>en-US</dc:language>\n",
|
||||||
"<dc:identifier id=\"BookID\" opf:scheme=\"UUID\">", options.uuid, "</dc:identifier>\n",
|
"<dc:identifier id=\"BookID\" opf:scheme=\"UUID\">", options.uuid, "</dc:identifier>\n"])
|
||||||
"<dc:Creator>KCC</dc:Creator>\n",
|
for author in options.authors:
|
||||||
"<meta name=\"generator\" content=\"KindleComicConverter-" + __version__ + "\"/>\n",
|
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=\"RegionMagnification\" content=\"true\"/>\n",
|
||||||
"<meta name=\"region-mag\" content=\"true\"/>\n",
|
"<meta name=\"region-mag\" content=\"true\"/>\n",
|
||||||
"<meta name=\"cover\" content=\"cover\"/>\n",
|
"<meta name=\"cover\" content=\"cover\"/>\n",
|
||||||
@@ -292,17 +308,24 @@ def getImageFileName(imgfile):
|
|||||||
return filename
|
return filename
|
||||||
|
|
||||||
|
|
||||||
def applyImgOptimization(img, opt, overrideQuality=5):
|
def applyImgOptimization(img, opt, hqImage=None):
|
||||||
img.getImageFill(opt.webtoon)
|
if not img.fill:
|
||||||
|
img.getImageFill(opt.webtoon)
|
||||||
if not opt.webtoon:
|
if not opt.webtoon:
|
||||||
img.cropWhiteSpace(10.0)
|
img.cropWhiteSpace(10.0)
|
||||||
if opt.cutpagenumbers and not opt.webtoon:
|
if opt.cutpagenumbers and not opt.webtoon:
|
||||||
img.cutPageNumber()
|
img.cutPageNumber()
|
||||||
img.optimizeImage(opt.gamma)
|
img.optimizeImage(opt.gamma)
|
||||||
if overrideQuality != 5:
|
if hqImage:
|
||||||
img.resizeImage(opt.upscale, opt.stretch, opt.bordersColor, overrideQuality)
|
img.resizeImage(opt.upscale, opt.stretch, opt.bordersColor, 0)
|
||||||
|
img.calculateBorder(hqImage, True)
|
||||||
else:
|
else:
|
||||||
img.resizeImage(opt.upscale, opt.stretch, opt.bordersColor, opt.quality)
|
img.resizeImage(opt.upscale, opt.stretch, opt.bordersColor, opt.quality)
|
||||||
|
if opt.panelview:
|
||||||
|
if opt.quality == 0:
|
||||||
|
img.calculateBorder(img)
|
||||||
|
elif opt.quality == 1:
|
||||||
|
img.calculateBorder(img, True)
|
||||||
if opt.forcepng and not opt.forcecolor:
|
if opt.forcepng and not opt.forcecolor:
|
||||||
img.quantizeImage()
|
img.quantizeImage()
|
||||||
|
|
||||||
@@ -374,21 +397,21 @@ def fileImgProcess(work):
|
|||||||
applyImgOptimization(img1, opt)
|
applyImgOptimization(img1, opt)
|
||||||
img1.saveToDir(dirpath, opt.forcepng, opt.forcecolor, wipe)
|
img1.saveToDir(dirpath, opt.forcepng, opt.forcecolor, wipe)
|
||||||
if opt.quality == 2:
|
if opt.quality == 2:
|
||||||
img3 = image.ComicPage(split[0], opt.profileData)
|
img0b = image.ComicPage(split[0], opt.profileData, img0.fill)
|
||||||
applyImgOptimization(img3, opt, 0)
|
applyImgOptimization(img0b, opt, img0)
|
||||||
img3.saveToDir(dirpath, opt.forcepng, opt.forcecolor, True)
|
img0b.saveToDir(dirpath, opt.forcepng, opt.forcecolor, True)
|
||||||
img4 = image.ComicPage(split[1], opt.profileData)
|
img1b = image.ComicPage(split[1], opt.profileData, img1.fill)
|
||||||
applyImgOptimization(img4, opt, 0)
|
applyImgOptimization(img1b, opt, img1)
|
||||||
img4.saveToDir(dirpath, opt.forcepng, opt.forcecolor, True)
|
img1b.saveToDir(dirpath, opt.forcepng, opt.forcecolor, True)
|
||||||
else:
|
else:
|
||||||
applyImgOptimization(img, opt)
|
applyImgOptimization(img, opt)
|
||||||
img.saveToDir(dirpath, opt.forcepng, opt.forcecolor, wipe)
|
img.saveToDir(dirpath, opt.forcepng, opt.forcecolor, wipe)
|
||||||
if opt.quality == 2:
|
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:
|
if img.rotated:
|
||||||
img2.image = img2.image.rotate(90)
|
img2.image = img2.image.rotate(90)
|
||||||
img2.rotated = True
|
img2.rotated = True
|
||||||
applyImgOptimization(img2, opt, 0)
|
applyImgOptimization(img2, opt, img)
|
||||||
img2.saveToDir(dirpath, opt.forcepng, opt.forcecolor, True)
|
img2.saveToDir(dirpath, opt.forcepng, opt.forcecolor, True)
|
||||||
except StandardError:
|
except StandardError:
|
||||||
return str(sys.exc_info()[1])
|
return str(sys.exc_info()[1])
|
||||||
@@ -535,7 +558,7 @@ def getWorkFolder(afile):
|
|||||||
if len(afile) > 240:
|
if len(afile) > 240:
|
||||||
raise UserWarning("Path is too long.")
|
raise UserWarning("Path is too long.")
|
||||||
if os.path.isdir(afile):
|
if os.path.isdir(afile):
|
||||||
workdir = tempfile.mkdtemp('', 'KCC-TMP-')
|
workdir = mkdtemp('', 'KCC-TMP-')
|
||||||
try:
|
try:
|
||||||
os.rmdir(workdir) # needed for copytree() fails if dst already exists
|
os.rmdir(workdir) # needed for copytree() fails if dst already exists
|
||||||
fullPath = os.path.join(workdir, 'OEBPS', 'Images')
|
fullPath = os.path.join(workdir, 'OEBPS', 'Images')
|
||||||
@@ -554,7 +577,7 @@ def getWorkFolder(afile):
|
|||||||
rmtree(path, True)
|
rmtree(path, True)
|
||||||
raise UserWarning("Failed to extract images.")
|
raise UserWarning("Failed to extract images.")
|
||||||
else:
|
else:
|
||||||
workdir = tempfile.mkdtemp('', 'KCC-TMP-')
|
workdir = mkdtemp('', 'KCC-TMP-')
|
||||||
cbx = cbxarchive.CBxArchive(afile)
|
cbx = cbxarchive.CBxArchive(afile)
|
||||||
if cbx.isCbxFile():
|
if cbx.isCbxFile():
|
||||||
try:
|
try:
|
||||||
@@ -573,9 +596,59 @@ def getWorkFolder(afile):
|
|||||||
return path
|
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):
|
def slugify(value):
|
||||||
# Normalizes string, converts to lowercase, removes non-alpha characters and converts spaces to hyphens.
|
# Normalizes string, converts to lowercase, removes non-alpha characters and converts spaces to hyphens.
|
||||||
import unicodedata
|
|
||||||
if isinstance(value, str):
|
if isinstance(value, str):
|
||||||
#noinspection PyArgumentList
|
#noinspection PyArgumentList
|
||||||
value = unicodedata.normalize('NFKD', unicode(value, 'latin1')).encode('ascii', 'ignore')
|
value = unicodedata.normalize('NFKD', unicode(value, 'latin1')).encode('ascii', 'ignore')
|
||||||
@@ -631,7 +704,7 @@ def getDirectorySize(start_path='.'):
|
|||||||
|
|
||||||
|
|
||||||
def createNewTome():
|
def createNewTome():
|
||||||
tomePathRoot = tempfile.mkdtemp('', 'KCC-TMP-')
|
tomePathRoot = mkdtemp('', 'KCC-TMP-')
|
||||||
tomePath = os.path.join(tomePathRoot, 'OEBPS', 'Images')
|
tomePath = os.path.join(tomePathRoot, 'OEBPS', 'Images')
|
||||||
os.makedirs(tomePath)
|
os.makedirs(tomePath)
|
||||||
return tomePath, tomePathRoot
|
return tomePath, tomePathRoot
|
||||||
@@ -853,6 +926,7 @@ def main(argv=None, qtGUI=None):
|
|||||||
parser.print_help()
|
parser.print_help()
|
||||||
return
|
return
|
||||||
path = getWorkFolder(args[0])
|
path = getWorkFolder(args[0])
|
||||||
|
checkComicInfo(path + "/OEBPS/Images/", args[0])
|
||||||
if options.webtoon:
|
if options.webtoon:
|
||||||
if GUI:
|
if GUI:
|
||||||
GUI.emit(QtCore.SIGNAL("progressBarTick"), 'status', 'Splitting images')
|
GUI.emit(QtCore.SIGNAL("progressBarTick"), 'status', 'Splitting images')
|
||||||
@@ -879,19 +953,13 @@ def main(argv=None, qtGUI=None):
|
|||||||
GUI.emit(QtCore.SIGNAL("progressBarTick"), 'status', 'Compressing CBZ files')
|
GUI.emit(QtCore.SIGNAL("progressBarTick"), 'status', 'Compressing CBZ files')
|
||||||
else:
|
else:
|
||||||
GUI.emit(QtCore.SIGNAL("progressBarTick"), 'status', 'Compressing EPUB files')
|
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:
|
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:
|
if len(tomes) > 1:
|
||||||
tomeNumber += 1
|
tomeNumber += 1
|
||||||
options.title = barePath + ' ' + str(tomeNumber)
|
options.title = options.baseTitle + ' [' + str(tomeNumber) + '/' + str(len(tomes)) + ']'
|
||||||
elif options.title == 'defaulttitle':
|
|
||||||
options.title = barePath
|
|
||||||
if options.cbzoutput:
|
if options.cbzoutput:
|
||||||
# if CBZ output wanted, compress all images and return filepath
|
# if CBZ output wanted, compress all images and return filepath
|
||||||
print "\nCreating CBZ file..."
|
print "\nCreating CBZ file..."
|
||||||
@@ -911,6 +979,8 @@ def main(argv=None, qtGUI=None):
|
|||||||
make_archive(tome + '_comic', 'zip', tome)
|
make_archive(tome + '_comic', 'zip', tome)
|
||||||
move(tome + '_comic.zip', filepath[-1])
|
move(tome + '_comic.zip', filepath[-1])
|
||||||
rmtree(tome, True)
|
rmtree(tome, True)
|
||||||
|
if GUI:
|
||||||
|
GUI.emit(QtCore.SIGNAL("progressBarTick"))
|
||||||
return filepath
|
return filepath
|
||||||
|
|
||||||
|
|
||||||
@@ -968,6 +1038,9 @@ def checkOptions():
|
|||||||
if options.profile == 'KFA' and (options.customwidth == 0 or options.customheight == 0):
|
if options.profile == 'KFA' and (options.customwidth == 0 or options.customheight == 0):
|
||||||
print "ERROR: Kindle for Android profile require --customwidth and --customheight options!"
|
print "ERROR: Kindle for Android profile require --customwidth and --customheight options!"
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
# CBZ files on Kindle DX/DXG support higher resolution
|
||||||
|
if options.profile == 'KDX' and options.cbzoutput:
|
||||||
|
options.customheight = 1200
|
||||||
# Override profile data
|
# Override profile data
|
||||||
if options.customwidth != 0 or options.customheight != 0:
|
if options.customwidth != 0 or options.customheight != 0:
|
||||||
X = image.ProfileData.Profiles[options.profile][1][0]
|
X = image.ProfileData.Profiles[options.profile][1][0]
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python2
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (c) 2012-2013 Ciro Mattia Gonano <ciromattia@gmail.com>
|
# 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
|
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
# PERFORMANCE OF THIS SOFTWARE.
|
# PERFORMANCE OF THIS SOFTWARE.
|
||||||
#
|
#
|
||||||
__version__ = '3.5'
|
__version__ = '3.6.2'
|
||||||
__license__ = 'ISC'
|
__license__ = 'ISC'
|
||||||
__copyright__ = '2012-2013, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@vulturis.eu>'
|
__copyright__ = '2012-2013, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@vulturis.eu>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
@@ -33,9 +33,25 @@ try:
|
|||||||
from PIL import Image, ImageStat
|
from PIL import Image, ImageStat
|
||||||
if tuple(map(int, ('2.2.1'.split(".")))) > tuple(map(int, (Image.PILLOW_VERSION.split(".")))):
|
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!"
|
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)
|
exit(1)
|
||||||
except ImportError:
|
except ImportError:
|
||||||
print "ERROR: Pillow is not installed!"
|
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)
|
exit(1)
|
||||||
try:
|
try:
|
||||||
from PyQt4 import QtCore
|
from PyQt4 import QtCore
|
||||||
|
|||||||
245
kcc/image.py
245
kcc/image.py
@@ -21,14 +21,31 @@ __copyright__ = '2012-2013, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jas
|
|||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
from sys import platform
|
||||||
try:
|
try:
|
||||||
# noinspection PyUnresolvedReferences
|
# noinspection PyUnresolvedReferences
|
||||||
from PIL import Image, ImageOps, ImageStat, ImageChops
|
from PIL import Image, ImageOps, ImageStat, ImageChops
|
||||||
if tuple(map(int, ('2.2.1'.split(".")))) > tuple(map(int, (Image.PILLOW_VERSION.split(".")))):
|
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!"
|
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)
|
exit(1)
|
||||||
except ImportError:
|
except ImportError:
|
||||||
print "ERROR: Pillow is not installed!"
|
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)
|
exit(1)
|
||||||
|
|
||||||
|
|
||||||
@@ -133,7 +150,7 @@ class ProfileData:
|
|||||||
|
|
||||||
|
|
||||||
class ComicPage:
|
class ComicPage:
|
||||||
def __init__(self, source, device):
|
def __init__(self, source, device, fill=None):
|
||||||
try:
|
try:
|
||||||
self.profile_label, self.size, self.palette, self.gamma, self.panelviewsize = device
|
self.profile_label, self.size, self.palette, self.gamma, self.panelviewsize = device
|
||||||
except KeyError:
|
except KeyError:
|
||||||
@@ -163,7 +180,10 @@ class ComicPage:
|
|||||||
self.border = None
|
self.border = None
|
||||||
self.noHPV = None
|
self.noHPV = None
|
||||||
self.noVPV = None
|
self.noVPV = None
|
||||||
self.fill = None
|
if fill:
|
||||||
|
self.fill = fill
|
||||||
|
else:
|
||||||
|
self.fill = None
|
||||||
|
|
||||||
def saveToDir(self, targetdir, forcepng, color, wipe):
|
def saveToDir(self, targetdir, forcepng, color, wipe):
|
||||||
try:
|
try:
|
||||||
@@ -181,7 +201,8 @@ class ComicPage:
|
|||||||
if self.noVPV:
|
if self.noVPV:
|
||||||
suffix += "_kccnv"
|
suffix += "_kccnv"
|
||||||
if self.border:
|
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:
|
if forcepng:
|
||||||
self.image.save(os.path.join(targetdir, os.path.splitext(self.filename)[0] + suffix + ".png"), "PNG",
|
self.image.save(os.path.join(targetdir, os.path.splitext(self.filename)[0] + suffix + ".png"), "PNG",
|
||||||
optimize=1)
|
optimize=1)
|
||||||
@@ -194,6 +215,8 @@ class ComicPage:
|
|||||||
def optimizeImage(self, gamma):
|
def optimizeImage(self, gamma):
|
||||||
if gamma < 0.1:
|
if gamma < 0.1:
|
||||||
gamma = self.gamma
|
gamma = self.gamma
|
||||||
|
if self.gamma != 1.0 and self.isImageColor(self.image):
|
||||||
|
gamma = 1.0
|
||||||
if gamma == 1.0:
|
if gamma == 1.0:
|
||||||
self.image = ImageOps.autocontrast(self.image)
|
self.image = ImageOps.autocontrast(self.image)
|
||||||
else:
|
else:
|
||||||
@@ -210,58 +233,61 @@ class ComicPage:
|
|||||||
# Quantize is deprecated but new function call it internally anyway...
|
# Quantize is deprecated but new function call it internally anyway...
|
||||||
self.image = self.image.quantize(palette=palImg)
|
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, isHQ=False):
|
||||||
|
if self.fill == 'white':
|
||||||
|
# This code trigger only when sourceImage is already saved. So we can break color quantization.
|
||||||
|
if sourceImage.image.mode == 'P':
|
||||||
|
sourceImage.image = sourceImage.image.convert('RGB')
|
||||||
|
border = ImageChops.invert(sourceImage.image).getbbox()
|
||||||
|
else:
|
||||||
|
border = sourceImage.image.getbbox()
|
||||||
|
if border is not None:
|
||||||
|
if isHQ:
|
||||||
|
multiplier = 1.0
|
||||||
|
else:
|
||||||
|
multiplier = 1.5
|
||||||
|
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]) * multiplier) < self.size[0]:
|
||||||
|
self.noHPV = True
|
||||||
|
if int((border[3] - border[1]) * multiplier) < self.size[1]:
|
||||||
|
self.noVPV = True
|
||||||
|
else:
|
||||||
|
self.border = [0, 0, 0, 0]
|
||||||
|
self.noHPV = True
|
||||||
|
self.noVPV = True
|
||||||
|
|
||||||
def resizeImage(self, upscale=False, stretch=False, bordersColor=None, qualityMode=0):
|
def resizeImage(self, upscale=False, stretch=False, bordersColor=None, qualityMode=0):
|
||||||
# High-quality downscaling filter
|
|
||||||
method = Image.ANTIALIAS
|
|
||||||
if bordersColor:
|
if bordersColor:
|
||||||
fill = bordersColor
|
fill = bordersColor
|
||||||
else:
|
else:
|
||||||
fill = self.fill
|
fill = self.fill
|
||||||
|
# Set target size
|
||||||
if qualityMode == 0:
|
if qualityMode == 0:
|
||||||
size = (self.size[0], self.size[1])
|
size = (self.size[0], self.size[1])
|
||||||
generateBorder = True
|
|
||||||
elif qualityMode == 1:
|
|
||||||
size = (self.panelviewsize[0], self.panelviewsize[1])
|
|
||||||
generateBorder = True
|
|
||||||
else:
|
else:
|
||||||
size = (self.panelviewsize[0], self.panelviewsize[1])
|
size = (self.panelviewsize[0], self.panelviewsize[1])
|
||||||
generateBorder = False
|
# If image is smaller than device resolution and upscale is off - Just expand it by adding margins
|
||||||
# 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] and not upscale:
|
||||||
if self.image.size[0] <= self.size[0] and self.image.size[1] <= self.size[1]:
|
borderw = (self.size[0] - self.image.size[0]) / 2
|
||||||
if not upscale:
|
borderh = (self.size[1] - self.image.size[1]) / 2
|
||||||
borderw = (self.size[0] - self.image.size[0]) / 2
|
self.image = ImageOps.expand(self.image, border=(borderw, borderh), fill=fill)
|
||||||
borderh = (self.size[1] - self.image.size[1]) / 2
|
return self.image
|
||||||
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 stretching is on - Resize without other considerations
|
# If stretching is on - Resize without other considerations
|
||||||
if stretch:
|
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)
|
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
|
return self.image
|
||||||
# Otherwise - Upscale/Downscale
|
# Otherwise - Upscale/Downscale
|
||||||
ratioDev = float(self.size[0]) / float(self.size[1])
|
ratioDev = float(self.size[0]) / float(self.size[1])
|
||||||
@@ -271,23 +297,11 @@ class ComicPage:
|
|||||||
elif (float(self.image.size[0]) / float(self.image.size[1])) > ratioDev:
|
elif (float(self.image.size[0]) / float(self.image.size[1])) > ratioDev:
|
||||||
diff = int(self.image.size[0] / ratioDev) - self.image.size[1]
|
diff = int(self.image.size[0] / ratioDev) - self.image.size[1]
|
||||||
self.image = ImageOps.expand(self.image, border=(0, diff / 2), fill=fill)
|
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))
|
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
|
return self.image
|
||||||
|
|
||||||
def splitPage(self, targetdir, righttoleft=False, rotate=False):
|
def splitPage(self, targetdir, righttoleft=False, rotate=False):
|
||||||
@@ -419,7 +433,7 @@ class ComicPage:
|
|||||||
self.image = self.image.crop((0, 0, widthImg - diff, heightImg))
|
self.image = self.image.crop((0, 0, widthImg - diff, heightImg))
|
||||||
return self.image
|
return self.image
|
||||||
|
|
||||||
def getImageHistogram(self, image):
|
def getImageHistogram(self, image, new=True):
|
||||||
histogram = image.histogram()
|
histogram = image.histogram()
|
||||||
RBGW = []
|
RBGW = []
|
||||||
pixelCount = 0
|
pixelCount = 0
|
||||||
@@ -428,37 +442,100 @@ class ComicPage:
|
|||||||
RBGW.append(histogram[i] + histogram[256 + i] + histogram[512 + i])
|
RBGW.append(histogram[i] + histogram[256 + i] + histogram[512 + i])
|
||||||
white = 0
|
white = 0
|
||||||
black = 0
|
black = 0
|
||||||
for i in range(245, 256):
|
for i in range(251, 256):
|
||||||
white += RBGW[i]
|
white += RBGW[i]
|
||||||
for i in range(11):
|
for i in range(5):
|
||||||
black += RBGW[i]
|
black += RBGW[i]
|
||||||
if black > white and black > pixelCount*0.5:
|
if new:
|
||||||
return True
|
if black > 0 and white == 0:
|
||||||
|
return 1
|
||||||
|
elif white > 0 and black == 0:
|
||||||
|
return -1
|
||||||
|
else:
|
||||||
|
return False
|
||||||
else:
|
else:
|
||||||
return False
|
if black > white and black > pixelCount*0.5:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
def getImageFill(self, isWebToon):
|
def getImageFill(self, isWebToon):
|
||||||
fill = 0
|
if isWebToon:
|
||||||
if isWebToon or self.rotated:
|
fill = 0
|
||||||
fill += self.getImageHistogram(self.image.crop((0, 0, self.image.size[0], 5)))
|
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],
|
fill += self.getImageHistogram(self.image.crop((0, self.image.size[1]-5, self.image.size[0],
|
||||||
self.image.size[1])))
|
self.image.size[1])), False)
|
||||||
else:
|
if fill == 2:
|
||||||
fill += self.getImageHistogram(self.image.crop((0, 0, 5, self.image.size[1])))
|
self.fill = 'black'
|
||||||
fill += self.getImageHistogram(self.image.crop((self.image.size[0]-5, 0, self.image.size[0],
|
elif fill == 0:
|
||||||
self.image.size[1])))
|
self.fill = 'white'
|
||||||
if fill == 2:
|
else:
|
||||||
self.fill = 'black'
|
fill = 0
|
||||||
elif fill == 0:
|
fill += self.getImageHistogram(self.image.crop((0, 0, 5, 5)), False)
|
||||||
self.fill = 'white'
|
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:
|
else:
|
||||||
fill = 0
|
fill = 0
|
||||||
fill += self.getImageHistogram(self.image.crop((0, 0, 5, 5)))
|
# Search fom horizontal solid lines
|
||||||
fill += self.getImageHistogram(self.image.crop((self.image.size[0]-5, 0, self.image.size[0], 5)))
|
startY = 0
|
||||||
fill += self.getImageHistogram(self.image.crop((0, self.image.size[1]-5, 5, self.image.size[1])))
|
stopY = 3
|
||||||
fill += self.getImageHistogram(self.image.crop((self.image.size[0]-5, self.image.size[1]-5,
|
searching = True
|
||||||
self.image.size[0], self.image.size[1])))
|
while stopY <= self.image.size[1]:
|
||||||
if fill > 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'
|
self.fill = 'black'
|
||||||
else:
|
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
|
||||||
|
|||||||
34
setup.py
34
setup.py
@@ -1,3 +1,4 @@
|
|||||||
|
#!/usr/bin/env python2
|
||||||
"""
|
"""
|
||||||
cx_Freeze build script for KCC.
|
cx_Freeze build script for KCC.
|
||||||
|
|
||||||
@@ -10,7 +11,7 @@ Usage (Windows):
|
|||||||
from sys import platform
|
from sys import platform
|
||||||
|
|
||||||
NAME = "KindleComicConverter"
|
NAME = "KindleComicConverter"
|
||||||
VERSION = "3.5"
|
VERSION = "3.6.2"
|
||||||
MAIN = "kcc.py"
|
MAIN = "kcc.py"
|
||||||
|
|
||||||
if platform == "darwin":
|
if platform == "darwin":
|
||||||
@@ -23,10 +24,11 @@ if platform == "darwin":
|
|||||||
argv_emulation=True,
|
argv_emulation=True,
|
||||||
iconfile='icons/comic2ebook.icns',
|
iconfile='icons/comic2ebook.icns',
|
||||||
includes=['PIL', 'sip', 'PyQt4', 'PyQt4.QtCore', 'PyQt4.QtGui', 'PyQt4.QtNetwork'],
|
includes=['PIL', 'sip', 'PyQt4', 'PyQt4.QtCore', 'PyQt4.QtGui', 'PyQt4.QtNetwork'],
|
||||||
|
qt_plugins=[],
|
||||||
excludes=['PyQt4.QtDeclarative', 'PyQt4.QtDesigner', 'PyQt4.QtHelp', 'PyQt4.QtMultimedia',
|
excludes=['PyQt4.QtDeclarative', 'PyQt4.QtDesigner', 'PyQt4.QtHelp', 'PyQt4.QtMultimedia',
|
||||||
'PyQt4.QtOpenGL', 'PyQt4.QtScript', 'PyQt4.QtScriptTools', 'PyQt4.QtSql', 'PyQt4.QtSvg',
|
'PyQt4.QtOpenGL', 'PyQt4.QtScript', 'PyQt4.QtScriptTools', 'PyQt4.QtSql', 'PyQt4.QtSvg',
|
||||||
'PyQt4.QtXmlPatterns', 'PyQt4.QtXml', 'PyQt4.QtWebKit', 'PyQt4.QtTest'],
|
'PyQt4.QtXmlPatterns', 'PyQt4.QtXml', 'PyQt4.QtWebKit', 'PyQt4.QtTest', 'Tkinter'],
|
||||||
resources=['LICENSE.txt', 'other/Additional-LICENSE.txt'],
|
resources=['LICENSE.txt', 'other/Additional-LICENSE.txt', 'other/unrar', 'other/7za'],
|
||||||
plist=dict(
|
plist=dict(
|
||||||
CFBundleName=NAME,
|
CFBundleName=NAME,
|
||||||
CFBundleShortVersionString=VERSION,
|
CFBundleShortVersionString=VERSION,
|
||||||
@@ -42,6 +44,10 @@ if platform == "darwin":
|
|||||||
CFBundleTypeRole='Viewer',
|
CFBundleTypeRole='Viewer',
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
|
LSMinimumSystemVersion='10.6.0',
|
||||||
|
LSEnvironment=dict(
|
||||||
|
PATH='/usr/local/bin:/usr/bin:/bin'
|
||||||
|
),
|
||||||
NSHumanReadableCopyright='ISC License (ISCL)'
|
NSHumanReadableCopyright='ISC License (ISCL)'
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -55,7 +61,8 @@ elif platform == "win32":
|
|||||||
['other/UnRAR.exe', 'UnRAR.exe'],
|
['other/UnRAR.exe', 'UnRAR.exe'],
|
||||||
['other/7za.exe', '7za.exe'],
|
['other/7za.exe', '7za.exe'],
|
||||||
['other/Additional-LICENSE.txt', 'Additional-LICENSE.txt']
|
['other/Additional-LICENSE.txt', 'Additional-LICENSE.txt']
|
||||||
], "compressed": True}},
|
], "compressed": True,
|
||||||
|
"excludes": ['Tkinter']}},
|
||||||
executables=[Executable(MAIN,
|
executables=[Executable(MAIN,
|
||||||
base=base,
|
base=base,
|
||||||
targetName="KCC.exe",
|
targetName="KCC.exe",
|
||||||
@@ -65,18 +72,10 @@ elif platform == "win32":
|
|||||||
appendScriptToLibrary=False,
|
appendScriptToLibrary=False,
|
||||||
compress=True)])
|
compress=True)])
|
||||||
else:
|
else:
|
||||||
from cx_Freeze import setup, Executable
|
print 'Please use setup.sh to build Linux package.'
|
||||||
extra_options = dict(
|
exit()
|
||||||
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)])
|
|
||||||
|
|
||||||
|
#noinspection PyUnboundLocalVariable
|
||||||
setup(
|
setup(
|
||||||
name=NAME,
|
name=NAME,
|
||||||
version=VERSION,
|
version=VERSION,
|
||||||
@@ -89,3 +88,8 @@ setup(
|
|||||||
packages=['kcc'], requires=['Pillow'],
|
packages=['kcc'], requires=['Pillow'],
|
||||||
**extra_options
|
**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
12
setup.sh
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Linux Python package build script
|
||||||
|
|
||||||
|
VERSION="3.6.2"
|
||||||
|
|
||||||
|
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
|
||||||
Reference in New Issue
Block a user