mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-24 12:54:40 +01:00
Merge branch 'develop' into bugfix/addon-versions-resolution
This commit is contained in:
commit
44257cd999
7 changed files with 135 additions and 126 deletions
|
|
@ -1,11 +1,9 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""AYON plugin tools."""
|
||||
import os
|
||||
import logging
|
||||
import re
|
||||
import collections
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
CAPITALIZE_REGEX = re.compile(r"[a-zA-Z0-9]")
|
||||
|
||||
|
|
|
|||
|
|
@ -249,7 +249,8 @@ def create_skeleton_instance(
|
|||
# map inputVersions `ObjectId` -> `str` so json supports it
|
||||
"inputVersions": list(map(str, data.get("inputVersions", []))),
|
||||
"colorspace": data.get("colorspace"),
|
||||
"hasExplicitFrames": data.get("hasExplicitFrames")
|
||||
"hasExplicitFrames": data.get("hasExplicitFrames", False),
|
||||
"reuseLastVersion": data.get("reuseLastVersion", False),
|
||||
}
|
||||
|
||||
if data.get("renderlayer"):
|
||||
|
|
|
|||
|
|
@ -7,13 +7,20 @@ import copy
|
|||
import warnings
|
||||
import hashlib
|
||||
import xml.etree.ElementTree
|
||||
from typing import TYPE_CHECKING, Optional, Union, List
|
||||
from typing import TYPE_CHECKING, Optional, Union, List, Any
|
||||
import clique
|
||||
import speedcopy
|
||||
import logging
|
||||
|
||||
import ayon_api
|
||||
import pyblish.util
|
||||
import pyblish.plugin
|
||||
import pyblish.api
|
||||
|
||||
from ayon_api import (
|
||||
get_server_api_connection,
|
||||
get_representations,
|
||||
get_last_version_by_product_name
|
||||
)
|
||||
from ayon_core.lib import (
|
||||
import_filepath,
|
||||
Logger,
|
||||
|
|
@ -34,6 +41,8 @@ if TYPE_CHECKING:
|
|||
|
||||
TRAIT_INSTANCE_KEY: str = "representations_with_traits"
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def get_template_name_profiles(
|
||||
project_name, project_settings=None, logger=None
|
||||
|
|
@ -1030,7 +1039,7 @@ def main_cli_publish(
|
|||
# NOTE: ayon-python-api does not have public api function to find
|
||||
# out if is used service user. So we need to have try > except
|
||||
# block.
|
||||
con = ayon_api.get_server_api_connection()
|
||||
con = get_server_api_connection()
|
||||
try:
|
||||
con.set_default_service_username(username)
|
||||
except ValueError:
|
||||
|
|
@ -1143,3 +1152,90 @@ def get_trait_representations(
|
|||
|
||||
"""
|
||||
return instance.data.get(TRAIT_INSTANCE_KEY, [])
|
||||
|
||||
|
||||
def fill_sequence_gaps_with_previous_version(
|
||||
collection: str,
|
||||
staging_dir: str,
|
||||
instance: pyblish.plugin.Instance,
|
||||
current_repre_name: str,
|
||||
start_frame: int,
|
||||
end_frame: int
|
||||
) -> tuple[Optional[dict[str, Any]], Optional[dict[int, str]]]:
|
||||
"""Tries to replace missing frames from ones from last version"""
|
||||
used_version_entity, repre_file_paths = _get_last_version_files(
|
||||
instance, current_repre_name
|
||||
)
|
||||
if repre_file_paths is None:
|
||||
# issues in getting last version files
|
||||
return (None, None)
|
||||
|
||||
prev_collection = clique.assemble(
|
||||
repre_file_paths,
|
||||
patterns=[clique.PATTERNS["frames"]],
|
||||
minimum_items=1
|
||||
)[0][0]
|
||||
prev_col_format = prev_collection.format("{head}{padding}{tail}")
|
||||
|
||||
added_files = {}
|
||||
anatomy = instance.context.data["anatomy"]
|
||||
col_format = collection.format("{head}{padding}{tail}")
|
||||
for frame in range(start_frame, end_frame + 1):
|
||||
if frame in collection.indexes:
|
||||
continue
|
||||
hole_fpath = os.path.join(staging_dir, col_format % frame)
|
||||
|
||||
previous_version_path = prev_col_format % frame
|
||||
previous_version_path = anatomy.fill_root(previous_version_path)
|
||||
if not os.path.exists(previous_version_path):
|
||||
log.warning(
|
||||
"Missing frame should be replaced from "
|
||||
f"'{previous_version_path}' but that doesn't exist. "
|
||||
)
|
||||
return (None, None)
|
||||
|
||||
log.warning(
|
||||
f"Replacing missing '{hole_fpath}' with "
|
||||
f"'{previous_version_path}'"
|
||||
)
|
||||
speedcopy.copyfile(previous_version_path, hole_fpath)
|
||||
added_files[frame] = hole_fpath
|
||||
|
||||
return (used_version_entity, added_files)
|
||||
|
||||
|
||||
def _get_last_version_files(
|
||||
instance: pyblish.plugin.Instance,
|
||||
current_repre_name: str,
|
||||
) -> tuple[Optional[dict[str, Any]], Optional[list[str]]]:
|
||||
product_name = instance.data["productName"]
|
||||
project_name = instance.data["projectEntity"]["name"]
|
||||
folder_entity = instance.data["folderEntity"]
|
||||
|
||||
version_entity = get_last_version_by_product_name(
|
||||
project_name,
|
||||
product_name,
|
||||
folder_entity["id"],
|
||||
fields={"id", "attrib"}
|
||||
)
|
||||
|
||||
if not version_entity:
|
||||
return None, None
|
||||
|
||||
matching_repres = get_representations(
|
||||
project_name,
|
||||
version_ids=[version_entity["id"]],
|
||||
representation_names=[current_repre_name],
|
||||
fields={"files"}
|
||||
)
|
||||
|
||||
matching_repre = next(matching_repres, None)
|
||||
if not matching_repre:
|
||||
return None, None
|
||||
|
||||
repre_file_paths = [
|
||||
file_info["path"]
|
||||
for file_info in matching_repre["files"]
|
||||
]
|
||||
|
||||
return (version_entity, repre_file_paths)
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ import copy
|
|||
|
||||
import pyblish.api
|
||||
|
||||
from ayon_core.pipeline.publish import get_publish_template_name
|
||||
|
||||
|
||||
class CollectResourcesPath(pyblish.api.InstancePlugin):
|
||||
"""Generate directory path where the files and resources will be stored.
|
||||
|
|
@ -77,16 +79,29 @@ class CollectResourcesPath(pyblish.api.InstancePlugin):
|
|||
|
||||
# This is for cases of Deprecated anatomy without `folder`
|
||||
# TODO remove when all clients have solved this issue
|
||||
template_data.update({
|
||||
"frame": "FRAME_TEMP",
|
||||
"representation": "TEMP"
|
||||
})
|
||||
template_data.update({"frame": "FRAME_TEMP", "representation": "TEMP"})
|
||||
|
||||
publish_templates = anatomy.get_template_item(
|
||||
"publish", "default", "directory"
|
||||
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"],
|
||||
logger=self.log,
|
||||
)
|
||||
|
||||
publish_template = anatomy.get_template_item(
|
||||
"publish", template_name, "directory")
|
||||
|
||||
publish_folder = os.path.normpath(
|
||||
publish_templates.format_strict(template_data)
|
||||
publish_template.format_strict(template_data)
|
||||
)
|
||||
resources_folder = os.path.join(publish_folder, "resources")
|
||||
|
||||
|
|
|
|||
|
|
@ -13,14 +13,15 @@ import clique
|
|||
import speedcopy
|
||||
import pyblish.api
|
||||
|
||||
from ayon_api import get_last_version_by_product_name, get_representations
|
||||
|
||||
from ayon_core.lib import (
|
||||
get_ffmpeg_tool_args,
|
||||
filter_profiles,
|
||||
path_to_subprocess_arg,
|
||||
run_subprocess,
|
||||
)
|
||||
from ayon_core.pipeline.publish.lib import (
|
||||
fill_sequence_gaps_with_previous_version
|
||||
)
|
||||
from ayon_core.lib.transcoding import (
|
||||
IMAGE_EXTENSIONS,
|
||||
get_ffprobe_streams,
|
||||
|
|
@ -130,7 +131,7 @@ def frame_to_timecode(frame: int, fps: float) -> str:
|
|||
|
||||
|
||||
class ExtractReview(pyblish.api.InstancePlugin):
|
||||
"""Extracting Review mov file for Ftrack
|
||||
"""Extracting Reviewable medias
|
||||
|
||||
Compulsory attribute of representation is tags list with "review",
|
||||
otherwise the representation is ignored.
|
||||
|
|
@ -508,10 +509,10 @@ class ExtractReview(pyblish.api.InstancePlugin):
|
|||
resolution_width=temp_data.resolution_width,
|
||||
resolution_height=temp_data.resolution_height,
|
||||
extension=temp_data.input_ext,
|
||||
temp_data=temp_data
|
||||
temp_data=temp_data,
|
||||
)
|
||||
elif fill_missing_frames == "previous_version":
|
||||
new_frame_files = self.fill_sequence_gaps_with_previous(
|
||||
fill_output = fill_sequence_gaps_with_previous_version(
|
||||
collection=collection,
|
||||
staging_dir=new_repre["stagingDir"],
|
||||
instance=instance,
|
||||
|
|
@ -519,8 +520,13 @@ class ExtractReview(pyblish.api.InstancePlugin):
|
|||
start_frame=temp_data.frame_start,
|
||||
end_frame=temp_data.frame_end,
|
||||
)
|
||||
_, new_frame_files = fill_output
|
||||
# fallback to original workflow
|
||||
if new_frame_files is None:
|
||||
self.log.warning(
|
||||
"Falling back to filling from currently "
|
||||
"last rendered."
|
||||
)
|
||||
new_frame_files = (
|
||||
self.fill_sequence_gaps_from_existing(
|
||||
collection=collection,
|
||||
|
|
@ -612,8 +618,6 @@ class ExtractReview(pyblish.api.InstancePlugin):
|
|||
"name": "{}_{}".format(output_name, output_ext),
|
||||
"outputName": output_name,
|
||||
"outputDef": output_def,
|
||||
"frameStartFtrack": temp_data.output_frame_start,
|
||||
"frameEndFtrack": temp_data.output_frame_end,
|
||||
"ffmpeg_cmd": subprcs_cmd
|
||||
})
|
||||
|
||||
|
|
@ -1050,92 +1054,6 @@ class ExtractReview(pyblish.api.InstancePlugin):
|
|||
|
||||
return all_args
|
||||
|
||||
def fill_sequence_gaps_with_previous(
|
||||
self,
|
||||
collection: str,
|
||||
staging_dir: str,
|
||||
instance: pyblish.plugin.Instance,
|
||||
current_repre_name: str,
|
||||
start_frame: int,
|
||||
end_frame: int
|
||||
) -> Optional[dict[int, str]]:
|
||||
"""Tries to replace missing frames from ones from last version"""
|
||||
repre_file_paths = self._get_last_version_files(
|
||||
instance, current_repre_name)
|
||||
if repre_file_paths is None:
|
||||
# issues in getting last version files, falling back
|
||||
return None
|
||||
|
||||
prev_collection = clique.assemble(
|
||||
repre_file_paths,
|
||||
patterns=[clique.PATTERNS["frames"]],
|
||||
minimum_items=1
|
||||
)[0][0]
|
||||
prev_col_format = prev_collection.format("{head}{padding}{tail}")
|
||||
|
||||
added_files = {}
|
||||
anatomy = instance.context.data["anatomy"]
|
||||
col_format = collection.format("{head}{padding}{tail}")
|
||||
for frame in range(start_frame, end_frame + 1):
|
||||
if frame in collection.indexes:
|
||||
continue
|
||||
hole_fpath = os.path.join(staging_dir, col_format % frame)
|
||||
|
||||
previous_version_path = prev_col_format % frame
|
||||
previous_version_path = anatomy.fill_root(previous_version_path)
|
||||
if not os.path.exists(previous_version_path):
|
||||
self.log.warning(
|
||||
"Missing frame should be replaced from "
|
||||
f"'{previous_version_path}' but that doesn't exist. "
|
||||
"Falling back to filling from currently last rendered."
|
||||
)
|
||||
return None
|
||||
|
||||
self.log.warning(
|
||||
f"Replacing missing '{hole_fpath}' with "
|
||||
f"'{previous_version_path}'"
|
||||
)
|
||||
speedcopy.copyfile(previous_version_path, hole_fpath)
|
||||
added_files[frame] = hole_fpath
|
||||
|
||||
return added_files
|
||||
|
||||
def _get_last_version_files(
|
||||
self,
|
||||
instance: pyblish.plugin.Instance,
|
||||
current_repre_name: str,
|
||||
):
|
||||
product_name = instance.data["productName"]
|
||||
project_name = instance.data["projectEntity"]["name"]
|
||||
folder_entity = instance.data["folderEntity"]
|
||||
|
||||
version_entity = get_last_version_by_product_name(
|
||||
project_name,
|
||||
product_name,
|
||||
folder_entity["id"],
|
||||
fields={"id"}
|
||||
)
|
||||
if not version_entity:
|
||||
return None
|
||||
|
||||
matching_repres = get_representations(
|
||||
project_name,
|
||||
version_ids=[version_entity["id"]],
|
||||
representation_names=[current_repre_name],
|
||||
fields={"files"}
|
||||
)
|
||||
|
||||
if not matching_repres:
|
||||
return None
|
||||
matching_repre = list(matching_repres)[0]
|
||||
|
||||
repre_file_paths = [
|
||||
file_info["path"]
|
||||
for file_info in matching_repre["files"]
|
||||
]
|
||||
|
||||
return repre_file_paths
|
||||
|
||||
def fill_sequence_gaps_with_blanks(
|
||||
self,
|
||||
collection: str,
|
||||
|
|
@ -1384,15 +1302,7 @@ class ExtractReview(pyblish.api.InstancePlugin):
|
|||
return audio_in_args, audio_filters, audio_out_args
|
||||
|
||||
for audio in audio_inputs:
|
||||
# NOTE modified, always was expected "frameStartFtrack" which is
|
||||
# STRANGE?!!! There should be different key, right?
|
||||
# TODO use different frame start!
|
||||
offset_seconds = 0
|
||||
frame_start_ftrack = instance.data.get("frameStartFtrack")
|
||||
if frame_start_ftrack is not None:
|
||||
offset_frames = frame_start_ftrack - audio["offset"]
|
||||
offset_seconds = offset_frames / temp_data.fps
|
||||
|
||||
if offset_seconds > 0:
|
||||
audio_in_args.append(
|
||||
"-ss {}".format(offset_seconds)
|
||||
|
|
|
|||
|
|
@ -27,17 +27,6 @@ codespell = "^2.2.6"
|
|||
semver = "^3.0.2"
|
||||
mypy = "^1.14.0"
|
||||
mock = "^5.0.0"
|
||||
tomlkit = "^0.13.2"
|
||||
requests = "^2.32.3"
|
||||
mkdocs-material = "^9.6.7"
|
||||
mkdocs-autoapi = "^0.4.0"
|
||||
mkdocstrings-python = "^1.16.2"
|
||||
mkdocs-minify-plugin = "^0.8.0"
|
||||
markdown-checklist = "^0.4.4"
|
||||
mdx-gh-links = "^0.4"
|
||||
pymdown-extensions = "^10.14.3"
|
||||
mike = "^2.1.3"
|
||||
mkdocstrings-shell = "^1.0.2"
|
||||
nxtools = "^1.6"
|
||||
|
||||
[tool.poetry.group.test.dependencies]
|
||||
|
|
|
|||
|
|
@ -454,7 +454,7 @@ DEFAULT_TOOLS_VALUES = {
|
|||
"hosts": [],
|
||||
"task_types": [],
|
||||
"tasks": [],
|
||||
"template": "{product[type]}{Task[name]}{Variant}"
|
||||
"template": "{product[type]}{Task[name]}{Variant}<_{Aov}>"
|
||||
},
|
||||
{
|
||||
"product_types": [
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue