Move Anim_Setup
module into Blender_Kitsu
#5
@ -40,7 +40,7 @@ from blender_kitsu import (
|
||||
ui,
|
||||
)
|
||||
|
||||
from blender_kitsu.anim_setup import ops #TODO Fix Registraion
|
||||
|
||||
|
||||
from blender_kitsu.logger import LoggerFactory, LoggerLevelManager
|
||||
|
||||
@ -81,7 +81,6 @@ if _need_reload:
|
||||
context.reload()
|
||||
tasks.reload()
|
||||
anim.reload()
|
||||
#ops.reload()
|
||||
|
||||
|
||||
def register():
|
||||
@ -97,7 +96,7 @@ def register():
|
||||
playblast.register()
|
||||
anim.register()
|
||||
shot_builder.register()
|
||||
ops.register()
|
||||
|
||||
|
||||
LoggerLevelManager.configure_levels()
|
||||
logger.info("Registered blender-kitsu")
|
||||
@ -116,7 +115,6 @@ def unregister():
|
||||
lookdev.unregister()
|
||||
playblast.unregister()
|
||||
shot_builder.unregister()
|
||||
ops.unregister()
|
||||
|
||||
LoggerLevelManager.restore_levels()
|
||||
|
||||
|
@ -1,127 +0,0 @@
|
||||
import bpy
|
||||
from bpy import context
|
||||
import re
|
||||
from pathlib import Path
|
||||
from typing import Set
|
||||
from blender_kitsu import prefs
|
||||
from blender_kitsu import cache
|
||||
from blender_kitsu import gazu
|
||||
|
||||
|
||||
class ANIM_SETUP_OT_setup_workspaces(bpy.types.Operator):
|
||||
bl_idname = "anim_setup.setup_workspaces"
|
||||
bl_label = "Setup Workspace"
|
||||
bl_description = "Sets up the workspaces for the animation task"
|
||||
|
||||
def execute(self, context: bpy.types.Context) -> Set[str]:
|
||||
# Remove non anim workspaces.
|
||||
for ws in bpy.data.workspaces:
|
||||
if ws.name != "Animation":
|
||||
bpy.ops.workspace.delete({"workspace": ws})
|
||||
self.report({"INFO"}, "Deleted non Animation workspaces")
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class ANIM_SETUP_OT_load_latest_edit(bpy.types.Operator):
|
||||
bl_idname = "asset_setup.load_latest_edit"
|
||||
bl_label = "Load edit"
|
||||
bl_description = (
|
||||
"Loads latest edit from shot_preview_folder "
|
||||
"Shifts edit so current shot starts at 3d_in metadata shot key from Kitsu"
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context: bpy.types.Context) -> bool:
|
||||
return cls.can_load_edit(context)
|
||||
|
||||
@classmethod
|
||||
def description(cls, context, properties):
|
||||
if cls.can_load_edit(context):
|
||||
return "Load latest edit from shared folder"
|
||||
else:
|
||||
return "Shared folder not set, or VSE area not available in this workspace"
|
||||
|
||||
def execute(self, context: bpy.types.Context) -> Set[str]:
|
||||
addon_prefs = prefs.addon_prefs_get(context)
|
||||
edit_export_path = Path(addon_prefs.edit_export_dir)
|
||||
strip_channel = 1
|
||||
latest_file = self._get_latest_edit(context)
|
||||
if not latest_file:
|
||||
self.report(
|
||||
{"ERROR"}, f"Found no edit file in: {edit_export_path.as_posix()}"
|
||||
)
|
||||
strip_filepath = latest_file.as_posix()
|
||||
strip_frame_start = 101
|
||||
|
||||
# Needs to be run in sequence editor area.
|
||||
# area_override = None
|
||||
scene = context.scene
|
||||
if not scene.sequence_editor:
|
||||
scene.sequence_editor_create()
|
||||
seq_editor = scene.sequence_editor
|
||||
strip = seq_editor.sequences.new_movie(
|
||||
strip_filepath,
|
||||
strip_filepath,
|
||||
strip_channel + 1,
|
||||
strip_frame_start,
|
||||
fit_method="FIT",
|
||||
)
|
||||
sound_strip = seq_editor.sequences.new_sound(
|
||||
strip_filepath,
|
||||
strip_filepath,
|
||||
strip_channel,
|
||||
strip_frame_start,
|
||||
)
|
||||
|
||||
bpy.ops.kitsu.con_detect_context()
|
||||
shot = cache.shot_active_get()
|
||||
|
||||
|
||||
# Update shift frame range prop.
|
||||
frame_in = shot.frame_in
|
||||
frame_3d_offset = 101
|
||||
|
||||
# Set sequence strip start kitsu data.
|
||||
for strip in scene.sequence_editor.sequences_all:
|
||||
strip.frame_start = -(frame_in) + frame_3d_offset #TODO CONFIRM LOGIC HERE
|
||||
|
||||
self.report({"INFO"}, f"Loaded latest edit: {latest_file.name}")
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
def _get_latest_edit(self, context: bpy.types.Context):
|
||||
addon_prefs = prefs.addon_prefs_get(context)
|
||||
|
||||
edit_export_path = Path(addon_prefs.edit_export_dir)
|
||||
|
||||
files_list = [
|
||||
f
|
||||
for f in edit_export_path.iterdir()
|
||||
if f.is_file() and self._is_valid_edit_name(f.name)
|
||||
]
|
||||
files_list = sorted(files_list, reverse=True)
|
||||
|
||||
return files_list[0]
|
||||
|
||||
def _is_valid_edit_name(self, filename: str) -> bool:
|
||||
pattern = r"petprojects_v\d\d\d.mp4"
|
||||
|
||||
match = re.search(pattern, filename)
|
||||
if match:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
classes = [
|
||||
ANIM_SETUP_OT_setup_workspaces,
|
||||
ANIM_SETUP_OT_load_latest_edit,
|
||||
]
|
||||
|
||||
|
||||
def register():
|
||||
for cls in classes:
|
||||
bpy.utils.register_class(cls)
|
||||
|
||||
def unregister():
|
||||
for cls in classes:
|
||||
bpy.utils.unregister_class(cls)
|
@ -40,6 +40,8 @@ from blender_kitsu.auth.ops import (
|
||||
)
|
||||
from blender_kitsu.context.ops import KITSU_OT_con_productions_load
|
||||
from blender_kitsu.lookdev.prefs import LOOKDEV_preferences
|
||||
from blender_kitsu.shot_builder.anim_setup.core import editorial_export_check_latest
|
||||
|
||||
|
||||
logger = LoggerFactory.getLogger()
|
||||
|
||||
@ -159,28 +161,6 @@ class KITSU_addon_preferences(bpy.types.AddonPreferences):
|
||||
def init_playblast_file_model(self, context: bpy.types.Context) -> None:
|
||||
ops_playblast_data.init_playblast_file_model(context)
|
||||
|
||||
def init_editoral_export_directory(self, context:bpy.types.Context) -> None:
|
||||
edit_export_path = Path(self.edit_export_dir)
|
||||
|
||||
files_list = [
|
||||
f
|
||||
for f in edit_export_path.iterdir()
|
||||
if f.is_file()
|
||||
]
|
||||
|
||||
files_list = sorted(files_list, reverse=True)
|
||||
valid_file = False
|
||||
for item in files_list:
|
||||
match = re.search(self.edit_export_file_pattern, item._str)
|
||||
if match:
|
||||
valid_file = True
|
||||
if not valid_file:
|
||||
self.edit_export_file_pattern = ""
|
||||
logger.error(
|
||||
"Failed to initialize editorial export file model. Invalid path/pattern. Check addon preferences"
|
||||
)
|
||||
logger.info("Initialized editorial export file model, successfully.")
|
||||
|
||||
bl_idname = __package__
|
||||
|
||||
host: bpy.props.StringProperty( # type: ignore
|
||||
@ -333,7 +313,6 @@ class KITSU_addon_preferences(bpy.types.AddonPreferences):
|
||||
options={"HIDDEN", "SKIP_SAVE"},
|
||||
description="File pattern to search for latest editorial export. Typically '{proj_name}_v\d\d\d.mp4'",
|
||||
default="petprojects_v\d\d\d.mp4",
|
||||
update=init_editoral_export_directory,
|
||||
|
||||
)
|
||||
|
||||
@ -505,29 +484,13 @@ class KITSU_addon_preferences(bpy.types.AddonPreferences):
|
||||
|
||||
@property
|
||||
def is_editorial_dir_valid(self) -> bool:
|
||||
edit_export_path = Path(self.edit_export_dir)
|
||||
|
||||
files_list = [
|
||||
f
|
||||
for f in edit_export_path.iterdir()
|
||||
if f.is_file()
|
||||
]
|
||||
|
||||
files_list = sorted(files_list, reverse=True)
|
||||
valid_file = False
|
||||
for item in files_list:
|
||||
match = re.search(self.edit_export_file_pattern, item._str)
|
||||
if match:
|
||||
valid_file = True
|
||||
if not valid_file:
|
||||
if editorial_export_check_latest(bpy.context) is None:
|
||||
logger.error(
|
||||
"Failed to initialize editorial export file model. Invalid path/pattern. Check addon preferences"
|
||||
)
|
||||
return False
|
||||
logger.info("Initialized editorial export file model, successfully.")
|
||||
return True
|
||||
|
||||
|
||||
def session_get(context: bpy.types.Context) -> Session:
|
||||
"""
|
||||
Shortcut to get session from blender_kitsu addon preferences
|
||||
|
@ -22,6 +22,7 @@ from blender_kitsu.shot_builder.ui import *
|
||||
from blender_kitsu.shot_builder.connectors.kitsu import *
|
||||
from blender_kitsu.shot_builder.operators import *
|
||||
import bpy
|
||||
from blender_kitsu.shot_builder.anim_setup import ops #TODO Fix Registraion
|
||||
|
||||
# import logging
|
||||
# logging.basicConfig(level=logging.DEBUG)
|
||||
@ -48,9 +49,11 @@ def register():
|
||||
for cls in classes:
|
||||
bpy.utils.register_class(cls)
|
||||
bpy.types.TOPBAR_MT_file_new.append(topbar_file_new_draw_handler)
|
||||
ops.register()
|
||||
|
||||
|
||||
def unregister():
|
||||
bpy.types.TOPBAR_MT_file_new.remove(topbar_file_new_draw_handler)
|
||||
for cls in classes:
|
||||
bpy.utils.unregister_class(cls)
|
||||
ops.unregister()
|
||||
|
83
blender_kitsu/shot_builder/anim_setup/core.py
Normal file
83
blender_kitsu/shot_builder/anim_setup/core.py
Normal file
@ -0,0 +1,83 @@
|
||||
import bpy
|
||||
import re
|
||||
from pathlib import Path
|
||||
from typing import Set
|
||||
from blender_kitsu import prefs
|
||||
from blender_kitsu import cache
|
||||
|
||||
|
||||
def animation_workspace_delete_others(self, context:bpy.types.Context):
|
||||
"""Delete any workspace that is not an animation workspace"""
|
||||
for ws in bpy.data.workspaces:
|
||||
if ws.name != "Animation":
|
||||
bpy.ops.workspace.delete({"workspace": ws})
|
||||
self.report({"INFO"}, "Deleted non Animation workspaces")
|
||||
|
||||
def editorial_export_get_latest(self, context:bpy.types.Context):
|
||||
"""Loads latest export from editorial department"""
|
||||
addon_prefs = prefs.addon_prefs_get(context)
|
||||
edit_export_path = Path(addon_prefs.edit_export_dir)
|
||||
strip_channel = 1
|
||||
latest_file = editorial_export_check_latest(context)
|
||||
if not latest_file:
|
||||
self.report(
|
||||
{"ERROR"}, f"Found no edit file in: {edit_export_path.as_posix()}"
|
||||
)
|
||||
strip_filepath = latest_file.as_posix()
|
||||
strip_frame_start = addon_prefs.shot_builder_frame_offset
|
||||
|
||||
scene = context.scene
|
||||
if not scene.sequence_editor:
|
||||
scene.sequence_editor_create()
|
||||
seq_editor = scene.sequence_editor
|
||||
movie_strip = seq_editor.sequences.new_movie(
|
||||
strip_filepath,
|
||||
strip_filepath,
|
||||
strip_channel + 1,
|
||||
strip_frame_start,
|
||||
fit_method="FIT",
|
||||
)
|
||||
sound_strip = seq_editor.sequences.new_sound(
|
||||
strip_filepath,
|
||||
strip_filepath,
|
||||
strip_channel,
|
||||
strip_frame_start,
|
||||
)
|
||||
shot = cache.shot_active_get()
|
||||
|
||||
|
||||
# Update shift frame range prop.
|
||||
frame_in = shot.frame_in
|
||||
frame_3d_offset = addon_prefs.shot_builder_frame_offset
|
||||
|
||||
# Set sequence strip start kitsu data.
|
||||
for strip in scene.sequence_editor.sequences_all:
|
||||
strip.frame_start = -(frame_in) + frame_3d_offset #TODO CONFIRM LOGIC HERE
|
||||
|
||||
self.report({"INFO"}, f"Loaded latest edit: {latest_file.name}")
|
||||
|
||||
|
||||
def editorial_export_check_latest(context: bpy.types.Context):
|
||||
"""Find latest export in editorial export directory"""
|
||||
addon_prefs = prefs.addon_prefs_get(context)
|
||||
|
||||
edit_export_path = Path(addon_prefs.edit_export_dir)
|
||||
|
||||
files_list = [
|
||||
f
|
||||
for f in edit_export_path.iterdir()
|
||||
if f.is_file() and editorial_export_is_valid_edit_name(addon_prefs.edit_export_file_pattern, f.name)
|
||||
]
|
||||
if len(files_list) >= 1:
|
||||
files_list = sorted(files_list, reverse=True)
|
||||
return files_list[0]
|
||||
return None
|
||||
|
||||
|
||||
def editorial_export_is_valid_edit_name(file_pattern:str, filename: str) -> bool:
|
||||
"""Verify file name matches file pattern set in preferences"""
|
||||
match = re.search(file_pattern, filename)
|
||||
if match:
|
||||
return True
|
||||
return False
|
||||
|
49
blender_kitsu/shot_builder/anim_setup/ops.py
Normal file
49
blender_kitsu/shot_builder/anim_setup/ops.py
Normal file
@ -0,0 +1,49 @@
|
||||
import bpy
|
||||
from typing import Set
|
||||
from blender_kitsu.shot_builder.anim_setup.core import editorial_export_get_latest, animation_workspace_delete_others, split_viewport
|
||||
|
||||
|
||||
class ANIM_SETUP_OT_setup_workspaces(bpy.types.Operator):
|
||||
bl_idname = "anim_setup.setup_workspaces"
|
||||
bl_label = "Setup Workspace"
|
||||
bl_description = "Sets up the workspaces for the animation task"
|
||||
|
||||
def execute(self, context: bpy.types.Context) -> Set[str]:
|
||||
animation_workspace_delete_others(self, context)
|
||||
return {"FINISHED"}
|
||||
|
||||
class ANIM_SETUP_OT_split_viewport(bpy.types.Operator):
|
||||
bl_idname = "anim_setup.split_viewport"
|
||||
bl_label = "Split Viewport"
|
||||
bl_description = "Split smallest 3D View in current workspace"
|
||||
|
||||
def execute(self, context: bpy.types.Context) -> Set[str]:
|
||||
split_viewport(self, context)
|
||||
return {"FINISHED"}
|
||||
|
||||
class ANIM_SETUP_OT_load_latest_edit(bpy.types.Operator):
|
||||
bl_idname = "asset_setup.load_latest_edit"
|
||||
bl_label = "Load edit"
|
||||
bl_description = (
|
||||
"Loads latest edit from shot_preview_folder "
|
||||
"Shifts edit so current shot starts at 3d_in metadata shot key from Kitsu"
|
||||
)
|
||||
|
||||
def execute(self, context: bpy.types.Context) -> Set[str]:
|
||||
editorial_export_get_latest(self, context)
|
||||
return {"FINISHED"}
|
||||
|
||||
classes = [
|
||||
ANIM_SETUP_OT_setup_workspaces,
|
||||
ANIM_SETUP_OT_load_latest_edit,
|
||||
ANIM_SETUP_OT_split_viewport
|
||||
]
|
||||
|
||||
|
||||
def register():
|
||||
for cls in classes:
|
||||
bpy.utils.register_class(cls)
|
||||
|
||||
def unregister():
|
||||
for cls in classes:
|
||||
bpy.utils.unregister_class(cls)
|
@ -24,6 +24,7 @@ from blender_kitsu.shot_builder.project import ensure_loaded_production, get_act
|
||||
from blender_kitsu.shot_builder.builder import ShotBuilder
|
||||
from blender_kitsu.shot_builder.task_type import TaskType
|
||||
from blender_kitsu import prefs, cache
|
||||
from blender_kitsu.shot_builder.anim_setup.core import editorial_export_get_latest, animation_workspace_delete_others
|
||||
|
||||
_production_task_type_items: List[Tuple[str, str, str]] = []
|
||||
|
||||
@ -122,6 +123,11 @@ class SHOTBUILDER_OT_NewShotFile(bpy.types.Operator):
|
||||
{'ERROR'}, "Operator is not able to determine the project root directory. Check project root directiory is configured in 'Blender Kitsu' addon preferences.")
|
||||
return {'CANCELLED'}
|
||||
|
||||
if not addon_prefs.is_editorial_dir_valid:
|
||||
self.report(
|
||||
{'ERROR'}, "Shot builder is dependant on a valid editorial export path and file pattern. Check Preferences, errors appear in console")
|
||||
return {'CANCELLED'}
|
||||
|
||||
self.production_root = addon_prefs.project_root_dir
|
||||
self.production_name = project.name
|
||||
|
||||
@ -151,13 +157,6 @@ class SHOTBUILDER_OT_NewShotFile(bpy.types.Operator):
|
||||
{'ERROR'}, "Shot builder can only be started from the File menu. Shortcuts like CTRL-N don't work")
|
||||
return {'CANCELLED'}
|
||||
addon_prefs = bpy.context.preferences.addons["blender_kitsu"].preferences
|
||||
|
||||
if not addon_prefs.is_editorial_dir_valid:
|
||||
self.report(
|
||||
{'ERROR'}, "Shot builder is dependant on a valid editorial export path and file pattern. Check Preferences, errors appear in console")
|
||||
return {'CANCELLED'}
|
||||
|
||||
|
||||
ensure_loaded_production(context)
|
||||
production = get_active_production()
|
||||
shot_builder = ShotBuilder(
|
||||
@ -165,9 +164,10 @@ class SHOTBUILDER_OT_NewShotFile(bpy.types.Operator):
|
||||
shot_builder.create_build_steps()
|
||||
shot_builder.build()
|
||||
#Load EDIT
|
||||
bpy.ops.asset_setup.load_latest_edit()
|
||||
bpy.ops.kitsu.con_detect_context() #TODO CONFIRM AND CHECK IF OVERRIDE IS NEEDED
|
||||
editorial_export_get_latest(self, context)
|
||||
# Load Anim Workspace
|
||||
bpy.ops.anim_setup.setup_workspaces()
|
||||
animation_workspace_delete_others(self, context)
|
||||
shot = cache.shot_active_get()
|
||||
|
||||
# Initilize armatures
|
||||
|
Loading…
Reference in New Issue
Block a user