Comment-only. Headers on the scripts touched during today's caching work (cache-first fetches, write-through per-item cache, single-walk consolidation, movieFile-embedded fix) still described pre-change behavior. Also brought common.sh's top-level cache doc block current -- it was written for the single-consumer 2026-07-16 state and didn't mention the tmpfs move, the write guard, or the 15+ consumers that now go through it.
494 lines
21 KiB
Bash
Executable File
494 lines
21 KiB
Bash
Executable File
#!/bin/bash
|
|
# ==============================================================================================
|
|
# ============================= Lidarr Release Fixer ===========================================
|
|
# ==============================================================================================
|
|
#
|
|
# PURPOSE
|
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
# Fix Lidarr albums where the wrong MusicBrainz release edition was selected,
|
|
# causing on-disk files to appear as unimported despite being present.
|
|
#
|
|
# Root cause: Lidarr tracks one specific release edition per album using
|
|
# foreignReleaseId. When this doesn't match the MUSICBRAINZ_ALBUMID embedded
|
|
# in the actual files, track ID lookup fails and RescanFolders imports 0 tracks
|
|
# even with perfect, fully tagged files.
|
|
#
|
|
# Fix: Read the MusicBrainz Album ID from the first FLAC or MP3 found in each
|
|
# album directory, look that release up in Lidarr's known releases for that
|
|
# album, switch monitored=true to the correct one, then queue a RefreshArtist
|
|
# to re-sync track IDs and trigger Lidarr's own import scan.
|
|
#
|
|
# ==============================================================================================
|
|
# OPERATIONAL MODEL
|
|
# ==============================================================================================
|
|
#
|
|
# For each monitored album with 0 tracked files:
|
|
# 1. Locate the album directory under the artist's root path (title glob match)
|
|
# 2. Find the first FLAC or MP3 file in that directory
|
|
# 3. Read MUSICBRAINZ_ALBUMID from the file's tags
|
|
# 4. Fetch the album's available releases from Lidarr
|
|
# 5. If the file's release exists and differs from Lidarr's current selection:
|
|
# — PUT the album with the correct release set to monitored=true
|
|
# — Mark the artist for a RefreshArtist command
|
|
#
|
|
# RefreshArtist is batched — one per artist, even if multiple albums were fixed.
|
|
# Lidarr handles the post-refresh rescan and import automatically.
|
|
#
|
|
# Cache-first artist list (2026-07-17) — comes from the shared tracked-data cache via
|
|
# arr_get_tracked_data(), fresh (kept warm every 30min by arr_cache_prefill.sh), live fetch
|
|
# as fallback. Everything below (per-album release lookups) still fetches live — that data
|
|
# isn't part of what's cached.
|
|
#
|
|
# ==============================================================================================
|
|
# DESIGN PRINCIPLES
|
|
# ==============================================================================================
|
|
#
|
|
# Fixer Before Cleanup
|
|
# Runs before lidarr_cleanup.sh in the daily job list. The strike system in
|
|
# cleanup provides a protection window, but correcting releases first means
|
|
# files that were wrongly treated as orphans get imported rather than aged out.
|
|
#
|
|
# No Blind Fixes
|
|
# Only switches to a release that is already in Lidarr's known release list for
|
|
# that album. If the file's MBID isn't a recognized release, the album is skipped
|
|
# rather than guessed at. False corrections are worse than leaving it alone.
|
|
#
|
|
# Files Are Never Touched
|
|
# This script only modifies the Lidarr database record (release selection). Audio
|
|
# files, tags, and directory structure are read-only. All actual importing is
|
|
# handled by Lidarr after RefreshArtist runs.
|
|
#
|
|
# ==============================================================================================
|
|
# OPERATIONAL SAFEGUARDS
|
|
# ==============================================================================================
|
|
#
|
|
# check_api — API reachability verified before any processing
|
|
# check_arr_version — aborts if Lidarr major version doesn't match LIDARR_VERSION_MAJOR
|
|
# artist count > 0 — aborts if artist fetch returns empty (API anomaly guard)
|
|
# release list validation — only fixes to releases explicitly known to Lidarr for that album
|
|
# acquire_lock "skip" — skips if another instance is already running
|
|
# detect_hosts() — exits cleanly on hosts without Lidarr configured
|
|
# curl + jq + perl check — fail fast if any required tool is missing
|
|
#
|
|
# ==============================================================================================
|
|
# TAG READING
|
|
# ==============================================================================================
|
|
#
|
|
# FLAC — Vorbis comment block (block type 4), key MUSICBRAINZ_ALBUMID
|
|
# MP3 — ID3v2 TXXX frame, description "MusicBrainz Album Id"
|
|
# Supports Latin-1, UTF-8 (enc 0/3) and UTF-16 (enc 1/2) encodings
|
|
#
|
|
# ==============================================================================================
|
|
# CONFIGURATION
|
|
# ==============================================================================================
|
|
#
|
|
# host*.conf
|
|
#
|
|
# HOST1_LIDARR_URL / HOST1_LIDARR_API_KEY / HOST1_LIDARR_MUSIC_ROOT
|
|
# HOST1_LIDARR_PATH_MAP — container path → host path translation
|
|
# All aliased by detect_hosts() — script uses unprefixed names
|
|
#
|
|
# master.conf
|
|
#
|
|
# LIDARR_RELEASE_FIXER_ENABLED — set false to disable without removing from job list
|
|
# LIDARR_VERSION_MAJOR — expected Lidarr major version for API safety check
|
|
#
|
|
# ==============================================================================================
|
|
# RUNTIME MODES
|
|
# ==============================================================================================
|
|
#
|
|
# lidarr_release_fixer.sh — normal run
|
|
# lidarr_release_fixer.sh --dry-run — preview, no API writes
|
|
# lidarr_release_fixer.sh --log — verbose output
|
|
# lidarr_release_fixer.sh --status — show config and exit
|
|
#
|
|
# ==============================================================================================
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
source "$SCRIPT_DIR/../load_config.sh"
|
|
parse_args "$@"
|
|
|
|
# ── Setup ─────────────────────────────────────────────────────────────────────────────────────
|
|
if [[ "$EUID" -ne 0 ]]; then
|
|
error "Must be run as root"
|
|
exit 1
|
|
fi
|
|
|
|
for _cmd in curl jq perl; do
|
|
if ! command -v "$_cmd" >/dev/null 2>&1; then
|
|
error "$_cmd not found — required"
|
|
exit 1
|
|
fi
|
|
done
|
|
unset _cmd
|
|
|
|
acquire_lock "skip"
|
|
trap "_release_all_locks" EXIT
|
|
|
|
detect_hosts
|
|
|
|
if [[ -z "${LIDARR_URL:-}" ]] || [[ -z "${LIDARR_API_KEY:-}" ]]; then
|
|
info "Lidarr not configured on $MY_ID — skipping"
|
|
exit 0
|
|
fi
|
|
|
|
if [[ "${LIDARR_RELEASE_FIXER_ENABLED:-true}" == "false" ]]; then
|
|
info "LIDARR_RELEASE_FIXER_ENABLED=false — skipping"
|
|
exit 0
|
|
fi
|
|
|
|
declare -A ARR_PATH_MAP
|
|
local_path_map_var="${MY_ID}_LIDARR_PATH_MAP"
|
|
eval "for key in \"\${!${local_path_map_var}[@]}\"; do
|
|
ARR_PATH_MAP[\"\$key\"]=\"\${${local_path_map_var}[\$key]}\"
|
|
done"
|
|
|
|
require_var LIDARR_URL
|
|
require_var LIDARR_API_KEY
|
|
require_var LIDARR_MUSIC_ROOT
|
|
|
|
# ── Status ────────────────────────────────────────────────────────────────────────────────────
|
|
if [[ "$SHOW_STATUS" == true ]]; then
|
|
echo ""
|
|
echo "━━━━━ $ICON_SUMMARY STATUS ━━━━━"
|
|
echo "$ICON_HOST Identity: $MY_ID ($LOCAL_SERVER_NAME)"
|
|
echo "$ICON_GEAR Lidarr URL: $LIDARR_URL"
|
|
echo "$ICON_GEAR Music root: $LIDARR_MUSIC_ROOT"
|
|
echo "$ICON_GEAR Enabled: ${LIDARR_RELEASE_FIXER_ENABLED:-true}"
|
|
echo "$ICON_GEAR Dry run: $DRY_RUN"
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━"
|
|
exit 0
|
|
fi
|
|
|
|
# ── API helpers ───────────────────────────────────────────────────────────────────────────────
|
|
lidarr_api() {
|
|
local endpoint="$1"
|
|
local response http_code body
|
|
response=$(curl -sf --max-time 30 \
|
|
-H "X-Api-Key: $LIDARR_API_KEY" \
|
|
-w "\n%{http_code}" \
|
|
"${LIDARR_URL}/api/v1/${endpoint}" 2>/dev/null)
|
|
http_code=$(echo "$response" | tail -1)
|
|
body=$(echo "$response" | head -n -1)
|
|
if [[ "$http_code" != "200" ]]; then
|
|
error "Lidarr API HTTP $http_code for: $endpoint"
|
|
return 1
|
|
fi
|
|
echo "$body"
|
|
}
|
|
|
|
lidarr_api_put() {
|
|
local endpoint="$1"
|
|
local payload="$2"
|
|
local http_code
|
|
http_code=$(curl -sf --max-time 30 -X PUT \
|
|
-H "X-Api-Key: $LIDARR_API_KEY" \
|
|
-H "Content-Type: application/json" \
|
|
-d "$payload" \
|
|
-w "%{http_code}" -o /dev/null \
|
|
"${LIDARR_URL}/api/v1/${endpoint}" 2>/dev/null)
|
|
if [[ "$http_code" != "202" ]] && [[ "$http_code" != "200" ]]; then
|
|
error "Lidarr PUT HTTP $http_code for: $endpoint"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
lidarr_api_post() {
|
|
local endpoint="$1"
|
|
local payload="$2"
|
|
curl -sf --max-time 30 -X POST \
|
|
-H "X-Api-Key: $LIDARR_API_KEY" \
|
|
-H "Content-Type: application/json" \
|
|
-d "$payload" \
|
|
-o /dev/null \
|
|
"${LIDARR_URL}/api/v1/${endpoint}" 2>/dev/null
|
|
}
|
|
|
|
# ── Tag readers ───────────────────────────────────────────────────────────────────────────────
|
|
# Read MUSICBRAINZ_ALBUMID from a FLAC file's Vorbis comment block (block type 4)
|
|
read_flac_mbid() {
|
|
perl -e '
|
|
open(my $fh, "<:raw", $ARGV[0]) or exit;
|
|
read($fh, my $magic, 4); substr($magic, 0, 4) eq "fLaC" or exit;
|
|
while (1) {
|
|
read($fh, my $hdr, 4) == 4 or last;
|
|
my $w = unpack("N", $hdr);
|
|
my $last = ($w >> 31) & 1;
|
|
my $type = ($w >> 24) & 0x7f;
|
|
my $len = $w & 0xffffff;
|
|
read($fh, my $data, $len);
|
|
if ($type == 4) {
|
|
my $pos = 0;
|
|
my $vl = unpack("V", substr($data, $pos, 4)); $pos += 4 + $vl;
|
|
my $n = unpack("V", substr($data, $pos, 4)); $pos += 4;
|
|
for (1..$n) {
|
|
my $cl = unpack("V", substr($data, $pos, 4)); $pos += 4;
|
|
my $c = substr($data, $pos, $cl); $pos += $cl;
|
|
if ($c =~ /^MUSICBRAINZ_ALBUMID=(.+)$/i) { print "$1\n"; exit; }
|
|
}
|
|
}
|
|
last if $last;
|
|
}
|
|
' "$1" 2>/dev/null
|
|
}
|
|
|
|
# Read MusicBrainz Album Id from an MP3's ID3v2 TXXX frame.
|
|
# Handles Latin-1/UTF-8 (enc 0/3) with single-null separator and
|
|
# UTF-16 (enc 1/2) by stripping null bytes and pattern-matching.
|
|
read_mp3_mbid() {
|
|
perl -e '
|
|
open(my $fh, "<:raw", $ARGV[0]) or exit;
|
|
read($fh, my $hdr, 10) == 10 or exit;
|
|
substr($hdr, 0, 3) eq "ID3" or exit;
|
|
my $ver = ord(substr($hdr, 3, 1));
|
|
my $sz = 0; $sz = ($sz << 7) | ord($_) for split //, substr($hdr, 6, 4);
|
|
read($fh, my $data, $sz) == $sz or exit;
|
|
my $pos = 0;
|
|
while ($pos + 10 <= $sz) {
|
|
my $id = substr($data, $pos, 4); $pos += 4;
|
|
last unless $id =~ /^[A-Z][A-Z0-9]{3}$/;
|
|
my $fs;
|
|
if ($ver >= 4) {
|
|
my $n = 0; $n = ($n << 7) | ord($_) for split //, substr($data, $pos, 4);
|
|
$fs = $n;
|
|
} else {
|
|
$fs = unpack("N", substr($data, $pos, 4));
|
|
}
|
|
$pos += 6;
|
|
last if $fs < 1 || $pos + $fs > $sz;
|
|
if ($id eq "TXXX") {
|
|
my $enc = ord(substr($data, $pos, 1));
|
|
my $body = substr($data, $pos + 1, $fs - 1);
|
|
if ($enc == 1 || $enc == 2) {
|
|
# UTF-16: strip BOM, collapse to ASCII, pattern match
|
|
$body =~ s/^\xff\xfe|^\xfe\xff//;
|
|
(my $flat = $body) =~ s/\x00//g;
|
|
if ($flat =~ /^MusicBrainz Album Id([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/i) {
|
|
print "$1\n"; exit;
|
|
}
|
|
} else {
|
|
my ($desc, $val) = split /\x00/, $body, 2;
|
|
if (defined $desc && lc($desc) eq "musicbrainz album id" &&
|
|
defined $val &&
|
|
$val =~ /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i) {
|
|
print "$val\n"; exit;
|
|
}
|
|
}
|
|
}
|
|
$pos += $fs;
|
|
}
|
|
' "$1" 2>/dev/null
|
|
}
|
|
|
|
read_file_mbid() {
|
|
local file="$1"
|
|
case "${file##*.}" in
|
|
[Ff][Ll][Aa][Cc]) read_flac_mbid "$file" ;;
|
|
[Mm][Pp]3) read_mp3_mbid "$file" ;;
|
|
esac
|
|
}
|
|
|
|
# ── Safety checks ─────────────────────────────────────────────────────────────────────────────
|
|
echo ""
|
|
echo "━━━ $ICON_SHIELD Safety Checks ━━━"
|
|
|
|
if ! check_api "$LIDARR_URL" "Lidarr" 10; then
|
|
notify "Lidarr release fixer aborted on $(hostname) — API unreachable" \
|
|
"Lidarr Release Fixer" "warning"
|
|
exit 1
|
|
fi
|
|
check_arr_version "$LIDARR_URL" "$LIDARR_API_KEY" "v1" "$LIDARR_VERSION_MAJOR" "Lidarr" || exit 1
|
|
info "API reachable and version OK"
|
|
|
|
# ── Fetch all artists and build path cache ────────────────────────────────────────────────────
|
|
echo ""
|
|
echo "━━━ $ICON_SYNC Fetching artist paths ━━━"
|
|
|
|
declare -A ARTIST_PATH_CACHE
|
|
declare -A ARTIST_NAME_CACHE
|
|
|
|
# Cache-first — arr_get_tracked_data() serves the shared cache when it's fresh (now kept
|
|
# current every 30min by arr_cache_prefill.sh in CRITICAL_MAINTENANCE_SCRIPTS), falls back to
|
|
# a live fetch when it's stale, and waits out an active rescan before either.
|
|
ALL_ARTISTS=$(arr_get_tracked_data "lidarr" "$LIDARR_URL" "$LIDARR_API_KEY" "v1") || {
|
|
error "Failed to fetch artists"
|
|
exit 1
|
|
}
|
|
|
|
while IFS= read -r artist; do
|
|
aid=$(echo "$artist" | jq -r '.id')
|
|
apath=$(echo "$artist" | jq -r '.path // empty')
|
|
aname=$(echo "$artist" | jq -r '.artistName // empty')
|
|
[[ -n "$apath" ]] && ARTIST_PATH_CACHE[$aid]=$(translate_path "$apath")
|
|
[[ -n "$aname" ]] && ARTIST_NAME_CACHE[$aid]="$aname"
|
|
done < <(echo "$ALL_ARTISTS" | jq -c '.[]' 2>/dev/null)
|
|
unset ALL_ARTISTS
|
|
|
|
ARTIST_COUNT="${#ARTIST_PATH_CACHE[@]}"
|
|
info "Loaded $ARTIST_COUNT artists"
|
|
|
|
if [[ "$ARTIST_COUNT" -eq 0 ]]; then
|
|
error "No artists returned — aborting"
|
|
exit 1
|
|
fi
|
|
|
|
# ── Fetch zero-file albums ────────────────────────────────────────────────────────────────────
|
|
echo ""
|
|
echo "━━━ $ICON_SYNC Fetching zero-file monitored albums ━━━"
|
|
|
|
ALL_ALBUMS=$(lidarr_api "album") || {
|
|
error "Failed to fetch albums"
|
|
exit 1
|
|
}
|
|
|
|
ZERO_FILE_ALBUMS=$(echo "$ALL_ALBUMS" | jq -c \
|
|
'[.[] | select(.monitored == true and .statistics.trackFileCount == 0)]' 2>/dev/null)
|
|
unset ALL_ALBUMS
|
|
|
|
TOTAL_ZERO=$(echo "$ZERO_FILE_ALBUMS" | jq 'length' 2>/dev/null)
|
|
info "Monitored albums with 0 tracked files: $TOTAL_ZERO"
|
|
|
|
# ── Process albums ────────────────────────────────────────────────────────────────────────────
|
|
START=$(date +%s)
|
|
FIXED=0
|
|
SKIPPED_NO_DIR=0
|
|
SKIPPED_NO_FILE=0
|
|
SKIPPED_NO_MBID=0
|
|
SKIPPED_NO_MATCH=0
|
|
SKIPPED_CORRECT=0
|
|
ERRORS=0
|
|
declare -A ARTISTS_TO_REFRESH
|
|
|
|
echo ""
|
|
echo "━━━ $ICON_GEAR Processing albums ━━━"
|
|
|
|
while IFS= read -r album; do
|
|
album_id=$(echo "$album" | jq -r '.id')
|
|
album_title=$(echo "$album" | jq -r '.title')
|
|
artist_id=$(echo "$album" | jq -r '.artistId')
|
|
artist_name="${ARTIST_NAME_CACHE[$artist_id]:-artist $artist_id}"
|
|
|
|
log "Checking: $artist_name — $album_title (album $album_id)"
|
|
|
|
artist_host_path="${ARTIST_PATH_CACHE[$artist_id]:-}"
|
|
if [[ -z "$artist_host_path" ]] || [[ ! -d "$artist_host_path" ]]; then
|
|
log "Artist dir not found: $artist_host_path"
|
|
(( SKIPPED_NO_DIR++ ))
|
|
continue
|
|
fi
|
|
|
|
# Find album directory — case-insensitive prefix match on title
|
|
album_dir=$(find "$artist_host_path" -maxdepth 1 -type d -iname "${album_title}*" \
|
|
2>/dev/null | head -1)
|
|
if [[ -z "$album_dir" ]]; then
|
|
log "Album dir not found: $album_title"
|
|
(( SKIPPED_NO_DIR++ ))
|
|
continue
|
|
fi
|
|
|
|
# Find first FLAC or MP3
|
|
music_file=$(find "$album_dir" -maxdepth 2 -type f \
|
|
\( -iname "*.flac" -o -iname "*.mp3" \) 2>/dev/null | head -1)
|
|
if [[ -z "$music_file" ]]; then
|
|
log "No FLAC or MP3 in: $album_dir"
|
|
(( SKIPPED_NO_FILE++ ))
|
|
continue
|
|
fi
|
|
|
|
# Read MBID from file tags
|
|
file_mbid=$(read_file_mbid "$music_file")
|
|
if [[ -z "$file_mbid" ]]; then
|
|
log "No MBID tag in: $music_file"
|
|
(( SKIPPED_NO_MBID++ ))
|
|
continue
|
|
fi
|
|
log "File MBID: $file_mbid"
|
|
|
|
# Fetch full album JSON (includes releases array)
|
|
album_full=$(lidarr_api "album/${album_id}") || { (( ERRORS++ )); sleep 0.2; continue; }
|
|
sleep 0.1
|
|
|
|
# Currently selected release
|
|
current_release_id=$(echo "$album_full" | jq -r \
|
|
'.releases[] | select(.monitored == true) | .foreignReleaseId' 2>/dev/null | head -1)
|
|
|
|
if [[ "$current_release_id" == "$file_mbid" ]]; then
|
|
log "Release already correct: $file_mbid"
|
|
(( SKIPPED_CORRECT++ ))
|
|
continue
|
|
fi
|
|
|
|
# Verify file's MBID is a known release for this album
|
|
release_known=$(echo "$album_full" | jq -r \
|
|
--arg rid "$file_mbid" \
|
|
'.releases[] | select(.foreignReleaseId == $rid) | .foreignReleaseId' 2>/dev/null)
|
|
if [[ -z "$release_known" ]]; then
|
|
log "File MBID $file_mbid not in Lidarr release list for: $album_title"
|
|
(( SKIPPED_NO_MATCH++ ))
|
|
continue
|
|
fi
|
|
|
|
# Build updated album object with correct release selected
|
|
updated_album=$(echo "$album_full" | jq \
|
|
--arg target "$file_mbid" \
|
|
'.releases = [.releases[] | .monitored = (.foreignReleaseId == $target)]' 2>/dev/null)
|
|
|
|
if [[ "$DRY_RUN" == true ]]; then
|
|
warn "DRY RUN — would fix: $artist_name — $album_title"
|
|
warn " $current_release_id → $file_mbid"
|
|
(( FIXED++ ))
|
|
continue
|
|
fi
|
|
|
|
if lidarr_api_put "album/${album_id}" "$updated_album"; then
|
|
warn "$ICON_GEAR Fixed: $artist_name — $album_title"
|
|
warn " $current_release_id → $file_mbid"
|
|
ARTISTS_TO_REFRESH[$artist_id]="$artist_id"
|
|
(( FIXED++ ))
|
|
else
|
|
(( ERRORS++ ))
|
|
fi
|
|
sleep 0.2
|
|
|
|
done < <(echo "$ZERO_FILE_ALBUMS" | jq -c '.[]')
|
|
|
|
# ── Queue RefreshArtist for all fixed artists ─────────────────────────────────────────────────
|
|
if [[ "${#ARTISTS_TO_REFRESH[@]}" -gt 0 ]] && [[ "$DRY_RUN" == false ]]; then
|
|
echo ""
|
|
echo "━━━ $ICON_SYNC Queuing RefreshArtist ━━━"
|
|
for artist_id in "${!ARTISTS_TO_REFRESH[@]}"; do
|
|
if lidarr_api_post "command" \
|
|
"{\"name\": \"RefreshArtist\", \"artistId\": ${artist_id}}"; then
|
|
log "Queued RefreshArtist for: ${ARTIST_NAME_CACHE[$artist_id]:-$artist_id}"
|
|
else
|
|
error "Failed to queue RefreshArtist for artist $artist_id"
|
|
fi
|
|
sleep 0.1
|
|
done
|
|
info "Refresh queued for ${#ARTISTS_TO_REFRESH[@]} artist(s)"
|
|
fi
|
|
|
|
# ── Summary ───────────────────────────────────────────────────────────────────────────────────
|
|
END=$(date +%s)
|
|
echo ""
|
|
echo "━━━━━ $ICON_SUMMARY LIDARR RELEASE FIXER SUMMARY ━━━━━"
|
|
echo "$ICON_HOST Identity: $MY_ID ($LOCAL_SERVER_NAME)"
|
|
echo "$ICON_GEAR Candidates: $TOTAL_ZERO zero-file albums"
|
|
echo "$ICON_DONE Fixed: $FIXED"
|
|
echo "$ICON_SKIP Already correct: $SKIPPED_CORRECT"
|
|
echo "$ICON_SKIP No dir on disk: $SKIPPED_NO_DIR"
|
|
echo "$ICON_SKIP No music file: $SKIPPED_NO_FILE"
|
|
echo "$ICON_SKIP No MBID tag: $SKIPPED_NO_MBID"
|
|
echo "$ICON_SKIP MBID not in list: $SKIPPED_NO_MATCH"
|
|
[[ "$ERRORS" -gt 0 ]] && echo " Errors: $ERRORS"
|
|
echo "$ICON_TIME Duration: $(format_duration $(( END - START )))"
|
|
[[ "$DRY_RUN" == true ]] && warn "DRY RUN — no changes made"
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
|
|
if [[ "$FIXED" -gt 0 ]] && [[ "$DRY_RUN" == false ]]; then
|
|
notify "Lidarr release fixer on $(hostname) — corrected $FIXED album release(s)" \
|
|
"Lidarr Release Fixer" "normal"
|
|
fi
|
|
|
|
exit 0
|