Alican

Members
  • Content Count

    17
  • Joined

  • Last visited

About Alican

  • Rank
    Member

Profile Information

  • Gender
    Male
  • Location
    Vancouver
  • Interests
    All things Python

Recent Profile Visitors

322 profile views
  1. For those who might be having similar issue, after doing some research I've come to the conclusion that it might be my Graphics card. I've also switched to CineSync for reviewing which seems to not have this problem.
  2. Hello everyone! I really like the review capability of ftrack and would love to see further progression of it. After I upload a mov/mp4 and when I try to view it, there is visible tearing in the image. This is only visible in the web viewer/player/review. When I download the uploaded file, the problem is not there. My original files also dont have this issue either. I am uploading these files manually by drag and dropping them into the Versions tab of a task. It is visible when playing and when its paused. What could be causing this? Ive tried many different codecs but the problem exists in all codecs. The only one that didnt have this issue was a single frame render of a jpg image. ✘ HD1080 H264 mp4 ✘ HD1080 H264 mov ✘ HD1080 Avid DNxHD mov ✘ HD1080 Avid DNxHR mov ✘ HD1080 Avid DNxHR mp4 ✘ HD1080 Apple ProRes 4444 mov ✘ HD1080 Apple ProRes 422 mov ✔ HD1080 single frame Jpeg Thank you for all your help! Ali
  3. UPDATE: I just got it to work. When I set the path to my local path in my file system, it actually uploads it to the ftrack server, it doesn't actually source from my local file. This is great! Sooo... @Lorenzo your initial reply makes so much more sense now, thank you. Which brings me to an important final question: Does this mean that I have to: use the session.encode_media(file) to encode and generate my new file and it's thumbnail, and then download the new encoded file, and then link the new downloaded file on my local directory to the path of create_component()? Did I get this right? Is this the correct workflow? Here is my new updated code that works: SHOT_NAME = 'CHM201_033_050' TASK_TYPE = 'Compositing' session = ftrack_api.session.Session(auto_connect_event_hub=True) shot = session.query('select name, id from Shot where name is {}'.format(SHOT_NAME)).first() comp = session.query('select name, id from Task where parent.id is {} and type.name is {}'.format(shot['id'], TASK_TYPE)).first() task = session.get('Task', comp['id']) asset_parent = task['parent'] location = session.query('Location where name is "ftrack.server"').one() asset_type = session.query('AssetType where name is "Compositing"').one() asset = session.create('Asset', { 'name' : 'Compositing Asset v24', 'type': asset_type, 'parent': asset_parent }) asset_version = session.create('AssetVersion', { 'asset': asset, 'task': task }) component = asset_version.create_component( path='C:/Users/luxali/Downloads/main.mov', data={ 'name': 'ftrackreview-mp4' }, location=location ) # Meta data needs to contain *frameIn*, *frameOut* and *frameRate*. component['metadata']['ftr_meta'] = json.dumps({ 'frameIn': 0, 'frameOut': 67, 'frameRate': 25 }) thumbnail_component = session.create_component( 'C:/Users/luxali/Downloads/main.jpg', dict(name='thumbnail'), location=location ) asset_version['thumbnail'] = thumbnail_component component.session.commit() Thank you so much!
  4. I am trying to do the uploading and encoding of my .mov renders through the API. Just like how I would if I was drag & dropping it into the Version tab of a task. I am not really trying to download anything. The manual uploading of versions is very clean, it encodes it, creates a thumbnail, and i can review it anytime and anywhere i want. > ftrack_upload_000.jpg --- result = ftrack_upload_001.jpg > ftrack_upload_001.jpg --- desired result that i'm looking for I want to upload in the same fashion though the API. I have read through the following documents: http://ftrack-python-api.rtd.ftrack.com/en/stable/example/encode_media.html (This creates encoded file, and thumbnail that lives in the ftrack server) http://ftrack-python-api.rtd.ftrack.com/en/stable/example/publishing.html (But these seem to always source the local file though the path key, instead of my encoded files in the server) http://ftrack-python-api.rtd.ftrack.com/en/stable/example/web_review.html None of these examples seem to get me a result like manually Drag&Dropping a file and having the file live in the server (ftrack_upload_001.jpg). They all end up linking to my file in our local drive. (path=path/to/my/file.mov) (ftrack_upload_002.jpg) > ftrack_upload_002.jpg --- my result following the 'publishing' example And yes, when I set the 'path' of the create_component() to the file that lives in our system it works. But then that means it still lives in our local storage instead of the ftrack server, and that also means i have to create the thumbnails individually as well. Is there a way to use the linkgs generated from encode_media to be used as a path somehow? So that my action replicates the Drag&Drop process? ftrack_upload_000.jpg : Manual Drag&Dropping ftrack_upload_001.jpg : Desired result after Drag&Dropping ftrack_upload_002.jpg : My result
  5. Just to add on, In the end, I would like to have a system that works just like drag and dropping the mov file to the Versions area of the task.
  6. Hello all, I've been trying to upload an existing .mov file to ftrack for review, I've made quite a progress using the examples provided, however I'm stuck at the component part. At the moment, it submits an encoding job, waits for the job to be done, and gives me the URLs of the paths. If I set the path key on the create_component() to the MOV_FILE, it uploads. but if I set it to the url of the encoded item, it gives me the error below. What am I doing wrong? When I inspect the link from the line that says Resoruce not found, I can see that it does exist. and it downloads a file called main.mov, which is exactly what I want. This tells me that this resource indeed exists in the server. The following is a standalone script that gets the MOV_FILE and uploads it as an asset for a task(TASK_TYPE) that belongs to a shot (SHOT_NAME). import os, sys import subprocess import time import getpass import ftrack_api import json os.environ['FTRACK_SERVER'] = FTRACK_SERVER os.environ['FTRACK_API_USER'] = getpass.getuser() os.environ['FTRACK_API_KEY'] = FTRACK_API_KEY session = ftrack_api.session.Session(auto_connect_event_hub=True) SHOT_NAME = 'CHM201_033_050' TASK_TYPE = 'Compositing' MOV_FILE = 'Q:/Charmed_S2/CHM201/07_DAILIES/SLATED_OUTPUTS/offline/AVdn/201_033_050_cmp_v023.mov' shot = session.query('select name, id from Shot where name is {}'.format(SHOT_NAME)).first() comp = session.query('select name, id from Task where parent.id is {} and type.name is {}'.format(shot['id'], TASK_TYPE)).first() mov = MOV_FILE task = session.get('Task', comp['id']) asset_parent = task['parent'] job = session.encode_media(mov) job_data = json.loads(job['data']) print 'Source component id', job_data['source_component_id'] print 'Keeping original component', job_data['keep_original'] location = session.query('Location where name is "ftrack.server"').one() for output in job_data['output']: print u'Output component - id: {0}, format: {1}'.format( output['component_id'], output['format'] ) #Wait for the job to complete keep_waiting = True timer = 0 wait = 5 while keep_waiting: print 'waiting.. {} seconds'.format(timer) time.sleep(wait) timer+=5 listen_session = ftrack_api.session.Session(auto_connect_event_hub=True) the_job = listen_session.query('Job where id is{}'.format(job['id'])).first() if the_job['status'] == 'done': print 'proceeding...' keep_waiting = False location = session.query('Location where name is "ftrack.server"').one() #Print out the urls and test them to make sure they exist for component in job['job_components']: component_name = component['component']['name'] print component_name, location.get_url(component) asset_type = session.query('AssetType where name is "Compositing"').one() asset = session.create('Asset', { 'name' : 'Compositing Asset v7', 'type': asset_type, 'parent': asset_parent }) asset_version = session.create('AssetVersion', { 'asset': asset, 'task': task }) for component in job['job_components']: component_name = component['component']['name'] print component_name, location.get_url(component) if component_name == 'thumbnail': new_component = asset_version.create_component( path=location.get_url(component), data={ 'name': 'thumbnail' }, location=location ) if component_name == 'ftrackreview-mp4': new_component = asset_version.create_component( path=location.get_url(component), data={ 'name': 'ftrackreview-mp4' }, location=location ) session.commit() Thank you for all your help as always! Ali
  7. Our actions are all in a shared location mapped through the FTRACK_CONNECT_PLUGIN_PATH. I am not running any listeners at the moment, and when I do it runs locally from my machine and not the shared location. When this happens I usually don't have a listener running. When and if this issue comes up again, I will try removing all actions & events and see if that helps. Thank you!
  8. No box at all. We are all in the same project, however not all in the same context. When it breaks on me, it breaks everywhere regardless of project or selection context. And everyone can still access the actions on any context on any project. Everyone is logged with their own credentials. But it seems user related since I can log in from a different machine using my ftrack credentials, and it still wont show up. And when it fixes itself, I am able to access the actions menu from any computer. I was able to replicate my problem by having multiple (2) ftrack web pages open. I am logged in as the same user on both of them. It works on one of them (the first one I had open) and it doesn't work on the other. I just closed down my Chrome entirely, and also quit the Ftrack-Connect app. I restarted everything, and actions box still not appearing.
  9. Hello Everyone! Sometimes, (like right now) Actions menu will not show up when I click on Actions. Other users in my studio is able to use it just fine. I tried doing the same steps from a different computer, and it wont show up on those either. So far I've tried different browsers, clearing cache on Chrome (Which is the browser we all use 99% of the time), and relaunching the Ftrack-Connect on the desktop., and restarting my computer numerous times. Has anyone else had this issue in the past, and how was it resolved? Thank you in advance!!
  10. Thank you for your reply Jakub. My problem was much simpler than I thought. I can have unlimited forms as I want, and its results always just updates the event['data']['values'] with the latest form. So once form a is done I just store the event['data']['values'] into a temp var. and then call it once i need to use it to populate the next form. Thanks!
  11. Hello all! I am trying to create multiple forms that open up consecutively and parse the values from one form and use it in the next one. Here is the Ftrack Hiearchy structure EpisodicS2└-- Episode1 └-- Sequence 1 <--- ( A ), ( B ) └-- Shot 1 └-- Task 1 <--- ( B ) └-- Task 2 <--- ( B ) └-- Task..10 I have created 2 forms. Form A and Form B. If I run the action on an Sequence type item it should open a Dialog Form A, and once that is submitted it should gather the values from Form A and popup another dialog, Form B. If I run the action on some selected tasks, instead of a Sequence, I would like it to skip Form A, and just open up Form B. If I run this action on Sequence, data['values'] does not show me the values from Form B, it just tries to grab the information from Form A. How can I update data['values'] so that it gets the values from Form B? This only works correctly if I run it on a task or selected tasks. But it does not work when I run it on a Sequence. In the example below, I have a line that prints data['values']. Inside the main_ui(). So I guess the question is how do I clear the data values from the first Dialog box? class LuxCIS(object): '''Custom action.''' label = 'Create CIS' identifier = 'create.cis' description = 'Creates a sequence CIS from selected items' icon = 'https://static.thenounproject.com/png/2160687-200.png' def __init__(self, session): '''Initialise action.''' super(LuxCIS, self).__init__() self.session = session self.logger = logging.getLogger( __name__ + '.' + self.__class__.__name__ ) self.template_files = [] def register(self): try: ftrack.EVENT_HUB.subscribe( 'topic=ftrack.action.discover and source.user.username={0}'.format( getpass.getuser() ), self.discover ) ftrack.EVENT_HUB.subscribe( 'topic=ftrack.action.launch and source.user.username={0} ' 'and data.actionIdentifier={1}'.format( getpass.getuser(), self.identifier ), self.launch ) except: self.session.event_hub.subscribe( 'topic=ftrack.action.discover', self.discover ) self.session.event_hub.subscribe( 'topic=ftrack.action.launch and data.actionIdentifier={0}'.format( self.identifier), self.launch ) def discover(self, event): '''Return action config if triggered on a single asset version.''' data = event['data'] # If selection contains more than one item return early since # this action can only handle a single version. selection = data.get('selection', []) self.logger.info('Got selection: {0}'.format(selection)) #if len(selection) != 1 or selection[0]['entityType'] != 'assetversion': # return return { 'items': [{ 'label': self.label, 'description': self.description, 'actionIdentifier': self.identifier, 'icon': self.icon }] } def main_ui(self,event,items): #for i in self.items: print i, i['name'] #Get global ftrack settings for project and sequence from the first item #Get all information for each task and shot associated #populate a UI using the gathered information #master_dict = self.get_items(items) data = event['data'] ui = [{ 'value': '<hr>', 'type': 'label' },{ 'value': '<i><b>Hey poulate me! i found tasks!</b></i>', 'type': 'label' },{ 'label': 'My Enumerator', 'type': 'enumerator', 'name': 'my_enumerator', 'data': [ { 'label': 'Option 1', 'value': 'opt1' }, { 'label': 'Option 2', 'value': 'opt2' } ] },{ 'value': '<hr>', 'type': 'label' }] if 'values' in event['data']: # do something with values print data['values'] # This should now display the values from the second UI form. return { 'success':True, 'message':"Mission didn't fail unsuccesfully!"} return {'items':ui} def launch(self, event): '''Callback method for custom action.''' self.template_files = [] items=[] data = event['data'] selection = data.get('selection', []) session = ftrack_api.Session() template_files_dir = "L:/HAL/LUX_SLATE/nuke_templates" if os.path.isdir(template_files_dir): self.template_files = [i for i in os.listdir(template_files_dir) if i[-3:] == '.nk'] #gather shot items from selection for entity in selection: item = session.query('select id from TypedContext where id is {0}'.format(entity['entityId'])).first() items.append(item) #if just 1 item is selected if len(items) == 1: #If the single selected item is a sequence, get all the tasks under the sequence if items[0]['object_type']['name'] == 'Sequence': get_task_types = session.query('select name from Task where ancestors any(id="{0}")'.format(entity['entityId'])).all() task_types = [] for t in get_task_types: temp_t = {'label':t['name'], 'value':t['name']} if temp_t not in task_types: task_types.append(temp_t) cis_sequence_settings = [{ 'value': '<hr>', 'type': 'label' },{ 'value': '<i><b>What type of tasks should I create the CIS from?</b></i>', 'type': 'label' },{ 'label': 'Task Type', 'type': 'enumerator', 'value': task_types[0]['label'], 'name': 'cis_task_types', 'data': task_types },{ 'value': '<hr>', 'type': 'label' }] if 'values' in data: values = data['values'] task_type = values['cis_task_types'] items = session.query('select id from Task where name is "{1}" and ancestors any(id="{0}")'.format( entity['entityId'], task_type )).all() return self.main_ui(event, items) return {'items':cis_sequence_settings} #if the item is not a sequence, could be shot, task, etc.... or even project else: return self.main_ui(event, items) #if more than one item is selected else: return self.main_ui(event, items) def register(session, **kw): '''Register plugin.''' # Validate that session is an instance of ftrack_api.Session. If not, # assume that register is being called from an incompatible API # and return without doing anything. if not isinstance(session, ftrack_api.Session): # Exit to avoid registering this plugin again. return action = LuxCIS(session) action.register() if __name__ == '__main__': logging.basicConfig(level=logging.INFO) session = ftrack_api.Session() register(session) # Wait for events. session.event_hub.wait() Sorry for the convuluded question, let me know if there is anything I can clarify. Thank you very much in advance. Ali
  12. Just wondering if there are any updates on this issue. I would like to start upgrading our internal scripts to Python3 as we are only few months away from 2020. Downloaded the latest version of python (Python3.8), and installed ftrack-python-api using pip. And just like Reynaldos results, it installed fine but having difficulty importing ftrack_api. Results after import ftrack_api: Traceback (most recent call last): File "L:\HAL\Alican\Logger\import_psutil.py", line 3, in <module> import ftrack_api File "C:\Program Files\Python38\lib\site-packages\ftrack_api\__init__.py", line 5, in <module> from .session import Session File "C:\Program Files\Python38\lib\site-packages\ftrack_api\session.py", line 27, in <module> import ftrack_api.exception File "C:\Program Files\Python38\lib\site-packages\ftrack_api\exception.py", line 7, in <module> import ftrack_api.entity.base File "C:\Program Files\Python38\lib\site-packages\ftrack_api\entity\base.py", line 11, in <module> import ftrack_api.attribute File "C:\Program Files\Python38\lib\site-packages\ftrack_api\attribute.py", line 13, in <module> import ftrack_api.collection File "C:\Program Files\Python38\lib\site-packages\ftrack_api\collection.py", line 15, in <module> import ftrack_api.cache File "C:\Program Files\Python38\lib\site-packages\ftrack_api\cache.py", line 24, in <module> import anydbm ModuleNotFoundError: No module named 'anydbm' Thank you in advance! Ali
  13. Hello everybody! This action populates some custom attributes within a Task that is nested under selected item. These attributes are paths to working directories and render outputs.. I have other actions that rely on these custom paths to do other things such as opening a shot with quicktime / launching the shot folder in explorer /etc.. Everything works as it should.... but I am concerned about the efficiency and speed of my code as populating each task takes about a second... and of course, the more tasks nested under my selection, the longer this process takes. Ideally... I would like to run this action at Project or at least Sequence level and let it update the custom attributes for all tasks nested under it. EpisodicS2 └-- Episode1 └-- Seq001 └-- Shot1 └-- Task1 └-- Task2 └-- Task..10 Am I doing everything right? Are there major deficiencies in my code? Thank you for all the help! Ali import logging import ftrack_api import os import re import ftrack import getpass import time os.environ['FTRACK_SERVER'] = ### os.environ['FTRACK_API_KEY'] = ### #this script will populate parameters of each task and shot nested under selected item with direct paths to file locations. ### prerequisites: #Task should have the following attributes : path, out_path, base_path #Shot should have the following attributes : plate_path #Project should have the following attributes : Project_Path #<----naming will need to be conformed ### class UpdatePaths(object): '''Custom action.''' label = 'Update Paths' identifier = 'update.task.paths' description = 'This updates the path parameter of all tasks nested under selected' icon = 'https://d30y9cdsu7xlg0.cloudfront.net/png/1406969-200.png' def __init__(self, session): '''Initialise action.''' super(UpdatePaths, self).__init__() self.session = session self.logger = logging.getLogger( __name__ + '.' + self.__class__.__name__ ) def register(self): '''Register action.''' try: ftrack.EVENT_HUB.subscribe( 'topic=ftrack.action.discover and source.user.username={0}'.format( getpass.getuser() ), self.discover ) ftrack.EVENT_HUB.subscribe( 'topic=ftrack.action.launch and source.user.username={0} ' 'and data.actionIdentifier={1}'.format( getpass.getuser(), self.identifier ), self.launch ) except: self.session.event_hub.subscribe( 'topic=ftrack.action.discover', self.discover ) self.session.event_hub.subscribe( 'topic=ftrack.action.launch and data.actionIdentifier={0}'.format( self.identifier), self.launch ) def discover(self, event): '''Return action config if triggered on a single asset version.''' data = event['data'] # If selection contains more than one item return early since # this action can only handle a single version. selection = data.get('selection', []) self.logger.info('Got selection: {0}'.format(selection)) #if len(selection) != 1 or selection[0]['entityType'] != 'assetversion': # return return { 'items': [{ 'label': self.label, 'description': self.description, 'actionIdentifier': self.identifier, 'icon': self.icon }] } def launch(self, event): start_time = time.time() data = event['data'] selection = data.get('selection', []) entityTypes = ['Project', 'Episode', 'Sequence', 'Shot', 'Task'] session = ftrack_api.Session() for entity in selection: num = 0 et = entityTypes[num] item = session.query('select name from {0} where id is {1}'.format(et, entity['entityId'])).first() while item == None: num += 1 et = entityTypes[num] item = session.query('select name from {0} where id is {1}'.format(et, entity['entityId'])).first() prj_item_selected = False #If the current selected item is a project, first process it byitself. if item['parent'] == None: project = item prj_item_selected = True project_base = item['custom_attributes']['Project_Path']+item['full_name']+'/' if item['custom_attributes']['base_path']!= project_base: item['custom_attributes']['base_path'] = project_base #now look for the selected items descendants items = item['descendants'][:] #if the selected item is not a project, then we will also need to add taht to the list we will iterate through. if not prj_item_selected: items += [item] project = session.get('Project', item['project_id']) print 'Total nested items:',len(items) for i in items: link_list = i['link'][1:] episode = '' #search for an episode item in the links for l in link_list: link_item = session.get(l['type'], l['id']) if link_item['object_type']['name'] == 'Episode': episode = link_item['name'] project_path = project['custom_attributes']['Project_Path'] project_name = project['full_name'] base_path = project_path+project_name+'/'+episode+'/' base_path = base_path.replace('//','/') if i['custom_attributes']['base_path'] != base_path: i['custom_attributes']['base_path'] = base_path if i['object_type']['name'] == 'Task': shot_name = i['parent']['name'] #this may break when it comes to asset builds since they are not nested under a shot.. task_type = i['type']['name'] task_name = i['name'] path = base_path out_path = base_path if task_type in ['Compositing', 'Precomp', 'Cleanplate', 'Retime', 'Rotoscoping', 'Paintout']: comp_out_dir = '02_OUTPUT/03_comp' if task_type.lower() != task_name.lower(): comp_out_dir = '02_OUTPUT/01_precomp/{task_name}'.format(task_name=task_name) path = '{base_path}{dept_name}/{shot_name}/'.format(base_path=base_path,shot_name=shot_name,dept_name='05_COMP') out_path = '{base_path}{dept_name}/{shot_name}/{comp_out_dir}/'.format(base_path=base_path,shot_name=shot_name,dept_name='05_COMP',comp_out_dir=comp_out_dir) if task_type in ['Matchmove', 'Tracking']: path = '{base_path}{dept_name}/scenes/{shot_name}/tracking/'.format(base_path=base_path,shot_name=shot_name,dept_name='04_3D') out_path = '{base_path}{dept_name}/{shot_name}/TRAC/'.format(base_path=base_path,shot_name=shot_name,dept_name='06_RENDERS') if task_type in ['Animation']: path = '{base_path}{dept_name}/scenes/{shot_name}/anim/'.format(base_path=base_path,shot_name=shot_name,dept_name='04_3D') out_path = '{base_path}{dept_name}/{shot_name}/ANIM/'.format(base_path=base_path,shot_name=shot_name,dept_name='06_RENDERS') if task_type in ['Layout']: path = '{base_path}{dept_name}/scenes/{shot_name}/layout/'.format(base_path=base_path,shot_name=shot_name,dept_name='04_3D') out_path = '{base_path}{dept_name}/{shot_name}/LYT/'.format(base_path=base_path,shot_name=shot_name,dept_name='06_RENDERS') if task_type in ['Lighting']: path = '{base_path}{dept_name}/scenes/{shot_name}/lighting/'.format(base_path=base_path,shot_name=shot_name,dept_name='04_3D') out_path = '{base_path}{dept_name}/{shot_name}/FINL/'.format(base_path=base_path,shot_name=shot_name,dept_name='06_RENDERS') if task_type in ['FX']: path = '{base_path}{dept_name}/scenes/{shot_name}/fx/'.format(base_path=base_path,shot_name=shot_name,dept_name='04_3D') out_path = '{base_path}{dept_name}/{shot_name}/FX/'.format(base_path=base_path,shot_name=shot_name,dept_name='06_RENDERS') path = path.replace('//', '/') out_path = out_path.replace('//', '/') if i['custom_attributes']['path']!= path: #only make changes if they dont already exist i['custom_attributes']['path'] = path if i['custom_attributes']['out_path'] != out_path: #only make changes if they dont already exist i['custom_attributes']['out_path'] = out_path session.commit() print("--- %s seconds ---" % (time.time() - start_time)) return { 'success': True, 'message': 'updated task paths!' } def register(session, **kw): '''Register plugin.''' # Validate that session is an instance of ftrack_api.Session. If not, # assume that register is being called from an incompatible API # and return without doing anything. if not isinstance(session, ftrack_api.Session): # Exit to avoid registering this plugin again. return action = UpdatePaths(session) action.register() if __name__ == '__main__': logging.basicConfig(level=logging.INFO) session = ftrack_api.Session() register(session) # Wait for events. session.event_hub.wait()
  14. Works like a charm! Thank you for your prompt reply!
  15. I am working from the 'Example Action' found here. http://ftrack.rtd.ftrack.com/en/3.5.0/developing/actions.htm My end goal is to create directory structures based on Ftrack hiearchy. At the moment, all I am doing is printing some information (object type and name) based on what is selected. Everything works... but when I change the name of the selected task, or its type through Ftrack, and when I run the action again, it returns the information from my selection prior to the changes made. How can I force update the events inside of the launch() function so that it always operates on the latest ftrack information? Here is a very basic version of my code: import logging import ftrack_api import os #os.environ['FTRACK_SERVER'] = ############ #os.environ['FTRACK_API_USER'] = os.environ.get("USERNAME") #os.environ['FTRACK_API_KEY'] = ############ class MyCustomAction(object): '''Custom action.''' label = 'Create Folders' identifier = 'create.initial.folders' description = 'This creates the initial task folders' def __init__(self, session): '''Initialise action.''' super(MyCustomAction, self).__init__() self.session = session self.logger = logging.getLogger( __name__ + '.' + self.__class__.__name__ ) def register(self): '''Register action.''' self.session.event_hub.subscribe( 'topic=ftrack.action.discover and source.user.username={0}'.format( os.environ.get("USERNAME")#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, os.environ.get("USERNAME")#self.session.api_user ), self.launch ) def discover(self, event): '''Return action config if triggered on a single asset version.''' data = event['data'] # If selection contains more than one item return early since # this action can only handle a single version. selection = data.get('selection', []) self.logger.info('Got selection: {0}'.format(selection)) #if len(selection) != 1 or selection[0]['entityType'] != 'assetversion': # return return { 'items': [{ 'label': self.label, 'description': self.description, 'actionIdentifier': self.identifier }] } def launch(self, event): ########## How can I update this ^^ event so that launch() doesnt operate on an outdated information ########## ########## ########## '''Callback method for custom action.''' selection = event['data'].get('selection', []) #print selection for entity in selection: item = self.session.get('Task', entity['entityId']) object_type = item['object_type']['name'] name = item['name'] print name, object_type return { 'success': True, 'message': 'Ran my custom action successfully!' } def register(session, **kw): '''Register plugin.''' # Validate that session is an instance of ftrack_api.Session. If not, # assume that register is being called from an incompatible API # and return without doing anything. if not isinstance(session, ftrack_api.Session): # Exit to avoid registering this plugin again. return action = MyCustomAction(session) action.register() if __name__ == '__main__': logging.basicConfig(level=logging.INFO) session = ftrack_api.Session() register(session) # Wait for events. session.event_hub.wait() Thanks! Ali