Merge remote-tracking branch 'origin/develop' into feature/houdini-cb-update

This commit is contained in:
Ondřej Samohel 2021-07-15 17:43:51 +02:00
commit c47e8808c8
No known key found for this signature in database
GPG key ID: 02376E18990A97C6
16 changed files with 360 additions and 66 deletions

View file

@ -1,8 +1,20 @@
# Changelog
## [3.2.0-nightly.7](https://github.com/pypeclub/OpenPype/tree/HEAD)
## [3.3.0-nightly.1](https://github.com/pypeclub/OpenPype/tree/HEAD)
[Full Changelog](https://github.com/pypeclub/OpenPype/compare/2.18.4...HEAD)
[Full Changelog](https://github.com/pypeclub/OpenPype/compare/3.2.0...HEAD)
**🐛 Bug fixes**
- Houdini colector formatting keys fix [\#1802](https://github.com/pypeclub/OpenPype/pull/1802)
**Merged pull requests:**
- Maya: Deadline custom settings [\#1797](https://github.com/pypeclub/OpenPype/pull/1797)
## [3.2.0](https://github.com/pypeclub/OpenPype/tree/3.2.0) (2021-07-13)
[Full Changelog](https://github.com/pypeclub/OpenPype/compare/CI/3.2.0-nightly.7...3.2.0)
**🚀 Enhancements**
@ -11,6 +23,7 @@
- Ftrack Multiple notes as server action [\#1795](https://github.com/pypeclub/OpenPype/pull/1795)
- Settings conditional dict [\#1777](https://github.com/pypeclub/OpenPype/pull/1777)
- Settings application use python 2 only where needed [\#1776](https://github.com/pypeclub/OpenPype/pull/1776)
- Settings UI copy/paste [\#1769](https://github.com/pypeclub/OpenPype/pull/1769)
- Workfile tool widths [\#1766](https://github.com/pypeclub/OpenPype/pull/1766)
- Push hierarchical attributes care about task parent changes [\#1763](https://github.com/pypeclub/OpenPype/pull/1763)
- Application executables with environment variables [\#1757](https://github.com/pypeclub/OpenPype/pull/1757)
@ -69,7 +82,7 @@
- Tools names forwards compatibility [\#1727](https://github.com/pypeclub/OpenPype/pull/1727)
**Merged pull requests:**
**⚠️ Deprecations**
- global: removing obsolete ftrack validator plugin [\#1710](https://github.com/pypeclub/OpenPype/pull/1710)
@ -105,7 +118,6 @@
- Bad zip can break OpenPype start [\#1691](https://github.com/pypeclub/OpenPype/pull/1691)
- Hiero: published whole edit mov [\#1687](https://github.com/pypeclub/OpenPype/pull/1687)
- Ftrack subprocess handle of stdout/stderr [\#1675](https://github.com/pypeclub/OpenPype/pull/1675)
- Settings list race condifiton and mutable dict list conversion [\#1671](https://github.com/pypeclub/OpenPype/pull/1671)
**Merged pull requests:**

View file

@ -133,10 +133,10 @@ class ExtractYetiRig(openpype.api.Extractor):
image_search_path = resources_dir = instance.data["resourcesDir"]
settings = instance.data.get("rigsettings", None)
if settings:
settings["imageSearchPath"] = image_search_path
with open(settings_path, "w") as fp:
json.dump(settings, fp, ensure_ascii=False)
assert settings, "Yeti rig settings were not collected."
settings["imageSearchPath"] = image_search_path
with open(settings_path, "w") as fp:
json.dump(settings, fp, ensure_ascii=False)
# add textures to transfers
if 'transfers' not in instance.data:
@ -192,12 +192,12 @@ class ExtractYetiRig(openpype.api.Extractor):
'stagingDir': dirname
}
)
self.log.info("settings file: {}".format(settings))
self.log.info("settings file: {}".format(settings_path))
instance.data["representations"].append(
{
'name': 'rigsettings',
'ext': 'rigsettings',
'files': os.path.basename(settings),
'files': os.path.basename(settings_path),
'stagingDir': dirname
}
)

View file

@ -2,7 +2,7 @@
Optional:
presets -> extensions (
example of use:
[".mov", ".mp4"]
["mov", "mp4"]
)
presets -> source_dir (
example of use:
@ -11,6 +11,7 @@ Optional:
"{root[work]}/{project[name]}/inputs"
"./input"
"../input"
""
)
"""
@ -48,7 +49,7 @@ class CollectEditorial(pyblish.api.InstancePlugin):
actions = []
# presets
extensions = [".mov", ".mp4"]
extensions = ["mov", "mp4"]
source_dir = None
def process(self, instance):
@ -72,7 +73,7 @@ class CollectEditorial(pyblish.api.InstancePlugin):
video_path = None
basename = os.path.splitext(os.path.basename(file_path))[0]
if self.source_dir:
if self.source_dir != "":
source_dir = self.source_dir.replace("\\", "/")
if ("./" in source_dir) or ("../" in source_dir):
# get current working dir
@ -98,7 +99,7 @@ class CollectEditorial(pyblish.api.InstancePlugin):
if os.path.splitext(f)[0] not in basename:
continue
# filter out by respected extensions
if os.path.splitext(f)[1] not in self.extensions:
if os.path.splitext(f)[1][1:] not in self.extensions:
continue
video_path = os.path.join(
staging_dir, f

View file

@ -17,16 +17,12 @@ class CollectInstances(pyblish.api.InstancePlugin):
"referenceMain": {
"family": "review",
"families": ["clip"],
"extensions": [".mp4"]
"extensions": ["mp4"]
},
"audioMain": {
"family": "audio",
"families": ["clip"],
"extensions": [".wav"],
},
"shotMain": {
"family": "shot",
"families": []
"extensions": ["wav"],
}
}
timeline_frame_start = 900000 # starndard edl default (10:00:00:00)
@ -178,7 +174,16 @@ class CollectInstances(pyblish.api.InstancePlugin):
data_key: instance.data.get(data_key)})
# adding subsets to context as instances
self.subsets.update({
"shotMain": {
"family": "shot",
"families": []
}
})
for subset, properities in self.subsets.items():
if properities["version"] == 0:
properities.pop("version")
# adding Review-able instance
subset_instance_data = instance_data.copy()
subset_instance_data.update(properities)

View file

@ -177,19 +177,23 @@ class CollectInstanceResources(pyblish.api.InstancePlugin):
collection_head_name = None
# loop trough collections and create representations
for _collection in collections:
ext = _collection.tail
ext = _collection.tail[1:]
collection_head_name = _collection.head
frame_start = list(_collection.indexes)[0]
frame_end = list(_collection.indexes)[-1]
repre_data = {
"frameStart": frame_start,
"frameEnd": frame_end,
"name": ext[1:],
"ext": ext[1:],
"name": ext,
"ext": ext,
"files": [item for item in _collection],
"stagingDir": staging_dir
}
if instance_data.get("keepSequence"):
repre_data_keep = deepcopy(repre_data)
instance_data["representations"].append(repre_data_keep)
if "review" in instance_data["families"]:
repre_data.update({
"thumbnail": True,
@ -208,20 +212,20 @@ class CollectInstanceResources(pyblish.api.InstancePlugin):
# loop trough reminders and create representations
for _reminding_file in remainder:
ext = os.path.splitext(_reminding_file)[-1]
ext = os.path.splitext(_reminding_file)[-1][1:]
if ext not in instance_data["extensions"]:
continue
if collection_head_name and (
(collection_head_name + ext[1:]) not in _reminding_file
) and (ext in [".mp4", ".mov"]):
(collection_head_name + ext) not in _reminding_file
) and (ext in ["mp4", "mov"]):
self.log.info(f"Skipping file: {_reminding_file}")
continue
frame_start = 1
frame_end = 1
repre_data = {
"name": ext[1:],
"ext": ext[1:],
"name": ext,
"ext": ext,
"files": _reminding_file,
"stagingDir": staging_dir
}

View file

@ -131,20 +131,21 @@ class CollectHierarchyInstance(pyblish.api.ContextPlugin):
tasks_to_add = dict()
project_tasks = io.find_one({"type": "project"})["config"]["tasks"]
for task_name, task_data in self.shot_add_tasks.items():
try:
if task_data["type"] in project_tasks.keys():
tasks_to_add.update({task_name: task_data})
else:
raise KeyError(
"Wrong FtrackTaskType `{}` for `{}` is not"
" existing in `{}``".format(
task_data["type"],
task_name,
list(project_tasks.keys())))
except KeyError as error:
_task_data = deepcopy(task_data)
# fixing enumerator from settings
_task_data["type"] = task_data["type"][0]
# check if task type in project task types
if _task_data["type"] in project_tasks.keys():
tasks_to_add.update({task_name: _task_data})
else:
raise KeyError(
"Wrong presets: `{0}`".format(error)
)
"Wrong FtrackTaskType `{}` for `{}` is not"
" existing in `{}``".format(
_task_data["type"],
task_name,
list(project_tasks.keys())))
instance.data["tasks"] = tasks_to_add
else:

View file

@ -1,5 +1,6 @@
import os
import re
import json
from openpype.modules.ftrack.lib import BaseAction, statics_icon
from openpype.api import Anatomy, get_project_settings
@ -84,6 +85,9 @@ class CreateProjectFolders(BaseAction):
}
try:
if isinstance(project_folder_structure, str):
project_folder_structure = json.loads(project_folder_structure)
# Get paths based on presets
basic_paths = self.get_path_items(project_folder_structure)
self.create_folders(basic_paths, project_entity)

View file

@ -172,6 +172,8 @@
"deadline_group": "",
"deadline_chunk_size": 1,
"deadline_priority": 50,
"publishing_script": "",
"skip_integration_repre_list": [],
"aov_filter": {
"maya": [
".+(?:\\.|_)([Bb]eauty)(?:\\.|_).*"
@ -184,6 +186,10 @@
".*"
]
}
},
"CleanUp": {
"paterns": [],
"remove_temp_renders": false
}
},
"tools": {
@ -271,28 +277,7 @@
}
}
},
"project_folder_structure": {
"__project_root__": {
"prod": {},
"resources": {
"footage": {
"plates": {},
"offline": {}
},
"audio": {},
"art_dept": {}
},
"editorial": {},
"assets[ftrack.Library]": {
"characters[ftrack]": {},
"locations[ftrack]": {}
},
"shots[ftrack.Sequence]": {
"scripts": {},
"editorial[ftrack.Folder]": {}
}
}
},
"project_folder_structure": "{\"__project_root__\": {\"prod\": {}, \"resources\": {\"footage\": {\"plates\": {}, \"offline\": {}}, \"audio\": {}, \"art_dept\": {}}, \"editorial\": {}, \"assets[ftrack.Library]\": {\"characters[ftrack]\": {}, \"locations[ftrack]\": {}}, \"shots[ftrack.Sequence]\": {\"scripts\": {}, \"editorial[ftrack.Folder]\": {}}}}",
"sync_server": {
"enabled": true,
"config": {

View file

@ -165,6 +165,58 @@
],
"output": []
}
},
"CollectEditorial": {
"source_dir": "",
"extensions": [
"mov",
"mp4"
]
},
"CollectHierarchyInstance": {
"shot_rename_template": "{project[code]}_{_sequence_}_{_shot_}",
"shot_rename_search_patterns": {
"_sequence_": "(\\d{4})(?=_\\d{4})",
"_shot_": "(\\d{4})(?!_\\d{4})"
},
"shot_add_hierarchy": {
"parents_path": "{project}/{folder}/{sequence}",
"parents": {
"project": "{project[name]}",
"sequence": "{_sequence_}",
"folder": "shots"
}
},
"shot_add_tasks": {}
},
"shot_add_tasks": {
"custom_start_frame": 0,
"timeline_frame_start": 900000,
"timeline_frame_offset": 0,
"subsets": {
"referenceMain": {
"family": "review",
"families": [
"clip"
],
"extensions": [
"mp4"
],
"version": 0,
"keepSequence": false
},
"audioMain": {
"family": "audio",
"families": [
"clip"
],
"extensions": [
"wav"
],
"version": 0,
"keepSequence": false
}
}
}
}
}

View file

@ -139,7 +139,8 @@ class HostsEnumEntity(BaseEnumEntity):
"photoshop",
"resolve",
"tvpaint",
"unreal"
"unreal",
"standalonepublisher"
]
if self.use_empty_value:
host_names.insert(0, "")

View file

@ -1,5 +1,6 @@
import re
import copy
import json
from abc import abstractmethod
from .base_entity import ItemEntity
@ -440,6 +441,7 @@ class RawJsonEntity(InputEntity):
def _item_initalization(self):
# Schema must define if valid value is dict or list
store_as_string = self.schema_data.get("store_as_string", False)
is_list = self.schema_data.get("is_list", False)
if is_list:
valid_value_types = (list, )
@ -448,6 +450,8 @@ class RawJsonEntity(InputEntity):
valid_value_types = (dict, )
value_on_not_set = {}
self.store_as_string = store_as_string
self._is_list = is_list
self.valid_value_types = valid_value_types
self.value_on_not_set = value_on_not_set
@ -491,6 +495,23 @@ class RawJsonEntity(InputEntity):
result = self.metadata != self._metadata_for_current_state()
return result
def schema_validations(self):
if self.store_as_string and self.is_env_group:
reason = (
"RawJson entity can't store environment group metadata"
" as string."
)
raise EntitySchemaError(self, reason)
super(RawJsonEntity, self).schema_validations()
def _convert_to_valid_type(self, value):
if isinstance(value, STRING_TYPE):
try:
return json.loads(value)
except Exception:
pass
return super(RawJsonEntity, self)._convert_to_valid_type(value)
def _metadata_for_current_state(self):
if (
self._override_state is OverrideState.PROJECT
@ -510,6 +531,9 @@ class RawJsonEntity(InputEntity):
value = super(RawJsonEntity, self)._settings_value()
if self.is_env_group and isinstance(value, dict):
value.update(self.metadata)
if self.store_as_string:
return json.dumps(value)
return value
def _prepare_value(self, value):

View file

@ -337,6 +337,11 @@ How output of the schema could look like on save:
- schema also defines valid value type
- by default it is dictionary
- to be able use list it is required to define `is_list` to `true`
- output can be stored as string
- this is to allow any keys in dictionary
- set key `store_as_string` to `true`
- code using that setting must expected that value is string and use json module to convert it to python types
```
{
"type": "raw-json",

View file

@ -17,7 +17,8 @@
"type": "raw-json",
"label": "Project Folder Structure",
"key": "project_folder_structure",
"use_label_wrap": true
"use_label_wrap": true,
"store_as_string": true
},
{
"type": "schema",

View file

@ -130,6 +130,165 @@
]
}
]
},
{
"type": "dict",
"collapsible": true,
"key": "CollectEditorial",
"label": "Collect Editorial",
"is_group": true,
"children": [
{
"type": "text",
"key": "source_dir",
"label": "Editorial resources pointer"
},
{
"type": "list",
"key": "extensions",
"label": "Accepted extensions",
"object_type": "text"
}
]
},
{
"type": "dict",
"collapsible": true,
"key": "CollectHierarchyInstance",
"label": "Collect Instance Hierarchy",
"is_group": true,
"children": [
{
"type": "text",
"key": "shot_rename_template",
"label": "Shot rename template"
},
{
"key": "shot_rename_search_patterns",
"label": "Shot renaming paterns search",
"type": "dict-modifiable",
"highlight_content": true,
"object_type": {
"type": "text"
}
},
{
"type": "dict",
"key": "shot_add_hierarchy",
"label": "Shot hierarchy",
"children": [
{
"type": "text",
"key": "parents_path",
"label": "Parents path template"
},
{
"key": "parents",
"label": "Parents",
"type": "dict-modifiable",
"highlight_content": true,
"object_type": {
"type": "text"
}
}
]
},
{
"key": "shot_add_tasks",
"label": "Add tasks to shot",
"type": "dict-modifiable",
"highlight_content": true,
"object_type": {
"type": "dict",
"children": [
{
"type": "task-types-enum",
"key": "type",
"label": "Task type"
}
]
}
}
]
},
{
"type": "dict",
"collapsible": true,
"key": "shot_add_tasks",
"label": "Collect Clip Instances",
"is_group": true,
"children": [
{
"type": "number",
"key": "custom_start_frame",
"label": "Custom start frame",
"default": 0,
"minimum": 1,
"maximum": 100000
},
{
"type": "number",
"key": "timeline_frame_start",
"label": "Timeline start frame",
"default": 900000,
"minimum": 1,
"maximum": 10000000
},
{
"type": "number",
"key": "timeline_frame_offset",
"label": "Timeline frame offset",
"default": 0,
"minimum": -1000000,
"maximum": 1000000
},
{
"key": "subsets",
"label": "Subsets",
"type": "dict-modifiable",
"highlight_content": true,
"object_type": {
"type": "dict",
"children": [
{
"type": "text",
"key": "family",
"label": "Family"
},
{
"type": "list",
"key": "families",
"label": "Families",
"object_type": "text"
},
{
"type": "splitter"
},
{
"type": "list",
"key": "extensions",
"label": "Extensions",
"object_type": "text"
},
{
"key": "version",
"label": "Version lock",
"type": "number",
"default": 0,
"minimum": 0,
"maximum": 10
}
,
{
"type": "boolean",
"key": "keepSequence",
"label": "Keep sequence if used for review",
"default": false
}
]
}
}
]
}
]
}

View file

@ -554,6 +554,22 @@
"key": "deadline_priority",
"label": "Deadline Priotity"
},
{
"type": "splitter"
},
{
"type": "text",
"key": "publishing_script",
"label": "Publishing script path"
},
{
"type": "list",
"key": "skip_integration_repre_list",
"label": "Skip integration of representation with ext",
"object_type": {
"type": "text"
}
},
{
"type": "dict",
"key": "aov_filter",
@ -594,6 +610,30 @@
]
}
]
},
{
"type": "dict",
"collapsible": true,
"key": "CleanUp",
"label": "Clean Up",
"is_group": true,
"children": [
{
"type": "list",
"key": "paterns",
"label": "Paterrns (regex)",
"object_type": {
"type": "text"
}
},
{
"type": "boolean",
"key": "remove_temp_renders",
"label": "Remove Temp renders",
"default": false
}
]
}
]
}

View file

@ -1,3 +1,3 @@
# -*- coding: utf-8 -*-
"""Package declaring Pype version."""
__version__ = "3.2.0-nightly.7"
__version__ = "3.3.0-nightly.1"