# Runs INSIDE guest 9202. Transmission's own RPC numbers vs the container's exact tx bytes. Password read at use, never printed. import json, subprocess, sys, time, urllib.request, base64 def sh(*a): return subprocess.run(a, capture_output=True, text=True).stdout.strip() C = 'transmission' IP = sh('docker', 'inspect', '-f', '{{range .NetworkSettings.Networks}}{{.IPAddress}} {{end}}', C).split()[0] PID = sh('docker', 'inspect', '-f', '{{.State.Pid}}', C) PW = sh('docker', 'exec', C, 'printenv', 'PASS') AUTH = 'Basic ' + base64.b64encode(('admin:' + PW).encode()).decode() SID = '' def rpc(method, args=None): global SID for _ in range(2): r = urllib.request.Request(f'http://{IP}:9091/transmission/rpc', data=json.dumps({'method': method, 'arguments': args or {}}).encode(), headers={'Authorization': AUTH, 'Host': 'transmission:9091', 'X-Transmission-Session-Id': SID, 'Content-Type': 'application/json'}, method='POST') try: with urllib.request.urlopen(r, timeout=20) as resp: return json.loads(resp.read()) except urllib.error.HTTPError as e: if e.code == 409: SID = e.headers.get('X-Transmission-Session-Id'); continue return {'http': e.code} def tx(): for line in open(f'/proc/{PID}/net/dev'): if line.strip().startswith('eth0:'): return int(line.split(':', 1)[1].split()[8]) mode = sys.argv[1] if mode == 'info': g = rpc('session-get')['arguments'] print('version', g.get('version'), {k: g.get(k) for k in ('speed-limit-up', 'speed-limit-up-enabled', 'alt-speed-up', 'alt-speed-enabled', 'alt-speed-time-enabled', 'units')}) print('session-stats', rpc('session-stats')['arguments']) elif mode == 'add': meta = base64.b64encode(open('/root/uwtest.torrent', 'rb').read()).decode() print(rpc('torrent-add', {'metainfo': meta, 'download-dir': '/mnt/felhom-drives/scratch_hdd/userdata/media/transmission', 'paused': False})) time.sleep(5); rpc('torrent-verify', {}); time.sleep(20) print(rpc('torrent-get', {'fields': ['name', 'status', 'percentDone', 'error', 'errorString']})) elif mode == 'limit': print(rpc('session-set', {'speed-limit-up': int(sys.argv[2]), 'speed-limit-up-enabled': True})) g = rpc('session-get')['arguments']; print({k: g.get(k) for k in ('speed-limit-up', 'speed-limit-up-enabled')}) elif mode == 'reannounce': print(rpc('torrent-reannounce', {})) elif mode == 'sample': dur, step = int(sys.argv[2]), int(sys.argv[3]); t0 = time.time(); prev = None while time.time() - t0 <= dur: st = rpc('session-stats')['arguments']; tg = rpc('torrent-get', {'fields': ['uploadedEver', 'rateUpload']})['arguments']['torrents'] row = {'t': round(time.time() - t0, 1), 'tr_upspeed': st['uploadSpeed'], 'tr_cur_up': st['current-stats']['uploadedBytes'], 'tr_tor_up': sum(x['uploadedEver'] for x in tg), 'tx': tx()} if prev: dt = row['t'] - prev['t'] row['rate_tr_Mbit'] = round((row['tr_cur_up'] - prev['tr_cur_up']) * 8 / dt / 1e6, 2) row['rate_container_Mbit'] = round((row['tx'] - prev['tx']) * 8 / dt / 1e6, 2) print(json.dumps(row), flush=True); prev = row; time.sleep(step)