{"id":6487,"date":"2024-01-01T13:23:01","date_gmt":"2024-01-01T11:23:01","guid":{"rendered":"https:\/\/www.enoni.de\/wp\/?p=6487"},"modified":"2026-02-03T14:39:20","modified_gmt":"2026-02-03T12:39:20","slug":"karma-on-deadline-via-husk","status":"publish","type":"post","link":"https:\/\/www.enoni.de\/wp\/karma-on-deadline-via-husk\/","title":{"rendered":"Karma on Deadline via Husk PRT.1"},"content":{"rendered":"\n<h1 class=\"wp-block-heading has-text-align-center small_highlight_glow\" style=\"font-size:30px\"><em>\u00bb A summary\/guide, how i render Karma jobs on Deadline and Husk &#8211; Directly from Houdini USD-ROPs in Solaris. \u00ab<\/em><\/h1>\n\n\n<div class=\"wp-block-image is-style-rounded\">\n<figure class=\"aligncenter size-full\"><a href=\"https:\/\/www.enoni.de\/wp\/wp-content\/uploads\/2023\/03\/demo_husk_deadline_ns_Pipe.gif\"><img decoding=\"async\" width=\"1280\" height=\"941\" src=\"https:\/\/www.enoni.de\/wp\/wp-content\/uploads\/2023\/03\/demo_husk_deadline_ns_Pipe.gif\" alt=\"\" class=\"wp-image-6521\"\/><\/a><figcaption class=\"wp-element-caption\">Custom script to send a USD-ROP directly to Deadline\/Husk<\/figcaption><\/figure>\n<\/div>\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p><\/p>\n\n\n\n<p>The Standart Deadline Submitter\/Implementation uses Hython to start Houdini render jobs. BTW, im using Deadline Version 10.1.18.4<\/p>\n\n\n\n<h1 class=\"wp-block-heading has-text-align-right small_highlight\" style=\"font-size:20px\"><em>Why Husk not Hython?<\/em><\/h1>\n\n\n\n<p><\/p>\n\n\n\n<p>Like Husk, Hython is a Command line tool as well. Its a kind of a Shell wrapper, so its a Houdini instance without a GUI &#8211; controllable via Python. Therefore the startup and loading time of scenes\/.hip files, cooking, etc. is a important factor. Another downside is, it needs a complete Houdini License. <\/p>\n\n\n\n<p>Husk uses just a Renderlicense for Karma. Here on my Houdini FX version i can utilize 5 Karma Renderer out of the box. (Indie has 1 Renderlicense for Karma)<\/p>\n\n\n\n<p>The Rendernodes just need a proper Houdini installation and Karma License applied in the License Manager.<\/p>\n\n\n\n<p>Husk is for rendering USD files (and any kind of Hydra Renderdelegates). So you have to export your USD render files manually, or build an extra export job on Deadline. (no topic of this blog post)<\/p>\n\n\n\n<p>To build a custom export and submitter script for Houdini we need some scripts and modifications on the Deadline Repositiory.<\/p>\n\n\n\n<h2 class=\"wp-block-heading small_highlight\" style=\"font-size:20px\"><em>Lets start.<\/em><\/h2>\n\n\n\n<p>First, we take look at the Houdini Docs: <\/p>\n\n\n\n<p class=\"my_block_border_o\"><a rel=\"noreferrer noopener\" href=\"https:\/\/www.sidefx.com\/docs\/houdini\/ref\/utils\/husk.html\" target=\"_blank\">https:\/\/www.sidefx.com\/docs\/houdini\/ref\/utils\/husk.html<\/a><\/p>\n\n\n\n<p>Here you can find all commands to utilize Husk or to modify your Deadline scripts for your needs. We need that later when we will modificate some of our Python scripts.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<p class=\"small_highlight has-medium-font-size\"><strong>About Deadline jobs:<\/strong><\/p>\n\n\n\n<p>There are different &#8220;Types&#8221; of jobs you can send to Deadline (Houdini, Nuke, Fusion, etc.). To send a proper &#8220;CallDeadlineCommand&#8221; via Python you just need two things.<br><\/p>\n\n\n\n<p>A <strong><em>jobInfoFile<\/em><\/strong> &amp; <strong><em>pluginInfoFile<\/em><\/strong> which are just dictonaries of data.<\/p>\n\n\n\n<p>The jobInfoFile for instance, contains infos about the <strong>plugin<\/strong>, jobname, framerange, Renderpool etc. The pluginInfoFile contains the scene file location and more.<\/p>\n\n\n\n<p>Here some links for Commands and Job Submission in Deadline:<\/p>\n\n\n\n<p><\/p>\n\n\n\n<div class=\"wp-block-group my_block_border\"><div class=\"wp-block-group__inner-container is-layout-constrained wp-block-group-is-layout-constrained\">\n<p class=\"my_block_border_o\"><a href=\"https:\/\/docs.thinkboxsoftware.com\/products\/deadline\/10.1\/1_User%20Manual\/manual\/command.html\">https:\/\/docs.thinkboxsoftware.com\/products\/deadline\/10.1\/1_User%20Manual\/manual\/command.html<\/a><\/p>\n\n\n\n<p class=\"my_block_border_o\"><a href=\"https:\/\/docs.thinkboxsoftware.com\/products\/deadline\/10.1\/1_User%20Manual\/manual\/manual-submission.html#manual-submission-ref-label\">https:\/\/docs.thinkboxsoftware.com\/products\/deadline\/10.1\/1_User%20Manual\/manual\/manual-submisbmission-ref-label<\/a><\/p>\n<\/div><\/div>\n\n\n\n<p><\/p>\n\n\n\n<p>Deadline has no proper <strong>plugin <\/strong>or GUI entries in the Deadline Monitor to render via Husk Cmd. There are several plugins you can find on Github. I am using a modificated version from here:<\/p>\n\n\n\n<p><\/p>\n\n\n\n<p class=\"my_block_border_o\"><a href=\"https:\/\/github.com\/DavidTree\/HuskStandaloneSubmitter\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/github.com\/DavidTree\/HuskStandaloneSubmitter<\/a><\/p>\n\n\n\n<p><\/p>\n\n\n\n<p>Follow the installation steps and copy the files to your Deadline Repository. You not really need the Submission script which allows you to add Husk jobs at the Deadline Monitor . Its nice to have.<\/p>\n\n\n<div class=\"wp-block-image is-style-rounded\">\n<figure class=\"aligncenter size-large\"><a href=\"https:\/\/www.enoni.de\/wp\/wp-content\/uploads\/2023\/03\/config_DeadlineRepository10_01.jpg\"><img decoding=\"async\" width=\"1508\" height=\"1080\" src=\"https:\/\/www.enoni.de\/wp\/wp-content\/uploads\/2023\/03\/config_DeadlineRepository10_01-1508x1080.jpg\" alt=\"\" class=\"wp-image-6513\" srcset=\"https:\/\/www.enoni.de\/wp\/wp-content\/uploads\/2023\/03\/config_DeadlineRepository10_01-1508x1080.jpg 1508w, https:\/\/www.enoni.de\/wp\/wp-content\/uploads\/2023\/03\/config_DeadlineRepository10_01-768x550.jpg 768w, https:\/\/www.enoni.de\/wp\/wp-content\/uploads\/2023\/03\/config_DeadlineRepository10_01-1536x1100.jpg 1536w, https:\/\/www.enoni.de\/wp\/wp-content\/uploads\/2023\/03\/config_DeadlineRepository10_01.jpg 1698w\" sizes=\"(max-width: 1508px) 100vw, 1508px\" \/><\/a><figcaption class=\"wp-element-caption\">HuskStandalone Config entry<\/figcaption><\/figure>\n<\/div>\n\n\n<p>Is every file in place you get an &#8220;HuskStandalone&#8221; entry in Deadline Monitor&gt;Tools&gt;Configure Plugins&#8230;<\/p>\n\n\n\n<p>Set a proper path to your Husk binary. In the screenshot i have &lt;ns_version&gt; placeholder instead of a right Houdini folder. Its because i will replace that string later dynamically in the Python script with the proper Houdini version.<\/p>\n\n\n\n<p>You can ignore that and just add a path like (example Windows):  <\/p>\n\n\n\n<p>C:\\Program Files\\Side Effects Software\\Houdini 19.5.435\\bin\\husk.exe<\/p>\n\n\n\n<p>Set a nice Plugin Icon as well.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<p class=\"small_highlight has-medium-font-size\"><strong>Edit some Python files &#8211; Deadline Repository.<\/strong><\/p>\n\n\n\n<p>First checking if the standart Deadline Submitter Houdini is working and installed. Some Python sys.path has been setted correctly.  This command in the Houdini Python Shell <strong>SHOULDNT <\/strong>throw an error:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"python\" class=\"language-python\">from CallDeadlineCommand import CallDeadlineCommand<\/code><\/pre>\n\n\n\n<p>This is important, because  we add an extra function in here:<\/p>\n\n\n\n<p class=\"my_block_border\">*\\DeadlineRepository10\\submission\\Houdini\\Main\\SubmitHoudiniToDeadlineFunctions.py<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"python\" class=\"language-python\">def SubmitRenderJob_husk( node, jobProperties, dependencies ):\n    if jobProperties.get(\"usdjob\"):\n        if jobProperties.get(\"usdjob\") == 1:\n            assemblyJobIds = []\n            jobName = jobProperties.get( \"jobname\", \"Untitled\" )\n            jobName = \"%s - %s\"%(jobName, node.path())\n            \n            subInfo = json.loads( hou.getenv(\"Deadline_Submission_Info\") )\n            homeDir = subInfo[\"UserHomeDir\"]\n\n            jobInfoFile = os.path.join(homeDir, \"temp\", \"houdini_submit_info.job\")\n            ## job file ##\n            with open( jobInfoFile, \"w\" ) as fileHandle:\n                fileHandle.write( \"Plugin=HuskStandalone\\n\" )\n                fileHandle.write( \"Name=%s\\n\" % jobName )\n                fileHandle.write( \"Comment=%s\\n\" % jobProperties.get( \"comment\", \"\" ) )\n                fileHandle.write( \"Department=%s\\n\" % jobProperties.get( \"department\", \"\" ) )\n                fileHandle.write( \"Pool=%s\\n\" % jobProperties.get( \"pool\", \"None\" ) )\n                fileHandle.write( \"SecondaryPool=%s\\n\" % jobProperties.get( \"secondarypool\", \"\" ) )\n                fileHandle.write( \"Group=%s\\n\" % jobProperties.get( \"group\", \"None\" ) )\n                fileHandle.write( \"Priority=%s\\n\" % jobProperties.get( \"priority\", 50 ) )\n                fileHandle.write( \"TaskTimeoutMinutes=%s\\n\" % jobProperties.get( \"tasktimeout\", 0 ) )\n                fileHandle.write( \"EnableAutoTimeout=%s\\n\" % jobProperties.get( \"autotimeout\", False ) )\n                fileHandle.write( \"ConcurrentTasks=%s\\n\" % jobProperties.get( \"concurrent\", 1 ) )\n                fileHandle.write( \"MachineLimit=%s\\n\" % jobProperties.get( \"machinelimit\", 0 ) )\n                fileHandle.write( \"LimitConcurrentTasksToNumberOfCpus=%s\\n\" % jobProperties.get( \"slavelimit\", False ) )\n                fileHandle.write( \"LimitGroups=%s\\n\" % jobProperties.get( \"limits\", 0 ) )\n                fileHandle.write( \"JobDependencies=%s\\n\" % dependencies )\n                fileHandle.write( \"OnJobComplete=%s\\n\" % jobProperties.get( \"onjobcomplete\", \"Nothing\" ) )\n                fileHandle.write( \"Frames=%s\\n\" % GetFrameList( node, jobProperties) )\n                fileHandle.write( \"ChunkSize=%s\\n\" % jobProperties.get( \"framespertask\", 1 ) )\n\n\n\n\n            pluginInfoFile = os.path.join( homeDir, \"temp\", \"houdini_plugin_info.job\")\n            with open( pluginInfoFile, \"w\" ) as fileHandle:\n                fileHandle.write( \"SceneFile=%s\\n\" % hou.parm(node.path() + \"\/lopoutput\").eval() )\n                fileHandle.write( \"LogLevel=%s\\n\" % jobProperties.get( \"usdloglevel\", 2 ) )\n                fileHandle.write( \"HouVersion=%s\\n\" % jobProperties.get( \"ns_pipe_hou_version\", \"Nothing\" ) )\n                fileHandle.write( \"OutImage=%s\\n\" % jobProperties.get( \"ns_pipe_image_out\", \"\" ) )\n\n\n            arguments = [ jobInfoFile, pluginInfoFile ]\n\n            jobResult = CallDeadlineCommand( arguments )\n            jobId = GetJobIdFromSubmission( jobResult )\n            assemblyJobIds.append( jobId )\n\n            print(\"---------------------------------------------------\")\n            print(\"\\n\".join( [ line.strip() for line in jobResult.split(\"\\n\") if line.strip() ] ) )\n            print(\"---------------------------------------------------\")\n\n        else:\n            return\n    else:\n        print(\"ns_Pipe&gt; Found no usdjob property\")<\/code><\/pre>\n\n\n\n<p><\/p>\n\n\n\n<p>This creates the necessary <strong><em>jobInfoFile<\/em><\/strong> &amp; <strong><em>pluginInfoFile<\/em><\/strong> . Here are some dictionary <strong><em>(jobProperties)<\/em><\/strong> values that are custom made and created when i trigger the Houdini Submitter script, we will see later.<\/p>\n\n\n\n<p>Now open the custom HuskStandalone.py file:<\/p>\n\n\n\n<p class=\"my_block_border\">*\\DeadlineRepository10\\custom\\plugins\\HuskStandalone\\HuskStandalone.py<\/p>\n\n\n\n<p>Here will be the arguments comped that will be sended to Husk per Deadline Task.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"python\" class=\"language-python\">#!\/usr\/bin\/env python3\n\nfrom System import *\nfrom System.Diagnostics import *\nfrom System.IO import *\n\nimport os\n\nfrom Deadline.Plugins import *\nfrom Deadline.Scripting import *\n\nfrom pathlib import Path\n\ndef GetDeadlinePlugin():\n    return HuskStandalone()\n\ndef CleanupDeadlinePlugin(deadlinePlugin):\n    deadlinePlugin.Cleanup()\n\nclass HuskStandalone(DeadlinePlugin):\n    # functions inside a class must be indented in python - DT\n    def __init__( self ):\n        self.InitializeProcessCallback += self.InitializeProcess\n        self.RenderExecutableCallback += self.RenderExecutable # get the renderExecutable Location\n        self.RenderArgumentCallback += self.RenderArgument # get the arguments to go after the EXE\n\n\n    def Cleanup( self ):\n        del self.InitializeProcessCallback\n        del self.RenderExecutableCallback\n        del self.RenderArgumentCallback\n\n    def InitializeProcess( self ):\n        self.SingleFramesOnly=True\n        self.StdoutHandling=True\n        self.PopupHandling=False\n\n        self.AddStdoutHandlerCallback(\"USD ERROR(.*)\").HandleCallback += self.HandleStdoutError # detect this error\n        self.AddStdoutHandlerCallback( r\"ALF_PROGRESS ([0-9]+(?=%))\" ).HandleCallback += self.HandleStdoutProgress\n\n    # get path to the executable\n    def RenderExecutable(self):\n        ## the replace fills in the correct Houdini_Version folder ##\n        return self.GetConfigEntry( \"USD_RenderExecutable\" ).replace(\"&lt;ns_version&gt;\", \"Houdini \" + self.GetPluginInfoEntry(\"HouVersion\"))\n\n    # get the settings that go after the filename in the render command, 3Delight only has simple options.\n    def RenderArgument( self ):\n\n        # construct fileName\n        # this will only support 1 frame per task\n\n        usdFile = self.GetPluginInfoEntry(\"SceneFile\")\n        usdFile = RepositoryUtils.CheckPathMapping( usdFile )\n        usdFile = usdFile.replace( \"\\\\\", \"\/\" )\n\n        usdPaddingLength = FrameUtils.GetPaddingSizeFromFilename( usdFile )\n\n        frameNumber = self.GetStartFrame() # check this 2021 USD\n\n        argument = \"\"\n        \n        argument += usdFile + \" \"\n\n        argument += \"--verbose a{} \".format(self.GetPluginInfoEntry(\"LogLevel\"))  # alfred style output and full verbosity\n\n        argument += \"--frame {} \".format(frameNumber)\n\n        argument += \"--frame-count 1\" + \" \" #only render 1 frame per task\n\n        #renderer handled in job file.\n        \n        \n        ## Custom render output destination ##\n        if self.GetPluginInfoEntry(\"OutImage\") != \"\":\n            output_path = os.path.dirname(self.GetPluginInfoEntry(\"OutImage\"))\n            out_image_path_parts = self.GetPluginInfoEntry(\"OutImage\").split(\"\/\")\n            image_comp_parts = out_image_path_parts[-1].split(\".\")\n            image_name = image_comp_parts[0]\n            padded_frame_number = StringUtils.ToZeroPaddedString(frameNumber, len(image_comp_parts[-2]))\n            image_format = image_comp_parts[-1]\n\n            argument += \"-o {0}\/{1}.{2}.{3}\".format(output_path, image_name, padded_frame_number, image_format)\n        else:\n            ## Fallback ##\n            outputPath = os.path.dirname(usdFile).split('\/') #[:-4] We are now going to site the composite USD in the project root.\n            outputPath.append(\"render\")\n            outputPath = os.path.abspath(os.path.join(*outputPath))\n        \n            if not os.path.isdir(outputPath):\n                os.mkdir(outputPath)\n\n            filename = Path(usdFile).name\n            filename = Path(filename).with_suffix(\"\")\n            \n            paddedFrameNumber = StringUtils.ToZeroPaddedString(frameNumber, 4)\n            \n            argument += \"-o {0}\/{1}.{2}.exr\".format(outputPath, filename, paddedFrameNumber)\n\n        argument += \" --make-output-path\"\n\n        argument += \" --exrmode 0\" ## Legacy exr mode for fusion cryptomattes ##\n\n        self.LogInfo( \"Rendering USD file: \" + usdFile )\n\n        return argument\n\n    # just incase we want to implement progress at some point\n    def HandleStdoutProgress(self):\n        self.SetStatusMessage(self.GetRegexMatch(0))\n        self.SetProgress(float(self.GetRegexMatch(1)))\n\n    # what to do when an error is detected.\n    def HandleStdoutError(self):\n        self.FailRender(self.GetRegexMatch(0))\n<\/code><\/pre>\n\n\n\n<p><\/p>\n\n\n\n<p>Here i added some code in the <strong><em>RenderExecutable<\/em><\/strong> and <strong><em>RenderArgument<\/em><\/strong> function. Just compare it with the original script.<\/p>\n\n\n\n<p>I added a new argument for Husk as well:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">argument += \" --exrmode 0\"<\/code><\/pre>\n\n\n\n<p>This is for Fusion Cryptomattes .exr files. Otherwise the Cryptos wont working.<\/p>\n\n\n\n<p class=\"small_highlight has-medium-font-size\"><strong>NOTE:<\/strong><\/p>\n\n\n\n<p>Check out other arguments to pass through here: <\/p>\n\n\n\n<p class=\"my_block_border_o\"><a href=\"https:\/\/www.sidefx.com\/docs\/houdini\/ref\/utils\/husk.html\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/www.sidefx.com\/docs\/houdini\/ref\/utils\/husk.html<\/a><\/p>\n\n\n\n<p>For example to set OCIO color transform with this line: <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">argument += \" --ocio 1\"<\/code><\/pre>\n\n\n\n<p><\/p>\n\n\n\n<p class=\"small_highlight has-medium-font-size\"><strong>Create a custom Houdini Submitter Script for USD-ROPs.<\/strong><\/p>\n\n\n\n<p>You can just create a Houdini shelf tool or build an entry in the OPmenu.xml to trigger the script. <\/p>\n\n\n\n<figure class=\"wp-block-image size-full is-style-rounded\"><a href=\"https:\/\/www.enoni.de\/wp\/wp-content\/uploads\/2023\/03\/HOU_deadline_script_husk_02.jpg\"><img decoding=\"async\" width=\"1787\" height=\"660\" src=\"https:\/\/www.enoni.de\/wp\/wp-content\/uploads\/2023\/03\/HOU_deadline_script_husk_02.jpg\" alt=\"\" class=\"wp-image-6559\" srcset=\"https:\/\/www.enoni.de\/wp\/wp-content\/uploads\/2023\/03\/HOU_deadline_script_husk_02.jpg 1787w, https:\/\/www.enoni.de\/wp\/wp-content\/uploads\/2023\/03\/HOU_deadline_script_husk_02-768x284.jpg 768w, https:\/\/www.enoni.de\/wp\/wp-content\/uploads\/2023\/03\/HOU_deadline_script_husk_02-1536x567.jpg 1536w\" sizes=\"(max-width: 1787px) 100vw, 1787px\" \/><\/a><figcaption class=\"wp-element-caption\">Create a Shelf Tool and paste the Python Code<\/figcaption><\/figure>\n\n\n\n<figure class=\"wp-block-image size-full is-style-rounded\"><a href=\"https:\/\/www.enoni.de\/wp\/wp-content\/uploads\/2023\/03\/HOU_deadline_script_husk_03.jpg\"><img decoding=\"async\" width=\"824\" height=\"852\" src=\"https:\/\/www.enoni.de\/wp\/wp-content\/uploads\/2023\/03\/HOU_deadline_script_husk_03.jpg\" alt=\"\" class=\"wp-image-6566\" srcset=\"https:\/\/www.enoni.de\/wp\/wp-content\/uploads\/2023\/03\/HOU_deadline_script_husk_03.jpg 824w, https:\/\/www.enoni.de\/wp\/wp-content\/uploads\/2023\/03\/HOU_deadline_script_husk_03-768x794.jpg 768w\" sizes=\"(max-width: 824px) 100vw, 824px\" \/><\/a><figcaption class=\"wp-element-caption\">Or entries in OPmenu.xml<\/figcaption><\/figure>\n\n\n\n<p>This will create a script, that has an input prompt where you can define how many frames Deadline will render per task. You can, of course, build a high advanced submitter with a lot more inputs, choices and a fancy GUI. <\/p>\n\n\n\n<p>Here for Octane you have to create a <strong>\u201chusk\u201d<\/strong> pool on Deadline:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"python\" class=\"language-python\">## Niclas Schlapmann - Freelance 3D Technical Artist\n## www.enoni.de\n## hello@enoni.de\n## 02.03.2023\n##################################### Imports ####################################\nimport hou\nimport os\nimport sys\nimport traceback\nimport json\nimport getpass\nimport time\nfrom time import *\n##################################################################################\n\nuser = getpass.getuser()\nlt = localtime()\nyear, month, day, hour, minute, sec = lt[0:6]\ndate = str(year)[2:4] + \"-\" + str(month).zfill(2) + \"-\" + str(day).zfill(2) + \" - \" + str(hour).zfill(2) + \":\" + str(minute).zfill(2) + \":\" + str(sec).zfill(2)\n\ndef deadline_submitter_husk_task():\n    renderNodes = hou.selectedNodes()\n    \n    if not renderNodes:\n        return\n    \n    if renderNodes[0].type().name() not in [\"usdrender_rop\", \"usd_rop\"]:\n        hou.ui.displayMessage(\"Select a proper USD-ROP or USDRender-ROP\")\n        return\n    \n    for renderNode in renderNodes:\n        jobname = hou.getenv(\"HIPNAME\")\n        pool = \"husk\"\n        secondarypool = \"husk\"\n        comment = \"submitted by &lt;\" + user + \"&gt; \" + date\n        department = \"enoni.de\"\n        \n        if renderNode.evalParm(renderNode.path() + \"\/trange\") &gt;= 1:\n            framelist = str(int(renderNode.evalParm(renderNode.path() + \"\/f1\"))) + \"-\" + str(int(renderNode.evalParm(renderNode.path() + \"\/f2\")))\n        else:\n            framelist = str(int(hou.frame())) + \"-\" + str(int(hou.frame()))\n\n        framecount = int(renderNode.evalParm(renderNode.path() + \"\/f2\")) - int(renderNode.evalParm(renderNode.path() + \"\/f1\")) + 1\n        frame_input_count = hou.ui.readInput(\"Frames per task:\", buttons=(\"OK\", \"Cancel\"), initial_contents=str(framecount))\n\n        if frame_input_count[0] == 1:\n            return\n        \n        ## create prop dictionary ##\n        jobProperties = {\n            'batch': False,\n            'jobname': jobname,\n            'comment': comment,\n            'department': department,\n            'pool': pool,\n            'secondarypool': secondarypool,\n            'group': 'none',\n            'priority': 99,\n            'tasktimeout': 0,\n            'autotimeout': 0,\n            'concurrent': 1,\n            'machinelimit': 0,\n            'slavelimit': 1,\n            'limits': '',\n            'onjobcomplete': 'Nothing',\n            'jobsuspended': 0,\n            'shouldprecache': 1,\n            'isblacklist': 0,\n            'machinelist': '',\n            'overrideframes': 1,\n            'framelist': framelist,\n            'framespertask': int(frame_input_count[1]),\n            'bits': '64bit',\n            'submitscene': 0,\n            'isframedependent': 0,\n            'gpuopenclenable': 0,\n            'gpuspertask': 0,\n            'gpudevices': '',\n            'ignoreinputs': 0,\n            'separateWedgeJobs': 0,\n            \n            'mantrajob': 0,\n            'mantrapool': pool,\n            'mantrasecondarypool': secondarypool,\n            'mantragroup': 'none',\n            'mantrapriority': 50,\n            'mantratasktimeout': 0,\n            'mantraautotimeout': 0,\n            'mantraconcurrent': 1,\n            'mantramachinelimit': 0,\n            'mantraslavelimit': 1,\n            'mantralimits': '',\n            'mantraonjobcomplete': 'Nothing',\n            'mantraisblacklist': 0,\n            'mantramachinelist': '',\n            'mantrathreads': 0,\n            'mantralocalexport': 0,\n            \n            'arnoldjob': 1,\n            'arnoldpool': pool,\n            'arnoldsecondarypool': secondarypool,\n            'arnoldgroup': 'none',\n            'arnoldpriority': 50,\n            'arnoldtasktimeout': 0,\n            'arnoldautotimeout': 0,\n            'arnoldconcurrent': 1,\n            'arnoldmachinelimit': 0,\n            'arnoldslavelimit': 1,\n            'arnoldonjobcomplete': 'Nothing',\n            'arnoldlimits': '',\n            'arnoldisblacklist': 0,\n            'arnoldmachinelist': '',\n            'arnoldthreads': 0,\n            'arnoldlocalexport': 1,\n            \n            'rendermanjob': 0,\n            'rendermanpool': pool,\n            'rendermansecondarypool': secondarypool,\n            'rendermangroup': 'none',\n            'rendermanpriority': 50,\n            'rendermantasktimeout': 0,\n            'rendermanconcurrent': 1,\n            'rendermanmachinelimit': 0,\n            'rendermanlimits': '',\n            'rendermanonjobcomplete': 'Nothing',\n            'rendermanisblacklist': 0,\n            'rendermanmachinelist': '',\n            'rendermanthreads': 0,\n            'rendermanarguments': '',\n            'rendermanlocalexport': 0,\n            \n            'redshiftjob': 0,\n            'redshiftpool': pool,\n            'redshiftsecondarypool': secondarypool,\n            'redshiftgroup': 'none',\n            'redshiftpriority': 50,\n            'redshifttasktimeout': 0,\n            'redshiftautotimeout': 0,\n            'redshiftconcurrent': 1,\n            'redshiftmachinelimit': 0,\n            'redshiftslavelimit': 1,\n            'redshiftlimits': '',\n            'redshiftonjobcomplete': 'Nothing',\n            'redshiftisblacklist': 0,\n            'redshiftmachinelist': '',\n            'redshiftarguments': '',\n            'redshiftlocalexport': 0,\n            \n            'usdjob': 1,\n            'usdpool': pool,\n            'usdsecondarypool': secondarypool,\n            'usdgroup': 'none',\n            'usdpriority': 50,\n            'usdtasktimeout': 0,\n            'usdautotimeout': 0,\n            'usdconcurrent': 1,\n            'usdmachinelimit': 0,\n            'usdslavelimit': 1,\n            'usdlimits': '',\n            'usdonjobcomplete': 'Nothing',\n            'usdisblacklist': 0,\n            'usdmachinelist': '',\n            'usdarguments': '',\n            'usdlocalexport': 1,\n            'usdloglevel': 2,\n    \n            'vrayjob': 0,\n            'vraypool': pool,\n            'vraysecondarypool': secondarypool,\n            'vraygroup': 'none',\n            'vraypriority': 50,\n            'vraytasktimeout': 0,\n            'vrayautotimeout': 0,\n            'vrayconcurrent': 1,\n            'vraymachinelimit': 0,\n            'vrayslavelimit': 1,\n            'vraylimits': '',\n            'vrayonjobcomplete': 'Nothing',\n            'vrayisblacklist': 0,\n            'vraymachinelist': '',\n            'vraythreads': 0,\n            'vrayarguments': '',\n            'vraylocalexport': 0,\n            \n            'tilesenabled': 0,\n            'tilesinx': 3,\n            'tilesiny': 3,\n            'tilessingleframeenabled': 1,\n            'tilessingleframe': 1,\n            'jigsawenabled': 1,\n            'jigsawregioncount': 0,\n            'jigsawregions': [],\n            'submitdependentassembly': 1,\n            'backgroundoption': 'Blank Image',\n            'backgroundimage': '',\n            'erroronmissingtiles': '1',\n            'erroronmissingbackground': '0',\n            'cleanuptiles': '1'\n        }\n        \n        ## write USD from USD-ROP ##\n        usd_file_path = renderNode.evalParm(renderNode.path() + \"\/lopoutput\")\n        if os.path.isfile(usd_file_path):\n            if hou.ui.displayMessage(\"USD render file already exist. Override\", buttons=(\"Yes\", \"Abort\")) == 0:\n                renderNode.parm(\"execute\").pressButton()\n            else:\n                return\n        else:\n            renderNode.parm(\"execute\").pressButton()\n        \n        ## Submit to Deadline ##\n        flag = 0\n        \n        ## imports and sys pathes for deadline ##\n        try:\n            from CallDeadlineCommand import CallDeadlineCommand\n        except ImportError:\n            path = \"\"\n            print(\"The CallDeadlineCommand.py script could not be found in the Houdini installation. Please make sure that the Deadline Client has been installed on this machine.\\n\")\n            hou.ui.displayMessage(\"The CallDeadlineCommand.py script could not be found in the Houdini installation. Please make sure that the Deadline Client has been installed on this machine.\", title=\"Submit Houdini To Deadline\")\n        else:\n            path = CallDeadlineCommand([\"-GetRepositoryPath\", \"submission\/Houdini\/Main\"]).strip()\n        \n        if path:\n            path = path.replace(\"\\\\\", \"\/\")\n            \n            # Add the path to the system path\n            if path not in sys.path:\n                print(\"Appending \\\"\" + path + \"\\\" to system path to import SubmitHoudiniToDeadline module\")\n                sys.path.append(path)\n            else:\n                pass\n            \n            # Import the script and call the main() function\n            try:\n                import SubmitHoudiniToDeadline\n            except:\n                print(traceback.format_exc())\n                print(\"The SubmitHoudiniToDeadline.py script could not be found in the Deadline Repository. Please make sure that the Deadline Client has been installed on this machine, that the Deadline Client bin folder is set in the DEADLINE_PATH environment variable, and that the Deadline Client has been configured to point to a valid Repository.\")\n        else:\n            print(\"The SubmitHoudiniToDeadline.py script could not be found in the Deadline Repository. Please make sure that the Deadline Client has been installed on this machine, that the Deadline Client bin folder is set in the DEADLINE_PATH environment variable, and that the Deadline Client has been configured to point to a valid Repository.\")\n        \n        ## Get Deadline Info ##\n        print(\"Grabbing submitter info...\")\n        try:\n            output = json.loads(CallDeadlineCommand([\"-prettyJSON\", \"-GetSubmissionInfo\", \"Pools\", \"Groups\", \"MaxPriority\", \"TaskLimit\", \"UserHomeDir\", \"RepoDir:submission\/Houdini\/Main\", \"RepoDir:submission\/Integration\/Main\", \"RepoDirNoCustom:draft\", \"RepoDirNoCustom:submission\/Jigsaw\", ]))\n        except:\n            print(\"Unable to get submitter info from Deadline:\\n\\n\" + traceback.format_exc())\n            raise\n        \n        if output[\"ok\"]:\n            submissionInfo = output[\"result\"]\n            hou.putenv(\"Deadline_Submission_Info\", json.dumps(submissionInfo))\n        else:\n            print(\"DeadlineCommand returned a bad result and was unable to grab the submitter info.\\n\\n\" + output[\"result\"])\n            raise Exception(output[\"result\"])\n        \n        ## Submit Render Job ##\n        try:\n            import SubmitHoudiniToDeadlineFunctions as SHTDFunctions\n            flag = 1\n        except Exception as e:\n            print(e)\n            hou.ui.displayMessage(\"ns_Pipe&gt; Library import failure. Make sure you have a proper Deadline installation.\")\n        \n        if flag:\n            try:\n                jobProperties.update({\"ns_pipe_hou_version\": hou.getenv(\"_HIP_SAVEVERSION\")})\n                jobProperties.update({\"ns_pipe_image_out\" : hou.parm(renderNode.path() + \"\/spare_output_image\").eval()})\n                jobIds = SHTDFunctions.SubmitRenderJob_husk(renderNode, jobProperties, \"\")\n            except Exception as e:\n                print(e)\n                hou.ui.displayMessage(\"ns_Pipe&gt; Can`t submitting to Deadline Repository.\")\n\n\ndeadline_submitter_husk_task()<\/code><\/pre>\n\n\n\n<p><\/p>\n\n\n\n<div class=\"wp-block-file my_block_border_o\"><a id=\"wp-block-file--media-d0648927-8f21-4916-aab2-b549abd5f6c9\" href=\"https:\/\/www.enoni.de\/wp\/wp-content\/uploads\/2023\/03\/Houdini_Deadline_Submitter_4_Husk.zip\">Houdini_Deadline_Submitter_4_Husk.zip<\/a><\/div>\n\n\n\n<p><\/p>\n\n\n\n<p class=\"small_highlight has-medium-font-size\"><strong>IMPORTANT:<\/strong><\/p>\n\n\n\n<p>You need on your USD-ROP  a string field where the script find the path for the image output. The string var field has to be named <strong><em>&#8220;spare_output_image&#8221;<\/em><\/strong>. I just referencing the path from the Karma Settings Node.<\/p>\n\n\n<div class=\"wp-block-image is-style-rounded\">\n<figure class=\"aligncenter size-full\"><a href=\"https:\/\/www.enoni.de\/wp\/wp-content\/uploads\/2023\/03\/HOU_deadline_script_husk.jpg\"><img decoding=\"async\" width=\"1661\" height=\"913\" src=\"https:\/\/www.enoni.de\/wp\/wp-content\/uploads\/2023\/03\/HOU_deadline_script_husk.jpg\" alt=\"\" class=\"wp-image-6553\" srcset=\"https:\/\/www.enoni.de\/wp\/wp-content\/uploads\/2023\/03\/HOU_deadline_script_husk.jpg 1661w, https:\/\/www.enoni.de\/wp\/wp-content\/uploads\/2023\/03\/HOU_deadline_script_husk-768x422.jpg 768w, https:\/\/www.enoni.de\/wp\/wp-content\/uploads\/2023\/03\/HOU_deadline_script_husk-1536x844.jpg 1536w\" sizes=\"(max-width: 1661px) 100vw, 1661px\" \/><\/a><figcaption class=\"wp-element-caption\">Custom String Field for ImageFile Output<\/figcaption><\/figure>\n<\/div>\n\n\n<p><\/p>\n\n\n\n<p>When you now trigger the script with selected USD-ROP node, you are able to send USD Husk Render jobs to Deadline. Modify it to your specific needs and build your own Deadline submitter. <\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u00bb A summary\/guide, how i render Karma jobs on Deadline and Husk &#8211; Directly from Houdini USD-ROPs in Solaris. \u00ab The Standart Deadline Submitter\/Implementation uses Hython to start Houdini render jobs. BTW, im using Deadline Version 10.1.18.4 Why Husk not Hython? Like Husk, Hython is a Command line tool as well. Its a kind of <\/p>\n","protected":false},"author":1,"featured_media":6530,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[51,28,64,56,46,37],"tags":[],"class_list":["post-6487","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-deadline","category-houdini","category-karma","category-pipeline","category-python","category-scripting"],"_links":{"self":[{"href":"https:\/\/www.enoni.de\/wp\/wp-json\/wp\/v2\/posts\/6487","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.enoni.de\/wp\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.enoni.de\/wp\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.enoni.de\/wp\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.enoni.de\/wp\/wp-json\/wp\/v2\/comments?post=6487"}],"version-history":[{"count":139,"href":"https:\/\/www.enoni.de\/wp\/wp-json\/wp\/v2\/posts\/6487\/revisions"}],"predecessor-version":[{"id":9566,"href":"https:\/\/www.enoni.de\/wp\/wp-json\/wp\/v2\/posts\/6487\/revisions\/9566"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.enoni.de\/wp\/wp-json\/wp\/v2\/media\/6530"}],"wp:attachment":[{"href":"https:\/\/www.enoni.de\/wp\/wp-json\/wp\/v2\/media?parent=6487"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.enoni.de\/wp\/wp-json\/wp\/v2\/categories?post=6487"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.enoni.de\/wp\/wp-json\/wp\/v2\/tags?post=6487"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}