mirror of
https://github.com/Yuvi9587/Kemono-Downloader.git
synced 2025-12-29 16:14:44 +00:00
Commit
This commit is contained in:
169
main.py
169
main.py
@@ -66,6 +66,10 @@ except Exception as e:
|
||||
sys.exit(1)
|
||||
# --- End Import ---
|
||||
|
||||
# --- Constants for Thread Limits ---
|
||||
MAX_THREADS = 200 # Absolute maximum allowed by the input validator
|
||||
RECOMMENDED_MAX_THREADS = 50 # Threshold for showing the informational warning
|
||||
# --- END ---
|
||||
|
||||
class DownloaderApp(QWidget):
|
||||
character_prompt_response_signal = pyqtSignal(bool)
|
||||
@@ -104,10 +108,10 @@ class DownloaderApp(QWidget):
|
||||
self._is_processing_external_link_queue = False
|
||||
# --- END ---
|
||||
|
||||
# --- ADDED: For Log Verbosity ---
|
||||
self.basic_log_mode = False # Start with full log
|
||||
# --- For Log Verbosity ---
|
||||
self.basic_log_mode = False # Start with full log (basic_log_mode is False)
|
||||
self.log_verbosity_button = None
|
||||
# --- END ADDED ---
|
||||
# --- END ---
|
||||
|
||||
self.main_log_output = None
|
||||
self.external_log_output = None
|
||||
@@ -118,7 +122,7 @@ class DownloaderApp(QWidget):
|
||||
self.manga_mode_checkbox = None
|
||||
|
||||
self.load_known_names_from_util()
|
||||
self.setWindowTitle("Kemono Downloader v3.0.0")
|
||||
self.setWindowTitle("Kemono Downloader v2.9 (Manga Mode - No Skip Button)")
|
||||
self.setGeometry(150, 150, 1050, 820) # Initial size
|
||||
self.setStyleSheet(self.get_dark_theme())
|
||||
self.init_ui()
|
||||
@@ -154,6 +158,10 @@ class DownloaderApp(QWidget):
|
||||
self.thread_count_input.textChanged.connect(self.update_multithreading_label)
|
||||
self.use_subfolder_per_post_checkbox.toggled.connect(self.update_ui_for_subfolders)
|
||||
|
||||
# --- MODIFIED: Connect multithreading checkbox toggle ---
|
||||
self.use_multithreading_checkbox.toggled.connect(self._handle_multithreading_toggle)
|
||||
# --- END MODIFIED ---
|
||||
|
||||
if self.reset_button:
|
||||
self.reset_button.clicked.connect(self.reset_application_state)
|
||||
|
||||
@@ -414,8 +422,10 @@ class DownloaderApp(QWidget):
|
||||
self.thread_count_input = QLineEdit()
|
||||
self.thread_count_input.setFixedWidth(40)
|
||||
self.thread_count_input.setText("4")
|
||||
self.thread_count_input.setToolTip("Number of threads (recommended: 4-10, max: 200).")
|
||||
self.thread_count_input.setValidator(QIntValidator(1,200)) # Min 1, Max 200
|
||||
# --- MODIFIED: Updated tooltip to remove recommendation ---
|
||||
self.thread_count_input.setToolTip(f"Number of threads (max: {MAX_THREADS}).")
|
||||
# --- END MODIFIED ---
|
||||
self.thread_count_input.setValidator(QIntValidator(1, MAX_THREADS)) # Use constant
|
||||
multithreading_layout.addWidget(self.thread_count_input)
|
||||
advanced_row2_layout.addLayout(multithreading_layout)
|
||||
|
||||
@@ -481,7 +491,7 @@ class DownloaderApp(QWidget):
|
||||
log_title_layout.addStretch(1)
|
||||
|
||||
# --- ADDED: Log Verbosity Button ---
|
||||
self.log_verbosity_button = QPushButton("Show Basic Log")
|
||||
self.log_verbosity_button = QPushButton("Show Basic Log") # Default text
|
||||
self.log_verbosity_button.setToolTip("Toggle between full and basic log details.")
|
||||
self.log_verbosity_button.setFixedWidth(110) # Adjust width as needed
|
||||
self.log_verbosity_button.setStyleSheet("padding: 4px 8px;")
|
||||
@@ -534,9 +544,9 @@ class DownloaderApp(QWidget):
|
||||
self.main_splitter.addWidget(right_panel_widget)
|
||||
|
||||
# --- Set initial sizes for the splitter ---
|
||||
# Calculate initial sizes (e.g., left 35%, right 65%)
|
||||
# Calculate initial sizes (e.g., left 30%, right 70%)
|
||||
initial_width = self.width() # Use the initial window width
|
||||
left_width = int(initial_width * 0.35)
|
||||
left_width = int(initial_width * 0.30)
|
||||
right_width = initial_width - left_width
|
||||
self.main_splitter.setSizes([left_width, right_width])
|
||||
|
||||
@@ -559,6 +569,7 @@ class DownloaderApp(QWidget):
|
||||
self.update_ui_for_manga_mode(self.manga_mode_checkbox.isChecked())
|
||||
self.link_input.textChanged.connect(self.update_page_range_enabled_state) # Connect after init
|
||||
self.load_known_names_from_util() # Load names into the list widget
|
||||
self._handle_multithreading_toggle(self.use_multithreading_checkbox.isChecked()) # Set initial state
|
||||
|
||||
|
||||
def get_dark_theme(self):
|
||||
@@ -591,15 +602,25 @@ class DownloaderApp(QWidget):
|
||||
# --- ADDED: Log Verbosity Filtering ---
|
||||
if self.basic_log_mode:
|
||||
# Define keywords/prefixes for messages to ALWAYS show in basic mode
|
||||
# Make these lowercase for case-insensitive matching
|
||||
basic_keywords = [
|
||||
'🚀 Starting Download', '🏁 Download Finished', '🏁 Download Cancelled',
|
||||
'❌', '⚠️', '✅ All posts processed', '✅ Reached end of posts',
|
||||
'Summary:', 'Progress:', '[Fetcher]', # Show fetcher logs for context
|
||||
'CRITICAL ERROR', 'IMPORT ERROR'
|
||||
'🚀 starting download', '🏁 download finished', '🏁 download cancelled',
|
||||
'❌', '⚠️', '✅ all posts processed', '✅ reached end of posts',
|
||||
'summary:', 'progress:', '[fetcher]', # Show fetcher logs for context
|
||||
'critical error', 'import error', 'error', 'fail', 'timeout',
|
||||
'unsupported url', 'invalid url', 'no posts found', 'could not create directory',
|
||||
'missing dependency', 'high thread count', 'manga mode filter warning',
|
||||
'duplicate name', 'potential name conflict', 'invalid filter name',
|
||||
'no valid character filters'
|
||||
]
|
||||
# Check if the message contains any of the basic keywords/prefixes
|
||||
if not any(keyword in message for keyword in basic_keywords):
|
||||
return # Skip appending this message in basic mode
|
||||
# Check if the lowercase message contains any of the basic keywords/prefixes
|
||||
message_lower = message.lower()
|
||||
if not any(keyword in message_lower for keyword in basic_keywords):
|
||||
# Allow specific positive confirmations even in basic mode
|
||||
if not message.strip().startswith("✅ Saved:") and \
|
||||
not message.strip().startswith("✅ Added") and \
|
||||
not message.strip().startswith("✅ Application reset complete"):
|
||||
return # Skip appending less important messages in basic mode
|
||||
# --- END ADDED ---
|
||||
|
||||
try:
|
||||
@@ -909,14 +930,35 @@ class DownloaderApp(QWidget):
|
||||
item.setHidden(search_text_lower not in item.text().lower())
|
||||
|
||||
def update_multithreading_label(self, text):
|
||||
try:
|
||||
num_threads = int(text)
|
||||
if num_threads > 0 :
|
||||
self.use_multithreading_checkbox.setText(f"Use Multithreading ({num_threads} Threads)")
|
||||
else: # Should be caught by validator, but defensive
|
||||
self.use_multithreading_checkbox.setText("Use Multithreading (Invalid: >0)")
|
||||
except ValueError: # If text is not a valid integer
|
||||
self.use_multithreading_checkbox.setText("Use Multithreading (Invalid Input)")
|
||||
# This method only updates the checkbox label text
|
||||
# The actual enabling/disabling is handled by _handle_multithreading_toggle
|
||||
if self.use_multithreading_checkbox.isChecked():
|
||||
try:
|
||||
num_threads = int(text)
|
||||
if num_threads > 0 :
|
||||
self.use_multithreading_checkbox.setText(f"Use Multithreading ({num_threads} Threads)")
|
||||
else:
|
||||
self.use_multithreading_checkbox.setText("Use Multithreading (Invalid: >0)")
|
||||
except ValueError:
|
||||
self.use_multithreading_checkbox.setText("Use Multithreading (Invalid Input)")
|
||||
else:
|
||||
self.use_multithreading_checkbox.setText("Use Multithreading (1 Thread)") # Show 1 thread when disabled
|
||||
|
||||
# --- ADDED: Handler for multithreading checkbox toggle ---
|
||||
def _handle_multithreading_toggle(self, checked):
|
||||
"""Handles enabling/disabling the thread count input."""
|
||||
if not checked:
|
||||
# Unchecked: Set to 1 and disable
|
||||
self.thread_count_input.setText("1")
|
||||
self.thread_count_input.setEnabled(False)
|
||||
self.thread_count_label.setEnabled(False)
|
||||
self.use_multithreading_checkbox.setText("Use Multithreading (1 Thread)")
|
||||
else:
|
||||
# Checked: Enable and update label based on current value
|
||||
self.thread_count_input.setEnabled(True)
|
||||
self.thread_count_label.setEnabled(True)
|
||||
self.update_multithreading_label(self.thread_count_input.text())
|
||||
# --- END ADDED ---
|
||||
|
||||
|
||||
def update_progress_display(self, total_posts, processed_posts):
|
||||
@@ -982,20 +1024,42 @@ class DownloaderApp(QWidget):
|
||||
manga_mode = manga_mode_is_checked and not post_id_from_url # Manga mode only for creator feeds
|
||||
|
||||
num_threads_str = self.thread_count_input.text().strip()
|
||||
MAX_ALLOWED_THREADS = 200
|
||||
MODERATE_THREAD_WARNING_THRESHOLD = 50
|
||||
try:
|
||||
num_threads = int(num_threads_str)
|
||||
if not (1 <= num_threads <= MAX_ALLOWED_THREADS): # Validate thread count
|
||||
QMessageBox.critical(self, "Thread Count Error", f"Number of threads must be between 1 and {MAX_ALLOWED_THREADS}.")
|
||||
self.thread_count_input.setText(str(min(max(1, num_threads), MAX_ALLOWED_THREADS))) # Correct to valid range
|
||||
return
|
||||
if num_threads > MODERATE_THREAD_WARNING_THRESHOLD: # Warn for very high thread counts
|
||||
QMessageBox.information(self, "High Thread Count Note",
|
||||
f"Using {num_threads} threads (above {MODERATE_THREAD_WARNING_THRESHOLD}) may increase resource usage and risk rate-limiting from the site.\n\nProceeding with caution.")
|
||||
self.log_signal.emit(f"ℹ️ Using high thread count: {num_threads}.")
|
||||
except ValueError:
|
||||
QMessageBox.critical(self, "Thread Count Error", "Invalid number of threads. Please enter a numeric value."); return
|
||||
num_threads = 1 # Default to 1 if multithreading is off or input is invalid
|
||||
if use_multithreading: # Only parse if multithreading is enabled
|
||||
try:
|
||||
num_threads_requested = int(num_threads_str)
|
||||
|
||||
# --- MODIFIED: Tiered Thread Count Warning/Cap ---
|
||||
if num_threads_requested > MAX_THREADS:
|
||||
# Hard cap warning (above 200)
|
||||
warning_message = (
|
||||
f"You have requested {num_threads_requested} threads, which is above the maximum limit of {MAX_THREADS}.\n\n"
|
||||
f"High thread counts can lead to instability or rate-limiting.\n\n"
|
||||
f"The thread count will be automatically capped at {MAX_THREADS} for this download."
|
||||
)
|
||||
QMessageBox.warning(self, "High Thread Count Warning", warning_message)
|
||||
self.log_signal.emit(f"⚠️ High thread count requested ({num_threads_requested}). Capping at {MAX_THREADS}.")
|
||||
num_threads = MAX_THREADS # Apply the cap
|
||||
self.thread_count_input.setText(str(num_threads)) # Update UI to show capped value
|
||||
elif num_threads_requested > RECOMMENDED_MAX_THREADS:
|
||||
# Informational warning (above 50 but <= 200)
|
||||
QMessageBox.information(self, "High Thread Count Note",
|
||||
f"Using {num_threads_requested} threads (above {RECOMMENDED_MAX_THREADS}) may increase resource usage and risk rate-limiting from the site.\n\nProceeding with caution.")
|
||||
self.log_signal.emit(f"ℹ️ Using high thread count: {num_threads_requested}.")
|
||||
num_threads = num_threads_requested # Use the requested value
|
||||
elif num_threads_requested < 1: # Should be caught by validator, but safety check
|
||||
self.log_signal.emit(f"⚠️ Invalid thread count ({num_threads_requested}). Using 1 thread.")
|
||||
num_threads = 1
|
||||
self.thread_count_input.setText(str(num_threads))
|
||||
else:
|
||||
num_threads = num_threads_requested # Use the requested value if within limits
|
||||
# --- END MODIFIED ---
|
||||
|
||||
except ValueError:
|
||||
QMessageBox.critical(self, "Thread Count Error", "Invalid number of threads. Please enter a numeric value."); return
|
||||
else:
|
||||
num_threads = 1 # Explicitly set to 1 if multithreading checkbox is off
|
||||
|
||||
|
||||
start_page_str, end_page_str = self.start_page_input.text().strip(), self.end_page_input.text().strip()
|
||||
start_page, end_page = None, None
|
||||
@@ -1148,7 +1212,7 @@ class DownloaderApp(QWidget):
|
||||
|
||||
should_use_multithreading = use_multithreading and not post_id_from_url # Multi-threading for creator feeds
|
||||
log_messages.append(f" Threading: {'Multi-threaded (posts)' if should_use_multithreading else 'Single-threaded (posts)'}")
|
||||
if should_use_multithreading: log_messages.append(f" Number of Post Worker Threads: {num_threads}")
|
||||
if should_use_multithreading: log_messages.append(f" Number of Post Worker Threads: {num_threads}") # Use potentially capped value
|
||||
log_messages.append("="*40)
|
||||
for msg in log_messages: self.log_signal.emit(msg)
|
||||
|
||||
@@ -1187,8 +1251,8 @@ class DownloaderApp(QWidget):
|
||||
|
||||
try:
|
||||
if should_use_multithreading:
|
||||
self.log_signal.emit(f" Initializing multi-threaded download with {num_threads} post workers...")
|
||||
self.start_multi_threaded_download(num_post_workers=num_threads, **args_template)
|
||||
self.log_signal.emit(f" Initializing multi-threaded download with {num_threads} post workers...") # Use potentially capped value
|
||||
self.start_multi_threaded_download(num_post_workers=num_threads, **args_template) # Pass capped value
|
||||
else: # Single post URL or multithreading disabled
|
||||
self.log_signal.emit(" Initializing single-threaded download...")
|
||||
# Keys expected by DownloadThread constructor
|
||||
@@ -1451,31 +1515,33 @@ class DownloaderApp(QWidget):
|
||||
self.download_thumbnails_checkbox, self.use_multithreading_checkbox,
|
||||
self.skip_words_input, self.character_search_input, self.new_char_input,
|
||||
self.add_char_button, self.delete_char_button,
|
||||
# self.external_links_checkbox, # MODIFIED: Keep this enabled
|
||||
# self.external_links_checkbox, # Keep this enabled
|
||||
self.start_page_input, self.end_page_input, self.page_range_label, self.to_label,
|
||||
self.character_input, self.custom_folder_input, self.custom_folder_label,
|
||||
self.reset_button,
|
||||
# self.log_verbosity_button, # MODIFIED: Keep this enabled
|
||||
# self.log_verbosity_button, # Keep this enabled
|
||||
self.manga_mode_checkbox
|
||||
]
|
||||
for widget in widgets_to_toggle:
|
||||
if widget: # Check if widget exists
|
||||
widget.setEnabled(enabled)
|
||||
|
||||
# --- ADDED: Explicitly keep these enabled ---
|
||||
# --- Explicitly keep these enabled ---
|
||||
if self.external_links_checkbox:
|
||||
self.external_links_checkbox.setEnabled(True)
|
||||
if self.log_verbosity_button:
|
||||
self.log_verbosity_button.setEnabled(True)
|
||||
# --- END ADDED ---
|
||||
|
||||
# Handle dependent widgets
|
||||
subfolders_currently_on = self.use_subfolders_checkbox.isChecked()
|
||||
self.use_subfolder_per_post_checkbox.setEnabled(enabled and subfolders_currently_on)
|
||||
# --- END ---
|
||||
|
||||
# --- MODIFIED: Handle thread count input based on checkbox state ---
|
||||
multithreading_currently_on = self.use_multithreading_checkbox.isChecked()
|
||||
self.thread_count_input.setEnabled(enabled and multithreading_currently_on)
|
||||
self.thread_count_label.setEnabled(enabled and multithreading_currently_on)
|
||||
# --- END MODIFIED ---
|
||||
|
||||
# Handle other dependent widgets
|
||||
subfolders_currently_on = self.use_subfolders_checkbox.isChecked()
|
||||
self.use_subfolder_per_post_checkbox.setEnabled(enabled and subfolders_currently_on)
|
||||
|
||||
self.cancel_btn.setEnabled(not enabled) # Cancel is enabled when download is running
|
||||
|
||||
@@ -1485,6 +1551,7 @@ class DownloaderApp(QWidget):
|
||||
self.update_page_range_enabled_state()
|
||||
if self.manga_mode_checkbox:
|
||||
self.update_ui_for_manga_mode(self.manga_mode_checkbox.isChecked())
|
||||
self._handle_multithreading_toggle(multithreading_currently_on) # Refresh thread count state
|
||||
|
||||
|
||||
def cancel_download(self):
|
||||
@@ -1656,6 +1723,10 @@ class DownloaderApp(QWidget):
|
||||
if self.manga_mode_checkbox:
|
||||
self.update_ui_for_manga_mode(self.manga_mode_checkbox.isChecked())
|
||||
self.filter_character_list("") # Clear character list filter
|
||||
|
||||
# --- MODIFIED: Reset thread count state based on checkbox ---
|
||||
self._handle_multithreading_toggle(self.use_multithreading_checkbox.isChecked())
|
||||
# --- END MODIFIED ---
|
||||
|
||||
# Reset button states
|
||||
self.download_btn.setEnabled(True)
|
||||
|
||||
Reference in New Issue
Block a user