Launching Applications Using Custom Browser Protocols

Launching external applications from within Shotgun is currently a difficult process and unfortunately not always a possibility. We're currently looking at ways we can simplify this by providing out own tools bundled with Shotgun that studios can use to launch external applications like asset management tools, frame flippers, etc.

Note: RV can now act as a protocol handler for URLs using the "rvlink" protocol.

In the meantime, below are a few nuggets of information that can help you get started if you have the resources and want to jump in and get wet right now.

Registering A Custom Protocol Handler

So you want to set up a type of link where if a user clicks on it, it will launch the [foo] application. Instead of having 'http' as the prefix, we need to designate a custom protocol, for example 'foo'. So ideally we'd like a link that looks like foo://some/info/here.

Operating System Dependency

The operating system has to be informed how to handle protocols. By default, all of the current operating systems know that 'http' should be handled by the default web browser, and 'mailto' should be handled by the default mail client. Sometimes when applications are installed, they register with the OS and tell it to launch the applications for a specific protocol. 

iTunes Example

If you install iTunes, the application registers 'itms' with the OS and tells it that iTunes will handle all 'itms' protocol requests to show a page within the iTunes Music Store. So when a user clicks on a link that starts with 'itms://' the operating system will know to launch iTunes with the link and iTunes will parse the link and know how to handle it. 

Registering a Protocol on Windows

On Windows, registering protocol handlers involves hacking into the Windows Registry. Here is what you want the registry key to look like

HKEY_CLASSES_ROOT
foo
(Default) = "URL:foo Protocol"
URL Protocol = ""
shell
open
command (Default) = "foo_path" "%1"

The target url would look like: foo://host/path...

MSDN Reference URL
http://msdn.microsoft.com/en-us/library/aa767914(VS.85).aspx

If you have your own in-house application you are looking to integrate, you can you can use QT - QSetting to set it up, but windows registry calls can be used as well.

This is what the code looks like to automatically have the application setup the registry keys

// cmdLine points to the foo path.
//Add foo to the Os protocols and set foobar to handle the protocol
QSettings fooKey("HKEY_CLASSES_ROOT\\foo", QSettings::NativeFormat);
mxKey.setValue(".", "URL:foo Protocol");
mxKey.setValue("URL Protocol", "");
QSettings fooOpenKey("HKEY_CLASSES_ROOT\\foo\\shell\\open\\command", QSettings::NativeFormat);
mxOpenKey.setValue(".", cmdLine);

Registering a Protocol on Linux

gconftool-2 -t string -s /desktop/gnome/url-handlers/foo/command 'foo "%s"'
gconftool-2 -s /desktop/gnome/url-handlers/foo/needs_terminal false -t bool
gconftool-2 -s /desktop/gnome/url-handlers/foo/enabled true -t bool

Then put the settings from your local gconf file into the global defaults in:

/etc/gconf/gconf.xml.defaults/%gconf-tree.xml

This specifically mentions 64-bit but the same thing works on 32-bit as well. Also, even though the change is only in the Gnome settings, it also works for KDE because apparently Firefox/Iceweasel defers to gnome-open regardless of what window manager you are running when it encounters a prefix which it doesn't understand (such as foo://). So, other browsers, like Konqueror in KDE, won't work under this scenario.

See http://askubuntu.com/questions/527166/how-to-set-subl-protocol-handler-with-unity for more information on setting up protocol handlers for Action Menu Items in Ubuntu.

Launching External Applications from Shotgun: Windows Advanced Example

Zoic Studios has set up a custom protocol handler that launches a flip book application. Here's how it works:

  • File paths to plates are uploaded to Shotgun
  • A user clicks on one of these links
  • The custom protocol is fired (For example: shotgun://something/here.dpx)
  • A python script queries Shotgun for a check box to determine if the user wants to open the path via the explorer or to open the path via a flip book

This solution has worked well for them, and they intend to extend it quite a bit more in the future.

Step 1: Set up the Custom "shotgun" Protocol

Using Windows Registry Editor Version 5.00 (Click here for more information on registering custom protocol handlers)

[HKEY_CLASSES_ROOT\shotgun]
@="URL:shotgun Protocol"
"URL Protocol"=""
[HKEY_CLASSES_ROOT\shotgun\shell]
[HKEY_CLASSES_ROOT\shotgun\shell\open]
[HKEY_CLASSES_ROOT\shotgun\shell\open\command]
@="\"python\" \"sgTriggerScript.py\" \"%1\""

The last line (beginning with @="\"python\") is very important. It says when somebody uses the 'shotgun' protocol handler, call python and our handler script called sgTriggerScript.py. The ”%1” in the last line is particularly important to understand. This is where things get tricky.

Problem: Custom protocol paths containing "/" can result in truncated urls

shotgun://X:/Studio_Projects/Project/Plates/testPlate/testPlate.1001.dpx

The problem is that the ”%1” gets confused by the ”/” and only passes the last segment of the path “testPlate.1001.dpx”.

Workaround

To get around this, ensure that the shotgun link field contains a slightly modified protocol path that replaces the ”/” with a ”>”. This makes sure that the full path gets passed to the python script and is not truncated. The path would look like this:

shotgun://X:>Studio_Projects>Project>Plates>testPlate>testPlate.1001.dpx

Step 2: Handing off the protocol path to Python

Once you have python calling a pre-built python script with the full protocol path, all you have to do is clean the path up by removing the protocol handle “shotgun” and replacing the ”>” with ”/” and you're good to go. In python you would do something like this:

plate = plate.replace('shotgun://', '')
plate = plate.replace('>', '/')

Since your string replacements occur inside of python, you can go ahead and do whatever you need to do. In the example above, the python script takes the plate path, fixes it up, gets the first frame of the file sequence inside the path, and gets the value of the check box to determine how to process.

Python Script

def main(script, plate):
# Flipbook Application
exe = 'C:\\djv\\bin\\djv_view.com'
 
# Plate Directory
plate = plate.replace('shotgun://', '')
plate = plate.replace('>', '/')
 
# First Frame Inside of the Plate Directory
file = os.listdir ( plate )
file = sorted(file)
file = file[1]
 
# Get Shotgun Action info from Check Box Field
elementName = file.partition('.')
elementName = elementName[0].replace('_prx', '')
element = sg.getByCode(sg.server.element, elementName, elementName[0:3] )
 
# Take Action
if element['sg_viewer'] == 1 :
# Open FlipBook
cmd = exe + " " + plate + "/" + file
os.system(cmd)
else :
# Open Directory
os.startfile( plate )
 
if __name__ == '__main__':
main(*sys.argv)
 

Possible Improvements

The best part of this whole approach is that now you have the ability to make multiple 'if' statements from the value of a list box rather than a check box. Some ideas would be:

  • Open File System
  • Open FlipBook
  • Upload to client ftp
  • Upload to archive
Follow

21 Comments

  • 0
    Avatar
    Hugh Macdonald

    That looks pretty cool - will have to have a play with this one!

     

    Any chance of some notes on how to register a new protocol under OSX?

  • 0
    Avatar
    Don Parker

    Note for all, Hugh got this working on OS X and created a forum post with details here:  https://support.shotgunsoftware.com/entries/127152

    Thanks Hugh!

  • 0
    Avatar
    Scott Lowe

    I'm new to both Python and Shotgun. I understand that on the last line of the windows reg key:

    @="\"python\" \"sgTriggerScript.py\" \"%1\""

    ...the "sgTriggerScript.py" is the doc that Python will attempt to run when Python is opened. Is there a specific location that the sgTriggerScript doc needs to be? I'm not very good at setting enviornmental variables, etc in Python yet.

     

    Python does open momentarily, but gives me this error:

    "Python.exe: can't open file 'sgTriggerScript.py': [Errno 2] No such file or directory"

     

    Any hints would help a lot! Thanks.

  • 0
    Avatar
    Hugh Macdonald

    I would suggest that you probably want to define the full path to sgTriggerScript.py in the reg key...

     

    Something like:

     

    @="\"python\" \"C:\\tech\\python\\sgTriggerScript.py\" \"%1\""

     

    And this is one of those situations where I really hate Windows.... That string will be parsed a couple of times... Each time, '\' will be replaced with '\', which is why we need 4 \s in there to end up with just one.

  • 0
    Avatar
    Scott Lowe

    Thanks for the quick reply. I'm not getting any more errors. But something is still not working...

    I wonder if this is really a question for the Python forms, but maybe it will help someone else like me.

     

    I wrote a script that I was hoping would dump a log file, but nothing seems to happen. Here's the sgTriggerScript I'm using:

     

    def main(script, plate):

        import logging

        LOG_FILENAME = 'mylog.out'

        logging.basicConfig(filename=LOG_FILENAME,level=logging.DEBUG)

        logging.debug(script, plate)

    if __name__ == '__main__':

            main(*sys.argv)

    _ def main(script, plate):_

    import logging

    LOG_FILENAME = 'mylog.out'

    logging.basicConfig(filename=LOG_FILENAME,level=logging.DEBUG)

    logging.debug(script, plate)

    _

    if __name__ == '__main__': _

    main(*sys.argv)

     

    Again, any hints would be very helpful. =)

  • 0
    Avatar
    Scott Lowe

    Ah! Excuse me. Please ignore the italicized code in my previous post.

  • 0
    Avatar
    Hugh Macdonald

    If you run from a command prompt:

        python sgTriggerScript.py

    Does it  do the right thing and write something out to the log file?

     

    Oh, you might want to specify the full path to the log file too - otherwise it'll end up wherever the script is run from, which could be anywhere.

  • 0
    Avatar
    Scott Lowe

    Success! I was able to get a log file to generate from both the command line AND a custom menu item.

    Here is the command:

    C:\Python26>python C:\pyTest\sgTriggerScript.py

     

    Here is the reg key I used for handling the custom protocal:

    [HKEY_CLASSES_ROOT\shotgun]

    @="URL:shotgun Protocol"

    "URL Protocol"=""

    [HKEY_CLASSES_ROOT\shotgun\shell]

    [HKEY_CLASSES_ROOT\shotgun\shell\open]

    [HKEY_CLASSES_ROOT\shotgun\shell\open\command]

    @="\"C:\\Python26\\python\" \"C:\\pyTest\\sgTriggerScript.py\" \"%1\""

     

    And, here is the Python script:

     

    import sys

    def main(script):

        import logging

        LOG_FILENAME = 'C:\Python26\mylog.out'

        logging.basicConfig(filename=LOG_FILENAME,level=logging.DEBUG)

        logging.debug(script)

    if __name__ == '__main__':

            main(sys.argv[0])

    import sys

    def main(script):

    import logging

    LOG_FILENAME = 'C:\pyTest\mylog.out'

    logging.basicConfig(filename=LOG_FILENAME,level=logging.DEBUG)

    logging.debug(script)

    if __name__ == '__main__':

    main(sys.argv[0])

     

    Unfortunately, I still don't think I've grasped how the action menu item is actually passing data to sys.argv. The log file doesn't seem to generate when I use "main(*sys.argv)". It only works with "main(sys,argv[0])". @Huge Thanks for walking me through this, BTW. It is a huge help.

  • 0
    Avatar
    Scott Lowe

    (I'm sorry, I don't know why it the code I copy into the comment gets posted twice.)

  • 0
    Avatar
    Prabu Kasireddy

    Has anybody faced problems when the number of rows in the entity exceeds a certain limit?

    We have an entity that has around 250 records.

    The custom protocol was working fine (executes a python script) until 70 records (atleast from what I remember)

    Now with 250+ records, when we try to select the Gear menu option, nothing shows up.

    However, when I apply a filter and have less records, the script executes.

     

    My guess is that the custom protocol (URL) is getting too lengthy.

     

    Also, I have tried changing the option in the status bar to show only 25 records per page and it still does not work, until I apply a filter.

    Any solutions or workarounds are appreciated.

  • 0
    Avatar
    Rob Blau

    I've seen that as well.  I think it is because the ids of ALL matching entities gets passed on through.  We use the full ID list in the case when selected ids is empty, but that is the only time.  It would be nice if the setup behaved better when custom menu items are used on a page where a LOT of entities match the filter.

    -r

  • 0
    Avatar
    Isaac Reuben

    What browser/OS are you using when it fails with a lot of records?  There is no official limit to the length of a url, according to the spec, though various implementations have different limits (Firefox, Safari, and Chrome all support at least 40,000 characters, but Internet Explorer is limited to 2083 chars).  The various points that url is passing through (OS when it handles the protocol, and then the script when it receives it) might have length issues as well.

    As Rob mentions, currently when using an action menu item we are sending *both* the list of selected ids, and the list of all ids that match the query.  If we made this a pref on the ActionMenuItem (only send selected ids), that could at least mitigate the problem if you just want to act on the selected entities anyway.  But I'd also like to see if we can make longer urls work in setups where they are failing now!

  • 0
    Avatar
    Rob Blau

    I've seen it in Firefox/Safari on OSX (10.5 and 10.6) and in Firefox on Linux.  Also with large result sets, I've seen Firefox bring up that little unresponsive script dialog (firebug seems to say it happens while iterating through the list of rows, or something like that in the js).  Even when it works it does slow the browser way down during the launch through the protocol handler.

    -r

  • 0
    Avatar
    Isaac Reuben

    Would having it only send the selected ids be a solution for your usage?  I'm imagining a pref that greys out the menu option unless you have something selected (like how "Edit Selected..." works). 

    I'm sure we can make sending very large result sets work reliably, but might require restructing how the data is passed around (send just the query info instead of the full list of ids, or stash the list of id's on server and pass a reference to that to script, like url shortening, which then gets the full data through a normal api request).

    If the ActionMenuItem is calling another web server (instead of the custom protocol), we're sending the values through in a POST request instead of GET, so not tacked onto the url and doesn't have these length restrictions.

    Thanks for the feedback!

  • 0
    Avatar
    Rob Blau

    For our case (not sure if this works in general) I'd LOVE the following:

    If something is selected, then those ids are sent through

    If nothing is selected, then all ids are sent through

    If more than X (configurable?) are being sent through, there is an alert saying something like "You are selecting more than X <things>, this could be slow" with continue or cancel.

    The only time we use all ids is when there is no selection (we've run into issues with wanting to run menu items on things that span multiple pages, and this is our workaround when cranking up the # of entities displayed on the page would just be too slow).

    -r

  • 0
    Avatar
    Hugh Macdonald

    Hi Isaac,

     

    I do like the idea of AMIs having a flag to say whether they can only work when items are selected. There are quite a few that shouldn't work without something selected, and to have them greyed out would be fantastic.

  • 0
    Avatar
    Prabu Kasireddy

    Hi Isaac,

    We are using Firefox on Windows XP 64 bit machines.

    Having an option to disable sending all the IDs sounds like a good idea.

    We currently have scenarios where we need to do some operations on a large number of records and we have built an python utility that will get all the information from Shotgun and then work on that result.

    We are expecting a lot of entities getting filled with more than 200 records per project and would like to have some kind of a setting that would make the URL short.

  • 0
    Avatar
    Isaac Reuben

    OK, just did some tests.  I'm not seeing any limit on the length of urls on the Mac side (50k chars works fine).  Works for Firefox 3.6, Safari 4, and Chrome 4.

    On the Windows side (XP 32bit), Firefox 3.0, Firefox 3.6, and Chrome 4 all have a 2048 char limit (like Internet Explorer does for urls in general) when launching custom protocol handlers.  They *don't* have that limit in general for urls, so seems to be related to how they are passing the value to the system.  I'm not sure if there is another way to setup a protocol handler that more directly launches the script. The reg setting we've been using adds an entry like:

    [HKEY_CLASSES_ROOT\shotguntest\shell\open\command]@="<exe\_to\_run>"

    but possibly there is another way to open the script directly instead of passing a command through the shell?  Seems to be suggesting that on these links:

    http://www.advancedinstaller.com/forums/viewtopic.php?f=2&t=9910#p26249

    http://blogs.msdn.com/oldnewthing/archive/2003/12/10/56028.aspx

    But looks like we need to do something about not making urls over 2048 chars, so first step will be adding option to only send selected ids (nice to have that option in any case!), and then we'll figure out another way to pass all the ids (either pass query info, or create a token that the script can pass back through api to get the full list).

  • 0
    Avatar
    Dumay Nicolas

    Hi Isaac,

    Any update on this side? We are facing the same 2048 char issue here :/

     

    Thanks

    Nicolas

  • 0
    Avatar
    Frank Rueter

    Is this thread still maintained? I am having trouble getting a custom protocol handler to work under linux (Kubuntu 12.10). I ran gconftool as  outlined above but when clicking one of my custom links I gets the same old "The address wasn't understood" error. I did get rvlink to work and remember there was a bit of pain involved, but can't remember the details.

    I also tried setting "network.protocol-handler.expose.shotgunpy;true" in Firefox' about:config to no avail.

    Any help would be greatly appreciated!

    frank

  • 0
    Avatar
Please sign in to leave a comment.