OK. I have some code that I use to download zipped folders from the web. Occasionally, something goes wrong. If I’m present, I want to have a chance to correct the problem. If I’m asleep I want the code to skip over the problem and go on to the next zipped folder.
What happens is that I sometime get an Apple Events Timed Out error from the Display Dialog. As you can see, I’ve tried using a with timeout to solve the problem, but it doesn’t always work. I know I could trap the error with a Try block and an On Error, but I’d like to fix the underlying problem instead. I’ve also noted that the 20 second delay in my code doesn’t always work. The first two time around, the dialog is displayed for less than a second. The third time it seems to be up for 20 seconds. After that I sometimes get short delays and sometimes 20 delays. I have no idea why this happens.
By the way, the reason for the folder_name _list is because I can not be sure of the actual name the folder will load as, so I make a list of the most likely possibilities. This is one reason the load sometimes fails.
Any Idea?
on save_zip_folder(folder_name, zip_URL, folder_name_list, download_folder)
tell application "Safari" to open location zip_URL
set delay_count to 0
set folder_loaded to false
repeat while delay_count < 600
tell application "System Events"
repeat with loaded_name in folder_name_list
if (exists folder loaded_name in folder download_folder) then
set folder_loaded to true
exit repeat
end if
end repeat
if (exists folder loaded_name in folder download_folder) then
set folder_loaded to true
exit repeat
end if
end tell
if delay_count > 120 then
with timeout of 600 seconds
set alert_reply to (display alert "Long Wait" message "Looking in " & download_folder & " for " & (item 1 of folder_name_list) as warning buttons {"Skip", "Continue"} giving up after 20)
end timeout
if (button returned of alert_reply) = "Skip" then
exit repeat
end if
delay 30
set delay_count to delay_count + 50
else
if delay_count > 90 then beep
delay 10
set delay_count to delay_count + 10
end if
end repeat
tell application "Safari" to close document 1
if folder_loaded then
tell application "Finder"
set comment of folder (download_folder & loaded_name) to zip_URL
set name of folder (download_folder & loaded_name) to folder_name
end tell
end if
return folder_loaded
end save_zip_folder
Model: Mac Pro
AppleScript: 2.4.3
Browser: Safari v5.0.5
Operating System: Mac OS X (10.7)
tell application "System Events"
repeat with loaded_name in folder_name_list
-- loaded_name is a reference to a list item
if (exists folder loaded_name in folder download_folder) then
set folder_loaded to true
exit repeat
end if
end repeat
-- outside the loop, loaded_name is unknown
if (exists folder loaded_name in folder download_folder) then
set folder_loaded to true
exit repeat
end if
end tell
I believe it should be possible to obtain the download’s name from the web page, and do away with the list of possible names.
Edit:
“zip_URL”: is that the page where to find the download, or is it the download? If the 2nd, it contains the name, right?
I have no idea why the dialog code does not perform the way you want, although I have doubts about the ‘with timeout’ construct which contains the dialog code. In a way, the script uses 3 devices to allow the download to complete: ‘with timeout’, ‘giving up after’, and the loop variable delay_count - and they seem not to work together.
You might try inserting a ‘log delay_count’ statement after each potential change, and see if it behaves as expected.
There may be an entirely different solution: the ‘waiting for a download to complete’ problem has probably been solved many times, but not by me…
Oh, and “open location” is not a Safari command, it belongs to Standard Additions. You can delete the “tell Safari to” bit at the top.
Thanks for the reply. I didn’t realize the Open Location would work without the Tell Application “Safari”. I tried it that way and it does work. I assume that without the tell it uses the default web browser.
As for the suspected bug. Sorry, that code works fine. loaded_name retains its value after the repeat loop ends regardless of whether the loop ends normally or via the exit repeat. If there is a normal exit it has the last used value. As an old-school structured programmer I agree that depending on a loop variable outside the loop can be risky, but it works in AppleScript.
I also believe it should be possible to obtain the download’s name from the web page, but I haven’t discovered a 100% reliable method. The zip_URL contains a URL in the form “http://something/something/.../something/zipname.zip”. Now my Mac automatically unzips zipname.zip 99.99% of the time, so I have to look for the unzipped name. The problem is that the unzipped folder name is not always zipname. That’s where the the folder_name_list comes in. I’ve discovered most of the exceptions used by the various web sites involved. In some cases a suffix is added or removed. In other cases the unzipped file has a default name like “maps” or “images” regardless of the zipname. I suspect those may be cases where the person who created the folder used a template and forgot to rename it.
Unless there is a problem, the code works fine. If there is a problem, it works OK most of the time. After 90 seconds it starts beeping every 10 seconds. At 2 minutes it displays the first alert, but only for less than a second. After another 30 seconds it displays the alert again, but still for less than a second. After yet another 30 seconds, it displays the alert again, this time for 20 seconds if I do nothing. The problem is that while looping till the 5 minutes (600 seconds) are up it will eventually time-out on the Display Alert. I haven’t pinned down when in the loop it fails. I do that now. Before I added the With Timeout block it timed out the first time the Display Alert was executed.
The timeout is caused when AppleScript is not the active application! Naturally, when I was looking for the problem it was the active app so I never saw it time out. I’ve added a “Tell me to activate” line ahead of the display alert and that solved the most important part. I also discovered the With Timeout has no value and I’ve removed it.
The only remaining piece is the random delay time for the Display Alert. No matter what I do, most of the time the display stays up for less than a second, too short to click it. HOWEVER, I just discovered that the giving up after works consistently in the Display Dialog!!! So, I’ve converted the Display Alert to a Display Dialog (with appropriate changes to the other parameters) and it seems to work fine.
The only remaining question is: Why does Display Dialog work with giving up after and Display Alert does not? In a simple one-line test of Display Alert that option works fine, but in my code it mostly doesn’t.
Yes, it is strange that the giving up works correctly outside the loop and not inside it. I can live with my fix, but the programmer in me would love to learn the cause.
Huh? :mad: Might call me old school too. I would never dream of using a loop variable outside its loop.
As for as I know ‘display dialog’ has been with us since day one, but ‘display alert’ is a later addition. Might that suggest a cause, in the light of other recent ‘slipups’?
Here’s a kludge if you have to use ‘display alert’ rather than ‘display dialog’:
-- Preferably somewhere before the repeat.
set currentApp to name of current application
-- Then at the point where the alert's displayed.
if delay_count > 120 then
activate
-- Set up an external process to click the alert's default button if it's still there after 20 seconds. (Uses GUI Scripting.) Don't wait for a response, but .
do shell script "osascript -e 'delay 20
tell app \"System Events\"
if (static text \"Long Wait\" of window 1 of application process \"" & currentApp & "\" exists) then keystroke return
end tell' > /dev/null 2>&1 & "
-- . display the alert immediately without a 'giving up after' parameter.
set alert_reply to (display alert "Long Wait" message "Looking in " & download_folder & " for " & (item 1 of folder_name_list) as warning buttons {"Skip", "Continue"})
-- etc.
end if