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

832 lines
23 KiB
Python

# SPDX-FileCopyrightText: 2021 Blender Studio Tools Authors
#
# SPDX-License-Identifier: GPL-3.0-or-later
from typing import Any, List, Optional, Union, Dict, Tuple
import bpy
from bpy.app.handlers import persistent
from .types import (
Project,
Episode,
Edit,
Sequence,
Shot,
Asset,
AssetType,
TaskType,
Task,
ProjectList,
TaskStatus,
Cache,
User,
)
from .logger import LoggerFactory
import gazu
from .util import addon_prefs_get
logger = LoggerFactory.getLogger()
# CACHE VARIABLES
# used to cache active entities to prevent a new api request when read only
_project_active: Project = Project()
_episode_active: Episode = Episode()
_sequence_active: Sequence = Sequence()
_shot_active: Shot = Shot()
_asset_active: Asset = Asset()
_edit_active: Edit = Edit()
_asset_type_active: AssetType = AssetType()
_task_type_active: TaskType = TaskType()
_user_active: User = User()
_user_all_tasks: List[Task] = []
_cache_initialized: bool = False
_cache_startup_initialized: bool = False
_episodes_enum_list: List[Tuple[str, str, str]] = []
_sequence_enum_list: List[Tuple[str, str, str]] = []
_shot_enum_list: List[Tuple[str, str, str]] = []
_asset_types_enum_list: List[Tuple[str, str, str]] = []
_asset_enum_list: List[Tuple[str, str, str]] = []
_projects_enum_list: List[Tuple[str, str, str]] = []
_task_types_enum_list: List[Tuple[str, str, str]] = []
_task_types_shots_enum_list: List[Tuple[str, str, str]] = []
_task_statuses_enum_list: List[Tuple[str, str, str]] = []
_user_all_tasks_enum_list: List[Tuple[str, str, str]] = []
_all_edits_enum_list: List[Tuple[str, str, str]] = []
_asset_cache_proj_id: str = ""
_episode_cache_proj_id: str = ""
_all_shot_tasks_cache_proj_id: str = ""
_all_task_type_cache_proj_id: str = ""
_seq_cache_proj_id: str = ""
_seq_cache_episode_id: str = ""
_shot_cache_seq_id: str = ""
_task_type_cache_shot_id: str = ""
_asset_cache_asset_type_id: str = ''
_all_edits_cache_proj_id: str = ""
def user_active_get() -> User:
global _user_active
return _user_active
def project_active_get() -> Project:
global _project_active
return _project_active
def project_active_set_by_id(context: bpy.types.Context, entity_id: str) -> None:
global _project_active
_project_active = Project.by_id(entity_id)
addon_prefs_get(context).project_active_id = entity_id
logger.debug("Set active project to %s", _project_active.name)
def project_active_reset(context: bpy.types.Context) -> None:
global _project_active
_project_active = Project()
addon_prefs_get(context).project_active_id = ""
logger.debug("Reset active project")
def episode_active_get() -> Project:
global _episode_active
return _episode_active
def episode_active_set_by_id(context: bpy.types.Context, entity_id: str) -> None:
global _episode_active
_episode_active = Episode.by_id(entity_id)
addon_prefs_get(context).episode_active_id = entity_id
logger.debug("Set active episode to %s", _episode_active.name)
def episode_active_reset_entity():
global _episode_active
_episode_active = Episode()
def episode_active_reset(context: bpy.types.Context) -> None:
episode_active_reset_entity()
context.scene.kitsu.episode_active_id = ""
context.scene.kitsu.episode_active_name = ""
logger.debug("Reset active episode")
def sequence_active_get() -> Sequence:
return _sequence_active
def sequence_active_set_by_id(context: bpy.types.Context, entity_id: str) -> None:
global _sequence_active
_sequence_active = Sequence.by_id(entity_id)
context.scene.kitsu.sequence_active_id = entity_id
logger.debug("Set active sequence to %s", _sequence_active.name)
def sequence_active_reset_entity() -> None:
global _sequence_active
_sequence_active = Sequence()
def sequence_active_reset(context: bpy.types.Context) -> None:
sequence_active_reset_entity()
context.scene.kitsu.sequence_active_id = ""
context.scene.kitsu.sequence_active_name = ""
logger.debug("Reset active sequence")
def output_collection_name_get() -> str:
active_shot = shot_active_get()
task_type = task_type_active_get()
output_col_name = active_shot.get_output_collection_name(task_type.get_short_name())
return output_col_name
def shot_active_get() -> Shot:
global _shot_active
return _shot_active
def shot_active_pull_update() -> Shot:
global _shot_active
Cache.clear_all()
shot_active_id = bpy.context.scene.kitsu.shot_active_id
_init_cache_entity(shot_active_id, Shot, "_shot_active", "shot")
return _shot_active
def shot_active_set_by_id(context: bpy.types.Context, entity_id: str) -> None:
global _shot_active
_shot_active = Shot.by_id(entity_id)
context.scene.kitsu.shot_active_id = entity_id
logger.debug("Set active shot to %s", _shot_active.name)
def shot_active_reset_entity() -> None:
global _shot_active
_shot_active = Shot()
def shot_active_reset(context: bpy.types.Context) -> None:
shot_active_reset_entity()
context.scene.kitsu.shot_active_id = ""
context.scene.kitsu.shot_active_name = ""
logger.debug("Reset active shot")
def asset_active_get() -> Asset:
global _asset_active
return _asset_active
def asset_active_set_by_id(context: bpy.types.Context, entity_id: str) -> None:
global _asset_active
_asset_active = Asset.by_id(entity_id)
context.scene.kitsu.asset_active_id = entity_id
logger.debug("Set active asset to %s", _asset_active.name)
def asset_active_reset_entity() -> None:
global _asset_active
_asset_active = Asset()
def asset_active_reset(context: bpy.types.Context) -> None:
asset_active_reset_entity()
context.scene.kitsu.asset_active_id = ""
context.scene.kitsu.asset_active_name = ""
logger.debug("Reset active asset")
def asset_type_active_get() -> AssetType:
global _asset_type_active
return _asset_type_active
def asset_type_active_set_by_id(context: bpy.types.Context, entity_id: str) -> None:
global _asset_type_active
if _asset_type_active != AssetType.by_id(entity_id):
asset_active_reset(context)
_asset_type_active = AssetType.by_id(entity_id)
context.scene.kitsu.asset_type_active_id = entity_id
logger.debug("Set active asset type to %s", _asset_type_active.name)
def asset_type_active_reset_entity() -> None:
global _asset_type_active
_asset_type_active = AssetType()
def asset_type_active_reset(context: bpy.types.Context) -> None:
asset_type_active_reset_entity()
context.scene.kitsu.asset_type_active_id = ""
context.scene.kitsu.asset_type_active_name = ""
logger.debug("Reset active asset type")
def task_type_active_get() -> TaskType:
global _task_type_active
return _task_type_active
def task_type_active_set_by_id(context: bpy.types.Context, entity_id: str) -> None:
global _task_type_active
_task_type_active = TaskType.by_id(entity_id)
context.scene.kitsu.task_type_active_id = entity_id
logger.debug("Set active task type to %s", _task_type_active.name)
def task_type_active_reset_entity() -> None:
global _task_type_active
_task_type_active = TaskType()
def task_type_active_reset(context: bpy.types.Context) -> None:
task_type_active_reset_entity()
context.scene.kitsu.task_type_active_id = ""
context.scene.kitsu.task_type_active_name = ""
logger.debug("Reset active task type")
def edit_active_set_by_id(context: bpy.types.Context, entity_id: str) -> None:
global _edit_active
_edit_active = Edit.by_id(entity_id)
context.scene.kitsu.edit_active_id = entity_id
logger.debug("Set active edit to %s", _task_type_active.name)
def edit_active_reset_entity() -> None:
global _edit_active
_edit_active = TaskType()
def edit_active_get() -> Project:
global _edit_active
return _edit_active
def edit_default_get(create: bool = False, episode_id: Optional[str] = None) -> Edit:
global _edit_active
episode = Episode.by_id(episode_id) if episode_id else episode_active_get()
_edit_active = Edit.get_project_default_edit(
project_active_get(), create=create, episode=episode
)
return _edit_active
def task_type_active_reset(context: bpy.types.Context) -> None:
edit_active_reset_entity()
context.scene.kitsu.edit_active_id = ""
context.scene.kitsu.edit_active_name = ""
logger.debug("Reset active edit")
def get_projects_enum_list(
self: bpy.types.Operator, context: bpy.types.Context
) -> List[Tuple[str, str, str]]:
global _projects_enum_list
if not addon_prefs_get(context).session.is_auth():
return []
projectlist = ProjectList()
_projects_enum_list.clear()
_projects_enum_list.extend([(p.id, p.name, p.description or "") for p in projectlist.projects])
return _projects_enum_list
def get_episodes_enum_list(
self: bpy.types.Operator, context: bpy.types.Context
) -> List[Tuple[str, str, str]]:
global _episodes_enum_list
global _episode_cache_proj_id
if not addon_prefs_get(context).session.is_auth():
return []
project_active = project_active_get()
if not project_active:
return []
# Return Cached list if project hasn't changed
if _episode_cache_proj_id == project_active.id:
return _episodes_enum_list
# Update Cache Sequence ID
_episode_cache_proj_id = project_active.id
_episodes_enum_list.clear()
_episodes_enum_list.extend(
[(e.id, e.name, e.description or "") for e in project_active.get_episodes_all()]
)
return _episodes_enum_list
def reset_sequences_enum_list() -> None:
global _sequence_enum_list
global _seq_cache_proj_id
global _seq_cache_episode_id
_sequence_enum_list.clear()
_seq_cache_proj_id = ""
_seq_cache_episode_id = ""
def get_sequences_enum_list(
self: bpy.types.Operator, context: bpy.types.Context
) -> List[Tuple[str, str, str]]:
global _sequence_enum_list
global _seq_cache_proj_id
global _seq_cache_episode_id
project_active = project_active_get()
episode_active = episode_active_get()
if not project_active:
return []
# Return Cached list if project hasn't changed
if _seq_cache_proj_id == project_active.id and _seq_cache_episode_id == episode_active.id:
return _sequence_enum_list
# Update Cache Sequence ID
_seq_cache_proj_id = project_active.id
_sequence_enum_list.clear()
if episode_active:
_sequence_enum_list.extend(
[(s.id, s.name, s.description or "") for s in episode_active.get_sequences_all()]
)
else:
_sequence_enum_list.extend(
[(s.id, s.name, s.description or "") for s in project_active.get_sequences_all()]
)
return _sequence_enum_list
def get_shots_enum_for_active_seq(
self: bpy.types.Operator, context: bpy.types.Context
) -> List[Tuple[str, str, str]]:
global _shot_enum_list
global _shot_cache_seq_id
seq_active = sequence_active_get()
if not seq_active:
return []
# Return Cached list if sequence hasn't changed
if _shot_cache_seq_id == seq_active.id:
return _shot_enum_list
# Update Cache Sequence ID
_shot_cache_seq_id = seq_active.id
_shot_enum_list.clear()
_shot_enum_list.extend(
[(s.id, s.name, s.description or "") for s in seq_active.get_all_shots()]
)
return _shot_enum_list
def get_shots_enum_for_seq(
self: bpy.types.Operator, context: bpy.types.Context, sequence: Sequence
) -> List[Tuple[str, str, str]]:
global _shot_enum_list
_shot_enum_list.clear()
_shot_enum_list.extend([(s.id, s.name, s.description or "") for s in sequence.get_all_shots()])
return _shot_enum_list
def get_assetypes_enum_list(
self: bpy.types.Operator, context: bpy.types.Context
) -> List[Tuple[str, str, str]]:
global _asset_types_enum_list
global _asset_cache_proj_id
project_active = project_active_get()
if not project_active:
return []
# Return Cached list if project hasn't changed
if _asset_cache_proj_id == project_active.id:
return _asset_types_enum_list
# Update Cache Sequence ID
_asset_cache_proj_id = project_active.id
_asset_types_enum_list.clear()
_asset_types_enum_list.extend(
[(at.id, at.name, "") for at in project_active.get_all_asset_types()]
)
return _asset_types_enum_list
def get_assets_enum_for_active_asset_type(
self: bpy.types.Operator, context: bpy.types.Context
) -> List[Tuple[str, str, str]]:
global _asset_enum_list
global _asset_cache_asset_type_id
project_active = project_active_get()
asset_type_active = asset_type_active_get()
episode_active = episode_active_get()
if not project_active or not asset_type_active:
return []
if _asset_cache_asset_type_id == asset_type_active.id:
return _asset_enum_list
_asset_cache_asset_type_id = asset_type_active.id
all_assets = project_active.get_all_assets_for_type(asset_type_active)
_asset_enum_list.clear()
if not episode_active:
_asset_enum_list.extend([(a.id, a.name, a.description or "") for a in all_assets])
else:
episode_assets = filter(
lambda p: p.source_id == episode_active.id or p.source_id is None, all_assets
)
_asset_enum_list.extend([(a.id, a.name, a.description or "") for a in episode_assets])
return _asset_enum_list
def get_task_types_enum_for_current_context(
self: bpy.types.Operator, context: bpy.types.Context
) -> List[Tuple[str, str, str]]:
global _task_types_enum_list
global _project_active
# Import within function to avoid circular import
from .context import core as context_core
items = []
if context_core.is_shot_context():
items = [
(t.id, t.name, "")
for t in TaskType.all_shot_task_types()
if t.id in _project_active.task_types
]
if context_core.is_asset_context():
items = [
(t.id, t.name, "")
for t in TaskType.all_asset_task_types()
if t.id in _project_active.task_types
]
if context_core.is_sequence_context():
items = [
(t.id, t.name, "")
for t in TaskType.all_sequence_task_types()
if t.id in _project_active.task_types
]
if context_core.is_edit_context():
items = [
(t.id, t.name, "")
for t in TaskType.all_edit_task_types()
if t.id in _project_active.task_types
]
_task_types_enum_list.clear()
_task_types_enum_list.extend(items)
return _task_types_enum_list
def get_shot_task_types_enum(
self: bpy.types.Operator, context: bpy.types.Context
) -> List[Tuple[str, str, str]]:
# Returns all avaliable task types across all shots in the current project
global _task_types_shots_enum_list
global _all_shot_tasks_cache_proj_id
project_active = project_active_get()
# Return Cached list if project hasn't changed
if _all_shot_tasks_cache_proj_id == project_active.id:
return _task_types_shots_enum_list
# Update Cache project ID
_all_shot_tasks_cache_proj_id = project_active.id
items = [(t.id, t.name, "") for t in TaskType.all_shot_task_types() if t.id in project_active.task_types]
_task_types_shots_enum_list.clear()
_task_types_shots_enum_list.extend(items)
return _task_types_shots_enum_list
def get_shot_task_types_enum_for_shot( # TODO Rename
self: bpy.types.Operator, context: bpy.types.Context, shot: Shot
) -> List[Tuple[str, str, str]]:
global _task_types_shots_enum_list
global _task_type_cache_shot_id
# Return Cached list if shot hasn't changed
if _task_type_cache_shot_id == shot.id:
return _task_types_shots_enum_list
# Update Cache Sequence ID
_task_type_cache_shot_id = shot.id
items = [(t.id, t.name, "") for t in shot.get_all_task_types()]
_task_types_shots_enum_list.clear()
_task_types_shots_enum_list.extend(items)
return _task_types_shots_enum_list
def reset_all_edits_enum_for_active_project() -> None:
global _all_edits_cache_proj_id
global _all_edits_enum_list
_all_edits_cache_proj_id = None
_all_edits_enum_list.clear()
gazu.cache.clear_all()
def get_all_edits_enum_for_active_project(
self: bpy.types.Operator, context: bpy.types.Context
) -> List[Tuple[str, str, str]]:
global _project_active
global _all_edits_cache_proj_id
global _all_edits_enum_list
if _all_edits_cache_proj_id == _project_active.id:
return _all_edits_enum_list
# Update Cache Project ID
_all_edits_cache_proj_id = _project_active.id
items = [(t.id, t.name, "") for t in _project_active.get_all_edits()]
_all_edits_enum_list.clear()
_all_edits_enum_list.extend(items)
return _all_edits_enum_list
def get_all_task_statuses_enum(
self: bpy.types.Operator, context: bpy.types.Context
) -> List[Tuple[str, str, str]]:
global _task_statuses_enum_list
global _all_task_type_cache_proj_id
project_active = project_active_get()
# Return Cached list if project hasn't changed
if _all_task_type_cache_proj_id == project_active.id:
return _task_statuses_enum_list
# Update Cache project ID
_all_task_type_cache_proj_id = project_active.id
items = [(t.id, t.name, "") for t in TaskStatus.all_task_statuses()]
_task_statuses_enum_list.clear()
_task_statuses_enum_list.extend(items)
return _task_statuses_enum_list
def load_user_all_tasks(context: bpy.types.Context) -> List[Task]:
global _user_all_tasks
global _user_active
tasks = _user_active.all_tasks_to_do()
_user_all_tasks.clear()
_user_all_tasks.extend(tasks)
_update_tasks_collection_prop(context)
logger.debug("Loaded assigned tasks for: %s", _user_active.full_name)
return _user_all_tasks
def _update_tasks_collection_prop(context: bpy.types.Context) -> None:
global _user_all_tasks
addon_prefs = addon_prefs_get(bpy.context)
tasks_coll_prop = addon_prefs.tasks
# Get current index.
idx = context.window_manager.kitsu.tasks_index
# Clear all old tasks.
tasks_coll_prop.clear()
# Populate with new tasks.
for task in _user_all_tasks:
item = tasks_coll_prop.add()
item.id = task.id
item.entity_id = task.entity_id
item.entity_name = task.entity_name
item.task_type_id = task.task_type_id
item.task_type_name = task.task_type_name
# Update index.
idx = len(tasks_coll_prop) - 1
def get_user_all_tasks_enum(
self: bpy.types.Operator, context: bpy.types.Context
) -> List[Tuple[str, str, str]]:
global _user_all_tasks_enum_list
global _user_all_tasks
enum_items = [(t.id, t.name, "") for t in _user_all_tasks]
_user_all_tasks_enum_list.clear()
_user_all_tasks_enum_list.extend(enum_items)
return _user_all_tasks_enum_list
def get_user_all_tasks() -> List[Task]:
global _user_all_tasks
return _user_all_tasks
def _init_cache_entity(
entity_id: str, entity_type: Any, cache_variable_name: Any, cache_name: str
) -> None:
if entity_id:
try:
globals()[cache_variable_name] = entity_type.by_id(entity_id)
logger.debug(
"Initiated active %s cache to: %s",
cache_name,
globals()[cache_variable_name].name,
)
except gazu.exception.RouteNotFoundException:
logger.error(
"Failed to initialize active %s cache. ID not found on server: %s",
cache_name,
entity_id,
)
def init_startup_variables(context: bpy.types.Context) -> None:
addon_prefs = addon_prefs_get(context)
global _cache_startup_initialized
global _user_active
global _user_all_tasks
if not addon_prefs.session.is_auth():
logger.debug("Skip initiating startup cache. Session not authorized")
return
if _cache_startup_initialized:
logger.debug("Startup Cache already initiated")
return
# User.
_user_active = User()
logger.debug("Initiated active user cache to: %s", _user_active.full_name)
# User Tasks.
load_user_all_tasks(context)
logger.debug("Initiated active user tasks")
_cache_startup_initialized = True
def clear_startup_variables():
global _user_active
global _user_all_tasks
global _cache_startup_initialized
_user_active = User()
logger.debug("Cleared active user cache")
_user_all_tasks.clear()
_update_tasks_collection_prop(bpy.context)
logger.debug("Cleared active user all tasks cache")
_cache_startup_initialized = False
def init_cache_variables() -> None:
global _project_active
global _episode_active
global _sequence_active
global _shot_active
global _asset_active
global _asset_type_active
global _task_type_active
global _cache_initialized
addon_prefs = addon_prefs_get(bpy.context)
if not addon_prefs.session.is_auth():
logger.debug("Skip initiating cache. Session not authorized")
return
if _cache_initialized:
logger.debug("Cache already initiated")
return
project_active_id = addon_prefs.project_active_id
episode_active_id = bpy.context.scene.kitsu.episode_active_id
sequence_active_id = bpy.context.scene.kitsu.sequence_active_id
shot_active_id = bpy.context.scene.kitsu.shot_active_id
asset_active_id = bpy.context.scene.kitsu.asset_active_id
asset_type_active_id = bpy.context.scene.kitsu.asset_type_active_id
task_type_active_id = bpy.context.scene.kitsu.task_type_active_id
_init_cache_entity(project_active_id, Project, "_project_active", "project")
_init_cache_entity(episode_active_id, Episode, "_episode_active", "episode")
_init_cache_entity(sequence_active_id, Sequence, "_sequence_active", "sequence")
_init_cache_entity(asset_type_active_id, AssetType, "_asset_type_active", "asset type")
_init_cache_entity(shot_active_id, Shot, "_shot_active", "shot")
_init_cache_entity(asset_active_id, Asset, "_asset_active", "asset")
_init_cache_entity(task_type_active_id, TaskType, "_task_type_active", "task type")
_cache_initialized = True
def clear_cache_variables():
global _project_active
global _episode_active
global _sequence_active
global _shot_active
global _asset_active
global _asset_type_active
global _task_type_active
global _cache_initialized
_user_active = User()
logger.debug("Cleared active user cache")
_shot_active = Shot()
logger.debug("Cleared active shot cache")
_asset_active = Asset()
logger.debug("Cleared active asset cache")
_sequence_active = Sequence()
logger.debug("Cleared active aequence cache")
_asset_type_active = AssetType()
logger.debug("Cleared active asset type cache")
_project_active = Project()
logger.debug("Cleared active project cache")
_task_type_active = TaskType()
logger.debug("Cleared active task type cache")
_cache_initialized = False
@persistent
def load_post_handler_update_cache(dummy: Any) -> None:
clear_cache_variables()
init_cache_variables()
@persistent
def load_post_handler_init_startup_variables(dummy: Any) -> None:
init_startup_variables(bpy.context)
# ---------REGISTER ----------.
def register():
# Handlers.
bpy.app.handlers.load_post.append(load_post_handler_update_cache)
bpy.app.handlers.load_post.append(load_post_handler_init_startup_variables)
def unregister():
# Clear handlers.
bpy.app.handlers.load_post.remove(load_post_handler_init_startup_variables)
bpy.app.handlers.load_post.remove(load_post_handler_update_cache)