Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Maya: Use resolution and frame range from task entity #39

Closed
Show file tree
Hide file tree
Changes from 1 commit
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
27 changes: 22 additions & 5 deletions client/ayon_core/hosts/maya/api/lib.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from maya import cmds, mel
from maya.api import OpenMaya

from ayon_api import get_task_by_name
from ayon_core.client import (
get_project,
get_asset_by_name,
Expand Down Expand Up @@ -2614,17 +2615,33 @@ def reset_scene_resolution():
project_name = get_current_project_name()
project_doc = get_project(project_name)
project_data = project_doc["data"]
asset_data = get_current_project_asset()["data"]
asset_data_root = get_current_project_asset()
asset_data = asset_data_root["data"]

task_name = get_current_task_name()
task_entity = get_task_by_name(
project_name, asset_data_root["_id"], task_name)

# Set project resolution
width_key = "resolutionWidth"
height_key = "resolutionHeight"
pixelAspect_key = "pixelAspect"

width = asset_data.get(width_key, project_data.get(width_key, 1920))
height = asset_data.get(height_key, project_data.get(height_key, 1080))
pixelAspect = asset_data.get(pixelAspect_key,
project_data.get(pixelAspect_key, 1))
# Get frame information from task entity
# NOTE: If there is no task override then the asset
# value is automatically returned instead
width = task_entity["attrib"].get(
width_key,
asset_data.get(
width_key, project_data.get(width_key, 1920)))
height = task_entity["attrib"].get(
height_key,
asset_data.get(
height_key, project_data.get(height_key, 1080)))
pixelAspect = task_entity["attrib"].get(
pixelAspect_key,
asset_data.get(
pixelAspect_key, project_data.get(pixelAspect_key, 1080)))
bradenjennings marked this conversation as resolved.
Show resolved Hide resolved

set_scene_resolution(width, height, pixelAspect)

Expand Down
52 changes: 40 additions & 12 deletions client/ayon_core/hosts/maya/plugins/publish/validate_frame_range.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,26 @@ def process(self, instance):
)
return

frame_start_handle = int(context.data.get("frameStartHandle"))
frame_end_handle = int(context.data.get("frameEndHandle"))
handle_start = int(context.data.get("handleStart"))
handle_end = int(context.data.get("handleEnd"))
frame_start = int(context.data.get("frameStart"))
frame_end = int(context.data.get("frameEnd"))
# Get frame information from task entity
# NOTE: If there is no task override then the asset
# value is automatically returned instead
task_entity = instance.context.data["taskEntity"]
frame_start_handle = task_entity["attrib"]["frameStart"] - \
task_entity["attrib"]["handleStart"]
frame_end_handle = task_entity["attrib"]["frameEnd"] + \
task_entity["attrib"]["handleEnd"]
handle_start = task_entity["attrib"]["handleStart"]
handle_end = task_entity["attrib"]["handleEnd"]
frame_start = task_entity["attrib"]["frameStart"]
frame_end = task_entity["attrib"]["frameEnd"]

# Get frame information from asset context
# frame_start_handle = int(context.data.get("frameStartHandle"))
# frame_end_handle = int(context.data.get("frameEndHandle"))
# handle_start = int(context.data.get("handleStart"))
# handle_end = int(context.data.get("handleEnd"))
# frame_start = int(context.data.get("frameStart"))
# frame_end = int(context.data.get("frameEnd"))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be nice to query this data in a separate class method so the logic isn't duplicated between here and repair

E.g.

        def get_instance_task_frame_range(instance):
            # Get frame information from task entity
            # NOTE: If there is no task override then the asset
            # value is automatically returned instead
            attrib = instance.context.data["taskEntity"]["attrib"]
            handle_start = attrib["handleStart"]
            handle_end = attrib["handleEnd"]
            frame_start = attrib["frameStart"]
            frame_end = attrib["frameEnd"]
            frame_start_handle = frame_start - handle_start
            frame_end_handle = frame_end + handle_end

We could potentially even improve the readability by having a FrameRange dataclass.
For example:

@dataclasses.dataclass
class FrameRange:
    """Frame range with handles.

    The frame range excludes the handles - and thus the handles are outside
    of the frame range. To get the inclusive start and end frame use
    `frame_start_handle` and `frame_end_handle`.

    """
    frame_start: int
    frame_end: int
    handle_start: int
    handle_end: int

    @property
    def frame_start_handle(self) -> int:
        return self.frame_start - self.handle_start

    @property
    def frame_end_handle(self) -> int:
        return self.frame_end + self.handle_end


# Example usage
frames = FrameRange(frame_start=1001,
                    frame_end=1010,
                    handle_start=5,
                    handle_end=10)

print(frames.frame_start_handle)

Then the method becomes:

        def get_instance_task_frame_range(instance):
            # Get frame information from task entity
            # NOTE: If there is no task override then the asset
            # value is automatically returned instead
            attrib = instance.context.data["taskEntity"]["attrib"]
            return FrameRange(
                frame_start=attrib["frameStart"],
                frame_end=attrib["frameEnd"],
                handle_start=attrib["handleStart"],
                handle_end=attrib["handleEnd"],
            )

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Furthermore I still think this should check the frame range against the entity of the instance - not the current asset so that you still validate to the asset you're publishing to, not where you're publishing from.

So likely we'll also need to collect the task entity for the instance - and use that one here instead.

Copy link
Contributor Author

@bradenjennings bradenjennings Feb 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I implemented the get_instance_task_frame_range, and FrameRange object as suggested in the validate_frame_range.py and pushed the changes now.

I'm not sure what you mean by this below. The validation and repair action seem to be doing the right thing in my testing so far.

Furthermore I still think this should check the frame range against the entity of the instance - not the current asset so that you still validate to the asset you're publishing to, not where you're publishing from.

So likely we'll also need to collect the task entity for the instance - and use that one here instead.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Create two instances in your scene, change folder + task on one of the instances via the publisher UI.
Your one scene, now publishes into two different folder + task entities.

The frame range validation should now validate the frame range of the selected task entity - not from your current context. So make sure in the database that each folder (and its task?) entity has a different frame range set so you can confirm the validation adheres to that actual selected task entity's frame range.

The current logic relies on instance.context of which during publishing there's only one - and thus I'm quite confident this currently does not work the way I describe.

Copy link
Contributor Author

@bradenjennings bradenjennings Feb 15, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@BigRoy is it just a matter of storing taskEntity in the instance data. so it gets stored per instance, rather than per context.
instance.data["taskEntity"]

however assetEntity is collected in collect_context_entities.py, so wouldn't it make sense to collect the taskEntity in the same place?
Once per context.

Copy link
Contributor Author

@bradenjennings bradenjennings Feb 15, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@BigRoy I updated the collection of taskEntity to happen within collect_instances.py rather than collect_context_entities.py,
https://github.com/ynput/ayon-core/pull/39/files#diff-13c18dd7cb684126254981e14bcf1590c1c5be889bc327ce05bbddc404d2133fR96

Is this the right approach?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@BigRoy do you have any further information about approach above?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it just a matter of storing taskEntity in the instance data. so it gets stored per instance, rather than per context.
instance.data["taskEntity"]

Yes, along those lines.

Basically the same behavior as is done for "assets" here basically inheriting the asset entity from context if it matches the context otherwise querying the differing asset entity instead.

Copy link
Contributor Author

@bradenjennings bradenjennings Feb 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@BigRoy sorry I do not follow.

is it possible for you to propose a code change for this?
It's really difficult to follow in written form.

Or is my code already basically doing the right thing.


inst_start = int(instance.data.get("frameStartHandle"))
inst_end = int(instance.data.get("frameEndHandle"))
Expand Down Expand Up @@ -128,12 +142,26 @@ def repair(cls, instance):
node = instance.data["name"]
context = instance.context

frame_start_handle = int(context.data.get("frameStartHandle"))
frame_end_handle = int(context.data.get("frameEndHandle"))
handle_start = int(context.data.get("handleStart"))
handle_end = int(context.data.get("handleEnd"))
frame_start = int(context.data.get("frameStart"))
frame_end = int(context.data.get("frameEnd"))
# Get frame information from task entity
# NOTE: If there is no task override then the asset
# value is automatically returned instead
task_entity = instance.context.data["taskEntity"]
frame_start_handle = task_entity["attrib"]["frameStart"] - \
task_entity["attrib"]["handleStart"]
frame_end_handle = task_entity["attrib"]["frameEnd"] + \
task_entity["attrib"]["handleEnd"]
handle_start = task_entity["attrib"]["handleStart"]
handle_end = task_entity["attrib"]["handleEnd"]
frame_start = task_entity["attrib"]["frameStart"]
frame_end = task_entity["attrib"]["frameEnd"]

# Get frame information from asset context
# frame_start_handle = int(context.data.get("frameStartHandle"))
# frame_end_handle = int(context.data.get("frameEndHandle"))
# handle_start = int(context.data.get("handleStart"))
# handle_end = int(context.data.get("handleEnd"))
# frame_start = int(context.data.get("frameStart"))
# frame_end = int(context.data.get("frameEnd"))

# Start
if cmds.attributeQuery("handleStart", node=node, exists=True):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,13 @@ def get_invalid_resolution(cls, instance):

@classmethod
def get_db_resolution(cls, instance):
task_doc = instance.context.data["taskEntity"]
asset_doc = instance.data["assetEntity"]
project_doc = instance.context.data["projectEntity"]
for data in [asset_doc["data"], project_doc["data"]]:
for data in [
task_doc["attrib"],
asset_doc["data"],
project_doc["data"]]:
if (
"resolutionWidth" in data and
"resolutionHeight" in data and
Expand Down
5 changes: 5 additions & 0 deletions client/ayon_core/plugins/publish/collect_context_entities.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import pyblish.api

from ayon_api import get_task_by_name
from ayon_core.client import get_project, get_asset_by_name
from ayon_core.pipeline import KnownPublishError

Expand Down Expand Up @@ -51,6 +52,10 @@ def process(self, context):

context.data["assetEntity"] = asset_entity

task_entity = get_task_by_name(
project_name, asset_entity["_id"], task_name)
context.data["taskEntity"] = task_entity

data = asset_entity['data']

# Task type
Expand Down
Loading