mirror of
https://github.com/ciromattia/kcc
synced 2026-04-16 22:18:51 +00:00
Compare commits
36 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bc98eecae9 | ||
|
|
e7a07377ef | ||
|
|
07ef11013a | ||
|
|
551fe6edbf | ||
|
|
dbf8a3ddbd | ||
|
|
8f9e230b62 | ||
|
|
36d9a4151e | ||
|
|
3e88dabd1a | ||
|
|
3b7d949128 | ||
|
|
68186285bd | ||
|
|
0abf620698 | ||
|
|
69d3bf3278 | ||
|
|
793992f408 | ||
|
|
f41d5327e0 | ||
|
|
6f960aa1d0 | ||
|
|
17c0a73f9f | ||
|
|
1fa5a5b19b | ||
|
|
e8d05c16aa | ||
|
|
74187b0d77 | ||
|
|
f39e0caad0 | ||
|
|
6299c45790 | ||
|
|
c7ebb230c2 | ||
|
|
3e4b729a30 | ||
|
|
16a1d9b45f | ||
|
|
b7aef324aa | ||
|
|
1a42730ea0 | ||
|
|
217a18b7b5 | ||
|
|
2ecbf7d2e9 | ||
|
|
a1cf9c5c7d | ||
|
|
32020d6b07 | ||
|
|
221f964f14 | ||
|
|
e9f0310b94 | ||
|
|
2fa90c9f59 | ||
|
|
cb0520dcab | ||
|
|
623bce6ae3 | ||
|
|
ad60894d19 |
15
.github/FUNDING.yml
vendored
Normal file
15
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
patreon: # Replace with a single Patreon username
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: eink_dude
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
|
||||
polar: # Replace with a single Polar username
|
||||
buy_me_a_coffee: # Replace with a single Buy Me a Coffee username
|
||||
thanks_dev: # Replace with a single thanks.dev username
|
||||
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
||||
@@ -27,7 +27,7 @@ on underpowered ereaders with small storage capacities.
|
||||
KCC avoids many common formatting issues (some of which occur [even on the Kindle Store](https://github.com/ciromattia/kcc/wiki/Kindle-Store-bad-formatting)), such as:
|
||||
1) faded black levels causing unneccessarily low contrast, which is hard to see and can cause eyestrain.
|
||||
2) unneccessary margins at the bottom of the screen
|
||||
3) Not utilizing the full 1860x2480 resolution of the 10" Kindle Scribe (feature in progress)
|
||||
3) Not utilizing the full 1860x2480 resolution of the 10" Kindle Scribe
|
||||
4) incorrect page turn direction for manga that's read right to left
|
||||
5) unaligned two page spreads in landscape, where pages are shifted over by 1
|
||||
|
||||
@@ -101,6 +101,8 @@ For flatpak, Docker, and AppImage versions, refer to the wiki: https://github.co
|
||||
## FAQ
|
||||
- All options have additional information in tooltips if you hover over the option.
|
||||
- To get the converted book onto your Kindle/Kobo, just drag and drop the mobi/kepub into the documents folder on your Kindle/Kobo via USB
|
||||
- Colors inverted?
|
||||
- Disable Kindle dark mode
|
||||
- Cannot connect Kindle Scribe or 2024+ Kindle to macOS
|
||||
- Use official MTP [Amazon USB File Transfer app](https://www.amazon.com/gp/help/customer/display.html/ref=hp_Connect_USB_MTP?nodeId=TCUBEdEkbIhK07ysFu)
|
||||
(no login required). Works much better than previously recommended Android File Transfer. Cannot run simutaneously with other transfer apps.
|
||||
@@ -252,6 +254,7 @@ OUTPUT SETTINGS:
|
||||
Split output into multiple files. 0: Don't split 1: Automatic mode 2: Consider every subdirectory as separate volume [Default=0]
|
||||
--spreadshift Shift first page to opposite side in landscape for two page spread alignment
|
||||
--norotate Do not rotate double page spreads in spread splitter option.
|
||||
--rotatefirst Put rotated spread first in spread splitter option.
|
||||
--reducerainbow Reduce rainbow effect on color eink by slightly blurring images
|
||||
|
||||
CUSTOM PROFILE:
|
||||
@@ -299,6 +302,8 @@ Then use the `gen_ui_files` scripts to autogenerate the python UI.
|
||||
|
||||
An example PR adding a new checkbox is here: https://github.com/ciromattia/kcc/pull/785
|
||||
|
||||
video of adding a new checkbox: https://youtu.be/g3I8DU74C7g
|
||||
|
||||
Do not use `git merge` to merge master from upstream,
|
||||
use the "Sync fork" button on your fork on GitHub in your branch
|
||||
to avoid weird looking merges in pull requests.
|
||||
|
||||
@@ -4,7 +4,7 @@ channels:
|
||||
- defaults
|
||||
dependencies:
|
||||
- python=3.11
|
||||
- Pillow>=5.2.0
|
||||
- Pillow>=11.3.0
|
||||
- psutil>=5.9.5
|
||||
- python-slugify>=1.2.1
|
||||
- raven>=6.0.0
|
||||
|
||||
514
gui/KCC.ui
514
gui/KCC.ui
@@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>519</width>
|
||||
<height>572</height>
|
||||
<width>566</width>
|
||||
<height>573</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@@ -75,6 +75,12 @@
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="kofiButton">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>30</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Support me on Ko-fi</string>
|
||||
</property>
|
||||
@@ -131,27 +137,62 @@
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QPushButton" name="directoryButton">
|
||||
<item row="1" column="3">
|
||||
<widget class="QPushButton" name="convertButton">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>30</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p style='white-space:pre'>Add directory containing JPG, PNG or GIF files to queue.<br/><span style=" font-weight:600;">CBR, CBZ and CB7 files inside will not be processed!</span></p></body></html></string>
|
||||
<string><html><head/><body><p style='white-space:pre'>Shift+Click to select the output directory for this list.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Add image folder</string>
|
||||
<string>Convert</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="KCC.qrc">
|
||||
<normaloff>:/Other/icons/folder_new.png</normaloff>:/Other/icons/folder_new.png</iconset>
|
||||
<normaloff>:/Other/icons/convert.png</normaloff>:/Other/icons/convert.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="3">
|
||||
<widget class="QPushButton" name="clearButton">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>30</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Clear list</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="KCC.qrc">
|
||||
<normaloff>:/Other/icons/clear.png</normaloff>:/Other/icons/clear.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="deviceBox">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>28</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p style='white-space:pre'>Target device.</p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QPushButton" name="fileButton">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
@@ -171,20 +212,46 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QComboBox" name="deviceBox">
|
||||
<item row="0" column="5">
|
||||
<widget class="QPushButton" name="defaultOutputFolderButton">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>28</height>
|
||||
<height>30</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p style='white-space:pre'>Target device.</p></body></html></string>
|
||||
<string><html><head/><body><p>Use this to select the default output directory.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="KCC.qrc">
|
||||
<normaloff>:/Other/icons/folder_new.png</normaloff>:/Other/icons/folder_new.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="3">
|
||||
<item row="0" column="4">
|
||||
<widget class="QCheckBox" name="defaultOutputFolderBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p><span style=" font-weight:600; text-decoration: underline;">Unchecked - next to source<br/></span>Place output files next to source files</p><p><span style=" font-weight:600; text-decoration: underline;">Indeterminate - folder next to source<br/></span>Place output files in a folder next to source files</p><p><span style=" font-weight:600; text-decoration: underline;">Checked - Custom<br/></span>Place output files in custom directory specified by right button</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Output Folder</string>
|
||||
</property>
|
||||
<property name="tristate">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="4" colspan="2">
|
||||
<widget class="QComboBox" name="formatBox">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
@@ -197,55 +264,14 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QPushButton" name="convertButton">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>30</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p style='white-space:pre'>Shift+Click to select the output directory.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Convert</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="KCC.qrc">
|
||||
<normaloff>:/Other/icons/convert.png</normaloff>:/Other/icons/convert.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QPushButton" name="clearButton">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>30</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Clear list</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="KCC.qrc">
|
||||
<normaloff>:/Other/icons/clear.png</normaloff>:/Other/icons/clear.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
<zorder>directoryButton</zorder>
|
||||
<zorder>clearButton</zorder>
|
||||
<zorder>fileButton</zorder>
|
||||
<zorder>deviceBox</zorder>
|
||||
<zorder>convertButton</zorder>
|
||||
<zorder>formatBox</zorder>
|
||||
<zorder>defaultOutputFolderButton</zorder>
|
||||
<zorder>fileButton</zorder>
|
||||
<zorder>defaultOutputFolderBox</zorder>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="2">
|
||||
@@ -427,13 +453,13 @@
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="4" column="2">
|
||||
<widget class="QCheckBox" name="croppingBox">
|
||||
<item row="6" column="2">
|
||||
<widget class="QCheckBox" name="interPanelCropBox">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p><span style=" font-weight:600; text-decoration: underline;">Unchecked - Disabled</span></p><p>Disabled</p><p><span style=" font-weight:600; text-decoration: underline;">Indeterminate - Margins<br/></span>Margins</p><p><span style=" font-weight:600; text-decoration: underline;">Checked - Margins + page numbers<br/></span>Margins +page numbers</p></body></html></string>
|
||||
<string><html><head/><body><p><span style=" font-weight:600; text-decoration: underline;">Unchecked - Disabled<br/></span>Disabled</p><p><span style=" font-weight:600; text-decoration: underline;">Indeterminate - Horizontal<br/></span>Crop empty horizontal lines.</p><p><span style=" font-weight:600; text-decoration: underline;">Checked - Both<br/></span>Crop empty horizontal and vertical lines.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Cropping mode</string>
|
||||
<string>Inter-panel crop</string>
|
||||
</property>
|
||||
<property name="tristate">
|
||||
<bool>true</bool>
|
||||
@@ -450,6 +476,41 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLineEdit" name="authorEdit">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::FocusPolicy::ClickFocus</enum>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Default Author is KCC</string>
|
||||
</property>
|
||||
<property name="placeholderText">
|
||||
<string>Default Author</string>
|
||||
</property>
|
||||
<property name="clearButtonEnabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="2">
|
||||
<widget class="QCheckBox" name="croppingBox">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p><span style=" font-weight:600; text-decoration: underline;">Unchecked - Disabled</span></p><p>Disabled</p><p><span style=" font-weight:600; text-decoration: underline;">Indeterminate - Margins<br/></span>Margins</p><p><span style=" font-weight:600; text-decoration: underline;">Checked - Margins + page numbers<br/></span>Margins +page numbers</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Cropping mode</string>
|
||||
</property>
|
||||
<property name="tristate">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QCheckBox" name="webtoonBox">
|
||||
<property name="toolTip">
|
||||
@@ -460,6 +521,155 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="2">
|
||||
<widget class="QCheckBox" name="colorBox">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p style='white-space:pre'>Disable conversion to grayscale.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Color mode</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<widget class="QCheckBox" name="deleteBox">
|
||||
<property name="toolTip">
|
||||
<string>Delete input file(s) or directory. It's not recoverable!</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Delete input</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0">
|
||||
<widget class="QCheckBox" name="comicinfoTitleBox">
|
||||
<property name="toolTip">
|
||||
<string>Write Title from ComicInfo.xml</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>ComicInfo Title</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QCheckBox" name="qualityBox">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p style='white-space:pre'><span style=" font-weight:600; text-decoration: underline;">Unchecked - 4 panels<br/></span>Zoom each corner separately.</p><p style='white-space:pre'><span style=" font-weight:600; text-decoration: underline;">Indeterminate - 2 panels<br/></span>Zoom only the top and bottom of the page.</p><p><span style=" font-weight:600; text-decoration: underline;">Checked - 4 high-quality panels<br/></span>Zoom each corner separately. Try to increase the quality of magnification. Check wiki for more details.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Panel View 4/2/HQ</string>
|
||||
</property>
|
||||
<property name="tristate">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="2">
|
||||
<widget class="QCheckBox" name="reduceRainbowBox">
|
||||
<property name="toolTip">
|
||||
<string>Reduce rainbow effect on color eink by slightly blurring images</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Rainbow blur</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="1">
|
||||
<widget class="QCheckBox" name="noRotateBox">
|
||||
<property name="toolTip">
|
||||
<string>Do not rotate double page spreads in spread splitter option.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>No rotate</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<widget class="QCheckBox" name="fileFusionBox">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Combines all selected files into a single file. (Helpful for combining chapters into volumes.)</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>File Fusion</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="2">
|
||||
<widget class="QCheckBox" name="gammaBox">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p style='white-space:pre'>Disable automatic gamma correction.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Custom gamma</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QCheckBox" name="mozJpegBox">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p><span style=" font-weight:600; text-decoration: underline;">Unchecked - JPEG<br/></span>Use JPEG files</p><p><span style=" font-weight:600; text-decoration: underline;">Indeterminate - force PNG<br/></span>Create PNG files instead JPEG</p><p><span style=" font-weight:600; text-decoration: underline;">Checked - mozJpeg<br/></span>10-20% smaller JPEG file, with the same image quality, but processing time multiplied by 2</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>JPEG/PNG/mozJpeg</string>
|
||||
</property>
|
||||
<property name="tristate">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="2">
|
||||
<widget class="QCheckBox" name="disableProcessingBox">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p style='white-space:pre'>Do not process any image, ignore profile and processing options.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Disable processing</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="1">
|
||||
<widget class="QCheckBox" name="chunkSizeCheckBox">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p><span style=" font-weight:700; text-decoration: underline;">Unchecked<br/></span>Maximal output file size is 100 MB for Webtoon, 400 MB for others before split occurs.</p><p><span style=" font-weight:700; text-decoration: underline;">Checked</span><br/>Output file size specified in &quot;Chunk size MB&quot; before split occurs.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Chunk size</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QCheckBox" name="spreadShiftBox">
|
||||
<property name="toolTip">
|
||||
<string>Shift first page to opposite side in landscape for two page spread alignment</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Spread shift</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QCheckBox" name="upscaleBox">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p><span style=" font-weight:600; text-decoration: underline;">Unchecked - Nothing<br/></span>Images smaller than device resolution will not be resized.</p><p><span style=" font-weight:600; text-decoration: underline;">Indeterminate - Stretching<br/></span>Images smaller than device resolution will be resized. Aspect ratio will be not preserved.</p><p><span style=" font-weight:600; text-decoration: underline;">Checked - Upscaling<br/></span>Images smaller than device resolution will be resized. Aspect ratio will be preserved.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Stretch/Upscale</string>
|
||||
</property>
|
||||
<property name="tristate">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QCheckBox" name="outputSplit">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p style='white-space:pre'><span style=" font-weight:600; text-decoration: underline;">Unchecked - Automatic mode<br/></span>The output will be split automatically.</p><p style='white-space:pre'><span style=" font-weight:600; text-decoration: underline;">Checked - Volume mode<br/></span>Every subdirectory will be considered as a separate volume.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Output split</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QCheckBox" name="rotateBox">
|
||||
<property name="toolTip">
|
||||
@@ -486,62 +696,6 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="2">
|
||||
<widget class="QCheckBox" name="gammaBox">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p style='white-space:pre'>Disable automatic gamma correction.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Custom gamma</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="2">
|
||||
<widget class="QCheckBox" name="interPanelCropBox">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p><span style=" font-weight:600; text-decoration: underline;">Unchecked - Disabled<br/></span>Disabled</p><p><span style=" font-weight:600; text-decoration: underline;">Indeterminate - Horizontal<br/></span>Crop empty horizontal lines.</p><p><span style=" font-weight:600; text-decoration: underline;">Checked - Both<br/></span>Crop empty horizontal and vertical lines.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Inter-panel crop</string>
|
||||
</property>
|
||||
<property name="tristate">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="2">
|
||||
<widget class="QCheckBox" name="colorBox">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p style='white-space:pre'>Disable conversion to grayscale.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Color mode</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QCheckBox" name="qualityBox">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p style='white-space:pre'><span style=" font-weight:600; text-decoration: underline;">Unchecked - 4 panels<br/></span>Zoom each corner separately.</p><p style='white-space:pre'><span style=" font-weight:600; text-decoration: underline;">Indeterminate - 2 panels<br/></span>Zoom only the top and bottom of the page.</p><p><span style=" font-weight:600; text-decoration: underline;">Checked - 4 high-quality panels<br/></span>Zoom each corner separately. Try to increase the quality of magnification. Check wiki for more details.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Panel View 4/2/HQ</string>
|
||||
</property>
|
||||
<property name="tristate">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="2">
|
||||
<widget class="QCheckBox" name="disableProcessingBox">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p style='white-space:pre'>Do not process any image, ignore profile and processing options.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Disable processing</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QCheckBox" name="maximizeStrips">
|
||||
<property name="toolTip">
|
||||
@@ -552,131 +706,13 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLineEdit" name="authorEdit">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::FocusPolicy::ClickFocus</enum>
|
||||
</property>
|
||||
<item row="8" column="1">
|
||||
<widget class="QCheckBox" name="rotateFirstBox">
|
||||
<property name="toolTip">
|
||||
<string>Default Author is KCC</string>
|
||||
</property>
|
||||
<property name="placeholderText">
|
||||
<string>Default Author</string>
|
||||
</property>
|
||||
<property name="clearButtonEnabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<widget class="QCheckBox" name="deleteBox">
|
||||
<property name="toolTip">
|
||||
<string>Delete input file(s) or directory. It's not recoverable!</string>
|
||||
<string><html><head/><body><p>When the spread splitter option is partially checked,</p><p><span style=" font-weight:600; text-decoration: underline;">Unchecked - Rotate Last<br/></span>Put the rotated 2 page spread after the split spreads.</p><p><span style=" font-weight:600; text-decoration: underline;">Checked - Rotate First<br/></span>Put the rotated 2 page spread before the split spreads.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Delete input</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QCheckBox" name="mozJpegBox">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p><span style=" font-weight:600; text-decoration: underline;">Unchecked - JPEG<br/></span>Use JPEG files</p><p><span style=" font-weight:600; text-decoration: underline;">Indeterminate - force PNG<br/></span>Create PNG files instead JPEG</p><p><span style=" font-weight:600; text-decoration: underline;">Checked - mozJpeg<br/></span>10-20% smaller JPEG file, with the same image quality, but processing time multiplied by 2</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>JPEG/PNG/mozJpeg</string>
|
||||
</property>
|
||||
<property name="tristate">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QCheckBox" name="spreadShiftBox">
|
||||
<property name="toolTip">
|
||||
<string>Shift first page to opposite side in landscape for two page spread alignment</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Spread shift</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<widget class="QCheckBox" name="fileFusionBox">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Combines all selected files into a single file. (Helpful for combining chapters into volumes.)</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>File Fusion</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QCheckBox" name="upscaleBox">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p><span style=" font-weight:600; text-decoration: underline;">Unchecked - Nothing<br/></span>Images smaller than device resolution will not be resized.</p><p><span style=" font-weight:600; text-decoration: underline;">Indeterminate - Stretching<br/></span>Images smaller than device resolution will be resized. Aspect ratio will be not preserved.</p><p><span style=" font-weight:600; text-decoration: underline;">Checked - Upscaling<br/></span>Images smaller than device resolution will be resized. Aspect ratio will be preserved.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Stretch/Upscale</string>
|
||||
</property>
|
||||
<property name="tristate">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QCheckBox" name="outputSplit">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p style='white-space:pre'><span style=" font-weight:600; text-decoration: underline;">Unchecked - Automatic mode<br/></span>The output will be split automatically.</p><p style='white-space:pre'><span style=" font-weight:600; text-decoration: underline;">Checked - Volume mode<br/></span>Every subdirectory will be considered as a separate volume.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Output split</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="1">
|
||||
<widget class="QCheckBox" name="noRotateBox">
|
||||
<property name="toolTip">
|
||||
<string>Do not rotate double page spreads in spread splitter option.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>No rotate</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="2">
|
||||
<widget class="QCheckBox" name="reduceRainbowBox">
|
||||
<property name="toolTip">
|
||||
<string>Reduce rainbow effect on color eink by slightly blurring images</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Rainbow blur</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="1">
|
||||
<widget class="QCheckBox" name="chunkSizeCheckBox">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p><span style=" font-weight:700; text-decoration: underline;">Unchecked<br/></span>Maximal output file size is 100 MB for Webtoon, 400 MB for others before split occurs.</p><p><span style=" font-weight:700; text-decoration: underline;">Checked</span><br/>Output file size specified in &quot;Chunk size MB&quot; before split occurs.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Chunk size</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0">
|
||||
<widget class="QCheckBox" name="comicinfoTitleBox">
|
||||
<property name="toolTip">
|
||||
<string>Write Title from ComicInfo.xml</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>ComicInfo Title</string>
|
||||
<string>Rotate First</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -807,8 +843,6 @@
|
||||
<tabstops>
|
||||
<tabstop>convertButton</tabstop>
|
||||
<tabstop>clearButton</tabstop>
|
||||
<tabstop>directoryButton</tabstop>
|
||||
<tabstop>fileButton</tabstop>
|
||||
<tabstop>deviceBox</tabstop>
|
||||
<tabstop>formatBox</tabstop>
|
||||
<tabstop>mangaBox</tabstop>
|
||||
|
||||
@@ -11,7 +11,7 @@ a = Analysis(['kcc-c2e.py'],
|
||||
hiddenimports=['_cffi_backend'],
|
||||
hookspath=[],
|
||||
runtime_hooks=[],
|
||||
excludes=[],
|
||||
excludes=['pkg_resources'],
|
||||
win_no_prefer_redirects=False,
|
||||
win_private_assemblies=False,
|
||||
cipher=block_cipher,
|
||||
|
||||
@@ -11,7 +11,7 @@ a = Analysis(['kcc-c2p.py'],
|
||||
hiddenimports=['_cffi_backend'],
|
||||
hookspath=[],
|
||||
runtime_hooks=[],
|
||||
excludes=[],
|
||||
excludes=['pkg_resources'],
|
||||
win_no_prefer_redirects=False,
|
||||
win_private_assemblies=False,
|
||||
cipher=block_cipher,
|
||||
|
||||
2
kcc.spec
2
kcc.spec
@@ -11,7 +11,7 @@ a = Analysis(['kcc.py'],
|
||||
hiddenimports=['_cffi_backend'],
|
||||
hookspath=[],
|
||||
runtime_hooks=[],
|
||||
excludes=[],
|
||||
excludes=['pkg_resources'],
|
||||
win_no_prefer_redirects=False,
|
||||
win_private_assemblies=False,
|
||||
cipher=block_cipher,
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
# 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 itertools
|
||||
from pathlib import Path
|
||||
from PySide6.QtCore import (QSize, QUrl, Qt, Signal, QIODeviceBase, QEvent, QThread, QSettings)
|
||||
from PySide6.QtGui import (QColor, QIcon, QPixmap, QDesktopServices)
|
||||
from PySide6.QtWidgets import (QApplication, QLabel, QListWidgetItem, QMainWindow, QApplication, QSystemTrayIcon, QFileDialog, QMessageBox, QDialog)
|
||||
@@ -27,7 +30,7 @@ import sys
|
||||
from urllib.parse import unquote
|
||||
from time import sleep
|
||||
from shutil import move, rmtree
|
||||
from subprocess import STDOUT, PIPE
|
||||
from subprocess import STDOUT, PIPE, CalledProcessError
|
||||
|
||||
import requests
|
||||
from xml.sax.saxutils import escape
|
||||
@@ -37,10 +40,10 @@ from packaging.version import Version
|
||||
from raven import Client
|
||||
from tempfile import gettempdir
|
||||
|
||||
from .shared import HTMLStripper, available_archive_tools, sanitizeTrace, walkLevel, subprocess_run
|
||||
from .shared import HTMLStripper, sanitizeTrace, walkLevel, subprocess_run
|
||||
from .comicarchive import SEVENZIP, available_archive_tools
|
||||
from . import __version__
|
||||
from . import comic2ebook
|
||||
from . import image
|
||||
from . import metadata
|
||||
from . import kindle
|
||||
from . import KCC_ui
|
||||
@@ -275,6 +278,8 @@ class WorkerThread(QThread):
|
||||
options.filefusion = False
|
||||
if GUI.noRotateBox.isChecked():
|
||||
options.norotate = True
|
||||
if GUI.rotateFirstBox.isChecked():
|
||||
options.rotatefirst = True
|
||||
if GUI.mozJpegBox.checkState() == Qt.CheckState.PartiallyChecked:
|
||||
options.forcepng = True
|
||||
elif GUI.mozJpegBox.checkState() == Qt.CheckState.Checked:
|
||||
@@ -487,17 +492,33 @@ class SystemTrayIcon(QSystemTrayIcon):
|
||||
|
||||
|
||||
class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
def selectDir(self):
|
||||
if self.needClean:
|
||||
self.needClean = False
|
||||
GUI.jobList.clear()
|
||||
dname = QFileDialog.getExistingDirectory(MW, 'Select directory', self.lastPath)
|
||||
def selectDefaultOutputFolder(self):
|
||||
dname = QFileDialog.getExistingDirectory(MW, 'Select default output folder', self.defaultOutputFolder)
|
||||
if self.is_directory_on_kindle(dname):
|
||||
return
|
||||
if dname != '':
|
||||
if sys.platform.startswith('win'):
|
||||
dname = dname.replace('/', '\\')
|
||||
self.lastPath = os.path.abspath(os.path.join(dname, os.pardir))
|
||||
GUI.jobList.addItem(dname)
|
||||
GUI.jobList.scrollToBottom()
|
||||
GUI.defaultOutputFolder = dname
|
||||
|
||||
def is_directory_on_kindle(self, dname):
|
||||
path = Path(dname)
|
||||
for parent in itertools.chain([path], path.parents):
|
||||
if parent.name == 'documents' and parent.parent.joinpath('system').joinpath('thumbnails').is_dir():
|
||||
self.addMessage("Cannot select Kindle as output directory", 'error')
|
||||
return True
|
||||
|
||||
def selectOutputFolder(self):
|
||||
dname = QFileDialog.getExistingDirectory(MW, 'Select output directory', self.lastPath)
|
||||
if self.is_directory_on_kindle(dname):
|
||||
return
|
||||
if dname != '':
|
||||
if sys.platform.startswith('win'):
|
||||
dname = dname.replace('/', '\\')
|
||||
GUI.targetDirectory = dname
|
||||
else:
|
||||
GUI.targetDirectory = ''
|
||||
return GUI.targetDirectory
|
||||
|
||||
def selectFile(self):
|
||||
if self.needClean:
|
||||
@@ -585,7 +606,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
GUI.editorButton.setEnabled(status)
|
||||
GUI.wikiButton.setEnabled(status)
|
||||
GUI.deviceBox.setEnabled(status)
|
||||
GUI.directoryButton.setEnabled(status)
|
||||
GUI.defaultOutputFolderButton.setEnabled(status)
|
||||
GUI.clearButton.setEnabled(status)
|
||||
GUI.fileButton.setEnabled(status)
|
||||
GUI.formatBox.setEnabled(status)
|
||||
@@ -782,13 +803,10 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
self.worker.sync()
|
||||
else:
|
||||
if QApplication.keyboardModifiers() == Qt.KeyboardModifier.ShiftModifier:
|
||||
dname = QFileDialog.getExistingDirectory(MW, 'Select output directory', self.lastPath)
|
||||
if dname != '':
|
||||
if sys.platform.startswith('win'):
|
||||
dname = dname.replace('/', '\\')
|
||||
GUI.targetDirectory = dname
|
||||
else:
|
||||
GUI.targetDirectory = ''
|
||||
if not self.selectOutputFolder():
|
||||
return
|
||||
elif GUI.defaultOutputFolderBox.isChecked():
|
||||
self.targetDirectory = self.defaultOutputFolder
|
||||
else:
|
||||
GUI.targetDirectory = ''
|
||||
self.progress.start()
|
||||
@@ -799,6 +817,12 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
self.addMessage('No files selected! Please choose files to convert.', 'error')
|
||||
self.needClean = True
|
||||
return
|
||||
if GUI.defaultOutputFolderBox.checkState() == Qt.CheckState.PartiallyChecked:
|
||||
parent = Path(self.jobList.item(0).text()).parent
|
||||
target_path = parent.joinpath(f"{parent.name}")
|
||||
if not target_path.exists():
|
||||
target_path.mkdir()
|
||||
self.targetDirectory = str(target_path)
|
||||
if self.currentMode > 2 and (GUI.widthBox.value() == 0 or GUI.heightBox.value() == 0):
|
||||
GUI.jobList.clear()
|
||||
self.addMessage('Target resolution is not set!', 'error')
|
||||
@@ -830,6 +854,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
event.ignore()
|
||||
self.settings.setValue('settingsVersion', __version__)
|
||||
self.settings.setValue('lastPath', self.lastPath)
|
||||
self.settings.setValue('defaultOutputFolder', self.defaultOutputFolder)
|
||||
self.settings.setValue('lastDevice', GUI.deviceBox.currentIndex())
|
||||
self.settings.setValue('currentFormat', GUI.formatBox.currentIndex())
|
||||
self.settings.setValue('startNumber', self.startNumber + 1)
|
||||
@@ -856,7 +881,9 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
'deleteBox': GUI.deleteBox.checkState().value,
|
||||
'spreadShiftBox': GUI.spreadShiftBox.checkState().value,
|
||||
'fileFusionBox': GUI.fileFusionBox.checkState().value,
|
||||
'defaultOutputFolderBox': GUI.defaultOutputFolderBox.checkState().value,
|
||||
'noRotateBox': GUI.noRotateBox.checkState().value,
|
||||
'rotateFirstBox': GUI.rotateFirstBox.checkState().value,
|
||||
'maximizeStrips': GUI.maximizeStrips.checkState().value,
|
||||
'gammaSlider': float(self.gammaValue) * 100,
|
||||
'chunkSizeCheckBox': GUI.chunkSizeCheckBox.checkState().value,
|
||||
@@ -921,7 +948,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
self.addMessage('Your <a href="https://www.amazon.com/b?node=23496309011">KindleGen</a>'
|
||||
' is outdated! MOBI conversion might fail.', 'warning')
|
||||
break
|
||||
except FileNotFoundError:
|
||||
except (FileNotFoundError, CalledProcessError):
|
||||
self.kindleGen = False
|
||||
if startup:
|
||||
self.display_kindlegen_missing()
|
||||
@@ -937,6 +964,9 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
self.settings = QSettings('ciromattia', 'kcc')
|
||||
self.settingsVersion = self.settings.value('settingsVersion', '', type=str)
|
||||
self.lastPath = self.settings.value('lastPath', '', type=str)
|
||||
self.defaultOutputFolder = str(self.settings.value('defaultOutputFolder', '', type=str))
|
||||
if not os.path.exists(self.defaultOutputFolder):
|
||||
self.defaultOutputFolder = ''
|
||||
self.lastDevice = self.settings.value('lastDevice', 0, type=int)
|
||||
self.currentFormat = self.settings.value('currentFormat', 0, type=int)
|
||||
self.startNumber = self.settings.value('startNumber', 0, type=int)
|
||||
@@ -965,7 +995,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
if self.windowSize == '0x0':
|
||||
MW.resize(500, 500)
|
||||
elif sys.platform.startswith('darwin'):
|
||||
for element in ['editorButton', 'wikiButton', 'directoryButton', 'clearButton', 'fileButton', 'deviceBox',
|
||||
for element in ['editorButton', 'wikiButton', 'defaultOutputFolderButton', 'clearButton', 'fileButton', 'deviceBox',
|
||||
'convertButton', 'formatBox']:
|
||||
getattr(GUI, element).setMinimumSize(QSize(0, 0))
|
||||
GUI.gridLayout.setContentsMargins(-1, -1, -1, -1)
|
||||
@@ -1133,21 +1163,20 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
self.addMessage('<b>Welcome!</b>', 'info')
|
||||
self.addMessage('<b>Tip:</b> Hover mouse over options to see additional information in tooltips.', 'info')
|
||||
self.addMessage('<b>Tip:</b> You can drag and drop image folders or comic files/archives into this window to convert.', 'info')
|
||||
self.addMessage('<b>Tip:</b> Shift clicking the Convert button lets you select a custom output directory', 'info')
|
||||
if self.startNumber < 5:
|
||||
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>.',
|
||||
'info')
|
||||
|
||||
self.tar = 'tar' in available_archive_tools()
|
||||
self.sevenzip = '7z' in available_archive_tools()
|
||||
self.sevenzip = SEVENZIP in available_archive_tools()
|
||||
if not any([self.tar, self.sevenzip]):
|
||||
self.addMessage('<a href="https://github.com/ciromattia/kcc#7-zip">Install 7z (link)</a>'
|
||||
' to enable CBZ/CBR/ZIP/etc processing.', 'warning')
|
||||
self.detectKindleGen(True)
|
||||
|
||||
APP.messageFromOtherInstance.connect(self.handleMessage)
|
||||
GUI.directoryButton.clicked.connect(self.selectDir)
|
||||
GUI.defaultOutputFolderButton.clicked.connect(self.selectDefaultOutputFolder)
|
||||
GUI.clearButton.clicked.connect(self.clearJobs)
|
||||
GUI.fileButton.clicked.connect(self.selectFile)
|
||||
GUI.editorButton.clicked.connect(self.selectFileMetaEditor)
|
||||
|
||||
@@ -12228,7 +12228,7 @@ qt_resource_struct = b"\
|
||||
\x00\x00\x01\x1c\x00\x00\x00\x00\x00\x01\x00\x01P\xb1\
|
||||
\x00\x00\x01\x88;p\xbcJ\
|
||||
\x00\x00\x012\x00\x00\x00\x00\x00\x01\x00\x01yY\
|
||||
\x00\x00\x01\x97| \xdd\x8b\
|
||||
\x00\x00\x01\x97\xc9|\x88\xde\
|
||||
\x00\x00\x01V\x00\x00\x00\x00\x00\x01\x00\x01\x9d\x9a\
|
||||
\x00\x00\x01\x88;p\xbcI\
|
||||
\x00\x00\x01\xb8\x00\x00\x00\x00\x00\x01\x00\x01\xf6n\
|
||||
|
||||
@@ -26,7 +26,7 @@ class Ui_mainWindow(object):
|
||||
def setupUi(self, mainWindow):
|
||||
if not mainWindow.objectName():
|
||||
mainWindow.setObjectName(u"mainWindow")
|
||||
mainWindow.resize(519, 572)
|
||||
mainWindow.resize(566, 573)
|
||||
icon = QIcon()
|
||||
icon.addFile(u":/Icon/icons/comic2ebook.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
|
||||
mainWindow.setWindowIcon(icon)
|
||||
@@ -60,6 +60,7 @@ class Ui_mainWindow(object):
|
||||
|
||||
self.kofiButton = QPushButton(self.toolWidget)
|
||||
self.kofiButton.setObjectName(u"kofiButton")
|
||||
self.kofiButton.setMinimumSize(QSize(0, 30))
|
||||
icon2 = QIcon()
|
||||
icon2.addFile(u":/Other/icons/kofi_symbol.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
|
||||
self.kofiButton.setIcon(icon2)
|
||||
@@ -89,63 +90,75 @@ class Ui_mainWindow(object):
|
||||
self.gridLayout_4 = QGridLayout(self.buttonWidget)
|
||||
self.gridLayout_4.setObjectName(u"gridLayout_4")
|
||||
self.gridLayout_4.setContentsMargins(0, 0, 0, 0)
|
||||
self.directoryButton = QPushButton(self.buttonWidget)
|
||||
self.directoryButton.setObjectName(u"directoryButton")
|
||||
self.directoryButton.setMinimumSize(QSize(0, 30))
|
||||
icon4 = QIcon()
|
||||
icon4.addFile(u":/Other/icons/folder_new.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
|
||||
self.directoryButton.setIcon(icon4)
|
||||
|
||||
self.gridLayout_4.addWidget(self.directoryButton, 0, 0, 1, 1)
|
||||
|
||||
self.fileButton = QPushButton(self.buttonWidget)
|
||||
self.fileButton.setObjectName(u"fileButton")
|
||||
self.fileButton.setMinimumSize(QSize(0, 30))
|
||||
icon5 = QIcon()
|
||||
icon5.addFile(u":/Other/icons/document_new.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
|
||||
self.fileButton.setIcon(icon5)
|
||||
|
||||
self.gridLayout_4.addWidget(self.fileButton, 0, 3, 1, 1)
|
||||
|
||||
self.deviceBox = QComboBox(self.buttonWidget)
|
||||
self.deviceBox.setObjectName(u"deviceBox")
|
||||
self.deviceBox.setMinimumSize(QSize(0, 28))
|
||||
|
||||
self.gridLayout_4.addWidget(self.deviceBox, 1, 0, 1, 1)
|
||||
|
||||
self.formatBox = QComboBox(self.buttonWidget)
|
||||
self.formatBox.setObjectName(u"formatBox")
|
||||
self.formatBox.setMinimumSize(QSize(0, 28))
|
||||
|
||||
self.gridLayout_4.addWidget(self.formatBox, 1, 3, 1, 1)
|
||||
|
||||
self.convertButton = QPushButton(self.buttonWidget)
|
||||
self.convertButton.setObjectName(u"convertButton")
|
||||
self.convertButton.setMinimumSize(QSize(0, 30))
|
||||
font = QFont()
|
||||
font.setBold(True)
|
||||
self.convertButton.setFont(font)
|
||||
icon6 = QIcon()
|
||||
icon6.addFile(u":/Other/icons/convert.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
|
||||
self.convertButton.setIcon(icon6)
|
||||
icon4 = QIcon()
|
||||
icon4.addFile(u":/Other/icons/convert.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
|
||||
self.convertButton.setIcon(icon4)
|
||||
|
||||
self.gridLayout_4.addWidget(self.convertButton, 1, 2, 1, 1)
|
||||
self.gridLayout_4.addWidget(self.convertButton, 1, 3, 1, 1)
|
||||
|
||||
self.clearButton = QPushButton(self.buttonWidget)
|
||||
self.clearButton.setObjectName(u"clearButton")
|
||||
self.clearButton.setMinimumSize(QSize(0, 30))
|
||||
icon5 = QIcon()
|
||||
icon5.addFile(u":/Other/icons/clear.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
|
||||
self.clearButton.setIcon(icon5)
|
||||
|
||||
self.gridLayout_4.addWidget(self.clearButton, 0, 3, 1, 1)
|
||||
|
||||
self.deviceBox = QComboBox(self.buttonWidget)
|
||||
self.deviceBox.setObjectName(u"deviceBox")
|
||||
self.deviceBox.setMinimumSize(QSize(0, 28))
|
||||
|
||||
self.gridLayout_4.addWidget(self.deviceBox, 1, 1, 1, 1)
|
||||
|
||||
self.fileButton = QPushButton(self.buttonWidget)
|
||||
self.fileButton.setObjectName(u"fileButton")
|
||||
self.fileButton.setMinimumSize(QSize(0, 30))
|
||||
icon6 = QIcon()
|
||||
icon6.addFile(u":/Other/icons/document_new.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
|
||||
self.fileButton.setIcon(icon6)
|
||||
|
||||
self.gridLayout_4.addWidget(self.fileButton, 0, 1, 1, 1)
|
||||
|
||||
self.defaultOutputFolderButton = QPushButton(self.buttonWidget)
|
||||
self.defaultOutputFolderButton.setObjectName(u"defaultOutputFolderButton")
|
||||
self.defaultOutputFolderButton.setMinimumSize(QSize(0, 30))
|
||||
icon7 = QIcon()
|
||||
icon7.addFile(u":/Other/icons/clear.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
|
||||
self.clearButton.setIcon(icon7)
|
||||
icon7.addFile(u":/Other/icons/folder_new.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
|
||||
self.defaultOutputFolderButton.setIcon(icon7)
|
||||
|
||||
self.gridLayout_4.addWidget(self.clearButton, 0, 2, 1, 1)
|
||||
self.gridLayout_4.addWidget(self.defaultOutputFolderButton, 0, 5, 1, 1)
|
||||
|
||||
self.defaultOutputFolderBox = QCheckBox(self.buttonWidget)
|
||||
self.defaultOutputFolderBox.setObjectName(u"defaultOutputFolderBox")
|
||||
sizePolicy1 = QSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed)
|
||||
sizePolicy1.setHorizontalStretch(0)
|
||||
sizePolicy1.setVerticalStretch(0)
|
||||
sizePolicy1.setHeightForWidth(self.defaultOutputFolderBox.sizePolicy().hasHeightForWidth())
|
||||
self.defaultOutputFolderBox.setSizePolicy(sizePolicy1)
|
||||
self.defaultOutputFolderBox.setTristate(True)
|
||||
|
||||
self.gridLayout_4.addWidget(self.defaultOutputFolderBox, 0, 4, 1, 1)
|
||||
|
||||
self.formatBox = QComboBox(self.buttonWidget)
|
||||
self.formatBox.setObjectName(u"formatBox")
|
||||
self.formatBox.setMinimumSize(QSize(0, 28))
|
||||
|
||||
self.gridLayout_4.addWidget(self.formatBox, 1, 4, 1, 2)
|
||||
|
||||
self.directoryButton.raise_()
|
||||
self.clearButton.raise_()
|
||||
self.fileButton.raise_()
|
||||
self.deviceBox.raise_()
|
||||
self.convertButton.raise_()
|
||||
self.formatBox.raise_()
|
||||
self.defaultOutputFolderButton.raise_()
|
||||
self.fileButton.raise_()
|
||||
self.defaultOutputFolderBox.raise_()
|
||||
|
||||
self.gridLayout.addWidget(self.buttonWidget, 3, 0, 1, 2)
|
||||
|
||||
@@ -166,11 +179,11 @@ class Ui_mainWindow(object):
|
||||
self.gridLayout_3.setContentsMargins(0, 0, 0, 0)
|
||||
self.hLabel = QLabel(self.customWidget)
|
||||
self.hLabel.setObjectName(u"hLabel")
|
||||
sizePolicy1 = QSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Preferred)
|
||||
sizePolicy1.setHorizontalStretch(0)
|
||||
sizePolicy1.setVerticalStretch(0)
|
||||
sizePolicy1.setHeightForWidth(self.hLabel.sizePolicy().hasHeightForWidth())
|
||||
self.hLabel.setSizePolicy(sizePolicy1)
|
||||
sizePolicy2 = QSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Preferred)
|
||||
sizePolicy2.setHorizontalStretch(0)
|
||||
sizePolicy2.setVerticalStretch(0)
|
||||
sizePolicy2.setHeightForWidth(self.hLabel.sizePolicy().hasHeightForWidth())
|
||||
self.hLabel.setSizePolicy(sizePolicy2)
|
||||
|
||||
self.gridLayout_3.addWidget(self.hLabel, 0, 2, 1, 1)
|
||||
|
||||
@@ -182,8 +195,8 @@ class Ui_mainWindow(object):
|
||||
|
||||
self.wLabel = QLabel(self.customWidget)
|
||||
self.wLabel.setObjectName(u"wLabel")
|
||||
sizePolicy1.setHeightForWidth(self.wLabel.sizePolicy().hasHeightForWidth())
|
||||
self.wLabel.setSizePolicy(sizePolicy1)
|
||||
sizePolicy2.setHeightForWidth(self.wLabel.sizePolicy().hasHeightForWidth())
|
||||
self.wLabel.setSizePolicy(sizePolicy2)
|
||||
|
||||
self.gridLayout_3.addWidget(self.wLabel, 0, 0, 1, 1)
|
||||
|
||||
@@ -222,11 +235,8 @@ class Ui_mainWindow(object):
|
||||
|
||||
self.preserveMarginBox = QSpinBox(self.croppingWidget)
|
||||
self.preserveMarginBox.setObjectName(u"preserveMarginBox")
|
||||
sizePolicy2 = QSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed)
|
||||
sizePolicy2.setHorizontalStretch(0)
|
||||
sizePolicy2.setVerticalStretch(0)
|
||||
sizePolicy2.setHeightForWidth(self.preserveMarginBox.sizePolicy().hasHeightForWidth())
|
||||
self.preserveMarginBox.setSizePolicy(sizePolicy2)
|
||||
sizePolicy1.setHeightForWidth(self.preserveMarginBox.sizePolicy().hasHeightForWidth())
|
||||
self.preserveMarginBox.setSizePolicy(sizePolicy1)
|
||||
self.preserveMarginBox.setMaximum(99)
|
||||
self.preserveMarginBox.setSingleStep(5)
|
||||
self.preserveMarginBox.setValue(0)
|
||||
@@ -241,65 +251,16 @@ class Ui_mainWindow(object):
|
||||
self.gridLayout_2 = QGridLayout(self.optionWidget)
|
||||
self.gridLayout_2.setObjectName(u"gridLayout_2")
|
||||
self.gridLayout_2.setContentsMargins(0, 0, 0, 0)
|
||||
self.croppingBox = QCheckBox(self.optionWidget)
|
||||
self.croppingBox.setObjectName(u"croppingBox")
|
||||
self.croppingBox.setTristate(True)
|
||||
|
||||
self.gridLayout_2.addWidget(self.croppingBox, 4, 2, 1, 1)
|
||||
|
||||
self.mangaBox = QCheckBox(self.optionWidget)
|
||||
self.mangaBox.setObjectName(u"mangaBox")
|
||||
|
||||
self.gridLayout_2.addWidget(self.mangaBox, 1, 0, 1, 1)
|
||||
|
||||
self.webtoonBox = QCheckBox(self.optionWidget)
|
||||
self.webtoonBox.setObjectName(u"webtoonBox")
|
||||
|
||||
self.gridLayout_2.addWidget(self.webtoonBox, 2, 0, 1, 1)
|
||||
|
||||
self.rotateBox = QCheckBox(self.optionWidget)
|
||||
self.rotateBox.setObjectName(u"rotateBox")
|
||||
self.rotateBox.setTristate(True)
|
||||
|
||||
self.gridLayout_2.addWidget(self.rotateBox, 1, 1, 1, 1)
|
||||
|
||||
self.borderBox = QCheckBox(self.optionWidget)
|
||||
self.borderBox.setObjectName(u"borderBox")
|
||||
self.borderBox.setTristate(True)
|
||||
|
||||
self.gridLayout_2.addWidget(self.borderBox, 3, 0, 1, 1)
|
||||
|
||||
self.gammaBox = QCheckBox(self.optionWidget)
|
||||
self.gammaBox.setObjectName(u"gammaBox")
|
||||
|
||||
self.gridLayout_2.addWidget(self.gammaBox, 2, 2, 1, 1)
|
||||
|
||||
self.interPanelCropBox = QCheckBox(self.optionWidget)
|
||||
self.interPanelCropBox.setObjectName(u"interPanelCropBox")
|
||||
self.interPanelCropBox.setTristate(True)
|
||||
|
||||
self.gridLayout_2.addWidget(self.interPanelCropBox, 6, 2, 1, 1)
|
||||
|
||||
self.colorBox = QCheckBox(self.optionWidget)
|
||||
self.colorBox.setObjectName(u"colorBox")
|
||||
self.mangaBox = QCheckBox(self.optionWidget)
|
||||
self.mangaBox.setObjectName(u"mangaBox")
|
||||
|
||||
self.gridLayout_2.addWidget(self.colorBox, 3, 2, 1, 1)
|
||||
|
||||
self.qualityBox = QCheckBox(self.optionWidget)
|
||||
self.qualityBox.setObjectName(u"qualityBox")
|
||||
self.qualityBox.setTristate(True)
|
||||
|
||||
self.gridLayout_2.addWidget(self.qualityBox, 1, 2, 1, 1)
|
||||
|
||||
self.disableProcessingBox = QCheckBox(self.optionWidget)
|
||||
self.disableProcessingBox.setObjectName(u"disableProcessingBox")
|
||||
|
||||
self.gridLayout_2.addWidget(self.disableProcessingBox, 5, 2, 1, 1)
|
||||
|
||||
self.maximizeStrips = QCheckBox(self.optionWidget)
|
||||
self.maximizeStrips.setObjectName(u"maximizeStrips")
|
||||
|
||||
self.gridLayout_2.addWidget(self.maximizeStrips, 4, 1, 1, 1)
|
||||
self.gridLayout_2.addWidget(self.mangaBox, 1, 0, 1, 1)
|
||||
|
||||
self.authorEdit = QLineEdit(self.optionWidget)
|
||||
self.authorEdit.setObjectName(u"authorEdit")
|
||||
@@ -313,27 +274,79 @@ class Ui_mainWindow(object):
|
||||
|
||||
self.gridLayout_2.addWidget(self.authorEdit, 0, 0, 1, 1)
|
||||
|
||||
self.croppingBox = QCheckBox(self.optionWidget)
|
||||
self.croppingBox.setObjectName(u"croppingBox")
|
||||
self.croppingBox.setTristate(True)
|
||||
|
||||
self.gridLayout_2.addWidget(self.croppingBox, 4, 2, 1, 1)
|
||||
|
||||
self.webtoonBox = QCheckBox(self.optionWidget)
|
||||
self.webtoonBox.setObjectName(u"webtoonBox")
|
||||
|
||||
self.gridLayout_2.addWidget(self.webtoonBox, 2, 0, 1, 1)
|
||||
|
||||
self.colorBox = QCheckBox(self.optionWidget)
|
||||
self.colorBox.setObjectName(u"colorBox")
|
||||
|
||||
self.gridLayout_2.addWidget(self.colorBox, 3, 2, 1, 1)
|
||||
|
||||
self.deleteBox = QCheckBox(self.optionWidget)
|
||||
self.deleteBox.setObjectName(u"deleteBox")
|
||||
|
||||
self.gridLayout_2.addWidget(self.deleteBox, 5, 1, 1, 1)
|
||||
|
||||
self.comicinfoTitleBox = QCheckBox(self.optionWidget)
|
||||
self.comicinfoTitleBox.setObjectName(u"comicinfoTitleBox")
|
||||
|
||||
self.gridLayout_2.addWidget(self.comicinfoTitleBox, 7, 0, 1, 1)
|
||||
|
||||
self.qualityBox = QCheckBox(self.optionWidget)
|
||||
self.qualityBox.setObjectName(u"qualityBox")
|
||||
self.qualityBox.setTristate(True)
|
||||
|
||||
self.gridLayout_2.addWidget(self.qualityBox, 1, 2, 1, 1)
|
||||
|
||||
self.reduceRainbowBox = QCheckBox(self.optionWidget)
|
||||
self.reduceRainbowBox.setObjectName(u"reduceRainbowBox")
|
||||
|
||||
self.gridLayout_2.addWidget(self.reduceRainbowBox, 7, 2, 1, 1)
|
||||
|
||||
self.noRotateBox = QCheckBox(self.optionWidget)
|
||||
self.noRotateBox.setObjectName(u"noRotateBox")
|
||||
|
||||
self.gridLayout_2.addWidget(self.noRotateBox, 6, 1, 1, 1)
|
||||
|
||||
self.fileFusionBox = QCheckBox(self.optionWidget)
|
||||
self.fileFusionBox.setObjectName(u"fileFusionBox")
|
||||
|
||||
self.gridLayout_2.addWidget(self.fileFusionBox, 6, 0, 1, 1)
|
||||
|
||||
self.gammaBox = QCheckBox(self.optionWidget)
|
||||
self.gammaBox.setObjectName(u"gammaBox")
|
||||
|
||||
self.gridLayout_2.addWidget(self.gammaBox, 2, 2, 1, 1)
|
||||
|
||||
self.mozJpegBox = QCheckBox(self.optionWidget)
|
||||
self.mozJpegBox.setObjectName(u"mozJpegBox")
|
||||
self.mozJpegBox.setTristate(True)
|
||||
|
||||
self.gridLayout_2.addWidget(self.mozJpegBox, 4, 0, 1, 1)
|
||||
|
||||
self.disableProcessingBox = QCheckBox(self.optionWidget)
|
||||
self.disableProcessingBox.setObjectName(u"disableProcessingBox")
|
||||
|
||||
self.gridLayout_2.addWidget(self.disableProcessingBox, 5, 2, 1, 1)
|
||||
|
||||
self.chunkSizeCheckBox = QCheckBox(self.optionWidget)
|
||||
self.chunkSizeCheckBox.setObjectName(u"chunkSizeCheckBox")
|
||||
|
||||
self.gridLayout_2.addWidget(self.chunkSizeCheckBox, 7, 1, 1, 1)
|
||||
|
||||
self.spreadShiftBox = QCheckBox(self.optionWidget)
|
||||
self.spreadShiftBox.setObjectName(u"spreadShiftBox")
|
||||
|
||||
self.gridLayout_2.addWidget(self.spreadShiftBox, 5, 0, 1, 1)
|
||||
|
||||
self.fileFusionBox = QCheckBox(self.optionWidget)
|
||||
self.fileFusionBox.setObjectName(u"fileFusionBox")
|
||||
|
||||
self.gridLayout_2.addWidget(self.fileFusionBox, 6, 0, 1, 1)
|
||||
|
||||
self.upscaleBox = QCheckBox(self.optionWidget)
|
||||
self.upscaleBox.setObjectName(u"upscaleBox")
|
||||
self.upscaleBox.setTristate(True)
|
||||
@@ -345,25 +358,27 @@ class Ui_mainWindow(object):
|
||||
|
||||
self.gridLayout_2.addWidget(self.outputSplit, 3, 1, 1, 1)
|
||||
|
||||
self.noRotateBox = QCheckBox(self.optionWidget)
|
||||
self.noRotateBox.setObjectName(u"noRotateBox")
|
||||
self.rotateBox = QCheckBox(self.optionWidget)
|
||||
self.rotateBox.setObjectName(u"rotateBox")
|
||||
self.rotateBox.setTristate(True)
|
||||
|
||||
self.gridLayout_2.addWidget(self.noRotateBox, 6, 1, 1, 1)
|
||||
self.gridLayout_2.addWidget(self.rotateBox, 1, 1, 1, 1)
|
||||
|
||||
self.reduceRainbowBox = QCheckBox(self.optionWidget)
|
||||
self.reduceRainbowBox.setObjectName(u"reduceRainbowBox")
|
||||
self.borderBox = QCheckBox(self.optionWidget)
|
||||
self.borderBox.setObjectName(u"borderBox")
|
||||
self.borderBox.setTristate(True)
|
||||
|
||||
self.gridLayout_2.addWidget(self.reduceRainbowBox, 7, 2, 1, 1)
|
||||
self.gridLayout_2.addWidget(self.borderBox, 3, 0, 1, 1)
|
||||
|
||||
self.chunkSizeCheckBox = QCheckBox(self.optionWidget)
|
||||
self.chunkSizeCheckBox.setObjectName(u"chunkSizeCheckBox")
|
||||
self.maximizeStrips = QCheckBox(self.optionWidget)
|
||||
self.maximizeStrips.setObjectName(u"maximizeStrips")
|
||||
|
||||
self.gridLayout_2.addWidget(self.chunkSizeCheckBox, 7, 1, 1, 1)
|
||||
self.gridLayout_2.addWidget(self.maximizeStrips, 4, 1, 1, 1)
|
||||
|
||||
self.comicinfoTitleBox = QCheckBox(self.optionWidget)
|
||||
self.comicinfoTitleBox.setObjectName(u"comicinfoTitleBox")
|
||||
self.rotateFirstBox = QCheckBox(self.optionWidget)
|
||||
self.rotateFirstBox.setObjectName(u"rotateFirstBox")
|
||||
|
||||
self.gridLayout_2.addWidget(self.comicinfoTitleBox, 7, 0, 1, 1)
|
||||
self.gridLayout_2.addWidget(self.rotateFirstBox, 8, 1, 1, 1)
|
||||
|
||||
|
||||
self.gridLayout.addWidget(self.optionWidget, 5, 0, 1, 2)
|
||||
@@ -433,9 +448,7 @@ class Ui_mainWindow(object):
|
||||
self.statusBar.setSizeGripEnabled(False)
|
||||
mainWindow.setStatusBar(self.statusBar)
|
||||
QWidget.setTabOrder(self.convertButton, self.clearButton)
|
||||
QWidget.setTabOrder(self.clearButton, self.directoryButton)
|
||||
QWidget.setTabOrder(self.directoryButton, self.fileButton)
|
||||
QWidget.setTabOrder(self.fileButton, self.deviceBox)
|
||||
QWidget.setTabOrder(self.clearButton, self.deviceBox)
|
||||
QWidget.setTabOrder(self.deviceBox, self.formatBox)
|
||||
QWidget.setTabOrder(self.formatBox, self.mangaBox)
|
||||
QWidget.setTabOrder(self.mangaBox, self.rotateBox)
|
||||
@@ -478,24 +491,28 @@ class Ui_mainWindow(object):
|
||||
self.kofiButton.setText(QCoreApplication.translate("mainWindow", u"Support me on Ko-fi", None))
|
||||
self.wikiButton.setText(QCoreApplication.translate("mainWindow", u"Wiki", None))
|
||||
#if QT_CONFIG(tooltip)
|
||||
self.directoryButton.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Add directory containing JPG, PNG or GIF files to queue.<br/><span style=\" font-weight:600;\">CBR, CBZ and CB7 files inside will not be processed!</span></p></body></html>", None))
|
||||
self.convertButton.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Shift+Click to select the output directory for this list.</p></body></html>", None))
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
self.convertButton.setText(QCoreApplication.translate("mainWindow", u"Convert", None))
|
||||
self.clearButton.setText(QCoreApplication.translate("mainWindow", u"Clear list", None))
|
||||
#if QT_CONFIG(tooltip)
|
||||
self.deviceBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Target device.</p></body></html>", None))
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
self.directoryButton.setText(QCoreApplication.translate("mainWindow", u"Add image folder", None))
|
||||
#if QT_CONFIG(tooltip)
|
||||
self.fileButton.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Add CBR, CBZ, CB7 or PDF file to queue.</p></body></html>", None))
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
self.fileButton.setText(QCoreApplication.translate("mainWindow", u"Add file(s)", None))
|
||||
#if QT_CONFIG(tooltip)
|
||||
self.deviceBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Target device.</p></body></html>", None))
|
||||
self.defaultOutputFolderButton.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p>Use this to select the default output directory.</p></body></html>", None))
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
self.defaultOutputFolderButton.setText("")
|
||||
#if QT_CONFIG(tooltip)
|
||||
self.defaultOutputFolderBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - next to source<br/></span>Place output files next to source files</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - folder next to source<br/></span>Place output files in a folder next to source files</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Custom<br/></span>Place output files in custom directory specified by right button</p></body></html>", None))
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
self.defaultOutputFolderBox.setText(QCoreApplication.translate("mainWindow", u"Output Folder", None))
|
||||
#if QT_CONFIG(tooltip)
|
||||
self.formatBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Output format.</p></body></html>", None))
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
#if QT_CONFIG(tooltip)
|
||||
self.convertButton.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Shift+Click to select the output directory.</p></body></html>", None))
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
self.convertButton.setText(QCoreApplication.translate("mainWindow", u"Convert", None))
|
||||
self.clearButton.setText(QCoreApplication.translate("mainWindow", u"Clear list", None))
|
||||
#if QT_CONFIG(tooltip)
|
||||
self.hLabel.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Resolution of the target device.</p></body></html>", None))
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
@@ -516,69 +533,73 @@ class Ui_mainWindow(object):
|
||||
self.preserveMarginLabel.setText(QCoreApplication.translate("mainWindow", u"Preserve Margin %", None))
|
||||
self.croppingPowerLabel.setText(QCoreApplication.translate("mainWindow", u"Cropping power:", None))
|
||||
#if QT_CONFIG(tooltip)
|
||||
self.croppingBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Disabled</span></p><p>Disabled</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - Margins<br/></span>Margins</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Margins + page numbers<br/></span>Margins +page numbers</p></body></html>", None))
|
||||
self.interPanelCropBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Disabled<br/></span>Disabled</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - Horizontal<br/></span>Crop empty horizontal lines.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Both<br/></span>Crop empty horizontal and vertical lines.</p></body></html>", None))
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
self.croppingBox.setText(QCoreApplication.translate("mainWindow", u"Cropping mode", None))
|
||||
self.interPanelCropBox.setText(QCoreApplication.translate("mainWindow", u"Inter-panel crop", None))
|
||||
#if QT_CONFIG(tooltip)
|
||||
self.mangaBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Enable right-to-left reading.</p></body></html>", None))
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
self.mangaBox.setText(QCoreApplication.translate("mainWindow", u"Right-to-left mode", None))
|
||||
#if QT_CONFIG(tooltip)
|
||||
self.authorEdit.setToolTip(QCoreApplication.translate("mainWindow", u"Default Author is KCC", None))
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
self.authorEdit.setPlaceholderText(QCoreApplication.translate("mainWindow", u"Default Author", None))
|
||||
#if QT_CONFIG(tooltip)
|
||||
self.croppingBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Disabled</span></p><p>Disabled</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - Margins<br/></span>Margins</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Margins + page numbers<br/></span>Margins +page numbers</p></body></html>", None))
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
self.croppingBox.setText(QCoreApplication.translate("mainWindow", u"Cropping mode", None))
|
||||
#if QT_CONFIG(tooltip)
|
||||
self.webtoonBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Enable special parsing mode for Korean Webtoons.</p></body></html>", None))
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
self.webtoonBox.setText(QCoreApplication.translate("mainWindow", u"Webtoon mode", None))
|
||||
#if QT_CONFIG(tooltip)
|
||||
self.rotateBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Split<br/></span>Double page spreads will be cut into two separate pages.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - Split and rotate<br/></span>Double page spreads will be displayed twice. First split and then rotated. </p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Rotate<br/></span>Double page spreads will be rotated.</p></body></html>", None))
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
self.rotateBox.setText(QCoreApplication.translate("mainWindow", u"Spread splitter", None))
|
||||
#if QT_CONFIG(tooltip)
|
||||
self.borderBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Autodetection<br/></span>The color of margins fill will be detected automatically.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - White<br/></span>Margins will be filled with white color.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Black<br/></span>Margins will be filled with black color.</p></body></html>", None))
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
self.borderBox.setText(QCoreApplication.translate("mainWindow", u"W/B margins", None))
|
||||
#if QT_CONFIG(tooltip)
|
||||
self.gammaBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Disable automatic gamma correction.</p></body></html>", None))
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
self.gammaBox.setText(QCoreApplication.translate("mainWindow", u"Custom gamma", None))
|
||||
#if QT_CONFIG(tooltip)
|
||||
self.interPanelCropBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Disabled<br/></span>Disabled</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - Horizontal<br/></span>Crop empty horizontal lines.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Both<br/></span>Crop empty horizontal and vertical lines.</p></body></html>", None))
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
self.interPanelCropBox.setText(QCoreApplication.translate("mainWindow", u"Inter-panel crop", None))
|
||||
#if QT_CONFIG(tooltip)
|
||||
self.colorBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Disable conversion to grayscale.</p></body></html>", None))
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
self.colorBox.setText(QCoreApplication.translate("mainWindow", u"Color mode", None))
|
||||
#if QT_CONFIG(tooltip)
|
||||
self.deleteBox.setToolTip(QCoreApplication.translate("mainWindow", u"Delete input file(s) or directory. It's not recoverable!", None))
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
self.deleteBox.setText(QCoreApplication.translate("mainWindow", u"Delete input", None))
|
||||
#if QT_CONFIG(tooltip)
|
||||
self.comicinfoTitleBox.setToolTip(QCoreApplication.translate("mainWindow", u"Write Title from ComicInfo.xml", None))
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
self.comicinfoTitleBox.setText(QCoreApplication.translate("mainWindow", u"ComicInfo Title", None))
|
||||
#if QT_CONFIG(tooltip)
|
||||
self.qualityBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - 4 panels<br/></span>Zoom each corner separately.</p><p style='white-space:pre'><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - 2 panels<br/></span>Zoom only the top and bottom of the page.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - 4 high-quality panels<br/></span>Zoom each corner separately. Try to increase the quality of magnification. Check wiki for more details.</p></body></html>", None))
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
self.qualityBox.setText(QCoreApplication.translate("mainWindow", u"Panel View 4/2/HQ", None))
|
||||
#if QT_CONFIG(tooltip)
|
||||
self.disableProcessingBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Do not process any image, ignore profile and processing options.</p></body></html>", None))
|
||||
self.reduceRainbowBox.setToolTip(QCoreApplication.translate("mainWindow", u"Reduce rainbow effect on color eink by slightly blurring images", None))
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
self.disableProcessingBox.setText(QCoreApplication.translate("mainWindow", u"Disable processing", None))
|
||||
self.reduceRainbowBox.setText(QCoreApplication.translate("mainWindow", u"Rainbow blur", None))
|
||||
#if QT_CONFIG(tooltip)
|
||||
self.maximizeStrips.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - 1x4<br/></span>Keep format 1x4 panels strips.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - 2x2<br/></span>Turn 1x4 strips to 2x2 to maximize screen usage.</p></body></html>", None))
|
||||
self.noRotateBox.setToolTip(QCoreApplication.translate("mainWindow", u"Do not rotate double page spreads in spread splitter option.", None))
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
self.maximizeStrips.setText(QCoreApplication.translate("mainWindow", u"1x4 to 2x2 strips", None))
|
||||
self.noRotateBox.setText(QCoreApplication.translate("mainWindow", u"No rotate", None))
|
||||
#if QT_CONFIG(tooltip)
|
||||
self.authorEdit.setToolTip(QCoreApplication.translate("mainWindow", u"Default Author is KCC", None))
|
||||
self.fileFusionBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p>Combines all selected files into a single file. (Helpful for combining chapters into volumes.)</p></body></html>", None))
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
self.authorEdit.setPlaceholderText(QCoreApplication.translate("mainWindow", u"Default Author", None))
|
||||
self.fileFusionBox.setText(QCoreApplication.translate("mainWindow", u"File Fusion", None))
|
||||
#if QT_CONFIG(tooltip)
|
||||
self.deleteBox.setToolTip(QCoreApplication.translate("mainWindow", u"Delete input file(s) or directory. It's not recoverable!", None))
|
||||
self.gammaBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Disable automatic gamma correction.</p></body></html>", None))
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
self.deleteBox.setText(QCoreApplication.translate("mainWindow", u"Delete input", None))
|
||||
self.gammaBox.setText(QCoreApplication.translate("mainWindow", u"Custom gamma", None))
|
||||
#if QT_CONFIG(tooltip)
|
||||
self.mozJpegBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - JPEG<br/></span>Use JPEG files</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - force PNG<br/></span>Create PNG files instead JPEG</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - mozJpeg<br/></span>10-20% smaller JPEG file, with the same image quality, but processing time multiplied by 2</p></body></html>", None))
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
self.mozJpegBox.setText(QCoreApplication.translate("mainWindow", u"JPEG/PNG/mozJpeg", None))
|
||||
#if QT_CONFIG(tooltip)
|
||||
self.disableProcessingBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Do not process any image, ignore profile and processing options.</p></body></html>", None))
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
self.disableProcessingBox.setText(QCoreApplication.translate("mainWindow", u"Disable processing", None))
|
||||
#if QT_CONFIG(tooltip)
|
||||
self.chunkSizeCheckBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p><span style=\" font-weight:700; text-decoration: underline;\">Unchecked<br/></span>Maximal output file size is 100 MB for Webtoon, 400 MB for others before split occurs.</p><p><span style=\" font-weight:700; text-decoration: underline;\">Checked</span><br/>Output file size specified in "Chunk size MB" before split occurs.</p></body></html>", None))
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
self.chunkSizeCheckBox.setText(QCoreApplication.translate("mainWindow", u"Chunk size", None))
|
||||
#if QT_CONFIG(tooltip)
|
||||
self.spreadShiftBox.setToolTip(QCoreApplication.translate("mainWindow", u"Shift first page to opposite side in landscape for two page spread alignment", None))
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
self.spreadShiftBox.setText(QCoreApplication.translate("mainWindow", u"Spread shift", None))
|
||||
#if QT_CONFIG(tooltip)
|
||||
self.fileFusionBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p>Combines all selected files into a single file. (Helpful for combining chapters into volumes.)</p></body></html>", None))
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
self.fileFusionBox.setText(QCoreApplication.translate("mainWindow", u"File Fusion", None))
|
||||
#if QT_CONFIG(tooltip)
|
||||
self.upscaleBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Nothing<br/></span>Images smaller than device resolution will not be resized.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - Stretching<br/></span>Images smaller than device resolution will be resized. Aspect ratio will be not preserved.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Upscaling<br/></span>Images smaller than device resolution will be resized. Aspect ratio will be preserved.</p></body></html>", None))
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
@@ -588,21 +609,21 @@ class Ui_mainWindow(object):
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
self.outputSplit.setText(QCoreApplication.translate("mainWindow", u"Output split", None))
|
||||
#if QT_CONFIG(tooltip)
|
||||
self.noRotateBox.setToolTip(QCoreApplication.translate("mainWindow", u"Do not rotate double page spreads in spread splitter option.", None))
|
||||
self.rotateBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Split<br/></span>Double page spreads will be cut into two separate pages.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - Split and rotate<br/></span>Double page spreads will be displayed twice. First split and then rotated. </p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Rotate<br/></span>Double page spreads will be rotated.</p></body></html>", None))
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
self.noRotateBox.setText(QCoreApplication.translate("mainWindow", u"No rotate", None))
|
||||
self.rotateBox.setText(QCoreApplication.translate("mainWindow", u"Spread splitter", None))
|
||||
#if QT_CONFIG(tooltip)
|
||||
self.reduceRainbowBox.setToolTip(QCoreApplication.translate("mainWindow", u"Reduce rainbow effect on color eink by slightly blurring images", None))
|
||||
self.borderBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Autodetection<br/></span>The color of margins fill will be detected automatically.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - White<br/></span>Margins will be filled with white color.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Black<br/></span>Margins will be filled with black color.</p></body></html>", None))
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
self.reduceRainbowBox.setText(QCoreApplication.translate("mainWindow", u"Rainbow blur", None))
|
||||
self.borderBox.setText(QCoreApplication.translate("mainWindow", u"W/B margins", None))
|
||||
#if QT_CONFIG(tooltip)
|
||||
self.chunkSizeCheckBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p><span style=\" font-weight:700; text-decoration: underline;\">Unchecked<br/></span>Maximal output file size is 100 MB for Webtoon, 400 MB for others before split occurs.</p><p><span style=\" font-weight:700; text-decoration: underline;\">Checked</span><br/>Output file size specified in "Chunk size MB" before split occurs.</p></body></html>", None))
|
||||
self.maximizeStrips.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - 1x4<br/></span>Keep format 1x4 panels strips.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - 2x2<br/></span>Turn 1x4 strips to 2x2 to maximize screen usage.</p></body></html>", None))
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
self.chunkSizeCheckBox.setText(QCoreApplication.translate("mainWindow", u"Chunk size", None))
|
||||
self.maximizeStrips.setText(QCoreApplication.translate("mainWindow", u"1x4 to 2x2 strips", None))
|
||||
#if QT_CONFIG(tooltip)
|
||||
self.comicinfoTitleBox.setToolTip(QCoreApplication.translate("mainWindow", u"Write Title from ComicInfo.xml", None))
|
||||
self.rotateFirstBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p>When the spread splitter option is partially checked,</p><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Rotate Last<br/></span>Put the rotated 2 page spread after the split spreads.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Rotate First<br/></span>Put the rotated 2 page spread before the split spreads.</p></body></html>", None))
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
self.comicinfoTitleBox.setText(QCoreApplication.translate("mainWindow", u"ComicInfo Title", None))
|
||||
self.rotateFirstBox.setText(QCoreApplication.translate("mainWindow", u"Rotate First", None))
|
||||
self.gammaLabel.setText(QCoreApplication.translate("mainWindow", u"Gamma: Auto", None))
|
||||
#if QT_CONFIG(tooltip)
|
||||
self.chunkSizeWidget.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p>Warning: chunk size greater than default may cause<br/>performance/battery issues, especially on older devices.</p></body></html>", None))
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
__version__ = '7.5.0'
|
||||
__version__ = '8.0.3'
|
||||
__license__ = 'ISC'
|
||||
__copyright__ = '2012-2022, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>, darodi'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
@@ -34,7 +34,7 @@ from tempfile import mkdtemp, gettempdir, TemporaryFile
|
||||
from shutil import move, copytree, rmtree, copyfile
|
||||
from multiprocessing import Pool
|
||||
from uuid import uuid4
|
||||
from natsort import os_sort_keygen
|
||||
from natsort import os_sort_keygen, os_sorted
|
||||
from slugify import slugify as slugify_ext
|
||||
from PIL import Image, ImageFile
|
||||
from pathlib import Path
|
||||
@@ -42,7 +42,8 @@ from subprocess import STDOUT, PIPE, CalledProcessError
|
||||
from psutil import virtual_memory, disk_usage
|
||||
from html import escape as hescape
|
||||
|
||||
from .shared import available_archive_tools, getImageFileName, walkSort, walkLevel, sanitizeTrace, subprocess_run
|
||||
from .shared import getImageFileName, walkSort, walkLevel, sanitizeTrace, subprocess_run
|
||||
from .comicarchive import SEVENZIP, available_archive_tools
|
||||
from . import comic2panel
|
||||
from . import image
|
||||
from . import comicarchive
|
||||
@@ -127,10 +128,10 @@ def buildHTML(path, imgfile, imgfilepath, imgfile2=None):
|
||||
# this display none div fixes formatting issues with virtual panel mode, for some reason
|
||||
'<div style="display:none;">.</div>\n',
|
||||
])
|
||||
f.write(f'<img width="{imgsize[0]}" height="{imgsize[1]}" src="{"../" * backref}Images/{postfix}{imgfile}"/>')
|
||||
f.write(f'<img width="{imgsize[0]}" height="{imgsize[1]}" src="{"../" * backref}Images/{postfix}{imgfile}"/>\n')
|
||||
if imgfile2:
|
||||
f.write(f'<img width="{imgsize2[0]}" height="{imgsize2[1]}" src="{"../" * backref}Images/{postfix}{imgfile2}"/>')
|
||||
f.write("\n</div>\n")
|
||||
f.write(f'<img width="{imgsize2[0]}" height="{imgsize2[1]}" src="{"../" * backref}Images/{postfix}{imgfile2}"/>\n')
|
||||
f.write("</div>\n")
|
||||
if options.iskindle and options.panelview:
|
||||
if options.autoscale:
|
||||
size = (getPanelViewResolution(imgsize, deviceres))
|
||||
@@ -336,6 +337,8 @@ def buildOPF(dstdir, title, filelist, cover=None):
|
||||
".xhtml\" media-type=\"application/xhtml+xml\"/>\n")
|
||||
if '.png' == filename[1]:
|
||||
mt = 'image/png'
|
||||
elif '.gif' == filename[1]:
|
||||
mt = 'image/gif'
|
||||
else:
|
||||
mt = 'image/jpeg'
|
||||
f.write("<item id=\"img_" + str(uniqueid) + "\" href=\"" + folder + "/" + path[1] + "\" media-type=\"" +
|
||||
@@ -447,7 +450,14 @@ def buildEPUB(path, chapternames, tomenumber, ischunked, cover: image.Cover, len
|
||||
"display: block;\n",
|
||||
"margin: 0;\n",
|
||||
"padding: 0;\n",
|
||||
"}\n"])
|
||||
"}\n",
|
||||
])
|
||||
if options.kindle_scribe_azw3:
|
||||
f.writelines([
|
||||
"img {\n",
|
||||
"display: block;\n",
|
||||
"}\n",
|
||||
])
|
||||
if options.iskindle and options.panelview:
|
||||
f.writelines(["#PV {\n",
|
||||
"position: absolute;\n",
|
||||
@@ -638,8 +648,12 @@ def imgFileProcessing(work):
|
||||
img.autocontrastImage()
|
||||
img.resizeImage()
|
||||
img.optimizeForDisplay(opt.reducerainbow)
|
||||
if opt.forcepng and not opt.forcecolor:
|
||||
if opt.forcecolor and img.color:
|
||||
pass
|
||||
elif opt.forcepng:
|
||||
img.quantizeImage()
|
||||
else:
|
||||
img.convertToGrayscale()
|
||||
output.append(img.saveToDir())
|
||||
return output
|
||||
except Exception:
|
||||
@@ -677,16 +691,6 @@ def getWorkFolder(afile):
|
||||
cbx = comicarchive.ComicArchive(afile)
|
||||
path = cbx.extract(workdir)
|
||||
sanitizePermissions(path)
|
||||
tdir = os.listdir(workdir)
|
||||
if len(tdir) == 2 and 'ComicInfo.xml' in tdir:
|
||||
tdir.remove('ComicInfo.xml')
|
||||
if os.path.isdir(os.path.join(workdir, tdir[0])):
|
||||
os.replace(
|
||||
os.path.join(workdir, 'ComicInfo.xml'),
|
||||
os.path.join(workdir, tdir[0], 'ComicInfo.xml')
|
||||
)
|
||||
if len(tdir) == 1 and os.path.isdir(os.path.join(workdir, tdir[0])):
|
||||
path = os.path.join(workdir, tdir[0])
|
||||
except OSError as e:
|
||||
rmtree(workdir, True)
|
||||
raise UserWarning(e)
|
||||
@@ -809,29 +813,48 @@ def getPanelViewSize(deviceres, size):
|
||||
return str(int(x)), str(int(y))
|
||||
|
||||
|
||||
def removeNonImages(filetree):
|
||||
# clean dot from original file
|
||||
dot_clean(filetree)
|
||||
|
||||
for root, dirs, files in os.walk(filetree):
|
||||
for name in files:
|
||||
_, ext = getImageFileName(name)
|
||||
if ext not in ('.png', '.jpg', '.jpeg', '.gif', '.webp', '.jp2', '.avif'):
|
||||
if os.path.exists(os.path.join(root, name)):
|
||||
os.remove(os.path.join(root, name))
|
||||
# remove empty nested folders
|
||||
for root, dirs, files in os.walk(filetree, False):
|
||||
if not files and not dirs:
|
||||
os.rmdir(root)
|
||||
|
||||
|
||||
def sanitizeTree(filetree):
|
||||
chapterNames = {}
|
||||
page = 1
|
||||
cover_path = None
|
||||
for root, dirs, files in os.walk(filetree):
|
||||
dirs.sort(key=OS_SORT_KEY)
|
||||
files.sort(key=OS_SORT_KEY)
|
||||
for name in files:
|
||||
splitname = os.path.splitext(name)
|
||||
_, ext = getImageFileName(name)
|
||||
|
||||
# 9999 page limit
|
||||
slugified = f'kcc-{page:04}'
|
||||
unique_name = f'kcc-{page:04}'
|
||||
page += 1
|
||||
|
||||
newKey = os.path.join(root, slugified + splitname[1])
|
||||
newKey = os.path.join(root, unique_name + ext)
|
||||
key = os.path.join(root, name)
|
||||
if key != newKey:
|
||||
os.replace(key, newKey)
|
||||
if not cover_path:
|
||||
cover_path = newKey
|
||||
is_natural_sorted = False
|
||||
if os_sorted(dirs) == sorted(dirs):
|
||||
is_natural_sorted = True
|
||||
dirs.sort(key=OS_SORT_KEY)
|
||||
for i, name in enumerate(dirs):
|
||||
tmpName = name
|
||||
slugified = slugify(name)
|
||||
slugified = slugify(name, is_natural_sorted)
|
||||
while os.path.exists(os.path.join(root, slugified)) and name.upper() != slugified.upper():
|
||||
slugified += "A"
|
||||
chapterNames[slugified] = tmpName
|
||||
@@ -857,8 +880,7 @@ def sanitizePermissions(filetree):
|
||||
os.chmod(os.path.join(root, name), S_IWRITE | S_IREAD)
|
||||
for name in dirs:
|
||||
os.chmod(os.path.join(root, name), S_IWRITE | S_IREAD | S_IEXEC)
|
||||
# clean dot from original file
|
||||
dot_clean(filetree)
|
||||
|
||||
|
||||
def dot_clean(filetree):
|
||||
for root, _, files in os.walk(filetree, topdown=False):
|
||||
@@ -977,10 +999,6 @@ def detectSuboptimalProcessing(tmppath, orgpath):
|
||||
os.remove(os.path.join(root, name))
|
||||
except OSError as e:
|
||||
raise RuntimeError(f"{name}: {e}")
|
||||
# remove empty nested folders
|
||||
for root, dirs, files in os.walk(tmppath, False):
|
||||
if not files and not dirs:
|
||||
os.rmdir(root)
|
||||
if alreadyProcessed:
|
||||
print("WARNING: Source files are probably created by KCC. The second conversion will decrease quality.")
|
||||
if GUI:
|
||||
@@ -1003,23 +1021,27 @@ def createNewTome(parent):
|
||||
return tomePath, tomePathRoot
|
||||
|
||||
|
||||
def slugify(value):
|
||||
if options.format == 'CBZ':
|
||||
def slugify(value, is_natural_sorted):
|
||||
if options.format == 'CBZ' and is_natural_sorted:
|
||||
return value
|
||||
value = slugify_ext(value, regex_pattern=r'[^-a-z0-9_\.]+').strip('.')
|
||||
value = sub(r'0*([0-9]{4,})', r'\1', sub(r'([0-9]+)', r'0000\1', value, count=2))
|
||||
if options.format != 'CBZ':
|
||||
# convert all unicode to ascii via slugify
|
||||
value = slugify_ext(value, regex_pattern=r'[^-a-z0-9_\.]+').strip('.')
|
||||
if not is_natural_sorted:
|
||||
# pad zeros to numbers
|
||||
value = sub(r'0*([0-9]{4,})', r'\1', sub(r'([0-9]+)', r'0000\1', value, count=2))
|
||||
return value
|
||||
|
||||
|
||||
def makeZIP(zipfilename, basedir, isepub=False):
|
||||
start = perf_counter()
|
||||
zipfilename = os.path.abspath(zipfilename) + '.zip'
|
||||
if '7z' in available_archive_tools():
|
||||
if SEVENZIP in available_archive_tools():
|
||||
if isepub:
|
||||
mimetypeFile = open(os.path.join(basedir, 'mimetype'), 'w')
|
||||
mimetypeFile.write('application/epub+zip')
|
||||
mimetypeFile.close()
|
||||
subprocess_run(['7z', 'a', '-tzip', zipfilename, os.path.join(basedir, "*")], capture_output=True, check=True)
|
||||
subprocess_run([SEVENZIP, 'a', '-tzip', zipfilename, os.path.join(basedir, "*")], capture_output=True, check=True)
|
||||
else:
|
||||
zipOutput = ZipFile(zipfilename, 'w', ZIP_DEFLATED)
|
||||
if isepub:
|
||||
@@ -1084,6 +1106,8 @@ def makeParser():
|
||||
help="Shift first page to opposite side in landscape for spread alignment")
|
||||
output_options.add_argument("--norotate", action="store_true", dest="norotate", default=False,
|
||||
help="Do not rotate double page spreads in spread splitter option.")
|
||||
output_options.add_argument("--rotatefirst", action="store_true", dest="rotatefirst", default=False,
|
||||
help="Put rotated 2 page spread first in spread splitter option.")
|
||||
|
||||
processing_options.add_argument("-n", "--noprocessing", action="store_true", dest="noprocessing", default=False,
|
||||
help="Do not modify image and ignore any profil or processing option")
|
||||
@@ -1219,13 +1243,13 @@ def checkTools(source):
|
||||
source = source.upper()
|
||||
if source.endswith('.CB7') or source.endswith('.7Z') or source.endswith('.RAR') or source.endswith('.CBR') or \
|
||||
source.endswith('.ZIP') or source.endswith('.CBZ'):
|
||||
if '7z' not in available_archive_tools():
|
||||
if SEVENZIP not in available_archive_tools():
|
||||
print('ERROR: 7z is missing!')
|
||||
sys.exit(1)
|
||||
if options.format == 'MOBI':
|
||||
try:
|
||||
subprocess_run(['kindlegen', '-locale', 'en'], stdout=PIPE, stderr=STDOUT, check=True)
|
||||
except FileNotFoundError:
|
||||
except (FileNotFoundError, CalledProcessError):
|
||||
print('ERROR: KindleGen is missing!')
|
||||
sys.exit(1)
|
||||
|
||||
@@ -1296,6 +1320,7 @@ def makeBook(source, qtgui=None):
|
||||
path = getWorkFolder(source)
|
||||
print("Checking images...")
|
||||
getComicInfo(os.path.join(path, "OEBPS", "Images"), source)
|
||||
removeNonImages(os.path.join(path, "OEBPS", "Images"))
|
||||
detectSuboptimalProcessing(os.path.join(path, "OEBPS", "Images"), source)
|
||||
chapterNames, cover_path = sanitizeTree(os.path.join(path, 'OEBPS', 'Images'))
|
||||
cover = image.Cover(cover_path, options)
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
# PERFORMANCE OF THIS SOFTWARE.
|
||||
#
|
||||
|
||||
from functools import cached_property
|
||||
from functools import cached_property, lru_cache
|
||||
import os
|
||||
import platform
|
||||
import distro
|
||||
@@ -28,6 +28,7 @@ from xml.parsers.expat import ExpatError
|
||||
from .shared import subprocess_run
|
||||
|
||||
EXTRACTION_ERROR = 'Failed to extract archive. Try extracting file outside of KCC.'
|
||||
SEVENZIP = '7zz' if platform.system() == 'Darwin' else '7z'
|
||||
|
||||
|
||||
class ComicArchive:
|
||||
@@ -39,7 +40,7 @@ class ComicArchive:
|
||||
@cached_property
|
||||
def type(self):
|
||||
extraction_commands = [
|
||||
['7z', 'l', '-y', '-p1', self.filepath],
|
||||
[SEVENZIP, 'l', '-y', '-p1', self.filepath],
|
||||
]
|
||||
|
||||
if distro.id() == 'fedora' or distro.like() == 'fedora':
|
||||
@@ -68,7 +69,7 @@ class ComicArchive:
|
||||
|
||||
extraction_commands = [
|
||||
['tar', '--exclude', '__MACOSX', '--exclude', '.DS_Store', '--exclude', 'thumbs.db', '--exclude', 'Thumbs.db', '-xf', self.filepath, '-C', targetdir],
|
||||
['7z', 'x', '-y', '-xr!__MACOSX', '-xr!.DS_Store', '-xr!thumbs.db', '-xr!Thumbs.db', '-o' + targetdir, self.filepath],
|
||||
[SEVENZIP, 'x', '-y', '-xr!__MACOSX', '-xr!.DS_Store', '-xr!thumbs.db', '-xr!Thumbs.db', '-o' + targetdir, self.filepath],
|
||||
]
|
||||
|
||||
if platform.system() == 'Darwin':
|
||||
@@ -86,7 +87,7 @@ class ComicArchive:
|
||||
for cmd in extraction_commands:
|
||||
try:
|
||||
subprocess_run(cmd, capture_output=True, check=True)
|
||||
return targetdir
|
||||
return os.path.join(targetdir, os.listdir(targetdir)[0])
|
||||
except FileNotFoundError:
|
||||
missing.append(cmd[0])
|
||||
except CalledProcessError:
|
||||
@@ -100,13 +101,13 @@ class ComicArchive:
|
||||
def addFile(self, sourcefile):
|
||||
if self.type in ['RAR', 'RAR5']:
|
||||
raise NotImplementedError
|
||||
process = subprocess_run(['7z', 'a', '-y', self.filepath, sourcefile],
|
||||
process = subprocess_run([SEVENZIP, 'a', '-y', self.filepath, sourcefile],
|
||||
stdout=PIPE, stderr=STDOUT)
|
||||
if process.returncode != 0:
|
||||
raise OSError('Failed to add the file.')
|
||||
|
||||
def extractMetadata(self):
|
||||
process = subprocess_run(['7z', 'x', '-y', '-so', self.filepath, 'ComicInfo.xml'],
|
||||
process = subprocess_run([SEVENZIP, 'x', '-y', '-so', self.filepath, 'ComicInfo.xml'],
|
||||
stdout=PIPE, stderr=STDOUT)
|
||||
if process.returncode != 0:
|
||||
raise OSError(EXTRACTION_ERROR)
|
||||
@@ -114,3 +115,16 @@ class ComicArchive:
|
||||
return parseString(process.stdout)
|
||||
except ExpatError:
|
||||
return None
|
||||
|
||||
@lru_cache
|
||||
def available_archive_tools():
|
||||
available = []
|
||||
|
||||
for tool in ['tar', SEVENZIP, 'unar', 'unrar']:
|
||||
try:
|
||||
subprocess_run([tool], stdout=PIPE, stderr=STDOUT)
|
||||
available.append(tool)
|
||||
except (FileNotFoundError, CalledProcessError):
|
||||
pass
|
||||
|
||||
return available
|
||||
|
||||
@@ -20,7 +20,9 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import io
|
||||
import os
|
||||
import numpy as np
|
||||
from pathlib import Path
|
||||
from functools import cached_property
|
||||
import mozjpeg_lossless_optimization
|
||||
from PIL import Image, ImageOps, ImageStat, ImageChops, ImageFilter, ImageDraw
|
||||
from .page_number_crop_alg import get_bbox_crop_margin_page_number, get_bbox_crop_margin
|
||||
@@ -146,11 +148,9 @@ class ComicPageParser:
|
||||
|
||||
# Detect corruption in source image, let caller catch any exceptions triggered.
|
||||
srcImgPath = os.path.join(source[0], source[1])
|
||||
Image.open(srcImgPath).verify()
|
||||
self.image = Image.open(srcImgPath)
|
||||
self.image.verify()
|
||||
self.image = Image.open(srcImgPath).convert('RGB')
|
||||
|
||||
self.color = self.colorCheck()
|
||||
self.fill = self.fillCheck()
|
||||
# backwards compatibility for Pillow >9.1.0
|
||||
if not hasattr(Image, 'Resampling'):
|
||||
@@ -181,13 +181,13 @@ class ComicPageParser:
|
||||
new_image = Image.new("RGB", (int(width / 2), int(height*2)))
|
||||
new_image.paste(pageone, (0, 0))
|
||||
new_image.paste(pagetwo, (0, height))
|
||||
self.payload.append(['N', self.source, new_image, self.color, self.fill])
|
||||
self.payload.append(['N', self.source, new_image, self.fill])
|
||||
elif (width > height) != (dstwidth > dstheight) and width <= dstheight and height <= dstwidth \
|
||||
and not self.opt.webtoon and self.opt.splitter == 1:
|
||||
spread = self.image
|
||||
if not self.opt.norotate:
|
||||
spread = spread.rotate(90, Image.Resampling.BICUBIC, True)
|
||||
self.payload.append(['R', self.source, spread, self.color, self.fill])
|
||||
self.payload.append(['R', self.source, spread, self.fill])
|
||||
elif (width > height) != (dstwidth > dstheight) and not self.opt.webtoon:
|
||||
if self.opt.splitter != 1:
|
||||
if width > height:
|
||||
@@ -202,38 +202,15 @@ class ComicPageParser:
|
||||
else:
|
||||
pageone = self.image.crop(leftbox)
|
||||
pagetwo = self.image.crop(rightbox)
|
||||
self.payload.append(['S1', self.source, pageone, self.color, self.fill])
|
||||
self.payload.append(['S2', self.source, pagetwo, self.color, self.fill])
|
||||
self.payload.append(['S1', self.source, pageone, self.fill])
|
||||
self.payload.append(['S2', self.source, pagetwo, self.fill])
|
||||
if self.opt.splitter > 0:
|
||||
spread = self.image
|
||||
if not self.opt.norotate:
|
||||
spread = spread.rotate(90, Image.Resampling.BICUBIC, True)
|
||||
self.payload.append(['R', self.source, spread,
|
||||
self.color, self.fill])
|
||||
self.payload.append(['R', self.source, spread, self.fill])
|
||||
else:
|
||||
self.payload.append(['N', self.source, self.image, self.color, self.fill])
|
||||
|
||||
def colorCheck(self):
|
||||
if self.opt.webtoon:
|
||||
return True
|
||||
else:
|
||||
img = self.image.copy()
|
||||
bands = img.getbands()
|
||||
if bands == ('R', 'G', 'B') or bands == ('R', 'G', 'B', 'A'):
|
||||
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 True
|
||||
else:
|
||||
return False
|
||||
else:
|
||||
return False
|
||||
self.payload.append(['N', self.source, self.image, self.fill])
|
||||
|
||||
def fillCheck(self):
|
||||
if self.opt.bordersColor:
|
||||
@@ -275,14 +252,14 @@ class ComicPageParser:
|
||||
|
||||
|
||||
class ComicPage:
|
||||
def __init__(self, options, mode, path, image, color, fill):
|
||||
def __init__(self, options, mode, path, image, fill):
|
||||
self.opt = options
|
||||
_, self.size, self.palette, self.gamma = self.opt.profileData
|
||||
if self.opt.hq:
|
||||
self.size = (int(self.size[0] * 1.5), int(self.size[1] * 1.5))
|
||||
self.kindle_scribe_azw3 = (options.profile == 'KS') and (options.format in ('MOBI', 'EPUB'))
|
||||
self.image = image
|
||||
self.color = color
|
||||
self.original_color_mode = image.mode
|
||||
self.image = image.convert("RGB")
|
||||
self.fill = fill
|
||||
self.rotated = False
|
||||
self.orgPath = os.path.join(path[0], path[1])
|
||||
@@ -290,7 +267,7 @@ class ComicPage:
|
||||
if 'N' in mode:
|
||||
self.targetPathOrder = '-kcc-x'
|
||||
elif 'R' in mode:
|
||||
self.targetPathOrder = '-kcc-d'
|
||||
self.targetPathOrder = '-kcc-a' if options.rotatefirst else '-kcc-d'
|
||||
if not options.norotate:
|
||||
self.rotated = True
|
||||
elif 'S1' in mode:
|
||||
@@ -301,11 +278,29 @@ class ComicPage:
|
||||
if not hasattr(Image, 'Resampling'):
|
||||
Image.Resampling = Image
|
||||
|
||||
@cached_property
|
||||
def color(self):
|
||||
if self.original_color_mode in ("L", "1"):
|
||||
return False
|
||||
img = self.image.convert("YCbCr")
|
||||
_, cb, cr = img.split()
|
||||
|
||||
cb_hist = cb.histogram()
|
||||
cr_hist = cr.histogram()
|
||||
cb_nonzero = [i for i, e in enumerate(cb_hist) if e]
|
||||
cr_nonzero = [i for i, e in enumerate(cr_hist) if e]
|
||||
cb_spread = cb_nonzero[-1] - cb_nonzero[0] if len(cb_nonzero) else 0
|
||||
cr_spread = cr_nonzero[-1] - cr_nonzero[0] if len(cr_nonzero) else 0
|
||||
|
||||
SPREAD_THRESHOLD=20
|
||||
if cb_spread < SPREAD_THRESHOLD and cr_spread < SPREAD_THRESHOLD:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def saveToDir(self):
|
||||
try:
|
||||
flags = []
|
||||
if not self.opt.forcecolor and not self.opt.forcepng:
|
||||
self.image = self.image.convert('L')
|
||||
if self.rotated:
|
||||
flags.append('Rotated')
|
||||
if self.fill != 'white':
|
||||
@@ -327,8 +322,12 @@ class ComicPage:
|
||||
def save_with_codec(self, image, targetPath):
|
||||
if self.opt.forcepng:
|
||||
image.info["transparency"] = None
|
||||
targetPath += '.png'
|
||||
image.save(targetPath, 'PNG', optimize=1)
|
||||
if self.opt.iskindle and ('MOBI' in self.opt.format or 'EPUB' in self.opt.format):
|
||||
targetPath += '.gif'
|
||||
image.save(targetPath, 'GIF', optimize=1, interlace=False)
|
||||
else:
|
||||
targetPath += '.png'
|
||||
image.save(targetPath, 'PNG', optimize=1)
|
||||
else:
|
||||
targetPath += '.jpg'
|
||||
if self.opt.mozjpeg:
|
||||
@@ -353,15 +352,16 @@ class ComicPage:
|
||||
else:
|
||||
self.image = ImageOps.autocontrast(Image.eval(self.image, lambda a: int(255 * (a / 255.) ** gamma)))
|
||||
|
||||
def convertToGrayscale(self):
|
||||
self.image = self.image.convert('L')
|
||||
|
||||
def quantizeImage(self):
|
||||
colors = len(self.palette) // 3
|
||||
if colors < 256:
|
||||
self.palette += self.palette[:3] * (256 - colors)
|
||||
# remove all color pixels from image, since colorCheck() has some tolerance
|
||||
# quantize with a small number of color pixels in a mostly b/w image can have unexpected results
|
||||
self.image = self.image.convert("L").convert("RGB")
|
||||
|
||||
palImg = Image.new('P', (1, 1))
|
||||
palImg.putpalette(self.palette)
|
||||
self.image = self.image.convert('L')
|
||||
self.image = self.image.convert('RGB')
|
||||
# Quantize is deprecated but new function call it internally anyway...
|
||||
self.image = self.image.quantize(palette=palImg)
|
||||
|
||||
def optimizeForDisplay(self, reducerainbow):
|
||||
@@ -473,6 +473,9 @@ class Cover:
|
||||
stroke_width=25
|
||||
)
|
||||
copy.save(target, "JPEG", optimize=1, quality=85)
|
||||
dot_cover = Path(target).with_stem('._' + Path(target).stem)
|
||||
if os.path.exists(dot_cover):
|
||||
os.remove(dot_cover)
|
||||
except IOError:
|
||||
raise RuntimeError('Failed to save cover.')
|
||||
|
||||
|
||||
@@ -18,9 +18,7 @@
|
||||
# PERFORMANCE OF THIS SOFTWARE.
|
||||
#
|
||||
|
||||
from functools import lru_cache
|
||||
import os
|
||||
from hashlib import md5
|
||||
from html.parser import HTMLParser
|
||||
import subprocess
|
||||
from packaging.version import Version
|
||||
@@ -50,12 +48,6 @@ class HTMLStripper(HTMLParser):
|
||||
def getImageFileName(imgfile):
|
||||
name, ext = os.path.splitext(imgfile)
|
||||
ext = ext.lower()
|
||||
if (name.startswith('.') and len(name) == 1):
|
||||
return None
|
||||
if name.startswith('._'):
|
||||
return None
|
||||
if ext not in ['.png', '.jpg', '.jpeg', '.gif', '.webp', '.jp2', '.j2k', '.jpx']:
|
||||
return None
|
||||
return [name, ext]
|
||||
|
||||
|
||||
@@ -124,27 +116,14 @@ def dependencyCheck(level):
|
||||
missing.append('python-slugify 1.2.1+')
|
||||
try:
|
||||
from PIL import __version__ as pillowVersion
|
||||
if Version('5.2.0') > Version(pillowVersion):
|
||||
missing.append('Pillow 5.2.0+')
|
||||
if Version('11.3.0') > Version(pillowVersion):
|
||||
missing.append('Pillow 11.3.0+')
|
||||
except ImportError:
|
||||
missing.append('Pillow 5.2.0+')
|
||||
missing.append('Pillow 11.3.0+')
|
||||
if len(missing) > 0:
|
||||
print('ERROR: ' + ', '.join(missing) + ' is not installed!')
|
||||
sys.exit(1)
|
||||
|
||||
@lru_cache
|
||||
def available_archive_tools():
|
||||
available = []
|
||||
|
||||
for tool in ['tar', '7z', 'unar', 'unrar']:
|
||||
try:
|
||||
subprocess_run([tool], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
available.append(tool)
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
|
||||
return available
|
||||
|
||||
def subprocess_run(command, **kwargs):
|
||||
if (os.name == 'nt'):
|
||||
kwargs.setdefault('creationflags', subprocess.CREATE_NO_WINDOW)
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
PySide6>=6.5.1
|
||||
Pillow>=5.2.0
|
||||
Pillow>=11.3.0
|
||||
psutil>=5.9.5
|
||||
requests>=2.31.0
|
||||
python-slugify>=1.2.1
|
||||
raven>=6.0.0
|
||||
packaging>=23.2
|
||||
mozjpeg-lossless-optimization==1.2.0
|
||||
mozjpeg-lossless-optimization>=1.2.0
|
||||
natsort>=8.4.0
|
||||
distro>=1.8.0
|
||||
numpy>=1.22.4
|
||||
|
||||
4
setup.py
4
setup.py
@@ -75,7 +75,7 @@ setuptools.setup(
|
||||
packages=['kindlecomicconverter'],
|
||||
install_requires=[
|
||||
'pyside6>=6.5.1',
|
||||
'Pillow>=5.2.0',
|
||||
'Pillow>=11.3.0',
|
||||
'psutil>=5.9.5',
|
||||
'python-slugify>=1.2.1,<9.0.0',
|
||||
'raven>=6.0.0',
|
||||
@@ -83,7 +83,7 @@ setuptools.setup(
|
||||
'mozjpeg-lossless-optimization>=1.1.2',
|
||||
'natsort>=8.4.0',
|
||||
'distro',
|
||||
'numpy>=1.22.4,<2.0.0'
|
||||
'numpy>=1.22.4'
|
||||
],
|
||||
classifiers=[],
|
||||
zip_safe=False,
|
||||
|
||||
Reference in New Issue
Block a user