Skip to content
This repository has been archived by the owner on Sep 20, 2024. It is now read-only.

Ftrack: Addiotional component metadata #3685

Merged
merged 5 commits into from
Aug 18, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 81 additions & 45 deletions openpype/modules/ftrack/plugins/publish/integrate_ftrack_instances.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import copy
import pyblish.api

from openpype.lib.openpype_version import get_openpype_version
from openpype.lib.transcoding import (
get_ffprobe_streams,
convert_ffprobe_fps_to_float,
Expand All @@ -20,6 +21,17 @@ class IntegrateFtrackInstance(pyblish.api.InstancePlugin):
label = "Integrate Ftrack Component"
families = ["ftrack"]

metadata_keys_to_label = {
"openpype_version": "OpenPype version",
"frame_start": "Frame start",
"frame_end": "Frame end",
"duration": "Duration",
"width": "Resolution width",
"height": "Resolution height",
"fps": "FPS",
"codec": "Codec"
}

family_mapping = {
"camera": "cam",
"look": "look",
Expand All @@ -43,6 +55,7 @@ class IntegrateFtrackInstance(pyblish.api.InstancePlugin):
}
keep_first_subset_name_for_review = True
asset_versions_status_profiles = {}
additional_metadata_keys = []

def process(self, instance):
self.log.debug("instance {}".format(instance))
Expand Down Expand Up @@ -105,7 +118,8 @@ def process(self, instance):
"component_data": None,
"component_path": None,
"component_location": None,
"component_location_name": None
"component_location_name": None,
"additional_data": {}
}

# Filter types of representations
Expand Down Expand Up @@ -152,6 +166,7 @@ def process(self, instance):
"name": "thumbnail"
}
thumbnail_item["thumbnail"] = True

# Create copy of item before setting location
src_components_to_add.append(copy.deepcopy(thumbnail_item))
# Create copy of first thumbnail
Expand Down Expand Up @@ -248,19 +263,15 @@ def process(self, instance):
first_thumbnail_component[
"asset_data"]["name"] = extended_asset_name

component_meta = self._prepare_component_metadata(
instance, repre, repre_path, True
)

# Change location
review_item["component_path"] = repre_path
# Change component data
review_item["component_data"] = {
# Default component name is "main".
"name": "ftrackreview-mp4",
"metadata": {
"ftr_meta": json.dumps(component_meta)
}
"metadata": self._prepare_component_metadata(
instance, repre, repre_path, True
)
}

if is_first_review_repre:
Expand Down Expand Up @@ -302,13 +313,9 @@ def process(self, instance):
component_data = copy_src_item["component_data"]
component_name = component_data["name"]
component_data["name"] = component_name + "_src"
component_meta = self._prepare_component_metadata(
component_data["metadata"] = self._prepare_component_metadata(
instance, repre, copy_src_item["component_path"], False
)
if component_meta:
component_data["metadata"] = {
"ftr_meta": json.dumps(component_meta)
}
component_list.append(copy_src_item)

# Add others representations as component
Expand All @@ -326,16 +333,12 @@ def process(self, instance):
):
other_item["asset_data"]["name"] = extended_asset_name

component_meta = self._prepare_component_metadata(
instance, repre, published_path, False
)
component_data = {
"name": repre["name"]
"name": repre["name"],
"metadata": self._prepare_component_metadata(
instance, repre, published_path, False
)
}
if component_meta:
component_data["metadata"] = {
"ftr_meta": json.dumps(component_meta)
}
other_item["component_data"] = component_data
other_item["component_location_name"] = unmanaged_location_name
other_item["component_path"] = published_path
Expand All @@ -354,6 +357,9 @@ def json_obj_parser(obj):
))
instance.data["ftrackComponentsList"] = component_list

def _collect_additional_metadata(self, streams):
pass

def _get_repre_path(self, instance, repre, only_published):
"""Get representation path that can be used for integration.
Expand Down Expand Up @@ -423,6 +429,11 @@ def _get_asset_version_status_name(self, instance):
def _prepare_component_metadata(
self, instance, repre, component_path, is_review
):
metadata = {}
if "openpype_version" in self.additional_metadata_keys:
label = self.metadata_keys_to_label["openpype_version"]
metadata[label] = get_openpype_version()

extension = os.path.splitext(component_path)[-1]
streams = []
try:
Expand All @@ -442,22 +453,32 @@ def _prepare_component_metadata(
# - exr is special case which can have issues with reading through
# ffmpegh but we want to set fps for it
if not video_streams and extension not in [".exr"]:
return {}
return metadata

stream_width = None
stream_height = None
stream_fps = None
frame_out = None
codec_label = None
for video_stream in video_streams:
codec_label = video_stream.get("codec_long_name")
if not codec_label:
codec_label = video_stream.get("codec")

if codec_label:
pix_fmt = video_stream.get("pix_fmt")
if pix_fmt:
codec_label += " ({})".format(pix_fmt)

tmp_width = video_stream.get("width")
tmp_height = video_stream.get("height")
if tmp_width and tmp_height:
stream_width = tmp_width
stream_height = tmp_height

input_framerate = video_stream.get("r_frame_rate")
duration = video_stream.get("duration")
if input_framerate is None or duration is None:
stream_duration = video_stream.get("duration")
if input_framerate is None or stream_duration is None:
continue
try:
stream_fps = convert_ffprobe_fps_to_float(
Expand All @@ -473,53 +494,68 @@ def _prepare_component_metadata(
stream_height = tmp_height

self.log.debug("FPS from stream is {} and duration is {}".format(
input_framerate, duration
input_framerate, stream_duration
))
frame_out = float(duration) * stream_fps
frame_out = float(stream_duration) * stream_fps
break

# Prepare FPS
instance_fps = instance.data.get("fps")
if instance_fps is None:
instance_fps = instance.context.data["fps"]

if not is_review:
output = {}
fps = stream_fps or instance_fps
if fps:
output["frameRate"] = fps
repre_fps = repre.get("fps")
if repre_fps is not None:
repre_fps = float(repre_fps)

if stream_width and stream_height:
output["width"] = int(stream_width)
output["height"] = int(stream_height)
return output
fps = stream_fps or repre_fps or instance_fps

# Prepare frame ranges
frame_start = repre.get("frameStartFtrack")
frame_end = repre.get("frameEndFtrack")
if frame_start is None or frame_end is None:
frame_start = instance.data["frameStart"]
frame_end = instance.data["frameEnd"]
duration = (frame_end - frame_start) + 1

for key, value in [
("fps", fps),
("frame_start", frame_start),
("frame_end", frame_end),
("duration", duration),
("width", stream_width),
("height", stream_height),
("fps", fps),
("codec", codec_label)
]:
if not value or key not in self.additional_metadata_keys:
continue
label = self.metadata_keys_to_label[key]
metadata[label] = value

fps = None
repre_fps = repre.get("fps")
if repre_fps is not None:
repre_fps = float(repre_fps)
if not is_review:
ftr_meta = {}
if fps:
ftr_meta["frameRate"] = fps

fps = stream_fps or repre_fps or instance_fps
if stream_width and stream_height:
ftr_meta["width"] = int(stream_width)
ftr_meta["height"] = int(stream_height)
metadata["ftr_meta"] = json.dumps(ftr_meta)
return metadata

# Frame end of uploaded video file should be duration in frames
# - frame start is always 0
# - frame end is duration in frames
if not frame_out:
frame_out = frame_end - frame_start + 1
frame_out = duration

# Ftrack documentation says that it is required to have
# 'width' and 'height' in review component. But with those values
# review video does not play.
component_meta = {
metadata["ftr_meta"] = json.dumps({
"frameIn": 0,
"frameOut": frame_out,
"frameRate": float(fps)
}

return component_meta
})
return metadata
3 changes: 2 additions & 1 deletion openpype/settings/defaults/project_settings/ftrack.json
Original file line number Diff line number Diff line change
Expand Up @@ -491,7 +491,8 @@
"usd": "usd"
},
"keep_first_subset_name_for_review": true,
"asset_versions_status_profiles": []
"asset_versions_status_profiles": [],
"additional_metadata_keys": []
},
"IntegrateFtrackFarmStatus": {
"farm_status_profiles": []
Expand Down
2 changes: 1 addition & 1 deletion openpype/settings/defaults/project_settings/shotgrid.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@
"step": "step"
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1039,6 +1039,22 @@
}
]
}
},
{
"key": "additional_metadata_keys",
"label": "Additional metadata keys on components",
"type": "enum",
"multiselection": true,
"enum_items": [
{"openpype_version": "OpenPype version"},
{"frame_start": "Frame start"},
{"frame_end": "Frame end"},
{"duration": "Duration"},
{"width": "Resolution width"},
{"height": "Resolution height"},
{"fps": "FPS"},
{"code": "Codec"}
]
}
]
},
Expand Down