Blender Kitsu: Fix Gazu Module out of sync #119
@ -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,12 +144,12 @@ 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 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':
|
||||||
@ -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,12 +243,18 @@ 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
|
||||||
@ -227,10 +267,10 @@ class SHOTBUILDER_OT_NewShotFile(bpy.types.Operator):
|
|||||||
|
|
||||||
# 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()
|
||||||
|
Loading…
Reference in New Issue
Block a user