Add write-through cache for per-item track/episode data
lidarr_cleanup.sh and sonarr_cleanup.sh already walk every artist/series individually (trackFile/episodefile) for their own cleanup decisions -- that walk now also writes the raw per-item data through to a short-lived tmpfs cache (arr_item_cache_write, 4h freshness, no persistent backup since every consumer already has its own live fallback). lidarr_missing_art.sh runs later in the same nightly window and now reads that cache first, skipping its own redundant per-artist walk entirely on a hit. Sonarr side is write-through only for now -- no second consumer exists yet, but the data's there for whenever one does. Future consumers: arr_get_cached_items() first, live per-item fetch as fallback, same pattern as these two.
This commit is contained in:
@@ -2387,6 +2387,71 @@ arr_cache_age_seconds() {
|
||||
echo $(( $(date +%s) - ${ts:-0} ))
|
||||
}
|
||||
|
||||
# ==============================================================================================
|
||||
# ── PER-ITEM TRACKED-FILE CACHE (Lidarr trackFile, Sonarr episodefile) ───────────────────────
|
||||
# ==============================================================================================
|
||||
# Different in kind from the tracked-LIBRARY cache above. That one caches the top-level
|
||||
# artist/series/movie list (one call). This caches the much more expensive per-item walk —
|
||||
# one live API call per artist/series — that lidarr_cleanup.sh/sonarr_cleanup.sh already have
|
||||
# to do for their own cleanup decisions regardless. Write-through only, from a walk that's
|
||||
# already happening: no independent live-fetch-and-refresh path here, since the field set
|
||||
# each consumer needs differs (lidarr_cleanup.sh only needs .path; lidarr_missing_art.sh also
|
||||
# needs .albumId) and there's no single generic "fetch everything" call worth centralizing —
|
||||
# each script keeps its own live per-item fallback, this is purely a fast path in front of it.
|
||||
#
|
||||
# Freshness is short (default 4h, not the library cache's 1 day) because this is meant for a
|
||||
# script running shortly after in the same maintenance window (e.g. lidarr_missing_art.sh
|
||||
# right after lidarr_cleanup.sh), not held across a whole day. tmpfs only, no persistent
|
||||
# backup — unlike the library cache, nothing unique lives only here (every consumer already
|
||||
# has its own live fallback), and it's short-lived by design, so surviving a reboot doesn't
|
||||
# matter the way it did for the always-wanted library cache. (2026-07-17)
|
||||
#
|
||||
# Future scripts needing this data: call arr_get_cached_items() first, fall back to your own
|
||||
# live per-item fetch on a miss, and write through via arr_item_cache_write() if you're the
|
||||
# one doing that fetch anyway — same pattern as lidarr_cleanup.sh/sonarr_cleanup.sh below.
|
||||
# ==============================================================================================
|
||||
|
||||
arr_item_cache_file() { echo "${ARR_CACHE_DIR}/${1}_items_cache.json"; }
|
||||
|
||||
# Args: arr_type, items_json (array of raw per-item track/episode-file objects)
|
||||
arr_item_cache_write() {
|
||||
local arr_type="$1" items_json="$2"
|
||||
|
||||
local url_var="${arr_type^^}_URL" key_var="${arr_type^^}_API_KEY"
|
||||
local url="${!url_var:-}" api_key="${!key_var:-}" api_version="${ARR_API_VERSION[$arr_type]:-}"
|
||||
if [[ -n "$url" && -n "$api_key" && -n "$api_version" ]]; then
|
||||
local active_cmd
|
||||
active_cmd=$(arr_active_rescan_command "$arr_type" "$url" "$api_key" "$api_version")
|
||||
[[ -n "$active_cmd" ]] && return 1
|
||||
fi
|
||||
|
||||
local cache_file items_tmp
|
||||
cache_file=$(arr_item_cache_file "$arr_type")
|
||||
items_tmp=$(mktemp)
|
||||
echo "$items_json" > "$items_tmp"
|
||||
mkdir -p "$(dirname "$cache_file")" 2>/dev/null
|
||||
jq -c -n --slurpfile items "$items_tmp" --argjson ts "$(date +%s)" \
|
||||
'{ts:$ts, items:$items[0]}' > "${cache_file}.tmp" 2>/dev/null \
|
||||
&& mv "${cache_file}.tmp" "$cache_file"
|
||||
rm -f "$items_tmp"
|
||||
}
|
||||
|
||||
# Echoes the cached per-item array if present and fresher than max_age_seconds, empty/1 on a
|
||||
# miss or stale cache — caller falls back to its own live per-item fetch exactly as it already
|
||||
# does today.
|
||||
# Args: arr_type, max_age_seconds (default 14400 = 4h)
|
||||
arr_get_cached_items() {
|
||||
local arr_type="$1" max_age="${2:-14400}"
|
||||
local cache_file
|
||||
cache_file=$(arr_item_cache_file "$arr_type")
|
||||
[[ -f "$cache_file" ]] || return 1
|
||||
local ts age
|
||||
ts=$(jq -r '.ts // 0' "$cache_file" 2>/dev/null)
|
||||
age=$(( $(date +%s) - ${ts:-0} ))
|
||||
[[ "$age" -ge "$max_age" ]] && return 1
|
||||
jq -c '.items // empty' "$cache_file" 2>/dev/null
|
||||
}
|
||||
|
||||
# Records how long a rescan-type command actually took for arr_type, keyed by command name,
|
||||
# so future waits can be calibrated per command type instead of guessed or blended across
|
||||
# very different operations — a whole-library RescanFolders/RescanSeries/RescanMovie takes
|
||||
|
||||
Reference in New Issue
Block a user