npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@harmonicinc/vos_roku_rafx_ssai

v0.0.19

Published

Harmonic VOS RAFX SSAI adapter for Roku apps

Readme

Harmonic RAFX SSAI adapter

Installation

Native BrightScript only

  1. Download latest release transpiled in BrightScript: hlit-rafx-ssai-brs.zip

  2. Copy all files inside /source to <your_app_root>/source/hlit-rafx-ssai/

    Dependencies are included under /source/roku_modules, so there's no need to install them separately.

    You should get something like this:

    └── source
        ├── hlit_rafx_ssai
        │   ├── DashParser.brs
        │   ├── MetadataParser.brs
        │   ├── PodHelper.brs
        │   ├── SSAI.brs
        │   └── roku_modules
        │       ├── bslib
        │       │   └── bslib.brs
        │       └── rokurequests
        │           └── Requests.brs
        └── main.brs
  3. Create a new task component that is responsible for player control & client-side ad tracking. You may use existing ones if you already have one in your app.

    For example, in Tasks folder in the example app, we have a dedicated PlayerTask to handle player-related functions

  4. Import required libraries

    In your task component XML, import required libraries.

     <script type="text/brightscript" uri="pkg:/source/hlit_rafx_ssai/SSAI.brs" />
     <script type="text/brightscript" uri="pkg:/source/hlit_rafx_ssai/PodHelper.brs" />
     <script type="text/brightscript" uri="pkg:/source/hlit_rafx_ssai/MetadataParser.brs" />
     <script type="text/brightscript" uri="pkg:/source/hlit_rafx_ssai/DashParser.brs" />
     <script type="text/brightscript" uri="pkg:/source/hlit_rafx_ssai/roku_modules/rokurequests/Requests.brs" />
     <script type="text/brightscript" uri="pkg:/source/hlit_rafx_ssai/roku_modules/bslib/bslib.brs" />

    Import Roku RAF library in your BrightScript file

    library "Roku_Ads.brs"

Using ropm & BrighterScript

  1. ropm package manager is supported. Install it by

    ropm install @harmonicinc/vos_roku_rafx_ssai
  2. Create a new task component that is responsible for player control & client-side ad tracking. You may use existing ones if you already have one in your app.

    For example, in Tasks folder in the example app, we have a dedicated PlayerTask.bs to handle player-related functions

  3. Import required libraries in your BrighterScript file

    import "pkg:/source/roku_modules/harmonicinc_vos_roku_rafx_ssai/ssai.bs"
    library "Roku_Ads.brs"

Usage

  1. Create Harmonic RAFX adapter by

    adapter = new harmonic.rafx.ssai.RAFX_SSAI()
    
    ' If you're using native BrightScript:
    adapter = harmonicinc_vos_roku_rafx_ssai_RAFX_SSAI()
  2. Provide the stream URL to the adapter

    result = adapter.getStreamInfo(m.top.video.content.url)
  3. (Optional) Provide the option object.

    initRequest

    Whether to send a request first to the session init API to initialise a session.

    By default, this is true if the options object is not provided. You may set initRequest to false so that the adapter will obtain the manifest directly.

    Note

    By setting the initRequest to true, you may omit the sessid query param in the URL provided to the adapter, and let the SSAI service generate a session ID for you.

    podRetentionSec

    How long to cache the ad metadata in seconds.

    By default, this will be 7200 (2 hours). You may set podRetentionSec to other values.

    pingIntervalSec

    The interval for metadata polling in seconds.

    By default, this will be 4 seconds. You may set pingIntervalSec to other values to control how frequently the adapter checks for new ad metadata.

    Example usage of options

    options = {
       initRequest: false,
       podRetentionSec: 3600,
       pingIntervalSec: 5
    }
    result = adapter.getStreamInfo(m.top.video.content.url, options)
  4. Check if result.ssai is true. If not, the stream is not compatible with the adapter

  5. Use the returned stream URL to play the video

    m.top.video.content.url = result.streamUrl

    See PlayerTask in the example app for reference

    The returned URL is different from the original stream URL. If the returned URL is not used, the SSAI and ad beacons may misalign with the video playback, or beacons will not be fired at all.

  6. (Optional) Add event listeners. Available events include pods and errors:

    adapter.addEventListener(adapter.AdEvent.PODS, podsCallback)
    adapter.addEventListener(adapter.AdEvent.ERROR, errorCallback)
    
    sub podsCallback(event as object)
        ' Your code here. Get pods by event.adPods
    end sub
    
    sub errorCallback(errorInfo as object)
        ' Handle errors: errorInfo.errorType, errorInfo.message, errorInfo.details
    end sub

    Available Error Types:

    • InitRequestFailed - Session initialization request failed
    • ManifestRequestFailed - Manifest request failed
    • MetadataRequestFailed - Ad metadata request failed
    • InvalidResponseFormat - Response format parsing failed
    • ParsingError - Metadata parsing error
    • InvalidStreamUrl - Stream URL construction failed
    • LateBeaconFailed - Late beacon firing failed
  7. Create message port and add it to the adapter.

    Note that the adapter currently does not support custom ad beacon firing at the time of writing. The adapter will handle all the tracking beacons by itself.

    port = CreateObject("roMessagePort")
    adapter.enableAds({
         player: {
             sgNode: <your video node>,
             port: port
         },
         useStitched: true ' required as firing event on client is not supported yet
     })
  8. Observe position field and create a message loop to feed it to the adapter

    m.top.video.observeFieldScoped("position", port)
     m.top.video.observeFieldScoped("control", port)
     m.top.video.observeFieldScoped("state", port)
    
     ' Play video
     m.top.video.control = "play"
        
     while true
         msg = wait(1000, port)
         if type(msg) = "roSGNodeEvent" and msg.getField() = "control" and msg.getNode() = m.top.video.id and msg.getData() = "stop" or m.top.video = invalid
             exit while
         end if
            
         curAd = adapter.onMessage(msg)
         if curAd = invalid
             m.top.video.setFocus(true)
         end if
    
         if "roSGNodeEvent" = type(msg) and "state" = msg.getField() and "finished" = msg.getData() and msg.getNode() = m.top.video.id
             exit while
         end if
     end while
    
     m.top.video.unobserveFieldScoped("position")
     m.top.video.unobserveFieldScoped("control")
     m.top.video.unobserveFieldScoped("state")

Development

Example app tryout

Example app is included in /demo for reference. Demo depends on local SDK instead of the one on npm.

ropm packager is required. Install it globally by

npm i ropm -g

You'll need to install the dependencies of the SDK first by

cd lib
npm i
ropm i

Move on to the demo app, do the same by

cd ../demo
npm i
ropm i

Build the app

Since the manifest URL is hardcoded, you'll need to replace it prior to building the app.

In demo/components/Tasks/PlayerTask.bs line 3, you should have the following:

const url = ""

Replace it with the manifest URL, for example

const url = "https://www.example.com/master.mpd"

Optional

To force the adapter to obtain the manifest directly instead of using the session init API, set the following in lines 4-6:

const options = {
   initRequest: false
}

Then locate to the demo app root directory i.e. demo/. Run the following:

npm run package

The Roku app package will be saved as /demo/out/demo.zip. Upload this zip file in Roku debug web UI to install it.

Develop on Roku w/ live reload

Edit demo/bsconfig.json. Fill in the host and password with the Roku device's IP and developer mode password respectively

Then run the following

npm run watch

Appendix

How the Playback URL and Beaconing URL are Obtained by the Library

[!NOTE]
Applicable when initRequest in the options provided is true (default is true).

  1. The library sends a GET request to the manifest endpoint with the query param "initSession=true". For e.g., a request is sent to:

    https://my-host/variant/v1/hls/index.m3u8?initSession=true
  2. The ad insertion service (PMM) responds with the URLs. For e.g.,

    {
        "manifestUrl": "./index.m3u8?sessid=a700d638-a4e8-49cd-b288-6809bd35a3ed&vosad_inst_id=pmm-0",
        "trackingUrl": "./metadata?sessid=a700d638-a4e8-49cd-b288-6809bd35a3ed&vosad_inst_id=pmm-0"
    }
  3. The library constructs the URLs by combining the host and base path in the original URL and the relative URLs obtained. For e.g.,

    Manifest URL: https://my-host/variant/v1/hls/index.m3u8?sessid=a700d638-a4e8-49cd-b288-6809bd35a3ed&vosad_inst_id=pmm-0
    
    Metadata URL: https://my-host/variant/v1/hls/metadata?sessid=a700d638-a4e8-49cd-b288-6809bd35a3ed&vosad_inst_id=pmm-0

[!NOTE]
The resulting manifest URL above can be obtained in the returned result's streamUrl when calling getStreamInfo.