arr-interconnect: wire seerr to jellyfin/radarr/sonarr + auto-approve
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
71c9bfac77
commit
384c6918ff
1 changed files with 167 additions and 0 deletions
|
|
@ -38,6 +38,14 @@ let
|
|||
JELLYFIN_KEY=$(sqlite3 /var/lib/jellyfin/data/jellyfin.db "SELECT AccessToken FROM ApiKeys LIMIT 1;" 2>/dev/null || true)
|
||||
fi
|
||||
|
||||
# Seerr generates main.apiKey into settings.json on first boot, before the
|
||||
# setup wizard runs, so the key alone doesn't mean it's configured.
|
||||
SEERR_SETTINGS=/var/lib/jellyseerr/config/settings.json
|
||||
SEERR_KEY=""
|
||||
if [ -f "$SEERR_SETTINGS" ]; then
|
||||
SEERR_KEY=$(jq -r '.main.apiKey // empty' "$SEERR_SETTINGS" 2>/dev/null || true)
|
||||
fi
|
||||
|
||||
# --- Helpers ---
|
||||
wait_for() {
|
||||
local name="$1" url="$2" key="$3"
|
||||
|
|
@ -473,6 +481,163 @@ RUBY
|
|||
fi
|
||||
fi
|
||||
|
||||
##########################################################################
|
||||
# Seerr → Jellyfin / Radarr / Sonarr, plus auto-approve for all users
|
||||
#
|
||||
# Every /api/v1/settings/* route resolves X-Api-Key to user id 1 and 403s
|
||||
# if that row is missing. User 1 is only created when someone completes
|
||||
# the setup wizard by signing in with Jellyfin admin credentials, which we
|
||||
# deliberately don't automate — it would mean parking the Jellyfin admin
|
||||
# password in a runtime secrets file for a job that runs once. So probe
|
||||
# for the admin first and skip the whole block until the wizard is done.
|
||||
##########################################################################
|
||||
if [ -n "$SEERR_KEY" ]; then
|
||||
wait_for "Seerr" "$BASE:5055/api/v1/status" "" || true
|
||||
|
||||
if ! curl -sf -o /dev/null -H "X-Api-Key: $SEERR_KEY" "$BASE:5055/api/v1/settings/main"; then
|
||||
echo "Seerr setup wizard not completed yet (no admin user), skipping Seerr config"
|
||||
else
|
||||
seerr_get() { curl -sf -H "X-Api-Key: $SEERR_KEY" "$BASE:5055/api/v1$1"; }
|
||||
seerr_post() { curl -sf -X POST -H "Content-Type: application/json" \
|
||||
-H "X-Api-Key: $SEERR_KEY" "$BASE:5055/api/v1$1" -d "$2"; }
|
||||
|
||||
# --- Jellyfin: wizard sets ip/port/serverId but leaves apiKey empty,
|
||||
# --- which the library scan and user import both need.
|
||||
if [ -n "$JELLYFIN_KEY" ]; then
|
||||
if [ -z "$(seerr_get /settings/jellyfin | jq -r '.apiKey // empty')" ]; then
|
||||
echo "Configuring Jellyfin in Seerr..."
|
||||
seerr_post /settings/jellyfin "$(jq -n --arg key "$JELLYFIN_KEY" '{
|
||||
ip: "127.0.0.1",
|
||||
port: 8096,
|
||||
useSsl: false,
|
||||
urlBase: "",
|
||||
apiKey: $key
|
||||
}')" > /dev/null && echo " done" || echo " failed"
|
||||
else
|
||||
echo "Seerr → Jellyfin already configured"
|
||||
fi
|
||||
fi
|
||||
|
||||
# --- Radarr. activeProfileId/activeDirectory are required and have no
|
||||
# --- sane default, so pull them from Radarr itself.
|
||||
# ponytail: takes Radarr's first quality profile and root folder; pick
|
||||
# them explicitly here if you ever run more than one of either.
|
||||
if [ -n "$RADARR_KEY" ] && [ "$(seerr_get /settings/radarr | jq 'length')" = "0" ]; then
|
||||
# || true on every assignment: set -e aborts the whole job on a bare
|
||||
# failing substitution, and Radarr being briefly down must not take
|
||||
# the rest of the interconnect with it.
|
||||
R_PROFILE=$(curl -sf -H "X-Api-Key: $RADARR_KEY" "$BASE:7878/api/v3/qualityprofile" | jq '.[0]' || true)
|
||||
R_ROOT=$(curl -sf -H "X-Api-Key: $RADARR_KEY" "$BASE:7878/api/v3/rootfolder" | jq -r '.[0].path' || true)
|
||||
|
||||
if [ -n "$R_PROFILE" ] && [ "$R_PROFILE" != "null" ] && [ -n "$R_ROOT" ]; then
|
||||
echo "Adding Radarr to Seerr..."
|
||||
seerr_post /settings/radarr "$(jq -n \
|
||||
--arg key "$RADARR_KEY" --arg root "$R_ROOT" \
|
||||
--argjson pid "$(echo "$R_PROFILE" | jq '.id')" \
|
||||
--arg pname "$(echo "$R_PROFILE" | jq -r '.name')" '{
|
||||
name: "Radarr",
|
||||
hostname: "localhost",
|
||||
port: 7878,
|
||||
apiKey: $key,
|
||||
useSsl: false,
|
||||
baseUrl: "",
|
||||
activeProfileId: $pid,
|
||||
activeProfileName: $pname,
|
||||
activeDirectory: $root,
|
||||
is4k: false,
|
||||
minimumAvailability: "released",
|
||||
isDefault: true,
|
||||
externalUrl: "https://radarr.nordhammer.it",
|
||||
syncEnabled: true,
|
||||
preventSearch: false,
|
||||
tagRequests: false,
|
||||
tags: []
|
||||
}')" > /dev/null && echo " done" || echo " failed"
|
||||
else
|
||||
echo "Radarr has no quality profile or root folder yet, skipping"
|
||||
fi
|
||||
else
|
||||
echo "Seerr → Radarr already configured, or Radarr key missing"
|
||||
fi
|
||||
|
||||
# --- Sonarr. Anime slots point at the same profile/folder as the
|
||||
# --- regular ones; split them in the UI if you ever separate anime.
|
||||
if [ -n "$SONARR_KEY" ] && [ "$(seerr_get /settings/sonarr | jq 'length')" = "0" ]; then
|
||||
S_PROFILE=$(curl -sf -H "X-Api-Key: $SONARR_KEY" "$BASE:8989/api/v3/qualityprofile" | jq '.[0]' || true)
|
||||
S_ROOT=$(curl -sf -H "X-Api-Key: $SONARR_KEY" "$BASE:8989/api/v3/rootfolder" | jq -r '.[0].path' || true)
|
||||
|
||||
if [ -n "$S_PROFILE" ] && [ "$S_PROFILE" != "null" ] && [ -n "$S_ROOT" ]; then
|
||||
echo "Adding Sonarr to Seerr..."
|
||||
seerr_post /settings/sonarr "$(jq -n \
|
||||
--arg key "$SONARR_KEY" --arg root "$S_ROOT" \
|
||||
--argjson pid "$(echo "$S_PROFILE" | jq '.id')" \
|
||||
--arg pname "$(echo "$S_PROFILE" | jq -r '.name')" '{
|
||||
name: "Sonarr",
|
||||
hostname: "localhost",
|
||||
port: 8989,
|
||||
apiKey: $key,
|
||||
useSsl: false,
|
||||
baseUrl: "",
|
||||
activeProfileId: $pid,
|
||||
activeProfileName: $pname,
|
||||
activeDirectory: $root,
|
||||
activeAnimeProfileId: $pid,
|
||||
activeAnimeProfileName: $pname,
|
||||
activeAnimeDirectory: $root,
|
||||
activeLanguageProfileId: 1,
|
||||
activeAnimeLanguageProfileId: 1,
|
||||
is4k: false,
|
||||
isDefault: true,
|
||||
enableSeasonFolders: true,
|
||||
externalUrl: "https://sonarr.nordhammer.it",
|
||||
syncEnabled: true,
|
||||
preventSearch: false,
|
||||
tagRequests: false,
|
||||
tags: [],
|
||||
animeTags: []
|
||||
}')" > /dev/null && echo " done" || echo " failed"
|
||||
else
|
||||
echo "Sonarr has no quality profile or root folder yet, skipping"
|
||||
fi
|
||||
else
|
||||
echo "Seerr → Sonarr already configured, or Sonarr key missing"
|
||||
fi
|
||||
|
||||
######################################################################
|
||||
# Auto-approve. REQUEST(32)|AUTO_APPROVE(128) = 160. AUTO_APPROVE is
|
||||
# checked with an 'or' against the movie/TV variants, so the single
|
||||
# bit covers both. defaultPermissions only lands on users imported
|
||||
# *after* it's set, so existing ones get a bulk update too.
|
||||
######################################################################
|
||||
AUTO_APPROVE_PERMS=160
|
||||
|
||||
if [ "$(seerr_get /settings/main | jq '.defaultPermissions')" != "$AUTO_APPROVE_PERMS" ]; then
|
||||
echo "Setting Seerr default permissions to request + auto-approve..."
|
||||
seerr_post /settings/main "$(jq -n --argjson p "$AUTO_APPROVE_PERMS" \
|
||||
'{defaultPermissions: $p}')" > /dev/null && echo " done" || echo " failed"
|
||||
else
|
||||
echo "Seerr default permissions already request + auto-approve"
|
||||
fi
|
||||
|
||||
# Admin (id 1) is excluded — it holds ADMIN and would be downgraded.
|
||||
EXISTING_IDS=$(seerr_get "/user?take=1000" \
|
||||
| jq -c --argjson p "$AUTO_APPROVE_PERMS" \
|
||||
'[.results[] | select(.id != 1 and .permissions != $p) | .id]' || true)
|
||||
|
||||
if [ -n "$EXISTING_IDS" ] && [ "$EXISTING_IDS" != "[]" ]; then
|
||||
echo "Granting auto-approve to existing Seerr users: $EXISTING_IDS"
|
||||
curl -sf -X PUT -H "Content-Type: application/json" \
|
||||
-H "X-Api-Key: $SEERR_KEY" "$BASE:5055/api/v1/user" \
|
||||
-d "$(jq -n --argjson ids "$EXISTING_IDS" --argjson p "$AUTO_APPROVE_PERMS" \
|
||||
'{ids: $ids, permissions: $p}')" > /dev/null && echo " done" || echo " failed"
|
||||
else
|
||||
echo "All existing Seerr users already have auto-approve"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
echo "Seerr hasn't written settings.json yet, skipping"
|
||||
fi
|
||||
|
||||
##########################################################################
|
||||
# Prowlarr auth — trust localhost so Authelia is the only gate. Other
|
||||
# *arr apps default to this; Prowlarr does not.
|
||||
|
|
@ -504,6 +669,7 @@ in
|
|||
"qbittorrent-nox.service"
|
||||
"sabnzbd.service"
|
||||
"docker-shelfarr.service"
|
||||
"seerr.service"
|
||||
];
|
||||
wants = [
|
||||
"sonarr.service"
|
||||
|
|
@ -513,6 +679,7 @@ in
|
|||
"qbittorrent-nox.service"
|
||||
"sabnzbd.service"
|
||||
"docker-shelfarr.service"
|
||||
"seerr.service"
|
||||
];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
serviceConfig = {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue