@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 CHANGED
@@ -1,73 +1,1325 @@
1
- # React + TypeScript + Vite
2
-
3
- This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
4
-
5
- Currently, two official plugins are available:
6
-
7
- - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) (or [oxc](https://oxc.rs) when used in [rolldown-vite](https://vite.dev/guide/rolldown)) for Fast Refresh
8
- - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
9
-
10
- ## React Compiler
11
-
12
- The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation).
13
-
14
- ## Expanding the ESLint configuration
15
-
16
- If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:
17
-
18
- ```js
19
- export default defineConfig([
20
- globalIgnores(['dist']),
21
- {
22
- files: ['**/*.{ts,tsx}'],
23
- extends: [
24
- // Other configs...
25
-
26
- // Remove tseslint.configs.recommended and replace with this
27
- tseslint.configs.recommendedTypeChecked,
28
- // Alternatively, use this for stricter rules
29
- tseslint.configs.strictTypeChecked,
30
- // Optionally, add this for stylistic rules
31
- tseslint.configs.stylisticTypeChecked,
32
-
33
- // Other configs...
34
- ],
35
- languageOptions: {
36
- parserOptions: {
37
- project: ['./tsconfig.node.json', './tsconfig.app.json'],
38
- tsconfigRootDir: import.meta.dirname,
39
- },
40
- // other options...
41
- },
42
- },
43
- ])
44
- ```
45
-
46
- You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules:
47
-
48
- ```js
49
- // eslint.config.js
50
- import reactX from 'eslint-plugin-react-x'
51
- import reactDom from 'eslint-plugin-react-dom'
52
-
53
- export default defineConfig([
54
- globalIgnores(['dist']),
55
- {
56
- files: ['**/*.{ts,tsx}'],
57
- extends: [
58
- // Other configs...
59
- // Enable lint rules for React
60
- reactX.configs['recommended-typescript'],
61
- // Enable lint rules for React DOM
62
- reactDom.configs.recommended,
63
- ],
64
- languageOptions: {
65
- parserOptions: {
66
- project: ['./tsconfig.node.json', './tsconfig.app.json'],
67
- tsconfigRootDir: import.meta.dirname,
68
- },
69
- // other options...
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
+ [![npm version](https://img.shields.io/npm/v/@alirezahosseini/sibtorsh-player.svg)](https://www.npmjs.com/package/@alirezahosseini/sibtorsh-player)
6
+ [![TypeScript](https://img.shields.io/badge/TypeScript-ready-blue.svg)](https://www.typescriptlang.org/)
7
+ [![React](https://img.shields.io/badge/React-18%2B-61dafb.svg)](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
+ ```