Move Anim_Setup module into Blender_Kitsu #5

Merged
Nick Alberelli merged 27 commits from :feature/merge_anim_setup_into_blender_kitsu into master 2023-04-05 17:38:41 +02:00
7 changed files with 149 additions and 180 deletions
Showing only changes of commit 3b7e20091e - Show all commits

View File

@ -40,7 +40,7 @@ from blender_kitsu import (
ui, ui,
) )
from blender_kitsu.anim_setup import ops #TODO Fix Registraion
from blender_kitsu.logger import LoggerFactory, LoggerLevelManager from blender_kitsu.logger import LoggerFactory, LoggerLevelManager
@ -81,7 +81,6 @@ if _need_reload:
context.reload() context.reload()
tasks.reload() tasks.reload()
anim.reload() anim.reload()
#ops.reload()
def register(): def register():
@ -97,7 +96,7 @@ def register():
playblast.register() playblast.register()
anim.register() anim.register()
shot_builder.register() shot_builder.register()
ops.register()
LoggerLevelManager.configure_levels() LoggerLevelManager.configure_levels()
logger.info("Registered blender-kitsu") logger.info("Registered blender-kitsu")
@ -116,7 +115,6 @@ def unregister():
lookdev.unregister() lookdev.unregister()
playblast.unregister() playblast.unregister()
shot_builder.unregister() shot_builder.unregister()
ops.unregister()
LoggerLevelManager.restore_levels() LoggerLevelManager.restore_levels()

View File

@ -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)

View File

@ -40,6 +40,8 @@ from blender_kitsu.auth.ops import (
) )
from blender_kitsu.context.ops import KITSU_OT_con_productions_load from blender_kitsu.context.ops import KITSU_OT_con_productions_load
from blender_kitsu.lookdev.prefs import LOOKDEV_preferences from blender_kitsu.lookdev.prefs import LOOKDEV_preferences
from blender_kitsu.shot_builder.anim_setup.core import editorial_export_check_latest
logger = LoggerFactory.getLogger() 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: def init_playblast_file_model(self, context: bpy.types.Context) -> None:
ops_playblast_data.init_playblast_file_model(context) 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__ bl_idname = __package__
host: bpy.props.StringProperty( # type: ignore host: bpy.props.StringProperty( # type: ignore
@ -333,7 +313,6 @@ class KITSU_addon_preferences(bpy.types.AddonPreferences):
options={"HIDDEN", "SKIP_SAVE"}, options={"HIDDEN", "SKIP_SAVE"},
description="File pattern to search for latest editorial export. Typically '{proj_name}_v\d\d\d.mp4'", description="File pattern to search for latest editorial export. Typically '{proj_name}_v\d\d\d.mp4'",
default="petprojects_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 @property
def is_editorial_dir_valid(self) -> bool: def is_editorial_dir_valid(self) -> bool:
edit_export_path = Path(self.edit_export_dir) if editorial_export_check_latest(bpy.context) is None:
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:
logger.error( logger.error(
"Failed to initialize editorial export file model. Invalid path/pattern. Check addon preferences" "Failed to initialize editorial export file model. Invalid path/pattern. Check addon preferences"
) )
return False return False
logger.info("Initialized editorial export file model, successfully.")
return True return True
def session_get(context: bpy.types.Context) -> Session: def session_get(context: bpy.types.Context) -> Session:
""" """
Shortcut to get session from blender_kitsu addon preferences Shortcut to get session from blender_kitsu addon preferences

View File

@ -22,6 +22,7 @@ from blender_kitsu.shot_builder.ui import *
from blender_kitsu.shot_builder.connectors.kitsu import * from blender_kitsu.shot_builder.connectors.kitsu import *
from blender_kitsu.shot_builder.operators import * from blender_kitsu.shot_builder.operators import *
import bpy import bpy
from blender_kitsu.shot_builder.anim_setup import ops #TODO Fix Registraion
# import logging # import logging
# logging.basicConfig(level=logging.DEBUG) # logging.basicConfig(level=logging.DEBUG)
@ -48,9 +49,11 @@ def register():
for cls in classes: for cls in classes:
bpy.utils.register_class(cls) bpy.utils.register_class(cls)
bpy.types.TOPBAR_MT_file_new.append(topbar_file_new_draw_handler) bpy.types.TOPBAR_MT_file_new.append(topbar_file_new_draw_handler)
ops.register()
def unregister(): def unregister():
bpy.types.TOPBAR_MT_file_new.remove(topbar_file_new_draw_handler) bpy.types.TOPBAR_MT_file_new.remove(topbar_file_new_draw_handler)
for cls in classes: for cls in classes:
bpy.utils.unregister_class(cls) bpy.utils.unregister_class(cls)
ops.unregister()

View 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

View 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)

View File

@ -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.builder import ShotBuilder
from blender_kitsu.shot_builder.task_type import TaskType from blender_kitsu.shot_builder.task_type import TaskType
from blender_kitsu import prefs, cache 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]] = [] _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.") {'ERROR'}, "Operator is not able to determine the project root directory. Check project root directiory is configured in 'Blender Kitsu' addon preferences.")
return {'CANCELLED'} 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_root = addon_prefs.project_root_dir
self.production_name = project.name 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") {'ERROR'}, "Shot builder can only be started from the File menu. Shortcuts like CTRL-N don't work")
return {'CANCELLED'} return {'CANCELLED'}
addon_prefs = bpy.context.preferences.addons["blender_kitsu"].preferences 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) ensure_loaded_production(context)
production = get_active_production() production = get_active_production()
shot_builder = ShotBuilder( shot_builder = ShotBuilder(
@ -165,9 +164,10 @@ class SHOTBUILDER_OT_NewShotFile(bpy.types.Operator):
shot_builder.create_build_steps() shot_builder.create_build_steps()
shot_builder.build() shot_builder.build()
#Load EDIT #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 # Load Anim Workspace
bpy.ops.anim_setup.setup_workspaces() animation_workspace_delete_others(self, context)
shot = cache.shot_active_get() shot = cache.shot_active_get()
# Initilize armatures # Initilize armatures