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 []