This commit is contained in:
Yuvi9587 2025-09-07 08:16:43 -07:00
parent ec94417569
commit a9b210b2ba
6 changed files with 47 additions and 91 deletions

View File

@ -60,7 +60,6 @@ RESOLUTION_KEY = "window_resolution"
UI_SCALE_KEY = "ui_scale_factor" UI_SCALE_KEY = "ui_scale_factor"
SAVE_CREATOR_JSON_KEY = "saveCreatorJsonProfile" SAVE_CREATOR_JSON_KEY = "saveCreatorJsonProfile"
FETCH_FIRST_KEY = "fetchAllPostsFirst" FETCH_FIRST_KEY = "fetchAllPostsFirst"
# --- FIX: Add the missing key for the Discord token ---
DISCORD_TOKEN_KEY = "discord/token" DISCORD_TOKEN_KEY = "discord/token"
POST_DOWNLOAD_ACTION_KEY = "postDownloadAction" POST_DOWNLOAD_ACTION_KEY = "postDownloadAction"

View File

@ -43,9 +43,7 @@ def fetch_channel_messages(channel_id, logger=print, cancellation_event=None, pa
} }
offset = 0 offset = 0
# --- FIX: Corrected the page size for Discord API pagination ---
page_size = 150 page_size = 150
# --- END FIX ---
while True: while True:
if cancellation_event and cancellation_event.is_set(): if cancellation_event and cancellation_event.is_set():

View File

@ -875,25 +875,22 @@ class PostProcessorWorker:
def process(self): def process(self):
if self.service == 'discord': if self.service == 'discord':
# For Discord, self.post is a MESSAGE object from the API.
post_title = self.post.get('content', '') or f"Message {self.post.get('id', 'N/A')}" post_title = self.post.get('content', '') or f"Message {self.post.get('id', 'N/A')}"
post_id = self.post.get('id', 'unknown_id') post_id = self.post.get('id', 'unknown_id')
post_main_file_info = {} # Discord messages don't have a single main file post_main_file_info = {}
post_attachments = self.post.get('attachments', []) post_attachments = self.post.get('attachments', [])
post_content_html = self.post.get('content', '') post_content_html = self.post.get('content', '')
post_data = self.post # Keep a reference to the original message object post_data = self.post
log_prefix = "Message" log_prefix = "Message"
else: else:
# Existing logic for standard creator posts
post_title = self.post.get('title', '') or 'untitled_post' post_title = self.post.get('title', '') or 'untitled_post'
post_id = self.post.get('id', 'unknown_id') post_id = self.post.get('id', 'unknown_id')
post_main_file_info = self.post.get('file') post_main_file_info = self.post.get('file')
post_attachments = self.post.get('attachments', []) post_attachments = self.post.get('attachments', [])
post_content_html = self.post.get('content', '') post_content_html = self.post.get('content', '')
post_data = self.post # Reference to the post object post_data = self.post
log_prefix = "Post" log_prefix = "Post"
# --- FIX: FETCH FULL POST DATA IF CONTENT IS MISSING BUT NEEDED ---
content_is_needed = ( content_is_needed = (
self.show_external_links or self.show_external_links or
self.extract_links_only or self.extract_links_only or

View File

@ -42,17 +42,12 @@ class ErrorFilesDialog(QDialog):
if app_icon and not app_icon.isNull(): if app_icon and not app_icon.isNull():
self.setWindowIcon(app_icon) self.setWindowIcon(app_icon)
# --- START OF FIX ---
# Get the user-defined scale factor from the parent application.
scale_factor = getattr(self.parent_app, 'scale_factor', 1.0) scale_factor = getattr(self.parent_app, 'scale_factor', 1.0)
# Define base dimensions and apply the correct scale factor.
base_width, base_height = 550, 400 base_width, base_height = 550, 400
self.setMinimumSize(int(base_width * scale_factor), int(base_height * scale_factor)) self.setMinimumSize(int(base_width * scale_factor), int(base_height * scale_factor))
self.resize(int(base_width * scale_factor * 1.1), int(base_height * scale_factor * 1.1)) self.resize(int(base_width * scale_factor * 1.1), int(base_height * scale_factor * 1.1))
# --- END OF FIX ---
# --- Initialize UI and Apply Theming ---
self._init_ui() self._init_ui()
self._retranslate_ui() self._retranslate_ui()
self._apply_theme() self._apply_theme()

View File

@ -260,6 +260,8 @@ class DownloaderApp (QWidget ):
self.is_fetching_only = False self.is_fetching_only = False
self.fetched_posts_for_download = [] self.fetched_posts_for_download = []
self.is_ready_to_download_fetched = False self.is_ready_to_download_fetched = False
self.last_logged_filter_mode = None
self.last_logged_external_link_status = None
print(f" Known.txt will be loaded/saved at: {self.config_file}") print(f" Known.txt will be loaded/saved at: {self.config_file}")
@ -406,7 +408,6 @@ class DownloaderApp (QWidget ):
try: try:
queue_logger(f" Downloading ({download_count+1}/{total_attachments}): '{filename_to_use}'...") queue_logger(f" Downloading ({download_count+1}/{total_attachments}): '{filename_to_use}'...")
# --- FIX: Stream the download in chunks for responsive controls ---
response = requests.get(file_url, stream=True, timeout=60) response = requests.get(file_url, stream=True, timeout=60)
response.raise_for_status() response.raise_for_status()
@ -2340,49 +2341,60 @@ class DownloaderApp (QWidget ):
self.log_signal.emit(f" ⚠️ Could not delete temp file '{filepath}': {e}") self.log_signal.emit(f" ⚠️ Could not delete temp file '{filepath}': {e}")
self.session_temp_files = [] self.session_temp_files = []
def update_external_links_setting (self ,checked ): def update_external_links_setting(self, checked, log_change=True):
is_only_links_mode =self .radio_only_links and self .radio_only_links .isChecked () is_only_links_mode = self.radio_only_links and self.radio_only_links.isChecked()
is_only_archives_mode =self .radio_only_archives and self .radio_only_archives .isChecked () is_only_archives_mode = self.radio_only_archives and self.radio_only_archives.isChecked()
is_only_audio_mode = hasattr(self, 'radio_only_audio') and self.radio_only_audio.isChecked()
if is_only_links_mode or is_only_archives_mode : if is_only_links_mode or is_only_archives_mode or is_only_audio_mode:
if self .external_log_output :self .external_log_output .hide () if self.external_log_output: self.external_log_output.hide()
if self .log_splitter :self .log_splitter .setSizes ([self .height (),0 ]) if self.log_splitter: self.log_splitter.setSizes([self.height(), 0])
return return
self .show_external_links =checked self.show_external_links = checked
if checked :
if self .external_log_output :self .external_log_output .show () if log_change and self.last_logged_external_link_status != checked:
if self .log_splitter :self .log_splitter .setSizes ([self .height ()//2 ,self .height ()//2 ]) if checked:
if self .main_log_output :self .main_log_output .setMinimumHeight (50 )
if self .external_log_output :self .external_log_output .setMinimumHeight (50 )
self.log_signal.emit(" External Links Log Enabled") self.log_signal.emit(" External Links Log Enabled")
if self .external_log_output : else:
self .external_log_output .clear ()
self .external_log_output .append ("🔗 External Links Found:")
self ._try_process_next_external_link ()
else :
if self .external_log_output :self .external_log_output .hide ()
if self .log_splitter :self .log_splitter .setSizes ([self .height (),0 ])
if self .main_log_output :self .main_log_output .setMinimumHeight (0 )
if self .external_log_output :self .external_log_output .setMinimumHeight (0 )
if self .external_log_output :self .external_log_output .clear ()
self.log_signal.emit(" External Links Log Disabled") self.log_signal.emit(" External Links Log Disabled")
self.last_logged_external_link_status = checked
if checked:
if self.external_log_output: self.external_log_output.show()
if self.log_splitter: self.log_splitter.setSizes([self.height() // 2, self.height() // 2])
if self.main_log_output: self.main_log_output.setMinimumHeight(50)
if self.external_log_output: self.external_log_output.setMinimumHeight(50)
if self.external_log_output:
self.external_log_output.clear()
self.external_log_output.append("🔗 External Links Found:")
self._try_process_next_external_link()
else:
if self.external_log_output: self.external_log_output.hide()
if self.log_splitter: self.log_splitter.setSizes([self.height(), 0])
if self.main_log_output: self.main_log_output.setMinimumHeight(0)
if self.external_log_output: self.external_log_output.setMinimumHeight(0)
if self.external_log_output: self.external_log_output.clear()
def _handle_filter_mode_change(self, button, checked): def _handle_filter_mode_change(self, button, checked):
if not button or not checked: if not button or not checked:
return return
new_mode_text = button.text()
if self.last_logged_filter_mode != new_mode_text:
self.log_signal.emit(f" Filter mode changed to: {new_mode_text}")
self.last_logged_filter_mode = new_mode_text
is_only_links = (button == self.radio_only_links) is_only_links = (button == self.radio_only_links)
if hasattr(self, 'use_multithreading_checkbox') and hasattr(self, 'thread_count_input'): if hasattr(self, 'use_multithreading_checkbox') and hasattr(self, 'thread_count_input'):
if is_only_links: if is_only_links:
# When "Only Links" is selected, enable multithreading, set threads to 20, and lock the input.
self.use_multithreading_checkbox.setChecked(True) self.use_multithreading_checkbox.setChecked(True)
self.thread_count_input.setText("20") self.thread_count_input.setText("20")
self.thread_count_input.setEnabled(False) self.thread_count_input.setEnabled(False)
self.thread_count_label.setEnabled(False) self.thread_count_label.setEnabled(False)
self.update_multithreading_label("20") self.update_multithreading_label("20")
else: else:
# When another mode is selected, re-enable the input for user control.
is_multithreading_checked = self.use_multithreading_checkbox.isChecked() is_multithreading_checked = self.use_multithreading_checkbox.isChecked()
self.thread_count_input.setEnabled(is_multithreading_checked) self.thread_count_input.setEnabled(is_multithreading_checked)
self.thread_count_label.setEnabled(is_multithreading_checked) self.thread_count_label.setEnabled(is_multithreading_checked)
@ -2448,37 +2460,10 @@ class DownloaderApp (QWidget ):
if is_only_links: if is_only_links:
self.progress_log_label.setText("📜 Extracted Links Log:") self.progress_log_label.setText("📜 Extracted Links Log:")
if self.external_log_output: self.external_log_output.hide()
if self.log_splitter: self.log_splitter.setSizes([self.height(), 0])
do_clear_log_in_filter_change = True
if self.mega_download_log_preserved_once and self.only_links_log_display_mode == LOG_DISPLAY_DOWNLOAD_PROGRESS:
do_clear_log_in_filter_change = False
if self.main_log_output and do_clear_log_in_filter_change:
self.log_signal.emit("INTERNAL: _handle_filter_mode_change - About to clear log.")
self.main_log_output.clear()
self.log_signal.emit("INTERNAL: _handle_filter_mode_change - Log cleared by _handle_filter_mode_change.")
if self.main_log_output: self.main_log_output.setMinimumHeight(0)
self.log_signal.emit(f" Filter mode changed to: {button.text()}")
self._try_process_next_external_link()
elif is_only_archives:
self.progress_log_label.setText("📜 Progress Log (Archives Only):")
if self.external_log_output: self.external_log_output.hide()
if self.log_splitter: self.log_splitter.setSizes([self.height(), 0])
if self.main_log_output: self.main_log_output.clear()
self.log_signal.emit(f" Filter mode changed to: {button.text()}")
elif is_only_audio:
self.progress_log_label.setText(self._tr("progress_log_label_text", "📜 Progress Log:") + f" ({self._tr('filter_audio_radio', '🎧 Only Audio')})")
if self.external_log_output: self.external_log_output.hide()
if self.log_splitter: self.log_splitter.setSizes([self.height(), 0])
if self.main_log_output: self.main_log_output.clear()
self.log_signal.emit(f" Filter mode changed to: {button.text()}")
else: else:
self.progress_log_label.setText(self._tr("progress_log_label_text", "📜 Progress Log:")) self.progress_log_label.setText(self._tr("progress_log_label_text", "📜 Progress Log:"))
self.update_external_links_setting(self.external_links_checkbox.isChecked() if self.external_links_checkbox else False) self.update_external_links_setting(self.external_links_checkbox.isChecked() if self.external_links_checkbox else False)
self.log_signal.emit(f" Filter mode changed to: {button.text()}")
if is_only_links: if is_only_links:
self._filter_links_log() self._filter_links_log()
@ -2509,13 +2494,11 @@ class DownloaderApp (QWidget ):
self.update_custom_folder_visibility() self.update_custom_folder_visibility()
self.update_ui_for_manga_mode(self.manga_mode_checkbox.isChecked() if self.manga_mode_checkbox else False) self.update_ui_for_manga_mode(self.manga_mode_checkbox.isChecked() if self.manga_mode_checkbox else False)
def _filter_links_log (self ): def _filter_links_log (self ):
if not (self .radio_only_links and self .radio_only_links .isChecked ()):return if not (self .radio_only_links and self .radio_only_links .isChecked ()):return
search_term =self .link_search_input .text ().lower ().strip ()if self .link_search_input else "" search_term =self .link_search_input .text ().lower ().strip ()if self .link_search_input else ""
# This block handles the "Download Progress" view for Mega/Drive links and should be kept
if self .mega_download_log_preserved_once and self .only_links_log_display_mode ==LOG_DISPLAY_DOWNLOAD_PROGRESS : if self .mega_download_log_preserved_once and self .only_links_log_display_mode ==LOG_DISPLAY_DOWNLOAD_PROGRESS :
self .log_signal .emit ("INTERNAL: _filter_links_log - Preserving Mega log.") self .log_signal .emit ("INTERNAL: _filter_links_log - Preserving Mega log.")
return return
@ -3294,7 +3277,6 @@ class DownloaderApp (QWidget ):
self.manga_rename_toggle_button, self.manga_date_prefix_input, self.manga_rename_toggle_button, self.manga_date_prefix_input,
self.multipart_toggle_button, self.custom_folder_input, self.custom_folder_label, self.multipart_toggle_button, self.custom_folder_input, self.custom_folder_label,
self.discord_scope_toggle_button self.discord_scope_toggle_button
# --- FIX: REMOVED self.save_discord_as_pdf_btn from this list ---
] ]
enable_state = not is_specialized enable_state = not is_specialized
@ -3335,14 +3317,10 @@ class DownloaderApp (QWidget ):
url_text = self.link_input.text().strip() url_text = self.link_input.text().strip()
service, _, _ = extract_post_info(url_text) service, _, _ = extract_post_info(url_text)
# --- FIX: Use two separate flags for better control ---
# This is true for BOTH kemono.cr/discord and discord.com
is_any_discord_url = (service == 'discord') is_any_discord_url = (service == 'discord')
# This is ONLY true for official discord.com
is_official_discord_url = 'discord.com' in url_text and is_any_discord_url is_official_discord_url = 'discord.com' in url_text and is_any_discord_url
if is_official_discord_url: if is_official_discord_url:
# Show the token input only for the official site
self.remove_from_filename_label_widget.setText("🔑 Discord Token:") self.remove_from_filename_label_widget.setText("🔑 Discord Token:")
self.remove_from_filename_input.setPlaceholderText("Enter your Discord Authorization Token here") self.remove_from_filename_input.setPlaceholderText("Enter your Discord Authorization Token here")
self.remove_from_filename_input.setEchoMode(QLineEdit.Password) self.remove_from_filename_input.setEchoMode(QLineEdit.Password)
@ -3355,16 +3333,13 @@ class DownloaderApp (QWidget ):
self.remove_from_filename_input.setPlaceholderText(self._tr("remove_from_filename_input_placeholder_text", "e.g., patreon, HD")) self.remove_from_filename_input.setPlaceholderText(self._tr("remove_from_filename_input_placeholder_text", "e.g., patreon, HD"))
self.remove_from_filename_input.setEchoMode(QLineEdit.Normal) self.remove_from_filename_input.setEchoMode(QLineEdit.Normal)
# Handle other specialized downloaders (Bunkr, nhentai, etc.)
is_saint2 = 'saint2.su' in url_text or 'saint2.pk' in url_text is_saint2 = 'saint2.su' in url_text or 'saint2.pk' in url_text
is_erome = 'erome.com' in url_text is_erome = 'erome.com' in url_text
is_specialized = service in ['bunkr', 'nhentai', 'hentai2read'] or is_saint2 or is_erome is_specialized = service in ['bunkr', 'nhentai', 'hentai2read'] or is_saint2 or is_erome
self._set_ui_for_specialized_downloader(is_specialized) self._set_ui_for_specialized_downloader(is_specialized)
# --- FIX: Show the Scope button for ANY Discord URL (Kemono or official) ---
self.discord_scope_toggle_button.setVisible(is_any_discord_url) self.discord_scope_toggle_button.setVisible(is_any_discord_url)
if hasattr(self, 'discord_message_limit_input'): if hasattr(self, 'discord_message_limit_input'):
# Only show the message limit for the official site, as it's an API feature
self.discord_message_limit_input.setVisible(is_official_discord_url) self.discord_message_limit_input.setVisible(is_official_discord_url)
if is_any_discord_url: if is_any_discord_url:
@ -3705,16 +3680,12 @@ class DownloaderApp (QWidget ):
server_id=server_id, channel_id=channel_id, url=api_url, limit=message_limit, parent=self server_id=server_id, channel_id=channel_id, url=api_url, limit=message_limit, parent=self
) )
# 2. Connect its signals to the main window's functions
self.download_thread.progress_signal.connect(self.handle_main_log) self.download_thread.progress_signal.connect(self.handle_main_log)
self.download_thread.progress_label_signal.connect(self.progress_label.setText) self.download_thread.progress_label_signal.connect(self.progress_label.setText)
self.download_thread.finished_signal.connect(self.download_finished) self.download_thread.finished_signal.connect(self.download_finished)
# --- FIX: Start the thread BEFORE updating the UI ---
# 3. Start the download process in the background
self.download_thread.start() self.download_thread.start()
# 4. NOW, update the UI. The app knows a download is active.
self.set_ui_enabled(False) self.set_ui_enabled(False)
self._update_button_states_and_connections() self._update_button_states_and_connections()
@ -5185,14 +5156,11 @@ class DownloaderApp (QWidget ):
self ._handle_favorite_mode_toggle (is_fav_mode_active ) self ._handle_favorite_mode_toggle (is_fav_mode_active )
def _handle_pause_resume_action(self): def _handle_pause_resume_action(self):
# --- FIX: Simplified and corrected the pause/resume logic ---
if not self._is_download_active(): if not self._is_download_active():
return return
# Toggle the main app's pause state tracker
self.is_paused = not self.is_paused self.is_paused = not self.is_paused
# Call the correct method on the thread based on the new state
if isinstance(self.download_thread, DiscordDownloadThread): if isinstance(self.download_thread, DiscordDownloadThread):
if self.is_paused: if self.is_paused:
self.download_thread.pause() self.download_thread.pause()
@ -5518,7 +5486,6 @@ class DownloaderApp (QWidget ):
self.log_signal.emit("✅ Download complete! Notification sound played.") self.log_signal.emit("✅ Download complete! Notification sound played.")
return return
# --- FIX: Ensure confirm_title is defined before it is used ---
confirm_title = self._tr("action_confirmation_title", "Action After Download") confirm_title = self._tr("action_confirmation_title", "Action After Download")
confirm_text = "" confirm_text = ""