diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
index dfadd0088c..be0a6e1299 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -35,9 +35,11 @@ body:
label: Version
description: What version are you running? Look to OpenPype Tray
options:
+ - 3.18.2-nightly.1
+ - 3.18.1
+ - 3.18.1-nightly.1
- 3.18.0
- 3.17.7
- - 3.18.1-nightly.1
- 3.17.7-nightly.7
- 3.17.7-nightly.6
- 3.17.7-nightly.5
@@ -133,8 +135,6 @@ body:
- 3.15.4-nightly.3
- 3.15.4-nightly.2
- 3.15.4-nightly.1
- - 3.15.3
- - 3.15.3-nightly.4
validations:
required: true
- type: dropdown
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a10c2715a3..f309d904eb 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,27 @@
# Changelog
+## [3.18.1](https://github.com/ynput/OpenPype/tree/3.18.1)
+
+
+[Full Changelog](https://github.com/ynput/OpenPype/compare/3.18.0...3.18.1)
+
+### **🚀 Enhancements**
+
+
+
+AYON: Update ayon api to 1.0.0-rc.3 #6052
+
+Updated ayon python api to 1.0.0-rc.3.
+
+
+___
+
+
+
+
+
+
## [3.18.0](https://github.com/ynput/OpenPype/tree/3.18.0)
diff --git a/README.md b/README.md
index ed3e058002..a79b9f2582 100644
--- a/README.md
+++ b/README.md
@@ -7,6 +7,10 @@ OpenPype
[](https://github.com/pypeclub/pype/actions/workflows/documentation.yml) 
+## Important Notice!
+
+OpenPype as a standalone product has reach end of it's life and this repository is now used as a pipeline core code for [AYON](https://ynput.io/ayon/). You can read more details about the end of life process here https://community.ynput.io/t/openpype-end-of-life-timeline/877
+
Introduction
------------
diff --git a/openpype/hosts/fusion/plugins/create/create_saver.py b/openpype/hosts/fusion/plugins/create/create_saver.py
index ecf36abdd2..5870828b41 100644
--- a/openpype/hosts/fusion/plugins/create/create_saver.py
+++ b/openpype/hosts/fusion/plugins/create/create_saver.py
@@ -14,7 +14,7 @@ from openpype.pipeline import (
legacy_io,
Creator as NewCreator,
CreatedInstance,
- Anatomy
+ Anatomy,
)
@@ -27,28 +27,21 @@ class CreateSaver(NewCreator):
description = "Fusion Saver to generate image sequence"
icon = "fa5.eye"
- instance_attributes = [
- "reviewable"
- ]
+ instance_attributes = ["reviewable"]
+ image_format = "exr"
# TODO: This should be renamed together with Nuke so it is aligned
temp_rendering_path_template = (
- "{workdir}/renders/fusion/{subset}/{subset}.{frame}.{ext}")
+ "{workdir}/renders/fusion/{subset}/{subset}.{frame}.{ext}"
+ )
def create(self, subset_name, instance_data, pre_create_data):
- self.pass_pre_attributes_to_instance(
- instance_data,
- pre_create_data
+ self.pass_pre_attributes_to_instance(instance_data, pre_create_data)
+
+ instance_data.update(
+ {"id": "pyblish.avalon.instance", "subset": subset_name}
)
- instance_data.update({
- "id": "pyblish.avalon.instance",
- "subset": subset_name
- })
-
- # TODO: Add pre_create attributes to choose file format?
- file_format = "OpenEXRFormat"
-
comp = get_current_comp()
with comp_lock_and_undo_chunk(comp):
args = (-32768, -32768) # Magical position numbers
@@ -56,19 +49,6 @@ class CreateSaver(NewCreator):
self._update_tool_with_data(saver, data=instance_data)
- saver["OutputFormat"] = file_format
-
- # Check file format settings are available
- if saver[file_format] is None:
- raise RuntimeError(
- f"File format is not set to {file_format}, this is a bug"
- )
-
- # Set file format attributes
- saver[file_format]["Depth"] = 0 # Auto | float16 | float32
- # TODO Is this needed?
- saver[file_format]["SaveAlpha"] = 1
-
# Register the CreatedInstance
instance = CreatedInstance(
family=self.family,
@@ -140,8 +120,15 @@ class CreateSaver(NewCreator):
return
original_subset = tool.GetData("openpype.subset")
+ original_format = tool.GetData(
+ "openpype.creator_attributes.image_format"
+ )
+
subset = data["subset"]
- if original_subset != subset:
+ if (
+ original_subset != subset
+ or original_format != data["creator_attributes"]["image_format"]
+ ):
self._configure_saver_tool(data, tool, subset)
def _configure_saver_tool(self, data, tool, subset):
@@ -151,17 +138,17 @@ class CreateSaver(NewCreator):
anatomy = Anatomy()
frame_padding = anatomy.templates["frame_padding"]
+ # get output format
+ ext = data["creator_attributes"]["image_format"]
+
# Subset change detected
workdir = os.path.normpath(legacy_io.Session["AVALON_WORKDIR"])
- formatting_data.update({
- "workdir": workdir,
- "frame": "0" * frame_padding,
- "ext": "exr"
- })
+ formatting_data.update(
+ {"workdir": workdir, "frame": "0" * frame_padding, "ext": ext}
+ )
# build file path to render
- filepath = self.temp_rendering_path_template.format(
- **formatting_data)
+ filepath = self.temp_rendering_path_template.format(**formatting_data)
comp = get_current_comp()
tool["Clip"] = comp.ReverseMapPath(os.path.normpath(filepath))
@@ -201,7 +188,8 @@ class CreateSaver(NewCreator):
attr_defs = [
self._get_render_target_enum(),
self._get_reviewable_bool(),
- self._get_frame_range_enum()
+ self._get_frame_range_enum(),
+ self._get_image_format_enum(),
]
return attr_defs
@@ -209,11 +197,7 @@ class CreateSaver(NewCreator):
"""Settings for publish page"""
return self.get_pre_create_attr_defs()
- def pass_pre_attributes_to_instance(
- self,
- instance_data,
- pre_create_data
- ):
+ def pass_pre_attributes_to_instance(self, instance_data, pre_create_data):
creator_attrs = instance_data["creator_attributes"] = {}
for pass_key in pre_create_data.keys():
creator_attrs[pass_key] = pre_create_data[pass_key]
@@ -236,13 +220,13 @@ class CreateSaver(NewCreator):
frame_range_options = {
"asset_db": "Current asset context",
"render_range": "From render in/out",
- "comp_range": "From composition timeline"
+ "comp_range": "From composition timeline",
}
return EnumDef(
"frame_range_source",
items=frame_range_options,
- label="Frame range source"
+ label="Frame range source",
)
def _get_reviewable_bool(self):
@@ -252,20 +236,33 @@ class CreateSaver(NewCreator):
label="Review",
)
+ def _get_image_format_enum(self):
+ image_format_options = ["exr", "tga", "tif", "png", "jpg"]
+ return EnumDef(
+ "image_format",
+ items=image_format_options,
+ default=self.image_format,
+ label="Output Image Format",
+ )
+
def apply_settings(self, project_settings):
"""Method called on initialization of plugin to apply settings."""
# plugin settings
- plugin_settings = (
- project_settings["fusion"]["create"][self.__class__.__name__]
- )
+ plugin_settings = project_settings["fusion"]["create"][
+ self.__class__.__name__
+ ]
# individual attributes
self.instance_attributes = plugin_settings.get(
- "instance_attributes") or self.instance_attributes
- self.default_variants = plugin_settings.get(
- "default_variants") or self.default_variants
- self.temp_rendering_path_template = (
- plugin_settings.get("temp_rendering_path_template")
- or self.temp_rendering_path_template
+ "instance_attributes", self.instance_attributes
+ )
+ self.default_variants = plugin_settings.get(
+ "default_variants", self.default_variants
+ )
+ self.temp_rendering_path_template = plugin_settings.get(
+ "temp_rendering_path_template", self.temp_rendering_path_template
+ )
+ self.image_format = plugin_settings.get(
+ "image_format", self.image_format
)
diff --git a/openpype/hosts/fusion/plugins/publish/extract_render_local.py b/openpype/hosts/fusion/plugins/publish/extract_render_local.py
index 08d608139d..068df22c06 100644
--- a/openpype/hosts/fusion/plugins/publish/extract_render_local.py
+++ b/openpype/hosts/fusion/plugins/publish/extract_render_local.py
@@ -146,11 +146,15 @@ class FusionRenderLocal(
staging_dir = os.path.dirname(path)
+ files = [os.path.basename(f) for f in expected_files]
+ if len(expected_files) == 1:
+ files = files[0]
+
repre = {
"name": ext[1:],
"ext": ext[1:],
"frameStart": f"%0{padding}d" % start,
- "files": [os.path.basename(f) for f in expected_files],
+ "files": files,
"stagingDir": staging_dir,
}
diff --git a/openpype/hosts/max/api/lib.py b/openpype/hosts/max/api/lib.py
index 298084a4e8..8531233bb2 100644
--- a/openpype/hosts/max/api/lib.py
+++ b/openpype/hosts/max/api/lib.py
@@ -511,3 +511,20 @@ def render_resolution(width, height):
finally:
rt.renderWidth = current_renderWidth
rt.renderHeight = current_renderHeight
+
+
+@contextlib.contextmanager
+def suspended_refresh():
+ """Suspended refresh for scene and modify panel redraw.
+ """
+ if is_headless():
+ yield
+ return
+ rt.disableSceneRedraw()
+ rt.suspendEditing()
+ try:
+ yield
+
+ finally:
+ rt.enableSceneRedraw()
+ rt.resumeEditing()
diff --git a/openpype/hosts/max/plugins/publish/extract_pointcache.py b/openpype/hosts/max/plugins/publish/extract_alembic.py
similarity index 56%
rename from openpype/hosts/max/plugins/publish/extract_pointcache.py
rename to openpype/hosts/max/plugins/publish/extract_alembic.py
index f6a8500c08..24e0121a61 100644
--- a/openpype/hosts/max/plugins/publish/extract_pointcache.py
+++ b/openpype/hosts/max/plugins/publish/extract_alembic.py
@@ -39,45 +39,41 @@ Note:
"""
import os
import pyblish.api
-from openpype.pipeline import publish
+from openpype.pipeline import publish, OptionalPyblishPluginMixin
from pymxs import runtime as rt
from openpype.hosts.max.api import maintained_selection
+from openpype.hosts.max.api.lib import suspended_refresh
+from openpype.lib import BoolDef
-class ExtractAlembic(publish.Extractor):
+class ExtractAlembic(publish.Extractor,
+ OptionalPyblishPluginMixin):
order = pyblish.api.ExtractorOrder
label = "Extract Pointcache"
hosts = ["max"]
families = ["pointcache"]
+ optional = True
def process(self, instance):
- start = instance.data["frameStartHandle"]
- end = instance.data["frameEndHandle"]
-
- self.log.debug("Extracting pointcache ...")
+ if not self.is_active(instance.data):
+ return
parent_dir = self.staging_dir(instance)
file_name = "{name}.abc".format(**instance.data)
path = os.path.join(parent_dir, file_name)
- # We run the render
- self.log.info("Writing alembic '%s' to '%s'" % (file_name, parent_dir))
-
- rt.AlembicExport.ArchiveType = rt.name("ogawa")
- rt.AlembicExport.CoordinateSystem = rt.name("maya")
- rt.AlembicExport.StartFrame = start
- rt.AlembicExport.EndFrame = end
-
- with maintained_selection():
- # select and export
- node_list = instance.data["members"]
- rt.Select(node_list)
- rt.exportFile(
- path,
- rt.name("noPrompt"),
- selectedOnly=True,
- using=rt.AlembicExport,
- )
+ with suspended_refresh():
+ self._set_abc_attributes(instance)
+ with maintained_selection():
+ # select and export
+ node_list = instance.data["members"]
+ rt.Select(node_list)
+ rt.exportFile(
+ path,
+ rt.name("noPrompt"),
+ selectedOnly=True,
+ using=rt.AlembicExport,
+ )
if "representations" not in instance.data:
instance.data["representations"] = []
@@ -89,3 +85,51 @@ class ExtractAlembic(publish.Extractor):
"stagingDir": parent_dir,
}
instance.data["representations"].append(representation)
+
+ def _set_abc_attributes(self, instance):
+ start = instance.data["frameStartHandle"]
+ end = instance.data["frameEndHandle"]
+ attr_values = self.get_attr_values_from_data(instance.data)
+ custom_attrs = attr_values.get("custom_attrs", False)
+ if not custom_attrs:
+ self.log.debug(
+ "No Custom Attributes included in this abc export...")
+ rt.AlembicExport.ArchiveType = rt.Name("ogawa")
+ rt.AlembicExport.CoordinateSystem = rt.Name("maya")
+ rt.AlembicExport.StartFrame = start
+ rt.AlembicExport.EndFrame = end
+ rt.AlembicExport.CustomAttributes = custom_attrs
+
+ @classmethod
+ def get_attribute_defs(cls):
+ return [
+ BoolDef("custom_attrs",
+ label="Custom Attributes",
+ default=False),
+ ]
+
+
+class ExtractCameraAlembic(ExtractAlembic):
+ """Extract Camera with AlembicExport."""
+
+ label = "Extract Alembic Camera"
+ families = ["camera"]
+
+
+class ExtractModel(ExtractAlembic):
+ """Extract Geometry in Alembic Format"""
+ label = "Extract Geometry (Alembic)"
+ families = ["model"]
+
+ def _set_abc_attributes(self, instance):
+ attr_values = self.get_attr_values_from_data(instance.data)
+ custom_attrs = attr_values.get("custom_attrs", False)
+ if not custom_attrs:
+ self.log.debug(
+ "No Custom Attributes included in this abc export...")
+ rt.AlembicExport.ArchiveType = rt.name("ogawa")
+ rt.AlembicExport.CoordinateSystem = rt.name("maya")
+ rt.AlembicExport.CustomAttributes = custom_attrs
+ rt.AlembicExport.UVs = True
+ rt.AlembicExport.VertexColors = True
+ rt.AlembicExport.PreserveInstances = True
diff --git a/openpype/hosts/max/plugins/publish/extract_camera_abc.py b/openpype/hosts/max/plugins/publish/extract_camera_abc.py
deleted file mode 100644
index a42f27be6e..0000000000
--- a/openpype/hosts/max/plugins/publish/extract_camera_abc.py
+++ /dev/null
@@ -1,64 +0,0 @@
-import os
-
-import pyblish.api
-from pymxs import runtime as rt
-
-from openpype.hosts.max.api import maintained_selection
-from openpype.pipeline import OptionalPyblishPluginMixin, publish
-
-
-class ExtractCameraAlembic(publish.Extractor, OptionalPyblishPluginMixin):
- """Extract Camera with AlembicExport."""
-
- order = pyblish.api.ExtractorOrder - 0.1
- label = "Extract Alembic Camera"
- hosts = ["max"]
- families = ["camera"]
- optional = True
-
- def process(self, instance):
- if not self.is_active(instance.data):
- return
- start = instance.data["frameStartHandle"]
- end = instance.data["frameEndHandle"]
-
- self.log.info("Extracting Camera ...")
-
- stagingdir = self.staging_dir(instance)
- filename = "{name}.abc".format(**instance.data)
- path = os.path.join(stagingdir, filename)
-
- # We run the render
- self.log.info(f"Writing alembic '{filename}' to '{stagingdir}'")
-
- rt.AlembicExport.ArchiveType = rt.Name("ogawa")
- rt.AlembicExport.CoordinateSystem = rt.Name("maya")
- rt.AlembicExport.StartFrame = start
- rt.AlembicExport.EndFrame = end
- rt.AlembicExport.CustomAttributes = True
-
- with maintained_selection():
- # select and export
- node_list = instance.data["members"]
- rt.Select(node_list)
- rt.ExportFile(
- path,
- rt.Name("noPrompt"),
- selectedOnly=True,
- using=rt.AlembicExport,
- )
-
- self.log.info("Performing Extraction ...")
- if "representations" not in instance.data:
- instance.data["representations"] = []
-
- representation = {
- "name": "abc",
- "ext": "abc",
- "files": filename,
- "stagingDir": stagingdir,
- "frameStart": start,
- "frameEnd": end,
- }
- instance.data["representations"].append(representation)
- self.log.info(f"Extracted instance '{instance.name}' to: {path}")
diff --git a/openpype/hosts/max/plugins/publish/extract_camera_fbx.py b/openpype/hosts/max/plugins/publish/extract_camera_fbx.py
index 537c88eb4d..4b5631b05f 100644
--- a/openpype/hosts/max/plugins/publish/extract_camera_fbx.py
+++ b/openpype/hosts/max/plugins/publish/extract_camera_fbx.py
@@ -20,13 +20,10 @@ class ExtractCameraFbx(publish.Extractor, OptionalPyblishPluginMixin):
if not self.is_active(instance.data):
return
- self.log.debug("Extracting Camera ...")
stagingdir = self.staging_dir(instance)
filename = "{name}.fbx".format(**instance.data)
filepath = os.path.join(stagingdir, filename)
- self.log.info(f"Writing fbx file '{filename}' to '{filepath}'")
-
rt.FBXExporterSetParam("Animation", True)
rt.FBXExporterSetParam("Cameras", True)
rt.FBXExporterSetParam("AxisConversionMethod", "Animation")
diff --git a/openpype/hosts/max/plugins/publish/extract_max_scene_raw.py b/openpype/hosts/max/plugins/publish/extract_max_scene_raw.py
index a7a889c587..791cc65fcd 100644
--- a/openpype/hosts/max/plugins/publish/extract_max_scene_raw.py
+++ b/openpype/hosts/max/plugins/publish/extract_max_scene_raw.py
@@ -26,7 +26,6 @@ class ExtractMaxSceneRaw(publish.Extractor, OptionalPyblishPluginMixin):
filename = "{name}.max".format(**instance.data)
max_path = os.path.join(stagingdir, filename)
- self.log.info("Writing max file '%s' to '%s'" % (filename, max_path))
if "representations" not in instance.data:
instance.data["representations"] = []
diff --git a/openpype/hosts/max/plugins/publish/extract_model.py b/openpype/hosts/max/plugins/publish/extract_model.py
deleted file mode 100644
index 38f4848c5e..0000000000
--- a/openpype/hosts/max/plugins/publish/extract_model.py
+++ /dev/null
@@ -1,63 +0,0 @@
-import os
-import pyblish.api
-from openpype.pipeline import publish, OptionalPyblishPluginMixin
-from pymxs import runtime as rt
-from openpype.hosts.max.api import maintained_selection
-
-
-class ExtractModel(publish.Extractor, OptionalPyblishPluginMixin):
- """
- Extract Geometry in Alembic Format
- """
-
- order = pyblish.api.ExtractorOrder - 0.1
- label = "Extract Geometry (Alembic)"
- hosts = ["max"]
- families = ["model"]
- optional = True
-
- def process(self, instance):
- if not self.is_active(instance.data):
- return
-
- self.log.debug("Extracting Geometry ...")
-
- stagingdir = self.staging_dir(instance)
- filename = "{name}.abc".format(**instance.data)
- filepath = os.path.join(stagingdir, filename)
-
- # We run the render
- self.log.info("Writing alembic '%s' to '%s'" % (filename, stagingdir))
-
- rt.AlembicExport.ArchiveType = rt.name("ogawa")
- rt.AlembicExport.CoordinateSystem = rt.name("maya")
- rt.AlembicExport.CustomAttributes = True
- rt.AlembicExport.UVs = True
- rt.AlembicExport.VertexColors = True
- rt.AlembicExport.PreserveInstances = True
-
- with maintained_selection():
- # select and export
- node_list = instance.data["members"]
- rt.Select(node_list)
- rt.exportFile(
- filepath,
- rt.name("noPrompt"),
- selectedOnly=True,
- using=rt.AlembicExport,
- )
-
- self.log.info("Performing Extraction ...")
- if "representations" not in instance.data:
- instance.data["representations"] = []
-
- representation = {
- "name": "abc",
- "ext": "abc",
- "files": filename,
- "stagingDir": stagingdir,
- }
- instance.data["representations"].append(representation)
- self.log.info(
- "Extracted instance '%s' to: %s" % (instance.name, filepath)
- )
diff --git a/openpype/hosts/max/plugins/publish/extract_model_fbx.py b/openpype/hosts/max/plugins/publish/extract_model_fbx.py
index fd48ed5007..6c42fd5364 100644
--- a/openpype/hosts/max/plugins/publish/extract_model_fbx.py
+++ b/openpype/hosts/max/plugins/publish/extract_model_fbx.py
@@ -20,12 +20,9 @@ class ExtractModelFbx(publish.Extractor, OptionalPyblishPluginMixin):
if not self.is_active(instance.data):
return
- self.log.debug("Extracting Geometry ...")
-
stagingdir = self.staging_dir(instance)
filename = "{name}.fbx".format(**instance.data)
filepath = os.path.join(stagingdir, filename)
- self.log.info("Writing FBX '%s' to '%s'" % (filepath, stagingdir))
rt.FBXExporterSetParam("Animation", False)
rt.FBXExporterSetParam("Cameras", False)
@@ -46,7 +43,6 @@ class ExtractModelFbx(publish.Extractor, OptionalPyblishPluginMixin):
using=rt.FBXEXP,
)
- self.log.info("Performing Extraction ...")
if "representations" not in instance.data:
instance.data["representations"] = []
diff --git a/openpype/hosts/max/plugins/publish/extract_model_obj.py b/openpype/hosts/max/plugins/publish/extract_model_obj.py
index a5d9ad6597..8464353164 100644
--- a/openpype/hosts/max/plugins/publish/extract_model_obj.py
+++ b/openpype/hosts/max/plugins/publish/extract_model_obj.py
@@ -3,6 +3,7 @@ import pyblish.api
from openpype.pipeline import publish, OptionalPyblishPluginMixin
from pymxs import runtime as rt
from openpype.hosts.max.api import maintained_selection
+from openpype.hosts.max.api.lib import suspended_refresh
from openpype.pipeline.publish import KnownPublishError
@@ -21,25 +22,21 @@ class ExtractModelObj(publish.Extractor, OptionalPyblishPluginMixin):
if not self.is_active(instance.data):
return
- self.log.debug("Extracting Geometry ...")
-
stagingdir = self.staging_dir(instance)
filename = "{name}.obj".format(**instance.data)
filepath = os.path.join(stagingdir, filename)
- self.log.info("Writing OBJ '%s' to '%s'" % (filepath, stagingdir))
-
- self.log.info("Performing Extraction ...")
- with maintained_selection():
- # select and export
- node_list = instance.data["members"]
- rt.Select(node_list)
- rt.exportFile(
- filepath,
- rt.name("noPrompt"),
- selectedOnly=True,
- using=rt.ObjExp,
- )
+ with suspended_refresh():
+ with maintained_selection():
+ # select and export
+ node_list = instance.data["members"]
+ rt.Select(node_list)
+ rt.exportFile(
+ filepath,
+ rt.name("noPrompt"),
+ selectedOnly=True,
+ using=rt.ObjExp,
+ )
if not os.path.exists(filepath):
raise KnownPublishError(
"File {} wasn't produced by 3ds max, please check the logs.")
diff --git a/openpype/hosts/nuke/api/pipeline.py b/openpype/hosts/nuke/api/pipeline.py
index 7bc17ff504..12562a6b6f 100644
--- a/openpype/hosts/nuke/api/pipeline.py
+++ b/openpype/hosts/nuke/api/pipeline.py
@@ -260,7 +260,7 @@ def _install_menu():
"Create...",
lambda: host_tools.show_publisher(
parent=(
- main_window if nuke.NUKE_VERSION_RELEASE >= 14 else None
+ main_window if nuke.NUKE_VERSION_MAJOR >= 14 else None
),
tab="create"
)
@@ -271,7 +271,7 @@ def _install_menu():
"Publish...",
lambda: host_tools.show_publisher(
parent=(
- main_window if nuke.NUKE_VERSION_RELEASE >= 14 else None
+ main_window if nuke.NUKE_VERSION_MAJOR >= 14 else None
),
tab="publish"
)
diff --git a/openpype/hosts/photoshop/plugins/publish/extract_review.py b/openpype/hosts/photoshop/plugins/publish/extract_review.py
index d5dac417d7..09c5d63aa5 100644
--- a/openpype/hosts/photoshop/plugins/publish/extract_review.py
+++ b/openpype/hosts/photoshop/plugins/publish/extract_review.py
@@ -170,8 +170,7 @@ class ExtractReview(publish.Extractor):
# Generate mov.
mov_path = os.path.join(staging_dir, "review.mov")
self.log.info(f"Generate mov review: {mov_path}")
- args = [
- ffmpeg_path,
+ args = ffmpeg_path + [
"-y",
"-i", source_files_pattern,
"-vf", "pad=ceil(iw/2)*2:ceil(ih/2)*2",
@@ -224,6 +223,7 @@ class ExtractReview(publish.Extractor):
"stagingDir": staging_dir,
"tags": ["thumbnail", "delete"]
})
+ instance.data["thumbnailPath"] = thumbnail_path
def _check_and_resize(self, processed_img_names, source_files_pattern,
staging_dir):
diff --git a/openpype/modules/ftrack/lib/custom_attributes.py b/openpype/modules/ftrack/lib/custom_attributes.py
index 3e40bb02f2..76c7bcd403 100644
--- a/openpype/modules/ftrack/lib/custom_attributes.py
+++ b/openpype/modules/ftrack/lib/custom_attributes.py
@@ -66,7 +66,7 @@ def get_openpype_attr(session, split_hierarchical=True, query_keys=None):
"select {}"
" from CustomAttributeConfiguration"
# Kept `pype` for Backwards Compatibility
- " where group.name in (\"pype\", \"{}\")"
+ " where group.name in (\"pype\", \"ayon\", \"{}\")"
).format(", ".join(query_keys), CUST_ATTR_GROUP)
all_avalon_attr = session.query(cust_attrs_query).all()
for cust_attr in all_avalon_attr:
diff --git a/openpype/modules/ftrack/plugins/publish/integrate_hierarchy_ftrack.py b/openpype/modules/ftrack/plugins/publish/integrate_hierarchy_ftrack.py
index a1aa7c0daa..68a31035f6 100644
--- a/openpype/modules/ftrack/plugins/publish/integrate_hierarchy_ftrack.py
+++ b/openpype/modules/ftrack/plugins/publish/integrate_hierarchy_ftrack.py
@@ -21,7 +21,7 @@ def get_pype_attr(session, split_hierarchical=True):
"select id, entity_type, object_type_id, is_hierarchical, default"
" from CustomAttributeConfiguration"
# Kept `pype` for Backwards Compatibility
- " where group.name in (\"pype\", \"{}\")"
+ " where group.name in (\"pype\", \"ayon\", \"{}\")"
).format(CUST_ATTR_GROUP)
all_avalon_attr = session.query(cust_attrs_query).all()
for cust_attr in all_avalon_attr:
diff --git a/openpype/plugins/publish/integrate_thumbnail_ayon.py b/openpype/plugins/publish/integrate_thumbnail_ayon.py
index 1947c9dd4c..e56c567667 100644
--- a/openpype/plugins/publish/integrate_thumbnail_ayon.py
+++ b/openpype/plugins/publish/integrate_thumbnail_ayon.py
@@ -5,7 +5,21 @@
pull into a scene.
This one is used only as image describing content of published item and
- shows up only in Loader in right column section.
+ shows up only in Loader or WebUI.
+
+ Instance must have 'published_representations' to
+ be able to integrate thumbnail.
+ Possible sources of thumbnail paths:
+ - instance.data["thumbnailPath"]
+ - representation with 'thumbnail' name in 'published_representations'
+ - context.data["thumbnailPath"]
+
+ Notes:
+ Issue with 'thumbnail' representation is that we most likely don't
+ want to integrate it as representation. Integrated representation
+ is polluting Loader and database without real usage. That's why
+ they usually have 'delete' tag to skip the integration.
+
"""
import os
@@ -92,11 +106,8 @@ class IntegrateThumbnailsAYON(pyblish.api.ContextPlugin):
continue
# Find thumbnail path on instance
- thumbnail_source = instance.data.get("thumbnailSource")
- thumbnail_path = instance.data.get("thumbnailPath")
thumbnail_path = (
- thumbnail_source
- or thumbnail_path
+ instance.data.get("thumbnailPath")
or self._get_instance_thumbnail_path(published_repres)
)
if thumbnail_path:
diff --git a/openpype/settings/defaults/project_settings/fusion.json b/openpype/settings/defaults/project_settings/fusion.json
index ab24727db5..0edcae060a 100644
--- a/openpype/settings/defaults/project_settings/fusion.json
+++ b/openpype/settings/defaults/project_settings/fusion.json
@@ -25,7 +25,8 @@
"instance_attributes": [
"reviewable",
"farm_rendering"
- ]
+ ],
+ "image_format": "exr"
}
},
"publish": {
diff --git a/openpype/settings/defaults/project_settings/maya.json b/openpype/settings/defaults/project_settings/maya.json
index 7719a5e255..34452eb8ce 100644
--- a/openpype/settings/defaults/project_settings/maya.json
+++ b/openpype/settings/defaults/project_settings/maya.json
@@ -436,7 +436,7 @@
"viewTransform": "sRGB gamma"
}
},
- "mel_workspace": "workspace -fr \"shaders\" \"renderData/shaders\";\nworkspace -fr \"images\" \"renders/maya\";\nworkspace -fr \"particles\" \"particles\";\nworkspace -fr \"mayaAscii\" \"\";\nworkspace -fr \"mayaBinary\" \"\";\nworkspace -fr \"scene\" \"\";\nworkspace -fr \"alembicCache\" \"cache/alembic\";\nworkspace -fr \"renderData\" \"renderData\";\nworkspace -fr \"sourceImages\" \"sourceimages\";\nworkspace -fr \"fileCache\" \"cache/nCache\";\n",
+ "mel_workspace": "workspace -fr \"shaders\" \"renderData/shaders\";\nworkspace -fr \"images\" \"renders/maya\";\nworkspace -fr \"particles\" \"particles\";\nworkspace -fr \"mayaAscii\" \"\";\nworkspace -fr \"mayaBinary\" \"\";\nworkspace -fr \"scene\" \"\";\nworkspace -fr \"alembicCache\" \"cache/alembic\";\nworkspace -fr \"renderData\" \"renderData\";\nworkspace -fr \"sourceImages\" \"sourceimages\";\nworkspace -fr \"fileCache\" \"cache/nCache\";\nworkspace -fr \"autoSave\" \"autosave\";",
"ext_mapping": {
"model": "ma",
"mayaAscii": "ma",
diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_fusion.json b/openpype/settings/entities/schemas/projects_schema/schema_project_fusion.json
index 342411f8a5..5177d8bc7c 100644
--- a/openpype/settings/entities/schemas/projects_schema/schema_project_fusion.json
+++ b/openpype/settings/entities/schemas/projects_schema/schema_project_fusion.json
@@ -80,6 +80,19 @@
"farm_rendering": "Farm rendering"
}
]
+ },
+ {
+ "key": "image_format",
+ "label": "Output Image Format",
+ "type": "enum",
+ "multiselect": false,
+ "enum_items": [
+ {"exr": "exr"},
+ {"tga": "tga"},
+ {"png": "png"},
+ {"tif": "tif"},
+ {"jpg": "jpg"}
+ ]
}
]
}
diff --git a/openpype/vendor/python/common/ayon_api/__init__.py b/openpype/vendor/python/common/ayon_api/__init__.py
index dc3d361f46..cc15ad9170 100644
--- a/openpype/vendor/python/common/ayon_api/__init__.py
+++ b/openpype/vendor/python/common/ayon_api/__init__.py
@@ -75,8 +75,6 @@ from ._api import (
download_installer,
upload_installer,
- get_dependencies_info,
- update_dependency_info,
get_dependency_packages,
create_dependency_package,
update_dependency_package,
@@ -277,8 +275,6 @@ __all__ = (
"download_installer",
"upload_installer",
- "get_dependencies_info",
- "update_dependency_info",
"get_dependency_packages",
"create_dependency_package",
"update_dependency_package",
diff --git a/openpype/vendor/python/common/ayon_api/_api.py b/openpype/vendor/python/common/ayon_api/_api.py
index 9d4fc697ae..a0374a08b9 100644
--- a/openpype/vendor/python/common/ayon_api/_api.py
+++ b/openpype/vendor/python/common/ayon_api/_api.py
@@ -611,16 +611,6 @@ def upload_installer(*args, **kwargs):
# Dependency packages
-def get_dependencies_info(*args, **kwargs):
- con = get_server_api_connection()
- return con.get_dependencies_info(*args, **kwargs)
-
-
-def update_dependency_info(*args, **kwargs):
- con = get_server_api_connection()
- return con.update_dependency_info(*args, **kwargs)
-
-
def download_dependency_package(*args, **kwargs):
con = get_server_api_connection()
return con.download_dependency_package(*args, **kwargs)
diff --git a/openpype/vendor/python/common/ayon_api/entity_hub.py b/openpype/vendor/python/common/ayon_api/entity_hub.py
index 61d740fe57..f894c428a8 100644
--- a/openpype/vendor/python/common/ayon_api/entity_hub.py
+++ b/openpype/vendor/python/common/ayon_api/entity_hub.py
@@ -7,9 +7,21 @@ import six
from ._api import get_server_api_connection
from .utils import create_entity_id, convert_entity_id, slugify_string
-UNKNOWN_VALUE = object()
-PROJECT_PARENT_ID = object()
-_NOT_SET = object()
+
+class _CustomNone(object):
+ def __init__(self, name=None):
+ self._name = name or "CustomNone"
+
+ def __repr__(self):
+ return "<{}>".format(self._name)
+
+ def __bool__(self):
+ return False
+
+
+UNKNOWN_VALUE = _CustomNone("UNKNOWN_VALUE")
+PROJECT_PARENT_ID = _CustomNone("PROJECT_PARENT_ID")
+_NOT_SET = _CustomNone("_NOT_SET")
class EntityHub(object):
@@ -1284,7 +1296,10 @@ class BaseEntity(object):
changes["name"] = self._name
if self._entity_hub.allow_data_changes:
- if self._orig_data != self._data:
+ if (
+ self._data is not UNKNOWN_VALUE
+ and self._orig_data != self._data
+ ):
changes["data"] = self._data
if self._orig_thumbnail_id != self._thumbnail_id:
diff --git a/openpype/vendor/python/common/ayon_api/server_api.py b/openpype/vendor/python/common/ayon_api/server_api.py
index e4e7146279..4aed4e811a 100644
--- a/openpype/vendor/python/common/ayon_api/server_api.py
+++ b/openpype/vendor/python/common/ayon_api/server_api.py
@@ -8,6 +8,7 @@ import collections
import platform
import copy
import uuid
+import warnings
from contextlib import contextmanager
import six
@@ -1022,17 +1023,10 @@ class ServerAPI(object):
for attr, filter_value in filters.items():
query.set_variable_value(attr, filter_value)
- # Backwards compatibility for server 0.3.x
- # - will be removed in future releases
- major, minor, _, _, _ = self.server_version_tuple
- access_groups_field = "accessGroups"
- if major == 0 and minor <= 3:
- access_groups_field = "roles"
-
for parsed_data in query.continuous_query(self):
for user in parsed_data["users"]:
- user[access_groups_field] = json.loads(
- user[access_groups_field])
+ user["accessGroups"] = json.loads(
+ user["accessGroups"])
yield user
def get_user(self, username=None):
@@ -2044,14 +2038,6 @@ class ServerAPI(object):
elif entity_type == "user":
entity_type_defaults = set(DEFAULT_USER_FIELDS)
- # Backwards compatibility for server 0.3.x
- # - will be removed in future releases
- major, minor, _, _, _ = self.server_version_tuple
- if major == 0 and minor <= 3:
- entity_type_defaults.discard("accessGroups")
- entity_type_defaults.discard("defaultAccessGroups")
- entity_type_defaults.add("roles")
- entity_type_defaults.add("defaultRoles")
else:
raise ValueError("Unknown entity type \"{}\"".format(entity_type))
@@ -2306,125 +2292,8 @@ class ServerAPI(object):
progress=progress
)
- def get_dependencies_info(self):
- """Information about dependency packages on server.
-
- Example data structure:
- {
- "packages": [
- {
- "name": str,
- "platform": str,
- "checksum": str,
- "sources": list[dict[str, Any]],
- "supportedAddons": dict[str, str],
- "pythonModules": dict[str, str]
- }
- ],
- "productionPackage": str
- }
-
- Deprecated:
- Deprecated since server version 0.2.1. Use
- 'get_dependency_packages' instead.
-
- Returns:
- dict[str, Any]: Information about dependency packages known for
- server.
- """
-
- major, minor, patch, _, _ = self.server_version_tuple
- if major == 0 and (minor < 2 or (minor == 2 and patch < 1)):
- result = self.get("dependencies")
- return result.data
- packages = self.get_dependency_packages()
- packages["productionPackage"] = None
- return packages
-
- def update_dependency_info(
- self,
- name,
- platform_name,
- size,
- checksum,
- checksum_algorithm=None,
- supported_addons=None,
- python_modules=None,
- sources=None
- ):
- """Update or create dependency package for identifiers.
-
- The endpoint can be used to create or update dependency package.
-
-
- Deprecated:
- Deprecated for server version 0.2.1. Use
- 'create_dependency_pacakge' instead.
-
- Args:
- name (str): Name of dependency package.
- platform_name (Literal["windows", "linux", "darwin"]): Platform
- for which is dependency package targeted.
- size (int): Size of dependency package in bytes.
- checksum (str): Checksum of archive file where dependencies are.
- checksum_algorithm (Optional[str]): Algorithm used to calculate
- checksum. By default, is used 'md5' (defined by server).
- supported_addons (Optional[dict[str, str]]): Name of addons for
- which was the package created.
- '{"": "", ...}'
- python_modules (Optional[dict[str, str]]): Python modules in
- dependencies package.
- '{"": "", ...}'
- sources (Optional[list[dict[str, Any]]]): Information about
- sources where dependency package is available.
- """
-
- kwargs = {
- key: value
- for key, value in (
- ("checksumAlgorithm", checksum_algorithm),
- ("supportedAddons", supported_addons),
- ("pythonModules", python_modules),
- ("sources", sources),
- )
- if value
- }
-
- response = self.put(
- "dependencies",
- name=name,
- platform=platform_name,
- size=size,
- checksum=checksum,
- **kwargs
- )
- response.raise_for_status("Failed to create/update dependency")
- return response.data
-
- def _get_dependency_package_route(
- self, filename=None, platform_name=None
- ):
- major, minor, patch, _, _ = self.server_version_tuple
- if (major, minor, patch) <= (0, 2, 0):
- # Backwards compatibility for AYON server 0.2.0 and lower
- self.log.warning((
- "Using deprecated dependency package route."
- " Please update your AYON server to version 0.2.1 or higher."
- " Backwards compatibility for this route will be removed"
- " in future releases of ayon-python-api."
- ))
- if platform_name is None:
- platform_name = platform.system().lower()
- base = "dependencies"
- if not filename:
- return base
- return "{}/{}/{}".format(base, filename, platform_name)
-
- if (major, minor) <= (0, 3):
- endpoint = "desktop/dependency_packages"
- else:
- endpoint = "desktop/dependencyPackages"
-
+ def _get_dependency_package_route(self, filename=None):
+ endpoint = "desktop/dependencyPackages"
if filename:
return "{}/{}".format(endpoint, filename)
return endpoint
@@ -2535,14 +2404,21 @@ class ServerAPI(object):
"""Remove dependency package for specific platform.
Args:
- filename (str): Filename of dependency package. Or name of package
- for server version 0.2.0 or lower.
- platform_name (Optional[str]): Which platform of the package
- should be removed. Current platform is used if not passed.
- Deprecated since version 0.2.1
+ filename (str): Filename of dependency package.
+ platform_name (Optional[str]): Deprecated.
"""
- route = self._get_dependency_package_route(filename, platform_name)
+ if platform_name is not None:
+ warnings.warn(
+ (
+ "Argument 'platform_name' is deprecated in"
+ " 'delete_dependency_package'. The argument will be"
+ " removed, please modify your code accordingly."
+ ),
+ DeprecationWarning
+ )
+
+ route = self._get_dependency_package_route(filename)
response = self.delete(route)
response.raise_for_status("Failed to delete dependency file")
return response.data
@@ -2567,18 +2443,25 @@ class ServerAPI(object):
to download.
dst_directory (str): Where the file should be downloaded.
dst_filename (str): Name of destination filename.
- platform_name (Optional[str]): Name of platform for which the
- dependency package is targeted. Default value is
- current platform. Deprecated since server version 0.2.1.
+ platform_name (Optional[str]): Deprecated.
chunk_size (Optional[int]): Download chunk size.
progress (Optional[TransferProgress]): Object that gives ability
to track download progress.
Returns:
str: Filepath to downloaded file.
- """
+ """
- route = self._get_dependency_package_route(src_filename, platform_name)
+ if platform_name is not None:
+ warnings.warn(
+ (
+ "Argument 'platform_name' is deprecated in"
+ " 'download_dependency_package'. The argument will be"
+ " removed, please modify your code accordingly."
+ ),
+ DeprecationWarning
+ )
+ route = self._get_dependency_package_route(src_filename)
package_filepath = os.path.join(dst_directory, dst_filename)
self.download_file(
route,
@@ -2597,32 +2480,24 @@ class ServerAPI(object):
src_filepath (str): Path to a package file.
dst_filename (str): Dependency package filename or name of package
for server version 0.2.0 or lower. Must be unique.
- platform_name (Optional[str]): For which platform is the
- package targeted. Deprecated since server version 0.2.1.
+ platform_name (Optional[str]): Deprecated.
progress (Optional[TransferProgress]): Object to keep track about
upload state.
"""
- route = self._get_dependency_package_route(dst_filename, platform_name)
+ if platform_name is not None:
+ warnings.warn(
+ (
+ "Argument 'platform_name' is deprecated in"
+ " 'upload_dependency_package'. The argument will be"
+ " removed, please modify your code accordingly."
+ ),
+ DeprecationWarning
+ )
+
+ route = self._get_dependency_package_route(dst_filename)
self.upload_file(route, src_filepath, progress=progress)
- def create_dependency_package_basename(self, platform_name=None):
- """Create basename for dependency package file.
-
- Deprecated:
- Use 'create_dependency_package_basename' from `ayon_api` or
- `ayon_api.utils` instead.
-
- Args:
- platform_name (Optional[str]): Name of platform for which the
- bundle is targeted. Default value is current platform.
-
- Returns:
- str: Dependency package name with timestamp and platform.
- """
-
- return create_dependency_package_basename(platform_name)
-
def upload_addon_zip(self, src_filepath, progress=None):
"""Upload addon zip file to server.
@@ -2650,14 +2525,6 @@ class ServerAPI(object):
)
return response.json()
- def _get_bundles_route(self):
- major, minor, patch, _, _ = self.server_version_tuple
- # Backwards compatibility for AYON server 0.3.0
- # - first version where bundles were available
- if major == 0 and minor == 3 and patch == 0:
- return "desktop/bundles"
- return "bundles"
-
def get_bundles(self):
"""Server bundles with basic information.
@@ -2688,7 +2555,7 @@ class ServerAPI(object):
dict[str, Any]: Server bundles with basic information.
"""
- response = self.get(self._get_bundles_route())
+ response = self.get("bundles")
response.raise_for_status()
return response.data
@@ -2731,7 +2598,7 @@ class ServerAPI(object):
if value is not None:
body[key] = value
- response = self.post(self._get_bundles_route(), **body)
+ response = self.post("bundles", **body)
response.raise_for_status()
def update_bundle(
@@ -2766,7 +2633,7 @@ class ServerAPI(object):
if value is not None
}
response = self.patch(
- "{}/{}".format(self._get_bundles_route(), bundle_name),
+ "{}/{}".format("bundles", bundle_name),
**body
)
response.raise_for_status()
@@ -2779,7 +2646,7 @@ class ServerAPI(object):
"""
response = self.delete(
- "{}/{}".format(self._get_bundles_route(), bundle_name)
+ "{}/{}".format("bundles", bundle_name)
)
response.raise_for_status()
@@ -3102,16 +2969,13 @@ class ServerAPI(object):
- test how it behaves if there is not any production/staging
bundle.
- Warnings:
- For AYON server < 0.3.0 bundle name will be ignored.
-
Example output:
{
"addons": [
{
"name": "addon-name",
"version": "addon-version",
- "settings": {...}
+ "settings": {...},
"siteSettings": {...}
}
]
@@ -3121,7 +2985,6 @@ class ServerAPI(object):
dict[str, Any]: All settings for single bundle.
"""
- major, minor, _, _, _ = self.server_version_tuple
query_values = {
key: value
for key, value in (
@@ -3137,21 +3000,8 @@ class ServerAPI(object):
if site_id:
query_values["site_id"] = site_id
- if major == 0 and minor >= 3:
- url = "settings"
- else:
- # Backward compatibility for AYON server < 0.3.0
- url = "settings/addons"
- query_values.pop("bundle_name", None)
- for new_key, old_key in (
- ("project_name", "project"),
- ("site_id", "site"),
- ):
- if new_key in query_values:
- query_values[old_key] = query_values.pop(new_key)
-
query = prepare_query_string(query_values)
- response = self.get("{}{}".format(url, query))
+ response = self.get("settings{}".format(query))
response.raise_for_status()
return response.data
@@ -3194,15 +3044,10 @@ class ServerAPI(object):
use_site=use_site
)
if only_values:
- major, minor, patch, _, _ = self.server_version_tuple
- if major == 0 and minor >= 3:
- output = {
- addon["name"]: addon["settings"]
- for addon in output["addons"]
- }
- else:
- # Backward compatibility for AYON server < 0.3.0
- output = output["settings"]
+ output = {
+ addon["name"]: addon["settings"]
+ for addon in output["addons"]
+ }
return output
def get_addons_project_settings(
@@ -3263,15 +3108,10 @@ class ServerAPI(object):
use_site=use_site
)
if only_values:
- major, minor, patch, _, _ = self.server_version_tuple
- if major == 0 and minor >= 3:
- output = {
- addon["name"]: addon["settings"]
- for addon in output["addons"]
- }
- else:
- # Backward compatibility for AYON server < 0.3.0
- output = output["settings"]
+ output = {
+ addon["name"]: addon["settings"]
+ for addon in output["addons"]
+ }
return output
def get_addons_settings(
diff --git a/openpype/vendor/python/common/ayon_api/version.py b/openpype/vendor/python/common/ayon_api/version.py
index bc1107da1e..ce0173a248 100644
--- a/openpype/vendor/python/common/ayon_api/version.py
+++ b/openpype/vendor/python/common/ayon_api/version.py
@@ -1,2 +1,2 @@
"""Package declaring Python API for Ayon server."""
-__version__ = "1.0.0-rc.1"
+__version__ = "1.0.0-rc.3"
diff --git a/openpype/version.py b/openpype/version.py
index 44cae8e131..e053a8364e 100644
--- a/openpype/version.py
+++ b/openpype/version.py
@@ -1,3 +1,3 @@
# -*- coding: utf-8 -*-
"""Package declaring Pype version."""
-__version__ = "3.18.1-nightly.1"
+__version__ = "3.18.2-nightly.1"
diff --git a/pyproject.toml b/pyproject.toml
index 040da82aa3..e64018498f 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "OpenPype"
-version = "3.18.0" # OpenPype
+version = "3.18.1" # OpenPype
description = "Open VFX and Animation pipeline with support."
authors = ["OpenPype Team "]
license = "MIT License"
diff --git a/server_addon/fusion/server/settings.py b/server_addon/fusion/server/settings.py
index 92fb362c66..1bc12773d2 100644
--- a/server_addon/fusion/server/settings.py
+++ b/server_addon/fusion/server/settings.py
@@ -25,6 +25,16 @@ def _create_saver_instance_attributes_enum():
]
+def _image_format_enum():
+ return [
+ {"value": "exr", "label": "exr"},
+ {"value": "tga", "label": "tga"},
+ {"value": "png", "label": "png"},
+ {"value": "tif", "label": "tif"},
+ {"value": "jpg", "label": "jpg"},
+ ]
+
+
class CreateSaverPluginModel(BaseSettingsModel):
_isGroup = True
temp_rendering_path_template: str = Field(
@@ -39,6 +49,10 @@ class CreateSaverPluginModel(BaseSettingsModel):
enum_resolver=_create_saver_instance_attributes_enum,
title="Instance attributes"
)
+ image_format: str = Field(
+ enum_resolver=_image_format_enum,
+ title="Output Image Format"
+ )
class CreatPluginsModel(BaseSettingsModel):
@@ -89,7 +103,8 @@ DEFAULT_VALUES = {
"instance_attributes": [
"reviewable",
"farm_rendering"
- ]
+ ],
+ "image_format": "exr"
}
}
}
diff --git a/server_addon/fusion/server/version.py b/server_addon/fusion/server/version.py
index 3dc1f76bc6..485f44ac21 100644
--- a/server_addon/fusion/server/version.py
+++ b/server_addon/fusion/server/version.py
@@ -1 +1 @@
-__version__ = "0.1.0"
+__version__ = "0.1.1"
diff --git a/server_addon/maya/server/settings/main.py b/server_addon/maya/server/settings/main.py
index 62fd12ec8a..a5b573a75e 100644
--- a/server_addon/maya/server/settings/main.py
+++ b/server_addon/maya/server/settings/main.py
@@ -97,7 +97,7 @@ DEFAULT_MEL_WORKSPACE_SETTINGS = "\n".join((
'workspace -fr "renderData" "renderData";',
'workspace -fr "sourceImages" "sourceimages";',
'workspace -fr "fileCache" "cache/nCache";',
- 'workspace -fr "autoSave" "autosave"',
+ 'workspace -fr "autoSave" "autosave";',
'',
))
diff --git a/server_addon/photoshop/server/settings/publish_plugins.py b/server_addon/photoshop/server/settings/publish_plugins.py
index 2863979ca9..21e7d670f0 100644
--- a/server_addon/photoshop/server/settings/publish_plugins.py
+++ b/server_addon/photoshop/server/settings/publish_plugins.py
@@ -29,7 +29,7 @@ class ColorCodeMappings(BaseSettingsModel):
)
layer_name_regex: list[str] = Field(
- "",
+ default_factory=list,
title="Layer name regex"
)
diff --git a/server_addon/photoshop/server/version.py b/server_addon/photoshop/server/version.py
index d4b9e2d7f3..a242f0e757 100644
--- a/server_addon/photoshop/server/version.py
+++ b/server_addon/photoshop/server/version.py
@@ -1,3 +1,3 @@
# -*- coding: utf-8 -*-
"""Package declaring addon version."""
-__version__ = "0.1.0"
+__version__ = "0.1.1"