import PubNub from 'pubnub'; /** * Type definitions for @pubnub/shaka-player */ /** * Role of a client in the sync session. * - 'master': Controls playback for all connected clients * - 'follower': Receives and applies sync commands from the master */ type SyncRole = 'master' | 'follower'; /** * Configuration options for SyncManager. */ interface SyncManagerConfig { /** * Your PubNub publish key from the PubNub Admin Dashboard. * @see https://admin.pubnub.com */ publishKey: string; /** * Your PubNub subscribe key from the PubNub Admin Dashboard. * @see https://admin.pubnub.com */ subscribeKey: string; /** * Optional unique identifier for this client. * If not provided, a random ID will be generated. * Each client in a sync session MUST have a unique userId. */ userId?: string; /** * Maximum allowed time drift (in seconds) before forcing correction. * Clients within this threshold won't be adjusted to avoid jitter. * @default 0.5 */ maxDriftThreshold?: number; /** * How often (in milliseconds) to send sync pulses when master is playing. * @default 5000 */ syncIntervalMs?: number; /** * Optional PubNub constructor class. * If not provided, the SyncManager will look for a global `PubNub` variable. * This allows you to pass the PubNub class directly when using npm/bundlers. * * @example * ```typescript * import PubNub from 'pubnub'; * const syncManager = new SyncManager(player, { * publishKey: '...', * subscribeKey: '...', * PubNub: PubNub * }); * ``` */ PubNub?: new (config: any) => PubNub; /** * Optional Access Manager v3 auth token. * When provided, the PubNub client will use this token for all API requests. * Tokens are generated server-side using `grantToken()` with a Secret Key. * * @see https://www.pubnub.com/docs/general/security/access-control */ authToken?: string; /** * Optional PubNub Secret Key. * **WARNING: Only use on the server or for demo/testing purposes.** * Never expose the Secret Key in production client-side code. * Required for calling `grantToken()` from the SyncManager. */ secretKey?: string; /** * Optional async callback invoked when the current auth token expires * (i.e., a 403 Forbidden response is received). The callback should * return a fresh token string. The SyncManager will apply it automatically * via `setToken()` and retry the failed operation. * * @example * ```typescript * onTokenExpired: async () => { * const res = await fetch('/api/token'); * const { token } = await res.json(); * return token; * } * ``` */ onTokenExpired?: () => Promise; } /** * Payload object for sync commands. */ interface SyncPayload { /** Timestamp when the command was sent (for latency calculation) */ timestamp: number; /** User ID of the sender */ senderId: string; /** Whether the video is currently paused */ isPaused: boolean; /** Current playback time in seconds */ currentTime?: number; /** Current playback rate (e.g., 1.0 for normal speed) */ playbackRate?: number; } /** * Message object for sync commands sent via PubNub. */ interface SyncMessage { /** Message type: 'SYNC_COMMAND' or 'MASTER_CLAIM' */ type: 'SYNC_COMMAND' | 'MASTER_CLAIM'; /** Command type for SYNC_COMMAND messages */ command?: 'play' | 'pause' | 'seek' | 'sync' | 'ratechange'; /** Command payload */ payload: SyncPayload; } /** * Event data for the 'masterchanged' event. */ interface MasterChangedEventData { /** User ID of the new master */ newMasterId: string; /** The role this client had before the change */ previousRole: SyncRole; } /** * Event data for the 'userjoined' event. */ interface UserJoinedEventData { /** User ID of the user who joined */ userId: string; /** Current occupancy count */ occupancy?: number; } /** * Event data for the 'userleft' event. */ interface UserLeftEventData { /** User ID of the user who left */ userId: string; /** Current occupancy count */ occupancy?: number; } /** * Event data for the 'accessdenied' event. * Emitted when a 403 Forbidden response is received from PubNub, * indicating that the current auth token is missing, expired, or * lacks the required permissions. */ interface AccessDeniedEventData { /** Human-readable reason for the access denial */ reason: string; } /** * Options for the `grantToken()` method on SyncManager. */ interface GrantTokenOptions { /** Time-to-live in minutes (1 to 43200). */ ttl: number; /** The UUID authorized to use this token. Defaults to the current userId. */ authorized_uuid?: string; /** Channel permissions to grant. If omitted, the current room channel is used with read+write. */ channels?: Record; } /** * Events emitted by SyncManager. */ interface SyncManagerEvents { /** Emitted when the master role changes */ masterchanged: MasterChangedEventData; /** Emitted when a user joins the sync room */ userjoined: UserJoinedEventData; /** Emitted when a user leaves the sync room */ userleft: UserLeftEventData; /** Emitted when connected to a sync room */ connected: { roomId: string; }; /** Emitted when disconnected from a sync room */ disconnected: { roomId: string; }; /** Emitted when a 403 Forbidden response is received (Access Manager) */ accessdenied: AccessDeniedEventData; } /** * Type for event listener callbacks. */ type SyncEventListener = (event: SyncManagerEvents[K]) => void; /** * Shaka Player instance type (minimal interface for type safety). * We use a minimal interface to avoid tight coupling with shaka-player types. */ interface ShakaPlayer { getMediaElement(): HTMLMediaElement | null; } /** * SyncManager - Real-time playback synchronization for Shaka Player * * Enables "Watch Party" experiences where multiple viewers stay in sync * using PubNub as the real-time messaging layer. */ /** * SyncManager enables real-time playback synchronization between multiple * clients using PubNub as the messaging layer. One client acts as the * "master" (controlling playback) while others are "followers" (receiving * sync commands). * * @example * ```typescript * import shaka from 'shaka-player'; * import { SyncManager } from '@pubnub/shaka-player'; * * const player = new shaka.Player(); * await player.attach(videoElement); * await player.load(manifestUrl); * * const syncManager = new SyncManager(player, { * publishKey: 'pub-c-xxx', * subscribeKey: 'sub-c-xxx', * }); * * syncManager.connect('watch-party-room'); * syncManager.becomeMaster(); // Take control * ``` */ declare class SyncManager { /** Reference to the Shaka Player instance */ private readonly shakaPlayer; /** Reference to the HTML video element */ private video; /** PubNub configuration */ private config; /** PubNub client instance */ private pubnub; /** PubNub subscription for the current room */ private subscription; /** Current channel/room name */ private channelName; /** Unique identifier for this client */ private userId; /** Current role: 'master' or 'follower' */ private role; /** Flag to prevent loops when processing remote commands */ private isProcessingRemoteCommand; /** Connection status */ private connected; /** Timer for periodic sync pulses */ private syncTimer; /** Maximum allowed drift before correction (seconds) */ private maxDriftThreshold; /** Sync pulse interval (milliseconds) */ private syncIntervalMs; /** Timer for re-enabling local events after remote command */ private remoteCommandTimeout; /** Event listeners */ private eventListeners; /** Bound event handlers for cleanup */ private boundHandlers; /** * Creates a new SyncManager instance. * * @param player - The Shaka Player instance to synchronize * @param config - Configuration object with PubNub credentials */ constructor(player: ShakaPlayer, config: SyncManagerConfig); /** * Returns the Shaka Player instance. */ getPlayer(): ShakaPlayer; /** * Connects to a sync room and begins listening for sync commands. * * @param roomId - Unique identifier for the sync room */ connect(roomId: string): void; /** * Disconnects from the current sync room. */ disconnect(): void; /** * Makes this client the master controller. * The master's playback actions are broadcast to all followers. */ becomeMaster(): void; /** * Makes this client a follower. * Followers receive and apply sync commands from the master. */ becomeFollower(): void; /** * Returns the current role of this client. */ getRole(): SyncRole; /** * Returns whether we're currently connected to a sync room. */ isConnected(): boolean; /** * Returns the current room ID. */ getRoomId(): string; /** * Returns this client's user ID. */ getUserId(): string; /** * Sets (or replaces) the Access Manager auth token at runtime. * Use this to refresh an expired token without reconnecting. * * @param token - A valid Access Manager v3 token string */ setAuthToken(token: string): void; /** * Grants an Access Manager v3 token for the current sync room. * **Requires `secretKey` in the config.** This should only be called * from a server or for demo/testing purposes. * * @param options - Grant options (ttl, optional authorized_uuid, optional channel permissions) * @returns The granted token string */ grantToken(options: GrantTokenOptions): Promise; /** * Parses an Access Manager v3 token and returns the embedded permissions. * Useful for debugging and inspecting token contents. * * @param token - The token string to parse * @returns Parsed token object with ttl, authorized_uuid, resources, and patterns */ parseToken(token: string): any; /** * Static utility to parse an Access Manager token without needing a * connected SyncManager instance. Requires passing the PubNub class. * * @param token - The token string to parse * @param PubNubClass - The PubNub constructor class * @returns Parsed token object */ static parseToken(token: string, PubNubClass: new (config: any) => PubNub): any; /** * Adds an event listener. */ addEventListener(event: K, listener: SyncEventListener): void; /** * Removes an event listener. */ removeEventListener(event: K, listener: SyncEventListener): void; /** * Destroys the SyncManager and releases all resources. */ destroy(): Promise; private attachVideoListeners; private detachVideoListeners; private onLocalPlay; private onLocalPause; private onLocalSeeked; private onLocalRateChange; private broadcastCommand; private broadcastFullState; private broadcastMasterClaim; private startSyncInterval; private onPubNubMessage; private handleMasterClaim; private applyRemoteCommand; private handleSyncPulse; private onPubNubStatus; private onPubNubPresence; /** * Checks whether an error is a 403 Access Denied error from PubNub. */ private isAccessDeniedError; /** * Attempts to refresh the auth token using the configured `onTokenExpired` callback. * Returns true if the token was successfully refreshed and applied. */ private attemptTokenRefresh; private generateUserId; private getGlobalPubNub; private emit; } /** * @pubnub/shaka-player * * Real-time playback synchronization for Shaka Player using PubNub. * Enables "Watch Party" experiences where multiple viewers stay in sync. * * @packageDocumentation */ /** * Library version. Updated automatically by release-please. */ declare const version = "1.2.0"; export { type AccessDeniedEventData, type GrantTokenOptions, SyncManager, type SyncManagerConfig, type SyncManagerEvents, type SyncMessage, type SyncPayload, type SyncRole, version };