#!/usr/bin/env python3 import os import time import requests import tempfile from pathlib import Path from watchdog.observers import Observer from watchdog.events import FileSystemEventHandler 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: self.port_syncer.update_port() class QBitTorrentPortSyncer: def __init__(self): 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') # 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: raise ValueError(f"Missing required environment variables: {', '.join(missing_vars)}") def update_port(self): """Update qBittorrent port from the port file""" try: # Read the port from the file with open(self.port_file, 'r') as f: port = f.read().strip() if not port: print(f"Warning: Port file {self.port_file} is empty") return # Remove old cookies file if os.path.exists(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 } session = requests.Session() login_response = session.post(login_url, data=login_data) if login_response.status_code != 200: print(f"Failed to login to qBittorrent: HTTP {login_response.status_code}") return # 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}"}}' } prefs_response = session.post(prefs_url, data=prefs_data) if prefs_response.status_code == 200: print(f"Successfully updated qbittorrent to port {port}") else: print(f"Failed to update port: HTTP {prefs_response.status_code}") except FileNotFoundError: print(f"Couldn't find file {self.port_file}") except Exception as e: print(f"Error updating port: {e}") def run(self): """Main loop to monitor port file changes""" observer = Observer() event_handler = PortFileHandler(self) while True: if os.path.exists(self.port_file): print(f"Found port file {self.port_file}, starting monitoring...") # Initial port update self.update_port() # Start file monitoring observer.schedule(event_handler, os.path.dirname(self.port_file), recursive=False) observer.start() try: while True: time.sleep(1) except KeyboardInterrupt: observer.stop() break else: print(f"Couldn't find file {self.port_file}") print("Trying again in 10 seconds") time.sleep(10) if __name__ == "__main__": try: syncer = QBitTorrentPortSyncer() syncer.run() except KeyboardInterrupt: print("\nShutting down...") except Exception as e: print(f"Error: {e}") exit(1)