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

video-iab-classifier

v0.1.0

Published

Video content recognition and IAB category classification for browser-based video players

Downloads

91

Readme

videocontext

Browser-based video content recognition with IAB content taxonomy classification.

Recognizes what's playing in HTML5 video players and outputs standardized IAB Content Taxonomy categories — ready for programmatic advertising (OpenRTB, VAST, Prebid.js) or any system that needs content classification.

Features

  • 3 recognition engines — use solo or in combination:
    • Metadata — page context, meta tags, schema.org, URL patterns, player metadata
    • Cloud AI — Claude Vision, Google Vision, AWS Rekognition (via server proxy)
    • On-device ML — TensorFlow.js or ONNX Runtime Web (fully client-side)
  • 5 player adapters — HTML5 <video>, Video.js, HLS.js, Shaka Player, JW Player
  • IAB taxonomy 2.0 & 3.0 — selectable per instance, with cross-version mapping
  • Ad-stack ready — OpenRTB and VAST formatted output, global window.__videocontext
  • Privacy-aware — on-device ML needs no external API; battery-aware frame capture

Install

npm install videocontext

Optional peer dependencies (only needed if using those engines):

npm install @tensorflow/tfjs    # for on-device TF.js engine
npm install onnxruntime-web      # for on-device ONNX engine

Quick Start

import { createVideoContext } from 'videocontext';

const vc = createVideoContext({
  target: '#my-video',          // CSS selector or HTMLVideoElement
  taxonomyVersion: '3.0',
  engines: [
    { type: 'metadata', weight: 1.0 }
  ],
  exposeGlobal: true,           // sets window.__videocontext
});

vc.on('classification', (result) => {
  console.log(result.categories);
  // [{ id: '1000', name: 'Sports', path: 'Sports', confidence: 0.85, tier: 1 }, ...]
});

await vc.init();

Configuration

interface VideoContextConfig {
  // Required
  target: string | HTMLVideoElement;

  // Taxonomy
  taxonomyVersion?: '2.0' | '3.0';           // default: '3.0'
  crossVersionMapping?: boolean;               // default: false

  // Engines (at least one required)
  engines?: EngineConfig[];                    // default: [{ type: 'metadata', weight: 1.0 }]
  confidenceThreshold?: number;                // default: 0.5 (0-1)
  maxCategories?: number;                      // default: 5

  // Frame capture (for cloud/ondevice engines)
  capture?: {
    fps?: number;                              // default: 0.2 (one frame per 5 seconds)
    maxDimension?: number;                     // default: 320
    format?: 'image/jpeg' | 'image/png' | 'image/webp';  // default: 'image/jpeg'
    quality?: number;                          // default: 0.7
    batteryAware?: boolean;                    // default: true
  };

  // Player
  adapter?: 'html5' | 'videojs' | 'hlsjs' | 'shaka' | 'jwplayer';  // default: auto-detect

  // Output
  exposeGlobal?: boolean;                      // default: false
  debounceMs?: number;                         // default: 5000
}

Engines

Metadata (no API, instant)

Extracts keywords from page context and matches them to IAB categories.

engines: [
  {
    type: 'metadata',
    weight: 1.0,
    options: {
      useSchemaOrg: true,    // scan JSON-LD / schema.org
      useMetaTags: true,     // scan <meta> tags and Open Graph
      useUrlPatterns: true,  // classify based on URL patterns
    }
  }
]

Cloud AI (requires server proxy)

Captures video frames and sends them to a cloud vision API for classification.

Security: API keys must never be in browser code. Use proxyUrl to route requests through your server.

engines: [
  {
    type: 'cloud',
    weight: 0.7,
    options: {
      provider: 'claude',                    // 'claude' | 'google-vision' | 'aws-rekognition'
      proxyUrl: '/api/classify-frame',       // your server endpoint
      maxFramesPerRequest: 3,
    }
  }
]

Claude Vision is prompted to return IAB categories directly — no label-to-taxonomy mapping needed.

Google Vision / AWS Rekognition return object labels that are fuzzy-matched against the IAB taxonomy.

On-Device ML (fully client-side)

Runs a classification model in the browser. No API calls, fully private.

engines: [
  {
    type: 'ondevice',
    weight: 0.8,
    options: {
      runtime: 'tfjs',                       // 'tfjs' | 'onnx'
      modelUrl: '/models/classifier/model.json',
      preferGpu: true,
    }
  }
]

Models are cached in IndexedDB after the first download. Place a labels.json file next to the model for human-readable class names.

Combining Engines

Use multiple engines with weights to get the best results:

engines: [
  { type: 'metadata', weight: 0.3 },
  { type: 'cloud', weight: 0.7, options: { provider: 'claude', proxyUrl: '/api/classify' } },
]

The classifier normalizes scores per engine, applies weights, groups by category, sums weighted scores, filters by threshold, and returns the top N.

Events

vc.on('classification', (result) => { ... });
vc.on('classification:change', (result, previous) => { ... });
vc.on('engine:result', (engineType, categories) => { ... });
vc.on('engine:error', (engineType, error) => { ... });
vc.on('adapter:detected', (adapterName) => { ... });
vc.on('capture:frame', (frameIndex) => { ... });
vc.on('ready', () => { ... });
vc.on('destroy', () => { ... });

Output Formats

Instance API

const result = vc.getResult();
// { timestamp, taxonomyVersion, categories, sources, confidence }

Global Object

When exposeGlobal: true:

window.__videocontext.result    // ClassificationResult
window.__videocontext.openrtb   // { cat: ['1000', '1028'], cattax: 6 }
window.__videocontext.vast      // [{ authority: '...', value: '1000' }, ...]

OpenRTB Integration (Prebid.js)

import { formatOpenRTB } from 'videocontext';

vc.on('classification:change', (result) => {
  const ortb = formatOpenRTB(result);
  // ortb = { cat: ['1000', '1028'], cattax: 6 }

  pbjs.mergeConfig({
    ortb2: {
      site: { content: ortb },
    },
  });
});

cattax values follow the OpenRTB spec: 2 = IAB 2.0, 6 = IAB 3.0.

VAST Integration

import { formatVAST } from 'videocontext';

const vast = formatVAST(result);
// [{ authority: 'https://iabtechlab.com/standards/content-taxonomy-3', value: '1000' }]

Player Adapters

Adapters are auto-detected by default. To force a specific adapter:

const vc = createVideoContext({
  target: '#player',
  adapter: 'videojs',  // 'html5' | 'videojs' | 'hlsjs' | 'shaka' | 'jwplayer'
});

Custom Adapter

Implement the PlayerAdapter interface:

import type { PlayerAdapter } from 'videocontext';

class MyPlayerAdapter implements PlayerAdapter {
  readonly name = 'my-player';
  canHandle(target) { ... }
  attach(target) { ... }
  detach() { ... }
  getVideoElement() { ... }
  getPlaybackState() { ... }
  getMetadata() { ... }
  on(event, handler) { ... }
  off(event, handler) { ... }
}

const vc = createVideoContext({
  target: '#player',
  adapter: new MyPlayerAdapter(),
});

API Reference

createVideoContext(config)

Factory function. Returns a new VideoContext instance.

VideoContext

| Method | Description | |--------|-------------| | init(): Promise<void> | Initialize adapters, engines, and start classification | | classify(): Promise<ClassificationResult> | Force an immediate classification cycle | | getResult(): ClassificationResult \| null | Get the latest classification result | | on(event, handler) | Subscribe to events | | off(event, handler) | Unsubscribe from events | | updateConfig(partial) | Update configuration at runtime | | destroy(): Promise<void> | Stop all engines and clean up resources |

ClassificationResult

{
  timestamp: string;              // ISO 8601
  taxonomyVersion: '2.0' | '3.0';
  categories: CategoryMatch[];
  mappedCategories?: CategoryMatch[];  // when crossVersionMapping is true
  sources: ('metadata' | 'cloud' | 'ondevice')[];
  confidence: number;             // 0-1 average confidence
}

CategoryMatch

{
  id: string;       // e.g., 'IAB17' (2.0) or '1000' (3.0)
  name: string;     // e.g., 'Sports'
  path: string;     // e.g., 'Sports > Soccer'
  confidence: number; // 0-1
  tier: number;      // 1-4
}

Edge Cases

| Scenario | Behavior | |----------|----------| | Cross-origin video | Canvas becomes tainted — frame capture disabled, falls back to metadata engine. Logs a warning. Fix: add crossorigin="anonymous" to <video>. | | DRM/EME content | Hardware DRM produces blank frames. Detected via pixel sampling, frame is discarded. | | Cloud API failure | Network errors return empty categories. Other engines continue. Error emitted via engine:error. | | Model load failure | On-device engine fails gracefully. Error emitted via engine:error. Other engines continue. | | Low battery | When batteryAware: true and battery < 20%, frame capture rate is halved. | | No engines initialize | init() throws an error. | | Missing taxonomy data | Logs a warning. Run npm run build:taxonomy. |

Building from Source

git clone <repo>
cd videocontext
npm install
npm run build:taxonomy   # generate IAB taxonomy JSON from TSV
npm run build            # compile ESM + CJS + types
npm test                 # run test suite
npm run typecheck        # TypeScript type check

License

MIT