First complete version

This commit is contained in:
2025-06-09 16:26:18 +02:00
parent 6eb94d9077
commit d7ca1fa2a8
6 changed files with 352 additions and 1 deletions
+13
View File
@@ -0,0 +1,13 @@
# NAS Connection Settings
NAS_USER="your_nas_user"
NAS_SSH_PORT=22
NAS_IP="192.168.1.100"
# NAS Paths
NAS_DB_DIR="/path/on/nas/darktable_db"
NAS_PHOTO_DIR="/path/on/nas/photo_library"
# Local Paths
LOCAL_PHOTO_DIR="$HOME/Pictures/raw"
LOCAL_DARKTABLE_DB_DIR="$HOME/.config/darktable"
BIN_DIR="$HOME/.local/bin"
+7
View File
@@ -0,0 +1,7 @@
*.log
*.tmp
.env
.DS_Store
thumbs.db
.idea/
.vscode/
+1 -1
View File
@@ -1,6 +1,6 @@
MIT License
Copyright (c) 2025 MaTr74
Copyright (c) 2025 Martin Tröger (@MaTr74)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
+39
View File
@@ -0,0 +1,39 @@
# Darktable Sync 🔄
Auto-sync your Darktable database and photos between different computers with a server as intermediate. Keeps local and remote files in sync using rsync over SSH. The wrapper script synchronizes files before launching Darktable and after it closes. The scheduled background synchronization ensures continuous and reliable data exchange — even if the wrapper script is not used.
Since only file synchronization is performed, only one Darktable instance should run at a time.
## Features ✨
- 🔄 Bidirectional sync between local machines and a server
- ⏲️ Automatic sync every 5 minutes via systemd timer
- 🖱️ Desktop shortcuts for starting Darktable with sync and sync only
- 📊 Desktop notifications
## Requirements 📋
- Bash 4+
- rsync
- SSH key-based auth to NAS
- systemd (for automatic sync)
## Installation 💻
```bash
git clone https://github.com/MaTr74/darktable-sync.git
cd darktable-sync
cp .env.example .env
nano .env # Edit with your values
chmod +x install.sh uninstall.sh
./install.sh
```
## Usage 🚀
- Start via desktop shortcut: "Darktable with Sync"
- Manual sync: Start via desktop shortcut: "Darktable Sync Only"
## Uninstall 🧹
```bash
./uninstall.sh
```
## License 📄
MIT License - see [LICENSE](LICENSE)
Executable
+265
View File
@@ -0,0 +1,265 @@
#!/bin/bash
set -e
### 🔧 Default Configuration (can be overridden by .env file)
NAS_USER="${NAS_USER:-$USER}" # Default: current user
NAS_SSH_PORT="${NAS_SSH_PORT:-22}" # Default: standard SSH port
NAS_IP="${NAS_IP:-192.168.1.100}" # Default: common local network
NAS_DB_DIR="${NAS_DB_DIR:-/volume1/Darktable/darktable_db}"
NAS_PHOTO_DIR="${NAS_PHOTO_DIR:-/volume1/Darktable/photo_library}"
LOCAL_PHOTO_DIR="${PHOTO_DIR:-$HOME/Pictures/raw}"
LOCAL_DARKTABLE_DB_DIR="${DARKTABLE_DB_DIR:-$HOME/.config/darktable}"
BIN_DIR="${BIN_DIR:-$HOME/.local/bin}"
APPLICATIONS_DIR="$HOME/.local/share/applications"
SYNC_SCRIPT="$BIN_DIR/darktable_sync.sh"
WRAPPER_SCRIPT="$BIN_DIR/darktable_wrapper.sh"
DESKTOP_SHORTCUT="$APPLICATIONS_DIR/darktable-with-sync.desktop"
SYNC_ONLY_SHORTCUT="$APPLICATIONS_DIR/darktable-sync-only.desktop"
### 📁 Prepare folders
mkdir -p "$BIN_DIR"
mkdir -p "$HOME/.config/systemd/user"
### 🔄 Load .env if present (overrides defaults)
ENV_FILE=".env"
if [[ -f "$ENV_FILE" ]]; then
echo "📥 Loading configuration from .env file..."
set -a # automatically export all variables
source "$ENV_FILE"
set +a
fi
### 📝 Show effective configuration
echo "Using configuration:"
echo "NAS_USER: $NAS_USER"
echo "NAS_IP: $NAS_IP"
echo "NAS_SSH_PORT: $NAS_SSH_PORT"
echo "NAS_DB_DIR: $NAS_DB_DIR"
echo "NAS_PHOTO_DIR: $NAS_PHOTO_DIR"
echo "PHOTO_DIR: $LOCAL_PHOTO_DIR"
echo "DARKTABLE_DB_DIR:$LOCAL_DARKTABLE_DB_DIR"
echo "BIN_DIR: $BIN_DIR"
### 🔍 Check dependencies
echo "🔍 Checking requirements..."
REQUIRED_CMDS=("rsync" "notify-send" "ping" "darktable" "systemctl" "xdg-user-dir")
for cmd in "${REQUIRED_CMDS[@]}"; do
if ! command -v "$cmd" >/dev/null 2>&1; then
echo "❌ Error: '$cmd' is not installed."
echo "👉 You can install it with: sudo apt install $cmd"
exit 1
fi
done
# Check folder presence
if [ ! -d "$LOCAL_PHOTO_DIR" ]; then
echo "❌ Local photo folder does not exist: $LOCAL_PHOTO_DIR"
echo "👉 Please create it using: mkdir -p \"$LOCAL_PHOTO_DIR\""
exit 1
fi
if [ ! -d "$LOCAL_DARKTABLE_DB_DIR" ]; then
echo "❌ Darktable database path does not exist: $LOCAL_DARKTABLE_DB_DIR"
echo "👉 Start Darktable once or create the directory manually."
exit 1
fi
# Check if NAS is reachable
if ping -c 1 "$NAS_IP" &>/dev/null; then
echo "✅ NAS is reachable: $NAS_IP"
if ! ssh -p "$NAS_SSH_PORT" "$NAS_USER@$NAS_IP" "[ -d '$NAS_DB_DIR' ]"; then
echo "❌ Remote directory missing on NAS: $NAS_DB_DIR"
echo "👉 Please create it or adjust the path."
exit 1
fi
if ! ssh -p "$NAS_SSH_PORT" "$NAS_USER@$NAS_IP" "[ -d '$NAS_PHOTO_DIR' ]"; then
echo "❌ Remote directory missing on NAS: $NAS_PHOTO_DIR"
echo "👉 Please create it or adjust the path."
exit 1
fi
else
echo "⚠️ NAS not reachable: $NAS_IP"
echo "➡️ Sync will fail until NAS is online."
fi
### ✅ Create sync script
cat > "$SYNC_SCRIPT" <<EOF
#!/bin/bash
log() {
echo "\$1"
}
count_synced_files() {
LOG="\$1"
DIRECTION="\$2"
case "\$DIRECTION" in
"up")
COUNT=\$(grep -E '^(<f|cd)' "\$LOG" | wc -l)
;;
"down")
COUNT=\$(grep -E '^(>f|cd)' "\$LOG" | wc -l)
;;
*)
COUNT=0
;;
esac
echo "\$COUNT"
}
SHOW_NOTIFY_START_STOP=false
if [[ "\$1" == "--with-notify-start-stop" ]]; then
SHOW_NOTIFY_START_STOP=true
fi
if ping -c 1 $NAS_IP &>/dev/null; then
export DISPLAY=:0
SYNC_LOG=\$(mktemp)
log "🔃 NAS 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 NAS..."
UPLOAD_LOG1=\$(mktemp)
rsync -avh --itemize-changes -e "ssh -p $NAS_SSH_PORT" "$LOCAL_DARKTABLE_DB_DIR/" "$NAS_USER@$NAS_IP:$NAS_DB_DIR/" 2>&1 | tee -a "\$SYNC_LOG" "\$UPLOAD_LOG1"
SENT1=\$(count_synced_files "\$UPLOAD_LOG1" "up")
rm "\$UPLOAD_LOG1"
log "⬆️ Uploading photos to NAS..."
UPLOAD_LOG2=\$(mktemp)
rsync -avh --itemize-changes -e "ssh -p $NAS_SSH_PORT" "$LOCAL_PHOTO_DIR/" "$NAS_USER@$NAS_IP:$NAS_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 NAS..."
DOWNLOAD_LOG1=\$(mktemp)
rsync -avh --itemize-changes -e "ssh -p $NAS_SSH_PORT" "$NAS_USER@$NAS_IP:$NAS_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 NAS..."
DOWNLOAD_LOG2=\$(mktemp)
rsync -avh --itemize-changes -e "ssh -p $NAS_SSH_PORT" "$NAS_USER@$NAS_IP:$NAS_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 "❌ NAS not reachable skipping sync."
fi
EOF
chmod +x "$SYNC_SCRIPT"
### ✅ Create wrapper script with sync + notify
cat > "$WRAPPER_SCRIPT" <<EOF
#!/bin/bash
log() {
echo "\$1"
}
log "🔄 Sync starting before Darktable..."
"$SYNC_SCRIPT" --with-notify-start-stop
log "🚀 Launching Darktable..."
darktable
log "🛑 Darktable closed."
log "🔄 Sync starting after Darktable..."
"$SYNC_SCRIPT" --with-notify-start-stop
log "Darktable Sync" "✅ Final sync completed." -t 3000
EOF
chmod +x "$WRAPPER_SCRIPT"
### ✅ Create systemd service and timer
cat > "$HOME/.config/systemd/user/darktable-sync.service" <<EOF
[Unit]
Description=Darktable Sync
[Service]
Type=oneshot
ExecStart=$SYNC_SCRIPT
EOF
cat > "$HOME/.config/systemd/user/darktable-sync.timer" <<EOF
[Unit]
Description=Run Darktable sync every 5 minutes
[Timer]
OnBootSec=2min
OnUnitActiveSec=5min
Unit=darktable-sync.service
[Install]
WantedBy=default.target
EOF
### ✅ Create desktop shortcut
cat > "$DESKTOP_SHORTCUT" <<EOF
[Desktop Entry]
Name=Darktable with Sync
Exec=$WRAPPER_SCRIPT
Icon=darktable
Type=Application
Terminal=false
Categories=Graphics;
StartupNotify=true
Keywords=Darktable;Photo;Sync;NAS;Rsync;
EOF
chmod +x "$DESKTOP_SHORTCUT"
### ✅ Create additional desktop shortcut (sync only)
cat > "$SYNC_ONLY_SHORTCUT" <<EOF
[Desktop Entry]
Name=Darktable Sync Only
Exec=$SYNC_SCRIPT --with-notify-start-stop
Icon=darktable
Type=Application
Terminal=false
Categories=Utility;
StartupNotify=true
Keywords=Darktable;Photo;Sync;NAS;Schedule;
EOF
chmod +x "$SYNC_ONLY_SHORTCUT"
### ▶️ Enable systemd timer
systemctl --user daemon-reexec
systemctl --user enable --now darktable-sync.timer
### ✅ Summary
echo ""
echo "✅ Setup complete."
echo ""
echo "🖱 You can start Darktable via 'Darktable with Sync' in your application menu."
echo "🕒 Sync will run every 5 minutes automatically when the NAS is online."
Executable
+27
View File
@@ -0,0 +1,27 @@
#!/bin/bash
# Load possible custom paths from install-time .env if exists
if [[ -f ".env" ]]; then
export $(grep -v '^#' .env | xargs)
fi
BIN_DIR="${BIN_DIR:-$HOME/.local/bin}"
APPLICATIONS_DIR="${APPLICATIONS_DIR:-$HOME/.local/share/applications}"
SYSTEMD_USER_DIR="${SYSTEMD_USER_DIR:-$HOME/.config/systemd/user}"
# Stop and disable systemd service
echo "🛑 Removing systemd services..."
systemctl --user disable --now darktable-sync.timer >/dev/null 2>&1 || true
systemctl --user daemon-reload
# Remove files
echo "🧹 Cleaning up installed files..."
rm -fv \
"$BIN_DIR/darktable_sync.sh" \
"$BIN_DIR/darktable_wrapper.sh" \
"$APPLICATIONS_DIR/darktable-with-sync.desktop" \
"$APPLICATIONS_DIR/darktable-sync-only.desktop" \
"$SYSTEMD_USER_DIR/darktable-sync.service" \
"$SYSTEMD_USER_DIR/darktable-sync.timer"
echo "✅ Uninstall complete. Config files in ~/.config/darktable remain untouched."