vimond-player-analytics-web
v1.1.11
Published
Vimond Analytics SDK for video player analytics
Maintainers
Readme
vimond-player-analytics-web
Vimond Analytics SDK for video player analytics. This package provides a wrapper around the Eyevinn Player Analytics Client SDK with Vimond-specific functionality and enhanced ad tracking capabilities.
Installation
pnpm install vimond-player-analytics-webFeatures
- 🎯 Automatic video event tracking via Eyevinn Player Analytics SDK
- 📊 Enhanced ad analytics with IMA support
- 🔄 Automatic metadata enrichment with URL parameters
- 📱 Device detection and browser information
- 🎬 Support for both live and VOD content
- 🔐 DRM detection and reporting
- 📈 Quality level and bitrate tracking
- 👤 Persistent user ID generation for cross-session tracking
Usage
Basic Setup
import { vimondAnalytics } from 'vimond-player-analytics-web';
const analytics = vimondAnalytics({
eventsinkUrl: 'https://your-eventsink-url.io',
sessionId: 'unique-session-id',
heartbeatInterval: 10000,
debug: false,
enableAdEvents: true,
adProfileId: 'your-ad-profile-id',
shardId: 'your-shard-id'
});
await analytics.init();
analytics.load(videoElement);Video.js Integration
import { VimondAnalyticsIntegration } from 'vimond-player-analytics-web';
const integration = VimondAnalyticsIntegration({
eventsinkUrl: 'https://your-eventsink-url.io',
sessionId: crypto.randomUUID(),
player: videoJsPlayer,
playback: {
assetId: 'content-id',
assetTitle: 'Content Title',
isLive: false,
isDrmProtected: true,
recommendedStream: {
manifest: { uri: 'https://example.com/manifest.m3u8' },
drmProxyType: 'widevine' // Optional
},
categoryId: 'category-id'
},
videoKey: 'unique-video-key', // Note: userId is automatically generated and persisted
enableAdEvents: true,
adProfileId: 'your-ad-profile-id',
shardId: 'your-shard-id',
additionalMetadata: {
customField1: 'value1',
customField2: 'value2'
}
});
await integration.init();Bitmovin React Native Integration
For React Native applications using Bitmovin Player:
import { BitmovinRNAnalyticsIntegration } from 'vimond-player-analytics-web';
const integration = BitmovinRNAnalyticsIntegration({
eventsinkUrl: 'https://your-eventsink-url.io',
sessionId: crypto.randomUUID(),
playback: {
assetId: 'content-id',
assetTitle: 'Content Title',
isLive: false,
isDrmProtected: 'drm', // 'drm' or 'none'
recommendedStream: {
manifest: { uri: 'https://example.com/manifest.m3u8' }
},
categoryId: 'category-id'
},
videoKey: 'unique-video-key', // Note: userId is automatically generated and persisted
duration: 120000, // Duration in milliseconds
player: {
getCurrentTime: async () => {
// Return current playback time
return await player.getCurrentTime();
},
isMuted: async () => {
// Return muted state
return await player.isMuted();
},
autoplay: () => {
// Return autoplay state
return player.autoplay();
}
},
deviceInfo: {
deviceName: 'Apple TV',
model: 'Apple TV 4K'
},
domain: 'example.com',
path: '/watch',
playerProfileId: 'your-player-profile-id',
adProfileId: 'your-ad-profile-id',
shardId: 'your-shard-id',
additionalMetadata: {
customField1: 'value1'
}
});
await integration.init();
// Handle player events
integration.handlePlayerEvent({
name: 'onPlaying',
// ... event data
});
integration.handlePlayerEvent({
name: 'onAdStarted',
ad: { id: 'ad-id' },
adBreak: { id: 'break-id' },
position: 'pre',
duration: 30
});Ad Event Tracking
// Report ad events with IMA integration
integration.reportAdEvent({
data: imaAdEvent,
adState: 'adStarted', // or 'adCompleted', 'adSkipped', 'adError', etc.
adPosition: 'pre', // 'pre', 'mid', or 'post'
metadata: {
customAdField: 'value'
}
});
// Track ad quartiles
const analytics = integration.getAnalyticsInstance();
analytics.setAdMidQuartile('1');
analytics.setAdThirdQuartile('1');
analytics.incrementCompletedAds();Configuration
VimondAnalyticsConfig
type VimondAnalyticsConfig = {
eventsinkUrl: string; // Required: Analytics endpoint URL
sessionId?: string; // Optional: Unique session identifier
heartbeatInterval?: number; // Optional: Heartbeat interval in ms (default: 10000)
debug?: boolean; // Optional: Enable debug logging
enableAdEvents?: boolean; // Optional: Enable ad event tracking
adProfileId?: string; // Optional: Ad profile identifier
shardId?: string; // Optional: Shard identifier for data partitioning
player?: { // Optional: Player state callbacks
muted?: () => boolean; // Optional: Function to get muted state
autoplay?: () => boolean; // Optional: Function to get autoplay state
};
}VideoJsAnalyticsConfig
type VideoJsAnalyticsConfig = {
player: Player; // Required: Video.js player instance
playback: {
assetId: string;
assetTitle: string;
isLive: boolean;
isDrmProtected: boolean;
recommendedStream?: {
manifest?: { uri: string };
drmProxyType?: string; // Optional: DRM proxy type
};
categoryId?: string;
};
videoKey: string; // Required: Unique video identifier
eventsinkUrl: string;
sessionId?: string;
heartbeatInterval?: number;
debug?: boolean;
enableAdEvents?: boolean;
adProfileId?: string;
shardId?: string;
additionalMetadata?: Record<string, unknown>; // Custom metadata fields
}BitmovinRNAnalyticsConfig
type BitmovinRNAnalyticsConfig = {
playback: {
assetId: string;
assetTitle: string;
isLive: boolean;
isDrmProtected: string; // 'drm' or 'none'
recommendedStream?: {
manifest?: { uri: string };
};
categoryId?: string;
};
videoKey: string; // Required: Unique video identifier
eventsinkUrl: string;
sessionId?: string;
heartbeatInterval?: number;
debug?: boolean;
adProfileId?: string;
shardId?: string;
domain?: string; // Optional: Domain for analytics
path?: string; // Optional: Path for analytics
playerProfileId?: string; // Optional: Player profile identifier
deviceInfo?: {
deviceName?: string; // Optional: Device name
model?: string; // Optional: Device model
};
duration: number; // Required: Content duration in milliseconds
player?: {
getCurrentTime?: () => Promise<number>; // Optional: Get current playback time
isMuted?: () => Promise<boolean>; // Optional: Get muted state
autoplay?: () => boolean; // Optional: Get autoplay state
};
additionalMetadata?: Record<string, string>; // Custom metadata fields
}API Reference
vimondAnalytics()
Factory function that creates an analytics instance.
Methods
init(): Promise<void>- Initialize the analytics connectorload(videoElement: HTMLVideoElement): void- Attach to video element for automatic event trackingreportMetadata(metadata: Record<string, unknown>): void- Report custom metadata (automatically enriched withplayerProfileIdfrom URL)reportAdEvent(payload: VimondAdEventPayload): void- Report ad events with automatic IMA data extractionreportBitrateChange(bitrate: number): void- Report quality/bitrate changessetAdMidQuartile(value: string): void- Set ad mid-quartile trackingsetAdThirdQuartile(value: string): void- Set ad third-quartile trackingincrementCompletedAds(): void- Increment completed ads counterresetCompletedAds(): void- Reset completed ads countergetCompletedAdsCount(): number- Get current completed ads countdeinit(): void- Clean up resources and disconnect
VimondAnalyticsIntegration()
Factory function for Video.js integration.
Features:
- Automatically monitors quality level changes and reports bitrate changes
- Sets up player state callbacks (muted, autoplay) for metadata enrichment
Methods
init(): Promise<void>- Initialize integration and report initial metadatareportAdEvent(payload): void- Report ad eventsreportMetadata(metadata): void- Report custom metadatagetAnalyticsInstance()- Get the underlying analytics instancedeinit(): void- Clean up resources and remove event listeners
BitmovinRNAnalyticsIntegration()
Factory function for Bitmovin React Native integration.
Methods
init(): Promise<void>- Initialize integration and report initial metadatahandlePlayerEvent(event: PlayerEventData): void- Handle player events from Bitmovin playerreportAdEvent(payload: AdEventPayload): void- Report ad events manuallyreportMetadata(metadata): void- Report custom metadatagetAnalyticsInstance()- Get the underlying analytics instancedeinit(): void- Clean up resources
Supported Player Events
The Bitmovin integration handles the following player events automatically:
onPlaying- Playback startedonPaused- Playback pausedonSeek- Seeking startedonSeeked- Seeking completedonPlaybackFinished- Playback endedonPlayerError/SourceError- Player errorsonStallStarted- Buffering startedonStallEnded- Buffering endedonVideoPlaybackQualityChanged- Quality/bitrate changedonAdStarted- Ad playback startedonAdFinished- Ad playback completedonAdSkipped- Ad was skippedonAdBreakFinished- Ad break completedonAdError- Ad error occurredonAdQuartile- Ad quartile reached (mid_point, third)
Automatic Metadata Enrichment
The SDK automatically enriches metadata with:
From URL Parameters (Web only)
playerProfileId- Extracted fromplayerIdURL parameteradProfileId- Extracted fromprofileIdURL parameter (for embedded players)
Player Source Detection (Web)
playerSource- Automatically set to'inline'for embedded players (path includes/embedded/) or'appbuilder'for app players- For Bitmovin RN:
playerSourceis set to'TvOS'
Device & Browser Information (Web)
deviceId- Browser name (from user agent)deviceModel- OS name (from user agent)deviceType- 'Desktop', 'Mobile', or 'Tablet' (detected from user agent)
Device Information (Bitmovin RN)
deviceId- Device name (from config)deviceModel- Device model (from config)deviceType- Always'TvOS'
Player State
muted- Player muted state ('0' or '1')autoplay- Player autoplay state ('0' or '1')
Content Information
contentId- Asset IDcontentTitle- Asset titlecontentUrl- Manifest URLdrmType- 'drm' or 'none'live- Boolean for live/VODcategoryId- Content categoryuserId- Persistent UUID automatically generated and stored for cross-session user tracking (see User ID Persistence below)
User ID Persistence
The SDK automatically generates and persists a unique user ID (UUID) for tracking customer journeys across sessions. This enables analytics at a unique user level.
How It Works
- Automatic Generation: A UUID v4 is generated on first use and stored persistently
- Cross-Session Tracking: The same UUID is reused across sessions until the user clears their data
- Platform Support:
- Web: Stored in
localStorage(persists until browser data is cleared) - React Native/tvOS/webOS: Stored using React Native's
SettingsAPI (persists across app restarts)
- Web: Stored in
- Privacy: The UUID is stored locally and only sent in analytics events. Users can clear it by clearing browser/app data
Storage Details
- Storage Key:
vimond_analytics_user_id - Web: Uses browser
localStorageAPI - React Native/tvOS/webOS: Uses React Native
SettingsAPI (compatible with tvOS storage limitations) - Fallback: If storage is unavailable, a UUID is still generated but won't persist (new UUID on each session)
Benefits
- ✅ Track user behavior across multiple video sessions
- ✅ Analyze customer journey at individual user level
- ✅ No manual user ID management required
- ✅ Works seamlessly across web and native platforms
Note: The
videoKeyparameter in configuration is still accepted for backward compatibility but is no longer used as theuserIdin analytics events. The persistent UUID takes precedence.
Ad Events
Supported Ad States
adStarted- Ad playback startedadCompleted- Ad playback completedadSkipped- Ad was skippedadError- Ad error occurredadAllCompleted- All ads in break completed (Bitmovin RN)adClicked- Ad was clicked (Bitmovin RN)adRequested- Ad was scheduled/requested (Bitmovin RN)adBlocked- Ad blocker detected- Custom states as needed
IMA Ad Data Extraction
The SDK automatically extracts from IMA ad objects:
- Ad ID (
adImpressionId) - Ad title
- Ad duration
- Ad tag URL
- Ad pod position
- Total ads in pod
- Skippable status
- VAST/VMAP detection
Environment Variables
When used in a web application, the following environment variables can be configured:
# Analytics Configuration
NEXT_PUBLIC_EYEVINN_ANALYTICS_URL=https://your-eventsink-url.io
NEXT_PUBLIC_ENABLE_EYEVINN_AD_EVENTS=true
NEXT_PUBLIC_HEARTBEAT_INTERVAL=10000
# Ad Configuration
NEXT_PUBLIC_DEFAULT_AD_PROFILE_ID=your-ad-profile-id
# Shard Configuration
NEXT_PUBLIC_SHARD_ID=your-shard-id
# Optional: Custom metadata fields (comma-separated)
NEXT_PUBLIC_EYEVINN_METADATA_FIELDS=keywords,tags,genreReal-World Example
Here's how the SDK is used in a React/Next.js application:
import { useEffect, useMemo, useRef } from 'react';
import { VimondAnalyticsIntegration } from 'vimond-player-analytics-web';
import type Player from 'video.js/dist/types/player';
export const useVideoAnalytics = (args: {
playback: {
assetId: string;
assetTitle: string;
isLive: boolean;
isDrmProtected: boolean;
recommendedStream?: {
manifest?: { uri: string };
drmProxyType?: string;
};
categoryId?: string;
};
playerInstanceRef: React.MutableRefObject<Player | null>;
videoKey?: string;
asset?: {
keywords?: string;
tags?: string;
};
}) => {
const analyticsIntegrationRef = useRef(null);
const analyticsSessionId = useMemo(() => crypto.randomUUID(), []);
useEffect(() => {
if (!playerInstanceRef.current) return;
const integration = VimondAnalyticsIntegration({
eventsinkUrl: process.env.NEXT_PUBLIC_EYEVINN_ANALYTICS_URL,
sessionId: analyticsSessionId,
heartbeatInterval: 10000,
debug: false,
enableAdEvents: true,
adProfileId: process.env.NEXT_PUBLIC_DEFAULT_AD_PROFILE_ID,
shardId: process.env.NEXT_PUBLIC_SHARD_ID,
player: playerInstanceRef.current,
playback: args.playback,
videoKey: args.videoKey ?? args.playback.assetId,
additionalMetadata: {
keywords: args.asset?.keywords,
tags: args.asset?.tags,
}
});
analyticsIntegrationRef.current = integration;
integration.init().catch(console.error);
return () => {
integration.deinit();
};
}, [args.playback.assetId]);
return {
reportAdEvent: (data, adState, adPosition = 'pre') => {
analyticsIntegrationRef.current?.reportAdEvent({
data,
adState,
adPosition,
});
},
getAnalyticsInstance: () => {
return analyticsIntegrationRef.current?.getAnalyticsInstance();
}
};
};Types
VimondAdEventPayload
Used for web integrations (Video.js and basic HTML video element).
type VimondAdEventPayload = {
data: unknown; // IMA ad event data
adState: string; // Ad state (e.g., 'adStarted', 'adCompleted')
adPosition?: 'pre' | 'mid' | 'post'; // Ad position in content
adMidQuartile?: string; // Mid-quartile reached ('0' or '1')
adThirdQuartile?: string; // Third-quartile reached ('0' or '1')
completedAdsCount?: string; // Number of completed ads
metadata?: Record<string, unknown>; // Additional metadata
}AdEventPayload
Used for Bitmovin React Native integration.
type AdEventPayload = {
data: unknown; // Ad event data
adState: string; // Ad state (e.g., 'adStarted', 'adCompleted')
event?: Record<string, unknown>; // Optional: Additional event data (ad, adBreak, position, duration, etc.)
}PlayerEventData
Event data structure for Bitmovin React Native player events.
type PlayerEventData = Record<string, unknown> & {
name: string; // Event name (e.g., 'onPlaying', 'onAdStarted')
// Additional properties vary by event type
}Development
Scripts
pnpm run build- Build the packagepnpm run dev- Watch mode for developmentpnpm run lint- Run ESLintpnpm run lint:fix- Fix ESLint issuespnpm run format- Format code with Prettierpnpm run format:check- Check code formatting
Code Quality
This package uses ESLint and Prettier for code quality and formatting:
- ESLint: TypeScript linting with strict rules
- Prettier: Code formatting with consistent style
- TypeScript: Strict type checking
Dependencies
@eyevinn/player-analytics-client-sdk-web- Core analytics SDK@eyevinn/player-analytics-specification- Analytics specification typesua-parser-js- User agent parsing for device detection
Peer Dependencies
video.js- Video.js player (forVimondAnalyticsIntegration)
License
MIT
