|
| 1 | +#! /usr/bin/env python3 |
| 2 | + |
| 3 | +import urllib.request |
| 4 | +import urllib.parse |
| 5 | +import re |
| 6 | +import os |
| 7 | +import subprocess |
| 8 | +import ssl |
| 9 | +import sys |
| 10 | +import argparse |
| 11 | +import tempfile |
| 12 | +import textwrap |
| 13 | + |
| 14 | +try: |
| 15 | + from tqdm import tqdm |
| 16 | + TQDM_AVAILABLE = True |
| 17 | +except ImportError: |
| 18 | + TQDM_AVAILABLE = False |
| 19 | + |
| 20 | +class DownloadProgressBar: |
| 21 | + def __init__(self, total=0, unit='iB', unit_scale=True, unit_divisor=1024): |
| 22 | + self.pbar = None |
| 23 | + self.total = total |
| 24 | + self.unit = unit |
| 25 | + self.unit_scale = unit_scale |
| 26 | + self.unit_divisor = unit_divisor |
| 27 | + |
| 28 | + def __call__(self, downloaded, block_size, total_size): |
| 29 | + if not self.pbar: |
| 30 | + self.total = total_size |
| 31 | + self.pbar = tqdm(total=total_size, unit=self.unit, unit_scale=self.unit_scale, unit_divisor=self.unit_divisor) |
| 32 | + |
| 33 | + self.pbar.update(downloaded - self.pbar.n) |
| 34 | + |
| 35 | + if downloaded >= total_size: |
| 36 | + self.pbar.close() |
| 37 | + |
| 38 | +def simple_progress_bar(downloaded, block_size, total_size): |
| 39 | + progress = min(round(downloaded / total_size * 100, 2), 100) |
| 40 | + sys.stdout.write(f"\rDownload progress: {progress:6.2f}%") |
| 41 | + sys.stdout.flush() |
| 42 | + |
| 43 | +def create_ssl_context(): |
| 44 | + cert_chain = textwrap.dedent(""" |
| 45 | + -----BEGIN CERTIFICATE----- |
| 46 | + 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 |
| 74 | + bP6MvPJwNQzcmRk13NfIRmPVNnGuV/u3gm3c |
| 75 | + -----END CERTIFICATE----- |
| 76 | + """).strip() |
| 77 | + |
| 78 | + context = ssl.create_default_context(cadata=cert_chain) |
| 79 | + |
| 80 | + return context |
| 81 | + |
| 82 | +def download_large_file(file_id, output_filename, ssl_context): |
| 83 | + url = f"https://drive.google.com/uc?export=download&id={file_id}" |
| 84 | + req = urllib.request.Request(url, method="GET") |
| 85 | + |
| 86 | + try: |
| 87 | + with urllib.request.urlopen(req, context=ssl_context) as response: |
| 88 | + response_content = response.read().decode('utf-8') |
| 89 | + except urllib.error.URLError as e: |
| 90 | + print(f"Error accessing Google Drive: {e}") |
| 91 | + return False |
| 92 | + |
| 93 | + form_action = re.search(r'<form id="download-form" action="([^"]*)"', response_content) |
| 94 | + confirm = re.search(r'name="confirm" value="([^"]*)"', response_content) |
| 95 | + uuid_value = re.search(r'name="uuid" value="([^"]*)"', response_content) |
| 96 | + |
| 97 | + if not all([form_action, confirm, uuid_value]): |
| 98 | + print("Failed to extract form details. The download process might have changed.") |
| 99 | + return False |
| 100 | + |
| 101 | + download_url = (f"{form_action.group(1)}?" |
| 102 | + f"id={file_id}&" |
| 103 | + f"export=download&" |
| 104 | + f"confirm={confirm.group(1)}&" |
| 105 | + f"uuid={uuid_value.group(1)}") |
| 106 | + |
| 107 | + print(f"Downloading file to {output_filename}...") |
| 108 | + try: |
| 109 | + with urllib.request.urlopen(download_url, context=ssl_context) as response, open(output_filename, 'wb') as out_file: |
| 110 | + file_size = int(response.headers.get('Content-Length', 0)) |
| 111 | + print(file_size) |
| 112 | + block_size = 8192 |
| 113 | + |
| 114 | + if TQDM_AVAILABLE: |
| 115 | + progress_bar = tqdm(total=file_size, unit='iB', unit_scale=True) |
| 116 | + else: |
| 117 | + progress_bar = simple_progress_bar |
| 118 | + |
| 119 | + size = 0 |
| 120 | + while True: |
| 121 | + buffer = response.read(block_size) |
| 122 | + if not buffer: |
| 123 | + break |
| 124 | + size += len(buffer) |
| 125 | + out_file.write(buffer) |
| 126 | + if TQDM_AVAILABLE: |
| 127 | + progress_bar.update(len(buffer)) |
| 128 | + else: |
| 129 | + progress_bar(size, block_size, file_size) |
| 130 | + |
| 131 | + if TQDM_AVAILABLE: |
| 132 | + progress_bar.close() |
| 133 | + else: |
| 134 | + print() # New line after progress bar |
| 135 | + |
| 136 | + print("Download completed.") |
| 137 | + return True |
| 138 | + except urllib.error.URLError as e: |
| 139 | + print(f"\nError downloading file: {e}") |
| 140 | + return False |
| 141 | + |
| 142 | +def extract_file(filename, extract_path): |
| 143 | + print(f"Extracting \033[94m{filename}\033[0m to \033[96m{extract_path}\033[0m...") |
| 144 | + try: |
| 145 | + subprocess.run(["7z", "x", filename, f"-o{extract_path}"], check=True) |
| 146 | + print("Extraction completed.") |
| 147 | + return True |
| 148 | + except subprocess.CalledProcessError: |
| 149 | + print("Extraction failed. Make sure 7z is installed and in your PATH.") |
| 150 | + return False |
| 151 | + |
| 152 | +def check_writable(path): |
| 153 | + try: |
| 154 | + testfile = tempfile.TemporaryFile(dir=path) |
| 155 | + testfile.close() |
| 156 | + except OSError: |
| 157 | + return False |
| 158 | + return True |
| 159 | + |
| 160 | +def print_usage(): |
| 161 | + print("Usage: python3 download_script.py [--datadir PATH]") |
| 162 | + print("Options:") |
| 163 | + print(" --datadir PATH, -d PATH Specify the path to the Stealth data directory") |
| 164 | + print(" (default: ~/.StealthCoin)") |
| 165 | + |
| 166 | +def main(): |
| 167 | + parser = argparse.ArgumentParser(description="Download and extract Stealth bootstrap file.") |
| 168 | + parser.add_argument('--datadir', '-d', type=str, help='Path to the Stealth data directory') |
| 169 | + args = parser.parse_args() |
| 170 | + |
| 171 | + if not check_writable('.'): |
| 172 | + print("Error: Current working directory is not writable.") |
| 173 | + sys.exit(1) |
| 174 | + |
| 175 | + if args.datadir: |
| 176 | + data_dir = os.path.abspath(args.datadir) |
| 177 | + else: |
| 178 | + data_dir = os.path.expanduser("~/.StealthCoin") |
| 179 | + |
| 180 | + if not os.path.exists(data_dir): |
| 181 | + print(f"Error: Cannot find the Stealth data directory at {data_dir}") |
| 182 | + print_usage() |
| 183 | + sys.exit(1) |
| 184 | + |
| 185 | + file_id = "1WNmH1DiO4-_D-UYupoyFXM-rKMlGVTet" |
| 186 | + output_filename = "xst-bootstrap.7z" |
| 187 | + |
| 188 | + print("This script will download a large file (3.0G) from Google Drive.") |
| 189 | + print("Google Drive can't scan this file for viruses due to its size.") |
| 190 | + |
| 191 | + ssl_context = create_ssl_context() |
| 192 | + print("Using bundled Google Drive certificate chain for secure connection.") |
| 193 | + |
| 194 | + user_input = input("\033[1mDo you want to proceed with the download? (y/n):\033[0m ").lower() |
| 195 | + |
| 196 | + if user_input == 'y': |
| 197 | + if download_large_file(file_id, output_filename, ssl_context): |
| 198 | + user_input = input("\033[1mDo you want to extract the downloaded file? (y/n):\033[0m ").lower() |
| 199 | + if user_input == 'y': |
| 200 | + extract_file(output_filename, data_dir) |
| 201 | + else: |
| 202 | + print("\033[31mDownload cancelled.\033[0m") |
| 203 | + |
| 204 | +if __name__ == "__main__": |
| 205 | + main() |
0 commit comments