@alirezahosseini/sibtorsh-player 0.2.15 → 0.3.1
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 +1324 -72
- package/dist/VideoPlayer.d.ts +1 -1
- package/dist/components/SelectMenu.d.ts +3 -0
- package/dist/components/VideoPlayerControl.d.ts +1 -1
- package/dist/components/svg/PlaySvg.d.ts +1 -1
- package/dist/components/svg/RetrySvg.d.ts +1 -1
- package/dist/components/svg/SelectSvg.d.ts +2 -0
- package/dist/interfaces/QualityInterface.d.ts +7 -0
- package/dist/interfaces/SelectMenuInterface.d.ts +10 -0
- package/dist/interfaces/SvgInterface.d.ts +1 -0
- package/dist/interfaces/VideoPlayerControlInterface.d.ts +7 -1
- package/dist/interfaces/VideoPlayerInterface.d.ts +1 -2
- package/dist/sibtorsh-player.css +1 -1
- package/dist/sibtorsh-player.es.js +6593 -5908
- package/dist/sibtorsh-player.umd.js +128 -43
- package/dist/utils/UrlUtils.d.ts +2 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,73 +1,1325 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
1
|
+
# Sibtorsh Player
|
|
2
|
+
|
|
3
|
+
A modern, responsive, and TypeScript-first video player for React with HLS support, native Safari playback, adaptive quality selection, encrypted HLS key handling, playback speed controls, fullscreen mode, keyboard shortcuts, and mobile-friendly interactions.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@alirezahosseini/sibtorsh-player)
|
|
6
|
+
[](https://www.typescriptlang.org/)
|
|
7
|
+
[](https://react.dev/)
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Table of Contents
|
|
12
|
+
|
|
13
|
+
- [Features](#features)
|
|
14
|
+
- [Installation](#installation)
|
|
15
|
+
- [Quick Start](#quick-start)
|
|
16
|
+
- [Props](#props)
|
|
17
|
+
- [HLS Playback](#hls-playback)
|
|
18
|
+
- [Encrypted HLS](#encrypted-hls)
|
|
19
|
+
- [Quality Selection](#quality-selection)
|
|
20
|
+
- [Playback Speed](#playback-speed)
|
|
21
|
+
- [Keyboard Shortcuts](#keyboard-shortcuts)
|
|
22
|
+
- [Player Interactions](#player-interactions)
|
|
23
|
+
- [Fullscreen](#fullscreen)
|
|
24
|
+
- [Playback Events](#playback-events)
|
|
25
|
+
- [Next.js Usage](#nextjs-usage)
|
|
26
|
+
- [Vite Usage](#vite-usage)
|
|
27
|
+
- [Private Streams](#private-streams)
|
|
28
|
+
- [CORS Configuration](#cors-configuration)
|
|
29
|
+
- [Visibility Handling](#visibility-handling)
|
|
30
|
+
- [Error Handling](#error-handling)
|
|
31
|
+
- [Styling and Sizing](#styling-and-sizing)
|
|
32
|
+
- [Browser Support](#browser-support)
|
|
33
|
+
- [Troubleshooting](#troubleshooting)
|
|
34
|
+
- [TypeScript](#typescript)
|
|
35
|
+
- [Publishing](#publishing)
|
|
36
|
+
- [Security](#security)
|
|
37
|
+
- [License](#license)
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Features
|
|
42
|
+
|
|
43
|
+
- HLS playback using `hls.js`
|
|
44
|
+
- Native HLS playback on Safari and iOS
|
|
45
|
+
- Support for MP4 and browser-supported video sources
|
|
46
|
+
- Adaptive bitrate playback
|
|
47
|
+
- Automatic quality selection
|
|
48
|
+
- Manual quality selection
|
|
49
|
+
- Native Safari quality switching
|
|
50
|
+
- AES-128 encrypted HLS support
|
|
51
|
+
- Custom encryption key URL using `keySrc`
|
|
52
|
+
- Playback speed selection from `0.5x` to `3x`
|
|
53
|
+
- Temporary `2x` playback using long press
|
|
54
|
+
- Forward 15 seconds
|
|
55
|
+
- Backward 15 seconds
|
|
56
|
+
- Forward and backward visual indicators
|
|
57
|
+
- Standard Fullscreen API support
|
|
58
|
+
- Safari fullscreen fallback
|
|
59
|
+
- iOS and PWA fullscreen behavior
|
|
60
|
+
- Keyboard shortcuts
|
|
61
|
+
- Mouse and touch interactions
|
|
62
|
+
- Buffered ranges support
|
|
63
|
+
- Automatic control hiding
|
|
64
|
+
- Automatic pause when the page is hidden
|
|
65
|
+
- Automatic pause outside the viewport
|
|
66
|
+
- Automatic resume when returning to the viewport
|
|
67
|
+
- HLS network error recovery
|
|
68
|
+
- HLS media error recovery
|
|
69
|
+
- Loading and retry states
|
|
70
|
+
- Responsive controls
|
|
71
|
+
- RTL-ready interface
|
|
72
|
+
- CSS container queries
|
|
73
|
+
- Full TypeScript declarations
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Installation
|
|
78
|
+
|
|
79
|
+
Using npm:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
npm install @alirezahosseini/sibtorsh-player
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Peer Dependencies
|
|
86
|
+
|
|
87
|
+
Your project must include React and React DOM:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
npm install react react-dom
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## Quick Start
|
|
96
|
+
|
|
97
|
+
Import the player component and its stylesheet:
|
|
98
|
+
|
|
99
|
+
```tsx
|
|
100
|
+
import VideoPlayer from "@alirezahosseini/sibtorsh-player";
|
|
101
|
+
import "@alirezahosseini/sibtorsh-player/dist/sibtorsh-player.css";
|
|
102
|
+
|
|
103
|
+
export default function App() {
|
|
104
|
+
return (
|
|
105
|
+
<div
|
|
106
|
+
style={{
|
|
107
|
+
width: "100%",
|
|
108
|
+
maxWidth: 960,
|
|
109
|
+
aspectRatio: "16 / 9"
|
|
110
|
+
}}
|
|
111
|
+
>
|
|
112
|
+
<VideoPlayer
|
|
113
|
+
src="https://example.com/video/master.m3u8"
|
|
114
|
+
poster="https://example.com/video/poster.jpg"
|
|
115
|
+
title="Sample video"
|
|
116
|
+
/>
|
|
117
|
+
</div>
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
The player fills the width and height of its parent element.
|
|
123
|
+
|
|
124
|
+
The parent should have an explicit height or aspect ratio:
|
|
125
|
+
|
|
126
|
+
```tsx
|
|
127
|
+
<div style={{width: "100%", aspectRatio: "16 / 9"}}>
|
|
128
|
+
<VideoPlayer src="https://example.com/master.m3u8" />
|
|
129
|
+
</div>
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## Props
|
|
135
|
+
|
|
136
|
+
| Prop | Type | Required | Description |
|
|
137
|
+
| --- | --- | --- | --- |
|
|
138
|
+
| `src` | `string` | Yes | Video source URL. Supports HLS playlists and browser-supported video formats. |
|
|
139
|
+
| `poster` | `string` | No | Poster image displayed before video playback starts. |
|
|
140
|
+
| `keySrc` | `string` | No | Custom URL used for HLS AES-128 encryption key requests. |
|
|
141
|
+
| `title` | `string` | No | Video title displayed inside the player controls. |
|
|
142
|
+
| `onPlay` | `() => void` | No | Called whenever video playback starts. |
|
|
143
|
+
| `onPause` | `() => void` | No | Called whenever video playback pauses. |
|
|
144
|
+
|
|
145
|
+
### VideoPlayerInterface
|
|
146
|
+
|
|
147
|
+
```ts
|
|
148
|
+
export default interface VideoPlayerInterface {
|
|
149
|
+
src: string;
|
|
150
|
+
poster?: string;
|
|
151
|
+
keySrc?: string;
|
|
152
|
+
title?: string;
|
|
153
|
+
onPlay?: () => void;
|
|
154
|
+
onPause?: () => void;
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## HLS Playback
|
|
161
|
+
|
|
162
|
+
Sibtorsh Player automatically chooses the correct HLS playback strategy.
|
|
163
|
+
|
|
164
|
+
### Browsers with Media Source Extensions
|
|
165
|
+
|
|
166
|
+
Browsers such as Chrome, Firefox, and Edge use `hls.js`.
|
|
167
|
+
|
|
168
|
+
```tsx
|
|
169
|
+
<VideoPlayer
|
|
170
|
+
src="https://cdn.example.com/course/master.m3u8"
|
|
171
|
+
poster="https://cdn.example.com/course/poster.webp"
|
|
172
|
+
title="Course lesson"
|
|
173
|
+
/>
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### Safari and iOS
|
|
177
|
+
|
|
178
|
+
Safari uses its native HLS playback implementation.
|
|
179
|
+
|
|
180
|
+
The player detects native HLS support using:
|
|
181
|
+
|
|
182
|
+
```ts
|
|
183
|
+
video.canPlayType("application/vnd.apple.mpegurl");
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
For Safari requests, the player appends the following query parameter to the master playlist URL:
|
|
187
|
+
|
|
188
|
+
```text
|
|
189
|
+
is_safari=1
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
For example:
|
|
193
|
+
|
|
194
|
+
```text
|
|
195
|
+
https://api.example.com/video/master.m3u8?is_safari=1
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Your backend can use this parameter to return a Safari-compatible playlist.
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
## Encrypted HLS
|
|
203
|
+
|
|
204
|
+
The player supports AES-128 encrypted HLS streams.
|
|
205
|
+
|
|
206
|
+
Pass the protected or presigned encryption key URL using `keySrc`:
|
|
207
|
+
|
|
208
|
+
```tsx
|
|
209
|
+
import VideoPlayer from "@alirezahosseini/sibtorsh-player";
|
|
210
|
+
import "@alirezahosseini/sibtorsh-player/dist/sibtorsh-player.css";
|
|
211
|
+
|
|
212
|
+
export function EncryptedVideo() {
|
|
213
|
+
return (
|
|
214
|
+
<div style={{aspectRatio: "16 / 9"}}>
|
|
215
|
+
<VideoPlayer
|
|
216
|
+
src="https://api.example.com/videos/lesson/master.m3u8"
|
|
217
|
+
keySrc="https://api.example.com/videos/lesson/key?token=SIGNED_TOKEN"
|
|
218
|
+
poster="https://cdn.example.com/posters/lesson.webp"
|
|
219
|
+
title="Protected lesson"
|
|
220
|
+
/>
|
|
221
|
+
</div>
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
When `hls.js` requests a URL containing `.key`, the player redirects that request to `keySrc`.
|
|
227
|
+
|
|
228
|
+
The internal behavior is similar to:
|
|
229
|
+
|
|
230
|
+
```ts
|
|
231
|
+
const hls = new Hls({
|
|
232
|
+
xhrSetup(xhr, url) {
|
|
233
|
+
if (url.includes(".key") && keySrc) {
|
|
234
|
+
xhr.open("GET", keySrc, true);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
> `keySrc` only controls encryption-key requests made by `hls.js`.
|
|
241
|
+
|
|
242
|
+
Safari uses native HLS, meaning Safari directly processes the playlist and key URLs. Your Safari playlist must therefore contain valid and authorized encryption-key URLs.
|
|
243
|
+
|
|
244
|
+
---
|
|
245
|
+
|
|
246
|
+
## Quality Selection
|
|
247
|
+
|
|
248
|
+
When the HLS master playlist contains multiple variants, the player automatically generates a quality menu.
|
|
249
|
+
|
|
250
|
+
Example master playlist:
|
|
251
|
+
|
|
252
|
+
```m3u8
|
|
253
|
+
#EXTM3U
|
|
254
|
+
#EXT-X-VERSION:3
|
|
255
|
+
|
|
256
|
+
#EXT-X-STREAM-INF:BANDWIDTH=2500000,RESOLUTION=1920x1080,NAME="1080p"
|
|
257
|
+
1080p/index.m3u8
|
|
258
|
+
|
|
259
|
+
#EXT-X-STREAM-INF:BANDWIDTH=1500000,RESOLUTION=1280x720,NAME="720p"
|
|
260
|
+
720p/index.m3u8
|
|
261
|
+
|
|
262
|
+
#EXT-X-STREAM-INF:BANDWIDTH=800000,RESOLUTION=854x480,NAME="480p"
|
|
263
|
+
480p/index.m3u8
|
|
264
|
+
|
|
265
|
+
#EXT-X-STREAM-INF:BANDWIDTH=500000,RESOLUTION=640x360,NAME="360p"
|
|
266
|
+
360p/index.m3u8
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
The generated quality options include:
|
|
270
|
+
|
|
271
|
+
```text
|
|
272
|
+
Automatic
|
|
273
|
+
1080p
|
|
274
|
+
720p
|
|
275
|
+
480p
|
|
276
|
+
360p
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
### Automatic Quality
|
|
280
|
+
|
|
281
|
+
The automatic quality option uses:
|
|
282
|
+
|
|
283
|
+
```ts
|
|
284
|
+
hls.currentLevel = -1;
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
This allows `hls.js` to select the most appropriate quality based on bandwidth and playback conditions.
|
|
288
|
+
|
|
289
|
+
### Manual Quality
|
|
290
|
+
|
|
291
|
+
Manual quality selection sets the selected HLS level:
|
|
292
|
+
|
|
293
|
+
```ts
|
|
294
|
+
hls.currentLevel = selectedQuality.level;
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
### Safari Quality Switching
|
|
298
|
+
|
|
299
|
+
Safari does not expose the same quality-level API as `hls.js`.
|
|
300
|
+
|
|
301
|
+
The player:
|
|
302
|
+
|
|
303
|
+
1. Downloads the master playlist.
|
|
304
|
+
2. Parses `#EXT-X-STREAM-INF` entries.
|
|
305
|
+
3. Creates a list of available rendition URLs.
|
|
306
|
+
4. Stores the current playback time.
|
|
307
|
+
5. Changes the video source.
|
|
308
|
+
6. Restores the previous playback time.
|
|
309
|
+
7. Continues playback if the video was already playing.
|
|
310
|
+
|
|
311
|
+
---
|
|
312
|
+
|
|
313
|
+
## Playback Speed
|
|
314
|
+
|
|
315
|
+
The player provides the following playback speeds:
|
|
316
|
+
|
|
317
|
+
```text
|
|
318
|
+
0.5x
|
|
319
|
+
1x
|
|
320
|
+
1.25x
|
|
321
|
+
1.5x
|
|
322
|
+
1.75x
|
|
323
|
+
2x
|
|
324
|
+
2.5x
|
|
325
|
+
3x
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
Example internal behavior:
|
|
329
|
+
|
|
330
|
+
```ts
|
|
331
|
+
video.playbackRate = selectedRate;
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
### Long Press Playback
|
|
335
|
+
|
|
336
|
+
Holding the player for one second temporarily changes the playback speed to `2x`.
|
|
337
|
+
|
|
338
|
+
```text
|
|
339
|
+
Press and hold → 2x
|
|
340
|
+
Release → 1x
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
This feature works with mouse and touch events.
|
|
344
|
+
|
|
345
|
+
---
|
|
346
|
+
|
|
347
|
+
## Keyboard Shortcuts
|
|
348
|
+
|
|
349
|
+
Keyboard shortcuts are ignored when the user is typing inside:
|
|
350
|
+
|
|
351
|
+
- Input elements
|
|
352
|
+
- Textarea elements
|
|
353
|
+
- Content-editable elements
|
|
354
|
+
|
|
355
|
+
| Key | Action |
|
|
356
|
+
| --- | --- |
|
|
357
|
+
| `Space` | Toggle play and pause |
|
|
358
|
+
| `ArrowRight` | Forward 15 seconds |
|
|
359
|
+
| `ArrowLeft` | Backward 15 seconds |
|
|
360
|
+
| `M` | Toggle mute |
|
|
361
|
+
|
|
362
|
+
Example:
|
|
363
|
+
|
|
364
|
+
```ts
|
|
365
|
+
if (event.code.toUpperCase() === "SPACE") {
|
|
366
|
+
event.preventDefault();
|
|
367
|
+
togglePlayHandle();
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
if (event.code.toUpperCase() === "ARROWRIGHT") {
|
|
371
|
+
event.preventDefault();
|
|
372
|
+
forwardCurrentTimeHandler();
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
if (event.code.toUpperCase() === "ARROWLEFT") {
|
|
376
|
+
event.preventDefault();
|
|
377
|
+
backwardCurrentTimeHandler();
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
if (event.code.toUpperCase() === "KEYM") {
|
|
381
|
+
event.preventDefault();
|
|
382
|
+
toggleMuteHandle();
|
|
383
|
+
}
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
---
|
|
387
|
+
|
|
388
|
+
## Player Interactions
|
|
389
|
+
|
|
390
|
+
| Interaction | Action |
|
|
391
|
+
| --- | --- |
|
|
392
|
+
| Click or tap | Show or hide player controls |
|
|
393
|
+
| Right-side double interaction | Forward 15 seconds |
|
|
394
|
+
| Left-side double interaction | Backward 15 seconds |
|
|
395
|
+
| Long press | Temporary `2x` playback |
|
|
396
|
+
| Mouse move | Show controls |
|
|
397
|
+
| Touch move | Show controls |
|
|
398
|
+
|
|
399
|
+
### Forward and Backward Indicators
|
|
400
|
+
|
|
401
|
+
After seeking forward or backward, the related indicator remains visible for five seconds.
|
|
402
|
+
|
|
403
|
+
If the action runs repeatedly, the timer restarts from the latest execution.
|
|
404
|
+
|
|
405
|
+
```ts
|
|
406
|
+
if (forwardTimeoutRef.current) {
|
|
407
|
+
clearTimeout(forwardTimeoutRef.current);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
forwardTimeoutRef.current = setTimeout(() => {
|
|
411
|
+
setIsForwardCurrentTime(false);
|
|
412
|
+
forwardTimeoutRef.current = null;
|
|
413
|
+
}, 5000);
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
Starting forward seeking immediately hides the backward indicator.
|
|
417
|
+
|
|
418
|
+
Starting backward seeking immediately hides the forward indicator.
|
|
419
|
+
|
|
420
|
+
---
|
|
421
|
+
|
|
422
|
+
## Fullscreen
|
|
423
|
+
|
|
424
|
+
The player supports the standard Fullscreen API:
|
|
425
|
+
|
|
426
|
+
```ts
|
|
427
|
+
element.requestFullscreen();
|
|
428
|
+
document.exitFullscreen();
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
It also checks browser-prefixed implementations:
|
|
432
|
+
|
|
433
|
+
```ts
|
|
434
|
+
element.webkitRequestFullscreen;
|
|
435
|
+
element.mozRequestFullScreen;
|
|
436
|
+
element.msRequestFullscreen;
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
Fullscreen change events supported by the player include:
|
|
440
|
+
|
|
441
|
+
```text
|
|
442
|
+
fullscreenchange
|
|
443
|
+
webkitfullscreenchange
|
|
444
|
+
mozfullscreenchange
|
|
445
|
+
MSFullscreenChange
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
### Safari and PWA Fallback
|
|
449
|
+
|
|
450
|
+
For iOS, Safari, and Safari-based PWA environments, the player provides a CSS-based fullscreen fallback.
|
|
451
|
+
|
|
452
|
+
In fullscreen mode, the video uses:
|
|
453
|
+
|
|
454
|
+
```text
|
|
455
|
+
object-contain
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
This ensures that the complete video frame remains visible.
|
|
459
|
+
|
|
460
|
+
---
|
|
461
|
+
|
|
462
|
+
## Playback Events
|
|
463
|
+
|
|
464
|
+
You can use `onPlay` and `onPause` for:
|
|
465
|
+
|
|
466
|
+
- Analytics
|
|
467
|
+
- Lesson progress tracking
|
|
468
|
+
- User activity tracking
|
|
469
|
+
- Saving playback state
|
|
470
|
+
- Updating application UI
|
|
471
|
+
|
|
472
|
+
Example:
|
|
473
|
+
|
|
474
|
+
```tsx
|
|
475
|
+
import VideoPlayer from "@alirezahosseini/sibtorsh-player";
|
|
476
|
+
import "@alirezahosseini/sibtorsh-player/dist/sibtorsh-player.css";
|
|
477
|
+
|
|
478
|
+
export function AnalyticsExample() {
|
|
479
|
+
return (
|
|
480
|
+
<div style={{aspectRatio: "16 / 9"}}>
|
|
481
|
+
<VideoPlayer
|
|
482
|
+
src="https://example.com/master.m3u8"
|
|
483
|
+
onPlay={() => {
|
|
484
|
+
console.log("Playback started");
|
|
485
|
+
}}
|
|
486
|
+
onPause={() => {
|
|
487
|
+
console.log("Playback paused");
|
|
488
|
+
}}
|
|
489
|
+
/>
|
|
490
|
+
</div>
|
|
491
|
+
);
|
|
492
|
+
}
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
---
|
|
496
|
+
|
|
497
|
+
## Next.js Usage
|
|
498
|
+
|
|
499
|
+
Sibtorsh Player uses browser APIs and should run as a client component.
|
|
500
|
+
|
|
501
|
+
### App Router
|
|
502
|
+
|
|
503
|
+
```tsx
|
|
504
|
+
"use client";
|
|
505
|
+
|
|
506
|
+
import VideoPlayer from "@alirezahosseini/sibtorsh-player";
|
|
507
|
+
import "@alirezahosseini/sibtorsh-player/dist/sibtorsh-player.css";
|
|
508
|
+
|
|
509
|
+
export default function LessonPlayer() {
|
|
510
|
+
return (
|
|
511
|
+
<div className="aspect-video w-full">
|
|
512
|
+
<VideoPlayer
|
|
513
|
+
src="https://example.com/master.m3u8"
|
|
514
|
+
poster="https://example.com/poster.webp"
|
|
515
|
+
title="Lesson 1"
|
|
516
|
+
/>
|
|
517
|
+
</div>
|
|
518
|
+
);
|
|
519
|
+
}
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
### Dynamic Import
|
|
523
|
+
|
|
524
|
+
When server-side rendering causes browser API errors, import the player dynamically:
|
|
525
|
+
|
|
526
|
+
```tsx
|
|
527
|
+
"use client";
|
|
528
|
+
|
|
529
|
+
import dynamic from "next/dynamic";
|
|
530
|
+
import "@alirezahosseini/sibtorsh-player/dist/sibtorsh-player.css";
|
|
531
|
+
|
|
532
|
+
const VideoPlayer = dynamic(
|
|
533
|
+
() => import("@alirezahosseini/sibtorsh-player"),
|
|
534
|
+
{
|
|
535
|
+
ssr: false
|
|
536
|
+
}
|
|
537
|
+
);
|
|
538
|
+
|
|
539
|
+
export default function LessonPlayer() {
|
|
540
|
+
return (
|
|
541
|
+
<div className="aspect-video w-full">
|
|
542
|
+
<VideoPlayer
|
|
543
|
+
src="https://example.com/master.m3u8"
|
|
544
|
+
poster="https://example.com/poster.webp"
|
|
545
|
+
title="Lesson 1"
|
|
546
|
+
/>
|
|
547
|
+
</div>
|
|
548
|
+
);
|
|
549
|
+
}
|
|
550
|
+
```
|
|
551
|
+
|
|
552
|
+
### Global CSS Import
|
|
553
|
+
|
|
554
|
+
In some Next.js configurations, package CSS should be imported inside the root layout:
|
|
555
|
+
|
|
556
|
+
```tsx
|
|
557
|
+
import "@alirezahosseini/sibtorsh-player/dist/sibtorsh-player.css";
|
|
558
|
+
|
|
559
|
+
export default function RootLayout({
|
|
560
|
+
children
|
|
561
|
+
}: {
|
|
562
|
+
children: React.ReactNode;
|
|
563
|
+
}) {
|
|
564
|
+
return (
|
|
565
|
+
<html lang="en">
|
|
566
|
+
<body>{children}</body>
|
|
567
|
+
</html>
|
|
568
|
+
);
|
|
569
|
+
}
|
|
570
|
+
```
|
|
571
|
+
|
|
572
|
+
---
|
|
573
|
+
|
|
574
|
+
## Vite Usage
|
|
575
|
+
|
|
576
|
+
```tsx
|
|
577
|
+
import React from "react";
|
|
578
|
+
import ReactDOM from "react-dom/client";
|
|
579
|
+
import VideoPlayer from "@alirezahosseini/sibtorsh-player";
|
|
580
|
+
|
|
581
|
+
import "@alirezahosseini/sibtorsh-player/dist/sibtorsh-player.css";
|
|
582
|
+
|
|
583
|
+
function App() {
|
|
584
|
+
return (
|
|
585
|
+
<main
|
|
586
|
+
style={{
|
|
587
|
+
width: "100%",
|
|
588
|
+
maxWidth: 960,
|
|
589
|
+
margin: "0 auto"
|
|
590
|
+
}}
|
|
591
|
+
>
|
|
592
|
+
<div style={{aspectRatio: "16 / 9"}}>
|
|
593
|
+
<VideoPlayer
|
|
594
|
+
src="https://example.com/master.m3u8"
|
|
595
|
+
poster="https://example.com/poster.webp"
|
|
596
|
+
title="Example video"
|
|
597
|
+
/>
|
|
598
|
+
</div>
|
|
599
|
+
</main>
|
|
600
|
+
);
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
ReactDOM.createRoot(
|
|
604
|
+
document.getElementById("root")!
|
|
605
|
+
).render(
|
|
606
|
+
<React.StrictMode>
|
|
607
|
+
<App />
|
|
608
|
+
</React.StrictMode>
|
|
609
|
+
);
|
|
610
|
+
```
|
|
611
|
+
|
|
612
|
+
---
|
|
613
|
+
|
|
614
|
+
## Private Streams
|
|
615
|
+
|
|
616
|
+
The player can consume:
|
|
617
|
+
|
|
618
|
+
- Authenticated playlist URLs
|
|
619
|
+
- Signed URLs
|
|
620
|
+
- Presigned object-storage URLs
|
|
621
|
+
- Protected encryption key URLs
|
|
622
|
+
- Private CDN URLs
|
|
623
|
+
- API-proxied HLS playlists
|
|
624
|
+
|
|
625
|
+
Example:
|
|
626
|
+
|
|
627
|
+
```tsx
|
|
628
|
+
<VideoPlayer
|
|
629
|
+
src={video.playlistUrl}
|
|
630
|
+
keySrc={video.keyUrl}
|
|
631
|
+
poster={video.posterUrl}
|
|
632
|
+
title={video.title}
|
|
633
|
+
/>
|
|
634
|
+
```
|
|
635
|
+
|
|
636
|
+
Recommended protected delivery flow:
|
|
637
|
+
|
|
638
|
+
```text
|
|
639
|
+
Frontend
|
|
640
|
+
↓
|
|
641
|
+
Request authorized video information
|
|
642
|
+
↓
|
|
643
|
+
Backend validates the authenticated user
|
|
644
|
+
↓
|
|
645
|
+
Backend returns a protected playlist URL
|
|
646
|
+
↓
|
|
647
|
+
Backend returns a protected key URL
|
|
648
|
+
↓
|
|
649
|
+
Player starts HLS playback
|
|
650
|
+
```
|
|
651
|
+
|
|
652
|
+
Recommended security approaches:
|
|
653
|
+
|
|
654
|
+
- Keep master playlists private.
|
|
655
|
+
- Keep rendition playlists private.
|
|
656
|
+
- Keep AES-128 keys private.
|
|
657
|
+
- Generate short-lived signed URLs.
|
|
658
|
+
- Bind URLs to authenticated users when possible.
|
|
659
|
+
- Validate active subscriptions.
|
|
660
|
+
- Use CDN token authentication.
|
|
661
|
+
- Configure CORS correctly.
|
|
662
|
+
- Avoid exposing permanent object-storage URLs.
|
|
663
|
+
|
|
664
|
+
---
|
|
665
|
+
|
|
666
|
+
## CORS Configuration
|
|
667
|
+
|
|
668
|
+
HLS playlists, segments, and encryption keys must allow requests from the application origin.
|
|
669
|
+
|
|
670
|
+
Example headers:
|
|
671
|
+
|
|
672
|
+
```http
|
|
673
|
+
Access-Control-Allow-Origin: https://your-app.example.com
|
|
674
|
+
Access-Control-Allow-Methods: GET, HEAD, OPTIONS
|
|
675
|
+
Access-Control-Allow-Headers: Range, Authorization, Content-Type
|
|
676
|
+
Access-Control-Expose-Headers: Content-Length, Content-Range, Accept-Ranges
|
|
677
|
+
```
|
|
678
|
+
|
|
679
|
+
When authentication cookies are required:
|
|
680
|
+
|
|
681
|
+
```http
|
|
682
|
+
Access-Control-Allow-Credentials: true
|
|
683
|
+
```
|
|
684
|
+
|
|
685
|
+
Do not combine credentials with:
|
|
686
|
+
|
|
687
|
+
```http
|
|
688
|
+
Access-Control-Allow-Origin: *
|
|
689
|
+
```
|
|
690
|
+
|
|
691
|
+
Instead, return the exact allowed origin:
|
|
692
|
+
|
|
693
|
+
```http
|
|
694
|
+
Access-Control-Allow-Origin: https://your-app.example.com
|
|
695
|
+
```
|
|
696
|
+
|
|
697
|
+
### Nginx Example
|
|
698
|
+
|
|
699
|
+
```nginx
|
|
700
|
+
location /videos/ {
|
|
701
|
+
add_header Access-Control-Allow-Origin "https://your-app.example.com" always;
|
|
702
|
+
add_header Access-Control-Allow-Methods "GET, HEAD, OPTIONS" always;
|
|
703
|
+
add_header Access-Control-Allow-Headers "Authorization, Range, Content-Type" always;
|
|
704
|
+
add_header Access-Control-Expose-Headers "Content-Length, Content-Range, Accept-Ranges" always;
|
|
705
|
+
|
|
706
|
+
if ($request_method = OPTIONS) {
|
|
707
|
+
return 204;
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
```
|
|
711
|
+
|
|
712
|
+
---
|
|
713
|
+
|
|
714
|
+
## Visibility Handling
|
|
715
|
+
|
|
716
|
+
The player automatically pauses video when the browser tab becomes hidden:
|
|
717
|
+
|
|
718
|
+
```ts
|
|
719
|
+
document.addEventListener(
|
|
720
|
+
"visibilitychange",
|
|
721
|
+
visibilityChangeHandler
|
|
722
|
+
);
|
|
723
|
+
```
|
|
724
|
+
|
|
725
|
+
The player also uses `IntersectionObserver`.
|
|
726
|
+
|
|
727
|
+
If less than 50% of the player is visible:
|
|
728
|
+
|
|
729
|
+
```text
|
|
730
|
+
Video pauses automatically
|
|
731
|
+
```
|
|
732
|
+
|
|
733
|
+
If the video was playing before leaving the viewport:
|
|
734
|
+
|
|
735
|
+
```text
|
|
736
|
+
Video resumes when it becomes visible again
|
|
737
|
+
```
|
|
738
|
+
|
|
739
|
+
The observer threshold is:
|
|
740
|
+
|
|
741
|
+
```ts
|
|
742
|
+
{
|
|
743
|
+
threshold: [0, 0.5, 1]
|
|
744
|
+
}
|
|
745
|
+
```
|
|
746
|
+
|
|
747
|
+
---
|
|
748
|
+
|
|
749
|
+
## Error Handling
|
|
750
|
+
|
|
751
|
+
The player handles native media errors and `hls.js` errors.
|
|
752
|
+
|
|
753
|
+
### Non-Fatal HLS Errors
|
|
754
|
+
|
|
755
|
+
Non-fatal HLS warnings are logged and playback continues:
|
|
756
|
+
|
|
757
|
+
```ts
|
|
758
|
+
if (!data.fatal) {
|
|
759
|
+
console.warn("Hls non-fatal warning:", data);
|
|
760
|
+
return;
|
|
761
|
+
}
|
|
762
|
+
```
|
|
763
|
+
|
|
764
|
+
An example of a recoverable warning is:
|
|
765
|
+
|
|
766
|
+
```text
|
|
767
|
+
bufferSeekOverHole
|
|
768
|
+
```
|
|
769
|
+
|
|
770
|
+
### Network Errors
|
|
771
|
+
|
|
772
|
+
Network errors attempt to restart loading:
|
|
773
|
+
|
|
774
|
+
```ts
|
|
775
|
+
hls.startLoad();
|
|
776
|
+
```
|
|
777
|
+
|
|
778
|
+
### Media Errors
|
|
779
|
+
|
|
780
|
+
Media errors attempt media recovery:
|
|
781
|
+
|
|
782
|
+
```ts
|
|
783
|
+
hls.recoverMediaError();
|
|
784
|
+
```
|
|
785
|
+
|
|
786
|
+
### Fatal Errors
|
|
787
|
+
|
|
788
|
+
Fatal and unrecoverable errors show the player error state.
|
|
789
|
+
|
|
790
|
+
```ts
|
|
791
|
+
errorHandler();
|
|
792
|
+
hls.destroy();
|
|
793
|
+
```
|
|
794
|
+
|
|
795
|
+
### Native Media Errors
|
|
796
|
+
|
|
797
|
+
The player handles browser media error codes:
|
|
798
|
+
|
|
799
|
+
| Error code | Description |
|
|
800
|
+
| --- | --- |
|
|
801
|
+
| `MEDIA_ERR_ABORTED` | Video loading was aborted |
|
|
802
|
+
| `MEDIA_ERR_NETWORK` | A network error occurred |
|
|
803
|
+
| `MEDIA_ERR_DECODE` | The browser could not decode the video |
|
|
804
|
+
| `MEDIA_ERR_SRC_NOT_SUPPORTED` | Video source or format is unsupported |
|
|
805
|
+
|
|
806
|
+
---
|
|
807
|
+
|
|
808
|
+
## Styling and Sizing
|
|
809
|
+
|
|
810
|
+
Always import the player stylesheet:
|
|
811
|
+
|
|
812
|
+
```tsx
|
|
813
|
+
import "@alirezahosseini/sibtorsh-player/dist/sibtorsh-player.css";
|
|
814
|
+
```
|
|
815
|
+
|
|
816
|
+
The player uses:
|
|
817
|
+
|
|
818
|
+
```text
|
|
819
|
+
width: 100%
|
|
820
|
+
height: 100%
|
|
821
|
+
```
|
|
822
|
+
|
|
823
|
+
The parent element must provide the required dimensions.
|
|
824
|
+
|
|
825
|
+
### Aspect Ratio Container
|
|
826
|
+
|
|
827
|
+
```tsx
|
|
828
|
+
<div
|
|
829
|
+
style={{
|
|
830
|
+
width: "100%",
|
|
831
|
+
maxWidth: 1200,
|
|
832
|
+
aspectRatio: "16 / 9"
|
|
833
|
+
}}
|
|
834
|
+
>
|
|
835
|
+
<VideoPlayer src="https://example.com/master.m3u8" />
|
|
836
|
+
</div>
|
|
837
|
+
```
|
|
838
|
+
|
|
839
|
+
### Fixed Height Container
|
|
840
|
+
|
|
841
|
+
```tsx
|
|
842
|
+
<div
|
|
843
|
+
style={{
|
|
844
|
+
width: "100%",
|
|
845
|
+
height: 500
|
|
846
|
+
}}
|
|
847
|
+
>
|
|
848
|
+
<VideoPlayer src="https://example.com/master.m3u8" />
|
|
849
|
+
</div>
|
|
850
|
+
```
|
|
851
|
+
|
|
852
|
+
### Tailwind CSS Container
|
|
853
|
+
|
|
854
|
+
```tsx
|
|
855
|
+
<div className="aspect-video w-full overflow-hidden rounded-xl">
|
|
856
|
+
<VideoPlayer src="https://example.com/master.m3u8" />
|
|
857
|
+
</div>
|
|
858
|
+
```
|
|
859
|
+
|
|
860
|
+
The player includes its own prefixed classes and responsive container-query behavior.
|
|
861
|
+
|
|
862
|
+
---
|
|
863
|
+
|
|
864
|
+
## Browser Support
|
|
865
|
+
|
|
866
|
+
| Browser | Playback method | Support |
|
|
867
|
+
| --- | --- | --- |
|
|
868
|
+
| Chrome | `hls.js` and native video | Supported |
|
|
869
|
+
| Microsoft Edge | `hls.js` and native video | Supported |
|
|
870
|
+
| Firefox | `hls.js` and native video | Supported |
|
|
871
|
+
| Safari macOS | Native HLS | Supported |
|
|
872
|
+
| Safari iOS | Native HLS | Supported |
|
|
873
|
+
| Chrome Android | `hls.js` or native video | Supported |
|
|
874
|
+
| Installed PWA | Browser-dependent | Supported with fallback behavior |
|
|
875
|
+
|
|
876
|
+
Actual codec support depends on:
|
|
877
|
+
|
|
878
|
+
- Browser
|
|
879
|
+
- Operating system
|
|
880
|
+
- Hardware decoder
|
|
881
|
+
- Video codec
|
|
882
|
+
- Audio codec
|
|
883
|
+
- HLS segment format
|
|
884
|
+
|
|
885
|
+
### Recommended Codecs
|
|
886
|
+
|
|
887
|
+
For broad compatibility:
|
|
888
|
+
|
|
889
|
+
```text
|
|
890
|
+
Video codec: H.264 / AVC
|
|
891
|
+
Audio codec: AAC
|
|
892
|
+
Playlist: HLS
|
|
893
|
+
Segments: MPEG-TS or compatible fragmented MP4
|
|
894
|
+
```
|
|
895
|
+
|
|
896
|
+
---
|
|
897
|
+
|
|
898
|
+
## Troubleshooting
|
|
899
|
+
|
|
900
|
+
### Player Has Zero Height
|
|
901
|
+
|
|
902
|
+
The player fills its parent element.
|
|
903
|
+
|
|
904
|
+
Incorrect:
|
|
905
|
+
|
|
906
|
+
```tsx
|
|
907
|
+
<div>
|
|
908
|
+
<VideoPlayer src="https://example.com/master.m3u8" />
|
|
909
|
+
</div>
|
|
910
|
+
```
|
|
911
|
+
|
|
912
|
+
Correct:
|
|
913
|
+
|
|
914
|
+
```tsx
|
|
915
|
+
<div style={{aspectRatio: "16 / 9"}}>
|
|
916
|
+
<VideoPlayer src="https://example.com/master.m3u8" />
|
|
917
|
+
</div>
|
|
918
|
+
```
|
|
919
|
+
|
|
920
|
+
---
|
|
921
|
+
|
|
922
|
+
### Player Styles Are Missing
|
|
923
|
+
|
|
924
|
+
Import the distributed CSS once:
|
|
925
|
+
|
|
926
|
+
```tsx
|
|
927
|
+
import "@alirezahosseini/sibtorsh-player/dist/sibtorsh-player.css";
|
|
928
|
+
```
|
|
929
|
+
|
|
930
|
+
---
|
|
931
|
+
|
|
932
|
+
### HLS Works in Safari but Not Chrome
|
|
933
|
+
|
|
934
|
+
Verify that:
|
|
935
|
+
|
|
936
|
+
- The master playlist allows CORS.
|
|
937
|
+
- Rendition playlists allow CORS.
|
|
938
|
+
- Segment URLs are valid.
|
|
939
|
+
- Key URLs are valid.
|
|
940
|
+
- Signed URLs have not expired.
|
|
941
|
+
- The stream uses supported codecs.
|
|
942
|
+
- The response content type is correct.
|
|
943
|
+
|
|
944
|
+
Recommended playlist content type:
|
|
945
|
+
|
|
946
|
+
```http
|
|
947
|
+
Content-Type: application/vnd.apple.mpegurl
|
|
948
|
+
```
|
|
949
|
+
|
|
950
|
+
---
|
|
951
|
+
|
|
952
|
+
### HLS Works in Chrome but Not Safari
|
|
953
|
+
|
|
954
|
+
Safari uses native HLS.
|
|
955
|
+
|
|
956
|
+
Verify that:
|
|
957
|
+
|
|
958
|
+
- The Safari playlist contains valid URLs.
|
|
959
|
+
- Key URLs are directly accessible by Safari.
|
|
960
|
+
- Cookies are configured correctly.
|
|
961
|
+
- The backend accepts `is_safari=1`.
|
|
962
|
+
- Rendition playlist URLs are absolute or correctly resolvable.
|
|
963
|
+
- Segment URLs have not expired.
|
|
964
|
+
|
|
965
|
+
---
|
|
966
|
+
|
|
967
|
+
### Quality Options Are Missing
|
|
968
|
+
|
|
969
|
+
Quality options are generated from the master playlist.
|
|
970
|
+
|
|
971
|
+
Make sure the source contains:
|
|
972
|
+
|
|
973
|
+
```m3u8
|
|
974
|
+
#EXT-X-STREAM-INF
|
|
975
|
+
```
|
|
976
|
+
|
|
977
|
+
A single rendition playlist does not provide multiple quality options.
|
|
978
|
+
|
|
979
|
+
---
|
|
980
|
+
|
|
981
|
+
### Safari Quality Switching Does Not Work
|
|
982
|
+
|
|
983
|
+
Make sure the master playlist can be fetched from JavaScript:
|
|
984
|
+
|
|
985
|
+
```ts
|
|
986
|
+
fetch(masterPlaylistUrl, {
|
|
987
|
+
method: "GET",
|
|
988
|
+
credentials: "include",
|
|
989
|
+
headers: {
|
|
990
|
+
Accept: "application/vnd.apple.mpegurl"
|
|
991
|
+
}
|
|
992
|
+
});
|
|
993
|
+
```
|
|
994
|
+
|
|
995
|
+
The playlist response must allow CORS.
|
|
996
|
+
|
|
997
|
+
---
|
|
998
|
+
|
|
999
|
+
### `bufferSeekOverHole` Warning
|
|
1000
|
+
|
|
1001
|
+
This is usually a recoverable `hls.js` warning.
|
|
1002
|
+
|
|
1003
|
+
Example:
|
|
1004
|
+
|
|
1005
|
+
```text
|
|
1006
|
+
fragment loaded with buffer holes, seeking from 0 to 0.1
|
|
1007
|
+
```
|
|
1008
|
+
|
|
1009
|
+
If:
|
|
1010
|
+
|
|
1011
|
+
```text
|
|
1012
|
+
fatal: false
|
|
1013
|
+
```
|
|
1014
|
+
|
|
1015
|
+
the player should continue playback.
|
|
1016
|
+
|
|
1017
|
+
Repeated gaps in the middle of a video may indicate timestamp discontinuities between HLS segments.
|
|
1018
|
+
|
|
1019
|
+
---
|
|
1020
|
+
|
|
1021
|
+
### Stream Returns 403
|
|
1022
|
+
|
|
1023
|
+
Check:
|
|
1024
|
+
|
|
1025
|
+
- Playlist signed URL expiration
|
|
1026
|
+
- Rendition signed URL expiration
|
|
1027
|
+
- Segment signed URL expiration
|
|
1028
|
+
- Key signed URL expiration
|
|
1029
|
+
- Authentication cookies
|
|
1030
|
+
- Authorization headers
|
|
1031
|
+
- User subscription
|
|
1032
|
+
- CORS configuration
|
|
1033
|
+
- CDN rules
|
|
1034
|
+
- Referrer restrictions
|
|
1035
|
+
|
|
1036
|
+
---
|
|
1037
|
+
|
|
1038
|
+
### Video Does Not Resume After Quality Change
|
|
1039
|
+
|
|
1040
|
+
Safari quality switching waits for `loadedmetadata`, restores the previous playback time, and then resumes playback.
|
|
1041
|
+
|
|
1042
|
+
Make sure the selected rendition:
|
|
1043
|
+
|
|
1044
|
+
- Is accessible
|
|
1045
|
+
- Has a valid duration
|
|
1046
|
+
- Uses compatible timestamps
|
|
1047
|
+
- Uses the same content timeline
|
|
1048
|
+
|
|
1049
|
+
---
|
|
1050
|
+
|
|
1051
|
+
### Fullscreen Does Not Work
|
|
1052
|
+
|
|
1053
|
+
Fullscreen APIs require direct user interaction.
|
|
1054
|
+
|
|
1055
|
+
Call fullscreen from:
|
|
1056
|
+
|
|
1057
|
+
- A click event
|
|
1058
|
+
- A touch event
|
|
1059
|
+
- A keyboard event initiated by the user
|
|
1060
|
+
|
|
1061
|
+
Browsers may block fullscreen requests triggered automatically.
|
|
1062
|
+
|
|
1063
|
+
---
|
|
1064
|
+
|
|
1065
|
+
## TypeScript
|
|
1066
|
+
|
|
1067
|
+
Type declarations are included in the package.
|
|
1068
|
+
|
|
1069
|
+
No separate type package is required:
|
|
1070
|
+
|
|
1071
|
+
```bash
|
|
1072
|
+
npm install @alirezahosseini/sibtorsh-player
|
|
1073
|
+
```
|
|
1074
|
+
|
|
1075
|
+
Import:
|
|
1076
|
+
|
|
1077
|
+
```tsx
|
|
1078
|
+
import VideoPlayer from "@alirezahosseini/sibtorsh-player";
|
|
1079
|
+
```
|
|
1080
|
+
|
|
1081
|
+
Example typed component:
|
|
1082
|
+
|
|
1083
|
+
```tsx
|
|
1084
|
+
import VideoPlayer from "@alirezahosseini/sibtorsh-player";
|
|
1085
|
+
|
|
1086
|
+
interface LessonPlayerProps {
|
|
1087
|
+
playlistUrl: string;
|
|
1088
|
+
keyUrl?: string;
|
|
1089
|
+
posterUrl?: string;
|
|
1090
|
+
title?: string;
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
export default function LessonPlayer({
|
|
1094
|
+
playlistUrl,
|
|
1095
|
+
keyUrl,
|
|
1096
|
+
posterUrl,
|
|
1097
|
+
title
|
|
1098
|
+
}: LessonPlayerProps) {
|
|
1099
|
+
return (
|
|
1100
|
+
<div style={{aspectRatio: "16 / 9"}}>
|
|
1101
|
+
<VideoPlayer
|
|
1102
|
+
src={playlistUrl}
|
|
1103
|
+
keySrc={keyUrl}
|
|
1104
|
+
poster={posterUrl}
|
|
1105
|
+
title={title}
|
|
1106
|
+
/>
|
|
1107
|
+
</div>
|
|
1108
|
+
);
|
|
1109
|
+
}
|
|
1110
|
+
```
|
|
1111
|
+
|
|
1112
|
+
---
|
|
1113
|
+
|
|
1114
|
+
## Development
|
|
1115
|
+
|
|
1116
|
+
Install dependencies:
|
|
1117
|
+
|
|
1118
|
+
```bash
|
|
1119
|
+
npm install
|
|
73
1120
|
```
|
|
1121
|
+
|
|
1122
|
+
Start development mode:
|
|
1123
|
+
|
|
1124
|
+
```bash
|
|
1125
|
+
npm run dev
|
|
1126
|
+
```
|
|
1127
|
+
|
|
1128
|
+
Build the package:
|
|
1129
|
+
|
|
1130
|
+
```bash
|
|
1131
|
+
npm run build
|
|
1132
|
+
```
|
|
1133
|
+
|
|
1134
|
+
The build process generates:
|
|
1135
|
+
|
|
1136
|
+
```text
|
|
1137
|
+
dist/sibtorsh-player.es.js
|
|
1138
|
+
dist/sibtorsh-player.umd.js
|
|
1139
|
+
dist/sibtorsh-player.css
|
|
1140
|
+
dist/*.d.ts
|
|
1141
|
+
```
|
|
1142
|
+
|
|
1143
|
+
---
|
|
1144
|
+
|
|
1145
|
+
## Publishing
|
|
1146
|
+
|
|
1147
|
+
npm does not allow publishing over an existing version.
|
|
1148
|
+
|
|
1149
|
+
Before publishing, increment the package version.
|
|
1150
|
+
|
|
1151
|
+
### Patch Release
|
|
1152
|
+
|
|
1153
|
+
Use for fixes and small changes:
|
|
1154
|
+
|
|
1155
|
+
```bash
|
|
1156
|
+
npm version patch
|
|
1157
|
+
npm run build
|
|
1158
|
+
npm publish
|
|
1159
|
+
```
|
|
1160
|
+
|
|
1161
|
+
Example:
|
|
1162
|
+
|
|
1163
|
+
```text
|
|
1164
|
+
0.3.1 → 0.3.2
|
|
1165
|
+
```
|
|
1166
|
+
|
|
1167
|
+
### Minor Release
|
|
1168
|
+
|
|
1169
|
+
Use for new backward-compatible features:
|
|
1170
|
+
|
|
1171
|
+
```bash
|
|
1172
|
+
npm version minor
|
|
1173
|
+
npm run build
|
|
1174
|
+
npm publish
|
|
1175
|
+
```
|
|
1176
|
+
|
|
1177
|
+
Example:
|
|
1178
|
+
|
|
1179
|
+
```text
|
|
1180
|
+
0.3.1 → 0.4.0
|
|
1181
|
+
```
|
|
1182
|
+
|
|
1183
|
+
### Major Release
|
|
1184
|
+
|
|
1185
|
+
Use for breaking changes:
|
|
1186
|
+
|
|
1187
|
+
```bash
|
|
1188
|
+
npm version major
|
|
1189
|
+
npm run build
|
|
1190
|
+
npm publish
|
|
1191
|
+
```
|
|
1192
|
+
|
|
1193
|
+
Example:
|
|
1194
|
+
|
|
1195
|
+
```text
|
|
1196
|
+
0.3.1 → 1.0.0
|
|
1197
|
+
```
|
|
1198
|
+
|
|
1199
|
+
### Public Package
|
|
1200
|
+
|
|
1201
|
+
```bash
|
|
1202
|
+
npm publish --access public
|
|
1203
|
+
```
|
|
1204
|
+
|
|
1205
|
+
### Restricted Package
|
|
1206
|
+
|
|
1207
|
+
```bash
|
|
1208
|
+
npm publish --access restricted
|
|
1209
|
+
```
|
|
1210
|
+
|
|
1211
|
+
Restricted packages require an npm plan that supports private packages.
|
|
1212
|
+
|
|
1213
|
+
### Verify Current npm User
|
|
1214
|
+
|
|
1215
|
+
```bash
|
|
1216
|
+
npm whoami
|
|
1217
|
+
```
|
|
1218
|
+
|
|
1219
|
+
### Verify Registry
|
|
1220
|
+
|
|
1221
|
+
```bash
|
|
1222
|
+
npm config get registry
|
|
1223
|
+
```
|
|
1224
|
+
|
|
1225
|
+
Expected registry:
|
|
1226
|
+
|
|
1227
|
+
```text
|
|
1228
|
+
https://registry.npmjs.org/
|
|
1229
|
+
```
|
|
1230
|
+
|
|
1231
|
+
### Preview Package Contents
|
|
1232
|
+
|
|
1233
|
+
Before publishing:
|
|
1234
|
+
|
|
1235
|
+
```bash
|
|
1236
|
+
npm pack --dry-run
|
|
1237
|
+
```
|
|
1238
|
+
|
|
1239
|
+
---
|
|
1240
|
+
|
|
1241
|
+
## Security
|
|
1242
|
+
|
|
1243
|
+
Client-side JavaScript cannot completely prevent an authorized user from:
|
|
1244
|
+
|
|
1245
|
+
- Inspecting network requests
|
|
1246
|
+
- Inspecting playlist URLs
|
|
1247
|
+
- Inspecting downloaded package files
|
|
1248
|
+
- Reading bundled JavaScript
|
|
1249
|
+
- Copying previously downloaded media
|
|
1250
|
+
- Recording the screen
|
|
1251
|
+
|
|
1252
|
+
For stronger content protection, combine the player with:
|
|
1253
|
+
|
|
1254
|
+
- Private HLS playlists
|
|
1255
|
+
- Private rendition playlists
|
|
1256
|
+
- AES-128 encryption
|
|
1257
|
+
- Private encryption keys
|
|
1258
|
+
- Short-lived signed URLs
|
|
1259
|
+
- Session-bound authorization
|
|
1260
|
+
- User-bound authorization
|
|
1261
|
+
- Domain validation
|
|
1262
|
+
- CDN token authentication
|
|
1263
|
+
- Watermarking
|
|
1264
|
+
- Concurrent-session limits
|
|
1265
|
+
- DRM for high-value content
|
|
1266
|
+
|
|
1267
|
+
Supported DRM systems may include:
|
|
1268
|
+
|
|
1269
|
+
```text
|
|
1270
|
+
Google Widevine
|
|
1271
|
+
Apple FairPlay
|
|
1272
|
+
Microsoft PlayReady
|
|
1273
|
+
```
|
|
1274
|
+
|
|
1275
|
+
Sibtorsh Player currently focuses on HLS and AES-128 delivery and does not claim to provide full DRM protection.
|
|
1276
|
+
|
|
1277
|
+
---
|
|
1278
|
+
|
|
1279
|
+
## Package Information
|
|
1280
|
+
|
|
1281
|
+
```text
|
|
1282
|
+
Package: @alirezahosseini/sibtorsh-player
|
|
1283
|
+
Framework: React
|
|
1284
|
+
Language: TypeScript
|
|
1285
|
+
HLS engine: hls.js
|
|
1286
|
+
Module formats: ES Module and UMD
|
|
1287
|
+
Stylesheet: Included
|
|
1288
|
+
Type declarations: Included
|
|
1289
|
+
```
|
|
1290
|
+
|
|
1291
|
+
---
|
|
1292
|
+
|
|
1293
|
+
## License
|
|
1294
|
+
|
|
1295
|
+
This package is distributed under the license defined by the project owner.
|
|
1296
|
+
|
|
1297
|
+
Before public or commercial distribution, update the `license` field in `package.json`.
|
|
1298
|
+
|
|
1299
|
+
Example proprietary configuration:
|
|
1300
|
+
|
|
1301
|
+
```json
|
|
1302
|
+
{
|
|
1303
|
+
"license": "UNLICENSED"
|
|
1304
|
+
}
|
|
1305
|
+
```
|
|
1306
|
+
|
|
1307
|
+
Example MIT configuration:
|
|
1308
|
+
|
|
1309
|
+
```json
|
|
1310
|
+
{
|
|
1311
|
+
"license": "MIT"
|
|
1312
|
+
}
|
|
1313
|
+
```
|
|
1314
|
+
|
|
1315
|
+
---
|
|
1316
|
+
|
|
1317
|
+
## Author
|
|
1318
|
+
|
|
1319
|
+
**Alireza Hosseini**
|
|
1320
|
+
|
|
1321
|
+
npm package:
|
|
1322
|
+
|
|
1323
|
+
```text
|
|
1324
|
+
@alirezahosseini/sibtorsh-player
|
|
1325
|
+
```
|