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:
Gmer4Lfe
2026-07-17 00:55:14 -04:00
parent a018245f40
commit de2879bdee
4 changed files with 108 additions and 6 deletions
+11
View File
@@ -329,12 +329,20 @@ TRACKED_FILE="$TMP_DIR/tracked_paths.txt"
# Fetches every artist's track-file paths fresh into TRACKED_FILE/TRACKED_MAP/TRACKED_COUNT.
# Pulled into a function so the rescan-aware retry below can re-fetch after waiting without
# duplicating this whole loop inline.
#
# Write-through — also caches the raw per-track data via arr_item_cache_write() (2026-07-17)
# so scripts running later in the same maintenance window (lidarr_missing_art.sh) can read it
# instead of repeating this same per-artist walk. This walk is happening regardless for our
# own cleanup decisions; the cache write is free by comparison. See common.sh for the pattern.
_fetch_tracked_files() {
> "$TRACKED_FILE"
local all_tracks_tmp
all_tracks_tmp=$(mktemp)
while IFS= read -r artist_id; do
[[ -z "$artist_id" ]] && continue
ARTIST_TRACKS=$(arr_api "$LIDARR_URL" "$LIDARR_API_KEY" "v1" "trackFile?artistId=${artist_id}" "Lidarr" 2>/dev/null)
if [[ -n "$ARTIST_TRACKS" ]]; then
echo "$ARTIST_TRACKS" >> "$all_tracks_tmp"
while IFS= read -r api_path; do
[[ -z "$api_path" ]] && continue
translate_path "$api_path" >> "$TRACKED_FILE"
@@ -342,6 +350,9 @@ _fetch_tracked_files() {
fi
done <<< "$ARTIST_IDS"
arr_item_cache_write "lidarr" "$(jq -s 'add // []' "$all_tracks_tmp" 2>/dev/null)"
rm -f "$all_tracks_tmp"
sort -u "$TRACKED_FILE" -o "$TRACKED_FILE"
# Build in-memory lookup map — O(1) per lookup vs O(n) grep per file
+20 -6
View File
@@ -261,15 +261,29 @@ _artist_list=$(arr_get_tracked_data "lidarr" "$LIDARR_URL" "$LIDARR_API_KEY" "v1
_map_artist_count=$(echo "$_artist_list" | jq '. | length')
info "Fetching track files for $_map_artist_count artists..."
while IFS= read -r _artist_id; do
[[ -z "$_artist_id" ]] && continue
# Cache-first for the per-track data too — lidarr_cleanup.sh (runs earlier in the same nightly
# window) already does this exact per-artist walk for its own cleanup decisions and writes the
# result through via arr_item_cache_write(). Read that instead of repeating the walk; fall
# back to the live per-artist walk below only if it's missing or from outside this window.
# See arr_item_cache_write()/arr_get_cached_items() in common.sh. (2026-07-17)
_cached_tracks=$(arr_get_cached_items "lidarr")
if [[ -n "$_cached_tracks" ]]; then
info "Using cached track data from lidarr_cleanup.sh — skipping live per-artist walk"
while IFS=$'\t' read -r _album_id _track_path; do
[[ -z "$_album_id" || -z "$_track_path" || "$_track_path" == "null" ]] && continue
ALBUM_DIR_MAP["$_album_id"]=$(dirname "$_track_path")
done < <(curl_json "$LIDARR_URL/api/v1/trackFile?artistId=${_artist_id}&apikey=$LIDARR_API_KEY" | \
jq -r '.[] | [(.albumId | tostring), .path] | @tsv' 2>/dev/null)
done < <(echo "$_artist_list" | jq -r '.[].id')
unset _artist_list _map_artist_count _artist_id _album_id _track_path
done < <(echo "$_cached_tracks" | jq -r '.[] | [(.albumId | tostring), .path] | @tsv' 2>/dev/null)
else
while IFS= read -r _artist_id; do
[[ -z "$_artist_id" ]] && continue
while IFS=$'\t' read -r _album_id _track_path; do
[[ -z "$_album_id" || -z "$_track_path" || "$_track_path" == "null" ]] && continue
ALBUM_DIR_MAP["$_album_id"]=$(dirname "$_track_path")
done < <(curl_json "$LIDARR_URL/api/v1/trackFile?artistId=${_artist_id}&apikey=$LIDARR_API_KEY" | \
jq -r '.[] | [(.albumId | tostring), .path] | @tsv' 2>/dev/null)
done < <(echo "$_artist_list" | jq -r '.[].id')
fi
unset _artist_list _map_artist_count _artist_id _album_id _track_path _cached_tracks
info "Mapped ${#ALBUM_DIR_MAP[@]} albums with local tracks"
+12
View File
@@ -310,9 +310,17 @@ TRACKED_FILE="$TMP_DIR/tracked_paths.txt"
# Fetches every series' episode-file paths fresh into TRACKED_FILE/TRACKED_MAP/TRACKED_COUNT.
# Pulled into a function so the rescan-aware retry below can re-fetch after waiting without
# duplicating this whole loop inline.
#
# Write-through — also caches the raw per-episode data via arr_item_cache_write() (2026-07-17)
# for any future script that needs Sonarr's per-episode file data — none exist yet (unlike
# Lidarr, where lidarr_missing_art.sh already reads this), but the walk is happening regardless
# for our own cleanup decisions, so the cache write is free by comparison. Future consumers:
# call arr_get_cached_items("sonarr") first, fall back to your own live walk on a miss.
_fetch_tracked_files() {
> "$TRACKED_FILE"
local _series_index=0
local all_episodes_tmp
all_episodes_tmp=$(mktemp)
while IFS= read -r series_id; do
[[ -z "$series_id" ]] && continue
(( _series_index++ ))
@@ -320,6 +328,7 @@ _fetch_tracked_files() {
log "Fetching files: $_series_index/$SERIES_COUNT series..."
SERIES_FILES=$(arr_api "$SONARR_URL" "$SONARR_API_KEY" "v3" "episodefile?seriesId=${series_id}" "Sonarr" 2>/dev/null)
if [[ -n "$SERIES_FILES" ]]; then
echo "$SERIES_FILES" >> "$all_episodes_tmp"
while IFS= read -r api_path; do
[[ -z "$api_path" ]] && continue
translate_path "$api_path" >> "$TRACKED_FILE"
@@ -327,6 +336,9 @@ _fetch_tracked_files() {
fi
done <<< "$SERIES_IDS"
arr_item_cache_write "sonarr" "$(jq -s 'add // []' "$all_episodes_tmp" 2>/dev/null)"
rm -f "$all_episodes_tmp"
sort -u "$TRACKED_FILE" -o "$TRACKED_FILE"
# Build in-memory lookup map — O(1) per lookup vs O(n) grep per file