Skip to content

Instantly share code, notes, and snippets.

@zakkarry
Last active January 29, 2025 20:24
Show Gist options
  • Save zakkarry/5a64fe663ffb95bbd96b99bb8f4c9008 to your computer and use it in GitHub Desktop.
Save zakkarry/5a64fe663ffb95bbd96b99bb8f4c9008 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python3
import json
import random
import requests
import time
from enum import Enum
from urllib.parse import urlparse
### CONFIGURATION VARIABLES ###
# this webui will need to be the JSON-RPC endpoint
# this ends with '/json'
deluge_webui = "http://deluge:8112/json"
deluge_password = "deluged"
### STOP EDITING HERE ###
### STOP EDITING HERE ###
### STOP EDITING HERE ###
### STOP EDITING HERE ###
# error codes we could potentially receive
class DelugeErrorCode(Enum):
NO_AUTH = 1
BAD_METHOD = 2
CALL_ERR = 3
RPC_FAIL = 4
BAD_JSON = 5
# color codes for terminal
use_colors_codes = False
CRED = "\033[91m" if (use_colors_codes) else ""
CGREEN = "\33[32m" if (use_colors_codes) else ""
CYELLOW = "\33[33m" if (use_colors_codes) else ""
CBLUE = "\33[4;34m" if (use_colors_codes) else ""
CBOLD = "\33[1m" if (use_colors_codes) else ""
CEND = "\033[0m" if (use_colors_codes) else ""
class DelugeHandler:
def __init__(self):
self.deluge_cookie = None
self.session = requests.Session()
def call(self, method, params, retries=1):
url = urlparse(deluge_webui).geturl()
headers = {"Content-Type": "application/json"}
id = random.randint(0, 0x7FFFFFFF)
# set our cookie if we have it
if self.deluge_cookie:
headers["Cookie"] = self.deluge_cookie
if method == "auth.login":
print(
f"[{CGREEN}init{CEND}/{CYELLOW}script{CEND}] -> {CYELLOW}Connecting to Deluge:{CEND} {CBLUE}{url}{CEND}"
)
# send our request to the JSON-RPC endpoint
try:
response = self.session.post(
url,
data=json.dumps({"method": method, "params": params, "id": id}),
headers=headers,
)
response.raise_for_status()
except requests.exceptions.RequestException as network_error:
raise ConnectionError(
f"[{CRED}json-rpc{CEND}/{CRED}error{CEND}]: Failed to connect to Deluge at {CBLUE}{url}{CEND}"
) from network_error
# make sure the json response is valid
try:
json_response = response.json()
except json.JSONDecodeError as json_parse_error:
raise ValueError(
f"[{CRED}json-rpc{CEND}/{CRED}error{CEND}]: Deluge method {method} response was {CYELLOW}non-JSON{CEND}: {json_parse_error}"
)
# check for authorization failures, and retry once
if json_response.get("error", [None]) != None:
if (
json_response.get("error", [None]).get("code")
== DelugeErrorCode.NO_AUTH
and retries > 0
):
self.deluge_cookie = None
self.call("auth.login", [deluge_password], 0)
if self.deluge_cookie:
return self.call(method, params)
else:
raise ConnectionError(
f"[{CRED}json-rpc{CEND}/{CRED}error{CEND}]: Connection lost with Deluge. Reauthentication {CYELLOW}failed{CEND}."
)
self.handle_cookies(response.headers)
return json_response
def handle_cookies(self, headers):
deluge_cookie = headers.get("Set-Cookie")
if deluge_cookie:
deluge_cookie = deluge_cookie.split(";")[0]
else:
deluge_cookie = None
def main():
deluge_handler = DelugeHandler()
try:
# auth.login
auth_response = deluge_handler.call("auth.login", [deluge_password], 0)
print(
f"[{CGREEN}json-rpc{CEND}/{CYELLOW}auth.login{CEND}]",
auth_response,
"\n",
)
if auth_response.get("result") != True:
exit(1)
webui_connected = deluge_handler.call("web.connected", [], 0)
print(f"[json-rpc/web.connected] {webui_connected}")
time.sleep(2)
# get hosts list
web_ui_daemons = deluge_handler.call("web.get_hosts", [], 0).get("result")
# check which host is connected
for daemon in web_ui_daemons:
webui_connected_host = daemon[0]
webui_connected = deluge_handler.call(
"web.get_host_status", [webui_connected_host], 0
).get("result")
if webui_connected[1] == "Connected":
# reconnect the web daemon to the previously connected host
web_disconnect = deluge_handler.call("web.disconnect", [], 0)
webui_connected = deluge_handler.call(
"web.get_host_status", [webui_connected_host], 0
).get("result")
print(f"[json-rpc/web.disconnect] {web_disconnect}")
break
# checks the status of webui being connected, and connects to the daemon
webui_connected = webui_connected[1]
if webui_connected == "Online":
deluge_handler.call("web.connect", [webui_connected_host], 0)
time.sleep(1)
webui_connected = deluge_handler.call(
"web.get_host_status", [webui_connected_host], 0
).get("result")
if webui_connected[1] != "Connected":
print(
f"\n\n[{CRED}error{CEND}]: {CYELLOW}Your WebUI is not automatically connectable to the Deluge daemon.{CEND}\n"
f"{CYELLOW}\t Open the WebUI's connection manager to resolve this.{CEND}\n\n"
)
exit(1)
else:
print(f"[json-rpc/web.connect] Successfully reconnected to daemon.\n")
except Exception as e:
print(f"\n\n[{CRED}error{CEND}]: {CBOLD}{e}{CEND}\n\n")
deluge_handler.call("auth.delete_session", [], 0)
deluge_handler.session.close()
exit(0)
if __name__ == "__main__":
main()

Comments are disabled for this gist.