#!/bin/bash # Enhanced NAS cleanup script with thumbnail protection # Usage: ./nascleanup.sh /volume1/Hydra/path/to/directory if [ $# -eq 0 ]; then echo "Usage: $0 " echo "Example: $0 /volume1/Hydra/Creative/artsy" exit 1 fi TARGET_DIR="$1" # Merge synoindexd layout ...//@eaDir/*.jpg into ...// (Synology Drive expects no inner @eaDir). flatten_indexer_layout() { local root="$1" [ -d "$root" ] || return 0 find "$root" -mindepth 2 -type d -name '@eaDir' -print0 2>/dev/null | while IFS= read -r -d '' NEST; do PARENT="$(dirname "$NEST")" [ -d "$NEST" ] || continue for f in "$NEST"/*; do [ -e "$f" ] || continue base="$(basename "$f")" [ "$base" = "SYNOINDEX_MEDIA_INFO" ] && continue mv -f "$f" "$PARENT/" 2>/dev/null || true done rm -rf "$NEST" 2>/dev/null || true done } echo "=== Enhanced Synology Thumbnail Cleanup ===" echo "Target directory: $TARGET_DIR" # 1. Stop indexing services temporarily echo "Stopping indexing services (best effort)..." # Non-login SSH often has a minimal PATH; binary lives in /usr/syno/sbin if [ -x /usr/syno/sbin/synoservicectl ]; then SYNOCTL=/usr/syno/sbin/synoservicectl elif command -v synoservicectl >/dev/null 2>&1; then SYNOCTL=synoservicectl else SYNOCTL="" fi if [ -n "$SYNOCTL" ]; then "$SYNOCTL" --stop synoindexd 2>/dev/null || true "$SYNOCTL" --stop pkgctl-SynoFinder 2>/dev/null || true elif command -v systemctl >/dev/null 2>&1; then # Plain `systemctl` as non-root usually fails silently; try sudo -n first (NOPASSWD). for u in synoindexd pkgctl-SynoFinder; do sudo -n systemctl stop "$u" 2>/dev/null || systemctl stop "$u" 2>/dev/null || true done else echo " -> Service control unavailable; skipping" fi # 2. Clear indexing queue to prevent regeneration echo "Clearing indexing queue (if accessible)..." if [ -w /var/spool ]; then rm -f /var/spool/syno_indexing_queue* 2>/dev/null || true fi # 3. Dry-run check (see what @eaDir directories exist) echo "=== Existing @eaDir directories ===" find "$TARGET_DIR" -type d -name '@eaDir' -exec echo '{}' \; # 4. Remove any existing @eaDir directories echo "=== Removing existing @eaDir directories ===" # Drop indexer sidecars first (often root-owned — passwordless sudo helps) if command -v sudo >/dev/null 2>&1; then sudo -n find "$TARGET_DIR" -name 'SYNOINDEX_MEDIA_INFO' \( -type f -o -type d \) -exec rm -rf '{}' \; 2>/dev/null || true fi find "$TARGET_DIR" -name 'SYNOINDEX_MEDIA_INFO' \( -type f -o -type d \) -exec chmod -R u+w '{}' \; 2>/dev/null || true find "$TARGET_DIR" -name 'SYNOINDEX_MEDIA_INFO' \( -type f -o -type d \) -exec rm -rf '{}' \; 2>/dev/null || true # First make them writable so we can remove them find "$TARGET_DIR" -type d -name '@eaDir' -exec chmod -R 755 '{}' \; 2>/dev/null || true find "$TARGET_DIR" -path '*/@eaDir/*' -type f -exec chmod 644 '{}' \; 2>/dev/null || true # Now remove them (2>/dev/null: races when parents disappear mid-find) find "$TARGET_DIR" -type d -name '@eaDir' -exec rm -rf '{}' \; 2>/dev/null || true # 5. Promote only TARGET_DIR/eaDir_tmp -> TARGET_DIR/@eaDir (thumbgen layout; not nested */eaDir_tmp) echo "=== Installing custom thumbnails ===" EADIR_TMP="$TARGET_DIR/eaDir_tmp" if [ -d "$EADIR_TMP" ]; then chmod -R 755 "$EADIR_TMP" 2>/dev/null || true find "$EADIR_TMP" -type f -exec chmod 644 '{}' \; 2>/dev/null || true echo " -> Flattening eaDir_tmp (indexer may have added ...//@eaDir/) before copy" flatten_indexer_layout "$EADIR_TMP" TARGET_AT="$TARGET_DIR/@eaDir" echo " -> Replacing $EADIR_TMP -> $TARGET_AT" rm -rf "$TARGET_AT" mkdir -p "$TARGET_AT" cp -R "$EADIR_TMP/." "$TARGET_AT/" echo " -> Flattening @eaDir after copy (same layout fix)" flatten_indexer_layout "$TARGET_AT" chmod -R 755 "$EADIR_TMP" 2>/dev/null || true find "$EADIR_TMP" -type f -exec chmod u+w '{}' \; 2>/dev/null || true find "$EADIR_TMP" -type d -exec chmod u+w '{}' \; 2>/dev/null || true if ! rm -rf "$EADIR_TMP" 2>/dev/null; then echo " -> rm eaDir_tmp failed; retrying with sudo -n" sudo -n rm -rf "$EADIR_TMP" 2>/dev/null || echo " -> WARNING: remove stale eaDir_tmp manually: sudo rm -rf \"$EADIR_TMP\"" fi else echo " -> No $EADIR_TMP; skipping install step" fi echo "=== Removing stray nested eaDir_tmp directories ===" find "$TARGET_DIR" -mindepth 2 -type d -name 'eaDir_tmp' -exec rm -rf '{}' \; 2>/dev/null || true # 6. Protect custom thumbnails with read-only permissions echo "=== Adding indexing exclusion hints ===" find "$TARGET_DIR" -type d -name '@eaDir' -exec sh -c 'touch "$1/.noindex" 2>/dev/null || true' _ {} \; 2>/dev/null || true # 7. Protect custom thumbnails with read-only permissions (after .noindex) echo "=== Protecting custom thumbnails ===" find "$TARGET_DIR" -type d -name '@eaDir' -exec chmod 555 '{}' \; 2>/dev/null || true find "$TARGET_DIR" -path '*/@eaDir/*' -type f -exec chmod 444 '{}' \; 2>/dev/null || true echo "=== Cleanup complete! ===" echo "Restarting indexing services (best effort)..." if [ -n "$SYNOCTL" ]; then "$SYNOCTL" --start synoindexd 2>/dev/null || true "$SYNOCTL" --start pkgctl-SynoFinder 2>/dev/null || true elif command -v systemctl >/dev/null 2>&1; then for u in synoindexd pkgctl-SynoFinder; do sudo -n systemctl start "$u" 2>/dev/null || systemctl start "$u" 2>/dev/null || true done fi echo "Note: If indexing was stopped and needs restarting, use DSM UI or: sudo systemctl restart synoindexd" echo "If rm failed with Permission denied: delete @eaDir and eaDir_tmp on your pasted Windows path (SMB), or: sudo rm -rf ${TARGET_DIR}/eaDir_tmp ${TARGET_DIR}/@eaDir — then re-run option 3."