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:
- Photos don't know what album(s) they are in
- Albums know what photos they contain
- The Applescript dictionary is wonky in how it reads containers.
- 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.
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.