# Copy Mac Finder paths to keyword hierarchy?



## rob211 (Feb 12, 2015)

Is there a plugin or method to copy the path of an image to a keyword hierarchy?

Say you have this in the Finder: ~/Pictures/Vacation/Yosemite/photo.jpg

And you'd like to get Pictures>Vacation>Yosemite as keywords for that photo?

Obviously I could do it with some cutting and pasting but I was wondering if there was something more automated or that would work in a batch.

Thanks,
Rob


----------



## clee01l (Feb 12, 2015)

Yes there is a Script that John Beardsworth wrote  for me.  I think he has incorporated that into one of his plugins.  We are going to have to wait for him because I can't remember which one that it is.


----------



## johnbeardy (Feb 12, 2015)

Probably this? Copy all the contents of this code box and follow the setup instructions at its top.

John


```
--[[
Changes all keywords in the catalogue to title case - ie first letter of each word will be capitalised

SETUP INSTRUCTIONS
Temporarily save this file onto your desktop and call it "File path to keywords.lua".

Now you need to create a scripts folder:
    In Lightroom, choose Lightroom > Preferences (Mac OS) or Edit > Preferences (Windows).
    Choose the Preset tab and select Show Lightroom Presets folder.
    Create a folder in the Lightroom folder called Scripts.
    Copy the "File path to keywords.lua" script into the Scripts folder.
    Quit and reopen Lightroom. 
You should now see a little scripts menu to the right of the Help menu. It has a single item Title case keywords.
--]]

------------------------------YOU CAN CHANGE THESE
local scriptTitle = "File path to keyword"
local scriptPrefix = "~"
local includeFileName =  true 
local allowExport =  true 


------------------------------DON'T TOUCH BELOW HERE
local LrFunctionContext = import 'LrFunctionContext'
local LrTasks = import 'LrTasks'
local catalog = import "LrApplication".activeCatalog()
local ProgressScope = import 'LrProgressScope'
local LrDialogs = import 'LrDialogs'
local LrPathUtils = import 'LrPathUtils'
if MAC_ENV then SEP = '/' else SEP = '\\' end


function fileName_to_keywords()
    local photos=catalog:getTargetPhotos()
    local ProgressScope = ProgressScope({title = scriptTitle  ,caption = "",})
    for j, photo in ipairs(photos) do         
        topKeyword = CreateParentKeyword()
            varPath =  photo:getRawMetadata('path') 
            if includeFileName == true then
            varPath = LrPathUtils.parent( varPath  )            
            end
            local folders ={}
            folders = split(varPath , SEP )
            for e, folder in ipairs(folders ) do 
                catalog:withWriteAccessDo( scriptTitle   , function()    
                    topKeyword = catalog:createKeyword( folder , {} , allowExport  , topKeyword  , true)
                end)
                catalog:withWriteAccessDo( scriptTitle  , function()    
                    photo:addKeyword( topKeyword )
                end)
            end
        ProgressScope:setPortionComplete(j/#photos)
        ProgressScope:setCaption( j .. "/" .. #photos .. '  ' .. photo:getFormattedMetadata('fileName'))
    end
    ProgressScope:done() 
end

function CreateParentKeyword()
    catalog:withWriteAccessDo( scriptTitle , function()
    topKeyword = catalog:createKeyword( scriptPrefix .. scriptTitle , {} , false, nil , true)
    while not topKeyword do
            LrTasks.yield()
       end
    end)
    return topKeyword 
end

function split(str, pat)
   local t = {}  -- NOTE: use {n = 0} in Lua-5.0
   local fpat = "(.-)" .. pat
   local last_end = 1
   local s, e, cap = str:find(fpat, 1)
   while s do
      if s ~= 1 or cap ~= "" then
     table.insert(t,cap)
      end
      last_end = e+1
      s, e, cap = str:find(fpat, last_end)
   end
   if last_end <= #str then
      cap = str:sub(last_end)
      table.insert(t, cap)
   end
   return t
end

LrTasks.startAsyncTask( function()
    fileName_to_keywords()
end)
```


----------



## rob211 (Feb 12, 2015)

Wow, fantastic! thanks so much (needs a change in the first line though).

How does editing the "local scriptPrefix =" affect the outcome of the script? Just the prefix to the parent keyword (~File path to keyword)?

Great job.

Rob


----------



## johnbeardy (Feb 12, 2015)

Yes, it's the "~" in the parent keyword "~File path to keyword". If you don't want a prefix, leave it as "".

John


----------



## swteven (Aug 24, 2017)

I tested this script out and it inserted all the folder names as parent keywords. I would like to modify the script if possible. I would like the script to NOT include most of the parent keywords shown in this image of the keyword hierarchy.





Most of the parent keywords are irrelevant for me. These would be :

~File path to keyword
Users
swteven
Pictures
DIG​
Is there a way to modify the script or delete the parent keywords manually ?

Thanks, Scott


----------



## johnbeardy (Aug 24, 2017)

Just drag A Houston Skyline to the top level of keywords. You can use Shift to select multiple keywords.

John


----------



## clee01l (Aug 24, 2017)

The easiest way without the script is to simply drag "A_HOUSTON_SKYLINE" to the top level hierarchy.  Once you have Isolated the "~File path to keyword...DIG" hierarchy, then you can delete those keywords. 

I have trouble dragging a buried keyword hierarchy like "A_HOUSTON_SKYLINE" to the top level. So I created a new TopLevel and named it "A_HOUSTON_SKYLINE"  (you can have two as long as they are not children for the same parent.)   Then I dragged  Keywords at the same hierarchal level as "Aerial Ship Channel East" to nest under the _New_ "A_HOUSTON_SKYLINE"


----------



## swteven (Aug 24, 2017)

Yeah, dragging the buried keyword hierarchy is tricky. After fumbling with it a bit I noticed that when you drag the Child keywords they would not insert easily. You have to hold them over a dividing line were between 2 top-level parent keywords. Then a blue horizontal line will glow between the 2 top-level parent keywords and they will insert properly.

If I tap my foot 3 times and hum "howdy doody", I get a free gum ball too !


----------



## inspiredron (Jan 16, 2019)

Does the script find the pathnames from the catalog itself - ie look at the pathname of the catalog or is it looking at the pathname of the folder locations thatis part of the structure on the hard disk(s). I have two different potential issues that I foresee depending on the answer.
If, as I suspect, it is the former, ie it picks up the hierarchy from the copy of the folder structure maintained by Lightroom then what will it do with keywords that have already been entered?  Can I simply delete the duplicates (ensuring that I delete them before removing the gash parent folders (DIG upwards in swteven's example) or will I have problems with duplication?
If the latter then how do I point the script to start garnering paths from the right point - 
eg from the z drive (actually on a NAS - z:\photos2018\America 
rather than  from c:\ with all the enormous folder structure that lies there?


----------



## swteven (Jan 17, 2019)

inspiredron,
Its been so long since I did this I cannot provide an answer. I ended up using keywords instead if I recall.


----------



## process (Feb 5, 2021)

I don't know if anyone's still reading and I'm certainly not very much into this stuff, but.... I made a couple of changes and it seems to work (copying only the folder's name to use as keywords -not the entire path as well).
I made two changes:

1) found at the beginning of the script, first the original part:
*local scriptPrefix = "~"*
which I changed to:
local scriptPrefix = ""

2) next, scrolling down a little past halfway, the original part being:
*   local fpat = "(.-)" .. pat*
which I changed to:
  local fpat = "" .. pat

Below is the entire script in the new, edited version. It would be great if others could try it out and see if it works for them as well (it's probably a good idea to give it a new name to avoid confusion with the original script, such as "EDITED filepath to keyword.lua").
*NOTE: I claim no ownership of this script! It's still the creation of John Beardsworth and all credit and thanks goes to him for posting it here in the first place (**posting #3**)!*



```
------------------------------YOU CAN CHANGE THESE
local scriptTitle = "File path to keyword"
local scriptPrefix = ""
local includeFileName =  true
local allowExport =  true


------------------------------DON'T TOUCH BELOW HERE
local LrFunctionContext = import 'LrFunctionContext'
local LrTasks = import 'LrTasks'
local catalog = import "LrApplication".activeCatalog()
local ProgressScope = import 'LrProgressScope'
local LrDialogs = import 'LrDialogs'
local LrPathUtils = import 'LrPathUtils'
if MAC_ENV then SEP = '/' else SEP = '\\' end


function fileName_to_keywords()
    local photos=catalog:getTargetPhotos()
    local ProgressScope = ProgressScope({title = scriptTitle  ,caption = "",})
    for j, photo in ipairs(photos) do      
        topKeyword = CreateParentKeyword()
            varPath =  photo:getRawMetadata('path')
            if includeFileName == true then
            varPath = LrPathUtils.parent( varPath  )        
            end
            local folders ={}
            folders = split(varPath , SEP )
            for e, folder in ipairs(folders ) do
                catalog:withWriteAccessDo( scriptTitle   , function()
                    topKeyword = catalog:createKeyword( folder , {} , allowExport  , topKeyword  , true)
                end)
                catalog:withWriteAccessDo( scriptTitle  , function()
                    photo:addKeyword( topKeyword )
                end)
            end
        ProgressScope:setPortionComplete(j/#photos)
        ProgressScope:setCaption( j .. "/" .. #photos .. '  ' .. photo:getFormattedMetadata('fileName'))
    end
    ProgressScope:done()
end

function CreateParentKeyword()
    catalog:withWriteAccessDo( scriptTitle , function()
    topKeyword = catalog:createKeyword( scriptPrefix .. scriptTitle , {} , false, nil , true)
    while not topKeyword do
            LrTasks.yield()
       end
    end)
    return topKeyword
end

function split(str, pat)
   local t = {}  -- NOTE: use {n = 0} in Lua-5.0
   local fpat = "" .. pat
   local last_end = 1
   local s, e, cap = str:find(fpat, 1)
   while s do
      if s ~= 1 or cap ~= "" then
     table.insert(t,cap)
      end
      last_end = e+1
      s, e, cap = str:find(fpat, last_end)
   end
   if last_end <= #str then
      cap = str:sub(last_end)
      table.insert(t, cap)
   end
   return t
end

LrTasks.startAsyncTask( function()
    fileName_to_keywords()
end)
```


----------

