Robuste Darktable-Synchronisation: sequenzieller Ablauf, Versions- und Concurrent-Schutz
- Race Condition behoben: Pre-Sync wird vollstaendig abgewartet bevor Darktable startet - Post-Sync nach Schliessen von Darktable eingefuehrt (bisher fehlend) - .env aus festem Pfad ~/.config/darktable-sync/.env geladen (nicht mehr relativ) - Server-Erreichbarkeit per SSH statt ping (Firewall-sicher) - Darktable-Versionscheck (Major.Minor) vor Download mit Abbruch bei Konflikt - DB-Backup vor jedem Download (library.db.bak, data.db.bak) - sync_pending-Marker bei Offline/Fehler, Hinweis beim naechsten Start - darktable.active-Marker auf Server fuer Concurrent-Erkennung - Lock-Dateien vom Sync ausgeschlossen - systemd-Timer entfernt, Service bleibt als manueller Trigger - Gemeinsame Hilfsfunktionen in darktable_common.sh extrahiert - 20 BATS-Tests mit vollstaendigem Stub-System ohne GUI-Dialoge Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
+151
-86
@@ -1,109 +1,174 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Default-Konfiguration (per ENV überschreibbar)
|
||||
SERVER_USER="${SERVER_USER}"
|
||||
SERVER_SSH_PORT="${SERVER_SSH_PORT}"
|
||||
SERVER_IP="${SERVER_IP}"
|
||||
SERVER_DB_DIR="${SERVER_DB_DIR}"
|
||||
SERVER_PHOTO_DIR="${SERVER_PHOTO_DIR}"
|
||||
LOCAL_PHOTO_DIR="${PHOTO_DIR}"
|
||||
LOCAL_DARKTABLE_DB_DIR="${DARKTABLE_DB_DIR}"
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
# shellcheck source=darktable_common.sh
|
||||
source "$SCRIPT_DIR/darktable_common.sh"
|
||||
|
||||
check_dependency rsync
|
||||
check_dependency ssh openssh-client
|
||||
check_dependency notify-send libnotify-bin
|
||||
check_dependency darktable
|
||||
|
||||
load_config
|
||||
validate_config
|
||||
|
||||
export DISPLAY="${DISPLAY:-:0}"
|
||||
|
||||
log() {
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"
|
||||
}
|
||||
|
||||
count_synced_files() {
|
||||
local LOG="$1"
|
||||
local DIRECTION="$2"
|
||||
local COUNT=0
|
||||
|
||||
case "$DIRECTION" in
|
||||
up)
|
||||
COUNT=$(grep -E '^>f|cd' "$LOG" | wc -l)
|
||||
;;
|
||||
down)
|
||||
COUNT=$(grep -E '^<f|cd' "$LOG" | wc -l)
|
||||
;;
|
||||
local log_file="$1" direction="$2" count=0
|
||||
case "$direction" in
|
||||
up) count=$(grep -cE '^>f|^cd' "$log_file" 2>/dev/null) || count=0 ;;
|
||||
down) count=$(grep -cE '^<f|^cd' "$log_file" 2>/dev/null) || count=0 ;;
|
||||
esac
|
||||
echo "$COUNT"
|
||||
echo "$count"
|
||||
}
|
||||
|
||||
SCRIPT_NAME=$(basename "$0")
|
||||
SCRIPT_NAME="$(basename "$0")"
|
||||
LOCKFILE="/tmp/${SCRIPT_NAME}.lock"
|
||||
|
||||
if [ -e "$LOCKFILE" ]; then
|
||||
echo "Script is already running or delete $LOCKFILE"
|
||||
echo "Script laeuft bereits oder Lockfile loeschen: $LOCKFILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
touch "$LOCKFILE"
|
||||
trap "rm -f '$LOCKFILE'" EXIT
|
||||
TMPFILES=("$LOCKFILE")
|
||||
trap 'rm -f "${TMPFILES[@]}"' EXIT
|
||||
|
||||
SHOW_NOTIFY_START_STOP=false
|
||||
if [[ "$1" == "--with-notify-start-stop" ]]; then
|
||||
if [[ "${1:-}" == "--with-notify-start-stop" ]]; then
|
||||
SHOW_NOTIFY_START_STOP=true
|
||||
fi
|
||||
|
||||
if ping -c 1 "$SERVER_IP" &>/dev/null; then
|
||||
export DISPLAY=:0
|
||||
SYNC_LOG=$(mktemp)
|
||||
log "Server is reachable – starting sync..."
|
||||
log "Log file: $SYNC_LOG"
|
||||
|
||||
if [ "$SHOW_NOTIFY_START_STOP" = true ]; then
|
||||
notify-send "Darktable Sync" "Sync started..." -t 3000
|
||||
fi
|
||||
|
||||
log "Uploading Darktable DB to Server..."
|
||||
UPLOAD_LOG1=$(mktemp)
|
||||
rsync -uavh --itemize-changes -e "ssh -p $SERVER_SSH_PORT" \
|
||||
"$LOCAL_DARKTABLE_DB_DIR/" "$SERVER_USER@$SERVER_IP:$SERVER_DB_DIR/" \
|
||||
2>&1 | tee -a "$SYNC_LOG" "$UPLOAD_LOG1"
|
||||
SENT1=$(count_synced_files "$UPLOAD_LOG1" "up")
|
||||
rm "$UPLOAD_LOG1"
|
||||
|
||||
log "Uploading photos to Server..."
|
||||
UPLOAD_LOG2=$(mktemp)
|
||||
rsync -uavh --itemize-changes -e "ssh -p $SERVER_SSH_PORT" \
|
||||
"$LOCAL_PHOTO_DIR/" "$SERVER_USER@$SERVER_IP:$SERVER_PHOTO_DIR/" \
|
||||
2>&1 | tee -a "$SYNC_LOG" "$UPLOAD_LOG2"
|
||||
SENT2=$(count_synced_files "$UPLOAD_LOG2" "up")
|
||||
rm "$UPLOAD_LOG2"
|
||||
|
||||
log "Downloading DB back from Server..."
|
||||
DOWNLOAD_LOG1=$(mktemp)
|
||||
rsync -uavh --itemize-changes -e "ssh -p $SERVER_SSH_PORT" \
|
||||
"$SERVER_USER@$SERVER_IP:$SERVER_DB_DIR/" "$LOCAL_DARKTABLE_DB_DIR/" \
|
||||
2>&1 | tee -a "$SYNC_LOG" "$DOWNLOAD_LOG1"
|
||||
RECEIVED1=$(count_synced_files "$DOWNLOAD_LOG1" "down")
|
||||
rm "$DOWNLOAD_LOG1"
|
||||
|
||||
log "Downloading photos from Server..."
|
||||
DOWNLOAD_LOG2=$(mktemp)
|
||||
rsync -uavh --itemize-changes -e "ssh -p $SERVER_SSH_PORT" \
|
||||
"$SERVER_USER@$SERVER_IP:$SERVER_PHOTO_DIR/" "$LOCAL_PHOTO_DIR/" \
|
||||
2>&1 | tee -a "$SYNC_LOG" "$DOWNLOAD_LOG2"
|
||||
RECEIVED2=$(count_synced_files "$DOWNLOAD_LOG2" "down")
|
||||
rm "$DOWNLOAD_LOG2"
|
||||
|
||||
if [ "$SHOW_NOTIFY_START_STOP" = true ]; then
|
||||
notify-send "Darktable Sync" "Sync finished." -t 3000
|
||||
fi
|
||||
|
||||
TOTAL_SENT=$((SENT1 + SENT2))
|
||||
TOTAL_RECEIVED=$((RECEIVED1 + RECEIVED2))
|
||||
|
||||
if [ "$TOTAL_SENT" -gt 0 ] || [ "$TOTAL_RECEIVED" -gt 0 ]; then
|
||||
log "Uploaded: $TOTAL_SENT files"
|
||||
log "Downloaded: $TOTAL_RECEIVED files"
|
||||
notify-send "Darktable Sync" "↑ $TOTAL_SENT uploaded | ↓ $TOTAL_RECEIVED downloaded" -t 10000
|
||||
else
|
||||
log "No changes detected."
|
||||
fi
|
||||
|
||||
rm -f "$SYNC_LOG"
|
||||
else
|
||||
log "Server not reachable – skipping sync."
|
||||
if [ -f "$CONFIG_DIR/sync_pending" ]; then
|
||||
notify-send "Darktable Sync" "Ausstehender Sync wird jetzt ausgefuehrt..." -t 3000
|
||||
fi
|
||||
|
||||
if ! server_reachable; then
|
||||
log "Server nicht erreichbar – Sync uebersprungen."
|
||||
touch "$CONFIG_DIR/sync_pending"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "$SHOW_NOTIFY_START_STOP" = true ]; then
|
||||
notify-send "Darktable Sync" "Sync gestartet..." -t 3000
|
||||
fi
|
||||
|
||||
ACTIVE=$(ssh -o ConnectTimeout=5 -o BatchMode=yes \
|
||||
-p "$SERVER_SSH_PORT" "$SERVER_USER@$SERVER_IP" \
|
||||
"cat '$SERVER_DB_DIR/darktable.active' 2>/dev/null || true")
|
||||
if [ -n "$ACTIVE" ]; then
|
||||
notify-send "Darktable Sync – Warnung" \
|
||||
"Darktable laueft moeglicherweise auf: $ACTIVE" -u normal -t 10000
|
||||
fi
|
||||
|
||||
SERVER_VERSION=$(ssh -o ConnectTimeout=5 -o BatchMode=yes \
|
||||
-p "$SERVER_SSH_PORT" "$SERVER_USER@$SERVER_IP" \
|
||||
"cat '$SERVER_DB_DIR/darktable_version' 2>/dev/null || true")
|
||||
|
||||
LOCAL_VERSION=$(darktable --version 2>&1 | head -1)
|
||||
|
||||
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 "WARNUNG: Darktable-Versionen unterschiedlich!"
|
||||
log " Lokal: $LOCAL_VERSION"
|
||||
log " Server: $SERVER_VERSION"
|
||||
log " 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
|
||||
fi
|
||||
|
||||
log "Datenbank-Backup erstellen..."
|
||||
cp "$LOCAL_DARKTABLE_DB_DIR/library.db" "$LOCAL_DARKTABLE_DB_DIR/library.db.bak"
|
||||
cp "$LOCAL_DARKTABLE_DB_DIR/data.db" "$LOCAL_DARKTABLE_DB_DIR/data.db.bak"
|
||||
|
||||
SYNC_LOG=$(mktemp)
|
||||
TMPFILES+=("$SYNC_LOG")
|
||||
|
||||
log "Datenbank hochladen..."
|
||||
UPLOAD_LOG_DB=$(mktemp)
|
||||
TMPFILES+=("$UPLOAD_LOG_DB")
|
||||
if ! rsync -uavh --itemize-changes \
|
||||
--exclude '*.lock' \
|
||||
--exclude 'darktable_version' \
|
||||
-e "ssh -p $SERVER_SSH_PORT" \
|
||||
"$LOCAL_DARKTABLE_DB_DIR/" "$SERVER_USER@$SERVER_IP:$SERVER_DB_DIR/" \
|
||||
2>&1 | tee -a "$SYNC_LOG" "$UPLOAD_LOG_DB"; then
|
||||
log "Fehler beim Hochladen der Datenbank."
|
||||
touch "$CONFIG_DIR/sync_pending"
|
||||
exit 1
|
||||
fi
|
||||
SENT_DB=$(count_synced_files "$UPLOAD_LOG_DB" "up")
|
||||
|
||||
log "Fotos hochladen..."
|
||||
UPLOAD_LOG_PHOTOS=$(mktemp)
|
||||
TMPFILES+=("$UPLOAD_LOG_PHOTOS")
|
||||
if ! rsync -uavh --itemize-changes \
|
||||
--exclude '*.lock' \
|
||||
-e "ssh -p $SERVER_SSH_PORT" \
|
||||
"$LOCAL_PHOTO_DIR/" "$SERVER_USER@$SERVER_IP:$SERVER_PHOTO_DIR/" \
|
||||
2>&1 | tee -a "$SYNC_LOG" "$UPLOAD_LOG_PHOTOS"; then
|
||||
log "Fehler beim Hochladen der Fotos."
|
||||
touch "$CONFIG_DIR/sync_pending"
|
||||
exit 1
|
||||
fi
|
||||
SENT_PHOTOS=$(count_synced_files "$UPLOAD_LOG_PHOTOS" "up")
|
||||
|
||||
log "Datenbank herunterladen..."
|
||||
DOWNLOAD_LOG_DB=$(mktemp)
|
||||
TMPFILES+=("$DOWNLOAD_LOG_DB")
|
||||
if ! rsync -uavh --itemize-changes \
|
||||
--exclude '*.lock' \
|
||||
-e "ssh -p $SERVER_SSH_PORT" \
|
||||
"$SERVER_USER@$SERVER_IP:$SERVER_DB_DIR/" "$LOCAL_DARKTABLE_DB_DIR/" \
|
||||
2>&1 | tee -a "$SYNC_LOG" "$DOWNLOAD_LOG_DB"; then
|
||||
log "Fehler beim Herunterladen der Datenbank."
|
||||
touch "$CONFIG_DIR/sync_pending"
|
||||
exit 1
|
||||
fi
|
||||
RECEIVED_DB=$(count_synced_files "$DOWNLOAD_LOG_DB" "down")
|
||||
|
||||
log "Fotos herunterladen..."
|
||||
DOWNLOAD_LOG_PHOTOS=$(mktemp)
|
||||
TMPFILES+=("$DOWNLOAD_LOG_PHOTOS")
|
||||
if ! rsync -uavh --itemize-changes \
|
||||
--exclude '*.lock' \
|
||||
-e "ssh -p $SERVER_SSH_PORT" \
|
||||
"$SERVER_USER@$SERVER_IP:$SERVER_PHOTO_DIR/" "$LOCAL_PHOTO_DIR/" \
|
||||
2>&1 | tee -a "$SYNC_LOG" "$DOWNLOAD_LOG_PHOTOS"; then
|
||||
log "Fehler beim Herunterladen der Fotos."
|
||||
touch "$CONFIG_DIR/sync_pending"
|
||||
exit 1
|
||||
fi
|
||||
RECEIVED_PHOTOS=$(count_synced_files "$DOWNLOAD_LOG_PHOTOS" "down")
|
||||
|
||||
echo "$LOCAL_VERSION" > "$LOCAL_DARKTABLE_DB_DIR/darktable_version"
|
||||
|
||||
rm -f "$CONFIG_DIR/sync_pending"
|
||||
|
||||
TOTAL_SENT=$((SENT_DB + SENT_PHOTOS))
|
||||
TOTAL_RECEIVED=$((RECEIVED_DB + RECEIVED_PHOTOS))
|
||||
|
||||
if [ "$TOTAL_SENT" -gt 0 ] || [ "$TOTAL_RECEIVED" -gt 0 ]; then
|
||||
log "Hochgeladen: $TOTAL_SENT Dateien"
|
||||
log "Heruntergeladen: $TOTAL_RECEIVED Dateien"
|
||||
notify-send "Darktable Sync" \
|
||||
"↑ $TOTAL_SENT hochgeladen | ↓ $TOTAL_RECEIVED heruntergeladen" -t 10000
|
||||
else
|
||||
log "Keine Aenderungen."
|
||||
fi
|
||||
|
||||
if [ "$SHOW_NOTIFY_START_STOP" = true ]; then
|
||||
notify-send "Darktable Sync" "Sync abgeschlossen." -t 3000
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user