transmogrifer.py is the primary command line interface for the Transmogrifier framework. This file, located at /usr/local/bin/transmogrifier.py, provides a means to interface with Final Cut Server assets and productions. It provides functionality for programatically reading and writing asset and production metadata, creating or deleting assets and productions, modifying production membership, or triggering archive and restore operations. Assets and productions can be referenced by their unique FCS ID, by their title, or in the case of assets, by their file system path. In addition to this, transmogrifier allows assets to be mass modified at the production or FCP project level.
Lastly, transmogrifier.py provides a module interface that allows for integration with various delivery services, such as YouTube, BrightCove, or ThePlatform.
All of this functionality significantly expands the types of automation that can be triggered in Final Cut Server.
The transmogrifier.py script has the following usage:
FCS transmogrifier
Version: 1.0b Build: 2011042001
Framework Version: 1.0b Build: 2011042001
Copyright (C) 2009-2011 Beau Hunter, 318 Inc.
Usage:
transmogrifier.py [options] [target]
transmogrifier.py [-f configfile] [-d supportdir] [-a action] [-t mediatitle]
Working with assets:
transmogrifier.py --setField="Keywords" --value="MyAsset" -t MyAsset
transmogrifier.py --setField="Keywords" --value="MyAsset" -i /fcsxmlinfile.xml
transmogrifier.py --appendField="Status" --value="Update!" -i /fcsxmlinfile.xml
transmogrifier.py --getAssetID --assetPath="/FCS/Media/myfile.mpg"
transmogrifier.py --getEntityPath --assetID=1
transmogrifier.py --getFilePath --assetID=1
transmogrifier.py --getArchiveFilePath --assetID=1
transmogrifier.py --getThumbnailPath --assetID=1
transmogrifier.py --getProxyPath --assetID=1
transmogrifier.py --getPosterFramePath --assetID=1
transmogrifier.py --archive --assetID=1
transmogrifier.py --restore --assetID=1
transmogrifier.py --analyze --assetID=1
Working with productions:
transmogrifier.py --setField="Owner" --value="Calvin" --assetsWithProductionID=1
transmogrifier.py --restore --productionID=1
transmogrifier.py --buildProduction --productionID=1
Working with devices:
transmogrifier.py --getDeviceName --deviceID=1
transmogrifier.py --getDeviceName --devicePath="/FCS/Media"
transmogrifier.py --getDevicePath --deviceName="Media"
transmogrifier.py --getDevicePath --deviceID=1
transmogrifier.py --getDeviceID --deviceName="Media"
transmogrifier.py --getDeviceID --devicePath="/FCS/Media"
Working with Modules:
transmogrifier.py --module=BrightCove -t MyAsset [-a action]
transmogrifier.py --module=BrightCove -a preflightCheck|upload -t MyAsset
transmogrifier.py -a createSupportFolders [-m BrightCove] [-d supportdir]
transmogrifier.py --module=BrightCove -a listFCSFields
Options:
-h, --help Displays this help message
-v, --version Display version number
-f configfilepath Use specified config file
-d supportdir Path to support folder
--debug Run in debug mode
--xmlout= Specify a FCS XML file to write out.
-m,--module=MODULE Delivery Target: 'BrightCove', 'YouTube',etc..
-a action Perform the requested action.
--fcsvr_client Utilise fcsvr_client for FCS I/O operations
This defaults to yes by default on certain operations
--nofcsvr_client Under no circumstances utilize fcsvr_client for
operations
--getField=FIELD Method to retrieve the value of FIELD
--getDBField=FIELD Method to retriev the value of FIELD using the
Final Cut Server database field name.
--setField=FIELD Method to specify FIELD to set with --value
--appendField=FIELD Append the specified field FIELD with --value
--value=value Data to append to set or import to --field
--withtimestamp Prepends a time stamp to STRING
--notimestamp Omits timestamp, if specified in config file
--getAssetID Outputs the assetID for specified target asset
--getAssetPath Outputs the asset's filesystem path
--getEntityPath Outputs the FCS address for specified target asset
--getEntityMetadataSet Outputs the metadata set associated with entity
--getProxyPath Outputs the asset's proxy path
--getEditProxyPath Outputs the asset's edit proxy path
--getThumbnailPath Outputs the asset's thumbnail path
--getPosterFramePath Outputs the asset's posterframe path
--getDeviceName Outputs the deviceName for specified target
--getDevicePath Outputs the device path for specified target
--getDeviceID Outputs the device id for the specified target
--getProductionTitle Outputs the production name of the specified target
--getProductionID Outputs the production id of the specified target
--addToProduction Adds the specified asset to the specified production
--archive Archive the specified target
--restore Restore the specified target
--filterMDSet=mdset Filters targets to only those with the provided
metadaset (experimental)
Targets:
-t title,--title= Title of the XML file to read in, useful when
using WriteXML response in FCS.
Utilizes paths set in transmogrifier.conf
--xmlin="/myfile.xml" Specify a FCS XML file to read in. Overwrites -t
--assetID=1 Asset with ID 1
--assetPath="/myfile" Asset residing at "/myfile"
--assetTitle="title" Asset with title "title"
--productionID=1 Production with ID 1
--productionTitle='title' Production with title 'title'
--productionTitleLike='title' Production with title matching substring 'title'
--assetsFromProductionID=1 All assets from the specified production.
--assetsFromProjectID= All assets linked from the provided FCP project file
--deviceID=1 Device with ID 1
--deviceName="Media" Device with name "Media"
Return Codes:
0 Clean Execution
1 Syntax Error
2 Syntax Error: parameter missing
3 One or more operations reported an error
4 Invalid target
5 Ambiguous/Conflicting Target
6 Invalid Action
7 fcsvr_client unavailable
8 Error reading from source (bad XML, fcsvr_client error)
9 Target(s) is(are) offline
99 Unknown Error
The following section discusses some common use-cases for transmogrifier.py.
Update the value for field Description for an asset with id: 10
Modify a string field
>>> transmogrifier.py --setField='Description' --value='My new description' --assetID=10
Modify a boolean field
>>> transmogrifier.py --setField='IsGood' --value=1 --assetID=10
Modify a date field:
>>> transmogrifier.py --setField='Simulation Date' --value="$(date -u +'%Y-%m-%d %H:%M:%SZ')" --assetID=10>>> transmogrifier.py --setField='Simulation Date' --value="$(date +'%Y-%m-%d %H:%M:%S')" --assetID=10>>> transmogrifier.py --setField='Simulation Date' --value="now()" --assetID=10
Retrieve the value for field Owner and the device name for an asset that resides at path /Users/Shared/FCSStore/Media/Library/myfile.mov
>>> transmogrifier.py --getField='Owner' --getDeviceName --assetPath='/Users/Shared/FCSStore/Media/Library/myfile.mov'
Owner: Beau Hunter
DEVICE_NAME: Media
Retrieve asset id, asset path, thumbnail path, posterframe path, and proxy path, value for field Owner and field Status for an asset with title Test Movie
>>> transmogrifier.py --getAssetID --getAssetPath --getThumbnailPath --getPosterFramePath \
--getProxyPath --getField=Owner --getField=Status --assetTitle="Test Movie"
ASSET_ID: 24
ASSET_PATH: /Users/Shared/FCSStore/Media/Library/Test Movie.mov
THUMBNAIL_PATH: /Users/Shared/FCSStore/Proxy/Proxies.bundle/00/00/0000000000000087/Test Movie.jpg
POSTERFRAME_PATH: /Users/Shared/FCSStore/Proxy/Proxies.bundle/00/00/0000000000000092/Test Movie.jpg
PROXY_PATH: /Users/Shared/FCSStore/Proxy/Proxies.bundle/00/00/0000000000000098/Test Movie.mov
Owner: Beau Hunter
Status: Ready for Review
Transmogrifier also has the capability to append a field, inserting a timestamp, which is pretty usefull for history-based fields. We can achieve this through the syntax below.
>>> transmogrifier.py --appendField='Review History' --value='My new comment' --withtimestamp --assetID=24
On the first run of the above command, the Review History field will have the following content:
2011-04-14 12:19: My new comment
On a subsequent run, the field value will be:
2011-04-14 12:19: My new comment
2011-04-14 12:20: My new comment
To take this further: say that we want to keep a record of all review comments, approvals, and rejections performed on an asset. In Final Cut Server, we would add a subscription to monitor the Status field for changes. This subscription would then trigger an external script response configured with the following:
Command: /usr/local/bin/transmogrifier.py
Parameters: '--appendField=Review History' "--value=[Current Reviewer] \
Set status to: [Status] \\nComments: [Reviewer's Comments]" \
--withtimestamp '--assetID=[Asset ID]'
When triggered, the asset’s Review History field will resemble the following:
2011-04-14 12:19: Beau Hunter Set status to: Rejected
Comments: Please fix the lower third.
2011-04-16 11:45: Beau Hunter Set status to: Approved
Comments: Looks better, approved.
The --appendField can also be used to increment integer fields:
>>> transmogrifier.py --getField='Test Int' --assetID=24
Test Int: 1000
>>> transmogrifier.py --appendField='Test Int' --value=30 --assetID=24
>>> transmogrifier.py --getField='Test Int' --assetID=24
Test Int: 1030
It is important to note that the vast majority of the above operations can be applied to a production target instead of an asset target:
>>> transmogrifier.py --getField='Title' --getField='Test Int' --productionID=10
Title: "My Great Production"
Test Int: 1
>>> transmogrifier.py --getProductionID --getField='Test Int' --productionWithTitle="My Great Production"
PRODUCTION_ID: 10
Test Int: 1
The transmogrifier.py script also provides a --debug flag, which will force the script to output verbosely. The --debug will also report a traceback in the event that an exception is thrown during operation: :;
>>> transmogrifier.py --getArchivePath --assetID=31
Error! Could not determine target from specified criteria for action:
getArchivePath! Error: "No asset with: id:'31' could be found!"
>>> transmogrifier.py --getArchivePath --assetID=31 --debug
DEBUG: Processing Action: getArchivePath
DEBUG: Building object for target:asset
An unknown error occurred: FCSEntityNotFoundError:"No asset with: id:'31' could be found!".
Traceback (most recent call last):
File "/usr/local/bin/transmogrifier.py", line 1595, in <module>
sys.exit(main())
File "/usr/local/bin/transmogrifier.py", line 922, in main
configParser=cfgParser)
File "/Users/hunterbj/Documents/Scripts/Final Cut Server/transmogrifier/fcsxml.py", line 1035, in __init__
self.initWithAssetID(assetID=id)
File "/Users/hunterbj/Documents/Scripts/Final Cut Server/transmogrifier/fcsxml.py", line 5257, in initWithAssetID
raise FCSEntityNotFoundError(entityType='asset',entityID=assetID)
fcsxml.FCSEntityNotFoundError: "No asset with: id:'31' could be found!"
As we mentioned earlier, it is possible to mass-modify assets based upon production or project membership. This can be leveraged to implement extremely powerful workflows that otherwise wouldn’t be possible using native Final Cut Server functionality.
For example, using Transmogrifier, it is extremely easy to archive all assets of a given production:
>>> transmogrifier.py --archive --assetsFromProductionID=10
Archiving Asset: /asset/40
Archiving Asset: /asset/43
Archiving Asset: /asset/46
And we can do the same thing on a specific project:
>>> transmogrifier.py --archive --assetsFromProjectID=24
Archiving Asset: /asset/2
Archiving Asset: /asset/14
We can also mass edit assets:
>>> transmogrifier.py --setField=Owner --value="Beau" --assetsFromProductionID=10
We can also filter any of the above commands using the --filterMDSet argument, which will force the specified actions to operate solely on assets matching the specific metadata set provided:
>>> transmogrifier.py --setField=Owner --value="Beau" --filterMDSet='asset_graphic' --assetsFromProductionID=10
This functionality opens the door to a lot of automation possibilities. Say that we want to be able to populate a field, Assigned Editor on a production, and have that same field value be populated to any FCP project files that reside in the production, without having to manually modify each record.
To accomplish this in Final Cut Server, we would add a production subscription to monitor the Assigned Editor field for changes. This subscription would then trigger an external script response configured with the following:
Command: /usr/local/bin/transmogrifier.py
Parameters: '--setField=Assigned Editor' '--value=[Assigned Editor]' \
'--filterMDSet=asset_project' '--assetsFromProductionID=[Production ID]'
This subscription+response configuration will ensure that production changes to Assigned Editor are automatically propagated to that production’s project files.
Another important production-based function is found in the --addToProduction flag. This function does what it says it does, and can be applied either to assets or productions using standard targets:
## Add asset ID 38 to production with title ``My Production``
>>> transmogrifier.py --addToProduction --assetID=38 --productionTitle="Mar-2011"
## Add production 10 to production 11
>>> transmogrifier.py --addToProduction --productionID=10 --productionID=11
## Add assets linked in FCP project file with asset id: 24 to production 11
>>> transmogrifier.py --addToProduction --assetsFromProjectID=24 --productionID=11
Once installed, Transmogrifier requires little configuration. Though there are a few tweaks that can be made to facilitate automation.
The first, and most important modification is a system-level modification to the /etc/sudoers file to enable escalation of fcsvr_client to root privileges without any kind of interactive authentication. The main reason that this is necessary is due to the need for fcsvr_client to run as root when performing modifications, coupled with the fact that all scripts triggered from FCS will be run under the permissions of the account which FCS is linked to (which will not be root).
To make this change to the sudoers file, the command visudo should be used. visudo is a program specifically written to modify the sudoers file, and will ensure that the file has appropriate syntax upon committing any changes. visudo can be called with no arguments, and will automatically open an editor for /etc/sudoers. This editor uses vim syntax.
To make the necessary modification, it is first important to know the runtime user configured for Final Cut Server, this can be found by running the following command(s):
$defaults read /Library/Preferences/com.apple.FinalCutServer.settings USER_ID
501
$id 501
uid=501(hunterbj) gid=20(staff) groups=20(staff),80(admin)...
In this case, the 501 user maps to the user account hunterbj. Thus we will add the line:
hunterbj ALL=NOPASSWD: /Library/Application\ Support/Final\ Cut\ Server/Final\ Cut\ Server.bundle/Contents/MacOS/fcsvr_client
To the sudoers file directly after the block:
# User privilege specification
root ALL=(ALL) ALL
%admin ALL=(ALL) ALL
Resulting in the following configuration:
# User privilege specification
root ALL=(ALL) ALL
%admin ALL=(ALL) ALL
hunterbj ALL=NOPASSWD: /Library/Application\ Support/Final\ Cut\ Server/Final\ Cut\ Server.bundle/Contents/MacOS/fcsvr_client
Save the file (type : followed by qw), and then verify that you can use sudo to call fcsvr_client without any sort of authentication:
## Change to the FCS binary directory
$cd /Library/Application\ Support/Final\ Cut\ Server/Final\ Cut\ Server.bundle/Contents/MacOS/
## Make sure no sudo credentials are cached
$sudo -k
## Attempt to run fcsvr_client
$sudo ./fcsvr_client
In the above example, fcsvr_client should output it’s syntax. If you are prompted for authentication in the last step, your sudoers modification was not successful. If you do not make this configuration, then many modify- based actions will not succeed, in such an event, fcsvr_client must be ran with root privileges by hand.
Outside of the sudoers modification, Transmogrifier will reference a configuration file located, by default, at /usr/local/etc/transmogrifier.conf. This configuration file has a number of entries that transmogrifier.py will reference:
[GLOBAL]
path=/FCSSupport
emailtonotify=beauh@mac.com
modules=BrightCove,YouTube
debug=True
printLogs=True
keepFiles=True
fcsXMLInDir=/FCSSupport/fcsvr_xmlin
[FCSVRClient]
defaultproductionmdset='pa_production_show'
defaultassetmdset='pa_asset_media'
defaultthumbnailmdset='pa_asset_thumbnail'
defaultDeviceName='Library'
useSudo=True
As shown above, there are a number parameters broken up into two specific setting sections which we will read in from this file.
One of the original functions of Transmogrifier was to facilitate XML workflows and asset delivery to external services, such as YouTube. This system was designed before any of the fcsvr_client functionality was developed, and as such, it relies on a complete XML based workflow using FCS ReadXML and WriteXML responses.
Upon first installing Transmogrifier for the purpose of working with these delivery modules, you will first want to configure the transmogrifier.conf file and specify which modules are configured for use. The following modules are currently supported by Transmogrifier:
Module Name | Module Object | Module File |
---|---|---|
BrightCove | BrightCoveObject | brightcove.py |
YouTube | YouTubeObject | youtube.py |
thePlatform | thePlatformObject | thePlatform.py |
ShakeBlender | ShakeBlenderObject | shakeblender.py |
To configure Transmogrifier for use with the BrightCove and YouTube modules, specify the following configuration:
[GLOBAL]
path=/FCSSupport
modules=BrightCove,YouTube
This configuration also specifies a support path of /FCSSupport. This folder will be used by our modules to store support files, subsequent ReadXML, WriteXML, and copy responses will then be created in Final Cut Server to utilize these directories.
The first step to utilize this directories is to have transmogrifier build out our folder structure. This can be accomplished by running the following command:
>>> transmogrifier.py -a createSupportFolders
Creating support folder: '/FCSSupport'
Creating fcsvr_xmlin directory.
Creating fcsvr_xmlout directory.
Creating folder structure for module: 'BrightCove'
BrightCove: Creating Directory: '/FCSSupport/BrightCove'
BrightCove: Creating Directory: '/FCSSupport/BrightCove/xmlin'
BrightCove: Creating Directory: '/FCSSupport/BrightCove/xmlout'
BrightCove: Creating Directory: '/FCSSupport/BrightCove/media'
BrightCove: Creating Directory: '/FCSSupport/BrightCove/media/thumbs'
BrightCove: Creating Directory: '/FCSSupport/BrightCove/media/stills'
Creating folder structure for module: 'YouTube'
YouTube: Creating Directory: '/FCSSupport/YouTube'
YouTube: Creating Directory: '/FCSSupport/YouTube/xmlin'
YouTube: Creating Directory: '/FCSSupport/YouTube/xmlout'
YouTube: Creating Directory: '/FCSSupport/YouTube/media'
YouTube: Creating Directory: '/FCSSupport/YouTube/upload'
YouTube: Creating Directory: '/FCSSupport/YouTube/inprogress'
Creating folder structure for module: 'thePlatform'
thePlatform: Creating Directory: '/FCSSupport/thePlatform'
thePlatform: Creating Directory: '/FCSSupport/thePlatform/media'
thePlatform: Creating Directory: '/FCSSupport/thePlatform/thumbnails'
thePlatform: Creating Directory: '/FCSSupport/thePlatform/xmlin'
thePlatform: Creating Directory: '/FCSSupport/thePlatform/xmlout'
As shown above, for each of the specified modules a number of folders are created. Each module has it’s own xmlin folder for XML files coming from Final Cut Server WriteXML responses; a WriteXML response should be created to populate it’s respective xmlin folder. Likewise, a Read XML watcher should be configured in Final Cut Server to read XML files from each Module’s xmlout folder. We will also want to create Final Cut Server copy responses to generate each module’s required media files.
The next step is to configure all of our appropriate fields in Final Cut Server. To do this, we first need to know which fields to create. This can be achieved by running the followig command:
>>> transmogrifier.py -a listFCSFields
Transmogrifier needs the following FCS Fields:
'(string) Status'
BrightCove module needs the following FCS Fields:
'(string) BrightCove Publish History',
'(bool) Publish to BrightCove',
'(bool) Published to BrightCove',
'(string) Publishing Approver'
YouTube module needs the following FCS Fields:
'(string) YouTube Publish History',
'(bool) Publish to YouTube',
'(bool) Published to YouTube',
'(string) YouTube GeneratedID',
'(string) Publishing Approver'
thePlatform module needs the following FCS Fields:
'(string) Status',
'(bool) Published to thePlatform',
'(bool) Publish to thePlatform',
'(string) thePlatform Publish History'
From here we will need to create the appropriate metadata fields and groups in Final Cut Server, as provided in the list above.
Lastly, we need to configure a Final Cut Server external script response with the following paramenter’s:
Command Path: /usr/local/bin/transmogrifier.py Command Parameter’s: -a upload -m BrightCove -t '[Title]'
You will want to create a separate external script response for each module. The end goal is that once your publishing action is triggered in FCS, the following steps should be triggered:
- Final Cut Server populates the module’s media files
- Final Cut Server populates an XML file in the module’s xmlin folder, named after the asset title.
- Final Cut Server triggers the external script response.
- Transmogrifier searches for the XML file and associated media assets
- Transmogrifier uploads all discovered media files, along with the XML file
- Transmogrifier reports back into FCS via the xmlout folder.
For more information on individual module configuration, consult each module’s documentation, linked in the Module Table earlier in this section.