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

Compare commits

...

28 Commits
5.4.5 ... 5.5.1

Author SHA1 Message Date
Paweł Jastrzębski
35bba68a72 Merge pull request #308 from ciromattia/dev
5.5.1
2019-03-08 08:44:48 +01:00
Paweł Jastrzębski
0b056a8fa8 Stabilise multiprocessing on OSX 2019-03-08 08:16:53 +01:00
Paweł Jastrzębski
a6f9e84251 Tweaks for OSX binary 2019-03-07 16:24:41 +01:00
Paweł Jastrzębski
259800e48b Tweaks for Windows binary 2019-03-07 11:36:56 +01:00
Paweł Jastrzębski
28e170f1d2 Merge pull request #306 from ciromattia/dev
5.5.0
2019-03-07 08:39:57 +01:00
Paweł Jastrzębski
7120c76025 Updated changelog 2019-03-07 08:38:22 +01:00
Paweł Jastrzębski
4891913b5c Added additional cleanup 2019-03-07 08:26:31 +01:00
Paweł Jastrzębski
cd83b2899c Fixed bookmark parsing (close #229) 2019-03-07 08:21:56 +01:00
Paweł Jastrzębski
535c2c220b Added RAR5 support 2019-03-06 20:12:11 +01:00
Paweł Jastrzębski
409f077c3e Bye, bye DEBs 2019-03-06 19:50:17 +01:00
Paweł Jastrzębski
3ecb2ba877 Fixed metadata encoding (close #281) 2019-03-06 16:37:26 +01:00
Paweł Jastrzębski
c07a9657ef Removed MCD support 2019-03-06 16:16:26 +01:00
Paweł Jastrzębski
7f719a22ad Added PW4 profile (close #293) 2019-03-06 16:01:18 +01:00
Paweł Jastrzębski
332d3d455e Version bump 2019-03-06 15:47:18 +01:00
Paweł Jastrzębski
2070a977ae Updated build enviroment 2019-03-06 10:56:44 +01:00
Paweł Jastrzębski
8f8d0d68a3 Fixed possible glob issues 2019-02-27 13:49:47 +01:00
Paweł Jastrzębski
5a8deb4623 Merge pull request #295 from murphytsai/feature/support-kobo-forma
Support Kobo Forma (close #294)
2019-01-04 10:09:25 +01:00
MurphyTsai
a7ea795df5 support kobo forma. 2019-01-03 14:52:28 +08:00
Paweł Jastrzębski
a2ffd259b8 Code cleanup 2018-07-10 09:00:13 +02:00
Paweł Jastrzębski
93e6b51466 Expanded output of corruption error (close #272) 2018-07-10 08:42:15 +02:00
Paweł Jastrzębski
7904662f25 Let 7-Zip handle all archive operations 2018-07-10 08:09:04 +02:00
Paweł Jastrzębski
6792c2d366 Bump MAX_IMAGE_PIXELS (close #273) 2018-07-09 08:30:12 +02:00
Paweł Jastrzębski
ef4a91e44d WebP support (close #263) 2018-07-08 08:42:34 +02:00
Paweł Jastrzębski
a2a405e5f5 Merge branch 'Gokuroro-master' into dev 2018-04-21 09:26:48 +02:00
Hugo
a63a46a741 Use more standard __version__ rather than PILLOW_VERSION 2018-04-18 11:42:56 +03:00
Carlos Rosa
2591b53a09 Make file type error more informative (display file name) 2018-04-04 11:05:13 -03:00
Carlos Rosa
1c615ffc20 Make format checking more straightforward 2018-04-04 11:04:31 -03:00
Carlos Rosa
6eb05b3a8f Allow for multiple file arguments on startup 2018-04-04 10:12:30 -03:00
41 changed files with 321 additions and 2495 deletions

View File

@@ -2,11 +2,11 @@ matrix:
include: include:
- os: osx - os: osx
language: generic language: generic
osx_image: xcode6.4 osx_image: xcode9.2
before_install: before_install:
- brew update - brew update
- brew install python3 - brew upgrade python3
- brew uninstall node - brew uninstall node
- travis_wait 30 brew install node@6 - travis_wait 30 brew install node@6
- brew link node@6 --force --overwrite - brew link node@6 --force --overwrite
@@ -15,7 +15,7 @@ before_install:
install: install:
- pip3 install -r requirements.txt - pip3 install -r requirements.txt
- pip3 install certifi https://github.com/bjones1/pyinstaller/archive/pyqt5_fix.zip - pip3 install certifi https://github.com/pyinstaller/pyinstaller/archive/develop.zip
- npm install -g appdmg - npm install -g appdmg
script: python3 setup.py build_binary script: python3 setup.py build_binary

View File

@@ -1,4 +1,14 @@
# CHANGELOG # CHANGELOG
#### 5.5.1:
* Fixes some stability issues
#### 5.5.0:
* Added support for WebP format
* Added profiles for Kindle Paperwhite 4 and Kobo Forma
* All archives are now handled by 7z
* Removed MCD support
* Fixed multiple smaller issues
#### 5.4.5: #### 5.4.5:
* Fixed EPUB output for non-Kindle devices * Fixed EPUB output for non-Kindle devices

View File

@@ -1,7 +1,7 @@
ISC LICENSE ISC LICENSE
Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com> Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
Copyright (c) 2013-2018 Paweł Jastrzębski <pawelj@iosphe.re> Copyright (c) 2013-2019 Paweł Jastrzębski <pawelj@iosphe.re>
Permission to use, copy, modify, and/or distribute this software for Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the any purpose with or without fee is hereby granted, provided that the

View File

@@ -28,11 +28,8 @@ If you find **KCC** valuable you can consider donating to the authors:
## BINARY RELEASES ## BINARY RELEASES
You can find the latest released binary at the following links: You can find the latest released binary at the following links:
- **[Windows](http://kcc.iosphe.re/Windows/) (64-bit only)** - **[Windows](http://kcc.iosphe.re/Windows/) (64-bit only)**
- **[macOS](http://kcc.iosphe.re/OSX/) (10.10+)** - **[macOS](http://kcc.iosphe.re/OSX/) (10.12+)**
- **Linux:** - **Linux:** Currently unavailable.
- [Ubuntu 17.10](http://kcc.iosphe.re/LinuxArtful/)
- [Ubuntu 16.04 / Debian 9](http://kcc.iosphe.re/LinuxXenial/)
- [Ubuntu 14.04 / Debian 8](http://kcc.iosphe.re/LinuxTrusty/)
## PYPI ## PYPI
**KCC** is also available on PyPI. **KCC** is also available on PyPI.
@@ -51,21 +48,20 @@ Following software is required to run Linux version of **KCC** and/or bare sourc
On Debian based distributions these two commands should install all needed dependencies: On Debian based distributions these two commands should install all needed dependencies:
``` ```
sudo apt-get install python3 python3-dev python3-pip libpng-dev libjpeg-dev p7zip-full unrar sudo apt-get install python3 python3-dev python3-pip libpng-dev libjpeg-dev p7zip-full
sudo pip3 install --upgrade pillow python-slugify psutil pyqt5 raven sudo pip3 install --upgrade pillow python-slugify psutil pyqt5 raven
``` ```
### Optional dependencies ### 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)* - [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)* - [7z](http://www.7-zip.org/download.html) *(For CBZ/ZIP, CBR/RAR, 7z/CB7 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, GIF or WebP files
- CBZ, ZIP - CBZ, ZIP *(With `7z` executable)*
- CBR, RAR *(With `unrar` executable)* - CBR, RAR *(With `7z` executable)*
- CB7, 7Z *(With `7za` executable)* - CB7, 7Z *(With `7z` executable)*
- PDF *(Only extracting JPG images)* - PDF *(Only extracting JPG images)*
## USAGE ## USAGE
@@ -87,7 +83,7 @@ Options:
-p PROFILE, --profile=PROFILE -p PROFILE, --profile=PROFILE
Device profile (Available options: K1, K2, K34, K578, Device profile (Available options: K1, K2, K34, K578,
KDX, KPW, KV, KO, KoMT, KoG, KoGHD, KoA, KoAHD, KoAH2O, KDX, KPW, KV, KO, KoMT, KoG, KoGHD, KoA, KoAHD, KoAH2O,
KoAO) [Default=KV] KoAO, KoF) [Default=KV]
-m, --manga-style Manga style (right-to-left reading and splitting) -m, --manga-style Manga style (right-to-left reading and splitting)
-q, --hq Try to increase the quality of magnification -q, --hq Try to increase the quality of magnification
-2, --two-panel Display two not four panels in Panel View mode -2, --two-panel Display two not four panels in Panel View mode
@@ -160,29 +156,28 @@ This script born as a cross-platform alternative to `KindleComicParser` by **Dc5
The app relies and includes the following scripts: The app relies and includes the following scripts:
- `DualMetaFix` script by **K. Hendricks**. Released with GPL-3 License. - `DualMetaFix` script by **K. Hendricks**. Released with GPL-3 License.
- `rarfile.py` script &copy; 2005-2014 **Marko Kreen** <markokr@gmail.com>. Released with ISC License.
- `image.py` class from **Alex Yatskov**'s [Mangle](https://github.com/FooSoft/mangle/) with subsequent [proDOOMman](https://github.com/proDOOMman/Mangle)'s and [Birua](https://github.com/Birua/Mangle)'s patches. - `image.py` class from **Alex Yatskov**'s [Mangle](https://github.com/FooSoft/mangle/) with subsequent [proDOOMman](https://github.com/proDOOMman/Mangle)'s and [Birua](https://github.com/Birua/Mangle)'s patches.
- Icon is by **Nikolay Verin** ([http://ncrow.deviantart.com/](http://ncrow.deviantart.com/)) and released under [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/) License. - Icon is by **Nikolay Verin** ([http://ncrow.deviantart.com/](http://ncrow.deviantart.com/)) and released under [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/) License.
## SAMPLE FILES CREATED BY KCC ## SAMPLE FILES CREATED BY KCC
* [Kindle Oasis 2](http://kcc.iosphe.re/Samples/Ubunchu!-KO.mobi) * [Kindle Oasis 2](http://kcc.iosphe.re/Samples/Ubunchu!-KO.mobi)
* [Kindle Paperwhite 3 / Voyage / Oasis](http://kcc.iosphe.re/Samples/Ubunchu!-KV.mobi) * [Kindle Paperwhite 3 / 4 / Voyage / Oasis](http://kcc.iosphe.re/Samples/Ubunchu!-KV.mobi)
* [Kindle Paperwhite 1 / 2](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!-K578.mobi) * [Kindle](http://kcc.iosphe.re/Samples/Ubunchu!-K578.mobi)
* [Kobo Aura](http://kcc.iosphe.re/Samples/Ubunchu-KoA.kepub.epub) * [Kobo Aura](http://kcc.iosphe.re/Samples/Ubunchu-KoA.kepub.epub)
* [Kobo Aura HD](http://kcc.iosphe.re/Samples/Ubunchu-KoAHD.kepub.epub) * [Kobo Aura HD](http://kcc.iosphe.re/Samples/Ubunchu-KoAHD.kepub.epub)
* [Kobo Aura H2O](http://kcc.iosphe.re/Samples/Ubunchu-KoAH2O.kepub.epub) * [Kobo Aura H2O](http://kcc.iosphe.re/Samples/Ubunchu-KoAH2O.kepub.epub)
* [Kobo Aura ONE](http://kcc.iosphe.re/Samples/Ubunchu-KoAO.kepub.epub) * [Kobo Aura ONE](http://kcc.iosphe.re/Samples/Ubunchu-KoAO.kepub.epub)
* [Kobo Forma](http://kcc.iosphe.re/Samples/Ubunchu-KoF.kepub.epub)
## PRIVACY ## PRIVACY
**KCC** is initiating internet connections in three cases: **KCC** is initiating internet connections in two cases:
* During startup - Version check * During startup - Version check.
* When MCD metadata are used - Cover download * When error occurs - Automatic reporting on Windows and MacOS.
* When error occurs - Automatic reporting
## 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).
## COPYRIGHT ## COPYRIGHT
Copyright (c) 2012-2018 Ciro Mattia Gonano and Paweł Jastrzębski. Copyright (c) 2012-2019 Ciro Mattia Gonano and Paweł Jastrzębski.
**KCC** is released under ISC LICENSE; see LICENSE.txt for further details. **KCC** is released under ISC LICENSE; see LICENSE.txt for further details.

View File

@@ -1,11 +1,11 @@
environment: environment:
PYTHON: "C:\\Python36-x64" PYTHON: "C:\\Python37-x64"
install: install:
- set PATH="%PYTHON%\\Scripts";"C:\\Program Files (x86)\\Inno Setup 5";%PATH% - set PATH="%PYTHON%\\Scripts";"C:\\Program Files (x86)\\Inno Setup 5";%PATH%
- "%PYTHON%\\python.exe -m pip install --upgrade pip setuptools wheel" - "%PYTHON%\\python.exe -m pip install --upgrade pip setuptools wheel"
- "%PYTHON%\\python.exe -m pip install -r requirements.txt" - "%PYTHON%\\python.exe -m pip install -r requirements.txt"
- "%PYTHON%\\python.exe -m pip install certifi https://github.com/bjones1/pyinstaller/archive/pyqt5_fix.zip" - "%PYTHON%\\python.exe -m pip install certifi https://github.com/pyinstaller/pyinstaller/archive/develop.zip"
- nuget install secure-file -ExcludeVersion - nuget install secure-file -ExcludeVersion
- secure-file\tools\secure-file -decrypt other\windows\sentry.py.enc -out kindlecomicconverter\sentry.py -secret %ENCRYPTION% - secure-file\tools\secure-file -decrypt other\windows\sentry.py.enc -out kindlecomicconverter\sentry.py -secret %ENCRYPTION%

View File

@@ -112,19 +112,6 @@
<item row="6" column="1"> <item row="6" column="1">
<widget class="QLineEdit" name="coloristLine"/> <widget class="QLineEdit" name="coloristLine"/>
</item> </item>
<item row="7" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/ciromattia/kcc/wiki/Manga-Cover-Database-support&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;MUid:&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QLineEdit" name="muidLine"/>
</item>
</layout> </layout>
</widget> </widget>
</item> </item>

View File

@@ -2,7 +2,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com> # Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
# Copyright (c) 2013-2018 Pawel Jastrzebski <pawelj@iosphe.re> # Copyright (c) 2013-2019 Pawel Jastrzebski <pawelj@iosphe.re>
# #
# Permission to use, copy, modify, and/or distribute this software for # Permission to use, copy, modify, and/or distribute this software for
# any purpose with or without fee is hereby granted, provided that the # any purpose with or without fee is hereby granted, provided that the
@@ -23,9 +23,10 @@ if sys.version_info[0] != 3:
print('ERROR: This is Python 3 script!') print('ERROR: This is Python 3 script!')
exit(1) exit(1)
from multiprocessing import freeze_support from multiprocessing import freeze_support, set_start_method
from kindlecomicconverter.startup import startC2E from kindlecomicconverter.startup import startC2E
if __name__ == "__main__": if __name__ == "__main__":
set_start_method('spawn')
freeze_support() freeze_support()
startC2E() startC2E()

View File

@@ -2,7 +2,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com> # Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
# Copyright (c) 2013-2018 Pawel Jastrzebski <pawelj@iosphe.re> # Copyright (c) 2013-2019 Pawel Jastrzebski <pawelj@iosphe.re>
# #
# Permission to use, copy, modify, and/or distribute this software for # Permission to use, copy, modify, and/or distribute this software for
# any purpose with or without fee is hereby granted, provided that the # any purpose with or without fee is hereby granted, provided that the
@@ -23,9 +23,10 @@ if sys.version_info[0] != 3:
print('ERROR: This is Python 3 script!') print('ERROR: This is Python 3 script!')
exit(1) exit(1)
from multiprocessing import freeze_support from multiprocessing import freeze_support, set_start_method
from kindlecomicconverter.startup import startC2P from kindlecomicconverter.startup import startC2P
if __name__ == "__main__": if __name__ == "__main__":
set_start_method('spawn')
freeze_support() freeze_support()
startC2P() startC2P()

View File

@@ -1,5 +1,5 @@
#define MyAppName "Kindle Comic Converter" #define MyAppName "Kindle Comic Converter"
#define MyAppVersion "5.4.5" #define MyAppVersion "5.5.1"
#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"
@@ -12,7 +12,7 @@ AppPublisher={#MyAppPublisher}
AppPublisherURL={#MyAppURL} AppPublisherURL={#MyAppURL}
AppSupportURL={#MyAppURL} AppSupportURL={#MyAppURL}
AppUpdatesURL={#MyAppURL} AppUpdatesURL={#MyAppURL}
AppCopyright=Copyright (C) 2012-2018 Ciro Mattia Gonano and Paweł Jastrzębski AppCopyright=Copyright (C) 2012-2019 Ciro Mattia Gonano and Paweł Jastrzębski
ArchitecturesAllowed=x64 ArchitecturesAllowed=x64
DefaultDirName={pf}\{#MyAppName} DefaultDirName={pf}\{#MyAppName}
DefaultGroupName={#MyAppName} DefaultGroupName={#MyAppName}
@@ -47,8 +47,8 @@ Name: "CB7association"; Description: "CB7"; GroupDescription: "File associations
Source: "dist\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion Source: "dist\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion
Source: "LICENSE.txt"; DestDir: "{app}"; Flags: ignoreversion solidbreak Source: "LICENSE.txt"; DestDir: "{app}"; Flags: ignoreversion solidbreak
Source: "other\windows\Additional-LICENSE.txt"; DestDir: "{app}"; Flags: ignoreversion Source: "other\windows\Additional-LICENSE.txt"; DestDir: "{app}"; Flags: ignoreversion
Source: "other\windows\UnRAR.exe"; DestDir: "{app}"; Flags: ignoreversion Source: "other\windows\7z.exe"; DestDir: "{app}"; Flags: ignoreversion
Source: "other\windows\7za.exe"; DestDir: "{app}"; Flags: ignoreversion Source: "other\windows\7z.dll"; DestDir: "{app}"; Flags: ignoreversion
[Icons] [Icons]
Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}" Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"

10
kcc.py
View File

@@ -2,7 +2,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com> # Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
# Copyright (c) 2013-2018 Pawel Jastrzebski <pawelj@iosphe.re> # Copyright (c) 2013-2019 Pawel Jastrzebski <pawelj@iosphe.re>
# #
# Permission to use, copy, modify, and/or distribute this software for # Permission to use, copy, modify, and/or distribute this software for
# any purpose with or without fee is hereby granted, provided that the # any purpose with or without fee is hereby granted, provided that the
@@ -28,11 +28,12 @@ import os
if sys.platform.startswith('darwin'): if sys.platform.startswith('darwin'):
if getattr(sys, 'frozen', False): if getattr(sys, 'frozen', False):
os.environ['PATH'] = os.path.dirname(os.path.abspath(sys.executable)) + \ os.environ['PATH'] = os.path.dirname(os.path.abspath(sys.executable)) + \
'/../Resources:/usr/local/bin:/usr/bin:/bin' '/../Resources:/usr/local/bin:/usr/bin:/bin'
os.chdir(os.path.dirname(os.path.abspath(sys.executable)) + '/../Resources')
os.system('defaults write com.kindlecomicconverter.KindleComicConverter ApplePersistenceIgnoreState YES') os.system('defaults write com.kindlecomicconverter.KindleComicConverter ApplePersistenceIgnoreState YES')
os.system('defaults write com.kindlecomicconverter.KindleComicConverter NSInitialToolTipDelay -int 1000') os.system('defaults write com.kindlecomicconverter.KindleComicConverter NSInitialToolTipDelay -int 1000')
else: else:
os.environ['PATH'] = os.path.dirname(os.path.abspath(__file__)) + '/other/osx/:' + os.environ['PATH'] os.chdir(os.path.dirname(os.path.abspath(__file__)))
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)))
@@ -46,10 +47,11 @@ if getattr(sys, 'frozen', False):
except ImportError: except ImportError:
pass pass
from multiprocessing import freeze_support from multiprocessing import freeze_support, set_start_method
from kindlecomicconverter.startup import start from kindlecomicconverter.startup import start
if __name__ == "__main__": if __name__ == "__main__":
set_start_method('spawn')
freeze_support() freeze_support()
start() start()

View File

@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com> # Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
# Copyright (c) 2013-2018 Pawel Jastrzebski <pawelj@iosphe.re> # Copyright (c) 2013-2019 Pawel Jastrzebski <pawelj@iosphe.re>
# #
# Permission to use, copy, modify, and/or distribute this software for # Permission to use, copy, modify, and/or distribute this software for
# any purpose with or without fee is hereby granted, provided that the # any purpose with or without fee is hereby granted, provided that the
@@ -22,8 +22,9 @@ import sys
from urllib.parse import unquote from urllib.parse import unquote
from urllib.request import urlopen, urlretrieve, Request from urllib.request import urlopen, urlretrieve, Request
from time import sleep from time import sleep
from shutil import move from shutil import move, rmtree
from subprocess import STDOUT, PIPE from subprocess import STDOUT, PIPE
# noinspection PyUnresolvedReferences
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
from xml.sax.saxutils import escape from xml.sax.saxutils import escape
@@ -31,7 +32,8 @@ from psutil import Popen, Process
from copy import copy from copy import copy
from distutils.version import StrictVersion from distutils.version import StrictVersion
from raven import Client from raven import Client
from .shared import md5Checksum, HTMLStripper, sanitizeTrace from tempfile import gettempdir
from .shared import md5Checksum, HTMLStripper, sanitizeTrace, walkLevel
from . import __version__ from . import __version__
from . import comic2ebook from . import comic2ebook
from . import metadata from . import metadata
@@ -173,7 +175,8 @@ class VersionThread(QtCore.QThread):
move(path[0], path[0] + '.exe') move(path[0], path[0] + '.exe')
MW.hideProgressBar.emit() MW.hideProgressBar.emit()
MW.modeConvert.emit(1) MW.modeConvert.emit(1)
Popen(path[0] + '.exe /SP- /silent /noicons', stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True) Popen(path[0] + '.exe /SP- /silent /noicons', stdout=PIPE, stderr=STDOUT, stdin=PIPE, close_fds=True,
shell=True)
MW.forceShutdown.emit() MW.forceShutdown.emit()
except Exception: except Exception:
MW.addMessage.emit('Failed to download the update!', 'warning', False) MW.addMessage.emit('Failed to download the update!', 'warning', False)
@@ -238,6 +241,7 @@ class WorkerThread(QtCore.QThread):
MW.addTrayMessage.emit('Conversion interrupted.', 'Critical') MW.addTrayMessage.emit('Conversion interrupted.', 'Critical')
MW.modeConvert.emit(1) MW.modeConvert.emit(1)
# noinspection PyUnboundLocalVariable
def run(self): def run(self):
MW.modeConvert.emit(0) MW.modeConvert.emit(0)
@@ -477,20 +481,11 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
if self.needClean: if self.needClean:
self.needClean = False self.needClean = False
GUI.jobList.clear() GUI.jobList.clear()
if self.UnRAR: if self.sevenzip:
if self.sevenza: fnames = QtWidgets.QFileDialog.getOpenFileNames(MW, 'Select file', self.lastPath,
fnames = QtWidgets.QFileDialog.getOpenFileNames(MW, 'Select file', self.lastPath, 'Comic (*.cbz *.cbr *.cb7 *.zip *.rar *.7z *.pdf)')
'Comic (*.cbz *.cbr *.cb7 *.zip *.rar *.7z *.pdf)')
else:
fnames = QtWidgets.QFileDialog.getOpenFileNames(MW, 'Select file', self.lastPath,
'Comic (*.cbz *.cbr *.zip *.rar *.pdf)')
else: else:
if self.sevenza: fnames = QtWidgets.QFileDialog.getOpenFileNames(MW, 'Select file', self.lastPath, 'Comic (*.pdf)')
fnames = QtWidgets.QFileDialog.getOpenFileNames(MW, 'Select file', self.lastPath,
'Comic (*.cbz *.cb7 *.zip *.7z *.pdf)')
else:
fnames = QtWidgets.QFileDialog.getOpenFileNames(MW, 'Select file', self.lastPath,
'Comic (*.cbz *.zip *.pdf)')
for fname in fnames[0]: for fname in fnames[0]:
if fname != '': if fname != '':
if sys.platform.startswith('win'): if sys.platform.startswith('win'):
@@ -509,20 +504,12 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
sname = sname.replace('/', '\\') sname = sname.replace('/', '\\')
self.lastPath = os.path.abspath(sname) self.lastPath = os.path.abspath(sname)
else: else:
if self.UnRAR: if self.sevenzip:
if self.sevenza: fname = QtWidgets.QFileDialog.getOpenFileName(MW, 'Select file', self.lastPath,
fname = QtWidgets.QFileDialog.getOpenFileName(MW, 'Select file', self.lastPath, 'Comic (*.cbz *.cbr *.cb7)')
'Comic (*.cbz *.cbr *.cb7)')
else:
fname = QtWidgets.QFileDialog.getOpenFileName(MW, 'Select file', self.lastPath,
'Comic (*.cbz *.cbr)')
else: else:
if self.sevenza: fname = ['']
fname = QtWidgets.QFileDialog.getOpenFileName(MW, 'Select file', self.lastPath, self.showDialog("Editor is disabled due to a lack of 7z.", 'error')
'Comic (*.cbz *.cb7)')
else:
fname = QtWidgets.QFileDialog.getOpenFileName(MW, 'Select file', self.lastPath,
'Comic (*.cbz)')
if fname[0] != '': if fname[0] != '':
if sys.platform.startswith('win'): if sys.platform.startswith('win'):
sname = fname[0].replace('/', '\\') sname = fname[0].replace('/', '\\')
@@ -812,16 +799,9 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
if self.needClean: if self.needClean:
self.needClean = False self.needClean = False
GUI.jobList.clear() GUI.jobList.clear()
if self.UnRAR: formats = ['.pdf']
if self.sevenza: if self.sevenzip:
formats = ['.cbz', '.cbr', '.cb7', '.zip', '.rar', '.7z', '.pdf'] formats.extend(['.cb7', '.7z', '.cbz', '.zip', '.cbr', '.rar'])
else:
formats = ['.cbz', '.cbr', '.zip', '.rar', '.pdf']
else:
if self.sevenza:
formats = ['.cbz', '.cb7', '.zip', '.7z', '.pdf']
else:
formats = ['.cbz', '.zip', '.pdf']
if os.path.isdir(message): if os.path.isdir(message):
GUI.jobList.addItem(message) GUI.jobList.addItem(message)
GUI.jobList.scrollToBottom() GUI.jobList.scrollToBottom()
@@ -831,7 +811,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
GUI.jobList.addItem(message) GUI.jobList.addItem(message)
GUI.jobList.scrollToBottom() GUI.jobList.scrollToBottom()
else: else:
self.addMessage('This file type is unsupported!', 'error') self.addMessage('Unsupported file type for ' + message, 'error')
def dragAndDrop(self, e): def dragAndDrop(self, e):
e.accept() e.accept()
@@ -858,7 +838,8 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
except Exception: except Exception:
pass pass
kindleGenExitCode = Popen('kindlegen -locale en', stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True) kindleGenExitCode = Popen('kindlegen -locale en', stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
if kindleGenExitCode.wait() == 0: kindleGenExitCode.communicate()
if kindleGenExitCode.returncode == 0:
self.kindleGen = True self.kindleGen = True
versionCheck = Popen('kindlegen -locale en', stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True) versionCheck = Popen('kindlegen -locale en', stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
for line in versionCheck.stdout: for line in versionCheck.stdout:
@@ -909,6 +890,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
self.targetDirectory = '' self.targetDirectory = ''
self.sentry = Client(release=__version__) self.sentry = Client(release=__version__)
if sys.platform.startswith('win'): if sys.platform.startswith('win'):
# noinspection PyUnresolvedReferences
from psutil import BELOW_NORMAL_PRIORITY_CLASS from psutil import BELOW_NORMAL_PRIORITY_CLASS
self.p = Process(os.getpid()) self.p = Process(os.getpid())
self.p.nice(BELOW_NORMAL_PRIORITY_CLASS) self.p.nice(BELOW_NORMAL_PRIORITY_CLASS)
@@ -934,8 +916,8 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
'DefaultUpscale': True, 'Label': 'KV'}, 'DefaultUpscale': True, 'Label': 'KV'},
"Kindle Voyage": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, "Kindle Voyage": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': True, 'Label': 'KV'}, 'DefaultUpscale': True, 'Label': 'KV'},
"Kindle PW 3": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, "Kindle PW 3/4": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': True, 'Label': 'KV'}, 'DefaultUpscale': True, 'Label': 'KV'},
"Kindle PW 1/2": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, "Kindle PW 1/2": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': False, 'Label': 'KPW'}, 'DefaultUpscale': False, 'Label': 'KPW'},
"Kindle": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, "Kindle": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
@@ -956,6 +938,8 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
'DefaultUpscale': True, 'Label': 'KoAH2O'}, 'DefaultUpscale': True, 'Label': 'KoAH2O'},
"Kobo Aura ONE": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1, "Kobo Aura ONE": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1,
'DefaultUpscale': True, 'Label': 'KoAO'}, 'DefaultUpscale': True, 'Label': 'KoAO'},
"Kobo Forma": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 1,
'DefaultUpscale': True, 'Label': 'KoF'},
"Other": {'PVOptions': False, 'ForceExpert': True, 'DefaultFormat': 1, "Other": {'PVOptions': False, 'ForceExpert': True, 'DefaultFormat': 1,
'DefaultUpscale': False, 'Label': 'OTHER'}, 'DefaultUpscale': False, 'Label': 'OTHER'},
"Kindle 1": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 0, "Kindle 1": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 0,
@@ -971,10 +955,11 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
"Kindle Oasis 2", "Kindle Oasis 2",
"Kindle Oasis", "Kindle Oasis",
"Kindle Voyage", "Kindle Voyage",
"Kindle PW 3", "Kindle PW 3/4",
"Kindle PW 1/2", "Kindle PW 1/2",
"Kindle", "Kindle",
"Separator", "Separator",
"Kobo Forma",
"Kobo Aura ONE", "Kobo Aura ONE",
"Kobo Aura H2O", "Kobo Aura H2O",
"Kobo Aura HD", "Kobo Aura HD",
@@ -1007,22 +992,14 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
self.addMessage('Since you are a new user of <b>KCC</b> please see few ' self.addMessage('Since you are a new user of <b>KCC</b> please see few '
'<a href="https://github.com/ciromattia/kcc/wiki/Important-tips">important tips</a>.', '<a href="https://github.com/ciromattia/kcc/wiki/Important-tips">important tips</a>.',
'info') 'info')
rarExitCode = Popen('unrar', stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True) process = Popen('7z', stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
rarExitCode = rarExitCode.wait() process.communicate()
if rarExitCode == 0 or rarExitCode == 1 or rarExitCode == 7: if process.returncode == 0 or process.returncode == 7:
self.UnRAR = True self.sevenzip = True
else: else:
self.UnRAR = False self.sevenzip = False
self.addMessage('Cannot find <a href="http://www.rarlab.com/rar_add.htm">UnRAR</a>!' self.addMessage('Cannot find <a href="http://www.7-zip.org/download.html">7z</a>!'
' Processing of CBR/RAR files will be disabled.', 'warning') ' Processing of archives will be disabled.', 'warning')
sevenzaExitCode = Popen('7za', stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
sevenzaExitCode = sevenzaExitCode.wait()
if sevenzaExitCode == 0 or sevenzaExitCode == 7:
self.sevenza = True
else:
self.sevenza = False
self.addMessage('Cannot find <a href="http://www.7-zip.org/download.html">7za</a>!'
' Processing of CB7/7Z files will be disabled.', 'warning')
self.detectKindleGen(True) self.detectKindleGen(True)
APP.messageFromOtherInstance.connect(self.handleMessage) APP.messageFromOtherInstance.connect(self.handleMessage)
@@ -1092,6 +1069,12 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
self.versionCheck.start() self.versionCheck.start()
self.tray.show() self.tray.show()
# Cleanup unfisnished conversion
for root, dirs, _ in walkLevel(gettempdir(), 0):
for tempdir in dirs:
if tempdir.startswith('KCC-'):
rmtree(os.path.join(root, tempdir), True)
if self.windowSize != '0x0': if self.windowSize != '0x0':
x, y = self.windowSize.split('x') x, y = self.windowSize.split('x')
MW.resize(int(x), int(y)) MW.resize(int(x), int(y))
@@ -1103,7 +1086,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
class KCCGUI_MetaEditor(KCC_ui_editor.Ui_editorDialog): class KCCGUI_MetaEditor(KCC_ui_editor.Ui_editorDialog):
def loadData(self, file): def loadData(self, file):
self.parser = metadata.MetadataParser(file) self.parser = metadata.MetadataParser(file)
if self.parser.compressor == 'rar': if self.parser.format in ['RAR', 'RAR5']:
self.editorWidget.setEnabled(False) self.editorWidget.setEnabled(False)
self.okButton.setEnabled(False) self.okButton.setEnabled(False)
self.statusLabel.setText('CBR metadata are read-only.') self.statusLabel.setText('CBR metadata are read-only.')
@@ -1111,11 +1094,8 @@ class KCCGUI_MetaEditor(KCC_ui_editor.Ui_editorDialog):
self.editorWidget.setEnabled(True) self.editorWidget.setEnabled(True)
self.okButton.setEnabled(True) self.okButton.setEnabled(True)
self.statusLabel.setText('Separate authors with a comma.') self.statusLabel.setText('Separate authors with a comma.')
for field in (self.seriesLine, self.volumeLine, self.numberLine, self.muidLine): for field in (self.seriesLine, self.volumeLine, self.numberLine):
if field.objectName() == 'muidLine': field.setText(self.parser.data[field.objectName().capitalize()[:-4]])
field.setText(self.parser.data['MUid'])
else:
field.setText(self.parser.data[field.objectName().capitalize()[:-4]])
for field in (self.writerLine, self.pencillerLine, self.inkerLine, self.coloristLine): for field in (self.writerLine, self.pencillerLine, self.inkerLine, self.coloristLine):
field.setText(', '.join(self.parser.data[field.objectName().capitalize()[:-4] + 's'])) field.setText(', '.join(self.parser.data[field.objectName().capitalize()[:-4] + 's']))
if self.seriesLine.text() == '': if self.seriesLine.text() == '':
@@ -1125,12 +1105,9 @@ class KCCGUI_MetaEditor(KCC_ui_editor.Ui_editorDialog):
self.seriesLine.setText(file.split('\\')[-1].split('/')[-1].split('.')[0]) self.seriesLine.setText(file.split('\\')[-1].split('/')[-1].split('.')[0])
def saveData(self): def saveData(self):
for field in (self.volumeLine, self.numberLine, self.muidLine): for field in (self.volumeLine, self.numberLine):
if field.text().isnumeric() or self.cleanData(field.text()) == '': if field.text().isnumeric() or self.cleanData(field.text()) == '':
if field.objectName() == 'muidLine': self.parser.data[field.objectName().capitalize()[:-4]] = self.cleanData(field.text())
self.parser.data['MUid'] = self.cleanData(field.text())
else:
self.parser.data[field.objectName().capitalize()[:-4]] = self.cleanData(field.text())
else: else:
self.statusLabel.setText(field.objectName().capitalize()[:-4] + ' field must be a number.') self.statusLabel.setText(field.objectName().capitalize()[:-4] + ' field must be a number.')
break break

View File

@@ -66,13 +66,6 @@ class Ui_editorDialog(object):
self.coloristLine = QtWidgets.QLineEdit(self.editorWidget) self.coloristLine = QtWidgets.QLineEdit(self.editorWidget)
self.coloristLine.setObjectName("coloristLine") self.coloristLine.setObjectName("coloristLine")
self.gridLayout.addWidget(self.coloristLine, 6, 1, 1, 1) self.gridLayout.addWidget(self.coloristLine, 6, 1, 1, 1)
self.label_8 = QtWidgets.QLabel(self.editorWidget)
self.label_8.setOpenExternalLinks(True)
self.label_8.setObjectName("label_8")
self.gridLayout.addWidget(self.label_8, 7, 0, 1, 1)
self.muidLine = QtWidgets.QLineEdit(self.editorWidget)
self.muidLine.setObjectName("muidLine")
self.gridLayout.addWidget(self.muidLine, 7, 1, 1, 1)
self.verticalLayout.addWidget(self.editorWidget) self.verticalLayout.addWidget(self.editorWidget)
self.optionWidget = QtWidgets.QWidget(editorDialog) self.optionWidget = QtWidgets.QWidget(editorDialog)
self.optionWidget.setObjectName("optionWidget") self.optionWidget.setObjectName("optionWidget")
@@ -117,7 +110,6 @@ class Ui_editorDialog(object):
self.label_5.setText(_translate("editorDialog", "Penciller:")) self.label_5.setText(_translate("editorDialog", "Penciller:"))
self.label_6.setText(_translate("editorDialog", "Inker:")) self.label_6.setText(_translate("editorDialog", "Inker:"))
self.label_7.setText(_translate("editorDialog", "Colorist:")) self.label_7.setText(_translate("editorDialog", "Colorist:"))
self.label_8.setText(_translate("editorDialog", "<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>"))
self.okButton.setText(_translate("editorDialog", "Save")) self.okButton.setText(_translate("editorDialog", "Save"))
self.cancelButton.setText(_translate("editorDialog", "Cancel")) self.cancelButton.setText(_translate("editorDialog", "Cancel"))

View File

@@ -1,4 +1,4 @@
__version__ = '5.4.5' __version__ = '5.5.1'
__license__ = 'ISC' __license__ = 'ISC'
__copyright__ = '2012-2018, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>' __copyright__ = '2012-2019, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'

View File

@@ -1,89 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
# Copyright (c) 2013-2018 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 zipfile import is_zipfile, ZipFile
from subprocess import STDOUT, PIPE
from psutil import Popen
from shutil import move
from . import rarfile
from .shared import check7ZFile as is_7zfile
class CBxArchive:
def __init__(self, fname):
self.fname = fname
if is_zipfile(fname):
self.compressor = 'zip'
elif rarfile.is_rarfile(fname):
self.compressor = 'rar'
elif is_7zfile(fname):
self.compressor = '7z'
else:
self.compressor = None
def isCbxFile(self):
return self.compressor is not None
def extractCBZ(self, targetdir):
cbzFile = ZipFile(self.fname)
filelist = []
for f in cbzFile.namelist():
if f.startswith('__MACOSX') or f.endswith('.DS_Store') or f.endswith('humbs.db'):
pass
elif f.endswith('/'):
os.makedirs(os.path.join(targetdir, f), exist_ok=True)
else:
filelist.append(f)
cbzFile.extractall(targetdir, filelist)
def extractCBR(self, targetdir):
cbrFile = rarfile.RarFile(self.fname)
cbrFile.extractall(targetdir)
for root, _, filenames in os.walk(targetdir):
for filename in filenames:
if filename.startswith('__MACOSX') or filename.endswith('.DS_Store') or filename.endswith('humbs.db'):
os.remove(os.path.join(root, filename))
def extractCB7(self, targetdir):
output = Popen('7za x "' + self.fname + '" -xr!__MACOSX -xr!.DS_Store -xr!thumbs.db -xr!Thumbs.db -o"' +
targetdir + '"', stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
extracted = False
for line in output.stdout:
if b"Everything is Ok" in line:
extracted = True
if not extracted:
raise OSError
def extract(self, targetdir):
if self.compressor == 'rar':
self.extractCBR(targetdir)
elif self.compressor == 'zip':
self.extractCBZ(targetdir)
elif self.compressor == '7z':
self.extractCB7(targetdir)
adir = os.listdir(targetdir)
if 'ComicInfo.xml' in adir:
adir.remove('ComicInfo.xml')
if len(adir) == 1 and os.path.isdir(os.path.join(targetdir, adir[0])):
for f in os.listdir(os.path.join(targetdir, adir[0])):
move(os.path.join(targetdir, adir[0], f), targetdir)
os.rmdir(os.path.join(targetdir, adir[0]))
return targetdir

View File

@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com> # Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
# Copyright (c) 2013-2018 Pawel Jastrzebski <pawelj@iosphe.re> # Copyright (c) 2013-2019 Pawel Jastrzebski <pawelj@iosphe.re>
# #
# Permission to use, copy, modify, and/or distribute this software for # Permission to use, copy, modify, and/or distribute this software for
# any purpose with or without fee is hereby granted, provided that the # any purpose with or without fee is hereby granted, provided that the
@@ -22,9 +22,7 @@ import os
import sys import sys
from time import strftime, gmtime from time import strftime, gmtime
from copy import copy from copy import copy
from glob import glob from glob import glob, escape
from json import loads
from urllib.request import Request, urlopen
from re import 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
@@ -37,7 +35,7 @@ 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, disk_usage from psutil import Popen, virtual_memory, disk_usage
from html import escape from html import escape as hescape
try: try:
from PyQt5 import QtCore from PyQt5 import QtCore
except ImportError: except ImportError:
@@ -45,7 +43,7 @@ except ImportError:
from .shared import md5Checksum, getImageFileName, walkSort, walkLevel, sanitizeTrace from .shared import md5Checksum, getImageFileName, walkSort, walkLevel, sanitizeTrace
from . import comic2panel from . import comic2panel
from . import image from . import image
from . import cbxarchive from . import comicarchive
from . import pdfjpgextract from . import pdfjpgextract
from . import dualmetafix from . import dualmetafix
from . import metadata from . import metadata
@@ -61,7 +59,7 @@ def main(argv=None):
parser.print_help() parser.print_help()
return 0 return 0
if sys.platform.startswith('win'): if sys.platform.startswith('win'):
sources = set([source for arg in args for source in glob(arg)]) sources = set([source for arg in args for source in glob(escape(arg))])
else: else:
sources = set(args) sources = set(args)
if len(sources) == 0: if len(sources) == 0:
@@ -112,7 +110,7 @@ def buildHTML(path, imgfile, imgfilepath):
"<!DOCTYPE html>\n", "<!DOCTYPE html>\n",
"<html xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:epub=\"http://www.idpf.org/2007/ops\">\n", "<html xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:epub=\"http://www.idpf.org/2007/ops\">\n",
"<head>\n", "<head>\n",
"<title>", escape(filename[0]), "</title>\n", "<title>", hescape(filename[0]), "</title>\n",
"<link href=\"", "../" * (backref - 1), "style.css\" type=\"text/css\" rel=\"stylesheet\"/>\n", "<link href=\"", "../" * (backref - 1), "style.css\" type=\"text/css\" rel=\"stylesheet\"/>\n",
"<meta name=\"viewport\" " "<meta name=\"viewport\" "
"content=\"width=" + str(imgsize[0]) + ", height=" + str(imgsize[1]) + "\"/>\n" "content=\"width=" + str(imgsize[0]) + ", height=" + str(imgsize[1]) + "\"/>\n"
@@ -210,7 +208,7 @@ def buildNCX(dstdir, title, chapters, chapternames):
"<meta name=\"dtb:maxPageNumber\" content=\"0\"/>\n", "<meta name=\"dtb:maxPageNumber\" content=\"0\"/>\n",
"<meta name=\"generated\" content=\"true\"/>\n", "<meta name=\"generated\" content=\"true\"/>\n",
"</head>\n", "</head>\n",
"<docTitle><text>", escape(title), "</text></docTitle>\n", "<docTitle><text>", hescape(title), "</text></docTitle>\n",
"<navMap>\n"]) "<navMap>\n"])
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('\\\\')
@@ -222,7 +220,7 @@ def buildNCX(dstdir, title, chapters, chapternames):
elif os.path.basename(folder) != "Text": elif os.path.basename(folder) != "Text":
title = chapternames[os.path.basename(folder)] title = chapternames[os.path.basename(folder)]
f.write("<navPoint id=\"" + navID + "\"><navLabel><text>" + f.write("<navPoint id=\"" + navID + "\"><navLabel><text>" +
escape(title) + "</text></navLabel><content src=\"" + filename[0].replace("\\", "/") + hescape(title) + "</text></navLabel><content src=\"" + filename[0].replace("\\", "/") +
".xhtml\"/></navPoint>\n") ".xhtml\"/></navPoint>\n")
f.write("</navMap>\n</ncx>") f.write("</navMap>\n</ncx>")
f.close() f.close()
@@ -235,7 +233,7 @@ def buildNAV(dstdir, title, chapters, chapternames):
"<!DOCTYPE html>\n", "<!DOCTYPE html>\n",
"<html xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:epub=\"http://www.idpf.org/2007/ops\">\n", "<html xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:epub=\"http://www.idpf.org/2007/ops\">\n",
"<head>\n", "<head>\n",
"<title>" + escape(title) + "</title>\n", "<title>" + hescape(title) + "</title>\n",
"<meta charset=\"utf-8\"/>\n", "<meta charset=\"utf-8\"/>\n",
"</head>\n", "</head>\n",
"<body>\n", "<body>\n",
@@ -248,7 +246,7 @@ def buildNAV(dstdir, title, chapters, chapternames):
title = chapternames[chapter[1]] title = chapternames[chapter[1]]
elif os.path.basename(folder) != "Text": elif os.path.basename(folder) != "Text":
title = chapternames[os.path.basename(folder)] title = chapternames[os.path.basename(folder)]
f.write("<li><a href=\"" + filename[0].replace("\\", "/") + ".xhtml\">" + escape(title) + "</a></li>\n") f.write("<li><a href=\"" + filename[0].replace("\\", "/") + ".xhtml\">" + hescape(title) + "</a></li>\n")
f.writelines(["</ol>\n", f.writelines(["</ol>\n",
"</nav>\n", "</nav>\n",
"<nav epub:type=\"page-list\">\n", "<nav epub:type=\"page-list\">\n",
@@ -260,7 +258,7 @@ def buildNAV(dstdir, title, chapters, chapternames):
title = chapternames[chapter[1]] title = chapternames[chapter[1]]
elif os.path.basename(folder) != "Text": elif os.path.basename(folder) != "Text":
title = chapternames[os.path.basename(folder)] title = chapternames[os.path.basename(folder)]
f.write("<li><a href=\"" + filename[0].replace("\\", "/") + ".xhtml\">" + escape(title) + "</a></li>\n") f.write("<li><a href=\"" + filename[0].replace("\\", "/") + ".xhtml\">" + hescape(title) + "</a></li>\n")
f.write("</ol>\n</nav>\n</body>\n</html>") f.write("</ol>\n</nav>\n</body>\n</html>")
f.close() f.close()
@@ -278,7 +276,7 @@ def buildOPF(dstdir, title, filelist, cover=None):
"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>", hescape(title), "</dc:title>\n",
"<dc:language>en-US</dc:language>\n", "<dc:language>en-US</dc:language>\n",
"<dc:identifier id=\"BookID\">urn: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:contributor id=\"contributor\">KindleComicConverter-" + __version__ + "</dc:contributor>\n"])
@@ -597,16 +595,12 @@ def getWorkFolder(afile):
raise UserWarning("Failed to extract images from PDF file.") raise UserWarning("Failed to extract images from PDF file.")
else: else:
workdir = mkdtemp('', 'KCC-') workdir = mkdtemp('', 'KCC-')
cbx = cbxarchive.CBxArchive(afile) try:
if cbx.isCbxFile(): cbx = comicarchive.ComicArchive(afile)
try: path = cbx.extract(workdir)
path = cbx.extract(workdir) except OSError as e:
except Exception:
rmtree(workdir, True)
raise UserWarning("Failed to extract archive.")
else:
rmtree(workdir, True) rmtree(workdir, True)
raise UserWarning("Failed to detect archive format.") raise UserWarning(e.strerror)
else: else:
raise UserWarning("Failed to open source file/directory.") raise UserWarning("Failed to open source file/directory.")
sanitizePermissions(path) sanitizePermissions(path)
@@ -652,7 +646,6 @@ def getOutputFilename(srcpath, wantedname, ext, tomenumber):
def getComicInfo(path, originalpath): 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.chapters = [] options.chapters = []
options.summary = '' options.summary = ''
titleSuffix = '' titleSuffix = ''
@@ -673,7 +666,7 @@ def getComicInfo(path, originalpath):
options.authors = [] options.authors = []
if defaultTitle: if defaultTitle:
if xml.data['Series']: if xml.data['Series']:
options.title = escape(xml.data['Series']) options.title = hescape(xml.data['Series'])
if xml.data['Volume']: if xml.data['Volume']:
titleSuffix += ' V' + xml.data['Volume'].zfill(2) titleSuffix += ' V' + xml.data['Volume'].zfill(2)
if xml.data['Number']: if xml.data['Number']:
@@ -681,35 +674,19 @@ def getComicInfo(path, originalpath):
options.title += titleSuffix options.title += titleSuffix
for field in ['Writers', 'Pencillers', 'Inkers', 'Colorists']: for field in ['Writers', 'Pencillers', 'Inkers', 'Colorists']:
for person in xml.data[field]: for person in xml.data[field]:
options.authors.append(escape(person)) options.authors.append(hescape(person))
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']
if xml.data['MUid']:
options.remoteCovers = getCoversFromMCB(xml.data['MUid'])
if xml.data['Bookmarks']: if xml.data['Bookmarks']:
options.chapters = xml.data['Bookmarks'] options.chapters = xml.data['Bookmarks']
if xml.data['Summary']: if xml.data['Summary']:
options.summary = escape(xml.data['Summary']) options.summary = hescape(xml.data['Summary'])
os.remove(xmlPath) os.remove(xmlPath)
def getCoversFromMCB(mangaid):
covers = {}
try:
jsonRaw = urlopen(Request('http://mcd.iosphe.re/api/v1/series/' + mangaid + '/',
headers={'User-Agent': 'KindleComicConverter/' + __version__}))
jsonData = loads(jsonRaw.read().decode('utf-8'))
for volume in jsonData['Covers']['a']:
if volume['Side'] == 'front':
covers[int(volume['Volume'])] = volume['Raw']
except Exception:
return {}
return covers
def getDirectorySize(start_path='.'): def getDirectorySize(start_path='.'):
total_size = 0 total_size = 0
for dirpath, _, filenames in os.walk(start_path): for dirpath, _, filenames in os.walk(start_path):
@@ -790,7 +767,8 @@ def splitDirectory(path):
level = -1 level = -1
for root, _, files in os.walk(os.path.join(path, 'OEBPS', 'Images')): for root, _, files in os.walk(os.path.join(path, 'OEBPS', 'Images')):
for f in files: for f in files:
if f.endswith('.jpg') or f.endswith('.jpeg') or f.endswith('.png') or f.endswith('.gif'): if f.endswith('.jpg') or f.endswith('.jpeg') or f.endswith('.png') or f.endswith('.gif') or \
f.endswith('.webp'):
newLevel = os.path.join(root, f).replace(os.path.join(path, 'OEBPS', 'Images'), '').count(os.sep) newLevel = os.path.join(root, f).replace(os.path.join(path, 'OEBPS', 'Images'), '').count(os.sep)
if level != -1 and level != newLevel: if level != -1 and level != newLevel:
level = 0 level = 0
@@ -872,7 +850,7 @@ def detectCorruption(tmppath, orgpath):
if 'decoder' in str(err) and 'not available' in str(err): if 'decoder' in str(err) and 'not available' in str(err):
raise RuntimeError('Pillow was compiled without JPG and/or PNG decoder.') raise RuntimeError('Pillow was compiled without JPG and/or PNG decoder.')
else: else:
raise RuntimeError('Image file %s is corrupted.' % pathOrg) raise RuntimeError('Image file %s is corrupted. Error: %s' % (pathOrg, str(err)))
else: else:
os.remove(os.path.join(root, name)) os.remove(os.path.join(root, name))
if alreadyProcessed: if alreadyProcessed:
@@ -932,7 +910,7 @@ def makeParser():
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, K34, K578, KDX, KPW, KV, KO, KoMT, KoG," help="Device profile (Available options: K1, K2, K34, K578, KDX, KPW, KV, KO, KoMT, KoG,"
" KoGHD, KoA, KoAHD, KoAH2O, KoAO) [Default=KV]") " KoGHD, KoA, KoAHD, KoAH2O, KoAO, KoF) [Default=KV]")
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,
help="Manga style (right-to-left reading and splitting)") help="Manga style (right-to-left reading and splitting)")
mainOptions.add_option("-q", "--hq", action="store_true", dest="hq", default=False, mainOptions.add_option("-q", "--hq", action="store_true", dest="hq", default=False,
@@ -1053,21 +1031,17 @@ def checkOptions():
def checkTools(source): def checkTools(source):
source = source.upper() source = source.upper()
if source.endswith('.CBR') or source.endswith('.RAR'): if source.endswith('.CB7') or source.endswith('.7Z') or source.endswith('.RAR') or source.endswith('.CBR') or \
rarExitCode = Popen('unrar', stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True) source.endswith('.ZIP') or source.endswith('.CBZ'):
rarExitCode = rarExitCode.wait() process = Popen('7z', stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
if rarExitCode != 0 and rarExitCode != 1 and rarExitCode != 7: process.communicate()
print('ERROR: UnRAR is missing!') if process.returncode != 0 and process.returncode != 7:
exit(1) print('ERROR: 7z is missing!')
elif source.endswith('.CB7') or source.endswith('.7Z'):
sevenzaExitCode = Popen('7za', stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
sevenzaExitCode = sevenzaExitCode.wait()
if sevenzaExitCode != 0 and sevenzaExitCode != 7:
print('ERROR: 7za is missing!')
exit(1) exit(1)
if options.format == 'MOBI': if options.format == 'MOBI':
kindleGenExitCode = Popen('kindlegen -locale en', stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True) kindleGenExitCode = Popen('kindlegen -locale en', stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
if kindleGenExitCode.wait() != 0: kindleGenExitCode.communicate()
if kindleGenExitCode.returncode != 0:
print('ERROR: KindleGen is missing!') print('ERROR: KindleGen is missing!')
exit(1) exit(1)

View File

@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com> # Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
# Copyright (c) 2013-2018 Pawel Jastrzebski <pawelj@iosphe.re> # Copyright (c) 2013-2019 Pawel Jastrzebski <pawelj@iosphe.re>
# #
# Permission to use, copy, modify, and/or distribute this software for # Permission to use, copy, modify, and/or distribute this software for
# any purpose with or without fee is hereby granted, provided that the # any purpose with or without fee is hereby granted, provided that the

View File

@@ -0,0 +1,81 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
# Copyright (c) 2013-2019 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 psutil import Popen
from shutil import move
from subprocess import STDOUT, PIPE
from xml.dom.minidom import parseString
from xml.parsers.expat import ExpatError
class ComicArchive:
def __init__(self, filepath):
self.filepath = filepath
self.type = None
if not os.path.isfile(self.filepath):
raise OSError('File not found.')
process = Popen('7z l -y -p1 "' + self.filepath + '"', stderr=STDOUT, stdout=PIPE, stdin=PIPE, shell=True)
for line in process.stdout:
if b'Type =' in line:
self.type = line.rstrip().decode().split(' = ')[1].upper()
break
process.communicate()
if process.returncode != 0:
raise OSError('Archive is corrupted or encrypted.')
elif self.type not in ['7Z', 'RAR', 'RAR5', 'ZIP']:
raise OSError('Unsupported archive format.')
def extract(self, targetdir):
if not os.path.isdir(targetdir):
raise OSError('Target directory don\'t exist.')
process = Popen('7z x -y -xr!__MACOSX -xr!.DS_Store -xr!thumbs.db -xr!Thumbs.db -o"' + targetdir + '" "' +
self.filepath + '"', stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
process.communicate()
if process.returncode != 0:
raise OSError('Failed to extract archive.')
tdir = os.listdir(targetdir)
if 'ComicInfo.xml' in tdir:
tdir.remove('ComicInfo.xml')
if len(tdir) == 1 and os.path.isdir(os.path.join(targetdir, tdir[0])):
for f in os.listdir(os.path.join(targetdir, tdir[0])):
move(os.path.join(targetdir, tdir[0], f), targetdir)
os.rmdir(os.path.join(targetdir, tdir[0]))
return targetdir
def addFile(self, sourcefile):
if self.type in ['RAR', 'RAR5']:
raise NotImplementedError
process = Popen('7z a -y "' + self.filepath + '" "' + sourcefile + '"',
stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
process.communicate()
if process.returncode != 0:
raise OSError('Failed to add the file.')
def extractMetadata(self):
process = Popen('7z x -y -so "' + self.filepath + '" ComicInfo.xml',
stdout=PIPE, stderr=STDOUT, stdin=PIPE, shell=True)
xml = process.communicate()
if process.returncode != 0:
raise OSError('Failed to extract archive.')
try:
return parseString(xml[0])
except ExpatError:
return None

View File

@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Based on initial version of DualMetaFix. Copyright (C) 2013 Kevin Hendricks # Based on initial version of DualMetaFix. Copyright (C) 2013 Kevin Hendricks
# Changes for KCC Copyright (C) 2014-2018 Pawel Jastrzebski <pawelj@iosphe.re> # Changes for KCC Copyright (C) 2014-2019 Pawel Jastrzebski <pawelj@iosphe.re>
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
@@ -24,6 +24,7 @@ import shutil
class DualMetaFixException(Exception): class DualMetaFixException(Exception):
pass pass
# palm database offset constants # palm database offset constants
number_of_pdb_records = 76 number_of_pdb_records = 76
first_pdb_record = 78 first_pdb_record = 78

View File

@@ -4,7 +4,7 @@
# Copyright (C) 2011 Stanislav (proDOOMman) Kosolapov <prodoomman@gmail.com> # Copyright (C) 2011 Stanislav (proDOOMman) Kosolapov <prodoomman@gmail.com>
# Copyright (c) 2016 Alberto Planas <aplanas@gmail.com> # Copyright (c) 2016 Alberto Planas <aplanas@gmail.com>
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com> # Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
# Copyright (c) 2013-2018 Pawel Jastrzebski <pawelj@iosphe.re> # Copyright (c) 2013-2019 Pawel Jastrzebski <pawelj@iosphe.re>
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
@@ -20,12 +20,8 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import os import os
from io import BytesIO
from urllib.request import Request, urlopen
from urllib.parse import quote
from PIL import Image, ImageOps, ImageStat, ImageChops, ImageFilter from PIL import Image, ImageOps, ImageStat, ImageChops, ImageFilter
from .shared import md5Checksum from .shared import md5Checksum
from . import __version__
class ProfileData: class ProfileData:
@@ -86,7 +82,7 @@ class ProfileData:
'K578': ("Kindle", (600, 800), Palette16, 1.8), 'K578': ("Kindle", (600, 800), Palette16, 1.8),
'KDX': ("Kindle DX/DXG", (824, 1000), Palette16, 1.8), 'KDX': ("Kindle DX/DXG", (824, 1000), Palette16, 1.8),
'KPW': ("Kindle Paperwhite 1/2", (758, 1024), Palette16, 1.8), 'KPW': ("Kindle Paperwhite 1/2", (758, 1024), Palette16, 1.8),
'KV': ("Kindle Paperwhite 3/Voyage/Oasis", (1072, 1448), Palette16, 1.8), 'KV': ("Kindle Paperwhite 3/4/Voyage/Oasis", (1072, 1448), Palette16, 1.8),
'KO': ("Kindle Oasis 2", (1264, 1680), Palette16, 1.8), 'KO': ("Kindle Oasis 2", (1264, 1680), Palette16, 1.8),
'KoMT': ("Kobo Mini/Touch", (600, 800), Palette16, 1.8), 'KoMT': ("Kobo Mini/Touch", (600, 800), Palette16, 1.8),
'KoG': ("Kobo Glo", (768, 1024), Palette16, 1.8), 'KoG': ("Kobo Glo", (768, 1024), Palette16, 1.8),
@@ -95,12 +91,14 @@ class ProfileData:
'KoAHD': ("Kobo Aura HD", (1080, 1440), Palette16, 1.8), 'KoAHD': ("Kobo Aura HD", (1080, 1440), Palette16, 1.8),
'KoAH2O': ("Kobo Aura H2O", (1080, 1430), Palette16, 1.8), 'KoAH2O': ("Kobo Aura H2O", (1080, 1430), Palette16, 1.8),
'KoAO': ("Kobo Aura ONE", (1404, 1872), Palette16, 1.8), 'KoAO': ("Kobo Aura ONE", (1404, 1872), Palette16, 1.8),
'KoF': ("Kobo Forma", (1440, 1920), Palette16, 1.8),
'OTHER': ("Other", (0, 0), Palette16, 1.8), 'OTHER': ("Other", (0, 0), Palette16, 1.8),
} }
class ComicPageParser: class ComicPageParser:
def __init__(self, source, options): def __init__(self, source, options):
Image.MAX_IMAGE_PIXELS = int(2048 * 2048 * 2048 // 4 // 3)
self.opt = options self.opt = options
self.source = source self.source = source
self.size = self.opt.profileData[1] self.size = self.opt.profileData[1]
@@ -345,15 +343,7 @@ class Cover:
self.tomeid = 1 self.tomeid = 1
else: else:
self.tomeid = tomeid self.tomeid = tomeid
if self.tomeid in self.options.remoteCovers: self.image = Image.open(source)
try:
source = urlopen(Request(quote(self.options.remoteCovers[self.tomeid]).replace('%3A', ':', 1),
headers={'User-Agent': 'KindleComicConverter/' + __version__})).read()
self.image = Image.open(BytesIO(source))
except Exception:
self.image = Image.open(source)
else:
self.image = Image.open(source)
self.process() self.process()
def process(self): def process(self):

View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (c) 2013-2018 Pawel Jastrzebski <pawelj@iosphe.re> # Copyright (c) 2013-2019 Pawel Jastrzebski <pawelj@iosphe.re>
# #
# Permission to use, copy, modify, and/or distribute this software for # Permission to use, copy, modify, and/or distribute this software for
# any purpose with or without fee is hereby granted, provided that the # any purpose with or without fee is hereby granted, provided that the

View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (c) 2013-2018 Pawel Jastrzebski <pawelj@iosphe.re> # Copyright (c) 2013-2019 Pawel Jastrzebski <pawelj@iosphe.re>
# #
# Permission to use, copy, modify, and/or distribute this software for # Permission to use, copy, modify, and/or distribute this software for
# any purpose with or without fee is hereby granted, provided that the # any purpose with or without fee is hereby granted, provided that the
@@ -18,14 +18,9 @@
import os import os
from xml.dom.minidom import parse, Document 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 tempfile import mkdtemp
from shutil import rmtree from shutil import rmtree
from .shared import removeFromZIP, check7ZFile as is_7zfile from . import comicarchive
from . import rarfile
class MetadataParser: class MetadataParser:
@@ -39,50 +34,20 @@ class MetadataParser:
'Inkers': [], 'Inkers': [],
'Colorists': [], 'Colorists': [],
'Summary': '', 'Summary': '',
'MUid': '',
'Bookmarks': []} 'Bookmarks': []}
self.rawdata = None self.rawdata = None
self.compressor = None self.format = None
if self.source.endswith('.xml') and os.path.exists(self.source): if self.source.endswith('.xml') and os.path.exists(self.source):
self.rawdata = parse(self.source) self.rawdata = parse(self.source)
self.parseXML()
elif not self.source.endswith('.xml'): elif not self.source.endswith('.xml'):
if is_zipfile(self.source): try:
self.compressor = 'zip' cbx = comicarchive.ComicArchive(self.source)
with ZipFile(self.source) as zip_file: self.rawdata = cbx.extractMetadata()
for member in zip_file.namelist(): self.format = cbx.type
if member != 'ComicInfo.xml': except OSError as e:
continue raise UserWarning(e.strerror)
with zip_file.open(member) as xml_file: if self.rawdata:
self.rawdata = parse(xml_file) self.parseXML()
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, stdin=PIPE, 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): def parseXML(self):
if len(self.rawdata.getElementsByTagName('Series')) != 0: if len(self.rawdata.getElementsByTagName('Series')) != 0:
@@ -99,11 +64,6 @@ class MetadataParser:
self.data[field + 's'].append(person) self.data[field + 's'].append(person)
self.data[field + 's'] = list(set(self.data[field + 's'])) self.data[field + 's'] = list(set(self.data[field + 's']))
self.data[field + 's'].sort() 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: if len(self.rawdata.getElementsByTagName('Page')) != 0:
for page in self.rawdata.getElementsByTagName('Page'): for page in self.rawdata.getElementsByTagName('Page'):
if 'Bookmark' in page.attributes and 'Image' in page.attributes: if 'Bookmark' in page.attributes and 'Image' in page.attributes:
@@ -116,8 +76,7 @@ class MetadataParser:
for row in (['Series', self.data['Series']], ['Volume', self.data['Volume']], for row in (['Series', self.data['Series']], ['Volume', self.data['Volume']],
['Number', self.data['Number']], ['Writer', ', '.join(self.data['Writers'])], ['Number', self.data['Number']], ['Writer', ', '.join(self.data['Writers'])],
['Penciller', ', '.join(self.data['Pencillers'])], ['Inker', ', '.join(self.data['Inkers'])], ['Penciller', ', '.join(self.data['Pencillers'])], ['Inker', ', '.join(self.data['Inkers'])],
['Colorist', ', '.join(self.data['Colorists'])], ['Summary', self.data['Summary']], ['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]): if self.rawdata.getElementsByTagName(row[0]):
node = self.rawdata.getElementsByTagName(row[0])[0] node = self.rawdata.getElementsByTagName(row[0])[0]
if row[1]: if row[1]:
@@ -138,8 +97,7 @@ class MetadataParser:
for row in (['Series', self.data['Series']], ['Volume', self.data['Volume']], for row in (['Series', self.data['Series']], ['Volume', self.data['Volume']],
['Number', self.data['Number']], ['Writer', ', '.join(self.data['Writers'])], ['Number', self.data['Number']], ['Writer', ', '.join(self.data['Writers'])],
['Penciller', ', '.join(self.data['Pencillers'])], ['Inker', ', '.join(self.data['Inkers'])], ['Penciller', ', '.join(self.data['Pencillers'])], ['Inker', ', '.join(self.data['Inkers'])],
['Colorist', ', '.join(self.data['Colorists'])], ['Summary', self.data['Summary']], ['Colorist', ', '.join(self.data['Colorists'])], ['Summary', self.data['Summary']]):
['ScanInformation', 'MCD(' + self.data['MUid'] + ')' if self.data['MUid'] else '']):
if row[1]: if row[1]:
main = doc.createElement(row[0]) main = doc.createElement(row[0])
root.appendChild(main) root.appendChild(main)
@@ -154,20 +112,9 @@ class MetadataParser:
tmpXML = os.path.join(workdir, 'ComicInfo.xml') tmpXML = os.path.join(workdir, 'ComicInfo.xml')
with open(tmpXML, 'w', encoding='utf-8') as f: with open(tmpXML, 'w', encoding='utf-8') as f:
self.rawdata.writexml(f, encoding='utf-8') self.rawdata.writexml(f, encoding='utf-8')
if is_zipfile(self.source): try:
removeFromZIP(self.source, 'ComicInfo.xml') cbx = comicarchive.ComicArchive(self.source)
with ZipFile(self.source, mode='a', compression=ZIP_DEFLATED) as zip_file: cbx.addFile(tmpXML)
zip_file.write(tmpXML, arcname=tmpXML.split(os.sep)[-1]) except OSError as e:
elif rarfile.is_rarfile(self.source): raise UserWarning(e.strerror)
raise NotImplementedError
elif is_7zfile(self.source):
output = Popen('7za a "' + self.source + '" "' + tmpXML + '"',
stdout=PIPE, stderr=STDOUT, stdin=PIPE, 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) rmtree(workdir)

View File

@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com> # Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
# Copyright (c) 2013-2018 Pawel Jastrzebski <pawelj@iosphe.re> # Copyright (c) 2013-2019 Pawel Jastrzebski <pawelj@iosphe.re>
# #
# Based upon the code snippet by Ned Batchelder # Based upon the code snippet by Ned Batchelder
# (http://nedbatchelder.com/blog/200712/extracting_jpgs_from_pdfs.html) # (http://nedbatchelder.com/blog/200712/extracting_jpgs_from_pdfs.html)

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com> # Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
# Copyright (c) 2013-2018 Pawel Jastrzebski <pawelj@iosphe.re> # Copyright (c) 2013-2019 Pawel Jastrzebski <pawelj@iosphe.re>
# #
# Permission to use, copy, modify, and/or distribute this software for # Permission to use, copy, modify, and/or distribute this software for
# any purpose with or without fee is hereby granted, provided that the # any purpose with or without fee is hereby granted, provided that the
@@ -22,9 +22,6 @@ import os
from hashlib import md5 from hashlib import md5
from html.parser import HTMLParser from html.parser import HTMLParser
from distutils.version import StrictVersion from distutils.version import StrictVersion
from shutil import rmtree, copy
from tempfile import mkdtemp
from zipfile import ZipFile, ZIP_DEFLATED
from re import split from re import split
from traceback import format_tb from traceback import format_tb
@@ -50,7 +47,7 @@ class HTMLStripper(HTMLParser):
def getImageFileName(imgfile): def getImageFileName(imgfile):
name, ext = os.path.splitext(imgfile) name, ext = os.path.splitext(imgfile)
ext = ext.lower() ext = ext.lower()
if name.startswith('.') or (ext != '.png' and ext != '.jpg' and ext != '.jpeg' and ext != '.gif'): if name.startswith('.') or ext not in ['.png', '.jpg', '.jpeg', '.gif', '.webp']:
return None return None
return [name, ext] return [name, ext]
@@ -86,38 +83,19 @@ def md5Checksum(fpath):
return m.hexdigest() return m.hexdigest()
def check7ZFile(fpath):
with open(fpath, 'rb') as fh:
header = fh.read(6)
return header == b"7z\xbc\xaf'\x1c"
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): def sanitizeTrace(traceback):
return ''.join(format_tb(traceback))\ return ''.join(format_tb(traceback))\
.replace('C:/projects/kcc/', '') \ .replace('C:/projects/kcc/', '')\
.replace('c:/projects/kcc/', '') \ .replace('c:/projects/kcc/', '')\
.replace('C:/python36-x64/', '')\ .replace('C:/python37-x64/', '')\
.replace('c:/python36-x64/', '')\ .replace('c:/python37-x64/', '')\
.replace('C:\\projects\\kcc\\', '') \ .replace('C:\\projects\\kcc\\', '')\
.replace('c:\\projects\\kcc\\', '') \ .replace('c:\\projects\\kcc\\', '')\
.replace('C:\\python36-x64\\', '')\ .replace('C:\\python37-x64\\', '')\
.replace('c:\\python36-x64\\', '') .replace('c:\\python37-x64\\', '')
# noinspection PyUnresolvedReferences
def dependencyCheck(level): def dependencyCheck(level):
missing = [] missing = []
if level > 2: if level > 2:
@@ -145,11 +123,11 @@ def dependencyCheck(level):
except ImportError: except ImportError:
missing.append('python-slugify 1.2.1+') missing.append('python-slugify 1.2.1+')
try: try:
from PIL import PILLOW_VERSION as pillowVersion from PIL import __version__ as pillowVersion
if StrictVersion('4.0.0') > StrictVersion(pillowVersion): if StrictVersion('5.2.0') > StrictVersion(pillowVersion):
missing.append('Pillow 4.0.0+') missing.append('Pillow 5.2.0+')
except ImportError: except ImportError:
missing.append('Pillow 4.0.0+') missing.append('Pillow 5.2.0+')
if len(missing) > 0: if len(missing) > 0:
print('ERROR: ' + ', '.join(missing) + ' is not installed!') print('ERROR: ' + ', '.join(missing) + ' is not installed!')
exit(1) exit(1)

View File

@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com> # Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
# Copyright (c) 2013-2018 Pawel Jastrzebski <pawelj@iosphe.re> # Copyright (c) 2013-2019 Pawel Jastrzebski <pawelj@iosphe.re>
# #
# Permission to use, copy, modify, and/or distribute this software for # Permission to use, copy, modify, and/or distribute this software for
# any purpose with or without fee is hereby granted, provided that the # any purpose with or without fee is hereby granted, provided that the
@@ -30,15 +30,15 @@ def start():
os.environ['QT_AUTO_SCREEN_SCALE_FACTOR'] = "1" os.environ['QT_AUTO_SCREEN_SCALE_FACTOR'] = "1"
KCCAplication = KCC_gui.QApplicationMessaging(sys.argv) KCCAplication = KCC_gui.QApplicationMessaging(sys.argv)
if KCCAplication.isRunning(): if KCCAplication.isRunning():
if len(sys.argv) > 1: for i in range(1, len(sys.argv)):
KCCAplication.sendMessage(sys.argv[1]) KCCAplication.sendMessage(sys.argv[i])
else: else:
KCCAplication.sendMessage('ARISE') KCCAplication.sendMessage('ARISE')
else: else:
KCCWindow = KCC_gui.QMainWindowKCC() KCCWindow = KCC_gui.QMainWindowKCC()
KCCUI = KCC_gui.KCCGUI(KCCAplication, KCCWindow) KCCUI = KCC_gui.KCCGUI(KCCAplication, KCCWindow)
if len(sys.argv) > 1: for i in range(1, len(sys.argv)):
KCCUI.handleMessage(sys.argv[1]) KCCUI.handleMessage(sys.argv[i])
sys.exit(KCCAplication.exec_()) sys.exit(KCCAplication.exec_())

View File

@@ -1,4 +0,0 @@
kindlecomicconverter: binary-without-manpage usr/bin/kcc
kindlecomicconverter: wrong-name-for-changelog-of-native-package usr/share/doc/kindlecomicconverter/changelog.Debian.gz
kindlecomicconverter: file-missing-in-md5sums usr/share/doc/kindlecomicconverter/changelog.Debian.gz
kindlecomicconverter: hardening-no-relro usr/bin/kcc

View File

@@ -1,11 +0,0 @@
[Desktop Entry]
Type=Application
Version=1.0
Name=Kindle Comic Converter
GenericName=Kindle Comic Converter
Comment=Comic and Manga converter for e-book readers
Icon=/usr/share/kindlecomicconverter/comic2ebook.png
Exec=/usr/bin/kcc %f
Terminal=false
Categories=Graphics;
MimeType=application/zip;application/x-rar;application/x-7z-compressed;

Binary file not shown.

BIN
other/osx/7z Executable file

Binary file not shown.

BIN
other/osx/7z.so Normal file

Binary file not shown.

Binary file not shown.

View File

@@ -30,7 +30,7 @@
<key>CFBundleExecutable</key> <key>CFBundleExecutable</key>
<string>MacOS/Kindle Comic Converter</string> <string>MacOS/Kindle Comic Converter</string>
<key>CFBundleGetInfoString</key> <key>CFBundleGetInfoString</key>
<string>KindleComicConverter 5.4.5, written 2012-2018 by Ciro Mattia Gonano and Pawel Jastrzebski</string> <string>KindleComicConverter 5.5.1, written 2012-2019 by Ciro Mattia Gonano and Pawel Jastrzebski</string>
<key>CFBundleIconFile</key> <key>CFBundleIconFile</key>
<string>comic2ebook.icns</string> <string>comic2ebook.icns</string>
<key>CFBundleIdentifier</key> <key>CFBundleIdentifier</key>
@@ -42,11 +42,11 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>5.4.5</string> <string>5.5.1</string>
<key>CFBundleSignature</key> <key>CFBundleSignature</key>
<string>????</string> <string>????</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>5.4.5</string> <string>5.5.1</string>
<key>LSEnvironment</key> <key>LSEnvironment</key>
<dict> <dict>
<key>PATH</key> <key>PATH</key>
@@ -55,7 +55,7 @@
<key>LSHasLocalizedDisplayName</key> <key>LSHasLocalizedDisplayName</key>
<false/> <false/>
<key>LSMinimumSystemVersion</key> <key>LSMinimumSystemVersion</key>
<string>10.10.0</string> <string>10.12.0</string>
<key>NSAppleScriptEnabled</key> <key>NSAppleScriptEnabled</key>
<false/> <false/>
<key>NSHumanReadableCopyright</key> <key>NSHumanReadableCopyright</key>

BIN
other/osx/Rar.so Normal file

Binary file not shown.

Binary file not shown.

BIN
other/windows/7z.dll Normal file

Binary file not shown.

BIN
other/windows/7z.exe Normal file

Binary file not shown.

Binary file not shown.

View File

@@ -1,53 +1,19 @@
****** ***** ****** UnRAR - free utility for RAR archives
** ** ** ** ** ** ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
****** ******* ****** License for use and distribution of
** ** ** ** ** ** ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
** ** ** ** ** ** FREEWARE version
~~~~~~~~~~~~~~~~
The UnRAR utility is freeware. This means:
1. All copyrights to RAR and the utility UnRAR are exclusively
owned by the author - Alexander Roshal.
2. The UnRAR utility may be freely distributed. It is allowed
to distribute UnRAR inside of other software packages.
3. THE RAR ARCHIVER AND THE UnRAR UTILITY ARE DISTRIBUTED "AS IS".
NO WARRANTY OF ANY KIND IS EXPRESSED OR IMPLIED. YOU USE AT
YOUR OWN RISK. THE AUTHOR WILL NOT BE LIABLE FOR DATA LOSS,
DAMAGES, LOSS OF PROFITS OR ANY OTHER KIND OF LOSS WHILE USING
OR MISUSING THIS SOFTWARE.
4. Neither RAR binary code, WinRAR binary code, UnRAR source or UnRAR
binary code may be used or reverse engineered to re-create the RAR
compression algorithm, which is proprietary, without written
permission of the author.
5. If you don't agree with terms of the license you must remove
UnRAR files from your storage devices and cease to use the
utility.
Thank you for your interest in RAR and UnRAR.
Alexander L. Roshal
7-Zip 7-Zip
~~~~~ ~~~~~
License for use and distribution License for use and distribution
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7-Zip Copyright (C) 1999-2012 Igor Pavlov. 7-Zip Copyright (C) 1999-2018 Igor Pavlov.
Licenses for files are: The licenses for files are:
1) 7z.dll: GNU LGPL + unRAR restriction 1) 7z.dll:
2) All other files: GNU LGPL - The "GNU LGPL" as main license for most of the code
- The "GNU LGPL" with "unRAR license restriction" for some code
The GNU LGPL + unRAR restriction means that you must follow both - The "BSD 3-clause License" for some code
GNU LGPL rules and unRAR restriction rules. 2) All other files: the "GNU LGPL".
Redistributions in binary form must reproduce related license information from this file.
Note: Note:
You can use 7-Zip on any computer, including a computer in a commercial You can use 7-Zip on any computer, including a computer in a commercial
@@ -71,8 +37,41 @@
http://www.gnu.org/ http://www.gnu.org/
unRAR restriction
-----------------
BSD 3-clause License
--------------------
The "BSD 3-clause License" is used for the code in 7z.dll that implements LZFSE data decompression.
That code was derived from the code in the "LZFSE compression library" developed by Apple Inc,
that also uses the "BSD 3-clause License":
----
Copyright (c) 2015-2016, Apple Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder(s) nor the names of any contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----
unRAR license restriction
-------------------------
The decompression engine for RAR archives was developed using source The decompression engine for RAR archives was developed using source
code of unRAR program. code of unRAR program.

Binary file not shown.

View File

@@ -1,5 +1,5 @@
PyQt5>=5.6.0 PyQt5>=5.6.0
Pillow>=4.0.0 Pillow>=5.2.0
psutil>=5.0.0 psutil>=5.0.0
python-slugify>=1.2.1 python-slugify>=1.2.1,<3.0.0
raven>=6.0.0 raven>=6.0.0

View File

@@ -6,7 +6,7 @@ pip/pyinstaller build script for KCC.
Install as Python package: Install as Python package:
python3 setup.py install python3 setup.py install
Create EXE/APP/DEB: Create EXE/APP:
python3 setup.py build_binary python3 setup.py build_binary
""" """
@@ -37,35 +37,20 @@ class BuildBinaryCommand(distutils.cmd.Command):
VERSION = __version__ VERSION = __version__
if sys.platform == 'darwin': if sys.platform == 'darwin':
os.system('pyinstaller -y -F -i icons/comic2ebook.icns -n "Kindle Comic Converter" -w -s kcc.py') os.system('pyinstaller -y -F -i icons/comic2ebook.icns -n "Kindle Comic Converter" -w -s kcc.py')
shutil.copy('other/osx/7za', 'dist/Kindle Comic Converter.app/Contents/Resources') os.makedirs('dist/Kindle Comic Converter.app/Contents/Resources/Codecs')
shutil.copy('other/osx/unrar', 'dist/Kindle Comic Converter.app/Contents/Resources') shutil.copy('other/osx/7z', 'dist/Kindle Comic Converter.app/Contents/Resources')
shutil.copy('other/osx/7z.so', 'dist/Kindle Comic Converter.app/Contents/Resources')
shutil.copy('other/osx/Rar.so', 'dist/Kindle Comic Converter.app/Contents/Resources/Codecs')
shutil.copy('other/osx/Info.plist', 'dist/Kindle Comic Converter.app/Contents') shutil.copy('other/osx/Info.plist', 'dist/Kindle Comic Converter.app/Contents')
shutil.copy('LICENSE.txt', 'dist/Kindle Comic Converter.app/Contents/Resources') shutil.copy('LICENSE.txt', 'dist/Kindle Comic Converter.app/Contents/Resources')
shutil.copy('other/windows/Additional-LICENSE.txt', 'dist/Kindle Comic Converter.app/Contents/Resources') shutil.copy('other/windows/Additional-LICENSE.txt', 'dist/Kindle Comic Converter.app/Contents/Resources')
os.chmod('dist/Kindle Comic Converter.app/Contents/Resources/unrar', 0o777) os.chmod('dist/Kindle Comic Converter.app/Contents/Resources/7z', 0o777)
os.chmod('dist/Kindle Comic Converter.app/Contents/Resources/7za', 0o777)
os.system('appdmg kcc.json dist/KindleComicConverter_osx_' + VERSION + '.dmg') os.system('appdmg kcc.json dist/KindleComicConverter_osx_' + VERSION + '.dmg')
exit(0) exit(0)
elif sys.platform == 'win32': elif sys.platform == 'win32':
os.system('pyinstaller -y -F -i icons\comic2ebook.ico -n KCC -w --noupx kcc.py') os.system('pyinstaller -y -F -i icons\\comic2ebook.ico -n KCC -w --noupx kcc.py')
exit(0) exit(0)
else: else:
os.system('pyinstaller -y -F kcc.py')
os.system('mkdir -p dist/usr/bin dist/usr/share/applications dist/usr/share/doc/kindlecomicconverter '
'dist/usr/share/kindlecomicconverter dist/usr/share/lintian/overrides')
os.system('mv dist/kcc dist/usr/bin')
os.system('cp icons/comic2ebook.png dist/usr/share/kindlecomicconverter')
os.system('cp LICENSE.txt dist/usr/share/doc/kindlecomicconverter/copyright')
os.system('cp other/linux/kindlecomicconverter.desktop dist/usr/share/applications')
os.system('cp other/linux/kindlecomicconverter dist/usr/share/lintian/overrides')
os.chdir('dist')
os.system('fpm -f -s dir -t deb -n kindlecomicconverter -v ' + VERSION +
' -m "Pawel Jastrzebski <pawelj@iosphe.re>" --license "ISC" '
'--description "$(printf "Comic and Manga converter for e-book '
'readers.\nThis app allows you to transform your PNG, JPG, GIF, '
'CBZ, CBR and CB7 files\ninto EPUB or MOBI format e-books.")" '
'--url "https://kcc.iosphe.re/" --deb-priority "optional" --vendor "" '
'--category "graphics" -d "unrar | unrar-free" -d "p7zip-full" -d "libc6" usr')
exit(0) exit(0)
@@ -93,7 +78,7 @@ setuptools.setup(
packages=['kindlecomicconverter'], packages=['kindlecomicconverter'],
install_requires=[ install_requires=[
'PyQt5>=5.6.0', 'PyQt5>=5.6.0',
'Pillow>=4.0.0', 'Pillow>=5.2.0',
'psutil>=5.0.0', 'psutil>=5.0.0',
'python-slugify>=1.2.1', 'python-slugify>=1.2.1',
'raven>=6.0.0', 'raven>=6.0.0',