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"