Naut-Peer/interop/seed_qbittorrent.py
ookami125 d8208685a2 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>
2026-06-21 23:12:32 -04:00

88 lines
2.4 KiB
Python

from __future__ import annotations
import argparse
import os
import signal
import subprocess
import time
from common import touch_ready, wait_for_tcp
def write_config(profile: str, data_dir: str, port: int) -> None:
cfg_dir = os.path.join(profile, "qBittorrent", "config")
os.makedirs(cfg_dir, exist_ok=True)
# qBittorrent stores settings in an INI-like file with escaped keys.
# These disable internet/discovery paths and fix the peer port.
with open(os.path.join(cfg_dir, "qBittorrent.conf"), "w", encoding="utf-8") as f:
f.write(f"""[BitTorrent]
Session\\AddTorrentPaused=false
Session\\BTProtocol=TCP
Session\\DHTEnabled=false
Session\\DefaultSavePath={data_dir}
Session\\DisableAutoTMMByDefault=true
Session\\LSDEnabled=false
Session\\PeXEnabled=false
Session\\Port={port}
Session\\QueueingSystemEnabled=false
Session\\UPnP=false
[LegalNotice]
Accepted=true
[Preferences]
WebUI\\Enabled=false
""")
def main() -> int:
ap = argparse.ArgumentParser(description="Seed a fixture with qBittorrent-nox.")
ap.add_argument("--torrent", required=True)
ap.add_argument("--data", required=True)
ap.add_argument("--port", type=int, required=True)
args = ap.parse_args()
profile = "/tmp/qbt-profile"
write_config(profile, args.data, args.port)
cmd = [
"qbittorrent-nox",
f"--profile={profile}",
"--configuration=interop",
f"--torrenting-port={args.port}",
f"--save-path={args.data}",
"--add-paused=false",
"--skip-dialog=true",
"--skip-hash-check",
args.torrent,
]
proc = subprocess.Popen(cmd)
stop = False
def _stop(signum, frame):
nonlocal stop
stop = True
proc.terminate()
signal.signal(signal.SIGTERM, _stop)
signal.signal(signal.SIGINT, _stop)
try:
wait_for_tcp("127.0.0.1", args.port, timeout=45)
if proc.poll() is not None:
return proc.returncode or 1
touch_ready()
print(f"qBittorrent seeding on {args.port}", flush=True)
while not stop and proc.poll() is None:
time.sleep(1)
return proc.returncode or 0
finally:
if proc.poll() is None:
proc.terminate()
try:
proc.wait(timeout=10)
except subprocess.TimeoutExpired:
proc.kill()
if __name__ == "__main__":
raise SystemExit(main())