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

expo-bb-player

v0.1.1

Published

React Native implementation of the Blue Billywig Native Player SDK

Downloads

11

Readme

expo-bb-player

React Native implementation of the Blue Billywig Outstream Native Player for Expo and bare React Native apps.

Installation

For Expo Managed Projects

If you're using an Expo managed project (created with create-expo-app), simply install the package:

npx expo install expo-bb-player

Then run prebuild to generate native directories:

# For iOS
npx expo prebuild -p ios

# For Android
npx expo prebuild -p android

For Bare React Native Projects

Prerequisites

This library is an Expo module, so you need to have Expo modules installed in your project first. If you haven't installed Expo modules yet, follow these steps:

  1. Automatic Setup (Recommended):
npx install-expo-modules@latest

This command will automatically configure your project to use Expo modules. For more details, see the official Expo documentation.

  1. Manual Setup:

If the automatic installation fails, follow the manual installation guide in the Expo documentation.

Installing expo-bb-player

Once Expo modules are installed in your project:

  1. Install the package:
npx expo install expo-bb-player
# or
npm install expo-bb-player
  1. For iOS, install CocoaPods dependencies:
npx pod-install
  1. For Android, the dependencies will be automatically linked through autolinking.

  2. Rebuild your app:

# For iOS
npx expo run:ios
# or
npx react-native run-ios

# For Android
npx expo run:android
# or
npx react-native run-android

Basic Usage

Here's a simple example of how to use the Blue Billywig Player:

import React, { useRef } from 'react';
import { View, StyleSheet, Button } from 'react-native';
import { ExpoBBPlayerView, type ExpoBBPlayerViewType } from 'expo-bb-player';

export default function App() {
  const playerRef = useRef<ExpoBBPlayerViewType>(null);

  return (
    <View style={styles.container}>
      <ExpoBBPlayerView
        ref={playerRef}
        jsonUrl="YOUR_BLUE_BILLYWIG_JSON_URL"
        options={{
          autoPlay: false,
          allowCollapseExpand: true,
        }}
        onDidTriggerPlay={() => {
          console.log('Player started playing');
        }}
        onDidTriggerPause={() => {
          console.log('Player paused');
        }}
      />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
  },
});

Advanced Example

Here's a more advanced example with event handlers and player controls:

import React, { useRef, useState } from 'react';
import { View, StyleSheet, Button, Text } from 'react-native';
import { 
  ExpoBBPlayerView, 
  type ExpoBBPlayerViewType,
  type State,
  type Phase 
} from 'expo-bb-player';

export default function AdvancedPlayer() {
  const playerRef = useRef<ExpoBBPlayerViewType>(null);
  const [playerState, setPlayerState] = useState<State>('IDLE');
  const [playerPhase, setPlayerPhase] = useState<Phase>('INIT');
  const [duration, setDuration] = useState(0);

  const handleEnterFullscreen = () => {
    playerRef.current?.enterFullscreen();
  };

  const handleSeek = (position: number) => {
    playerRef.current?.seek(position);
  };

  const handleSetVolume = (volume: number) => {
    playerRef.current?.setVolume(volume);
  };

  return (
    <View style={styles.container}>
      <ExpoBBPlayerView
        ref={playerRef}
        jsonUrl="YOUR_BLUE_BILLYWIG_JSON_URL"
        options={{
          autoPlay: true,
          autoMute: false,
          allowCollapseExpand: true,
        }}
        onDidTriggerStateChange={(state) => {
          setPlayerState(state);
          console.log('State changed:', state);
        }}
        onDidTriggerPhaseChange={(phase) => {
          setPlayerPhase(phase);
          console.log('Phase changed:', phase);
        }}
        onDidTriggerDurationChange={(duration) => {
          setDuration(duration);
          console.log('Duration:', duration);
        }}
        onDidTriggerProjectLoaded={(project) => {
          console.log('Project loaded:', project);
        }}
        onDidTriggerMediaClipLoaded={(mediaClip) => {
          console.log('Media clip loaded:', mediaClip);
        }}
        onDidFailWithError={(error) => {
          console.error('Player error:', error);
        }}
        onDidTriggerAdStarted={() => {
          console.log('Ad started');
        }}
        onDidTriggerAdFinished={() => {
          console.log('Ad finished');
        }}
      />
      <View style={styles.info}>
        <Text>State: {playerState}</Text>
        <Text>Phase: {playerPhase}</Text>
        <Text>Duration: {duration.toFixed(2)}s</Text>
      </View>
      <View style={styles.controls}>
        <Button title="Fullscreen" onPress={handleEnterFullscreen} />
        <Button title="Seek to 10s" onPress={() => handleSeek(10)} />
        <Button title="Volume 50%" onPress={() => handleSetVolume(0.5)} />
      </View>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
  },
  info: {
    padding: 20,
    backgroundColor: '#f0f0f0',
  },
  controls: {
    flexDirection: 'row',
    justifyContent: 'space-around',
    padding: 20,
  },
});

API Reference

Props

| Prop | Type | Description | |------|------|-------------| | jsonUrl | string | The Blue Billywig JSON URL for the media | | options | Record<string, unknown> | Player configuration options passed through to the native SDK | | style | ViewStyle | Styling for the player container | | Event props | See Events | Subscribe to native player events (see Events section below) |

Player Options

Common options you can pass to the options prop:

{
  autoPlay: boolean;           // Auto-play the video
  allowCollapseExpand: boolean; // Allow collapse/expand functionality
  autoLoop: boolean;            // Loop the video

}

Methods

Access these methods via the player ref:

// Playback controls
play(): Promise<void>
pause(): Promise<void>
seek(position: number): Promise<void>

// Volume controls
setVolume(volume: number): Promise<void>  // 0.0 - 1.0
setMuted(muted: boolean): Promise<void>

// Fullscreen controls
// Android: implemented. iOS: currently no-op.
enterFullscreen(): Promise<void>
exitFullscreen(): Promise<void>

// Expand/Collapse controls (outstream sizing)
expand(): Promise<void>
collapse(): Promise<void>

// Ad/clip data
adMediaHeight(): Promise<number | undefined>
adMediaWidth(): Promise<number | undefined>
adMediaClip(): Promise<MediaClip | undefined>
autoPlayNextCancel(): Promise<void>

// State getters
playerState(): Promise<State>
phase(): Promise<Phase>
duration(): Promise<number | undefined>
volume(): Promise<number | undefined>
muted(): Promise<boolean | undefined>
inView(): Promise<boolean | undefined>
mode(): Promise<string | undefined>
controls(): Promise<boolean | undefined> // Android only; iOS returns undefined
playoutData(): Promise<Playout | undefined>
projectData(): Promise<Project | undefined>

Platform notes:

  • iOS: enterFullscreen and exitFullscreen are currently not supported.
  • iOS: controls() is not exposed by the native SDK and will return undefined.

Events

All events below are exposed as optional props on ExpoBBPlayerView. Payload types are indicated in parentheses.

| Event | Payload | Description | |-------|---------|-------------| | onDidFailWithError | string | A fatal or setup error occurred | | onDidRequestCollapse | - | Player requested to collapse (outstream) | | onDidRequestExpand | - | Player requested to expand (outstream) | | onDidRequestOpenUrl | string | Player requested to open a URL | | onDidSetupWithJsonUrl | string | Player finished initial setup with the provided JSON URL | | onDidTriggerCanPlay | - | Player is ready; API getters should now be available | | onDidTriggerPlay | - | Playback started | | onDidTriggerPause | - | Playback paused | | onDidTriggerPlaying | - | Playback is progressing | | onDidTriggerEnded | - | Playback ended | | onDidTriggerStateChange | State | Player state changed | | onDidTriggerPhaseChange | Phase | Player phase changed | | onDidTriggerDurationChange | number | Media duration changed | | onDidTriggerVolumeChange | number | Volume changed | | onDidTriggerSeeked | number | Seek completed to the provided position (seconds) | | onDidTriggerSeeking | - | Seek started | | onDidTriggerFullscreen | - | Entered fullscreen | | onDidTriggerRetractFullscreen | - | Exited fullscreen | | onDidTriggerModeChange | string | Player mode changed | | onDidTriggerProjectLoaded | Project | Project loaded | | onDidTriggerMediaClipLoaded | MediaClip | Media clip loaded | | onDidTriggerMediaClipFailed | - | Media clip failed to load | | onDidTriggerStall | - | Playback stalled | | onDidTriggerCustomStatistics | { ident: string; ev: string; aux: Record<string,string>; } | Custom statistics event | | onDidTriggerAutoPause | Android: string (why) · iOS: void | Auto-pause was triggered | | onDidTriggerAutoPausePlay | Android: string (why) · iOS: void | Auto-pause resume was triggered | | onDidTriggerAdLoadStart | - | Ad load started | | onDidTriggerAdLoaded | - | Ad loaded | | onDidTriggerAdStarted | - | Ad started | | onDidTriggerAdQuartile1 | - | Ad reached 25% | | onDidTriggerAdQuartile2 | - | Ad reached 50% | | onDidTriggerAdQuartile3 | - | Ad reached 75% | | onDidTriggerAdFinished | - | Ad finished | | onDidTriggerAdNotFound | - | No ad was available | | onDidTriggerAdError | string | Ad-related error | | onDidTriggerAllAdsCompleted | - | All ads completed | | onDidTriggerViewStarted | - | View tracking started | | onDidTriggerViewFinished | - | View tracking finished |

Platform notes:

  • Android and iOS emit the full set of events above. On iOS, onDidTriggerAutoPause and onDidTriggerAutoPausePlay do not include the why payload, while on Android the why string is provided.
  • Some events are emitted only when relevant (e.g., ad-related events only during ad playback).

Types

type State = "IDLE" | "LOADING" | "PLAYING" | "PAUSED" | "ERROR";
type Phase = "INIT" | "PRE" | "MAIN" | "POST" | "EXIT";
// Selected payload types used in events and getters
type CustomStatistics = { ident: string; ev: string; aux: Record<string, string> };
type Project = { /* see src/types.ts for full shape */ };
type MediaClip = { /* see src/types.ts for full shape */ };
type Playout = { /* see src/types.ts for full shape */ };

SDK Version Management

This package automatically uses the latest compatible version of the Blue Billywig native SDKs:

  • iOS: Version ~>8.30
  • Android: Version range [8.30,9.0)

Automatic Updates

When you run pod install (iOS) or build your Android app, the build system will automatically resolve to the latest available version within these constraints. This ensures you benefit from bug fixes and improvements in the native SDKs without manual intervention.

iOS: Uses CocoaPods pessimistic versioning (~>8.30), which allows any version from 8.30 up to (but not including) 9.0.

Android: Uses Gradle range notation ([8.30,9.0)), which allows any version from 8.30 (inclusive) to 9.0 (exclusive).

Version Constraints

The minimum version constraint of 8.30 ensures users have access to the latest features and critical fixes. When we release version 9.0, we will update this package to support it.

For Package Maintainers

The lockfiles (Podfile.lock and Gemfile.lock) are intentionally not committed to version control. This ensures that:

  1. End users always get the latest SDK versions within the specified constraints
  2. SDK updates flow through automatically without requiring package updates
  3. CI/CD builds catch any breaking changes early

Example Project

Check out the /example directory for a complete working example with:

  • Multiple player configurations
  • Event handling demonstrations
  • API usage examples

To run the example:

cd example
npm install

# For iOS
npx expo run:ios

# For Android
npx expo run:android