mirror of
https://github.com/Yuvi9587/Kemono-Downloader.git
synced 2025-12-29 16:14:44 +00:00
Commit
This commit is contained in:
@@ -13,6 +13,16 @@ from PyQt5 .QtCore import QObject ,pyqtSignal ,QThread ,QMutex ,QMutexLocker
|
||||
from urllib .parse import urlparse
|
||||
try :
|
||||
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 :
|
||||
print ("ERROR: mega.py library not found. Please install it: pip install mega.py")
|
||||
try :
|
||||
|
||||
228
drive.py
Normal file
228
drive.py
Normal 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
193
main.py
@@ -61,8 +61,9 @@ try :
|
||||
STYLE_DATE_BASED ,
|
||||
STYLE_POST_TITLE_GLOBAL_NUMBERING ,
|
||||
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.")
|
||||
except ImportError as e :
|
||||
@@ -96,7 +97,9 @@ except ImportError as e :
|
||||
STYLE_DATE_BASED ="date_based"
|
||||
STYLE_POST_TITLE_GLOBAL_NUMBERING ="post_title_global_numbering"
|
||||
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 :
|
||||
print (f"--- UNEXPECTED IMPORT ERROR ---")
|
||||
@@ -141,29 +144,30 @@ CONFIRM_ADD_ALL_CANCEL_DOWNLOAD =3
|
||||
LOG_DISPLAY_LINKS = "links"
|
||||
LOG_DISPLAY_DOWNLOAD_PROGRESS = "download_progress"
|
||||
|
||||
class DownloadMegaLinksDialog (QDialog ):
|
||||
"""A dialog to select and initiate download for extracted Mega links."""
|
||||
class DownloadExtractedLinksDialog(QDialog):
|
||||
"""A dialog to select and initiate download for extracted supported links."""
|
||||
|
||||
download_requested =pyqtSignal (list )
|
||||
|
||||
def __init__ (self ,mega_links_data ,parent =None ):
|
||||
def __init__ (self ,links_data ,parent =None ):
|
||||
|
||||
|
||||
super ().__init__ (parent )
|
||||
self .mega_links_data =mega_links_data
|
||||
self .setWindowTitle ("Download Selected Mega Links")
|
||||
self .links_data = links_data
|
||||
self .setWindowTitle ("Download Selected External Links")
|
||||
self .setMinimumSize (500 ,400 )
|
||||
|
||||
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 .setWordWrap (True )
|
||||
layout .addWidget (label )
|
||||
|
||||
self .links_list_widget =QListWidget ()
|
||||
self .links_list_widget .setSelectionMode (QAbstractItemView .NoSelection )
|
||||
for link_info in self .mega_links_data :
|
||||
display_text =f"{link_info ['title']} - {link_info ['link_text']} ({link_info ['url']})"
|
||||
for link_info in self .links_data :
|
||||
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 .setData (Qt .UserRole ,link_info )
|
||||
item .setFlags (item .flags ()|Qt .ItemIsUserCheckable )
|
||||
@@ -209,7 +213,7 @@ class DownloadMegaLinksDialog (QDialog ):
|
||||
self .download_requested .emit (selected_links )
|
||||
self .accept ()
|
||||
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 ):
|
||||
"""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 }")
|
||||
return QDialog .Rejected
|
||||
|
||||
class MegaDownloadThread (QThread ):
|
||||
"""A QThread to handle downloading multiple Mega links sequentially."""
|
||||
class ExternalLinkDownloadThread(QThread):
|
||||
"""A QThread to handle downloading multiple external links sequentially."""
|
||||
progress_signal =pyqtSignal (str )
|
||||
file_complete_signal =pyqtSignal (str ,bool )
|
||||
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 )
|
||||
self .tasks =tasks_to_download
|
||||
self .download_base_path =download_base_path
|
||||
self .parent_logger_func =parent_logger_func
|
||||
self .is_cancelled =False
|
||||
|
||||
def run (self ):
|
||||
self .progress_signal .emit (f"ℹ️ Starting Mega download thread for {len (self .tasks )} link(s).")
|
||||
def run(self):
|
||||
self.progress_signal.emit(f"ℹ️ Starting external link download thread for {len(self.tasks)} link(s).")
|
||||
for i ,task_info in enumerate (self .tasks ):
|
||||
if self .is_cancelled :
|
||||
self .progress_signal .emit ("Mega download cancelled by user.")
|
||||
self.progress_signal.emit("External link download cancelled by user.")
|
||||
break
|
||||
|
||||
platform = task_info.get('platform', 'unknown').lower()
|
||||
full_mega_url =task_info ['url']
|
||||
post_title =task_info ['title']
|
||||
self .progress_signal .emit (f"Mega Download ({i +1 }/{len (self .tasks )}): Starting '{post_title }' from {full_mega_url }")
|
||||
try :
|
||||
key = task_info.get('key', '') # Primarily for Mega
|
||||
|
||||
download_mega_file (full_mega_url ,self .download_base_path ,logger_func =self .parent_logger_func )
|
||||
self .file_complete_signal .emit (full_mega_url ,True )
|
||||
self.progress_signal.emit(f"Download ({i + 1}/{len(self.tasks)}): Starting '{post_title}' ({platform.upper()}) from {full_mega_url}")
|
||||
|
||||
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 :
|
||||
self .progress_signal .emit (f"❌ Error downloading Mega link '{full_mega_url }' (from post '{post_title }'): {e }")
|
||||
self .file_complete_signal .emit (full_mega_url ,False )
|
||||
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 .finished_signal .emit ()
|
||||
|
||||
def cancel (self ):
|
||||
@@ -2350,7 +2377,7 @@ class DownloaderApp (QWidget ):
|
||||
self .download_thread =None
|
||||
self .thread_pool =None
|
||||
self .cancellation_event =threading .Event ()
|
||||
self .mega_download_thread =None
|
||||
self .external_link_download_thread =None
|
||||
self .pause_event =threading .Event ()
|
||||
self .active_futures =[]
|
||||
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.")
|
||||
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 :
|
||||
|
||||
if link_data_tuple [3 ]=='mega':
|
||||
mega_links_to_show .append ({
|
||||
platform = link_data_tuple[3].lower()
|
||||
if platform in supported_platforms:
|
||||
links_to_show_in_dialog.append ({
|
||||
'title':link_data_tuple [0 ],
|
||||
'link_text':link_data_tuple [1 ],
|
||||
'url':link_data_tuple [2 ],
|
||||
'platform':link_data_tuple [3 ],
|
||||
'platform':platform,
|
||||
'key':link_data_tuple [4 ]
|
||||
})
|
||||
|
||||
if not mega_links_to_show :
|
||||
QMessageBox .information (self ,"No Mega Links","No Mega links were found in the extracted links.")
|
||||
if not links_to_show_in_dialog :
|
||||
QMessageBox .information (self ,"No Supported Links","No Mega, Google Drive, or Dropbox links were found in the extracted links.")
|
||||
return
|
||||
|
||||
dialog =DownloadMegaLinksDialog (mega_links_to_show ,self )
|
||||
dialog .download_requested .connect (self ._handle_mega_links_download_request )
|
||||
dialog = DownloadExtractedLinksDialog(links_to_show_in_dialog, self)
|
||||
dialog .download_requested .connect (self ._handle_extracted_links_download_request )
|
||||
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 :
|
||||
self .log_signal .emit ("ℹ️ No Mega links selected for download from dialog.")
|
||||
self.log_signal.emit("ℹ️ No links selected for download from dialog.")
|
||||
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 \
|
||||
self.only_links_log_display_mode == LOG_DISPLAY_DOWNLOAD_PROGRESS:
|
||||
self.main_log_output.clear()
|
||||
@@ -3470,15 +3499,14 @@ class DownloaderApp (QWidget ):
|
||||
download_dir_for_mega =""
|
||||
|
||||
if current_main_dir and os .path .isdir (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 :
|
||||
|
||||
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 :
|
||||
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 )
|
||||
@@ -3486,53 +3514,48 @@ class DownloaderApp (QWidget ):
|
||||
chosen_dir =QFileDialog .getExistingDirectory (
|
||||
self ,
|
||||
"Select Download Folder for Mega Links",
|
||||
suggestion_path
|
||||
suggestion_path,
|
||||
options=QFileDialog.ShowDirsOnly | QFileDialog.DontUseNativeDialog # Added DontUseNativeDialog for consistency
|
||||
)
|
||||
|
||||
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
|
||||
download_dir_for_mega =chosen_dir
|
||||
|
||||
|
||||
|
||||
self .log_signal .emit (f"ℹ️ Preparing to download {len (selected_links_info )} selected Mega link(s) to: {download_dir_for_mega }")
|
||||
self.log_signal.emit(f"ℹ️ Preparing to download {len(selected_links_info)} selected external link(s) to: {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
|
||||
|
||||
tasks_for_thread =[]
|
||||
for item in selected_links_info :
|
||||
full_url =item ['url']
|
||||
key =item ['key']
|
||||
# selected_links_info already contains dicts with 'url', 'title', 'platform', 'key'
|
||||
tasks_for_thread = selected_links_info
|
||||
|
||||
if key :
|
||||
url_parts =urlparse (full_url )
|
||||
if key not in url_parts .fragment :
|
||||
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 }"
|
||||
if self.external_link_download_thread and self.external_link_download_thread.isRunning():
|
||||
QMessageBox.warning(self, "Busy", "Another external link download is already in progress.")
|
||||
return
|
||||
|
||||
tasks_for_thread .append ({'url':full_url ,'title':item ['title']})
|
||||
|
||||
if self .mega_download_thread and self .mega_download_thread .isRunning ():
|
||||
QMessageBox .warning (self ,"Busy","Another Mega download is already in progress.")
|
||||
return
|
||||
|
||||
self .mega_download_thread =MegaDownloadThread (tasks_for_thread ,download_dir_for_mega ,self .log_signal .emit ,self )
|
||||
self .mega_download_thread .finished .connect (self ._on_mega_download_thread_finished )
|
||||
self.external_link_download_thread = ExternalLinkDownloadThread(
|
||||
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)
|
||||
# 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 .progress_label .setText (f"Downloading Mega Links (0/{len (tasks_for_thread )})...")
|
||||
self .mega_download_thread .start ()
|
||||
self.progress_label.setText(f"Downloading External Links (0/{len(tasks_for_thread)})...")
|
||||
self.external_link_download_thread.start()
|
||||
|
||||
def _on_mega_download_thread_finished (self ):
|
||||
self .log_signal .emit ("✅ Mega download thread finished.")
|
||||
self .progress_label .setText ("Mega downloads complete. Ready for new task.")
|
||||
def _on_external_link_download_thread_finished(self):
|
||||
self.log_signal.emit("✅ External link download thread finished.")
|
||||
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.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.log_signal.emit("INTERNAL: mega_download_log_preserved_once RESET to False.") # Debug
|
||||
|
||||
if self .mega_download_thread :
|
||||
self .mega_download_thread .deleteLater ()
|
||||
self .mega_download_thread =None
|
||||
if self.external_link_download_thread:
|
||||
self.external_link_download_thread.deleteLater()
|
||||
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 ):
|
||||
"""Shows the placeholder dialog for future settings."""
|
||||
dialog =FutureSettingsDialog (self )
|
||||
@@ -5660,7 +5687,7 @@ class DownloaderApp (QWidget ):
|
||||
if self .cancellation_event .is_set ():
|
||||
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 ()
|
||||
|
||||
|
||||
@@ -5750,9 +5777,9 @@ class DownloaderApp (QWidget ):
|
||||
if self .bottom_action_buttons_stack :
|
||||
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 .mega_download_thread .cancel ()
|
||||
self .external_link_download_thread .cancel ()
|
||||
else :
|
||||
pass
|
||||
|
||||
@@ -5938,9 +5965,9 @@ class DownloaderApp (QWidget ):
|
||||
self .log_signal .emit ("⚠️ Requesting cancellation of download process (soft reset)...")
|
||||
|
||||
if self .mega_download_thread and self .mega_download_thread .isRunning ():
|
||||
self .log_signal .emit (" Cancelling active Mega download thread...")
|
||||
self .mega_download_thread .cancel ()
|
||||
|
||||
self .log_signal .emit (" Cancelling active External Link download thread...")
|
||||
self .external_link_download_thread .cancel ()
|
||||
|
||||
current_url =self .link_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 =None
|
||||
|
||||
if self .mega_download_thread and not self .mega_download_thread .isRunning ():
|
||||
self .mega_download_thread .deleteLater ()
|
||||
self .mega_download_thread =None
|
||||
if self.external_link_download_thread and not self.external_link_download_thread.isRunning():
|
||||
self.external_link_download_thread.deleteLater()
|
||||
self.external_link_download_thread = None
|
||||
|
||||
self .active_retry_futures .clear ()
|
||||
self .active_retry_futures_map .clear ()
|
||||
|
||||
Reference in New Issue
Block a user