Skip to content

Instantly share code, notes, and snippets.

@idwpan
Last active October 21, 2025 17:20
Show Gist options
  • Select an option

  • Save idwpan/420d7d5e702240026bd2b65cf33515fe to your computer and use it in GitHub Desktop.

Select an option

Save idwpan/420d7d5e702240026bd2b65cf33515fe to your computer and use it in GitHub Desktop.
PowerShell script to auto update the qBittorrent listening port when ProtonVPN is connected
########################################################################################################################
# Script to Synchronize qBittorrent Listening Port with ProtonVPN assigned port on Windows.
#
# Description:
# This PowerShell script automates the synchronization of qBittorrent's listening port with the
# port number assigned by ProtonVPN's port forwarding feature. It continuously monitors the
# system to check if ProtonVPN is connected and if qBittorrent is running. When both conditions
# are met, it retrieves the latest port number from ProtonVPN's notifications and updates
# qBittorrent's listening port via its Web API if there's a mismatch.
#
# Usage:
# 1. Ensure qBittorrent's Web UI is enabled and note the username and password.
# 2. Encrypt and store your qBittorrent password:
# - Convert your password to a secure string and export it:
# `$securePassword = ConvertTo-SecureString "YourPassword" -AsPlainText -Force`
# `$securePassword | ConvertFrom-SecureString | Out-File "$env:LOCALAPPDATA\qBittorrentPassword.txt"`
# 3. Adjust the qBittorrentURL and qBittorrentUser variables as needed.
# 4. Run the script, when ProtonVPN is connected the qBittorrent Listening Port should be automatically updated.
# 5. Optional - Configure the script to run automatically via Task Scheduler.
#
# Disclaimer:
# Use this script responsibly and ensure compliance with all applicable laws and terms of service
# of the software and services involved.
########################################################################################################################
# Define qBittorrent API credentials and URL
$qBittorrentURL = "http://localhost:8080"
$qBittorrentUser = "admin"
# Function to retrieve the secure password from an encrypted file
function Get-SecurePassword {
# Path to the encrypted password file
$passwordFilePath = "$env:LOCALAPPDATA\qBittorrentPassword.txt"
# Check if the password file exists
if (Test-Path $passwordFilePath) {
# Read the encrypted password and convert it to a SecureString
$encryptedPassword = Get-Content -Path $passwordFilePath
$securePassword = $encryptedPassword | ConvertTo-SecureString
return $securePassword
}
else {
Write-Error "Password file not found at $passwordFilePath"
return $null
}
}
$qBittorrentPassword = Get-SecurePassword
# Install and import the PSSQLite module for SQLite database interaction
if (-not (Get-Module -Name PSSQLite -ListAvailable)) {
Install-Module -Name PSSQLite -Scope CurrentUser -Force
}
$modulePath = "$env:USERPROFILE\Documents\PowerShell\Modules\PSSQLite"
Import-Module -Name $modulePath
# Function to check if qBittorrent process is running
function Get-qBittorrentRunning {
return Get-Process -Name 'qbittorrent' -ErrorAction SilentlyContinue
}
# Function to check if ProtonVPN is connected by checking network adapters
function Get-ProtonVPNConnected {
# Adjust the adapter name pattern if necessary
$adapter = Get-NetAdapter | Where-Object {
($_.Name -Match 'ProtonVPN') -and ($_.Status -eq 'Up')
}
return $null -ne $adapter
}
# Function to get the latest port number from ProtonVPN notifications
function Get-ProtonVPNPort {
# Define the paths for the notifications database
$databasePath = "$env:LOCALAPPDATA\Microsoft\Windows\Notifications\wpndatabase.db"
$databaseCopy = "$env:TEMP\wpndatabase_copy.db"
# Copy the database to a temporary location to avoid file locks
Copy-Item -Path $databasePath -Destination $databaseCopy -Force #-ErrorAction SilentlyContinue
if (-not (Test-Path $databaseCopy)) {
Write-Error "Failed to copy the notifications database."
return $null
}
# Query the Notification table
$query = "SELECT * FROM Notification;"
try {
$notifications = Invoke-SqliteQuery -DataSource $databaseCopy -Query $query
}
catch {
Write-Error "Failed to query the notifications database."
return $null
}
# Initialize the variable to store the port number
$portNumber = $null
# Loop backwards through the notifications to find the latest port
for ($i = $notifications.Count - 1; $i -ge 0; $i--) {
$notification = $notifications[$i]
# Extract the Payload column
$payloadBytes = $notification.Payload
# Convert bytes to string using UTF8 encoding
$payloadString = [System.Text.Encoding]::UTF8.GetString($payloadBytes)
# Parse the payload as XML
try {
$xml = [xml]$payloadString
# Navigate the XML to find the message content
$messageNodes = $xml.Toast.Visual.Binding.Text
$message = $messageNodes -join " "
# Check if the message starts with "Active port:"
if ($message.StartsWith("Active port:")) {
# Extract the number after "Active port:"
if ($message -match "Active port:\s*(\d+)") {
$portNumber = [int]$matches[1]
# Exit the loop since we've found the desired notification
break
}
}
}
catch {
# Ignore parsing errors for non-XML payloads
continue
}
}
# Check if the port number was found
if ($null -ne $portNumber) {
return $portNumber
}
else {
Write-Host "No matching port notification found."
return $null
}
}
# Function to get qBittorrent session for authentication
function Get-qBittorrentSession {
param (
[String]$qBittorrentUrl,
[String]$username,
[SecureString]$password
)
$passwordPlain = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto(
[System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($password)
)
$session = New-Object Microsoft.PowerShell.Commands.WebRequestSession
try {
Invoke-RestMethod -Uri "$qBittorrentUrl/api/v2/auth/login" -Method Post -WebSession $session -Body @{
username = $username
password = $passwordPlain
} | Out-Null
return $session
}
catch {
Write-Error "Failed to authenticate to qBittorrent. Please check your credentials and URL."
return $null
}
}
# Function to retrieve the current listening port from qBittorrent preferences
function Get-qBittorrentPort {
param (
[String]$qBittorrentUrl,
[String]$username,
[SecureString]$password
)
$session = Get-qBittorrentSession -qBittorrentUrl $qBittorrentUrl -username $username -password $password
if (-not $session) { return $null }
try {
$preferences = Invoke-RestMethod -Uri "$qBittorrentUrl/api/v2/app/preferences" -Method Get -WebSession $session
return $preferences.listen_port
}
catch {
Write-Error "An error occurred while retrieving the port."
return $null
}
}
# Function to set the listening port in qBittorrent preferences
function Set-qBittorrentPort {
param (
[String]$qBittorrentUrl,
[String]$username,
[SecureString]$password,
[int]$port
)
$session = Get-qBittorrentSession -qBittorrentUrl $qBittorrentUrl -username $username -password $password
if (-not $session) { return }
try {
Invoke-RestMethod -Uri "$qBittorrentUrl/api/v2/app/setPreferences" -Method Post -WebSession $session -Body @{
json = "{`"listen_port`": $port}"
} | Out-Null
}
catch {
Write-Error "An error occurred while setting the port."
}
}
# Main routine to synchronize qBittorrent port with ProtonVPN port
# Check if ProtonVPN is connected
if (-not (Get-ProtonVPNConnected)) {
Write-Host "ProtonVPN not connected."
return
}
# Check if qBittorrent is running
if (-not (Get-qBittorrentRunning)) {
Write-Host "qBittorrent not running."
return
}
# Get the latest port from ProtonVPN
$protonPort = Get-ProtonVPNPort
if ($null -eq $protonPort) {
Write-Host "Could not determine ProtonVPN port."
return
}
# Get the current port from qBittorrent
$qbitPort = Get-qBittorrentPort -qBittorrentUrl $qBittorrentURL -username $qBittorrentUser -password $qBittorrentPassword
if ($null -eq $qbitPort) {
Write-Host "Could not determine current qBittorrent port."
return
}
# Update the qBittorrent port if it differs
if ($protonPort -ne $qbitPort) {
Set-qBittorrentPort -qBittorrentUrl $qBittorrentURL -username $qBittorrentUser -password $qBittorrentPassword -port $protonPort
Write-Host "Configured qBittorrent port to: $protonPort"
}
@DaniK-BIP
Copy link

Hi!

I've been tinkering around with this, resolving for example my custom documents location so the script can find the PSSQLite module, question,
does this work for the destop version of qbittorrent as well?

I currently have both Proton VPN and qbittorent as desktop versions, thank you in advance

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