mirror of
https://github.com/ynput/ayon-core.git
synced 2026-01-01 16:34:53 +01:00
Removed unfinished actions (create folder, open folder, batch create)
This commit is contained in:
parent
94515dea68
commit
c38001016e
9 changed files with 46 additions and 754 deletions
|
|
@ -8,7 +8,6 @@ import ftrack_api
|
||||||
from ftrack_action_handler import BaseAction
|
from ftrack_action_handler import BaseAction
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ClientReviewSort(BaseAction):
|
class ClientReviewSort(BaseAction):
|
||||||
'''Custom action.'''
|
'''Custom action.'''
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ from ftrack_action_handler import BaseAction
|
||||||
from avalon import io, inventory, lib
|
from avalon import io, inventory, lib
|
||||||
from avalon.vendor import toml
|
from avalon.vendor import toml
|
||||||
|
|
||||||
|
|
||||||
class AvalonIdAttribute(BaseAction):
|
class AvalonIdAttribute(BaseAction):
|
||||||
'''Edit meta data action.'''
|
'''Edit meta data action.'''
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,186 +0,0 @@
|
||||||
import logging
|
|
||||||
import os
|
|
||||||
import getpass
|
|
||||||
import argparse
|
|
||||||
import errno
|
|
||||||
import sys
|
|
||||||
import threading
|
|
||||||
import ftrack_api
|
|
||||||
from ftrack_action_handler import BaseAction
|
|
||||||
|
|
||||||
PLUGIN_DIRECTORY = os.path.abspath(
|
|
||||||
os.path.join(os.path.dirname(__file__), '..'))
|
|
||||||
|
|
||||||
if PLUGIN_DIRECTORY not in sys.path:
|
|
||||||
sys.path.append(PLUGIN_DIRECTORY)
|
|
||||||
|
|
||||||
import ft_utils
|
|
||||||
|
|
||||||
|
|
||||||
def async(fn):
|
|
||||||
'''Run *fn* asynchronously.'''
|
|
||||||
def wrapper(*args, **kwargs):
|
|
||||||
thread = threading.Thread(target=fn, args=args, kwargs=kwargs)
|
|
||||||
thread.start()
|
|
||||||
return wrapper
|
|
||||||
|
|
||||||
|
|
||||||
class CreateFolders(BaseAction):
|
|
||||||
|
|
||||||
#: Action identifier.
|
|
||||||
identifier = 'create.folders'
|
|
||||||
#: Action label.
|
|
||||||
label = 'Create Folders'
|
|
||||||
#: Action Icon.
|
|
||||||
icon = 'https://cdn1.iconfinder.com/data/icons/rcons-folder-action/32/folder_add-512.png'
|
|
||||||
|
|
||||||
raise ValueError('Not working version of action')
|
|
||||||
@async
|
|
||||||
def createFoldersFromEntity(self, entity):
|
|
||||||
'''Generate folder structure from *entity*.
|
|
||||||
|
|
||||||
Entity is assumed to be either a project, episode, sequence or shot.
|
|
||||||
|
|
||||||
'''
|
|
||||||
|
|
||||||
root = entity.getProject().getRoot()
|
|
||||||
|
|
||||||
self.logger.info(root)
|
|
||||||
|
|
||||||
if entity.getObjectType() in (
|
|
||||||
'Episode', 'Sequence', 'Folder', 'Shot'):
|
|
||||||
objects = entity.getChildren(objectType='Shot', depth=None)
|
|
||||||
objects.append(entity)
|
|
||||||
else:
|
|
||||||
objects = entity.getChildren(depth=None)
|
|
||||||
|
|
||||||
for obj in objects:
|
|
||||||
|
|
||||||
tasks = obj.getTasks()
|
|
||||||
paths_collected = set([])
|
|
||||||
if obj.getObjectType() in (
|
|
||||||
'Episode', 'Sequence', 'Shot', 'Folder'):
|
|
||||||
task_mask = 'shot.task'
|
|
||||||
else:
|
|
||||||
task_mask = 'asset.task'
|
|
||||||
|
|
||||||
self.logger.info(task_mask)
|
|
||||||
|
|
||||||
for task in tasks:
|
|
||||||
self.logger.info(task)
|
|
||||||
paths = ft_utils.getAllPathsYaml(task)
|
|
||||||
self.logger.info(paths)
|
|
||||||
for path in paths:
|
|
||||||
if task_mask in path[1].name:
|
|
||||||
temppath = os.path.join(
|
|
||||||
root, path[0].lower().replace(" ", '_').replace('\'', ''))
|
|
||||||
paths_collected.add(temppath)
|
|
||||||
|
|
||||||
for path in paths_collected:
|
|
||||||
self.logger.info(path)
|
|
||||||
try:
|
|
||||||
os.makedirs(path)
|
|
||||||
except OSError as error:
|
|
||||||
if error.errno != errno.EEXIST:
|
|
||||||
raise
|
|
||||||
|
|
||||||
|
|
||||||
def discover(self, session, entities, event):
|
|
||||||
|
|
||||||
if len(entities) == 0 or entities[0].entity_type not in [
|
|
||||||
'Episode', 'Sequence', 'Shot', 'Folder', 'Asset Build']:
|
|
||||||
return False
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def launch(self, session, entities, event):
|
|
||||||
|
|
||||||
userId = event['source']['user']['id']
|
|
||||||
user = session.query('User where id is ' + userId).one()
|
|
||||||
|
|
||||||
job = session.create('Job', {
|
|
||||||
'user': user,
|
|
||||||
'status': 'running',
|
|
||||||
'data': json.dumps({
|
|
||||||
'description': 'Creating Folders'
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
'''Callback method for custom action.'''
|
|
||||||
|
|
||||||
try:
|
|
||||||
session.event_hub.publishReply(
|
|
||||||
event,
|
|
||||||
data={
|
|
||||||
'success': True,
|
|
||||||
'message': 'Folder Creation Job Started!'
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
for entity in entities:
|
|
||||||
self.createFoldersFromEntity(entity)
|
|
||||||
|
|
||||||
job.setStatus('done')
|
|
||||||
except:
|
|
||||||
job.setStatus('failed')
|
|
||||||
raise
|
|
||||||
|
|
||||||
|
|
||||||
return {
|
|
||||||
'success': True,
|
|
||||||
'message': 'Created Folders Successfully!'
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def register(session, **kw):
|
|
||||||
'''Register plugin. Called when used as an plugin.'''
|
|
||||||
|
|
||||||
# Validate that session is an instance of ftrack_api.Session. If not,
|
|
||||||
# assume that register is being called from an old or incompatible API and
|
|
||||||
# return without doing anything.
|
|
||||||
if not isinstance(session, ftrack_api.session.Session):
|
|
||||||
return
|
|
||||||
|
|
||||||
action_handler = CreateFolders(session)
|
|
||||||
action_handler.register()
|
|
||||||
|
|
||||||
|
|
||||||
def main(arguments=None):
|
|
||||||
'''Set up logging and register action.'''
|
|
||||||
if arguments is None:
|
|
||||||
arguments = []
|
|
||||||
|
|
||||||
parser = argparse.ArgumentParser()
|
|
||||||
# Allow setting of logging level from arguments.
|
|
||||||
loggingLevels = {}
|
|
||||||
for level in (
|
|
||||||
logging.NOTSET, logging.DEBUG, logging.INFO, logging.WARNING,
|
|
||||||
logging.ERROR, logging.CRITICAL
|
|
||||||
):
|
|
||||||
loggingLevels[logging.getLevelName(level).lower()] = level
|
|
||||||
|
|
||||||
parser.add_argument(
|
|
||||||
'-v', '--verbosity',
|
|
||||||
help='Set the logging output verbosity.',
|
|
||||||
choices=loggingLevels.keys(),
|
|
||||||
default='info'
|
|
||||||
)
|
|
||||||
namespace = parser.parse_args(arguments)
|
|
||||||
|
|
||||||
# Set up basic logging
|
|
||||||
logging.basicConfig(level=loggingLevels[namespace.verbosity])
|
|
||||||
|
|
||||||
session = ftrack_api.Session()
|
|
||||||
|
|
||||||
register(session)
|
|
||||||
|
|
||||||
# Wait for events
|
|
||||||
logging.info(
|
|
||||||
'Registered actions and listening for events. Use Ctrl-C to abort.'
|
|
||||||
)
|
|
||||||
session.event_hub.wait()
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
raise SystemExit(main(sys.argv[1:]))
|
|
||||||
|
|
@ -1,163 +0,0 @@
|
||||||
import sys
|
|
||||||
import argparse
|
|
||||||
import logging
|
|
||||||
import getpass
|
|
||||||
import subprocess
|
|
||||||
import os
|
|
||||||
|
|
||||||
import ftrack_api
|
|
||||||
from ftrack_action_handler import BaseAction
|
|
||||||
import ft_utils
|
|
||||||
|
|
||||||
class openFolder(BaseAction):
|
|
||||||
'''Open folders action'''
|
|
||||||
|
|
||||||
#: Action identifier.
|
|
||||||
identifier = 'open.folders'
|
|
||||||
#: Action label.
|
|
||||||
label = 'Open Folders'
|
|
||||||
#: Action Icon.
|
|
||||||
icon = "https://cdn3.iconfinder.com/data/icons/stroke/53/Open-Folder-256.png"
|
|
||||||
raise ValueError('Not working version of action')
|
|
||||||
|
|
||||||
def discover(self, session, entities, event):
|
|
||||||
''' Validation '''
|
|
||||||
|
|
||||||
if len(entities) == 0 or entities[0].entity_type in ['assetversion', 'Component']:
|
|
||||||
return False
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def get_paths(self, entity):
|
|
||||||
'''Prepare all the paths for the entity.
|
|
||||||
|
|
||||||
This function uses custom module to deal with paths.
|
|
||||||
You will need to replace it with your logic.
|
|
||||||
'''
|
|
||||||
|
|
||||||
root = entity['project']['root']
|
|
||||||
entity_type = entity.entity_type.lower()
|
|
||||||
|
|
||||||
if entity_type == 'task':
|
|
||||||
if entity['parent'].entity_type == 'Asset Build':
|
|
||||||
templates = ['asset.task']
|
|
||||||
else:
|
|
||||||
templates = ['shot.task']
|
|
||||||
|
|
||||||
elif entity_type in ['shot', 'folder', 'sequence', 'episode']:
|
|
||||||
templates = ['shot']
|
|
||||||
|
|
||||||
elif entity_type in ['asset build', 'library']:
|
|
||||||
templates = ['asset']
|
|
||||||
|
|
||||||
paths = ft_utils.getPathsYaml(entity,
|
|
||||||
templateList=templates,
|
|
||||||
root=root)
|
|
||||||
return paths
|
|
||||||
|
|
||||||
def launch(self, session, entities, event):
|
|
||||||
'''Callback method for action.'''
|
|
||||||
selection = event['data'].get('selection', [])
|
|
||||||
self.logger.info(u'Launching action with selection \
|
|
||||||
{0}'.format(selection))
|
|
||||||
|
|
||||||
# Prepare lists to keep track of failures and successes
|
|
||||||
fails = []
|
|
||||||
hits = set([])
|
|
||||||
|
|
||||||
for entity in entities:
|
|
||||||
|
|
||||||
# Get paths base on the entity.
|
|
||||||
# This function needs to be chagned to fit your path logic
|
|
||||||
paths = self.get_paths(entity)
|
|
||||||
|
|
||||||
# For each path, check if it exists on the disk and try opening it
|
|
||||||
for path in paths:
|
|
||||||
if os.path.isdir(path):
|
|
||||||
self.logger.info('Opening: ' + path)
|
|
||||||
|
|
||||||
# open the folder
|
|
||||||
if sys.platform == 'darwin':
|
|
||||||
subprocess.Popen(['open', '--', path])
|
|
||||||
elif sys.platform == 'linux2':
|
|
||||||
subprocess.Popen(['gnome-open', '--', path])
|
|
||||||
elif sys.platform == 'win32':
|
|
||||||
subprocess.Popen(['explorer', path])
|
|
||||||
|
|
||||||
# add path to list of hits
|
|
||||||
hits.add(entity['name'])
|
|
||||||
|
|
||||||
# Add entity to fails list if no folder could be openned for it
|
|
||||||
if entity['name'] not in hits:
|
|
||||||
fails.append(entity['name'])
|
|
||||||
|
|
||||||
# Inform user of the result
|
|
||||||
if len(hits) == 0:
|
|
||||||
return {
|
|
||||||
'success': False,
|
|
||||||
'message': 'No folders found for: {}'.format(', '.join(fails))
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(fails) > 0:
|
|
||||||
return {
|
|
||||||
'success': True,
|
|
||||||
'message': 'No folders found for: {}'.format(', '.join(fails))
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
'success': True,
|
|
||||||
'message': 'Opening folders'
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def register(session, **kw):
|
|
||||||
'''Register action. Called when used as an event plugin.'''
|
|
||||||
|
|
||||||
# Validate that session is an instance of ftrack_api.Session. If not,
|
|
||||||
# assume that register is being called from an old or incompatible API and
|
|
||||||
# return without doing anything.
|
|
||||||
if not isinstance(session, ftrack_api.session.Session):
|
|
||||||
return
|
|
||||||
|
|
||||||
action_handler = openFolder(session)
|
|
||||||
action_handler.register()
|
|
||||||
|
|
||||||
|
|
||||||
def main(arguments=None):
|
|
||||||
'''Set up logging and register action.'''
|
|
||||||
if arguments is None:
|
|
||||||
arguments = []
|
|
||||||
|
|
||||||
parser = argparse.ArgumentParser()
|
|
||||||
# Allow setting of logging level from arguments.
|
|
||||||
loggingLevels = {}
|
|
||||||
for level in (
|
|
||||||
logging.NOTSET, logging.DEBUG, logging.INFO, logging.WARNING,
|
|
||||||
logging.ERROR, logging.CRITICAL
|
|
||||||
):
|
|
||||||
loggingLevels[logging.getLevelName(level).lower()] = level
|
|
||||||
|
|
||||||
parser.add_argument(
|
|
||||||
'-v', '--verbosity',
|
|
||||||
help='Set the logging output verbosity.',
|
|
||||||
choices=loggingLevels.keys(),
|
|
||||||
default='info'
|
|
||||||
)
|
|
||||||
namespace = parser.parse_args(arguments)
|
|
||||||
|
|
||||||
# Set up basic logging
|
|
||||||
logging.basicConfig(level=loggingLevels[namespace.verbosity])
|
|
||||||
|
|
||||||
session = ftrack_api.Session()
|
|
||||||
register(session)
|
|
||||||
|
|
||||||
# Wait for events
|
|
||||||
logging.info(
|
|
||||||
'Registered actions and listening for events. Use Ctrl-C to abort.'
|
|
||||||
)
|
|
||||||
session.event_hub.wait()
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
raise SystemExit(main(sys.argv[1:]))
|
|
||||||
|
|
@ -1,349 +0,0 @@
|
||||||
# :coding: utf-8
|
|
||||||
# :copyright: Copyright (c) 2015 ftrack
|
|
||||||
import sys
|
|
||||||
import argparse
|
|
||||||
import logging
|
|
||||||
import collections
|
|
||||||
import threading
|
|
||||||
import getpass
|
|
||||||
import ftrack_api
|
|
||||||
from ftrack_action_handler import BaseAction
|
|
||||||
|
|
||||||
STRUCTURE_NAMES = ['episode', 'sequence', 'shot']
|
|
||||||
|
|
||||||
|
|
||||||
TASK_TYPE_ENUMERATOR_OPTIONS = [
|
|
||||||
{'label': task_type.getName(), 'value': task_type.getId()}
|
|
||||||
for task_type in ftrack.getTaskTypes()
|
|
||||||
]
|
|
||||||
|
|
||||||
TASK_TYPE_LOOKUP = dict(
|
|
||||||
(task_type.getId(), task_type.getName())
|
|
||||||
for task_type in ftrack.getTaskTypes()
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def async(fn):
|
|
||||||
'''Run *fn* asynchronously.'''
|
|
||||||
def wrapper(*args, **kwargs):
|
|
||||||
thread = threading.Thread(target=fn, args=args, kwargs=kwargs)
|
|
||||||
thread.start()
|
|
||||||
return wrapper
|
|
||||||
|
|
||||||
|
|
||||||
def get_names(base_name, padding, start, end, incremental):
|
|
||||||
'''Return names from expression.'''
|
|
||||||
names = []
|
|
||||||
for part in range(start, end + incremental, incremental):
|
|
||||||
names.append(
|
|
||||||
base_name + str(part).zfill(padding)
|
|
||||||
)
|
|
||||||
return names
|
|
||||||
|
|
||||||
|
|
||||||
def generate_structure(values):
|
|
||||||
'''Return structure from *values*.'''
|
|
||||||
structure = []
|
|
||||||
|
|
||||||
for structure_name in STRUCTURE_NAMES:
|
|
||||||
if (structure_name + '_expression') not in values:
|
|
||||||
continue
|
|
||||||
|
|
||||||
object_expression = values[structure_name + '_expression']
|
|
||||||
object_incremental = values[structure_name + '_incremental']
|
|
||||||
|
|
||||||
padding = object_expression.count('#')
|
|
||||||
_range, incremental = object_incremental.split(':')
|
|
||||||
start, end = _range.split('-')
|
|
||||||
|
|
||||||
start = int(start)
|
|
||||||
end = int(end)
|
|
||||||
incremental = int(incremental)
|
|
||||||
|
|
||||||
base_name = object_expression.replace('#', '')
|
|
||||||
|
|
||||||
logging.info(
|
|
||||||
(
|
|
||||||
'Create from expression {expression} with {base_name}, '
|
|
||||||
'{padding} and {start}-{end}:{incremental}'
|
|
||||||
).format(
|
|
||||||
expression=object_expression,
|
|
||||||
base_name=base_name,
|
|
||||||
padding=padding,
|
|
||||||
start=start,
|
|
||||||
end=end,
|
|
||||||
incremental=incremental
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
names = get_names(
|
|
||||||
base_name=base_name,
|
|
||||||
padding=padding,
|
|
||||||
start=start,
|
|
||||||
end=end,
|
|
||||||
incremental=incremental
|
|
||||||
)
|
|
||||||
|
|
||||||
structure.append({
|
|
||||||
'object_type': structure_name,
|
|
||||||
'data': names
|
|
||||||
})
|
|
||||||
|
|
||||||
tasks = collections.defaultdict(dict)
|
|
||||||
for name, value in values.iteritems():
|
|
||||||
if name.startswith('task_'):
|
|
||||||
_, index, key = name.split('_')
|
|
||||||
if key == 'bid' and value:
|
|
||||||
value = float(value) * 3600
|
|
||||||
tasks[index][key] = value
|
|
||||||
|
|
||||||
task_data = []
|
|
||||||
structure.append({
|
|
||||||
'object_type': 'task',
|
|
||||||
'data': task_data
|
|
||||||
})
|
|
||||||
for task in tasks.values():
|
|
||||||
task_data.append(task)
|
|
||||||
|
|
||||||
return structure
|
|
||||||
|
|
||||||
|
|
||||||
@async
|
|
||||||
def create(parent, structure):
|
|
||||||
'''Create *structure* under *parent*.'''
|
|
||||||
return create_from_structure(parent, structure)
|
|
||||||
|
|
||||||
|
|
||||||
def create_from_structure(parent, structure):
|
|
||||||
'''Create *structure* under *parent*.'''
|
|
||||||
level = structure[0]
|
|
||||||
children = structure[1:]
|
|
||||||
object_type = level['object_type']
|
|
||||||
|
|
||||||
for data in level['data']:
|
|
||||||
|
|
||||||
if object_type == 'episode':
|
|
||||||
new_object = parent.createEpisode(data)
|
|
||||||
|
|
||||||
if object_type == 'sequence':
|
|
||||||
new_object = parent.createSequence(data)
|
|
||||||
|
|
||||||
if object_type == 'shot':
|
|
||||||
new_object = parent.createShot(data)
|
|
||||||
|
|
||||||
if object_type == 'task':
|
|
||||||
new_object = parent.createTask(
|
|
||||||
TASK_TYPE_LOOKUP[data['typeid']]
|
|
||||||
)
|
|
||||||
new_object.set(data)
|
|
||||||
|
|
||||||
logging.info(
|
|
||||||
'Created {new_object} on parent {parent}'.format(
|
|
||||||
parent=parent, new_object=new_object
|
|
||||||
)
|
|
||||||
)
|
|
||||||
if children:
|
|
||||||
create_from_structure(new_object, children)
|
|
||||||
|
|
||||||
|
|
||||||
def get_form(number_of_tasks, structure_type, prefix, padding_count):
|
|
||||||
'''Return form from *number_of_tasks* and *structure_type*.'''
|
|
||||||
mappings = {
|
|
||||||
'episode': ['episode', 'sequence', 'shot'],
|
|
||||||
'sequence': ['sequence', 'shot'],
|
|
||||||
'shot': ['shot']
|
|
||||||
}
|
|
||||||
|
|
||||||
items = []
|
|
||||||
|
|
||||||
for structure_name in mappings[structure_type]:
|
|
||||||
items.extend(
|
|
||||||
[
|
|
||||||
{
|
|
||||||
'value': '##{0}##'.format(structure_name.capitalize()),
|
|
||||||
'type': 'label'
|
|
||||||
}, {
|
|
||||||
'label': 'Expression',
|
|
||||||
'type': 'text',
|
|
||||||
'value': prefix + '#' * padding_count,
|
|
||||||
'name': '{0}_expression'.format(structure_name)
|
|
||||||
}, {
|
|
||||||
'label': 'Incremental',
|
|
||||||
'type': 'text',
|
|
||||||
'value': '10-20:10',
|
|
||||||
'name': '{0}_incremental'.format(structure_name)
|
|
||||||
}
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
for index in range(0, number_of_tasks):
|
|
||||||
items.extend(
|
|
||||||
[
|
|
||||||
{
|
|
||||||
'value': '##Template for Task{0}##'.format(index),
|
|
||||||
'type': 'label'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'label': 'Type',
|
|
||||||
'type': 'enumerator',
|
|
||||||
'name': 'task_{0}_typeid'.format(index),
|
|
||||||
'data': TASK_TYPE_ENUMERATOR_OPTIONS
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'label': 'Bid',
|
|
||||||
'type': 'number',
|
|
||||||
'name': 'task_{0}_bid'.format(index)
|
|
||||||
}
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
return {'items': items}
|
|
||||||
|
|
||||||
|
|
||||||
class BatchCreate(BaseAction):
|
|
||||||
'''Batch create objects in ftrack.'''
|
|
||||||
|
|
||||||
#: Action identifier.
|
|
||||||
identifier = 'batch_create'
|
|
||||||
#: Action label.
|
|
||||||
label = 'Batch create'
|
|
||||||
|
|
||||||
def discover(self, session, entities, event):
|
|
||||||
|
|
||||||
if (len(entities) != 1 or entities[0].entity_type.lower()
|
|
||||||
not in ['project', 'episode', 'sequence']):
|
|
||||||
return False
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
def interface(self, session, entities, event):
|
|
||||||
if 'values' not in event['data']:
|
|
||||||
data = [
|
|
||||||
{
|
|
||||||
'label': 'Episode, Sequence, Shot',
|
|
||||||
'value': 'episode'
|
|
||||||
}, {
|
|
||||||
'label': 'Sequence, Shot',
|
|
||||||
'value': 'sequence'
|
|
||||||
}, {
|
|
||||||
'label': 'Shot',
|
|
||||||
'value': 'shot'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
entity = None
|
|
||||||
data_value = 'episode'
|
|
||||||
entity_name = ''
|
|
||||||
try:
|
|
||||||
entity = ftrack.Project(selection[0]['entityId'])
|
|
||||||
entity_name = entity.getFullName()
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
try:
|
|
||||||
entity = ftrack.Task(selection[0]['entityId'])
|
|
||||||
object_type = entity.getObjectType()
|
|
||||||
entity_name = entity.getName()
|
|
||||||
|
|
||||||
if object_type == 'Episode':
|
|
||||||
del data[0]
|
|
||||||
data_value = 'sequence'
|
|
||||||
|
|
||||||
if object_type == 'Sequence':
|
|
||||||
del data[0]
|
|
||||||
del data[0]
|
|
||||||
data_value = 'shot'
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
'label': 'Select structure',
|
|
||||||
'type': 'enumerator',
|
|
||||||
'value': data_value,
|
|
||||||
'name': 'structure_type',
|
|
||||||
'data': data
|
|
||||||
}, {
|
|
||||||
'label': 'Padding count',
|
|
||||||
'type': 'number',
|
|
||||||
'name': 'padding_count',
|
|
||||||
'value': 4
|
|
||||||
}, {
|
|
||||||
'label': 'Number of tasks',
|
|
||||||
'type': 'number',
|
|
||||||
'name': 'number_of_tasks',
|
|
||||||
'value': 2
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
def launch(self, session, entities, event):
|
|
||||||
'''Callback method for action.'''
|
|
||||||
selection = event['data'].get('selection', [])
|
|
||||||
values = event['data'].get('values', {})
|
|
||||||
if values:
|
|
||||||
if 'number_of_tasks' in values:
|
|
||||||
form = get_form(
|
|
||||||
int(values['number_of_tasks']),
|
|
||||||
values['structure_type'],
|
|
||||||
entity_name + '_',
|
|
||||||
int(values['padding_count'])
|
|
||||||
)
|
|
||||||
return form
|
|
||||||
|
|
||||||
else:
|
|
||||||
structure = generate_structure(values)
|
|
||||||
logging.info('Creating structure "{0}"'.format(str(structure)))
|
|
||||||
create(entity, structure)
|
|
||||||
return {
|
|
||||||
'success': True,
|
|
||||||
'message': 'Action completed successfully'
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def register(session, **kw):
|
|
||||||
'''Register plugin. Called when used as an plugin.'''
|
|
||||||
|
|
||||||
# Validate that session is an instance of ftrack_api.Session. If not,
|
|
||||||
# assume that register is being called from an old or incompatible API and
|
|
||||||
# return without doing anything.
|
|
||||||
if not isinstance(session, ftrack_api.session.Session):
|
|
||||||
return
|
|
||||||
|
|
||||||
action_handler = BatchCreate(session)
|
|
||||||
action_handler.register()
|
|
||||||
|
|
||||||
|
|
||||||
def main(arguments=None):
|
|
||||||
'''Set up logging and register action.'''
|
|
||||||
if arguments is None:
|
|
||||||
arguments = []
|
|
||||||
|
|
||||||
parser = argparse.ArgumentParser()
|
|
||||||
# Allow setting of logging level from arguments.
|
|
||||||
loggingLevels = {}
|
|
||||||
for level in (
|
|
||||||
logging.NOTSET, logging.DEBUG, logging.INFO, logging.WARNING,
|
|
||||||
logging.ERROR, logging.CRITICAL
|
|
||||||
):
|
|
||||||
loggingLevels[logging.getLevelName(level).lower()] = level
|
|
||||||
|
|
||||||
parser.add_argument(
|
|
||||||
'-v', '--verbosity',
|
|
||||||
help='Set the logging output verbosity.',
|
|
||||||
choices=loggingLevels.keys(),
|
|
||||||
default='info'
|
|
||||||
)
|
|
||||||
namespace = parser.parse_args(arguments)
|
|
||||||
|
|
||||||
# Set up basic logging
|
|
||||||
logging.basicConfig(level=loggingLevels[namespace.verbosity])
|
|
||||||
|
|
||||||
session = ftrack_api.Session()
|
|
||||||
register(session)
|
|
||||||
|
|
||||||
# Wait for events
|
|
||||||
logging.info(
|
|
||||||
'Registered actions and listening for events. Use Ctrl-C to abort.'
|
|
||||||
)
|
|
||||||
session.event_hub.wait()
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
raise SystemExit(main(sys.argv[1:]))
|
|
||||||
|
|
@ -1,9 +1,7 @@
|
||||||
import logging
|
import logging
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import pprint
|
|
||||||
import os
|
import os
|
||||||
import getpass
|
|
||||||
import re
|
import re
|
||||||
from operator import itemgetter
|
from operator import itemgetter
|
||||||
import ftrack_api
|
import ftrack_api
|
||||||
|
|
@ -72,7 +70,7 @@ class DJVViewAction(object):
|
||||||
}
|
}
|
||||||
|
|
||||||
def register(self):
|
def register(self):
|
||||||
'''Registers the action, subscribing the the discover and launch topics.'''
|
'''Registers the action, subscribing the discover and launch topics.'''
|
||||||
self.session.event_hub.subscribe(
|
self.session.event_hub.subscribe(
|
||||||
'topic=ftrack.action.discover and source.user.username={0}'.format(
|
'topic=ftrack.action.discover and source.user.username={0}'.format(
|
||||||
self.session.api_user
|
self.session.api_user
|
||||||
|
|
@ -91,19 +89,19 @@ class DJVViewAction(object):
|
||||||
def get_applications(self):
|
def get_applications(self):
|
||||||
applications = []
|
applications = []
|
||||||
|
|
||||||
label="DJVView {version}"
|
label = "DJVView {version}"
|
||||||
versionExpression=re.compile(r"(?P<version>\d+.\d+.\d+)")
|
versionExpression = re.compile(r"(?P<version>\d+.\d+.\d+)")
|
||||||
applicationIdentifier="djvview"
|
applicationIdentifier = "djvview"
|
||||||
description="DJV View Launcher"
|
description = "DJV View Launcher"
|
||||||
icon="http://a.fsdn.com/allura/p/djv/icon"
|
icon = "http://a.fsdn.com/allura/p/djv/icon"
|
||||||
expression = []
|
expression = []
|
||||||
if sys.platform == "win32":
|
if sys.platform == "win32":
|
||||||
expression = ["C:\\", "Program Files", "djv-\d.+",
|
expression = ["C:\\", "Program Files", "djv-\d.+",
|
||||||
"bin", "djv_view.exe"]
|
"bin", "djv_view.exe"]
|
||||||
|
|
||||||
elif sys.platform == "darwin":
|
elif sys.platform == "darwin":
|
||||||
expression = ["Application", "DJV.app", "Contents", "MacOS", "DJV"]
|
expression = ["Application", "DJV.app", "Contents", "MacOS", "DJV"]
|
||||||
## Linuxs
|
# Linuxs
|
||||||
else:
|
else:
|
||||||
expression = ["usr", "local", "djv", "djv_view"]
|
expression = ["usr", "local", "djv", "djv_view"]
|
||||||
|
|
||||||
|
|
@ -218,38 +216,37 @@ class DJVViewAction(object):
|
||||||
end = 379
|
end = 379
|
||||||
fps = 24
|
fps = 24
|
||||||
# TODO issequence is probably already built-in validation in ftrack
|
# TODO issequence is probably already built-in validation in ftrack
|
||||||
isseq = re.findall( '%[0-9]*d', filename )
|
isseq = re.findall('%[0-9]*d', filename)
|
||||||
if len(isseq) > 0:
|
if len(isseq) > 0:
|
||||||
padding = re.findall( '%[0-9]*d', filename ).pop()
|
padding = re.findall('%[0-9]*d', filename).pop()
|
||||||
range = ( padding % start ) + '-' + ( padding % end )
|
range = (padding % start) + '-' + (padding % end)
|
||||||
filename = re.sub( '%[0-9]*d', range, filename )
|
filename = re.sub('%[0-9]*d', range, filename)
|
||||||
|
|
||||||
|
|
||||||
cmd = []
|
cmd = []
|
||||||
# DJV path
|
# DJV path
|
||||||
cmd.append( os.path.normpath( self.djv_path ) )
|
cmd.append(os.path.normpath(self.djv_path))
|
||||||
### DJV Options Start ################################################
|
# DJV Options Start ##############################################
|
||||||
# cmd.append( '-file_layer (value)' ) #layer name
|
# cmd.append('-file_layer (value)') #layer name
|
||||||
cmd.append( '-file_proxy 1/2' ) #Proxy scale: 1/2, 1/4, 1/8
|
cmd.append('-file_proxy 1/2') # Proxy scale: 1/2, 1/4, 1/8
|
||||||
cmd.append( '-file_cache True' ) # Cache: True, False.
|
cmd.append('-file_cache True') # Cache: True, False.
|
||||||
# cmd.append( '-window_fullscreen' ) #Start in full screen
|
# cmd.append('-window_fullscreen') #Start in full screen
|
||||||
# cmd.append("-window_toolbar False") # Toolbar controls: False, True.
|
# cmd.append("-window_toolbar False") # Toolbar controls: False, True.
|
||||||
# cmd.append("-window_playbar False") # Window controls: False, True.
|
# cmd.append("-window_playbar False") # Window controls: False, True.
|
||||||
# cmd.append("-view_grid None") # Grid overlay: None, 1x1, 10x10, 100x100.
|
# cmd.append("-view_grid None") # Grid overlay: None, 1x1, 10x10, 100x100.
|
||||||
# cmd.append("-view_hud True") # Heads up display: True, False.
|
# cmd.append("-view_hud True") # Heads up display: True, False.
|
||||||
cmd.append("-playback Forward") # Playback: Stop, Forward, Reverse.
|
cmd.append("-playback Forward") # Playback: Stop, Forward, Reverse.
|
||||||
# cmd.append("-playback_frame (value)") # Frame.
|
# cmd.append("-playback_frame (value)") # Frame.
|
||||||
cmd.append("-playback_speed " + str(fps))
|
cmd.append("-playback_speed " + str(fps))
|
||||||
# cmd.append("-playback_timer (value)") # Timer: Sleep, Timeout. Value: Sleep.
|
# cmd.append("-playback_timer (value)") # Timer: Sleep, Timeout. Value: Sleep.
|
||||||
# cmd.append("-playback_timer_resolution (value)") # Timer resolution (seconds): 0.001.
|
# cmd.append("-playback_timer_resolution (value)") # Timer resolution (seconds): 0.001.
|
||||||
cmd.append("-time_units Frames") # Time units: Timecode, Frames.
|
cmd.append("-time_units Frames") # Time units: Timecode, Frames.
|
||||||
### DJV Options End ##################################################
|
# DJV Options End ################################################
|
||||||
|
|
||||||
# PATH TO COMPONENT
|
# PATH TO COMPONENT
|
||||||
cmd.append( os.path.normpath( filename ) )
|
cmd.append(os.path.normpath(filename))
|
||||||
|
|
||||||
# Run DJV with these commands
|
# Run DJV with these commands
|
||||||
subprocess.Popen( ' '.join( cmd ) )
|
subprocess.Popen(' '.join(cmd))
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'success': True,
|
'success': True,
|
||||||
|
|
@ -297,7 +294,7 @@ class DJVViewAction(object):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# TODO This is proper way to get filepath!!!
|
# TODO This is proper way to get filepath!!!
|
||||||
# NOT WORKING RIGHT NOW
|
# THIS WON'T WORK RIGHT NOW
|
||||||
location = component['component_locations'][0]['location']
|
location = component['component_locations'][0]['location']
|
||||||
file_path = location.get_filesystem_path(component)
|
file_path = location.get_filesystem_path(component)
|
||||||
# if component.isSequence():
|
# if component.isSequence():
|
||||||
|
|
@ -305,7 +302,7 @@ class DJVViewAction(object):
|
||||||
# frame = int(component.getMembers()[0].getName())
|
# frame = int(component.getMembers()[0].getName())
|
||||||
# file_path = file_path % frame
|
# file_path = file_path % frame
|
||||||
except:
|
except:
|
||||||
# This is NOT proper way
|
# This works but is NOT proper way
|
||||||
file_path = component['component_locations'][0]['resource_identifier']
|
file_path = component['component_locations'][0]['resource_identifier']
|
||||||
|
|
||||||
event["data"]["items"].append(
|
event["data"]["items"].append(
|
||||||
|
|
@ -334,8 +331,6 @@ class DJVViewAction(object):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def register(session, **kw):
|
def register(session, **kw):
|
||||||
"""Register hooks."""
|
"""Register hooks."""
|
||||||
if not isinstance(session, ftrack_api.session.Session):
|
if not isinstance(session, ftrack_api.session.Session):
|
||||||
|
|
@ -344,6 +339,7 @@ def register(session, **kw):
|
||||||
action = DJVViewAction(session)
|
action = DJVViewAction(session)
|
||||||
action.register()
|
action.register()
|
||||||
|
|
||||||
|
|
||||||
def main(arguments=None):
|
def main(arguments=None):
|
||||||
'''Set up logging and register action.'''
|
'''Set up logging and register action.'''
|
||||||
if arguments is None:
|
if arguments is None:
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@ from app.api import (
|
||||||
t = Templates()
|
t = Templates()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class AppAction(object):
|
class AppAction(object):
|
||||||
'''Custom Action base class
|
'''Custom Action base class
|
||||||
|
|
||||||
|
|
@ -50,14 +49,13 @@ class AppAction(object):
|
||||||
self.icon = icon
|
self.icon = icon
|
||||||
self.description = description
|
self.description = description
|
||||||
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def session(self):
|
def session(self):
|
||||||
'''Return current session.'''
|
'''Return current session.'''
|
||||||
return self._session
|
return self._session
|
||||||
|
|
||||||
def register(self):
|
def register(self):
|
||||||
'''Registers the action, subscribing the the discover and launch topics.'''
|
'''Registers the action, subscribing the discover and launch topics.'''
|
||||||
self.session.event_hub.subscribe(
|
self.session.event_hub.subscribe(
|
||||||
'topic=ftrack.action.discover and source.user.username={0}'.format(
|
'topic=ftrack.action.discover and source.user.username={0}'.format(
|
||||||
self.session.api_user
|
self.session.api_user
|
||||||
|
|
@ -72,7 +70,6 @@ class AppAction(object):
|
||||||
self._launch
|
self._launch
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _discover(self, event):
|
def _discover(self, event):
|
||||||
args = self._translate_event(
|
args = self._translate_event(
|
||||||
self.session, event
|
self.session, event
|
||||||
|
|
@ -101,11 +98,10 @@ class AppAction(object):
|
||||||
|
|
||||||
*session* is a `ftrack_api.Session` instance
|
*session* is a `ftrack_api.Session` instance
|
||||||
|
|
||||||
|
*entities* is a list of tuples each containing the entity type and
|
||||||
*entities* is a list of tuples each containing the entity type and the entity id.
|
the entity id. If the entity is a hierarchical you will always get
|
||||||
If the entity is a hierarchical you will always get the entity
|
the entity type TypedContext, once retrieved through a get operation
|
||||||
type TypedContext, once retrieved through a get operation you
|
you will have the "real" entity type ie. example Shot, Sequence
|
||||||
will have the "real" entity type ie. example Shot, Sequence
|
|
||||||
or Asset Build.
|
or Asset Build.
|
||||||
|
|
||||||
*event* the unmodified original event
|
*event* the unmodified original event
|
||||||
|
|
@ -230,8 +226,8 @@ class AppAction(object):
|
||||||
entity = session.get(entity, id)
|
entity = session.get(entity, id)
|
||||||
|
|
||||||
silo = "Film"
|
silo = "Film"
|
||||||
if entity.entity_type=="AssetBuild":
|
if entity.entity_type == "AssetBuild":
|
||||||
silo= "Asset"
|
silo = "Asset"
|
||||||
|
|
||||||
# set environments for Avalon
|
# set environments for Avalon
|
||||||
os.environ["AVALON_PROJECT"] = entity['project']['full_name']
|
os.environ["AVALON_PROJECT"] = entity['project']['full_name']
|
||||||
|
|
@ -241,24 +237,22 @@ class AppAction(object):
|
||||||
os.environ["AVALON_APP"] = self.identifier
|
os.environ["AVALON_APP"] = self.identifier
|
||||||
os.environ["AVALON_APP_NAME"] = self.identifier + "_" + self.variant
|
os.environ["AVALON_APP_NAME"] = self.identifier + "_" + self.variant
|
||||||
|
|
||||||
|
|
||||||
anatomy = t.anatomy
|
anatomy = t.anatomy
|
||||||
io.install()
|
io.install()
|
||||||
hierarchy = io.find_one({"type":'asset', "name":entity['parent']['name']})['data']['parents']
|
hierarchy = io.find_one({"type": 'asset', "name": entity['parent']['name']})['data']['parents']
|
||||||
io.uninstall()
|
io.uninstall()
|
||||||
if hierarchy:
|
if hierarchy:
|
||||||
# hierarchy = os.path.sep.join(hierarchy)
|
# hierarchy = os.path.sep.join(hierarchy)
|
||||||
hierarchy = os.path.join(*hierarchy)
|
hierarchy = os.path.join(*hierarchy)
|
||||||
|
|
||||||
data = { "project": {"name": entity['project']['full_name'],
|
data = {"project": {"name": entity['project']['full_name'],
|
||||||
"code": entity['project']['name']},
|
"code": entity['project']['name']},
|
||||||
"task": entity['name'],
|
"task": entity['name'],
|
||||||
"asset": entity['parent']['name'],
|
"asset": entity['parent']['name'],
|
||||||
"hierarchy": hierarchy}
|
"hierarchy": hierarchy}
|
||||||
|
|
||||||
anatomy = anatomy.format(data)
|
anatomy = anatomy.format(data)
|
||||||
|
|
||||||
|
|
||||||
os.environ["AVALON_WORKDIR"] = os.path.join(anatomy.work.root, anatomy.work.folder)
|
os.environ["AVALON_WORKDIR"] = os.path.join(anatomy.work.root, anatomy.work.folder)
|
||||||
|
|
||||||
# TODO Add paths to avalon setup from tomls
|
# TODO Add paths to avalon setup from tomls
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,7 @@ import ftrack_utils
|
||||||
import ftrack_api
|
import ftrack_api
|
||||||
|
|
||||||
|
|
||||||
session = ftrack_api.Session(
|
session = ftrack_api.Session()
|
||||||
server_url="https://pype.ftrackapp.com",
|
|
||||||
api_key="4e01eda0-24b3-4451-8e01-70edc03286be",
|
|
||||||
api_user="jakub.trllo",
|
|
||||||
)
|
|
||||||
|
|
||||||
objTypes = set()
|
objTypes = set()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,15 @@
|
||||||
# fttrack help functions
|
# fttrack help functions
|
||||||
|
|
||||||
|
import ftrack_api
|
||||||
# import ftrack
|
|
||||||
import os
|
import os
|
||||||
from pprint import *
|
from pprint import *
|
||||||
|
|
||||||
|
|
||||||
|
def checkLogin():
|
||||||
|
# check Environments FTRACK_API_USER, FTRACK_API_KEY
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def checkRegex():
|
def checkRegex():
|
||||||
# _handle_result -> would be solution?
|
# _handle_result -> would be solution?
|
||||||
# """ TODO Check if name of entities match REGEX"""
|
# """ TODO Check if name of entities match REGEX"""
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue