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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@goboss/react-native-video-player-sdk

v1.2.21

Published

GoBOSS React Native video player SDK — HLS/DASH/MP4 streaming, DRM, subtitles, chapters, analytics, Chromecast, Android TV.

Readme

@goboss/react-native-video-player-sdk

A full-featured React Native video player SDK supporting HLS, DASH, and MP4 streaming with DRM, subtitles, chapters, analytics, Chromecast, and overlay ads.

One package, two players. Install a single npm package and get both:

  • RNVideoPlayer — touch UI for phones/tablets (gestures, fullscreen, Picture-in-Picture, AirPlay)
  • TVVideoPlayer — D-pad UI for Android TV / Fire TV (remote-control navigation, no touch/PiP)

Both share the same underlying VideoPlayerSDK core (playback, DRM, analytics, ad breaks, subtitles), so behavior and events stay consistent across form factors. There is no separate TV package to install — pick the component at runtime with Platform.isTV.

Installation

npm install @goboss/react-native-video-player-sdk react-native-svg
cd ios && pod install

react-native-svg is a required peer dependency (it renders every control-bar icon). Install it alongside the SDK — most React Native apps already have it, in which case npm dedupes to the copy you have. Chromecast is the only other opt-in package; nothing else needs a separate npm install.

What's included automatically

| Dependency | Why | Install step | |---|---|---| | react-native-video | Core <Video> playback | Auto — regular dependency | | react-native-safe-area-context | Layout insets | Auto — regular dependency | | lucide-react-native | Control-bar icons | Auto — regular dependency | | @react-native-async-storage/async-storage | Resume position, watched-ad memory | Auto — regular dependency | | @react-native-community/netinfo | Network type in analytics | Auto — regular dependency |

All five ship as real dependencies of the SDK, so npm install @goboss/react-native-video-player-sdk pulls them in with no extra commands. Playback, DRM, subtitles, chapters, key moments, ad breaks, resume-position, thumbnail previews, and analytics all work immediately — for both RNVideoPlayer (mobile) and TVVideoPlayer (Android TV).

What you must install: react-native-svg

| | | |---|---| | Package | react-native-svg (>= 15) | | Auto-installed? | No — it's a peer dependency | | Why not? | It's a native module; it must resolve to a single copy in your app. lucide-react-native (the control-bar icons) imports it at module load, so the SDK will not bundle without it. | | What happens if you skip it | Metro fails to resolve react-native-svg when it bundles RNVideoPlayer / TVVideoPlayer. |

Most React Native apps already depend on react-native-svg; if yours does, npm dedupes to your existing copy and there's nothing more to do.

What's opt-in: Chromecast

| | | |---|---| | Package | react-native-google-cast | | Auto-installed? | No | | Why not? | Needs a Cast receiver App ID that belongs to your app — there's no value the SDK could ship that works for every consumer. It also crashes at native launch on Android if its native config is missing, so silently bundling it would risk breaking apps that never asked for Chromecast. | | What happens if you skip it | Player works normally — the Cast button just doesn't render. | | How you'll know it's missing | A console.warn fires the moment you import RNVideoPlayer: "[RNVideoPlayer] react-native-google-cast not installed — Chromecast button hidden." |

To enable it:

npm install react-native-google-cast

Then add a CastOptionsProvider native class and the OPTIONS_PROVIDER_CLASS_NAME meta-data entry to your AndroidManifest.xml — see android/app/src/main/AndroidManifest.xml in this repo for a working reference.

Android TV launcher (optional)

To make the app appear on the Android TV home screen, add to your AndroidManifest.xml:

<uses-feature android:name="android.software.leanback" android:required="false" />
<uses-feature android:name="android.hardware.touchscreen" android:required="false" />

and add <category android:name="android.intent.category.LEANBACK_LAUNCHER" /> to your launcher activity's intent filter. TVVideoPlayer itself works without this — it's only required for TV-store/launcher visibility.


Quick Start

Render RNVideoPlayer on phones/tablets and TVVideoPlayer on Android TV / Fire TV, switching on Platform.isTV:

import React from 'react';
import { View, Platform } from 'react-native';
import { RNVideoPlayer, TVVideoPlayer } from '@goboss/react-native-video-player-sdk';

export default function App() {
  if (Platform.isTV) {
    return (
      <View style={{ flex: 1, backgroundColor: '#000' }}>
        <TVVideoPlayer
          src="https://example.com/stream.m3u8"
          themeColor="#6366f1"
        />
      </View>
    );
  }

  return (
    <View style={{ flex: 1, backgroundColor: '#000' }}>
      <RNVideoPlayer
        src="https://example.com/stream.m3u8"
        themeColor="#6366f1"
      />
    </View>
  );
}

If your app only targets one form factor, just render that one component directly — no need for the Platform.isTV check.


Props

RNVideoPlayer (mobile/tablet)

| Prop | Type | Required | Default | Description | |------|------|----------|---------|-------------| | src | string | Yes | — | Stream URL — HLS (.m3u8), DASH (.mpd), or MP4 | | themeColor | string | No | #6366f1 | Accent color for controls and progress bar | | title | string | No | — | Video title shown in the player | | headers | Record<string, string> | No | — | Custom HTTP headers for stream/segment requests | | chapters | Chapter[] | No | [] | Chapter markers shown on the seekbar | | keyMoments | KeyMoment[] | No | — | Key moment markers (fetched from server and updated via onKeyMomentsLoaded) | | chaptersDisplayMode | 'strip' \| 'panel' | No | 'panel' | Key moments tray layout: bottom strip or right-side panel (default right). Aliases: keyMomentsTray / keyMomentsPosition | | keyMomentsTray | 'bottom' \| 'right' \| 'strip' \| 'panel' \| 'none' \| boolean | No | — | Tray placement; false / 'none' hides the control | | keyMomentsPosition | 'bottom' \| 'right' | No | — | Shorthand for tray placement | | subtitles | SubtitleTrack[] | No | [] | Subtitle/caption tracks | | subtitleStyle | SubtitleStyle | No | {} | Subtitle appearance — size, background, color | | thumbnails | ThumbnailConfig | No | — | Sprite sheet config for thumbnail previews on seek | | drmConfig | object | No | — | Widevine / FairPlay / PlayReady DRM configuration | | watermark | string | No | — | Watermark text overlaid on the video | | playerKey | string | Yes | — | SDK license / package key. Required — playback is blocked with an error screen if it is missing, empty or rejected by the entitlement API | | enableSecureMode | boolean | No | false | Enables FLAG_SECURE on Android to block screenshots and screen recording (no-op on iOS). An entitlement package returning screenRecordingPrevention forces it on regardless | | adTrackingUrl | string | No | — | MediaTailor tracking URL. Polled for avails (linear ad pods — per-ad duration, skip offset, CTA) and, unless overlayAdUrl is set, for non-linear overlay ads too | | overlayAdUrl | string | No | adTrackingUrl | Separate tracking URL for overlay (non-linear) ads only; linear pods always come from adTrackingUrl | | enableSkipAd | boolean | No | false | Fallback skippability for ad breaks whose avails carry no per-ad metadata. When avails supply pod metadata, each ad's own skipOffset wins over this | | skipAdAfterSeconds | number | No | 5 | Seconds before the fallback Skip Ad button activates. Ignored when a per-ad skipOffset from avails applies | | videoId | string | No | derived from src | Stable content ID sent to the analytics API | | watchProgressInterval | number | No | 30 | Interval in seconds for watch-progress analytics events | | onKeyMomentsLoaded | (keyMoments: KeyMoment[]) => void | No | — | Called when key moments are loaded from the server | | onAnalyticsEvent | (event: AnalyticsEvent) => void | No | — | Called for every analytics event the player raises, with the full payload — see Analytics Events | | introStartTime | number | No | — | Content-relative intro start, in seconds. Skip Intro shows between this and introEndTime | | introEndTime | number | No | — | Content-relative intro end, in seconds — Skip Intro seeks here (+0.5s) | | hasNextEpisode | boolean | No | false | Whether another episode is available. Enables the Next Episode control bar button and the outro card | | outroStartTime | number | No | — | Content-relative outro/credits start, in seconds — reveals the auto-advancing Next Episode card once the playhead reaches it | | nextEpisodeLabel | string | No | Next Episode | Label on the Next Episode card and button | | nextEpisodeAutoAdvanceSeconds | number | No | 5 | Seconds the countdown runs before auto-advancing once the outro card is shown | | nextEpisodeThumbnailUrl | string | No | — | Poster of the upcoming episode, stacked above the countdown card | | onNextEpisode | () => void | No | — | Called on Next Episode press/select, or when the auto-advance countdown fires. Swap src (or remount) to load the next episode | | episodes | EpisodeItem[] | No | — | Episodes of the series. Two or more reveals the episodes button in the control bar, which opens the episode tray | | currentEpisodeId | string | No | — | id of the episode playing now — marked "Now Playing" and scrolled/focused to when the tray opens | | seriesTitle | string | No | title | Series name, shown as the tray's eyebrow heading | | onSelectEpisode | (episode: EpisodeItem, index: number) => void | No | — | Called when a tray card is chosen. Required for the button to appear — swap src and currentEpisodeId to play it |

Skip Intro / Next Episode (series)

All timing, the chip/card UI and the seek/auto-advance behaviour live in the SDK — the host only supplies the numbers and an onNextEpisode handler:

<RNVideoPlayer
  playerKey="bsp_..."
  src={episode.streamUrl}
  introStartTime={0}
  introEndTime={78}                 // Skip Intro shows 0s–77.5s, seeks to 78.5s on press
  hasNextEpisode={!!episode.next}
  outroStartTime={1230}             // Next Episode card appears once content time reaches 1230s
  nextEpisodeLabel="S2 · E5"
  nextEpisodeAutoAdvanceSeconds={5}
  nextEpisodeThumbnailUrl={episode.next?.thumbnail}
  onNextEpisode={() => setEpisode(episode.next)}
/>

Times are content-relative (ad durations excluded) — stitched SSAI breaks before the intro/outro don't shift them. The card's auto-advance timer is cancelled if the viewer presses it early, rewinds back before the outro, an ad starts, or an error overlay appears.

Episode tray (series)

Pass the series' episodes and the player adds an episodes button to the control bar. Pressing it opens a browsable tray of episode cards — artwork, S1 E3 badge, runtime, a resume bar and the synopsis — with the episode now playing marked and scrolled to:

<RNVideoPlayer
  playerKey="bsp_..."
  src={episode.streamUrl}
  seriesTitle="The Art of Motion"
  currentEpisodeId={episode.id}
  episodes={[
    { id: 'ep1', seasonNumber: 1, episodeNumber: 1, title: 'First Light',
      description: 'A rider chases the sunrise across the salt flats.',
      thumbnailUrl: 'https://…/ep1.jpg', durationSeconds: 1425, progress: 1 },
    { id: 'ep2', seasonNumber: 1, episodeNumber: 2, title: 'Downhill',
      thumbnailUrl: 'https://…/ep2.jpg', durationSeconds: 1682, progress: 0.62 },
    // …
  ]}
  onSelectEpisode={(ep) => setEpisode(ep)}   // swap `src` + `currentEpisodeId`
/>

Every field but id and title is optional — thumbnailUrl falls back to a placeholder, episodeNumber to the item's position, progress (0–1) draws the resume bar only when set, and seasonNumber turns the E3 badge into S1 E3.

The tray adapts to where it is shown:

  • Mobile — a bottom sheet in portrait, a right-hand drawer once the player is wider than it is tall (landscape / fullscreen). Tapping the scrim or the Android back button closes it; the controls stay up while it is open.
  • TV — a full-width strip of cards above the control bar. Left/right walks the cards, OK plays one, and Back closes the strip and returns focus to the episodes button — the same D-pad grammar as the chapters strip.

The SDK renders the list and nothing more: it never fetches episodes and never changes the source itself. onSelectEpisode is where the host swaps src and currentEpisodeId.

TVVideoPlayer (Android TV / Fire TV)

Same as RNVideoPlayer above, with two differences:

  • No thumbnails prop — seek-preview thumbnails are a touch/scrub interaction and don't apply to D-pad seeking.
  • No fullscreen or Picture-in-Picture callbacks — TV playback is always fullscreen; there's no PiP on Android TV.

Navigation is handled entirely via the D-pad (remote) — play/pause, seek, and menu selection all respond to directional + select key events instead of touch gestures.


Analytics Events

onAnalyticsEvent is the raw event firehose, available on both RNVideoPlayer and TVVideoPlayer. It fires for every event the player raises — playback (play, pause, seek, ended, complete), buffering (buffering_start, buffering_end, stall), engagement (session_start, video_loaded, first_quartile, midpoint, third_quartile, heartbeat, watch_progress, streaming_time), ad breaks (ad_break_start, ad_break_end), control interactions (mute, fast_forward, fast_backward, key_moments, quality_change, speed_change, subtitle_change, audio_track_change, pip_toggle, fullscreen_enter, fullscreen_exit) and errors (error, network_error).

Every event carries the same AnalyticsEvent payload: the raw currentTime/duration, their ad-excluded contentTime/contentDuration equivalents, the session context (sessionId, videoId, deviceType, networkType) and the running counters (watchPercentage, totalWatchTime, contentWatchTime, adWatchTime, streamingTime, bufferingCount, totalBufferingTime), plus any extras specific to the event (seekFrom/seekTo, isMuted, skipSeconds, momentTitle/momentTime, subtitleLanguage/subtitleEnabled, audioTrack, pipState, errorCode/ errorMessage/isRecoverable).

<RNVideoPlayer
  src={STREAM_URL}
  playerKey={PLAYER_KEY}
  onAnalyticsEvent={(event) => {
    myTracker.track(event.eventType, event);
  }}
/>

This is independent of the analytics the SDK reports to GoBOSS on its own — the player always opens a session, heartbeats and posts telemetry using playerKey, whether or not this prop is supplied. Use it for a second, custom pipeline. For logging and business logic prefer the delegate callbacks below, which hand each event only the arguments relevant to it. Throwing from this callback is caught and logged; it never interrupts playback.


Delegate Callbacks

Beyond onKeyMomentsLoaded, both RNVideoPlayer and TVVideoPlayer accept per-event delegate callbacks. Each carries only the arguments relevant to that event, rather than the entire player state. TVVideoPlayer accepts every callback below except the fullscreen and Picture-in-Picture ones, which don't apply on TV.

| Callback | Arguments | Fires when | |----------|-----------|------------| | playerDidStartPlaying | { startTime, autoplay } | Playback starts for the first time | | playerDidResumePlaying | { resumedAt, reason } | Playback resumes after a pause | | playerDidPause | { pausedAt, reason } | Playback is paused | | playerDidFinishPlaying | { finishedAt, watchDuration } | Playback reaches the end | | playerDidDismiss | { dismissedAt, watchDuration } | The player is unmounted/closed | | playerDidStartSeeking | { fromTime } | A seek begins | | playerDidFinishSeeking | { fromTime, toTime, seekDuration } | A seek completes — seekDuration is wall-clock seconds from gesture to landing | | playerDidUpdateCurrentTime | { currentTime, duration } | The playhead position updates | | playerDidStartBuffering | { startedAt } | Buffering starts | | playerDidFinishBuffering | { endedAt } | Buffering ends | | playerDidChangeQuality | { fromQuality, toQuality, isAutoSwitch } | The rendition changes (manual or ABR) | | playerDidChangeSubtitle | { fromSubtitle, toSubtitle } | The subtitle track changes | | playerDidDisableSubtitle | { disabledSubtitle } | Subtitles are turned off | | playerDidChangeAudioTrack | { fromAudioTrack, toAudioTrack } | The audio track changes | | playerDidChangePlaybackSpeed | { fromSpeed, toSpeed } | The playback rate changes | | playerDidSelectKeyMoment | { chapterId, chapterTitle, fromTime, toTime } | A chapter/key moment is selected | | playerDidEncounterPlaybackError | { errorCode, message, isRecoverable } | A non-network playback error occurs | | playerDidEncounterNetworkError | { statusCode, message, isRecoverable } | A network-related error occurs | | playerDidEnterFullscreen (mobile/tablet only) | { orientation? } | The player enters fullscreen | | playerDidExitFullscreen (mobile/tablet only) | { orientation? } | The player exits fullscreen | | playerDidEnterPictureInPicture (mobile/tablet only) | { enteredAt? } | The player enters Picture-in-Picture | | playerDidExitPictureInPicture (mobile/tablet only) | { exitedAt? } | The player exits Picture-in-Picture |

Pause and resume attribution

playerDidPause and playerDidResumePlaying carry a reason so a viewer action can be told apart from something the player or the platform did on its own:

| reason | Pause | Resume | |----------|-------|--------| | 'user' | The viewer used the play/pause control, the TV remote, or the PiP transport button | Same, in the other direction | | 'network' | The connection dropped and the SDK paused to wait it out | Connectivity returned and playback resumed automatically | | 'system' | The platform or the SDK did it unprompted — audio-focus loss, a call or other interruption, backgrounding, or a programmatic pause such as the Next Episode hand-off | A platform-driven restore: returning from PiP or the background, or playback re-asserted after a recovery reload |

'system' is the resting value: a transition that arrives without anything having claimed it is, by definition, not one the SDK asked for.

<RNVideoPlayer
  src="https://example.com/stream.m3u8"
  playerDidStartPlaying={({ startTime, autoplay }) => console.log('started', startTime, autoplay)}
  playerDidPause={({ pausedAt, reason }) => console.log('paused at', pausedAt, 'because', reason)}
  playerDidChangeQuality={({ fromQuality, toQuality, isAutoSwitch }) =>
    console.log(`quality ${fromQuality} -> ${toQuality}`, isAutoSwitch ? '(auto)' : '(manual)')
  }
  playerDidEncounterPlaybackError={({ errorCode, message }) => console.warn(errorCode, message)}
/>

Examples

With Subtitles (External VTT)

Pass external .vtt or .srt files via the subtitles prop. The SDK fetches and parses them in JS and renders cues through the built-in overlay — no native configuration needed.

import { RNVideoPlayer } from '@goboss/react-native-video-player-sdk';
import type { SubtitleTrack } from '@goboss/react-native-video-player-sdk';

const subtitles: SubtitleTrack[] = [
  {
    id: 'en',
    label: 'English',
    lang: 'en',
    src: 'https://cdn.example.com/subtitles/en.vtt',
    isDefault: true,                               // auto-selected on load
  },
  {
    id: 'ta',
    label: 'Tamil',
    lang: 'ta',
    src: 'https://cdn.example.com/subtitles/ta.vtt',
  },
];

<RNVideoPlayer
  src="https://example.com/stream.m3u8"
  subtitles={subtitles}
  subtitleStyle={{ size: 'medium', background: 'dim', color: '#ffffff' }}
/>

SubtitleTrack fields

| Field | Type | Required | Description | |-------|------|----------|-------------| | id | string | Yes | Unique identifier — used to switch the active track | | label | string | Yes | Display name shown in the subtitle menu | | lang | string | Yes | BCP-47 language code (e.g. 'en', 'ta', 'fr') | | src | string | Yes (for external VTT) | URL of the .vtt or .srt file — fetched and parsed by the SDK | | absoluteSrc | string | No | Direct CDN URL for Chromecast (without proxy/token rewrites) — required only if you use Chromecast | | isDefault | boolean | No | Auto-selects this track when the player loads | | cues | Array<{startTime, endTime, text}> | No | Pre-parsed cues — skip the fetch step if you already have them |

Subtitle sources — how they work

| Source | How to use | Rendered by | |--------|-----------|-------------| | External VTT/SRT | Pass src URL in the subtitles prop | JS overlay (SDK) | | HLS embedded (#EXT-X-MEDIA:TYPE=SUBTITLES) | Nothing — SDK auto-discovers from manifest | JS overlay (SDK) | | CEA-608/708 closed captions | Nothing — SDK auto-discovers via onTextTracks | Native (ExoPlayer/AVPlayer) |

subtitleStyle options

| Field | Values | Default | Description | |-------|--------|---------|-------------| | size | 'small' | 'medium' | 'large' | 'medium' | Subtitle text size | | background | 'none' | 'dim' | 'solid' | 'dim' | Background behind subtitle text | | color | CSS color string | '#ffffff' | Subtitle text color |

With Chapters

import type { Chapter } from '@goboss/react-native-video-player-sdk';

const chapters: Chapter[] = [
  { title: 'Intro', startTime: 0, endTime: 30 },
  { title: 'Act 1', startTime: 30, endTime: 300 },
];

<RNVideoPlayer
  src="https://example.com/stream.m3u8"
  chapters={chapters}
  themeColor="#e11d48"
/>

With DRM (Widevine / FairPlay)

<RNVideoPlayer
  src="https://example.com/protected-stream.m3u8"
  drmConfig={{
    widevineLicenseUrl: 'https://license.example.com/widevine',
    fairplayLicenseUrl: 'https://license.example.com/fairplay',
    authToken: 'Bearer your-token',
  }}
/>

With Thumbnail Previews

<RNVideoPlayer
  src="https://example.com/stream.m3u8"
  thumbnails={{
    spriteUrl: 'https://example.com/thumbnails/sprite.jpg',
    tileWidth: 160,
    tileHeight: 90,
    columns: 10,
    rows: 10,
    interval: 10,
    totalCount: 100,
  }}
/>

With Custom Headers

<RNVideoPlayer
  src="https://example.com/stream.m3u8"
  headers={{
    Authorization: 'Bearer your-token',
    Referer: 'https://yourapp.com',
  }}
/>

Supported Formats

| Format | Extension | Notes | |--------|-----------|-------| | HLS | .m3u8 | Adaptive bitrate, live streams, DVR | | MPEG-DASH | .mpd | Adaptive bitrate | | MP4 | .mp4 | Progressive download |


Features

  • Adaptive streaming — HLS & DASH with automatic quality switching
  • DRM — Widevine (Android), FairPlay (iOS), PlayReady
  • Subtitles — VTT, SRT, HLS embedded tracks, CEA-608/708 closed captions
  • Chapters — seekbar markers with titles
  • Thumbnails — sprite-sheet preview on seek
  • Chromecast — cast to Google Cast devices (requires react-native-google-cast)
  • AirPlay — streams over AirPlay out of the box (iOS Control Center); an in-app route-picker button needs a small native module (see scripts/consumer-app-setup/AIRPLAY-SETUP.md)
  • Picture-in-Picture — native PiP on Android & iOS, from the in-player PiP button beside AirPlay/Cast or by backgrounding the app; the Android button needs the native files in scripts/consumer-app-setup/ (see PIP-SETUP.md)
  • Overlay Ads — AWS MediaTailor non-linear overlay ads
  • Analytics — built-in event tracking delivered via your own analyticsCallback / API endpoint; Android HLS also tags sid/vid on .ts segment URLs via HlsManifestInterceptor (see scripts/consumer-app-setup/HLS-SETUP.md)
  • Live streams — DVR scrubbing, live-edge seek

Requirements

  • React Native >= 0.74
  • iOS 13+
  • Android API 21+
  • Android TV / Fire TV — same Android API 21+ baseline, via TVVideoPlayer