2025-12-01
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
# SPDX-FileCopyrightText: 2021 Blender Studio Tools Authors
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import importlib
|
||||
from ..edit import ops, ui
|
||||
|
||||
|
||||
# ---------REGISTER ----------.
|
||||
|
||||
|
||||
def reload():
|
||||
global ops
|
||||
global ui
|
||||
|
||||
ops = importlib.reload(ops)
|
||||
ui = importlib.reload(ui)
|
||||
|
||||
|
||||
def register():
|
||||
ops.register()
|
||||
ui.register()
|
||||
|
||||
|
||||
def unregister():
|
||||
ui.unregister()
|
||||
ops.unregister()
|
||||
@@ -0,0 +1,95 @@
|
||||
# SPDX-FileCopyrightText: 2021 Blender Studio Tools Authors
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import bpy
|
||||
from .. import prefs, propsdata
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def edit_export_get_latest(context: bpy.types.Context):
|
||||
"""Find latest render in editorial export directory"""
|
||||
|
||||
files_list = edit_export_get_all(context)
|
||||
if len(files_list) >= 1:
|
||||
files_list = sorted(files_list, reverse=True)
|
||||
return files_list[0]
|
||||
return None
|
||||
|
||||
|
||||
def edit_export_get_all(context: bpy.types.Context):
|
||||
"""Find all renders in editorial export directory"""
|
||||
addon_prefs = prefs.addon_prefs_get(context)
|
||||
|
||||
edit_export_path = propsdata.get_edit_export_dir()
|
||||
|
||||
if not edit_export_path.exists():
|
||||
print(f"Editorial export directory '{edit_export_path}' does not exist.")
|
||||
return []
|
||||
|
||||
files_list = [
|
||||
f
|
||||
for f in edit_export_path.iterdir()
|
||||
if f.is_file() and edit_export_is_valid_name(addon_prefs.edit_export_file_pattern, f.name)
|
||||
]
|
||||
return files_list
|
||||
|
||||
|
||||
def edit_export_is_valid_name(file_pattern: str, filename: str) -> bool:
|
||||
"""Verify file name matches file pattern set in preferences"""
|
||||
# Prevents un-expected matches
|
||||
file_pattern = re.escape(file_pattern)
|
||||
# Replace `#` with `\d` to represent digits
|
||||
match = re.search(file_pattern.replace('\#', '\d'), filename)
|
||||
if match:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def edit_export_import_latest(
|
||||
context: bpy.types.Context, shot
|
||||
) -> list[bpy.types.Strip]: # 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
|
||||
@@ -0,0 +1,355 @@
|
||||
# SPDX-FileCopyrightText: 2021 Blender Studio Tools Authors
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import bpy
|
||||
from bpy.types import Strip, Context
|
||||
import os
|
||||
from typing import Set, List
|
||||
from pathlib import Path
|
||||
from .. import cache, prefs, util, propsdata
|
||||
from ..types import Task, TaskStatus
|
||||
from ..playblast.core import override_render_path, override_render_format
|
||||
from . import opsdata
|
||||
from ..logger import LoggerFactory
|
||||
from .core import edit_export_import_latest, edit_export_get_all, edit_export_get_latest
|
||||
|
||||
logger = LoggerFactory.getLogger()
|
||||
|
||||
|
||||
class KITSU_OT_edit_export_publish(bpy.types.Operator):
|
||||
bl_idname = "kitsu.edit_export_publish"
|
||||
bl_label = "Export & Publish"
|
||||
bl_description = (
|
||||
"Renders current VSE Edit as .mp4"
|
||||
"Saves the set version to disk and uploads it to Kitsu with the specified "
|
||||
"comment and task type. Overrides some render settings during render. "
|
||||
)
|
||||
|
||||
task_status: bpy.props.EnumProperty(name="Task Status", items=cache.get_all_task_statuses_enum)
|
||||
comment: bpy.props.StringProperty(name="Comment")
|
||||
use_frame_start: bpy.props.BoolProperty(name="Submit update to 'frame_start'.", default=False)
|
||||
frame_start: bpy.props.IntProperty(
|
||||
name="Frame Start",
|
||||
description="Send an integer for the 'frame_start' value of the current Kitsu Edit. \nThis is used by Watchtower to pad the edit in the timeline.",
|
||||
default=0,
|
||||
)
|
||||
thumbnail_frame: bpy.props.IntProperty(
|
||||
name="Thumbnail Frame",
|
||||
description="Frame to use as the thumbnail on Kitsu",
|
||||
min=0,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context: bpy.types.Context) -> bool:
|
||||
kitsu_props = context.scene.kitsu
|
||||
addon_prefs = prefs.addon_prefs_get(context)
|
||||
if not prefs.session_auth(context):
|
||||
cls.poll_message_set("Login to a Kitsu Server")
|
||||
if not cache.project_active_get():
|
||||
cls.poll_message_set("Select an active project")
|
||||
return False
|
||||
if kitsu_props.edit_active_id == "" or cache.edit_active_get().id == "":
|
||||
cls.poll_message_set("Select an edit entity from Kitsu Context Menu")
|
||||
return False
|
||||
if kitsu_props.task_type_active_id == "":
|
||||
cls.poll_message_set("Select a task type from Kitsu Context Menu")
|
||||
return False
|
||||
if not addon_prefs.is_edit_export_root_valid:
|
||||
cls.poll_message_set("Edit Export Directory is Invalid, see Add-On preferences")
|
||||
return False
|
||||
if not addon_prefs.is_edit_export_pattern_valid:
|
||||
cls.poll_message_set("Edit Export File Pattern is Invalid, see Add-On preferences")
|
||||
return False
|
||||
return True
|
||||
|
||||
def invoke(self, context, event):
|
||||
self.thumbnail_frame = context.scene.frame_current
|
||||
|
||||
# Remove file name if set in render filepath
|
||||
dir_path = bpy.path.abspath(context.scene.render.filepath)
|
||||
if not os.path.isdir(Path(dir_path)):
|
||||
dir_path = Path(dir_path).parent
|
||||
self.render_dir = str(dir_path)
|
||||
|
||||
# 'frame_start' is optionally property appearing on all edit_entries for a project if it exists (used in watchtower)
|
||||
server_frame_start = cache.edit_active_get().get_frame_start()
|
||||
if server_frame_start is int:
|
||||
self.frame_start = server_frame_start
|
||||
self.use_frame_start = bool(server_frame_start is not None)
|
||||
return context.window_manager.invoke_props_dialog(self)
|
||||
|
||||
def draw(self, context: bpy.types.Context) -> None:
|
||||
layout = self.layout
|
||||
layout.prop(self, "comment")
|
||||
layout.prop(self, 'task_status')
|
||||
|
||||
# Only set `frame_start` if exists on current project
|
||||
if self.use_frame_start:
|
||||
layout.prop(self, "frame_start")
|
||||
layout.prop(self, "thumbnail_frame")
|
||||
|
||||
def execute(self, context: bpy.types.Context) -> Set[str]:
|
||||
edit_entity = cache.edit_active_get()
|
||||
task_type_entity = cache.task_type_active_get()
|
||||
kitsu_props = context.scene.kitsu
|
||||
|
||||
# Ensure thumbnail frame is not outside of frame range
|
||||
if self.thumbnail_frame not in range(
|
||||
0, (context.scene.frame_end - context.scene.frame_start) + 1
|
||||
):
|
||||
self.report(
|
||||
{"ERROR"},
|
||||
f"Thumbnail frame '{self.thumbnail_frame}' is outside of frame range ",
|
||||
)
|
||||
return {"CANCELLED"}
|
||||
|
||||
# Build render_path
|
||||
render_path = Path(kitsu_props.edit_export_file)
|
||||
render_path_str = render_path.as_posix()
|
||||
render_name = render_path.name
|
||||
render_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
if not render_path.parent.exists():
|
||||
self.report({"ERROR"}, f"Render path is not set to a directory. '{self.render_dir}'")
|
||||
return {"CANCELLED"}
|
||||
|
||||
# Render Sequence to .mp4
|
||||
with override_render_path(self, context, render_path_str):
|
||||
with override_render_format(self, context, enable_sequencer=True):
|
||||
bpy.ops.render.opengl(animation=True, sequencer=True)
|
||||
|
||||
# Create comment with video
|
||||
task_status = TaskStatus.by_id(self.task_status)
|
||||
task = Task.by_name(edit_entity, task_type_entity)
|
||||
comment = task.add_comment(
|
||||
task_status,
|
||||
comment=self.comment,
|
||||
)
|
||||
task.add_preview_to_comment(
|
||||
comment,
|
||||
render_path_str,
|
||||
self.thumbnail_frame,
|
||||
)
|
||||
# Update edit_entry's frame_start if 'frame_start' is found on server
|
||||
if self.use_frame_start:
|
||||
edit_entity.set_frame_start(self.frame_start)
|
||||
|
||||
self.report({"INFO"}, f"Submitted new comment 'Revision {kitsu_props.edit_export_version}'")
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class KITSU_OT_edit_export_set_version(bpy.types.Operator):
|
||||
bl_idname = "kitsu.edit_export_set_version"
|
||||
bl_label = "Version"
|
||||
bl_property = "versions"
|
||||
bl_description = (
|
||||
"Sets version of edit export. Warning triangle in ui "
|
||||
"indicates that version already exists on disk"
|
||||
)
|
||||
|
||||
versions: bpy.props.EnumProperty(
|
||||
items=opsdata.get_edit_export_versions_enum_list, name="Versions"
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context: bpy.types.Context) -> bool:
|
||||
addon_prefs = prefs.addon_prefs_get(context)
|
||||
if addon_prefs.edit_export_dir == "":
|
||||
cls.poll_message_set("Edit Export Directory is not set, check add-on preferences")
|
||||
return False
|
||||
if not addon_prefs.is_edit_export_root_valid:
|
||||
cls.poll_message_set("Edit Export Directory is Invalid, see Add-On preferences")
|
||||
return False
|
||||
return True
|
||||
|
||||
def execute(self, context: bpy.types.Context) -> Set[str]:
|
||||
kitsu_props = context.scene.kitsu
|
||||
version = self.versions
|
||||
|
||||
if not version:
|
||||
return {"CANCELLED"}
|
||||
|
||||
if kitsu_props.get('edit_export_version') == version:
|
||||
return {"CANCELLED"}
|
||||
|
||||
# Update global scene cache version prop.
|
||||
kitsu_props.edit_export_version = version
|
||||
logger.info("Set edit export version to %s", version)
|
||||
|
||||
# Redraw ui.
|
||||
util.ui_redraw()
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
def invoke(self, context: bpy.types.Context, event: bpy.types.Event) -> Set[str]:
|
||||
context.window_manager.invoke_search_popup(self) # type: ignore
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class KITSU_OT_edit_export_increment_version(bpy.types.Operator):
|
||||
bl_idname = "kitsu.edit_export_increment_version"
|
||||
bl_label = "Add Version Increment"
|
||||
bl_description = "Increment the edit export version by one"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context: bpy.types.Context) -> bool:
|
||||
return True
|
||||
|
||||
def execute(self, context: bpy.types.Context) -> Set[str]:
|
||||
# Incremenet version.
|
||||
version = opsdata.add_edit_export_version_increment(context)
|
||||
|
||||
# Update cache_version prop.
|
||||
context.scene.kitsu.edit_export_version = version
|
||||
|
||||
# Report.
|
||||
self.report({"INFO"}, f"Add edit export version {version}")
|
||||
|
||||
util.ui_redraw()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class KITSU_OT_edit_export_import_latest(bpy.types.Operator):
|
||||
bl_idname = "kitsu.edit_export_import_latest"
|
||||
bl_label = "Import Latest Edit Export"
|
||||
bl_description = (
|
||||
"Find and import the latest editorial render found in the Editorial Export Directory for the current shot. "
|
||||
"Will only Import if the latest export is not already imported. "
|
||||
"Will remove any previous exports currently in the file's Video Sequence Editor"
|
||||
)
|
||||
|
||||
_existing_edit_exports = []
|
||||
_removed_movie = 0
|
||||
_removed_audio = 0
|
||||
_latest_export_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_export_root_valid:
|
||||
cls.poll_message_set("Edit Export 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: Strip, 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: Strip, 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_exports(
|
||||
self, context: Context, all_edit_export_paths: List[Path]
|
||||
) -> List[Strip]:
|
||||
sequences = context.scene.sequence_editor.sequences
|
||||
|
||||
# Collect Existing Edit Export
|
||||
for strip in sequences:
|
||||
if self.compare_strip_to_paths(strip, all_edit_export_paths):
|
||||
self._existing_edit_exports.append(strip)
|
||||
return self._existing_edit_exports
|
||||
|
||||
def check_if_latest_edit_export_is_imported(self, context: Context) -> bool:
|
||||
# Check if latest edit export is already loaded.
|
||||
for strip in self._existing_edit_exports:
|
||||
latest_edit_export_path = edit_export_get_latest(context)
|
||||
if self.compare_strip_to_path(strip, latest_edit_export_path):
|
||||
self._latest_export_name = latest_edit_export_path.name
|
||||
return True
|
||||
|
||||
def remove_existing_edit_exports(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_exports:
|
||||
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_exports = []
|
||||
self._removed_movie = 0
|
||||
self._removed_audio = 0
|
||||
self._latest_export_name = ""
|
||||
|
||||
addon_prefs = prefs.addon_prefs_get(context)
|
||||
|
||||
# Get paths to all edit exports
|
||||
all_edit_export_paths = edit_export_get_all(context)
|
||||
if all_edit_export_paths == []:
|
||||
self.report(
|
||||
{"WARNING"},
|
||||
f"No Edit Exports 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 exports
|
||||
self.get_existing_edit_exports(context, all_edit_export_paths)
|
||||
|
||||
# Stop latest export is already imported
|
||||
if self.check_if_latest_edit_export_is_imported(context):
|
||||
self.report(
|
||||
{"WARNING"},
|
||||
f"Latest Editorial Export already loaded '{self._latest_export_name}'",
|
||||
)
|
||||
return {"CANCELLED"}
|
||||
|
||||
# Remove old edit exports
|
||||
self.remove_existing_edit_exports(context)
|
||||
|
||||
# Import new edit export
|
||||
shot = cache.shot_active_get()
|
||||
strips = edit_export_import_latest(context, shot)
|
||||
|
||||
if strips is None:
|
||||
self.report({"WARNING"}, f"Loaded Latest Editorial Export 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 Export, '{strips[0].name}'. {removed_msg}"
|
||||
)
|
||||
else:
|
||||
self.report({"INFO"}, f"Loaded Latest Editorial Export, '{strips[0].name}'")
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
classes = [
|
||||
KITSU_OT_edit_export_publish,
|
||||
KITSU_OT_edit_export_set_version,
|
||||
KITSU_OT_edit_export_increment_version,
|
||||
KITSU_OT_edit_export_import_latest,
|
||||
]
|
||||
|
||||
|
||||
def register():
|
||||
for cls in classes:
|
||||
bpy.utils.register_class(cls)
|
||||
|
||||
|
||||
def unregister():
|
||||
for cls in reversed(classes):
|
||||
bpy.utils.unregister_class(cls)
|
||||
@@ -0,0 +1,97 @@
|
||||
# SPDX-FileCopyrightText: 2021 Blender Studio Tools Authors
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from typing import Any, List, Tuple
|
||||
from pathlib import Path
|
||||
from .. import prefs, propsdata
|
||||
import bpy
|
||||
|
||||
from ..models import FileListModel
|
||||
from ..logger import LoggerFactory
|
||||
|
||||
EDIT_EXPORT_FILE_MODEL = FileListModel()
|
||||
_edit_export_enum_list: List[Tuple[str, str, str]] = []
|
||||
_edit_export_file_model_init: bool = False
|
||||
|
||||
logger = LoggerFactory.getLogger()
|
||||
|
||||
# TODO Compare to Playblast opsdata, maybe centralize some repeated code
|
||||
|
||||
|
||||
def init_edit_export_file_model(
|
||||
context: bpy.types.Context,
|
||||
) -> None:
|
||||
global EDIT_EXPORT_FILE_MODEL
|
||||
global _edit_export_file_model_init
|
||||
|
||||
addon_prefs = prefs.addon_prefs_get(context)
|
||||
kitsu_props = context.scene.kitsu
|
||||
edit_export_dir = propsdata.get_edit_export_dir()
|
||||
|
||||
# Is None if invalid.
|
||||
if addon_prefs.edit_export_dir == "" or not edit_export_dir.exists():
|
||||
logger.error(
|
||||
"Failed to initialize edit export file model. Invalid path. Check addon preferences"
|
||||
)
|
||||
return
|
||||
|
||||
EDIT_EXPORT_FILE_MODEL.filter_name = Path(kitsu_props.edit_export_file).name
|
||||
EDIT_EXPORT_FILE_MODEL.reset()
|
||||
EDIT_EXPORT_FILE_MODEL.root_path = edit_export_dir
|
||||
|
||||
if not EDIT_EXPORT_FILE_MODEL.versions:
|
||||
EDIT_EXPORT_FILE_MODEL.append_item("v001")
|
||||
# Update edit_export_version prop.
|
||||
kitsu_props.edit_export_version = "v001"
|
||||
|
||||
else:
|
||||
# Update edit_export_version prop.
|
||||
kitsu_props.edit_export_version = EDIT_EXPORT_FILE_MODEL.versions[0]
|
||||
|
||||
_edit_export_file_model_init = True
|
||||
|
||||
|
||||
def add_edit_export_version_increment(context: bpy.types.Context) -> str:
|
||||
# Init model if it did not happen.
|
||||
if not _edit_export_file_model_init:
|
||||
init_edit_export_file_model(context)
|
||||
|
||||
# Should be already sorted.
|
||||
versions = EDIT_EXPORT_FILE_MODEL.versions
|
||||
|
||||
if len(versions) > 0:
|
||||
latest_version = versions[0]
|
||||
increment = "v{:03}".format(int(latest_version.replace("v", "")) + 1)
|
||||
else:
|
||||
increment = "v001"
|
||||
|
||||
EDIT_EXPORT_FILE_MODEL.append_item(increment)
|
||||
return increment
|
||||
|
||||
|
||||
def get_edit_export_versions_enum_list(
|
||||
self: Any,
|
||||
context: bpy.types.Context,
|
||||
) -> List[Tuple[str, str, str]]:
|
||||
global _edit_export_enum_list
|
||||
global EDIT_EXPORT_FILE_MODEL
|
||||
global init_edit_export_file_model
|
||||
global _edit_export_file_model_init
|
||||
|
||||
# Init model if it did not happen.
|
||||
if not _edit_export_file_model_init:
|
||||
init_edit_export_file_model(context)
|
||||
|
||||
# Clear all versions in enum list.
|
||||
_edit_export_enum_list.clear()
|
||||
_edit_export_enum_list.extend(EDIT_EXPORT_FILE_MODEL.versions_as_enum_list)
|
||||
|
||||
return _edit_export_enum_list
|
||||
|
||||
|
||||
def add_version_custom(custom_version: str) -> None:
|
||||
global _edit_export_enum_list
|
||||
global EDIT_EXPORT_FILE_MODEL
|
||||
|
||||
EDIT_EXPORT_FILE_MODEL.append_item(custom_version)
|
||||
@@ -0,0 +1,116 @@
|
||||
# SPDX-FileCopyrightText: 2021 Blender Studio Tools Authors
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import bpy
|
||||
from .. import prefs, ui
|
||||
from ..context import core as context_core
|
||||
from pathlib import Path
|
||||
from .ops import (
|
||||
KITSU_OT_edit_export_set_version,
|
||||
KITSU_OT_edit_export_increment_version,
|
||||
KITSU_OT_edit_export_publish,
|
||||
KITSU_OT_edit_export_import_latest,
|
||||
)
|
||||
from ..generic.ops import KITSU_OT_open_path
|
||||
|
||||
|
||||
class KITSU_PT_edit_export_publish(bpy.types.Panel):
|
||||
"""
|
||||
Panel in sequence editor that exposes a set of tools that are used to export latest edit
|
||||
"""
|
||||
|
||||
bl_category = "Kitsu"
|
||||
bl_label = "Export & Publish"
|
||||
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:
|
||||
return bool(prefs.session_auth(context) and context_core.is_edit_context())
|
||||
|
||||
def draw(self, context: bpy.types.Context) -> None:
|
||||
addon_prefs = prefs.addon_prefs_get(context)
|
||||
layout = self.layout
|
||||
split_factor_small = 0.95
|
||||
|
||||
# # ERROR.
|
||||
if not addon_prefs.is_edit_export_root_valid:
|
||||
box = ui.draw_error_box(layout)
|
||||
ui.draw_error_invalid_edit_export_root_dir(box)
|
||||
|
||||
# Edit Export version op.
|
||||
row = layout.row(align=True)
|
||||
row.operator(
|
||||
KITSU_OT_edit_export_set_version.bl_idname,
|
||||
text=context.scene.kitsu.edit_export_version,
|
||||
icon="DOWNARROW_HLT",
|
||||
)
|
||||
# Edit Export increment version op.
|
||||
row.operator(
|
||||
KITSU_OT_edit_export_increment_version.bl_idname,
|
||||
text="",
|
||||
icon="ADD",
|
||||
)
|
||||
|
||||
# Edit Export op.
|
||||
row = layout.row(align=True)
|
||||
row.operator(KITSU_OT_edit_export_publish.bl_idname, icon="RENDER_ANIMATION")
|
||||
|
||||
# Edit Export path label.
|
||||
if Path(context.scene.kitsu.edit_export_file).exists():
|
||||
split = layout.split(factor=1 - split_factor_small, align=True)
|
||||
split.label(icon="ERROR")
|
||||
sub_split = split.split(factor=split_factor_small)
|
||||
sub_split.label(text=context.scene.kitsu.edit_export_file)
|
||||
sub_split.operator(
|
||||
KITSU_OT_open_path.bl_idname, icon="FILE_FOLDER", text=""
|
||||
).filepath = context.scene.kitsu.edit_export_file
|
||||
else:
|
||||
row = layout.row(align=True)
|
||||
row.label(text=context.scene.kitsu.edit_export_file)
|
||||
row.operator(KITSU_OT_open_path.bl_idname, icon="FILE_FOLDER", text="").filepath = (
|
||||
context.scene.kitsu.edit_export_file
|
||||
)
|
||||
|
||||
|
||||
class KITSU_PT_edit_export_tools(bpy.types.Panel):
|
||||
"""
|
||||
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_export_import_latest.bl_idname)
|
||||
|
||||
|
||||
classes = [KITSU_PT_edit_export_publish, KITSU_PT_edit_export_tools]
|
||||
|
||||
|
||||
def register():
|
||||
for cls in classes:
|
||||
bpy.utils.register_class(cls)
|
||||
|
||||
|
||||
def unregister():
|
||||
for cls in reversed(classes):
|
||||
bpy.utils.unregister_class(cls)
|
||||
Reference in New Issue
Block a user