Addon: bundle BAT and start of interfacing with it
The add-on can now create BAT packs, but still only at a hard-coded path.
This commit is contained in:
parent
d18f5d25c5
commit
a803edcce4
@ -35,6 +35,12 @@ def discard_global_flamenco_data(_):
|
|||||||
comms.discard_flamenco_data()
|
comms.discard_flamenco_data()
|
||||||
|
|
||||||
|
|
||||||
|
def redraw(self, context):
|
||||||
|
if context.area is None:
|
||||||
|
return
|
||||||
|
context.area.tag_redraw()
|
||||||
|
|
||||||
|
|
||||||
def register() -> None:
|
def register() -> None:
|
||||||
from . import dependencies
|
from . import dependencies
|
||||||
|
|
||||||
@ -43,6 +49,46 @@ def register() -> None:
|
|||||||
bpy.app.handlers.load_pre.append(discard_global_flamenco_data)
|
bpy.app.handlers.load_pre.append(discard_global_flamenco_data)
|
||||||
bpy.app.handlers.load_factory_preferences_post.append(discard_global_flamenco_data)
|
bpy.app.handlers.load_factory_preferences_post.append(discard_global_flamenco_data)
|
||||||
|
|
||||||
|
bpy.types.WindowManager.flamenco_bat_status = bpy.props.EnumProperty(
|
||||||
|
items=[
|
||||||
|
("IDLE", "IDLE", "Not doing anything."),
|
||||||
|
("SAVING", "SAVING", "Saving your file."),
|
||||||
|
("INVESTIGATING", "INVESTIGATING", "Finding all dependencies."),
|
||||||
|
("TRANSFERRING", "TRANSFERRING", "Transferring all dependencies."),
|
||||||
|
("COMMUNICATING", "COMMUNICATING", "Communicating with Flamenco Server."),
|
||||||
|
("DONE", "DONE", "Not doing anything, but doing something earlier."),
|
||||||
|
("ABORTING", "ABORTING", "User requested we stop doing something."),
|
||||||
|
("ABORTED", "ABORTED", "We stopped doing something."),
|
||||||
|
],
|
||||||
|
name="flamenco_status",
|
||||||
|
default="IDLE",
|
||||||
|
description="Current status of the Flamenco add-on",
|
||||||
|
update=redraw,
|
||||||
|
)
|
||||||
|
|
||||||
|
bpy.types.WindowManager.flamenco_bat_status_txt = bpy.props.StringProperty(
|
||||||
|
name="Flamenco Status",
|
||||||
|
default="",
|
||||||
|
description="Textual description of what Flamenco is doing",
|
||||||
|
update=redraw,
|
||||||
|
)
|
||||||
|
|
||||||
|
bpy.types.WindowManager.flamenco_bat_progress = bpy.props.IntProperty(
|
||||||
|
name="Flamenco Progress",
|
||||||
|
default=0,
|
||||||
|
description="File transfer progress",
|
||||||
|
subtype="PERCENTAGE",
|
||||||
|
min=0,
|
||||||
|
max=100,
|
||||||
|
update=redraw,
|
||||||
|
)
|
||||||
|
|
||||||
|
bpy.types.Scene.flamenco_job_name = bpy.props.StringProperty(
|
||||||
|
name="Flamenco Job Name",
|
||||||
|
default="",
|
||||||
|
description="Name of the Flamenco job; an empty name will use the blend file name as job name",
|
||||||
|
)
|
||||||
|
|
||||||
# Placeholder to contain the result of a 'ping' to Flamenco Manager,
|
# Placeholder to contain the result of a 'ping' to Flamenco Manager,
|
||||||
# so that it can be shown in the preferences panel.
|
# so that it can be shown in the preferences panel.
|
||||||
bpy.types.WindowManager.flamenco_status_ping = bpy.props.StringProperty()
|
bpy.types.WindowManager.flamenco_status_ping = bpy.props.StringProperty()
|
||||||
|
283
addon/flamenco/bat_interface.py
Normal file
283
addon/flamenco/bat_interface.py
Normal file
@ -0,0 +1,283 @@
|
|||||||
|
"""BAT packing interface for Flamenco."""
|
||||||
|
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Optional
|
||||||
|
import logging
|
||||||
|
import queue
|
||||||
|
import threading
|
||||||
|
import typing
|
||||||
|
|
||||||
|
import bpy
|
||||||
|
|
||||||
|
from . import wheels
|
||||||
|
|
||||||
|
pack = wheels.load_wheel("blender_asset_tracer.pack")
|
||||||
|
progress = wheels.load_wheel("blender_asset_tracer.pack.progress")
|
||||||
|
transfer = wheels.load_wheel("blender_asset_tracer.pack.transfer")
|
||||||
|
shaman = wheels.load_wheel("blender_asset_tracer.pack.shaman")
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# For using in other parts of the add-on, so only this file imports BAT.
|
||||||
|
Aborted = pack.Aborted
|
||||||
|
FileTransferError = transfer.FileTransferError
|
||||||
|
parse_shaman_endpoint = shaman.parse_endpoint
|
||||||
|
|
||||||
|
|
||||||
|
class Message:
|
||||||
|
"""Superclass for message objects queued by the BatProgress class."""
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class MsgSetWMAttribute(Message):
|
||||||
|
"""Set a WindowManager attribute to a value."""
|
||||||
|
|
||||||
|
attribute_name: str
|
||||||
|
value: object
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class MsgException(Message):
|
||||||
|
"""Report an exception."""
|
||||||
|
|
||||||
|
ex: BaseException
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class MsgProgress(Message):
|
||||||
|
"""Report packing progress."""
|
||||||
|
|
||||||
|
percentage: int # 0 - 100
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class MsgDone(Message):
|
||||||
|
output_path: Path
|
||||||
|
missing_files: list[Path]
|
||||||
|
|
||||||
|
|
||||||
|
class BatProgress(progress.Callback):
|
||||||
|
"""Report progress of BAT Packing to the given queue."""
|
||||||
|
|
||||||
|
def __init__(self, queue: queue.Queue[Message]) -> None:
|
||||||
|
super().__init__()
|
||||||
|
self.queue = queue
|
||||||
|
|
||||||
|
def _set_attr(self, attr: str, value):
|
||||||
|
msg = MsgSetWMAttribute(attr, value)
|
||||||
|
self.queue.put(msg)
|
||||||
|
|
||||||
|
def _txt(self, msg: str):
|
||||||
|
"""Set a text in a thread-safe way."""
|
||||||
|
self._set_attr("flamenco_bat_status_txt", msg)
|
||||||
|
|
||||||
|
def _status(self, status: str):
|
||||||
|
"""Set the flamenco_bat_status property in a thread-safe way."""
|
||||||
|
self._set_attr("flamenco_bat_status", status)
|
||||||
|
|
||||||
|
def _progress(self, progress: int):
|
||||||
|
"""Set the flamenco_bat_progress property in a thread-safe way."""
|
||||||
|
self._set_attr("flamenco_bat_progress", progress)
|
||||||
|
msg = MsgProgress(percentage=progress)
|
||||||
|
self.queue.put(msg)
|
||||||
|
|
||||||
|
def pack_start(self) -> None:
|
||||||
|
self._txt("Starting BAT Pack operation")
|
||||||
|
|
||||||
|
def pack_done(
|
||||||
|
self, output_blendfile: Path, missing_files: typing.Set[Path]
|
||||||
|
) -> None:
|
||||||
|
if missing_files:
|
||||||
|
self._txt("There were %d missing files" % len(missing_files))
|
||||||
|
else:
|
||||||
|
self._txt("Pack of %s done" % output_blendfile.name)
|
||||||
|
|
||||||
|
def pack_aborted(self, reason: str):
|
||||||
|
self._txt("Aborted: %s" % reason)
|
||||||
|
self._status("ABORTED")
|
||||||
|
|
||||||
|
def trace_blendfile(self, filename: Path) -> None:
|
||||||
|
"""Called for every blendfile opened when tracing dependencies."""
|
||||||
|
self._txt("Inspecting %s" % filename.name)
|
||||||
|
|
||||||
|
def trace_asset(self, filename: Path) -> None:
|
||||||
|
if filename.stem == ".blend":
|
||||||
|
return
|
||||||
|
self._txt("Found asset %s" % filename.name)
|
||||||
|
|
||||||
|
def rewrite_blendfile(self, orig_filename: Path) -> None:
|
||||||
|
self._txt("Rewriting %s" % orig_filename.name)
|
||||||
|
|
||||||
|
def transfer_file(self, src: Path, dst: Path) -> None:
|
||||||
|
self._txt("Transferring %s" % src.name)
|
||||||
|
|
||||||
|
def transfer_file_skipped(self, src: Path, dst: Path) -> None:
|
||||||
|
self._txt("Skipped %s" % src.name)
|
||||||
|
|
||||||
|
def transfer_progress(self, total_bytes: int, transferred_bytes: int) -> None:
|
||||||
|
self._progress(round(100 * transferred_bytes / total_bytes))
|
||||||
|
|
||||||
|
def missing_file(self, filename: Path) -> None:
|
||||||
|
# TODO(Sybren): report missing files in a nice way
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
# class ShamanPacker(shaman.ShamanPacker):
|
||||||
|
# """Packer with support for getting an auth token from Flamenco Server."""
|
||||||
|
|
||||||
|
# def __init__(
|
||||||
|
# self,
|
||||||
|
# bfile: Path,
|
||||||
|
# project: Path,
|
||||||
|
# target: str,
|
||||||
|
# endpoint: str,
|
||||||
|
# checkout_id: str,
|
||||||
|
# *,
|
||||||
|
# manager_id: str,
|
||||||
|
# **kwargs
|
||||||
|
# ) -> None:
|
||||||
|
# self.manager_id = manager_id
|
||||||
|
# super().__init__(bfile, project, target, endpoint, checkout_id, **kwargs)
|
||||||
|
|
||||||
|
# def _get_auth_token(self) -> str:
|
||||||
|
# """get a token from Flamenco Server"""
|
||||||
|
|
||||||
|
# from ..blender import PILLAR_SERVER_URL
|
||||||
|
# from ..pillar import blender_id_subclient, uncached_session, SUBCLIENT_ID
|
||||||
|
|
||||||
|
# url = urllib.parse.urljoin(
|
||||||
|
# PILLAR_SERVER_URL, "flamenco/jwt/generate-token/%s" % self.manager_id
|
||||||
|
# )
|
||||||
|
# auth_token = blender_id_subclient()["token"]
|
||||||
|
|
||||||
|
# resp = uncached_session.get(url, auth=(auth_token, SUBCLIENT_ID))
|
||||||
|
# resp.raise_for_status()
|
||||||
|
# return resp.text
|
||||||
|
|
||||||
|
|
||||||
|
class PackThread(threading.Thread):
|
||||||
|
queue: queue.Queue[Message]
|
||||||
|
|
||||||
|
def __init__(self, packer: pack.Packer) -> None:
|
||||||
|
# Quitting Blender should abort the transfer (instead of hanging until
|
||||||
|
# the transfer is done), hence daemon=True.
|
||||||
|
super().__init__(daemon=True, name="PackThread")
|
||||||
|
|
||||||
|
self.queue = queue.SimpleQueue()
|
||||||
|
|
||||||
|
self.packer = packer
|
||||||
|
self.packer.progress_cb = BatProgress(queue=self.queue)
|
||||||
|
|
||||||
|
def run(self) -> None:
|
||||||
|
global _running_packthread
|
||||||
|
|
||||||
|
try:
|
||||||
|
self._run()
|
||||||
|
except BaseException as ex:
|
||||||
|
log.error("Error packing with BAT: %s", ex)
|
||||||
|
self.queue.put(MsgException(ex=ex))
|
||||||
|
finally:
|
||||||
|
with _packer_lock:
|
||||||
|
_running_packthread = None
|
||||||
|
|
||||||
|
def _run(self) -> None:
|
||||||
|
with self.packer:
|
||||||
|
log.debug("awaiting strategise")
|
||||||
|
self._set_bat_status("INVESTIGATING")
|
||||||
|
self.packer.strategise()
|
||||||
|
|
||||||
|
log.debug("awaiting execute")
|
||||||
|
self._set_bat_status("TRANSFERRING")
|
||||||
|
self.packer.execute()
|
||||||
|
|
||||||
|
log.debug("done")
|
||||||
|
self._set_bat_status("DONE")
|
||||||
|
|
||||||
|
msg = MsgDone(self.packer.output_path, self.packer.missing_files)
|
||||||
|
self.queue.put(msg)
|
||||||
|
|
||||||
|
def _set_bat_status(self, status: str) -> None:
|
||||||
|
self.queue.put(MsgSetWMAttribute("flamenco_bat_status", status))
|
||||||
|
|
||||||
|
def poll(self, timeout: Optional[int] = None) -> Optional[Message]:
|
||||||
|
"""Poll the queue, return the first message or None if there is none.
|
||||||
|
|
||||||
|
:param timeout: Max time to wait for a message to appear on the queue.
|
||||||
|
If None, will not wait and just return None immediately (if there is
|
||||||
|
no queued message).
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
return self.queue.get(block=timeout is not None, timeout=timeout)
|
||||||
|
except queue.Empty:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def abort(self) -> None:
|
||||||
|
"""Abort the running pack operation."""
|
||||||
|
self.packer.abort()
|
||||||
|
|
||||||
|
|
||||||
|
_running_packthread: typing.Optional[PackThread] = None
|
||||||
|
_packer_lock = threading.RLock()
|
||||||
|
|
||||||
|
|
||||||
|
def copy(
|
||||||
|
base_blendfile: Path,
|
||||||
|
project: Path,
|
||||||
|
target: str,
|
||||||
|
exclusion_filter: str,
|
||||||
|
*,
|
||||||
|
relative_only: bool,
|
||||||
|
packer_class=pack.Packer,
|
||||||
|
**packer_args
|
||||||
|
) -> PackThread:
|
||||||
|
"""Use BAT to copy the given file and dependencies to the target location.
|
||||||
|
|
||||||
|
Runs BAT in a separate thread, and returns early. Use poll() to get updates
|
||||||
|
& the final result.
|
||||||
|
"""
|
||||||
|
global _running_packthread
|
||||||
|
|
||||||
|
with _packer_lock:
|
||||||
|
if _running_packthread is not None:
|
||||||
|
raise RuntimeError("other packing operation already in progress")
|
||||||
|
|
||||||
|
packer = packer_class(
|
||||||
|
base_blendfile,
|
||||||
|
project,
|
||||||
|
target,
|
||||||
|
compress=True,
|
||||||
|
relative_only=relative_only,
|
||||||
|
**packer_args
|
||||||
|
)
|
||||||
|
if exclusion_filter:
|
||||||
|
filter_parts = exclusion_filter.strip().split(" ")
|
||||||
|
packer.exclude(*filter_parts)
|
||||||
|
|
||||||
|
packthread = PackThread(packer=packer)
|
||||||
|
with _packer_lock:
|
||||||
|
_running_packthread = packthread
|
||||||
|
|
||||||
|
packthread.start()
|
||||||
|
return packthread
|
||||||
|
|
||||||
|
|
||||||
|
def abort() -> None:
|
||||||
|
"""Abort a running copy() call.
|
||||||
|
|
||||||
|
No-op when there is no running copy(). Can be called from any thread.
|
||||||
|
"""
|
||||||
|
|
||||||
|
with _packer_lock:
|
||||||
|
if _running_packthread is None:
|
||||||
|
log.debug("No running packer, ignoring call to abort()")
|
||||||
|
return
|
||||||
|
log.info("Aborting running packer")
|
||||||
|
_running_packthread.abort()
|
||||||
|
|
||||||
|
|
||||||
|
def is_packing() -> bool:
|
||||||
|
"""Returns whether a BAT packing operation is running."""
|
||||||
|
|
||||||
|
with _packer_lock:
|
||||||
|
return _running_packthread is not None
|
@ -25,6 +25,12 @@ class FLAMENCO_PT_job_submission(bpy.types.Panel):
|
|||||||
row.operator("flamenco.fetch_job_types", text="", icon="FILE_REFRESH")
|
row.operator("flamenco.fetch_job_types", text="", icon="FILE_REFRESH")
|
||||||
self.draw_job_settings(context, layout)
|
self.draw_job_settings(context, layout)
|
||||||
|
|
||||||
|
layout.separator()
|
||||||
|
col = layout.column(align=True)
|
||||||
|
col.prop(context.scene, "flamenco_job_name", text="Job Name")
|
||||||
|
|
||||||
|
self.draw_flamenco_status(context, layout)
|
||||||
|
|
||||||
def draw_job_settings(
|
def draw_job_settings(
|
||||||
self, context: bpy.types.Context, layout: bpy.types.UILayout
|
self, context: bpy.types.Context, layout: bpy.types.UILayout
|
||||||
) -> None:
|
) -> None:
|
||||||
@ -43,6 +49,42 @@ class FLAMENCO_PT_job_submission(bpy.types.Panel):
|
|||||||
continue
|
continue
|
||||||
layout.prop(propgroup, setting.key)
|
layout.prop(propgroup, setting.key)
|
||||||
|
|
||||||
|
def draw_flamenco_status(
|
||||||
|
self, context: bpy.types.Context, layout: bpy.types.UILayout
|
||||||
|
) -> None:
|
||||||
|
# Show current status of Flamenco.
|
||||||
|
flamenco_status = context.window_manager.flamenco_bat_status
|
||||||
|
if flamenco_status in {"IDLE", "ABORTED", "DONE"}:
|
||||||
|
ui = layout
|
||||||
|
props = ui.operator(
|
||||||
|
"flamenco.submit_job",
|
||||||
|
text="Submit to Flamenco",
|
||||||
|
icon="RENDER_ANIMATION",
|
||||||
|
)
|
||||||
|
props.job_name = context.scene.flamenco_job_name
|
||||||
|
elif flamenco_status == "INVESTIGATING":
|
||||||
|
row = layout.row(align=True)
|
||||||
|
row.label(text="Investigating your files")
|
||||||
|
# row.operator(FLAMENCO_OT_abort.bl_idname, text="", icon="CANCEL")
|
||||||
|
elif flamenco_status == "COMMUNICATING":
|
||||||
|
layout.label(text="Communicating with Flamenco Server")
|
||||||
|
elif flamenco_status == "ABORTING":
|
||||||
|
row = layout.row(align=True)
|
||||||
|
row.label(text="Aborting, please wait.")
|
||||||
|
# row.operator(FLAMENCO_OT_abort.bl_idname, text="", icon="CANCEL")
|
||||||
|
if flamenco_status == "TRANSFERRING":
|
||||||
|
row = layout.row(align=True)
|
||||||
|
row.prop(
|
||||||
|
context.window_manager,
|
||||||
|
"flamenco_bat_progress",
|
||||||
|
text=context.window_manager.flamenco_bat_status_txt,
|
||||||
|
)
|
||||||
|
# row.operator(FLAMENCO_OT_abort.bl_idname, text="", icon="CANCEL")
|
||||||
|
elif (
|
||||||
|
flamenco_status != "IDLE" and context.window_manager.flamenco_bat_status_txt
|
||||||
|
):
|
||||||
|
layout.label(text=context.window_manager.flamenco_bat_status_txt)
|
||||||
|
|
||||||
|
|
||||||
classes = (FLAMENCO_PT_job_submission,)
|
classes = (FLAMENCO_PT_job_submission,)
|
||||||
register, unregister = bpy.utils.register_classes_factory(classes)
|
register, unregister = bpy.utils.register_classes_factory(classes)
|
||||||
|
@ -1,8 +1,22 @@
|
|||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
# <pep8 compliant>
|
# <pep8 compliant>
|
||||||
|
|
||||||
|
import logging
|
||||||
|
from pathlib import Path, PurePath
|
||||||
|
from typing import Optional, TYPE_CHECKING
|
||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
|
|
||||||
|
from . import preferences
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from .bat_interface import PackThread, Message
|
||||||
|
else:
|
||||||
|
PackThread = object
|
||||||
|
Message = object
|
||||||
|
|
||||||
|
_log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class FlamencoOpMixin:
|
class FlamencoOpMixin:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@ -59,6 +73,7 @@ class FLAMENCO_OT_ping_manager(FlamencoOpMixin, bpy.types.Operator):
|
|||||||
from flamenco.manager import ApiException
|
from flamenco.manager import ApiException
|
||||||
from flamenco.manager.apis import MetaApi
|
from flamenco.manager.apis import MetaApi
|
||||||
from flamenco.manager.models import FlamencoVersion
|
from flamenco.manager.models import FlamencoVersion
|
||||||
|
from urllib3.exceptions import HTTPError, MaxRetryError
|
||||||
|
|
||||||
context.window_manager.flamenco_status_ping = "..."
|
context.window_manager.flamenco_status_ping = "..."
|
||||||
|
|
||||||
@ -68,6 +83,14 @@ class FLAMENCO_OT_ping_manager(FlamencoOpMixin, bpy.types.Operator):
|
|||||||
except ApiException as ex:
|
except ApiException as ex:
|
||||||
report = "Manager cannot be reached: %s" % ex
|
report = "Manager cannot be reached: %s" % ex
|
||||||
level = "ERROR"
|
level = "ERROR"
|
||||||
|
except MaxRetryError as ex:
|
||||||
|
# This is the common error, when for example the port number is
|
||||||
|
# incorrect and nothing is listening.
|
||||||
|
report = "Manager cannot be reached"
|
||||||
|
level = "WARNING"
|
||||||
|
except HTTPError as ex:
|
||||||
|
report = "Manager cannot be reached: %s" % ex
|
||||||
|
level = "ERROR"
|
||||||
else:
|
else:
|
||||||
report = "%s version %s found" % (response.name, response.version)
|
report = "%s version %s found" % (response.name, response.version)
|
||||||
level = "INFO"
|
level = "INFO"
|
||||||
@ -77,8 +100,146 @@ class FLAMENCO_OT_ping_manager(FlamencoOpMixin, bpy.types.Operator):
|
|||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
class FLAMENCO_OT_submit_job(FlamencoOpMixin, bpy.types.Operator):
|
||||||
|
bl_idname = "flamenco.submit_job"
|
||||||
|
bl_label = "Flamenco: Submit Job"
|
||||||
|
bl_description = "Pack the current blend file and send it to Flamenco"
|
||||||
|
bl_options = {"REGISTER"} # No UNDO.
|
||||||
|
|
||||||
|
job_name: bpy.props.StringProperty(name="Job Name")
|
||||||
|
|
||||||
|
timer: Optional[bpy.types.Timer] = None
|
||||||
|
packthread: Optional[PackThread] = None
|
||||||
|
|
||||||
|
log = _log.getChild(bl_idname)
|
||||||
|
|
||||||
|
def invoke(self, context: bpy.types.Context, event) -> set[str]:
|
||||||
|
filepath = self._save_blendfile(context)
|
||||||
|
return self._bat_pack(context, filepath)
|
||||||
|
|
||||||
|
def modal(self, context: bpy.types.Context, event) -> set[str]:
|
||||||
|
# This function is called for TIMER events to poll the BAT pack thread.
|
||||||
|
if event.type != "TIMER":
|
||||||
|
return {"PASS_THROUGH"}
|
||||||
|
|
||||||
|
if self.packthread is None:
|
||||||
|
# If there is no pack thread running, there isn't much we can do.
|
||||||
|
return self._quit(context)
|
||||||
|
|
||||||
|
msg = self.packthread.poll()
|
||||||
|
if not msg:
|
||||||
|
return {"RUNNING_MODAL"}
|
||||||
|
|
||||||
|
return self._on_bat_pack_msg(context, msg)
|
||||||
|
|
||||||
|
def _save_blendfile(self, context):
|
||||||
|
"""Save to a different file, specifically for Flamenco.
|
||||||
|
|
||||||
|
We shouldn't overwrite the artist's file.
|
||||||
|
We can compress, since this file won't be managed by SVN and doesn't need diffability.
|
||||||
|
"""
|
||||||
|
render = context.scene.render
|
||||||
|
|
||||||
|
# Remember settings we need to restore after saving.
|
||||||
|
old_use_file_extension = render.use_file_extension
|
||||||
|
old_use_overwrite = render.use_overwrite
|
||||||
|
old_use_placeholder = render.use_placeholder
|
||||||
|
|
||||||
|
# TODO: see about disabling the denoiser (like the old Blender Cloud addon did).
|
||||||
|
|
||||||
|
try:
|
||||||
|
# The file extension should be determined by the render settings, not necessarily
|
||||||
|
# by the setttings in the output panel.
|
||||||
|
render.use_file_extension = True
|
||||||
|
|
||||||
|
# Rescheduling should not overwrite existing frames.
|
||||||
|
render.use_overwrite = False
|
||||||
|
render.use_placeholder = False
|
||||||
|
|
||||||
|
filepath = Path(context.blend_data.filepath).with_suffix(".flamenco.blend")
|
||||||
|
self.log.info("Saving copy to temporary file %s", filepath)
|
||||||
|
bpy.ops.wm.save_as_mainfile(
|
||||||
|
filepath=str(filepath), compress=True, copy=True
|
||||||
|
)
|
||||||
|
finally:
|
||||||
|
# Restore the settings we changed, even after an exception.
|
||||||
|
render.use_file_extension = old_use_file_extension
|
||||||
|
render.use_overwrite = old_use_overwrite
|
||||||
|
render.use_placeholder = old_use_placeholder
|
||||||
|
|
||||||
|
return filepath
|
||||||
|
|
||||||
|
def _bat_pack(self, context: bpy.types.Context, blendfile: Path) -> set[str]:
|
||||||
|
from . import bat_interface
|
||||||
|
|
||||||
|
if bat_interface.is_packing():
|
||||||
|
self.report({"ERROR"}, "Another packing operation is running")
|
||||||
|
self._quit()
|
||||||
|
return {"CANCELLED"}
|
||||||
|
|
||||||
|
# TODO: get project path from addon preferences / project definition on Manager.
|
||||||
|
project_path = blendfile.parent
|
||||||
|
try:
|
||||||
|
project_path = Path(bpy.path.abspath(str(project_path))).resolve()
|
||||||
|
except FileNotFoundError:
|
||||||
|
# Path.resolve() will raise a FileNotFoundError if the project path doesn't exist.
|
||||||
|
self.report({"ERROR"}, "Project path %s does not exist" % project_path)
|
||||||
|
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)
|
||||||
|
|
||||||
|
self.packthread = bat_interface.copy(
|
||||||
|
base_blendfile=blendfile,
|
||||||
|
project=project_path,
|
||||||
|
target=render_output,
|
||||||
|
exclusion_filter="", # TODO: get from GUI.
|
||||||
|
relative_only=True, # TODO: get from GUI.
|
||||||
|
)
|
||||||
|
|
||||||
|
context.window_manager.modal_handler_add(self)
|
||||||
|
wm = context.window_manager
|
||||||
|
self.timer = wm.event_timer_add(0.25, window=context.window)
|
||||||
|
|
||||||
|
return {"RUNNING_MODAL"}
|
||||||
|
|
||||||
|
def _on_bat_pack_msg(self, context: bpy.types.Context, msg: Message) -> set[str]:
|
||||||
|
from . import bat_interface
|
||||||
|
|
||||||
|
if isinstance(msg, bat_interface.MsgDone):
|
||||||
|
self.report({"INFO"}, "BAT pack is done")
|
||||||
|
# TODO: actually send the job to Flamenco!
|
||||||
|
return self._quit(context)
|
||||||
|
|
||||||
|
if isinstance(msg, bat_interface.MsgException):
|
||||||
|
self.log.error("Error performing BAT pack: %s", msg.ex)
|
||||||
|
self.report({"ERROR"}, "Error performing BAT pack")
|
||||||
|
|
||||||
|
# This was an exception caught at the top level of the thread, so
|
||||||
|
# the packing thread itself has stopped.
|
||||||
|
return self._quit(context)
|
||||||
|
|
||||||
|
if isinstance(msg, bat_interface.MsgSetWMAttribute):
|
||||||
|
wm = context.window_manager
|
||||||
|
setattr(wm, msg.attribute_name, msg.value)
|
||||||
|
|
||||||
|
return {"RUNNING_MODAL"}
|
||||||
|
|
||||||
|
def _quit(self, context: bpy.types.Context) -> set[str]:
|
||||||
|
"""Stop any timer and return a 'FINISHED' status.
|
||||||
|
|
||||||
|
Does neither check nor abort the BAT pack thread.
|
||||||
|
"""
|
||||||
|
if self.timer is not None:
|
||||||
|
context.window_manager.event_timer_remove(self.timer)
|
||||||
|
self.timer = None
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
classes = (
|
classes = (
|
||||||
FLAMENCO_OT_fetch_job_types,
|
FLAMENCO_OT_fetch_job_types,
|
||||||
FLAMENCO_OT_ping_manager,
|
FLAMENCO_OT_ping_manager,
|
||||||
|
FLAMENCO_OT_submit_job,
|
||||||
)
|
)
|
||||||
register, unregister = bpy.utils.register_classes_factory(classes)
|
register, unregister = bpy.utils.register_classes_factory(classes)
|
||||||
|
@ -31,12 +31,18 @@ class FlamencoPreferences(bpy.types.AddonPreferences):
|
|||||||
col.label(text=context.window_manager.flamenco_status_ping)
|
col.label(text=context.window_manager.flamenco_status_ping)
|
||||||
|
|
||||||
|
|
||||||
def manager_url(context: bpy.types.Context) -> str:
|
def get(context: bpy.types.Context) -> FlamencoPreferences:
|
||||||
"""Returns the configured Manager URL."""
|
"""Return the add-on preferences."""
|
||||||
prefs = context.preferences.addons["flamenco"].preferences
|
prefs = context.preferences.addons["flamenco"].preferences
|
||||||
assert isinstance(
|
assert isinstance(
|
||||||
prefs, FlamencoPreferences
|
prefs, FlamencoPreferences
|
||||||
), "Expected FlamencoPreferences, got %s instead" % (type(prefs))
|
), "Expected FlamencoPreferences, got %s instead" % (type(prefs))
|
||||||
|
return prefs
|
||||||
|
|
||||||
|
|
||||||
|
def manager_url(context: bpy.types.Context) -> str:
|
||||||
|
"""Returns the configured Manager URL."""
|
||||||
|
prefs = get(context)
|
||||||
return str(prefs.manager_url)
|
return str(prefs.manager_url)
|
||||||
|
|
||||||
|
|
||||||
|
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user