Using JavaScript to Find Photos Without Keywords

There is no way in Photos to get a list of all of the photos that have no keyword tags applied to them. But with a little JavaScript in Automator you can add this command to Photos and use it with all photos or any selection of photos.

Video Transcript / Captions
Closed captioning for this video is available on YouTube: Using JavaScript to Find Photos Without Keywords.

I was recently asked if there was a way in Photos to select all of the photos that do not have a keyword applied. Let me show you what I mean.

Here's a sample Photos album. I've got keywords applied to some of these photos but not others. I can use Command I and then select a photo and see that there's a keyword applied to this one. There's one to this one, but say not to this one. There's no keyword there at all. Now it's easy to use a SmartAlbum to find out something like if the album is not any then create a SmartAlbum with these in it. So this would put all the photos that are not in any album there. But there's no way to do that for keyword. For keyword you would go in there and you can say is or is not a specific keyword but in order to actually find all the ones that have no keyword applied you would have had to add the plus button, say All, and then continue to add keyword is not, and then every single one. It isn't that bad if you have six keywords like I have here but if you six hundred keywords like you may have in a complex photos album or library that, you know, has all your photos in it and you like to add lots of keywords, it's just not possible.

So, how can you do it? There doesn't seem to be a way. What's even more frustrating is you can go to View, Metadata, Keywords like I have checked there and you can actually see this little tag on any photos that have keywords applied and there's no tag on the ones that don't. So you can actually see them but there is no way to gather them all together so you can, say, maybe have an album that you can then work on applying keywords or maybe if it's a special project delete the photos that have no keywords applied to them.

But, you can do this if you use some scripting. So you know that there are two ways to do scripting on the Mac. One is Script Editor and the other is in Automator. I'm going to use Automator for this because it will allow me to very easily attach this as a command that appears in Photos. So I'm going to run Automator with a Command space, type Automator, and bring up Automator. I'm going to create something that's a new service. The service will allow me to add a menu item in Photos that has a link to this script. I'm going to say Choose.

So now I've got a new script here that I can start. I'm going to go and say I want this to receive no input but not just any application. I want this to be something specific that works in Photos. So I'm going to go and select Photos from the list here. Great. No input in Photos. Now I can start to drag and drop selections. So what I want to do is that instead of using something built in here in Automator I'm actually going to type JavaScript. Start typing it here and you can see that there is a Run JavaScript action. I'll put that in and see here I can actually type my JavaScript in here which is where we'll put our scripts.

So to save some time I'm actually going to copy and paste here. I'm going to select this and delete it. The first line of the script is actually just going to define a variable called Photos as the Application Photos. So now we can refer to commands having to do with the photos app very easily. The second command is going to simply create a new album. So this is called noKeywordAlbum. It's just a name I chose as a variable inside the script and it's going to use the Photos.make command and it's going to have two parameters inside of an object here. It's going to say, going to create a new album, that's the type of thing it's going to create, and the named is going to be No Keywords. So that's what I'm going to call this album and it'll just appear. If I would have run this at this point it would create an album here that would be called No Keywords and that's all it'll do. So let's test it. You can see it did it right there. Created this No Keywords album. I'll go in here and I'll delete the album so we can start off fresh but it's nice to test the script that way.

Next we're going to go and start to go through all the photos in the Photos app. So I'm going to create a variable called selectedPhotos, again my name that I chose, and set to Photo.selection() with the parentheses there that's basically going to say select all of the photos that I have selected in the app. This is useful because you don't want it to have to automatically assume you want to go through the entire photos library but you can easily choose all your photos. Just go to All Photos, Command A, and you have them all selected even if you have a hundred thousand photos Command A will select them all.

But if you did want to run the script on a small selection like say all of the photos in a specific album that had no keyword you can do that as well by going to the album, selecting all just inside that album and the script will work for that. So that's really handy. So next we're going to loop through all of the photos. So I've done a for loop here. I've created a variable called photo, lowercase p there, of selected Photos. So it's going to basically loop through setting the variable photo to each photo in selected photos. Each time it loops I want it to grab the keywords inside of the photo. So there I'm using the photo right there, photo.keywords, it's getting all those and putting them in the variable keywords.

Next I'm going to check to see if that's null because if there are no keywords assigned to that photo then keywords is going to be equal to null. This is where we want to do something. We're going to use the add command. Photos.add and we're going to take an array, that's what those brackets are, of that one photo and add it to the noKeyword album which is what we created here that new album called No Keywords. Then we're going to simply close this if statement and close the loop and that's all it needs to do. It loops through all the photos, adds any that have keywords equal to null to that new album we created.

Now we can test this. I'm going to hit the play button. First let's bring the photos to the front here. Bring Automator on top of that and we'll see what happens when I hit play. You can see it created that album called No Keywords. Sure enough if we look in it there are five photos and these are indeed the five photos that have no keywords applied to it. So that last thing we need to do is Save. Because we set this up as a service in the Photos app I hit Command S, to save. It'll ask me the name it so I'm going to call it Create Album with Photos that have No Keywords and hit Save. Now when I switch over to albums and I go to Photos, services, there it is. I can execute this script, this Automator script here, just by using this. I don't even have to have Automator open. So I can go to Automator here and Quit because I've saved that. I can now go into Photos and I can do it again. Watch what happens when I do it again by the way. It's smart enough to actually create an album called No Keywords with parentheses one in it. Now I had nothing selected. Let's try it again. Let's select all these photos, Photos, Services, Create them, No Keywords two and it has those in there.

So I can actually now rename these really easily so that I can say take an album, find all the photos that have no keywords, rename that to something, do it in another album or another selection. All that. It's very easy and versatile. All done with just that little bit of scripting.

Here is the text of the script:

function run(input, parameters) { var Photos = Application("Photos"); Photos.includeStandardAdditions = true var noKeywordAlbum = Photos.make({new:"album",named:"No Keywords"}); var selectedPhotos = Photos.selection(); for (var photo of selectedPhotos) { var keywords = photo.keywords(); if (keywords == null) { Photos.add([photo],{to:noKeywordAlbum}); } } }

Comments: 11 Responses to “Using JavaScript to Find Photos Without Keywords”

    2 years ago

    Thanks Gary, I found this useful. Do you think something similar could work to find photos with “unnamed” \ “click to name” faces?

    2 years ago

    David: You don’t really need this for faces, as the functionality inside Photos should work for that. But you can look in the Script Editor library window to see what other properties are available for the Photos app.

    2 years ago

    Outstanding post Gary. I’ll be using these ideas. Thanks for a great blog with very useful information.

    2 years ago

    thank you, I’m always amazed how many hidden gems are within the Mac OS

    2 years ago

    I wondered if you might record these commands in a simple cut and paste form for those of us not into Javascript ? Your selected words seem most adequate

    2 years ago

    Dave: I put them up under the video above.

    2 years ago

    Gary, thank you. My library is arranged by “Title,” but sometimes snaps taken on other devices enter All Photos w/o title and I don’t notice until they’re buried. Could I use this process/service by following your instructions but substituting “Title” for “Keyword”?

    2 years ago

    Gary, I re-watched the video and had the idea that I should check out creating a smart album more fully, and I see that there IS an option right there for creating an album “Title” and “is empty.” So, your video helped me answer my own question! Thank you!

    2 years ago

    I have a lot of photos whose file name should be the title. I am trying to write a Javascript but am having difficulty with assigning the title to a variable. If the Title is null, set the Title to the filename.

    2 years ago

    Bob: Structures like “the Title” is AppleScript. Look in the Script Editor library to find JavaScript functions, but make sure you switch to JavaScript from AppleScript.
    For reading the title it is actually .name() and for setting it it is .name. You can get the file name with .filename(). You may want to check for both null and “” (empty string).

    2 years ago

    I done did it. Grin.
    I post links to many of your posts to our club’s web pages.
    Thanks again.

Comments Closed.