HTML5 Video Editor Integration Guide
Deployment
The editor server instance could be installed locally using docker.
To get the docker-compose configuration files please contact info@solveigmm.com
Host requirements
RAM at least 2GB, 2 CPU ( AVX instructions set is required by mongodb)
HDD 20 GB of free space (Docker images size is 10 GB)
GPU to with minimum OpenGL 2.0 GLSL 1.10 support (optional, for chromakey feature)
Install docker compose
To install Docker Compose for your host please use https://www.docker.com/
Minimal docker version required 20.10.21
Ubuntu 22.04:
sudo su
apt-get update
apt-get upgrade
apt-get install docker-compose
Windows:
Follow installation instruction from official package https://www.docker.com
The WSL 2 option should be set to provides better performance than the Hyper-V backend
MacOS:
Follow installation instruction from official package https://www.docker.com
For the MACOS host docker should be run with ordinary user (without su privileges)
Configuration notes
Replace MASTER_SERVER_IP variable in docker_build_solveig/master/.env by your server IP address on the network (localhost and 127.0.0.1 will not work)
For the server instance there should be ports exposed and accessible for MASTER_SERVER_IP. Please expose it before running the server:
9092 – kafka
27017- mongodb
80 – nginx front
3478(TCP and UDP) - coturn
The main docker configuration file:
docker_build_solveig/master/docker-compose.yml
To run the server:
docker-compose up -d --force-recreate
To stop the server:
docker-compose down
The Editor is available by URL ( Google Chrome browser is recommended):
http://localhost
The test media files are located at ./smm_cloud_editor_v2/shared/files
You can try to put your own there to see it in media tab or import media files via browser
Chromakey requirements
For "chromakey" feature the host instance should have GPU with minimum OpenGL 2.0 GLSL 1.10 support.
The gpu driver should be installed and configured properly.
Amazon instances with gpu: https://docs.aws.amazon.com/dlami/latest/devguide/gpu.html
G4ad instance GPU driver installation
To install driver at G4ad instance:
https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/install-amd-driver.html
Alternative way to install drivers:
Works with the kernel version linux-image-5.19.0-1029-aws
sudo apt update
sudo apt install linux-image-5.19.0-1029-aws linux-headers-5.19.0-1029-aws linux-modules-extra-5.19.0-1029
Change /etc/default/grub
GRUB_DEFAULT="Advanced options for Ubuntu>Ubuntu, with Linux 5.19.0-1029-aws"
sudo update-grub
apt-mark hold 5.19.0-1029-aws
sudo reboot
uname -r (should display 5.19.0-1029-aws)
Remove the Newer Kernels:
sudo apt remove linux-image-6.8.0-1015-aws linux-headers-6.8.0-1015-aws
sudo apt remove linux-image-6.8.0-1018-aws linux-headers-6.8.0-1018-aws
sudo reboot
sudo su
wget https://repo.radeon.com/amdgpu-install/22.40.5/ubuntu/jammy/amdgpu-install_5.4.50405-1_all.deb
apt install ./amdgpu-install_5.4.50405-1_all.deb
apt-get update
amdgpu-install --usecase=graphics --no-32
sudo reboot
External image library sources requirements
Pexels and Unsplash images source types requires developer's account creation:
https://www.pexels.com/api/documentation/#authorization
https://unsplash.com/developers
The Api keys are configured at "docker_build_solveig/master/.env" :
#PEXELS_API_KEY=<your_api_key>
#UNSPLASH_API_KEY=<your_api_key>
#UNSPLASH_API_SECRETE=<your_api_key_secrete>
#UNSPLASH_APP_NAME=<your_unsplash_app_name>
Customization
By default each user has options for customization since it has linked developer's profile (configured via ENABLE_DEV_PROFILE_FOR_USERS=TRUE environment variable).
You can adjust basic styles and text definitions via "Customize editor" UI.
With the developer's profile you can configure:
User interface styles and text definitions:
- companyName - Used at preview screen instead of default "Solveig Multimedia"
- appTitle - Used at page's header and "title" tag instead of default "Online Video Editor"
- svgLogo - Logo image used at preview screen and header
- customCSS - custom CSS that allows to change html elements styles
- customJS - custom JS that allows to run external scripts.
- customLanguage - custom JSON with text definitions.
See dev_profile_object for details.
callBackUrl to receive events that are performed by the user while using the editor. See CallBack events for details
Custom urls that will be used by the editor to fetch media assets information. See Override media assets information API and Generate media info for details
outputS3Bucket to upload output results to Amazon S3 Bucket. See S3 bucket configuration for details
If the server configured with disabled customization for regular users (ENABLE_DEV_PROFILE_FOR_USERS=FALSE) to configure UI it is required to create a developer's user profile to get an apiKey using /api/developer_profiles.
The customization UI will be available at https://smarteditingonline.solveigmm.com/?auth_token=<apiKey>
To create regular users sessions with customized interface use /api/users. The apiKey field should identify the developer's profile. The user's session will be available at https://smarteditingonline.solveigmm.com/?auth_token=<userId>
Override media assets information API
The Developer profile allows to override media assets information API by custom implementation at your server. The following variables available:
- fetchFileUrl - to override fetching media information /api/mediafiles/<id>
- mediaTabContentUrl to override fetch media files to display at media tab /api/mediafiles
- fetchThumbnailUrl to override fetch media file's thumbnail to display at timeline/api/clip_thumbnails
The urls responses should include the headers to prevent CORS policy issues:
'Access-Control-Allow-Origin':'*'
'Access-Control-Allow-Headers':'content-type'
All request from the editor will contain an Authorization header with the token that was assigned to user's session at apiAuthToken
"Authorization": <auth_token>
Generate media info
The media file information required for Custom media assets could be prepared in advance.
Use existing html5editor backend server to generate media info.
All request should contain an Authorization header:
"Authorization": <valid_api_key>
valid_api_key
- the valid api developer's key
Create media info task
Request:
POST
/api/task/mediainfo
Request Body:
{
"originalUrl": <string>, (optional)
"awsBucket": { (optional)
"awsBucketName":<string>,
"awsAccessKey":<string>,
"awsSecreteKey":<string>
}
}
Description:
originalUrl
- file's url of the orignal media file
awsBucket
- AWS bucket credentials object. (Note: aws bucket processing works only with ENABLE_KEEP_MEDIAINFO_DATA=true)
Response:
{
"id": <taskId>
}
Get media info
Request:
GET
/api/task/mediainfo/<taksId>
Response:
Status 202 - The media file processing is in progress
Status 200 - The media file was processed
JSON mediainfo object:
{
"info":{
"name":<string>,
"filetype":<string>,
"mediaType":<string>,
"duration":<number>,
"size":<number>,
"width":<number>,
"height":<number>,
"thumbnail":<base64_encoded_png_image>,
"previewUrl":<string_url>,
"audioTracks":[<string>, <string>]
},
"thumbnails":[{
"time":<number>,
"data":<base64_encoded_png_image>
}]
}
Description:
info
- should be used as a response for custom api urls implementation ( fetchFileUrl, mediaTabContentUrl at devProfile ).
thumbnails
- should be used to implement custom api url for timeline thumbnails (fetchThumbnailUrl at devProfile )
Custom media assets examples
Import media by url example
In this example the user's session will be created with the import process started.
NodeJS example
const { platform } = require('os');
const { exec } = require('child_process');
const axios = require('axios')
const masterServerIP = 'smarteditingonline.solveigmm.com' // should be changed according to your installation
const baseURL = `https://${masterServerIP}`
// import media url could be set via command line argument
let url = 'https://smarteditingonline.solveigmm.com/files/test_video.mp4'
const myArgs = process.argv.slice(2)
if (myArgs.length){
url = myArgs[0]
}
const axiosInst = axios.create({ baseURL })
const openChrome = url => {
let command
const osPlatform = platform()
if (osPlatform === 'win32') {
command = `start chrome ${url}`;
} else if (osPlatform === 'darwin') {
command = `open -a "Google Chrome" ${url}`;
} else {
command = `google-chrome --no-sandbox ${url}`;
}
exec(command);
}
const registerUser = async () => {
try {
const user = {
name: 'Example user',
}
const resp = await axiosInst.post('/api/users', user)
return resp.data.id
} catch (e) {
throw Error(`Error registerUser: ${e}`)
}
}
const importFromUrl = async userId => {
try {
const headers = { 'Authorization': userId }
await axiosInst.post('/api/import/media/url', {url}, { headers })
} catch (e) {
throw Error(`Error registerUser: ${e}`)
}
}
const main = async () => {
const userId = await registerUser()
await importFromUrl(userId)
const chromeUrl = `${baseURL}?session_id=${userId}`
console.log(`open chrome with the url: ${chromeUrl}`)
openChrome(chromeUrl)
}
main()
Python example
import sys
import platform
import subprocess
import requests
master_server_ip = 'smarteditingonline.solveigmm.com' # should be changed according to your installation
base_url = f'https://{master_server_ip}'
# import media URL could be set via command line argument
url = 'https://smarteditingonline.solveigmm.com/files/test_video.mp4'
my_args = sys.argv[1:]
if my_args:
url = my_args[0]
axios_inst = requests.Session()
axios_inst.headers.update({'Content-Type': 'application/json', 'Accept': 'application/json'})
axios_inst.headers.update({'Authorization': ''})
def open_chrome(url):
os_platform = platform.system().lower()
if os_platform == 'windows':
command = f'start chrome {url}'
elif os_platform == 'darwin':
command = f'open -a "Google Chrome" {url}'
else:
command = f'google-chrome --no-sandbox {url}'
subprocess.run(command, shell=True)
def register_user():
try:
user = {'name': 'Example user'}
resp = axios_inst.post(f'{base_url}/api/users', json=user)
resp.raise_for_status()
return resp.json()['id']
except requests.exceptions.RequestException as e:
raise Exception(f'Error register_user: {e}')
def import_from_url(user_id):
try:
headers = {'Authorization': user_id}
axios_inst.post(f'{base_url}/api/import/media/url', json={'url': url}, headers=headers).raise_for_status()
except requests.exceptions.RequestException as e:
raise Exception(f'Error import_from_url: {e}')
def main():
user_id = register_user()
import_from_url(user_id)
chrome_url = f'{base_url}?session_id={user_id}'
print(f'Open Chrome with the URL: {chrome_url}')
open_chrome(chrome_url)
if __name__ == '__main__':
main()
PHP example
<?php
$masterServerIP = 'smarteditingonline.solveigmm.com'; // should be changed according to your installation
$baseURL = "https://$masterServerIP";
// import media URL could be set via command line argument
$url = 'https://smarteditingonline.solveigmm.com/files/test_video.mp4';
$myArgs = array_slice($argv, 1);
if (!empty($myArgs)) {
$url = $myArgs[0];
}
function openChrome($url) {
$osPlatform = strtolower(php_uname('s'));
$command = '';
if (strpos($osPlatform, 'win') !== false) {
$command = "start chrome $url";
} elseif (strpos($osPlatform, 'darwin') !== false) {
$command = "open -a 'Google Chrome' $url";
} else {
$command = "google-chrome --no-sandbox $url";
}
shell_exec($command);
}
function registerUser() {
global $baseURL;
try {
$user = ['name' => 'Example user'];
$headers = ['Content-Type: application/json'];
$response = sendRequest("$baseURL/api/users", 'POST', json_encode($user), $headers);
$responseData = json_decode($response, true);
return $responseData['id'];
} catch (Exception $e) {
throw new Exception("Error registerUser: " . $e->getMessage());
}
}
function importFromUrl($userId) {
global $baseURL, $url;
try {
$headers = ['Authorization: ' . $userId,
'Content-Type: application/json'];
sendRequest("$baseURL/api/import/media/url", 'POST', json_encode(['url' => $url]), $headers);
} catch (Exception $e) {
throw new Exception("Error importFromUrl: " . $e->getMessage());
}
}
function sendRequest($url, $method = 'GET', $data = null, $headers = null) {
$ch = curl_init();
// Disable SSL certificate verification (not recommended for production)
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
if ($data !== null) {
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
}
if ($headers !== null) {
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
}
$response = curl_exec($ch);
if ($response === false) {
throw new Exception("cURL error: " . curl_error($ch));
}
curl_close($ch);
return $response;
}
function main() {
global $baseURL;
$userId = registerUser();
importFromUrl($userId);
$chromeUrl = "$baseURL?session_id=$userId";
echo "Open Chrome with the URL: $chromeUrl\n";
openChrome($chromeUrl);
}
if (basename(__FILE__) === basename($_SERVER["SCRIPT_FILENAME"])) {
main();
}
?>
React video editor component example
In this example the video editor component "SolveigEditor" is embedded into React application.
The media url is imported using "videoUrl" property.
The callback "onVideoAdded" is called after the video was put into timeline.
The callback "onRenderComplete" is called after the project rendering completed with the url of the output media.
The "apiUrl" property defines the address of the backend api
To run example use:
npm install
npm start
index.js
/* eslint-disable no-unused-expressions */
/* eslint-disable no-sequences */
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
App.js
import { useState } from "react";
import "./App.css";
import { SolveigEditor } from "solveigmm-video-editor";
// Files hosted from the public folder
const baseUrl = process.env.PUBLIC_URL || '';
const EDITOR_JS_SRC = `${baseUrl}/editor/main.chunk.js`;
const EDITOR_CSS_SRC = `${baseUrl}/editor/main.css`;
const INITIAL_BACK_URL = localStorage.getItem('backUrl') || 'https://smarteditingonline.solveigmm.com/'
// In this example, we are using submittedUrl and submittedApi variables only for the form handling.
// You can change this approach
function App() {
const [videoUrl, setVideoUrl] = useState(
"https://smarteditingonline.solveigmm.com/files/test_video.mp4"
);
const [backUrl, setBackUrl] = useState(INITIAL_BACK_URL);
const [submittedUrl, setSubmittedUrl] = useState("");
const [submittedApi, setSubmittedApi] = useState(INITIAL_BACK_URL);
const [showEditor, setShowEditor] = useState(true);
const handleChangeShowEditor = (v) => {
setShowEditor(v);
localStorage.setItem("showEditor", v);
};
const handleRenderComplete = (data) => {
alert(`Rendering completed. Callback url: ${data.detail.downloadUrl}`);
};
return (
<div className="App">
<header className="App-header">
<div className="app-form-field">
<span className="app-form-field-text">Backend API url:</span>
<input
className="edit-input-link"
value={backUrl}
onChange={(e) => setBackUrl(e.target.value)}
placeholder="enter back url"
/>
<button
className="button-edit"
onClick={() => {
localStorage.setItem('backUrl', backUrl)
setSubmittedApi(backUrl);
setSubmittedUrl("");
// Must reload page when change backend url (for reconnect websocket)
setTimeout(() => {
window.location.reload()
}, 100)
}}
target="_blank"
rel="noopener noreferrer"
>
Change & reload
</button>
</div>
<div className="app-form-field">
<span className="app-form-field-text">Input Media url:</span>
<input
className="edit-input-link"
value={videoUrl}
onChange={(e) => setVideoUrl(e.target.value)}
placeholder="enter video url"
/>
<button
className="button-edit"
onClick={() => {
setSubmittedUrl(videoUrl);
// clear submittedUrl variable for importing twice from the same url
setTimeout(() => {
setSubmittedUrl('');
})
}}
target="_blank"
rel="noopener noreferrer"
>
Start editing
</button>
</div>
<div className="editor-actions">
<div className="app-form-field">
<input
id="showeditor"
className="app-form-field-checkbox"
type="checkbox"
checked={showEditor}
onChange={(e) => handleChangeShowEditor(e.target.checked)}
/>
<label htmlFor="showeditor" className="app-form-field-text">
Show editor
</label>
</div>
</div>
</header>
<SolveigEditor
src={EDITOR_JS_SRC}
css={EDITOR_CSS_SRC}
width="100%"
height="600px"
hidden={!showEditor}
videoUrl={submittedUrl}
apiUrl={submittedApi}
onVideoAdded={() => handleChangeShowEditor(true)}
onRenderComplete={handleRenderComplete}
/>
</div>
);
}
export default App;
Custom media assets with prepared media info
The media asset's information will be stored at locally installed editor's database. The media asset's file will be downloaded to fetch media info and removed after operation completed.
The user's session will be created with the custom media assets.
Configure .env docker variables:
MASTER_SERVER_IP - the editor's ip address
ADMIN_SECRETE - secrete to manage developer profiles.
Register developer profile
Register developer profile via API /api/dev_profiles
Use ADMIN_SECRETE as Authorization header
Replace MASTER_SERVER_IP to actual value
{
"fetchFileUrl": "http://MASTER_SERVER_IP/api/mediafiles",
"mediaTabContentUrl": "http://MASTER_SERVER_IP/api/mediafiles",
"fetchThumbnailUrl":"http://MASTER_SERVER_IP/api/clip_thumbnail"
}
The result is DEV_API_KEY created.
Create media info task
Using DEV_API_KEY as Authorization header create media info task request /api/task/mediainfo and wait for import process completed.
Specify originalUrl to media file:
originalUrl: <string>
or aws bucket credentials as follow:
awsBucket: {
"awsBucketName":<string>
"awsAccessKey":<string>,
"awsSecreteKey":<string>,
}
Register user's session
Register user's session with DEV_API_KEY via /api/users
{
"apiKey": DEV_API_KEY,
"apiAuthToken": DEV_API_KEY
}
The result is USER_SESSION_ID created.
Open user's session
Open the user's session via url
http://MASTER_SERVER_IP?session_id=USER_SESSION_ID
NodeJS example
const axios = require('axios')
const originalUrl = 'https://smarteditingonline.solveigmm.com/files/test_video.mp4'
const masterServerIP = '172.16.1.121' // should be changed according to your installation
const adminSecrete = 'kCYqikHH5860' // should be changed according to your installation
const devProfileName = 'Developer'
const devProfileEmail = 'dev@example.com'
const baseURL = `http://${masterServerIP}`
const axiosInst = axios.create({ baseURL })
axiosInst.defaults.headers.common['Authorization'] = adminSecrete
const registerDeveloperProfile = async () => {
try {
const profile = {
name: devProfileName,
eMail: devProfileEmail,
fetchFileUrl: `${baseURL}/api/mediafiles`,
mediaTabContentUrl: `${baseURL}/api/mediafiles`,
fetchThumbnailUrl: `${baseURL}/api/clip_thumbnail`
}
const resp = await axiosInst.post('/api/dev_profiles', profile)
return resp.data.apiKey
} catch (e) {
throw Error(`Error registerDeveloperProfile: ${e}`)
}
}
const processMediaInfo = async ({ apiKey, originalUrl }) => {
try {
const headers = { 'Authorization': apiKey }
let resp = await axiosInst.post('/api/task/mediainfo', { originalUrl }, { headers })
const id = resp.data.id
do {
resp = await axiosInst.get(`/api/task/mediainfo/${id}`, { headers })
} while (resp.status === 202)
} catch (e) {
throw Error(`Error processMediaInfo: ${e}`)
}
}
const registerUser = async ({ apiKey }) => {
try {
const user = {
apiKey,
apiAuthToken: apiKey
}
const resp = await axiosInst.post('/api/users', user)
return resp.data.id
} catch (e) {
throw Error(`Error registerUser: ${e}`)
}
}
const main = async () => {
const apiKey = await registerDeveloperProfile()
await processMediaInfo({ apiKey, originalUrl })
const sessionId = await registerUser({apiKey})
console.log(`open chrome with the url: ${baseURL}?session_id=${sessionId}`)
}
main()
Python example
import requests
original_url = 'https://smarteditingonline.solveigmm.com/files/test_video.mp4'
master_server_ip = '172.16.1.121' # should be changed according to your installation
admin_secret = 'kCYqikHH5860' # should be changed according to your installation
dev_profile_name = 'Developer'
dev_profile_email = 'dev@example.com'
base_url = f'http://{master_server_ip}'
session = requests.Session()
session.headers.update({'Authorization': admin_secret})
def register_developer_profile():
try:
profile = {
'name': dev_profile_name,
'eMail': dev_profile_email,
'fetchFileUrl': f'{base_url}/api/mediafiles',
'mediaTabContentUrl': f'{base_url}/api/mediafiles',
'fetchThumbnailUrl': f'{base_url}/api/clip_thumbnail'
}
resp = session.post(f'{base_url}/api/dev_profiles', json=profile)
resp.raise_for_status()
return resp.json()['apiKey']
except requests.exceptions.RequestException as e:
raise Exception(f'Error register_developer_profile: {e}')
def process_media_info(api_key, original_url):
try:
headers = {'Authorization': api_key}
resp = session.post(f'{base_url}/api/task/mediainfo', json={'originalUrl': original_url}, headers=headers)
task_id = resp.json()['id']
while True:
resp = session.get(f'{base_url}/api/task/mediainfo/{task_id}', headers=headers)
if resp.status_code != 202:
break
except requests.exceptions.RequestException as e:
raise Exception(f'Error process_media_info: {e}')
def register_user(api_key):
try:
user = {
'apiKey': api_key,
'apiAuthToken': api_key
}
resp = session.post(f'{base_url}/api/users', json=user)
resp.raise_for_status()
return resp.json()['id']
except requests.exceptions.RequestException as e:
raise Exception(f'Error register_user: {e}')
def main():
api_key = register_developer_profile()
process_media_info(api_key, original_url)
session_id = register_user(api_key)
print(f'Open Chrome with the URL: {base_url}?session_id={session_id}')
if __name__ == '__main__':
main()
PHP example
<?php
$originalUrl = 'https://smarteditingonline.solveigmm.com/files/test_video.mp4';
$masterServerIP = '172.16.1.121'; // should be changed according to your installation
$adminSecret = 'kCYqikHH5860'; // should be changed according to your installation
$devProfileName = 'Developer';
$devProfileEmail = 'dev@example.com';
$baseURL = "http://$masterServerIP";
function sendRequest($url, $method = 'GET', $data = null, $headers = null) {
$ch = curl_init();
// Disable SSL certificate verification (not recommended for production)
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
if ($data !== null) {
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
}
if ($headers !== null) {
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
}
$response = curl_exec($ch);
if ($response === false) {
throw new Exception("cURL error: " . curl_error($ch));
}
curl_close($ch);
return $response;
}
function registerDeveloperProfile()
{
global $baseURL,$adminSecret, $devProfileName, $devProfileEmail;
$profile = [
'name' => $devProfileName,
'eMail' => $devProfileEmail,
'fetchFileUrl' => "$baseURL/api/mediafiles",
'mediaTabContentUrl' => "$baseURL/api/mediafiles",
'fetchThumbnailUrl' => "$baseURL/api/clip_thumbnail",
];
$headers = ['Authorization: ' . $adminSecret,
'Content-Type: application/json'];
$response = sendRequest("$baseURL/api/dev_profiles", 'POST', json_encode($profile), $headers);
$data = json_decode($response, true);
return $data['apiKey'];
}
function processMediaInfo($apiKey, $originalUrl)
{
global $baseURL;
$headers = ['Authorization: ' . $apiKey,
'Content-Type: application/json'];
$postData = [
'originalUrl' => $originalUrl,
];
$response = sendRequest("$baseURL/api/task/mediainfo", 'POST', json_encode(['originalUrl' => $originalUrl,]), $headers);
$data = json_decode($response, true);
$id = $data['id'];
do {
$response = sendRequest("$baseURL/api/task/mediainfo/$id", 'GET', null, $headers);
$data = json_decode($response, true);
} while (array_key_exists('status', $data));
}
function registerUser($apiKey)
{
global $baseURL;
$user = [
'apiKey' => $apiKey,
'apiAuthToken' => $apiKey,
];
$headers = ['Content-Type: application/json'];
$response = sendRequest("$baseURL/api/users", 'POST', json_encode($user), $headers);
$data = json_decode($response, true);
return $data['id'];
}
function main()
{
try {
$apiKey = registerDeveloperProfile();
processMediaInfo($apiKey, $GLOBALS['originalUrl']);
$sessionId = registerUser($apiKey);
echo "open chrome with the url: $GLOBALS[baseURL]?session_id=$sessionId";
} catch (Exception $e) {
echo 'Error: ' . $e->getMessage();
}
}
main();
?>
Custom media assets using mediaFiles.json
The Custom media assets could be specified by mediaFiles.json list.
The path to json list specified by MEDIA_JSON_LIST config (default uses /var/www/smm_cloud_editor_v2/mediaFiles.json)
MEDIA_FILES_DIR configures root directory for media files (default uses /var/www/smm_cloud_editor_v2/shared/media_json_files)
Example mediaFiles.json:
In this example the 'media_json_video_mxf_xavc.mxf' will be available to all user the 'media_json_url.mxf' will be visible only to single user (1701244924069)
{
"entries":[
{
"originalPath": "media_json_video_mxf_xavc.mxf",
"proxyPath": "media_json_video_mxf_xavc.mp4"
},
{
"originalUrl": "https://smarteditingonline.solveigmm.com/files/media_json_url.mxf",
"proxyUrl": "https://smarteditingonline.solveigmm.com/files/media_json_url.mp4",
"userId": "1701244924069"
}
]
}
Fields Description:
originalPath
- The absolute or relative path to original media file
proxyPath
- The absolute or relative path to proxy media file (used for preview media files with format natively not supported by browser)
originalUrl
- The url of the original media
proxyUrl
- The proxy media file url
userId
- set the user that will be able to see media file. if not specfied avaliable to all users
To switch user profile please use the following url:
http://localhost?auth_token=1701244924069
The editor watches json list's changes and updates information on media files in database.
The import process and control it's status uses the same API as for audio library with the url /api/import/mediafiles_json
Output S3 bucket configuration
To upload output results to Amazon S3 Bucket outputS3Bucket should be configured with the following fields:
{
"awsBucketName":<string>,
"awsEndPoint":<string>,
"awsAccessKey":<string>,
"awsSecreteKey":<string>,
}
The example permission policy configuration for Amazon is the following:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::<bucketName>"
]
},
{
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:GetObject",
"s3:PutObjectAcl"
],
"Resource": [
"arn:aws:s3:::<bucketName>/*"
]
}
]
}
CallBack events
The editor sends POST requests with JSON object body to the callback url that was specified during developer registration.
Common event structure
{
"type": <string>,
"userData": <string>,
"apiAuthToken": <string>
"payload": <object>
}
type
- the event type
userData
- the data that was passed at user's session registration
apiAuthToken
- the apiAuthToken that was passed at user's session registration
payload
- contains event dependant fields
Check event at callbackUrl
Each event's POST request contains the Authorization header that is set to the unique_user_session_id that was created by Registration api ("/api/users"). You can use it to restrict posting of unauthorised data to callback.
Project created
Called when the user created new project
type:
project_create
payload:
{
"projectId":<unique_project_id>,
"thumbnail":<base64_image_string>
}
Project updated
Called when the user make some changes to project
type:
project_update
payload:
{
"projectId":<unique_project_id>,
"thumbnail":<base64_image_string>
}
Render project started
Called when the user starts project rendering
type:
render_start
payload:
{
"projectId":<unique_project_id>,
}
Render project completed
Called when the project rendering completed
type:
render_complete
payload:
{
"projectId":<unique_project_id>,
"downloadUrl":<output_file_url>,
"error":<error_message>
}
HTML5 Video Editor - Backend Server API
Authorization
auth_token is used to identify user in API requests,
Backend uses WebSocket connection associated with auth_token in order to track if the user is online.
For the new connection auth_token is created as follow (JS):
const client = new WebSocket('ws://backendHost:backendPort/websocket')
client.onopen = () => {
const authToken = Date.now().toString()
client.send(JSON.stringify({
type: 'user_event',
payload: { authToken },
}))
}
client.onmessage = message => {
const dataFromServer = JSON.parse(message.data)
if (dataFromServer.type === 'ping') {
client.send(JSON.stringify({
type: 'pong',
}))
}
}
Create user
Headers:
"Content-Type": "application/json"
Request:
POST /api/users
Request Body:
{
"name": <string>,
"avatarUrl":<string>,
"data": <json_object>,
"apiKey":<string>,
"apiAuthToken": <string>,
"hotkeysProfiles":<string>
}
Description:
All properties are optional
name
- name of the user
avatar
- base64 svg or png image of user's profile avatar
data
- custom user's data json_object
apiKey
- associates the user with the the developer's profile
apiAuthToken
- used as Authorization header with custom urls overriden by developer's profile (should be used with apiKey)
hotkeysProfiles
- Contains hotkeys profiles definition
Response:
JSON encoded object:
{
"id": <string>
}
Description:
id
- users's id
Update existing user
Headers:
"Content-Type": "application/json"
Request:
PATCH /api/users/<id>
Request Body:
userObject as for create user request
Response:
JSON encoded object:
{
"id": <string>
}
Description:
id
- users's id
Get user data
Request:
GET /api/users/<id>
Parameters:
id
- user's id
Response:
{
"name": <string>
"data": <string>
}
Create Developer's Profile
Managing developer's profile requires Authorization header to be set as specified in ADMIN_SECRETE config variable
Headers:
"Content-Type": "application/json"
"Authorization": <admin_secrete_token>
Request:
POST /api/dev_profiles
Request Body:
{
"name":<string>,
"eMail":<string>,
"callbackUrl":<string>,
"fetchFileUrl":<string>,
"mediaTabContentUrl":<string>,
"fetchThumbnailUrl":<string>,
"outputS3Bucket": {
"awsBucketName":<string>,
"awsEndPoint":<string>,
"awsAccessKey":<string>,
"awsSecreteKey":<string>,
}
"companyName": <string>,
"appTitle": <string>,
"appDescription":<string>,
"svgLogo":<string>,
"hideSVGLogo":<boolean>,
"previewPlayerLogo":<string>,
"hidePreviewPlayerLogo":<boolean>,
"favIcon":<string>,
"defaultUserAvatar":<string>,
"hideDefaultUserAvatar":<boolean>,
"customCSS": <string>,
"customJS": <string>,
"customLanguage": <string>,
"imageLibSrcType": <string>,
"pexelsApiKey":<string>,
"unsplashApiKey": <string>,
"unsplashApiSecrete":<string>,
"unsplashAppName":<string>,
"pexelsApiKey":<string>
"defaultHotkeysProfile":<string>
}
Description:
Required fields:
name
- The name of the profile
eMail
- The profiles' e-mail
Optional fields:
callbackUrl
- The url to recieve callbacks from the editor
fetchFileUrl, mediaTabContentUrl, fetchThumbnailUrl
- Custom media files' urls
outputS3Bucket
- Bucket to upload output media files
companyName
- Displayed on preview player window
appTitle
- Displayed at header and browser tab
appDescription
- Meta description tag of the web page
svgLogo
- Base64 string or url with the logo content( SVG and PNG formats supported) that should be displayed at header, preview player window (if not overriden by 'previewPlayerLogo' ), and browser tab icon (if not overriden by favIcon)
hideSVGLogo
- Hide logo logo at header and preview
previewPlayerLogo
- Base64 string or url with the logo content( SVG and PNG formats supported) that overrides preview player window logo
hidePreviewPlayerLogo
Hide logo at preview player window
favIcon
- Base64 string or url with the logo content( ICO formats supported) that overrides browser tab icon
defaultUserAvatar
- Base64 string or url with the logo content(SVG and PNG formats supported) that should be displayed if no user avatar specified.
hideDefaultUserAvatar
- Hide user's avatar logo
customCSS
- plaintext CSS or url with overriden styles
customJS
- plaintext JS or ulr with code
customLanguage
- plaintext JSON or url with text definitions. See Language JSON format for details.
defaultHotkeysProfile
- plaintext JSON or url with hotkeys definitions for the default profile. See Hotkeys JSON format for defails.
imageLibSrcType
- coma separated image sources (could be json, unsplash, pexels)
unsplashApiKey
, unsplashApiSecrete
, unsplashAppName
- credentials to use unsplash.com as source for images library
pexelsApiKey
- credentials to use pexels.com as source for images library
In case of using external hosted resources to prevent browser CORS errors make sure the respose contains headers:
'Access-Control-Allow-Origin' '*'
'Access-Control-Allow-Headers' 'authorization,content-type'
Response:
JSON encoded object:
{
"apiKey":<string>,
}
The language json template is the following:
{
languageCode: {
textId1: textValue1,
textId2: textValue2,
}
}
languageCode
- Language code according to RFC 5664 Specification
textId1
- Unique id for the text. The full list of available values could be get from UI "Customization Editor" using "Edit Text values" button
textValue1
- The value of the text for the specific language
The language json example:
{
"en": {
"EDITOR": "Editor",
"PROJECTS": "Projects",
"MY_MEDIA": "My Media",
"MY_RECORDINGS": "My Recordings",
"TRANSITIONS": "Transitions",
"TEXT": "Text",
"AUDIO": "Audio",
"FILTERS": "Filters",
"DESIGN": "Design",
"IMAGES": "Images"
}
}
Hotkeys JSON format: The hotkeys json template is the following:
{
profileName:{
functionId1: keyCodeValue1,
functionId1: keyCodeValue1
},
active: profileName
}
profileName
- the hotkeys profile name
functionId1
- the unique function id
keyCodeValue1
- the unique key code value
active
- defines the current active hotkeys profile
Update existing developer's profile
Headers:
"Content-Type": "application/json"
"Authorization": <admin_secrete_token>
Request:
PATCH /api/dev_profiles/<api_key>
Request Body:
dev_profile_object as for create request
Response:
JSON encoded object:
{
"apiKey": <string>
}
List dev profiles
Headers:
"Authorization": <admin_secrete_token>
Request:
GET /api/dev_profiles?start_number=0&count=10
Parameters:
start_number
- profiles num to start with
count
- count per page
Response:
{
"totalCount":<number>,
"profiles":[<dev_profile_object>]
}
Description:
totalCount
- defines total count of profiles
profiles
- An array of dev_profile_object
Delete dev profile
Headers:
"Authorization": <admin_secrete_token>
Request:
DELETE /api/dev_profiles/<api_key>
Get media files list
Headers:
"Authorization": <user_id>
Request:
GET /api/mediafiles?start_number=0&count=20&filter=video&sort_field=createTime&sort_order=asc&search=abc&folder=<id>
Parameters:
start_number
- the start number of first element in requested media list
count
- the max count of requested entries
filter
- defines applied filter to all files by types could be video, audio, image, folder, voiceover(optional, could be comma separated combination of multiple items)
sort_field
- defines sort field
name
- by name of the filecreateTime
- by creation time (default)size
- by sizeduration
- by duration
sort_order
- defines order in which entries should be sorted(optional)
asc
- ascendingdesc
- descending (default)
search
- defines search string that will be applied to entries(optional)
Response: Description: Used to get information on single media file Headers: Request: Parameters: Response: JSON encoded media_file_entry_object object: media_error_description_object: Used to get status on media_file_entry_object Headers: Request: Parameters: Response: JSON encoded object Description:
Object provides current task information
Used to delete media_file_entry_object Headers: Request: Parameters: Response: Creates new folder with specified name Headers: Request: Request Body: Description: Response: JSON encoded object Removes folder with all it's content recursively with subfolders Headers: Request: Parameters: Response: Headers: Request: Parameters: <id>- requested folder id Request Body: Description: Response: JSON encoded object: Headers: Request: Parameters: Request Body: Description: NOTE: in case case of moving files to root folder /api/folders/root/entries should be used Fetches list of audio library files.
To initiate audio library files fetch process use /api/import/audiolib_json
sort_field Request: Parameters: Response: Response is the same as for api /api/mediafiles Used to get information on audio library file Request: Response: Response is the same as /api/mediafiles/ Fetches list of existing categories Request: Parameters: Response: JSON array describing media_file_entry_object's: <auidolib_category_object> is JSON object Fetches list of existing categories Request: Parameters: Response: JSON array describing media_file_entry_object's: <auidolib_mood_object> is JSON object The Image library api could use different sources: The "imageLibSources" payload variable of "user_info" websocket message contains the list of available sources for the current user session. Headers: Request: Parameters: Response: JSON encoded object Headers: Request: Parameters: Response: JSON encoded image_library_object: Headers: Request: Parameters: Response: JSON object: To get thumbnails for the clip on timeline from prepared database of thumbnails use if To get list of thumbnails for the media file with step and requested count Request: Parameters: Response: JSON encoded object Description: Request: Parameters: Response: JSON encoded object Description: Editing Task is passed via POST request the request body should contain json object with XTL format task Headers: Request: Request Body: Example of xtl_task: Response: JSON encoded object Description: To get task's status use GET request Headers: Request: Parameters: Response: JSON encoded object Description: engine_error_description_object: To stop task use DELETE request Headers: Request: Parameters: Response:
JSON encoded object Project passed via POST request the request body should contain json object with project's data Headers: Request: Request Body: Description: Response: JSON encoded object: Description: Error Codes: Headers: Request: Parameters: Request Body: Description: Response:
JSON encoded object: Error Codes: To get project's data use GET request Headers: Request: Parameters: Response: JSON encoded <project_object>: Description: Error Codes: Headers: Request: Parameters: Response: JSON array describing media_file_entry_object's: Description: Error Codes: Headers: Request: Parameters: Response: JSON array describing media_file_entry_object's: Description: Error Codes: Used to delete project Headers: Request: Parameters: Response: Error Codes: The action history is saved at backend to keep project's changes between autosave intervals and ability to restore the changes if the user disconnected between autosave intervals.
The order of POST requests will define the order of the entries returned by GET
The total actions count for the project configured by ACTION_HISTORY_DEPTH (default value 1000) backend variable. Exceeding this count will remove earlier actions. The actions are linked to the project, removing the project removes all actions related to it. Action passed via POST request the request body should contain json object with action's data. On new action all action with Headers: Request: Request Body: Description: Response: JSON encoded object: Description: Error Codes: In order to undo/redo action, the "canceled" field should be updated. Headers: Request: Parameters: Request Body: Description: Response: JSON encoded object: Error Codes: Headers: Request: Parameters: Response: JSON array describing media_file_entry_object's: Description: The action object: The entries are in the order that they were POSTed. Error Codes: For editing with text overlays or image overlay it's required to put image data of rendered text or image to use it in editing task, it will be automatically deleted after user disconnected. Headers: Request: Request Body: Description: Response:
JSON encoded object Create an upload session on response Headers: Request: Request Body: Description: Response: JSON encoded object Error Codes: Headers: Request: Request Body: Description: Response: Status 200 - the media file was uploaded
Status 206 - more data expected Use the Range header in the response to determine where to start the next chunk. The header contains information on which bytes the server has received. Do not assume that the server received all bytes sent in the previous request. For example, Status 5xx - upload request was interrupted Initiates process of importing media by url at backend side. Headers: Request: Request Body: Description: Response: JSON encoded object Error Codes: Initiates process of importing media by aws bucket credentials at backend side. Headers: Request: Request Body: Description: Response: JSON encoded object Error Codes: Initiates process of fetching audio library files using json list.
The json list's absolute path is set by env variable AUDIOLIB_JSON_LIST (default uses /var/www/smm_cloud_editor_v2/audioLib.json)
The media absolute path is configured by AUDIOLIB_FILES_DIR env variable The example of audioLib.json: Initiate importing process. To get the progress and errors use GET Request: Response: JSON encoded object Error Codes: Request: Response: JSON encoded object Error Codes: The json list's absolute path is set by env variable IMAGELIB_JSON_LIST (default uses /var/www/smm_cloud_editor_v2/imageLib.json) The media absolute path is configured by IMAGELIB_FILES_DIR env variable The example of imageLib.json: Fields Description: The import process and control it's status uses the same API as for audio library with the url /api/import/imagelib_json Creates import project session on response the id of the new project will be available Headers: Request: Request Body: Description: Response: JSON encoded object Error Codes: Headers: Request: Request Body: Description: For example, Response: Use the Range header in the response to determine where to start the next chunk. The header contains information on which bytes the server has received. Do not assume that the server received all bytes sent in the previous request. For example Range bytes=0-524287 - means that server has received the first 524,288 bytes ( https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Range ) Headers: Request: Response: JSON encoded object Description: Headers: Request: Response: JSON encoded object Voiceover is implemented using kurento media server Creates webrtc connection and start sending voice data to server Headers: Request: Request Body: Description: Response: JSON encoded object Error Codes: Start record voiceover data to file Headers: Request: Request Body: Description: Response: JSON encoded object Error Codes: Headers: Request: Description: Response: JSON encoded object Error Codes: Get backend live statistics. Request: Response: JSON encoded object Description: Request: Response: JSON encoded object Description Version string contains 1 - major, 0 - minor, 20 - year, 06 - month, 18 - dayfolder
- defines the folder with {
"total_count":4,
"entries":[
<media_file_entry_object>,
<media_file_entry_object>,
<media_file_entry_object>,
<media_file_entry_object>,
]
}
total_count
- defines total count of files
entries
- An array of media_file_entry_objects
Get information on media file
"Authorization": <user_id>
valid_auth_token
- was received on authorizationGET
/api/mediafiles/<id>
id
- media file id{
"id":<string>,
"name":<string>,
"type":<string>,
"filetype":<string>,
"mediaType":<string>,
"duration":<number>,
"size":<number>,
"width":<number>,
"height":<number>,
"thumbnail":<base64_encoded_png_image>,
"previewUrl":<string_url>,
"originalUrl"::<string_url>,
"audioTracks":[<string>,<string>],
"status":<string>,
"progress":<number>,
"error": <media_error_description_object>,
"taskId": <string>,
"categoryId":<string>,
"moodId":<string>
}
id
- defines file idname
- defines file name of entrytype
- defines entry type, could be "test"
, "uploaded"
, "url"
, "output"
test
- test file available to all usersuploaded
- files that was uploaded by userurl
- file is imported from urloutput
- files that was exported/renderedfiletype
- defines entry type, could be: "mp4"
, "mov"
, "mp3"
, "png"
,"m4a"
,"jpg"
,"mxf"
, "folder"
, "webm"
mediaType
- defines entry media type, could be: "video"
, "audio"
, "image"
duration
- is duration in 100ns units,size
- file size in bytes,width, height
- is video resolution in pixels of source mediathumbnail
- is png image converted to base64 encoded string.previewUrl
- url for playing media fileoriginalUrl
- url for original media file. In case of using proxy file for previewUrlaudioTracks
- array of audio tracks namesstatus
- defines media file status if some operation is in progress, could be
render
- status for project output file that is rendering nowupload
- status for media file that user is uploading nowimport
- status for media file that is importing from url nowconvert
- status for media file that is converting nowprepare_thumbnails
- status for media file that is involved in thumbnails building process.ready
- status indicates that on media file there will no further operations expectedprogress
- determine progress of the current stageerror
- determine error description JSON object <media_error_description_object>taskId
- determine id of render task used for output files (optional)categoryId
- category id of the media file, used with audio library files (optional)moodId
- mood id of the media file, used with audio library files (optional)
Media error description object
{
"type": <error_type>,
"message": <text>,
"title": <error_title>,
}
error_type
could be:
exceeded_file_size
- Exceeded single file size limitexceeded_per_user_size
- Exceeded per user size limitlow_disk_space
- low disk space on serverunsupported_media
- media file is not supportedinternal_server_error
- Internal server errornot_found
- media file not foundused_in_projects
- media file is used in projectstext
- contains error descriptionerror_title
- the error title
Get status on media file
"Authorization": <user_id>
valid_auth_token
- was received on authorizationGET
/api/mediafile/status?&id="1233f2"
id
- media file id{
"status":<media_status_string>,
"progress":<number>,
"error": <media_error_description_object>,
"taskInfo":<task_info_object>
}
media_status_string
- string description of current status
new
- initial status for new filesupload
- the media is now uploading by users3_upload
- the media is uploading to s3 bucket by serverimport
- the media is downloading from urlconvert
- the media is convertingmediainfo
- the server fetches media infoprepare_thumbnails
- the prepare thumbnails is in progressready
- the final status for media is ready to userender
- the media is in render status (used for output items in media tab)progress
- main progress in percenterror
- see media_error_description_objecttaskInfo
- see task_info_object (optional):task_info_object
{
status:<task_status_string>,
progress: <number>
}
status
- current task's status
new
- initial task statusrunning
- task is runningcompleted
- task was successful completedaborted
- task was aborted by user or error messagedownloading
- downloading media fileuploading
- uploading media fileprogress
- current status progress in percent
Delete media file
"Authorization": <user_id>
valid_auth_token
- was received on authorizationDELETE
/api/mediafiles/<id>?force=1
id
- media file idforce
- ignore error{
"error": <media_error_description_object>
}
Working with folders
Create folder
"Content-Type": "application/json"
"Authorization": <user_id>
POST /api/folders
valid_auth_token
- was received on authorization{
"name": "folder_name"
"folder": "folder_id"
}
name
- the name of the folder,folder
- the folder's id to create new folder in{
"id":"2sdv342s8834"
}
Delete folder
"Authorization": <user_id>
DELETE
/api/folders/<id>?force=1
id
- folder's idforce
- ignore error{
"error": <media_error_description_object>
}
Update folder name
"Content-Type": "application/json"
"Authorization": <user_id>
PATCH
/api/folders/<id>
{
"name": <folder_new_name>,
}
name
- name of the folder. Optional{}
Move media file(s), folders to another folder
"Content-Type": "application/json"
"Authorization": <user_id>
PUT
/api/folders/<id>/entries
<id>
- requested folder id{
"files": [],
"folders": [],
}
files
- an array of file ids to move to folder ( Optional)folders
- an array of folder ids to move to folder ( Optional)
Audio library
Get audio library files list
GET /api/audiolib_files?type=music&start_number=0&count=20&sort_field=createTime&sort_order=asc&search=abc&category_id=<id>&mood_id=<id>
start_number
- the start number of first element in requested media listcount
- the max count of requested entriestype
- selects the list of specific type could be:
music
- audio tracks with music composition (default)sound
- audio samples of specific sounds or noisescategory_id
- optional field to filter by specific categorymood_id
- optional field to filter by specific moodsort_field
- defines sort field
name
- by name of the file (default)createTime
- by creation timesort_order
- defines order in which entries should be sorted(optional)
asc
- ascending (default)desc
- descendingsearch
- defines search string that will be applied to entries(optional) Get information on audio library file
GET /api/audiolib_files/<id>
Get categories list for audio library files
GET /api/audiolib_files/categories?type=music
type
- field to define type of the list could be:
music
- audio tracks with music composition (default)sound
- audio samples of specific sounds or noises{
"entries":[
<auidolib_category_object>,
<auidolib_category_object>,
<auidolib_category_object>,
<auidolib_category_object>,
],
}
{
"id": <unique_id>
"name": <category_name>
"entriesCount": 20
}
Get Moods list for audio library files
GET /api/audiolib_files/moods?type=music
type
- field to define type of the list could be:
music
- audio tracks with music composition (default)sound
- audio samples of specific sounds or noises{
"entries":[
<auidolib_mood_object>,
<auidolib_mood_object>,
<auidolib_mood_object>,
<auidolib_mood_object>,
],
}
{
"id": <unique_id>
"name": <mood_name>
"entriesCount": 20
}
Images library
Get images library files list
"Authorization": <user_id>
GET
/api/imagelib_files/<source>?page=<number>&count=<number>&search=<string>&sort_field=createTime&sort_order=asc&category_id=<string>
source
- defines the image library sourcepage
- the page numbercount
- the count of entries per pagecategory_id
- optional field to filter by specific categorysort_field
- defines sort field
name
- by name of the file (default)createTime
- by creation timesort_order
- defines order in which entries should be sorted(optional)
asc
- ascending(default)desc
- descendingsearch
- defines search string that will be applied to entries(optional){
entries: [<image_library_object>]
total_count:<number>
}
entries
- an array of image_library_objectstotal_count
- number of all entries matched to request Get information on image library file
"Authorization": <user_id>
GET
/api/imagelib_files/<source>/<id>
source
- defines the image library sourceid
- defines image file id{
"id":<string>,
"name":<string>,
"width":<number>,
"height":<number>,
"thumbnail":<string>,
"previewUrl":<string>,
"originalUrl":<string>,
"webpageUrl":<string>,
"userName":<string>,
"size":<number>,
"filetype":<string>,
}
name
- the title of the imagewidth
, height
- is image resolution in pixels of source mediathumbnail
- is png image converted to base64 encoded string or urlpreviewUrl
- url for previeworiginalUrl
- original url for mediawebpageUrl
- web page of the image resource on external resource(optional)userName
- the user's name that image resource belongs to (optional)size
- the size of the original image in bytes (optional)filetype
- defines entry type (could be: "png", "jpg") Get categories list for image library files
"Authorization": <user_id>
GET /api/imagelib_files/<source>/categories?page=<number>&count=<number>search=<string>
source
- defines the image library sourcepage
- the page numbercount
- the count of entries per pagesearch
- defines search string that will be applied to entries(optional){
"entries":[<imagelib_category_object>],
"total_count": <number>
}
entries
- an array of image_library_category_objectstotal_count
- number of all entries matched to request{
"id": <sting>,
"name": <string>,
"entriesCount": <number>,
"description":<string>,
"parent": <string>,
}
id
- unique category idname
- the category nameentriesCount
- the entries count in this categorydescription
- the description for category(optional)parent
- the parent category id (optional)
Thumbnails for clip on timeline
cached=true
parameter in requestcached=false
thumbnails will be generated in real timeGET
/api/clip_thumbnails?id=0&start_time=10000000&step=5000000&count=20&cached=true
id
- the media file's id ( see /api/mediafiles)start_time
- the time in 100ns units for the start timestep
- the time in 100ns units time stepcount
- count of requested thumbnailscached
- should get thumbnails from prepared database{
"status":"",
"thumbnails":[
{
time:0
data:"base64_encoded_png_image"
},
{
time:5000000
data:"base64_encoded_png_image"
}
]
}
status
- contains error message, empty string means operation success,thumbnails
- an array of thumbnails objects,time
- thumbnail time in 100ns units,data
- base64_encoded_png_image
Get single thumbnail
GET
/api/clip_thumbnail?id=0&time=10000000
id
- the media file's id ( see /api/mediafiles)time
- thumnbail's time in 100ns units{
"data":<base64_encoded_png_image>
}
data
- is png image converted to base64 encoded string.
Editing task
Create new task
"Content-Type": "application/json"
"Authorization": <user_id>
POST
/api/task/editing
{
"xtl_task": <valid_xtl_task_string>
}
<timelines version="2">
<timeline>
<group >
<track video="1" audio="1" accuracy="frame">
<clip src="media_file_id" start="00:00:01:00" stop="00:00:06:00" timeFormat="time10ms" />
<clip src="media_file_id" start="00:00:07:00" stop="00:00:12:00" timeFormat="time10ms" />
</track>
</group>
</timeline>
</timelines>
{
"taskId":"2sdv342s8834"
"fileId": "output_23234234234.mp4"
"error": <error_description_message>
}
taskId
- an id of newly created taskfileId
- identifies output Media Fileerror
- contains error description (optional)
Editing task operation status
"Authorization": <user_id>
GET
/api/task/editing?id=<task_id>
id
- requested task's id{
"id":<task_id>,
"progress":50,
"status":"running",
"error": <engine_error_description_object>,
"outputUrls":[
"http://35.234.23.3/output_files/43234/32343/name.mp4"
]
}
id
- an id of task
progress
- task progress in percent
status
- string description of current task status could be:
running
- task is runningcompleted
- task was successful completedaborted
- task was aborted by user or error messagedownloading
- downloading media files before trimming (in case of "url" file types)uploading
- uploading saved result (e.g. to amazon bucket)error
- task's Engine error description object
outputUrls
- links to output media file(s)
Engine error description object
{
"type": <type>,
"message": <message>,
"code": <code>,
}
type
- could be:
engine_error
- Edit engine errordownload_error
- unable to download input mediaupload_error
- upload errormessage
- contains error description
code
- internal engine error code
Stop editing task
"Authorization": <user_id>
DELETE
/api/task/editing?id=<task_id>
id
- requested task's id{
"status":"",
}
Projects
Create new project
"Content-Type": "application/json"
"Authorization": <user_id>
POST
/api/projects
{
"name": <project_name>,
"data": <project_data>,
"thumbnail": <base_64_string>,
"createTime": <unix_timestamp_ms>
}
name
- name of the project.data
- actual project's data.thumbnail
- the project thumbnailcreateTime
- the project createTime (Optional). If not set the backend time will be used{
"id": "B7pAdG3HJUZ25uJkwuuVcQH6Hkndx6gARcV76EV4"
}
id
- project's id400
- bad request404
- no user or no project with id found500
- internal server error
Update existing project
"Content-Type": "application/json"
"Authorization": <user_id>
PATCH
/api/projects/<id>
<id>
- requested project id{
"name": <project_name>,
"data": <project_data>,
"thumbnail": <base64_string>,
"modifTime":<unix_timestamp_ms>
}
name
- name of the project. Optionaldata
- actual project's data. Optionalthumbnail
- thumbnail for the project. OptionalmodifTime
- project's modification time. Optional. If not set the backend time will be used.{}
400
- bad request404
- no user or no project with id found500
- internal server error
Get existing project's data
"Authorization": <user_id>
GET
/api/projects/<id>
<id>
- requested project id{
"id":<project_id>,
"data":<project_data>,
"name":<project_name>,
"createTime":<unix_timestamp>,
"modifTime":<unix_timestamp>,
"thumbnail": <base64_string>
}
id
- an id of the projectdata
- actual project's dataname
- name of the projectcreateTime
- project's creation timemodifTime
- last project's modification timethumbnail
- thumbnail for the project400
- bad request404
- no user or no project with id found500
- internal server error
Get projects list
"Authorization": <user_id>
GET /api/projects?start_number=<number>&count=<number>&data=<0|1>&search=<string>
start_number
- the start number of first element in requested project listcount
- the max count of requested entriesdata
- fill <project_object>'s data field for non zero valuesearch
- defines search string that will be applied to entries(optional){
"total_count":<number>,
"entries":[
<project_object>,
<project_object>,
<project_object>,
<project_object>,
],
}
total_count
- defines total count of files
The project_object description could be found in /api/project
400
- bad request404
- no user or no project with id found500
- internal server error
Check media presence in projects
"Authorization": <user_id>
GET /api/projects/presence?file_id=<id>
file_id
- defines file's id to search{
"presence":<boolean>,
}
total_count
- defines total count of files
The project_object description could be found in /api/project
400
- bad request404
- no user or no project with id found500
- internal server error
Delete project
"Authorization": <user_id>
DELETE
/api/projects/<id>
id
- project id{}
400
- bad request404
- no user or no project with id found500
- internal server error
Create new action
canceled="true"
field will be removed, before posting action that will remove canceled actions the project should be in sync."Content-Type": "application/json"
"Authorization": <user_id>
POST
/api/actions
{
"id": <action_id>,
"project": <project_id>,
"name": <action_name>,
"data": <action_data>,
"createTime": <unix_time_ms>,
}
id
- action's idprojectId
- project's id to which action belongs toname
- the name of the actiondata
- the action datacreateTime
- the action's createTime (Optional). If not set the backend time will be used{
"id": "1632798732"
}
id
- actions'id400
- bad request404
- no user or no project with id found500
- internal server error
Undo Redo action
"Content-Type": "application/json"
"Authorization": <user_id>
PATCH
/api/actions/<id>
<id>
- requested action id{
"modifTime": <unix_time_ms>
"canceled": true
}
canceled
- true means action was undoed false action is activemodifTime
- the time of modification. If not set backend time will be used{}
400
- bad request404
- no user or no project with id found500
- internal server error
Get actions list
"Authorization": <user_id>
GET /api/actions?project=<valid_project_id>
project
- the project id that actions belongs to{
"entries":[
<action_object>,
],
}
{
"id": <action_id>,
"name": <action_name>,
"data": <action_data>,
"canceled": <boolean>,
"createTime": <unix_time_ms>
"modifTime": <unix_time_ms>
}
id
- action id.name
- name of the action.data
- actual action's data.canceled
- "true"
- action was canceled (undo operation was done), "false"
- action is activecreateTime
- action's creation time unix time in msmodifTime
- action's modification time unix time in ms400
- bad request404
- no user or no project with id found500
- internal server error
Overlay image upload
"Content-Type": "application/json"
"Authorization": <user_id>
POST
/api/overlay_img
{
"data":"base64_encoded_png_image"
}
data
- base64 encoded_png_image{
"status":"",
"id":<overlay_img_id>,
}
status
- contains error message, empty string means operation success,id
- an id of overlay_img
Local media upload
upload_session_id
will be available. The upload session is valid within 24 hours. Upload media file parts with specifying upload_session_id
. The progress of current session could be gotten via upload_session_id
Create an upload session
"Content-Type": "application/json"
"Authorization": <user_id>
POST
/api/upload/media
{
"size":12345
"name":"my media.mp4"
"folder":"folder_id"
}
valid_auth_token
- User gets this token after authorizationsize
- is number of bytes of file data, which will be transferred in subsequent requestsfolder
- destination folder's id (optional){
"id":<upload_session_id>,
}
id
- an id of uploaded media400
- bad request404
- no user found500
- internal server error507
- Upload limit exceeded error514
- Unsupported media
Put media file parts to upload session
"Authorization": <user_id>
"Content-Length": <chunk_size>
"Content-Range": bytes <range_start>-<range_end>/<total_size>
"Content-Type": "application/octet-stream"
PUT
/api/upload/media?id=<upload_session_id>
<binary_data>
chunk_size
- number of bytes in the current chunk<range_start>-<range_end>/<total_size>
- show which bytes in the file you upload.
For example, Content-Range: bytes 0-524287/2000000
shows that you upload the first 524,288 bytes (256 x 1024 x 2) in a 2,000,000 byte file ( https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Range )Range bytes=0-524287
- means that server has received the first 524,288 bytes ( https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Range )
Import media by URL
Create an url import request
"Content-Type": "application/json"
"Authorization": <user_id>
POST
/api/import/media/url
{
"url":<string>
"folder":<string>
}
url
- url to import fromfolder
- destination folder's id (optional){
"id":<imported_media_id>,
}
id
- an id of imported media400
- bad request404
- no user found500
- internal server error
Import AWS bucket
Create an aws bucket import request
"Content-Type": "application/json"
"Authorization": <user_id>
POST
/api/import/media/awsbucket
{
"awsAccessKey":<key>,
"awsSecreteKey":<secrete>,
"awsBucketName":<bucket_name>,
}
awsAccessKey, awsSecreteKey, awsBucketName
- Credentials to access aws bucket.{
"id":<awsbucket_id>,
"error":<error description>,
}
id
- an id of imported aws_bucketerror
- description if present400
- bad request404
- no user found500
- internal server error
Import audio library files from json list
{
"entries": [
{
"name": "Bubble_G_Funk_Remastered.mp3",
"relativePath": "Bubble_G_Funk_Remastered.mp3",
"category": "Electro",
"type": "music"
},
{
"name": "cats.mp3",
"relativePath": "cats.mp3",
"category": "animals",
"type": "sound"
},
]
}
Create an audio library files import request
POST
/api/import/audiolib_json
{
}
500
- internal server error Get an audio library files import process status
GET
/api/import/audiolib_json
{
"processedCount": 1
"totalCount": 256
"error":<error description>,
}
error
- contains error description if presentprocessedCount
- contains count of files already handledtotal count
- contains total number of files500
- internal server error Import images library files from json list
{
"entries": [
{
"name": "Chair",
"relativePath": "architecture1.jpg",
"category": "Architecture"
},
{
"name": "Blue Ocean",
"url": "https://smarteditingonline.solveigmm.com/files/ocean.jpg",
"category": "Nature",
"subCategory":"Ocean",
"width":1920,
"height":1080,
"thumbnail":"https://smarteditingonline.solveigmm.com/files/ocean.jpg",
"size":241888,
"filetype":"jpg",
"userName":"SolveigMM",
"userWebPage":"https://solveigmm.com"
}
]
}
name
- The name of the image displayed in mediarelativePath
- The relative path to IMAGELIB_FILES_DIR of the image on local driveurl
- The url of the original imagecategory
- The root category name for the imagesubCategory
- The subcategory name for the imagewidth, height
- The original image dimensionsthumbnail
- The thumbnail url to be displayed in media (the default size required 174x113, other size will be scaled automatically)size
- The file size of the image displayed in popupfiletype
- The file type of the image displayed in popupuserName, userWebPage
- used to reference image author Import Project
Create an import session
"Content-Type": "application/json"
"Authorization": <user_id>
POST
/api/upload/project
{
"size":12345
"name":"edlproject.zip"
"fps":29.97
"createFolder":true
}
size
- is number of bytes of file data, which will be transferred in subsequent requestsname
- the name of the uploaded project file (optional)fps
- the fps that will be used at the import stage e.g. for edl that is required(optional)createFolder
- import media files to the folder with the project name or to root media (optional){
"id":<project_id>,
}
id
- an id of the new project400
- bad request500
- internal server error Put project file parts to import session
"Authorization": <user_id>
"Content-Length": <chunk_size>
"Content-Range": bytes <range_start>-<range_end>/<total_size>
"Content-Type": "application/octet-stream"
PUT
/api/upload/project?id=<project_id>
<binary_data>
valid_auth_token
- User gets this token after authorizationchunk_size
- number of bytes in the current chunk<range_start>-<range_end>/<total_size>
- show which bytes in the file you upload.Content-Range: bytes 0-524287/2000000
shows that you upload the first 524,288 bytes (256 x 1024 x 2) in a 2,000,000 byte file (
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Range )Status 200
- the media file was uploadedStatus 206
- more data expectedStatus 5xx
- upload request was interrupted Get import project session progress and status
"Authorization": <user_id>
GET
/api/upload/project?id=<project_id>
{
"stage": <import_project_stage>,
"progress": 0
"error": "error description"
}
import_project_stage
- the status of the import process, could be:
"upload"
- the data is uploading now"unpack"
- the data is unpacking"import_media"
- import media files"ready"
- the import process completedprogress
- the progress value of the current stageerror
- error description if present Cancel import project session
"Authorization": <user_id>
DELETE
/api/upload/project?id=<project_id>
{}
Voiceover recording
Create voiceover recording session
"Content-Type": "application/json"
"Authorization": <user_id>
POST
/api/voiceovers
{
"sdpOffer":<sdp_offer>
"iceCandidates":<array_of_local_ice_candidates>
}
offer
- SDP offer string created by browser webRtcPeer.generateOffericeCandidates
- local ice candidates array gathered by browser{
"id":<new_voiceover_id>
"sdpAnswer": <remote_sdp_answer>
"iceCandidates": <array_of_remote_ice_candidates>
}
500
- internal server error Start voiceover recording
"Content-Type": "application/json"
"Authorization": <user_id>
PUT
/api/voiceovers/<id>
{
"name": <the file name for_recording>
}
id
- recording idname
- new recording file name (optional) , the backend will set name automatically if missing{
error: <error description>
}
error
- Error description if present500
- internal server error Stop and delete voiceover recording session
"Authorization": <user_id>
DELETE
/api/voiceovers/<id>?keepRecording=1
id
- recording idkeepRecording
- flag determines keeping media file recorded during the session{
}
500
- internal server error Statistics
GET
/api/stat
{
"activeUsersCount":1,
"activeBuildThumbsTasksCount":0,
"activeRenderTasksCount":1,
"activeImportFromURLTasksCount":0,
"activeImportFromLocalDriveCount":0
}
activeUsersCount
- Count of active useractiveBuildThumbsTasksCount
- Count of active build timeline thumbnails tasksactiveRenderTasksCount
- Count of active render tasksactiveImportFromURLTasksCount
- Count of files that are downloading from url nowactiveImportFromLocalDriveCount
- Count of files that are uploading from local drive now
Backend version
GET
/api/version
{
"version":"1.0.2006.18",
}