Files
projects/Dockers/gluetun-qbittorrent-port-syncer/main.py
T
Bram 7daa18b8cd
Build and Push Docker Images / build-and-push (push) Successful in 21s
More logging
2025-10-24 22:31:24 +02:00

161 lines
6.1 KiB
Python

#!/usr/bin/env python3
import os
import time
import requests
import tempfile
import logging
from pathlib import Path
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
# Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
logger = logging.getLogger(__name__)
class PortFileHandler(FileSystemEventHandler):
def __init__(self, port_syncer):
self.port_syncer = port_syncer
def on_modified(self, event):
if not event.is_directory and event.src_path == self.port_syncer.port_file:
logger.info(f"Port file {event.src_path} was modified")
self.port_syncer.update_port()
class QBitTorrentPortSyncer:
def __init__(self):
logger.info("Initializing QBitTorrent Port Syncer...")
self.cookies_file = "/tmp/cookies.txt"
self.port_file = os.getenv('PORT_FORWARDED', '/tmp/port_forwarded')
self.qbittorrent_user = os.getenv('QBITTORRENT_USER')
self.qbittorrent_pass = os.getenv('QBITTORRENT_PASS')
self.qbittorrent_server = os.getenv('QBITTORRENT_SERVER')
self.qbittorrent_port = os.getenv('QBITTORRENT_PORT')
self.http_s = os.getenv('HTTP_S', 'http')
logger.info(f"Configuration loaded:")
logger.info(f" Port file: {self.port_file}")
logger.info(f" qBittorrent server: {self.qbittorrent_server}:{self.qbittorrent_port}")
logger.info(f" Protocol: {self.http_s}")
# Validate required environment variables
required_vars = [
'PORT_FORWARDED', 'QBITTORRENT_USER', 'QBITTORRENT_PASS',
'QBITTORRENT_SERVER', 'QBITTORRENT_PORT'
]
missing_vars = [var for var in required_vars if not os.getenv(var)]
if missing_vars:
logger.error(f"Missing required environment variables: {', '.join(missing_vars)}")
raise ValueError(f"Missing required environment variables: {', '.join(missing_vars)}")
logger.info("Initialization completed successfully")
def update_port(self):
"""Update qBittorrent port from the port file"""
logger.info(f"Attempting to update port from file: {self.port_file}")
try:
# Read the port from the file
with open(self.port_file, 'r') as f:
port = f.read().strip()
if not port:
logger.warning(f"Port file {self.port_file} is empty")
return
logger.info(f"Read port from file: {port}")
# Remove old cookies file
if os.path.exists(self.cookies_file):
logger.debug(f"Removing old cookies file: {self.cookies_file}")
os.remove(self.cookies_file)
# Login to qBittorrent
login_url = f"{self.http_s}://{self.qbittorrent_server}:{self.qbittorrent_port}/api/v2/auth/login"
login_data = {
'username': self.qbittorrent_user,
'password': self.qbittorrent_pass
}
logger.info(f"Attempting to login to qBittorrent at: {login_url}")
session = requests.Session()
login_response = session.post(login_url, data=login_data)
if login_response.status_code != 200:
logger.error(f"Failed to login to qBittorrent: HTTP {login_response.status_code}")
logger.error(f"Response content: {login_response.text}")
return
logger.info("Successfully logged into qBittorrent")
# Update port preferences
prefs_url = f"{self.http_s}://{self.qbittorrent_server}:{self.qbittorrent_port}/api/v2/app/setPreferences"
prefs_data = {
'json': f'{{"listen_port": "{port}"}}'
}
logger.info(f"Updating port preferences to: {port}")
prefs_response = session.post(prefs_url, data=prefs_data)
if prefs_response.status_code == 200:
logger.info(f"Successfully updated qBittorrent to port {port}")
else:
logger.error(f"Failed to update port: HTTP {prefs_response.status_code}")
logger.error(f"Response content: {prefs_response.text}")
except FileNotFoundError:
logger.error(f"Couldn't find file {self.port_file}")
except Exception as e:
logger.error(f"Error updating port: {e}")
def run(self):
"""Main loop to monitor port file changes"""
logger.info("Starting QBitTorrent Port Syncer...")
observer = Observer()
event_handler = PortFileHandler(self)
while True:
if os.path.exists(self.port_file):
logger.info(f"Found port file {self.port_file}, starting monitoring...")
# Push port on startup - wait a moment for file to be fully written
logger.info("Waiting 2 seconds for port file to be fully written...")
time.sleep(2)
logger.info("Pushing port on startup...")
self.update_port()
# Start file monitoring
logger.info(f"Starting file monitoring for directory: {os.path.dirname(self.port_file)}")
observer.schedule(event_handler, os.path.dirname(self.port_file), recursive=False)
observer.start()
logger.info("File monitoring started successfully")
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
logger.info("Received keyboard interrupt, stopping...")
observer.stop()
break
else:
logger.warning(f"Couldn't find file {self.port_file}")
logger.info("Trying again in 10 seconds...")
time.sleep(10)
if __name__ == "__main__":
try:
logger.info("Starting QBitTorrent Port Syncer application...")
syncer = QBitTorrentPortSyncer()
syncer.run()
except KeyboardInterrupt:
logger.info("Shutting down...")
except Exception as e:
logger.error(f"Application error: {e}")
exit(1)