mirror of
https://github.com/ynput/ayon-core.git
synced 2026-01-01 08:24:53 +01:00
"Component open" and "DJV launcher" actions are working. NOT PROPERLY!!!
This commit is contained in:
parent
490bf1c3ff
commit
94515dea68
5 changed files with 408 additions and 125 deletions
|
|
@ -24,8 +24,7 @@ class ComponentOpen(BaseAction):
|
|||
|
||||
def discover(self, session, entities, event):
|
||||
''' Validation '''
|
||||
|
||||
if len(entities) != 1 or entities[0].entity_type != 'Component':
|
||||
if len(entities) != 1 or entities[0].entity_type != 'FileComponent':
|
||||
return False
|
||||
|
||||
return True
|
||||
|
|
@ -43,10 +42,14 @@ class ComponentOpen(BaseAction):
|
|||
}
|
||||
|
||||
# Get component filepath
|
||||
# TODO with locations it will be different???
|
||||
fpath = entity['component_locations'][0]['resource_identifier']
|
||||
items = fpath.split(os.sep)
|
||||
items.pop(-1)
|
||||
fpath = os.sep.join(items)
|
||||
|
||||
if os.path.isfile(fpath):
|
||||
if sys.platform == 'win': # windows
|
||||
if os.path.isdir(fpath):
|
||||
if 'win' in sys.platform: # windows
|
||||
subprocess.Popen('explorer "%s"' % fpath)
|
||||
elif sys.platform == 'darwin': # macOS
|
||||
subprocess.Popen(['open', fpath])
|
||||
|
|
@ -63,7 +66,7 @@ class ComponentOpen(BaseAction):
|
|||
|
||||
return {
|
||||
'success': True,
|
||||
'message': 'Component Opened'
|
||||
'message': 'Component folder Opened'
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import sys
|
|||
import argparse
|
||||
import logging
|
||||
import os
|
||||
import json
|
||||
import ftrack_api
|
||||
from ftrack_action_handler import BaseAction
|
||||
|
||||
|
|
@ -161,9 +160,7 @@ class SyncToAvalon(BaseAction):
|
|||
job = session.create('Job', {
|
||||
'user': user,
|
||||
'status': 'running',
|
||||
'data': json.dumps({
|
||||
'description': 'Synch Ftrack to Avalon.'
|
||||
})
|
||||
'data': {'description': 'Synch Ftrack to Avalon.'}
|
||||
})
|
||||
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -33,7 +33,22 @@ class TestAction(BaseAction):
|
|||
def launch(self, session, entities, event):
|
||||
|
||||
for entity in entities:
|
||||
print("TEST")
|
||||
index = 0
|
||||
name = entity['components'][index]['name']
|
||||
filetype = entity['components'][index]['file_type']
|
||||
path = entity['components'][index]['component_locations'][0]['resource_identifier']
|
||||
|
||||
# entity['components'][index]['component_locations'][0]['resource_identifier'] = r"C:\Users\jakub.trllo\Desktop\test\exr\int_c022_lighting_v001_main_AO.%04d.exr"
|
||||
location = entity['components'][0]['component_locations'][0]['location']
|
||||
component = entity['components'][0]
|
||||
|
||||
|
||||
# print(location.get_filesystem_path(component))
|
||||
|
||||
# for k in p:
|
||||
# print(100*"-")
|
||||
# print(k)
|
||||
# print(p[k])
|
||||
|
||||
return True
|
||||
|
||||
|
|
|
|||
383
pype/ftrack/actions/djvview.py
Normal file
383
pype/ftrack/actions/djvview.py
Normal file
|
|
@ -0,0 +1,383 @@
|
|||
import logging
|
||||
import subprocess
|
||||
import sys
|
||||
import pprint
|
||||
import os
|
||||
import getpass
|
||||
import re
|
||||
from operator import itemgetter
|
||||
import ftrack_api
|
||||
|
||||
|
||||
class DJVViewAction(object):
|
||||
"""Launch DJVView action."""
|
||||
identifier = "djvview-launch-action"
|
||||
# label = "DJV View"
|
||||
# icon = "http://a.fsdn.com/allura/p/djv/icon"
|
||||
|
||||
def __init__(self, session):
|
||||
'''Expects a ftrack_api.Session instance'''
|
||||
|
||||
self.logger = logging.getLogger(
|
||||
'{0}.{1}'.format(__name__, self.__class__.__name__)
|
||||
)
|
||||
|
||||
if self.identifier is None:
|
||||
raise ValueError(
|
||||
'Action missing identifier.'
|
||||
)
|
||||
|
||||
self.session = session
|
||||
|
||||
def is_valid_selection(self, event):
|
||||
selection = event["data"].get("selection", [])
|
||||
|
||||
if not selection:
|
||||
return
|
||||
|
||||
entityType = selection[0]["entityType"]
|
||||
|
||||
if entityType not in ["assetversion", "task"]:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def discover(self, event):
|
||||
"""Return available actions based on *event*. """
|
||||
|
||||
if not self.is_valid_selection(event):
|
||||
return
|
||||
|
||||
items = []
|
||||
applications = self.get_applications()
|
||||
applications = sorted(
|
||||
applications, key=lambda application: application["label"]
|
||||
)
|
||||
|
||||
for application in applications:
|
||||
self.djv_path = application.get("path", None)
|
||||
applicationIdentifier = application["identifier"]
|
||||
label = application["label"]
|
||||
items.append({
|
||||
"actionIdentifier": self.identifier,
|
||||
"label": label,
|
||||
"variant": application.get("variant", None),
|
||||
"description": application.get("description", None),
|
||||
"icon": application.get("icon", "default"),
|
||||
"applicationIdentifier": applicationIdentifier
|
||||
})
|
||||
|
||||
return {
|
||||
"items": items
|
||||
}
|
||||
|
||||
def register(self):
|
||||
'''Registers the action, subscribing the the discover and launch topics.'''
|
||||
self.session.event_hub.subscribe(
|
||||
'topic=ftrack.action.discover and source.user.username={0}'.format(
|
||||
self.session.api_user
|
||||
), self.discover
|
||||
)
|
||||
|
||||
self.session.event_hub.subscribe(
|
||||
'topic=ftrack.action.launch and data.actionIdentifier={0} and source.user.username={1}'.format(
|
||||
self.identifier,
|
||||
self.session.api_user
|
||||
),
|
||||
self.launch
|
||||
)
|
||||
print("----- action - <" + self.__class__.__name__ + "> - Has been registered -----")
|
||||
|
||||
def get_applications(self):
|
||||
applications = []
|
||||
|
||||
label="DJVView {version}"
|
||||
versionExpression=re.compile(r"(?P<version>\d+.\d+.\d+)")
|
||||
applicationIdentifier="djvview"
|
||||
description="DJV View Launcher"
|
||||
icon="http://a.fsdn.com/allura/p/djv/icon"
|
||||
expression = []
|
||||
if sys.platform == "win32":
|
||||
expression = ["C:\\", "Program Files", "djv-\d.+",
|
||||
"bin", "djv_view.exe"]
|
||||
|
||||
elif sys.platform == "darwin":
|
||||
expression = ["Application", "DJV.app", "Contents", "MacOS", "DJV"]
|
||||
## Linuxs
|
||||
else:
|
||||
expression = ["usr", "local", "djv", "djv_view"]
|
||||
|
||||
pieces = expression[:]
|
||||
start = pieces.pop(0)
|
||||
|
||||
if sys.platform == 'win32':
|
||||
# On Windows C: means current directory so convert roots that look
|
||||
# like drive letters to the C:\ format.
|
||||
if start and start[-1] == ':':
|
||||
start += '\\'
|
||||
|
||||
if not os.path.exists(start):
|
||||
raise ValueError(
|
||||
'First part "{0}" of expression "{1}" must match exactly to an '
|
||||
'existing entry on the filesystem.'
|
||||
.format(start, expression)
|
||||
)
|
||||
|
||||
|
||||
expressions = list(map(re.compile, pieces))
|
||||
expressionsCount = len(expression)-1
|
||||
|
||||
for location, folders, files in os.walk(start, topdown=True, followlinks=True):
|
||||
level = location.rstrip(os.path.sep).count(os.path.sep)
|
||||
expression = expressions[level]
|
||||
|
||||
if level < (expressionsCount - 1):
|
||||
# If not yet at final piece then just prune directories.
|
||||
folders[:] = [folder for folder in folders
|
||||
if expression.match(folder)]
|
||||
else:
|
||||
# Match executable. Note that on OSX executable might equate to
|
||||
# a folder (.app).
|
||||
for entry in folders + files:
|
||||
match = expression.match(entry)
|
||||
if match:
|
||||
# Extract version from full matching path.
|
||||
path = os.path.join(start, location, entry)
|
||||
versionMatch = versionExpression.search(path)
|
||||
if versionMatch:
|
||||
version = versionMatch.group('version')
|
||||
|
||||
applications.append({
|
||||
'identifier': applicationIdentifier.format(
|
||||
version=version
|
||||
),
|
||||
'path': path,
|
||||
'version': version,
|
||||
'label': label.format(version=version),
|
||||
'icon': icon,
|
||||
# 'variant': variant.format(version=version),
|
||||
'description': description
|
||||
})
|
||||
else:
|
||||
self.logger.debug(
|
||||
'Discovered application executable, but it '
|
||||
'does not appear to o contain required version '
|
||||
'information: {0}'.format(path)
|
||||
)
|
||||
|
||||
# Don't descend any further as out of patterns to match.
|
||||
del folders[:]
|
||||
|
||||
return applications
|
||||
|
||||
def translate_event(self, session, event):
|
||||
'''Return *event* translated structure to be used with the API.'''
|
||||
|
||||
selection = event['data'].get('selection', [])
|
||||
|
||||
entities = list()
|
||||
for entity in selection:
|
||||
entities.append(
|
||||
(session.get(self.get_entity_type(entity), entity.get('entityId')))
|
||||
)
|
||||
|
||||
return entities
|
||||
|
||||
def get_entity_type(self, entity):
|
||||
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):
|
||||
"""Callback method for DJVView action."""
|
||||
session = self.session
|
||||
entities = self.translate_event(session, event)
|
||||
|
||||
# Launching application
|
||||
if "values" in event["data"]:
|
||||
|
||||
filename = event['data']['values']['path']
|
||||
|
||||
# TODO These should be obtained in another way
|
||||
start = 375
|
||||
end = 379
|
||||
fps = 24
|
||||
# TODO issequence is probably already built-in validation in ftrack
|
||||
isseq = re.findall( '%[0-9]*d', filename )
|
||||
if len(isseq) > 0:
|
||||
padding = re.findall( '%[0-9]*d', filename ).pop()
|
||||
range = ( padding % start ) + '-' + ( padding % end )
|
||||
filename = re.sub( '%[0-9]*d', range, filename )
|
||||
|
||||
|
||||
cmd = []
|
||||
# DJV path
|
||||
cmd.append( os.path.normpath( self.djv_path ) )
|
||||
### DJV Options Start ################################################
|
||||
# cmd.append( '-file_layer (value)' ) #layer name
|
||||
cmd.append( '-file_proxy 1/2' ) #Proxy scale: 1/2, 1/4, 1/8
|
||||
cmd.append( '-file_cache True' ) # Cache: True, False.
|
||||
# cmd.append( '-window_fullscreen' ) #Start in full screen
|
||||
# cmd.append("-window_toolbar False") # Toolbar 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_hud True") # Heads up display: True, False.
|
||||
cmd.append("-playback Forward") # Playback: Stop, Forward, Reverse.
|
||||
# cmd.append("-playback_frame (value)") # Frame.
|
||||
cmd.append("-playback_speed " + str(fps))
|
||||
# cmd.append("-playback_timer (value)") # Timer: Sleep, Timeout. Value: Sleep.
|
||||
# cmd.append("-playback_timer_resolution (value)") # Timer resolution (seconds): 0.001.
|
||||
cmd.append("-time_units Frames") # Time units: Timecode, Frames.
|
||||
### DJV Options End ##################################################
|
||||
|
||||
# PATH TO COMPONENT
|
||||
cmd.append( os.path.normpath( filename ) )
|
||||
|
||||
# Run DJV with these commands
|
||||
subprocess.Popen( ' '.join( cmd ) )
|
||||
|
||||
return {
|
||||
'success': True,
|
||||
'message': 'DJV View started.'
|
||||
}
|
||||
|
||||
if 'items' not in event["data"]:
|
||||
event["data"]['items'] = []
|
||||
|
||||
try:
|
||||
for entity in entities:
|
||||
versions = []
|
||||
allowed_types = ["img", "mov", "exr"]
|
||||
|
||||
if entity.entity_type.lower() == "assetversion":
|
||||
if entity['components'][0]['file_type'] in allowed_types:
|
||||
versions.append(entity)
|
||||
|
||||
if entity.entity_type.lower() == "task":
|
||||
# AssetVersions are obtainable only from shot!
|
||||
shotentity = entity['parent']
|
||||
|
||||
for asset in shotentity['assets']:
|
||||
for version in asset['versions']:
|
||||
# Get only AssetVersion of selected task
|
||||
if version['task']['id'] != entity['id']:
|
||||
continue
|
||||
# Get only components with allowed type
|
||||
if version['components'][0]['file_type'] in allowed_types:
|
||||
versions.append(version)
|
||||
|
||||
# Raise error if no components were found
|
||||
if len(versions) < 1:
|
||||
raise ValueError('There are no Asset Versions to open.')
|
||||
|
||||
for version in versions:
|
||||
for component in version['components']:
|
||||
label = "v{0} - {1} - {2}"
|
||||
|
||||
label = label.format(
|
||||
str(version['version']).zfill(3),
|
||||
version['asset']['type']['name'],
|
||||
component['name']
|
||||
)
|
||||
|
||||
try:
|
||||
# TODO This is proper way to get filepath!!!
|
||||
# NOT WORKING RIGHT NOW
|
||||
location = component['component_locations'][0]['location']
|
||||
file_path = location.get_filesystem_path(component)
|
||||
# if component.isSequence():
|
||||
# if component.getMembers():
|
||||
# frame = int(component.getMembers()[0].getName())
|
||||
# file_path = file_path % frame
|
||||
except:
|
||||
# This is NOT proper way
|
||||
file_path = component['component_locations'][0]['resource_identifier']
|
||||
|
||||
event["data"]["items"].append(
|
||||
{"label": label, "value": file_path}
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
return {
|
||||
'success': False,
|
||||
'message': str(e)
|
||||
}
|
||||
|
||||
return {
|
||||
"items": [
|
||||
{
|
||||
"label": "Items to view",
|
||||
"type": "enumerator",
|
||||
"name": "path",
|
||||
"data": sorted(
|
||||
event["data"]['items'],
|
||||
key=itemgetter("label"),
|
||||
reverse=True
|
||||
)
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
def register(session, **kw):
|
||||
"""Register hooks."""
|
||||
if not isinstance(session, ftrack_api.session.Session):
|
||||
return
|
||||
|
||||
action = DJVViewAction(session)
|
||||
action.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,115 +0,0 @@
|
|||
import os
|
||||
import logging
|
||||
import json
|
||||
|
||||
import ftrack
|
||||
import ftrack_api
|
||||
import clique
|
||||
import ftrack_template
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def modify_launch(session, event):
|
||||
"""Modify the application launch command with potential files to open"""
|
||||
|
||||
# Collect published paths
|
||||
data = {}
|
||||
for item in event["data"].get("selection", []):
|
||||
|
||||
versions = []
|
||||
|
||||
if entity.entity_type == "Assetversion":
|
||||
version = ftrack.AssetVersion(item["entityId"])
|
||||
if version.getAsset().getType().getShort() in ["img", "mov"]:
|
||||
versions.append(version)
|
||||
|
||||
# Add latest version of "img" and "mov" type from tasks.
|
||||
if item["entityType"] == "task":
|
||||
task = ftrack.Task(item["entityId"])
|
||||
for asset in task.getAssets(assetTypes=["img", "mov"]):
|
||||
versions.append(asset.getVersions()[-1])
|
||||
|
||||
for version in versions:
|
||||
for component in version.getComponents():
|
||||
component_list = data.get(component.getName(), [])
|
||||
component_list.append(component)
|
||||
data[component.getName()] = component_list
|
||||
|
||||
label = "v{0} - {1} - {2}"
|
||||
label = label.format(
|
||||
str(version.getVersion()).zfill(3),
|
||||
version.getAsset().getType().getName(),
|
||||
component.getName()
|
||||
)
|
||||
|
||||
file_path = component.getFilesystemPath()
|
||||
if component.isSequence():
|
||||
if component.getMembers():
|
||||
frame = int(component.getMembers()[0].getName())
|
||||
file_path = file_path % frame
|
||||
|
||||
event["data"]["items"].append(
|
||||
{"label": label, "value": file_path}
|
||||
)
|
||||
|
||||
# Collect workspace paths
|
||||
session = ftrack_api.Session()
|
||||
for item in event["data"].get("selection", []):
|
||||
if item["entityType"] == "task":
|
||||
templates = ftrack_template.discover_templates()
|
||||
task_area, template = ftrack_template.format(
|
||||
{}, templates, entity=session.get("Task", item["entityId"])
|
||||
)
|
||||
|
||||
# Traverse directory and collect collections from json files.
|
||||
instances = []
|
||||
for root, dirs, files in os.walk(task_area):
|
||||
for f in files:
|
||||
if f.endswith(".json"):
|
||||
with open(os.path.join(root, f)) as json_data:
|
||||
for data in json.load(json_data):
|
||||
instances.append(data)
|
||||
|
||||
check_values = []
|
||||
for data in instances:
|
||||
if "collection" in data:
|
||||
|
||||
# Check all files in the collection
|
||||
collection = clique.parse(data["collection"])
|
||||
for f in list(collection):
|
||||
if not os.path.exists(f):
|
||||
collection.remove(f)
|
||||
|
||||
if list(collection):
|
||||
value = list(collection)[0]
|
||||
|
||||
# Check if value already exists
|
||||
if value in check_values:
|
||||
continue
|
||||
else:
|
||||
check_values.append(value)
|
||||
|
||||
# Add workspace items
|
||||
event["data"]["items"].append(
|
||||
{
|
||||
"label": "{0} - {1}".format(
|
||||
data["name"],
|
||||
os.path.basename(collection.format())
|
||||
),
|
||||
"value": value
|
||||
}
|
||||
)
|
||||
|
||||
return event
|
||||
|
||||
|
||||
def register(session, **kw):
|
||||
# Validate session
|
||||
if not isinstance(session, ftrack_api.session.Session):
|
||||
return
|
||||
|
||||
session.event_hub.subscribe(
|
||||
'topic=djvview.launch',
|
||||
modify_launch(session)
|
||||
)
|
||||
Loading…
Add table
Add a link
Reference in a new issue