Merge pull request #790 from ynput/feature/integrate-reviewables-to-ayon

Review: Integrate reviewables to AYON
This commit is contained in:
Jakub Trllo 2024-07-30 14:35:52 +02:00 committed by GitHub
commit 354e9af391
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 205 additions and 7 deletions

View file

@ -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",

View file

@ -6,6 +6,7 @@ import collections
import tempfile
import subprocess
import platform
from typing import Optional
import xml.etree.ElementTree
@ -1455,3 +1456,87 @@ 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"
# (
# 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

View file

@ -114,18 +114,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):

View file

@ -265,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 = {}

View file

@ -0,0 +1,102 @@
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 IntegrateAsset
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" Current 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
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(
f"Could not determine Content-Type for {repre_path}"
)
continue
label = self._get_review_label(repre, uploaded_labels)
query = ""
if label:
query = f"?label={label}"
endpoint = (
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 or filename}' ...")
headers = ayon_con.get_headers(content_type)
headers["x-file-name"] = filename
self.log.info(f"Uploading reviewable {repre_path}")
ayon_con.upload_file(
endpoint,
repre_path,
headers=headers,
request_type=RequestTypes.post,
)
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

View file

@ -1012,7 +1012,8 @@ DEFAULT_PUBLISH_VALUES = {
"ext": "png",
"tags": [
"ftrackreview",
"kitsureview"
"kitsureview",
"webreview"
],
"burnins": [],
"ffmpeg_args": {
@ -1052,7 +1053,8 @@ DEFAULT_PUBLISH_VALUES = {
"tags": [
"burnin",
"ftrackreview",
"kitsureview"
"kitsureview",
"webreview"
],
"burnins": [],
"ffmpeg_args": {
@ -1064,7 +1066,10 @@ DEFAULT_PUBLISH_VALUES = {
"output": [
"-pix_fmt yuv420p",
"-crf 18",
"-intra"
"-c:a acc",
"-b:a 192k",
"-g 1",
"-movflags faststart"
]
},
"filter": {