mirror of
https://github.com/Yuvi9587/Kemono-Downloader.git
synced 2025-12-29 16:14:44 +00:00
Commit
This commit is contained in:
122
src/ui/dialogs/KeepDuplicatesDialog.py
Normal file
122
src/ui/dialogs/KeepDuplicatesDialog.py
Normal file
@@ -0,0 +1,122 @@
|
||||
# KeepDuplicatesDialog.py
|
||||
|
||||
# --- PyQt5 Imports ---
|
||||
from PyQt5.QtWidgets import (
|
||||
QDialog, QVBoxLayout, QGroupBox, QRadioButton,
|
||||
QPushButton, QHBoxLayout, QButtonGroup, QLabel, QLineEdit
|
||||
)
|
||||
from PyQt5.QtGui import QIntValidator
|
||||
|
||||
# --- Local Application Imports ---
|
||||
from ...i18n.translator import get_translation
|
||||
from ...config.constants import DUPLICATE_HANDLING_HASH, DUPLICATE_HANDLING_KEEP_ALL
|
||||
|
||||
class KeepDuplicatesDialog(QDialog):
|
||||
"""A dialog to choose the duplicate handling method, with a limit option."""
|
||||
|
||||
def __init__(self, current_mode, current_limit, parent=None):
|
||||
super().__init__(parent)
|
||||
self.parent_app = parent
|
||||
self.selected_mode = current_mode
|
||||
self.limit = current_limit
|
||||
|
||||
self._init_ui()
|
||||
self._retranslate_ui()
|
||||
|
||||
if self.parent_app and hasattr(self.parent_app, '_apply_theme_to_widget'):
|
||||
self.parent_app._apply_theme_to_widget(self)
|
||||
|
||||
# Set the initial state based on current settings
|
||||
if current_mode == DUPLICATE_HANDLING_KEEP_ALL:
|
||||
self.radio_keep_everything.setChecked(True)
|
||||
self.limit_input.setText(str(current_limit) if current_limit > 0 else "")
|
||||
else:
|
||||
self.radio_skip_by_hash.setChecked(True)
|
||||
self.limit_input.setEnabled(False)
|
||||
|
||||
def _init_ui(self):
|
||||
"""Initializes the UI components."""
|
||||
main_layout = QVBoxLayout(self)
|
||||
info_label = QLabel()
|
||||
info_label.setWordWrap(True)
|
||||
main_layout.addWidget(info_label)
|
||||
|
||||
options_group = QGroupBox()
|
||||
options_layout = QVBoxLayout(options_group)
|
||||
self.button_group = QButtonGroup(self)
|
||||
|
||||
# --- Skip by Hash Option ---
|
||||
self.radio_skip_by_hash = QRadioButton()
|
||||
self.button_group.addButton(self.radio_skip_by_hash)
|
||||
options_layout.addWidget(self.radio_skip_by_hash)
|
||||
|
||||
# --- Keep Everything Option with Limit Input ---
|
||||
keep_everything_layout = QHBoxLayout()
|
||||
self.radio_keep_everything = QRadioButton()
|
||||
self.button_group.addButton(self.radio_keep_everything)
|
||||
keep_everything_layout.addWidget(self.radio_keep_everything)
|
||||
keep_everything_layout.addStretch(1)
|
||||
|
||||
self.limit_label = QLabel()
|
||||
self.limit_input = QLineEdit()
|
||||
self.limit_input.setValidator(QIntValidator(0, 99))
|
||||
self.limit_input.setFixedWidth(50)
|
||||
keep_everything_layout.addWidget(self.limit_label)
|
||||
keep_everything_layout.addWidget(self.limit_input)
|
||||
options_layout.addLayout(keep_everything_layout)
|
||||
|
||||
main_layout.addWidget(options_group)
|
||||
|
||||
# --- OK and Cancel buttons ---
|
||||
button_layout = QHBoxLayout()
|
||||
self.ok_button = QPushButton()
|
||||
self.cancel_button = QPushButton()
|
||||
button_layout.addStretch(1)
|
||||
button_layout.addWidget(self.ok_button)
|
||||
button_layout.addWidget(self.cancel_button)
|
||||
main_layout.addLayout(button_layout)
|
||||
|
||||
# --- Connections ---
|
||||
self.ok_button.clicked.connect(self.accept)
|
||||
self.cancel_button.clicked.connect(self.reject)
|
||||
self.radio_keep_everything.toggled.connect(self.limit_input.setEnabled)
|
||||
|
||||
def _tr(self, key, default_text=""):
|
||||
if self.parent_app and callable(get_translation):
|
||||
return get_translation(self.parent_app.current_selected_language, key, default_text)
|
||||
return default_text
|
||||
|
||||
def _retranslate_ui(self):
|
||||
"""Sets the text for UI elements."""
|
||||
self.setWindowTitle(self._tr("duplicates_dialog_title", "Duplicate Handling Options"))
|
||||
self.findChild(QLabel).setText(self._tr("duplicates_dialog_info",
|
||||
"Choose how to handle files that have identical content to already downloaded files."))
|
||||
self.findChild(QGroupBox).setTitle(self._tr("duplicates_dialog_group_title", "Mode"))
|
||||
|
||||
self.radio_skip_by_hash.setText(self._tr("duplicates_dialog_skip_hash", "Skip by Hash (Recommended)"))
|
||||
self.radio_keep_everything.setText(self._tr("duplicates_dialog_keep_all", "Keep Everything"))
|
||||
|
||||
self.limit_label.setText(self._tr("duplicates_limit_label", "Limit:"))
|
||||
self.limit_input.setPlaceholderText(self._tr("duplicates_limit_placeholder", "0=all"))
|
||||
self.limit_input.setToolTip(self._tr("duplicates_limit_tooltip",
|
||||
"Set a limit for identical files to keep. 0 means no limit."))
|
||||
|
||||
self.ok_button.setText(self._tr("ok_button", "OK"))
|
||||
self.cancel_button.setText(self._tr("cancel_button_text_simple", "Cancel"))
|
||||
|
||||
def accept(self):
|
||||
"""Sets the selected mode and limit when OK is clicked."""
|
||||
if self.radio_keep_everything.isChecked():
|
||||
self.selected_mode = DUPLICATE_HANDLING_KEEP_ALL
|
||||
try:
|
||||
self.limit = int(self.limit_input.text()) if self.limit_input.text() else 0
|
||||
except ValueError:
|
||||
self.limit = 0
|
||||
else:
|
||||
self.selected_mode = DUPLICATE_HANDLING_HASH
|
||||
self.limit = 0
|
||||
super().accept()
|
||||
|
||||
def get_selected_options(self):
|
||||
"""Returns the chosen mode and limit as a dictionary."""
|
||||
return {"mode": self.selected_mode, "limit": self.limit}
|
||||
@@ -1,14 +1,35 @@
|
||||
# src/ui/dialogs/SupportDialog.py
|
||||
|
||||
from PyQt5.QtWidgets import (
|
||||
QDialog, QVBoxLayout, QLabel, QFrame, QDialogButtonBox
|
||||
)
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtGui import QFont
|
||||
# --- Standard Library Imports ---
|
||||
import sys
|
||||
import os
|
||||
|
||||
# Assuming execution from project root, so we can import from utils
|
||||
# --- PyQt5 Imports ---
|
||||
from PyQt5.QtWidgets import (
|
||||
QDialog, QVBoxLayout, QLabel, QFrame, QDialogButtonBox, QGridLayout
|
||||
)
|
||||
from PyQt5.QtCore import Qt, QSize
|
||||
from PyQt5.QtGui import QFont, QPixmap
|
||||
|
||||
# --- Local Application Imports ---
|
||||
from ...utils.resolution import get_dark_theme
|
||||
|
||||
# --- Helper function for robust asset loading ---
|
||||
def get_asset_path(filename):
|
||||
"""
|
||||
Gets the absolute path to a file in the assets folder,
|
||||
handling both development and frozen (PyInstaller) environments.
|
||||
"""
|
||||
if getattr(sys, 'frozen', False) and hasattr(sys, '_MEIPASS'):
|
||||
# Running in a PyInstaller bundle
|
||||
base_path = sys._MEIPASS
|
||||
else:
|
||||
# Running in a normal Python environment from src/ui/dialogs/
|
||||
base_path = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
return os.path.join(base_path, 'assets', filename)
|
||||
|
||||
|
||||
class SupportDialog(QDialog):
|
||||
"""
|
||||
A dialog to show support and donation options.
|
||||
@@ -17,11 +38,16 @@ class SupportDialog(QDialog):
|
||||
super().__init__(parent)
|
||||
self.parent_app = parent
|
||||
self.setWindowTitle("❤️ Support the Developer")
|
||||
self.setMinimumWidth(400)
|
||||
self.setMinimumWidth(450)
|
||||
|
||||
self._init_ui()
|
||||
self._apply_theme()
|
||||
|
||||
def _init_ui(self):
|
||||
"""Initializes all UI components and layouts for the dialog."""
|
||||
# Main layout
|
||||
layout = QVBoxLayout(self)
|
||||
layout.setSpacing(15)
|
||||
main_layout = QVBoxLayout(self)
|
||||
main_layout.setSpacing(15)
|
||||
|
||||
# Title Label
|
||||
title_label = QLabel("Thank You for Your Support!")
|
||||
@@ -30,7 +56,7 @@ class SupportDialog(QDialog):
|
||||
font.setBold(True)
|
||||
title_label.setFont(font)
|
||||
title_label.setAlignment(Qt.AlignCenter)
|
||||
layout.addWidget(title_label)
|
||||
main_layout.addWidget(title_label)
|
||||
|
||||
# Informational Text
|
||||
info_label = QLabel(
|
||||
@@ -39,50 +65,86 @@ class SupportDialog(QDialog):
|
||||
)
|
||||
info_label.setWordWrap(True)
|
||||
info_label.setAlignment(Qt.AlignCenter)
|
||||
layout.addWidget(info_label)
|
||||
main_layout.addWidget(info_label)
|
||||
|
||||
# Separator
|
||||
line = QFrame()
|
||||
line.setFrameShape(QFrame.HLine)
|
||||
line.setFrameShadow(QFrame.Sunken)
|
||||
layout.addWidget(line)
|
||||
main_layout.addWidget(line)
|
||||
|
||||
# Donation Options
|
||||
options_layout = QVBoxLayout()
|
||||
options_layout.setSpacing(10)
|
||||
# --- Donation Options Layout (using a grid for icons and text) ---
|
||||
options_layout = QGridLayout()
|
||||
options_layout.setSpacing(18)
|
||||
options_layout.setColumnStretch(0, 1) # Add stretch to center the content horizontally
|
||||
options_layout.setColumnStretch(3, 1)
|
||||
|
||||
link_font = self.font()
|
||||
link_font.setPointSize(12)
|
||||
link_font.setBold(True)
|
||||
|
||||
scale = getattr(self.parent_app, 'scale_factor', 1.0)
|
||||
icon_size = int(32 * scale)
|
||||
|
||||
# --- Ko-fi ---
|
||||
kofi_label = QLabel(
|
||||
kofi_icon_label = QLabel()
|
||||
kofi_pixmap = QPixmap(get_asset_path("kofi.png"))
|
||||
if not kofi_pixmap.isNull():
|
||||
kofi_icon_label.setPixmap(kofi_pixmap.scaled(QSize(icon_size, icon_size), Qt.KeepAspectRatio, Qt.SmoothTransformation))
|
||||
|
||||
kofi_text_label = QLabel(
|
||||
'<a href="https://ko-fi.com/yuvi427183" style="color: #13C2C2; text-decoration: none;">'
|
||||
'☕ Buy me a Ko-fi'
|
||||
'</a>'
|
||||
)
|
||||
kofi_label.setOpenExternalLinks(True)
|
||||
kofi_label.setAlignment(Qt.AlignCenter)
|
||||
font.setPointSize(12)
|
||||
kofi_label.setFont(font)
|
||||
options_layout.addWidget(kofi_label)
|
||||
kofi_text_label.setOpenExternalLinks(True)
|
||||
kofi_text_label.setFont(link_font)
|
||||
|
||||
options_layout.addWidget(kofi_icon_label, 0, 1, Qt.AlignRight | Qt.AlignVCenter)
|
||||
options_layout.addWidget(kofi_text_label, 0, 2, Qt.AlignLeft | Qt.AlignVCenter)
|
||||
|
||||
# --- GitHub Sponsors ---
|
||||
github_label = QLabel(
|
||||
'<a href="https://github.com/sponsors/Yuvi9587" style="color: #C9D1D9; text-decoration: none;">'
|
||||
github_icon_label = QLabel()
|
||||
github_pixmap = QPixmap(get_asset_path("github_sponsors.png"))
|
||||
if not github_pixmap.isNull():
|
||||
github_icon_label.setPixmap(github_pixmap.scaled(QSize(icon_size, icon_size), Qt.KeepAspectRatio, Qt.SmoothTransformation))
|
||||
|
||||
github_text_label = QLabel(
|
||||
'<a href="https://github.com/sponsors/Yuvi9587" style="color: #EA4AAA; text-decoration: none;">'
|
||||
'💜 Sponsor on GitHub'
|
||||
'</a>'
|
||||
)
|
||||
github_label.setOpenExternalLinks(True)
|
||||
github_label.setAlignment(Qt.AlignCenter)
|
||||
github_label.setFont(font)
|
||||
options_layout.addWidget(github_label)
|
||||
github_text_label.setOpenExternalLinks(True)
|
||||
github_text_label.setFont(link_font)
|
||||
|
||||
layout.addLayout(options_layout)
|
||||
options_layout.addWidget(github_icon_label, 1, 1, Qt.AlignRight | Qt.AlignVCenter)
|
||||
options_layout.addWidget(github_text_label, 1, 2, Qt.AlignLeft | Qt.AlignVCenter)
|
||||
|
||||
# --- Buy Me a Coffee (New) ---
|
||||
bmac_icon_label = QLabel()
|
||||
bmac_pixmap = QPixmap(get_asset_path("bmac.png"))
|
||||
if not bmac_pixmap.isNull():
|
||||
bmac_icon_label.setPixmap(bmac_pixmap.scaled(QSize(icon_size, icon_size), Qt.KeepAspectRatio, Qt.SmoothTransformation))
|
||||
|
||||
bmac_text_label = QLabel(
|
||||
'<a href="https://buymeacoffee.com/yuvi9587" style="color: #FFDD00; text-decoration: none;">'
|
||||
'🍺 Buy Me a Coffee'
|
||||
'</a>'
|
||||
)
|
||||
bmac_text_label.setOpenExternalLinks(True)
|
||||
bmac_text_label.setFont(link_font)
|
||||
|
||||
options_layout.addWidget(bmac_icon_label, 2, 1, Qt.AlignRight | Qt.AlignVCenter)
|
||||
options_layout.addWidget(bmac_text_label, 2, 2, Qt.AlignLeft | Qt.AlignVCenter)
|
||||
|
||||
main_layout.addLayout(options_layout)
|
||||
|
||||
# Close Button
|
||||
self.button_box = QDialogButtonBox(QDialogButtonBox.Close)
|
||||
self.button_box.rejected.connect(self.reject)
|
||||
layout.addWidget(self.button_box)
|
||||
main_layout.addWidget(self.button_box)
|
||||
|
||||
self.setLayout(layout)
|
||||
self._apply_theme()
|
||||
self.setLayout(main_layout)
|
||||
|
||||
def _apply_theme(self):
|
||||
"""Applies the current theme from the parent application."""
|
||||
@@ -90,4 +152,4 @@ class SupportDialog(QDialog):
|
||||
scale = getattr(self.parent_app, 'scale_factor', 1)
|
||||
self.setStyleSheet(get_dark_theme(scale))
|
||||
else:
|
||||
self.setStyleSheet("")
|
||||
self.setStyleSheet("")
|
||||
@@ -12,7 +12,7 @@ import subprocess
|
||||
import datetime
|
||||
import requests
|
||||
import unicodedata
|
||||
from collections import deque
|
||||
from collections import deque, defaultdict
|
||||
import threading
|
||||
from concurrent.futures import Future, ThreadPoolExecutor ,CancelledError
|
||||
from urllib .parse import urlparse
|
||||
@@ -57,6 +57,7 @@ from .dialogs.ConfirmAddAllDialog import ConfirmAddAllDialog
|
||||
from .dialogs.MoreOptionsDialog import MoreOptionsDialog
|
||||
from .dialogs.SinglePDF import create_single_pdf_from_content
|
||||
from .dialogs.SupportDialog import SupportDialog
|
||||
from .dialogs.KeepDuplicatesDialog import KeepDuplicatesDialog
|
||||
|
||||
class DynamicFilterHolder:
|
||||
"""A thread-safe class to hold and update character filters during a download."""
|
||||
@@ -223,6 +224,10 @@ class DownloaderApp (QWidget ):
|
||||
self.more_filter_scope = None
|
||||
self.text_export_format = 'pdf'
|
||||
self.single_pdf_setting = False
|
||||
self.keep_duplicates_mode = DUPLICATE_HANDLING_HASH
|
||||
self.keep_duplicates_limit = 0 # 0 means no limit
|
||||
self.downloaded_hash_counts = defaultdict(int)
|
||||
self.downloaded_hash_counts_lock = threading.Lock()
|
||||
self.session_temp_files = []
|
||||
|
||||
print(f"ℹ️ Known.txt will be loaded/saved at: {self.config_file}")
|
||||
@@ -695,6 +700,8 @@ class DownloaderApp (QWidget ):
|
||||
self .cookie_text_input .textChanged .connect (self ._handle_cookie_text_manual_change )
|
||||
if hasattr (self ,'download_thumbnails_checkbox'):
|
||||
self .download_thumbnails_checkbox .toggled .connect (self ._handle_thumbnail_mode_change )
|
||||
if hasattr(self, 'keep_duplicates_checkbox'):
|
||||
self.keep_duplicates_checkbox.toggled.connect(self._handle_keep_duplicates_toggled)
|
||||
self .gui_update_timer .timeout .connect (self ._process_worker_queue )
|
||||
self .gui_update_timer .start (100 )
|
||||
self .log_signal .connect (self .handle_main_log )
|
||||
@@ -2628,7 +2635,8 @@ class DownloaderApp (QWidget ):
|
||||
self .file_progress_label .setText ("")
|
||||
|
||||
def start_download(self, direct_api_url=None, override_output_dir=None, is_restore=False):
|
||||
self.is_finishing = False
|
||||
self.is_finishing = False
|
||||
self.downloaded_hash_counts.clear()
|
||||
global KNOWN_NAMES, BackendDownloadThread, PostProcessorWorker, extract_post_info, clean_folder_name, MAX_FILE_THREADS_PER_POST_OR_WORKER
|
||||
|
||||
self._clear_stale_temp_files()
|
||||
@@ -3071,7 +3079,6 @@ class DownloaderApp (QWidget ):
|
||||
else:
|
||||
log_messages.append(f" Mode: Creator Feed")
|
||||
log_messages.append(f" Post Processing: {'Multi-threaded (' + str(effective_num_post_workers) + ' workers)' if effective_num_post_workers > 1 else 'Single-threaded (1 worker)'}")
|
||||
log_messages.append(f" ↳ File Downloads per Worker: Up to {effective_num_file_threads_per_worker} concurrent file(s)")
|
||||
pr_log = "All"
|
||||
if start_page or end_page:
|
||||
pr_log = f"{f'From {start_page} ' if start_page else ''}{'to ' if start_page and end_page else ''}{f'{end_page}' if end_page else (f'Up to {end_page}' if end_page else (f'From {start_page}' if start_page else 'Specific Range'))}".strip()
|
||||
@@ -3192,7 +3199,11 @@ class DownloaderApp (QWidget ):
|
||||
'session_lock': self.session_lock,
|
||||
'creator_download_folder_ignore_words': creator_folder_ignore_words_for_run,
|
||||
'use_date_prefix_for_subfolder': self.date_prefix_checkbox.isChecked() if hasattr(self, 'date_prefix_checkbox') else False,
|
||||
'keep_in_post_duplicates': self.keep_duplicates_checkbox.isChecked() if hasattr(self, 'keep_duplicates_checkbox') else False,
|
||||
'keep_in_post_duplicates': self.keep_duplicates_checkbox.isChecked(),
|
||||
'keep_duplicates_mode': self.keep_duplicates_mode,
|
||||
'keep_duplicates_limit': self.keep_duplicates_limit,
|
||||
'downloaded_hash_counts': self.downloaded_hash_counts,
|
||||
'downloaded_hash_counts_lock': self.downloaded_hash_counts_lock,
|
||||
'skip_current_file_flag': None,
|
||||
'processed_post_ids': processed_post_ids_for_restore,
|
||||
}
|
||||
@@ -3222,6 +3233,8 @@ class DownloaderApp (QWidget ):
|
||||
'allow_multipart_download', 'use_cookie', 'cookie_text', 'app_base_dir', 'selected_cookie_file', 'override_output_dir', 'project_root_dir',
|
||||
'text_only_scope', 'text_export_format',
|
||||
'single_pdf_mode',
|
||||
'use_date_prefix_for_subfolder','keep_in_post_duplicates', 'keep_duplicates_mode',
|
||||
'keep_duplicates_limit', 'downloaded_hash_counts', 'downloaded_hash_counts_lock',
|
||||
'processed_post_ids'
|
||||
]
|
||||
args_template['skip_current_file_flag'] = None
|
||||
@@ -3494,9 +3507,9 @@ class DownloaderApp (QWidget ):
|
||||
'skip_current_file_flag','manga_date_file_counter_ref','scan_content_for_images',
|
||||
'manga_mode_active','manga_filename_style','manga_date_prefix','text_only_scope',
|
||||
'text_export_format', 'single_pdf_mode',
|
||||
'use_date_prefix_for_subfolder','keep_in_post_duplicates','manga_global_file_counter_ref',
|
||||
'use_date_prefix_for_subfolder','keep_in_post_duplicates','keep_duplicates_mode','manga_global_file_counter_ref',
|
||||
'creator_download_folder_ignore_words','session_file_path','project_root_dir','session_lock',
|
||||
'processed_post_ids' # This key was missing
|
||||
'processed_post_ids', 'keep_duplicates_limit', 'downloaded_hash_counts', 'downloaded_hash_counts_lock'
|
||||
]
|
||||
|
||||
num_file_dl_threads_for_each_worker = worker_args_template.get('num_file_threads_for_worker', 1)
|
||||
@@ -3537,7 +3550,7 @@ class DownloaderApp (QWidget ):
|
||||
|
||||
if permanent:
|
||||
self.permanently_failed_files_for_dialog.extend(permanent)
|
||||
self._update_error_button_count() # <-- THIS IS THE FIX
|
||||
self._update_error_button_count()
|
||||
|
||||
# Other result handling
|
||||
if history_data: self._add_to_history_candidates(history_data)
|
||||
@@ -3676,7 +3689,7 @@ class DownloaderApp (QWidget ):
|
||||
self .external_links_checkbox ,self .manga_mode_checkbox ,self .manga_rename_toggle_button ,self .use_cookie_checkbox ,self .cookie_text_input ,self .cookie_browse_button ,
|
||||
self .multipart_toggle_button ,self .radio_only_audio ,
|
||||
self .character_search_input ,self .new_char_input ,self .add_char_button ,self .add_to_filter_button ,self .delete_char_button ,
|
||||
self .reset_button
|
||||
self .reset_button, self.radio_more, self.keep_duplicates_checkbox
|
||||
]
|
||||
|
||||
widgets_to_enable_on_pause =self ._get_configurable_widgets_on_pause ()
|
||||
@@ -4063,6 +4076,42 @@ class DownloaderApp (QWidget ):
|
||||
self .set_ui_enabled (True )
|
||||
self .cancellation_message_logged_this_session =False
|
||||
|
||||
def _handle_keep_duplicates_toggled(self, checked):
|
||||
"""Shows the duplicate handling dialog when the checkbox is checked."""
|
||||
if checked:
|
||||
dialog = KeepDuplicatesDialog(self.keep_duplicates_mode, self.keep_duplicates_limit, self)
|
||||
if dialog.exec_() == QDialog.Accepted:
|
||||
options = dialog.get_selected_options()
|
||||
self.keep_duplicates_mode = options["mode"]
|
||||
self.keep_duplicates_limit = options["limit"]
|
||||
|
||||
limit_text = f"with a limit of {self.keep_duplicates_limit}" if self.keep_duplicates_limit > 0 else "with no limit"
|
||||
self.log_signal.emit(f"ℹ️ Duplicate handling mode set to: '{self.keep_duplicates_mode}' {limit_text}.")
|
||||
self.log_signal.emit(f"")
|
||||
self.log_signal.emit(f"")
|
||||
|
||||
# Log warning only after the confirmation and only if the specific mode is selected
|
||||
if self.keep_duplicates_mode == DUPLICATE_HANDLING_KEEP_ALL:
|
||||
self._log_keep_everything_warning()
|
||||
else:
|
||||
self.keep_duplicates_checkbox.setChecked(False)
|
||||
else:
|
||||
self.keep_duplicates_mode = DUPLICATE_HANDLING_HASH
|
||||
self.keep_duplicates_limit = 0
|
||||
self.log_signal.emit("ℹ️ 'Keep Duplicates' disabled. Reverted to default hash checking.")
|
||||
|
||||
def _log_keep_everything_warning(self):
|
||||
"""Logs a formatted warning when the 'Keep Everything' mode is selected."""
|
||||
|
||||
warning_html = (
|
||||
f'{HTML_PREFIX}'
|
||||
'<h2 style="margin-top: 8px; margin-bottom: 4px; font-weight: bold;">⚠️ ATTENTION: "Keep Everything" Enabled</h2>'
|
||||
'<h3><p style="margin-top: 0; margin-bottom: 4px;">This mode will download every single file from the API response for a post,</p>'
|
||||
'<p style="margin-top: 0; margin-bottom: 4px;">even if they have identical content. This can lead to many redundant files.</p>'
|
||||
'<p style="margin-top: 0; margin-bottom: 4px;"><b>Recommendation:</b> Consider using the <b>limit feature</b>.</p>'
|
||||
'<p style="margin-top: 0; margin-bottom: 0;">For example, setting the limit to <b>2</b> will download a file with the same content up to two times.</p></h3>'
|
||||
)
|
||||
self.log_signal.emit(warning_html)
|
||||
|
||||
def _handle_thumbnail_mode_change (self ,thumbnails_checked ):
|
||||
"""Handles UI changes when 'Download Thumbnails Only' is toggled."""
|
||||
@@ -4266,9 +4315,7 @@ class DownloaderApp (QWidget ):
|
||||
if self .progress_log_label :self .progress_log_label .setText (self ._tr ("progress_log_label_text","📜 Progress Log:"))
|
||||
|
||||
def reset_application_state(self):
|
||||
# --- Stop all background tasks and threads ---
|
||||
if self._is_download_active():
|
||||
# Try to cancel download thread
|
||||
if self.download_thread and self.download_thread.isRunning():
|
||||
self.log_signal.emit("⚠️ Cancelling active download thread for reset...")
|
||||
self.cancellation_event.set()
|
||||
@@ -4308,6 +4355,14 @@ class DownloaderApp (QWidget ):
|
||||
if self.pause_event:
|
||||
self.pause_event.clear()
|
||||
self.is_paused = False
|
||||
|
||||
self.log_signal.emit("🔄 Resetting application state to defaults...")
|
||||
self._clear_session_file()
|
||||
self._reset_ui_to_defaults()
|
||||
self._load_saved_download_location()
|
||||
self.main_log_output.clear()
|
||||
self.external_log_output.clear()
|
||||
|
||||
|
||||
# --- Reset UI and all state ---
|
||||
self.log_signal.emit("🔄 Resetting application state to defaults...")
|
||||
@@ -4407,6 +4462,10 @@ class DownloaderApp (QWidget ):
|
||||
self.use_multithreading_checkbox.setChecked(True)
|
||||
if self.favorite_mode_checkbox:
|
||||
self.favorite_mode_checkbox.setChecked(False)
|
||||
|
||||
if hasattr(self, 'keep_duplicates_checkbox'):
|
||||
self.keep_duplicates_checkbox.setChecked(False)
|
||||
|
||||
self.external_links_checkbox.setChecked(False)
|
||||
if self.manga_mode_checkbox:
|
||||
self.manga_mode_checkbox.setChecked(False)
|
||||
@@ -4451,7 +4510,6 @@ class DownloaderApp (QWidget ):
|
||||
if self.pause_event:
|
||||
self.pause_event.clear()
|
||||
|
||||
# Reset extracted/external links state
|
||||
self.external_link_queue.clear()
|
||||
self.extracted_links_cache = []
|
||||
self._is_processing_external_link_queue = False
|
||||
|
||||
Reference in New Issue
Block a user