mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-24 21:04:40 +01:00
Merge branch 'develop' into bugfix/traypublish-editorial-avoid-audio-track
This commit is contained in:
commit
2669a6d923
37 changed files with 855 additions and 162 deletions
8
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
8
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
|
|
@ -35,6 +35,10 @@ body:
|
|||
label: Version
|
||||
description: What version are you running? Look to OpenPype Tray
|
||||
options:
|
||||
- 3.18.7-nightly.2
|
||||
- 3.18.7-nightly.1
|
||||
- 3.18.6
|
||||
- 3.18.6-nightly.2
|
||||
- 3.18.6-nightly.1
|
||||
- 3.18.5
|
||||
- 3.18.5-nightly.3
|
||||
|
|
@ -131,10 +135,6 @@ body:
|
|||
- 3.15.10-nightly.1
|
||||
- 3.15.9
|
||||
- 3.15.9-nightly.2
|
||||
- 3.15.9-nightly.1
|
||||
- 3.15.8
|
||||
- 3.15.8-nightly.3
|
||||
- 3.15.8-nightly.2
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
|
|
|
|||
126
CHANGELOG.md
126
CHANGELOG.md
|
|
@ -1,6 +1,132 @@
|
|||
# Changelog
|
||||
|
||||
|
||||
## [3.18.6](https://github.com/ynput/OpenPype/tree/3.18.6)
|
||||
|
||||
|
||||
[Full Changelog](https://github.com/ynput/OpenPype/compare/3.18.5...3.18.6)
|
||||
|
||||
### **🚀 Enhancements**
|
||||
|
||||
|
||||
<details>
|
||||
<summary>AYON: Use `SettingsField` from ayon server <a href="https://github.com/ynput/OpenPype/pull/6173">#6173</a></summary>
|
||||
|
||||
This is preparation for new version of pydantic which will require to customize the field class for AYON purposes as raw pydantic Field could not be used.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Nuke: Expose write knobs - OP-7592 <a href="https://github.com/ynput/OpenPype/pull/6137">#6137</a></summary>
|
||||
|
||||
This PR adds `exposed_knobs` to the creator plugins settings at `ayon+settings://nuke/create/CreateWriteRender/exposed_knobs`.When exposed knobs will be linked from the write node to the outside publish group, for users to adjust.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>AYON: Remove kitsu addon <a href="https://github.com/ynput/OpenPype/pull/6172">#6172</a></summary>
|
||||
|
||||
Removed kitsu addon from server addons because already has own repository.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
### **🐛 Bug fixes**
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Fusion: provide better logging for validate saver crash due type error <a href="https://github.com/ynput/OpenPype/pull/6082">#6082</a></summary>
|
||||
|
||||
Handles reported issue for `NoneType` error thrown in conversion `int(tool["Comments"][frame])`. It is most likely happening when saver node has no input connections.There is a validator for that, but it might be not obvious, that this error is caused by missing input connections and it has been already reported by `"Validate Saver Has Input"`.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Workfile Template Builder: Use correct variable in create placeholder <a href="https://github.com/ynput/OpenPype/pull/6141">#6141</a></summary>
|
||||
|
||||
Use correct variable where failed instances are stored for validation.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>ExtractOIIOTranscode: Missing product_names to subsets conversion <a href="https://github.com/ynput/OpenPype/pull/6159">#6159</a></summary>
|
||||
|
||||
The `Product Names` filtering should be fixed with this.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Blender: Fix missing animation data when updating blend assets <a href="https://github.com/ynput/OpenPype/pull/6165">#6165</a></summary>
|
||||
|
||||
Fix missing animation data when updating blend assets.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>TrayPublisher: Pre-fill of version works in AYON <a href="https://github.com/ynput/OpenPype/pull/6180">#6180</a></summary>
|
||||
|
||||
Use `folderPath` instead of `asset` in AYON mode to calculate next available version.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
### **🔀 Refactored code**
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Chore: remove Muster <a href="https://github.com/ynput/OpenPype/pull/6085">#6085</a></summary>
|
||||
|
||||
Muster isn't maintained for a long time and it wasn't working anyway. This is removing related code from the code base. If there is renewed interest in Muster, it needs to be re-implemented in modern AYON compatible way.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
### **Merged pull requests**
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Maya: change label in the render settings to be more readable <a href="https://github.com/ynput/OpenPype/pull/6134">#6134</a></summary>
|
||||
|
||||
AYON replacement for #5713.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.18.5](https://github.com/ynput/OpenPype/tree/3.18.5)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -43,7 +43,8 @@ from .lib import (
|
|||
get_node_data,
|
||||
set_node_data,
|
||||
update_node_data,
|
||||
create_write_node
|
||||
create_write_node,
|
||||
link_knobs
|
||||
)
|
||||
from .utils import (
|
||||
colorspace_exists_on_node,
|
||||
|
|
@ -95,6 +96,7 @@ __all__ = (
|
|||
"set_node_data",
|
||||
"update_node_data",
|
||||
"create_write_node",
|
||||
"link_knobs",
|
||||
|
||||
"colorspace_exists_on_node",
|
||||
"get_colorspace_list",
|
||||
|
|
|
|||
|
|
@ -3499,3 +3499,27 @@ def create_camera_node_by_version():
|
|||
return nuke.createNode("Camera4")
|
||||
else:
|
||||
return nuke.createNode("Camera2")
|
||||
|
||||
|
||||
def link_knobs(knobs, node, group_node):
|
||||
"""Link knobs from inside `group_node`"""
|
||||
|
||||
missing_knobs = []
|
||||
for knob in knobs:
|
||||
if knob in group_node.knobs():
|
||||
continue
|
||||
|
||||
if knob not in node.knobs().keys():
|
||||
missing_knobs.append(knob)
|
||||
|
||||
link = nuke.Link_Knob("")
|
||||
link.makeLink(node.name(), knob)
|
||||
link.setName(knob)
|
||||
link.setFlag(0x1000)
|
||||
group_node.addKnob(link)
|
||||
|
||||
if missing_knobs:
|
||||
raise ValueError(
|
||||
"Write node exposed knobs missing:\n\n{}\n\nPlease review"
|
||||
" project settings.".format("\n".join(missing_knobs))
|
||||
)
|
||||
|
|
|
|||
|
|
@ -44,7 +44,8 @@ from .lib import (
|
|||
get_view_process_node,
|
||||
get_viewer_config_from_string,
|
||||
deprecated,
|
||||
get_filenames_without_hash
|
||||
get_filenames_without_hash,
|
||||
link_knobs
|
||||
)
|
||||
from .pipeline import (
|
||||
list_instances,
|
||||
|
|
@ -1344,3 +1345,11 @@ def _remove_old_knobs(node):
|
|||
node.removeKnob(knob)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
|
||||
def exposed_write_knobs(settings, plugin_name, instance_node):
|
||||
exposed_knobs = settings["nuke"]["create"][plugin_name]["exposed_knobs"]
|
||||
if exposed_knobs:
|
||||
instance_node.addKnob(nuke.Text_Knob('', 'Write Knobs'))
|
||||
write_node = nuke.allNodes(group=instance_node, filter="Write")[0]
|
||||
link_knobs(exposed_knobs, write_node, instance_node)
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ from openpype.lib import (
|
|||
EnumDef
|
||||
)
|
||||
from openpype.hosts.nuke import api as napi
|
||||
from openpype.hosts.nuke.api.plugin import exposed_write_knobs
|
||||
|
||||
|
||||
class CreateWriteImage(napi.NukeWriteCreator):
|
||||
|
|
@ -132,6 +133,10 @@ class CreateWriteImage(napi.NukeWriteCreator):
|
|||
instance.data_to_store()
|
||||
)
|
||||
|
||||
exposed_write_knobs(
|
||||
self.project_settings, self.__class__.__name__, instance_node
|
||||
)
|
||||
|
||||
return instance
|
||||
|
||||
except Exception as er:
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ from openpype.lib import (
|
|||
BoolDef
|
||||
)
|
||||
from openpype.hosts.nuke import api as napi
|
||||
from openpype.hosts.nuke.api.plugin import exposed_write_knobs
|
||||
|
||||
|
||||
class CreateWritePrerender(napi.NukeWriteCreator):
|
||||
|
|
@ -119,6 +120,10 @@ class CreateWritePrerender(napi.NukeWriteCreator):
|
|||
instance.data_to_store()
|
||||
)
|
||||
|
||||
exposed_write_knobs(
|
||||
self.project_settings, self.__class__.__name__, instance_node
|
||||
)
|
||||
|
||||
return instance
|
||||
|
||||
except Exception as er:
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ from openpype.lib import (
|
|||
BoolDef
|
||||
)
|
||||
from openpype.hosts.nuke import api as napi
|
||||
from openpype.hosts.nuke.api.plugin import exposed_write_knobs
|
||||
|
||||
|
||||
class CreateWriteRender(napi.NukeWriteCreator):
|
||||
|
|
@ -113,6 +114,10 @@ class CreateWriteRender(napi.NukeWriteCreator):
|
|||
instance.data_to_store()
|
||||
)
|
||||
|
||||
exposed_write_knobs(
|
||||
self.project_settings, self.__class__.__name__, instance_node
|
||||
)
|
||||
|
||||
return instance
|
||||
|
||||
except Exception as er:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,77 @@
|
|||
import pyblish.api
|
||||
|
||||
from openpype.pipeline.publish import get_errored_instances_from_context
|
||||
from openpype.hosts.nuke.api.lib import link_knobs
|
||||
from openpype.pipeline.publish import (
|
||||
OptionalPyblishPluginMixin,
|
||||
PublishValidationError
|
||||
)
|
||||
|
||||
|
||||
class RepairExposedKnobs(pyblish.api.Action):
|
||||
label = "Repair"
|
||||
on = "failed"
|
||||
icon = "wrench"
|
||||
|
||||
def process(self, context, plugin):
|
||||
instances = get_errored_instances_from_context(context)
|
||||
|
||||
for instance in instances:
|
||||
child_nodes = (
|
||||
instance.data.get("transientData", {}).get("childNodes")
|
||||
or instance
|
||||
)
|
||||
|
||||
write_group_node = instance.data["transientData"]["node"]
|
||||
# get write node from inside of group
|
||||
write_node = None
|
||||
for x in child_nodes:
|
||||
if x.Class() == "Write":
|
||||
write_node = x
|
||||
|
||||
plugin_name = plugin.families_mapping[instance.data["family"]]
|
||||
nuke_settings = instance.context.data["project_settings"]["nuke"]
|
||||
create_settings = nuke_settings["create"][plugin_name]
|
||||
exposed_knobs = create_settings["exposed_knobs"]
|
||||
link_knobs(exposed_knobs, write_node, write_group_node)
|
||||
|
||||
|
||||
class ValidateExposedKnobs(
|
||||
OptionalPyblishPluginMixin,
|
||||
pyblish.api.InstancePlugin
|
||||
):
|
||||
""" Validate write node exposed knobs.
|
||||
|
||||
Compare exposed linked knobs to settings.
|
||||
"""
|
||||
|
||||
order = pyblish.api.ValidatorOrder
|
||||
optional = True
|
||||
families = ["render", "prerender", "image"]
|
||||
label = "Validate Exposed Knobs"
|
||||
actions = [RepairExposedKnobs]
|
||||
hosts = ["nuke"]
|
||||
families_mapping = {
|
||||
"render": "CreateWriteRender",
|
||||
"prerender": "CreateWritePrerender",
|
||||
"image": "CreateWriteImage"
|
||||
}
|
||||
|
||||
def process(self, instance):
|
||||
if not self.is_active(instance.data):
|
||||
return
|
||||
|
||||
plugin = self.families_mapping[instance.data["family"]]
|
||||
group_node = instance.data["transientData"]["node"]
|
||||
nuke_settings = instance.context.data["project_settings"]["nuke"]
|
||||
create_settings = nuke_settings["create"][plugin]
|
||||
exposed_knobs = create_settings["exposed_knobs"]
|
||||
unexposed_knobs = []
|
||||
for knob in exposed_knobs:
|
||||
if knob not in group_node.knobs():
|
||||
unexposed_knobs.append(knob)
|
||||
|
||||
if unexposed_knobs:
|
||||
raise PublishValidationError(
|
||||
"Missing exposed knobs: {}".format(unexposed_knobs)
|
||||
)
|
||||
|
|
@ -10,7 +10,7 @@ from openpype.hosts.nuke.api.lib import (
|
|||
|
||||
from openpype.pipeline.publish import (
|
||||
PublishXmlValidationError,
|
||||
OptionalPyblishPluginMixin,
|
||||
OptionalPyblishPluginMixin
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -3,12 +3,11 @@ import sys
|
|||
import contextlib
|
||||
import traceback
|
||||
|
||||
from qtpy import QtWidgets
|
||||
|
||||
from openpype.lib import env_value_to_bool, Logger
|
||||
from openpype.modules import ModulesManager
|
||||
from openpype.pipeline import install_host
|
||||
from openpype.tools.utils import host_tools
|
||||
from openpype.tools.utils import get_openpype_qt_app
|
||||
from openpype.tests.lib import is_in_tests
|
||||
|
||||
from .launch_logic import ProcessLauncher, stub
|
||||
|
|
@ -30,7 +29,7 @@ def main(*subprocess_args):
|
|||
|
||||
# coloring in StdOutBroker
|
||||
os.environ["OPENPYPE_LOG_NO_COLORS"] = "False"
|
||||
app = QtWidgets.QApplication([])
|
||||
app = get_openpype_qt_app()
|
||||
app.setQuitOnLastWindowClosed(False)
|
||||
|
||||
launcher = ProcessLauncher(subprocess_args)
|
||||
|
|
|
|||
|
|
@ -1,10 +1,13 @@
|
|||
import os
|
||||
|
||||
import click
|
||||
|
||||
from openpype.lib import get_openpype_execute_args
|
||||
from openpype.lib.execute import run_detached_process
|
||||
from openpype.modules import OpenPypeModule, ITrayAction, IHostAddon
|
||||
from openpype.modules import (
|
||||
click_wrap,
|
||||
OpenPypeModule,
|
||||
ITrayAction,
|
||||
IHostAddon,
|
||||
)
|
||||
|
||||
STANDALONEPUBLISH_ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
|
|
@ -37,10 +40,10 @@ class StandAlonePublishAddon(OpenPypeModule, ITrayAction, IHostAddon):
|
|||
run_detached_process(args)
|
||||
|
||||
def cli(self, click_group):
|
||||
click_group.add_command(cli_main)
|
||||
click_group.add_command(cli_main.to_click_obj())
|
||||
|
||||
|
||||
@click.group(
|
||||
@click_wrap.group(
|
||||
StandAlonePublishAddon.name,
|
||||
help="StandalonePublisher related commands.")
|
||||
def cli_main():
|
||||
|
|
|
|||
|
|
@ -1,10 +1,13 @@
|
|||
import os
|
||||
|
||||
import click
|
||||
|
||||
from openpype.lib import get_openpype_execute_args
|
||||
from openpype.lib.execute import run_detached_process
|
||||
from openpype.modules import OpenPypeModule, ITrayAction, IHostAddon
|
||||
from openpype.modules import (
|
||||
click_wrap,
|
||||
OpenPypeModule,
|
||||
ITrayAction,
|
||||
IHostAddon,
|
||||
)
|
||||
|
||||
TRAYPUBLISH_ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
|
|
@ -38,10 +41,12 @@ class TrayPublishAddon(OpenPypeModule, IHostAddon, ITrayAction):
|
|||
run_detached_process(args)
|
||||
|
||||
def cli(self, click_group):
|
||||
click_group.add_command(cli_main)
|
||||
click_group.add_command(cli_main.to_click_obj())
|
||||
|
||||
|
||||
@click.group(TrayPublishAddon.name, help="TrayPublisher related commands.")
|
||||
@click_wrap.group(
|
||||
TrayPublishAddon.name,
|
||||
help="TrayPublisher related commands.")
|
||||
def cli_main():
|
||||
pass
|
||||
|
||||
|
|
|
|||
|
|
@ -221,9 +221,16 @@ class SettingsCreator(TrayPublishCreator):
|
|||
):
|
||||
filtered_instance_data.append(instance)
|
||||
|
||||
asset_names = {
|
||||
instance["asset"]
|
||||
for instance in filtered_instance_data}
|
||||
if AYON_SERVER_ENABLED:
|
||||
asset_names = {
|
||||
instance["folderPath"]
|
||||
for instance in filtered_instance_data
|
||||
}
|
||||
else:
|
||||
asset_names = {
|
||||
instance["asset"]
|
||||
for instance in filtered_instance_data
|
||||
}
|
||||
subset_names = {
|
||||
instance["subset"]
|
||||
for instance in filtered_instance_data}
|
||||
|
|
@ -231,7 +238,10 @@ class SettingsCreator(TrayPublishCreator):
|
|||
asset_names, subset_names
|
||||
)
|
||||
for instance in filtered_instance_data:
|
||||
asset_name = instance["asset"]
|
||||
if AYON_SERVER_ENABLED:
|
||||
asset_name = instance["folderPath"]
|
||||
else:
|
||||
asset_name = instance["asset"]
|
||||
subset_name = instance["subset"]
|
||||
version = subset_docs_by_asset_id[asset_name][subset_name]
|
||||
instance["creator_attributes"]["version_to_use"] = version
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
import os
|
||||
|
||||
import click
|
||||
|
||||
from openpype.modules import OpenPypeModule, IHostAddon
|
||||
from openpype.modules import click_wrap, OpenPypeModule, IHostAddon
|
||||
|
||||
WEBPUBLISHER_ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
|
|
@ -38,10 +36,10 @@ class WebpublisherAddon(OpenPypeModule, IHostAddon):
|
|||
)
|
||||
|
||||
def cli(self, click_group):
|
||||
click_group.add_command(cli_main)
|
||||
click_group.add_command(cli_main.to_click_obj())
|
||||
|
||||
|
||||
@click.group(
|
||||
@click_wrap.group(
|
||||
WebpublisherAddon.name,
|
||||
help="Webpublisher related commands.")
|
||||
def cli_main():
|
||||
|
|
@ -49,10 +47,10 @@ def cli_main():
|
|||
|
||||
|
||||
@cli_main.command()
|
||||
@click.argument("path")
|
||||
@click.option("-u", "--user", help="User email address")
|
||||
@click.option("-p", "--project", help="Project")
|
||||
@click.option("-t", "--targets", help="Targets", default=None,
|
||||
@click_wrap.argument("path")
|
||||
@click_wrap.option("-u", "--user", help="User email address")
|
||||
@click_wrap.option("-p", "--project", help="Project")
|
||||
@click_wrap.option("-t", "--targets", help="Targets", default=None,
|
||||
multiple=True)
|
||||
def publish(project, path, user=None, targets=None):
|
||||
"""Start publishing (Inner command).
|
||||
|
|
@ -67,11 +65,11 @@ def publish(project, path, user=None, targets=None):
|
|||
|
||||
|
||||
@cli_main.command()
|
||||
@click.argument("path")
|
||||
@click.option("-p", "--project", help="Project")
|
||||
@click.option("-h", "--host", help="Host")
|
||||
@click.option("-u", "--user", help="User email address")
|
||||
@click.option("-t", "--targets", help="Targets", default=None,
|
||||
@click_wrap.argument("path")
|
||||
@click_wrap.option("-p", "--project", help="Project")
|
||||
@click_wrap.option("-h", "--host", help="Host")
|
||||
@click_wrap.option("-u", "--user", help="User email address")
|
||||
@click_wrap.option("-t", "--targets", help="Targets", default=None,
|
||||
multiple=True)
|
||||
def publishfromapp(project, path, host, user=None, targets=None):
|
||||
"""Start publishing through application (Inner command).
|
||||
|
|
@ -86,10 +84,10 @@ def publishfromapp(project, path, host, user=None, targets=None):
|
|||
|
||||
|
||||
@cli_main.command()
|
||||
@click.option("-e", "--executable", help="Executable")
|
||||
@click.option("-u", "--upload_dir", help="Upload dir")
|
||||
@click.option("-h", "--host", help="Host", default=None)
|
||||
@click.option("-p", "--port", help="Port", default=None)
|
||||
@click_wrap.option("-e", "--executable", help="Executable")
|
||||
@click_wrap.option("-u", "--upload_dir", help="Upload dir")
|
||||
@click_wrap.option("-h", "--host", help="Host", default=None)
|
||||
@click_wrap.option("-p", "--port", help="Port", default=None)
|
||||
def webserver(executable, upload_dir, host=None, port=None):
|
||||
"""Start service for communication with Webpublish Front end.
|
||||
|
||||
|
|
|
|||
|
|
@ -1227,12 +1227,8 @@ def get_rescaled_command_arguments(
|
|||
target_par = target_par or 1.0
|
||||
input_par = 1.0
|
||||
|
||||
# ffmpeg command
|
||||
input_file_metadata = get_ffprobe_data(input_path, logger=log)
|
||||
stream = input_file_metadata["streams"][0]
|
||||
input_width = int(stream["width"])
|
||||
input_height = int(stream["height"])
|
||||
stream_input_par = stream.get("sample_aspect_ratio")
|
||||
input_height, input_width, stream_input_par = _get_image_dimensions(
|
||||
application, input_path, log)
|
||||
if stream_input_par:
|
||||
input_par = (
|
||||
float(stream_input_par.split(":")[0])
|
||||
|
|
@ -1345,6 +1341,48 @@ def get_rescaled_command_arguments(
|
|||
return command_args
|
||||
|
||||
|
||||
def _get_image_dimensions(application, input_path, log):
|
||||
"""Uses 'ffprobe' first and then 'oiiotool' if available to get dim.
|
||||
|
||||
Args:
|
||||
application (str): "oiiotool"|"ffmpeg"
|
||||
input_path (str): path to image file
|
||||
log (Optional[logging.Logger]): Logger used for logging.
|
||||
Returns:
|
||||
(tuple) (int, int, dict) - (height, width, sample_aspect_ratio)
|
||||
Raises:
|
||||
RuntimeError if image dimensions couldn't be parsed out.
|
||||
"""
|
||||
# ffmpeg command
|
||||
input_file_metadata = get_ffprobe_data(input_path, logger=log)
|
||||
input_width = input_height = 0
|
||||
stream = next(
|
||||
(
|
||||
s for s in input_file_metadata["streams"]
|
||||
if s.get("codec_type") == "video"
|
||||
),
|
||||
{}
|
||||
)
|
||||
if stream:
|
||||
input_width = int(stream["width"])
|
||||
input_height = int(stream["height"])
|
||||
|
||||
# fallback for weird files with width=0, height=0
|
||||
if (input_width == 0 or input_height == 0) and application == "oiiotool":
|
||||
# Load info about file from oiio tool
|
||||
input_info = get_oiio_info_for_input(input_path, logger=log)
|
||||
if input_info:
|
||||
input_width = int(input_info["width"])
|
||||
input_height = int(input_info["height"])
|
||||
|
||||
if input_width == 0 or input_height == 0:
|
||||
raise RuntimeError("Couldn't read {} either "
|
||||
"with ffprobe or oiiotool".format(input_path))
|
||||
|
||||
stream_input_par = stream.get("sample_aspect_ratio")
|
||||
return input_height, input_width, stream_input_par
|
||||
|
||||
|
||||
def convert_color_values(application, color_value):
|
||||
"""Get color mapping for ffmpeg and oiiotool.
|
||||
Args:
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from . import click_wrap
|
||||
from .interfaces import (
|
||||
ILaunchHookPaths,
|
||||
IPluginPaths,
|
||||
|
|
@ -28,6 +29,8 @@ from .base import (
|
|||
|
||||
|
||||
__all__ = (
|
||||
"click_wrap",
|
||||
|
||||
"ILaunchHookPaths",
|
||||
"IPluginPaths",
|
||||
"ITrayModule",
|
||||
|
|
|
|||
365
openpype/modules/click_wrap.py
Normal file
365
openpype/modules/click_wrap.py
Normal file
|
|
@ -0,0 +1,365 @@
|
|||
"""Simplified wrapper for 'click' python module.
|
||||
|
||||
Module 'click' is used as main cli handler in AYON/OpenPype. Addons can
|
||||
register their own subcommands with options. This wrapper allows to define
|
||||
commands and options as with 'click', but without any dependency.
|
||||
|
||||
Why not to use 'click' directly? Version of 'click' used in AYON/OpenPype
|
||||
is not compatible with 'click' version used in some DCCs (e.g. Houdini 20+).
|
||||
And updating 'click' would break other DCCs.
|
||||
|
||||
How to use it? If you already have cli commands defined in addon, just replace
|
||||
'click' with 'click_wrap' and it should work and modify your addon's cli
|
||||
method to convert 'click_wrap' object to 'click' object.
|
||||
|
||||
Before
|
||||
```python
|
||||
import click
|
||||
from openpype.modules import OpenPypeModule
|
||||
|
||||
|
||||
class ExampleAddon(OpenPypeModule):
|
||||
name = "example"
|
||||
|
||||
def cli(self, click_group):
|
||||
click_group.add_command(cli_main)
|
||||
|
||||
|
||||
@click.group(ExampleAddon.name, help="Example addon")
|
||||
def cli_main():
|
||||
pass
|
||||
|
||||
|
||||
@cli_main.command(help="Example command")
|
||||
@click.option("--arg1", help="Example argument 1", default="default1")
|
||||
@click.option("--arg2", help="Example argument 2", is_flag=True)
|
||||
def mycommand(arg1, arg2):
|
||||
print(arg1, arg2)
|
||||
```
|
||||
|
||||
Now
|
||||
```
|
||||
from openpype import click_wrap
|
||||
from openpype.modules import OpenPypeModule
|
||||
|
||||
|
||||
class ExampleAddon(OpenPypeModule):
|
||||
name = "example"
|
||||
|
||||
def cli(self, click_group):
|
||||
click_group.add_command(cli_main.to_click_obj())
|
||||
|
||||
|
||||
@click_wrap.group(ExampleAddon.name, help="Example addon")
|
||||
def cli_main():
|
||||
pass
|
||||
|
||||
|
||||
@cli_main.command(help="Example command")
|
||||
@click_wrap.option("--arg1", help="Example argument 1", default="default1")
|
||||
@click_wrap.option("--arg2", help="Example argument 2", is_flag=True)
|
||||
def mycommand(arg1, arg2):
|
||||
print(arg1, arg2)
|
||||
```
|
||||
|
||||
|
||||
Added small enhancements:
|
||||
- most of the methods can be used as chained calls
|
||||
- functions/methods 'command' and 'group' can be used in a way that
|
||||
first argument is callback function and the rest are arguments
|
||||
for click
|
||||
|
||||
Example:
|
||||
```python
|
||||
from openpype import click_wrap
|
||||
from openpype.modules import OpenPypeModule
|
||||
|
||||
|
||||
class ExampleAddon(OpenPypeModule):
|
||||
name = "example"
|
||||
|
||||
def cli(self, click_group):
|
||||
# Define main command (name 'example')
|
||||
main = click_wrap.group(
|
||||
self._cli_main, name=self.name, help="Example addon"
|
||||
)
|
||||
# Add subcommand (name 'mycommand')
|
||||
(
|
||||
main.command(
|
||||
self._cli_command, name="mycommand", help="Example command"
|
||||
)
|
||||
.option(
|
||||
"--arg1", help="Example argument 1", default="default1"
|
||||
)
|
||||
.option(
|
||||
"--arg2", help="Example argument 2", is_flag=True,
|
||||
)
|
||||
)
|
||||
# Convert main command to click object and add it to parent group
|
||||
click_group.add_command(main.to_click_obj())
|
||||
|
||||
def _cli_main(self):
|
||||
pass
|
||||
|
||||
def _cli_command(self, arg1, arg2):
|
||||
print(arg1, arg2)
|
||||
```
|
||||
|
||||
```shell
|
||||
openpype_console addon example mycommand --arg1 value1 --arg2
|
||||
```
|
||||
"""
|
||||
|
||||
import collections
|
||||
|
||||
FUNC_ATTR_NAME = "__ayon_cli_options__"
|
||||
|
||||
|
||||
class Command(object):
|
||||
def __init__(self, func, *args, **kwargs):
|
||||
# Command function
|
||||
self._func = func
|
||||
# Command definition arguments
|
||||
self._args = args
|
||||
# Command definition kwargs
|
||||
self._kwargs = kwargs
|
||||
# Both 'options' and 'arguments' are stored to the same variable
|
||||
# - keep order of options and arguments
|
||||
self._options = getattr(func, FUNC_ATTR_NAME, [])
|
||||
|
||||
def to_click_obj(self):
|
||||
"""Converts this object to click object.
|
||||
|
||||
Returns:
|
||||
click.Command: Click command object.
|
||||
"""
|
||||
return convert_to_click(self)
|
||||
|
||||
# --- Methods for 'convert_to_click' function ---
|
||||
def get_args(self):
|
||||
"""
|
||||
Returns:
|
||||
tuple: Command definition arguments.
|
||||
"""
|
||||
return self._args
|
||||
|
||||
def get_kwargs(self):
|
||||
"""
|
||||
Returns:
|
||||
dict[str, Any]: Command definition kwargs.
|
||||
"""
|
||||
return self._kwargs
|
||||
|
||||
def get_func(self):
|
||||
"""
|
||||
Returns:
|
||||
Function: Function to invoke on command trigger.
|
||||
"""
|
||||
return self._func
|
||||
|
||||
def iter_options(self):
|
||||
"""
|
||||
Yields:
|
||||
tuple[str, tuple, dict]: Option type name with args and kwargs.
|
||||
"""
|
||||
for item in self._options:
|
||||
yield item
|
||||
# -----------------------------------------------
|
||||
|
||||
def add_option(self, *args, **kwargs):
|
||||
return self.add_option_by_type("option", *args, **kwargs)
|
||||
|
||||
def add_argument(self, *args, **kwargs):
|
||||
return self.add_option_by_type("argument", *args, **kwargs)
|
||||
|
||||
option = add_option
|
||||
argument = add_argument
|
||||
|
||||
def add_option_by_type(self, option_name, *args, **kwargs):
|
||||
self._options.append((option_name, args, kwargs))
|
||||
return self
|
||||
|
||||
|
||||
class Group(Command):
|
||||
def __init__(self, func, *args, **kwargs):
|
||||
super(Group, self).__init__(func, *args, **kwargs)
|
||||
# Store sub-groupd and sub-commands to the same variable
|
||||
self._commands = []
|
||||
|
||||
# --- Methods for 'convert_to_click' function ---
|
||||
def iter_commands(self):
|
||||
for command in self._commands:
|
||||
yield command
|
||||
# -----------------------------------------------
|
||||
|
||||
def add_command(self, command):
|
||||
"""Add prepared command object as child.
|
||||
|
||||
Args:
|
||||
command (Command): Prepared command object.
|
||||
"""
|
||||
if command not in self._commands:
|
||||
self._commands.append(command)
|
||||
|
||||
def add_group(self, group):
|
||||
"""Add prepared group object as child.
|
||||
|
||||
Args:
|
||||
group (Group): Prepared group object.
|
||||
"""
|
||||
if group not in self._commands:
|
||||
self._commands.append(group)
|
||||
|
||||
def command(self, *args, **kwargs):
|
||||
"""Add child command.
|
||||
|
||||
Returns:
|
||||
Union[Command, Function]: New command object, or wrapper function.
|
||||
"""
|
||||
return self._add_new(Command, *args, **kwargs)
|
||||
|
||||
def group(self, *args, **kwargs):
|
||||
"""Add child group.
|
||||
|
||||
Returns:
|
||||
Union[Group, Function]: New group object, or wrapper function.
|
||||
"""
|
||||
return self._add_new(Group, *args, **kwargs)
|
||||
|
||||
def _add_new(self, target_cls, *args, **kwargs):
|
||||
func = None
|
||||
if args and callable(args[0]):
|
||||
args = list(args)
|
||||
func = args.pop(0)
|
||||
args = tuple(args)
|
||||
|
||||
def decorator(_func):
|
||||
out = target_cls(_func, *args, **kwargs)
|
||||
self._commands.append(out)
|
||||
return out
|
||||
|
||||
if func is not None:
|
||||
return decorator(func)
|
||||
return decorator
|
||||
|
||||
|
||||
def convert_to_click(obj_to_convert):
|
||||
"""Convert wrapped object to click object.
|
||||
|
||||
Args:
|
||||
obj_to_convert (Command): Object to convert to click object.
|
||||
|
||||
Returns:
|
||||
click.Command: Click command object.
|
||||
"""
|
||||
import click
|
||||
|
||||
commands_queue = collections.deque()
|
||||
commands_queue.append((obj_to_convert, None))
|
||||
top_obj = None
|
||||
while commands_queue:
|
||||
item = commands_queue.popleft()
|
||||
command_obj, parent_obj = item
|
||||
if not isinstance(command_obj, Command):
|
||||
raise TypeError(
|
||||
"Invalid type '{}' expected 'Command'".format(
|
||||
type(command_obj)
|
||||
)
|
||||
)
|
||||
|
||||
if isinstance(command_obj, Group):
|
||||
click_obj = (
|
||||
click.group(
|
||||
*command_obj.get_args(),
|
||||
**command_obj.get_kwargs()
|
||||
)(command_obj.get_func())
|
||||
)
|
||||
|
||||
else:
|
||||
click_obj = (
|
||||
click.command(
|
||||
*command_obj.get_args(),
|
||||
**command_obj.get_kwargs()
|
||||
)(command_obj.get_func())
|
||||
)
|
||||
|
||||
for item in command_obj.iter_options():
|
||||
option_name, args, kwargs = item
|
||||
if option_name == "option":
|
||||
click.option(*args, **kwargs)(click_obj)
|
||||
elif option_name == "argument":
|
||||
click.argument(*args, **kwargs)(click_obj)
|
||||
else:
|
||||
raise ValueError(
|
||||
"Invalid option name '{}'".format(option_name)
|
||||
)
|
||||
|
||||
if top_obj is None:
|
||||
top_obj = click_obj
|
||||
|
||||
if parent_obj is not None:
|
||||
parent_obj.add_command(click_obj)
|
||||
|
||||
if isinstance(command_obj, Group):
|
||||
for command in command_obj.iter_commands():
|
||||
commands_queue.append((command, click_obj))
|
||||
|
||||
return top_obj
|
||||
|
||||
|
||||
def group(*args, **kwargs):
|
||||
func = None
|
||||
if args and callable(args[0]):
|
||||
args = list(args)
|
||||
func = args.pop(0)
|
||||
args = tuple(args)
|
||||
|
||||
def decorator(_func):
|
||||
return Group(_func, *args, **kwargs)
|
||||
|
||||
if func is not None:
|
||||
return decorator(func)
|
||||
return decorator
|
||||
|
||||
|
||||
def command(*args, **kwargs):
|
||||
func = None
|
||||
if args and callable(args[0]):
|
||||
args = list(args)
|
||||
func = args.pop(0)
|
||||
args = tuple(args)
|
||||
|
||||
def decorator(_func):
|
||||
return Command(_func, *args, **kwargs)
|
||||
|
||||
if func is not None:
|
||||
return decorator(func)
|
||||
return decorator
|
||||
|
||||
|
||||
def argument(*args, **kwargs):
|
||||
def decorator(func):
|
||||
return _add_option_to_func(
|
||||
func, "argument", *args, **kwargs
|
||||
)
|
||||
return decorator
|
||||
|
||||
|
||||
def option(*args, **kwargs):
|
||||
def decorator(func):
|
||||
return _add_option_to_func(
|
||||
func, "option", *args, **kwargs
|
||||
)
|
||||
return decorator
|
||||
|
||||
|
||||
def _add_option_to_func(func, option_name, *args, **kwargs):
|
||||
if isinstance(func, Command):
|
||||
func.add_option_by_type(option_name, *args, **kwargs)
|
||||
return func
|
||||
|
||||
if not hasattr(func, FUNC_ATTR_NAME):
|
||||
setattr(func, FUNC_ATTR_NAME, [])
|
||||
cli_options = getattr(func, FUNC_ATTR_NAME)
|
||||
cli_options.append((option_name, args, kwargs))
|
||||
return func
|
||||
|
|
@ -8,9 +8,9 @@ in global space here until are required or used.
|
|||
"""
|
||||
|
||||
import os
|
||||
import click
|
||||
|
||||
from openpype.modules import (
|
||||
click_wrap,
|
||||
JsonFilesSettingsDef,
|
||||
OpenPypeAddOn,
|
||||
ModulesManager,
|
||||
|
|
@ -115,10 +115,12 @@ class ExampleAddon(OpenPypeAddOn, IPluginPaths, ITrayAction):
|
|||
}
|
||||
|
||||
def cli(self, click_group):
|
||||
click_group.add_command(cli_main)
|
||||
click_group.add_command(cli_main.to_click_obj())
|
||||
|
||||
|
||||
@click.group(ExampleAddon.name, help="Example addon dynamic cli commands.")
|
||||
@click_wrap.group(
|
||||
ExampleAddon.name,
|
||||
help="Example addon dynamic cli commands.")
|
||||
def cli_main():
|
||||
pass
|
||||
|
||||
|
|
|
|||
|
|
@ -3,9 +3,8 @@ import json
|
|||
import collections
|
||||
import platform
|
||||
|
||||
import click
|
||||
|
||||
from openpype.modules import (
|
||||
click_wrap,
|
||||
OpenPypeModule,
|
||||
ITrayModule,
|
||||
IPluginPaths,
|
||||
|
|
@ -489,7 +488,7 @@ class FtrackModule(
|
|||
return cred.get("username"), cred.get("api_key")
|
||||
|
||||
def cli(self, click_group):
|
||||
click_group.add_command(cli_main)
|
||||
click_group.add_command(cli_main.to_click_obj())
|
||||
|
||||
|
||||
def _check_ftrack_url(url):
|
||||
|
|
@ -540,24 +539,24 @@ def resolve_ftrack_url(url, logger=None):
|
|||
return ftrack_url
|
||||
|
||||
|
||||
@click.group(FtrackModule.name, help="Ftrack module related commands.")
|
||||
@click_wrap.group(FtrackModule.name, help="Ftrack module related commands.")
|
||||
def cli_main():
|
||||
pass
|
||||
|
||||
|
||||
@cli_main.command()
|
||||
@click.option("-d", "--debug", is_flag=True, help="Print debug messages")
|
||||
@click.option("--ftrack-url", envvar="FTRACK_SERVER",
|
||||
@click_wrap.option("-d", "--debug", is_flag=True, help="Print debug messages")
|
||||
@click_wrap.option("--ftrack-url", envvar="FTRACK_SERVER",
|
||||
help="Ftrack server url")
|
||||
@click.option("--ftrack-user", envvar="FTRACK_API_USER",
|
||||
@click_wrap.option("--ftrack-user", envvar="FTRACK_API_USER",
|
||||
help="Ftrack api user")
|
||||
@click.option("--ftrack-api-key", envvar="FTRACK_API_KEY",
|
||||
@click_wrap.option("--ftrack-api-key", envvar="FTRACK_API_KEY",
|
||||
help="Ftrack api key")
|
||||
@click.option("--legacy", is_flag=True,
|
||||
@click_wrap.option("--legacy", is_flag=True,
|
||||
help="run event server without mongo storing")
|
||||
@click.option("--clockify-api-key", envvar="CLOCKIFY_API_KEY",
|
||||
@click_wrap.option("--clockify-api-key", envvar="CLOCKIFY_API_KEY",
|
||||
help="Clockify API key.")
|
||||
@click.option("--clockify-workspace", envvar="CLOCKIFY_WORKSPACE",
|
||||
@click_wrap.option("--clockify-workspace", envvar="CLOCKIFY_WORKSPACE",
|
||||
help="Clockify workspace")
|
||||
def eventserver(
|
||||
debug,
|
||||
|
|
|
|||
|
|
@ -131,6 +131,8 @@ class PostFtrackHook(PostLaunchHook):
|
|||
for key, value in status_mapping.items():
|
||||
if key in already_tested:
|
||||
continue
|
||||
|
||||
value = value.lower()
|
||||
if actual_status in value or "__any__" in value:
|
||||
if key != "__ignore__":
|
||||
next_status_name = key
|
||||
|
|
|
|||
|
|
@ -41,8 +41,7 @@ import json
|
|||
import copy
|
||||
import platform
|
||||
|
||||
import click
|
||||
from openpype.modules import OpenPypeModule
|
||||
from openpype.modules import OpenPypeModule, click_wrap
|
||||
from openpype.settings import get_system_settings
|
||||
|
||||
|
||||
|
|
@ -153,7 +152,7 @@ class JobQueueModule(OpenPypeModule):
|
|||
return requests.get(api_path).json()
|
||||
|
||||
def cli(self, click_group):
|
||||
click_group.add_command(cli_main)
|
||||
click_group.add_command(cli_main.to_click_obj())
|
||||
|
||||
@classmethod
|
||||
def get_server_url_from_settings(cls):
|
||||
|
|
@ -213,7 +212,7 @@ class JobQueueModule(OpenPypeModule):
|
|||
return main(str(executable), server_url)
|
||||
|
||||
|
||||
@click.group(
|
||||
@click_wrap.group(
|
||||
JobQueueModule.name,
|
||||
help="Application job server. Can be used as render farm."
|
||||
)
|
||||
|
|
@ -225,8 +224,8 @@ def cli_main():
|
|||
"start_server",
|
||||
help="Start server handling workers and their jobs."
|
||||
)
|
||||
@click.option("--port", help="Server port")
|
||||
@click.option("--host", help="Server host (ip address)")
|
||||
@click_wrap.option("--port", help="Server port")
|
||||
@click_wrap.option("--host", help="Server host (ip address)")
|
||||
def cli_start_server(port, host):
|
||||
JobQueueModule.start_server(port, host)
|
||||
|
||||
|
|
@ -236,7 +235,9 @@ def cli_start_server(port, host):
|
|||
"Start a worker for a specific application. (e.g. \"tvpaint/11.5\")"
|
||||
)
|
||||
)
|
||||
@click.argument("app_name")
|
||||
@click.option("--server_url", help="Server url which handle workers and jobs.")
|
||||
@click_wrap.argument("app_name")
|
||||
@click_wrap.option(
|
||||
"--server_url",
|
||||
help="Server url which handle workers and jobs.")
|
||||
def cli_start_worker(app_name, server_url):
|
||||
JobQueueModule.start_worker(app_name, server_url)
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
"""Kitsu module."""
|
||||
|
||||
import click
|
||||
import os
|
||||
|
||||
from openpype.modules import (
|
||||
click_wrap,
|
||||
OpenPypeModule,
|
||||
IPluginPaths,
|
||||
ITrayAction,
|
||||
|
|
@ -98,17 +98,17 @@ class KitsuModule(OpenPypeModule, IPluginPaths, ITrayAction):
|
|||
}
|
||||
|
||||
def cli(self, click_group):
|
||||
click_group.add_command(cli_main)
|
||||
click_group.add_command(cli_main.to_click_obj())
|
||||
|
||||
|
||||
@click.group(KitsuModule.name, help="Kitsu dynamic cli commands.")
|
||||
@click_wrap.group(KitsuModule.name, help="Kitsu dynamic cli commands.")
|
||||
def cli_main():
|
||||
pass
|
||||
|
||||
|
||||
@cli_main.command()
|
||||
@click.option("--login", envvar="KITSU_LOGIN", help="Kitsu login")
|
||||
@click.option(
|
||||
@click_wrap.option("--login", envvar="KITSU_LOGIN", help="Kitsu login")
|
||||
@click_wrap.option(
|
||||
"--password", envvar="KITSU_PWD", help="Password for kitsu username"
|
||||
)
|
||||
def push_to_zou(login, password):
|
||||
|
|
@ -124,11 +124,11 @@ def push_to_zou(login, password):
|
|||
|
||||
|
||||
@cli_main.command()
|
||||
@click.option("-l", "--login", envvar="KITSU_LOGIN", help="Kitsu login")
|
||||
@click.option(
|
||||
@click_wrap.option("-l", "--login", envvar="KITSU_LOGIN", help="Kitsu login")
|
||||
@click_wrap.option(
|
||||
"-p", "--password", envvar="KITSU_PWD", help="Password for kitsu username"
|
||||
)
|
||||
@click.option(
|
||||
@click_wrap.option(
|
||||
"-prj",
|
||||
"--project",
|
||||
"projects",
|
||||
|
|
@ -136,7 +136,7 @@ def push_to_zou(login, password):
|
|||
default=[],
|
||||
help="Sync specific kitsu projects",
|
||||
)
|
||||
@click.option(
|
||||
@click_wrap.option(
|
||||
"-lo",
|
||||
"--listen-only",
|
||||
"listen_only",
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import copy
|
|||
import signal
|
||||
from collections import deque, defaultdict
|
||||
|
||||
import click
|
||||
from bson.objectid import ObjectId
|
||||
|
||||
from openpype.client import (
|
||||
|
|
@ -15,7 +14,12 @@ from openpype.client import (
|
|||
get_representations,
|
||||
get_representation_by_id,
|
||||
)
|
||||
from openpype.modules import OpenPypeModule, ITrayModule, IPluginPaths
|
||||
from openpype.modules import (
|
||||
OpenPypeModule,
|
||||
ITrayModule,
|
||||
IPluginPaths,
|
||||
click_wrap,
|
||||
)
|
||||
from openpype.settings import (
|
||||
get_project_settings,
|
||||
get_system_settings,
|
||||
|
|
@ -2405,7 +2409,7 @@ class SyncServerModule(OpenPypeModule, ITrayModule, IPluginPaths):
|
|||
return presets[project_name]['sites'][site_name]['root']
|
||||
|
||||
def cli(self, click_group):
|
||||
click_group.add_command(cli_main)
|
||||
click_group.add_command(cli_main.to_click_obj())
|
||||
|
||||
# Webserver module implementation
|
||||
def webserver_initialization(self, server_manager):
|
||||
|
|
@ -2417,13 +2421,15 @@ class SyncServerModule(OpenPypeModule, ITrayModule, IPluginPaths):
|
|||
)
|
||||
|
||||
|
||||
@click.group(SyncServerModule.name, help="SyncServer module related commands.")
|
||||
@click_wrap.group(
|
||||
SyncServerModule.name,
|
||||
help="SyncServer module related commands.")
|
||||
def cli_main():
|
||||
pass
|
||||
|
||||
|
||||
@cli_main.command()
|
||||
@click.option(
|
||||
@click_wrap.option(
|
||||
"-a",
|
||||
"--active_site",
|
||||
required=True,
|
||||
|
|
|
|||
|
|
@ -1458,7 +1458,7 @@ class _AyonSettingsCache:
|
|||
|
||||
variant = "production"
|
||||
if is_dev_mode_enabled():
|
||||
variant = cls._get_dev_mode_settings_variant()
|
||||
variant = cls._get_bundle_name()
|
||||
elif is_staging_enabled():
|
||||
variant = "staging"
|
||||
|
||||
|
|
@ -1474,28 +1474,6 @@ class _AyonSettingsCache:
|
|||
def _get_bundle_name(cls):
|
||||
return os.environ["AYON_BUNDLE_NAME"]
|
||||
|
||||
@classmethod
|
||||
def _get_dev_mode_settings_variant(cls):
|
||||
"""Develop mode settings variant.
|
||||
|
||||
Returns:
|
||||
str: Name of settings variant.
|
||||
"""
|
||||
|
||||
con = get_ayon_server_api_connection()
|
||||
bundles = con.get_bundles()
|
||||
user = con.get_user()
|
||||
username = user["name"]
|
||||
for bundle in bundles["bundles"]:
|
||||
if (
|
||||
bundle.get("isDev")
|
||||
and bundle.get("activeUser") == username
|
||||
):
|
||||
return bundle["name"]
|
||||
# Return fake variant - distribution logic will tell user that he
|
||||
# does not have set any dev bundle
|
||||
return "dev"
|
||||
|
||||
@classmethod
|
||||
def get_value_by_project(cls, project_name):
|
||||
cache_item = _AyonSettingsCache.cache_by_project_name[project_name]
|
||||
|
|
|
|||
|
|
@ -531,6 +531,9 @@ class FrontendLoaderController(_BaseLoaderController):
|
|||
|
||||
Product types have defined if are checked for filtering or not.
|
||||
|
||||
Args:
|
||||
project_name (Union[str, None]): Project name.
|
||||
|
||||
Returns:
|
||||
list[ProductTypeItem]: List of product type items for a project.
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -179,12 +179,15 @@ class ProductsModel:
|
|||
"""Product type items for project.
|
||||
|
||||
Args:
|
||||
project_name (str): Project name.
|
||||
project_name (Union[str, None]): Project name.
|
||||
|
||||
Returns:
|
||||
list[ProductTypeItem]: Product type items.
|
||||
"""
|
||||
|
||||
if not project_name:
|
||||
return []
|
||||
|
||||
cache = self._product_type_items_cache[project_name]
|
||||
if not cache.is_valid:
|
||||
product_types = ayon_api.get_project_product_types(project_name)
|
||||
|
|
|
|||
|
|
@ -322,6 +322,7 @@ class LoaderWindow(QtWidgets.QWidget):
|
|||
)
|
||||
|
||||
def refresh(self):
|
||||
self._reset_on_show = False
|
||||
self._controller.reset()
|
||||
|
||||
def showEvent(self, event):
|
||||
|
|
@ -332,6 +333,13 @@ class LoaderWindow(QtWidgets.QWidget):
|
|||
|
||||
self._show_timer.start()
|
||||
|
||||
def closeEvent(self, event):
|
||||
super(LoaderWindow, self).closeEvent(event)
|
||||
# Deselect project so current context will be selected
|
||||
# on next 'showEvent'
|
||||
self._controller.set_selected_project(None)
|
||||
self._reset_on_show = True
|
||||
|
||||
def keyPressEvent(self, event):
|
||||
modifiers = event.modifiers()
|
||||
ctrl_pressed = QtCore.Qt.ControlModifier & modifiers
|
||||
|
|
@ -378,8 +386,7 @@ class LoaderWindow(QtWidgets.QWidget):
|
|||
self._show_timer.stop()
|
||||
|
||||
if self._reset_on_show:
|
||||
self._reset_on_show = False
|
||||
self._controller.reset()
|
||||
self.refresh()
|
||||
|
||||
def _show_group_dialog(self):
|
||||
project_name = self._projects_combobox.get_selected_project_name()
|
||||
|
|
|
|||
|
|
@ -1212,12 +1212,12 @@ class SwitchAssetDialog(QtWidgets.QDialog):
|
|||
))
|
||||
|
||||
version_ids = set()
|
||||
version_docs_by_parent_id = {}
|
||||
version_docs_by_parent_id_and_name = collections.defaultdict(dict)
|
||||
for version_doc in version_docs:
|
||||
parent_id = version_doc["parent"]
|
||||
if parent_id not in version_docs_by_parent_id:
|
||||
version_ids.add(version_doc["_id"])
|
||||
version_docs_by_parent_id[parent_id] = version_doc
|
||||
version_ids.add(version_doc["_id"])
|
||||
product_id = version_doc["parent"]
|
||||
name = version_doc["name"]
|
||||
version_docs_by_parent_id_and_name[product_id][name] = version_doc
|
||||
|
||||
hero_version_docs_by_parent_id = {}
|
||||
for hero_version_doc in hero_version_docs:
|
||||
|
|
@ -1242,7 +1242,7 @@ class SwitchAssetDialog(QtWidgets.QDialog):
|
|||
selected_product_name,
|
||||
selected_representation,
|
||||
product_docs_by_parent_and_name,
|
||||
version_docs_by_parent_id,
|
||||
version_docs_by_parent_id_and_name,
|
||||
hero_version_docs_by_parent_id,
|
||||
repre_docs_by_parent_id_by_name,
|
||||
)
|
||||
|
|
@ -1256,10 +1256,10 @@ class SwitchAssetDialog(QtWidgets.QDialog):
|
|||
container,
|
||||
loader,
|
||||
selected_folder_id,
|
||||
product_name,
|
||||
selected_product_name,
|
||||
selected_representation,
|
||||
product_docs_by_parent_and_name,
|
||||
version_docs_by_parent_id,
|
||||
version_docs_by_parent_id_and_name,
|
||||
hero_version_docs_by_parent_id,
|
||||
repre_docs_by_parent_id_by_name,
|
||||
):
|
||||
|
|
@ -1272,15 +1272,18 @@ class SwitchAssetDialog(QtWidgets.QDialog):
|
|||
|
||||
container_product_id = container_version["parent"]
|
||||
container_product = self._product_docs_by_id[container_product_id]
|
||||
container_product_name = container_product["name"]
|
||||
|
||||
container_folder_id = container_product["parent"]
|
||||
|
||||
if selected_folder_id:
|
||||
folder_id = selected_folder_id
|
||||
else:
|
||||
folder_id = container_product["parent"]
|
||||
folder_id = container_folder_id
|
||||
|
||||
products_by_name = product_docs_by_parent_and_name[folder_id]
|
||||
if product_name:
|
||||
product_doc = products_by_name[product_name]
|
||||
if selected_product_name:
|
||||
product_doc = products_by_name[selected_product_name]
|
||||
else:
|
||||
product_doc = products_by_name[container_product["name"]]
|
||||
|
||||
|
|
@ -1300,7 +1303,26 @@ class SwitchAssetDialog(QtWidgets.QDialog):
|
|||
repre_doc = _repres.get(container_repre_name)
|
||||
|
||||
if not repre_doc:
|
||||
version_doc = version_docs_by_parent_id[product_id]
|
||||
version_docs_by_name = (
|
||||
version_docs_by_parent_id_and_name[product_id]
|
||||
)
|
||||
# If asset or subset are selected for switching, we use latest
|
||||
# version else we try to keep the current container version.
|
||||
version_name = None
|
||||
if (
|
||||
selected_folder_id in (None, container_folder_id)
|
||||
and selected_product_name in (None, container_product_name)
|
||||
):
|
||||
version_name = container_version.get("name")
|
||||
|
||||
version_doc = None
|
||||
if version_name is not None:
|
||||
version_doc = version_docs_by_name.get(version_name)
|
||||
|
||||
if version_doc is None:
|
||||
version_name = max(version_docs_by_name)
|
||||
version_doc = version_docs_by_name[version_name]
|
||||
|
||||
version_id = version_doc["_id"]
|
||||
repres_by_name = repre_docs_by_parent_id_by_name[version_id]
|
||||
if selected_representation:
|
||||
|
|
|
|||
|
|
@ -1299,15 +1299,21 @@ class SwitchAssetDialog(QtWidgets.QDialog):
|
|||
|
||||
# If asset or subset are selected for switching, we use latest
|
||||
# version else we try to keep the current container version.
|
||||
version_name = None
|
||||
if (
|
||||
selected_asset not in (None, container_asset_name)
|
||||
or selected_subset not in (None, container_subset_name)
|
||||
selected_asset in (None, container_asset_name)
|
||||
and selected_subset in (None, container_subset_name)
|
||||
):
|
||||
version_name = max(version_docs_by_name)
|
||||
else:
|
||||
version_name = container_version["name"]
|
||||
version_name = container_version.get("name")
|
||||
|
||||
version_doc = None
|
||||
if version_name is not None:
|
||||
version_doc = version_docs_by_name.get(version_name)
|
||||
|
||||
if version_doc is None:
|
||||
version_name = max(version_docs_by_name)
|
||||
version_doc = version_docs_by_name[version_name]
|
||||
|
||||
version_doc = version_docs_by_name[version_name]
|
||||
version_id = version_doc["_id"]
|
||||
repres_docs_by_name = repre_docs_by_parent_id_by_name[
|
||||
version_id
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""Package declaring Pype version."""
|
||||
__version__ = "3.18.6-nightly.1"
|
||||
__version__ = "3.18.7-nightly.2"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[tool.poetry]
|
||||
name = "OpenPype"
|
||||
version = "3.18.5" # OpenPype
|
||||
version = "3.18.6" # OpenPype
|
||||
description = "Open VFX and Animation pipeline with support."
|
||||
authors = ["OpenPype Team <info@openpype.io>"]
|
||||
license = "MIT License"
|
||||
|
|
|
|||
|
|
@ -1175,30 +1175,6 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"djvview": {
|
||||
"enabled": true,
|
||||
"label": "DJV View",
|
||||
"icon": "{}/app_icons/djvView.png",
|
||||
"host_name": "",
|
||||
"environment": "{}",
|
||||
"variants": [
|
||||
{
|
||||
"name": "1-1",
|
||||
"label": "1.1",
|
||||
"executables": {
|
||||
"windows": [],
|
||||
"darwin": [],
|
||||
"linux": []
|
||||
},
|
||||
"arguments": {
|
||||
"windows": [],
|
||||
"darwin": [],
|
||||
"linux": []
|
||||
},
|
||||
"environment": "{}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"wrap": {
|
||||
"enabled": true,
|
||||
"label": "Wrap",
|
||||
|
|
|
|||
|
|
@ -57,9 +57,9 @@ class CreateSaverPluginModel(BaseSettingsModel):
|
|||
enum_resolver=_create_saver_instance_attributes_enum,
|
||||
title="Instance attributes"
|
||||
)
|
||||
output_formats: list[str] = SettingsField(
|
||||
default_factory=list,
|
||||
title="Output formats"
|
||||
image_format: str = SettingsField(
|
||||
enum_resolver=_image_format_enum,
|
||||
title="Output Image Format"
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -90,6 +90,8 @@ class CreateImageSaverModel(CreateSaverPluginModel):
|
|||
0,
|
||||
title="Default rendered frame"
|
||||
)
|
||||
|
||||
|
||||
class CreatPluginsModel(BaseSettingsModel):
|
||||
CreateSaver: CreateSaverModel = SettingsField(
|
||||
default_factory=CreateSaverModel,
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
__version__ = "0.1.3"
|
||||
__version__ = "0.1.4"
|
||||
|
|
|
|||
|
|
@ -55,7 +55,10 @@ class CreateWriteRenderModel(BaseSettingsModel):
|
|||
enum_resolver=instance_attributes_enum,
|
||||
title="Instance attributes"
|
||||
)
|
||||
|
||||
exposed_knobs: list[str] = SettingsField(
|
||||
title="Write Node Exposed Knobs",
|
||||
default_factory=list
|
||||
)
|
||||
prenodes: list[PrenodeModel] = SettingsField(
|
||||
default_factory=list,
|
||||
title="Preceding nodes",
|
||||
|
|
@ -81,7 +84,10 @@ class CreateWritePrerenderModel(BaseSettingsModel):
|
|||
enum_resolver=instance_attributes_enum,
|
||||
title="Instance attributes"
|
||||
)
|
||||
|
||||
exposed_knobs: list[str] = SettingsField(
|
||||
title="Write Node Exposed Knobs",
|
||||
default_factory=list
|
||||
)
|
||||
prenodes: list[PrenodeModel] = SettingsField(
|
||||
default_factory=list,
|
||||
title="Preceding nodes",
|
||||
|
|
@ -107,7 +113,10 @@ class CreateWriteImageModel(BaseSettingsModel):
|
|||
enum_resolver=instance_attributes_enum,
|
||||
title="Instance attributes"
|
||||
)
|
||||
|
||||
exposed_knobs: list[str] = SettingsField(
|
||||
title="Write Node Exposed Knobs",
|
||||
default_factory=list
|
||||
)
|
||||
prenodes: list[PrenodeModel] = SettingsField(
|
||||
default_factory=list,
|
||||
title="Preceding nodes",
|
||||
|
|
@ -146,6 +155,7 @@ DEFAULT_CREATE_SETTINGS = {
|
|||
"reviewable",
|
||||
"farm_rendering"
|
||||
],
|
||||
"exposed_knobs": [],
|
||||
"prenodes": [
|
||||
{
|
||||
"name": "Reformat01",
|
||||
|
|
@ -179,6 +189,7 @@ DEFAULT_CREATE_SETTINGS = {
|
|||
"farm_rendering",
|
||||
"use_range_limit"
|
||||
],
|
||||
"exposed_knobs": [],
|
||||
"prenodes": []
|
||||
},
|
||||
"CreateWriteImage": {
|
||||
|
|
@ -191,6 +202,7 @@ DEFAULT_CREATE_SETTINGS = {
|
|||
"instance_attributes": [
|
||||
"use_range_limit"
|
||||
],
|
||||
"exposed_knobs": [],
|
||||
"prenodes": [
|
||||
{
|
||||
"name": "FrameHold01",
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
__version__ = "0.1.8"
|
||||
__version__ = "0.1.9"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue