@zezosoft/react-player 0.0.8 → 0.0.10

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.
Files changed (45) hide show
  1. package/README.md +536 -146
  2. package/dist/VideoPlayer/VideoPlayer.d.ts +1 -0
  3. package/dist/VideoPlayer/components/AdOverlay.d.ts +10 -0
  4. package/dist/VideoPlayer/components/ErrorOverlay.d.ts +8 -0
  5. package/dist/VideoPlayer/components/Overlay.d.ts +4 -0
  6. package/dist/VideoPlayer/components/SubtitleOverlay.d.ts +7 -0
  7. package/dist/VideoPlayer/components/controls/BottomControls.d.ts +5 -0
  8. package/dist/VideoPlayer/components/controls/ControlsHeader.d.ts +5 -0
  9. package/dist/VideoPlayer/components/controls/MiddleControls.d.ts +3 -0
  10. package/dist/VideoPlayer/components/controls/VideoPlayerControls.d.ts +4 -0
  11. package/dist/VideoPlayer/components/controls/index.d.ts +4 -0
  12. package/dist/VideoPlayer/components/time-line/TimeLine.d.ts +21 -0
  13. package/dist/VideoPlayer/components/time-line/components/HoverTimeWithPreview.d.ts +16 -0
  14. package/dist/VideoPlayer/components/time-line/components/Thumb.d.ts +9 -0
  15. package/dist/VideoPlayer/components/time-line/components/TimeCodeItem.d.ts +21 -0
  16. package/dist/VideoPlayer/components/time-line/components/TimeCodes.d.ts +15 -0
  17. package/dist/VideoPlayer/components/time-line/utils/getEndTimeByIndex.d.ts +2 -0
  18. package/dist/VideoPlayer/components/time-line/utils/getHoverTimePosition.d.ts +3 -0
  19. package/dist/VideoPlayer/components/time-line/utils/getPositionPercent.d.ts +1 -0
  20. package/dist/VideoPlayer/components/time-line/utils/getTimeScale.d.ts +1 -0
  21. package/dist/VideoPlayer/components/time-line/utils/isInRange.d.ts +1 -0
  22. package/dist/VideoPlayer/components/time-line/utils/positionToMs.d.ts +1 -0
  23. package/dist/VideoPlayer/components/time-line/utils/secondsToTime.d.ts +6 -0
  24. package/dist/VideoPlayer/components/time-line/utils/timeToTimeString.d.ts +1 -0
  25. package/dist/VideoPlayer/constants.d.ts +3 -0
  26. package/dist/VideoPlayer/hooks/index.d.ts +4 -0
  27. package/dist/VideoPlayer/hooks/useAdManager.d.ts +8 -0
  28. package/dist/VideoPlayer/hooks/useNetworkSpeed.d.ts +7 -0
  29. package/dist/VideoPlayer/hooks/usePrimaryVideoLifecycle.d.ts +17 -0
  30. package/dist/VideoPlayer/hooks/useVideoError.d.ts +7 -0
  31. package/dist/VideoPlayer/hooks/useVideoSource.d.ts +1 -14
  32. package/dist/VideoPlayer/hooks/useVideoTracking.d.ts +2 -2
  33. package/dist/VideoPlayer/types/AdTypes.d.ts +33 -0
  34. package/dist/VideoPlayer/types/VideoPlayerTypes.d.ts +34 -10
  35. package/dist/VideoPlayer/utils/index.d.ts +1 -1
  36. package/dist/VideoPlayer/utils/qualityManager.d.ts +6 -32
  37. package/dist/components/ui/FullScreenToggle.d.ts +1 -1
  38. package/dist/components/ui/PiPictureInPictureToggle.d.ts +1 -1
  39. package/dist/index.d.ts +1 -1
  40. package/dist/index.js +2487 -493
  41. package/dist/store/slices/adsSlice.d.ts +24 -0
  42. package/dist/store/slices/errorSlice.d.ts +5 -0
  43. package/dist/store/slices/index.d.ts +2 -0
  44. package/dist/store/types/StoreTypes.d.ts +41 -9
  45. package/package.json +12 -11
package/README.md CHANGED
@@ -1,35 +1,69 @@
1
1
  # @zezosoft/react-player 🎬
2
2
 
3
- A powerful and flexible **React video player** by **Zezosoft**, supporting HLS, MP4, preview thumbnails, tracking, subtitles, episode playback, and advanced controls.
3
+ A powerful and flexible **React video player** by **Zezosoft**, supporting HLS, MP4, DASH, YouTube, preview thumbnails, tracking, subtitles, episode playback, ads, and advanced controls.
4
4
 
5
5
  ---
6
6
 
7
7
  ## 🚀 Features
8
8
 
9
- ✅ **Supports HLS, MP4**
10
- ✅ **Preview Thumbnails on Hover**
11
- ✅ **Event Tracking (Views, Watch Time, etc.)**
12
- ✅ **Customizable Player Size & Controls**
13
- ✅ **Time-Stamped Labels for Video Chapters**
14
- ✅ **Subtitles (WebVTT)**
15
- ✅ **Intro Skipping**
16
- ✅ **Next Episode Auto Play and Button**
9
+ ✅ **Multiple Video Formats** - HLS, MP4, DASH, YouTube
10
+ ✅ **Preview Thumbnails on Hover** - Show video previews while scrubbing
11
+ ✅ **Event Tracking** - Track views, watch time, and user interactions
12
+ ✅ **Customizable Player Size & Controls** - Full control over player appearance
13
+ ✅ **Time-Stamped Labels** - Video chapters with time markers
14
+ ✅ **Subtitles (WebVTT)** - Multi-language subtitle support with custom styling
15
+ ✅ **Intro Skipping** - Automatic skip intro button
16
+ ✅ **Episode Playback** - Next episode auto-play and playlist support
17
+ ✅ **Ad Support** - Pre-roll, mid-roll, post-roll, and overlay ads
18
+ ✅ **Watch History** - Track user progress and resume playback
17
19
 
18
20
  ---
19
21
 
20
22
  ## 📦 Installation
21
23
 
22
- Install the package using **npm**
24
+ Install the package using **npm** or **yarn**:
23
25
 
24
26
  ```sh
25
27
  npm install @zezosoft/react-player
26
28
  ```
27
29
 
30
+ or
31
+
32
+ ```sh
33
+ yarn add @zezosoft/react-player
34
+ ```
35
+
28
36
  ---
29
37
 
30
- ## 🛠️ Basic Usage
38
+ ## 🛠️ Quick Start
31
39
 
32
- Import and use the `VideoPlayer` component in your React project:
40
+ Here's a simple example to get you started:
41
+
42
+ ```tsx
43
+ import { VideoPlayer } from "@zezosoft/react-player";
44
+
45
+ function App() {
46
+ return (
47
+ <VideoPlayer
48
+ video={{
49
+ src: "https://bitdash-a.akamaihd.net/content/sintel/hls/playlist.m3u8",
50
+ title: "My Video",
51
+ poster: "https://example.com/poster.jpg",
52
+ }}
53
+ style={{
54
+ width: "100%",
55
+ height: "auto",
56
+ }}
57
+ />
58
+ );
59
+ }
60
+ ```
61
+
62
+ ---
63
+
64
+ ## 📖 Complete Example
65
+
66
+ Here's a comprehensive example showing all available features:
33
67
 
34
68
  ```tsx
35
69
  import { useCallback, useRef } from "react";
@@ -53,42 +87,99 @@ function App() {
53
87
  }, []);
54
88
 
55
89
  return (
56
- <div className="w-[720px]">
90
+ <div style={{ width: "720px", margin: "0 auto" }}>
57
91
  <VideoPlayer
58
- trackPoster="https://i.ytimg.com/vi/VAUfyxw-Yvk/maxresdefault.jpg"
59
- trackSrc="https://bitdash-a.akamaihd.net/content/sintel/hls/playlist.m3u8"
60
- trackTitle="Mehmaan"
61
- width="720px"
62
- height="405px"
63
- timeCodes={[
64
- { fromMs: 0, description: "Introduction" },
65
- { fromMs: 130000, description: "Exciting Scene" },
66
- { fromMs: 270000, description: "Climax" },
67
- ]}
68
- getPreviewScreenUrl={handleGettingPreview}
69
- tracking={{
70
- onViewed: () => console.log("Video Viewed"),
71
- onWatchTimeUpdated: (e) => console.log("Watch Time Updated", e),
92
+ video={{
93
+ src: "https://bitdash-a.akamaihd.net/content/sintel/hls/playlist.m3u8",
94
+ title: "Mehmaan",
95
+ poster: "https://i.ytimg.com/vi/VAUfyxw-Yvk/maxresdefault.jpg",
96
+ type: "hls",
97
+ isTrailer: false,
98
+ showControls: true,
99
+ isMute: false,
100
+ startFrom: 0,
101
+ }}
102
+ style={{
103
+ width: "720px",
104
+ height: "405px",
105
+ className: "my-video-player",
106
+ subtitleStyle: {
107
+ fontSize: "1.5rem",
108
+ backgroundColor: "rgba(0, 0, 0, 0.8)",
109
+ textColor: "#ffffff",
110
+ position: "bottom",
111
+ borderRadius: "8px",
112
+ padding: "8px 16px",
113
+ maxWidth: "80%",
114
+ },
115
+ }}
116
+ events={{
117
+ onEnded: (e) => console.log("Video ended", e),
118
+ onError: (e) => console.error("Video error", e),
119
+ onClose: () => console.log("Player closed"),
120
+ onWatchHistoryUpdate: (data) => {
121
+ console.log("Watch history:", data);
122
+ // Save to your backend
123
+ },
72
124
  }}
73
- subtitles={[
74
- {
75
- lang: "en",
76
- label: "English",
77
- url: "https://example.com/subtitles-en.vtt",
125
+ features={{
126
+ timeCodes: [
127
+ { fromMs: 0, description: "Introduction" },
128
+ { fromMs: 130000, description: "Exciting Scene" },
129
+ { fromMs: 270000, description: "Climax" },
130
+ ],
131
+ getPreviewScreenUrl: handleGettingPreview,
132
+ tracking: {
133
+ onViewed: () => console.log("Video Viewed"),
134
+ onWatchTimeUpdated: (e) => {
135
+ console.log("Watch Time Updated", {
136
+ watchTime: e.watchTime,
137
+ currentTime: e.currentTime,
138
+ duration: e.duration,
139
+ });
140
+ },
78
141
  },
79
- {
80
- lang: "hi",
81
- label: "Hindi",
82
- url: "https://example.com/subtitles-hi.vtt",
142
+ subtitles: [
143
+ {
144
+ lang: "en",
145
+ label: "English",
146
+ url: "https://example.com/subtitles-en.vtt",
147
+ },
148
+ {
149
+ lang: "hi",
150
+ label: "Hindi",
151
+ url: "https://example.com/subtitles-hi.vtt",
152
+ },
153
+ ],
154
+ episodeList: [
155
+ { id: 1, title: "Episode 1", url: "https://example.com/ep1.m3u8" },
156
+ { id: 2, title: "Episode 2", url: "https://example.com/ep2.m3u8" },
157
+ ],
158
+ currentEpisodeIndex: 0,
159
+ intro: { start: 5, end: 20 },
160
+ nextEpisodeConfig: { showAtTime: 300, showAtEnd: true },
161
+ ads: {
162
+ preRoll: {
163
+ id: "preroll-1",
164
+ type: "pre-roll",
165
+ time: 0,
166
+ adUrl: "https://example.com/ad.mp4",
167
+ skipable: true,
168
+ skipAfter: 5,
169
+ },
170
+ midRoll: [
171
+ {
172
+ id: "midroll-1",
173
+ type: "mid-roll",
174
+ time: 120,
175
+ adUrl: "https://example.com/mid-ad.mp4",
176
+ skipable: false,
177
+ },
178
+ ],
179
+ onAdStart: (adBreak) => console.log("Ad started", adBreak),
180
+ onAdEnd: (adBreak) => console.log("Ad ended", adBreak),
83
181
  },
84
- ]}
85
- episodeList={[
86
- { id: 1, title: "Episode 1", url: "https://example.com/ep1.m3u8" },
87
- { id: 2, title: "Episode 2", url: "https://example.com/ep2.m3u8" },
88
- ]}
89
- currentEpisodeIndex={0}
90
- intro={{ start: 5, end: 20 }}
91
- nextEpisodeConfig={{ showAtTime: 300, showAtEnd: true }}
182
+ }}
92
183
  />
93
184
  </div>
94
185
  );
@@ -99,185 +190,484 @@ export default App;
99
190
 
100
191
  ---
101
192
 
102
- ## 🎨 Props Reference
103
-
104
- | Prop Name | Type | Default | Description |
105
- | --------------------- | ----------------------------------------------------- | ----------- | ---------------------------------------------------------- |
106
- | `trackPoster` | `string` | `""` | URL of the video poster image. |
107
- | `trackSrc` | `string` | `""` | Video source URL (MP4, HLS, etc.). |
108
- | `trackTitle` | `string` | `""` | Title of the video. |
109
- | `isTrailer` | `boolean` | `false` | Specifies if the video is a trailer. |
110
- | `width` | `string` | `"100%"` | Width of the video player. |
111
- | `height` | `string` | `"auto"` | Height of the video player. |
112
- | `timeCodes` | `Array<{ fromMs: number, description: string }>` | `[]` | List of time-based markers with descriptions. |
113
- | `getPreviewScreenUrl` | `(timeMs: number) => string` | `null` | Function to generate preview screen URLs based on time. |
114
- | `tracking` | `object` | `{}` | Tracking event callbacks. |
115
- | `subtitles` | `Array<{ lang: string; label: string; url: string }>` | `[]` | Subtitle tracks in WebVTT format. |
116
- | `episodeList` | `Array<{ id: number; title: string; url: string }>` | `[]` | List of episodes to support autoplay/playlist. |
117
- | `currentEpisodeIndex` | `number` | `0` | Index of currently playing episode. |
118
- | `intro` | `{ start: number; end: number }` | `undefined` | Defines intro duration in seconds. |
119
- | `nextEpisodeConfig` | `{ showAtTime?: number; showAtEnd?: boolean }` | `undefined` | When to show next episode UI — at specific time or at end. |
120
- | |
193
+ ## 📚 Props Reference
121
194
 
122
- ---
195
+ The `VideoPlayer` component accepts four main prop objects:
123
196
 
124
- ## 📢 Tracking Events
197
+ ### `video` (Required)
125
198
 
126
- | Event Name | Description |
127
- | -------------------- | ------------------------------------------------ |
128
- | `onViewed` | Triggered when the video starts playing. |
129
- | `onWatchTimeUpdated` | Triggered when user leaves with >30s watch time. |
199
+ Video source and basic configuration.
130
200
 
131
- #### Example usage:
201
+ | Prop | Type | Default | Description |
202
+ | -------------- | -------------------------------------------------- | ------------ | ------------------------------------------------------- |
203
+ | `src` | `string` | **Required** | Video source URL (MP4, HLS, DASH, YouTube, etc.) |
204
+ | `title` | `string` | `""` | Title of the video displayed in the player header |
205
+ | `poster` | `string` | `""` | URL of the poster/thumbnail image shown before playback |
206
+ | `type` | `"hls" \| "dash" \| "mp4" \| "youtube" \| "other"` | `undefined` | Video format type (auto-detected if not provided) |
207
+ | `isTrailer` | `boolean` | `false` | If `true`, shows trailer-specific UI elements |
208
+ | `showControls` | `boolean` | `true` | Show/hide player controls |
209
+ | `isMute` | `boolean` | `false` | Start video muted |
210
+ | `startFrom` | `number` | `0` | Start playback from specific time in seconds |
132
211
 
133
- ```tsx
134
- tracking={{
135
- onViewed: () => console.log("Viewed!"),
136
- onWatchTimeUpdated: ({ watchTime }) =>
137
- console.log("Total watch time (sec):", watchTime),
138
- }}
139
- ```
212
+ ### `style` (Optional)
140
213
 
141
- ---
214
+ Styling and appearance configuration.
142
215
 
143
- ## 🎨 Customization & Styling
216
+ | Prop | Type | Default | Description |
217
+ | --------------- | --------------------- | ----------- | ---------------------------------------------- |
218
+ | `className` | `string` | `undefined` | Custom CSS class name for the player container |
219
+ | `width` | `string` | `"100%"` | Player width (e.g., `"720px"`, `"100%"`) |
220
+ | `height` | `string` | `"auto"` | Player height (e.g., `"405px"`, `"auto"`) |
221
+ | `subtitleStyle` | `SubtitleStyleConfig` | `undefined` | Custom styling for subtitles (see below) |
144
222
 
145
- 🔹 Change Player Dimensions
223
+ **SubtitleStyleConfig:**
146
224
 
147
- Modify `width` and `height`:
225
+ | Prop | Type | Default | Description |
226
+ | ----------------- | ------------------------------- | --------------------------------------------- | ---------------------------------- |
227
+ | `fontSize` | `string` | `"1.75rem"` | Subtitle font size |
228
+ | `backgroundColor` | `string` | `"linear-gradient(135deg, #fbbf24, #f59e0b)"` | Subtitle background color/gradient |
229
+ | `textColor` | `string` | `"#000000"` | Subtitle text color |
230
+ | `position` | `"top" \| "center" \| "bottom"` | `"bottom"` | Vertical position of subtitles |
231
+ | `borderRadius` | `string` | `"12px"` | Border radius of subtitle box |
232
+ | `padding` | `string` | `"12px 20px"` | Padding inside subtitle box |
233
+ | `maxWidth` | `string` | `"80%"` | Maximum width of subtitle box |
148
234
 
149
- ```tsx
150
- <VideoPlayer width="800px" height="450px" />
235
+ ### `events` (Optional)
236
+
237
+ Event callbacks for player lifecycle.
238
+
239
+ | Prop | Type | Description |
240
+ | ---------------------- | ------------------------------------------------------------- | -------------------------------------------------- |
241
+ | `onEnded` | `(e: React.SyntheticEvent<HTMLVideoElement>) => void` | Called when video playback ends |
242
+ | `onError` | `(e?: React.SyntheticEvent<HTMLVideoElement, Event>) => void` | Called when video encounters an error |
243
+ | `onClose` | `() => void` | Called when player is closed |
244
+ | `onWatchHistoryUpdate` | `(data: WatchHistoryData) => void` | Called when player closes with watch progress data |
245
+
246
+ **WatchHistoryData:**
247
+
248
+ ```typescript
249
+ {
250
+ currentTime: number; // Current playback time in seconds
251
+ duration: number; // Total video duration in seconds
252
+ progress: number; // Progress percentage (0-100)
253
+ isCompleted: boolean; // Whether video was fully watched
254
+ watchedAt: number; // Timestamp when watch session ended
255
+ }
151
256
  ```
152
257
 
153
- 🔹 Custom Preview Thumbnails
258
+ ### `features` (Optional)
259
+
260
+ Advanced features and functionality.
261
+
262
+ | Prop | Type | Default | Description |
263
+ | --------------------- | ------------------------------------------------ | ----------- | ------------------------------------------------------------------------ |
264
+ | `timeCodes` | `Array<{ fromMs: number, description: string }>` | `[]` | Time-based chapter markers (in milliseconds) |
265
+ | `getPreviewScreenUrl` | `(hoverTimeValue: number) => string` | `undefined` | Function to generate preview thumbnail URLs while hovering over seek bar |
266
+ | `tracking` | `TrackingConfig` | `undefined` | Event tracking configuration (see below) |
267
+ | `subtitles` | `Array<SubtitleTrack>` | `[]` | Subtitle tracks in WebVTT format |
268
+ | `episodeList` | `Array<Episode>` | `[]` | List of episodes for playlist/autoplay |
269
+ | `currentEpisodeIndex` | `number` | `0` | Index of currently playing episode |
270
+ | `intro` | `{ start: number, end: number }` | `undefined` | Intro skip configuration (times in seconds) |
271
+ | `nextEpisodeConfig` | `{ showAtTime?: number, showAtEnd?: boolean }` | `undefined` | Configuration for next episode button |
272
+ | `ads` | `AdConfig` | `undefined` | Advertisement configuration (see below) |
273
+
274
+ **TrackingConfig:**
275
+
276
+ ```typescript
277
+ {
278
+ onViewed?: () => void;
279
+ onWatchTimeUpdated?: (e: {
280
+ watchTime?: number; // Total watch time in seconds
281
+ currentTime: number; // Current playback position
282
+ duration: number; // Total video duration
283
+ }) => void;
284
+ }
285
+ ```
154
286
 
155
- Dynamically generate preview images:
287
+ **SubtitleTrack:**
156
288
 
157
- ```tsx
158
- const getPreviewImage = (hoverTime) =>
159
- `https://fakeimg.pl/720x405?text=${hoverTime}`;
160
- <VideoPlayer getPreviewScreenUrl={getPreviewImage} />;
289
+ ```typescript
290
+ {
291
+ lang: string; // Language code (e.g., "en", "hi", "fr")
292
+ label: string; // Display label (e.g., "English", "Hindi")
293
+ url: string; // URL to WebVTT subtitle file
294
+ }
161
295
  ```
162
296
 
163
- 🔹 Time-Stamps for Video Sections
297
+ **Episode:**
164
298
 
165
- Mark important video sections:
299
+ ```typescript
300
+ {
301
+ id: number; // Unique episode identifier
302
+ title: string; // Episode title
303
+ url: string; // Episode video URL
304
+ }
305
+ ```
306
+
307
+ **AdConfig:**
308
+
309
+ ```typescript
310
+ {
311
+ preRoll?: AdBreak; // Pre-roll ad (plays before video)
312
+ midRoll?: AdBreak[]; // Mid-roll ads (plays during video)
313
+ postRoll?: AdBreak; // Post-roll ad (plays after video)
314
+ overlay?: { // Overlay ad (image overlay)
315
+ imageUrl: string;
316
+ clickUrl?: string;
317
+ showDuration: number;
318
+ position?: "top-left" | "top-right" | "bottom-left" | "bottom-right";
319
+ };
320
+ smartPlacement?: { // Smart ad placement
321
+ enabled: boolean;
322
+ minVideoDuration?: number;
323
+ minGapBetweenAds?: number;
324
+ avoidNearEnd?: number;
325
+ preferNaturalBreaks?: boolean;
326
+ };
327
+ onAdStart?: (adBreak: AdBreak) => void;
328
+ onAdEnd?: (adBreak: AdBreak) => void;
329
+ onAdSkip?: (adBreak: AdBreak) => void;
330
+ onAdError?: (adBreak: AdBreak, error: Error) => void;
331
+ }
332
+ ```
333
+
334
+ **AdBreak:**
335
+
336
+ ```typescript
337
+ {
338
+ id: string; // Unique ad identifier
339
+ type: "pre-roll" | "mid-roll" | "post-roll" | "overlay";
340
+ time: number; // Time in seconds when ad should play
341
+ adUrl: string; // URL to ad video
342
+ skipable?: boolean; // Whether ad can be skipped
343
+ skipAfter?: number; // Seconds before skip button appears
344
+ duration?: number; // Ad duration in seconds
345
+ sponsoredUrl?: string; // URL for sponsored content
346
+ title?: string; // Ad title
347
+ description?: string; // Ad description
348
+ relevance?: "high" | "medium" | "low";
349
+ }
350
+ ```
351
+
352
+ ---
353
+
354
+ ## 🎯 Usage Examples
355
+
356
+ ### Basic Video Player
166
357
 
167
358
  ```tsx
168
359
  <VideoPlayer
169
- timeCodes={[
170
- { fromMs: 0, description: "Intro" },
171
- { fromMs: 120000, description: "Main Scene" },
172
- ]}
360
+ video={{
361
+ src: "https://example.com/video.mp4",
362
+ title: "My Video",
363
+ }}
173
364
  />
174
365
  ```
175
366
 
176
- 🔹 Subtitles Support
177
-
178
- Add support for multiple subtitle tracks in .vtt format:
367
+ ### HLS Video with Poster
179
368
 
180
369
  ```tsx
181
- subtitles={[
182
- {
183
- lang: "en",
184
- label: "English",
185
- url: "https://example.com/subtitles-en.vtt",
186
- },
187
- {
188
- lang: "fr",
189
- label: "French",
190
- url: "https://example.com/subtitles-fr.vtt",
191
- },
192
- ]}
370
+ <VideoPlayer
371
+ video={{
372
+ src: "https://example.com/playlist.m3u8",
373
+ type: "hls",
374
+ poster: "https://example.com/poster.jpg",
375
+ title: "Streaming Video",
376
+ }}
377
+ style={{
378
+ width: "100%",
379
+ height: "450px",
380
+ }}
381
+ />
193
382
  ```
194
383
 
195
- 🔹 Skip Intro Button
384
+ ### Video with Preview Thumbnails
385
+
386
+ ```tsx
387
+ const getPreview = (hoverTime: number) => {
388
+ // Generate thumbnail URL based on hover time
389
+ return `https://example.com/thumbnails/${Math.floor(hoverTime)}.jpg`;
390
+ };
391
+
392
+ <VideoPlayer
393
+ video={{ src: "https://example.com/video.mp4" }}
394
+ features={{
395
+ getPreviewScreenUrl: getPreview,
396
+ }}
397
+ />;
398
+ ```
196
399
 
197
- Automatically show a "Skip Intro" button between a specific start and end time
400
+ ### Video with Chapters
198
401
 
199
402
  ```tsx
200
- <VideoPlayer intro={{ start: 5, end: 20 }} />
403
+ <VideoPlayer
404
+ video={{ src: "https://example.com/video.mp4" }}
405
+ features={{
406
+ timeCodes: [
407
+ { fromMs: 0, description: "Introduction" },
408
+ { fromMs: 60000, description: "Chapter 1" },
409
+ { fromMs: 120000, description: "Chapter 2" },
410
+ { fromMs: 180000, description: "Conclusion" },
411
+ ],
412
+ }}
413
+ />
201
414
  ```
202
415
 
203
- 🔹 Next Episode Playback
416
+ ### Video with Subtitles
204
417
 
205
- Automatically show "Next Episode" option when video ends or reaches a set time
418
+ ```tsx
419
+ <VideoPlayer
420
+ video={{ src: "https://example.com/video.mp4" }}
421
+ features={{
422
+ subtitles: [
423
+ {
424
+ lang: "en",
425
+ label: "English",
426
+ url: "https://example.com/subtitles-en.vtt",
427
+ },
428
+ {
429
+ lang: "es",
430
+ label: "Spanish",
431
+ url: "https://example.com/subtitles-es.vtt",
432
+ },
433
+ ],
434
+ }}
435
+ style={{
436
+ subtitleStyle: {
437
+ fontSize: "1.25rem",
438
+ backgroundColor: "rgba(0, 0, 0, 0.75)",
439
+ textColor: "#ffffff",
440
+ position: "bottom",
441
+ },
442
+ }}
443
+ />
444
+ ```
445
+
446
+ ### Video with Intro Skip
206
447
 
207
448
  ```tsx
208
449
  <VideoPlayer
209
- nextEpisodeConfig={{
210
- showAtTime: 300,
211
- showAtEnd: true,
450
+ video={{ src: "https://example.com/video.mp4" }}
451
+ features={{
452
+ intro: {
453
+ start: 10, // Intro starts at 10 seconds
454
+ end: 45, // Intro ends at 45 seconds
455
+ },
212
456
  }}
213
457
  />
214
458
  ```
215
459
 
216
- 🔹 Episode Playlist Support
460
+ ### Episode Playlist
461
+
462
+ ```tsx
463
+ const [currentEpisode, setCurrentEpisode] = useState(0);
464
+
465
+ const episodes = [
466
+ { id: 1, title: "Episode 1", url: "https://example.com/ep1.m3u8" },
467
+ { id: 2, title: "Episode 2", url: "https://example.com/ep2.m3u8" },
468
+ { id: 3, title: "Episode 3", url: "https://example.com/ep3.m3u8" },
469
+ ];
470
+
471
+ <VideoPlayer
472
+ video={{
473
+ src: episodes[currentEpisode].url,
474
+ title: episodes[currentEpisode].title,
475
+ }}
476
+ features={{
477
+ episodeList: episodes,
478
+ currentEpisodeIndex: currentEpisode,
479
+ nextEpisodeConfig: {
480
+ showAtTime: 300, // Show next episode button 5 minutes before end
481
+ showAtEnd: true, // Also show at video end
482
+ },
483
+ }}
484
+ events={{
485
+ onEnded: () => {
486
+ if (currentEpisode < episodes.length - 1) {
487
+ setCurrentEpisode(currentEpisode + 1);
488
+ }
489
+ },
490
+ }}
491
+ />;
492
+ ```
217
493
 
218
- Pass a playlist of episodes and control which one plays currently
494
+ ### Video with Tracking
219
495
 
220
496
  ```tsx
221
497
  <VideoPlayer
222
- episodeList={[
223
- { id: 1, title: "Episode 1", url: "https://example.com/ep1.m3u8" },
224
- { id: 2, title: "Episode 2", url: "https://example.com/ep2.m3u8" },
225
- ]}
226
- currentEpisodeIndex={0}
498
+ video={{ src: "https://example.com/video.mp4" }}
499
+ features={{
500
+ tracking: {
501
+ onViewed: () => {
502
+ // Track video view
503
+ analytics.track("video_viewed", { videoId: "123" });
504
+ },
505
+ onWatchTimeUpdated: ({ watchTime, currentTime, duration }) => {
506
+ // Track watch time
507
+ analytics.track("watch_time_updated", {
508
+ watchTime,
509
+ progress: (currentTime / duration) * 100,
510
+ });
511
+ },
512
+ },
513
+ }}
227
514
  />
228
515
  ```
229
516
 
230
- 🔹 Tracking Events
517
+ ### Video with Watch History
518
+
519
+ ```tsx
520
+ <VideoPlayer
521
+ video={{ src: "https://example.com/video.mp4" }}
522
+ events={{
523
+ onWatchHistoryUpdate: async (data) => {
524
+ // Save watch history to backend
525
+ await fetch("/api/watch-history", {
526
+ method: "POST",
527
+ headers: { "Content-Type": "application/json" },
528
+ body: JSON.stringify({
529
+ videoId: "123",
530
+ currentTime: data.currentTime,
531
+ duration: data.duration,
532
+ progress: data.progress,
533
+ isCompleted: data.isCompleted,
534
+ }),
535
+ });
536
+ },
537
+ }}
538
+ />
539
+ ```
231
540
 
232
- Track when the video is viewed or how much time was watched
541
+ ### Video with Ads
233
542
 
234
543
  ```tsx
235
544
  <VideoPlayer
236
- tracking={{
237
- onViewed: () => console.log("User viewed the video."),
238
- onWatchTimeUpdated: ({ watchTime }) =>
239
- console.log("Total watch time in seconds:", watchTime),
545
+ video={{ src: "https://example.com/video.mp4" }}
546
+ features={{
547
+ ads: {
548
+ preRoll: {
549
+ id: "preroll-1",
550
+ type: "pre-roll",
551
+ time: 0,
552
+ adUrl: "https://example.com/pre-roll-ad.mp4",
553
+ skipable: true,
554
+ skipAfter: 5,
555
+ },
556
+ midRoll: [
557
+ {
558
+ id: "midroll-1",
559
+ type: "mid-roll",
560
+ time: 120,
561
+ adUrl: "https://example.com/mid-roll-ad.mp4",
562
+ skipable: false,
563
+ },
564
+ ],
565
+ onAdStart: (adBreak) => {
566
+ console.log("Ad started:", adBreak.id);
567
+ },
568
+ onAdEnd: (adBreak) => {
569
+ console.log("Ad ended:", adBreak.id);
570
+ },
571
+ },
240
572
  }}
241
573
  />
242
574
  ```
243
575
 
576
+ ### Resume from Last Position
577
+
578
+ ```tsx
579
+ const [watchHistory, setWatchHistory] = useState(null);
580
+
581
+ // Load watch history on mount
582
+ useEffect(() => {
583
+ fetch("/api/watch-history/123")
584
+ .then((res) => res.json())
585
+ .then((data) => setWatchHistory(data));
586
+ }, []);
587
+
588
+ <VideoPlayer
589
+ video={{
590
+ src: "https://example.com/video.mp4",
591
+ startFrom: watchHistory?.currentTime || 0,
592
+ }}
593
+ events={{
594
+ onWatchHistoryUpdate: (data) => {
595
+ // Save progress
596
+ setWatchHistory(data);
597
+ },
598
+ }}
599
+ />;
600
+ ```
601
+
244
602
  ---
245
603
 
246
604
  ## ❓ Troubleshooting
247
605
 
248
- #### Video not playing?
606
+ ### Video Not Playing?
249
607
 
250
- - Check if the `trackSrc` URL is correct.
251
- - Ensure the video format (MP4, HLS) is supported.
252
- - If using HLS, ensure you're serving files correctly (CORS issues may block playback).
608
+ - **Check the video URL**: Ensure `video.src` is a valid, accessible URL
609
+ - **Check video format**: Verify the video format is supported (HLS, MP4, DASH, YouTube)
610
+ - **CORS issues**: If using HLS or external sources, ensure CORS headers are properly configured
611
+ - **Specify video type**: Try explicitly setting `video.type` to help with format detection
253
612
 
254
- #### ❌ Subtitles not showing?
613
+ ```tsx
614
+ <VideoPlayer
615
+ video={{
616
+ src: "https://example.com/video.m3u8",
617
+ type: "hls", // Explicitly specify type
618
+ }}
619
+ />
620
+ ```
255
621
 
256
- - Check that .vtt files are correctly hosted and publicly accessible.
257
- - Ensure the subtitles prop includes proper lang, label, and url.
622
+ ### Subtitles Not Showing?
258
623
 
259
- #### Preview thumbnails not loading?
624
+ - **Check VTT file URL**: Ensure subtitle URLs are publicly accessible
625
+ - **Verify VTT format**: Ensure WebVTT files are properly formatted
626
+ - **Check CORS**: Subtitle files must be accessible from your domain
627
+ - **Test subtitle URL**: Open the subtitle URL directly in a browser to verify it loads
260
628
 
261
- - Confirm `getPreviewScreenUrl` is returning a valid image URL.
262
- - Use the browser console (F12) to check errors.
629
+ ### Preview Thumbnails Not Loading?
630
+
631
+ - **Verify function returns URL**: Ensure `getPreviewScreenUrl` returns a valid image URL
632
+ - **Check image loading**: The function should return a URL that loads successfully
633
+ - **Use browser console**: Check for image loading errors in the browser console (F12)
634
+
635
+ ### Player Not Responsive?
636
+
637
+ - **Use percentage widths**: Set `style.width` to `"100%"` for responsive behavior
638
+ - **Container styling**: Wrap the player in a container with appropriate CSS
639
+ - **Height auto**: Use `style.height: "auto"` to maintain aspect ratio
640
+
641
+ ```tsx
642
+ <div style={{ maxWidth: "1200px", margin: "0 auto" }}>
643
+ <VideoPlayer
644
+ video={{ src: "https://example.com/video.mp4" }}
645
+ style={{
646
+ width: "100%",
647
+ height: "auto",
648
+ }}
649
+ />
650
+ </div>
651
+ ```
263
652
 
264
- #### Player not responsive?
653
+ ### Ads Not Playing?
265
654
 
266
- - Make sure you're setting `width="100%"` for fluid responsiveness
267
- - Wrap the player inside a `div` with CSS styles
655
+ - **Check ad URLs**: Ensure ad video URLs are valid and accessible
656
+ - **Verify ad timing**: For mid-roll ads, ensure `time` is less than video duration
657
+ - **Check ad format**: Ad videos should be in supported formats (MP4, HLS)
268
658
 
269
659
  ---
270
660
 
271
661
  ## 🔗 Related Links
272
662
 
273
663
  - 📚 [Official Documentation](https://github.com/zezosoft/react-player)
274
- - 🛠 [Issues & Support](https://github.com/zezosoft/react-player)
664
+ - 🛠 [Issues & Support](https://github.com/zezosoft/react-player/issues)
275
665
 
276
666
  ---
277
667
 
278
668
  ## 📝 License
279
669
 
280
- Licensed under the MIT License.
670
+ Licensed under the MIT License.
281
671
  Developed by [Zezosoft](https://zezosoft.com). 🚀
282
672
 
283
673
  ---