From 09a476e11a481f221376d660911e3afc30d624d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Tue, 15 Mar 2022 16:56:44 +0100 Subject: [PATCH] Compute render output path when evaluating job settings Compute render output path when evaluating job settings, which is done within the Flamenco add-on, instead of in the job compiler script. This allows the UI to show the render path, rather than it only being known after the job has been submitted. --- addon/flamenco/gui.py | 82 ++++++++++-- addon/flamenco/job_submission.py | 4 +- addon/flamenco/job_types_propgroup.py | 125 +++++++++++++++++- addon/flamenco/manager/__init__.py | 2 +- addon/flamenco/manager/api_client.py | 2 +- addon/flamenco/manager/configuration.py | 2 +- .../manager/docs/AvailableJobSetting.md | 1 + .../manager/model/available_job_setting.py | 4 + addon/flamenco/manager_README.md | 2 +- addon/flamenco/operators.py | 11 +- .../job_compilers/job_compilers_test.go | 6 +- .../scripts/simple_blender_render.js | 29 ++-- pkg/api/flamenco-manager.yaml | 3 + pkg/api/openapi_spec.gen.go | 119 ++++++++--------- pkg/api/openapi_types.gen.go | 3 + 15 files changed, 287 insertions(+), 108 deletions(-) diff --git a/addon/flamenco/gui.py b/addon/flamenco/gui.py index de9e180f..93dbe2b6 100644 --- a/addon/flamenco/gui.py +++ b/addon/flamenco/gui.py @@ -1,8 +1,22 @@ # SPDX-License-Identifier: GPL-3.0-or-later - # + +from typing import Optional, TYPE_CHECKING + +from flamenco import job_submission +from flamenco.job_types_propgroup import JobTypePropertyGroup + import bpy +if TYPE_CHECKING: + from flamenco.manager.models import ( + AvailableJobSetting as _AvailableJobSetting, + SubmittedJob as _SubmittedJob, + ) +else: + _AvailableJobSetting = object + _SubmittedJob = object + class FLAMENCO_PT_job_submission(bpy.types.Panel): bl_space_type = "PROPERTIES" @@ -10,6 +24,10 @@ class FLAMENCO_PT_job_submission(bpy.types.Panel): bl_context = "output" bl_label = "Flamenco 3" + # A temporary job can be constructed so that dynamic, read-only properties can be evaluated. + # This is only scoped to a single draw() call. + job: Optional[_SubmittedJob] = None + def draw(self, context: bpy.types.Context) -> None: from . import job_types @@ -24,14 +42,20 @@ class FLAMENCO_PT_job_submission(bpy.types.Panel): row.prop(context.scene, "flamenco_job_type", text="") row.operator("flamenco.fetch_job_types", text="", icon="FILE_REFRESH") + layout.separator() + + col = layout.column(align=True) + col.prop(context.scene, "flamenco_job_name", text="Job Name") + layout.separator() + self.draw_job_settings(context, layout.column(align=True)) layout.separator() - col = layout.column(align=True) - col.prop(context.scene, "flamenco_job_name", text="Job Name") self.draw_flamenco_status(context, layout) + self.job = None + def draw_job_settings( self, context: bpy.types.Context, layout: bpy.types.UILayout ) -> None: @@ -48,17 +72,47 @@ class FLAMENCO_PT_job_submission(bpy.types.Panel): layout.label(text="Job Settings:") layout.use_property_split = True for setting in job_type.settings: - if not setting.get("visible", True): - continue - row = layout.row(align=True) - row.prop(propgroup, setting.key) - setting_eval = setting.get("eval", "") - if setting_eval: - props = row.operator( - "flamenco.eval_setting", text="", icon="SCRIPTPLUGINS" - ) - props.setting_key = setting.key - props.setting_eval = setting_eval + self.draw_setting(context, layout, propgroup, setting) + + def draw_setting( + self, + context: bpy.types.Context, + layout: bpy.types.UILayout, + propgroup: JobTypePropertyGroup, + setting: _AvailableJobSetting, + ) -> None: + if not setting.get("visible", True): + return + + if setting.get("editable", True): + self.draw_setting_editable(layout, propgroup, setting) + else: + self.draw_setting_readonly(context, layout, propgroup, setting) + + def draw_setting_editable( + self, + layout: bpy.types.UILayout, + propgroup: JobTypePropertyGroup, + setting: _AvailableJobSetting, + ) -> None: + row = layout.row(align=True) + row.prop(propgroup, setting.key) + setting_eval = setting.get("eval", "") + if not setting_eval: + return + + props = row.operator("flamenco.eval_setting", text="", icon="SCRIPTPLUGINS") + props.setting_key = setting.key + props.setting_eval = setting_eval + + def draw_setting_readonly( + self, + context: bpy.types.Context, + layout: bpy.types.UILayout, + propgroup: JobTypePropertyGroup, + setting: _AvailableJobSetting, + ) -> None: + layout.prop(propgroup, setting.key) def draw_flamenco_status( self, context: bpy.types.Context, layout: bpy.types.UILayout diff --git a/addon/flamenco/job_submission.py b/addon/flamenco/job_submission.py index 298e00d5..8e6d77e4 100644 --- a/addon/flamenco/job_submission.py +++ b/addon/flamenco/job_submission.py @@ -83,7 +83,9 @@ def eval_hidden_settings( setting_eval = setting.get("eval", "") if setting_eval: - value = JobTypePropertyGroup.eval_setting(context, setting_eval) + value = JobTypePropertyGroup.eval_setting( + context, job, setting.key, setting_eval + ) elif "default" in setting: value = setting.default else: diff --git a/addon/flamenco/job_types_propgroup.py b/addon/flamenco/job_types_propgroup.py index 656ffdd2..c9986f80 100644 --- a/addon/flamenco/job_types_propgroup.py +++ b/addon/flamenco/job_types_propgroup.py @@ -3,7 +3,9 @@ # SPDX-License-Identifier: GPL-3.0-or-later import logging -from typing import TYPE_CHECKING, Callable, Optional, Any +import pprint +from pathlib import Path +from typing import TYPE_CHECKING, Callable, Optional, Any, Union import bpy @@ -14,11 +16,38 @@ if TYPE_CHECKING: AvailableJobType as _AvailableJobType, AvailableJobSetting as _AvailableJobSetting, JobSettings as _JobSettings, + SubmittedJob as _SubmittedJob, ) else: _AvailableJobType = object _AvailableJobSetting = object _JobSettings = object + _SubmittedJob = object + + +class SettingEvalError(Exception): + """Raised when the evaluation of a setting fails.""" + + def __init__( + self, + setting_key: str, + setting_eval: str, + eval_locals: dict[str, Any], + exception: Exception, + ) -> None: + self.setting_key = setting_key + self.setting_eval = setting_eval + self.locals = eval_locals.copy() + self.exception = exception + + print("Evaluation error of setting %r:" % setting_key) + print("Expression: %s" % setting_eval) + print("Local variables:") + pprint.pprint(eval_locals) + print("Exception: %s" % exception) + + msg = "Evaluation error of setting %r: %s" % (setting_key, exception) + super().__init__(msg) class JobTypePropertyGroup: @@ -47,24 +76,74 @@ class JobTypePropertyGroup: return js + def label(self, setting_key: str) -> str: + """Return the UI label for this setting.""" + return self.bl_rna.properties[setting_key].name + def eval_and_assign( - self, context: bpy.types.Context, setting_key: str, setting_eval: str + self, + context: bpy.types.Context, + job: _SubmittedJob, + setting_key: str, + setting_eval: str, ) -> None: """Evaluate `setting_eval` and assign the result to the job setting.""" - value = self.eval_setting(context, setting_eval) + value = self.eval_setting(context, setting_key, setting_eval) setattr(self, setting_key, value) - @staticmethod - def eval_setting(context: bpy.types.Context, setting_eval: str) -> Any: + def eval_setting( + self, + context: bpy.types.Context, + setting_key: str, + setting_eval: str, + ) -> Any: """Evaluate `setting_eval` and return the result.""" - eval_globals = { + eval_locals = { "bpy": bpy, "C": context, + "jobname": context.scene.flamenco_job_name, + "Path": Path, + "last_n_dir_parts": self.last_n_dir_parts, + "settings": self, } - value = eval(setting_eval, eval_globals, {}) + try: + value = eval(setting_eval, {}, eval_locals) + except Exception as ex: + raise SettingEvalError(setting_key, setting_eval, eval_locals, ex) from ex return value + @staticmethod + def last_n_dir_parts(n: int, filepath: Union[str, Path, None] = None) -> Path: + """Return the last `n` parts of the directory of `filepath`. + + If `n` is 0, returns an empty `Path()`. + If `filepath` is None, uses bpy.data.filepath instead. + + >>> str(lastNDirParts(2, "/path/to/some/file.blend")) + "to/some" + + Always returns a relative path: + >>> str(lastNDirParts(200, "C:\\path\\to\\some\\file.blend")) + "path\\to\\some" + """ + + if n <= 0: + return Path() + + if filepath is None: + filepath = Path(bpy.data.filepath) + elif isinstance(filepath, str): + filepath = Path(filepath) + + dirpath = filepath.parent + if n >= len(dirpath.parts): + all_parts = dirpath.relative_to(dirpath.anchor) + return all_parts + + subset = Path(*dirpath.parts[-n:]) + return subset + # Mapping from AvailableJobType.setting.type to a callable that converts a value # to the appropriate type. This is necessary due to the ambiguity between floats @@ -137,6 +216,19 @@ def _create_property(job_type: _AvailableJobType, setting: _AvailableJobSetting) # Remove the 'ANIMATABLE' option. prop_kwargs.setdefault("options", set()) + # Add any extra arguments. + propargs = setting.get("propargs", {}) + coerce_keys = {"min", "max"} + for key, value in propargs.items(): + if key in coerce_keys: + propargs[key] = value_coerce(value) + prop_kwargs.update(propargs) + + # Construct a getter if it's a non-editable property. By having a getter and + # not a setter, the property automatically becomes read-only in the UI. + if not setting.get("editable", True): + prop_kwargs["get"] = _create_prop_getter(job_type, setting) + prop_name = _job_setting_key_to_label(setting.key) prop = prop_type(name=prop_name, **prop_kwargs) return prop @@ -229,3 +321,22 @@ def _set_if_available( some_dict[key] = value else: some_dict[key] = transform(value) + + +def _create_prop_getter( + job_type: _AvailableJobType, setting: _AvailableJobSetting +) -> Callable[[JobTypePropertyGroup], Any]: + def evaluate_setting(propgroup: JobTypePropertyGroup) -> Any: + value = propgroup.eval_setting( + bpy.context, + setting.key, + setting.eval, + ) + return value + + def default_value(propgroup: JobTypePropertyGroup) -> Any: + return setting.default + + if setting.get("eval"): + return evaluate_setting + return default_value diff --git a/addon/flamenco/manager/__init__.py b/addon/flamenco/manager/__init__.py index 50e98009..f108c44b 100644 --- a/addon/flamenco/manager/__init__.py +++ b/addon/flamenco/manager/__init__.py @@ -10,7 +10,7 @@ """ -__version__ = "4196460c-dirty" +__version__ = "7bfde1df-dirty" # import ApiClient from flamenco.manager.api_client import ApiClient diff --git a/addon/flamenco/manager/api_client.py b/addon/flamenco/manager/api_client.py index 8240e09e..077c81e6 100644 --- a/addon/flamenco/manager/api_client.py +++ b/addon/flamenco/manager/api_client.py @@ -76,7 +76,7 @@ class ApiClient(object): self.default_headers[header_name] = header_value self.cookie = cookie # Set default User-Agent. - self.user_agent = 'Flamenco/4196460c-dirty (Blender add-on)' + self.user_agent = 'Flamenco/7bfde1df-dirty (Blender add-on)' def __enter__(self): return self diff --git a/addon/flamenco/manager/configuration.py b/addon/flamenco/manager/configuration.py index 58a76d2b..bd7ee737 100644 --- a/addon/flamenco/manager/configuration.py +++ b/addon/flamenco/manager/configuration.py @@ -404,7 +404,7 @@ conf = flamenco.manager.Configuration( "OS: {env}\n"\ "Python Version: {pyversion}\n"\ "Version of the API: 1.0.0\n"\ - "SDK Package Version: 4196460c-dirty".\ + "SDK Package Version: 7bfde1df-dirty".\ format(env=sys.platform, pyversion=sys.version) def get_host_settings(self): diff --git a/addon/flamenco/manager/docs/AvailableJobSetting.md b/addon/flamenco/manager/docs/AvailableJobSetting.md index 86cd669d..5f74c901 100644 --- a/addon/flamenco/manager/docs/AvailableJobSetting.md +++ b/addon/flamenco/manager/docs/AvailableJobSetting.md @@ -9,6 +9,7 @@ Name | Type | Description | Notes **type** | [**AvailableJobSettingType**](AvailableJobSettingType.md) | | **subtype** | [**AvailableJobSettingSubtype**](AvailableJobSettingSubtype.md) | | [optional] **choices** | **[str]** | When given, limit the valid values to these choices. Only usable with string type. | [optional] +**propargs** | **{str: (bool, date, datetime, dict, float, int, list, str, none_type)}** | Any extra arguments to the bpy.props.SomeProperty() call used to create this property. | [optional] **description** | **bool, date, datetime, dict, float, int, list, str, none_type** | The description/tooltip shown in the user interface. | [optional] **default** | **bool, date, datetime, dict, float, int, list, str, none_type** | The default value shown to the user when determining this setting. | [optional] **eval** | **str** | Python expression to be evaluated in order to determine the default value for this setting. | [optional] diff --git a/addon/flamenco/manager/model/available_job_setting.py b/addon/flamenco/manager/model/available_job_setting.py index d99d7bee..3971be98 100644 --- a/addon/flamenco/manager/model/available_job_setting.py +++ b/addon/flamenco/manager/model/available_job_setting.py @@ -93,6 +93,7 @@ class AvailableJobSetting(ModelNormal): 'type': (AvailableJobSettingType,), # noqa: E501 'subtype': (AvailableJobSettingSubtype,), # noqa: E501 'choices': ([str],), # noqa: E501 + 'propargs': ({str: (bool, date, datetime, dict, float, int, list, str, none_type)},), # noqa: E501 'description': (bool, date, datetime, dict, float, int, list, str, none_type,), # noqa: E501 'default': (bool, date, datetime, dict, float, int, list, str, none_type,), # noqa: E501 'eval': (str,), # noqa: E501 @@ -111,6 +112,7 @@ class AvailableJobSetting(ModelNormal): 'type': 'type', # noqa: E501 'subtype': 'subtype', # noqa: E501 'choices': 'choices', # noqa: E501 + 'propargs': 'propargs', # noqa: E501 'description': 'description', # noqa: E501 'default': 'default', # noqa: E501 'eval': 'eval', # noqa: E501 @@ -166,6 +168,7 @@ class AvailableJobSetting(ModelNormal): _visited_composed_classes = (Animal,) subtype (AvailableJobSettingSubtype): [optional] # noqa: E501 choices ([str]): When given, limit the valid values to these choices. Only usable with string type.. [optional] # noqa: E501 + propargs ({str: (bool, date, datetime, dict, float, int, list, str, none_type)}): Any extra arguments to the bpy.props.SomeProperty() call used to create this property.. [optional] # noqa: E501 description (bool, date, datetime, dict, float, int, list, str, none_type): The description/tooltip shown in the user interface.. [optional] # noqa: E501 default (bool, date, datetime, dict, float, int, list, str, none_type): The default value shown to the user when determining this setting.. [optional] # noqa: E501 eval (str): Python expression to be evaluated in order to determine the default value for this setting.. [optional] # noqa: E501 @@ -261,6 +264,7 @@ class AvailableJobSetting(ModelNormal): _visited_composed_classes = (Animal,) subtype (AvailableJobSettingSubtype): [optional] # noqa: E501 choices ([str]): When given, limit the valid values to these choices. Only usable with string type.. [optional] # noqa: E501 + propargs ({str: (bool, date, datetime, dict, float, int, list, str, none_type)}): Any extra arguments to the bpy.props.SomeProperty() call used to create this property.. [optional] # noqa: E501 description (bool, date, datetime, dict, float, int, list, str, none_type): The description/tooltip shown in the user interface.. [optional] # noqa: E501 default (bool, date, datetime, dict, float, int, list, str, none_type): The default value shown to the user when determining this setting.. [optional] # noqa: E501 eval (str): Python expression to be evaluated in order to determine the default value for this setting.. [optional] # noqa: E501 diff --git a/addon/flamenco/manager_README.md b/addon/flamenco/manager_README.md index ea5ea2f1..9b58ade4 100644 --- a/addon/flamenco/manager_README.md +++ b/addon/flamenco/manager_README.md @@ -4,7 +4,7 @@ Render Farm manager API The `flamenco.manager` package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project: - API version: 1.0.0 -- Package version: 4196460c-dirty +- Package version: 7bfde1df-dirty - Build package: org.openapitools.codegen.languages.PythonClientCodegen For more information, please visit [https://flamenco.io/](https://flamenco.io/) diff --git a/addon/flamenco/operators.py b/addon/flamenco/operators.py index b0f24944..1440ec1d 100644 --- a/addon/flamenco/operators.py +++ b/addon/flamenco/operators.py @@ -5,6 +5,8 @@ import logging from pathlib import Path from typing import Optional, TYPE_CHECKING +from flamenco.job_types_propgroup import JobTypePropertyGroup + import bpy from . import job_types, job_submission @@ -115,8 +117,13 @@ class FLAMENCO_OT_eval_setting(FlamencoOpMixin, bpy.types.Operator): setting_eval: bpy.props.StringProperty(name="Python Expression") # type: ignore def execute(self, context: bpy.types.Context) -> set[str]: - propgroup = context.scene.flamenco_job_settings - propgroup.eval_and_assign(context, self.setting_key, self.setting_eval) + job = job_submission.job_for_scene(context.scene) + if job is None: + self.report({"ERROR"}, "This Scene has no Flamenco job") + return {"CANCELLED"} + + propgroup: JobTypePropertyGroup = context.scene.flamenco_job_settings + propgroup.eval_and_assign(context, job, self.setting_key, self.setting_eval) return {"FINISHED"} diff --git a/internal/manager/job_compilers/job_compilers_test.go b/internal/manager/job_compilers/job_compilers_test.go index 7cea0c94..ace90cca 100644 --- a/internal/manager/job_compilers/job_compilers_test.go +++ b/internal/manager/job_compilers/job_compilers_test.go @@ -28,7 +28,9 @@ func exampleSubmittedJob() api.SubmittedJob { "images_or_video": "images", "image_file_extension": ".png", "video_container_format": "", - "render_output": "/render/sprites/farm_output/promo/square_ellie/square_ellie.lighting_light_breakdown2/######", + "render_output_root": "/render/sprites/farm_output/promo/square_ellie", + "add_path_components": 1, + "render_output_path": "/render/sprites/farm_output/promo/square_ellie/square_ellie.lighting_light_breakdown2/######", }} metadata := api.JobMetadata{ AdditionalProperties: map[string]string{ @@ -144,7 +146,7 @@ func TestSimpleBlenderRenderWindowsPaths(t *testing.T) { // Adjust the job to get paths in Windows notation. sj.Settings.AdditionalProperties["blendfile"] = "R:\\sf\\jobs\\scene123.blend" - sj.Settings.AdditionalProperties["render_output"] = "R:\\sprites\\farm_output\\promo\\square_ellie\\square_ellie.lighting_light_breakdown2\\######" + sj.Settings.AdditionalProperties["render_output_path"] = "R:\\sprites\\farm_output\\promo\\square_ellie\\square_ellie.lighting_light_breakdown2\\######" aj, err := s.Compile(ctx, sj) if err != nil { diff --git a/internal/manager/job_compilers/scripts/simple_blender_render.js b/internal/manager/job_compilers/scripts/simple_blender_render.js index 97b0c707..71869ed7 100644 --- a/internal/manager/job_compilers/scripts/simple_blender_render.js +++ b/internal/manager/job_compilers/scripts/simple_blender_render.js @@ -7,14 +7,19 @@ const JOB_TYPE = { { key: "chunk_size", type: "int32", default: 1, description: "Number of frames to render in one Blender render task" }, { key: "frames", type: "string", required: true, eval: "f'{C.scene.frame_start}-{C.scene.frame_end}'", description: "Frame range to render. Examples: '47', '1-30', '3, 5-10, 47-327'" }, + + // render_output_root + add_path_components determine the value of render_output_path. { key: "render_output_root", type: "string", subtype: "dir_path", required: true, description: "Base directory of where render output is stored. Will have some job-specific parts appended to it"}, + { key: "add_path_components", type: "int32", required: true, default: 0, propargs: {min: 0, max: 32}, + description: "Number of path components of the current blend file to use in the render output path"}, + { key: "render_output_path", type: "string", subtype: "file_path", editable: false, + eval: "str(Path(settings.render_output_root) / last_n_dir_parts(settings.add_path_components) / jobname / '{timestamp}' / '######.{ext}')", + description: "Final file path of where render output will be saved"}, // Automatically evaluated settings: { key: "blender_cmd", type: "string", default: "{blender}", visible: false }, { key: "blendfile", type: "string", required: true, description: "Path of the Blend file to render", visible: false }, - { key: "render_output_path", type: "string", subtype: "file_path", visible: false, - description: "Final file path of where render output is stored, set by the job compiler"}, { key: "fps", type: "float", eval: "C.scene.render.fps / C.scene.render.fps_base", visible: false }, { key: "images_or_video", @@ -55,15 +60,14 @@ const videoContainerToExtension = { function compileJob(job) { print("Blender Render job submitted"); - - const renderOutput = renderOutputPath(job); - job.settings.render_output_path = renderOutput; print("job: ", job); + const settings = job.settings; + + const renderOutput = settings.render_output_path; const finalDir = path.dirname(renderOutput); const renderDir = intermediatePath(job, finalDir); - const settings = job.settings; const renderTasks = authorRenderTasks(settings, renderDir, renderOutput); const videoTask = authorCreateVideoTask(settings, renderDir); @@ -79,19 +83,6 @@ function compileJob(job) { } } -// Return the intended render output path. -function renderOutputPath(job) { - // {DIR}/{job name}/{date and time of job submission}/######.{extension} - const pathSafeJobName = job.name.replace(/[/\\:?*]/, "-"); - const extension = guessOutputFileExtension(job.settings); - return path.join( - job.settings.render_output_root, - pathSafeJobName, - formatTimestampLocal(job.created), - `######${extension}` - ); -} - // Determine the intermediate render output path. function intermediatePath(job, finalDir) { const basename = path.basename(finalDir); diff --git a/pkg/api/flamenco-manager.yaml b/pkg/api/flamenco-manager.yaml index 9f8693de..4837fefa 100644 --- a/pkg/api/flamenco-manager.yaml +++ b/pkg/api/flamenco-manager.yaml @@ -403,6 +403,9 @@ components: description: When given, limit the valid values to these choices. Only usable with string type. type: array items: {type: string} + "propargs": + description: Any extra arguments to the bpy.props.SomeProperty() call used to create this property. + type: object "description": description: The description/tooltip shown in the user interface. "default": diff --git a/pkg/api/openapi_spec.gen.go b/pkg/api/openapi_spec.gen.go index 780344b1..aac60025 100644 --- a/pkg/api/openapi_spec.gen.go +++ b/pkg/api/openapi_spec.gen.go @@ -18,65 +18,66 @@ import ( // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", + "+BN0ZoqB6J3hvW2vTq/+PwAA///UdlNGQjgAAA==", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/pkg/api/openapi_types.gen.go b/pkg/api/openapi_types.gen.go index c480b40b..1527a982 100644 --- a/pkg/api/openapi_types.gen.go +++ b/pkg/api/openapi_types.gen.go @@ -135,6 +135,9 @@ type AvailableJobSetting struct { // Identifier for the setting, must be unique within the job type. Key string `json:"key"` + // Any extra arguments to the bpy.props.SomeProperty() call used to create this property. + Propargs *map[string]interface{} `json:"propargs,omitempty"` + // Whether to immediately reject a job definition, of this type, without this particular setting. Required *bool `json:"required,omitempty"`