From ff4bea25f69628985bbf10ba9ada52e26407e9d3 Mon Sep 17 00:00:00 2001 From: Chris King Date: Wed, 12 Mar 2025 10:47:54 -0700 Subject: [PATCH] Add initial commit of new media management docker stack Includes transmission, gluetun, sonarr, radarr stacks Includes framework for adding plex, tautulli, prowlarr, overseerr, requestrr, and trash guides sync stacks Includes port-watcher docker container that monitors gluetun port forwarding file and sets transmission peer_port automatically --- media-dude/.env | 0 media-dude/docker-compose.yml | 0 media-dude/torrentarr/compose.torrentarr.yml | 74 +++++++++++++++++ media-dude/torrentarr/movies/.env | 4 + media-dude/torrentarr/movies/compose.yml | 15 ++++ media-dude/torrentarr/port-watcher/Dockerfile | 8 ++ .../torrentarr/port-watcher/port-watcher.py | 82 +++++++++++++++++++ media-dude/torrentarr/tv/.env | 4 + media-dude/torrentarr/tv/compose.yml | 15 ++++ 9 files changed, 202 insertions(+) create mode 100644 media-dude/.env create mode 100644 media-dude/docker-compose.yml create mode 100644 media-dude/torrentarr/compose.torrentarr.yml create mode 100644 media-dude/torrentarr/movies/.env create mode 100644 media-dude/torrentarr/movies/compose.yml create mode 100644 media-dude/torrentarr/port-watcher/Dockerfile create mode 100644 media-dude/torrentarr/port-watcher/port-watcher.py create mode 100644 media-dude/torrentarr/tv/.env create mode 100644 media-dude/torrentarr/tv/compose.yml diff --git a/media-dude/.env b/media-dude/.env new file mode 100644 index 0000000..e69de29 diff --git a/media-dude/docker-compose.yml b/media-dude/docker-compose.yml new file mode 100644 index 0000000..e69de29 diff --git a/media-dude/torrentarr/compose.torrentarr.yml b/media-dude/torrentarr/compose.torrentarr.yml new file mode 100644 index 0000000..4f4f4b0 --- /dev/null +++ b/media-dude/torrentarr/compose.torrentarr.yml @@ -0,0 +1,74 @@ +services: + transmission: + image: lscr.io/linuxserver/transmission:latest + environment: + DOCKER_MODS: linuxserver/mods:transmission-env-var-settings + PUID: 998 # media user + PGID: 998 # media group + UMASK: "002" + TZ: America/Los_Angeles + TRANSMISSION_DOWNLOAD_DIR: ${TORRENTARR_DOWNLOAD_DIR:?error}/complete + TRANSMISSION_INCOMPLETE_DIR: ${TORRENTARR_DOWNLOAD_DIR:?error}/incomplete + TRANSMISSION_SPEED_LIMIT_UP: "3750" + TRANSMISSION_SPEED_LIMIT_UP_ENABLED: "true" + TRANSMISSION_WATCH_DIR_ENABLED: "false" + TRANSMISSION_RPC_PORT: ${TORRENTARR_TRANSMISSION_RPC_PORT:?error} + TRANSMISSION_RPC_AUTHENTICATION_REQUIRED: "false" + volumes: + - ./transmission_config:/config + - ${TORRENTARR_DOWNLOAD_DIR:?error}:${TORRENTARR_DOWNLOAD_DIR:?error} + network_mode: "service:gluetun" + restart: unless-stopped + depends_on: + gluetun: + condition: service_healthy + + gluetun: + image: qmcgaw/gluetun + cap_add: + - NET_ADMIN + devices: + - /dev/net/tun:/dev/net/tun + volumes: + - gluetun_forwarding:/tmp/gluetun_forwarding + ports: + - ${TORRENTARR_TRANSMISSION_RPC_PORT:?error}:${TORRENTARR_TRANSMISSION_RPC_PORT:?error} + restart: unless-stopped + environment: + VPN_SERVICE_PROVIDER: protonvpn + VPN_TYPE: wireguard + VPN_PORT_FORWARDING: on + VPN_PORT_FORWARDING_STATUS_FILE: /tmp/gluetun_forwarding/forwarded_port + PORT_FORWARD_ONLY: "on" + SERVER_COUNTRIES: United States + SERVER_CITIES: Los Angeles + UPDATER_PERIOD: 24h + secrets: + - wireguard_private_key + port-watcher: + build: ../port-watcher + volumes: + - gluetun_forwarding:/watch + environment: + PORT_FILE: /watch/forwarded_port + TRANSMISSION_HOST: gluetun + TRANSMISSION_PORT: ${TORRENTARR_TRANSMISSION_RPC_PORT:?error} + restart: unless-stopped + healthcheck: + test: ["CMD", "test", "-f", "/watch/forwarded_port"] + interval: 10s + timeout: 60s + retries: 10 + start_period: 10s + depends_on: + transmission: + condition: service_started + gluetun: + condition: service_healthy + +volumes: + gluetun_forwarding: + +secrets: + wireguard_private_key: + file: ./secrets/wireguard_private_key \ No newline at end of file diff --git a/media-dude/torrentarr/movies/.env b/media-dude/torrentarr/movies/.env new file mode 100644 index 0000000..daa7cdc --- /dev/null +++ b/media-dude/torrentarr/movies/.env @@ -0,0 +1,4 @@ +COMPOSE_FILE=compose.yml:../compose.torrentarr.yml +TORRENTARR_DOWNLOAD_DIR=/media/movies/torrents +TORRENTARR_TRANSMISSION_RPC_PORT=10011 +COMPOSE_BAKE=true \ No newline at end of file diff --git a/media-dude/torrentarr/movies/compose.yml b/media-dude/torrentarr/movies/compose.yml new file mode 100644 index 0000000..8401c59 --- /dev/null +++ b/media-dude/torrentarr/movies/compose.yml @@ -0,0 +1,15 @@ +name: torrentarr-movies +services: + radarr: + image: ghcr.io/hotio/radarr + restart: unless-stopped + ports: + - "7878:7878" + environment: + PUID: 998 + PGID: 998 + UMASK: "002" + TZ: America/Los_Angeles + volumes: + - ./radarr_config:/config + - /media/movies/library:/media/movies/library \ No newline at end of file diff --git a/media-dude/torrentarr/port-watcher/Dockerfile b/media-dude/torrentarr/port-watcher/Dockerfile new file mode 100644 index 0000000..1727c44 --- /dev/null +++ b/media-dude/torrentarr/port-watcher/Dockerfile @@ -0,0 +1,8 @@ +FROM python:3.11-alpine + +WORKDIR /app +COPY port-watcher.py . + +RUN pip install watchdog transmission-rpc + +CMD ["python", "port-watcher.py"] \ No newline at end of file diff --git a/media-dude/torrentarr/port-watcher/port-watcher.py b/media-dude/torrentarr/port-watcher/port-watcher.py new file mode 100644 index 0000000..bfad882 --- /dev/null +++ b/media-dude/torrentarr/port-watcher/port-watcher.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python3 + +import os +import time +import logging +from transmission_rpc import Client +from watchdog.observers import Observer +from watchdog.events import FileSystemEventHandler + +logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(message)s') +logger = logging.getLogger() + +PORT_FILE = os.getenv('PORT_FILE', '/watch/forwarded_port') +TRANSMISSION_HOST = os.getenv('TRANSMISSION_HOST', 'gluetun') +TRANSMISSION_PORT = os.getenv('TRANSMISSION_PORT', 9091) + +class PortFileHandler(FileSystemEventHandler): + def __init__(self): + self.last_port = None + self.transmission_client = Client(host=TRANSMISSION_HOST, port=TRANSMISSION_PORT) + self.check_port_file() # Initial check + + def on_modified(self, event): + if not event.is_directory and event.src_path == PORT_FILE: + self.check_port_file() + + def check_port_file(self): + try: + if not os.path.exists(PORT_FILE): + logger.info(f"Port file not found: {PORT_FILE}") + return + + with open(PORT_FILE, 'r') as f: + port = f.read().strip() + + if port != self.last_port and port.isdigit(): + self.last_port = port + logger.info(f"Port forwarding changed to: {port}") + self.update_transmission(port) + except Exception as e: + logger.error(f"Error checking port file: {e}") + + def update_transmission(self, port): + max_attempts = 5 + attempt = 1 + delay = 5 # seconds between retry attempts + + while attempt <= max_attempts: + logger.info(f"Attempt {attempt}/{max_attempts}: Setting Transmission peer_port to {port}") + try: + self.transmission_client.set_session(peer_port=int(port)) + logger.info(f"Successfully updated Transmission peer_port to {port}") + logger.info(f"Testing Transmission peer port...") + if self.transmission_client.port_test(): + logger.info("Transmission peer port is open") + else: + logger.warning("Transmission peer port does not appear to be open") + return + except Exception as e: + logger.warning(f"Attempt {attempt}/{max_attempts} failed: {e}") + if attempt < max_attempts: + logger.info(f"Retrying in {delay} seconds...") + time.sleep(delay) + attempt += 1 + + logger.error(f"Failed to update Transmission peer_port after {max_attempts} attempts") + +if __name__ == "__main__": + path = os.path.dirname(PORT_FILE) + logger.info(f"Starting port-watcher monitoring {PORT_FILE}") + + event_handler = PortFileHandler() + observer = Observer() + observer.schedule(event_handler, path, recursive=False) + observer.start() + + try: + while True: + time.sleep(1) + except KeyboardInterrupt: + observer.stop() + observer.join() \ No newline at end of file diff --git a/media-dude/torrentarr/tv/.env b/media-dude/torrentarr/tv/.env new file mode 100644 index 0000000..08af5d7 --- /dev/null +++ b/media-dude/torrentarr/tv/.env @@ -0,0 +1,4 @@ +COMPOSE_FILE=compose.yml:../compose.torrentarr.yml +TORRENTARR_DOWNLOAD_DIR=/media/tv/torrents +TORRENTARR_TRANSMISSION_RPC_PORT=10010 +COMPOSE_BAKE=true \ No newline at end of file diff --git a/media-dude/torrentarr/tv/compose.yml b/media-dude/torrentarr/tv/compose.yml new file mode 100644 index 0000000..3272ecb --- /dev/null +++ b/media-dude/torrentarr/tv/compose.yml @@ -0,0 +1,15 @@ +name: torrentarr-tv +services: + sonarr: + image: ghcr.io/hotio/sonarr + restart: unless-stopped + ports: + - "8989:8989" + environment: + PUID: 998 + PGID: 998 + UMASK: "002" + TZ: America/Los_Angeles + volumes: + - ./sonarr_config:/config + - /media/tv/library:/media/tv/library \ No newline at end of file