Skip to main content
Known Participant
April 23, 2025
Question

Trying to write a plugin

  • April 23, 2025
  • 3 replies
  • 447 views

I'm trying to write a plugin that will take the Geolocation information  (city, state, country) and write those as keywords.

 

The plugin is below.  When I ran the plugin i got an error : "An internal error has occurred: Unknown key: "City"    Thoughts, ideas?

 

--------------------------------------------------------

 

local LrDialogs = import 'LrDialogs'
local LrTasks = import 'LrTasks'
local LrApplication = import 'LrApplication'
local LrProgressScope = import 'LrProgressScope'

LrTasks.startAsyncTask(function()
    local catalog = LrApplication.activeCatalog()
    local photos = catalog:getTargetPhotos()

    if #photos == 0 then
        LrDialogs.message("No photos selected", "Please select one or more photos.", "info")
        return
    end

    local progress = LrProgressScope {
        title = "Copying location metadata to keywords...",
    }

    catalog:withWriteAccessDo("Add Geo Keywords", function(context)
        for i, photo in ipairs(photos) do
            progress:setPortionComplete(i, #photos)
            progress:setCaption("Processing photo " .. i .. " of " .. #photos)

            local city = photo:getRawMetadata("City") or ""
            local state = photo:getRawMetadata("State") or ""
            local country = photo:getRawMetadata("Country") or ""

            local newKeywords = {}
            for _, value in ipairs({ City, State, Country }) do
                value = value:match("^%s*(.-)%s*$") -- trim whitespace
                if value ~= "" then
                    table.insert(newKeywords, value)
                end
            end

            if #newKeywords > 0 then
                local existingKeywords = photo:getRawMetadata("keywords") or {}
                local keywordSet = {}

                for _, k in ipairs(existingKeywords) do
                    keywordSet[k:getName()] = true
                end

                for _, kw in ipairs(newKeywords) do
                    if not keywordSet[kw] then
                        local keyword = catalog:createKeyword(kw, {}, true, nil)
                        photo:addKeyword(keyword)
                    end
                end
            end

            if progress:isCanceled() then break end
        end
    end)

    progress:done()
    LrDialogs.message("Done", "Location data copied to keywords.", "info")
end)

3 replies

Legend
April 24, 2025

@tudor@apmadoc.net 

 

Your script has some errors: Lua variables are case-sensitive, so city is not the same as City.

 

for _, value in ipairs({ City, State, Country }) do

 

will not work because the local variables you have assigned are city, state and country. So, @jan roelofd68883271 is correct.

 

As @johnrellis has pointed out, the key for "state" is "stateProvince", not "State".

Known Participant
April 24, 2025

I made the modifications as suggested (see below).   The plugin completes with the "Done - locations data copied to keywords"  However the keywords never show up.  I tried reading metadata from the file (just in case), but no luck.

 

for the "existing keywords" line,  I tried both getFormattedMetadata and getRawMetadata.  That made no difference.   


Again, thanks for everyone's help on this!!

 

 

local LrDialogs = import 'LrDialogs'
local LrTasks = import 'LrTasks'
local LrApplication = import 'LrApplication'
local LrProgressScope = import 'LrProgressScope'

LrTasks.startAsyncTask(function()
    local catalog = LrApplication.activeCatalog()
    local photos = catalog:getTargetPhotos()

    if #photos == 0 then
        LrDialogs.message("No photos selected", "Please select one or more photos.", "info")
        return
    end

    local progress = LrProgressScope {
        title = "Copying location metadata to keywords...",
    }

    catalog:withWriteAccessDo("Add Geo Keywords", function(context)
        for i, photo in ipairs(photos) do
            progress:setPortionComplete(i, #photos)
            progress:setCaption("Processing photo " .. i .. " of " .. #photos)

            local city = photo:getFormattedMetadata("city") or ""
            local state = photo:getFormattedMetadata("stateProvince") or ""
            local country = photo:getFormattedMetadata("country") or ""

            local newKeywords = {}
            for _, value in ipairs({ city, state, country }) do
                value = value:match("^%s*(.-)%s*$") -- trim whitespace
                if value ~= "" then
                    table.insert(newKeywords, value)
                end
            end

           if #newKeywords > 0 then
                local existingKeywords = photo:getRawMetadata("keywords") or {}
                local keywordSet = {}

                for _, k in ipairs(existingKeywords) do
                    keywordSet[k:getName()] = true
                end

                for _, kw in ipairs(newKeywords) do
                    if not keywordSet[kw] then
                        local keyword = catalog:createKeyword(kw, {}, true, nil)
                        photo:addKeyword(keyword)
                    end
                end
            end

            if progress:isCanceled() then break end
        end
    end)

    progress:done()
    LrDialogs.message("Done", "Location data copied to keywords.", "info")
end)

 

johnrellis
Legend
April 25, 2025

One problem is with these two lines:

local keyword = catalog:createKeyword(kw, {}, true, nil)
photo:addKeyword(keyword)

 

The call to catalog:createKeyword() doesn't specify the last parameter "returnExisting", so it's taken as false, and if a keyword with name "kw" already exists, then createKeyword() returns false. (Not the best way to have defined this, but that's what it does.)  "photo:addKeyword (keyword)" will then fail, since "keyword" is false rather than an LrKeyword.

 

If you add true as the last parameter to createKeyword(), then the code should work.

johnrellis
Legend
April 24, 2025

Use photo:getFormattedMetadata(), not :getRawMetadata(), with keys "city", "stateProvince", and "county" (note the case).  The API documentation for :getFormattedMetadata() and :getRawMetadata() lists all the keys.  

 

You might my free Show Metadata plugin useful:

https://www.dropbox.com/scl/fi/gofi2bjaumk5hbjsxkxu4/showmetadata.1.7.zip?rlkey=a1n4jbn5umsnsmabcufr6eebv&dl=0

 

It displays all the available metadata for a photo, including some fields that are undocumented.

Participating Frequently
April 23, 2025

Not sure, but shouldn't it be ipairs({ city, state, country }) instead of ipairs({ City, State, Country }) ?

Known Participant
April 23, 2025

I tried both ways, same error

Participating Frequently
April 23, 2025

I'm sorry, we'll have to wait for the expert then 😉