Creating a Lightroom Classic Collection from Exported Filenames
This documents the process of creating a Lightroom Classic plugin to find original RAW files based on a directory of previously exported JPEGs.
The Problem
Over time, ~250 photos were exported from Lightroom Classic to share with others, but they were never tagged or organized in the catalog. Now we need to:
- Find those original RAW files in the Lightroom catalog
- Review and improve the RAW edits
- Re-export them at higher quality for a website
The challenge: we only have a directory of exported JPEGs with filenames like DSC_0043-Pano-Enhanced-NR.jpg, and need to match them back to originals like DSC_0043.NEF in the catalog.
The Approach
- Extract the base
DSC_XXXXidentifier from each exported JPEG filename - Exclude photos already processed (listed in a manifest file)
- Create a Lightroom Lua plugin that searches the catalog and creates a Collection
Step 1: Extract and Filter Filenames
First, extract the DSC_XXXX pattern from exported JPEGs:
# Extract unique DSC IDs from exported JPEGs
ls /mnt/c/Users/jbuck/Downloads/photo_export/*.jpg \
| xargs -n1 basename \
| sed 's/\.[^.]*$//' \
| sed 's/\(DSC_[0-9]*\).*/\1/' \
| sort -u > /tmp/export_ids.txtThis handles various suffixes Lightroom adds during export:
DSC_0043.jpg→DSC_0043DSC_0043-Pano-Enhanced-NR.jpg→DSC_0043DSC_0043-Enhanced-NR.jpg→DSC_0043
Next, extract IDs already in the manifest (photos already processed for the website):
# Extract DSC IDs from manifest.json
jq -r '.photos[].importedFile' site/src/lib/data/manifest.json \
| sed 's/.*\(DSC_[0-9]*\).*/\1/' \
| sort -u > /tmp/manifest_ids.txtFinally, find IDs that are NOT yet in the manifest:
# Find IDs in export folder but not in manifest
comm -23 /tmp/export_ids.txt /tmp/manifest_ids.txt > /tmp/remaining_ids.txt
# Copy to plugin directory
cp /tmp/remaining_ids.txt \
"/mnt/c/Users/jbuck/Documents/LrPlugins/FindByFilename.lrplugin/filenames.txt"Result: 208 unique DSC IDs to find in the Lightroom catalog.
Step 2: Create the Lightroom Plugin
The plugin consists of two Lua files in a .lrplugin folder.
Plugin Structure
FindByFilename.lrplugin/
├── Info.lua # Plugin metadata
├── CreateCollection.lua # Main logic
└── filenames.txt # List of DSC IDs to findInfo.lua
return {
LrSdkVersion = 10.0,
LrToolkitIdentifier = "com.jbuck.findbyfilename",
LrPluginName = "Find By Filename",
LrLibraryMenuItems = {
{ title = "Create Collection from File List", file = "CreateCollection.lua" }
},
VERSION = { major = 1, minor = 0, revision = 0 },
}CreateCollection.lua
local LrApplication = import "LrApplication"
local LrDialogs = import "LrDialogs"
local LrTasks = import "LrTasks"
local LrPathUtils = import "LrPathUtils"
local LrFileUtils = import "LrFileUtils"
-- Extract DSC_XXXX pattern from a filename
local function extractDscId(filename)
return filename:match("(DSC_%d+)")
end
LrTasks.startAsyncTask(function()
local catalog = LrApplication.activeCatalog()
-- Get the path to filenames.txt (same directory as this script)
local pluginPath = _PLUGIN.path
local filenamesPath = LrPathUtils.child(pluginPath, "filenames.txt")
if not LrFileUtils.exists(filenamesPath) then
LrDialogs.message("Error", "Could not find filenames.txt in plugin folder:\n" .. filenamesPath)
return
end
-- Read the DSC ID list
local dscIds = {}
local file = io.open(filenamesPath, "r")
if not file then
LrDialogs.message("Error", "Could not open filenames.txt")
return
end
local lineCount = 0
for line in file:lines() do
local trimmed = line:match("^%s*(.-)%s*$") -- trim whitespace
if trimmed and #trimmed > 0 then
dscIds[trimmed:upper()] = true
lineCount = lineCount + 1
end
end
file:close()
LrDialogs.showBezel("Searching for " .. lineCount .. " photos...")
-- Find matching photos in catalog
local allPhotos = catalog:getAllPhotos()
local matches = {}
local matchedIds = {}
for _, photo in ipairs(allPhotos) do
local filename = photo:getFormattedMetadata("fileName")
local dscId = extractDscId(filename)
if dscId and dscIds[dscId:upper()] then
table.insert(matches, photo)
matchedIds[dscId:upper()] = true
end
end
if #matches == 0 then
LrDialogs.message("No Matches", "No photos found matching the file list.")
return
end
-- Create collection
catalog:withWriteAccessDo("Create To Re-Export Collection", function()
-- Remove existing collection if present
local existingCollections = catalog:getChildCollections()
for _, coll in ipairs(existingCollections) do
if coll:getName() == "To Re-Export" then
coll:delete()
break
end
end
local collection = catalog:createCollection("To Re-Export", nil, true)
collection:addPhotos(matches)
end)
-- Count how many IDs were not found
local notFound = 0
local notFoundList = {}
for dscId, _ in pairs(dscIds) do
if not matchedIds[dscId] then
notFound = notFound + 1
if #notFoundList < 10 then
table.insert(notFoundList, dscId)
end
end
end
local message = "Found " .. #matches .. " photos.\nAdded to 'To Re-Export' collection."
if notFound > 0 then
message = message .. "\n\n" .. notFound .. " IDs not found in catalog"
if #notFoundList > 0 then
message = message .. ":\n" .. table.concat(notFoundList, ", ")
if notFound > 10 then
message = message .. "..."
end
end
end
LrDialogs.message("Complete", message)
end)Step 3: Install and Run the Plugin
- Open Lightroom Classic
- Go to File → Plug-in Manager
- Click Add (bottom left)
- Navigate to the
.lrpluginfolder and select it - Click Done
- Go to Library → Plug-in Extras → Create Collection from File List
The plugin will:
- Search the entire catalog for photos matching the DSC IDs
- Create a collection called “To Re-Export” containing all matches
- Report how many were found and list any that weren’t in the catalog
Notes
- The plugin matches on the
DSC_XXXXpattern, which handles both original RAW files (DSC_0043.NEF) and Lightroom-generated files (DSC_0043-Pano.dng) - Matching is case-insensitive
- Running the plugin again replaces the existing “To Re-Export” collection
- To update the file list, edit
filenames.txtin the plugin folder and re-run