Compare commits

...

19 commits

Author SHA1 Message Date
Jakub Trllo
f9bbab9944
Merge pull request #1622 from ynput/bugfix/902-ay-3875_ayon-integrate-hero-for-review
Integrate Hero: Use FileTransaction in integrate plugin
2025-12-22 13:29:11 +01:00
Jakub Trllo
826d22b166
Merge branch 'develop' into bugfix/902-ay-3875_ayon-integrate-hero-for-review 2025-12-22 13:27:09 +01:00
Jakub Trllo
b6b2726795
Merge pull request #1621 from ynput/enhancement/YN-0290_provide_source_version_description
Library: provide source version description
2025-12-19 16:21:18 +01:00
Jakub Trllo
1612b0297d fix long lines 2025-12-19 11:35:26 +01:00
Petr Kalis
a802285a6c Removed unnecessary f 2025-12-19 11:25:30 +01:00
Petr Kalis
07edce9c9c Fix missing quote 2025-12-19 11:25:08 +01:00
Petr Kalis
0dc34c32d8
Merge branch 'develop' into enhancement/YN-0290_provide_source_version_description 2025-12-19 11:23:44 +01:00
Jakub Trllo
7485d99cf6 use FileTransaction in integrate hero 2025-12-19 11:23:37 +01:00
Petr Kalis
3d0cd51e65
Updates to description format
Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com>
2025-12-19 11:22:39 +01:00
Petr Kalis
8f1eebfcbf
Refactor version parts concatenation
Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com>
2025-12-19 11:22:20 +01:00
Petr Kalis
f46f1d2e8d
Refactor description concatenation
Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com>
2025-12-19 11:21:54 +01:00
Jakub Trllo
92d4da9efa
Merge pull request #1620 from ynput/bugfix/thumbnail-safe-rescale-args
Extract thumbnail from source: Safe collection of rescale arguments
2025-12-18 17:13:33 +01:00
Petr Kalis
1be1a30b38 Always put src description on new line 2025-12-18 16:47:33 +01:00
Jakub Trllo
c55c6a2675 fix doubled line 2025-12-18 16:44:15 +01:00
Jakub Trllo
818a9f21f3 safe collection of rescale arguments 2025-12-18 16:39:52 +01:00
Petr Kalis
a83ebe3c8d Merge branch 'bugfix/thumbnail-args' into enhancement/YN-0290_provide_source_version_description 2025-12-18 16:25:09 +01:00
Petr Kalis
0b6c0f3de9 Added source version description
Links copied version to original with author information. Author is not passed on version as it might require admin privileges.
2025-12-18 16:24:06 +01:00
github-actions[bot]
4b4ccad085 chore(): update bug report / version 2025-12-18 13:13:01 +00:00
Ynbot
11f5c4ba8b [Automated] Update version in package.py for develop 2025-12-18 13:11:59 +00:00
7 changed files with 97 additions and 70 deletions

View file

@ -35,6 +35,7 @@ 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.7.0
- 1.6.13 - 1.6.13
- 1.6.12 - 1.6.12
- 1.6.11 - 1.6.11

View file

@ -160,9 +160,14 @@ class ExtractThumbnailFromSource(pyblish.api.InstancePlugin):
dst_path: str, dst_path: str,
) -> bool: ) -> bool:
self.log.debug("Outputting thumbnail with OIIO: {}".format(dst_path)) self.log.debug("Outputting thumbnail with OIIO: {}".format(dst_path))
resolution_args = self._get_resolution_args( try:
"oiiotool", src_path resolution_args = self._get_resolution_args(
) "oiiotool", src_path
)
except Exception:
self.log.warning("Failed to get resolution args for OIIO.")
return False
oiio_cmd = get_oiio_tool_args("oiiotool", "-a", src_path) oiio_cmd = get_oiio_tool_args("oiiotool", "-a", src_path)
if resolution_args: if resolution_args:
# resize must be before -o # resize must be before -o
@ -188,9 +193,13 @@ class ExtractThumbnailFromSource(pyblish.api.InstancePlugin):
src_path: str, src_path: str,
dst_path: str, dst_path: str,
) -> bool: ) -> bool:
resolution_args = self._get_resolution_args( try:
"ffmpeg", src_path resolution_args = self._get_resolution_args(
) "ffmpeg", src_path
)
except Exception:
self.log.warning("Failed to get resolution args for ffmpeg.")
return False
max_int = str(2147483647) max_int = str(2147483647)
ffmpeg_cmd = get_ffmpeg_tool_args( ffmpeg_cmd = get_ffmpeg_tool_args(

View file

@ -1,11 +1,8 @@
import os import os
import sys
import copy import copy
import errno
import itertools import itertools
import shutil import shutil
from concurrent.futures import ThreadPoolExecutor
from speedcopy import copyfile
import clique import clique
import pyblish.api import pyblish.api
@ -16,11 +13,15 @@ from ayon_api.operations import (
) )
from ayon_api.utils import create_entity_id from ayon_api.utils import create_entity_id
from ayon_core.lib import create_hard_link, source_hash from ayon_core.lib import source_hash
from ayon_core.lib.file_transaction import wait_for_future_errors from ayon_core.lib.file_transaction import (
FileTransaction,
DuplicateDestinationError,
)
from ayon_core.pipeline.publish import ( from ayon_core.pipeline.publish import (
get_publish_template_name, get_publish_template_name,
OptionalPyblishPluginMixin, OptionalPyblishPluginMixin,
KnownPublishError,
) )
@ -421,19 +422,40 @@ class IntegrateHeroVersion(
(repre_entity, dst_paths) (repre_entity, dst_paths)
) )
self.path_checks = [] file_transactions = FileTransaction(
log=self.log,
# Enforce unique transfers
allow_queue_replacements=False
)
mode = FileTransaction.MODE_COPY
if self.use_hardlinks:
mode = FileTransaction.MODE_LINK
# Copy(hardlink) paths of source and destination files try:
# TODO should we *only* create hardlinks? for src_path, dst_path in itertools.chain(
# TODO should we keep files for deletion until this is successful? src_to_dst_file_paths,
with ThreadPoolExecutor(max_workers=8) as executor: other_file_paths_mapping
futures = [ ):
executor.submit(self.copy_file, src_path, dst_path) file_transactions.add(src_path, dst_path, mode=mode)
for src_path, dst_path in itertools.chain(
src_to_dst_file_paths, other_file_paths_mapping self.log.debug("Integrating source files to destination ...")
) file_transactions.process()
]
wait_for_future_errors(executor, futures) except DuplicateDestinationError as exc:
# Raise DuplicateDestinationError as KnownPublishError
# and rollback the transactions
file_transactions.rollback()
raise KnownPublishError(exc).with_traceback(sys.exc_info()[2])
except Exception as exc:
# Rollback the transactions
file_transactions.rollback()
self.log.critical("Error when copying files", exc_info=True)
raise exc
# Finalizing can't rollback safely so no use for moving it to
# the try, except.
file_transactions.finalize()
# Update prepared representation etity data with files # Update prepared representation etity data with files
# and integrate it to server. # and integrate it to server.
@ -622,48 +644,6 @@ class IntegrateHeroVersion(
).format(path)) ).format(path))
return path return path
def copy_file(self, src_path, dst_path):
# TODO check drives if are the same to check if cas hardlink
dirname = os.path.dirname(dst_path)
try:
os.makedirs(dirname)
self.log.debug("Folder(s) created: \"{}\"".format(dirname))
except OSError as exc:
if exc.errno != errno.EEXIST:
self.log.error("An unexpected error occurred.", exc_info=True)
raise
self.log.debug("Folder already exists: \"{}\"".format(dirname))
if self.use_hardlinks:
# First try hardlink and copy if paths are cross drive
self.log.debug("Hardlinking file \"{}\" to \"{}\"".format(
src_path, dst_path
))
try:
create_hard_link(src_path, dst_path)
# Return when successful
return
except OSError as exc:
# re-raise exception if different than
# EXDEV - cross drive path
# EINVAL - wrong format, must be NTFS
self.log.debug(
"Hardlink failed with errno:'{}'".format(exc.errno))
if exc.errno not in [errno.EXDEV, errno.EINVAL]:
raise
self.log.debug(
"Hardlinking failed, falling back to regular copy...")
self.log.debug("Copying file \"{}\" to \"{}\"".format(
src_path, dst_path
))
copyfile(src_path, dst_path)
def version_from_representations(self, project_name, repres): def version_from_representations(self, project_name, repres):
for repre in repres: for repre in repres:
version = ayon_api.get_version_by_id( version = ayon_api.get_version_by_id(

View file

@ -1045,10 +1045,23 @@ class ProjectPushItemProcess:
copied_tags = self._get_transferable_tags(src_version_entity) copied_tags = self._get_transferable_tags(src_version_entity)
copied_status = self._get_transferable_status(src_version_entity) copied_status = self._get_transferable_status(src_version_entity)
description_parts = []
dst_attr_description = dst_attrib.get("description")
if dst_attr_description:
description_parts.append(dst_attr_description)
description = self._create_src_version_description(
self._item.src_project_name,
src_version_entity
)
if description:
description_parts.append(description)
dst_attrib["description"] = "\n\n".join(description_parts)
version_entity = new_version_entity( version_entity = new_version_entity(
dst_version, dst_version,
product_id, product_id,
author=src_version_entity["author"],
status=copied_status, status=copied_status,
tags=copied_tags, tags=copied_tags,
task_id=self._task_info.get("id"), task_id=self._task_info.get("id"),
@ -1370,6 +1383,30 @@ class ProjectPushItemProcess:
return copied_status["name"] return copied_status["name"]
return None return None
def _create_src_version_description(
self,
src_project_name: str,
src_version_entity: dict[str, Any]
) -> str:
"""Creates description text about source version."""
src_version_id = src_version_entity["id"]
src_author = src_version_entity["author"]
query = "&".join([
f"project={src_project_name}",
"type=version",
f"id={src_version_id}"
])
version_url = (
f"{ayon_api.get_base_url()}"
f"/projects/{src_project_name}/products?{query}"
)
description = (
f"Version copied from from {version_url} "
f"created by '{src_author}', "
)
return description
class IntegrateModel: class IntegrateModel:
def __init__(self, controller): def __init__(self, controller):

View file

@ -1,3 +1,3 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
"""Package declaring AYON addon 'core' version.""" """Package declaring AYON addon 'core' version."""
__version__ = "1.7.0" __version__ = "1.7.0+dev"

View file

@ -1,6 +1,6 @@
name = "core" name = "core"
title = "Core" title = "Core"
version = "1.7.0" version = "1.7.0+dev"
client_dir = "ayon_core" client_dir = "ayon_core"

View file

@ -5,7 +5,7 @@
[tool.poetry] [tool.poetry]
name = "ayon-core" name = "ayon-core"
version = "1.7.0" version = "1.7.0+dev"
description = "" description = ""
authors = ["Ynput Team <team@ynput.io>"] authors = ["Ynput Team <team@ynput.io>"]
readme = "README.md" readme = "README.md"