diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
index 7a39103859..35564c2bf0 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -35,6 +35,8 @@ body:
label: Version
description: What version are you running? Look to OpenPype Tray
options:
+ - 3.16.7-nightly.1
+ - 3.16.6
- 3.16.6-nightly.1
- 3.16.5
- 3.16.5-nightly.5
@@ -133,8 +135,6 @@ body:
- 3.14.9
- 3.14.9-nightly.5
- 3.14.9-nightly.4
- - 3.14.9-nightly.3
- - 3.14.9-nightly.2
validations:
required: true
- type: dropdown
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c4f9ff57ea..0d7620869b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,243 @@
# Changelog
+## [3.16.6](https://github.com/ynput/OpenPype/tree/3.16.6)
+
+
+[Full Changelog](https://github.com/ynput/OpenPype/compare/3.16.5...3.16.6)
+
+### **🆕 New features**
+
+
+
+Workfiles tool: Refactor workfiles tool (for AYON) #5550
+
+Refactored workfiles tool to new tool. Separated backend and frontend logic. Refactored logic is AYON-centric and is used only in AYON mode, so it does not affect OpenPype.
+
+
+___
+
+
+
+
+
+AfterEffects: added validator for missing files in FootageItems #5590
+
+Published composition in AE could contain multiple FootageItems as a layers. If FootageItem contains imported file and it doesn't exist, render triggered by Publish process will silently fail and no output is generated. This could cause failure later in the process with unclear reason. (In `ExtractReview`).This PR adds validation to protect from this.
+
+
+___
+
+
+
+### **🚀 Enhancements**
+
+
+
+Maya: Yeti Cache Include viewport preview settings from source #5561
+
+When publishing and loading yeti caches persist the display output and preview colors + settings to ensure consistency in the view
+
+
+___
+
+
+
+
+
+Houdini: validate colorspace in review rop #5322
+
+Adding a validator that checks if 'OCIO Colorspace' parameter on review rop was set to a valid value.It is a step towards managing colorspace in review ropvalid values are the ones in the dropdown menuthis validator also provides some helper actions This PR is related to #4836 and #4833
+
+
+___
+
+
+
+
+
+Colorspace: adding abstraction of publishing related functions #5497
+
+The functionality of Colorspace has been abstracted for greater usability.
+
+
+___
+
+
+
+
+
+Nuke: removing redundant workfile colorspace attributes #5580
+
+Nuke root workfile colorspace data type knobs are long time configured automatically via config roles or the default values are also working well. Therefore there is no need for pipeline managed knobs.
+
+
+___
+
+
+
+
+
+Ftrack: Less verbose logs for Ftrack integration in artist facing logs #5596
+
+- Reduce artist-facing logs for component integration for Ftrack
+- Avoid "Comment is not set" log in artist facing report for Kitsu and Ftrack
+- Remove info log about `ffprobe` inspecting a file (changed to debug log)
+- interesting to see however that it ffprobes the same jpeg twice - but maybe once for thumbnail?
+
+
+___
+
+
+
+### **🐛 Bug fixes**
+
+
+
+Maya: Fix rig validators for new out_SET and controls_SET names #5595
+
+Fix usage of `out_SET` and `controls_SET` since #5310 because they can now be prefixed by the subset name.
+
+
+___
+
+
+
+
+
+TrayPublisher: set default frame values to sequential data #5530
+
+We are inheriting default frame handles and fps data either from project or setting them to 0. This is just for case a production will decide not to injest the sequential representations with asset based metadata.
+
+
+___
+
+
+
+
+
+Publisher: Screenshot opacity value fix #5576
+
+Fix opacity value.
+
+
+___
+
+
+
+
+
+AfterEffects: fix imports of image sequences #5581
+
+#4602 broke imports of image sequences.
+
+
+___
+
+
+
+
+
+AYON: Fix representation context conversion #5591
+
+Do not fix `"folder"` key in representation context until it is needed.
+
+
+___
+
+
+
+
+
+ayon-nuke: default factory to lists #5594
+
+Default factory were missing in settings schemas for complicated objects like lists and it was causing settings to be failing saving.
+
+
+___
+
+
+
+
+
+Maya: Fix look assigner showing no asset if 'not found' representations are present #5597
+
+Fix Maya Look assigner failing to show any content if it finds an invalid container for which it can't find the asset in the current project. (This can happen when e.g. loading something from a library project).There was logic already to avoid this but there was a bug where it used variable `_id` which did not exist and likely had to be `asset_id`.I've fixed that and improved the logged message a bit, e.g.:
+```
+// Warning: openpype.hosts.maya.tools.mayalookassigner.commands : Id found on 22 nodes for which no asset is found database, skipping '641d78ec85c3c5b102e836b0'
+```
+Example not found representation in Loader:The issue isn't necessarily related to NOT FOUND representations but in essence boils down to finding nodes with asset ids that do not exist in the current project which could very well just be local meshes in your scene.**Note:**I've excluded logging the nodes themselves because that tends to be a very long list of nodes. Only downside to removing that is that it's unclear which nodes are related to that `id`. If there are any ideas on how to still provide a concise informational message about that that'd be great so I could add it. Things I had considered:
+- Report the containers, issue here is that it's about asset ids on nodes which don't HAVE to be in containers - it could be local geometry
+- Report the namespaces, issue here is that it could be nodes without namespaces (plus potentially not about ALL nodes in a namespace)
+- Report the short names of the nodes; it's shorter and readable but still likely a lot of nodes.@tokejepsen @LiborBatek any other ideas?
+
+
+___
+
+
+
+
+
+Photoshop: fixed blank Flatten image #5600
+
+Flatten image is simplified publishing approach where all visible layers are "flatten" and published together. This image could be used as a reference etc.This is implemented by auto creator which wasn't updated after first publish. This would result in missing newly created layers after `auto_image` instance was created.
+
+
+___
+
+
+
+
+
+Blender: Remove Hardcoded Subset Name for Reviews #5603
+
+Fixes hardcoded subset name for Reviews in Blender.
+
+
+___
+
+
+
+
+
+TVPaint: Fix tool callbacks #5608
+
+Do not wait for callback to finish.
+
+
+___
+
+
+
+### **🔀 Refactored code**
+
+
+
+Chore: Remove unused variables and cleanup #5588
+
+Removing some unused variables. In some cases the unused variables _seemed like they should've been used - maybe?_ so please **double check the code whether it doesn't hint to an already existing bug**.Also tweaked some other small bugs in code + tweaked logging levels.
+
+
+___
+
+
+
+### **Merged pull requests**
+
+
+
+Chore: Loader log deprecation warning for 'fname' attribute #5587
+
+Since https://github.com/ynput/OpenPype/pull/4602 the `fname` attribute on the `LoaderPlugin` should've been deprecated and set for removal over time. However, no deprecation warning was logged whatsoever and thus one usage appears to have sneaked in (fixed with this PR) and a new one tried to sneak in with a recent PR
+
+
+___
+
+
+
+
+
+
## [3.16.5](https://github.com/ynput/OpenPype/tree/3.16.5)
diff --git a/openpype/hosts/aftereffects/api/extension.zxp b/openpype/hosts/aftereffects/api/extension.zxp
index 358e9740d3..933dc7dc6c 100644
Binary files a/openpype/hosts/aftereffects/api/extension.zxp and b/openpype/hosts/aftereffects/api/extension.zxp differ
diff --git a/openpype/hosts/aftereffects/api/extension/CSXS/manifest.xml b/openpype/hosts/aftereffects/api/extension/CSXS/manifest.xml
index 0057758320..7329a9e723 100644
--- a/openpype/hosts/aftereffects/api/extension/CSXS/manifest.xml
+++ b/openpype/hosts/aftereffects/api/extension/CSXS/manifest.xml
@@ -1,5 +1,5 @@
-
@@ -10,22 +10,22 @@
-
+
-
+
-
-
+
+
-
+
-
-
+
+
-
+
@@ -63,7 +63,7 @@
550
400
-->
-
+
./icons/iconNormal.png
@@ -71,9 +71,9 @@
./icons/iconDisabled.png
./icons/iconDarkNormal.png
./icons/iconDarkRollover.png
-
+
-
\ No newline at end of file
+
diff --git a/openpype/hosts/aftereffects/api/extension/jsx/hostscript.jsx b/openpype/hosts/aftereffects/api/extension/jsx/hostscript.jsx
index bc443930df..c00844e637 100644
--- a/openpype/hosts/aftereffects/api/extension/jsx/hostscript.jsx
+++ b/openpype/hosts/aftereffects/api/extension/jsx/hostscript.jsx
@@ -215,6 +215,8 @@ function _getItem(item, comps, folders, footages){
* Refactor
*/
var item_type = '';
+ var path = '';
+ var containing_comps = [];
if (item instanceof FolderItem){
item_type = 'folder';
if (!folders){
@@ -222,10 +224,18 @@ function _getItem(item, comps, folders, footages){
}
}
if (item instanceof FootageItem){
- item_type = 'footage';
if (!footages){
return "{}";
}
+ item_type = 'footage';
+ if (item.file){
+ path = item.file.fsName;
+ }
+ if (item.usedIn){
+ for (j = 0; j < item.usedIn.length; ++j){
+ containing_comps.push(item.usedIn[j].id);
+ }
+ }
}
if (item instanceof CompItem){
item_type = 'comp';
@@ -236,7 +246,9 @@ function _getItem(item, comps, folders, footages){
var item = {"name": item.name,
"id": item.id,
- "type": item_type};
+ "type": item_type,
+ "path": path,
+ "containing_comps": containing_comps};
return JSON.stringify(item);
}
diff --git a/openpype/hosts/aftereffects/api/ws_stub.py b/openpype/hosts/aftereffects/api/ws_stub.py
index f5b96fa63a..18f530e272 100644
--- a/openpype/hosts/aftereffects/api/ws_stub.py
+++ b/openpype/hosts/aftereffects/api/ws_stub.py
@@ -37,6 +37,9 @@ class AEItem(object):
height = attr.ib(default=None)
is_placeholder = attr.ib(default=False)
uuid = attr.ib(default=False)
+ path = attr.ib(default=False) # path to FootageItem to validate
+ # list of composition Footage is in
+ containing_comps = attr.ib(factory=list)
class AfterEffectsServerStub():
@@ -704,7 +707,10 @@ class AfterEffectsServerStub():
d.get("instance_id"),
d.get("width"),
d.get("height"),
- d.get("is_placeholder"))
+ d.get("is_placeholder"),
+ d.get("uuid"),
+ d.get("path"),
+ d.get("containing_comps"),)
ret.append(item)
return ret
diff --git a/openpype/hosts/aftereffects/plugins/load/load_file.py b/openpype/hosts/aftereffects/plugins/load/load_file.py
index def7c927ab..8d52aac546 100644
--- a/openpype/hosts/aftereffects/plugins/load/load_file.py
+++ b/openpype/hosts/aftereffects/plugins/load/load_file.py
@@ -31,13 +31,8 @@ class FileLoader(api.AfterEffectsLoader):
path = self.filepath_from_context(context)
- repr_cont = context["representation"]["context"]
- if "#" not in path:
- frame = repr_cont.get("frame")
- if frame:
- padding = len(frame)
- path = path.replace(frame, "#" * padding)
- import_options['sequence'] = True
+ if len(context["representation"]["files"]) > 1:
+ import_options['sequence'] = True
if not path:
repr_id = context["representation"]["_id"]
diff --git a/openpype/hosts/aftereffects/plugins/publish/help/validate_footage_items.xml b/openpype/hosts/aftereffects/plugins/publish/help/validate_footage_items.xml
new file mode 100644
index 0000000000..01c8966015
--- /dev/null
+++ b/openpype/hosts/aftereffects/plugins/publish/help/validate_footage_items.xml
@@ -0,0 +1,14 @@
+
+
+
+Footage item missing
+
+## Footage item missing
+
+ FootageItem `{name}` contains missing `{path}`. Render will not produce any frames and AE will stop react to any integration
+### How to repair?
+
+Remove `{name}` or provide missing file.
+
+
+
diff --git a/openpype/hosts/aftereffects/plugins/publish/validate_footage_items.py b/openpype/hosts/aftereffects/plugins/publish/validate_footage_items.py
new file mode 100644
index 0000000000..40a08a2c3f
--- /dev/null
+++ b/openpype/hosts/aftereffects/plugins/publish/validate_footage_items.py
@@ -0,0 +1,49 @@
+# -*- coding: utf-8 -*-
+"""Validate presence of footage items in composition
+Requires:
+"""
+import os
+
+import pyblish.api
+
+from openpype.pipeline import (
+ PublishXmlValidationError
+)
+from openpype.hosts.aftereffects.api import get_stub
+
+
+class ValidateFootageItems(pyblish.api.InstancePlugin):
+ """
+ Validates if FootageItems contained in composition exist.
+
+ AE fails silently and doesn't render anything if footage item file is
+ missing. This will result in nonresponsiveness of AE UI as it expects
+ reaction from user, but it will not provide dialog.
+ This validator tries to check existence of the files.
+ It will not protect from missing frame in multiframes though
+ (as AE api doesn't provide this information and it cannot be told how many
+ frames should be there easily). Missing frame is replaced by placeholder.
+ """
+
+ order = pyblish.api.ValidatorOrder
+ label = "Validate Footage Items"
+ families = ["render.farm", "render.local", "render"]
+ hosts = ["aftereffects"]
+ optional = True
+
+ def process(self, instance):
+ """Plugin entry point."""
+
+ comp_id = instance.data["comp_id"]
+ for footage_item in get_stub().get_items(comps=False, folders=False,
+ footages=True):
+ self.log.info(footage_item)
+ if comp_id not in footage_item.containing_comps:
+ continue
+
+ path = footage_item.path
+ if path and not os.path.exists(path):
+ msg = f"File {path} not found."
+ formatting = {"name": footage_item.name, "path": path}
+ raise PublishXmlValidationError(self, msg,
+ formatting_data=formatting)
diff --git a/openpype/hosts/blender/plugins/publish/collect_review.py b/openpype/hosts/blender/plugins/publish/collect_review.py
index 6459927015..3bf2e39e24 100644
--- a/openpype/hosts/blender/plugins/publish/collect_review.py
+++ b/openpype/hosts/blender/plugins/publish/collect_review.py
@@ -39,15 +39,11 @@ class CollectReview(pyblish.api.InstancePlugin):
]
if not instance.data.get("remove"):
-
- task = instance.context.data["task"]
-
# Store focal length in `burninDataMembers`
burninData = instance.data.setdefault("burninDataMembers", {})
burninData["focalLength"] = focal_length
instance.data.update({
- "subset": f"{task}Review",
"review_camera": camera,
"frameStart": instance.context.data["frameStart"],
"frameEnd": instance.context.data["frameEnd"],
diff --git a/openpype/hosts/photoshop/plugins/create/create_flatten_image.py b/openpype/hosts/photoshop/plugins/create/create_flatten_image.py
index 9d4189a1a3..e4229788bd 100644
--- a/openpype/hosts/photoshop/plugins/create/create_flatten_image.py
+++ b/openpype/hosts/photoshop/plugins/create/create_flatten_image.py
@@ -4,6 +4,7 @@ from openpype.lib import BoolDef
import openpype.hosts.photoshop.api as api
from openpype.hosts.photoshop.lib import PSAutoCreator
from openpype.pipeline.create import get_subset_name
+from openpype.lib import prepare_template_data
from openpype.client import get_asset_by_name
@@ -37,19 +38,14 @@ class AutoImageCreator(PSAutoCreator):
asset_doc = get_asset_by_name(project_name, asset_name)
if existing_instance is None:
- subset_name = get_subset_name(
- self.family, self.default_variant, task_name, asset_doc,
+ subset_name = self.get_subset_name(
+ self.default_variant, task_name, asset_doc,
project_name, host_name
)
- publishable_ids = [layer.id for layer in api.stub().get_layers()
- if layer.visible]
data = {
"asset": asset_name,
"task": task_name,
- # ids are "virtual" layers, won't get grouped as 'members' do
- # same difference in color coded layers in WP
- "ids": publishable_ids
}
if not self.active_on_create:
@@ -69,8 +65,8 @@ class AutoImageCreator(PSAutoCreator):
existing_instance["asset"] != asset_name
or existing_instance["task"] != task_name
):
- subset_name = get_subset_name(
- self.family, self.default_variant, task_name, asset_doc,
+ subset_name = self.get_subset_name(
+ self.default_variant, task_name, asset_doc,
project_name, host_name
)
@@ -118,3 +114,29 @@ class AutoImageCreator(PSAutoCreator):
Artist might disable this instance from publishing or from creating
review for it though.
"""
+
+ def get_subset_name(
+ self,
+ variant,
+ task_name,
+ asset_doc,
+ project_name,
+ host_name=None,
+ instance=None
+ ):
+ dynamic_data = prepare_template_data({"layer": "{layer}"})
+ subset_name = get_subset_name(
+ self.family, variant, task_name, asset_doc,
+ project_name, host_name, dynamic_data=dynamic_data
+ )
+ return self._clean_subset_name(subset_name)
+
+ def _clean_subset_name(self, subset_name):
+ """Clean all variants leftover {layer} from subset name."""
+ dynamic_data = prepare_template_data({"layer": "{layer}"})
+ for value in dynamic_data.values():
+ if value in subset_name:
+ return (subset_name.replace(value, "")
+ .replace("__", "_")
+ .replace("..", "."))
+ return subset_name
diff --git a/openpype/hosts/photoshop/plugins/create/create_image.py b/openpype/hosts/photoshop/plugins/create/create_image.py
index 8d3ac9f459..af20d456e0 100644
--- a/openpype/hosts/photoshop/plugins/create/create_image.py
+++ b/openpype/hosts/photoshop/plugins/create/create_image.py
@@ -94,12 +94,17 @@ class ImageCreator(Creator):
name = self._clean_highlights(stub, directory)
layer_names_in_hierarchy.append(name)
- data.update({"subset": subset_name})
- data.update({"members": [str(group.id)]})
- data.update({"layer_name": layer_name})
- data.update({"long_name": "_".join(layer_names_in_hierarchy)})
+ data_update = {
+ "subset": subset_name,
+ "members": [str(group.id)],
+ "layer_name": layer_name,
+ "long_name": "_".join(layer_names_in_hierarchy)
+ }
+ data.update(data_update)
- creator_attributes = {"mark_for_review": self.mark_for_review}
+ mark_for_review = (pre_create_data.get("mark_for_review") or
+ self.mark_for_review)
+ creator_attributes = {"mark_for_review": mark_for_review}
data.update({"creator_attributes": creator_attributes})
if not self.active_on_create:
@@ -124,8 +129,6 @@ class ImageCreator(Creator):
if creator_id == self.identifier:
instance_data = self._handle_legacy(instance_data)
- layer = api.stub().get_layer(instance_data["members"][0])
- instance_data["layer"] = layer
instance = CreatedInstance.from_existing(
instance_data, self
)
diff --git a/openpype/hosts/photoshop/plugins/publish/collect_auto_image_refresh.py b/openpype/hosts/photoshop/plugins/publish/collect_auto_image_refresh.py
new file mode 100644
index 0000000000..741fb0e9cd
--- /dev/null
+++ b/openpype/hosts/photoshop/plugins/publish/collect_auto_image_refresh.py
@@ -0,0 +1,24 @@
+import pyblish.api
+
+from openpype.hosts.photoshop import api as photoshop
+
+
+class CollectAutoImageRefresh(pyblish.api.ContextPlugin):
+ """Refreshes auto_image instance with currently visible layers..
+ """
+
+ label = "Collect Auto Image Refresh"
+ order = pyblish.api.CollectorOrder
+ hosts = ["photoshop"]
+ order = pyblish.api.CollectorOrder + 0.2
+
+ def process(self, context):
+ for instance in context:
+ creator_identifier = instance.data.get("creator_identifier")
+ if creator_identifier and creator_identifier == "auto_image":
+ self.log.debug("Auto image instance found, won't create new")
+ # refresh existing auto image instance with current visible
+ publishable_ids = [layer.id for layer in photoshop.stub().get_layers() # noqa
+ if layer.visible]
+ instance.data["ids"] = publishable_ids
+ return
diff --git a/openpype/hosts/photoshop/plugins/publish/collect_image.py b/openpype/hosts/photoshop/plugins/publish/collect_image.py
new file mode 100644
index 0000000000..64727cef33
--- /dev/null
+++ b/openpype/hosts/photoshop/plugins/publish/collect_image.py
@@ -0,0 +1,20 @@
+import pyblish.api
+
+from openpype.hosts.photoshop import api
+
+
+class CollectImage(pyblish.api.InstancePlugin):
+ """Collect layer metadata into a instance.
+
+ Used later in validation
+ """
+ order = pyblish.api.CollectorOrder + 0.200
+ label = 'Collect Image'
+
+ hosts = ["photoshop"]
+ families = ["image"]
+
+ def process(self, instance):
+ if instance.data.get("members"):
+ layer = api.stub().get_layer(instance.data["members"][0])
+ instance.data["layer"] = layer
diff --git a/openpype/hosts/photoshop/plugins/publish/extract_image.py b/openpype/hosts/photoshop/plugins/publish/extract_image.py
index cdb28c742d..680f580cc0 100644
--- a/openpype/hosts/photoshop/plugins/publish/extract_image.py
+++ b/openpype/hosts/photoshop/plugins/publish/extract_image.py
@@ -45,9 +45,11 @@ class ExtractImage(pyblish.api.ContextPlugin):
# Perform extraction
files = {}
ids = set()
- layer = instance.data.get("layer")
- if layer:
- ids.add(layer.id)
+ # real layers and groups
+ members = instance.data("members")
+ if members:
+ ids.update(set([int(member) for member in members]))
+ # virtual groups collected by color coding or auto_image
add_ids = instance.data.pop("ids", None)
if add_ids:
ids.update(set(add_ids))
diff --git a/openpype/hosts/photoshop/plugins/publish/extract_review.py b/openpype/hosts/photoshop/plugins/publish/extract_review.py
index 4aa7a05bd1..afddbdba31 100644
--- a/openpype/hosts/photoshop/plugins/publish/extract_review.py
+++ b/openpype/hosts/photoshop/plugins/publish/extract_review.py
@@ -1,4 +1,5 @@
import os
+import shutil
from PIL import Image
from openpype.lib import (
@@ -55,6 +56,7 @@ class ExtractReview(publish.Extractor):
}
if instance.data["family"] != "review":
+ self.log.debug("Existing extracted file from image family used.")
# enable creation of review, without this jpg review would clash
# with jpg of the image family
output_name = repre_name
@@ -62,8 +64,15 @@ class ExtractReview(publish.Extractor):
repre_skeleton.update({"name": repre_name,
"outputName": output_name})
- if self.make_image_sequence and len(layers) > 1:
- self.log.info("Extract layers to image sequence.")
+ img_file = self.output_seq_filename % 0
+ self._prepare_file_for_image_family(img_file, instance,
+ staging_dir)
+ repre_skeleton.update({
+ "files": img_file,
+ })
+ processed_img_names = [img_file]
+ elif self.make_image_sequence and len(layers) > 1:
+ self.log.debug("Extract layers to image sequence.")
img_list = self._save_sequence_images(staging_dir, layers)
repre_skeleton.update({
@@ -72,17 +81,17 @@ class ExtractReview(publish.Extractor):
"fps": fps,
"files": img_list,
})
- instance.data["representations"].append(repre_skeleton)
processed_img_names = img_list
else:
- self.log.info("Extract layers to flatten image.")
- img_list = self._save_flatten_image(staging_dir, layers)
+ self.log.debug("Extract layers to flatten image.")
+ img_file = self._save_flatten_image(staging_dir, layers)
repre_skeleton.update({
- "files": img_list,
+ "files": img_file,
})
- instance.data["representations"].append(repre_skeleton)
- processed_img_names = [img_list]
+ processed_img_names = [img_file]
+
+ instance.data["representations"].append(repre_skeleton)
ffmpeg_args = get_ffmpeg_tool_args("ffmpeg")
@@ -111,6 +120,35 @@ class ExtractReview(publish.Extractor):
self.log.info(f"Extracted {instance} to {staging_dir}")
+ def _prepare_file_for_image_family(self, img_file, instance, staging_dir):
+ """Converts existing file for image family to .jpg
+
+ Image instance could have its own separate review (instance per layer
+ for example). This uses extracted file instead of extracting again.
+ Args:
+ img_file (str): name of output file (with 0000 value for ffmpeg
+ later)
+ instance:
+ staging_dir (str): temporary folder where extracted file is located
+ """
+ repre_file = instance.data["representations"][0]
+ source_file_path = os.path.join(repre_file["stagingDir"],
+ repre_file["files"])
+ if not os.path.exists(source_file_path):
+ raise RuntimeError(f"{source_file_path} doesn't exist for "
+ "review to create from")
+ _, ext = os.path.splitext(repre_file["files"])
+ if ext != ".jpg":
+ im = Image.open(source_file_path)
+ # without this it produces messy low quality jpg
+ rgb_im = Image.new("RGBA", (im.width, im.height), "#ffffff")
+ rgb_im.alpha_composite(im)
+ rgb_im.convert("RGB").save(os.path.join(staging_dir, img_file))
+ else:
+ # handles already .jpg
+ shutil.copy(source_file_path,
+ os.path.join(staging_dir, img_file))
+
def _generate_mov(self, ffmpeg_path, instance, fps, no_of_frames,
source_files_pattern, staging_dir):
"""Generates .mov to upload to Ftrack.
@@ -218,6 +256,11 @@ class ExtractReview(publish.Extractor):
(list) of PSItem
"""
layers = []
+ # creating review for existing 'image' instance
+ if instance.data["family"] == "image" and instance.data.get("layer"):
+ layers.append(instance.data["layer"])
+ return layers
+
for image_instance in instance.context:
if image_instance.data["family"] != "image":
continue
diff --git a/openpype/hosts/tvpaint/api/communication_server.py b/openpype/hosts/tvpaint/api/communication_server.py
index 6f76c25e0c..d67ef8f798 100644
--- a/openpype/hosts/tvpaint/api/communication_server.py
+++ b/openpype/hosts/tvpaint/api/communication_server.py
@@ -11,7 +11,7 @@ import filecmp
import tempfile
import threading
import shutil
-from queue import Queue
+
from contextlib import closing
from aiohttp import web
@@ -319,19 +319,19 @@ class QtTVPaintRpc(BaseTVPaintRpc):
async def workfiles_tool(self):
log.info("Triggering Workfile tool")
item = MainThreadItem(self.tools_helper.show_workfiles)
- self._execute_in_main_thread(item)
+ self._execute_in_main_thread(item, wait=False)
return
async def loader_tool(self):
log.info("Triggering Loader tool")
item = MainThreadItem(self.tools_helper.show_loader)
- self._execute_in_main_thread(item)
+ self._execute_in_main_thread(item, wait=False)
return
async def publish_tool(self):
log.info("Triggering Publish tool")
item = MainThreadItem(self.tools_helper.show_publisher_tool)
- self._execute_in_main_thread(item)
+ self._execute_in_main_thread(item, wait=False)
return
async def scene_inventory_tool(self):
@@ -350,13 +350,13 @@ class QtTVPaintRpc(BaseTVPaintRpc):
async def library_loader_tool(self):
log.info("Triggering Library loader tool")
item = MainThreadItem(self.tools_helper.show_library_loader)
- self._execute_in_main_thread(item)
+ self._execute_in_main_thread(item, wait=False)
return
async def experimental_tools(self):
log.info("Triggering Library loader tool")
item = MainThreadItem(self.tools_helper.show_experimental_tools_dialog)
- self._execute_in_main_thread(item)
+ self._execute_in_main_thread(item, wait=False)
return
async def _async_execute_in_main_thread(self, item, **kwargs):
@@ -867,7 +867,7 @@ class QtCommunicator(BaseCommunicator):
def __init__(self, qt_app):
super().__init__()
- self.callback_queue = Queue()
+ self.callback_queue = collections.deque()
self.qt_app = qt_app
def _create_routes(self):
@@ -880,14 +880,14 @@ class QtCommunicator(BaseCommunicator):
def execute_in_main_thread(self, main_thread_item, wait=True):
"""Add `MainThreadItem` to callback queue and wait for result."""
- self.callback_queue.put(main_thread_item)
+ self.callback_queue.append(main_thread_item)
if wait:
return main_thread_item.wait()
return
async def async_execute_in_main_thread(self, main_thread_item, wait=True):
"""Add `MainThreadItem` to callback queue and wait for result."""
- self.callback_queue.put(main_thread_item)
+ self.callback_queue.append(main_thread_item)
if wait:
return await main_thread_item.async_wait()
@@ -904,9 +904,9 @@ class QtCommunicator(BaseCommunicator):
self._exit()
return None
- if self.callback_queue.empty():
- return None
- return self.callback_queue.get()
+ if self.callback_queue:
+ return self.callback_queue.popleft()
+ return None
def _on_client_connect(self):
super()._on_client_connect()
diff --git a/openpype/hosts/tvpaint/tvpaint_plugin/plugin_code/library.cpp b/openpype/hosts/tvpaint/tvpaint_plugin/plugin_code/library.cpp
index 88106bc770..ec45a45123 100644
--- a/openpype/hosts/tvpaint/tvpaint_plugin/plugin_code/library.cpp
+++ b/openpype/hosts/tvpaint/tvpaint_plugin/plugin_code/library.cpp
@@ -573,56 +573,6 @@ void FAR PASCAL PI_Close( PIFilter* iFilter )
}
-/**************************************************************************************/
-// we have something to do !
-
-int FAR PASCAL PI_Parameters( PIFilter* iFilter, char* iArg )
-{
- if( !iArg )
- {
-
- // If the requester is not open, we open it.
- if( Data.mReq == 0)
- {
- // Create empty requester because menu items are defined with
- // `define_menu` callback
- DWORD req = TVOpenFilterReqEx(
- iFilter,
- 185,
- 20,
- NULL,
- NULL,
- PIRF_STANDARD_REQ | PIRF_COLLAPSABLE_REQ,
- FILTERREQ_NO_TBAR
- );
- if( req == 0 )
- {
- TVWarning( iFilter, TXT_REQUESTER_ERROR );
- return 0;
- }
-
-
- Data.mReq = req;
- // This is a very simple requester, so we create it's content right here instead
- // of waiting for the PICBREQ_OPEN message...
- // Not recommended for more complex requesters. (see the other examples)
-
- // Sets the title of the requester.
- TVSetReqTitle( iFilter, Data.mReq, TXT_REQUESTER );
- // Request to listen to ticks
- TVGrabTicks(iFilter, req, PITICKS_FLAG_ON);
- }
- else
- {
- // If it is already open, we just put it on front of all other requesters.
- TVReqToFront( iFilter, Data.mReq );
- }
- }
-
- return 1;
-}
-
-
int newMenuItemsProcess(PIFilter* iFilter) {
// Menu items defined with `define_menu` should be propagated.
@@ -702,6 +652,62 @@ int newMenuItemsProcess(PIFilter* iFilter) {
return 1;
}
+
+/**************************************************************************************/
+// we have something to do !
+
+int FAR PASCAL PI_Parameters( PIFilter* iFilter, char* iArg )
+{
+ if( !iArg )
+ {
+
+ // If the requester is not open, we open it.
+ if( Data.mReq == 0)
+ {
+ // Create empty requester because menu items are defined with
+ // `define_menu` callback
+ DWORD req = TVOpenFilterReqEx(
+ iFilter,
+ 185,
+ 20,
+ NULL,
+ NULL,
+ PIRF_STANDARD_REQ | PIRF_COLLAPSABLE_REQ,
+ FILTERREQ_NO_TBAR
+ );
+ if( req == 0 )
+ {
+ TVWarning( iFilter, TXT_REQUESTER_ERROR );
+ return 0;
+ }
+
+ Data.mReq = req;
+
+ // This is a very simple requester, so we create it's content right here instead
+ // of waiting for the PICBREQ_OPEN message...
+ // Not recommended for more complex requesters. (see the other examples)
+
+ // Sets the title of the requester.
+ TVSetReqTitle( iFilter, Data.mReq, TXT_REQUESTER );
+ // Request to listen to ticks
+ TVGrabTicks(iFilter, req, PITICKS_FLAG_ON);
+
+ if ( Data.firstParams == true ) {
+ Data.firstParams = false;
+ } else {
+ newMenuItemsProcess(iFilter);
+ }
+ }
+ else
+ {
+ // If it is already open, we just put it on front of all other requesters.
+ TVReqToFront( iFilter, Data.mReq );
+ }
+ }
+
+ return 1;
+}
+
/**************************************************************************************/
// something happened that needs our attention.
// Global variable where current button up data are stored
diff --git a/openpype/hosts/tvpaint/tvpaint_plugin/plugin_files/windows_x64/plugin/OpenPypePlugin.dll b/openpype/hosts/tvpaint/tvpaint_plugin/plugin_files/windows_x64/plugin/OpenPypePlugin.dll
index 7081778bee..9c6e969e24 100644
Binary files a/openpype/hosts/tvpaint/tvpaint_plugin/plugin_files/windows_x64/plugin/OpenPypePlugin.dll and b/openpype/hosts/tvpaint/tvpaint_plugin/plugin_files/windows_x64/plugin/OpenPypePlugin.dll differ
diff --git a/openpype/hosts/tvpaint/tvpaint_plugin/plugin_files/windows_x86/plugin/OpenPypePlugin.dll b/openpype/hosts/tvpaint/tvpaint_plugin/plugin_files/windows_x86/plugin/OpenPypePlugin.dll
index 0f2afec245..b573476a21 100644
Binary files a/openpype/hosts/tvpaint/tvpaint_plugin/plugin_files/windows_x86/plugin/OpenPypePlugin.dll and b/openpype/hosts/tvpaint/tvpaint_plugin/plugin_files/windows_x86/plugin/OpenPypePlugin.dll differ
diff --git a/openpype/settings/defaults/project_settings/nuke.json b/openpype/settings/defaults/project_settings/nuke.json
index b736c462ff..7961e77113 100644
--- a/openpype/settings/defaults/project_settings/nuke.json
+++ b/openpype/settings/defaults/project_settings/nuke.json
@@ -28,11 +28,7 @@
"colorManagement": "Nuke",
"OCIO_config": "nuke-default",
"workingSpaceLUT": "linear",
- "monitorLut": "sRGB",
- "int8Lut": "sRGB",
- "int16Lut": "sRGB",
- "logLut": "Cineon",
- "floatLut": "linear"
+ "monitorLut": "sRGB"
},
"nodes": {
"requiredNodes": [
diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_imageio.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_imageio.json
index d4cd332ef8..af826fcf46 100644
--- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_imageio.json
+++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_imageio.json
@@ -106,26 +106,6 @@
"type": "text",
"key": "monitorLut",
"label": "monitor"
- },
- {
- "type": "text",
- "key": "int8Lut",
- "label": "8-bit files"
- },
- {
- "type": "text",
- "key": "int16Lut",
- "label": "16-bit files"
- },
- {
- "type": "text",
- "key": "logLut",
- "label": "log files"
- },
- {
- "type": "text",
- "key": "floatLut",
- "label": "float files"
}
]
}
diff --git a/openpype/version.py b/openpype/version.py
index b6c56296bc..c4a87e7843 100644
--- a/openpype/version.py
+++ b/openpype/version.py
@@ -1,3 +1,3 @@
# -*- coding: utf-8 -*-
"""Package declaring Pype version."""
-__version__ = "3.16.6-nightly.1"
+__version__ = "3.16.7-nightly.1"
diff --git a/pyproject.toml b/pyproject.toml
index 68fbf19c91..f859e1aff4 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "OpenPype"
-version = "3.16.5" # OpenPype
+version = "3.16.6" # OpenPype
description = "Open VFX and Animation pipeline with support."
authors = ["OpenPype Team "]
license = "MIT License"
diff --git a/server_addon/nuke/server/settings/imageio.py b/server_addon/nuke/server/settings/imageio.py
index 44bb72769e..811b12104b 100644
--- a/server_addon/nuke/server/settings/imageio.py
+++ b/server_addon/nuke/server/settings/imageio.py
@@ -61,22 +61,6 @@ def ocio_configs_switcher_enum():
class WorkfileColorspaceSettings(BaseSettingsModel):
"""Nuke workfile colorspace preset. """
- """# TODO: enhance settings with host api:
- we need to add mapping to resolve properly keys.
- Nuke is excpecting camel case key names,
- but for better code consistency we need to
- be using snake_case:
-
- color_management = colorManagement
- ocio_config = OCIO_config
- working_space_name = workingSpaceLUT
- monitor_name = monitorLut
- monitor_out_name = monitorOutLut
- int_8_name = int8Lut
- int_16_name = int16Lut
- log_name = logLut
- float_name = floatLut
- """
colorManagement: Literal["Nuke", "OCIO"] = Field(
title="Color Management"
@@ -95,18 +79,6 @@ class WorkfileColorspaceSettings(BaseSettingsModel):
monitorLut: str = Field(
title="Monitor"
)
- int8Lut: str = Field(
- title="8-bit files"
- )
- int16Lut: str = Field(
- title="16-bit files"
- )
- logLut: str = Field(
- title="Log files"
- )
- floatLut: str = Field(
- title="Float files"
- )
class ReadColorspaceRulesItems(BaseSettingsModel):
@@ -233,10 +205,6 @@ DEFAULT_IMAGEIO_SETTINGS = {
"OCIO_config": "nuke-default",
"workingSpaceLUT": "linear",
"monitorLut": "sRGB",
- "int8Lut": "sRGB",
- "int16Lut": "sRGB",
- "logLut": "Cineon",
- "floatLut": "linear"
},
"nodes": {
"requiredNodes": [
diff --git a/website/docs/admin_hosts_aftereffects.md b/website/docs/admin_hosts_aftereffects.md
index 974428fe06..72fdb32faf 100644
--- a/website/docs/admin_hosts_aftereffects.md
+++ b/website/docs/admin_hosts_aftereffects.md
@@ -18,6 +18,10 @@ Location: Settings > Project > AfterEffects
## Publish plugins
+### Collect Review
+
+Enable/disable creation of auto instance of review.
+
### Validate Scene Settings
#### Skip Resolution Check for Tasks
@@ -28,6 +32,10 @@ Set regex pattern(s) to look for in a Task name to skip resolution check against
Set regex pattern(s) to look for in a Task name to skip `frameStart`, `frameEnd` check against values from DB.
+### ValidateContainers
+
+By default this validator will look loaded items with lower version than latest. This validator is context wide so it must be disabled in Context button.
+
### AfterEffects Submit to Deadline
* `Use Published scene` - Set to True (green) when Deadline should take published scene as a source instead of uploaded local one.
diff --git a/website/docs/admin_hosts_photoshop.md b/website/docs/admin_hosts_photoshop.md
index de684f01d2..d79789760e 100644
--- a/website/docs/admin_hosts_photoshop.md
+++ b/website/docs/admin_hosts_photoshop.md
@@ -33,7 +33,6 @@ Provides list of [variants](artist_concepts.md#variant) that will be shown to an
Provides simplified publishing process. It will create single `image` instance for artist automatically. This instance will
produce flatten image from all visible layers in a workfile.
-- Subset template for flatten image - provide template for subset name for this instance (example `imageBeauty`)
- Review - should be separate review created for this instance
### Create Review
@@ -111,11 +110,11 @@ Set Byte limit for review file. Applicable if gigantic `image` instances are pro
#### Extract jpg Options
-Handles tags for produced `.jpg` representation. `Create review` and `Add review to Ftrack` are defaults.
+Handles tags for produced `.jpg` representation. `Create review` and `Add review to Ftrack` are defaults.
#### Extract mov Options
-Handles tags for produced `.mov` representation. `Create review` and `Add review to Ftrack` are defaults.
+Handles tags for produced `.mov` representation. `Create review` and `Add review to Ftrack` are defaults.
### Workfile Builder
@@ -124,4 +123,4 @@ Allows to open prepared workfile for an artist when no workfile exists. Useful t
Could be configured per `Task type`, eg. `composition` task type could use different `.psd` template file than `art` task.
Workfile template must be accessible for all artists.
-(Currently not handled by [SiteSync](module_site_sync.md))
\ No newline at end of file
+(Currently not handled by [SiteSync](module_site_sync.md))
diff --git a/website/docs/assets/admin_hosts_photoshop_settings.png b/website/docs/assets/admin_hosts_photoshop_settings.png
index aaa6ecbed7..9478fbedf7 100644
Binary files a/website/docs/assets/admin_hosts_photoshop_settings.png and b/website/docs/assets/admin_hosts_photoshop_settings.png differ