+# :copyright: Copyright (c) 2015 ftrack
+ '''Run *fn* asynchronously.'''
+ def wrapper(*args, **kwargs):
+ thread = threading.Thread(target=fn, args=args, kwargs=kwargs)
+class ImportThumbnails(ftrack.Action):
+ '''Batch import thumbnails to a project.'''
+ identifier = 'ftrack.batch.import.thumbnails'
+ label = 'Batch import thumbnails'
+ def validate_input(self, input_values):
+ '''Validate the input parameters.'''
+ folder_path = input_values['folder_path']
+ if not os.path.isdir(folder_path):
+ def upload_files_as_thumbnails(self, files):
+ '''Upload the files as thumbnails.'''
+ job = ftrack.createJob(
+ 'Creating thumbnails.', 'running'
+ for ftrack_path, file_path in files:
+ entity = ftrack.getFromPath(ftrack_path)
+ except ftrack.FTrackError:
+ print 'Could not find entity with path "{}"'.format(
+ entity.createThumbnail(file_path)
+ # Except anything and fail the job.
+ job.setStatus('failed')
+ job.setDescription('Creating thumbnails failed.')
+ job.setDescription('Creating thumbnails done.')
+ def get_files(self, path, project_name):
+ '''Return a list of tuples with ftrack path and filepaths.'''
+ for filename in os.listdir(path):
+ absolute_file_path = os.path.join(path, filename)
+ if os.path.isfile(absolute_file_path):
+ ftrack_path, _ = os.path.splitext(filename)
+ '{}.{}'.format(project_name, ftrack_path),
+ def discover(self, event):
+ '''Return action config if triggered on a single asset version.'''
+ # If selection contains more than one item return early since
+ # this action can only handle a single version.
+ selection = data.get('selection', [])
+ selection[0]['entityType'] != 'show'
+ 'actionIdentifier': self.identifier
+ def launch(self, event):
+ '''Callback method for action.'''
+ project_id = event['data']['selection'][0]['entityId']
+ project = ftrack.Project(project_id)
+ input_values = event['data']['values']
+ if not self.validate_input(input_values):
+This action will batch import thumbnails to selected project.
+Specify a *folder path* to a folder on your disk which
+contains images you want to use. The images should be named
+to match the entity path in ftrack.
+This will set the thumbnail for the *sequence*, *shot* and the
+ 'label': 'Folder path',
+ 'value': '/Users/carlclaesson/batch_thumbnails'
+ files = self.get_files(input_values['folder_path'], project.getName())
+ self.upload_files_as_thumbnails(files)
+ 'message': 'Action completed successfully'
+def main(arguments=None):
+ '''Set up logging and register action.'''
+ parser = argparse.ArgumentParser()
+ # Allow setting of logging level from arguments.
+ logging.NOTSET, logging.DEBUG, logging.INFO, logging.WARNING,
+ logging.ERROR, logging.CRITICAL
+ loggingLevels[logging.getLevelName(level).lower()] = level
+ help='Set the logging output verbosity.',
+ choices=loggingLevels.keys(),
+ namespace = parser.parse_args(arguments)
+ logging.basicConfig(level=loggingLevels[namespace.verbosity])
+ action = ImportThumbnails()
+ ftrack.EVENT_HUB.wait()
+if __name__ == '__main__':
+ raise SystemExit(main(sys.argv[1:]))