Skip to content

Commit 06a23e3

Browse files
authored
Merge pull request #52 from ynput/AY-6643_project_settings
AY-6643 Report AYON project resolution and fps within Resolve project settings.
2 parents 6593201 + dc45e08 commit 06a23e3

File tree

5 files changed

+234
-4
lines changed

5 files changed

+234
-4
lines changed

client/ayon_resolve/api/__init__.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,11 @@
4545
get_pype_clip_metadata,
4646
set_project_manager_to_folder_name,
4747
get_otio_clip_instance_data,
48-
get_reformated_path
48+
get_reformated_path,
49+
detect_project_fps_mismatch,
50+
detect_project_resolution_mismatch,
51+
set_project_fps,
52+
set_project_resolution,
4953
)
5054

5155
from .menu import launch_ayon_menu
@@ -126,6 +130,10 @@
126130
"set_project_manager_to_folder_name",
127131
"get_otio_clip_instance_data",
128132
"get_reformated_path",
133+
"detect_project_fps_mismatch",
134+
"detect_project_resolution_mismatch",
135+
"set_project_fps",
136+
"set_project_resolution",
129137

130138
# menu
131139
"launch_ayon_menu",

client/ayon_resolve/api/lib.py

Lines changed: 163 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@
1313
is_overlapping_otio_ranges,
1414
frames_to_timecode
1515
)
16-
from ayon_core.pipeline.context_tools import get_current_project_name
16+
from ayon_core.pipeline.context_tools import (
17+
get_current_project_name,
18+
get_current_task_entity
19+
)
1720
from ayon_core.pipeline.tempdir import create_custom_tempdir
1821

1922
from . import constants
@@ -1226,3 +1229,162 @@ def export_timeline_otio_native(timeline, filepath):
12261229
log.error(f"Failed to export timeline otio: {e}")
12271230
return False
12281231
return True
1232+
1233+
1234+
def detect_project_fps_mismatch(task_entity=None):
1235+
""" Detect potential fps mismatch between project settings
1236+
and task entity. Return any mismatch as a information
1237+
string to be displayed to the user.
1238+
"""
1239+
task_entity = task_entity or get_current_task_entity()
1240+
attributes = task_entity["attrib"]
1241+
task_fps = attributes["fps"]
1242+
1243+
resolve_project = get_current_resolve_project()
1244+
resolve_fps = resolve_project.GetSetting("timelineFrameRate")
1245+
1246+
if task_fps != resolve_fps:
1247+
return (
1248+
f"Project current fps is {resolve_fps} "
1249+
f"while task fps is {task_fps}."
1250+
)
1251+
1252+
return None
1253+
1254+
1255+
def detect_project_resolution_mismatch(task_entity=None):
1256+
""" Detect potential resolution mismatch between project settings
1257+
and task entity. Return any mismatch as a information
1258+
string to be displayed to the user.
1259+
"""
1260+
task_entity = task_entity or get_current_task_entity()
1261+
attributes = task_entity["attrib"]
1262+
width = attributes["resolutionWidth"]
1263+
height = attributes["resolutionHeight"]
1264+
1265+
resolve_project = get_current_resolve_project()
1266+
resolve_width = resolve_project.GetSetting("timelineResolutionWidth")
1267+
resolve_height = resolve_project.GetSetting("timelineResolutionHeight")
1268+
1269+
if (str(width), str(height)) != (resolve_width, resolve_height):
1270+
return (
1271+
f"Project current resolution is {resolve_width}x{resolve_height} "
1272+
f"while task resolution is {width}x{height}."
1273+
)
1274+
1275+
return None
1276+
1277+
1278+
def set_project_fps(task_entity=None):
1279+
""" Attempt to set project frame rate from AYON current task.
1280+
This might not be possible if a timeline already exists within the project.
1281+
Return a error string if any issue so it can be displayed to the user.
1282+
"""
1283+
task_entity = task_entity or get_current_task_entity()
1284+
attributes = task_entity["attrib"]
1285+
1286+
# Set project frame rate and resolution
1287+
resolve_project = get_current_resolve_project()
1288+
project_fps = attributes["fps"]
1289+
1290+
SUPPORTED_FPS = {
1291+
16.0: "16",
1292+
18.0: "18",
1293+
23.976: "23.976",
1294+
24.0: "24",
1295+
25.0: "25",
1296+
29.97: "29.97",
1297+
30.0: "30",
1298+
47.952: "47.952",
1299+
48.0: "48",
1300+
50.0: "50"
1301+
}
1302+
1303+
if float(project_fps) in SUPPORTED_FPS:
1304+
if not resolve_project.SetSetting(
1305+
"timelineFrameRate",
1306+
SUPPORTED_FPS[float(project_fps)]
1307+
):
1308+
# Resolve does not allow to edit timeline fps
1309+
# project settings once a timeline has been created.
1310+
return (
1311+
"Cannot override Project fps from AYON."
1312+
" This could be because a timeline already exists."
1313+
)
1314+
1315+
return None
1316+
1317+
else:
1318+
return (
1319+
"Fps set in AYON project is not supported by Resolve"
1320+
f" attempt to set {project_fps},"
1321+
f" supported are {tuple(SUPPORTED_FPS.keys())}."
1322+
)
1323+
1324+
1325+
def set_project_resolution(task_entity=None):
1326+
""" Attempt to set project resolution from AYON current task.
1327+
Return a error string if any issue so it can be displayed to the user.
1328+
"""
1329+
task_entity = task_entity or get_current_task_entity()
1330+
attributes = task_entity["attrib"]
1331+
1332+
resolve_project = get_current_resolve_project()
1333+
width = attributes["resolutionWidth"]
1334+
height = attributes["resolutionHeight"]
1335+
1336+
resolution_params = {
1337+
"timelineResolutionHeight": height,
1338+
"timelineResolutionWidth": width,
1339+
}
1340+
1341+
# In order to set vertical resolution in resolve,
1342+
# the "Use vertical resolution" option need to be enabled.
1343+
# This is not exposed from the Python API.
1344+
if height > width:
1345+
return (
1346+
"Cannot override Project resolution from AYON."
1347+
f" Vertical resolution {width}x{height}"
1348+
" is unsupported from the API."
1349+
)
1350+
1351+
for resolve_param, value in resolution_params.items():
1352+
if not resolve_project.SetSetting(
1353+
resolve_param,
1354+
str(int(value))
1355+
):
1356+
return (
1357+
"Cannot override Project resolution from AYON."
1358+
f" trying to set {resolve_param} = {value}"
1359+
)
1360+
1361+
SUPPORTED_PIXEL_ASPECTS = {
1362+
1.0: "Square",
1363+
16/9: "16:9 anamorphic",
1364+
4/3: "4:3 standard definition",
1365+
2.0: "Cinemascope",
1366+
}
1367+
pixel_aspect_ratio = round(attributes["pixelAspect"], 2)
1368+
1369+
for supported_pa in SUPPORTED_PIXEL_ASPECTS:
1370+
if round(supported_pa, 2) != pixel_aspect_ratio:
1371+
continue
1372+
1373+
if not resolve_project.SetSetting(
1374+
"timelinePixelAspectRatio",
1375+
SUPPORTED_PIXEL_ASPECTS[supported_pa]
1376+
):
1377+
return (
1378+
"Cannot override Project pixel aspect ratio from AYON."
1379+
" trying to set timelinePixelAspectRatio = "
1380+
f"{SUPPORTED_PIXEL_ASPECTS[supported_pa]}"
1381+
)
1382+
1383+
return None
1384+
1385+
else:
1386+
return (
1387+
"Pixel Aspect Ratio set in AYON project is not supported"
1388+
f" by Resolve, attempt to set {pixel_aspect_ratio},"
1389+
f" supported are {tuple(SUPPORTED_PIXEL_ASPECTS.keys())}."
1390+
)

client/ayon_resolve/api/pipeline.py

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@
55
import json
66
import contextlib
77
from collections import OrderedDict
8+
import sys
89

910
from pyblish import api as pyblish
11+
from qtpy import QtWidgets
1012

1113
from ayon_core.lib import Logger
1214
from ayon_core.pipeline import (
@@ -16,6 +18,11 @@
1618
register_inventory_action_path,
1719
AVALON_CONTAINER_ID,
1820
)
21+
from ayon_core.pipeline.context_tools import (
22+
get_current_task_entity,
23+
get_current_project_name
24+
)
25+
from ayon_core.settings import get_project_settings
1926
from ayon_core.host import (
2027
HostBase,
2128
IWorkfileHost,
@@ -79,9 +86,14 @@ def install(self):
7986
get_resolve_module()
8087

8188
def open_workfile(self, filepath):
82-
return open_file(filepath)
89+
success = open_file(filepath)
90+
if success:
91+
self._show_ayon_settings_confirmation_windows()
92+
93+
return success
8394

8495
def save_workfile(self, filepath=None):
96+
self._show_ayon_settings_confirmation_windows()
8597
return save_file(filepath)
8698

8799
def work_root(self, session):
@@ -107,6 +119,49 @@ def update_context_data(self, data, changes):
107119
# TODO: implement to support persisting context attributes
108120
pass
109121

122+
@staticmethod
123+
def _show_ayon_settings_confirmation_windows():
124+
"""
125+
"""
126+
settings = get_project_settings(
127+
get_current_project_name()
128+
)
129+
130+
# Only display this confirmation window if relevant setting is enabled.
131+
if not settings["resolve"]["report_fps_resolution"]:
132+
return
133+
134+
current_task = get_current_task_entity()
135+
mismatch_fps = lib.detect_project_fps_mismatch(current_task)
136+
mismatch_res = lib.detect_project_resolution_mismatch(current_task)
137+
138+
if not mismatch_fps and not mismatch_res:
139+
log.info("Current resolve project matches task settings.")
140+
return
141+
142+
app = QtWidgets.QApplication.instance() or QtWidgets.QApplication(sys.argv)
143+
if QtWidgets.QMessageBox.information(
144+
app.activeWindow(),
145+
"Report project settings from AYON task ?",
146+
(
147+
"Your project settings mismatch the current task context. "
148+
"Would you like to reset it to match the task attributes?\n"
149+
) + "".join([f"\n - {info}" for info in [mismatch_fps, mismatch_res] if info]),
150+
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No,
151+
) == QtWidgets.QMessageBox.Yes:
152+
153+
issues_fps = lib.set_project_fps(current_task) or ""
154+
issues_resolution = lib.set_project_resolution(current_task) or ""
155+
if issues_fps or issues_resolution:
156+
QtWidgets.QMessageBox.warning(
157+
app.activeWindow(),
158+
"Cannot edit settings from AYON task.",
159+
(
160+
f"{issues_fps}\n{issues_resolution}\n\nMore information available at: "
161+
"https://docs.ayon.dev/docs/addon_resolve_artist#report-fps-and-resolution-from-ayon-task"
162+
)
163+
)
164+
110165

111166
def containerise(timeline_item,
112167
name,

client/ayon_resolve/startup.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,13 @@ def main():
5454
else:
5555
log.info("No last workfile set to open. Skipping..")
5656

57-
# Launch AYON menu
57+
# Gathered project settings
5858
from ayon_core.settings import get_project_settings
5959
from ayon_core.pipeline.context_tools import get_current_project_name
6060
project_name = get_current_project_name()
6161
log.info(f"Current project name in context: {project_name}")
6262

63+
# Launch AYON menu
6364
settings = get_project_settings(project_name)
6465
if settings.get("resolve", {}).get("launch_openpype_menu_on_start", True):
6566
log.info("Launching AYON menu..")

server/settings.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,9 @@ class ResolveSettings(BaseSettingsModel):
141141
launch_openpype_menu_on_start: bool = SettingsField(
142142
False, title="Launch OpenPype menu on start of Resolve"
143143
)
144+
report_fps_resolution: bool = SettingsField(
145+
False, title="Set FPS and Resolution from current task"
146+
)
144147
imageio: ResolveImageIOModel = SettingsField(
145148
default_factory=ResolveImageIOModel,
146149
title="Color Management (ImageIO)"
@@ -157,6 +160,7 @@ class ResolveSettings(BaseSettingsModel):
157160

158161
DEFAULT_VALUES = {
159162
"launch_openpype_menu_on_start": False,
163+
"report_fps_resolution": False,
160164
"create": {
161165
"CreateShotClip": {
162166
"hierarchy": "{folder}/{sequence}",

0 commit comments

Comments
 (0)