Nodeflux Central
Video Analysis

API Reference — Video Analysis

Endpoint untuk upload video rekaman, manajemen Job analisis offline, assign analitik, dan konfigurasi pipeline.

Semua endpoint memerlukan header Authorization: Bearer <token>. Basis URL mengikuti variabel lingkungan VITE_API_URL yang dikonfigurasi pada instalasi Lenz. Endpoint yang menggunakan {nvrServerId} di-proxy oleh backend API ke Media Server yang dikonfigurasi sebagai System Media Server — ganti {nvrServerId} dengan ID Media Server yang aktif.


Daftar & Detail Job

Endpoint teragregasi yang dipakai langsung oleh halaman Video Analysis. Berbeda dari endpoint /api/visionaire-media/{nvrServerId}/videos* di bawah (yang menangani upload mentah dan manajemen video per Media Server), endpoint di sini menggabungkan data video, stream internal yang menjalankannya, dan status analisisnya dalam satu respons — lengkap dengan paginasi, pencarian, filter status, dan histogram jumlah Job per status (counts) untuk badge tab.

analysis_status memiliki lima nilai: idle (belum di-assign analitik), scheduled (terjadwal), progressing (berjalan), completed (selesai), dan error. Halaman daftar melipat kelima nilai ini menjadi tiga tab: Belum Mulai (idle + scheduled), Berjalan (progressing), dan Selesai (completed + error).

Daftar Job

GET
/api/video-analysis

Authorization

bearerAuth
AuthorizationBearer <token>

In: header

Query Parameters

nvr_server_id?integer

Filter berdasarkan Media Server tertentu (opsional)

status?string

Filter status, dipisah koma. Nilai valid: idle, scheduled, progressing, completed, error. Nilai tak dikenal diabaikan.

search?string

Pencarian case-insensitive pada original_filename atau video_id

sort_by?string

Kolom pengurutan

Default"started_at"
Value in"started_at" | "created_at" | "original_filename" | "file_size" | "duration" | "synced_at"
sort_order?string
Default"desc"
Value in"asc" | "desc"
page?integer
Default1
per_page?integer

Jumlah item per halaman; 0 berarti kembalikan semua item

Default20

Response Body

application/json

curl -X GET "https://lenz.example.com/api/video-analysis?status=progressing%2Ccompleted"
{  "data": {    "videos": [      {        "nvr_server_id": 1,        "video_id": "vid_abc123",        "instance": "main",        "original_filename": "rekaman-cctv.mp4",        "file_path": "/storage/videos/rekaman-cctv.mp4",        "file_size": 104857600,        "duration": 3600,        "upload_status": "completed",        "started_at": "2026-06-20T08:00:00Z",        "ended_at": "2026-06-20T09:00:00Z",        "started_at_src": "metadata",        "started_at_src_info": "Diambil dari metadata file (Recorded_Date)",        "created_at": "2026-06-20T10:00:00Z",        "synced_at": "2026-06-20T10:01:00Z",        "updated_at": "2026-06-20T10:02:00Z",        "stream": {          "stream_id": "f1e2d3c4-stream",          "stream_name": "rekaman-cctv.mp4",          "stream_address": "rtsp://media-server/offline/rekaman-cctv",          "stream_node_num": 1,          "active": true,          "deleted_at": null        },        "analysis_status": "progressing"      }    ],    "pagination": {      "current_page": 1,      "per_page": 20,      "total_items": 42,      "total_pages": 3    },    "counts": {      "idle": 5,      "scheduled": 2,      "progressing": 1,      "completed": 30,      "error": 4    },    "success": true  }}

Detail Job

GET
/api/video-analysis/{nvrServerId}/{videoId}

Authorization

bearerAuth
AuthorizationBearer <token>

In: header

Path Parameters

nvrServerId*integer

ID Media Server

videoId*string

ID video

Response Body

application/json

application/json

curl -X GET "https://lenz.example.com/api/video-analysis/1/vid_abc123"
{  "data": {    "video": {      "nvr_server_id": 1,      "video_id": "vid_abc123",      "instance": "main",      "original_filename": "rekaman-cctv.mp4",      "file_size": 104857600,      "duration": 3600,      "upload_status": "completed",      "started_at": "2026-06-20T08:00:00Z",      "started_at_src": "metadata",      "started_at_src_info": "Diambil dari metadata file (Recorded_Date)",      "updated_at": "2026-06-20T10:02:00Z",      "stream": {        "stream_id": "f1e2d3c4-stream",        "stream_name": "rekaman-cctv.mp4",        "stream_node_num": 1,        "active": true,        "deleted_at": null      },      "analysis_status": "progressing"    },    "success": true  }}
{  "data": {    "success": false,    "message": "video not found in analysis view"  }}

Daftar Chunk Job

GET
/api/video-analysis/{nvrServerId}/{videoId}/chunks

Authorization

bearerAuth
AuthorizationBearer <token>

In: header

Path Parameters

nvrServerId*integer

ID Media Server

videoId*string

ID video

Response Body

application/json

application/json

curl -X GET "https://lenz.example.com/api/video-analysis/1/vid_abc123/chunks"
{  "data": {    "parent_stream_id": "f1e2d3c4-stream",    "chunks": [      {        "stream_id": "chunk-stream-0",        "stream_name": "rekaman-cctv.mp4_chunk_0",        "stream_node_num": 1,        "active": true,        "updated_at": "2026-06-20T10:05:00Z",        "deleted_at": null      },      {        "stream_id": "chunk-stream-1",        "stream_name": "rekaman-cctv.mp4_chunk_1",        "stream_node_num": 1,        "active": true,        "updated_at": "2026-06-20T10:05:30Z",        "deleted_at": null      }    ],    "success": true  }}
{  "data": {    "success": false,    "message": "video has no parent stream"  }}

Upload Video

Endpoint untuk mengunggah file video rekaman secara bertahap (chunked upload). File besar dikirim dalam potongan-potongan kecil menggunakan session_id yang sama. Sistem mendukung resume — jika koneksi terputus, lanjutkan dari chunk terakhir tanpa mengulang dari awal.

Unggah Chunk Video

POST
/api/visionaire-media/{nvrServerId}/videos/upload

Authorization

bearerAuth
AuthorizationBearer <token>

In: header

Path Parameters

nvrServerId*integer

ID numerik Media Server (NVR).

Query Parameters

autoplay?boolean

Jika true, video otomatis diputar sebagai stream RTSP setelah upload selesai.

Defaulttrue

Request Body

multipart/form-data

TypeScript Definitions

Use the request body type in TypeScript.

Response Body

application/json

curl -X POST "https://lenz.example.com/api/visionaire-media/0/videos/upload" \  -F file="string" \  -F filename="rekaman-2026-01.mp4" \  -F chunk_index="0" \  -F total_chunks="10" \  -F total_size="104857600"
{  "success": true,  "data": {    "message": "Chunk received",    "session_id": "sess-abc123",    "video_id": "abc123def456",    "chunk_index": 0,    "chunks_received": 1,    "total_chunks": 10,    "bytes_written": 10485760,    "total_size": 104857600,    "progress": 10,    "status": "uploading",    "seq": 1,    "instance": null  }}

Status Upload

GET
/api/visionaire-media/{nvrServerId}/videos/upload/status/{sessionId}

Authorization

bearerAuth
AuthorizationBearer <token>

In: header

Path Parameters

nvrServerId*integer

ID numerik Media Server (NVR).

sessionId*string

ID sesi upload yang dikembalikan saat chunk pertama diterima.

Response Body

application/json

curl -X GET "https://lenz.example.com/api/visionaire-media/0/videos/upload/status/string"
{  "success": true,  "data": {    "session_id": "sess-abc123",    "video_id": "abc123def456",    "original_filename": "rekaman-2026-01.mp4",    "status": "uploading",    "bytes_written": 52428800,    "total_size": 104857600,    "chunks_received": 5,    "total_chunks": 10,    "progress": 50,    "created_at": "2026-01-15T08:00:00Z",    "updated_at": "2026-01-15T08:02:30Z"  }}

Cek Resume Upload

GET
/api/visionaire-media/{nvrServerId}/videos/upload/resume/{sessionId}

Authorization

bearerAuth
AuthorizationBearer <token>

In: header

Path Parameters

nvrServerId*integer

ID numerik Media Server (NVR).

sessionId*string

ID sesi upload yang ingin dilanjutkan.

Query Parameters

filename?string

Nama file asli (opsional, untuk verifikasi).

video_id?string

ID video yang terkait (opsional, untuk verifikasi).

Response Body

application/json

curl -X GET "https://lenz.example.com/api/visionaire-media/0/videos/upload/resume/string"
{  "success": true,  "data": {    "message": "Session found, can resume",    "session_id": "sess-abc123",    "video_id": "abc123def456",    "original_filename": "rekaman-2026-01.mp4",    "status": "paused",    "chunks_received": 5,    "total_chunks": 10,    "bytes_written": 52428800,    "total_size": 104857600,    "progress": 50,    "missing_chunks": [      5,      6,      7,      8,      9    ],    "can_reuse_session": true  }}

Batalkan Upload

POST
/api/visionaire-media/{nvrServerId}/videos/upload/cancel/{sessionId}

Authorization

bearerAuth
AuthorizationBearer <token>

In: header

Path Parameters

nvrServerId*integer

ID numerik Media Server (NVR).

sessionId*string

ID sesi upload yang akan dibatalkan.

Response Body

application/json

curl -X POST "https://lenz.example.com/api/visionaire-media/0/videos/upload/cancel/string"
{  "success": true,  "message": "Upload cancelled successfully",  "session_id": "sess-abc123"}

Manajemen Video (Job)

Endpoint per-Media-Server untuk melihat, mengambil detail, memperbarui metadata, dan menghapus video yang telah diunggah. Setiap video yang diunggah mewakili satu Job analisis offline. Endpoint ini di-proxy ke Media Server; halaman daftar/detail Video Analysis sendiri membaca dari endpoint teragregasi /api/video-analysis di atas.

Daftar Video

GET
/api/visionaire-media/{nvrServerId}/videos

Authorization

bearerAuth
AuthorizationBearer <token>

In: header

Path Parameters

nvrServerId*integer

ID numerik Media Server (NVR).

Query Parameters

search?string

Pencarian parsial case-insensitive pada ID dan nama file video.

sort_by?string

Kolom pengurutan.

Default"created_at"
Value in"original_filename" | "file_size" | "duration" | "started_at" | "created_at"
sort_order?string

Arah pengurutan.

Default"desc"
Value in"asc" | "desc"
page?integer

Nomor halaman (1-indexed).

Default1
Range1 <= value
per_page?integer

Jumlah item per halaman. Nilai 0 mengembalikan semua item.

Default10
Range0 <= value

Response Body

application/json

curl -X GET "https://lenz.example.com/api/visionaire-media/0/videos"
{  "success": true,  "data": {    "videos": [      {        "id": "abc123def456",        "original_filename": "rekaman-2026-01.mp4",        "file_path": "/storage/videos/rekaman-2026-01.mp4",        "file_size": 104857600,        "created_at": "2026-01-15T08:00:00Z",        "status": "streaming",        "duration": 3600,        "started_at": "2026-01-15T00:00:00Z",        "started_at_src": "metadata",        "ended_at": "2026-01-15T01:00:00Z",        "instance": null      }    ],    "count": 1,    "pagination": {      "current_page": 1,      "per_page": 10,      "total_items": 1,      "total_pages": 1    }  }}

Detail Video

GET
/api/visionaire-media/{nvrServerId}/videos/{videoId}

Authorization

bearerAuth
AuthorizationBearer <token>

In: header

Path Parameters

nvrServerId*integer

ID numerik Media Server (NVR).

videoId*string

ID unik video.

Response Body

application/json

application/json

curl -X GET "https://lenz.example.com/api/visionaire-media/0/videos/string"
{  "success": true,  "data": {    "video": {      "id": "abc123def456",      "original_filename": "rekaman-2026-01.mp4",      "file_path": "/storage/videos/rekaman-2026-01.mp4",      "file_size": 104857600,      "created_at": "2026-01-15T08:00:00Z",      "status": "streaming",      "duration": 3600,      "started_at": "2026-01-15T00:00:00Z",      "started_at_src": "metadata",      "ended_at": "2026-01-15T01:00:00Z",      "device": {        "name": "rekaman-2026-01",        "url": "file:///storage/videos/rekaman-2026-01.mp4",        "scheme": "file",        "channel": "live",        "silent_audio": false,        "status": "running",        "publish_url": "rtsp://192.168.1.10:1935/live/rekaman-2026-01",        "created_at": "2026-01-15T08:00:00Z",        "updated_at": "2026-01-15T08:01:00Z",        "error_count": 0,        "restart_count": 0,        "uptime": 3600000      },      "instance": null    }  }}
{  "success": false,  "message": "video not found"}

Perbarui Metadata Video

Memperbarui metadata video, misalnya menyetel ulang timestamp awal rekaman (started_at) setelah upload — nilai ini menentukan korelasi waktu event di Event History.

PATCH
/api/visionaire-media/{nvrServerId}/videos/{videoId}

Authorization

bearerAuth
AuthorizationBearer <token>

In: header

Path Parameters

nvrServerId*integer

ID numerik Media Server (NVR).

videoId*string

ID unik video.

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

Response Body

application/json

curl -X PATCH "https://lenz.example.com/api/visionaire-media/0/videos/string" \  -H "Content-Type: application/json" \  -d '{    "started_at": "2026-01-15T00:00:00Z",    "started_at_src": "manual"  }'
{  "success": true,  "data": {    "video": {      "id": "abc123def456",      "original_filename": "rekaman-2026-01.mp4",      "file_size": 104857600,      "created_at": "2026-01-15T08:00:00Z",      "status": "streaming",      "duration": 3600,      "started_at": "2026-01-15T00:00:00Z",      "started_at_src": "manual"    }  }}

Hapus Video

DELETE
/api/visionaire-media/{nvrServerId}/videos/{videoId}

Authorization

bearerAuth
AuthorizationBearer <token>

In: header

Path Parameters

nvrServerId*integer

ID numerik Media Server (NVR).

videoId*string

ID unik video.

Response Body

application/json

curl -X DELETE "https://lenz.example.com/api/visionaire-media/0/videos/string"
{  "success": true,  "message": "Video deleted successfully",  "video_id": "abc123def456"}

Katalog Analitik

Endpoint untuk mengambil daftar analitik yang tersedia di instance Visionaire — digunakan untuk mengisi dropdown pemilihan analitik saat assign ke video. Field free_seats menunjukkan berapa seat lisensi masih tersedia untuk analitik tersebut.

Daftar Analitik Tersedia

GET
/api/visionaire/analytic_list

Authorization

bearerAuth
AuthorizationBearer <token>

In: header

Query Parameters

instance?string

ID instance spesifik pada mode federation (opsional)

is_aggregated?boolean

Jika true, gabungkan data dari seluruh instance pada mode federation

Response Body

application/json

curl -X GET "https://lenz.example.com/api/visionaire/analytic_list"
{  "analytic_num": 2,  "analytics": [    {      "id": "NFDSS-FR",      "name": "Face Recognition",      "description": "Deteksi dan pengenalan wajah",      "publisher": "Nodeflux",      "type": "fr",      "major_version": 4,      "minor_version": 2,      "free_seats": 3,      "all_seats": 5,      "seats": [        {          "serial_number": "SN-001",          "active_status": true        },        {          "serial_number": "SN-002",          "active_status": false        }      ]    },    {      "id": "NFDSS-LPR",      "name": "License Plate Recognition",      "description": "Pembacaan plat nomor kendaraan",      "publisher": "Nodeflux",      "type": "lpr",      "major_version": 4,      "minor_version": 1,      "free_seats": 2,      "all_seats": 4,      "seats": []    }  ],  "code": 200,  "deployment_key": "dk-abc123",  "hardware_id": "hw-xyz456",  "message": "success",  "version": {    "major": 4,    "minor": 2,    "patch": 1  }}

Daftar Analitik Aktif di Pipeline

GET
/api/visionaire_active_analytic_list

Authorization

bearerAuth
AuthorizationBearer <token>

In: header

Response Body

application/json

curl -X GET "https://lenz.example.com/api/visionaire_active_analytic_list"
{  "ok": true,  "message": "successfully get active analytic list",  "data": {    "analytic_num": 1,    "analytics": [      {        "id": "NFDSS-FR",        "name": "Face Recognition",        "description": "Deteksi dan pengenalan wajah",        "publisher": "Nodeflux",        "type": "fr",        "major_version": 4,        "minor_version": 2,        "free_seats": 3,        "all_seats": 5,        "seats": []      }    ]  }}

Assignment Pipeline

Endpoint untuk men-assign, memperbarui, dan menghapus analitik pada stream internal yang dibuat dari video yang diunggah. Setelah upload selesai, sistem membuat stream internal dengan offline_mode: true — gunakan nodeId dan streamId dari stream tersebut untuk endpoint ini.

Sistem tidak mengizinkan assign analitik baru saat analisis sedang berjalan (running) atau terjadwal (scheduled). Tunggu hingga analisis selesai, atau batalkan jadwal terlebih dahulu sebelum menambah analitik baru.

Detail Pipeline Assignment

GET
/api/visionaire/pipeline/{nodeId}/{streamId}/{analyticId}

Authorization

bearerAuth
AuthorizationBearer <token>

In: header

Path Parameters

nodeId*string

ID node tempat stream berjalan.

streamId*string

ID stream video.

analyticId*string

ID analitik yang ditugaskan.

Query Parameters

instance?string

ID instance pada mode federation (opsional).

Response Body

application/json

curl -X GET "https://lenz.example.com/api/visionaire/pipeline/node-001/stream-uuid-001/NFV4-MPA"
{  "analytic_id": "NFV4-MPA",  "code": 200,  "message": "success",  "node_num": 1,  "stream_id": "stream-uuid-001",  "config": {    "args": {},    "env": {},    "object_confidence_threshold": 0.5,    "sub_analytics": [      {        "name": "counting",        "is_active": true,        "areas": [          {            "name": "Pintu Masuk",            "points": [              {                "x": 0.2,                "y": 0.5              },              {                "x": 0.8,                "y": 0.5              }            ],            "direction": "both"          }        ],        "object_confidence_threshold": 0.5      }    ]  }}

Buat Pipeline Assignment

POST
/api/visionaire/pipeline/{nodeId}/{streamId}/{analyticId}

Authorization

bearerAuth
AuthorizationBearer <token>

In: header

Path Parameters

nodeId*string

ID node tempat stream berjalan.

streamId*string

ID stream video.

analyticId*string

ID analitik yang ditugaskan.

Query Parameters

instance?string

ID instance pada mode federation (opsional).

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

Response Body

application/json

curl -X POST "https://lenz.example.com/api/visionaire/pipeline/node-001/stream-uuid-001/NFV4-MPA" \  -H "Content-Type: application/json" \  -d '{    "sub_analytics": [      {        "name": "counting",        "is_active": true,        "areas": [          {            "name": "Pintu Masuk",            "points": [              {                "x": 0.2,                "y": 0.5              },              {                "x": 0.8,                "y": 0.5              }            ],            "direction": "both"          }        ],        "object_confidence_threshold": 0.5      }    ]  }'
{  "analytic_id": "NFV4-MPA",  "code": 200,  "message": "success",  "node_num": 1,  "stream_id": "stream-uuid-001",  "ok": true}

Perbarui Pipeline Assignment

PUT
/api/visionaire/pipeline/{nodeId}/{streamId}/{analyticId}

Authorization

bearerAuth
AuthorizationBearer <token>

In: header

Path Parameters

nodeId*string

ID node tempat stream berjalan.

streamId*string

ID stream video.

analyticId*string

ID analitik yang ditugaskan.

Query Parameters

instance?string

ID instance pada mode federation (opsional).

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

Response Body

application/json

curl -X PUT "https://lenz.example.com/api/visionaire/pipeline/node-001/stream-uuid-001/NFV4-MPA" \  -H "Content-Type: application/json" \  -d '{    "sub_analytics": [      {        "name": "counting",        "is_active": true,        "areas": [          {            "name": "Pintu Masuk — Revisi",            "points": [              {                "x": 0.1,                "y": 0.5              },              {                "x": 0.9,                "y": 0.5              }            ],            "direction": "in"          }        ],        "object_confidence_threshold": 0.6      }    ]  }'
{  "analytic_id": "NFV4-MPA",  "code": 200,  "message": "success",  "node_num": 1,  "stream_id": "stream-uuid-001",  "ok": true}

Hapus Pipeline Assignment

DELETE
/api/visionaire/pipeline/{nodeId}/{streamId}/{analyticId}

Authorization

bearerAuth
AuthorizationBearer <token>

In: header

Path Parameters

nodeId*string

ID node tempat stream berjalan.

streamId*string

ID stream video.

analyticId*string

ID analitik yang ditugaskan.

Query Parameters

instance?string

ID instance pada mode federation (opsional).

Response Body

application/json

curl -X DELETE "https://lenz.example.com/api/visionaire/pipeline/node-001/stream-uuid-001/NFV4-MPA"
{  "analytic_id": "NFV4-MPA",  "code": 200,  "message": "success",  "node_num": 1,  "stream_id": "stream-uuid-001",  "ok": true}

Konfigurasi Analitik

Endpoint untuk mengambil konfigurasi default dan skema form analitik — digunakan saat membangun UI form konfigurasi pipeline, serta untuk memvalidasi konfigurasi raw JSON sebelum diterapkan.

Konfigurasi Default Analitik

GET
/api/visionaire/analytics/{analyticId}/config

Authorization

bearerAuth
AuthorizationBearer <token>

In: header

Path Parameters

analyticId*string

ID analitik

Response Body

application/json

curl -X GET "https://lenz.example.com/api/visionaire/analytics/NFDSS-FR/config"
{  "analytic_id": "NFDSS-FR",  "configs": [    {      "name": "object_confidence_threshold",      "type": "number",      "label": "Confidence Threshold",      "description": "Ambang batas kepercayaan deteksi (0.0 – 1.0)",      "value": 0.5,      "required": false,      "min": 0,      "max": 1,      "step": 0.01    }  ],  "sub_analytics": [],  "message": "success",  "ok": true}
Empty

Skema Form Konfigurasi Analitik

GET
/api/visionaire/analytics/{analyticId}/config/form

Authorization

bearerAuth
AuthorizationBearer <token>

In: header

Path Parameters

analyticId*string

ID analitik

Response Body

application/json

curl -X GET "https://lenz.example.com/api/visionaire/analytics/NFDSS-FR/config/form"
{  "analytic_id": "NFDSS-FR",  "configs": [    {      "name": "object_confidence_threshold",      "type": "number",      "label": "Confidence Threshold",      "description": "Ambang batas kepercayaan deteksi (0.0 – 1.0)",      "value": 0.5,      "required": false,      "min": 0,      "max": 1,      "step": 0.01    }  ],  "sub_analytics": [    {      "name": "counting",      "display_name": "Counting",      "configs": []    }  ],  "message": "success",  "ok": true}

Validasi Konfigurasi Pipeline (JSON)

POST
/api/visionaire/analytics/config/validate

Authorization

bearerAuth
AuthorizationBearer <token>

In: header

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

Response Body

application/json

application/json

curl -X POST "https://lenz.example.com/api/visionaire/analytics/config/validate" \  -H "Content-Type: application/json" \  -d '{    "analytic_id": "NFDSS-FR",    "config": "{\\"object_confidence_threshold\\": 0.7, \\"areas\\": []}",    "logic": null  }'
{  "valid": true,  "errors": [],  "parsed_config": {    "object_confidence_threshold": 0.7,    "areas": []  },  "message": "configuration is valid",  "ok": true}
{  "valid": false,  "errors": [    "object_confidence_threshold must be between 0 and 1"  ],  "parsed_config": null,  "message": "configuration validation failed",  "ok": false}

On this page