Monthly Archives: June 2018

Sync folders when drive is plugged in.

Published / by Kevin / Leave a Comment

The previous AppleScript post is an earlier version of nearly the same thing. I posted it here to be able to write a new script for where I work. It’s intention is to keep an on-site duplicate of an off-site backup. This can of course be used for simple backups and/or folder syncing operations if given the appropriate trigger(s).

To have this script run automagically attach it as a Folder Action to the Volumes folder (/Volumes).

Also, as the previous post, this script calls an rsync binary in a custom install location. Please be aware of that.

-- The name of the volume to watch for.
property trigger : "Backup"

-- The paths of the folders to sync. Drag-n-drop supported (choose "Alias").
property theFolders : {"/Do not/forget to/add quotes around", "/And/commas between/items"}

-- See the rsync manual (man rsync) for more info.
property rsync_options : "-rtE --ignore-existing"

property skip : {} -- Leave empty. Meant to hold items from theFolders which gave rsync an error so we can start over but then ignore items in this list.

on adding folder items to this_folder after receiving added_items
  repeat with i in added_items
    if i is trigger then
      do_it(skip)
      return
    end if
  end repeat
end adding folder items to

on run
  do_it(skip)
end run

on do_it(skip)
  tell application "System Events" to set trigger_path to POSIX path of every disk whose name is trigger -- When a drive is mounted and its previous mount point was not removed the new mount point will be given a unique number to follow the name (which isn't shown in the Finder), e.g. "/Volumes/Backup 1". This asks the System for the path rather than us blindly using text.
  if trigger_path is {} then
    display dialog "The drive '" & trigger & "' does not appear to be mounted. :("
    return
  end if
  set rsync_options to rsync_options & " "
  repeat with i in theFolders
    tell application "Finder"
      if not (exists (i as POSIX file)) then
        set end of skip to i as text
      end if
    end tell
    log skip
    if i is not in skip then
      set end of skip to i as text
      try
        do shell script "/usr/local/bin/rsync " & rsync_options & quoted form of i & " '" & trigger_path & "/'"
      on error the_err
        if i is last item in theFolders then
          display dialog "rsync encountered a problem: " & the_err
        else
          if button returned of (display dialog "rsync encountered a problem: " & the_err & return buttons {"Cancel", "Continue"}) is "Continue" then
            do_it(skip)
          end if
        end if
      end try
    end if
  end repeat
end do_it

This script includes error handling that isn’t well tested. Getting it to work with it at all was a bit of a trick (particularly if not (exists (i as POSIX file)) since if not (exists POSIX file i) does not work as expected). So that whole “skip” idea may not work as intended if you are asked to continue. The last do_it(skip) call may need to be my do_it(skip) instead.

Auto-backup of Photos Library.photoslibrary when portable drive is attached

Published / by Kevin / Leave a Comment

This script relies on Folder Actions to fire it once it is attached to /Volumes and the appropriately named hard drive is plugged in. I use it to update my off-site photo backup drive.

Settings in the script are commented with instructions, which are hopefully clear enough. It does mention iPhoto, because it was written when iPhoto was a thing. A good thing, a great thing even. Now… *meh*

The options for rsync are fairly standard (recursive, preserve times, extended attributes, and skip files already on the backup). The path to rsync is from OS X shipping with a version of rsync that was unbearably slow and me installing a newer version. I’m not sure when Apple ditched the older rsync version, but it sure wasn’t soon enough. I’m leaving it here for example purposes, but this script will fail if you don’t have an rsync binary there.

-- The name of the physical disk.
property top_drive : "Portable HDD"

-- The name of the disk image file.
-- If it is beyond root of top_drive, the (non-POSIX) path will need to be added. ie "backups:iPhoto backup.dmg"
property iPhoto_dmg : "Photos backup.sparseimage"

-- The name of the disk image drive.
property iPhoto_drive : "Photos backup"

-- POSIX path to iPhoto Library (Default is ~/Pictures/iPhoto Library).
property iPhoto_lib : quoted form of (POSIX path of ((path to pictures folder) as text) & "Photos Library.photoslibrary")

-- rsync options
property rsync_options : "-rtE --ignore-existing"

-- Path to rsync binary.
property rsync_path : "/usr/local/bin/" -- Leave blank ("") if not using a user installed version of rsync.

on logErr(the_msg, the_num)
  do shell script "logger '" & "iPhoto backup: " & the_msg & " (" & the_num & ")'"
end logErr

on adding folder items to this_folder after receiving added_items
  set new_item to item 1 of added_items as text
  if new_item is (top_drive & ":") then
    tell application "Finder"
      try
        open top_drive & ":" & iPhoto_dmg as alias
        delay 10
      on error errMsg number errNum
        my logErr(errMsg, errNum)
      end try
    end tell
    set n to 0
    try
      set rsync_options to rsync_options & " "
      repeat until n = 10
        tell application "Finder" to set all_drives to name of every disk
        if iPhoto_drive is in all_drives then
          set start_time to current date
          set rsync_it to (do shell script rsync_path & "rsync " & rsync_options & iPhoto_lib & " " & quoted form of ("/Volumes/" & iPhoto_drive))
          set total_time to (current date) - start_time
          my logErr("Done", "" & total_time & " sec")
          exit repeat
        else
          delay 6
          set n to n + 1
        end if
      end repeat
      tell application "Finder" to eject disk iPhoto_drive
      display dialog "iPhoto backup complete." & return & return & "You may now eject the disk \"" & top_drive & "\"." with icon 2
    on error errMsg number errNum
      my logErr(errMsg, errNum)
      display dialog "Oops! An error occurred. :(" & return & return & errNum & " : " & errMsg with icon 0
    end try
  end if
end adding folder items to