From 39cc1f2877f125ce47155be52837019f7ba36003 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 19 Jul 2024 12:51:06 +0200 Subject: [PATCH 01/18] base of upload logic in integrate plugin --- client/ayon_core/plugins/publish/integrate.py | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/client/ayon_core/plugins/publish/integrate.py b/client/ayon_core/plugins/publish/integrate.py index a2cf910fa6..8ab48ce9e5 100644 --- a/client/ayon_core/plugins/publish/integrate.py +++ b/client/ayon_core/plugins/publish/integrate.py @@ -6,6 +6,7 @@ import copy import clique import pyblish.api from ayon_api import ( + get_server_api_connection, get_attributes_for_type, get_product_by_name, get_version_by_name, @@ -25,6 +26,7 @@ from ayon_core.lib.file_transaction import ( DuplicateDestinationError ) from ayon_core.pipeline.publish import ( + get_publish_repre_path, KnownPublishError, get_publish_template_name, ) @@ -348,6 +350,8 @@ class IntegrateAsset(pyblish.api.InstancePlugin): self.log.debug("{}".format(op_session.to_data())) op_session.commit() + self._upload_reviewable(project_name, version_entity["id"], instance) + # Backwards compatibility used in hero integration. # todo: can we avoid the need to store this? instance.data["published_representations"] = { @@ -984,6 +988,57 @@ class IntegrateAsset(pyblish.api.InstancePlugin): "hash_type": "op3", } + def _upload_reviewable(self, project_name, version_id, instance): + uploaded_labels = set() + ayon_con = get_server_api_connection() + base_headers = ayon_con.get_headers() + endpoint = ( + f"/api/projects/{project_name}/versions/{version_id}/reviewables" + ) + for repre in instance.data["representations"]: + repre_tags = repre.get("tags") or [] + # Ignore representations that are not reviewable + if "webreview" not in repre_tags: + continue + + # exclude representations with are going to be published on farm + if "publish_on_farm" in repre_tags: + continue + + # Skip thumbnails + if repre.get("thumbnail") or "thumbnail" in repre_tags: + continue + + # include only thumbnail representations + repre_path = get_publish_repre_path( + instance, repre, False + ) + repre_ext = os.path.splitext(repre_path)[-1].lower() + + label = repre.get("outputName") + if not label: + # TODO how to label the reviewable if there is no output name? + label = "Main" + + # Make sure label is unique + orig_label = label + idx = 0 + while label in uploaded_labels: + idx += 1 + label = f"{orig_label}_{idx}" + + uploaded_labels.add(label) + + # Upload the reviewable + self.log.info(f"Uploading reviewable '{label}' ...") + headers = copy.deepcopy(base_headers) + ayon_con.upload_file( + f"/api/projects/{project_name}/versions/{version_id}/reviewables", + repre_path, + headers=headers + ) + + def _validate_path_in_project_roots(self, anatomy, file_path): """Checks if 'file_path' starts with any of the roots. From facf2425ecbb01883b6bd80f5b80e153e64c8df2 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 19 Jul 2024 12:51:22 +0200 Subject: [PATCH 02/18] mark openpype keys --- client/ayon_core/plugins/publish/integrate.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/plugins/publish/integrate.py b/client/ayon_core/plugins/publish/integrate.py index 8ab48ce9e5..3c29438f78 100644 --- a/client/ayon_core/plugins/publish/integrate.py +++ b/client/ayon_core/plugins/publish/integrate.py @@ -116,18 +116,19 @@ class IntegrateAsset(pyblish.api.InstancePlugin): # the database even if not used by the destination template db_representation_context_keys = [ "project", - "asset", "hierarchy", "folder", "task", "product", - "subset", - "family", "version", "representation", "username", "user", - "output" + "output", + # OpenPype keys - should be removed + "asset", # folder[name] + "subset", # product[name] + "family", # product[type] ] def process(self, instance): From 3268240c6bd002b447ef96c36ff569aafab57ad1 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 19 Jul 2024 15:11:42 +0200 Subject: [PATCH 03/18] implemented 'get_media_mime_type' --- client/ayon_core/lib/__init__.py | 2 + client/ayon_core/lib/transcoding.py | 87 +++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+) diff --git a/client/ayon_core/lib/__init__.py b/client/ayon_core/lib/__init__.py index 1f864284cd..12c391d867 100644 --- a/client/ayon_core/lib/__init__.py +++ b/client/ayon_core/lib/__init__.py @@ -109,6 +109,7 @@ from .transcoding import ( convert_ffprobe_fps_value, convert_ffprobe_fps_to_float, get_rescaled_command_arguments, + get_media_mime_type, ) from .plugin_tools import ( @@ -209,6 +210,7 @@ __all__ = [ "convert_ffprobe_fps_value", "convert_ffprobe_fps_to_float", "get_rescaled_command_arguments", + "get_media_mime_type", "compile_list_of_regexes", diff --git a/client/ayon_core/lib/transcoding.py b/client/ayon_core/lib/transcoding.py index bff28614ea..7ccd2ce819 100644 --- a/client/ayon_core/lib/transcoding.py +++ b/client/ayon_core/lib/transcoding.py @@ -6,6 +6,7 @@ import collections import tempfile import subprocess import platform +from typing import Optional import xml.etree.ElementTree @@ -1455,3 +1456,89 @@ def get_oiio_input_and_channel_args(oiio_input_info, alpha_default=None): input_arg += ":ch={}".format(input_channels_str) return input_arg, channels_arg + + +def _get_media_mime_type_from_ftyp(content): + if content[8:10] == b"qt": + return "video/quicktime" + + if content[8:12] == b"isom": + return "video/mp4" + if content[8:12] in (b"M4V\x20", b"mp42"): + return "video/mp4v" + if content[8:13] in (b"3gpis"): + return "video/3gpp" + # ( + # b"avc1", b"iso2", b"isom", b"mmp4", b"mp41", b"mp71", + # b"msnv", b"ndas", b"ndsc", b"ndsh", b"ndsm", b"ndsp", b"ndss", + # b"ndxc", b"ndxh", b"ndxm", b"ndxp", b"ndxs" + # ) + return None + + +def get_media_mime_type(filepath: str) -> Optional[str]: + """Determine Mime-Type of a file. + + Args: + filepath (str): Path to file. + + Returns: + Optional[str]: Mime type or None if is unknown mime type. + + """ + if not filepath or not os.path.exists(filepath): + return None + + with open(filepath, "rb") as stream: + content = stream.read() + + content_len = len(content) + # Pre-validation (largest definition check) + # - hopefully there cannot be media defined in less than 12 bytes + if content_len < 12: + return None + + # FTYP + if content[4:8] == b"ftyp": + return _get_media_mime_type_from_ftyp(content) + + # BMP + if content[0:2] == b"BM": + return "image/bmp" + + # Tiff + if content[0:2] in (b"MM", b"II"): + return "tiff" + + # PNG + if content[0:4] == b"\211PNG": + return "image/png" + + # SVG + if b'xmlns="http://www.w3.org/2000/svg"' in content: + return "image/svg+xml" + + # JPEG, JFIF or Exif + if ( + content[0:4] == b"\xff\xd8\xff\xdb" + or content[6:10] in (b"JFIF", b"Exif") + ): + return "image/jpeg" + + # Webp + if content[0:4] == b"RIFF" and content[8:12] == b"WEBP": + return "image/webp" + + # Gif + if content[0:6] in (b"GIF87a", b"GIF89a"): + return "gif" + + # Adobe PhotoShop file (8B > Adobe, PS > PhotoShop) + if content[0:4] == b"8BPS": + return "image/vnd.adobe.photoshop" + + # Windows ICO > this might be wild guess as multiple files can start + # with this header + if content[0:4] == b"\x00\x00\x01\x00": + return "image/x-icon" + return None From b87b9cc35493920af6a4da4bfb53bb6cf7002df0 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 19 Jul 2024 15:12:05 +0200 Subject: [PATCH 04/18] properly implement upload --- client/ayon_core/plugins/publish/integrate.py | 34 +++++++++++++------ 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/client/ayon_core/plugins/publish/integrate.py b/client/ayon_core/plugins/publish/integrate.py index 3c29438f78..ababbd88fc 100644 --- a/client/ayon_core/plugins/publish/integrate.py +++ b/client/ayon_core/plugins/publish/integrate.py @@ -20,7 +20,7 @@ from ayon_api.operations import ( ) from ayon_api.utils import create_entity_id -from ayon_core.lib import source_hash +from ayon_core.lib import source_hash, get_media_mime_type from ayon_core.lib.file_transaction import ( FileTransaction, DuplicateDestinationError @@ -990,12 +990,10 @@ class IntegrateAsset(pyblish.api.InstancePlugin): } def _upload_reviewable(self, project_name, version_id, instance): - uploaded_labels = set() ayon_con = get_server_api_connection() base_headers = ayon_con.get_headers() - endpoint = ( - f"/api/projects/{project_name}/versions/{version_id}/reviewables" - ) + + uploaded_labels = set() for repre in instance.data["representations"]: repre_tags = repre.get("tags") or [] # Ignore representations that are not reviewable @@ -1014,13 +1012,25 @@ class IntegrateAsset(pyblish.api.InstancePlugin): repre_path = get_publish_repre_path( instance, repre, False ) - repre_ext = os.path.splitext(repre_path)[-1].lower() + if not repre_path or not os.path.exists(repre_path): + # TODO log skipper path + continue + content_type = get_media_mime_type(repre_path) + if not content_type: + self.log.warning("Could not determine Content-Type") + continue + + # Use output name as label if available label = repre.get("outputName") - if not label: - # TODO how to label the reviewable if there is no output name? - label = "Main" + query = "" + if label: + query = f"?label={label}" + endpoint = ( + f"/api/projects/{project_name}" + f"/versions/{version_id}/reviewables{query}" + ) # Make sure label is unique orig_label = label idx = 0 @@ -1033,13 +1043,15 @@ class IntegrateAsset(pyblish.api.InstancePlugin): # Upload the reviewable self.log.info(f"Uploading reviewable '{label}' ...") headers = copy.deepcopy(base_headers) + headers["Content-Type"] = content_type + headers["x-file-name"] = os.path.basename(repre_path) + self.log.info(f"Uploading reviewable {repre_path}") ayon_con.upload_file( - f"/api/projects/{project_name}/versions/{version_id}/reviewables", + endpoint, repre_path, headers=headers ) - def _validate_path_in_project_roots(self, anatomy, file_path): """Checks if 'file_path' starts with any of the roots. From 8bac6f0277a10967a7636b37181781c351e066dc Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 19 Jul 2024 17:15:16 +0200 Subject: [PATCH 05/18] fix upload method --- client/ayon_core/plugins/publish/integrate.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/plugins/publish/integrate.py b/client/ayon_core/plugins/publish/integrate.py index ababbd88fc..18cd3e5cdd 100644 --- a/client/ayon_core/plugins/publish/integrate.py +++ b/client/ayon_core/plugins/publish/integrate.py @@ -11,6 +11,7 @@ from ayon_api import ( get_product_by_name, get_version_by_name, get_representations, + RequestTypes, ) from ayon_api.operations import ( OperationsSession, @@ -1028,7 +1029,7 @@ class IntegrateAsset(pyblish.api.InstancePlugin): query = f"?label={label}" endpoint = ( - f"/api/projects/{project_name}" + f"/projects/{project_name}" f"/versions/{version_id}/reviewables{query}" ) # Make sure label is unique @@ -1049,7 +1050,8 @@ class IntegrateAsset(pyblish.api.InstancePlugin): ayon_con.upload_file( endpoint, repre_path, - headers=headers + headers=headers, + request_type=RequestTypes.post, ) def _validate_path_in_project_roots(self, anatomy, file_path): From 595aae2f416f1ffb67830efc473c9890de9803c0 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 19 Jul 2024 17:15:37 +0200 Subject: [PATCH 06/18] modified default extract review settings --- server/settings/publish_plugins.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/server/settings/publish_plugins.py b/server/settings/publish_plugins.py index 36bb3f7340..987eefb39e 100644 --- a/server/settings/publish_plugins.py +++ b/server/settings/publish_plugins.py @@ -1011,7 +1011,8 @@ DEFAULT_PUBLISH_VALUES = { "ext": "png", "tags": [ "ftrackreview", - "kitsureview" + "kitsureview", + "webreview" ], "burnins": [], "ffmpeg_args": { @@ -1051,7 +1052,8 @@ DEFAULT_PUBLISH_VALUES = { "tags": [ "burnin", "ftrackreview", - "kitsureview" + "kitsureview", + "webreview" ], "burnins": [], "ffmpeg_args": { @@ -1063,7 +1065,10 @@ DEFAULT_PUBLISH_VALUES = { "output": [ "-pix_fmt yuv420p", "-crf 18", - "-intra" + "-c:a acc", + "-b:a 192k", + "-g 1", + "-movflags faststart" ] }, "filter": { From 194944e86c8f7a6a543025285a64d2031a9ea1ed Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 19 Jul 2024 17:35:39 +0200 Subject: [PATCH 07/18] validate supported version --- client/ayon_core/plugins/publish/integrate.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/client/ayon_core/plugins/publish/integrate.py b/client/ayon_core/plugins/publish/integrate.py index 18cd3e5cdd..ce8a955911 100644 --- a/client/ayon_core/plugins/publish/integrate.py +++ b/client/ayon_core/plugins/publish/integrate.py @@ -992,6 +992,14 @@ class IntegrateAsset(pyblish.api.InstancePlugin): def _upload_reviewable(self, project_name, version_id, instance): ayon_con = get_server_api_connection() + major, minor, _, _, _ = ayon_con.get_server_version_tuple() + if (major, minor) < (1, 3): + self.log.info( + "Skipping reviewable upload, supported from server 1.3.x." + f" User server version {ayon_con.get_server_version()}" + ) + return + base_headers = ayon_con.get_headers() uploaded_labels = set() From bb93a744bc19b0285e80f90c72df9de215ecc4d1 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 19 Jul 2024 18:34:08 +0200 Subject: [PATCH 08/18] fix possible clash of content type --- client/ayon_core/plugins/publish/integrate.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/plugins/publish/integrate.py b/client/ayon_core/plugins/publish/integrate.py index ce8a955911..ebea0447b2 100644 --- a/client/ayon_core/plugins/publish/integrate.py +++ b/client/ayon_core/plugins/publish/integrate.py @@ -1000,8 +1000,6 @@ class IntegrateAsset(pyblish.api.InstancePlugin): ) return - base_headers = ayon_con.get_headers() - uploaded_labels = set() for repre in instance.data["representations"]: repre_tags = repre.get("tags") or [] @@ -1051,8 +1049,8 @@ class IntegrateAsset(pyblish.api.InstancePlugin): # Upload the reviewable self.log.info(f"Uploading reviewable '{label}' ...") - headers = copy.deepcopy(base_headers) - headers["Content-Type"] = content_type + + headers = ayon_con.get_headers(content_type) headers["x-file-name"] = os.path.basename(repre_path) self.log.info(f"Uploading reviewable {repre_path}") ayon_con.upload_file( From 432ead8178e7f75229c67924e411f2ac6d3055e3 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 19 Jul 2024 18:54:12 +0200 Subject: [PATCH 09/18] removed invalid 3gpp --- client/ayon_core/lib/transcoding.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/client/ayon_core/lib/transcoding.py b/client/ayon_core/lib/transcoding.py index 7ccd2ce819..ead8b621b9 100644 --- a/client/ayon_core/lib/transcoding.py +++ b/client/ayon_core/lib/transcoding.py @@ -1466,8 +1466,6 @@ def _get_media_mime_type_from_ftyp(content): return "video/mp4" if content[8:12] in (b"M4V\x20", b"mp42"): return "video/mp4v" - if content[8:13] in (b"3gpis"): - return "video/3gpp" # ( # b"avc1", b"iso2", b"isom", b"mmp4", b"mp41", b"mp71", # b"msnv", b"ndas", b"ndsc", b"ndsh", b"ndsm", b"ndsp", b"ndss", From 7b01a73e7eb7716a411ef3a1bc10dd7ca2454f91 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 22 Jul 2024 11:08:11 +0200 Subject: [PATCH 10/18] support older ayon api --- client/ayon_core/plugins/publish/integrate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/plugins/publish/integrate.py b/client/ayon_core/plugins/publish/integrate.py index ebea0447b2..ab86855b10 100644 --- a/client/ayon_core/plugins/publish/integrate.py +++ b/client/ayon_core/plugins/publish/integrate.py @@ -11,8 +11,8 @@ from ayon_api import ( get_product_by_name, get_version_by_name, get_representations, - RequestTypes, ) +from ayon_api.server_api import RequestTypes from ayon_api.operations import ( OperationsSession, new_product_entity, From a6a65418e3a039a4b85995edaa4372d511ce1647 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 22 Jul 2024 11:12:36 +0200 Subject: [PATCH 11/18] duplicated code to integrate hero version --- .../plugins/publish/integrate_hero_version.py | 77 ++++++++++++++++++- 1 file changed, 76 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/plugins/publish/integrate_hero_version.py b/client/ayon_core/plugins/publish/integrate_hero_version.py index 4fb8b886a9..3e85226f75 100644 --- a/client/ayon_core/plugins/publish/integrate_hero_version.py +++ b/client/ayon_core/plugins/publish/integrate_hero_version.py @@ -10,10 +10,12 @@ from ayon_api.operations import ( OperationsSession, new_version_entity, ) +from ayon_api.server_api import RequestTypes from ayon_api.utils import create_entity_id -from ayon_core.lib import create_hard_link, source_hash +from ayon_core.lib import create_hard_link, source_hash, get_media_mime_type from ayon_core.pipeline.publish import ( + get_publish_repre_path, get_publish_template_name, OptionalPyblishPluginMixin, ) @@ -475,6 +477,9 @@ class IntegrateHeroVersion( ) op_session.commit() + self._upload_reviewable( + project_name, new_hero_version["id"], instance + ) # Remove backuped previous hero if ( @@ -672,3 +677,73 @@ class IntegrateHeroVersion( file_name = os.path.basename(value) file_name, _ = os.path.splitext(file_name) return file_name + + def _upload_reviewable(self, project_name, version_id, instance): + ayon_con = ayon_api.get_server_api_connection() + major, minor, _, _, _ = ayon_con.get_server_version_tuple() + if (major, minor) < (1, 3): + self.log.info( + "Skipping reviewable upload, supported from server 1.3.x." + f" User server version {ayon_con.get_server_version()}" + ) + return + + uploaded_labels = set() + for repre in instance.data["representations"]: + repre_tags = repre.get("tags") or [] + # Ignore representations that are not reviewable + if "webreview" not in repre_tags: + continue + + # exclude representations with are going to be published on farm + if "publish_on_farm" in repre_tags: + continue + + # Skip thumbnails + if repre.get("thumbnail") or "thumbnail" in repre_tags: + continue + + # include only thumbnail representations + repre_path = get_publish_repre_path( + instance, repre, False + ) + if not repre_path or not os.path.exists(repre_path): + # TODO log skipper path + continue + + content_type = get_media_mime_type(repre_path) + if not content_type: + self.log.warning("Could not determine Content-Type") + continue + + # Use output name as label if available + label = repre.get("outputName") + query = "" + if label: + query = f"?label={label}" + + endpoint = ( + f"/projects/{project_name}" + f"/versions/{version_id}/reviewables{query}" + ) + # Make sure label is unique + orig_label = label + idx = 0 + while label in uploaded_labels: + idx += 1 + label = f"{orig_label}_{idx}" + + uploaded_labels.add(label) + + # Upload the reviewable + self.log.info(f"Uploading reviewable '{label}' ...") + + headers = ayon_con.get_headers(content_type) + headers["x-file-name"] = os.path.basename(repre_path) + self.log.info(f"Uploading reviewable {repre_path}") + ayon_con.upload_file( + endpoint, + repre_path, + headers=headers, + request_type=RequestTypes.post, + ) From f61675b10e85c309c33a794fd5911c45af788425 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 22 Jul 2024 15:05:24 +0200 Subject: [PATCH 12/18] separate upload review to separated plugin --- client/ayon_core/plugins/publish/integrate.py | 75 +------------- .../plugins/publish/integrate_hero_version.py | 80 +-------------- .../plugins/publish/integrate_review.py | 98 +++++++++++++++++++ 3 files changed, 103 insertions(+), 150 deletions(-) create mode 100644 client/ayon_core/plugins/publish/integrate_review.py diff --git a/client/ayon_core/plugins/publish/integrate.py b/client/ayon_core/plugins/publish/integrate.py index ab86855b10..6837145f5d 100644 --- a/client/ayon_core/plugins/publish/integrate.py +++ b/client/ayon_core/plugins/publish/integrate.py @@ -6,13 +6,11 @@ import copy import clique import pyblish.api from ayon_api import ( - get_server_api_connection, get_attributes_for_type, get_product_by_name, get_version_by_name, get_representations, ) -from ayon_api.server_api import RequestTypes from ayon_api.operations import ( OperationsSession, new_product_entity, @@ -21,13 +19,12 @@ from ayon_api.operations import ( ) from ayon_api.utils import create_entity_id -from ayon_core.lib import source_hash, get_media_mime_type +from ayon_core.lib import source_hash from ayon_core.lib.file_transaction import ( FileTransaction, DuplicateDestinationError ) from ayon_core.pipeline.publish import ( - get_publish_repre_path, KnownPublishError, get_publish_template_name, ) @@ -990,76 +987,6 @@ class IntegrateAsset(pyblish.api.InstancePlugin): "hash_type": "op3", } - def _upload_reviewable(self, project_name, version_id, instance): - ayon_con = get_server_api_connection() - major, minor, _, _, _ = ayon_con.get_server_version_tuple() - if (major, minor) < (1, 3): - self.log.info( - "Skipping reviewable upload, supported from server 1.3.x." - f" User server version {ayon_con.get_server_version()}" - ) - return - - uploaded_labels = set() - for repre in instance.data["representations"]: - repre_tags = repre.get("tags") or [] - # Ignore representations that are not reviewable - if "webreview" not in repre_tags: - continue - - # exclude representations with are going to be published on farm - if "publish_on_farm" in repre_tags: - continue - - # Skip thumbnails - if repre.get("thumbnail") or "thumbnail" in repre_tags: - continue - - # include only thumbnail representations - repre_path = get_publish_repre_path( - instance, repre, False - ) - if not repre_path or not os.path.exists(repre_path): - # TODO log skipper path - continue - - content_type = get_media_mime_type(repre_path) - if not content_type: - self.log.warning("Could not determine Content-Type") - continue - - # Use output name as label if available - label = repre.get("outputName") - query = "" - if label: - query = f"?label={label}" - - endpoint = ( - f"/projects/{project_name}" - f"/versions/{version_id}/reviewables{query}" - ) - # Make sure label is unique - orig_label = label - idx = 0 - while label in uploaded_labels: - idx += 1 - label = f"{orig_label}_{idx}" - - uploaded_labels.add(label) - - # Upload the reviewable - self.log.info(f"Uploading reviewable '{label}' ...") - - headers = ayon_con.get_headers(content_type) - headers["x-file-name"] = os.path.basename(repre_path) - self.log.info(f"Uploading reviewable {repre_path}") - ayon_con.upload_file( - endpoint, - repre_path, - headers=headers, - request_type=RequestTypes.post, - ) - def _validate_path_in_project_roots(self, anatomy, file_path): """Checks if 'file_path' starts with any of the roots. diff --git a/client/ayon_core/plugins/publish/integrate_hero_version.py b/client/ayon_core/plugins/publish/integrate_hero_version.py index 3e85226f75..2163596864 100644 --- a/client/ayon_core/plugins/publish/integrate_hero_version.py +++ b/client/ayon_core/plugins/publish/integrate_hero_version.py @@ -10,12 +10,10 @@ from ayon_api.operations import ( OperationsSession, new_version_entity, ) -from ayon_api.server_api import RequestTypes from ayon_api.utils import create_entity_id -from ayon_core.lib import create_hard_link, source_hash, get_media_mime_type +from ayon_core.lib import create_hard_link, source_hash from ayon_core.pipeline.publish import ( - get_publish_repre_path, get_publish_template_name, OptionalPyblishPluginMixin, ) @@ -267,6 +265,9 @@ class IntegrateHeroVersion( project_name, "version", new_hero_version ) + # Store hero entity to 'instance.data' + instance.data["heroVersionEntity"] = new_hero_version + # Separate old representations into `to replace` and `to delete` old_repres_to_replace = {} old_repres_to_delete = {} @@ -477,9 +478,6 @@ class IntegrateHeroVersion( ) op_session.commit() - self._upload_reviewable( - project_name, new_hero_version["id"], instance - ) # Remove backuped previous hero if ( @@ -677,73 +675,3 @@ class IntegrateHeroVersion( file_name = os.path.basename(value) file_name, _ = os.path.splitext(file_name) return file_name - - def _upload_reviewable(self, project_name, version_id, instance): - ayon_con = ayon_api.get_server_api_connection() - major, minor, _, _, _ = ayon_con.get_server_version_tuple() - if (major, minor) < (1, 3): - self.log.info( - "Skipping reviewable upload, supported from server 1.3.x." - f" User server version {ayon_con.get_server_version()}" - ) - return - - uploaded_labels = set() - for repre in instance.data["representations"]: - repre_tags = repre.get("tags") or [] - # Ignore representations that are not reviewable - if "webreview" not in repre_tags: - continue - - # exclude representations with are going to be published on farm - if "publish_on_farm" in repre_tags: - continue - - # Skip thumbnails - if repre.get("thumbnail") or "thumbnail" in repre_tags: - continue - - # include only thumbnail representations - repre_path = get_publish_repre_path( - instance, repre, False - ) - if not repre_path or not os.path.exists(repre_path): - # TODO log skipper path - continue - - content_type = get_media_mime_type(repre_path) - if not content_type: - self.log.warning("Could not determine Content-Type") - continue - - # Use output name as label if available - label = repre.get("outputName") - query = "" - if label: - query = f"?label={label}" - - endpoint = ( - f"/projects/{project_name}" - f"/versions/{version_id}/reviewables{query}" - ) - # Make sure label is unique - orig_label = label - idx = 0 - while label in uploaded_labels: - idx += 1 - label = f"{orig_label}_{idx}" - - uploaded_labels.add(label) - - # Upload the reviewable - self.log.info(f"Uploading reviewable '{label}' ...") - - headers = ayon_con.get_headers(content_type) - headers["x-file-name"] = os.path.basename(repre_path) - self.log.info(f"Uploading reviewable {repre_path}") - ayon_con.upload_file( - endpoint, - repre_path, - headers=headers, - request_type=RequestTypes.post, - ) diff --git a/client/ayon_core/plugins/publish/integrate_review.py b/client/ayon_core/plugins/publish/integrate_review.py new file mode 100644 index 0000000000..1c62f25d94 --- /dev/null +++ b/client/ayon_core/plugins/publish/integrate_review.py @@ -0,0 +1,98 @@ +import os + +import pyblish.api +import ayon_api +from ayon_api.server_api import RequestTypes + +from ayon_core.lib import get_media_mime_type +from ayon_core.pipeline.publish import get_publish_repre_path + + +class IntegrateAYONReview(pyblish.api.InstancePlugin): + label = "Integrate AYON Review" + # Must happen after IntegrateNew + order = pyblish.api.IntegratorOrder + 0.15 + + def process(self, instance): + project_name = instance.context.data["projectName"] + src_version_entity = instance.data.get("versionEntity") + src_hero_version_entity = instance.data.get("heroVersionEntity") + for version_entity in ( + src_version_entity, + src_hero_version_entity, + ): + if not version_entity: + continue + + version_id = version_entity["id"] + self._upload_reviewable(project_name, version_id, instance) + + def _upload_reviewable(self, project_name, version_id, instance): + ayon_con = ayon_api.get_server_api_connection() + major, minor, _, _, _ = ayon_con.get_server_version_tuple() + if (major, minor) < (1, 3): + self.log.info( + "Skipping reviewable upload, supported from server 1.3.x." + f" User server version {ayon_con.get_server_version()}" + ) + return + + uploaded_labels = set() + for repre in instance.data["representations"]: + repre_tags = repre.get("tags") or [] + # Ignore representations that are not reviewable + if "webreview" not in repre_tags: + continue + + # exclude representations with are going to be published on farm + if "publish_on_farm" in repre_tags: + continue + + # Skip thumbnails + if repre.get("thumbnail") or "thumbnail" in repre_tags: + continue + + # include only thumbnail representations + repre_path = get_publish_repre_path( + instance, repre, False + ) + if not repre_path or not os.path.exists(repre_path): + # TODO log skipper path + continue + + content_type = get_media_mime_type(repre_path) + if not content_type: + self.log.warning("Could not determine Content-Type") + continue + + # Use output name as label if available + label = repre.get("outputName") + query = "" + if label: + query = f"?label={label}" + + endpoint = ( + f"/projects/{project_name}" + f"/versions/{version_id}/reviewables{query}" + ) + # Make sure label is unique + orig_label = label + idx = 0 + while label in uploaded_labels: + idx += 1 + label = f"{orig_label}_{idx}" + + uploaded_labels.add(label) + + # Upload the reviewable + self.log.info(f"Uploading reviewable '{label}' ...") + + headers = ayon_con.get_headers(content_type) + headers["x-file-name"] = os.path.basename(repre_path) + self.log.info(f"Uploading reviewable {repre_path}") + ayon_con.upload_file( + endpoint, + repre_path, + headers=headers, + request_type=RequestTypes.post, + ) \ No newline at end of file From d060244d49f6185195fcca04737419b2dbb06639 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 23 Jul 2024 09:44:42 +0200 Subject: [PATCH 13/18] remove unecessary comment --- client/ayon_core/plugins/publish/integrate_review.py | 1 - 1 file changed, 1 deletion(-) diff --git a/client/ayon_core/plugins/publish/integrate_review.py b/client/ayon_core/plugins/publish/integrate_review.py index 1c62f25d94..a5979e3b1c 100644 --- a/client/ayon_core/plugins/publish/integrate_review.py +++ b/client/ayon_core/plugins/publish/integrate_review.py @@ -52,7 +52,6 @@ class IntegrateAYONReview(pyblish.api.InstancePlugin): if repre.get("thumbnail") or "thumbnail" in repre_tags: continue - # include only thumbnail representations repre_path = get_publish_repre_path( instance, repre, False ) From 722b3a5d63a623ed46700528692c1dad020edfd9 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 23 Jul 2024 09:53:21 +0200 Subject: [PATCH 14/18] fix label calculation --- .../plugins/publish/integrate_review.py | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/client/ayon_core/plugins/publish/integrate_review.py b/client/ayon_core/plugins/publish/integrate_review.py index a5979e3b1c..6a79e72c1e 100644 --- a/client/ayon_core/plugins/publish/integrate_review.py +++ b/client/ayon_core/plugins/publish/integrate_review.py @@ -64,8 +64,7 @@ class IntegrateAYONReview(pyblish.api.InstancePlugin): self.log.warning("Could not determine Content-Type") continue - # Use output name as label if available - label = repre.get("outputName") + label = self._get_review_label(repre, uploaded_labels) query = "" if label: query = f"?label={label}" @@ -74,14 +73,6 @@ class IntegrateAYONReview(pyblish.api.InstancePlugin): f"/projects/{project_name}" f"/versions/{version_id}/reviewables{query}" ) - # Make sure label is unique - orig_label = label - idx = 0 - while label in uploaded_labels: - idx += 1 - label = f"{orig_label}_{idx}" - - uploaded_labels.add(label) # Upload the reviewable self.log.info(f"Uploading reviewable '{label}' ...") @@ -94,4 +85,16 @@ class IntegrateAYONReview(pyblish.api.InstancePlugin): repre_path, headers=headers, request_type=RequestTypes.post, - ) \ No newline at end of file + ) + + def _get_review_label(self, repre, uploaded_labels): + # Use output name as label if available + label = repre.get("outputName") + if not label: + return None + orig_label = label + idx = 0 + while label in uploaded_labels: + idx += 1 + label = f"{orig_label}_{idx}" + return label From 754f2d0e5f010cc4bcc95a6506db87b1f7bd4587 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 23 Jul 2024 09:53:30 +0200 Subject: [PATCH 15/18] log representation path --- client/ayon_core/plugins/publish/integrate_review.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/plugins/publish/integrate_review.py b/client/ayon_core/plugins/publish/integrate_review.py index 6a79e72c1e..1adac420a6 100644 --- a/client/ayon_core/plugins/publish/integrate_review.py +++ b/client/ayon_core/plugins/publish/integrate_review.py @@ -61,7 +61,9 @@ class IntegrateAYONReview(pyblish.api.InstancePlugin): content_type = get_media_mime_type(repre_path) if not content_type: - self.log.warning("Could not determine Content-Type") + self.log.warning( + f"Could not determine Content-Type for {repre_path}" + ) continue label = self._get_review_label(repre, uploaded_labels) From 5f7dd573197e8acc9048f87c9f57586851e732cf Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 23 Jul 2024 18:10:33 +0200 Subject: [PATCH 16/18] use filename if label is empty --- client/ayon_core/plugins/publish/integrate_review.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/plugins/publish/integrate_review.py b/client/ayon_core/plugins/publish/integrate_review.py index 1adac420a6..790c7da9c6 100644 --- a/client/ayon_core/plugins/publish/integrate_review.py +++ b/client/ayon_core/plugins/publish/integrate_review.py @@ -75,12 +75,12 @@ class IntegrateAYONReview(pyblish.api.InstancePlugin): f"/projects/{project_name}" f"/versions/{version_id}/reviewables{query}" ) - + filename = os.path.basename(repre_path) # Upload the reviewable - self.log.info(f"Uploading reviewable '{label}' ...") + self.log.info(f"Uploading reviewable '{label or filename}' ...") headers = ayon_con.get_headers(content_type) - headers["x-file-name"] = os.path.basename(repre_path) + headers["x-file-name"] = filename self.log.info(f"Uploading reviewable {repre_path}") ayon_con.upload_file( endpoint, From f5abf6e981821819752e94c193154609781ce95e Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 24 Jul 2024 09:47:50 +0200 Subject: [PATCH 17/18] Enhance comments and logs Co-authored-by: Roy Nieterau --- client/ayon_core/plugins/publish/integrate_review.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/plugins/publish/integrate_review.py b/client/ayon_core/plugins/publish/integrate_review.py index 790c7da9c6..0a6b24adb4 100644 --- a/client/ayon_core/plugins/publish/integrate_review.py +++ b/client/ayon_core/plugins/publish/integrate_review.py @@ -10,7 +10,7 @@ from ayon_core.pipeline.publish import get_publish_repre_path class IntegrateAYONReview(pyblish.api.InstancePlugin): label = "Integrate AYON Review" - # Must happen after IntegrateNew + # Must happen after IntegrateAsset order = pyblish.api.IntegratorOrder + 0.15 def process(self, instance): @@ -33,7 +33,7 @@ class IntegrateAYONReview(pyblish.api.InstancePlugin): if (major, minor) < (1, 3): self.log.info( "Skipping reviewable upload, supported from server 1.3.x." - f" User server version {ayon_con.get_server_version()}" + f" Current server version {ayon_con.get_server_version()}" ) return From b5336f2e483912b05b884375aee0073f76a1b0cf Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 24 Jul 2024 13:51:40 +0200 Subject: [PATCH 18/18] removed upload reviewable from integrate plugin --- client/ayon_core/plugins/publish/integrate.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/client/ayon_core/plugins/publish/integrate.py b/client/ayon_core/plugins/publish/integrate.py index 6837145f5d..69c14465eb 100644 --- a/client/ayon_core/plugins/publish/integrate.py +++ b/client/ayon_core/plugins/publish/integrate.py @@ -349,8 +349,6 @@ class IntegrateAsset(pyblish.api.InstancePlugin): self.log.debug("{}".format(op_session.to_data())) op_session.commit() - self._upload_reviewable(project_name, version_entity["id"], instance) - # Backwards compatibility used in hero integration. # todo: can we avoid the need to store this? instance.data["published_representations"] = {