Exit
  • Global community
    • Language:
      • Deutsch
      • English
      • Español
      • Français
      • Português
  • 日本語コミュニティ
  • 한국 커뮤니티
0

4000. + a question re external files.

Community Expert ,
Jun 12, 2009 Jun 12, 2009

Hey folks, seeing as I hang here most, I figure it's the best place for my 4000th post on Adobe Forums (get a life!).

Does anyone have a little tutorial on passing the rendered photos to an external file? I want it inside a normal plugin, not as a post process action.

It's for an entirely personal project, passing images to ffmpeg to have my timelapse movies created with an export from Lightroom.

Sean McCormack. Author of 'Essential Development 3'. Magazine Writer. Former Official Fuji X-Photographer.
TOPICS
SDK
9.2K
Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines

correct answers 1 Correct answer

Participant , Jun 13, 2009 Jun 13, 2009

In your processRenderedPhotos(), you have a loop where you go through all of the exportContext:renditions one by one (standard stuff for almost all export plugins). You're probably launching the app within that loop. Move it after the loop so it launches a single time once all photos are rendered.

Translate
Participant ,
Jun 12, 2009 Jun 12, 2009

Sean - Are you looking for sample code that sets up a command line and then launches an external app? I have this for ImageMagick's convert which I can post here along with comments. My code does this once per exported image though rather than build a list of the images and pass them all at once which is what it sounds like you're trying to do. Not that adding that extra bit should be hard. Let me know and I'll share if relevant.

Dave

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Jun 12, 2009 Jun 12, 2009

Sounds close Dave..

I'd need to pass 3 variables: frame rate, data rate and output filename.

I assume it'll be of the type local status = LrTasks.execute( 'mytool "' .. pathOrMessage .. '"' )

But I'm not sure how to add multiple variables. I'm not sure what the correct mix of '"..xxx.."' is!

A sample call would be ffmpeg -r 10 -b 1800 -i %03d.jpg test1800.mp4

-r=frame rate -b=bit rate -i image name as a sequence(which is done with the renditions) then output.

These variables will be handled by prefs (because once I'm happy with the settings, I'll probably stick with them)

UploadTask.exportPresetFields = {

{ key = 'fr', default = '24' },

{ key = 'dr', default = '1800' },

{ key = 'moviename', default = '/movie.mp4' },

{ key = 'LR_export_destinationType', default = 'tempFolder' },

}

Sean McCormack. Author of 'Essential Development 3'. Magazine Writer. Former Official Fuji X-Photographer.
Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Jun 13, 2009 Jun 13, 2009

Does this snippet from my exiftool plugin help? It's sending mulitple variables to the selected thumbnail (effectively extending Ctrl/Cmd S) .

John

if WIN_ENV == true then
command = '"' .. LrPathUtils.child(LrPathUtils.child( _PLUGIN.path, "win"), "exiftool.exe") .. '" ' .. newUrgency .. ' "' .. phoPath .. '" ' .. newOverwrite
quotedCommand = '"' .. command .. '"'
else command = '"' .. LrPathUtils.child(LrPathUtils.child(_PLUGIN.path, "mac"), "exiftool") .. '" ' .. newUrgency .. ' "' .. phoPath .. '" ' .. newOverwrite quotedCommand = command
end
Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Jun 13, 2009 Jun 13, 2009

John's reply shows what to do in essential form. With no offense intended to John, I think this can be improved upon in two ways. I find that all of the concatenation operators make things hard to read. I also find that keeping entirely separate versions for Windows vs. Mac is potentially error prone when the only thing that actually changes is the name of the executable.

What I prefer to do is make a table for the parameters, insert them one by one, then concatenate the table contents at the end. Here is an abridged version of the code I use with edits for your app (this formatting stinks - John,  how did you get your code to look like that in the embedded window?):

local params = {}


if WIN_ENV == true then
    table.insert( params, '"ffmpeg.exe"')
else
    table.insert( params, '"ffmpeg"')
end


table.insert( params, "-r " .. exportPresetFields.fr )
table.insert( params, "-b " .. exportPresetFields.br )
table.insert( params, "-i " .. exportPresetFields.moviename )


-- combine params into one string with a space inbetween

local execString = table.concat( params, " " )


logmsg ( "Running app with shell command: " .. execString )


local result = LrTasks.execute( execString )
logmsg( "The result is: " .. result );
if ( result ~= 0 ) then
    logmsg( "ERROR: " .. result )
end

The logmsg() function is just my wrapper around a LR logging function. This is all a bit simplified from my actual code for clarity. For instance, to collect the output of the application into a file, I actually have this as the very last thing inserted into the table:

if ( debugMode ) then
    table.insert( params, ">> debugLog.txt 2>&1" )
end

This redirects the normal and error output into debugLog.txt. If I recall, you're on a Mac. I'm on Windows and I can offer up another trick or two about managing the console window that appears (or not), etc. but maybe not important since this is a plugin for yourself.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Jun 13, 2009 Jun 13, 2009

No offence taken, Dave. I'm now picking up speed with this Lua stuff and am very confident I'm doing some things in a crude and inelegant manner!

To get the code looking OK here, I switched to HTML view and then use the pre HTML tag.

John

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Jun 13, 2009 Jun 13, 2009

There's even more answer to questions I hadn't asked Dave, cheers.

e.g. I've been sending logs to the Console already.. which is how I was able to figure out that TweetPhoto was having server hiccups. I wasn't sure how to fit text in with the log message though.

Sean McCormack. Author of 'Essential Development 3'. Magazine Writer. Former Official Fuji X-Photographer.
Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Jun 13, 2009 Jun 13, 2009

Sean - I have no access to a Mac so please let me know if the code works as expected there. Especially the redirect to a file. It comes down to how Adobe has chosen to launch other apps from LR on the Mac. If they're launching it via the default command shell (which I believe is bash on a Mac) then that redirect should work. This is useful for me as well since when it comes time to make a plugin available to the community, it's one less reason for me to go buy a Mac just to test it. 🙂

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Jun 13, 2009 Jun 13, 2009

Will do.

I may just try this as a post process action after all.

Sean McCormack. Author of 'Essential Development 3'. Magazine Writer. Former Official Fuji X-Photographer.
Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Jun 13, 2009 Jun 13, 2009

FYI The reason for that Mac/Win code was originally because of these shell differences. On PC, I could use LrShell to launch a PDF directly - just sending its name launched it in Acrobat Reader. That default app approach failed on Mac and I was forced to point LrTasks at Preview.

John

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Jun 13, 2009 Jun 13, 2009

I've got to the point where I'm feeding the params in, but.....

It's processing on a per photo basis. What's the trick to force it wait until all photos have rendered before executing?

Sean McCormack. Author of 'Essential Development 3'. Magazine Writer. Former Official Fuji X-Photographer.
Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Jun 13, 2009 Jun 13, 2009

In your processRenderedPhotos(), you have a loop where you go through all of the exportContext:renditions one by one (standard stuff for almost all export plugins). You're probably launching the app within that loop. Move it after the loop so it launches a single time once all photos are rendered.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Engaged ,
Jun 13, 2009 Jun 13, 2009

DFBurns wrote:

...

local params = {}

if WIN_ENV == true then
    table.insert( params, '"ffmpeg.exe"')
else
    table.insert( params, '"ffmpeg"')
end

table.insert( params, "-r " .. exportPresetFields.fr )
...

Thanks for sharing!  This approach is much more elegant than what I had been using up until now.

Matt

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Jun 13, 2009 Jun 13, 2009

Almost there.. It's all passing correctly now.. Thanks again Dave.

Changes I had to make: filterContext.propertyTable.fr for exportPresetFields.fr, etc

I added in ffmpegPath=LrPathUtils.parent(pathOrMessage) to allow me to tell ffmpeg where the files were, as it requires a sequence as input.

I also used that path as a prefix for the move location output folder.

Now I just need to get an acceptable quality movie as output, because currently the quality SUCKS!

Sean McCormack. Author of 'Essential Development 3'. Magazine Writer. Former Official Fuji X-Photographer.
Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Jun 13, 2009 Jun 13, 2009

That's probably a matter of choosing the right options for ffmpeg. I've never used it myself but I've heard that it's pretty decent. When I've done time-lapse movies, I did it manually using Quicktime Pro. A quick Google around shows that there is a scripting interface for QT at least on Windows using COM. You can write that script (sort of like VBScript) to a file then launch that script to do the work. A kludge but it'd probably work. 🙂 See here for info:

http://www.xsi-blog.com/archives/103

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Jun 13, 2009 Jun 13, 2009

Hi Dave,

My post above was being made as you posted.. so we crossed over.

The point was that I didn't actually want to have to have another program open to run this.. And of course I'm on a mac. I did look into Applescript and Automator, but couldn't find how to call Open Image Sequence from them.. Anyhow all working.

I just need to add an Overwrite Movie checkbox and maybe change the frame rate and data rate to popup menus... and I'm done.

Again this works for me because I have ffmpeg compiled and living in my /usr/bin folder..

I suspect I'd need to make it ./ffmpeg to run locally inside the plugin (haven't tried)..

And so you get the 10 points too

Sean McCormack. Author of 'Essential Development 3'. Magazine Writer. Former Official Fuji X-Photographer.
Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Beginner ,
Jun 15, 2009 Jun 15, 2009

Good advice to build your argument string using a table and then concat the table at the end - as well as tidier code, this is faster than building strings using 'x .. y'.  You can make that faster still by using:

myTable[#myTable + 1] = "some text"

rather than:

table.insert(myTable, "some text")

An inline expression is generally faster than calling a function.

Sean, on your question of finding the last photo from filterContext:renditions - if you know all the files are the same size, can't you get your values once and not call the two functions again - i.e. get the first photo's data rather than try to get the last.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Jun 15, 2009 Jun 15, 2009
LATEST

True, and I was thinking this myself today.

Sean McCormack. Author of 'Essential Development 3'. Magazine Writer. Former Official Fuji X-Photographer.
Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Jun 13, 2009 Jun 13, 2009

Joy.

It works...

The movie sucked because 2 images were at 1080X720, with the rest at 1280x720 causing a bit of shoehorning. All fine now...

Here's the (probably sucky) code: (please note the comment in the middle, if anyone knows the answer to that, please pipe up!)

function ffmpegFilterProvider.postProcessRenderedPhotos( functionContext, filterContext )           local renditionOptions = {           plugin = _PLUGIN,           renditionsToSatisfy = filterContext.renditionsToSatisfy,           filterSettings = function( renditionToSatisfy, exportSettings )                -- This hook function gives you the opportunity to change the render                -- settings for each photo before Lightroom renders it.           end,      }                          for sourceRendition, renditionToSatisfy in filterContext:renditions(renditionOptions) do                -- Wait for the upstream task to finish its work on this photo.                     local success, pathOrMessage = sourceRendition:waitForRender()                     if success then

-- take the file path and image size

-- because this is for me, I know the files have to have the same crop, so taking it from the final

-- rendered file is okay. If someone wants to suggest how we know the file is the final one to render, then

-- I could simply call that one, rather than calling LrPathUtils and LrPhotoInfo each render

          ffmpegPath=LrPathUtils.parent(pathOrMessage)           movieSize=LrPhotoInfo.fileAttributes(pathOrMessage)                          end           end      local params = {}      local movie={}       table.insert( movie, ffmpegPath)       table.insert( movie, "/")       table.insert( movie, filterContext.propertyTable.moviename)      local movieString = table.concat( movie, "" )                     if WIN_ENV == true then                         table.insert( params, '"ffmpeg.exe"')                     else                         table.insert( params, '"ffmpeg"')                     end                     table.insert( params, "-r " .. filterContext.propertyTable.fr ) --Frame Rate                     table.insert( params, "-b " .. filterContext.propertyTable.dr ) --Bit Rate                     table.insert( params, "-i " ..ffmpegPath.."/%03d.jpg" ) --Type of Sequence, in this case in the form 001                     table.insert( params, "-s " ..movieSize.width.."x"..movieSize.height ) --Movie Size                     table.insert( params, "" .. movieString ) --Movie path and filename                     -- combine params into one string with a space inbetween                     local execString = table.concat( params, " " )                     log:trace(execString)                     local result = LrTasks.execute( execString )                     log:trace( "The result is: " .. result );                          if ( result ~= 0 ) then                              log:trace( "ERROR: " .. result )                          end end

Sean McCormack. Author of 'Essential Development 3'. Magazine Writer. Former Official Fuji X-Photographer.
Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Jun 13, 2009 Jun 13, 2009

Matt - It really keeps things sane when you have 18 command line options to pass to ImageMagick like my code does. When/if you need to reorder options is cleaner with this technique as well. Just cut the line and paste it in the new location. Cheers,

Dave

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines