Update PR #1452 to latest HydraLauncher and fix conflicts

This commit is contained in:
ItsYeBoi20
2025-10-01 05:04:34 +02:00
parent 79498abdb5
commit aacf9abc6a
26 changed files with 805 additions and 179 deletions

View File

@@ -0,0 +1,151 @@
import aria2p
from aria2p.client import ClientException as DownloadNotFound
class HttpMultiLinkDownloader:
def __init__(self):
self.downloads = []
self.completed_downloads = []
self.total_size = None
self.aria2 = aria2p.API(
aria2p.Client(
host="http://localhost",
port=6800,
secret=""
)
)
def start_download(self, urls: list[str], save_path: str, header: str = None, out: str = None, total_size: int = None):
"""Add multiple URLs to download queue with same options"""
options = {"dir": save_path}
if header:
options["header"] = header
if out:
options["out"] = out
# Clear any existing downloads first
self.cancel_download()
self.completed_downloads = []
self.total_size = total_size
for url in urls:
try:
added_downloads = self.aria2.add(url, options=options)
self.downloads.extend(added_downloads)
except Exception as e:
print(f"Error adding download for URL {url}: {str(e)}")
def pause_download(self):
"""Pause all active downloads"""
if self.downloads:
try:
self.aria2.pause(self.downloads)
except Exception as e:
print(f"Error pausing downloads: {str(e)}")
def cancel_download(self):
"""Cancel and remove all downloads"""
if self.downloads:
try:
# First try to stop the downloads
self.aria2.remove(self.downloads)
except Exception as e:
print(f"Error removing downloads: {str(e)}")
finally:
# Clear the downloads list regardless of success/failure
self.downloads = []
self.completed_downloads = []
def get_download_status(self):
"""Get status for all tracked downloads, auto-remove completed/failed ones"""
if not self.downloads and not self.completed_downloads:
return []
total_completed = 0
current_download_speed = 0
active_downloads = []
to_remove = []
# First calculate sizes from completed downloads
for completed in self.completed_downloads:
total_completed += completed['size']
# Then check active downloads
for download in self.downloads:
try:
current_download = self.aria2.get_download(download.gid)
# Skip downloads that are not properly initialized
if not current_download or not current_download.files:
to_remove.append(download)
continue
# Add to completed size and speed calculations
total_completed += current_download.completed_length
current_download_speed += current_download.download_speed
# If download is complete, move it to completed_downloads
if current_download.status == 'complete':
self.completed_downloads.append({
'name': current_download.name,
'size': current_download.total_length
})
to_remove.append(download)
else:
active_downloads.append({
'name': current_download.name,
'size': current_download.total_length,
'completed': current_download.completed_length,
'speed': current_download.download_speed
})
except DownloadNotFound:
to_remove.append(download)
continue
except Exception as e:
print(f"Error getting download status: {str(e)}")
continue
# Clean up completed/removed downloads from active list
for download in to_remove:
try:
if download in self.downloads:
self.downloads.remove(download)
except ValueError:
pass
# Return aggregate status
if self.total_size or active_downloads or self.completed_downloads:
# Use the first active download's name as the folder name, or completed if none active
folder_name = None
if active_downloads:
folder_name = active_downloads[0]['name']
elif self.completed_downloads:
folder_name = self.completed_downloads[0]['name']
if folder_name and '/' in folder_name:
folder_name = folder_name.split('/')[0]
# Use provided total size if available, otherwise sum from downloads
total_size = self.total_size
if not total_size:
total_size = sum(d['size'] for d in active_downloads) + sum(d['size'] for d in self.completed_downloads)
# Calculate completion status based on total downloaded vs total size
is_complete = len(active_downloads) == 0 and total_completed >= (total_size * 0.99) # Allow 1% margin for size differences
# If all downloads are complete, clear the completed_downloads list to prevent status updates
if is_complete:
self.completed_downloads = []
return [{
'folderName': folder_name,
'fileSize': total_size,
'progress': total_completed / total_size if total_size > 0 else 0,
'downloadSpeed': current_download_speed,
'numPeers': 0,
'numSeeds': 0,
'status': 'complete' if is_complete else 'active',
'bytesDownloaded': total_completed,
}]
return []

View File

@@ -3,6 +3,7 @@ import sys, json, urllib.parse, psutil
from torrent_downloader import TorrentDownloader
from http_downloader import HttpDownloader
from profile_image_processor import ProfileImageProcessor
from http_multi_link_downloader import HttpMultiLinkDownloader
import libtorrent as lt
app = Flask(__name__)
@@ -24,7 +25,15 @@ if start_download_payload:
initial_download = json.loads(urllib.parse.unquote(start_download_payload))
downloading_game_id = initial_download['game_id']
if initial_download['url'].startswith('magnet'):
if isinstance(initial_download['url'], list):
# Handle multiple URLs using HttpMultiLinkDownloader
http_multi_downloader = HttpMultiLinkDownloader()
downloads[initial_download['game_id']] = http_multi_downloader
try:
http_multi_downloader.start_download(initial_download['url'], initial_download['save_path'], initial_download.get('header'), initial_download.get("out"))
except Exception as e:
print("Error starting multi-link download", e)
elif initial_download['url'].startswith('magnet'):
torrent_downloader = TorrentDownloader(torrent_session)
downloads[initial_download['game_id']] = torrent_downloader
try:
@@ -62,12 +71,23 @@ def status():
return auth_error
downloader = downloads.get(downloading_game_id)
if downloader:
status = downloads.get(downloading_game_id).get_download_status()
return jsonify(status), 200
else:
if not downloader:
return jsonify(None)
status = downloader.get_download_status()
if not status:
return jsonify(None)
if isinstance(status, list):
if not status: # Empty list
return jsonify(None)
# For multi-link downloader, use the aggregated status
# The status will already be aggregated by the HttpMultiLinkDownloader
return jsonify(status[0]), 200
return jsonify(status), 200
@app.route("/seed-status", methods=["GET"])
def seed_status():
auth_error = validate_rpc_password()
@@ -81,10 +101,24 @@ def seed_status():
continue
response = downloader.get_download_status()
if response is None:
if not response:
continue
if response.get('status') == 5:
if isinstance(response, list):
# For multi-link downloader, check if all files are complete
if response and all(item['status'] == 'complete' for item in response):
seed_status.append({
'gameId': game_id,
'status': 'complete',
'folderName': response[0]['folderName'],
'fileSize': sum(item['fileSize'] for item in response),
'bytesDownloaded': sum(item['bytesDownloaded'] for item in response),
'downloadSpeed': 0,
'numPeers': 0,
'numSeeds': 0,
'progress': 1.0
})
elif response.get('status') == 5: # Original torrent seeding check
seed_status.append({
'gameId': game_id,
**response,
@@ -143,7 +177,15 @@ def action():
existing_downloader = downloads.get(game_id)
if url.startswith('magnet'):
if isinstance(url, list):
# Handle multiple URLs using HttpMultiLinkDownloader
if existing_downloader and isinstance(existing_downloader, HttpMultiLinkDownloader):
existing_downloader.start_download(url, data['save_path'], data.get('header'), data.get('out'))
else:
http_multi_downloader = HttpMultiLinkDownloader()
downloads[game_id] = http_multi_downloader
http_multi_downloader.start_download(url, data['save_path'], data.get('header'), data.get('out'))
elif url.startswith('magnet'):
if existing_downloader and isinstance(existing_downloader, TorrentDownloader):
existing_downloader.start_download(url, data['save_path'])
else: