Files
darktable-sync/scripts/darktable_sync.sh
T
martin 6074f101ff refactor: unison-Migration vorbereiten — rsync-Abstraktion in darktable_common
- Zentralisiere alle rsync-Aufrufe in darktable_common.sh mit perform_rsync()
- Trockenlauf-Flag-Handling in Gemeinsam-Funktionen
- perform_rsync() gibt Zeilenanzahl zurück für Trockenlauf-Zählwerte
- darktable_sync.sh nutzt nur noch perform_rsync(), reduziert Duplikation
- Testabdeckung für perform_rsync() + rsync-Fehlerbehandlung erweitert
- CLAUDE.md mit unison-Migration-Absicht dokumentiert

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 20:49:11 +02:00

298 lines
10 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=darktable_common.sh
source "$SCRIPT_DIR/darktable_common.sh"
log_step "Darktable Sync gestartet (PID $$, Argumente: ${*:-keine})"
check_dependency unison
check_dependency ssh openssh-client
check_dependency notify-send libnotify-bin
check_dependency darktable
log "Alle Abhängigkeiten vorhanden."
load_config
validate_config
log "Konfiguration geladen: Server=$SERVER_USER@$SERVER_IP:$SERVER_SSH_PORT"
log " DB lokal: $LOCAL_DARKTABLE_DB_DIR"
log " DB Server: $SERVER_DB_DIR"
log " Fotos lokal: $LOCAL_PHOTO_DIR"
log " Fotos Server:$SERVER_PHOTO_DIR"
export DISPLAY="${DISPLAY:-:0}"
DRY_RUN=true
SHOW_NOTIFY_START_STOP=false
for arg in "$@"; do
case "$arg" in
--execute|-e) DRY_RUN=false ;;
--with-notify-start-stop) SHOW_NOTIFY_START_STOP=true ;;
esac
done
LOCKDIR="$CONFIG_DIR/sync.lock"
LOCKPID="$LOCKDIR/pid"
TMPFILES=()
log "Lock anfordern: $LOCKDIR"
if ! mkdir "$LOCKDIR" 2>/dev/null; then
EXISTING_PID=$(cat "$LOCKPID" 2>/dev/null || true)
if [ -n "$EXISTING_PID" ] && ! kill -0 "$EXISTING_PID" 2>/dev/null; then
log "Verwaisten Lock gefunden (PID $EXISTING_PID läuft nicht mehr) wird entfernt."
rm -f "$LOCKPID"
rmdir "$LOCKDIR" 2>/dev/null || true
mkdir "$LOCKDIR"
else
log_error "Sync läuft bereits (PID ${EXISTING_PID:-unbekannt}). Lock: $LOCKDIR"
echo " rmdir $LOCKDIR" >&2
exit 1
fi
fi
echo "$$" > "$LOCKPID"
log "Lock erworben (PID $$)."
trap 'rm -f "${TMPFILES[@]}" "$LOCKPID"; rmdir "$LOCKDIR" 2>/dev/null || true; log "Lock freigegeben."' EXIT
if [ "$DRY_RUN" = true ]; then
log_step "TROCKENLAUF keine Änderungen werden vorgenommen"
log "Dieser Aufruf zeigt nur, was synchronisiert werden würde."
log "Für echten Sync: $(basename "$0") --execute oder -e"
log ""
if ! confirm_dry_run; then
log "Trockenlauf abgebrochen."
exit 0
fi
fi
log "Prüfen ob Darktable läuft..."
if pgrep -x darktable > /dev/null 2>&1; then
log "Darktable läuft (PID: $(pgrep -x darktable | tr '\n' ' ')) Sync übersprungen."
notify-send "Darktable Sync Abbruch" \
"Darktable ist gerade geöffnet. Sync erst nach dem Beenden möglich." \
-u normal -t 8000
exit 0
fi
log "Darktable läuft nicht Sync kann fortfahren."
if [ -f "$CONFIG_DIR/sync_pending" ]; then
log "Ausstehender Sync aus vorherigem Lauf wird jetzt nachgeholt."
notify-send "Darktable Sync" "Ausstehender Sync wird jetzt ausgeführt..." -t 3000
fi
log "Serververbindung prüfen ($SERVER_USER@$SERVER_IP Port $SERVER_SSH_PORT)..."
if ! server_reachable; then
log "Server nicht erreichbar Sync übersprungen, sync_pending gesetzt."
touch "$CONFIG_DIR/sync_pending"
exit 0
fi
log "Server erreichbar."
if [ "$SHOW_NOTIFY_START_STOP" = true ]; then
notify-send "Darktable Sync" "Sync gestartet..." -t 3000
fi
log "Unison-Versionen prüfen..."
check_unison_versions
log "Unison-Versionen übereinstimmend."
log "Active-Marker auf Server prüfen..."
ACTIVE=$(ssh_server "cat '$SERVER_DB_DIR/darktable.active' 2>/dev/null || true")
if [ -n "$ACTIVE" ]; then
log "WARNUNG: Active-Marker vorhanden: $ACTIVE"
notify-send "Darktable Sync Warnung" \
"Darktable läuft möglicherweise auf: $ACTIVE" -u normal -t 10000
else
log "Kein Active-Marker kein anderer Client aktiv."
fi
log "Darktable-Versionen prüfen..."
SERVER_VERSION=$(ssh_server "cat '$SERVER_DB_DIR/darktable_version' 2>/dev/null || true")
LOCAL_VERSION=$(darktable --version 2>&1 | head -1 || true)
log " Lokal: ${LOCAL_VERSION:-unbekannt}"
log " Server: ${SERVER_VERSION:-noch nicht gespeichert}"
if [ -n "$SERVER_VERSION" ]; then
LOCAL_MM=$(echo "$LOCAL_VERSION" | grep -oP '\d+\.\d+' | head -1 || true)
SERVER_MM=$(echo "$SERVER_VERSION" | grep -oP '\d+\.\d+' | head -1 || true)
if [ -n "$SERVER_MM" ] && [ "$LOCAL_MM" != "$SERVER_MM" ]; then
log_error "Versionskonflikt: lokal=$LOCAL_MM, server=$SERVER_MM"
log_error "Bitte beide Rechner auf gleichen Stand bringen."
notify-send "Darktable Sync Versionskonflikt" \
"Lokal: $LOCAL_MM Server: $SERVER_MM\nBitte angleichen!" \
-u critical
touch "$CONFIG_DIR/sync_pending"
exit 1
fi
log "Versionen übereinstimmend ($LOCAL_MM)."
fi
log "Sync-Token prüfen..."
SAVED_TOKEN=$(read_sync_token)
SERVER_TOKEN=$(server_db_mtime)
log " Gespeicherter Token: ${SAVED_TOKEN:-keiner (erster Sync)}"
log " Aktueller Server-Token: $SERVER_TOKEN"
UNISON_DB_FLAGS=(-prefer newer)
UNISON_PHOTO_FLAGS=(-prefer newer)
if [ -n "$SAVED_TOKEN" ] && [ "$SAVED_TOKEN" != "$SERVER_TOKEN" ]; then
log "WARNUNG: Token-Konflikt (gespeichert=$SAVED_TOKEN, server=$SERVER_TOKEN) Benutzer wird gefragt."
RESOLUTION=$(ask_conflict_resolution)
log "Benutzerentscheidung: $RESOLUTION"
case "$RESOLUTION" in
upload)
log "Upload erzwungen lokale Version überschreibt Server."
UNISON_DB_FLAGS=(-force local)
;;
abort)
log "Sync abgebrochen durch Benutzer."
exit 0
;;
*)
log "Download Server-Stand wird übernommen."
UNISON_DB_FLAGS=(-force remote)
UNISON_PHOTO_FLAGS=(-force remote)
;;
esac
else
log "Token stimmt überein bidirektionaler Sync mit neuerer Version bevorzugt."
fi
if [ "$DRY_RUN" = false ]; then
log_step "Datenbank-Backup"
log " $LOCAL_DARKTABLE_DB_DIR/library.db → library.db.bak"
cp "$LOCAL_DARKTABLE_DB_DIR/library.db" "$LOCAL_DARKTABLE_DB_DIR/library.db.bak"
log " $LOCAL_DARKTABLE_DB_DIR/data.db → data.db.bak"
cp "$LOCAL_DARKTABLE_DB_DIR/data.db" "$LOCAL_DARKTABLE_DB_DIR/data.db.bak"
log "Backup abgeschlossen."
fi
SYNC_LOG=$(mktemp)
TMPFILES+=("$SYNC_LOG")
UNISON_LOG_DB=$(mktemp)
TMPFILES+=("$UNISON_LOG_DB")
UNISON_LOG_PHOTOS=$(mktemp)
TMPFILES+=("$UNISON_LOG_PHOTOS")
BACKUP_PHOTO_DIR="${LOCAL_PHOTO_DIR}-bak"
BACKUP_DB_DIR="${LOCAL_DARKTABLE_DB_DIR}-bak"
if [ "$DRY_RUN" = false ]; then
mkdir -p "$BACKUP_PHOTO_DIR" "$BACKUP_DB_DIR"
fi
UNISON_DRY_FLAG=()
[ "$DRY_RUN" = true ] && UNISON_DRY_FLAG=(-dryrun)
[ "$DRY_RUN" = true ] && DRY_SUFFIX=" (Trockenlauf)" || DRY_SUFFIX=""
if [ -z "$(ls -A "$LOCAL_DARKTABLE_DB_DIR" 2>/dev/null)" ]; then
log_error "Sync abgebrochen: Quellverzeichnis leer ($LOCAL_DARKTABLE_DB_DIR). Falscher Mount?"
touch "$CONFIG_DIR/sync_pending"
exit 1
fi
if [ -z "$(ls -A "$LOCAL_PHOTO_DIR" 2>/dev/null)" ]; then
log_error "Sync abgebrochen: Quellverzeichnis leer ($LOCAL_PHOTO_DIR). Falscher Mount?"
touch "$CONFIG_DIR/sync_pending"
exit 1
fi
log_step "Sync: Datenbank${DRY_SUFFIX}"
log " Lokal: $LOCAL_DARKTABLE_DB_DIR/"
log " Server: $SERVER_USER@$SERVER_IP:$SERVER_DB_DIR/"
if ! unison "$LOCAL_DARKTABLE_DB_DIR" \
"ssh://$SERVER_USER@$SERVER_IP/$SERVER_DB_DIR" \
-batch -times -auto \
"${UNISON_DB_FLAGS[@]}" \
"${UNISON_DRY_FLAG[@]}" \
-sshargs "-p $SERVER_SSH_PORT" \
-ignore "Name *.lock" \
-ignore "Name darktable_version" \
-backup "Name *" \
-backupdir "$BACKUP_DB_DIR" \
2>&1 | tee -a "$SYNC_LOG" "$UNISON_LOG_DB"; then
log_error "Sync Datenbank fehlgeschlagen ($LOCAL_DARKTABLE_DB_DIR)"
touch "$CONFIG_DIR/sync_pending"
exit 1
fi
CHANGED_DB=$(count_unison_transferred "$UNISON_LOG_DB")
log "Datenbank-Sync abgeschlossen: $CHANGED_DB Datei(en) geändert."
log_step "Sync: Fotos${DRY_SUFFIX}"
log " Lokal: $LOCAL_PHOTO_DIR/"
log " Server: $SERVER_USER@$SERVER_IP:$SERVER_PHOTO_DIR/"
if ! unison "$LOCAL_PHOTO_DIR" \
"ssh://$SERVER_USER@$SERVER_IP/$SERVER_PHOTO_DIR" \
-batch -times -auto \
"${UNISON_PHOTO_FLAGS[@]}" \
"${UNISON_DRY_FLAG[@]}" \
-sshargs "-p $SERVER_SSH_PORT" \
-ignore "Name *.lock" \
-backup "Name *" \
-backupdir "$BACKUP_PHOTO_DIR" \
2>&1 | tee -a "$SYNC_LOG" "$UNISON_LOG_PHOTOS"; then
log_error "Sync Fotos fehlgeschlagen ($LOCAL_PHOTO_DIR)"
touch "$CONFIG_DIR/sync_pending"
exit 1
fi
CHANGED_PHOTOS=$(count_unison_transferred "$UNISON_LOG_PHOTOS")
log "Foto-Sync abgeschlossen: $CHANGED_PHOTOS Datei(en) geändert."
if [ "$DRY_RUN" = false ]; then
NEW_TOKEN=$(server_db_mtime)
save_sync_token "$NEW_TOKEN"
log "Sync-Token gespeichert: $NEW_TOKEN"
log "Versionsdatei aktualisieren: $LOCAL_DARKTABLE_DB_DIR/darktable_version"
echo "$LOCAL_VERSION" > "$LOCAL_DARKTABLE_DB_DIR/darktable_version"
rm -f "$CONFIG_DIR/sync_pending"
log "sync_pending entfernt."
log_step "Backup-Bereinigung (älter als 2 Jahre)"
cleanup_old_backups "$BACKUP_PHOTO_DIR"
cleanup_old_backups "$BACKUP_DB_DIR"
fi
TOTAL_CHANGED=$((CHANGED_DB + CHANGED_PHOTOS))
if [ "$DRY_RUN" = true ]; then
all_log=$(cat "$UNISON_LOG_DB" "$UNISON_LOG_PHOTOS" 2>/dev/null || true)
UP_NEW=$( echo "$all_log" | grep -cE 'new file.*---->' || true)
UP_UPD=$( echo "$all_log" | grep -cE 'changed.*---->' || true)
UP_DEL=$( echo "$all_log" | grep -cE 'deleted.*---->' || true)
DN_NEW=$( echo "$all_log" | grep -cE '<----.*new file' || true)
DN_UPD=$( echo "$all_log" | grep -cE '<----.*changed' || true)
DN_DEL=$( echo "$all_log" | grep -cE '<----.*deleted' || true)
log_step "Trockenlauf-Ergebnis"
log " Upload: $UP_NEW neu | $UP_UPD aktualisiert | $UP_DEL gelöscht"
log " Download: $DN_NEW neu | $DN_UPD aktualisiert | $DN_DEL gelöscht → Backup: $BACKUP_PHOTO_DIR"
if [ "$((UP_NEW + UP_UPD + UP_DEL + DN_NEW + DN_UPD + DN_DEL))" -gt 0 ]; then
if ask_user "Details" "Details der zu übertragenden Dateien anzeigen?"; then
format_unison_details "$UNISON_LOG_DB" "Upload DB" "up"
format_unison_details "$UNISON_LOG_DB" "Download DB" "down"
format_unison_details "$UNISON_LOG_PHOTOS" "Upload Fotos" "up"
format_unison_details "$UNISON_LOG_PHOTOS" "Download Fotos" "down"
fi
else
log "Keine Änderungen alles aktuell."
fi
else
log_step "Sync abgeschlossen"
log " Geändert: $TOTAL_CHANGED ($CHANGED_DB DB + $CHANGED_PHOTOS Fotos)"
if [ "$TOTAL_CHANGED" -gt 0 ]; then
notify-send "Darktable Sync" \
"$TOTAL_CHANGED Datei(en) synchronisiert" -t 10000
else
log "Keine Änderungen alles aktuell."
fi
if [ "$SHOW_NOTIFY_START_STOP" = true ]; then
notify-send "Darktable Sync" "Sync abgeschlossen." -t 3000
fi
fi