# PRESTOplay Media Player Package

## 1. Preface / Introduction

### a. Background and Purpose
This package provides a customizable, modular web-based media player built on top of [PRESTOplay](https://castlabs.com/products/prestoplay/). It supports advanced features such as DRM, adaptive streaming (DASH, HLS, Smooth), and robust subtitle and audio-only handling. The code is designed to quickly integrate secure video playback into web applications, with a modern component-based architecture for easy customization and extensibility.

### b. Scope and Learning Objectives
- Understand the structure and flow of the player code.
- Learn how to configure and modify variables (such as video source, DRM credentials, and UI options) to achieve the desired playback.
- Gain insight into extending or customizing the player for your own needs.
- Learn about the component-based architecture and how to customize individual UI components.

---

## 2. Environment and Prerequisites

### a. Programming Language, Libraries, and Frameworks
- **Language:** JavaScript (ES6+)
- **Main Library:** [PRESTOplay](https://castlabs.com/prestoplay/web-apps/) (`@castlabs/prestoplay`)
- **Bundler:** Webpack
- **Build Tools:** Babel (for ES6+ transpilation)

### b. Execution Environment
- **OS:** macOS, Windows, iOS, Android
- **Browser:** Modern browsers (Chrome, Edge, Safari, Firefox)

### c. Installation and Setup
1. Download the zipped package at: [https://dr.orca.jp/PRESTOplay-drm-player/{uploaded-date}/prestoplay-package-zipped](https://dr.orca.jp/PRESTOplay-drm-player/{uploaded-date}/prestoplay-package-zipped)
2. Unzipped the package and run:
   ```sh
   npm run build
   npm link
   ```
3. Then navigate to your frontend project source, use this command to install the package:
   ```sh
   npm link prestoplay-drm-player
   ```

---

## 3. Overview of the Code

### a. Final Output
The final product is a JS package that initializes a video player that supports:
- DRM-protected and non-protected video playback
- Customizable component-based UI controls (play/pause, volume, progress, subtitles, quality, playback speed, fullscreen)
- Subtitle selection and rendering (VTT, TTML, HTML Cue)
- Responsive UI with modern design
- Audio-only content detection and UI adaptation
- Loading indicators
- Fullscreen support
- Time display and progress tracking

### b. Directory Structure
```
prestoplay-package/
  ├── dist/                          # Built output files
  │   ├── prestoplay-drm-player.min.js
  │   └── prestoplay-drm-player.min.js.LICENSE.txt
  ├── src/
  │   ├── index.js                   # Main player logic and class
  │   ├── assets/
  │   │   └── icons/
  │   │       ├── index.js           # SVG icons as string constants
  │   │       └── *.svg              # Individual SVG icon files
  │   ├── components/
  │   │   ├── control-bar/
  │   │   │   ├── control-bar.js     # Main control bar orchestrator
  │   │   │   ├── play-pause/
  │   │   │   │   └── play-pause.js  # Play/pause/replay button component
  │   │   │   ├── volume-control/
  │   │   │   │   ├── volume-control.js
  │   │   │   │   └── volume-control.css
  │   │   │   ├── progress/
  │   │   │   │   ├── progress.js    # Progress bar and timeline
  │   │   │   │   └── progress.css
  │   │   │   ├── time-display/
  │   │   │   │   ├── time-display.js # Current time / duration display
  │   │   │   │   └── time-display.css
  │   │   │   ├── subtitle-toggle/
  │   │   │   │   ├── subtitle-toggle.js # Toggle subtitle on/off
  │   │   │   │   └── subtitle-toggle.css
  │   │   │   ├── settings/
  │   │   │   │   ├── settings.js    # Settings menu (quality, speed, subtitles)
  │   │   │   │   └── settings.css
  │   │   │   └── fullscreen/
  │   │   │       ├── fullscreen.js  # Fullscreen control
  │   │   │       ├── fullscreen-engine.js # Fullscreen API wrapper
  │   │   │       └── fullscreen.css
  │   │   ├── loading/
  │   │   │   ├── loading.js         # Loading spinner component
  │   │   │   └── loading.css
  │   │   └── top-bar/
  │   │       ├── top-bar.js         # Top bar with title and share button
  │   │       └── top-bar.css
  │   ├── styles/
  │   │   ├── index.css              # Main styles entry point
  │   │   ├── base.css               # Base styles and resets
  │   │   ├── controls.css           # Control bar styles
  │   │   └── responsive.css         # Responsive design styles
  │   └── utils/
  │       ├── buttonFactory.js       # Button creation utility
  │       ├── common.js              # Common utility functions
  │       └── renditionHelper.js     # Video quality/rendition helpers
  ├── package.json                   # Project metadata and dependencies
  ├── webpack.config.js              # Webpack build configuration
  └── README.md                      # Project documentation
```

**File Roles:**
- `src/index.js`: Main entry point, player class, and orchestration.
- `src/components/control-bar/`: Modular control bar components (play/pause, volume, progress, etc.).
- `src/components/loading/`: Loading spinner indicator.
- `src/components/top-bar/`: Top bar with title and share functionality.
- `src/assets/icons/`: SVG icons exported as string constants for compatibility.
- `src/styles/`: Modular CSS styles organized by purpose.
- `src/utils/`: Utility functions for buttons, quality formatting, etc.

---

## 4. Step-by-Step Code Explanation

### 4.1. Player Initialization
**Purpose:**
Create and configure a PRESTOplay player instance, attach it to the DOM, and set up custom controls.

**Example Initialization:**
```js
const player = new PrestoPlayDRMPlayer({
  containerId: "video-container", // (required) id of the container div
  source: "https://example.com/video.mpd", // (required) video source URL
  prestoplayCred: {                // (required) PRESTOplay credentials
    license: "<your-license>",    // (required) PRESTOplay license string
    viewerId: "<your-viewer-id>"  // (required) PRESTOplay merchant UUID
  },
  width: "100%",                   // (optional) video width
  height: "auto",                  // (optional) video height
  isDRM: true,                     // (optional) enable DRM (default: false)
  drmConfig: {                     // (optional) DRM config object (required if isDRM is true)
    env: "DRMtoday_STAGING",       // (optional) DRM environment: "DRMtoday" (Production) or "DRMtoday_STAGING" (Staging)
    customData: {
      userId: "rental1",
      sessionId: "p0",
      merchant: "your-merchant-id",
      assetId: "Widevine-Test"
    }
  },
  autoplay: false,                 // (optional) autoplay video
  loop: false,                     // (optional) loop video
  muted: false                     // (optional) start muted
});
```

**Required Parameters:**
- `containerId`: The id of the HTML element to attach the player to.
- `source`: The video source URL (supports DASH `.mpd`, HLS `.m3u8`, Smooth Streaming).
- `prestoplayCred.license`: PRESTOplay license string obtained from the PRESTOplay license page.
- `prestoplayCred.viewerId`: PRESTOplay merchant UUID (universally unique identifier).

**Optional Parameters:**
- `width`, `height`: Video dimensions (default: `width: "100%"`, `height: "auto"`).
- `isDRM`: Enable DRM protection (default: `false`).
- `drmConfig`: DRM configuration object (see example above).
- `autoplay`, `loop`, `muted`: Standard video options.

**Code Snippet (Class):**
```js
class PrestoPlayDRMPlayer {
  constructor(options = {}) {
    if (!options.containerId) throw new Error("containerId is required");
    if (!options.source) throw new Error("source is required");
    if (!options.prestoplayCred?.license || !options.prestoplayCred?.viewerId)
      throw new Error("license and viewerId is required to initialize a PRESTOplay player");
    // ...
  }
}
```

**About PRESTOplay License and Merchant:**
```js
this.player = new clpp.Player(this.video, this.options.prestoplayCred);
```
- `prestoplayCred.license` is the license string you obtain from the PRESTOplay license page. **Note:** This license is only valid for the domains that have been registered with the license itself. If you use it on an unregistered domain, playback will fail.
- `prestoplayCred.viewerId` is your merchant UUID (universally unique identifier) provided by PRESTOplay.

**Explanation:**
- The constructor validates required options and sets up the player container.
- Throws clear errors if required parameters are missing.
- Automatically initializes custom UI components after video metadata is loaded.

**Pitfall:**
- Forgetting to provide `containerId`, `source`, or PRESTOplay license/credentials will cause initialization to fail.
- Using a license on an unregistered domain will cause DRM playback to fail.

---

### 4.2. DRM and Source Loading
**Purpose:**
Configure DRM and load the video source.

**Code Snippet:**
```js
const options = this.getPrestoPlayOptions();
this.player.load(options);
```

The `getPrestoPlayOptions()` method prepares the options:
```js
getPrestoPlayOptions() {
  const options = { ...this.options };
  delete options.containerId;
  delete options.isDRM;

  if (this.options.isDRM && this.options.drmConfig) {
    options.drm = options.drmConfig;
  }
  delete options.drmConfig;

  return options;
}
```

**Explanation:**
- The `load` method loads the video, sets up the player, and applies DRM if needed.
- DRM configuration is only included when `isDRM` is `true`.
- Supports multiple streaming formats: DASH, HLS, and Smooth Streaming.

**Pitfall:**
- DRM misconfiguration will result in playback errors.
- Ensure DRM credentials match the content's DRM system (Widevine, PlayReady, FairPlay).

---

### 4.3. Component-Based UI Architecture
**Purpose:**
The player uses a modular, component-based architecture for UI controls. Each component is self-contained and can be easily customized or extended.

**Component Structure:**
```js
import { ControlBarComponent } from "./components/control-bar/control-bar.js";
import { LoadingComponent } from "./components/loading/loading.js";
import { TopBarComponent } from "./components/top-bar/top-bar.js";

// Components are initialized after metadata is loaded
this.loadingComponent = new LoadingComponent(this.player);
this.controlBarComponent = new ControlBarComponent(
  this.player,
  this.video,
  this.container
);
```

**Control Bar Components:**

1. **PlayPauseComponent**: Handles play, pause, and replay functionality.
   - Automatically switches between play, pause, and replay icons based on player state.

2. **VolumeControlComponent**: Manages volume and mute functionality.
   - Shows different icons based on volume level (muted, low, high).
   - Includes a volume slider (hidden by default, shown on hover for desktop).

3. **ProgressComponent**: Displays and controls video timeline.
   - Shows buffered progress and current position.
   - Allows seeking by clicking on the progress bar.

4. **TimeDisplayComponent**: Shows current time and total duration.
   - Format: `currentTime / totalDuration` (e.g., `01:23 / 05:45`).

5. **SubtitleToggleComponent**: Toggles subtitle visibility on/off.
   - Works in conjunction with SettingsComponent for subtitle selection.

6. **SettingsComponent**: Provides a settings menu with submenus:
   - **Playback Speed**: Adjust playback speed (0.5x, 0.75x, 1x, 1.25x, 1.5x, 2x).
   - **Quality**: Select video quality/rendition (Auto, 1080p, 720p, etc.).
   - **Subtitles**: Select subtitle track language.

7. **FullscreenComponent**: Handles fullscreen mode.
   - Supports both native fullscreen API and fullscreen-like behavior.
   - Works across different browsers and devices.

**LoadingComponent**: Shows a loading spinner during:
- Video loading
- Seeking operations
- Buffering states

**TopBarComponent**: (Optional, currently commented out)
- Displays video title
- Includes a share button for copying video URL

**Explanation:**
- Each component is a class with `init()`, `cleanup()`, and lifecycle methods.
- Components communicate with the player through PRESTOplay's event system.
- Components can be easily extended or replaced without affecting others.

**Pitfall:**
- When customizing components, ensure you maintain the component interface (`container`, `cleanup()` method) for proper integration.

---

### 4.4. Audio-Only Content Detection
**Purpose:**
Automatically detect audio-only content and adjust the UI accordingly.

**Code Snippet:**
```js
this.player.on(clpp.events.LOADEDMETADATA, () => {
  const trackManager = this.player.getTrackManager();
  const videoTracks = trackManager.getVideoTracks();
  const audioTracks = trackManager.getAudioTracks();
  if (audioTracks.length > 0 && videoTracks.length === 0) {
    this.container.classList.add("audio-only");
  }
});
```

**Explanation:**
- The player checks for video tracks after metadata is loaded.
- If no video tracks are found but audio tracks exist, it adds the `audio-only` CSS class.
- The UI can then be styled differently for audio-only content.

**Pitfall:**
- If you customize the UI, ensure you handle the `audio-only` class correctly in your CSS.

---

### 4.5. Subtitle Support
**Purpose:**
Support multiple subtitle formats and provide subtitle selection UI.

**Supported Formats:**
- VTT (WebVTT)
- TTML
- HTML Cue

**Code Snippet:**
```js
// Subtitle tracks are automatically detected via PRESTOplay
const textTracks = this.player.getTrackManager().getTextTracks();

// SettingsComponent provides UI for subtitle selection
this.subtitles = textTracks.map((track) => ({
  value: track.language,
  label: track.label || track.language || "Unknown",
  language: track.language || "unknown",
}));
```

**Explanation:**
- PRESTOplay automatically detects and loads subtitle tracks from the manifest.
- The SettingsComponent lists all available subtitle tracks.
- Users can select subtitles through the settings menu or toggle them on/off with the subtitle button.

**Pitfall:**
- Not all browsers support all subtitle formats. Test with your target audience.
- Some subtitle formats may require specific PRESTOplay components to be installed.

---

### 4.6. Icon System
**Purpose:**
SVG icons are exported as string constants to ensure compatibility across different build systems (including Vue.js, React, etc.).

**Code Snippet:**
```js
// src/assets/icons/index.js
export const playIcon = `<svg>...</svg>`;
export const pauseIcon = `<svg>...</svg>`;
// ... other icons
```

**Usage:**
```js
import { playIcon, pauseIcon } from "../assets/icons/index.js";
button.innerHTML = playIcon;
```

**Explanation:**
- Icons are stored as SVG string constants rather than imported as files.
- This ensures they work correctly when the library is integrated into Vue.js, React, or other frameworks where SVG imports might be handled differently.
- Icons are set via `innerHTML` for direct DOM insertion.

**Pitfall:**
- If you add new icons, ensure they follow the same pattern (string constants) for consistency.

---

## 5. Use of Visuals and Diagrams

### 5.1. User Authorization Callback Implementation

<div style="text-align: center; margin: 20px 0;">
  <img src="manual/user-authorization-callback.png" alt="User Authorization Callback Flow" style="max-width: 100%; height: auto; border: 1px solid #ddd; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1);">
</div>

**Authentication Flow:**

- **Authentication**: The user's Client device authenticates with Your session service.
- **Session ID Generation**: Upon successful authentication, Your session service generates and sends a unique sessionId back to the Client device.
- **License Request**: The Client device sends a license request to the DRMtoday license server. This request includes the sessionId in its custom data payload.
- **Authorization Callback**: The DRMtoday license server pauses processing and sends a real-time "User authorization callback" to Your auth service, forwarding the sessionId.
- **Session Validation**: Your auth service validates the sessionId to confirm the user is active and authorized to view the content.
- **Customer Rights Token**: After successful validation, Your auth service sends a Customer rights token (containing playback rules) back to the DRMtoday license server.
- **License Creation**: DRMtoday uses the Customer Rights Token to create the final, secure license.
- **License Delivery**: The DRMtoday license server sends the final license back to the Client device, which can then decrypt and play the video.

### 5.2. Authorization Methods Comparison

| Feature | Upfront Authorization Token | User Authorization Callback |
|---|---|---|
| **Core Idea** | The backend server pre-authorizes the user and gives the player a "ticket" (the token) to present to DRMtoday. | DRMtoday receives a request and asks the backend server in real-time, "Is this user allowed to watch this?" |
| **Workflow** | One-way trip: Client -> The Backend Server -> Client -> DRMtoday | Round trip: Client -> DRMtoday -> The Backend Server -> DRMtoday -> Client |
| **Backend Responsibility** | Generate a secure, signed, and short-lived token before the license request is made. | Implement a server endpoint that can receive a real-time callback from DRMtoday and respond instantly. |
| **Complexity** | Simpler for the DRM provider (DRMtoday), but requires the backend server to handle token generation logic. | More complex backend logic is required to handle the real-time callback, but it gives you more direct control. |
| **Latency** | Potentially lower latency, as DRMtoday can issue the license immediately after validating the token without an extra network hop. | Slightly higher latency due to the additional real-time callback from DRMtoday to the backend server. |
| **Real-time Control** | Less real-time control. A user's rights are based on when the token was generated (tokens are valid for 10 minutes). | Maximum real-time control. You can deny a license request the instant a user's subscription expires. |
| **Best For** | Standard VOD playback, SPAs, and situations where you want to reduce the real-time load on the backend server. | Live streaming, pay-per-view events, or systems where immediate, up-to-the-second authorization is critical. |

---

## 6. Supplementary Notes and Advanced Topics

### a. Better Design Practices
- **Component-Based Architecture:**
  - Each UI control is a separate, self-contained component class.
  - Components communicate through PRESTOplay's event system.
  - Easy to extend, customize, or replace individual components.
- **Separation of Concerns:**
  - Logic for controls, DRM, and player management are split into separate modules.
  - CSS is modularized by component and purpose.
  - Utilities are separated into dedicated utility modules.
- **Extensibility:**
  - You can add new controls by creating new component classes following the same pattern.
  - Components expose `container` and `cleanup()` methods for integration.
- **Framework Compatibility:**
  - SVG icons are exported as strings to work with Vue.js, React, and other frameworks.
  - The library is built as a UMD module for maximum compatibility.

### b. Advanced Considerations
- **Security:**
  - Never expose DRM credentials in client-side code for production.
  - Use HTTPS for all media and license requests.
  - Validate and sanitize all user inputs.
- **Performance:**
  - Components are only initialized after video metadata is loaded to reduce initial load time.
  - Event listeners are properly cleaned up when the player is disposed.
- **Browser Compatibility:**
  - The library handles browser differences (e.g., fullscreen API, touch devices).
  - Some features may have limited support on certain browsers (e.g., DRM support varies by browser).

---

## 7. Error Handling and Debugging Tips

### a. Common Error Messages
- **"Container with id ... not found":**
  - Check your HTML and ensure the container element exists before initializing the player.
  - Ensure the container ID matches exactly (case-sensitive).
- **"license and viewerId is required":**
  - Provide valid PRESTOplay credentials in the `prestoplayCred` option.
  - Ensure both `license` and `viewerId` are provided.
- **"drmConfig parameter is required if isDRM is true":**
  - When `isDRM` is `true`, you must provide a valid `drmConfig` object.
- **DRM playback errors:**
  - Check your license server, network connectivity, and browser DRM support.
  - Verify DRM credentials match the content's DRM system.
  - Check browser console for detailed error messages.

### b. Debugging Tools
- **Browser DevTools:**
  - Use the Console tab to inspect errors and player events.
  - Use the Network tab to monitor license requests and media segments.
  - Use the Elements tab to inspect the player DOM structure.
- **PRESTOplay Logs:**
  - Enable verbose logging in PRESTOplay configuration for more details.
  - Check browser console for PRESTOplay-specific error messages.
- **Component Debugging:**
  - Each component exposes its `container` element for DOM inspection.
  - Event listeners can be monitored through browser DevTools Event Listeners panel.

---

## 8. API Reference

### PrestoPlayDRMPlayer Methods

#### `play()`
Starts video playback.
```js
player.play();
```

#### `pause()`
Pauses video playback.
```js
player.pause();
```

#### `stop()`
Stops video playback and seeks to the beginning.
```js
player.stop();
```

#### `dispose()`
Cleans up the player instance, removes event listeners, and destroys the PRESTOplay player.
```js
player.dispose();
```

### Accessing PRESTOplay Player
The underlying PRESTOplay player instance is available via:
```js
player.player // Access to clpp.Player instance
```

You can use all PRESTOplay APIs through this instance, including:
- `player.player.getVolume()` / `player.player.setVolume()`
- `player.player.getCurrentTime()` / `player.player.seek()`
- `player.player.getTrackManager()` for tracks, renditions, etc.

---

## 9. Building and Distribution

### Build Commands
```sh
# Production build
npm run build

# Development build with watch mode
npm run dev
```

### Output
- Built files are output to `dist/prestoplay-drm-player.min.js`
- The library is exported as a UMD module with global name `PrestoPlayDRMPlayer`
- Default export is the `PrestoPlayDRMPlayer` class

### Integration in Projects
The library can be used in:
- Vanilla JavaScript projects
- Vue.js projects (with proper SVG handling)
- React projects
- Angular projects
- Any project that supports ES6 modules or UMD bundles

---

## 10. License and Support

- **License:** MIT
- **Author:** Orca
- **PRESTOplay:** Licensed by CastLabs

For issues and support, please refer to the repository's issue tracker.
