# XStream Player SDK

A powerful and feature-rich video player SDK for streaming content, built with TypeScript and HLS.js.

## Features

- 🎥 **HLS (HTTP Live Streaming) support** - Industry-standard adaptive streaming
- 📊 **Adaptive bitrate streaming** - Automatic quality adjustment based on network conditions
- 🎯 **Auto level capping** - Restrict maximum quality based on resolution or bitrate
- 🎬 **Multi-audio track support** - Switch between different audio languages
- 📝 **Subtitle/Caption support** - Multiple subtitle tracks with language selection
- 📈 **Analytics tracking** - Built-in watch time, bandwidth, and error tracking
- 🎚️ **Quality level switching** - Manual or automatic quality control
- ⚡ **Playback rate control** - Speed up or slow down playback
- 🎨 **Customizable branding** - Server-controlled theming and messaging
- 📺 **VAST/VPAID ads support** - Pre-roll, mid-roll, and post-roll advertisements
- 🔄 **Automatic error recovery** - Smart retry strategies for recoverable errors
- 🎯 **TypeScript support** - Fully typed API for better development experience
- 🌐 **Comprehensive event system** - React to all player state changes
- 🔐 **Secure playback** - Session management and authentication
- 💾 **Local storage integration** - Secure device registration

## Installation

```bash
npm install @xnstream/player-sdk
```

## Quick Start

```typescript
import { StreamPlayer } from '@xnstream/player-sdk';

// Create player instance
const player = await StreamPlayer.create({
  appId: 'your-app-id',
  stream_code: 'your-stream-code',
  containerId: 'player-container',
  autoPlay: true,
  startAt: 0
});

// Initialize the player
await player.initialize();

// Listen for events
player.on('ready', () => {
  console.log('Player is ready');
});

player.on('playing', () => {
  console.log('Playback started');
});

player.on('error', (error) => {
  console.error('Playback error:', error);
});

// Play the stream
await player.play();
```

## API Reference

### StreamPlayer

The main player class that handles video playback and streaming.

#### Core Methods

##### Player Lifecycle
- `create(options: StreamPlayerOptions): Promise<StreamPlayer>` - Create player instance
- `initialize(): Promise<void>` - Initialize and load the stream
- `destroy(): void` - Cleanup and destroy player instance

##### Playback Control
- `play(): Promise<void>` - Start playback
- `pause(): void` - Pause playback
- `seek(time: number): void` - Seek to specific time
- `rewind(offset?: number): void` - Rewind by offset (default: 10s)
- `fastForward(offset?: number): void` - Fast forward by offset (default: 10s)
- `seekToLive(): void` - Jump to live edge (for live streams)

##### Volume Control
- `setVolume(volume: number): void` - Set volume (0.0 to 1.0)
- `getVolume(): number` - Get current volume
- `toggleMute(): void` - Toggle mute state
- `isMuted(): boolean` - Check if muted

##### Playback Rate
- `setPlaybackRate(rate: number): void` - Set playback speed (e.g., 0.5, 1.0, 2.0)
- `getPlaybackRate(): number` - Get current playback rate

##### Quality Control
- `switchLevel(level: number): void` - Switch to specific quality level
- `getLevels(): Level[]` - Get available quality levels
- `getCurrentLevel(): number` - Get current quality level ID
- `isAutolevelEnabled(): boolean` - Check if automatic quality switching is enabled

##### Audio Tracks
- `getAudioTracks(): AudioTrack[]` - Get available audio tracks
- `getCurrentAudioTrack(): number` - Get current audio track ID
- `setAudioTrack(trackId: number): void` - Switch audio track

##### Subtitle Tracks
- `getSubtitleTracks(): SubtitleTrack[]` - Get available subtitle tracks
- `getCurrentSubtitleTrack(): number` - Get current subtitle track ID (-1 = disabled)
- `setSubtitleTrack(trackId: number): void` - Switch subtitle track (-1 to disable)

##### Configuration
- `getPlayerConfig(): PlayerConfig | undefined` - Get player configuration from backend

#### Events

##### Playback Events
- `ready` - Player is ready to play
- `playing` - Playback has started
- `paused` - Playback is paused
- `ended` - Playback has finished
- `buffering` - Player is buffering

##### Progress Events
- `onProgressUpdate: (progress: ProgressUpdate) => void` - Progress updates (every 300ms)
- `onDataLoaded: (duration: number) => void` - Video metadata loaded

##### Quality Events
- `onLevelsLoaded: (levels: Level[]) => void` - Quality levels detected
- `onLevelSwitch: (level: number) => void` - Quality level changed
- `onBufferUpdated: (buffer: BufferAppendedData) => void` - Buffer updated

##### Audio Track Events
- `onAudioTracksLoaded: (tracks: AudioTrack[]) => void` - Audio tracks detected
- `onAudioTrackSwitch: (trackId: number) => void` - Audio track switched

##### Subtitle Track Events
- `onSubtitleTracksLoaded: (tracks: SubtitleTrack[]) => void` - Subtitle tracks detected
- `onSubtitleTrackSwitch: (trackId: number) => void` - Subtitle track switched

##### Control Events
- `volumechange: (volume: number) => void` - Volume changed
- `ratechange: (rate: number) => void` - Playback rate changed

##### Resource Events
- `onResourceChange: (resource: Resource) => void` - Stream resource loaded

##### Error Events
- `error: (error: UnifiedError) => void` - Error occurred (with retry information)

##### Ad Events
- `onAdStart: () => void` - Ad playback started
- `onAdEnd: () => void` - Ad playback completed
- `onAdProgress: (progress) => void` - Ad playback progress
- `onAdError: (error: UnifiedError) => void` - Ad error occurred
- `onAdClick: () => void` - User clicked on ad
- `onAdFirstQuartile: () => void` - Ad 25% complete
- `onAdSecondQuartile: () => void` - Ad 50% complete
- `onAdThirdQuartile: () => void` - Ad 75% complete

## Configuration

### StreamPlayerOptions

```typescript
interface StreamPlayerOptions {
  appId: string;                      // Your application ID
  stream_code: string;                // Stream identifier
  containerId: string;                // DOM element ID for player
  autoPlay?: boolean;                 // Auto-start playback (default: false)
  startAt?: number;                   // Start position in seconds
  lowlatenctMode?: boolean;           // Enable low-latency mode for live streams
  context?: Record<string, any>;      // Custom context for analytics
  adOptions?: IAdsRequest;            // Advertisement configuration
  levelCapping?: LevelCappingOptions; // Auto quality level capping options
}

interface LevelCappingOptions {
  width?: number;                     // Maximum video width
  height?: number;                    // Maximum video height
  bitrate?: number;                   // Maximum bitrate in bps
}
```

### PlayerConfig (Backend Configuration)

The player receives configuration from the backend that controls branding and behavior:

```typescript
interface PlayerConfig {
  controls?: {
    viewsCounter?: boolean;          // Show view counter
    disablePause?: boolean;          // Disable pause button
    liveRewind?: boolean;            // Enable rewind for live streams
    autoPlay?: boolean;              // Override client autoPlay setting
    liveIcon?: boolean;              // Show live indicator
  };
  branding?: {
    theme?: string;                  // Theme name
    colorScheme?: string;            // Color scheme
    pageTitle?: string;              // Custom page title
    offlineMessage?: string;         // Custom offline message title
    offlineDescription?: string;     // Custom offline message description
  };
}
```

The backend configuration takes precedence over client options and provides custom error messages.

## Advanced Features

### Audio and Subtitle Tracks

The player supports multiple audio and subtitle tracks:

```typescript
// Listen for available tracks
player.on('onAudioTracksLoaded', (tracks) => {
  console.log('Audio tracks:', tracks);
  // Display track selector UI
});

player.on('onSubtitleTracksLoaded', (tracks) => {
  console.log('Subtitle tracks:', tracks);
  // Display subtitle selector UI
});

// Switch tracks
player.setAudioTrack(1);      // Switch to audio track 1
player.setSubtitleTrack(0);   // Enable subtitle track 0
player.setSubtitleTrack(-1);  // Disable subtitles

// React to track changes
player.on('onAudioTrackSwitch', (trackId) => {
  console.log('Audio switched to:', trackId);
});

player.on('onSubtitleTrackSwitch', (trackId) => {
  console.log('Subtitle switched to:', trackId);
});
```

### Error Handling

The SDK includes comprehensive error handling with automatic retry strategies:

```typescript
player.on('error', (error) => {
  console.log('Error type:', error.type);
  console.log('Error context:', error.context);
  console.log('Is retryable:', error.isRetryable);
  console.log('Retry strategy:', error.retryStrategy);
  console.log('Severity:', error.severity);
  
  // All errors are automatically retried if retryable
  // Custom error messages from backend are displayed
});
```

**Error Types:**
- `validation` - Input validation errors
- `client` - Client-side errors (4xx)
- `server` - Server-side errors (5xx)
- `network` - Network connectivity issues
- `media` - Media playback errors
- `storage` - Local storage errors
- `security` - Security/encryption errors
- `ads` - Advertisement errors

**Error Contexts:**
- `unauthorized`, `forbidden`, `notFound`
- `sourceOffline`, `sessionExpired`
- `geoRestricted`, `deviceLimitReached`, `subscriptionRequired`
- `mediaUnavailable`, `manifestError`, `fragmentError`
- `networkTimeout`, `networkUnavailable`
- And more...

All errors include retry strategies with exponential backoff where appropriate.

### Auto Level Capping

The SDK allows you to restrict the maximum quality level that can be selected automatically by the adaptive bitrate algorithm. This is useful for:
- Limiting bandwidth usage on mobile networks
- Matching video quality to device capabilities
- Preventing high-resolution playback on smaller screens

```typescript
// Cap quality to 1080p (Full HD)
const player = await StreamPlayer.create({
  appId: 'my-app-id',
  stream_code: 'my-stream',
  containerId: 'player-container',
  levelCapping: {
    width: 1920,
    height: 1080
  }
});

// Cap quality by bitrate (e.g., 5 Mbps)
const player = await StreamPlayer.create({
  appId: 'my-app-id',
  stream_code: 'my-stream',
  containerId: 'player-container',
  levelCapping: {
    bitrate: 5000000  // 5 Mbps in bits per second
  }
});

// Cap to 720p for mobile devices
const isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
const player = await StreamPlayer.create({
  appId: 'my-app-id',
  stream_code: 'my-stream',
  containerId: 'player-container',
  levelCapping: isMobile ? {
    width: 1280,
    height: 720
  } : undefined
});
```

**How it works:**
- The player finds the highest quality level that doesn't exceed your specified constraints
- If both `width/height` and `bitrate` are provided, `width/height` takes priority
- The cap only affects automatic quality selection (ABR), not manual quality switching
- If no level matches the constraints, no capping is applied

### Custom Error Messages

The player displays custom error messages from the backend configuration:

```typescript
// Backend provides custom offline message via PlayerConfig
{
  "branding": {
    "offlineMessage": "Stream Temporarily Unavailable",
    "offlineDescription": "We're experiencing technical difficulties. Check back soon."
  }
}
```

### Analytics

The SDK includes built-in analytics tracking for:
- **Watch time** - Accurate playback duration tracking
- **Bandwidth usage** - Data consumption monitoring
- **Error tracking** - Comprehensive error reporting
- **Quality level changes** - Bitrate switching events
- **Delivery metrics** - Fragment loading performance
- **Ad analytics** - Advertisement interaction tracking

## Type Definitions

The SDK is fully typed. Key types include:

```typescript
import type {
  StreamPlayer,
  StreamPlayerOptions,
  LevelCappingOptions,
  StreamPlayerEvents,
  Level,
  AudioTrack,
  SubtitleTrack,
  PlayerConfig,
  UnifiedError,
  ProgressUpdate,
  Resource
} from '@xnstream/player-sdk';
```

## Browser Support

- ✅ Chrome (latest)
- ✅ Firefox (latest)
- ✅ Safari (latest)
- ✅ Edge (latest)
- ✅ Mobile browsers (iOS Safari, Chrome Mobile)

Requires support for:
- ES2015+ JavaScript
- Promises/async-await
- Fetch API
- HLS.js compatibility

## Example: Complete Player Setup

```typescript
import { StreamPlayer } from '@xnstream/player-sdk';
import type { AudioTrack, SubtitleTrack, UnifiedError } from '@xnstream/player-sdk';

async function setupPlayer() {
  // Create player
  const player = await StreamPlayer.create({
    appId: 'my-app-id',
    stream_code: 'my-stream',
    containerId: 'player-container',
    autoPlay: false,
    startAt: 0,
    context: {
      user_id: '12345',
      session_type: 'premium'
    }
  });

  // Setup event listeners
  player.on('ready', () => {
    console.log('Player ready');
  });

  player.on('onLevelsLoaded', (levels) => {
    console.log('Quality levels:', levels);
  });

  player.on('onAudioTracksLoaded', (tracks) => {
    // Build audio track selector
    tracks.forEach(track => {
      console.log(`Audio: ${track.name} (${track.lang})`);
    });
  });

  player.on('onSubtitleTracksLoaded', (tracks) => {
    // Build subtitle track selector
    tracks.forEach(track => {
      console.log(`Subtitle: ${track.name} (${track.lang})`);
    });
  });

  player.on('error', (error: UnifiedError) => {
    console.error('Error:', error.message);
    if (error.isRetryable) {
      console.log('Will retry automatically');
    }
  });

  player.on('onProgressUpdate', (progress) => {
    console.log(`Position: ${progress.position}s`);
  });

  // Initialize and play
  await player.initialize();
  await player.play();

  // Example: Switch to Spanish audio
  const spanishAudio = player.getAudioTracks().find(t => t.lang === 'es');
  if (spanishAudio) {
    player.setAudioTrack(spanishAudio.id);
  }

  // Example: Enable English subtitles
  const englishSubs = player.getSubtitleTracks().find(t => t.lang === 'en');
  if (englishSubs) {
    player.setSubtitleTrack(englishSubs.id);
  }
}

setupPlayer();
```

## License

MIT

## Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

## Support

For issues and questions, please visit our GitHub repository.

## Repository

This project is hosted on GitHub: [https://github.com/caltek/xPlayerSDKJS](https://github.com/caltek/xPlayerSDKJS) 