# Lightroom 5 SDK: catalog:getFolderByPath does not work when folder is on a network drive.

• Problem
• Updated 4 years ago
Is there any way around this? - thanks in advance..

For example:
local photo = catalog:getTargetPhoto()
local file = photo:getRawMetadata( 'path' )
local parent = LrPathUtils.parent( file )
local folder = catalog:getFolderByPath( parent )
assert( folder ~= nil, "problem.." )

no problem asserted if photo on local drive
problem asserted if photo on network drive

note: when LrFolder found by starting with catalog:getFolders(), and recursing.., the path returned via: lrFolder:getPath() is the same as 'parent' computed above - something like:

\\MYDRV\MyFolder

So the folder path is correctly recorded in the catalog.. - just catalog:getFolderByPath is broken.

Rob
• 4831 Posts
• 379 Reply Likes

Posted 4 years ago

John R. Ellis, Champion

• 3584 Posts
• 926 Reply Likes
Interestingly, I can find a network folder if is named with a drive letter, e.g. Y:\test, but not if it is named with a UNC path, e.g. \\ellisthinkpad2\test.

Also, catalog:findPhotoByPath() works just fine with UNC paths.
• 4831 Posts
• 379 Reply Likes
Thanks John - 'tis a viable work-around in some cases (mapping network drives to a letter).

John R. Ellis, Champion

• 3584 Posts
• 926 Reply Likes
Have you considered just recursing the catalog:getFolders() tree looking for the desired folder?
• 4831 Posts
• 379 Reply Likes
Yes - in fact, that may be the long term solution (until an Adobe fix), since it does not depend on user action - thanks again John.
• 4831 Posts
• 379 Reply Likes
Initial prototype for function which works for unmapped network drives too:
```
--- Initialize folder cache to assure fresh results via get-folder-by-path.

--

function Catalog:initFolderCache()

self.folderCache = {}

end

--- Equivalent to Lr's native method, except works for unmapped network drives too (Lr's doesn't, @Lr5.4 anyway).

--

--  @usage it is recommended but not required to initialize folder cache before calling the first time.

--

--  @param folderPath (string, required) path of folder for which corresponding lr-folder object is desired.

--

--  @return folder (lr-folder object) hopefully never nil (assuming valid folder-path), but best to check in calling context.

--

function Catalog:getFolderByPath( folderPath )

local folder = catalog:getFolderByPath( folderPath )

if folder then -- mapped drive..

return folder

end

if not self.folderCache then

Debug.pause( "Consider initializing folder cache before first call." )

self.folderCache = {} -- lookup

else

folder = self.folderCache[folderPath]

end

if not folder then

local y = 0

local function find( lrFolder )

local path = lrFolder:getPath()

self.folderCache[path] = lrFolder

if path == folderPath then -- case sensitive is OK, I hope ###1.

folder = lrFolder

return true

end

y = app:yield( y ) -- yield every 20 times, so Lr stays responsive.

for j, v in ipairs( lrFolder:getChildren() ) do

if find( v ) then return true end

end

return false

end

local allFolders = catalog:getFolders()

for i, f in ipairs( allFolders ) do

if find( f ) then break end

end

Debug.pauseIf( folder==nil, "no folder for path: "..folderPath )

end

return folder

end

```

John - this is an example of a case that I wonder if weak tables could help, since folders could change around asynchronously - ya know, maybe have weak references in folder-cache that would last for a "reasonable" amount of time, then go "poof" and disappear somehow (when garbage collected).. - still workin' on this idea.

John R. Ellis, Champion

• 3584 Posts
• 926 Reply Likes
Here's a slight tweak to the find() subfunction that should make it significantly faster on average -- it stops recursing when it gets to a folder, e.g. "/a/c", that couldn't possibly be a parent of the folder path you're looking for, e.g. "/a/b/x":
```
local function find( lrFolder )

local path = lrFolder:getPath()

self.folderCache[path] = lrFolder

if path == folderPath then -- case sensitive is OK, I hope ###1.

folder = lrFolder

return true

elseif path ~= folderPath:sub( 1, #path ) then

return false

end

y = app:yield( y ) -- yield every 20 times, so Lr stays responsive.

for j, v in ipairs( lrFolder:getChildren() ) do

if find( v ) then return true end

end

return false

end

```

My guess is that this will be fast enough for most uses that there would be no need to cache the result (and thus no need for thinking about caching policies and weak references). But just a guess...
• 4831 Posts
• 379 Reply Likes
Here is the "final" version, which uses a non-cached method with 'find' function ala John Ellis, by default, but supports cached mode if calling context is willing to take the care needed to manage cache initialization.
```
--- Initialize folder cache to assure fresh results via (and set mode of) get-folder-by-path.

--

--  @param unInit (boolean, default = false) if false, get-folder-by-path will use cached mode (faster, but results will be stale unless care is taken to init before each "run"..).

--          if true, get-folder-by-path will use non-cached mode (slower, but results are always fresh).

--

function Catalog:initFolderCache( unInit )

if unInit then

self.folderCache = nil

else

self.folderCache = {}

end

end

```

```
--- Equivalent to Lr's native method, except works for unmapped network drives too (Lr's doesn't, @Lr5.4 anyway).

--

--  @usage it is recommended but not required to initialize folder cache before calling the first time.

--

--  @param folderPath (string, required) path of folder for which corresponding lr-folder object is desired.

--

--  @return folder (lr-folder object) hopefully never nil (assuming valid folder-path), but best to check in calling context.

--

function Catalog:getFolderByPath( folderPath )

local folder = catalog:getFolderByPath( folderPath ) -- ### set to nil to test "finding" below.

if folder then -- mapped drive..

return folder

end

local find

if not self.folderCache then

--Debug.pause( "Consider initializing folder cache before first call." )

find = function( lrFolder )

local path = lrFolder:getPath()

if path == folderPath then -- ###2: case sensitive.

folder = lrFolder

return true

elseif path ~= folderPath:sub( 1, #path ) then -- i.e. if not str:isBeginningWith( folderPath, path ) then

return false

end

-- assume find will be fast enough that yielding is not required.

for j, v in ipairs( lrFolder:getChildren() ) do

if find( v ) then return true end

end

return false

end

else

folder = self.folderCache[folderPath]

if folder then

return folder

end

local y = 0

find = function( lrFolder )

local path = lrFolder:getPath()

self.folderCache[path] = lrFolder

if path == folderPath then -- ###2: ditto.

folder = lrFolder

return true

end

y = app:yield( y ) -- yield every 20 times, so Lr stays responsive.

for j, v in ipairs( lrFolder:getChildren() ) do

if find( v ) then return true end

end

return false

end

end

local allFolders = catalog:getFolders()

for i, f in ipairs( allFolders ) do

if find( f ) then break end

end

Debug.pauseIf( folder==nil, "no folder for path: "..folderPath )

return folder

end

```

Timing results:
* without caching, to find folders of 11884 photos: 22.094 seconds.
* with freshly initialized cache: 1.111 seconds.
* again without re-initializing cache: 0.746 seconds.

My intention is to convert all plugins immediately to use non-cached mode, so they work with network drives too (reminder: ~NO penalty if photos *not* on network drive), then use cached mode more strategically..

Rob

John R. Ellis, Champion

• 3584 Posts
• 926 Reply Likes
So it takes about 2 msecs / folder without caching, and 0.1 msecs with caching. Another reminder that many APIs in the SDK are not all that efficient.