From 7bf2b2373830b97bfa36df3f866d1bfef00dbcae Mon Sep 17 00:00:00 2001 From: Xi Xu Date: Tue, 8 Jul 2025 18:03:18 +0800 Subject: [PATCH] Initial commit --- .gitignore | 78 ++++++++++ LICENSE | 21 +++ README.md | 194 +++++++++++++++++++++++ download_isos.py | 222 +++++++++++++++++++++++++++ examples/README.md | 101 ++++++++++++ examples/create_custom_categories.py | 115 ++++++++++++++ examples/download_specific.py | 42 +++++ requirements.txt | 2 + setup.py | 153 ++++++++++++++++++ urls.txt | 17 ++ 10 files changed, 945 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 download_isos.py create mode 100644 examples/README.md create mode 100644 examples/create_custom_categories.py create mode 100644 examples/download_specific.py create mode 100644 requirements.txt create mode 100644 setup.py create mode 100644 urls.txt diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8787bf0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,78 @@ +# Downloaded ISO files +isos/ +*.iso + +# Python +__pycache__/ +*.py[cod] +*$py.class +*.so +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# IDE +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# OS +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db + +# Logs +*.log diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..99f5cea --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Xi Xu + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..e182d55 --- /dev/null +++ b/README.md @@ -0,0 +1,194 @@ +# Linux ISO Downloader + +A Python utility for downloading Linux distribution ISO files in parallel with resume capability, progress tracking, and robust error handling. + +## šŸš€ Quick Start + +```bash +# Clone the repository +git clone https://github.com/your-username/linux-iso.git +cd linux-iso + +# Run the setup script (recommended for first-time users) +python setup.py + +# Or install dependencies manually +pip install -r requirements.txt + +# Start downloading all Linux ISOs +python download_isos.py +``` + +## ⭐ Features + +- **⚔ Parallel Downloads**: Download multiple ISO files simultaneously with configurable worker threads +- **šŸ”„ Resume Support**: Automatically resume interrupted downloads +- **šŸ“Š Progress Tracking**: Real-time progress bars for each download +- **šŸ” Retry Logic**: Configurable retry attempts with exponential backoff +- **šŸ›”ļø Error Handling**: Robust error handling with detailed failure reporting +- **āš™ļø Flexible Configuration**: Command-line options for all parameters + +## 🐧 Supported Distributions + +The included `urls.txt` contains direct links to the latest stable releases of popular Linux distributions: + +| Distribution | Architecture | Version | +|--------------|-------------|---------| +| AlmaLinux | x86_64 | 10.0 | +| Arch Linux | x86_64 | Latest | +| CachyOS | x86_64 | Latest | +| CentOS | x86_64 | 7.9/8.5 | +| Debian | amd64 | 12.11.0 | +| EndeavourOS | x86_64 | Latest | +| Fedora | x86_64 | 42 | +| Kali Linux | amd64 | 2025.2 | +| Linux Mint | x86_64 | 22.1 | +| Manjaro | x86_64 | 25.0.4 | +| openEuler | x86_64 | 24.03-LTS-SP2 | +| Pop!_OS | amd64 | 22.04 | +| openSUSE | x86_64 | Tumbleweed | +| Oracle Linux | x86_64 | 10.0 | +| Rocky Linux | x86_64 | 10.0 | +| Ubuntu | amd64 | 24.04.2 | + +## šŸ“¦ Installation + +### Prerequisites + +- Python 3.6 or higher +- pip package manager + +### Install Dependencies + +```bash +pip install -r requirements.txt +``` + +## Usage + +### Basic Usage + +Download all ISOs from the default `urls.txt` file: + +```bash +python download_isos.py +``` + +### Advanced Usage + +```bash +python download_isos.py --urls-file custom_urls.txt --output-dir /path/to/isos --max-workers 10 --retries 3 +``` + +### Command Line Options + +- `--urls-file`: File containing URLs to download (default: `urls.txt`) +- `--output-dir`: Directory to save downloaded files (default: `./isos`) +- `--max-workers`: Maximum number of parallel downloads (default: 5) +- `--retries`: Number of retry attempts per file (default: 5) +- `--chunk-size`: Chunk size for downloading in bytes (default: 8192) + +### Help + +```bash +python download_isos.py --help +``` + +## URL File Format + +The URL file should contain one URL per line. Empty lines are ignored. Example: + +```text +https://releases.ubuntu.com/24.04/ubuntu-24.04.2-desktop-amd64.iso +https://download.fedoraproject.org/pub/fedora/linux/releases/42/Workstation/x86_64/iso/Fedora-Workstation-Live-42-1.1.x86_64.iso +``` + +## Examples + +### Download with Custom Settings + +```bash +# Use 8 parallel workers and save to custom directory +python download_isos.py --max-workers 8 --output-dir ~/Downloads/linux-isos + +# Use custom URL file with different retry settings +python download_isos.py --urls-file my_distros.txt --retries 10 +``` + +### Download Single Distribution + +Create a custom URL file with just one distribution: + +```bash +echo "https://releases.ubuntu.com/24.04/ubuntu-24.04.2-desktop-amd64.iso" > ubuntu.txt +python download_isos.py --urls-file ubuntu.txt +``` + +## šŸ“š Examples + +The `examples/` directory contains sample scripts demonstrating various usage patterns: + +- **`download_specific.py`** - Download only specific distributions (Ubuntu + Fedora) +- **`create_custom_categories.py`** - Create and use custom distribution categories + +See the [examples README](examples/README.md) for detailed usage instructions. + +## Output + +The script provides real-time progress information: + +- Individual progress bars for each file +- Download speed and ETA +- Success/failure summary +- Detailed error reporting for failed downloads + +## Error Handling + +- **Network Issues**: Automatic retry with exponential backoff +- **Interrupted Downloads**: Resume from where it left off +- **Invalid URLs**: Detailed error messages +- **Disk Space**: Handles low disk space gracefully +- **Permissions**: Clear error messages for permission issues + +## Performance Tips + +1. **Optimal Worker Count**: Start with 5 workers and adjust based on your network and system performance +2. **Chunk Size**: Default 8KB works well for most cases; increase for faster networks +3. **Retry Attempts**: Increase for unreliable connections +4. **Resume Downloads**: The script automatically resumes interrupted downloads + +## Troubleshooting + +### Common Issues + +**Downloads are slow:** + +- Reduce `--max-workers` if you have limited bandwidth +- Check your internet connection +- Some mirrors may be slower than others + +**Downloads fail repeatedly:** + +- Increase `--retries` for unreliable connections +- Check if the URLs are still valid +- Verify you have write permissions to the output directory + +**Out of disk space:** + +- Check available disk space before starting large downloads +- Use `--output-dir` to specify a location with more space + +## šŸ† Acknowledgments + +- Thanks to all the Linux distribution maintainers for providing direct download links +- Built with Python's `requests` library for HTTP handling +- Uses `tqdm` for beautiful progress bars +- Implements concurrent downloads with `concurrent.futures` + +## šŸ“„ License + +This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. + +## āš ļø Disclaimer + +This tool downloads files from third-party servers. Please respect the bandwidth and terms of service of the mirror providers. Consider using official torrents or mirrors when available. diff --git a/download_isos.py b/download_isos.py new file mode 100644 index 0000000..027d50c --- /dev/null +++ b/download_isos.py @@ -0,0 +1,222 @@ +#!/usr/bin/env python3 + +import argparse +import concurrent.futures +import os +import sys +import time +from pathlib import Path +from urllib.parse import urlparse + +import requests +from tqdm import tqdm + + +class ISODownloader: + def __init__(self, output_dir, max_workers=5, retry_attempts=3, chunk_size=8192): + self.output_dir = Path(output_dir) + self.max_workers = max_workers + self.retry_attempts = retry_attempts + self.chunk_size = chunk_size + self.session = requests.Session() + # Configure session with longer timeouts + self.session.mount( + "https://", + requests.adapters.HTTPAdapter( + max_retries=3, pool_connections=max_workers, pool_maxsize=max_workers + ), + ) + self.session.mount( + "http://", + requests.adapters.HTTPAdapter( + max_retries=3, pool_connections=max_workers, pool_maxsize=max_workers + ), + ) + + def download_file(self, url): + """Download a single file with retry capability and progress tracking""" + file_name = os.path.basename(urlparse(url).path) + file_path = self.output_dir / file_name + + # Create output directory if it doesn't exist + self.output_dir.mkdir(parents=True, exist_ok=True) + + # Check if file already exists and get its size + file_size = file_path.stat().st_size if file_path.exists() else 0 + + for attempt in range(1, self.retry_attempts + 1): + try: + # Setup the request with resume capability if file exists + headers = {} + if file_size > 0: + headers["Range"] = f"bytes={file_size}-" + + # Make the request with a timeout + response = self.session.get( + url, headers=headers, stream=True, timeout=30, allow_redirects=True + ) + + if ( + response.status_code == 416 + ): # Range not satisfiable, file may be complete + print(f"File {file_name} appears to be complete, skipping.") + return True + + # Handle non-successful status codes + response.raise_for_status() + + # Get total file size + total_size = int(response.headers.get("content-length", 0)) + if file_size > 0 and response.status_code == 206: # Partial content + total_size += file_size + else: + # If not a resume, start from scratch + file_size = 0 + + # Setup progress bar + desc = f"[Attempt {attempt}] {file_name}" + with tqdm( + total=total_size, + initial=file_size, + unit="B", + unit_scale=True, + desc=desc, + ncols=100, + ) as pbar: + # Write to file + mode = "ab" if file_size > 0 else "wb" + with open(file_path, mode) as f: + for chunk in response.iter_content(chunk_size=self.chunk_size): + if chunk: # filter out keep-alive chunks + f.write(chunk) + pbar.update(len(chunk)) + + return True + + except (OSError, requests.exceptions.RequestException) as e: + if attempt < self.retry_attempts: + wait_time = 2**attempt # Exponential backoff + print(f"Error downloading {file_name}: {e}") + print( + f"Retrying in {wait_time} seconds... (Attempt {attempt}/{self.retry_attempts})" + ) + time.sleep(wait_time) + else: + print( + f"Failed to download {file_name} after {self.retry_attempts} attempts: {e}" + ) + return False + + def download_all(self, urls): + """Download all files in parallel""" + results = [] + successful = 0 + failed = 0 + + print(f"Starting downloads with {self.max_workers} parallel workers") + + with concurrent.futures.ThreadPoolExecutor( + max_workers=self.max_workers + ) as executor: + # Submit all download tasks + future_to_url = { + executor.submit(self.download_file, url): url for url in urls + } + + # Process results as they complete + for future in concurrent.futures.as_completed(future_to_url): + url = future_to_url[future] + file_name = os.path.basename(urlparse(url).path) + + try: + if future.result(): + successful += 1 + else: + failed += 1 + results.append(f"Failed: {file_name}") + except Exception as e: + failed += 1 + results.append(f"Error with {file_name}: {str(e)}") + + print("\nDownload Summary:") + print(f" Successful: {successful}") + print(f" Failed: {failed}") + + if failed > 0: + print("\nFailed Downloads:") + for result in results: + print(f" - {result}") + + return successful, failed, results + + +def main(): + parser = argparse.ArgumentParser(description="Download ISO files in parallel.") + parser.add_argument( + "--urls-file", + default="urls.txt", + help="File containing URLs to download (default: urls.txt)", + ) + parser.add_argument( + "--output-dir", + default="./isos", + help="Directory to save downloaded files (default: ./isos)", + ) + parser.add_argument( + "--max-workers", + type=int, + default=5, + help="Maximum number of parallel downloads (default: 5)", + ) + parser.add_argument( + "--retries", + type=int, + default=5, + help="Number of retry attempts per file (default: 5)", + ) + parser.add_argument( + "--chunk-size", + type=int, + default=8192, + help="Chunk size for downloading in bytes (default: 8192)", + ) + + args = parser.parse_args() + + # Read URLs from file + try: + with open(args.urls_file) as f: + urls = [line.strip() for line in f if line.strip()] + except OSError as e: + print(f"Error reading URLs file: {e}") + sys.exit(1) + + if not urls: + print("No URLs found in the file.") + sys.exit(1) + + print(f"Found {len(urls)} URLs to download.") + + # Initialize downloader + downloader = ISODownloader( + args.output_dir, + max_workers=args.max_workers, + retry_attempts=args.retries, + chunk_size=args.chunk_size, + ) + + # Start the download + start_time = time.time() + successful, failed, results = downloader.download_all(urls) + elapsed_time = time.time() - start_time + + # Print summary + print(f"\nDownload process completed in {elapsed_time:.2f} seconds.") + print(f"Files saved to: {os.path.abspath(args.output_dir)}") + + if failed > 0: + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..72c12cf --- /dev/null +++ b/examples/README.md @@ -0,0 +1,101 @@ +# Examples + +This directory contains example scripts demonstrating various ways to use the Linux ISO downloader. + +## Available Examples + +### 1. `download_specific.py` + +Download only specific distributions (Ubuntu and Fedora in this example). + +```bash +python examples/download_specific.py +``` + +**Features:** + +- Downloads only Ubuntu and Fedora ISOs +- Uses fewer parallel workers (2) for demonstration +- Saves files to `./examples_output/` + +### 2. `create_custom_categories.py` + +Demonstrates how to create custom URL files for different categories of Linux distributions. + +```bash +# Create category files and download lightweight distributions +python examples/create_custom_categories.py + +# Download enterprise distributions +python examples/create_custom_categories.py --enterprise + +# Show help +python examples/create_custom_categories.py --help +``` + +**Features:** + +- Creates custom URL files for different distribution categories +- Lightweight distributions (Arch-based): Arch Linux, EndeavourOS, Manjaro +- Enterprise distributions (RHEL-based): AlmaLinux, Rocky Linux, Oracle Linux +- Organizes downloads into category-specific subdirectories + +## Output Structure + +When running examples, files are organized as follows: + +```text +examples_output/ +ā”œā”€ā”€ lightweight/ # From create_custom_categories.py +│ ā”œā”€ā”€ archlinux-x86_64.iso +│ ā”œā”€ā”€ EndeavourOS-Mercury-Neo-2025.03.19.iso +│ └── manjaro-gnome-25.0.4-250623-linux612.iso +ā”œā”€ā”€ enterprise/ # From create_custom_categories.py --enterprise +│ ā”œā”€ā”€ AlmaLinux-10.0-x86_64-dvd.iso +│ ā”œā”€ā”€ Rocky-10.0-x86_64-dvd1.iso +│ └── OracleLinux-R10-U0-x86_64-dvd.iso +└── ubuntu-24.04.2-desktop-amd64.iso # From download_specific.py +└── Fedora-Workstation-Live-42-1.1.x86_64.iso +``` + +## Custom URL Files + +The `create_custom_categories.py` script generates these URL files: + +- `examples/lightweight_distros.txt` - Arch-based distributions +- `examples/enterprise_distros.txt` - Enterprise Linux distributions + +You can modify these files or create your own custom URL files to download specific sets of distributions. + +## Tips + +1. **Start Small**: Begin with the `download_specific.py` example to understand the basic usage +2. **Custom Categories**: Use `create_custom_categories.py` as a template for your own categorization needs +3. **Modify URLs**: Edit the generated URL files in the `examples/` directory to customize which distributions to download +4. **Output Directories**: Each example uses different output directories to keep downloads organized + +## Creating Your Own Examples + +Use these examples as templates to create your own custom download scripts: + +1. Import the `ISODownloader` class from `download_isos` +2. Define your list of URLs +3. Create a downloader instance with your preferred settings +4. Call `download_all()` with your URLs + +```python +from download_isos import ISODownloader + +# Your custom URLs +urls = ["https://example.com/distro.iso"] + +# Create downloader +downloader = ISODownloader( + output_dir="./my_downloads", + max_workers=3, + retry_attempts=5 +) + +# Download +successful, failed, results = downloader.download_all(urls) +``` diff --git a/examples/create_custom_categories.py b/examples/create_custom_categories.py new file mode 100644 index 0000000..77286c9 --- /dev/null +++ b/examples/create_custom_categories.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python3 +""" +Example script demonstrating how to create custom URL files and download +specific sets of distributions. +This example shows how to create different categories of Linux distributions. +""" + +import os + +from download_isos import ISODownloader + + +def create_lightweight_distros(): + """Create a URL file for lightweight Linux distributions.""" + lightweight_urls = [ + "https://geo.mirror.pkgbuild.com/iso/2025.07.01/archlinux-x86_64.iso", + "https://mirrors.gigenet.com/endeavouros/iso/EndeavourOS_Mercury-Neo-2025.03.19.iso", + "https://download.manjaro.org/gnome/25.0.4/manjaro-gnome-25.0.4-250623-linux612.iso", + ] + + with open("examples/lightweight_distros.txt", "w") as f: + for url in lightweight_urls: + f.write(url + "\n") + + print("āœ… Created lightweight_distros.txt") + + +def create_enterprise_distros(): + """Create a URL file for enterprise Linux distributions.""" + enterprise_urls = [ + "https://repo.almalinux.org/almalinux/10/isos/x86_64/AlmaLinux-10.0-x86_64-dvd.iso", + "https://download.rockylinux.org/pub/rocky/10/isos/x86_64/Rocky-10.0-x86_64-dvd1.iso", + "https://yum.oracle.com/ISOS/OracleLinux/OL10/u0/x86_64/OracleLinux-R10-U0-x86_64-dvd.iso", + ] + + with open("examples/enterprise_distros.txt", "w") as f: + for url in enterprise_urls: + f.write(url + "\n") + + print("āœ… Created enterprise_distros.txt") + + +def download_category(url_file, category_name): + """Download ISOs from a specific category URL file.""" + print(f"\n🐧 Downloading {category_name} distributions...") + + # Read URLs from the category file + try: + with open(url_file) as f: + urls = [line.strip() for line in f if line.strip()] + except FileNotFoundError: + print(f"āŒ Error: {url_file} not found!") + return + + if not urls: + print(f"āŒ No URLs found in {url_file}") + return + + print(f"šŸ“‹ Found {len(urls)} URLs to download") + + # Create output directory based on category + output_dir = f"./examples_output/{category_name.lower().replace(' ', '_')}" + + # Create downloader instance + downloader = ISODownloader(output_dir=output_dir, max_workers=3, retry_attempts=3) + + # Download the files + successful, failed, results = downloader.download_all(urls) + + print(f"\nāœ… Successfully downloaded: {successful} files") + if failed > 0: + print(f"āŒ Failed downloads: {failed}") + for result in results: + print(f" - {result}") + + print(f"\nšŸ“‚ Files saved to: {os.path.abspath(output_dir)}") + + +def main(): + """Main function to demonstrate custom URL file creation and usage.""" + print("šŸ”§ Creating custom URL files for different distribution categories...") + + # Create the examples directory if it doesn't exist + os.makedirs("examples", exist_ok=True) + + # Create category-specific URL files + create_lightweight_distros() + create_enterprise_distros() + + print("\nšŸ“‹ Available categories:") + print(" 1. Lightweight distributions (Arch-based)") + print(" 2. Enterprise distributions (RHEL-based)") + + # Example: Download lightweight distributions + print("\n" + "=" * 50) + download_category("examples/lightweight_distros.txt", "Lightweight") + + print("\n" + "=" * 50) + print("šŸ’” To download enterprise distributions, run:") + print(" python examples/create_custom_categories.py --enterprise") + + +if __name__ == "__main__": + import sys + + if "--enterprise" in sys.argv: + download_category("examples/enterprise_distros.txt", "Enterprise") + elif "--help" in sys.argv or "-h" in sys.argv: + print("Usage:") + print(" python examples/create_custom_categories.py") + print(" # Create files and download lightweight") + print(" python examples/create_custom_categories.py --enterprise") + print(" # Download enterprise distributions") + else: + main() diff --git a/examples/download_specific.py b/examples/download_specific.py new file mode 100644 index 0000000..c51d35e --- /dev/null +++ b/examples/download_specific.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 +""" +Example script demonstrating how to use the ISO downloader for specific distributions. +This script shows how to download only Ubuntu and Fedora ISOs. +""" + +import os + +from download_isos import ISODownloader + + +def main(): + # Define specific URLs for Ubuntu and Fedora + ubuntu_fedora_urls = [ + "https://releases.ubuntu.com/noble/ubuntu-24.04.2-desktop-amd64.iso", + "https://download.fedoraproject.org/pub/fedora/linux/releases/42/Workstation/x86_64/iso/Fedora-Workstation-Live-42-1.1.x86_64.iso", + ] + + print("🐧 Downloading Ubuntu and Fedora ISOs...") + print("šŸ“ Output directory: ./examples_output") + + # Create downloader instance + downloader = ISODownloader( + output_dir="./examples_output", + max_workers=2, # Use fewer workers for this example + retry_attempts=3, + ) + + # Download the files + successful, failed, results = downloader.download_all(ubuntu_fedora_urls) + + print(f"\nāœ… Successfully downloaded: {successful} files") + if failed > 0: + print(f"āŒ Failed downloads: {failed}") + for result in results: + print(f" - {result}") + + print(f"\nšŸ“‚ Files saved to: {os.path.abspath('./examples_output')}") + + +if __name__ == "__main__": + main() diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..aebd384 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +requests>=2.31.0 +tqdm>=4.66.0 diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..667d55f --- /dev/null +++ b/setup.py @@ -0,0 +1,153 @@ +#!/usr/bin/env python3 +""" +Setup script to help users get started with the Linux ISO downloader. +This script checks dependencies and provides initial setup guidance. +""" + +import subprocess +import sys +from pathlib import Path + + +def check_python_version(): + """Check if Python version is compatible.""" + version = sys.version_info + if version.major < 3 or (version.major == 3 and version.minor < 6): + print("āŒ Python 3.6 or higher is required.") + print(f" Current version: Python {version.major}.{version.minor}") + return False + + print(f"āœ… Python {version.major}.{version.minor}.{version.micro} - Compatible") + return True + + +def check_dependencies(): + """Check if required dependencies are installed.""" + try: + import requests + + print(f"āœ… requests {requests.__version__} - Installed") + except ImportError: + print("āŒ requests library not found") + return False + + try: + import tqdm + + print(f"āœ… tqdm {tqdm.__version__} - Installed") + except ImportError: + print("āŒ tqdm library not found") + return False + + return True + + +def install_dependencies(): + """Install required dependencies.""" + print("\nšŸ”§ Installing dependencies...") + try: + subprocess.check_call( + [sys.executable, "-m", "pip", "install", "-r", "requirements.txt"] + ) + print("āœ… Dependencies installed successfully!") + return True + except subprocess.CalledProcessError: + print("āŒ Failed to install dependencies") + return False + + +def create_output_directory(): + """Create the default output directory.""" + output_dir = Path("./isos") + if not output_dir.exists(): + output_dir.mkdir(parents=True, exist_ok=True) + print(f"āœ… Created output directory: {output_dir.absolute()}") + else: + print(f"āœ… Output directory already exists: {output_dir.absolute()}") + + +def check_urls_file(): + """Check if the URLs file exists.""" + urls_file = Path("urls.txt") + if urls_file.exists(): + with open(urls_file) as f: + urls = [line.strip() for line in f if line.strip()] + print(f"āœ… Found {len(urls)} URLs in urls.txt") + return True + else: + print("āŒ urls.txt file not found") + return False + + +def print_usage_examples(): + """Print usage examples.""" + print("\n" + "=" * 60) + print("šŸš€ QUICK START EXAMPLES") + print("=" * 60) + + print("\n1. Download all Linux ISOs:") + print(" python download_isos.py") + + print("\n2. Download with custom settings:") + print(" python download_isos.py --max-workers 3 --retries 5") + + print("\n3. Download to custom directory:") + print(" python download_isos.py --output-dir /path/to/downloads") + + print("\n4. Run example scripts:") + print(" python examples/download_specific.py") + print(" python examples/create_custom_categories.py") + + print("\n5. Get help:") + print(" python download_isos.py --help") + + print("\n" + "=" * 60) + print("šŸ“‹ WHAT'S INCLUDED") + print("=" * 60) + print("• 17 Linux distributions in urls.txt") + print("• Parallel downloading with progress bars") + print("• Automatic resume for interrupted downloads") + print("• Example scripts in the examples/ directory") + print("• Comprehensive error handling and retry logic") + + +def main(): + """Main setup function.""" + print("🐧 Linux ISO Downloader Setup") + print("=" * 40) + + # Check Python version + if not check_python_version(): + sys.exit(1) + + # Check if dependencies are already installed + if not check_dependencies(): + print("\nšŸ“¦ Installing missing dependencies...") + if not install_dependencies(): + print("\nāŒ Setup failed. Please install dependencies manually:") + print(" pip install -r requirements.txt") + sys.exit(1) + + # Verify installation + print("\nšŸ” Verifying installation...") + if not check_dependencies(): + print("āŒ Dependency verification failed") + sys.exit(1) + + # Create output directory + print("\nšŸ“ Setting up directories...") + create_output_directory() + + # Check URLs file + print("\nšŸ”— Checking URL configuration...") + check_urls_file() + + # Print usage examples + print_usage_examples() + + print("\nāœ… Setup complete! You're ready to download Linux ISOs.") + print("\nšŸ’” Tip: Start with 'python download_isos.py --help' to see all options.") + + +if __name__ == "__main__": + main() diff --git a/urls.txt b/urls.txt new file mode 100644 index 0000000..a366153 --- /dev/null +++ b/urls.txt @@ -0,0 +1,17 @@ +https://repo.almalinux.org/almalinux/10/isos/x86_64/AlmaLinux-10.0-x86_64-dvd.iso +https://geo.mirror.pkgbuild.com/iso/2025.07.01/archlinux-x86_64.iso +https://cdn77.cachyos.org/ISO/desktop/250530/cachyos-desktop-linux-250530.iso +https://vault.centos.org/7.9.2009/isos/x86_64/CentOS-7-x86_64-Everything-2207-02.iso +https://vault.centos.org/8.5.2111/isos/x86_64/CentOS-8.5.2111-x86_64-dvd1.iso +https://cdimage.debian.org/debian-cd/current/amd64/iso-dvd/debian-12.11.0-amd64-DVD-1.iso +https://mirrors.gigenet.com/endeavouros/iso/EndeavourOS_Mercury-Neo-2025.03.19.iso +https://download.fedoraproject.org/pub/fedora/linux/releases/42/Workstation/x86_64/iso/Fedora-Workstation-Live-42-1.1.x86_64.iso +https://cdimage.kali.org/kali-2025.2/kali-linux-2025.2-installer-amd64.iso +https://mirrors.cicku.me/linuxmint/iso/stable/22.1/linuxmint-22.1-cinnamon-64bit.iso +https://download.manjaro.org/gnome/25.0.4/manjaro-gnome-25.0.4-250623-linux612.iso +https://mirror.nyist.edu.cn/openeuler/openEuler-24.03-LTS-SP2/DevStation/x86_64/openEuler-24.03-LTS-SP2-DevStation-x86_64-dvd.iso +https://iso.pop-os.org/22.04/amd64/nvidia/55/pop-os_22.04_amd64_nvidia_55.iso +https://download.opensuse.org/tumbleweed/iso/openSUSE-Tumbleweed-DVD-x86_64-Current.iso +https://yum.oracle.com/ISOS/OracleLinux/OL10/u0/x86_64/OracleLinux-R10-U0-x86_64-dvd.iso +https://download.rockylinux.org/pub/rocky/10/isos/x86_64/Rocky-10.0-x86_64-dvd1.iso +https://releases.ubuntu.com/noble/ubuntu-24.04.2-desktop-amd64.iso