Blender Kitsu: Fix Gazu Module out of sync #119

Merged
Nick Alberelli merged 11 commits from :fix/restore-gazu-out-of-sync into main 2023-07-13 19:39:30 +02:00
Showing only changes of commit 7c5593fbce - Show all commits

View File

@ -30,31 +30,49 @@ 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 ( from blender_kitsu.shot_builder.anim_setup.core import (
animation_workspace_delete_others,
animation_workspace_vse_area_add,
)
from blender_kitsu.shot_builder.editorial.core import editorial_export_get_latest from blender_kitsu.shot_builder.editorial.core import editorial_export_get_latest
from blender_kitsu.shot_builder.builder.save_file import save_shot_builder_file from blender_kitsu.shot_builder.builder.save_file import save_shot_builder_file
_production_task_type_items: List[Tuple[str, str, str]] = [] _production_task_type_items: List[Tuple[str, str, str]] = []
def production_task_type_items(self: Any, context: bpy.types.Context) -> List[Tuple[str, str, str]]:
def production_task_type_items(
self: Any, context: bpy.types.Context
) -> List[Tuple[str, str, str]]:
global _production_task_type_items global _production_task_type_items
return _production_task_type_items return _production_task_type_items
_production_seq_id_items: List[Tuple[str, str, str]] = [] _production_seq_id_items: List[Tuple[str, str, str]] = []
def production_seq_id_items(self: Any, context: bpy.types.Context) -> List[Tuple[str, str, str]]:
def production_seq_id_items(
self: Any, context: bpy.types.Context
) -> List[Tuple[str, str, str]]:
global _production_seq_id_items global _production_seq_id_items
return _production_seq_id_items return _production_seq_id_items
_production_shots: List[ShotRef] = [] _production_shots: List[ShotRef] = []
def production_shots(self: Any, context: bpy.types.Context) -> List[Tuple[str, str, str]]:
def production_shots(
self: Any, context: bpy.types.Context
) -> List[Tuple[str, str, str]]:
global _production_shots global _production_shots
return _production_shots return _production_shots
_production_shot_id_items_for_seq: List[Tuple[str, str, str]] = [] _production_shot_id_items_for_seq: List[Tuple[str, str, str]] = []
def production_shot_id_items_for_seq(self: Any, context: bpy.types.Context) -> List[Tuple[str, str, str]]:
def production_shot_id_items_for_seq(
self: Any, context: bpy.types.Context
) -> List[Tuple[str, str, str]]:
global _production_shot_id_items_for_seq global _production_shot_id_items_for_seq
global _production_shot_id_items global _production_shot_id_items
@ -62,7 +80,8 @@ def production_shot_id_items_for_seq(self: Any, context: bpy.types.Context) -> L
return [] return []
shots_for_seq: List[Tuple(str, str, str)] = [ shots_for_seq: List[Tuple(str, str, str)] = [
(s.name, s.name, "") for s in _production_shots (s.name, s.name, "")
for s in _production_shots
if s.sequence.name == self.seq_id if s.sequence.name == self.seq_id
] ]
@ -71,14 +90,17 @@ def production_shot_id_items_for_seq(self: Any, context: bpy.types.Context) -> L
return _production_shot_id_items_for_seq return _production_shot_id_items_for_seq
def reset_shot_id_enum(self : Any, context: bpy.types.Context) -> None:
def reset_shot_id_enum(self: Any, context: bpy.types.Context) -> None:
production_shot_id_items_for_seq(self, context) production_shot_id_items_for_seq(self, context)
global _production_shot_id_items_for_seq global _production_shot_id_items_for_seq
if _production_shot_id_items_for_seq: if _production_shot_id_items_for_seq:
self.shot_id = _production_shot_id_items_for_seq[0][0] self.shot_id = _production_shot_id_items_for_seq[0][0]
class SHOTBUILDER_OT_NewShotFile(bpy.types.Operator): class SHOTBUILDER_OT_NewShotFile(bpy.types.Operator):
"""Build a new shot file""" """Build a new shot file"""
bl_idname = "shotbuilder.new_shot_file" bl_idname = "shotbuilder.new_shot_file"
bl_label = "New Production Shot File" bl_label = "New Production Shot File"
@ -88,14 +110,13 @@ class SHOTBUILDER_OT_NewShotFile(bpy.types.Operator):
_file_path = '' _file_path = ''
production_root: bpy.props.StringProperty( # type: ignore production_root: bpy.props.StringProperty( # type: ignore
name="Production Root", name="Production Root", description="Root of the production", subtype='DIR_PATH'
description="Root of the production", )
subtype='DIR_PATH')
production_name: bpy.props.StringProperty( # type: ignore production_name: bpy.props.StringProperty( # type: ignore
name="Production", name="Production",
description="Name of the production to create a shot file for", description="Name of the production to create a shot file for",
options=set() options=set(),
) )
seq_id: bpy.props.EnumProperty( # type: ignore seq_id: bpy.props.EnumProperty( # type: ignore
@ -114,7 +135,7 @@ class SHOTBUILDER_OT_NewShotFile(bpy.types.Operator):
task_type: bpy.props.EnumProperty( # type: ignore task_type: bpy.props.EnumProperty( # type: ignore
name="Task", name="Task",
description="Task to create the shot file for", description="Task to create the shot file for",
items=production_task_type_items items=production_task_type_items,
) )
auto_save: bpy.props.BoolProperty( auto_save: bpy.props.BoolProperty(
name="Save after building.", name="Save after building.",
@ -123,14 +144,14 @@ class SHOTBUILDER_OT_NewShotFile(bpy.types.Operator):
) )
def modal(self, context, event): def modal(self, context, event):
if event.type == 'TIMER' and not self._add_vse_area: if event.type == 'TIMER' and not self._add_vse_area:
# Show Storyboard/Animatic from VSE # Show Storyboard/Animatic from VSE
"""Running as Modal Event because functions within execute() function like """Running as Modal Event because functions within execute() function like
animation_workspace_delete_others() changed UI context that needs to be refreshed. animation_workspace_delete_others() changed UI context that needs to be refreshed.
https://docs.blender.org/api/current/info_gotcha.html#no-updates-after-changing-ui-context""" https://docs.blender.org/api/current/info_gotcha.html#no-updates-after-changing-ui-context
#TODO this is a hack, should be inherient to above builder """
#TODO fix during refactor # TODO this is a hack, should be inherient to above builder
# TODO fix during refactor
if self.task_type == 'anim': if self.task_type == 'anim':
animation_workspace_vse_area_add(context) animation_workspace_vse_area_add(context)
self._add_vse_area = True self._add_vse_area = True
@ -140,10 +161,15 @@ class SHOTBUILDER_OT_NewShotFile(bpy.types.Operator):
file_path = pathlib.Path() file_path = pathlib.Path()
try: try:
save_shot_builder_file(self._file_path) save_shot_builder_file(self._file_path)
self.report({"INFO"}, f"Saved Shot{self.shot_id} at {self._file_path}") self.report(
{"INFO"}, f"Saved Shot{self.shot_id} at {self._file_path}"
)
return {'FINISHED'} return {'FINISHED'}
except FileExistsError: except FileExistsError:
self.report({"ERROR"}, f"Cannot create a file/folder when that file/folder already exists {file_path}") self.report(
{"ERROR"},
f"Cannot create a file/folder when that file/folder already exists {file_path}",
)
return {'CANCELLED'} return {'CANCELLED'}
self.report({"INFO"}, f"Built Shot {self.shot_id}, file is not saved!") self.report({"INFO"}, f"Built Shot {self.shot_id}, file is not saved!")
return {'FINISHED'} return {'FINISHED'}
@ -156,35 +182,39 @@ class SHOTBUILDER_OT_NewShotFile(bpy.types.Operator):
if addon_prefs.session.is_auth() is False: if addon_prefs.session.is_auth() is False:
self.report( self.report(
{'ERROR'}, "Must be logged into Kitsu to continue. \nCheck login status in 'Blender Kitsu' addon preferences.") {'ERROR'},
"Must be logged into Kitsu to continue. \nCheck login status in 'Blender Kitsu' addon preferences.",
)
return {'CANCELLED'} return {'CANCELLED'}
if project.id == "": if project.id == "":
self.report( self.report(
{'ERROR'}, "Operator is not able to determine the Kitsu production's name. \nCheck project is selected in 'Blender Kitsu' addon preferences.") {'ERROR'},
"Operator is not able to determine the Kitsu production's name. \nCheck project is selected in 'Blender Kitsu' addon preferences.",
)
return {'CANCELLED'} return {'CANCELLED'}
if not addon_prefs.is_project_root_valid: if not addon_prefs.is_project_root_valid:
self.report( self.report(
{'ERROR'}, "Operator is not able to determine the project root directory. \nCheck project root directiory is configured in 'Blender Kitsu' addon preferences.") {'ERROR'},
"Operator is not able to determine the project root directory. \nCheck project root directiory is configured in 'Blender Kitsu' addon preferences.",
)
return {'CANCELLED'} 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
if not ensure_loaded_production(context): if not ensure_loaded_production(context):
self.report( self.report(
{'ERROR'}, "Shot builder configuration files not found in current project directory. \nCheck addon preferences to ensure project root contains shot_builder config.") {'ERROR'},
"Shot builder configuration files not found in current project directory. \nCheck addon preferences to ensure project root contains shot_builder config.",
)
return {'CANCELLED'} return {'CANCELLED'}
production = get_active_production() production = get_active_production()
global _production_task_type_items global _production_task_type_items
_production_task_type_items = production.get_task_type_items( _production_task_type_items = production.get_task_type_items(context=context)
context=context)
global _production_seq_id_items global _production_seq_id_items
_production_seq_id_items = production.get_seq_items(context=context) _production_seq_id_items = production.get_seq_items(context=context)
@ -192,7 +222,9 @@ class SHOTBUILDER_OT_NewShotFile(bpy.types.Operator):
global _production_shots global _production_shots
_production_shots = production.get_shots(context=context) _production_shots = production.get_shots(context=context)
return cast(Set[str], context.window_manager.invoke_props_dialog(self, width=400)) return cast(
Set[str], context.window_manager.invoke_props_dialog(self, width=400)
)
def execute(self, context: bpy.types.Context) -> Set[str]: def execute(self, context: bpy.types.Context) -> Set[str]:
addon_prefs = bpy.context.preferences.addons["blender_kitsu"].preferences addon_prefs = bpy.context.preferences.addons["blender_kitsu"].preferences
@ -201,7 +233,9 @@ class SHOTBUILDER_OT_NewShotFile(bpy.types.Operator):
wm.modal_handler_add(self) wm.modal_handler_add(self)
if not self.production_root: if not self.production_root:
self.report( self.report(
{'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'}
if self._built_shot: if self._built_shot:
@ -209,28 +243,34 @@ class SHOTBUILDER_OT_NewShotFile(bpy.types.Operator):
ensure_loaded_production(context) ensure_loaded_production(context)
production = get_active_production() production = get_active_production()
shot_builder = ShotBuilder( shot_builder = ShotBuilder(
context=context, production=production, shot_name=self.shot_id, task_type=TaskType(self.task_type)) context=context,
production=production,
shot_name=self.shot_id,
task_type=TaskType(self.task_type),
)
shot_builder.create_build_steps() shot_builder.create_build_steps()
shot_builder.build() shot_builder.build()
# Build Kitsu Context # Build Kitsu Context
sequence = gazu.shot.get_sequence_by_name(production.config['KITSU_PROJECT_ID'], self.seq_id) sequence = gazu.shot.get_sequence_by_name(
production.config['KITSU_PROJECT_ID'], self.seq_id
)
shot = gazu.shot.get_shot_by_name(sequence, self.shot_id) shot = gazu.shot.get_shot_by_name(sequence, self.shot_id)
#TODO this is a hack, should be inherient to above builder # TODO this is a hack, should be inherient to above builder
#TODO fix during refactor # TODO fix during refactor
if self.task_type == 'anim': if self.task_type == 'anim':
#Load EDIT # Load EDIT
editorial_export_get_latest(context, shot) editorial_export_get_latest(context, shot)
# Load Anim Workspace # Load Anim Workspace
animation_workspace_delete_others() animation_workspace_delete_others()
# Initilize armatures # Initilize armatures
for obj in [obj for obj in bpy.data.objects if obj.type == "ARMATURE"]: for obj in [obj for obj in bpy.data.objects if obj.type == "ARMATURE"]:
base_name = obj.name.split( base_name = obj.name.split(addon_prefs.shot_builder_armature_prefix)[-1]
addon_prefs.shot_builder_armature_prefix)[-1]
new_action = bpy.data.actions.new( new_action = bpy.data.actions.new(
f"{addon_prefs.shot_builder_action_prefix}{base_name}.{self.shot_id}.v001") f"{addon_prefs.shot_builder_action_prefix}{base_name}.{self.shot_id}.v001"
)
new_action.use_fake_user = True new_action.use_fake_user = True
obj.animation_data.action = new_action obj.animation_data.action = new_action
@ -247,7 +287,6 @@ class SHOTBUILDER_OT_NewShotFile(bpy.types.Operator):
self._built_shot = True self._built_shot = True
return {'RUNNING_MODAL'} return {'RUNNING_MODAL'}
def draw(self, context: bpy.types.Context) -> None: def draw(self, context: bpy.types.Context) -> None:
layout = self.layout layout = self.layout
row = layout.row() row = layout.row()