Compare commits

...

6 Commits

Author SHA1 Message Date
Chris King
ff4bea25f6 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
2025-03-12 10:47:54 -07:00
Chris King
2abb9cb5d2 Add script for managing torrentarr containers (not in use) 2025-03-12 10:45:08 -07:00
Chris King
8231589d4d Update Overseer lastscan times 2025-03-12 10:44:38 -07:00
Chris King
1ffdc6caaf Add custom TimestampTrade plugin
Add custom RemoveMarkers plugin
2025-03-12 10:43:57 -07:00
Chris King
eb5693322d Update stashapp config 2025-03-12 10:43:10 -07:00
Chris King
773fb9067b Rename stashapp matcher to @exclude
Update stashapp exclusions
2025-03-12 10:37:43 -07:00
14 changed files with 306 additions and 8 deletions

View File

@@ -202,8 +202,8 @@ stash.tremendousturtle.tools {
@cf header CF-Connecting-IP *
# Match the bedroom Nvidia Shield IP to skip Authentik
@shield client_ip 192.168.1.142
reverse_proxy @shield stashapp-app-1:9999 {
@exclude client_ip 192.168.1.142 192.168.1.234 127.0.0.1
reverse_proxy @exclude stashapp-app-1:9999 {
header_up X-Real-IP {remote_host}
header_up X-Forwarded-Port {server_port}
}

0
media-dude/.env Normal file
View File

View File

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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"]

View File

@@ -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()

View File

@@ -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

View File

@@ -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

View File

@@ -33,21 +33,21 @@
"name": "4k Movies",
"enabled": true,
"type": "movie",
"lastScan": 1736797500041
"lastScan": 1741801200034
},
{
"id": "2",
"name": "Movies",
"enabled": true,
"type": "movie",
"lastScan": 1736797500057
"lastScan": 1741801200054
},
{
"id": "1",
"name": "TV Shows",
"enabled": true,
"type": "show",
"lastScan": 1736797500105
"lastScan": 1741801200272
}
],
"machineId": "5e16f8ceb511bde943f92bbe07e3e6e33307eb16"

89
scripts/torrentarr.sh Executable file
View File

@@ -0,0 +1,89 @@
#!/bin/bash
# Define available options as a simple array
OPTIONS=("tv" "movies" "all")
# Default values
TARGET_ALL=true
INSTANCE="all"
ACTION="up"
# Parse command line arguments
while [[ "$#" -gt 0 ]]; do
case $1 in
--down|-d)
ACTION="down"
;;
--restart|-r)
ACTION="restart"
;;
*)
# Assume this is the instance name
INSTANCE="$1"
# Only set TARGET_ALL to false if a specific instance is provided
if [ "$INSTANCE" != "all" ]; then
TARGET_ALL=false
fi
;;
esac
shift
done
# Validate the instance name
if ! [[ " ${OPTIONS[*]} " == *" ${INSTANCE} "* ]]; then
echo "Unknown instance: $INSTANCE"
echo "Valid options: ${OPTIONS[*]}"
exit 1
fi
# Function to execute docker compose commands
run_docker_compose() {
local instance=$1
local action=$2
local ENV_FILE="./${instance}.env"
if [ "$action" == "restart" ]; then
echo "Restart: Stopping $instance torrentarr instance..."
docker compose --env-file "$ENV_FILE" down --remove-orphans
echo "Restart: Starting $instance torrentarr instance..."
docker compose --env-file "$ENV_FILE" up -d
else
if [ "$action" == "down" ]; then
echo "Stopping $inst torrentarr instance..."
docker compose --env-file "$ENV_FILE" down --remove-orphans
echo "Stopped $inst torrentarr instance."
elif [ "$action" == "up" ]; then
echo "Starting $inst torrentarr instance..."
docker compose --env-file "$ENV_FILE" up -d
echo "Started $inst torrentarr instance."
fi
fi
}
# Handle all instances or specific instance
if [ "$TARGET_ALL" = true ]; then
if [ "$ACTION" == "up" ]; then
echo "Starting all instances..."
elif [ "$ACTION" == "down" ]; then
echo "Stopping all instances..."
elif [ "$ACTION" == "restart" ]; then
echo "Restarting all instances..."
fi
# Loop through all options except "all"
for inst in "${OPTIONS[@]}"; do
if [ "$inst" != "all" ]; then
run_docker_compose "$inst" $ACTION
fi
done
if [ "$ACTION" == "up" ]; then
echo "Started all instances"
elif [ "$ACTION" == "down" ]; then
echo "Stopped all instances"
elif [ "$ACTION" == "restart" ]; then
echo "Restarted all instances"
fi
else
run_docker_compose "$INSTANCE" $ACTION
fi

View File

@@ -147,7 +147,7 @@ plugins:
zzTracing: true
zzdryRun: false
PerformerDetailsExtended:
additionalStyling: true
additionalStyling: false
appearsMostWithGendered: true
scenesTimespanReverse: false
showWhenCollapsed: true
@@ -172,12 +172,15 @@ plugins:
timestampTrade:
addTimestampTradeUrl: true
addTsTradeTag: true
createGalleryFromScene: false
createMarkers: true
createMovieFromScene: true
disableGalleryLookupHook: true
disableSceneMarkersHook: true
disableSceneMarkersHook: false
extraUrls: true
matchFunscripts: false
mergeMarkers: true
overwriteMarkers: false
plugins_path: /plugins/
port: 9999
preview_audio: true
@@ -374,7 +377,7 @@ ui:
interactiveHeatmapsSpeeds: false
markerImagePreviews: false
markerScreenshots: false
markers: false
markers: true
phashes: true
previewOptions:
previewExcludeEnd: "0"

View File

@@ -47,6 +47,10 @@ services:
## Custom DupFileManager plugin
- /code/Axter-Stash-Gitea/plugins/DupFileManager:/plugins/community/DupFileManager
- /code/Axter-Stash-Gitea/plugins/DupFileManager/web:/custom_web
## Custom TimestampTrade plugin
- /code/Stash-CommunityScripts/plugins/timestampTrade:/plugins/timestampTrade
## Custom RemoveMarkers plugin
- /code/Stash-Plugins/RemoveMarkers:/plugins/RemoveMarkers
## Where to store generated content (screenshots,previews,transcodes,sprites)
- /media/stashapp/generated:/generated