#!/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")" # ---------------------------------------- # 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 } # ---------------------------------------- # 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 "========================================" # ---------------------------------------- # 1. INCOMING → READY # ---------------------------------------- log "---- MOVE: INCOMING → READY ----" find "$INCOMING" -type f -mmin +1 | 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 # ---------------------------------------- # 2. READY → DESTS # ---------------------------------------- log "---- SYNC: READY → DESTINATIONS ----" log "DEST1: $DEST1" log "DEST2: $DEST2" find "$READY" -type f 2>/dev/null | while read -r FILE; do [ -f "$FILE" ] || continue REL="${FILE#"$READY"/}" DEST1_FILE="$DEST1/$REL" DEST2_FILE="$DEST2/$REL" mkdir -p "$(dirname "$DEST1_FILE")" mkdir -p "$(dirname "$DEST2_FILE")" log "SYNC -> $REL" rsync_cmd "$FILE" "$DEST1_FILE" STATUS1=$? rsync_cmd "$FILE" "$DEST2_FILE" STATUS2=$? if [[ $STATUS1 -eq 0 && $STATUS2 -eq 0 ]]; then log "SET OWNER -> $REL" DIR1="$(dirname "$DEST1_FILE")" DIR2="$(dirname "$DEST2_FILE")" run_cmd chown -R "$CHOWN_USER:$CHOWN_GROUP" "$DIR1" run_cmd chown -R "$CHOWN_USER:$CHOWN_GROUP" "$DIR2" log "OK -> delete $REL" run_cmd rm "$FILE" else log "ERROR -> $REL (dest1=$STATUS1 dest2=$STATUS2)" fi done # ---------------------------------------- # 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 "========================================"