1

Launching External Applications using Custom Protocols under OSX

After a lot of messing around, I've managed to get this working under OSX...

This is how I'm doing it:

 

in the AppleScript Script Editor, write the following script:

on open location this_URL
    do shell script "/jobs/tech/scripts/shotgunLocalCommand.py '" & this_URL & "'"
end open location

 

If you want to make sure you're running the Python from a certain shell (in my case, I'm using tcsh generally, and have a .tcshrc file that defines some environment variables that I want to have access to) then that middle line might want to be:

    do shell script "tcsh -c \"/jobs/tech/scripts/shotgunLocalCommand.py '" & this_URL & "'\""

I was wanting to do all of my actual processing inside a python script - but because of the way URL handers work in OSX, they have to call an application bundle rather than a script, so doing this in AppleScript seemed to be the easiest way to do it.

 

in the Script Editor, Save As an "Application Bundle"

Find the saved Application Bundle, and Open Contents. Find the Info.plist file, and open it. Add the following:

<key>CFBundleIdentifier</key>
<string>com.mycompany.AppleScript.Shotgun</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string>Shotgun</string>
<key>CFBundleURLSchemes</key>
<array>
 <string>shotgun</string>
</array>
</dict>
</array>

Just before the last two lines, which should be:

</dict>
</plist>

There are three strings in there that might want to be changed:

com.mycompany.AppleScript.Shotgun
Shotgun
shotgun


The third of these is the handler ID - so a URL would be shotgun://something

So, then this passes over to the Python script.

This is what I've got for this:

#!/usr/bin/env python
import sys
import urllib
arg = sys.argv[1]
handler, fullPath = arg.split(":", 1)
path, fullArgs = fullPath.split("?", 1)
action = path.strip("/")
args = fullArgs.split("&")
params = {}
for arg in args:
    key, value = map(urllib.unquote, arg.split("=", 1))
    params[key] = value

 

So if you've told your ActionMenuItem that the URL you want it to use is:

shotgun://playQuicktime

 

You'll end up with, in that Python script, the variable 'action' containing "playQuicktime" (or "playquicktime" - I have a feeling it lowercases everything) and params will be a dictionary containing all of the variables that are passed through from Shotgun (as listed in then "POST contents" section here: https://support.shotgunsoftware.com/entries/110709-creating-custom-menu-items-for-integration-with-other-pipeline-tools

15 comments

  • 0
    Avatar
    Rob Blau

    I'm curious if anybody has figured out a good way to get this working in a large studio where you try to automate the registration of the URL Handler for people, and if so how they've gone about doing it.  General info on the ins and outs of URL handlers on osx can be read here:

    http://developer.apple.com/mac/library/DOCUMENTATION/Carbon/Conceptual/LaunchServicesConcepts/LSCConcepts/LSCConcepts.html#//apple_ref/doc/uid/TP30000999-CH202-SW24

    The parsing of the bundle's plist file that Hugh describes above (Thanks Hugh!  Great Post!) only happens when you run the bundle or when you copy it into a folder that Launch Services cares about (like Applications).  In addition you can use the oh so strangely located:

    /System/Library/Frameworks/ApplicationServices.framework/Frameworks/LaunchServices.framework/Support/lsregister

    to have that happen via some tie in to an event that is run as the user (we've been having this happen as something with a login).  But all in all, that's not a terribly satisfying or error free way to make it happen (Launch Services seems to sometimes not like registering a protocol, or god forbid you actually want to try and version your bundle and try to get the newer one to be picked up reliably without simply overwriting the older one ... something that sounds like it should work by revving the version in the plist file but with which I've seen problems).

    Any tips?

    -r

  • 0
    Avatar
    Hugh Macdonald

    This certainly isn't something that I've looked into at all yet... I've only got it working on my one machine, and one other that I tested it on. We've only really got 20 at the most workstations, so it isn't so bad to have to do them by hand, but I'd rather find some way of doing them automatically if I can...

     

    Would love to hear if you find anything out.

     

    Oh, and how did you get that fixed-width section in your post? That's what I've been trying to do in my post...

     

    Unless it was just because it started with a '/'...

    /testing/something

  • 0
    Avatar
    Hugh Macdonald

    Oops - didn't realise I wouldn't be able to edit that one!

     

    Never mind... I'd love to know how you did it in your post, though...!

  • 0
    Avatar
    Rob Blau
    Wish I could claim some special knowledge. I did a copy paste from a web site and that is how it came through. I was able to edit the text in the fixed width area after the fact though.
  • 0
    Avatar
    KP

    Unfortunately the formatting issue is under the control of Zendesk and doesn't allow end-users to preformat code blocks :/. I've cleaned this post up for you though. Thanks for the great info. 

    Some other resources I've found out there include:

    1. http://www.macosxautomation.com/applescript/linktrigger/index.html
    2. http://tldr.mactipper.com/2009/05/assign-url-to-applescript.html
    3. http://developer.apple.com/mac/library/documentation/Carbon/Conceptual/LaunchServicesConcepts/LSCIntro/LSCIntro.html
    4. http://thexlab.com/faqs/resetlaunchservices.html

    Thanks Hugh for providing these details! We'll definitely be using this to help put together our official docs on how to handle custom protocols in OS X. 

  • 0
    Avatar
    Tim BOWMAN

    I'm trying to combine this with the custom menu items and running into a problem with the POST data. When I pass the URL to my Python handler script via Terminal, the POST data gets split at each "&" and I end up with argv[1] being the first piece of data and a whole lot of backgrounded processes.

    Has anyone come up with a good workaround?

  • 0
    Avatar
    Scott Ballard

    I thought I would note that under Snow Leopard that you can only save Applications as Application Bundles and hence they appears as "Applications" in the Save As drop-down menu. This should save about 15 minutes of Googling trying to figure out why there is no "Application Bundles" in the menu.

  • 0
    Avatar
    Scott Ballard

    Since Bash is the default shell on OSX, I wanted to make a note about setting environment variables in bash that your Python script might pickup. I tried setting them in .bash_profile and .profile but they weren't picked up when the script was launched from the protocol handler. The only method I found that worked was it set it in:

    /etc/launchd.conf

    example:

    setenv VARIABLE VALUE

     

    * Note: you will need to restart the machine after setting this!

  • 0
    Avatar
    BlueBolt
    Does anyone have a solution for getting current version of osx chrome to pick up custom protocol handlers. I am currently  doing this on snow leopard + lion.  The above method works well in Safari (thanks Hugh) so far I've tried the method of running navigator.registerProtocolHandler() in the java console on chrome and I've tried adding an entry in to com.apple.LaunchServices.plist, neither of which I could make work. Any pointers would be most welcome. 
    Cheers,
    George
  • 0
    Avatar
    Mike Boers

    I have actions working in Chrome over here. Take a look at how my "sgactions" project registers itself: https://github.com/westernx/sgactions/blob/master/sgactions/register.py#L71

  • 0
    Avatar
    Luke Stanley

    py2app can help setup URL schemes, not sure but Deluge's source probably has a good set of cross-platform ways of doing that :)

  • 0
    Avatar
    Scott Ballard

    Despite me setting this up years ago I found that setup was a bit different this time (OSX 10.8.2 Mountain Lion and 10.9.2 Mavericks) and want to make some notes for the next time I do it (in a couple years :)

    • Make sure to copy the handler application to the Applications directory.
    • I read on a forum that the protocol handler registers with the OS when the protocol handler app is copied into the Applications directory. You also seem to have to run it once by double-clicking on it. Then you can use the url in Safari or Chrome.
    • The protocol handler worked as expected with Google Chrome and no special OS registration was required.
    • "Show startup screen" and "Stay open after run handler" options are available in AppleScript Editor > Export. (why no Save As!?)
    • I found that the app already included a <key>CFBundleIdentifier</key> (closer to the top of the info.plist) which I needed to replace with xml info provided in Hugh MacDonald's instructions (not putting it before the last two lines)
    • the lsregister -u (unregister command) in Mike Boers registry.py script doesn't seem to actually unregister the protocol handler (even from command line). I ran out of time to debug this further but ended up not needed his registry script to make things work. (nice script though Mike!)
  • 0
    Avatar
    Dave Lajoie

    Hello Everyone!

    I can confirm Scott's finding.

    I would say launch services with custom application bundle can lead to problem. In my case I end up created various application all registered to my protocol, and upon listing all applications linked to my protocol, all my attempts ( various .app created with apple script editor ) appeared to be registered, even if the application was moved to the trash.

    The only way I could start from a clean slate is by running lsregister -kill which clears and reset the database.

    Anyway, apple script application bundle are difficult to debug.

    Thanks to Hugh and Mike for sharing the technic and code. I have saved hours of investigation. I am still not up and running, but I am close. :)

     

  • 0
    Avatar
    Renaud Lessard

    When implementing the handler in OSX 10.10.3, I've realized that the handler stay active even after moving it out of the /Applications directory. 

    It's like OSX is following the file, modifying the script with the Script Editor on any location update the handler (even on network share!).

    I was often in the situation when I've removed the script it it kept running.

    One neat trick I've found is to export the Application with the "Stay open after run handler" flag and when running, resolve the process location using the Performance Monitor.

    Hope it help!

  • 0
    Avatar
    Zhaoxi Zhang

    Is this thread still active? I was following the instructions posted here, but I cannot even launch the Application from Chrome by clicking the custom URL in the page. It looks like Chrome could recognize the protocol and pick up the right Application by popping up the dialog External Protocol Request like what it does to ITunes, asking me whether to start the Application or Do Nothing. However, it couldn't correctly start the Application. Any one could help? 

     

Please sign in to leave a comment.