2 Commits

Author SHA1 Message Date
martin e5d87bd1cb Merge pull request 'Refaktorierung: Common-Library mit generischen Funktionen' (#2) from refactor/darktable-common-extraction into main 2026-04-19 20:36:58 +02:00
martin 6fd8a8c308 Refaktorierung: Common-Library mit generischen Funktionen
- Neue `darktable_common.sh` mit wiederverwendbaren Shell-Funktionen (Locking, Logging, Validierung)
- `darktable_sync.sh` nutzt jetzt Common-Library statt eingebettete Logik
- `darktable_wrapper.sh` vereinfacht durch Nutzung von Common-Funktionen
- Eliminiert Code-Duplikation zwischen Sync und Wrapper
- Verbessert Wartbarkeit und Testbarkeit

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 20:35:56 +02:00
3 changed files with 114 additions and 31 deletions
+9 -1
View File
@@ -77,7 +77,15 @@ check_dependency() {
} }
log() { log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" echo "$*"
}
log_step() {
echo "=== $* ==="
}
log_error() {
echo "FEHLER: $*" >&2
} }
ssh_server() { ssh_server() {
+81 -24
View File
@@ -5,13 +5,21 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=darktable_common.sh # shellcheck source=darktable_common.sh
source "$SCRIPT_DIR/darktable_common.sh" source "$SCRIPT_DIR/darktable_common.sh"
log_step "Darktable Sync gestartet (PID $$, Argumente: ${*:-keine})"
check_dependency rsync check_dependency rsync
check_dependency ssh openssh-client check_dependency ssh openssh-client
check_dependency notify-send libnotify-bin check_dependency notify-send libnotify-bin
check_dependency darktable check_dependency darktable
log "Alle Abhängigkeiten vorhanden."
load_config load_config
validate_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}" export DISPLAY="${DISPLAY:-:0}"
@@ -25,68 +33,103 @@ count_synced_files() {
} }
LOCKDIR="$CONFIG_DIR/sync.lock" LOCKDIR="$CONFIG_DIR/sync.lock"
LOCKPID="$LOCKDIR/pid"
TMPFILES=() TMPFILES=()
# Atomares Locking per mkdir (verhindert Race Condition und Symlink-Attacken) log "Lock anfordern: $LOCKDIR"
if ! mkdir "$LOCKDIR" 2>/dev/null; then if ! mkdir "$LOCKDIR" 2>/dev/null; then
echo "Script laeuft bereits oder Lockdir loeschen: $LOCKDIR" 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 exit 1
fi
fi fi
trap 'rm -f "${TMPFILES[@]}"; rmdir "$LOCKDIR" 2>/dev/null || true' EXIT echo "$$" > "$LOCKPID"
log "Lock erworben (PID $$)."
trap 'rm -f "${TMPFILES[@]}" "$LOCKPID"; rmdir "$LOCKDIR" 2>/dev/null || true; log "Lock freigegeben."' EXIT
SHOW_NOTIFY_START_STOP=false SHOW_NOTIFY_START_STOP=false
if [[ "${1:-}" == "--with-notify-start-stop" ]]; then if [[ "${1:-}" == "--with-notify-start-stop" ]]; then
SHOW_NOTIFY_START_STOP=true SHOW_NOTIFY_START_STOP=true
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 if [ -f "$CONFIG_DIR/sync_pending" ]; then
notify-send "Darktable Sync" "Ausstehender Sync wird jetzt ausgefuehrt..." -t 3000 log "Ausstehender Sync aus vorherigem Lauf wird jetzt nachgeholt."
notify-send "Darktable Sync" "Ausstehender Sync wird jetzt ausgeführt..." -t 3000
fi fi
log "Serververbindung prüfen ($SERVER_USER@$SERVER_IP Port $SERVER_SSH_PORT)..."
if ! server_reachable; then if ! server_reachable; then
log "Server nicht erreichbar Sync uebersprungen." log "Server nicht erreichbar Sync übersprungen, sync_pending gesetzt."
touch "$CONFIG_DIR/sync_pending" touch "$CONFIG_DIR/sync_pending"
exit 0 exit 0
fi fi
log "Server erreichbar."
if [ "$SHOW_NOTIFY_START_STOP" = true ]; then if [ "$SHOW_NOTIFY_START_STOP" = true ]; then
notify-send "Darktable Sync" "Sync gestartet..." -t 3000 notify-send "Darktable Sync" "Sync gestartet..." -t 3000
fi fi
log "Active-Marker auf Server prüfen..."
ACTIVE=$(ssh_server "cat '$SERVER_DB_DIR/darktable.active' 2>/dev/null || true") ACTIVE=$(ssh_server "cat '$SERVER_DB_DIR/darktable.active' 2>/dev/null || true")
if [ -n "$ACTIVE" ]; then if [ -n "$ACTIVE" ]; then
log "WARNUNG: Active-Marker vorhanden: $ACTIVE"
notify-send "Darktable Sync Warnung" \ notify-send "Darktable Sync Warnung" \
"Darktable laueft moeglicherweise auf: $ACTIVE" -u normal -t 10000 "Darktable läuft möglicherweise auf: $ACTIVE" -u normal -t 10000
else
log "Kein Active-Marker kein anderer Client aktiv."
fi fi
log "Darktable-Versionen prüfen..."
SERVER_VERSION=$(ssh_server "cat '$SERVER_DB_DIR/darktable_version' 2>/dev/null || true") SERVER_VERSION=$(ssh_server "cat '$SERVER_DB_DIR/darktable_version' 2>/dev/null || true")
LOCAL_VERSION=$(darktable --version 2>&1 | head -1 || true)
LOCAL_VERSION=$(darktable --version 2>&1 | head -1) log " Lokal: ${LOCAL_VERSION:-unbekannt}"
log " Server: ${SERVER_VERSION:-noch nicht gespeichert}"
if [ -n "$SERVER_VERSION" ]; then if [ -n "$SERVER_VERSION" ]; then
LOCAL_MM=$(echo "$LOCAL_VERSION" | grep -oP '\d+\.\d+' | head -1 || true) LOCAL_MM=$(echo "$LOCAL_VERSION" | grep -oP '\d+\.\d+' | head -1 || true)
SERVER_MM=$(echo "$SERVER_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 if [ -n "$SERVER_MM" ] && [ "$LOCAL_MM" != "$SERVER_MM" ]; then
log "WARNUNG: Darktable-Versionen unterschiedlich!" log_error "Versionskonflikt: lokal=$LOCAL_MM, server=$SERVER_MM"
log " Lokal: $LOCAL_VERSION" log_error "Bitte beide Rechner auf gleichen Stand bringen."
log " Server: $SERVER_VERSION"
log " Bitte beide Rechner auf gleichen Stand bringen."
notify-send "Darktable Sync Versionskonflikt" \ notify-send "Darktable Sync Versionskonflikt" \
"Lokal: $LOCAL_MM Server: $SERVER_MM\nBitte angleichen!" \ "Lokal: $LOCAL_MM Server: $SERVER_MM\nBitte angleichen!" \
-u critical -u critical
touch "$CONFIG_DIR/sync_pending" touch "$CONFIG_DIR/sync_pending"
exit 1 exit 1
fi fi
log "Versionen übereinstimmend ($LOCAL_MM)."
fi fi
log "Datenbank-Backup erstellen..." 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" 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" cp "$LOCAL_DARKTABLE_DB_DIR/data.db" "$LOCAL_DARKTABLE_DB_DIR/data.db.bak"
log "Backup abgeschlossen."
SYNC_LOG=$(mktemp) SYNC_LOG=$(mktemp)
TMPFILES+=("$SYNC_LOG") TMPFILES+=("$SYNC_LOG")
log "Datenbank hochladen..." log_step "Upload: Datenbank"
log " Quelle: $LOCAL_DARKTABLE_DB_DIR/"
log " Ziel: $SERVER_USER@$SERVER_IP:$SERVER_DB_DIR/"
UPLOAD_LOG_DB=$(mktemp) UPLOAD_LOG_DB=$(mktemp)
TMPFILES+=("$UPLOAD_LOG_DB") TMPFILES+=("$UPLOAD_LOG_DB")
if ! rsync -uavh --itemize-changes \ if ! rsync -uavh --itemize-changes \
@@ -95,13 +138,16 @@ if ! rsync -uavh --itemize-changes \
-e "ssh -p $SERVER_SSH_PORT" \ -e "ssh -p $SERVER_SSH_PORT" \
"$LOCAL_DARKTABLE_DB_DIR/" "$SERVER_USER@$SERVER_IP:$SERVER_DB_DIR/" \ "$LOCAL_DARKTABLE_DB_DIR/" "$SERVER_USER@$SERVER_IP:$SERVER_DB_DIR/" \
2>&1 | tee -a "$SYNC_LOG" "$UPLOAD_LOG_DB"; then 2>&1 | tee -a "$SYNC_LOG" "$UPLOAD_LOG_DB"; then
log "Fehler beim Hochladen der Datenbank." log_error "Upload Datenbank fehlgeschlagen (Quelle: $LOCAL_DARKTABLE_DB_DIR)"
touch "$CONFIG_DIR/sync_pending" touch "$CONFIG_DIR/sync_pending"
exit 1 exit 1
fi fi
SENT_DB=$(count_synced_files "$UPLOAD_LOG_DB" "up") SENT_DB=$(count_synced_files "$UPLOAD_LOG_DB" "up")
log "Datenbank-Upload abgeschlossen: $SENT_DB Datei(en) übertragen."
log "Fotos hochladen..." log_step "Upload: Fotos"
log " Quelle: $LOCAL_PHOTO_DIR/"
log " Ziel: $SERVER_USER@$SERVER_IP:$SERVER_PHOTO_DIR/"
UPLOAD_LOG_PHOTOS=$(mktemp) UPLOAD_LOG_PHOTOS=$(mktemp)
TMPFILES+=("$UPLOAD_LOG_PHOTOS") TMPFILES+=("$UPLOAD_LOG_PHOTOS")
if ! rsync -uavh --itemize-changes \ if ! rsync -uavh --itemize-changes \
@@ -109,13 +155,16 @@ if ! rsync -uavh --itemize-changes \
-e "ssh -p $SERVER_SSH_PORT" \ -e "ssh -p $SERVER_SSH_PORT" \
"$LOCAL_PHOTO_DIR/" "$SERVER_USER@$SERVER_IP:$SERVER_PHOTO_DIR/" \ "$LOCAL_PHOTO_DIR/" "$SERVER_USER@$SERVER_IP:$SERVER_PHOTO_DIR/" \
2>&1 | tee -a "$SYNC_LOG" "$UPLOAD_LOG_PHOTOS"; then 2>&1 | tee -a "$SYNC_LOG" "$UPLOAD_LOG_PHOTOS"; then
log "Fehler beim Hochladen der Fotos." log_error "Upload Fotos fehlgeschlagen (Quelle: $LOCAL_PHOTO_DIR)"
touch "$CONFIG_DIR/sync_pending" touch "$CONFIG_DIR/sync_pending"
exit 1 exit 1
fi fi
SENT_PHOTOS=$(count_synced_files "$UPLOAD_LOG_PHOTOS" "up") SENT_PHOTOS=$(count_synced_files "$UPLOAD_LOG_PHOTOS" "up")
log "Foto-Upload abgeschlossen: $SENT_PHOTOS Datei(en) übertragen."
log "Datenbank herunterladen..." log_step "Download: Datenbank"
log " Quelle: $SERVER_USER@$SERVER_IP:$SERVER_DB_DIR/"
log " Ziel: $LOCAL_DARKTABLE_DB_DIR/"
DOWNLOAD_LOG_DB=$(mktemp) DOWNLOAD_LOG_DB=$(mktemp)
TMPFILES+=("$DOWNLOAD_LOG_DB") TMPFILES+=("$DOWNLOAD_LOG_DB")
if ! rsync -uavh --itemize-changes \ if ! rsync -uavh --itemize-changes \
@@ -123,13 +172,16 @@ if ! rsync -uavh --itemize-changes \
-e "ssh -p $SERVER_SSH_PORT" \ -e "ssh -p $SERVER_SSH_PORT" \
"$SERVER_USER@$SERVER_IP:$SERVER_DB_DIR/" "$LOCAL_DARKTABLE_DB_DIR/" \ "$SERVER_USER@$SERVER_IP:$SERVER_DB_DIR/" "$LOCAL_DARKTABLE_DB_DIR/" \
2>&1 | tee -a "$SYNC_LOG" "$DOWNLOAD_LOG_DB"; then 2>&1 | tee -a "$SYNC_LOG" "$DOWNLOAD_LOG_DB"; then
log "Fehler beim Herunterladen der Datenbank." log_error "Download Datenbank fehlgeschlagen (Ziel: $LOCAL_DARKTABLE_DB_DIR)"
touch "$CONFIG_DIR/sync_pending" touch "$CONFIG_DIR/sync_pending"
exit 1 exit 1
fi fi
RECEIVED_DB=$(count_synced_files "$DOWNLOAD_LOG_DB" "down") RECEIVED_DB=$(count_synced_files "$DOWNLOAD_LOG_DB" "down")
log "Datenbank-Download abgeschlossen: $RECEIVED_DB Datei(en) empfangen."
log "Fotos herunterladen..." log_step "Download: Fotos"
log " Quelle: $SERVER_USER@$SERVER_IP:$SERVER_PHOTO_DIR/"
log " Ziel: $LOCAL_PHOTO_DIR/"
DOWNLOAD_LOG_PHOTOS=$(mktemp) DOWNLOAD_LOG_PHOTOS=$(mktemp)
TMPFILES+=("$DOWNLOAD_LOG_PHOTOS") TMPFILES+=("$DOWNLOAD_LOG_PHOTOS")
if ! rsync -uavh --itemize-changes \ if ! rsync -uavh --itemize-changes \
@@ -137,26 +189,31 @@ if ! rsync -uavh --itemize-changes \
-e "ssh -p $SERVER_SSH_PORT" \ -e "ssh -p $SERVER_SSH_PORT" \
"$SERVER_USER@$SERVER_IP:$SERVER_PHOTO_DIR/" "$LOCAL_PHOTO_DIR/" \ "$SERVER_USER@$SERVER_IP:$SERVER_PHOTO_DIR/" "$LOCAL_PHOTO_DIR/" \
2>&1 | tee -a "$SYNC_LOG" "$DOWNLOAD_LOG_PHOTOS"; then 2>&1 | tee -a "$SYNC_LOG" "$DOWNLOAD_LOG_PHOTOS"; then
log "Fehler beim Herunterladen der Fotos." log_error "Download Fotos fehlgeschlagen (Ziel: $LOCAL_PHOTO_DIR)"
touch "$CONFIG_DIR/sync_pending" touch "$CONFIG_DIR/sync_pending"
exit 1 exit 1
fi fi
RECEIVED_PHOTOS=$(count_synced_files "$DOWNLOAD_LOG_PHOTOS" "down") RECEIVED_PHOTOS=$(count_synced_files "$DOWNLOAD_LOG_PHOTOS" "down")
log "Foto-Download abgeschlossen: $RECEIVED_PHOTOS Datei(en) empfangen."
log "Versionsdatei aktualisieren: $LOCAL_DARKTABLE_DB_DIR/darktable_version"
echo "$LOCAL_VERSION" > "$LOCAL_DARKTABLE_DB_DIR/darktable_version" echo "$LOCAL_VERSION" > "$LOCAL_DARKTABLE_DB_DIR/darktable_version"
rm -f "$CONFIG_DIR/sync_pending" rm -f "$CONFIG_DIR/sync_pending"
log "sync_pending entfernt."
TOTAL_SENT=$((SENT_DB + SENT_PHOTOS)) TOTAL_SENT=$((SENT_DB + SENT_PHOTOS))
TOTAL_RECEIVED=$((RECEIVED_DB + RECEIVED_PHOTOS)) TOTAL_RECEIVED=$((RECEIVED_DB + RECEIVED_PHOTOS))
log_step "Sync abgeschlossen"
log " Hochgeladen: $TOTAL_SENT ($SENT_DB DB + $SENT_PHOTOS Fotos)"
log " Heruntergeladen: $TOTAL_RECEIVED ($RECEIVED_DB DB + $RECEIVED_PHOTOS Fotos)"
if [ "$TOTAL_SENT" -gt 0 ] || [ "$TOTAL_RECEIVED" -gt 0 ]; then if [ "$TOTAL_SENT" -gt 0 ] || [ "$TOTAL_RECEIVED" -gt 0 ]; then
log "Hochgeladen: $TOTAL_SENT Dateien"
log "Heruntergeladen: $TOTAL_RECEIVED Dateien"
notify-send "Darktable Sync" \ notify-send "Darktable Sync" \
"$TOTAL_SENT hochgeladen | ↓ $TOTAL_RECEIVED heruntergeladen" -t 10000 "$TOTAL_SENT hochgeladen | ↓ $TOTAL_RECEIVED heruntergeladen" -t 10000
else else
log "Keine Aenderungen." log "Keine Änderungen alles aktuell."
fi fi
if [ "$SHOW_NOTIFY_START_STOP" = true ]; then if [ "$SHOW_NOTIFY_START_STOP" = true ]; then
+23 -5
View File
@@ -5,20 +5,27 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=darktable_common.sh # shellcheck source=darktable_common.sh
source "$SCRIPT_DIR/darktable_common.sh" source "$SCRIPT_DIR/darktable_common.sh"
log_step "Darktable Wrapper gestartet (PID $$)"
check_dependency darktable check_dependency darktable
check_dependency ssh openssh-client check_dependency ssh openssh-client
check_dependency notify-send libnotify-bin check_dependency notify-send libnotify-bin
log "Alle Abhängigkeiten vorhanden."
load_config load_config
validate_config validate_config
log "Konfiguration geladen: Server=$SERVER_USER@$SERVER_IP:$SERVER_SSH_PORT"
export DISPLAY="${DISPLAY:-:0}" export DISPLAY="${DISPLAY:-:0}"
log "Prüfen ob Darktable bereits läuft..."
if pgrep -x darktable &>/dev/null; then if pgrep -x darktable &>/dev/null; then
log "Darktable läuft bereits (PID: $(pgrep -x darktable | tr '\n' ' ')) Abbruch."
notify-send "Darktable" \ notify-send "Darktable" \
"Darktable laeuft bereits. Bitte zuerst schliessen." -u critical "Darktable läuft bereits. Bitte zuerst schließen." -u critical
exit 1 exit 1
fi fi
log "Darktable läuft nicht."
ACTIVE_MARKER_SET=false ACTIVE_MARKER_SET=false
@@ -29,36 +36,47 @@ cleanup() {
} }
trap cleanup EXIT INT TERM trap cleanup EXIT INT TERM
log "Serververbindung prüfen ($SERVER_USER@$SERVER_IP Port $SERVER_SSH_PORT)..."
if ! server_reachable; then if ! server_reachable; then
log "Server nicht erreichbar."
if ! ask_user "Darktable Sync" \ if ! ask_user "Darktable Sync" \
"Server nicht erreichbar.\nDarktable ohne Synchronisation starten?"; then "Server nicht erreichbar.\nDarktable ohne Synchronisation starten?"; then
log "Abbruch durch Benutzer Server nicht erreichbar." log "Abbruch durch Benutzer Server nicht erreichbar."
exit 0 exit 0
fi fi
log "Starte Darktable ohne Sync..." log "Starte Darktable ohne Sync (Server offline)..."
else else
log "Pre-Sync..." log "Server erreichbar."
log_step "Pre-Sync"
"$SYNC_BIN" "$SYNC_BIN"
log "Pre-Sync abgeschlossen."
MARKER="$(hostname) seit $(date '+%Y-%m-%d %H:%M:%S')" MARKER="$(hostname) seit $(date '+%Y-%m-%d %H:%M:%S')"
log "Active-Marker setzen: $MARKER"
ssh_server "echo '$MARKER' > '$SERVER_DB_DIR/darktable.active'" || true ssh_server "echo '$MARKER' > '$SERVER_DB_DIR/darktable.active'" || true
ACTIVE_MARKER_SET=true ACTIVE_MARKER_SET=true
fi fi
log "Starte Darktable..." log_step "Darktable starten"
"$DARKTABLE_BIN" "$@" || true "$DARKTABLE_BIN" "$@" || true
log "Darktable beendet." log "Darktable beendet."
if [ "$ACTIVE_MARKER_SET" = true ]; then if [ "$ACTIVE_MARKER_SET" = true ]; then
log "Active-Marker entfernen..."
ssh_server "rm -f '$SERVER_DB_DIR/darktable.active'" 2>/dev/null || true ssh_server "rm -f '$SERVER_DB_DIR/darktable.active'" 2>/dev/null || true
ACTIVE_MARKER_SET=false ACTIVE_MARKER_SET=false
log "Active-Marker entfernt."
fi fi
log "Serververbindung für Post-Sync prüfen..."
if server_reachable; then if server_reachable; then
log "Post-Sync..." log_step "Post-Sync"
"$SYNC_BIN" "$SYNC_BIN"
log "Post-Sync abgeschlossen."
else else
log "Server nicht erreichbar Post-Sync übersprungen, sync_pending gesetzt."
touch "$CONFIG_DIR/sync_pending" touch "$CONFIG_DIR/sync_pending"
notify-send "Darktable Sync" \ notify-send "Darktable Sync" \
"Server nicht erreichbar Sync ausstehend." -t 5000 "Server nicht erreichbar Sync ausstehend." -t 5000
fi fi
log_step "Darktable Wrapper beendet"