The Media API allows you to manage media for Designer projects on your Disguise server. You are able to upload media files, remove them, and view what is available on your server.
It is important to note that these endpoints will allow you only to access certain directories, such as your Designer projects folder. These paths can be found at Paths.
In the following examples, you’ll notice the use of path symbols embedded in curly braces; for more info, visit Paths.
First let’s set up the info we’ll need to provide to the endpoint.
// Example IP address for Disguise server.
const DISGUISE_SERVER_IP = "10.0.0.1";
const PROVISIONED_MEDIA_FILE_NAME = "background.mov";
// Provisioning API endpoint to get our media on the server
const provisionEndpoint = new URL(
"/api/service/media/provision",
`http://${DISGUISE_SERVER_IP}`
);
const provisionBody = {
// Example path to some media on a network drive accessible to the server.
// Also used to identify this file for future use unless a specific uid is
// given in this same body.
"mediaPath": `\\\\our-network-share/media/${PROVISIONED_MEDIA_FILE_NAME}`,
// Where to provision the media to.
// The local path filenames do not need to match the original.
"localPath": [
`{project:my-show}/objects/VideoFile/${PROVISIONED_MEDIA_FILE_NAME}`,
`{project:my-show2}/objects/VideoFile/${PROVISIONED_MEDIA_FILE_NAME}`
]
};
# Example IP address for Disguise server.
DISGUISE_SERVER_IP = "10.0.0.1"
PROVISIONED_MEDIA_FILE_NAME = "background.mov"
BASE_URL = f"http://{DISGUISE_SERVER_IP}"
# Provisioning API endpoint
provision_endpoint = f"{BASE_URL}/api/service/media/provision"
provision_body = {
# Example path to some media on a network drive accessible to the server.
# Also used to identify this file for future use unless a specific uid is
# given in this same body.
"mediaPath": rf"\\our-network-share/{PROVISIONED_MEDIA_FILE_NAME}",
# Where to provision the media to.
# The local path filenames do not need to match the original.
"localPath": [
f"{project:my-show}/objects/VideoFile/{PROVISIONED_MEDIA_FILE_NAME}",
f"{project:my-show2}/objects/VideoFile/{PROVISIONED_MEDIA_FILE_NAME}",
],
}
If you need to provision multiple files, you should use the transfers field; an array populated with objects identical to the top level structure of the request body above. For example:
{
"transfers": [
{
"mediaPath": "\\our-network-share/media/file1.mov",
"localPath": "{project:my-show}/objects/VideoFile/file_one.mov"
},
{
"mediaPath": "\\our-network-share/media/file2.mov",
"localPath": "{project:my-show}/objects/VideoFile/file_two.mov"
}
]
}
Now let’s send the request and check the response.
const provisionResponse = await fetch(provisionEndpoint, {
method: 'POST',
body: JSON.stringify(provisionBody),
});
const provisionResponseData = await provisionResponse.json();
if (!provisionResponse.ok) {
throw new Error(`HTTP error ${provisionResponse.status}:
${provisionResponseData.status.message}`);
}
// We have one result, as we did just one provision.
const provisionResult = provisionResponseData.result[0]
if (!provisionResult.success) {
throw new Error(`Provisioning error: '${provisionResult.errorMessage.message}'`)
} import requests
provision_response = requests.post(provision_endpoint, json=provision_body)
provision_response_data = provision_response.json()
if not provision_response.ok:
raise RuntimeError(
f"HTTP error {provision_response.status_code}: "
f"{provision_response_data['status']['message']}"
)
# We have one result, as we did just one provision.
provision_result = provision_response_data["result"][0]
if not provision_result["success"]:
raise RuntimeError(
f"Provisioning error: "
f"'{provision_result['errorMessage']['message']}'"
)
Provided the request was successful, we will receive both a task UID and a websocket URL using which we can track the progress of the provisioning task.
import WebSocket from 'ws';
// The UID for the copy task, used to track its progress with the task
// status streaming API.
const provisionTaskUid = provisionResult["taskUid"];
// We receive the URL for the task status websocket API in the result
// of the provision request.
const taskSocket = new WebSocket(provisionResult.taskStatusStream);
const waitForProvision = new Promise((resolve, reject) => {
// Timeout for provision.
const timeout = setTimeout(() => {
reject(new Error("Timed out waiting for media provision."));
}, 30_000);
taskSocket.onopen = () => {
taskSocket.send(JSON.stringify({
op: "Set",
// Tell the API we only want to receive state reports for our provision task.
policies: [{ uid: provisionTaskUid, reports: ["State"]}]
}));
}
taskSocket.onmessage = (event) => {
// Array of info objects about running tasks.
const tasks = JSON.parse(event.data).tasks;
if (tasks.length === 0)
return;
// We will only have one task; our provisioning task.
if(tasks[0].state.state === "Completed")
resolve();
}
});
await waitForProvision;
taskSocket.close();
import json
import time
from websockets.sync.client import connect
# The UID for the copy task, used to track its progress
provision_task_uid = provision_result["taskUid"]
# Task status WebSocket URL
task_socket_url = provision_result["taskStatusStream"]
with connect(task_socket_url, open_timeout=30) as sock:
sock.send(json.dumps({
"op": "Set",
# Tell the API we only want to receive state reports for our provision task.
"policies": [
{"uid": provision_task_uid, "reports": ["State"]}
],
}))
# Timeout for provision.
timeout_seconds = 30
start_time = time.monotonic()
while True:
if time.monotonic() - start_time > timeout_seconds:
raise TimeoutError("Timed out waiting for media provision.")
message = sock.recv()
tasks = json.loads(message).get("tasks", [])
if not tasks:
continue
# We will only have one task; our provisioning task.
if tasks[0]["state"]["state"] == "Completed":
break
sock.close()
After we’ve determined that the provision task has finished, we can find the media on the server using the /media/list endpoint.
for (const localPath of provisionBody.localPath) {
// Construct media list API endpoint to view media at given location.
// We want to check the media at the two project directories we provisioned to.
const listMediaUrlParams = new URLSearchParams({
directory: path.dirname(localPath)}
).toString();
const listMediaEndpoint = new URL(
`/api/service/media/list?${listMediaUrlParams}`,
`http://${DISGUISE_SERVER_IP}`
);
const listResponse = await fetch(listMediaEndpoint, { method: 'GET' });
const listResponseData = await listResponse.json();
if (!listResponse.ok)
throw new Error(`HTTP Error ${listResponse.status}:
'${listResponseData.status.message}`);
if (!listResponseData.files.find(file =>
file.path.includes(path.basename(localPath))
))
throw new Error("Couldn't find provisioned media file!");
}
from pathlib import PurePosixPath
from urllib.parse import urlencode
for local_path in provision_body["localPath"]:
directory = str(PurePosixPath(local_path).parent)
filename = PurePosixPath(local_path).name
# Media list API endpoint to view media at the given location.
list_params = urlencode({"directory": directory})
list_media_endpoint = f"{BASE_URL}/api/service/media/list?{list_params}"
list_response = requests.get(list_media_endpoint)
list_response_data = list_response.json()
if not list_response.ok:
raise RuntimeError(
f"HTTP error {list_response.status_code}: "
f"{list_response_data['status']['message']}"
)
if not any(filename in file["path"] for file in list_response_data["files"]):
raise RuntimeError("Couldn't find provisioned media file!")
We may now remove the media from the server using the /media/remove endpoint.
for (const localPath of provisionBody.localPath) {
const removeEndpoint = new URL(
"/api/service/media/remove",
`http://${DISGUISE_SERVER_IP}`
);
const removeResponse = await fetch(removeEndpoint, {
method: 'POST',
body: JSON.stringify({path: localPath})
});
const removeResponseData = await removeResponse.json();
if (!removeResponse.ok)
throw new Error(`HTTP error ${removeResponse.status}:
${removeResponseData.status.message}`);
}
for local_path in provision_body["localPath"]:
remove_endpoint = f"{BASE_URL}/api/service/media/remove"
remove_response = requests.post(remove_endpoint, json={"path": local_path})
remove_response_data = remove_response.json()
if not remove_response.ok:
raise RuntimeError(
f"HTTP error {remove_response.status_code}: "
f"{remove_response_data['status']['message']}"
)
As is mentioned on the Paths page, we are given access only to certain directories via API. When attempting to access locations other than these, we will receive an appropriate error message.
Let’s have a look at some examples of disallowed locations in a provision request. These directories will fail for all API endpoints.
{
"mediaPath": "\\our-network-share/media/background.mov",
"localPath": [ "C:/Users/Disguise/Documents/background.mov" ]
}
{
"mediaPath": "\\our-network-share/media/background.mov",
"localPath": [ "{projects}/../background.mov" ] // Trying to escape out of an allowed directory.
}
{
"mediaPath": "\\our-network-share/media/background.mov",
"localPath": [ "C:/Users/Disguise/Documents/d3 Projects/../background.mov" ]
}
Upon inspecting the result of the provision task for any of the above provision requests we will find the following message:
"message": "'{THE_PATH}' is restricted. Please use a d3 directory."