Skip to content

Instantly share code, notes, and snippets.

@aksiksi
Forked from fizzxed/update_qbit_port.sh
Last active March 1, 2024 23:48
Show Gist options
  • Save aksiksi/34178962254fb73a8920d8b5e6650f98 to your computer and use it in GitHub Desktop.
Save aksiksi/34178962254fb73a8920d8b5e6650f98 to your computer and use it in GitHub Desktop.
Determine protonvpn port via gluetun and update qbittorrent
{ pkgs, ... }:
let
update-qbittorrent-port = pkgs.stdenv.mkDerivation rec {
name = "update-qbittorrent-port";
src = pkgs.fetchurl {
url = "https://gist.githubusercontent.com/aksiksi/34178962254fb73a8920d8b5e6650f98/raw/7917342ff0088fb58ed829571f7bc3f65108fc31/update_qbit_port.sh";
hash = "sha256-35eodSANzs/ycDPeoy70NhFsgnDOxmJTka/WJ6j6gKY=";
};
dontUnpack = true;
installPhase = ''
mkdir -p $out/bin
ln -s ${pkgs.coreutils}/bin/date $out/bin/date
ln -s ${pkgs.curl}/bin/curl $out/bin/curl
ln -s ${pkgs.gnugrep}/bin/grep $out/bin/grep
ln -s ${pkgs.podman}/bin/podman $out/bin/podman
ln -s ${pkgs.netcat}/bin/netcat $out/bin/netcat
cp ${src} $out/run.sh
chmod +x $out/run.sh
'';
buildInputs = [
pkgs.coreutils # date
pkgs.curl
pkgs.gnugrep
pkgs.podman
pkgs.netcat
];
};
in
{
systemd.services.update-qbittorrent-port = {
serviceConfig.Type = "oneshot";
path = [ update-qbittorrent-port ];
script = ''
.${update-qbittorrent-port}/run.sh
'';
environment = {
DOCKER_CMD = "podman";
DISCORD_WEBHOOK_URL = "my-url";
QBITTORRENT_USER = "admin";
QBITTORRENT_PASS = "adminadmin";
QBITTORRENT_PORT = "8888";
QBITTORRENT_SERVER = "localhost";
GLUETUN_SERVER = "localhost";
GLUETUN_PORT = "8000";
VPN_CT_NAME = "gluetun";
};
};
systemd.timers.update-qbittorrent-port = {
timerConfig = {
# Every 5 minutes.
OnUnitActiveSec = 300;
# 5 second jitter.
RandomizedDelaySec = "5s";
# Last run is persisted across reboots.
Persistent = true;
Unit = "update-qbittorrent-port.service";
};
partOf = [ "update-qbittorrent-port.service" ];
wantedBy = [ "timers.target" ];
};
}
#!/usr/bin/env bash
# Determine protonvpn port via gluetun and update qbittorrent
# Variables:
# QBITTORRENT_USER # qbittorrent username
# QBITTORRENT_PASS # qbittorrent password
# DISCORD_WEBHOOK_URL # Discord webhook URL for port change notifications
QBITTORRENT_PORT=${QBITTORRENT_PORT:-8080}
QBITTORRENT_SERVER=${QBITTORRENT_SERVER:-localhost}
GLUETUN_SERVER=${GLUETUN_SERVER:-localhost}
GLUETUN_PORT=${GLUETUN_PORT:-8000}
VPN_CT_NAME=${VPN_CT_NAME:-gluetun}
DOCKER_CMD=${DOCKER_CMD:-docker}
timestamp() {
date '+%Y-%m-%d %H:%M:%S'
}
findconfiguredport() {
curl -s -i --header "Referer: http://${QBITTORRENT_SERVER}:${QBITTORRENT_PORT}" --cookie "$1" "http://${QBITTORRENT_SERVER}:${QBITTORRENT_PORT}/api/v2/app/preferences" | grep -oP '(?<=\"listen_port\"\:)(\d{1,5})'
}
findactiveport() {
curl -s -i "http://${GLUETUN_SERVER}:${GLUETUN_PORT}/v1/openvpn/portforwarded" | grep -oP '(?<=\"port\"\:)(\d{1,5})'
}
getpublicip() {
curl -s -i "http://${GLUETUN_SERVER}:${GLUETUN_PORT}/v1/publicip/ip" | grep -oP '(?<="public_ip":.)(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})'
}
qbt_login() {
qbt_sid=$(curl -s -i --header "Referer: http://${QBITTORRENT_SERVER}:${QBITTORRENT_PORT}" --data "username=${QBITTORRENT_USER}" --data-urlencode "password=${QBITTORRENT_PASS}" "http://${QBITTORRENT_SERVER}:${QBITTORRENT_PORT}/api/v2/auth/login" | grep -oP '(?!set-cookie:.)SID=.*(?=\;.HttpOnly\;)')
return $?
}
qbt_changeport() {
curl -s -i --header "Referer: http://${QBITTORRENT_SERVER}:${QBITTORRENT_PORT}" --cookie "$1" --data-urlencode "json={\"listen_port\":$2,\"random_port\":false,\"upnp\":false}" "http://${QBITTORRENT_SERVER}:${QBITTORRENT_PORT}/api/v2/app/setPreferences" >/dev/null 2>&1
return $?
}
qbt_checksid() {
if curl -s --header "Referer: http://${QBITTORRENT_SERVER}:${QBITTORRENT_PORT}" --cookie "${qbt_sid}" "http://${QBITTORRENT_SERVER}:${QBITTORRENT_PORT}/api/v2/app/version" | grep -qi forbidden; then
return 1
else
return 0
fi
}
qbt_isreachable() {
nc -4 -vw 5 ${QBITTORRENT_SERVER} ${QBITTORRENT_PORT} &>/dev/null 2>&1
}
check_vpn_ct_health() {
while true;
do
if ! ${DOCKER_CMD} inspect "${VPN_CT_NAME}" --format='{{json .State.Status}}' | grep -q '"running"'; then
echo "$(timestamp) | Waiting for ${VPN_CT_NAME} healthy state.."
sleep 3
else
echo "$(timestamp) | VPN container ${VPN_CT_NAME} in healthy state!"
break
fi
done
}
get_portmap() {
res=0
public_ip=$(getpublicip)
if ! qbt_checksid; then
echo "$(timestamp) | qBittorrent Cookie invalid, getting new SessionID"
if ! qbt_login; then
echo "$(timestamp) | Failed getting new SessionID from qBittorrent"
return 1
fi
else
echo "$(timestamp) | qBittorrent SessionID Ok!"
fi
configured_port=$(findconfiguredport "${qbt_sid}")
active_port=$(findactiveport)
echo "$(timestamp) | Public IP: ${public_ip}"
echo "$(timestamp) | Configured Port: ${configured_port}"
echo "$(timestamp) | Active Port: ${active_port}"
if [ ${configured_port} != ${active_port} ]; then
if qbt_changeport "${qbt_sid}" ${active_port}; then
echo "$(timestamp) | Port Changed to: $(findconfiguredport ${qbt_sid})"
else
echo "$(timestamp) | Port Change failed."
res=1
fi
curl -H "Content-Type: application/json" -d "{\"username\": \"qBittorrent Port\", \"content\": \"qBittorrent forwarded port changed from ${configured_port} to ${active_port} with result=${res}\"}" ${DISCORD_WEBHOOK_URL}
else
echo "$(timestamp) | Port OK (Act: ${active_port} Cfg: ${configured_port})"
fi
return $res
}
public_ip=
configured_port=
active_port=
qbt_sid=
# Wait for a healthy state on the VPN container
check_vpn_ct_health
# check and possibly update the port
get_portmap
exit $?
@aksiksi
Copy link
Author

aksiksi commented Dec 27, 2023

Modified/fixed to support Podman which does not expose the State.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment