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
88
interop/seed_qbittorrent.py
Normal file
88
interop/seed_qbittorrent.py
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
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())
|
||||
Loading…
Add table
Add a link
Reference in a new issue