Home > Enterprise >  MacOS Photos - use applescript to update keywords with the name(s) of the albums they are in
MacOS Photos - use applescript to update keywords with the name(s) of the albums they are in

Time:10-08

TLDR:
I would like to run an Applescript that will iterate newly added photos from a smart album to add the Album Name and Folder Name they are contained in as keywords on the photo.

Case:
My Structure in Photos is this:
My Albums¬
FolderName¬
AlbumName¬
List of Photos

I have created a smart album that is: "Any photos added in the last 90 days"

I would like the applescript to basically do this:

  • Select the photos in the Smart Album
  • Iterate each one, "ask" which Albums and folders they are in
  • Copy the Album names and folder names to append them to the 'keywords' of the photo.

Some things I know about the way Photos App works:

  1. Photos don't know what album(s) they are in
  2. Albums know what photos they contain
  3. The Applescript dictionary is wonky in how it reads containers.
  4. After research, I think the trick is to 'ask' all folders which albums have the selected photo, and then somehow capture the album name and the folder(s) where 'contains photo' is true. But I am seriously lost on how to make this

Those 3 things are tripping me up.
I am really hopeful that someone has done something like this and can help me understand the scripting around this one.

Ultimately, I'm trying to make photos more searchable using smart filters - so having keywords automatically appended will help a lot.

Cat in Photos app with keyword injection

Up to this point, I can get applescript to find a selection (manually chosen files) to iterate through, but I would really like to have it done by a named smart album.

tell application "Photos"
    set eachFile to {}
    set theSelections to (get selection) -- This gets the hilighted photos 
    repeat with i in theSelections
        set selectedPhotoId to get id of i
        set theKeywords to the ({keywords of i})
        
        display dialog theAlbum

        
    end repeat
    
end tell 

My effort so far has been to just figure out which dictionary terms could return some values for me, but I am missing many pieces so far.

CodePudding user response:

global theKeyWords

tell application "Photos"
    -- get the hilighted photos
    set theSelection to (get selection)
    repeat with i from 1 to count theSelection
        set theKeyWords to {}
        set selectionID to id of item i of theSelection
        -- first, get names of albums in the root
        set theKeyWords to theKeyWords & (name of albums whose id of media items contains selectionID)
        -- recursively get names of albums in the subfolders
        my getAlbumNames(its folders, selectionID)
        -- set the keywords of processed photo
        set keywords of media item id selectionID to theKeyWords
    end repeat
end tell

-- recursive handler 
on getAlbumNames(theFolders, selectionID)
    if theFolders is {} then return
    tell application "Photos"
        repeat with aFolder in theFolders
            tell aFolder
                set theKeyWords to theKeyWords & (name of albums whose id of media items contains selectionID)
                my getAlbumNames(its folders, selectionID)
            end tell
        end repeat
    end tell
end getAlbumNames

CodePudding user response:

Looping through all photos in a particular top-level album requires mainly that it be a top-level album.

set sourceAlbumName to "Last Fortnight"
repeat with recentPhoto in media items in album sourceAlbumName
    …
end repeat

The rest is more difficult, and for the reason you mentioned: you cannot, as far as I can tell, just ask for a list of all albums that contain a photo.

However, you can ask a folder whether it contains any albums that contain that photo.

So what you end up needing to do is loop through all folders (including the top-level one, the application itself) and find all albums that contain the photo. Add their names to the list of keywords. If there are folders in the folder, do the same for that.

And then, for your purposes, if any folder contains albums that contain the photo, or contains folders that contain albums that contain the photo, etc., add that folder to the list of keywords as well.

This pretty much requires a recursive handler.

What this example handler does is, it first gets all of the albums of the folder it’s been given, and copies those to a list of new keywords. It then goes through each folder in the folder it’s been given, and calls the handler with that folder. If it gets something back, then it adds what it gets back as well as the folder to the list of new keywords.

to getSubcontainers for parentFolder given id:photoId
    set newKeywords to {}
    --using terms from because tell application "Photos" has trouble
    --when the parentFolder *is* application "Photos"
    using terms from application "Photos"
        tell parentFolder
            --get the albums that contain this photo
            set containingAlbums to get (albums whose id of media items contains photoId)
            repeat with containingAlbum in containingAlbums
                copy name of containingAlbum to end of newKeywords
            end repeat
            
            --get folders that contain albums that contain this photo
            repeat with currentFolder in folders
                tell me to getSubcontainers for currentFolder given id:photoId
                
                --if some album contained within this folder contained the photo
                --then this folder is also a keyword
                if the result is not {} then
                    set newKeywords to newKeywords & the result
                    copy name of currentFolder as string to end of newKeywords
                end if
            end repeat
        end tell
    end using terms from
    return newKeywords
end getSubcontainers

Here’s the top-level of the script. Note that by default it is set to stop after doing one photo, and to not actually change the keywords.

I called the test album Last Fortnight, because I didn’t want 90 days worth of photos in it while I was testing.

The script makes sure not to add keywords in multiple times, as well as to not add the source album as a keyword, since presumably it doesn’t count.

--how many photos should we change before we bail?
set maxPhotos to 1
--if testing is true, do not actually change the keywords
set testing to true
--the album to pull photos from for changing
set sourceAlbumName to "Last Fortnight"

tell application "Photos"
    set currentPhotoCount to 0
    repeat with recentPhoto in media items in album sourceAlbumName
        set currentPhotoCount to currentPhotoCount   1
        tell me to getSubcontainers for application "Photos" given id:id of recentPhoto
        set containerNames to the result
        
        --create a new list of keywords that includes both existing keywords on the photo
        --and the new folder and album names
        set photoKeys to keywords of recentPhoto
        repeat with newKey in containerNames
            set newKey to newKey as string
            if newKey is not in photoKeys and newKey is not sourceAlbumName then
                copy newKey to the end of photoKeys
            end if
        end repeat
        
        --set the photo to have the new list of keywords
        if not testing then
            set keywords of recentPhoto to photoKeys
        end if
        
        --I'm exiting the repeat so that I can see that it worked on the first x photos in the smart album
        --instead of waiting for it to go through all photos
        --and also to avoid incorrectly changing too many photos
        if currentPhotoCount ≥ maxPhotos then
            exit repeat
        end if
    end repeat
end tell

--for testing, to see what the new keywords will be
if testing then
    get photoKeys
end if

Note that as far as I can tell there is no defined way of telling if an album is a smart album or a manually-managed album. If that’s important to you, I suspect trying to add a photo to the album will (a) result in a catchable error, and (b) make the script take a lot longer to run.

If you don’t want smart albums added as keywords, I’d suggest either marking their names in some way so as to know they’re smart, and then excluding them that way (and newKey does not end in " Smart"), or just keeping a list of albums to exclude as keywords, and checking the key against that list (and newKey is not in invalidKeywords). I’d probably do the latter.

  • Related