Added the Scene Manager (Libreflow casting, load, update, assetize)
New Scene Manager panel (WTD tab) talking to a running Libreflow through its IPC server and the wtdcmds commands: pick the shot context, read the casting, link the latest available publishes (overrides for chars and props, collection instances otherwise), flag out-of-date versions in red, update / roll back / remove loaded assets, and publish local collections as assets. Requests run in the background. Settings in config.py (SCENE_MANAGER_*, LIBREFLOW_*). The Shot Viewer groups rigs loaded by the Scene Manager by asset type. The manifest declares the network permission. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
10fe71e79e
commit
c238f4db7c
10 files changed
+1630
-11
No files matched your search
+201
@@ -3,6 +3,9 @@ import textwrap
|
||||
import bpy
|
||||
|
||||
from .. import config, rename, shot_viewer, tools_ops
|
||||
from ..scene_manager import ipc
|
||||
from ..scene_manager import library as sm_library
|
||||
from ..scene_manager import operators as sm
|
||||
from ..core import CheckContext, Severity, checks_for_context, contexts, runner
|
||||
from ..core.runner import Status
|
||||
from .operators import EXPANDED
|
||||
@@ -316,6 +319,180 @@ class _ShotViewerPanel:
|
||||
).name = rig.name
|
||||
|
||||
|
||||
MESSAGE_ICONS = {"INFO": "INFO", "WARNING": "ERROR", "ERROR": "CANCEL"}
|
||||
|
||||
|
||||
class _SceneManagerPanel:
|
||||
bl_region_type = "UI"
|
||||
bl_category = "WTD"
|
||||
bl_label = "Scene Manager"
|
||||
|
||||
def draw(self, context) -> None:
|
||||
layout = self.layout
|
||||
state = sm.STATE
|
||||
scene = context.scene
|
||||
|
||||
row = layout.row(align=True)
|
||||
if sm.connected():
|
||||
session = sm.current_session()
|
||||
text = f"{session.get('session_name', '?')} ({session.get('cluster_name', '')})"
|
||||
if state.ping.get("user"):
|
||||
text += f" {state.ping['user']} @ {state.ping.get('site', '')}"
|
||||
row.label(text=text, icon="LINKED")
|
||||
else:
|
||||
row.label(text=state.connection_error, icon="UNLINKED")
|
||||
row.operator(sm.WTD_OT_scene_connect.bl_idname, text="", icon="FILE_REFRESH")
|
||||
if len(state.sessions) > 1:
|
||||
col = layout.column(align=True)
|
||||
chosen = context.window_manager.wtd_libreflow.session_uid
|
||||
for uid, info in state.sessions.items():
|
||||
col.operator(
|
||||
sm.WTD_OT_scene_select_session.bl_idname,
|
||||
text=f"{info.get('session_name', uid)}: {info.get('current_oid') or ''}",
|
||||
depress=uid == chosen,
|
||||
).uid = uid
|
||||
|
||||
layout.prop(scene.wtd_scene_manager, "context_oid", text="", icon="SEQUENCE")
|
||||
row = layout.row(align=True)
|
||||
row.operator(sm.WTD_OT_scene_use_current_oid.bl_idname, text="Libreflow Oid", icon="IMPORT")
|
||||
row.operator(sm.WTD_OT_scene_pick_shot.bl_idname, text="Pick Shot", icon="VIEWZOOM")
|
||||
text, valid = sm.context_label(sm.context_oid(scene))
|
||||
if text:
|
||||
row = layout.row()
|
||||
row.alert = not valid
|
||||
_wrapped_label(row, context, text, icon="CHECKMARK" if valid else "ERROR")
|
||||
|
||||
busy = ipc.busy_label()
|
||||
if busy:
|
||||
layout.label(text=f"{busy}...", icon="SORTTIME")
|
||||
message, level = state.message
|
||||
if message:
|
||||
row = layout.row()
|
||||
row.alert = level == "ERROR"
|
||||
_wrapped_label(row, context, message, icon=MESSAGE_ICONS[level])
|
||||
|
||||
|
||||
class _SceneCastingPanel:
|
||||
bl_region_type = "UI"
|
||||
bl_category = "WTD"
|
||||
bl_label = "Casting"
|
||||
bl_parent_id = "WTD_PT_scene_manager_view3d"
|
||||
|
||||
def draw(self, context) -> None:
|
||||
layout = self.layout
|
||||
oid = sm.context_oid(context.scene)
|
||||
row = layout.row(align=True)
|
||||
row.scale_y = 1.3
|
||||
row.operator(sm.WTD_OT_scene_load.bl_idname, text="Load Missing", icon="IMPORT")
|
||||
row.operator(sm.WTD_OT_scene_refresh.bl_idname, text="", icon="FILE_REFRESH")
|
||||
casting = sm.STATE.casting.get(oid)
|
||||
if casting is None:
|
||||
text = "Not read yet: refresh" if sm.is_shot_context(oid) else "Set a shot as context"
|
||||
layout.label(text=text, icon="INFO")
|
||||
return
|
||||
if not casting["assets"]:
|
||||
layout.label(text="Nothing cast in this shot", icon="INFO")
|
||||
|
||||
found = sm_library.all_instances()
|
||||
col = layout.column(align=True)
|
||||
for asset in casting["assets"]:
|
||||
library = sm_library.find_library(asset["file"]["file_oid"]) if asset.get("file") else None
|
||||
have = len(found.get(library.name, [])) if library else 0
|
||||
want = asset.get("nb_occurences") or 1
|
||||
error = asset.get("error")
|
||||
icon = "ERROR" if error else "CHECKMARK" if have >= want else "IMPORT"
|
||||
row = col.row(align=True)
|
||||
row.alert = bool(error)
|
||||
row.label(text=f"{asset['name']} ({asset.get('asset_type', '')})", icon=icon)
|
||||
row.label(text=f"{have}/{want}")
|
||||
if not error and have < want:
|
||||
row.operator(sm.WTD_OT_scene_load.bl_idname, text="", icon="IMPORT").asset_oid = asset["oid"]
|
||||
if error:
|
||||
sub = col.row()
|
||||
sub.alert = True
|
||||
_wrapped_label(sub, context, error, icon="BLANK1")
|
||||
|
||||
cast = {asset["oid"] for asset in casting["assets"]}
|
||||
extra = [library for library in sm_library.managed_libraries() if library["wtd_asset_oid"] not in cast]
|
||||
if extra:
|
||||
box = layout.box()
|
||||
box.label(text="Loaded, no longer cast:", icon="ERROR")
|
||||
for library in extra:
|
||||
box.label(text=library.get("wtd_asset_name", library.name), icon="BLANK1")
|
||||
|
||||
|
||||
class _SceneLoadedPanel:
|
||||
bl_region_type = "UI"
|
||||
bl_category = "WTD"
|
||||
bl_label = "Loaded"
|
||||
bl_parent_id = "WTD_PT_scene_manager_view3d"
|
||||
|
||||
def draw(self, context) -> None:
|
||||
layout = self.layout
|
||||
libraries = sm_library.managed_libraries()
|
||||
if not libraries:
|
||||
layout.label(text="Nothing loaded by the Scene Manager", icon="INFO")
|
||||
return
|
||||
row = layout.row(align=True)
|
||||
row.operator(sm.WTD_OT_scene_update.bl_idname, text="Update All", icon="FILE_REFRESH")
|
||||
row.operator(sm.WTD_OT_scene_refresh.bl_idname, text="", icon="VIEWZOOM")
|
||||
|
||||
found = sm_library.all_instances()
|
||||
for library in libraries:
|
||||
state, text = sm.version_status(library)
|
||||
outdated = state in {"UPDATE", "PENDING"}
|
||||
expanded = library.name in sm.EXPANDED
|
||||
row = layout.row(align=True)
|
||||
row.operator(
|
||||
sm.WTD_OT_scene_toggle_expand.bl_idname,
|
||||
text="",
|
||||
emboss=False,
|
||||
icon="DOWNARROW_HLT" if expanded else "RIGHTARROW",
|
||||
).library = library.name
|
||||
row.label(text=library.get("wtd_asset_name", library.name), icon="LIBRARY_DATA_DIRECT")
|
||||
version = row.row(align=True)
|
||||
version.alert = outdated
|
||||
version.label(text=library.get("wtd_revision", "?"))
|
||||
if state == "UPDATE":
|
||||
row.operator(sm.WTD_OT_scene_update.bl_idname, text="", icon="FILE_REFRESH").library = library.name
|
||||
row.operator(sm.WTD_OT_scene_pick_revision.bl_idname, text="", icon="COLLAPSEMENU").library = library.name
|
||||
row.operator(sm.WTD_OT_scene_remove.bl_idname, text="", icon="X").library = library.name
|
||||
if outdated:
|
||||
sub = layout.row()
|
||||
sub.alert = True
|
||||
_wrapped_label(sub, context, text, icon="BLANK1")
|
||||
if not expanded:
|
||||
continue
|
||||
col = layout.column(align=True)
|
||||
for instance in found.get(library.name, []):
|
||||
is_object = isinstance(instance, bpy.types.Object)
|
||||
sub = col.row(align=True)
|
||||
sub.label(text=instance.name, icon="OUTLINER_OB_EMPTY" if is_object else "OUTLINER_COLLECTION")
|
||||
op = sub.operator(sm.WTD_OT_scene_select_instance.bl_idname, text="", icon="RESTRICT_SELECT_OFF")
|
||||
op.library, op.instance = library.name, instance.name
|
||||
sub.operator(sm.WTD_OT_scene_remove.bl_idname, text="", icon="X").instance = instance.name
|
||||
|
||||
layout.separator()
|
||||
row = layout.row(align=True)
|
||||
row.operator(sm.WTD_OT_scene_update.bl_idname, text="Update Selected").selected = True
|
||||
row.operator(sm.WTD_OT_scene_remove.bl_idname, text="Remove Selected").selected = True
|
||||
|
||||
|
||||
class _SceneAssetizePanel:
|
||||
bl_region_type = "UI"
|
||||
bl_category = "WTD"
|
||||
bl_label = "Assetize"
|
||||
bl_parent_id = "WTD_PT_scene_manager_view3d"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
|
||||
def draw(self, context) -> None:
|
||||
layout = self.layout
|
||||
_wrapped_label(
|
||||
layout, context, "Publish a local collection of this scene to Libreflow as an asset.", icon="INFO"
|
||||
)
|
||||
layout.operator(sm.WTD_OT_scene_assetize.bl_idname, text="Assetize Collection...", icon="EXPORT")
|
||||
|
||||
|
||||
class WTD_PT_sanity_view3d(_SanityPanel, bpy.types.Panel):
|
||||
bl_idname = "WTD_PT_sanity_view3d"
|
||||
bl_space_type = "VIEW_3D"
|
||||
@@ -341,6 +518,26 @@ class WTD_PT_shot_viewer_view3d(_ShotViewerPanel, bpy.types.Panel):
|
||||
bl_space_type = "VIEW_3D"
|
||||
|
||||
|
||||
class WTD_PT_scene_manager_view3d(_SceneManagerPanel, bpy.types.Panel):
|
||||
bl_idname = "WTD_PT_scene_manager_view3d"
|
||||
bl_space_type = "VIEW_3D"
|
||||
|
||||
|
||||
class WTD_PT_scene_casting_view3d(_SceneCastingPanel, bpy.types.Panel):
|
||||
bl_idname = "WTD_PT_scene_casting_view3d"
|
||||
bl_space_type = "VIEW_3D"
|
||||
|
||||
|
||||
class WTD_PT_scene_loaded_view3d(_SceneLoadedPanel, bpy.types.Panel):
|
||||
bl_idname = "WTD_PT_scene_loaded_view3d"
|
||||
bl_space_type = "VIEW_3D"
|
||||
|
||||
|
||||
class WTD_PT_scene_assetize_view3d(_SceneAssetizePanel, bpy.types.Panel):
|
||||
bl_idname = "WTD_PT_scene_assetize_view3d"
|
||||
bl_space_type = "VIEW_3D"
|
||||
|
||||
|
||||
class WTD_PT_sanity_sequencer(_SanityPanel, bpy.types.Panel):
|
||||
bl_idname = "WTD_PT_sanity_sequencer"
|
||||
bl_space_type = "SEQUENCE_EDITOR"
|
||||
@@ -351,6 +548,10 @@ classes = (
|
||||
WTD_PT_sanity_view3d,
|
||||
WTD_PT_tools_view3d,
|
||||
WTD_PT_shot_viewer_view3d,
|
||||
WTD_PT_scene_manager_view3d,
|
||||
WTD_PT_scene_casting_view3d,
|
||||
WTD_PT_scene_loaded_view3d,
|
||||
WTD_PT_scene_assetize_view3d,
|
||||
WTD_PT_rename_view3d,
|
||||
WTD_PT_rename_node_editor,
|
||||
WTD_PT_sanity_sequencer,
|
||||
|
||||
Reference in new issue
Block a user