Mac Create an agent to watch program launches

maverick28

macrumors 6502
Original poster
Mar 14, 2014
369
248
Hello,

I want to create an agent that would launch a certain script when iTunes is launched. Obviously, with the traditional set of properties such as KeepAlive and RunAtLoad it will resume endlessly as soon as the default 10-sec interval elapses which is not the desired behaviour as I need it running the script at iTunes' launch time and only once per launch session. I'm not a programmer, so my knowledge is not first-hand, but I'm aware of the notions of file descriptors, ports and files opened by a Mac application. In Activity Monitor I see those but I don't know which of them could be useful. If it's a path than I'd experiment with it by setting either WatchPaths, PathState or QueueDirectories but I don't even have a clue what paths and ports are indicative of the launched process or if it's some other properties of Launchd.plist.

My job is as follows:

XML:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>org.smartsolutions.addtracks</string>
    <key>ProgramArguments</key>
    <array>
        <string>/usr/bin/osascript</string>
        <string>/Users/me/Documents/Run Targeted iTunes Script.scpt</string>
    </array>
    <key>KeepAlive</key>
    <true/>
    <key>RunAtLoad</key>
    <true/>
    <key>StandardOutPath</key>
    <string>/Users/me/Documents/MyLaunchd_Logs/Addtracks-out.log</string>
    <key>StandardErrorPath</key>
    <string>/Users/me/Documents/MyLaunchd_Logs/Addtracks-err.log</string>
</dict>
</plist>
NOTE. "Run targeted iTunes script.scpt" is the script that targets (runs) another AppleScript script.
 
Last edited:

maverick28

macrumors 6502
Original poster
Mar 14, 2014
369
248
Something would need to watch for launches, so take a look at adding an observer for NSWorkspace's NSWorkspaceDidLaunchApplicationNotification notification to whatever you are using for your agent app.
As I said I'm not a programmer, I don't write apps nor am I familiar with programming languages such as Obj-C and others. I just wrote a simple XML plist and want to know how to make it watch iTunes launches.
 

Red Menace

macrumors 6502
May 29, 2011
428
76
Littleton, Colorado, USA
A launchd.plist doesn't watch for application launches, the daemon/agent it manages is what would do that. You didn't mention what you are using, so I just suggested that approach.
 

maverick28

macrumors 6502
Original poster
Mar 14, 2014
369
248
I gave an account quite explicitly in my first post and even posted the plist itself. I was seeking for more detailed and clear recommendations, not "something could be done about this or that". I failed to understand what did you mean by "the daemon/agent it manages is what would do that" and why launchd.plist doesn't watch. Since it operates in my home directory it should kick into action as soon as I log into my account. What other kinds of demons and agents are you talking of?
 
Last edited:

Red Menace

macrumors 6502
May 29, 2011
428
76
Littleton, Colorado, USA
Launchd can be set to run jobs in a few situations, such as on a schedule or filesystem changes or mounts, but it isn't a general purpose scripting tool - it just loads and maintains services, which are the daemons and agents that you are launching with a launchd.plist. It doesn't sound like you need to use launchd at all, just a login application to watch for workspace notifications.
 

maverick28

macrumors 6502
Original poster
Mar 14, 2014
369
248
The problem is it either runs continually or doesn't at all with every approach. I need it to run after iTunes is launched but only once. The script that's the bare bones of the workflow is a simple AppleScript script that looks for certain tracks and adds them if those satisfy my search criteria. Even if it was a login item then how would I tell it to run only ONCE? It has the "if running of application "iTunes" then [the rest of the script]" clause, so it will be running as long as iTunes is running with or without launchd. That's the problem.
 
Last edited:

Red Menace

macrumors 6502
May 29, 2011
428
76
Littleton, Colorado, USA
The following script, saved as a stay-open application, sets up an observer for application launches and will run the doStuff handler each time iTunes is launched - just replace the contents of the handler with your current workflow (minus any statements that check if iTunes is running). After the app is running to your liking, it can be set to be an agent (no menu or dock icon) by adding the LSUIElement key to the application Info.plist.

AppleScript:
use scripting additions
use framework "Foundation"

property NSWorkspace : a reference to current application's NSWorkspace
property launchNotification : a reference to current application's NSWorkspaceDidLaunchApplicationNotification
property workspaceNotificationCenter : missing value

on run
   tell NSWorkspace's sharedWorkspace's notificationCenter -- add observer for app launches
      set workspaceNotificationCenter to it
      its addObserver:(me) selector:"appLaunchNotification:" |name|:launchNotification object:(missing value)
   end tell
end run

on appLaunchNotification:aNotification -- an application was launched
   # aNotification's userInfo contains the application that was launched
   set applicationInfo to NSWorkspaceApplicationKey of aNotification's userInfo
   if (localizedName of applicationInfo) as text is "iTunes" then doStuff()
end appLaunchNotification:

on doStuff() -- replace with workflow to run
   beep
   activate me
   display dialog "Doing iTunes Stuff..." giving up after 3
end doStuff

on quit
   workspaceNotificationCenter's removeObserver:me
   continue quit
end quit
 

maverick28

macrumors 6502
Original poster
Mar 14, 2014
369
248
The following script, saved as a stay-open application, sets up an observer for application launches and will run the doStuff handler each time iTunes is launched - just replace the contents of the handler with your current workflow (minus any statements that check if iTunes is running). After the app is running to your liking, it can be set to be an agent (no menu or dock icon) by adding the LSUIElement key to the application Info.plist.

AppleScript:
use scripting additions
use framework "Foundation"

property NSWorkspace : a reference to current application's NSWorkspace
property launchNotification : a reference to current application's NSWorkspaceDidLaunchApplicationNotification
property workspaceNotificationCenter : missing value

on run
   tell NSWorkspace's sharedWorkspace's notificationCenter -- add observer for app launches
      set workspaceNotificationCenter to it
      its addObserver:(me) selector:"appLaunchNotification:" |name|:launchNotification object:(missing value)
   end tell
end run

on appLaunchNotification:aNotification -- an application was launched
   # aNotification's userInfo contains the application that was launched
   set applicationInfo to NSWorkspaceApplicationKey of aNotification's userInfo
   if (localizedName of applicationInfo) as text is "iTunes" then doStuff()
end appLaunchNotification:

on doStuff() -- replace with workflow to run
   beep
   activate me
   display dialog "Doing iTunes Stuff..." giving up after 3
end doStuff

on quit
   workspaceNotificationCenter's removeObserver:me
   continue quit
end quit
Thank you, very much. What's the compatibility? I see it's AppleScript-ObjC.
 

Red Menace

macrumors 6502
May 29, 2011
428
76
Littleton, Colorado, USA
It uses some AppleScriptObjC for the workspace notifications, but it is still AppleScript. I don't know what is in your script, but you should be able to put it in (or call it from) the doStuff handler with minimal (if any) changes. Note that the handler is only called when iTunes is launched, so you won't need to check if it is running.
 

maverick28

macrumors 6502
Original poster
Mar 14, 2014
369
248
Hello,
I get compilation errors in 10.7 about the colon before the "(me)" reference and, possibly, the one-line syntax of putting the properties inside the run handler.
 

Red Menace

macrumors 6502
May 29, 2011
428
76
Littleton, Colorado, USA
Properties cannot be declared in a handler, they need to be declared in their containing script object.

The interleaved method syntax was introduced in OS X 10.9 Mavericks, so if you are using something older than that you will need to use the older underscore style, for example

Code:
addObserver_selector_name_object_(me, "appLaunchNotification:", launchNotification, missing value)
and

Code:
workspaceNotificationCenter's removeObserver_(me)
There may be more differences that I can't remember, since I haven't run anything that old in quite a while.
 

maverick28

macrumors 6502
Original poster
Mar 14, 2014
369
248
I corrected the syntax and it compiled, however, running the script stumbled over the error -1700 "Can’t make notificationCenter of sharedWorkspace of NSWorkspace into type reference." I'm talking about your explicit run handler.

If it's MountainLion's and higher Notification Center then it doesn't belong to Lion.
 

Red Menace

macrumors 6502
May 29, 2011
428
76
Littleton, Colorado, USA
I think you are talking about a different Notification Center - the NSNotificationCenter class has been in the Cocoa API since 10.0. If you are actually trying to build the application in Lion, you will probably need to use Xcode or maybe the Script Editor's Cocoa-AppleScript template, since being able to use AppleScriptObjC anywhere was a feature introduced in Yosemite (AppleScript's use statement didn't exist back then).
 

maverick28

macrumors 6502
Original poster
Mar 14, 2014
369
248
If you are actually trying to build the application in Lion, you will probably need to use Xcode.
I don't need to build an application. I just want to use it as a standalone AppleScript script or applet as you suggested previously. It throws the aforementioned coercion error when I try to execute your script.
 

Red Menace

macrumors 6502
May 29, 2011
428
76
Littleton, Colorado, USA
An applet is an application. If you are looking at building my posted script on a ten-year-old system (or pre-Yosemite), you will probably need to use Xcode (it has an AppleScript app template) - I don't have a system that old to test with. In the future you might mention the system you are developing for.
 

maverick28

macrumors 6502
Original poster
Mar 14, 2014
369
248
1 of all, as of 16th post in this thread you know the system version I'm talking about. 2, your code doesn't run without errors from Script Editor on this system. I want to know why and how to make it run.
And to build an app around AppleScript you don't need Xcode, it's as simple as saving the current script file as an app in Script Editor.
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.