Tag Archives: macOS

AppleScript Folder Action to auto-trim folder

Published / by Kevin / Leave a Comment

[UPDATE] This does not work as expected. Doesn’t seem to run as a plain AppleScript and when re-worked into an Automator folder action workflow (word salad anyone?) it might work… It has and it also hasn’t. But, even when it has I have the sneaking suspicion iCloud does not propagate deletes from macOS. Wouldn’t be the only inexplicable behavior from that service.

I have a security camera that FTPs a new folder everyday into a local iCloud folder on my home server. FYI, the real location of your iCloud Drive is within ~/Library/Mobile Documents, which is a package folder.

This storage process has not proved ideal since iCloud doesn’t handle automation very well. It works with Finder windows and Save dialogs, but background processes not so much. The result for me has been constant reminders that my iCloud drive is “full” despite deleting the bulk of folders. Most of the space never comes back and many times folders just “deleted” reappear.

Maybe adding another automated process to this soup is not going to help and may make things worse (will bird even notice when items are removed this way?), but what the hell. This script will delete files oldest first until the number of items it counted when it was called reaches the “keep” count (set to “5” here).

This may be attached to any folder, but be aware that it does not send files to the Trash. They are removed with rm.

-- http://strawhousepig.net/

-- Number of items to keep.
property keep : 5

-- Number of seconds until the script may run again (incase of runaway process adding to folder).
property cycle : 10

-- plist to store time last run.
property the_dom : "applescript.folderaction.trimfolder"
property the_key : "epoch"
property the_val : (do shell script "date '+%s'")

on adding folder items to this_folder after receiving added_items
  do_it(this_folder)
end adding folder items to

on run
  set this_folder to choose folder
  do_it(this_folder)
end run

on do_it(this_folder)
  if my read_array(the_dom, the_key, the_val, cycle) is true then return
  do shell script "defaults write " & the_dom & " " & the_key & " " & quoted form of the_val
  tell application "Finder"
    set _items to items of this_folder
    set _items to (sort _items by creation date)
  end tell
  if (count of _items) is greater than keep then
    try
      repeat until (count of _items) is equal to keep
        do shell script "rm -R " & quoted form of POSIX path of (item 1 of _items as alias) -- Will bird sync when using 'rm'?? :/
        set _items to rest of _items
      end repeat
    on error theErr number theNum
      if theNum is not -128 then display dialog theErr giving up after 60
    end try
  end if
end do_it

on read_array(the_dom, the_key, the_val, cycle)
  try
    set last_run to (do shell script "defaults read " & the_dom & " " & the_key)
  on error -- Will error if there is nothing to read.
    return false
  end try
  if the_val - last_run is less than cycle then
    return true
  else
    return false
  end if
end read_array

Sync folders when drive is plugged in.

Published / by Kevin / Leave a Comment

[UPDATE] Didn’t realize how dangerous this is if you actually need to get a backup file(s) off the drive rather than restore from catastrophic failure. Now prompts to confirm sync before proceeding. Should also prompt to eject when complete.

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. Its 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.

Because I can: rsync manual

-- 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"}

-- rsync options. I use '-rtE' (recursive, preserve times, copy Extended attributes/resource forks)
property rsync_options : "-rtE --delete" -- For mirroring the contents of source dir.
-- property rsync_options : "-rtE --ignore-existing" -- For keeping deleted files on backup. WARNING: also dupes files moved within the source dir.
-- See the rsync manual (man rsync) for more info.

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

on adding folder items to this_folder after receiving added_items
  try
    repeat with i in added_items
      if name of (info for i) is trigger then
        if the result of (display dialog trigger & " drive connected. Would you like to sync available source folders now?" giving up after 600) is not false then
          do_it(skip)
          do shell script "logger 'Sync backup folders: Complete'"
          ejectit(trigger)
        end if
        exit repeat
      end if
    end repeat
  on error theErr
    do shell script "logger 'Sync backup folders: " & theErr & "'"
  end try
end adding folder items to

on run
  do_it(skip)
  do shell script "logger 'Sync backup folders: Complete'"
  ejectit(trigger)
end run

on ejectit(trigger)
  if the result of (display dialog "Backup sync complete. Eject drive '" & trigger & "'?") is not false then
    tell application "Finder" to eject trigger
  end if
end ejectit

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
      try
        do shell script "/usr/local/bin/rsync " & rsync_options & quoted form of i & " '" & trigger_path & "/'"
        set end of skip to i as text
      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
            set end of skip to i as text
            do_it(skip)
          end if
        end if
      end try
    end if
  end repeat
  set skip to {}
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.

Set creation and modification date

Published / by Kevin / Leave a Comment

This is to correct file times on photos that were imported from cameras with bad date settings or when they have been transferred to-and-fro and picked up the wrong data some where or after re-encoding high bit rate video to save a few GBs. [This does not alter the date atoms encoded in (most?) video files. ffmpeg can do that, however.]
Continue reading