From bd126b704ea64fb1b2c6b963fbe1fa69a040d778 Mon Sep 17 00:00:00 2001 From: soulis-1256 Date: Wed, 5 Aug 2026 16:12:47 +0300 Subject: [PATCH] Fix quality profile setup and split Recyclarr into include packs Always sync HD/UHD (and Sonarr WEB) as include packs, name them with an assemblrr prefix, soften blocking CF scores, zero quality minSize, relax minimumSeeders, and update Seerr on reconfigure. Document TRaSH guide drift and tighten comments and uninstall messaging. --- README.md | 29 +++ bin/cli.sh | 2 +- bin/configure.sh | 10 + bin/setup.sh | 9 +- lib/arr.sh | 246 +++++++++++++++--- lib/core.sh | 2 +- lib/fzf-tui.sh | 20 +- lib/seerr.sh | 144 ++++++---- platform/windows/bootstrap-dev.ps1 | 4 +- templates/recyclarr-full_hd.yml | 146 ----------- templates/recyclarr-ultra_hd.yml | 152 ----------- templates/recyclarr/includes/radarr-hd.yml | 8 + .../recyclarr/includes/radarr-soft-cfs.yml | 14 + templates/recyclarr/includes/radarr-uhd.yml | 8 + .../recyclarr/includes/sonarr-soft-cfs.yml | 14 + .../recyclarr/includes/sonarr-web-1080.yml | 8 + .../recyclarr/includes/sonarr-web-2160.yml | 8 + templates/recyclarr/recyclarr.yml | 23 ++ tests/integration/watchdog_stall.sh | 2 +- 19 files changed, 439 insertions(+), 410 deletions(-) delete mode 100644 templates/recyclarr-full_hd.yml delete mode 100644 templates/recyclarr-ultra_hd.yml create mode 100644 templates/recyclarr/includes/radarr-hd.yml create mode 100644 templates/recyclarr/includes/radarr-soft-cfs.yml create mode 100644 templates/recyclarr/includes/radarr-uhd.yml create mode 100644 templates/recyclarr/includes/sonarr-soft-cfs.yml create mode 100644 templates/recyclarr/includes/sonarr-web-1080.yml create mode 100644 templates/recyclarr/includes/sonarr-web-2160.yml create mode 100644 templates/recyclarr/recyclarr.yml diff --git a/README.md b/README.md index 55cf3f3..e2852e8 100644 --- a/README.md +++ b/README.md @@ -95,6 +95,35 @@ Configuration lives in your installation directory (default: `~/assemblrr`). - `compose/custom.yaml`: Optional services (from the example under `compose/examples/`). - `config/`: Persistent service data. +## Quality Profiles & TRaSH Guides + +assemblrr uses [Recyclarr](https://recyclarr.dev/) to sync [TRaSH Guides](https://trash-guides.info/) quality profiles and custom formats into Radarr and Sonarr. + +**Both resolutions are always installed** as assemblrr-named profiles (movies: **assemblrr HD Bluray + WEB** + **assemblrr UHD Bluray + WEB**; TV: **assemblrr WEB-1080p** + **assemblrr WEB-2160p**). The setup question only picks Seerr’s *default* for movie requests — the other profile stays available in the quality dropdown. Stock Radarr/Sonarr profiles (Any, HD-1080p, …) also appear there; that is normal. + +assemblrr deliberately **does not** apply TRaSH quality min-size limits (they can reject smaller legitimate releases), and softens hard-block custom formats (LQ / x265 / 3D scores set to 0). Tier preferences from the guides still apply when better releases exist. + +**Those guides change over time.** Trash IDs, profile names, and Recyclarr YAML schema can all break without warning. When that happens you may see: + +- Recyclarr sync errors (`Invalid quality profile trash_id`, `YAML error`, etc.) +- Seerr defaulting to **Any** instead of your chosen quality +- Missing Recyclarr profiles in Radarr/Sonarr (only stock profiles like Any / HD-1080p) + +That is expected community-guide churn — not a VPN or Docker failure. + +### What to do when it breaks + +1. Check Recyclarr: `docker logs recyclarr` and/or `docker exec recyclarr recyclarr sync` +2. List current guide IDs from inside the container: + ```bash + docker exec recyclarr recyclarr list quality-profiles radarr + docker exec recyclarr recyclarr list quality-profiles sonarr + ``` +3. Update the relevant pack under `templates/recyclarr/includes/` (or the live copies in your install’s `config/recyclarr/includes/`) with the new trash IDs. Root config is `templates/recyclarr/recyclarr.yml`. +4. Re-run sync, then refresh Seerr: `assemblrr configure` + +We try to keep templates current, but **plan on occasional manual updates** if you rely on TRaSH-backed profiles long-term. Official docs: [Recyclarr](https://recyclarr.dev/) · [TRaSH Guides](https://trash-guides.info/). + ## Development ### Windows (powershell) diff --git a/bin/cli.sh b/bin/cli.sh index da8ae4b..f3bf9f0 100644 --- a/bin/cli.sh +++ b/bin/cli.sh @@ -337,7 +337,7 @@ uninstall_app() { log_info "Media directory preserved at $MEDIA_DIRECTORY" fi else - log_info "Media directory preserved at $MEDIA_DIRECTORY (pass --media with --force to delete it)" + log_info "Media directory preserved at $MEDIA_DIRECTORY" fi echo "Removing CLI..." diff --git a/bin/configure.sh b/bin/configure.sh index c51f783..ecd5a30 100644 --- a/bin/configure.sh +++ b/bin/configure.sh @@ -146,6 +146,16 @@ fi # Configure Recyclarr (must run before Seerr so quality profiles exist in Radarr/Sonarr) configure_recyclarr || true +# Enforce minSize=0 and record preferred quality profiles after Recyclarr sync. +if [ -n "$RADARR_API_KEY" ]; then + relax_quality_sizes "Radarr" "7878" "$RADARR_API_KEY" || true + set_default_quality_profile "Radarr" "7878" "$RADARR_API_KEY" "lookup_radarr_profile" || true +fi +if [ -n "$SONARR_API_KEY" ]; then + relax_quality_sizes "Sonarr" "8989" "$SONARR_API_KEY" || true + set_default_quality_profile "Sonarr" "8989" "$SONARR_API_KEY" "lookup_sonarr_profile" || true +fi + # Configure Seerr (must run after media server is configured AND Recyclarr has synced profiles) configure_seerr || true diff --git a/bin/setup.sh b/bin/setup.sh index ea12dec..33840b8 100644 --- a/bin/setup.sh +++ b/bin/setup.sh @@ -239,8 +239,13 @@ copy_configuration_files() { ["compose/vpn.yaml"]="compose/vpn.yaml" ["compose/examples/custom.yaml.example"]="compose/examples/custom.yaml.example" [".env.example"]=".env.example" - ["templates/recyclarr-ultra_hd.yml"]="templates/recyclarr-ultra_hd.yml" - ["templates/recyclarr-full_hd.yml"]="templates/recyclarr-full_hd.yml" + ["templates/recyclarr/recyclarr.yml"]="templates/recyclarr/recyclarr.yml" + ["templates/recyclarr/includes/radarr-hd.yml"]="templates/recyclarr/includes/radarr-hd.yml" + ["templates/recyclarr/includes/radarr-uhd.yml"]="templates/recyclarr/includes/radarr-uhd.yml" + ["templates/recyclarr/includes/radarr-soft-cfs.yml"]="templates/recyclarr/includes/radarr-soft-cfs.yml" + ["templates/recyclarr/includes/sonarr-web-1080.yml"]="templates/recyclarr/includes/sonarr-web-1080.yml" + ["templates/recyclarr/includes/sonarr-web-2160.yml"]="templates/recyclarr/includes/sonarr-web-2160.yml" + ["templates/recyclarr/includes/sonarr-soft-cfs.yml"]="templates/recyclarr/includes/sonarr-soft-cfs.yml" ["branding.conf"]="branding.conf" ["bin/cli.sh"]="cli.sh" ["bin/configure.sh"]="configure.sh" diff --git a/lib/arr.sh b/lib/arr.sh index 35e9ba2..1cde09e 100644 --- a/lib/arr.sh +++ b/lib/arr.sh @@ -48,14 +48,13 @@ set_arr_auth() { # --- qBittorrent helpers --- -# Set qBittorrent credentials and save path using the temp password from container logs -# Called once before configuring any *arr service. -# On re-runs, tries the final credentials first to avoid triggering brute-force bans. +# Set qBittorrent WebUI credentials and default save paths. +# Prefer final credentials when already configured (avoids brute-force lockouts). qbit_set_credentials() { local qbit_port=8081 local qbit_cookie_jar="/tmp/qb_cookie_jar_init_$$_${qbit_port}" - # Try logging in with the final credentials first (already set from a previous run) + # Idempotent: succeed if final credentials already work if [ -n "$AUTH_USERNAME" ] && [ -n "$AUTH_PASSWORD" ]; then curl -s --connect-timeout 5 -c "$qbit_cookie_jar" \ -H "Referer: http://${API_HOST}:${qbit_port}" \ @@ -64,7 +63,6 @@ qbit_set_credentials() { local qbit_sid qbit_sid=$(qbit_cookie_sid "$qbit_cookie_jar") if [ -n "$qbit_sid" ]; then - # Credentials already set — ensure save path is configured curl -s --connect-timeout 5 \ "http://${API_HOST}:${qbit_port}/api/v2/app/setPreferences" \ -b "$qbit_cookie_jar" \ @@ -77,7 +75,7 @@ qbit_set_credentials() { fi fi - # Credentials not set yet — use temp password from container logs + # First-time setup: temporary password from container logs local wait_time=0 local max_wait=60 @@ -116,7 +114,7 @@ qbit_set_credentials() { return 1 } -# Create a qBittorrent category with a save path +# Create or update a qBittorrent category with a save path qbit_create_category() { local service_name="$1" local category="$2" @@ -131,13 +129,20 @@ qbit_create_category() { local qbit_sid qbit_sid=$(qbit_cookie_sid "$qbit_cookie_jar") if [ -n "$qbit_sid" ]; then + # createCategory + editCategory so savePath is set whether the category is new or existing curl -s --connect-timeout 10 \ "http://${API_HOST}:${qbit_port}/api/v2/torrents/createCategory" \ -b "$qbit_cookie_jar" \ -H "Referer: http://${API_HOST}:${qbit_port}" \ -d "category=${category}&savePath=${save_path}" \ 2>/dev/null >/dev/null || true - log_step "${service_name}: created qBittorrent '${category}' category (${save_path})" + curl -s --connect-timeout 10 \ + "http://${API_HOST}:${qbit_port}/api/v2/torrents/editCategory" \ + -b "$qbit_cookie_jar" \ + -H "Referer: http://${API_HOST}:${qbit_port}" \ + -d "category=${category}&savePath=${save_path}" \ + 2>/dev/null >/dev/null || true + log_step "${service_name}: qBittorrent '${category}' category → ${save_path}" else log_step_fail "${service_name}: could not login to qBittorrent to create categories" fi @@ -184,16 +189,16 @@ configure_arr_service() { log_step "${service_name}: root folder ${root_path} already exists" fi - # 1b. Remove stale auto-detected root folders + # Keep only the configured root folder local all_root_folders all_root_folders=$(api_get "$port" "/api/v3/rootfolder" "$apikey") - local stale_ids - stale_ids=$(echo "$all_root_folders" | jq -r --arg rp "$root_path" '.[] | select(.path != $rp) | .id' 2>/dev/null || echo "") + local extra_ids + extra_ids=$(echo "$all_root_folders" | jq -r --arg rp "$root_path" '.[] | select(.path != $rp) | .id' 2>/dev/null || echo "") - for stale_id in $stale_ids; do - if [ -n "$stale_id" ]; then - api_delete "$port" "/api/v3/rootfolder/${stale_id}" "$apikey" >/dev/null || true - log_step "${service_name}: removed stale root folder (id: ${stale_id})" + for extra_id in $extra_ids; do + if [ -n "$extra_id" ]; then + api_delete "$port" "/api/v3/rootfolder/${extra_id}" "$apikey" >/dev/null || true + log_step "${service_name}: removed extra root folder (id: ${extra_id})" fi done @@ -296,6 +301,81 @@ configure_arr_service() { set_arr_auth "$service_name" "$port" "$apikey" } +# --- Quality sizes & default profiles --- + +# Set minSize=0 on every quality definition (no minimum file-size filter). +# Values live in the *arr DB; they only change when written via this API. +relax_quality_sizes() { + local service_name="$1" + local port="$2" + local apikey="$3" + + local defs + defs=$(api_get "$port" "/api/v3/qualitydefinition" "$apikey") + if [ -z "$defs" ] || [ "$defs" = "[]" ]; then + log_step_fail "${service_name}: no quality definitions to update" + return 1 + fi + + # Only minSize is cleared; leave max/preferred alone (bulk update is picky about nulls). + local payload + payload=$(echo "$defs" | jq '[.[] | .minSize = 0]' 2>/dev/null || echo "") + if [ -z "$payload" ]; then + log_step_fail "${service_name}: failed to build quality definition payload" + return 1 + fi + + local result + result=$(api_put "$port" "/api/v3/qualitydefinition/update" "$apikey" "$payload") + if echo "$result" | jq -e 'type == "array"' >/dev/null 2>&1; then + log_step "${service_name}: quality minSize set to 0" + return 0 + fi + # Fallback: one PUT per definition + local ok=0 total=0 + while IFS= read -r row; do + [ -z "$row" ] && continue + total=$((total + 1)) + local id + id=$(echo "$row" | jq -r '.id // empty') + [ -z "$id" ] && continue + if api_put "$port" "/api/v3/qualitydefinition/${id}" "$apikey" "$row" >/dev/null 2>&1; then + ok=$((ok + 1)) + fi + done < <(echo "$payload" | jq -c '.[]' 2>/dev/null || true) + + if [ "$ok" -gt 0 ]; then + log_step "${service_name}: quality minSize set to 0 (${ok}/${total})" + return 0 + fi + log_step_fail "${service_name}: failed to update quality definitions" + return 1 +} + +# Record the preferred quality profile id/name for this install (setup choice). +set_default_quality_profile() { + local service_name="$1" + local port="$2" + local apikey="$3" + local profile_func="$4" + + local profile profile_id + profile=$($profile_func "$apikey") + profile_id="${profile%%:*}" + local profile_name="${profile##*:}" + + if [ -z "$profile_id" ] || [ "$profile_id" = "1" ] && [ "$profile_name" = "Any" ]; then + log_step "${service_name}: preferred quality profile → Any" + return 0 + fi + + if [ -n "${INSTALL_DIR:-}" ]; then + mkdir -p "$INSTALL_DIR/config" + echo "${profile_id}:${profile_name}" > "$INSTALL_DIR/config/.${service_name,,}-default-quality-profile" 2>/dev/null || true + fi + log_step "${service_name}: preferred quality profile → ${profile_name} (id ${profile_id})" +} + # --- Quality profile lookup --- lookup_radarr_profile() { @@ -308,27 +388,42 @@ lookup_radarr_profile() { return fi - # Recyclarr creates profiles with names like "Ultra-HD" or "HD Bluray + WEB" - # Match by keyword since the exact name depends on TRaSH Guide version - # Default Radarr profiles to skip: Any, SD, HD-720p, HD-1080p, HD - 720p/1080p - local target_keyword + # Prefer assemblrr-named Recyclarr profiles; fall back to stock *arr names. + local target_name="" + local stock_fallback="" + if [ "${SEERR_IS_4K:-false}" = "true" ]; then - target_keyword="Ultra" + target_name="assemblrr UHD Bluray + WEB" + stock_fallback="Ultra-HD" elif [ "${SEERR_DEFAULT_PROFILE:-1}" != "1" ]; then - target_keyword="Bluray + WEB" + target_name="assemblrr HD Bluray + WEB" + stock_fallback="HD-1080p" else echo "1:Any" return fi local matched - matched=$(echo "$profiles_json" | jq -r --arg kw "$target_keyword" '.[] | select(.name | contains($kw)) | select(.name as $n | ["Any","SD","HD-720p","HD-1080p","HD - 720p/1080p"] | index($n) | not) | "\(.id):\(.name)"' 2>/dev/null | head -1 || echo "") + matched=$(echo "$profiles_json" | jq -r --arg name "$target_name" ' + .[] | select(.name == $name) | "\(.id):\(.name)" + ' 2>/dev/null | head -1 || echo "") if [ -n "$matched" ]; then echo "$matched" - else - echo "1:Any" + return fi + + if [ -n "$stock_fallback" ]; then + matched=$(echo "$profiles_json" | jq -r --arg name "$stock_fallback" ' + .[] | select(.name == $name) | "\(.id):\(.name)" + ' 2>/dev/null | head -1 || echo "") + if [ -n "$matched" ]; then + echo "$matched" + return + fi + fi + + echo "1:Any" } lookup_sonarr_profile() { @@ -337,28 +432,43 @@ lookup_sonarr_profile() { profiles_json=$(api_get "8989" "/api/v3/qualityprofile" "$apikey") if [ -z "$profiles_json" ]; then - echo "1:Default" + echo "1:Any" return fi - # Recyclarr creates profiles with names like "Ultra-HD" or "WEB-1080p" - # Match by keyword since the exact name depends on TRaSH Guide version - # Default Sonarr profiles to skip: Any, SD, HD-720p, HD-1080p, HD - 720p/1080p - local target_keyword - if [ "${SEERR_IS_4K:-false}" = "true" ]; then - target_keyword="Ultra" - else - target_keyword="WEB" - fi + # Default TV profile is assemblrr WEB-1080p (movies 4K choice does not change this). + local target_name="assemblrr WEB-1080p" + local stock_fallback="HD-1080p" local matched - matched=$(echo "$profiles_json" | jq -r --arg kw "$target_keyword" '.[] | select(.name | contains($kw)) | select(.name as $n | ["Any","SD","HD-720p","HD-1080p","HD - 720p/1080p"] | index($n) | not) | "\(.id):\(.name)"' 2>/dev/null | head -1 || echo "") + matched=$(echo "$profiles_json" | jq -r --arg name "$target_name" ' + .[] | select(.name == $name) | "\(.id):\(.name)" + ' 2>/dev/null | head -1 || echo "") if [ -n "$matched" ]; then echo "$matched" - else - echo "1:Default" + return fi + + matched=$(echo "$profiles_json" | jq -r ' + .[] | select(.name | test("^assemblrr.*WEB.*1080"; "i")) | "\(.id):\(.name)" + ' 2>/dev/null | head -1 || echo "") + if [ -n "$matched" ]; then + echo "$matched" + return + fi + + if [ -n "$stock_fallback" ]; then + matched=$(echo "$profiles_json" | jq -r --arg name "$stock_fallback" ' + .[] | select(.name == $name) | "\(.id):\(.name)" + ' 2>/dev/null | head -1 || echo "") + if [ -n "$matched" ]; then + echo "$matched" + return + fi + fi + + echo "1:Any" } # --- Radarr configuration --- @@ -374,6 +484,58 @@ configure_radarr() { # --- Prowlarr configuration --- +# Set minimumSeeders=0 on Prowlarr app profiles and *arr indexers. +# Peer counts from indexers are often incomplete; requiring seeders>=1 blocks valid grabs. +relax_minimum_seeders() { + local prowlarr_key="$1" + local radarr_key="${2:-}" + local sonarr_key="${3:-}" + + local profiles + profiles=$(api_get "9696" "/api/v1/appprofile" "$prowlarr_key") + if [ -n "$profiles" ] && [ "$profiles" != "[]" ]; then + local profile_id profile_payload + while IFS= read -r row; do + [ -z "$row" ] && continue + profile_id=$(echo "$row" | jq -r '.id // empty') + [ -z "$profile_id" ] && continue + profile_payload=$(echo "$row" | jq '.minimumSeeders = 0' 2>/dev/null || echo "") + [ -z "$profile_payload" ] && continue + if api_put "9696" "/api/v1/appprofile/${profile_id}" "$prowlarr_key" "$profile_payload" >/dev/null 2>&1; then + log_step "Prowlarr: app profile '${profile_id}' minimumSeeders → 0" + fi + done < <(echo "$profiles" | jq -c '.[]' 2>/dev/null || true) + fi + + local port key name + for port_key_name in "7878:${radarr_key}:Radarr" "8989:${sonarr_key}:Sonarr"; do + port="${port_key_name%%:*}" + rest="${port_key_name#*:}" + key="${rest%%:*}" + name="${rest#*:}" + [ -z "$key" ] && continue + + local indexers + indexers=$(api_get "$port" "/api/v3/indexer" "$key") + [ -z "$indexers" ] || [ "$indexers" = "[]" ] && continue + + while IFS= read -r idx; do + [ -z "$idx" ] && continue + local idx_id idx_payload + idx_id=$(echo "$idx" | jq -r '.id // empty') + [ -z "$idx_id" ] && continue + idx_payload=$(echo "$idx" | jq ' + .fields = [.fields[] | + if .name == "minimumSeeders" then .value = 0 else . end + ] + ' 2>/dev/null || echo "") + [ -z "$idx_payload" ] && continue + api_put "$port" "/api/v3/indexer/${idx_id}" "$key" "$idx_payload" >/dev/null 2>&1 || true + done < <(echo "$indexers" | jq -c '.[]' 2>/dev/null || true) + log_step "${name}: indexer minimumSeeders → 0" + done +} + configure_prowlarr() { local apikey="$1" local radarr_apikey="$2" @@ -436,8 +598,8 @@ configure_prowlarr() { fi fi - # 2. Add public indexers - echo "Adding ${#PUBLIC_INDEXERS[@]} indexer(s) to Prowlarr" >&2 + # 2. Add selected indexers + echo "Adding ${#SELECTED_INDEXERS[@]} indexer(s) to Prowlarr" >&2 local indexer_schemas indexer_schemas=$(api_get "9696" "/api/v1/indexer/schema" "$apikey") @@ -452,7 +614,7 @@ configure_prowlarr() { local existing_names existing_names=$(jq_json_list_values "$existing_indexers" "name") - for indexer_name in ${PUBLIC_INDEXERS[@]+"${PUBLIC_INDEXERS[@]}"}; do + for indexer_name in ${SELECTED_INDEXERS[@]+"${SELECTED_INDEXERS[@]}"}; do # Skip if already added if echo "$existing_names" | grep -q -F -x "$indexer_name"; then log_step "Prowlarr: ${indexer_name} indexer already exists" @@ -502,7 +664,9 @@ configure_prowlarr() { echo >&2 log_success "Indexers added to Prowlarr!" >&2 - # 3. Set authentication + relax_minimum_seeders "$apikey" "$radarr_apikey" "${sonarr_apikey:-}" + + # 4. Set authentication set_arr_auth "Prowlarr" "9696" "$apikey" "v1" } diff --git a/lib/core.sh b/lib/core.sh index c03ec5a..ebded0e 100644 --- a/lib/core.sh +++ b/lib/core.sh @@ -245,7 +245,7 @@ prepare_install_dirs() { "scripts" ) - # Reclaim install root if a previous run left it root-owned + # Ensure install root is owned by the target user if [ -d "$install_dir" ] && [ ! -w "$install_dir" ]; then ensure_owned "$install_dir" "$(id -u)" "$(id -g)" fi diff --git a/lib/fzf-tui.sh b/lib/fzf-tui.sh index c2dca99..7a0a1db 100644 --- a/lib/fzf-tui.sh +++ b/lib/fzf-tui.sh @@ -1,12 +1,12 @@ #!/bin/bash # fzf-based TUI for selections (Prowlarr indexers, Jellyfin languages) # Provides reusable fzf selection patterns for assemblrr. -# Sets PUBLIC_INDEXERS array for indexers. +# Sets SELECTED_INDEXERS array for Prowlarr. # Usage: source lib/fzf-tui.sh; configure_indexers "$api_key" set -euo pipefail -PUBLIC_INDEXERS=() +SELECTED_INDEXERS=() # --- TTY detection (reused across functions) --- _fzf_has_tty() { @@ -73,8 +73,8 @@ fzf_single_select() { } # --- Generic multi-select fzf (for indexers) --- -# Args: $1 = data_source (jq expression or curl), $2 = awk filter -# Sets PUBLIC_INDEXERS array +# Args: $1 = tab-separated list lines +# Sets SELECTED_INDEXERS array fzf_multi_select() { local data="$1" local has_tty=false @@ -104,20 +104,20 @@ fzf_multi_select() { if [ -n "$selected" ]; then while IFS= read -r idx; do - [ -n "$idx" ] && PUBLIC_INDEXERS+=("$idx") + [ -n "$idx" ] && SELECTED_INDEXERS+=("$idx") done <<< "$selected" - echo "Selected ${#PUBLIC_INDEXERS[@]} indexer(s): $(IFS=,; echo "${PUBLIC_INDEXERS[*]}")" >&2 + echo "Selected ${#SELECTED_INDEXERS[@]} indexer(s): $(IFS=,; echo "${SELECTED_INDEXERS[*]}")" >&2 else echo "No indexers selected." >&2 fi else - # No TTY or no fzf — show top public indexers and prompt - echo "No interactive terminal available — listing popular public indexers." >&2 - echo "$data" | jq -r 'sort_by(.name | ascii_downcase) | .[0:20][] | " \(.name)"' 2>/dev/null >&2 + # No TTY or no fzf — list options and prompt + echo "No interactive terminal available — listing available indexers." >&2 + echo "$data" | cut -f1 | head -20 | sed 's/^/ /' >&2 echo "Enter indexer names separated by spaces (or blank to skip):" >&2 read -r input_line || true for idx in $input_line; do - PUBLIC_INDEXERS+=("$idx") + SELECTED_INDEXERS+=("$idx") done fi } diff --git a/lib/seerr.sh b/lib/seerr.sh index e90c77e..ec73676 100644 --- a/lib/seerr.sh +++ b/lib/seerr.sh @@ -7,8 +7,9 @@ set -euo pipefail -# --- Shared helper: connect a service (Radarr/Sonarr) to Seerr --- -# Usage: seerr_connect_service +# --- Shared helper: connect or update a service (Radarr/Sonarr) in Seerr --- +# Usage: seerr_connect_service [existing_id] +# With existing_id: PUT update; without: POST create. # Example: seerr_connect_service "radarr" 7878 "$RADARR_API_KEY" "lookup_radarr_profile" "/data/media/movies" "true" "$cookie_jar" seerr_connect_service() { local service_type="$1" @@ -18,6 +19,7 @@ seerr_connect_service() { local active_dir="$5" local is_4k="$6" local cookie_jar="$7" + local existing_id="${8:-}" if [ -z "$api_key" ]; then log_step_fail "Seerr: ${service_type} API key not provided" @@ -29,13 +31,12 @@ seerr_connect_service() { return 1 fi - # Look up quality profile from the service (created by Recyclarr) local profile profile=$($profile_func "$api_key") local profile_id="${profile%%:*}" local profile_name="${profile##*:}" - # Build the service payload + # id is read-only on PUT — omit it from the body local payload payload=$(cat </dev/null || echo "$payload") fi - # POST to Seerr local http_code + if [ -n "$existing_id" ]; then + http_code=$(curl -s -o /dev/null -w "%{http_code}" --connect-timeout 5 -X PUT -b "$cookie_jar" \ + -H "Content-Type: application/json" \ + -d "$payload" \ + "http://${API_HOST}:5055/api/v1/settings/${service_type}/${existing_id}" 2>/dev/null || echo "000") + if [ "$http_code" -ge 200 ] && [ "$http_code" -lt 300 ]; then + log_step "Seerr: updated ${service_type^} profile → ${profile_name}" + return 0 + else + log_step_fail "Seerr: failed to update ${service_type} (HTTP $http_code)" + return 1 + fi + fi + + # POST new connection http_code=$(curl -s -o /dev/null -w "%{http_code}" --connect-timeout 5 -X POST -b "$cookie_jar" \ -H "Content-Type: application/json" \ -d "$payload" \ "http://${API_HOST}:5055/api/v1/settings/${service_type}" 2>/dev/null || echo "000") if [ "$http_code" -ge 200 ] && [ "$http_code" -lt 300 ]; then - log_step "Seerr: connected ${service_type^}" + log_step "Seerr: connected ${service_type^} (profile: ${profile_name})" return 0 else log_step_fail "Seerr: failed to connect ${service_type} (HTTP $http_code)" @@ -99,6 +114,24 @@ seerr_service_connected() { return 0 } +# --- Shared helper: first existing Seerr server id for a service (or empty) --- +# Usage: seerr_service_id +seerr_service_id() { + local service_type="$1" + local cookie_jar="$2" + + local settings + settings=$(curl -sf --connect-timeout 5 -b "$cookie_jar" \ + "http://${API_HOST}:5055/api/v1/settings/${service_type}" 2>/dev/null || echo "") + + if [ -z "$settings" ] || [ "$settings" = "[]" ]; then + echo "" + return 1 + fi + + echo "$settings" | jq -r '.[0].id // empty' 2>/dev/null || echo "" +} + # --- Shared helper: get Seerr session cookie --- # Usage: seerr_get_cookie # Returns 0 on success, cookie file will be populated @@ -158,42 +191,60 @@ EOF configure_recyclarr() { local recyclarr_config_dir="$INSTALL_DIR/config/recyclarr" - mkdir -p "$recyclarr_config_dir" + local template_root="$INSTALL_DIR/templates/recyclarr" + mkdir -p "$recyclarr_config_dir/includes" if [ ! -w "$recyclarr_config_dir" ]; then log_step_fail "Recyclarr: config directory not writable (${recyclarr_config_dir})" return 1 fi - # Export variables for envsubst + # Only substitute known vars so $schema in YAML comments is left alone export RADARR_API_KEY SONARR_API_KEY - # Pick the right template based on user's 4K choice - local template_file="recyclarr-full_hd.yml" # default: 1080p - local profile_label="HD Bluray + WEB" + local default_label="assemblrr HD Bluray + WEB" if [ "${SEERR_IS_4K}" = "true" ]; then - template_file="recyclarr-ultra_hd.yml" # 4K - profile_label="UHD Bluray + WEB" + default_label="assemblrr UHD Bluray + WEB" fi - if [ -f "$INSTALL_DIR/templates/${template_file}" ]; then - if envsubst < "$INSTALL_DIR/templates/${template_file}" > "$recyclarr_config_dir/recyclarr.yml"; then - log_step "Recyclarr: generated config (${profile_label} profile)" - else - log_step_fail "Recyclarr: failed to write config (${recyclarr_config_dir}/recyclarr.yml)" - return 1 - fi - else - log_step_fail "Recyclarr: template ${template_file} not found" + if [ ! -f "$template_root/recyclarr.yml" ]; then + log_step_fail "Recyclarr: template root not found (${template_root}/recyclarr.yml)" return 1 fi - # Trigger a sync + if ! envsubst '${RADARR_API_KEY} ${SONARR_API_KEY}' \ + < "$template_root/recyclarr.yml" \ + > "$recyclarr_config_dir/recyclarr.yml"; then + log_step_fail "Recyclarr: failed to write config (${recyclarr_config_dir}/recyclarr.yml)" + return 1 + fi + + # Include packs (no secrets). Replace the directory contents with the template set. + local include_src="$template_root/includes" + if [ ! -d "$include_src" ]; then + log_step_fail "Recyclarr: includes directory not found (${include_src})" + return 1 + fi + rm -f "$recyclarr_config_dir/includes"/*.yml 2>/dev/null || true + if ! cp "$include_src"/*.yml "$recyclarr_config_dir/includes/"; then + log_step_fail "Recyclarr: failed to copy include packs" + return 1 + fi + + local pack_count + pack_count=$(find "$recyclarr_config_dir/includes" -maxdepth 1 -name '*.yml' | wc -l | tr -d ' ') + log_step "Recyclarr: generated config (${pack_count} include packs; Seerr default: ${default_label})" + _cfg_log_info "Recyclarr: syncing TRaSH Guides to Radarr and Sonarr... this might take a moment" - if docker exec recyclarr recyclarr sync > /dev/null 2>&1; then + local sync_out + if sync_out=$(docker exec recyclarr recyclarr sync 2>&1); then log_step "Recyclarr: synced Custom Formats and Quality Profiles successfully" else - log_step_fail "Recyclarr: failed to sync to Radarr/Sonarr (check docker logs recyclarr)" + log_step_fail "Recyclarr: failed to sync to Radarr/Sonarr" + echo "$sync_out" | grep -E '•|Error|error|Invalid|YAML' | head -5 | while IFS= read -r line; do + _cfg_log_info " $line" + done + return 1 fi } @@ -245,31 +296,21 @@ configure_seerr() { echo "Connecting Seerr services" >&2 - # Connect Radarr if not already connected if [ -n "$RADARR_API_KEY" ]; then - local radarr_count - radarr_count=$(seerr_service_connected "radarr" "$seerr_cookie_jar") || radarr_count=0 - if [ "$radarr_count" -eq 0 ]; then - if ! seerr_connect_service "radarr" 7878 "$RADARR_API_KEY" "lookup_radarr_profile" \ - "/data/media/movies" "${SEERR_IS_4K:-false}" "$seerr_cookie_jar"; then - log_step_fail "Seerr: failed to connect Radarr (re-run to retry)" - fi - else - log_step "Seerr: Radarr already connected" + local radarr_id + radarr_id=$(seerr_service_id "radarr" "$seerr_cookie_jar" || true) + if ! seerr_connect_service "radarr" 7878 "$RADARR_API_KEY" "lookup_radarr_profile" \ + "/data/media/movies" "${SEERR_IS_4K:-false}" "$seerr_cookie_jar" "$radarr_id"; then + log_step_fail "Seerr: failed to configure Radarr (re-run to retry)" fi fi - # Connect Sonarr if not already connected if [ -n "$SONARR_API_KEY" ]; then - local sonarr_count - sonarr_count=$(seerr_service_connected "sonarr" "$seerr_cookie_jar") || sonarr_count=0 - if [ "$sonarr_count" -eq 0 ]; then - if ! seerr_connect_service "sonarr" 8989 "$SONARR_API_KEY" "lookup_sonarr_profile" \ - "/data/media/tv" "false" "$seerr_cookie_jar"; then - log_step_fail "Seerr: failed to connect Sonarr (re-run to retry)" - fi - else - log_step "Seerr: Sonarr already connected" + local sonarr_id + sonarr_id=$(seerr_service_id "sonarr" "$seerr_cookie_jar" || true) + if ! seerr_connect_service "sonarr" 8989 "$SONARR_API_KEY" "lookup_sonarr_profile" \ + "/data/media/tv" "false" "$seerr_cookie_jar" "$sonarr_id"; then + log_step_fail "Seerr: failed to configure Sonarr (re-run to retry)" fi fi @@ -350,8 +391,7 @@ EOF return 1 fi - # Check for error in response — "hostname already configured" means media server - # was connected by a previous run, which is fine (admin user already exists) + # "already configured" is success when re-running against an initialized Seerr local auth_error auth_error=$(echo "$auth_response" | jq -r '.error // ""' 2>/dev/null || echo "") if [ -n "$auth_error" ]; then @@ -365,7 +405,6 @@ EOF return 1 fi else - # Successful auth — verify admin user was created local auth_id auth_id=$(echo "$auth_response" | jq -r '.id // ""' 2>/dev/null || echo "") if [ -n "$auth_id" ]; then @@ -377,12 +416,10 @@ EOF fi fi - # Step 5: Get a session cookie for subsequent API calls + # Step 5: session cookie for subsequent API calls local seerr_cookie_jar="/tmp/seerr_cookie_jar_$$_${seerr_port}" - # Get session cookie (with hostname for initial setup) if ! seerr_get_cookie "$seerr_cookie_jar" "true"; then - # Retry with login-only if first attempt fails (hostname already configured) if ! seerr_get_cookie "$seerr_cookie_jar" "false"; then log_step_fail "Seerr: failed to get session cookie" rm -f "$seerr_cookie_jar" @@ -390,10 +427,7 @@ EOF fi fi - # Note: Library sync is skipped here — Jellyfin hasn't indexed anything yet on first setup. - # Seerr will auto-sync libraries periodically (every 24h) once content exists. - - # Step 6: Initialize Seerr (mark setup as complete) + # Step 6: mark setup complete local init_code init_code=$(curl -s -o /dev/null -w "%{http_code}" --connect-timeout 5 -X POST -b "$seerr_cookie_jar" \ -H "Content-Type: application/json" \ diff --git a/platform/windows/bootstrap-dev.ps1 b/platform/windows/bootstrap-dev.ps1 index cc172ff..7493827 100644 --- a/platform/windows/bootstrap-dev.ps1 +++ b/platform/windows/bootstrap-dev.ps1 @@ -250,7 +250,9 @@ cp /tmp/$AppName/compose/base.yaml /tmp/$AppName/compose/vpn.yaml /tmp/$AppName/ mkdir -p "`$INSTALL_DIR/compose/examples" cp /tmp/$AppName/compose/examples/custom.yaml.example "`$INSTALL_DIR/compose/examples/" 2>/dev/null || true cp /tmp/$AppName/.env.example "`$INSTALL_DIR/" 2>/dev/null || true -cp /tmp/$AppName/templates/recyclarr-full_hd.yml /tmp/$AppName/templates/recyclarr-ultra_hd.yml "`$INSTALL_DIR/templates/" +mkdir -p "`$INSTALL_DIR/templates/recyclarr/includes" +cp /tmp/$AppName/templates/recyclarr/recyclarr.yml "`$INSTALL_DIR/templates/recyclarr/" +cp /tmp/$AppName/templates/recyclarr/includes/*.yml "`$INSTALL_DIR/templates/recyclarr/includes/" mkdir -p "`$INSTALL_DIR/scripts" cp /tmp/$AppName/scripts/* "`$INSTALL_DIR/scripts/" echo "Files synced to install directory" diff --git a/templates/recyclarr-full_hd.yml b/templates/recyclarr-full_hd.yml deleted file mode 100644 index 39041e4..0000000 --- a/templates/recyclarr-full_hd.yml +++ /dev/null @@ -1,146 +0,0 @@ -# yaml-language-server: $schema=https://schemas.recyclarr.dev/v8/config-schema.json -# HD Bluray + WEB profile (1080p) — used when user chooses non-4K -# Template: recyclarr-full_hd.yml - -radarr: - radarr-instance: - base_url: http://radarr:7878 - api_key: ${RADARR_API_KEY} - - quality_definition: - type: movie - - quality_profiles: - - name: HD Bluray + WEB - reset_unmatched_scores: - enabled: true - upgrade: - allowed: true - until_quality: Bluray-1080p - until_score: 10000 - min_format_score: 0 - quality_sort: top - qualities: - - name: Bluray-1080p - - name: WEB 1080p - qualities: - - WEBDL-1080p - - WEBRip-1080p - - name: Bluray-720p - - custom_formats: - # HQ Release Groups + Misc + Unwanted - - trash_ids: - - ed27ebfef2f323e964fb1f61391bcb35 # HD Bluray Tier 01 - - c20c8647f2746a1f4c4262b0fbbeeeae # HD Bluray Tier 02 - - 5608c71bcebba0a5e666223bae8c9227 # HD Bluray Tier 03 - - c20f169ef63c5f40c2def54abaf4438e # WEB Tier 01 - - 403816d65392c79236dcb6dd591aeda4 # WEB Tier 02 - - af94e0fe497124d1f9ce732069ec8c3b # WEB Tier 03 - - e7718d7a3ce595f289bfee26adc178f5 # Repack/Proper - - ae43b294509409a6a13919dedd4764c4 # Repack2 - - 5caaaa1c08c1742aa4342d8c4cc463f2 # Repack3 - - ed38b889b31be83fda192888e2286d83 # BR-DISK - - e6886871085226c3da1830830146846c # Generated Dynamic HDR - - 90a6f9a284dff5103f6346090e6280c8 # LQ - - e204b80c87be9497a8a6eaff48f72905 # LQ (Release Title) - - dc98083864ea246d05a42df0d05f81cc # x265 (HD) - - b8cd450cbfa689c0259a01d9e29ba3d6 # 3D - - bfd8eb01832d646a0a89c4deb46f8564 # Upscaled - - 0a3f082873eb454bde444150b70253cc # Extras - - 712d74cd88bceb883ee32f773656b1f5 # Sing-Along Versions - - cae4ca30163749b891686f95532519bd # AV1 - - cc5e51a9e85a6296ceefe097a77f12f4 # BCORE - - 16622a6911d1ab5d5b8b713d5b0036d4 # CRiT - - 2a6039655313bf5dab1e43523b62c374 # MA - assign_scores_to: - - name: HD Bluray + WEB - - # Streaming Services - - trash_ids: - - b3b3a6ac74ecbd56bcdbefa4799fb9df # AMZN - - 40e9380490e748672c2522eaaeb692f7 # ATVP - - 84272245b2988854bfb76a16e60baea5 # DSNP - - 509e5f41146e278f9eab1ddaceb34515 # HBO - - 5763d1b0ce84aff3b21038eea8e9b8ad # HMAX - - 526d445d4c16214309f0fd2b3be18a89 # Hulu - - e0ec9672be6cac914ffad34a6b077209 # iT - - 6a061313d22e51e0f25b7cd4dc065233 # MAX - - 170b1d363bd8516fbf3a3eb05d4faff6 # NF - - c9fd353f8f5f1baf56dc601c4cb29920 # PCOK - - e36a0ba1bc902b26ee40818a1d59b8bd # PMTP - - c2863d2a50c9acad1fb50e53ece60817 # STAN - assign_scores_to: - - name: HD Bluray + WEB - - # Audio - Uncomment to enable Advanced Audio Formats - - trash_ids: - # - 496f355514737f7d83bf7aa4d24f8169 # TrueHD Atmos - # - 2f22d89048b01681dde8afe203bf2e95 # DTS X - # - 417804f7f2c4308c1f4c5d380d4c4475 # ATMOS (undefined) - # - 1af239278386be2919e1bcee0bde047e # DD+ ATMOS - # - 3cafb66171b47f226146a0770576870f # TrueHD - # - dcf3ec6938fa32445f590a4da84256cd # DTS-HD MA - # - a570d4a0e56a2874b64e5bfa55202a1b # FLAC - # - e7c2fcae07cbada050a0af3357491d7b # PCM - # - 8e109e50e0a0b83a5098b056e13bf6db # DTS-HD HRA - # - 185f1dd7264c4562b9022d963ac37424 # DD+ - # - f9f847ac70a0af62ea4a08280b859636 # DTS-ES - # - 1c1a4c5e823891c75bc50380a6866f73 # DTS - # - 240770601cc226190c367ef59aba7463 # AAC - # - c2998bd0d90ed5621d8df281e839436e # DD - assign_scores_to: - - name: HD Bluray + WEB - - # Movie Versions - Uncomment to prefer specific versions - - trash_ids: - # - 570bc9ebecd92723d2d21500f4be314c # Remaster - # - eca37840c13c6ef2dd0262b141a5482f # 4K Remaster - # - e0c07d59beb37348e975a930d5e50319 # Criterion Collection - # - 9d27d9d2181838f76dee150882bdc58c # Masters of Cinema - # - db9b4c4b53d312a3ca5f1378f6440fc9 # Vinegar Syndrome - # - 957d0f44b592285f26449575e8b1167e # Special Edition - # - eecf3a857724171f968a66cb5719e152 # IMAX - # - 9f6cbff8cfe4ebbc1bde14c7b7bec0de # IMAX Enhanced - assign_scores_to: - - name: HD Bluray + WEB - - # Optional Unwanted - - trash_ids: - # - b6832f586342ef70d9c128d40c07b872 # Bad Dual Groups - # - cc444569854e9de0b084ab2b8b1532b2 # Black and White Editions - # - ae9b7c9ebde1f3bd336a8cbd1ec4c5e5 # No-RlsGroup - # - 7357cf5161efbf8c4d5d0c30b4815ee2 # Obfuscated - # - 5c44f52a8714fdd79bb4d98e2673be1f # Retags - # - f537cf427b64c38c8e36298f657e4828 # Scene - assign_scores_to: - - name: HD Bluray + WEB - -sonarr: - sonarr-instance: - base_url: http://sonarr:8989 - api_key: ${SONARR_API_KEY} - - quality_definition: - type: series - - quality_profiles: - - trash_id: a5ffae4bbda71217e132ce4ed37367c0 # WEB-1080p - reset_unmatched_scores: - enabled: true - - custom_format_groups: - add: - - trash_id: 7a2ba4144a594826d4ac03515b371937 # [Optional] Streaming Services - select_all: true - - custom_formats: - # Optional - - trash_ids: - # - 32b367365729d530ca1c124a0b180c64 # Bad Dual Groups - # - 82d40da2bc6923f41e14394075dd4b03 # No-RlsGroup - # - e1a997ddb54e3ecbfe06341ad323c458 # Obfuscated - # - 06d66ab109d4d2eddb2794d21526d140 # Retags - # - 1b3994c551cbb92a2c781af061f4ab44 # Scene - assign_scores_to: - - name: WEB-1080p diff --git a/templates/recyclarr-ultra_hd.yml b/templates/recyclarr-ultra_hd.yml deleted file mode 100644 index 61574fe..0000000 --- a/templates/recyclarr-ultra_hd.yml +++ /dev/null @@ -1,152 +0,0 @@ -# yaml-language-server: $schema=https://schemas.recyclarr.dev/v8/config-schema.json -# UHD Bluray + WEB profile (4K) — used when user chooses Ultra-HD -# Template: recyclarr-ultra_hd.yml - -radarr: - radarr-instance: - base_url: http://radarr:7878 - api_key: ${RADARR_API_KEY} - - quality_definition: - type: movie - - quality_profiles: - - trash_id: 64fb5f9858489bdac2af690e27c8f42f # UHD Bluray + WEB - reset_unmatched_scores: - enabled: true - - custom_format_groups: - add: - - trash_id: ff204bbcecdd487d1cefcefdbf0c278d # [Optional] Golden Rule UHD - select: - - dc98083864ea246d05a42df0d05f81cc # x265 (HD) - - 839bea857ed2c0a8e084f3cbdbd65ecb # x265 (no HDR/DV) - - trash_id: a3ac6af01d78e4f21fcb75f601ac96df # [Unwanted] Unwanted Formats - select: - - b8cd450cbfa689c0259a01d9e29ba3d6 # 3D - - cae4ca30163749b891686f95532519bd # AV1 - - b6832f586342ef70d9c128d40c07b872 # Bad Dual Groups - - cc444569854e9de0b084ab2b8b1532b2 # Black and White Editions - - ed38b889b31be83fda192888e2286d83 # BR-DISK - - 0a3f082873eb454bde444150b70253cc # Extras - - e6886871085226c3da1830830146846c # Generated Dynamic HDR - - 90a6f9a284dff5103f6346090e6280c8 # LQ - - e204b80c87be9497a8a6eaff48f72905 # LQ (Release Title) - - 712d74cd88bceb883ee32f773656b1f5 # Sing-Along Versions - - bfd8eb01832d646a0a89c4deb46f8564 # Upscaled - - custom_formats: - # Audio - Uncomment to enable Advanced Audio Formats - - trash_ids: - # - 496f355514737f7d83bf7aa4d24f8169 # TrueHD Atmos - # - 2f22d89048b01681dde8afe203bf2e95 # DTS X - # - 417804f7f2c4308c1f4c5d380d4c4475 # ATMOS (undefined) - # - 1af239278386be2919e1bcee0bde047e # DD+ ATMOS - # - 3cafb66171b47f226146a0770576870f # TrueHD - # - dcf3ec6938fa32445f590a4da84256cd # DTS-HD MA - # - a570d4a0e56a2874b64e5bfa55202a1b # FLAC - # - e7c2fcae07cbada050a0af3357491d7b # PCM - # - 8e109e50e0a0b83a5098b056e13bf6db # DTS-HD HRA - # - 185f1dd7264c4562b9022d963ac37424 # DD+ - # - f9f847ac70a0af62ea4a08280b859636 # DTS-ES - # - 1c1a4c5e823891c75bc50380a6866f73 # DTS - # - 240770601cc226190c367ef59aba7463 # AAC - # - c2998bd0d90ed5621d8df281e839436e # DD - assign_scores_to: - - name: UHD Bluray + WEB - - # Movie Versions - Uncomment to prefer specific versions - - trash_ids: - # - 570bc9ebecd92723d2d21500f4be314c # Remaster - # - eca37840c13c6ef2dd0262b141a5482f # 4K Remaster - # - e0c07d59beb37348e975a930d5e50319 # Criterion Collection - # - 9d27d9d2181838f76dee150882bdc58c # Masters of Cinema - # - db9b4c4b53d312a3ca5f1378f6440fc9 # Vinegar Syndrome - # - 957d0f44b592285f26449575e8b1167e # Special Edition - # - eecf3a857724171f968a66cb5719e152 # IMAX - # - 9f6cbff8cfe4ebbc1bde14c7b7bec0de # IMAX Enhanced - assign_scores_to: - - name: UHD Bluray + WEB - - # Optional Unwanted - - trash_ids: - # - b6832f586342ef70d9c128d40c07b872 # Bad Dual Groups - # - cc444569854e9de0b084ab2b8b1532b2 # Black and White Editions - # - ae9b7c9ebde1f3bd336a8cbd1ec4c5e5 # No-RlsGroup - # - 7357cf5161efbf8c4d5d0c30b4815ee2 # Obfuscated - # - 5c44f52a8714fdd79bb4d98e2673be1f # Retags - # - f537cf427b64c38c8e36298f657e4828 # Scene - assign_scores_to: - - name: UHD Bluray + WEB - - # HDR/DV Preferences - - trash_ids: - # Uncomment to allow x265 HD releases with HDR/DV - # - dc98083864ea246d05a42df0d05f81cc # x265 (HD) - # assign_scores_to: - # - name: UHD Bluray + WEB - # score: 0 - # - trash_ids: - # - 839bea857ed2c0a8e084f3cbdbd65ecb # x265 (no HDR/DV) - assign_scores_to: - - name: UHD Bluray + WEB - - - trash_ids: - # Comment out the next line if all your setups are fully DV compatible - - 923b6abef9b17f937fab56cfcf89e1f1 # DV (w/o HDR fallback) - - # HDR10+ Boost - Uncomment if you want to prefer HDR10+ releases - # DV Boost - Uncomment if you want to prefer DV releases - # Uncomment both if you want to prefer both (DV HDR10+) - # - b337d6812e06c200ec9a2d3cfa9d20a7 # DV Boost - # - caa37d0df9c348912df1fb1d88f9273a # HDR10+ Boost - assign_scores_to: - - name: UHD Bluray + WEB - - # SDR Blocking - Only use ONE of the following - - trash_ids: - - 9c38ebb7384dada637be8899efa68e6f # SDR (block ALL SDR releases) - # - 25c12f78430a3a23413652cbd1d48d77 # SDR (no WEBDL) - allow SDR WEB only - assign_scores_to: - - name: UHD Bluray + WEB - -sonarr: - sonarr-instance: - base_url: http://sonarr:8989 - api_key: ${SONARR_API_KEY} - - quality_definition: - type: series - - quality_profiles: - - trash_id: a5ffae4bbda71217e132ce4ed37367c0 # WEB-1080p - reset_unmatched_scores: - enabled: true - - custom_format_groups: - add: - - trash_id: 7a2ba4144a594826d4ac03515b371937 # [Optional] Streaming Services - select_all: true - - custom_formats: - # Optional - - trash_ids: - # - 32b367365729d530ca1c124a0b180c64 # Bad Dual Groups - # - 82d40da2bc6923f41e14394075dd4b03 # No-RlsGroup - # - e1a997ddb54e3ecbfe06341ad323c458 # Obfuscated - # - 06d66ab109d4d2eddb2794d21526d140 # Retags - # - 1b3994c551cbb92a2c781af061f4ab44 # Scene - assign_scores_to: - - name: WEB-1080p - - # x265 HD handling - - trash_ids: - # Uncomment to allow x265 HD releases with HDR/DV - # - 47435ece6b99a0b477caf360e79ba0bb # x265 (HD) - # assign_scores_to: - # - name: WEB-1080p - # score: 0 - # - trash_ids: - # - 9b64dff695c2115facf1b6ea59c9bd07 # x265 (no HDR/DV) - assign_scores_to: - - name: WEB-1080p diff --git a/templates/recyclarr/includes/radarr-hd.yml b/templates/recyclarr/includes/radarr-hd.yml new file mode 100644 index 0000000..1effb02 --- /dev/null +++ b/templates/recyclarr/includes/radarr-hd.yml @@ -0,0 +1,8 @@ +# Include: Radarr 1080p profile (guide-backed HD Bluray + WEB) + +quality_profiles: + - trash_id: d1d67249d3890e49bc12e275d989a7e9 # HD Bluray + WEB + name: assemblrr HD Bluray + WEB + reset_unmatched_scores: + enabled: true + min_format_score: -1000 diff --git a/templates/recyclarr/includes/radarr-soft-cfs.yml b/templates/recyclarr/includes/radarr-soft-cfs.yml new file mode 100644 index 0000000..2e1a5cc --- /dev/null +++ b/templates/recyclarr/includes/radarr-soft-cfs.yml @@ -0,0 +1,14 @@ +# Soften hard-block custom formats on assemblrr Radarr profiles + +custom_formats: + - trash_ids: + - 90a6f9a284dff5103f6346090e6280c8 # LQ + - e204b80c87be9497a8a6eaff48f72905 # LQ (Release Title) + - dc98083864ea246d05a42df0d05f81cc # x265 (HD) + - 839bea857ed2c0a8e084f3cbdbd65ecb # x265 (no HDR/DV) + - b8cd450cbfa689c0259a01d9e29ba3d6 # 3D + assign_scores_to: + - name: assemblrr HD Bluray + WEB + score: 0 + - name: assemblrr UHD Bluray + WEB + score: 0 diff --git a/templates/recyclarr/includes/radarr-uhd.yml b/templates/recyclarr/includes/radarr-uhd.yml new file mode 100644 index 0000000..591349f --- /dev/null +++ b/templates/recyclarr/includes/radarr-uhd.yml @@ -0,0 +1,8 @@ +# Include: Radarr 4K profile (guide-backed UHD Bluray + WEB) + +quality_profiles: + - trash_id: 64fb5f9858489bdac2af690e27c8f42f # UHD Bluray + WEB + name: assemblrr UHD Bluray + WEB + reset_unmatched_scores: + enabled: true + min_format_score: -1000 diff --git a/templates/recyclarr/includes/sonarr-soft-cfs.yml b/templates/recyclarr/includes/sonarr-soft-cfs.yml new file mode 100644 index 0000000..ca5ac60 --- /dev/null +++ b/templates/recyclarr/includes/sonarr-soft-cfs.yml @@ -0,0 +1,14 @@ +# Soften hard-block custom formats on assemblrr Sonarr profiles + +custom_formats: + - trash_ids: + - 9c11cd3f07101cdba90a2d81cf0e56b4 # LQ + - e2315f990da2e2cbfc9fa5b7a6fcfe48 # LQ (Release Title) + - 47435ece6b99a0b477caf360e79ba0bb # x265 (HD) + - 9b64dff695c2115facf1b6ea59c9bd07 # x265 (no HDR/DV) + - c9eafd50846d299b862ca9bb6ea91950 # x265 + assign_scores_to: + - name: assemblrr WEB-1080p + score: 0 + - name: assemblrr WEB-2160p + score: 0 diff --git a/templates/recyclarr/includes/sonarr-web-1080.yml b/templates/recyclarr/includes/sonarr-web-1080.yml new file mode 100644 index 0000000..74c84ed --- /dev/null +++ b/templates/recyclarr/includes/sonarr-web-1080.yml @@ -0,0 +1,8 @@ +# Include: Sonarr 1080p profile (guide-backed WEB-1080p) + +quality_profiles: + - trash_id: 72dae194fc92bf828f32cde7744e51a1 # WEB-1080p + name: assemblrr WEB-1080p + reset_unmatched_scores: + enabled: true + min_format_score: -1000 diff --git a/templates/recyclarr/includes/sonarr-web-2160.yml b/templates/recyclarr/includes/sonarr-web-2160.yml new file mode 100644 index 0000000..6be9d51 --- /dev/null +++ b/templates/recyclarr/includes/sonarr-web-2160.yml @@ -0,0 +1,8 @@ +# Include: Sonarr 4K profile (guide-backed WEB-2160p) + +quality_profiles: + - trash_id: d1498e7d189fbe6c7110ceaabb7473e6 # WEB-2160p + name: assemblrr WEB-2160p + reset_unmatched_scores: + enabled: true + min_format_score: -1000 diff --git a/templates/recyclarr/recyclarr.yml b/templates/recyclarr/recyclarr.yml new file mode 100644 index 0000000..8faae53 --- /dev/null +++ b/templates/recyclarr/recyclarr.yml @@ -0,0 +1,23 @@ +# yaml-language-server: $schema=https://schemas.recyclarr.dev/v8/config-schema.json +# Root: instance URLs/keys + include list. Profile packs are under includes/. +# All packs listed here are synced; Seerr's default is chosen at setup. +# See README "Quality Profiles & TRaSH Guides". + +radarr: + radarr-instance: + base_url: http://radarr:7878 + api_key: ${RADARR_API_KEY} + # No quality_definition → no min/max size filters from Recyclarr. + include: + - config: radarr-hd.yml + - config: radarr-uhd.yml + - config: radarr-soft-cfs.yml + +sonarr: + sonarr-instance: + base_url: http://sonarr:8989 + api_key: ${SONARR_API_KEY} + include: + - config: sonarr-web-1080.yml + - config: sonarr-web-2160.yml + - config: sonarr-soft-cfs.yml diff --git a/tests/integration/watchdog_stall.sh b/tests/integration/watchdog_stall.sh index 28546e8..2f901b8 100755 --- a/tests/integration/watchdog_stall.sh +++ b/tests/integration/watchdog_stall.sh @@ -7,7 +7,7 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # shellcheck disable=SC1091 source "$SCRIPT_DIR/../helpers.sh" -# Ubuntu 22.04 desktop infohash + public trackers +# Ubuntu 22.04 desktop infohash (official FOSS image) TARGET_HASH="3b245504cf5f11bbdbe1201cea6a6bf45aee1bc0" MAGNET="magnet:?xt=urn:btih:${TARGET_HASH}&dn=ubuntu-22.04.4-desktop-amd64.iso&tr=udp%3A%2F%2Ftracker.opentrackr.org%3A1337%2Fannounce&tr=udp%3A%2F%2Ftracker.openbittorrent.com%3A6969%2Fannounce"