Files
Musicbrainz-Picard/unRAID Userscript/music_sync.sh
2026-04-03 13:46:40 +02:00

221 lines
4.8 KiB
Bash

#!/bin/bash
# ----------------------------------------
# CONFIG
# ----------------------------------------
INCOMING="/mnt/user/Cache/Picard/incoming"
READY="/mnt/user/Cache/Picard/ready"
DEST1="/mnt/user/Cache/Syncthing/Julian/Musik/Thorsten -> Julian"
DEST2="/mnt/user/Media/Musik/[0] Navidrome"
LOG="/mnt/user/Cache/Picard/sync.log"
LOCKFILE="/mnt/user/Cache/Picard/sync.lock"
LOG_LINES=5000
DRY_RUN=false
CHOWN_USER="thorsten"
CHOWN_GROUP="users"
# ----------------------------------------
# NORMALIZE PATHS
# ----------------------------------------
normalize_path() {
local p="$1"
[[ "$p" != "/" ]] && p="${p%/}"
echo "$p"
}
INCOMING="$(normalize_path "$INCOMING")"
READY="$(normalize_path "$READY")"
DEST1="$(normalize_path "$DEST1")"
DEST2="$(normalize_path "$DEST2")"
# ----------------------------------------
# TRACKING
# ----------------------------------------
declare -A CHANGED_DIRS_DEST1
declare -A CHANGED_DIRS_DEST2
# ----------------------------------------
# FUNCTIONS
# ----------------------------------------
log() {
MSG="$(date '+%Y-%m-%d %H:%M:%S') | $1"
echo "$MSG"
echo "$MSG" >> "$LOG"
}
rotate_log() {
if [ -f "$LOG" ]; then
tail -n "$LOG_LINES" "$LOG" > "${LOG}.tmp" && mv "${LOG}.tmp" "$LOG"
fi
}
run_cmd() {
if [ "$DRY_RUN" = true ]; then
log "[DRY-RUN] $*"
else
"$@"
fi
}
rsync_cmd() {
if [ "$DRY_RUN" = true ]; then
rsync -a --dry-run "$@"
return 0
else
rsync -a "$@"
return $?
fi
}
# ----------------------------------------
# FIX OWNERSHIP TREE
# ----------------------------------------
fix_tree() {
local DIR="$1"
local ROOT="$2"
while [[ "$DIR" == "$ROOT"* ]]; do
run_cmd chown "$CHOWN_USER:$CHOWN_GROUP" "$DIR"
[[ "$DIR" == "$ROOT" ]] && break
DIR="$(dirname "$DIR")"
done
}
# ----------------------------------------
# VERIFY DESTINATIONS
# ----------------------------------------
for DEST in "$DEST1" "$DEST2"; do
if [ ! -d "$DEST" ]; then
echo "$(date '+%Y-%m-%d %H:%M:%S') | ERROR: Destination not found -> $DEST"
exit 1
fi
done
# ----------------------------------------
# LOCKFILE
# ----------------------------------------
if [ -f "$LOCKFILE" ]; then
echo "Lockfile exists -> $LOCKFILE"
exit 1
fi
trap "rm -f '$LOCKFILE'" EXIT
touch "$LOCKFILE"
# ----------------------------------------
# QUICK EXIT
# ----------------------------------------
if ! find "$INCOMING" -type f -mmin +1 -print -quit | grep -q .; then
echo "$(date '+%Y-%m-%d %H:%M:%S') | No files to process -> exit"
exit 0
fi
# ----------------------------------------
# START
# ----------------------------------------
rotate_log
log "========================================"
log "START RUN (DRY_RUN=$DRY_RUN)"
log "========================================"
CHANGED=false
# ----------------------------------------
# 1. INCOMING → READY
# ----------------------------------------
log "---- MOVE: INCOMING → READY ----"
while read -r FILE; do
[ -f "$FILE" ] || continue
REL="${FILE#"$INCOMING"/}"
TARGET="$READY/$REL"
mkdir -p "$READY/$(dirname "$REL")"
log "MOVE -> $REL"
run_cmd mv "$FILE" "$TARGET"
done < <(find "$INCOMING" -type f -mmin +1)
# ----------------------------------------
# 2. READY → DESTS
# ----------------------------------------
log "---- SYNC: READY → DESTINATIONS ----"
log "DEST1: $DEST1"
log "DEST2: $DEST2"
while read -r FILE; do
[ -f "$FILE" ] || continue
REL="${FILE#"$READY"/}"
DEST1_FILE="$DEST1/$REL"
DEST2_FILE="$DEST2/$REL"
DIR1="$(dirname "$DEST1_FILE")"
DIR2="$(dirname "$DEST2_FILE")"
mkdir -p "$DIR1"
mkdir -p "$DIR2"
log "SYNC -> $REL"
rsync_cmd "$FILE" "$DEST1_FILE"
STATUS1=$?
rsync_cmd "$FILE" "$DEST2_FILE"
STATUS2=$?
if [[ $STATUS1 -eq 0 && $STATUS2 -eq 0 ]]; then
CHANGED=true
CHANGED_DIRS_DEST1["$DIR1"]=1
CHANGED_DIRS_DEST2["$DIR2"]=1
log "OK -> delete $REL"
run_cmd rm "$FILE"
else
log "ERROR -> $REL (dest1=$STATUS1 dest2=$STATUS2)"
fi
done < <(find "$READY" -type f 2>/dev/null)
# ----------------------------------------
# 3. FIX OWNERSHIP (SMART TREE)
# ----------------------------------------
log "CHANGED=$CHANGED"
if [ "$CHANGED" = true ]; then
log "FIX OWNERSHIP (tree mode)"
for DIR in "${!CHANGED_DIRS_DEST1[@]}"; do
log "FIX DEST1 -> $DIR"
fix_tree "$DIR" "$DEST1"
done
for DIR in "${!CHANGED_DIRS_DEST2[@]}"; do
log "FIX DEST2 -> $DIR"
fix_tree "$DIR" "$DEST2"
done
fi
# ----------------------------------------
# CLEANUP
# ----------------------------------------
if [ "$DRY_RUN" != true ]; then
find "$INCOMING" -mindepth 1 -type d -empty -delete
find "$READY" -mindepth 1 -type d -empty -delete 2>/dev/null
else
log "[DRY-RUN] Skipping cleanup"
fi
log "========================================"
log "END RUN"
log "========================================"