Applescript - how to control multiple instances of the same applicatio

This question can sound familiar, but I tried to adapt several strategies without much luck.

I have 2 or more instances of the same application supporting segregation (e.g. Firefox, Chrome, Spotify, open -n etc) and I’m trying to control one specific instance by PID (other methods are welcome) via Applescript and the app specific suite / dictionary.

E.g. after launching a couple Spotify instances with --mu argument, I’m able to iterate through instances and display a dialog:


tell application "System Events"
	set pidList to the unix id of (processes whose name contains "Spotify")
	repeat with someID in pidList
		tell (first process whose unix id is someID)
			display dialog someID
		end tell
	end repeat
end tell

But, as soon as I try to send specific commands, I get variable not found, because the application is not recognized through its specific suite. E.g. these don’t work:

tell (first process whose unix id is someID) to play
tell application (whose unix id is someID) to player state
tell (first process whose name is "Spotify") to stop

I will appreciate your help and I will try your hints :slight_smile:

Model: MacMini 2011 & 2019
AppleScript: 2.5
Browser: Safari 605.1.15
Operating System: macOS 10.12

You can address specific versions by including the full path in the tell statement:

tell application "Macintosh HD:Applications:Some App 1.0"

Thanks for the hint, but I don’t have multiple versions. I’m trying to control multiple instances of the same application, e.g. /Applications/Spotify.app. I hope that now it’s clearer.

Processes do not understand the commands of the application itself. Instead of sending a play command to a process, send to its frontmost window click menu item Play of menu… of menu bar 1 of process.

If you have several instances of the same application, then more than one icon of the same application should appear in the dock. Otherwise, these are ordinary multiple instances - windows of 1 instance of application. Which Finder also has a lot. To have real several instances of the same application at least they should defer by name or location on the disk(s)

For example, to be able to run 2 real instances of VLC.app, you can store 1-st VLC.app on hard disk, and 2-nd on external disk. With same base name, but different locations.

See here 2 real instances of VLC.app. One VLC.app I have in Applications folder of system domain, other in Applications folder of user domain. Note that 2 same icons of VLC in the Dock are opened. And note the 1st has focus currently (blue colored). Well, now I can see 2 different movies at the same time, and write the scripts… Like Julius Caesar … :lol:

Then I’m afraid you’re out of luck. Which is ironic, given that AppleScript reference apps by PID under the hood itself.

@KniazidisR I appreciate your hints, VLC is one of those apps that support multiple instances. Unluckily it’s not exactly my situation, so I post a couple pictures to clarify.

The first image depicts multiple Spotify instances. To see the multiple icons on the dock, you need to open https://funkyimg.com/i/33WdV.png

I’ve run 4 instances to make the screenshot, but they could have been 400. They have different prefs folders, different PIDs, but same bundle identifier “com.spotify.client”, same application path “/Applications/Spotify.app”, the same dictionary suite “Spotify Suite”.

The second picture is basically the same, with Chrome as example: https://funkyimg.com/i/33WdW.png

Again they have different prefs folders, different PIDs, but the same bundle identifier “com.google.chrome”, the same application path “/Applications/Google\ Chrome.app”, the same dictionary “Chromium Suite”.

I’m able to iterate them with “System Events” and to use the Standard Suite (e.g. I can send each instance to foreground). But I’m unable to bind the specific Suite, in order to send specific commands to each instance. An example for Spotify is “player state”, which return important informations that can be parsed.

Is there a method to make this pseudocode work?


tell application "System Events"
	set pidList to the unix id of (processes whose name contains "Spotify")
	repeat with someID in pidList
		tell (first process whose unix id is someID)
			bind "Spotify Suite" 
			set PlayerState to player state 
			display dialog PlayerState
		end tell
	end repeat
end tell

I think, you should speak to frontmost window of process someID. Only using GUI scripting as I say in the first post. But I am not sure this will help. Try, try, try clause, try again :slight_smile:

I appreciate the suggestion, but I’m not so skilled. Can you please give me a couple examples of code?

To test, I opened 2 movies in 2 VLC instances, then paused both movies.

The next code gives command to begin play only the first movie (that is, only of first instance):


tell application "System Events"
	set pidList to the unix id of (processes whose name contains "VLC")
	
	tell (first process whose unix id is item 1 of pidList) -- the first only
		tell menu bar 1
			tell menu "Playback"
				click menu item "Play"
			end tell
		end tell
	end tell

end tell

NOTE: the windows of 2 instances of VLC may have on the screen same bounds initially. So, one may be hidden beside the other. In this case, drag the frontmost little to left, or to right, to see both windows

I appreciate your replies and your efforts in suggesting me a workaround.

I have not so clear how windows apply in your code. I have clear that you control VLC via menu items (they’re standard commands) as I saw for VLC in several codes around, but I’m more speaking about general situations.

If I would need just to play / stop Spotify, I would just use space keystroke. I’m referring to general situations e.g. Chrome, where you may want to perform actions not included into menu items, e.g. the command “execute” in Chrome Suite.

The example “player state” I gave for Spotify is the best one: it returns informations that can be then parsed back from the script, avoiding visual feedback which relies on somebody in front of the screen, hence it’s out of automation context.

I’m not the best in AS like in other languages, so basically I’m asking if there’s a way to iterate some unique identifier of those available (frontmost window, process id, a generated file / folder, etc) to “bind” the custom dictionary commands e.g. “Spotify Suite” etc. Or the bundle identifier, or anything that would allow to send those custom commands.

I hope that the situation is clearer now, thanks again!

In my code I doesn’t use window because command “Play” is menu item. Some other commands are not menu items but only buttons or checkboxes of window, or of some sheet of window. To see the hierarchy of UI elements you can put UI elements after each level:


tell application "System Events"
	set pidList to the unix id of (processes whose name contains "VLC")
	tell (first process whose unix id is item 1 of pidList)
		return UI elements -- Result: {window..., menu bar 1, UI Element 3, menu bar 2}
	end tell
end tell

As you see, the ui element Window is here the child of process. To see the UI elements of window itself we go 1 level deeper:


tell application "System Events"
	set pidList to the unix id of (processes whose name contains "VLC")
	tell (first process whose unix id is item 1 of pidList)
		-- return UI elements -- Result: {window..., menu bar 1, UI Element 3, menu bar 2}
		tell window 1
			UI elements
		end tell
	end tell
end tell

And so on… You can find all commands of application on Graphic User Interface (GUI) step by step.

After several tries, it almost works :slight_smile:

The issue is that I get 2 images (logo + background), 3 buttons (close, minimize, zoom), menu 1 and window 1. Basically several apps are wrappers to HTML / Javascript pages rendered by embedded CEF (Chromium Embedded Framework) e.g. Spotify.app/Contents/Frameworks/Chromium Embedded Framework.framework

In your opinion, is it something that can be referenced / commanded through Applescript? Just to make an example, the login screen can’t be filled or any button clicked. Thanks again for your support!

Why not? Everything you can do manually, you can script using GUI scripting. Login screen has window, and text fields. Using clicks, keystrokes & return with System Events you can login automatically. Of course, you should keystroke correct userName and Password. On this forum you can find examples to login automatically with different accounts. As in this pseudo code:

tell application “System Events” to tell process “SomeProcess”
tell window “someWindow”
tell textfield “someTextfield_userName”
keystroke “myUserName” & return
keystroke “myPassword” & return
end tell
end tell
end tell

Each application process has its own window, which you can control. But it’s much easier to have multiple instances of the same application with slightly different names. Just duplicate the application and add an index to them.

For example, you can have the applications “Chrom 1”, “Chrom 2”, “Chrom 3”. At the same time, they will take up not much more RAM than in the official version. They will work more stable too. Nor will Chrom control you, but you will have full control over all running Chrome. Catch the difference? GUI scripting is not needed.