From 4196460c29a607e1d3d2d052bf2bf546d5c05616 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Mon, 14 Mar 2022 18:18:10 +0100 Subject: [PATCH] Automatically include input blend file path in job settings --- FEATURES.md | 5 +- addon/flamenco/job_submission.py | 57 ++++++++++++++++++- addon/flamenco/job_types.py | 25 +------- addon/flamenco/operators.py | 23 +++++--- .../scripts/simple_blender_render.js | 6 +- 5 files changed, 77 insertions(+), 39 deletions(-) diff --git a/FEATURES.md b/FEATURES.md index 75a74a10..b716c606 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -58,9 +58,10 @@ Note that list is **not** in any specific order. ## Blender Add-on -- [ ] Communication with the Flamenco Manager API +- [x] Communication with the Flamenco Manager API - [ ] Job construction -- [ ] BAT-packing & uploading to Shaman +- [x] BAT-packing +- [ ] uploading to Shaman - [ ] User authentication - [ ] Editable job names (for rendering multiple jobs from the same filename) - [ ] Add `options={'HIDDEN'}` to the Flamenco RNA properties. diff --git a/addon/flamenco/job_submission.py b/addon/flamenco/job_submission.py index 74ebbdfc..806ed3cb 100644 --- a/addon/flamenco/job_submission.py +++ b/addon/flamenco/job_submission.py @@ -1,18 +1,73 @@ # SPDX-License-Identifier: GPL-3.0-or-later -from typing import TYPE_CHECKING +from pathlib import Path +from typing import TYPE_CHECKING, Optional, Union + +import bpy + +from .job_types_propgroup import JobTypePropertyGroup if TYPE_CHECKING: from .manager import ApiClient as _ApiClient from .manager.models import ( + AvailableJobType as _AvailableJobType, Job as _Job, SubmittedJob as _SubmittedJob, ) else: + _AvailableJobType = object _ApiClient = object _Job = object _SubmittedJob = object +# If a job has a setting with this key, type 'str', and subtype 'file_path', +# it'll be set to the path of the BAT-packed blend file. +BLENDFILE_SETTING_KEY = "blendfile" + + +def job_for_scene(scene: bpy.types.Scene) -> Optional[_SubmittedJob]: + from flamenco.manager.models import SubmittedJob, JobMetadata + + propgroup = getattr(scene, "flamenco_job_settings", None) + assert isinstance(propgroup, JobTypePropertyGroup), "did not expect %s" % ( + type(propgroup) + ) + + settings = propgroup.as_jobsettings() + metadata = JobMetadata() + + job: SubmittedJob = SubmittedJob( + name=scene.flamenco_job_name, + type=propgroup.job_type.name, + priority=50, + settings=settings, + metadata=metadata, + ) + return job + + +def set_blend_file( + job_type: _AvailableJobType, job: _SubmittedJob, blendfile: Union[str, Path] +) -> None: + """Update the job's 'blendfile' setting, if available. + + If a job has a 'blendfile' setting, type 'str', it'll be set to the path/URL + of the BAT-packed blend file. + """ + from .manager.models import AvailableJobSetting, AvailableJobSettingType + + expected_type = AvailableJobSettingType("string") + for setting in job_type.settings: + if setting.key == BLENDFILE_SETTING_KEY and setting.type == expected_type: + break + else: + # Not having this setting is fine. + return + + assert isinstance(setting, AvailableJobSetting) + job.settings[BLENDFILE_SETTING_KEY] = str(blendfile) + + def submit_job(job: _SubmittedJob, api_client: _ApiClient) -> _Job: """Send the given job to Flamenco Manager.""" from flamenco.manager import ApiClient diff --git a/addon/flamenco/job_types.py b/addon/flamenco/job_types.py index 29f7a677..a808324b 100644 --- a/addon/flamenco/job_types.py +++ b/addon/flamenco/job_types.py @@ -2,8 +2,7 @@ import json import logging -import uuid -from typing import TYPE_CHECKING, Callable, Optional, Union +from typing import TYPE_CHECKING, Optional, Union import bpy @@ -133,28 +132,6 @@ def update_job_type_properties(scene: bpy.types.Scene) -> None: ) -def job_for_scene(scene: bpy.types.Scene) -> Optional[_SubmittedJob]: - from flamenco.manager.models import SubmittedJob, JobMetadata - - propgroup = getattr(scene, "flamenco_job_settings", None) - assert isinstance( - propgroup, job_types_propgroup.JobTypePropertyGroup - ), "did not expect %s" % (type(propgroup)) - - settings = propgroup.as_jobsettings() - metadata = JobMetadata() - - job: SubmittedJob = SubmittedJob( - name=scene.flamenco_job_name, - type=propgroup.job_type.name, - priority=50, - id=str(uuid.uuid4()), - settings=settings, - metadata=metadata, - ) - return job - - def _clear_available_job_types(scene: bpy.types.Scene) -> None: global _available_job_types global _job_type_enum_items diff --git a/addon/flamenco/operators.py b/addon/flamenco/operators.py index 66ee9668..dd14ab98 100644 --- a/addon/flamenco/operators.py +++ b/addon/flamenco/operators.py @@ -7,7 +7,7 @@ from typing import Optional, TYPE_CHECKING import bpy -from . import job_submission +from . import job_types, job_submission if TYPE_CHECKING: from .bat_interface import ( @@ -111,6 +111,7 @@ class FLAMENCO_OT_submit_job(FlamencoOpMixin, bpy.types.Operator): bl_description = "Pack the current blend file and send it to Flamenco" bl_options = {"REGISTER"} # No UNDO. + blendfile_on_farm: Optional[Path] = None job_name: bpy.props.StringProperty(name="Job Name") # type: ignore job: Optional[_SubmittedJob] = None @@ -123,9 +124,7 @@ class FLAMENCO_OT_submit_job(FlamencoOpMixin, bpy.types.Operator): filepath = self._save_blendfile(context) # Construct the Job locally before trying to pack. If any validations fail, better fail early. - from . import job_types - - self.job = job_types.job_for_scene(context.scene) + self.job = job_submission.job_for_scene(context.scene) if self.job is None: self.report({"ERROR"}, "Unable to create job") return {"CANCELLED"} @@ -202,13 +201,17 @@ class FLAMENCO_OT_submit_job(FlamencoOpMixin, bpy.types.Operator): return {"CANCELLED"} # Determine where the render output will be stored. - render_output = Path("/render/_flamenco/tests/renders") / self.job_name - self.log.info("Will output render files to %s", render_output) + pack_target_dir = Path("/render/_flamenco/tests/renders") / self.job_name + + # TODO: this should take the blendfile location relative to the project path into account. + pack_target_file = pack_target_dir / blendfile.name + self.log.info("Will store blend file at %s", pack_target_file) + self.blendfile_on_farm = pack_target_file self.packthread = bat_interface.copy( base_blendfile=blendfile, project=project_path, - target=render_output, + target=pack_target_dir, exclusion_filter="", # TODO: get from GUI. relative_only=True, # TODO: get from GUI. ) @@ -223,7 +226,6 @@ class FLAMENCO_OT_submit_job(FlamencoOpMixin, bpy.types.Operator): from . import bat_interface if isinstance(msg, bat_interface.MsgDone): - self.report({"INFO"}, "BAT pack is done") self._submit_job(context) return self._quit(context) @@ -245,10 +247,13 @@ class FLAMENCO_OT_submit_job(FlamencoOpMixin, bpy.types.Operator): """Use the Flamenco API to submit the new Job.""" assert self.job is not None + job_type = job_types.active_job_type(context.scene) + job_submission.set_blend_file(job_type, self.job, self.blendfile_on_farm) + api_client = self.get_api_client(context) job = job_submission.submit_job(self.job, api_client) - self.report({"INFO"}, "Job submitted") + self.report({"INFO"}, "Job %s submitted" % job.name) def _quit(self, context: bpy.types.Context) -> set[str]: """Stop any timer and return a 'FINISHED' status. diff --git a/internal/manager/job_compilers/scripts/simple_blender_render.js b/internal/manager/job_compilers/scripts/simple_blender_render.js index f9325d7d..21299b04 100644 --- a/internal/manager/job_compilers/scripts/simple_blender_render.js +++ b/internal/manager/job_compilers/scripts/simple_blender_render.js @@ -4,7 +4,7 @@ const JOB_TYPE = { label: "Simple Blender Render", settings: [ { key: "blender_cmd", type: "string", default: "{blender}", visible: false }, - { key: "filepath", type: "string", subtype: "file_path", required: true, description: "Blend file to render", visible: false }, + { key: "blendfile", type: "string", required: true, description: "Path of the Blend file to render", visible: false }, { key: "chunk_size", type: "int32", default: 1, description: "Number of frames to render in one Blender render task" }, { key: "frames", type: "string", required: true }, { key: "render_output", type: "string", subtype: "hashed_file_path", required: true }, @@ -76,7 +76,7 @@ function authorRenderTasks(settings, renderDir, renderOutput) { const command = author.Command("blender-render", { exe: settings.blender_cmd, argsBefore: [], - blendfile: settings.filepath, + blendfile: settings.blendfile, args: [ "--render-output", path.join(renderDir, path.basename(renderOutput)), "--render-format", settings.format, @@ -99,7 +99,7 @@ function authorCreateVideoTask(settings, renderDir) { return; } - const stem = path.stem(settings.filepath).replace('.flamenco', ''); + const stem = path.stem(settings.blendfile).replace('.flamenco', ''); const outfile = path.join(renderDir, `${stem}-${settings.frames}.mp4`); const task = author.Task('create-video', 'ffmpeg');