mirror of
https://github.com/ciromattia/kcc
synced 2026-04-15 05:28:49 +00:00
Compare commits
39 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5450502c2a | ||
|
|
c976b06413 | ||
|
|
b6facda95b | ||
|
|
3f608eb602 | ||
|
|
104cd04994 | ||
|
|
b323204628 | ||
|
|
56195d301d | ||
|
|
287723ca6f | ||
|
|
90490149c7 | ||
|
|
2210f484df | ||
|
|
d5502e85b0 | ||
|
|
59b26cfc8b | ||
|
|
a722e5fa49 | ||
|
|
fce3072dca | ||
|
|
f53cdf9cd7 | ||
|
|
de74318c69 | ||
|
|
b137e69510 | ||
|
|
888663fa4c | ||
|
|
8a2ba96ac5 | ||
|
|
cb6b0e0a7b | ||
|
|
49d2a7fbab | ||
|
|
3d84b27d58 | ||
|
|
e98a23d0c0 | ||
|
|
7f80dacea8 | ||
|
|
6efb3dcef3 | ||
|
|
942a828b7e | ||
|
|
df5ee1badf | ||
|
|
e3ab28642d | ||
|
|
a1831c45d5 | ||
|
|
52bea9957a | ||
|
|
a71339ec34 | ||
|
|
c5f09c44b8 | ||
|
|
64b199ef74 | ||
|
|
8dd0b0e694 | ||
|
|
181a2e8ab4 | ||
|
|
3fdff845b7 | ||
|
|
2ee5dc310b | ||
|
|
f32e9560b5 | ||
|
|
621827c1c2 |
@@ -110,7 +110,7 @@
|
|||||||
<enum>Qt::NoFocus</enum>
|
<enum>Qt::NoFocus</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string><html><head/><body><p style='white-space:pre'>Enable auto-splitting of webtoons like <span style=" font-style:italic;">Tower of God</span> or <span style=" font-style:italic;">Noblesse</span>.<br/>Pages with a low width, high height and vertical panel flow.</p></body></html></string>
|
<string><html><head/><body><p>Enable auto-splitting of webtoons like <span style=" font-style:italic;">Tower of God</span> or <span style=" font-style:italic;">Noblesse</span>.<br/>This mode was created for pages with a low width, high height and vertical panel flow.</p></body></html></string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Webtoon mode</string>
|
<string>Webtoon mode</string>
|
||||||
@@ -386,7 +386,7 @@
|
|||||||
<enum>Qt::NoFocus</enum>
|
<enum>Qt::NoFocus</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string><html><head/><body><p><span style=" font-weight:600; text-decoration: underline;">Unchecked - Normal quality mode<br/></span><span style=" font-style:italic;">Use it when Panel View support is not needed.<br/></span>- Maximum quality when zoom is not enabled.<br/>- Poor quality when zoom is enabled.<br/>- Lowest file size.</p><p><span style=" font-weight:600; text-decoration: underline;">Indeterminate - High quality mode<br/></span><span style=" font-style:italic;">Not zoomed images </span><span style=" font-weight:600; font-style:italic;">might</span><span style=" font-style:italic;"> be blurry.<br/></span>- High quality when zoom is not enabled.<br/>- Maximum quality when zoom is enabled.</p><p><span style=" font-weight:600; text-decoration: underline;">Checked - Ultra quality mode<br/></span><span style=" font-style:italic;">Maximum possible quality.<br/></span>- Maximum quality when zoom is not enabled.<br/>- Maximum quality when zoom is enabled.<br/>- Very high file size.</p></body></html></string>
|
<string><html><head/><body><p><span style=" font-weight:600; text-decoration: underline;">Unchecked - Normal quality mode</span><br/>Maximal quality of images but very poor magnification quality.<br/>Use it only when zoom is not needed or output files needs to be small.</p><p><span style=" font-weight:600; text-decoration: underline;">Indeterminate - High quality mode</span><br/>In most cases high quality of images and magnification.<br/>Overall quality highly depends on the resolution of source files.<br/>On Kindle models older than Paperwhite non-zoomed images might be a little blurred.<br/><br/><span style=" font-weight:600; text-decoration: underline;">Checked - Ultra quality mode</span><br/>Highest possible quality. Output files will be big.</p></body></html></string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>High/Ultra quality</string>
|
<string>High/Ultra quality</string>
|
||||||
|
|||||||
@@ -110,7 +110,7 @@
|
|||||||
<enum>Qt::NoFocus</enum>
|
<enum>Qt::NoFocus</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string><html><head/><body><p style='white-space:pre'>Enable auto-splitting of webtoons like <span style=" font-style:italic;">Tower of God</span> or <span style=" font-style:italic;">Noblesse</span>.<br/>Pages with a low width, high height and vertical panel flow.</p></body></html></string>
|
<string><html><head/><body><p>Enable auto-splitting of webtoons like <span style=" font-style:italic;">Tower of God </span>or <span style=" font-style:italic;">Noblesse</span>.<br/>This mode was created for pages with a low width, high height and vertical panel flow.</p><p><br/></p></body></html></string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Webtoon mode</string>
|
<string>Webtoon mode</string>
|
||||||
@@ -391,7 +391,7 @@
|
|||||||
<enum>Qt::NoFocus</enum>
|
<enum>Qt::NoFocus</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string><html><head/><body><p><span style=" font-weight:600; text-decoration: underline;">Unchecked - Normal quality mode<br/></span><span style=" font-style:italic;">Use it when Panel View support is not needed.<br/></span>- Maximum quality when zoom is not enabled.<br/>- Poor quality when zoom is enabled.<br/>- Lowest file size.</p><p><span style=" font-weight:600; text-decoration: underline;">Indeterminate - High quality mode<br/></span><span style=" font-style:italic;">Not zoomed image </span><span style=" font-weight:600; font-style:italic;">might</span><span style=" font-style:italic;"> be a little blurry.<br/></span>- High quality when zoom is not enabled.<br/>- Maximum quality when zoom is enabled.</p><p><span style=" font-weight:600; text-decoration: underline;">Checked - Ultra quality mode<br/></span><span style=" font-style:italic;">Maximum possible quality.<br/></span>- Maximum quality when zoom is not enabled.<br/>- Maximum quality when zoom is enabled.<br/>- Very high file size.</p></body></html></string>
|
<string><html><head/><body><p><span style=" font-weight:600; text-decoration: underline;">Unchecked - Normal quality mode</span><br/>Maximal quality of images but very poor magnification quality.<br/>Use it only when zoom is not needed or output files needs to be small.</p><p><span style=" font-weight:600; text-decoration: underline;">Indeterminate - High quality mode</span><br/>In most cases high quality of images and magnification.<br/>Overall quality highly depends on the resolution of source files.<br/>On Kindle models older than Paperwhite non-zoomed images might be a little blurred.</p><p><span style=" font-weight:600; text-decoration: underline;">Checked - Ultra quality mode</span><br/>Highest possible quality. Output files will be big.</p></body></html></string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>High/Ultra quality</string>
|
<string>High/Ultra quality</string>
|
||||||
|
|||||||
4
KCC.ui
4
KCC.ui
@@ -94,7 +94,7 @@
|
|||||||
<enum>Qt::NoFocus</enum>
|
<enum>Qt::NoFocus</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string><html><head/><body><p style='white-space:pre'>Enable auto-splitting of webtoons like <span style=" font-style:italic;">Tower of God</span> or <span style=" font-style:italic;">Noblesse</span>.<br/>Pages with a low width, high height and vertical panel flow.</p></body></html></string>
|
<string><html><head/><body><p style="white-space:pre">Enable auto-splitting of webtoons like <span style=" font-style:italic;">Tower of God</span> or <span style=" font-style:italic;">Noblesse</span>.<br/>This mode is created for pages with a low width, high height and vertical panel flow.</p></body></html></string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Webtoon mode</string>
|
<string>Webtoon mode</string>
|
||||||
@@ -338,7 +338,7 @@
|
|||||||
<enum>Qt::NoFocus</enum>
|
<enum>Qt::NoFocus</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string><html><head/><body><p><span style=" font-weight:600; text-decoration: underline;">Unchecked - Normal quality mode<br/></span><span style=" font-style:italic;">Use it when Panel View support is not needed.</span><span style=" font-weight:600; text-decoration: underline;"><br/></span>- Maximum quality when zoom is not enabled.<br/>- Poor quality when zoom is enabled.<br/>- Lowest file size.</p><p><span style=" font-weight:600; text-decoration: underline;">Indeterminate - High quality mode<br/></span><span style=" font-style:italic;">Not zoomed images </span><span style=" font-weight:600; font-style:italic;">might </span><span style=" font-style:italic;">be blurry.</span><span style=" font-weight:600; text-decoration: underline;"><br/></span>- High quality when zoom is not enabled.<br/>- Maximum quality when zoom is enabled.</p><p><span style=" font-weight:600; text-decoration: underline;">Checked - Ultra quality mode<br/></span><span style=" font-style:italic;">Maximum possible quality.</span><span style=" font-weight:600; text-decoration: underline;"><br/></span>- Maximum quality when zoom is not enabled.<br/>- Maximum quality when zoom is enabled.<br/>- Very high file size.</p></body></html></string>
|
<string><html><head/><body><p style="white-space:pre"><span style=" font-weight:600; text-decoration: underline;">Unchecked - Normal quality mode<br/></span>Maximal quality of images but very poor magnification quality.<br/>Use it only when zoom is not needed or output files needs to be small.</p><p style="white-space:pre"><span style=" font-weight:600; text-decoration: underline;">Indeterminate - High quality mode<br/></span>In most cases high quality of images and magnification.<br/>Overall quality highly depends on the resolution of source files.<br/>On Kindle models older than Paperwhite non-zoomed images might be a little blurred.</p><p style="white-space:pre"><span style=" font-weight:600; text-decoration: underline;">Checked - Ultra quality mode<br/></span>Highest possible quality. Output files will be big.</p></body></html></string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>High/Ultra quality</string>
|
<string>High/Ultra quality</string>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
ISC LICENSE
|
ISC LICENSE
|
||||||
|
|
||||||
Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
||||||
Copyright (c) 2013-2014 Paweł Jastrzębski <pawelj@vulturis.eu>
|
Copyright (c) 2013-2014 Paweł Jastrzębski <pawelj@iosphe.re>
|
||||||
|
|
||||||
Permission to use, copy, modify, and/or distribute this software for
|
Permission to use, copy, modify, and/or distribute this software for
|
||||||
any purpose with or without fee is hereby granted, provided that the
|
any purpose with or without fee is hereby granted, provided that the
|
||||||
|
|||||||
59
README.md
59
README.md
@@ -17,14 +17,18 @@ If you can fix an open issue, fork & make a pull request.
|
|||||||
If you want more chances an issue is fixes or your wanted feature added, consider [placing a bounty](https://www.bountysource.com/trackers/65571-ciromattia-kcc)!
|
If you want more chances an issue is fixes or your wanted feature added, consider [placing a bounty](https://www.bountysource.com/trackers/65571-ciromattia-kcc)!
|
||||||
|
|
||||||
If you find **KCC** valuable you can consider donating to the authors:
|
If you find **KCC** valuable you can consider donating to the authors:
|
||||||
* Ciro Mattia Gonano: [](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:
|
||||||
* 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)
|
- [](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=D8WNYNPBGDAS2)
|
||||||
|
- [](http://flattr.com/thing/2260449/ciromattiakcc-on-GitHub)
|
||||||
|
- Paweł Jastrzębski:
|
||||||
|
- [](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YTTJ4LK2JDHPS)
|
||||||
|
- Bitcoin: 1W15wwqsfd7wbaZ6wvSJf1LW1bz6q5L8b
|
||||||
|
|
||||||
## 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:
|
||||||
- **Windows:** [http://kcc.vulturis.eu/Windows/](http://kcc.vulturis.eu/Windows/)
|
- **Windows:** [http://kcc.iosphe.re/Windows/](http://kcc.iosphe.re/Windows/)
|
||||||
- **Linux:** [http://kcc.vulturis.eu/Linux/](http://kcc.vulturis.eu/Linux/)
|
- **Linux:** [http://kcc.iosphe.re/Linux/](http://kcc.iosphe.re/Linux/)
|
||||||
- **OS X (10.8+):** [http://kcc.vulturis.eu/OSX/](http://kcc.vulturis.eu/OSX/)
|
- **OS X (10.8+):** [http://kcc.iosphe.re/OSX/](http://kcc.iosphe.re/OSX/)
|
||||||
|
|
||||||
## INPUT FORMATS
|
## INPUT FORMATS
|
||||||
**KCC** can understand and convert, at the moment, the following input types:
|
**KCC** can understand and convert, at the moment, the following input types:
|
||||||
@@ -40,8 +44,8 @@ You can find the latest released binary at the following links:
|
|||||||
- [7za](http://www.7-zip.org/download.html) *(For 7z/CB7 support)*
|
- [7za](http://www.7-zip.org/download.html) *(For 7z/CB7 support)*
|
||||||
|
|
||||||
### For running from source:
|
### For running from source:
|
||||||
- Python 3.3
|
- Python 3.3+
|
||||||
- [PyQt5](http://www.riverbankcomputing.co.uk/software/pyqt/download5)
|
- [PyQt5](http://www.riverbankcomputing.co.uk/software/pyqt/download5) 5.2.0+
|
||||||
- [Pillow](http://pypi.python.org/pypi/Pillow/) 2.3.0+
|
- [Pillow](http://pypi.python.org/pypi/Pillow/) 2.3.0+
|
||||||
- [psutil](https://pypi.python.org/pypi/psutil) 2.0+
|
- [psutil](https://pypi.python.org/pypi/psutil) 2.0+
|
||||||
- [python-slugify](http://pypi.python.org/pypi/python-slugify)
|
- [python-slugify](http://pypi.python.org/pypi/python-slugify)
|
||||||
@@ -53,8 +57,8 @@ sudo pip3 install pillow python-slugify psutil
|
|||||||
```
|
```
|
||||||
|
|
||||||
### For freezing code:
|
### For freezing code:
|
||||||
- Windows - [cx_Freeze](https://bitbucket.org/anthony_tuininga/cx_freeze) HEAD version with [this](https://bitbucket.org/anthony_tuininga/cx_freeze/pull-request/29/conversions-to-support-untranslated-wide) patchset.
|
- Windows - [py2exe](https://pypi.python.org/pypi/py2exe) 0.9.2+
|
||||||
- OS X - [py2app](https://bitbucket.org/ronaldoussoren/py2app) 0.8+
|
- OS X - [py2app](https://bitbucket.org/ronaldoussoren/py2app) 0.8.0+
|
||||||
|
|
||||||
## USAGE
|
## USAGE
|
||||||
|
|
||||||
@@ -126,29 +130,29 @@ Options:
|
|||||||
```
|
```
|
||||||
|
|
||||||
## CREDITS
|
## CREDITS
|
||||||
**KCC** is made by [Ciro Mattia Gonano](http://github.com/ciromattia) and [Paweł Jastrzębski](http://github.com/AcidWeb)
|
**KCC** is made by [Ciro Mattia Gonano](http://github.com/ciromattia) and [Paweł Jastrzębski](http://github.com/AcidWeb).
|
||||||
|
|
||||||
This script born as a cross-platform alternative to `KindleComicParser` by **Dc5e** (published [here](http://www.mobileread.com/forums/showthread.php?t=192783))
|
This script born as a cross-platform alternative to `KindleComicParser` by **Dc5e** (published [here](http://www.mobileread.com/forums/showthread.php?t=192783)).
|
||||||
|
|
||||||
The app relies and includes the following scripts/binaries:
|
The app relies and includes the following scripts/binaries:
|
||||||
|
|
||||||
- `KindleUnpack` script by Charles **M. Hannum, P. Durrant, K. Hendricks, S. Siebert, fandrieu, DiapDealer, nickredding**. Released with GPLv3 License.
|
- `DualMetaFix` script by **K. Hendricks**. Released with GPL-3 License.
|
||||||
- `rarfile.py` script © 2005-2011 **Marko Kreen** <markokr@gmail.com>. Released with ISC License.
|
- `rarfile.py` script © 2005-2011 **Marko Kreen** <markokr@gmail.com>. Released with ISC License.
|
||||||
- `image.py` class from **Alex Yatskov**'s [Mangle](http://foosoft.net/mangle/) with subsequent [proDOOMman](https://github.com/proDOOMman/Mangle)'s and [Birua](https://github.com/Birua/Mangle)'s patches.
|
- `image.py` class from **Alex Yatskov**'s [Mangle](https://github.com/FooSoft/mangle/) with subsequent [proDOOMman](https://github.com/proDOOMman/Mangle)'s and [Birua](https://github.com/Birua/Mangle)'s patches.
|
||||||
- Icon is by **Nikolay Verin** ([http://ncrow.deviantart.com/](http://ncrow.deviantart.com/)) and released under [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/) License.
|
- Icon is by **Nikolay Verin** ([http://ncrow.deviantart.com/](http://ncrow.deviantart.com/)) and released under [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/) License.
|
||||||
|
|
||||||
## SAMPLE FILES CREATED BY KCC
|
## SAMPLE FILES CREATED BY KCC
|
||||||
* [Kindle Paperwhite](http://kcc.vulturis.eu/Samples/Ubunchu!-KPW.mobi)
|
* [Kindle Paperwhite](http://kcc.iosphe.re/Samples/Ubunchu!-KPW.mobi)
|
||||||
* [Kindle](http://kcc.vulturis.eu/Samples/Ubunchu!-K345.mobi)
|
* [Kindle](http://kcc.iosphe.re/Samples/Ubunchu!-K345.mobi)
|
||||||
* [Kindle DX/DXG](http://kcc.vulturis.eu/Samples/Ubunchu!-KDX.mobi)
|
* [Kindle DX/DXG](http://kcc.iosphe.re/Samples/Ubunchu!-KDX.mobi)
|
||||||
* [Kindle Fire HD](http://kcc.vulturis.eu/Samples/Ubunchu!-KFHD.mobi)
|
* [Kindle Fire HD](http://kcc.iosphe.re/Samples/Ubunchu!-KFHD.mobi)
|
||||||
* [Kindle Fire HD 8.9"](http://kcc.vulturis.eu/Samples/Ubunchu!-KFHD8.mobi)
|
* [Kindle Fire HD 8.9"](http://kcc.iosphe.re/Samples/Ubunchu!-KFHD8.mobi)
|
||||||
* [Kindle Fire HDX](http://kcc.vulturis.eu/Samples/Ubunchu!-KFHDX.mobi)
|
* [Kindle Fire HDX](http://kcc.iosphe.re/Samples/Ubunchu!-KFHDX.mobi)
|
||||||
* [Kindle Fire HDX 8.9"](http://kcc.vulturis.eu/Samples/Ubunchu!-KFHDX8.mobi)
|
* [Kindle Fire HDX 8.9"](http://kcc.iosphe.re/Samples/Ubunchu!-KFHDX8.mobi)
|
||||||
* [Kobo Mini/Touch](http://kcc.vulturis.eu/Samples/Ubunchu!-KoMT.cbz)
|
* [Kobo Mini/Touch](http://kcc.iosphe.re/Samples/Ubunchu!-KoMT.cbz)
|
||||||
* [Kobo Glow](http://kcc.vulturis.eu/Samples/Ubunchu!-KoG.cbz)
|
* [Kobo Glow](http://kcc.iosphe.re/Samples/Ubunchu!-KoG.cbz)
|
||||||
* [Kobo Aura](http://kcc.vulturis.eu/Samples/Ubunchu!-KoA.cbz)
|
* [Kobo Aura](http://kcc.iosphe.re/Samples/Ubunchu!-KoA.cbz)
|
||||||
* [Kobo Aura HD](http://kcc.vulturis.eu/Samples/Ubunchu!-KoAHD.cbz)
|
* [Kobo Aura HD](http://kcc.iosphe.re/Samples/Ubunchu!-KoAHD.cbz)
|
||||||
|
|
||||||
## CHANGELOG
|
## CHANGELOG
|
||||||
####1.0
|
####1.0
|
||||||
@@ -343,6 +347,13 @@ The app relies and includes the following scripts/binaries:
|
|||||||
* Fixed some Windows and OSX specific bugs
|
* Fixed some Windows and OSX specific bugs
|
||||||
* Fixed problem with marigns when using HQ mode
|
* Fixed problem with marigns when using HQ mode
|
||||||
|
|
||||||
|
####4.1:
|
||||||
|
* Thanks to code contributed by Kevin Hendricks speed of MOBI creation was greatly increased
|
||||||
|
* Improved performance on Windows
|
||||||
|
* Improved MOBI splitting and changed maximal size of output file
|
||||||
|
* Fixed _No optimization_ mode
|
||||||
|
* Multiple small tweaks nad minor bug fixes
|
||||||
|
|
||||||
## KNOWN ISSUES
|
## KNOWN ISSUES
|
||||||
Please check [wiki page](https://github.com/ciromattia/kcc/wiki/Known-issues).
|
Please check [wiki page](https://github.com/ciromattia/kcc/wiki/Known-issues).
|
||||||
|
|
||||||
|
|||||||
28
kcc-c2e.py
28
kcc-c2e.py
@@ -2,7 +2,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
||||||
# Copyright (c) 2013-2014 Pawel Jastrzebski <pawelj@vulturis.eu>
|
# Copyright (c) 2013-2014 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and/or distribute this software for
|
# Permission to use, copy, modify, and/or distribute this software for
|
||||||
# any purpose with or without fee is hereby granted, provided that the
|
# any purpose with or without fee is hereby granted, provided that the
|
||||||
@@ -18,9 +18,9 @@
|
|||||||
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
# PERFORMANCE OF THIS SOFTWARE.
|
# PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
__version__ = '4.0.2'
|
__version__ = '4.1'
|
||||||
__license__ = 'ISC'
|
__license__ = 'ISC'
|
||||||
__copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@vulturis.eu>'
|
__copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
@@ -28,25 +28,27 @@ if sys.version_info[0] != 3:
|
|||||||
print('ERROR: This is Python 3 script!')
|
print('ERROR: This is Python 3 script!')
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
# Dependiences check
|
# Dependency check
|
||||||
missing = []
|
missing = []
|
||||||
try:
|
try:
|
||||||
# noinspection PyUnresolvedReferences
|
# noinspection PyUnresolvedReferences
|
||||||
from psutil import virtual_memory, Popen
|
import psutil
|
||||||
|
if tuple(map(int, ('2.0.0'.split(".")))) > tuple(map(int, psutil.version_info)):
|
||||||
|
missing.append('psutil 2.0.0+')
|
||||||
except ImportError:
|
except ImportError:
|
||||||
missing.append('psutil')
|
missing.append('psutil 2.0.0+')
|
||||||
try:
|
try:
|
||||||
# noinspection PyUnresolvedReferences
|
# noinspection PyUnresolvedReferences
|
||||||
from slugify import slugify
|
import PIL
|
||||||
except ImportError:
|
if tuple(map(int, ('2.3.0'.split(".")))) > tuple(map(int, (PIL.PILLOW_VERSION.split(".")))):
|
||||||
missing.append('python-slugify')
|
|
||||||
try:
|
|
||||||
# noinspection PyUnresolvedReferences
|
|
||||||
from PIL import Image, ImageOps, ImageStat, ImageChops
|
|
||||||
if tuple(map(int, ('2.3.0'.split(".")))) > tuple(map(int, (Image.PILLOW_VERSION.split(".")))):
|
|
||||||
missing.append('Pillow 2.3.0+')
|
missing.append('Pillow 2.3.0+')
|
||||||
except ImportError:
|
except ImportError:
|
||||||
missing.append('Pillow 2.3.0+')
|
missing.append('Pillow 2.3.0+')
|
||||||
|
try:
|
||||||
|
# noinspection PyUnresolvedReferences
|
||||||
|
import slugify
|
||||||
|
except ImportError:
|
||||||
|
missing.append('python-slugify')
|
||||||
if len(missing) > 0:
|
if len(missing) > 0:
|
||||||
try:
|
try:
|
||||||
# noinspection PyUnresolvedReferences
|
# noinspection PyUnresolvedReferences
|
||||||
|
|||||||
18
kcc-c2p.py
18
kcc-c2p.py
@@ -2,7 +2,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
||||||
# Copyright (c) 2013-2014 Pawel Jastrzebski <pawelj@vulturis.eu>
|
# Copyright (c) 2013-2014 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and/or distribute this software for
|
# Permission to use, copy, modify, and/or distribute this software for
|
||||||
# any purpose with or without fee is hereby granted, provided that the
|
# any purpose with or without fee is hereby granted, provided that the
|
||||||
@@ -18,22 +18,22 @@
|
|||||||
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
# PERFORMANCE OF THIS SOFTWARE.
|
# PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
|
__version__ = '4.1'
|
||||||
|
__license__ = 'ISC'
|
||||||
|
__copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>'
|
||||||
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
if sys.version_info[0] != 3:
|
if sys.version_info[0] != 3:
|
||||||
print('ERROR: This is Python 3 script!')
|
print('ERROR: This is Python 3 script!')
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
__version__ = '4.0.2'
|
# Dependency check
|
||||||
__license__ = 'ISC'
|
|
||||||
__copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@vulturis.eu>'
|
|
||||||
__docformat__ = 'restructuredtext en'
|
|
||||||
|
|
||||||
# Dependiences check
|
|
||||||
missing = []
|
missing = []
|
||||||
try:
|
try:
|
||||||
# noinspection PyUnresolvedReferences
|
# noinspection PyUnresolvedReferences
|
||||||
from PIL import Image, ImageOps, ImageStat, ImageChops
|
import PIL
|
||||||
if tuple(map(int, ('2.3.0'.split(".")))) > tuple(map(int, (Image.PILLOW_VERSION.split(".")))):
|
if tuple(map(int, ('2.3.0'.split(".")))) > tuple(map(int, (PIL.PILLOW_VERSION.split(".")))):
|
||||||
missing.append('Pillow 2.3.0+')
|
missing.append('Pillow 2.3.0+')
|
||||||
except ImportError:
|
except ImportError:
|
||||||
missing.append('Pillow 2.3.0+')
|
missing.append('Pillow 2.3.0+')
|
||||||
|
|||||||
22
kcc.iss
22
kcc.iss
@@ -1,7 +1,7 @@
|
|||||||
#define MyAppName "Kindle Comic Converter"
|
#define MyAppName "Kindle Comic Converter"
|
||||||
#define MyAppVersion "4.0.2"
|
#define MyAppVersion "4.1"
|
||||||
#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.iosphe.re/"
|
||||||
#define MyAppExeName "KCC.exe"
|
#define MyAppExeName "KCC.exe"
|
||||||
|
|
||||||
[Setup]
|
[Setup]
|
||||||
@@ -42,18 +42,16 @@ Name: "CB7association"; Description: "CB7"; GroupDescription: "File associations
|
|||||||
|
|
||||||
[Files]
|
[Files]
|
||||||
; x64 files
|
; x64 files
|
||||||
Source: "build\exe.win-amd64-3.3\imageformats\*"; DestDir: "{app}\imageformats\"; Flags: ignoreversion; Check: Is64BitInstallMode
|
Source: "dist_64\imageformats\*"; DestDir: "{app}\imageformats\"; Flags: ignoreversion; Check: Is64BitInstallMode
|
||||||
Source: "build\exe.win-amd64-3.3\platforms\*"; DestDir: "{app}\platforms\"; Flags: ignoreversion; Check: Is64BitInstallMode
|
Source: "dist_64\platforms\*"; DestDir: "{app}\platforms\"; Flags: ignoreversion; Check: Is64BitInstallMode
|
||||||
Source: "build\exe.win-amd64-3.3\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion; Check: Is64BitInstallMode
|
Source: "dist_64\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion; Check: Is64BitInstallMode
|
||||||
Source: "build\exe.win-amd64-3.3\*.pyd"; DestDir: "{app}"; Flags: ignoreversion; Check: Is64BitInstallMode
|
Source: "dist_64\*.dll"; DestDir: "{app}"; Flags: ignoreversion; Check: Is64BitInstallMode
|
||||||
Source: "build\exe.win-amd64-3.3\*.dll"; DestDir: "{app}"; Flags: ignoreversion; Check: Is64BitInstallMode
|
|
||||||
Source: "other\vcredist_x64.exe"; DestDir: "{tmp}"; Flags: ignoreversion deleteafterinstall; Check: Is64BitInstallMode
|
Source: "other\vcredist_x64.exe"; DestDir: "{tmp}"; Flags: ignoreversion deleteafterinstall; Check: Is64BitInstallMode
|
||||||
; x86 files
|
; x86 files
|
||||||
Source: "build\exe.win32-3.3\imageformats\*"; DestDir: "{app}\imageformats\"; Flags: ignoreversion; Check: not Is64BitInstallMode
|
Source: "dist\imageformats\*"; DestDir: "{app}\imageformats\"; Flags: ignoreversion; Check: not Is64BitInstallMode
|
||||||
Source: "build\exe.win32-3.3\platforms\*"; DestDir: "{app}\platforms\"; Flags: ignoreversion; Check: not Is64BitInstallMode
|
Source: "dist\platforms\*"; DestDir: "{app}\platforms\"; Flags: ignoreversion; Check: not Is64BitInstallMode
|
||||||
Source: "build\exe.win32-3.3\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion; Check: not Is64BitInstallMode
|
Source: "dist\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion; Check: not Is64BitInstallMode
|
||||||
Source: "build\exe.win32-3.3\*.pyd"; DestDir: "{app}"; Flags: ignoreversion; Check: not Is64BitInstallMode
|
Source: "dist\*.dll"; DestDir: "{app}"; Flags: ignoreversion; Check: not Is64BitInstallMode
|
||||||
Source: "build\exe.win32-3.3\*.dll"; DestDir: "{app}"; Flags: ignoreversion; Check: not Is64BitInstallMode
|
|
||||||
Source: "other\vcredist_x86.exe"; DestDir: "{tmp}"; Flags: ignoreversion deleteafterinstall; Check: not Is64BitInstallMode
|
Source: "other\vcredist_x86.exe"; DestDir: "{tmp}"; Flags: ignoreversion deleteafterinstall; Check: not Is64BitInstallMode
|
||||||
; Common files
|
; Common files
|
||||||
Source: "LICENSE.txt"; DestDir: "{app}"; Flags: ignoreversion solidbreak
|
Source: "LICENSE.txt"; DestDir: "{app}"; Flags: ignoreversion solidbreak
|
||||||
|
|||||||
37
kcc.py
37
kcc.py
@@ -2,7 +2,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
||||||
# Copyright (c) 2013-2014 Pawel Jastrzebski <pawelj@vulturis.eu>
|
# Copyright (c) 2013-2014 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and/or distribute this software for
|
# Permission to use, copy, modify, and/or distribute this software for
|
||||||
# any purpose with or without fee is hereby granted, provided that the
|
# any purpose with or without fee is hereby granted, provided that the
|
||||||
@@ -18,9 +18,9 @@
|
|||||||
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
# PERFORMANCE OF THIS SOFTWARE.
|
# PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
__version__ = '4.0.2'
|
__version__ = '4.1'
|
||||||
__license__ = 'ISC'
|
__license__ = 'ISC'
|
||||||
__copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@vulturis.eu>'
|
__copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
@@ -28,30 +28,34 @@ if sys.version_info[0] != 3:
|
|||||||
print('ERROR: This is Python 3 script!')
|
print('ERROR: This is Python 3 script!')
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
# Dependiences check
|
# Dependency check
|
||||||
missing = []
|
missing = []
|
||||||
try:
|
try:
|
||||||
# noinspection PyUnresolvedReferences
|
# noinspection PyUnresolvedReferences
|
||||||
from PyQt5 import QtCore, QtGui, QtNetwork, QtWidgets
|
from PyQt5 import QtCore, QtNetwork, QtWidgets
|
||||||
|
if tuple(map(int, ('5.2.0'.split(".")))) > tuple(map(int, (QtCore.qVersion().split(".")))):
|
||||||
|
missing.append('PyQt5 5.2.0+')
|
||||||
except ImportError:
|
except ImportError:
|
||||||
missing.append('PyQt5')
|
missing.append('PyQt5 5.2.0+')
|
||||||
try:
|
try:
|
||||||
# noinspection PyUnresolvedReferences
|
# noinspection PyUnresolvedReferences
|
||||||
from psutil import virtual_memory, Popen
|
import psutil
|
||||||
|
if tuple(map(int, ('2.0.0'.split(".")))) > tuple(map(int, psutil.version_info)):
|
||||||
|
missing.append('psutil 2.0.0+')
|
||||||
except ImportError:
|
except ImportError:
|
||||||
missing.append('psutil')
|
missing.append('psutil 2.0.0+')
|
||||||
try:
|
try:
|
||||||
# noinspection PyUnresolvedReferences
|
# noinspection PyUnresolvedReferences
|
||||||
from slugify import slugify
|
import PIL
|
||||||
except ImportError:
|
if tuple(map(int, ('2.3.0'.split(".")))) > tuple(map(int, (PIL.PILLOW_VERSION.split(".")))):
|
||||||
missing.append('python-slugify')
|
|
||||||
try:
|
|
||||||
# noinspection PyUnresolvedReferences
|
|
||||||
from PIL import Image, ImageOps, ImageStat, ImageChops
|
|
||||||
if tuple(map(int, ('2.3.0'.split(".")))) > tuple(map(int, (Image.PILLOW_VERSION.split(".")))):
|
|
||||||
missing.append('Pillow 2.3.0+')
|
missing.append('Pillow 2.3.0+')
|
||||||
except ImportError:
|
except ImportError:
|
||||||
missing.append('Pillow 2.3.0+')
|
missing.append('Pillow 2.3.0+')
|
||||||
|
try:
|
||||||
|
# noinspection PyUnresolvedReferences
|
||||||
|
import slugify
|
||||||
|
except ImportError:
|
||||||
|
missing.append('python-slugify')
|
||||||
if len(missing) > 0:
|
if len(missing) > 0:
|
||||||
try:
|
try:
|
||||||
# noinspection PyUnresolvedReferences
|
# noinspection PyUnresolvedReferences
|
||||||
@@ -110,10 +114,11 @@ class QApplicationMessaging(QtWidgets.QApplication):
|
|||||||
self._timeout = 1000
|
self._timeout = 1000
|
||||||
self._server = QtNetwork.QLocalServer(self)
|
self._server = QtNetwork.QLocalServer(self)
|
||||||
if not self.isRunning():
|
if not self.isRunning():
|
||||||
|
# noinspection PyUnresolvedReferences
|
||||||
self._server.newConnection.connect(self.handleMessage)
|
self._server.newConnection.connect(self.handleMessage)
|
||||||
self._server.listen(self._key)
|
self._server.listen(self._key)
|
||||||
|
|
||||||
def __del__(self):
|
def shutdown(self):
|
||||||
if self._memory.isAttached():
|
if self._memory.isAttached():
|
||||||
self._memory.detach()
|
self._memory.detach()
|
||||||
self._server.close()
|
self._server.close()
|
||||||
|
|||||||
146
kcc/KCC_gui.py
146
kcc/KCC_gui.py
@@ -1,7 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
||||||
# Copyright (c) 2013-2014 Pawel Jastrzebski <pawelj@vulturis.eu>
|
# Copyright (c) 2013-2014 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and/or distribute this software for
|
# Permission to use, copy, modify, and/or distribute this software for
|
||||||
# any purpose with or without fee is hereby granted, provided that the
|
# any purpose with or without fee is hereby granted, provided that the
|
||||||
@@ -17,9 +17,9 @@
|
|||||||
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
# PERFORMANCE OF THIS SOFTWARE.
|
# PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
__version__ = '4.0.2'
|
__version__ = '4.1'
|
||||||
__license__ = 'ISC'
|
__license__ = 'ISC'
|
||||||
__copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@vulturis.eu>'
|
__copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
import os
|
import os
|
||||||
@@ -36,10 +36,11 @@ from subprocess import STDOUT, PIPE
|
|||||||
from PyQt5 import QtGui, QtCore, QtWidgets
|
from PyQt5 import QtGui, QtCore, QtWidgets
|
||||||
from xml.dom.minidom import parse
|
from xml.dom.minidom import parse
|
||||||
from html.parser import HTMLParser
|
from html.parser import HTMLParser
|
||||||
from psutil import virtual_memory, Popen
|
from psutil import virtual_memory, Popen, Process
|
||||||
|
from uuid import uuid4
|
||||||
from .shared import md5Checksum
|
from .shared import md5Checksum
|
||||||
from . import comic2ebook
|
from . import comic2ebook
|
||||||
from . import kindlesplit
|
from . import dualmetafix
|
||||||
from . import KCC_rc_web
|
from . import KCC_rc_web
|
||||||
if sys.platform.startswith('darwin'):
|
if sys.platform.startswith('darwin'):
|
||||||
from . import KCC_ui_osx as KCC_ui
|
from . import KCC_ui_osx as KCC_ui
|
||||||
@@ -196,7 +197,7 @@ class VersionThread(QtCore.QThread):
|
|||||||
def run(self):
|
def run(self):
|
||||||
try:
|
try:
|
||||||
sleep(1)
|
sleep(1)
|
||||||
XML = urlopen('http://kcc.vulturis.eu/Version.php')
|
XML = urlopen('http://kcc.iosphe.re/Version.php')
|
||||||
XML = parse(XML)
|
XML = parse(XML)
|
||||||
except Exception:
|
except Exception:
|
||||||
return
|
return
|
||||||
@@ -210,7 +211,7 @@ class VersionThread(QtCore.QThread):
|
|||||||
'<br/>Current version: ' + latestVersion +
|
'<br/>Current version: ' + latestVersion +
|
||||||
'<br/><br/>Would you like to start automatic update?', 'question')
|
'<br/><br/>Would you like to start automatic update?', 'question')
|
||||||
else:
|
else:
|
||||||
MW.addMessage.emit('<a href="http://kcc.vulturis.eu/">'
|
MW.addMessage.emit('<a href="http://kcc.iosphe.re/">'
|
||||||
'<b>New version is available!</b></a> '
|
'<b>New version is available!</b></a> '
|
||||||
'(<a href="https://github.com/ciromattia/kcc/releases/">'
|
'(<a href="https://github.com/ciromattia/kcc/releases/">'
|
||||||
'Changelog</a>)', 'warning', False)
|
'Changelog</a>)', 'warning', False)
|
||||||
@@ -220,7 +221,7 @@ class VersionThread(QtCore.QThread):
|
|||||||
try:
|
try:
|
||||||
MW.modeConvert.emit(-1)
|
MW.modeConvert.emit(-1)
|
||||||
MW.progressBarTick.emit('Downloading update')
|
MW.progressBarTick.emit('Downloading update')
|
||||||
path = urlretrieve('http://kcc.vulturis.eu/Windows/KindleComicConverter_win_'
|
path = urlretrieve('http://kcc.iosphe.re/Windows/KindleComicConverter_win_'
|
||||||
+ self.newVersion + '.exe', reporthook=self.getNewVersionTick)
|
+ self.newVersion + '.exe', reporthook=self.getNewVersionTick)
|
||||||
if self.md5 != md5Checksum(path[0]):
|
if self.md5 != md5Checksum(path[0]):
|
||||||
raise Exception
|
raise Exception
|
||||||
@@ -278,8 +279,9 @@ class KindleGenThread(QtCore.QRunnable):
|
|||||||
kindlegenErrorCode = 0
|
kindlegenErrorCode = 0
|
||||||
kindlegenError = ''
|
kindlegenError = ''
|
||||||
try:
|
try:
|
||||||
if os.path.getsize(self.work) < 367001600:
|
if os.path.getsize(self.work) < 629145600:
|
||||||
output = Popen('kindlegen -locale en "' + self.work + '"', stdout=PIPE, stderr=STDOUT, shell=True)
|
output = Popen('kindlegen -dont_append_source -locale en "' + self.work + '"', stdout=PIPE,
|
||||||
|
stderr=STDOUT, shell=True)
|
||||||
for line in output.stdout:
|
for line in output.stdout:
|
||||||
line = line.decode('utf-8')
|
line = line.decode('utf-8')
|
||||||
# ERROR: Generic error
|
# ERROR: Generic error
|
||||||
@@ -302,27 +304,20 @@ class KindleGenThread(QtCore.QRunnable):
|
|||||||
self.signals.result.emit([kindlegenErrorCode, kindlegenError, self.work])
|
self.signals.result.emit([kindlegenErrorCode, kindlegenError, self.work])
|
||||||
|
|
||||||
|
|
||||||
class KindleUnpackThread(QtCore.QRunnable):
|
class DualMetaFixThread(QtCore.QRunnable):
|
||||||
def __init__(self, batch):
|
def __init__(self, batch):
|
||||||
super(KindleUnpackThread, self).__init__()
|
super(DualMetaFixThread, self).__init__()
|
||||||
self.signals = WorkerSignals()
|
self.signals = WorkerSignals()
|
||||||
self.work = batch
|
self.work = batch
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
item = self.work[0]
|
item = self.work
|
||||||
profile = self.work[1]
|
|
||||||
os.remove(item)
|
os.remove(item)
|
||||||
mobiPath = item.replace('.epub', '.mobi')
|
mobiPath = item.replace('.epub', '.mobi')
|
||||||
move(mobiPath, mobiPath + '_toclean')
|
move(mobiPath, mobiPath + '_toclean')
|
||||||
try:
|
try:
|
||||||
# MOBI file produced by KindleGen is hybrid. KF8 + M7 + Source header
|
# noinspection PyArgumentList
|
||||||
# KindleSplit is removing redundant data as we need only KF8 part for new Kindle models
|
dualmetafix.DualMobiMetaFix(mobiPath + '_toclean', mobiPath, bytes(str(uuid4()), 'UTF-8'))
|
||||||
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])
|
self.signals.result.emit([True])
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
self.signals.result.emit([False, format(err)])
|
self.signals.result.emit([False, format(err)])
|
||||||
@@ -372,49 +367,64 @@ class WorkerThread(QtCore.QThread):
|
|||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
MW.modeConvert.emit(0)
|
MW.modeConvert.emit(0)
|
||||||
|
|
||||||
|
parser = comic2ebook.makeParser()
|
||||||
|
options, _ = parser.parse_args()
|
||||||
|
|
||||||
profile = GUI.profiles[str(GUI.DeviceBox.currentText())]['Label']
|
profile = GUI.profiles[str(GUI.DeviceBox.currentText())]['Label']
|
||||||
argv = ["--profile=" + profile]
|
options.profile = profile
|
||||||
|
argv = ''
|
||||||
currentJobs = []
|
currentJobs = []
|
||||||
|
|
||||||
|
# Basic mode settings
|
||||||
if GUI.MangaBox.isChecked():
|
if GUI.MangaBox.isChecked():
|
||||||
argv.append("--manga-style")
|
options.righttoleft = True
|
||||||
if GUI.RotateBox.isChecked():
|
if GUI.RotateBox.isChecked():
|
||||||
argv.append("--rotate")
|
options.rotate = True
|
||||||
if GUI.QualityBox.checkState() == 1:
|
if GUI.QualityBox.checkState() == 1:
|
||||||
argv.append("--quality=1")
|
options.quality = 1
|
||||||
elif GUI.QualityBox.checkState() == 2:
|
elif GUI.QualityBox.checkState() == 2:
|
||||||
argv.append("--quality=2")
|
options.quality = 2
|
||||||
if str(GUI.FormatBox.currentText()) == 'CBZ':
|
if str(GUI.FormatBox.currentText()) == 'CBZ':
|
||||||
argv.append("--cbz-output")
|
options.cbzoutput = True
|
||||||
if GUI.currentMode == 1:
|
if GUI.currentMode == 1:
|
||||||
if profile in ['KFHD', 'KFHD8', 'KFHDX', 'KFHDX8']:
|
if profile in ['KFHD', 'KFHD8', 'KFHDX', 'KFHDX8']:
|
||||||
argv.append("--upscale")
|
options.upscale = True
|
||||||
|
|
||||||
|
# Advanced mode settings
|
||||||
if GUI.currentMode > 1:
|
if GUI.currentMode > 1:
|
||||||
if GUI.ProcessingBox.isChecked():
|
if GUI.ProcessingBox.isChecked():
|
||||||
argv.append("--noprocessing")
|
options.imgproc = False
|
||||||
if GUI.NoRotateBox.isChecked():
|
if GUI.NoRotateBox.isChecked():
|
||||||
argv.append("--nosplitrotate")
|
options.nosplitrotate = True
|
||||||
if GUI.UpscaleBox.checkState() == 1:
|
if GUI.UpscaleBox.checkState() == 1:
|
||||||
argv.append("--stretch")
|
options.stretch = True
|
||||||
elif GUI.UpscaleBox.checkState() == 2:
|
elif GUI.UpscaleBox.checkState() == 2:
|
||||||
argv.append("--upscale")
|
options.upscale = True
|
||||||
if GUI.BorderBox.checkState() == 1:
|
if GUI.BorderBox.checkState() == 1:
|
||||||
argv.append("--whiteborders")
|
options.white_borders = True
|
||||||
elif GUI.BorderBox.checkState() == 2:
|
elif GUI.BorderBox.checkState() == 2:
|
||||||
argv.append("--blackborders")
|
options.black_borders = True
|
||||||
if GUI.NoDitheringBox.isChecked():
|
if GUI.NoDitheringBox.isChecked():
|
||||||
argv.append("--forcepng")
|
options.forcepng = True
|
||||||
if GUI.WebtoonBox.isChecked():
|
if GUI.WebtoonBox.isChecked():
|
||||||
argv.append("--webtoon")
|
options.webtoon = True
|
||||||
if float(GUI.GammaValue) > 0.09:
|
if float(GUI.GammaValue) > 0.09:
|
||||||
# noinspection PyTypeChecker
|
# noinspection PyTypeChecker
|
||||||
argv.append("--gamma=" + GUI.GammaValue)
|
options.gamma = float(GUI.GammaValue)
|
||||||
if str(GUI.FormatBox.currentText()) == 'MOBI':
|
if str(GUI.FormatBox.currentText()) == 'MOBI':
|
||||||
argv.append("--batchsplit")
|
options.batchsplit = True
|
||||||
|
|
||||||
|
# Other/custom settings.
|
||||||
if GUI.currentMode > 2:
|
if GUI.currentMode > 2:
|
||||||
argv.append("--customwidth=" + str(GUI.customWidth.text()))
|
options.customwidth = str(GUI.customWidth.text())
|
||||||
argv.append("--customheight=" + str(GUI.customHeight.text()))
|
options.customheight = str(GUI.customHeight.text())
|
||||||
if GUI.ColorBox.isChecked():
|
if GUI.ColorBox.isChecked():
|
||||||
argv.append("--forcecolor")
|
options.forcecolor = True
|
||||||
|
|
||||||
|
comic2ebook.options = options
|
||||||
|
comic2ebook.checkOptions()
|
||||||
|
|
||||||
for i in range(GUI.JobList.count()):
|
for i in range(GUI.JobList.count()):
|
||||||
# Make sure that we don't consider any system message as job to do
|
# Make sure that we don't consider any system message as job to do
|
||||||
if GUI.JobList.item(i).icon().isNull():
|
if GUI.JobList.item(i).icon().isNull():
|
||||||
@@ -436,7 +446,8 @@ class WorkerThread(QtCore.QThread):
|
|||||||
jobargv = list(argv)
|
jobargv = list(argv)
|
||||||
jobargv.append(job)
|
jobargv.append(job)
|
||||||
try:
|
try:
|
||||||
outputPath = comic2ebook.main(jobargv, self)
|
comic2ebook.options.title = 'defaulttitle'
|
||||||
|
outputPath = comic2ebook.makeBook(job, self)
|
||||||
MW.hideProgressBar.emit()
|
MW.hideProgressBar.emit()
|
||||||
except UserWarning as warn:
|
except UserWarning as warn:
|
||||||
if not self.conversionAlive:
|
if not self.conversionAlive:
|
||||||
@@ -492,6 +503,7 @@ class WorkerThread(QtCore.QThread):
|
|||||||
for item in outputPath:
|
for item in outputPath:
|
||||||
if os.path.exists(item):
|
if os.path.exists(item):
|
||||||
os.remove(item)
|
os.remove(item)
|
||||||
|
sleep(1)
|
||||||
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.clean()
|
self.clean()
|
||||||
@@ -499,20 +511,19 @@ class WorkerThread(QtCore.QThread):
|
|||||||
if self.kindlegenErrorCode[0] == 0:
|
if self.kindlegenErrorCode[0] == 0:
|
||||||
GUI.progress.content = ''
|
GUI.progress.content = ''
|
||||||
MW.addMessage.emit('Creating MOBI files... <b>Done!</b>', 'info', True)
|
MW.addMessage.emit('Creating MOBI files... <b>Done!</b>', 'info', True)
|
||||||
MW.addMessage.emit('Cleaning MOBI files', 'info', False)
|
MW.addMessage.emit('Processing MOBI files', 'info', False)
|
||||||
GUI.progress.content = 'Cleaning MOBI files'
|
GUI.progress.content = 'Processing MOBI files'
|
||||||
self.workerOutput = []
|
self.workerOutput = []
|
||||||
# Multithreading KindleUnpack in current form is a waste of resources.
|
# DualMetaFix is very fast and there is not reason to use multithreading.
|
||||||
# Unless we higly optimise KindleUnpack or drop 32bit support this will not change.
|
|
||||||
self.pool.setMaxThreadCount(1)
|
self.pool.setMaxThreadCount(1)
|
||||||
for item in outputPath:
|
for item in outputPath:
|
||||||
worker = KindleUnpackThread([item, profile])
|
worker = DualMetaFixThread(item)
|
||||||
worker.signals.result.connect(self.addResult)
|
worker.signals.result.connect(self.addResult)
|
||||||
self.pool.start(worker)
|
self.pool.start(worker)
|
||||||
self.pool.waitForDone()
|
self.pool.waitForDone()
|
||||||
sleep(0.5)
|
sleep(0.5)
|
||||||
for success in self.workerOutput:
|
for success in self.workerOutput:
|
||||||
if not success:
|
if not success[0]:
|
||||||
self.errors = True
|
self.errors = True
|
||||||
break
|
break
|
||||||
if not self.errors:
|
if not self.errors:
|
||||||
@@ -527,7 +538,7 @@ class WorkerThread(QtCore.QThread):
|
|||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
GUI.completedWork[os.path.basename(mobiPath)] = mobiPath
|
GUI.completedWork[os.path.basename(mobiPath)] = mobiPath
|
||||||
MW.addMessage.emit('Cleaning MOBI files... <b>Done!</b>', 'info', True)
|
MW.addMessage.emit('Processing MOBI files... <b>Done!</b>', 'info', True)
|
||||||
else:
|
else:
|
||||||
GUI.progress.content = ''
|
GUI.progress.content = ''
|
||||||
for item in outputPath:
|
for item in outputPath:
|
||||||
@@ -536,14 +547,15 @@ class WorkerThread(QtCore.QThread):
|
|||||||
os.remove(mobiPath)
|
os.remove(mobiPath)
|
||||||
if os.path.exists(mobiPath + '_toclean'):
|
if os.path.exists(mobiPath + '_toclean'):
|
||||||
os.remove(mobiPath + '_toclean')
|
os.remove(mobiPath + '_toclean')
|
||||||
MW.addMessage.emit('KindleUnpack failed to clean MOBI file!', 'error', False)
|
MW.addMessage.emit('Failed to process MOBI file!', 'error', False)
|
||||||
MW.addTrayMessage.emit('KindleUnpack failed to clean MOBI file!', 'Critical')
|
MW.addTrayMessage.emit('Failed to process MOBI file!', 'Critical')
|
||||||
else:
|
else:
|
||||||
GUI.progress.content = ''
|
GUI.progress.content = ''
|
||||||
epubSize = (os.path.getsize(self.kindlegenErrorCode[2]))/1024/1024
|
epubSize = (os.path.getsize(self.kindlegenErrorCode[2]))//1024//1024
|
||||||
for item in outputPath:
|
for item in outputPath:
|
||||||
if os.path.exists(item):
|
if os.path.exists(item):
|
||||||
os.remove(item)
|
os.remove(item)
|
||||||
|
sleep(1)
|
||||||
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'))
|
||||||
MW.addMessage.emit('KindleGen failed to create MOBI!', 'error', False)
|
MW.addMessage.emit('KindleGen failed to create MOBI!', 'error', False)
|
||||||
@@ -552,7 +564,7 @@ class WorkerThread(QtCore.QThread):
|
|||||||
MW.showDialog.emit("KindleGen error:\n\n" + self.kindlegenErrorCode[1], 'error')
|
MW.showDialog.emit("KindleGen error:\n\n" + self.kindlegenErrorCode[1], 'error')
|
||||||
if self.kindlegenErrorCode[0] == 23026:
|
if self.kindlegenErrorCode[0] == 23026:
|
||||||
MW.addMessage.emit('Created EPUB file was too big.', 'error', False)
|
MW.addMessage.emit('Created EPUB file was too big.', 'error', False)
|
||||||
MW.addMessage.emit('EPUB file: ' + str(epubSize) + 'MB. Supported size: ~300MB.', 'error',
|
MW.addMessage.emit('EPUB file: ' + str(epubSize) + 'MB. Supported size: ~350MB.', 'error',
|
||||||
False)
|
False)
|
||||||
else:
|
else:
|
||||||
for item in outputPath:
|
for item in outputPath:
|
||||||
@@ -576,6 +588,7 @@ class SystemTrayIcon(QtWidgets.QSystemTrayIcon):
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
if self.isSystemTrayAvailable():
|
if self.isSystemTrayAvailable():
|
||||||
QtWidgets.QSystemTrayIcon.__init__(self, GUI.icons.programIcon, MW)
|
QtWidgets.QSystemTrayIcon.__init__(self, GUI.icons.programIcon, MW)
|
||||||
|
# noinspection PyUnresolvedReferences
|
||||||
self.activated.connect(self.catchClicks)
|
self.activated.connect(self.catchClicks)
|
||||||
|
|
||||||
def catchClicks(self):
|
def catchClicks(self):
|
||||||
@@ -997,8 +1010,8 @@ class KCCGUI(KCC_ui.Ui_KCC):
|
|||||||
'customHeight': GUI.customHeight.text(),
|
'customHeight': GUI.customHeight.text(),
|
||||||
'GammaSlider': float(self.GammaValue)*100})
|
'GammaSlider': float(self.GammaValue)*100})
|
||||||
self.settings.sync()
|
self.settings.sync()
|
||||||
if not sys.platform.startswith('linux'):
|
self.tray.hide()
|
||||||
self.tray.hide()
|
APP.shutdown()
|
||||||
|
|
||||||
def handleMessage(self, message):
|
def handleMessage(self, message):
|
||||||
MW.raise_()
|
MW.raise_()
|
||||||
@@ -1073,6 +1086,7 @@ class KCCGUI(KCC_ui.Ui_KCC):
|
|||||||
self.versionCheck = VersionThread()
|
self.versionCheck = VersionThread()
|
||||||
self.contentServer = WebServerThread()
|
self.contentServer = WebServerThread()
|
||||||
self.progress = ProgressThread()
|
self.progress = ProgressThread()
|
||||||
|
self.tray = SystemTrayIcon()
|
||||||
self.conversionAlive = False
|
self.conversionAlive = False
|
||||||
self.needClean = True
|
self.needClean = True
|
||||||
self.GammaValue = 1.0
|
self.GammaValue = 1.0
|
||||||
@@ -1083,9 +1097,6 @@ class KCCGUI(KCC_ui.Ui_KCC):
|
|||||||
self.statusBarFontSize = 10
|
self.statusBarFontSize = 10
|
||||||
self.statusBarStyle = 'QLabel{padding-top:2px;padding-bottom:3px;}'
|
self.statusBarStyle = 'QLabel{padding-top:2px;padding-bottom:3px;}'
|
||||||
self.ProgressBar.setStyleSheet('QProgressBar{padding-top:5px;text-align:center;}')
|
self.ProgressBar.setStyleSheet('QProgressBar{padding-top:5px;text-align:center;}')
|
||||||
self.tray = SystemTrayIcon()
|
|
||||||
self.tray.show()
|
|
||||||
MW.addTrayMessage.connect(self.tray.addTrayMessage)
|
|
||||||
elif sys.platform.startswith('linux'):
|
elif sys.platform.startswith('linux'):
|
||||||
self.listFontSize = 8
|
self.listFontSize = 8
|
||||||
self.statusBarFontSize = 8
|
self.statusBarFontSize = 8
|
||||||
@@ -1096,9 +1107,11 @@ class KCCGUI(KCC_ui.Ui_KCC):
|
|||||||
self.statusBarFontSize = 8
|
self.statusBarFontSize = 8
|
||||||
self.statusBarStyle = 'QLabel{padding-top:3px;padding-bottom:3px}'
|
self.statusBarStyle = 'QLabel{padding-top:3px;padding-bottom:3px}'
|
||||||
self.statusBar.setStyleSheet('QStatusBar::item{border:0px;border-top:2px solid #C2C7CB;}')
|
self.statusBar.setStyleSheet('QStatusBar::item{border:0px;border-top:2px solid #C2C7CB;}')
|
||||||
self.tray = SystemTrayIcon()
|
# Decrease priority to increase system responsiveness during conversion
|
||||||
self.tray.show()
|
from psutil import BELOW_NORMAL_PRIORITY_CLASS
|
||||||
MW.addTrayMessage.connect(self.tray.addTrayMessage)
|
self.p = Process(os.getpid())
|
||||||
|
self.p.nice(BELOW_NORMAL_PRIORITY_CLASS)
|
||||||
|
self.p.ionice(1)
|
||||||
|
|
||||||
self.profiles = {
|
self.profiles = {
|
||||||
"Kindle Paperwhite": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0,
|
"Kindle Paperwhite": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0,
|
||||||
@@ -1157,7 +1170,7 @@ class KCCGUI(KCC_ui.Ui_KCC):
|
|||||||
"Kindle 2",
|
"Kindle 2",
|
||||||
]
|
]
|
||||||
|
|
||||||
statusBarLabel = QtWidgets.QLabel('<b><a href="http://kcc.vulturis.eu/">HOMEPAGE</a> - <a href="https://github.'
|
statusBarLabel = QtWidgets.QLabel('<b><a href="http://kcc.iosphe.re/">HOMEPAGE</a> - <a href="https://github.'
|
||||||
'com/ciromattia/kcc/blob/master/README.md#issues--new-features--donations">DO'
|
'com/ciromattia/kcc/blob/master/README.md#issues--new-features--donations">DO'
|
||||||
'NATE</a> - <a href="https://github.com/ciromattia/kcc/wiki">WIKI</a> - <a hr'
|
'NATE</a> - <a href="https://github.com/ciromattia/kcc/wiki">WIKI</a> - <a hr'
|
||||||
'ef="http://www.mobileread.com/forums/showthread.php?t=207461">FORUM</a></b>')
|
'ef="http://www.mobileread.com/forums/showthread.php?t=207461">FORUM</a></b>')
|
||||||
@@ -1175,6 +1188,11 @@ class KCCGUI(KCC_ui.Ui_KCC):
|
|||||||
self.addMessage('Since you are new user of <b>KCC</b> please see few '
|
self.addMessage('Since you are new user of <b>KCC</b> please see few '
|
||||||
'<a href="https://github.com/ciromattia/kcc/wiki/Important-tips">important tips</a>.',
|
'<a href="https://github.com/ciromattia/kcc/wiki/Important-tips">important tips</a>.',
|
||||||
'info')
|
'info')
|
||||||
|
if not sys.platform.startswith('win'):
|
||||||
|
try:
|
||||||
|
os.chmod('/usr/local/bin/kindlegen', 0o755)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
kindleGenExitCode = Popen('kindlegen -locale en', stdout=PIPE, stderr=STDOUT, shell=True)
|
kindleGenExitCode = Popen('kindlegen -locale en', stdout=PIPE, stderr=STDOUT, shell=True)
|
||||||
if kindleGenExitCode.wait() == 0:
|
if kindleGenExitCode.wait() == 0:
|
||||||
self.KindleGen = True
|
self.KindleGen = True
|
||||||
@@ -1238,6 +1256,7 @@ class KCCGUI(KCC_ui.Ui_KCC):
|
|||||||
MW.forceShutdown.connect(self.forceShutdown)
|
MW.forceShutdown.connect(self.forceShutdown)
|
||||||
MW.dialogAnswer.connect(self.versionCheck.getNewVersion)
|
MW.dialogAnswer.connect(self.versionCheck.getNewVersion)
|
||||||
MW.closeEvent = self.saveSettings
|
MW.closeEvent = self.saveSettings
|
||||||
|
MW.addTrayMessage.connect(self.tray.addTrayMessage)
|
||||||
|
|
||||||
GUI.Form.setAcceptDrops(True)
|
GUI.Form.setAcceptDrops(True)
|
||||||
GUI.Form.dragEnterEvent = self.dragAndDrop
|
GUI.Form.dragEnterEvent = self.dragAndDrop
|
||||||
@@ -1280,6 +1299,7 @@ class KCCGUI(KCC_ui.Ui_KCC):
|
|||||||
self.worker.sync()
|
self.worker.sync()
|
||||||
self.versionCheck.start()
|
self.versionCheck.start()
|
||||||
self.contentServer.start()
|
self.contentServer.start()
|
||||||
|
self.tray.show()
|
||||||
MW.setWindowTitle("Kindle Comic Converter " + __version__)
|
MW.setWindowTitle("Kindle Comic Converter " + __version__)
|
||||||
MW.show()
|
MW.show()
|
||||||
MW.raise_()
|
MW.raise_()
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
# Form implementation generated from reading ui file 'KCC.ui'
|
# Form implementation generated from reading ui file 'KCC.ui'
|
||||||
#
|
#
|
||||||
# Created: Sat Jan 25 17:36:53 2014
|
# Created: Sun May 18 09:08:27 2014
|
||||||
# by: PyQt5 UI code generator 5.2
|
# by: PyQt5 UI code generator 5.2.1
|
||||||
#
|
#
|
||||||
# WARNING! All changes made in this file will be lost!
|
# WARNING! All changes made in this file will be lost!
|
||||||
|
|
||||||
@@ -264,7 +264,7 @@ class Ui_KCC(object):
|
|||||||
self.ProcessingBox.setText(_translate("KCC", "No optimisation"))
|
self.ProcessingBox.setText(_translate("KCC", "No optimisation"))
|
||||||
self.UpscaleBox.setToolTip(_translate("KCC", "<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Nothing<br/></span>Images smaller than device resolution will not be resized.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - Stretching<br/></span>Images smaller than device resolution will be resized. Aspect ratio will be not preserved.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Upscaling<br/></span>Images smaller than device resolution will be resized. Aspect ratio will be preserved.</p></body></html>"))
|
self.UpscaleBox.setToolTip(_translate("KCC", "<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Nothing<br/></span>Images smaller than device resolution will not be resized.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - Stretching<br/></span>Images smaller than device resolution will be resized. Aspect ratio will be not preserved.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Upscaling<br/></span>Images smaller than device resolution will be resized. Aspect ratio will be preserved.</p></body></html>"))
|
||||||
self.UpscaleBox.setText(_translate("KCC", "Stretch/Upscale"))
|
self.UpscaleBox.setText(_translate("KCC", "Stretch/Upscale"))
|
||||||
self.WebtoonBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Enable auto-splitting of webtoons like <span style=\" font-style:italic;\">Tower of God</span> or <span style=\" font-style:italic;\">Noblesse</span>.<br/>Pages with a low width, high height and vertical panel flow.</p></body></html>"))
|
self.WebtoonBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\"white-space:pre\">Enable auto-splitting of webtoons like <span style=\" font-style:italic;\">Tower of God</span> or <span style=\" font-style:italic;\">Noblesse</span>.<br/>This mode is created for pages with a low width, high height and vertical panel flow.</p></body></html>"))
|
||||||
self.WebtoonBox.setText(_translate("KCC", "Webtoon mode"))
|
self.WebtoonBox.setText(_translate("KCC", "Webtoon mode"))
|
||||||
self.NoDitheringBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Create PNG files instead JPEG.<br/>Quality increase is not noticeable on most of devices.<br/>Output files <span style=\" font-weight:600;\">might</span> be smaller.<br/><span style=\" font-weight:600;\">MOBI conversion will be much slower.</span></p></body></html>"))
|
self.NoDitheringBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Create PNG files instead JPEG.<br/>Quality increase is not noticeable on most of devices.<br/>Output files <span style=\" font-weight:600;\">might</span> be smaller.<br/><span style=\" font-weight:600;\">MOBI conversion will be much slower.</span></p></body></html>"))
|
||||||
self.NoDitheringBox.setText(_translate("KCC", "PNG output"))
|
self.NoDitheringBox.setText(_translate("KCC", "PNG output"))
|
||||||
@@ -281,7 +281,7 @@ class Ui_KCC(object):
|
|||||||
self.ClearButton.setText(_translate("KCC", "Clear list"))
|
self.ClearButton.setText(_translate("KCC", "Clear list"))
|
||||||
self.MangaBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Enable right-to-left reading.</p></body></html>"))
|
self.MangaBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Enable right-to-left reading.</p></body></html>"))
|
||||||
self.MangaBox.setText(_translate("KCC", "Manga mode"))
|
self.MangaBox.setText(_translate("KCC", "Manga mode"))
|
||||||
self.QualityBox.setToolTip(_translate("KCC", "<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Normal quality mode<br/></span><span style=\" font-style:italic;\">Use it when Panel View support is not needed.</span><span style=\" font-weight:600; text-decoration: underline;\"><br/></span>- Maximum quality when zoom is not enabled.<br/>- Poor quality when zoom is enabled.<br/>- Lowest file size.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - High quality mode<br/></span><span style=\" font-style:italic;\">Not zoomed images </span><span style=\" font-weight:600; font-style:italic;\">might </span><span style=\" font-style:italic;\">be blurry.</span><span style=\" font-weight:600; text-decoration: underline;\"><br/></span>- High quality when zoom is not enabled.<br/>- Maximum quality when zoom is enabled.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Ultra quality mode<br/></span><span style=\" font-style:italic;\">Maximum possible quality.</span><span style=\" font-weight:600; text-decoration: underline;\"><br/></span>- Maximum quality when zoom is not enabled.<br/>- Maximum quality when zoom is enabled.<br/>- Very high file size.</p></body></html>"))
|
self.QualityBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\"white-space:pre\"><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Normal quality mode<br/></span>Maximal quality of images but very poor magnification quality.<br/>Use it only when zoom is not needed or output files needs to be small.</p><p style=\"white-space:pre\"><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - High quality mode<br/></span>In most cases high quality of images and magnification.<br/>Overall quality highly depends on the resolution of source files.<br/>On Kindle models older than Paperwhite non-zoomed images might be a little blurred.</p><p style=\"white-space:pre\"><span style=\" font-weight:600; text-decoration: underline;\">Checked - Ultra quality mode<br/></span>Highest possible quality. Output files will be big.</p></body></html>"))
|
||||||
self.QualityBox.setText(_translate("KCC", "High/Ultra quality"))
|
self.QualityBox.setText(_translate("KCC", "High/Ultra quality"))
|
||||||
self.RotateBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Disable splitting of two-page spreads.<br/>They will be rotated instead.</p></body></html>"))
|
self.RotateBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Disable splitting of two-page spreads.<br/>They will be rotated instead.</p></body></html>"))
|
||||||
self.RotateBox.setText(_translate("KCC", "Horizontal mode"))
|
self.RotateBox.setText(_translate("KCC", "Horizontal mode"))
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
# Form implementation generated from reading ui file 'KCC-Linux.ui'
|
# Form implementation generated from reading ui file 'KCC-Linux.ui'
|
||||||
#
|
#
|
||||||
# Created: Sat Jan 25 17:37:02 2014
|
# Created: Sun May 18 09:08:37 2014
|
||||||
# by: PyQt5 UI code generator 5.2
|
# by: PyQt5 UI code generator 5.2.1
|
||||||
#
|
#
|
||||||
# WARNING! All changes made in this file will be lost!
|
# WARNING! All changes made in this file will be lost!
|
||||||
|
|
||||||
@@ -333,7 +333,7 @@ class Ui_KCC(object):
|
|||||||
self.ProcessingBox.setText(_translate("KCC", "No optimisation"))
|
self.ProcessingBox.setText(_translate("KCC", "No optimisation"))
|
||||||
self.UpscaleBox.setToolTip(_translate("KCC", "<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Nothing<br/></span>Images smaller than device resolution will not be resized.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - Stretching<br/></span>Images smaller than device resolution will be resized. Aspect ratio will be not preserved.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Upscaling<br/></span>Images smaller than device resolution will be resized. Aspect ratio will be preserved.</p></body></html>"))
|
self.UpscaleBox.setToolTip(_translate("KCC", "<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Nothing<br/></span>Images smaller than device resolution will not be resized.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - Stretching<br/></span>Images smaller than device resolution will be resized. Aspect ratio will be not preserved.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Upscaling<br/></span>Images smaller than device resolution will be resized. Aspect ratio will be preserved.</p></body></html>"))
|
||||||
self.UpscaleBox.setText(_translate("KCC", "Stretch/Upscale"))
|
self.UpscaleBox.setText(_translate("KCC", "Stretch/Upscale"))
|
||||||
self.WebtoonBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Enable auto-splitting of webtoons like <span style=\" font-style:italic;\">Tower of God</span> or <span style=\" font-style:italic;\">Noblesse</span>.<br/>Pages with a low width, high height and vertical panel flow.</p></body></html>"))
|
self.WebtoonBox.setToolTip(_translate("KCC", "<html><head/><body><p>Enable auto-splitting of webtoons like <span style=\" font-style:italic;\">Tower of God</span> or <span style=\" font-style:italic;\">Noblesse</span>.<br/>This mode was created for pages with a low width, high height and vertical panel flow.</p></body></html>"))
|
||||||
self.WebtoonBox.setText(_translate("KCC", "Webtoon mode"))
|
self.WebtoonBox.setText(_translate("KCC", "Webtoon mode"))
|
||||||
self.NoDitheringBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Create PNG files instead JPEG.<br/>Quality increase is not noticeable on most of devices.<br/>Output files <span style=\" font-weight:600;\">might</span> be smaller.<br/><span style=\" font-weight:600;\">MOBI conversion will be much slower.</span></p></body></html>"))
|
self.NoDitheringBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Create PNG files instead JPEG.<br/>Quality increase is not noticeable on most of devices.<br/>Output files <span style=\" font-weight:600;\">might</span> be smaller.<br/><span style=\" font-weight:600;\">MOBI conversion will be much slower.</span></p></body></html>"))
|
||||||
self.NoDitheringBox.setText(_translate("KCC", "PNG output"))
|
self.NoDitheringBox.setText(_translate("KCC", "PNG output"))
|
||||||
@@ -350,7 +350,7 @@ class Ui_KCC(object):
|
|||||||
self.ClearButton.setText(_translate("KCC", "Clear list"))
|
self.ClearButton.setText(_translate("KCC", "Clear list"))
|
||||||
self.MangaBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Enable right-to-left reading.</p></body></html>"))
|
self.MangaBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Enable right-to-left reading.</p></body></html>"))
|
||||||
self.MangaBox.setText(_translate("KCC", "Manga mode"))
|
self.MangaBox.setText(_translate("KCC", "Manga mode"))
|
||||||
self.QualityBox.setToolTip(_translate("KCC", "<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Normal quality mode<br/></span><span style=\" font-style:italic;\">Use it when Panel View support is not needed.<br/></span>- Maximum quality when zoom is not enabled.<br/>- Poor quality when zoom is enabled.<br/>- Lowest file size.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - High quality mode<br/></span><span style=\" font-style:italic;\">Not zoomed images </span><span style=\" font-weight:600; font-style:italic;\">might</span><span style=\" font-style:italic;\"> be blurry.<br/></span>- High quality when zoom is not enabled.<br/>- Maximum quality when zoom is enabled.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Ultra quality mode<br/></span><span style=\" font-style:italic;\">Maximum possible quality.<br/></span>- Maximum quality when zoom is not enabled.<br/>- Maximum quality when zoom is enabled.<br/>- Very high file size.</p></body></html>"))
|
self.QualityBox.setToolTip(_translate("KCC", "<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Normal quality mode</span><br/>Maximal quality of images but very poor magnification quality.<br/>Use it only when zoom is not needed or output files needs to be small.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - High quality mode</span><br/>In most cases high quality of images and magnification.<br/>Overall quality highly depends on the resolution of source files.<br/>On Kindle models older than Paperwhite non-zoomed images might be a little blurred.<br/><br/><span style=\" font-weight:600; text-decoration: underline;\">Checked - Ultra quality mode</span><br/>Highest possible quality. Output files will be big.</p></body></html>"))
|
||||||
self.QualityBox.setText(_translate("KCC", "High/Ultra quality"))
|
self.QualityBox.setText(_translate("KCC", "High/Ultra quality"))
|
||||||
self.RotateBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Disable splitting of two-page spreads.<br/>They will be rotated instead.</p></body></html>"))
|
self.RotateBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Disable splitting of two-page spreads.<br/>They will be rotated instead.</p></body></html>"))
|
||||||
self.RotateBox.setText(_translate("KCC", "Horizontal mode"))
|
self.RotateBox.setText(_translate("KCC", "Horizontal mode"))
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
# Form implementation generated from reading ui file 'KCC-OSX.ui'
|
# Form implementation generated from reading ui file 'KCC-OSX.ui'
|
||||||
#
|
#
|
||||||
# Created: Sat Jan 25 17:37:10 2014
|
# Created: Sun May 18 09:08:44 2014
|
||||||
# by: PyQt5 UI code generator 5.2
|
# by: PyQt5 UI code generator 5.2.1
|
||||||
#
|
#
|
||||||
# WARNING! All changes made in this file will be lost!
|
# WARNING! All changes made in this file will be lost!
|
||||||
|
|
||||||
@@ -356,7 +356,7 @@ class Ui_KCC(object):
|
|||||||
self.ProcessingBox.setText(_translate("KCC", "No optimisation"))
|
self.ProcessingBox.setText(_translate("KCC", "No optimisation"))
|
||||||
self.UpscaleBox.setToolTip(_translate("KCC", "<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Nothing<br/></span>Images smaller than device resolution will not be resized.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - Stretching<br/></span>Images smaller than device resolution will be resized. Aspect ratio will be not preserved.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Upscaling<br/></span>Images smaller than device resolution will be resized. Aspect ratio will be preserved.</p></body></html>"))
|
self.UpscaleBox.setToolTip(_translate("KCC", "<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Nothing<br/></span>Images smaller than device resolution will not be resized.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - Stretching<br/></span>Images smaller than device resolution will be resized. Aspect ratio will be not preserved.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Upscaling<br/></span>Images smaller than device resolution will be resized. Aspect ratio will be preserved.</p></body></html>"))
|
||||||
self.UpscaleBox.setText(_translate("KCC", "Stretch/Upscale"))
|
self.UpscaleBox.setText(_translate("KCC", "Stretch/Upscale"))
|
||||||
self.WebtoonBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Enable auto-splitting of webtoons like <span style=\" font-style:italic;\">Tower of God</span> or <span style=\" font-style:italic;\">Noblesse</span>.<br/>Pages with a low width, high height and vertical panel flow.</p></body></html>"))
|
self.WebtoonBox.setToolTip(_translate("KCC", "<html><head/><body><p>Enable auto-splitting of webtoons like <span style=\" font-style:italic;\">Tower of God </span>or <span style=\" font-style:italic;\">Noblesse</span>.<br/>This mode was created for pages with a low width, high height and vertical panel flow.</p><p><br/></p></body></html>"))
|
||||||
self.WebtoonBox.setText(_translate("KCC", "Webtoon mode"))
|
self.WebtoonBox.setText(_translate("KCC", "Webtoon mode"))
|
||||||
self.NoDitheringBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Create PNG files instead JPEG.<br/>Quality increase is not noticeable on most of devices.<br/>Output files <span style=\" font-weight:600;\">might</span> be smaller.<br/><span style=\" font-weight:600;\">MOBI conversion will be much slower.</span></p></body></html>"))
|
self.NoDitheringBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Create PNG files instead JPEG.<br/>Quality increase is not noticeable on most of devices.<br/>Output files <span style=\" font-weight:600;\">might</span> be smaller.<br/><span style=\" font-weight:600;\">MOBI conversion will be much slower.</span></p></body></html>"))
|
||||||
self.NoDitheringBox.setText(_translate("KCC", "PNG output"))
|
self.NoDitheringBox.setText(_translate("KCC", "PNG output"))
|
||||||
@@ -373,7 +373,7 @@ class Ui_KCC(object):
|
|||||||
self.ClearButton.setText(_translate("KCC", "Clear list"))
|
self.ClearButton.setText(_translate("KCC", "Clear list"))
|
||||||
self.MangaBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Enable right-to-left reading.</p></body></html>"))
|
self.MangaBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Enable right-to-left reading.</p></body></html>"))
|
||||||
self.MangaBox.setText(_translate("KCC", "Manga mode"))
|
self.MangaBox.setText(_translate("KCC", "Manga mode"))
|
||||||
self.QualityBox.setToolTip(_translate("KCC", "<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Normal quality mode<br/></span><span style=\" font-style:italic;\">Use it when Panel View support is not needed.<br/></span>- Maximum quality when zoom is not enabled.<br/>- Poor quality when zoom is enabled.<br/>- Lowest file size.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - High quality mode<br/></span><span style=\" font-style:italic;\">Not zoomed image </span><span style=\" font-weight:600; font-style:italic;\">might</span><span style=\" font-style:italic;\"> be a little blurry.<br/></span>- High quality when zoom is not enabled.<br/>- Maximum quality when zoom is enabled.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Ultra quality mode<br/></span><span style=\" font-style:italic;\">Maximum possible quality.<br/></span>- Maximum quality when zoom is not enabled.<br/>- Maximum quality when zoom is enabled.<br/>- Very high file size.</p></body></html>"))
|
self.QualityBox.setToolTip(_translate("KCC", "<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Normal quality mode</span><br/>Maximal quality of images but very poor magnification quality.<br/>Use it only when zoom is not needed or output files needs to be small.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - High quality mode</span><br/>In most cases high quality of images and magnification.<br/>Overall quality highly depends on the resolution of source files.<br/>On Kindle models older than Paperwhite non-zoomed images might be a little blurred.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Ultra quality mode</span><br/>Highest possible quality. Output files will be big.</p></body></html>"))
|
||||||
self.QualityBox.setText(_translate("KCC", "High/Ultra quality"))
|
self.QualityBox.setText(_translate("KCC", "High/Ultra quality"))
|
||||||
self.RotateBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Disable splitting of two-page spreads.<br/>They will be rotated instead.</p></body></html>"))
|
self.RotateBox.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Disable splitting of two-page spreads.<br/>They will be rotated instead.</p></body></html>"))
|
||||||
self.RotateBox.setText(_translate("KCC", "Horizontal mode"))
|
self.RotateBox.setText(_translate("KCC", "Horizontal mode"))
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
__version__ = '4.0.2'
|
__version__ = '4.1'
|
||||||
__license__ = 'ISC'
|
__license__ = 'ISC'
|
||||||
__copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@vulturis.eu>'
|
__copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
||||||
# Copyright (c) 2013-2014 Pawel Jastrzebski <pawelj@vulturis.eu>
|
# Copyright (c) 2013-2014 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and/or distribute this software for
|
# Permission to use, copy, modify, and/or distribute this software for
|
||||||
# any purpose with or without fee is hereby granted, provided that the
|
# any purpose with or without fee is hereby granted, provided that the
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
__license__ = 'ISC'
|
__license__ = 'ISC'
|
||||||
__copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@vulturis.eu>'
|
__copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
||||||
# Copyright (c) 2013-2014 Pawel Jastrzebski <pawelj@vulturis.eu>
|
# Copyright (c) 2013-2014 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and/or distribute this software for
|
# Permission to use, copy, modify, and/or distribute this software for
|
||||||
# any purpose with or without fee is hereby granted, provided that the
|
# any purpose with or without fee is hereby granted, provided that the
|
||||||
@@ -18,9 +18,9 @@
|
|||||||
# PERFORMANCE OF THIS SOFTWARE.
|
# PERFORMANCE OF THIS SOFTWARE.
|
||||||
#
|
#
|
||||||
|
|
||||||
__version__ = '4.0.2'
|
__version__ = '4.1'
|
||||||
__license__ = 'ISC'
|
__license__ = 'ISC'
|
||||||
__copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@vulturis.eu>'
|
__copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
import os
|
import os
|
||||||
@@ -29,7 +29,7 @@ from re import split, sub
|
|||||||
from stat import S_IWRITE, S_IREAD, S_IEXEC
|
from stat import S_IWRITE, S_IREAD, S_IEXEC
|
||||||
from zipfile import ZipFile, ZIP_STORED, ZIP_DEFLATED
|
from zipfile import ZipFile, ZIP_STORED, ZIP_DEFLATED
|
||||||
from tempfile import mkdtemp
|
from tempfile import mkdtemp
|
||||||
from shutil import move, copyfile, copytree, rmtree
|
from shutil import move, copytree, rmtree
|
||||||
from optparse import OptionParser, OptionGroup
|
from optparse import OptionParser, OptionGroup
|
||||||
from multiprocessing import Pool
|
from multiprocessing import Pool
|
||||||
from xml.dom.minidom import parse
|
from xml.dom.minidom import parse
|
||||||
@@ -51,21 +51,27 @@ def buildHTML(path, imgfile, imgfilepath):
|
|||||||
imgfilepath = md5Checksum(imgfilepath)
|
imgfilepath = md5Checksum(imgfilepath)
|
||||||
filename = getImageFileName(imgfile)
|
filename = getImageFileName(imgfile)
|
||||||
if filename is not None:
|
if filename is not None:
|
||||||
if "Rotated" in theGreatIndex[imgfilepath]:
|
if options.imgproc:
|
||||||
rotatedPage = True
|
if "Rotated" in theGreatIndex[imgfilepath]:
|
||||||
|
rotatedPage = True
|
||||||
|
else:
|
||||||
|
rotatedPage = False
|
||||||
|
if "NoPanelView" in theGreatIndex[imgfilepath]:
|
||||||
|
noPV = True
|
||||||
|
else:
|
||||||
|
noPV = False
|
||||||
|
if "NoHorizontalPanelView" in theGreatIndex[imgfilepath]:
|
||||||
|
noHorizontalPV = True
|
||||||
|
else:
|
||||||
|
noHorizontalPV = False
|
||||||
|
if "NoVerticalPanelView" in theGreatIndex[imgfilepath]:
|
||||||
|
noVerticalPV = True
|
||||||
|
else:
|
||||||
|
noVerticalPV = False
|
||||||
else:
|
else:
|
||||||
rotatedPage = False
|
rotatedPage = False
|
||||||
if "NoPanelView" in theGreatIndex[imgfilepath]:
|
|
||||||
noPV = True
|
|
||||||
else:
|
|
||||||
noPV = False
|
noPV = False
|
||||||
if "NoHorizontalPanelView" in theGreatIndex[imgfilepath]:
|
|
||||||
noHorizontalPV = True
|
|
||||||
else:
|
|
||||||
noHorizontalPV = False
|
noHorizontalPV = False
|
||||||
if "NoVerticalPanelView" in theGreatIndex[imgfilepath]:
|
|
||||||
noVerticalPV = True
|
|
||||||
else:
|
|
||||||
noVerticalPV = False
|
noVerticalPV = False
|
||||||
htmlpath = ''
|
htmlpath = ''
|
||||||
postfix = ''
|
postfix = ''
|
||||||
@@ -163,30 +169,31 @@ def buildHTML(path, imgfile, imgfilepath):
|
|||||||
|
|
||||||
|
|
||||||
def checkMargins(path):
|
def checkMargins(path):
|
||||||
for flag in theGreatIndex[path]:
|
if options.imgproc:
|
||||||
if "Margins-" in flag:
|
for flag in theGreatIndex[path]:
|
||||||
flag = flag.split('-')
|
if "Margins-" in flag:
|
||||||
xl = flag[1]
|
flag = flag.split('-')
|
||||||
yu = flag[2]
|
xl = flag[1]
|
||||||
xr = flag[3]
|
yu = flag[2]
|
||||||
yd = flag[4]
|
xr = flag[3]
|
||||||
if xl != "0":
|
yd = flag[4]
|
||||||
xl = "-" + str(float(xl)/100) + "%"
|
if xl != "0":
|
||||||
else:
|
xl = "-" + str(float(xl)/100) + "%"
|
||||||
xl = "0%"
|
else:
|
||||||
if xr != "0":
|
xl = "0%"
|
||||||
xr = "-" + str(float(xr)/100) + "%"
|
if xr != "0":
|
||||||
else:
|
xr = "-" + str(float(xr)/100) + "%"
|
||||||
xr = "0%"
|
else:
|
||||||
if yu != "0":
|
xr = "0%"
|
||||||
yu = "-" + str(float(yu)/100) + "%"
|
if yu != "0":
|
||||||
else:
|
yu = "-" + str(float(yu)/100) + "%"
|
||||||
yu = "0%"
|
else:
|
||||||
if yd != "0":
|
yu = "0%"
|
||||||
yd = "-" + str(float(yd)/100) + "%"
|
if yd != "0":
|
||||||
else:
|
yd = "-" + str(float(yd)/100) + "%"
|
||||||
yd = "0%"
|
else:
|
||||||
return xl, yu, xr, yd
|
yd = "0%"
|
||||||
|
return xl, yu, xr, yd
|
||||||
return '0%', '0%', '0%', '0%'
|
return '0%', '0%', '0%', '0%'
|
||||||
|
|
||||||
|
|
||||||
@@ -218,13 +225,15 @@ def buildNCX(dstdir, title, chapters, chapterNames):
|
|||||||
+ ".html\"/></navPoint>\n")
|
+ ".html\"/></navPoint>\n")
|
||||||
f.write("</navMap>\n</ncx>")
|
f.write("</navMap>\n</ncx>")
|
||||||
f.close()
|
f.close()
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
def buildOPF(dstdir, title, filelist, cover=None):
|
def buildOPF(dstdir, title, filelist, cover=None):
|
||||||
opffile = os.path.join(dstdir, 'OEBPS', 'content.opf')
|
opffile = os.path.join(dstdir, 'OEBPS', 'content.opf')
|
||||||
profilelabel, deviceres, palette, gamma, panelviewsize = options.profileData
|
profilelabel, deviceres, palette, gamma, panelviewsize = options.profileData
|
||||||
imgres = str(deviceres[0]) + "x" + str(deviceres[1])
|
if options.quality == 1:
|
||||||
|
imgres = str(panelviewsize[0]) + "x" + str(panelviewsize[1])
|
||||||
|
else:
|
||||||
|
imgres = str(deviceres[0]) + "x" + str(deviceres[1])
|
||||||
if options.righttoleft:
|
if options.righttoleft:
|
||||||
writingmode = "horizontal-rl"
|
writingmode = "horizontal-rl"
|
||||||
else:
|
else:
|
||||||
@@ -294,14 +303,13 @@ def buildOPF(dstdir, title, filelist, cover=None):
|
|||||||
"</rootfiles>\n",
|
"</rootfiles>\n",
|
||||||
"</container>"])
|
"</container>"])
|
||||||
f.close()
|
f.close()
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
def applyImgOptimization(img, opt, hqImage=None):
|
def applyImgOptimization(img, opt, hqImage=None):
|
||||||
if not img.fill:
|
if not img.fill:
|
||||||
img.getImageFill(opt.webtoon)
|
img.getImageFill(opt.webtoon)
|
||||||
if not opt.webtoon:
|
if not opt.webtoon:
|
||||||
img.cropWhiteSpace(10.0)
|
img.cropWhiteSpace()
|
||||||
if opt.cutpagenumbers and not opt.webtoon:
|
if opt.cutpagenumbers and not opt.webtoon:
|
||||||
img.cutPageNumber()
|
img.cutPageNumber()
|
||||||
img.optimizeImage(opt.gamma)
|
img.optimizeImage(opt.gamma)
|
||||||
@@ -551,7 +559,7 @@ def genEpubStruct(path, chapterNames):
|
|||||||
if cover is None:
|
if cover is None:
|
||||||
cover = os.path.join(os.path.join(path, 'OEBPS', 'Images'),
|
cover = os.path.join(os.path.join(path, 'OEBPS', 'Images'),
|
||||||
'cover' + getImageFileName(filelist[-1][1])[1])
|
'cover' + getImageFileName(filelist[-1][1])[1])
|
||||||
copyfile(os.path.join(filelist[-1][0], filelist[-1][1]), cover)
|
image.Cover(os.path.join(filelist[-1][0], filelist[-1][1]), cover)
|
||||||
buildNCX(path, options.title, chapterlist, chapterNames)
|
buildNCX(path, options.title, chapterlist, chapterNames)
|
||||||
# Ensure we're sorting files alphabetically
|
# Ensure we're sorting files alphabetically
|
||||||
convert = lambda text: int(text) if text.isdigit() else text
|
convert = lambda text: int(text) if text.isdigit() else text
|
||||||
@@ -722,7 +730,7 @@ def splitDirectory(path, mode):
|
|||||||
for root, dirs, files in walkLevel(path, 0):
|
for root, dirs, files in walkLevel(path, 0):
|
||||||
for name in files:
|
for name in files:
|
||||||
size = os.path.getsize(os.path.join(root, name))
|
size = os.path.getsize(os.path.join(root, name))
|
||||||
if currentSize + size > 262144000:
|
if currentSize + size > 419430400:
|
||||||
currentTarget, pathRoot = createNewTome()
|
currentTarget, pathRoot = createNewTome()
|
||||||
output.append(pathRoot)
|
output.append(pathRoot)
|
||||||
currentSize = size
|
currentSize = size
|
||||||
@@ -734,7 +742,7 @@ def splitDirectory(path, mode):
|
|||||||
for root, dirs, files in walkLevel(path, 0):
|
for root, dirs, files in walkLevel(path, 0):
|
||||||
for name in dirs:
|
for name in dirs:
|
||||||
size = getDirectorySize(os.path.join(root, name))
|
size = getDirectorySize(os.path.join(root, name))
|
||||||
if currentSize + size > 262144000:
|
if currentSize + size > 419430400:
|
||||||
currentTarget, pathRoot = createNewTome()
|
currentTarget, pathRoot = createNewTome()
|
||||||
output.append(pathRoot)
|
output.append(pathRoot)
|
||||||
currentSize = size
|
currentSize = size
|
||||||
@@ -748,7 +756,7 @@ def splitDirectory(path, mode):
|
|||||||
for name in dirs:
|
for name in dirs:
|
||||||
size = getDirectorySize(os.path.join(root, name))
|
size = getDirectorySize(os.path.join(root, name))
|
||||||
currentSize = 0
|
currentSize = 0
|
||||||
if size > 262144000:
|
if size > 419430400:
|
||||||
if not firstTome:
|
if not firstTome:
|
||||||
currentTarget, pathRoot = createNewTome()
|
currentTarget, pathRoot = createNewTome()
|
||||||
output.append(pathRoot)
|
output.append(pathRoot)
|
||||||
@@ -757,7 +765,7 @@ def splitDirectory(path, mode):
|
|||||||
for rootInside, dirsInside, filesInside in walkLevel(os.path.join(root, name), 0):
|
for rootInside, dirsInside, filesInside in walkLevel(os.path.join(root, name), 0):
|
||||||
for nameInside in dirsInside:
|
for nameInside in dirsInside:
|
||||||
size = getDirectorySize(os.path.join(rootInside, nameInside))
|
size = getDirectorySize(os.path.join(rootInside, nameInside))
|
||||||
if currentSize + size > 262144000:
|
if currentSize + size > 419430400:
|
||||||
currentTarget, pathRoot = createNewTome()
|
currentTarget, pathRoot = createNewTome()
|
||||||
output.append(pathRoot)
|
output.append(pathRoot)
|
||||||
currentSize = size
|
currentSize = size
|
||||||
@@ -777,65 +785,61 @@ def splitDirectory(path, mode):
|
|||||||
|
|
||||||
#noinspection PyUnboundLocalVariable
|
#noinspection PyUnboundLocalVariable
|
||||||
def preSplitDirectory(path):
|
def preSplitDirectory(path):
|
||||||
if getDirectorySize(os.path.join(path, 'OEBPS', 'Images')) > 262144000:
|
# Detect directory stucture
|
||||||
# Detect directory stucture
|
for root, dirs, files in walkLevel(os.path.join(path, 'OEBPS', 'Images'), 0):
|
||||||
for root, dirs, files in walkLevel(os.path.join(path, 'OEBPS', 'Images'), 0):
|
subdirectoryNumber = len(dirs)
|
||||||
subdirectoryNumber = len(dirs)
|
filesNumber = len(files)
|
||||||
filesNumber = len(files)
|
if subdirectoryNumber == 0:
|
||||||
if subdirectoryNumber == 0:
|
# No subdirectories
|
||||||
# No subdirectories
|
mode = 0
|
||||||
mode = 0
|
|
||||||
else:
|
|
||||||
if filesNumber > 0:
|
|
||||||
print('\nWARNING: Automatic output splitting failed.')
|
|
||||||
if GUI:
|
|
||||||
GUI.addMessage.emit('Automatic output splitting failed. <a href='
|
|
||||||
'"https://github.com/ciromattia/kcc/wiki'
|
|
||||||
'/Automatic-output-splitting">'
|
|
||||||
'More details.</a>', 'warning', False)
|
|
||||||
GUI.addMessage.emit('', '', False)
|
|
||||||
return [path]
|
|
||||||
detectedSubSubdirectories = False
|
|
||||||
detectedFilesInSubdirectories = False
|
|
||||||
for root, dirs, files in walkLevel(os.path.join(path, 'OEBPS', 'Images'), 1):
|
|
||||||
if root != os.path.join(path, 'OEBPS', 'Images'):
|
|
||||||
if len(dirs) != 0:
|
|
||||||
detectedSubSubdirectories = True
|
|
||||||
elif len(dirs) == 0 and detectedSubSubdirectories:
|
|
||||||
print('\nWARNING: Automatic output splitting failed.')
|
|
||||||
if GUI:
|
|
||||||
GUI.addMessage.emit('Automatic output splitting failed. <a href='
|
|
||||||
'"https://github.com/ciromattia/kcc/wiki'
|
|
||||||
'/Automatic-output-splitting">'
|
|
||||||
'More details.</a>', 'warning', False)
|
|
||||||
GUI.addMessage.emit('', '', False)
|
|
||||||
return [path]
|
|
||||||
if len(files) != 0:
|
|
||||||
detectedFilesInSubdirectories = True
|
|
||||||
if detectedSubSubdirectories:
|
|
||||||
# Two levels of subdirectories
|
|
||||||
mode = 2
|
|
||||||
else:
|
|
||||||
# One level of subdirectories
|
|
||||||
mode = 1
|
|
||||||
if detectedFilesInSubdirectories and detectedSubSubdirectories:
|
|
||||||
print('\nWARNING: Automatic output splitting failed.')
|
|
||||||
if GUI:
|
|
||||||
GUI.addMessage.emit('Automatic output splitting failed. <a href='
|
|
||||||
'"https://github.com/ciromattia/kcc/wiki'
|
|
||||||
'/Automatic-output-splitting">'
|
|
||||||
'More details.</a>', 'warning', False)
|
|
||||||
GUI.addMessage.emit('', '', False)
|
|
||||||
return [path]
|
|
||||||
# Split directories
|
|
||||||
splitter = splitDirectory(os.path.join(path, 'OEBPS', 'Images'), mode)
|
|
||||||
path = [path]
|
|
||||||
for tome in splitter:
|
|
||||||
path.append(tome)
|
|
||||||
return path
|
|
||||||
else:
|
else:
|
||||||
# No splitting is necessary
|
if filesNumber > 0:
|
||||||
return [path]
|
print('\nWARNING: Automatic output splitting failed.')
|
||||||
|
if GUI:
|
||||||
|
GUI.addMessage.emit('Automatic output splitting failed. <a href='
|
||||||
|
'"https://github.com/ciromattia/kcc/wiki'
|
||||||
|
'/Automatic-output-splitting">'
|
||||||
|
'More details.</a>', 'warning', False)
|
||||||
|
GUI.addMessage.emit('', '', False)
|
||||||
|
return [path]
|
||||||
|
detectedSubSubdirectories = False
|
||||||
|
detectedFilesInSubdirectories = False
|
||||||
|
for root, dirs, files in walkLevel(os.path.join(path, 'OEBPS', 'Images'), 1):
|
||||||
|
if root != os.path.join(path, 'OEBPS', 'Images'):
|
||||||
|
if len(dirs) != 0:
|
||||||
|
detectedSubSubdirectories = True
|
||||||
|
elif len(dirs) == 0 and detectedSubSubdirectories:
|
||||||
|
print('\nWARNING: Automatic output splitting failed.')
|
||||||
|
if GUI:
|
||||||
|
GUI.addMessage.emit('Automatic output splitting failed. <a href='
|
||||||
|
'"https://github.com/ciromattia/kcc/wiki'
|
||||||
|
'/Automatic-output-splitting">'
|
||||||
|
'More details.</a>', 'warning', False)
|
||||||
|
GUI.addMessage.emit('', '', False)
|
||||||
|
return [path]
|
||||||
|
if len(files) != 0:
|
||||||
|
detectedFilesInSubdirectories = True
|
||||||
|
if detectedSubSubdirectories:
|
||||||
|
# Two levels of subdirectories
|
||||||
|
mode = 2
|
||||||
|
else:
|
||||||
|
# One level of subdirectories
|
||||||
|
mode = 1
|
||||||
|
if detectedFilesInSubdirectories and detectedSubSubdirectories:
|
||||||
|
print('\nWARNING: Automatic output splitting failed.')
|
||||||
|
if GUI:
|
||||||
|
GUI.addMessage.emit('Automatic output splitting failed. <a href='
|
||||||
|
'"https://github.com/ciromattia/kcc/wiki'
|
||||||
|
'/Automatic-output-splitting">'
|
||||||
|
'More details.</a>', 'warning', False)
|
||||||
|
GUI.addMessage.emit('', '', False)
|
||||||
|
return [path]
|
||||||
|
# Split directories
|
||||||
|
splitter = splitDirectory(os.path.join(path, 'OEBPS', 'Images'), mode)
|
||||||
|
path = [path]
|
||||||
|
for tome in splitter:
|
||||||
|
path.append(tome)
|
||||||
|
return path
|
||||||
|
|
||||||
|
|
||||||
def detectCorruption(tmpPath, orgPath):
|
def detectCorruption(tmpPath, orgPath):
|
||||||
@@ -881,14 +885,16 @@ def Usage():
|
|||||||
parser.print_help()
|
parser.print_help()
|
||||||
|
|
||||||
|
|
||||||
def main(argv=None, qtGUI=None):
|
def makeParser():
|
||||||
global parser, options, GUI
|
"""Create and return an option parser set up with kcc's options."""
|
||||||
parser = OptionParser(usage="Usage: kcc-c2e [options] comic_file|comic_folder", add_help_option=False)
|
psr = OptionParser(usage="Usage: kcc-c2e [options] comic_file|comic_folder", add_help_option=False)
|
||||||
mainOptions = OptionGroup(parser, "MAIN")
|
|
||||||
processingOptions = OptionGroup(parser, "PROCESSING")
|
mainOptions = OptionGroup(psr, "MAIN")
|
||||||
outputOptions = OptionGroup(parser, "OUTPUT SETTINGS")
|
processingOptions = OptionGroup(psr, "PROCESSING")
|
||||||
customProfileOptions = OptionGroup(parser, "CUSTOM PROFILE")
|
outputOptions = OptionGroup(psr, "OUTPUT SETTINGS")
|
||||||
otherOptions = OptionGroup(parser, "OTHER")
|
customProfileOptions = OptionGroup(psr, "CUSTOM PROFILE")
|
||||||
|
otherOptions = OptionGroup(psr, "OTHER")
|
||||||
|
|
||||||
mainOptions.add_option("-p", "--profile", action="store", dest="profile", default="KHD",
|
mainOptions.add_option("-p", "--profile", action="store", dest="profile", default="KHD",
|
||||||
help="Device profile (Choose one among K1, K2, K345, KDX, KHD, KF, KFHD, KFHD8, KFHDX,"
|
help="Device profile (Choose one among K1, K2, K345, KDX, KHD, KF, KFHD, KFHD8, KFHDX,"
|
||||||
" KFHDX8, KFA, KoMT, KoG, KoA, KoAHD) [Default=KHD]")
|
" KFHDX8, KFA, KoMT, KoG, KoA, KoAHD) [Default=KHD]")
|
||||||
@@ -898,6 +904,7 @@ def main(argv=None, qtGUI=None):
|
|||||||
help="Manga style (Right-to-left reading and splitting)")
|
help="Manga style (Right-to-left reading and splitting)")
|
||||||
mainOptions.add_option("-w", "--webtoon", action="store_true", dest="webtoon", default=False,
|
mainOptions.add_option("-w", "--webtoon", action="store_true", dest="webtoon", default=False,
|
||||||
help="Webtoon processing mode"),
|
help="Webtoon processing mode"),
|
||||||
|
|
||||||
outputOptions.add_option("-o", "--output", action="store", dest="output", default=None,
|
outputOptions.add_option("-o", "--output", action="store", dest="output", default=None,
|
||||||
help="Output generated file to specified directory or file")
|
help="Output generated file to specified directory or file")
|
||||||
outputOptions.add_option("-t", "--title", action="store", dest="title", default="defaulttitle",
|
outputOptions.add_option("-t", "--title", action="store", dest="title", default="defaulttitle",
|
||||||
@@ -906,6 +913,7 @@ def main(argv=None, qtGUI=None):
|
|||||||
help="Outputs a CBZ archive and does not generate EPUB")
|
help="Outputs a CBZ archive and does not generate EPUB")
|
||||||
outputOptions.add_option("--batchsplit", action="store_true", dest="batchsplit", default=False,
|
outputOptions.add_option("--batchsplit", action="store_true", dest="batchsplit", default=False,
|
||||||
help="Split output into multiple files"),
|
help="Split output into multiple files"),
|
||||||
|
|
||||||
processingOptions.add_option("--blackborders", action="store_true", dest="black_borders", default=False,
|
processingOptions.add_option("--blackborders", action="store_true", dest="black_borders", default=False,
|
||||||
help="Disable autodetection and force black borders")
|
help="Disable autodetection and force black borders")
|
||||||
processingOptions.add_option("--whiteborders", action="store_true", dest="white_borders", default=False,
|
processingOptions.add_option("--whiteborders", action="store_true", dest="white_borders", default=False,
|
||||||
@@ -928,17 +936,26 @@ def main(argv=None, qtGUI=None):
|
|||||||
help="Stretch images to device's resolution")
|
help="Stretch images to device's resolution")
|
||||||
processingOptions.add_option("--upscale", action="store_true", dest="upscale", default=False,
|
processingOptions.add_option("--upscale", action="store_true", dest="upscale", default=False,
|
||||||
help="Resize images smaller than device's resolution")
|
help="Resize images smaller than device's resolution")
|
||||||
|
|
||||||
customProfileOptions.add_option("--customwidth", type="int", dest="customwidth", default=0,
|
customProfileOptions.add_option("--customwidth", type="int", dest="customwidth", default=0,
|
||||||
help="Replace screen width provided by device profile")
|
help="Replace screen width provided by device profile")
|
||||||
customProfileOptions.add_option("--customheight", type="int", dest="customheight", default=0,
|
customProfileOptions.add_option("--customheight", type="int", dest="customheight", default=0,
|
||||||
help="Replace screen height provided by device profile")
|
help="Replace screen height provided by device profile")
|
||||||
|
|
||||||
otherOptions.add_option("-h", "--help", action="help",
|
otherOptions.add_option("-h", "--help", action="help",
|
||||||
help="Show this help message and exit")
|
help="Show this help message and exit")
|
||||||
parser.add_option_group(mainOptions)
|
|
||||||
parser.add_option_group(outputOptions)
|
psr.add_option_group(mainOptions)
|
||||||
parser.add_option_group(processingOptions)
|
psr.add_option_group(outputOptions)
|
||||||
parser.add_option_group(customProfileOptions)
|
psr.add_option_group(processingOptions)
|
||||||
parser.add_option_group(otherOptions)
|
psr.add_option_group(customProfileOptions)
|
||||||
|
psr.add_option_group(otherOptions)
|
||||||
|
return psr
|
||||||
|
|
||||||
|
|
||||||
|
def main(argv=None, qtGUI=None):
|
||||||
|
global parser, options, GUI
|
||||||
|
parser = makeParser()
|
||||||
options, args = parser.parse_args(argv)
|
options, args = parser.parse_args(argv)
|
||||||
checkOptions()
|
checkOptions()
|
||||||
if qtGUI:
|
if qtGUI:
|
||||||
@@ -949,29 +966,44 @@ def main(argv=None, qtGUI=None):
|
|||||||
if len(args) != 1:
|
if len(args) != 1:
|
||||||
parser.print_help()
|
parser.print_help()
|
||||||
return
|
return
|
||||||
path = getWorkFolder(args[0])
|
outputPath = makeBook(args[0], qtGUI=qtGUI)
|
||||||
|
return outputPath
|
||||||
|
|
||||||
|
|
||||||
|
def makeBook(source, qtGUI=None):
|
||||||
|
"""Generates EPUB/CBZ comic ebook from a bunch of images."""
|
||||||
|
global GUI
|
||||||
|
GUI = qtGUI
|
||||||
|
path = getWorkFolder(source)
|
||||||
print("\nChecking images...")
|
print("\nChecking images...")
|
||||||
detectCorruption(os.path.join(path, "OEBPS", "Images"), args[0])
|
detectCorruption(os.path.join(path, "OEBPS", "Images"), source)
|
||||||
checkComicInfo(os.path.join(path, "OEBPS", "Images"), args[0])
|
checkComicInfo(os.path.join(path, "OEBPS", "Images"), source)
|
||||||
|
|
||||||
if options.webtoon:
|
if options.webtoon:
|
||||||
if options.customheight > 0:
|
if options.customheight > 0:
|
||||||
comic2panel.main(['-y ' + str(options.customheight), '-i', '-m', path], qtGUI)
|
comic2panel.main(['-y ' + str(options.customheight), '-i', '-m', path], qtGUI)
|
||||||
else:
|
else:
|
||||||
comic2panel.main(['-y ' + str(image.ProfileData.Profiles[options.profile][1][1]), '-i', '-m', path], qtGUI)
|
comic2panel.main(['-y ' + str(image.ProfileData.Profiles[options.profile][1][1]), '-i', '-m', path], qtGUI)
|
||||||
|
|
||||||
if options.imgproc:
|
if options.imgproc:
|
||||||
print("\nProcessing images...")
|
print("\nProcessing images...")
|
||||||
if GUI:
|
if GUI:
|
||||||
GUI.progressBarTick.emit('Processing images')
|
GUI.progressBarTick.emit('Processing images')
|
||||||
dirImgProcess(os.path.join(path, "OEBPS", "Images"))
|
dirImgProcess(os.path.join(path, "OEBPS", "Images"))
|
||||||
|
|
||||||
if GUI:
|
if GUI:
|
||||||
GUI.progressBarTick.emit('1')
|
GUI.progressBarTick.emit('1')
|
||||||
|
|
||||||
chapterNames = sanitizeTree(os.path.join(path, 'OEBPS', 'Images'))
|
chapterNames = sanitizeTree(os.path.join(path, 'OEBPS', 'Images'))
|
||||||
|
|
||||||
if options.batchsplit:
|
if options.batchsplit:
|
||||||
tomes = preSplitDirectory(path)
|
tomes = preSplitDirectory(path)
|
||||||
else:
|
else:
|
||||||
tomes = [path]
|
tomes = [path]
|
||||||
|
|
||||||
filepath = []
|
filepath = []
|
||||||
tomeNumber = 0
|
tomeNumber = 0
|
||||||
|
|
||||||
if GUI:
|
if GUI:
|
||||||
if options.cbzoutput:
|
if options.cbzoutput:
|
||||||
GUI.progressBarTick.emit('Compressing CBZ files')
|
GUI.progressBarTick.emit('Compressing CBZ files')
|
||||||
@@ -979,36 +1011,43 @@ def main(argv=None, qtGUI=None):
|
|||||||
GUI.progressBarTick.emit('Compressing EPUB files')
|
GUI.progressBarTick.emit('Compressing EPUB files')
|
||||||
GUI.progressBarTick.emit(str(len(tomes) + 1))
|
GUI.progressBarTick.emit(str(len(tomes) + 1))
|
||||||
GUI.progressBarTick.emit('tick')
|
GUI.progressBarTick.emit('tick')
|
||||||
|
|
||||||
options.baseTitle = options.title
|
options.baseTitle = options.title
|
||||||
|
|
||||||
for tome in tomes:
|
for tome in tomes:
|
||||||
if len(tomes) > 1:
|
if len(tomes) > 1:
|
||||||
tomeNumber += 1
|
tomeNumber += 1
|
||||||
options.title = options.baseTitle + ' [' + str(tomeNumber) + '/' + str(len(tomes)) + ']'
|
options.title = options.baseTitle + ' [' + str(tomeNumber) + '/' + str(len(tomes)) + ']'
|
||||||
|
|
||||||
if options.cbzoutput:
|
if options.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...")
|
||||||
if len(tomes) > 1:
|
if len(tomes) > 1:
|
||||||
filepath.append(getOutputFilename(args[0], options.output, '.cbz', ' ' + str(tomeNumber)))
|
filepath.append(getOutputFilename(source, options.output, '.cbz', ' ' + str(tomeNumber)))
|
||||||
else:
|
else:
|
||||||
filepath.append(getOutputFilename(args[0], options.output, '.cbz', ''))
|
filepath.append(getOutputFilename(source, options.output, '.cbz', ''))
|
||||||
makeZIP(tome + '_comic', os.path.join(tome, "OEBPS", "Images"))
|
makeZIP(tome + '_comic', os.path.join(tome, "OEBPS", "Images"))
|
||||||
else:
|
else:
|
||||||
print("\nCreating EPUB structure...")
|
print("\nCreating EPUB structure...")
|
||||||
genEpubStruct(tome, chapterNames)
|
genEpubStruct(tome, chapterNames)
|
||||||
# actually zip the ePub
|
# actually zip the ePub
|
||||||
if len(tomes) > 1:
|
if len(tomes) > 1:
|
||||||
filepath.append(getOutputFilename(args[0], options.output, '.epub', ' ' + str(tomeNumber)))
|
filepath.append(getOutputFilename(source, options.output, '.epub', ' ' + str(tomeNumber)))
|
||||||
else:
|
else:
|
||||||
filepath.append(getOutputFilename(args[0], options.output, '.epub', ''))
|
filepath.append(getOutputFilename(source, options.output, '.epub', ''))
|
||||||
makeZIP(tome + '_comic', tome, True)
|
makeZIP(tome + '_comic', tome, True)
|
||||||
|
|
||||||
move(tome + '_comic.zip', filepath[-1])
|
move(tome + '_comic.zip', filepath[-1])
|
||||||
rmtree(tome, True)
|
rmtree(tome, True)
|
||||||
|
|
||||||
if GUI:
|
if GUI:
|
||||||
GUI.progressBarTick.emit('tick')
|
GUI.progressBarTick.emit('tick')
|
||||||
return filepath
|
return filepath
|
||||||
|
|
||||||
|
|
||||||
def getOutputFilename(srcpath, wantedname, ext, tomeNumber):
|
def getOutputFilename(srcpath, wantedname, ext, tomeNumber):
|
||||||
|
if srcpath[-1] == os.path.sep:
|
||||||
|
srcpath = srcpath[:-1]
|
||||||
if not ext.startswith('.'):
|
if not ext.startswith('.'):
|
||||||
ext = '.' + ext
|
ext = '.' + ext
|
||||||
if wantedname is not None:
|
if wantedname is not None:
|
||||||
@@ -1083,4 +1122,4 @@ def checkOptions():
|
|||||||
(int(X*1.5), int(Y*1.5)))
|
(int(X*1.5), int(Y*1.5)))
|
||||||
image.ProfileData.Profiles["Custom"] = newProfile
|
image.ProfileData.Profiles["Custom"] = newProfile
|
||||||
options.profile = "Custom"
|
options.profile = "Custom"
|
||||||
options.profileData = image.ProfileData.Profiles[options.profile]
|
options.profileData = image.ProfileData.Profiles[options.profile]
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
||||||
# Copyright (c) 2013-2014 Pawel Jastrzebski <pawelj@vulturis.eu>
|
# Copyright (c) 2013-2014 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and/or distribute this software for
|
# Permission to use, copy, modify, and/or distribute this software for
|
||||||
# any purpose with or without fee is hereby granted, provided that the
|
# any purpose with or without fee is hereby granted, provided that the
|
||||||
@@ -18,9 +18,9 @@
|
|||||||
# PERFORMANCE OF THIS SOFTWARE.
|
# PERFORMANCE OF THIS SOFTWARE.
|
||||||
#
|
#
|
||||||
|
|
||||||
__version__ = '4.0.2'
|
__version__ = '4.1'
|
||||||
__license__ = 'ISC'
|
__license__ = 'ISC'
|
||||||
__copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@vulturis.eu>'
|
__copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
import os
|
import os
|
||||||
@@ -124,7 +124,7 @@ def splitImage(work):
|
|||||||
path = work[0]
|
path = work[0]
|
||||||
name = work[1]
|
name = work[1]
|
||||||
opt = work[2]
|
opt = work[2]
|
||||||
# Harcoded opttions
|
# Hardcoded options
|
||||||
threshold = 1.0
|
threshold = 1.0
|
||||||
delta = 15
|
delta = 15
|
||||||
fileExpanded = os.path.splitext(name)
|
fileExpanded = os.path.splitext(name)
|
||||||
|
|||||||
184
kcc/dualmetafix.py
Normal file
184
kcc/dualmetafix.py
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# Based on initial version of DualMetaFix. Copyright (C) 2013 Kevin Hendricks
|
||||||
|
# Changes for KCC Copyright (C) 2014 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import struct
|
||||||
|
import mmap
|
||||||
|
import shutil
|
||||||
|
|
||||||
|
|
||||||
|
class DualMetaFixException(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
# palm database offset constants
|
||||||
|
number_of_pdb_records = 76
|
||||||
|
first_pdb_record = 78
|
||||||
|
|
||||||
|
# important rec0 offsets
|
||||||
|
mobi_header_base = 16
|
||||||
|
mobi_header_length = 20
|
||||||
|
mobi_version = 36
|
||||||
|
title_offset = 84
|
||||||
|
|
||||||
|
|
||||||
|
def getint(data, ofs, sz='L'):
|
||||||
|
i, = struct.unpack_from('>'+sz, data, ofs)
|
||||||
|
return i
|
||||||
|
|
||||||
|
|
||||||
|
def writeint(data, ofs, n, slen='L'):
|
||||||
|
if slen == 'L':
|
||||||
|
return data[:ofs]+struct.pack('>L', n)+data[ofs+4:]
|
||||||
|
else:
|
||||||
|
return data[:ofs]+struct.pack('>H', n)+data[ofs+2:]
|
||||||
|
|
||||||
|
|
||||||
|
def getsecaddr(datain, secno):
|
||||||
|
nsec = getint(datain, number_of_pdb_records, 'H')
|
||||||
|
if (secno < 0) | (secno >= nsec):
|
||||||
|
emsg = 'requested section number %d out of range (nsec=%d)' % (secno, nsec)
|
||||||
|
raise DualMetaFixException(emsg)
|
||||||
|
secstart = getint(datain, first_pdb_record+secno*8)
|
||||||
|
if secno == nsec-1:
|
||||||
|
secend = len(datain)
|
||||||
|
else:
|
||||||
|
secend = getint(datain, first_pdb_record+(secno+1)*8)
|
||||||
|
return secstart, secend
|
||||||
|
|
||||||
|
|
||||||
|
def readsection(datain, secno):
|
||||||
|
secstart, secend = getsecaddr(datain, secno)
|
||||||
|
return datain[secstart:secend]
|
||||||
|
|
||||||
|
|
||||||
|
# overwrite section - must be exact same length as original
|
||||||
|
def replacesection(datain, secno, secdata):
|
||||||
|
secstart, secend = getsecaddr(datain, secno)
|
||||||
|
seclen = secend - secstart
|
||||||
|
if len(secdata) != seclen:
|
||||||
|
raise DualMetaFixException('section length change in replacesection')
|
||||||
|
datain[secstart:secstart+seclen] = secdata
|
||||||
|
|
||||||
|
|
||||||
|
def get_exth_params(rec0):
|
||||||
|
ebase = mobi_header_base + getint(rec0, mobi_header_length)
|
||||||
|
if rec0[ebase:ebase+4] != b'EXTH':
|
||||||
|
raise DualMetaFixException('EXTH tag not found where expected')
|
||||||
|
elen = getint(rec0, ebase+4)
|
||||||
|
enum = getint(rec0, ebase+8)
|
||||||
|
rlen = len(rec0)
|
||||||
|
return ebase, elen, enum, rlen
|
||||||
|
|
||||||
|
|
||||||
|
def add_exth(rec0, exth_num, exth_bytes):
|
||||||
|
ebase, elen, enum, rlen = get_exth_params(rec0)
|
||||||
|
newrecsize = 8+len(exth_bytes)
|
||||||
|
newrec0 = rec0[0:ebase+4]+struct.pack('>L', elen+newrecsize)+struct.pack('>L', enum+1)+struct.pack('>L', exth_num)\
|
||||||
|
+ struct.pack('>L', newrecsize)+exth_bytes+rec0[ebase+12:]
|
||||||
|
newrec0 = writeint(newrec0, title_offset, getint(newrec0, title_offset)+newrecsize)
|
||||||
|
# keep constant record length by removing newrecsize null bytes from end
|
||||||
|
sectail = newrec0[-newrecsize:]
|
||||||
|
if sectail != b'\0'*newrecsize:
|
||||||
|
raise DualMetaFixException('add_exth: trimmed non-null bytes at end of section')
|
||||||
|
newrec0 = newrec0[0:rlen]
|
||||||
|
return newrec0
|
||||||
|
|
||||||
|
|
||||||
|
def read_exth(rec0, exth_num):
|
||||||
|
exth_values = []
|
||||||
|
ebase, elen, enum, rlen = get_exth_params(rec0)
|
||||||
|
ebase += 12
|
||||||
|
while enum > 0:
|
||||||
|
exth_id = getint(rec0, ebase)
|
||||||
|
if exth_id == exth_num:
|
||||||
|
# We might have multiple exths, so build a list.
|
||||||
|
exth_values.append(rec0[ebase+8:ebase+getint(rec0, ebase+4)])
|
||||||
|
enum -= 1
|
||||||
|
ebase = ebase+getint(rec0, ebase+4)
|
||||||
|
return exth_values
|
||||||
|
|
||||||
|
|
||||||
|
def del_exth(rec0, exth_num):
|
||||||
|
ebase, elen, enum, rlen = get_exth_params(rec0)
|
||||||
|
ebase_idx = ebase+12
|
||||||
|
enum_idx = 0
|
||||||
|
while enum_idx < enum:
|
||||||
|
exth_id = getint(rec0, ebase_idx)
|
||||||
|
exth_size = getint(rec0, ebase_idx+4)
|
||||||
|
if exth_id == exth_num:
|
||||||
|
newrec0 = rec0
|
||||||
|
newrec0 = writeint(newrec0, title_offset, getint(newrec0, title_offset)-exth_size)
|
||||||
|
newrec0 = newrec0[:ebase_idx]+newrec0[ebase_idx+exth_size:]
|
||||||
|
newrec0 = newrec0[0:ebase+4]+struct.pack('>L', elen-exth_size)+struct.pack('>L', enum-1)+newrec0[ebase+12:]
|
||||||
|
newrec0 += b'\0'*exth_size
|
||||||
|
if rlen != len(newrec0):
|
||||||
|
raise DualMetaFixException('del_exth: incorrect section size change')
|
||||||
|
return newrec0
|
||||||
|
enum_idx += 1
|
||||||
|
ebase_idx = ebase_idx+exth_size
|
||||||
|
return rec0
|
||||||
|
|
||||||
|
|
||||||
|
class DualMobiMetaFix:
|
||||||
|
def __init__(self, infile, outfile, asin):
|
||||||
|
shutil.copyfile(infile, outfile)
|
||||||
|
f = open(outfile, "r+b")
|
||||||
|
self.datain = mmap.mmap(f.fileno(), 0)
|
||||||
|
self.datain_rec0 = readsection(self.datain, 0)
|
||||||
|
|
||||||
|
# in the first mobi header
|
||||||
|
# add 501 to "EBOK", add 113 as asin, add 504 as asin
|
||||||
|
rec0 = self.datain_rec0
|
||||||
|
rec0 = del_exth(rec0, 501)
|
||||||
|
rec0 = del_exth(rec0, 113)
|
||||||
|
rec0 = del_exth(rec0, 504)
|
||||||
|
rec0 = add_exth(rec0, 501, b'EBOK')
|
||||||
|
rec0 = add_exth(rec0, 113, asin)
|
||||||
|
rec0 = add_exth(rec0, 504, asin)
|
||||||
|
replacesection(self.datain, 0, rec0)
|
||||||
|
|
||||||
|
ver = getint(self.datain_rec0, mobi_version)
|
||||||
|
self.combo = (ver != 8)
|
||||||
|
if not self.combo:
|
||||||
|
return
|
||||||
|
|
||||||
|
exth121 = read_exth(self.datain_rec0, 121)
|
||||||
|
if len(exth121) == 0:
|
||||||
|
self.combo = False
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
# only pay attention to first exth121
|
||||||
|
# (there should only be one)
|
||||||
|
datain_kf8, = struct.unpack_from('>L', exth121[0], 0)
|
||||||
|
if datain_kf8 == 0xffffffff:
|
||||||
|
self.combo = False
|
||||||
|
return
|
||||||
|
self.datain_kfrec0 = readsection(self.datain, datain_kf8)
|
||||||
|
|
||||||
|
# in the second header
|
||||||
|
# add 501 to "EBOK", add 113 as asin, add 504 as asin
|
||||||
|
rec0 = self.datain_kfrec0
|
||||||
|
rec0 = del_exth(rec0, 501)
|
||||||
|
rec0 = del_exth(rec0, 113)
|
||||||
|
rec0 = del_exth(rec0, 504)
|
||||||
|
rec0 = add_exth(rec0, 501, b'EBOK')
|
||||||
|
rec0 = add_exth(rec0, 113, asin)
|
||||||
|
rec0 = add_exth(rec0, 504, asin)
|
||||||
|
replacesection(self.datain, datain_kf8, rec0)
|
||||||
|
|
||||||
|
self.datain.flush()
|
||||||
|
self.datain.close()
|
||||||
49
kcc/image.py
49
kcc/image.py
@@ -1,7 +1,7 @@
|
|||||||
# Copyright (C) 2010 Alex Yatskov
|
# Copyright (C) 2010 Alex Yatskov
|
||||||
# Copyright (C) 2011 Stanislav (proDOOMman) Kosolapov <prodoomman@gmail.com>
|
# Copyright (C) 2011 Stanislav (proDOOMman) Kosolapov <prodoomman@gmail.com>
|
||||||
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
||||||
# Copyright (c) 2013-2014 Pawel Jastrzebski <pawelj@vulturis.eu>
|
# Copyright (c) 2013-2014 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
__license__ = 'ISC'
|
__license__ = 'ISC'
|
||||||
__copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@vulturis.eu>'
|
__copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
import os
|
import os
|
||||||
@@ -144,7 +144,7 @@ class ComicPage:
|
|||||||
+ str(self.border[2]) + "-" + str(self.border[3]))
|
+ str(self.border[2]) + "-" + str(self.border[3]))
|
||||||
if forcepng:
|
if forcepng:
|
||||||
filename += ".png"
|
filename += ".png"
|
||||||
self.image.save(filename, "PNG", optimize=1)
|
self.image.save(filename, "PNG", optimize=1)
|
||||||
else:
|
else:
|
||||||
filename += ".jpg"
|
filename += ".jpg"
|
||||||
self.image.save(filename, "JPEG", optimize=1)
|
self.image.save(filename, "JPEG", optimize=1)
|
||||||
@@ -362,27 +362,28 @@ class ComicPage:
|
|||||||
self.image = self.image.crop((0, 0, widthImg, heightImg - diff))
|
self.image = self.image.crop((0, 0, widthImg, heightImg - diff))
|
||||||
return self.image
|
return self.image
|
||||||
|
|
||||||
def cropWhiteSpace(self, threshold):
|
def cropWhiteSpace(self):
|
||||||
if ImageChops.invert(self.image).getbbox() is not None:
|
if ImageChops.invert(self.image).getbbox() is not None:
|
||||||
widthImg, heightImg = self.image.size
|
widthImg, heightImg = self.image.size
|
||||||
delta = 10
|
delta = 10
|
||||||
diff = delta
|
diff = delta
|
||||||
|
fixedThreshold = 0.1
|
||||||
# top
|
# top
|
||||||
while ImageStat.Stat(self.image.crop((0, 0, widthImg, diff))).var[0] < threshold and diff < heightImg:
|
while ImageStat.Stat(self.image.crop((0, 0, widthImg, diff))).var[0] < fixedThreshold and diff < heightImg:
|
||||||
diff += delta
|
diff += delta
|
||||||
diff -= delta
|
diff -= delta
|
||||||
self.image = self.image.crop((0, diff, widthImg, heightImg))
|
self.image = self.image.crop((0, diff, widthImg, heightImg))
|
||||||
widthImg, heightImg = self.image.size
|
widthImg, heightImg = self.image.size
|
||||||
diff = delta
|
diff = delta
|
||||||
# left
|
# left
|
||||||
while ImageStat.Stat(self.image.crop((0, 0, diff, heightImg))).var[0] < threshold and diff < widthImg:
|
while ImageStat.Stat(self.image.crop((0, 0, diff, heightImg))).var[0] < fixedThreshold and diff < widthImg:
|
||||||
diff += delta
|
diff += delta
|
||||||
diff -= delta
|
diff -= delta
|
||||||
self.image = self.image.crop((diff, 0, widthImg, heightImg))
|
self.image = self.image.crop((diff, 0, widthImg, heightImg))
|
||||||
widthImg, heightImg = self.image.size
|
widthImg, heightImg = self.image.size
|
||||||
diff = delta
|
diff = delta
|
||||||
# down
|
# down
|
||||||
while ImageStat.Stat(self.image.crop((0, heightImg - diff, widthImg, heightImg))).var[0] < threshold\
|
while ImageStat.Stat(self.image.crop((0, heightImg - diff, widthImg, heightImg))).var[0] < fixedThreshold\
|
||||||
and diff < heightImg:
|
and diff < heightImg:
|
||||||
diff += delta
|
diff += delta
|
||||||
diff -= delta
|
diff -= delta
|
||||||
@@ -390,7 +391,7 @@ class ComicPage:
|
|||||||
widthImg, heightImg = self.image.size
|
widthImg, heightImg = self.image.size
|
||||||
diff = delta
|
diff = delta
|
||||||
# right
|
# right
|
||||||
while ImageStat.Stat(self.image.crop((widthImg - diff, 0, widthImg, heightImg))).var[0] < threshold\
|
while ImageStat.Stat(self.image.crop((widthImg - diff, 0, widthImg, heightImg))).var[0] < fixedThreshold\
|
||||||
and diff < widthImg:
|
and diff < widthImg:
|
||||||
diff += delta
|
diff += delta
|
||||||
diff -= delta
|
diff -= delta
|
||||||
@@ -468,3 +469,35 @@ class ComicPage:
|
|||||||
else:
|
else:
|
||||||
# Detection failed
|
# Detection failed
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
class Cover:
|
||||||
|
def __init__(self, source, target):
|
||||||
|
self.source = source
|
||||||
|
self.target = target
|
||||||
|
self.image = Image.open(source)
|
||||||
|
self.image = self.image.convert('RGB')
|
||||||
|
self.process()
|
||||||
|
self.save()
|
||||||
|
|
||||||
|
def trim(self):
|
||||||
|
bg = Image.new(self.image.mode, self.image.size, self.image.getpixel((0, 0)))
|
||||||
|
diff = ImageChops.difference(self.image, bg)
|
||||||
|
diff = ImageChops.add(diff, diff, 2.0, -100)
|
||||||
|
bbox = diff.getbbox()
|
||||||
|
if bbox:
|
||||||
|
return self.image.crop(bbox)
|
||||||
|
else:
|
||||||
|
return self.image
|
||||||
|
|
||||||
|
def process(self):
|
||||||
|
self.image = self.trim()
|
||||||
|
|
||||||
|
def save(self):
|
||||||
|
try:
|
||||||
|
if os.path.splitext(self.source)[1].lower() == '.png':
|
||||||
|
self.image.save(self.target, "PNG", optimize=1)
|
||||||
|
else:
|
||||||
|
self.image.save(self.target, "JPEG", optimize=1)
|
||||||
|
except IOError:
|
||||||
|
raise RuntimeError('Failed to save cover')
|
||||||
|
|||||||
@@ -1,382 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
# Based on initial version of KindleUnpack. Copyright (C) 2009 Charles M. Hannum <root@ihack.net>
|
|
||||||
# Improvements Copyright (C) 2009-2012 P. Durrant, K. Hendricks, S. Siebert, fandrieu, DiapDealer, nickredding
|
|
||||||
# Changes for KCC Copyright (C) 2013-2014 Pawel Jastrzebski <pawelj@vulturis.eu>
|
|
||||||
#
|
|
||||||
# This program is free software: you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU General Public License as published by
|
|
||||||
# the Free Software Foundation, either version 3 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# This program is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
import struct
|
|
||||||
# from uuid import uuid4
|
|
||||||
|
|
||||||
# important pdb header offsets
|
|
||||||
unique_id_seed = 68
|
|
||||||
number_of_pdb_records = 76
|
|
||||||
|
|
||||||
# important palmdoc header offsets
|
|
||||||
book_length = 4
|
|
||||||
book_record_count = 8
|
|
||||||
first_pdb_record = 78
|
|
||||||
|
|
||||||
# important rec0 offsets
|
|
||||||
length_of_book = 4
|
|
||||||
mobi_header_base = 16
|
|
||||||
mobi_header_length = 20
|
|
||||||
mobi_type = 24
|
|
||||||
mobi_version = 36
|
|
||||||
first_non_text = 80
|
|
||||||
title_offset = 84
|
|
||||||
first_image_record = 108
|
|
||||||
first_content_index = 192
|
|
||||||
last_content_index = 194
|
|
||||||
kf8_last_content_index = 192 # for KF8 mobi headers
|
|
||||||
fcis_index = 200
|
|
||||||
flis_index = 208
|
|
||||||
srcs_index = 224
|
|
||||||
srcs_count = 228
|
|
||||||
primary_index = 244
|
|
||||||
datp_index = 256
|
|
||||||
huffoff = 112
|
|
||||||
hufftbloff = 120
|
|
||||||
|
|
||||||
|
|
||||||
def getint(datain, ofs, sz='L'):
|
|
||||||
i, = struct.unpack_from('>'+sz, datain, ofs)
|
|
||||||
return i
|
|
||||||
|
|
||||||
|
|
||||||
def writeint(datain, ofs, n, length='L'):
|
|
||||||
if length == 'L':
|
|
||||||
return datain[:ofs]+struct.pack('>L', n)+datain[ofs+4:]
|
|
||||||
else:
|
|
||||||
return datain[:ofs]+struct.pack('>H', n)+datain[ofs+2:]
|
|
||||||
|
|
||||||
|
|
||||||
def getsecaddr(datain, secno):
|
|
||||||
nsec = getint(datain, number_of_pdb_records, 'H')
|
|
||||||
assert secno >= 0 & secno < nsec, 'secno %d out of range (nsec=%d)' % (secno, nsec)
|
|
||||||
secstart = getint(datain, first_pdb_record+secno*8)
|
|
||||||
if secno == nsec-1:
|
|
||||||
secend = len(datain)
|
|
||||||
else:
|
|
||||||
secend = getint(datain, first_pdb_record+(secno+1)*8)
|
|
||||||
return secstart, secend
|
|
||||||
|
|
||||||
|
|
||||||
def readsection(datain, secno):
|
|
||||||
secstart, secend = getsecaddr(datain, secno)
|
|
||||||
return datain[secstart:secend]
|
|
||||||
|
|
||||||
|
|
||||||
def writesection(datain, secno, secdata): # overwrite, accounting for different length
|
|
||||||
dataout = deletesectionrange(datain, secno, secno)
|
|
||||||
return insertsection(dataout, secno, secdata)
|
|
||||||
|
|
||||||
|
|
||||||
def nullsection(datain, secno): # make it zero-length without deleting it
|
|
||||||
datalst = []
|
|
||||||
nsec = getint(datain, number_of_pdb_records, 'H')
|
|
||||||
secstart, secend = getsecaddr(datain, secno)
|
|
||||||
zerosecstart, zerosecend = getsecaddr(datain, 0)
|
|
||||||
dif = secend-secstart
|
|
||||||
datalst.append(datain[:first_pdb_record])
|
|
||||||
for i in range(0, secno+1):
|
|
||||||
ofs, flgval = struct.unpack_from('>2L', datain, first_pdb_record+i*8)
|
|
||||||
datalst.append(struct.pack('>L', ofs) + struct.pack('>L', flgval))
|
|
||||||
for i in range(secno+1, nsec):
|
|
||||||
ofs, flgval = struct.unpack_from('>2L', datain, first_pdb_record+i*8)
|
|
||||||
ofs -= dif
|
|
||||||
datalst.append(struct.pack('>L', ofs) + struct.pack('>L', flgval))
|
|
||||||
lpad = zerosecstart - (first_pdb_record + 8*nsec)
|
|
||||||
if lpad > 0:
|
|
||||||
datalst.append(b'\0' * lpad)
|
|
||||||
datalst.append(datain[zerosecstart: secstart])
|
|
||||||
datalst.append(datain[secend:])
|
|
||||||
dataout = b"".join(datalst)
|
|
||||||
return dataout
|
|
||||||
|
|
||||||
|
|
||||||
def deletesectionrange(datain, firstsec, lastsec): # delete a range of sections
|
|
||||||
datalst = []
|
|
||||||
firstsecstart, firstsecend = getsecaddr(datain, firstsec)
|
|
||||||
lastsecstart, lastsecend = getsecaddr(datain, lastsec)
|
|
||||||
zerosecstart, zerosecend = getsecaddr(datain, 0)
|
|
||||||
dif = lastsecend - firstsecstart + 8*(lastsec-firstsec+1)
|
|
||||||
nsec = getint(datain, number_of_pdb_records, 'H')
|
|
||||||
datalst.append(datain[:unique_id_seed])
|
|
||||||
datalst.append(struct.pack('>L', 2*(nsec-(lastsec-firstsec+1))+1))
|
|
||||||
datalst.append(datain[unique_id_seed+4:number_of_pdb_records])
|
|
||||||
datalst.append(struct.pack('>H', nsec-(lastsec-firstsec+1)))
|
|
||||||
newstart = zerosecstart - 8*(lastsec-firstsec+1)
|
|
||||||
for i in range(0, firstsec):
|
|
||||||
ofs, flgval = struct.unpack_from('>2L', datain, first_pdb_record+i*8)
|
|
||||||
ofs -= 8 * (lastsec - firstsec + 1)
|
|
||||||
datalst.append(struct.pack('>L', ofs) + struct.pack('>L', flgval))
|
|
||||||
for i in range(lastsec+1, nsec):
|
|
||||||
ofs, flgval = struct.unpack_from('>2L', datain, first_pdb_record+i*8)
|
|
||||||
ofs -= dif
|
|
||||||
flgval = 2*(i-(lastsec-firstsec+1))
|
|
||||||
datalst.append(struct.pack('>L', ofs) + struct.pack('>L', flgval))
|
|
||||||
lpad = newstart - (first_pdb_record + 8*(nsec - (lastsec - firstsec + 1)))
|
|
||||||
if lpad > 0:
|
|
||||||
datalst.append(b'\0' * lpad)
|
|
||||||
datalst.append(datain[zerosecstart:firstsecstart])
|
|
||||||
datalst.append(datain[lastsecend:])
|
|
||||||
dataout = b"".join(datalst)
|
|
||||||
return dataout
|
|
||||||
|
|
||||||
|
|
||||||
def insertsection(datain, secno, secdata): # insert a new section
|
|
||||||
datalst = []
|
|
||||||
nsec = getint(datain, number_of_pdb_records, 'H')
|
|
||||||
secstart, secend = getsecaddr(datain, secno)
|
|
||||||
zerosecstart, zerosecend = getsecaddr(datain, 0)
|
|
||||||
dif = len(secdata)
|
|
||||||
datalst.append(datain[:unique_id_seed])
|
|
||||||
datalst.append(struct.pack('>L', 2*(nsec+1)+1))
|
|
||||||
datalst.append(datain[unique_id_seed+4:number_of_pdb_records])
|
|
||||||
datalst.append(struct.pack('>H', nsec+1))
|
|
||||||
newstart = zerosecstart + 8
|
|
||||||
for i in range(0, secno):
|
|
||||||
ofs, flgval = struct.unpack_from('>2L', datain, first_pdb_record+i*8)
|
|
||||||
ofs += 8
|
|
||||||
datalst.append(struct.pack('>L', ofs) + struct.pack('>L', flgval))
|
|
||||||
datalst.append(struct.pack('>L', secstart + 8) + struct.pack('>L', (2*secno)))
|
|
||||||
for i in range(secno, nsec):
|
|
||||||
ofs, flgval = struct.unpack_from('>2L', datain, first_pdb_record+i*8)
|
|
||||||
ofs = ofs + dif + 8
|
|
||||||
flgval = 2*(i+1)
|
|
||||||
datalst.append(struct.pack('>L', ofs) + struct.pack('>L', flgval))
|
|
||||||
lpad = newstart - (first_pdb_record + 8*(nsec + 1))
|
|
||||||
if lpad > 0:
|
|
||||||
datalst.append(b'\0' * lpad)
|
|
||||||
datalst.append(datain[zerosecstart:secstart])
|
|
||||||
datalst.append(secdata)
|
|
||||||
datalst.append(datain[secstart:])
|
|
||||||
dataout = b"".join(datalst)
|
|
||||||
return dataout
|
|
||||||
|
|
||||||
|
|
||||||
def insertsectionrange(sectionsource, firstsec, lastsec, sectiontarget, targetsec): # insert a range of sections
|
|
||||||
dataout = sectiontarget
|
|
||||||
for idx in range(lastsec, firstsec-1, -1):
|
|
||||||
dataout = insertsection(dataout, targetsec, readsection(sectionsource, idx))
|
|
||||||
return dataout
|
|
||||||
|
|
||||||
|
|
||||||
def get_exth_params(rec0):
|
|
||||||
ebase = mobi_header_base + getint(rec0, mobi_header_length)
|
|
||||||
elen = getint(rec0, ebase+4)
|
|
||||||
enum = getint(rec0, ebase+8)
|
|
||||||
return ebase, elen, enum
|
|
||||||
|
|
||||||
|
|
||||||
def add_exth(rec0, exth_num, exth_bytes):
|
|
||||||
ebase, elen, enum = get_exth_params(rec0)
|
|
||||||
newrecsize = 8+len(exth_bytes)
|
|
||||||
newrec0 = rec0[0:ebase+4]+struct.pack('>L', elen+newrecsize)+struct.pack('>L', enum+1) +\
|
|
||||||
struct.pack('>L', exth_num) + struct.pack('>L', newrecsize)+exth_bytes+rec0[ebase+12:]
|
|
||||||
newrec0 = writeint(newrec0, title_offset, getint(newrec0, title_offset)+newrecsize)
|
|
||||||
return newrec0
|
|
||||||
|
|
||||||
|
|
||||||
def read_exth(rec0, exth_num):
|
|
||||||
exth_values = []
|
|
||||||
ebase, elen, enum = get_exth_params(rec0)
|
|
||||||
ebase += 12
|
|
||||||
while enum > 0:
|
|
||||||
exth_id = getint(rec0, ebase)
|
|
||||||
if exth_id == exth_num:
|
|
||||||
# We might have multiple exths, so build a list.
|
|
||||||
exth_values.append(rec0[ebase+8:ebase+getint(rec0, ebase+4)])
|
|
||||||
enum -= 1
|
|
||||||
ebase = ebase+getint(rec0, ebase+4)
|
|
||||||
return exth_values
|
|
||||||
|
|
||||||
|
|
||||||
def write_exth(rec0, exth_num, exth_bytes):
|
|
||||||
ebase, elen, enum = get_exth_params(rec0)
|
|
||||||
ebase_idx = ebase+12
|
|
||||||
enum_idx = enum
|
|
||||||
while enum_idx > 0:
|
|
||||||
exth_id = getint(rec0, ebase_idx)
|
|
||||||
if exth_id == exth_num:
|
|
||||||
dif = len(exth_bytes)+8-getint(rec0, ebase_idx+4)
|
|
||||||
newrec0 = rec0
|
|
||||||
if dif != 0:
|
|
||||||
newrec0 = writeint(newrec0, title_offset, getint(newrec0, title_offset)+dif)
|
|
||||||
return newrec0[:ebase+4]+struct.pack('>L', elen+len(exth_bytes)+8-getint(rec0, ebase_idx+4)) +\
|
|
||||||
struct.pack('>L', enum)+rec0[ebase+12:ebase_idx+4] +\
|
|
||||||
struct.pack('>L', len(exth_bytes)+8)+exth_bytes +\
|
|
||||||
rec0[ebase_idx+getint(rec0, ebase_idx+4):]
|
|
||||||
enum_idx -= 1
|
|
||||||
ebase_idx = ebase_idx+getint(rec0, ebase_idx+4)
|
|
||||||
return rec0
|
|
||||||
|
|
||||||
|
|
||||||
def del_exth(rec0, exth_num):
|
|
||||||
ebase, elen, enum = get_exth_params(rec0)
|
|
||||||
ebase_idx = ebase+12
|
|
||||||
enum_idx = 0
|
|
||||||
while enum_idx < enum:
|
|
||||||
exth_id = getint(rec0, ebase_idx)
|
|
||||||
exth_size = getint(rec0, ebase_idx+4)
|
|
||||||
if exth_id == exth_num:
|
|
||||||
newrec0 = rec0
|
|
||||||
newrec0 = writeint(newrec0, title_offset, getint(newrec0, title_offset)-exth_size)
|
|
||||||
newrec0 = newrec0[:ebase_idx]+newrec0[ebase_idx+exth_size:]
|
|
||||||
newrec0 = newrec0[0:ebase+4]+struct.pack('>L', elen-exth_size)+struct.pack('>L', enum-1)+newrec0[ebase+12:]
|
|
||||||
return newrec0
|
|
||||||
enum_idx += 1
|
|
||||||
ebase_idx = ebase_idx+exth_size
|
|
||||||
return rec0
|
|
||||||
|
|
||||||
|
|
||||||
class mobi_split:
|
|
||||||
def __init__(self, infile, newKindle):
|
|
||||||
try:
|
|
||||||
datain = open(infile, 'rb').read()
|
|
||||||
datain_rec0 = readsection(datain, 0)
|
|
||||||
ver = getint(datain_rec0, mobi_version)
|
|
||||||
# fake_asin = str(uuid4())
|
|
||||||
self.combo = (ver != 8)
|
|
||||||
if not self.combo:
|
|
||||||
return
|
|
||||||
exth121 = read_exth(datain_rec0, 121)
|
|
||||||
if len(exth121) == 0:
|
|
||||||
self.combo = False
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
# only pay attention to first exth121
|
|
||||||
# (there should only be one)
|
|
||||||
datain_kf8, = struct.unpack_from('>L', exth121[0], 0)
|
|
||||||
if datain_kf8 == 0xffffffff:
|
|
||||||
self.combo = False
|
|
||||||
return
|
|
||||||
datain_kfrec0 = readsection(datain, datain_kf8)
|
|
||||||
firstimage = getint(datain_rec0, first_image_record)
|
|
||||||
lastimage = getint(datain_rec0, last_content_index, 'H')
|
|
||||||
|
|
||||||
if not newKindle:
|
|
||||||
# create the standalone mobi7
|
|
||||||
num_sec = getint(datain, number_of_pdb_records, 'H')
|
|
||||||
# remove BOUNDARY up to but not including ELF record
|
|
||||||
self.result_file = deletesectionrange(datain, datain_kf8-1, num_sec-2)
|
|
||||||
# check if there are SRCS records and delete them
|
|
||||||
srcs = getint(datain_rec0, srcs_index)
|
|
||||||
num_srcs = getint(datain_rec0, srcs_count)
|
|
||||||
if srcs != 0xffffffff and num_srcs > 0:
|
|
||||||
self.result_file = deletesectionrange(self.result_file, srcs, srcs+num_srcs-1)
|
|
||||||
datain_rec0 = writeint(datain_rec0, srcs_index, 0xffffffff)
|
|
||||||
datain_rec0 = writeint(datain_rec0, srcs_count, 0)
|
|
||||||
# reset the EXTH 121 KF8 Boundary meta data to 0xffffffff
|
|
||||||
datain_rec0 = write_exth(datain_rec0, 121, struct.pack('>L', 0xffffffff))
|
|
||||||
# datain_rec0 = del_exth(datain_rec0,121)
|
|
||||||
# datain_rec0 = del_exth(datain_rec0,534)
|
|
||||||
# don't remove the EXTH 125 KF8 Count of Resources, seems to be present in mobi6 files as well
|
|
||||||
# set the EXTH 129 KF8 Masthead / Cover Image string to the null string
|
|
||||||
datain_rec0 = write_exth(datain_rec0, 129, b'')
|
|
||||||
# don't remove the EXTH 131 KF8 Unidentified Count, seems to be present in mobi6 files as well
|
|
||||||
|
|
||||||
# Make sure we have an ASIN & cdeType set...
|
|
||||||
# if len(read_exth(datain_rec0, 113)) == 0:
|
|
||||||
# datain_rec0 = add_exth(datain_rec0, 113, fake_asin)
|
|
||||||
# if len(read_exth(datain_rec0, 504)) == 0:
|
|
||||||
# datain_rec0 = add_exth(datain_rec0, 504, fake_asin)
|
|
||||||
if len(read_exth(datain_rec0, 501)) == 0:
|
|
||||||
datain_rec0 = add_exth(datain_rec0, 501, b'EBOK')
|
|
||||||
|
|
||||||
# need to reset flags stored in 0x80-0x83
|
|
||||||
# old mobi with exth: 0x50, mobi7 part with exth: 0x1850, mobi8 part with exth: 0x1050
|
|
||||||
# Bit Flags
|
|
||||||
# 0x1000 = Bit 12 indicates if embedded fonts are used or not
|
|
||||||
# 0x0800 = means this Header points to *shared* images/resource/fonts ??
|
|
||||||
# 0x0080 = unknown new flag, why is this now being set by Kindlegen 2.8?
|
|
||||||
# 0x0040 = exth exists
|
|
||||||
# 0x0010 = Not sure but this is always set so far
|
|
||||||
fval, = struct.unpack_from('>L', datain_rec0, 0x80)
|
|
||||||
# need to remove flag 0x0800 for KindlePreviewer 2.8 and unset Bit 12 for embedded fonts
|
|
||||||
fval &= 0x07FF
|
|
||||||
datain_rec0 = datain_rec0[:0x80] + struct.pack('>L', fval) + datain_rec0[0x84:]
|
|
||||||
self.result_file = writesection(self.result_file, 0, datain_rec0)
|
|
||||||
if lastimage == 0xffff:
|
|
||||||
# find the lowest of the next sections and copy up to that.
|
|
||||||
ofs_list = [(kf8_last_content_index, 'L'), (fcis_index, 'L'), (flis_index, 'L'), (datp_index, 'L'),
|
|
||||||
(hufftbloff, 'L')]
|
|
||||||
for ofs, sz in ofs_list:
|
|
||||||
n = getint(datain_kfrec0, ofs, sz)
|
|
||||||
if 0 < n < lastimage:
|
|
||||||
lastimage = n-1
|
|
||||||
|
|
||||||
# Try to null out FONT and RES, but leave the (empty) PDB record so image refs remain valid
|
|
||||||
for i in range(firstimage, lastimage):
|
|
||||||
imgsec = readsection(self.result_file, i)
|
|
||||||
if imgsec[0:4] in ['RESC', 'FONT']:
|
|
||||||
self.result_file = nullsection(self.result_file, i)
|
|
||||||
# mobi7 finished
|
|
||||||
else:
|
|
||||||
# create standalone mobi8
|
|
||||||
self.result_file = deletesectionrange(datain, 0, datain_kf8-1)
|
|
||||||
target = getint(datain_kfrec0, first_image_record)
|
|
||||||
self.result_file = insertsectionrange(datain, firstimage, lastimage, self.result_file, target)
|
|
||||||
datain_kfrec0 = readsection(self.result_file, 0)
|
|
||||||
|
|
||||||
# Only keep the correct EXTH 116 StartOffset, KG 2.5 carries over the one from the mobi7 part,
|
|
||||||
# which then points at garbage in the mobi8 part, and confuses FW 3.4
|
|
||||||
kf8starts = read_exth(datain_kfrec0, 116)
|
|
||||||
# If we have multiple StartOffset, keep only the last one
|
|
||||||
kf8start_count = len(kf8starts)
|
|
||||||
while kf8start_count > 1:
|
|
||||||
kf8start_count -= 1
|
|
||||||
datain_kfrec0 = del_exth(datain_kfrec0, 116)
|
|
||||||
|
|
||||||
# update the EXTH 125 KF8 Count of Images/Fonts/Resources
|
|
||||||
datain_kfrec0 = write_exth(datain_kfrec0, 125, struct.pack('>L', lastimage-firstimage+1))
|
|
||||||
|
|
||||||
# Same dance for the KF8, we want an ASIN & cdeType :)
|
|
||||||
# if len(read_exth(datain_kfrec0, 113)) == 0:
|
|
||||||
# datain_kfrec0 = add_exth(datain_kfrec0, 113, fake_asin)
|
|
||||||
# if len(read_exth(datain_kfrec0, 504)) == 0:
|
|
||||||
# datain_kfrec0 = add_exth(datain_kfrec0, 504, fake_asin)
|
|
||||||
if len(read_exth(datain_kfrec0, 501)) == 0:
|
|
||||||
datain_kfrec0 = add_exth(datain_kfrec0, 501, b'EBOK')
|
|
||||||
|
|
||||||
# need to reset flags stored in 0x80-0x83
|
|
||||||
# old mobi with exth: 0x50, mobi7 part with exth: 0x1850, mobi8 part with exth: 0x1050
|
|
||||||
# standalone mobi8 with exth: 0x0050
|
|
||||||
# Bit Flags
|
|
||||||
# 0x1000 = Bit 12 indicates if embedded fonts are used or not
|
|
||||||
# 0x0800 = means this Header points to *shared* images/resource/fonts ??
|
|
||||||
# 0x0080 = unknown new flag, why is this now being set by Kindlegen 2.8?
|
|
||||||
# 0x0040 = exth exists
|
|
||||||
# 0x0010 = Not sure but this is always set so far
|
|
||||||
fval, = struct.unpack_from('>L', datain_kfrec0, 0x80)
|
|
||||||
fval &= 0x1FFF
|
|
||||||
fval |= 0x0800
|
|
||||||
datain_kfrec0 = datain_kfrec0[:0x80] + struct.pack('>L', fval) + datain_kfrec0[0x84:]
|
|
||||||
|
|
||||||
# properly update other index pointers that have been shifted by the insertion of images
|
|
||||||
ofs_list = [(kf8_last_content_index, 'L'), (fcis_index, 'L'), (flis_index, 'L'), (datp_index, 'L'),
|
|
||||||
(hufftbloff, 'L')]
|
|
||||||
for ofs, sz in ofs_list:
|
|
||||||
n = getint(datain_kfrec0, ofs, sz)
|
|
||||||
if n != 0xffffffff:
|
|
||||||
datain_kfrec0 = writeint(datain_kfrec0, ofs, n+lastimage-firstimage+1, sz)
|
|
||||||
self.result_file = writesection(self.result_file, 0, datain_kfrec0)
|
|
||||||
# mobi8 finished
|
|
||||||
except Exception:
|
|
||||||
raise
|
|
||||||
|
|
||||||
def getResult(self):
|
|
||||||
return self.result_file
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
||||||
# Copyright (c) 2013-2014 Pawel Jastrzebski <pawelj@vulturis.eu>
|
# Copyright (c) 2013-2014 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||||
#
|
#
|
||||||
# Based upon the code snippet by Ned Batchelder
|
# Based upon the code snippet by Ned Batchelder
|
||||||
# (http://nedbatchelder.com/blog/200712/extracting_jpgs_from_pdfs.html)
|
# (http://nedbatchelder.com/blog/200712/extracting_jpgs_from_pdfs.html)
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
__license__ = 'ISC'
|
__license__ = 'ISC'
|
||||||
__copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@vulturis.eu>'
|
__copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
|
||||||
# Copyright (c) 2013-2014 Pawel Jastrzebski <pawelj@vulturis.eu>
|
# Copyright (c) 2013-2014 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and/or distribute this software for
|
# Permission to use, copy, modify, and/or distribute this software for
|
||||||
# any purpose with or without fee is hereby granted, provided that the
|
# any purpose with or without fee is hereby granted, provided that the
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
__license__ = 'ISC'
|
__license__ = 'ISC'
|
||||||
__copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@vulturis.eu>'
|
__copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
82
setup.py
82
setup.py
@@ -1,12 +1,12 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""
|
"""
|
||||||
cx_Freeze build script for KCC.
|
cx_Freeze/py2app build script for KCC.
|
||||||
|
|
||||||
Usage (Mac OS X):
|
Usage (Mac OS X):
|
||||||
python setup.py py2app
|
python setup.py py2app
|
||||||
|
|
||||||
Usage (Windows):
|
Usage (Windows):
|
||||||
python setup.py build
|
python setup.py py2exe
|
||||||
"""
|
"""
|
||||||
from sys import platform, version_info
|
from sys import platform, version_info
|
||||||
if version_info[0] != 3:
|
if version_info[0] != 3:
|
||||||
@@ -14,7 +14,7 @@ if version_info[0] != 3:
|
|||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
NAME = "KindleComicConverter"
|
NAME = "KindleComicConverter"
|
||||||
VERSION = "4.0.2"
|
VERSION = "4.1"
|
||||||
MAIN = "kcc.py"
|
MAIN = "kcc.py"
|
||||||
|
|
||||||
if platform == "darwin":
|
if platform == "darwin":
|
||||||
@@ -55,31 +55,61 @@ if platform == "darwin":
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
elif platform == "win32":
|
elif platform == "win32":
|
||||||
|
# noinspection PyUnresolvedReferences
|
||||||
|
import py2exe
|
||||||
import platform as arch
|
import platform as arch
|
||||||
from cx_Freeze import setup, Executable
|
from distutils.core import setup
|
||||||
if arch.architecture()[0] == '64bit':
|
if arch.architecture()[0] == '64bit':
|
||||||
library = 'libEGL64.dll'
|
suffix = '_64'
|
||||||
else:
|
else:
|
||||||
library = 'libEGL32.dll'
|
suffix = ''
|
||||||
base = "Win32GUI"
|
additional_files = [('imageformats', ['C:\Python34' + suffix +
|
||||||
|
'\Lib\site-packages\PyQt5\plugins\imageformats\qgif.dll',
|
||||||
|
'C:\Python34' + suffix +
|
||||||
|
'\Lib\site-packages\PyQt5\plugins\imageformats\qico.dll',
|
||||||
|
'C:\Python34' + suffix +
|
||||||
|
'\Lib\site-packages\PyQt5\plugins\imageformats\qjpeg.dll',
|
||||||
|
'C:\Python34' + suffix +
|
||||||
|
'\Lib\site-packages\PyQt5\plugins\imageformats\qmng.dll',
|
||||||
|
'C:\Python34' + suffix +
|
||||||
|
'\Lib\site-packages\PyQt5\plugins\imageformats\qsvg.dll',
|
||||||
|
'C:\Python34' + suffix +
|
||||||
|
'\Lib\site-packages\PyQt5\plugins\imageformats\qtga.dll',
|
||||||
|
'C:\Python34' + suffix +
|
||||||
|
'\Lib\site-packages\PyQt5\plugins\imageformats\qtiff.dll',
|
||||||
|
'C:\Python34' + suffix +
|
||||||
|
'\Lib\site-packages\PyQt5\plugins\imageformats\qwbmp.dll']),
|
||||||
|
('platforms', ['C:\Python34' + suffix +
|
||||||
|
'\Lib\site-packages\PyQt5\plugins\platforms\qminimal.dll',
|
||||||
|
'C:\Python34' + suffix +
|
||||||
|
'\Lib\site-packages\PyQt5\plugins\platforms\qoffscreen.dll',
|
||||||
|
'C:\Python34' + suffix +
|
||||||
|
'\Lib\site-packages\PyQt5\plugins\platforms\qwindows.dll']),
|
||||||
|
('', ['LICENSE.txt',
|
||||||
|
'other\\7za.exe',
|
||||||
|
'other\\UnRAR.exe',
|
||||||
|
'other\\Additional-LICENSE.txt',
|
||||||
|
'other\\7za.exe',
|
||||||
|
'C:\Python34' + suffix + '\Lib\site-packages\PyQt5\libEGL.dll'])]
|
||||||
extra_options = dict(
|
extra_options = dict(
|
||||||
options={"build_exe": {"optimize": 2,
|
options={'py2exe': {"bundle_files": 2,
|
||||||
"include_files": ['LICENSE.txt',
|
"dll_excludes": ["tcl85.dll", "tk85.dll"],
|
||||||
['other/UnRAR.exe', 'UnRAR.exe'],
|
"dist_dir": "dist" + suffix,
|
||||||
['other/7za.exe', '7za.exe'],
|
"compressed": True,
|
||||||
['other/Additional-LICENSE.txt', 'Additional-LICENSE.txt'],
|
"includes": ["sip"],
|
||||||
['other/' + library, 'libEGL.dll']
|
"excludes": ["tkinter"],
|
||||||
],
|
"optimize": 2}},
|
||||||
"copy_dependent_files": True,
|
windows=[{"script": "kcc.py",
|
||||||
"create_shared_zip": False,
|
"dest_base": "KCC",
|
||||||
"append_script_to_exe": True,
|
"version": VERSION,
|
||||||
"replace_paths": '*=',
|
"copyright": "Ciro Mattia Gonano, Pawel Jastrzebski © 2014",
|
||||||
"excludes": ['tkinter']}},
|
"legal_copyright": "ISC License (ISCL)",
|
||||||
executables=[Executable(MAIN,
|
"product_version": VERSION,
|
||||||
base=base,
|
"product_name": "Kindle Comic Converter",
|
||||||
targetName="KCC.exe",
|
"file_description": "Kindle Comic Converter",
|
||||||
icon="icons/comic2ebook.ico",
|
"icon_resources": [(1, "icons\comic2ebook.ico")]}],
|
||||||
compress=False)])
|
zipfile=None,
|
||||||
|
data_files=additional_files)
|
||||||
else:
|
else:
|
||||||
print('Please use setup.sh to build Linux package.')
|
print('Please use setup.sh to build Linux package.')
|
||||||
exit()
|
exit()
|
||||||
@@ -89,8 +119,8 @@ setup(
|
|||||||
name=NAME,
|
name=NAME,
|
||||||
version=VERSION,
|
version=VERSION,
|
||||||
author="Ciro Mattia Gonano, Pawel Jastrzebski",
|
author="Ciro Mattia Gonano, Pawel Jastrzebski",
|
||||||
author_email="ciromattia@gmail.com, pawelj@vulturis.eu",
|
author_email="ciromattia@gmail.com, pawelj@iosphe.re",
|
||||||
description="A tool to convert comics (CBR/CBZ/PDFs/image folders) to MOBI.",
|
description="Kindle Comic Converter",
|
||||||
license="ISC License (ISCL)",
|
license="ISC License (ISCL)",
|
||||||
keywords="kindle comic mobipocket mobi cbz cbr manga",
|
keywords="kindle comic mobipocket mobi cbz cbr manga",
|
||||||
url="http://github.com/ciromattia/kcc",
|
url="http://github.com/ciromattia/kcc",
|
||||||
|
|||||||
Reference in New Issue
Block a user