.*)_(.*)_SHD", title="Validation regex")
+
+
+class ValidateAttributesModel(BaseSettingsModel):
+ enabled: bool = Field(title="ValidateAttributes")
+ attributes: str = Field(
+ "{}", title="Attributes", widget="textarea")
+
+ @validator("attributes")
+ def validate_json(cls, value):
+ if not value.strip():
+ return "{}"
+ try:
+ converted_value = json.loads(value)
+ success = isinstance(converted_value, dict)
+ except json.JSONDecodeError:
+ success = False
+
+ if not success:
+ raise BadRequestException(
+ "The attibutes can't be parsed as json object"
+ )
+ return value
+
+
+class ValidateLoadedPluginModel(BaseSettingsModel):
+ enabled: bool = Field(title="ValidateLoadedPlugin")
+ optional: bool = Field(title="Optional")
+ whitelist_native_plugins: bool = Field(
+ title="Whitelist Maya Native Plugins"
+ )
+ authorized_plugins: list[str] = Field(
+ default_factory=list, title="Authorized plugins"
+ )
+
+
+class ValidateMayaUnitsModel(BaseSettingsModel):
+ enabled: bool = Field(title="ValidateMayaUnits")
+ optional: bool = Field(title="Optional")
+ validate_linear_units: bool = Field(title="Validate linear units")
+ linear_units: str = Field(
+ enum_resolver=linear_unit_enum, title="Linear Units"
+ )
+ validate_angular_units: bool = Field(title="Validate angular units")
+ angular_units: str = Field(
+ enum_resolver=angular_unit_enum, title="Angular units"
+ )
+ validate_fps: bool = Field(title="Validate fps")
+
+
+class ValidateUnrealStaticMeshNameModel(BaseSettingsModel):
+ enabled: bool = Field(title="ValidateUnrealStaticMeshName")
+ optional: bool = Field(title="Optional")
+ validate_mesh: bool = Field(title="Validate mesh names")
+ validate_collision: bool = Field(title="Validate collison names")
+
+
+class ValidateCycleErrorModel(BaseSettingsModel):
+ enabled: bool = Field(title="ValidateCycleError")
+ optional: bool = Field(title="Optional")
+ families: list[str] = Field(default_factory=list, title="Families")
+
+
+class ValidatePluginPathAttributesAttrModel(BaseSettingsModel):
+ name: str = Field(title="Node type")
+ value: str = Field(title="Attribute")
+
+
+class ValidatePluginPathAttributesModel(BaseSettingsModel):
+ """Fill in the node types and attributes you want to validate.
+
+ e.g. AlembicNode.abc_file, the node type is AlembicNode
+ and the node attribute is abc_file
+ """
+
+ enabled: bool = True
+ optional: bool = Field(title="Optional")
+ active: bool = Field(title="Active")
+ attribute: list[ValidatePluginPathAttributesAttrModel] = Field(
+ default_factory=list,
+ title="File Attribute"
+ )
+
+ @validator("attribute")
+ def validate_unique_outputs(cls, value):
+ ensure_unique_names(value)
+ return value
+
+
+# Validate Render Setting
+class RendererAttributesModel(BaseSettingsModel):
+ _layout = "compact"
+ type: str = Field(title="Type")
+ value: str = Field(title="Value")
+
+
+class ValidateRenderSettingsModel(BaseSettingsModel):
+ arnold_render_attributes: list[RendererAttributesModel] = Field(
+ default_factory=list, title="Arnold Render Attributes")
+ vray_render_attributes: list[RendererAttributesModel] = Field(
+ default_factory=list, title="VRay Render Attributes")
+ redshift_render_attributes: list[RendererAttributesModel] = Field(
+ default_factory=list, title="Redshift Render Attributes")
+ renderman_render_attributes: list[RendererAttributesModel] = Field(
+ default_factory=list, title="Renderman Render Attributes")
+
+
+class BasicValidateModel(BaseSettingsModel):
+ enabled: bool = Field(title="Enabled")
+ optional: bool = Field(title="Optional")
+ active: bool = Field(title="Active")
+
+
+class ValidateCameraContentsModel(BaseSettingsModel):
+ enabled: bool = Field(title="Enabled")
+ optional: bool = Field(title="Optional")
+ validate_shapes: bool = Field(title="Validate presence of shapes")
+
+
+class ExtractProxyAlembicModel(BaseSettingsModel):
+ enabled: bool = Field(title="Enabled")
+ families: list[str] = Field(
+ default_factory=list,
+ title="Families")
+
+
+class ExtractAlembicModel(BaseSettingsModel):
+ enabled: bool = Field(title="Enabled")
+ families: list[str] = Field(
+ default_factory=list,
+ title="Families")
+
+
+class ExtractObjModel(BaseSettingsModel):
+ enabled: bool = Field(title="Enabled")
+ optional: bool = Field(title="Optional")
+
+
+class ExtractMayaSceneRawModel(BaseSettingsModel):
+ """Add loaded instances to those published families:"""
+ enabled: bool = Field(title="ExtractMayaSceneRaw")
+ add_for_families: list[str] = Field(default_factory=list, title="Families")
+
+
+class ExtractCameraAlembicModel(BaseSettingsModel):
+ """
+ List of attributes that will be added to the baked alembic camera. Needs to be written in python list syntax.
+ """
+ enabled: bool = Field(title="ExtractCameraAlembic")
+ optional: bool = Field(title="Optional")
+ active: bool = Field(title="Active")
+ bake_attributes: str = Field(
+ "[]", title="Base Attributes", widget="textarea"
+ )
+
+ @validator("bake_attributes")
+ def validate_json_list(cls, value):
+ if not value.strip():
+ return "[]"
+ try:
+ converted_value = json.loads(value)
+ success = isinstance(converted_value, list)
+ except json.JSONDecodeError:
+ success = False
+
+ if not success:
+ raise BadRequestException(
+ "The text can't be parsed as json object"
+ )
+ return value
+
+
+class ExtractGLBModel(BaseSettingsModel):
+ enabled: bool = True
+ active: bool = Field(title="Active")
+ ogsfx_path: str = Field(title="GLSL Shader Directory")
+
+
+class ExtractLookArgsModel(BaseSettingsModel):
+ argument: str = Field(title="Argument")
+ parameters: list[str] = Field(default_factory=list, title="Parameters")
+
+
+class ExtractLookModel(BaseSettingsModel):
+ maketx_arguments: list[ExtractLookArgsModel] = Field(
+ default_factory=list,
+ title="Extra arguments for maketx command line"
+ )
+
+
+class ExtractGPUCacheModel(BaseSettingsModel):
+ enabled: bool = True
+ families: list[str] = Field(default_factory=list, title="Families")
+ step: float = Field(1.0, ge=1.0, title="Step")
+ stepSave: int = Field(1, ge=1, title="Step Save")
+ optimize: bool = Field(title="Optimize Hierarchy")
+ optimizationThreshold: int = Field(1, ge=1, title="Optimization Threshold")
+ optimizeAnimationsForMotionBlur: bool = Field(
+ title="Optimize Animations For Motion Blur"
+ )
+ writeMaterials: bool = Field(title="Write Materials")
+ useBaseTessellation: bool = Field(title="User Base Tesselation")
+
+
+class PublishersModel(BaseSettingsModel):
+ CollectMayaRender: CollectMayaRenderModel = Field(
+ default_factory=CollectMayaRenderModel,
+ title="Collect Render Layers",
+ section="Collectors"
+ )
+ CollectFbxCamera: CollectFbxCameraModel = Field(
+ default_factory=CollectFbxCameraModel,
+ title="Collect Camera for FBX export",
+ )
+ CollectGLTF: CollectGLTFModel = Field(
+ default_factory=CollectGLTFModel,
+ title="Collect Assets for GLB/GLTF export"
+ )
+ ValidateInstanceInContext: BasicValidateModel = Field(
+ default_factory=BasicValidateModel,
+ title="Validate Instance In Context",
+ section="Validators"
+ )
+ ValidateContainers: BasicValidateModel = Field(
+ default_factory=BasicValidateModel,
+ title="Validate Containers"
+ )
+ ValidateFrameRange: ValidateFrameRangeModel = Field(
+ default_factory=ValidateFrameRangeModel,
+ title="Validate Frame Range"
+ )
+ ValidateShaderName: ValidateShaderNameModel = Field(
+ default_factory=ValidateShaderNameModel,
+ title="Validate Shader Name"
+ )
+ ValidateShadingEngine: BasicValidateModel = Field(
+ default_factory=BasicValidateModel,
+ title="Validate Look Shading Engine Naming"
+ )
+ ValidateMayaColorSpace: BasicValidateModel = Field(
+ default_factory=BasicValidateModel,
+ title="Validate Colorspace"
+ )
+ ValidateAttributes: ValidateAttributesModel = Field(
+ default_factory=ValidateAttributesModel,
+ title="Validate Attributes"
+ )
+ ValidateLoadedPlugin: ValidateLoadedPluginModel = Field(
+ default_factory=ValidateLoadedPluginModel,
+ title="Validate Loaded Plugin"
+ )
+ ValidateMayaUnits: ValidateMayaUnitsModel = Field(
+ default_factory=ValidateMayaUnitsModel,
+ title="Validate Maya Units"
+ )
+ ValidateUnrealStaticMeshName: ValidateUnrealStaticMeshNameModel = Field(
+ default_factory=ValidateUnrealStaticMeshNameModel,
+ title="Validate Unreal Static Mesh Name"
+ )
+ ValidateCycleError: ValidateCycleErrorModel = Field(
+ default_factory=ValidateCycleErrorModel,
+ title="Validate Cycle Error"
+ )
+ ValidatePluginPathAttributes: ValidatePluginPathAttributesModel = Field(
+ default_factory=ValidatePluginPathAttributesModel,
+ title="Plug-in Path Attributes"
+ )
+ ValidateRenderSettings: ValidateRenderSettingsModel = Field(
+ default_factory=ValidateRenderSettingsModel,
+ title="Validate Render Settings"
+ )
+ ValidateCurrentRenderLayerIsRenderable: BasicValidateModel = Field(
+ default_factory=BasicValidateModel,
+ title="Validate Current Render Layer Has Renderable Camera"
+ )
+ ValidateGLSLMaterial: BasicValidateModel = Field(
+ default_factory=BasicValidateModel,
+ title="Validate GLSL Material"
+ )
+ ValidateGLSLPlugin: BasicValidateModel = Field(
+ default_factory=BasicValidateModel,
+ title="Validate GLSL Plugin"
+ )
+ ValidateRenderImageRule: BasicValidateModel = Field(
+ default_factory=BasicValidateModel,
+ title="Validate Render Image Rule (Workspace)"
+ )
+ ValidateRenderNoDefaultCameras: BasicValidateModel = Field(
+ default_factory=BasicValidateModel,
+ title="Validate No Default Cameras Renderable"
+ )
+ ValidateRenderSingleCamera: BasicValidateModel = Field(
+ default_factory=BasicValidateModel,
+ title="Validate Render Single Camera "
+ )
+ ValidateRenderLayerAOVs: BasicValidateModel = Field(
+ default_factory=BasicValidateModel,
+ title="Validate Render Passes/AOVs Are Registered"
+ )
+ ValidateStepSize: BasicValidateModel = Field(
+ default_factory=BasicValidateModel,
+ title="Validate Step Size"
+ )
+ ValidateVRayDistributedRendering: BasicValidateModel = Field(
+ default_factory=BasicValidateModel,
+ title="VRay Distributed Rendering"
+ )
+ ValidateVrayReferencedAOVs: BasicValidateModel = Field(
+ default_factory=BasicValidateModel,
+ title="VRay Referenced AOVs"
+ )
+ ValidateVRayTranslatorEnabled: BasicValidateModel = Field(
+ default_factory=BasicValidateModel,
+ title="VRay Translator Settings"
+ )
+ ValidateVrayProxy: BasicValidateModel = Field(
+ default_factory=BasicValidateModel,
+ title="VRay Proxy Settings"
+ )
+ ValidateVrayProxyMembers: BasicValidateModel = Field(
+ default_factory=BasicValidateModel,
+ title="VRay Proxy Members"
+ )
+ ValidateYetiRenderScriptCallbacks: BasicValidateModel = Field(
+ default_factory=BasicValidateModel,
+ title="Yeti Render Script Callbacks"
+ )
+ ValidateYetiRigCacheState: BasicValidateModel = Field(
+ default_factory=BasicValidateModel,
+ title="Yeti Rig Cache State"
+ )
+ ValidateYetiRigInputShapesInInstance: BasicValidateModel = Field(
+ default_factory=BasicValidateModel,
+ title="Yeti Rig Input Shapes In Instance"
+ )
+ ValidateYetiRigSettings: BasicValidateModel = Field(
+ default_factory=BasicValidateModel,
+ title="Yeti Rig Settings"
+ )
+ # Model - START
+ ValidateModelName: ValidateModelNameModel = Field(
+ default_factory=ValidateModelNameModel,
+ title="Validate Model Name",
+ section="Model",
+ )
+ ValidateModelContent: ValidateModelContentModel = Field(
+ default_factory=ValidateModelContentModel,
+ title="Validate Model Content",
+ )
+ ValidateTransformNamingSuffix: ValidateTransformNamingSuffixModel = Field(
+ default_factory=ValidateTransformNamingSuffixModel,
+ title="Validate Transform Naming Suffix",
+ )
+ ValidateColorSets: BasicValidateModel = Field(
+ default_factory=BasicValidateModel,
+ title="Validate Color Sets",
+ )
+ ValidateMeshHasOverlappingUVs: BasicValidateModel = Field(
+ default_factory=BasicValidateModel,
+ title="Validate Mesh Has Overlapping UVs",
+ )
+ ValidateMeshArnoldAttributes: BasicValidateModel = Field(
+ default_factory=BasicValidateModel,
+ title="Validate Mesh Arnold Attributes",
+ )
+ ValidateMeshShaderConnections: BasicValidateModel = Field(
+ default_factory=BasicValidateModel,
+ title="Validate Mesh Shader Connections",
+ )
+ ValidateMeshSingleUVSet: BasicValidateModel = Field(
+ default_factory=BasicValidateModel,
+ title="Validate Mesh Single UV Set",
+ )
+ ValidateMeshHasUVs: BasicValidateModel = Field(
+ default_factory=BasicValidateModel,
+ title="Validate Mesh Has UVs",
+ )
+ ValidateMeshLaminaFaces: BasicValidateModel = Field(
+ default_factory=BasicValidateModel,
+ title="Validate Mesh Lamina Faces",
+ )
+ ValidateMeshNgons: BasicValidateModel = Field(
+ default_factory=BasicValidateModel,
+ title="Validate Mesh Ngons",
+ )
+ ValidateMeshNonManifold: BasicValidateModel = Field(
+ default_factory=BasicValidateModel,
+ title="Validate Mesh Non-Manifold",
+ )
+ ValidateMeshNoNegativeScale: BasicValidateModel = Field(
+ default_factory=BasicValidateModel,
+ title="Validate Mesh No Negative Scale",
+ )
+ ValidateMeshNonZeroEdgeLength: BasicValidateModel = Field(
+ default_factory=BasicValidateModel,
+ title="Validate Mesh Edge Length Non Zero",
+ )
+ ValidateMeshNormalsUnlocked: BasicValidateModel = Field(
+ default_factory=BasicValidateModel,
+ title="Validate Mesh Normals Unlocked",
+ )
+ ValidateMeshUVSetMap1: ValidateMeshUVSetMap1Model = Field(
+ default_factory=ValidateMeshUVSetMap1Model,
+ title="Validate Mesh UV Set Map 1",
+ )
+ ValidateMeshVerticesHaveEdges: BasicValidateModel = Field(
+ default_factory=BasicValidateModel,
+ title="Validate Mesh Vertices Have Edges",
+ )
+ ValidateNoAnimation: ValidateNoAnimationModel = Field(
+ default_factory=ValidateNoAnimationModel,
+ title="Validate No Animation",
+ )
+ ValidateNoNamespace: BasicValidateModel = Field(
+ default_factory=BasicValidateModel,
+ title="Validate No Namespace",
+ )
+ ValidateNoNullTransforms: BasicValidateModel = Field(
+ default_factory=BasicValidateModel,
+ title="Validate No Null Transforms",
+ )
+ ValidateNoUnknownNodes: BasicValidateModel = Field(
+ default_factory=BasicValidateModel,
+ title="Validate No Unknown Nodes",
+ )
+ ValidateNodeNoGhosting: BasicValidateModel = Field(
+ default_factory=BasicValidateModel,
+ title="Validate Node No Ghosting",
+ )
+ ValidateShapeDefaultNames: BasicValidateModel = Field(
+ default_factory=BasicValidateModel,
+ title="Validate Shape Default Names",
+ )
+ ValidateShapeRenderStats: BasicValidateModel = Field(
+ default_factory=BasicValidateModel,
+ title="Validate Shape Render Stats",
+ )
+ ValidateShapeZero: BasicValidateModel = Field(
+ default_factory=BasicValidateModel,
+ title="Validate Shape Zero",
+ )
+ ValidateTransformZero: BasicValidateModel = Field(
+ default_factory=BasicValidateModel,
+ title="Validate Transform Zero",
+ )
+ ValidateUniqueNames: BasicValidateModel = Field(
+ default_factory=BasicValidateModel,
+ title="Validate Unique Names",
+ )
+ ValidateNoVRayMesh: BasicValidateModel = Field(
+ default_factory=BasicValidateModel,
+ title="Validate No V-Ray Proxies (VRayMesh)",
+ )
+ ValidateUnrealMeshTriangulated: BasicValidateModel = Field(
+ default_factory=BasicValidateModel,
+ title="Validate if Mesh is Triangulated",
+ )
+ ValidateAlembicVisibleOnly: BasicValidateModel = Field(
+ default_factory=BasicValidateModel,
+ title="Validate Alembic Visible Node",
+ )
+ ExtractProxyAlembic: ExtractProxyAlembicModel = Field(
+ default_factory=ExtractProxyAlembicModel,
+ title="Extract Proxy Alembic",
+ section="Model Extractors",
+ )
+ ExtractAlembic: ExtractAlembicModel = Field(
+ default_factory=ExtractAlembicModel,
+ title="Extract Alembic",
+ )
+ ExtractObj: ExtractObjModel = Field(
+ default_factory=ExtractObjModel,
+ title="Extract OBJ"
+ )
+ # Model - END
+
+ # Rig - START
+ ValidateRigContents: BasicValidateModel = Field(
+ default_factory=BasicValidateModel,
+ title="Validate Rig Contents",
+ section="Rig",
+ )
+ ValidateRigJointsHidden: BasicValidateModel = Field(
+ default_factory=BasicValidateModel,
+ title="Validate Rig Joints Hidden",
+ )
+ ValidateRigControllers: BasicValidateModel = Field(
+ default_factory=BasicValidateModel,
+ title="Validate Rig Controllers",
+ )
+ ValidateAnimationContent: BasicValidateModel = Field(
+ default_factory=BasicValidateModel,
+ title="Validate Animation Content",
+ )
+ ValidateOutRelatedNodeIds: BasicValidateModel = Field(
+ default_factory=BasicValidateModel,
+ title="Validate Animation Out Set Related Node Ids",
+ )
+ ValidateRigControllersArnoldAttributes: BasicValidateModel = Field(
+ default_factory=BasicValidateModel,
+ title="Validate Rig Controllers (Arnold Attributes)",
+ )
+ ValidateSkeletalMeshHierarchy: BasicValidateModel = Field(
+ default_factory=BasicValidateModel,
+ title="Validate Skeletal Mesh Top Node",
+ )
+ ValidateSkinclusterDeformerSet: BasicValidateModel = Field(
+ default_factory=BasicValidateModel,
+ title="Validate Skincluster Deformer Relationships",
+ )
+ ValidateRigOutSetNodeIds: ValidateRigOutSetNodeIdsModel = Field(
+ default_factory=ValidateRigOutSetNodeIdsModel,
+ title="Validate Rig Out Set Node Ids",
+ )
+ # Rig - END
+ ValidateCameraAttributes: BasicValidateModel = Field(
+ default_factory=BasicValidateModel,
+ title="Validate Camera Attributes"
+ )
+ ValidateAssemblyName: BasicValidateModel = Field(
+ default_factory=BasicValidateModel,
+ title="Validate Assembly Name"
+ )
+ ValidateAssemblyNamespaces: BasicValidateModel = Field(
+ default_factory=BasicValidateModel,
+ title="Validate Assembly Namespaces"
+ )
+ ValidateAssemblyModelTransforms: BasicValidateModel = Field(
+ default_factory=BasicValidateModel,
+ title="Validate Assembly Model Transforms"
+ )
+ ValidateAssRelativePaths: BasicValidateModel = Field(
+ default_factory=BasicValidateModel,
+ title="Validate Ass Relative Paths"
+ )
+ ValidateInstancerContent: BasicValidateModel = Field(
+ default_factory=BasicValidateModel,
+ title="Validate Instancer Content"
+ )
+ ValidateInstancerFrameRanges: BasicValidateModel = Field(
+ default_factory=BasicValidateModel,
+ title="Validate Instancer Cache Frame Ranges"
+ )
+ ValidateNoDefaultCameras: BasicValidateModel = Field(
+ default_factory=BasicValidateModel,
+ title="Validate No Default Cameras"
+ )
+ ValidateUnrealUpAxis: BasicValidateModel = Field(
+ default_factory=BasicValidateModel,
+ title="Validate Unreal Up-Axis Check"
+ )
+ ValidateCameraContents: ValidateCameraContentsModel = Field(
+ default_factory=ValidateCameraContentsModel,
+ title="Validate Camera Content"
+ )
+ ExtractPlayblast: ExtractPlayblastSetting = Field(
+ default_factory=ExtractPlayblastSetting,
+ title="Extract Playblast Settings",
+ section="Extractors"
+ )
+ ExtractMayaSceneRaw: ExtractMayaSceneRawModel = Field(
+ default_factory=ExtractMayaSceneRawModel,
+ title="Maya Scene(Raw)"
+ )
+ ExtractCameraAlembic: ExtractCameraAlembicModel = Field(
+ default_factory=ExtractCameraAlembicModel,
+ title="Extract Camera Alembic"
+ )
+ ExtractGLB: ExtractGLBModel = Field(
+ default_factory=ExtractGLBModel,
+ title="Extract GLB"
+ )
+ ExtractLook: ExtractLookModel = Field(
+ default_factory=ExtractLookModel,
+ title="Extract Look"
+ )
+ ExtractGPUCache: ExtractGPUCacheModel = Field(
+ default_factory=ExtractGPUCacheModel,
+ title="Extract GPU Cache",
+ )
+
+
+DEFAULT_SUFFIX_NAMING = {
+ "mesh": ["_GEO", "_GES", "_GEP", "_OSD"],
+ "nurbsCurve": ["_CRV"],
+ "nurbsSurface": ["_NRB"],
+ "locator": ["_LOC"],
+ "group": ["_GRP"]
+}
+
+DEFAULT_PUBLISH_SETTINGS = {
+ "CollectMayaRender": {
+ "sync_workfile_version": False
+ },
+ "CollectFbxCamera": {
+ "enabled": False
+ },
+ "CollectGLTF": {
+ "enabled": False
+ },
+ "ValidateInstanceInContext": {
+ "enabled": True,
+ "optional": True,
+ "active": True
+ },
+ "ValidateContainers": {
+ "enabled": True,
+ "optional": True,
+ "active": True
+ },
+ "ValidateFrameRange": {
+ "enabled": True,
+ "optional": True,
+ "active": True,
+ "exclude_product_types": [
+ "model",
+ "rig",
+ "staticMesh"
+ ]
+ },
+ "ValidateShaderName": {
+ "enabled": False,
+ "optional": True,
+ "active": True,
+ "regex": "(?P.*)_(.*)_SHD"
+ },
+ "ValidateShadingEngine": {
+ "enabled": True,
+ "optional": True,
+ "active": True
+ },
+ "ValidateMayaColorSpace": {
+ "enabled": True,
+ "optional": True,
+ "active": True
+ },
+ "ValidateAttributes": {
+ "enabled": False,
+ "attributes": "{}"
+ },
+ "ValidateLoadedPlugin": {
+ "enabled": False,
+ "optional": True,
+ "whitelist_native_plugins": False,
+ "authorized_plugins": []
+ },
+ "ValidateMayaUnits": {
+ "enabled": True,
+ "optional": False,
+ "validate_linear_units": True,
+ "linear_units": "cm",
+ "validate_angular_units": True,
+ "angular_units": "deg",
+ "validate_fps": True
+ },
+ "ValidateUnrealStaticMeshName": {
+ "enabled": True,
+ "optional": True,
+ "validate_mesh": False,
+ "validate_collision": True
+ },
+ "ValidateCycleError": {
+ "enabled": True,
+ "optional": False,
+ "families": [
+ "rig"
+ ]
+ },
+ "ValidatePluginPathAttributes": {
+ "enabled": True,
+ "optional": False,
+ "active": True,
+ "attribute": [
+ {"name": "AlembicNode", "value": "abc_File"},
+ {"name": "VRayProxy", "value": "fileName"},
+ {"name": "RenderManArchive", "value": "filename"},
+ {"name": "pgYetiMaya", "value": "cacheFileName"},
+ {"name": "aiStandIn", "value": "dso"},
+ {"name": "RedshiftSprite", "value": "tex0"},
+ {"name": "RedshiftBokeh", "value": "dofBokehImage"},
+ {"name": "RedshiftCameraMap", "value": "tex0"},
+ {"name": "RedshiftEnvironment", "value": "tex2"},
+ {"name": "RedshiftDomeLight", "value": "tex1"},
+ {"name": "RedshiftIESLight", "value": "profile"},
+ {"name": "RedshiftLightGobo", "value": "tex0"},
+ {"name": "RedshiftNormalMap", "value": "tex0"},
+ {"name": "RedshiftProxyMesh", "value": "fileName"},
+ {"name": "RedshiftVolumeShape", "value": "fileName"},
+ {"name": "VRayTexGLSL", "value": "fileName"},
+ {"name": "VRayMtlGLSL", "value": "fileName"},
+ {"name": "VRayVRmatMtl", "value": "fileName"},
+ {"name": "VRayPtex", "value": "ptexFile"},
+ {"name": "VRayLightIESShape", "value": "iesFile"},
+ {"name": "VRayMesh", "value": "materialAssignmentsFile"},
+ {"name": "VRayMtlOSL", "value": "fileName"},
+ {"name": "VRayTexOSL", "value": "fileName"},
+ {"name": "VRayTexOCIO", "value": "ocioConfigFile"},
+ {"name": "VRaySettingsNode", "value": "pmap_autoSaveFile2"},
+ {"name": "VRayScannedMtl", "value": "file"},
+ {"name": "VRayScene", "value": "parameterOverrideFilePath"},
+ {"name": "VRayMtlMDL", "value": "filename"},
+ {"name": "VRaySimbiont", "value": "file"},
+ {"name": "dlOpenVDBShape", "value": "filename"},
+ {"name": "pgYetiMayaShape", "value": "liveABCFilename"},
+ {"name": "gpuCache", "value": "cacheFileName"},
+ ]
+ },
+ "ValidateRenderSettings": {
+ "arnold_render_attributes": [],
+ "vray_render_attributes": [],
+ "redshift_render_attributes": [],
+ "renderman_render_attributes": []
+ },
+ "ValidateCurrentRenderLayerIsRenderable": {
+ "enabled": True,
+ "optional": False,
+ "active": True
+ },
+ "ValidateGLSLMaterial": {
+ "enabled": True,
+ "optional": False,
+ "active": True
+ },
+ "ValidateGLSLPlugin": {
+ "enabled": True,
+ "optional": False,
+ "active": True
+ },
+ "ValidateRenderImageRule": {
+ "enabled": True,
+ "optional": False,
+ "active": True
+ },
+ "ValidateRenderNoDefaultCameras": {
+ "enabled": True,
+ "optional": False,
+ "active": True
+ },
+ "ValidateRenderSingleCamera": {
+ "enabled": True,
+ "optional": False,
+ "active": True
+ },
+ "ValidateRenderLayerAOVs": {
+ "enabled": True,
+ "optional": False,
+ "active": True
+ },
+ "ValidateStepSize": {
+ "enabled": True,
+ "optional": False,
+ "active": True
+ },
+ "ValidateVRayDistributedRendering": {
+ "enabled": True,
+ "optional": False,
+ "active": True
+ },
+ "ValidateVrayReferencedAOVs": {
+ "enabled": True,
+ "optional": False,
+ "active": True
+ },
+ "ValidateVRayTranslatorEnabled": {
+ "enabled": True,
+ "optional": False,
+ "active": True
+ },
+ "ValidateVrayProxy": {
+ "enabled": True,
+ "optional": False,
+ "active": True
+ },
+ "ValidateVrayProxyMembers": {
+ "enabled": True,
+ "optional": False,
+ "active": True
+ },
+ "ValidateYetiRenderScriptCallbacks": {
+ "enabled": True,
+ "optional": False,
+ "active": True
+ },
+ "ValidateYetiRigCacheState": {
+ "enabled": True,
+ "optional": False,
+ "active": True
+ },
+ "ValidateYetiRigInputShapesInInstance": {
+ "enabled": True,
+ "optional": False,
+ "active": True
+ },
+ "ValidateYetiRigSettings": {
+ "enabled": True,
+ "optional": False,
+ "active": True
+ },
+ "ValidateModelName": {
+ "enabled": False,
+ "database": True,
+ "material_file": {
+ "windows": "",
+ "darwin": "",
+ "linux": ""
+ },
+ "regex": "(.*)_(\\d)*_(?P.*)_(GEO)",
+ "top_level_regex": ".*_GRP"
+ },
+ "ValidateModelContent": {
+ "enabled": True,
+ "optional": False,
+ "validate_top_group": True
+ },
+ "ValidateTransformNamingSuffix": {
+ "enabled": True,
+ "optional": True,
+ "SUFFIX_NAMING_TABLE": json.dumps(DEFAULT_SUFFIX_NAMING, indent=4),
+ "ALLOW_IF_NOT_IN_SUFFIX_TABLE": True
+ },
+ "ValidateColorSets": {
+ "enabled": True,
+ "optional": True,
+ "active": True
+ },
+ "ValidateMeshHasOverlappingUVs": {
+ "enabled": False,
+ "optional": True,
+ "active": True
+ },
+ "ValidateMeshArnoldAttributes": {
+ "enabled": False,
+ "optional": True,
+ "active": True
+ },
+ "ValidateMeshShaderConnections": {
+ "enabled": True,
+ "optional": True,
+ "active": True
+ },
+ "ValidateMeshSingleUVSet": {
+ "enabled": False,
+ "optional": True,
+ "active": True
+ },
+ "ValidateMeshHasUVs": {
+ "enabled": True,
+ "optional": True,
+ "active": True
+ },
+ "ValidateMeshLaminaFaces": {
+ "enabled": False,
+ "optional": True,
+ "active": True
+ },
+ "ValidateMeshNgons": {
+ "enabled": False,
+ "optional": True,
+ "active": True
+ },
+ "ValidateMeshNonManifold": {
+ "enabled": False,
+ "optional": True,
+ "active": True
+ },
+ "ValidateMeshNoNegativeScale": {
+ "enabled": True,
+ "optional": False,
+ "active": True
+ },
+ "ValidateMeshNonZeroEdgeLength": {
+ "enabled": True,
+ "optional": True,
+ "active": True
+ },
+ "ValidateMeshNormalsUnlocked": {
+ "enabled": False,
+ "optional": True,
+ "active": True
+ },
+ "ValidateMeshUVSetMap1": {
+ "enabled": False,
+ "optional": True,
+ "active": True
+ },
+ "ValidateMeshVerticesHaveEdges": {
+ "enabled": True,
+ "optional": True,
+ "active": True
+ },
+ "ValidateNoAnimation": {
+ "enabled": False,
+ "optional": True,
+ "active": True
+ },
+ "ValidateNoNamespace": {
+ "enabled": True,
+ "optional": False,
+ "active": True
+ },
+ "ValidateNoNullTransforms": {
+ "enabled": True,
+ "optional": False,
+ "active": True
+ },
+ "ValidateNoUnknownNodes": {
+ "enabled": True,
+ "optional": False,
+ "active": True
+ },
+ "ValidateNodeNoGhosting": {
+ "enabled": False,
+ "optional": False,
+ "active": True
+ },
+ "ValidateShapeDefaultNames": {
+ "enabled": False,
+ "optional": True,
+ "active": True
+ },
+ "ValidateShapeRenderStats": {
+ "enabled": False,
+ "optional": True,
+ "active": True
+ },
+ "ValidateShapeZero": {
+ "enabled": False,
+ "optional": True,
+ "active": True
+ },
+ "ValidateTransformZero": {
+ "enabled": False,
+ "optional": True,
+ "active": True
+ },
+ "ValidateUniqueNames": {
+ "enabled": False,
+ "optional": True,
+ "active": True
+ },
+ "ValidateNoVRayMesh": {
+ "enabled": True,
+ "optional": False,
+ "active": True
+ },
+ "ValidateUnrealMeshTriangulated": {
+ "enabled": False,
+ "optional": True,
+ "active": True
+ },
+ "ValidateAlembicVisibleOnly": {
+ "enabled": True,
+ "optional": False,
+ "active": True
+ },
+ "ExtractProxyAlembic": {
+ "enabled": True,
+ "families": [
+ "proxyAbc"
+ ]
+ },
+ "ExtractAlembic": {
+ "enabled": True,
+ "families": [
+ "pointcache",
+ "model",
+ "vrayproxy.alembic"
+ ]
+ },
+ "ExtractObj": {
+ "enabled": False,
+ "optional": True,
+ "active": True
+ },
+ "ValidateRigContents": {
+ "enabled": False,
+ "optional": True,
+ "active": True
+ },
+ "ValidateRigJointsHidden": {
+ "enabled": False,
+ "optional": True,
+ "active": True
+ },
+ "ValidateRigControllers": {
+ "enabled": False,
+ "optional": True,
+ "active": True
+ },
+ "ValidateAnimationContent": {
+ "enabled": True,
+ "optional": False,
+ "active": True
+ },
+ "ValidateOutRelatedNodeIds": {
+ "enabled": True,
+ "optional": False,
+ "active": True
+ },
+ "ValidateRigControllersArnoldAttributes": {
+ "enabled": True,
+ "optional": False,
+ "active": True
+ },
+ "ValidateSkeletalMeshHierarchy": {
+ "enabled": True,
+ "optional": False,
+ "active": True
+ },
+ "ValidateSkinclusterDeformerSet": {
+ "enabled": True,
+ "optional": False,
+ "active": True
+ },
+ "ValidateRigOutSetNodeIds": {
+ "enabled": True,
+ "optional": False,
+ "allow_history_only": False
+ },
+ "ValidateCameraAttributes": {
+ "enabled": False,
+ "optional": True,
+ "active": True
+ },
+ "ValidateAssemblyName": {
+ "enabled": True,
+ "optional": True,
+ "active": True
+ },
+ "ValidateAssemblyNamespaces": {
+ "enabled": True,
+ "optional": False,
+ "active": True
+ },
+ "ValidateAssemblyModelTransforms": {
+ "enabled": True,
+ "optional": False,
+ "active": True
+ },
+ "ValidateAssRelativePaths": {
+ "enabled": True,
+ "optional": False,
+ "active": True
+ },
+ "ValidateInstancerContent": {
+ "enabled": True,
+ "optional": False,
+ "active": True
+ },
+ "ValidateInstancerFrameRanges": {
+ "enabled": True,
+ "optional": False,
+ "active": True
+ },
+ "ValidateNoDefaultCameras": {
+ "enabled": True,
+ "optional": False,
+ "active": True
+ },
+ "ValidateUnrealUpAxis": {
+ "enabled": False,
+ "optional": True,
+ "active": True
+ },
+ "ValidateCameraContents": {
+ "enabled": True,
+ "optional": False,
+ "validate_shapes": True
+ },
+ "ExtractPlayblast": DEFAULT_PLAYBLAST_SETTING,
+ "ExtractMayaSceneRaw": {
+ "enabled": True,
+ "add_for_families": [
+ "layout"
+ ]
+ },
+ "ExtractCameraAlembic": {
+ "enabled": True,
+ "optional": True,
+ "active": True,
+ "bake_attributes": "[]"
+ },
+ "ExtractGLB": {
+ "enabled": True,
+ "active": True,
+ "ogsfx_path": "/maya2glTF/PBR/shaders/glTF_PBR.ogsfx"
+ },
+ "ExtractLook": {
+ "maketx_arguments": []
+ },
+ "ExtractGPUCache": {
+ "enabled": False,
+ "families": [
+ "model",
+ "animation",
+ "pointcache"
+ ],
+ "step": 1.0,
+ "stepSave": 1,
+ "optimize": True,
+ "optimizationThreshold": 40000,
+ "optimizeAnimationsForMotionBlur": True,
+ "writeMaterials": True,
+ "useBaseTessellation": True
+ }
+}
diff --git a/server_addon/maya/server/settings/render_settings.py b/server_addon/maya/server/settings/render_settings.py
new file mode 100644
index 0000000000..b6163a04ce
--- /dev/null
+++ b/server_addon/maya/server/settings/render_settings.py
@@ -0,0 +1,500 @@
+"""Providing models and values for Maya Render Settings."""
+from pydantic import Field
+
+from ayon_server.settings import BaseSettingsModel
+
+
+def aov_separators_enum():
+ return [
+ {"value": "dash", "label": "- (dash)"},
+ {"value": "underscore", "label": "_ (underscore)"},
+ {"value": "dot", "label": ". (dot)"}
+ ]
+
+
+def arnold_image_format_enum():
+ """Return enumerator for Arnold output formats."""
+ return [
+ {"label": "jpeg", "value": "jpeg"},
+ {"label": "png", "value": "png"},
+ {"label": "deepexr", "value": "deep exr"},
+ {"label": "tif", "value": "tif"},
+ {"label": "exr", "value": "exr"},
+ {"label": "maya", "value": "maya"},
+ {"label": "mtoa_shaders", "value": "mtoa_shaders"}
+ ]
+
+
+def arnold_aov_list_enum():
+ """Return enumerator for Arnold AOVs.
+
+ Note: Key is value, Value in this case is Label. This
+ was taken from v3 settings.
+ """
+ return [
+ {"value": "empty", "label": "< empty >"},
+ {"value": "ID", "label": "ID"},
+ {"value": "N", "label": "N"},
+ {"value": "P", "label": "P"},
+ {"value": "Pref", "label": "Pref"},
+ {"value": "RGBA", "label": "RGBA"},
+ {"value": "Z", "label": "Z"},
+ {"value": "albedo", "label": "albedo"},
+ {"value": "background", "label": "background"},
+ {"value": "coat", "label": "coat"},
+ {"value": "coat_albedo", "label": "coat_albedo"},
+ {"value": "coat_direct", "label": "coat_direct"},
+ {"value": "coat_indirect", "label": "coat_indirect"},
+ {"value": "cputime", "label": "cputime"},
+ {"value": "crypto_asset", "label": "crypto_asset"},
+ {"value": "crypto_material", "label": "cypto_material"},
+ {"value": "crypto_object", "label": "crypto_object"},
+ {"value": "diffuse", "label": "diffuse"},
+ {"value": "diffuse_albedo", "label": "diffuse_albedo"},
+ {"value": "diffuse_direct", "label": "diffuse_direct"},
+ {"value": "diffuse_indirect", "label": "diffuse_indirect"},
+ {"value": "direct", "label": "direct"},
+ {"value": "emission", "label": "emission"},
+ {"value": "highlight", "label": "highlight"},
+ {"value": "indirect", "label": "indirect"},
+ {"value": "motionvector", "label": "motionvector"},
+ {"value": "opacity", "label": "opacity"},
+ {"value": "raycount", "label": "raycount"},
+ {"value": "rim_light", "label": "rim_light"},
+ {"value": "shadow", "label": "shadow"},
+ {"value": "shadow_diff", "label": "shadow_diff"},
+ {"value": "shadow_mask", "label": "shadow_mask"},
+ {"value": "shadow_matte", "label": "shadow_matte"},
+ {"value": "sheen", "label": "sheen"},
+ {"value": "sheen_albedo", "label": "sheen_albedo"},
+ {"value": "sheen_direct", "label": "sheen_direct"},
+ {"value": "sheen_indirect", "label": "sheen_indirect"},
+ {"value": "specular", "label": "specular"},
+ {"value": "specular_albedo", "label": "specular_albedo"},
+ {"value": "specular_direct", "label": "specular_direct"},
+ {"value": "specular_indirect", "label": "specular_indirect"},
+ {"value": "sss", "label": "sss"},
+ {"value": "sss_albedo", "label": "sss_albedo"},
+ {"value": "sss_direct", "label": "sss_direct"},
+ {"value": "sss_indirect", "label": "sss_indirect"},
+ {"value": "transmission", "label": "transmission"},
+ {"value": "transmission_albedo", "label": "transmission_albedo"},
+ {"value": "transmission_direct", "label": "transmission_direct"},
+ {"value": "transmission_indirect", "label": "transmission_indirect"},
+ {"value": "volume", "label": "volume"},
+ {"value": "volume_Z", "label": "volume_Z"},
+ {"value": "volume_albedo", "label": "volume_albedo"},
+ {"value": "volume_direct", "label": "volume_direct"},
+ {"value": "volume_indirect", "label": "volume_indirect"},
+ {"value": "volume_opacity", "label": "volume_opacity"},
+ ]
+
+
+def vray_image_output_enum():
+ """Return output format for Vray enumerator."""
+ return [
+ {"label": "png", "value": "png"},
+ {"label": "jpg", "value": "jpg"},
+ {"label": "vrimg", "value": "vrimg"},
+ {"label": "hdr", "value": "hdr"},
+ {"label": "exr", "value": "exr"},
+ {"label": "exr (multichannel)", "value": "exr (multichannel)"},
+ {"label": "exr (deep)", "value": "exr (deep)"},
+ {"label": "tga", "value": "tga"},
+ {"label": "bmp", "value": "bmp"},
+ {"label": "sgi", "value": "sgi"}
+ ]
+
+
+def vray_aov_list_enum():
+ """Return enumerator for Vray AOVs.
+
+ Note: Key is value, Value in this case is Label. This
+ was taken from v3 settings.
+ """
+
+ return [
+ {"value": "empty", "label": "< empty >"},
+ {"value": "atmosphereChannel", "label": "atmosphere"},
+ {"value": "backgroundChannel", "label": "background"},
+ {"value": "bumpNormalsChannel", "label": "bumpnormals"},
+ {"value": "causticsChannel", "label": "caustics"},
+ {"value": "coatFilterChannel", "label": "coat_filter"},
+ {"value": "coatGlossinessChannel", "label": "coatGloss"},
+ {"value": "coatReflectionChannel", "label": "coat_reflection"},
+ {"value": "vrayCoatChannel", "label": "coat_specular"},
+ {"value": "CoverageChannel", "label": "coverage"},
+ {"value": "cryptomatteChannel", "label": "cryptomatte"},
+ {"value": "customColor", "label": "custom_color"},
+ {"value": "drBucketChannel", "label": "DR"},
+ {"value": "denoiserChannel", "label": "denoiser"},
+ {"value": "diffuseChannel", "label": "diffuse"},
+ {"value": "ExtraTexElement", "label": "extraTex"},
+ {"value": "giChannel", "label": "GI"},
+ {"value": "LightMixElement", "label": "None"},
+ {"value": "lightingChannel", "label": "lighting"},
+ {"value": "LightingAnalysisChannel", "label": "LightingAnalysis"},
+ {"value": "materialIDChannel", "label": "materialID"},
+ {"value": "MaterialSelectElement", "label": "materialSelect"},
+ {"value": "matteShadowChannel", "label": "matteShadow"},
+ {"value": "MultiMatteElement", "label": "multimatte"},
+ {"value": "multimatteIDChannel", "label": "multimatteID"},
+ {"value": "normalsChannel", "label": "normals"},
+ {"value": "nodeIDChannel", "label": "objectId"},
+ {"value": "objectSelectChannel", "label": "objectSelect"},
+ {"value": "rawCoatFilterChannel", "label": "raw_coat_filter"},
+ {"value": "rawCoatReflectionChannel", "label": "raw_coat_reflection"},
+ {"value": "rawDiffuseFilterChannel", "label": "rawDiffuseFilter"},
+ {"value": "rawGiChannel", "label": "rawGI"},
+ {"value": "rawLightChannel", "label": "rawLight"},
+ {"value": "rawReflectionChannel", "label": "rawReflection"},
+ {
+ "value": "rawReflectionFilterChannel",
+ "label": "rawReflectionFilter"
+ },
+ {"value": "rawRefractionChannel", "label": "rawRefraction"},
+ {
+ "value": "rawRefractionFilterChannel",
+ "label": "rawRefractionFilter"
+ },
+ {"value": "rawShadowChannel", "label": "rawShadow"},
+ {"value": "rawSheenFilterChannel", "label": "raw_sheen_filter"},
+ {
+ "value": "rawSheenReflectionChannel",
+ "label": "raw_sheen_reflection"
+ },
+ {"value": "rawTotalLightChannel", "label": "rawTotalLight"},
+ {"value": "reflectIORChannel", "label": "reflIOR"},
+ {"value": "reflectChannel", "label": "reflect"},
+ {"value": "reflectionFilterChannel", "label": "reflectionFilter"},
+ {"value": "reflectGlossinessChannel", "label": "reflGloss"},
+ {"value": "refractChannel", "label": "refract"},
+ {"value": "refractionFilterChannel", "label": "refractionFilter"},
+ {"value": "refractGlossinessChannel", "label": "refrGloss"},
+ {"value": "renderIDChannel", "label": "renderId"},
+ {"value": "FastSSS2Channel", "label": "SSS"},
+ {"value": "sampleRateChannel", "label": "sampleRate"},
+ {"value": "samplerInfo", "label": "samplerInfo"},
+ {"value": "selfIllumChannel", "label": "selfIllum"},
+ {"value": "shadowChannel", "label": "shadow"},
+ {"value": "sheenFilterChannel", "label": "sheen_filter"},
+ {"value": "sheenGlossinessChannel", "label": "sheenGloss"},
+ {"value": "sheenReflectionChannel", "label": "sheen_reflection"},
+ {"value": "vraySheenChannel", "label": "sheen_specular"},
+ {"value": "specularChannel", "label": "specular"},
+ {"value": "Toon", "label": "Toon"},
+ {"value": "toonLightingChannel", "label": "toonLighting"},
+ {"value": "toonSpecularChannel", "label": "toonSpecular"},
+ {"value": "totalLightChannel", "label": "totalLight"},
+ {"value": "unclampedColorChannel", "label": "unclampedColor"},
+ {"value": "VRScansPaintMaskChannel", "label": "VRScansPaintMask"},
+ {"value": "VRScansZoneMaskChannel", "label": "VRScansZoneMask"},
+ {"value": "velocityChannel", "label": "velocity"},
+ {"value": "zdepthChannel", "label": "zDepth"},
+ {"value": "LightSelectElement", "label": "lightselect"},
+ ]
+
+
+def redshift_engine_enum():
+ """Get Redshift engine type enumerator."""
+ return [
+ {"value": "0", "label": "None"},
+ {"value": "1", "label": "Photon Map"},
+ {"value": "2", "label": "Irradiance Cache"},
+ {"value": "3", "label": "Brute Force"}
+ ]
+
+
+def redshift_image_output_enum():
+ """Return output format for Redshift enumerator."""
+ return [
+ {"value": "iff", "label": "Maya IFF"},
+ {"value": "exr", "label": "OpenEXR"},
+ {"value": "tif", "label": "TIFF"},
+ {"value": "png", "label": "PNG"},
+ {"value": "tga", "label": "Targa"},
+ {"value": "jpg", "label": "JPEG"}
+ ]
+
+
+def redshift_aov_list_enum():
+ """Return enumerator for Vray AOVs.
+
+ Note: Key is value, Value in this case is Label. This
+ was taken from v3 settings.
+ """
+ return [
+ {"value": "empty", "label": "< none >"},
+ {"value": "AO", "label": "Ambient Occlusion"},
+ {"value": "Background", "label": "Background"},
+ {"value": "Beauty", "label": "Beauty"},
+ {"value": "BumpNormals", "label": "Bump Normals"},
+ {"value": "Caustics", "label": "Caustics"},
+ {"value": "CausticsRaw", "label": "Caustics Raw"},
+ {"value": "Cryptomatte", "label": "Cryptomatte"},
+ {"value": "Custom", "label": "Custom"},
+ {"value": "Z", "label": "Depth"},
+ {"value": "DiffuseFilter", "label": "Diffuse Filter"},
+ {"value": "DiffuseLighting", "label": "Diffuse Lighting"},
+ {"value": "DiffuseLightingRaw", "label": "Diffuse Lighting Raw"},
+ {"value": "Emission", "label": "Emission"},
+ {"value": "GI", "label": "Global Illumination"},
+ {"value": "GIRaw", "label": "Global Illumination Raw"},
+ {"value": "Matte", "label": "Matte"},
+ {"value": "MotionVectors", "label": "Ambient Occlusion"},
+ {"value": "N", "label": "Normals"},
+ {"value": "ID", "label": "ObjectID"},
+ {"value": "ObjectBumpNormal", "label": "Object-Space Bump Normals"},
+ {"value": "ObjectPosition", "label": "Object-Space Positions"},
+ {"value": "PuzzleMatte", "label": "Puzzle Matte"},
+ {"value": "Reflections", "label": "Reflections"},
+ {"value": "ReflectionsFilter", "label": "Reflections Filter"},
+ {"value": "ReflectionsRaw", "label": "Reflections Raw"},
+ {"value": "Refractions", "label": "Refractions"},
+ {"value": "RefractionsFilter", "label": "Refractions Filter"},
+ {"value": "RefractionsRaw", "label": "Refractions Filter"},
+ {"value": "Shadows", "label": "Shadows"},
+ {"value": "SpecularLighting", "label": "Specular Lighting"},
+ {"value": "SSS", "label": "Sub Surface Scatter"},
+ {"value": "SSSRaw", "label": "Sub Surface Scatter Raw"},
+ {
+ "value": "TotalDiffuseLightingRaw",
+ "label": "Total Diffuse Lighting Raw"
+ },
+ {
+ "value": "TotalTransLightingRaw",
+ "label": "Total Translucency Filter"
+ },
+ {"value": "TransTint", "label": "Translucency Filter"},
+ {"value": "TransGIRaw", "label": "Translucency Lighting Raw"},
+ {"value": "VolumeFogEmission", "label": "Volume Fog Emission"},
+ {"value": "VolumeFogTint", "label": "Volume Fog Tint"},
+ {"value": "VolumeLighting", "label": "Volume Lighting"},
+ {"value": "P", "label": "World Position"},
+ ]
+
+
+class AdditionalOptionsModel(BaseSettingsModel):
+ """Additional Option"""
+ _layout = "compact"
+
+ attribute: str = Field("", title="Attribute name")
+ value: str = Field("", title="Value")
+
+
+class ArnoldSettingsModel(BaseSettingsModel):
+ image_prefix: str = Field(title="Image prefix template")
+ image_format: str = Field(
+ enum_resolver=arnold_image_format_enum, title="Output Image Format")
+ multilayer_exr: bool = Field(title="Multilayer (exr)")
+ tiled: bool = Field(title="Tiled (tif, exr)")
+ aov_list: list[str] = Field(
+ default_factory=list,
+ enum_resolver=arnold_aov_list_enum,
+ title="AOVs to create"
+ )
+ additional_options: list[AdditionalOptionsModel] = Field(
+ default_factory=list,
+ title="Additional Arnold Options",
+ description=(
+ "Add additional options - put attribute and value, like AASamples"
+ )
+ )
+
+
+class VraySettingsModel(BaseSettingsModel):
+ image_prefix: str = Field(title="Image prefix template")
+ # engine was str because of JSON limitation (key must be string)
+ engine: str = Field(
+ enum_resolver=lambda: [
+ {"label": "V-Ray", "value": "1"},
+ {"label": "V-Ray GPU", "value": "2"}
+ ],
+ title="Production Engine"
+ )
+ image_format: str = Field(
+ enum_resolver=vray_image_output_enum,
+ title="Output Image Format"
+ )
+ aov_list: list[str] = Field(
+ default_factory=list,
+ enum_resolver=vray_aov_list_enum,
+ title="AOVs to create"
+ )
+ additional_options: list[AdditionalOptionsModel] = Field(
+ default_factory=list,
+ title="Additional Vray Options",
+ description=(
+ "Add additional options - put attribute and value,"
+ " like aaFilterSize"
+ )
+ )
+
+
+class RedshiftSettingsModel(BaseSettingsModel):
+ image_prefix: str = Field(title="Image prefix template")
+ # both engines are using the same enumerator,
+ # both were originally str because of JSON limitation.
+ primary_gi_engine: str = Field(
+ enum_resolver=redshift_engine_enum,
+ title="Primary GI Engine"
+ )
+ secondary_gi_engine: str = Field(
+ enum_resolver=redshift_engine_enum,
+ title="Secondary GI Engine"
+ )
+ image_format: str = Field(
+ enum_resolver=redshift_image_output_enum,
+ title="Output Image Format"
+ )
+ multilayer_exr: bool = Field(title="Multilayer (exr)")
+ force_combine: bool = Field(title="Force combine beauty and AOVs")
+ aov_list: list[str] = Field(
+ default_factory=list,
+ enum_resolver=redshift_aov_list_enum,
+ title="AOVs to create"
+ )
+ additional_options: list[AdditionalOptionsModel] = Field(
+ default_factory=list,
+ title="Additional Vray Options",
+ description=(
+ "Add additional options - put attribute and value,"
+ " like reflectionMaxTraceDepth"
+ )
+ )
+
+
+def renderman_display_filters():
+ return [
+ "PxrBackgroundDisplayFilter",
+ "PxrCopyAOVDisplayFilter",
+ "PxrEdgeDetect",
+ "PxrFilmicTonemapperDisplayFilter",
+ "PxrGradeDisplayFilter",
+ "PxrHalfBufferErrorFilter",
+ "PxrImageDisplayFilter",
+ "PxrLightSaturation",
+ "PxrShadowDisplayFilter",
+ "PxrStylizedHatching",
+ "PxrStylizedLines",
+ "PxrStylizedToon",
+ "PxrWhitePointDisplayFilter"
+ ]
+
+
+def renderman_sample_filters_enum():
+ return [
+ "PxrBackgroundSampleFilter",
+ "PxrCopyAOVSampleFilter",
+ "PxrCryptomatte",
+ "PxrFilmicTonemapperSampleFilter",
+ "PxrGradeSampleFilter",
+ "PxrShadowFilter",
+ "PxrWatermarkFilter",
+ "PxrWhitePointSampleFilter"
+ ]
+
+
+class RendermanSettingsModel(BaseSettingsModel):
+ image_prefix: str = Field(
+ "", title="Image prefix template")
+ image_dir: str = Field(
+ "", title="Image Output Directory")
+ display_filters: list[str] = Field(
+ default_factory=list,
+ title="Display Filters",
+ enum_resolver=renderman_display_filters
+ )
+ imageDisplay_dir: str = Field(
+ "", title="Image Display Filter Directory")
+ sample_filters: list[str] = Field(
+ default_factory=list,
+ title="Sample Filters",
+ enum_resolver=renderman_sample_filters_enum
+ )
+ cryptomatte_dir: str = Field(
+ "", title="Cryptomatte Output Directory")
+ watermark_dir: str = Field(
+ "", title="Watermark Filter Directory")
+ additional_options: list[AdditionalOptionsModel] = Field(
+ default_factory=list,
+ title="Additional Renderer Options"
+ )
+
+
+class RenderSettingsModel(BaseSettingsModel):
+ apply_render_settings: bool = Field(
+ title="Apply Render Settings on creation"
+ )
+ default_render_image_folder: str = Field(
+ title="Default render image folder"
+ )
+ enable_all_lights: bool = Field(
+ title="Include all lights in Render Setup Layers by default"
+ )
+ aov_separator: str = Field(
+ "underscore",
+ title="AOV Separator character",
+ enum_resolver=aov_separators_enum
+ )
+ reset_current_frame: bool = Field(
+ title="Reset Current Frame")
+ remove_aovs: bool = Field(
+ title="Remove existing AOVs")
+ arnold_renderer: ArnoldSettingsModel = Field(
+ default_factory=ArnoldSettingsModel,
+ title="Arnold Renderer")
+ vray_renderer: VraySettingsModel = Field(
+ default_factory=VraySettingsModel,
+ title="Vray Renderer")
+ redshift_renderer: RedshiftSettingsModel = Field(
+ default_factory=RedshiftSettingsModel,
+ title="Redshift Renderer")
+ renderman_renderer: RendermanSettingsModel = Field(
+ default_factory=RendermanSettingsModel,
+ title="Renderman Renderer")
+
+
+DEFAULT_RENDER_SETTINGS = {
+ "apply_render_settings": True,
+ "default_render_image_folder": "renders/maya",
+ "enable_all_lights": True,
+ "aov_separator": "underscore",
+ "reset_current_frame": False,
+ "remove_aovs": False,
+ "arnold_renderer": {
+ "image_prefix": "//_",
+ "image_format": "exr",
+ "multilayer_exr": True,
+ "tiled": True,
+ "aov_list": [],
+ "additional_options": []
+ },
+ "vray_renderer": {
+ "image_prefix": "//",
+ "engine": "1",
+ "image_format": "exr",
+ "aov_list": [],
+ "additional_options": []
+ },
+ "redshift_renderer": {
+ "image_prefix": "//",
+ "primary_gi_engine": "0",
+ "secondary_gi_engine": "0",
+ "image_format": "exr",
+ "multilayer_exr": True,
+ "force_combine": True,
+ "aov_list": [],
+ "additional_options": []
+ },
+ "renderman_renderer": {
+ "image_prefix": "{aov_separator}..",
+ "image_dir": "/",
+ "display_filters": [],
+ "imageDisplay_dir": "/{aov_separator}imageDisplayFilter..",
+ "sample_filters": [],
+ "cryptomatte_dir": "/{aov_separator}cryptomatte..",
+ "watermark_dir": "/{aov_separator}watermarkFilter..",
+ "additional_options": []
+ }
+}
diff --git a/server_addon/maya/server/settings/scriptsmenu.py b/server_addon/maya/server/settings/scriptsmenu.py
new file mode 100644
index 0000000000..82c1c2e53c
--- /dev/null
+++ b/server_addon/maya/server/settings/scriptsmenu.py
@@ -0,0 +1,43 @@
+from pydantic import Field
+
+from ayon_server.settings import BaseSettingsModel
+
+
+class ScriptsmenuSubmodel(BaseSettingsModel):
+ """Item Definition"""
+ _isGroup = True
+ type: str = Field(title="Type")
+ command: str = Field(title="Command")
+ sourcetype: str = Field(title="Source Type")
+ title: str = Field(title="Title")
+ tooltip: str = Field(title="Tooltip")
+ tags: list[str] = Field(default_factory=list, title="A list of tags")
+
+
+class ScriptsmenuModel(BaseSettingsModel):
+ _isGroup = True
+
+ name: str = Field(title="Menu Name")
+ definition: list[ScriptsmenuSubmodel] = Field(
+ default_factory=list,
+ title="Menu Definition",
+ description="Scriptmenu Items Definition"
+ )
+
+
+DEFAULT_SCRIPTSMENU_SETTINGS = {
+ "name": "OpenPype Tools",
+ "definition": [
+ {
+ "type": "action",
+ "command": "import openpype.hosts.maya.api.commands as op_cmds; op_cmds.edit_shader_definitions()",
+ "sourcetype": "python",
+ "title": "Edit shader name definitions",
+ "tooltip": "Edit shader name definitions used in validation and renaming.",
+ "tags": [
+ "pipeline",
+ "shader"
+ ]
+ }
+ ]
+}
diff --git a/server_addon/maya/server/settings/templated_workfile_settings.py b/server_addon/maya/server/settings/templated_workfile_settings.py
new file mode 100644
index 0000000000..ef81b31a07
--- /dev/null
+++ b/server_addon/maya/server/settings/templated_workfile_settings.py
@@ -0,0 +1,25 @@
+from pydantic import Field
+from ayon_server.settings import BaseSettingsModel, task_types_enum
+
+
+class WorkfileBuildProfilesModel(BaseSettingsModel):
+ _layout = "expanded"
+ task_types: list[str] = Field(
+ default_factory=list,
+ title="Task types",
+ enum_resolver=task_types_enum
+ )
+ task_names: list[str] = Field(default_factory=list, title="Task names")
+ path: str = Field("", title="Path to template")
+
+
+class TemplatedProfilesModel(BaseSettingsModel):
+ profiles: list[WorkfileBuildProfilesModel] = Field(
+ default_factory=list,
+ title="Profiles"
+ )
+
+
+DEFAULT_TEMPLATED_WORKFILE_SETTINGS = {
+ "profiles": []
+}
diff --git a/server_addon/maya/server/settings/workfile_build_settings.py b/server_addon/maya/server/settings/workfile_build_settings.py
new file mode 100644
index 0000000000..dc56d1a320
--- /dev/null
+++ b/server_addon/maya/server/settings/workfile_build_settings.py
@@ -0,0 +1,131 @@
+from pydantic import Field
+from ayon_server.settings import BaseSettingsModel, task_types_enum
+
+
+class ContextItemModel(BaseSettingsModel):
+ _layout = "expanded"
+ product_name_filters: list[str] = Field(
+ default_factory=list, title="Product name Filters")
+ product_types: list[str] = Field(
+ default_factory=list, title="Product types")
+ repre_names: list[str] = Field(
+ default_factory=list, title="Repre Names")
+ loaders: list[str] = Field(
+ default_factory=list, title="Loaders")
+
+
+class WorkfileSettingModel(BaseSettingsModel):
+ _layout = "expanded"
+ task_types: list[str] = Field(
+ default_factory=list,
+ enum_resolver=task_types_enum,
+ title="Task types")
+ tasks: list[str] = Field(
+ default_factory=list,
+ title="Task names")
+ current_context: list[ContextItemModel] = Field(
+ default_factory=list,
+ title="Current Context")
+ linked_assets: list[ContextItemModel] = Field(
+ default_factory=list,
+ title="Linked Assets")
+
+
+class ProfilesModel(BaseSettingsModel):
+ profiles: list[WorkfileSettingModel] = Field(
+ default_factory=list,
+ title="Profiles"
+ )
+
+
+DEFAULT_WORKFILE_SETTING = {
+ "profiles": [
+ {
+ "task_types": [],
+ "tasks": [
+ "Lighting"
+ ],
+ "current_context": [
+ {
+ "product_name_filters": [
+ ".+[Mm]ain"
+ ],
+ "product_types": [
+ "model"
+ ],
+ "repre_names": [
+ "abc",
+ "ma"
+ ],
+ "loaders": [
+ "ReferenceLoader"
+ ]
+ },
+ {
+ "product_name_filters": [],
+ "product_types": [
+ "animation",
+ "pointcache",
+ "proxyAbc"
+ ],
+ "repre_names": [
+ "abc"
+ ],
+ "loaders": [
+ "ReferenceLoader"
+ ]
+ },
+ {
+ "product_name_filters": [],
+ "product_types": [
+ "rendersetup"
+ ],
+ "repre_names": [
+ "json"
+ ],
+ "loaders": [
+ "RenderSetupLoader"
+ ]
+ },
+ {
+ "product_name_filters": [],
+ "product_types": [
+ "camera"
+ ],
+ "repre_names": [
+ "abc"
+ ],
+ "loaders": [
+ "ReferenceLoader"
+ ]
+ }
+ ],
+ "linked_assets": [
+ {
+ "product_name_filters": [],
+ "product_types": [
+ "sedress"
+ ],
+ "repre_names": [
+ "ma"
+ ],
+ "loaders": [
+ "ReferenceLoader"
+ ]
+ },
+ {
+ "product_name_filters": [],
+ "product_types": [
+ "ArnoldStandin"
+ ],
+ "repre_names": [
+ "ass"
+ ],
+ "loaders": [
+ "assLoader"
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/server_addon/maya/server/version.py b/server_addon/maya/server/version.py
new file mode 100644
index 0000000000..a242f0e757
--- /dev/null
+++ b/server_addon/maya/server/version.py
@@ -0,0 +1,3 @@
+# -*- coding: utf-8 -*-
+"""Package declaring addon version."""
+__version__ = "0.1.1"
diff --git a/server_addon/muster/server/__init__.py b/server_addon/muster/server/__init__.py
new file mode 100644
index 0000000000..2cb8943554
--- /dev/null
+++ b/server_addon/muster/server/__init__.py
@@ -0,0 +1,17 @@
+from typing import Type
+
+from ayon_server.addons import BaseServerAddon
+
+from .version import __version__
+from .settings import MusterSettings, DEFAULT_VALUES
+
+
+class MusterAddon(BaseServerAddon):
+ name = "muster"
+ version = __version__
+ title = "Muster"
+ settings_model: Type[MusterSettings] = MusterSettings
+
+ async def get_default_settings(self):
+ settings_model_cls = self.get_settings_model()
+ return settings_model_cls(**DEFAULT_VALUES)
diff --git a/server_addon/muster/server/settings.py b/server_addon/muster/server/settings.py
new file mode 100644
index 0000000000..e37c762870
--- /dev/null
+++ b/server_addon/muster/server/settings.py
@@ -0,0 +1,41 @@
+from pydantic import Field
+from ayon_server.settings import BaseSettingsModel
+
+
+class TemplatesMapping(BaseSettingsModel):
+ _layout = "compact"
+ name: str = Field(title="Name")
+ value: int = Field(title="mapping")
+
+
+class MusterSettings(BaseSettingsModel):
+ enabled: bool = True
+ MUSTER_REST_URL: str = Field(
+ "",
+ title="Muster Rest URL",
+ scope=["studio"],
+ )
+
+ templates_mapping: list[TemplatesMapping] = Field(
+ default_factory=list,
+ title="Templates mapping",
+ )
+
+
+DEFAULT_VALUES = {
+ "enabled": False,
+ "MUSTER_REST_URL": "http://127.0.0.1:9890",
+ "templates_mapping": [
+ {"name": "file_layers", "value": 7},
+ {"name": "mentalray", "value": 2},
+ {"name": "mentalray_sf", "value": 6},
+ {"name": "redshift", "value": 55},
+ {"name": "renderman", "value": 29},
+ {"name": "software", "value": 1},
+ {"name": "software_sf", "value": 5},
+ {"name": "turtle", "value": 10},
+ {"name": "vector", "value": 4},
+ {"name": "vray", "value": 37},
+ {"name": "ffmpeg", "value": 48}
+ ]
+}
diff --git a/server_addon/muster/server/version.py b/server_addon/muster/server/version.py
new file mode 100644
index 0000000000..485f44ac21
--- /dev/null
+++ b/server_addon/muster/server/version.py
@@ -0,0 +1 @@
+__version__ = "0.1.1"
diff --git a/server_addon/nuke/server/__init__.py b/server_addon/nuke/server/__init__.py
new file mode 100644
index 0000000000..032ceea5fb
--- /dev/null
+++ b/server_addon/nuke/server/__init__.py
@@ -0,0 +1,17 @@
+from typing import Type
+
+from ayon_server.addons import BaseServerAddon
+
+from .version import __version__
+from .settings import NukeSettings, DEFAULT_VALUES
+
+
+class NukeAddon(BaseServerAddon):
+ name = "nuke"
+ title = "Nuke"
+ version = __version__
+ settings_model: Type[NukeSettings] = NukeSettings
+
+ async def get_default_settings(self):
+ settings_model_cls = self.get_settings_model()
+ return settings_model_cls(**DEFAULT_VALUES)
diff --git a/server_addon/nuke/server/settings/__init__.py b/server_addon/nuke/server/settings/__init__.py
new file mode 100644
index 0000000000..1e58865395
--- /dev/null
+++ b/server_addon/nuke/server/settings/__init__.py
@@ -0,0 +1,10 @@
+from .main import (
+ NukeSettings,
+ DEFAULT_VALUES,
+)
+
+
+__all__ = (
+ "NukeSettings",
+ "DEFAULT_VALUES",
+)
diff --git a/server_addon/nuke/server/settings/common.py b/server_addon/nuke/server/settings/common.py
new file mode 100644
index 0000000000..f1bb46ff90
--- /dev/null
+++ b/server_addon/nuke/server/settings/common.py
@@ -0,0 +1,128 @@
+import json
+from pydantic import Field
+from ayon_server.exceptions import BadRequestException
+from ayon_server.settings import BaseSettingsModel
+from ayon_server.types import (
+ ColorRGBA_float,
+ ColorRGB_uint8
+)
+
+
+def validate_json_dict(value):
+ if not value.strip():
+ return "{}"
+ try:
+ converted_value = json.loads(value)
+ success = isinstance(converted_value, dict)
+ except json.JSONDecodeError:
+ success = False
+
+ if not success:
+ raise BadRequestException(
+ "Environment's can't be parsed as json object"
+ )
+ return value
+
+
+class Vector2d(BaseSettingsModel):
+ _layout = "compact"
+
+ x: float = Field(1.0, title="X")
+ y: float = Field(1.0, title="Y")
+
+
+class Vector3d(BaseSettingsModel):
+ _layout = "compact"
+
+ x: float = Field(1.0, title="X")
+ y: float = Field(1.0, title="Y")
+ z: float = Field(1.0, title="Z")
+
+
+def formatable_knob_type_enum():
+ return [
+ {"value": "text", "label": "Text"},
+ {"value": "number", "label": "Number"},
+ {"value": "decimal_number", "label": "Decimal number"},
+ {"value": "2d_vector", "label": "2D vector"},
+ # "3D vector"
+ ]
+
+
+class Formatable(BaseSettingsModel):
+ _layout = "compact"
+
+ template: str = Field(
+ "",
+ placeholder="""{{key}} or {{key}};{{key}}""",
+ title="Template"
+ )
+ to_type: str = Field(
+ "Text",
+ title="To Knob type",
+ enum_resolver=formatable_knob_type_enum,
+ )
+
+
+knob_types_enum = [
+ {"value": "text", "label": "Text"},
+ {"value": "formatable", "label": "Formate from template"},
+ {"value": "color_gui", "label": "Color GUI"},
+ {"value": "boolean", "label": "Boolean"},
+ {"value": "number", "label": "Number"},
+ {"value": "decimal_number", "label": "Decimal number"},
+ {"value": "vector_2d", "label": "2D vector"},
+ {"value": "vector_3d", "label": "3D vector"},
+ {"value": "color", "label": "Color"},
+ {"value": "expression", "label": "Expression"}
+]
+
+
+class KnobModel(BaseSettingsModel):
+ """# TODO: new data structure
+ - v3 was having type, name, value but
+ ayon is not able to make it the same. Current model is
+ defining `type` as `text` and instead of `value` the key is `text`.
+ So if `type` is `boolean` then key is `boolean` (value).
+ """
+ _layout = "expanded"
+
+ type: str = Field(
+ title="Type",
+ description="Switch between different knob types",
+ enum_resolver=lambda: knob_types_enum,
+ conditionalEnum=True
+ )
+
+ name: str = Field(
+ title="Name",
+ placeholder="Name"
+ )
+ text: str = Field("", title="Value")
+ color_gui: ColorRGB_uint8 = Field(
+ (0, 0, 255),
+ title="RGB Uint8",
+ )
+ boolean: bool = Field(False, title="Value")
+ number: int = Field(0, title="Value")
+ decimal_number: float = Field(0.0, title="Value")
+ vector_2d: Vector2d = Field(
+ default_factory=Vector2d,
+ title="Value"
+ )
+ vector_3d: Vector3d = Field(
+ default_factory=Vector3d,
+ title="Value"
+ )
+ color: ColorRGBA_float = Field(
+ (0.0, 0.0, 1.0, 1.0),
+ title="RGBA Float"
+ )
+ formatable: Formatable = Field(
+ default_factory=Formatable,
+ title="Formatable"
+ )
+ expression: str = Field(
+ "",
+ title="Expression"
+ )
diff --git a/server_addon/nuke/server/settings/create_plugins.py b/server_addon/nuke/server/settings/create_plugins.py
new file mode 100644
index 0000000000..0bbae4ee77
--- /dev/null
+++ b/server_addon/nuke/server/settings/create_plugins.py
@@ -0,0 +1,223 @@
+from pydantic import validator, Field
+from ayon_server.settings import (
+ BaseSettingsModel,
+ ensure_unique_names
+)
+from .common import KnobModel
+
+
+def instance_attributes_enum():
+ """Return create write instance attributes."""
+ return [
+ {"value": "reviewable", "label": "Reviewable"},
+ {"value": "farm_rendering", "label": "Farm rendering"},
+ {"value": "use_range_limit", "label": "Use range limit"}
+ ]
+
+
+class PrenodeModel(BaseSettingsModel):
+ # TODO: missing in host api
+ # - good for `dependency`
+ name: str = Field(
+ title="Node name"
+ )
+
+ # TODO: `nodeclass` should be renamed to `nuke_node_class`
+ nodeclass: str = Field(
+ "",
+ title="Node class"
+ )
+ dependent: str = Field(
+ "",
+ title="Incoming dependency"
+ )
+
+ """# TODO: Changes in host api:
+ - Need complete rework of knob types in nuke integration.
+ - We could not support v3 style of settings.
+ """
+ knobs: list[KnobModel] = Field(
+ title="Knobs",
+ )
+
+ @validator("knobs")
+ def ensure_unique_names(cls, value):
+ """Ensure name fields within the lists have unique names."""
+ ensure_unique_names(value)
+ return value
+
+
+class CreateWriteRenderModel(BaseSettingsModel):
+ temp_rendering_path_template: str = Field(
+ title="Temporary rendering path template"
+ )
+ default_variants: list[str] = Field(
+ title="Default variants",
+ default_factory=list
+ )
+ instance_attributes: list[str] = Field(
+ default_factory=list,
+ enum_resolver=instance_attributes_enum,
+ title="Instance attributes"
+ )
+
+ """# TODO: Changes in host api:
+ - prenodes key was originally dict and now is list
+ (we could not support v3 style of settings)
+ """
+ prenodes: list[PrenodeModel] = Field(
+ title="Preceding nodes",
+ )
+
+ @validator("prenodes")
+ def ensure_unique_names(cls, value):
+ """Ensure name fields within the lists have unique names."""
+ ensure_unique_names(value)
+ return value
+
+
+class CreateWritePrerenderModel(BaseSettingsModel):
+ temp_rendering_path_template: str = Field(
+ title="Temporary rendering path template"
+ )
+ default_variants: list[str] = Field(
+ title="Default variants",
+ default_factory=list
+ )
+ instance_attributes: list[str] = Field(
+ default_factory=list,
+ enum_resolver=instance_attributes_enum,
+ title="Instance attributes"
+ )
+
+ """# TODO: Changes in host api:
+ - prenodes key was originally dict and now is list
+ (we could not support v3 style of settings)
+ """
+ prenodes: list[PrenodeModel] = Field(
+ title="Preceding nodes",
+ )
+
+ @validator("prenodes")
+ def ensure_unique_names(cls, value):
+ """Ensure name fields within the lists have unique names."""
+ ensure_unique_names(value)
+ return value
+
+
+class CreateWriteImageModel(BaseSettingsModel):
+ temp_rendering_path_template: str = Field(
+ title="Temporary rendering path template"
+ )
+ default_variants: list[str] = Field(
+ title="Default variants",
+ default_factory=list
+ )
+ instance_attributes: list[str] = Field(
+ default_factory=list,
+ enum_resolver=instance_attributes_enum,
+ title="Instance attributes"
+ )
+
+ """# TODO: Changes in host api:
+ - prenodes key was originally dict and now is list
+ (we could not support v3 style of settings)
+ """
+ prenodes: list[PrenodeModel] = Field(
+ title="Preceding nodes",
+ )
+
+ @validator("prenodes")
+ def ensure_unique_names(cls, value):
+ """Ensure name fields within the lists have unique names."""
+ ensure_unique_names(value)
+ return value
+
+
+class CreatorPluginsSettings(BaseSettingsModel):
+ CreateWriteRender: CreateWriteRenderModel = Field(
+ default_factory=CreateWriteRenderModel,
+ title="Create Write Render"
+ )
+ CreateWritePrerender: CreateWritePrerenderModel = Field(
+ default_factory=CreateWritePrerenderModel,
+ title="Create Write Prerender"
+ )
+ CreateWriteImage: CreateWriteImageModel = Field(
+ default_factory=CreateWriteImageModel,
+ title="Create Write Image"
+ )
+
+
+DEFAULT_CREATE_SETTINGS = {
+ "CreateWriteRender": {
+ "temp_rendering_path_template": "{work}/renders/nuke/{product[name]}/{product[name]}.{frame}.{ext}",
+ "default_variants": [
+ "Main",
+ "Mask"
+ ],
+ "instance_attributes": [
+ "reviewable",
+ "farm_rendering"
+ ],
+ "prenodes": [
+ {
+ "name": "Reformat01",
+ "nodeclass": "Reformat",
+ "dependent": "",
+ "knobs": [
+ {
+ "type": "text",
+ "name": "resize",
+ "text": "none"
+ },
+ {
+ "type": "boolean",
+ "name": "black_outside",
+ "boolean": True
+ }
+ ]
+ }
+ ]
+ },
+ "CreateWritePrerender": {
+ "temp_rendering_path_template": "{work}/renders/nuke/{product[name]}/{product[name]}.{frame}.{ext}",
+ "default_variants": [
+ "Key01",
+ "Bg01",
+ "Fg01",
+ "Branch01",
+ "Part01"
+ ],
+ "instance_attributes": [
+ "farm_rendering",
+ "use_range_limit"
+ ],
+ "prenodes": []
+ },
+ "CreateWriteImage": {
+ "temp_rendering_path_template": "{work}/renders/nuke/{product[name]}/{product[name]}.{ext}",
+ "default_variants": [
+ "StillFrame",
+ "MPFrame",
+ "LayoutFrame"
+ ],
+ "instance_attributes": [
+ "use_range_limit"
+ ],
+ "prenodes": [
+ {
+ "name": "FrameHold01",
+ "nodeclass": "FrameHold",
+ "dependent": "",
+ "knobs": [
+ {
+ "type": "expression",
+ "name": "first_frame",
+ "expression": "parent.first"
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/server_addon/nuke/server/settings/dirmap.py b/server_addon/nuke/server/settings/dirmap.py
new file mode 100644
index 0000000000..2da6d7bf60
--- /dev/null
+++ b/server_addon/nuke/server/settings/dirmap.py
@@ -0,0 +1,47 @@
+from pydantic import Field
+from ayon_server.settings import BaseSettingsModel
+
+
+class DirmapPathsSubmodel(BaseSettingsModel):
+ _layout = "compact"
+ source_path: list[str] = Field(
+ default_factory=list,
+ title="Source Paths"
+ )
+ destination_path: list[str] = Field(
+ default_factory=list,
+ title="Destination Paths"
+ )
+
+
+class DirmapSettings(BaseSettingsModel):
+ """Nuke color management project settings."""
+ _isGroup: bool = True
+
+ enabled: bool = Field(title="enabled")
+ paths: DirmapPathsSubmodel = Field(
+ default_factory=DirmapPathsSubmodel,
+ title="Dirmap Paths"
+ )
+
+
+"""# TODO:
+nuke is having originally implemented
+following data inputs:
+
+"nuke-dirmap": {
+ "enabled": false,
+ "paths": {
+ "source-path": [],
+ "destination-path": []
+ }
+}
+"""
+
+DEFAULT_DIRMAP_SETTINGS = {
+ "enabled": False,
+ "paths": {
+ "source_path": [],
+ "destination_path": []
+ }
+}
diff --git a/server_addon/nuke/server/settings/filters.py b/server_addon/nuke/server/settings/filters.py
new file mode 100644
index 0000000000..7e2702b3b7
--- /dev/null
+++ b/server_addon/nuke/server/settings/filters.py
@@ -0,0 +1,19 @@
+from pydantic import Field, validator
+from ayon_server.settings import BaseSettingsModel, ensure_unique_names
+
+
+class PublishGUIFilterItemModel(BaseSettingsModel):
+ _layout = "compact"
+ name: str = Field(title="Name")
+ value: bool = Field(True, title="Active")
+
+
+class PublishGUIFiltersModel(BaseSettingsModel):
+ _layout = "compact"
+ name: str = Field(title="Name")
+ value: list[PublishGUIFilterItemModel] = Field(default_factory=list)
+
+ @validator("value")
+ def validate_unique_outputs(cls, value):
+ ensure_unique_names(value)
+ return value
diff --git a/server_addon/nuke/server/settings/general.py b/server_addon/nuke/server/settings/general.py
new file mode 100644
index 0000000000..bcbb183952
--- /dev/null
+++ b/server_addon/nuke/server/settings/general.py
@@ -0,0 +1,42 @@
+from pydantic import Field
+from ayon_server.settings import BaseSettingsModel
+
+
+class MenuShortcut(BaseSettingsModel):
+ """Nuke general project settings."""
+
+ create: str = Field(
+ title="Create..."
+ )
+ publish: str = Field(
+ title="Publish..."
+ )
+ load: str = Field(
+ title="Load..."
+ )
+ manage: str = Field(
+ title="Manage..."
+ )
+ build_workfile: str = Field(
+ title="Build Workfile..."
+ )
+
+
+class GeneralSettings(BaseSettingsModel):
+ """Nuke general project settings."""
+
+ menu: MenuShortcut = Field(
+ default_factory=MenuShortcut,
+ title="Menu Shortcuts",
+ )
+
+
+DEFAULT_GENERAL_SETTINGS = {
+ "menu": {
+ "create": "ctrl+alt+c",
+ "publish": "ctrl+alt+p",
+ "load": "ctrl+alt+l",
+ "manage": "ctrl+alt+m",
+ "build_workfile": "ctrl+alt+b"
+ }
+}
diff --git a/server_addon/nuke/server/settings/gizmo.py b/server_addon/nuke/server/settings/gizmo.py
new file mode 100644
index 0000000000..4cdd614da8
--- /dev/null
+++ b/server_addon/nuke/server/settings/gizmo.py
@@ -0,0 +1,79 @@
+from pydantic import Field
+from ayon_server.settings import (
+ BaseSettingsModel,
+ MultiplatformPathModel,
+ MultiplatformPathListModel,
+)
+
+
+class SubGizmoItem(BaseSettingsModel):
+ title: str = Field(
+ title="Label"
+ )
+ sourcetype: str = Field(
+ title="Type of usage"
+ )
+ command: str = Field(
+ title="Python command"
+ )
+ icon: str = Field(
+ title="Icon Path"
+ )
+ shortcut: str = Field(
+ title="Hotkey"
+ )
+
+
+class GizmoDefinitionItem(BaseSettingsModel):
+ gizmo_toolbar_path: str = Field(
+ title="Gizmo Menu"
+ )
+ sub_gizmo_list: list[SubGizmoItem] = Field(
+ default_factory=list, title="Sub Gizmo List")
+
+
+class GizmoItem(BaseSettingsModel):
+ """Nuke gizmo item """
+
+ toolbar_menu_name: str = Field(
+ title="Toolbar Menu Name"
+ )
+ gizmo_source_dir: MultiplatformPathListModel = Field(
+ default_factory=MultiplatformPathListModel,
+ title="Gizmo Directory Path"
+ )
+ toolbar_icon_path: MultiplatformPathModel = Field(
+ default_factory=MultiplatformPathModel,
+ title="Toolbar Icon Path"
+ )
+ gizmo_definition: list[GizmoDefinitionItem] = Field(
+ default_factory=list, title="Gizmo Definition")
+
+
+DEFAULT_GIZMO_ITEM = {
+ "toolbar_menu_name": "OpenPype Gizmo",
+ "gizmo_source_dir": {
+ "windows": [],
+ "darwin": [],
+ "linux": []
+ },
+ "toolbar_icon_path": {
+ "windows": "",
+ "darwin": "",
+ "linux": ""
+ },
+ "gizmo_definition": [
+ {
+ "gizmo_toolbar_path": "/path/to/menu",
+ "sub_gizmo_list": [
+ {
+ "sourcetype": "python",
+ "title": "Gizmo Note",
+ "command": "nuke.nodes.StickyNote(label='You can create your own toolbar menu in the Nuke GizmoMenu of OpenPype')",
+ "icon": "",
+ "shortcut": ""
+ }
+ ]
+ }
+ ]
+}
diff --git a/server_addon/nuke/server/settings/imageio.py b/server_addon/nuke/server/settings/imageio.py
new file mode 100644
index 0000000000..b43017ef8b
--- /dev/null
+++ b/server_addon/nuke/server/settings/imageio.py
@@ -0,0 +1,410 @@
+from typing import Literal
+from pydantic import validator, Field
+from ayon_server.settings import (
+ BaseSettingsModel,
+ ensure_unique_names,
+)
+
+from .common import KnobModel
+
+
+class NodesModel(BaseSettingsModel):
+ """# TODO: This needs to be somehow labeled in settings panel
+ or at least it could show gist of configuration
+ """
+ _layout = "expanded"
+ plugins: list[str] = Field(
+ title="Used in plugins"
+ )
+ # TODO: rename `nukeNodeClass` to `nuke_node_class`
+ nukeNodeClass: str = Field(
+ title="Nuke Node Class",
+ )
+
+ """ # TODO: Need complete rework of knob types
+ in nuke integration. We could not support v3 style of settings.
+ """
+ knobs: list[KnobModel] = Field(
+ title="Knobs",
+ )
+
+ @validator("knobs")
+ def ensure_unique_names(cls, value):
+ """Ensure name fields within the lists have unique names."""
+ ensure_unique_names(value)
+ return value
+
+
+class NodesSetting(BaseSettingsModel):
+ # TODO: rename `requiredNodes` to `required_nodes`
+ requiredNodes: list[NodesModel] = Field(
+ title="Plugin required",
+ default_factory=list
+ )
+ # TODO: rename `overrideNodes` to `override_nodes`
+ overrideNodes: list[NodesModel] = Field(
+ title="Plugin's node overrides",
+ default_factory=list
+ )
+
+
+def ocio_configs_switcher_enum():
+ return [
+ {"value": "nuke-default", "label": "nuke-default"},
+ {"value": "spi-vfx", "label": "spi-vfx"},
+ {"value": "spi-anim", "label": "spi-anim"},
+ {"value": "aces_0.1.1", "label": "aces_0.1.1"},
+ {"value": "aces_0.7.1", "label": "aces_0.7.1"},
+ {"value": "aces_1.0.1", "label": "aces_1.0.1"},
+ {"value": "aces_1.0.3", "label": "aces_1.0.3"},
+ {"value": "aces_1.1", "label": "aces_1.1"},
+ {"value": "aces_1.2", "label": "aces_1.2"},
+ {"value": "aces_1.3", "label": "aces_1.3"},
+ {"value": "custom", "label": "custom"}
+ ]
+
+
+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"
+ )
+
+ OCIO_config: str = Field(
+ title="OpenColorIO Config",
+ description="Switch between OCIO configs",
+ enum_resolver=ocio_configs_switcher_enum,
+ conditionalEnum=True
+ )
+
+ workingSpaceLUT: str = Field(
+ title="Working Space"
+ )
+ 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):
+ _layout = "expanded"
+
+ regex: str = Field("", title="Regex expression")
+ colorspace: str = Field("", title="Colorspace")
+
+
+class RegexInputsModel(BaseSettingsModel):
+ inputs: list[ReadColorspaceRulesItems] = Field(
+ default_factory=list,
+ title="Inputs"
+ )
+
+
+class ViewProcessModel(BaseSettingsModel):
+ viewerProcess: str = Field(
+ title="Viewer Process Name"
+ )
+
+
+class ImageIOConfigModel(BaseSettingsModel):
+ override_global_config: bool = Field(
+ False,
+ title="Override global OCIO config"
+ )
+ filepath: list[str] = Field(
+ default_factory=list,
+ title="Config path"
+ )
+
+
+class ImageIOFileRuleModel(BaseSettingsModel):
+ name: str = Field("", title="Rule name")
+ pattern: str = Field("", title="Regex pattern")
+ colorspace: str = Field("", title="Colorspace name")
+ ext: str = Field("", title="File extension")
+
+
+class ImageIOFileRulesModel(BaseSettingsModel):
+ activate_host_rules: bool = Field(False)
+ rules: list[ImageIOFileRuleModel] = Field(
+ default_factory=list,
+ title="Rules"
+ )
+
+ @validator("rules")
+ def validate_unique_outputs(cls, value):
+ ensure_unique_names(value)
+ return value
+
+
+class ImageIOSettings(BaseSettingsModel):
+ """Nuke color management project settings. """
+ _isGroup: bool = True
+
+ """# TODO: enhance settings with host api:
+ to restruture settings for simplification.
+
+ now: nuke/imageio/viewer/viewerProcess
+ future: nuke/imageio/viewer
+ """
+ activate_host_color_management: bool = Field(
+ True, title="Enable Color Management")
+ ocio_config: ImageIOConfigModel = Field(
+ default_factory=ImageIOConfigModel,
+ title="OCIO config"
+ )
+ file_rules: ImageIOFileRulesModel = Field(
+ default_factory=ImageIOFileRulesModel,
+ title="File Rules"
+ )
+ viewer: ViewProcessModel = Field(
+ default_factory=ViewProcessModel,
+ title="Viewer",
+ description="""Viewer profile is used during
+ Creation of new viewer node at knob viewerProcess"""
+ )
+
+ """# TODO: enhance settings with host api:
+ to restruture settings for simplification.
+
+ now: nuke/imageio/baking/viewerProcess
+ future: nuke/imageio/baking
+ """
+ baking: ViewProcessModel = Field(
+ default_factory=ViewProcessModel,
+ title="Baking",
+ description="""Baking profile is used during
+ publishing baked colorspace data at knob viewerProcess"""
+ )
+
+ workfile: WorkfileColorspaceSettings = Field(
+ default_factory=WorkfileColorspaceSettings,
+ title="Workfile"
+ )
+
+ nodes: NodesSetting = Field(
+ default_factory=NodesSetting,
+ title="Nodes"
+ )
+ """# TODO: enhance settings with host api:
+ - old settings are using `regexInputs` key but we
+ need to rename to `regex_inputs`
+ - no need for `inputs` middle part. It can stay
+ directly on `regex_inputs`
+ """
+ regexInputs: RegexInputsModel = Field(
+ default_factory=RegexInputsModel,
+ title="Assign colorspace to read nodes via rules"
+ )
+
+
+DEFAULT_IMAGEIO_SETTINGS = {
+ "viewer": {
+ "viewerProcess": "sRGB"
+ },
+ "baking": {
+ "viewerProcess": "rec709"
+ },
+ "workfile": {
+ "colorManagement": "Nuke",
+ "OCIO_config": "nuke-default",
+ "workingSpaceLUT": "linear",
+ "monitorLut": "sRGB",
+ "int8Lut": "sRGB",
+ "int16Lut": "sRGB",
+ "logLut": "Cineon",
+ "floatLut": "linear"
+ },
+ "nodes": {
+ "requiredNodes": [
+ {
+ "plugins": [
+ "CreateWriteRender"
+ ],
+ "nukeNodeClass": "Write",
+ "knobs": [
+ {
+ "type": "text",
+ "name": "file_type",
+ "text": "exr"
+ },
+ {
+ "type": "text",
+ "name": "datatype",
+ "text": "16 bit half"
+ },
+ {
+ "type": "text",
+ "name": "compression",
+ "text": "Zip (1 scanline)"
+ },
+ {
+ "type": "boolean",
+ "name": "autocrop",
+ "boolean": True
+ },
+ {
+ "type": "color_gui",
+ "name": "tile_color",
+ "color_gui": [
+ 186,
+ 35,
+ 35
+ ]
+ },
+ {
+ "type": "text",
+ "name": "channels",
+ "text": "rgb"
+ },
+ {
+ "type": "text",
+ "name": "colorspace",
+ "text": "linear"
+ },
+ {
+ "type": "boolean",
+ "name": "create_directories",
+ "boolean": True
+ }
+ ]
+ },
+ {
+ "plugins": [
+ "CreateWritePrerender"
+ ],
+ "nukeNodeClass": "Write",
+ "knobs": [
+ {
+ "type": "text",
+ "name": "file_type",
+ "text": "exr"
+ },
+ {
+ "type": "text",
+ "name": "datatype",
+ "text": "16 bit half"
+ },
+ {
+ "type": "text",
+ "name": "compression",
+ "text": "Zip (1 scanline)"
+ },
+ {
+ "type": "boolean",
+ "name": "autocrop",
+ "boolean": True
+ },
+ {
+ "type": "color_gui",
+ "name": "tile_color",
+ "color_gui": [
+ 171,
+ 171,
+ 10
+ ]
+ },
+ {
+ "type": "text",
+ "name": "channels",
+ "text": "rgb"
+ },
+ {
+ "type": "text",
+ "name": "colorspace",
+ "text": "linear"
+ },
+ {
+ "type": "boolean",
+ "name": "create_directories",
+ "boolean": True
+ }
+ ]
+ },
+ {
+ "plugins": [
+ "CreateWriteImage"
+ ],
+ "nukeNodeClass": "Write",
+ "knobs": [
+ {
+ "type": "text",
+ "name": "file_type",
+ "text": "tiff"
+ },
+ {
+ "type": "text",
+ "name": "datatype",
+ "text": "16 bit"
+ },
+ {
+ "type": "text",
+ "name": "compression",
+ "text": "Deflate"
+ },
+ {
+ "type": "color_gui",
+ "name": "tile_color",
+ "color_gui": [
+ 56,
+ 162,
+ 7
+ ]
+ },
+ {
+ "type": "text",
+ "name": "channels",
+ "text": "rgb"
+ },
+ {
+ "type": "text",
+ "name": "colorspace",
+ "text": "sRGB"
+ },
+ {
+ "type": "boolean",
+ "name": "create_directories",
+ "boolean": True
+ }
+ ]
+ }
+ ],
+ "overrideNodes": []
+ },
+ "regexInputs": {
+ "inputs": [
+ {
+ "regex": "(beauty).*(?=.exr)",
+ "colorspace": "linear"
+ }
+ ]
+ }
+}
diff --git a/server_addon/nuke/server/settings/loader_plugins.py b/server_addon/nuke/server/settings/loader_plugins.py
new file mode 100644
index 0000000000..6db381bffb
--- /dev/null
+++ b/server_addon/nuke/server/settings/loader_plugins.py
@@ -0,0 +1,80 @@
+from pydantic import Field
+from ayon_server.settings import BaseSettingsModel
+
+
+class LoadImageModel(BaseSettingsModel):
+ enabled: bool = Field(
+ title="Enabled"
+ )
+ """# TODO: v3 api used `_representation`
+ New api is hiding it so it had to be renamed
+ to `representations_include`
+ """
+ representations_include: list[str] = Field(
+ default_factory=list,
+ title="Include representations"
+ )
+
+ node_name_template: str = Field(
+ title="Read node name template"
+ )
+
+
+class LoadClipOptionsModel(BaseSettingsModel):
+ start_at_workfile: bool = Field(
+ title="Start at workfile's start frame"
+ )
+ add_retime: bool = Field(
+ title="Add retime"
+ )
+
+
+class LoadClipModel(BaseSettingsModel):
+ enabled: bool = Field(
+ title="Enabled"
+ )
+ """# TODO: v3 api used `_representation`
+ New api is hiding it so it had to be renamed
+ to `representations_include`
+ """
+ representations_include: list[str] = Field(
+ default_factory=list,
+ title="Include representations"
+ )
+
+ node_name_template: str = Field(
+ title="Read node name template"
+ )
+ options_defaults: LoadClipOptionsModel = Field(
+ default_factory=LoadClipOptionsModel,
+ title="Loader option defaults"
+ )
+
+
+class LoaderPuginsModel(BaseSettingsModel):
+ LoadImage: LoadImageModel = Field(
+ default_factory=LoadImageModel,
+ title="Load Image"
+ )
+ LoadClip: LoadClipModel = Field(
+ default_factory=LoadClipModel,
+ title="Load Clip"
+ )
+
+
+DEFAULT_LOADER_PLUGINS_SETTINGS = {
+ "LoadImage": {
+ "enabled": True,
+ "representations_include": [],
+ "node_name_template": "{class_name}_{ext}"
+ },
+ "LoadClip": {
+ "enabled": True,
+ "representations_include": [],
+ "node_name_template": "{class_name}_{ext}",
+ "options_defaults": {
+ "start_at_workfile": True,
+ "add_retime": True
+ }
+ }
+}
diff --git a/server_addon/nuke/server/settings/main.py b/server_addon/nuke/server/settings/main.py
new file mode 100644
index 0000000000..4687d48ac9
--- /dev/null
+++ b/server_addon/nuke/server/settings/main.py
@@ -0,0 +1,128 @@
+from pydantic import validator, Field
+
+from ayon_server.settings import (
+ BaseSettingsModel,
+ ensure_unique_names
+)
+
+from .general import (
+ GeneralSettings,
+ DEFAULT_GENERAL_SETTINGS
+)
+from .imageio import (
+ ImageIOSettings,
+ DEFAULT_IMAGEIO_SETTINGS
+)
+from .dirmap import (
+ DirmapSettings,
+ DEFAULT_DIRMAP_SETTINGS
+)
+from .scriptsmenu import (
+ ScriptsmenuSettings,
+ DEFAULT_SCRIPTSMENU_SETTINGS
+)
+from .gizmo import (
+ GizmoItem,
+ DEFAULT_GIZMO_ITEM
+)
+from .create_plugins import (
+ CreatorPluginsSettings,
+ DEFAULT_CREATE_SETTINGS
+)
+from .publish_plugins import (
+ PublishPuginsModel,
+ DEFAULT_PUBLISH_PLUGIN_SETTINGS
+)
+from .loader_plugins import (
+ LoaderPuginsModel,
+ DEFAULT_LOADER_PLUGINS_SETTINGS
+)
+from .workfile_builder import (
+ WorkfileBuilderModel,
+ DEFAULT_WORKFILE_BUILDER_SETTINGS
+)
+from .templated_workfile_build import (
+ TemplatedWorkfileBuildModel
+)
+from .filters import PublishGUIFilterItemModel
+
+
+class NukeSettings(BaseSettingsModel):
+ """Nuke addon settings."""
+
+ general: GeneralSettings = Field(
+ default_factory=GeneralSettings,
+ title="General",
+ )
+
+ imageio: ImageIOSettings = Field(
+ default_factory=ImageIOSettings,
+ title="Color Management (imageio)",
+ )
+ """# TODO: fix host api:
+ - rename `nuke-dirmap` to `dirmap` was inevitable
+ """
+ dirmap: DirmapSettings = Field(
+ default_factory=DirmapSettings,
+ title="Nuke Directory Mapping",
+ )
+
+ scriptsmenu: ScriptsmenuSettings = Field(
+ default_factory=ScriptsmenuSettings,
+ title="Scripts Menu Definition",
+ )
+
+ gizmo: list[GizmoItem] = Field(
+ default_factory=list, title="Gizmo Menu")
+
+ create: CreatorPluginsSettings = Field(
+ default_factory=CreatorPluginsSettings,
+ title="Creator Plugins",
+ )
+
+ publish: PublishPuginsModel = Field(
+ default_factory=PublishPuginsModel,
+ title="Publish Plugins",
+ )
+
+ load: LoaderPuginsModel = Field(
+ default_factory=LoaderPuginsModel,
+ title="Loader Plugins",
+ )
+
+ workfile_builder: WorkfileBuilderModel = Field(
+ default_factory=WorkfileBuilderModel,
+ title="Workfile Builder",
+ )
+
+ templated_workfile_build: TemplatedWorkfileBuildModel = Field(
+ title="Templated Workfile Build",
+ default_factory=TemplatedWorkfileBuildModel
+ )
+
+ filters: list[PublishGUIFilterItemModel] = Field(
+ default_factory=list
+ )
+
+ @validator("filters")
+ def ensure_unique_names(cls, value):
+ """Ensure name fields within the lists have unique names."""
+ ensure_unique_names(value)
+ return value
+
+
+DEFAULT_VALUES = {
+ "general": DEFAULT_GENERAL_SETTINGS,
+ "imageio": DEFAULT_IMAGEIO_SETTINGS,
+ "dirmap": DEFAULT_DIRMAP_SETTINGS,
+ "scriptsmenu": DEFAULT_SCRIPTSMENU_SETTINGS,
+ "gizmo": [DEFAULT_GIZMO_ITEM],
+ "create": DEFAULT_CREATE_SETTINGS,
+ "publish": DEFAULT_PUBLISH_PLUGIN_SETTINGS,
+ "load": DEFAULT_LOADER_PLUGINS_SETTINGS,
+ "workfile_builder": DEFAULT_WORKFILE_BUILDER_SETTINGS,
+ "templated_workfile_build": {
+ "profiles": []
+ },
+ "filters": []
+}
diff --git a/server_addon/nuke/server/settings/publish_plugins.py b/server_addon/nuke/server/settings/publish_plugins.py
new file mode 100644
index 0000000000..7e898f8c9a
--- /dev/null
+++ b/server_addon/nuke/server/settings/publish_plugins.py
@@ -0,0 +1,504 @@
+from pydantic import validator, Field
+from ayon_server.settings import (
+ BaseSettingsModel,
+ ensure_unique_names,
+ task_types_enum
+)
+from .common import KnobModel, validate_json_dict
+
+
+def nuke_render_publish_types_enum():
+ """Return all nuke render families available in creators."""
+ return [
+ {"value": "render", "label": "Render"},
+ {"value": "prerender", "label": "Prerender"},
+ {"value": "image", "label": "Image"}
+ ]
+
+
+def nuke_product_types_enum():
+ """Return all nuke families available in creators."""
+ return [
+ {"value": "nukenodes", "label": "Nukenodes"},
+ {"value": "model", "label": "Model"},
+ {"value": "camera", "label": "Camera"},
+ {"value": "gizmo", "label": "Gizmo"},
+ {"value": "source", "label": "Source"}
+ ] + nuke_render_publish_types_enum()
+
+
+class NodeModel(BaseSettingsModel):
+ # TODO: missing in host api
+ name: str = Field(
+ title="Node name"
+ )
+ # TODO: `nodeclass` rename to `nuke_node_class`
+ nodeclass: str = Field(
+ "",
+ title="Node class"
+ )
+ dependent: str = Field(
+ "",
+ title="Incoming dependency"
+ )
+ """# TODO: Changes in host api:
+ - Need complete rework of knob types in nuke integration.
+ - We could not support v3 style of settings.
+ """
+ knobs: list[KnobModel] = Field(
+ title="Knobs",
+ )
+
+ @validator("knobs")
+ def ensure_unique_names(cls, value):
+ """Ensure name fields within the lists have unique names."""
+ ensure_unique_names(value)
+ return value
+
+
+class ThumbnailRepositionNodeModel(BaseSettingsModel):
+ node_class: str = Field(title="Node class")
+ knobs: list[KnobModel] = Field(title="Knobs", default_factory=list)
+
+ @validator("knobs")
+ def ensure_unique_names(cls, value):
+ """Ensure name fields within the lists have unique names."""
+ ensure_unique_names(value)
+ return value
+
+
+class CollectInstanceDataModel(BaseSettingsModel):
+ sync_workfile_version_on_product_types: list[str] = Field(
+ default_factory=list,
+ enum_resolver=nuke_product_types_enum,
+ title="Sync workfile versions for familes"
+ )
+
+
+class OptionalPluginModel(BaseSettingsModel):
+ enabled: bool = Field(True)
+ optional: bool = Field(title="Optional")
+ active: bool = Field(title="Active")
+
+
+class ValidateKnobsModel(BaseSettingsModel):
+ enabled: bool = Field(title="Enabled")
+ knobs: str = Field(
+ "{}",
+ title="Knobs",
+ widget="textarea",
+ )
+
+ @validator("knobs")
+ def validate_json(cls, value):
+ return validate_json_dict(value)
+
+
+class ExtractThumbnailModel(BaseSettingsModel):
+ enabled: bool = Field(title="Enabled")
+ use_rendered: bool = Field(title="Use rendered images")
+ bake_viewer_process: bool = Field(title="Bake view process")
+ bake_viewer_input_process: bool = Field(title="Bake viewer input process")
+ """# TODO: needs to rewrite from v3 to ayon
+ - `nodes` in v3 was dict but now `prenodes` is list of dict
+ - also later `nodes` should be `prenodes`
+ """
+
+ nodes: list[NodeModel] = Field(
+ title="Nodes (deprecated)"
+ )
+ reposition_nodes: list[ThumbnailRepositionNodeModel] = Field(
+ title="Reposition nodes",
+ default_factory=list
+ )
+
+
+class ExtractReviewDataModel(BaseSettingsModel):
+ enabled: bool = Field(title="Enabled")
+
+
+class ExtractReviewDataLutModel(BaseSettingsModel):
+ enabled: bool = Field(title="Enabled")
+
+
+class BakingStreamFilterModel(BaseSettingsModel):
+ task_types: list[str] = Field(
+ default_factory=list,
+ title="Task types",
+ enum_resolver=task_types_enum
+ )
+ product_types: list[str] = Field(
+ default_factory=list,
+ enum_resolver=nuke_render_publish_types_enum,
+ title="Sync workfile versions for familes"
+ )
+ product_names: list[str] = Field(
+ default_factory=list, title="Product names")
+
+
+class ReformatNodesRepositionNodes(BaseSettingsModel):
+ node_class: str = Field(title="Node class")
+ knobs: list[KnobModel] = Field(
+ default_factory=list,
+ title="Node knobs")
+
+
+class ReformatNodesConfigModel(BaseSettingsModel):
+ """Only reposition nodes supported.
+
+ You can add multiple reformat nodes and set their knobs.
+ Order of reformat nodes is important. First reformat node will
+ be applied first and last reformat node will be applied last.
+ """
+ enabled: bool = Field(False)
+ reposition_nodes: list[ReformatNodesRepositionNodes] = Field(
+ default_factory=list,
+ title="Reposition knobs"
+ )
+
+
+class BakingStreamModel(BaseSettingsModel):
+ name: str = Field(title="Output name")
+ filter: BakingStreamFilterModel = Field(
+ title="Filter", default_factory=BakingStreamFilterModel)
+ read_raw: bool = Field(title="Read raw switch")
+ viewer_process_override: str = Field(title="Viewer process override")
+ bake_viewer_process: bool = Field(title="Bake view process")
+ bake_viewer_input_process: bool = Field(title="Bake viewer input process")
+ reformat_nodes_config: ReformatNodesConfigModel = Field(
+ default_factory=ReformatNodesConfigModel,
+ title="Reformat Nodes")
+ extension: str = Field(title="File extension")
+ add_custom_tags: list[str] = Field(
+ title="Custom tags", default_factory=list)
+
+
+class ExtractReviewDataMovModel(BaseSettingsModel):
+ enabled: bool = Field(title="Enabled")
+ viewer_lut_raw: bool = Field(title="Viewer lut raw")
+ outputs: list[BakingStreamModel] = Field(
+ title="Baking streams"
+ )
+
+
+class FSubmissionNoteModel(BaseSettingsModel):
+ enabled: bool = Field(title="enabled")
+ template: str = Field(title="Template")
+
+
+class FSubmistingForModel(BaseSettingsModel):
+ enabled: bool = Field(title="enabled")
+ template: str = Field(title="Template")
+
+
+class FVFXScopeOfWorkModel(BaseSettingsModel):
+ enabled: bool = Field(title="enabled")
+ template: str = Field(title="Template")
+
+
+class ExctractSlateFrameParamModel(BaseSettingsModel):
+ f_submission_note: FSubmissionNoteModel = Field(
+ title="f_submission_note",
+ default_factory=FSubmissionNoteModel
+ )
+ f_submitting_for: FSubmistingForModel = Field(
+ title="f_submitting_for",
+ default_factory=FSubmistingForModel
+ )
+ f_vfx_scope_of_work: FVFXScopeOfWorkModel = Field(
+ title="f_vfx_scope_of_work",
+ default_factory=FVFXScopeOfWorkModel
+ )
+
+
+class ExtractSlateFrameModel(BaseSettingsModel):
+ viewer_lut_raw: bool = Field(title="Viewer lut raw")
+ """# TODO: v3 api different model:
+ - not possible to replicate v3 model:
+ {"name": [bool, str]}
+ - not it is:
+ {"name": {"enabled": bool, "template": str}}
+ """
+ key_value_mapping: ExctractSlateFrameParamModel = Field(
+ title="Key value mapping",
+ default_factory=ExctractSlateFrameParamModel
+ )
+
+
+class IncrementScriptVersionModel(BaseSettingsModel):
+ enabled: bool = Field(title="Enabled")
+ optional: bool = Field(title="Optional")
+ active: bool = Field(title="Active")
+
+
+class PublishPuginsModel(BaseSettingsModel):
+ CollectInstanceData: CollectInstanceDataModel = Field(
+ title="Collect Instance Version",
+ default_factory=CollectInstanceDataModel,
+ section="Collectors"
+ )
+ ValidateCorrectAssetName: OptionalPluginModel = Field(
+ title="Validate Correct Folder Name",
+ default_factory=OptionalPluginModel,
+ section="Validators"
+ )
+ ValidateContainers: OptionalPluginModel = Field(
+ title="Validate Containers",
+ default_factory=OptionalPluginModel
+ )
+ ValidateKnobs: ValidateKnobsModel = Field(
+ title="Validate Knobs",
+ default_factory=ValidateKnobsModel
+ )
+ ValidateOutputResolution: OptionalPluginModel = Field(
+ title="Validate Output Resolution",
+ default_factory=OptionalPluginModel
+ )
+ ValidateGizmo: OptionalPluginModel = Field(
+ title="Validate Gizmo",
+ default_factory=OptionalPluginModel
+ )
+ ValidateBackdrop: OptionalPluginModel = Field(
+ title="Validate Backdrop",
+ default_factory=OptionalPluginModel
+ )
+ ValidateScript: OptionalPluginModel = Field(
+ title="Validate Script",
+ default_factory=OptionalPluginModel
+ )
+ ExtractThumbnail: ExtractThumbnailModel = Field(
+ title="Extract Thumbnail",
+ default_factory=ExtractThumbnailModel,
+ section="Extractors"
+ )
+ ExtractReviewData: ExtractReviewDataModel = Field(
+ title="Extract Review Data",
+ default_factory=ExtractReviewDataModel
+ )
+ ExtractReviewDataLut: ExtractReviewDataLutModel = Field(
+ title="Extract Review Data Lut",
+ default_factory=ExtractReviewDataLutModel
+ )
+ ExtractReviewDataMov: ExtractReviewDataMovModel = Field(
+ title="Extract Review Data Mov",
+ default_factory=ExtractReviewDataMovModel
+ )
+ ExtractSlateFrame: ExtractSlateFrameModel = Field(
+ title="Extract Slate Frame",
+ default_factory=ExtractSlateFrameModel
+ )
+ # TODO: plugin should be renamed - `workfile` not `script`
+ IncrementScriptVersion: IncrementScriptVersionModel = Field(
+ title="Increment Workfile Version",
+ default_factory=IncrementScriptVersionModel,
+ section="Integrators"
+ )
+
+
+DEFAULT_PUBLISH_PLUGIN_SETTINGS = {
+ "CollectInstanceData": {
+ "sync_workfile_version_on_product_types": [
+ "nukenodes",
+ "camera",
+ "gizmo",
+ "source",
+ "render",
+ "write"
+ ]
+ },
+ "ValidateCorrectAssetName": {
+ "enabled": True,
+ "optional": True,
+ "active": True
+ },
+ "ValidateContainers": {
+ "enabled": True,
+ "optional": True,
+ "active": True
+ },
+ "ValidateKnobs": {
+ "enabled": False,
+ "knobs": "\n".join([
+ '{',
+ ' "render": {',
+ ' "review": true',
+ ' }',
+ '}'
+ ])
+ },
+ "ValidateOutputResolution": {
+ "enabled": True,
+ "optional": True,
+ "active": True
+ },
+ "ValidateGizmo": {
+ "enabled": True,
+ "optional": True,
+ "active": True
+ },
+ "ValidateBackdrop": {
+ "enabled": True,
+ "optional": True,
+ "active": True
+ },
+ "ValidateScript": {
+ "enabled": True,
+ "optional": True,
+ "active": True
+ },
+ "ExtractThumbnail": {
+ "enabled": True,
+ "use_rendered": True,
+ "bake_viewer_process": True,
+ "bake_viewer_input_process": True,
+ "nodes": [
+ {
+ "name": "Reformat01",
+ "nodeclass": "Reformat",
+ "dependency": "",
+ "knobs": [
+ {
+ "type": "text",
+ "name": "type",
+ "text": "to format"
+ },
+ {
+ "type": "text",
+ "name": "format",
+ "text": "HD_1080"
+ },
+ {
+ "type": "text",
+ "name": "filter",
+ "text": "Lanczos6"
+ },
+ {
+ "type": "boolean",
+ "name": "black_outside",
+ "boolean": True
+ },
+ {
+ "type": "boolean",
+ "name": "pbb",
+ "boolean": False
+ }
+ ]
+ }
+ ],
+ "reposition_nodes": [
+ {
+ "node_class": "Reformat",
+ "knobs": [
+ {
+ "type": "text",
+ "name": "type",
+ "text": "to format"
+ },
+ {
+ "type": "text",
+ "name": "format",
+ "text": "HD_1080"
+ },
+ {
+ "type": "text",
+ "name": "filter",
+ "text": "Lanczos6"
+ },
+ {
+ "type": "bool",
+ "name": "black_outside",
+ "boolean": True
+ },
+ {
+ "type": "bool",
+ "name": "pbb",
+ "boolean": False
+ }
+ ]
+ }
+ ]
+ },
+ "ExtractReviewData": {
+ "enabled": False
+ },
+ "ExtractReviewDataLut": {
+ "enabled": False
+ },
+ "ExtractReviewDataMov": {
+ "enabled": True,
+ "viewer_lut_raw": False,
+ "outputs": [
+ {
+ "name": "baking",
+ "filter": {
+ "task_types": [],
+ "product_types": [],
+ "product_names": []
+ },
+ "read_raw": False,
+ "viewer_process_override": "",
+ "bake_viewer_process": True,
+ "bake_viewer_input_process": True,
+ "reformat_nodes_config": {
+ "enabled": False,
+ "reposition_nodes": [
+ {
+ "node_class": "Reformat",
+ "knobs": [
+ {
+ "type": "text",
+ "name": "type",
+ "text": "to format"
+ },
+ {
+ "type": "text",
+ "name": "format",
+ "text": "HD_1080"
+ },
+ {
+ "type": "text",
+ "name": "filter",
+ "text": "Lanczos6"
+ },
+ {
+ "type": "bool",
+ "name": "black_outside",
+ "boolean": True
+ },
+ {
+ "type": "bool",
+ "name": "pbb",
+ "boolean": False
+ }
+ ]
+ }
+ ]
+ },
+ "extension": "mov",
+ "add_custom_tags": []
+ }
+ ]
+ },
+ "ExtractSlateFrame": {
+ "viewer_lut_raw": False,
+ "key_value_mapping": {
+ "f_submission_note": {
+ "enabled": True,
+ "template": "{comment}"
+ },
+ "f_submitting_for": {
+ "enabled": True,
+ "template": "{intent[value]}"
+ },
+ "f_vfx_scope_of_work": {
+ "enabled": False,
+ "template": ""
+ }
+ }
+ },
+ "IncrementScriptVersion": {
+ "enabled": True,
+ "optional": True,
+ "active": True
+ }
+}
diff --git a/server_addon/nuke/server/settings/scriptsmenu.py b/server_addon/nuke/server/settings/scriptsmenu.py
new file mode 100644
index 0000000000..9d1c32ebac
--- /dev/null
+++ b/server_addon/nuke/server/settings/scriptsmenu.py
@@ -0,0 +1,54 @@
+from pydantic import Field
+from ayon_server.settings import BaseSettingsModel
+
+
+class ScriptsmenuSubmodel(BaseSettingsModel):
+ """Item Definition"""
+ _isGroup = True
+
+ type: str = Field(title="Type")
+ command: str = Field(title="Command")
+ sourcetype: str = Field(title="Source Type")
+ title: str = Field(title="Title")
+ tooltip: str = Field(title="Tooltip")
+
+
+class ScriptsmenuSettings(BaseSettingsModel):
+ """Nuke script menu project settings."""
+ _isGroup = True
+
+ # TODO: in api rename key `name` to `menu_name`
+ name: str = Field(title="Menu Name")
+ definition: list[ScriptsmenuSubmodel] = Field(
+ default_factory=list,
+ title="Definition",
+ description="Scriptmenu Items Definition"
+ )
+
+
+DEFAULT_SCRIPTSMENU_SETTINGS = {
+ "name": "OpenPype Tools",
+ "definition": [
+ {
+ "type": "action",
+ "sourcetype": "python",
+ "title": "OpenPype Docs",
+ "command": "import webbrowser;webbrowser.open(url='https://openpype.io/docs/artist_hosts_nuke_tut')",
+ "tooltip": "Open the OpenPype Nuke user doc page"
+ },
+ {
+ "type": "action",
+ "sourcetype": "python",
+ "title": "Set Frame Start (Read Node)",
+ "command": "from openpype.hosts.nuke.startup.frame_setting_for_read_nodes import main;main();",
+ "tooltip": "Set frame start for read node(s)"
+ },
+ {
+ "type": "action",
+ "sourcetype": "python",
+ "title": "Set non publish output for Write Node",
+ "command": "from openpype.hosts.nuke.startup.custom_write_node import main;main();",
+ "tooltip": "Open the OpenPype Nuke user doc page"
+ }
+ ]
+}
diff --git a/server_addon/nuke/server/settings/templated_workfile_build.py b/server_addon/nuke/server/settings/templated_workfile_build.py
new file mode 100644
index 0000000000..e0245c8d06
--- /dev/null
+++ b/server_addon/nuke/server/settings/templated_workfile_build.py
@@ -0,0 +1,33 @@
+from pydantic import Field
+from ayon_server.settings import (
+ BaseSettingsModel,
+ task_types_enum,
+)
+
+
+class TemplatedWorkfileProfileModel(BaseSettingsModel):
+ task_types: list[str] = Field(
+ default_factory=list,
+ title="Task types",
+ enum_resolver=task_types_enum
+ )
+ task_names: list[str] = Field(
+ default_factory=list,
+ title="Task names"
+ )
+ path: str = Field(
+ title="Path to template"
+ )
+ keep_placeholder: bool = Field(
+ False,
+ title="Keep placeholders")
+ create_first_version: bool = Field(
+ True,
+ title="Create first version"
+ )
+
+
+class TemplatedWorkfileBuildModel(BaseSettingsModel):
+ profiles: list[TemplatedWorkfileProfileModel] = Field(
+ default_factory=list
+ )
diff --git a/server_addon/nuke/server/settings/workfile_builder.py b/server_addon/nuke/server/settings/workfile_builder.py
new file mode 100644
index 0000000000..ee67c7c16a
--- /dev/null
+++ b/server_addon/nuke/server/settings/workfile_builder.py
@@ -0,0 +1,72 @@
+from pydantic import Field
+from ayon_server.settings import (
+ BaseSettingsModel,
+ task_types_enum,
+ MultiplatformPathModel,
+)
+
+
+class CustomTemplateModel(BaseSettingsModel):
+ task_types: list[str] = Field(
+ default_factory=list,
+ title="Task types",
+ enum_resolver=task_types_enum
+ )
+ path: MultiplatformPathModel = Field(
+ default_factory=MultiplatformPathModel,
+ title="Gizmo Directory Path"
+ )
+
+
+class BuilderProfileItemModel(BaseSettingsModel):
+ product_name_filters: list[str] = Field(
+ default_factory=list,
+ title="Product name"
+ )
+ product_types: list[str] = Field(
+ default_factory=list,
+ title="Product types"
+ )
+ repre_names: list[str] = Field(
+ default_factory=list,
+ title="Representations"
+ )
+ loaders: list[str] = Field(
+ default_factory=list,
+ title="Loader plugins"
+ )
+
+
+class BuilderProfileModel(BaseSettingsModel):
+ task_types: list[str] = Field(
+ default_factory=list,
+ title="Task types",
+ enum_resolver=task_types_enum
+ )
+ tasks: list[str] = Field(
+ default_factory=list,
+ title="Task names"
+ )
+ current_context: list[BuilderProfileItemModel] = Field(
+ title="Current context")
+ linked_assets: list[BuilderProfileItemModel] = Field(
+ title="Linked assets/shots")
+
+
+class WorkfileBuilderModel(BaseSettingsModel):
+ create_first_version: bool = Field(
+ title="Create first workfile")
+ custom_templates: list[CustomTemplateModel] = Field(
+ title="Custom templates")
+ builder_on_start: bool = Field(
+ title="Run Builder at first workfile")
+ profiles: list[BuilderProfileModel] = Field(
+ title="Builder profiles")
+
+
+DEFAULT_WORKFILE_BUILDER_SETTINGS = {
+ "create_first_version": False,
+ "custom_templates": [],
+ "builder_on_start": False,
+ "profiles": []
+}
diff --git a/server_addon/nuke/server/version.py b/server_addon/nuke/server/version.py
new file mode 100644
index 0000000000..485f44ac21
--- /dev/null
+++ b/server_addon/nuke/server/version.py
@@ -0,0 +1 @@
+__version__ = "0.1.1"
diff --git a/server_addon/client/pyproject.toml b/server_addon/openpype/client/pyproject.toml
similarity index 100%
rename from server_addon/client/pyproject.toml
rename to server_addon/openpype/client/pyproject.toml
diff --git a/server_addon/server/__init__.py b/server_addon/openpype/server/__init__.py
similarity index 100%
rename from server_addon/server/__init__.py
rename to server_addon/openpype/server/__init__.py
diff --git a/server_addon/photoshop/LICENSE b/server_addon/photoshop/LICENSE
new file mode 100644
index 0000000000..d645695673
--- /dev/null
+++ b/server_addon/photoshop/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/server_addon/photoshop/README.md b/server_addon/photoshop/README.md
new file mode 100644
index 0000000000..2d1e1c745c
--- /dev/null
+++ b/server_addon/photoshop/README.md
@@ -0,0 +1,4 @@
+Photoshp Addon
+===============
+
+Integration with Adobe Photoshop.
diff --git a/server_addon/photoshop/server/__init__.py b/server_addon/photoshop/server/__init__.py
new file mode 100644
index 0000000000..3a45f7a809
--- /dev/null
+++ b/server_addon/photoshop/server/__init__.py
@@ -0,0 +1,16 @@
+from ayon_server.addons import BaseServerAddon
+
+from .settings import PhotoshopSettings, DEFAULT_PHOTOSHOP_SETTING
+from .version import __version__
+
+
+class Photoshop(BaseServerAddon):
+ name = "photoshop"
+ title = "Photoshop"
+ version = __version__
+
+ settings_model = PhotoshopSettings
+
+ async def get_default_settings(self):
+ settings_model_cls = self.get_settings_model()
+ return settings_model_cls(**DEFAULT_PHOTOSHOP_SETTING)
diff --git a/server_addon/photoshop/server/settings/__init__.py b/server_addon/photoshop/server/settings/__init__.py
new file mode 100644
index 0000000000..9ae5764362
--- /dev/null
+++ b/server_addon/photoshop/server/settings/__init__.py
@@ -0,0 +1,10 @@
+from .main import (
+ PhotoshopSettings,
+ DEFAULT_PHOTOSHOP_SETTING,
+)
+
+
+__all__ = (
+ "PhotoshopSettings",
+ "DEFAULT_PHOTOSHOP_SETTING",
+)
diff --git a/server_addon/photoshop/server/settings/creator_plugins.py b/server_addon/photoshop/server/settings/creator_plugins.py
new file mode 100644
index 0000000000..2fe63a7e3a
--- /dev/null
+++ b/server_addon/photoshop/server/settings/creator_plugins.py
@@ -0,0 +1,79 @@
+from pydantic import Field
+
+from ayon_server.settings import BaseSettingsModel
+
+
+class CreateImagePluginModel(BaseSettingsModel):
+ enabled: bool = Field(True, title="Enabled")
+ active_on_create: bool = Field(True, title="Active by default")
+ mark_for_review: bool = Field(False, title="Review by default")
+ default_variants: list[str] = Field(
+ default_factory=list,
+ title="Default Variants"
+ )
+
+
+class AutoImageCreatorPluginModel(BaseSettingsModel):
+ enabled: bool = Field(False, title="Enabled")
+ active_on_create: bool = Field(True, title="Active by default")
+ mark_for_review: bool = Field(False, title="Review by default")
+ default_variant: str = Field("", title="Default Variants")
+
+
+class CreateReviewPlugin(BaseSettingsModel):
+ enabled: bool = Field(True, title="Enabled")
+ active_on_create: bool = Field(True, title="Active by default")
+ default_variant: str = Field("", title="Default Variants")
+
+
+class CreateWorkfilelugin(BaseSettingsModel):
+ enabled: bool = Field(True, title="Enabled")
+ active_on_create: bool = Field(True, title="Active by default")
+ default_variant: str = Field("", title="Default Variants")
+
+
+class PhotoshopCreatorPlugins(BaseSettingsModel):
+ ImageCreator: CreateImagePluginModel = Field(
+ title="Create Image",
+ default_factory=CreateImagePluginModel,
+ )
+ AutoImageCreator: AutoImageCreatorPluginModel = Field(
+ title="Create Flatten Image",
+ default_factory=AutoImageCreatorPluginModel,
+ )
+ ReviewCreator: CreateReviewPlugin = Field(
+ title="Create Review",
+ default_factory=CreateReviewPlugin,
+ )
+ WorkfileCreator: CreateWorkfilelugin = Field(
+ title="Create Workfile",
+ default_factory=CreateWorkfilelugin,
+ )
+
+
+DEFAULT_CREATE_SETTINGS = {
+ "ImageCreator": {
+ "enabled": True,
+ "active_on_create": True,
+ "mark_for_review": False,
+ "default_variants": [
+ "Main"
+ ]
+ },
+ "AutoImageCreator": {
+ "enabled": False,
+ "active_on_create": True,
+ "mark_for_review": False,
+ "default_variant": ""
+ },
+ "ReviewCreator": {
+ "enabled": True,
+ "active_on_create": True,
+ "default_variant": ""
+ },
+ "WorkfileCreator": {
+ "enabled": True,
+ "active_on_create": True,
+ "default_variant": "Main"
+ }
+}
diff --git a/server_addon/photoshop/server/settings/imageio.py b/server_addon/photoshop/server/settings/imageio.py
new file mode 100644
index 0000000000..56b7f2fa32
--- /dev/null
+++ b/server_addon/photoshop/server/settings/imageio.py
@@ -0,0 +1,64 @@
+from pydantic import Field, validator
+from ayon_server.settings import BaseSettingsModel
+from ayon_server.settings.validators import ensure_unique_names
+
+
+class ImageIOConfigModel(BaseSettingsModel):
+ override_global_config: bool = Field(
+ False,
+ title="Override global OCIO config"
+ )
+ filepath: list[str] = Field(
+ default_factory=list,
+ title="Config path"
+ )
+
+
+class ImageIOFileRuleModel(BaseSettingsModel):
+ name: str = Field("", title="Rule name")
+ pattern: str = Field("", title="Regex pattern")
+ colorspace: str = Field("", title="Colorspace name")
+ ext: str = Field("", title="File extension")
+
+
+class ImageIOFileRulesModel(BaseSettingsModel):
+ activate_host_rules: bool = Field(False)
+ rules: list[ImageIOFileRuleModel] = Field(
+ default_factory=list,
+ title="Rules"
+ )
+
+ @validator("rules")
+ def validate_unique_outputs(cls, value):
+ ensure_unique_names(value)
+ return value
+
+
+class ImageIORemappingRulesModel(BaseSettingsModel):
+ host_native_name: str = Field(
+ title="Application native colorspace name"
+ )
+ ocio_name: str = Field(title="OCIO colorspace name")
+
+
+class ImageIORemappingModel(BaseSettingsModel):
+ rules: list[ImageIORemappingRulesModel] = Field(
+ default_factory=list)
+
+
+class PhotoshopImageIOModel(BaseSettingsModel):
+ activate_host_color_management: bool = Field(
+ True, title="Enable Color Management"
+ )
+ remapping: ImageIORemappingModel = Field(
+ title="Remapping colorspace names",
+ default_factory=ImageIORemappingModel
+ )
+ ocio_config: ImageIOConfigModel = Field(
+ default_factory=ImageIOConfigModel,
+ title="OCIO config"
+ )
+ file_rules: ImageIOFileRulesModel = Field(
+ default_factory=ImageIOFileRulesModel,
+ title="File Rules"
+ )
diff --git a/server_addon/photoshop/server/settings/main.py b/server_addon/photoshop/server/settings/main.py
new file mode 100644
index 0000000000..ae7705b3db
--- /dev/null
+++ b/server_addon/photoshop/server/settings/main.py
@@ -0,0 +1,41 @@
+from pydantic import Field
+from ayon_server.settings import BaseSettingsModel
+
+from .imageio import PhotoshopImageIOModel
+from .creator_plugins import PhotoshopCreatorPlugins, DEFAULT_CREATE_SETTINGS
+from .publish_plugins import PhotoshopPublishPlugins, DEFAULT_PUBLISH_SETTINGS
+from .workfile_builder import WorkfileBuilderPlugin
+
+
+class PhotoshopSettings(BaseSettingsModel):
+ """Photoshop Project Settings."""
+
+ imageio: PhotoshopImageIOModel = Field(
+ default_factory=PhotoshopImageIOModel,
+ title="OCIO config"
+ )
+
+ create: PhotoshopCreatorPlugins = Field(
+ default_factory=PhotoshopCreatorPlugins,
+ title="Creator plugins"
+ )
+
+ publish: PhotoshopPublishPlugins = Field(
+ default_factory=PhotoshopPublishPlugins,
+ title="Publish plugins"
+ )
+
+ workfile_builder: WorkfileBuilderPlugin = Field(
+ default_factory=WorkfileBuilderPlugin,
+ title="Workfile Builder"
+ )
+
+
+DEFAULT_PHOTOSHOP_SETTING = {
+ "create": DEFAULT_CREATE_SETTINGS,
+ "publish": DEFAULT_PUBLISH_SETTINGS,
+ "workfile_builder": {
+ "create_first_version": False,
+ "custom_templates": []
+ }
+}
diff --git a/server_addon/photoshop/server/settings/publish_plugins.py b/server_addon/photoshop/server/settings/publish_plugins.py
new file mode 100644
index 0000000000..6bc72b4072
--- /dev/null
+++ b/server_addon/photoshop/server/settings/publish_plugins.py
@@ -0,0 +1,221 @@
+from pydantic import Field
+
+from ayon_server.settings import BaseSettingsModel
+
+
+create_flatten_image_enum = [
+ {"value": "flatten_with_images", "label": "Flatten with images"},
+ {"value": "flatten_only", "label": "Flatten only"},
+ {"value": "no", "label": "No"},
+]
+
+
+color_code_enum = [
+ {"value": "red", "label": "Red"},
+ {"value": "orange", "label": "Orange"},
+ {"value": "yellowColor", "label": "Yellow"},
+ {"value": "grain", "label": "Green"},
+ {"value": "blue", "label": "Blue"},
+ {"value": "violet", "label": "Violet"},
+ {"value": "gray", "label": "Gray"},
+]
+
+
+class ColorCodeMappings(BaseSettingsModel):
+ color_code: list[str] = Field(
+ title="Color codes for layers",
+ default_factory=list,
+ enum_resolver=lambda: color_code_enum,
+ )
+
+ layer_name_regex: list[str] = Field(
+ "",
+ title="Layer name regex"
+ )
+
+ product_type: str = Field(
+ "",
+ title="Resulting product type"
+ )
+
+ product_name_template: str = Field(
+ "",
+ title="Product name template"
+ )
+
+
+class ExtractedOptions(BaseSettingsModel):
+ tags: list[str] = Field(
+ title="Tags",
+ default_factory=list
+ )
+
+
+class CollectColorCodedInstancesPlugin(BaseSettingsModel):
+ """Set color for publishable layers, set its resulting product type
+ and template for product name. \n Can create flatten image from published
+ instances.
+ (Applicable only for remote publishing!)"""
+
+ enabled: bool = Field(True, title="Enabled")
+ create_flatten_image: str = Field(
+ "",
+ title="Create flatten image",
+ enum_resolver=lambda: create_flatten_image_enum,
+ )
+
+ flatten_product_type_template: str = Field(
+ "",
+ title="Subset template for flatten image"
+ )
+
+ color_code_mapping: list[ColorCodeMappings] = Field(
+ title="Color code mappings",
+ default_factory=ColorCodeMappings,
+ )
+
+
+class CollectReviewPlugin(BaseSettingsModel):
+ """Should review product be created"""
+ enabled: bool = Field(True, title="Enabled")
+
+
+class CollectVersionPlugin(BaseSettingsModel):
+ """Synchronize version for image and review instances by workfile version""" # noqa
+ enabled: bool = Field(True, title="Enabled")
+
+
+class ValidateContainersPlugin(BaseSettingsModel):
+ """Check that workfile contains latest version of loaded items""" # noqa
+ _isGroup = True
+ enabled: bool = True
+ optional: bool = Field(False, title="Optional")
+ active: bool = Field(True, title="Active")
+
+
+class ValidateNamingPlugin(BaseSettingsModel):
+ """Validate naming of products and layers""" # noqa
+ invalid_chars: str = Field(
+ '',
+ title="Regex pattern of invalid characters"
+ )
+
+ replace_char: str = Field(
+ '',
+ title="Replacement character"
+ )
+
+
+class ExtractImagePlugin(BaseSettingsModel):
+ """Currently only jpg and png are supported"""
+ formats: list[str] = Field(
+ title="Extract Formats",
+ default_factory=list,
+ )
+
+
+class ExtractReviewPlugin(BaseSettingsModel):
+ make_image_sequence: bool = Field(
+ False,
+ title="Make an image sequence instead of flatten image"
+ )
+
+ max_downscale_size: int = Field(
+ 8192,
+ title="Maximum size of sources for review",
+ description="FFMpeg can only handle limited resolution for creation of review and/or thumbnail", # noqa
+ gt=300, # greater than
+ le=16384, # less or equal
+ )
+
+ jpg_options: ExtractedOptions = Field(
+ title="Extracted jpg Options",
+ default_factory=ExtractedOptions
+ )
+
+ mov_options: ExtractedOptions = Field(
+ title="Extracted mov Options",
+ default_factory=ExtractedOptions
+ )
+
+
+class PhotoshopPublishPlugins(BaseSettingsModel):
+ CollectColorCodedInstances: CollectColorCodedInstancesPlugin = Field(
+ title="Collect Color Coded Instances",
+ default_factory=CollectColorCodedInstancesPlugin,
+ )
+ CollectReview: CollectReviewPlugin = Field(
+ title="Collect Review",
+ default_factory=CollectReviewPlugin,
+ )
+
+ CollectVersion: CollectVersionPlugin = Field(
+ title="Create Image",
+ default_factory=CollectVersionPlugin,
+ )
+
+ ValidateContainers: ValidateContainersPlugin = Field(
+ title="Validate Containers",
+ default_factory=ValidateContainersPlugin,
+ )
+
+ ValidateNaming: ValidateNamingPlugin = Field(
+ title="Validate naming of products and layers",
+ default_factory=ValidateNamingPlugin,
+ )
+
+ ExtractImage: ExtractImagePlugin = Field(
+ title="Extract Image",
+ default_factory=ExtractImagePlugin,
+ )
+
+ ExtractReview: ExtractReviewPlugin = Field(
+ title="Extract Review",
+ default_factory=ExtractReviewPlugin,
+ )
+
+
+DEFAULT_PUBLISH_SETTINGS = {
+ "CollectColorCodedInstances": {
+ "create_flatten_image": "no",
+ "flatten_product_type_template": "",
+ "color_code_mapping": []
+ },
+ "CollectReview": {
+ "enabled": True
+ },
+ "CollectVersion": {
+ "enabled": False
+ },
+ "ValidateContainers": {
+ "enabled": True,
+ "optional": True,
+ "active": True
+ },
+ "ValidateNaming": {
+ "invalid_chars": "[ \\\\/+\\*\\?\\(\\)\\[\\]\\{\\}:,;]",
+ "replace_char": "_"
+ },
+ "ExtractImage": {
+ "formats": [
+ "png",
+ "jpg"
+ ]
+ },
+ "ExtractReview": {
+ "make_image_sequence": False,
+ "max_downscale_size": 8192,
+ "jpg_options": {
+ "tags": [
+ "review",
+ "ftrackreview"
+ ]
+ },
+ "mov_options": {
+ "tags": [
+ "review",
+ "ftrackreview"
+ ]
+ }
+ }
+}
diff --git a/server_addon/photoshop/server/settings/workfile_builder.py b/server_addon/photoshop/server/settings/workfile_builder.py
new file mode 100644
index 0000000000..ec2ee136ad
--- /dev/null
+++ b/server_addon/photoshop/server/settings/workfile_builder.py
@@ -0,0 +1,41 @@
+from pydantic import Field
+from pathlib import Path
+
+from ayon_server.settings import BaseSettingsModel
+
+
+class PathsTemplate(BaseSettingsModel):
+ windows: Path = Field(
+ '',
+ title="Windows"
+ )
+ darwin: Path = Field(
+ '',
+ title="MacOS"
+ )
+ linux: Path = Field(
+ '',
+ title="Linux"
+ )
+
+
+class CustomBuilderTemplate(BaseSettingsModel):
+ task_types: list[str] = Field(
+ default_factory=list,
+ title="Task types",
+ )
+ template_path: PathsTemplate = Field(
+ default_factory=PathsTemplate
+ )
+
+
+class WorkfileBuilderPlugin(BaseSettingsModel):
+ _title = "Workfile Builder"
+ create_first_version: bool = Field(
+ False,
+ title="Create first workfile"
+ )
+
+ custom_templates: list[CustomBuilderTemplate] = Field(
+ default_factory=CustomBuilderTemplate
+ )
diff --git a/server_addon/photoshop/server/version.py b/server_addon/photoshop/server/version.py
new file mode 100644
index 0000000000..d4b9e2d7f3
--- /dev/null
+++ b/server_addon/photoshop/server/version.py
@@ -0,0 +1,3 @@
+# -*- coding: utf-8 -*-
+"""Package declaring addon version."""
+__version__ = "0.1.0"
diff --git a/server_addon/resolve/server/__init__.py b/server_addon/resolve/server/__init__.py
new file mode 100644
index 0000000000..a84180d0f5
--- /dev/null
+++ b/server_addon/resolve/server/__init__.py
@@ -0,0 +1,19 @@
+from typing import Type
+
+from ayon_server.addons import BaseServerAddon
+
+from .version import __version__
+from .settings import ResolveSettings, DEFAULT_VALUES
+
+
+class ResolveAddon(BaseServerAddon):
+ name = "resolve"
+ title = "DaVinci Resolve"
+ version = __version__
+ settings_model: Type[ResolveSettings] = ResolveSettings
+ frontend_scopes = {}
+ services = {}
+
+ async def get_default_settings(self):
+ settings_model_cls = self.get_settings_model()
+ return settings_model_cls(**DEFAULT_VALUES)
diff --git a/server_addon/resolve/server/imageio.py b/server_addon/resolve/server/imageio.py
new file mode 100644
index 0000000000..c2bfcd40d0
--- /dev/null
+++ b/server_addon/resolve/server/imageio.py
@@ -0,0 +1,64 @@
+from pydantic import Field, validator
+from ayon_server.settings import BaseSettingsModel
+from ayon_server.settings.validators import ensure_unique_names
+
+
+class ImageIOConfigModel(BaseSettingsModel):
+ override_global_config: bool = Field(
+ False,
+ title="Override global OCIO config"
+ )
+ filepath: list[str] = Field(
+ default_factory=list,
+ title="Config path"
+ )
+
+
+class ImageIOFileRuleModel(BaseSettingsModel):
+ name: str = Field("", title="Rule name")
+ pattern: str = Field("", title="Regex pattern")
+ colorspace: str = Field("", title="Colorspace name")
+ ext: str = Field("", title="File extension")
+
+
+class ImageIOFileRulesModel(BaseSettingsModel):
+ activate_host_rules: bool = Field(False)
+ rules: list[ImageIOFileRuleModel] = Field(
+ default_factory=list,
+ title="Rules"
+ )
+
+ @validator("rules")
+ def validate_unique_outputs(cls, value):
+ ensure_unique_names(value)
+ return value
+
+
+class ImageIORemappingRulesModel(BaseSettingsModel):
+ host_native_name: str = Field(
+ title="Application native colorspace name"
+ )
+ ocio_name: str = Field(title="OCIO colorspace name")
+
+
+class ImageIORemappingModel(BaseSettingsModel):
+ rules: list[ImageIORemappingRulesModel] = Field(
+ default_factory=list)
+
+
+class ResolveImageIOModel(BaseSettingsModel):
+ activate_host_color_management: bool = Field(
+ True, title="Enable Color Management"
+ )
+ remapping: ImageIORemappingModel = Field(
+ title="Remapping colorspace names",
+ default_factory=ImageIORemappingModel
+ )
+ ocio_config: ImageIOConfigModel = Field(
+ default_factory=ImageIOConfigModel,
+ title="OCIO config"
+ )
+ file_rules: ImageIOFileRulesModel = Field(
+ default_factory=ImageIOFileRulesModel,
+ title="File Rules"
+ )
diff --git a/server_addon/resolve/server/settings.py b/server_addon/resolve/server/settings.py
new file mode 100644
index 0000000000..326f6bea1e
--- /dev/null
+++ b/server_addon/resolve/server/settings.py
@@ -0,0 +1,114 @@
+from pydantic import Field
+from ayon_server.settings import BaseSettingsModel
+
+from .imageio import ResolveImageIOModel
+
+
+class CreateShotClipModels(BaseSettingsModel):
+ hierarchy: str = Field(
+ "{folder}/{sequence}",
+ title="Shot parent hierarchy",
+ section="Shot Hierarchy And Rename Settings"
+ )
+ clipRename: bool = Field(
+ True,
+ title="Rename clips"
+ )
+ clipName: str = Field(
+ "{track}{sequence}{shot}",
+ title="Clip name template"
+ )
+ countFrom: int = Field(
+ 10,
+ title="Count sequence from"
+ )
+ countSteps: int = Field(
+ 10,
+ title="Stepping number"
+ )
+
+ folder: str = Field(
+ "shots",
+ title="{folder}",
+ section="Shot Template Keywords"
+ )
+ episode: str = Field(
+ "ep01",
+ title="{episode}"
+ )
+ sequence: str = Field(
+ "sq01",
+ title="{sequence}"
+ )
+ track: str = Field(
+ "{_track_}",
+ title="{track}"
+ )
+ shot: str = Field(
+ "sh###",
+ title="{shot}"
+ )
+
+ vSyncOn: bool = Field(
+ False,
+ title="Enable Vertical Sync",
+ section="Vertical Synchronization Of Attributes"
+ )
+
+ workfileFrameStart: int = Field(
+ 1001,
+ title="Workfiles Start Frame",
+ section="Shot Attributes"
+ )
+ handleStart: int = Field(
+ 10,
+ title="Handle start (head)"
+ )
+ handleEnd: int = Field(
+ 10,
+ title="Handle end (tail)"
+ )
+
+
+class CreatorPuginsModel(BaseSettingsModel):
+ CreateShotClip: CreateShotClipModels = Field(
+ default_factory=CreateShotClipModels,
+ title="Create Shot Clip"
+ )
+
+
+class ResolveSettings(BaseSettingsModel):
+ launch_openpype_menu_on_start: bool = Field(
+ False, title="Launch OpenPype menu on start of Resolve"
+ )
+ imageio: ResolveImageIOModel = Field(
+ default_factory=ResolveImageIOModel,
+ title="Color Management (ImageIO)"
+ )
+ create: CreatorPuginsModel = Field(
+ default_factory=CreatorPuginsModel,
+ title="Creator plugins",
+ )
+
+
+DEFAULT_VALUES = {
+ "launch_openpype_menu_on_start": False,
+ "create": {
+ "CreateShotClip": {
+ "hierarchy": "{folder}/{sequence}",
+ "clipRename": True,
+ "clipName": "{track}{sequence}{shot}",
+ "countFrom": 10,
+ "countSteps": 10,
+ "folder": "shots",
+ "episode": "ep01",
+ "sequence": "sq01",
+ "track": "{_track_}",
+ "shot": "sh###",
+ "vSyncOn": False,
+ "workfileFrameStart": 1001,
+ "handleStart": 10,
+ "handleEnd": 10
+ }
+ }
+}
diff --git a/server_addon/resolve/server/version.py b/server_addon/resolve/server/version.py
new file mode 100644
index 0000000000..3dc1f76bc6
--- /dev/null
+++ b/server_addon/resolve/server/version.py
@@ -0,0 +1 @@
+__version__ = "0.1.0"
diff --git a/server_addon/royal_render/server/__init__.py b/server_addon/royal_render/server/__init__.py
new file mode 100644
index 0000000000..c5f0aafa00
--- /dev/null
+++ b/server_addon/royal_render/server/__init__.py
@@ -0,0 +1,17 @@
+from typing import Type
+
+from ayon_server.addons import BaseServerAddon
+
+from .version import __version__
+from .settings import RoyalRenderSettings, DEFAULT_VALUES
+
+
+class RoyalRenderAddon(BaseServerAddon):
+ name = "royalrender"
+ version = __version__
+ title = "Royal Render"
+ settings_model: Type[RoyalRenderSettings] = RoyalRenderSettings
+
+ async def get_default_settings(self):
+ settings_model_cls = self.get_settings_model()
+ return settings_model_cls(**DEFAULT_VALUES)
diff --git a/server_addon/royal_render/server/settings.py b/server_addon/royal_render/server/settings.py
new file mode 100644
index 0000000000..677d7e2671
--- /dev/null
+++ b/server_addon/royal_render/server/settings.py
@@ -0,0 +1,70 @@
+from pydantic import Field
+from ayon_server.settings import BaseSettingsModel, MultiplatformPathModel
+
+
+class CustomPath(MultiplatformPathModel):
+ _layout = "expanded"
+
+
+class ServerListSubmodel(BaseSettingsModel):
+ _layout = "expanded"
+ name: str = Field("", title="Name")
+ value: CustomPath = Field(
+ default_factory=CustomPath
+ )
+
+
+class CollectSequencesFromJobModel(BaseSettingsModel):
+ review: bool = Field(True, title="Generate reviews from sequences")
+
+
+class PublishPluginsModel(BaseSettingsModel):
+ CollectSequencesFromJob: CollectSequencesFromJobModel = Field(
+ default_factory=CollectSequencesFromJobModel,
+ title="Collect Sequences from the Job"
+ )
+
+
+class RoyalRenderSettings(BaseSettingsModel):
+ enabled: bool = True
+ # WARNING/TODO this needs change
+ # - both system and project settings contained 'rr_path'
+ # where project settings did choose one of rr_path from system settings
+ # that is not possible in AYON
+ rr_paths: list[ServerListSubmodel] = Field(
+ default_factory=list,
+ title="Royal Render Root Paths",
+ scope=["studio"],
+ )
+ # This was 'rr_paths' in project settings and should be enum of
+ # 'rr_paths' from system settings, but that's not possible in AYON
+ selected_rr_paths: list[str] = Field(
+ default_factory=list,
+ title="Selected Royal Render Paths",
+ section="---",
+ )
+ publish: PublishPluginsModel = Field(
+ default_factory=PublishPluginsModel,
+ title="Publish plugins",
+ )
+
+
+DEFAULT_VALUES = {
+ "enabled": False,
+ "rr_paths": [
+ {
+ "name": "default",
+ "value": {
+ "windows": "",
+ "darwin": "",
+ "linux": ""
+ }
+ }
+ ],
+ "selected_rr_paths": ["default"],
+ "publish": {
+ "CollectSequencesFromJob": {
+ "review": True
+ }
+ }
+}
diff --git a/server_addon/royal_render/server/version.py b/server_addon/royal_render/server/version.py
new file mode 100644
index 0000000000..485f44ac21
--- /dev/null
+++ b/server_addon/royal_render/server/version.py
@@ -0,0 +1 @@
+__version__ = "0.1.1"
diff --git a/server_addon/timers_manager/server/__init__.py b/server_addon/timers_manager/server/__init__.py
new file mode 100644
index 0000000000..29f9d47370
--- /dev/null
+++ b/server_addon/timers_manager/server/__init__.py
@@ -0,0 +1,13 @@
+from typing import Type
+
+from ayon_server.addons import BaseServerAddon
+
+from .version import __version__
+from .settings import TimersManagerSettings
+
+
+class TimersManagerAddon(BaseServerAddon):
+ name = "timers_manager"
+ version = __version__
+ title = "Timers Manager"
+ settings_model: Type[TimersManagerSettings] = TimersManagerSettings
diff --git a/server_addon/timers_manager/server/settings.py b/server_addon/timers_manager/server/settings.py
new file mode 100644
index 0000000000..a5c5721a57
--- /dev/null
+++ b/server_addon/timers_manager/server/settings.py
@@ -0,0 +1,25 @@
+from pydantic import Field
+from ayon_server.settings import BaseSettingsModel
+
+
+class TimersManagerSettings(BaseSettingsModel):
+ auto_stop: bool = Field(
+ True,
+ title="Auto stop timer",
+ scope=["studio"],
+ )
+ full_time: int = Field(
+ 15,
+ title="Max idle time",
+ scope=["studio"],
+ )
+ message_time: float = Field(
+ 0.5,
+ title="When dialog will show",
+ scope=["studio"],
+ )
+ disregard_publishing: bool = Field(
+ False,
+ title="Disregard publishing",
+ scope=["studio"],
+ )
diff --git a/server_addon/timers_manager/server/version.py b/server_addon/timers_manager/server/version.py
new file mode 100644
index 0000000000..485f44ac21
--- /dev/null
+++ b/server_addon/timers_manager/server/version.py
@@ -0,0 +1 @@
+__version__ = "0.1.1"
diff --git a/server_addon/traypublisher/server/LICENSE b/server_addon/traypublisher/server/LICENSE
new file mode 100644
index 0000000000..d645695673
--- /dev/null
+++ b/server_addon/traypublisher/server/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/server_addon/traypublisher/server/README.md b/server_addon/traypublisher/server/README.md
new file mode 100644
index 0000000000..c0029bc782
--- /dev/null
+++ b/server_addon/traypublisher/server/README.md
@@ -0,0 +1,4 @@
+Photoshp Addon
+===============
+
+Integration with Adobe Traypublisher.
diff --git a/server_addon/traypublisher/server/__init__.py b/server_addon/traypublisher/server/__init__.py
new file mode 100644
index 0000000000..e6f079609f
--- /dev/null
+++ b/server_addon/traypublisher/server/__init__.py
@@ -0,0 +1,16 @@
+from ayon_server.addons import BaseServerAddon
+
+from .version import __version__
+from .settings import TraypublisherSettings, DEFAULT_TRAYPUBLISHER_SETTING
+
+
+class Traypublisher(BaseServerAddon):
+ name = "traypublisher"
+ title = "TrayPublisher"
+ version = __version__
+
+ settings_model = TraypublisherSettings
+
+ async def get_default_settings(self):
+ settings_model_cls = self.get_settings_model()
+ return settings_model_cls(**DEFAULT_TRAYPUBLISHER_SETTING)
diff --git a/server_addon/traypublisher/server/settings/__init__.py b/server_addon/traypublisher/server/settings/__init__.py
new file mode 100644
index 0000000000..bcf8beffa7
--- /dev/null
+++ b/server_addon/traypublisher/server/settings/__init__.py
@@ -0,0 +1,10 @@
+from .main import (
+ TraypublisherSettings,
+ DEFAULT_TRAYPUBLISHER_SETTING,
+)
+
+
+__all__ = (
+ "TraypublisherSettings",
+ "DEFAULT_TRAYPUBLISHER_SETTING",
+)
diff --git a/server_addon/traypublisher/server/settings/creator_plugins.py b/server_addon/traypublisher/server/settings/creator_plugins.py
new file mode 100644
index 0000000000..345cb92e63
--- /dev/null
+++ b/server_addon/traypublisher/server/settings/creator_plugins.py
@@ -0,0 +1,46 @@
+from pydantic import Field
+
+from ayon_server.settings import BaseSettingsModel
+
+
+class BatchMovieCreatorPlugin(BaseSettingsModel):
+ """Allows to publish multiple video files in one go.
Name of matching
+ asset is parsed from file names ('asset.mov', 'asset_v001.mov',
+ 'my_asset_to_publish.mov')"""
+
+ default_variants: list[str] = Field(
+ title="Default variants",
+ default_factory=list
+ )
+
+ default_tasks: list[str] = Field(
+ title="Default tasks",
+ default_factory=list
+ )
+
+ extensions: list[str] = Field(
+ title="Extensions",
+ default_factory=list
+ )
+
+
+class TrayPublisherCreatePluginsModel(BaseSettingsModel):
+ BatchMovieCreator: BatchMovieCreatorPlugin = Field(
+ title="Batch Movie Creator",
+ default_factory=BatchMovieCreatorPlugin
+ )
+
+
+DEFAULT_CREATORS = {
+ "BatchMovieCreator": {
+ "default_variants": [
+ "Main"
+ ],
+ "default_tasks": [
+ "Compositing"
+ ],
+ "extensions": [
+ ".mov"
+ ]
+ },
+}
diff --git a/server_addon/traypublisher/server/settings/editorial_creators.py b/server_addon/traypublisher/server/settings/editorial_creators.py
new file mode 100644
index 0000000000..4111f22576
--- /dev/null
+++ b/server_addon/traypublisher/server/settings/editorial_creators.py
@@ -0,0 +1,181 @@
+from pydantic import Field
+
+from ayon_server.settings import BaseSettingsModel, task_types_enum
+
+
+class ClipNameTokenizerItem(BaseSettingsModel):
+ _layout = "expanded"
+ # TODO was 'dict-modifiable', is list of dicts now, must be fixed in code
+ name: str = Field("#TODO", title="Tokenizer name")
+ regex: str = Field("", title="Tokenizer regex")
+
+
+class ShotAddTasksItem(BaseSettingsModel):
+ _layout = "expanded"
+ # TODO was 'dict-modifiable', is list of dicts now, must be fixed in code
+ name: str = Field('', title="Key")
+ task_type: list[str] = Field(
+ title="Task type",
+ default_factory=list,
+ enum_resolver=task_types_enum)
+
+
+class ShotRenameSubmodel(BaseSettingsModel):
+ enabled: bool = True
+ shot_rename_template: str = Field(
+ "",
+ title="Shot rename template"
+ )
+
+
+parent_type_enum = [
+ {"value": "Project", "label": "Project"},
+ {"value": "Folder", "label": "Folder"},
+ {"value": "Episode", "label": "Episode"},
+ {"value": "Sequence", "label": "Sequence"},
+]
+
+
+class TokenToParentConvertorItem(BaseSettingsModel):
+ # TODO - was 'type' must be renamed in code to `parent_type`
+ parent_type: str = Field(
+ "Project",
+ enum_resolver=lambda: parent_type_enum
+ )
+ name: str = Field(
+ "",
+ title="Parent token name",
+ description="Unique name used in `Parent path template`"
+ )
+ value: str = Field(
+ "",
+ title="Parent token value",
+ description="Template where any text, Anatomy keys and Tokens could be used" # noqa
+ )
+
+
+class ShotHierchySubmodel(BaseSettingsModel):
+ enabled: bool = True
+ parents_path: str = Field(
+ "",
+ title="Parents path template",
+ description="Using keys from \"Token to parent convertor\" or tokens directly" # noqa
+ )
+ parents: list[TokenToParentConvertorItem] = Field(
+ default_factory=TokenToParentConvertorItem,
+ title="Token to parent convertor"
+ )
+
+
+output_file_type = [
+ {"value": ".mp4", "label": "MP4"},
+ {"value": ".mov", "label": "MOV"},
+ {"value": ".wav", "label": "WAV"}
+]
+
+
+class ProductTypePresetItem(BaseSettingsModel):
+ product_type: str = Field("", title="Product type")
+ # TODO add placeholder '< Inherited >'
+ variant: str = Field("", title="Variant")
+ review: bool = Field(True, title="Review")
+ output_file_type: str = Field(
+ ".mp4",
+ enum_resolver=lambda: output_file_type
+ )
+
+
+class EditorialSimpleCreatorPlugin(BaseSettingsModel):
+ default_variants: list[str] = Field(
+ default_factory=list,
+ title="Default Variants"
+ )
+ clip_name_tokenizer: list[ClipNameTokenizerItem] = Field(
+ default_factory=ClipNameTokenizerItem,
+ description=(
+ "Using Regex expression to create tokens. \nThose can be used"
+ " later in \"Shot rename\" creator \nor \"Shot hierarchy\"."
+ "\n\nTokens should be decorated with \"_\" on each side"
+ )
+ )
+ shot_rename: ShotRenameSubmodel = Field(
+ title="Shot Rename",
+ default_factory=ShotRenameSubmodel
+ )
+ shot_hierarchy: ShotHierchySubmodel = Field(
+ title="Shot Hierarchy",
+ default_factory=ShotHierchySubmodel
+ )
+ shot_add_tasks: list[ShotAddTasksItem] = Field(
+ title="Add tasks to shot",
+ default_factory=ShotAddTasksItem
+ )
+ product_type_presets: list[ProductTypePresetItem] = Field(
+ default_factory=list
+ )
+
+
+class TraypublisherEditorialCreatorPlugins(BaseSettingsModel):
+ editorial_simple: EditorialSimpleCreatorPlugin = Field(
+ title="Editorial simple creator",
+ default_factory=EditorialSimpleCreatorPlugin,
+ )
+
+
+DEFAULT_EDITORIAL_CREATORS = {
+ "editorial_simple": {
+ "default_variants": [
+ "Main"
+ ],
+ "clip_name_tokenizer": [
+ {"name": "_sequence_", "regex": "(sc\\d{3})"},
+ {"name": "_shot_", "regex": "(sh\\d{3})"}
+ ],
+ "shot_rename": {
+ "enabled": True,
+ "shot_rename_template": "{project[code]}_{_sequence_}_{_shot_}"
+ },
+ "shot_hierarchy": {
+ "enabled": True,
+ "parents_path": "{project}/{folder}/{sequence}",
+ "parents": [
+ {
+ "parent_type": "Project",
+ "name": "project",
+ "value": "{project[name]}"
+ },
+ {
+ "parent_type": "Folder",
+ "name": "folder",
+ "value": "shots"
+ },
+ {
+ "parent_type": "Sequence",
+ "name": "sequence",
+ "value": "{_sequence_}"
+ }
+ ]
+ },
+ "shot_add_tasks": [],
+ "product_type_presets": [
+ {
+ "product_type": "review",
+ "variant": "Reference",
+ "review": True,
+ "output_file_type": ".mp4"
+ },
+ {
+ "product_type": "plate",
+ "variant": "",
+ "review": False,
+ "output_file_type": ".mov"
+ },
+ {
+ "product_type": "audio",
+ "variant": "",
+ "review": False,
+ "output_file_type": ".wav"
+ }
+ ]
+ }
+}
diff --git a/server_addon/traypublisher/server/settings/imageio.py b/server_addon/traypublisher/server/settings/imageio.py
new file mode 100644
index 0000000000..3df0d2f2fb
--- /dev/null
+++ b/server_addon/traypublisher/server/settings/imageio.py
@@ -0,0 +1,48 @@
+from pydantic import Field, validator
+from ayon_server.settings import BaseSettingsModel
+from ayon_server.settings.validators import ensure_unique_names
+
+
+class ImageIOConfigModel(BaseSettingsModel):
+ override_global_config: bool = Field(
+ False,
+ title="Override global OCIO config"
+ )
+ filepath: list[str] = Field(
+ default_factory=list,
+ title="Config path"
+ )
+
+
+class ImageIOFileRuleModel(BaseSettingsModel):
+ name: str = Field("", title="Rule name")
+ pattern: str = Field("", title="Regex pattern")
+ colorspace: str = Field("", title="Colorspace name")
+ ext: str = Field("", title="File extension")
+
+
+class ImageIOFileRulesModel(BaseSettingsModel):
+ activate_host_rules: bool = Field(False)
+ rules: list[ImageIOFileRuleModel] = Field(
+ default_factory=list,
+ title="Rules"
+ )
+
+ @validator("rules")
+ def validate_unique_outputs(cls, value):
+ ensure_unique_names(value)
+ return value
+
+
+class TrayPublisherImageIOModel(BaseSettingsModel):
+ activate_host_color_management: bool = Field(
+ True, title="Enable Color Management"
+ )
+ ocio_config: ImageIOConfigModel = Field(
+ default_factory=ImageIOConfigModel,
+ title="OCIO config"
+ )
+ file_rules: ImageIOFileRulesModel = Field(
+ default_factory=ImageIOFileRulesModel,
+ title="File Rules"
+ )
diff --git a/server_addon/traypublisher/server/settings/main.py b/server_addon/traypublisher/server/settings/main.py
new file mode 100644
index 0000000000..fad96bef2f
--- /dev/null
+++ b/server_addon/traypublisher/server/settings/main.py
@@ -0,0 +1,52 @@
+from pydantic import Field
+from ayon_server.settings import BaseSettingsModel
+
+from .imageio import TrayPublisherImageIOModel
+from .simple_creators import (
+ SimpleCreatorPlugin,
+ DEFAULT_SIMPLE_CREATORS,
+)
+from .editorial_creators import (
+ TraypublisherEditorialCreatorPlugins,
+ DEFAULT_EDITORIAL_CREATORS,
+)
+from .creator_plugins import (
+ TrayPublisherCreatePluginsModel,
+ DEFAULT_CREATORS,
+)
+from .publish_plugins import (
+ TrayPublisherPublishPlugins,
+ DEFAULT_PUBLISH_PLUGINS,
+)
+
+
+class TraypublisherSettings(BaseSettingsModel):
+ """Traypublisher Project Settings."""
+ imageio: TrayPublisherImageIOModel = Field(
+ default_factory=TrayPublisherImageIOModel,
+ title="Color Management (ImageIO)"
+ )
+ simple_creators: list[SimpleCreatorPlugin] = Field(
+ title="Simple Create Plugins",
+ default_factory=SimpleCreatorPlugin,
+ )
+ editorial_creators: TraypublisherEditorialCreatorPlugins = Field(
+ title="Editorial Creators",
+ default_factory=TraypublisherEditorialCreatorPlugins,
+ )
+ create: TrayPublisherCreatePluginsModel = Field(
+ title="Create",
+ default_factory=TrayPublisherCreatePluginsModel
+ )
+ publish: TrayPublisherPublishPlugins = Field(
+ title="Publish Plugins",
+ default_factory=TrayPublisherPublishPlugins
+ )
+
+
+DEFAULT_TRAYPUBLISHER_SETTING = {
+ "simple_creators": DEFAULT_SIMPLE_CREATORS,
+ "editorial_creators": DEFAULT_EDITORIAL_CREATORS,
+ "create": DEFAULT_CREATORS,
+ "publish": DEFAULT_PUBLISH_PLUGINS,
+}
diff --git a/server_addon/traypublisher/server/settings/publish_plugins.py b/server_addon/traypublisher/server/settings/publish_plugins.py
new file mode 100644
index 0000000000..8c844f29f2
--- /dev/null
+++ b/server_addon/traypublisher/server/settings/publish_plugins.py
@@ -0,0 +1,50 @@
+from pydantic import Field
+
+from ayon_server.settings import BaseSettingsModel
+
+
+class ValidatePluginModel(BaseSettingsModel):
+ _isGroup = True
+ enabled: bool = True
+ optional: bool = Field(True, title="Optional")
+ active: bool = Field(True, title="Active")
+
+
+class ValidateFrameRangeModel(ValidatePluginModel):
+ """Allows to publish multiple video files in one go.
Name of matching
+ asset is parsed from file names ('asset.mov', 'asset_v001.mov',
+ 'my_asset_to_publish.mov')"""
+
+
+class TrayPublisherPublishPlugins(BaseSettingsModel):
+ CollectFrameDataFromAssetEntity: ValidatePluginModel = Field(
+ default_factory=ValidatePluginModel,
+ title="Collect Frame Data From Folder Entity",
+ )
+ ValidateFrameRange: ValidateFrameRangeModel = Field(
+ title="Validate Frame Range",
+ default_factory=ValidateFrameRangeModel,
+ )
+ ValidateExistingVersion: ValidatePluginModel = Field(
+ title="Validate Existing Version",
+ default_factory=ValidatePluginModel,
+ )
+
+
+DEFAULT_PUBLISH_PLUGINS = {
+ "CollectFrameDataFromAssetEntity": {
+ "enabled": True,
+ "optional": True,
+ "active": True
+ },
+ "ValidateFrameRange": {
+ "enabled": True,
+ "optional": True,
+ "active": True
+ },
+ "ValidateExistingVersion": {
+ "enabled": True,
+ "optional": True,
+ "active": True
+ }
+}
diff --git a/server_addon/traypublisher/server/settings/simple_creators.py b/server_addon/traypublisher/server/settings/simple_creators.py
new file mode 100644
index 0000000000..94d6602738
--- /dev/null
+++ b/server_addon/traypublisher/server/settings/simple_creators.py
@@ -0,0 +1,292 @@
+from pydantic import Field
+
+from ayon_server.settings import BaseSettingsModel
+
+
+class SimpleCreatorPlugin(BaseSettingsModel):
+ _layout = "expanded"
+ product_type: str = Field("", title="Product type")
+ # TODO add placeholder
+ identifier: str = Field("", title="Identifier")
+ label: str = Field("", title="Label")
+ icon: str = Field("", title="Icon")
+ default_variants: list[str] = Field(
+ default_factory=list,
+ title="Default Variants"
+ )
+ description: str = Field(
+ "",
+ title="Description",
+ widget="textarea"
+ )
+ detailed_description: str = Field(
+ "",
+ title="Detailed Description",
+ widget="textarea"
+ )
+ allow_sequences: bool = Field(
+ False,
+ title="Allow sequences"
+ )
+ allow_multiple_items: bool = Field(
+ False,
+ title="Allow multiple items"
+ )
+ allow_version_control: bool = Field(
+ False,
+ title="Allow version control"
+ )
+ extensions: list[str] = Field(
+ default_factory=list,
+ title="Extensions"
+ )
+
+
+DEFAULT_SIMPLE_CREATORS = [
+ {
+ "product_type": "workfile",
+ "identifier": "",
+ "label": "Workfile",
+ "icon": "fa.file",
+ "default_variants": [
+ "Main"
+ ],
+ "description": "Backup of a working scene",
+ "detailed_description": "Workfiles are full scenes from any application that are directly edited by artists. They represent a state of work on a task at a given point and are usually not directly referenced into other scenes.",
+ "allow_sequences": False,
+ "allow_multiple_items": False,
+ "allow_version_control": False,
+ "extensions": [
+ ".ma",
+ ".mb",
+ ".nk",
+ ".hrox",
+ ".hip",
+ ".hiplc",
+ ".hipnc",
+ ".blend",
+ ".scn",
+ ".tvpp",
+ ".comp",
+ ".zip",
+ ".prproj",
+ ".drp",
+ ".psd",
+ ".psb",
+ ".aep"
+ ]
+ },
+ {
+ "product_type": "model",
+ "identifier": "",
+ "label": "Model",
+ "icon": "fa.cubes",
+ "default_variants": [
+ "Main",
+ "Proxy",
+ "Sculpt"
+ ],
+ "description": "Clean models",
+ "detailed_description": "Models should only contain geometry data, without any extras like cameras, locators or bones.\n\nKeep in mind that models published from tray publisher are not validated for correctness. ",
+ "allow_sequences": False,
+ "allow_multiple_items": True,
+ "allow_version_control": False,
+ "extensions": [
+ ".ma",
+ ".mb",
+ ".obj",
+ ".abc",
+ ".fbx",
+ ".bgeo",
+ ".bgeogz",
+ ".bgeosc",
+ ".usd",
+ ".blend"
+ ]
+ },
+ {
+ "product_type": "pointcache",
+ "identifier": "",
+ "label": "Pointcache",
+ "icon": "fa.gears",
+ "default_variants": [
+ "Main"
+ ],
+ "description": "Geometry Caches",
+ "detailed_description": "Alembic or bgeo cache of animated data",
+ "allow_sequences": True,
+ "allow_multiple_items": True,
+ "allow_version_control": False,
+ "extensions": [
+ ".abc",
+ ".bgeo",
+ ".bgeogz",
+ ".bgeosc"
+ ]
+ },
+ {
+ "product_type": "plate",
+ "identifier": "",
+ "label": "Plate",
+ "icon": "mdi.camera-image",
+ "default_variants": [
+ "Main",
+ "BG",
+ "Animatic",
+ "Reference",
+ "Offline"
+ ],
+ "description": "Footage Plates",
+ "detailed_description": "Any type of image seqeuence coming from outside of the studio. Usually camera footage, but could also be animatics used for reference.",
+ "allow_sequences": True,
+ "allow_multiple_items": True,
+ "allow_version_control": False,
+ "extensions": [
+ ".exr",
+ ".png",
+ ".dpx",
+ ".jpg",
+ ".tiff",
+ ".tif",
+ ".mov",
+ ".mp4",
+ ".avi"
+ ]
+ },
+ {
+ "product_type": "render",
+ "identifier": "",
+ "label": "Render",
+ "icon": "mdi.folder-multiple-image",
+ "default_variants": [],
+ "description": "Rendered images or video",
+ "detailed_description": "Sequence or single file renders",
+ "allow_sequences": True,
+ "allow_multiple_items": True,
+ "allow_version_control": False,
+ "extensions": [
+ ".exr",
+ ".png",
+ ".dpx",
+ ".jpg",
+ ".jpeg",
+ ".tiff",
+ ".tif",
+ ".mov",
+ ".mp4",
+ ".avi"
+ ]
+ },
+ {
+ "product_type": "camera",
+ "identifier": "",
+ "label": "Camera",
+ "icon": "fa.video-camera",
+ "default_variants": [],
+ "description": "3d Camera",
+ "detailed_description": "Ideally this should be only camera itself with baked animation, however, it can technically also include helper geometry.",
+ "allow_sequences": False,
+ "allow_multiple_items": True,
+ "allow_version_control": False,
+ "extensions": [
+ ".abc",
+ ".ma",
+ ".hip",
+ ".blend",
+ ".fbx",
+ ".usd"
+ ]
+ },
+ {
+ "product_type": "image",
+ "identifier": "",
+ "label": "Image",
+ "icon": "fa.image",
+ "default_variants": [
+ "Reference",
+ "Texture",
+ "Concept",
+ "Background"
+ ],
+ "description": "Single image",
+ "detailed_description": "Any image data can be published as image product type. References, textures, concept art, matte paints. This is a fallback 2d product type for everything that doesn't fit more specific product type.",
+ "allow_sequences": False,
+ "allow_multiple_items": True,
+ "allow_version_control": False,
+ "extensions": [
+ ".exr",
+ ".jpg",
+ ".jpeg",
+ ".dpx",
+ ".bmp",
+ ".tif",
+ ".tiff",
+ ".png",
+ ".psb",
+ ".psd"
+ ]
+ },
+ {
+ "product_type": "vdb",
+ "identifier": "",
+ "label": "VDB Volumes",
+ "icon": "fa.cloud",
+ "default_variants": [],
+ "description": "Sparse volumetric data",
+ "detailed_description": "Hierarchical data structure for the efficient storage and manipulation of sparse volumetric data discretized on three-dimensional grids",
+ "allow_sequences": True,
+ "allow_multiple_items": True,
+ "allow_version_control": False,
+ "extensions": [
+ ".vdb"
+ ]
+ },
+ {
+ "product_type": "matchmove",
+ "identifier": "",
+ "label": "Matchmove",
+ "icon": "fa.empire",
+ "default_variants": [
+ "Camera",
+ "Object",
+ "Mocap"
+ ],
+ "description": "Matchmoving script",
+ "detailed_description": "Script exported from matchmoving application to be later processed into a tracked camera with additional data",
+ "allow_sequences": False,
+ "allow_multiple_items": True,
+ "allow_version_control": False,
+ "extensions": []
+ },
+ {
+ "product_type": "rig",
+ "identifier": "",
+ "label": "Rig",
+ "icon": "fa.wheelchair",
+ "default_variants": [],
+ "description": "CG rig file",
+ "detailed_description": "CG rigged character or prop. Rig should be clean of any extra data and directly loadable into it's respective application\t",
+ "allow_sequences": False,
+ "allow_multiple_items": False,
+ "allow_version_control": False,
+ "extensions": [
+ ".ma",
+ ".blend",
+ ".hip",
+ ".hda"
+ ]
+ },
+ {
+ "product_type": "simpleUnrealTexture",
+ "identifier": "",
+ "label": "Simple UE texture",
+ "icon": "fa.image",
+ "default_variants": [],
+ "description": "Simple Unreal Engine texture",
+ "detailed_description": "Texture files with Unreal Engine naming conventions",
+ "allow_sequences": False,
+ "allow_multiple_items": True,
+ "allow_version_control": False,
+ "extensions": []
+ }
+]
diff --git a/server_addon/traypublisher/server/version.py b/server_addon/traypublisher/server/version.py
new file mode 100644
index 0000000000..df0c92f1e2
--- /dev/null
+++ b/server_addon/traypublisher/server/version.py
@@ -0,0 +1,3 @@
+# -*- coding: utf-8 -*-
+"""Package declaring addon version."""
+__version__ = "0.1.2"
diff --git a/server_addon/tvpaint/server/__init__.py b/server_addon/tvpaint/server/__init__.py
new file mode 100644
index 0000000000..033d7d3792
--- /dev/null
+++ b/server_addon/tvpaint/server/__init__.py
@@ -0,0 +1,17 @@
+from typing import Type
+
+from ayon_server.addons import BaseServerAddon
+
+from .version import __version__
+from .settings import TvpaintSettings, DEFAULT_VALUES
+
+
+class TvpaintAddon(BaseServerAddon):
+ name = "tvpaint"
+ title = "TVPaint"
+ version = __version__
+ settings_model: Type[TvpaintSettings] = TvpaintSettings
+
+ async def get_default_settings(self):
+ settings_model_cls = self.get_settings_model()
+ return settings_model_cls(**DEFAULT_VALUES)
diff --git a/server_addon/tvpaint/server/settings/__init__.py b/server_addon/tvpaint/server/settings/__init__.py
new file mode 100644
index 0000000000..abee32e897
--- /dev/null
+++ b/server_addon/tvpaint/server/settings/__init__.py
@@ -0,0 +1,10 @@
+from .main import (
+ TvpaintSettings,
+ DEFAULT_VALUES,
+)
+
+
+__all__ = (
+ "TvpaintSettings",
+ "DEFAULT_VALUES",
+)
diff --git a/server_addon/tvpaint/server/settings/create_plugins.py b/server_addon/tvpaint/server/settings/create_plugins.py
new file mode 100644
index 0000000000..349bfdd288
--- /dev/null
+++ b/server_addon/tvpaint/server/settings/create_plugins.py
@@ -0,0 +1,133 @@
+from pydantic import Field
+from ayon_server.settings import BaseSettingsModel
+
+
+class CreateWorkfileModel(BaseSettingsModel):
+ enabled: bool = Field(True)
+ default_variant: str = Field(title="Default variant")
+ default_variants: list[str] = Field(
+ default_factory=list, title="Default variants")
+
+
+class CreateReviewModel(BaseSettingsModel):
+ enabled: bool = Field(True)
+ active_on_create: bool = Field(True, title="Active by default")
+ default_variant: str = Field(title="Default variant")
+ default_variants: list[str] = Field(
+ default_factory=list, title="Default variants")
+
+
+class CreateRenderSceneModel(BaseSettingsModel):
+ enabled: bool = Field(True)
+ active_on_create: bool = Field(True, title="Active by default")
+ mark_for_review: bool = Field(True, title="Review by default")
+ default_pass_name: str = Field(title="Default beauty pass")
+ default_variant: str = Field(title="Default variant")
+ default_variants: list[str] = Field(
+ default_factory=list, title="Default variants")
+
+
+class CreateRenderLayerModel(BaseSettingsModel):
+ mark_for_review: bool = Field(True, title="Review by default")
+ default_pass_name: str = Field(title="Default beauty pass")
+ default_variant: str = Field(title="Default variant")
+ default_variants: list[str] = Field(
+ default_factory=list, title="Default variants")
+
+
+class CreateRenderPassModel(BaseSettingsModel):
+ mark_for_review: bool = Field(True, title="Review by default")
+ default_variant: str = Field(title="Default variant")
+ default_variants: list[str] = Field(
+ default_factory=list, title="Default variants")
+
+
+class AutoDetectCreateRenderModel(BaseSettingsModel):
+ """The creator tries to auto-detect Render Layers and Render Passes in scene.
+
+ For Render Layers is used group name as a variant and for Render Passes is
+ used TVPaint layer name.
+
+ Group names can be renamed by their used order in scene. The renaming
+ template where can be used '{group_index}' formatting key which is
+ filled by "used position index of group".
+ - Template: 'L{group_index}'
+ - Group offset: '10'
+ - Group padding: '3'
+
+ Would create group names "L010", "L020", ...
+ """
+
+ enabled: bool = Field(True)
+ allow_group_rename: bool = Field(title="Allow group rename")
+ group_name_template: str = Field(title="Group name template")
+ group_idx_offset: int = Field(1, title="Group index Offset", ge=1)
+ group_idx_padding: int = Field(4, title="Group index Padding", ge=1)
+
+
+class CreatePluginsModel(BaseSettingsModel):
+ create_workfile: CreateWorkfileModel = Field(
+ default_factory=CreateWorkfileModel,
+ title="Create Workfile"
+ )
+ create_review: CreateReviewModel = Field(
+ default_factory=CreateReviewModel,
+ title="Create Review"
+ )
+ create_render_scene: CreateRenderSceneModel = Field(
+ default_factory=CreateReviewModel,
+ title="Create Render Scene"
+ )
+ create_render_layer: CreateRenderLayerModel= Field(
+ default_factory=CreateRenderLayerModel,
+ title="Create Render Layer"
+ )
+ create_render_pass: CreateRenderPassModel = Field(
+ default_factory=CreateRenderPassModel,
+ title="Create Render Pass"
+ )
+ auto_detect_render: AutoDetectCreateRenderModel = Field(
+ default_factory=AutoDetectCreateRenderModel,
+ title="Auto-Detect Create Render",
+ )
+
+
+DEFAULT_CREATE_SETTINGS = {
+ "create_workfile": {
+ "enabled": True,
+ "default_variant": "Main",
+ "default_variants": []
+ },
+ "create_review": {
+ "enabled": True,
+ "active_on_create": True,
+ "default_variant": "Main",
+ "default_variants": []
+ },
+ "create_render_scene": {
+ "enabled": True,
+ "active_on_create": False,
+ "mark_for_review": True,
+ "default_pass_name": "beauty",
+ "default_variant": "Main",
+ "default_variants": []
+ },
+ "create_render_layer": {
+ "mark_for_review": False,
+ "default_pass_name": "beauty",
+ "default_variant": "Main",
+ "default_variants": []
+ },
+ "create_render_pass": {
+ "mark_for_review": False,
+ "default_variant": "Main",
+ "default_variants": []
+ },
+ "auto_detect_render": {
+ "enabled": False,
+ "allow_group_rename": True,
+ "group_name_template": "L{group_index}",
+ "group_idx_offset": 10,
+ "group_idx_padding": 3
+ }
+}
diff --git a/server_addon/tvpaint/server/settings/filters.py b/server_addon/tvpaint/server/settings/filters.py
new file mode 100644
index 0000000000..009febae06
--- /dev/null
+++ b/server_addon/tvpaint/server/settings/filters.py
@@ -0,0 +1,19 @@
+from pydantic import Field
+
+from ayon_server.settings import BaseSettingsModel
+
+
+class FiltersSubmodel(BaseSettingsModel):
+ _layout = "compact"
+ name: str = Field(title="Name")
+ value: str = Field(
+ "",
+ title="Textarea",
+ widget="textarea",
+ )
+
+
+class PublishFiltersModel(BaseSettingsModel):
+ env_search_replace_values: list[FiltersSubmodel] = Field(
+ default_factory=list
+ )
diff --git a/server_addon/tvpaint/server/settings/imageio.py b/server_addon/tvpaint/server/settings/imageio.py
new file mode 100644
index 0000000000..50f8b7eef4
--- /dev/null
+++ b/server_addon/tvpaint/server/settings/imageio.py
@@ -0,0 +1,48 @@
+from pydantic import Field, validator
+from ayon_server.settings import BaseSettingsModel
+from ayon_server.settings.validators import ensure_unique_names
+
+
+class ImageIOConfigModel(BaseSettingsModel):
+ override_global_config: bool = Field(
+ False,
+ title="Override global OCIO config"
+ )
+ filepath: list[str] = Field(
+ default_factory=list,
+ title="Config path"
+ )
+
+
+class ImageIOFileRuleModel(BaseSettingsModel):
+ name: str = Field("", title="Rule name")
+ pattern: str = Field("", title="Regex pattern")
+ colorspace: str = Field("", title="Colorspace name")
+ ext: str = Field("", title="File extension")
+
+
+class ImageIOFileRulesModel(BaseSettingsModel):
+ activate_host_rules: bool = Field(False)
+ rules: list[ImageIOFileRuleModel] = Field(
+ default_factory=list,
+ title="Rules"
+ )
+
+ @validator("rules")
+ def validate_unique_outputs(cls, value):
+ ensure_unique_names(value)
+ return value
+
+
+class TVPaintImageIOModel(BaseSettingsModel):
+ activate_host_color_management: bool = Field(
+ True, title="Enable Color Management"
+ )
+ ocio_config: ImageIOConfigModel = Field(
+ default_factory=ImageIOConfigModel,
+ title="OCIO config"
+ )
+ file_rules: ImageIOFileRulesModel = Field(
+ default_factory=ImageIOFileRulesModel,
+ title="File Rules"
+ )
diff --git a/server_addon/tvpaint/server/settings/main.py b/server_addon/tvpaint/server/settings/main.py
new file mode 100644
index 0000000000..4cd6ac4b1a
--- /dev/null
+++ b/server_addon/tvpaint/server/settings/main.py
@@ -0,0 +1,90 @@
+from pydantic import Field, validator
+from ayon_server.settings import (
+ BaseSettingsModel,
+ ensure_unique_names,
+)
+
+from .imageio import TVPaintImageIOModel
+from .workfile_builder import WorkfileBuilderPlugin
+from .create_plugins import CreatePluginsModel, DEFAULT_CREATE_SETTINGS
+from .publish_plugins import (
+ PublishPluginsModel,
+ LoadPluginsModel,
+ DEFAULT_PUBLISH_SETTINGS,
+)
+
+
+class PublishGUIFilterItemModel(BaseSettingsModel):
+ _layout = "compact"
+ name: str = Field(title="Name")
+ value: bool = Field(True, title="Active")
+
+
+class PublishGUIFiltersModel(BaseSettingsModel):
+ _layout = "compact"
+ name: str = Field(title="Name")
+ value: list[PublishGUIFilterItemModel] = Field(default_factory=list)
+
+ @validator("value")
+ def validate_unique_outputs(cls, value):
+ ensure_unique_names(value)
+ return value
+
+
+class TvpaintSettings(BaseSettingsModel):
+ imageio: TVPaintImageIOModel = Field(
+ default_factory=TVPaintImageIOModel,
+ title="Color Management (ImageIO)"
+ )
+ stop_timer_on_application_exit: bool = Field(
+ title="Stop timer on application exit")
+ create: CreatePluginsModel = Field(
+ default_factory=CreatePluginsModel,
+ title="Create plugins"
+ )
+ publish: PublishPluginsModel = Field(
+ default_factory=PublishPluginsModel,
+ title="Publish plugins")
+ load: LoadPluginsModel = Field(
+ default_factory=LoadPluginsModel,
+ title="Load plugins")
+ workfile_builder: WorkfileBuilderPlugin = Field(
+ default_factory=WorkfileBuilderPlugin,
+ title="Workfile Builder"
+ )
+ filters: list[PublishGUIFiltersModel] = Field(
+ default_factory=list,
+ title="Publish GUI Filters")
+
+ @validator("filters")
+ def validate_unique_outputs(cls, value):
+ ensure_unique_names(value)
+ return value
+
+
+DEFAULT_VALUES = {
+ "stop_timer_on_application_exit": False,
+ "create": DEFAULT_CREATE_SETTINGS,
+ "publish": DEFAULT_PUBLISH_SETTINGS,
+ "load": {
+ "LoadImage": {
+ "defaults": {
+ "stretch": True,
+ "timestretch": True,
+ "preload": True
+ }
+ },
+ "ImportImage": {
+ "defaults": {
+ "stretch": True,
+ "timestretch": True,
+ "preload": True
+ }
+ }
+ },
+ "workfile_builder": {
+ "create_first_version": False,
+ "custom_templates": []
+ },
+ "filters": []
+}
diff --git a/server_addon/tvpaint/server/settings/publish_plugins.py b/server_addon/tvpaint/server/settings/publish_plugins.py
new file mode 100644
index 0000000000..76c7eaac01
--- /dev/null
+++ b/server_addon/tvpaint/server/settings/publish_plugins.py
@@ -0,0 +1,132 @@
+from pydantic import Field
+
+from ayon_server.settings import BaseSettingsModel
+from ayon_server.types import ColorRGBA_uint8
+
+
+class CollectRenderInstancesModel(BaseSettingsModel):
+ ignore_render_pass_transparency: bool = Field(
+ title="Ignore Render Pass opacity"
+ )
+
+
+class ExtractSequenceModel(BaseSettingsModel):
+ """Review BG color is used for whole scene review and for thumbnails."""
+ # TODO Use alpha color
+ review_bg: ColorRGBA_uint8 = Field(
+ (255, 255, 255, 1.0),
+ title="Review BG color")
+
+
+class ValidatePluginModel(BaseSettingsModel):
+ enabled: bool = True
+ optional: bool = Field(True, title="Optional")
+ active: bool = Field(True, title="Active")
+
+
+def compression_enum():
+ return [
+ {"value": "ZIP", "label": "ZIP"},
+ {"value": "ZIPS", "label": "ZIPS"},
+ {"value": "DWAA", "label": "DWAA"},
+ {"value": "DWAB", "label": "DWAB"},
+ {"value": "PIZ", "label": "PIZ"},
+ {"value": "RLE", "label": "RLE"},
+ {"value": "PXR24", "label": "PXR24"},
+ {"value": "B44", "label": "B44"},
+ {"value": "B44A", "label": "B44A"},
+ {"value": "none", "label": "None"}
+ ]
+
+
+class ExtractConvertToEXRModel(BaseSettingsModel):
+ """WARNING: This plugin does not work on MacOS (using OIIO tool)."""
+ enabled: bool = False
+ replace_pngs: bool = True
+
+ exr_compression: str = Field(
+ "ZIP",
+ enum_resolver=compression_enum,
+ title="EXR Compression"
+ )
+
+
+class LoadImageDefaultModel(BaseSettingsModel):
+ _layout = "expanded"
+ stretch: bool = Field(title="Stretch")
+ timestretch: bool = Field(title="TimeStretch")
+ preload: bool = Field(title="Preload")
+
+
+class LoadImageModel(BaseSettingsModel):
+ defaults: LoadImageDefaultModel = Field(
+ default_factory=LoadImageDefaultModel
+ )
+
+
+class PublishPluginsModel(BaseSettingsModel):
+ CollectRenderInstances: CollectRenderInstancesModel = Field(
+ default_factory=CollectRenderInstancesModel,
+ title="Collect Render Instances")
+ ExtractSequence: ExtractSequenceModel = Field(
+ default_factory=ExtractSequenceModel,
+ title="Extract Sequence")
+ ValidateProjectSettings: ValidatePluginModel = Field(
+ default_factory=ValidatePluginModel,
+ title="Validate Project Settings")
+ ValidateMarks: ValidatePluginModel = Field(
+ default_factory=ValidatePluginModel,
+ title="Validate MarkIn/Out")
+ ValidateStartFrame: ValidatePluginModel = Field(
+ default_factory=ValidatePluginModel,
+ title="Validate Scene Start Frame")
+ ValidateAssetName: ValidatePluginModel = Field(
+ default_factory=ValidatePluginModel,
+ title="Validate Folder Name")
+ ExtractConvertToEXR: ExtractConvertToEXRModel = Field(
+ default_factory=ExtractConvertToEXRModel,
+ title="Extract Convert To EXR")
+
+
+class LoadPluginsModel(BaseSettingsModel):
+ LoadImage: LoadImageModel = Field(
+ default_factory=LoadImageModel,
+ title="Load Image")
+ ImportImage: LoadImageModel = Field(
+ default_factory=LoadImageModel,
+ title="Import Image")
+
+
+DEFAULT_PUBLISH_SETTINGS = {
+ "CollectRenderInstances": {
+ "ignore_render_pass_transparency": False
+ },
+ "ExtractSequence": {
+ "review_bg": [255, 255, 255, 1.0]
+ },
+ "ValidateProjectSettings": {
+ "enabled": True,
+ "optional": True,
+ "active": True
+ },
+ "ValidateMarks": {
+ "enabled": True,
+ "optional": True,
+ "active": True
+ },
+ "ValidateStartFrame": {
+ "enabled": False,
+ "optional": True,
+ "active": True
+ },
+ "ValidateAssetName": {
+ "enabled": True,
+ "optional": True,
+ "active": True
+ },
+ "ExtractConvertToEXR": {
+ "enabled": False,
+ "replace_pngs": True,
+ "exr_compression": "ZIP"
+ }
+}
diff --git a/server_addon/tvpaint/server/settings/workfile_builder.py b/server_addon/tvpaint/server/settings/workfile_builder.py
new file mode 100644
index 0000000000..e0aba5da7e
--- /dev/null
+++ b/server_addon/tvpaint/server/settings/workfile_builder.py
@@ -0,0 +1,30 @@
+from pydantic import Field
+
+from ayon_server.settings import (
+ BaseSettingsModel,
+ MultiplatformPathModel,
+ task_types_enum,
+)
+
+
+class CustomBuilderTemplate(BaseSettingsModel):
+ task_types: list[str] = Field(
+ default_factory=list,
+ title="Task types",
+ enum_resolver=task_types_enum
+ )
+ template_path: MultiplatformPathModel = Field(
+ default_factory=MultiplatformPathModel
+ )
+
+
+class WorkfileBuilderPlugin(BaseSettingsModel):
+ _title = "Workfile Builder"
+ create_first_version: bool = Field(
+ False,
+ title="Create first workfile"
+ )
+
+ custom_templates: list[CustomBuilderTemplate] = Field(
+ default_factory=CustomBuilderTemplate
+ )
diff --git a/server_addon/tvpaint/server/version.py b/server_addon/tvpaint/server/version.py
new file mode 100644
index 0000000000..3dc1f76bc6
--- /dev/null
+++ b/server_addon/tvpaint/server/version.py
@@ -0,0 +1 @@
+__version__ = "0.1.0"
diff --git a/server_addon/unreal/server/__init__.py b/server_addon/unreal/server/__init__.py
new file mode 100644
index 0000000000..a5f3e9597d
--- /dev/null
+++ b/server_addon/unreal/server/__init__.py
@@ -0,0 +1,19 @@
+from typing import Type
+
+from ayon_server.addons import BaseServerAddon
+
+from .version import __version__
+from .settings import UnrealSettings, DEFAULT_VALUES
+
+
+class UnrealAddon(BaseServerAddon):
+ name = "unreal"
+ title = "Unreal"
+ version = __version__
+ settings_model: Type[UnrealSettings] = UnrealSettings
+ frontend_scopes = {}
+ services = {}
+
+ async def get_default_settings(self):
+ settings_model_cls = self.get_settings_model()
+ return settings_model_cls(**DEFAULT_VALUES)
diff --git a/server_addon/unreal/server/imageio.py b/server_addon/unreal/server/imageio.py
new file mode 100644
index 0000000000..dde042ba47
--- /dev/null
+++ b/server_addon/unreal/server/imageio.py
@@ -0,0 +1,48 @@
+from pydantic import Field, validator
+from ayon_server.settings import BaseSettingsModel
+from ayon_server.settings.validators import ensure_unique_names
+
+
+class ImageIOConfigModel(BaseSettingsModel):
+ override_global_config: bool = Field(
+ False,
+ title="Override global OCIO config"
+ )
+ filepath: list[str] = Field(
+ default_factory=list,
+ title="Config path"
+ )
+
+
+class ImageIOFileRuleModel(BaseSettingsModel):
+ name: str = Field("", title="Rule name")
+ pattern: str = Field("", title="Regex pattern")
+ colorspace: str = Field("", title="Colorspace name")
+ ext: str = Field("", title="File extension")
+
+
+class ImageIOFileRulesModel(BaseSettingsModel):
+ activate_host_rules: bool = Field(False)
+ rules: list[ImageIOFileRuleModel] = Field(
+ default_factory=list,
+ title="Rules"
+ )
+
+ @validator("rules")
+ def validate_unique_outputs(cls, value):
+ ensure_unique_names(value)
+ return value
+
+
+class UnrealImageIOModel(BaseSettingsModel):
+ activate_host_color_management: bool = Field(
+ True, title="Enable Color Management"
+ )
+ ocio_config: ImageIOConfigModel = Field(
+ default_factory=ImageIOConfigModel,
+ title="OCIO config"
+ )
+ file_rules: ImageIOFileRulesModel = Field(
+ default_factory=ImageIOFileRulesModel,
+ title="File Rules"
+ )
diff --git a/server_addon/unreal/server/settings.py b/server_addon/unreal/server/settings.py
new file mode 100644
index 0000000000..479e041e25
--- /dev/null
+++ b/server_addon/unreal/server/settings.py
@@ -0,0 +1,64 @@
+from pydantic import Field
+from ayon_server.settings import BaseSettingsModel
+
+from .imageio import UnrealImageIOModel
+
+
+class ProjectSetup(BaseSettingsModel):
+ dev_mode: bool = Field(
+ False,
+ title="Dev mode"
+ )
+
+
+def _render_format_enum():
+ return [
+ {"value": "png", "label": "PNG"},
+ {"value": "exr", "label": "EXR"},
+ {"value": "jpg", "label": "JPG"},
+ {"value": "bmp", "label": "BMP"}
+ ]
+
+
+class UnrealSettings(BaseSettingsModel):
+ imageio: UnrealImageIOModel = Field(
+ default_factory=UnrealImageIOModel,
+ title="Color Management (ImageIO)"
+ )
+ level_sequences_for_layouts: bool = Field(
+ False,
+ title="Generate level sequences when loading layouts"
+ )
+ delete_unmatched_assets: bool = Field(
+ False,
+ title="Delete assets that are not matched"
+ )
+ render_config_path: str = Field(
+ "",
+ title="Render Config Path"
+ )
+ preroll_frames: int = Field(
+ 0,
+ title="Pre-roll frames"
+ )
+ render_format: str = Field(
+ "png",
+ title="Render format",
+ enum_resolver=_render_format_enum
+ )
+ project_setup: ProjectSetup = Field(
+ default_factory=ProjectSetup,
+ title="Project Setup",
+ )
+
+
+DEFAULT_VALUES = {
+ "level_sequences_for_layouts": False,
+ "delete_unmatched_assets": False,
+ "render_config_path": "",
+ "preroll_frames": 0,
+ "render_format": "png",
+ "project_setup": {
+ "dev_mode": False
+ }
+}
diff --git a/server_addon/unreal/server/version.py b/server_addon/unreal/server/version.py
new file mode 100644
index 0000000000..3dc1f76bc6
--- /dev/null
+++ b/server_addon/unreal/server/version.py
@@ -0,0 +1 @@
+__version__ = "0.1.0"
diff --git a/setup.py b/setup.py
index 260728dde6..4b6f286730 100644
--- a/setup.py
+++ b/setup.py
@@ -1,7 +1,6 @@
# -*- coding: utf-8 -*-
"""Setup info for building OpenPype 3.0."""
import os
-import sys
import re
import platform
import distutils.spawn
@@ -125,7 +124,6 @@ bin_includes = [
include_files = [
"igniter",
"openpype",
- "common",
"schema",
"LICENSE",
"README.md"
@@ -170,22 +168,7 @@ executables = [
target_name="openpype_console",
icon=icon_path.as_posix()
),
- Executable(
- "ayon_start.py",
- base=base,
- target_name="ayon",
- icon=icon_path.as_posix()
- ),
]
-if IS_WINDOWS:
- executables.append(
- Executable(
- "ayon_start.py",
- base=None,
- target_name="ayon_console",
- icon=icon_path.as_posix()
- )
- )
if IS_LINUX:
executables.append(
diff --git a/common/ayon_common/distribution/file_handler.py b/tests/lib/file_handler.py
similarity index 100%
rename from common/ayon_common/distribution/file_handler.py
rename to tests/lib/file_handler.py
diff --git a/tests/lib/testing_classes.py b/tests/lib/testing_classes.py
index f04607dc27..2af4af02de 100644
--- a/tests/lib/testing_classes.py
+++ b/tests/lib/testing_classes.py
@@ -12,7 +12,7 @@ import requests
import re
from tests.lib.db_handler import DBHandler
-from common.ayon_common.distribution.file_handler import RemoteFileHandler
+from tests.lib.file_handler import RemoteFileHandler
from openpype.modules import ModulesManager
from openpype.settings import get_project_settings
diff --git a/tools/run_tray_ayon.ps1 b/tools/run_tray_ayon.ps1
deleted file mode 100644
index 54a80f93fd..0000000000
--- a/tools/run_tray_ayon.ps1
+++ /dev/null
@@ -1,41 +0,0 @@
-<#
-.SYNOPSIS
- Helper script AYON Tray.
-
-.DESCRIPTION
-
-
-.EXAMPLE
-
-PS> .\run_tray.ps1
-
-#>
-$current_dir = Get-Location
-$script_dir = Split-Path -Path $MyInvocation.MyCommand.Definition -Parent
-$ayon_root = (Get-Item $script_dir).parent.FullName
-
-# Install PSWriteColor to support colorized output to terminal
-$env:PSModulePath = $env:PSModulePath + ";$($ayon_root)\tools\modules\powershell"
-
-$env:_INSIDE_OPENPYPE_TOOL = "1"
-
-# make sure Poetry is in PATH
-if (-not (Test-Path 'env:POETRY_HOME')) {
- $env:POETRY_HOME = "$ayon_root\.poetry"
-}
-$env:PATH = "$($env:PATH);$($env:POETRY_HOME)\bin"
-
-
-Set-Location -Path $ayon_root
-
-Write-Color -Text ">>> ", "Reading Poetry ... " -Color Green, Gray -NoNewline
-if (-not (Test-Path -PathType Container -Path "$($env:POETRY_HOME)\bin")) {
- Write-Color -Text "NOT FOUND" -Color Yellow
- Write-Color -Text "*** ", "We need to install Poetry create virtual env first ..." -Color Yellow, Gray
- & "$ayon_root\tools\create_env.ps1"
-} else {
- Write-Color -Text "OK" -Color Green
-}
-
-& "$($env:POETRY_HOME)\bin\poetry" run python "$($ayon_root)\ayon_start.py" tray --debug
-Set-Location -Path $current_dir
diff --git a/tools/run_tray_ayon.sh b/tools/run_tray_ayon.sh
deleted file mode 100755
index 3039750b87..0000000000
--- a/tools/run_tray_ayon.sh
+++ /dev/null
@@ -1,78 +0,0 @@
-#!/usr/bin/env bash
-# Run AYON Tray
-
-# Colors for terminal
-
-RST='\033[0m' # Text Reset
-
-# Regular Colors
-Black='\033[0;30m' # Black
-Red='\033[0;31m' # Red
-Green='\033[0;32m' # Green
-Yellow='\033[0;33m' # Yellow
-Blue='\033[0;34m' # Blue
-Purple='\033[0;35m' # Purple
-Cyan='\033[0;36m' # Cyan
-White='\033[0;37m' # White
-
-# Bold
-BBlack='\033[1;30m' # Black
-BRed='\033[1;31m' # Red
-BGreen='\033[1;32m' # Green
-BYellow='\033[1;33m' # Yellow
-BBlue='\033[1;34m' # Blue
-BPurple='\033[1;35m' # Purple
-BCyan='\033[1;36m' # Cyan
-BWhite='\033[1;37m' # White
-
-# Bold High Intensity
-BIBlack='\033[1;90m' # Black
-BIRed='\033[1;91m' # Red
-BIGreen='\033[1;92m' # Green
-BIYellow='\033[1;93m' # Yellow
-BIBlue='\033[1;94m' # Blue
-BIPurple='\033[1;95m' # Purple
-BICyan='\033[1;96m' # Cyan
-BIWhite='\033[1;97m' # White
-
-
-##############################################################################
-# Return absolute path
-# Globals:
-# None
-# Arguments:
-# Path to resolve
-# Returns:
-# None
-###############################################################################
-realpath () {
- echo $(cd $(dirname "$1"); pwd)/$(basename "$1")
-}
-
-# Main
-main () {
- # Directories
- ayon_root=$(realpath $(dirname $(dirname "${BASH_SOURCE[0]}")))
-
- _inside_openpype_tool="1"
-
- if [[ -z $POETRY_HOME ]]; then
- export POETRY_HOME="$ayon_root/.poetry"
- fi
-
- echo -e "${BIGreen}>>>${RST} Reading Poetry ... \c"
- if [ -f "$POETRY_HOME/bin/poetry" ]; then
- echo -e "${BIGreen}OK${RST}"
- else
- echo -e "${BIYellow}NOT FOUND${RST}"
- echo -e "${BIYellow}***${RST} We need to install Poetry and virtual env ..."
- . "$ayon_root/tools/create_env.sh" || { echo -e "${BIRed}!!!${RST} Poetry installation failed"; return; }
- fi
-
- pushd "$ayon_root" > /dev/null || return > /dev/null
-
- echo -e "${BIGreen}>>>${RST} Running AYON Tray with debug option ..."
- "$POETRY_HOME/bin/poetry" run python3 "$ayon_root/ayon_start.py" tray --debug
-}
-
-main
diff --git a/website/docs/admin_hosts_resolve.md b/website/docs/admin_hosts_resolve.md
index 09e7df1d9f..8bb8440f78 100644
--- a/website/docs/admin_hosts_resolve.md
+++ b/website/docs/admin_hosts_resolve.md
@@ -4,100 +4,38 @@ title: DaVinci Resolve Setup
sidebar_label: DaVinci Resolve
---
-import Tabs from '@theme/Tabs';
-import TabItem from '@theme/TabItem';
+:::warning
+Only Resolve Studio is supported due to Python API limitation in Resolve (free).
+:::
## Resolve requirements
Due to the way resolve handles python and python scripts there are a few steps required steps needed to be done on any machine that will be using OpenPype with resolve.
-### Installing Resolve's own python 3.6 interpreter.
-Resolve uses a hardcoded method to look for the python executable path. All of tho following paths are defined automatically by Python msi installer. We are using Python 3.6.2.
+## Basic setup
-
+- Supported version is up to v18
+- Install Python 3.6.2 (latest tested v17) or up to 3.9.13 (latest tested on v18)
+- pip install PySide2:
+ - Python 3.9.*: open terminal and go to python.exe directory, then `python -m pip install PySide2`
+- pip install OpenTimelineIO:
+ - Python 3.9.*: open terminal and go to python.exe directory, then `python -m pip install OpenTimelineIO`
+ - Python 3.6: open terminal and go to python.exe directory, then `python -m pip install git+https://github.com/PixarAnimationStudios/OpenTimelineIO.git@5aa24fbe89d615448876948fe4b4900455c9a3e8` and move built files from `./Lib/site-packages/opentimelineio/cxx-libs/bin and lib` to `./Lib/site-packages/opentimelineio/`. I was building it on Win10 machine with Visual Studio Community 2019 and
+  with installed CMake in PATH.
+- make sure Resolve Fusion (Fusion Tab/menu/Fusion/Fusion Settings) is set to Python 3.6
+ 
+- Open OpenPype **Tray/Admin/Studio settings** > `applications/resolve/environment` and add Python3 path to `RESOLVE_PYTHON3_HOME` platform related.
-
+## Editorial setup
-`%LOCALAPPDATA%\Programs\Python\Python36`
+This is how it looks on my testing project timeline
+
+Notice I had renamed tracks to `main` (holding metadata markers) and `review` used for generating review data with ffmpeg confersion to jpg sequence.
-
-
-
-`/opt/Python/3.6/bin`
-
-
-
-
-`~/Library/Python/3.6/bin`
-
-
-
-
-
-### Installing PySide2 into python 3.6 for correct gui work
-
-OpenPype is using its own window widget inside Resolve, for that reason PySide2 has to be installed into the python 3.6 (as explained above).
-
-
-
-
-
-paste to any terminal of your choice
-
-```bash
-%LOCALAPPDATA%\Programs\Python\Python36\python.exe -m pip install PySide2
-```
-
-
-
-
-paste to any terminal of your choice
-
-```bash
-/opt/Python/3.6/bin/python -m pip install PySide2
-```
-
-
-
-
-paste to any terminal of your choice
-
-```bash
-~/Library/Python/3.6/bin/python -m pip install PySide2
-```
-
-
-
-
-
-
-### Set Resolve's Fusion settings for Python 3.6 interpereter
-
-
-
-
-As it is shown in below picture you have to go to Fusion Tab and then in Fusion menu find Fusion Settings. Go to Fusion/Script and find Default Python Version and switch to Python 3.6
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
+1. you need to start OpenPype menu from Resolve/EditTab/Menu/Workspace/Scripts/Comp/**__OpenPype_Menu__**
+2. then select any clips in `main` track and change their color to `Chocolate`
+3. in OpenPype Menu select `Create`
+4. in Creator select `Create Publishable Clip [New]` (temporary name)
+5. set `Rename clips` to True, Master Track to `main` and Use review track to `review` as in picture
+ 
+6. after you hit `ok` all clips are colored to `ping` and marked with openpype metadata tag
+7. git `Publish` on openpype menu and see that all had been collected correctly. That is the last step for now as rest is Work in progress. Next steps will follow.