So I’m learning python (by doing, reading, and doing some more), and I’ve been wanting to automate updating the listening port in my qbittorrent docker container when the gluetun vpn container changes the forwarded port.

This is what I came up with, which I combined with an hourly cronjob to keep it the listen port updated.

(Code under the spoiler tag to make this more readable).

Tap for spoiler
import re
import os
import logging
from datetime import datetime
from dotenv import load_dotenv

import docker
from qbittorrent import Client

# FUNCTION DECLARATION #


def log(code, message):
    logFilePath = "/opt/pyprojects/linux-iso-torrents/pf.log"
    logDateTime = datetime.today().strftime("%Y-%m-%d %H:%M:%S")
    message = f"[{logDateTime}] {message}"
    logger = logging.getLogger(__name__)
    logging.basicConfig(
        filename=f"{logFilePath}", encoding="utf-8", level=logging.DEBUG
    )
    match code:
        case "debug":
            logger.debug(message)
        case "info":
            logger.info(message)
        case "warning":
            logger.warning(message)
        case "error":
            logger.error(message)


def get_current_forwarded_port():
    client = docker.from_env()
    container = client.containers.get("a40124291102d")
    logs = container.logs().decode("utf-8")
    allPortForwards = re.findall(r".+port forwarded is [0-9]{5}", logs)
    currentForwardedPort = allPortForwards[-1].split(" ")[-1]

    return currentForwardedPort


def get_current_listening_port():
    qbConfigUrl = "/home/server/docker/qbit/config/qBittorrent/qBittorrent.conf"
    with open(qbConfigUrl) as f:
        config = f.read()
        qbitListenPort = re.search(r"Session\\Port=[0-9]{5}", config)
        currentListeningPort = qbitListenPort.group(0).split("=")[-1]

    return currentListeningPort


def update_qbittorrent_listen_port(port):
    QB_URL = os.getenv("QB_URL")
    QB_USER = os.getenv("QB_USER")
    QB_PASSWORD = os.getenv("QB_PASSWORD")
    portJSON = {}
    portJSON["listen_port"] = port
    qb = Client(QB_URL)
    qb.login(f"{QB_USER}", f"{QB_PASSWORD}")

    qb.set_preferences(**portJSON)


# BEGIN SCRIPT #

load_dotenv()

currentForwardedPort = get_current_forwarded_port()
currentListeningPort = get_current_listening_port()

if currentForwardedPort != currentListeningPort:
    update_qbittorrent_listen_port(currentPort)
    log("info", f"qbittorrent listen port set to {currentForwardedPort}")
else:
    log("info", "forwarded port and listen port are a match")

There’s more I want to do, the next thing being to check the status of both containers and if one or both are down, to log that and gracefully exit, but for now, I’m pretty happy with this (open to any feedback, always willing to learn).

  • litchralee@sh.itjust.works
    link
    fedilink
    English
    arrow-up
    15
    ·
    edit-2
    1 day ago

    One way to make this more Pythonic – and less C or POSIX-oriented – is to use the pathlib library for all filesystem operations. For example, while you could open a file in a contextmanager, pathlib makes it really easy to read a file:

    from pathlib import Path
    ...
    
    config = Path("/some/file/here.conf").read_text()
    

    This automatically opens the file (which checks for existence), reads out the entire file as a string (rather than bytes, but there’s a method for that too), and then closes up the file. If any of those steps go awry, you get a Python exception and a backtrace explaining exactly what happened.

    • harsh3466@lemmy.mlOP
      link
      fedilink
      arrow-up
      3
      ·
      1 day ago

      Ooh, that’s very nice. I will definitely use that instead of the with open... I’m using now. Thank you!!