Skip to content

Commit d644b48

Browse files
committed
feat: make Tdarr native-first on macOS with quality preset
1 parent f8ad8e5 commit d644b48

17 files changed

Lines changed: 972 additions & 26 deletions

.env.example

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@
88
# plex (default, runs natively on macOS) or jellyfin (runs in Docker)
99
MEDIA_SERVER=plex
1010

11+
# --- TDARR ---
12+
# native (recommended on macOS) or docker
13+
TDARR_MODE=native
14+
# Optional pinned Tdarr version for native mode (blank = latest available)
15+
TDARR_VERSION=
16+
1117
# --- PATHS ---
1218
MEDIA_DIR=/Users/YOURUSERNAME/Media
1319

README.md

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ There are dozens of *arr stack Docker Compose repos on GitHub. Almost all of the
3737
- **Auto-wires keys.** The configure script also writes Radarr/Sonarr API keys into Recyclarr and Unpackerr automatically.
3838
- **Built for macOS.** Native paths, launchd instead of systemd, OrbStack or Docker Desktop instead of bare Docker. Not a Linux guide with "should work on Mac" in the footnotes.
3939
- **Self-healing.** Hourly health checks, download watchdog, VPN failover between providers. Runs unattended.
40-
- **Quality automation.** TRaSH Guides profiles filter out bad releases. Kometa keeps Plex metadata clean. Tdarr saves disk space with automatic transcoding.
40+
- **Quality automation.** TRaSH Guides profiles filter out bad releases. Kometa keeps Plex metadata clean. Tdarr runs native-first on macOS with a quality-preserving flow preset.
4141

4242
New to self-hosted media? Start with the [basic version](https://github.com/liamvibecodes/mac-media-stack) first.
4343

@@ -47,7 +47,7 @@ New to self-hosted media? Start with the [basic version](https://github.com/liam
4747

4848
| Service | What It Does |
4949
|---------|-------------|
50-
| **Tdarr** | Automatic transcoding (convert codecs, save disk space) |
50+
| **Tdarr** | Native-first transcoding on macOS with quality-preserving HEVC flow preset |
5151
| **Recyclarr** | TRaSH Guides quality profiles (penalizes bad release groups, scene releases) |
5252
| **Kometa** | Plex metadata automation (franchise collections, resolution overlays, RT ratings) |
5353
| **Unpackerr** | Auto-extracts RAR'd downloads for Radarr/Sonarr |
@@ -126,6 +126,17 @@ Then open Tidarr at `http://localhost:8484` to authenticate with your Tidal acco
126126
Franchise sorting is kept manual by default because it requires your Plex token:
127127
`PLEX_TOKEN=... python3 scripts/franchise-sort.py`
128128

129+
### Tdarr Mode
130+
131+
- Default is `TDARR_MODE=native` (recommended on macOS).
132+
- Native Tdarr is installed and managed by `scripts/setup-tdarr-native.sh` (launchd server + node).
133+
- Docker Tdarr remains available via `TDARR_MODE=docker` and `--profile tdarr-docker`.
134+
- The stack preloads this Tdarr flow preset: `Quality-First HEVC (Resolution Preserving)`.
135+
- No resolution downscale
136+
- No hard bitrate cap
137+
- H.264 -> H.265 (CRF 19, preset slow)
138+
- Replace only when output size ratio stays within 25-99%
139+
129140
### Download Watchdog Configuration
130141

131142
The download watchdog reads qBittorrent credentials and behavior settings from environment variables or automatically detects them from your config files. Optional environment variables:
@@ -153,6 +164,12 @@ Optional flags when running from a local clone:
153164
bash bootstrap.sh --media-dir /Volumes/T9/Media --install-dir ~/mac-media-stack-advanced --non-interactive
154165
```
155166

167+
Use Docker-based Tdarr instead of native Tdarr:
168+
169+
```bash
170+
bash bootstrap.sh --tdarr-docker
171+
```
172+
156173
To use Jellyfin instead of Plex:
157174

158175
```bash
@@ -188,6 +205,10 @@ bash scripts/doctor.sh # preflight validation before first boot
188205
docker compose up -d
189206
# if MEDIA_SERVER=jellyfin in .env:
190207
docker compose --profile jellyfin up -d
208+
# if TDARR_MODE=docker in .env:
209+
docker compose --profile tdarr-docker up -d
210+
# if TDARR_MODE=native in .env (default):
211+
bash scripts/setup-tdarr-native.sh
191212
docker compose --profile autoupdate up -d watchtower # optional auto-updates
192213
bash scripts/configure.sh
193214
bash scripts/install-launchd-jobs.sh
@@ -247,6 +268,8 @@ bash scripts/vpn-mode.sh nord
247268
| `scripts/auto-heal.sh` | Hourly self-healer |
248269
| `scripts/backup.sh` | Config and database backup |
249270
| `scripts/download-watchdog.py` | Stalled torrent detection and auto-fix |
271+
| `scripts/setup-tdarr-native.sh` | Installs/updates native Tdarr and launchd services |
272+
| `scripts/tdarr-apply-quality-flow.sh` | Loads quality-first HEVC flow preset into Tdarr DB |
250273
| `scripts/vpn-mode.sh` | Manual VPN provider switcher |
251274
| `scripts/vpn-failover-watch.sh` | Automatic VPN failover daemon |
252275
| `scripts/run-kometa.sh` | Trigger Kometa metadata run |
@@ -263,6 +286,7 @@ Pre-configured templates in `configs/` (copy to your Media folder after first bo
263286

264287
- **recyclarr.yml** - TRaSH Guides quality profiles for Radarr and Sonarr
265288
- **kometa.yml** - Plex metadata automation (franchise collections, resolution overlays)
289+
- **tdarr-flow-quality-first-hevc.json** - Quality-first H.264 -> H.265 flow preset loaded by `scripts/tdarr-apply-quality-flow.sh`
266290

267291
Recyclarr API keys are auto-injected by `scripts/configure.sh`. Kometa still needs your manual Plex token + TMDB API key.
268292

SETUP.md

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ If you already have OrbStack (or Docker Desktop) and Plex installed, you can run
1414
curl -fsSL https://raw.githubusercontent.com/liamvibecodes/mac-media-stack-advanced/main/bootstrap.sh | bash
1515
```
1616

17-
It will prompt you for VPN keys, configure all core services, auto-wire Recyclarr + Unpackerr API keys, and install automation jobs. You'll still need to do Step 7 for Kometa/Tdarr manual setup afterward.
17+
It will prompt you for VPN keys, configure all core services, auto-wire Recyclarr + Unpackerr API keys, install native Tdarr on macOS, preload the quality-first Tdarr flow, and install automation jobs. You'll still need Step 7 for Kometa and final Tdarr library mapping.
1818

1919
To run from a local clone with custom paths:
2020

@@ -82,6 +82,11 @@ This stack supports two media servers. Choose one:
8282

8383
To use Jellyfin, pass `--jellyfin` to the bootstrap command, or set `MEDIA_SERVER=jellyfin` in your `.env` file.
8484

85+
### Tdarr Mode
86+
87+
`TDARR_MODE=native` is the default and recommended on macOS (more reliable for hardware decode/encode behavior and avoids Docker VM overhead).
88+
If you prefer containerized Tdarr, set `TDARR_MODE=docker` in `.env` or use `--tdarr-docker` with `bootstrap.sh`.
89+
8590
---
8691

8792
## Step 2: Download and Setup
@@ -130,6 +135,18 @@ If using Jellyfin, start with the profile enabled:
130135
docker compose --profile jellyfin up -d
131136
```
132137

138+
If using Docker Tdarr (`TDARR_MODE=docker`), also enable the Tdarr profile:
139+
140+
```bash
141+
docker compose --profile tdarr-docker up -d
142+
```
143+
144+
If using native Tdarr (`TDARR_MODE=native`, default), run:
145+
146+
```bash
147+
bash scripts/setup-tdarr-native.sh
148+
```
149+
133150
Wait for all containers to show OK. First pull takes 3-5 GB.
134151

135152
Optional: enable automatic container updates (Watchtower):
@@ -236,9 +253,21 @@ Replace `YOUR_PLEX_TOKEN` and `YOUR_TMDB_API_KEY`, then save.
236253
### Tdarr (transcoding)
237254

238255
1. Open http://localhost:8265
239-
2. Configure your libraries (Movies: `/movies`, TV: `/tv`)
240-
3. Add transcode plugins based on your preference (H.265 conversion saves ~50% disk space)
241-
4. Set the temp/cache directory to `/temp`
256+
2. Add libraries:
257+
- Native mode (`TDARR_MODE=native`):
258+
- Movies source: `<MEDIA_DIR>/Movies`
259+
- TV source: `<MEDIA_DIR>/TV Shows`
260+
- Temp/cache: `<MEDIA_DIR>/tdarr-transcode-cache`
261+
- Docker mode (`TDARR_MODE=docker`):
262+
- Movies source: `/movies`
263+
- TV source: `/tv`
264+
- Temp/cache: `/temp`
265+
3. Assign the preloaded flow: `Quality-First HEVC (Resolution Preserving)`
266+
4. Keep the defaults in that flow for quality-first behavior:
267+
- No resolution downscale
268+
- No hard bitrate cap
269+
- H.264 -> H.265 (CRF 19, preset slow)
270+
- Replace only when output size ratio is 25-99%
242271

243272
### Unpackerr
244273

@@ -259,6 +288,7 @@ This installs:
259288
- Log prune (daily cleanup, removes logs older than 30 days)
260289
- Download watchdog (stalled torrent auto-fix every 15 min)
261290
- Kometa scheduler (metadata refresh every 4 hours)
291+
- Native Tdarr launchd services (when `TDARR_MODE=native`)
262292

263293
Automation logs go to `<MEDIA_DIR>/logs/` and launchd stdout/stderr logs go to `<MEDIA_DIR>/logs/launchd/` (default `~/Media/...`).
264294

UPGRADE.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,10 @@ bash scripts/doctor.sh
7070
docker compose up -d
7171
# If using Jellyfin:
7272
docker compose --profile jellyfin up -d
73+
# If using Docker Tdarr:
74+
docker compose --profile tdarr-docker up -d
75+
# If using native Tdarr (default):
76+
bash scripts/setup-tdarr-native.sh
7377
bash scripts/configure.sh
7478
bash scripts/install-launchd-jobs.sh
7579
```
@@ -82,7 +86,7 @@ bash scripts/health-check.sh
8286

8387
After either upgrade path, confirm:
8488

85-
1. Tdarr libraries/plugins are configured at `http://localhost:8265`
89+
1. Tdarr is reachable at `http://localhost:8265` and libraries are assigned to `Quality-First HEVC (Resolution Preserving)`
8690
2. `bash scripts/health-check.sh` reports clean results
8791
3. If using Plex: `~/Media/config/kometa/config.yml` has `PLEX_TOKEN` + TMDB key
8892
4. If using Jellyfin: open Jellystat at `http://localhost:3000` and connect it to `http://jellyfin:8096`

bootstrap.sh

Lines changed: 62 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ MEDIA_DIR="$HOME/Media"
1515
INSTALL_DIR="$HOME/mac-media-stack-advanced"
1616
NON_INTERACTIVE=false
1717
MEDIA_SERVER=plex
18+
TDARR_MODE=native
1819

1920
usage() {
2021
cat <<EOF
@@ -24,12 +25,15 @@ Options:
2425
--media-dir DIR Media root path (default: ~/Media)
2526
--install-dir DIR Repo install directory (default: ~/mac-media-stack-advanced)
2627
--jellyfin Use Jellyfin instead of Plex as the media server
28+
--tdarr-mode MODE Tdarr mode: native (default) or docker
29+
--tdarr-docker Shortcut for --tdarr-mode docker
2730
--non-interactive Skip interactive prompts (manual Seerr wiring required)
2831
--help Show this help message
2932
3033
Examples:
3134
bash bootstrap.sh
3235
bash bootstrap.sh --media-dir /Volumes/T9/Media
36+
bash bootstrap.sh --tdarr-docker
3337
bash bootstrap.sh --media-dir /Volumes/T9/Media --non-interactive
3438
EOF
3539
}
@@ -56,6 +60,18 @@ while [[ $# -gt 0 ]]; do
5660
MEDIA_SERVER=jellyfin
5761
shift
5862
;;
63+
--tdarr-mode)
64+
if [[ $# -lt 2 || "$2" == --* ]]; then
65+
echo "Missing value for --tdarr-mode"
66+
exit 1
67+
fi
68+
TDARR_MODE="$2"
69+
shift 2
70+
;;
71+
--tdarr-docker)
72+
TDARR_MODE="docker"
73+
shift
74+
;;
5975
--non-interactive)
6076
NON_INTERACTIVE=true
6177
shift
@@ -75,6 +91,12 @@ done
7591
MEDIA_DIR="${MEDIA_DIR/#\~/$HOME}"
7692
INSTALL_DIR="${INSTALL_DIR/#\~/$HOME}"
7793

94+
if [[ "$TDARR_MODE" != "native" && "$TDARR_MODE" != "docker" ]]; then
95+
echo -e "${RED}Invalid TDARR mode:${NC} $TDARR_MODE"
96+
echo "Use --tdarr-mode native or --tdarr-mode docker"
97+
exit 1
98+
fi
99+
78100
echo ""
79101
echo "======================================="
80102
echo " Mac Media Stack Installer (Advanced)"
@@ -171,6 +193,7 @@ else
171193
echo " You can continue and install Plex later."
172194
fi
173195
fi
196+
echo -e "${GREEN}OK${NC} Tdarr mode: $TDARR_MODE"
174197

175198
# Check git
176199
if ! command -v git &>/dev/null; then
@@ -213,6 +236,11 @@ if [[ -f .env ]]; then
213236
else
214237
sed -i '' "1s|^|MEDIA_SERVER=$MEDIA_SERVER\n|" .env
215238
fi
239+
if grep -q '^TDARR_MODE=' .env; then
240+
sed -i '' "s|^TDARR_MODE=.*|TDARR_MODE=$TDARR_MODE|" .env
241+
else
242+
printf '\nTDARR_MODE=%s\n' "$TDARR_MODE" >> .env
243+
fi
216244
fi
217245

218246
# Create Jellyfin config directory if needed
@@ -269,8 +297,15 @@ echo ""
269297
echo -e "${CYAN}Starting media stack (first run downloads ~3-5 GB)...${NC}"
270298
echo ""
271299
COMPOSE_UP_CMD="docker compose up -d"
300+
COMPOSE_PROFILES=()
272301
if [[ "$MEDIA_SERVER" == "jellyfin" ]]; then
273-
COMPOSE_UP_CMD="docker compose --profile jellyfin up -d"
302+
COMPOSE_PROFILES+=(--profile jellyfin)
303+
fi
304+
if [[ "$TDARR_MODE" == "docker" ]]; then
305+
COMPOSE_PROFILES+=(--profile tdarr-docker)
306+
fi
307+
if [[ ${#COMPOSE_PROFILES[@]} -gt 0 ]]; then
308+
COMPOSE_UP_CMD="docker compose ${COMPOSE_PROFILES[*]} up -d"
274309
fi
275310
if ! $COMPOSE_UP_CMD; then
276311
echo ""
@@ -289,11 +324,23 @@ wait_for_service "Prowlarr" "http://localhost:9696" || true
289324
wait_for_service "Radarr" "http://localhost:7878" || true
290325
wait_for_service "Sonarr" "http://localhost:8989" || true
291326
wait_for_service "Seerr" "http://localhost:5055" || true
292-
wait_for_service "Tdarr" "http://localhost:8265" || true
293327
if [[ "$MEDIA_SERVER" == "jellyfin" ]]; then
294328
wait_for_service "Jellyfin" "http://localhost:8096/health" || true
295329
fi
296330

331+
if [[ "$TDARR_MODE" == "native" ]]; then
332+
echo ""
333+
echo -e "${CYAN}Setting up native Tdarr...${NC}"
334+
if ! bash scripts/setup-tdarr-native.sh --media-dir "$MEDIA_DIR"; then
335+
echo -e "${RED}Native Tdarr setup failed.${NC}"
336+
echo "Run this after fixing the issue:"
337+
echo " bash scripts/setup-tdarr-native.sh --media-dir $MEDIA_DIR"
338+
exit 1
339+
fi
340+
else
341+
wait_for_service "Tdarr" "http://localhost:8265" || true
342+
fi
343+
297344
# Configure
298345
echo ""
299346
if [[ "$NON_INTERACTIVE" == true ]]; then
@@ -322,15 +369,26 @@ echo " Tdarr: http://localhost:8265"
322369
echo ""
323370
echo " Media location: $MEDIA_DIR"
324371
echo ""
372+
373+
if [[ "$TDARR_MODE" == "docker" ]]; then
374+
TDARR_MOVIES_PATH="/movies"
375+
TDARR_TV_PATH="/tv"
376+
else
377+
TDARR_MOVIES_PATH="$MEDIA_DIR/Movies"
378+
TDARR_TV_PATH="$MEDIA_DIR/TV Shows"
379+
fi
380+
325381
echo " Remaining manual steps:"
326382
if [[ "$MEDIA_SERVER" == "jellyfin" ]]; then
327383
echo " 1. Complete Jellyfin setup wizard at http://localhost:8096"
328384
echo " Add libraries: Movies = /data/movies, TV Shows = /data/tvshows"
329-
echo " 2. Configure Tdarr at http://localhost:8265"
385+
echo " 2. In Tdarr, add libraries ($TDARR_MOVIES_PATH and $TDARR_TV_PATH) and assign the preloaded"
386+
echo " 'Quality-First HEVC (Resolution Preserving)' flow"
330387
else
331388
echo " 1. Set up Plex libraries (Movies: $MEDIA_DIR/Movies, TV: $MEDIA_DIR/TV Shows)"
332389
echo " 2. Edit $MEDIA_DIR/config/kometa/config.yml with Plex token + TMDB key"
333-
echo " 3. Configure Tdarr at http://localhost:8265"
390+
echo " 3. In Tdarr, add libraries ($TDARR_MOVIES_PATH and $TDARR_TV_PATH) and assign the preloaded"
391+
echo " 'Quality-First HEVC (Resolution Preserving)' flow"
334392
fi
335393
echo ""
336394
echo " Optional - Music (Lidarr + Tidarr):"

0 commit comments

Comments
 (0)