1

How do I add custom logic (hooks) to Multi Publish?

We have a Rhino engine we built ourselves and we are using the standard toolkit apps to interface with shotgun (tk-multi-publish, tk-multi-snapshot, tk-multi-workfiles).

This works fine for tk-multi-snapshot and tk-multi-workfiles, where we have added our own rhino specific hook files. For example, the app comes bundled with hooks such as scene_operation_tk-maya.py, scene_operation_tk-nuke.py, and we have added a scene_operation_tk-rhino.py to our hooks folder. However, some of the the hooks in tk-multi-publish (post_publish.py, primary_pre_publish.py, primary_publish.py) contain logic covering multiple engines at once. What is the best way to maintain these hooks for a new custom engine (like our Rhino engine)?

5 comments

  • 0
    Avatar
    Permanently deleted user

    When we develop toolkit apps such as the Multi Publish App, we try to use a lot of hooks. There are a couple of reasons for this: If an app has all its business logic inside of hooks, clients can customize the behaviour to a great extent. The UI itself is part of the app code and is something we maintain, but clients can change for example how things are loaded and exported in and out of maya - and in the case of the multi publish app, you can set up entire workflows with multiple data outputs and exports just by changing these hooks.

    Another side effect of putting all the business logic in hooks is that it is easy to add support for a new engine. Because all the nuke/maya/etc calls are contained inside hooks, we can just add another set of hook code and the new engine is supported. Each app we maintain comes with a set of good default hooks to make sure it works out of the box and doesn't need initial configuration and this is typically done in two different ways. For smaller hooks, all the hook logic may be contained in a single file and then there is an if statement inside that file that will run different code depending on which engine you are running. For more complex hooks, this setup tends to be tricky to maintain, so we split the actual hook file by engine, and toolkit (via configuration logic) will figure out which file to load. You can see an example of both these approaches in the image below:

    hooks_in_app.png

    Customizing hooks for your project is a lot easier and more straight forward than when developing the default hooks shown above. This is because when you configure an app in your environment configuration, you always do this on a per engine basis. So you may have an environment that looks like this:

    engines:

    tk-maya:
    # engine configuration goes here
    apps:
    tk-multi-publish:
    # multi publish configuration for maya goes here
    post_publish_hook: default
    ...

    tk-nuke:
    # engine configuration goes here
    apps:
    tk-multi-publish:
    # multi publish configuration for maya goes here
    post_publish_hook: default

    The default toolkit configuration will look something like the above; the hook parameters all use the default value, meaning that toolkit will go into the hook folder in the app itself (shown in the picture above), and then run the hooks from there.

    If you wanted to customize the hooks for example in Nuke, or even add multi publish support to a completely new engine (like asked in the question here), you simply switch the default value for something custom:

    tk-nuke:
    # engine configuration goes here
    apps:
    tk-multi-publish:
    # multi publish configuration for maya goes here
    post_publish_hook: nuke_post_publish

    Now that the value is no longer default, toolkit will look for a file named nuke_post_publish.py in the hooks folder inside of the project configuration instead of in the app code itself. You can name this hook file whatever you want, just make sure that the hook configuration setting is named the same way. 

    So if you are running a custom engine or a custom setup, keep the hooks in you project configuration's hook folder and change the environment config to point to these local hooks instead of using default values. Since you are configuring these settings per engine, there is no need to cover multiple engines in a single file, like the default app hooks are doing. 

     

     

  • 0
    Avatar
    Philip Scadding

    This is helpful, however I would like to add a hook to the 3dsMax post publish, does this mean I can only have one or another? Can I have both the default one that is supplied by you and my custom one?

    if not, is it best to copy the things you do in the default hook the into my new hook file and then add my content as well?

     

  • 0
    Avatar
    Permanently deleted user

    Hello!

    Great question! Let me try and clarify!

    In our current multi publish setup, there are a whole bunch of default hooks that come bundled with the app itself, to make it work nicely out of the box. As shown above, some of that default hook logic is grouped into distinct files, one for each engine (for example scan_scene_tk-nuke.py) and in other hook files the actual engine logic happens inside the hook code itself:

    if engine_name == "tk-maya":
      return self._do_nuke_stuff()
    elif engine_name == "tk-nuke":   
      return self._do_maya_stuff()
    

    In both these cases, the app comes with all the hook code it needs in order to support a whole suite of apps: Nuke, Maya, etc. However, once the actual engine is running, and you do your publish, you only need the hook code for that particular engine. So in the case of the "per-file hooks", nuke will never access scan_scene-tk-maya.py for example - all it needs is scan_scene_tk-nuke.py. Similarly, in the case of the post publish hook, the code will only run the logic inside of the if engine_name == "tk-nuke"... branch of the code.

    So when you are writing a custom hook for 3dsmax, no need to keep all of the rest of the code in the post publish hook around! Because your hook only deals with 3dsmax, you can strip out all the nuke and maya related code from the hook and write a nice and clean hook that only handles the 3dsmax case. Then, in your 3dsmax engine configuration, point multi publish to that hook instead of the default one! 

    There is not currently a way of running first our default hook and then your custom code - whenever you are creating your own hook, you are overriding a default behaviour. Whenever customizing hooks, we recommend basing your hooks on our default hooks - just copy them across into your project's hooks area and then start customizing.

    Hope this answers the question!

  • 0
    Avatar
    Philip Scadding

    Ok Thank you, thats what I've done and it works fine. I guess I was just a bit concerned encase you update the hook, and we don't realize, and when we do, I then need to go through and work out whats changed and implement it into mine. I guess if I write mine in a way that is clear what lines are from the default and what lines are ours then all should be good.

    Thanks answer.

  • 0
    Avatar
    Permanently deleted user

    Great!

    Yes, you are highlighting a very relevant problem here: If we create a hook with a bug in it, and this hook gets used by clients as part of their customization, there is no way we can push a bug fix to them! The default hooks can be updated as part of a normal app update, but as soon as the hook code is customized, we can of course not update.

    Our approach here is to aim for lightweight, orthogonal hooks with a very concise set of business logic. We are hoping to do some work soon where we will start breaking down some of the "if statements" we have in some of our hooks into separate files to make the separation clearer and make it easier for clients to do customisation. 

Please sign in to leave a comment.