Roku SDK

If you host video on open.video and want it inside your own Roku channel, the Open Video Roku SDK gives you content discovery, stream playback, ads (preroll and midroll through Roku's Ad Framework) and viewing analytics without building any of it. You keep the app, the Channel Store listing, the layout and the Video node; the SDK runs everything behind it.

Status: pilot. The SDK is built and hosted, and the first partner integrations are in progress. The configuration keys and component contracts on these pages are stable.

What you get

Component What it does
OpenVideo:OpenVideoCatalog Lists your channel's videos (featured, latest, most viewed, most liked), playlists and their contents, search, and per-video details. See Catalog.
OpenVideo:OpenVideoAdController Given a video id and your Video node: resolves the stream, runs the ad break plan, plays content, fires view analytics and keeps resume bookmarks. See Ad controller.

Both are headless SceneGraph components. They never take focus and never draw UI, except an optional full-screen backdrop during ad breaks (you can supply your own).

Requirements

  • Roku OS 9.4 or newer (rsg_version=1.3).
  • Your channel manifest must include bs_libs_required=roku_ads_lib. The SDK renders ads through RAF, which Roku loads for the host channel, not for the library.
  • Your content is hosted on open.video under a channel slug.

How it loads

The SDK is a Roku ComponentLibrary, fetched at runtime from a versioned, immutable URL. Pin one version per release of your channel:

https://sdk.open.video/roku/v0.1.4/OpenVideoRokuSDK.zip

A new SDK release never changes an existing URL, so a release you didn't opt into can't break your channel. Moving to a newer SDK is a one-line change and a normal Channel Store resubmission.

The newest release is always described at https://sdk.open.video/roku/latest.json ({ "version", "url", "sha256" }), and https://sdk.open.video/roku/latest/OpenVideoRokuSDK.zip serves it. Use latest for sideloaded development builds only. A published channel must pin a version: latest can change under a certified channel with no resubmission and no way for you to roll back.

Ads play client-side through RAF by default, which is the path where Google connected-TV demand can fill. Server-stitched ad breaks are opt-in (ssaiStitchPlayback: true, see Configuration).

Quickstart

The complete reference channel is in the SDK repository under examples/minimal-host/, about 50 lines of SDK-facing BrightScript. The essentials:

1. Load the library once, at scene init:

library = CreateObject("roSGNode", "ComponentLibrary")
library.id = "OpenVideo"                 ' becomes the component prefix
library.uri = m.cfg.sdkLibraryUrl
library.ObserveField("loadStatus", "onSdkLoadStatusChanged")
m.top.AppendChild(library)

Wait for loadStatus = "ready" before creating any OpenVideo:* node. Handle "failed" with a retry: a TV with no network at launch is common.

2. Discover content:

m.catalog = CreateObject("roSGNode", "OpenVideo:OpenVideoCatalog")
m.catalog.config = m.cfg
m.catalog.ObserveField("result", "onCatalogResult")
m.catalog.callFunc("listVideos", { category: "featured", page: 1 })

Every catalog call returns a requestId and delivers on the result field: { requestId, op, page, items, hasMore, error }. Items carry id, title, description, posterUrl, durationSeconds and channel fields, enough to render a grid.

3. Play:

m.controller = CreateObject("roSGNode", "OpenVideo:OpenVideoAdController")
m.controller.player = m.video            ' your own <Video enableUI="false">
m.controller.adFacade = m.adFacade       ' optional full-screen Group for ads
m.controller.config = m.cfg
m.controller.ObserveField("state", "onControllerState")
m.controller.ObserveField("error", "onControllerError")
m.controller.callFunc("startSession")   ' once per app launch

' on grid select:
m.controller.callFunc("play", { videoId: item.id, metadata: item, isAutoplay: false })

Transport is yours: forward back to stopPlayback({ reason: "user" }), OK/play to togglePause(), and left/right to seekRelative(-10) / seekRelative(10). Keep focus on your scene while playing so remote keys reach onKeyEvent.

state moves through resolving, then adBreakStart/adBreakEnd, contentStart, playing/paused/buffering, finished, stopped. When you see finished, call stopPlayback({ reason: "finished" }) (or "endcard" if you show an up-next card first) and return to your UI on stopped. A fatal error emits state = "error" and then "stopped", so one stopped handler covers every exit.

Next steps

Not yet available

  • A drop-in player with default controls. The controller is headless; you draw the transport.
  • Publisher-hosted HLS. Content must be hosted on open.video.