Blender Kitsu: Set Custom Thumbnail during Playblast #77

Merged
Nick Alberelli merged 12 commits from feature/custom-playblast-thumbnails into main 2023-06-15 21:26:54 +02:00
3 changed files with 71 additions and 85 deletions
Showing only changes of commit 3f0eb47f32 - Show all commits

View File

@ -411,6 +411,7 @@ def get_user_all_tasks() -> List[Task]:
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)

View File

@ -1,6 +1,8 @@
import string
import json
from .exception import TaskStatusNotFound
from . import client as raw
from .sorting import sort_by_name
from .helpers import (
@ -41,7 +43,9 @@ def all_task_types_for_project(project, client=default):
list: Task types stored in database.
"""
project = normalize_model_parameter(project)
task_types = raw.fetch_all("projects/%s/task-types" % project["id"], client=client)
task_types = raw.fetch_all(
"projects/%s/task-types" % project["id"], client=client
)
return sort_by_name(task_types)
@ -151,7 +155,7 @@ def all_tasks_for_edit(edit, relations=False, client=default):
edit = normalize_model_parameter(edit)
params = {}
if relations:
params = {"relations": "true"}
params = {"relations": "true"}
path = "edits/%s/tasks" % edit["id"]
tasks = raw.fetch_all(path, params, client=client)
return sort_by_name(tasks)
@ -271,7 +275,9 @@ def all_task_types_for_asset(asset, client=default):
list: Task types of tasks related to given asset.
"""
asset = normalize_model_parameter(asset)
task_types = raw.fetch_all("assets/%s/task-types" % asset["id"], client=client)
task_types = raw.fetch_all(
"assets/%s/task-types" % asset["id"], client=client
)
return sort_by_name(task_types)
@ -405,7 +411,9 @@ def get_task_type_by_name(task_type_name, client=default):
Returns:
dict: Task type object for given name.
"""
return raw.fetch_first("task-types", {"name": task_type_name}, client=client)
return raw.fetch_first(
"task-types", {"name": task_type_name}, client=client
)
@cache
@ -591,7 +599,9 @@ def start_task(task, started_task_status=None, client=default):
dict: Created comment.
"""
if started_task_status is None:
started_task_status = get_task_status_by_short_name("wip", client=client)
started_task_status = get_task_status_by_short_name(
"wip", client=client
)
if started_task_status is None:
raise TaskStatusNotFound(
(
@ -745,7 +755,9 @@ def add_comment(
data["created_at"] = created_at
if len(attachments) == 0:
return raw.post("actions/tasks/%s/comment" % task["id"], data, client=client)
return raw.post(
"actions/tasks/%s/comment" % task["id"], data, client=client
)
else:
attachment = attachments.pop()
data["checklist"] = json.dumps(checklist)
@ -758,7 +770,9 @@ def add_comment(
)
def add_attachment_files_to_comment(task, comment, attachments=[], client=default):
def add_attachment_files_to_comment(
task, comment, attachments=[], client=default
):
"""
Add attachments files to a given comment.
@ -778,7 +792,8 @@ def add_attachment_files_to_comment(task, comment, attachments=[], client=defaul
comment = normalize_model_parameter(comment)
attachment = attachments.pop()
return raw.upload(
"actions/tasks/%s/comments/%s/add-attachment" % (task["id"], comment["id"]),
"actions/tasks/%s/comments/%s/add-attachment"
% (task["id"], comment["id"]),
attachment,
extra_files=attachments,
client=client,
@ -830,7 +845,9 @@ def create_preview(task, comment, client=default):
return raw.post(path, {}, client=client)
def upload_preview_file(preview, file_path, normalize_movie=True, client=default):
def upload_preview_file(
preview, file_path, normalize_movie=True, client=default
):
"""
Create a preview into given comment.
@ -838,7 +855,9 @@ def upload_preview_file(preview, file_path, normalize_movie=True, client=default
task (str / dict): The task dict or the task ID.
file_path (str): Path of the file to upload as preview.
"""
path = "pictures/preview-files/%s" % normalize_model_parameter(preview)["id"]
path = (
"pictures/preview-files/%s" % normalize_model_parameter(preview)["id"]
)
if not normalize_movie:
path += "?normalize=false"
return raw.upload(path, file_path, client=client)
@ -877,21 +896,19 @@ def add_preview(
)
def set_main_preview(preview_file, frame_number, client=default):
def set_main_preview(preview_file, client=default):
"""
Set given preview as thumbnail of given entity.
Args:
preview_file (str / dict): The preview file dict or ID.
frame_number (int): Frame of preview video to set as main preview
Returns:
dict: Created preview file model.
"""
data = {"frame_number": frame_number} if frame_number > 1 else {}
preview_file = normalize_model_parameter(preview_file)
path = "actions/preview-files/%s/set-main-preview" % preview_file["id"]
return raw.put(path, data, client=client)
return raw.put(path, {}, client=client)
@cache
@ -983,7 +1000,9 @@ def update_task(task, client=default):
dict: Updated task.
"""
if "assignees" in task:
task["assignees"] = normalize_list_of_models_for_links(task["assignees"])
task["assignees"] = normalize_list_of_models_for_links(
task["assignees"]
)
return raw.put("data/tasks/%s" % task["id"], task, client=client)

View File

@ -25,8 +25,8 @@ from blender_kitsu.shot_builder.task_type import TaskType
from blender_kitsu.shot_builder.render_settings import RenderSettings
from blender_kitsu.shot_builder.connectors.connector import Connector
import requests
from blender_kitsu import cache
from blender_kitsu.gazu.asset import all_assets_for_shot
from blender_kitsu import cache
from blender_kitsu.gazu.asset import all_assets_for_shot
from blender_kitsu.gazu.shot import all_shots_for_project, all_sequences_for_project
import typing
@ -43,19 +43,16 @@ class KitsuPreferences(bpy.types.PropertyGroup):
backend: bpy.props.StringProperty( # type: ignore
name="Server URL",
description="Kitsu server address",
default="https://kitsu.blender.cloud/api",
)
default="https://kitsu.blender.cloud/api")
username: bpy.props.StringProperty( # type: ignore
name="Username",
description="Username to connect to Kitsu",
)
description="Username to connect to Kitsu",)
password: bpy.props.StringProperty( # type: ignore
name="Password",
description="Password to connect to Kitsu",
subtype='PASSWORD',
)
subtype='PASSWORD',)
def draw(self, layout: bpy.types.UILayout, context: bpy.types.Context) -> None:
layout.label(text="Kitsu")
@ -66,11 +63,10 @@ class KitsuPreferences(bpy.types.PropertyGroup):
def _validate(self):
if not (self.backend and self.username and self.password):
raise KitsuException(
"Kitsu connector has not been configured in the add-on preferences"
)
"Kitsu connector has not been configured in the add-on preferences")
class KitsuDataContainer:
class KitsuDataContainer():
def __init__(self, data: typing.Dict[str, typing.Optional[str]]):
self._data = data
@ -113,17 +109,7 @@ class KitsuSequenceRef(ShotRef):
class KitsuShotRef(ShotRef):
def __init__(
self,
kitsu_id: str,
name: str,
code: str,
frame_start: int,
frames: int,
frame_end: int,
frames_per_second: float,
sequence: KitsuSequenceRef,
):
def __init__(self, kitsu_id: str, name: str, code: str, frame_start: int, frames: int, frame_end: int, frames_per_second: float, sequence: KitsuSequenceRef):
super().__init__(name=name, code=code)
self.kitsu_id = kitsu_id
self.frame_start = frame_start
@ -151,7 +137,8 @@ class KitsuConnector(Connector):
def __get_production_data(self) -> KitsuProject:
production = cache.project_active_get()
project = KitsuProject(typing.cast(typing.Dict[str, typing.Any], production))
project = KitsuProject(typing.cast(
typing.Dict[str, typing.Any], production))
return project
def get_name(self) -> str:
@ -160,9 +147,8 @@ class KitsuConnector(Connector):
def get_task_types(self) -> typing.List[TaskType]:
project = cache.project_active_get()
task_types = project.task_types
task_types = project.task_types
import pprint
pprint.pprint(task_types)
return []
@ -170,76 +156,56 @@ class KitsuConnector(Connector):
project = cache.project_active_get()
kitsu_sequences = all_sequences_for_project(project.id)
sequence_lookup = {
sequence_data['id']: KitsuSequenceRef(
kitsu_id=sequence_data['id'],
name=sequence_data['name'],
code=sequence_data['code'],
)
for sequence_data in kitsu_sequences
}
sequence_lookup = {sequence_data['id']: KitsuSequenceRef(
kitsu_id=sequence_data['id'],
name=sequence_data['name'],
code=sequence_data['code'],
) for sequence_data in kitsu_sequences}
kitsu_shots = all_shots_for_project(project.id)
shots: typing.List[ShotRef] = []
for shot_data in kitsu_shots:
# Initialize default values
#Initialize default values
frame_start = vars.DEFAULT_FRAME_START
frame_end = 0
# shot_data['data'] can be None
if shot_data['data']:
# If 3d_in key not found use default start frame.
frame_start = int(
shot_data['data'].get('3d_in', vars.DEFAULT_FRAME_START)
)
frame_start = int(shot_data['data'].get('3d_in', vars.DEFAULT_FRAME_START))
frame_end = int(shot_data['data'].get('3d_out', 0))
# If 3d_in and 3d_out available use that to calculate frames.
# If not try shot_data['nb_frames'] or 0 -> invalid.
frames = int(
(frame_end - frame_start + 1)
if frame_end
else shot_data['nb_frames'] or 0
)
frames = int((frame_end - frame_start + 1) if frame_end else shot_data['nb_frames'] or 0)
if frames < 0:
logger.error(
"%s duration is negative: %i. Check frame range information on Kitsu",
shot_data['name'],
frames,
)
logger.error("%s duration is negative: %i. Check frame range information on Kitsu", shot_data['name'], frames)
frames = 0
shots.append(
KitsuShotRef(
kitsu_id=shot_data['id'],
name=shot_data['name'],
code=shot_data['code'],
frame_start=frame_start,
frames=frames,
frame_end=frame_end,
frames_per_second=24.0,
sequence=sequence_lookup[shot_data['parent_id']],
)
)
shots.append(KitsuShotRef(
kitsu_id=shot_data['id'],
name=shot_data['name'],
code=shot_data['code'],
frame_start=frame_start,
frames=frames,
frame_end = frame_end,
frames_per_second=24.0,
sequence=sequence_lookup[shot_data['parent_id']],
))
return shots
def get_assets_for_shot(self, shot: Shot) -> typing.List[AssetRef]:
kitsu_assets = all_assets_for_shot(shot.kitsu_id)
return [
AssetRef(name=asset_data['name'], code=asset_data['code'])
for asset_data in kitsu_assets
]
kitsu_assets = all_assets_for_shot(shot.kitsu_id)
return [AssetRef(name=asset_data['name'], code=asset_data['code'])
for asset_data in kitsu_assets]
def get_render_settings(self, shot: Shot) -> RenderSettings:
"""
Retrieve the render settings for the given shot.
"""
project = cache.project_active_get()
return RenderSettings(
width=int(project.resolution.split('x')[0]),
height=int(project.resolution.split('x')[1]),
frames_per_second=project.fps,
)
return RenderSettings(width=int(project.resolution.split('x')[0]), height=int(project.resolution.split('x')[1]), frames_per_second=project.fps)