mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-24 21:04:40 +01:00
Merge branch 'develop' into feature/PYPE-14-ftrack-stop-timer
This commit is contained in:
commit
c7f4476545
16 changed files with 636 additions and 202 deletions
|
|
@ -219,7 +219,7 @@ class SyncToAvalon(BaseAction):
|
|||
})
|
||||
|
||||
elif self.avalon_project['name'] != entity['full_name']:
|
||||
raise ValueError('You can\'t change name {} to {}, avalon DB won\'t work properly!'.format(avalon_asset['name'], name))
|
||||
raise ValueError('You can\'t change name {} to {}, avalon DB won\'t work properly!'.format(self.avalon_project['name'], name))
|
||||
|
||||
data = ftrack_utils.get_data(self, entity, session,self.custom_attributes)
|
||||
|
||||
|
|
@ -235,7 +235,7 @@ class SyncToAvalon(BaseAction):
|
|||
if self.ca_mongoid in entity['custom_attributes']:
|
||||
entity['custom_attributes'][self.ca_mongoid] = str(self.projectId)
|
||||
else:
|
||||
self.log.error("Custom attribute for <{}> is not created.".format(entity['name']))
|
||||
self.log.error('Custom attribute for "{}" is not created.'.format(entity['name']))
|
||||
return
|
||||
|
||||
## ----- ASSETS ------
|
||||
|
|
@ -271,15 +271,12 @@ class SyncToAvalon(BaseAction):
|
|||
self.log.debug("Asset {} - created".format(name))
|
||||
|
||||
# Raise error if it seems to be different ent. with same name
|
||||
else:
|
||||
aD = avalon_asset['data']
|
||||
# check_attr = ['parents', 'ftrackId', 'visualParent']
|
||||
if (avalon_asset['data']['parents'] != data['parents'] or
|
||||
avalon_asset['silo'] != silo):
|
||||
elif (avalon_asset['data']['parents'] != data['parents'] or
|
||||
avalon_asset['silo'] != silo):
|
||||
raise ValueError('In Avalon DB already exists entity with name "{0}"'.format(name))
|
||||
|
||||
elif avalon_asset['name'] != entity['name']:
|
||||
raise ValueError('You can\'t change name {} to {}, avalon DB won\'t work properly - please create new asset'.format(avalon_asset['name'], name))
|
||||
raise ValueError('You can\'t change name {} to {}, avalon DB won\'t work properly - please set name back'.format(avalon_asset['name'], name))
|
||||
elif avalon_asset['silo'] != silo or avalon_asset['data']['parents'] != data['parents']:
|
||||
old_path = "/".join(avalon_asset['data']['parents'])
|
||||
new_path = "/".join(data['parents'])
|
||||
|
|
|
|||
225
pype/ftrack/events/event_sync_to_avalon.py
Normal file
225
pype/ftrack/events/event_sync_to_avalon.py
Normal file
|
|
@ -0,0 +1,225 @@
|
|||
import os
|
||||
import sys
|
||||
import re
|
||||
import ftrack_api
|
||||
from ftrack_event_handler import BaseEvent
|
||||
from pype import lib
|
||||
from avalon import io, inventory
|
||||
from avalon.vendor import toml
|
||||
from bson.objectid import ObjectId
|
||||
from pype.ftrack import ftrack_utils
|
||||
|
||||
class Sync_to_Avalon(BaseEvent):
|
||||
|
||||
def launch(self, session, entities, event):
|
||||
|
||||
self.ca_mongoid = 'avalon_mongo_id'
|
||||
# If mongo_id textfield has changed: RETURN!
|
||||
# - infinite loop
|
||||
for ent in event['data']['entities']:
|
||||
if 'keys' in ent:
|
||||
if self.ca_mongoid in ent['keys']:
|
||||
return
|
||||
self.proj = None
|
||||
|
||||
# get project
|
||||
for entity in entities:
|
||||
try:
|
||||
base_proj = entity['link'][0]
|
||||
except:
|
||||
continue
|
||||
self.proj = session.get(base_proj['type'], base_proj['id'])
|
||||
break
|
||||
|
||||
# check if project is set to auto-sync
|
||||
if (self.proj is None or
|
||||
'avalon_auto_sync' not in self.proj['custom_attributes'] or
|
||||
self.proj['custom_attributes']['avalon_auto_sync'] is False):
|
||||
return
|
||||
|
||||
# check if project have Custom Attribute 'avalon_mongo_id'
|
||||
if self.ca_mongoid not in self.proj['custom_attributes']:
|
||||
message = "Custom attribute '{}' for 'Project' is not created or don't have set permissions for API".format(self.ca_mongoid)
|
||||
self.log.warning(message)
|
||||
self.show_message(event, message, False)
|
||||
return
|
||||
|
||||
self.projectId = self.proj['custom_attributes'][self.ca_mongoid]
|
||||
|
||||
os.environ["AVALON_PROJECT"] = self.proj['full_name']
|
||||
|
||||
# get avalon project if possible
|
||||
io.install()
|
||||
try:
|
||||
self.avalon_project = io.find_one({"_id": ObjectId(self.projectId)})
|
||||
except:
|
||||
self.avalon_project = None
|
||||
|
||||
importEntities = []
|
||||
if self.avalon_project is None:
|
||||
self.avalon_project = io.find_one({"type": "project", "name": self.proj["full_name"]})
|
||||
if self.avalon_project is None:
|
||||
importEntities.append(self.proj)
|
||||
else:
|
||||
self.projectId = self.avalon_project['_id']
|
||||
|
||||
io.uninstall()
|
||||
|
||||
for entity in entities:
|
||||
if entity.entity_type.lower() in ['task']:
|
||||
entity = entity['parent']
|
||||
|
||||
try:
|
||||
mongo_id = entity['custom_attributes'][self.ca_mongoid]
|
||||
except:
|
||||
message = "Custom attribute '{}' for '{}' is not created or don't have set permissions for API".format(self.ca_mongoid, entity.entity_type)
|
||||
self.log.warning(message)
|
||||
self.show_message(event, message, False)
|
||||
return
|
||||
|
||||
if entity not in importEntities:
|
||||
importEntities.append(entity)
|
||||
|
||||
if len(importEntities) < 1:
|
||||
return
|
||||
|
||||
self.setAvalonAttributes()
|
||||
|
||||
io.install()
|
||||
try:
|
||||
for entity in importEntities:
|
||||
self.importToAvalon(session, entity)
|
||||
session.commit()
|
||||
|
||||
except ValueError as ve:
|
||||
message = str(ve)
|
||||
self.show_message(event, message, False)
|
||||
self.log.warning(message)
|
||||
|
||||
except Exception as e:
|
||||
message = str(e)
|
||||
ftrack_message = "SyncToAvalon event ended with unexpected error please check log file for more information."
|
||||
self.show_message(event, ftrack_message, False)
|
||||
self.log.error(message)
|
||||
|
||||
io.uninstall()
|
||||
|
||||
return
|
||||
|
||||
def importToAvalon(self, session, entity):
|
||||
if self.ca_mongoid not in entity['custom_attributes']:
|
||||
raise ValueError("Custom attribute '{}' for '{}' is not created or don't have set permissions for API".format(self.ca_mongoid, entity['name']))
|
||||
|
||||
ftrack_utils.avalon_check_name(entity)
|
||||
|
||||
entity_type = entity.entity_type
|
||||
|
||||
if entity_type in ['Project']:
|
||||
type = 'project'
|
||||
name = entity['full_name']
|
||||
config = ftrack_utils.get_config(entity)
|
||||
template = lib.get_avalon_project_template_schema()
|
||||
|
||||
if self.avalon_project is None:
|
||||
inventory.save(name, config, template)
|
||||
self.avalon_project = io.find_one({'type': 'project', 'name': name})
|
||||
|
||||
elif self.avalon_project['name'] != name:
|
||||
raise ValueError('You can\'t change name {} to {}, avalon DB won\'t work properly!'.format(self.avalon_project['name'], name))
|
||||
|
||||
self.projectId = self.avalon_project['_id']
|
||||
|
||||
data = ftrack_utils.get_data(self, entity, session,self.custom_attributes)
|
||||
|
||||
io.update_many(
|
||||
{"_id": ObjectId(self.projectId)},
|
||||
{'$set':{
|
||||
'name':name,
|
||||
'config':config,
|
||||
'data':data,
|
||||
}})
|
||||
|
||||
entity['custom_attributes'][self.ca_mongoid] = str(self.projectId)
|
||||
|
||||
return
|
||||
|
||||
if self.avalon_project is None:
|
||||
self.importToAvalon(session, self.proj)
|
||||
|
||||
data = ftrack_utils.get_data(self, entity, session,self.custom_attributes)
|
||||
|
||||
# return if entity is silo
|
||||
if len(data['parents']) == 0:
|
||||
return
|
||||
else:
|
||||
silo = data['parents'][0]
|
||||
|
||||
name = entity['name']
|
||||
|
||||
os.environ["AVALON_ASSET"] = name
|
||||
os.environ['AVALON_SILO'] = silo
|
||||
|
||||
avalon_asset = None
|
||||
# existence of this custom attr is already checked
|
||||
mongo_id = entity['custom_attributes'][self.ca_mongoid]
|
||||
|
||||
if mongo_id is not "":
|
||||
avalon_asset = io.find_one({'_id': ObjectId(mongo_id)})
|
||||
|
||||
if avalon_asset is None:
|
||||
avalon_asset = io.find_one({'type': 'asset', 'name': name})
|
||||
if avalon_asset is None:
|
||||
mongo_id = inventory.create_asset(name, silo, data, ObjectId(self.projectId))
|
||||
# Raise error if it seems to be different ent. with same name
|
||||
elif (avalon_asset['data']['parents'] != data['parents'] or
|
||||
avalon_asset['silo'] != silo):
|
||||
raise ValueError('In Avalon DB already exists entity with name "{0}"'.format(name))
|
||||
elif avalon_asset['name'] != entity['name']:
|
||||
raise ValueError('You can\'t change name {} to {}, avalon DB won\'t work properly - please set name back'.format(avalon_asset['name'], name))
|
||||
elif avalon_asset['silo'] != silo or avalon_asset['data']['parents'] != data['parents']:
|
||||
old_path = "/".join(avalon_asset['data']['parents'])
|
||||
new_path = "/".join(data['parents'])
|
||||
raise ValueError('You can\'t move with entities. Entity "{}" was moved from "{}" to "{}" , avalon DB won\'t work properly'.format(avalon_asset['name'], old_path, new_path))
|
||||
|
||||
|
||||
io.update_many(
|
||||
{"_id": ObjectId(mongo_id)},
|
||||
{'$set':{
|
||||
'name':name,
|
||||
'silo':silo,
|
||||
'data':data,
|
||||
'parent': ObjectId(self.projectId)}})
|
||||
|
||||
entity['custom_attributes'][self.ca_mongoid] = str(mongo_id)
|
||||
|
||||
def setAvalonAttributes(self):
|
||||
self.custom_attributes = []
|
||||
all_avalon_attr = self.session.query('CustomAttributeGroup where name is "avalon"').one()
|
||||
for cust_attr in all_avalon_attr['custom_attribute_configurations']:
|
||||
if 'avalon_' not in cust_attr['key']:
|
||||
self.custom_attributes.append(cust_attr)
|
||||
|
||||
def _translate_event(self, session, event):
|
||||
exceptions = ['assetversion', 'job', 'user', 'reviewsessionobject', 'timer', 'socialfeed', 'timelog']
|
||||
_selection = event['data'].get('entities',[])
|
||||
|
||||
_entities = list()
|
||||
for entity in _selection:
|
||||
if entity['entityType'] in exceptions:
|
||||
continue
|
||||
_entities.append(
|
||||
(
|
||||
session.get(self._get_entity_type(entity), entity.get('entityId'))
|
||||
)
|
||||
)
|
||||
|
||||
return [_entities, event]
|
||||
|
||||
def register(session, **kw):
|
||||
'''Register plugin. Called when used as an plugin.'''
|
||||
|
||||
if not isinstance(session, ftrack_api.session.Session):
|
||||
return
|
||||
|
||||
event = Sync_to_Avalon(session)
|
||||
event.register()
|
||||
25
pype/ftrack/events/event_test.py
Normal file
25
pype/ftrack/events/event_test.py
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
import os
|
||||
import sys
|
||||
import re
|
||||
import ftrack_api
|
||||
from ftrack_event_handler import BaseEvent
|
||||
from app import api
|
||||
|
||||
class Test_Event(BaseEvent):
|
||||
|
||||
def launch(self, session, entities, event):
|
||||
|
||||
'''just a testing event'''
|
||||
|
||||
# self.log.info(event)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def register(session, **kw):
|
||||
'''Register plugin. Called when used as an plugin.'''
|
||||
if not isinstance(session, ftrack_api.session.Session):
|
||||
return
|
||||
|
||||
event = Test_Event(session)
|
||||
event.register()
|
||||
154
pype/ftrack/events/ftrack_event_handler.py
Normal file
154
pype/ftrack/events/ftrack_event_handler.py
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
# :coding: utf-8
|
||||
# :copyright: Copyright (c) 2017 ftrack
|
||||
import os
|
||||
import logging
|
||||
import getpass
|
||||
# import platform
|
||||
import ftrack_api
|
||||
import toml
|
||||
from avalon import io, lib, pipeline
|
||||
from avalon import session as sess
|
||||
import acre
|
||||
|
||||
from app.api import (
|
||||
Templates,
|
||||
Logger
|
||||
)
|
||||
|
||||
|
||||
class BaseEvent(object):
|
||||
'''Custom Event base class
|
||||
|
||||
BaseEvent is based on ftrack.update event
|
||||
- get entities from event
|
||||
|
||||
If want to use different event base
|
||||
- override register and *optional _translate_event method
|
||||
|
||||
'''
|
||||
|
||||
def __init__(self, session):
|
||||
'''Expects a ftrack_api.Session instance'''
|
||||
|
||||
self.log = Logger.getLogger(self.__class__.__name__)
|
||||
|
||||
self._session = session
|
||||
|
||||
@property
|
||||
def session(self):
|
||||
'''Return current session.'''
|
||||
return self._session
|
||||
|
||||
def register(self):
|
||||
'''Registers the event, subscribing the the discover and launch topics.'''
|
||||
self.session.event_hub.subscribe('topic=ftrack.update', self._launch)
|
||||
|
||||
self.log.info("Event '{}' - Registered successfully".format(self.__class__.__name__))
|
||||
|
||||
def _translate_event(self, session, event):
|
||||
'''Return *event* translated structure to be used with the API.'''
|
||||
_selection = event['data'].get('entities',[])
|
||||
|
||||
_entities = list()
|
||||
for entity in _selection:
|
||||
if entity['entityType'] in ['socialfeed']:
|
||||
continue
|
||||
_entities.append(
|
||||
(
|
||||
session.get(self._get_entity_type(entity), entity.get('entityId'))
|
||||
)
|
||||
)
|
||||
|
||||
return [
|
||||
_entities,
|
||||
event
|
||||
]
|
||||
|
||||
def _get_entity_type(self, entity):
|
||||
'''Return translated entity type tht can be used with API.'''
|
||||
# Get entity type and make sure it is lower cased. Most places except
|
||||
# the component tab in the Sidebar will use lower case notation.
|
||||
entity_type = entity.get('entityType').replace('_', '').lower()
|
||||
|
||||
for schema in self.session.schemas:
|
||||
alias_for = schema.get('alias_for')
|
||||
|
||||
if (
|
||||
alias_for and isinstance(alias_for, str) and
|
||||
alias_for.lower() == entity_type
|
||||
):
|
||||
return schema['id']
|
||||
|
||||
for schema in self.session.schemas:
|
||||
if schema['id'].lower() == entity_type:
|
||||
return schema['id']
|
||||
|
||||
raise ValueError(
|
||||
'Unable to translate entity type: {0}.'.format(entity_type)
|
||||
)
|
||||
|
||||
def _launch(self, event):
|
||||
|
||||
self.session.reset()
|
||||
|
||||
args = self._translate_event(
|
||||
self.session, event
|
||||
)
|
||||
|
||||
self.launch(
|
||||
self.session, *args
|
||||
)
|
||||
|
||||
return
|
||||
|
||||
def launch(self, session, entities, event):
|
||||
'''Callback method for the custom action.
|
||||
|
||||
return either a bool ( True if successful or False if the action failed )
|
||||
or a dictionary with they keys `message` and `success`, the message should be a
|
||||
string and will be displayed as feedback to the user, success should be a bool,
|
||||
True if successful or False if the action failed.
|
||||
|
||||
*session* is a `ftrack_api.Session` instance
|
||||
|
||||
*entities* is a list of tuples each containing the entity type and the entity id.
|
||||
If the entity is a hierarchical you will always get the entity
|
||||
type TypedContext, once retrieved through a get operation you
|
||||
will have the "real" entity type ie. example Shot, Sequence
|
||||
or Asset Build.
|
||||
|
||||
*event* the unmodified original event
|
||||
|
||||
'''
|
||||
raise NotImplementedError()
|
||||
|
||||
def show_message(self, event, input_message, result = False):
|
||||
"""
|
||||
Shows message to user who triggered event
|
||||
- event - just source of user id
|
||||
- input_message - message that is shown to user
|
||||
- result - changes color of message (based on ftrack settings)
|
||||
- True = Violet
|
||||
- False = Red
|
||||
"""
|
||||
if not isinstance(result, bool):
|
||||
result = False
|
||||
|
||||
try:
|
||||
message = str(input_message)
|
||||
except:
|
||||
return
|
||||
|
||||
user_id = event['source']['user']['id']
|
||||
self.session.event_hub.publish(
|
||||
ftrack_api.event.base.Event(
|
||||
topic='ftrack.action.trigger-user-interface',
|
||||
data=dict(
|
||||
type='message',
|
||||
success=result,
|
||||
message=message
|
||||
),
|
||||
target='applicationId=ftrack.client.web and user.id="{0}"'.format(user_id)
|
||||
),
|
||||
on_error='ignore'
|
||||
)
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
# import ftrack_api as local session
|
||||
import ftrack_api
|
||||
from utils import print_entity_head
|
||||
#
|
||||
session = ftrack_api.Session()
|
||||
|
||||
# ----------------------------------
|
||||
|
||||
|
||||
def test_event(event):
|
||||
'''just a testing event'''
|
||||
|
||||
# start of event procedure ----------------------------------
|
||||
for entity in event['data'].get('entities', []):
|
||||
if entity['entityType'] == 'task' and entity['action'] == 'update':
|
||||
|
||||
print "\n\nevent script: {}".format(__file__)
|
||||
print_entity_head.print_entity_head(entity, session)
|
||||
|
||||
# for k in task.keys():
|
||||
# print k, task[k]
|
||||
# print '\n'
|
||||
# print task['assignments']
|
||||
|
||||
for e in entity.keys():
|
||||
print '{0}: {1}'.format(e, entity[e])
|
||||
|
||||
# end of event procedure ----------------------------------
|
||||
|
|
@ -62,7 +62,7 @@ def get_data(parent, entity, session, custom_attributes):
|
|||
for parent in parents:
|
||||
parentId = io.find_one({'type': 'asset', 'name': parName})['_id']
|
||||
if parent['parent'].entity_type != 'project' and parentId is None:
|
||||
parent.importToAvalon(parent)
|
||||
parent.importToAvalon(session, parent)
|
||||
parentId = io.find_one({'type': 'asset', 'name': parName})['_id']
|
||||
|
||||
hierarchy = os.path.sep.join(folderStruct)
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import sys
|
|||
from avalon import api as avalon
|
||||
from pyblish import api as pyblish
|
||||
|
||||
from .. import api as pype
|
||||
from .. import api
|
||||
|
||||
from pype.nuke import menu
|
||||
|
||||
|
|
@ -15,12 +15,12 @@ import nuke
|
|||
|
||||
# removing logger handler created in avalon_core
|
||||
for name, handler in [(handler.get_name(), handler)
|
||||
for handler in pype.Logger.logging.root.handlers[:]]:
|
||||
for handler in api.Logger.logging.root.handlers[:]]:
|
||||
if "pype" not in str(name).lower():
|
||||
pype.Logger.logging.root.removeHandler(handler)
|
||||
api.Logger.logging.root.removeHandler(handler)
|
||||
|
||||
|
||||
log = pype.Logger.getLogger(__name__, "nuke")
|
||||
log = api.Logger.getLogger(__name__, "nuke")
|
||||
|
||||
AVALON_CONFIG = os.getenv("AVALON_CONFIG", "pype")
|
||||
|
||||
|
|
@ -37,7 +37,7 @@ self = sys.modules[__name__]
|
|||
self.nLogger = None
|
||||
|
||||
|
||||
class NukeHandler(pype.Logger.logging.Handler):
|
||||
class NukeHandler(api.Logger.logging.Handler):
|
||||
'''
|
||||
Nuke Handler - emits logs into nuke's script editor.
|
||||
warning will emit nuke.warning()
|
||||
|
|
@ -45,7 +45,7 @@ class NukeHandler(pype.Logger.logging.Handler):
|
|||
'''
|
||||
|
||||
def __init__(self):
|
||||
pype.Logger.logging.Handler.__init__(self)
|
||||
api.Logger.logging.Handler.__init__(self)
|
||||
self.set_name("Pype_Nuke_Handler")
|
||||
|
||||
def emit(self, record):
|
||||
|
|
@ -65,11 +65,11 @@ class NukeHandler(pype.Logger.logging.Handler):
|
|||
nuke_handler = NukeHandler()
|
||||
if nuke_handler.get_name() \
|
||||
not in [handler.get_name()
|
||||
for handler in pype.Logger.logging.root.handlers[:]]:
|
||||
pype.Logger.logging.getLogger().addHandler(nuke_handler)
|
||||
for handler in api.Logger.logging.root.handlers[:]]:
|
||||
api.Logger.logging.getLogger().addHandler(nuke_handler)
|
||||
|
||||
if not self.nLogger:
|
||||
self.nLogger = pype.Logger
|
||||
self.nLogger = api.Logger
|
||||
|
||||
|
||||
def reload_config():
|
||||
|
|
@ -86,8 +86,6 @@ def reload_config():
|
|||
"app.api",
|
||||
"{}.api".format(AVALON_CONFIG),
|
||||
"{}.templates".format(AVALON_CONFIG),
|
||||
"{}.nuke".format(AVALON_CONFIG),
|
||||
"{}.nuke.lib".format(AVALON_CONFIG),
|
||||
"{}.nuke.templates".format(AVALON_CONFIG),
|
||||
"{}.nuke.menu".format(AVALON_CONFIG)
|
||||
):
|
||||
|
|
@ -100,7 +98,8 @@ def reload_config():
|
|||
|
||||
|
||||
def install():
|
||||
pype.fill_avalon_workdir()
|
||||
|
||||
api.fill_avalon_workdir()
|
||||
reload_config()
|
||||
|
||||
log.info("Registering Nuke plug-ins..")
|
||||
|
|
@ -128,7 +127,7 @@ def install():
|
|||
menu.install()
|
||||
|
||||
# load data from templates
|
||||
pype.load_data_from_templates()
|
||||
api.load_data_from_templates()
|
||||
|
||||
|
||||
def uninstall():
|
||||
|
|
@ -140,7 +139,7 @@ def uninstall():
|
|||
pyblish.deregister_callback("instanceToggled", on_pyblish_instance_toggled)
|
||||
|
||||
# reset data from templates
|
||||
pype.reset_data_from_templates()
|
||||
api.reset_data_from_templates()
|
||||
|
||||
|
||||
def on_pyblish_instance_toggled(instance, old_value, new_value):
|
||||
|
|
|
|||
157
pype/nuke/lib.py
157
pype/nuke/lib.py
|
|
@ -2,6 +2,7 @@ import sys
|
|||
from collections import OrderedDict
|
||||
from pprint import pprint
|
||||
from avalon.vendor.Qt import QtGui
|
||||
from avalon import api, io
|
||||
import avalon.nuke
|
||||
import pype.api as pype
|
||||
import nuke
|
||||
|
|
@ -99,57 +100,6 @@ def add_rendering_knobs(node):
|
|||
return node
|
||||
|
||||
|
||||
def update_frame_range(start, end, root=None):
|
||||
"""Set Nuke script start and end frame range
|
||||
|
||||
Args:
|
||||
start (float, int): start frame
|
||||
end (float, int): end frame
|
||||
root (object, Optional): root object from nuke's script
|
||||
|
||||
Returns:
|
||||
None
|
||||
|
||||
"""
|
||||
|
||||
knobs = {
|
||||
"first_frame": start,
|
||||
"last_frame": end
|
||||
}
|
||||
|
||||
with avalon.nuke.viewer_update_and_undo_stop():
|
||||
for key, value in knobs.items():
|
||||
if root:
|
||||
root[key].setValue(value)
|
||||
else:
|
||||
nuke.root()[key].setValue(value)
|
||||
|
||||
|
||||
def get_additional_data(container):
|
||||
"""Get Nuke's related data for the container
|
||||
|
||||
Args:
|
||||
container(dict): the container found by the ls() function
|
||||
|
||||
Returns:
|
||||
dict
|
||||
"""
|
||||
|
||||
node = container["_tool"]
|
||||
tile_color = node['tile_color'].value()
|
||||
if tile_color is None:
|
||||
return {}
|
||||
|
||||
hex = '%08x' % tile_color
|
||||
rgba = [
|
||||
float(int(hex[0:2], 16)) / 255.0,
|
||||
float(int(hex[2:4], 16)) / 255.0,
|
||||
float(int(hex[4:6], 16)) / 255.0
|
||||
]
|
||||
|
||||
return {"color": QtGui.QColor().fromRgbF(rgba[0], rgba[1], rgba[2])}
|
||||
|
||||
|
||||
def set_viewers_colorspace(viewer):
|
||||
assert isinstance(viewer, dict), log.error(
|
||||
"set_viewers_colorspace(): argument should be dictionary")
|
||||
|
|
@ -245,6 +195,111 @@ def get_avalon_knob_data(node):
|
|||
return None
|
||||
return data
|
||||
|
||||
|
||||
def reset_resolution():
|
||||
"""Set resolution to project resolution."""
|
||||
log.info("Reseting resolution")
|
||||
project = io.find_one({"type": "project"})
|
||||
asset = api.Session["AVALON_ASSET"]
|
||||
asset = io.find_one({"name": asset, "type": "asset"})
|
||||
|
||||
try:
|
||||
width = asset["data"].get("resolution_width", 1920)
|
||||
height = asset["data"].get("resolution_height", 1080)
|
||||
pixel_aspect = asset["data"].get("pixel_aspect", 1)
|
||||
bbox = asset["data"].get("crop", "0.0.1920.1080")
|
||||
|
||||
try:
|
||||
x, y, r, t = bbox.split(".")
|
||||
except Exception as e:
|
||||
x = 0
|
||||
y = 0
|
||||
r = width
|
||||
t = height
|
||||
log.error("{}: {} \nFormat:Crop need to be set with dots, example: "
|
||||
"0.0.1920.1080, /nSetting to default".format(__name__, e))
|
||||
|
||||
except KeyError:
|
||||
log.warning(
|
||||
"No resolution information found for \"{0}\".".format(
|
||||
project["name"]
|
||||
)
|
||||
)
|
||||
return
|
||||
|
||||
used_formats = list()
|
||||
for f in nuke.formats():
|
||||
if project["name"] in str(f.name()):
|
||||
used_formats.append(f)
|
||||
else:
|
||||
format_name = project["name"] + "_1"
|
||||
|
||||
crnt_fmt_str = ""
|
||||
if used_formats:
|
||||
check_format = used_formats[-1]
|
||||
format_name = "{}_{}".format(
|
||||
project["name"],
|
||||
int(used_formats[-1].name()[-1])+1
|
||||
)
|
||||
log.info(
|
||||
"Format exists: {}. "
|
||||
"Will create new: {}...".format(
|
||||
used_formats[-1].name(),
|
||||
format_name)
|
||||
)
|
||||
crnt_fmt_kargs = {
|
||||
"width": (check_format.width()),
|
||||
"height": (check_format.height()),
|
||||
"x": int(check_format.x()),
|
||||
"y": int(check_format.y()),
|
||||
"r": int(check_format.r()),
|
||||
"t": int(check_format.t()),
|
||||
"pixel_aspect": float(check_format.pixelAspect())
|
||||
}
|
||||
crnt_fmt_str = make_format_string(**crnt_fmt_kargs)
|
||||
log.info("crnt_fmt_str: {}".format(crnt_fmt_str))
|
||||
|
||||
new_fmt_kargs = {
|
||||
"width": int(width),
|
||||
"height": int(height),
|
||||
"x": int(x),
|
||||
"y": int(y),
|
||||
"r": int(r),
|
||||
"t": int(t),
|
||||
"pixel_aspect": float(pixel_aspect),
|
||||
"project_name": format_name
|
||||
}
|
||||
|
||||
new_fmt_str = make_format_string(**new_fmt_kargs)
|
||||
log.info("new_fmt_str: {}".format(new_fmt_str))
|
||||
|
||||
if new_fmt_str not in crnt_fmt_str:
|
||||
make_format(frm_str=new_fmt_str,
|
||||
project_name=new_fmt_kargs["project_name"])
|
||||
|
||||
log.info("Format is set")
|
||||
|
||||
|
||||
def make_format_string(**args):
|
||||
format_str = (
|
||||
"{width} "
|
||||
"{height} "
|
||||
"{x} "
|
||||
"{y} "
|
||||
"{r} "
|
||||
"{t} "
|
||||
"{pixel_aspect:.2f}".format(**args)
|
||||
)
|
||||
return format_str
|
||||
|
||||
|
||||
def make_format(**args):
|
||||
log.info("Format does't exist, will create: \n{}".format(args))
|
||||
nuke.addFormat("{frm_str} "
|
||||
"{project_name}".format(**args))
|
||||
nuke.root()["format"].setValue("{project_name}".format(**args))
|
||||
|
||||
|
||||
# TODO: bellow functions are wip and needs to be check where they are used
|
||||
# ------------------------------------
|
||||
|
||||
|
|
|
|||
|
|
@ -5,8 +5,17 @@ from pype.nuke import lib
|
|||
|
||||
|
||||
def install():
|
||||
|
||||
menubar = nuke.menu("Nuke")
|
||||
menu = menubar.findItem(Session["AVALON_LABEL"])
|
||||
|
||||
menu.addSeparator()
|
||||
menu.addCommand("Set colorspace...", lib.set_colorspace)
|
||||
# replace reset resolution from avalon core to pype's
|
||||
name = "Reset Resolution"
|
||||
rm_item = [(i, item)
|
||||
for i, item in enumerate(menu.items())
|
||||
if name in item.name()][0]
|
||||
menu.removeItem(rm_item[1].name())
|
||||
menu.addCommand(rm_item[1].name(), lib.reset_resolution, index=rm_item[0])
|
||||
|
||||
# add colorspace menu item
|
||||
menu.addCommand("Set colorspace...", lib.set_colorspace, index=rm_item[0]+1)
|
||||
|
|
|
|||
|
|
@ -1,98 +0,0 @@
|
|||
from .lib import *
|
||||
|
||||
|
||||
def load_capture_preset(path):
|
||||
import capture_gui
|
||||
import capture
|
||||
|
||||
path = path
|
||||
preset = capture_gui.lib.load_json(path)
|
||||
print preset
|
||||
|
||||
options = dict()
|
||||
|
||||
# CODEC
|
||||
id = 'Codec'
|
||||
for key in preset[id]:
|
||||
options[str(key)] = preset[id][key]
|
||||
|
||||
# GENERIC
|
||||
id = 'Generic'
|
||||
for key in preset[id]:
|
||||
if key.startswith('isolate'):
|
||||
pass
|
||||
# options['isolate'] = preset[id][key]
|
||||
else:
|
||||
options[str(key)] = preset[id][key]
|
||||
|
||||
# RESOLUTION
|
||||
id = 'Resolution'
|
||||
options['height'] = preset[id]['height']
|
||||
options['width'] = preset[id]['width']
|
||||
|
||||
# DISPLAY OPTIONS
|
||||
id = 'Display Options'
|
||||
disp_options = {}
|
||||
for key in preset['Display Options']:
|
||||
if key.startswith('background'):
|
||||
disp_options[key] = preset['Display Options'][key]
|
||||
else:
|
||||
disp_options['displayGradient'] = True
|
||||
|
||||
options['display_options'] = disp_options
|
||||
|
||||
# VIEWPORT OPTIONS
|
||||
temp_options = {}
|
||||
id = 'Renderer'
|
||||
for key in preset[id]:
|
||||
temp_options[str(key)] = preset[id][key]
|
||||
|
||||
temp_options2 = {}
|
||||
id = 'Viewport Options'
|
||||
light_options = {0: "default",
|
||||
1: 'all',
|
||||
2: 'selected',
|
||||
3: 'flat',
|
||||
4: 'nolights'}
|
||||
for key in preset[id]:
|
||||
if key == 'high_quality':
|
||||
temp_options2['multiSampleEnable'] = True
|
||||
temp_options2['multiSampleCount'] = 4
|
||||
temp_options2['textureMaxResolution'] = 512
|
||||
temp_options2['enableTextureMaxRes'] = True
|
||||
|
||||
if key == 'alphaCut':
|
||||
temp_options2['transparencyAlgorithm'] = 5
|
||||
temp_options2['transparencyQuality'] = 1
|
||||
|
||||
if key == 'headsUpDisplay':
|
||||
temp_options['headsUpDisplay'] = True
|
||||
|
||||
if key == 'displayLights':
|
||||
temp_options[str(key)] = light_options[preset[id][key]]
|
||||
else:
|
||||
temp_options[str(key)] = preset[id][key]
|
||||
|
||||
for key in ['override_viewport_options', 'high_quality', 'alphaCut']:
|
||||
temp_options.pop(key, None)
|
||||
|
||||
options['viewport_options'] = temp_options
|
||||
options['viewport2_options'] = temp_options2
|
||||
|
||||
# use active sound track
|
||||
scene = capture.parse_active_scene()
|
||||
options['sound'] = scene['sound']
|
||||
cam_options = dict()
|
||||
cam_options['overscan'] = 1.0
|
||||
cam_options['displayFieldChart'] = False
|
||||
cam_options['displayFilmGate'] = False
|
||||
cam_options['displayFilmOrigin'] = False
|
||||
cam_options['displayFilmPivot'] = False
|
||||
cam_options['displayGateMask'] = False
|
||||
cam_options['displayResolution'] = False
|
||||
cam_options['displaySafeAction'] = False
|
||||
cam_options['displaySafeTitle'] = False
|
||||
|
||||
# options['display_options'] = temp_options
|
||||
|
||||
return options
|
||||
|
|
@ -105,3 +105,99 @@ def filter_instances(context, plugin):
|
|||
instances = pyblish.api.instances_by_plugin(allInstances, plugin)
|
||||
|
||||
return instances
|
||||
|
||||
def load_capture_preset(path):
|
||||
import capture_gui
|
||||
import capture
|
||||
|
||||
path = path
|
||||
preset = capture_gui.lib.load_json(path)
|
||||
print preset
|
||||
|
||||
options = dict()
|
||||
|
||||
# CODEC
|
||||
id = 'Codec'
|
||||
for key in preset[id]:
|
||||
options[str(key)] = preset[id][key]
|
||||
|
||||
# GENERIC
|
||||
id = 'Generic'
|
||||
for key in preset[id]:
|
||||
if key.startswith('isolate'):
|
||||
pass
|
||||
# options['isolate'] = preset[id][key]
|
||||
else:
|
||||
options[str(key)] = preset[id][key]
|
||||
|
||||
# RESOLUTION
|
||||
id = 'Resolution'
|
||||
options['height'] = preset[id]['height']
|
||||
options['width'] = preset[id]['width']
|
||||
|
||||
# DISPLAY OPTIONS
|
||||
id = 'Display Options'
|
||||
disp_options = {}
|
||||
for key in preset['Display Options']:
|
||||
if key.startswith('background'):
|
||||
disp_options[key] = preset['Display Options'][key]
|
||||
else:
|
||||
disp_options['displayGradient'] = True
|
||||
|
||||
options['display_options'] = disp_options
|
||||
|
||||
# VIEWPORT OPTIONS
|
||||
temp_options = {}
|
||||
id = 'Renderer'
|
||||
for key in preset[id]:
|
||||
temp_options[str(key)] = preset[id][key]
|
||||
|
||||
temp_options2 = {}
|
||||
id = 'Viewport Options'
|
||||
light_options = {0: "default",
|
||||
1: 'all',
|
||||
2: 'selected',
|
||||
3: 'flat',
|
||||
4: 'nolights'}
|
||||
for key in preset[id]:
|
||||
if key == 'high_quality':
|
||||
temp_options2['multiSampleEnable'] = True
|
||||
temp_options2['multiSampleCount'] = 4
|
||||
temp_options2['textureMaxResolution'] = 512
|
||||
temp_options2['enableTextureMaxRes'] = True
|
||||
|
||||
if key == 'alphaCut':
|
||||
temp_options2['transparencyAlgorithm'] = 5
|
||||
temp_options2['transparencyQuality'] = 1
|
||||
|
||||
if key == 'headsUpDisplay':
|
||||
temp_options['headsUpDisplay'] = True
|
||||
|
||||
if key == 'displayLights':
|
||||
temp_options[str(key)] = light_options[preset[id][key]]
|
||||
else:
|
||||
temp_options[str(key)] = preset[id][key]
|
||||
|
||||
for key in ['override_viewport_options', 'high_quality', 'alphaCut']:
|
||||
temp_options.pop(key, None)
|
||||
|
||||
options['viewport_options'] = temp_options
|
||||
options['viewport2_options'] = temp_options2
|
||||
|
||||
# use active sound track
|
||||
scene = capture.parse_active_scene()
|
||||
options['sound'] = scene['sound']
|
||||
cam_options = dict()
|
||||
cam_options['overscan'] = 1.0
|
||||
cam_options['displayFieldChart'] = False
|
||||
cam_options['displayFilmGate'] = False
|
||||
cam_options['displayFilmOrigin'] = False
|
||||
cam_options['displayFilmPivot'] = False
|
||||
cam_options['displayGateMask'] = False
|
||||
cam_options['displayResolution'] = False
|
||||
cam_options['displaySafeAction'] = False
|
||||
cam_options['displaySafeTitle'] = False
|
||||
|
||||
# options['display_options'] = temp_options
|
||||
|
||||
return options
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue