commit 2fc4b8f086446ce96c850e2c45ae4fceaf22269a Author: Rick McEwen Date: Wed Aug 20 10:37:50 2025 -0600 Initial Commit diff --git a/LR Plugin Instructions.pdf b/LR Plugin Instructions.pdf new file mode 100644 index 0000000..f24be56 Binary files /dev/null and b/LR Plugin Instructions.pdf differ diff --git a/photonodes.lrplugin/Info.lua b/photonodes.lrplugin/Info.lua new file mode 100644 index 0000000..bc16666 --- /dev/null +++ b/photonodes.lrplugin/Info.lua @@ -0,0 +1,36 @@ +--[[---------------------------------------------------------------------------- + +Info.lua +Summary information for ftp_upload sample plug-in + +-------------------------------------------------------------------------------- + +ADOBE SYSTEMS INCORPORATED + Copyright 2007 Adobe Systems Incorporated + All Rights Reserved. + +NOTICE: Adobe permits you to use, modify, and distribute this file in accordance +with the terms of the Adobe license agreement accompanying it. If you have received +this file from a source other than Adobe, then your use, modification, or distribution +of it requires the prior written permission of Adobe. + + +------------------------------------------------------------------------------]] + +return { + + LrSdkVersion = 3.0, + LrSdkMinimumVersion = 1.3, -- minimum SDK version required by this plug-in + + LrToolkitIdentifier = 'com.photonodes.lightroom.export.web_upload', + + LrPluginName = LOC "$$$/photonodes/PluginName=Photonodes Export Plugin", + + LrExportServiceProvider = { + title = "PhotoNodes", + file = 'PhotonodesUploadServiceProvider.lua', + }, + + VERSION = { major=1, minor=2, revision=0, }, + +} diff --git a/photonodes.lrplugin/PhotonodesUploadExportDialogSections.lua b/photonodes.lrplugin/PhotonodesUploadExportDialogSections.lua new file mode 100644 index 0000000..c88b82f --- /dev/null +++ b/photonodes.lrplugin/PhotonodesUploadExportDialogSections.lua @@ -0,0 +1,168 @@ +-- Lightroom SDK +local LrView = import 'LrView' + +--============================================================================-- + +PhotonodesUploadExportDialogSections = {} + +------------------------------------------------------------------------------- + +local function updateExportStatus( propertyTable ) + + local message = nil + + repeat + -- Use a repeat loop to allow easy way to "break" out. + -- (It only goes through once.) + + if propertyTable.client_id == nil or propertyTable.client_id == "" then + message = LOC "$$$/Photonodes/ExportDialog/Messages/SelectPreset=Enter a PhotoNodes Client Id" + break + end + + if propertyTable.event_code == nil or propertyTable.event_code == "" then + message = LOC "$$$/Photonodes/ExportDialog/Messages/SelectPreset=Enter a PhotoNodes Event Code" + break + end + + if propertyTable.gallery_name == nil or propertyTable.gallery_name == "" then + message = LOC "$$$/Photonodes/ExportDialog/Messages/SelectPreset=Enter a Gallery Name" + break + end + + + + until true + + if message then + propertyTable.message = message + propertyTable.hasError = true + propertyTable.hasNoError = false + propertyTable.LR_cantExportBecause = message + else + propertyTable.message = nil + propertyTable.hasError = false + propertyTable.hasNoError = true + propertyTable.LR_cantExportBecause = nil + end + +end + +------------------------------------------------------------------------------- + +function PhotonodesUploadExportDialogSections.startDialog( propertyTable ) + + propertyTable:addObserver( 'items', updateExportStatus ) + propertyTable:addObserver( 'client_id', updateExportStatus ) + propertyTable:addObserver( 'event_code', updateExportStatus ) + propertyTable:addObserver( 'gallery_name', updateExportStatus ) + + updateExportStatus( propertyTable ) + +end + +------------------------------------------------------------------------------- + +function PhotonodesUploadExportDialogSections.sectionsForBottomOfDialog( _, propertyTable ) + + local f = LrView.osFactory() + local bind = LrView.bind + local share = LrView.share + + local result = { + + { + title = LOC "$$$/Photonodes/ExportDialog/EventSettings=Photonodes Settings", + + synopsis = bind { key = 'fullPath', object = propertyTable }, + +-- f:row { +-- f:static_text { +-- title = LOC "$$$/FtpUpload/ExportDialog/Destination=Destination:", +-- alignment = 'right', +-- width = share 'labelWidth' +-- }, + +-- LrFtp.makeFtpPresetPopup { +-- factory = f, +-- properties = propertyTable, +-- valueBinding = 'ftpPreset', +-- itemsBinding = 'items', +-- fill_horizontal = 1, +-- }, +-- }, + + f:row { + f:spacer { + width = share 'labelWidth' + }, + + f:static_text { + title = LOC "$$$/PhotonodesUpload/ExportDialog/ClientId=Client Id:", + alignment = 'right', + width = share 'labelWidth', + + }, + + f:edit_field { + title = LOC "$$$/PhotonodesUpload/ExportDialog/ClientId=Client Id:", + value = bind 'client_id', + enabled = true, + truncation = 'middle', + immediate = true, + fill_horizontal = 1, + }, + }, + + + f:row { + f:spacer { + width = share 'labelWidth' + }, + + f:static_text { + title = LOC "$$$/PhotonodesUpload/ExportDialog/EventCode=Event Code:", + alignment = 'right', + width = share 'labelWidth', + + }, + + f:edit_field { + title = LOC "$$$/PhotonodesUpload/ExportDialog/EventCode=Event Code:", + value = bind 'event_code', + enabled = true, + truncation = 'middle', + immediate = true, + fill_horizontal = 1, + }, + }, + + f:row { + f:spacer { + width = share 'labelWidth' + }, + + f:static_text { + title = LOC "$$$/PhotonodesUpload/ExportDialog/GalleryName=Gallery Name:", + alignment = 'right', + width = share 'labelWidth', + + }, + + f:edit_field { + title = LOC "$$$/PhotonodesUpload/ExportDialog/GalleryName=Gallery Name:", + value = bind 'gallery_name', + enabled = true, + truncation = 'middle', + immediate = true, + fill_horizontal = 1, + }, + }, + + + }, + } + + return result + +end diff --git a/photonodes.lrplugin/PhotonodesUploadServiceProvider.lua b/photonodes.lrplugin/PhotonodesUploadServiceProvider.lua new file mode 100644 index 0000000..30f4708 --- /dev/null +++ b/photonodes.lrplugin/PhotonodesUploadServiceProvider.lua @@ -0,0 +1,27 @@ +-- PhotoNodes Upload plug-in +require "PhotonodesUploadExportDialogSections" +require "PhotonodesUploadTask" + + +--============================================================================-- + +return { + + hideSections = { 'exportLocation' }, + + allowFileFormats = nil, -- nil equates to all available formats + + allowColorSpaces = nil, -- nil equates to all color spaces + + exportPresetFields = { + { key = 'client_id', default = nil; }, + { key = 'event_code', default = 'LR Photos' }, + { key = "gallery_name", default = nil }, + }, + + startDialog = PhotonodesUploadExportDialogSections.startDialog, + sectionsForBottomOfDialog = PhotonodesUploadExportDialogSections.sectionsForBottomOfDialog, + + processRenderedPhotos = PhotonodesUploadTask.processRenderedPhotos, + +} diff --git a/photonodes.lrplugin/PhotonodesUploadTask.lua b/photonodes.lrplugin/PhotonodesUploadTask.lua new file mode 100644 index 0000000..de54b0e --- /dev/null +++ b/photonodes.lrplugin/PhotonodesUploadTask.lua @@ -0,0 +1,188 @@ +-- Lightroom API +local LrPathUtils = import 'LrPathUtils' +local LrHttp = import 'LrHttp' +local LrFileUtils = import 'LrFileUtils' +local LrErrors = import 'LrErrors' +local LrDialogs = import 'LrDialogs' +local logger = import 'LrLogger' + +--============================================================================-- + +local myLogger = logger( 'photonodes_logger' ) -- the log file name +myLogger:enable( "logfile" ) + +local function table_to_string(tbl) + local result = "{" + for k, v in pairs(tbl) do + -- Check the key type (ignore any numerical keys - assume its an array) + if type(k) == "string" then + result = result.."[\""..k.."\"]".."=" + end + + -- Check the value type + if type(v) == "table" then + result = result..table_to_string(v) + elseif type(v) == "boolean" then + result = result..tostring(v) + else + result = result.."\""..v.."\"" + end + result = result.."," + end + -- Remove leading commas from the result + if result ~= "" then + result = result:sub(1, result:len()-1) + end + return result.."}" +end + + +PhotonodesUploadTask = {} + + +function PhotonodesUploadTask.outputToLog( message ) + myLogger:trace( message ) +end + +function PhotonodesUploadTask.debugToLog( val ) + myLogger:debug( val ) +end +-------------------------------------------------------------------------------- + +function PhotonodesUploadTask.processRenderedPhotos( functionContext, exportContext ) + + -- Make a local reference to the export parameters. + + local exportSession = exportContext.exportSession + local exportParams = exportContext.propertyTable + + + -- Set progress title. + + local nPhotos = exportSession:countRenditions() + + local progressScope = exportContext:configureProgress { + title = nPhotos > 1 + and LOC( "$$$/photonodes/Upload/Progress=Uploading ^1 photos to Photonodes", nPhotos ) + or LOC "$$$/photonodes/Upload/Progress/One=Uploading one photo to Photonodes", + } + + + local client_id = exportParams.client_id + local event_code = exportParams.event_code + local gallery_name = exportParams.gallery_name + + PhotonodesUploadTask.outputToLog(string.format("Client Id: %s Event Code: %s Gallery Name: %s", client_id, event_code, gallery_name)) + + if client_id==nil or client_id=="" then + LrErrors.throwUserError( LOC "$$$/photonodes/Upload/Errors/InvalidParameters=The specified Client Id incomplete and cannot be used." ) + elseif event_code==nil or event_code=="" then + LrErrors.throwUserError( LOC "$$$/photonodes/Upload/Errors/InvalidParameters=The specified Event Code is incomplete and cannot be used." ) + elseif gallery_name==nil or gallery_name=="" then + LrErrors.throwUserError( LOC "$$$/photonodes/Upload/Errors/InvalidParameters=The specified Gallery Name is incomplete and cannot be used." ) + end + + + -- This url uses event_id for event_code + -- local upload_url = string.format("http://api.photonodes.com/kiosk/api/gallerybyname/photos/upload/%s/%s", client_id, event_code) + + -- This url uses event code for event_code (staging) + -- local upload_url = string.format("http://staging.photonodes.com/plugin/api/gallerybyname/photos/upload/%s", client_id) + + -- This url uses event code for event_code (live) + local upload_url = string.format("http://api.photonodes.com/plugin/api/gallerybyname/photos/upload/%s", client_id) + + PhotonodesUploadTask.outputToLog(string.format("Upload Url: %s", upload_url)) + + -- Iterate through photo renditions. + + local failures = {} + local successes = {} + + for _, rendition in exportContext:renditions{ stopIfCanceled = true } do + + -- Wait for next photo to render. + + local success, pathOrMessage = rendition:waitForRender() + + -- Check for cancellation again after photo has been rendered. + + if progressScope:isCanceled() then break end + + if success then + + local filename = LrPathUtils.leafName( pathOrMessage ) + + local mimeChunks = {} + mimeChunks[ #mimeChunks + 1 ] = { name = 'gallery_name', value = gallery_name } + mimeChunks[ #mimeChunks + 1 ] = { name = 'event_code', value = event_code } + mimeChunks[ #mimeChunks + 1 ] = { name = 'photo_file', fileName = filename, filePath = pathOrMessage, contentType = 'application/octet-stream' } + + local result, hdrs = LrHttp.postMultipart(upload_url, mimeChunks ) + + PhotonodesUploadTask.outputToLog(string.format("Http Post result: %s", result)) + PhotonodesUploadTask.outputToLog(string.format("Http Post headers: %s", table_to_string(hdrs))) + + if not result then + if hdrs and hdrs.error then + PhotonodesUploadTask.outputToLog(string.format("Http Post error: %s", formatError( hdrs.error.nativeCode ))) + LrErrors.throwUserError( formatError( hdrs.error.nativeCode ) ) + end + end + + if hdrs and hdrs.status == 200 then + success = true + elseif hdrs and hdrs.status == 404 then + PhotonodesUploadTask.outputToLog("Http Post error: 404") + LrErrors.throwUserError( "Client Id or Event Code not found" ) + elseif hdrs and hdrs.status == 403 then + PhotonodesUploadTask.outputToLog("Http Post error: 403") + LrErrors.throwUserError( "Error uploading file" ) + else + success=false + end + + -- Http stuff goes here + --local success = ftpInstance:putFile( pathOrMessage, filename ) + + if not success then + + -- If we can't upload that file, log it. For example, maybe user has exceeded disk + -- quota, or the file already exists and we don't have permission to overwrite, or + -- we don't have permission to write to that directory, etc.... + + table.insert( failures, filename ) + else + table.insert(successes, 1) + end + + -- When done with photo, delete temp file. There is a cleanup step that happens later, + -- but this will help manage space in the event of a large upload. + + LrFileUtils.delete( pathOrMessage ) + + end + + end + + + if #failures > 0 then + local message + if #failures == 1 then + message = LOC "$$$/photonodes/Upload/Errors/OneFileFailed=1 file failed to upload correctly." + else + message = LOC ( "$$$/photonodes/Upload/Errors/SomeFileFailed=^1 files failed to upload correctly.", #failures ) + end + LrDialogs.message( message, table.concat( failures, "\n" ) ) + else + + if #successes == 1 then + message = string.format("1 file successfully uploaded to gallery %s.", gallery_name) + else + message = string.format("%s files successfully uploaded to gallery %s.", #successes, gallery_name) + end + LrDialogs.message( message ) + + end + +end