226 lines
7.1 KiB
Python
226 lines
7.1 KiB
Python
# SPDX-FileCopyrightText: 2021 Blender Studio Tools Authors
|
|
#
|
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
import re
|
|
|
|
from typing import Any, Dict, List, Tuple
|
|
from pathlib import Path
|
|
|
|
import bpy
|
|
from . import bkglobals
|
|
from . import cache, prefs
|
|
|
|
# TODO: restructure that to not import from anim.
|
|
from .playblast import ops as ops_playblast
|
|
from .playblast import opsdata as ops_playblast_data
|
|
from .logger import LoggerFactory
|
|
from .context import core as context_core
|
|
|
|
logger = LoggerFactory.getLogger()
|
|
|
|
|
|
# Get functions for window manager properties.
|
|
def _get_project_active(self):
|
|
return cache.project_active_get().name
|
|
|
|
|
|
def _resolve_pattern(pattern: str, var_lookup_table: Dict[str, str]) -> str:
|
|
matches = re.findall(r"\<(\w+)\>", pattern)
|
|
matches = list(set(matches))
|
|
# If no variable detected just return value.
|
|
if len(matches) == 0:
|
|
return pattern
|
|
else:
|
|
result = pattern
|
|
for to_replace in matches:
|
|
if to_replace in var_lookup_table:
|
|
to_insert = var_lookup_table[to_replace]
|
|
result = result.replace("<{}>".format(to_replace), to_insert)
|
|
else:
|
|
logger.warning("Failed to resolve variable: %s not defined!", to_replace)
|
|
return ""
|
|
return result
|
|
|
|
|
|
def _get_sequences(self: Any, context: bpy.types.Context) -> List[Tuple[str, str, str]]:
|
|
addon_prefs = bpy.context.preferences.addons[__package__].preferences
|
|
project_active = cache.project_active_get()
|
|
|
|
if not project_active or not addon_prefs.session.is_auth:
|
|
return [("None", "None", "")]
|
|
|
|
enum_list = [(s.name, s.name, "") for s in project_active.get_sequences_all()]
|
|
return enum_list
|
|
|
|
|
|
def _gen_shot_preview(self: Any) -> str:
|
|
addon_prefs = bpy.context.preferences.addons[__package__].preferences
|
|
shot_counter_increment = addon_prefs.shot_counter_increment
|
|
shot_counter_digits = addon_prefs.shot_counter_digits
|
|
shot_counter_start = self.shot_counter_start
|
|
shot_pattern = addon_prefs.shot_pattern
|
|
examples: List[str] = []
|
|
sequence = self.selected_sequence_name
|
|
episode = cache.episode_active_get()
|
|
var_project = (
|
|
self.var_project_custom if self.var_use_custom_project else self.var_project_active
|
|
)
|
|
var_sequence = self.var_sequence_custom if self.var_use_custom_seq else sequence
|
|
var_lookup_table = {
|
|
"Sequence": var_sequence,
|
|
"Project": var_project,
|
|
"Episode": episode.name,
|
|
}
|
|
|
|
for count in range(3):
|
|
counter_number = shot_counter_start + (shot_counter_increment * count)
|
|
counter = str(counter_number).rjust(shot_counter_digits, "0")
|
|
var_lookup_table["Counter"] = counter
|
|
examples.append(_resolve_pattern(shot_pattern, var_lookup_table))
|
|
|
|
return " | ".join(examples) + "..."
|
|
|
|
|
|
def get_task_type_name_file_suffix() -> str:
|
|
name = cache.task_type_active_get().name.lower()
|
|
|
|
task_mappings = {**bkglobals.SHOT_TASK_MAPPING, **bkglobals.SEQ_TASK_MAPPING}
|
|
for key, value in task_mappings.items():
|
|
if name == value.lower():
|
|
return key
|
|
|
|
return name
|
|
|
|
|
|
def get_playblast_dir(self: Any) -> str:
|
|
# shared/editorial/footage/{dev,pre,pro,post}
|
|
# shared/editorial/<episode>/footage/{dev,pre,pro,post}
|
|
|
|
# shared/editorial/footage//110_rextoria/110_0030/110_0030-anim
|
|
|
|
addon_prefs = prefs.addon_prefs_get(bpy.context)
|
|
if not addon_prefs.is_playblast_root_valid:
|
|
return ""
|
|
|
|
episode = cache.episode_active_get()
|
|
sequence = cache.sequence_active_get()
|
|
shot = cache.shot_active_get()
|
|
delimiter = bkglobals.DELIMITER
|
|
|
|
# Start building path
|
|
if context_core.is_sequence_context():
|
|
playblast_dir = addon_prefs.seq_playblast_root_path
|
|
else:
|
|
playblast_dir = addon_prefs.shot_playblast_root_path
|
|
|
|
playblast_dir = set_episode_variable(playblast_dir)
|
|
|
|
if context_core.is_sequence_context():
|
|
playblast_dir = playblast_dir / sequence.name / 'sequence_previews'
|
|
return playblast_dir.as_posix()
|
|
|
|
task_type_name_suffix = get_task_type_name_file_suffix()
|
|
|
|
playblast_dir = (
|
|
playblast_dir / sequence.name / shot.name / f"{shot.name}{delimiter}{task_type_name_suffix}"
|
|
)
|
|
return playblast_dir.as_posix()
|
|
|
|
|
|
def get_playblast_file(self: Any) -> str:
|
|
if not self.playblast_dir:
|
|
return ""
|
|
|
|
task_type_name_suffix = get_task_type_name_file_suffix()
|
|
version = self.playblast_version
|
|
shot = cache.shot_active_get()
|
|
episode = cache.episode_active_get()
|
|
sequence = cache.sequence_active_get()
|
|
delimiter = bkglobals.DELIMITER
|
|
|
|
# 070_0010_A-anim-v001.mp4.
|
|
|
|
kitsu_props = bpy.context.scene.kitsu
|
|
kitsu_props.get("category")
|
|
|
|
if context_core.is_sequence_context():
|
|
# Assuming sequences called 000_name, get 000
|
|
entity_name = sequence.name.split('_')[0]
|
|
if episode:
|
|
# If episode is present, append to the name
|
|
entity_name = f"{episode.name}_{entity_name}"
|
|
elif context_core.is_shot_context():
|
|
entity_name = shot.name
|
|
else:
|
|
entity_name = ''
|
|
|
|
file_name = f"{entity_name}{delimiter}{task_type_name_suffix}{delimiter}{version}.mp4"
|
|
|
|
return Path(self.playblast_dir).joinpath(file_name).as_posix()
|
|
|
|
|
|
def get_edit_export_dir() -> str:
|
|
addon_prefs = prefs.addon_prefs_get(bpy.context)
|
|
return set_episode_variable(Path(addon_prefs.edit_export_dir))
|
|
|
|
|
|
def get_edit_export_file(self: Any) -> str:
|
|
addon_prefs = prefs.addon_prefs_get(bpy.context)
|
|
export_dir = get_edit_export_dir()
|
|
version = self.edit_export_version
|
|
file_pattern = addon_prefs.edit_export_file_pattern
|
|
file_name = file_pattern.replace('v###', version)
|
|
return export_dir.joinpath(file_name).as_posix()
|
|
|
|
|
|
_active_category_cache_init: bool = False
|
|
_active_category_cache: str = ""
|
|
|
|
|
|
def reset_task_type(self: Any, context: bpy.types.Context) -> None:
|
|
global _active_category_cache_init
|
|
global _active_category_cache
|
|
|
|
if not _active_category_cache_init:
|
|
_active_category_cache = self.category
|
|
_active_category_cache_init = True
|
|
return
|
|
|
|
if self.category == _active_category_cache:
|
|
return None
|
|
|
|
cache.task_type_active_reset(context)
|
|
_active_category_cache = self.category
|
|
return None
|
|
|
|
|
|
def on_shot_change(self: Any, context: bpy.types.Context) -> None:
|
|
# Reset versions.
|
|
ops_playblast_data.init_playblast_file_model(context)
|
|
|
|
# Check frame range.
|
|
ops_playblast.load_post_handler_check_frame_range(context)
|
|
|
|
|
|
def reset_all_kitsu_props(self: Any, context: bpy.types.Context) -> None:
|
|
cache.sequence_active_reset(context)
|
|
cache.asset_type_active_reset(context)
|
|
cache.shot_active_reset(context)
|
|
cache.asset_active_reset(context)
|
|
cache.episode_active_reset(context)
|
|
cache.task_type_active_reset(context)
|
|
|
|
|
|
def set_episode_variable(base_path: Path) -> Path:
|
|
episode = cache.episode_active_get()
|
|
active_project = cache.project_active_get()
|
|
if not (
|
|
episode
|
|
and '<episode>' in base_path.parts
|
|
and active_project.production_type == bkglobals.KITSU_TV_PROJECT
|
|
):
|
|
return base_path
|
|
i = base_path.parts.index('<episode>')
|
|
return Path(*base_path.parts[:i]).joinpath(episode.name, *base_path.parts[i + 1 :])
|