← Modes

Creating a Custom Mode

A Custom Mode lets you plug your own content library into Combust Media. You run a small server that returns JSON, generate an encrypted mode code from this site, and share that code with users — they paste it into the app to unlock your content.

Combust Media never proxies your content: the app talks directly to your server using the URLs in your descriptor. You stay in full control of what you serve and to whom.


Overview

1. Build a server that answers the mode contract (JSON routes)
2. Write a mode descriptor (title, URLs, bearer token, …)
3. Generate an encrypted code here  →  Create a mode
4. Share the code — users add it in the app to unlock your mode

There are four media_type values:

media_type Player Flow Use for
vod_video Video Main → Info → Watch Movies, series
vod_audio Audio Main → Info → Watch Audiobooks, podcasts
live_tv Video Main → Watch Live TV channels
live_radio Audio Main → Watch Live radio stations

Mobile currently ships vod_video. The others are part of the contract and are rolling out.


Step 1 — Build your server

Your server exposes a few JSON endpoints. Every route must require your bearer token (sent as Authorization: Bearer <token> on each request).

Routes for vod_video / vod_audio

Route Required Returns
GET /main Yes Home-screen sections + items
GET /info Yes Detail for one item
GET /watch Yes Playback URL + subtitles
GET /search/[query] No Paginated search results

GET /main

{
  "sections": [
    {
      "title": "Trending Movies",
      "media_items": [
        {
          "id": "movie1",
          "title": "John Wick: Chapter 4",
          "image": "https://img.example.com/3.jpg",
          "type": "movie",
          "release_date": "2023",
          "duration": "120m",
          "info_url": "https://yourserver.com/info?id=movie1"
        }
      ]
    }
  ]
}

Each item's type is movie or series. info_url is called next. Important: every URL you embed (info_url, watch_url) must be reachable from the user's device — use your public host, not localhost.

GET /info

The app calls the item's info_url. episodes is always an array — a single entry for a movie, one per episode for a series.

{
  "id": "tv1",
  "title": "Platonic",
  "cover": "https://img.example.com/cover_1.jpg",
  "image": "https://img.example.com/1.jpg",
  "description": "A long description…",
  "type": "series",
  "release_date": "2023-05-23",
  "duration": "45 min",
  "rating": "9.0",
  "episodes": [
    { "title": "Eps 1: Pilot", "episode": "1", "season": "1", "watch_url": "https://yourserver.com/watch?id=tv1_1" }
  ]
}

For a movie, episodes is [{ "watch_url": "https://yourserver.com/watch?id=movie1" }].

GET /watch

The app calls the watch_url. Return the direct media URL (e.g. an HLS .m3u8) and any subtitles (empty array if none).

{
  "url": "https://yourserver.com/media/file.m3u8",
  "subtitles": [
    { "url": "https://yourserver.com/subs/eng.vtt", "lang": "English" }
  ]
}

GET /search/[query]

Only if you set a search URL. The app appends the URL-encoded query and an optional ?page.

{
  "next_page_url": "https://yourserver.com/search/rick?page=2",
  "prev_page_url": null,
  "media_items": [ /* same shape as /main items */ ]
}

Live routes (live_tv / live_radio)

Live content skips the info step — tapping a channel goes straight to playback. /main returns channels (with logo, watch_url, optional info_url for EPG) instead of media_items, and /watch returns just url.


Step 2 — Write your descriptor

Your descriptor is the set of fields below. You don't write JSON by hand — the mode builder form collects them.

Field Required Notes
title Yes Display name
description Yes What the mode provides
media_type Yes One of the four types
logo_url Yes Square image, ≥200×200
bearer_token Yes Token your server checks on every request
main_screen_url Yes Your /main URL
search_screen_url No Your /search URL

Modes have no developer-settable expiry. Every mode ships locked — access is granted per person with a renewal code (see below).


Step 3 — Generate your code

Open the mode builder, fill in the fields, and click Generate code. You'll get a string that starts with v1:. The descriptor is encrypted server-side with a key that never leaves Combust Media — the raw fields (including your bearer token) are never exposed in the code.


Step 4 — Share it

Give the v1:… code to your users however you like (email, a web page, a private group). In the app they open Custom → Add, paste the code, and your mode appears — but it stays locked until they redeem a renewal code, so pair it with one per user (see below). You can also add codes yourself on the Custom modes page.


Access & renewal codes

A mode code grants no access on its own — every mode expires the moment it's created, so it lands in a user's app locked. Access is granted per person with a renewal code:

  • You mint renewal codes on the renewal codes page. Each carries a grants access until date and is single-use (self-expires if unredeemed).
  • The user redeems a code (XXXX-XXXX) in the app, which sets their expiry to that date.
  • Redeeming again with a later date extends them; mint one code per person.

So distribute the v1:… mode code together with a renewal code for each user. This runs subscription-style, per-person access — different people, different expiry dates, one mode — with no payments handled inside the app.


Good to know

  • Caching: the app caches /main for about 30 minutes; /info and /watch are always fetched fresh, so playback URLs can be short-lived or signed.
  • Security: your bearer token authorizes every request — rotate it (with a new code) if it leaks. The encrypted code is safe to distribute; the underlying token is not embedded in plaintext.
  • Reachability: test that every URL your server returns loads from a phone on mobile data, not just your local network.