Welcome to ftrack-sftp-accessor’s documentation!

Installation

This accessor is designed to be used as a single install studio wide. This negates the need to have it installed in every instance of ftrack-connect used in your studio. You’ll first need a sftp server configured where you can successfully log in and make transfers.

The easiest way to install is within a virtual environment using your package manager of choice:

pip install ftrack-sftp-accessor

Alternatively, download this repo and install it’s dependencies.

Configuration

Configure a new location within ftrack with the name ‘studio.sftp’. This will be used as the location for sftp transfers.

Set all other ftrack environment variables for your ftrack instance.

Usage

Setting up the accessor for the sftp location is done like so:

session = ftrack_api.Session()
location = session.query('Location where name is "studio.sftp"').one()

hostname = os.getenv("FTRACK_SFTP_ACCESSOR_HOSTNAME", None)
port = os.getenv("FTRACK_SFTP_ACCESSOR_PORT", 22)

location.accessor = SFTPAccessor(
    hostname,
    port=port
)
location.structure = ftrack_api.structure.standard.StandardStructure()
location.priority = 30

Several environment variables are used to configure the plugin:

FTRACK_SFTP_ACCESSOR_HOSTNAME=<your_sftp_host>
FTRACK_SFTP_ACCESSOR_PORT=22

For an install managed entirely by local ssh keys, where you’re able to authenticate successfully without specifying a username/password combination each time, this will be enough for the plugin to make transfers.

Alternatively, you can specify a username and password like so:

FTRACK_SFTP_ACCESSOR_USERNAME=user
FTRACK_SFTP_ACCESSOR_PASSWORD=**********

The default behaviour is to upload directly to the sftp users home folder. You can specify an optional subfolder which the accessor will use instead:

FTRACK_SFTP_ACCESSOR_FOLDER=ftrack

And then in your plugin:

location = session.query('Location where name is "studio.sftp"').one()
hostname = os.getenv("FTRACK_SFTP_ACCESSOR_HOSTNAME", None)
port = os.getenv("FTRACK_SFTP_ACCESSOR_PORT", 22)
username = os.getenv("FTRACK_SFTP_ACCESSOR_USERNAME", None)
password = os.getenv("FTRACK_SFTP_ACCESSOR_PASSWORD", None)
folder = os.getenv("FTRACK_SFTP_ACCESSOR_FOLDER", None)

location.accessor = SFTPAccessor(
    hostname,
    username,
    port=port,
    password=password,
    folder=folder
)

Uploading Components

When running the accessor outside of ftrack-connect, you will need to ensure your local storage is also correctly configured within a script, as the connect location configured by the desktop client will not be available as an option:

ftrack_location = session.query('Location where name is "ftrack.connect"').one()
ftrack_location.structure = ftrack_api.structure.standard.StandardStructure()
ftrack_location.accessor = ftrack_api.accessor.disk.DiskAccessor(
    prefix="/path/to/ftrack_storage"
)

To upload a component to an AssetVersion, lookup the version and create a component at the new location:

version = session.get("AssetVersion", "87d07110-962b-11ea-8647-927195c3dfa3")
version.create_component(
    path="/path/to/file/upload.mp4", location=location
)

Downloading Components

To download a component, lookup the component required and transfer it from ftrack to your sftp location:

version = session.get("AssetVersion", "b1ddbf94-6807-4f9f-a404-02298df80bba")

for component in version["components"]:
    if component["name"] == "main":
        ftrack_location.add_component(component, sftp_location)

Transfer Component Action

Optionally, to ease transfer of components between locations via the ftrack interface, it is possible to use the transfer components action found here.

Install the transfer component action in your plugin folder and you’ll need to additionally install the action handler it depends upon:

pip install ftrack-action-handler

Once installed, ensure both plugins are on the FTRACK_EVENT_PLUGIN_PATH (or add them to your plugins folder) and the transfer components plugin should become available under ftracks action menu.

Examples

Creating a plugin using SFTPAccessor

# :coding: utf-8
# :copyright: Copyright (c) 2014-2022 ftrack

import ftrack_api
import ftrack_api.entity.location
import ftrack_api.accessor.disk
import ftrack_api.structure.id
import logging
import os
import sys
import functools

NAME = "ftrack-sftp-accessor"
VERSION = "0.1.0"

logger = logging.getLogger("{}".format(NAME.replace("-", "_")))


def configure_location(event):
    from ftrack_sftp_accessor.sftp import SFTPAccessor

    """Configure locations for session."""
    session = event["data"]["session"]

    # Find location(s) and customise instances.
    location = session.query('Location where name is "studio.sftp"').one()

    hostname = os.getenv("FTRACK_SFTP_ACCESSOR_HOSTNAME", None)
    port = os.getenv("FTRACK_SFTP_ACCESSOR_PORT", 22)
    username = os.getenv("FTRACK_SFTP_ACCESSOR_USERNAME", None)
    password = os.getenv("FTRACK_SFTP_ACCESSOR_PASSWORD", None)
    folder = os.getenv("FTRACK_SFTP_ACCESSOR_FOLDER", None)

    # Setup accessor to use bucket
    location.accessor = SFTPAccessor(
        hostname, username, port=port, password=password, folder=folder
    )
    location.structure = ftrack_api.structure.standard.StandardStructure()
    location.priority = 30

    # Setup any other locations you require
    ftrack_location = session.query('Location where name is "ftrack.connect"').one()
    ftrack_location.structure = ftrack_api.structure.standard.StandardStructure()
    ftrack_location.accessor = ftrack_api.accessor.disk.DiskAccessor(
        prefix="/Users/ian/ftrack_storage"
    )


def register(session):
    if not isinstance(session, ftrack_api.session.Session):
        return

    logging.info("Registering sftp accessor")

    session.event_hub.subscribe(
        "topic=ftrack.api.session.configure-location and source.user.username={}".format(
            session.api_user
        ),
        configure_location,
    )

Starting FTrack using Plugin

# :coding: utf-8
# :copyright: Copyright (c) 2014-2022 ftrack

import ftrack_api.structure.id
import sys
import os
import logging

"""
Starts ftrack using a plugin which registers a location
"""
logging.basicConfig()
plugin_logger = logging.getLogger()
plugin_logger.setLevel(logging.INFO)

root_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir))

sys.path.append(os.path.join(root_dir, "ftrack_sftp_accessor"))

plugin_paths = [os.path.join(root_dir, "plugins")]

session = ftrack_api.Session(plugin_paths=plugin_paths, auto_connect_event_hub=True)
session.event_hub.wait()

Uploading Components

# :coding: utf-8
# :copyright: Copyright (c) 2014-2022 ftrack

import ftrack_api.structure.id
from ftrack_sftp_accessor.sftp import SFTPAccessor
import os


session = ftrack_api.Session()
location = session.query('Location where name is "studio.sftp"').one()

hostname = os.getenv("FTRACK_SFTP_ACCESSOR_HOSTNAME", None)
username = os.getenv("FTRACK_SFTP_ACCESSOR_USERNAME", None)
port = os.getenv("FTRACK_SFTP_ACCESSOR_PORT", 22)
folder = os.getenv("FTRACK_SFTP_ACCESSOR_FOLDER", "ftrack")

# Setup accessor
location.accessor = SFTPAccessor(hostname, username, port=port, folder=folder)
location.structure = ftrack_api.structure.standard.StandardStructure()
location.priority = 30

version = session.get("AssetVersion", "83b88aa2-b739-4ed1-b840-2ccdac7da271")
version.create_component(
    path="/Users/ian/Downloads/film0001_512kb.mp4", location=location
)

Downloading Components

# :coding: utf-8
# :copyright: Copyright (c) 2014-2022 ftrack

import ftrack_api.structure.id
import ftrack_api.accessor.disk
import os

from ftrack_sftp_accessor.sftp import SFTPAccessor

"""
Copies a component from a sftp location to a local location
"""
session = ftrack_api.Session()
sftp_location = session.ensure("Location", {"name": "studio.sftp"})

hostname = os.getenv("FTRACK_SFTP_ACCESSOR_HOSTNAME", None)
username = os.getenv("FTRACK_SFTP_ACCESSOR_USERNAME", None)
port = os.getenv("FTRACK_SFTP_ACCESSOR_PORT", 22)
folder = os.getenv("FTRACK_SFTP_ACCESSOR_FOLDER", "ftrack")

sftp_location.accessor = SFTPAccessor(hostname, username, port=port, folder=folder)
sftp_location.structure = ftrack_api.structure.standard.StandardStructure()
sftp_location.priority = 30

ftrack_location = session.ensure("Location", {"name": "ftrack.connect"})
ftrack_location.structure = ftrack_api.structure.standard.StandardStructure()
ftrack_location.accessor = ftrack_api.accessor.disk.DiskAccessor(
    prefix="/Users/ian/ftrack_storage"
)

version = session.get("AssetVersion", "9d5d86b7-46b5-49fc-993c-03ce885a6950")

for component in version["components"]:
    if component["name"] == "main":
        ftrack_location.add_component(component, sftp_location)

API reference

class ftrack_sftp_accessor.sftp.SFTPAccessor(host, username, port=22, password=None, folder=None)[source]

Provide SFTP location access.

__init__(host, username, port=22, password=None, folder=None)[source]

Initialise location accessor.

Uses the server credentials specified by host, password, port and password to create a sftp connection.

If specified, folder indicates the subfolder where assets are stored

exists(resource_identifier)[source]

Return if resource_identifier is valid and exists in location.

get_container(resource_identifier)[source]

Return resource_identifier of container for resource_identifier.

Raise AccessorParentResourceNotFoundError if container of resource_identifier could not be determined.

get_url(resource_identifier=None)[source]

Return url for resource_identifier.

is_container(resource_identifier)[source]

Return whether resource_identifier refers to a container.

is_file(resource_identifier)[source]

Return whether resource_identifier refers to a file.

is_sequence(resource_identifier)[source]

Return whether resource_identifier refers to a file sequence.

list(resource_identifier)[source]

Return list of entries in resource_identifier container.

Each entry in the returned list should be a valid resource identifier.

Raise AccessorResourceNotFoundError if resource_identifier does not exist or AccessorResourceInvalidError if resource_identifier is not a container.

make_container(resource_identifier, recursive=True)[source]

Make a container at resource_identifier.

If recursive is True, also make any intermediate containers.

open(resource_identifier, mode='rb')[source]

Return Data for resource_identifier.

remove(resource_identifier)[source]

Remove resource_identifier.

Raise AccessorResourceNotFoundError if resource_identifier does not exist.

property sftp

Return SFTP resource.

property ssh

Return SSH resource.

Release Notes

0.5.1

28 February 2023
  • changed

    Improve toml file with missing details for pypi release.

  • changed

    ftrack-action-handler is now optional.

0.5.0

16 February 2023
  • changed

    Improve toml file with missing information.

  • added

    Initial code implementation.

Indices and tables