Blender Kitsu: Include Episode in Shot Builder Paths & Improve 3d_start #192
@ -34,7 +34,9 @@ Sets are more tricky to define, since they can differ even on a shot level. In p
|
|||||||
|
|
||||||
## Shots
|
## Shots
|
||||||
|
|
||||||
**Location:** `{project root}/pro/shots/{sequence number}/{shot identifier}/{shot identifier}-{task identifier}.blend`
|
**Standard Location:** `{project root}/pro/shots/{sequence number}/{shot identifier}/{shot identifier}-{task identifier}.blend`
|
||||||
|
|
||||||
|
**TV Show Location:** `{project root}/pro/shots/{episode identifier}/{sequence number}/{shot identifier}/{shot identifier}-{task identifier}.blend`
|
||||||
|
|
||||||
##
|
##
|
||||||
Example: `prop-dresser_wood.faded-modeling.png`
|
Example: `prop-dresser_wood.faded-modeling.png`
|
||||||
|
@ -49,7 +49,7 @@ class KITSU_PT_vi3d_anim_tools(bpy.types.Panel):
|
|||||||
def poll(cls, context: bpy.types.Context) -> bool:
|
def poll(cls, context: bpy.types.Context) -> bool:
|
||||||
return bool(
|
return bool(
|
||||||
prefs.session_auth(context)
|
prefs.session_auth(context)
|
||||||
and cache.task_type_active_get().name == 'Animation'
|
and cache.task_type_active_get().name in ['Animation', 'Layout']
|
||||||
)
|
)
|
||||||
|
|
||||||
def draw(self, context: bpy.types.Context) -> None:
|
def draw(self, context: bpy.types.Context) -> None:
|
||||||
|
@ -87,7 +87,6 @@ class KITSU_OT_con_episodes_load(bpy.types.Operator):
|
|||||||
return bool(prefs.session_auth(context) and cache.project_active_get())
|
return bool(prefs.session_auth(context) and cache.project_active_get())
|
||||||
|
|
||||||
def execute(self, context: bpy.types.Context) -> Set[str]:
|
def execute(self, context: bpy.types.Context) -> Set[str]:
|
||||||
|
|
||||||
# Store vars to check if project / seq / shot changed.
|
# Store vars to check if project / seq / shot changed.
|
||||||
zep_prev_id = cache.episode_active_get().id
|
zep_prev_id = cache.episode_active_get().id
|
||||||
|
|
||||||
@ -303,7 +302,17 @@ class KITSU_OT_con_detect_context(bpy.types.Operator):
|
|||||||
# Update kitsu metadata.
|
# Update kitsu metadata.
|
||||||
filepath = Path(bpy.data.filepath)
|
filepath = Path(bpy.data.filepath)
|
||||||
active_project = cache.project_active_get()
|
active_project = cache.project_active_get()
|
||||||
|
|
||||||
|
# TODO REFACTOR THIS WHOLE THING, BAD HACK
|
||||||
|
# Path is different for tvshow
|
||||||
|
if (
|
||||||
|
active_project.production_type == 'tvshow'
|
||||||
|
and filepath.parents[3].name == bkglobals.SHOT_DIR_NAME
|
||||||
|
):
|
||||||
|
category = filepath.parents[3].name
|
||||||
|
else:
|
||||||
category = filepath.parents[2].name
|
category = filepath.parents[2].name
|
||||||
|
|
||||||
item_group = filepath.parents[1].name
|
item_group = filepath.parents[1].name
|
||||||
item = filepath.parents[0].name
|
item = filepath.parents[0].name
|
||||||
item_task_type = filepath.stem.split(bkglobals.FILE_DELIMITER)[-1]
|
item_task_type = filepath.stem.split(bkglobals.FILE_DELIMITER)[-1]
|
||||||
|
@ -288,14 +288,9 @@ def get_frame_range(): # TODO return type
|
|||||||
|
|
||||||
# Pull update for shot.
|
# Pull update for shot.
|
||||||
cache.shot_active_pull_update()
|
cache.shot_active_pull_update()
|
||||||
if "3d_start" not in active_shot.data:
|
kitsu_3d_start = active_shot.get_3d_start()
|
||||||
logger.warning(
|
frame_in = kitsu_3d_start
|
||||||
"Failed to check frame range. Shot %s missing '3d_start' attribute on server",
|
frame_out = kitsu_3d_start + int(active_shot.nb_frames) - 1
|
||||||
active_shot.name,
|
|
||||||
)
|
|
||||||
return
|
|
||||||
frame_in = int(active_shot.data["3d_start"])
|
|
||||||
frame_out = int(active_shot.data["3d_start"]) + int(active_shot.nb_frames) - 1
|
|
||||||
return frame_in, frame_out
|
return frame_in, frame_out
|
||||||
|
|
||||||
|
|
||||||
|
@ -493,12 +493,11 @@ def draw_frame_range_warning(self, context):
|
|||||||
layout.label(
|
layout.label(
|
||||||
text="Frame Range on server does not match the active shot. Please 'pull' the correct frame range from the server"
|
text="Frame Range on server does not match the active shot. Please 'pull' the correct frame range from the server"
|
||||||
)
|
)
|
||||||
layout.label(
|
layout.label(text=f" File Frame Range: {context.scene.frame_start}-{context.scene.frame_end}")
|
||||||
text=f" File Frame Range: {context.scene.frame_start}-{context.scene.frame_end}"
|
|
||||||
)
|
|
||||||
if active_shot:
|
if active_shot:
|
||||||
|
kitsu_3d_start = active_shot.get_3d_start()
|
||||||
layout.label(
|
layout.label(
|
||||||
text=f' Server Frame Range: {int(active_shot.data["3d_start"])}-{int(active_shot.data["3d_start"]) + int(active_shot.nb_frames) - 1}'
|
text=f'Server Frame Range: {kitsu_3d_start}-{kitsu_3d_start + int(active_shot.nb_frames) - 1}'
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
layout.label(text=f' Server Frame Range: not found')
|
layout.label(text=f' Server Frame Range: not found')
|
||||||
|
@ -55,9 +55,7 @@ def remove_all_data():
|
|||||||
for obj in bpy.data.objects:
|
for obj in bpy.data.objects:
|
||||||
bpy.data.objects.remove(obj)
|
bpy.data.objects.remove(obj)
|
||||||
|
|
||||||
bpy.ops.outliner.orphans_purge(
|
bpy.ops.outliner.orphans_purge(do_local_ids=True, do_linked_ids=True, do_recursive=True)
|
||||||
do_local_ids=True, do_linked_ids=True, do_recursive=True
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def set_shot_scene(context: bpy.types.Context, scene_name: str) -> bpy.types.Scene:
|
def set_shot_scene(context: bpy.types.Context, scene_name: str) -> bpy.types.Scene:
|
||||||
@ -84,19 +82,12 @@ def set_resolution_and_fps(project: Project, scene: bpy.types.Scene):
|
|||||||
scene.render.resolution_percentage = 100
|
scene.render.resolution_percentage = 100
|
||||||
|
|
||||||
|
|
||||||
def get_3d_start(shot: Shot):
|
|
||||||
if shot.data and shot.data.get("3d_start"): # shot.data and
|
|
||||||
return int(shot.data.get("3d_start"))
|
|
||||||
else:
|
|
||||||
return bkglobals.FRAME_START
|
|
||||||
|
|
||||||
|
|
||||||
def set_frame_range(shot: Shot, scene: bpy.types.Scene):
|
def set_frame_range(shot: Shot, scene: bpy.types.Scene):
|
||||||
start_3d = get_3d_start(shot)
|
kitsu_start_3d = shot.get_3d_start()
|
||||||
scene.frame_start = start_3d
|
scene.frame_start = kitsu_start_3d
|
||||||
if not shot.nb_frames:
|
if not shot.nb_frames:
|
||||||
raise Exception(f"{shot.name} has missing frame duration information")
|
raise Exception(f"{shot.name} has missing frame duration information")
|
||||||
scene.frame_end = start_3d + shot.nb_frames - 1
|
scene.frame_end = kitsu_start_3d + shot.nb_frames - 1
|
||||||
|
|
||||||
|
|
||||||
def link_data_block(file_path: str, data_block_name: str, data_block_type: str):
|
def link_data_block(file_path: str, data_block_name: str, data_block_type: str):
|
||||||
@ -151,9 +142,7 @@ def link_camera_rig(
|
|||||||
output_collection.objects.link(camera_object)
|
output_collection.objects.link(camera_object)
|
||||||
return
|
return
|
||||||
|
|
||||||
collection_name = (
|
collection_name = "CA-camera_rig" # TODO Rename the asset itself, this breaks convention
|
||||||
"CA-camera_rig" # TODO Rename the asset itself, this breaks convention
|
|
||||||
)
|
|
||||||
|
|
||||||
override_camera_col = link_and_override_collection(
|
override_camera_col = link_and_override_collection(
|
||||||
file_path=path, collection_name=collection_name, scene=scene
|
file_path=path, collection_name=collection_name, scene=scene
|
||||||
@ -180,9 +169,7 @@ def create_task_type_output_collection(
|
|||||||
scene.collection.children.link(output_collection)
|
scene.collection.children.link(output_collection)
|
||||||
|
|
||||||
for view_layer in scene.view_layers:
|
for view_layer in scene.view_layers:
|
||||||
view_layer_output_collection = view_layer.layer_collection.children.get(
|
view_layer_output_collection = view_layer.layer_collection.children.get(output_col_name)
|
||||||
output_col_name
|
|
||||||
)
|
|
||||||
view_layer_output_collection.exclude = True
|
view_layer_output_collection.exclude = True
|
||||||
return output_collection
|
return output_collection
|
||||||
|
|
||||||
@ -192,11 +179,9 @@ def link_task_type_output_collections(shot: Shot, task_type: TaskType):
|
|||||||
if bkglobals.OUTPUT_COL_LINK_MAPPING.get(task_type_short_name) == None:
|
if bkglobals.OUTPUT_COL_LINK_MAPPING.get(task_type_short_name) == None:
|
||||||
return
|
return
|
||||||
for short_name in bkglobals.OUTPUT_COL_LINK_MAPPING.get(task_type_short_name):
|
for short_name in bkglobals.OUTPUT_COL_LINK_MAPPING.get(task_type_short_name):
|
||||||
external_filepath = shot.get_shot_filepath(bpy.context, short_name)
|
external_filepath = shot.get_filepath(bpy.context, short_name)
|
||||||
if not Path(external_filepath).exists():
|
if not Path(external_filepath).exists():
|
||||||
print(
|
print(f"Unable to link output collection for {Path(external_filepath).name}")
|
||||||
f"Unable to link output collection for {Path(external_filepath).name}"
|
|
||||||
)
|
|
||||||
file_path = external_filepath.__str__()
|
file_path = external_filepath.__str__()
|
||||||
colection_name = shot.get_output_collection_name(short_name)
|
colection_name = shot.get_output_collection_name(short_name)
|
||||||
link_data_block(file_path, colection_name, 'Collection')
|
link_data_block(file_path, colection_name, 'Collection')
|
||||||
|
@ -2,7 +2,6 @@ import bpy
|
|||||||
from .. import prefs
|
from .. import prefs
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import re
|
import re
|
||||||
from .core import get_3d_start
|
|
||||||
|
|
||||||
|
|
||||||
def editorial_export_get_latest(
|
def editorial_export_get_latest(
|
||||||
@ -41,7 +40,7 @@ def editorial_export_get_latest(
|
|||||||
|
|
||||||
# Update shift frame range prop.
|
# Update shift frame range prop.
|
||||||
frame_in = shot.data.get("frame_in")
|
frame_in = shot.data.get("frame_in")
|
||||||
frame_3d_start = get_3d_start(shot)
|
frame_3d_start = shot.get_3d_start()
|
||||||
frame_3d_offset = frame_3d_start - addon_prefs.shot_builder_frame_offset
|
frame_3d_offset = frame_3d_start - addon_prefs.shot_builder_frame_offset
|
||||||
edit_export_offset = addon_prefs.edit_export_frame_offset
|
edit_export_offset = addon_prefs.edit_export_frame_offset
|
||||||
|
|
||||||
|
@ -23,9 +23,7 @@ from .hooks import Hooks
|
|||||||
active_project = None
|
active_project = None
|
||||||
|
|
||||||
|
|
||||||
def get_shots_for_seq(
|
def get_shots_for_seq(self: Any, context: bpy.types.Context) -> List[Tuple[str, str, str]]:
|
||||||
self: Any, context: bpy.types.Context
|
|
||||||
) -> List[Tuple[str, str, str]]:
|
|
||||||
if self.seq_id != '':
|
if self.seq_id != '':
|
||||||
seq = active_project.get_sequence(self.seq_id)
|
seq = active_project.get_sequence(self.seq_id)
|
||||||
shot_enum = cache.get_shots_enum_for_seq(self, context, seq)
|
shot_enum = cache.get_shots_enum_for_seq(self, context, seq)
|
||||||
@ -34,9 +32,7 @@ def get_shots_for_seq(
|
|||||||
return [('NONE', "No Shots Found", '')]
|
return [('NONE', "No Shots Found", '')]
|
||||||
|
|
||||||
|
|
||||||
def get_tasks_for_shot(
|
def get_tasks_for_shot(self: Any, context: bpy.types.Context) -> List[Tuple[str, str, str]]:
|
||||||
self: Any, context: bpy.types.Context
|
|
||||||
) -> List[Tuple[str, str, str]]:
|
|
||||||
global active_project
|
global active_project
|
||||||
if not (self.shot_id == '' or self.shot_id == 'NONE'):
|
if not (self.shot_id == '' or self.shot_id == 'NONE'):
|
||||||
shot = active_project.get_shot(self.shot_id)
|
shot = active_project.get_shot(self.shot_id)
|
||||||
@ -186,9 +182,7 @@ class KITSU_OT_build_new_shot(bpy.types.Operator):
|
|||||||
|
|
||||||
self.production_name = project.name
|
self.production_name = project.name
|
||||||
|
|
||||||
return cast(
|
return cast(Set[str], context.window_manager.invoke_props_dialog(self, width=400))
|
||||||
Set[str], context.window_manager.invoke_props_dialog(self, width=400)
|
|
||||||
)
|
|
||||||
|
|
||||||
def _get_task_type_for_shot(self, context, shot):
|
def _get_task_type_for_shot(self, context, shot):
|
||||||
for task_type in shot.get_all_task_types():
|
for task_type in shot.get_all_task_types():
|
||||||
@ -202,13 +196,13 @@ class KITSU_OT_build_new_shot(bpy.types.Operator):
|
|||||||
shot = active_project.get_shot(self.shot_id)
|
shot = active_project.get_shot(self.shot_id)
|
||||||
task_type = self._get_task_type_for_shot(context, shot)
|
task_type = self._get_task_type_for_shot(context, shot)
|
||||||
task_type_short_name = task_type.get_short_name()
|
task_type_short_name = task_type.get_short_name()
|
||||||
shot_file_path_str = shot.get_shot_filepath(context, task_type_short_name)
|
shot_file_path_str = shot.get_filepath(context, task_type_short_name)
|
||||||
|
|
||||||
# Open Template File
|
# Open Template File
|
||||||
replace_workspace_with_template(context, task_type_short_name)
|
replace_workspace_with_template(context, task_type_short_name)
|
||||||
|
|
||||||
# Set Up Scene + Naming
|
# Set Up Scene + Naming
|
||||||
shot_task_name = shot.get_shot_task_name(task_type.get_short_name())
|
shot_task_name = shot.get_task_name(task_type.get_short_name())
|
||||||
scene = set_shot_scene(context, shot_task_name)
|
scene = set_shot_scene(context, shot_task_name)
|
||||||
remove_all_data()
|
remove_all_data()
|
||||||
set_resolution_and_fps(active_project, scene)
|
set_resolution_and_fps(active_project, scene)
|
||||||
@ -250,9 +244,7 @@ class KITSU_OT_build_new_shot(bpy.types.Operator):
|
|||||||
if self.save_file:
|
if self.save_file:
|
||||||
save_shot_builder_file(file_path=shot_file_path_str)
|
save_shot_builder_file(file_path=shot_file_path_str)
|
||||||
|
|
||||||
self.report(
|
self.report({"INFO"}, f"Successfully Built Shot:`{shot.name}` Task: `{task_type.name}`")
|
||||||
{"INFO"}, f"Successfully Built Shot:`{shot.name}` Task: `{task_type.name}`"
|
|
||||||
)
|
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1868,22 +1868,8 @@ class KITSU_OT_sqe_pull_edit(bpy.types.Operator):
|
|||||||
def _apply_strip_slip_from_shot(
|
def _apply_strip_slip_from_shot(
|
||||||
self, context: bpy.types.Context, strip: bpy.types.Sequence, shot: Shot
|
self, context: bpy.types.Context, strip: bpy.types.Sequence, shot: Shot
|
||||||
) -> None:
|
) -> None:
|
||||||
if "3d_start" not in shot.data:
|
|
||||||
logger.warning(
|
|
||||||
"%s no update to frame_start_offset. '3d_start' key not in shot.data",
|
|
||||||
shot.name,
|
|
||||||
)
|
|
||||||
return
|
|
||||||
|
|
||||||
if not shot.data["3d_start"]:
|
|
||||||
logger.warning(
|
|
||||||
"%s no update to frame_start_offset. '3d_start' key invalid value: %i",
|
|
||||||
shot.name,
|
|
||||||
shot.data["3d_start"],
|
|
||||||
)
|
|
||||||
return
|
|
||||||
# get offset
|
# get offset
|
||||||
offset = strip.kitsu_frame_start - int(shot.data["3d_start"])
|
offset = strip.kitsu_frame_start - int(shot.get_3d_start())
|
||||||
|
|
||||||
# Deselect everything.
|
# Deselect everything.
|
||||||
if context.selected_sequences:
|
if context.selected_sequences:
|
||||||
|
@ -33,11 +33,7 @@ logger = LoggerFactory.getLogger()
|
|||||||
def shot_meta(strip: bpy.types.Sequence, shot: Shot) -> None:
|
def shot_meta(strip: bpy.types.Sequence, shot: Shot) -> None:
|
||||||
# Update shot info.
|
# Update shot info.
|
||||||
|
|
||||||
# Only set 3d_start if none is found
|
kitsu_3d_start = shot.get_3d_start()
|
||||||
try:
|
|
||||||
kitsu_3d_start = shot.data["3d_start"]
|
|
||||||
except:
|
|
||||||
kitsu_3d_start = bkglobals.FRAME_START
|
|
||||||
shot.name = strip.kitsu.shot_name
|
shot.name = strip.kitsu.shot_name
|
||||||
shot.description = strip.kitsu.shot_description
|
shot.description = strip.kitsu.shot_description
|
||||||
shot.data["frame_in"] = strip.frame_final_start
|
shot.data["frame_in"] = strip.frame_final_start
|
||||||
|
@ -596,22 +596,36 @@ class Shot(Entity):
|
|||||||
gazu.shot.update_shot(asdict(self))
|
gazu.shot.update_shot(asdict(self))
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def get_shot_task_name(self, task_type_short_name: str) -> str: #
|
def get_3d_start(self) -> int:
|
||||||
|
try:
|
||||||
|
logger.info(f"3d_start not found on server, defaulting to '{bkglobals.FRAME_START}'")
|
||||||
|
return int(self.data["3d_start"])
|
||||||
|
except:
|
||||||
|
return bkglobals.FRAME_START
|
||||||
|
|
||||||
|
def get_task_name(self, task_type_short_name: str) -> str: #
|
||||||
return f"{self.name}{bkglobals.FILE_DELIMITER}{task_type_short_name}"
|
return f"{self.name}{bkglobals.FILE_DELIMITER}{task_type_short_name}"
|
||||||
|
|
||||||
def get_output_collection_name(self, task_type_short_name: str) -> str:
|
def get_output_collection_name(self, task_type_short_name: str) -> str:
|
||||||
return f"{self.get_shot_task_name(task_type_short_name)}{bkglobals.FILE_DELIMITER}output"
|
return f"{self.get_task_name(task_type_short_name)}{bkglobals.FILE_DELIMITER}output"
|
||||||
|
|
||||||
def get_shot_dir(self, context) -> str:
|
def get_dir(self, context) -> str:
|
||||||
project_root_dir = prefs.project_root_dir_get(context)
|
project_root_dir = prefs.project_root_dir_get(context)
|
||||||
all_shots_dir = project_root_dir.joinpath('pro').joinpath('shots')
|
all_shots_dir = project_root_dir.joinpath('pro').joinpath('shots')
|
||||||
|
|
||||||
|
# Add Episode to Path if avaliable
|
||||||
|
if self.episode_id:
|
||||||
|
base_dir = all_shots_dir.joinpath(self.episode_name)
|
||||||
|
else:
|
||||||
|
base_dir = all_shots_dir
|
||||||
|
|
||||||
seq = self.get_sequence()
|
seq = self.get_sequence()
|
||||||
shot_dir = all_shots_dir.joinpath(seq.name).joinpath(self.name)
|
shot_dir = base_dir.joinpath(seq.name).joinpath(self.name)
|
||||||
return shot_dir.__str__()
|
return shot_dir.__str__()
|
||||||
|
|
||||||
def get_shot_filepath(self, context, task_type_short_name: str) -> str:
|
def get_filepath(self, context, task_type_short_name: str) -> str:
|
||||||
file_name = self.get_shot_task_name(task_type_short_name) + '.blend'
|
file_name = self.get_task_name(task_type_short_name) + '.blend'
|
||||||
return Path(self.get_shot_dir(context)).joinpath(file_name).__str__()
|
return Path(self.get_dir(context)).joinpath(file_name).__str__()
|
||||||
|
|
||||||
def update_data(self, data: Dict[str, Any]) -> Shot:
|
def update_data(self, data: Dict[str, Any]) -> Shot:
|
||||||
gazu.shot.update_shot_data(asdict(self), data=data)
|
gazu.shot.update_shot_data(asdict(self), data=data)
|
||||||
|
Loading…
Reference in New Issue
Block a user