from __future__ import annotations import hashlib import json import os import socket import time import sys ROOT = os.environ.get("TORRENT_PEER_ROOT", "/work") sys.path.insert(0, os.path.join(ROOT, "harness")) from torrent_meta import Metadata, load_metadata # noqa: E402 def parse_size(text: str) -> int: s = text.strip().upper() mult = 1 if s[-1:] in ("K", "M", "G"): mult = {"K": 1024, "M": 1024**2, "G": 1024**3}[s[-1]] s = s[:-1] return int(s) * mult def file_sha1(path: str) -> str: h = hashlib.sha1() with open(path, "rb") as f: for chunk in iter(lambda: f.read(1024 * 1024), b""): h.update(chunk) return h.hexdigest() def write_manifest(path: str, **items) -> None: tmp = path + ".tmp" with open(tmp, "w", encoding="utf-8") as f: json.dump(items, f, indent=2, sort_keys=True) f.write("\n") os.replace(tmp, path) def wait_for_tcp(host: str, port: int, timeout: float = 30.0) -> None: deadline = time.time() + timeout last_error = None while time.time() < deadline: try: with socket.create_connection((host, port), timeout=1.0): return except OSError as exc: last_error = exc time.sleep(0.25) raise TimeoutError(f"timed out waiting for {host}:{port}: {last_error}") def resolve_ipv4(host: str) -> str: infos = socket.getaddrinfo(host, None, socket.AF_INET, socket.SOCK_STREAM) if not infos: raise OSError(f"no IPv4 address for {host}") return infos[0][4][0] def touch_ready(path: str = "/tmp/seed-ready") -> None: with open(path, "w", encoding="utf-8") as f: f.write("ready\n")