Example of a folder action that works on multiple files?

I have a folder action which allows me to drop a video file in and it then executes a shell script to trigger a FFMPEG conversion. It works fine, but it only works on file at a time. If I have 10 files, I want to convert I have to drag them in one by one.

Can anyone share with me a generic example of a folder action that shows how to handle multiple files please?

I thought that I could change 'Ignore partial files' to 'Trigger all changes immediately'. This sort of worked. But when I dragged 10 video files, only 3 were processed. Or is this not a KM issue but maybe something with FFMpeg that is causeing just a handful to be processed.

Any thoughts/help appreciated :slight_smile:

If you drag 10 individual files into the folder your macro will trigger 10 times -- once for each file added. So it's not the triggering...

The more likely problem is that you've used a global variable to store the file path. Those 10 macro instances fire at the same time, fighting each other to use that variable, and some instances are getting the same value as others -- you're doing 10 lots processing, but some files you are processing multiple times.

Make your variables local and most of your problems will be solved (I think!).

1 Like

I don't know if @kevinb was heading in the same direction, but I was also going to suggest using a "Semaphore Lock" to serialise your macros so they run one at a time.

While it's a Band-Aid for his particular problem -- better to correct your variable scoping -- it still might be a good idea. AFAIK ffmpeg will use multiple cores, so serialising won't result in most of your CPU being unused most of the time. Importantly, semaphore locks will stop multiple ffmpeg processes fighting for disk access.

So you might want to try your macro on the same 10 files both with and without a semaphore lock, to see which is faster.

1 Like

Does FFMpeg have a watched folder function?

This sounds very similar to a couple of media conversion tasks I do weekly for distributing my radio show segments to stations with different ingestion file format requirements (WAV to 320 MP3 and 256 MP3, etc.). My workflow is this:

  1. KM watches a folder for new media.
  2. When new media is encountered, KM launches Adobe Media Encoder '25.
  3. That second folder is configured as a watch folder in AME. By launching AME, it looks for files there, and executes the Format-Preset-Output configured for that inbox, converting the media to the new format and deleting the old formats.
  4. KM pauses until the original folder has been empty for 5 minutes, and then quits AME so I don't have to have it running in the background all the time.

(Technically, I use Hazel to monitor the folders and then kick off a KM macro, but KM can directly do the monitoring, too.)

1 Like

No, I was wandering down a different path and got lost. I do still wonder though about the triggering method... I know a lot of people like to use the mouse (or trackpad) more than I do, but dragging files to a folder to launch the macro seems inefficient to me.

For what it's worth, a macro like this could be triggered from any convenient keyboard method (macro by name, hotkey or whatever), and I would not use a folder trigger unless the files had arrived there as part of some other automation.

So, if I wanted to do that, what would I need to change in the example I provided? I'm not really all that clear what you mean by local variables, so what that also probably means is but if you looked at all of the macros I've ever worked on, you will probably have some kind of fit. Well, they might make me more efficient, they themselves are probably horribly efficient :smiley:

So would this work if, say, I had selected files in Finder and then triggered the macro with a hotkey?

Oh, I see that it is looking for files in a specific source folder. Ignore me ...

It's more likely the case that you should ignore me, since I'm not in the zone today!

But it's just the efficiency of the triggering method that I was wondering about. If the source folder is not always the same, then you can do things such as:

... if it should suit you to select the files first.

I have given this some more thought. The scenario I have is basically this: I create a lot of video files while doing some auditing of webpages. At the end of the process I have video files that can be in multiple folders in different locations within those folders. Ideally what I would like to do is to go to the search field in Finder, then type *.mp4 so that it lists all of the video files that are anywhere inside this root folder. Then, I would like to highlight those files in Finder and to then run a Keyboard Maestro macro which was run the FFMpeg conversion on each of those selected files and save them in the same location. I imagine it is a case of creating a new file then deleting the old one after the process has finished and then renaming the newly created file to match the old file name. The other issue here is that I have no way of knowing when the FFMpeg process has completed. Does anyone have something like this?

I wonder if it might be better to either (1) set things up so that the MP4s are created in the same folder or (2) get KM to move the MP4s to a common folder. That would make it easier to select the files manually (and, if you still wanted to use a folder trigger, only one folder would be involved).

If you were to use method (2), but you currently rely upon information in the names of the different folders, your macro could make up for that by appending informational text to the name of the files as needed.

You can use a For Each loop on Finder Selections, so set your macro with a hot key trigger of your liking. Do your search, select the matches, then invoke the macro. In the macro, use a For Each loop on the items in the Finder selection:

To make the macro wait until ffmpeg is done with each file, you can use a two-action sequence within the For Each loop. The first action is a Do Shell Script action:

ffmpeg -options -whatever -complex_stuff "$KMVAR_local_theFile"
osascript -e 'tell application "Keyboard Maestro Engine" to setvariable "zz_Done" to "true"'

The second action is an Until action with no actions inside it; here's one I use for a macro that uploads files via rsync:

My variable has a different name, but it's the one I set in the shell script action. After this action, the rest of your macro can continue, knowing ffmpeg is done.

This works because the shell command to set the global Keyboard Maestro variable (remember to delete it as the last step in the loop, so it's reset before the loop repeats!) won't happen until ffmpeg finishes, and the next line of the shell script action executes.

-rob.

You could add something to your shell script. Maybe this is a bit primitive, but how about adding ; echo "Completed" and then testing for that. Change "Ignore results" in the action to "Save results to a variable". Then test whether the variable contains "Completed".

/opt/homebrew/bin/ffmpeg -i "$KMVAR_FileName" -filter:v fps=30 -preset fast -crf 28 "$KMVAR FileNameNew"; echo "Completed"

Read the "Scope" section of the manual and you'll see that you make a variable "local" by starting its name with "local".

myVar -- global variable
localmyVar -- local variable
Local_myVar -- local variable

Global variables are persistent (they still exist after a macro finishes executing) and, important here, are accessible to all instances of all your macros -- if Macro A sets myVar to 1 then Macro B sets myVar to 2, Macro A will now see myVar as 2.

That's what's happening in your macro, except with executing instances of the same macro. By the time the first executing instance, triggered by the first file dropped, goes to get the first file's path from FileName -- the third instance, triggered by the third file, has changed the value to that of the third file's path.

Local variables don't persist, and are "private" to their executing instance -- each instance has its own version. So the first instance of your macro has its own Local_FileName to store the first dropped file's path in, the second instance its Local_FileName to store the second dropped file's path in, and so on.

You get an ffmpeg process for every file you add. Do you mean each process, or all processes?

But, unless you've set your shell script action to run asynchronously, the shell won't complete nor the macro move to the next action until ffmpeg has finished. So you should (test first!) be OK to delete and rename the files immediately after the shell script action.

I didn't read this entire thread, (so I may be missing the point) but when I saw that statement above I decided to chime in. This KM action should be able to tell you (when the loop ends) when a process of a specific name ("Snake" in this case) has finished. This approach may also work for FFMpeg.

image

Some really useful stuff here. Thanks all. I will go away and tinker some more!

Ah, they have to stay where they are - there are other process that will run later and files need to be in specific places.

This step would be reversible... that is, you could move back to the folders referenced in info in the file name...

It's just one possibility to perhaps consider.