Blender Kitsu: Add Import Edit Render Operator for Shots #236
@ -6,6 +6,16 @@ from pathlib import Path
|
|||||||
|
|
||||||
def edit_export_get_latest(context: bpy.types.Context):
|
def edit_export_get_latest(context: bpy.types.Context):
|
||||||
"""Find latest export in editorial export directory"""
|
"""Find latest export in editorial export directory"""
|
||||||
|
|
||||||
|
files_list = edit_exports_get_all(context)
|
||||||
|
if len(files_list) >= 1:
|
||||||
|
files_list = sorted(files_list, reverse=True)
|
||||||
|
return files_list[0]
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def edit_exports_get_all(context: bpy.types.Context):
|
||||||
|
"""Find latest export in editorial export directory"""
|
||||||
addon_prefs = prefs.addon_prefs_get(context)
|
addon_prefs = prefs.addon_prefs_get(context)
|
||||||
|
|
||||||
edit_export_path = Path(addon_prefs.edit_export_dir)
|
edit_export_path = Path(addon_prefs.edit_export_dir)
|
||||||
@ -16,10 +26,7 @@ def edit_export_get_latest(context: bpy.types.Context):
|
|||||||
if f.is_file()
|
if f.is_file()
|
||||||
and edit_export_is_valid_edit_name(addon_prefs.edit_export_file_pattern, f.name)
|
and edit_export_is_valid_edit_name(addon_prefs.edit_export_file_pattern, f.name)
|
||||||
]
|
]
|
||||||
if len(files_list) >= 1:
|
return files_list
|
||||||
files_list = sorted(files_list, reverse=True)
|
|
||||||
return files_list[0]
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def edit_export_is_valid_edit_name(file_pattern: str, filename: str) -> bool:
|
def edit_export_is_valid_edit_name(file_pattern: str, filename: str) -> bool:
|
||||||
@ -31,3 +38,51 @@ def edit_export_is_valid_edit_name(file_pattern: str, filename: str) -> bool:
|
|||||||
if match:
|
if match:
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def edit_export_import_latest(
|
||||||
|
context: bpy.types.Context, shot
|
||||||
|
) -> list[bpy.types.Sequence]: # TODO add info to shot
|
||||||
|
"""Loads latest export from editorial department"""
|
||||||
|
addon_prefs = prefs.addon_prefs_get(context)
|
||||||
|
strip_channel = 1
|
||||||
|
latest_file = edit_export_get_latest(context)
|
||||||
|
if not latest_file:
|
||||||
|
return None
|
||||||
|
# Check if Kitsu server returned empty shot
|
||||||
|
if shot.id == '':
|
||||||
|
return None
|
||||||
|
strip_filepath = latest_file.as_posix()
|
||||||
|
strip_frame_start = addon_prefs.shot_builder_frame_offset
|
||||||
|
|
||||||
|
scene = context.scene
|
||||||
|
if not scene.sequence_editor:
|
||||||
|
scene.sequence_editor_create()
|
||||||
|
seq_editor = scene.sequence_editor
|
||||||
|
movie_strip = seq_editor.sequences.new_movie(
|
||||||
|
latest_file.name,
|
||||||
|
strip_filepath,
|
||||||
|
strip_channel + 1,
|
||||||
|
strip_frame_start,
|
||||||
|
fit_method="ORIGINAL",
|
||||||
|
)
|
||||||
|
sound_strip = seq_editor.sequences.new_sound(
|
||||||
|
latest_file.name,
|
||||||
|
strip_filepath,
|
||||||
|
strip_channel,
|
||||||
|
strip_frame_start,
|
||||||
|
)
|
||||||
|
new_strips = [movie_strip, sound_strip]
|
||||||
|
|
||||||
|
# Update shift frame range prop.
|
||||||
|
frame_in = shot.data.get("frame_in")
|
||||||
|
frame_3d_start = shot.get_3d_start()
|
||||||
|
frame_3d_offset = frame_3d_start - addon_prefs.shot_builder_frame_offset
|
||||||
|
edit_export_offset = addon_prefs.edit_export_frame_offset
|
||||||
|
|
||||||
|
# Set sequence strip start kitsu data.
|
||||||
|
for strip in new_strips:
|
||||||
|
strip.frame_start = (
|
||||||
|
-frame_in + (strip_frame_start * 2) + frame_3d_offset + edit_export_offset
|
||||||
|
)
|
||||||
|
return new_strips
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
import bpy
|
import bpy
|
||||||
|
from bpy.types import Sequence, Context
|
||||||
import os
|
import os
|
||||||
from typing import Set
|
from typing import Set, List
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from .. import cache, prefs, util
|
from .. import cache, prefs, util
|
||||||
from ..types import Task, TaskStatus
|
from ..types import Task, TaskStatus
|
||||||
from ..playblast.core import override_render_path, override_render_format
|
from ..playblast.core import override_render_path, override_render_format
|
||||||
from . import opsdata
|
from . import opsdata
|
||||||
from ..logger import LoggerFactory
|
from ..logger import LoggerFactory
|
||||||
|
from .core import edit_export_import_latest, edit_exports_get_all, edit_export_get_latest
|
||||||
|
|
||||||
logger = LoggerFactory.getLogger()
|
logger = LoggerFactory.getLogger()
|
||||||
|
|
||||||
@ -52,7 +54,7 @@ class KITSU_OT_edit_render_publish(bpy.types.Operator):
|
|||||||
cls.poll_message_set("Edit Render Directory is Invalid, see Add-On preferences")
|
cls.poll_message_set("Edit Render Directory is Invalid, see Add-On preferences")
|
||||||
return False
|
return False
|
||||||
if not addon_prefs.is_edit_render_pattern_valid:
|
if not addon_prefs.is_edit_render_pattern_valid:
|
||||||
cls.poll_message_set("Edit Export File Pattern is Invalid, see Add-On preferences")
|
cls.poll_message_set("Edit Render File Pattern is Invalid, see Add-On preferences")
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@ -195,10 +197,135 @@ class KITSU_OT_edit_render_increment_version(bpy.types.Operator):
|
|||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
class KITSU_OT_edit_render_import_latest(bpy.types.Operator):
|
||||||
|
bl_idname = "kitsu.edit_render_import_latest"
|
||||||
|
bl_label = "Import Latest Edit Render"
|
||||||
|
bl_description = "" # TODO
|
||||||
|
|
||||||
|
_existing_edit_renders = []
|
||||||
|
_removed_movie = 0
|
||||||
|
_removed_audio = 0
|
||||||
|
_latest_render_name = ""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context: bpy.types.Context) -> bool:
|
||||||
|
if not prefs.session_auth(context):
|
||||||
|
cls.poll_message_set("Login to a Kitsu Server")
|
||||||
|
return False
|
||||||
|
if not cache.project_active_get():
|
||||||
|
cls.poll_message_set("Select an active project")
|
||||||
|
return False
|
||||||
|
if cache.shot_active_get().id == "":
|
||||||
|
cls.poll_message_set("Please set an active shot in Kitsu Context UI")
|
||||||
|
return False
|
||||||
|
if not prefs.addon_prefs_get(context).is_edit_render_root_valid:
|
||||||
|
cls.poll_message_set("Edit Render Directory is Invalid, see Add-On Preferences")
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def get_filepath(self, strip):
|
||||||
|
if hasattr(strip, "filepath"):
|
||||||
|
return strip.filepath
|
||||||
|
if hasattr(strip, "sound"):
|
||||||
|
return strip.sound.filepath
|
||||||
|
|
||||||
|
def compare_strip_to_path(self, strip: Sequence, compare_path: Path) -> bool:
|
||||||
|
strip_path = Path(bpy.path.abspath(self.get_filepath(strip)))
|
||||||
|
return bool(compare_path.absolute() == strip_path.absolute())
|
||||||
|
|
||||||
|
def compare_strip_to_paths(self, strip: Sequence, compare_paths: List[Path]) -> bool:
|
||||||
|
for compare_path in compare_paths:
|
||||||
|
if self.compare_strip_to_path(strip, compare_path):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def get_existing_edit_renders(
|
||||||
|
self, context: Context, all_edit_render_paths: List[Path]
|
||||||
|
) -> List[Sequence]:
|
||||||
|
sequences = context.scene.sequence_editor.sequences
|
||||||
|
|
||||||
|
# Collect Existing Edit Renders
|
||||||
|
for strip in sequences:
|
||||||
|
if self.compare_strip_to_paths(strip, all_edit_render_paths):
|
||||||
|
self._existing_edit_renders.append(strip)
|
||||||
|
return self._existing_edit_renders
|
||||||
|
|
||||||
|
def check_if_latest_edit_render_is_imported(self, context: Context) -> bool:
|
||||||
|
# Check if latest edit render is already loaded.
|
||||||
|
for strip in self._existing_edit_renders:
|
||||||
|
latest_edit_render_path = edit_export_get_latest(context)
|
||||||
|
if self.compare_strip_to_path(strip, latest_edit_render_path):
|
||||||
|
self._latest_render_name = latest_edit_render_path.name
|
||||||
|
return True
|
||||||
|
|
||||||
|
def remove_existing_edit_renders(self, context: Context) -> None:
|
||||||
|
# Remove Existing Strips to make way for new Strip
|
||||||
|
sequences = context.scene.sequence_editor.sequences
|
||||||
|
for strip in self._existing_edit_renders:
|
||||||
|
if strip.type == "MOVIE":
|
||||||
|
self._removed_movie += 1
|
||||||
|
if strip.type == "SOUND":
|
||||||
|
self._removed_audio += 1
|
||||||
|
sequences.remove(strip)
|
||||||
|
|
||||||
|
def execute(self, context: bpy.types.Context) -> Set[str]:
|
||||||
|
# Reset Values
|
||||||
|
self._existing_edit_renders = []
|
||||||
|
self._removed_movie = 0
|
||||||
|
self._removed_audio = 0
|
||||||
|
self._latest_render_name = ""
|
||||||
|
|
||||||
|
addon_prefs = prefs.addon_prefs_get(context)
|
||||||
|
|
||||||
|
# Get paths to all edit renders
|
||||||
|
all_edit_render_paths = edit_exports_get_all(context)
|
||||||
|
if all_edit_render_paths == []:
|
||||||
|
self.report(
|
||||||
|
{"WARNING"},
|
||||||
|
f"No Edit Renders found in '{addon_prefs.edit_export_dir}' using pattern '{addon_prefs.edit_export_file_pattern}' See Add-On Preferences",
|
||||||
|
)
|
||||||
|
return {"CANCELLED"}
|
||||||
|
|
||||||
|
# Collect all existing edit renders
|
||||||
|
self.get_existing_edit_renders(context, all_edit_render_paths)
|
||||||
|
|
||||||
|
# Stop latest render is already imported
|
||||||
|
if self.check_if_latest_edit_render_is_imported(context):
|
||||||
|
self.report(
|
||||||
|
{"WARNING"},
|
||||||
|
f"Latest Editorial Render already loaded '{self._latest_render_name}'",
|
||||||
|
)
|
||||||
|
return {"CANCELLED"}
|
||||||
|
|
||||||
|
# Remove old edit renders
|
||||||
|
self.remove_existing_edit_renders(context)
|
||||||
|
|
||||||
|
# Import new edit render
|
||||||
|
shot = cache.shot_active_get()
|
||||||
|
strips = edit_export_import_latest(context, shot)
|
||||||
|
|
||||||
|
if strips is None:
|
||||||
|
self.report({"WARNING"}, f"Loaded Latest Editorial Render failed to import!")
|
||||||
|
return {"CANCELLED"}
|
||||||
|
|
||||||
|
# Report.
|
||||||
|
if self._removed_movie > 0 or self._removed_audio > 0:
|
||||||
|
removed_msg = (
|
||||||
|
f"Removed {self._removed_movie} Movie Strips and {self._removed_audio} Audio Strips"
|
||||||
|
)
|
||||||
|
self.report(
|
||||||
|
{"INFO"}, f"Loaded Latest Editorial Render, '{strips[0].name}'. {removed_msg}"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.report({"INFO"}, f"Loaded Latest Editorial Render, '{strips[0].name}'")
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
classes = [
|
classes = [
|
||||||
KITSU_OT_edit_render_publish,
|
KITSU_OT_edit_render_publish,
|
||||||
KITSU_OT_edit_render_set_version,
|
KITSU_OT_edit_render_set_version,
|
||||||
KITSU_OT_edit_render_increment_version,
|
KITSU_OT_edit_render_increment_version,
|
||||||
|
KITSU_OT_edit_render_import_latest,
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ from .ops import (
|
|||||||
KITSU_OT_edit_render_set_version,
|
KITSU_OT_edit_render_set_version,
|
||||||
KITSU_OT_edit_render_increment_version,
|
KITSU_OT_edit_render_increment_version,
|
||||||
KITSU_OT_edit_render_publish,
|
KITSU_OT_edit_render_publish,
|
||||||
|
KITSU_OT_edit_render_import_latest,
|
||||||
)
|
)
|
||||||
from ..generic.ops import KITSU_OT_open_path
|
from ..generic.ops import KITSU_OT_open_path
|
||||||
|
|
||||||
@ -71,9 +72,34 @@ class KITSU_PT_edit_render_publish(bpy.types.Panel):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
classes = [
|
class KITSU_PT_edit_render_tools(bpy.types.Panel):
|
||||||
KITSU_PT_edit_render_publish,
|
"""
|
||||||
]
|
Panel in sequence editor that exposes a set of tools that are used to load the latest edit
|
||||||
|
"""
|
||||||
|
|
||||||
|
bl_category = "Kitsu"
|
||||||
|
bl_label = "General Tools"
|
||||||
|
bl_space_type = "SEQUENCE_EDITOR"
|
||||||
|
bl_region_type = "UI"
|
||||||
|
bl_options = {"DEFAULT_CLOSED"}
|
||||||
|
bl_order = 50
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context: bpy.types.Context) -> bool:
|
||||||
|
if not prefs.session_auth(context):
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not (context_core.is_sequence_context() or context_core.is_shot_context()):
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def draw(self, context: bpy.types.Context) -> None:
|
||||||
|
box = self.layout.box()
|
||||||
|
box.label(text="General", icon="MODIFIER")
|
||||||
|
box.operator(KITSU_OT_edit_render_import_latest.bl_idname)
|
||||||
|
|
||||||
|
|
||||||
|
classes = [KITSU_PT_edit_render_publish, KITSU_PT_edit_render_tools]
|
||||||
|
|
||||||
|
|
||||||
def register():
|
def register():
|
||||||
|
@ -1,53 +0,0 @@
|
|||||||
import bpy
|
|
||||||
from .. import prefs
|
|
||||||
from pathlib import Path
|
|
||||||
import re
|
|
||||||
from ..edit import core as edit_core
|
|
||||||
|
|
||||||
|
|
||||||
def edit_export_import_latest(
|
|
||||||
context: bpy.types.Context, shot
|
|
||||||
) -> list[bpy.types.Sequence]: # TODO add info to shot
|
|
||||||
"""Loads latest export from editorial department"""
|
|
||||||
addon_prefs = prefs.addon_prefs_get(context)
|
|
||||||
strip_channel = 1
|
|
||||||
latest_file = edit_core.edit_export_get_latest(context)
|
|
||||||
if not latest_file:
|
|
||||||
return None
|
|
||||||
# Check if Kitsu server returned empty shot
|
|
||||||
if shot.id == '':
|
|
||||||
return None
|
|
||||||
strip_filepath = latest_file.as_posix()
|
|
||||||
strip_frame_start = addon_prefs.shot_builder_frame_offset
|
|
||||||
|
|
||||||
scene = context.scene
|
|
||||||
if not scene.sequence_editor:
|
|
||||||
scene.sequence_editor_create()
|
|
||||||
seq_editor = scene.sequence_editor
|
|
||||||
movie_strip = seq_editor.sequences.new_movie(
|
|
||||||
latest_file.name,
|
|
||||||
strip_filepath,
|
|
||||||
strip_channel + 1,
|
|
||||||
strip_frame_start,
|
|
||||||
fit_method="ORIGINAL",
|
|
||||||
)
|
|
||||||
sound_strip = seq_editor.sequences.new_sound(
|
|
||||||
latest_file.name,
|
|
||||||
strip_filepath,
|
|
||||||
strip_channel,
|
|
||||||
strip_frame_start,
|
|
||||||
)
|
|
||||||
new_strips = [movie_strip, sound_strip]
|
|
||||||
|
|
||||||
# Update shift frame range prop.
|
|
||||||
frame_in = shot.data.get("frame_in")
|
|
||||||
frame_3d_start = shot.get_3d_start()
|
|
||||||
frame_3d_offset = frame_3d_start - addon_prefs.shot_builder_frame_offset
|
|
||||||
edit_export_offset = addon_prefs.edit_export_frame_offset
|
|
||||||
|
|
||||||
# Set sequence strip start kitsu data.
|
|
||||||
for strip in new_strips:
|
|
||||||
strip.frame_start = (
|
|
||||||
-frame_in + (strip_frame_start * 2) + frame_3d_offset + edit_export_offset
|
|
||||||
)
|
|
||||||
return new_strips
|
|
@ -16,7 +16,7 @@ from .core import (
|
|||||||
)
|
)
|
||||||
from ..context import core as context_core
|
from ..context import core as context_core
|
||||||
|
|
||||||
from .editorial import edit_export_import_latest
|
from ..edit.core import edit_export_import_latest
|
||||||
from .file_save import save_shot_builder_file
|
from .file_save import save_shot_builder_file
|
||||||
from .template import replace_workspace_with_template
|
from .template import replace_workspace_with_template
|
||||||
from .assets import get_shot_assets
|
from .assets import get_shot_assets
|
||||||
|
Loading…
Reference in New Issue
Block a user