mirror of
https://github.com/ciromattia/kcc
synced 2026-05-03 14:22:10 +00:00
Compare commits
88 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1fa6d315b1 | ||
|
|
112917754a | ||
|
|
65774c6f12 | ||
|
|
7b3ce8827f | ||
|
|
b12825045b | ||
|
|
ac9f3a5d87 | ||
|
|
00969a3739 | ||
|
|
21f738b44a | ||
|
|
f2238b16a6 | ||
|
|
14f677ec68 | ||
|
|
7b3bf4618f | ||
|
|
eab63a0f74 | ||
|
|
2128104db7 | ||
|
|
c6179b0064 | ||
|
|
1d4319be2e | ||
|
|
f5a738e2d4 | ||
|
|
477d834a91 | ||
|
|
c8698f6d99 | ||
|
|
0988601842 | ||
|
|
57e9637c81 | ||
|
|
a7440e06a9 | ||
|
|
a9ed1e7610 | ||
|
|
b1bc140ad3 | ||
|
|
9014ed53d4 | ||
|
|
cad05904f3 | ||
|
|
10386d8af3 | ||
|
|
c991feb9ce | ||
|
|
d26eb7cdcd | ||
|
|
351084b703 | ||
|
|
e861e7f6e8 | ||
|
|
370c9d4df7 | ||
|
|
8e5704683c | ||
|
|
c65e1c8dea | ||
|
|
677622c103 | ||
|
|
af0ebb85a0 | ||
|
|
8af029ac92 | ||
|
|
a268e12a90 | ||
|
|
d621335e6c | ||
|
|
ec1d9c2d93 | ||
|
|
85b9dbbf83 | ||
|
|
feeced44bf | ||
|
|
cbea18398b | ||
|
|
4c9857f14d | ||
|
|
6b58ef4557 | ||
|
|
24d697c965 | ||
|
|
8b07d4eb69 | ||
|
|
e6c5ac915f | ||
|
|
b22e4757a3 | ||
|
|
91b06016bb | ||
|
|
5631391245 | ||
|
|
c33887b7b7 | ||
|
|
8d82f58f09 | ||
|
|
36985f5169 | ||
|
|
9d190c1585 | ||
|
|
3834850317 | ||
|
|
84fc23b979 | ||
|
|
77748afdbd | ||
|
|
431e2ffaf2 | ||
|
|
16df4cd083 | ||
|
|
1aa34347c1 | ||
|
|
561af90b06 | ||
|
|
00d239e1d8 | ||
|
|
26bd2d3ed0 | ||
|
|
e7aa49b70c | ||
|
|
da41edc2f1 | ||
|
|
ecbf60fb28 | ||
|
|
57b571b6c2 | ||
|
|
44bdc0245b | ||
|
|
1ec07fe4ec | ||
|
|
5f8f7e0919 | ||
|
|
f404b9090d | ||
|
|
68521f7c63 | ||
|
|
f5dd813c4c | ||
|
|
7924c492b3 | ||
|
|
2fc21c33e2 | ||
|
|
cb76504acb | ||
|
|
db6b0eddfe | ||
|
|
7d529a2acc | ||
|
|
ad3ff35aaa | ||
|
|
c62eeeb712 | ||
|
|
5a36a13105 | ||
|
|
12684d6562 | ||
|
|
c5f68ae12a | ||
|
|
7bd9c766cc | ||
|
|
c6b1417d9c | ||
|
|
98bf28a713 | ||
|
|
f2d6d5b458 | ||
|
|
5de492ffb6 |
27
KCC.qrc
27
KCC.qrc
@@ -1,27 +0,0 @@
|
|||||||
<RCC>
|
|
||||||
<qresource prefix="Icon">
|
|
||||||
<file>icons/comic2ebook.png</file>
|
|
||||||
</qresource>
|
|
||||||
<qresource prefix="Devices">
|
|
||||||
<file>icons/Kobo.png</file>
|
|
||||||
<file>icons/Other.png</file>
|
|
||||||
<file>icons/Kindle.png</file>
|
|
||||||
</qresource>
|
|
||||||
<qresource prefix="Formats">
|
|
||||||
<file>icons/CBZ.png</file>
|
|
||||||
<file>icons/EPUB.png</file>
|
|
||||||
<file>icons/MOBI.png</file>
|
|
||||||
</qresource>
|
|
||||||
<qresource prefix="Status">
|
|
||||||
<file>icons/error.png</file>
|
|
||||||
<file>icons/info.png</file>
|
|
||||||
<file>icons/warning.png</file>
|
|
||||||
</qresource>
|
|
||||||
<qresource prefix="Other">
|
|
||||||
<file>icons/list_background.png</file>
|
|
||||||
<file>icons/clear.png</file>
|
|
||||||
<file>icons/convert.png</file>
|
|
||||||
<file>icons/document_new.png</file>
|
|
||||||
<file>icons/folder_new.png</file>
|
|
||||||
</qresource>
|
|
||||||
</RCC>
|
|
||||||
119
README.md
119
README.md
@@ -1,7 +1,7 @@
|
|||||||
# KCC
|
# KCC
|
||||||
|
|
||||||
**Kindle Comic Converter** is a Python app to convert comic/manga files or folders to EPUB, Panel View MOBI or E-Ink optimized CBZ.
|
**Kindle Comic Converter** is a Python app to convert comic/manga files or folders to EPUB, Panel View MOBI or E-Ink optimized CBZ.
|
||||||
It was initially developed for Kindle but since version 2.2 it outputs valid EPUB 2.0 so _**despite its name, KCC is
|
It was initially developed for Kindle but since version 4.6 it outputs valid EPUB 3.0 so _**despite its name, KCC is
|
||||||
actually a comic/manga to EPUB converter that every e-reader owner can happily use**_.
|
actually a comic/manga to EPUB converter that every e-reader owner can happily use**_.
|
||||||
It can also optionally optimize images by applying a number of transformations.
|
It can also optionally optimize images by applying a number of transformations.
|
||||||
|
|
||||||
@@ -29,6 +29,26 @@ You can find the latest released binary at the following links:
|
|||||||
- **Linux:** [http://kcc.iosphe.re/Linux/](http://kcc.iosphe.re/Linux/)
|
- **Linux:** [http://kcc.iosphe.re/Linux/](http://kcc.iosphe.re/Linux/)
|
||||||
- **OS X (10.8+):** [http://kcc.iosphe.re/OSX/](http://kcc.iosphe.re/OSX/)
|
- **OS X (10.8+):** [http://kcc.iosphe.re/OSX/](http://kcc.iosphe.re/OSX/)
|
||||||
|
|
||||||
|
## DEPENDENCIES
|
||||||
|
Following software is required to run Linux version of **KCC** and/or bare sources:
|
||||||
|
- Python 3.3+
|
||||||
|
- [PyQt](http://www.riverbankcomputing.co.uk/software/pyqt/download5) 5.2.1+
|
||||||
|
- [Pillow](http://pypi.python.org/pypi/Pillow/) 2.8.2+
|
||||||
|
- [psutil](https://pypi.python.org/pypi/psutil) 3.0.0+
|
||||||
|
- [python-slugify](http://pypi.python.org/pypi/python-slugify) 1.1.3+
|
||||||
|
- [scandir](https://pypi.python.org/pypi/scandir) 1.1.0+
|
||||||
|
|
||||||
|
On Debian based distributions these two commands should install all needed dependencies:
|
||||||
|
```
|
||||||
|
sudo apt-get install python3 python3-dev python3-pip python3-pyqt5 libpng-dev libjpeg-dev p7zip-full unrar
|
||||||
|
sudo pip3 install pillow python-slugify psutil scandir
|
||||||
|
```
|
||||||
|
|
||||||
|
### Optional dependencies
|
||||||
|
- [KindleGen](http://www.amazon.com/gp/feature.html?ie=UTF8&docId=1000765211) v2.9+ in a directory reachable by your _PATH_ or in _KCC_ directory *(For MOBI generation)*
|
||||||
|
- [UnRAR](http://www.rarlab.com/download.htm) *(For CBR/RAR support)*
|
||||||
|
- [7za](http://www.7-zip.org/download.html) *(For 7z/CB7 support)*
|
||||||
|
|
||||||
## INPUT FORMATS
|
## INPUT FORMATS
|
||||||
**KCC** can understand and convert, at the moment, the following input types:
|
**KCC** can understand and convert, at the moment, the following input types:
|
||||||
- Folders containing: PNG, JPG or GIF files
|
- Folders containing: PNG, JPG or GIF files
|
||||||
@@ -37,28 +57,6 @@ You can find the latest released binary at the following links:
|
|||||||
- CB7, 7Z *(With `7za` executable)*
|
- CB7, 7Z *(With `7za` executable)*
|
||||||
- PDF *(Only extracting JPG images)*
|
- PDF *(Only extracting JPG images)*
|
||||||
|
|
||||||
## OPTIONAL REQUIREMENTS
|
|
||||||
- [KindleGen](http://www.amazon.com/gp/feature.html?ie=UTF8&docId=1000765211) v2.9+ in a directory reachable by your _PATH_ or in _KCC_ directory *(For MOBI generation)*
|
|
||||||
- [UnRAR](http://www.rarlab.com/download.htm) *(For CBR/RAR support)*
|
|
||||||
- [7za](http://www.7-zip.org/download.html) *(For 7z/CB7 support)*
|
|
||||||
|
|
||||||
### For running from source:
|
|
||||||
- Python 3.3+
|
|
||||||
- [PyQt5](http://www.riverbankcomputing.co.uk/software/pyqt/download5) 5.2.0+
|
|
||||||
- [Pillow](http://pypi.python.org/pypi/Pillow/) 2.7.0+
|
|
||||||
- [psutil](https://pypi.python.org/pypi/psutil) 2.0+
|
|
||||||
- [python-slugify](http://pypi.python.org/pypi/python-slugify)
|
|
||||||
|
|
||||||
On Debian based distributions these two commands should install all dependencies:
|
|
||||||
```
|
|
||||||
sudo apt-get install python3 python3-dev python3-pip python3-pyqt5 libpng-dev libjpeg-dev p7zip-full unrar
|
|
||||||
sudo pip3 install pillow python-slugify psutil
|
|
||||||
```
|
|
||||||
|
|
||||||
### For freezing code:
|
|
||||||
- Windows - [py2exe](https://pypi.python.org/pypi/py2exe) 0.9.2.2+
|
|
||||||
- OS X - [py2app](https://bitbucket.org/ronaldoussoren/py2app) 0.9.0+
|
|
||||||
|
|
||||||
## USAGE
|
## USAGE
|
||||||
|
|
||||||
Should be pretty self-explanatory. All options have detailed informations in tooltips.
|
Should be pretty self-explanatory. All options have detailed informations in tooltips.
|
||||||
@@ -77,8 +75,7 @@ Options:
|
|||||||
MAIN:
|
MAIN:
|
||||||
-p PROFILE, --profile=PROFILE
|
-p PROFILE, --profile=PROFILE
|
||||||
Device profile (Available options: K1, K2, K345, KDX,
|
Device profile (Available options: K1, K2, K345, KDX,
|
||||||
KPW, KV, KFHD, KFHDX, KFHDX8, KFA, KoMT, KoG, KoA,
|
KPW, KV, KoMT, KoG, KoGHD, KoA, KoAHD, KoAH2O) [Default=KV]
|
||||||
KoAHD, KoAH2O) [Default=KV]
|
|
||||||
-q QUALITY, --quality=QUALITY
|
-q QUALITY, --quality=QUALITY
|
||||||
Quality of Panel View. 0 - Normal 1 - High 2 - Ultra [Default=0]
|
Quality of Panel View. 0 - Normal 1 - High 2 - Ultra [Default=0]
|
||||||
-m, --manga-style Manga style (Right-to-left reading and splitting)
|
-m, --manga-style Manga style (Right-to-left reading and splitting)
|
||||||
@@ -147,17 +144,67 @@ The app relies and includes the following scripts:
|
|||||||
- Icon is by **Nikolay Verin** ([http://ncrow.deviantart.com/](http://ncrow.deviantart.com/)) and released under [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/) License.
|
- Icon is by **Nikolay Verin** ([http://ncrow.deviantart.com/](http://ncrow.deviantart.com/)) and released under [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/) License.
|
||||||
|
|
||||||
## SAMPLE FILES CREATED BY KCC
|
## SAMPLE FILES CREATED BY KCC
|
||||||
* [Kindle Voyage](http://kcc.iosphe.re/Samples/Ubunchu!-KV.mobi)
|
* [Kindle Paperwhite 3 / Voyage](http://kcc.iosphe.re/Samples/Ubunchu!-KV.mobi)
|
||||||
* [Kindle Paperwhite](http://kcc.iosphe.re/Samples/Ubunchu!-KPW.mobi)
|
* [Kindle Paperwhite 1 / 2](http://kcc.iosphe.re/Samples/Ubunchu!-KPW.mobi)
|
||||||
* [Kindle](http://kcc.iosphe.re/Samples/Ubunchu!-K345.mobi)
|
* [Kindle](http://kcc.iosphe.re/Samples/Ubunchu!-K345.mobi)
|
||||||
* [Kindle DX/DXG](http://kcc.iosphe.re/Samples/Ubunchu!-KDX.cbz)
|
* [Kindle DX/DXG](http://kcc.iosphe.re/Samples/Ubunchu!-KDX.cbz)
|
||||||
* [Kobo Mini/Touch](http://kcc.iosphe.re/Samples/Ubunchu!-KoMT.cbz)
|
* [Kobo Mini/Touch](http://kcc.iosphe.re/Samples/Ubunchu-KoMT.kepub.epub)
|
||||||
* [Kobo Glow](http://kcc.iosphe.re/Samples/Ubunchu!-KoG.cbz)
|
* [Kobo Glo](http://kcc.iosphe.re/Samples/Ubunchu-KoG.kepub.epub)
|
||||||
* [Kobo Aura](http://kcc.iosphe.re/Samples/Ubunchu!-KoA.cbz)
|
* [Kobo Glo HD](http://kcc.iosphe.re/Samples/Ubunchu-KoGHD.kepub.epub)
|
||||||
* [Kobo Aura HD](http://kcc.iosphe.re/Samples/Ubunchu!-KoAHD.cbz)
|
* [Kobo Aura](http://kcc.iosphe.re/Samples/Ubunchu-KoA.kepub.epub)
|
||||||
* [Kobo Aura H2O](http://kcc.iosphe.re/Samples/Ubunchu!-KoAH2O.cbz)
|
* [Kobo Aura HD](http://kcc.iosphe.re/Samples/Ubunchu-KoAHD.kepub.epub)
|
||||||
|
* [Kobo Aura H2O](http://kcc.iosphe.re/Samples/Ubunchu-KoAH2O.kepub.epub)
|
||||||
|
|
||||||
## CHANGELOG
|
## CHANGELOG
|
||||||
|
####4.6.5:
|
||||||
|
* Fixed multiple Windows and OS X issues
|
||||||
|
* Allowed Linux release to use older PyQT5 version
|
||||||
|
|
||||||
|
####4.6.4:
|
||||||
|
* Fixed multiple Windows specific problems
|
||||||
|
* Improved error handling
|
||||||
|
* Improved color detection algorithm
|
||||||
|
* New, slimmer OS X release
|
||||||
|
|
||||||
|
####4.6.3:
|
||||||
|
* Implemented remote bug reporting
|
||||||
|
* Minor bug fixes and GUI tweaks
|
||||||
|
|
||||||
|
####4.6.2:
|
||||||
|
* Fixed critical MOBI header bug
|
||||||
|
* Fixed metadata encoding error
|
||||||
|
|
||||||
|
####4.6.1:
|
||||||
|
* Fixed KEPUB TOC generator
|
||||||
|
* Added warning about too small input files
|
||||||
|
* ComicRack Summary metadata field is now parsed
|
||||||
|
* Small tweaks of KEPUB output
|
||||||
|
|
||||||
|
####4.6:
|
||||||
|
* KEPUB is now default output for all Kobo profiles
|
||||||
|
* EPUB output now produce fully valid EPUB 3.0.1
|
||||||
|
* Added profile for Kindle Paperwhite 3
|
||||||
|
* Dropped official support of all Kindle Fire models and Kindle for Android
|
||||||
|
* Other minor tweaks
|
||||||
|
|
||||||
|
####4.5.1:
|
||||||
|
* Added Kobo Glo HD profile
|
||||||
|
* Fixed RAR/CBR parsing anomalies
|
||||||
|
* Minor bug fixes and tweaks
|
||||||
|
|
||||||
|
####4.5:
|
||||||
|
* Added simple ComicRack metadata editor
|
||||||
|
* Re-enabled Manga Cover Database support
|
||||||
|
* ComicRack bookmarks are now parsed
|
||||||
|
* Fixed glitches in Kindle Voyage profile
|
||||||
|
* Fixed problems with directory locks on Windows
|
||||||
|
* Fixed sorting anomalies
|
||||||
|
* Improved conversion speed
|
||||||
|
|
||||||
|
####4.4.1:
|
||||||
|
* Fixed problems with OSX GUI
|
||||||
|
* Added one missing DLL to Windows installer
|
||||||
|
|
||||||
####4.4:
|
####4.4:
|
||||||
* Improved speed and quality of conversion
|
* Improved speed and quality of conversion
|
||||||
* Added RAR5 support
|
* Added RAR5 support
|
||||||
@@ -390,6 +437,14 @@ The app relies and includes the following scripts:
|
|||||||
####1.0
|
####1.0
|
||||||
* Initial version
|
* Initial version
|
||||||
|
|
||||||
|
## PRIVACY
|
||||||
|
**KCC** is initiating internet connections in three cases:
|
||||||
|
* During startup - Version check
|
||||||
|
* When MCD metadata are used - Cover download
|
||||||
|
* When error occurs - Automatic reporting
|
||||||
|
|
||||||
|
Error report include **KCC** version, OS version and content of error message.
|
||||||
|
|
||||||
## KNOWN ISSUES
|
## KNOWN ISSUES
|
||||||
Please check [wiki page](https://github.com/ciromattia/kcc/wiki/Known-issues).
|
Please check [wiki page](https://github.com/ciromattia/kcc/wiki/Known-issues).
|
||||||
|
|
||||||
|
|||||||
@@ -451,9 +451,6 @@
|
|||||||
<property name="styleSheet">
|
<property name="styleSheet">
|
||||||
<string notr="true">QListWidget#JobList {background:#ffffff;background-image:url(:/Other/icons/list_background.png);background-position:center center;background-repeat:no-repeat;}QScrollBar:vertical{border:1px solid #999;background:#FFF;width:5px;margin:0}QScrollBar::handle:vertical{background:DarkGray;min-height:0}QScrollBar::add-line:vertical{height:0;background:DarkGray;subcontrol-position:bottom;subcontrol-origin:margin}QScrollBar::sub-line:vertical{height:0;background:DarkGray;subcontrol-position:top;subcontrol-origin:margin}QScrollBar:horizontal{border:1px solid #999;background:#FFF;height:5px;margin:0}QScrollBar::handle:horizontal{background:DarkGray;min-width:0}QScrollBar::add-line:horizontal{width:0;background:DarkGray;subcontrol-position:bottom;subcontrol-origin:margin}QScrollBar::sub-line:horizontal{width:0;background:DarkGray;subcontrol-position:top;subcontrol-origin:margin}</string>
|
<string notr="true">QListWidget#JobList {background:#ffffff;background-image:url(:/Other/icons/list_background.png);background-position:center center;background-repeat:no-repeat;}QScrollBar:vertical{border:1px solid #999;background:#FFF;width:5px;margin:0}QScrollBar::handle:vertical{background:DarkGray;min-height:0}QScrollBar::add-line:vertical{height:0;background:DarkGray;subcontrol-position:bottom;subcontrol-origin:margin}QScrollBar::sub-line:vertical{height:0;background:DarkGray;subcontrol-position:top;subcontrol-origin:margin}QScrollBar:horizontal{border:1px solid #999;background:#FFF;height:5px;margin:0}QScrollBar::handle:horizontal{background:DarkGray;min-width:0}QScrollBar::add-line:horizontal{width:0;background:DarkGray;subcontrol-position:bottom;subcontrol-origin:margin}QScrollBar::sub-line:horizontal{width:0;background:DarkGray;subcontrol-position:top;subcontrol-origin:margin}</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="verticalScrollBarPolicy">
|
|
||||||
<enum>Qt::ScrollBarAlwaysOn</enum>
|
|
||||||
</property>
|
|
||||||
<property name="showDropIndicator" stdset="0">
|
<property name="showDropIndicator" stdset="0">
|
||||||
<bool>false</bool>
|
<bool>false</bool>
|
||||||
</property>
|
</property>
|
||||||
@@ -466,19 +463,13 @@
|
|||||||
<height>18</height>
|
<height>18</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="verticalScrollMode">
|
|
||||||
<enum>QAbstractItemView::ScrollPerPixel</enum>
|
|
||||||
</property>
|
|
||||||
<property name="horizontalScrollMode">
|
|
||||||
<enum>QAbstractItemView::ScrollPerPixel</enum>
|
|
||||||
</property>
|
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QPushButton" name="BasicModeButton">
|
<widget class="QPushButton" name="BasicModeButton">
|
||||||
<property name="geometry">
|
<property name="geometry">
|
||||||
<rect>
|
<rect>
|
||||||
<x>10</x>
|
<x>10</x>
|
||||||
<y>10</y>
|
<y>10</y>
|
||||||
<width>195</width>
|
<width>141</width>
|
||||||
<height>32</height>
|
<height>32</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
@@ -498,9 +489,9 @@
|
|||||||
<widget class="QPushButton" name="AdvModeButton">
|
<widget class="QPushButton" name="AdvModeButton">
|
||||||
<property name="geometry">
|
<property name="geometry">
|
||||||
<rect>
|
<rect>
|
||||||
<x>217</x>
|
<x>260</x>
|
||||||
<y>10</y>
|
<y>10</y>
|
||||||
<width>195</width>
|
<width>151</width>
|
||||||
<height>32</height>
|
<height>32</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
@@ -767,6 +758,32 @@
|
|||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</widget>
|
</widget>
|
||||||
|
<widget class="QPushButton" name="EditorButton">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>160</x>
|
||||||
|
<y>10</y>
|
||||||
|
<width>91</width>
|
||||||
|
<height>32</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<family>DejaVu Sans</family>
|
||||||
|
<pointsize>9</pointsize>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="focusPolicy">
|
||||||
|
<enum>Qt::NoFocus</enum>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Editor</string>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="KCC.qrc">
|
||||||
|
<normaloff>:/Other/icons/editor.png</normaloff>:/Other/icons/editor.png</iconset>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
<zorder>OptionsAdvanced</zorder>
|
<zorder>OptionsAdvanced</zorder>
|
||||||
<zorder>DeviceBox</zorder>
|
<zorder>DeviceBox</zorder>
|
||||||
<zorder>FormatBox</zorder>
|
<zorder>FormatBox</zorder>
|
||||||
@@ -780,6 +797,7 @@
|
|||||||
<zorder>AdvModeButton</zorder>
|
<zorder>AdvModeButton</zorder>
|
||||||
<zorder>OptionsAdvancedGamma</zorder>
|
<zorder>OptionsAdvancedGamma</zorder>
|
||||||
<zorder>OptionsExpert</zorder>
|
<zorder>OptionsExpert</zorder>
|
||||||
|
<zorder>EditorButton</zorder>
|
||||||
<zorder>ProgressBar</zorder>
|
<zorder>ProgressBar</zorder>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QStatusBar" name="statusBar">
|
<widget class="QStatusBar" name="statusBar">
|
||||||
@@ -456,9 +456,6 @@
|
|||||||
<property name="styleSheet">
|
<property name="styleSheet">
|
||||||
<string notr="true">QListWidget#JobList {background:#ffffff;background-image:url(:/Other/icons/list_background.png);background-position:center center;background-repeat:no-repeat;}QScrollBar:vertical{border:1px solid #999;background:#FFF;width:5px;margin:0}QScrollBar::handle:vertical{background:DarkGray;min-height:0}QScrollBar::add-line:vertical{height:0;background:DarkGray;subcontrol-position:bottom;subcontrol-origin:margin}QScrollBar::sub-line:vertical{height:0;background:DarkGray;subcontrol-position:top;subcontrol-origin:margin}QScrollBar:horizontal{border:1px solid #999;background:#FFF;height:5px;margin:0}QScrollBar::handle:horizontal{background:DarkGray;min-width:0}QScrollBar::add-line:horizontal{width:0;background:DarkGray;subcontrol-position:bottom;subcontrol-origin:margin}QScrollBar::sub-line:horizontal{width:0;background:DarkGray;subcontrol-position:top;subcontrol-origin:margin}</string>
|
<string notr="true">QListWidget#JobList {background:#ffffff;background-image:url(:/Other/icons/list_background.png);background-position:center center;background-repeat:no-repeat;}QScrollBar:vertical{border:1px solid #999;background:#FFF;width:5px;margin:0}QScrollBar::handle:vertical{background:DarkGray;min-height:0}QScrollBar::add-line:vertical{height:0;background:DarkGray;subcontrol-position:bottom;subcontrol-origin:margin}QScrollBar::sub-line:vertical{height:0;background:DarkGray;subcontrol-position:top;subcontrol-origin:margin}QScrollBar:horizontal{border:1px solid #999;background:#FFF;height:5px;margin:0}QScrollBar::handle:horizontal{background:DarkGray;min-width:0}QScrollBar::add-line:horizontal{width:0;background:DarkGray;subcontrol-position:bottom;subcontrol-origin:margin}QScrollBar::sub-line:horizontal{width:0;background:DarkGray;subcontrol-position:top;subcontrol-origin:margin}</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="verticalScrollBarPolicy">
|
|
||||||
<enum>Qt::ScrollBarAlwaysOn</enum>
|
|
||||||
</property>
|
|
||||||
<property name="showDropIndicator" stdset="0">
|
<property name="showDropIndicator" stdset="0">
|
||||||
<bool>false</bool>
|
<bool>false</bool>
|
||||||
</property>
|
</property>
|
||||||
@@ -477,7 +474,7 @@
|
|||||||
<rect>
|
<rect>
|
||||||
<x>5</x>
|
<x>5</x>
|
||||||
<y>10</y>
|
<y>10</y>
|
||||||
<width>210</width>
|
<width>156</width>
|
||||||
<height>41</height>
|
<height>41</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
@@ -499,9 +496,9 @@
|
|||||||
<widget class="QPushButton" name="AdvModeButton">
|
<widget class="QPushButton" name="AdvModeButton">
|
||||||
<property name="geometry">
|
<property name="geometry">
|
||||||
<rect>
|
<rect>
|
||||||
<x>207</x>
|
<x>260</x>
|
||||||
<y>10</y>
|
<y>10</y>
|
||||||
<width>210</width>
|
<width>156</width>
|
||||||
<height>41</height>
|
<height>41</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
@@ -782,9 +779,34 @@
|
|||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</widget>
|
</widget>
|
||||||
|
<widget class="QPushButton" name="EditorButton">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>160</x>
|
||||||
|
<y>10</y>
|
||||||
|
<width>101</width>
|
||||||
|
<height>41</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<family>Lucida Grande</family>
|
||||||
|
<pointsize>12</pointsize>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="focusPolicy">
|
||||||
|
<enum>Qt::NoFocus</enum>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Editor</string>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="KCC.qrc">
|
||||||
|
<normaloff>:/Other/icons/editor.png</normaloff>:/Other/icons/editor.png</iconset>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
<zorder>BasicModeButton</zorder>
|
<zorder>BasicModeButton</zorder>
|
||||||
<zorder>AdvModeButton</zorder>
|
<zorder>AdvModeButton</zorder>
|
||||||
<zorder>ProgressBar</zorder>
|
|
||||||
<zorder>JobList</zorder>
|
<zorder>JobList</zorder>
|
||||||
<zorder>OptionsAdvanced</zorder>
|
<zorder>OptionsAdvanced</zorder>
|
||||||
<zorder>DeviceBox</zorder>
|
<zorder>DeviceBox</zorder>
|
||||||
@@ -796,6 +818,8 @@
|
|||||||
<zorder>OptionsBasic</zorder>
|
<zorder>OptionsBasic</zorder>
|
||||||
<zorder>OptionsAdvancedGamma</zorder>
|
<zorder>OptionsAdvancedGamma</zorder>
|
||||||
<zorder>OptionsExpert</zorder>
|
<zorder>OptionsExpert</zorder>
|
||||||
|
<zorder>EditorButton</zorder>
|
||||||
|
<zorder>ProgressBar</zorder>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QStatusBar" name="statusBar">
|
<widget class="QStatusBar" name="statusBar">
|
||||||
<property name="font">
|
<property name="font">
|
||||||
28
gui/KCC.qrc
Normal file
28
gui/KCC.qrc
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<RCC>
|
||||||
|
<qresource prefix="Icon">
|
||||||
|
<file>../icons/comic2ebook.png</file>
|
||||||
|
</qresource>
|
||||||
|
<qresource prefix="Devices">
|
||||||
|
<file>../icons/Kobo.png</file>
|
||||||
|
<file>../icons/Other.png</file>
|
||||||
|
<file>../icons/Kindle.png</file>
|
||||||
|
</qresource>
|
||||||
|
<qresource prefix="Formats">
|
||||||
|
<file>../icons/CBZ.png</file>
|
||||||
|
<file>../icons/EPUB.png</file>
|
||||||
|
<file>../icons/MOBI.png</file>
|
||||||
|
</qresource>
|
||||||
|
<qresource prefix="Status">
|
||||||
|
<file>../icons/error.png</file>
|
||||||
|
<file>../icons/info.png</file>
|
||||||
|
<file>../icons/warning.png</file>
|
||||||
|
</qresource>
|
||||||
|
<qresource prefix="Other">
|
||||||
|
<file>../icons/editor.png</file>
|
||||||
|
<file>../icons/list_background.png</file>
|
||||||
|
<file>../icons/clear.png</file>
|
||||||
|
<file>../icons/convert.png</file>
|
||||||
|
<file>../icons/document_new.png</file>
|
||||||
|
<file>../icons/folder_new.png</file>
|
||||||
|
</qresource>
|
||||||
|
</RCC>
|
||||||
@@ -391,9 +391,6 @@
|
|||||||
<property name="styleSheet">
|
<property name="styleSheet">
|
||||||
<string notr="true">QListWidget#JobList {background:#ffffff;background-image:url(:/Other/icons/list_background.png);background-position:center center;background-repeat:no-repeat;}QScrollBar:vertical{border:1px solid #999;background:#FFF;width:5px;margin:0}QScrollBar::handle:vertical{background:DarkGray;min-height:0}QScrollBar::add-line:vertical{height:0;background:DarkGray;subcontrol-position:bottom;subcontrol-origin:margin}QScrollBar::sub-line:vertical{height:0;background:DarkGray;subcontrol-position:top;subcontrol-origin:margin}QScrollBar:horizontal{border:1px solid #999;background:#FFF;height:5px;margin:0}QScrollBar::handle:horizontal{background:DarkGray;min-width:0}QScrollBar::add-line:horizontal{width:0;background:DarkGray;subcontrol-position:bottom;subcontrol-origin:margin}QScrollBar::sub-line:horizontal{width:0;background:DarkGray;subcontrol-position:top;subcontrol-origin:margin}</string>
|
<string notr="true">QListWidget#JobList {background:#ffffff;background-image:url(:/Other/icons/list_background.png);background-position:center center;background-repeat:no-repeat;}QScrollBar:vertical{border:1px solid #999;background:#FFF;width:5px;margin:0}QScrollBar::handle:vertical{background:DarkGray;min-height:0}QScrollBar::add-line:vertical{height:0;background:DarkGray;subcontrol-position:bottom;subcontrol-origin:margin}QScrollBar::sub-line:vertical{height:0;background:DarkGray;subcontrol-position:top;subcontrol-origin:margin}QScrollBar:horizontal{border:1px solid #999;background:#FFF;height:5px;margin:0}QScrollBar::handle:horizontal{background:DarkGray;min-width:0}QScrollBar::add-line:horizontal{width:0;background:DarkGray;subcontrol-position:bottom;subcontrol-origin:margin}QScrollBar::sub-line:horizontal{width:0;background:DarkGray;subcontrol-position:top;subcontrol-origin:margin}</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="verticalScrollBarPolicy">
|
|
||||||
<enum>Qt::ScrollBarAlwaysOn</enum>
|
|
||||||
</property>
|
|
||||||
<property name="showDropIndicator" stdset="0">
|
<property name="showDropIndicator" stdset="0">
|
||||||
<bool>false</bool>
|
<bool>false</bool>
|
||||||
</property>
|
</property>
|
||||||
@@ -412,7 +409,7 @@
|
|||||||
<rect>
|
<rect>
|
||||||
<x>10</x>
|
<x>10</x>
|
||||||
<y>10</y>
|
<y>10</y>
|
||||||
<width>195</width>
|
<width>141</width>
|
||||||
<height>32</height>
|
<height>32</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
@@ -431,9 +428,9 @@
|
|||||||
<widget class="QPushButton" name="AdvModeButton">
|
<widget class="QPushButton" name="AdvModeButton">
|
||||||
<property name="geometry">
|
<property name="geometry">
|
||||||
<rect>
|
<rect>
|
||||||
<x>217</x>
|
<x>261</x>
|
||||||
<y>10</y>
|
<y>10</y>
|
||||||
<width>195</width>
|
<width>151</width>
|
||||||
<height>32</height>
|
<height>32</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
@@ -656,6 +653,31 @@
|
|||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</widget>
|
</widget>
|
||||||
|
<widget class="QPushButton" name="EditorButton">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>160</x>
|
||||||
|
<y>10</y>
|
||||||
|
<width>91</width>
|
||||||
|
<height>32</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>9</pointsize>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="focusPolicy">
|
||||||
|
<enum>Qt::NoFocus</enum>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Editor</string>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="KCC.qrc">
|
||||||
|
<normaloff>:/Other/icons/editor.png</normaloff>:/Other/icons/editor.png</iconset>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
<zorder>OptionsAdvanced</zorder>
|
<zorder>OptionsAdvanced</zorder>
|
||||||
<zorder>DeviceBox</zorder>
|
<zorder>DeviceBox</zorder>
|
||||||
<zorder>FormatBox</zorder>
|
<zorder>FormatBox</zorder>
|
||||||
@@ -669,6 +691,7 @@
|
|||||||
<zorder>AdvModeButton</zorder>
|
<zorder>AdvModeButton</zorder>
|
||||||
<zorder>OptionsAdvancedGamma</zorder>
|
<zorder>OptionsAdvancedGamma</zorder>
|
||||||
<zorder>OptionsExpert</zorder>
|
<zorder>OptionsExpert</zorder>
|
||||||
|
<zorder>EditorButton</zorder>
|
||||||
<zorder>ProgressBar</zorder>
|
<zorder>ProgressBar</zorder>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QStatusBar" name="statusBar">
|
<widget class="QStatusBar" name="statusBar">
|
||||||
226
gui/MetaEditor-Linux.ui
Normal file
226
gui/MetaEditor-Linux.ui
Normal file
@@ -0,0 +1,226 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>MetaEditorDialog</class>
|
||||||
|
<widget class="QDialog" name="MetaEditorDialog">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>400</width>
|
||||||
|
<height>320</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>400</width>
|
||||||
|
<height>320</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>400</width>
|
||||||
|
<height>320</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Metadata editor</string>
|
||||||
|
</property>
|
||||||
|
<property name="windowIcon">
|
||||||
|
<iconset resource="KCC.qrc">
|
||||||
|
<normaloff>:/Icon/icons/comic2ebook.png</normaloff>:/Icon/icons/comic2ebook.png</iconset>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="horizontalLayoutWidget">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>10</x>
|
||||||
|
<y>280</y>
|
||||||
|
<width>381</width>
|
||||||
|
<height>31</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="StatusLabel">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>10</pointsize>
|
||||||
|
<weight>75</weight>
|
||||||
|
<bold>true</bold>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="styleSheet">
|
||||||
|
<string notr="true">color: rgb(255, 0, 0);</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="OKButton">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<weight>75</weight>
|
||||||
|
<bold>true</bold>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Save</string>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="KCC.qrc">
|
||||||
|
<normaloff>:/Other/icons/convert.png</normaloff>:/Other/icons/convert.png</iconset>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="CancelButton">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<weight>75</weight>
|
||||||
|
<bold>true</bold>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Cancel</string>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="KCC.qrc">
|
||||||
|
<normaloff>:/Other/icons/clear.png</normaloff>:/Other/icons/clear.png</iconset>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<widget class="QFrame" name="EditorFrame">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>10</x>
|
||||||
|
<y>10</y>
|
||||||
|
<width>381</width>
|
||||||
|
<height>271</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="formLayoutWidget">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>381</width>
|
||||||
|
<height>266</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<layout class="QFormLayout" name="formLayout">
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string>Series:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QLineEdit" name="SeriesLine"/>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QLabel" name="label_2">
|
||||||
|
<property name="text">
|
||||||
|
<string>Volume:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QLineEdit" name="VolumeLine"/>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QLabel" name="label_3">
|
||||||
|
<property name="text">
|
||||||
|
<string>Number:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<widget class="QLineEdit" name="NumberLine"/>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="0">
|
||||||
|
<widget class="QLabel" name="label_4">
|
||||||
|
<property name="text">
|
||||||
|
<string>Writer:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="1">
|
||||||
|
<widget class="QLineEdit" name="WriterLine"/>
|
||||||
|
</item>
|
||||||
|
<item row="5" column="0">
|
||||||
|
<widget class="QLabel" name="label_5">
|
||||||
|
<property name="text">
|
||||||
|
<string>Penciller:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="5" column="1">
|
||||||
|
<widget class="QLineEdit" name="PencillerLine"/>
|
||||||
|
</item>
|
||||||
|
<item row="6" column="0">
|
||||||
|
<widget class="QLabel" name="label_6">
|
||||||
|
<property name="text">
|
||||||
|
<string>Inker:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="6" column="1">
|
||||||
|
<widget class="QLineEdit" name="InkerLine"/>
|
||||||
|
</item>
|
||||||
|
<item row="7" column="0">
|
||||||
|
<widget class="QLabel" name="label_7">
|
||||||
|
<property name="text">
|
||||||
|
<string>Colorist:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="7" column="1">
|
||||||
|
<widget class="QLineEdit" name="ColoristLine"/>
|
||||||
|
</item>
|
||||||
|
<item row="8" column="0">
|
||||||
|
<widget class="QLabel" name="label_8">
|
||||||
|
<property name="text">
|
||||||
|
<string><html><head/><body><p><a href="https://github.com/ciromattia/kcc/wiki/Manga-Cover-Database-support"><span style=" text-decoration: underline; color:#0000ff;">MUid:</span></a></p></body></html></string>
|
||||||
|
</property>
|
||||||
|
<property name="textFormat">
|
||||||
|
<enum>Qt::RichText</enum>
|
||||||
|
</property>
|
||||||
|
<property name="openExternalLinks">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="8" column="1">
|
||||||
|
<widget class="QLineEdit" name="MUidLine"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
<zorder>horizontalLayoutWidget</zorder>
|
||||||
|
<zorder>EditorFrame</zorder>
|
||||||
|
<zorder>StatusLabel</zorder>
|
||||||
|
</widget>
|
||||||
|
<resources>
|
||||||
|
<include location="KCC.qrc"/>
|
||||||
|
</resources>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
||||||
226
gui/MetaEditor-OSX.ui
Normal file
226
gui/MetaEditor-OSX.ui
Normal file
@@ -0,0 +1,226 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>MetaEditorDialog</class>
|
||||||
|
<widget class="QDialog" name="MetaEditorDialog">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>400</width>
|
||||||
|
<height>295</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>400</width>
|
||||||
|
<height>295</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>400</width>
|
||||||
|
<height>295</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Metadata editor</string>
|
||||||
|
</property>
|
||||||
|
<property name="windowIcon">
|
||||||
|
<iconset resource="KCC.qrc">
|
||||||
|
<normaloff>:/Icon/icons/comic2ebook.png</normaloff>:/Icon/icons/comic2ebook.png</iconset>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="horizontalLayoutWidget">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>10</x>
|
||||||
|
<y>260</y>
|
||||||
|
<width>381</width>
|
||||||
|
<height>32</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="StatusLabel">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>10</pointsize>
|
||||||
|
<weight>75</weight>
|
||||||
|
<bold>true</bold>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="styleSheet">
|
||||||
|
<string notr="true">color: rgb(255, 0, 0);</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="OKButton">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<weight>75</weight>
|
||||||
|
<bold>true</bold>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Save</string>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="KCC.qrc">
|
||||||
|
<normaloff>:/Other/icons/convert.png</normaloff>:/Other/icons/convert.png</iconset>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="CancelButton">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<weight>75</weight>
|
||||||
|
<bold>true</bold>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Cancel</string>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="KCC.qrc">
|
||||||
|
<normaloff>:/Other/icons/clear.png</normaloff>:/Other/icons/clear.png</iconset>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<widget class="QFrame" name="EditorFrame">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>10</x>
|
||||||
|
<y>10</y>
|
||||||
|
<width>381</width>
|
||||||
|
<height>251</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="formLayoutWidget">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>381</width>
|
||||||
|
<height>250</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<layout class="QFormLayout" name="formLayout">
|
||||||
|
<property name="fieldGrowthPolicy">
|
||||||
|
<enum>QFormLayout::ExpandingFieldsGrow</enum>
|
||||||
|
</property>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string>Series:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QLineEdit" name="SeriesLine"/>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QLabel" name="label_2">
|
||||||
|
<property name="text">
|
||||||
|
<string>Volume:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QLineEdit" name="VolumeLine"/>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QLabel" name="label_3">
|
||||||
|
<property name="text">
|
||||||
|
<string>Number:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<widget class="QLineEdit" name="NumberLine"/>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="0">
|
||||||
|
<widget class="QLabel" name="label_4">
|
||||||
|
<property name="text">
|
||||||
|
<string>Writer:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="1">
|
||||||
|
<widget class="QLineEdit" name="WriterLine"/>
|
||||||
|
</item>
|
||||||
|
<item row="5" column="0">
|
||||||
|
<widget class="QLabel" name="label_5">
|
||||||
|
<property name="text">
|
||||||
|
<string>Penciller:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="5" column="1">
|
||||||
|
<widget class="QLineEdit" name="PencillerLine"/>
|
||||||
|
</item>
|
||||||
|
<item row="6" column="0">
|
||||||
|
<widget class="QLabel" name="label_6">
|
||||||
|
<property name="text">
|
||||||
|
<string>Inker:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="6" column="1">
|
||||||
|
<widget class="QLineEdit" name="InkerLine"/>
|
||||||
|
</item>
|
||||||
|
<item row="7" column="0">
|
||||||
|
<widget class="QLabel" name="label_7">
|
||||||
|
<property name="text">
|
||||||
|
<string>Colorist:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="7" column="1">
|
||||||
|
<widget class="QLineEdit" name="ColoristLine"/>
|
||||||
|
</item>
|
||||||
|
<item row="8" column="0">
|
||||||
|
<widget class="QLabel" name="label_8">
|
||||||
|
<property name="text">
|
||||||
|
<string><html><head/><body><p><a href="https://github.com/ciromattia/kcc/wiki/Manga-Cover-Database-support"><span style=" text-decoration: underline; color:#0000ff;">MUid:</span></a></p></body></html></string>
|
||||||
|
</property>
|
||||||
|
<property name="textFormat">
|
||||||
|
<enum>Qt::RichText</enum>
|
||||||
|
</property>
|
||||||
|
<property name="openExternalLinks">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="8" column="1">
|
||||||
|
<widget class="QLineEdit" name="MUidLine"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
<resources>
|
||||||
|
<include location="KCC.qrc"/>
|
||||||
|
</resources>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
||||||
222
gui/MetaEditor.ui
Normal file
222
gui/MetaEditor.ui
Normal file
@@ -0,0 +1,222 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>MetaEditorDialog</class>
|
||||||
|
<widget class="QDialog" name="MetaEditorDialog">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>400</width>
|
||||||
|
<height>260</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>400</width>
|
||||||
|
<height>260</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>400</width>
|
||||||
|
<height>260</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Metadata editor</string>
|
||||||
|
</property>
|
||||||
|
<property name="windowIcon">
|
||||||
|
<iconset resource="KCC.qrc">
|
||||||
|
<normaloff>:/Icon/icons/comic2ebook.png</normaloff>:/Icon/icons/comic2ebook.png</iconset>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="horizontalLayoutWidget">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>10</x>
|
||||||
|
<y>220</y>
|
||||||
|
<width>381</width>
|
||||||
|
<height>31</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="StatusLabel">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<weight>75</weight>
|
||||||
|
<bold>true</bold>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="styleSheet">
|
||||||
|
<string notr="true">color: rgb(255, 0, 0);</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="OKButton">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<weight>75</weight>
|
||||||
|
<bold>true</bold>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Save</string>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="KCC.qrc">
|
||||||
|
<normaloff>:/Other/icons/convert.png</normaloff>:/Other/icons/convert.png</iconset>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="CancelButton">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<weight>75</weight>
|
||||||
|
<bold>true</bold>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Cancel</string>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="KCC.qrc">
|
||||||
|
<normaloff>:/Other/icons/clear.png</normaloff>:/Other/icons/clear.png</iconset>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<widget class="QFrame" name="EditorFrame">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>10</x>
|
||||||
|
<y>10</y>
|
||||||
|
<width>381</width>
|
||||||
|
<height>211</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="formLayoutWidget">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>381</width>
|
||||||
|
<height>211</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<layout class="QFormLayout" name="formLayout">
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string>Series:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QLineEdit" name="SeriesLine"/>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QLabel" name="label_2">
|
||||||
|
<property name="text">
|
||||||
|
<string>Volume:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QLineEdit" name="VolumeLine"/>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QLabel" name="label_3">
|
||||||
|
<property name="text">
|
||||||
|
<string>Number:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<widget class="QLineEdit" name="NumberLine"/>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="0">
|
||||||
|
<widget class="QLabel" name="label_4">
|
||||||
|
<property name="text">
|
||||||
|
<string>Writer:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="1">
|
||||||
|
<widget class="QLineEdit" name="WriterLine"/>
|
||||||
|
</item>
|
||||||
|
<item row="5" column="0">
|
||||||
|
<widget class="QLabel" name="label_5">
|
||||||
|
<property name="text">
|
||||||
|
<string>Penciller:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="5" column="1">
|
||||||
|
<widget class="QLineEdit" name="PencillerLine"/>
|
||||||
|
</item>
|
||||||
|
<item row="6" column="0">
|
||||||
|
<widget class="QLabel" name="label_6">
|
||||||
|
<property name="text">
|
||||||
|
<string>Inker:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="6" column="1">
|
||||||
|
<widget class="QLineEdit" name="InkerLine"/>
|
||||||
|
</item>
|
||||||
|
<item row="7" column="0">
|
||||||
|
<widget class="QLabel" name="label_7">
|
||||||
|
<property name="text">
|
||||||
|
<string>Colorist:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="7" column="1">
|
||||||
|
<widget class="QLineEdit" name="ColoristLine"/>
|
||||||
|
</item>
|
||||||
|
<item row="8" column="0">
|
||||||
|
<widget class="QLabel" name="label_8">
|
||||||
|
<property name="text">
|
||||||
|
<string><html><head/><body><p><a href="https://github.com/ciromattia/kcc/wiki/Manga-Cover-Database-support"><span style=" text-decoration: underline; color:#0000ff;">MUid:</span></a></p></body></html></string>
|
||||||
|
</property>
|
||||||
|
<property name="textFormat">
|
||||||
|
<enum>Qt::RichText</enum>
|
||||||
|
</property>
|
||||||
|
<property name="openExternalLinks">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="8" column="1">
|
||||||
|
<widget class="QLineEdit" name="MUidLine"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
<resources>
|
||||||
|
<include location="KCC.qrc"/>
|
||||||
|
</resources>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
||||||
BIN
icons/WizardOSX.png
Normal file
BIN
icons/WizardOSX.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 328 KiB |
BIN
icons/editor.png
Normal file
BIN
icons/editor.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
@@ -18,11 +18,6 @@
|
|||||||
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
# PERFORMANCE OF THIS SOFTWARE.
|
# PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
__version__ = '4.4'
|
|
||||||
__license__ = 'ISC'
|
|
||||||
__copyright__ = '2012-2015, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>'
|
|
||||||
__docformat__ = 'restructuredtext en'
|
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
if sys.version_info[0] != 3:
|
if sys.version_info[0] != 3:
|
||||||
print('ERROR: This is Python 3 script!')
|
print('ERROR: This is Python 3 script!')
|
||||||
@@ -32,10 +27,11 @@ from kcc.shared import dependencyCheck
|
|||||||
dependencyCheck(2)
|
dependencyCheck(2)
|
||||||
|
|
||||||
from multiprocessing import freeze_support
|
from multiprocessing import freeze_support
|
||||||
|
from kcc import __version__
|
||||||
from kcc.comic2ebook import main
|
from kcc.comic2ebook import main
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
freeze_support()
|
freeze_support()
|
||||||
print(('comic2ebook v%(__version__)s. Written by Ciro Mattia Gonano and Pawel Jastrzebski.' % globals()))
|
print('comic2ebook v' + __version__ + ' - Written by Ciro Mattia Gonano and Pawel Jastrzebski.')
|
||||||
main(sys.argv[1:])
|
main(sys.argv[1:])
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
@@ -18,11 +18,6 @@
|
|||||||
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
# PERFORMANCE OF THIS SOFTWARE.
|
# PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
__version__ = '4.4'
|
|
||||||
__license__ = 'ISC'
|
|
||||||
__copyright__ = '2012-2015, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>'
|
|
||||||
__docformat__ = 'restructuredtext en'
|
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
if sys.version_info[0] != 3:
|
if sys.version_info[0] != 3:
|
||||||
print('ERROR: This is Python 3 script!')
|
print('ERROR: This is Python 3 script!')
|
||||||
@@ -32,10 +27,11 @@ from kcc.shared import dependencyCheck
|
|||||||
dependencyCheck(1)
|
dependencyCheck(1)
|
||||||
|
|
||||||
from multiprocessing import freeze_support
|
from multiprocessing import freeze_support
|
||||||
|
from kcc import __version__
|
||||||
from kcc.comic2panel import main
|
from kcc.comic2panel import main
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
freeze_support()
|
freeze_support()
|
||||||
print(('comic2panel v%(__version__)s. Written by Ciro Mattia Gonano and Pawel Jastrzebski.' % globals()))
|
print('comic2panel v' + __version__ + ' - Written by Ciro Mattia Gonano and Pawel Jastrzebski.')
|
||||||
main(sys.argv[1:])
|
main(sys.argv[1:])
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
48
kcc.iss
48
kcc.iss
@@ -1,5 +1,5 @@
|
|||||||
#define MyAppName "Kindle Comic Converter"
|
#define MyAppName "Kindle Comic Converter"
|
||||||
#define MyAppVersion "4.4"
|
#define MyAppVersion "4.6.5"
|
||||||
#define MyAppPublisher "Ciro Mattia Gonano, Paweł Jastrzębski"
|
#define MyAppPublisher "Ciro Mattia Gonano, Paweł Jastrzębski"
|
||||||
#define MyAppURL "http://kcc.iosphe.re/"
|
#define MyAppURL "http://kcc.iosphe.re/"
|
||||||
#define MyAppExeName "KCC.exe"
|
#define MyAppExeName "KCC.exe"
|
||||||
@@ -85,3 +85,49 @@ Root: HKCR; SubKey: ".cb7"; ValueType: string; ValueData: "KCCCB7"; Flags: unins
|
|||||||
Root: HKCR; SubKey: "KCCCB7"; ValueType: string; ValueData: "KCC 7z Archive"; Flags: uninsdeletekey; Tasks: CB7association
|
Root: HKCR; SubKey: "KCCCB7"; ValueType: string; ValueData: "KCC 7z Archive"; Flags: uninsdeletekey; Tasks: CB7association
|
||||||
Root: HKCR; SubKey: "KCCCB7\Shell\Open\Command"; ValueType: string; ValueData: """{app}\{#MyAppExeName}"" ""%1"""; Flags: uninsdeletekey; Tasks: CB7association
|
Root: HKCR; SubKey: "KCCCB7\Shell\Open\Command"; ValueType: string; ValueData: """{app}\{#MyAppExeName}"" ""%1"""; Flags: uninsdeletekey; Tasks: CB7association
|
||||||
Root: HKCR; Subkey: "KCCCB7\DefaultIcon"; ValueType: string; ValueData: "{app}\{#MyAppExeName},0"; Flags: uninsdeletevalue; Tasks: CB7association
|
Root: HKCR; Subkey: "KCCCB7\DefaultIcon"; ValueType: string; ValueData: "{app}\{#MyAppExeName},0"; Flags: uninsdeletevalue; Tasks: CB7association
|
||||||
|
|
||||||
|
[Code]
|
||||||
|
function GetUninstallString(): String;
|
||||||
|
var
|
||||||
|
sUnInstPath: String;
|
||||||
|
sUnInstallString: String;
|
||||||
|
begin
|
||||||
|
sUnInstPath := ExpandConstant('Software\Microsoft\Windows\CurrentVersion\Uninstall\{#emit SetupSetting("AppId")}_is1');
|
||||||
|
sUnInstallString := '';
|
||||||
|
if not RegQueryStringValue(HKLM, sUnInstPath, 'UninstallString', sUnInstallString) then
|
||||||
|
RegQueryStringValue(HKCU, sUnInstPath, 'UninstallString', sUnInstallString);
|
||||||
|
Result := sUnInstallString;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function IsUpgrade(): Boolean;
|
||||||
|
begin
|
||||||
|
Result := (GetUninstallString() <> '');
|
||||||
|
end;
|
||||||
|
|
||||||
|
function UnInstallOldVersion(): Integer;
|
||||||
|
var
|
||||||
|
sUnInstallString: String;
|
||||||
|
iResultCode: Integer;
|
||||||
|
begin
|
||||||
|
Result := 0;
|
||||||
|
sUnInstallString := GetUninstallString();
|
||||||
|
if sUnInstallString <> '' then begin
|
||||||
|
sUnInstallString := RemoveQuotes(sUnInstallString);
|
||||||
|
if Exec(sUnInstallString, '/SILENT /NORESTART /SUPPRESSMSGBOXES','', SW_HIDE, ewWaitUntilTerminated, iResultCode) then
|
||||||
|
Result := 3
|
||||||
|
else
|
||||||
|
Result := 2;
|
||||||
|
end else
|
||||||
|
Result := 1;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure CurStepChanged(CurStep: TSetupStep);
|
||||||
|
begin
|
||||||
|
if (CurStep=ssInstall) then
|
||||||
|
begin
|
||||||
|
if (IsUpgrade()) then
|
||||||
|
begin
|
||||||
|
UnInstallOldVersion();
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
6
kcc.py
6
kcc.py
@@ -18,11 +18,6 @@
|
|||||||
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
# PERFORMANCE OF THIS SOFTWARE.
|
# PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
__version__ = '4.4'
|
|
||||||
__license__ = 'ISC'
|
|
||||||
__copyright__ = '2012-2015, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>'
|
|
||||||
__docformat__ = 'restructuredtext en'
|
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
if sys.version_info[0] != 3:
|
if sys.version_info[0] != 3:
|
||||||
print('ERROR: This is Python 3 script!')
|
print('ERROR: This is Python 3 script!')
|
||||||
@@ -35,6 +30,7 @@ if sys.platform.startswith('darwin'):
|
|||||||
os.environ['PATH'] = os.path.dirname(os.path.abspath(__file__)) + '/other/:' + os.environ['PATH']
|
os.environ['PATH'] = os.path.dirname(os.path.abspath(__file__)) + '/other/:' + os.environ['PATH']
|
||||||
else:
|
else:
|
||||||
os.environ['PATH'] = './../Resources:/usr/local/bin:/usr/bin:/bin'
|
os.environ['PATH'] = './../Resources:/usr/local/bin:/usr/bin:/bin'
|
||||||
|
os.system('defaults write com.kindlecomicconverter.KindleComicConverter ApplePersistenceIgnoreState YES')
|
||||||
elif sys.platform.startswith('win'):
|
elif sys.platform.startswith('win'):
|
||||||
if getattr(sys, 'frozen', False):
|
if getattr(sys, 'frozen', False):
|
||||||
os.chdir(os.path.dirname(os.path.abspath(sys.executable)))
|
os.chdir(os.path.dirname(os.path.abspath(sys.executable)))
|
||||||
|
|||||||
147
kcc/KCC_MetaEditor_ui.py
Normal file
147
kcc/KCC_MetaEditor_ui.py
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Form implementation generated from reading ui file 'MetaEditor.ui'
|
||||||
|
#
|
||||||
|
# Created: Sun Feb 8 11:52:00 2015
|
||||||
|
# by: PyQt5 UI code generator 5.4
|
||||||
|
#
|
||||||
|
# WARNING! All changes made in this file will be lost!
|
||||||
|
|
||||||
|
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||||
|
|
||||||
|
class Ui_MetaEditorDialog(object):
|
||||||
|
def setupUi(self, MetaEditorDialog):
|
||||||
|
MetaEditorDialog.setObjectName("MetaEditorDialog")
|
||||||
|
MetaEditorDialog.resize(400, 260)
|
||||||
|
MetaEditorDialog.setMinimumSize(QtCore.QSize(400, 260))
|
||||||
|
MetaEditorDialog.setMaximumSize(QtCore.QSize(400, 260))
|
||||||
|
icon = QtGui.QIcon()
|
||||||
|
icon.addPixmap(QtGui.QPixmap(":/Icon/icons/comic2ebook.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||||
|
MetaEditorDialog.setWindowIcon(icon)
|
||||||
|
self.horizontalLayoutWidget = QtWidgets.QWidget(MetaEditorDialog)
|
||||||
|
self.horizontalLayoutWidget.setGeometry(QtCore.QRect(10, 220, 381, 31))
|
||||||
|
self.horizontalLayoutWidget.setObjectName("horizontalLayoutWidget")
|
||||||
|
self.horizontalLayout = QtWidgets.QHBoxLayout(self.horizontalLayoutWidget)
|
||||||
|
self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
|
||||||
|
self.horizontalLayout.setObjectName("horizontalLayout")
|
||||||
|
self.StatusLabel = QtWidgets.QLabel(self.horizontalLayoutWidget)
|
||||||
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding)
|
||||||
|
sizePolicy.setHorizontalStretch(0)
|
||||||
|
sizePolicy.setVerticalStretch(0)
|
||||||
|
sizePolicy.setHeightForWidth(self.StatusLabel.sizePolicy().hasHeightForWidth())
|
||||||
|
self.StatusLabel.setSizePolicy(sizePolicy)
|
||||||
|
font = QtGui.QFont()
|
||||||
|
font.setBold(True)
|
||||||
|
font.setWeight(75)
|
||||||
|
self.StatusLabel.setFont(font)
|
||||||
|
self.StatusLabel.setStyleSheet("color: rgb(255, 0, 0);")
|
||||||
|
self.StatusLabel.setObjectName("StatusLabel")
|
||||||
|
self.horizontalLayout.addWidget(self.StatusLabel)
|
||||||
|
self.OKButton = QtWidgets.QPushButton(self.horizontalLayoutWidget)
|
||||||
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum)
|
||||||
|
sizePolicy.setHorizontalStretch(0)
|
||||||
|
sizePolicy.setVerticalStretch(0)
|
||||||
|
sizePolicy.setHeightForWidth(self.OKButton.sizePolicy().hasHeightForWidth())
|
||||||
|
self.OKButton.setSizePolicy(sizePolicy)
|
||||||
|
font = QtGui.QFont()
|
||||||
|
font.setBold(True)
|
||||||
|
font.setWeight(75)
|
||||||
|
self.OKButton.setFont(font)
|
||||||
|
icon1 = QtGui.QIcon()
|
||||||
|
icon1.addPixmap(QtGui.QPixmap(":/Other/icons/convert.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||||
|
self.OKButton.setIcon(icon1)
|
||||||
|
self.OKButton.setObjectName("OKButton")
|
||||||
|
self.horizontalLayout.addWidget(self.OKButton)
|
||||||
|
self.CancelButton = QtWidgets.QPushButton(self.horizontalLayoutWidget)
|
||||||
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum)
|
||||||
|
sizePolicy.setHorizontalStretch(0)
|
||||||
|
sizePolicy.setVerticalStretch(0)
|
||||||
|
sizePolicy.setHeightForWidth(self.CancelButton.sizePolicy().hasHeightForWidth())
|
||||||
|
self.CancelButton.setSizePolicy(sizePolicy)
|
||||||
|
font = QtGui.QFont()
|
||||||
|
font.setBold(True)
|
||||||
|
font.setWeight(75)
|
||||||
|
self.CancelButton.setFont(font)
|
||||||
|
icon2 = QtGui.QIcon()
|
||||||
|
icon2.addPixmap(QtGui.QPixmap(":/Other/icons/clear.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||||
|
self.CancelButton.setIcon(icon2)
|
||||||
|
self.CancelButton.setObjectName("CancelButton")
|
||||||
|
self.horizontalLayout.addWidget(self.CancelButton)
|
||||||
|
self.EditorFrame = QtWidgets.QFrame(MetaEditorDialog)
|
||||||
|
self.EditorFrame.setGeometry(QtCore.QRect(10, 10, 381, 211))
|
||||||
|
self.EditorFrame.setObjectName("EditorFrame")
|
||||||
|
self.formLayoutWidget = QtWidgets.QWidget(self.EditorFrame)
|
||||||
|
self.formLayoutWidget.setGeometry(QtCore.QRect(0, 0, 381, 211))
|
||||||
|
self.formLayoutWidget.setObjectName("formLayoutWidget")
|
||||||
|
self.formLayout = QtWidgets.QFormLayout(self.formLayoutWidget)
|
||||||
|
self.formLayout.setContentsMargins(0, 0, 0, 0)
|
||||||
|
self.formLayout.setObjectName("formLayout")
|
||||||
|
self.label = QtWidgets.QLabel(self.formLayoutWidget)
|
||||||
|
self.label.setObjectName("label")
|
||||||
|
self.formLayout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.label)
|
||||||
|
self.SeriesLine = QtWidgets.QLineEdit(self.formLayoutWidget)
|
||||||
|
self.SeriesLine.setObjectName("SeriesLine")
|
||||||
|
self.formLayout.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.SeriesLine)
|
||||||
|
self.label_2 = QtWidgets.QLabel(self.formLayoutWidget)
|
||||||
|
self.label_2.setObjectName("label_2")
|
||||||
|
self.formLayout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.label_2)
|
||||||
|
self.VolumeLine = QtWidgets.QLineEdit(self.formLayoutWidget)
|
||||||
|
self.VolumeLine.setObjectName("VolumeLine")
|
||||||
|
self.formLayout.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.VolumeLine)
|
||||||
|
self.label_3 = QtWidgets.QLabel(self.formLayoutWidget)
|
||||||
|
self.label_3.setObjectName("label_3")
|
||||||
|
self.formLayout.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.label_3)
|
||||||
|
self.NumberLine = QtWidgets.QLineEdit(self.formLayoutWidget)
|
||||||
|
self.NumberLine.setObjectName("NumberLine")
|
||||||
|
self.formLayout.setWidget(3, QtWidgets.QFormLayout.FieldRole, self.NumberLine)
|
||||||
|
self.label_4 = QtWidgets.QLabel(self.formLayoutWidget)
|
||||||
|
self.label_4.setObjectName("label_4")
|
||||||
|
self.formLayout.setWidget(4, QtWidgets.QFormLayout.LabelRole, self.label_4)
|
||||||
|
self.WriterLine = QtWidgets.QLineEdit(self.formLayoutWidget)
|
||||||
|
self.WriterLine.setObjectName("WriterLine")
|
||||||
|
self.formLayout.setWidget(4, QtWidgets.QFormLayout.FieldRole, self.WriterLine)
|
||||||
|
self.label_5 = QtWidgets.QLabel(self.formLayoutWidget)
|
||||||
|
self.label_5.setObjectName("label_5")
|
||||||
|
self.formLayout.setWidget(5, QtWidgets.QFormLayout.LabelRole, self.label_5)
|
||||||
|
self.PencillerLine = QtWidgets.QLineEdit(self.formLayoutWidget)
|
||||||
|
self.PencillerLine.setObjectName("PencillerLine")
|
||||||
|
self.formLayout.setWidget(5, QtWidgets.QFormLayout.FieldRole, self.PencillerLine)
|
||||||
|
self.label_6 = QtWidgets.QLabel(self.formLayoutWidget)
|
||||||
|
self.label_6.setObjectName("label_6")
|
||||||
|
self.formLayout.setWidget(6, QtWidgets.QFormLayout.LabelRole, self.label_6)
|
||||||
|
self.InkerLine = QtWidgets.QLineEdit(self.formLayoutWidget)
|
||||||
|
self.InkerLine.setObjectName("InkerLine")
|
||||||
|
self.formLayout.setWidget(6, QtWidgets.QFormLayout.FieldRole, self.InkerLine)
|
||||||
|
self.label_7 = QtWidgets.QLabel(self.formLayoutWidget)
|
||||||
|
self.label_7.setObjectName("label_7")
|
||||||
|
self.formLayout.setWidget(7, QtWidgets.QFormLayout.LabelRole, self.label_7)
|
||||||
|
self.ColoristLine = QtWidgets.QLineEdit(self.formLayoutWidget)
|
||||||
|
self.ColoristLine.setObjectName("ColoristLine")
|
||||||
|
self.formLayout.setWidget(7, QtWidgets.QFormLayout.FieldRole, self.ColoristLine)
|
||||||
|
self.label_8 = QtWidgets.QLabel(self.formLayoutWidget)
|
||||||
|
self.label_8.setTextFormat(QtCore.Qt.RichText)
|
||||||
|
self.label_8.setOpenExternalLinks(True)
|
||||||
|
self.label_8.setObjectName("label_8")
|
||||||
|
self.formLayout.setWidget(8, QtWidgets.QFormLayout.LabelRole, self.label_8)
|
||||||
|
self.MUidLine = QtWidgets.QLineEdit(self.formLayoutWidget)
|
||||||
|
self.MUidLine.setObjectName("MUidLine")
|
||||||
|
self.formLayout.setWidget(8, QtWidgets.QFormLayout.FieldRole, self.MUidLine)
|
||||||
|
|
||||||
|
self.retranslateUi(MetaEditorDialog)
|
||||||
|
QtCore.QMetaObject.connectSlotsByName(MetaEditorDialog)
|
||||||
|
|
||||||
|
def retranslateUi(self, MetaEditorDialog):
|
||||||
|
_translate = QtCore.QCoreApplication.translate
|
||||||
|
MetaEditorDialog.setWindowTitle(_translate("MetaEditorDialog", "Metadata editor"))
|
||||||
|
self.OKButton.setText(_translate("MetaEditorDialog", "Save"))
|
||||||
|
self.CancelButton.setText(_translate("MetaEditorDialog", "Cancel"))
|
||||||
|
self.label.setText(_translate("MetaEditorDialog", "Series:"))
|
||||||
|
self.label_2.setText(_translate("MetaEditorDialog", "Volume:"))
|
||||||
|
self.label_3.setText(_translate("MetaEditorDialog", "Number:"))
|
||||||
|
self.label_4.setText(_translate("MetaEditorDialog", "Writer:"))
|
||||||
|
self.label_5.setText(_translate("MetaEditorDialog", "Penciller:"))
|
||||||
|
self.label_6.setText(_translate("MetaEditorDialog", "Inker:"))
|
||||||
|
self.label_7.setText(_translate("MetaEditorDialog", "Colorist:"))
|
||||||
|
self.label_8.setText(_translate("MetaEditorDialog", "<html><head/><body><p><a href=\"https://github.com/ciromattia/kcc/wiki/Manga-Cover-Database-support\"><span style=\" text-decoration: underline; color:#0000ff;\">MUid:</span></a></p></body></html>"))
|
||||||
|
|
||||||
|
from . import KCC_rc
|
||||||
148
kcc/KCC_MetaEditor_ui_linux.py
Normal file
148
kcc/KCC_MetaEditor_ui_linux.py
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Form implementation generated from reading ui file 'gui/MetaEditor.ui'
|
||||||
|
#
|
||||||
|
# Created: Sun Feb 8 03:24:23 2015
|
||||||
|
# by: PyQt5 UI code generator 5.2.1
|
||||||
|
#
|
||||||
|
# WARNING! All changes made in this file will be lost!
|
||||||
|
|
||||||
|
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||||
|
|
||||||
|
class Ui_MetaEditorDialog(object):
|
||||||
|
def setupUi(self, MetaEditorDialog):
|
||||||
|
MetaEditorDialog.setObjectName("MetaEditorDialog")
|
||||||
|
MetaEditorDialog.resize(400, 320)
|
||||||
|
MetaEditorDialog.setMinimumSize(QtCore.QSize(400, 320))
|
||||||
|
MetaEditorDialog.setMaximumSize(QtCore.QSize(400, 320))
|
||||||
|
icon = QtGui.QIcon()
|
||||||
|
icon.addPixmap(QtGui.QPixmap(":/Icon/icons/comic2ebook.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||||
|
MetaEditorDialog.setWindowIcon(icon)
|
||||||
|
self.horizontalLayoutWidget = QtWidgets.QWidget(MetaEditorDialog)
|
||||||
|
self.horizontalLayoutWidget.setGeometry(QtCore.QRect(10, 280, 381, 31))
|
||||||
|
self.horizontalLayoutWidget.setObjectName("horizontalLayoutWidget")
|
||||||
|
self.horizontalLayout = QtWidgets.QHBoxLayout(self.horizontalLayoutWidget)
|
||||||
|
self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
|
||||||
|
self.horizontalLayout.setObjectName("horizontalLayout")
|
||||||
|
self.StatusLabel = QtWidgets.QLabel(self.horizontalLayoutWidget)
|
||||||
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding)
|
||||||
|
sizePolicy.setHorizontalStretch(0)
|
||||||
|
sizePolicy.setVerticalStretch(0)
|
||||||
|
sizePolicy.setHeightForWidth(self.StatusLabel.sizePolicy().hasHeightForWidth())
|
||||||
|
self.StatusLabel.setSizePolicy(sizePolicy)
|
||||||
|
font = QtGui.QFont()
|
||||||
|
font.setPointSize(10)
|
||||||
|
font.setBold(True)
|
||||||
|
font.setWeight(75)
|
||||||
|
self.StatusLabel.setFont(font)
|
||||||
|
self.StatusLabel.setStyleSheet("color: rgb(255, 0, 0);")
|
||||||
|
self.StatusLabel.setObjectName("StatusLabel")
|
||||||
|
self.horizontalLayout.addWidget(self.StatusLabel)
|
||||||
|
self.OKButton = QtWidgets.QPushButton(self.horizontalLayoutWidget)
|
||||||
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum)
|
||||||
|
sizePolicy.setHorizontalStretch(0)
|
||||||
|
sizePolicy.setVerticalStretch(0)
|
||||||
|
sizePolicy.setHeightForWidth(self.OKButton.sizePolicy().hasHeightForWidth())
|
||||||
|
self.OKButton.setSizePolicy(sizePolicy)
|
||||||
|
font = QtGui.QFont()
|
||||||
|
font.setBold(True)
|
||||||
|
font.setWeight(75)
|
||||||
|
self.OKButton.setFont(font)
|
||||||
|
icon1 = QtGui.QIcon()
|
||||||
|
icon1.addPixmap(QtGui.QPixmap(":/Other/icons/convert.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||||
|
self.OKButton.setIcon(icon1)
|
||||||
|
self.OKButton.setObjectName("OKButton")
|
||||||
|
self.horizontalLayout.addWidget(self.OKButton)
|
||||||
|
self.CancelButton = QtWidgets.QPushButton(self.horizontalLayoutWidget)
|
||||||
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum)
|
||||||
|
sizePolicy.setHorizontalStretch(0)
|
||||||
|
sizePolicy.setVerticalStretch(0)
|
||||||
|
sizePolicy.setHeightForWidth(self.CancelButton.sizePolicy().hasHeightForWidth())
|
||||||
|
self.CancelButton.setSizePolicy(sizePolicy)
|
||||||
|
font = QtGui.QFont()
|
||||||
|
font.setBold(True)
|
||||||
|
font.setWeight(75)
|
||||||
|
self.CancelButton.setFont(font)
|
||||||
|
icon2 = QtGui.QIcon()
|
||||||
|
icon2.addPixmap(QtGui.QPixmap(":/Other/icons/clear.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||||
|
self.CancelButton.setIcon(icon2)
|
||||||
|
self.CancelButton.setObjectName("CancelButton")
|
||||||
|
self.horizontalLayout.addWidget(self.CancelButton)
|
||||||
|
self.EditorFrame = QtWidgets.QFrame(MetaEditorDialog)
|
||||||
|
self.EditorFrame.setGeometry(QtCore.QRect(10, 10, 381, 271))
|
||||||
|
self.EditorFrame.setObjectName("EditorFrame")
|
||||||
|
self.formLayoutWidget = QtWidgets.QWidget(self.EditorFrame)
|
||||||
|
self.formLayoutWidget.setGeometry(QtCore.QRect(0, 0, 381, 266))
|
||||||
|
self.formLayoutWidget.setObjectName("formLayoutWidget")
|
||||||
|
self.formLayout = QtWidgets.QFormLayout(self.formLayoutWidget)
|
||||||
|
self.formLayout.setContentsMargins(0, 0, 0, 0)
|
||||||
|
self.formLayout.setObjectName("formLayout")
|
||||||
|
self.label = QtWidgets.QLabel(self.formLayoutWidget)
|
||||||
|
self.label.setObjectName("label")
|
||||||
|
self.formLayout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.label)
|
||||||
|
self.SeriesLine = QtWidgets.QLineEdit(self.formLayoutWidget)
|
||||||
|
self.SeriesLine.setObjectName("SeriesLine")
|
||||||
|
self.formLayout.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.SeriesLine)
|
||||||
|
self.label_2 = QtWidgets.QLabel(self.formLayoutWidget)
|
||||||
|
self.label_2.setObjectName("label_2")
|
||||||
|
self.formLayout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.label_2)
|
||||||
|
self.VolumeLine = QtWidgets.QLineEdit(self.formLayoutWidget)
|
||||||
|
self.VolumeLine.setObjectName("VolumeLine")
|
||||||
|
self.formLayout.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.VolumeLine)
|
||||||
|
self.label_3 = QtWidgets.QLabel(self.formLayoutWidget)
|
||||||
|
self.label_3.setObjectName("label_3")
|
||||||
|
self.formLayout.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.label_3)
|
||||||
|
self.NumberLine = QtWidgets.QLineEdit(self.formLayoutWidget)
|
||||||
|
self.NumberLine.setObjectName("NumberLine")
|
||||||
|
self.formLayout.setWidget(3, QtWidgets.QFormLayout.FieldRole, self.NumberLine)
|
||||||
|
self.label_4 = QtWidgets.QLabel(self.formLayoutWidget)
|
||||||
|
self.label_4.setObjectName("label_4")
|
||||||
|
self.formLayout.setWidget(4, QtWidgets.QFormLayout.LabelRole, self.label_4)
|
||||||
|
self.WriterLine = QtWidgets.QLineEdit(self.formLayoutWidget)
|
||||||
|
self.WriterLine.setObjectName("WriterLine")
|
||||||
|
self.formLayout.setWidget(4, QtWidgets.QFormLayout.FieldRole, self.WriterLine)
|
||||||
|
self.label_5 = QtWidgets.QLabel(self.formLayoutWidget)
|
||||||
|
self.label_5.setObjectName("label_5")
|
||||||
|
self.formLayout.setWidget(5, QtWidgets.QFormLayout.LabelRole, self.label_5)
|
||||||
|
self.PencillerLine = QtWidgets.QLineEdit(self.formLayoutWidget)
|
||||||
|
self.PencillerLine.setObjectName("PencillerLine")
|
||||||
|
self.formLayout.setWidget(5, QtWidgets.QFormLayout.FieldRole, self.PencillerLine)
|
||||||
|
self.label_6 = QtWidgets.QLabel(self.formLayoutWidget)
|
||||||
|
self.label_6.setObjectName("label_6")
|
||||||
|
self.formLayout.setWidget(6, QtWidgets.QFormLayout.LabelRole, self.label_6)
|
||||||
|
self.InkerLine = QtWidgets.QLineEdit(self.formLayoutWidget)
|
||||||
|
self.InkerLine.setObjectName("InkerLine")
|
||||||
|
self.formLayout.setWidget(6, QtWidgets.QFormLayout.FieldRole, self.InkerLine)
|
||||||
|
self.label_7 = QtWidgets.QLabel(self.formLayoutWidget)
|
||||||
|
self.label_7.setObjectName("label_7")
|
||||||
|
self.formLayout.setWidget(7, QtWidgets.QFormLayout.LabelRole, self.label_7)
|
||||||
|
self.ColoristLine = QtWidgets.QLineEdit(self.formLayoutWidget)
|
||||||
|
self.ColoristLine.setObjectName("ColoristLine")
|
||||||
|
self.formLayout.setWidget(7, QtWidgets.QFormLayout.FieldRole, self.ColoristLine)
|
||||||
|
self.label_8 = QtWidgets.QLabel(self.formLayoutWidget)
|
||||||
|
self.label_8.setTextFormat(QtCore.Qt.RichText)
|
||||||
|
self.label_8.setOpenExternalLinks(True)
|
||||||
|
self.label_8.setObjectName("label_8")
|
||||||
|
self.formLayout.setWidget(8, QtWidgets.QFormLayout.LabelRole, self.label_8)
|
||||||
|
self.MUidLine = QtWidgets.QLineEdit(self.formLayoutWidget)
|
||||||
|
self.MUidLine.setObjectName("MUidLine")
|
||||||
|
self.formLayout.setWidget(8, QtWidgets.QFormLayout.FieldRole, self.MUidLine)
|
||||||
|
|
||||||
|
self.retranslateUi(MetaEditorDialog)
|
||||||
|
QtCore.QMetaObject.connectSlotsByName(MetaEditorDialog)
|
||||||
|
|
||||||
|
def retranslateUi(self, MetaEditorDialog):
|
||||||
|
_translate = QtCore.QCoreApplication.translate
|
||||||
|
MetaEditorDialog.setWindowTitle(_translate("MetaEditorDialog", "Metadata editor"))
|
||||||
|
self.OKButton.setText(_translate("MetaEditorDialog", "Save"))
|
||||||
|
self.CancelButton.setText(_translate("MetaEditorDialog", "Cancel"))
|
||||||
|
self.label.setText(_translate("MetaEditorDialog", "Series:"))
|
||||||
|
self.label_2.setText(_translate("MetaEditorDialog", "Volume:"))
|
||||||
|
self.label_3.setText(_translate("MetaEditorDialog", "Number:"))
|
||||||
|
self.label_4.setText(_translate("MetaEditorDialog", "Writer:"))
|
||||||
|
self.label_5.setText(_translate("MetaEditorDialog", "Penciller:"))
|
||||||
|
self.label_6.setText(_translate("MetaEditorDialog", "Inker:"))
|
||||||
|
self.label_7.setText(_translate("MetaEditorDialog", "Colorist:"))
|
||||||
|
self.label_8.setText(_translate("MetaEditorDialog", "<html><head/><body><p><a href=\"https://github.com/ciromattia/kcc/wiki/Manga-Cover-Database-support\"><span style=\" text-decoration: underline; color:#0000ff;\">MUid:</span></a></p></body></html>"))
|
||||||
|
|
||||||
|
from . import KCC_rc
|
||||||
149
kcc/KCC_MetaEditor_ui_osx.py
Normal file
149
kcc/KCC_MetaEditor_ui_osx.py
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Form implementation generated from reading ui file '/Users/pawelj/Documents/KCC/gui/MetaEditor.ui'
|
||||||
|
#
|
||||||
|
# Created: Sun Feb 8 12:47:09 2015
|
||||||
|
# by: PyQt5 UI code generator 5.4
|
||||||
|
#
|
||||||
|
# WARNING! All changes made in this file will be lost!
|
||||||
|
|
||||||
|
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||||
|
|
||||||
|
class Ui_MetaEditorDialog(object):
|
||||||
|
def setupUi(self, MetaEditorDialog):
|
||||||
|
MetaEditorDialog.setObjectName("MetaEditorDialog")
|
||||||
|
MetaEditorDialog.resize(400, 295)
|
||||||
|
MetaEditorDialog.setMinimumSize(QtCore.QSize(400, 295))
|
||||||
|
MetaEditorDialog.setMaximumSize(QtCore.QSize(400, 295))
|
||||||
|
icon = QtGui.QIcon()
|
||||||
|
icon.addPixmap(QtGui.QPixmap(":/Icon/icons/comic2ebook.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||||
|
MetaEditorDialog.setWindowIcon(icon)
|
||||||
|
self.horizontalLayoutWidget = QtWidgets.QWidget(MetaEditorDialog)
|
||||||
|
self.horizontalLayoutWidget.setGeometry(QtCore.QRect(10, 260, 381, 32))
|
||||||
|
self.horizontalLayoutWidget.setObjectName("horizontalLayoutWidget")
|
||||||
|
self.horizontalLayout = QtWidgets.QHBoxLayout(self.horizontalLayoutWidget)
|
||||||
|
self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
|
||||||
|
self.horizontalLayout.setObjectName("horizontalLayout")
|
||||||
|
self.StatusLabel = QtWidgets.QLabel(self.horizontalLayoutWidget)
|
||||||
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding)
|
||||||
|
sizePolicy.setHorizontalStretch(0)
|
||||||
|
sizePolicy.setVerticalStretch(0)
|
||||||
|
sizePolicy.setHeightForWidth(self.StatusLabel.sizePolicy().hasHeightForWidth())
|
||||||
|
self.StatusLabel.setSizePolicy(sizePolicy)
|
||||||
|
font = QtGui.QFont()
|
||||||
|
font.setPointSize(10)
|
||||||
|
font.setBold(True)
|
||||||
|
font.setWeight(75)
|
||||||
|
self.StatusLabel.setFont(font)
|
||||||
|
self.StatusLabel.setStyleSheet("color: rgb(255, 0, 0);")
|
||||||
|
self.StatusLabel.setObjectName("StatusLabel")
|
||||||
|
self.horizontalLayout.addWidget(self.StatusLabel)
|
||||||
|
self.OKButton = QtWidgets.QPushButton(self.horizontalLayoutWidget)
|
||||||
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum)
|
||||||
|
sizePolicy.setHorizontalStretch(0)
|
||||||
|
sizePolicy.setVerticalStretch(0)
|
||||||
|
sizePolicy.setHeightForWidth(self.OKButton.sizePolicy().hasHeightForWidth())
|
||||||
|
self.OKButton.setSizePolicy(sizePolicy)
|
||||||
|
font = QtGui.QFont()
|
||||||
|
font.setBold(True)
|
||||||
|
font.setWeight(75)
|
||||||
|
self.OKButton.setFont(font)
|
||||||
|
icon1 = QtGui.QIcon()
|
||||||
|
icon1.addPixmap(QtGui.QPixmap(":/Other/icons/convert.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||||
|
self.OKButton.setIcon(icon1)
|
||||||
|
self.OKButton.setObjectName("OKButton")
|
||||||
|
self.horizontalLayout.addWidget(self.OKButton)
|
||||||
|
self.CancelButton = QtWidgets.QPushButton(self.horizontalLayoutWidget)
|
||||||
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum)
|
||||||
|
sizePolicy.setHorizontalStretch(0)
|
||||||
|
sizePolicy.setVerticalStretch(0)
|
||||||
|
sizePolicy.setHeightForWidth(self.CancelButton.sizePolicy().hasHeightForWidth())
|
||||||
|
self.CancelButton.setSizePolicy(sizePolicy)
|
||||||
|
font = QtGui.QFont()
|
||||||
|
font.setBold(True)
|
||||||
|
font.setWeight(75)
|
||||||
|
self.CancelButton.setFont(font)
|
||||||
|
icon2 = QtGui.QIcon()
|
||||||
|
icon2.addPixmap(QtGui.QPixmap(":/Other/icons/clear.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||||
|
self.CancelButton.setIcon(icon2)
|
||||||
|
self.CancelButton.setObjectName("CancelButton")
|
||||||
|
self.horizontalLayout.addWidget(self.CancelButton)
|
||||||
|
self.EditorFrame = QtWidgets.QFrame(MetaEditorDialog)
|
||||||
|
self.EditorFrame.setGeometry(QtCore.QRect(10, 10, 381, 251))
|
||||||
|
self.EditorFrame.setObjectName("EditorFrame")
|
||||||
|
self.formLayoutWidget = QtWidgets.QWidget(self.EditorFrame)
|
||||||
|
self.formLayoutWidget.setGeometry(QtCore.QRect(0, 0, 381, 250))
|
||||||
|
self.formLayoutWidget.setObjectName("formLayoutWidget")
|
||||||
|
self.formLayout = QtWidgets.QFormLayout(self.formLayoutWidget)
|
||||||
|
self.formLayout.setContentsMargins(0, 0, 0, 0)
|
||||||
|
self.formLayout.setFieldGrowthPolicy(QtWidgets.QFormLayout.ExpandingFieldsGrow)
|
||||||
|
self.formLayout.setObjectName("formLayout")
|
||||||
|
self.label = QtWidgets.QLabel(self.formLayoutWidget)
|
||||||
|
self.label.setObjectName("label")
|
||||||
|
self.formLayout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.label)
|
||||||
|
self.SeriesLine = QtWidgets.QLineEdit(self.formLayoutWidget)
|
||||||
|
self.SeriesLine.setObjectName("SeriesLine")
|
||||||
|
self.formLayout.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.SeriesLine)
|
||||||
|
self.label_2 = QtWidgets.QLabel(self.formLayoutWidget)
|
||||||
|
self.label_2.setObjectName("label_2")
|
||||||
|
self.formLayout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.label_2)
|
||||||
|
self.VolumeLine = QtWidgets.QLineEdit(self.formLayoutWidget)
|
||||||
|
self.VolumeLine.setObjectName("VolumeLine")
|
||||||
|
self.formLayout.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.VolumeLine)
|
||||||
|
self.label_3 = QtWidgets.QLabel(self.formLayoutWidget)
|
||||||
|
self.label_3.setObjectName("label_3")
|
||||||
|
self.formLayout.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.label_3)
|
||||||
|
self.NumberLine = QtWidgets.QLineEdit(self.formLayoutWidget)
|
||||||
|
self.NumberLine.setObjectName("NumberLine")
|
||||||
|
self.formLayout.setWidget(3, QtWidgets.QFormLayout.FieldRole, self.NumberLine)
|
||||||
|
self.label_4 = QtWidgets.QLabel(self.formLayoutWidget)
|
||||||
|
self.label_4.setObjectName("label_4")
|
||||||
|
self.formLayout.setWidget(4, QtWidgets.QFormLayout.LabelRole, self.label_4)
|
||||||
|
self.WriterLine = QtWidgets.QLineEdit(self.formLayoutWidget)
|
||||||
|
self.WriterLine.setObjectName("WriterLine")
|
||||||
|
self.formLayout.setWidget(4, QtWidgets.QFormLayout.FieldRole, self.WriterLine)
|
||||||
|
self.label_5 = QtWidgets.QLabel(self.formLayoutWidget)
|
||||||
|
self.label_5.setObjectName("label_5")
|
||||||
|
self.formLayout.setWidget(5, QtWidgets.QFormLayout.LabelRole, self.label_5)
|
||||||
|
self.PencillerLine = QtWidgets.QLineEdit(self.formLayoutWidget)
|
||||||
|
self.PencillerLine.setObjectName("PencillerLine")
|
||||||
|
self.formLayout.setWidget(5, QtWidgets.QFormLayout.FieldRole, self.PencillerLine)
|
||||||
|
self.label_6 = QtWidgets.QLabel(self.formLayoutWidget)
|
||||||
|
self.label_6.setObjectName("label_6")
|
||||||
|
self.formLayout.setWidget(6, QtWidgets.QFormLayout.LabelRole, self.label_6)
|
||||||
|
self.InkerLine = QtWidgets.QLineEdit(self.formLayoutWidget)
|
||||||
|
self.InkerLine.setObjectName("InkerLine")
|
||||||
|
self.formLayout.setWidget(6, QtWidgets.QFormLayout.FieldRole, self.InkerLine)
|
||||||
|
self.label_7 = QtWidgets.QLabel(self.formLayoutWidget)
|
||||||
|
self.label_7.setObjectName("label_7")
|
||||||
|
self.formLayout.setWidget(7, QtWidgets.QFormLayout.LabelRole, self.label_7)
|
||||||
|
self.ColoristLine = QtWidgets.QLineEdit(self.formLayoutWidget)
|
||||||
|
self.ColoristLine.setObjectName("ColoristLine")
|
||||||
|
self.formLayout.setWidget(7, QtWidgets.QFormLayout.FieldRole, self.ColoristLine)
|
||||||
|
self.label_8 = QtWidgets.QLabel(self.formLayoutWidget)
|
||||||
|
self.label_8.setTextFormat(QtCore.Qt.RichText)
|
||||||
|
self.label_8.setOpenExternalLinks(True)
|
||||||
|
self.label_8.setObjectName("label_8")
|
||||||
|
self.formLayout.setWidget(8, QtWidgets.QFormLayout.LabelRole, self.label_8)
|
||||||
|
self.MUidLine = QtWidgets.QLineEdit(self.formLayoutWidget)
|
||||||
|
self.MUidLine.setObjectName("MUidLine")
|
||||||
|
self.formLayout.setWidget(8, QtWidgets.QFormLayout.FieldRole, self.MUidLine)
|
||||||
|
|
||||||
|
self.retranslateUi(MetaEditorDialog)
|
||||||
|
QtCore.QMetaObject.connectSlotsByName(MetaEditorDialog)
|
||||||
|
|
||||||
|
def retranslateUi(self, MetaEditorDialog):
|
||||||
|
_translate = QtCore.QCoreApplication.translate
|
||||||
|
MetaEditorDialog.setWindowTitle(_translate("MetaEditorDialog", "Metadata editor"))
|
||||||
|
self.OKButton.setText(_translate("MetaEditorDialog", "Save"))
|
||||||
|
self.CancelButton.setText(_translate("MetaEditorDialog", "Cancel"))
|
||||||
|
self.label.setText(_translate("MetaEditorDialog", "Series:"))
|
||||||
|
self.label_2.setText(_translate("MetaEditorDialog", "Volume:"))
|
||||||
|
self.label_3.setText(_translate("MetaEditorDialog", "Number:"))
|
||||||
|
self.label_4.setText(_translate("MetaEditorDialog", "Writer:"))
|
||||||
|
self.label_5.setText(_translate("MetaEditorDialog", "Penciller:"))
|
||||||
|
self.label_6.setText(_translate("MetaEditorDialog", "Inker:"))
|
||||||
|
self.label_7.setText(_translate("MetaEditorDialog", "Colorist:"))
|
||||||
|
self.label_8.setText(_translate("MetaEditorDialog", "<html><head/><body><p><a href=\"https://github.com/ciromattia/kcc/wiki/Manga-Cover-Database-support\"><span style=\" text-decoration: underline; color:#0000ff;\">MUid:</span></a></p></body></html>"))
|
||||||
|
|
||||||
|
from . import KCC_rc
|
||||||
290
kcc/KCC_gui.py
290
kcc/KCC_gui.py
@@ -17,35 +17,41 @@
|
|||||||
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
# PERFORMANCE OF THIS SOFTWARE.
|
# PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
__version__ = '4.4'
|
|
||||||
__license__ = 'ISC'
|
|
||||||
__copyright__ = '2012-2015, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>'
|
|
||||||
__docformat__ = 'restructuredtext en'
|
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
from urllib.parse import unquote
|
from urllib.parse import unquote
|
||||||
from urllib.request import urlopen, urlretrieve
|
from urllib.request import urlopen, urlretrieve, Request
|
||||||
from socket import gethostbyname_ex, gethostname
|
from socket import gethostbyname_ex, gethostname
|
||||||
from traceback import format_tb
|
from time import sleep, time
|
||||||
from time import sleep
|
from datetime import datetime
|
||||||
from shutil import move
|
from shutil import move
|
||||||
from http.server import BaseHTTPRequestHandler, HTTPServer
|
from http.server import BaseHTTPRequestHandler, HTTPServer
|
||||||
from socketserver import ThreadingMixIn
|
from socketserver import ThreadingMixIn
|
||||||
from subprocess import STDOUT, PIPE
|
from subprocess import STDOUT, PIPE
|
||||||
from PyQt5 import QtGui, QtCore, QtWidgets, QtNetwork
|
from PyQt5 import QtGui, QtCore, QtWidgets, QtNetwork
|
||||||
from xml.dom.minidom import parse
|
from xml.dom.minidom import parse, Document
|
||||||
from psutil import Popen, Process
|
from psutil import Popen, Process
|
||||||
from copy import copy
|
from copy import copy
|
||||||
from .shared import md5Checksum, HTMLStripper
|
from distutils.version import StrictVersion
|
||||||
|
from xml.sax.saxutils import escape
|
||||||
|
from platform import platform
|
||||||
|
from .shared import md5Checksum, HTMLStripper, sanitizeTrace
|
||||||
|
from . import __version__
|
||||||
from . import comic2ebook
|
from . import comic2ebook
|
||||||
from . import KCC_rc_web
|
from . import KCC_rc_web
|
||||||
|
from . import metadata
|
||||||
if sys.platform.startswith('darwin'):
|
if sys.platform.startswith('darwin'):
|
||||||
from . import KCC_ui_osx as KCC_ui
|
from . import KCC_ui_osx as KCC_ui
|
||||||
elif sys.platform.startswith('linux'):
|
elif sys.platform.startswith('linux'):
|
||||||
from . import KCC_ui_linux as KCC_ui
|
from . import KCC_ui_linux as KCC_ui
|
||||||
else:
|
else:
|
||||||
from . import KCC_ui
|
from . import KCC_ui
|
||||||
|
if sys.platform.startswith('darwin'):
|
||||||
|
from . import KCC_MetaEditor_ui_osx as KCC_MetaEditor_ui
|
||||||
|
elif sys.platform.startswith('linux'):
|
||||||
|
from . import KCC_MetaEditor_ui_linux as KCC_MetaEditor_ui
|
||||||
|
else:
|
||||||
|
from . import KCC_MetaEditor_ui
|
||||||
|
|
||||||
|
|
||||||
class QApplicationMessaging(QtWidgets.QApplication):
|
class QApplicationMessaging(QtWidgets.QApplication):
|
||||||
@@ -60,7 +66,6 @@ class QApplicationMessaging(QtWidgets.QApplication):
|
|||||||
socket.connectToServer(self._key, QtCore.QIODevice.WriteOnly)
|
socket.connectToServer(self._key, QtCore.QIODevice.WriteOnly)
|
||||||
if not socket.waitForConnected(self._timeout):
|
if not socket.waitForConnected(self._timeout):
|
||||||
self._server = QtNetwork.QLocalServer(self)
|
self._server = QtNetwork.QLocalServer(self)
|
||||||
# noinspection PyUnresolvedReferences
|
|
||||||
self._server.newConnection.connect(self.handleMessage)
|
self._server.newConnection.connect(self.handleMessage)
|
||||||
self._server.listen(self._key)
|
self._server.listen(self._key)
|
||||||
else:
|
else:
|
||||||
@@ -103,7 +108,6 @@ class QMainWindowKCC(QtWidgets.QMainWindow):
|
|||||||
showDialog = QtCore.pyqtSignal(str, str)
|
showDialog = QtCore.pyqtSignal(str, str)
|
||||||
hideProgressBar = QtCore.pyqtSignal()
|
hideProgressBar = QtCore.pyqtSignal()
|
||||||
forceShutdown = QtCore.pyqtSignal()
|
forceShutdown = QtCore.pyqtSignal()
|
||||||
dialogAnswer = QtCore.pyqtSignal(int)
|
|
||||||
|
|
||||||
|
|
||||||
class Icons:
|
class Icons:
|
||||||
@@ -134,7 +138,7 @@ class Icons:
|
|||||||
|
|
||||||
|
|
||||||
class WebServerHandler(BaseHTTPRequestHandler):
|
class WebServerHandler(BaseHTTPRequestHandler):
|
||||||
# noinspection PyAttributeOutsideInit, PyArgumentList
|
# noinspection PyAttributeOutsideInit
|
||||||
def do_GET(self):
|
def do_GET(self):
|
||||||
if self.path == '/':
|
if self.path == '/':
|
||||||
self.path = '/index.html'
|
self.path = '/index.html'
|
||||||
@@ -233,38 +237,46 @@ class VersionThread(QtCore.QThread):
|
|||||||
QtCore.QThread.__init__(self)
|
QtCore.QThread.__init__(self)
|
||||||
self.newVersion = ''
|
self.newVersion = ''
|
||||||
self.md5 = ''
|
self.md5 = ''
|
||||||
|
self.barProgress = 0
|
||||||
|
self.answer = None
|
||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
self.wait()
|
self.wait()
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
try:
|
try:
|
||||||
XML = urlopen('http://kcc.iosphe.re/Version.php')
|
XML = parse(urlopen(Request('https://kcc.iosphe.re/Version/',
|
||||||
XML = parse(XML)
|
headers={'User-Agent': 'KindleComicConverter/' + __version__})))
|
||||||
except Exception:
|
except Exception:
|
||||||
return
|
return
|
||||||
latestVersion = XML.childNodes[0].getElementsByTagName('latest')[0].childNodes[0].toxml()
|
latestVersion = XML.childNodes[0].getElementsByTagName('LatestVersion')[0].childNodes[0].toxml()
|
||||||
if tuple(map(int, (latestVersion.split(".")))) > tuple(map(int, (__version__.split(".")))):
|
if StrictVersion(latestVersion) > StrictVersion(__version__):
|
||||||
if sys.platform.startswith('win'):
|
if sys.platform.startswith('win'):
|
||||||
self.newVersion = latestVersion
|
self.newVersion = latestVersion
|
||||||
self.md5 = XML.childNodes[0].getElementsByTagName('WindowsMD5')[0].childNodes[0].toxml()
|
self.md5 = XML.childNodes[0].getElementsByTagName('MD5')[0].childNodes[0].toxml()
|
||||||
MW.showDialog.emit('<b>New version released!</b> <a href="https://github.com/ciromattia/kcc/releases/">'
|
MW.showDialog.emit('<b>New version released!</b> <a href="https://github.com/ciromattia/kcc/releases/">'
|
||||||
'See changelog.</a><<br/><br/>Installed version: ' + __version__ +
|
'See changelog.</a><br/><br/>Installed version: ' + __version__ +
|
||||||
'<br/>Current version: ' + latestVersion +
|
'<br/>Current version: ' + latestVersion +
|
||||||
'<br/><br/>Would you like to start automatic update?', 'question')
|
'<br/><br/>Would you like to start automatic update?', 'question')
|
||||||
|
self.getNewVersion()
|
||||||
else:
|
else:
|
||||||
MW.addMessage.emit('<a href="http://kcc.iosphe.re/">'
|
MW.addMessage.emit('<a href="https://kcc.iosphe.re/">'
|
||||||
'<b>New version is available!</b></a> '
|
'<b>New version is available!</b></a> '
|
||||||
'(<a href="https://github.com/ciromattia/kcc/releases/">'
|
'(<a href="https://github.com/ciromattia/kcc/releases/">'
|
||||||
'Changelog</a>)', 'warning', False)
|
'Changelog</a>)', 'warning', False)
|
||||||
|
|
||||||
def getNewVersion(self, dialogAnswer):
|
def setAnswer(self, dialogAnswer):
|
||||||
if dialogAnswer == QtWidgets.QMessageBox.Yes:
|
self.answer = dialogAnswer
|
||||||
|
|
||||||
|
def getNewVersion(self):
|
||||||
|
while self.answer is None:
|
||||||
|
sleep(1)
|
||||||
|
if self.answer == QtWidgets.QMessageBox.Yes:
|
||||||
try:
|
try:
|
||||||
MW.modeConvert.emit(-1)
|
MW.modeConvert.emit(-1)
|
||||||
MW.progressBarTick.emit('Downloading update')
|
MW.progressBarTick.emit('Downloading update')
|
||||||
path = urlretrieve('http://kcc.iosphe.re/Windows/KindleComicConverter_win_'
|
path = urlretrieve('https://kcc.iosphe.re/Windows/KindleComicConverter_win_' +
|
||||||
+ self.newVersion + '.exe', reporthook=self.getNewVersionTick)
|
self.newVersion + '.exe', reporthook=self.getNewVersionTick)
|
||||||
if self.md5 != md5Checksum(path[0]):
|
if self.md5 != md5Checksum(path[0]):
|
||||||
raise Exception
|
raise Exception
|
||||||
move(path[0], path[0] + '.exe')
|
move(path[0], path[0] + '.exe')
|
||||||
@@ -278,8 +290,11 @@ class VersionThread(QtCore.QThread):
|
|||||||
MW.modeConvert.emit(1)
|
MW.modeConvert.emit(1)
|
||||||
|
|
||||||
def getNewVersionTick(self, size, blockSize, totalSize):
|
def getNewVersionTick(self, size, blockSize, totalSize):
|
||||||
|
progress = int((size / (totalSize // blockSize)) * 100)
|
||||||
if size == 0:
|
if size == 0:
|
||||||
MW.progressBarTick.emit(str(int(totalSize / blockSize)))
|
MW.progressBarTick.emit('100')
|
||||||
|
if progress > self.barProgress:
|
||||||
|
self.barProgress = progress
|
||||||
MW.progressBarTick.emit('tick')
|
MW.progressBarTick.emit('tick')
|
||||||
|
|
||||||
|
|
||||||
@@ -308,7 +323,6 @@ class ProgressThread(QtCore.QThread):
|
|||||||
|
|
||||||
|
|
||||||
class WorkerThread(QtCore.QThread):
|
class WorkerThread(QtCore.QThread):
|
||||||
# noinspection PyArgumentList
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
QtCore.QThread.__init__(self)
|
QtCore.QThread.__init__(self)
|
||||||
self.conversionAlive = False
|
self.conversionAlive = False
|
||||||
@@ -333,12 +347,6 @@ class WorkerThread(QtCore.QThread):
|
|||||||
MW.addTrayMessage.emit('Conversion interrupted.', 'Critical')
|
MW.addTrayMessage.emit('Conversion interrupted.', 'Critical')
|
||||||
MW.modeConvert.emit(1)
|
MW.modeConvert.emit(1)
|
||||||
|
|
||||||
def sanitizeTrace(self, traceback):
|
|
||||||
return ''.join(format_tb(traceback))\
|
|
||||||
.replace('C:\\Users\\AcidWeb\\Documents\\Projekty\\KCC\\', '')\
|
|
||||||
.replace('C:\\Python34\\', '')\
|
|
||||||
.replace('C:\\Python34_64\\', '')
|
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
MW.modeConvert.emit(0)
|
MW.modeConvert.emit(0)
|
||||||
|
|
||||||
@@ -360,9 +368,6 @@ class WorkerThread(QtCore.QThread):
|
|||||||
elif GUI.QualityBox.checkState() == 2:
|
elif GUI.QualityBox.checkState() == 2:
|
||||||
options.quality = 2
|
options.quality = 2
|
||||||
options.format = str(GUI.FormatBox.currentText())
|
options.format = str(GUI.FormatBox.currentText())
|
||||||
if GUI.currentMode == 1:
|
|
||||||
if 'KFH' in profile:
|
|
||||||
options.upscale = True
|
|
||||||
|
|
||||||
# Advanced mode settings
|
# Advanced mode settings
|
||||||
if GUI.currentMode > 1:
|
if GUI.currentMode > 1:
|
||||||
@@ -425,16 +430,20 @@ class WorkerThread(QtCore.QThread):
|
|||||||
GUI.progress.content = ''
|
GUI.progress.content = ''
|
||||||
self.errors = True
|
self.errors = True
|
||||||
MW.addMessage.emit(str(warn), 'warning', False)
|
MW.addMessage.emit(str(warn), 'warning', False)
|
||||||
MW.addMessage.emit('Failed to create output file!', 'error', False)
|
MW.addMessage.emit('Error during conversion! Please consult '
|
||||||
MW.addTrayMessage.emit('Failed to create output file!', 'Critical')
|
'<a href="https://github.com/ciromattia/kcc/wiki/Error-messages">wiki</a> '
|
||||||
|
'for more details.', 'error', False)
|
||||||
|
MW.addTrayMessage.emit('Error during conversion!', 'Critical')
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
GUI.progress.content = ''
|
GUI.progress.content = ''
|
||||||
self.errors = True
|
self.errors = True
|
||||||
_, _, traceback = sys.exc_info()
|
_, _, traceback = sys.exc_info()
|
||||||
MW.showDialog.emit("Error during conversion %s:\n\n%s\n\nTraceback:\n%s"
|
MW.showDialog.emit("Error during conversion %s:\n\n%s\n\nTraceback:\n%s"
|
||||||
% (jobargv[-1], str(err), self.sanitizeTrace(traceback)), 'error')
|
% (jobargv[-1], str(err), sanitizeTrace(traceback)), 'error')
|
||||||
MW.addMessage.emit('Failed to create EPUB!', 'error', False)
|
MW.addMessage.emit('Error during conversion! Please consult '
|
||||||
MW.addTrayMessage.emit('Failed to create EPUB!', 'Critical')
|
'<a href="https://github.com/ciromattia/kcc/wiki/Error-messages">wiki</a> '
|
||||||
|
'for more details.', 'error', False)
|
||||||
|
MW.addTrayMessage.emit('Error during conversion!', 'Critical')
|
||||||
if not self.conversionAlive:
|
if not self.conversionAlive:
|
||||||
for item in outputPath:
|
for item in outputPath:
|
||||||
if os.path.exists(item):
|
if os.path.exists(item):
|
||||||
@@ -449,7 +458,7 @@ class WorkerThread(QtCore.QThread):
|
|||||||
MW.addMessage.emit('Creating EPUB files... <b>Done!</b>', 'info', True)
|
MW.addMessage.emit('Creating EPUB files... <b>Done!</b>', 'info', True)
|
||||||
if str(GUI.FormatBox.currentText()) == 'MOBI':
|
if str(GUI.FormatBox.currentText()) == 'MOBI':
|
||||||
MW.progressBarTick.emit('Creating MOBI files')
|
MW.progressBarTick.emit('Creating MOBI files')
|
||||||
MW.progressBarTick.emit(str(len(outputPath)*2+1))
|
MW.progressBarTick.emit(str(len(outputPath) * 2 + 1))
|
||||||
MW.progressBarTick.emit('tick')
|
MW.progressBarTick.emit('tick')
|
||||||
MW.addMessage.emit('Creating MOBI files', 'info', False)
|
MW.addMessage.emit('Creating MOBI files', 'info', False)
|
||||||
GUI.progress.content = 'Creating MOBI files'
|
GUI.progress.content = 'Creating MOBI files'
|
||||||
@@ -488,7 +497,7 @@ class WorkerThread(QtCore.QThread):
|
|||||||
GUI.progress.content = ''
|
GUI.progress.content = ''
|
||||||
mobiPath = item.replace('.epub', '.mobi')
|
mobiPath = item.replace('.epub', '.mobi')
|
||||||
os.remove(mobiPath + '_toclean')
|
os.remove(mobiPath + '_toclean')
|
||||||
if GUI.targetDirectory and GUI.targetDirectory != os.path.split(mobiPath)[0]:
|
if GUI.targetDirectory and GUI.targetDirectory != os.path.dirname(mobiPath):
|
||||||
try:
|
try:
|
||||||
move(mobiPath, GUI.targetDirectory)
|
move(mobiPath, GUI.targetDirectory)
|
||||||
mobiPath = os.path.join(GUI.targetDirectory, os.path.basename(mobiPath))
|
mobiPath = os.path.join(GUI.targetDirectory, os.path.basename(mobiPath))
|
||||||
@@ -508,7 +517,7 @@ class WorkerThread(QtCore.QThread):
|
|||||||
MW.addTrayMessage.emit('Failed to process MOBI file!', 'Critical')
|
MW.addTrayMessage.emit('Failed to process MOBI file!', 'Critical')
|
||||||
else:
|
else:
|
||||||
GUI.progress.content = ''
|
GUI.progress.content = ''
|
||||||
epubSize = (os.path.getsize(self.kindlegenErrorCode[2]))//1024//1024
|
epubSize = (os.path.getsize(self.kindlegenErrorCode[2])) // 1024 // 1024
|
||||||
for item in outputPath:
|
for item in outputPath:
|
||||||
if os.path.exists(item):
|
if os.path.exists(item):
|
||||||
os.remove(item)
|
os.remove(item)
|
||||||
@@ -524,7 +533,7 @@ class WorkerThread(QtCore.QThread):
|
|||||||
False)
|
False)
|
||||||
else:
|
else:
|
||||||
for item in outputPath:
|
for item in outputPath:
|
||||||
if GUI.targetDirectory and GUI.targetDirectory != os.path.split(item)[0]:
|
if GUI.targetDirectory and GUI.targetDirectory != os.path.dirname(item):
|
||||||
try:
|
try:
|
||||||
move(item, GUI.targetDirectory)
|
move(item, GUI.targetDirectory)
|
||||||
item = os.path.join(GUI.targetDirectory, os.path.basename(item))
|
item = os.path.join(GUI.targetDirectory, os.path.basename(item))
|
||||||
@@ -535,6 +544,7 @@ class WorkerThread(QtCore.QThread):
|
|||||||
GUI.progress.stop()
|
GUI.progress.stop()
|
||||||
MW.hideProgressBar.emit()
|
MW.hideProgressBar.emit()
|
||||||
GUI.needClean = True
|
GUI.needClean = True
|
||||||
|
if not self.errors:
|
||||||
MW.addMessage.emit('<b>All jobs completed.</b>', 'info', False)
|
MW.addMessage.emit('<b>All jobs completed.</b>', 'info', False)
|
||||||
MW.addTrayMessage.emit('All jobs completed.', 'Information')
|
MW.addTrayMessage.emit('All jobs completed.', 'Information')
|
||||||
MW.modeConvert.emit(1)
|
MW.modeConvert.emit(1)
|
||||||
@@ -542,9 +552,9 @@ class WorkerThread(QtCore.QThread):
|
|||||||
|
|
||||||
class SystemTrayIcon(QtWidgets.QSystemTrayIcon):
|
class SystemTrayIcon(QtWidgets.QSystemTrayIcon):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
if self.isSystemTrayAvailable():
|
if self.isSystemTrayAvailable():
|
||||||
QtWidgets.QSystemTrayIcon.__init__(self, GUI.icons.programIcon, MW)
|
QtWidgets.QSystemTrayIcon.__init__(self, GUI.icons.programIcon, MW)
|
||||||
# noinspection PyUnresolvedReferences
|
|
||||||
self.activated.connect(self.catchClicks)
|
self.activated.connect(self.catchClicks)
|
||||||
|
|
||||||
def catchClicks(self):
|
def catchClicks(self):
|
||||||
@@ -597,6 +607,36 @@ class KCCGUI(KCC_ui.Ui_KCC):
|
|||||||
GUI.JobList.addItem(fname)
|
GUI.JobList.addItem(fname)
|
||||||
GUI.JobList.scrollToBottom()
|
GUI.JobList.scrollToBottom()
|
||||||
|
|
||||||
|
def selectFileMetaEditor(self):
|
||||||
|
if self.UnRAR:
|
||||||
|
if self.sevenza:
|
||||||
|
fname = QtWidgets.QFileDialog.getOpenFileName(MW, 'Select file', self.lastPath,
|
||||||
|
'Comic (*.cbz *.cbr *.cb7)')
|
||||||
|
else:
|
||||||
|
fname = QtWidgets.QFileDialog.getOpenFileName(MW, 'Select file', self.lastPath,
|
||||||
|
'Comic (*.cbz *.cbr)')
|
||||||
|
else:
|
||||||
|
if self.sevenza:
|
||||||
|
fname = QtWidgets.QFileDialog.getOpenFileName(MW, 'Select file', self.lastPath,
|
||||||
|
'Comic (*.cbz *.cb7)')
|
||||||
|
else:
|
||||||
|
fname = QtWidgets.QFileDialog.getOpenFileName(MW, 'Select file', self.lastPath,
|
||||||
|
'Comic (*.cbz)')
|
||||||
|
if fname[0] != '':
|
||||||
|
if sys.platform.startswith('win'):
|
||||||
|
fname = fname[0].replace('/', '\\')
|
||||||
|
else:
|
||||||
|
fname = fname[0]
|
||||||
|
self.lastPath = os.path.abspath(os.path.join(fname, os.pardir))
|
||||||
|
try:
|
||||||
|
self.editor.loadData(fname)
|
||||||
|
except Exception as err:
|
||||||
|
_, _, traceback = sys.exc_info()
|
||||||
|
self.showDialog("Failed to parse metadata!\n\n%s\n\nTraceback:\n%s"
|
||||||
|
% (str(err), sanitizeTrace(traceback)), 'error')
|
||||||
|
else:
|
||||||
|
self.editor.ui.exec_()
|
||||||
|
|
||||||
def clearJobs(self):
|
def clearJobs(self):
|
||||||
GUI.JobList.clear()
|
GUI.JobList.clear()
|
||||||
|
|
||||||
@@ -611,6 +651,7 @@ class KCCGUI(KCC_ui.Ui_KCC):
|
|||||||
MW.setMinimumSize(QtCore.QSize(420, 287))
|
MW.setMinimumSize(QtCore.QSize(420, 287))
|
||||||
MW.resize(420, 287)
|
MW.resize(420, 287)
|
||||||
GUI.BasicModeButton.setEnabled(True)
|
GUI.BasicModeButton.setEnabled(True)
|
||||||
|
GUI.EditorButton.setEnabled(True)
|
||||||
GUI.AdvModeButton.setEnabled(True)
|
GUI.AdvModeButton.setEnabled(True)
|
||||||
GUI.BasicModeButton.setStyleSheet('font-weight:Bold;')
|
GUI.BasicModeButton.setStyleSheet('font-weight:Bold;')
|
||||||
GUI.AdvModeButton.setStyleSheet('font-weight:Normal;')
|
GUI.AdvModeButton.setStyleSheet('font-weight:Normal;')
|
||||||
@@ -640,6 +681,7 @@ class KCCGUI(KCC_ui.Ui_KCC):
|
|||||||
MW.setMinimumSize(QtCore.QSize(420, 365))
|
MW.setMinimumSize(QtCore.QSize(420, 365))
|
||||||
MW.resize(420, 365)
|
MW.resize(420, 365)
|
||||||
GUI.BasicModeButton.setEnabled(True)
|
GUI.BasicModeButton.setEnabled(True)
|
||||||
|
GUI.EditorButton.setEnabled(True)
|
||||||
GUI.AdvModeButton.setEnabled(True)
|
GUI.AdvModeButton.setEnabled(True)
|
||||||
GUI.BasicModeButton.setStyleSheet('font-weight:Normal;')
|
GUI.BasicModeButton.setStyleSheet('font-weight:Normal;')
|
||||||
GUI.AdvModeButton.setStyleSheet('font-weight:Bold;')
|
GUI.AdvModeButton.setStyleSheet('font-weight:Bold;')
|
||||||
@@ -669,6 +711,7 @@ class KCCGUI(KCC_ui.Ui_KCC):
|
|||||||
MW.setMinimumSize(QtCore.QSize(420, 397))
|
MW.setMinimumSize(QtCore.QSize(420, 397))
|
||||||
MW.resize(420, 397)
|
MW.resize(420, 397)
|
||||||
GUI.BasicModeButton.setEnabled(False)
|
GUI.BasicModeButton.setEnabled(False)
|
||||||
|
GUI.EditorButton.setEnabled(True)
|
||||||
GUI.AdvModeButton.setEnabled(False)
|
GUI.AdvModeButton.setEnabled(False)
|
||||||
GUI.BasicModeButton.setStyleSheet('font-weight:Normal;')
|
GUI.BasicModeButton.setStyleSheet('font-weight:Normal;')
|
||||||
GUI.AdvModeButton.setStyleSheet('font-weight:Normal;')
|
GUI.AdvModeButton.setStyleSheet('font-weight:Normal;')
|
||||||
@@ -699,6 +742,7 @@ class KCCGUI(KCC_ui.Ui_KCC):
|
|||||||
status = True
|
status = True
|
||||||
if self.currentMode != 3:
|
if self.currentMode != 3:
|
||||||
GUI.BasicModeButton.setEnabled(status)
|
GUI.BasicModeButton.setEnabled(status)
|
||||||
|
GUI.EditorButton.setEnabled(status)
|
||||||
GUI.AdvModeButton.setEnabled(status)
|
GUI.AdvModeButton.setEnabled(status)
|
||||||
if self.currentMode != 1:
|
if self.currentMode != 1:
|
||||||
GUI.FormatBox.setEnabled(status)
|
GUI.FormatBox.setEnabled(status)
|
||||||
@@ -798,7 +842,7 @@ class KCCGUI(KCC_ui.Ui_KCC):
|
|||||||
|
|
||||||
def changeGamma(self, value):
|
def changeGamma(self, value):
|
||||||
value = float(value)
|
value = float(value)
|
||||||
value = '%.2f' % (value/100)
|
value = '%.2f' % (value / 100)
|
||||||
if float(value) <= 0.09:
|
if float(value) <= 0.09:
|
||||||
GUI.GammaLabel.setText('Gamma: Auto')
|
GUI.GammaLabel.setText('Gamma: Auto')
|
||||||
else:
|
else:
|
||||||
@@ -866,7 +910,7 @@ class KCCGUI(KCC_ui.Ui_KCC):
|
|||||||
else:
|
else:
|
||||||
item = QtWidgets.QListWidgetItem(' ' + self.stripTags(message))
|
item = QtWidgets.QListWidgetItem(' ' + self.stripTags(message))
|
||||||
if replace:
|
if replace:
|
||||||
GUI.JobList.takeItem(GUI.JobList.count()-1)
|
GUI.JobList.takeItem(GUI.JobList.count() - 1)
|
||||||
# Due to lack of HTML support in QListWidgetItem we overlay text field with QLabel
|
# Due to lack of HTML support in QListWidgetItem we overlay text field with QLabel
|
||||||
# We still fill original text field with transparent content to trigger creation of horizontal scrollbar
|
# We still fill original text field with transparent content to trigger creation of horizontal scrollbar
|
||||||
item.setForeground(QtGui.QColor('transparent'))
|
item.setForeground(QtGui.QColor('transparent'))
|
||||||
@@ -883,10 +927,35 @@ class KCCGUI(KCC_ui.Ui_KCC):
|
|||||||
def showDialog(self, message, kind):
|
def showDialog(self, message, kind):
|
||||||
if kind == 'error':
|
if kind == 'error':
|
||||||
QtWidgets.QMessageBox.critical(MW, 'KCC - Error', message, QtWidgets.QMessageBox.Ok)
|
QtWidgets.QMessageBox.critical(MW, 'KCC - Error', message, QtWidgets.QMessageBox.Ok)
|
||||||
|
try:
|
||||||
|
doc = Document()
|
||||||
|
root = doc.createElement('KCCErrorReport')
|
||||||
|
doc.appendChild(root)
|
||||||
|
main = doc.createElement('Timestamp')
|
||||||
|
root.appendChild(main)
|
||||||
|
text = doc.createTextNode(datetime.fromtimestamp(time()).strftime('%Y-%m-%d %H:%M:%S'))
|
||||||
|
main.appendChild(text)
|
||||||
|
main = doc.createElement('OS')
|
||||||
|
root.appendChild(main)
|
||||||
|
text = doc.createTextNode(platform())
|
||||||
|
main.appendChild(text)
|
||||||
|
main = doc.createElement('Version')
|
||||||
|
root.appendChild(main)
|
||||||
|
text = doc.createTextNode(__version__)
|
||||||
|
main.appendChild(text)
|
||||||
|
main = doc.createElement('Error')
|
||||||
|
root.appendChild(main)
|
||||||
|
text = doc.createTextNode(message)
|
||||||
|
main.appendChild(text)
|
||||||
|
urlopen(Request(url='https://kcc.iosphe.re/ErrorHandle/', data=doc.toxml(encoding='utf-8'),
|
||||||
|
headers={'Content-Type': 'application/xml',
|
||||||
|
'User-Agent': 'KindleComicConverter/' + __version__}))
|
||||||
|
except:
|
||||||
|
pass
|
||||||
elif kind == 'question':
|
elif kind == 'question':
|
||||||
dialogResponse = QtWidgets.QMessageBox.question(MW, 'KCC - Question', message,
|
GUI.versionCheck.setAnswer(QtWidgets.QMessageBox.question(MW, 'KCC - Question', message,
|
||||||
QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No)
|
QtWidgets.QMessageBox.Yes,
|
||||||
MW.dialogAnswer.emit(dialogResponse)
|
QtWidgets.QMessageBox.No))
|
||||||
|
|
||||||
def updateProgressbar(self, command):
|
def updateProgressbar(self, command):
|
||||||
if command == 'tick':
|
if command == 'tick':
|
||||||
@@ -894,6 +963,7 @@ class KCCGUI(KCC_ui.Ui_KCC):
|
|||||||
elif command.isdigit():
|
elif command.isdigit():
|
||||||
GUI.ProgressBar.setMaximum(int(command) - 1)
|
GUI.ProgressBar.setMaximum(int(command) - 1)
|
||||||
GUI.BasicModeButton.hide()
|
GUI.BasicModeButton.hide()
|
||||||
|
GUI.EditorButton.hide()
|
||||||
GUI.AdvModeButton.hide()
|
GUI.AdvModeButton.hide()
|
||||||
GUI.ProgressBar.reset()
|
GUI.ProgressBar.reset()
|
||||||
GUI.ProgressBar.show()
|
GUI.ProgressBar.show()
|
||||||
@@ -949,6 +1019,7 @@ class KCCGUI(KCC_ui.Ui_KCC):
|
|||||||
def hideProgressBar(self):
|
def hideProgressBar(self):
|
||||||
GUI.ProgressBar.hide()
|
GUI.ProgressBar.hide()
|
||||||
GUI.BasicModeButton.show()
|
GUI.BasicModeButton.show()
|
||||||
|
GUI.EditorButton.show()
|
||||||
GUI.AdvModeButton.show()
|
GUI.AdvModeButton.show()
|
||||||
|
|
||||||
def saveSettings(self, event):
|
def saveSettings(self, event):
|
||||||
@@ -979,7 +1050,7 @@ class KCCGUI(KCC_ui.Ui_KCC):
|
|||||||
'ColorBox': GUI.ColorBox.checkState(),
|
'ColorBox': GUI.ColorBox.checkState(),
|
||||||
'customWidth': GUI.customWidth.text(),
|
'customWidth': GUI.customWidth.text(),
|
||||||
'customHeight': GUI.customHeight.text(),
|
'customHeight': GUI.customHeight.text(),
|
||||||
'GammaSlider': float(self.GammaValue)*100})
|
'GammaSlider': float(self.GammaValue) * 100})
|
||||||
self.settings.sync()
|
self.settings.sync()
|
||||||
self.tray.hide()
|
self.tray.hide()
|
||||||
|
|
||||||
@@ -1045,11 +1116,9 @@ class KCCGUI(KCC_ui.Ui_KCC):
|
|||||||
line = line.decode("utf-8")
|
line = line.decode("utf-8")
|
||||||
if 'Amazon kindlegen' in line:
|
if 'Amazon kindlegen' in line:
|
||||||
versionCheck = line.split('V')[1].split(' ')[0]
|
versionCheck = line.split('V')[1].split(' ')[0]
|
||||||
if tuple(map(int, (versionCheck.split(".")))) < tuple(map(int, ('2.9'.split(".")))):
|
if StrictVersion(versionCheck) < StrictVersion('2.9'):
|
||||||
self.addMessage('Your <a href="http://www.amazon.com/gp/feature.html?ie=UTF8&docId='
|
self.addMessage('Your <a href="http://www.amazon.com/gp/feature.html?ie=UTF8&docId='
|
||||||
'1000765211">KindleGen</a> is outdated! Creating MOBI might fail.'
|
'1000765211">KindleGen</a> is outdated! MOBI conversion might fail.', 'warning')
|
||||||
' Please update <a href="http://www.amazon.com/gp/feature.html?ie=UTF8&docId='
|
|
||||||
'1000765211">KindleGen</a> from Amazon\'s website.', 'warning')
|
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
self.KindleGen = False
|
self.KindleGen = False
|
||||||
@@ -1058,6 +1127,8 @@ class KCCGUI(KCC_ui.Ui_KCC):
|
|||||||
'<b>KindleGen</b></a>! MOBI conversion will be unavailable!', 'error')
|
'<b>KindleGen</b></a>! MOBI conversion will be unavailable!', 'error')
|
||||||
if sys.platform.startswith('win'):
|
if sys.platform.startswith('win'):
|
||||||
self.addMessage('Download it and place EXE in KCC directory.', 'error')
|
self.addMessage('Download it and place EXE in KCC directory.', 'error')
|
||||||
|
elif sys.platform.startswith('darwin'):
|
||||||
|
self.addMessage('Install it using <a href="http://brew.sh/">Brew</a>.', 'error')
|
||||||
else:
|
else:
|
||||||
self.addMessage('Download it and place executable in /usr/local/bin directory.', 'error')
|
self.addMessage('Download it and place executable in /usr/local/bin directory.', 'error')
|
||||||
|
|
||||||
@@ -1068,16 +1139,11 @@ class KCCGUI(KCC_ui.Ui_KCC):
|
|||||||
MW = KCCWindow
|
MW = KCCWindow
|
||||||
GUI = self
|
GUI = self
|
||||||
self.setupUi(MW)
|
self.setupUi(MW)
|
||||||
# User settings will be reverted to default ones if were created in one of the following versions
|
self.editor = KCCGUI_MetaEditor()
|
||||||
# Empty string cover all versions before this system was implemented
|
|
||||||
purgeSettingsVersions = ['']
|
|
||||||
self.icons = Icons()
|
self.icons = Icons()
|
||||||
self.webContent = KCC_rc_web.WebContent()
|
self.webContent = KCC_rc_web.WebContent()
|
||||||
self.settings = QtCore.QSettings('KindleComicConverter', 'KindleComicConverter')
|
self.settings = QtCore.QSettings('KindleComicConverter', 'KindleComicConverter')
|
||||||
self.settingsVersion = self.settings.value('settingsVersion', '', type=str)
|
self.settingsVersion = self.settings.value('settingsVersion', '', type=str)
|
||||||
if self.settingsVersion in purgeSettingsVersions:
|
|
||||||
QtCore.QSettings.clear(self.settings)
|
|
||||||
self.settingsVersion = self.settings.value('settingsVersion', '', type=str)
|
|
||||||
self.lastPath = self.settings.value('lastPath', '', type=str)
|
self.lastPath = self.settings.value('lastPath', '', type=str)
|
||||||
self.lastDevice = self.settings.value('lastDevice', 0, type=int)
|
self.lastDevice = self.settings.value('lastDevice', 0, type=int)
|
||||||
self.currentMode = self.settings.value('currentMode', 1, type=int)
|
self.currentMode = self.settings.value('currentMode', 1, type=int)
|
||||||
@@ -1119,63 +1185,53 @@ class KCCGUI(KCC_ui.Ui_KCC):
|
|||||||
self.p.ionice(1)
|
self.p.ionice(1)
|
||||||
|
|
||||||
self.profiles = {
|
self.profiles = {
|
||||||
"Kindle Voyage": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0,
|
"K. PW 3/Voyage": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0,
|
||||||
'DefaultUpscale': False, 'Label': 'KV'},
|
'DefaultUpscale': False, 'Label': 'KV'},
|
||||||
"Kindle Paperwhite": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0,
|
"Kindle PW 1/2": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0,
|
||||||
'DefaultUpscale': False, 'Label': 'KPW'},
|
'DefaultUpscale': False, 'Label': 'KPW'},
|
||||||
"Kindle": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0,
|
"Kindle": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0,
|
||||||
'DefaultUpscale': False, 'Label': 'K345'},
|
'DefaultUpscale': False, 'Label': 'K345'},
|
||||||
"Kindle DX/DXG": {'Quality': False, 'ForceExpert': False, 'DefaultFormat': 2,
|
"Kindle DX/DXG": {'Quality': False, 'ForceExpert': False, 'DefaultFormat': 2,
|
||||||
'DefaultUpscale': False, 'Label': 'KDX'},
|
'DefaultUpscale': False, 'Label': 'KDX'},
|
||||||
"K. Fire HD": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0,
|
"Kobo Mini/Touch": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 1,
|
||||||
'DefaultUpscale': True, 'Label': 'KFHD'},
|
|
||||||
"K. Fire HDX": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0,
|
|
||||||
'DefaultUpscale': True, 'Label': 'KFHDX'},
|
|
||||||
"K. Fire HDX 8.9": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0,
|
|
||||||
'DefaultUpscale': True, 'Label': 'KFHDX8'},
|
|
||||||
"Kobo Mini/Touch": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 2,
|
|
||||||
'DefaultUpscale': False, 'Label': 'KoMT'},
|
'DefaultUpscale': False, 'Label': 'KoMT'},
|
||||||
"Kobo Glow": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 2,
|
"Kobo Glo": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 1,
|
||||||
'DefaultUpscale': False, 'Label': 'KoG'},
|
'DefaultUpscale': False, 'Label': 'KoG'},
|
||||||
"Kobo Aura": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 2,
|
"Kobo Glo HD": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 1,
|
||||||
|
'DefaultUpscale': False, 'Label': 'KoGHD'},
|
||||||
|
"Kobo Aura": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 1,
|
||||||
'DefaultUpscale': False, 'Label': 'KoA'},
|
'DefaultUpscale': False, 'Label': 'KoA'},
|
||||||
"Kobo Aura HD": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 2,
|
"Kobo Aura HD": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 1,
|
||||||
'DefaultUpscale': False, 'Label': 'KoAHD'},
|
'DefaultUpscale': False, 'Label': 'KoAHD'},
|
||||||
"Kobo Aura H2O": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 2,
|
"Kobo Aura H2O": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 1,
|
||||||
'DefaultUpscale': False, 'Label': 'KoAH2O'},
|
'DefaultUpscale': False, 'Label': 'KoAH2O'},
|
||||||
"Other": {'Quality': False, 'ForceExpert': True, 'DefaultFormat': 1,
|
"Other": {'Quality': False, 'ForceExpert': True, 'DefaultFormat': 1,
|
||||||
'DefaultUpscale': False, 'Label': 'OTHER'},
|
'DefaultUpscale': False, 'Label': 'OTHER'},
|
||||||
"Kindle for Android": {'Quality': False, 'ForceExpert': True, 'DefaultFormat': 0,
|
|
||||||
'DefaultUpscale': False, 'Label': 'KFA'},
|
|
||||||
"Kindle 1": {'Quality': False, 'ForceExpert': False, 'DefaultFormat': 0,
|
"Kindle 1": {'Quality': False, 'ForceExpert': False, 'DefaultFormat': 0,
|
||||||
'DefaultUpscale': False, 'Label': 'K1'},
|
'DefaultUpscale': False, 'Label': 'K1'},
|
||||||
"Kindle 2": {'Quality': False, 'ForceExpert': False, 'DefaultFormat': 0,
|
"Kindle 2": {'Quality': False, 'ForceExpert': False, 'DefaultFormat': 0,
|
||||||
'DefaultUpscale': False, 'Label': 'K2'}
|
'DefaultUpscale': False, 'Label': 'K2'}
|
||||||
}
|
}
|
||||||
profilesGUI = [
|
profilesGUI = [
|
||||||
"Kindle Voyage",
|
"K. PW 3/Voyage",
|
||||||
"Kindle Paperwhite",
|
"Kindle PW 1/2",
|
||||||
"Kindle",
|
"Kindle",
|
||||||
"Separator",
|
"Separator",
|
||||||
"K. Fire HD",
|
|
||||||
"K. Fire HDX",
|
|
||||||
"K. Fire HDX 8.9",
|
|
||||||
"Separator",
|
|
||||||
"Kobo Mini/Touch",
|
"Kobo Mini/Touch",
|
||||||
"Kobo Glow",
|
"Kobo Glo",
|
||||||
|
"Kobo Glo HD",
|
||||||
"Kobo Aura",
|
"Kobo Aura",
|
||||||
"Kobo Aura HD",
|
"Kobo Aura HD",
|
||||||
"Kobo Aura H2O",
|
"Kobo Aura H2O",
|
||||||
"Separator",
|
"Separator",
|
||||||
"Other",
|
"Other",
|
||||||
"Separator",
|
"Separator",
|
||||||
"Kindle for Android",
|
|
||||||
"Kindle 1",
|
"Kindle 1",
|
||||||
"Kindle 2",
|
"Kindle 2",
|
||||||
"Kindle DX/DXG",
|
"Kindle DX/DXG",
|
||||||
]
|
]
|
||||||
|
|
||||||
statusBarLabel = QtWidgets.QLabel('<b><a href="http://kcc.iosphe.re/">HOMEPAGE</a> - <a href="https://github.'
|
statusBarLabel = QtWidgets.QLabel('<b><a href="https://kcc.iosphe.re/">HOMEPAGE</a> - <a href="https://github.'
|
||||||
'com/ciromattia/kcc/blob/master/README.md#issues--new-features--donations">DO'
|
'com/ciromattia/kcc/blob/master/README.md#issues--new-features--donations">DO'
|
||||||
'NATE</a> - <a href="https://github.com/ciromattia/kcc/wiki">WIKI</a> - <a hr'
|
'NATE</a> - <a href="https://github.com/ciromattia/kcc/wiki">WIKI</a> - <a hr'
|
||||||
'ef="http://www.mobileread.com/forums/showthread.php?t=207461">FORUM</a></b>')
|
'ef="http://www.mobileread.com/forums/showthread.php?t=207461">FORUM</a></b>')
|
||||||
@@ -1217,6 +1273,7 @@ class KCCGUI(KCC_ui.Ui_KCC):
|
|||||||
GUI.DirectoryButton.clicked.connect(self.selectDir)
|
GUI.DirectoryButton.clicked.connect(self.selectDir)
|
||||||
GUI.ClearButton.clicked.connect(self.clearJobs)
|
GUI.ClearButton.clicked.connect(self.clearJobs)
|
||||||
GUI.FileButton.clicked.connect(self.selectFile)
|
GUI.FileButton.clicked.connect(self.selectFile)
|
||||||
|
GUI.EditorButton.clicked.connect(self.selectFileMetaEditor)
|
||||||
GUI.ConvertButton.clicked.connect(self.convertStart)
|
GUI.ConvertButton.clicked.connect(self.convertStart)
|
||||||
GUI.GammaSlider.valueChanged.connect(self.changeGamma)
|
GUI.GammaSlider.valueChanged.connect(self.changeGamma)
|
||||||
GUI.NoRotateBox.stateChanged.connect(self.toggleNoSplitRotate)
|
GUI.NoRotateBox.stateChanged.connect(self.toggleNoSplitRotate)
|
||||||
@@ -1231,7 +1288,6 @@ class KCCGUI(KCC_ui.Ui_KCC):
|
|||||||
MW.showDialog.connect(self.showDialog)
|
MW.showDialog.connect(self.showDialog)
|
||||||
MW.hideProgressBar.connect(self.hideProgressBar)
|
MW.hideProgressBar.connect(self.hideProgressBar)
|
||||||
MW.forceShutdown.connect(self.forceShutdown)
|
MW.forceShutdown.connect(self.forceShutdown)
|
||||||
MW.dialogAnswer.connect(self.versionCheck.getNewVersion)
|
|
||||||
MW.closeEvent = self.saveSettings
|
MW.closeEvent = self.saveSettings
|
||||||
MW.addTrayMessage.connect(self.tray.addTrayMessage)
|
MW.addTrayMessage.connect(self.tray.addTrayMessage)
|
||||||
|
|
||||||
@@ -1243,7 +1299,7 @@ class KCCGUI(KCC_ui.Ui_KCC):
|
|||||||
if profile == "Other":
|
if profile == "Other":
|
||||||
GUI.DeviceBox.addItem(self.icons.deviceOther, profile)
|
GUI.DeviceBox.addItem(self.icons.deviceOther, profile)
|
||||||
elif profile == "Separator":
|
elif profile == "Separator":
|
||||||
GUI.DeviceBox.insertSeparator(GUI.DeviceBox.count()+1)
|
GUI.DeviceBox.insertSeparator(GUI.DeviceBox.count() + 1)
|
||||||
elif 'Ko' in profile:
|
elif 'Ko' in profile:
|
||||||
GUI.DeviceBox.addItem(self.icons.deviceKobo, profile)
|
GUI.DeviceBox.addItem(self.icons.deviceKobo, profile)
|
||||||
else:
|
else:
|
||||||
@@ -1277,6 +1333,66 @@ class KCCGUI(KCC_ui.Ui_KCC):
|
|||||||
self.versionCheck.start()
|
self.versionCheck.start()
|
||||||
self.contentServer.start()
|
self.contentServer.start()
|
||||||
self.tray.show()
|
self.tray.show()
|
||||||
|
|
||||||
|
# Linux hack as PyQt 5.5 not hit mainstream distributions yet
|
||||||
|
if sys.platform.startswith('linux') and StrictVersion(QtCore.qVersion()) > StrictVersion('5.4.9'):
|
||||||
|
self.JobList.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel)
|
||||||
|
self.JobList.setHorizontalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel)
|
||||||
|
|
||||||
MW.setWindowTitle("Kindle Comic Converter " + __version__)
|
MW.setWindowTitle("Kindle Comic Converter " + __version__)
|
||||||
MW.show()
|
MW.show()
|
||||||
MW.raise_()
|
MW.raise_()
|
||||||
|
|
||||||
|
|
||||||
|
class KCCGUI_MetaEditor(KCC_MetaEditor_ui.Ui_MetaEditorDialog):
|
||||||
|
def loadData(self, file):
|
||||||
|
self.parser = metadata.MetadataParser(file)
|
||||||
|
if self.parser.compressor == 'rar':
|
||||||
|
self.EditorFrame.setEnabled(False)
|
||||||
|
self.OKButton.setEnabled(False)
|
||||||
|
self.StatusLabel.setText('CBR metadata are read-only.')
|
||||||
|
else:
|
||||||
|
self.EditorFrame.setEnabled(True)
|
||||||
|
self.OKButton.setEnabled(True)
|
||||||
|
self.StatusLabel.setText('Separate authors with a comma.')
|
||||||
|
for field in (self.SeriesLine, self.VolumeLine, self.NumberLine, self.MUidLine):
|
||||||
|
field.setText(self.parser.data[field.objectName()[:-4]])
|
||||||
|
for field in (self.WriterLine, self.PencillerLine, self.InkerLine, self.ColoristLine):
|
||||||
|
field.setText(', '.join(self.parser.data[field.objectName()[:-4] + 's']))
|
||||||
|
if self.SeriesLine.text() == '':
|
||||||
|
self.SeriesLine.setText(file.split('\\')[-1].split('.')[0])
|
||||||
|
|
||||||
|
def saveData(self):
|
||||||
|
for field in (self.VolumeLine, self.NumberLine, self.MUidLine):
|
||||||
|
if field.text().isnumeric() or self.cleanData(field.text()) == '':
|
||||||
|
self.parser.data[field.objectName()[:-4]] = self.cleanData(field.text())
|
||||||
|
else:
|
||||||
|
self.StatusLabel.setText(field.objectName()[:-4] + ' field must be a number.')
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
self.parser.data['Series'] = self.cleanData(self.SeriesLine.text())
|
||||||
|
for field in (self.WriterLine, self.PencillerLine, self.InkerLine, self.ColoristLine):
|
||||||
|
values = self.cleanData(field.text()).split(',')
|
||||||
|
tmpData = []
|
||||||
|
for value in values:
|
||||||
|
if self.cleanData(value) != '':
|
||||||
|
tmpData.append(self.cleanData(value))
|
||||||
|
self.parser.data[field.objectName()[:-4] + 's'] = tmpData
|
||||||
|
try:
|
||||||
|
self.parser.saveXML()
|
||||||
|
except Exception as err:
|
||||||
|
_, _, traceback = sys.exc_info()
|
||||||
|
GUI.showDialog("Failed to save metadata!\n\n%s\n\nTraceback:\n%s"
|
||||||
|
% (str(err), sanitizeTrace(traceback)), 'error')
|
||||||
|
self.ui.close()
|
||||||
|
|
||||||
|
def cleanData(self, s):
|
||||||
|
return escape(s.strip())
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.ui = QtWidgets.QDialog()
|
||||||
|
self.parser = None
|
||||||
|
self.setupUi(self.ui)
|
||||||
|
self.ui.setWindowFlags(self.ui.windowFlags() & ~QtCore.Qt.WindowContextHelpButtonHint)
|
||||||
|
self.OKButton.clicked.connect(self.saveData)
|
||||||
|
self.CancelButton.clicked.connect(self.ui.close)
|
||||||
|
|||||||
7166
kcc/KCC_rc.py
7166
kcc/KCC_rc.py
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
# Form implementation generated from reading ui file 'KCC.ui'
|
# Form implementation generated from reading ui file 'KCC.ui'
|
||||||
#
|
#
|
||||||
# Created: Sun Jan 4 09:58:25 2015
|
# Created: Sun Feb 8 09:50:43 2015
|
||||||
# by: PyQt5 UI code generator 5.4
|
# by: PyQt5 UI code generator 5.4
|
||||||
#
|
#
|
||||||
# WARNING! All changes made in this file will be lost!
|
# WARNING! All changes made in this file will be lost!
|
||||||
@@ -139,21 +139,20 @@ class Ui_KCC(object):
|
|||||||
self.JobList.setGeometry(QtCore.QRect(10, 50, 401, 101))
|
self.JobList.setGeometry(QtCore.QRect(10, 50, 401, 101))
|
||||||
self.JobList.setFocusPolicy(QtCore.Qt.NoFocus)
|
self.JobList.setFocusPolicy(QtCore.Qt.NoFocus)
|
||||||
self.JobList.setStyleSheet("QListWidget#JobList {background:#ffffff;background-image:url(:/Other/icons/list_background.png);background-position:center center;background-repeat:no-repeat;}QScrollBar:vertical{border:1px solid #999;background:#FFF;width:5px;margin:0}QScrollBar::handle:vertical{background:DarkGray;min-height:0}QScrollBar::add-line:vertical{height:0;background:DarkGray;subcontrol-position:bottom;subcontrol-origin:margin}QScrollBar::sub-line:vertical{height:0;background:DarkGray;subcontrol-position:top;subcontrol-origin:margin}QScrollBar:horizontal{border:1px solid #999;background:#FFF;height:5px;margin:0}QScrollBar::handle:horizontal{background:DarkGray;min-width:0}QScrollBar::add-line:horizontal{width:0;background:DarkGray;subcontrol-position:bottom;subcontrol-origin:margin}QScrollBar::sub-line:horizontal{width:0;background:DarkGray;subcontrol-position:top;subcontrol-origin:margin}")
|
self.JobList.setStyleSheet("QListWidget#JobList {background:#ffffff;background-image:url(:/Other/icons/list_background.png);background-position:center center;background-repeat:no-repeat;}QScrollBar:vertical{border:1px solid #999;background:#FFF;width:5px;margin:0}QScrollBar::handle:vertical{background:DarkGray;min-height:0}QScrollBar::add-line:vertical{height:0;background:DarkGray;subcontrol-position:bottom;subcontrol-origin:margin}QScrollBar::sub-line:vertical{height:0;background:DarkGray;subcontrol-position:top;subcontrol-origin:margin}QScrollBar:horizontal{border:1px solid #999;background:#FFF;height:5px;margin:0}QScrollBar::handle:horizontal{background:DarkGray;min-width:0}QScrollBar::add-line:horizontal{width:0;background:DarkGray;subcontrol-position:bottom;subcontrol-origin:margin}QScrollBar::sub-line:horizontal{width:0;background:DarkGray;subcontrol-position:top;subcontrol-origin:margin}")
|
||||||
self.JobList.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
|
|
||||||
self.JobList.setProperty("showDropIndicator", False)
|
self.JobList.setProperty("showDropIndicator", False)
|
||||||
self.JobList.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection)
|
self.JobList.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection)
|
||||||
self.JobList.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel)
|
self.JobList.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel)
|
||||||
self.JobList.setHorizontalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel)
|
self.JobList.setHorizontalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel)
|
||||||
self.JobList.setObjectName("JobList")
|
self.JobList.setObjectName("JobList")
|
||||||
self.BasicModeButton = QtWidgets.QPushButton(self.Form)
|
self.BasicModeButton = QtWidgets.QPushButton(self.Form)
|
||||||
self.BasicModeButton.setGeometry(QtCore.QRect(10, 10, 195, 32))
|
self.BasicModeButton.setGeometry(QtCore.QRect(10, 10, 141, 32))
|
||||||
font = QtGui.QFont()
|
font = QtGui.QFont()
|
||||||
font.setPointSize(9)
|
font.setPointSize(9)
|
||||||
self.BasicModeButton.setFont(font)
|
self.BasicModeButton.setFont(font)
|
||||||
self.BasicModeButton.setFocusPolicy(QtCore.Qt.NoFocus)
|
self.BasicModeButton.setFocusPolicy(QtCore.Qt.NoFocus)
|
||||||
self.BasicModeButton.setObjectName("BasicModeButton")
|
self.BasicModeButton.setObjectName("BasicModeButton")
|
||||||
self.AdvModeButton = QtWidgets.QPushButton(self.Form)
|
self.AdvModeButton = QtWidgets.QPushButton(self.Form)
|
||||||
self.AdvModeButton.setGeometry(QtCore.QRect(217, 10, 195, 32))
|
self.AdvModeButton.setGeometry(QtCore.QRect(261, 10, 151, 32))
|
||||||
font = QtGui.QFont()
|
font = QtGui.QFont()
|
||||||
font.setPointSize(9)
|
font.setPointSize(9)
|
||||||
self.AdvModeButton.setFont(font)
|
self.AdvModeButton.setFont(font)
|
||||||
@@ -232,6 +231,16 @@ class Ui_KCC(object):
|
|||||||
self.customHeight.setMaxLength(4)
|
self.customHeight.setMaxLength(4)
|
||||||
self.customHeight.setObjectName("customHeight")
|
self.customHeight.setObjectName("customHeight")
|
||||||
self.gridLayout_2.addWidget(self.customHeight, 0, 3, 1, 1)
|
self.gridLayout_2.addWidget(self.customHeight, 0, 3, 1, 1)
|
||||||
|
self.EditorButton = QtWidgets.QPushButton(self.Form)
|
||||||
|
self.EditorButton.setGeometry(QtCore.QRect(160, 10, 91, 32))
|
||||||
|
font = QtGui.QFont()
|
||||||
|
font.setPointSize(9)
|
||||||
|
self.EditorButton.setFont(font)
|
||||||
|
self.EditorButton.setFocusPolicy(QtCore.Qt.NoFocus)
|
||||||
|
icon5 = QtGui.QIcon()
|
||||||
|
icon5.addPixmap(QtGui.QPixmap(":/Other/icons/editor.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||||
|
self.EditorButton.setIcon(icon5)
|
||||||
|
self.EditorButton.setObjectName("EditorButton")
|
||||||
KCC.setCentralWidget(self.Form)
|
KCC.setCentralWidget(self.Form)
|
||||||
self.statusBar = QtWidgets.QStatusBar(KCC)
|
self.statusBar = QtWidgets.QStatusBar(KCC)
|
||||||
font = QtGui.QFont()
|
font = QtGui.QFont()
|
||||||
@@ -301,6 +310,7 @@ class Ui_KCC(object):
|
|||||||
self.hLabel.setText(_translate("KCC", "Custom height: "))
|
self.hLabel.setText(_translate("KCC", "Custom height: "))
|
||||||
self.customHeight.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Resolution of target device.</p></body></html>"))
|
self.customHeight.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Resolution of target device.</p></body></html>"))
|
||||||
self.customHeight.setInputMask(_translate("KCC", "0000"))
|
self.customHeight.setInputMask(_translate("KCC", "0000"))
|
||||||
|
self.EditorButton.setText(_translate("KCC", "Editor"))
|
||||||
self.ActionBasic.setText(_translate("KCC", "Basic"))
|
self.ActionBasic.setText(_translate("KCC", "Basic"))
|
||||||
self.ActionAdvanced.setText(_translate("KCC", "Advanced"))
|
self.ActionAdvanced.setText(_translate("KCC", "Advanced"))
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Form implementation generated from reading ui file 'KCC-Linux.ui'
|
# Form implementation generated from reading ui file 'gui/KCC-Linux.ui'
|
||||||
#
|
#
|
||||||
# Created: Sun Jan 4 10:06:14 2015
|
# Created: Sun Feb 8 03:10:09 2015
|
||||||
# by: PyQt5 UI code generator 5.4
|
# by: PyQt5 UI code generator 5.2.1
|
||||||
#
|
#
|
||||||
# WARNING! All changes made in this file will be lost!
|
# WARNING! All changes made in this file will be lost!
|
||||||
|
|
||||||
@@ -179,15 +179,12 @@ class Ui_KCC(object):
|
|||||||
self.JobList.setFont(font)
|
self.JobList.setFont(font)
|
||||||
self.JobList.setFocusPolicy(QtCore.Qt.NoFocus)
|
self.JobList.setFocusPolicy(QtCore.Qt.NoFocus)
|
||||||
self.JobList.setStyleSheet("QListWidget#JobList {background:#ffffff;background-image:url(:/Other/icons/list_background.png);background-position:center center;background-repeat:no-repeat;}QScrollBar:vertical{border:1px solid #999;background:#FFF;width:5px;margin:0}QScrollBar::handle:vertical{background:DarkGray;min-height:0}QScrollBar::add-line:vertical{height:0;background:DarkGray;subcontrol-position:bottom;subcontrol-origin:margin}QScrollBar::sub-line:vertical{height:0;background:DarkGray;subcontrol-position:top;subcontrol-origin:margin}QScrollBar:horizontal{border:1px solid #999;background:#FFF;height:5px;margin:0}QScrollBar::handle:horizontal{background:DarkGray;min-width:0}QScrollBar::add-line:horizontal{width:0;background:DarkGray;subcontrol-position:bottom;subcontrol-origin:margin}QScrollBar::sub-line:horizontal{width:0;background:DarkGray;subcontrol-position:top;subcontrol-origin:margin}")
|
self.JobList.setStyleSheet("QListWidget#JobList {background:#ffffff;background-image:url(:/Other/icons/list_background.png);background-position:center center;background-repeat:no-repeat;}QScrollBar:vertical{border:1px solid #999;background:#FFF;width:5px;margin:0}QScrollBar::handle:vertical{background:DarkGray;min-height:0}QScrollBar::add-line:vertical{height:0;background:DarkGray;subcontrol-position:bottom;subcontrol-origin:margin}QScrollBar::sub-line:vertical{height:0;background:DarkGray;subcontrol-position:top;subcontrol-origin:margin}QScrollBar:horizontal{border:1px solid #999;background:#FFF;height:5px;margin:0}QScrollBar::handle:horizontal{background:DarkGray;min-width:0}QScrollBar::add-line:horizontal{width:0;background:DarkGray;subcontrol-position:bottom;subcontrol-origin:margin}QScrollBar::sub-line:horizontal{width:0;background:DarkGray;subcontrol-position:top;subcontrol-origin:margin}")
|
||||||
self.JobList.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
|
|
||||||
self.JobList.setProperty("showDropIndicator", False)
|
self.JobList.setProperty("showDropIndicator", False)
|
||||||
self.JobList.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection)
|
self.JobList.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection)
|
||||||
self.JobList.setIconSize(QtCore.QSize(18, 18))
|
self.JobList.setIconSize(QtCore.QSize(18, 18))
|
||||||
self.JobList.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel)
|
|
||||||
self.JobList.setHorizontalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel)
|
|
||||||
self.JobList.setObjectName("JobList")
|
self.JobList.setObjectName("JobList")
|
||||||
self.BasicModeButton = QtWidgets.QPushButton(self.Form)
|
self.BasicModeButton = QtWidgets.QPushButton(self.Form)
|
||||||
self.BasicModeButton.setGeometry(QtCore.QRect(10, 10, 195, 32))
|
self.BasicModeButton.setGeometry(QtCore.QRect(10, 10, 141, 32))
|
||||||
font = QtGui.QFont()
|
font = QtGui.QFont()
|
||||||
font.setFamily("DejaVu Sans")
|
font.setFamily("DejaVu Sans")
|
||||||
font.setPointSize(9)
|
font.setPointSize(9)
|
||||||
@@ -195,7 +192,7 @@ class Ui_KCC(object):
|
|||||||
self.BasicModeButton.setFocusPolicy(QtCore.Qt.NoFocus)
|
self.BasicModeButton.setFocusPolicy(QtCore.Qt.NoFocus)
|
||||||
self.BasicModeButton.setObjectName("BasicModeButton")
|
self.BasicModeButton.setObjectName("BasicModeButton")
|
||||||
self.AdvModeButton = QtWidgets.QPushButton(self.Form)
|
self.AdvModeButton = QtWidgets.QPushButton(self.Form)
|
||||||
self.AdvModeButton.setGeometry(QtCore.QRect(217, 10, 195, 32))
|
self.AdvModeButton.setGeometry(QtCore.QRect(260, 10, 151, 32))
|
||||||
font = QtGui.QFont()
|
font = QtGui.QFont()
|
||||||
font.setFamily("DejaVu Sans")
|
font.setFamily("DejaVu Sans")
|
||||||
font.setPointSize(9)
|
font.setPointSize(9)
|
||||||
@@ -302,6 +299,17 @@ class Ui_KCC(object):
|
|||||||
self.customHeight.setMaxLength(4)
|
self.customHeight.setMaxLength(4)
|
||||||
self.customHeight.setObjectName("customHeight")
|
self.customHeight.setObjectName("customHeight")
|
||||||
self.gridLayout_2.addWidget(self.customHeight, 0, 3, 1, 1)
|
self.gridLayout_2.addWidget(self.customHeight, 0, 3, 1, 1)
|
||||||
|
self.EditorButton = QtWidgets.QPushButton(self.Form)
|
||||||
|
self.EditorButton.setGeometry(QtCore.QRect(160, 10, 91, 32))
|
||||||
|
font = QtGui.QFont()
|
||||||
|
font.setFamily("DejaVu Sans")
|
||||||
|
font.setPointSize(9)
|
||||||
|
self.EditorButton.setFont(font)
|
||||||
|
self.EditorButton.setFocusPolicy(QtCore.Qt.NoFocus)
|
||||||
|
icon5 = QtGui.QIcon()
|
||||||
|
icon5.addPixmap(QtGui.QPixmap(":/Other/icons/editor.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||||
|
self.EditorButton.setIcon(icon5)
|
||||||
|
self.EditorButton.setObjectName("EditorButton")
|
||||||
KCC.setCentralWidget(self.Form)
|
KCC.setCentralWidget(self.Form)
|
||||||
self.statusBar = QtWidgets.QStatusBar(KCC)
|
self.statusBar = QtWidgets.QStatusBar(KCC)
|
||||||
font = QtGui.QFont()
|
font = QtGui.QFont()
|
||||||
@@ -370,6 +378,7 @@ class Ui_KCC(object):
|
|||||||
self.hLabel.setText(_translate("KCC", "Custom height: "))
|
self.hLabel.setText(_translate("KCC", "Custom height: "))
|
||||||
self.customHeight.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Resolution of target device.</p></body></html>"))
|
self.customHeight.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Resolution of target device.</p></body></html>"))
|
||||||
self.customHeight.setInputMask(_translate("KCC", "0000"))
|
self.customHeight.setInputMask(_translate("KCC", "0000"))
|
||||||
|
self.EditorButton.setText(_translate("KCC", "Editor"))
|
||||||
self.ActionBasic.setText(_translate("KCC", "Basic"))
|
self.ActionBasic.setText(_translate("KCC", "Basic"))
|
||||||
self.ActionAdvanced.setText(_translate("KCC", "Advanced"))
|
self.ActionAdvanced.setText(_translate("KCC", "Advanced"))
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Form implementation generated from reading ui file 'KCC-OSX.ui'
|
# Form implementation generated from reading ui file '/Users/pawelj/Documents/KCC/gui/KCC-OSX.ui'
|
||||||
#
|
#
|
||||||
# Created: Sun Jan 4 10:26:09 2015
|
# Created: Sun Feb 8 12:37:33 2015
|
||||||
# by: PyQt5 UI code generator 5.4
|
# by: PyQt5 UI code generator 5.4
|
||||||
#
|
#
|
||||||
# WARNING! All changes made in this file will be lost!
|
# WARNING! All changes made in this file will be lost!
|
||||||
@@ -186,14 +186,13 @@ class Ui_KCC(object):
|
|||||||
self.JobList.setFont(font)
|
self.JobList.setFont(font)
|
||||||
self.JobList.setFocusPolicy(QtCore.Qt.NoFocus)
|
self.JobList.setFocusPolicy(QtCore.Qt.NoFocus)
|
||||||
self.JobList.setStyleSheet("QListWidget#JobList {background:#ffffff;background-image:url(:/Other/icons/list_background.png);background-position:center center;background-repeat:no-repeat;}QScrollBar:vertical{border:1px solid #999;background:#FFF;width:5px;margin:0}QScrollBar::handle:vertical{background:DarkGray;min-height:0}QScrollBar::add-line:vertical{height:0;background:DarkGray;subcontrol-position:bottom;subcontrol-origin:margin}QScrollBar::sub-line:vertical{height:0;background:DarkGray;subcontrol-position:top;subcontrol-origin:margin}QScrollBar:horizontal{border:1px solid #999;background:#FFF;height:5px;margin:0}QScrollBar::handle:horizontal{background:DarkGray;min-width:0}QScrollBar::add-line:horizontal{width:0;background:DarkGray;subcontrol-position:bottom;subcontrol-origin:margin}QScrollBar::sub-line:horizontal{width:0;background:DarkGray;subcontrol-position:top;subcontrol-origin:margin}")
|
self.JobList.setStyleSheet("QListWidget#JobList {background:#ffffff;background-image:url(:/Other/icons/list_background.png);background-position:center center;background-repeat:no-repeat;}QScrollBar:vertical{border:1px solid #999;background:#FFF;width:5px;margin:0}QScrollBar::handle:vertical{background:DarkGray;min-height:0}QScrollBar::add-line:vertical{height:0;background:DarkGray;subcontrol-position:bottom;subcontrol-origin:margin}QScrollBar::sub-line:vertical{height:0;background:DarkGray;subcontrol-position:top;subcontrol-origin:margin}QScrollBar:horizontal{border:1px solid #999;background:#FFF;height:5px;margin:0}QScrollBar::handle:horizontal{background:DarkGray;min-width:0}QScrollBar::add-line:horizontal{width:0;background:DarkGray;subcontrol-position:bottom;subcontrol-origin:margin}QScrollBar::sub-line:horizontal{width:0;background:DarkGray;subcontrol-position:top;subcontrol-origin:margin}")
|
||||||
self.JobList.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
|
|
||||||
self.JobList.setProperty("showDropIndicator", False)
|
self.JobList.setProperty("showDropIndicator", False)
|
||||||
self.JobList.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection)
|
self.JobList.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection)
|
||||||
self.JobList.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel)
|
self.JobList.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel)
|
||||||
self.JobList.setHorizontalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel)
|
self.JobList.setHorizontalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel)
|
||||||
self.JobList.setObjectName("JobList")
|
self.JobList.setObjectName("JobList")
|
||||||
self.BasicModeButton = QtWidgets.QPushButton(self.Form)
|
self.BasicModeButton = QtWidgets.QPushButton(self.Form)
|
||||||
self.BasicModeButton.setGeometry(QtCore.QRect(5, 10, 210, 41))
|
self.BasicModeButton.setGeometry(QtCore.QRect(5, 10, 156, 41))
|
||||||
font = QtGui.QFont()
|
font = QtGui.QFont()
|
||||||
font.setFamily("Lucida Grande")
|
font.setFamily("Lucida Grande")
|
||||||
font.setPointSize(12)
|
font.setPointSize(12)
|
||||||
@@ -203,7 +202,7 @@ class Ui_KCC(object):
|
|||||||
self.BasicModeButton.setFocusPolicy(QtCore.Qt.NoFocus)
|
self.BasicModeButton.setFocusPolicy(QtCore.Qt.NoFocus)
|
||||||
self.BasicModeButton.setObjectName("BasicModeButton")
|
self.BasicModeButton.setObjectName("BasicModeButton")
|
||||||
self.AdvModeButton = QtWidgets.QPushButton(self.Form)
|
self.AdvModeButton = QtWidgets.QPushButton(self.Form)
|
||||||
self.AdvModeButton.setGeometry(QtCore.QRect(207, 10, 210, 41))
|
self.AdvModeButton.setGeometry(QtCore.QRect(260, 10, 156, 41))
|
||||||
font = QtGui.QFont()
|
font = QtGui.QFont()
|
||||||
font.setFamily("Lucida Grande")
|
font.setFamily("Lucida Grande")
|
||||||
font.setPointSize(12)
|
font.setPointSize(12)
|
||||||
@@ -324,6 +323,17 @@ class Ui_KCC(object):
|
|||||||
self.customHeight.setMaxLength(4)
|
self.customHeight.setMaxLength(4)
|
||||||
self.customHeight.setObjectName("customHeight")
|
self.customHeight.setObjectName("customHeight")
|
||||||
self.gridLayout_2.addWidget(self.customHeight, 0, 3, 1, 1)
|
self.gridLayout_2.addWidget(self.customHeight, 0, 3, 1, 1)
|
||||||
|
self.EditorButton = QtWidgets.QPushButton(self.Form)
|
||||||
|
self.EditorButton.setGeometry(QtCore.QRect(160, 10, 101, 41))
|
||||||
|
font = QtGui.QFont()
|
||||||
|
font.setFamily("Lucida Grande")
|
||||||
|
font.setPointSize(12)
|
||||||
|
self.EditorButton.setFont(font)
|
||||||
|
self.EditorButton.setFocusPolicy(QtCore.Qt.NoFocus)
|
||||||
|
icon5 = QtGui.QIcon()
|
||||||
|
icon5.addPixmap(QtGui.QPixmap(":/Other/icons/editor.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||||
|
self.EditorButton.setIcon(icon5)
|
||||||
|
self.EditorButton.setObjectName("EditorButton")
|
||||||
KCC.setCentralWidget(self.Form)
|
KCC.setCentralWidget(self.Form)
|
||||||
self.statusBar = QtWidgets.QStatusBar(KCC)
|
self.statusBar = QtWidgets.QStatusBar(KCC)
|
||||||
font = QtGui.QFont()
|
font = QtGui.QFont()
|
||||||
@@ -393,6 +403,7 @@ class Ui_KCC(object):
|
|||||||
self.hLabel.setText(_translate("KCC", "Custom height: "))
|
self.hLabel.setText(_translate("KCC", "Custom height: "))
|
||||||
self.customHeight.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Resolution of target device.</p></body></html>"))
|
self.customHeight.setToolTip(_translate("KCC", "<html><head/><body><p style=\'white-space:pre\'>Resolution of target device.</p></body></html>"))
|
||||||
self.customHeight.setInputMask(_translate("KCC", "0000"))
|
self.customHeight.setInputMask(_translate("KCC", "0000"))
|
||||||
|
self.EditorButton.setText(_translate("KCC", "Editor"))
|
||||||
self.ActionBasic.setText(_translate("KCC", "Basic"))
|
self.ActionBasic.setText(_translate("KCC", "Basic"))
|
||||||
self.ActionAdvanced.setText(_translate("KCC", "Advanced"))
|
self.ActionAdvanced.setText(_translate("KCC", "Advanced"))
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
__version__ = '4.4'
|
__version__ = '4.6.5'
|
||||||
__license__ = 'ISC'
|
__license__ = 'ISC'
|
||||||
__copyright__ = '2012-2015, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>'
|
__copyright__ = '2012-2015, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
@@ -16,18 +16,18 @@
|
|||||||
# PERFORMANCE OF THIS SOFTWARE.
|
# PERFORMANCE OF THIS SOFTWARE.
|
||||||
#
|
#
|
||||||
|
|
||||||
__license__ = 'ISC'
|
|
||||||
__copyright__ = '2012-2015, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>'
|
|
||||||
__docformat__ = 'restructuredtext en'
|
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
from zipfile import is_zipfile, ZipFile
|
from zipfile import is_zipfile, ZipFile
|
||||||
from subprocess import STDOUT, PIPE
|
from subprocess import STDOUT, PIPE
|
||||||
from psutil import Popen
|
from psutil import Popen
|
||||||
from shutil import move, copy
|
from shutil import move, copy
|
||||||
|
try:
|
||||||
|
from scandir import walk
|
||||||
|
except ImportError:
|
||||||
|
walk = os.walk
|
||||||
from . import rarfile
|
from . import rarfile
|
||||||
from .shared import check7ZFile as is_7zfile
|
from .shared import check7ZFile as is_7zfile, saferReplace
|
||||||
|
|
||||||
|
|
||||||
class CBxArchive:
|
class CBxArchive:
|
||||||
@@ -49,7 +49,7 @@ class CBxArchive:
|
|||||||
cbzFile = ZipFile(self.origFileName)
|
cbzFile = ZipFile(self.origFileName)
|
||||||
filelist = []
|
filelist = []
|
||||||
for f in cbzFile.namelist():
|
for f in cbzFile.namelist():
|
||||||
if f.startswith('__MACOSX') or f.endswith('.DS_Store') or f.endswith('thumbs.db'):
|
if f.startswith('__MACOSX') or f.endswith('.DS_Store') or f.endswith('humbs.db'):
|
||||||
pass # skip MacOS special files
|
pass # skip MacOS special files
|
||||||
elif f.endswith('/'):
|
elif f.endswith('/'):
|
||||||
try:
|
try:
|
||||||
@@ -62,26 +62,19 @@ class CBxArchive:
|
|||||||
|
|
||||||
def extractCBR(self, targetdir):
|
def extractCBR(self, targetdir):
|
||||||
cbrFile = rarfile.RarFile(self.origFileName)
|
cbrFile = rarfile.RarFile(self.origFileName)
|
||||||
filelist = []
|
cbrFile.extractall(targetdir)
|
||||||
for f in cbrFile.namelist():
|
for root, dirnames, filenames in walk(targetdir):
|
||||||
if f.startswith('__MACOSX') or f.endswith('.DS_Store') or f.endswith('thumbs.db'):
|
for filename in filenames:
|
||||||
pass # skip MacOS special files
|
if filename.startswith('__MACOSX') or filename.endswith('.DS_Store') or filename.endswith('humbs.db'):
|
||||||
elif f.endswith('/'):
|
os.remove(os.path.join(root, filename))
|
||||||
try:
|
|
||||||
os.makedirs(os.path.join(targetdir, f))
|
|
||||||
except Exception:
|
|
||||||
pass # the dir exists so we are going to extract the images only.
|
|
||||||
else:
|
|
||||||
filelist.append(f)
|
|
||||||
cbrFile.extractall(targetdir, filelist)
|
|
||||||
|
|
||||||
def extractCB7(self, targetdir):
|
def extractCB7(self, targetdir):
|
||||||
# Workaround for some wide UTF-8 + Popen abnormalities
|
# Workaround for some wide UTF-8 + Popen abnormalities
|
||||||
if sys.platform.startswith('darwin'):
|
if sys.platform.startswith('darwin'):
|
||||||
copy(self.origFileName, os.path.join(os.path.dirname(self.origFileName), 'TMP_KCC_TMP'))
|
copy(self.origFileName, os.path.join(os.path.dirname(self.origFileName), 'TMP_KCC_TMP'))
|
||||||
self.origFileName = os.path.join(os.path.dirname(self.origFileName), 'TMP_KCC_TMP')
|
self.origFileName = os.path.join(os.path.dirname(self.origFileName), 'TMP_KCC_TMP')
|
||||||
output = Popen('7za x "' + self.origFileName + '" -xr!__MACOSX -xr!.DS_Store -xr!thumbs.db -o"'
|
output = Popen('7za x "' + self.origFileName + '" -xr!__MACOSX -xr!.DS_Store -xr!thumbs.db -xr!Thumbs.db -o"' +
|
||||||
+ targetdir + '"', stdout=PIPE, stderr=STDOUT, shell=True)
|
targetdir + '"', stdout=PIPE, stderr=STDOUT, shell=True)
|
||||||
extracted = False
|
extracted = False
|
||||||
for line in output.stdout:
|
for line in output.stdout:
|
||||||
if b"Everything is Ok" in line:
|
if b"Everything is Ok" in line:
|
||||||
@@ -105,7 +98,7 @@ class CBxArchive:
|
|||||||
for f in os.listdir(os.path.join(targetdir, adir[0])):
|
for f in os.listdir(os.path.join(targetdir, adir[0])):
|
||||||
# If directory names contain UTF-8 chars shutil.move can't clean up the mess alone
|
# If directory names contain UTF-8 chars shutil.move can't clean up the mess alone
|
||||||
if os.path.isdir(os.path.join(targetdir, f)):
|
if os.path.isdir(os.path.join(targetdir, f)):
|
||||||
os.replace(os.path.join(targetdir, adir[0], f), os.path.join(targetdir, adir[0], f + '-A'))
|
saferReplace(os.path.join(targetdir, adir[0], f), os.path.join(targetdir, adir[0], f + '-A'))
|
||||||
f += '-A'
|
f += '-A'
|
||||||
move(os.path.join(targetdir, adir[0], f), targetdir)
|
move(os.path.join(targetdir, adir[0], f), targetdir)
|
||||||
os.rmdir(os.path.join(targetdir, adir[0]))
|
os.rmdir(os.path.join(targetdir, adir[0]))
|
||||||
|
|||||||
@@ -18,40 +18,42 @@
|
|||||||
# PERFORMANCE OF THIS SOFTWARE.
|
# PERFORMANCE OF THIS SOFTWARE.
|
||||||
#
|
#
|
||||||
|
|
||||||
__version__ = '4.4'
|
|
||||||
__license__ = 'ISC'
|
|
||||||
__copyright__ = '2012-2015, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>'
|
|
||||||
__docformat__ = 'restructuredtext en'
|
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
from time import strftime, gmtime
|
||||||
from copy import copy
|
from copy import copy
|
||||||
from glob import glob
|
from glob import glob
|
||||||
from json import loads
|
from json import loads
|
||||||
from urllib.request import Request, urlopen
|
from urllib.request import Request, urlopen
|
||||||
from re import split, sub
|
from re import sub
|
||||||
from stat import S_IWRITE, S_IREAD, S_IEXEC
|
from stat import S_IWRITE, S_IREAD, S_IEXEC
|
||||||
from zipfile import ZipFile, ZIP_STORED, ZIP_DEFLATED
|
from zipfile import ZipFile, ZIP_STORED, ZIP_DEFLATED
|
||||||
from tempfile import mkdtemp
|
from tempfile import mkdtemp, gettempdir, TemporaryFile
|
||||||
from shutil import move, copytree, rmtree
|
from shutil import move, copytree, rmtree
|
||||||
from optparse import OptionParser, OptionGroup
|
from optparse import OptionParser, OptionGroup
|
||||||
from multiprocessing import Pool
|
from multiprocessing import Pool
|
||||||
from xml.dom.minidom import parse
|
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
from slugify import slugify as slugifyExt
|
from slugify import slugify as slugifyExt
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
from subprocess import STDOUT, PIPE
|
from subprocess import STDOUT, PIPE
|
||||||
from psutil import Popen, virtual_memory
|
from psutil import Popen, virtual_memory
|
||||||
|
from html import escape
|
||||||
try:
|
try:
|
||||||
from PyQt5 import QtCore
|
from PyQt5 import QtCore
|
||||||
except ImportError:
|
except ImportError:
|
||||||
QtCore = None
|
QtCore = None
|
||||||
from .shared import md5Checksum, getImageFileName, walkLevel
|
try:
|
||||||
|
from scandir import walk
|
||||||
|
except ImportError:
|
||||||
|
walk = os.walk
|
||||||
|
from .shared import md5Checksum, getImageFileName, walkSort, walkLevel, saferReplace
|
||||||
from . import comic2panel
|
from . import comic2panel
|
||||||
from . import image
|
from . import image
|
||||||
from . import cbxarchive
|
from . import cbxarchive
|
||||||
from . import pdfjpgextract
|
from . import pdfjpgextract
|
||||||
from . import dualmetafix
|
from . import dualmetafix
|
||||||
|
from . import metadata
|
||||||
|
from . import __version__
|
||||||
|
|
||||||
|
|
||||||
def main(argv=None):
|
def main(argv=None):
|
||||||
@@ -67,6 +69,7 @@ def main(argv=None):
|
|||||||
print('No matching files found.')
|
print('No matching files found.')
|
||||||
return
|
return
|
||||||
for source in sources:
|
for source in sources:
|
||||||
|
source = source.rstrip('\\').rstrip('/')
|
||||||
options = copy(optionstemplate)
|
options = copy(optionstemplate)
|
||||||
checkOptions()
|
checkOptions()
|
||||||
if len(sources) > 1:
|
if len(sources) > 1:
|
||||||
@@ -75,9 +78,10 @@ def main(argv=None):
|
|||||||
return outputPath
|
return outputPath
|
||||||
|
|
||||||
|
|
||||||
def buildHTML(path, imgfile, imgfilepath):
|
def buildHTML(path, imgfile, imgfilepath, forcePV=False):
|
||||||
imgfilepath = md5Checksum(imgfilepath)
|
imgfilepath = md5Checksum(imgfilepath)
|
||||||
filename = getImageFileName(imgfile)
|
filename = getImageFileName(imgfile)
|
||||||
|
additionalStyle = ''
|
||||||
if options.imgproc:
|
if options.imgproc:
|
||||||
if "Rotated" in options.imgIndex[imgfilepath]:
|
if "Rotated" in options.imgIndex[imgfilepath]:
|
||||||
rotatedPage = True
|
rotatedPage = True
|
||||||
@@ -95,11 +99,17 @@ def buildHTML(path, imgfile, imgfilepath):
|
|||||||
noVerticalPV = True
|
noVerticalPV = True
|
||||||
else:
|
else:
|
||||||
noVerticalPV = False
|
noVerticalPV = False
|
||||||
|
if "BlackFill" in options.imgIndex[imgfilepath]:
|
||||||
|
additionalStyle = ' style="background-color:#000000" '
|
||||||
else:
|
else:
|
||||||
rotatedPage = False
|
rotatedPage = False
|
||||||
noPV = False
|
noPV = False
|
||||||
noHorizontalPV = False
|
noHorizontalPV = False
|
||||||
noVerticalPV = False
|
noVerticalPV = False
|
||||||
|
if forcePV and noPV:
|
||||||
|
noPV = False
|
||||||
|
noHorizontalPV = True
|
||||||
|
noVerticalPV = True
|
||||||
htmlpath = ''
|
htmlpath = ''
|
||||||
postfix = ''
|
postfix = ''
|
||||||
backref = 1
|
backref = 1
|
||||||
@@ -115,21 +125,22 @@ def buildHTML(path, imgfile, imgfilepath):
|
|||||||
os.makedirs(htmlpath)
|
os.makedirs(htmlpath)
|
||||||
htmlfile = os.path.join(htmlpath, filename[0] + '.html')
|
htmlfile = os.path.join(htmlpath, filename[0] + '.html')
|
||||||
f = open(htmlfile, "w", encoding='UTF-8')
|
f = open(htmlfile, "w", encoding='UTF-8')
|
||||||
f.writelines(["<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" ",
|
if options.iskindle:
|
||||||
"\"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n",
|
f.writelines(["<?xml version=\"1.0\" encoding=\"utf-8\"?>\n",
|
||||||
"<html xmlns=\"http://www.w3.org/1999/xhtml\">\n",
|
"<!DOCTYPE html>\n",
|
||||||
|
"<html xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:epub=\"http://www.idpf.org/2007/ops\">\n",
|
||||||
"<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",
|
"<link href=\"", "../" * (backref - 1), "style.css\" type=\"text/css\" rel=\"stylesheet\"/>\n",
|
||||||
"<link href=\"", "../" * (backref - 1),
|
"<meta charset=\"utf-8\"/>\n",
|
||||||
"style.css\" type=\"text/css\" rel=\"stylesheet\"/>\n",
|
|
||||||
"</head>\n",
|
"</head>\n",
|
||||||
"<body>\n",
|
"<body" + additionalStyle + ">\n",
|
||||||
"<div class=\"fs\">\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"
|
||||||
])
|
])
|
||||||
if options.panelview and not noPV:
|
if (options.panelview or forcePV) and not noPV:
|
||||||
|
options.panelviewused = True
|
||||||
if not noHorizontalPV and not noVerticalPV:
|
if not noHorizontalPV and not noVerticalPV:
|
||||||
if rotatedPage:
|
if rotatedPage:
|
||||||
if options.righttoleft:
|
if options.righttoleft:
|
||||||
@@ -167,8 +178,8 @@ def buildHTML(path, imgfile, imgfilepath):
|
|||||||
f.writelines(["<div id=\"" + boxes[i] + "\"><a class=\"app-amzn-magnify\" data-app-amzn-magnify=",
|
f.writelines(["<div id=\"" + boxes[i] + "\"><a class=\"app-amzn-magnify\" data-app-amzn-magnify=",
|
||||||
"'{\"targetId\":\"" + boxes[i] + "-Panel-Parent\", \"ordinal\":" + str(order[i]),
|
"'{\"targetId\":\"" + boxes[i] + "-Panel-Parent\", \"ordinal\":" + str(order[i]),
|
||||||
"}'></a></div>\n"])
|
"}'></a></div>\n"])
|
||||||
if options.quality == 2:
|
if options.quality == 2 and not forcePV:
|
||||||
imgfilepv = str.split(imgfile, ".")
|
imgfilepv = imgfile.split(".")
|
||||||
imgfilepv[0] += "-hq"
|
imgfilepv[0] += "-hq"
|
||||||
imgfilepv = ".".join(imgfilepv)
|
imgfilepv = ".".join(imgfilepv)
|
||||||
else:
|
else:
|
||||||
@@ -191,6 +202,21 @@ def buildHTML(path, imgfile, imgfilepath):
|
|||||||
"\"/></div></div>\n",
|
"\"/></div></div>\n",
|
||||||
])
|
])
|
||||||
f.writelines(["</div>\n</body>\n</html>"])
|
f.writelines(["</div>\n</body>\n</html>"])
|
||||||
|
else:
|
||||||
|
f.writelines(["<?xml version=\"1.0\" encoding=\"utf-8\"?>\n",
|
||||||
|
"<!DOCTYPE html>\n",
|
||||||
|
"<html xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:epub=\"http://www.idpf.org/2007/ops\">\n",
|
||||||
|
"<head>\n",
|
||||||
|
"<title>", filename[0], "</title>\n",
|
||||||
|
"<link href=\"", "../" * (backref - 1), "style.css\" type=\"text/css\" rel=\"stylesheet\"/>\n",
|
||||||
|
"<meta charset=\"utf-8\"/>\n",
|
||||||
|
"</head>\n",
|
||||||
|
"<body" + additionalStyle + ">\n",
|
||||||
|
"<div class=\"epub:type=bodymatter\">\n",
|
||||||
|
"<img src=\"", "../" * backref, "Images/", postfix, imgfile, "\"/>\n",
|
||||||
|
"</div>\n",
|
||||||
|
"</body>\n</html>"
|
||||||
|
])
|
||||||
f.close()
|
f.close()
|
||||||
return path, imgfile
|
return path, imgfile
|
||||||
|
|
||||||
@@ -200,11 +226,9 @@ def buildNCX(dstdir, title, chapters, chapterNames):
|
|||||||
ncxfile = os.path.join(dstdir, 'OEBPS', 'toc.ncx')
|
ncxfile = os.path.join(dstdir, 'OEBPS', 'toc.ncx')
|
||||||
f = open(ncxfile, "w", encoding='UTF-8')
|
f = open(ncxfile, "w", encoding='UTF-8')
|
||||||
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\" ",
|
|
||||||
"\"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=\"", options.uuid, "\"/>\n",
|
"<meta name=\"dtb:uid\" content=\"urn:uuid:", options.uuid, "\"/>\n",
|
||||||
"<meta name=\"dtb:depth\" content=\"1\"/>\n",
|
"<meta name=\"dtb:depth\" content=\"1\"/>\n",
|
||||||
"<meta name=\"dtb:totalPageCount\" content=\"0\"/>\n",
|
"<meta name=\"dtb:totalPageCount\" content=\"0\"/>\n",
|
||||||
"<meta name=\"dtb:maxPageNumber\" content=\"0\"/>\n",
|
"<meta name=\"dtb:maxPageNumber\" content=\"0\"/>\n",
|
||||||
@@ -215,16 +239,58 @@ def buildNCX(dstdir, title, chapters, chapterNames):
|
|||||||
])
|
])
|
||||||
for chapter in chapters:
|
for chapter in chapters:
|
||||||
folder = chapter[0].replace(os.path.join(dstdir, 'OEBPS'), '').lstrip('/').lstrip('\\\\')
|
folder = chapter[0].replace(os.path.join(dstdir, 'OEBPS'), '').lstrip('/').lstrip('\\\\')
|
||||||
if os.path.basename(folder) != "Text":
|
|
||||||
title = chapterNames[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('/', '_').replace('\\', '_') + "\"><navLabel><text>"
|
navID = folder.replace('/', '_').replace('\\', '_')
|
||||||
+ title + "</text></navLabel><content src=\"" + filename[0].replace("\\", "/")
|
if options.chapters:
|
||||||
+ ".html\"/></navPoint>\n")
|
title = chapterNames[chapter[1]]
|
||||||
|
navID = filename[0].replace('/', '_').replace('\\', '_')
|
||||||
|
elif os.path.basename(folder) != "Text":
|
||||||
|
title = chapterNames[os.path.basename(folder)]
|
||||||
|
f.write("<navPoint id=\"" + navID + "\"><navLabel><text>" +
|
||||||
|
title + "</text></navLabel><content src=\"" + filename[0].replace("\\", "/") +
|
||||||
|
".html\"/></navPoint>\n")
|
||||||
f.write("</navMap>\n</ncx>")
|
f.write("</navMap>\n</ncx>")
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
|
|
||||||
|
def buildNAV(dstdir, title, chapters, chapterNames):
|
||||||
|
navfile = os.path.join(dstdir, 'OEBPS', 'nav.xhtml')
|
||||||
|
f = open(navfile, "w", encoding='UTF-8')
|
||||||
|
f.writelines(["<?xml version=\"1.0\" encoding=\"utf-8\"?>\n",
|
||||||
|
"<!DOCTYPE html>\n",
|
||||||
|
"<html xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:epub=\"http://www.idpf.org/2007/ops\">\n",
|
||||||
|
"<head>\n",
|
||||||
|
"<title>" + title + "</title>\n",
|
||||||
|
"<meta charset=\"utf-8\"/>\n",
|
||||||
|
"</head>\n",
|
||||||
|
"<body>\n",
|
||||||
|
"<nav xmlns:epub=\"http://www.idpf.org/2007/ops\" epub:type=\"toc\" id=\"toc\">\n",
|
||||||
|
"<ol>\n"])
|
||||||
|
for chapter in chapters:
|
||||||
|
folder = chapter[0].replace(os.path.join(dstdir, 'OEBPS'), '').lstrip('/').lstrip('\\\\')
|
||||||
|
filename = getImageFileName(os.path.join(folder, chapter[1]))
|
||||||
|
if options.chapters:
|
||||||
|
title = chapterNames[chapter[1]]
|
||||||
|
elif os.path.basename(folder) != "Text":
|
||||||
|
title = chapterNames[os.path.basename(folder)]
|
||||||
|
f.write("<li><a href=\"" + filename[0].replace("\\", "/") + ".html\">" + title + "</a></li>\n")
|
||||||
|
f.writelines(["</ol>\n",
|
||||||
|
"</nav>\n",
|
||||||
|
"<nav epub:type=\"page-list\">\n",
|
||||||
|
"<ol>\n"
|
||||||
|
])
|
||||||
|
for chapter in chapters:
|
||||||
|
folder = chapter[0].replace(os.path.join(dstdir, 'OEBPS'), '').lstrip('/').lstrip('\\\\')
|
||||||
|
filename = getImageFileName(os.path.join(folder, chapter[1]))
|
||||||
|
if options.chapters:
|
||||||
|
title = chapterNames[chapter[1]]
|
||||||
|
elif os.path.basename(folder) != "Text":
|
||||||
|
title = chapterNames[os.path.basename(folder)]
|
||||||
|
f.write("<li><a href=\"" + filename[0].replace("\\", "/") + ".html\">" + title + "</a></li>\n")
|
||||||
|
f.write("</ol>\n</nav>\n</body>\n</html>")
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
|
||||||
def buildOPF(dstdir, title, filelist, cover=None):
|
def buildOPF(dstdir, title, filelist, cover=None):
|
||||||
opffile = os.path.join(dstdir, 'OEBPS', 'content.opf')
|
opffile = os.path.join(dstdir, 'OEBPS', 'content.opf')
|
||||||
profilelabel, deviceres, palette, gamma, panelviewsize = options.profileData
|
profilelabel, deviceres, palette, gamma, panelviewsize = options.profileData
|
||||||
@@ -234,60 +300,68 @@ def buildOPF(dstdir, title, filelist, cover=None):
|
|||||||
writingmode = "horizontal-lr"
|
writingmode = "horizontal-lr"
|
||||||
f = open(opffile, "w", encoding='UTF-8')
|
f = open(opffile, "w", encoding='UTF-8')
|
||||||
f.writelines(["<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n",
|
f.writelines(["<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n",
|
||||||
"<package version=\"2.0\" unique-identifier=\"BookID\" ",
|
"<package version=\"3.0\" unique-identifier=\"BookID\" ",
|
||||||
|
"prefix=\"rendition: http://www.idpf.org/vocab/rendition/#\" ",
|
||||||
"xmlns=\"http://www.idpf.org/2007/opf\">\n",
|
"xmlns=\"http://www.idpf.org/2007/opf\">\n",
|
||||||
"<metadata xmlns:opf=\"http://www.idpf.org/2007/opf\" ",
|
"<metadata xmlns:opf=\"http://www.idpf.org/2007/opf\" ",
|
||||||
"xmlns:dc=\"http://purl.org/dc/elements/1.1/\">\n",
|
"xmlns:dc=\"http://purl.org/dc/elements/1.1/\">\n",
|
||||||
"<dc:title>", title, "</dc:title>\n",
|
"<dc:title>", title, "</dc:title>\n",
|
||||||
"<dc:language>en-US</dc:language>\n",
|
"<dc:language>en-US</dc:language>\n",
|
||||||
"<dc:identifier id=\"BookID\" opf:scheme=\"UUID\">", options.uuid, "</dc:identifier>\n"])
|
"<dc:identifier id=\"BookID\">urn:uuid:", options.uuid, "</dc:identifier>\n",
|
||||||
|
"<dc:contributor id=\"contributor\">KindleComicConverter-" + __version__ + "</dc:contributor>\n",
|
||||||
|
"<dc:description>", options.summary, "</dc:description>\n"])
|
||||||
for author in options.authors:
|
for author in options.authors:
|
||||||
f.writelines(["<dc:creator>", author, "</dc:creator>\n"])
|
f.writelines(["<dc:creator>", author, "</dc:creator>\n"])
|
||||||
f.writelines(["<meta name=\"generator\" content=\"KindleComicConverter-" + __version__ + "\"/>\n",
|
f.writelines(["<meta property=\"dcterms:modified\">" + strftime("%Y-%m-%dT%H:%M:%SZ", gmtime()) + "</meta>\n",
|
||||||
"<meta name=\"RegionMagnification\" content=\"true\"/>\n",
|
|
||||||
"<meta name=\"region-mag\" content=\"true\"/>\n",
|
|
||||||
"<meta name=\"cover\" content=\"cover\"/>\n",
|
"<meta name=\"cover\" content=\"cover\"/>\n",
|
||||||
|
"<meta property=\"rendition:orientation\">portrait</meta>\n",
|
||||||
|
"<meta property=\"rendition:spread\">portrait</meta>\n",
|
||||||
|
"<meta property=\"rendition:layout\">pre-paginated</meta>\n"])
|
||||||
|
if options.iskindle and options.profile != 'Custom':
|
||||||
|
f.writelines(["<meta name=\"original-resolution\" content=\"",
|
||||||
|
str(deviceres[0]) + "x" + str(deviceres[1]) + "\"/>\n",
|
||||||
"<meta name=\"book-type\" content=\"comic\"/>\n",
|
"<meta name=\"book-type\" content=\"comic\"/>\n",
|
||||||
"<meta name=\"rendition:layout\" content=\"pre-paginated\"/>\n",
|
"<meta name=\"RegionMagnification\" content=\"true\"/>\n",
|
||||||
|
"<meta name=\"primary-writing-mode\" content=\"" + writingmode + "\"/>\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=\"rendition:orientation\" content=\"portrait\"/>\n",
|
|
||||||
"<meta name=\"orientation-lock\" content=\"portrait\"/>\n",
|
|
||||||
"<meta name=\"original-resolution\" content=\"",
|
|
||||||
str(deviceres[0]) + "x" + str(deviceres[1]), "\"/>\n",
|
|
||||||
"<meta name=\"primary-writing-mode\" content=\"", writingmode, "\"/>\n",
|
|
||||||
"<meta name=\"ke-border-color\" content=\"#ffffff\"/>\n",
|
"<meta name=\"ke-border-color\" content=\"#ffffff\"/>\n",
|
||||||
"<meta name=\"ke-border-width\" content=\"0\"/>\n",
|
"<meta name=\"ke-border-width\" content=\"0\"/>\n"])
|
||||||
"</metadata>\n<manifest>\n<item id=\"ncx\" href=\"toc.ncx\" ",
|
f.writelines(["</metadata>\n<manifest>\n<item id=\"ncx\" href=\"toc.ncx\" ",
|
||||||
"media-type=\"application/x-dtbncx+xml\"/>\n"])
|
"media-type=\"application/x-dtbncx+xml\"/>\n",
|
||||||
|
"<item id=\"nav\" href=\"nav.xhtml\" ",
|
||||||
|
"properties=\"nav\" media-type=\"application/xhtml+xml\"/>\n"])
|
||||||
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]:
|
||||||
mt = 'image/png'
|
mt = 'image/png'
|
||||||
else:
|
else:
|
||||||
mt = 'image/jpeg'
|
mt = 'image/jpeg'
|
||||||
f.write("<item id=\"cover\" href=\"Images/cover" + filename[1] + "\" media-type=\"" + mt + "\"/>\n")
|
f.write("<item id=\"cover\" href=\"Images/cover" + filename[1] + "\" media-type=\"" + mt +
|
||||||
|
"\" properties=\"cover-image\"/>\n")
|
||||||
reflist = []
|
reflist = []
|
||||||
for path in filelist:
|
for path in filelist:
|
||||||
folder = path[0].replace(os.path.join(dstdir, 'OEBPS'), '').lstrip('/').lstrip('\\\\').replace("\\", "/")
|
folder = path[0].replace(os.path.join(dstdir, 'OEBPS'), '').lstrip('/').lstrip('\\\\').replace("\\", "/")
|
||||||
filename = getImageFileName(path[1])
|
filename = getImageFileName(path[1])
|
||||||
uniqueid = os.path.join(folder, filename[0]).replace('/', '_').replace('\\', '_')
|
uniqueid = os.path.join(folder, filename[0]).replace('/', '_').replace('\\', '_')
|
||||||
reflist.append(uniqueid)
|
reflist.append(uniqueid)
|
||||||
f.write("<item id=\"page_" + str(uniqueid) + "\" href=\""
|
f.write("<item id=\"page_" + str(uniqueid) + "\" href=\"" +
|
||||||
+ 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_" + str(uniqueid) + "\" href=\"" + folder + "/" + path[1] + "\" media-type=\""
|
f.write("<item id=\"img_" + str(uniqueid) + "\" href=\"" + folder + "/" + path[1] + "\" media-type=\"" +
|
||||||
+ mt + "\"/>\n")
|
mt + "\"/>\n")
|
||||||
f.write("<item id=\"css\" href=\"Text/style.css\" media-type=\"text/css\"/>\n")
|
f.write("<item id=\"css\" href=\"Text/style.css\" media-type=\"text/css\"/>\n")
|
||||||
f.write("</manifest>\n<spine toc=\"ncx\">\n")
|
if options.righttoleft:
|
||||||
|
f.write("</manifest>\n<spine page-progression-direction=\"rtl\" toc=\"ncx\">\n")
|
||||||
|
else:
|
||||||
|
f.write("</manifest>\n<spine page-progression-direction=\"ltr\" toc=\"ncx\">\n")
|
||||||
for entry in reflist:
|
for entry in reflist:
|
||||||
f.write("<itemref idref=\"page_" + entry + "\"/>\n")
|
f.write("<itemref idref=\"page_" + entry + "\"/>\n")
|
||||||
f.write("</spine>\n<guide>\n</guide>\n</package>\n")
|
f.write("</spine>\n</package>\n")
|
||||||
f.close()
|
f.close()
|
||||||
os.mkdir(os.path.join(dstdir, 'META-INF'))
|
os.mkdir(os.path.join(dstdir, 'META-INF'))
|
||||||
f = open(os.path.join(dstdir, 'META-INF', 'container.xml'), 'w', encoding='UTF-8')
|
f = open(os.path.join(dstdir, 'META-INF', 'container.xml'), 'w', encoding='UTF-8')
|
||||||
@@ -304,11 +378,11 @@ def buildEPUB(path, chapterNames, tomeNumber):
|
|||||||
filelist = []
|
filelist = []
|
||||||
chapterlist = []
|
chapterlist = []
|
||||||
cover = None
|
cover = None
|
||||||
|
lastfile = None
|
||||||
_, deviceres, _, _, panelviewsize = options.profileData
|
_, deviceres, _, _, panelviewsize = options.profileData
|
||||||
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', encoding='UTF-8')
|
f = open(os.path.join(path, 'OEBPS', 'Text', 'style.css'), 'w', encoding='UTF-8')
|
||||||
# DON'T COMPRESS CSS. KINDLE WILL FAIL TO PARSE IT.
|
if options.iskindle:
|
||||||
# Generic Panel View support + Margins fix for Non-Kindle devices.
|
|
||||||
f.writelines(["@page {\n",
|
f.writelines(["@page {\n",
|
||||||
"margin-bottom: 0;\n",
|
"margin-bottom: 0;\n",
|
||||||
"margin-top: 0\n",
|
"margin-top: 0\n",
|
||||||
@@ -415,13 +489,28 @@ def buildEPUB(path, chapterNames, tomeNumber):
|
|||||||
"width: 50%;\n",
|
"width: 50%;\n",
|
||||||
"}",
|
"}",
|
||||||
])
|
])
|
||||||
|
else:
|
||||||
|
f.writelines([
|
||||||
|
"@namespace epub \"http://www.idpf.org/2007/ops\";\n",
|
||||||
|
"@charset \"UTF-8\";\n",
|
||||||
|
"body {\n",
|
||||||
|
"margin: 0;\n",
|
||||||
|
"}\n",
|
||||||
|
"img {\n",
|
||||||
|
"position: absolute;\n",
|
||||||
|
"margin: 0;\n",
|
||||||
|
"z-index: 0;\n",
|
||||||
|
"height: 100%;\n",
|
||||||
|
"}"])
|
||||||
f.close()
|
f.close()
|
||||||
for (dirpath, dirnames, filenames) in os.walk(os.path.join(path, 'OEBPS', 'Images')):
|
for (dirpath, dirnames, filenames) in walk(os.path.join(path, 'OEBPS', 'Images')):
|
||||||
chapter = False
|
chapter = False
|
||||||
|
dirnames, filenames = walkSort(dirnames, filenames)
|
||||||
for afile in filenames:
|
for afile in filenames:
|
||||||
filename = getImageFileName(afile)
|
filename = getImageFileName(afile)
|
||||||
if '-kcc-hq' not in filename[0]:
|
if '-kcc-hq' not in filename[0]:
|
||||||
filelist.append(buildHTML(dirpath, afile, os.path.join(dirpath, afile)))
|
filelist.append(buildHTML(dirpath, afile, os.path.join(dirpath, afile)))
|
||||||
|
lastfile = (dirpath, afile, os.path.join(dirpath, afile))
|
||||||
if not chapter:
|
if not chapter:
|
||||||
chapterlist.append((dirpath.replace('Images', 'Text'), filelist[-1][1]))
|
chapterlist.append((dirpath.replace('Images', 'Text'), filelist[-1][1]))
|
||||||
chapter = True
|
chapter = True
|
||||||
@@ -429,32 +518,41 @@ def buildEPUB(path, chapterNames, tomeNumber):
|
|||||||
cover = os.path.join(os.path.join(path, 'OEBPS', 'Images'),
|
cover = os.path.join(os.path.join(path, 'OEBPS', 'Images'),
|
||||||
'cover' + getImageFileName(filelist[-1][1])[1])
|
'cover' + getImageFileName(filelist[-1][1])[1])
|
||||||
image.Cover(os.path.join(filelist[-1][0], filelist[-1][1]), cover, options, tomeNumber)
|
image.Cover(os.path.join(filelist[-1][0], filelist[-1][1]), cover, options, tomeNumber)
|
||||||
|
# Hack that force Panel View on at last one page
|
||||||
|
if lastfile and not options.panelviewused and 'Ko' not in options.profile \
|
||||||
|
and options.profile not in ['K1', 'K2', 'KDX', 'Custom']:
|
||||||
|
filelist[-1] = buildHTML(lastfile[0], lastfile[1], lastfile[2], True)
|
||||||
|
# Overwrite chapternames if tree is flat and ComicInfo.xml has bookmarks
|
||||||
|
if not chapterNames and options.chapters:
|
||||||
|
chapterlist = []
|
||||||
|
globaldiff = 0
|
||||||
|
for aChapter in options.chapters:
|
||||||
|
pageid = aChapter[0]
|
||||||
|
for x in range(0, pageid + globaldiff + 1):
|
||||||
|
if '-aaa-kcc' in filelist[x][1]:
|
||||||
|
pageid += 1
|
||||||
|
if '-bbb-kcc' in filelist[pageid][1]:
|
||||||
|
pageid -= 1
|
||||||
|
filename = filelist[pageid][1]
|
||||||
|
chapterlist.append((filelist[pageid][0].replace('Images', 'Text'), filename))
|
||||||
|
chapterNames[filename] = aChapter[1]
|
||||||
|
globaldiff = pageid - (aChapter[0] + globaldiff)
|
||||||
buildNCX(path, options.title, chapterlist, chapterNames)
|
buildNCX(path, options.title, chapterlist, chapterNames)
|
||||||
# Ensure we're sorting files alphabetically
|
buildNAV(path, options.title, chapterlist, chapterNames)
|
||||||
convert = lambda text: int(text) if text.isdigit() else text
|
|
||||||
alphanum_key = lambda key: [convert(c) for c in split('([0-9]+)', key)]
|
|
||||||
filelist.sort(key=lambda name: (alphanum_key(name[0].lower()), alphanum_key(name[1].lower())))
|
|
||||||
buildOPF(path, options.title, filelist, cover)
|
buildOPF(path, options.title, filelist, cover)
|
||||||
|
|
||||||
|
|
||||||
def imgOptimization(img, opt, hqImage=None):
|
def imgOptimization(img, opt):
|
||||||
if not img.fill:
|
if not img.fill:
|
||||||
img.getImageFill()
|
img.getImageFill()
|
||||||
if not opt.webtoon:
|
if not opt.webtoon:
|
||||||
img.cropWhiteSpace()
|
img.cropWhiteSpace()
|
||||||
if opt.cutpagenumbers and not opt.webtoon:
|
if opt.cutpagenumbers and not opt.webtoon:
|
||||||
img.cutPageNumber()
|
img.cutPageNumber()
|
||||||
img.optimizeImage()
|
img.autocontrastImage()
|
||||||
if hqImage:
|
|
||||||
img.resizeImage(0)
|
|
||||||
img.calculateBorder(hqImage, True)
|
|
||||||
else:
|
|
||||||
img.resizeImage()
|
img.resizeImage()
|
||||||
if opt.panelview:
|
if not img.second and opt.panelview:
|
||||||
if opt.quality == 0:
|
img.calculateBorder()
|
||||||
img.calculateBorder(img)
|
|
||||||
elif opt.quality == 1:
|
|
||||||
img.calculateBorder(img, True)
|
|
||||||
if opt.forcepng and not opt.forcecolor:
|
if opt.forcepng and not opt.forcecolor:
|
||||||
img.quantizeImage()
|
img.quantizeImage()
|
||||||
|
|
||||||
@@ -467,7 +565,7 @@ def imgDirectoryProcessing(path):
|
|||||||
options.imgPurgeIndex = []
|
options.imgPurgeIndex = []
|
||||||
work = []
|
work = []
|
||||||
pagenumber = 0
|
pagenumber = 0
|
||||||
for (dirpath, dirnames, filenames) in os.walk(path):
|
for (dirpath, dirnames, filenames) in walk(path):
|
||||||
for afile in filenames:
|
for afile in filenames:
|
||||||
pagenumber += 1
|
pagenumber += 1
|
||||||
work.append([afile, dirpath, options])
|
work.append([afile, dirpath, options])
|
||||||
@@ -516,10 +614,6 @@ def imgFileProcessing(work):
|
|||||||
opt = work[2]
|
opt = work[2]
|
||||||
output = []
|
output = []
|
||||||
img = image.ComicPage(os.path.join(dirpath, afile), opt)
|
img = image.ComicPage(os.path.join(dirpath, afile), opt)
|
||||||
if opt.quality == 2:
|
|
||||||
wipe = False
|
|
||||||
else:
|
|
||||||
wipe = True
|
|
||||||
if opt.nosplitrotate:
|
if opt.nosplitrotate:
|
||||||
splitter = None
|
splitter = None
|
||||||
else:
|
else:
|
||||||
@@ -527,87 +621,76 @@ def imgFileProcessing(work):
|
|||||||
if splitter is not None:
|
if splitter is not None:
|
||||||
img0 = image.ComicPage(splitter[0], opt)
|
img0 = image.ComicPage(splitter[0], opt)
|
||||||
imgOptimization(img0, opt)
|
imgOptimization(img0, opt)
|
||||||
|
if not img0.noHQ:
|
||||||
output.append(img0.saveToDir(dirpath))
|
output.append(img0.saveToDir(dirpath))
|
||||||
img1 = image.ComicPage(splitter[1], opt)
|
img1 = image.ComicPage(splitter[1], opt)
|
||||||
imgOptimization(img1, opt)
|
imgOptimization(img1, opt)
|
||||||
|
if not img1.noHQ:
|
||||||
output.append(img1.saveToDir(dirpath))
|
output.append(img1.saveToDir(dirpath))
|
||||||
if wipe:
|
output.extend([img.origFileName, img0.origFileName, img1.origFileName])
|
||||||
output.append(img0.origFileName)
|
|
||||||
output.append(img1.origFileName)
|
|
||||||
if opt.quality == 2:
|
if opt.quality == 2:
|
||||||
img0b = image.ComicPage(splitter[0], opt, img0.fill)
|
output.extend([img0.origFileName, img1.origFileName])
|
||||||
imgOptimization(img0b, opt, img0)
|
img0b = image.ComicPage(splitter[0], opt, img0)
|
||||||
|
imgOptimization(img0b, opt)
|
||||||
output.append(img0b.saveToDir(dirpath))
|
output.append(img0b.saveToDir(dirpath))
|
||||||
img1b = image.ComicPage(splitter[1], opt, img1.fill)
|
img1b = image.ComicPage(splitter[1], opt, img1)
|
||||||
imgOptimization(img1b, opt, img1)
|
imgOptimization(img1b, opt)
|
||||||
output.append(img1b.saveToDir(dirpath))
|
output.append(img1b.saveToDir(dirpath))
|
||||||
output.append(img0.origFileName)
|
|
||||||
output.append(img1.origFileName)
|
|
||||||
output.append(img.origFileName)
|
|
||||||
else:
|
else:
|
||||||
|
output.append(img.origFileName)
|
||||||
imgOptimization(img, opt)
|
imgOptimization(img, opt)
|
||||||
|
if not img.noHQ:
|
||||||
output.append(img.saveToDir(dirpath))
|
output.append(img.saveToDir(dirpath))
|
||||||
if wipe:
|
|
||||||
output.append(img.origFileName)
|
|
||||||
if opt.quality == 2:
|
if opt.quality == 2:
|
||||||
img2 = image.ComicPage(os.path.join(dirpath, afile), opt, img.fill)
|
img2 = image.ComicPage(os.path.join(dirpath, afile), opt, img)
|
||||||
if img.rotated:
|
imgOptimization(img2, opt)
|
||||||
img2.image = img2.image.rotate(90, Image.BICUBIC, True)
|
|
||||||
img2.rotated = True
|
|
||||||
imgOptimization(img2, opt, img)
|
|
||||||
output.append(img2.saveToDir(dirpath))
|
output.append(img2.saveToDir(dirpath))
|
||||||
output.append(img.origFileName)
|
|
||||||
return output
|
return output
|
||||||
except Exception:
|
except Exception:
|
||||||
return str(sys.exc_info()[1])
|
return str(sys.exc_info()[1])
|
||||||
|
|
||||||
|
|
||||||
def getWorkFolder(afile):
|
def getWorkFolder(afile):
|
||||||
if len(afile) > 240:
|
|
||||||
raise UserWarning("Path is too long.")
|
|
||||||
if os.path.isdir(afile):
|
if os.path.isdir(afile):
|
||||||
workdir = mkdtemp('', 'KCC-TMP-')
|
workdir = mkdtemp('', 'KCC-')
|
||||||
try:
|
try:
|
||||||
os.rmdir(workdir) # needed for copytree() fails if dst already exists
|
os.rmdir(workdir)
|
||||||
fullPath = os.path.join(workdir, 'OEBPS', 'Images')
|
fullPath = os.path.join(workdir, 'OEBPS', 'Images')
|
||||||
if len(fullPath) > 240:
|
|
||||||
raise UserWarning("Path is too long.")
|
|
||||||
copytree(afile, fullPath)
|
copytree(afile, fullPath)
|
||||||
sanitizePermissions(fullPath)
|
sanitizePermissions(fullPath)
|
||||||
return workdir
|
return workdir
|
||||||
except OSError:
|
except:
|
||||||
rmtree(workdir, True)
|
rmtree(workdir, True)
|
||||||
raise
|
raise UserWarning("Failed to prepare a workspace.")
|
||||||
elif afile.lower().endswith('.pdf'):
|
elif afile.lower().endswith('.pdf'):
|
||||||
pdf = pdfjpgextract.PdfJpgExtract(afile)
|
pdf = pdfjpgextract.PdfJpgExtract(afile)
|
||||||
path, njpg = pdf.extract()
|
path, njpg = pdf.extract()
|
||||||
if njpg == 0:
|
if njpg == 0:
|
||||||
rmtree(path, True)
|
rmtree(path, True)
|
||||||
raise UserWarning("Failed to extract images.")
|
raise UserWarning("Failed to extract images from PDF file.")
|
||||||
else:
|
else:
|
||||||
workdir = mkdtemp('', 'KCC-TMP-')
|
workdir = mkdtemp('', 'KCC-')
|
||||||
cbx = cbxarchive.CBxArchive(afile)
|
cbx = cbxarchive.CBxArchive(afile)
|
||||||
if cbx.isCbxFile():
|
if cbx.isCbxFile():
|
||||||
try:
|
try:
|
||||||
path = cbx.extract(workdir)
|
path = cbx.extract(workdir)
|
||||||
except OSError:
|
except:
|
||||||
rmtree(workdir, True)
|
rmtree(workdir, True)
|
||||||
raise UserWarning("Failed to extract file.")
|
raise UserWarning("Failed to extract archive.")
|
||||||
else:
|
else:
|
||||||
rmtree(workdir, True)
|
rmtree(workdir, True)
|
||||||
raise TypeError
|
raise UserWarning("Failed to detect archive format.")
|
||||||
if len(os.path.join(path, 'OEBPS', 'Images')) > 240:
|
newpath = mkdtemp('', 'KCC-')
|
||||||
raise UserWarning("Path is too long.")
|
copytree(path, os.path.join(newpath, 'OEBPS', 'Images'))
|
||||||
move(path, path + "_temp")
|
rmtree(path, True)
|
||||||
move(path + "_temp", os.path.join(path, 'OEBPS', 'Images'))
|
return newpath
|
||||||
return path
|
|
||||||
|
|
||||||
|
|
||||||
def getOutputFilename(srcpath, wantedname, ext, tomeNumber):
|
def getOutputFilename(srcpath, wantedname, ext, tomeNumber):
|
||||||
if srcpath[-1] == os.path.sep:
|
if srcpath[-1] == os.path.sep:
|
||||||
srcpath = srcpath[:-1]
|
srcpath = srcpath[:-1]
|
||||||
if not ext.startswith('.'):
|
if 'Ko' in options.profile and options.format == 'EPUB':
|
||||||
ext = '.' + ext
|
ext = '.kepub.epub'
|
||||||
if wantedname is not None:
|
if wantedname is not None:
|
||||||
if wantedname.endswith(ext):
|
if wantedname.endswith(ext):
|
||||||
filename = os.path.abspath(wantedname)
|
filename = os.path.abspath(wantedname)
|
||||||
@@ -618,6 +701,13 @@ def getOutputFilename(srcpath, wantedname, ext, tomeNumber):
|
|||||||
os.path.basename(os.path.splitext(srcpath)[0]) + ext)
|
os.path.basename(os.path.splitext(srcpath)[0]) + ext)
|
||||||
elif os.path.isdir(srcpath):
|
elif os.path.isdir(srcpath):
|
||||||
filename = srcpath + tomeNumber + ext
|
filename = srcpath + tomeNumber + ext
|
||||||
|
else:
|
||||||
|
if 'Ko' in options.profile and options.format == 'EPUB':
|
||||||
|
path = srcpath.split(os.path.sep)
|
||||||
|
path[-1] = ''.join(e for e in path[-1].split('.')[0] if e.isalnum()) + tomeNumber + ext
|
||||||
|
if not path[-1].split('.')[0]:
|
||||||
|
path[-1] = 'KCCPlaceholder' + tomeNumber + ext
|
||||||
|
filename = os.path.sep.join(path)
|
||||||
else:
|
else:
|
||||||
filename = os.path.splitext(srcpath)[0] + tomeNumber + ext
|
filename = os.path.splitext(srcpath)[0] + tomeNumber + ext
|
||||||
if os.path.isfile(filename):
|
if os.path.isfile(filename):
|
||||||
@@ -633,6 +723,8 @@ def getComicInfo(path, originalPath):
|
|||||||
xmlPath = os.path.join(path, 'ComicInfo.xml')
|
xmlPath = os.path.join(path, 'ComicInfo.xml')
|
||||||
options.authors = ['KCC']
|
options.authors = ['KCC']
|
||||||
options.remoteCovers = {}
|
options.remoteCovers = {}
|
||||||
|
options.chapters = []
|
||||||
|
options.summary = ''
|
||||||
titleSuffix = ''
|
titleSuffix = ''
|
||||||
if options.title == 'defaulttitle':
|
if options.title == 'defaulttitle':
|
||||||
defaultTitle = True
|
defaultTitle = True
|
||||||
@@ -644,57 +736,45 @@ def getComicInfo(path, originalPath):
|
|||||||
defaultTitle = False
|
defaultTitle = False
|
||||||
if os.path.exists(xmlPath):
|
if os.path.exists(xmlPath):
|
||||||
try:
|
try:
|
||||||
xml = parse(xmlPath)
|
xml = metadata.MetadataParser(xmlPath)
|
||||||
except Exception:
|
except Exception:
|
||||||
os.remove(xmlPath)
|
os.remove(xmlPath)
|
||||||
return
|
return
|
||||||
options.authors = []
|
options.authors = []
|
||||||
if defaultTitle:
|
if defaultTitle:
|
||||||
if len(xml.getElementsByTagName('Series')) != 0:
|
if xml.data['Series']:
|
||||||
options.title = xml.getElementsByTagName('Series')[0].firstChild.nodeValue
|
options.title = escape(xml.data['Series'])
|
||||||
if len(xml.getElementsByTagName('Volume')) != 0:
|
if xml.data['Volume']:
|
||||||
titleSuffix += ' V' + xml.getElementsByTagName('Volume')[0].firstChild.nodeValue
|
titleSuffix += ' V' + xml.data['Volume']
|
||||||
if len(xml.getElementsByTagName('Number')) != 0:
|
if xml.data['Number']:
|
||||||
titleSuffix += ' #' + xml.getElementsByTagName('Number')[0].firstChild.nodeValue
|
titleSuffix += ' #' + xml.data['Number']
|
||||||
options.title += titleSuffix
|
options.title += titleSuffix
|
||||||
if len(xml.getElementsByTagName('Writer')) != 0:
|
for field in ['Writers', 'Pencillers', 'Inkers', 'Colorists']:
|
||||||
authorsTemp = str.split(xml.getElementsByTagName('Writer')[0].firstChild.nodeValue, ', ')
|
for person in xml.data[field]:
|
||||||
for author in authorsTemp:
|
options.authors.append(escape(person))
|
||||||
options.authors.append(author)
|
|
||||||
if len(xml.getElementsByTagName('Penciller')) != 0:
|
|
||||||
authorsTemp = str.split(xml.getElementsByTagName('Penciller')[0].firstChild.nodeValue, ', ')
|
|
||||||
for author in authorsTemp:
|
|
||||||
options.authors.append(author)
|
|
||||||
if len(xml.getElementsByTagName('Inker')) != 0:
|
|
||||||
authorsTemp = str.split(xml.getElementsByTagName('Inker')[0].firstChild.nodeValue, ', ')
|
|
||||||
for author in authorsTemp:
|
|
||||||
options.authors.append(author)
|
|
||||||
if len(xml.getElementsByTagName('Colorist')) != 0:
|
|
||||||
authorsTemp = str.split(xml.getElementsByTagName('Colorist')[0].firstChild.nodeValue, ', ')
|
|
||||||
for author in authorsTemp:
|
|
||||||
options.authors.append(author)
|
|
||||||
if len(options.authors) > 0:
|
if len(options.authors) > 0:
|
||||||
options.authors = list(set(options.authors))
|
options.authors = list(set(options.authors))
|
||||||
options.authors.sort()
|
options.authors.sort()
|
||||||
else:
|
else:
|
||||||
options.authors = ['KCC']
|
options.authors = ['KCC']
|
||||||
# Disabled due to closure of MCD
|
if xml.data['MUid']:
|
||||||
# if len(xml.getElementsByTagName('ScanInformation')) != 0:
|
options.remoteCovers = getCoversFromMCB(xml.data['MUid'])
|
||||||
# coverId = xml.getElementsByTagName('ScanInformation')[0].firstChild.nodeValue
|
if xml.data['Bookmarks']:
|
||||||
# coverId = compile('(MCD\\()(\\d+)(\\))').search(coverId)
|
options.chapters = xml.data['Bookmarks']
|
||||||
# if coverId:
|
if xml.data['Summary']:
|
||||||
# options.remoteCovers = getCoversFromMCB(coverId.group(2))
|
options.summary = escape(xml.data['Summary'])
|
||||||
os.remove(xmlPath)
|
os.remove(xmlPath)
|
||||||
|
|
||||||
|
|
||||||
def getCoversFromMCB(mangaID):
|
def getCoversFromMCB(mangaID):
|
||||||
covers = {}
|
covers = {}
|
||||||
try:
|
try:
|
||||||
jsonRaw = urlopen(Request('http://manga.joentjuh.nl/json/series/' + mangaID + '/',
|
jsonRaw = urlopen(Request('http://mcd.iosphe.re/api/v1/series/' + mangaID + '/',
|
||||||
headers={'User-Agent': 'KindleComicConverter/' + __version__}))
|
headers={'User-Agent': 'KindleComicConverter/' + __version__}))
|
||||||
jsonData = loads(jsonRaw.readall().decode('utf-8'))
|
jsonData = loads(jsonRaw.readall().decode('utf-8'))
|
||||||
for volume in jsonData['volumes']:
|
for volume in jsonData['Covers']['a']:
|
||||||
covers[int(volume['volume'])] = volume['releases'][0]['files']['front']['url']
|
if volume['Side'] == 'front':
|
||||||
|
covers[int(volume['Volume'])] = volume['Raw']
|
||||||
except Exception:
|
except Exception:
|
||||||
return {}
|
return {}
|
||||||
return covers
|
return covers
|
||||||
@@ -702,7 +782,7 @@ def getCoversFromMCB(mangaID):
|
|||||||
|
|
||||||
def getDirectorySize(start_path='.'):
|
def getDirectorySize(start_path='.'):
|
||||||
total_size = 0
|
total_size = 0
|
||||||
for dirpath, dirnames, filenames in os.walk(start_path):
|
for dirpath, dirnames, filenames in walk(start_path):
|
||||||
for f in filenames:
|
for f in filenames:
|
||||||
fp = os.path.join(dirpath, f)
|
fp = os.path.join(dirpath, f)
|
||||||
total_size += os.path.getsize(fp)
|
total_size += os.path.getsize(fp)
|
||||||
@@ -711,7 +791,7 @@ def getDirectorySize(start_path='.'):
|
|||||||
|
|
||||||
def sanitizeTree(filetree):
|
def sanitizeTree(filetree):
|
||||||
chapterNames = {}
|
chapterNames = {}
|
||||||
for root, dirs, files in os.walk(filetree, False):
|
for root, dirs, files in walk(filetree, False):
|
||||||
for name in files:
|
for name in files:
|
||||||
splitname = os.path.splitext(name)
|
splitname = os.path.splitext(name)
|
||||||
slugified = slugify(splitname[0])
|
slugified = slugify(splitname[0])
|
||||||
@@ -721,7 +801,7 @@ def sanitizeTree(filetree):
|
|||||||
newKey = os.path.join(root, slugified + splitname[1])
|
newKey = os.path.join(root, slugified + splitname[1])
|
||||||
key = os.path.join(root, name)
|
key = os.path.join(root, name)
|
||||||
if key != newKey:
|
if key != newKey:
|
||||||
os.replace(key, newKey)
|
saferReplace(key, newKey)
|
||||||
for name in dirs:
|
for name in dirs:
|
||||||
tmpName = name
|
tmpName = name
|
||||||
slugified = slugify(name)
|
slugified = slugify(name)
|
||||||
@@ -731,15 +811,14 @@ def sanitizeTree(filetree):
|
|||||||
newKey = os.path.join(root, slugified)
|
newKey = os.path.join(root, slugified)
|
||||||
key = os.path.join(root, name)
|
key = os.path.join(root, name)
|
||||||
if key != newKey:
|
if key != newKey:
|
||||||
os.replace(key, newKey)
|
saferReplace(key, newKey)
|
||||||
return chapterNames
|
return chapterNames
|
||||||
|
|
||||||
|
|
||||||
def sanitizeTreeKobo(filetree):
|
def sanitizeTreeKobo(filetree):
|
||||||
pageNumber = 0
|
pageNumber = 0
|
||||||
for root, dirs, files in os.walk(filetree):
|
for root, dirs, files in walk(filetree):
|
||||||
files.sort()
|
dirs, files = walkSort(dirs, files)
|
||||||
dirs.sort()
|
|
||||||
for name in files:
|
for name in files:
|
||||||
splitname = os.path.splitext(name)
|
splitname = os.path.splitext(name)
|
||||||
slugified = str(pageNumber).zfill(5)
|
slugified = str(pageNumber).zfill(5)
|
||||||
@@ -750,11 +829,11 @@ def sanitizeTreeKobo(filetree):
|
|||||||
newKey = os.path.join(root, slugified + splitname[1])
|
newKey = os.path.join(root, slugified + splitname[1])
|
||||||
key = os.path.join(root, name)
|
key = os.path.join(root, name)
|
||||||
if key != newKey:
|
if key != newKey:
|
||||||
os.replace(key, newKey)
|
saferReplace(key, newKey)
|
||||||
|
|
||||||
|
|
||||||
def sanitizePermissions(filetree):
|
def sanitizePermissions(filetree):
|
||||||
for root, dirs, files in os.walk(filetree, False):
|
for root, dirs, files in walk(filetree, False):
|
||||||
for name in files:
|
for name in files:
|
||||||
os.chmod(os.path.join(root, name), S_IWRITE | S_IREAD)
|
os.chmod(os.path.join(root, name), S_IWRITE | S_IREAD)
|
||||||
for name in dirs:
|
for name in dirs:
|
||||||
@@ -886,7 +965,9 @@ def splitProcess(path, mode):
|
|||||||
|
|
||||||
|
|
||||||
def detectCorruption(tmpPath, orgPath):
|
def detectCorruption(tmpPath, orgPath):
|
||||||
for root, dirs, files in os.walk(tmpPath, False):
|
imageNumber = 0
|
||||||
|
imageSmaller = 0
|
||||||
|
for root, dirs, files in walk(tmpPath, False):
|
||||||
for name in files:
|
for name in files:
|
||||||
if getImageFileName(name) is not None:
|
if getImageFileName(name) is not None:
|
||||||
path = os.path.join(root, name)
|
path = os.path.join(root, name)
|
||||||
@@ -899,11 +980,24 @@ def detectCorruption(tmpPath, orgPath):
|
|||||||
img.verify()
|
img.verify()
|
||||||
img = Image.open(path)
|
img = Image.open(path)
|
||||||
img.load()
|
img.load()
|
||||||
except Exception:
|
imageNumber += 1
|
||||||
|
if options.profileData[1][0] > img.size[0] and options.profileData[1][1] > img.size[1]:
|
||||||
|
imageSmaller += 1
|
||||||
|
except Exception as err:
|
||||||
rmtree(os.path.join(tmpPath, '..', '..'), True)
|
rmtree(os.path.join(tmpPath, '..', '..'), True)
|
||||||
|
if 'decoder' in str(err) and 'not available' in str(err):
|
||||||
|
raise RuntimeError('Pillow was compiled without JPG and/or PNG decoder.')
|
||||||
|
else:
|
||||||
raise RuntimeError('Image file %s is corrupted.' % pathOrg)
|
raise RuntimeError('Image file %s is corrupted.' % pathOrg)
|
||||||
else:
|
else:
|
||||||
os.remove(os.path.join(root, name))
|
os.remove(os.path.join(root, name))
|
||||||
|
if imageSmaller > imageNumber * 0.5 and not options.upscale and not options.stretch:
|
||||||
|
print("\nMore than half of images are smaller than target device resolution. "
|
||||||
|
"Consider enabling stretching or upscaling to improve readability.")
|
||||||
|
if GUI:
|
||||||
|
GUI.addMessage.emit('More than half of images are smaller than target device resolution.', 'warning', False)
|
||||||
|
GUI.addMessage.emit('Consider enabling stretching or upscaling to improve readability.', 'warning', False)
|
||||||
|
GUI.addMessage.emit('', '', False)
|
||||||
|
|
||||||
|
|
||||||
def detectMargins(path):
|
def detectMargins(path):
|
||||||
@@ -915,20 +1009,20 @@ def detectMargins(path):
|
|||||||
yu = flag[2]
|
yu = flag[2]
|
||||||
xr = flag[3]
|
xr = flag[3]
|
||||||
yd = flag[4]
|
yd = flag[4]
|
||||||
if xl != "0":
|
if xl != "0.0":
|
||||||
xl = "-" + str(float(xl)/100) + "%"
|
xl = "-" + xl + "%"
|
||||||
else:
|
else:
|
||||||
xl = "0%"
|
xl = "0%"
|
||||||
if xr != "0":
|
if xr != "0.0":
|
||||||
xr = "-" + str(float(xr)/100) + "%"
|
xr = "-" + xr + "%"
|
||||||
else:
|
else:
|
||||||
xr = "0%"
|
xr = "0%"
|
||||||
if yu != "0":
|
if yu != "0.0":
|
||||||
yu = "-" + str(float(yu)/100) + "%"
|
yu = "-" + yu + "%"
|
||||||
else:
|
else:
|
||||||
yu = "0%"
|
yu = "0%"
|
||||||
if yd != "0":
|
if yd != "0.0":
|
||||||
yd = "-" + str(float(yd)/100) + "%"
|
yd = "-" + yd + "%"
|
||||||
else:
|
else:
|
||||||
yd = "0%"
|
yd = "0%"
|
||||||
return xl, yu, xr, yd
|
return xl, yu, xr, yd
|
||||||
@@ -936,7 +1030,7 @@ def detectMargins(path):
|
|||||||
|
|
||||||
|
|
||||||
def createNewTome():
|
def createNewTome():
|
||||||
tomePathRoot = mkdtemp('', 'KCC-TMP-')
|
tomePathRoot = mkdtemp('', 'KCC-')
|
||||||
tomePath = os.path.join(tomePathRoot, 'OEBPS', 'Images')
|
tomePath = os.path.join(tomePathRoot, 'OEBPS', 'Images')
|
||||||
os.makedirs(tomePath)
|
os.makedirs(tomePath)
|
||||||
return tomePath, tomePathRoot
|
return tomePath, tomePathRoot
|
||||||
@@ -944,7 +1038,7 @@ def createNewTome():
|
|||||||
|
|
||||||
def slugify(value):
|
def slugify(value):
|
||||||
value = slugifyExt(value)
|
value = slugifyExt(value)
|
||||||
value = sub(r'0*([0-9]{4,})', r'\1', sub(r'([0-9]+)', r'0000\1', value))
|
value = sub(r'0*([0-9]{4,})', r'\1', sub(r'([0-9]+)', r'0000\1', value, count=2))
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
@@ -953,7 +1047,7 @@ def makeZIP(zipFilename, baseDir, isEPUB=False):
|
|||||||
zipOutput = ZipFile(zipFilename, 'w', ZIP_DEFLATED)
|
zipOutput = ZipFile(zipFilename, 'w', ZIP_DEFLATED)
|
||||||
if isEPUB:
|
if isEPUB:
|
||||||
zipOutput.writestr('mimetype', 'application/epub+zip', ZIP_STORED)
|
zipOutput.writestr('mimetype', 'application/epub+zip', ZIP_STORED)
|
||||||
for dirpath, dirnames, filenames in os.walk(baseDir):
|
for dirpath, dirnames, filenames in walk(baseDir):
|
||||||
for name in filenames:
|
for name in filenames:
|
||||||
path = os.path.normpath(os.path.join(dirpath, name))
|
path = os.path.normpath(os.path.join(dirpath, name))
|
||||||
aPath = os.path.normpath(os.path.join(dirpath.replace(baseDir, ''), name))
|
aPath = os.path.normpath(os.path.join(dirpath.replace(baseDir, ''), name))
|
||||||
@@ -974,8 +1068,8 @@ def makeParser():
|
|||||||
otherOptions = OptionGroup(psr, "OTHER")
|
otherOptions = OptionGroup(psr, "OTHER")
|
||||||
|
|
||||||
mainOptions.add_option("-p", "--profile", action="store", dest="profile", default="KV",
|
mainOptions.add_option("-p", "--profile", action="store", dest="profile", default="KV",
|
||||||
help="Device profile (Available options: K1, K2, K345, KDX, KPW, KV, KFHD, KFHDX, KFHDX8,"
|
help="Device profile (Available options: K1, K2, K345, KDX, KPW, KV, KoMT, KoG, KoGHD,"
|
||||||
" KFA, KoMT, KoG, KoA, KoAHD, KoAH2O) [Default=KV]")
|
" KoA, KoAHD, KoAH2O) [Default=KV]")
|
||||||
mainOptions.add_option("-q", "--quality", type="int", dest="quality", default="0",
|
mainOptions.add_option("-q", "--quality", type="int", dest="quality", default="0",
|
||||||
help="Quality of Panel View. 0 - Normal 1 - High 2 - Ultra [Default=0]")
|
help="Quality of Panel View. 0 - Normal 1 - High 2 - Ultra [Default=0]")
|
||||||
mainOptions.add_option("-m", "--manga-style", action="store_true", dest="righttoleft", default=False,
|
mainOptions.add_option("-m", "--manga-style", action="store_true", dest="righttoleft", default=False,
|
||||||
@@ -1034,14 +1128,18 @@ def makeParser():
|
|||||||
def checkOptions():
|
def checkOptions():
|
||||||
global options
|
global options
|
||||||
options.panelview = True
|
options.panelview = True
|
||||||
|
options.panelviewused = False
|
||||||
|
options.iskindle = False
|
||||||
options.bordersColor = None
|
options.bordersColor = None
|
||||||
if options.format == 'Auto':
|
if options.format == 'Auto':
|
||||||
if options.profile in ['K1', 'K2', 'K345', 'KPW', 'KV', 'KFHD', 'KFHDX', 'KFHDX8', 'KFA']:
|
if options.profile in ['K1', 'K2', 'K345', 'KPW', 'KV']:
|
||||||
options.format = 'MOBI'
|
options.format = 'MOBI'
|
||||||
elif options.profile in ['Other']:
|
elif options.profile in ['OTHER', 'KoMT', 'KoG', 'KoGHD', 'KoA', 'KoAHD', 'KoAH2O']:
|
||||||
options.format = 'EPUB'
|
options.format = 'EPUB'
|
||||||
elif options.profile in ['KDX', 'KoMT', 'KoG', 'KoA', 'KoAHD', 'KoAH2O']:
|
elif options.profile in ['KDX']:
|
||||||
options.format = 'CBZ'
|
options.format = 'CBZ'
|
||||||
|
if options.profile in ['K1', 'K2', 'K345', 'KPW', 'KV', 'OTHER']:
|
||||||
|
options.iskindle = True
|
||||||
if options.white_borders:
|
if options.white_borders:
|
||||||
options.bordersColor = 'white'
|
options.bordersColor = 'white'
|
||||||
if options.black_borders:
|
if options.black_borders:
|
||||||
@@ -1049,11 +1147,6 @@ def checkOptions():
|
|||||||
# Splitting MOBI is not optional
|
# Splitting MOBI is not optional
|
||||||
if options.format == 'MOBI':
|
if options.format == 'MOBI':
|
||||||
options.batchsplit = True
|
options.batchsplit = True
|
||||||
# Disabling grayscale conversion for Kindle Fire family.
|
|
||||||
if 'KFH' in options.profile or options.forcecolor:
|
|
||||||
options.forcecolor = True
|
|
||||||
else:
|
|
||||||
options.forcecolor = False
|
|
||||||
# Older Kindle don't need higher resolution files due lack of Panel View.
|
# Older Kindle don't need higher resolution files due lack of Panel View.
|
||||||
if options.profile == 'K1' or options.profile == 'K2' or options.profile == 'KDX':
|
if options.profile == 'K1' or options.profile == 'K2' or options.profile == 'KDX':
|
||||||
options.quality = 0
|
options.quality = 0
|
||||||
@@ -1072,10 +1165,6 @@ def checkOptions():
|
|||||||
# Kobo models can't use ultra quality mode
|
# Kobo models can't use ultra quality mode
|
||||||
if options.quality == 2:
|
if options.quality == 2:
|
||||||
options.quality = 1
|
options.quality = 1
|
||||||
# Kindle for Android profile require target resolution.
|
|
||||||
if options.profile == 'KFA' and (options.customwidth == 0 or options.customheight == 0):
|
|
||||||
print("ERROR: Kindle for Android profile require --customwidth and --customheight options!")
|
|
||||||
sys.exit(1)
|
|
||||||
# CBZ files on Kindle DX/DXG support higher resolution
|
# CBZ files on Kindle DX/DXG support higher resolution
|
||||||
if options.profile == 'KDX' and options.format == 'CBZ':
|
if options.profile == 'KDX' and options.format == 'CBZ':
|
||||||
options.customheight = 1200
|
options.customheight = 1200
|
||||||
@@ -1091,7 +1180,7 @@ def checkOptions():
|
|||||||
if options.customheight != 0:
|
if options.customheight != 0:
|
||||||
Y = options.customheight
|
Y = options.customheight
|
||||||
newProfile = ("Custom", (int(X), int(Y)), image.ProfileData.Palette16,
|
newProfile = ("Custom", (int(X), int(Y)), image.ProfileData.Palette16,
|
||||||
image.ProfileData.Profiles[options.profile][3], (int(int(X)*1.5), int(int(Y)*1.5)))
|
image.ProfileData.Profiles[options.profile][3], (int(int(X) * 1.5), int(int(Y) * 1.5)))
|
||||||
image.ProfileData.Profiles["Custom"] = newProfile
|
image.ProfileData.Profiles["Custom"] = newProfile
|
||||||
options.profile = "Custom"
|
options.profile = "Custom"
|
||||||
options.profileData = image.ProfileData.Profiles[options.profile]
|
options.profileData = image.ProfileData.Profiles[options.profile]
|
||||||
@@ -1118,6 +1207,24 @@ def checkTools(source):
|
|||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
def checkPre(source):
|
||||||
|
# Make sure that all temporary files are gone
|
||||||
|
for root, dirs, _ in walkLevel(gettempdir(), 0):
|
||||||
|
for tempdir in dirs:
|
||||||
|
if tempdir.startswith('KCC-'):
|
||||||
|
rmtree(os.path.join(root, tempdir), True)
|
||||||
|
# Make sure that target directory is writable
|
||||||
|
if os.path.isdir(source):
|
||||||
|
src = os.path.abspath(os.path.join(source, '..'))
|
||||||
|
else:
|
||||||
|
src = os.path.dirname(source)
|
||||||
|
try:
|
||||||
|
with TemporaryFile(prefix='KCC-', dir=src):
|
||||||
|
pass
|
||||||
|
except:
|
||||||
|
raise UserWarning("Target directory is not writable.")
|
||||||
|
|
||||||
|
|
||||||
def makeBook(source, qtGUI=None):
|
def makeBook(source, qtGUI=None):
|
||||||
"""Generates MOBI/EPUB/CBZ comic ebook from a bunch of images."""
|
"""Generates MOBI/EPUB/CBZ comic ebook from a bunch of images."""
|
||||||
global GUI
|
global GUI
|
||||||
@@ -1126,14 +1233,12 @@ def makeBook(source, qtGUI=None):
|
|||||||
GUI.progressBarTick.emit('1')
|
GUI.progressBarTick.emit('1')
|
||||||
else:
|
else:
|
||||||
checkTools(source)
|
checkTools(source)
|
||||||
|
checkPre(source)
|
||||||
path = getWorkFolder(source)
|
path = getWorkFolder(source)
|
||||||
print("\nChecking images...")
|
print("\nChecking images...")
|
||||||
getComicInfo(os.path.join(path, "OEBPS", "Images"), source)
|
getComicInfo(os.path.join(path, "OEBPS", "Images"), source)
|
||||||
detectCorruption(os.path.join(path, "OEBPS", "Images"), source)
|
detectCorruption(os.path.join(path, "OEBPS", "Images"), source)
|
||||||
if options.webtoon:
|
if options.webtoon:
|
||||||
if options.customheight > 0:
|
|
||||||
comic2panel.main(['-y ' + str(options.customheight), '-i', '-m', path], qtGUI)
|
|
||||||
else:
|
|
||||||
comic2panel.main(['-y ' + str(image.ProfileData.Profiles[options.profile][1][1]), '-i', '-m', path], qtGUI)
|
comic2panel.main(['-y ' + str(image.ProfileData.Profiles[options.profile][1][1]), '-i', '-m', path], qtGUI)
|
||||||
if options.imgproc:
|
if options.imgproc:
|
||||||
print("\nProcessing images...")
|
print("\nProcessing images...")
|
||||||
@@ -1211,7 +1316,7 @@ def makeMOBIFix(item):
|
|||||||
mobiPath = item.replace('.epub', '.mobi')
|
mobiPath = item.replace('.epub', '.mobi')
|
||||||
move(mobiPath, mobiPath + '_toclean')
|
move(mobiPath, mobiPath + '_toclean')
|
||||||
try:
|
try:
|
||||||
dualmetafix.DualMobiMetaFix(mobiPath + '_toclean', mobiPath, bytes(str(uuid4()), 'UTF-8'))
|
dualmetafix.DualMobiMetaFix(mobiPath + '_toclean', mobiPath, bytes(options.uuid, 'UTF-8'))
|
||||||
return [True]
|
return [True]
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
return [False, format(err)]
|
return [False, format(err)]
|
||||||
@@ -1261,7 +1366,7 @@ def makeMOBI(work, qtGUI=None):
|
|||||||
global GUI, makeMOBIWorkerPool, makeMOBIWorkerOutput
|
global GUI, makeMOBIWorkerPool, makeMOBIWorkerOutput
|
||||||
GUI = qtGUI
|
GUI = qtGUI
|
||||||
makeMOBIWorkerOutput = []
|
makeMOBIWorkerOutput = []
|
||||||
availableMemory = virtual_memory().total/1000000000
|
availableMemory = virtual_memory().total / 1000000000
|
||||||
if availableMemory <= 2:
|
if availableMemory <= 2:
|
||||||
threadNumber = 1
|
threadNumber = 1
|
||||||
elif 2 < availableMemory <= 4:
|
elif 2 < availableMemory <= 4:
|
||||||
|
|||||||
@@ -18,22 +18,21 @@
|
|||||||
# PERFORMANCE OF THIS SOFTWARE.
|
# PERFORMANCE OF THIS SOFTWARE.
|
||||||
#
|
#
|
||||||
|
|
||||||
__version__ = '4.4'
|
|
||||||
__license__ = 'ISC'
|
|
||||||
__copyright__ = '2012-2015, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>'
|
|
||||||
__docformat__ = 'restructuredtext en'
|
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
from shutil import rmtree, copytree, move
|
from shutil import rmtree, copytree, move
|
||||||
from optparse import OptionParser, OptionGroup
|
from optparse import OptionParser, OptionGroup
|
||||||
from multiprocessing import Pool
|
from multiprocessing import Pool
|
||||||
from PIL import Image, ImageStat, ImageOps
|
from PIL import Image, ImageStat, ImageOps
|
||||||
from .shared import getImageFileName, walkLevel
|
from .shared import getImageFileName, walkLevel, walkSort
|
||||||
try:
|
try:
|
||||||
from PyQt5 import QtCore
|
from PyQt5 import QtCore
|
||||||
except ImportError:
|
except ImportError:
|
||||||
QtCore = None
|
QtCore = None
|
||||||
|
try:
|
||||||
|
from scandir import walk
|
||||||
|
except ImportError:
|
||||||
|
walk = os.walk
|
||||||
|
|
||||||
|
|
||||||
def mergeDirectoryTick(output):
|
def mergeDirectoryTick(output):
|
||||||
@@ -89,19 +88,19 @@ def sanitizePanelSize(panel, opt):
|
|||||||
newPanels = []
|
newPanels = []
|
||||||
if panel[2] > 6 * opt.height:
|
if panel[2] > 6 * opt.height:
|
||||||
diff = int(panel[2] / 8)
|
diff = int(panel[2] / 8)
|
||||||
newPanels.append([panel[0], panel[1] - diff*7, diff])
|
newPanels.append([panel[0], panel[1] - diff * 7, diff])
|
||||||
newPanels.append([panel[1] - diff*7, panel[1] - diff*6, diff])
|
newPanels.append([panel[1] - diff * 7, panel[1] - diff * 6, diff])
|
||||||
newPanels.append([panel[1] - diff*6, panel[1] - diff*5, diff])
|
newPanels.append([panel[1] - diff * 6, panel[1] - diff * 5, diff])
|
||||||
newPanels.append([panel[1] - diff*5, panel[1] - diff*4, diff])
|
newPanels.append([panel[1] - diff * 5, panel[1] - diff * 4, diff])
|
||||||
newPanels.append([panel[1] - diff*4, panel[1] - diff*3, diff])
|
newPanels.append([panel[1] - diff * 4, panel[1] - diff * 3, diff])
|
||||||
newPanels.append([panel[1] - diff*3, panel[1] - diff*2, diff])
|
newPanels.append([panel[1] - diff * 3, panel[1] - diff * 2, diff])
|
||||||
newPanels.append([panel[1] - diff*2, panel[1] - diff, diff])
|
newPanels.append([panel[1] - diff * 2, panel[1] - diff, diff])
|
||||||
newPanels.append([panel[1] - diff, panel[1], diff])
|
newPanels.append([panel[1] - diff, panel[1], diff])
|
||||||
elif panel[2] > 3 * opt.height:
|
elif panel[2] > 3 * opt.height:
|
||||||
diff = int(panel[2] / 4)
|
diff = int(panel[2] / 4)
|
||||||
newPanels.append([panel[0], panel[1] - diff*3, diff])
|
newPanels.append([panel[0], panel[1] - diff * 3, diff])
|
||||||
newPanels.append([panel[1] - diff*3, panel[1] - diff*2, diff])
|
newPanels.append([panel[1] - diff * 3, panel[1] - diff * 2, diff])
|
||||||
newPanels.append([panel[1] - diff*2, panel[1] - diff, diff])
|
newPanels.append([panel[1] - diff * 2, panel[1] - diff, diff])
|
||||||
newPanels.append([panel[1] - diff, panel[1], diff])
|
newPanels.append([panel[1] - diff, panel[1], diff])
|
||||||
elif panel[2] > 1.5 * opt.height:
|
elif panel[2] > 1.5 * opt.height:
|
||||||
newPanels.append([panel[0], panel[1] - int(panel[2] / 2), int(panel[2] / 2)])
|
newPanels.append([panel[0], panel[1] - int(panel[2] / 2), int(panel[2] / 2)])
|
||||||
@@ -251,7 +250,8 @@ def main(argv=None, qtGUI=None):
|
|||||||
mergeWorkerOutput = []
|
mergeWorkerOutput = []
|
||||||
mergeWorkerPool = Pool()
|
mergeWorkerPool = Pool()
|
||||||
mergeWork.append([options.targetDir])
|
mergeWork.append([options.targetDir])
|
||||||
for root, dirs, files in os.walk(options.targetDir, False):
|
for root, dirs, files in walk(options.targetDir, False):
|
||||||
|
dirs, files = walkSort(dirs, files)
|
||||||
for directory in dirs:
|
for directory in dirs:
|
||||||
directoryNumer += 1
|
directoryNumer += 1
|
||||||
mergeWork.append([os.path.join(root, directory)])
|
mergeWork.append([os.path.join(root, directory)])
|
||||||
@@ -269,7 +269,7 @@ def main(argv=None, qtGUI=None):
|
|||||||
rmtree(options.targetDir, True)
|
rmtree(options.targetDir, True)
|
||||||
raise RuntimeError("One of workers crashed. Cause: " + mergeWorkerOutput[0])
|
raise RuntimeError("One of workers crashed. Cause: " + mergeWorkerOutput[0])
|
||||||
print("\nSplitting images...")
|
print("\nSplitting images...")
|
||||||
for root, dirs, files in os.walk(options.targetDir, False):
|
for root, dirs, files in walk(options.targetDir, False):
|
||||||
for name in files:
|
for name in files:
|
||||||
if getImageFileName(name) is not None:
|
if getImageFileName(name) is not None:
|
||||||
pagenumber += 1
|
pagenumber += 1
|
||||||
|
|||||||
@@ -36,15 +36,15 @@ title_offset = 84
|
|||||||
|
|
||||||
|
|
||||||
def getint(data, ofs, sz='L'):
|
def getint(data, ofs, sz='L'):
|
||||||
i, = struct.unpack_from('>'+sz, data, ofs)
|
i, = struct.unpack_from('>' + sz, data, ofs)
|
||||||
return i
|
return i
|
||||||
|
|
||||||
|
|
||||||
def writeint(data, ofs, n, slen='L'):
|
def writeint(data, ofs, n, slen='L'):
|
||||||
if slen == 'L':
|
if slen == 'L':
|
||||||
return data[:ofs]+struct.pack('>L', n)+data[ofs+4:]
|
return data[:ofs] + struct.pack('>L', n) + data[ofs + 4:]
|
||||||
else:
|
else:
|
||||||
return data[:ofs]+struct.pack('>H', n)+data[ofs+2:]
|
return data[:ofs] + struct.pack('>H', n) + data[ofs + 2:]
|
||||||
|
|
||||||
|
|
||||||
def getsecaddr(datain, secno):
|
def getsecaddr(datain, secno):
|
||||||
@@ -52,11 +52,11 @@ def getsecaddr(datain, secno):
|
|||||||
if (secno < 0) | (secno >= nsec):
|
if (secno < 0) | (secno >= nsec):
|
||||||
emsg = 'requested section number %d out of range (nsec=%d)' % (secno, nsec)
|
emsg = 'requested section number %d out of range (nsec=%d)' % (secno, nsec)
|
||||||
raise DualMetaFixException(emsg)
|
raise DualMetaFixException(emsg)
|
||||||
secstart = getint(datain, first_pdb_record+secno*8)
|
secstart = getint(datain, first_pdb_record + secno * 8)
|
||||||
if secno == nsec-1:
|
if secno == nsec - 1:
|
||||||
secend = len(datain)
|
secend = len(datain)
|
||||||
else:
|
else:
|
||||||
secend = getint(datain, first_pdb_record+(secno+1)*8)
|
secend = getint(datain, first_pdb_record + (secno + 1) * 8)
|
||||||
return secstart, secend
|
return secstart, secend
|
||||||
|
|
||||||
|
|
||||||
@@ -71,28 +71,28 @@ def replacesection(datain, secno, secdata):
|
|||||||
seclen = secend - secstart
|
seclen = secend - secstart
|
||||||
if len(secdata) != seclen:
|
if len(secdata) != seclen:
|
||||||
raise DualMetaFixException('section length change in replacesection')
|
raise DualMetaFixException('section length change in replacesection')
|
||||||
datain[secstart:secstart+seclen] = secdata
|
datain[secstart:secstart + seclen] = secdata
|
||||||
|
|
||||||
|
|
||||||
def get_exth_params(rec0):
|
def get_exth_params(rec0):
|
||||||
ebase = mobi_header_base + getint(rec0, mobi_header_length)
|
ebase = mobi_header_base + getint(rec0, mobi_header_length)
|
||||||
if rec0[ebase:ebase+4] != b'EXTH':
|
if rec0[ebase:ebase + 4] != b'EXTH':
|
||||||
raise DualMetaFixException('EXTH tag not found where expected')
|
raise DualMetaFixException('EXTH tag not found where expected')
|
||||||
elen = getint(rec0, ebase+4)
|
elen = getint(rec0, ebase + 4)
|
||||||
enum = getint(rec0, ebase+8)
|
enum = getint(rec0, ebase + 8)
|
||||||
rlen = len(rec0)
|
rlen = len(rec0)
|
||||||
return ebase, elen, enum, rlen
|
return ebase, elen, enum, rlen
|
||||||
|
|
||||||
|
|
||||||
def add_exth(rec0, exth_num, exth_bytes):
|
def add_exth(rec0, exth_num, exth_bytes):
|
||||||
ebase, elen, enum, rlen = get_exth_params(rec0)
|
ebase, elen, enum, rlen = get_exth_params(rec0)
|
||||||
newrecsize = 8+len(exth_bytes)
|
newrecsize = 8 + len(exth_bytes)
|
||||||
newrec0 = rec0[0:ebase+4]+struct.pack('>L', elen+newrecsize)+struct.pack('>L', enum+1)+struct.pack('>L', exth_num)\
|
newrec0 = rec0[0:ebase + 4] + struct.pack('>L', elen + newrecsize) + struct.pack('>L', enum + 1) + \
|
||||||
+ struct.pack('>L', newrecsize)+exth_bytes+rec0[ebase+12:]
|
struct.pack('>L', exth_num) + struct.pack('>L', newrecsize) + exth_bytes + rec0[ebase + 12:]
|
||||||
newrec0 = writeint(newrec0, title_offset, getint(newrec0, title_offset)+newrecsize)
|
newrec0 = writeint(newrec0, title_offset, getint(newrec0, title_offset) + newrecsize)
|
||||||
# keep constant record length by removing newrecsize null bytes from end
|
# keep constant record length by removing newrecsize null bytes from end
|
||||||
sectail = newrec0[-newrecsize:]
|
sectail = newrec0[-newrecsize:]
|
||||||
if sectail != b'\0'*newrecsize:
|
if sectail != b'\0' * newrecsize:
|
||||||
raise DualMetaFixException('add_exth: trimmed non-null bytes at end of section')
|
raise DualMetaFixException('add_exth: trimmed non-null bytes at end of section')
|
||||||
newrec0 = newrec0[0:rlen]
|
newrec0 = newrec0[0:rlen]
|
||||||
return newrec0
|
return newrec0
|
||||||
@@ -106,30 +106,31 @@ def read_exth(rec0, exth_num):
|
|||||||
exth_id = getint(rec0, ebase)
|
exth_id = getint(rec0, ebase)
|
||||||
if exth_id == exth_num:
|
if exth_id == exth_num:
|
||||||
# We might have multiple exths, so build a list.
|
# We might have multiple exths, so build a list.
|
||||||
exth_values.append(rec0[ebase+8:ebase+getint(rec0, ebase+4)])
|
exth_values.append(rec0[ebase + 8:ebase + getint(rec0, ebase + 4)])
|
||||||
enum -= 1
|
enum -= 1
|
||||||
ebase = ebase+getint(rec0, ebase+4)
|
ebase = ebase + getint(rec0, ebase + 4)
|
||||||
return exth_values
|
return exth_values
|
||||||
|
|
||||||
|
|
||||||
def del_exth(rec0, exth_num):
|
def del_exth(rec0, exth_num):
|
||||||
ebase, elen, enum, rlen = get_exth_params(rec0)
|
ebase, elen, enum, rlen = get_exth_params(rec0)
|
||||||
ebase_idx = ebase+12
|
ebase_idx = ebase + 12
|
||||||
enum_idx = 0
|
enum_idx = 0
|
||||||
while enum_idx < enum:
|
while enum_idx < enum:
|
||||||
exth_id = getint(rec0, ebase_idx)
|
exth_id = getint(rec0, ebase_idx)
|
||||||
exth_size = getint(rec0, ebase_idx+4)
|
exth_size = getint(rec0, ebase_idx + 4)
|
||||||
if exth_id == exth_num:
|
if exth_id == exth_num:
|
||||||
newrec0 = rec0
|
newrec0 = rec0
|
||||||
newrec0 = writeint(newrec0, title_offset, getint(newrec0, title_offset)-exth_size)
|
newrec0 = writeint(newrec0, title_offset, getint(newrec0, title_offset) - exth_size)
|
||||||
newrec0 = newrec0[:ebase_idx]+newrec0[ebase_idx+exth_size:]
|
newrec0 = newrec0[:ebase_idx] + newrec0[ebase_idx + exth_size:]
|
||||||
newrec0 = newrec0[0:ebase+4]+struct.pack('>L', elen-exth_size)+struct.pack('>L', enum-1)+newrec0[ebase+12:]
|
newrec0 = newrec0[0:ebase + 4] + struct.pack('>L', elen - exth_size) + \
|
||||||
newrec0 += b'\0'*exth_size
|
struct.pack('>L', enum - 1) + newrec0[ebase + 12:]
|
||||||
|
newrec0 += b'\0' * exth_size
|
||||||
if rlen != len(newrec0):
|
if rlen != len(newrec0):
|
||||||
raise DualMetaFixException('del_exth: incorrect section size change')
|
raise DualMetaFixException('del_exth: incorrect section size change')
|
||||||
return newrec0
|
return newrec0
|
||||||
enum_idx += 1
|
enum_idx += 1
|
||||||
ebase_idx = ebase_idx+exth_size
|
ebase_idx = ebase_idx + exth_size
|
||||||
return rec0
|
return rec0
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
184
kcc/image.py
184
kcc/image.py
@@ -16,11 +16,6 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
__version__ = '4.4'
|
|
||||||
__license__ = 'ISC'
|
|
||||||
__copyright__ = '2012-2015, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>'
|
|
||||||
__docformat__ = 'restructuredtext en'
|
|
||||||
|
|
||||||
import os
|
import os
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from urllib.request import Request, urlopen
|
from urllib.request import Request, urlopen
|
||||||
@@ -28,6 +23,7 @@ from urllib.parse import quote
|
|||||||
from functools import reduce
|
from functools import reduce
|
||||||
from PIL import Image, ImageOps, ImageStat, ImageChops
|
from PIL import Image, ImageOps, ImageStat, ImageChops
|
||||||
from .shared import md5Checksum
|
from .shared import md5Checksum
|
||||||
|
from . import __version__
|
||||||
|
|
||||||
|
|
||||||
class ProfileData:
|
class ProfileData:
|
||||||
@@ -86,14 +82,11 @@ class ProfileData:
|
|||||||
'K2': ("Kindle 2", (600, 670), Palette15, 1.8, (900, 1005)),
|
'K2': ("Kindle 2", (600, 670), Palette15, 1.8, (900, 1005)),
|
||||||
'K345': ("Kindle", (600, 800), Palette16, 1.8, (900, 1200)),
|
'K345': ("Kindle", (600, 800), Palette16, 1.8, (900, 1200)),
|
||||||
'KDX': ("Kindle DX/DXG", (824, 1000), Palette16, 1.8, (1236, 1500)),
|
'KDX': ("Kindle DX/DXG", (824, 1000), Palette16, 1.8, (1236, 1500)),
|
||||||
'KPW': ("Kindle Paperwhite", (758, 1024), Palette16, 1.8, (1137, 1536)),
|
'KPW': ("Kindle Paperwhite 1/2", (758, 1024), Palette16, 1.8, (1137, 1536)),
|
||||||
'KV': ("Kindle Voyage", (1072, 1448), Palette16, 1.8, (1608, 2172)),
|
'KV': ("Kindle Paperwhite 3/Voyage", (1072, 1448), Palette16, 1.8, (1608, 2172)),
|
||||||
'KFHD': ("K. Fire HD", (800, 1280), PalleteNull, 1.0, (1200, 1920)),
|
|
||||||
'KFHDX': ("K. Fire HDX", (1200, 1920), PalleteNull, 1.0, (1800, 2880)),
|
|
||||||
'KFHDX8': ("K. Fire HDX 8.9", (1600, 2560), PalleteNull, 1.0, (2400, 3840)),
|
|
||||||
'KFA': ("Kindle for Android", (0, 0), PalleteNull, 1.0, (0, 0)),
|
|
||||||
'KoMT': ("Kobo Mini/Touch", (600, 800), Palette16, 1.8, (900, 1200)),
|
'KoMT': ("Kobo Mini/Touch", (600, 800), Palette16, 1.8, (900, 1200)),
|
||||||
'KoG': ("Kobo Glow", (768, 1024), Palette16, 1.8, (1152, 1536)),
|
'KoG': ("Kobo Glo", (768, 1024), Palette16, 1.8, (1152, 1536)),
|
||||||
|
'KoGHD': ("Kobo Glo HD", (1072, 1448), Palette16, 1.8, (1608, 2172)),
|
||||||
'KoA': ("Kobo Aura", (758, 1024), Palette16, 1.8, (1137, 1536)),
|
'KoA': ("Kobo Aura", (758, 1024), Palette16, 1.8, (1137, 1536)),
|
||||||
'KoAHD': ("Kobo Aura HD", (1080, 1440), Palette16, 1.8, (1620, 2160)),
|
'KoAHD': ("Kobo Aura HD", (1080, 1440), Palette16, 1.8, (1620, 2160)),
|
||||||
'KoAH2O': ("Kobo Aura H2O", (1080, 1430), Palette16, 1.8, (1620, 2145)),
|
'KoAH2O': ("Kobo Aura H2O", (1080, 1430), Palette16, 1.8, (1620, 2145)),
|
||||||
@@ -102,7 +95,7 @@ class ProfileData:
|
|||||||
|
|
||||||
|
|
||||||
class ComicPage:
|
class ComicPage:
|
||||||
def __init__(self, source, options, fill=None):
|
def __init__(self, source, options, original=None):
|
||||||
try:
|
try:
|
||||||
self.profile_label, self.size, self.palette, self.gamma, self.panelviewsize = options.profileData
|
self.profile_label, self.size, self.palette, self.gamma, self.panelviewsize = options.profileData
|
||||||
except KeyError:
|
except KeyError:
|
||||||
@@ -111,18 +104,29 @@ class ComicPage:
|
|||||||
self.filename = os.path.basename(self.origFileName)
|
self.filename = os.path.basename(self.origFileName)
|
||||||
self.image = Image.open(source)
|
self.image = Image.open(source)
|
||||||
self.image = self.image.convert('RGB')
|
self.image = self.image.convert('RGB')
|
||||||
|
self.opt = options
|
||||||
|
if original:
|
||||||
|
self.second = True
|
||||||
|
self.rotated = original.rotated
|
||||||
|
self.border = original.border
|
||||||
|
self.noHPV = original.noHPV
|
||||||
|
self.noVPV = original.noVPV
|
||||||
|
self.noPV = original.noPV
|
||||||
|
self.noHQ = original.noHQ
|
||||||
|
self.fill = original.fill
|
||||||
|
self.color = original.color
|
||||||
|
if self.rotated:
|
||||||
|
self.image = self.image.rotate(90, Image.BICUBIC, True)
|
||||||
|
self.opt.quality = 0
|
||||||
|
else:
|
||||||
|
self.second = False
|
||||||
self.rotated = None
|
self.rotated = None
|
||||||
self.border = None
|
self.border = None
|
||||||
self.noHPV = None
|
self.noHPV = None
|
||||||
self.noVPV = None
|
self.noVPV = None
|
||||||
self.noPV = None
|
self.noPV = None
|
||||||
self.purge = False
|
|
||||||
self.hq = False
|
|
||||||
self.opt = options
|
|
||||||
if fill:
|
|
||||||
self.fill = fill
|
|
||||||
else:
|
|
||||||
self.fill = None
|
self.fill = None
|
||||||
|
self.noHQ = False
|
||||||
if options.webtoon:
|
if options.webtoon:
|
||||||
self.color = True
|
self.color = True
|
||||||
else:
|
else:
|
||||||
@@ -130,16 +134,12 @@ class ComicPage:
|
|||||||
|
|
||||||
def saveToDir(self, targetdir):
|
def saveToDir(self, targetdir):
|
||||||
try:
|
try:
|
||||||
if not self.purge:
|
|
||||||
flags = []
|
flags = []
|
||||||
filename = os.path.join(targetdir, os.path.splitext(self.filename)[0]) + '-KCC'
|
filename = os.path.join(targetdir, os.path.splitext(self.filename)[0]) + '-KCC'
|
||||||
if not self.opt.forcecolor and not self.opt.forcepng:
|
if not self.opt.forcecolor and not self.opt.forcepng:
|
||||||
self.image = self.image.convert('L')
|
self.image = self.image.convert('L')
|
||||||
if self.rotated:
|
if self.rotated:
|
||||||
flags.append('Rotated')
|
flags.append('Rotated')
|
||||||
if self.hq:
|
|
||||||
flags.append('HighQuality')
|
|
||||||
filename += '-HQ'
|
|
||||||
if self.noPV:
|
if self.noPV:
|
||||||
flags.append('NoPanelView')
|
flags.append('NoPanelView')
|
||||||
else:
|
else:
|
||||||
@@ -148,8 +148,12 @@ class ComicPage:
|
|||||||
if self.noVPV:
|
if self.noVPV:
|
||||||
flags.append('NoVerticalPanelView')
|
flags.append('NoVerticalPanelView')
|
||||||
if self.border:
|
if self.border:
|
||||||
flags.append('Margins-' + str(self.border[0]) + '-' + str(self.border[1]) + '-'
|
flags.append('Margins-' + str(self.border[0]) + '-' + str(self.border[1]) + '-' +
|
||||||
+ str(self.border[2]) + '-' + str(self.border[3]))
|
str(self.border[2]) + '-' + str(self.border[3]))
|
||||||
|
if self.fill != 'white':
|
||||||
|
flags.append('BlackFill')
|
||||||
|
if self.opt.quality == 2:
|
||||||
|
filename += '-HQ'
|
||||||
if self.opt.forcepng:
|
if self.opt.forcepng:
|
||||||
filename += '.png'
|
filename += '.png'
|
||||||
self.image.save(filename, 'PNG', optimize=1)
|
self.image.save(filename, 'PNG', optimize=1)
|
||||||
@@ -157,12 +161,10 @@ class ComicPage:
|
|||||||
filename += '.jpg'
|
filename += '.jpg'
|
||||||
self.image.save(filename, 'JPEG', optimize=1, quality=80)
|
self.image.save(filename, 'JPEG', optimize=1, quality=80)
|
||||||
return [md5Checksum(filename), flags]
|
return [md5Checksum(filename), flags]
|
||||||
else:
|
|
||||||
return None
|
|
||||||
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 autocontrastImage(self):
|
||||||
gamma = self.opt.gamma
|
gamma = self.opt.gamma
|
||||||
if gamma < 0.1:
|
if gamma < 0.1:
|
||||||
gamma = self.gamma
|
gamma = self.gamma
|
||||||
@@ -184,80 +186,65 @@ class ComicPage:
|
|||||||
# Quantize is deprecated but new function call it internally anyway...
|
# Quantize is deprecated but new function call it internally anyway...
|
||||||
self.image = self.image.quantize(palette=palImg)
|
self.image = self.image.quantize(palette=palImg)
|
||||||
|
|
||||||
def calculateBorderPercent(self, x, img, isWidth):
|
def calculateBorder(self):
|
||||||
if isWidth:
|
if self.noPV:
|
||||||
return int(round(float(x)/float(img.image.size[0]), 4) * 10000 * 1.5)
|
self.border = [0.0, 0.0, 0.0, 0.0]
|
||||||
else:
|
|
||||||
return int(round(float(x)/float(img.image.size[1]), 4) * 10000 * 1.5)
|
|
||||||
|
|
||||||
def calculateBorder(self, sourceImage, isHQ=False):
|
|
||||||
if (isHQ and sourceImage.purge) or self.noPV:
|
|
||||||
self.border = [0, 0, 0, 0]
|
|
||||||
self.noPV = True
|
|
||||||
return
|
return
|
||||||
if self.fill == 'white':
|
if self.fill == 'white':
|
||||||
# Only already saved files can have P mode. So we can break color quantization.
|
border = ImageChops.invert(self.image).getbbox()
|
||||||
if sourceImage.image.mode == 'P':
|
|
||||||
sourceImage.image = sourceImage.image.convert('RGB')
|
|
||||||
border = ImageChops.invert(sourceImage.image).getbbox()
|
|
||||||
else:
|
else:
|
||||||
border = sourceImage.image.getbbox()
|
border = self.image.getbbox()
|
||||||
if border is not None:
|
if self.opt.quality == 2:
|
||||||
if isHQ:
|
|
||||||
multiplier = 1.0
|
multiplier = 1.0
|
||||||
else:
|
else:
|
||||||
multiplier = 1.5
|
multiplier = 1.5
|
||||||
self.border = [self.calculateBorderPercent(border[0], sourceImage, True),
|
if border is not None:
|
||||||
self.calculateBorderPercent(border[1], sourceImage, False),
|
self.border = [round(float(border[0]) / float(self.image.size[0]) * 150, 3),
|
||||||
self.calculateBorderPercent((sourceImage.image.size[0] - border[2]), sourceImage, True),
|
round(float(border[1]) / float(self.image.size[1]) * 150, 3),
|
||||||
self.calculateBorderPercent((sourceImage.image.size[1] - border[3]), sourceImage, False)]
|
round(float(self.image.size[0] - border[2]) / float(self.image.size[0]) * 150, 3),
|
||||||
if int((border[2] - border[0]) * multiplier) < self.size[0]:
|
round(float(self.image.size[1] - border[3]) / float(self.image.size[1]) * 150, 3)]
|
||||||
|
if int((border[2] - border[0]) * multiplier) < self.size[0] + 10:
|
||||||
self.noHPV = True
|
self.noHPV = True
|
||||||
if int((border[3] - border[1]) * multiplier) < self.size[1]:
|
if int((border[3] - border[1]) * multiplier) < self.size[1] + 10:
|
||||||
self.noVPV = True
|
self.noVPV = True
|
||||||
else:
|
else:
|
||||||
self.border = [0, 0, 0, 0]
|
self.border = [0.0, 0.0, 0.0, 0.0]
|
||||||
self.noHPV = True
|
self.noHPV = True
|
||||||
self.noVPV = True
|
self.noVPV = True
|
||||||
|
|
||||||
def resizeImage(self, qualityMode=None):
|
def resizeImage(self):
|
||||||
upscale = self.opt.upscale
|
if self.opt.bordersColor:
|
||||||
stretch = self.opt.stretch
|
fill = self.opt.bordersColor
|
||||||
bordersColor = self.opt.bordersColor
|
|
||||||
if qualityMode is None:
|
|
||||||
qualityMode = self.opt.quality
|
|
||||||
if bordersColor:
|
|
||||||
fill = bordersColor
|
|
||||||
else:
|
else:
|
||||||
fill = self.fill
|
fill = self.fill
|
||||||
# Set target size
|
# Set target size
|
||||||
if qualityMode == 0:
|
if self.opt.quality == 0:
|
||||||
size = (self.size[0], self.size[1])
|
size = (self.size[0], self.size[1])
|
||||||
elif qualityMode == 1 and not stretch and not upscale and self.image.size[0] <=\
|
elif self.opt.quality == 1 and not self.opt.stretch and not self.opt.upscale and self.image.size[0] <=\
|
||||||
self.size[0] and self.image.size[1] <= self.size[1]:
|
self.size[0] and self.image.size[1] <= self.size[1]:
|
||||||
size = (self.size[0], self.size[1])
|
size = (self.size[0], self.size[1])
|
||||||
elif qualityMode == 1:
|
elif self.opt.quality == 1:
|
||||||
# Forcing upscale to make sure that margins will be not too big
|
# Forcing upscale to make sure that margins will be not too big
|
||||||
if not stretch:
|
if not self.opt.stretch:
|
||||||
upscale = True
|
self.opt.upscale = True
|
||||||
size = (self.panelviewsize[0], self.panelviewsize[1])
|
size = (self.panelviewsize[0], self.panelviewsize[1])
|
||||||
elif qualityMode == 2 and not stretch and not upscale and self.image.size[0] <=\
|
elif self.opt.quality == 2 and not self.opt.stretch and not self.opt.upscale and self.image.size[0] <=\
|
||||||
self.size[0] and self.image.size[1] <= self.size[1]:
|
self.size[0] and self.image.size[1] <= self.size[1]:
|
||||||
self.purge = True
|
# HQ version will not be needed
|
||||||
return self.image
|
self.noHQ = True
|
||||||
|
return
|
||||||
else:
|
else:
|
||||||
self.hq = True
|
|
||||||
size = (self.panelviewsize[0], self.panelviewsize[1])
|
size = (self.panelviewsize[0], self.panelviewsize[1])
|
||||||
# If stretching is on - Resize without other considerations
|
# If stretching is on - Resize without other considerations
|
||||||
if stretch:
|
if self.opt.stretch:
|
||||||
if self.image.size[0] <= size[0] and self.image.size[1] <= size[1]:
|
if self.image.size[0] <= size[0] and self.image.size[1] <= size[1]:
|
||||||
method = Image.BICUBIC
|
method = Image.BICUBIC
|
||||||
else:
|
else:
|
||||||
method = Image.LANCZOS
|
method = Image.LANCZOS
|
||||||
self.image = self.image.resize(size, method)
|
self.image = self.image.resize(size, method)
|
||||||
return self.image
|
return
|
||||||
# If image is smaller than target resolution and upscale is off - Just expand it by adding margins
|
# If image is smaller than target resolution and upscale is off - Just expand it by adding margins
|
||||||
if self.image.size[0] <= size[0] and self.image.size[1] <= size[1] and not upscale:
|
if self.image.size[0] <= size[0] and self.image.size[1] <= size[1] and not self.opt.upscale:
|
||||||
borderw = int((size[0] - self.image.size[0]) / 2)
|
borderw = int((size[0] - self.image.size[0]) / 2)
|
||||||
borderh = int((size[1] - self.image.size[1]) / 2)
|
borderh = int((size[1] - self.image.size[1]) / 2)
|
||||||
# PV is disabled when source image is smaller than device screen and upscale is off
|
# PV is disabled when source image is smaller than device screen and upscale is off
|
||||||
@@ -267,7 +254,7 @@ class ComicPage:
|
|||||||
# Border can't be float so sometimes image might be 1px too small/large
|
# Border can't be float so sometimes image might be 1px too small/large
|
||||||
if self.image.size[0] != size[0] or self.image.size[1] != size[1]:
|
if self.image.size[0] != size[0] or self.image.size[1] != size[1]:
|
||||||
self.image = ImageOps.fit(self.image, size, method=Image.BICUBIC, centering=(0.5, 0.5))
|
self.image = ImageOps.fit(self.image, size, method=Image.BICUBIC, centering=(0.5, 0.5))
|
||||||
return self.image
|
return
|
||||||
# Otherwise - Upscale/Downscale
|
# Otherwise - Upscale/Downscale
|
||||||
ratioDev = float(size[0]) / float(size[1])
|
ratioDev = float(size[0]) / float(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:
|
||||||
@@ -281,7 +268,7 @@ class ComicPage:
|
|||||||
else:
|
else:
|
||||||
method = Image.LANCZOS
|
method = Image.LANCZOS
|
||||||
self.image = ImageOps.fit(self.image, size, method=method, centering=(0.5, 0.5))
|
self.image = ImageOps.fit(self.image, size, method=method, centering=(0.5, 0.5))
|
||||||
return self.image
|
return
|
||||||
|
|
||||||
def splitPage(self, targetdir):
|
def splitPage(self, targetdir):
|
||||||
width, height = self.image.size
|
width, height = self.image.size
|
||||||
@@ -374,7 +361,6 @@ class ComicPage:
|
|||||||
else:
|
else:
|
||||||
diff = pageNumberCut1
|
diff = pageNumberCut1
|
||||||
self.image = self.image.crop((0, 0, widthImg, heightImg - diff))
|
self.image = self.image.crop((0, 0, widthImg, heightImg - diff))
|
||||||
return self.image
|
|
||||||
|
|
||||||
def cropWhiteSpace(self):
|
def cropWhiteSpace(self):
|
||||||
if ImageChops.invert(self.image).getbbox() is not None:
|
if ImageChops.invert(self.image).getbbox() is not None:
|
||||||
@@ -410,7 +396,6 @@ class ComicPage:
|
|||||||
diff += delta
|
diff += delta
|
||||||
diff -= delta
|
diff -= delta
|
||||||
self.image = self.image.crop((0, 0, widthImg - diff, heightImg))
|
self.image = self.image.crop((0, 0, widthImg - diff, heightImg))
|
||||||
return self.image
|
|
||||||
|
|
||||||
def getImageHistogram(self, image):
|
def getImageHistogram(self, image):
|
||||||
histogram = image.histogram()
|
histogram = image.histogram()
|
||||||
@@ -443,13 +428,13 @@ class ComicPage:
|
|||||||
while startY < bw.size[1]:
|
while startY < bw.size[1]:
|
||||||
if startY + 5 > bw.size[1]:
|
if startY + 5 > bw.size[1]:
|
||||||
startY = bw.size[1] - 5
|
startY = bw.size[1] - 5
|
||||||
fill += self.getImageHistogram(bw.crop((0, startY, bw.size[0], startY+5)))
|
fill += self.getImageHistogram(bw.crop((0, startY, bw.size[0], startY + 5)))
|
||||||
startY += 5
|
startY += 5
|
||||||
startX = 0
|
startX = 0
|
||||||
while startX < bw.size[0]:
|
while startX < bw.size[0]:
|
||||||
if startX + 5 > bw.size[0]:
|
if startX + 5 > bw.size[0]:
|
||||||
startX = bw.size[0] - 5
|
startX = bw.size[0] - 5
|
||||||
fill += self.getImageHistogram(bw.crop((startX, 0, startX+5, bw.size[1])))
|
fill += self.getImageHistogram(bw.crop((startX, 0, startX + 5, bw.size[1])))
|
||||||
startX += 5
|
startX += 5
|
||||||
if fill > 0:
|
if fill > 0:
|
||||||
self.fill = 'black'
|
self.fill = 'black'
|
||||||
@@ -457,28 +442,24 @@ class ComicPage:
|
|||||||
self.fill = 'white'
|
self.fill = 'white'
|
||||||
|
|
||||||
def isImageColor(self):
|
def isImageColor(self):
|
||||||
v = ImageStat.Stat(self.image).var
|
img = self.image.copy()
|
||||||
isMonochromatic = reduce(lambda x, y: x and y < 0.005, v, True)
|
bands = img.getbands()
|
||||||
if isMonochromatic:
|
if bands == ('R', 'G', 'B') or bands == ('R', 'G', 'B', 'A'):
|
||||||
# Monochromatic
|
thumb = img.resize((40, 40))
|
||||||
|
SSE, bias = 0, [0, 0, 0]
|
||||||
|
bias = ImageStat.Stat(thumb).mean[:3]
|
||||||
|
bias = [b - sum(bias) / 3 for b in bias]
|
||||||
|
for pixel in thumb.getdata():
|
||||||
|
mu = sum(pixel) / 3
|
||||||
|
SSE += sum((pixel[i] - mu - bias[i]) * (pixel[i] - mu - bias[i]) for i in [0, 1, 2])
|
||||||
|
MSE = float(SSE) / (40 * 40)
|
||||||
|
if MSE <= 22:
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
if len(v) == 3:
|
|
||||||
maxmin = abs(max(v) - min(v))
|
|
||||||
if maxmin > 1000:
|
|
||||||
# Color
|
|
||||||
return True
|
return True
|
||||||
elif maxmin > 100:
|
elif len(bands) == 1:
|
||||||
# Probably color
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
# Grayscale
|
|
||||||
return False
|
|
||||||
elif len(v) == 1:
|
|
||||||
# Black and white
|
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
# Detection failed
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
@@ -512,7 +493,7 @@ class Cover:
|
|||||||
def processExternal(self):
|
def processExternal(self):
|
||||||
self.image = self.image.convert('RGB')
|
self.image = self.image.convert('RGB')
|
||||||
self.image.thumbnail(self.options.profileData[1], Image.LANCZOS)
|
self.image.thumbnail(self.options.profileData[1], Image.LANCZOS)
|
||||||
self.save(True)
|
self.save()
|
||||||
|
|
||||||
def trim(self):
|
def trim(self):
|
||||||
bg = Image.new(self.image.mode, self.image.size, self.image.getpixel((0, 0)))
|
bg = Image.new(self.image.mode, self.image.size, self.image.getpixel((0, 0)))
|
||||||
@@ -524,15 +505,8 @@ class Cover:
|
|||||||
else:
|
else:
|
||||||
return self.image
|
return self.image
|
||||||
|
|
||||||
def save(self, external=False):
|
def save(self):
|
||||||
if external:
|
|
||||||
source = self.options.remoteCovers[self.tomeNumber].split('/')[-1]
|
|
||||||
else:
|
|
||||||
source = self.source
|
|
||||||
try:
|
try:
|
||||||
if os.path.splitext(source)[1].lower() == '.png':
|
|
||||||
self.image.save(self.target, "PNG", optimize=1)
|
|
||||||
else:
|
|
||||||
self.image.save(self.target, "JPEG", optimize=1, quality=80)
|
self.image.save(self.target, "JPEG", optimize=1, quality=80)
|
||||||
except IOError:
|
except IOError:
|
||||||
raise RuntimeError('Failed to save cover')
|
raise RuntimeError('Failed to process downloaded cover.')
|
||||||
|
|||||||
172
kcc/metadata.py
Normal file
172
kcc/metadata.py
Normal file
@@ -0,0 +1,172 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# Copyright (c) 2013-2015 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||||
|
#
|
||||||
|
# Permission to use, copy, modify, and/or distribute this software for
|
||||||
|
# any purpose with or without fee is hereby granted, provided that the
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
import os
|
||||||
|
from xml.dom.minidom import parse, Document
|
||||||
|
from re import compile
|
||||||
|
from zipfile import is_zipfile, ZipFile, ZIP_DEFLATED
|
||||||
|
from subprocess import STDOUT, PIPE
|
||||||
|
from psutil import Popen
|
||||||
|
from tempfile import mkdtemp
|
||||||
|
from shutil import rmtree
|
||||||
|
from .shared import removeFromZIP, check7ZFile as is_7zfile
|
||||||
|
from . import rarfile
|
||||||
|
|
||||||
|
|
||||||
|
class MetadataParser:
|
||||||
|
def __init__(self, source):
|
||||||
|
self.source = source
|
||||||
|
self.data = {'Series': '',
|
||||||
|
'Volume': '',
|
||||||
|
'Number': '',
|
||||||
|
'Writers': [],
|
||||||
|
'Pencillers': [],
|
||||||
|
'Inkers': [],
|
||||||
|
'Colorists': [],
|
||||||
|
'Summary': '',
|
||||||
|
'MUid': '',
|
||||||
|
'Bookmarks': []}
|
||||||
|
self.rawdata = None
|
||||||
|
self.compressor = None
|
||||||
|
if self.source.endswith('.xml'):
|
||||||
|
self.rawdata = parse(self.source)
|
||||||
|
self.parseXML()
|
||||||
|
else:
|
||||||
|
if is_zipfile(self.source):
|
||||||
|
self.compressor = 'zip'
|
||||||
|
with ZipFile(self.source) as zip_file:
|
||||||
|
for member in zip_file.namelist():
|
||||||
|
if member != 'ComicInfo.xml':
|
||||||
|
continue
|
||||||
|
with zip_file.open(member) as xml_file:
|
||||||
|
self.rawdata = parse(xml_file)
|
||||||
|
elif rarfile.is_rarfile(self.source):
|
||||||
|
self.compressor = 'rar'
|
||||||
|
with rarfile.RarFile(self.source) as rar_file:
|
||||||
|
for member in rar_file.namelist():
|
||||||
|
if member != 'ComicInfo.xml':
|
||||||
|
continue
|
||||||
|
with rar_file.open(member) as xml_file:
|
||||||
|
self.rawdata = parse(xml_file)
|
||||||
|
elif is_7zfile(self.source):
|
||||||
|
self.compressor = '7z'
|
||||||
|
workdir = mkdtemp('', 'KCC-')
|
||||||
|
tmpXML = os.path.join(workdir, 'ComicInfo.xml')
|
||||||
|
output = Popen('7za e "' + self.source + '" ComicInfo.xml -o"' + workdir + '"',
|
||||||
|
stdout=PIPE, stderr=STDOUT, shell=True)
|
||||||
|
extracted = False
|
||||||
|
for line in output.stdout:
|
||||||
|
if b"Everything is Ok" in line or b"No files to process" in line:
|
||||||
|
extracted = True
|
||||||
|
if not extracted:
|
||||||
|
rmtree(workdir)
|
||||||
|
raise OSError('Failed to extract 7ZIP file.')
|
||||||
|
if os.path.isfile(tmpXML):
|
||||||
|
self.rawdata = parse(tmpXML)
|
||||||
|
rmtree(workdir)
|
||||||
|
else:
|
||||||
|
raise OSError('Failed to detect archive format.')
|
||||||
|
if self.rawdata:
|
||||||
|
self.parseXML()
|
||||||
|
|
||||||
|
def parseXML(self):
|
||||||
|
if len(self.rawdata.getElementsByTagName('Series')) != 0:
|
||||||
|
self.data['Series'] = self.rawdata.getElementsByTagName('Series')[0].firstChild.nodeValue
|
||||||
|
if len(self.rawdata.getElementsByTagName('Volume')) != 0:
|
||||||
|
self.data['Volume'] = self.rawdata.getElementsByTagName('Volume')[0].firstChild.nodeValue
|
||||||
|
if len(self.rawdata.getElementsByTagName('Number')) != 0:
|
||||||
|
self.data['Number'] = self.rawdata.getElementsByTagName('Number')[0].firstChild.nodeValue
|
||||||
|
if len(self.rawdata.getElementsByTagName('Summary')) != 0:
|
||||||
|
self.data['Summary'] = self.rawdata.getElementsByTagName('Summary')[0].firstChild.nodeValue
|
||||||
|
for field in ['Writer', 'Penciller', 'Inker', 'Colorist']:
|
||||||
|
if len(self.rawdata.getElementsByTagName(field)) != 0:
|
||||||
|
for person in self.rawdata.getElementsByTagName(field)[0].firstChild.nodeValue.split(', '):
|
||||||
|
self.data[field + 's'].append(person)
|
||||||
|
self.data[field + 's'] = list(set(self.data[field + 's']))
|
||||||
|
self.data[field + 's'].sort()
|
||||||
|
if len(self.rawdata.getElementsByTagName('ScanInformation')) != 0:
|
||||||
|
coverId = compile('(MCD\\()(\\d+)(\\))')\
|
||||||
|
.search(self.rawdata.getElementsByTagName('ScanInformation')[0].firstChild.nodeValue)
|
||||||
|
if coverId:
|
||||||
|
self.data['MUid'] = coverId.group(2)
|
||||||
|
if len(self.rawdata.getElementsByTagName('Page')) != 0:
|
||||||
|
for page in self.rawdata.getElementsByTagName('Page'):
|
||||||
|
if 'Bookmark' in page.attributes and 'Image' in page.attributes:
|
||||||
|
self.data['Bookmarks'].append((int(page.attributes['Image'].value),
|
||||||
|
page.attributes['Bookmark'].value))
|
||||||
|
|
||||||
|
def saveXML(self):
|
||||||
|
if self.rawdata:
|
||||||
|
root = self.rawdata.getElementsByTagName('ComicInfo')[0]
|
||||||
|
for row in (['Series', self.data['Series']], ['Volume', self.data['Volume']],
|
||||||
|
['Number', self.data['Number']], ['Writer', ', '.join(self.data['Writers'])],
|
||||||
|
['Penciller', ', '.join(self.data['Pencillers'])], ['Inker', ', '.join(self.data['Inkers'])],
|
||||||
|
['Colorist', ', '.join(self.data['Colorists'])], ['Summary', self.data['Summary']],
|
||||||
|
['ScanInformation', 'MCD(' + self.data['MUid'] + ')' if self.data['MUid'] else '']):
|
||||||
|
if self.rawdata.getElementsByTagName(row[0]):
|
||||||
|
node = self.rawdata.getElementsByTagName(row[0])[0]
|
||||||
|
if row[1]:
|
||||||
|
node.firstChild.replaceWholeText(row[1])
|
||||||
|
else:
|
||||||
|
root.removeChild(node)
|
||||||
|
elif row[1]:
|
||||||
|
main = self.rawdata.createElement(row[0])
|
||||||
|
root.appendChild(main)
|
||||||
|
text = self.rawdata.createTextNode(row[1])
|
||||||
|
main.appendChild(text)
|
||||||
|
else:
|
||||||
|
doc = Document()
|
||||||
|
root = doc.createElement('ComicInfo')
|
||||||
|
root.setAttribute('xmlns:xsd', 'http://www.w3.org/2001/XMLSchema')
|
||||||
|
root.setAttribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance')
|
||||||
|
doc.appendChild(root)
|
||||||
|
for row in (['Series', self.data['Series']], ['Volume', self.data['Volume']],
|
||||||
|
['Number', self.data['Number']], ['Writer', ', '.join(self.data['Writers'])],
|
||||||
|
['Penciller', ', '.join(self.data['Pencillers'])], ['Inker', ', '.join(self.data['Inkers'])],
|
||||||
|
['Colorist', ', '.join(self.data['Colorists'])], ['Summary', self.data['Summary']],
|
||||||
|
['ScanInformation', 'MCD(' + self.data['MUid'] + ')' if self.data['MUid'] else '']):
|
||||||
|
if row[1]:
|
||||||
|
main = doc.createElement(row[0])
|
||||||
|
root.appendChild(main)
|
||||||
|
text = doc.createTextNode(row[1])
|
||||||
|
main.appendChild(text)
|
||||||
|
self.rawdata = doc
|
||||||
|
if self.source.endswith('.xml'):
|
||||||
|
with open(self.source, 'w', encoding='utf-8') as f:
|
||||||
|
self.rawdata.writexml(f, encoding='utf-8')
|
||||||
|
else:
|
||||||
|
workdir = mkdtemp('', 'KCC-')
|
||||||
|
tmpXML = os.path.join(workdir, 'ComicInfo.xml')
|
||||||
|
with open(tmpXML, 'w', encoding='utf-8') as f:
|
||||||
|
self.rawdata.writexml(f, encoding='utf-8')
|
||||||
|
if is_zipfile(self.source):
|
||||||
|
removeFromZIP(self.source, 'ComicInfo.xml')
|
||||||
|
with ZipFile(self.source, mode='a', compression=ZIP_DEFLATED) as zip_file:
|
||||||
|
zip_file.write(tmpXML, arcname=tmpXML.split(os.sep)[-1])
|
||||||
|
elif rarfile.is_rarfile(self.source):
|
||||||
|
raise NotImplementedError
|
||||||
|
elif is_7zfile(self.source):
|
||||||
|
output = Popen('7za a "' + self.source + '" "' + tmpXML + '"', stdout=PIPE, stderr=STDOUT, shell=True)
|
||||||
|
extracted = False
|
||||||
|
for line in output.stdout:
|
||||||
|
if b"Everything is Ok" in line:
|
||||||
|
extracted = True
|
||||||
|
if not extracted:
|
||||||
|
rmtree(workdir)
|
||||||
|
raise OSError('Failed to modify 7ZIP file.')
|
||||||
|
rmtree(workdir)
|
||||||
@@ -19,10 +19,6 @@
|
|||||||
# PERFORMANCE OF THIS SOFTWARE.
|
# PERFORMANCE OF THIS SOFTWARE.
|
||||||
#
|
#
|
||||||
|
|
||||||
__license__ = 'ISC'
|
|
||||||
__copyright__ = '2012-2015, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>'
|
|
||||||
__docformat__ = 'restructuredtext en'
|
|
||||||
|
|
||||||
import os
|
import os
|
||||||
from random import choice
|
from random import choice
|
||||||
from string import ascii_uppercase, digits
|
from string import ascii_uppercase, digits
|
||||||
@@ -33,7 +29,7 @@ class PdfJpgExtract:
|
|||||||
self.origFileName = origFileName
|
self.origFileName = origFileName
|
||||||
self.filename = os.path.splitext(origFileName)
|
self.filename = os.path.splitext(origFileName)
|
||||||
# noinspection PyUnusedLocal
|
# noinspection PyUnusedLocal
|
||||||
self.path = self.filename[0] + "-KCC-TMP-" + ''.join(choice(ascii_uppercase + digits) for x in range(3))
|
self.path = self.filename[0] + "-KCC-" + ''.join(choice(ascii_uppercase + digits) for x in range(3))
|
||||||
|
|
||||||
def getPath(self):
|
def getPath(self):
|
||||||
return self.path
|
return self.path
|
||||||
|
|||||||
@@ -360,9 +360,8 @@ class RarCannotExec(RarExecError):
|
|||||||
|
|
||||||
def is_rarfile(xfile):
|
def is_rarfile(xfile):
|
||||||
'''Check quickly whether file is rar archive.'''
|
'''Check quickly whether file is rar archive.'''
|
||||||
fd = XFile(xfile)
|
with open(xfile, 'rb') as fh:
|
||||||
buf = fd.read(len(RAR_ID))
|
buf = fh.read(len(RAR_ID))
|
||||||
fd.close()
|
|
||||||
if buf == RAR_ID or buf == RAR5_ID:
|
if buf == RAR_ID or buf == RAR5_ID:
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
|
|||||||
108
kcc/shared.py
108
kcc/shared.py
@@ -16,13 +16,21 @@
|
|||||||
# PERFORMANCE OF THIS SOFTWARE.
|
# PERFORMANCE OF THIS SOFTWARE.
|
||||||
#
|
#
|
||||||
|
|
||||||
__license__ = 'ISC'
|
|
||||||
__copyright__ = '2012-2015, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>'
|
|
||||||
__docformat__ = 'restructuredtext en'
|
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
from sys import version_info
|
||||||
from hashlib import md5
|
from hashlib import md5
|
||||||
from html.parser import HTMLParser
|
from html.parser import HTMLParser
|
||||||
|
from distutils.version import StrictVersion
|
||||||
|
from time import sleep
|
||||||
|
from shutil import rmtree, move, copy
|
||||||
|
from tempfile import mkdtemp
|
||||||
|
from zipfile import ZipFile, ZIP_DEFLATED
|
||||||
|
from re import split
|
||||||
|
from traceback import format_tb
|
||||||
|
try:
|
||||||
|
from scandir import walk
|
||||||
|
except ImportError:
|
||||||
|
walk = os.walk
|
||||||
|
|
||||||
|
|
||||||
class HTMLStripper(HTMLParser):
|
class HTMLStripper(HTMLParser):
|
||||||
@@ -39,6 +47,9 @@ class HTMLStripper(HTMLParser):
|
|||||||
def get_data(self):
|
def get_data(self):
|
||||||
return ''.join(self.fed)
|
return ''.join(self.fed)
|
||||||
|
|
||||||
|
def error(self, message):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def getImageFileName(imgfile):
|
def getImageFileName(imgfile):
|
||||||
name, ext = os.path.splitext(imgfile)
|
name, ext = os.path.splitext(imgfile)
|
||||||
@@ -48,11 +59,20 @@ def getImageFileName(imgfile):
|
|||||||
return [name, ext]
|
return [name, ext]
|
||||||
|
|
||||||
|
|
||||||
|
def walkSort(dirnames, filenames):
|
||||||
|
convert = lambda text: int(text) if text.isdigit() else text
|
||||||
|
alphanum_key = lambda key: [convert(c) for c in split('([0-9]+)', key)]
|
||||||
|
dirnames.sort(key=lambda name: alphanum_key(name.lower()))
|
||||||
|
filenames.sort(key=lambda name: alphanum_key(name.lower()))
|
||||||
|
return dirnames, filenames
|
||||||
|
|
||||||
|
|
||||||
def walkLevel(some_dir, level=1):
|
def walkLevel(some_dir, level=1):
|
||||||
some_dir = some_dir.rstrip(os.path.sep)
|
some_dir = some_dir.rstrip(os.path.sep)
|
||||||
assert os.path.isdir(some_dir)
|
assert os.path.isdir(some_dir)
|
||||||
num_sep = some_dir.count(os.path.sep)
|
num_sep = some_dir.count(os.path.sep)
|
||||||
for root, dirs, files in os.walk(some_dir):
|
for root, dirs, files in walk(some_dir):
|
||||||
|
dirs, files = walkSort(dirs, files)
|
||||||
yield root, dirs, files
|
yield root, dirs, files
|
||||||
num_sep_this = root.count(os.path.sep)
|
num_sep_this = root.count(os.path.sep)
|
||||||
if num_sep + level <= num_sep_this:
|
if num_sep + level <= num_sep_this:
|
||||||
@@ -76,40 +96,74 @@ def check7ZFile(filePath):
|
|||||||
return header == b"7z\xbc\xaf'\x1c"
|
return header == b"7z\xbc\xaf'\x1c"
|
||||||
|
|
||||||
|
|
||||||
# noinspection PyUnresolvedReferences
|
def saferReplace(old, new):
|
||||||
|
for x in range(5):
|
||||||
|
try:
|
||||||
|
os.replace(old, new)
|
||||||
|
except PermissionError:
|
||||||
|
sleep(5)
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
raise PermissionError
|
||||||
|
|
||||||
|
|
||||||
|
def removeFromZIP(zipfname, *filenames):
|
||||||
|
tempdir = mkdtemp('', 'KCC-')
|
||||||
|
try:
|
||||||
|
tempname = os.path.join(tempdir, 'KCC.zip')
|
||||||
|
with ZipFile(zipfname, 'r') as zipread:
|
||||||
|
with ZipFile(tempname, 'w', compression=ZIP_DEFLATED) as zipwrite:
|
||||||
|
for item in zipread.infolist():
|
||||||
|
if item.filename not in filenames:
|
||||||
|
zipwrite.writestr(item, zipread.read(item.filename))
|
||||||
|
copy(tempname, zipfname)
|
||||||
|
finally:
|
||||||
|
rmtree(tempdir, True)
|
||||||
|
|
||||||
|
|
||||||
|
def sanitizeTrace(traceback):
|
||||||
|
return ''.join(format_tb(traceback))\
|
||||||
|
.replace('C:\\Users\\pawel\\Documents\\Projekty\\KCC\\', '')\
|
||||||
|
.replace('C:\\Python34\\', '')\
|
||||||
|
.replace('C:\\Python34_64\\', '')
|
||||||
|
|
||||||
|
|
||||||
def dependencyCheck(level):
|
def dependencyCheck(level):
|
||||||
missing = []
|
missing = []
|
||||||
if level > 2:
|
if level > 2:
|
||||||
try:
|
try:
|
||||||
from PyQt5 import QtCore, QtNetwork, QtWidgets
|
from PyQt5.QtCore import qVersion as qtVersion
|
||||||
if tuple(map(int, ('5.2.0'.split(".")))) > tuple(map(int, (QtCore.qVersion().split(".")))):
|
if StrictVersion('5.2.1') > StrictVersion(qtVersion()):
|
||||||
missing.append('PyQt5 5.2.0+')
|
missing.append('PyQt 5.2.1+')
|
||||||
except ImportError:
|
except ImportError:
|
||||||
missing.append('PyQt5 5.2.0+')
|
missing.append('PyQt 5.2.1+')
|
||||||
if level > 1:
|
if level > 1:
|
||||||
try:
|
try:
|
||||||
import psutil
|
from psutil import __version__ as psutilVersion
|
||||||
if tuple(map(int, ('2.0.0'.split(".")))) > tuple(map(int, psutil.version_info)):
|
if StrictVersion('3.0.0') > StrictVersion(psutilVersion):
|
||||||
missing.append('psutil 2.0.0+')
|
missing.append('psutil 3.0.0+')
|
||||||
except ImportError:
|
except ImportError:
|
||||||
missing.append('psutil 2.0.0+')
|
missing.append('psutil 3.0.0+')
|
||||||
try:
|
try:
|
||||||
import slugify
|
from slugify import __version__ as slugifyVersion
|
||||||
|
if StrictVersion('1.1.3') > StrictVersion(slugifyVersion):
|
||||||
|
missing.append('python-slugify 1.1.3+')
|
||||||
except ImportError:
|
except ImportError:
|
||||||
missing.append('python-slugify')
|
missing.append('python-slugify 1.1.3+')
|
||||||
try:
|
try:
|
||||||
import PIL
|
from PIL import PILLOW_VERSION as pillowVersion
|
||||||
if tuple(map(int, ('2.7.0'.split(".")))) > tuple(map(int, (PIL.PILLOW_VERSION.split(".")))):
|
if StrictVersion('2.8.2') > StrictVersion(pillowVersion):
|
||||||
missing.append('Pillow 2.7.0+')
|
missing.append('Pillow 2.8.2+')
|
||||||
except ImportError:
|
except ImportError:
|
||||||
missing.append('Pillow 2.7.0+')
|
missing.append('Pillow 2.8.2+')
|
||||||
|
if version_info[1] < 5:
|
||||||
|
try:
|
||||||
|
from scandir import __version__ as scandirVersion
|
||||||
|
if StrictVersion('1.1') > StrictVersion(scandirVersion):
|
||||||
|
missing.append('scandir 1.1+')
|
||||||
|
except ImportError:
|
||||||
|
missing.append('scandir 1.1+')
|
||||||
if len(missing) > 0:
|
if len(missing) > 0:
|
||||||
try:
|
|
||||||
import tkinter
|
|
||||||
import tkinter.messagebox
|
|
||||||
importRoot = tkinter.Tk()
|
|
||||||
importRoot.withdraw()
|
|
||||||
tkinter.messagebox.showerror('KCC - Error', 'ERROR: ' + ', '.join(missing) + ' is not installed!')
|
|
||||||
except ImportError:
|
|
||||||
print('ERROR: ' + ', '.join(missing) + ' is not installed!')
|
print('ERROR: ' + ', '.join(missing) + ' is not installed!')
|
||||||
exit(1)
|
exit(1)
|
||||||
BIN
other/7za.exe
Normal file
BIN
other/7za.exe
Normal file
Binary file not shown.
BIN
other/UnRAR.exe
Normal file
BIN
other/UnRAR.exe
Normal file
Binary file not shown.
BIN
other/unrar
Executable file
BIN
other/unrar
Executable file
Binary file not shown.
10
setup.json
Normal file
10
setup.json
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"title": "Kindle Comic Converter",
|
||||||
|
"icon": "icons/comic2ebook.icns",
|
||||||
|
"background": "icons/WizardOSX.png",
|
||||||
|
"icon-size": 160,
|
||||||
|
"contents": [
|
||||||
|
{ "x": 180, "y": 300, "type": "file", "path": "dist/Kindle Comic Converter.app" },
|
||||||
|
{ "x": 520, "y": 300, "type": "link", "path": "/Applications" }
|
||||||
|
]
|
||||||
|
}
|
||||||
157
setup.py
157
setup.py
@@ -1,24 +1,31 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""
|
"""
|
||||||
py2exe/py2app build script for KCC.
|
pip/py2exe/py2app build script for KCC.
|
||||||
|
|
||||||
Usage (Windows):
|
Usage (Windows):
|
||||||
python setup.py py2exe
|
py -3.4 setup.py py2exe
|
||||||
|
|
||||||
|
Usage (Linux):
|
||||||
|
python3 setup.py make_pyz or python3 setup.py install
|
||||||
|
|
||||||
Usage (Mac OS X):
|
Usage (Mac OS X):
|
||||||
python setup.py py2app
|
python3 setup.py py2app
|
||||||
"""
|
"""
|
||||||
from sys import platform, version_info
|
|
||||||
if version_info[0] != 3:
|
|
||||||
print('ERROR: This is Python 3 script!')
|
|
||||||
exit(1)
|
|
||||||
|
|
||||||
NAME = "KindleComicConverter"
|
from sys import platform, version_info, argv
|
||||||
VERSION = "4.4"
|
from kcc import __version__
|
||||||
MAIN = "kcc.py"
|
|
||||||
|
|
||||||
if platform == "darwin":
|
|
||||||
|
NAME = 'KindleComicConverter'
|
||||||
|
VERSION = __version__
|
||||||
|
MAIN = 'kcc.py'
|
||||||
|
extra_options = {}
|
||||||
|
|
||||||
|
|
||||||
|
if platform == 'darwin':
|
||||||
from setuptools import setup
|
from setuptools import setup
|
||||||
|
from os import chmod, makedirs, system
|
||||||
|
from shutil import copyfile
|
||||||
extra_options = dict(
|
extra_options = dict(
|
||||||
setup_requires=['py2app'],
|
setup_requires=['py2app'],
|
||||||
app=[MAIN],
|
app=[MAIN],
|
||||||
@@ -26,17 +33,14 @@ if platform == "darwin":
|
|||||||
py2app=dict(
|
py2app=dict(
|
||||||
argv_emulation=True,
|
argv_emulation=True,
|
||||||
iconfile='icons/comic2ebook.icns',
|
iconfile='icons/comic2ebook.icns',
|
||||||
includes=['PIL', 'sip', 'PyQt5', 'PyQt5.QtCore', 'PyQt5.QtGui', 'PyQt5.QtNetwork', 'PyQt5.QtWidgets',
|
includes=['sip', 'PyQt5.QtPrintSupport'],
|
||||||
'PyQt5.QtPrintSupport'],
|
|
||||||
resources=['LICENSE.txt', 'other/qt.conf', 'other/Additional-LICENSE.txt', 'other/unrar', 'other/7za'],
|
resources=['LICENSE.txt', 'other/qt.conf', 'other/Additional-LICENSE.txt', 'other/unrar', 'other/7za'],
|
||||||
plist=dict(
|
plist=dict(
|
||||||
CFBundleName=NAME,
|
CFBundleName='Kindle Comic Converter',
|
||||||
CFBundleShortVersionString=VERSION,
|
CFBundleShortVersionString=VERSION,
|
||||||
CFBundleGetInfoString=NAME + " " + VERSION +
|
CFBundleGetInfoString=NAME + ' ' + VERSION +
|
||||||
", written 2012-2015 by Ciro Mattia Gonano and Pawel Jastrzebski",
|
', written 2012-2015 by Ciro Mattia Gonano and Pawel Jastrzebski',
|
||||||
CFBundleExecutable=NAME,
|
CFBundleExecutable=NAME,
|
||||||
CFBundleIdentifier='com.github.ciromattia.kcc',
|
|
||||||
CFBundleSignature='dplt',
|
|
||||||
CFBundleDocumentTypes=[
|
CFBundleDocumentTypes=[
|
||||||
dict(
|
dict(
|
||||||
CFBundleTypeExtensions=['cbz', 'cbr', 'cb7', 'zip', 'rar', '7z', 'pdf'],
|
CFBundleTypeExtensions=['cbz', 'cbr', 'cb7', 'zip', 'rar', '7z', 'pdf'],
|
||||||
@@ -45,6 +49,7 @@ if platform == "darwin":
|
|||||||
CFBundleTypeRole='Editor',
|
CFBundleTypeRole='Editor',
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
|
CFBundleIdentifier='com.kindlecomicconverter.KindleComicConverter',
|
||||||
LSMinimumSystemVersion='10.8.0',
|
LSMinimumSystemVersion='10.8.0',
|
||||||
LSEnvironment=dict(
|
LSEnvironment=dict(
|
||||||
PATH='./../Resources:/usr/local/bin:/usr/bin:/bin'
|
PATH='./../Resources:/usr/local/bin:/usr/bin:/bin'
|
||||||
@@ -54,12 +59,11 @@ if platform == "darwin":
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
elif platform == "win32":
|
elif platform == 'win32':
|
||||||
# noinspection PyUnresolvedReferences
|
|
||||||
import py2exe
|
import py2exe
|
||||||
import platform as arch
|
from platform import architecture
|
||||||
from distutils.core import setup
|
from distutils.core import setup
|
||||||
if arch.architecture()[0] == '64bit':
|
if architecture()[0] == '64bit':
|
||||||
suffix = '_64'
|
suffix = '_64'
|
||||||
else:
|
else:
|
||||||
suffix = ''
|
suffix = ''
|
||||||
@@ -69,47 +73,92 @@ elif platform == "win32":
|
|||||||
'other\\7za.exe',
|
'other\\7za.exe',
|
||||||
'other\\UnRAR.exe',
|
'other\\UnRAR.exe',
|
||||||
'other\\Additional-LICENSE.txt',
|
'other\\Additional-LICENSE.txt',
|
||||||
|
'C:\Python34' + suffix + '\Lib\site-packages\PyQt5\libGLESv2.dll',
|
||||||
'C:\Python34' + suffix + '\Lib\site-packages\PyQt5\libEGL.dll'])]
|
'C:\Python34' + suffix + '\Lib\site-packages\PyQt5\libEGL.dll'])]
|
||||||
extra_options = dict(
|
extra_options = dict(
|
||||||
options={'py2exe': {"bundle_files": 1,
|
options={'py2exe': {'bundle_files': 1,
|
||||||
"dll_excludes": ["tcl85.dll", "tk85.dll"],
|
'dist_dir': 'dist' + suffix,
|
||||||
"dist_dir": "dist" + suffix,
|
'compressed': True,
|
||||||
"compressed": True,
|
'includes': ['sip'],
|
||||||
"includes": ["sip"],
|
'excludes': ['tkinter'],
|
||||||
"excludes": ["tkinter"],
|
'optimize': 2}},
|
||||||
"optimize": 2}},
|
windows=[{'script': MAIN,
|
||||||
windows=[{"script": MAIN,
|
'dest_base': 'KCC',
|
||||||
"dest_base": "KCC",
|
'version': VERSION,
|
||||||
"version": VERSION,
|
'copyright': 'Ciro Mattia Gonano, Pawel Jastrzebski © 2012-2015',
|
||||||
"copyright": "Ciro Mattia Gonano, Pawel Jastrzebski © 2012-2015",
|
'legal_copyright': 'ISC License (ISCL)',
|
||||||
"legal_copyright": "ISC License (ISCL)",
|
'product_version': VERSION,
|
||||||
"product_version": VERSION,
|
'product_name': 'Kindle Comic Converter',
|
||||||
"product_name": "Kindle Comic Converter",
|
'file_description': 'Kindle Comic Converter',
|
||||||
"file_description": "Kindle Comic Converter",
|
'icon_resources': [(1, 'icons\comic2ebook.ico')]}],
|
||||||
"icon_resources": [(1, "icons\comic2ebook.ico")]}],
|
|
||||||
zipfile=None,
|
zipfile=None,
|
||||||
data_files=additional_files)
|
data_files=additional_files)
|
||||||
else:
|
else:
|
||||||
print('Please use setup.sh to build Linux package.')
|
if len(argv) > 1 and argv[1] == 'make_pyz':
|
||||||
exit()
|
from os import system
|
||||||
|
script = '''
|
||||||
|
cp kcc.py __main__.py
|
||||||
|
zip kcc.zip __main__.py kcc/*.py
|
||||||
|
echo "#!/usr/bin/env python3" > kcc-bin
|
||||||
|
cat kcc.zip >> kcc-bin
|
||||||
|
chmod +x kcc-bin
|
||||||
|
|
||||||
|
cp kcc-c2e.py __main__.py
|
||||||
|
zip kcc-c2e.zip __main__.py kcc/*.py
|
||||||
|
echo "#!/usr/bin/env python3" > kcc-c2e-bin
|
||||||
|
cat kcc-c2e.zip >> kcc-c2e-bin
|
||||||
|
chmod +x kcc-c2e-bin
|
||||||
|
|
||||||
|
cp kcc-c2p.py __main__.py
|
||||||
|
zip kcc-c2p.zip __main__.py kcc/*.py
|
||||||
|
echo "#!/usr/bin/env python3" > kcc-c2p-bin
|
||||||
|
cat kcc-c2p.zip >> kcc-c2p-bin
|
||||||
|
chmod +x kcc-c2p-bin
|
||||||
|
|
||||||
|
tar --xform s:^.*/:: --xform s/LICENSE.txt/LICENSE/ --xform s/kcc-bin/kcc/ --xform s/kcc-c2p-bin/kcc-c2p/ \
|
||||||
|
--xform s/kcc-c2e-bin/kcc-c2e/ --xform s/comic2ebook/kcc/ -czf KindleComicConverter_linux_'''\
|
||||||
|
+ VERSION + '''.tar.gz kcc-bin kcc-c2e-bin kcc-c2p-bin LICENSE.txt README.md icons/comic2ebook.png
|
||||||
|
rm __main__.py kcc.zip kcc-c2e.zip kcc-c2p.zip kcc-bin kcc-c2e-bin kcc-c2p-bin
|
||||||
|
'''
|
||||||
|
system("bash -c '%s'" % script)
|
||||||
|
exit(0)
|
||||||
|
else:
|
||||||
|
from setuptools import setup
|
||||||
|
from os import makedirs
|
||||||
|
from shutil import copyfile
|
||||||
|
makedirs('build/_scripts/', exist_ok=True)
|
||||||
|
copyfile('kcc.py', 'build/_scripts/kcc')
|
||||||
|
copyfile('kcc-c2e.py', 'build/_scripts/kcc-c2e')
|
||||||
|
copyfile('kcc-c2p.py', 'build/_scripts/kcc-c2p')
|
||||||
|
extra_options = dict(
|
||||||
|
scripts=['build/_scripts/kcc', 'build/_scripts/kcc-c2e', 'build/_scripts/kcc-c2p'],
|
||||||
|
packages=['kcc'],
|
||||||
|
install_requires=[
|
||||||
|
'Pillow>=2.8.2',
|
||||||
|
'psutil>=3.0.0',
|
||||||
|
'python-slugify>=1.1.3',
|
||||||
|
],
|
||||||
|
zip_safe=False,
|
||||||
|
)
|
||||||
|
if version_info[1] < 5:
|
||||||
|
extra_options['install_requires'].append('scandir>=1.1.0')
|
||||||
|
|
||||||
|
|
||||||
# noinspection PyUnboundLocalVariable
|
|
||||||
setup(
|
setup(
|
||||||
name=NAME,
|
name=NAME,
|
||||||
version=VERSION,
|
version=VERSION,
|
||||||
author="Ciro Mattia Gonano, Pawel Jastrzebski",
|
author='Ciro Mattia Gonano, Pawel Jastrzebski',
|
||||||
author_email="ciromattia@gmail.com, pawelj@iosphe.re",
|
author_email='ciromattia@gmail.com, pawelj@iosphe.re',
|
||||||
description="Kindle Comic Converter",
|
description='Comic and manga converter for E-Book readers.',
|
||||||
license="ISC License (ISCL)",
|
license='ISC License (ISCL)',
|
||||||
keywords="kindle comic mobipocket mobi cbz cbr manga",
|
keywords='kindle comic mobipocket mobi cbz cbr manga',
|
||||||
url="http://github.com/ciromattia/kcc",
|
url='http://github.com/ciromattia/kcc',
|
||||||
**extra_options
|
**extra_options
|
||||||
)
|
)
|
||||||
|
|
||||||
if platform == "darwin":
|
if platform == 'darwin':
|
||||||
from os import chmod, makedirs
|
makedirs('dist/Kindle Comic Converter.app/Contents/PlugIns/platforms', exist_ok=True)
|
||||||
from shutil import copyfile
|
copyfile('other/libqcocoa.dylib', 'dist/Kindle Comic Converter.app/Contents/PlugIns/platforms/libqcocoa.dylib')
|
||||||
makedirs('dist/' + NAME + '.app/Contents/PlugIns/platforms')
|
chmod('dist/Kindle Comic Converter.app/Contents/Resources/unrar', 0o777)
|
||||||
copyfile('other/libqcocoa.dylib', 'dist/' + NAME + '.app/Contents/PlugIns/platforms/libqcocoa.dylib')
|
chmod('dist/Kindle Comic Converter.app/Contents/Resources/7za', 0o777)
|
||||||
chmod('dist/' + NAME + '.app/Contents/Resources/unrar', 0o777)
|
system('appdmg setup.json dist/KindleComicConverter_osx_' + VERSION + '.dmg')
|
||||||
chmod('dist/' + NAME + '.app/Contents/Resources/7za', 0o777)
|
|
||||||
|
|||||||
25
setup.sh
25
setup.sh
@@ -1,25 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
# Linux Python package build script
|
|
||||||
|
|
||||||
VERSION="4.4"
|
|
||||||
|
|
||||||
cp kcc.py __main__.py
|
|
||||||
zip kcc.zip __main__.py kcc/*.py
|
|
||||||
echo "#!/usr/bin/env python3" > kcc-bin
|
|
||||||
cat kcc.zip >> kcc-bin
|
|
||||||
chmod +x kcc-bin
|
|
||||||
|
|
||||||
cp kcc-c2e.py __main__.py
|
|
||||||
zip kcc-c2e.zip __main__.py kcc/*.py
|
|
||||||
echo "#!/usr/bin/env python3" > kcc-c2e-bin
|
|
||||||
cat kcc-c2e.zip >> kcc-c2e-bin
|
|
||||||
chmod +x kcc-c2e-bin
|
|
||||||
|
|
||||||
cp kcc-c2p.py __main__.py
|
|
||||||
zip kcc-c2p.zip __main__.py kcc/*.py
|
|
||||||
echo "#!/usr/bin/env python3" > kcc-c2p-bin
|
|
||||||
cat kcc-c2p.zip >> kcc-c2p-bin
|
|
||||||
chmod +x kcc-c2p-bin
|
|
||||||
|
|
||||||
tar --xform s:^.*/:: --xform s/kcc-bin/kcc/ --xform s/kcc-c2p-bin/kcc-c2p/ --xform s/kcc-c2e-bin/kcc-c2e/ --xform s/comic2ebook/kcc/ -czf KindleComicConverter_linux_$VERSION.tar.gz kcc-bin kcc-c2e-bin kcc-c2p-bin LICENSE.txt icons/comic2ebook.png
|
|
||||||
rm __main__.py kcc.zip kcc-c2e.zip kcc-c2p.zip kcc-bin kcc-c2e-bin kcc-c2p-bin
|
|
||||||
Reference in New Issue
Block a user