Assorted shell and Python scripts
1#!/usr/bin/env -S uv run --script
2# /// script
3# dependencies = [
4# "qbittorrent-api",
5# "docopt",
6# "rich",
7# ]
8# ///
9
10"""update_tracker.py
11
12Description:
13This script collects infohashes of all torrents in each qBittorrent instance,
14updates opentracker, and reannounces all torrents to their trackers.
15
16Expectations:
17- A JSON qBittorrent authentication file at ~/.config/qbittorrent_auth.json
18- SSH pubkey access to torrent tracker server
19- rsync installed on the host system running this script
20
21Usage:
22 update_tracker.py (--add-tracker DOMAIN)
23 update_tracker.py -h
24
25Options:
26 --add-tracker DOMAIN ensure the provided tracker domain is added to each torrent's tracker list
27 -h, --help show this help message and exit
28
29Examples:
30 update_tracker.py --add-tracker hyperreal.coffee
31"""
32
33import json
34import subprocess
35import tempfile
36from pathlib import Path
37
38import qbittorrentapi
39from docopt import docopt
40from rich.console import Console
41from rich.text import Text
42
43if __name__ == "__main__":
44 args = docopt(__doc__) # type: ignore
45
46 tracker_domain = args["--add-tracker"]
47
48 console = Console()
49 with console.status("[bold green]Executing the tasks...") as status:
50 # JSON file containing authentication info for each qBittorrent instance
51 QBITTORRENT_AUTH_FILE = Path.home().joinpath(".config/qbittorrent_auth.json")
52
53 # Open authentication file and load JSON data
54 with open(QBITTORRENT_AUTH_FILE, "r") as qbt_auth:
55 auth_data = json.load(qbt_auth)
56
57 # Collect infohashes of all torrents in each qBittorrent instance
58 console.log(
59 "Collecting infohashes of all torrents in each qBittorrent instance."
60 )
61 torrent_infohashes = []
62 for item in auth_data["instances"]:
63 with qbittorrentapi.Client(
64 host=item["hostname"],
65 username=item["username"],
66 password=item["password"],
67 ) as qbt_client:
68 try:
69 qbt_client.auth_log_in()
70 except qbittorrentapi.LoginFailed as e:
71 print(e)
72
73 for torrent in qbt_client.torrents_info():
74 torrent_infohashes.append(torrent.hash)
75
76 # Format the infohashes to have a \n at the end
77 console.log("Formatting infohashes to have a newline at the end.")
78 format_infohashes = set([f"{infohash}\n" for infohash in torrent_infohashes])
79
80 # Create a NamedTemporaryFile and write all infohashes to it, one per line
81 console.log("Creating temporary file to write infohashes to.")
82
83 with tempfile.NamedTemporaryFile() as ntf:
84 with open(ntf.name, "w") as tf:
85 tf.writelines(format_infohashes)
86
87 # Use `sudo cp -f` to copy the infohashes file to the torrent tracker's config
88 # directory, overwriting the whitelist.txt file.
89 console.log(
90 "Copying the temporary infohashes file to the torrent tracker's whitelist."
91 )
92 subprocess.run(
93 ["sudo", "cp", "-f", ntf.name, "/etc/opentracker/whitelist.txt"]
94 )
95
96 # Run `sudo systemctl restart opentracker.service`
97 console.log("Restarting opentracker.service")
98 subprocess.run(["sudo", "systemctl", "restart", "opentracker.service"])
99
100 # Reannounce all torrents in each qBittorrent instance to their trackers
101 console.log("Reannouncing all torrents to their trackers.")
102 for item in auth_data["instances"]:
103 with qbittorrentapi.Client(
104 host=item["hostname"],
105 username=item["username"],
106 password=item["password"],
107 ) as qbt_client:
108 for torrent in qbt_client.torrents_info():
109 torrent.reannounce()
110
111 console.log("Done!")
112
113 # Print output and make it look sexy ;)
114 console = Console()
115 tasks = Text("\nTasks completed:\n")
116 tasks.stylize("bold magenta")
117 console.print(tasks)
118 console.print(":white_check_mark: update the tracker's whitelist")
119
120 if tracker_domain:
121 console.print(
122 f":white_check_mark: ensure {tracker_domain}:6969/announce is in each torrent's tracker list"
123 )
124
125 console.print(":white_check_mark: reannounce all torrents to their trackers")
126
127 torrents = Text(str(len(torrent_infohashes)))
128 torrents.stylize("bold green")
129 console.print(torrents + " torrents were updated")