mirror of
https://github.com/ciromattia/kcc
synced 2026-04-18 06:58:58 +00:00
Compare commits
114 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
148211a5c7 | ||
|
|
23e07f47f0 | ||
|
|
724156c554 | ||
|
|
b972e4c746 | ||
|
|
f0afa1fff2 | ||
|
|
a36c05f0c5 | ||
|
|
4f3a66b4eb | ||
|
|
6369c7ea44 | ||
|
|
f1b8aff8d4 | ||
|
|
be270aa797 | ||
|
|
f33d355024 | ||
|
|
6f913b026e | ||
|
|
220b4e0954 | ||
|
|
bac4a4fd86 | ||
|
|
d923299230 | ||
|
|
a79ebcec86 | ||
|
|
69bbee7648 | ||
|
|
57d7729d05 | ||
|
|
25a1dcb72c | ||
|
|
cff9b73b80 | ||
|
|
23336de5b5 | ||
|
|
d346ca0466 | ||
|
|
22fadf078e | ||
|
|
74acf85683 | ||
|
|
713de03d1d | ||
|
|
5817f52110 | ||
|
|
dfc03aee38 | ||
|
|
6bc8038068 | ||
|
|
eb7d56c1b9 | ||
|
|
f5b515ef79 | ||
|
|
943431346d | ||
|
|
a597173b71 | ||
|
|
c484cc8fff | ||
|
|
f195a4ccbf | ||
|
|
f55bb6dce6 | ||
|
|
e7f49f8330 | ||
|
|
cec9356fc2 | ||
|
|
6d704b382d | ||
|
|
1c5f67111f | ||
|
|
660df4b370 | ||
|
|
19dc5cc6f2 | ||
|
|
67baf90d04 | ||
|
|
07bf41ea6c | ||
|
|
c2cfc9b8db | ||
|
|
7a7a2bc10b | ||
|
|
e3127ed516 | ||
|
|
93a98db43e | ||
|
|
750b55649a | ||
|
|
ca5854a8bd | ||
|
|
712f728a5e | ||
|
|
eb7766a5e5 | ||
|
|
0cabbfde96 | ||
|
|
c5983276e5 | ||
|
|
bb0856c841 | ||
|
|
c21e8e398e | ||
|
|
bba0223efc | ||
|
|
9709bc9c12 | ||
|
|
0d61442c18 | ||
|
|
89fa1393db | ||
|
|
15e86b28ab | ||
|
|
a72d1128dd | ||
|
|
0fd3f6bc0f | ||
|
|
d76ce6c92f | ||
|
|
b6fa993c17 | ||
|
|
81509012b4 | ||
|
|
6db1ab0792 | ||
|
|
513ea0a7cd | ||
|
|
640b5117f9 | ||
|
|
c9d558a353 | ||
|
|
378aff4f35 | ||
|
|
6142c87fa3 | ||
|
|
b01e8e2bc2 | ||
|
|
10724489fc | ||
|
|
3365f111e4 | ||
|
|
b1d22cd05b | ||
|
|
74f2250952 | ||
|
|
36516fd594 | ||
|
|
2dab7a707b | ||
|
|
fc8b26e292 | ||
|
|
5c0964c8fa | ||
|
|
6836c6ee17 | ||
|
|
6adc2dff93 | ||
|
|
63deec88db | ||
|
|
c37e281d69 | ||
|
|
4a0497addc | ||
|
|
e2f5c549aa | ||
|
|
a8195d44ee | ||
|
|
18a505637d | ||
|
|
62475e12c6 | ||
|
|
46888e10d8 | ||
|
|
eb406aada0 | ||
|
|
1582d03fab | ||
|
|
4cfac52d6a | ||
|
|
04ea816365 | ||
|
|
6a298175f0 | ||
|
|
690f0298e6 | ||
|
|
fc93697c28 | ||
|
|
32391f6a5a | ||
|
|
f163eac853 | ||
|
|
ce824f4cab | ||
|
|
df41ad405e | ||
|
|
685cdc929b | ||
|
|
5e65c5149c | ||
|
|
718af10be7 | ||
|
|
b162425e52 | ||
|
|
f1b63420f6 | ||
|
|
95e7329abf | ||
|
|
5d3b7e83f5 | ||
|
|
751e6eb4e7 | ||
|
|
074e31cb2e | ||
|
|
e7e87d03cd | ||
|
|
38669d08ed | ||
|
|
825eafad7f | ||
|
|
f805984c1c |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -3,3 +3,5 @@
|
|||||||
*.cbr
|
*.cbr
|
||||||
.idea
|
.idea
|
||||||
build
|
build
|
||||||
|
awkcc
|
||||||
|
.DS_Store
|
||||||
|
|||||||
123
README.md
123
README.md
@@ -1,38 +1,38 @@
|
|||||||
# KCC
|
# KCC
|
||||||
|
|
||||||
`KCC` (a.k.a. `KindleComicConverter`) is a Python app to convert comic files or folders to ePub or Panel View Mobipocket.
|
`KCC` (a.k.a. `KindleComicConverter`) is a Python app to convert comic files or folders to ePub or Panel View MOBI.
|
||||||
It was initally developed for Kindle but as of version 2.2 it outputs valid ePub 2.0 so _**despite its name, KCC is
|
It was initally developed for Kindle but since v2.2 it outputs valid ePub 2.0 so _**despite its name, KCC is
|
||||||
actually a comic 2 epub converter that every ereader owner can happily use**_.
|
actually a comic 2 epub converter that every ereader owner can happily use**_.
|
||||||
It also optimizes comic images by:
|
|
||||||
- enhancing contrast
|
It can also optionally optimize images by applying a number of transformations.
|
||||||
- cutting page numbering
|
|
||||||
- cropping white borders
|
### A word of warning
|
||||||
- resizing larger images to device's native resolution
|
**KCC** _is not_ [Amazon's Kindle Comic Creator](http://www.amazon.com/gp/feature.html?ie=UTF8&docId=1001103761) nor is in any way endorsed by Amazon.
|
||||||
- quantizing images to device's palette
|
Amazon's tool is for comic _publishers_ and involves a lot of manual effort, while **KCC** is for comic _readers_.
|
||||||
|
If you want to read some comments over *Amazon's kc2* you can take a look at [this](http://www.mobileread.com/forums/showthread.php?t=207461&page=7#96) and [that](http://www.mobileread.com/forums/showthread.php?t=211047) threads on Mobileread.
|
||||||
|
_kc2_ in no way is a replacement for **KCC** so you can be quite confident we'll going to carry on developing our little monster ;)
|
||||||
|
|
||||||
## 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:
|
||||||
- OS X: [https://dl.dropbox.com/u/16806101/KindleComicConverter_osx_2.6.zip](https://dl.dropbox.com/u/16806101/KindleComicConverter_osx_2.6.zip)
|
- OS X: [https://dl.dropbox.com/u/16806101/KindleComicConverter_osx_2.9.zip](https://dl.dropbox.com/u/16806101/KindleComicConverter_osx_2.9.zip)
|
||||||
- Win64: [https://dl.dropbox.com/u/16806101/KindleComicConverter_win-amd64_2.6.zip](https://dl.dropbox.com/u/16806101/KindleComicConverter_win-amd64_2.6.zip)
|
- Win64: [https://dl.dropbox.com/u/16806101/KindleComicConverter_win-amd64_2.9.zip](https://dl.dropbox.com/u/16806101/KindleComicConverter_win-amd64_2.9.zip)
|
||||||
- Win32: [http://pawelj.vulturis.eu/Shared/KindleComicConverter_win-x86_2.6.zip](http://pawelj.vulturis.eu/Shared/KindleComicConverter_win-x86_2.6.zip) *(thanks to [AcidWeb](https://github.com/AcidWeb))*
|
- Win32: [http://pawelj.vulturis.eu/Shared/KindleComicConverter_win-x86_2.9.zip](http://pawelj.vulturis.eu/Shared/KindleComicConverter_win-x86_2.9.zip) *(thanks to [AcidWeb](https://github.com/AcidWeb))*
|
||||||
- Linux: just download sourcecode and launch `python kcc.py` *(provided you have Python and Pillow installed)*
|
- Linux: Just download sourcecode and launch `python kcc.py` *(Provided you have Python and Pillow installed)*
|
||||||
|
|
||||||
## INPUT FORMATS
|
## INPUT FORMATS
|
||||||
`kcc` can understand and convert, at the moment, the following file types:
|
`kcc` can understand and convert, at the moment, the following file types:
|
||||||
|
- PNG, JPG, GIF, TIFF, BMP
|
||||||
|
- Folders
|
||||||
- CBZ, ZIP
|
- CBZ, ZIP
|
||||||
- CBR, RAR *(with `unrar` executable)*
|
- CBR, RAR *(With `unrar` executable)*
|
||||||
- folders
|
- PDF *(Extracting only contained JPG images)*
|
||||||
- PDF *(extracting only contained JPG images)*
|
|
||||||
|
|
||||||
~~For now the script does not understand folder depth, so it will work on flat folders/archives only.~~
|
|
||||||
As of v. 1.50, KCC supports subfolders!
|
|
||||||
|
|
||||||
## OPTIONAL REQUIREMENTS
|
## OPTIONAL REQUIREMENTS
|
||||||
- `kindlegen` in /usr/local/bin/ *(for .mobi generation)*
|
- `kindlegen` v2.7+ in a directory reachable by your PATH or in KCC directory *(For .mobi generation)*
|
||||||
- [unrar](http://www.rarlab.com/download.htm) *(for CBR support)*
|
- [unrar](http://www.rarlab.com/download.htm) *(For CBR support)*
|
||||||
|
|
||||||
### for compiling/running from source:
|
### For compiling/running from source:
|
||||||
- Python 2.7+ (included in MacOS and Linux, follow the [official documentation](http://www.python.org/getit/windows/) to install on Windows)
|
- Python 2.7+ (Included in MacOS and Linux, follow the [official documentation](http://www.python.org/getit/windows/) to install on Windows)
|
||||||
- [Pillow](http://pypi.python.org/pypi/Pillow/) for comic optimizations like split double pages, resize to optimal resolution, improve contrast and palette, etc.
|
- [Pillow](http://pypi.python.org/pypi/Pillow/) for comic optimizations like split double pages, resize to optimal resolution, improve contrast and palette, etc.
|
||||||
Please refer to official documentation for installing into your system.
|
Please refer to official documentation for installing into your system.
|
||||||
|
|
||||||
@@ -47,41 +47,32 @@ Conversion being done, you should find an .epub and a .mobi files alongside the
|
|||||||
### Standalone `comic2ebook.py` usage:
|
### Standalone `comic2ebook.py` usage:
|
||||||
|
|
||||||
```
|
```
|
||||||
comic2ebook.py [options] comic_file|comic_folder
|
Usage: comic2ebook.py [options] comic_file|comic_folder
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
--version show program's version number and exit
|
--version show program's version number and exit
|
||||||
-h, --help show this help message and exit
|
-h, --help show this help message and exit
|
||||||
-p PROFILE, --profile=PROFILE
|
-p PROFILE, --profile=PROFILE
|
||||||
Device profile (choose one among K1, K2, K3, K4, KDX,
|
Device profile (Choose one among K1, K2, K3, K4NT, K4T, KDX, KDXG or KHD) [Default=KHD]
|
||||||
KDXG or KHD) [default=KHD]
|
|
||||||
-t TITLE, --title=TITLE
|
-t TITLE, --title=TITLE
|
||||||
Comic title [default=filename]
|
Comic title [Default=filename]
|
||||||
-m, --manga-style 'Manga style' (right-to-left reading and splitting)
|
-m, --manga-style Manga style (Right-to-left reading and splitting) [Default=False]
|
||||||
[default=False]
|
-c, --cbz-output Outputs a CBZ archive and does not generate EPUB
|
||||||
-v, --verbose Verbose output [default=False]
|
--nopanelviewhq Disable high quality Panel View [Default=False]
|
||||||
--no-image-processing
|
--noprocessing Do not apply image preprocessing (Page splitting and optimizations) [Default=True]
|
||||||
Do not apply image preprocessing (page splitting and
|
--forcepng Create PNG files instead JPEG (For non-Kindle devices) [Default=False]
|
||||||
optimizations) [default=True]
|
--gamma=GAMMA Apply gamma correction to linearize the image [Default=Auto]
|
||||||
--upscale-images Resize images smaller than device's resolution
|
--upscale Resize images smaller than device's resolution [Default=False]
|
||||||
[default=False]
|
--stretch Stretch images to device's resolution [Default=False]
|
||||||
--stretch-images Stretch images to device's resolution [default=False]
|
--blackborders Use black borders instead of white ones when not stretching and ratio is not like the device's one [Default=False]
|
||||||
--black-borders Use black borders (instead of white ones) when not
|
--rotate Rotate landscape pages instead of splitting them [Default=False]
|
||||||
stretching and ratio is not like the device's one
|
--nosplitrotate Disable splitting and rotation [Default=False]
|
||||||
[default=False]
|
--nocutpagenumbers Do not try to cut page numbering on images [Default=True]
|
||||||
--no-cut-page-numbers
|
|
||||||
Do not try to cut page numbering on images
|
|
||||||
[default=True]
|
|
||||||
--rotate Disable page spliting. Instead rotate images
|
|
||||||
[default=False]
|
|
||||||
-o OUTPUT, --output=OUTPUT
|
-o OUTPUT, --output=OUTPUT
|
||||||
Output directory or file for generated ePub
|
Output generated file (EPUB or CBZ) to specified directory or file
|
||||||
|
-v, --verbose Verbose output [Default=False]
|
||||||
```
|
```
|
||||||
|
|
||||||
The script takes care of creating an *.epub* from your archive/folder, then:
|
|
||||||
1. Run `Kindlegen` on the generated *.epub*. Depending on how many images you have, this may take awhile. Once completed, the `.mobi` file should be in the directory.
|
|
||||||
2. (optionally) remove the SRCS record to reduce the `.mobi` filesize in half. You can use [Kindlestrip](http://www.mobileread.com/forums/showthread.php?t=96903).
|
|
||||||
3. Copy the `.mobi` file to your Kindle!
|
|
||||||
|
|
||||||
## 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)
|
||||||
|
|
||||||
@@ -94,10 +85,7 @@ The app relies and includes the following scripts/binaries:
|
|||||||
- `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
|
||||||
- the icon is by **Nikolay Verin** ([http://ncrow.deviantart.com/](http://ncrow.deviantart.com/)) and released under [CC Attribution-NonCommercial-ShareAlike 3.0 Unported](http://creativecommons.org/licenses/by-nc-sa/3.0/) License
|
- the icon is by **Nikolay Verin** ([http://ncrow.deviantart.com/](http://ncrow.deviantart.com/)) and released under [CC Attribution-NonCommercial-ShareAlike 3.0 Unported](http://creativecommons.org/licenses/by-nc-sa/3.0/) 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](http://foosoft.net/mangle/) with subsequent [proDOOMman](https://github.com/proDOOMman/Mangle)'s and [Birua](https://github.com/Birua/Mangle)'s patches
|
||||||
|
- `magic.py` from [python-magic](https://github.com/ahupp/python-magic) library
|
||||||
Also, for .mobi generation you need to have `kindlegen` v2.7+ (with KF8 support) which is downloadable from Amazon website
|
|
||||||
and installed in a directory reachable by your PATH (e.g. `/usr/local/bin/` or `C:\Windows\`)
|
|
||||||
|
|
||||||
|
|
||||||
## CHANGELOG
|
## CHANGELOG
|
||||||
- 1.00: Initial version
|
- 1.00: Initial version
|
||||||
@@ -120,10 +108,33 @@ and installed in a directory reachable by your PATH (e.g. `/usr/local/bin/` or `
|
|||||||
- 2.4: Use temporary directory as workdir (fixes converting from external volumes and zipfiles renaming)
|
- 2.4: Use temporary directory as workdir (fixes converting from external volumes and zipfiles renaming)
|
||||||
Fixed "add folders" from GUI.
|
Fixed "add folders" from GUI.
|
||||||
- 2.5: Added --black-borders option to set added borders black when page's ratio is not the device's one (#11).
|
- 2.5: Added --black-borders option to set added borders black when page's ratio is not the device's one (#11).
|
||||||
Fixes epub containing zipped itself (#10).
|
Fixes epub containing zipped itself (#10)
|
||||||
- 2.6: Added --rotate option to rotate landscape images instead of splitting them.
|
- 2.6: Added --rotate option to rotate landscape images instead of splitting them (#16, #24)
|
||||||
Added --output option to customize ePub output dir/file.
|
Added --output option to customize ePub output dir/file (#22)
|
||||||
Add rendition:layout and rendition:orientation ePub meta tags (supported by new kindlegen 2.8)
|
Add rendition:layout and rendition:orientation ePub meta tags (supported by new kindlegen 2.8)
|
||||||
|
Fixed natural sorting for files (#18)
|
||||||
|
- 2.7: Lots of GUI improvements (#27, #13)
|
||||||
|
Added gamma support within --gamma option (defaults to profile-specified gamma) (#26, #27)
|
||||||
|
Added --nodithering option to prevent dithering optimizations (#27)
|
||||||
|
Epub margins support (#30)
|
||||||
|
Fixed no file added if file has no spaces on Windows (#25)
|
||||||
|
Gracefully exit if unrar missing (#15)
|
||||||
|
Do not call kindlegen if source epub is bigger than 320MB (#17)
|
||||||
|
Get filetype from magic number (#14)
|
||||||
|
PDF conversion works again
|
||||||
|
- 2.8: updated rarfile library
|
||||||
|
Panel View support + HQ support (#36) - new option: --nopanelviewhq
|
||||||
|
Split profiles for K4NT and K4T
|
||||||
|
Rewrite of Landscape Mode support (huge readability improvement for KPW)
|
||||||
|
Upscale use now BILINEAR method
|
||||||
|
Added generic CSS file
|
||||||
|
Optimized archive extraction for zip/rar files (#40)
|
||||||
|
- 2.9: Added support for generating a plain CBZ (skipping all the EPUB/Mobi generation) (#45)
|
||||||
|
Prevent output file overwriting the source one: if a duplicate name is detected, append _kcc to the name
|
||||||
|
Rarfile library updated to 2.6
|
||||||
|
Added GIF, TIFF and BMP to supported formats (#42)
|
||||||
|
Filenames slugifications (#28, #31, #9, #8)
|
||||||
|
|
||||||
|
|
||||||
## COPYRIGHT
|
## COPYRIGHT
|
||||||
|
|
||||||
|
|||||||
BIN
comic2ebook.ico
Normal file
BIN
comic2ebook.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 345 KiB |
13
kcc.py
13
kcc.py
@@ -16,7 +16,7 @@
|
|||||||
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
# PERFORMANCE OF THIS SOFTWARE.
|
# PERFORMANCE OF THIS SOFTWARE.
|
||||||
#
|
#
|
||||||
__version__ = '2.6'
|
__version__ = '2.9'
|
||||||
__license__ = 'ISC'
|
__license__ = 'ISC'
|
||||||
__copyright__ = '2012-2013, Ciro Mattia Gonano <ciromattia@gmail.com>'
|
__copyright__ = '2012-2013, Ciro Mattia Gonano <ciromattia@gmail.com>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
@@ -26,9 +26,14 @@ from kcc import gui
|
|||||||
from sys import platform
|
from sys import platform
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
root = Tk()
|
||||||
|
root.resizable(width=False, height=False)
|
||||||
|
root.config(padx=5, pady=5, takefocus=True)
|
||||||
|
root.title("Kindle Comic Converter v" + __version__)
|
||||||
|
#root.wm_attributes("-topmost", 1)
|
||||||
if platform == 'darwin':
|
if platform == 'darwin':
|
||||||
os.environ['PATH'] = '/usr/local/bin:' + os.environ['PATH']
|
os.environ['PATH'] = '/usr/local/bin:' + os.environ['PATH']
|
||||||
root = Tk()
|
elif platform == 'win32':
|
||||||
app = gui.MainWindow(master=root,title="Kindle Comic Converter v" + __version__)
|
root.iconbitmap(default='comic2ebook.ico')
|
||||||
root.tkraise()
|
gui.MainWindow(master=root)
|
||||||
root.mainloop()
|
root.mainloop()
|
||||||
|
|||||||
@@ -19,23 +19,26 @@ __copyright__ = '2012-2013, Ciro Mattia Gonano <ciromattia@gmail.com>'
|
|||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import zipfile
|
||||||
|
import rarfile
|
||||||
|
|
||||||
|
|
||||||
class CBxArchive:
|
class CBxArchive:
|
||||||
def __init__(self, origFileName):
|
def __init__(self, origFileName):
|
||||||
self.cbxexts = ['.zip', '.cbz', '.rar', '.cbr']
|
|
||||||
self.origFileName = origFileName
|
self.origFileName = origFileName
|
||||||
self.filename = os.path.splitext(origFileName)
|
if zipfile.is_zipfile(origFileName):
|
||||||
|
self.compressor = 'zip'
|
||||||
|
elif rarfile.is_rarfile(origFileName):
|
||||||
|
self.compressor = 'rar'
|
||||||
|
else:
|
||||||
|
self.compressor = None
|
||||||
|
|
||||||
def isCbxFile(self):
|
def isCbxFile(self):
|
||||||
return self.filename[1].lower() in self.cbxexts
|
return self.compressor is not None
|
||||||
|
|
||||||
def extractCBZ(self, targetdir):
|
def extractCBZ(self, targetdir):
|
||||||
try:
|
cbzFile = zipfile.ZipFile(self.origFileName)
|
||||||
from zipfile import ZipFile
|
filelist = []
|
||||||
except ImportError:
|
|
||||||
self.cbzFile = None
|
|
||||||
cbzFile = ZipFile(self.origFileName)
|
|
||||||
for f in cbzFile.namelist():
|
for f in cbzFile.namelist():
|
||||||
if f.startswith('__MACOSX') or f.endswith('.DS_Store'):
|
if f.startswith('__MACOSX') or f.endswith('.DS_Store'):
|
||||||
pass # skip MacOS special files
|
pass # skip MacOS special files
|
||||||
@@ -45,15 +48,12 @@ class CBxArchive:
|
|||||||
except:
|
except:
|
||||||
pass # the dir exists so we are going to extract the images only.
|
pass # the dir exists so we are going to extract the images only.
|
||||||
else:
|
else:
|
||||||
cbzFile.extract(f, targetdir)
|
filelist.append(f)
|
||||||
|
cbzFile.extractall(targetdir, filelist)
|
||||||
|
|
||||||
def extractCBR(self, targetdir):
|
def extractCBR(self, targetdir):
|
||||||
try:
|
|
||||||
import rarfile
|
|
||||||
except ImportError:
|
|
||||||
self.cbrFile = None
|
|
||||||
return
|
|
||||||
cbrFile = rarfile.RarFile(self.origFileName)
|
cbrFile = rarfile.RarFile(self.origFileName)
|
||||||
|
filelist = []
|
||||||
for f in cbrFile.namelist():
|
for f in cbrFile.namelist():
|
||||||
if f.startswith('__MACOSX') or f.endswith('.DS_Store'):
|
if f.startswith('__MACOSX') or f.endswith('.DS_Store'):
|
||||||
pass # skip MacOS special files
|
pass # skip MacOS special files
|
||||||
@@ -63,12 +63,14 @@ class CBxArchive:
|
|||||||
except:
|
except:
|
||||||
pass # the dir exists so we are going to extract the images only.
|
pass # the dir exists so we are going to extract the images only.
|
||||||
else:
|
else:
|
||||||
cbrFile.extract(f, targetdir)
|
filelist.append(f)
|
||||||
|
cbrFile.extractall(targetdir, filelist)
|
||||||
|
|
||||||
def extract(self, targetdir):
|
def extract(self, targetdir):
|
||||||
if '.cbr' == self.filename[1].lower() or '.rar' == self.filename[1].lower():
|
print "\n" + targetdir + "\n"
|
||||||
|
if self.compressor == 'rar':
|
||||||
self.extractCBR(targetdir)
|
self.extractCBR(targetdir)
|
||||||
elif '.cbz' == self.filename[1].lower() or '.zip' == self.filename[1].lower():
|
elif self.compressor == 'zip':
|
||||||
self.extractCBZ(targetdir)
|
self.extractCBZ(targetdir)
|
||||||
adir = os.listdir(targetdir)
|
adir = os.listdir(targetdir)
|
||||||
if len(adir) == 1 and os.path.isdir(os.path.join(targetdir, adir[0])):
|
if len(adir) == 1 and os.path.isdir(os.path.join(targetdir, adir[0])):
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (c) 2012 Ciro Mattia Gonano <ciromattia@gmail.com>
|
# Copyright (c) 2012 Ciro Mattia Gonano <ciromattia@gmail.com>
|
||||||
#
|
#
|
||||||
@@ -16,7 +17,7 @@
|
|||||||
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
# PERFORMANCE OF THIS SOFTWARE.
|
# PERFORMANCE OF THIS SOFTWARE.
|
||||||
#
|
#
|
||||||
__version__ = '2.6'
|
__version__ = '2.9'
|
||||||
__license__ = 'ISC'
|
__license__ = 'ISC'
|
||||||
__copyright__ = '2012-2013, Ciro Mattia Gonano <ciromattia@gmail.com>'
|
__copyright__ = '2012-2013, Ciro Mattia Gonano <ciromattia@gmail.com>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
@@ -60,23 +61,49 @@ def buildHTML(path, imgfile):
|
|||||||
"<head>\n",
|
"<head>\n",
|
||||||
"<title>", filename[0], "</title>\n",
|
"<title>", filename[0], "</title>\n",
|
||||||
"<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>\n",
|
"<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>\n",
|
||||||
|
"<link href=\"", "../" * (backref - 1),
|
||||||
|
"style.css\" type=\"text/css\" rel=\"stylesheet\"/>\n",
|
||||||
"</head>\n",
|
"</head>\n",
|
||||||
"<body>\n",
|
"<body>\n",
|
||||||
|
"<div class=\"fs\">\n",
|
||||||
"<div><img src=\"", "../" * backref, "Images/", postfix, imgfile, "\" alt=\"",
|
"<div><img src=\"", "../" * backref, "Images/", postfix, imgfile, "\" alt=\"",
|
||||||
imgfile, "\" class=\"singlePage\"/></div>\n",
|
imgfile, "\" class=\"singlePage\"/></div>\n"
|
||||||
#"<div id=\"", filename[0], "-1\">\n",
|
|
||||||
#"<a class=\"app-amzn-magnify\" data-app-amzn-magnify='{\"targetId\":\"", filename[0],
|
|
||||||
#"-1-magTargetParent\", \"ordinal\":1}'></a>\n",
|
|
||||||
#"</div>\n",
|
|
||||||
#"<div id=\"", filename[0], "-1-magTargetParent\" class=\"target-mag-parent\">\n",
|
|
||||||
#"<div class=\"target-mag-lb\">\n",
|
|
||||||
#"</div>\n",
|
|
||||||
#"<div id=\"", filename[0], "-1-magTarget\" class=\"target-mag\">\n",
|
|
||||||
#"<img src=\"../" * backref, "Images/", postfix, imgfile, "\" alt=\"", imgfile, "\"/>\n",
|
|
||||||
#"</div></div>\n",
|
|
||||||
"</body>\n",
|
|
||||||
"</html>"
|
|
||||||
])
|
])
|
||||||
|
if options.panelview:
|
||||||
|
if options.righttoleft:
|
||||||
|
f.writelines(["<div id=\"BoxTL\"><a class=\"app-amzn-magnify\" data-app-amzn-magnify='{\"targetId\":\"",
|
||||||
|
"BoxTL-Panel-Parent\", \"ordinal\":2}'></a></div>\n",
|
||||||
|
"<div id=\"BoxTR\"><a class=\"app-amzn-magnify\" data-app-amzn-magnify='{\"targetId\":\"",
|
||||||
|
"BoxTR-Panel-Parent\", \"ordinal\":1}'></a></div>\n",
|
||||||
|
"<div id=\"BoxBL\"><a class=\"app-amzn-magnify\" data-app-amzn-magnify='{\"targetId\":\"",
|
||||||
|
"BoxBL-Panel-Parent\", \"ordinal\":4}'></a></div>\n",
|
||||||
|
"<div id=\"BoxBR\"><a class=\"app-amzn-magnify\" data-app-amzn-magnify='{\"targetId\":\"",
|
||||||
|
"BoxBR-Panel-Parent\", \"ordinal\":3}'></a></div>\n"
|
||||||
|
])
|
||||||
|
else:
|
||||||
|
f.writelines(["<div id=\"BoxTL\"><a class=\"app-amzn-magnify\" data-app-amzn-magnify='{\"targetId\":\"",
|
||||||
|
"BoxTL-Panel-Parent\", \"ordinal\":1}'></a></div>\n",
|
||||||
|
"<div id=\"BoxTR\"><a class=\"app-amzn-magnify\" data-app-amzn-magnify='{\"targetId\":\"",
|
||||||
|
"BoxTR-Panel-Parent\", \"ordinal\":2}'></a></div>\n",
|
||||||
|
"<div id=\"BoxBL\"><a class=\"app-amzn-magnify\" data-app-amzn-magnify='{\"targetId\":\"",
|
||||||
|
"BoxBL-Panel-Parent\", \"ordinal\":3}'></a></div>\n",
|
||||||
|
"<div id=\"BoxBR\"><a class=\"app-amzn-magnify\" data-app-amzn-magnify='{\"targetId\":\"",
|
||||||
|
"BoxBR-Panel-Parent\", \"ordinal\":4}'></a></div>\n"
|
||||||
|
])
|
||||||
|
f.writelines(["<div id=\"BoxTL-Panel-Parent\" class=\"target-mag-parent\"><div id=\"BoxTL-Panel\" class=\"",
|
||||||
|
"target-mag\"><img src=\"", "../" * backref, "Images/", postfix, imgfile, "\" alt=\"",
|
||||||
|
imgfile, "\"/></div></div>\n",
|
||||||
|
"<div id=\"BoxTR-Panel-Parent\" class=\"target-mag-parent\"><div id=\"BoxTR-Panel\" class=\"",
|
||||||
|
"target-mag\"><img src=\"", "../" * backref, "Images/", postfix, imgfile, "\" alt=\"",
|
||||||
|
imgfile, "\"/></div></div>\n",
|
||||||
|
"<div id=\"BoxBL-Panel-Parent\" class=\"target-mag-parent\"><div id=\"BoxBL-Panel\" class=\"",
|
||||||
|
"target-mag\"><img src=\"", "../" * backref, "Images/", postfix, imgfile, "\" alt=\"",
|
||||||
|
imgfile, "\"/></div></div>\n",
|
||||||
|
"<div id=\"BoxBR-Panel-Parent\" class=\"target-mag-parent\"><div id=\"BoxBR-Panel\" class=\"",
|
||||||
|
"target-mag\"><img src=\"", "../" * backref, "Images/", postfix, imgfile, "\" alt=\"",
|
||||||
|
imgfile, "\"/></div></div>\n"
|
||||||
|
])
|
||||||
|
f.writelines(["</div>\n</body>\n</html>"])
|
||||||
f.close()
|
f.close()
|
||||||
return path, imgfile
|
return path, imgfile
|
||||||
|
|
||||||
@@ -102,7 +129,7 @@ def buildNCX(dstdir, title, chapters):
|
|||||||
f = open(ncxfile, "w")
|
f = open(ncxfile, "w")
|
||||||
f.writelines(["<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n",
|
f.writelines(["<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n",
|
||||||
"<!DOCTYPE ncx PUBLIC \"-//NISO//DTD ncx 2005-1//EN\" ",
|
"<!DOCTYPE ncx PUBLIC \"-//NISO//DTD ncx 2005-1//EN\" ",
|
||||||
"http://www.daisy.org/z3986/2005/ncx-2005-1.dtd\">\n",
|
"\"http://www.daisy.org/z3986/2005/ncx-2005-1.dtd\">\n",
|
||||||
"<ncx version=\"2005-1\" xml:lang=\"en-US\" xmlns=\"http://www.daisy.org/z3986/2005/ncx/\">\n",
|
"<ncx version=\"2005-1\" xml:lang=\"en-US\" xmlns=\"http://www.daisy.org/z3986/2005/ncx/\">\n",
|
||||||
"<head>\n",
|
"<head>\n",
|
||||||
"<meta name=\"dtb:uid\" content=\"015ffaec-9340-42f8-b163-a0c5ab7d0611\"/>\n",
|
"<meta name=\"dtb:uid\" content=\"015ffaec-9340-42f8-b163-a0c5ab7d0611\"/>\n",
|
||||||
@@ -117,19 +144,19 @@ def buildNCX(dstdir, title, chapters):
|
|||||||
folder = chapter[0].replace(os.path.join(dstdir, 'OEBPS'), '').lstrip('/').lstrip('\\\\')
|
folder = chapter[0].replace(os.path.join(dstdir, 'OEBPS'), '').lstrip('/').lstrip('\\\\')
|
||||||
title = os.path.basename(folder)
|
title = os.path.basename(folder)
|
||||||
filename = getImageFileName(os.path.join(folder, chapter[1]))
|
filename = getImageFileName(os.path.join(folder, chapter[1]))
|
||||||
f.write("<navPoint id=\"" + folder.replace('/', '_') + "\"><navLabel><text>" + title
|
f.write("<navPoint id=\"" + folder.replace('/', '_').replace('\\', '_') + "\"><navLabel><text>" + title
|
||||||
+ "</text></navLabel><content src=\"" + filename[0] + ".html\"/></navPoint>\n")
|
+ "</text></navLabel><content src=\"" + filename[0] + ".html\"/></navPoint>\n")
|
||||||
f.write("</navMap>\n</ncx>")
|
f.write("</navMap>\n</ncx>")
|
||||||
f.close()
|
f.close()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
def buildOPF(profile, dstdir, title, filelist, cover=None, righttoleft=False):
|
def buildOPF(profile, dstdir, title, filelist, cover=None):
|
||||||
opffile = os.path.join(dstdir, 'OEBPS', 'content.opf')
|
opffile = os.path.join(dstdir, 'OEBPS', 'content.opf')
|
||||||
# read the first file resolution
|
# read the first file resolution
|
||||||
profilelabel, deviceres, palette = image.ProfileData.Profiles[profile]
|
profilelabel, deviceres, palette, gamma, panelviewsize = image.ProfileData.Profiles[profile]
|
||||||
imgres = str(deviceres[0]) + "x" + str(deviceres[1])
|
imgres = str(deviceres[0]) + "x" + str(deviceres[1])
|
||||||
if righttoleft:
|
if options.righttoleft:
|
||||||
writingmode = "horizontal-rl"
|
writingmode = "horizontal-rl"
|
||||||
facing = "right"
|
facing = "right"
|
||||||
facing1 = "right"
|
facing1 = "right"
|
||||||
@@ -155,16 +182,19 @@ def buildOPF(profile, dstdir, title, filelist, cover=None, righttoleft=False):
|
|||||||
"<meta name=\"book-type\" content=\"comic\"/>\n",
|
"<meta name=\"book-type\" content=\"comic\"/>\n",
|
||||||
"<meta name=\"zero-gutter\" content=\"true\"/>\n",
|
"<meta name=\"zero-gutter\" content=\"true\"/>\n",
|
||||||
"<meta name=\"zero-margin\" content=\"true\"/>\n",
|
"<meta name=\"zero-margin\" content=\"true\"/>\n",
|
||||||
"<meta name=\"fixed-layout\" content=\"true\"/>\n",
|
"<meta name=\"fixed-layout\" content=\"true\"/>\n"
|
||||||
"<meta name=\"orientation-lock\" content=\"none\"/>\n",
|
])
|
||||||
"<meta name=\"original-resolution\" content=\"", imgres, "\"/>\n",
|
if options.landscapemode:
|
||||||
|
f.writelines(["<meta name=\"rendition:orientation\" content=\"auto\"/>\n",
|
||||||
|
"<meta name=\"orientation-lock\" content=\"none\"/>\n"])
|
||||||
|
else:
|
||||||
|
f.writelines(["<meta name=\"rendition:orientation\" content=\"portrait\"/>\n",
|
||||||
|
"<meta name=\"orientation-lock\" content=\"portrait\"/>\n"])
|
||||||
|
f.writelines(["<meta name=\"original-resolution\" content=\"", imgres, "\"/>\n",
|
||||||
"<meta name=\"primary-writing-mode\" content=\"", writingmode, "\"/>\n",
|
"<meta name=\"primary-writing-mode\" content=\"", writingmode, "\"/>\n",
|
||||||
"<meta name=\"rendition:layout\" content=\"pre-paginated\"/>\n",
|
"<meta name=\"rendition:layout\" content=\"pre-paginated\"/>\n",
|
||||||
"<meta name=\"rendition:orientation\" content=\"auto\"/>\n",
|
|
||||||
"</metadata>\n<manifest>\n<item id=\"ncx\" href=\"toc.ncx\" ",
|
"</metadata>\n<manifest>\n<item id=\"ncx\" href=\"toc.ncx\" ",
|
||||||
"media-type=\"application/x-dtbncx+xml\"/>\n"
|
"media-type=\"application/x-dtbncx+xml\"/>\n"])
|
||||||
])
|
|
||||||
# set cover
|
|
||||||
if cover is not None:
|
if cover is not None:
|
||||||
filename = getImageFileName(cover.replace(os.path.join(dstdir, 'OEBPS'), '').lstrip('/').lstrip('\\\\'))
|
filename = getImageFileName(cover.replace(os.path.join(dstdir, 'OEBPS'), '').lstrip('/').lstrip('\\\\'))
|
||||||
if '.png' == filename[1]:
|
if '.png' == filename[1]:
|
||||||
@@ -176,43 +206,57 @@ def buildOPF(profile, dstdir, title, filelist, cover=None, righttoleft=False):
|
|||||||
for path in filelist:
|
for path in filelist:
|
||||||
folder = path[0].replace(os.path.join(dstdir, 'OEBPS'), '').lstrip('/').lstrip('\\\\')
|
folder = path[0].replace(os.path.join(dstdir, 'OEBPS'), '').lstrip('/').lstrip('\\\\')
|
||||||
filename = getImageFileName(path[1])
|
filename = getImageFileName(path[1])
|
||||||
uniqueid = os.path.join(folder, filename[0]).replace('/', '_')
|
uniqueid = os.path.join(folder, filename[0]).replace('/', '_').replace('\\', '_')
|
||||||
reflist.append(uniqueid)
|
reflist.append(uniqueid)
|
||||||
f.write("<item id=\"page_" + uniqueid + "\" href=\""
|
f.write("<item id=\"page_" + uniqueid + "\" href=\""
|
||||||
+ os.path.join(folder.replace('Images', 'Text'), filename[0])
|
+ folder.replace('Images', 'Text') + "/" + filename[0]
|
||||||
+ ".html\" media-type=\"application/xhtml+xml\"/>\n")
|
+ ".html\" media-type=\"application/xhtml+xml\"/>\n")
|
||||||
if '.png' == filename[1]:
|
if '.png' == filename[1]:
|
||||||
mt = 'image/png'
|
mt = 'image/png'
|
||||||
else:
|
else:
|
||||||
mt = 'image/jpeg'
|
mt = 'image/jpeg'
|
||||||
f.write("<item id=\"img_" + uniqueid + "\" href=\"" + os.path.join(folder, path[1]) + "\" media-type=\""
|
f.write("<item id=\"img_" + uniqueid + "\" href=\"" + folder + "/" + path[1] + "\" media-type=\""
|
||||||
+ mt + "\"/>\n")
|
+ mt + "\"/>\n")
|
||||||
if (options.profile == 'K4' or options.profile == 'KHD') and splittedSomething:
|
if options.landscapemode and splitCount > 0:
|
||||||
f.write("<item id=\"blank-page\" href=\""
|
splitCountUsed = 1
|
||||||
+ os.path.join('Text', 'blank.html')
|
while splitCountUsed <= splitCount:
|
||||||
+ "\" media-type=\"application/xhtml+xml\"/>\n")
|
f.write("<item id=\"blank-page" + str(splitCountUsed) +
|
||||||
|
"\" href=\"Text/blank.html\" media-type=\"application/xhtml+xml\"/>\n")
|
||||||
|
splitCountUsed += 1
|
||||||
|
f.write("<item id=\"css\" href=\"Text/style.css\" media-type=\"text/css\"/>\n")
|
||||||
f.write("</manifest>\n<spine toc=\"ncx\">\n")
|
f.write("</manifest>\n<spine toc=\"ncx\">\n")
|
||||||
|
splitCountUsed = 1
|
||||||
for entry in reflist:
|
for entry in reflist:
|
||||||
if entry.endswith("-1"):
|
if entry.endswith("-1"):
|
||||||
if (righttoleft and facing == 'left') or (not righttoleft and facing == 'right') and \
|
# noinspection PyRedundantParentheses
|
||||||
(options.profile == 'K4' or options.profile == 'KHD'):
|
if ((options.righttoleft and facing == 'left') or (not options.righttoleft and facing == 'right')) and\
|
||||||
f.write("<itemref idref=\"blank-page\" properties=\"layout-blank\"/>\n")
|
options.landscapemode:
|
||||||
|
f.write("<itemref idref=\"blank-page" + str(splitCountUsed) + "\" properties=\"layout-blank\"/>\n")
|
||||||
|
splitCountUsed += 1
|
||||||
|
if options.landscapemode:
|
||||||
f.write("<itemref idref=\"page_" + entry + "\" properties=\"page-spread-" + facing1 + "\"/>\n")
|
f.write("<itemref idref=\"page_" + entry + "\" properties=\"page-spread-" + facing1 + "\"/>\n")
|
||||||
|
else:
|
||||||
|
f.write("<itemref idref=\"page_" + entry + "\"/>\n")
|
||||||
elif entry.endswith("-2"):
|
elif entry.endswith("-2"):
|
||||||
|
if options.landscapemode:
|
||||||
f.write("<itemref idref=\"page_" + entry + "\" properties=\"page-spread-" + facing2 + "\"/>\n")
|
f.write("<itemref idref=\"page_" + entry + "\" properties=\"page-spread-" + facing2 + "\"/>\n")
|
||||||
if righttoleft:
|
else:
|
||||||
|
f.write("<itemref idref=\"page_" + entry + "\"/>\n")
|
||||||
|
if options.righttoleft:
|
||||||
facing = "right"
|
facing = "right"
|
||||||
else:
|
else:
|
||||||
facing = "left"
|
facing = "left"
|
||||||
else:
|
else:
|
||||||
|
if options.landscapemode:
|
||||||
f.write("<itemref idref=\"page_" + entry + "\" properties=\"page-spread-" + facing + "\"/>\n")
|
f.write("<itemref idref=\"page_" + entry + "\" properties=\"page-spread-" + facing + "\"/>\n")
|
||||||
|
else:
|
||||||
|
f.write("<itemref idref=\"page_" + entry + "\"/>\n")
|
||||||
if facing == 'right':
|
if facing == 'right':
|
||||||
facing = 'left'
|
facing = 'left'
|
||||||
else:
|
else:
|
||||||
facing = 'right'
|
facing = 'right'
|
||||||
f.write("</spine>\n<guide>\n</guide>\n</package>\n")
|
f.write("</spine>\n<guide>\n</guide>\n</package>\n")
|
||||||
f.close()
|
f.close()
|
||||||
# finish with standard ePub folders
|
|
||||||
os.mkdir(os.path.join(dstdir, 'META-INF'))
|
os.mkdir(os.path.join(dstdir, 'META-INF'))
|
||||||
f = open(os.path.join(dstdir, 'mimetype'), 'w')
|
f = open(os.path.join(dstdir, 'mimetype'), 'w')
|
||||||
f.write('application/epub+zip')
|
f.write('application/epub+zip')
|
||||||
@@ -233,6 +277,10 @@ def getImageFileName(imgfile):
|
|||||||
if filename[0].startswith('.') or\
|
if filename[0].startswith('.') or\
|
||||||
(filename[1].lower() != '.png' and
|
(filename[1].lower() != '.png' and
|
||||||
filename[1].lower() != '.jpg' and
|
filename[1].lower() != '.jpg' and
|
||||||
|
filename[1].lower() != '.gif' and
|
||||||
|
filename[1].lower() != '.tif' and
|
||||||
|
filename[1].lower() != '.tiff' and
|
||||||
|
filename[1].lower() != '.bmp' and
|
||||||
filename[1].lower() != '.jpeg'):
|
filename[1].lower() != '.jpeg'):
|
||||||
return None
|
return None
|
||||||
return filename
|
return filename
|
||||||
@@ -248,18 +296,22 @@ def isInFilelist(filename, filelist):
|
|||||||
|
|
||||||
|
|
||||||
def applyImgOptimization(img, isSplit=False, toRight=False):
|
def applyImgOptimization(img, isSplit=False, toRight=False):
|
||||||
img.optimizeImage()
|
|
||||||
img.cropWhiteSpace(10.0)
|
img.cropWhiteSpace(10.0)
|
||||||
if options.cutpagenumbers:
|
if options.cutpagenumbers:
|
||||||
img.cutPageNumber()
|
img.cutPageNumber()
|
||||||
img.resizeImage(options.upscale, options.stretch, options.black_borders, isSplit, toRight)
|
img.resizeImage(options.upscale, options.stretch, options.black_borders, isSplit, toRight, options.landscapemode,
|
||||||
|
options.nopanelviewhq)
|
||||||
|
img.optimizeImage(options.gamma)
|
||||||
|
if options.forcepng:
|
||||||
img.quantizeImage()
|
img.quantizeImage()
|
||||||
|
|
||||||
|
|
||||||
def dirImgProcess(path):
|
def dirImgProcess(path):
|
||||||
global options
|
global options, splitCount
|
||||||
global splittedSomething
|
if options.righttoleft:
|
||||||
splittedSomething = False
|
facing = "right"
|
||||||
|
else:
|
||||||
|
facing = "left"
|
||||||
|
|
||||||
for (dirpath, dirnames, filenames) in os.walk(path):
|
for (dirpath, dirnames, filenames) in os.walk(path):
|
||||||
for afile in filenames:
|
for afile in filenames:
|
||||||
@@ -269,26 +321,38 @@ def dirImgProcess(path):
|
|||||||
else:
|
else:
|
||||||
print ".",
|
print ".",
|
||||||
img = image.ComicPage(os.path.join(dirpath, afile), options.profile)
|
img = image.ComicPage(os.path.join(dirpath, afile), options.profile)
|
||||||
|
if options.nosplitrotate:
|
||||||
|
split = None
|
||||||
|
else:
|
||||||
split = img.splitPage(dirpath, options.righttoleft, options.rotate)
|
split = img.splitPage(dirpath, options.righttoleft, options.rotate)
|
||||||
if split is not None:
|
if split is not None:
|
||||||
splittedSomething = True
|
|
||||||
if options.verbose:
|
if options.verbose:
|
||||||
print "Splitted " + afile
|
print "Splitted " + afile
|
||||||
if options.righttoleft:
|
if options.righttoleft:
|
||||||
toRight1 = False
|
toRight1 = False
|
||||||
toRight2 = True
|
toRight2 = True
|
||||||
|
if facing == "left":
|
||||||
|
splitCount += 1
|
||||||
|
facing = "right"
|
||||||
else:
|
else:
|
||||||
toRight1 = True
|
toRight1 = True
|
||||||
toRight2 = False
|
toRight2 = False
|
||||||
|
if facing == "right":
|
||||||
|
splitCount += 1
|
||||||
|
facing = "left"
|
||||||
img0 = image.ComicPage(split[0], options.profile)
|
img0 = image.ComicPage(split[0], options.profile)
|
||||||
applyImgOptimization(img0, True, toRight1)
|
applyImgOptimization(img0, True, toRight1)
|
||||||
img0.saveToDir(dirpath)
|
img0.saveToDir(dirpath, options.forcepng)
|
||||||
img1 = image.ComicPage(split[1], options.profile)
|
img1 = image.ComicPage(split[1], options.profile)
|
||||||
applyImgOptimization(img1, True, toRight2)
|
applyImgOptimization(img1, True, toRight2)
|
||||||
img1.saveToDir(dirpath)
|
img1.saveToDir(dirpath, options.forcepng)
|
||||||
else:
|
else:
|
||||||
|
if facing == "right":
|
||||||
|
facing = "left"
|
||||||
|
else:
|
||||||
|
facing = "right"
|
||||||
applyImgOptimization(img)
|
applyImgOptimization(img)
|
||||||
img.saveToDir(dirpath)
|
img.saveToDir(dirpath, options.forcepng)
|
||||||
|
|
||||||
|
|
||||||
def genEpubStruct(path):
|
def genEpubStruct(path):
|
||||||
@@ -296,13 +360,134 @@ def genEpubStruct(path):
|
|||||||
filelist = []
|
filelist = []
|
||||||
chapterlist = []
|
chapterlist = []
|
||||||
cover = None
|
cover = None
|
||||||
|
_, deviceres, _, _, panelviewsize = image.ProfileData.Profiles[options.profile]
|
||||||
|
sanitizeTree(os.path.join(path, 'OEBPS', 'Images'))
|
||||||
os.mkdir(os.path.join(path, 'OEBPS', 'Text'))
|
os.mkdir(os.path.join(path, 'OEBPS', 'Text'))
|
||||||
|
f = open(os.path.join(path, 'OEBPS', 'Text', 'style.css'), 'w')
|
||||||
|
#DON'T COMPRESS CSS. KINDLE WILL FAIL TO PARSE IT.
|
||||||
|
#Generic Panel View support + Margins fix for Non-Kindle devices.
|
||||||
|
f.writelines(["@page {\n",
|
||||||
|
"margin-bottom: 0;\n",
|
||||||
|
"margin-top: 0\n",
|
||||||
|
"}\n",
|
||||||
|
"body {\n",
|
||||||
|
"display: block;\n",
|
||||||
|
"margin-bottom: 0;\n",
|
||||||
|
"margin-left: 0;\n",
|
||||||
|
"margin-right: 0;\n",
|
||||||
|
"margin-top: 0;\n",
|
||||||
|
"padding-bottom: 0;\n",
|
||||||
|
"padding-left: 0;\n",
|
||||||
|
"padding-right: 0;\n",
|
||||||
|
"padding-top: 0;\n",
|
||||||
|
"text-align: left\n",
|
||||||
|
"}\n",
|
||||||
|
"div.fs {\n",
|
||||||
|
"height: ", str(deviceres[1]), "px;\n",
|
||||||
|
"width: ", str(deviceres[0]), "px;\n",
|
||||||
|
"position: relative;\n",
|
||||||
|
"display: block;\n",
|
||||||
|
"text-align: center\n",
|
||||||
|
"}\n",
|
||||||
|
"div.fs a {\n",
|
||||||
|
"display: block;\n",
|
||||||
|
"width : 100%;\n",
|
||||||
|
"height: 100%;\n",
|
||||||
|
"}\n",
|
||||||
|
"div.fs div {\n",
|
||||||
|
"position: absolute;\n",
|
||||||
|
"}\n",
|
||||||
|
"img.singlePage {\n",
|
||||||
|
"position: absolute;\n",
|
||||||
|
"height: ", str(deviceres[1]), "px;\n",
|
||||||
|
"width: ", str(deviceres[0]), "px;\n",
|
||||||
|
"}\n",
|
||||||
|
"div.target-mag-parent {\n",
|
||||||
|
"width:100%;\n",
|
||||||
|
"height:100%;\n",
|
||||||
|
"display:none;\n",
|
||||||
|
"}\n",
|
||||||
|
"div.target-mag {\n",
|
||||||
|
"position: absolute;\n",
|
||||||
|
"display: block;\n",
|
||||||
|
"overflow: hidden;\n",
|
||||||
|
"}\n",
|
||||||
|
"div.target-mag img {\n",
|
||||||
|
"position: absolute;\n",
|
||||||
|
"height: ", str(panelviewsize[1]), "px;\n",
|
||||||
|
"width: ", str(panelviewsize[0]), "px;\n",
|
||||||
|
"}\n",
|
||||||
|
"#BoxTL {\n",
|
||||||
|
"top: 0;\n",
|
||||||
|
"left: 0;\n",
|
||||||
|
"height: 50%;\n",
|
||||||
|
"width: 50%;\n",
|
||||||
|
"}\n",
|
||||||
|
"#BoxTR {\n",
|
||||||
|
"top: 0;\n",
|
||||||
|
"right: 0;\n",
|
||||||
|
"height: 50%;\n",
|
||||||
|
"width: 50%;\n",
|
||||||
|
"}\n",
|
||||||
|
"#BoxBL {\n",
|
||||||
|
"bottom: 0;\n",
|
||||||
|
"left: 0;\n",
|
||||||
|
"height: 50%;\n",
|
||||||
|
"width: 50%;\n",
|
||||||
|
"}\n",
|
||||||
|
"#BoxBR {\n",
|
||||||
|
"bottom: 0;\n",
|
||||||
|
"right: 0;\n",
|
||||||
|
"height: 50%;\n",
|
||||||
|
"width: 50%;\n",
|
||||||
|
"}\n",
|
||||||
|
"#BoxTL-Panel {\n",
|
||||||
|
"top: 0;\n",
|
||||||
|
"left: 0;\n",
|
||||||
|
"height: 100%;\n",
|
||||||
|
"width: 100%;\n",
|
||||||
|
"}\n",
|
||||||
|
"#BoxTL-Panel img {\n",
|
||||||
|
"top: 0%;\n",
|
||||||
|
"left: 0%;\n",
|
||||||
|
"}\n",
|
||||||
|
"#BoxTR-Panel {\n",
|
||||||
|
"top: 0;\n",
|
||||||
|
"right: 0;\n",
|
||||||
|
"height: 100%;\n",
|
||||||
|
"width: 100%;\n",
|
||||||
|
"}\n",
|
||||||
|
"#BoxTR-Panel img {\n",
|
||||||
|
"top: 0%;\n",
|
||||||
|
"right: 0%;\n",
|
||||||
|
"}\n",
|
||||||
|
"#BoxBL-Panel {\n",
|
||||||
|
"bottom: 0;\n",
|
||||||
|
"left: 0;\n",
|
||||||
|
"height: 100%;\n",
|
||||||
|
"width: 100%;\n",
|
||||||
|
"}\n",
|
||||||
|
"#BoxBL-Panel img {\n",
|
||||||
|
"bottom: 0%;\n",
|
||||||
|
"left: 0%;\n",
|
||||||
|
"}\n",
|
||||||
|
"#BoxBR-Panel {\n",
|
||||||
|
"bottom: 0;\n",
|
||||||
|
"right: 0;\n",
|
||||||
|
"height: 100%;\n",
|
||||||
|
"width: 100%;\n",
|
||||||
|
"}\n",
|
||||||
|
"#BoxBR-Panel img {\n",
|
||||||
|
"bottom: 0%;\n",
|
||||||
|
"right: 0%;\n",
|
||||||
|
"}"
|
||||||
|
])
|
||||||
|
f.close()
|
||||||
for (dirpath, dirnames, filenames) in os.walk(os.path.join(path, 'OEBPS', 'Images')):
|
for (dirpath, dirnames, filenames) in os.walk(os.path.join(path, 'OEBPS', 'Images')):
|
||||||
chapter = False
|
chapter = False
|
||||||
for afile in filenames:
|
for afile in filenames:
|
||||||
filename = getImageFileName(afile)
|
filename = getImageFileName(afile)
|
||||||
if filename is not None:
|
if filename is not None:
|
||||||
# put credits at the end
|
|
||||||
if "credit" in afile.lower():
|
if "credit" in afile.lower():
|
||||||
os.rename(os.path.join(dirpath, afile), os.path.join(dirpath, 'ZZZ999_' + afile))
|
os.rename(os.path.join(dirpath, afile), os.path.join(dirpath, 'ZZZ999_' + afile))
|
||||||
afile = 'ZZZ999_' + afile
|
afile = 'ZZZ999_' + afile
|
||||||
@@ -322,14 +507,13 @@ def genEpubStruct(path):
|
|||||||
convert = lambda text: int(text) if text.isdigit() else text
|
convert = lambda text: int(text) if text.isdigit() else text
|
||||||
alphanum_key = lambda key: [convert(c) for c in re.split('([0-9]+)', key)]
|
alphanum_key = lambda key: [convert(c) for c in re.split('([0-9]+)', key)]
|
||||||
filelist.sort(key=lambda name: (alphanum_key(name[0].lower()), alphanum_key(name[1].lower())))
|
filelist.sort(key=lambda name: (alphanum_key(name[0].lower()), alphanum_key(name[1].lower())))
|
||||||
buildOPF(options.profile, path, options.title, filelist, cover, options.righttoleft)
|
buildOPF(options.profile, path, options.title, filelist, cover)
|
||||||
if (options.profile == 'K4' or options.profile == 'KHD') and splittedSomething:
|
if options.landscapemode and splitCount > 0:
|
||||||
filelist.append(buildBlankHTML(os.path.join(path, 'OEBPS', 'Text')))
|
filelist.append(buildBlankHTML(os.path.join(path, 'OEBPS', 'Text')))
|
||||||
|
|
||||||
|
|
||||||
def getWorkFolder(afile):
|
def getWorkFolder(afile):
|
||||||
workdir = tempfile.mkdtemp()
|
workdir = tempfile.mkdtemp()
|
||||||
fname = os.path.splitext(afile)
|
|
||||||
if os.path.isdir(afile):
|
if os.path.isdir(afile):
|
||||||
try:
|
try:
|
||||||
import shutil
|
import shutil
|
||||||
@@ -338,13 +522,18 @@ def getWorkFolder(afile):
|
|||||||
path = workdir
|
path = workdir
|
||||||
except OSError:
|
except OSError:
|
||||||
raise
|
raise
|
||||||
elif fname[1].lower() == '.pdf':
|
elif afile.lower().endswith('.pdf'):
|
||||||
pdf = pdfjpgextract.PdfJpgExtract(afile)
|
pdf = pdfjpgextract.PdfJpgExtract(afile)
|
||||||
path = pdf.extract()
|
path = pdf.extract()
|
||||||
else:
|
else:
|
||||||
cbx = cbxarchive.CBxArchive(afile)
|
cbx = cbxarchive.CBxArchive(afile)
|
||||||
if cbx.isCbxFile():
|
if cbx.isCbxFile():
|
||||||
|
try:
|
||||||
path = cbx.extract(workdir)
|
path = cbx.extract(workdir)
|
||||||
|
except OSError:
|
||||||
|
print 'Unrar not found, please download from ' + \
|
||||||
|
'http://www.rarlab.com/download.htm and put into your PATH.'
|
||||||
|
sys.exit(21)
|
||||||
else:
|
else:
|
||||||
raise TypeError
|
raise TypeError
|
||||||
move(path, path + "_temp")
|
move(path, path + "_temp")
|
||||||
@@ -352,73 +541,145 @@ def getWorkFolder(afile):
|
|||||||
return path
|
return path
|
||||||
|
|
||||||
|
|
||||||
|
def slugify(value):
|
||||||
|
"""
|
||||||
|
Normalizes string, converts to lowercase, removes non-alpha characters,
|
||||||
|
and converts spaces to hyphens.
|
||||||
|
"""
|
||||||
|
import unicodedata
|
||||||
|
value = unicodedata.normalize('NFKD', unicode(value, 'latin1')).encode('ascii', 'ignore')
|
||||||
|
value = re.sub('[^\w\s\.-]', '', value).strip().lower()
|
||||||
|
value = re.sub('[-\.\s]+', '-', value)
|
||||||
|
value = re.sub(r'([0-9]+)', r'00000\1', value)
|
||||||
|
value = re.sub(r'0*([0-9]{6,})', r'\1', value)
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
def sanitizeTree(filetree):
|
||||||
|
for root, dirs, files in os.walk(filetree):
|
||||||
|
for name in files:
|
||||||
|
if name.startswith('.') or name.lower() == 'thumbs.db':
|
||||||
|
os.remove(os.path.join(root, name))
|
||||||
|
else:
|
||||||
|
splitname = os.path.splitext(name)
|
||||||
|
os.rename(os.path.join(root, name),
|
||||||
|
os.path.join(root, slugify(splitname[0]) + splitname[1]))
|
||||||
|
for name in dirs:
|
||||||
|
if name.startswith('.'):
|
||||||
|
os.remove(os.path.join(root, name))
|
||||||
|
else:
|
||||||
|
os.rename(os.path.join(root, name), os.path.join(root, slugify(name)))
|
||||||
|
|
||||||
|
|
||||||
def Copyright():
|
def Copyright():
|
||||||
print ('comic2ebook v%(__version__)s. '
|
print ('comic2ebook v%(__version__)s. '
|
||||||
'Written 2012 by Ciro Mattia Gonano.' % globals())
|
'Written 2012 by Ciro Mattia Gonano.' % globals())
|
||||||
|
|
||||||
|
|
||||||
def Usage():
|
def Usage():
|
||||||
print "Generates HTML, NCX and OPF for a Comic ebook from a bunch of images"
|
print "Generates HTML, NCX and OPF for a Comic ebook from a bunch of images."
|
||||||
print "Optimized for creating Mobipockets to be read into Kindle Paperwhite"
|
|
||||||
parser.print_help()
|
parser.print_help()
|
||||||
|
|
||||||
|
|
||||||
def main(argv=None):
|
def main(argv=None):
|
||||||
global parser, options, epub_path
|
global parser, options, epub_path, splitCount
|
||||||
usage = "Usage: %prog [options] comic_file|comic_folder"
|
usage = "Usage: %prog [options] comic_file|comic_folder"
|
||||||
parser = OptionParser(usage=usage, version=__version__)
|
parser = OptionParser(usage=usage, version=__version__)
|
||||||
parser.add_option("-p", "--profile", action="store", dest="profile", default="KHD",
|
parser.add_option("-p", "--profile", action="store", dest="profile", default="KHD",
|
||||||
help="Device profile (choose one among K1, K2, K3, K4, KDX, KDXG or KHD) [default=KHD]")
|
help="Device profile (Choose one among K1, K2, K3, K4NT, K4T, KDX, KDXG or KHD) [Default=KHD]")
|
||||||
parser.add_option("-t", "--title", action="store", dest="title", default="defaulttitle",
|
parser.add_option("-t", "--title", action="store", dest="title", default="defaulttitle",
|
||||||
help="Comic title [default=filename]")
|
help="Comic title [Default=filename]")
|
||||||
parser.add_option("-m", "--manga-style", action="store_true", dest="righttoleft", default=False,
|
parser.add_option("-m", "--manga-style", action="store_true", dest="righttoleft", default=False,
|
||||||
help="'Manga style' (right-to-left reading and splitting) [default=False]")
|
help="Manga style (Right-to-left reading and splitting) [Default=False]")
|
||||||
parser.add_option("-v", "--verbose", action="store_true", dest="verbose", default=False,
|
parser.add_option("-c", "--cbz-output", action="store_true", dest="cbzoutput", default=False,
|
||||||
help="Verbose output [default=False]")
|
help="Outputs a CBZ archive and does not generate EPUB")
|
||||||
parser.add_option("--no-image-processing", action="store_false", dest="imgproc", default=True,
|
parser.add_option("--nopanelviewhq", action="store_true", dest="nopanelviewhq", default=False,
|
||||||
help="Do not apply image preprocessing (page splitting and optimizations) [default=True]")
|
help="Disable high quality Panel View [Default=False]")
|
||||||
parser.add_option("--upscale-images", action="store_true", dest="upscale", default=False,
|
parser.add_option("--noprocessing", action="store_false", dest="imgproc", default=True,
|
||||||
help="Resize images smaller than device's resolution [default=False]")
|
help="Do not apply image preprocessing (Page splitting and optimizations) [Default=True]")
|
||||||
parser.add_option("--stretch-images", action="store_true", dest="stretch", default=False,
|
parser.add_option("--forcepng", action="store_true", dest="forcepng", default=False,
|
||||||
help="Stretch images to device's resolution [default=False]")
|
help="Create PNG files instead JPEG (For non-Kindle devices) [Default=False]")
|
||||||
parser.add_option("--black-borders", action="store_true", dest="black_borders", default=False,
|
parser.add_option("--gamma", type="float", dest="gamma", default="0.0",
|
||||||
help="Use black borders (instead of white ones) when not stretching and ratio "
|
help="Apply gamma correction to linearize the image [Default=Auto]")
|
||||||
+ "is not like the device's one [default=False]")
|
parser.add_option("--upscale", action="store_true", dest="upscale", default=False,
|
||||||
parser.add_option("--no-cut-page-numbers", action="store_false", dest="cutpagenumbers", default=True,
|
help="Resize images smaller than device's resolution [Default=False]")
|
||||||
help="Do not try to cut page numbering on images [default=True]")
|
parser.add_option("--stretch", action="store_true", dest="stretch", default=False,
|
||||||
|
help="Stretch images to device's resolution [Default=False]")
|
||||||
|
parser.add_option("--blackborders", action="store_true", dest="black_borders", default=False,
|
||||||
|
help="Use black borders instead of white ones when not stretching and ratio "
|
||||||
|
+ "is not like the device's one [Default=False]")
|
||||||
parser.add_option("--rotate", action="store_true", dest="rotate", default=False,
|
parser.add_option("--rotate", action="store_true", dest="rotate", default=False,
|
||||||
help="Rotate landscape pages instead of splitting them [default=False]")
|
help="Rotate landscape pages instead of splitting them [Default=False]")
|
||||||
|
parser.add_option("--nosplitrotate", action="store_true", dest="nosplitrotate", default=False,
|
||||||
|
help="Disable splitting and rotation [Default=False]")
|
||||||
|
parser.add_option("--nocutpagenumbers", action="store_false", dest="cutpagenumbers", default=True,
|
||||||
|
help="Do not try to cut page numbering on images [Default=True]")
|
||||||
parser.add_option("-o", "--output", action="store", dest="output", default=None,
|
parser.add_option("-o", "--output", action="store", dest="output", default=None,
|
||||||
help="Output directory or file for generated ePub")
|
help="Output generated file (EPUB or CBZ) to specified directory or file")
|
||||||
|
parser.add_option("-v", "--verbose", action="store_true", dest="verbose", default=False,
|
||||||
|
help="Verbose output [Default=False]")
|
||||||
options, args = parser.parse_args(argv)
|
options, args = parser.parse_args(argv)
|
||||||
|
checkOptions()
|
||||||
if len(args) != 1:
|
if len(args) != 1:
|
||||||
parser.print_help()
|
parser.print_help()
|
||||||
return
|
return
|
||||||
path = getWorkFolder(args[0])
|
path = getWorkFolder(args[0])
|
||||||
if options.title == 'defaulttitle':
|
if options.title == 'defaulttitle':
|
||||||
options.title = os.path.splitext(os.path.basename(args[0]))[0]
|
options.title = os.path.splitext(os.path.basename(args[0]))[0]
|
||||||
|
splitCount = 0
|
||||||
if options.imgproc:
|
if options.imgproc:
|
||||||
print "Processing images..."
|
print "Processing images..."
|
||||||
dirImgProcess(path + "/OEBPS/Images/")
|
dirImgProcess(path + "/OEBPS/Images/")
|
||||||
print "Creating ePub structure..."
|
if options.cbzoutput:
|
||||||
|
# if CBZ output wanted, compress all images and return filepath
|
||||||
|
print "\nCreating CBZ file..."
|
||||||
|
filepath = getOutputFilename(args[0], options.output, '.cbz')
|
||||||
|
make_archive(path + '_comic', 'zip', path + '/OEBPS/Images')
|
||||||
|
else:
|
||||||
|
print "\nCreating ePub structure..."
|
||||||
genEpubStruct(path)
|
genEpubStruct(path)
|
||||||
# actually zip the ePub
|
# actually zip the ePub
|
||||||
if options.output is not None:
|
filepath = getOutputFilename(args[0], options.output, '.epub')
|
||||||
if options.output.endswith('.epub'):
|
|
||||||
epubpath = os.path.abspath(options.output)
|
|
||||||
elif os.path.isdir(args[0]):
|
|
||||||
epubpath = os.path.abspath(options.output) + "/" + os.path.basename(args[0]) + '.epub'
|
|
||||||
else:
|
|
||||||
epubpath = os.path.abspath(options.output) + "/" \
|
|
||||||
+ os.path.basename(os.path.splitext(args[0])[0]) + '.epub'
|
|
||||||
elif os.path.isdir(args[0]):
|
|
||||||
epubpath = args[0] + '.epub'
|
|
||||||
else:
|
|
||||||
epubpath = os.path.splitext(args[0])[0] + '.epub'
|
|
||||||
make_archive(path + '_comic', 'zip', path)
|
make_archive(path + '_comic', 'zip', path)
|
||||||
move(path + '_comic.zip', epubpath)
|
move(path + '_comic.zip', filepath)
|
||||||
rmtree(path)
|
rmtree(path)
|
||||||
return epubpath
|
return filepath
|
||||||
|
|
||||||
|
|
||||||
|
def getOutputFilename(srcpath, wantedname, ext):
|
||||||
|
if not ext.startswith('.'):
|
||||||
|
ext = '.' + ext
|
||||||
|
if wantedname is not None:
|
||||||
|
if wantedname.endswith(ext):
|
||||||
|
filename = os.path.abspath(wantedname)
|
||||||
|
elif os.path.isdir(srcpath):
|
||||||
|
filename = os.path.abspath(options.output) + "/" + os.path.basename(srcpath) + ext
|
||||||
|
else:
|
||||||
|
filename = os.path.abspath(options.output) + "/" \
|
||||||
|
+ os.path.basename(os.path.splitext(srcpath)[0]) + ext
|
||||||
|
elif os.path.isdir(srcpath):
|
||||||
|
filename = srcpath + ext
|
||||||
|
else:
|
||||||
|
filename = os.path.splitext(srcpath)[0] + ext
|
||||||
|
if os.path.isfile(filename):
|
||||||
|
filename = os.path.splitext(filename)[0] + '_kcc' + ext
|
||||||
|
return filename
|
||||||
|
|
||||||
|
|
||||||
|
def checkOptions():
|
||||||
|
global options
|
||||||
|
if options.profile == 'K4T' or options.profile == 'KHD':
|
||||||
|
options.landscapemode = True
|
||||||
|
else:
|
||||||
|
options.landscapemode = False
|
||||||
|
if options.profile == 'K3' or options.profile == 'K4NT':
|
||||||
|
#Real Panel View
|
||||||
|
options.panelview = True
|
||||||
|
else:
|
||||||
|
#Virtual Panel View
|
||||||
|
options.panelview = False
|
||||||
|
if options.profile == 'K1' or options.profile == 'K2' or options.profile == 'KDX' or options.profile == 'KDXG':
|
||||||
|
options.nopanelviewhq = True
|
||||||
|
|
||||||
|
|
||||||
def getEpubPath():
|
def getEpubPath():
|
||||||
|
|||||||
221
kcc/gui.py
221
kcc/gui.py
@@ -31,6 +31,7 @@ from subprocess import call
|
|||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
import stat
|
import stat
|
||||||
|
import traceback
|
||||||
|
|
||||||
|
|
||||||
class MainWindow:
|
class MainWindow:
|
||||||
@@ -39,25 +40,22 @@ class MainWindow:
|
|||||||
self.filelist = []
|
self.filelist = []
|
||||||
self.refresh_list()
|
self.refresh_list()
|
||||||
|
|
||||||
|
def change_gamma(self):
|
||||||
|
if self.aEntry['state'] == DISABLED:
|
||||||
|
self.aEntry['state'] = NORMAL
|
||||||
|
else:
|
||||||
|
self.aEntry['state'] = DISABLED
|
||||||
|
|
||||||
def open_files(self):
|
def open_files(self):
|
||||||
filetypes = [('all files', '.*'), ('Comic files', ('*.cbr', '*.cbz', '*.zip', '*.rar', '*.pdf'))]
|
filetypes = [('All files', '.*'), ('Comic files', ('*.cbr', '*.cbz', '*.zip', '*.rar', '*.pdf'))]
|
||||||
f = tkFileDialog.askopenfilenames(title="Choose a file...", filetypes=filetypes)
|
f = tkFileDialog.askopenfilenames(title="Choose files", filetypes=filetypes)
|
||||||
if not isinstance(f, tuple):
|
if not isinstance(f, tuple):
|
||||||
try:
|
f = self.master.tk.splitlist(f)
|
||||||
import re
|
|
||||||
f = re.findall('\{(.*?)\}', f)
|
|
||||||
except:
|
|
||||||
import tkMessageBox
|
|
||||||
tkMessageBox.showerror(
|
|
||||||
"Open file",
|
|
||||||
"askopenfilename() returned other than a tuple and no regex module could be found"
|
|
||||||
)
|
|
||||||
sys.exit(1)
|
|
||||||
self.filelist.extend(f)
|
self.filelist.extend(f)
|
||||||
self.refresh_list()
|
self.refresh_list()
|
||||||
|
|
||||||
def open_folder(self):
|
def open_folder(self):
|
||||||
f = tkFileDialog.askdirectory(title="Choose a folder...")
|
f = tkFileDialog.askdirectory(title="Choose folder:")
|
||||||
self.filelist.extend([f])
|
self.filelist.extend([f])
|
||||||
self.refresh_list()
|
self.refresh_list()
|
||||||
|
|
||||||
@@ -67,6 +65,13 @@ class MainWindow:
|
|||||||
for afile in self.filelist:
|
for afile in self.filelist:
|
||||||
self.filelocation.insert(END, afile)
|
self.filelocation.insert(END, afile)
|
||||||
self.filelocation.config(state=DISABLED)
|
self.filelocation.config(state=DISABLED)
|
||||||
|
try:
|
||||||
|
if len(self.filelist) > 0:
|
||||||
|
self.submit['state'] = NORMAL
|
||||||
|
else:
|
||||||
|
self.submit['state'] = DISABLED
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
|
||||||
def initialize(self):
|
def initialize(self):
|
||||||
self.filelocation = Listbox(self.master)
|
self.filelocation = Listbox(self.master)
|
||||||
@@ -74,97 +79,155 @@ class MainWindow:
|
|||||||
self.refresh_list()
|
self.refresh_list()
|
||||||
|
|
||||||
self.clear_file = Button(self.master, text="Clear files", command=self.clear_files)
|
self.clear_file = Button(self.master, text="Clear files", command=self.clear_files)
|
||||||
self.clear_file.grid(row=4, column=0, rowspan=3)
|
self.clear_file.grid(row=4, column=0, sticky=W + E + N + S)
|
||||||
self.open_file = Button(self.master, text="Add files...", command=self.open_files)
|
self.open_file = Button(self.master, text="Add files", command=self.open_files)
|
||||||
self.open_file.grid(row=4, column=1, rowspan=3)
|
self.open_file.grid(row=4, column=1, sticky=W + E + N + S)
|
||||||
self.open_folder = Button(self.master, text="Add folder...", command=self.open_folder)
|
self.open_folder = Button(self.master, text="Add folder", command=self.open_folder)
|
||||||
self.open_folder.grid(row=4, column=2, rowspan=3)
|
self.open_folder.grid(row=4, column=2, sticky=W + E + N + S)
|
||||||
|
|
||||||
self.profile = StringVar()
|
self.profile = StringVar()
|
||||||
profiles = sorted(ProfileData.ProfileLabels.iterkeys())
|
profiles = sorted(ProfileData.ProfileLabels.iterkeys())
|
||||||
self.profile.set(profiles[-1])
|
self.profile.set(profiles[-1])
|
||||||
w = apply(OptionMenu, (self.master, self.profile) + tuple(profiles))
|
w = apply(OptionMenu, (self.master, self.profile) + tuple(profiles))
|
||||||
w.grid(row=1, column=3)
|
w.grid(row=4, column=3, sticky=W + E + N + S)
|
||||||
|
|
||||||
self.options = {
|
self.options = {
|
||||||
'epub_only': IntVar(None, 0),
|
'Aepub_only': IntVar(None, 0),
|
||||||
'image_preprocess': IntVar(None, 1),
|
'Bcbz_only': IntVar(None, 0),
|
||||||
'rotate': IntVar(None, 0),
|
'Cmangastyle': IntVar(None, 0),
|
||||||
'cut_page_numbers': IntVar(None, 1),
|
'Dnopanelviewhq': IntVar(None, 0),
|
||||||
'mangastyle': IntVar(None, 0),
|
'Eimage_preprocess': IntVar(None, 0),
|
||||||
'image_upscale': IntVar(None, 0),
|
'Fforcepng': IntVar(None, 0),
|
||||||
'image_stretch': IntVar(None, 0),
|
'Gimage_gamma': DoubleVar(None, 0.0),
|
||||||
'black_borders': IntVar(None, 0)
|
'Himage_upscale': IntVar(None, 0),
|
||||||
|
'Iimage_stretch': IntVar(None, 0),
|
||||||
|
'Jblack_borders': IntVar(None, 0),
|
||||||
|
'Krotate': IntVar(None, 0),
|
||||||
|
'Lnosplitrotate': IntVar(None, 0),
|
||||||
|
'Mcut_page_numbers': IntVar(None, 0)
|
||||||
}
|
}
|
||||||
self.optionlabels = {
|
self.optionlabels = {
|
||||||
'epub_only': "Generate ePub only (does not call 'kindlegen')",
|
'Aepub_only': "Generate EPUB only",
|
||||||
'image_preprocess': "Apply image optimizations",
|
'Bcbz_only': "Generate CBZ only (skip EPUB/Mobi generation)",
|
||||||
'rotate': "Rotate landscape images instead of splitting them",
|
'Cmangastyle': "Manga mode",
|
||||||
'cut_page_numbers': "Cut page numbers",
|
'Dnopanelviewhq': "Disable high quality Panel View",
|
||||||
'mangastyle': "Manga-style (right-to-left reading, applies to reading and splitting)",
|
'Eimage_preprocess': "Disable image optimizations",
|
||||||
'image_upscale': "Allow image upscaling",
|
'Fforcepng': "Create PNG files instead of JPEG",
|
||||||
'image_stretch': "Stretch images",
|
'Gimage_gamma': "Custom gamma correction",
|
||||||
'black_borders': "Use black borders"
|
'Himage_upscale': "Allow image upscaling",
|
||||||
|
'Iimage_stretch': "Stretch images",
|
||||||
|
'Jblack_borders': "Use black borders (instead of white ones)",
|
||||||
|
'Krotate': "Rotate images (instead of splitting them)",
|
||||||
|
'Lnosplitrotate': "Disable both splitting and rotation",
|
||||||
|
'Mcut_page_numbers': "Disable page numbers cutting"
|
||||||
}
|
}
|
||||||
for key in self.options:
|
self.optionsButtons = {}
|
||||||
aCheckButton = Checkbutton(self.master, text=self.optionlabels[key], variable=self.options[key])
|
for key in sorted(self.options):
|
||||||
aCheckButton.grid(column=3, sticky='w')
|
if isinstance(self.options[key], IntVar) or isinstance(self.options[key], BooleanVar):
|
||||||
self.progressbar = ttk.Progressbar(orient=HORIZONTAL, length=200, mode='determinate')
|
self.optionsButtons[key] = Checkbutton(self.master, text=self.optionlabels[key],
|
||||||
|
variable=self.options[key])
|
||||||
|
self.optionsButtons[key].grid(columnspan=4, sticky=W + N + S)
|
||||||
|
elif isinstance(self.options[key], DoubleVar):
|
||||||
|
self.optionsButtons[key] = Checkbutton(self.master, text=self.optionlabels[key],
|
||||||
|
command=self.change_gamma)
|
||||||
|
self.optionsButtons[key].grid(columnspan=4, sticky=W + N + S)
|
||||||
|
self.aEntry = Entry(self.master, textvariable=self.options[key])
|
||||||
|
self.aEntry['state'] = DISABLED
|
||||||
|
self.aEntry.grid(column=3, row=(self.master.grid_size()[1] - 1), sticky=W + N + S)
|
||||||
|
|
||||||
self.submit = Button(self.master, text="Execute!", command=self.start_conversion, fg="red")
|
self.submit = Button(self.master, text="CONVERT", command=self.start_conversion, fg="red", state=DISABLED)
|
||||||
self.submit.grid(column=3)
|
self.submit.grid(columnspan=4, sticky=W + E + N + S)
|
||||||
self.progressbar.grid(column=0, columnspan=4, sticky=W + E + N + S)
|
aLabel = Label(self.master, text="File progress:", anchor=W, justify=LEFT)
|
||||||
|
aLabel.grid(column=0, sticky=E)
|
||||||
|
self.progress_file = ttk.Progressbar(orient=HORIZONTAL, length=200, mode='determinate', maximum=4)
|
||||||
|
self.progress_file.grid(column=1, columnspan=3, row=(self.master.grid_size()[1] - 1), sticky=W + E + N + S)
|
||||||
|
aLabel = Label(self.master, text="Overall progress:", anchor=W, justify=LEFT)
|
||||||
|
aLabel.grid(column=0, sticky=E)
|
||||||
|
self.progress_overall = ttk.Progressbar(orient=HORIZONTAL, length=200, mode='determinate')
|
||||||
|
self.progress_overall.grid(column=1, columnspan=3, row=(self.master.grid_size()[1] - 1), sticky=W + E + N + S)
|
||||||
|
|
||||||
self.notelabel = Label(self.master,
|
retcode = call("kindlegen", shell=True)
|
||||||
text="GUI can seem frozen while converting, kindly wait until some message appears!")
|
if retcode == 1:
|
||||||
self.notelabel.grid(column=0, columnspan=4, sticky=W + E + N + S)
|
self.optionsButtons['Aepub_only'].select()
|
||||||
|
self.optionsButtons['Aepub_only']['state'] = DISABLED
|
||||||
|
|
||||||
def start_conversion(self):
|
def start_conversion(self):
|
||||||
self.progressbar.start()
|
self.submit['state'] = DISABLED
|
||||||
|
self.master.update()
|
||||||
self.convert()
|
self.convert()
|
||||||
self.progressbar.stop()
|
self.submit['state'] = NORMAL
|
||||||
|
self.master.update()
|
||||||
|
|
||||||
def convert(self):
|
def convert(self):
|
||||||
if len(self.filelist) < 1:
|
if len(self.filelist) < 1:
|
||||||
tkMessageBox.showwarning('No file selected', "You should really select some files to convert...")
|
tkMessageBox.showwarning('No files selected!', "Please choose files to convert.")
|
||||||
return
|
return
|
||||||
profilekey = ProfileData.ProfileLabels[self.profile.get()]
|
profilekey = ProfileData.ProfileLabels[self.profile.get()]
|
||||||
argv = ["-p", profilekey]
|
argv = ["-p", profilekey]
|
||||||
if self.options['image_preprocess'].get() == 0:
|
if self.options['Bcbz_only'].get() == 1:
|
||||||
argv.append("--no-image-processing")
|
argv.append("-c")
|
||||||
if self.options['rotate'].get() == 1:
|
if self.options['Cmangastyle'].get() == 1:
|
||||||
argv.append("--rotate")
|
|
||||||
if self.options['cut_page_numbers'].get() == 0:
|
|
||||||
argv.append("--no-cut-page-numbers")
|
|
||||||
if self.options['mangastyle'].get() == 1:
|
|
||||||
argv.append("-m")
|
argv.append("-m")
|
||||||
if self.options['image_upscale'].get() == 1:
|
if self.options['Dnopanelviewhq'].get() == 1:
|
||||||
argv.append("--upscale-images")
|
argv.append("--nopanelviewhq")
|
||||||
if self.options['image_stretch'].get() == 1:
|
if self.options['Eimage_preprocess'].get() == 1:
|
||||||
argv.append("--stretch-images")
|
argv.append("--noprocessing")
|
||||||
if self.options['black_borders'].get() == 1:
|
if self.options['Fforcepng'].get() == 1:
|
||||||
argv.append("--black-borders")
|
argv.append("--forcepng")
|
||||||
|
if self.options['Gimage_gamma'].get() != 0.0:
|
||||||
|
argv.append("--gamma")
|
||||||
|
argv.append(self.options['Gimage_gamma'].get())
|
||||||
|
if self.options['Himage_upscale'].get() == 1:
|
||||||
|
argv.append("--upscale")
|
||||||
|
if self.options['Iimage_stretch'].get() == 1:
|
||||||
|
argv.append("--stretch")
|
||||||
|
if self.options['Jblack_borders'].get() == 1:
|
||||||
|
argv.append("--blackborders")
|
||||||
|
if self.options['Krotate'].get() == 1:
|
||||||
|
argv.append("--rotate")
|
||||||
|
if self.options['Lnosplitrotate'].get() == 1:
|
||||||
|
argv.append("--nosplitrotate")
|
||||||
|
if self.options['Mcut_page_numbers'].get() == 1:
|
||||||
|
argv.append("--nocutpagenumbers")
|
||||||
errors = False
|
errors = False
|
||||||
|
left_files = len(self.filelist)
|
||||||
|
filenum = 0
|
||||||
|
self.progress_overall['value'] = 0
|
||||||
|
self.progress_overall['maximum'] = left_files
|
||||||
for entry in self.filelist:
|
for entry in self.filelist:
|
||||||
|
self.progress_overall['value'] = filenum
|
||||||
|
self.progress_file['value'] = 1
|
||||||
self.master.update()
|
self.master.update()
|
||||||
|
filenum += 1
|
||||||
subargv = list(argv)
|
subargv = list(argv)
|
||||||
try:
|
try:
|
||||||
subargv.append(entry)
|
subargv.append(entry)
|
||||||
epub_path = comic2ebook.main(subargv)
|
epub_path = comic2ebook.main(subargv)
|
||||||
except Exception, err:
|
self.progress_file['value'] = 2
|
||||||
tkMessageBox.showerror('Error comic2ebook', "Error on file %s:\n%s" % (subargv[-1], str(err)))
|
self.master.update()
|
||||||
|
except Exception as err:
|
||||||
|
type_, value_, traceback_ = sys.exc_info()
|
||||||
|
tkMessageBox.showerror('KCC Error', "Error on file %s:\n%s\nTraceback:\n%s" %
|
||||||
|
(subargv[-1], str(err), traceback.format_tb(traceback_)))
|
||||||
errors = True
|
errors = True
|
||||||
continue
|
continue
|
||||||
if self.options['epub_only'] == 1:
|
if self.options['Aepub_only'].get() == 0 and self.options['Bcbz_only'].get() == 0:
|
||||||
continue
|
|
||||||
try:
|
try:
|
||||||
|
if os.path.getsize(epub_path) > 314572800:
|
||||||
|
# do not call kindlegen if source is bigger than 300MB
|
||||||
|
tkMessageBox.showwarning('KindleGen Warning',
|
||||||
|
"ePub file %s is bigger than 300MB, not suitable for kindlegen" %
|
||||||
|
epub_path)
|
||||||
|
continue
|
||||||
retcode = call("kindlegen \"" + epub_path + "\"", shell=True)
|
retcode = call("kindlegen \"" + epub_path + "\"", shell=True)
|
||||||
if retcode < 0:
|
if retcode < 0:
|
||||||
print >>sys.stderr, "Child was terminated by signal", -retcode
|
print >>sys.stderr, "Child was terminated by signal", -retcode
|
||||||
else:
|
else:
|
||||||
print >>sys.stderr, "Child returned", retcode
|
print >>sys.stderr, "Child returned", retcode
|
||||||
|
self.progress_file['value'] = 3
|
||||||
|
self.master.update()
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
tkMessageBox.showerror('Error kindlegen', "Error on file %s:\n%s" % (epub_path, e))
|
tkMessageBox.showerror('KindleGen Error', "Error on file %s:\n%s" % (epub_path, e))
|
||||||
errors = True
|
errors = True
|
||||||
continue
|
continue
|
||||||
mobifile = epub_path.replace('.epub', '.mobi')
|
mobifile = epub_path.replace('.epub', '.mobi')
|
||||||
@@ -172,20 +235,23 @@ class MainWindow:
|
|||||||
shutil.move(mobifile, mobifile + '_tostrip')
|
shutil.move(mobifile, mobifile + '_tostrip')
|
||||||
kindlestrip.main((mobifile + '_tostrip', mobifile))
|
kindlestrip.main((mobifile + '_tostrip', mobifile))
|
||||||
os.remove(mobifile + '_tostrip')
|
os.remove(mobifile + '_tostrip')
|
||||||
|
self.progress_file['value'] = 4
|
||||||
|
self.master.update()
|
||||||
except Exception, err:
|
except Exception, err:
|
||||||
tkMessageBox.showerror('Error', "Error on file %s:\n%s" % (mobifile, str(err)))
|
tkMessageBox.showerror('KindleStrip Error', "Error on file %s:\n%s" % (mobifile, str(err)))
|
||||||
errors = True
|
errors = True
|
||||||
continue
|
continue
|
||||||
if errors:
|
|
||||||
tkMessageBox.showinfo(
|
|
||||||
"Done",
|
|
||||||
"Conversion finished (some errors have been reported)"
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
tkMessageBox.showinfo(
|
self.progress_file['value'] = 4
|
||||||
"Done",
|
self.master.update()
|
||||||
"Conversion successfully done!"
|
if errors:
|
||||||
)
|
tkMessageBox.showwarning("Done", "Conversion completed with errors.")
|
||||||
|
else:
|
||||||
|
tkMessageBox.showinfo("Done", "Conversion successful!")
|
||||||
|
# reset progressbars
|
||||||
|
self.progress_overall['value'] = 0
|
||||||
|
self.progress_file['value'] = 0
|
||||||
|
self.master.update()
|
||||||
|
|
||||||
def remove_readonly(self, fn, path):
|
def remove_readonly(self, fn, path):
|
||||||
if fn is os.rmdir:
|
if fn is os.rmdir:
|
||||||
@@ -195,8 +261,7 @@ class MainWindow:
|
|||||||
os.chmod(path, stat.S_IWRITE)
|
os.chmod(path, stat.S_IWRITE)
|
||||||
os.remove(path)
|
os.remove(path)
|
||||||
|
|
||||||
def __init__(self, master, title):
|
def __init__(self, master):
|
||||||
self.filelist = []
|
self.filelist = []
|
||||||
self.master = master
|
self.master = master
|
||||||
self.master.title(title)
|
|
||||||
self.initialize()
|
self.initialize()
|
||||||
|
|||||||
195
kcc/image.py
195
kcc/image.py
@@ -20,7 +20,7 @@ __copyright__ = '2012-2013, Ciro Mattia Gonano <ciromattia@gmail.com>'
|
|||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
import os
|
import os
|
||||||
from PIL import Image, ImageOps, ImageDraw, ImageStat
|
from PIL import Image, ImageOps, ImageStat
|
||||||
|
|
||||||
|
|
||||||
class ImageFlags:
|
class ImageFlags:
|
||||||
@@ -77,20 +77,22 @@ class ProfileData:
|
|||||||
]
|
]
|
||||||
|
|
||||||
Profiles = {
|
Profiles = {
|
||||||
'K1': ("Kindle", (600, 800), Palette4),
|
'K1': ("Kindle 1", (600, 800), Palette4, 1.8, (900, 1200)),
|
||||||
'K2': ("Kindle 2", (600, 800), Palette15),
|
'K2': ("Kindle 2", (600, 800), Palette15, 1.8, (900, 1200)),
|
||||||
'K3': ("Kindle 3/Keyboard", (600, 800), Palette16),
|
'K3': ("Kindle Keyboard", (600, 800), Palette16, 1.8, (900, 1200)),
|
||||||
'K4': ("Kindle 4/NT/Touch", (600, 800), Palette16),
|
'K4NT': ("Kindle Non-Touch", (600, 800), Palette16, 1.8, (900, 1200)),
|
||||||
'KHD': ("Kindle Paperwhite", (758, 1024), Palette16),
|
'K4T': ("Kindle Touch", (600, 800), Palette16, 1.8, (900, 1200)),
|
||||||
'KDX': ("Kindle DX", (824, 1200), Palette15),
|
'KHD': ("Kindle Paperwhite", (758, 1024), Palette16, 1.8, (1137, 1536)),
|
||||||
'KDXG': ("Kindle DXG", (824, 1200), Palette16)
|
'KDX': ("Kindle DX", (824, 1200), Palette15, 1.8, (1236, 1800)),
|
||||||
|
'KDXG': ("Kindle DXG", (824, 1200), Palette16, 1.8, (1236, 1800))
|
||||||
}
|
}
|
||||||
|
|
||||||
ProfileLabels = {
|
ProfileLabels = {
|
||||||
"Kindle": 'K1',
|
"Kindle 1": 'K1',
|
||||||
"Kindle 2": 'K2',
|
"Kindle 2": 'K2',
|
||||||
"Kindle 3/Keyboard": 'K3',
|
"Kindle 3/Keyboard": 'K3',
|
||||||
"Kindle 4/NT/Touch": 'K4',
|
"Kindle 4/Non-Touch": 'K4NT',
|
||||||
|
"Kindle 4/Touch": 'K4T',
|
||||||
"Kindle Paperwhite": 'KHD',
|
"Kindle Paperwhite": 'KHD',
|
||||||
"Kindle DX": 'KDX',
|
"Kindle DX": 'KDX',
|
||||||
"Kindle DXG": 'KDXG'
|
"Kindle DXG": 'KDXG'
|
||||||
@@ -101,7 +103,7 @@ class ComicPage:
|
|||||||
def __init__(self, source, device):
|
def __init__(self, source, device):
|
||||||
try:
|
try:
|
||||||
self.profile = device
|
self.profile = device
|
||||||
self.profile_label, self.size, self.palette = ProfileData.Profiles[device]
|
self.profile_label, self.size, self.palette, self.gamma, self.panelviewsize = ProfileData.Profiles[device]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise RuntimeError('Unexpected output device %s' % device)
|
raise RuntimeError('Unexpected output device %s' % device)
|
||||||
try:
|
try:
|
||||||
@@ -111,18 +113,29 @@ class ComicPage:
|
|||||||
raise RuntimeError('Cannot read image file %s' % source)
|
raise RuntimeError('Cannot read image file %s' % source)
|
||||||
self.image = self.image.convert('RGB')
|
self.image = self.image.convert('RGB')
|
||||||
|
|
||||||
def saveToDir(self, targetdir):
|
def saveToDir(self, targetdir, forcepng):
|
||||||
filename = os.path.basename(self.origFileName)
|
filename = os.path.basename(self.origFileName)
|
||||||
try:
|
try:
|
||||||
self.image = self.image.convert('L') # convert to grayscale
|
self.image = self.image.convert('L') # convert to grayscale
|
||||||
self.image.save(os.path.join(targetdir, filename), "JPEG")
|
os.remove(os.path.join(targetdir, filename))
|
||||||
|
if forcepng:
|
||||||
|
self.image.save(os.path.join(targetdir, os.path.splitext(filename)[0] + ".png"), "PNG")
|
||||||
|
else:
|
||||||
|
self.image.save(os.path.join(targetdir, os.path.splitext(filename)[0] + ".jpg"), "JPEG")
|
||||||
except IOError as e:
|
except IOError as e:
|
||||||
raise RuntimeError('Cannot write image in directory %s: %s' % (targetdir, e))
|
raise RuntimeError('Cannot write image in directory %s: %s' % (targetdir, e))
|
||||||
|
|
||||||
def optimizeImage(self):
|
def optimizeImage(self, gamma):
|
||||||
|
if gamma < 0.1:
|
||||||
|
gamma = self.gamma
|
||||||
|
if gamma == 1.0:
|
||||||
self.image = ImageOps.autocontrast(self.image)
|
self.image = ImageOps.autocontrast(self.image)
|
||||||
|
else:
|
||||||
|
self.image = ImageOps.autocontrast(Image.eval(self.image, lambda a: 255 * (a / 255.) ** gamma))
|
||||||
|
|
||||||
def quantizeImage(self):
|
def quantizeImage(self):
|
||||||
|
self.image = self.image.convert('L') # convert to grayscale
|
||||||
|
self.image = self.image.convert("RGB") # convert back to RGB
|
||||||
colors = len(self.palette) / 3
|
colors = len(self.palette) / 3
|
||||||
if colors < 256:
|
if colors < 256:
|
||||||
self.palette += self.palette[:3] * (256 - colors)
|
self.palette += self.palette[:3] * (256 - colors)
|
||||||
@@ -130,46 +143,48 @@ class ComicPage:
|
|||||||
palImg.putpalette(self.palette)
|
palImg.putpalette(self.palette)
|
||||||
self.image = self.image.quantize(palette=palImg)
|
self.image = self.image.quantize(palette=palImg)
|
||||||
|
|
||||||
def resizeImage(self, upscale=False, stretch=False, black_borders=False, isSplit=False, toRight=False):
|
def resizeImage(self, upscale=False, stretch=False, black_borders=False, isSplit=False, toRight=False,
|
||||||
|
landscapeMode=False, noPanelViewHQ=False):
|
||||||
method = Image.ANTIALIAS
|
method = Image.ANTIALIAS
|
||||||
if black_borders:
|
if black_borders:
|
||||||
fill = 'black'
|
fill = 'black'
|
||||||
else:
|
else:
|
||||||
fill = 'white'
|
fill = 'white'
|
||||||
|
if noPanelViewHQ:
|
||||||
|
size = (self.size[0], self.size[1])
|
||||||
|
else:
|
||||||
|
size = (self.panelviewsize[0], self.panelviewsize[1])
|
||||||
|
if isSplit and landscapeMode:
|
||||||
|
upscale = True
|
||||||
if self.image.size[0] <= self.size[0] and self.image.size[1] <= self.size[1]:
|
if self.image.size[0] <= self.size[0] and self.image.size[1] <= self.size[1]:
|
||||||
if not upscale:
|
if not upscale:
|
||||||
if isSplit and (self.profile == 'K4' or self.profile == 'KHD'):
|
|
||||||
borderw = (self.size[0] - self.image.size[0])
|
|
||||||
borderh = (self.size[1] - self.image.size[1]) / 2
|
|
||||||
self.image = ImageOps.expand(self.image, border=(0, borderh), fill=fill)
|
|
||||||
tempImg = Image.new(self.image.mode, (self.image.size[0] + borderw, self.image.size[1]), fill)
|
|
||||||
if toRight:
|
|
||||||
tempImg.paste(self.image, (borderw, 0))
|
|
||||||
else:
|
|
||||||
tempImg.paste(self.image, (0, 0))
|
|
||||||
self.image = tempImg
|
|
||||||
else:
|
|
||||||
borderw = (self.size[0] - self.image.size[0]) / 2
|
borderw = (self.size[0] - self.image.size[0]) / 2
|
||||||
borderh = (self.size[1] - self.image.size[1]) / 2
|
borderh = (self.size[1] - self.image.size[1]) / 2
|
||||||
self.image = ImageOps.expand(self.image, border=(borderw, borderh), fill=fill)
|
self.image = ImageOps.expand(self.image, border=(borderw, borderh), fill=fill)
|
||||||
return self.image
|
return self.image
|
||||||
else:
|
else:
|
||||||
method = Image.NEAREST
|
method = Image.BILINEAR
|
||||||
|
|
||||||
if stretch: # if stretching call directly resize() without other considerations.
|
if stretch: # if stretching call directly resize() without other considerations.
|
||||||
self.image = self.image.resize(self.size, method)
|
self.image = self.image.resize(size, method)
|
||||||
return self.image
|
return self.image
|
||||||
|
|
||||||
ratioDev = float(self.size[0]) / float(self.size[1])
|
ratioDev = float(self.size[0]) / float(self.size[1])
|
||||||
if (float(self.image.size[0]) / float(self.image.size[1])) < ratioDev:
|
if (float(self.image.size[0]) / float(self.image.size[1])) < ratioDev:
|
||||||
|
if isSplit and landscapeMode:
|
||||||
|
diff = int(self.image.size[1] * ratioDev) - self.image.size[0]
|
||||||
|
self.image = ImageOps.expand(self.image, border=(diff / 2, 0), fill=fill)
|
||||||
|
tempImg = Image.new(self.image.mode, (self.image.size[0] + diff, self.image.size[1]), fill)
|
||||||
|
if toRight:
|
||||||
|
tempImg.paste(self.image, (diff, 0))
|
||||||
|
else:
|
||||||
|
tempImg.paste(self.image, (0, 0))
|
||||||
|
self.image = tempImg
|
||||||
|
else:
|
||||||
diff = int(self.image.size[1] * ratioDev) - self.image.size[0]
|
diff = int(self.image.size[1] * ratioDev) - self.image.size[0]
|
||||||
if isSplit and (self.profile == 'K4' or self.profile == 'KHD'):
|
|
||||||
diff = 2
|
|
||||||
self.image = ImageOps.expand(self.image, border=(diff / 2, 0), fill=fill)
|
self.image = ImageOps.expand(self.image, border=(diff / 2, 0), fill=fill)
|
||||||
elif (float(self.image.size[0]) / float(self.image.size[1])) > ratioDev:
|
elif (float(self.image.size[0]) / float(self.image.size[1])) > ratioDev:
|
||||||
diff = int(self.image.size[0] / ratioDev) - self.image.size[1]
|
diff = int(self.image.size[0] / ratioDev) - self.image.size[1]
|
||||||
self.image = ImageOps.expand(self.image, border=(0, diff / 2), fill=fill)
|
self.image = ImageOps.expand(self.image, border=(0, diff / 2), fill=fill)
|
||||||
self.image = ImageOps.fit(self.image, self.size, method=method, centering=(0.5, 0.5))
|
self.image = ImageOps.fit(self.image, size, method=method, centering=(0.5, 0.5))
|
||||||
return self.image
|
return self.image
|
||||||
|
|
||||||
def splitPage(self, targetdir, righttoleft=False, rotate=False):
|
def splitPage(self, targetdir, righttoleft=False, rotate=False):
|
||||||
@@ -209,29 +224,6 @@ class ComicPage:
|
|||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def frameImage(self):
|
|
||||||
foreground = tuple(self.palette[:3])
|
|
||||||
background = tuple(self.palette[-3:])
|
|
||||||
widthDev, heightDev = self.size
|
|
||||||
widthImg, heightImg = self.image.size
|
|
||||||
pastePt = (
|
|
||||||
max(0, (widthDev - widthImg) / 2),
|
|
||||||
max(0, (heightDev - heightImg) / 2)
|
|
||||||
)
|
|
||||||
corner1 = (
|
|
||||||
pastePt[0] - 1,
|
|
||||||
pastePt[1] - 1
|
|
||||||
)
|
|
||||||
corner2 = (
|
|
||||||
pastePt[0] + widthImg + 1,
|
|
||||||
pastePt[1] + heightImg + 1
|
|
||||||
)
|
|
||||||
imageBg = Image.new(self.image.mode, self.size, background)
|
|
||||||
imageBg.paste(self.image, pastePt)
|
|
||||||
draw = ImageDraw.Draw(imageBg)
|
|
||||||
draw.rectangle([corner1, corner2], outline=foreground)
|
|
||||||
self.image = imageBg
|
|
||||||
|
|
||||||
def cutPageNumber(self):
|
def cutPageNumber(self):
|
||||||
widthImg, heightImg = self.image.size
|
widthImg, heightImg = self.image.size
|
||||||
delta = 2
|
delta = 2
|
||||||
@@ -324,37 +316,60 @@ class ComicPage:
|
|||||||
# print "New size: %sx%s"%(self.image.size[0],self.image.size[1])
|
# print "New size: %sx%s"%(self.image.size[0],self.image.size[1])
|
||||||
return self.image
|
return self.image
|
||||||
|
|
||||||
def addProgressbar(self, file_number, files_totalnumber, size, howoften):
|
# def addProgressbar(self, file_number, files_totalnumber, size, howoften):
|
||||||
if file_number // howoften != float(file_number) / howoften:
|
# if file_number // howoften != float(file_number) / howoften:
|
||||||
return self.image
|
# return self.image
|
||||||
white = (255, 255, 255)
|
# white = (255, 255, 255)
|
||||||
black = (0, 0, 0)
|
# black = (0, 0, 0)
|
||||||
widthDev, heightDev = size
|
# widthDev, heightDev = size
|
||||||
widthImg, heightImg = self.image.size
|
# widthImg, heightImg = self.image.size
|
||||||
pastePt = (
|
# pastePt = (
|
||||||
max(0, (widthDev - widthImg) / 2),
|
# max(0, (widthDev - widthImg) / 2),
|
||||||
max(0, (heightDev - heightImg) / 2)
|
# max(0, (heightDev - heightImg) / 2)
|
||||||
)
|
# )
|
||||||
imageBg = Image.new('RGB', size, white)
|
# imageBg = Image.new('RGB', size, white)
|
||||||
imageBg.paste(self.image, pastePt)
|
# imageBg.paste(self.image, pastePt)
|
||||||
self.image = imageBg
|
# self.image = imageBg
|
||||||
widthImg, heightImg = self.image.size
|
# widthImg, heightImg = self.image.size
|
||||||
draw = ImageDraw.Draw(self.image)
|
# draw = ImageDraw.Draw(self.image)
|
||||||
#Black rectangle
|
# #Black rectangle
|
||||||
draw.rectangle([(0, heightImg - 3), (widthImg, heightImg)], outline=black, fill=black)
|
# draw.rectangle([(0, heightImg - 3), (widthImg, heightImg)], outline=black, fill=black)
|
||||||
#White rectangle
|
# #White rectangle
|
||||||
draw.rectangle([(widthImg * file_number / files_totalnumber, heightImg - 3), (widthImg - 1, heightImg)],
|
# draw.rectangle([(widthImg * file_number / files_totalnumber, heightImg - 3), (widthImg - 1, heightImg)],
|
||||||
outline=black, fill=white)
|
# outline=black, fill=white)
|
||||||
#Making notches
|
# #Making notches
|
||||||
for i in range(1, 10):
|
# for i in range(1, 10):
|
||||||
if i <= (10 * file_number / files_totalnumber):
|
# if i <= (10 * file_number / files_totalnumber):
|
||||||
notch_colour = white # White
|
# notch_colour = white # White
|
||||||
else:
|
# else:
|
||||||
notch_colour = black # Black
|
# notch_colour = black # Black
|
||||||
draw.line([(widthImg * float(i) / 10, heightImg - 3), (widthImg * float(i) / 10, heightImg)],
|
# draw.line([(widthImg * float(i) / 10, heightImg - 3), (widthImg * float(i) / 10, heightImg)],
|
||||||
fill=notch_colour)
|
# fill=notch_colour)
|
||||||
#The 50%
|
# #The 50%
|
||||||
if i == 5:
|
# if i == 5:
|
||||||
draw.rectangle([(widthImg / 2 - 1, heightImg - 5), (widthImg / 2 + 1, heightImg)],
|
# draw.rectangle([(widthImg / 2 - 1, heightImg - 5), (widthImg / 2 + 1, heightImg)],
|
||||||
outline=black, fill=notch_colour)
|
# outline=black, fill=notch_colour)
|
||||||
return self.image
|
# return self.image
|
||||||
|
#
|
||||||
|
# def frameImage(self):
|
||||||
|
# foreground = tuple(self.palette[:3])
|
||||||
|
# background = tuple(self.palette[-3:])
|
||||||
|
# widthDev, heightDev = self.size
|
||||||
|
# widthImg, heightImg = self.image.size
|
||||||
|
# pastePt = (
|
||||||
|
# max(0, (widthDev - widthImg) / 2),
|
||||||
|
# max(0, (heightDev - heightImg) / 2)
|
||||||
|
# )
|
||||||
|
# corner1 = (
|
||||||
|
# pastePt[0] - 1,
|
||||||
|
# pastePt[1] - 1
|
||||||
|
# )
|
||||||
|
# corner2 = (
|
||||||
|
# pastePt[0] + widthImg + 1,
|
||||||
|
# pastePt[1] + heightImg + 1
|
||||||
|
# )
|
||||||
|
# imageBg = Image.new(self.image.mode, self.size, background)
|
||||||
|
# imageBg.paste(self.image, pastePt)
|
||||||
|
# draw = ImageDraw.Draw(imageBg)
|
||||||
|
# draw.rectangle([corner1, corner2], outline=foreground)
|
||||||
|
# self.image = imageBg
|
||||||
|
|||||||
@@ -70,3 +70,4 @@ class PdfJpgExtract:
|
|||||||
|
|
||||||
njpg += 1
|
njpg += 1
|
||||||
i = iend
|
i = iend
|
||||||
|
return self.path
|
||||||
|
|||||||
237
kcc/rarfile.py
237
kcc/rarfile.py
@@ -1,6 +1,6 @@
|
|||||||
# rarfile.py
|
# rarfile.py
|
||||||
#
|
#
|
||||||
# Copyright (c) 2005-2012 Marko Kreen <markokr@gmail.com>
|
# Copyright (c) 2005-2013 Marko Kreen <markokr@gmail.com>
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and/or distribute this software for any
|
# Permission to use, copy, modify, and/or distribute this software for any
|
||||||
# purpose with or without fee is hereby granted, provided that the above
|
# purpose with or without fee is hereby granted, provided that the above
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
r"""RAR archive reader.
|
r"""RAR archive reader.
|
||||||
|
|
||||||
This is Python module for Rar archive reading. The interface
|
This is Python module for Rar archive reading. The interface
|
||||||
is made as zipfile like as possible.
|
is made as :mod:`zipfile`-like as possible.
|
||||||
|
|
||||||
Basic logic:
|
Basic logic:
|
||||||
- Parse archive structure with Python.
|
- Parse archive structure with Python.
|
||||||
@@ -34,7 +34,17 @@ Example::
|
|||||||
for f in rf.infolist():
|
for f in rf.infolist():
|
||||||
print f.filename, f.file_size
|
print f.filename, f.file_size
|
||||||
if f.filename == 'README':
|
if f.filename == 'README':
|
||||||
print rf.read(f)
|
print(rf.read(f))
|
||||||
|
|
||||||
|
Archive files can also be accessed via file-like object returned
|
||||||
|
by :meth:`RarFile.open`::
|
||||||
|
|
||||||
|
import rarfile
|
||||||
|
|
||||||
|
with rarfile.RarFile('archive.rar') as rf:
|
||||||
|
with rf.open('README') as f:
|
||||||
|
for ln in f:
|
||||||
|
print(ln.strip())
|
||||||
|
|
||||||
There are few module-level parameters to tune behaviour,
|
There are few module-level parameters to tune behaviour,
|
||||||
here they are with defaults, and reason to change it::
|
here they are with defaults, and reason to change it::
|
||||||
@@ -64,7 +74,7 @@ For more details, refer to source.
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__version__ = '2.5'
|
__version__ = '2.6'
|
||||||
|
|
||||||
# export only interesting items
|
# export only interesting items
|
||||||
__all__ = ['is_rarfile', 'RarInfo', 'RarFile', 'RarExtFile']
|
__all__ = ['is_rarfile', 'RarInfo', 'RarFile', 'RarExtFile']
|
||||||
@@ -73,7 +83,7 @@ __all__ = ['is_rarfile', 'RarInfo', 'RarFile', 'RarExtFile']
|
|||||||
## Imports and compat - support both Python 2.x and 3.x
|
## Imports and compat - support both Python 2.x and 3.x
|
||||||
##
|
##
|
||||||
|
|
||||||
import sys, os, struct
|
import sys, os, struct, errno
|
||||||
from struct import pack, unpack
|
from struct import pack, unpack
|
||||||
from binascii import crc32
|
from binascii import crc32
|
||||||
from tempfile import mkstemp
|
from tempfile import mkstemp
|
||||||
@@ -148,45 +158,45 @@ except ImportError:
|
|||||||
## Module configuration. Can be tuned after importing.
|
## Module configuration. Can be tuned after importing.
|
||||||
##
|
##
|
||||||
|
|
||||||
# default fallback charset
|
#: default fallback charset
|
||||||
DEFAULT_CHARSET = "windows-1252"
|
DEFAULT_CHARSET = "windows-1252"
|
||||||
|
|
||||||
# list of encodings to try, with fallback to DEFAULT_CHARSET if none succeed
|
#: list of encodings to try, with fallback to DEFAULT_CHARSET if none succeed
|
||||||
TRY_ENCODINGS = ('utf8', 'utf-16le')
|
TRY_ENCODINGS = ('utf8', 'utf-16le')
|
||||||
|
|
||||||
# 'unrar', 'rar' or full path to either one
|
#: 'unrar', 'rar' or full path to either one
|
||||||
UNRAR_TOOL = "unrar"
|
UNRAR_TOOL = "unrar"
|
||||||
|
|
||||||
# Command line args to use for opening file for reading.
|
#: Command line args to use for opening file for reading.
|
||||||
OPEN_ARGS = ('p', '-inul')
|
OPEN_ARGS = ('p', '-inul')
|
||||||
|
|
||||||
# Command line args to use for extracting file to disk.
|
#: Command line args to use for extracting file to disk.
|
||||||
EXTRACT_ARGS = ('x', '-y', '-idq')
|
EXTRACT_ARGS = ('x', '-y', '-idq')
|
||||||
|
|
||||||
# args for testrar()
|
#: args for testrar()
|
||||||
TEST_ARGS = ('t', '-idq')
|
TEST_ARGS = ('t', '-idq')
|
||||||
|
|
||||||
# whether to speed up decompression by using tmp archive
|
#: whether to speed up decompression by using tmp archive
|
||||||
USE_EXTRACT_HACK = 1
|
USE_EXTRACT_HACK = 1
|
||||||
|
|
||||||
# limit the filesize for tmp archive usage
|
#: limit the filesize for tmp archive usage
|
||||||
HACK_SIZE_LIMIT = 20*1024*1024
|
HACK_SIZE_LIMIT = 20*1024*1024
|
||||||
|
|
||||||
# whether to parse file/archive comments.
|
#: whether to parse file/archive comments.
|
||||||
NEED_COMMENTS = 1
|
NEED_COMMENTS = 1
|
||||||
|
|
||||||
# whether to convert comments to unicode strings
|
#: whether to convert comments to unicode strings
|
||||||
UNICODE_COMMENTS = 0
|
UNICODE_COMMENTS = 0
|
||||||
|
|
||||||
# When RAR is corrupt, stopping on bad header is better
|
#: When RAR is corrupt, stopping on bad header is better
|
||||||
# On unknown/misparsed RAR headers reporting is better
|
#: On unknown/misparsed RAR headers reporting is better
|
||||||
REPORT_BAD_HEADER = 0
|
REPORT_BAD_HEADER = 0
|
||||||
|
|
||||||
# Convert RAR time tuple into datetime() object
|
#: Convert RAR time tuple into datetime() object
|
||||||
USE_DATETIME = 0
|
USE_DATETIME = 0
|
||||||
|
|
||||||
# Separator for path name components. RAR internally uses '\\'.
|
#: Separator for path name components. RAR internally uses '\\'.
|
||||||
# Use '/' to be similar with zipfile.
|
#: Use '/' to be similar with zipfile.
|
||||||
PATH_SEP = '\\'
|
PATH_SEP = '\\'
|
||||||
|
|
||||||
##
|
##
|
||||||
@@ -320,6 +330,8 @@ class RarMemoryError(RarExecError):
|
|||||||
"""Memory error"""
|
"""Memory error"""
|
||||||
class RarCreateError(RarExecError):
|
class RarCreateError(RarExecError):
|
||||||
"""Create error"""
|
"""Create error"""
|
||||||
|
class RarNoFilesError(RarExecError):
|
||||||
|
"""No files that match pattern were found"""
|
||||||
class RarUserBreak(RarExecError):
|
class RarUserBreak(RarExecError):
|
||||||
"""User stop"""
|
"""User stop"""
|
||||||
class RarUnknownError(RarExecError):
|
class RarUnknownError(RarExecError):
|
||||||
@@ -335,49 +347,57 @@ def is_rarfile(fn):
|
|||||||
|
|
||||||
|
|
||||||
class RarInfo(object):
|
class RarInfo(object):
|
||||||
'''An entry in rar archive.
|
r'''An entry in rar archive.
|
||||||
|
|
||||||
@ivar filename:
|
:mod:`zipfile`-compatible fields:
|
||||||
|
|
||||||
|
filename
|
||||||
File name with relative path.
|
File name with relative path.
|
||||||
Default path separator is '/', to change set rarfile.PATH_SEP.
|
Default path separator is '\\', to change set rarfile.PATH_SEP.
|
||||||
Always unicode string.
|
Always unicode string.
|
||||||
@ivar date_time:
|
date_time
|
||||||
Modification time, tuple of (year, month, day, hour, minute, second).
|
Modification time, tuple of (year, month, day, hour, minute, second).
|
||||||
Or datetime() object if USE_DATETIME is set.
|
Or datetime() object if USE_DATETIME is set.
|
||||||
@ivar file_size:
|
file_size
|
||||||
Uncompressed size.
|
Uncompressed size.
|
||||||
@ivar compress_size:
|
compress_size
|
||||||
Compressed size.
|
Compressed size.
|
||||||
@ivar compress_type:
|
CRC
|
||||||
Compression method: 0x30 - 0x35.
|
|
||||||
@ivar extract_version:
|
|
||||||
Minimal Rar version needed for decompressing.
|
|
||||||
@ivar host_os:
|
|
||||||
Host OS type, one of RAR_OS_* constants.
|
|
||||||
@ivar mode:
|
|
||||||
File attributes. May be either dos-style or unix-style, depending on host_os.
|
|
||||||
@ivar CRC:
|
|
||||||
CRC-32 of uncompressed file, unsigned int.
|
CRC-32 of uncompressed file, unsigned int.
|
||||||
@ivar volume:
|
comment
|
||||||
|
File comment. Byte string or None. Use UNICODE_COMMENTS
|
||||||
|
to get automatic decoding to unicode.
|
||||||
|
volume
|
||||||
Volume nr, starting from 0.
|
Volume nr, starting from 0.
|
||||||
@ivar volume_file:
|
|
||||||
Volume file name, where file starts.
|
|
||||||
@ivar type:
|
|
||||||
One of RAR_BLOCK_* types. Only entries with type==RAR_BLOCK_FILE are shown in .infolist().
|
|
||||||
@ivar flags:
|
|
||||||
For files, RAR_FILE_* bits.
|
|
||||||
@ivar comment:
|
|
||||||
File comment (unicode string or None).
|
|
||||||
|
|
||||||
@ivar mtime:
|
RAR-specific fields:
|
||||||
|
|
||||||
|
compress_type
|
||||||
|
Compression method: 0x30 - 0x35.
|
||||||
|
extract_version
|
||||||
|
Minimal Rar version needed for decompressing.
|
||||||
|
host_os
|
||||||
|
Host OS type, one of RAR_OS_* constants.
|
||||||
|
mode
|
||||||
|
File attributes. May be either dos-style or unix-style, depending on host_os.
|
||||||
|
volume_file
|
||||||
|
Volume file name, where file starts.
|
||||||
|
mtime
|
||||||
Optional time field: Modification time, with float seconds.
|
Optional time field: Modification time, with float seconds.
|
||||||
Same as .date_time but with more precision.
|
Same as .date_time but with more precision.
|
||||||
@ivar ctime:
|
ctime
|
||||||
Optional time field: creation time, with float seconds.
|
Optional time field: creation time, with float seconds.
|
||||||
@ivar atime:
|
atime
|
||||||
Optional time field: last access time, with float seconds.
|
Optional time field: last access time, with float seconds.
|
||||||
@ivar arctime:
|
arctime
|
||||||
Optional time field: archival time, with float seconds.
|
Optional time field: archival time, with float seconds.
|
||||||
|
|
||||||
|
Internal fields:
|
||||||
|
|
||||||
|
type
|
||||||
|
One of RAR_BLOCK_* types. Only entries with type==RAR_BLOCK_FILE are shown in .infolist().
|
||||||
|
flags
|
||||||
|
For files, RAR_FILE_* bits.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
__slots__ = (
|
__slots__ = (
|
||||||
@@ -431,19 +451,27 @@ class RarInfo(object):
|
|||||||
|
|
||||||
class RarFile(object):
|
class RarFile(object):
|
||||||
'''Parse RAR structure, provide access to files in archive.
|
'''Parse RAR structure, provide access to files in archive.
|
||||||
|
|
||||||
@ivar comment:
|
|
||||||
Archive comment (unicode string or None).
|
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
#: Archive comment. Byte string or None. Use UNICODE_COMMENTS
|
||||||
|
#: to get automatic decoding to unicode.
|
||||||
|
comment = None
|
||||||
|
|
||||||
def __init__(self, rarfile, mode="r", charset=None, info_callback=None, crc_check = True):
|
def __init__(self, rarfile, mode="r", charset=None, info_callback=None, crc_check = True):
|
||||||
"""Open and parse a RAR archive.
|
"""Open and parse a RAR archive.
|
||||||
|
|
||||||
@param rarfile: archive file name
|
Parameters:
|
||||||
@param mode: only 'r' is supported.
|
|
||||||
@param charset: fallback charset to use, if filenames are not already Unicode-enabled.
|
rarfile
|
||||||
@param info_callback: debug callback, gets to see all archive entries.
|
archive file name
|
||||||
@param crc_check: set to False to disable CRC checks
|
mode
|
||||||
|
only 'r' is supported.
|
||||||
|
charset
|
||||||
|
fallback charset to use, if filenames are not already Unicode-enabled.
|
||||||
|
info_callback
|
||||||
|
debug callback, gets to see all archive entries.
|
||||||
|
crc_check
|
||||||
|
set to False to disable CRC checks
|
||||||
"""
|
"""
|
||||||
self.rarfile = rarfile
|
self.rarfile = rarfile
|
||||||
self.comment = None
|
self.comment = None
|
||||||
@@ -455,6 +483,7 @@ class RarFile(object):
|
|||||||
self._needs_password = False
|
self._needs_password = False
|
||||||
self._password = None
|
self._password = None
|
||||||
self._crc_check = crc_check
|
self._crc_check = crc_check
|
||||||
|
self._vol_list = []
|
||||||
|
|
||||||
self._main = None
|
self._main = None
|
||||||
|
|
||||||
@@ -487,6 +516,14 @@ class RarFile(object):
|
|||||||
'''Return RarInfo objects for all files/directories in archive.'''
|
'''Return RarInfo objects for all files/directories in archive.'''
|
||||||
return self._info_list
|
return self._info_list
|
||||||
|
|
||||||
|
def volumelist(self):
|
||||||
|
'''Returns filenames of archive volumes.
|
||||||
|
|
||||||
|
In case of single-volume archive, the list contains
|
||||||
|
just the name of main archive file.
|
||||||
|
'''
|
||||||
|
return self._vol_list
|
||||||
|
|
||||||
def getinfo(self, fname):
|
def getinfo(self, fname):
|
||||||
'''Return RarInfo for file.'''
|
'''Return RarInfo for file.'''
|
||||||
|
|
||||||
@@ -508,7 +545,8 @@ class RarFile(object):
|
|||||||
raise NoRarEntry("No such file: "+fname)
|
raise NoRarEntry("No such file: "+fname)
|
||||||
|
|
||||||
def open(self, fname, mode = 'r', psw = None):
|
def open(self, fname, mode = 'r', psw = None):
|
||||||
'''Return open file object, where the data can be read.
|
'''Returns file-like object (:class:`RarExtFile`),
|
||||||
|
from where the data can be read.
|
||||||
|
|
||||||
The object implements io.RawIOBase interface, so it can
|
The object implements io.RawIOBase interface, so it can
|
||||||
be further wrapped with io.BufferedReader and io.TextIOWrapper.
|
be further wrapped with io.BufferedReader and io.TextIOWrapper.
|
||||||
@@ -520,9 +558,14 @@ class RarFile(object):
|
|||||||
uncompressed files, on compressed files the seeking is implemented
|
uncompressed files, on compressed files the seeking is implemented
|
||||||
by reading ahead and/or restarting the decompression.
|
by reading ahead and/or restarting the decompression.
|
||||||
|
|
||||||
@param fname: file name or RarInfo instance.
|
Parameters:
|
||||||
@param mode: must be 'r'
|
|
||||||
@param psw: password to use for extracting.
|
fname
|
||||||
|
file name or RarInfo instance.
|
||||||
|
mode
|
||||||
|
must be 'r'
|
||||||
|
psw
|
||||||
|
password to use for extracting.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
if mode != 'r':
|
if mode != 'r':
|
||||||
@@ -569,8 +612,12 @@ class RarFile(object):
|
|||||||
|
|
||||||
For longer files using .open() may be better idea.
|
For longer files using .open() may be better idea.
|
||||||
|
|
||||||
@param fname: filename or RarInfo instance
|
Parameters:
|
||||||
@param psw: password to use for extracting.
|
|
||||||
|
fname
|
||||||
|
filename or RarInfo instance
|
||||||
|
psw
|
||||||
|
password to use for extracting.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
f = self.open(fname, 'r', psw)
|
f = self.open(fname, 'r', psw)
|
||||||
@@ -591,9 +638,14 @@ class RarFile(object):
|
|||||||
def extract(self, member, path=None, pwd=None):
|
def extract(self, member, path=None, pwd=None):
|
||||||
"""Extract single file into current directory.
|
"""Extract single file into current directory.
|
||||||
|
|
||||||
@param member: filename or RarInfo instance
|
Parameters:
|
||||||
@param path: optional destination path
|
|
||||||
@param pwd: optional password to use
|
member
|
||||||
|
filename or RarInfo instance
|
||||||
|
path
|
||||||
|
optional destination path
|
||||||
|
pwd
|
||||||
|
optional password to use
|
||||||
"""
|
"""
|
||||||
if isinstance(member, RarInfo):
|
if isinstance(member, RarInfo):
|
||||||
fname = member.filename
|
fname = member.filename
|
||||||
@@ -604,9 +656,14 @@ class RarFile(object):
|
|||||||
def extractall(self, path=None, members=None, pwd=None):
|
def extractall(self, path=None, members=None, pwd=None):
|
||||||
"""Extract all files into current directory.
|
"""Extract all files into current directory.
|
||||||
|
|
||||||
@param path: optional destination path
|
Parameters:
|
||||||
@param members: optional filename or RarInfo instance list to extract
|
|
||||||
@param pwd: optional password to use
|
path
|
||||||
|
optional destination path
|
||||||
|
members
|
||||||
|
optional filename or RarInfo instance list to extract
|
||||||
|
pwd
|
||||||
|
optional password to use
|
||||||
"""
|
"""
|
||||||
fnlist = []
|
fnlist = []
|
||||||
if members is not None:
|
if members is not None:
|
||||||
@@ -691,6 +748,7 @@ class RarFile(object):
|
|||||||
more_vols = 0
|
more_vols = 0
|
||||||
endarc = 0
|
endarc = 0
|
||||||
volfile = self.rarfile
|
volfile = self.rarfile
|
||||||
|
self._vol_list = [self.rarfile]
|
||||||
while 1:
|
while 1:
|
||||||
if endarc:
|
if endarc:
|
||||||
h = None # don't read past ENDARC
|
h = None # don't read past ENDARC
|
||||||
@@ -705,6 +763,7 @@ class RarFile(object):
|
|||||||
self._fd = fd
|
self._fd = fd
|
||||||
more_vols = 0
|
more_vols = 0
|
||||||
endarc = 0
|
endarc = 0
|
||||||
|
self._vol_list.append(volfile)
|
||||||
continue
|
continue
|
||||||
break
|
break
|
||||||
h.volume = volume
|
h.volume = volume
|
||||||
@@ -1208,7 +1267,7 @@ class UnicodeFilename:
|
|||||||
|
|
||||||
|
|
||||||
class RarExtFile(RawIOBase):
|
class RarExtFile(RawIOBase):
|
||||||
"""Base class for 'file-like' object that RarFile.open() returns.
|
"""Base class for file-like object that :meth:`RarFile.open` returns.
|
||||||
|
|
||||||
Provides public methods and common crc checking.
|
Provides public methods and common crc checking.
|
||||||
|
|
||||||
@@ -1216,13 +1275,15 @@ class RarExtFile(RawIOBase):
|
|||||||
- no short reads - .read() and .readinfo() read as much as requested.
|
- no short reads - .read() and .readinfo() read as much as requested.
|
||||||
- no internal buffer, use io.BufferedReader for that.
|
- no internal buffer, use io.BufferedReader for that.
|
||||||
|
|
||||||
@ivar name:
|
If :mod:`io` module is available (Python 2.6+, 3.x), then this calls
|
||||||
filename of the archive entry.
|
will inherit from :class:`io.RawIOBase` class. This makes line-based
|
||||||
|
access available: :meth:`RarExtFile.readline` and ``for ln in f``.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, rf, inf):
|
#: Filename of the archive entry
|
||||||
"""Fill common fields"""
|
name = None
|
||||||
|
|
||||||
|
def __init__(self, rf, inf):
|
||||||
RawIOBase.__init__(self)
|
RawIOBase.__init__(self)
|
||||||
|
|
||||||
# standard io.* properties
|
# standard io.* properties
|
||||||
@@ -1323,7 +1384,13 @@ class RarExtFile(RawIOBase):
|
|||||||
return self.inf.file_size - self.remain
|
return self.inf.file_size - self.remain
|
||||||
|
|
||||||
def seek(self, ofs, whence = 0):
|
def seek(self, ofs, whence = 0):
|
||||||
"""Seek in data."""
|
"""Seek in data.
|
||||||
|
|
||||||
|
On uncompressed files, the seeking works by actual
|
||||||
|
seeks so it's fast. On compresses files its slow
|
||||||
|
- forward seeking happends by reading ahead,
|
||||||
|
backwards by re-opening and decompressing from the start.
|
||||||
|
"""
|
||||||
|
|
||||||
# disable crc check when seeking
|
# disable crc check when seeking
|
||||||
self.crc_check = 0
|
self.crc_check = 0
|
||||||
@@ -1372,8 +1439,17 @@ class RarExtFile(RawIOBase):
|
|||||||
"""Returns True"""
|
"""Returns True"""
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def writable(self):
|
||||||
|
"""Returns False.
|
||||||
|
|
||||||
|
Writing is not supported."""
|
||||||
|
return False
|
||||||
|
|
||||||
def seekable(self):
|
def seekable(self):
|
||||||
"""Returns True"""
|
"""Returns True.
|
||||||
|
|
||||||
|
Seeking is supported, although it's slow on compressed files.
|
||||||
|
"""
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def readall(self):
|
def readall(self):
|
||||||
@@ -1757,8 +1833,15 @@ def custom_popen(cmd):
|
|||||||
creationflags = 0x08000000 # CREATE_NO_WINDOW
|
creationflags = 0x08000000 # CREATE_NO_WINDOW
|
||||||
|
|
||||||
# run command
|
# run command
|
||||||
p = Popen(cmd, bufsize = 0, stdout = PIPE, stdin = PIPE, stderr = STDOUT,
|
try:
|
||||||
|
p = Popen(cmd, bufsize = 0,
|
||||||
|
stdout = PIPE, stdin = PIPE, stderr = STDOUT,
|
||||||
creationflags = creationflags)
|
creationflags = creationflags)
|
||||||
|
except OSError:
|
||||||
|
ex = sys.exc_info()[1]
|
||||||
|
if ex.errno == errno.ENOENT:
|
||||||
|
raise RarExecError("Unrar not installed? (rarfile.UNRAR_TOOL=%r)" % UNRAR_TOOL)
|
||||||
|
raise
|
||||||
return p
|
return p
|
||||||
|
|
||||||
def check_returncode(p, out):
|
def check_returncode(p, out):
|
||||||
@@ -1772,7 +1855,7 @@ def check_returncode(p, out):
|
|||||||
errmap = [None,
|
errmap = [None,
|
||||||
RarWarning, RarFatalError, RarCRCError, RarLockedArchiveError,
|
RarWarning, RarFatalError, RarCRCError, RarLockedArchiveError,
|
||||||
RarWriteError, RarOpenError, RarUserError, RarMemoryError,
|
RarWriteError, RarOpenError, RarUserError, RarMemoryError,
|
||||||
RarCreateError] # codes from rar.txt
|
RarCreateError, RarNoFilesError] # codes from rar.txt
|
||||||
if code > 0 and code < len(errmap):
|
if code > 0 and code < len(errmap):
|
||||||
exc = errmap[code]
|
exc = errmap[code]
|
||||||
elif code == 255:
|
elif code == 255:
|
||||||
|
|||||||
@@ -1,65 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
||||||
<plist version="1.0">
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleAllowMixedLocalizations</key>
|
|
||||||
<true/>
|
|
||||||
<key>CFBundleDevelopmentRegion</key>
|
|
||||||
<string>English</string>
|
|
||||||
<key>CFBundleDocumentTypes</key>
|
|
||||||
<array>
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleTypeExtensions</key>
|
|
||||||
<array>
|
|
||||||
<string>*</string>
|
|
||||||
</array>
|
|
||||||
<key>CFBundleTypeOSTypes</key>
|
|
||||||
<array>
|
|
||||||
<string>****</string>
|
|
||||||
</array>
|
|
||||||
<key>CFBundleTypeRole</key>
|
|
||||||
<string>Viewer</string>
|
|
||||||
</dict>
|
|
||||||
</array>
|
|
||||||
<key>CFBundleExecutable</key>
|
|
||||||
<string>droplet</string>
|
|
||||||
<key>CFBundleGetInfoString</key>
|
|
||||||
<string>KindleComicConverter 2.0, Written 2012 by Ciro Mattia Gonano</string>
|
|
||||||
<key>CFBundleIconFile</key>
|
|
||||||
<string>droplet</string>
|
|
||||||
<key>CFBundleIdentifier</key>
|
|
||||||
<string>com.github.ciromattia.kcc</string>
|
|
||||||
<key>CFBundleInfoDictionaryVersion</key>
|
|
||||||
<string>6.0</string>
|
|
||||||
<key>CFBundleName</key>
|
|
||||||
<string>KindleComicConverter 1.20</string>
|
|
||||||
<key>CFBundlePackageType</key>
|
|
||||||
<string>APPL</string>
|
|
||||||
<key>CFBundleShortVersionString</key>
|
|
||||||
<string>1.0</string>
|
|
||||||
<key>CFBundleSignature</key>
|
|
||||||
<string>dplt</string>
|
|
||||||
<key>LSMinimumSystemVersionByArchitecture</key>
|
|
||||||
<dict>
|
|
||||||
<key>x86_64</key>
|
|
||||||
<string>10.6</string>
|
|
||||||
</dict>
|
|
||||||
<key>LSRequiresCarbon</key>
|
|
||||||
<true/>
|
|
||||||
<key>WindowState</key>
|
|
||||||
<dict>
|
|
||||||
<key>dividerCollapsed</key>
|
|
||||||
<true/>
|
|
||||||
<key>eventLogLevel</key>
|
|
||||||
<integer>-1</integer>
|
|
||||||
<key>name</key>
|
|
||||||
<string>ScriptWindowState</string>
|
|
||||||
<key>positionOfDivider</key>
|
|
||||||
<real>568</real>
|
|
||||||
<key>savedFrame</key>
|
|
||||||
<string>144 338 889 690 0 0 1680 1028 </string>
|
|
||||||
<key>selectedTabView</key>
|
|
||||||
<string>event log</string>
|
|
||||||
</dict>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
||||||
Binary file not shown.
@@ -1,22 +0,0 @@
|
|||||||
{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf340
|
|
||||||
{\fonttbl\f0\fnil\fcharset0 Verdana;}
|
|
||||||
{\colortbl;\red255\green255\blue255;\red76\green78\blue78;}
|
|
||||||
\pard\tx576\tx1152\tx1728\tx2304\tx2880\tx3456\tx4032\tx4608\tx5184\tx5760\tx6337\tx6913\tx7489\tx8065\tx8641\tx9217\tx9793\tx10369\tx10945\tx11521\tx12097\tx12674\tx13250\tx13826\tx14402\tx14978\tx15554\tx16130\tx16706\tx17282\tx17858\tx18435\tx19011\tx19587\tx20163\tx20739\tx21315\tx21891\tx22467\tx23043\tx23619\tx24195\tx24772\tx25348\tx25924\tx26500\tx27076\tx27652\tx28228\tx28804\tx29380\tx29956\tx30532\tx31109\tx31685\tx32261\tx32837\tx33413\tx33989\tx34565\tx35141\tx35717\tx36293\tx36870\tx37446\tx38022\tx38598\tx39174\tx39750\tx40326\tx40902\tx41478\tx42054\tx42630\tx43207\tx43783\tx44359\tx44935\tx45511\tx46087\tx46663\tx47239\tx47815\tx48391\tx48967\tx49544\tx50120\tx50696\tx51272\tx51848\tx52424\tx53000\tx53576\tx54152\tx54728\tx55305\tx55881\tx56457\tx57033\tx57609\li785\fi-786\pardirnatural
|
|
||||||
|
|
||||||
\f0\fs24 \cf2 \CocoaLigature0 Copyright (c) 2012 Ciro Mattia Gonano <ciromattia@gmail.com>\
|
|
||||||
\
|
|
||||||
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.\
|
|
||||||
\
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\
|
|
||||||
\
|
|
||||||
This script heavily relies on KindleStrip (C) by Paul Durrant and released in public domain (http://www.mobileread.com/forums/showthread.php?t=96903)\
|
|
||||||
Also, you need to have kindlegen v2.7 (with KF8 support) which is downloadable from Amazon website.\
|
|
||||||
\
|
|
||||||
Changelog:\
|
|
||||||
1.0: first release\
|
|
||||||
1.10: add CBZ/CBR support to comic2ebook.py\
|
|
||||||
1.11: add CBZ/CBR support to KindleComicConverter\
|
|
||||||
1.2: added image page splitting and optimizations\
|
|
||||||
\
|
|
||||||
Todo:\
|
|
||||||
- bundle a script to manipulate images (to get rid of Mangle/E-nki/whatsoever)}
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 362 B |
49
setup.py
49
setup.py
@@ -1,33 +1,36 @@
|
|||||||
"""
|
"""
|
||||||
py2app/py2exe build script for MyApplication.
|
py2app/cx_Freeze build script for KCC.
|
||||||
|
|
||||||
Will automatically ensure that all build prerequisites are available
|
Will automatically ensure that all build prerequisites are available via ez_setup
|
||||||
via ez_setup
|
|
||||||
|
|
||||||
Usage (Mac OS X):
|
Usage (Mac OS X):
|
||||||
python setup.py py2app
|
python setup.py py2app
|
||||||
|
|
||||||
Usage (Windows):
|
Usage (Windows):
|
||||||
python setup.py py2exe
|
python setup.py build
|
||||||
"""
|
"""
|
||||||
import ez_setup
|
from ez_setup import use_setuptools
|
||||||
ez_setup.use_setuptools()
|
use_setuptools()
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
NAME = "KindleComicConverter"
|
||||||
|
VERSION = "2.9"
|
||||||
|
MAIN = "kcc.py"
|
||||||
|
|
||||||
|
includefiles = ['README.md', 'MANIFEST.in', 'LICENSE.txt', 'comic2ebook.ico', 'comic2ebook.icns']
|
||||||
|
includes = []
|
||||||
|
excludes = []
|
||||||
|
|
||||||
|
if sys.platform == "darwin":
|
||||||
from setuptools import setup
|
from setuptools import setup
|
||||||
|
|
||||||
NAME = 'KindleComicConverter'
|
|
||||||
VERSION = "2.6"
|
|
||||||
mainscript = 'kcc.py'
|
|
||||||
|
|
||||||
if sys.platform == 'darwin':
|
|
||||||
extra_options = dict(
|
extra_options = dict(
|
||||||
setup_requires=['py2app'],
|
setup_requires=['py2app'],
|
||||||
app=[mainscript],
|
app=[MAIN],
|
||||||
options=dict(
|
options=dict(
|
||||||
py2app=dict(
|
py2app=dict(
|
||||||
argv_emulation=True,
|
argv_emulation=True,
|
||||||
iconfile='resources/comic2ebook.icns',
|
iconfile='comic2ebook.icns',
|
||||||
plist=dict(
|
plist=dict(
|
||||||
CFBundleName=NAME,
|
CFBundleName=NAME,
|
||||||
CFBundleShortVersionString=VERSION,
|
CFBundleShortVersionString=VERSION,
|
||||||
@@ -43,13 +46,17 @@ elif sys.platform == 'win32':
|
|||||||
from cx_Freeze import setup, Executable
|
from cx_Freeze import setup, Executable
|
||||||
base = "Win32GUI"
|
base = "Win32GUI"
|
||||||
extra_options = dict(
|
extra_options = dict(
|
||||||
executables=[Executable("kcc.py", base=base)]
|
options={"build_exe": {"include_files": includefiles, 'excludes': excludes, 'compressed': True}},
|
||||||
)
|
executables=[Executable(MAIN,
|
||||||
|
base=base,
|
||||||
|
icon="comic2ebook.ico",
|
||||||
|
copyDependentFiles=True,
|
||||||
|
appendScriptToExe=True,
|
||||||
|
appendScriptToLibrary=False,
|
||||||
|
compress=True)])
|
||||||
else:
|
else:
|
||||||
extra_options = dict(
|
extra_options = dict(
|
||||||
# Normally unix-like platforms will use "setup.py install"
|
scripts=[MAIN],
|
||||||
# and install the main script as such
|
|
||||||
scripts=[mainscript],
|
|
||||||
)
|
)
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
@@ -71,12 +78,10 @@ setup(
|
|||||||
'Intended Audience :: End Users/Desktop',
|
'Intended Audience :: End Users/Desktop',
|
||||||
'Operating System :: OS Independent',
|
'Operating System :: OS Independent',
|
||||||
'Programming Language :: Python',
|
'Programming Language :: Python',
|
||||||
'Programming Language :: Python :: 3',
|
'Programming Language :: Python :: 2.7',
|
||||||
'Topic :: Multimedia :: Graphics :: Graphics Conversion',
|
'Topic :: Multimedia :: Graphics :: Graphics Conversion',
|
||||||
'Topic :: Utilities'
|
'Topic :: Utilities'
|
||||||
],
|
],
|
||||||
packages=['kcc'],
|
packages=['kcc'],
|
||||||
# make sure to add custom_fixers to the MANIFEST.in
|
|
||||||
include_package_data=True,
|
|
||||||
**extra_options
|
**extra_options
|
||||||
)
|
)
|
||||||
|
|||||||
22
setup_console.py
Normal file
22
setup_console.py
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
"""
|
||||||
|
cx_freeze build script for Windows KCC No-GUI release.
|
||||||
|
|
||||||
|
Usage (Windows):
|
||||||
|
python setup_console.py build
|
||||||
|
"""
|
||||||
|
import sys
|
||||||
|
from cx_Freeze import setup, Executable
|
||||||
|
sys.path.insert(0, 'kcc')
|
||||||
|
|
||||||
|
setup(
|
||||||
|
name = "KindleComicConverter",
|
||||||
|
version = "2.9",
|
||||||
|
author = "Ciro Mattia Gonano",
|
||||||
|
author_email = "ciromattia@gmail.com",
|
||||||
|
description = "A tool to convert comics (CBR/CBZ/PDFs/image folders) to MOBI.",
|
||||||
|
license= "ISC License (ISCL)",
|
||||||
|
keywords= "kindle comic mobipocket mobi cbz cbr manga",
|
||||||
|
url = "http://github.com/ciromattia/kcc",
|
||||||
|
executables = [Executable("kcc/comic2ebook.py", compress=True, copyDependentFiles=True, appendScriptToExe=True, appendScriptToLibrary=False),
|
||||||
|
Executable("kcc/kindlestrip.py", compress=True, copyDependentFiles=True, appendScriptToExe=True, appendScriptToLibrary=False)]
|
||||||
|
)
|
||||||
86
setup_cx.py
86
setup_cx.py
@@ -1,86 +0,0 @@
|
|||||||
"""
|
|
||||||
py2app/py2exe build script for MyApplication.
|
|
||||||
|
|
||||||
Will automatically ensure that all build prerequisites are available
|
|
||||||
via ez_setup
|
|
||||||
|
|
||||||
Usage (Mac OS X):
|
|
||||||
python setup_cx.py bdist_dmg
|
|
||||||
|
|
||||||
Usage (Windows):
|
|
||||||
python setup_cx.py bdist_msi
|
|
||||||
"""
|
|
||||||
import sys
|
|
||||||
from cx_Freeze import setup, Executable
|
|
||||||
|
|
||||||
NAME='KindleComicConverter'
|
|
||||||
VERSION="2.4"
|
|
||||||
mainscript = 'kcc.py'
|
|
||||||
|
|
||||||
base = None
|
|
||||||
|
|
||||||
if sys.platform == 'darwin':
|
|
||||||
extra_options = dict(
|
|
||||||
bundle-iconfile='resources/comic2ebook.icns',
|
|
||||||
volume-label=NAME + " " + VERSION
|
|
||||||
app=[mainscript],
|
|
||||||
options=dict(
|
|
||||||
py2app=dict(
|
|
||||||
argv_emulation=True,
|
|
||||||
iconfile='resources/comic2ebook.icns',
|
|
||||||
plist=dict(
|
|
||||||
CFBundleName = NAME,
|
|
||||||
CFBundleShortVersionString = VERSION,
|
|
||||||
CFBundleGetInfoString = NAME + " " + VERSION + ", written 2012-2013 by Ciro Mattia Gonano",
|
|
||||||
CFBundleExecutable = NAME,
|
|
||||||
CFBundleIdentifier = 'com.github.ciromattia.kcc',
|
|
||||||
CFBundleSignature = 'dplt'
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
elif sys.platform == 'win32':
|
|
||||||
base = "Win32GUI"
|
|
||||||
extra_options = dict(
|
|
||||||
upgrade-code = 'KindleComicConverter',
|
|
||||||
executables = [Executable("kcc.py", base=base)]
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
extra_options = dict(
|
|
||||||
# Normally unix-like platforms will use "setup.py install"
|
|
||||||
# and install the main script as such
|
|
||||||
scripts=[mainscript],
|
|
||||||
)
|
|
||||||
|
|
||||||
options = dict(
|
|
||||||
|
|
||||||
)
|
|
||||||
|
|
||||||
setup(
|
|
||||||
name=NAME,
|
|
||||||
version=VERSION,
|
|
||||||
author="Ciro Mattia Gonano",
|
|
||||||
author_email="ciromattia@gmail.com",
|
|
||||||
description=("A tool to convert comics (CBR/CBZ/PDFs/image folders) to Mobipocket."),
|
|
||||||
license = "ISC License (ISCL)",
|
|
||||||
keywords = "kindle comic mobipocket mobi cbz cbr manga",
|
|
||||||
url = "http://github.com/ciromattia/kcc",
|
|
||||||
classifiers=[
|
|
||||||
'Development Status :: 4 - Beta'
|
|
||||||
'License :: OSI Approved :: ISC License (ISCL)',
|
|
||||||
'Environment :: Console',
|
|
||||||
'Environment :: MacOS X',
|
|
||||||
'Environment :: Win32 (MS Windows)',
|
|
||||||
'Environment :: X11 Applications',
|
|
||||||
'Intended Audience :: End Users/Desktop',
|
|
||||||
'Operating System :: OS Independent',
|
|
||||||
'Programming Language :: Python',
|
|
||||||
'Programming Language :: Python :: 3',
|
|
||||||
'Topic :: Multimedia :: Graphics :: Graphics Conversion',
|
|
||||||
'Topic :: Utilities'
|
|
||||||
],
|
|
||||||
# make sure to add custom_fixers to the MANIFEST.in
|
|
||||||
include_package_data=True,
|
|
||||||
executables = [Executable("kcc.py", base=base)]),
|
|
||||||
**extra_options
|
|
||||||
)
|
|
||||||
Reference in New Issue
Block a user