@obipascal/player 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -5,6 +5,7 @@ A modern, feature-rich HLS video player SDK for educational platforms with Cloud
|
|
|
5
5
|
## ✨ Features
|
|
6
6
|
|
|
7
7
|
### Core Playback
|
|
8
|
+
|
|
8
9
|
- 🎬 **HLS Streaming**: Full HLS.js support with adaptive bitrate streaming
|
|
9
10
|
- 🔒 **CloudFront Integration**: Native support for CloudFront signed cookies and S3-hosted videos
|
|
10
11
|
- 🎯 **Skip Controls**: 10-second forward/backward skip with circular arrow buttons
|
|
@@ -13,11 +14,13 @@ A modern, feature-rich HLS video player SDK for educational platforms with Cloud
|
|
|
13
14
|
- 🎛️ **Playback Rate**: Adjustable speed (0.5x - 2x)
|
|
14
15
|
|
|
15
16
|
### Subtitle & Accessibility
|
|
17
|
+
|
|
16
18
|
- 📝 **Subtitle Support**: Full subtitle/caption support with programmatic API
|
|
17
19
|
- 🌐 **Multi-language**: Support for multiple subtitle tracks with language selection
|
|
18
20
|
- ♿ **Accessibility**: WCAG compliant with keyboard navigation
|
|
19
21
|
|
|
20
22
|
### UI & Controls
|
|
23
|
+
|
|
21
24
|
- 🎨 **Modern UI Design**: Beautiful controls with blur effects, gradients, and smooth animations
|
|
22
25
|
- 🖱️ **Smart Controls**: Auto-hide on inactivity, fade on hover
|
|
23
26
|
- 📍 **Sticky Controls**: Optional persistent controls (toggle in settings)
|
|
@@ -27,6 +30,7 @@ A modern, feature-rich HLS video player SDK for educational platforms with Cloud
|
|
|
27
30
|
- 🎨 **Custom Theming**: Full CSS variable theming with 8 customizable properties
|
|
28
31
|
|
|
29
32
|
### Developer Experience
|
|
33
|
+
|
|
30
34
|
- ⚛️ **React Support**: Component, Hook, and Context Provider patterns
|
|
31
35
|
- 🔧 **TypeScript**: Full TypeScript support with comprehensive type definitions
|
|
32
36
|
- 📊 **Analytics & QoE**: Built-in analytics tracking and Quality of Experience metrics
|
|
@@ -37,13 +41,13 @@ A modern, feature-rich HLS video player SDK for educational platforms with Cloud
|
|
|
37
41
|
## 📦 Installation
|
|
38
42
|
|
|
39
43
|
```bash
|
|
40
|
-
npm install @
|
|
44
|
+
npm install @obipascal/player hls.js
|
|
41
45
|
```
|
|
42
46
|
|
|
43
47
|
Or with yarn:
|
|
44
48
|
|
|
45
49
|
```bash
|
|
46
|
-
yarn add @
|
|
50
|
+
yarn add @obipascal/player hls.js
|
|
47
51
|
```
|
|
48
52
|
|
|
49
53
|
## 🚀 Quick Start
|
|
@@ -60,7 +64,7 @@ yarn add @wontum/player hls.js
|
|
|
60
64
|
<div id="player-container"></div>
|
|
61
65
|
|
|
62
66
|
<script type="module">
|
|
63
|
-
import { WontumPlayer } from "@
|
|
67
|
+
import { WontumPlayer } from "@obipascal/player"
|
|
64
68
|
|
|
65
69
|
const player = new WontumPlayer({
|
|
66
70
|
src: "https://media.example.com/video/playlist.m3u8",
|
|
@@ -98,7 +102,7 @@ yarn add @wontum/player hls.js
|
|
|
98
102
|
player.on("timeupdate", (event) => {
|
|
99
103
|
console.log("Current time:", event.data.currentTime)
|
|
100
104
|
})
|
|
101
|
-
|
|
105
|
+
|
|
102
106
|
// Programmatic subtitle control
|
|
103
107
|
player.enableSubtitles(0) // Enable first subtitle track
|
|
104
108
|
player.toggleSubtitles() // Toggle subtitles on/off
|
|
@@ -110,7 +114,7 @@ yarn add @wontum/player hls.js
|
|
|
110
114
|
### React Component
|
|
111
115
|
|
|
112
116
|
```tsx
|
|
113
|
-
import { WontumPlayerReact } from "@
|
|
117
|
+
import { WontumPlayerReact } from "@obipascal/player"
|
|
114
118
|
|
|
115
119
|
function VideoPlayer() {
|
|
116
120
|
return (
|
|
@@ -146,7 +150,7 @@ function VideoPlayer() {
|
|
|
146
150
|
### React Hook (Custom Controls)
|
|
147
151
|
|
|
148
152
|
```tsx
|
|
149
|
-
import { useWontumPlayer } from "@
|
|
153
|
+
import { useWontumPlayer } from "@obipascal/player"
|
|
150
154
|
|
|
151
155
|
function CustomPlayer() {
|
|
152
156
|
const { containerRef, player, state } = useWontumPlayer({
|
|
@@ -182,7 +186,7 @@ function CustomPlayer() {
|
|
|
182
186
|
### React Context Provider
|
|
183
187
|
|
|
184
188
|
```tsx
|
|
185
|
-
import { WontumPlayerProvider, useWontumPlayerContext } from "@
|
|
189
|
+
import { WontumPlayerProvider, useWontumPlayerContext } from "@obipascal/player"
|
|
186
190
|
|
|
187
191
|
function App() {
|
|
188
192
|
return (
|
|
@@ -213,43 +217,165 @@ function ControlPanel() {
|
|
|
213
217
|
|
|
214
218
|
## 🔒 CloudFront & S3 Integration
|
|
215
219
|
|
|
216
|
-
|
|
220
|
+
This player supports **three video hosting scenarios**. Choose the one that fits your needs:
|
|
217
221
|
|
|
218
|
-
|
|
222
|
+
### Scenario 1: Public Videos (Easiest - No Authentication Required)
|
|
219
223
|
|
|
220
|
-
|
|
221
|
-
|
|
224
|
+
**When to use:** Your videos are publicly accessible and don't require user authentication.
|
|
225
|
+
|
|
226
|
+
**Setup:** Just provide the video URL!
|
|
222
227
|
|
|
223
|
-
|
|
224
|
-
|
|
228
|
+
```typescript
|
|
229
|
+
import { WontumPlayer } from "@obipascal/player"
|
|
225
230
|
|
|
226
231
|
const player = new WontumPlayer({
|
|
227
|
-
src: "https://
|
|
232
|
+
src: "https://d1234567890.cloudfront.net/video/playlist.m3u8",
|
|
228
233
|
container: "#player",
|
|
229
|
-
s3Config: {
|
|
230
|
-
cloudfront: {
|
|
231
|
-
domain: "media.yourdomain.com",
|
|
232
|
-
// Cookies are automatically sent by browser
|
|
233
|
-
},
|
|
234
|
-
},
|
|
235
234
|
})
|
|
236
235
|
```
|
|
237
236
|
|
|
238
|
-
|
|
237
|
+
✅ **That's it!** No backend needed. Works for public S3 buckets or CloudFront distributions.
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
### Scenario 2: Private Videos with CloudFront Signed Cookies (Recommended)
|
|
242
|
+
|
|
243
|
+
**When to use:** You want to restrict video access to authorized users (e.g., paid courses, premium content).
|
|
244
|
+
|
|
245
|
+
**How it works:**
|
|
246
|
+
|
|
247
|
+
1. User logs into your app
|
|
248
|
+
2. Your backend verifies the user and sets CloudFront signed cookies
|
|
249
|
+
3. Player automatically sends these cookies with every video request
|
|
250
|
+
4. CloudFront checks the cookies and allows/denies access
|
|
251
|
+
|
|
252
|
+
**Frontend Setup:**
|
|
239
253
|
|
|
240
254
|
```typescript
|
|
241
|
-
|
|
242
|
-
|
|
255
|
+
import { WontumPlayer } from "@obipascal/player"
|
|
256
|
+
|
|
257
|
+
// STEP 1: Call your backend to set signed cookies BEFORE creating the player
|
|
258
|
+
async function initializePlayer() {
|
|
259
|
+
// This endpoint sets CloudFront cookies in the browser
|
|
260
|
+
await fetch("/api/auth/video-access", {
|
|
261
|
+
credentials: "include", // Important: include cookies
|
|
262
|
+
})
|
|
263
|
+
|
|
264
|
+
// STEP 2: Create player - it will automatically use the cookies
|
|
265
|
+
const player = new WontumPlayer({
|
|
266
|
+
src: "https://media.yourdomain.com/videos/lesson-1/playlist.m3u8",
|
|
267
|
+
container: "#player",
|
|
268
|
+
s3Config: {
|
|
269
|
+
cloudFrontDomains: ["media.yourdomain.com"], // Your CloudFront domain
|
|
270
|
+
signUrl: async (url) => {
|
|
271
|
+
// This function is called when player needs to access a video
|
|
272
|
+
// Call your backend to refresh/set cookies if needed
|
|
273
|
+
const response = await fetch("/api/auth/sign-url", {
|
|
274
|
+
method: "POST",
|
|
275
|
+
headers: { "Content-Type": "application/json" },
|
|
276
|
+
credentials: "include",
|
|
277
|
+
body: JSON.stringify({ url }),
|
|
278
|
+
})
|
|
279
|
+
|
|
280
|
+
if (!response.ok) {
|
|
281
|
+
throw new Error("Failed to authenticate video access")
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Backend sets cookies, return the URL
|
|
285
|
+
return url
|
|
286
|
+
},
|
|
287
|
+
},
|
|
288
|
+
})
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
initializePlayer()
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
**Backend Setup (Node.js/Express):**
|
|
295
|
+
|
|
296
|
+
```typescript
|
|
297
|
+
import express from "express"
|
|
243
298
|
import { getSignedCookies } from "@aws-sdk/cloudfront-signer"
|
|
299
|
+
import fs from "fs"
|
|
300
|
+
|
|
301
|
+
const app = express()
|
|
244
302
|
|
|
245
|
-
|
|
303
|
+
// STEP 1: Create endpoint that sets CloudFront signed cookies
|
|
304
|
+
app.get("/api/auth/video-access", (req, res) => {
|
|
305
|
+
// Check if user is logged in (your authentication logic)
|
|
306
|
+
if (!req.user) {
|
|
307
|
+
return res.status(401).json({ error: "Not authenticated" })
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Define what resources user can access
|
|
311
|
+
const policy = {
|
|
312
|
+
Statement: [
|
|
313
|
+
{
|
|
314
|
+
Resource: "https://media.yourdomain.com/*", // All videos on this domain
|
|
315
|
+
Condition: {
|
|
316
|
+
DateLessThan: {
|
|
317
|
+
"AWS:EpochTime": Math.floor(Date.now() / 1000) + 3600, // Expires in 1 hour
|
|
318
|
+
},
|
|
319
|
+
},
|
|
320
|
+
},
|
|
321
|
+
],
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// Generate CloudFront signed cookies
|
|
325
|
+
const cookies = getSignedCookies({
|
|
326
|
+
keyPairId: process.env.CLOUDFRONT_KEY_PAIR_ID!, // Your CloudFront key pair ID
|
|
327
|
+
privateKey: fs.readFileSync("./cloudfront-private-key.pem", "utf8"), // Your private key
|
|
328
|
+
policy: JSON.stringify(policy),
|
|
329
|
+
})
|
|
330
|
+
|
|
331
|
+
// Set the three required cookies
|
|
332
|
+
res.cookie("CloudFront-Policy", cookies["CloudFront-Policy"], {
|
|
333
|
+
domain: ".yourdomain.com", // Use your domain
|
|
334
|
+
path: "/",
|
|
335
|
+
secure: true, // HTTPS only
|
|
336
|
+
httpOnly: true, // Prevent JavaScript access
|
|
337
|
+
sameSite: "none",
|
|
338
|
+
})
|
|
339
|
+
|
|
340
|
+
res.cookie("CloudFront-Signature", cookies["CloudFront-Signature"], {
|
|
341
|
+
domain: ".yourdomain.com",
|
|
342
|
+
path: "/",
|
|
343
|
+
secure: true,
|
|
344
|
+
httpOnly: true,
|
|
345
|
+
sameSite: "none",
|
|
346
|
+
})
|
|
347
|
+
|
|
348
|
+
res.cookie("CloudFront-Key-Pair-Id", cookies["CloudFront-Key-Pair-Id"], {
|
|
349
|
+
domain: ".yourdomain.com",
|
|
350
|
+
path: "/",
|
|
351
|
+
secure: true,
|
|
352
|
+
httpOnly: true,
|
|
353
|
+
sameSite: "none",
|
|
354
|
+
})
|
|
355
|
+
|
|
356
|
+
res.json({ success: true })
|
|
357
|
+
})
|
|
358
|
+
|
|
359
|
+
// STEP 2: Optional endpoint for on-demand signing (called by signUrl function)
|
|
360
|
+
app.post("/api/auth/sign-url", (req, res) => {
|
|
361
|
+
const { url } = req.body
|
|
362
|
+
|
|
363
|
+
// Verify user is authorized
|
|
364
|
+
if (!req.user) {
|
|
365
|
+
return res.status(401).json({ error: "Not authenticated" })
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// You can add additional authorization logic here
|
|
369
|
+
// For example, check if user has access to this specific video
|
|
370
|
+
|
|
371
|
+
// Refresh cookies (same code as above)
|
|
246
372
|
const policy = {
|
|
247
373
|
Statement: [
|
|
248
374
|
{
|
|
249
375
|
Resource: "https://media.yourdomain.com/*",
|
|
250
376
|
Condition: {
|
|
251
377
|
DateLessThan: {
|
|
252
|
-
"AWS:EpochTime": Math.floor(Date.now() / 1000) + 3600,
|
|
378
|
+
"AWS:EpochTime": Math.floor(Date.now() / 1000) + 3600,
|
|
253
379
|
},
|
|
254
380
|
},
|
|
255
381
|
},
|
|
@@ -257,43 +383,142 @@ app.get("/api/video-auth", async (req, res) => {
|
|
|
257
383
|
}
|
|
258
384
|
|
|
259
385
|
const cookies = getSignedCookies({
|
|
260
|
-
keyPairId: process.env.CLOUDFRONT_KEY_PAIR_ID
|
|
261
|
-
privateKey:
|
|
386
|
+
keyPairId: process.env.CLOUDFRONT_KEY_PAIR_ID!,
|
|
387
|
+
privateKey: fs.readFileSync("./cloudfront-private-key.pem", "utf8"),
|
|
262
388
|
policy: JSON.stringify(policy),
|
|
263
389
|
})
|
|
264
390
|
|
|
265
|
-
// Set cookies
|
|
266
391
|
res.cookie("CloudFront-Policy", cookies["CloudFront-Policy"], {
|
|
267
392
|
domain: ".yourdomain.com",
|
|
393
|
+
path: "/",
|
|
268
394
|
secure: true,
|
|
269
395
|
httpOnly: true,
|
|
396
|
+
sameSite: "none",
|
|
270
397
|
})
|
|
398
|
+
|
|
271
399
|
res.cookie("CloudFront-Signature", cookies["CloudFront-Signature"], {
|
|
272
400
|
domain: ".yourdomain.com",
|
|
401
|
+
path: "/",
|
|
273
402
|
secure: true,
|
|
274
403
|
httpOnly: true,
|
|
404
|
+
sameSite: "none",
|
|
275
405
|
})
|
|
406
|
+
|
|
276
407
|
res.cookie("CloudFront-Key-Pair-Id", cookies["CloudFront-Key-Pair-Id"], {
|
|
277
408
|
domain: ".yourdomain.com",
|
|
409
|
+
path: "/",
|
|
278
410
|
secure: true,
|
|
279
411
|
httpOnly: true,
|
|
412
|
+
sameSite: "none",
|
|
280
413
|
})
|
|
281
414
|
|
|
282
415
|
res.json({ success: true })
|
|
283
416
|
})
|
|
284
417
|
```
|
|
285
418
|
|
|
286
|
-
|
|
419
|
+
**AWS CloudFront Setup:**
|
|
420
|
+
|
|
421
|
+
1. Create a CloudFront key pair in AWS Console → CloudFront → Key pairs
|
|
422
|
+
2. Download the private key file
|
|
423
|
+
3. Set up environment variables:
|
|
424
|
+
```bash
|
|
425
|
+
CLOUDFRONT_KEY_PAIR_ID=APKA...
|
|
426
|
+
```
|
|
427
|
+
4. Configure your CloudFront distribution to require signed cookies
|
|
428
|
+
|
|
429
|
+
---
|
|
287
430
|
|
|
288
|
-
|
|
431
|
+
### Scenario 3: Private S3 Videos with Presigned URLs
|
|
432
|
+
|
|
433
|
+
**When to use:** Videos are in private S3 buckets without CloudFront.
|
|
434
|
+
|
|
435
|
+
**How it works:**
|
|
436
|
+
|
|
437
|
+
1. Your backend generates temporary presigned URLs for S3 objects
|
|
438
|
+
2. Player uses these URLs to access videos
|
|
439
|
+
3. URLs expire after a set time (e.g., 1 hour)
|
|
440
|
+
|
|
441
|
+
**Frontend Setup:**
|
|
289
442
|
|
|
290
443
|
```typescript
|
|
444
|
+
import { WontumPlayer } from "@obipascal/player"
|
|
445
|
+
|
|
291
446
|
const player = new WontumPlayer({
|
|
292
|
-
src: "
|
|
447
|
+
src: "s3://my-bucket/videos/lesson-1/playlist.m3u8", // S3 URI
|
|
293
448
|
container: "#player",
|
|
449
|
+
s3Config: {
|
|
450
|
+
getPresignedUrl: async (s3Key) => {
|
|
451
|
+
// Call your backend to generate presigned URL
|
|
452
|
+
const response = await fetch("/api/s3/presigned-url", {
|
|
453
|
+
method: "POST",
|
|
454
|
+
headers: { "Content-Type": "application/json" },
|
|
455
|
+
body: JSON.stringify({ key: s3Key }),
|
|
456
|
+
})
|
|
457
|
+
|
|
458
|
+
if (!response.ok) {
|
|
459
|
+
throw new Error("Failed to get presigned URL")
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
const data = await response.json()
|
|
463
|
+
return data.url // Return the presigned URL
|
|
464
|
+
},
|
|
465
|
+
},
|
|
294
466
|
})
|
|
295
467
|
```
|
|
296
468
|
|
|
469
|
+
**Backend Setup (Node.js):**
|
|
470
|
+
|
|
471
|
+
```typescript
|
|
472
|
+
import { S3Client, GetObjectCommand } from "@aws-sdk/client-s3"
|
|
473
|
+
import { getSignedUrl } from "@aws-sdk/s3-request-presigner"
|
|
474
|
+
|
|
475
|
+
const s3Client = new S3Client({
|
|
476
|
+
region: "us-east-1",
|
|
477
|
+
credentials: {
|
|
478
|
+
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
|
|
479
|
+
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
|
|
480
|
+
},
|
|
481
|
+
})
|
|
482
|
+
|
|
483
|
+
app.post("/api/s3/presigned-url", async (req, res) => {
|
|
484
|
+
const { key } = req.body
|
|
485
|
+
|
|
486
|
+
// Verify user is authorized to access this video
|
|
487
|
+
if (!req.user) {
|
|
488
|
+
return res.status(401).json({ error: "Not authenticated" })
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
try {
|
|
492
|
+
// Generate presigned URL
|
|
493
|
+
const command = new GetObjectCommand({
|
|
494
|
+
Bucket: "my-bucket",
|
|
495
|
+
Key: key, // e.g., "videos/lesson-1/playlist.m3u8"
|
|
496
|
+
})
|
|
497
|
+
|
|
498
|
+
const url = await getSignedUrl(s3Client, command, {
|
|
499
|
+
expiresIn: 3600, // URL valid for 1 hour
|
|
500
|
+
})
|
|
501
|
+
|
|
502
|
+
res.json({ url })
|
|
503
|
+
} catch (error) {
|
|
504
|
+
console.error("Error generating presigned URL:", error)
|
|
505
|
+
res.status(500).json({ error: "Failed to generate presigned URL" })
|
|
506
|
+
}
|
|
507
|
+
})
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
---
|
|
511
|
+
|
|
512
|
+
### Which Method Should I Use?
|
|
513
|
+
|
|
514
|
+
| Method | Best For | Complexity | Performance |
|
|
515
|
+
| ---------------------- | ---------------------------------------------- | ----------- | ------------ |
|
|
516
|
+
| **Public Videos** | Free content, marketing videos | ⭐ Easy | ⚡ Fast |
|
|
517
|
+
| **CloudFront Cookies** | ⭐ **Recommended** for paid courses, premium | ⭐⭐ Medium | ⚡⚡ Fastest |
|
|
518
|
+
| **S3 Presigned URLs** | Direct S3 access, simple private video hosting | ⭐⭐ Medium | ⚡ Good |
|
|
519
|
+
|
|
520
|
+
💡 **Tip:** Use CloudFront with signed cookies for production. It's more secure and performant for HLS videos (which have many file segments).
|
|
521
|
+
|
|
297
522
|
## 📝 Subtitle Support
|
|
298
523
|
|
|
299
524
|
### Adding Subtitles
|
|
@@ -568,15 +793,7 @@ function CustomPlayer() {
|
|
|
568
793
|
Wontum Player comes with 7 beautiful pre-made themes:
|
|
569
794
|
|
|
570
795
|
```typescript
|
|
571
|
-
import {
|
|
572
|
-
netflixTheme,
|
|
573
|
-
youtubeTheme,
|
|
574
|
-
modernTheme,
|
|
575
|
-
greenTheme,
|
|
576
|
-
cyberpunkTheme,
|
|
577
|
-
pastelTheme,
|
|
578
|
-
educationTheme,
|
|
579
|
-
} from "@wontum/player"
|
|
796
|
+
import { netflixTheme, youtubeTheme, modernTheme, greenTheme, cyberpunkTheme, pastelTheme, educationTheme } from "@obipascal/player"
|
|
580
797
|
|
|
581
798
|
const player = new WontumPlayer({
|
|
582
799
|
src: "https://media.example.com/video/playlist.m3u8",
|
|
@@ -586,6 +803,7 @@ const player = new WontumPlayer({
|
|
|
586
803
|
```
|
|
587
804
|
|
|
588
805
|
**Available Themes:**
|
|
806
|
+
|
|
589
807
|
- `netflixTheme()` - Netflix-inspired red and black
|
|
590
808
|
- `youtubeTheme()` - YouTube-inspired red and white
|
|
591
809
|
- `modernTheme()` - Modern blue gradient
|
|
@@ -620,7 +838,7 @@ const player = new WontumPlayer({
|
|
|
620
838
|
Quick brand color presets:
|
|
621
839
|
|
|
622
840
|
```typescript
|
|
623
|
-
import { BrandPresets } from "@
|
|
841
|
+
import { BrandPresets } from "@obipascal/player"
|
|
624
842
|
|
|
625
843
|
const player = new WontumPlayer({
|
|
626
844
|
src: "https://media.example.com/video/playlist.m3u8",
|
|
@@ -634,6 +852,7 @@ const player = new WontumPlayer({
|
|
|
634
852
|
```
|
|
635
853
|
|
|
636
854
|
**Available Brand Colors:**
|
|
855
|
+
|
|
637
856
|
- `blue`, `lightBlue`, `darkBlue`
|
|
638
857
|
- `red`, `lightRed`, `darkRed`
|
|
639
858
|
- `green`, `lightGreen`, `darkGreen`
|
|
@@ -788,6 +1007,7 @@ For detailed API documentation including all methods, events, types, and configu
|
|
|
788
1007
|
### Quick Reference
|
|
789
1008
|
|
|
790
1009
|
**Player Methods:**
|
|
1010
|
+
|
|
791
1011
|
- **Playback:** `play()`, `pause()`, `seek(time)`, `skipForward(seconds)`, `skipBackward(seconds)`
|
|
792
1012
|
- **Volume:** `setVolume(level)`, `mute()`, `unmute()`
|
|
793
1013
|
- **Subtitles:** `enableSubtitles(index)`, `disableSubtitles()`, `toggleSubtitles()`, `getSubtitleTracks()`, `areSubtitlesEnabled()`
|
|
@@ -798,6 +1018,7 @@ For detailed API documentation including all methods, events, types, and configu
|
|
|
798
1018
|
- **Lifecycle:** `destroy()`
|
|
799
1019
|
|
|
800
1020
|
**Events (25 total):**
|
|
1021
|
+
|
|
801
1022
|
- **Playback:** `play`, `pause`, `ended`, `timeupdate`, `durationchange`
|
|
802
1023
|
- **Loading:** `loadstart`, `loadedmetadata`, `loadeddata`, `canplay`, `canplaythrough`
|
|
803
1024
|
- **Buffering:** `waiting`, `playing`, `stalled`, `suspend`, `abort`
|
|
@@ -812,13 +1033,13 @@ For detailed API documentation including all methods, events, types, and configu
|
|
|
812
1033
|
|
|
813
1034
|
## 🌐 Browser Support
|
|
814
1035
|
|
|
815
|
-
| Browser
|
|
816
|
-
|
|
817
|
-
| Chrome
|
|
818
|
-
| Edge
|
|
819
|
-
| Firefox
|
|
820
|
-
| Safari
|
|
821
|
-
| iOS Safari
|
|
1036
|
+
| Browser | Minimum Version |
|
|
1037
|
+
| -------------- | ----------------- |
|
|
1038
|
+
| Chrome | Latest 2 versions |
|
|
1039
|
+
| Edge | Latest 2 versions |
|
|
1040
|
+
| Firefox | Latest 2 versions |
|
|
1041
|
+
| Safari | Latest 2 versions |
|
|
1042
|
+
| iOS Safari | iOS 12+ |
|
|
822
1043
|
| Android Chrome | Latest 2 versions |
|
|
823
1044
|
|
|
824
1045
|
**Note:** HLS playback requires HLS.js support. Native HLS playback is supported on Safari.
|
|
@@ -14,7 +14,7 @@ export declare function cloudFrontWithRetryExample(): WontumPlayer;
|
|
|
14
14
|
* Note: This is a code example as a string. For actual React usage,
|
|
15
15
|
* copy this to a .tsx file in your project.
|
|
16
16
|
*/
|
|
17
|
-
export declare const ReactCloudFrontPlayerExample = "\nimport React from \"react\"\nimport { WontumPlayerReact } from \"@
|
|
17
|
+
export declare const ReactCloudFrontPlayerExample = "\nimport React from \"react\"\nimport { WontumPlayerReact } from \"@obipascal/player\"\n\nexport function CloudFrontPlayer({ videoUrl, userId }: { videoUrl: string; userId: string }) {\n\t// Sign URL function\n\tasync function signUrl(url: string): Promise<string> {\n\t\tconst response = await fetch(\"/api/cloudfront/sign\", {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: {\n\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t},\n\t\t\tbody: JSON.stringify({ url, userId }),\n\t\t\tcredentials: \"include\",\n\t\t})\n\n\t\tconst data = await response.json()\n\t\treturn data.url\n\t}\n\n\treturn (\n\t\t<WontumPlayerReact\n\t\t\tsrc={videoUrl}\n\t\t\twidth=\"100%\"\n\t\t\theight=\"500px\"\n\t\t\ts3Config={{\n\t\t\t\tsignUrl,\n\t\t\t\tcloudFrontDomains: [\"media.domain.com\", \"cdn.domain.com\"], // Multiple domains\n\t\t\t}}\n\t\t\tanalytics={{\n\t\t\t\tenabled: true,\n\t\t\t\tuserId,\n\t\t\t\tvideoId: videoUrl,\n\t\t\t}}\n\t\t\tonError={(error) => {\n\t\t\t\tconsole.error(\"Player error:\", error)\n\t\t\t\t// Handle signing errors\n\t\t\t\tif (error?.message?.includes(\"sign\")) {\n\t\t\t\t\talert(\"Failed to authenticate video. Please refresh and try again.\")\n\t\t\t\t}\n\t\t\t}}\n\t\t/>\n\t)\n}\n";
|
|
18
18
|
/**
|
|
19
19
|
* Example 4: Backend Implementation (Node.js/Express)
|
|
20
20
|
*/
|
|
@@ -31,7 +31,7 @@ export declare function educationTheme(): WontumPlayer;
|
|
|
31
31
|
/**
|
|
32
32
|
* React Example with Custom Theme
|
|
33
33
|
*/
|
|
34
|
-
export declare const ReactCustomThemeExample = "\nimport React from 'react';\nimport { WontumPlayerReact } from '@
|
|
34
|
+
export declare const ReactCustomThemeExample = "\nimport React from 'react';\nimport { WontumPlayerReact } from '@obipascal/player';\n\nfunction CustomStyledPlayer() {\n return (\n <WontumPlayerReact\n src=\"https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8\"\n theme={{\n primaryColor: '#ff6b6b', // Coral red\n accentColor: '#ee5a6f',\n fontFamily: 'Helvetica, Arial, sans-serif',\n controlsBackground: 'linear-gradient(to top, rgba(0,0,0,0.85), transparent)',\n buttonHoverBg: 'rgba(255, 107, 107, 0.25)',\n progressHeight: '5px',\n borderRadius: '8px'\n }}\n />\n );\n}\n";
|
|
35
35
|
/**
|
|
36
36
|
* All Available Theme Options
|
|
37
37
|
*/
|
|
@@ -40,7 +40,7 @@ export declare function minimalEventExample(): void;
|
|
|
40
40
|
/**
|
|
41
41
|
* React example with all events
|
|
42
42
|
*/
|
|
43
|
-
export declare const ReactAllEventsExample = "\nimport React from 'react';\nimport { WontumPlayerReact } from '@
|
|
43
|
+
export declare const ReactAllEventsExample = "\nimport React from 'react';\nimport { WontumPlayerReact } from '@obipascal/player';\n\nfunction VideoPlayerWithEvents() {\n return (\n <WontumPlayerReact\n src=\"https://media.domain.com/video.m3u8\"\n \n {/* Playback Events */}\n onPlay={(e) => console.log('Play', e)}\n onPause={(e) => console.log('Pause', e)}\n onPlaying={(e) => console.log('Playing', e)}\n onEnded={(e) => console.log('Ended', e)}\n onTimeupdate={(e) => console.log('Time:', e.data.currentTime)}\n \n {/* Seeking */}\n onSeeking={(e) => console.log('Seeking', e)}\n onSeeked={(e) => console.log('Seeked', e)}\n \n {/* Loading Events */}\n onLoadstart={(e) => console.log('Load Start', e)}\n onLoadeddata={(e) => console.log('Loaded Data', e)}\n onLoadedmetadata={(e) => console.log('Metadata', e)}\n onCanplay={(e) => console.log('Can Play', e)}\n onCanplaythrough={(e) => console.log('Can Play Through', e)}\n onProgress={(e) => console.log('Progress', e)}\n onWaiting={(e) => console.log('Buffering', e)}\n onDurationchange={(e) => console.log('Duration', e)}\n \n {/* Media State */}\n onVolumechange={(e) => console.log('Volume', e)}\n onRatechange={(e) => console.log('Rate', e)}\n onResize={(e) => console.log('Resize', e)}\n \n {/* Network/Error */}\n onAbort={(e) => console.log('Abort', e)}\n onEmptied={(e) => console.log('Emptied', e)}\n onStalled={(e) => console.log('Stalled', e)}\n onSuspend={(e) => console.log('Suspend', e)}\n onError={(e) => console.error('Error', e)}\n \n {/* Custom Events */}\n onQualitychange={(e) => console.log('Quality', e)}\n onFullscreenchange={(e) => console.log('Fullscreen', e)}\n />\n );\n}\n";
|
|
44
44
|
/**
|
|
45
45
|
* Compare with Mux Player events
|
|
46
46
|
*/
|
|
@@ -35,4 +35,4 @@ export declare const htmlExample = "\n<!DOCTYPE html>\n<html>\n<head>\n <title>
|
|
|
35
35
|
/**
|
|
36
36
|
* React example with public stream
|
|
37
37
|
*/
|
|
38
|
-
export declare const reactPublicStreamExample = "\nimport React from 'react';\nimport { WontumPlayerReact } from '@
|
|
38
|
+
export declare const reactPublicStreamExample = "\nimport React from 'react';\nimport { WontumPlayerReact } from '@obipascal/player';\n\nfunction App() {\n return (\n <div style={{ width: '100%', height: '600px', background: '#000' }}>\n <WontumPlayerReact\n src=\"https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8\"\n autoplay\n onLoadedmetadata={(e) => console.log('Duration:', e.data.duration)}\n onPlay={() => console.log('Playing!')}\n onError={(e) => console.error('Error:', e.data)}\n />\n </div>\n );\n}\n\nexport default App;\n";
|
package/package.json
CHANGED