mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-24 12:54:40 +01:00
Merge branch 'develop' into bugfix/YN-0080-frame-range-is-showing-wrong-in-publisher
This commit is contained in:
commit
95e4195561
11 changed files with 189 additions and 22 deletions
1
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
1
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
|
|
@ -35,6 +35,7 @@ body:
|
|||
label: Version
|
||||
description: What version are you running? Look to AYON Tray
|
||||
options:
|
||||
- 1.6.7
|
||||
- 1.6.6
|
||||
- 1.6.5
|
||||
- 1.6.4
|
||||
|
|
|
|||
|
|
@ -141,6 +141,9 @@ def _get_ayon_bundle_data() -> tuple[
|
|||
]:
|
||||
studio_bundle_name = os.environ.get("AYON_STUDIO_BUNDLE_NAME")
|
||||
project_bundle_name = os.getenv("AYON_BUNDLE_NAME")
|
||||
# If AYON launcher <1.4.0 was used
|
||||
if not studio_bundle_name:
|
||||
studio_bundle_name = project_bundle_name
|
||||
bundles = ayon_api.get_bundles()["bundles"]
|
||||
studio_bundle = next(
|
||||
(
|
||||
|
|
|
|||
|
|
@ -983,7 +983,26 @@ def get_instance_expected_output_path(
|
|||
"version": version
|
||||
})
|
||||
|
||||
path_template_obj = anatomy.get_template_item("publish", "default")["path"]
|
||||
# Get instance publish template name
|
||||
task_name = task_type = None
|
||||
task_entity = instance.data.get("taskEntity")
|
||||
if task_entity:
|
||||
task_name = task_entity["name"]
|
||||
task_type = task_entity["taskType"]
|
||||
|
||||
template_name = get_publish_template_name(
|
||||
project_name=instance.context.data["projectName"],
|
||||
host_name=instance.context.data["hostName"],
|
||||
product_type=instance.data["productType"],
|
||||
task_name=task_name,
|
||||
task_type=task_type,
|
||||
project_settings=instance.context.data["project_settings"],
|
||||
)
|
||||
|
||||
path_template_obj = anatomy.get_template_item(
|
||||
"publish",
|
||||
template_name
|
||||
)["path"]
|
||||
template_filled = path_template_obj.format_strict(template_data)
|
||||
return os.path.normpath(template_filled)
|
||||
|
||||
|
|
|
|||
|
|
@ -71,6 +71,12 @@ class CollectOtioRanges(pyblish.api.InstancePlugin):
|
|||
import opentimelineio as otio
|
||||
|
||||
otio_clip = instance.data["otioClip"]
|
||||
if isinstance(
|
||||
otio_clip.media_reference,
|
||||
otio.schema.MissingReference
|
||||
):
|
||||
self.log.info("Clip has no media reference")
|
||||
return
|
||||
|
||||
# Collect timeline ranges if workfile start frame is available
|
||||
if "workfileFrameStart" in instance.data:
|
||||
|
|
|
|||
|
|
@ -60,6 +60,13 @@ class CollectOtioSubsetResources(
|
|||
|
||||
# get basic variables
|
||||
otio_clip = instance.data["otioClip"]
|
||||
if isinstance(
|
||||
otio_clip.media_reference,
|
||||
otio.schema.MissingReference
|
||||
):
|
||||
self.log.info("Clip has no media reference")
|
||||
return
|
||||
|
||||
otio_available_range = otio_clip.available_range()
|
||||
media_fps = otio_available_range.start_time.rate
|
||||
available_duration = otio_available_range.duration.value
|
||||
|
|
|
|||
|
|
@ -3,9 +3,10 @@ import re
|
|||
import copy
|
||||
import itertools
|
||||
import sys
|
||||
import tempfile
|
||||
import traceback
|
||||
import uuid
|
||||
from typing import Optional, Dict, Any
|
||||
from typing import Optional, Any
|
||||
|
||||
import ayon_api
|
||||
from ayon_api.utils import create_entity_id
|
||||
|
|
@ -225,8 +226,8 @@ class ProjectPushRepreItem:
|
|||
but filenames are not template based.
|
||||
|
||||
Args:
|
||||
repre_entity (Dict[str, Ant]): Representation entity.
|
||||
roots (Dict[str, str]): Project roots (based on project anatomy).
|
||||
repre_entity (dict[str, Ant]): Representation entity.
|
||||
roots (dict[str, str]): Project roots (based on project anatomy).
|
||||
"""
|
||||
|
||||
def __init__(self, repre_entity, roots):
|
||||
|
|
@ -482,6 +483,8 @@ class ProjectPushItemProcess:
|
|||
self._log_info("Destination project was found")
|
||||
self._fill_or_create_destination_folder()
|
||||
self._log_info("Destination folder was determined")
|
||||
self._fill_or_create_destination_task()
|
||||
self._log_info("Destination task was determined")
|
||||
self._determine_product_type()
|
||||
self._determine_publish_template_name()
|
||||
self._determine_product_name()
|
||||
|
|
@ -707,11 +710,14 @@ class ProjectPushItemProcess:
|
|||
project_entity,
|
||||
src_folder_type
|
||||
)
|
||||
new_thumbnail_id = self._create_new_folder_thumbnail(
|
||||
project_entity, src_folder_entity)
|
||||
folder_entity = new_folder_entity(
|
||||
folder_name,
|
||||
dst_folder_type,
|
||||
parent_id=parent_id,
|
||||
attribs=new_folder_attrib
|
||||
attribs=new_folder_attrib,
|
||||
thumbnail_id=new_thumbnail_id
|
||||
)
|
||||
if folder_label:
|
||||
folder_entity["label"] = folder_label
|
||||
|
|
@ -731,6 +737,40 @@ class ProjectPushItemProcess:
|
|||
folder_entity["path"] = "/".join([parent_path, folder_name])
|
||||
return folder_entity
|
||||
|
||||
def _create_new_folder_thumbnail(
|
||||
self,
|
||||
project_entity: dict[str, Any],
|
||||
src_folder_entity: dict[str, Any]
|
||||
) -> Optional[str]:
|
||||
"""Copy thumbnail possibly set on folder.
|
||||
|
||||
Could be different from representation thumbnails, and it is only shown
|
||||
when folder is selected.
|
||||
"""
|
||||
if not src_folder_entity["thumbnailId"]:
|
||||
return None
|
||||
|
||||
thumbnail = ayon_api.get_folder_thumbnail(
|
||||
self._item.src_project_name,
|
||||
src_folder_entity["id"],
|
||||
src_folder_entity["thumbnailId"]
|
||||
)
|
||||
if not thumbnail.id:
|
||||
return None
|
||||
|
||||
with tempfile.NamedTemporaryFile(delete=False) as tmp_file:
|
||||
tmp_file.write(thumbnail.content)
|
||||
temp_file_path = tmp_file.name
|
||||
|
||||
new_thumbnail_id = None
|
||||
try:
|
||||
new_thumbnail_id = ayon_api.create_thumbnail(
|
||||
project_entity["name"], temp_file_path)
|
||||
finally:
|
||||
if os.path.exists(temp_file_path):
|
||||
os.remove(temp_file_path)
|
||||
return new_thumbnail_id
|
||||
|
||||
def _get_dst_folder_type(
|
||||
self,
|
||||
project_entity: dict[str, Any],
|
||||
|
|
@ -750,7 +790,6 @@ class ProjectPushItemProcess:
|
|||
def _fill_or_create_destination_folder(self):
|
||||
dst_project_name = self._item.dst_project_name
|
||||
dst_folder_id = self._item.dst_folder_id
|
||||
dst_task_name = self._item.dst_task_name
|
||||
new_folder_name = self._item.new_folder_name
|
||||
if not dst_folder_id and not new_folder_name:
|
||||
self._status.set_failed(
|
||||
|
|
@ -781,9 +820,11 @@ class ProjectPushItemProcess:
|
|||
new_folder_name
|
||||
)
|
||||
self._folder_entity = folder_entity
|
||||
if not dst_task_name:
|
||||
self._task_info = {}
|
||||
return
|
||||
|
||||
def _fill_or_create_destination_task(self):
|
||||
folder_entity = self._folder_entity
|
||||
dst_task_name = self._item.dst_task_name
|
||||
dst_project_name = self._item.dst_project_name
|
||||
|
||||
folder_path = folder_entity["path"]
|
||||
folder_tasks = {
|
||||
|
|
@ -792,6 +833,20 @@ class ProjectPushItemProcess:
|
|||
dst_project_name, folder_ids=[folder_entity["id"]]
|
||||
)
|
||||
}
|
||||
|
||||
if not dst_task_name:
|
||||
src_task_info = self._get_src_task_info()
|
||||
if not src_task_info: # really no task selected nor on source
|
||||
self._task_info = {}
|
||||
return
|
||||
|
||||
dst_task_name = src_task_info["name"]
|
||||
if dst_task_name.lower() not in folder_tasks:
|
||||
task_info = self._make_sure_task_exists(
|
||||
folder_entity, src_task_info
|
||||
)
|
||||
folder_tasks[dst_task_name.lower()] = task_info
|
||||
|
||||
task_info = folder_tasks.get(dst_task_name.lower())
|
||||
if not task_info:
|
||||
self._status.set_failed(
|
||||
|
|
@ -810,7 +865,10 @@ class ProjectPushItemProcess:
|
|||
task_type["name"]: task_type
|
||||
for task_type in self._project_entity["taskTypes"]
|
||||
}
|
||||
task_type_info = task_types_by_name.get(task_type_name, {})
|
||||
task_type_info = copy.deepcopy(
|
||||
task_types_by_name.get(task_type_name, {})
|
||||
)
|
||||
task_type_info.pop("name") # do not overwrite real task name
|
||||
task_info.update(task_type_info)
|
||||
self._task_info = task_info
|
||||
|
||||
|
|
@ -945,8 +1003,8 @@ class ProjectPushItemProcess:
|
|||
version = get_versioning_start(
|
||||
project_name,
|
||||
self.host_name,
|
||||
task_name=self._task_info["name"],
|
||||
task_type=self._task_info["taskType"],
|
||||
task_name=self._task_info.get("name"),
|
||||
task_type=self._task_info.get("taskType"),
|
||||
product_type=product_type,
|
||||
product_name=product_entity["name"],
|
||||
)
|
||||
|
|
@ -970,10 +1028,16 @@ class ProjectPushItemProcess:
|
|||
existing_version_entity["attrib"].update(dst_attrib)
|
||||
self._version_entity = existing_version_entity
|
||||
return
|
||||
copied_tags = self._get_transferable_tags(src_version_entity)
|
||||
copied_status = self._get_transferable_status(src_version_entity)
|
||||
|
||||
version_entity = new_version_entity(
|
||||
version,
|
||||
product_id,
|
||||
author=src_version_entity["author"],
|
||||
status=copied_status,
|
||||
tags=copied_tags,
|
||||
task_id=self._task_info.get("id"),
|
||||
attribs=dst_attrib,
|
||||
thumbnail_id=thumbnail_id,
|
||||
)
|
||||
|
|
@ -982,6 +1046,47 @@ class ProjectPushItemProcess:
|
|||
)
|
||||
self._version_entity = version_entity
|
||||
|
||||
def _make_sure_task_exists(
|
||||
self,
|
||||
folder_entity: dict[str, Any],
|
||||
task_info: dict[str, Any],
|
||||
) -> dict[str, Any]:
|
||||
"""Creates destination task from source task information"""
|
||||
project_name = self._item.dst_project_name
|
||||
found_task_type = False
|
||||
src_task_type = task_info["taskType"]
|
||||
for task_type in self._project_entity["taskTypes"]:
|
||||
if task_type["name"].lower() == src_task_type.lower():
|
||||
found_task_type = True
|
||||
break
|
||||
|
||||
if not found_task_type:
|
||||
self._status.set_failed(
|
||||
f"'{src_task_type}' task type is not configured in "
|
||||
"project Anatomy."
|
||||
)
|
||||
|
||||
raise PushToProjectError(self._status.fail_reason)
|
||||
|
||||
task_info = self._operations.create_task(
|
||||
project_name,
|
||||
task_info["name"],
|
||||
folder_id=folder_entity["id"],
|
||||
task_type=src_task_type,
|
||||
attrib=task_info["attrib"],
|
||||
)
|
||||
self._task_info = task_info.data
|
||||
return self._task_info
|
||||
|
||||
def _get_src_task_info(self):
|
||||
src_version_entity = self._src_version_entity
|
||||
if not src_version_entity["taskId"]:
|
||||
return None
|
||||
src_task = ayon_api.get_task_by_id(
|
||||
self._item.src_project_name, src_version_entity["taskId"]
|
||||
)
|
||||
return src_task
|
||||
|
||||
def _integrate_representations(self):
|
||||
try:
|
||||
self._real_integrate_representations()
|
||||
|
|
@ -1217,10 +1322,12 @@ class ProjectPushItemProcess:
|
|||
if context_value and isinstance(context_value, dict):
|
||||
for context_sub_key in context_value.keys():
|
||||
value_to_update = formatting_data.get(context_key, {}).get(
|
||||
context_sub_key)
|
||||
context_sub_key
|
||||
)
|
||||
if value_to_update:
|
||||
repre_context[context_key][
|
||||
context_sub_key] = value_to_update
|
||||
repre_context[context_key][context_sub_key] = (
|
||||
value_to_update
|
||||
)
|
||||
else:
|
||||
value_to_update = formatting_data.get(context_key)
|
||||
if value_to_update:
|
||||
|
|
@ -1229,6 +1336,28 @@ class ProjectPushItemProcess:
|
|||
repre_context.pop("task", None)
|
||||
return repre_context
|
||||
|
||||
def _get_transferable_tags(self, src_version_entity):
|
||||
"""Copy over only tags present in destination project"""
|
||||
dst_project_tags = [
|
||||
tag["name"] for tag in self._project_entity["tags"]
|
||||
]
|
||||
copied_tags = []
|
||||
for src_tag in src_version_entity["tags"]:
|
||||
if src_tag in dst_project_tags:
|
||||
copied_tags.append(src_tag)
|
||||
return copied_tags
|
||||
|
||||
def _get_transferable_status(self, src_version_entity):
|
||||
"""Copy over status, first status if not matching found"""
|
||||
dst_project_statuses = {
|
||||
status["name"]: status
|
||||
for status in self._project_entity["statuses"]
|
||||
}
|
||||
copied_status = dst_project_statuses.get(src_version_entity["status"])
|
||||
if copied_status:
|
||||
return copied_status["name"]
|
||||
return None
|
||||
|
||||
|
||||
class IntegrateModel:
|
||||
def __init__(self, controller):
|
||||
|
|
@ -1301,6 +1430,6 @@ class IntegrateModel:
|
|||
return
|
||||
item.integrate()
|
||||
|
||||
def get_items(self) -> Dict[str, ProjectPushItemProcess]:
|
||||
def get_items(self) -> dict[str, ProjectPushItemProcess]:
|
||||
"""Returns dict of all ProjectPushItemProcess items """
|
||||
return self._process_items
|
||||
|
|
|
|||
|
|
@ -358,9 +358,8 @@ class WorkfilesToolWindow(QtWidgets.QWidget):
|
|||
if not self._host_is_valid:
|
||||
return
|
||||
|
||||
self._folders_widget.set_project_name(
|
||||
self._controller.get_current_project_name()
|
||||
)
|
||||
self._project_name = self._controller.get_current_project_name()
|
||||
self._folders_widget.set_project_name(self._project_name)
|
||||
|
||||
def _on_save_as_finished(self, event):
|
||||
if event["failed"]:
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""Package declaring AYON addon 'core' version."""
|
||||
__version__ = "1.6.6+dev"
|
||||
__version__ = "1.6.7+dev"
|
||||
|
|
|
|||
|
|
@ -19,3 +19,6 @@ OpenTimelineIO = "0.16.0"
|
|||
opencolorio = "^2.3.2,<2.4.0"
|
||||
Pillow = "9.5.0"
|
||||
websocket-client = ">=0.40.0,<2"
|
||||
|
||||
[ayon.runtimeDependencies.darwin]
|
||||
pyobjc-core = "^11.1"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
name = "core"
|
||||
title = "Core"
|
||||
version = "1.6.6+dev"
|
||||
version = "1.6.7+dev"
|
||||
|
||||
client_dir = "ayon_core"
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
[tool.poetry]
|
||||
name = "ayon-core"
|
||||
version = "1.6.6+dev"
|
||||
version = "1.6.7+dev"
|
||||
description = ""
|
||||
authors = ["Ynput Team <team@ynput.io>"]
|
||||
readme = "README.md"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue