Scripting

### Simple scripting

It is possible to embed scripts to be executed, either at each frame or on a script-driven timeout.


```
[
{ "id": "seq1", "start": -1, "seq": [ { "src": [{"in": "media.mp4"}], "start": 1, "stop": 5} ] },
{ "id": "s1", "sources": ["seq1"]}

{ "script": "file.js"}
]
```

This will load the content of `file.js` and execute it. The `script` objects are designed for very simple scripts, with no possible function definitions and module import.

Now let's code the script. Globals used for most scripts are explained in [gpac -h avmix:playlist](avmix#scripts).
Our script changes a scene rotation every 2.5 seconds.

```
//get a scene
let s = get_scene("s1");
//get current rotation value
let rot = s.get("rotation");
rot += 10;
if (rot>360) rot=0;
//set new rotation value
s.set("rotation", rot);
//ask to be updated in 2.5 seconds
return 2.5;
```

### Complex scripting

If your script requires external modules and performs quite complex things, you will need to use a JS scene module not drawing anything.

```
[
{ "id": "seq1", "start": -1, "seq": [ { "src": [{"in": "media.mp4"}], "start": 1, "stop": 5} ] },
{ "id": "s1", "sources": ["seq1"]}

{ "js": "mymodule.js"}
]
```

The `mymodule.js` script will need to export a `load` function returning an object implementing a `update` function. All other callbacks are not needed if you don't draw.
 
```
//content of mymodule.js
import {XMLHttpRequest} from 'xhr'

export function load() { return {

rot: 0,
next: 0,

update: function()
{
//manage how often you want to perform our tasks, here only once every 2 seconds
if (this.next>video_time) return 0;
this.next += 2*video_timescale;

print('In callback !');
//do your logic here, XHR and co - here we force rotation on scene 's1'

this.rot += 10;
if (rot>360) rot=0;
//set new rotation value
let s = get_scene("s1").set("rotation", rot);

//always return 0 meaning "don't draw scene"
return 0;
}


}; }

```
 
Your module can also control the playlist through several functions:

- remove_element(id_or_elem): removes a scene, group or sequence from playlist
- parse_element(JSON_obj): parses a root JSON object and add to the playlist
- parse_scene(JSON_obj, parent_group): parses a scene from its JSON object and add it to parent_group, or to root if parent_group is null
- parse_group(JSON_obj, parent_group): parses a group from its JSON object and add it to parent_group, or to root if parent_group is null
- reload_playlist(JSON_obj): parses a new playlist from its JSON object. If the calling scene is no longer in the resulting scene tree, it will be added to the root of the scene tree.

__Warning All these functions MUST be called from within the `update()` function.__


```
//resets a playlist to empty, but our module update() function will still be called after that
reload_playlist([]);

//reloads a playlist to something different
reload_playlist(
[
	//new sequence
	{"id": "seq2", "start": 0, "loop": -1, "seq": [ {"src": [{"in": "media2.mp4", "media":"v"}] } ]},
	//new scene
	{"id": "s1", "sources": ["seq2"], "width": 60, "height": 60, "shape": "ellipse"}
]);

//remove a sequence: the associated scene will no longer display the video
remove_element("seq1");

//insert a sequence with same ID as removed one: the associated scene will now display the new sequence
let seq = {"id": "seq1", "start": 0, "loop": -1, "seq": [ {"src": [{"in": "media2.mp4", "media":"v"}]} ]};
parse_playlist_element(seq);


//removes a scene
remove_element("s1");

//insert a scene in a given group, using an already running sequence
let scene_json = {"id": "s1", "sources": ["seq1"], "width": 60, "height": 60, "shape": "ellipse"};
let g = get_group("g1");
let new_scene = parse_scene(scene_json, g);
//if needed, move new_scene at desired location in g.scenes
```

A script can also modify elements of a playlist.
- Scene and group objects can be retrieved using `get_scene` and `get_group`, and properties can be accessed through `elt.get('propName')` and set through `elt.set('propName', value)`.
- Other elements cannot be directly access, but their properties can be queried and updated: 
  - use  `query_element(id, 'propName')` to query a property value on an element (returns property if success, undefined otherwise). The returned property shall not be modified.
  - use  `update_element(id, 'propName', value)` to modify a property (returns true if success, false otherwise).

Note that the `update_element` and  `query_element` functions can also be used on groups and scenes.

For example, to pause and resume a timer with ID `t1`, use
```
//pauses the timer
update_element('t1', 'pause', true);
//resumes the timer
update_element('t1', 'pause', false);
```