diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..f085658 --- /dev/null +++ b/.env.example @@ -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" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..808978b --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +*.log +*.tmp +.env +.DS_Store +thumbs.db +.idea/ +.vscode/ diff --git a/LICENSE b/LICENSE index 60252c5..cd78e80 100644 --- a/LICENSE +++ b/LICENSE @@ -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 diff --git a/README.md b/README.md new file mode 100644 index 0000000..bd087a1 --- /dev/null +++ b/README.md @@ -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) diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..60ab862 --- /dev/null +++ b/install.sh @@ -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" <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" < "$HOME/.config/systemd/user/darktable-sync.service" < "$HOME/.config/systemd/user/darktable-sync.timer" < "$DESKTOP_SHORTCUT" < "$SYNC_ONLY_SHORTCUT" </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."