This commit is contained in:
Yuvi9587
2025-06-06 13:09:06 +01:00
parent d8ed588033
commit 3bc3c7b760
3 changed files with 348 additions and 83 deletions

View File

@@ -13,6 +13,16 @@ 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:
from drive import download_mega_file as drive_download_mega_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:
print(f"ERROR importing from drive.py: {drive_import_err}. External drive downloads will fail.")
except ImportError : except ImportError :
print ("ERROR: mega.py library not found. Please install it: pip install mega.py") print ("ERROR: mega.py library not found. Please install it: pip install mega.py")
try : try :

228
drive.py Normal file
View File

@@ -0,0 +1,228 @@
from mega import Mega # Correctly import the Mega class
import os
import requests # Added import for requests.exceptions
import traceback
from urllib.parse import urlparse, urlunparse, parse_qs, urlencode
try:
import gdown
GDOWN_AVAILABLE = True
except ImportError:
GDOWN_AVAILABLE = False
def download_mega_file(mega_link, download_path=".", logger_func=print):
"""
Downloads a file from a public Mega.nz link.
Args:
mega_link (str): The public Mega.nz link to the file.
download_path (str, optional): The directory to save the downloaded file.
Defaults to the current directory.
logger_func (callable, optional): Function to use for logging. Defaults to print.
"""
logger_func("Initializing Mega client...")
mega_client = Mega() # Instantiate the imported Mega class
# For public links, login is usually not required.
# If you needed to log in for private files:
# m = mega.login("your_email@example.com", "your_password")
# Or anonymous login for some operations:
m = mega_client.login() # Call login on the instance
logger_func(f"Attempting to download from: {mega_link}")
try:
# Ensure the download path exists
if not os.path.exists(download_path):
logger_func(f"Download path '{download_path}' does not exist. Creating it...")
os.makedirs(download_path, exist_ok=True)
# It's often better to let mega.py handle finding the file from the URL
# and then downloading it.
# The `download_url` method is convenient for public links.
logger_func(f"Starting download to '{download_path}'...")
# mega.py's download_url will download the file to the specified path
# and name it appropriately.
file_handle = m.download_url(mega_link, dest_path=download_path) # m is the logged-in client
if file_handle:
# The download_url method itself handles the file saving.
# The return value 'file_handle' might be the filename or a handle,
# depending on the library version and specifics.
# For modern mega.py, it often returns the local path to the downloaded file.
logger_func(f"File downloaded successfully! (Handle: {file_handle})")
# If you need the exact filename it was saved as:
# You might need to inspect what `download_url` returns or list dir contents
# if the filename isn't directly returned in a usable way.
# Often, the library names it based on the Mega file name.
# For this example, we'll assume it's handled and saved in download_path.
# To get the actual filename (as mega.py might rename it)
# We can list the directory and find the newest file if only one download happened
# Or, more robustly, use the information from `m.get_public_url_info(mega_link)`
# to get the expected filename.
try:
info = m.get_public_url_info(mega_link)
filename = info.get('name', 'downloaded_file') # Default if name not found
full_download_path = os.path.join(download_path, filename)
logger_func(f"Downloaded file should be at: {full_download_path}")
if not os.path.exists(full_download_path):
logger_func(f"Warning: Expected file '{full_download_path}' not found. The library might have saved it with a different name or failed silently after appearing to succeed.")
except Exception as e_info:
logger_func(f"Could not retrieve file info to confirm filename: {e_info}")
else:
logger_func("Download failed. The download_url method did not return a file handle.")
except PermissionError:
logger_func(f"Error: Permission denied to write to '{download_path}'. Please check permissions.")
except FileNotFoundError:
logger_func(f"Error: The specified download path '{download_path}' is invalid or a component was not found.")
except ConnectionError as e: # More specific requests exception
logger_func(f"Error: Connection problem. {e}")
except requests.exceptions.RequestException as e: # Catch other requests-related errors
logger_func(f"Error during request to Mega: {e}")
except Exception as e:
logger_func(f"An error occurred during Mega download: {e}")
traceback.print_exc()
def download_gdrive_file(gdrive_link, download_path=".", logger_func=print):
"""
Downloads a file from a public Google Drive link.
Args:
gdrive_link (str): The public Google Drive link to the file.
download_path (str, optional): The directory to save the downloaded file.
Defaults to the current directory.
logger_func (callable, optional): Function to use for logging. Defaults to print.
"""
if not GDOWN_AVAILABLE:
logger_func("❌ Error: gdown library is not installed. Cannot download from Google Drive.")
logger_func("Please install it: pip install gdown")
raise ImportError("gdown library not found. Please install it: pip install gdown")
logger_func(f"Attempting to download from Google Drive: {gdrive_link}")
try:
if not os.path.exists(download_path):
logger_func(f"Download path '{download_path}' does not exist. Creating it...")
os.makedirs(download_path, exist_ok=True)
logger_func(f"Starting Google Drive download to '{download_path}'...")
# gdown.download returns the path to the downloaded file
output_file_path = gdown.download(gdrive_link, output=download_path, quiet=False, fuzzy=True)
if output_file_path and os.path.exists(os.path.join(download_path, os.path.basename(output_file_path))):
logger_func(f"✅ Google Drive file downloaded successfully: {output_file_path}")
elif output_file_path: # gdown might return a path but if download_path was a dir, it might be just the filename
full_path_check = os.path.join(download_path, output_file_path)
if os.path.exists(full_path_check):
logger_func(f"✅ Google Drive file downloaded successfully: {full_path_check}")
else:
logger_func(f"⚠️ Google Drive download finished, gdown returned '{output_file_path}', but file not found at expected location.")
logger_func(f" Please check '{download_path}' for the downloaded file, it might have a different name than expected by gdown's return.")
# As a fallback, list files in dir if only one was expected
files_in_dest = [f for f in os.listdir(download_path) if os.path.isfile(os.path.join(download_path, f))]
if len(files_in_dest) == 1:
logger_func(f" Found one file in destination: {os.path.join(download_path, files_in_dest[0])}. Assuming this is it.")
elif len(files_in_dest) > 1 and output_file_path in files_in_dest: # if gdown returned just filename
logger_func(f" Confirmed file '{output_file_path}' exists in '{download_path}'.")
else:
raise Exception(f"gdown download failed or file not found. Returned: {output_file_path}")
else:
logger_func("❌ Google Drive download failed. gdown did not return an output path.")
raise Exception("gdown download failed.")
except PermissionError:
logger_func(f"❌ Error: Permission denied to write to '{download_path}'. Please check permissions.")
raise
except Exception as e:
logger_func(f"❌ An error occurred during Google Drive download: {e}")
traceback.print_exc()
raise
def _get_filename_from_headers(headers):
cd = headers.get('content-disposition')
if not cd:
return None
fname_match = re.findall('filename="?([^"]+)"?', cd)
if fname_match:
return fname_match[0].strip()
return None
def download_dropbox_file(dropbox_link, download_path=".", logger_func=print):
"""
Downloads a file from a public Dropbox link.
Args:
dropbox_link (str): The public Dropbox link to the file.
download_path (str, optional): The directory to save the downloaded file.
Defaults to the current directory.
logger_func (callable, optional): Function to use for logging. Defaults to print.
"""
logger_func(f"Attempting to download from Dropbox: {dropbox_link}")
# Modify URL for direct download
parsed_url = urlparse(dropbox_link)
query_params = parse_qs(parsed_url.query)
query_params['dl'] = ['1'] # Set dl=1 for direct download
new_query = urlencode(query_params, doseq=True)
direct_download_url = urlunparse(parsed_url._replace(query=new_query))
logger_func(f" Using direct download URL: {direct_download_url}")
try:
if not os.path.exists(download_path):
logger_func(f"Download path '{download_path}' does not exist. Creating it...")
os.makedirs(download_path, exist_ok=True)
with requests.get(direct_download_url, stream=True, allow_redirects=True, timeout=(10,300)) as r:
r.raise_for_status()
filename = _get_filename_from_headers(r.headers) or os.path.basename(urlparse(dropbox_link).path) or "dropbox_downloaded_file"
# Sanitize filename (basic)
filename = re.sub(r'[<>:"/\\|?*]', '_', filename)
full_save_path = os.path.join(download_path, filename)
logger_func(f"Starting Dropbox download of '{filename}' to '{full_save_path}'...")
with open(full_save_path, 'wb') as f:
for chunk in r.iter_content(chunk_size=8192):
f.write(chunk)
logger_func(f"✅ Dropbox file downloaded successfully: {full_save_path}")
except Exception as e:
logger_func(f"❌ An error occurred during Dropbox download: {e}")
traceback.print_exc()
raise
if __name__ == "__main__":
# --- IMPORTANT ---
# Replace this with an ACTUAL PUBLIC MEGA.NZ FILE LINK to test.
# Using the specific link you provided:
mega_file_link = "https://mega.nz/file/03oRjBQT#Tcbp5sQVIyPbdmv8sLgbb9Lf9AZvZLdKRSQiuXkNW0k"
if not mega_file_link.startswith("https://mega.nz/file/"):
print("Invalid Mega file link format. It should start with 'https://mega.nz/file/'.") # Or use logger_func if testing outside main
else:
# Specify your desired download directory
# If you want to download to a specific folder, e.g., "MegaDownloads" in your user's Downloads folder:
# user_downloads_path = os.path.expanduser("~/Downloads")
# download_directory = os.path.join(user_downloads_path, "MegaDownloads")
# For this example, we'll download to a "mega_downloads" subfolder in the script's directory
script_dir = os.path.dirname(os.path.abspath(__file__))
download_directory = os.path.join(script_dir, "mega_downloads")
print(f"Files will be downloaded to: {download_directory}") # Or use logger_func
download_mega_file(mega_file_link, download_directory, logger_func=print)
# Test Google Drive
# gdrive_test_link = "YOUR_PUBLIC_GOOGLE_DRIVE_FILE_LINK_HERE" # Replace with a real link
# if gdrive_test_link and gdrive_test_link != "YOUR_PUBLIC_GOOGLE_DRIVE_FILE_LINK_HERE":
# gdrive_download_dir = os.path.join(script_dir, "gdrive_downloads")
# print(f"\nGoogle Drive files will be downloaded to: {gdrive_download_dir}")
# download_gdrive_file(gdrive_test_link, gdrive_download_dir, logger_func=print)
#
# # Test Dropbox
# dropbox_test_link = "YOUR_PUBLIC_DROPBOX_FILE_LINK_HERE" # Replace with a real link
# if dropbox_test_link and dropbox_test_link != "YOUR_PUBLIC_DROPBOX_FILE_LINK_HERE":
# dropbox_download_dir = os.path.join(script_dir, "dropbox_downloads")
# print(f"\nDropbox files will be downloaded to: {dropbox_download_dir}")
# download_dropbox_file(dropbox_test_link, dropbox_download_dir, logger_func=print)

193
main.py
View File

@@ -61,8 +61,9 @@ try :
STYLE_DATE_BASED , STYLE_DATE_BASED ,
STYLE_POST_TITLE_GLOBAL_NUMBERING , STYLE_POST_TITLE_GLOBAL_NUMBERING ,
CREATOR_DOWNLOAD_DEFAULT_FOLDER_IGNORE_WORDS , CREATOR_DOWNLOAD_DEFAULT_FOLDER_IGNORE_WORDS ,
download_mega_file drive_download_mega_file, # Use the aliased import from drive.py
download_gdrive_file,
download_dropbox_file
) )
print ("Successfully imported names from downloader_utils.") print ("Successfully imported names from downloader_utils.")
except ImportError as e : except ImportError as e :
@@ -96,7 +97,9 @@ except ImportError as e :
STYLE_DATE_BASED ="date_based" STYLE_DATE_BASED ="date_based"
STYLE_POST_TITLE_GLOBAL_NUMBERING ="post_title_global_numbering" STYLE_POST_TITLE_GLOBAL_NUMBERING ="post_title_global_numbering"
CREATOR_DOWNLOAD_DEFAULT_FOLDER_IGNORE_WORDS =set () CREATOR_DOWNLOAD_DEFAULT_FOLDER_IGNORE_WORDS =set ()
def download_mega_file (*args ,**kwargs ):pass def drive_download_mega_file(*args, **kwargs): print("drive_download_mega_file (stub)"); pass
def download_gdrive_file(*args, **kwargs): print("download_gdrive_file (stub)"); pass
def download_dropbox_file(*args, **kwargs): print("download_dropbox_file (stub)"); pass
except Exception as e : except Exception as e :
print (f"--- UNEXPECTED IMPORT ERROR ---") print (f"--- UNEXPECTED IMPORT ERROR ---")
@@ -141,29 +144,30 @@ CONFIRM_ADD_ALL_CANCEL_DOWNLOAD =3
LOG_DISPLAY_LINKS = "links" LOG_DISPLAY_LINKS = "links"
LOG_DISPLAY_DOWNLOAD_PROGRESS = "download_progress" LOG_DISPLAY_DOWNLOAD_PROGRESS = "download_progress"
class DownloadMegaLinksDialog (QDialog ): class DownloadExtractedLinksDialog(QDialog):
"""A dialog to select and initiate download for extracted Mega links.""" """A dialog to select and initiate download for extracted supported links."""
download_requested =pyqtSignal (list ) download_requested =pyqtSignal (list )
def __init__ (self ,mega_links_data ,parent =None ): def __init__ (self ,links_data ,parent =None ):
super ().__init__ (parent ) super ().__init__ (parent )
self .mega_links_data =mega_links_data self .links_data = links_data
self .setWindowTitle ("Download Selected Mega Links") self .setWindowTitle ("Download Selected External Links")
self .setMinimumSize (500 ,400 ) self .setMinimumSize (500 ,400 )
layout =QVBoxLayout (self ) layout =QVBoxLayout (self )
label =QLabel (f"Found {len (self .mega_links_data )} Mega link(s). Select which ones to download:") label =QLabel (f"Found {len (self .links_data )} supported link(s) (Mega, GDrive, Dropbox). Select to download:")
label .setAlignment (Qt .AlignCenter ) label .setAlignment (Qt .AlignCenter )
label .setWordWrap (True ) label .setWordWrap (True )
layout .addWidget (label ) layout .addWidget (label )
self .links_list_widget =QListWidget () self .links_list_widget =QListWidget ()
self .links_list_widget .setSelectionMode (QAbstractItemView .NoSelection ) self .links_list_widget .setSelectionMode (QAbstractItemView .NoSelection )
for link_info in self .mega_links_data : for link_info in self .links_data :
display_text =f"{link_info ['title']} - {link_info ['link_text']} ({link_info ['url']})" platform_display = link_info.get('platform', 'unknown').upper()
display_text =f"[{platform_display}] {link_info ['title']} - {link_info ['link_text']} ({link_info ['url']})"
item =QListWidgetItem (display_text ) item =QListWidgetItem (display_text )
item .setData (Qt .UserRole ,link_info ) item .setData (Qt .UserRole ,link_info )
item .setFlags (item .flags ()|Qt .ItemIsUserCheckable ) item .setFlags (item .flags ()|Qt .ItemIsUserCheckable )
@@ -209,7 +213,7 @@ class DownloadMegaLinksDialog (QDialog ):
self .download_requested .emit (selected_links ) self .download_requested .emit (selected_links )
self .accept () self .accept ()
else : else :
QMessageBox .information (self ,"No Selection","Please select at least one Mega link to download.") QMessageBox .information (self ,"No Selection","Please select at least one link to download.")
class ConfirmAddAllDialog (QDialog ): class ConfirmAddAllDialog (QDialog ):
"""A dialog to confirm adding multiple new names to Known.txt.""" """A dialog to confirm adding multiple new names to Known.txt."""
@@ -2280,36 +2284,59 @@ class TourDialog (QDialog ):
print (f"[Tour] CRITICAL ERROR in run_tour_if_needed: {e }") print (f"[Tour] CRITICAL ERROR in run_tour_if_needed: {e }")
return QDialog .Rejected return QDialog .Rejected
class MegaDownloadThread (QThread ): class ExternalLinkDownloadThread(QThread):
"""A QThread to handle downloading multiple Mega links sequentially.""" """A QThread to handle downloading multiple external links sequentially."""
progress_signal =pyqtSignal (str ) progress_signal =pyqtSignal (str )
file_complete_signal =pyqtSignal (str ,bool ) file_complete_signal =pyqtSignal (str ,bool )
finished_signal =pyqtSignal () finished_signal =pyqtSignal ()
def __init__ (self ,tasks_to_download ,download_base_path ,parent_logger_func ,parent =None ): def __init__ (self ,tasks_to_download ,download_base_path ,parent_logger_func ,parent =None):
super ().__init__ (parent ) super ().__init__ (parent )
self .tasks =tasks_to_download self .tasks =tasks_to_download
self .download_base_path =download_base_path self .download_base_path =download_base_path
self .parent_logger_func =parent_logger_func self .parent_logger_func =parent_logger_func
self .is_cancelled =False self .is_cancelled =False
def run (self ): def run(self):
self .progress_signal .emit (f" Starting Mega download thread for {len (self .tasks )} link(s).") self.progress_signal.emit(f" Starting external link download thread for {len(self.tasks)} link(s).")
for i ,task_info in enumerate (self .tasks ): for i ,task_info in enumerate (self .tasks ):
if self .is_cancelled : if self .is_cancelled :
self .progress_signal .emit ("Mega download cancelled by user.") self.progress_signal.emit("External link download cancelled by user.")
break break
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']
self .progress_signal .emit (f"Mega Download ({i +1 }/{len (self .tasks )}): Starting '{post_title }' from {full_mega_url }") key = task_info.get('key', '') # Primarily for Mega
try :
download_mega_file (full_mega_url ,self .download_base_path ,logger_func =self .parent_logger_func ) self.progress_signal.emit(f"Download ({i + 1}/{len(self.tasks)}): Starting '{post_title}' ({platform.upper()}) from {full_mega_url}")
self .file_complete_signal .emit (full_mega_url ,True )
try :
if platform == 'mega':
# Ensure key is part of the URL for mega.py
if key:
parsed_original_url = urlparse(full_mega_url)
if key not in parsed_original_url.fragment:
base_url_no_fragment = full_mega_url.split('#')[0]
full_mega_url_with_key = f"{base_url_no_fragment}#{key}"
self.progress_signal.emit(f" Adjusted Mega URL with key: {full_mega_url_with_key}")
else:
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)
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)
elif platform == 'google drive':
download_gdrive_file(full_mega_url, self.download_base_path, logger_func=self.parent_logger_func)
elif platform == 'dropbox':
download_dropbox_file(full_mega_url, self.download_base_path, logger_func=self.parent_logger_func)
else:
self.progress_signal.emit(f"⚠️ Unsupported platform '{platform}' for link: {full_mega_url}")
self.file_complete_signal.emit(full_mega_url, False)
continue
self.file_complete_signal.emit(full_mega_url, True)
except Exception as e : except Exception as e :
self .progress_signal .emit (f"❌ Error downloading Mega link '{full_mega_url }' (from post '{post_title }'): {e }") self.progress_signal.emit(f"❌ Error downloading ({platform.upper()}) link '{full_mega_url}' (from post '{post_title}'): {e}")
self .file_complete_signal .emit (full_mega_url ,False ) self.file_complete_signal.emit(full_mega_url, False)
self .finished_signal .emit () self .finished_signal .emit ()
def cancel (self ): def cancel (self ):
@@ -2350,7 +2377,7 @@ class DownloaderApp (QWidget ):
self .download_thread =None self .download_thread =None
self .thread_pool =None self .thread_pool =None
self .cancellation_event =threading .Event () self .cancellation_event =threading .Event ()
self .mega_download_thread =None self .external_link_download_thread =None
self .pause_event =threading .Event () self .pause_event =threading .Event ()
self .active_futures =[] self .active_futures =[]
self .total_posts_to_process =0 self .total_posts_to_process =0
@@ -3435,31 +3462,33 @@ class DownloaderApp (QWidget ):
self .log_signal .emit (" Download extracted links button clicked, but not in 'Only Links' mode.") self .log_signal .emit (" Download extracted links button clicked, but not in 'Only Links' mode.")
return return
mega_links_to_show =[] supported_platforms = {'mega', 'google drive', 'dropbox'}
links_to_show_in_dialog =[]
for link_data_tuple in self .extracted_links_cache : for link_data_tuple in self .extracted_links_cache :
platform = link_data_tuple[3].lower()
if link_data_tuple [3 ]=='mega': if platform in supported_platforms:
mega_links_to_show .append ({ links_to_show_in_dialog.append ({
'title':link_data_tuple [0 ], 'title':link_data_tuple [0 ],
'link_text':link_data_tuple [1 ], 'link_text':link_data_tuple [1 ],
'url':link_data_tuple [2 ], 'url':link_data_tuple [2 ],
'platform':link_data_tuple [3 ], 'platform':platform,
'key':link_data_tuple [4 ] 'key':link_data_tuple [4 ]
}) })
if not mega_links_to_show : if not links_to_show_in_dialog :
QMessageBox .information (self ,"No Mega Links","No Mega links were found in the extracted links.") QMessageBox .information (self ,"No Supported Links","No Mega, Google Drive, or Dropbox links were found in the extracted links.")
return return
dialog =DownloadMegaLinksDialog (mega_links_to_show ,self ) dialog = DownloadExtractedLinksDialog(links_to_show_in_dialog, self)
dialog .download_requested .connect (self ._handle_mega_links_download_request ) dialog .download_requested .connect (self ._handle_extracted_links_download_request )
dialog .exec_ () dialog .exec_ ()
def _handle_mega_links_download_request (self ,selected_links_info ): def _handle_extracted_links_download_request(self, selected_links_info):
if not selected_links_info : if not selected_links_info :
self .log_signal .emit (" No Mega 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()
@@ -3470,15 +3499,14 @@ class DownloaderApp (QWidget ):
download_dir_for_mega ="" download_dir_for_mega =""
if current_main_dir and os .path .isdir (current_main_dir ): if current_main_dir and os .path .isdir (current_main_dir ):
download_dir_for_mega =current_main_dir download_dir_for_mega =current_main_dir
self .log_signal .emit (f" Using existing main download location for Mega links: {download_dir_for_mega }") self.log_signal.emit(f" Using existing main download location for external links: {download_dir_for_mega}")
else : else :
if not current_main_dir : if not current_main_dir :
self .log_signal .emit (" Main download location is empty. Prompting for Mega download folder.") self.log_signal.emit(" Main download location is empty. Prompting for download folder.")
else : else :
self .log_signal .emit (f"⚠️ Main download location '{current_main_dir }' is not a valid directory. Prompting for Mega download folder.") self.log_signal.emit(
f"⚠️ Main download location '{current_main_dir}' is not a valid directory. Prompting for download folder.")
suggestion_path =current_main_dir if current_main_dir else QStandardPaths .writableLocation (QStandardPaths .DownloadLocation ) suggestion_path =current_main_dir if current_main_dir else QStandardPaths .writableLocation (QStandardPaths .DownloadLocation )
@@ -3486,53 +3514,48 @@ class DownloaderApp (QWidget ):
chosen_dir =QFileDialog .getExistingDirectory ( chosen_dir =QFileDialog .getExistingDirectory (
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
) )
if not chosen_dir : if not chosen_dir :
self .log_signal .emit (" Mega links download cancelled - no download directory selected from prompt.") self.log_signal.emit(" External links download cancelled - no download directory selected from prompt.")
return return
download_dir_for_mega =chosen_dir download_dir_for_mega =chosen_dir
self.log_signal.emit(f" Preparing to download {len(selected_links_info)} selected external link(s) to: {download_dir_for_mega}")
self .log_signal .emit (f" Preparing to download {len (selected_links_info )} selected Mega link(s) to: {download_dir_for_mega }")
if not os .path .exists (download_dir_for_mega ): if not os .path .exists (download_dir_for_mega ):
self .log_signal .emit (f"❌ Critical Error: Selected Mega 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
tasks_for_thread =[] # selected_links_info already contains dicts with 'url', 'title', 'platform', 'key'
for item in selected_links_info : tasks_for_thread = selected_links_info
full_url =item ['url']
key =item ['key']
if key : if self.external_link_download_thread and self.external_link_download_thread.isRunning():
url_parts =urlparse (full_url ) QMessageBox.warning(self, "Busy", "Another external link download is already in progress.")
if key not in url_parts .fragment : return
if url_parts .fragment :
base_url_no_fragment =full_url .split ('#')[0 ]
full_url =f"{base_url_no_fragment }#{key }"
else :
full_url =f"{full_url }#{key }"
tasks_for_thread .append ({'url':full_url ,'title':item ['title']}) self.external_link_download_thread = ExternalLinkDownloadThread(
tasks_for_thread,
if self .mega_download_thread and self .mega_download_thread .isRunning (): download_dir_for_mega,
QMessageBox .warning (self ,"Busy","Another Mega download is already in progress.") self.log_signal.emit,
return self
)
self .mega_download_thread =MegaDownloadThread (tasks_for_thread ,download_dir_for_mega ,self .log_signal .emit ,self ) self.external_link_download_thread.finished.connect(self._on_external_link_download_thread_finished)
self .mega_download_thread .finished .connect (self ._on_mega_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.file_complete_signal.connect(self._on_single_external_file_complete)
self .set_ui_enabled (False ) self .set_ui_enabled (False )
self .progress_label .setText (f"Downloading Mega Links (0/{len (tasks_for_thread )})...") self.progress_label.setText(f"Downloading External Links (0/{len(tasks_for_thread)})...")
self .mega_download_thread .start () self.external_link_download_thread.start()
def _on_mega_download_thread_finished (self ): def _on_external_link_download_thread_finished(self):
self .log_signal .emit ("Mega download thread finished.") self.log_signal.emit("External link download thread finished.")
self .progress_label .setText ("Mega 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 # Mark that a mega download just finished
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.") # Debug
@@ -3550,10 +3573,14 @@ class DownloaderApp (QWidget ):
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.") # Debug
if self .mega_download_thread : if self.external_link_download_thread:
self .mega_download_thread .deleteLater () self.external_link_download_thread.deleteLater()
self .mega_download_thread =None self.external_link_download_thread = None
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
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."""
dialog =FutureSettingsDialog (self ) dialog =FutureSettingsDialog (self )
@@ -5660,7 +5687,7 @@ class DownloaderApp (QWidget ):
if self .cancellation_event .is_set (): if self .cancellation_event .is_set ():
self .log_signal .emit (" Cancellation detected after/during task submission loop.") self .log_signal .emit (" Cancellation detected after/during task submission loop.")
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 .mega_download_thread .cancel () self .mega_download_thread .cancel ()
@@ -5750,9 +5777,9 @@ class DownloaderApp (QWidget ):
if self .bottom_action_buttons_stack : if self .bottom_action_buttons_stack :
self .bottom_action_buttons_stack .setCurrentIndex (0 ) self .bottom_action_buttons_stack .setCurrentIndex (0 )
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 Mega download due to UI state change.") self .log_signal .emit (" Cancelling active Mega download due to UI state change.")
self .mega_download_thread .cancel () self .external_link_download_thread .cancel ()
else : else :
pass pass
@@ -5938,9 +5965,9 @@ class DownloaderApp (QWidget ):
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 .mega_download_thread and self .mega_download_thread .isRunning ():
self .log_signal .emit (" Cancelling active Mega download thread...") self .log_signal .emit (" Cancelling active External Link download thread...")
self .mega_download_thread .cancel () self .external_link_download_thread .cancel ()
current_url =self .link_input .text () current_url =self .link_input .text ()
current_dir =self .dir_input .text () current_dir =self .dir_input .text ()
@@ -6220,9 +6247,9 @@ class DownloaderApp (QWidget ):
self .retry_thread_pool .shutdown (wait =True ) self .retry_thread_pool .shutdown (wait =True )
self .retry_thread_pool =None self .retry_thread_pool =None
if self .mega_download_thread and not self .mega_download_thread .isRunning (): if self.external_link_download_thread and not self.external_link_download_thread.isRunning():
self .mega_download_thread .deleteLater () self.external_link_download_thread.deleteLater()
self .mega_download_thread =None self.external_link_download_thread = None
self .active_retry_futures .clear () self .active_retry_futures .clear ()
self .active_retry_futures_map .clear () self .active_retry_futures_map .clear ()