This commit is contained in:
@@ -0,0 +1,34 @@
|
|||||||
|
FROM python:3.11-slim
|
||||||
|
|
||||||
|
# Install system dependencies
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
curl \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Install Python dependencies
|
||||||
|
RUN pip install --no-cache-dir \
|
||||||
|
requests \
|
||||||
|
watchdog
|
||||||
|
|
||||||
|
# Create app directory
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copy the Python script
|
||||||
|
COPY main.py .
|
||||||
|
|
||||||
|
# Make the script executable
|
||||||
|
RUN chmod +x main.py
|
||||||
|
|
||||||
|
# Create a non-root user
|
||||||
|
RUN useradd -m -u 1000 appuser && \
|
||||||
|
chown -R appuser:appuser /app
|
||||||
|
|
||||||
|
# Switch to non-root user
|
||||||
|
USER appuser
|
||||||
|
|
||||||
|
# Set default environment variables
|
||||||
|
ENV PORT_FORWARDED=/tmp/port_forwarded
|
||||||
|
ENV HTTP_S=http
|
||||||
|
|
||||||
|
# Run the application
|
||||||
|
CMD ["python", "main.py"]
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
#!/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)
|
||||||
Reference in New Issue
Block a user