Quick keyword tagging in iPhoto

Written . Tagged AppleScript, OS X, Ruby.

Tagging in iPhoto is an annoying matter of dragging photos to fields or checking boxes.

There is a Keyword Assistant that does not run in Intel mode, and a Keyword Tool that didn’t do it for me inbetween the crashes.

I wrote my own AppleScript (with some nested Ruby for string manipulation) that suits me better.

2006-09-12: Keyword Assistant now works with Intel.

I hooked it to ⇧⌃T using Quicksilver. Just select the images to tag or untag and run the script.

A dialog prompts for the keywords. Start with a minus to untag. Separate keywords with commas.

Keywords you use must exist within iPhoto already. The script tries matching what you type to extant keywords in two ways, in descending order of preference. Matching is case-insensitive.

The first method is a prefix match. “foo” is e.g. resolved to “Foobar”.

The second method is an initials match. “fb” is e.g. resolved to “Foo Bar” unless there is e.g. a “Fbsomething” (prefix match).

If several keywords match using the same method (e.g. two keywords share a prefix) the alphabetically first one is used.

So If the iPhoto keywords available are “Henrik, Johan, Johanna, Linnea, Linnea K”, I could type “h,j,lk” to tag with “Henrik,Johan,Linnea K”. To tag with “Johanna” I’d need to type at least “Johann”. To untag all, I could type “-h,j,lk”.

The script doesn’t handle non-ASCII well, because text encoding was too much pain in this case, and because I don’t need it myself.

Download iPhoto tag.scpt. Code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
-- iPhoto quick keyword tagging
-- by Henrik Nyh <http://henrik.nyh.se> 2006-08-14
-- Free to modify, but please credit.
--
-- KNOWN ISSUES:
-- Does not handle non-ASCII very well.

tell app "iPhoto"
  -- Fetch keywords
  set the keys to the name of every keyword whose name is not "_Favorite_"

  -- Prompt
  activate
  set theDialog to display dialog "Tags to apply, comma-separated:" default answer "" buttons {"Cancel", "Tag"} default button "Tag"
  set tags to text returned of theDialog
end tell

-- Add or remove tags?

set AppleScript's text item delimiters to ""
set charsTags to the text items of tags
set removeTags to (the first item of charsTags is "-")
if removeTags then set the tags to the rest of charsTags as string

-- Match abbreviated keywords to actual keywords in Ruby

set AppleScript's text item delimiters to "\",\""
set the keys to the keys as string

set the expandedTags to do shell script "ruby -e '
tags = \"" & tags & "\".split(/\\s*,\\s*/)
normalized = {}
[\"" & keys & "\"].each {|kw| normalized[kw.downcase] = kw}

tag_with = tags.map do |tag|
  normalized[
    normalized.keys.sort.find {|kw| kw.index(tag)==0} ||  # prefix match
    normalized.keys.sort.find {|kw| kw.gsub(/(\\w)\\w*\\s*/, %q{\\1}) == tag}  # initials match
  ]
end.compact.uniq.join(%{,})
puts tag_with
'"

-- Split keywords
set AppleScript's text item delimiters to ","
set expandedTags to the text items of expandedTags

-- Apply or remove keywords
repeat with tag in expandedTags
  if removeTags -- remove keywords
    tell app "iPhoto"
        repeat with pic in (selection as list)
          remove keyword tag from pic
        end repeat
    end tell
    -- iPhoto seems to need this to refresh the display
    tell app "SystemUIServer" to activate
    tell app "iPhoto" to activate
  else -- apply keywords
    tell app "iPhoto" to assign keyword string tag
  end if
end repeat