mirror of
https://github.com/ynput/ayon-core.git
synced 2026-01-01 08:24:53 +01:00
Merge branch 'develop' into enhancement/1496-yn-0069-more-user-data-in-templates
This commit is contained in:
commit
7542d446d0
9 changed files with 67 additions and 70 deletions
2
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
2
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
|
|
@ -35,6 +35,8 @@ body:
|
||||||
label: Version
|
label: Version
|
||||||
description: What version are you running? Look to AYON Tray
|
description: What version are you running? Look to AYON Tray
|
||||||
options:
|
options:
|
||||||
|
- 1.6.6
|
||||||
|
- 1.6.5
|
||||||
- 1.6.4
|
- 1.6.4
|
||||||
- 1.6.3
|
- 1.6.3
|
||||||
- 1.6.2
|
- 1.6.2
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
"""Base class for AYON addons."""
|
"""Base class for AYON addons."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import copy
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
|
|
@ -13,6 +12,7 @@ import collections
|
||||||
import warnings
|
import warnings
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
|
from urllib.parse import urlencode
|
||||||
from types import ModuleType
|
from types import ModuleType
|
||||||
import typing
|
import typing
|
||||||
from typing import Optional, Any, Union
|
from typing import Optional, Any, Union
|
||||||
|
|
@ -136,39 +136,47 @@ def load_addons(force: bool = False) -> None:
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
|
|
||||||
|
|
||||||
def _get_ayon_bundle_data() -> Optional[dict[str, Any]]:
|
def _get_ayon_bundle_data() -> tuple[
|
||||||
|
dict[str, Any], Optional[dict[str, Any]]
|
||||||
|
]:
|
||||||
studio_bundle_name = os.environ.get("AYON_STUDIO_BUNDLE_NAME")
|
studio_bundle_name = os.environ.get("AYON_STUDIO_BUNDLE_NAME")
|
||||||
project_bundle_name = os.getenv("AYON_BUNDLE_NAME")
|
project_bundle_name = os.getenv("AYON_BUNDLE_NAME")
|
||||||
bundles = ayon_api.get_bundles()["bundles"]
|
bundles = ayon_api.get_bundles()["bundles"]
|
||||||
project_bundle = next(
|
studio_bundle = next(
|
||||||
(
|
(
|
||||||
bundle
|
bundle
|
||||||
for bundle in bundles
|
for bundle in bundles
|
||||||
if bundle["name"] == project_bundle_name
|
if bundle["name"] == studio_bundle_name
|
||||||
),
|
),
|
||||||
None
|
None
|
||||||
)
|
)
|
||||||
studio_bundle = None
|
|
||||||
if studio_bundle_name and project_bundle_name != studio_bundle_name:
|
if studio_bundle is None:
|
||||||
studio_bundle = next(
|
raise RuntimeError(f"Failed to find bundle '{studio_bundle_name}'.")
|
||||||
|
|
||||||
|
project_bundle = None
|
||||||
|
if project_bundle_name and project_bundle_name != studio_bundle_name:
|
||||||
|
project_bundle = next(
|
||||||
(
|
(
|
||||||
bundle
|
bundle
|
||||||
for bundle in bundles
|
for bundle in bundles
|
||||||
if bundle["name"] == studio_bundle_name
|
if bundle["name"] == project_bundle_name
|
||||||
),
|
),
|
||||||
None
|
None
|
||||||
)
|
)
|
||||||
|
|
||||||
if project_bundle and studio_bundle:
|
if project_bundle is None:
|
||||||
addons = copy.deepcopy(studio_bundle["addons"])
|
raise RuntimeError(
|
||||||
addons.update(project_bundle["addons"])
|
f"Failed to find project bundle '{project_bundle_name}'."
|
||||||
project_bundle["addons"] = addons
|
)
|
||||||
return project_bundle
|
|
||||||
|
return studio_bundle, project_bundle
|
||||||
|
|
||||||
|
|
||||||
def _get_ayon_addons_information(
|
def _get_ayon_addons_information(
|
||||||
bundle_info: dict[str, Any]
|
studio_bundle: dict[str, Any],
|
||||||
) -> list[dict[str, Any]]:
|
project_bundle: Optional[dict[str, Any]],
|
||||||
|
) -> dict[str, str]:
|
||||||
"""Receive information about addons to use from server.
|
"""Receive information about addons to use from server.
|
||||||
|
|
||||||
Todos:
|
Todos:
|
||||||
|
|
@ -181,22 +189,20 @@ def _get_ayon_addons_information(
|
||||||
list[dict[str, Any]]: List of addon information to use.
|
list[dict[str, Any]]: List of addon information to use.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
output = []
|
key_values = {
|
||||||
bundle_addons = bundle_info["addons"]
|
"summary": "true",
|
||||||
addons = ayon_api.get_addons_info()["addons"]
|
"bundle_name": studio_bundle["name"],
|
||||||
for addon in addons:
|
}
|
||||||
name = addon["name"]
|
if project_bundle:
|
||||||
versions = addon.get("versions")
|
key_values["project_bundle_name"] = project_bundle["name"]
|
||||||
addon_version = bundle_addons.get(name)
|
|
||||||
if addon_version is None or not versions:
|
query = urlencode(key_values)
|
||||||
continue
|
|
||||||
version = versions.get(addon_version)
|
response = ayon_api.get(f"settings?{query}")
|
||||||
if version:
|
return {
|
||||||
version = copy.deepcopy(version)
|
addon["name"]: addon["version"]
|
||||||
version["name"] = name
|
for addon in response.data["addons"]
|
||||||
version["version"] = addon_version
|
}
|
||||||
output.append(version)
|
|
||||||
return output
|
|
||||||
|
|
||||||
|
|
||||||
def _load_ayon_addons(log: logging.Logger) -> list[ModuleType]:
|
def _load_ayon_addons(log: logging.Logger) -> list[ModuleType]:
|
||||||
|
|
@ -214,8 +220,8 @@ def _load_ayon_addons(log: logging.Logger) -> list[ModuleType]:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
all_addon_modules = []
|
all_addon_modules = []
|
||||||
bundle_info = _get_ayon_bundle_data()
|
studio_bundle, project_bundle = _get_ayon_bundle_data()
|
||||||
addons_info = _get_ayon_addons_information(bundle_info)
|
addons_info = _get_ayon_addons_information(studio_bundle, project_bundle)
|
||||||
if not addons_info:
|
if not addons_info:
|
||||||
return all_addon_modules
|
return all_addon_modules
|
||||||
|
|
||||||
|
|
@ -227,17 +233,16 @@ def _load_ayon_addons(log: logging.Logger) -> list[ModuleType]:
|
||||||
dev_addons_info = {}
|
dev_addons_info = {}
|
||||||
if dev_mode_enabled:
|
if dev_mode_enabled:
|
||||||
# Get dev addons info only when dev mode is enabled
|
# Get dev addons info only when dev mode is enabled
|
||||||
dev_addons_info = bundle_info.get("addonDevelopment", dev_addons_info)
|
dev_addons_info = studio_bundle.get(
|
||||||
|
"addonDevelopment", dev_addons_info
|
||||||
|
)
|
||||||
|
|
||||||
addons_dir_exists = os.path.exists(addons_dir)
|
addons_dir_exists = os.path.exists(addons_dir)
|
||||||
if not addons_dir_exists:
|
if not addons_dir_exists:
|
||||||
log.warning(
|
log.warning(
|
||||||
f"Addons directory does not exists. Path \"{addons_dir}\"")
|
f"Addons directory does not exists. Path \"{addons_dir}\"")
|
||||||
|
|
||||||
for addon_info in addons_info:
|
for addon_name, addon_version in addons_info.items():
|
||||||
addon_name = addon_info["name"]
|
|
||||||
addon_version = addon_info["version"]
|
|
||||||
|
|
||||||
# core addon does not have any addon object
|
# core addon does not have any addon object
|
||||||
if addon_name == "core":
|
if addon_name == "core":
|
||||||
continue
|
continue
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ class CollectCoreJobEnvVars(pyblish.api.ContextPlugin):
|
||||||
|
|
||||||
for key in [
|
for key in [
|
||||||
"AYON_BUNDLE_NAME",
|
"AYON_BUNDLE_NAME",
|
||||||
|
"AYON_STUDIO_BUNDLE_NAME",
|
||||||
"AYON_USE_STAGING",
|
"AYON_USE_STAGING",
|
||||||
"AYON_IN_TESTS",
|
"AYON_IN_TESTS",
|
||||||
# NOTE Not sure why workdir is needed?
|
# NOTE Not sure why workdir is needed?
|
||||||
|
|
|
||||||
|
|
@ -130,7 +130,7 @@ class ExtractOTIOReview(
|
||||||
# NOTE it looks like it is set only in hiero integration
|
# NOTE it looks like it is set only in hiero integration
|
||||||
res_data = {"width": self.to_width, "height": self.to_height}
|
res_data = {"width": self.to_width, "height": self.to_height}
|
||||||
for key in res_data:
|
for key in res_data:
|
||||||
for meta_prefix in ("ayon.source.", "openpype.source."):
|
for meta_prefix in ("ayon.source", "openpype.source"):
|
||||||
meta_key = f"{meta_prefix}.{key}"
|
meta_key = f"{meta_prefix}.{key}"
|
||||||
value = media_metadata.get(meta_key)
|
value = media_metadata.get(meta_key)
|
||||||
if value is not None:
|
if value is not None:
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""Package declaring AYON addon 'core' version."""
|
"""Package declaring AYON addon 'core' version."""
|
||||||
__version__ = "1.6.4+dev"
|
__version__ = "1.6.6+dev"
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
name = "core"
|
name = "core"
|
||||||
title = "Core"
|
title = "Core"
|
||||||
version = "1.6.4+dev"
|
version = "1.6.6+dev"
|
||||||
|
|
||||||
client_dir = "ayon_core"
|
client_dir = "ayon_core"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "ayon-core"
|
name = "ayon-core"
|
||||||
version = "1.6.4+dev"
|
version = "1.6.6+dev"
|
||||||
description = ""
|
description = ""
|
||||||
authors = ["Ynput Team <team@ynput.io>"]
|
authors = ["Ynput Team <team@ynput.io>"]
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|
@ -27,17 +27,6 @@ codespell = "^2.2.6"
|
||||||
semver = "^3.0.2"
|
semver = "^3.0.2"
|
||||||
mypy = "^1.14.0"
|
mypy = "^1.14.0"
|
||||||
mock = "^5.0.0"
|
mock = "^5.0.0"
|
||||||
tomlkit = "^0.13.2"
|
|
||||||
requests = "^2.32.3"
|
|
||||||
mkdocs-material = "^9.6.7"
|
|
||||||
mkdocs-autoapi = "^0.4.0"
|
|
||||||
mkdocstrings-python = "^1.16.2"
|
|
||||||
mkdocs-minify-plugin = "^0.8.0"
|
|
||||||
markdown-checklist = "^0.4.4"
|
|
||||||
mdx-gh-links = "^0.4"
|
|
||||||
pymdown-extensions = "^10.14.3"
|
|
||||||
mike = "^2.1.3"
|
|
||||||
mkdocstrings-shell = "^1.0.2"
|
|
||||||
nxtools = "^1.6"
|
nxtools = "^1.6"
|
||||||
|
|
||||||
[tool.poetry.group.test.dependencies]
|
[tool.poetry.group.test.dependencies]
|
||||||
|
|
|
||||||
|
|
@ -454,7 +454,7 @@ DEFAULT_TOOLS_VALUES = {
|
||||||
"hosts": [],
|
"hosts": [],
|
||||||
"task_types": [],
|
"task_types": [],
|
||||||
"tasks": [],
|
"tasks": [],
|
||||||
"template": "{product[type]}{Task[name]}{Variant}"
|
"template": "{product[type]}{Task[name]}{Variant}<_{Aov}>"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"product_types": [
|
"product_types": [
|
||||||
|
|
|
||||||
|
|
@ -246,75 +246,75 @@ def test_multiple_review_clips_no_gap():
|
||||||
expected = [
|
expected = [
|
||||||
# 10 head black frames generated from gap (991-1000)
|
# 10 head black frames generated from gap (991-1000)
|
||||||
'/path/to/ffmpeg -t 0.4 -r 25.0 -f lavfi'
|
'/path/to/ffmpeg -t 0.4 -r 25.0 -f lavfi'
|
||||||
' -i color=c=black:s=1280x720 -tune '
|
' -i color=c=black:s=1920x1080 -tune '
|
||||||
'stillimage -start_number 991 -pix_fmt rgba C:/result/output.%04d.png',
|
'stillimage -start_number 991 -pix_fmt rgba C:/result/output.%04d.png',
|
||||||
|
|
||||||
# Alternance 25fps tiff sequence and 24fps exr sequence
|
# Alternance 25fps tiff sequence and 24fps exr sequence
|
||||||
# for 100 frames each
|
# for 100 frames each
|
||||||
'/path/to/ffmpeg -start_number 1000 -framerate 25.0 -i '
|
'/path/to/ffmpeg -start_number 1000 -framerate 25.0 -i '
|
||||||
f'C:\\no_tc{os.sep}output.%04d.tif '
|
f'C:\\no_tc{os.sep}output.%04d.tif '
|
||||||
'-vf scale=1280:720:flags=lanczos -compression_level 5 '
|
'-vf scale=1920:1080:flags=lanczos -compression_level 5 '
|
||||||
'-start_number 1001 -pix_fmt rgba C:/result/output.%04d.png',
|
'-start_number 1001 -pix_fmt rgba C:/result/output.%04d.png',
|
||||||
|
|
||||||
'/path/to/ffmpeg -start_number 1000 -framerate 24.0 -i '
|
'/path/to/ffmpeg -start_number 1000 -framerate 24.0 -i '
|
||||||
f'C:\\with_tc{os.sep}output.%04d.exr '
|
f'C:\\with_tc{os.sep}output.%04d.exr '
|
||||||
'-vf scale=1280:720:flags=lanczos -compression_level 5 '
|
'-vf scale=1920:1080:flags=lanczos -compression_level 5 '
|
||||||
'-start_number 1102 -pix_fmt rgba C:/result/output.%04d.png',
|
'-start_number 1102 -pix_fmt rgba C:/result/output.%04d.png',
|
||||||
|
|
||||||
'/path/to/ffmpeg -start_number 1000 -framerate 25.0 -i '
|
'/path/to/ffmpeg -start_number 1000 -framerate 25.0 -i '
|
||||||
f'C:\\no_tc{os.sep}output.%04d.tif '
|
f'C:\\no_tc{os.sep}output.%04d.tif '
|
||||||
'-vf scale=1280:720:flags=lanczos -compression_level 5 '
|
'-vf scale=1920:1080:flags=lanczos -compression_level 5 '
|
||||||
'-start_number 1198 -pix_fmt rgba C:/result/output.%04d.png',
|
'-start_number 1198 -pix_fmt rgba C:/result/output.%04d.png',
|
||||||
|
|
||||||
'/path/to/ffmpeg -start_number 1000 -framerate 24.0 -i '
|
'/path/to/ffmpeg -start_number 1000 -framerate 24.0 -i '
|
||||||
f'C:\\with_tc{os.sep}output.%04d.exr '
|
f'C:\\with_tc{os.sep}output.%04d.exr '
|
||||||
'-vf scale=1280:720:flags=lanczos -compression_level 5 '
|
'-vf scale=1920:1080:flags=lanczos -compression_level 5 '
|
||||||
'-start_number 1299 -pix_fmt rgba C:/result/output.%04d.png',
|
'-start_number 1299 -pix_fmt rgba C:/result/output.%04d.png',
|
||||||
|
|
||||||
# Repeated 25fps tiff sequence multiple times till the end
|
# Repeated 25fps tiff sequence multiple times till the end
|
||||||
'/path/to/ffmpeg -start_number 1000 -framerate 25.0 -i '
|
'/path/to/ffmpeg -start_number 1000 -framerate 25.0 -i '
|
||||||
f'C:\\no_tc{os.sep}output.%04d.tif '
|
f'C:\\no_tc{os.sep}output.%04d.tif '
|
||||||
'-vf scale=1280:720:flags=lanczos -compression_level 5 '
|
'-vf scale=1920:1080:flags=lanczos -compression_level 5 '
|
||||||
'-start_number 1395 -pix_fmt rgba C:/result/output.%04d.png',
|
'-start_number 1395 -pix_fmt rgba C:/result/output.%04d.png',
|
||||||
|
|
||||||
'/path/to/ffmpeg -start_number 1000 -framerate 25.0 -i '
|
'/path/to/ffmpeg -start_number 1000 -framerate 25.0 -i '
|
||||||
f'C:\\no_tc{os.sep}output.%04d.tif '
|
f'C:\\no_tc{os.sep}output.%04d.tif '
|
||||||
'-vf scale=1280:720:flags=lanczos -compression_level 5 '
|
'-vf scale=1920:1080:flags=lanczos -compression_level 5 '
|
||||||
'-start_number 1496 -pix_fmt rgba C:/result/output.%04d.png',
|
'-start_number 1496 -pix_fmt rgba C:/result/output.%04d.png',
|
||||||
|
|
||||||
'/path/to/ffmpeg -start_number 1000 -framerate 25.0 -i '
|
'/path/to/ffmpeg -start_number 1000 -framerate 25.0 -i '
|
||||||
f'C:\\no_tc{os.sep}output.%04d.tif '
|
f'C:\\no_tc{os.sep}output.%04d.tif '
|
||||||
'-vf scale=1280:720:flags=lanczos -compression_level 5 '
|
'-vf scale=1920:1080:flags=lanczos -compression_level 5 '
|
||||||
'-start_number 1597 -pix_fmt rgba C:/result/output.%04d.png',
|
'-start_number 1597 -pix_fmt rgba C:/result/output.%04d.png',
|
||||||
|
|
||||||
'/path/to/ffmpeg -start_number 1000 -framerate 25.0 -i '
|
'/path/to/ffmpeg -start_number 1000 -framerate 25.0 -i '
|
||||||
f'C:\\no_tc{os.sep}output.%04d.tif '
|
f'C:\\no_tc{os.sep}output.%04d.tif '
|
||||||
'-vf scale=1280:720:flags=lanczos -compression_level 5 '
|
'-vf scale=1920:1080:flags=lanczos -compression_level 5 '
|
||||||
'-start_number 1698 -pix_fmt rgba C:/result/output.%04d.png',
|
'-start_number 1698 -pix_fmt rgba C:/result/output.%04d.png',
|
||||||
|
|
||||||
'/path/to/ffmpeg -start_number 1000 -framerate 25.0 -i '
|
'/path/to/ffmpeg -start_number 1000 -framerate 25.0 -i '
|
||||||
f'C:\\no_tc{os.sep}output.%04d.tif '
|
f'C:\\no_tc{os.sep}output.%04d.tif '
|
||||||
'-vf scale=1280:720:flags=lanczos -compression_level 5 '
|
'-vf scale=1920:1080:flags=lanczos -compression_level 5 '
|
||||||
'-start_number 1799 -pix_fmt rgba C:/result/output.%04d.png',
|
'-start_number 1799 -pix_fmt rgba C:/result/output.%04d.png',
|
||||||
|
|
||||||
'/path/to/ffmpeg -start_number 1000 -framerate 25.0 -i '
|
'/path/to/ffmpeg -start_number 1000 -framerate 25.0 -i '
|
||||||
f'C:\\no_tc{os.sep}output.%04d.tif '
|
f'C:\\no_tc{os.sep}output.%04d.tif '
|
||||||
'-vf scale=1280:720:flags=lanczos -compression_level 5 '
|
'-vf scale=1920:1080:flags=lanczos -compression_level 5 '
|
||||||
'-start_number 1900 -pix_fmt rgba C:/result/output.%04d.png',
|
'-start_number 1900 -pix_fmt rgba C:/result/output.%04d.png',
|
||||||
|
|
||||||
'/path/to/ffmpeg -start_number 1000 -framerate 25.0 -i '
|
'/path/to/ffmpeg -start_number 1000 -framerate 25.0 -i '
|
||||||
f'C:\\no_tc{os.sep}output.%04d.tif '
|
f'C:\\no_tc{os.sep}output.%04d.tif '
|
||||||
'-vf scale=1280:720:flags=lanczos -compression_level 5 '
|
'-vf scale=1920:1080:flags=lanczos -compression_level 5 '
|
||||||
'-start_number 2001 -pix_fmt rgba C:/result/output.%04d.png',
|
'-start_number 2001 -pix_fmt rgba C:/result/output.%04d.png',
|
||||||
|
|
||||||
'/path/to/ffmpeg -start_number 1000 -framerate 25.0 -i '
|
'/path/to/ffmpeg -start_number 1000 -framerate 25.0 -i '
|
||||||
f'C:\\no_tc{os.sep}output.%04d.tif '
|
f'C:\\no_tc{os.sep}output.%04d.tif '
|
||||||
'-vf scale=1280:720:flags=lanczos -compression_level 5 '
|
'-vf scale=1920:1080:flags=lanczos -compression_level 5 '
|
||||||
'-start_number 2102 -pix_fmt rgba C:/result/output.%04d.png',
|
'-start_number 2102 -pix_fmt rgba C:/result/output.%04d.png',
|
||||||
|
|
||||||
'/path/to/ffmpeg -start_number 1000 -framerate 25.0 -i '
|
'/path/to/ffmpeg -start_number 1000 -framerate 25.0 -i '
|
||||||
f'C:\\no_tc{os.sep}output.%04d.tif '
|
f'C:\\no_tc{os.sep}output.%04d.tif '
|
||||||
'-vf scale=1280:720:flags=lanczos -compression_level 5 '
|
'-vf scale=1920:1080:flags=lanczos -compression_level 5 '
|
||||||
'-start_number 2203 -pix_fmt rgba C:/result/output.%04d.png'
|
'-start_number 2203 -pix_fmt rgba C:/result/output.%04d.png'
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -348,12 +348,12 @@ def test_multiple_review_clips_with_gap():
|
||||||
|
|
||||||
'/path/to/ffmpeg -start_number 1000 -framerate 24.0 -i '
|
'/path/to/ffmpeg -start_number 1000 -framerate 24.0 -i '
|
||||||
f'C:\\with_tc{os.sep}output.%04d.exr '
|
f'C:\\with_tc{os.sep}output.%04d.exr '
|
||||||
'-vf scale=1280:720:flags=lanczos -compression_level 5 '
|
'-vf scale=1920:1080:flags=lanczos -compression_level 5 '
|
||||||
'-start_number 1003 -pix_fmt rgba C:/result/output.%04d.png',
|
'-start_number 1003 -pix_fmt rgba C:/result/output.%04d.png',
|
||||||
|
|
||||||
'/path/to/ffmpeg -start_number 1000 -framerate 24.0 -i '
|
'/path/to/ffmpeg -start_number 1000 -framerate 24.0 -i '
|
||||||
f'C:\\with_tc{os.sep}output.%04d.exr '
|
f'C:\\with_tc{os.sep}output.%04d.exr '
|
||||||
'-vf scale=1280:720:flags=lanczos -compression_level 5 '
|
'-vf scale=1920:1080:flags=lanczos -compression_level 5 '
|
||||||
'-start_number 1091 -pix_fmt rgba C:/result/output.%04d.png'
|
'-start_number 1091 -pix_fmt rgba C:/result/output.%04d.png'
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue