Blender Kitsu: Set Custom Thumbnail during Playblast #77
@ -411,6 +411,7 @@ def get_user_all_tasks() -> List[Task]:
|
|||||||
def _init_cache_entity(
|
def _init_cache_entity(
|
||||||
entity_id: str, entity_type: Any, cache_variable_name: Any, cache_name: str
|
entity_id: str, entity_type: Any, cache_variable_name: Any, cache_name: str
|
||||||
) -> None:
|
) -> None:
|
||||||
|
|
||||||
if entity_id:
|
if entity_id:
|
||||||
try:
|
try:
|
||||||
globals()[cache_variable_name] = entity_type.by_id(entity_id)
|
globals()[cache_variable_name] = entity_type.by_id(entity_id)
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import string
|
import string
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
from .exception import TaskStatusNotFound
|
||||||
|
|
||||||
from . import client as raw
|
from . import client as raw
|
||||||
from .sorting import sort_by_name
|
from .sorting import sort_by_name
|
||||||
from .helpers import (
|
from .helpers import (
|
||||||
@ -41,7 +43,9 @@ def all_task_types_for_project(project, client=default):
|
|||||||
list: Task types stored in database.
|
list: Task types stored in database.
|
||||||
"""
|
"""
|
||||||
project = normalize_model_parameter(project)
|
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)
|
return sort_by_name(task_types)
|
||||||
|
|
||||||
|
|
||||||
@ -271,7 +275,9 @@ def all_task_types_for_asset(asset, client=default):
|
|||||||
list: Task types of tasks related to given asset.
|
list: Task types of tasks related to given asset.
|
||||||
"""
|
"""
|
||||||
asset = normalize_model_parameter(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)
|
return sort_by_name(task_types)
|
||||||
|
|
||||||
|
|
||||||
@ -405,7 +411,9 @@ def get_task_type_by_name(task_type_name, client=default):
|
|||||||
Returns:
|
Returns:
|
||||||
dict: Task type object for given name.
|
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
|
@cache
|
||||||
@ -591,7 +599,9 @@ def start_task(task, started_task_status=None, client=default):
|
|||||||
dict: Created comment.
|
dict: Created comment.
|
||||||
"""
|
"""
|
||||||
if started_task_status is None:
|
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:
|
if started_task_status is None:
|
||||||
raise TaskStatusNotFound(
|
raise TaskStatusNotFound(
|
||||||
(
|
(
|
||||||
@ -745,7 +755,9 @@ def add_comment(
|
|||||||
data["created_at"] = created_at
|
data["created_at"] = created_at
|
||||||
|
|
||||||
if len(attachments) == 0:
|
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:
|
else:
|
||||||
attachment = attachments.pop()
|
attachment = attachments.pop()
|
||||||
data["checklist"] = json.dumps(checklist)
|
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.
|
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)
|
comment = normalize_model_parameter(comment)
|
||||||
attachment = attachments.pop()
|
attachment = attachments.pop()
|
||||||
return raw.upload(
|
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,
|
attachment,
|
||||||
extra_files=attachments,
|
extra_files=attachments,
|
||||||
client=client,
|
client=client,
|
||||||
@ -830,7 +845,9 @@ def create_preview(task, comment, client=default):
|
|||||||
return raw.post(path, {}, client=client)
|
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.
|
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.
|
task (str / dict): The task dict or the task ID.
|
||||||
file_path (str): Path of the file to upload as preview.
|
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:
|
if not normalize_movie:
|
||||||
path += "?normalize=false"
|
path += "?normalize=false"
|
||||||
return raw.upload(path, file_path, client=client)
|
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.
|
Set given preview as thumbnail of given entity.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
preview_file (str / dict): The preview file dict or ID.
|
preview_file (str / dict): The preview file dict or ID.
|
||||||
frame_number (int): Frame of preview video to set as main preview
|
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
dict: Created preview file model.
|
dict: Created preview file model.
|
||||||
"""
|
"""
|
||||||
data = {"frame_number": frame_number} if frame_number > 1 else {}
|
|
||||||
preview_file = normalize_model_parameter(preview_file)
|
preview_file = normalize_model_parameter(preview_file)
|
||||||
path = "actions/preview-files/%s/set-main-preview" % preview_file["id"]
|
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
|
@cache
|
||||||
@ -983,7 +1000,9 @@ def update_task(task, client=default):
|
|||||||
dict: Updated task.
|
dict: Updated task.
|
||||||
"""
|
"""
|
||||||
if "assignees" in 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)
|
return raw.put("data/tasks/%s" % task["id"], task, client=client)
|
||||||
|
|
||||||
|
|
||||||
|
@ -43,19 +43,16 @@ class KitsuPreferences(bpy.types.PropertyGroup):
|
|||||||
backend: bpy.props.StringProperty( # type: ignore
|
backend: bpy.props.StringProperty( # type: ignore
|
||||||
name="Server URL",
|
name="Server URL",
|
||||||
description="Kitsu server address",
|
description="Kitsu server address",
|
||||||
default="https://kitsu.blender.cloud/api",
|
default="https://kitsu.blender.cloud/api")
|
||||||
)
|
|
||||||
|
|
||||||
username: bpy.props.StringProperty( # type: ignore
|
username: bpy.props.StringProperty( # type: ignore
|
||||||
name="Username",
|
name="Username",
|
||||||
description="Username to connect to Kitsu",
|
description="Username to connect to Kitsu",)
|
||||||
)
|
|
||||||
|
|
||||||
password: bpy.props.StringProperty( # type: ignore
|
password: bpy.props.StringProperty( # type: ignore
|
||||||
name="Password",
|
name="Password",
|
||||||
description="Password to connect to Kitsu",
|
description="Password to connect to Kitsu",
|
||||||
subtype='PASSWORD',
|
subtype='PASSWORD',)
|
||||||
)
|
|
||||||
|
|
||||||
def draw(self, layout: bpy.types.UILayout, context: bpy.types.Context) -> None:
|
def draw(self, layout: bpy.types.UILayout, context: bpy.types.Context) -> None:
|
||||||
layout.label(text="Kitsu")
|
layout.label(text="Kitsu")
|
||||||
@ -66,11 +63,10 @@ class KitsuPreferences(bpy.types.PropertyGroup):
|
|||||||
def _validate(self):
|
def _validate(self):
|
||||||
if not (self.backend and self.username and self.password):
|
if not (self.backend and self.username and self.password):
|
||||||
raise KitsuException(
|
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]]):
|
def __init__(self, data: typing.Dict[str, typing.Optional[str]]):
|
||||||
self._data = data
|
self._data = data
|
||||||
|
|
||||||
@ -113,17 +109,7 @@ class KitsuSequenceRef(ShotRef):
|
|||||||
|
|
||||||
|
|
||||||
class KitsuShotRef(ShotRef):
|
class KitsuShotRef(ShotRef):
|
||||||
def __init__(
|
def __init__(self, kitsu_id: str, name: str, code: str, frame_start: int, frames: int, frame_end: int, frames_per_second: float, sequence: KitsuSequenceRef):
|
||||||
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)
|
super().__init__(name=name, code=code)
|
||||||
self.kitsu_id = kitsu_id
|
self.kitsu_id = kitsu_id
|
||||||
self.frame_start = frame_start
|
self.frame_start = frame_start
|
||||||
@ -151,7 +137,8 @@ class KitsuConnector(Connector):
|
|||||||
|
|
||||||
def __get_production_data(self) -> KitsuProject:
|
def __get_production_data(self) -> KitsuProject:
|
||||||
production = cache.project_active_get()
|
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
|
return project
|
||||||
|
|
||||||
def get_name(self) -> str:
|
def get_name(self) -> str:
|
||||||
@ -162,7 +149,6 @@ class KitsuConnector(Connector):
|
|||||||
project = cache.project_active_get()
|
project = cache.project_active_get()
|
||||||
task_types = project.task_types
|
task_types = project.task_types
|
||||||
import pprint
|
import pprint
|
||||||
|
|
||||||
pprint.pprint(task_types)
|
pprint.pprint(task_types)
|
||||||
return []
|
return []
|
||||||
|
|
||||||
@ -170,19 +156,17 @@ class KitsuConnector(Connector):
|
|||||||
project = cache.project_active_get()
|
project = cache.project_active_get()
|
||||||
kitsu_sequences = all_sequences_for_project(project.id)
|
kitsu_sequences = all_sequences_for_project(project.id)
|
||||||
|
|
||||||
sequence_lookup = {
|
sequence_lookup = {sequence_data['id']: KitsuSequenceRef(
|
||||||
sequence_data['id']: KitsuSequenceRef(
|
|
||||||
kitsu_id=sequence_data['id'],
|
kitsu_id=sequence_data['id'],
|
||||||
name=sequence_data['name'],
|
name=sequence_data['name'],
|
||||||
code=sequence_data['code'],
|
code=sequence_data['code'],
|
||||||
)
|
) for sequence_data in kitsu_sequences}
|
||||||
for sequence_data in kitsu_sequences
|
|
||||||
}
|
|
||||||
|
|
||||||
kitsu_shots = all_shots_for_project(project.id)
|
kitsu_shots = all_shots_for_project(project.id)
|
||||||
shots: typing.List[ShotRef] = []
|
shots: typing.List[ShotRef] = []
|
||||||
|
|
||||||
for shot_data in kitsu_shots:
|
for shot_data in kitsu_shots:
|
||||||
|
|
||||||
#Initialize default values
|
#Initialize default values
|
||||||
frame_start = vars.DEFAULT_FRAME_START
|
frame_start = vars.DEFAULT_FRAME_START
|
||||||
frame_end = 0
|
frame_end = 0
|
||||||
@ -190,28 +174,17 @@ class KitsuConnector(Connector):
|
|||||||
# shot_data['data'] can be None
|
# shot_data['data'] can be None
|
||||||
if shot_data['data']:
|
if shot_data['data']:
|
||||||
# If 3d_in key not found use default start frame.
|
# If 3d_in key not found use default start frame.
|
||||||
frame_start = int(
|
frame_start = int(shot_data['data'].get('3d_in', vars.DEFAULT_FRAME_START))
|
||||||
shot_data['data'].get('3d_in', vars.DEFAULT_FRAME_START)
|
|
||||||
)
|
|
||||||
frame_end = int(shot_data['data'].get('3d_out', 0))
|
frame_end = int(shot_data['data'].get('3d_out', 0))
|
||||||
|
|
||||||
# If 3d_in and 3d_out available use that to calculate frames.
|
# If 3d_in and 3d_out available use that to calculate frames.
|
||||||
# If not try shot_data['nb_frames'] or 0 -> invalid.
|
# If not try shot_data['nb_frames'] or 0 -> invalid.
|
||||||
frames = int(
|
frames = int((frame_end - frame_start + 1) if frame_end else shot_data['nb_frames'] or 0)
|
||||||
(frame_end - frame_start + 1)
|
|
||||||
if frame_end
|
|
||||||
else shot_data['nb_frames'] or 0
|
|
||||||
)
|
|
||||||
if frames < 0:
|
if frames < 0:
|
||||||
logger.error(
|
logger.error("%s duration is negative: %i. Check frame range information on Kitsu", shot_data['name'], frames)
|
||||||
"%s duration is negative: %i. Check frame range information on Kitsu",
|
|
||||||
shot_data['name'],
|
|
||||||
frames,
|
|
||||||
)
|
|
||||||
frames = 0
|
frames = 0
|
||||||
|
|
||||||
shots.append(
|
shots.append(KitsuShotRef(
|
||||||
KitsuShotRef(
|
|
||||||
kitsu_id=shot_data['id'],
|
kitsu_id=shot_data['id'],
|
||||||
name=shot_data['name'],
|
name=shot_data['name'],
|
||||||
code=shot_data['code'],
|
code=shot_data['code'],
|
||||||
@ -220,26 +193,19 @@ class KitsuConnector(Connector):
|
|||||||
frame_end = frame_end,
|
frame_end = frame_end,
|
||||||
frames_per_second=24.0,
|
frames_per_second=24.0,
|
||||||
sequence=sequence_lookup[shot_data['parent_id']],
|
sequence=sequence_lookup[shot_data['parent_id']],
|
||||||
)
|
))
|
||||||
)
|
|
||||||
|
|
||||||
return shots
|
return shots
|
||||||
|
|
||||||
def get_assets_for_shot(self, shot: Shot) -> typing.List[AssetRef]:
|
def get_assets_for_shot(self, shot: Shot) -> typing.List[AssetRef]:
|
||||||
kitsu_assets = all_assets_for_shot(shot.kitsu_id)
|
kitsu_assets = all_assets_for_shot(shot.kitsu_id)
|
||||||
|
|
||||||
return [
|
return [AssetRef(name=asset_data['name'], code=asset_data['code'])
|
||||||
AssetRef(name=asset_data['name'], code=asset_data['code'])
|
for asset_data in kitsu_assets]
|
||||||
for asset_data in kitsu_assets
|
|
||||||
]
|
|
||||||
|
|
||||||
def get_render_settings(self, shot: Shot) -> RenderSettings:
|
def get_render_settings(self, shot: Shot) -> RenderSettings:
|
||||||
"""
|
"""
|
||||||
Retrieve the render settings for the given shot.
|
Retrieve the render settings for the given shot.
|
||||||
"""
|
"""
|
||||||
project = cache.project_active_get()
|
project = cache.project_active_get()
|
||||||
return RenderSettings(
|
return RenderSettings(width=int(project.resolution.split('x')[0]), height=int(project.resolution.split('x')[1]), frames_per_second=project.fps)
|
||||||
width=int(project.resolution.split('x')[0]),
|
|
||||||
height=int(project.resolution.split('x')[1]),
|
|
||||||
frames_per_second=project.fps,
|
|
||||||
)
|
|
||||||
|
Loading…
Reference in New Issue
Block a user