mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-24 21:04:40 +01:00
Merged in bugfix/PYPE-160-mov-generation (pull request #60)
Bugfix/PYPE-160 mov generation Approved-by: Milan Kolar <milan@orbi.tools>
This commit is contained in:
commit
d800519176
7 changed files with 99 additions and 114 deletions
28
pype/plugins/global/publish/validate_ffmpeg_installed.py
Normal file
28
pype/plugins/global/publish/validate_ffmpeg_installed.py
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
import pyblish.api
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
|
||||
class ValidateFfmpegInstallef(pyblish.api.Validator):
|
||||
"""Validate availability of ffmpeg tool in PATH"""
|
||||
|
||||
order = pyblish.api.ValidatorOrder
|
||||
label = 'Validate ffmpeg installation'
|
||||
families = ['review']
|
||||
optional = True
|
||||
|
||||
def is_tool(self, name):
|
||||
try:
|
||||
devnull = open(os.devnull, "w")
|
||||
subprocess.Popen(
|
||||
[name], stdout=devnull, stderr=devnull
|
||||
).communicate()
|
||||
except OSError as e:
|
||||
if e.errno == os.errno.ENOENT:
|
||||
return False
|
||||
return True
|
||||
|
||||
def process(self, instance):
|
||||
if self.is_tool('ffmpeg') is False:
|
||||
self.log.error("ffmpeg not found in PATH")
|
||||
raise RuntimeError('ffmpeg not installed.')
|
||||
|
|
@ -26,4 +26,7 @@ class CollectModelData(pyblish.api.InstancePlugin):
|
|||
instance.data['endFrame'] = frame
|
||||
|
||||
# make ftrack publishable
|
||||
instance.data["families"] = ['ftrack']
|
||||
if instance.data.get('families'):
|
||||
instance.data['families'].append('ftrack')
|
||||
else:
|
||||
instance.data['families'] = ['ftrack']
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import pymel.core as pm
|
|||
import pyblish.api
|
||||
import avalon.api
|
||||
|
||||
class CollectReviewData(pyblish.api.InstancePlugin):
|
||||
class CollectReview(pyblish.api.InstancePlugin):
|
||||
"""Collect Review data
|
||||
|
||||
"""
|
||||
|
|
@ -15,12 +15,9 @@ class CollectReviewData(pyblish.api.InstancePlugin):
|
|||
|
||||
def process(self, instance):
|
||||
|
||||
# make ftrack publishable
|
||||
instance.data["families"] = ['ftrack']
|
||||
context = instance.context
|
||||
self.log.debug('instance: {}'.format(instance))
|
||||
|
||||
task = avalon.api.Session["AVALON_TASK"]
|
||||
# pseudo code
|
||||
|
||||
# get cameras
|
||||
members = instance.data['setMembers']
|
||||
|
|
@ -33,7 +30,7 @@ class CollectReviewData(pyblish.api.InstancePlugin):
|
|||
camera = cameras[0]
|
||||
self.log.debug('camera: {}'.format(camera))
|
||||
|
||||
objectset = context.data['objectsets']
|
||||
objectset = instance.context.data['objectsets']
|
||||
|
||||
reviewable_subset = None
|
||||
reviewable_subset = list(set(members) & set(objectset))
|
||||
|
|
@ -41,18 +38,34 @@ class CollectReviewData(pyblish.api.InstancePlugin):
|
|||
assert len(reviewable_subset) <= 1, "Multiple subsets for review"
|
||||
self.log.debug('subset for review: {}'.format(reviewable_subset))
|
||||
|
||||
for inst in context:
|
||||
self.log.debug('instance: {}'.format(instance))
|
||||
i = 0
|
||||
for inst in instance.context:
|
||||
data = instance.context[i].data
|
||||
|
||||
if inst.name == reviewable_subset[0]:
|
||||
if inst.data.get('families'):
|
||||
inst.data['families'].append('review')
|
||||
if data.get('families'):
|
||||
data['families'].append('review')
|
||||
else:
|
||||
inst.data['families'] = ['review']
|
||||
inst.data['review_camera'] = camera
|
||||
self.log.info('adding review family to {}'.format(reviewable_subset))
|
||||
data['families'] = ['review']
|
||||
self.log.debug('adding review family to {}'.format(reviewable_subset))
|
||||
data['review_camera'] = camera
|
||||
data["publish"] = False
|
||||
data['startFrameReview'] = instance.data['startFrame']
|
||||
data['endFrameReview'] = instance.data['endFrame']
|
||||
data['handles'] = instance.data['handles']
|
||||
data['step'] = instance.data['step']
|
||||
data['fps'] = instance.data['fps']
|
||||
cmds.setAttr(str(instance) + '.active', 0)
|
||||
inst.data['publish'] = 0
|
||||
inst.data['active'] = 0
|
||||
i += 1
|
||||
instance.context[i].data.update(data)
|
||||
instance.data['remove'] = True
|
||||
else:
|
||||
instance.data['subset'] = task + 'Review'
|
||||
instance.data['review_camera'] = camera
|
||||
instance.data['startFrameReview'] = instance.data['startFrame']
|
||||
instance.data['endFrameReview'] = instance.data['endFrame']
|
||||
|
||||
# make ftrack publishable
|
||||
instance.data["families"] = ['ftrack']
|
||||
|
||||
cmds.setAttr(str(instance) + '.active', 1)
|
||||
|
|
|
|||
|
|
@ -1,38 +1,26 @@
|
|||
import os
|
||||
import subprocess
|
||||
import contextlib
|
||||
import time
|
||||
import sys
|
||||
|
||||
import capture_gui
|
||||
import clique
|
||||
|
||||
import pype.maya.lib as lib
|
||||
import pype.api
|
||||
import avalon.maya
|
||||
|
||||
from maya import cmds
|
||||
from maya import cmds, mel
|
||||
import pymel.core as pm
|
||||
from pype.vendor import ffmpeg
|
||||
reload(ffmpeg)
|
||||
|
||||
import avalon.maya
|
||||
|
||||
# import maya_utils as mu
|
||||
|
||||
# from tweakHUD import master
|
||||
# from tweakHUD import draft_hud as dHUD
|
||||
# from tweakHUD import ftrackStrings as fStrings
|
||||
|
||||
#
|
||||
# def soundOffsetFunc(oSF, SF, H):
|
||||
# tmOff = (oSF - H) - SF
|
||||
# return tmOff
|
||||
|
||||
|
||||
# TODO: move codec settings to presets
|
||||
class ExtractQuicktime(pype.api.Extractor):
|
||||
"""Extract a Camera as Alembic.
|
||||
"""Extract Quicktime from viewport capture.
|
||||
|
||||
The cameras gets baked to world space by default. Only when the instance's
|
||||
`bakeToWorldSpace` is set to False it will include its full hierarchy.
|
||||
Takes review camera and creates review Quicktime video based on viewport
|
||||
capture.
|
||||
|
||||
"""
|
||||
|
||||
|
|
@ -44,8 +32,17 @@ class ExtractQuicktime(pype.api.Extractor):
|
|||
def process(self, instance):
|
||||
self.log.info("Extracting capture..")
|
||||
|
||||
start = instance.data.get("startFrame", 1)
|
||||
end = instance.data.get("endFrame", 25)
|
||||
# get scene fps
|
||||
fps = mel.eval('currentTimeUnitToFPS()')
|
||||
|
||||
# if start and end frames cannot be determined, get them
|
||||
# from Maya timeline
|
||||
start = instance.data.get("startFrameReview")
|
||||
end = instance.data.get("endFrameReview")
|
||||
if start is None:
|
||||
start = cmds.playbackOptions(query=True, animationStartTime=True)
|
||||
if end is None:
|
||||
end = cmds.playbackOptions(query=True, animationEndTime=True)
|
||||
self.log.info("start: {}, end: {}".format(start, end))
|
||||
handles = instance.data.get("handles", 0)
|
||||
if handles:
|
||||
|
|
@ -53,46 +50,7 @@ class ExtractQuicktime(pype.api.Extractor):
|
|||
end += handles
|
||||
|
||||
# get cameras
|
||||
members = instance.data['setMembers']
|
||||
camera = instance.data['review_camera']
|
||||
# cameras = cmds.ls(members, leaf=True, shapes=True, long=True,
|
||||
# dag=True, type="camera")
|
||||
|
||||
# # validate required settings
|
||||
# assert len(cameras) == 1, "Not a single camera found in extraction"
|
||||
# camera = cameras[0]
|
||||
|
||||
|
||||
# project_code = ftrack_data['Project']['code']
|
||||
# task_type = ftrack_data['Task']['type']
|
||||
#
|
||||
# # load Preset
|
||||
# studio_repos = os.path.abspath(os.environ.get('studio_repos'))
|
||||
# shot_preset_path = os.path.join(studio_repos, 'maya',
|
||||
# 'capture_gui_presets',
|
||||
# (project_code + '_' + task_type + '_' + asset + '.json'))
|
||||
#
|
||||
# task_preset_path = os.path.join(studio_repos, 'maya',
|
||||
# 'capture_gui_presets',
|
||||
# (project_code + '_' + task_type + '.json'))
|
||||
#
|
||||
# project_preset_path = os.path.join(studio_repos, 'maya',
|
||||
# 'capture_gui_presets',
|
||||
# (project_code + '.json'))
|
||||
#
|
||||
# default_preset_path = os.path.join(studio_repos, 'maya',
|
||||
# 'capture_gui_presets',
|
||||
# 'default.json')
|
||||
#
|
||||
# if os.path.isfile(shot_preset_path):
|
||||
# preset_to_use = shot_preset_path
|
||||
# elif os.path.isfile(task_preset_path):
|
||||
# preset_to_use = task_preset_path
|
||||
# elif os.path.isfile(project_preset_path):
|
||||
# preset_to_use = project_preset_path
|
||||
# else:
|
||||
# preset_to_use = default_preset_path
|
||||
|
||||
capture_preset = ""
|
||||
try:
|
||||
preset = lib.load_capture_preset(capture_preset)
|
||||
|
|
@ -100,15 +58,13 @@ class ExtractQuicktime(pype.api.Extractor):
|
|||
preset = {}
|
||||
self.log.info('using viewport preset: {}'.format(capture_preset))
|
||||
|
||||
#preset["off_screen"] = False
|
||||
|
||||
preset['camera'] = camera
|
||||
preset['format'] = "image"
|
||||
# preset['compression'] = "qt"
|
||||
preset['quality'] = 50
|
||||
preset['compression'] = "jpg"
|
||||
preset['start_frame'] = 1
|
||||
preset['end_frame'] = 25
|
||||
preset['start_frame'] = start
|
||||
preset['end_frame'] = end
|
||||
preset['camera_options'] = {
|
||||
"displayGateMask": False,
|
||||
"displayResolution": False,
|
||||
|
|
@ -143,50 +99,34 @@ class ExtractQuicktime(pype.api.Extractor):
|
|||
self.log.info("file list {}".format(playblast))
|
||||
# self.log.info("Calculating HUD data overlay")
|
||||
|
||||
# stagingdir = "C:/Users/milan.kolar/AppData/Local/Temp/pyblish_tmp_ucsymm"
|
||||
collected_frames = os.listdir(stagingdir)
|
||||
collections, remainder = clique.assemble(collected_frames)
|
||||
input_path = os.path.join(stagingdir, collections[0].format('{head}{padding}{tail}'))
|
||||
input_path = os.path.join(
|
||||
stagingdir, collections[0].format('{head}{padding}{tail}'))
|
||||
self.log.info("input {}".format(input_path))
|
||||
|
||||
movieFile = filename + ".mov"
|
||||
full_movie_path = os.path.join(stagingdir, movieFile)
|
||||
self.log.info("output {}".format(full_movie_path))
|
||||
# fls = [os.path.join(stagingdir, filename).replace("\\","/") for f in os.listdir( dir_path ) if f.endswith(preset['compression'])]
|
||||
# self.log.info("file list {}}".format(fls[0]))
|
||||
|
||||
out, err = (
|
||||
ffmpeg
|
||||
.input(input_path, framerate=25)
|
||||
.output(full_movie_path)
|
||||
.run(overwrite_output=True)
|
||||
)
|
||||
with avalon.maya.suspended_refresh():
|
||||
try:
|
||||
(
|
||||
ffmpeg
|
||||
.input(input_path, framerate=fps, start_number=int(start))
|
||||
.output(full_movie_path)
|
||||
.run(overwrite_output=True,
|
||||
capture_stdout=True,
|
||||
capture_stderr=True)
|
||||
)
|
||||
except ffmpeg.Error as e:
|
||||
ffmpeg_error = 'ffmpeg error: {}'.format(e.stderr)
|
||||
self.log.error(ffmpeg_error)
|
||||
raise RuntimeError(ffmpeg_error)
|
||||
|
||||
if "files" not in instance.data:
|
||||
instance.data["files"] = list()
|
||||
instance.data["files"].append(movieFile)
|
||||
|
||||
# ftrackStrings = fStrings.annotationData()
|
||||
# nData = ftrackStrings.niceData
|
||||
# nData['version'] = instance.context.data('version')
|
||||
# fFrame = int(pm.playbackOptions( q = True, minTime = True))
|
||||
# eFrame = int(pm.playbackOptions( q = True, maxTime = True))
|
||||
# nData['frame'] = [(str("{0:05d}".format(f))) for f in range(fFrame, eFrame + 1)]
|
||||
# soundOfst = int(float(nData['oFStart'])) - int(float(nData['handle'])) - fFrame
|
||||
# soundFile = mu.giveMePublishedAudio()
|
||||
# self.log.info("SOUND offset %s" % str(soundOfst))
|
||||
# self.log.info("SOUND source video to %s" % str(soundFile))
|
||||
# ann = dHUD.draftAnnotate()
|
||||
# if soundFile:
|
||||
# ann.addAnotation(seqFls = fls, outputMoviePth = movieFullPth, annotateDataArr = nData, soundFile = soundFile, soundOffset = soundOfst)
|
||||
# else:
|
||||
# ann.addAnotation(seqFls = fls, outputMoviePth = movieFullPth, annotateDataArr = nData)
|
||||
|
||||
# for f in fls:
|
||||
# os.remove(f)
|
||||
|
||||
# playblast = (ann.expPth).replace("\\","/")
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def maintained_time():
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ class ValidateMeshNonZeroEdgeLength(pyblish.api.InstancePlugin):
|
|||
version = (0, 1, 0)
|
||||
label = 'Mesh Edge Length Non Zero'
|
||||
actions = [pype.maya.action.SelectInvalidAction]
|
||||
optional = True
|
||||
|
||||
__tolerance = 1e-5
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ def get_file_rule(rule):
|
|||
return mel.eval('workspace -query -fileRuleEntry "{}"'.format(rule))
|
||||
|
||||
|
||||
class ValidateRenderImageRule(pyblish.api.ContextPlugin):
|
||||
class ValidateRenderImageRule(pyblish.api.InstancePlugin):
|
||||
"""Validates "images" file rule is set to "renders/"
|
||||
|
||||
"""
|
||||
|
|
@ -19,7 +19,7 @@ class ValidateRenderImageRule(pyblish.api.ContextPlugin):
|
|||
hosts = ["maya"]
|
||||
families = ["renderlayer"]
|
||||
|
||||
def process(self, context):
|
||||
def process(self, instance):
|
||||
|
||||
assert get_file_rule("images") == "renders", (
|
||||
"Workspace's `images` file rule must be set to: renders"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue