Add lidarr_release_fixer.sh — daily fix for wrong MusicBrainz release editions
Reads MUSICBRAINZ_ALBUMID from FLAC (vorbis block type 4) and MP3 (ID3v2 TXXX) files, matches against Lidarr's known releases, switches monitored=true to the correct one, and queues RefreshArtist. Runs before lidarr_cleanup.sh in the daily job list so the strike system doesn't act on files that just needed a release correction.
This commit is contained in:
Executable
+462
@@ -0,0 +1,462 @@
|
||||
#!/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.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# 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
|
||||
#
|
||||
# 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
|
||||
}
|
||||
|
||||
translate_container_path() {
|
||||
local cpath="$1"
|
||||
for cp in "${!ARR_PATH_MAP[@]}"; do
|
||||
if [[ "$cpath" == "${cp}"* ]]; then
|
||||
echo "${ARR_PATH_MAP[$cp]}${cpath#$cp}"
|
||||
return
|
||||
fi
|
||||
done
|
||||
echo "$cpath"
|
||||
}
|
||||
|
||||
# ── 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
|
||||
|
||||
ALL_ARTISTS=$(lidarr_api "artist") || {
|
||||
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_container_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
|
||||
Reference in New Issue
Block a user