l-min-components 1.0.1117 → 1.0.1121

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "l-min-components",
3
- "version": "1.0.1117",
3
+ "version": "1.0.1121",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "src/assets",
@@ -24,6 +24,7 @@
24
24
  "draft-js": "^0.11.7",
25
25
  "draftjs-to-html": "^0.9.1",
26
26
  "emoji-picker-react": "^4.12.0",
27
+ "hls.js": "^1.5.20",
27
28
  "html-to-draftjs": "^1.5.0",
28
29
  "i": "^0.3.7",
29
30
  "iso-639-1": "^3.1.2",
@@ -39,7 +39,6 @@ export { default as EnterpriseRightBar } from "./fileRightBar/enterpriseRightBar
39
39
  export { default as FullPageLoader } from "./fullPageLoader";
40
40
  export { default as AdminAppMainLayout } from "./AdminAppMainLayout";
41
41
  export { default as PaginationComponent } from "./paginate";
42
- export { default as VideoPlayer } from "./video-player";
43
42
  export { default as AdminLogin } from "./AdminLogin";
44
43
  export { default as AdminResetPassword } from "./AdminResetPassword";
45
44
  export { default as AdminChangePassword } from "./AdminResetPassword/change-password";
@@ -54,3 +53,4 @@ export { default as MobileLayout } from "./mobileLayout";
54
53
  export { default as useTranslation } from "../hooks/useTranslation";
55
54
  export { default as ImageComponent } from "./ImageComponent";
56
55
  export { default as DeveloperBanner } from "./banner/developerBanner";
56
+ export { default as VideoPlayer } from "./video-player";
@@ -1,66 +1,53 @@
1
- import React, { useState, useRef, useEffect, useContext } from "react";
1
+ import {
2
+ useState,
3
+ useRef,
4
+ useEffect,
5
+ useContext,
6
+ useMemo,
7
+ useCallback,
8
+ } from "react";
2
9
  import { OutletContext } from "../AppMainLayout";
10
+ import FullPageLoader from "../fullPageLoader";
3
11
  import screenfull from "screenfull";
12
+ import Hls from "hls.js";
4
13
  import {
5
14
  LoadingScreen,
6
15
  RangeContainer,
7
16
  VideoController,
8
17
  VideoPlayerContainer,
9
- VideoPrompts,
10
18
  } from "./index.styled";
11
- import ReactPlayer from "react-player";
12
19
  import VideoScreenIcon from "./icons/videoScreenIcon";
13
20
  import PlayIcon from "./icons/playVideoIcon";
14
21
  import PauseIcon from "./icons/pause";
15
- // import LoaderCustom from "../loader";
16
- import FullPageLoader from "../fullPageLoader";
17
22
 
18
- /**
19
- *
20
- * @param {object} props
21
- * @param {string} props.width - Width of the player in px
22
- * @param {string} props.height - Height of the player in px
23
- * @param {React.CSSProperties} props.style - Player container style
24
- * @param {boolean} props.hideRange - To hide the range controller
25
- * @param {boolean} props.showPrompts - show next and prev controller
26
- * @param {string} props.src - Video source url
27
- * @param {string} props.streamUrl - Stream video url
28
- * @param {string} props.thumbnailSrc - Thumbnail url
29
- * @returns {JSX.Element}
30
- */
31
23
  const VideoPlayer = ({
32
24
  width,
33
25
  height = "360px",
34
26
  style,
35
27
  hideRange,
36
28
  showPrompts,
29
+ controlSize,
37
30
  src,
38
31
  streamUrl,
39
- thumbnailSrc,
32
+ small,
40
33
  }) => {
41
- const [isPlaying, setIsPlayering] = useState(false);
34
+ const [isPlaying, setIsPlaying] = useState(false);
42
35
  const [isSeeking, setIsSeeking] = useState(false);
36
+ const [error, setError] = useState(null);
43
37
  const [progress, setProgress] = useState();
44
38
  const [hideController, setHideController] = useState(false);
45
39
  const [count, setCount] = useState(0);
46
- const playContainerRef = useRef();
47
- const [url, setUrl] = useState(null);
48
- const [image, setImage] = useState(null);
49
- const playerRef = useRef();
50
- const { accessToken } = useContext(OutletContext);
51
40
  const [isReady, setIsReady] = useState(false);
52
41
  const [rangeProgress, setRangeProgress] = useState(0);
53
42
 
54
- useEffect(() => {
55
- if (src || streamUrl) {
56
- setUrl(src ? src : `https://dev-117782726-api.learngual.com${streamUrl}`);
57
- }
58
- if (thumbnailSrc) {
59
- setImage(thumbnailSrc);
60
- }
61
- }, [streamUrl, thumbnailSrc, src]);
43
+ const playContainerRef = useRef();
44
+ const videoRef = useRef();
45
+ const { accessToken, generalData } = useContext(OutletContext);
62
46
 
63
- const format = (secs) => {
47
+ const accountId = generalData?.selectedAccount?.id;
48
+ const url = useMemo(() => src || `${streamUrl}`, [src, streamUrl]);
49
+
50
+ const formatTime = useCallback((secs) => {
64
51
  const sec_num = parseInt(secs, 10);
65
52
  const hours = Math.floor(sec_num / 3600);
66
53
  const minutes = Math.floor(sec_num / 60) % 60;
@@ -69,18 +56,22 @@ const VideoPlayer = ({
69
56
  return [hours, minutes, seconds]
70
57
  .map((v) => (v < 10 ? "0" + v : v))
71
58
  .join(":");
72
- };
59
+ }, []);
73
60
 
74
- const handlePlayPause = () => {
75
- setIsPlayering(!isPlaying);
76
- setImage(null);
77
- };
61
+ const handlePlayPause = useCallback(() => {
62
+ setIsPlaying((prev) => !prev);
63
+ if (videoRef.current) {
64
+ isPlaying ? videoRef.current.pause() : videoRef.current.play();
65
+ }
66
+ }, [isPlaying]);
78
67
 
79
- const handleFullScreen = () => {
80
- screenfull.toggle(playContainerRef.current);
81
- };
68
+ const handleFullScreen = useCallback(() => {
69
+ if (screenfull.isEnabled) {
70
+ screenfull.toggle(playContainerRef.current);
71
+ }
72
+ }, []);
82
73
 
83
- const handleProgress = (progress) => {
74
+ const handleProgress = useCallback(() => {
84
75
  if (count > 3) {
85
76
  setHideController(true);
86
77
  setCount(0);
@@ -88,11 +79,15 @@ const VideoPlayer = ({
88
79
  if (!hideController) {
89
80
  setCount(count + 1);
90
81
  }
91
- if (!isSeeking) {
92
- setRangeProgress(progress?.played);
93
- setProgress(progress);
82
+ if (videoRef?.current) {
83
+ const currentTime = videoRef.current.currentTime;
84
+ const duration = videoRef.current.duration;
85
+ const played = currentTime / duration;
86
+
87
+ setRangeProgress(played);
88
+ setProgress({ played, currentTime, duration });
94
89
  }
95
- };
90
+ }, [isSeeking, count, hideController]);
96
91
 
97
92
  const handleSeekChange = (e) => {
98
93
  const value = parseFloat(e.target.value) / 100;
@@ -101,7 +96,9 @@ const VideoPlayer = ({
101
96
 
102
97
  const handleSeekMouseUp = () => {
103
98
  setIsSeeking(false);
104
- playerRef.current?.seekTo(rangeProgress, "fraction");
99
+ if (videoRef.current) {
100
+ videoRef.current.currentTime = rangeProgress * videoRef.current.duration;
101
+ }
105
102
  };
106
103
 
107
104
  const handleSeekMouseDown = () => {
@@ -113,29 +110,56 @@ const VideoPlayer = ({
113
110
  setCount(0);
114
111
  };
115
112
 
116
- const calcShowPrompts = () => {
117
- if (progress?.playedSeconds) {
118
- return Math.floor(
119
- (progress.playedSeconds * 100) / progress.loadedSeconds,
120
- );
113
+ useEffect(() => {
114
+ let hls;
115
+
116
+ if (videoRef.current && accountId) {
117
+ if (!src && streamUrl && Hls.isSupported()) {
118
+ hls = new Hls({
119
+ xhrSetup: function (xhr, url) {
120
+ const modifiedURL = `${url}?_account=${accountId}`;
121
+ xhr.open("GET", modifiedURL, true);
122
+ xhr.setRequestHeader("Authorization", `Bearer ${accessToken}`);
123
+ },
124
+ });
125
+ hls.loadSource(url);
126
+ hls.attachMedia(videoRef.current);
127
+
128
+ hls.on(Hls.Events.MANIFEST_PARSED, () => {
129
+ setIsReady(true);
130
+ setError(null);
131
+ });
132
+
133
+ hls.on(Hls.Events.ERROR, (event, data) => {
134
+ if (data?.response?.code === 400) {
135
+ setError("This resource is being processed, please try again late");
136
+ }
137
+ });
138
+ } else if (
139
+ videoRef.current.canPlayType("application/vnd.apple.mpegurl") &&
140
+ !src
141
+ ) {
142
+ videoRef.current.src = url;
143
+ videoRef.current.addEventListener("loadedmetadata", () => {
144
+ setIsReady(true);
145
+ });
146
+ } else {
147
+ if (videoRef.current && src) {
148
+ videoRef.current.src = src;
149
+
150
+ videoRef.current.load();
151
+ setIsReady(true);
152
+ }
153
+ }
121
154
  }
122
- };
123
-
124
- const currentTime = playerRef.current?.getCurrentTime()
125
- ? format(playerRef.current?.getCurrentTime())
126
- : "00:00:00";
127
155
 
128
- const duration = playerRef.current?.getDuration()
129
- ? format(playerRef.current?.getDuration())
130
- : "00:00:00";
131
-
132
- useEffect(() => {
133
- if (progress?.loaded) {
134
- setIsReady(true);
135
- } else {
156
+ return () => {
157
+ if (hls) {
158
+ hls.destroy();
159
+ }
136
160
  setIsReady(false);
137
- }
138
- }, [progress?.loaded]);
161
+ };
162
+ }, [url, accountId, accessToken, videoRef?.current]);
139
163
 
140
164
  return (
141
165
  <VideoPlayerContainer
@@ -145,72 +169,37 @@ const VideoPlayer = ({
145
169
  height={height}
146
170
  onMouseMove={handleMouseMove}
147
171
  >
148
- {showPrompts && calcShowPrompts() > 80 && (
149
- <VideoPrompts>
150
- <div>
151
- <p>Continue watching</p>
152
- <p>Previuos Part (Lesson 1)</p>
153
- </div>
154
- <div>
155
- <p>Continue watching</p>
156
- <p>Next Part (Lesson 6)</p>
157
- </div>
158
- </VideoPrompts>
159
- )}
160
- {!isReady && !image && (
172
+ {!isReady && (
161
173
  <LoadingScreen>
162
- {/* <LoaderCustom zoom={0.5} /> */}
163
- <FullPageLoader isSectionLoader loaderWidth={300} />
174
+ <FullPageLoader
175
+ isSectionLoader
176
+ hasBackground
177
+ loaderWidth={small ? 150 : 200}
178
+ loaderHeight={small ? 150 : 200}
179
+ zIndex={5}
180
+ />
181
+
182
+ {error && <p className={`error ${small ? "sm" : ""}`}>{error}</p>}
164
183
  </LoadingScreen>
165
184
  )}
166
- <ReactPlayer
185
+
186
+ <video
187
+ ref={videoRef}
167
188
  width="100%"
168
- ref={playerRef}
169
189
  height="100%"
170
- url={url}
171
- // muted={true}
172
- step="any"
173
- light={image}
174
- playing={isPlaying}
175
- onProgress={handleProgress}
176
- onEnded={() => {
177
- setIsPlayering(false);
178
- setHideController(false);
179
- setCount(0);
180
- setProgress({ ...progress, played: 1 });
190
+ onTimeUpdate={handleProgress}
191
+ controls={false}
192
+ onCanPlay={() => {
193
+ setIsReady(true);
181
194
  }}
182
- config={
183
- streamUrl
184
- ? {
185
- file: {
186
- forceHLS: true,
187
- hlsOptions: {
188
- xhrSetup: function (xhr, url) {
189
- if (accessToken) {
190
- const modifiedURL = `${url}?_account=7d5529dcf5`;
191
- xhr.open("GET", modifiedURL, true);
192
- xhr.setRequestHeader(
193
- "Authorization",
194
- `Bearer ${accessToken}`,
195
- );
196
- }
197
- },
198
- },
199
- },
200
- hls: {
201
- enableLowInitialLatency: true,
202
- smoothQualityChange: true,
203
- startLevel: 0,
204
- },
205
- }
206
- : { file: null, hls: null }
207
- }
208
195
  />
209
- <VideoController hide={hideController}>
210
- {!hideRange && (!image || !isReady) && (
196
+
197
+ <VideoController hide={hideController} size={controlSize}>
198
+ {!hideRange && (
211
199
  <RangeContainer>
212
200
  <p>
213
- {currentTime} / {duration}
201
+ {formatTime(progress?.currentTime || 0)} /{" "}
202
+ {formatTime(progress?.duration || 0)}
214
203
  </p>
215
204
  <input
216
205
  type="range"
@@ -225,7 +214,7 @@ const VideoPlayer = ({
225
214
  <VideoScreenIcon onClick={handleFullScreen} />
226
215
  </RangeContainer>
227
216
  )}
228
- {(isReady || image) && (
217
+ {isReady && (
229
218
  <div className="play_pause_wrap" onClick={handlePlayPause}>
230
219
  {isPlaying ? <PauseIcon fill="#fff" /> : <PlayIcon fill="#fff" />}
231
220
  </div>
@@ -63,9 +63,12 @@ export const RangeContainer = styled.div`
63
63
  .range {
64
64
  border-radius: 9px;
65
65
  flex: 1;
66
+ width: 100%;
67
+ display: block;
66
68
  -webkit-appearance: none;
67
69
  appearance: none;
68
70
  overflow: hidden;
71
+ cursor: pointer;
69
72
  &::-webkit-slider-runnable-track {
70
73
  height: 15px;
71
74
  background: rgba(198, 204, 204, 0.4);
@@ -156,6 +159,23 @@ export const LoadingScreen = styled.div`
156
159
  bottom: 0;
157
160
  z-index: 40;
158
161
  background-color: rgba(0, 0, 0, 0.05);
159
- display: grid;
160
- place-items: center;
162
+ display: flex;
163
+ flex-direction: column;
164
+ justify-content: flex-end;
165
+
166
+ align-items: center;
167
+ gap: 12px;
168
+ .error {
169
+ color: #f95454;
170
+ text-align: center;
171
+ padding: 0 16px;
172
+ font-size: 14px;
173
+ font-style: italic;
174
+ font-weight: 500;
175
+ margin-bottom: 100px;
176
+ &.sm {
177
+ font-size: 12px;
178
+ margin-bottom: 16px;
179
+ }
180
+ }
161
181
  `;