nixos/services/bazarr-sync.nix

185 lines
5.7 KiB
Nix
Raw Normal View History

# services/bazarr-sync.nix — Subtitle sync via bazarr-sync.
#
# Two systemd timers:
# bazarr-sync — hourly, targets only movies/shows added in the last
# 2 hours (queries Radarr/Sonarr APIs for recent items)
# bazarr-sync-full — weekly full-library sync (every Sunday 04:00)
#
# Both use the same bazarr-sync container image under podman with --network host
# so it can reach Bazarr on localhost:6767.
{ config, lib, pkgs, ... }:
let
deps = lib.makeBinPath [ pkgs.curl pkgs.jq pkgs.gnused pkgs.gnugrep pkgs.coreutils pkgs.podman pkgs.yq-go ];
# Shared preamble: extract API keys and write config.yaml
preamble = ''
set -euo pipefail
PATH="${deps}:$PATH"
BASE="http://127.0.0.1"
CONFIG_DIR="/var/lib/bazarr-sync"
CONFIG_FILE="$CONFIG_DIR/config.yaml"
# --- Extract API keys (same pattern as arr-interconnect) ---
extract_arr_key() {
if [ -f "$1" ]; then
sed -n 's/.*<ApiKey>\(.*\)<\/ApiKey>.*/\1/p' "$1"
fi
}
SONARR_KEY=$(extract_arr_key "/var/lib/sonarr/config.xml")
RADARR_KEY=$(extract_arr_key "/var/lib/radarr/config.xml")
BAZARR_KEY=""
if [ -f "/var/lib/bazarr/config/config.yaml" ]; then
BAZARR_KEY=$(yq '.auth.apikey' /var/lib/bazarr/config/config.yaml || true)
fi
if [ -z "$BAZARR_KEY" ]; then
echo "ERROR: Could not extract Bazarr API key"
exit 1
fi
# --- Write config.yaml for bazarr-sync ---
cat > "$CONFIG_FILE" <<EOF
address: "127.0.0.1"
port: 6767
protocol: "http"
apitoken: "$BAZARR_KEY"
EOF
IMAGE="ghcr.io/ajmandourah/bazarr-sync:latest"
run_sync() {
podman run --rm --network host \
-v "$CONFIG_FILE":/usr/src/app/config.yaml:ro \
"$IMAGE" bazarr-sync sync "$@"
}
'';
# Hourly: only sync recently added content
recentScript = pkgs.writeShellScript "bazarr-sync-recent" ''
${preamble}
LOOKBACK_HOURS=2
CUTOFF=$(date -u -d "-$LOOKBACK_HOURS hours" +%Y-%m-%dT%H:%M:%SZ)
# --- Query Radarr for recently added movies ---
RADARR_IDS=""
if [ -n "$RADARR_KEY" ]; then
echo "Querying Radarr for movies added since $CUTOFF..."
RADARR_IDS=$(curl -sf -H "X-Api-Key: $RADARR_KEY" \
"$BASE:7878/api/v3/movie" | \
jq -r --arg cutoff "$CUTOFF" \
'[.[] | select(.added >= $cutoff) | .id] | map(tostring) | join(",")') || true
if [ -n "$RADARR_IDS" ]; then
echo " Found recently added movies: $RADARR_IDS"
else
echo " No recently added movies"
fi
fi
# --- Query Sonarr for series with recently imported episodes ---
SONARR_IDS=""
if [ -n "$SONARR_KEY" ]; then
echo "Querying Sonarr for recently imported episodes since $CUTOFF..."
SONARR_IDS=$(curl -sf -H "X-Api-Key: $SONARR_KEY" \
"$BASE:8989/api/v3/history?pageSize=200&sortKey=date&sortDirection=descending" | \
jq -r --arg cutoff "$CUTOFF" \
'[.records[] | select(.date >= $cutoff and .eventType == "downloadFolderImported") | .seriesId] | unique | map(tostring) | join(",")') || true
if [ -n "$SONARR_IDS" ]; then
echo " Found series with recent imports: $SONARR_IDS"
else
echo " No recently imported episodes"
fi
fi
# --- Exit early if nothing to do ---
if [ -z "$RADARR_IDS" ] && [ -z "$SONARR_IDS" ]; then
echo "Nothing new to sync. Exiting."
exit 0
fi
# --- Run bazarr-sync for recent movies ---
if [ -n "$RADARR_IDS" ]; then
echo "Syncing subtitles for movies: $RADARR_IDS"
run_sync movies --radarr-id "$RADARR_IDS" || echo "WARNING: Movie sync failed"
fi
# --- Run bazarr-sync for recent shows ---
if [ -n "$SONARR_IDS" ]; then
echo "Syncing subtitles for shows: $SONARR_IDS"
run_sync shows --sonarr-id "$SONARR_IDS" || echo "WARNING: Show sync failed"
fi
echo "Sync complete."
'';
# Weekly: full library sync
fullScript = pkgs.writeShellScript "bazarr-sync-full" ''
${preamble}
echo "Starting full library subtitle sync..."
echo "Syncing all movies..."
run_sync movies || echo "WARNING: Full movie sync failed"
echo "Syncing all shows..."
run_sync shows || echo "WARNING: Full show sync failed"
echo "Full sync complete."
'';
serviceAfter = [ "bazarr.service" "sonarr.service" "radarr.service" "network-online.target" ];
in
{
config = lib.mkIf (config.networking.hostName == "FredOS-Mediaserver") {
# Persistent directory for the generated config.yaml
systemd.tmpfiles.rules = [
"d /var/lib/bazarr-sync 0700 root root -"
];
# --- Hourly: recent additions only ---
systemd.services.bazarr-sync = {
description = "Sync subtitles for recently added media via bazarr-sync";
after = serviceAfter;
wants = [ "network-online.target" ];
serviceConfig = {
Type = "oneshot";
ExecStart = recentScript;
};
};
systemd.timers.bazarr-sync = {
description = "Run bazarr-sync hourly for recent additions";
wantedBy = [ "timers.target" ];
timerConfig = {
OnCalendar = "hourly";
RandomizedDelaySec = "5m";
Persistent = true;
};
};
# --- Weekly: full library sync ---
systemd.services.bazarr-sync-full = {
description = "Full library subtitle sync via bazarr-sync";
after = serviceAfter;
wants = [ "network-online.target" ];
serviceConfig = {
Type = "oneshot";
ExecStart = fullScript;
};
};
systemd.timers.bazarr-sync-full = {
description = "Run full bazarr-sync weekly (Sunday 04:00)";
wantedBy = [ "timers.target" ];
timerConfig = {
OnCalendar = "Sun *-*-* 04:00:00";
RandomizedDelaySec = "15m";
Persistent = true;
};
};
};
}