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

@devix-technologies/react-gjirafa-vp-player

v1.0.32

Published

A lightweight React wrapper for the [Gjirafa VP Player SDK](https://vp.gjirafa.tech/documentation/docs/web-player/).

Readme

React Gjirafa VP Player

A lightweight React wrapper for the Gjirafa VP Player SDK.

v2.0.0 - Simplified Architecture

Version 2.0.0 is a major simplification. The wrapper is now a thin bridge to the GTech JS player:

  • No configuration interference - GTech admin panel is the source of truth
  • Auto-initialization - Managed scripts auto-init with embedded config
  • Event forwarding - React callbacks for SDK events

Installation

npm install @devix-technologies/react-gjirafa-vp-player
# or
pnpm add @devix-technologies/react-gjirafa-vp-player

Quick Start

import { VPPlayer } from "@devix-technologies/react-gjirafa-vp-player";

// Using scriptId + videoId (recommended)
<VPPlayer
  scriptId="ptkzurnx"
  videoId="vjsobqhe"
  onPlay={() => console.log('Playing!')}
/>

// OR using full script URL
<VPPlayer
  scriptUrl="https://host.vpplayer.tech/player/ptkzurnx/vjsobqhe.js"
  onPlay={() => console.log('Playing!')}
/>

How It Works

  1. Wrapper generates a unique divId for the container
  2. Script URL: https://host.vpplayer.tech/player/{scriptId}/{videoId}.js?divId={ourDivId}
  3. Script loads and auto-initializes with GTech admin config
  4. Wrapper does NOT call .setup() - script already did it
  5. Wrapper attaches event listeners for React callbacks

Usage Examples

Web Player

<VPPlayer
  scriptId="ptkzurnx"
  videoId="vjsobqhe"
  onPlay={() => analytics.track("play")}
  onQuartile25={() => analytics.track("25% watched")}
  onProgress20s={() => analytics.track("20s milestone")}
/>

Vertical Player

<VPPlayer scriptId="rbqcdwzlg" videoId="vjsobqhe" isVertical={true} />

Reels Mode (TikTok-style)

<VPPlayer
  scriptId="rbqcdwzlg"
  videoId="vjsobqhe"
  isReels={true}
  thumbnailUrl="https://example.com/thumb.jpg"
  onClose={() => setShowPlayer(false)}
/>

Manual Playlist (Custom Videos Array)

For vertical player with your own videos array (not using GTech admin playlists):

<VPPlayer
  scriptId="rbqcdwzlg"
  projectId="agmipnzb"
  isVertical={true}
  playlist={{
    videos: [
      {
        videoId: "1",
        title: "Big Buck Bunny",
        file: "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4",
        thumbnailUrl: "https://example.com/thumb1.jpg",
        duration: 498,
      },
      {
        videoId: "2",
        title: "Elephants Dream",
        file: "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ElephantsDream.mp4",
        thumbnailUrl: "https://example.com/thumb2.jpg",
        duration: 738,
      },
    ],
    startIndex: 0,
  }}
  onNext={() => console.log("Next video")}
  onVideoStarted={(data) => console.log("Playing:", data.title)}
/>

Note: Manual playlist mode calls player.setup() internally with the built config. This is required because the VP Vertical Player SDK needs video.file to be set even in playlist mode.

Custom Container Sizing

The player fills its container (100% width/height). Control sizing with a wrapper:

// Responsive 16:9
<div style={{ width: '100%', maxWidth: '800px', aspectRatio: '16/9' }}>
  <VPPlayer scriptId="ptkzurnx" videoId="vjsobqhe" />
</div>

// Fixed size
<div style={{ width: '400px', height: '225px' }}>
  <VPPlayer scriptId="ptkzurnx" videoId="vjsobqhe" />
</div>

Video Locking

Video locking lets you lock a portion of each video (e.g. require watching N seconds or N% before skipping). The SDK supports this via a shouldLockVideo callback and removeVideoLock(). The recommended approach is to set the callback inside onVideoSwitch so locking applies after each video switch.

Ref methods

  • shouldLockVideo(callback) – Set the callback that returns lock config. Pass a function that returns { isEnabled, type, value }, or null to clear. Matches SDK naming.
  • removeVideoLock() – Clear any active lock.

Lock config shape

{ isEnabled: boolean; type: "seconds" | "percentage"; value: number }

Recommended pattern: lock on video switch

Use onVideoSwitch to set or clear shouldLockVideo after each switch. In the callback you can decide whether to lock (e.g. from app state) and for how long.

const playerRef = useRef<VPPlayerRef>(null);

<VPPlayer
  ref={playerRef}
  scriptId="ptkzurnx"
  videoId="vjsobqhe"
  config={{ /* ... */ }}
  onVideoSwitch={() => {
    const shouldLock = true; // your logic, e.g. from state
    if (shouldLock) {
      playerRef.current?.shouldLockVideo(() => ({
        isEnabled: true,
        type: "seconds",
        value: 20,
      }));
    } else {
      playerRef.current?.removeVideoLock();
      playerRef.current?.shouldLockVideo(() => ({
        isEnabled: false,
        type: "seconds",
        value: 0,
      }));
    }
  }}
/>

To only lock for a period after each switch, enable locking in onVideoSwitch with the desired value (e.g. 20 seconds). To disable locking for the next video, call removeVideoLock() and set shouldLockVideo to return isEnabled: false.

Ads

Ad breaks come from the fetched config, and the player boots its ad stack whenever video.advertising is true. Two things change that.

Ad-free viewers

Pass disableAds for anyone whose subscription has paid the ads away. The wrapper merges video.advertising: false into the config, so the ad stack never starts and the IMA SDK is never even requested.

<VPPlayer
  scriptId="ptkzurnx"
  videoId="vjsobqhe"
  disableAds={hasPremiumAccess || hasAdFree}
  config={{ config: { autoplay: true } }}
/>

Stubbed IMA SDK (VP-589)

The player reads google.ima.ImaSdkSettings.VpaidMode.ENABLED without guarding it. When something has replaced the SDK with a stub — an ad blocker surrogate, a browser tracking-protection shim, an HTTPS-filtering antivirus, a truncated response — that read throws inside an async setup chain nothing catches, and the viewer is left with a black frame and a spinner for good.

A missing SDK is harmless: the player detects it and skips the ad break by itself. Only a half-present one is fatal.

So when the config carries an ad break, the wrapper probes the SDK before setup() and disables advertising if it finds a stub. Playback survives; the ad does not. The probe is skipped entirely for configs with no ad break, so an ad-free video costs no extra request.

The probe requests the same SDK the player does, over https. Should the player ever move to a different SDK URL, the probe would be checking a file nobody uses; it warns on the console when the URL it knows about loads but defines no SDK, so a stale constant is visible rather than silent. IMA_SDK_URL in constants/vpPlayer.ts carries the command for re-checking it against the player bundle.

See the Custom Features/Ad SDK Guard stories.

Props Reference

Identification (one required)

| Prop | Type | Description | | ----------- | -------- | ---------------------------------- | | scriptId | string | GTech script ID (e.g., "ptkzurnx") | | videoId | string | GTech video ID (e.g., "vjsobqhe") | | scriptUrl | string | Full managed script URL |

Optional

| Prop | Type | Description | | --------------- | ---------------------- | ---------------------------------------------------- | | projectId | string | GTech project ID (required for manual playlist) | | playlist | ManualPlaylistConfig | Custom videos array for manual playlist mode | | disableAds | boolean | Drop the ad break for an ad-free viewer | | playerId | string | Custom container ID (auto-generated if not provided) | | isVertical | boolean | Force vertical player mode | | isReels | boolean | Enable Reels overlay mode | | thumbnailUrl | string | Thumbnail for Reels mode | | className | string | CSS class for container | | hiddenClasses | string[] | SDK element classes to hide |

Event Callbacks

| Prop | Horizontal | Vertical | Description | | ---------------- | :--------: | :------: | ----------------------------------------------------- | | onReady | ✓ | ✓ | Player initialized | | onPlay | ✓ | ✓ | Playback started (initial play) | | onPause | ✓ | ✓ | Playback paused (user-initiated) | | onResume | ✗ | ✓ | Playback resumed after pause (vertical only) | | onComplete | ✓ | ✗ | Video completed (horizontal only) | | onError | ✓ | ✓ | Error occurred | | onVideoStarted | ✓ | ✓ | Video metadata available (returns CurrentVideoData) | | onTimeUpdate | ✓ | ✓ | Continuous time updates (returns seconds) | | onQuartile25 | ✓ | ✓ | 25% watched | | onQuartile50 | ✓ | ✓ | 50% watched | | onQuartile75 | ✓ | ✓ | 75% watched | | onNext | ✓ | ✓ | Next video in playlist | | onPrevious | ✗ | ✓ | Previous video (vertical only) | | onProgress10s | ✓ | ✓ | Every ~10 seconds (returns seconds) | | onProgress20s | ✓ | ✓ | 20 second milestone (returns seconds) | | onVideoSwitch | ✓ | ✓ | SDK vp-video-switch (use for video locking) | | onClose | - | - | Reels overlay closed |

Event Implementation Notes

Horizontal Player uses standard VP Player SDK events via .on() listener:

  • ready, play, pause, complete, video-started, video-state, time, playlistItem, error
  • Quartiles: analytics-25%-completed, analytics-50%-completed, analytics-75%-completed

Vertical Player uses a combination of .on() listener and the global vp-event handler:

Standard events via .on():

  • vp-first-frame, vp-video-started, vp-video-state, vp-time, vp-video-switch, error
  • Quartiles: analytics-25%-completed, analytics-50%-completed, analytics-75%-completed

Special handling via vp-event handler (not exposed via standard .on()):

The following events are intercepted from the global vp-event emission because the VP Vertical Player SDK doesn't expose them via the standard .on() listener:

| Event | SDK Event | Special Logic | | ---------- | ------------------ | -------------------------------------------------------------- | | onPlay | vp-state-playing | Fires on initial play (when not resuming from pause) | | onPause | vp-state-paused | Only fires on user-initiated pause (not on video loop/end) | | onResume | vp-state-playing | Fires when resuming after user-initiated pause |

User Interaction Tracking:

To distinguish between user actions and automatic playback events (like video loops), we track the vp-user-interaction event:

  • When vp-user-interaction fires before vp-state-paused → user clicked pause → onPause fires
  • When vp-user-interaction fires before vp-state-playing (after pause) → user clicked play → onResume fires
  • When vp-state-paused fires without prior user interaction → video ended/looped → no onPause (silent)
  • When vp-state-playing fires without prior pause → initial play or loop restart → onPlay fires

This ensures analytics events like player_pause and player_resume only fire for actual user actions, not automatic video looping.

Key Differences:

  • onComplete - Only available on horizontal player (vertical auto-advances to next video)
  • onResume - Only available on vertical player (horizontal has no pause/resume distinction)
  • onPrevious - Only available on vertical player (horizontal has no direction detection)
  • onNext on horizontal fires for ANY playlist change (next button, auto-advance after complete)
  • Direction detection on vertical uses player._switchDirection property (via queueMicrotask) to differentiate onNext vs onPrevious

URL Helpers

import {
  getFullyManagedPlayerScriptUrl,
  getVerticalPlayerScriptUrl,
  appendDivIdToUrl,
} from "@devix-technologies/react-gjirafa-vp-player";

// Build managed script URL
const url = getFullyManagedPlayerScriptUrl("ptkzurnx", "vjsobqhe");
// => "https://host.vpplayer.tech/player/ptkzurnx/vjsobqhe.js"

// Build vertical player URL
const verticalUrl = getVerticalPlayerScriptUrl("rbqcdwzlg");
// => "https://host.vpplayer.tech/vertical-player/rbqcdwzlg.js"

Advanced: Direct Hook Usage

For programmatic control, use the hook directly:

import {
  useVPPlayerLogic,
  PlayerContainer,
} from "@devix-technologies/react-gjirafa-vp-player";

function CustomPlayer() {
  const { playerRef, playerInstanceRef, isScriptLoaded, generatedPlayerId } =
    useVPPlayerLogic({
      scriptId: "ptkzurnx",
      videoId: "vjsobqhe",
    });

  return (
    <div>
      <PlayerContainer
        id={generatedPlayerId}
        ref={playerRef}
        width="100%"
        height="auto"
        $hiddenClasses={[]}
      />
      <button onClick={() => playerInstanceRef.current?.play?.()}>Play</button>
      <button onClick={() => playerInstanceRef.current?.pause?.()}>
        Pause
      </button>
    </div>
  );
}

Migration from v1.x

See MIGRATION.md for detailed upgrade instructions.

Quick Summary

// BEFORE (v1.x)
<VPPlayer
  videoId="vjsobqhe"
  projectId="abc123"
  scriptUrl="https://host.vpplayer.tech/player/ptkzurnx.js"
  pureMode={true}
  config={{ video: { autoplay: true } }}
  onPlayerPlay={() => console.log('play')}
/>

// AFTER (v2.0.0)
<VPPlayer
  scriptId="ptkzurnx"
  videoId="vjsobqhe"
  onPlay={() => console.log('play')}
/>

License

MIT