diff --git a/src/deadline/ae_adaptor/AEClient/ae_client.py b/src/deadline/ae_adaptor/AEClient/ae_client.py index 35e7c07..3ea0bd7 100644 --- a/src/deadline/ae_adaptor/AEClient/ae_client.py +++ b/src/deadline/ae_adaptor/AEClient/ae_client.py @@ -44,7 +44,7 @@ def __init__(self, server_path: str) -> None: client_path_abs = client_path_abs.replace("\\", "\\\\") startup_script_inline = f"var x = new File('{client_path_abs}') ; x.open(); eval(x.read()); app.exitAfterLaunchAndEval = false;" - ae_exe = os.environ.get("AFTEREFFECTS_ADAPTOR_AEFX_EXECUTABLE", "afterfx") + ae_exe = os.environ.get("AFTERFX_EXECUTABLE", "afterfx") # flag -noui for no ui doesn't close properly when running in monitor cmd_args = [ae_exe, "-noui", "-s", startup_script_inline] diff --git a/src/deadline/ae_submitter/Imports.jsx b/src/deadline/ae_submitter/Imports.jsx index e29c636..4f26c27 100644 --- a/src/deadline/ae_submitter/Imports.jsx +++ b/src/deadline/ae_submitter/Imports.jsx @@ -12,6 +12,8 @@ #include "data/InitData.jsx" +#include "data/RenderScript.jsx" + #include "submission/AssetReferenceTemplate.jsx" #include "submission/DataTemplate.jsx" diff --git a/src/deadline/ae_submitter/UI/AESubmitterUI.jsx b/src/deadline/ae_submitter/UI/AESubmitterUI.jsx index 8afc2bf..b6aff1d 100644 --- a/src/deadline/ae_submitter/UI/AESubmitterUI.jsx +++ b/src/deadline/ae_submitter/UI/AESubmitterUI.jsx @@ -75,6 +75,7 @@ function __generateSubmitterUI() { } else { initFirstAndLast = dcUtil.parseBool(dcSettings.getIniSetting("FirstAndLast", "false")); } + var initFramesPerTask = parseInt(dcSettings.getIniSetting("FramesPerTask", "10")); var initIgnoreMissingLayers = dcUtil.parseBool(dcSettings.getIniSetting("MissingLayers", "false")); var initIgnoreMissingEffects = dcUtil.parseBool(dcSettings.getIniSetting("MissingEffects", "false")); @@ -672,6 +673,12 @@ function __generateSubmitterUI() { frameListGroup.useCompFrameList.value = initUseCompFrameRange; frameListGroup.useCompFrameList.helpTip = 'If enabled, the Comp\'s frame list will be used instead of the frame list in this submitter.'; + frameListGroup.framesPerTask = frameListGroup.add('edittext', undefined, initFramesPerTask); + frameListGroup.framesPerTask.size = SHORT_TEXT_SIZE; + frameListGroup.framesPerTaskLabel = frameListGroup.add('statictext', undefined, 'Frames Per Task'); + frameListGroup.framesPerTaskLabel.size = LABEL_SIZE; + frameListGroup.framesPerTaskLabel.helpTip = 'This is the number of frames to be rendered per Job Task.'; + // Create Comp submission group and widgets (Select One, Use Selected in RQ, All as separate) compSubmissionGroup = deadlineCloud.aeOptionsPanel.add('group', undefined); compSubmissionGroup.alignment = [ScriptUI.Alignment.FILL, ScriptUI.Alignment.TOP]; @@ -1222,6 +1229,17 @@ function __generateSubmitterUI() { } } + frameListGroup.framesPerTask.onChange = function() { + currentValue = frameListGroup.framesPerTask.text.replace(/[^\d]/g, ''); + + if (parseInt(currentValue) > 9999) { + frameListGroup.framesPerTask.text = "9999" + } + if (parseInt(currentValue) < 1) { + frameListGroup.framesPerTask.text = "1" + } + } + frameListGroup.frameListLabel.enabled = !initSubmitEntireQueue && !initMultiMachine; frameListGroup.frameList.enabled = !initUseCompFrameRange && !initSubmitEntireQueue && !initMultiMachine; frameListGroup.useCompFrameList.enabled = !initSubmitEntireQueue && !initMultiMachine; diff --git a/src/deadline/ae_submitter/UI/SubmitButton.jsx b/src/deadline/ae_submitter/UI/SubmitButton.jsx index 520ebb9..5a066b1 100644 --- a/src/deadline/ae_submitter/UI/SubmitButton.jsx +++ b/src/deadline/ae_submitter/UI/SubmitButton.jsx @@ -79,26 +79,40 @@ function __generateSubmitButton() { function generateStep(basicTemplate, itemName, stepsTemplate, stepID, renderQueueItem) { var compNameToCheck = dcUtil.removeIllegalCharacters(renderQueueItem.comp.name); - // logger.debug("[generateStep] itemName: " + itemName + " stepID: " + stepID + " compNameToCheck: " + compNameToCheck, _submitButtonFileName); stepsTemplate[0].name = itemName; - stepsTemplate[0].parameterSpace.taskParameterDefinitions[0].range = "{{Param." + itemName + "_Frames}}"; - stepsTemplate[0].script.embeddedFiles[0].data = "frame: {{Task.Param.Frame}}"; - if(itemName != compNameToCheck) - { - stepsTemplate[0].stepEnvironments[0].script.embeddedFiles[0].data += "comp_name: {{Param." + compNameToCheck + "_CompName}} \n"; - } - else - { - stepsTemplate[0].stepEnvironments[0].script.embeddedFiles[0].data += "comp_name: {{Param." + itemName + "_CompName}} \n"; - } - stepsTemplate[0].stepEnvironments[0].script.embeddedFiles[0].data += "output_file_path: {{Param." + itemName + "_OutputFilePath}} \n"; - stepsTemplate[0].stepEnvironments[0].script.embeddedFiles[0].data += "output_pattern: {{Param." + itemName + "_OutputPattern}} \n"; - stepsTemplate[0].stepEnvironments[0].script.embeddedFiles[0].data += "output_format: {{Param." + itemName + "_OutputFormat}} \n"; + stepsTemplate[0].parameterSpace.taskParameterDefinitions[0].range = "{{Param." + itemName + "_FrameStart}} - {{Param." + itemName + "_FrameEnd}} : {{Param." + itemName + "_ChunkSize}}"; + stepsTemplate[0].parameterSpace.taskParameterDefinitions[1].range = "{{Param." + itemName + "_FrameStartPlusChunkSizeMinusOne}}-{{Param." + itemName + "_FrameEndMinusOne}}:{{Param." + itemName + "_ChunkSize}},{{Param." + itemName + "_FrameEnd}}"; + + stepsTemplate[0].script.embeddedFiles[0].data = dcRenderScript.generateRenderCommand("{{Param." + itemName + "_CompName}}") + basicTemplate.steps[stepID-1] = stepsTemplate[0]; // logger.debug("[generateStep] basicTemplate: " + JSON.stringify(basicTemplate), _submitButtonFileName); return basicTemplate; } + function applyFrameChunkingDataToTemplate(itemName, frameList) + { + var output = { + "parameterDefinitions": [], + "parameterValues": [], + } + + var chunkParametersList = dcUtil.getFrameChunkParameters(frameList, frameListGroup.framesPerTask.text); + // Currently only support a single frame range + output.parameterDefinitions.push(applyDataToTemplate(itemName + "_FrameStart", dcUtil.deepCopy(dcDataTemplate.FrameStart))); + output.parameterDefinitions.push(applyDataToTemplate(itemName + "_FrameEnd", dcUtil.deepCopy(dcDataTemplate.FrameEnd))); + output.parameterDefinitions.push(applyDataToTemplate(itemName + "_ChunkSize", dcUtil.deepCopy(dcDataTemplate.ChunkSize))); + output.parameterDefinitions.push(applyDataToTemplate(itemName + "_FrameStartPlusChunkSizeMinusOne", dcUtil.deepCopy(dcDataTemplate.FrameStartPlusChunkSizeMinusOne))); + output.parameterDefinitions.push(applyDataToTemplate(itemName + "_FrameEndMinusOne", dcUtil.deepCopy(dcDataTemplate.FrameEndMinusOne))); + + output.parameterValues.push(applyDataToParameterTemplate(itemName + "_FrameStart", chunkParametersList[0].frameStart)); + output.parameterValues.push(applyDataToParameterTemplate(itemName + "_FrameEnd", chunkParametersList[0].frameEnd)); + output.parameterValues.push(applyDataToParameterTemplate(itemName + "_ChunkSize", chunkParametersList[0].chunkSize)); + output.parameterValues.push(applyDataToParameterTemplate(itemName + "_FrameStartPlusChunkSizeMinusOne", chunkParametersList[0].frameStartPlusChunkSizeMinusOne)); + output.parameterValues.push(applyDataToParameterTemplate(itemName + "_FrameEndMinusOne", chunkParametersList[0].frameEndMinusOne)); + return output + } + function applyDataToTemplate(dataName, dataTemplate) { if(dataName.indexOf("Frames") >= 0 || dataName.indexOf("OutputFilePath") >= 0) @@ -315,6 +329,7 @@ function __generateSubmitButton() { if (submissionText == useQueue) totalJobs = 1; + queuedCount = 1; progressBarPanel.progressBar.value = 0; @@ -467,12 +482,13 @@ function __generateSubmitButton() { var __compName = dcUtil.removeIllegalCharacters(app.project.renderQueue.item(j).comp.name); var frameList = getFrameList(app.project.renderQueue.item(j)); - - // logger.debug("[createDataAndParameterTemplateOneJob] __compName: " + __compName, _submitButtonFileName); - // logger.debug("[createDataAndParameterTemplateOneJob] frameList: " + frameList, _submitButtonFileName); - + + var chunking = applyFrameChunkingDataToTemplate(__compName, frameList) + // Add data to the main template - jobTemplate.parameterDefinitions.push(applyDataToTemplate(__compName + "_Frames", dcUtil.deepCopy(dcDataTemplate.Frames))); + for (var pIdx = 0; pIdx < chunking.parameterDefinitions.length; pIdx++) { + jobTemplate.parameterDefinitions.push(chunking.parameterDefinitions[pIdx]); + } jobTemplate.parameterDefinitions.push(applyDataToTemplate(__compName + "_OutputPattern", dcUtil.deepCopy(dcDataTemplate.OutputPattern))); jobTemplate.parameterDefinitions.push(applyDataToTemplate(__compName + "_OutputFormat", dcUtil.deepCopy(dcDataTemplate.OutputFormat))); jobTemplate.parameterDefinitions.push(applyDataToTemplate(__compName + "_CompName", dcUtil.deepCopy(dcDataTemplate.CompName))); @@ -480,13 +496,13 @@ function __generateSubmitButton() { // Add steps data per task that needs to be run jobTemplate = generateStep(jobTemplate, __compName, dcUtil.deepCopy(stepsTemplate), stepIndex, app.project.renderQueue.item(j)); - // logger.debug("[createDataAndParameterTemplateOneJob] jobTemplate: " + JSON.stringify(jobTemplate), _submitButtonFileName); jobTemplate = applyHostReqToTemplate(jobTemplate); - // logger.debug("[createDataAndParameterTemplateOneJob] jobTemplate applyHostReqToTemplate: " + JSON.stringify(jobTemplate), _submitButtonFileName); // Add data to the parameter template - jobParams.parameterValues.push(applyDataToParameterTemplate(__compName + "_Frames", frameList)); + for (var pIdx = 0; pIdx < chunking.parameterValues.length; pIdx++) { + jobParams.parameterValues.push(chunking.parameterValues[pIdx]); + } jobParams.parameterValues.push(applyDataToParameterTemplate(__compName + "_CompName", app.project.renderQueue.item(j).comp.name)); jobParams.parameterValues.push(applyDataToParameterTemplate(__compName + "_OutputPattern", dcUtil.removePercentageFromFileName(getRenderQueueItemData(app.project.renderQueue.item(j))["fileName"]))); jobParams.parameterValues.push(applyDataToParameterTemplate(__compName + "_OutputFormat", getRenderQueueItemData(app.project.renderQueue.item(j))["extension"])); @@ -505,9 +521,13 @@ function __generateSubmitButton() { { var compNameToCheck = dcUtil.removeIllegalCharacters(renderQueueItem.comp.name); var frameList = getFrameList(renderQueueItem); + + var chunking = applyFrameChunkingDataToTemplate(itemName, frameList) jobTemplate = generatePartialTemplate(); // Add data to the main template - jobTemplate.parameterDefinitions.push(applyDataToTemplate(itemName + "_Frames", dcDataTemplate.Frames)); + for (var pIdx = 0; pIdx < chunking.parameterDefinitions.length; pIdx++) { + jobTemplate.parameterDefinitions.push(chunking.parameterDefinitions[pIdx]); + } jobTemplate.parameterDefinitions.push(applyDataToTemplate(itemName + "_OutputPattern", dcDataTemplate.OutputPattern)); jobTemplate.parameterDefinitions.push(applyDataToTemplate(itemName + "_OutputFormat", dcDataTemplate.OutputFormat)); // jobTemplate.parameterDefinitions.push(applyDataToTemplate(itemName + "_CompName", dcDataTemplate.CompName)); @@ -523,7 +543,9 @@ function __generateSubmitButton() { jobParams.parameterValues.push(applyDataToParameterTemplate("deadline:maxFailedTasksCount", dcProperties.deadlineJobParameters.maxFailedTasksCount.get())); jobParams.parameterValues.push(applyDataToParameterTemplate("deadline:maxRetriesPerTask", dcProperties.deadlineJobParameters.maxRetriesPerTask.get())); jobParams.parameterValues.push(applyDataToParameterTemplate("deadline:priority", dcProperties.deadlineJobParameters.priority.get())); - jobParams.parameterValues.push(applyDataToParameterTemplate(itemName + "_Frames", frameList)); + for (var pIdx = 0; pIdx < chunking.parameterValues.length; pIdx++) { + jobParams.parameterValues.push(chunking.parameterValues[pIdx]); + } // jobParams.parameterValues.push(applyDataToParameterTemplate(itemName + "_CompName", comp)); jobParams.parameterValues.push(applyDataToParameterTemplate(itemName + "_OutputPattern", dcUtil.removePercentageFromFileName((getRenderQueueItemData(renderQueueItem)["fileName"])))); jobParams.parameterValues.push(applyDataToParameterTemplate(itemName + "_OutputFormat", getRenderQueueItemData(renderQueueItem)["extension"])); diff --git a/src/deadline/ae_submitter/data/InitData.jsx b/src/deadline/ae_submitter/data/InitData.jsx index 7fa11fe..b820465 100644 --- a/src/deadline/ae_submitter/data/InitData.jsx +++ b/src/deadline/ae_submitter/data/InitData.jsx @@ -87,9 +87,82 @@ function __generateInitData() } } } + var fontReferences = generateFontReferences(); + logger.debug("Get Fonts: " + key, scriptFileInitDataName); + for (var i = 0; i < fontReferences.length; i++) { + detectedItemsList.push(fontReferences[i]); + } dcProperties.jobAttachments.autoDetectedInputFiles.set(detectedItemsList); } + function _generateFontReferences() { + var fontLocations = []; + var usedList = app.project.usedFonts; + for (var i = 0; i < usedList.length; i++) { + var font = usedList[i].font; + var fontFamilyName = font.familyName; + var familyStyle = font.styleName; + var fontName = fontFamilyName + " " + familyStyle + ".otf"; + var fontLocation = font.location; + fontLocations.push([fontName, fontLocation]); + } + return fontLocations + } + + function _oldGenerateFontReferences() { + var fontLocations = []; + var items = app.project.items; + for (var i = items.length; i >= 1; i--) { + var myItem = app.project.item(i); + if (myItem instanceof CompItem) { + for (var j = myItem.layers.length; j >= 1; j--) { + var myLayer = myItem.layers[j]; + if (myLayer instanceof TextLayer){ + var textDocument = myLayer.text.sourceText.value; + var fontLocation = textDocument.fontLocation; + var fontFamilyName = textDocument.fontFamily; + var familyStyle = textDocument.fontStyle; + var fontName = fontFamilyName + " " + familyStyle + ".otf"; + fontLocations.push([fontName, fontLocation]); + } + } + } + } + return fontLocations + } + + function generateFontReferences() { + var rawFontPaths = []; + if (parseInt(app.version[0] + app.version[1]) >= 24) { + rawFontPaths = _generateFontReferences(); + } else { + rawFontPaths = _oldGenerateFontReferences(); + } + var _tempFilePath = dcUtil.normPath(Folder.temp.fsName + "/" + "tempFonts"); + var formattedFontPaths = []; + var tempFontPath = new Folder(_tempFilePath); + if (!tempFontPath.exists) { + tempFontPath.create(); + } + + + for (var i = 0; i < rawFontPaths.length; i++) { + var fontName = rawFontPaths[i][0]; + var fontLocation = rawFontPaths[i][1]; + if (fontLocation.indexOf("C:\\Windows\\Fonts") !== -1) { + // We are filtering out fonts installed in C:\Windows\Fonts potentially here. + // I would make the argument that if a font is installed to the system (not an Adobe Font), that + // it should be managed on a system level not through the submitter. + continue + } + var fontFile = File(fontLocation); + var _filePath = dcUtil.normPath(_tempFilePath + "/" + fontName); + fontFile.copy(_filePath); + formattedFontPaths.push(_filePath); + } + return formattedFontPaths; + } + function initAutoDetectOutputDirectories() { var detectedOutputDirectories= []; @@ -153,4 +226,4 @@ function __generateInitData() } } -dcInitData = __generateInitData(); \ No newline at end of file +dcInitData = __generateInitData(); diff --git a/src/deadline/ae_submitter/data/RenderScript.jsx b/src/deadline/ae_submitter/data/RenderScript.jsx new file mode 100644 index 0000000..07fd2db --- /dev/null +++ b/src/deadline/ae_submitter/data/RenderScript.jsx @@ -0,0 +1,19 @@ +function __generateRenderScriptTemplate(compParameterName) { + return ( +"@ECHO OFF\n" + +"IF \"%AFTEREFFECTS_ADAPTOR_AERENDER_EXECUTABLE%\" == \"\" (\n" + +" set AFTEREFFECTS_ADAPTOR_AERENDER_EXECUTABLE=aerender.exe\n" + +")\n" + +"echo \"Running: \\\"%AFTEREFFECTS_ADAPTOR_AERENDER_EXECUTABLE%\\\" -project \\\"{{Param.AfterEffectsProjectFile}}\\\" -comp \\\"" + compParameterName + "\\\" -s {{Task.Param.FrameChunkStart}} -e {{Task.Param.FrameChunkEnd}}\"\n" + +"\"%AFTEREFFECTS_ADAPTOR_AERENDER_EXECUTABLE%\" -project \"{{Param.AfterEffectsProjectFile}}\" -comp \"" + compParameterName + "\" -s {{Task.Param.FrameChunkStart}} -e {{Task.Param.FrameChunkEnd}}\n" + +"IF %ERRORLEVEL% NEQ 0 (\n" + +" echo \"Return code: %ERRORLEVEL%\"\n" + +" exit %ERRORLEVEL%\n" + +")\n" + +"exit 0\n" + ) +} + +dcRenderScript = { + "generateRenderCommand": __generateRenderScriptTemplate, +}; diff --git a/src/deadline/ae_submitter/submission/DataTemplate.jsx b/src/deadline/ae_submitter/submission/DataTemplate.jsx index 21460c0..3f29ebd 100644 --- a/src/deadline/ae_submitter/submission/DataTemplate.jsx +++ b/src/deadline/ae_submitter/submission/DataTemplate.jsx @@ -1,16 +1,65 @@ function __generateDataTemplate() { - var Frames = + var FrameStart = { - "name": "Frames", + "name": "FrameStart", "type": "STRING", "userInterface": { "control": "LINE_EDIT", - "label": "Frames", + "label": "Starting Frames", "groupLabel": "After Effects Settings" }, - "description": "The frames to render. E.g. 1,8,11", + "description": "The starting frame to render.", "minLength": 1 } + var FrameEnd = + { + "name": "FrameEnd", + "type": "STRING", + "userInterface": { + "control": "LINE_EDIT", + "label": "Ending Frames", + "groupLabel": "After Effects Settings" + }, + "description": "The ending frame to render.", + "minLength": 1 + } + var ChunkSize = + { + "name": "ChunkSize", + "type": "STRING", + "userInterface": { + "control": "LINE_EDIT", + "label": "Frames Per Task", + "groupLabel": "After Effects Settings" + }, + "description": "The chunk size of frames per task to render", + "minLength": 1 + } + var FrameStartPlusChunkSizeMinusOne = + { + "name": "FrameStartPlusChunkSizeMinusOne", + "type": "STRING", + "userInterface": { + "control": "LINE_EDIT", + "label": "FrameStart + ChunkSize - 1", + "groupLabel": "After Effects Settings" + }, + "description": "[Internal] This value needs to equal FrameStart + ChunkSize - 1", + "minLength": 1 + } + var FrameEndMinusOne = + { + "name": "FrameEndMinusOne", + "type": "STRING", + "userInterface": { + "control": "LINE_EDIT", + "label": "FrameEnd - 1", + "groupLabel": "After Effects Settings" + }, + "description": "[Internal] This value needs to equal FrameEnd - 1", + "minLength": 1 + } + var OutputPattern = { "name": "OutputPattern", @@ -45,7 +94,11 @@ function __generateDataTemplate() { "description": "The render output path." } return { - "Frames": Frames, + "FrameStart": FrameStart, + "FrameEnd": FrameEnd, + "ChunkSize": ChunkSize, + "FrameStartPlusChunkSizeMinusOne": FrameStartPlusChunkSizeMinusOne, + "FrameEndMinusOne": FrameEndMinusOne, "OutputPattern" : OutputPattern, "OutputFormat": OutputFormat, "CompName": CompName, diff --git a/src/deadline/ae_submitter/submission/JobTemplate.jsx b/src/deadline/ae_submitter/submission/JobTemplate.jsx index 3b7675a..685f138 100644 --- a/src/deadline/ae_submitter/submission/JobTemplate.jsx +++ b/src/deadline/ae_submitter/submission/JobTemplate.jsx @@ -29,14 +29,58 @@ var OPENJD_TEMPLATE = { "description": "The After Effects Project file to render." }, { - "name": "Frames", + "name": "FrameStart", "type": "STRING", "userInterface": { "control": "LINE_EDIT", - "label": "Frames", + "label": "Starting Frame", "groupLabel": "After Effects Settings" }, - "description": "The frame range to render. E.g. 1,8,11", + "description": "The starting frame to render.", + "minLength": 1 + }, + { + "name": "FrameEnd", + "type": "STRING", + "userInterface": { + "control": "LINE_EDIT", + "label": "Ending Frame", + "groupLabel": "After Effects Settings" + }, + "description": "The ending frame to render.", + "minLength": 1 + }, + { + "name": "ChunkSize", + "type": "STRING", + "userInterface": { + "control": "LINE_EDIT", + "label": "Frames Per Task", + "groupLabel": "After Effects Settings" + }, + "description": "The chunk size of frames per task to render", + "minLength": 1 + }, + { + "name": "FrameStartPlusChunkSizeMinusOne", + "type": "STRING", + "userInterface": { + "control": "LINE_EDIT", + "label": "FrameStart + ChunkSize - 1", + "groupLabel": "After Effects Settings" + }, + "description": "[Internal] This value needs to equal FrameStart + ChunkSize - 1", + "minLength": 1 + }, + { + "name": "FrameEndMinusOne", + "type": "STRING", + "userInterface": { + "control": "LINE_EDIT", + "label": "FrameEnd - 1", + "groupLabel": "After Effects Settings" + }, + "description": "[Internal] This value needs to equal FrameEnd - 1", "minLength": 1 }, { @@ -74,48 +118,45 @@ var OPENJD_TEMPLATE = { "parameterSpace": { "taskParameterDefinitions": [ { - "name": "Frame", - "type": "INT", - "range": "{{Param.Frames}}" + "name": "FrameChunkStart", + "type": "INT", + "range": "{{Param.FrameStart}}-{{Param.FrameEnd}}:{{Param.ChunkSize}}" + }, + { + "name": "FrameChunkEnd", + "type": "INT", + "range": "{{Param.FrameStartPlusChunkSizeMinusOne}}-{{Param.FrameEndMinusOne}}:{{Param.ChunkSize}},{{Param.FrameEnd}}" } - ] + ], + "combination": "(FrameChunkStart, FrameChunkEnd)" }, "stepEnvironments": [{ - "name": "AfterEffects", - "description": "Runs After Effects in the background.", + "name": "Install Fonts", + "description": "Installs and uninstall fonts", "script": { "embeddedFiles": [ { - "name": "initData", - "filename": "init-data.yaml", - "type": "TEXT", - "data": "project_file: {{Param.AfterEffectsProjectFile}} \n" - - }, - { - "name": "runStart", - "filename": "start.bat", - "type": "TEXT", - "data": "afterfx-openjd daemon start --connection-file {{Session.WorkingDirectory}}/connection.json --init-data file://{{Env.File.initData}} \n" - }, - { - "name": "runStop", - "filename": "stop.bat", + "name": "font_installer", + "filename": "font_installer.py", "type": "TEXT", - "data": "afterfx-openjd daemon stop --connection-file {{Session.WorkingDirectory}}/connection.json \n" + "data": "import argparse\nimport os\nimport shutil\nimport ctypes\nfrom ctypes import wintypes\n\ntry:\n import winreg\nexcept ImportError:\n import _winreg as winreg\n\nuser32 = ctypes.WinDLL('user32', use_last_error=True)\ngdi32 = ctypes.WinDLL('gdi32', use_last_error=True)\n\nFONTS_REG_PATH = r'Software\\Microsoft\\Windows NT\\CurrentVersion\\Fonts'\n\nHWND_BROADCAST = 0xFFFF\nSMTO_ABORTIFHUNG = 0x0002\nWM_FONTCHANGE = 0x001D\nGFRI_DESCRIPTION = 1\nGFRI_ISTRUETYPE = 3\n\nINSTALL_SCOPE_USER = 'USER'\nINSTALL_SCOPE_SYSTEM = 'SYSTEM'\n\nFONT_LOCATION_SYSTEM = os.path.join(os.environ.get('SystemRoot'), 'Fonts')\nFONT_LOCATION_USER = os.path.join(os.environ.get('LocalAppData'), 'Microsoft', 'Windows', 'Fonts')\n\nFONT_EXTENSIONS = {'.OTF', 'TTF'}\n\n# Check if the Fonts folder exists, create it if it doesn't\nif not os.path.exists(FONT_LOCATION_USER):\n print('Creating User Fonts folder: %s' % FONT_LOCATION_USER)\n os.makedirs(FONT_LOCATION_USER)\n\ndef install_font(src_path, scope=INSTALL_SCOPE_USER):\n try:\n # copy the font to the Windows Fonts folder\n if scope == INSTALL_SCOPE_SYSTEM:\n dst_path = os.path.join(FONT_LOCATION_SYSTEM, os.path.basename(src_path))\n registry_scope = winreg.HKEY_LOCAL_MACHINE\n else:\n dst_path = os.path.join(FONT_LOCATION_USER, os.path.basename(src_path))\n registry_scope = winreg.HKEY_CURRENT_USER\n\n shutil.copy(src_path, dst_path)\n # load the font in the current session\n if not gdi32.AddFontResourceW(dst_path):\n os.remove(dst_path)\n raise Exception('AddFontResource failed to load \\'%s\\'' % src_path)\n # notify running programs\n user32.SendMessageTimeoutW(\n HWND_BROADCAST, WM_FONTCHANGE, 0, 0, SMTO_ABORTIFHUNG, 1000, None\n )\n # store the fontname/filename in the registry\n filename = os.path.basename(dst_path)\n fontname = os.path.splitext(filename)[0]\n # try to get the font's real name\n cb = wintypes.DWORD()\n if gdi32.GetFontResourceInfoW(filename, ctypes.byref(cb), None, GFRI_DESCRIPTION):\n buf = (ctypes.c_wchar * cb.value)()\n if gdi32.GetFontResourceInfoW(filename, ctypes.byref(cb), buf, GFRI_DESCRIPTION):\n fontname = buf.value\n is_truetype = wintypes.BOOL()\n cb.value = ctypes.sizeof(is_truetype)\n gdi32.GetFontResourceInfoW(\n filename, ctypes.byref(cb), ctypes.byref(is_truetype), GFRI_ISTRUETYPE\n )\n if is_truetype:\n fontname += ' (TrueType)'\n with winreg.OpenKey(registry_scope, FONTS_REG_PATH, 0, winreg.KEY_SET_VALUE) as key:\n winreg.SetValueEx(key, fontname, 0, winreg.REG_SZ, filename)\n except Exception:\n import traceback\n\n return False, traceback.format_exc()\n return True, ''\n\n\ndef uninstall_font(src_path, scope=INSTALL_SCOPE_USER):\n try:\n # copy the font to the Windows Fonts folder\n if scope == INSTALL_SCOPE_SYSTEM:\n dst_path = os.path.join(FONT_LOCATION_SYSTEM, os.path.basename(src_path))\n registry_scope = winreg.HKEY_LOCAL_MACHINE\n else:\n dst_path = os.path.join(FONT_LOCATION_USER, os.path.basename(src_path))\n registry_scope = winreg.HKEY_CURRENT_USER\n\n # remove the fontname/filename from the registry\n filename = os.path.basename(dst_path)\n fontname = os.path.splitext(filename)[0]\n # try to get the font's real name\n cb = wintypes.DWORD()\n if gdi32.GetFontResourceInfoW(filename, ctypes.byref(cb), None, GFRI_DESCRIPTION):\n buf = (ctypes.c_wchar * cb.value)()\n if gdi32.GetFontResourceInfoW(filename, ctypes.byref(cb), buf, GFRI_DESCRIPTION):\n fontname = buf.value\n is_truetype = wintypes.BOOL()\n cb.value = ctypes.sizeof(is_truetype)\n gdi32.GetFontResourceInfoW(\n filename, ctypes.byref(cb), ctypes.byref(is_truetype), GFRI_ISTRUETYPE\n )\n if is_truetype:\n fontname += ' (TrueType)'\n\n with winreg.OpenKey(registry_scope, FONTS_REG_PATH, 0, winreg.KEY_SET_VALUE) as key:\n winreg.DeleteValue(key, fontname)\n\n # unload the font in the current session\n if not gdi32.RemoveFontResourceW(dst_path):\n os.remove(dst_path)\n raise Exception('RemoveFontResourceW failed to load \\'%s\\'' % src_path)\n\n if os.path.exists(dst_path):\n os.remove(dst_path)\n\n # notify running programs\n user32.SendMessageTimeoutW(\n HWND_BROADCAST, WM_FONTCHANGE, 0, 0, SMTO_ABORTIFHUNG, 1000, None\n )\n except Exception:\n import traceback\n\n return False, traceback.format_exc()\n return True, ''\n\ndef _find_fonts(folder):\n fonts = set()\n for path in os.listdir(folder):\n print('path: ' + path)\n if path.startswith('assetroot-'):\n asset_dir = os.path.join(folder, path)\n print(' asset_dir: ' + asset_dir)\n for asset_path in os.listdir(asset_dir):\n print(' asset_path: ' + asset_path)\n _, ext = os.path.splitext(os.path.join(asset_dir, asset_path))\n if ext.upper() in FONT_EXTENSIONS:\n fonts.add(os.path.join(asset_dir, asset_path))\n return fonts\n\ndef _install_fonts(folder):\n fonts = _find_fonts(folder)\n installed_fonts = set()\n if not fonts:\n print('No fonts to install')\n return\n\n for font in fonts:\n print('Installing font: %s' % font)\n installed, msg = install_font(font)\n if not installed:\n print(' Error installing font: %s' % msg)\n else:\n installed_fonts.add(font)\n return installed_fonts\n\ndef _remove_fonts(folder):\n fonts = _find_fonts(folder)\n if not fonts:\n print('No fonts to uninstall')\n return\n\n for font in fonts:\n print('Uninstalling font: %s' % font)\n uninstalled, msg = uninstall_font(font)\n if not uninstalled:\n print(' Error uninstalling font: %s' % msg)\n\nif __name__ == '__main__':\n parser = argparse.ArgumentParser(\n prog='InstallFonts',\n description='Installs and uninstalls fonts for After Effects')\n\n group = parser.add_mutually_exclusive_group(required=True)\n group.add_argument('-i', '--install', metavar='FOLDER_PATH', help='Install font located in FOLDER_PATH')\n group.add_argument('-un', '--uninstall', metavar='FOLDER_PATH', help='Remove fonts located in FOLDER_PATH')\n\n args = parser.parse_args()\n if args.install:\n _install_fonts(args.install)\n elif args.uninstall:\n _remove_fonts(args.uninstall)\n else:\n raise RuntimeError('Argparse forces you to specify install or uninstall')\n \n print('Done running font installer')" } ], "actions": { "onEnter": { - "command": "powershell", + "command": "python", "args": [ - "{{Env.File.runStart}}" + "{{Env.File.font_installer}}", + "--install", + "{{Session.WorkingDirectory}}" ] }, "onExit": { - "command": "powershell", + "command": "python", "args": [ - "{{Env.File.runStop}}" + "{{Env.File.font_installer}}", + "--uninstall", + "{{Session.WorkingDirectory}}" ] } } @@ -126,25 +167,27 @@ var OPENJD_TEMPLATE = { "onRun": { "command": "powershell", "args": [ - "{{Task.File.runScript}}" + "{{Task.File.runAerender}}" ] } }, "embeddedFiles": [ { - "name": "runData", - "filename": "run-data.yaml", - "type": "TEXT", - "data": [ - "frame: {{Task.Param.Frame}}" - ] - }, - { - "name": "runScript", - "filename": "bootstrap.bat", + "name": "runAerender", + "filename": "aerender.bat", "type": "TEXT", "runnable": true, - "data": "afterfx-openjd daemon run --connection-file {{ Session.WorkingDirectory }}/connection.json --run-data file://{{Task.File.runData}} \n" + "data": "@ECHO OFF\n" + + "IF \"%AFTEREFFECTS_ADAPTOR_AERENDER_EXECUTABLE%\" == \"\" (\n" + + " set AFTEREFFECTS_ADAPTOR_AERENDER_EXECUTABLE=aerender.exe\n" + + ")\n" + + "\n" + + "\"%AFTEREFFECTS_ADAPTOR_AERENDER_EXECUTABLE%\" -project \"{{Param.AfterEffectsProjectFile}}\" -comp \"{{Task.Param.Comp}}\" -s {{Task.Param.FrameChunkStart}} -e {{Task.Param.FrameChunkEnd}}\n" + + "IF %ERRORLEVEL% NEQ 0 (\n" + + " echo \"Return code: %ERRORLEVEL%\"\n" + + " exit %ERRORLEVEL%\n" + + ")\n" + + "exit 0\n", } ] } diff --git a/src/deadline/ae_submitter/utils/Util.jsx b/src/deadline/ae_submitter/utils/Util.jsx index 1574389..19618f3 100644 --- a/src/deadline/ae_submitter/utils/Util.jsx +++ b/src/deadline/ae_submitter/utils/Util.jsx @@ -837,6 +837,40 @@ function __generateUtil() { return fileName; } + function getFrameChunkParameters(frameList, framesPerTask) { + var frameChunkParametersList = []; + var splitList = frameList.split(","); + + for(var i = 0; i < splitList.length; i++) { + if(splitList[i].indexOf("-") == -1) + { + // Single Frame + frameChunkParametersList.push({ + "originalRange": splitList[i] + "-" + splitList[i], + "frameStart": parseInt(splitList[i]), + "frameEnd": parseInt(splitList[i]), + "chunkSize": 1, + "frameStartPlusChunkSizeMinusOne": parseInt(frameStart) + 1 - 1, + "frameEndMinusOne": parseInt(splitList[i]) - 1, + }) + } else { + // Range + var numbers = splitList[i].split("-"); + // Either the framesPerTask we requested, or the smaller range if X-Y is less than framesPerTask + var chunkSize = Math.min(parseInt(numbers[1]) - parseInt(numbers[0]) + 1, framesPerTask) + frameChunkParametersList.push({ + "originalRange": splitList[i], + "frameStart": parseInt(numbers[0]), + "frameEnd": parseInt(numbers[1]), + "chunkSize": chunkSize, + "frameStartPlusChunkSizeMinusOne": parseInt(numbers[0]) + chunkSize - 1, + "frameEndMinusOne": parseInt(numbers[1]) - 1, + }) + } + } + return frameChunkParametersList + } + function getDuplicateFrames(frameList) { /** @@ -954,6 +988,7 @@ function __generateUtil() { "removeIllegalCharacters": removeIllegalCharacters, "removePercentageFromFileName": removePercentageFromFileName, "getDuplicateFrames": getDuplicateFrames, + "getFrameChunkParameters": getFrameChunkParameters, "getTempFile": getTempFile, "getUserDirectory": getUserDirectory }