import hashlibimport timeimport requestsimport sysfrom urllib.parse import parse_qs, urlparseCHUNK_SIZE = 50 * 1024 * 1024def sha256_bytes(data: bytes) -> bytes: return hashlib.sha256(data).digest()def parse_yifile_link(full_link: str): # 修复:兼容被CMD截断的链接,手动提取#后面的所有内容 if "#" in full_link: hash_part = full_link.split("#", 1)[1] else: hash_part = full_link # 处理带&的参数,parse_qs会自动处理 params = parse_qs(hash_part, keep_blank_values=True) url = params.get("url", [None])[0] k = params.get("k", [None])[0] name = params.get("name", ["download.7z"])[0] # 额外兼容:如果url里也带了&,手动提取 if url and "&" in url: url = url.split("&")[0] return url, k, namedef format_size(b): for u in ["B", "KB", "MB", "GB", "TB"]: if b < 1024: return f"{b:.2f}{u}" b /= 1024 return f"{b:.2f} TB"def format_eta(seconds): if seconds <= 0: return "00:00:00" hours = int(seconds // 3600) minutes = int((seconds % 3600) // 60) secs = int(seconds % 60) return f"{hours:02d}:{minutes:02d}:{secs:02d}"def aes_ctr_decrypt(cipher_data: bytes, key: bytes, counter: bytes): from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes from cryptography.hazmat.backends import default_backend backend = default_backend() cipher = Cipher(algorithms.AES(key), modes.CTR(counter), backend=backend) decryptor = cipher.decryptor() return decryptor.update(cipher_data) + decryptor.finalize()def download_and_decrypt(full_link: str): down_url, pass_key, file_name = parse_yifile_link(full_link) print("🔍 解析结果:") print(f"源文件地址:{down_url}") print(f"解密密钥:{pass_key}") print(f"输出文件名:{file_name}\n") if not down_url or not pass_key: print("❌ 错误:未能解析出 url 或 k 密钥") return key_bytes = sha256_bytes(f"PWD|{pass_key}".encode("utf-8")) base_ctr = sha256_bytes(f"CTR|{pass_key}".encode("utf-8"))[:16] try: resp = requests.get(down_url, stream=True, timeout=20) resp.raise_for_status() except Exception as e: print(f"❌ 下载请求失败:{e}") return total_size = int(resp.headers.get("Content-Length", 0)) downloaded = 0 last_time = time.time() last_down = 0 chunk_buf = b"" chunk_idx = 0 print(f"📁 文件总大小:{format_size(total_size)}\n") print("⏬ 开始下载并解密,实时进度如下:\n") with open(file_name, "wb") as f_out: for data in resp.iter_content(chunk_size=1024*1024): if not data: continue chunk_buf += data downloaded += len(data) now = time.time() if now - last_time >= 1.0: speed = (downloaded - last_down) / (now - last_time) last_down = downloaded last_time = now progress = (downloaded / total_size * 100) if total_size else 0 remaining_bytes = total_size - downloaded eta = remaining_bytes / speed if speed > 0 else 0 eta_str = format_eta(eta) print( f"\r进度:{progress:.1f}% | " f"已下载:{format_size(downloaded)} / {format_size(total_size)} | " f"速度:{format_size(speed)}/s | " f"剩余:{eta_str}", end="", flush=True ) while len(chunk_buf) >= CHUNK_SIZE: block = chunk_buf[:CHUNK_SIZE] chunk_buf = chunk_buf[CHUNK_SIZE:] ctr = bytearray(base_ctr) ctr[12] = (chunk_idx >> 24) & 0xFF ctr[13] = (chunk_idx >> 16) & 0xFF ctr[14] = (chunk_idx >> 8) & 0xFF ctr[15] = chunk_idx & 0xFF plain = aes_ctr_decrypt(block, key_bytes, bytes(ctr)) f_out.write(plain) chunk_idx += 1 if chunk_buf: ctr = bytearray(base_ctr) ctr[12] = (chunk_idx >> 24) & 0xFF ctr[13] = (chunk_idx >> 16) & 0xFF ctr[14] = (chunk_idx >> 8) & 0xFF ctr[15] = chunk_idx & 0xFF plain = aes_ctr_decrypt(chunk_buf, key_bytes, bytes(ctr)) f_out.write(plain) print("\n\n✅ 下载 + 解密全部完成!") print(f"✅ 已保存为:{file_name}")if __name__ == "__main__": if len(sys.argv) >= 2: link = sys.argv[1].strip() else: print("请粘贴完整的下载链接,然后按回车:") link = input().strip() download_and_decrypt(link)