Long time reader, first time poster. This forum has been an excellent resource for me as I’ve learned to build programs with Xcode for the company I work for. I have finally hit a bug that I cannot find an answer for anywhere though.
Happy to post code snippets if needed, but I don’t think this is script related. Basically the program is an ffmpeg/rsync utility that automatically takes videos people record out in the field, compresses them, and then uploads them to our servers here at work. I use expect scripts to handle output from the rsync command. For progress reporting I run the scripts as background processes with stdout and stderr going to a .txt file that is read and parsed in loops that repeat until the output at the end of the loop equals the output at the beginning. All commands are referenced inside the Resources of the application.
So here is what keeps happening. When I clean, build, and run the program everything works perfectly, as long as I don’t touch the mouse. As soon as I touch the mouse or click it the program will skip forward in the script. It happens across the entire app. It is mostly noticed in my progress repeat loops that will exit moving on to the next process that it is not ready for. The bug continues to occur after the app has been archived and on all machines it is run on. I have already tried rebuilding from scratch, though a lot of things were copied from what I had deemed the “unstable” version. What on earth could be causing this strange behavior!!!
I have tried everything I can think of. Please help!!!
this is cocoa applescript (I assume the same thing but I just wanted to verify)
also, i am currently researching if there is a way to have the app ignore mouse events during the repeat loops. kind of a work around. I am having no luck as of yet so if anyone knows of a way to do this that’d be awesome. Unless of course you can figure out the underlying problem
None that I am aware of, except for buttons that act as senders to initiate parts of the script. My expertise in these cocoa-applescript apps is really just the applescript and the bourne commands within. I will be buying your book soon to hopefully learn more about the ObjC side of these things but for now those files remain untouched in my app.
I apologize in advance for not having many resources to dedicate to the troubleshooting. The program doesn’t go anywhere if you don’t have a username and password to ssh into our servers. I could share the project (as I feel this is a problem with it as a whole and not necessarily a single line somewhere), but the issue could really only be reproduced if I set up credentials for someone on my end. I am very trusting of these types of communities, but my bosses are not so much and if something bad were to happen…well I’d have some explaining to do haha. I suppose it could be easily adapted for any other ssh server though if someone is really dedicated to helping me figure this one out
EDIT: I am noticing some other weird problems now in Xcode that are happening across other projects. Nothing affecting functionality, but for instance, when a window is selected in the interface builder, the screen position tab of the utility pane is blank across all of my projects out of nowhere. Perhaps I should try removing and reinstalling Xcode. If I were to do so would this affect any of my projects?
EDIT 2: I’m an idiot. the screen positioning/sizing pane was collapsed inside itself. I didn’t even know you could do that (and frankly I don’t see why you can lol) Still having the original issue though
There are a few of them, but here is the repeat loop that handles compression with it’s context. apologies for any redundancy or anything that makes you just think “why the heck did he do it that way”. This thing has been kind of an ongoing project since the onset of me learning Xcode and my thoughts were very disconnected at times. The file drop area is basically a huge text field as for the life of me I cannot figure out drop areas in Xcode for files. I like to think I am smarter then this, but I clearly need to learn more about how the underlying ObjC works.
on initiateCompression_(sender)
-- First we want to make sure we have all the information we need, and remove any temp files that might be left from a previous session
try
do shell script "rm -rf /tmp/*"
end try
-- find out have many files have been specified and build a string accordingly
-- The initial string will first be modified to get rid of any empty entries, and then will be run through a subroutine that compensates for any characters that need to be escaped to get past the UNIX shell
set single_string to (stringValue() of files_drop) as string
set AppleScript's text item delimiters to {return, linefeed, return & linefeed, linefeed & return}
set file_list to text items of single_string as list
set AppleScript's text item delimiters to {}
set files_to_compress to {}
set files_to_upload to {}
repeat with i from 1 to number of items in file_list
set this_item to item i of file_list
set this_item to replaceString(this_item, " ", "\\ ")
set this_item to replaceString(this_item, "(", "\\(")
set this_item to replaceString(this_item, ")", "\\)")
set files_to_compress's end to this_item
end repeat
if files_to_compress is {}
error_dialog's setStringValue_("No files specified")
error_window's orderFront_(me)
error number 128
end if
-- if compression is enabled run the files through the bundled ffmpeg application
set compression to (stringValue() of compression_enabled) as text
if compression is equal to "1" then
-- set the location of ffmpeg to a variable
try
set path_to_resources to (POSIX path of (path to current application))
set ffmpeg to (path_to_resources & "Contents/Resources/ffmpeg") as Unicode text
on error errMsg
error_dialog's setStringValue_("Could not locate the compressor. Try without compression or contact IT: " & errMsg)
error_window's orderFront_(me)
error number -128
end try
set total_time to 0
tell progress_bar to startAnimation:me
progress_label's setStringValue_("Preparing files for compression")
repeat with a from 1 to number of items in files_to_compress
set this_file to item a of files_to_compress as text
set getinfo to (ffmpeg & " -i " & this_file & " &> /tmp/output.txt &")
do shell script getinfo
delay 0.5
set the_specs to (do shell script "grep \"Duration\" /tmp/output.txt")
set full_duration to characters 13 thru 23 of the_specs as text
set duration to convertduration(full_duration)
set total_time to total_time + duration
do shell script "rm -rf /tmp/output.txt"
end repeat
-- run the files through the compressor updating the progress indicators as necessary.
try
tell progress_bar to setIndeterminate:FALSE
tell progress_bar to setMaxValue:total_time
tell progress_bar to setDoubleValue:0
progress_label's setStringValue_("Compressing the media...")
repeat with a from 1 to number of items in files_to_compress
set this_file to item a of files_to_compress
set Applescript's text item delimiters to "."
set file_path to text item 1 of this_file
set extension to last text item of this_file
if (extension is not equal to "mov") then
display dialog "Compression for non-mov files is not supported!"
error number -128
end if
set AppleScript's text item delimiters to "/"
set file_name to text item -1 of file_path
set file_name to replaceString(file_name, " ", "")
delay 0.5
set compress_script to ffmpeg & " -i " & this_file & " -map 0:0 -map 0:1 -map 0:2 -map 0:3 -map 0:4 -map 0:5 -map 0:6 -map 0:7 -map 0:8 -acodec copy -codec:v mpeg4 -b:v 8000k -threads 0 -codec:a libvo_aacenc /tmp/" & file_name & ".mp4 &> /tmp/output.txt &"
do shell script compress_script
delay 3
my compressionProgress()
end repeat
tell progress_bar to setIndeterminate:true
do shell script "rm -rf /tmp/output.txt"
on error
error_dialog's setStringValue_("Compression failed! Try without compression or contact IT")
error_window's orderFront_(me)
error number -128
end try
set files_to_upload to (paragraphs of (do shell script "ls /tmp/*.mp4")) as list
tell progress_bar to setIndeterminate:TRUE
delay 0.5
else
tell progress_bar to startAnimation:me
progress_label's setStringValue_("Preparing files for upload...")
delay 0.5
repeat with a from 1 to number of items in files_to_compress
set this_file to item a of files_to_compress
set files_to_upload's end to this_file
set copy_script to "cp -r" & space & this_file & space & "/tmp/"
do shell script copy_script
end repeat
delay 0.5
end if
my initiateUpload_()
end initiateCompression_
Here are the relevant subroutines to that section.
on compressionProgress()
set initial_duration to 0
repeat
delay 0.5
set the_output to paragraph -3 of (do shell script "cat /tmp/output.txt|tail")
set full_duration to characters 48 thru 59 of the_output as text
set full_duration to replaceString(full_duration, " ", "")
set full_duration to replaceString(full_duration, "/", "")
set current_duration to convertduration(full_duration)
set the_difference to (current_duration - initial_duration)
set initial_duration to current_duration
tell progress_bar to incrementBy:the_difference
if the_difference is 0 then
do shell script "rm -rf /tmp/output.txt"
delay 0.5
exit repeat
end if
delay 0.5
end repeat
end compressionProgress
on convertduration(full_duration)
set h to characters 1 thru 2 of full_duration as text
set m to characters 4 thru 5 of full_duration as text
set s to characters 7 thru 8 of full_duration as text
set ms to characters 10 thru 11 of full_duration as text
set ms_hour to h * 36000
set ms_min to m * 6000
set ms_sec to s * 100
set new_duration to (ms_hour + ms_min + ms_sec + ms)
return new_duration
end convertduration
on replaceString(theText, oldString, newString)
set AppleScript's text item delimiters to oldString
set tempList to every text item of theText
set AppleScript's text item delimiters to newString
set theText to the tempList as string
set AppleScript's text item delimiters to ""
return theText
end replaceString
I am aware that I am starting to push Applescript’s abilities to their limits. That’s why it’s time to start running with this newfound passion of mine
So what you really need to do is get rid of most, if not all, uses of the delay command, and use NSTimer instead. Instead of sitting in a loop, your bits of code will be in handlers called periodically by timers.
You will probably also need to use displayIfNeeded() to update the display periodically. This is something the delay command sort of handles as well, but the way it does it is partly the cause of your problems. And I suspect it’s in your script several times more for the side-effect of updating things than any need to bring the process to a halt.
Thanks so much! Just for some clarification. I have never used displayifneeded before. Would the syntax for that in my case be as simple as something like this instead of my current setStringValue()'s
tell progress_label to displayIfNeeded("Current progress here")
Sorry i needed to edit hopefully I didn’t throw you off Shane. as I think I am sort of understanding it.
on myFunction_(sender)
do shell script (start_my_compression_here)
set myTimer to NSTimer’s scheduledTimerWithTimeInterval_target_selector_userInfo_repeats_(1, me, “compressionProgress:”,
end myFunction_
on compressionProgress_(sender)
–repeat loop for displaying progress in here (minus the repeat part)
end compressionProgress_
No. You usually have an outlet to the window, say theWindow, and use:
theWindow's displayIfNeeded()
whenever you want to update the display within long-running code.
You’ll probably want to use scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:, which creates it and queues it at the same time.
The above is where you do the work you’re now doing in your repeat loop.
No, your timer does it for you. If you really need just a delay, you can possibly use performSelector:withObject:afterDelay:, or NSThread’s sleepForTimeInterval:.
I don’t want to sound like an ad, but this stuff is hard to in a few posts here – that’s why I wrote a book on it.
You’re absolutely right and I appreciate you taking the time to help. Since I am doing this all for my company I was kind of holding out to see if I can’t talk them into buying it for me. I’ll play around with the information you’ve provided me and check back if I run into any trouble or if all gets fixed.
NSThread’s sleepForTimeInterval: in place of my delays solved all my issues for now. I can see where I need to add displayIfNeeded and I will be sure to take care of that, but I am about to retire for the weekend.
After reading the documentation on NSTimer you are absolutely right that that is how I should be handling this, but I will wait until I get my hands on your book to get a better grasp on how to use the ObjC framework within my cocoa apps.