Is 'Delay' the most efficient method for my Folder Action?

I am using a Folder Action to post-process mpeg files. I am using a piece of software called Tivotool to pull the mpeg files from my DVR.

TivoTool pulls file, and begins to write the mpeg file in folder “pre-process”
My folder action responds by running a ffmpeg command to process these mpeg files into another format, and places the final movie files in the folder “Post-Process”.

The problem is that TIvotool begins writing the mpeg file immediatly in my “pre-process” folder, so my Folder Action IMMEDIATLY begins to run, before the mpeg file is finished being written.

Since I am a complete noob to applescript, I have a delay set of 8 minutes. So, once TivoTool begins to write the mpeg file, the Folder Action waits 8 minutes before running my ffmpeg command. This allows TivoTool time to completely write the file - about 5-7 minutes for a 1 hour mpeg.

This works, but does not seem very efficient when I have several mpeg files in my TivoTool que. (Maybe I am using delay in the wrong place?) I was hoping one of you experts could show me a better way of doing this. Is there some applescript command that could tell when the mpeg file from TivoTool is finished being written, before it begins to act on that file? Or some other method?

on adding folder items to this_folder after receiving added_items -- Loop through the file references. repeat with this_item in added_items set this_name to name of (info for this_item) -- Get the POSIX path of the file being referenced. set this_File to quoted form of POSIX path of this_item delay 800 do shell script "~/Movies/vx8300/Temp/ffmpeg -i " & this_File & " -acodec aac -ac 2 -ar 22050 -ab 128 -f 3gp -vcodec h263 -s 176x144 -r 15 ~/Movies/vx8300/Post-Process/" & quoted form of this_name & ".3gp" end repeat end adding folder items to
Thank so much,
Ryan

Model: Mac Mini PPC
AppleScript: 1.10.7
Browser: Firefox 2.0b2
Operating System: Mac OS X (10.4)

You can use something similar to this code instead of the “delay” (pretty self-explaining):

set this_item_oldsize to 0
repeat
	delay 3
	set this_item_newsize to get eof of this_item --> get file size
	if this_item_newsize is this_item_oldsize then exit repeat
	set this_item_oldsize to this_item_newsize
end repeat

getting eof is much nicer that getting size, jj - neat way to size documents of any kind.

In regards to my original post, I tried the code jj supplied, but nothing happens when I copy a file into the folder. Here is what I have:


on adding folder items to this_folder after receiving added_items
	-- Loop through the file references.
	repeat with this_item in added_items
		set this_name to name of (info for this_item)
		-- Get the POSIX path of the file being referenced.
		set this_File to quoted form of POSIX path of this_item
		set this_item_oldsize to 0
		repeat
			delay 3
			set this_item_newsize to get eof of this_item --> get file size
			if this_item_newsize is this_item_oldsize then exit repeat
			set this_item_oldsize to this_item_newsize
		end repeat
		do shell script "~/Movies/vx8300/Temp/ffmpeg -i " & this_File & " -acodec aac -ac 2 -ar 22050 -ab 128 -f 3gp -vcodec h263 -s 176x144 -r 15 ~/Movies/vx8300/Post-Process/" & quoted form of this_name & ".3gp"
	end repeat
end adding folder items to 

Thanks again,
Ryan

Model: Mac Mini PPC
AppleScript: 1.10.7
Browser: Firefox 2.0b2
Operating System: Mac OS X (10.4)

Describe ‘nothing’ for us. Do you mean it does nothing at all?

One thing I’d try right away would be to change this line:

set this_item_newsize to get eof of this_item → get file size

to this:

set this_item_newsize to get eof of contents of this_item → get file size

I followed Adams suggested change, and managed to get the script working, with only one slight problem.

When I queue multiple items to be copied to the folder, the script will wait for the first file to be written completely, before processing the actions, but the problem is for the files afterwards, it will begin processing them immediately. It doesn’t seem to be ‘listening’ to the size part in the middle of the script.

Here is my code


on adding folder items to this_folder after receiving added_items
	-- Loop through the file references.
	repeat with this_item in added_items
		do shell script "chmod -R 777 ~/Movies/vx8300/1of3/*.*"
		
		set this_name to name of (info for this_item)
		-- Get the POSIX path of the file being referenced.
		set this_File to quoted form of POSIX path of this_item
		set this_item_oldsize to 0
		repeat
			delay 3
			set this_item_newsize to get eof of contents of this_item --> get file size
			if this_item_newsize is this_item_oldsize then exit repeat
			set this_item_oldsize to this_item_newsize
		end repeat
		delay 3
		
		do shell script "~/Movies/vx8300/ffmpeg -i " & this_File & " -acodec aac -ac 2 -ar 22050 -ab 128 -f 3gp -vcodec -b 96 h263 -s 176x144 -r 15 ~/Movies/vx8300/3of3/" & quoted form of this_name & ".3gp"
	end repeat
end adding folder items to

If I could get it to do the same file size check on all files that are being copied, and not just the first one, I will be all set.

Thanks so much,
Ryan

I’ve never known what was going on with folder actions, so I allways stick with a monitorization app for this kind of task, a stay-open applet checking a given folder periodically. Something as:

global dirToMonitorize, idleAmount, dirToMonitorizePP, detectedItems, queueItems, queueSizes

(*

This is a sample skeleton for a monitorization app. It will check the given folder (dirToMonitorize)
for new files. When they arrive -they finished growing in size-, it will execute the handler
"processItem" with such files as argument.

You can customize the vars dirToMonitorize and idleAmount, and add your own actions to the
"processItem" handler.

You can as well add error trapping, logging, etc. Specially if you will be deleting files, adding
new ones with old ones' names or replacing files, while this app is running. In such case, I would
simply check file sizes for every file, every time, and fire "processItem" when newSize = oldSize.
This way, you would rely only in the list-of-files and list-of-sizes of the last run, and check it
against the new list-of-files and list-of-sizes.

*)

set dirToMonitorize to alias "path:to:dir:"
set idleAmount to 2

to processItem(filespec) --> filespec = file "blah:foo:bar.mpg"
	tell application (path to frontmost application as text) to ¬
		display dialog (name of (info for filespec)) & return & return & ¬
			"Finished: " & (time string of (current date)) with icon 1 giving up after 5
end processItem


set dirToMonitorizePP to POSIX path of dirToMonitorize
set detectedItems to list folder dirToMonitorize without invisibles
set queueItems to {}
set queueSizes to {}

on idle
	set itemLst to list folder dirToMonitorize without invisibles
	if itemLst is not detectedItems then addNewItems(itemLst)
	if queueItems is not {} then monitorize()
	return idleAmount
end idle

to monitorize()
	set itemsInProcess to {}
	repeat with i from 1 to count queueItems
		set itemToMonitorizeNewSize to get eof of ¬
			(POSIX file (dirToMonitorizePP & queueItems's item i))
		if itemToMonitorizeNewSize is queueSizes's item i then --> item stopped growing
			processItem(POSIX file (dirToMonitorizePP & queueItems's item i))
			set itemsInProcess's end to i
		else
			set queueSizes's item i to itemToMonitorizeNewSize
		end if
	end repeat
	
	--> remove items sent to process from queue
	set queueItems to strip(itemsInProcess, queueItems)
	set queueSizes to strip(itemsInProcess, queueSizes)
end monitorize

to addNewItems(itemLst)
	repeat with i from 1 to count itemLst
		if itemLst's item i is not in detectedItems then
			set detectedItems's end to itemLst's item i
			set queueItems's end to itemLst's item i
			set queueSizes's end to 0
		end if
	end repeat
end addNewItems

to strip(itlst, lst)
	script o
		property a : lst
		property nl : {}
		property b : itlst
	end script
	set o's b to o's b as list
	repeat with i from 1 to count lst
		if i is in o's b then
		else
			set o's nl's end to o's a's item i
		end if
	end repeat
	o's nl
end strip

I agree with jj, '79. When I have troubles of this sort with a Folder Action, I always abandon that approach and go for a stay-open app with an ‘on idle’ handler that simply checks what’s going on externally to the folder itself. Folks worry that stay-open AppleScripts will tie up their CPU but for tasks like this that is not so - when you are through with the task, you quit the stay-open in the dock.

Adam, JJ, thank you for script information.

How do I use this script? I tried saving it as an application/run only - but it doesn’t seem to do anything once I run it and try dropping files in the folder. I’m just doing a simple chmod to test the operation of the script.



global dirToMonitorize, idleAmount, dirToMonitorizePP, detectedItems, queueItems, queueSizes

(*

This is a sample skeleton for a monitorization app. It will check the given folder (dirToMonitorize)
for new files. When they arrive -they finished growing in size-, it will execute the handler
"processItem" with such files as argument.

You can customize the vars dirToMonitorize and idleAmount, and add your own actions to the
"processItem" handler.

You can as well add error trapping, logging, etc. Specially if you will be deleting files, adding
new ones with old ones' names or replacing files, while this app is running. In such case, I would
simply check file sizes for every file, every time, and fire "processItem" when newSize = oldSize.
This way, you would rely only in the list-of-files and list-of-sizes of the last run, and check it
against the new list-of-files and list-of-sizes.

*)

set dirToMonitorize to alias "Firewire:Users:mac:Movies:vx8300:1of3:"
set idleAmount to 2

to processItem(filespec) --> filespec = file "blah:foo:bar.mpg"
	do shell script "chmod -R 777 ~/Movies/vx8300/1of3/*.*"
	tell application (path to frontmost application as text) to ¬
		display dialog (name of (info for filespec)) & return & return & ¬
			"Finished: " & (time string of (current date)) with icon 1 giving up after 5
end processItem


set dirToMonitorizePP to POSIX path of dirToMonitorize
set detectedItems to list folder dirToMonitorize without invisibles
set queueItems to {}
set queueSizes to {}

on idle
	set itemLst to list folder dirToMonitorize without invisibles
	if itemLst is not detectedItems then addNewItems(itemLst)
	if queueItems is not {} then monitorize()
	return idleAmount
end idle

to monitorize()
	set itemsInProcess to {}
	repeat with i from 1 to count queueItems
		set itemToMonitorizeNewSize to get eof of ¬
			(POSIX file (dirToMonitorizePP & queueItems's item i))
		if itemToMonitorizeNewSize is queueSizes's item i then --> item stopped growing
			processItem(POSIX file (dirToMonitorizePP & queueItems's item i))
			set itemsInProcess's end to i
		else
			set queueSizes's item i to itemToMonitorizeNewSize
		end if
	end repeat
	
	--> remove items sent to process from queue
	set queueItems to strip(itemsInProcess, queueItems)
	set queueSizes to strip(itemsInProcess, queueSizes)
end monitorize

to addNewItems(itemLst)
	repeat with i from 1 to count itemLst
		if itemLst's item i is not in detectedItems then
			set detectedItems's end to itemLst's item i
			set queueItems's end to itemLst's item i
			set queueSizes's end to 0
		end if
	end repeat
end addNewItems

to strip(itlst, lst)
	script o
		property a : lst
		property nl : {}
		property b : itlst
	end script
	set o's b to o's b as list
	repeat with i from 1 to count lst
		if i is in o's b then
		else
			set o's nl's end to o's a's item i
		end if
	end repeat
	o's nl
end strip

I greatly appreciate all the help in getting me this far.

Ryan

Ryan;

Your script need not be ‘run only’, but it must be ‘stay-open’. The ‘on run’ portion of the script (in your case, implicit - it’s everything that isn’t a handler at the top level of the script) will run only once when you start the script. After that, only what’s in the ‘on idle’ portion and handlers it calls will run every ‘idleAmount’ seconds. That means your ‘on idle’ handler has to keep things current. At the end of your handler for example, you ought to save a new value of ‘detectedItems’ so anything you have processed will not be processed again. If that’s not clear, ask some more.

Adam

Just an tiny extension to Adam’s answer, in the FAQ: http://applescriptsourcebook.com/viewtopic.php?id=14466