Project File Export
Convert a gtrk v1 timeline structure into project files for multiple non-linear editors (NLE) in a single call: Premiere/FCP7 XML, Final Cut Pro X, OpenTimelineIO, JianYing draft, CapCut draft, and the Tonghe gtrk project file. Ideal for importing an algorithmically arranged video structure straight into an editor for further refinement.
ℹ️ Note: Unlike other endpoints, this one requires no file upload — the request body itself is the timeline structure. The asset paths in
materials[].pathare local references in the caller's editing environment; the server never reads the asset files, it only converts the structure.materials[].idis the platform-returned file ID (file_id) in cloud scenarios.
Create Task
Basic Information
| Item | Value |
|---|---|
| Request Method | POST |
| Request Path | /task/video_project_struct |
| Content-Type | application/json |
| Authentication | Raw API key in the Authorization header |
Request Body
The body is a gtrk v1 timeline structure. All time fields are in seconds.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
version | string | Yes | - | Contract version, fixed at "v1" |
video_size | array | Yes | - | Canvas size [width, height] in pixels, e.g. [1920, 1080] |
video_rate | number | Yes | - | Overall frame rate (fps, integer), e.g. 60 |
duration | number | Yes | - | Total output duration (seconds) |
materials | array | Yes | - | Asset list; each entry has an id plus metadata (see below) |
video_track | array | Yes | - | Video tracks (layered), see below |
audio_track | array | Yes | - | Audio tracks (layered), see below |
beat_track | array | No | [] | HTML particle overlay layers. This endpoint ignores beat_track; to render particles use html_animate_render |
struct_meta | object | No | {} | Contains nle_draft_dir (draft folder path), see below |
project_formats | array | No | all | Subset of formats to produce; omit to produce all public formats. The legacy name output_formats is still accepted as an alias |
materials[] entry
Each asset is identified by a single id — the platform file_id in cloud scenarios, or a local ID in purely local scenarios. Clips in video_track / audio_track / beat_track reference this id via their material field.
Each entry uses one of two reference forms, and both may be mixed within one request:
- Local reference: the entry carries a non-empty
path(absolute path recommended, always forward slashes/). Theidmay then be any local identifier — it only serves as the clip reference key and no platform file check is performed. The path is written into the generated project file as-is for your local editing software to load, so make sure it is valid in your environment; - Cloud reference: the entry has no
path. Theidmust then be a platform file ID (file_id) of an existing file, otherwise6004(file not found) is returned.
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Unique asset identifier; any local ID when path is present, must be a platform file ID (file_id) when it is not; referenced by clips via material |
path | string | No | Local asset path (absolute + forward slashes recommended) for NLE project files / offline ffmpeg; never read by the server; when present, the entry is exempt from the platform file check |
duration | number | No | Total asset duration (seconds) |
video_size | array | No | Original asset size [width, height] (video assets) |
video_rate | number | No | Asset frame rate (video assets) |
audio_channel | string | No | Channel layout, "stereo" or "mono" (audio assets) |
💡 Tip: Fill in as much metadata per asset as you can, so that a client holding the gtrk can rebuild the project offline with ffmpeg without resolving IDs back through the cloud. A missing field does not break serialization — it is simply omitted.
Track object in video_track[] / audio_track[]
Both video and audio tracks are layered by track: track_index is an explicit integer z-order, and the track with the smallest track_index is the base track (main); the rest are overlays (larger track_index is closer to the front).
| Field | Type | Required | Description |
|---|---|---|---|
track_index | number | Yes | Explicit layer; the smallest is the base track (main) |
track_size | array | No | (video track only) Original asset size [width, height] of this track, for scaling against the canvas video_size |
muted | boolean | No | (video track only) Track-level default mute, can be overridden by a clip-level muted |
volume | number | No | (audio track only) Track-level default volume, can be overridden by a clip-level volume |
track_timeline | array | Yes | Sequence of clip objects on the track. Array order does not matter (the server places clips by track_st) |
Clip object in track_timeline[] (video / audio)
Every non-gap clip carries both the source-trim timecodes (clip_st/clip_ed) and the on-track timecodes (track_st/track_ed/duration). The two sets are kept redundantly to serve both "start + duration" and "start + end" consumers. The array order of track_timeline does not matter: every clip is placed absolutely on the timeline by its track_st (the server always positions clips by track_st), so out-of-order submissions produce the same output as time-ordered ones, and real gaps between clips are preserved as-is.
| Field | Type | Required | Description |
|---|---|---|---|
clip_id | string | Yes | Clip identifier; an empty string "" means a Gap (a gap omits material / clip_st / clip_ed) |
material | string | Yes | References a materials[].id (omitted for gaps) |
clip_st | number | Yes | Source in-point within the asset (seconds) |
clip_ed | number | Yes | Source out-point within the asset (seconds) |
track_st | number | Yes | Clip in-point on the timeline (seconds) |
track_ed | number | Yes | Clip out-point on the timeline (seconds) |
duration | number | Yes | Clip duration (seconds) |
muted | boolean | No | (video) Clip-level mute, overrides the track-level muted |
volume | number | No | (audio) Clip-level volume, overrides the track-level volume |
ℹ️ Two-layer mute / volume semantics: video uses
muted, audio usesvolume, each at both the track level and the clip level. Precedence: clip-level if present, otherwise track-level, otherwise the default (muteddefaults tofalse,volumedefaults to1.0).
struct_meta object
| Field | Type | Required | Description |
|---|---|---|---|
nle_draft_dir | string | No | Absolute path to the JianYing / CapCut draft folder. The legacy name capcut_draft_path is still accepted as an alias |
⚠️ What
nle_draft_diractually is: it is the absolute path of the draft folder where the JianYing / CapCut draft should land on disk. JianYing / CapCut recognize a draft by the pair ofdraft_content.json+draft_meta_info.jsonsitting in that folder. If you omit this path, the endpoint only producesdraft_content.json(the content file); withoutdraft_meta_info.json(the meta file) the editor may fail to open the draft. So to open the finished draft in JianYing / CapCut, always passnle_draft_dir.
beat_track[] particle overlays
beat_track carries HTML animation particle overlay layers. This Project File Export endpoint ignores beat_track — NLE project files do not carry HTML particles. To actually render particles into the picture, use html_animate_render instead. The request body may still include beat_track to keep the gtrk structure complete, but it does not affect this endpoint's output.
project_formats values
There are 6 public output formats:
| Value | Output | Saved as |
|---|---|---|
xml | Premiere / FCP7 XML | .xml |
fcpxml | Final Cut Pro X | .xml |
otio | OpenTimelineIO | .json |
jianying | JianYing draft (produced as a pair automatically) | draft_content.json + draft_meta_info.json |
capcut | CapCut draft (produced as a pair automatically) | draft_content.json + draft_meta_info.json |
gtrk | Tonghe project file (single-index JSON, for import into the Tonghe client) | .gtrk |
ℹ️ Note: JianYing / CapCut drafts require both the content and meta files placed together in the draft folder to be recognized, so selecting
jianying/capcutautomatically produces both.files[].formatin the response still identifies individual files (e.g.jianying_draft/jianying_meta). Compatibility: the legacy fine-grained values (jianying_draft, etc.) are still accepted with their original single-file semantics; new integrations should use the table above.
Request Example
The example below shows cutting multiple segments from one source: materials declares a single video source, video_track cuts several segments from the same asset using different clip_st / clip_ed and stitches them together, then layers an audio_track, and specifies project_formats.
curl -X POST https://api.ai-mcn.tv:10000/task/video_project_struct \
-H "Authorization: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"version": "v1",
"video_size": [1920, 1080],
"video_rate": 60,
"duration": 9.0,
"materials": [
{
"id": "F100",
"path": "C:/clips/source.mp4",
"duration": 120.0,
"video_size": [1920, 1080],
"video_rate": 60,
"audio_channel": "stereo"
},
{
"id": "F200",
"path": "C:/clips/bgm.mp3",
"duration": 180.0,
"audio_channel": "stereo"
}
],
"video_track": [
{
"track_index": 0,
"track_size": [1920, 1080],
"muted": false,
"track_timeline": [
{
"clip_id": "c1", "material": "F100",
"clip_st": 10.0, "clip_ed": 13.0,
"track_st": 0.0, "track_ed": 3.0, "duration": 3.0
},
{
"clip_id": "c2", "material": "F100",
"clip_st": 55.0, "clip_ed": 59.0,
"track_st": 3.0, "track_ed": 7.0, "duration": 4.0,
"muted": true
},
{
"clip_id": "c3", "material": "F100",
"clip_st": 88.0, "clip_ed": 90.0,
"track_st": 7.0, "track_ed": 9.0, "duration": 2.0
}
]
}
],
"audio_track": [
{
"track_index": 0,
"volume": 0.8,
"track_timeline": [
{
"clip_id": "a1", "material": "F200",
"clip_st": 0.0, "clip_ed": 9.0,
"track_st": 0.0, "track_ed": 9.0, "duration": 9.0
}
]
}
],
"struct_meta": {
"nle_draft_dir": "C:/Users/Me/AppData/Local/JianyingPro/User Data/Projects/com.lveditor.draft/20260626"
},
"project_formats": ["xml", "gtrk", "jianying"]
}'
Success Example
{
"code": 200,
"msg": "success",
"data": {
"task_id": "537489015178247",
"task_type": "video_project_struct",
"status": "queued"
}
}
Query Task Result
Basic Information
| Item | Value |
|---|---|
| Request Method | GET |
| Request Path | /task/video_project_struct/{task_id} |
| Authentication | Raw API key in the Authorization header |
Response Fields (output_result)
| Field | Type | Description |
|---|---|---|
files | array | List of successfully produced project files |
files[].format | string | Output format: xml / fcpxml / otio / gtrk / jianying_draft / jianying_meta / capcut_draft / capcut_meta |
files[].file_id | string | Output file ID |
files[].download_url | string | Download path |
files[].filename | string | Suggested file name (e.g. draft_content.json; the editor only recognizes it once placed in a draft folder) |
errors | object | Failed formats and reasons (non-empty on partial success) |
ℹ️ Partial success: each format is generated independently; a failure in one format does not affect the others, and the task is still marked
completedwith the failures recorded undererrors. The task is markedfailedonly when all formats fail.
Success Example
{
"code": 200,
"msg": "success",
"data": {
"task_id": "537489015178247",
"status": "completed",
"progress": 100,
"output_result": {
"files": [
{"format": "xml", "file_id": "537489015178248", "download_url": "/download/a1/537489015178248.xml", "filename": "premiere.xml"},
{"format": "gtrk", "file_id": "537489015178249", "download_url": "/download/a2/537489015178249.gtrk", "filename": "project.gtrk"},
{"format": "jianying_draft", "file_id": "537489015178250", "download_url": "/download/a3/537489015178250.json", "filename": "draft_content.json"},
{"format": "jianying_meta", "file_id": "537489015178251", "download_url": "/download/a4/537489015178251.json", "filename": "draft_meta_info.json"}
],
"errors": {}
},
"create_time": "2026-06-01T08:00:00Z",
"update_time": "2026-06-01T08:00:03Z"
}
}
Error Codes
| Code | HTTP Status | Description | Resolution |
|---|---|---|---|
6013 | 400 | Missing required parameter (video_size/video_rate/duration/materials/video_track/audio_track) | Provide the required parameters |
6004 | 400 | A cloud-referenced asset does not exist (an entry without path whose id matches no platform file) | Verify the file_id, or add a path to switch the entry to a local reference |
6016 | 400 | Invalid parameter type or structure (e.g. a material not in the materials list, an illegal value in project_formats) | Check the timeline structure and values |
6502 | 401 | Authentication failed | Check the Authorization header |
6201 | 402 | Quota insufficient | Buy a quota pack or top up |
6202 | 402 | Balance insufficient | Top up in the dashboard |
Limitations
- Structure-only conversion: the server does not read the files referenced by
materials[].path; it only writes the paths into the project files. Make sure those paths are valid in your editing environment. - Particles are not rendered here:
beat_trackis ignored by this endpoint; render HTML particles via html_animate_render. - Per-call billing: billed once per request, regardless of how many formats are produced.
- Draft version compatibility: the generated JianYing / CapCut drafts must be opened with a compatible client version (the draft schema evolves with the app). Place
draft_content.jsonanddraft_meta_info.jsoninto the corresponding draft folder to open and keep editing. - Draft meta:
jianying_meta/capcut_metaare produced only whenstruct_meta.nle_draft_diris provided; without it only the content file is produced and the editor may fail to open the draft.