Files
Kemono-Downloader/src/services/drive_downloader.py

165 lines
6.4 KiB
Python
Raw Normal View History

2025-07-01 22:48:58 +05:30
# --- Standard Library Imports ---
import os
import re
import traceback
import json
from urllib.parse import urlparse, urlunparse, parse_qs, urlencode
# --- Third-Party Library Imports ---
import requests
try:
from mega import Mega
MEGA_AVAILABLE = True
except ImportError:
MEGA_AVAILABLE = False
try:
import gdown
2025-07-22 07:00:34 -07:00
GDRIVE_AVAILABLE = True
2025-07-01 22:48:58 +05:30
except ImportError:
2025-07-22 07:00:34 -07:00
GDRIVE_AVAILABLE = False
2025-07-01 22:48:58 +05:30
# --- Helper Functions ---
def _get_filename_from_headers(headers):
"""
Extracts a filename from the Content-Disposition header.
Args:
headers (dict): A dictionary of HTTP response headers.
Returns:
str or None: The extracted filename, or None if not found.
"""
cd = headers.get('content-disposition')
if not cd:
return None
fname_match = re.findall('filename="?([^"]+)"?', cd)
if fname_match:
# Sanitize the filename to prevent directory traversal issues
# and remove invalid characters for most filesystems.
sanitized_name = re.sub(r'[<>:"/\\|?*]', '_', fname_match[0].strip())
return sanitized_name
return None
# --- Main Service Downloader Functions ---
2025-07-22 07:00:34 -07:00
def download_mega_file(mega_url, download_path, logger_func=print):
2025-07-01 22:48:58 +05:30
"""
2025-07-22 07:00:34 -07:00
Downloads a file from a Mega.nz URL.
Handles both public links and links that include a decryption key.
2025-07-01 22:48:58 +05:30
"""
if not MEGA_AVAILABLE:
2025-07-22 07:00:34 -07:00
logger_func("❌ Mega download failed: 'mega.py' library is not installed.")
return
2025-07-01 22:48:58 +05:30
logger_func(f" [Mega] Initializing Mega client...")
try:
2025-07-22 07:00:34 -07:00
mega = Mega()
# Anonymous login is sufficient for public links
m = mega.login()
# --- MODIFIED PART: Added error handling for invalid links ---
try:
file_details = m.find(mega_url)
if file_details is None:
logger_func(f" [Mega] ❌ Download failed. The link appears to be invalid or has been taken down: {mega_url}")
return
except (ValueError, json.JSONDecodeError) as e:
# This block catches the "Expecting value" error
logger_func(f" [Mega] ❌ Download failed. The link is likely invalid or expired. Error: {e}")
return
except Exception as e:
# Catch other potential errors from the mega.py library
logger_func(f" [Mega] ❌ An unexpected error occurred trying to access the link: {e}")
return
# --- END OF MODIFIED PART ---
filename = file_details[1]['a']['n']
logger_func(f" [Mega] File found: '{filename}'. Starting download...")
# Sanitize filename before saving
safe_filename = "".join([c for c in filename if c.isalpha() or c.isdigit() or c in (' ', '.', '_', '-')]).rstrip()
final_path = os.path.join(download_path, safe_filename)
# Check if file already exists
if os.path.exists(final_path):
logger_func(f" [Mega] File '{safe_filename}' already exists. Skipping download.")
return
# Start the download
m.download_url(mega_url, dest_path=download_path, dest_filename=safe_filename)
logger_func(f" [Mega] ✅ Successfully downloaded '{safe_filename}' to '{download_path}'")
2025-07-01 22:48:58 +05:30
except Exception as e:
2025-07-22 07:00:34 -07:00
logger_func(f" [Mega] ❌ An unexpected error occurred during the Mega download process: {e}")
2025-07-01 22:48:58 +05:30
2025-07-22 07:00:34 -07:00
def download_gdrive_file(url, download_path, logger_func=print):
"""Downloads a file from a Google Drive link."""
if not GDRIVE_AVAILABLE:
logger_func("❌ Google Drive download failed: 'gdown' library is not installed.")
return
2025-07-01 22:48:58 +05:30
try:
2025-07-22 07:00:34 -07:00
logger_func(f" [G-Drive] Starting download for: {url}")
# --- MODIFIED PART: Added a message and set quiet=True ---
logger_func(" [G-Drive] Download in progress... This may take some time. Please wait.")
# By setting quiet=True, the progress bar will no longer be printed to the terminal.
output_path = gdown.download(url, output=download_path, quiet=True, fuzzy=True)
# --- END OF MODIFIED PART ---
if output_path and os.path.exists(output_path):
logger_func(f" [G-Drive] ✅ Successfully downloaded to '{output_path}'")
2025-07-01 22:48:58 +05:30
else:
2025-07-22 07:00:34 -07:00
logger_func(f" [G-Drive] ❌ Download failed. The file may have been moved, deleted, or is otherwise inaccessible.")
2025-07-01 22:48:58 +05:30
except Exception as e:
2025-07-22 07:00:34 -07:00
logger_func(f" [G-Drive] ❌ An unexpected error occurred: {e}")
2025-07-01 22:48:58 +05:30
def download_dropbox_file(dropbox_link, download_path=".", logger_func=print):
"""
Downloads a file from a public Dropbox link by modifying the URL for direct download.
Args:
dropbox_link (str): The public Dropbox link to the file.
download_path (str): The directory to save the downloaded file.
logger_func (callable): Function to use for logging.
"""
logger_func(f" [Dropbox] Attempting to download: {dropbox_link}")
# Modify the Dropbox URL to force a direct download instead of showing the preview page.
parsed_url = urlparse(dropbox_link)
query_params = parse_qs(parsed_url.query)
query_params['dl'] = ['1']
new_query = urlencode(query_params, doseq=True)
direct_download_url = urlunparse(parsed_url._replace(query=new_query))
logger_func(f" [Dropbox] Using direct download URL: {direct_download_url}")
try:
if not os.path.exists(download_path):
os.makedirs(download_path, exist_ok=True)
logger_func(f" [Dropbox] Created download directory: {download_path}")
with requests.get(direct_download_url, stream=True, allow_redirects=True, timeout=(10, 300)) as r:
r.raise_for_status()
# Determine filename from headers or URL
filename = _get_filename_from_headers(r.headers) or os.path.basename(parsed_url.path) or "dropbox_file"
full_save_path = os.path.join(download_path, filename)
logger_func(f" [Dropbox] Starting download of '{filename}'...")
# Write file to disk in chunks
with open(full_save_path, 'wb') as f:
for chunk in r.iter_content(chunk_size=8192):
f.write(chunk)
logger_func(f" [Dropbox] ✅ Dropbox file downloaded successfully: {full_save_path}")
except Exception as e:
logger_func(f" [Dropbox] ❌ An error occurred during Dropbox download: {e}")
traceback.print_exc(limit=2)
raise