Files
blender-portable-repo/scripts/addons/blender_kitsu/propsdata.py
T
2026-03-17 14:58:51 -06:00

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 :])