Initial commit: multi-peer torrent download engine
Reactor/loop-pool engine with TCP/µTP/MSE transports, per-connection pipelining, priority-driven piece selection with endgame, and the Python FFI test harness. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
commit
d8208685a2
55 changed files with 9989 additions and 0 deletions
80
interop/seed_rtorrent.py
Normal file
80
interop/seed_rtorrent.py
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import signal
|
||||
import subprocess
|
||||
import time
|
||||
|
||||
from common import touch_ready, wait_for_tcp
|
||||
|
||||
|
||||
def write_rc(path: str, torrent: str, data: str, port: int, session: str) -> None:
|
||||
os.makedirs(session, exist_ok=True)
|
||||
with open(path, "w", encoding="utf-8") as f:
|
||||
f.write(f"""
|
||||
directory.default.set = {data}
|
||||
session.path.set = {session}
|
||||
network.port_range.set = {port}-{port}
|
||||
network.port_random.set = no
|
||||
dht.mode.set = disable
|
||||
protocol.pex.set = no
|
||||
trackers.use_udp.set = no
|
||||
network.http.max_open.set = 0
|
||||
pieces.hash.on_completion.set = no
|
||||
""")
|
||||
|
||||
|
||||
def main() -> int:
|
||||
ap = argparse.ArgumentParser(description="Seed a fixture with rTorrent.")
|
||||
ap.add_argument("--torrent", required=True)
|
||||
ap.add_argument("--data", required=True)
|
||||
ap.add_argument("--port", type=int, required=True)
|
||||
args = ap.parse_args()
|
||||
|
||||
rc = "/tmp/rtorrent.rc"
|
||||
session = "/tmp/rtorrent-session"
|
||||
write_rc(rc, args.torrent, args.data, args.port, session)
|
||||
env = os.environ.copy()
|
||||
env.setdefault("TERM", "xterm")
|
||||
command = f"rtorrent -n -o import={rc}"
|
||||
proc = subprocess.Popen(
|
||||
["script", "-q", "-e", "-c", command, "/dev/null"],
|
||||
env=env,
|
||||
stdin=subprocess.PIPE,
|
||||
)
|
||||
stop = False
|
||||
|
||||
def _stop(signum, frame):
|
||||
nonlocal stop
|
||||
stop = True
|
||||
proc.terminate()
|
||||
subprocess.run(["pkill", "-TERM", "rtorrent"], check=False)
|
||||
|
||||
signal.signal(signal.SIGTERM, _stop)
|
||||
signal.signal(signal.SIGINT, _stop)
|
||||
|
||||
try:
|
||||
wait_for_tcp("127.0.0.1", args.port, timeout=45)
|
||||
if proc.stdin:
|
||||
proc.stdin.write(b"\x7f" + args.torrent.encode("utf-8") + b"\n")
|
||||
proc.stdin.flush()
|
||||
time.sleep(2)
|
||||
if proc.poll() is not None:
|
||||
return proc.returncode or 1
|
||||
touch_ready()
|
||||
print(f"rTorrent seeding on {args.port}", flush=True)
|
||||
while not stop:
|
||||
if subprocess.run(["pgrep", "rtorrent"], stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL).returncode != 0:
|
||||
return 1
|
||||
time.sleep(1)
|
||||
return 0
|
||||
finally:
|
||||
subprocess.run(["pkill", "-TERM", "rtorrent"], check=False)
|
||||
if proc.poll() is None:
|
||||
proc.terminate()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Loading…
Add table
Add a link
Reference in a new issue