Publish to custom Locations
3 3

5 posts in this topic


I added a new location and structure plugin to ftrack, to be able to save assets in our own folder structure. It works fine if I publish assets from python, however the nuke plugin seems to ignore the new location.

I registered the location with the highest priority, but the assets still get published to the ftrack locations. I can't even see the location in the Import Asset dialog.


Are these locations hardcoded, or did I miss something?

Share this post

Link to post
Share on other sites

I'm trying to get this custom structure plugin working so I can publish to our own folder structure:

I'm using the example resolver plugin as well:

Right now, when I publish a component, it publishes, but the version path online is totally blank and I can't seem to find any sign of the file on my local system.

I have this working with the custom resolver and location plugin, but integrating the custom structure plugin seems to cause some issues.

I see that it's not production ready, but how would I go about hooking this up with my location and resolver plugins?

Share this post

Link to post
Share on other sites

Hi Mike,

I would recommend some debugging here to see where it goes wrong. It sounds like there is no proper resource identifier being generated from the structure plugin. A few prints in the structure plugin or in the surrounding code when using the location should point you in the right direction.

Share this post

Link to post
Share on other sites

Thanks, Mattias, just needed a push in the right direction!  

I was able to get it working, but I can't get proper logging from the resource plugin.  It only outputs certain log statements in the main class e.g. If I put something like'blah blah blah') in the getResourceIdentifier function, nothing outputs to the log (A better example in the structure_plugin below)

Here is my directory structure in case you need it:

+ ftrack-connect-plugins (The FTRACK_CONNECT_PLUGIN_PATH env points to this dir)
	+ mb_location
		+ hook
			+ location
		+ resource
			+ mb_structure
				- mb_structure_plugin

import os
import sys
import logging

import ftrack

def register(registry, **kw):
	'''Register plugin.'''

	logger = logging.getLogger('ftrack_plugin:studio_location')

	# Validate that registry is the correct ftrack.Registry. If not,
	# assume that register is being called with another purpose or from a
	# new or incompatible API and return without doing anything.
	if registry is not ftrack.LOCATION_PLUGINS:
		# Exit to avoid registering this plugin again.
			'Not subscribing plugin as passed argument {0!r} is not an '
			'ftrack.Registry instance.'.format(registry)

	# Import custom structure plugin.
	RESOURCE_DIRECTORY = os.path.abspath(
	os.path.join(os.path.dirname(__file__), '..', '..', 'resource')
	if RESOURCE_DIRECTORY not in sys.path:
		sys.path.append(RESOURCE_DIRECTORY)'Resource directory : %s' % RESOURCE_DIRECTORY)

	from mb_structure import mb_structure_plugin

	# Location and prefix variables.
	site = os.getenv('SITE').lower()
	studio = os.getenv('STUDIO').lower()
	LOCATION = '{0}.{1}'.format(studio, site)
	PREFIX = os.path.join('S:', os.sep, '__FTRACKTEST')

	ftrack.ensureLocation(LOCATION)'LOCATION: %s' % LOCATION)'PREFIX: %s' % PREFIX)

	# Create a location instance.
	location = ftrack.Location(


import logging
import ftrack

logger = logging.getLogger('ftrack_plugin:mb_structure')'Init structure module...')                                      # This outputs

class DumpingGroundStructure(ftrack.Structure):
	'''Dumping ground structure.
	Follows pattern:
	For example:
	.. warning::
		Not recommended for production use.
	''''Init structure...')                                          # This outputs

	def getResourceIdentifier(self, entity):
		'''Return a :term:`resource identifier` for supplied *entity*.
		.. note::
			Only supports :py:class:`~ftrack.Component` entities.
		''''Getting resource identifier...')                          # This doesn't output

		if not isinstance(entity, ftrack.Component):
			raise NotImplementedError('Cannot generate resource identifier for '
									  'unsupported entity {0}'.format(entity))
		component = entity

		# Can't generate identifier for a non-sequence container component.
		if (
			and component.getSystemType() != 'sequence'
			raise NotImplementedError('Cannot generate resource identifier for '
									  'container component {0}'.format(entity))

		container = component.getContainer()
		if container:
			# Use container for consistency across sibling members.
			hierarchy = container.getParents()
			hierarchy = component.getParents()

		# Construct structure.
		structure = []
		if self.prefix:
			structure.append(self.prefix)'Prefix : %s' % self.prefix)

		# Compute and add new filename if appropriate. Note that a sequence will
		# have a file name in the form prefix.%04d.ext.
		# E.g. test_sc010_010_spacestationLights_img_v001_main.%04d.exr
		fileNameStructure = self._getHierarchyStructure(hierarchy)

		if container:
			# Use container name for consistency across sibling members.

		fileName = '_'.join(fileNameStructure)

		if container:
			# Member of a container so add entity name as index.
			fileName += '.{0}'.format(entity.getName())

		elif component.getSystemType() in ('sequence',):
			# Add a sequence identifier.
			sequenceExpression = self._getSequenceExpression(component)
			fileName += '.{0}'.format(sequenceExpression)

		if fileName is not None:
			if container:
				# Use container extension for consistency across sibling
				fileType = container.getFileType()
				fileType = component.getFileType()

			if fileType:
				fileName += component.getFileType()


		return self.pathSeparator.join(structure)

	def _getHierarchyStructure(self, hierarchy):
		'''Return structure for *hierarchy*.

		Examine the *hierarchy* and return ordered list of names to use for

		Example result::

			['myproject', 'sc001', '010', 'render', 'img', 'v001']

		structure = []

		for ancestor in reversed(hierarchy):
				name = ancestor.getName()

			except AttributeError:
				if isinstance(ancestor, ftrack.AssetVersion):
					# Add padded version number for asset version.
					version = 'v{0:03d}'.format(ancestor.getVersion())


			if isinstance(ancestor, ftrack.Component):
				# Ignore intermediate components.

				name = name.lower().replace(' ', '_')

				if isinstance(ancestor, ftrack.Asset):
					# Add asset type short code.
					assetType = ancestor.getType().getShort()

		return structure



Share this post

Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
3 3