This tutorial is targeted organisations that seek to integrate FilmHUB in their production tracking software using Python programming language, the steps followed are:
We assume you, "mike@acmefilm.se", have a main Mac server "alpha" @ headquarters with a network disk/share attached called "Projects" (MAC path: "/Volumes/projects", PC path: "P:").
Your task would be to have large package of film material being sent smoothly to a remote freelancer "jennifer@vfxhouse.com" when task is assigned in your production management system, for easy sync and pick up of work project files. You also want to implement a "button" that freelancer can press that sends back the result into the correct folders at headquarters, with a post publish step that updates the internal film project tracking tool.
Important note: The terms mentioned/configuration steps outlined below are described in detail in our FilmHUB Admin Manual, for instructions on how to use the FilmHUB desktop app - have a look at the FilmHUB User Manual. Always consult the manuals before your reach out to support@filmhubsoftware.com.
You now have a fully working setup replacing FTP and WeTransfer, with encryption and centralised queue management system. We are now going to continue and make file transfers submitted by Python scripts to enable an automised workflow.
This step is optional, you can run the Python API as your own user but we recommend you create a separate user for this purpose:
The FilmHUB Python API has the same role as the web admin interface - it only controls FilmHUB and cannot perform actual file transfers (A file transfer happens between your server and a client ). This means you can choose any machine that supports Python (v2 at the time of writing) for running your scripts. In this tutorial we have chosen the file server for this purpose, to keep things simple:
pip install filmhub-python-api". If you cannot use Pip, simply download and unzip [https://<your domain>.filmhub.cloud/installer/filmhub-python-api.zip] into your Python site-packages.FILMHUB_DOMAIN=acmefilmFILMHUB_API_USER=workflow@acmefilm.seFILMHUB_API_KEY=...Note: You can skip these environment variables and instead enter them when creating the API session later.
In your Python code, to use the FilmHUB API, prepare a session instance that will be used throughout rest of your scripts to access FilmHUB:
import filmhub_apisession = filmhub_api.Session()
Note: For detailed information about how to install, setup and use the Python API, refer to: https://support.filmhubsoftware.com/python-api
You are now all setup to start using the FilmHUB API within your Python scripts.
Within your organization, when a freelancer is assigned to a VFX task, a Python script is triggered that shares the shot folder and send a starter file package so they can start working.
Let's assume the following:
/Volumes/projects/film/seq/0010.source/A001C001_1203AC, should be shared read only.vfx/compositing/film_seq_0010_compositing_v001.nk, shared with both read & write permissions as freelancer should be able to upload the result here.reference/film_seq_0010_v001.mov, should be shared read only.If the freelancer have not been using FilmHUB before at domain "acmefilm", they have to be invited:
u = session.find_one("user WHERE code=jennifer@vfxhouse.com")if u is None: u = session.create("user",{"code":"jennifer@vfxhouse.com","clearance":"user"})Note: We assign 'user' clearance meaning that she will only have access to their default home share (@<default share>/filmhub/<E-mail>/ folder) and additional shares granted access to. Assign 'employee' access to grant read&write access to entire projects share.
A share in FilmHUB is similar to a FTP account on a FTP server - a directory on a root share/volume that one or more specific users have access to through ACLs. We put the share at root of film project folder, this way we can easily share further shots/assets later using the same workarea to the same freelancer or other freelancers.
Note: Even if share resides in the project root, it does NOT mean that user have access to entire project. Access is later granted by defining ACL:s on subfolders within the workarea.
The share "film-vfx" might already exist, so we attempt to first load it and then create if it does not exist:
share = session.find_one("workarea WHERE code=film-vfx")if share is None: share = session.create("workarea", {"code":"film-vfx","share":"projects","path":"film"})A dict containing share data is returned, we use the 'id' key when referencing the share from now on.
Next we need to give freelancer read access to the shot source & reference folders. For each check if an ACL exists and creates otherwise:
p_source="seq/0010/source"acl_source = session.find_one("acl WHERE (ident=user:{0} AND target=share:{1} AND path={2})".format(u['id'], share['id'], p_source))if acl_source is None: acl_source = session.create("acl",{"ident":"user:{0}".format(u['id']),"target":"share:{0}".format(share['id']),"path":p_source,"read":True,"write":False})p_ref="seq/0010/reference"acl_ref = session.find_one("acl WHERE (ident=user:{0} AND target=share:{1} AND path={2})".format(u['id'], share['id'], p_ref))if acl_ref is None: acl_ref = session.create("acl",{"ident":"user:{0}".format(u['id']),"target":"share:{0}".format(share['id']),"path":p_ref,"read":True,"write":False})Finally we give read & write access to "vfx/compositing" work folder:
p_work="seq/0010/vfx/compositing"acl_work = session.find_one("acl WHERE (ident=user:{0} AND target=workarea:{1} AND path={2})".format(u['id'], share['id'], p_work))if acl_work is None: acl_work = session.create("acl",{"ident":"user:{0}".format(u['id']),"target":"share:{0}".format(share['id']),"path":p_work,"read":True,"write":True})From now on, the freelancer can download additional source using FilmHUB desktop app or through web app, and also upload material shot folder.
In the next step we collect all files needed for freelancer to do their work, let's assume they all reside within the folders we shared above. That way we later can supply further material and freelancer can easily fetch this using the FilmHUB desktop app.
Compiling and sending file package:
job = session.create("job",{ "code":"film_seq_0010_compositing_v001", "tasks":{ "0":{ "source":"workarea=film-vfx:seq/0010/source/A001C001_1203AC", "dest":"jennifer@vfxhouse.com:film/seq/0010/source/A001C001_1203AC" }, "1":{ "source":"workarea=film-vfx:seq/0010/source/reference", "dest":"jennifer@vfxhouse.com:film/seq/0010/reference" }, "2":{ "source":"workarea=film-vfx:seq/0010/vfx/compositing", "dest":"jennifer@vfxhouse.com:film/seq/0010/vfx/compositing" }, }})Notice how also supply a relative path at receiver end, this way files gets delivered with the same folder structure and we can easily remap paths when project files arrives back.
The freelancer, Jennifer, will receive an E-mail that a package is pending and can download it using the FilmHUB desktop app. We assume she downloads it to the following local work path:
N:\nas\work\acmefilmResulting in the following files:
N:\nas\work\acmefilm\film\seq\0010\source\A001C001_1203ACN:\nas\work\acmefilm\film\seq\0010\referenceN:\nas\work\acmefilm\film\seq\0010\vfx\compositingThis optional configuration makes freelancer more integrated into workflow - destination paths are set and packages starts downloading directly on her end.
These are the steps to configure a local path for root share/volume "projects":
FILMHUB_PROJECTS_PATH=N:\nas\work\acmefilm".From now on, all packages coming from acmefilm will land in N:\nas\work\acmefilm with mirrored paths. If freelancer drag-n-drops a file from beneath this folder, it will be recognized and uploaded with mirrored paths - given it is a writeable destination of course.
We assume the freelancer has:
FILMHUB_DOMAIN, FILMHUB_API_USER and FILMHUB_API_KEY environment variables has been set, or is provided when creating the FilmHUB API session.Our task is to write a small Python script that sends back the work done to acmefilm, followed by a post process call at the server that publishes the Quicktime into a shot management database.
First, we detect and store all dependencies into an list, together with the path prefix of project directory. How that is done in Nuke is left out, but a pointer would be to loop over all Reads+Writes and filter out those residing in "vfx/compositing" subfolder only:
project_dir="N:\\nas\\work\\acmefilm"files=[ "seq\\0010\\vfx\\compositing\\sky_element_v002", # An asset "seq\\0010\\vfx\\compositing\\film_seq_0010_compositing_v004_jen.nk", # The project file "seq\\0010\\vfx\\compositing\\render\\film_seq_0010_compositing_v004", # The render "seq\\0010\\vfx\\compositing\\render\\film_seq_0010_compositing_v004.mov", # The proxy to publish]Note: if we were sending files back into other share folders, like for example seq/0010/source, job submit below will fail as she does not have write access to these folders.
Now we have all we need to send back the files:
import filmhub_apisession = filmhub_api.Session()tasks = {}n = 0for p in files: tasks[str(n)] = {"source":"%s\\%s"%(project_dir,p),"destination":"acmefilme:share=film-vfx:%s"%p n+=1job = session.create("job",{ "code":"film_seq_0010_compositing_v004", "tasks":tasks})The job will automatically start if Jennifer has FilmHUB desktop client running and enabled.
As a last step, and we are lazy, we want the proxy Quicktime of her work to be automatically published to a shot management system. To achieve this we first configure a hook in FilmHUB:
/Volumes/projects/pipeline/filmhub_job_done.py ${PATH_JSON_INPUT}The FilmHUB server (for default share, "projects" in this case) will now run this script with path to job JSON data when all files have arrived.
/Volumes/projects/pipeline/filmhub_job_done.py:
#!/usr/bin/pythonimport json,sys,oswith open(sys.argv[1],"r") as f: job_data = json.load(f) # Find the quicktime amongst files for p in [d["source"]["path_abs"] for d in job_data["tasks"].values()]: if p.lower().endswith(".mov"): publish(user=job_data["source_hr"].split(":")[1],comment="Freelancer publish",filename=p) # Some publish funcNote: How you write the "publish" function is depending on which project management system you have, we leave that to you.
These scriptlets are just rough guidelines, you can implement this in any way you want. Our goal with this tutorial has been to show how much more powerful an API scriptable file delivery system can be compared to an traditional FTP/Dropbox/WeTransfer solution.