This commit is contained in:
Yuvi9587 2025-06-06 17:29:17 +01:00
parent b744e83f09
commit 0940bdb8dd
2 changed files with 325 additions and 329 deletions

View File

@ -13,14 +13,13 @@ from PyQt5 .QtCore import QObject ,pyqtSignal ,QThread ,QMutex ,QMutexLocker
from urllib .parse import urlparse from urllib .parse import urlparse
try : try :
from mega import Mega from mega import Mega
# Import download functions from drive.py
# Assuming drive.py is in the same directory
try : try :
from drive import download_mega_file as drive_download_mega_file, \ from drive import download_mega_file as drive_download_mega_file ,download_gdrive_file ,download_dropbox_file
download_gdrive_file, download_dropbox_file
# To avoid confusion, we'll use drive_download_mega_file internally when calling from main
# and ensure this module exports it as download_mega_file for compatibility if needed,
# or main.py can be updated to call drive_download_mega_file.
except ImportError as drive_import_err : except ImportError as drive_import_err :
print (f"ERROR importing from drive.py: {drive_import_err }. External drive downloads will fail.") print (f"ERROR importing from drive.py: {drive_import_err }. External drive downloads will fail.")
except ImportError : except ImportError :
@ -57,6 +56,7 @@ FILE_DOWNLOAD_STATUS_FAILED_PERMANENTLY_THIS_SESSION ="failed_permanent_session"
fastapi_app =None fastapi_app =None
KNOWN_NAMES =[] KNOWN_NAMES =[]
MIN_SIZE_FOR_MULTIPART_DOWNLOAD =10 *1024 *1024 MIN_SIZE_FOR_MULTIPART_DOWNLOAD =10 *1024 *1024
GOFILE_GUEST_TOKEN =None
MAX_PARTS_FOR_MULTIPART_DOWNLOAD =15 MAX_PARTS_FOR_MULTIPART_DOWNLOAD =15
MAX_FILENAME_COMPONENT_LENGTH =150 MAX_FILENAME_COMPONENT_LENGTH =150
IMAGE_EXTENSIONS ={ IMAGE_EXTENSIONS ={
@ -704,6 +704,7 @@ def get_link_platform (url ):
if 'mega.nz'in domain or 'mega.io'in domain :return 'mega' if 'mega.nz'in domain or 'mega.io'in domain :return 'mega'
if 'dropbox.com'in domain :return 'dropbox' if 'dropbox.com'in domain :return 'dropbox'
if 'patreon.com'in domain :return 'patreon' if 'patreon.com'in domain :return 'patreon'
if 'gofile.io'in domain :return 'gofile'
if 'instagram.com'in domain :return 'instagram' if 'instagram.com'in domain :return 'instagram'
if 'twitter.com'in domain or 'x.com'in domain :return 'twitter/x' if 'twitter.com'in domain or 'x.com'in domain :return 'twitter/x'
if 'discord.gg'in domain or 'discord.com/invite'in domain :return 'discord invite' if 'discord.gg'in domain or 'discord.com/invite'in domain :return 'discord invite'
@ -2133,7 +2134,7 @@ def download_mega_file (mega_link ,download_path =".",logger_func =print ):
try : try :
# Pre-flight check for link validity and attributes
logger_func (f" Verifying Mega link and fetching attributes: {mega_link }") logger_func (f" Verifying Mega link and fetching attributes: {mega_link }")
file_attributes =m .get_public_url_info (mega_link ) file_attributes =m .get_public_url_info (mega_link )
@ -2141,12 +2142,12 @@ def download_mega_file (mega_link ,download_path =".",logger_func =print ):
logger_func (f"❌ Error: Could not retrieve valid file information for the Mega link. Link might be invalid, expired, or a folder. Info received: {file_attributes }") logger_func (f"❌ Error: Could not retrieve valid file information for the Mega link. Link might be invalid, expired, or a folder. Info received: {file_attributes }")
raise ValueError (f"Invalid or inaccessible Mega link. get_public_url_info returned: {file_attributes }") raise ValueError (f"Invalid or inaccessible Mega link. get_public_url_info returned: {file_attributes }")
expected_filename = file_attributes.get('name') # Changed from 'n' expected_filename =file_attributes .get ('name')
file_size = file_attributes.get('size') # Changed from 's' file_size =file_attributes .get ('size')
if not expected_filename : if not expected_filename :
logger_func(f"⚠️ Critical: File name ('name') not found in Mega link attributes. Attributes: {file_attributes}") # Updated log logger_func (f"⚠️ Critical: File name ('name') not found in Mega link attributes. Attributes: {file_attributes }")
raise ValueError(f"File name ('name') not found in Mega link attributes: {file_attributes}") # Updated ValueError raise ValueError (f"File name ('name') not found in Mega link attributes: {file_attributes }")
logger_func (f" Link verified. Expected filename: '{expected_filename }'. Size: {file_size if file_size is not None else 'Unknown'} bytes.") logger_func (f" Link verified. Expected filename: '{expected_filename }'. Size: {file_size if file_size is not None else 'Unknown'} bytes.")
@ -2156,19 +2157,19 @@ def download_mega_file (mega_link ,download_path =".",logger_func =print ):
logger_func (f"Starting download of '{expected_filename }' to '{download_path }'...") logger_func (f"Starting download of '{expected_filename }' to '{download_path }'...")
# m.download_url returns a tuple (filepath, filename) on success for mega.py 1.0.8
download_result =m .download_url (mega_link ,dest_path =download_path ,dest_filename =None ) download_result =m .download_url (mega_link ,dest_path =download_path ,dest_filename =None )
if download_result and isinstance (download_result ,tuple )and len (download_result )==2 : if download_result and isinstance (download_result ,tuple )and len (download_result )==2 :
saved_filepath ,saved_filename =download_result saved_filepath ,saved_filename =download_result
# Ensure saved_filepath is an absolute path if dest_path was relative
if not os .path .isabs (saved_filepath )and dest_path : if not os .path .isabs (saved_filepath )and dest_path :
saved_filepath =os .path .join (os .path .abspath (dest_path ),saved_filename ) saved_filepath =os .path .join (os .path .abspath (dest_path ),saved_filename )
logger_func (f"File downloaded successfully! Saved as: {saved_filepath }") logger_func (f"File downloaded successfully! Saved as: {saved_filepath }")
if not os .path .exists (saved_filepath ): if not os .path .exists (saved_filepath ):
logger_func (f"⚠️ Warning: mega.py reported success but file '{saved_filepath }' not found on disk.") logger_func (f"⚠️ Warning: mega.py reported success but file '{saved_filepath }' not found on disk.")
# Optionally, verify filename if needed, though saved_filename should be correct
if saved_filename !=expected_filename : if saved_filename !=expected_filename :
logger_func (f" Note: Saved filename '{saved_filename }' differs from initially expected '{expected_filename }'. This is usually fine.") logger_func (f" Note: Saved filename '{saved_filename }' differs from initially expected '{expected_filename }'. This is usually fine.")
else : else :
@ -2184,7 +2185,7 @@ def download_mega_file (mega_link ,download_path =".",logger_func =print ):
except requests .exceptions .RequestException as e : except requests .exceptions .RequestException as e :
logger_func (f"Error during request to Mega (network issue, etc.): {e }") logger_func (f"Error during request to Mega (network issue, etc.): {e }")
raise raise
except ValueError as ve: # Catch our custom ValueError from pre-flight except ValueError as ve :
logger_func (f"ValueError during Mega processing (likely invalid link): {ve }") logger_func (f"ValueError during Mega processing (likely invalid link): {ve }")
raise raise
except Exception as e : except Exception as e :

147
main.py
View File

@ -157,16 +157,16 @@ class DownloadExtractedLinksDialog(QDialog):
self .links_data =links_data self .links_data =links_data
self .setWindowTitle ("Download Selected External Links") self .setWindowTitle ("Download Selected External Links")
# Adjust dialog size relative to parent
if parent : if parent :
parent_width =parent .width () parent_width =parent .width ()
parent_height =parent .height () parent_height =parent .height ()
dialog_width = int(parent_width * 0.6) # 60% of parent width dialog_width =int (parent_width *0.6 )
dialog_height = int(parent_height * 0.7) # 70% of parent height dialog_height =int (parent_height *0.7 )
min_w, min_h = 500, 400 # Keep a reasonable minimum min_w ,min_h =500 ,400
self .resize (max (dialog_width ,min_w ),max (dialog_height ,min_h )) self .resize (max (dialog_width ,min_w ),max (dialog_height ,min_h ))
else : else :
self.setMinimumSize(500, 400) # Fallback if no parent self .setMinimumSize (500 ,400 )
@ -184,20 +184,20 @@ class DownloadExtractedLinksDialog(QDialog):
post_title_for_group =link_info_item .get ('title','Untitled Post') post_title_for_group =link_info_item .get ('title','Untitled Post')
grouped_links [post_title_for_group ].append (link_info_item ) grouped_links [post_title_for_group ].append (link_info_item )
# Sort by post title for consistent order
sorted_post_titles =sorted (grouped_links .keys (),key =lambda x :x .lower ()) sorted_post_titles =sorted (grouped_links .keys (),key =lambda x :x .lower ())
for post_title_key in sorted_post_titles : for post_title_key in sorted_post_titles :
# Add header for post_title
header_item =QListWidgetItem (f"{post_title_key }") header_item =QListWidgetItem (f"{post_title_key }")
header_item.setFlags(Qt.NoItemFlags) # Not selectable, not checkable header_item .setFlags (Qt .NoItemFlags )
font =header_item .font () font =header_item .font ()
font .setBold (True ) font .setBold (True )
font.setPointSize(font.pointSize() + 1) # Slightly larger font .setPointSize (font .pointSize ()+1 )
header_item .setFont (font ) header_item .setFont (font )
if parent and hasattr (parent ,'current_theme')and parent .current_theme =="dark": if parent and hasattr (parent ,'current_theme')and parent .current_theme =="dark":
header_item .setForeground (Qt .cyan ) header_item .setForeground (Qt .cyan )
else: # Light theme or no theme info else :
header_item .setForeground (Qt .blue ) header_item .setForeground (Qt .blue )
self .links_list_widget .addItem (header_item ) self .links_list_widget .addItem (header_item )
@ -210,8 +210,8 @@ class DownloadExtractedLinksDialog(QDialog):
item .setCheckState (Qt .Checked ) item .setCheckState (Qt .Checked )
self .links_list_widget .addItem (item ) self .links_list_widget .addItem (item )
# Add a little space after each group, or a separator item if preferred
# self.links_list_widget.addItem(QListWidgetItem("")) # Simple spacer
layout .addWidget (self .links_list_widget ) layout .addWidget (self .links_list_widget )
@ -241,17 +241,15 @@ class DownloadExtractedLinksDialog(QDialog):
def _set_all_items_checked (self ,check_state ): def _set_all_items_checked (self ,check_state ):
for i in range (self .links_list_widget .count ()): for i in range (self .links_list_widget .count ()):
item =self .links_list_widget .item (i ) item =self .links_list_widget .item (i )
if item.flags() & Qt.ItemIsUserCheckable: # Only operate on checkable items if item .flags ()&Qt .ItemIsUserCheckable :
item .setCheckState (check_state ) item .setCheckState (check_state )
def _handle_download_selected (self ): def _handle_download_selected (self ):
selected_links =[] selected_links =[]
for i in range (self .links_list_widget .count ()): for i in range (self .links_list_widget .count ()):
item =self .links_list_widget .item (i ) item =self .links_list_widget .item (i )
# Ensure item is checkable, actually checked, and has data (is a link item)
if item.flags() & Qt.ItemIsUserCheckable and \ if item .flags ()&Qt .ItemIsUserCheckable and item .checkState ()==Qt .Checked and item .data (Qt .UserRole )is not None :
item.checkState() == Qt.Checked and \
item.data(Qt.UserRole) is not None:
selected_links .append (item .data (Qt .UserRole )) selected_links .append (item .data (Qt .UserRole ))
if selected_links : if selected_links :
self .download_requested .emit (selected_links ) self .download_requested .emit (selected_links )
@ -2351,13 +2349,13 @@ class ExternalLinkDownloadThread(QThread):
platform =task_info .get ('platform','unknown').lower () platform =task_info .get ('platform','unknown').lower ()
full_mega_url =task_info ['url'] full_mega_url =task_info ['url']
post_title =task_info ['title'] post_title =task_info ['title']
key = task_info.get('key', '') # Primarily for Mega key =task_info .get ('key','')
self .progress_signal .emit (f"Download ({i +1 }/{len (self .tasks )}): Starting '{post_title }' ({platform .upper ()}) from {full_mega_url }") self .progress_signal .emit (f"Download ({i +1 }/{len (self .tasks )}): Starting '{post_title }' ({platform .upper ()}) from {full_mega_url }")
try : try :
if platform =='mega': if platform =='mega':
# Ensure key is part of the URL for mega.py
if key : if key :
parsed_original_url =urlparse (full_mega_url ) parsed_original_url =urlparse (full_mega_url )
if key not in parsed_original_url .fragment : if key not in parsed_original_url .fragment :
@ -2366,7 +2364,7 @@ class ExternalLinkDownloadThread(QThread):
self .progress_signal .emit (f" Adjusted Mega URL with key: {full_mega_url_with_key }") self .progress_signal .emit (f" Adjusted Mega URL with key: {full_mega_url_with_key }")
else : else :
full_mega_url_with_key =full_mega_url full_mega_url_with_key =full_mega_url
else: # No key provided, use URL as is (might fail if key is required and not in fragment) else :
full_mega_url_with_key =full_mega_url full_mega_url_with_key =full_mega_url
drive_download_mega_file (full_mega_url_with_key ,self .download_base_path ,logger_func =self .parent_logger_func ) drive_download_mega_file (full_mega_url_with_key ,self .download_base_path ,logger_func =self .parent_logger_func )
elif platform =='google drive': elif platform =='google drive':
@ -2507,7 +2505,7 @@ class DownloaderApp (QWidget ):
self .char_filter_scope =CHAR_SCOPE_TITLE self .char_filter_scope =CHAR_SCOPE_TITLE
self .manga_filename_style =self .settings .value (MANGA_FILENAME_STYLE_KEY ,STYLE_POST_TITLE ,type =str ) self .manga_filename_style =self .settings .value (MANGA_FILENAME_STYLE_KEY ,STYLE_POST_TITLE ,type =str )
self .current_theme =self .settings .value (THEME_KEY ,"dark",type =str ) self .current_theme =self .settings .value (THEME_KEY ,"dark",type =str )
self .only_links_log_display_mode = LOG_DISPLAY_LINKS # New state variable self .only_links_log_display_mode =LOG_DISPLAY_LINKS
self .mega_download_log_preserved_once =False self .mega_download_log_preserved_once =False
self .allow_multipart_download_setting =False self .allow_multipart_download_setting =False
self .use_cookie_setting =False self .use_cookie_setting =False
@ -3445,7 +3443,7 @@ class DownloaderApp (QWidget ):
self .log_display_mode_toggle_button .setToolTip ("Toggle log display mode for 'Only Links'") self .log_display_mode_toggle_button .setToolTip ("Toggle log display mode for 'Only Links'")
self .log_display_mode_toggle_button .setFixedWidth (120 ) self .log_display_mode_toggle_button .setFixedWidth (120 )
self .log_display_mode_toggle_button .setStyleSheet ("padding: 4px 8px; margin-top: 5px;") self .log_display_mode_toggle_button .setStyleSheet ("padding: 4px 8px; margin-top: 5px;")
self.log_display_mode_toggle_button.setVisible(False) # Initially hidden self .log_display_mode_toggle_button .setVisible (False )
export_button_layout .addWidget (self .log_display_mode_toggle_button ) export_button_layout .addWidget (self .log_display_mode_toggle_button )
right_layout .addLayout (export_button_layout ) right_layout .addLayout (export_button_layout )
@ -3545,12 +3543,11 @@ class DownloaderApp (QWidget ):
self .log_signal .emit (" No links selected for download from dialog.") self .log_signal .emit (" No links selected for download from dialog.")
return return
# Preserve log logic (might need adjustment if GDrive/Dropbox have different log styles)
if self.radio_only_links and self.radio_only_links.isChecked() and \ if self .radio_only_links and self .radio_only_links .isChecked ()and self .only_links_log_display_mode ==LOG_DISPLAY_DOWNLOAD_PROGRESS :
self.only_links_log_display_mode == LOG_DISPLAY_DOWNLOAD_PROGRESS:
self .main_log_output .clear () self .main_log_output .clear ()
self .log_signal .emit (" Displaying Mega download progress (extracted links hidden)...") self .log_signal .emit (" Displaying Mega download progress (extracted links hidden)...")
self.mega_download_log_preserved_once = False # Ensure no append logic triggers self .mega_download_log_preserved_once =False
current_main_dir =self .dir_input .text ().strip () current_main_dir =self .dir_input .text ().strip ()
download_dir_for_mega ="" download_dir_for_mega =""
@ -3572,7 +3569,7 @@ class DownloaderApp (QWidget ):
self , self ,
"Select Download Folder for Mega Links", "Select Download Folder for Mega Links",
suggestion_path , suggestion_path ,
options=QFileDialog.ShowDirsOnly | QFileDialog.DontUseNativeDialog # Added DontUseNativeDialog for consistency options =QFileDialog .ShowDirsOnly |QFileDialog .DontUseNativeDialog
) )
if not chosen_dir : if not chosen_dir :
@ -3586,7 +3583,7 @@ class DownloaderApp (QWidget ):
self .log_signal .emit (f"❌ Critical Error: Selected download directory '{download_dir_for_mega }' does not exist.") self .log_signal .emit (f"❌ Critical Error: Selected download directory '{download_dir_for_mega }' does not exist.")
return return
# selected_links_info already contains dicts with 'url', 'title', 'platform', 'key'
tasks_for_thread =selected_links_info tasks_for_thread =selected_links_info
if self .external_link_download_thread and self .external_link_download_thread .isRunning (): if self .external_link_download_thread and self .external_link_download_thread .isRunning ():
@ -3600,7 +3597,7 @@ class DownloaderApp (QWidget ):
self self
) )
self .external_link_download_thread .finished .connect (self ._on_external_link_download_thread_finished ) self .external_link_download_thread .finished .connect (self ._on_external_link_download_thread_finished )
# Connect progress_signal if ExternalLinkDownloadThread has it (it does)
self .external_link_download_thread .progress_signal .connect (self .handle_main_log ) self .external_link_download_thread .progress_signal .connect (self .handle_main_log )
self .external_link_download_thread .file_complete_signal .connect (self ._on_single_external_file_complete ) self .external_link_download_thread .file_complete_signal .connect (self ._on_single_external_file_complete )
@ -3614,29 +3611,29 @@ class DownloaderApp (QWidget ):
self .log_signal .emit ("✅ External link download thread finished.") self .log_signal .emit ("✅ External link download thread finished.")
self .progress_label .setText ("External link downloads complete. Ready for new task.") self .progress_label .setText ("External link downloads complete. Ready for new task.")
self.mega_download_log_preserved_once = True # Mark that a mega download just finished self .mega_download_log_preserved_once =True
self.log_signal.emit("INTERNAL: mega_download_log_preserved_once SET to True.") # Debug self .log_signal .emit ("INTERNAL: mega_download_log_preserved_once SET to True.")
if self .radio_only_links and self .radio_only_links .isChecked (): if self .radio_only_links and self .radio_only_links .isChecked ():
self .log_signal .emit (HTML_PREFIX +"<br><hr>--- End of Mega Download Log ---<br>") self .log_signal .emit (HTML_PREFIX +"<br><hr>--- End of Mega Download Log ---<br>")
# The _filter_links_log will be called by set_ui_enabled -> _handle_filter_mode_change
# No need to call it directly here as it might interfere with the flag.
self .set_ui_enabled (True ) # Enable UI after potential log update
# Reset the flag AFTER the UI update cycle (triggered by set_ui_enabled) is complete.
# This ensures the flag protects the log content throughout the entire UI update. self .set_ui_enabled (True )
if self .mega_download_log_preserved_once : if self .mega_download_log_preserved_once :
self .mega_download_log_preserved_once =False self .mega_download_log_preserved_once =False
self.log_signal.emit("INTERNAL: mega_download_log_preserved_once RESET to False.") # Debug self .log_signal .emit ("INTERNAL: mega_download_log_preserved_once RESET to False.")
if self .external_link_download_thread : if self .external_link_download_thread :
self .external_link_download_thread .deleteLater () self .external_link_download_thread .deleteLater ()
self .external_link_download_thread =None self .external_link_download_thread =None
def _on_single_external_file_complete (self ,url ,success ): def _on_single_external_file_complete (self ,url ,success ):
# This is a new slot to potentially update progress if needed per file
# For now, the thread's own progress_signal handles detailed logging.
pass pass
def _show_future_settings_dialog (self ): def _show_future_settings_dialog (self ):
"""Shows the placeholder dialog for future settings.""" """Shows the placeholder dialog for future settings."""
@ -3871,8 +3868,8 @@ class DownloaderApp (QWidget ):
link_data =(post_title ,link_text ,link_url ,platform ,decryption_key ) link_data =(post_title ,link_text ,link_url ,platform ,decryption_key )
self .external_link_queue .append (link_data ) self .external_link_queue .append (link_data )
if self .radio_only_links and self .radio_only_links .isChecked (): if self .radio_only_links and self .radio_only_links .isChecked ():
self .extracted_links_cache .append (link_data ) # Keep for now, might be redundant with below self .extracted_links_cache .append (link_data )
self ._update_download_extracted_links_button_state() # Update button if in mode self ._update_download_extracted_links_button_state ()
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 ()
should_display_in_external_log =self .show_external_links and not is_only_links_mode should_display_in_external_log =self .show_external_links and not is_only_links_mode
@ -3883,8 +3880,8 @@ class DownloaderApp (QWidget ):
QTimer .singleShot (0 ,self ._try_process_next_external_link ) QTimer .singleShot (0 ,self ._try_process_next_external_link )
return return
# Always add to extracted_links_cache so it's available if user switches mode later
if link_data not in self.extracted_links_cache: # Avoid duplicates if already added above if link_data not in self .extracted_links_cache :
self .extracted_links_cache .append (link_data ) self .extracted_links_cache .append (link_data )
def _try_process_next_external_link (self ): def _try_process_next_external_link (self ):
@ -3916,7 +3913,7 @@ class DownloaderApp (QWidget ):
post_title ,link_text ,link_url ,platform ,decryption_key =link_data post_title ,link_text ,link_url ,platform ,decryption_key =link_data
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 ()
max_link_text_len =50 # Consistent with _filter_links_log max_link_text_len =50
display_text =(link_text [:max_link_text_len ].strip ()+"..." display_text =(link_text [:max_link_text_len ].strip ()+"..."
if len (link_text )>max_link_text_len else link_text .strip ()) if len (link_text )>max_link_text_len else link_text .strip ())
formatted_link_info =f"{display_text } - {link_url } - {platform }" formatted_link_info =f"{display_text } - {link_url } - {platform }"
@ -3932,10 +3929,10 @@ class DownloaderApp (QWidget ):
title_html =f'<b style="color: #87CEEB;">{html .escape (post_title )}</b><br>' title_html =f'<b style="color: #87CEEB;">{html .escape (post_title )}</b><br>'
self .log_signal .emit (HTML_PREFIX +title_html ) self .log_signal .emit (HTML_PREFIX +title_html )
self ._current_link_post_title =post_title self ._current_link_post_title =post_title
# Emit plain text link info; handle_main_log will .append() it
self .log_signal .emit (formatted_link_info ) self .log_signal .emit (formatted_link_info )
elif self .show_external_links : elif self .show_external_links :
separator ="-"*45 # This separator is for _append_to_external_log's internal use if it were to use it, not for HTML separator ="-"*45
self ._append_to_external_log (formatted_link_info ,separator ) self ._append_to_external_log (formatted_link_info ,separator )
self ._is_processing_external_link_queue =False self ._is_processing_external_link_queue =False
@ -4105,18 +4102,17 @@ 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 .external_log_output :self .external_log_output .hide ()
if self .log_splitter :self .log_splitter .setSizes ([self .height (),0 ]) # type: ignore if self .log_splitter :self .log_splitter .setSizes ([self .height (),0 ])
# Conditional clear to preserve Mega download log in progress view
do_clear_log_in_filter_change =True do_clear_log_in_filter_change =True
if self.mega_download_log_preserved_once and \ if self .mega_download_log_preserved_once and self .only_links_log_display_mode ==LOG_DISPLAY_DOWNLOAD_PROGRESS :
self.only_links_log_display_mode == LOG_DISPLAY_DOWNLOAD_PROGRESS:
do_clear_log_in_filter_change =False do_clear_log_in_filter_change =False
if self .main_log_output and do_clear_log_in_filter_change : 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.") # Debug self .log_signal .emit ("INTERNAL: _handle_filter_mode_change - About to clear log.")
self .main_log_output .clear () self .main_log_output .clear ()
self.log_signal.emit("INTERNAL: _handle_filter_mode_change - Log cleared by _handle_filter_mode_change.") # Debug 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 ) if self .main_log_output :self .main_log_output .setMinimumHeight (0 )
self .log_signal .emit ("="*20 +" Mode changed to: Only Links "+"="*20 ) self .log_signal .emit ("="*20 +" Mode changed to: Only Links "+"="*20 )
@ -4138,7 +4134,7 @@ class DownloaderApp (QWidget ):
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"="*20 +f" Mode changed to: {filter_mode_text } "+"="*20 ) self .log_signal .emit (f"="*20 +f" Mode changed to: {filter_mode_text } "+"="*20 )
# Call _filter_links_log AFTER potential clear and other UI updates for "Only Links"
if is_only_links : if is_only_links :
self ._filter_links_log () self ._filter_links_log ()
@ -4174,15 +4170,14 @@ class DownloaderApp (QWidget ):
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 ""
if self.mega_download_log_preserved_once and \ if self .mega_download_log_preserved_once and self .only_links_log_display_mode ==LOG_DISPLAY_DOWNLOAD_PROGRESS :
self.only_links_log_display_mode == LOG_DISPLAY_DOWNLOAD_PROGRESS:
# Mega download just finished, in progress view. Preserve the log.
# Do nothing to the log content itself.
self .log_signal .emit ("INTERNAL: _filter_links_log - Preserving Mega log (due to mega_download_log_preserved_once).") self .log_signal .emit ("INTERNAL: _filter_links_log - Preserving Mega log (due to mega_download_log_preserved_once).")
elif self .only_links_log_display_mode ==LOG_DISPLAY_DOWNLOAD_PROGRESS : elif self .only_links_log_display_mode ==LOG_DISPLAY_DOWNLOAD_PROGRESS :
# This is when 'Progress View' is selected for the 'Only Links' log scope,
# AND a Mega download hasn't *just* finished (mega_download_log_preserved_once is False).
# We should clear the log and show the placeholder message.
self .log_signal .emit ("INTERNAL: _filter_links_log - In Progress View. Clearing for placeholder.") self .log_signal .emit ("INTERNAL: _filter_links_log - In Progress View. Clearing for placeholder.")
if self .main_log_output :self .main_log_output .clear () if self .main_log_output :self .main_log_output .clear ()
self .log_signal .emit ("INTERNAL: _filter_links_log - Cleared for progress placeholder.") self .log_signal .emit ("INTERNAL: _filter_links_log - Cleared for progress placeholder.")
@ -4190,11 +4185,11 @@ class DownloaderApp (QWidget ):
" Perform a Mega download to see its progress here, or switch back to 🔗 view.") " Perform a Mega download to see its progress here, or switch back to 🔗 view.")
self .log_signal .emit ("INTERNAL: _filter_links_log - Placeholder message emitted.") self .log_signal .emit ("INTERNAL: _filter_links_log - Placeholder message emitted.")
else: # self.only_links_log_display_mode == LOG_DISPLAY_LINKS else :
# In links view. Clear and show the (filtered) links.
self.log_signal.emit("INTERNAL: _filter_links_log - In links view branch. About to clear.") # Debug self .log_signal .emit ("INTERNAL: _filter_links_log - In links view branch. About to clear.")
if self .main_log_output :self .main_log_output .clear () if self .main_log_output :self .main_log_output .clear ()
self.log_signal.emit("INTERNAL: _filter_links_log - Cleared for links view.") # Debug self .log_signal .emit ("INTERNAL: _filter_links_log - Cleared for links view.")
current_title_for_display =None current_title_for_display =None
any_links_displayed_this_call =False any_links_displayed_this_call =False
@ -4205,13 +4200,13 @@ class DownloaderApp (QWidget ):
search_term in link_text .lower ()or search_term in link_text .lower ()or
search_term in link_url .lower ()or search_term in link_url .lower ()or
search_term in platform .lower ()or search_term in platform .lower ()or
(decryption_key and search_term in decryption_key.lower())) # type: ignore (decryption_key and search_term in decryption_key .lower ()))
if not matches_search : if not matches_search :
continue continue
any_links_displayed_this_call =True any_links_displayed_this_call =True
if post_title !=current_title_for_display : if post_title !=current_title_for_display :
if current_title_for_display is not None : # Add separator only if it's not the very first title if current_title_for_display is not None :
if self .main_log_output :self .main_log_output .insertHtml (separator_html ) if self .main_log_output :self .main_log_output .insertHtml (separator_html )
title_html =f'<b style="color: #87CEEB;">{html .escape (post_title )}</b><br>' title_html =f'<b style="color: #87CEEB;">{html .escape (post_title )}</b><br>'
@ -4220,19 +4215,19 @@ class DownloaderApp (QWidget ):
max_link_text_len =50 max_link_text_len =50
display_text =(link_text [:max_link_text_len ].strip ()+"..."if len (link_text )>max_link_text_len else link_text .strip ()) display_text =(link_text [:max_link_text_len ].strip ()+"..."if len (link_text )>max_link_text_len else link_text .strip ())
# Construct plain text link line
plain_link_info_line =f"{display_text } - {link_url } - {platform }" plain_link_info_line =f"{display_text } - {link_url } - {platform }"
if decryption_key : if decryption_key :
plain_link_info_line +=f" (Decryption Key: {decryption_key })" plain_link_info_line +=f" (Decryption Key: {decryption_key })"
if self .main_log_output : if self .main_log_output :
self.main_log_output.append(plain_link_info_line) # Appending plain text for default styling & spacing self .main_log_output .append (plain_link_info_line )
if any_links_displayed_this_call : if any_links_displayed_this_call :
if self.main_log_output: self.main_log_output.append("") # Extra blank line at the end if self .main_log_output :self .main_log_output .append ("")
elif not search_term and self.main_log_output: # No links in cache at all elif not search_term and self .main_log_output :
self .log_signal .emit (" (No links extracted yet or all filtered out in links view)") self .log_signal .emit (" (No links extracted yet or all filtered out in links view)")
# self.mega_download_log_preserved_once is now reset in _on_mega_download_thread_finished
if self .main_log_output :self .main_log_output .verticalScrollBar ().setValue (self .main_log_output .verticalScrollBar ().maximum ()) if self .main_log_output :self .main_log_output .verticalScrollBar ().setValue (self .main_log_output .verticalScrollBar ().maximum ())
@ -5982,7 +5977,7 @@ class DownloaderApp (QWidget ):
self .favorite_download_queue .clear () self .favorite_download_queue .clear ()
self .is_processing_favorites_queue =False self .is_processing_favorites_queue =False
self.only_links_log_display_mode = LOG_DISPLAY_LINKS # Reset on soft reset too self .only_links_log_display_mode =LOG_DISPLAY_LINKS
if hasattr (self ,'link_input'): if hasattr (self ,'link_input'):
if self .download_extracted_links_button : if self .download_extracted_links_button :
@ -6012,7 +6007,7 @@ class DownloaderApp (QWidget ):
"After Mega download, Mega log is shown THEN links are appended.\n" "After Mega download, Mega log is shown THEN links are appended.\n"
"Click to switch to 'Download Progress View'." "Click to switch to 'Download Progress View'."
) )
else: # LOG_DISPLAY_DOWNLOAD_PROGRESS else :
self .log_display_mode_toggle_button .setText ("⬇️ Progress View") self .log_display_mode_toggle_button .setText ("⬇️ Progress View")
self .log_display_mode_toggle_button .setToolTip ( self .log_display_mode_toggle_button .setToolTip (
"Current View: Mega Download Progress.\n" "Current View: Mega Download Progress.\n"
@ -6023,13 +6018,13 @@ class DownloaderApp (QWidget ):
def _toggle_log_display_mode (self ): def _toggle_log_display_mode (self ):
self .only_links_log_display_mode =LOG_DISPLAY_DOWNLOAD_PROGRESS if self .only_links_log_display_mode ==LOG_DISPLAY_LINKS else LOG_DISPLAY_LINKS self .only_links_log_display_mode =LOG_DISPLAY_DOWNLOAD_PROGRESS if self .only_links_log_display_mode ==LOG_DISPLAY_LINKS else LOG_DISPLAY_LINKS
self ._update_log_display_mode_button_text () self ._update_log_display_mode_button_text ()
self._filter_links_log() # Refresh the log based on the new mode self ._filter_links_log ()
def cancel_download_button_action (self ): def cancel_download_button_action (self ):
if not self .cancel_btn .isEnabled ()and not self .cancellation_event .is_set ():self .log_signal .emit (" No active download to cancel or already cancelling.");return if not self .cancel_btn .isEnabled ()and not self .cancellation_event .is_set ():self .log_signal .emit (" No active download to cancel or already cancelling.");return
self .log_signal .emit ("⚠️ Requesting cancellation of download process (soft reset)...") self .log_signal .emit ("⚠️ Requesting cancellation of download process (soft reset)...")
if self .mega_download_thread and self .mega_download_thread .isRunning (): if self .external_link_download_thread and self .external_link_download_thread .isRunning ():
self .log_signal .emit (" Cancelling active External Link download thread...") self .log_signal .emit (" Cancelling active External Link download thread...")
self .external_link_download_thread .cancel () self .external_link_download_thread .cancel ()
@ -6369,7 +6364,7 @@ class DownloaderApp (QWidget ):
self .already_logged_bold_key_terms .clear () self .already_logged_bold_key_terms .clear ()
self .missed_key_terms_buffer .clear () self .missed_key_terms_buffer .clear ()
self .favorite_download_queue .clear () self .favorite_download_queue .clear ()
self .only_links_log_display_mode = LOG_DISPLAY_LINKS # Reset on full reset self .only_links_log_display_mode =LOG_DISPLAY_LINKS
self .mega_download_log_preserved_once =False self .mega_download_log_preserved_once =False
self .permanently_failed_files_for_dialog .clear () self .permanently_failed_files_for_dialog .clear ()
self .favorite_download_scope =FAVORITE_SCOPE_SELECTED_LOCATION self .favorite_download_scope =FAVORITE_SCOPE_SELECTED_LOCATION