Skip to content

Streaming API

Play media files from the Local Media Store, start HLS transcode sessions, and save playback progress.

Try it with a token

Start with the Authentication login flow, then use that bearer token in the requests below.

Direct file streaming

http
GET /api/stream/file?path=/data/movies/Inception%20(2010)/Inception.mkv
Authorization: Bearer <token>
Range: bytes=0-
bash
curl -i \
  -H "Authorization: Bearer $OMNILUX_TOKEN" \
  -H "Range: bytes=0-" \
  --get \
  --data-urlencode "path=/data/movies/Inception (2010)/Inception.mkv" \
  "http://your-server:4000/api/stream/file"

The path query value must point to a validated media file inside an allowed library root. The endpoint supports HTTP range requests and returns either 200 OK or 206 Partial Content.

Error example:

json
{
  "error": "Path outside allowed media roots",
  "code": "FORBIDDEN"
}

Start an HLS transcode session

http
POST /api/stream/transcode/start
Authorization: Bearer <token>
Content-Type: application/json

{
  "mediaPath": "/data/movies/Inception (2010)/Inception.mkv",
  "audioStreamIndex": 1,
  "startPositionSeconds": 120,
  "profile": {
    "resolution": "1080p"
  },
  "subtitleMode": "embedded",
  "subtitleStreamIndex": 2,
  "forceTranscode": true
}
bash
curl -sS \
  -X POST \
  -H "Authorization: Bearer $OMNILUX_TOKEN" \
  -H "Content-Type: application/json" \
  --data @- \
  "http://your-server:4000/api/stream/transcode/start" <<'EOF'
{
  "mediaPath": "/data/movies/Inception (2010)/Inception.mkv",
  "audioStreamIndex": 1,
  "startPositionSeconds": 120,
  "profile": { "resolution": "1080p" },
  "subtitleMode": "embedded",
  "subtitleStreamIndex": 2,
  "forceTranscode": true
}
EOF

Response:

json
{
  "sessionId": "stream_01HV6Y7J6P46B8Q4K6XK5J9A6A",
  "playbackMode": "transcode",
  "playlistUrl": "/api/stream/transcode/stream_01HV6Y7J6P46B8Q4K6XK5J9A6A/index.m3u8"
}

When the runtime decides the file can be played directly, the response can be:

json
{
  "sessionId": null,
  "playbackMode": "direct",
  "playlistUrl": null
}

HLS playlist and session assets

http
GET /api/stream/transcode/:sessionId/index.m3u8
Authorization: Bearer <token>

The playlist endpoint returns an HLS manifest when the session is ready. While FFmpeg is still preparing the playlist, it can return:

json
{
  "status": "preparing"
}

Players then request segment and subtitle assets under the same session path:

http
GET /api/stream/transcode/:sessionId/:asset
Authorization: Bearer <token>

Stop a session when the player is done:

http
DELETE /api/stream/transcode/:sessionId
Authorization: Bearer <token>

Transcode status

http
GET /api/stream/transcode/:sessionId/status
Authorization: Bearer <token>
json
{
  "ready": true,
  "progress": 100,
  "error": null,
  "mode": "transcode",
  "diagnostics": {
    "encoder": "libx264"
  }
}

Report watch progress

http
POST /api/stream/progress
Authorization: Bearer <token>
Content-Type: application/json

{
  "mediaPath": "/data/movies/Inception (2010)/Inception.mkv",
  "mediaType": "video",
  "title": "Inception",
  "positionSeconds": 1234,
  "durationSeconds": 8880,
  "completed": false,
  "player": "web"
}
bash
curl -sS \
  -X POST \
  -H "Authorization: Bearer $OMNILUX_TOKEN" \
  -H "Content-Type: application/json" \
  --data @- \
  "http://your-server:4000/api/stream/progress" <<'EOF'
{
  "mediaPath": "/data/movies/Inception (2010)/Inception.mkv",
  "mediaType": "video",
  "title": "Inception",
  "positionSeconds": 1234,
  "durationSeconds": 8880,
  "completed": false,
  "player": "web"
}
EOF

The response is the saved watch-progress record.

Read or reset progress for a file:

http
GET /api/stream/progress?path=/data/movies/Inception%20(2010)/Inception.mkv
DELETE /api/stream/progress?path=/data/movies/Inception%20(2010)/Inception.mkv
Authorization: Bearer <token>

Mark a title watched:

http
POST /api/stream/progress/watched
Authorization: Bearer <token>
Content-Type: application/json

{
  "mediaPath": "/data/movies/Inception (2010)/Inception.mkv",
  "mediaType": "video",
  "title": "Inception",
  "durationSeconds": 8880
}

Fetch recently watched items:

http
GET /api/stream/history?limit=20
Authorization: Bearer <token>

Audiobook progress

Audiobook playback has a chapter-aware progress endpoint:

http
POST /api/stream/audiobook/progress
Authorization: Bearer <token>
Content-Type: application/json

{
  "audiobookPath": "/data/audiobooks/Dune/Dune.m4b",
  "title": "Dune",
  "chapterIndex": 3,
  "chapterPositionSeconds": 90,
  "positionSeconds": 5400,
  "durationSeconds": 72000,
  "progressPercentage": 7.5,
  "playbackRate": 1.25,
  "completed": false
}

The response is:

json
{
  "progress": {
    "audiobookPath": "/data/audiobooks/Dune/Dune.m4b",
    "positionSeconds": 5400,
    "durationSeconds": 72000,
    "progressPercentage": 7.5,
    "completed": false
  }
}

Media info, tracks, and subtitles

Use media info endpoints before choosing a playback path:

http
GET /api/stream/info/full?path=/data/movies/Inception%20(2010)/Inception.mkv
Authorization: Bearer <token>

The response includes probe data, stream tracks, subtitle options, watch progress, and playback capability hints used by the bundled web player.

V2 worker capabilities

http
GET /api/stream/v2-workers
Authorization: Bearer <token>

Returns the current V2 transcoding worker capability view. This is diagnostic metadata only; native workers remain optional and local FFmpeg stays the baseline fallback.

json
{
  "local": {
    "kind": "transcoding",
    "boundary": "ffmpeg-subprocess",
    "status": "available",
    "protocol": { "name": "omnilux-runtime-transcoding", "version": "2.0-draft" },
    "supportedEncoders": ["libx264", "h264_nvenc"],
    "maxConcurrentJobs": 1,
    "activeJobCount": 0,
    "fallback": {
      "mode": "ffmpeg-subprocess",
      "reason": "WORKER_FALLBACK_USED",
      "userMessage": "Local FFmpeg is available as the baseline transcoding path."
    }
  },
  "nodes": [],
  "workers": []
}

If FFmpeg is missing, local.status is unsupported and local.fallback.mode is unsupported-state.