ChandimaPrabath commited on
Commit
ded211a
1 Parent(s): 25dca33

movie player 0.1

Browse files
frontend/package-lock.json CHANGED
@@ -18,6 +18,7 @@
18
  "react": "^18",
19
  "react-dom": "^18",
20
  "sharp": "^0.33.5",
 
21
  "video.js": "^8.17.4"
22
  },
23
  "devDependencies": {
@@ -59,6 +60,24 @@
59
  "tslib": "^2.4.0"
60
  }
61
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
  "node_modules/@eslint-community/eslint-utils": {
63
  "version": "4.4.0",
64
  "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
@@ -865,6 +884,11 @@
865
  "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
866
  "dev": true
867
  },
 
 
 
 
 
868
  "node_modules/@typescript-eslint/parser": {
869
  "version": "7.2.0",
870
  "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.2.0.tgz",
@@ -1503,6 +1527,14 @@
1503
  "node": ">= 6"
1504
  }
1505
  },
 
 
 
 
 
 
 
 
1506
  "node_modules/caniuse-lite": {
1507
  "version": "1.0.30001651",
1508
  "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001651.tgz",
@@ -1656,6 +1688,24 @@
1656
  "node": ">= 8"
1657
  }
1658
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1659
  "node_modules/cssesc": {
1660
  "version": "3.0.0",
1661
  "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
@@ -1668,6 +1718,11 @@
1668
  "node": ">=4"
1669
  }
1670
  },
 
 
 
 
 
1671
  "node_modules/damerau-levenshtein": {
1672
  "version": "1.0.8",
1673
  "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz",
@@ -4389,8 +4444,7 @@
4389
  "node_modules/postcss-value-parser": {
4390
  "version": "4.2.0",
4391
  "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
4392
- "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
4393
- "dev": true
4394
  },
4395
  "node_modules/prelude-ls": {
4396
  "version": "1.2.1",
@@ -4737,6 +4791,11 @@
4737
  "node": ">= 0.4"
4738
  }
4739
  },
 
 
 
 
 
4740
  "node_modules/sharp": {
4741
  "version": "0.33.5",
4742
  "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.5.tgz",
@@ -5077,6 +5136,65 @@
5077
  "url": "https://github.com/sponsors/sindresorhus"
5078
  }
5079
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5080
  "node_modules/styled-jsx": {
5081
  "version": "5.1.1",
5082
  "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz",
@@ -5099,6 +5217,11 @@
5099
  }
5100
  }
5101
  },
 
 
 
 
 
5102
  "node_modules/sucrase": {
5103
  "version": "3.35.0",
5104
  "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz",
 
18
  "react": "^18",
19
  "react-dom": "^18",
20
  "sharp": "^0.33.5",
21
+ "styled-components": "^6.1.13",
22
  "video.js": "^8.17.4"
23
  },
24
  "devDependencies": {
 
60
  "tslib": "^2.4.0"
61
  }
62
  },
63
+ "node_modules/@emotion/is-prop-valid": {
64
+ "version": "1.2.2",
65
+ "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.2.tgz",
66
+ "integrity": "sha512-uNsoYd37AFmaCdXlg6EYD1KaPOaRWRByMCYzbKUX4+hhMfrxdVSelShywL4JVaAeM/eHUOSprYBQls+/neX3pw==",
67
+ "dependencies": {
68
+ "@emotion/memoize": "^0.8.1"
69
+ }
70
+ },
71
+ "node_modules/@emotion/memoize": {
72
+ "version": "0.8.1",
73
+ "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz",
74
+ "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA=="
75
+ },
76
+ "node_modules/@emotion/unitless": {
77
+ "version": "0.8.1",
78
+ "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz",
79
+ "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ=="
80
+ },
81
  "node_modules/@eslint-community/eslint-utils": {
82
  "version": "4.4.0",
83
  "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
 
884
  "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
885
  "dev": true
886
  },
887
+ "node_modules/@types/stylis": {
888
+ "version": "4.2.5",
889
+ "resolved": "https://registry.npmjs.org/@types/stylis/-/stylis-4.2.5.tgz",
890
+ "integrity": "sha512-1Xve+NMN7FWjY14vLoY5tL3BVEQ/n42YLwaqJIPYhotZ9uBHt87VceMwWQpzmdEt2TNXIorIFG+YeCUUW7RInw=="
891
+ },
892
  "node_modules/@typescript-eslint/parser": {
893
  "version": "7.2.0",
894
  "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.2.0.tgz",
 
1527
  "node": ">= 6"
1528
  }
1529
  },
1530
+ "node_modules/camelize": {
1531
+ "version": "1.0.1",
1532
+ "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz",
1533
+ "integrity": "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==",
1534
+ "funding": {
1535
+ "url": "https://github.com/sponsors/ljharb"
1536
+ }
1537
+ },
1538
  "node_modules/caniuse-lite": {
1539
  "version": "1.0.30001651",
1540
  "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001651.tgz",
 
1688
  "node": ">= 8"
1689
  }
1690
  },
1691
+ "node_modules/css-color-keywords": {
1692
+ "version": "1.0.0",
1693
+ "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz",
1694
+ "integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==",
1695
+ "engines": {
1696
+ "node": ">=4"
1697
+ }
1698
+ },
1699
+ "node_modules/css-to-react-native": {
1700
+ "version": "3.2.0",
1701
+ "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz",
1702
+ "integrity": "sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==",
1703
+ "dependencies": {
1704
+ "camelize": "^1.0.0",
1705
+ "css-color-keywords": "^1.0.0",
1706
+ "postcss-value-parser": "^4.0.2"
1707
+ }
1708
+ },
1709
  "node_modules/cssesc": {
1710
  "version": "3.0.0",
1711
  "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
 
1718
  "node": ">=4"
1719
  }
1720
  },
1721
+ "node_modules/csstype": {
1722
+ "version": "3.1.3",
1723
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
1724
+ "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
1725
+ },
1726
  "node_modules/damerau-levenshtein": {
1727
  "version": "1.0.8",
1728
  "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz",
 
4444
  "node_modules/postcss-value-parser": {
4445
  "version": "4.2.0",
4446
  "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
4447
+ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
 
4448
  },
4449
  "node_modules/prelude-ls": {
4450
  "version": "1.2.1",
 
4791
  "node": ">= 0.4"
4792
  }
4793
  },
4794
+ "node_modules/shallowequal": {
4795
+ "version": "1.1.0",
4796
+ "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz",
4797
+ "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ=="
4798
+ },
4799
  "node_modules/sharp": {
4800
  "version": "0.33.5",
4801
  "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.5.tgz",
 
5136
  "url": "https://github.com/sponsors/sindresorhus"
5137
  }
5138
  },
5139
+ "node_modules/styled-components": {
5140
+ "version": "6.1.13",
5141
+ "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.1.13.tgz",
5142
+ "integrity": "sha512-M0+N2xSnAtwcVAQeFEsGWFFxXDftHUD7XrKla06QbpUMmbmtFBMMTcKWvFXtWxuD5qQkB8iU5gk6QASlx2ZRMw==",
5143
+ "dependencies": {
5144
+ "@emotion/is-prop-valid": "1.2.2",
5145
+ "@emotion/unitless": "0.8.1",
5146
+ "@types/stylis": "4.2.5",
5147
+ "css-to-react-native": "3.2.0",
5148
+ "csstype": "3.1.3",
5149
+ "postcss": "8.4.38",
5150
+ "shallowequal": "1.1.0",
5151
+ "stylis": "4.3.2",
5152
+ "tslib": "2.6.2"
5153
+ },
5154
+ "engines": {
5155
+ "node": ">= 16"
5156
+ },
5157
+ "funding": {
5158
+ "type": "opencollective",
5159
+ "url": "https://opencollective.com/styled-components"
5160
+ },
5161
+ "peerDependencies": {
5162
+ "react": ">= 16.8.0",
5163
+ "react-dom": ">= 16.8.0"
5164
+ }
5165
+ },
5166
+ "node_modules/styled-components/node_modules/postcss": {
5167
+ "version": "8.4.38",
5168
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz",
5169
+ "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==",
5170
+ "funding": [
5171
+ {
5172
+ "type": "opencollective",
5173
+ "url": "https://opencollective.com/postcss/"
5174
+ },
5175
+ {
5176
+ "type": "tidelift",
5177
+ "url": "https://tidelift.com/funding/github/npm/postcss"
5178
+ },
5179
+ {
5180
+ "type": "github",
5181
+ "url": "https://github.com/sponsors/ai"
5182
+ }
5183
+ ],
5184
+ "dependencies": {
5185
+ "nanoid": "^3.3.7",
5186
+ "picocolors": "^1.0.0",
5187
+ "source-map-js": "^1.2.0"
5188
+ },
5189
+ "engines": {
5190
+ "node": "^10 || ^12 || >=14"
5191
+ }
5192
+ },
5193
+ "node_modules/styled-components/node_modules/tslib": {
5194
+ "version": "2.6.2",
5195
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
5196
+ "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
5197
+ },
5198
  "node_modules/styled-jsx": {
5199
  "version": "5.1.1",
5200
  "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz",
 
5217
  }
5218
  }
5219
  },
5220
+ "node_modules/stylis": {
5221
+ "version": "4.3.2",
5222
+ "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.2.tgz",
5223
+ "integrity": "sha512-bhtUjWd/z6ltJiQwg0dUfxEJ+W+jdqQd8TbWLWyeIJHlnsqmGLRFFd8e5mA0AZi/zx90smXRlN66YMTcaSFifg=="
5224
+ },
5225
  "node_modules/sucrase": {
5226
  "version": "3.35.0",
5227
  "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz",
frontend/package.json CHANGED
@@ -19,6 +19,7 @@
19
  "react": "^18",
20
  "react-dom": "^18",
21
  "sharp": "^0.33.5",
 
22
  "video.js": "^8.17.4"
23
  },
24
  "devDependencies": {
 
19
  "react": "^18",
20
  "react-dom": "^18",
21
  "sharp": "^0.33.5",
22
+ "styled-components": "^6.1.13",
23
  "video.js": "^8.17.4"
24
  },
25
  "devDependencies": {
frontend/src/app/tvshows/TvShows.css ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ .page-container{
2
+ width: 100dvw;
3
+ height: 100dvh;
4
+ }
frontend/src/app/tvshows/page.js CHANGED
@@ -1,10 +1,13 @@
 
 
 
1
  export default function TVShows() {
2
  return (
3
  <>
4
- <div>
5
  <h1>TV Shows</h1>
6
  <p className='text-white'>Browse our TV show collection.</p>
7
- {/* Add TV show list here */}
8
  </div>
9
  </>
10
  );
 
1
+ import { Spinner } from "@/components/Spinner";
2
+ import './TvShows.css';
3
+
4
  export default function TVShows() {
5
  return (
6
  <>
7
+ <div className="page-container">
8
  <h1>TV Shows</h1>
9
  <p className='text-white'>Browse our TV show collection.</p>
10
+ <Spinner/>
11
  </div>
12
  </>
13
  );
frontend/src/components/MoviePlayer.css CHANGED
@@ -11,7 +11,7 @@
11
  }
12
 
13
  .video-title {
14
- color: #fff;
15
  text-align: left;
16
  padding: 20px;
17
  padding-bottom: 50px;
@@ -49,22 +49,35 @@
49
  opacity: 1;
50
  }
51
 
52
- .player-controls-down{
53
  display: flex;
54
  flex-direction: row;
55
- width: 100dvw;
56
  justify-content: center;
57
  align-items: center;
58
  }
59
 
60
- .player-controls-top{
61
  display: flex;
62
- flex-direction: column;
63
- width: 100dvw;
64
- justify-content: center;
 
65
  align-items: center;
66
  }
67
 
 
 
 
 
 
 
 
 
 
 
 
 
68
  .controls {
69
  display: flex;
70
  justify-content: center;
@@ -73,14 +86,16 @@
73
  background-color: rgba(0, 0, 0, 0.7);
74
  bottom: 0;
75
  position: relative;
76
- width: 100dvw;
77
  flex-direction: column;
78
  }
79
 
 
 
 
 
80
  .play-pause-btn,
81
  .control-btn {
82
- color: #fff;
83
- border: 1px solid #5f59d1;
84
  border-radius: 10px;
85
  padding: 10px 15px;
86
  margin: 0 5px;
@@ -91,11 +106,11 @@
91
 
92
  .play-pause-btn:hover,
93
  .control-btn:hover {
94
- background-color: #5f6293;
95
  }
96
 
97
  .control-btn:disabled {
98
- background-color: #232435;
99
  cursor: not-allowed;
100
  }
101
 
@@ -113,3 +128,12 @@
113
  width: 100px;
114
  cursor: pointer;
115
  }
 
 
 
 
 
 
 
 
 
 
11
  }
12
 
13
  .video-title {
14
+ color: #8e91b4;
15
  text-align: left;
16
  padding: 20px;
17
  padding-bottom: 50px;
 
49
  opacity: 1;
50
  }
51
 
52
+ .player-controls-top {
53
  display: flex;
54
  flex-direction: row;
55
+ width: 95dvw;
56
  justify-content: center;
57
  align-items: center;
58
  }
59
 
60
+ .player-controls-down {
61
  display: flex;
62
+ flex-direction: row;
63
+ width: 90dvw;
64
+
65
+ justify-content: space-between;
66
  align-items: center;
67
  }
68
 
69
+ .player-controls-left {
70
+ width: 100%;
71
+ display: flex;
72
+ justify-content: left;
73
+ }
74
+
75
+ .player-controls-right {
76
+ width: 100%;
77
+ display: flex;
78
+ justify-content: right;
79
+ }
80
+
81
  .controls {
82
  display: flex;
83
  justify-content: center;
 
86
  background-color: rgba(0, 0, 0, 0.7);
87
  bottom: 0;
88
  position: relative;
 
89
  flex-direction: column;
90
  }
91
 
92
+ .current-time, .duration{
93
+ color: white;
94
+ }
95
+
96
  .play-pause-btn,
97
  .control-btn {
98
+ color: #6c66b0;
 
99
  border-radius: 10px;
100
  padding: 10px 15px;
101
  margin: 0 5px;
 
106
 
107
  .play-pause-btn:hover,
108
  .control-btn:hover {
109
+ color: #5f6293;
110
  }
111
 
112
  .control-btn:disabled {
113
+ color: #232435;
114
  cursor: not-allowed;
115
  }
116
 
 
128
  width: 100px;
129
  cursor: pointer;
130
  }
131
+
132
+ .buffering-indicator{
133
+ color: white;
134
+ position: fixed;
135
+ top: 50%;
136
+ left: 50%;
137
+ transform: translate(-50%,-50%);
138
+ opacity: 1;
139
+ }
frontend/src/components/MoviePlayer.js CHANGED
@@ -11,6 +11,7 @@ import {
11
  faVolumeMute,
12
  faExpand,
13
  } from "@fortawesome/free-solid-svg-icons";
 
14
 
15
  export default function MoviePlayer({ videoUrl, title }) {
16
  const videoRef = useRef(null);
@@ -20,6 +21,7 @@ export default function MoviePlayer({ videoUrl, title }) {
20
  const [progress, setProgress] = useState(0);
21
  const [isFullscreen, setIsFullscreen] = useState(false);
22
  const [showControls, setShowControls] = useState(true);
 
23
  const overlayTimeout = useRef(null);
24
 
25
  useEffect(() => {
@@ -29,15 +31,21 @@ export default function MoviePlayer({ videoUrl, title }) {
29
  const handlePause = () => setIsPlaying(false);
30
  const handleTimeUpdate = () =>
31
  setProgress((videoElement.currentTime / videoElement.duration) * 100);
 
 
32
 
33
  videoElement.addEventListener("play", handlePlay);
34
  videoElement.addEventListener("pause", handlePause);
35
  videoElement.addEventListener("timeupdate", handleTimeUpdate);
 
 
36
 
37
  return () => {
38
  videoElement.removeEventListener("play", handlePlay);
39
  videoElement.removeEventListener("pause", handlePause);
40
  videoElement.removeEventListener("timeupdate", handleTimeUpdate);
 
 
41
  };
42
  }, []);
43
 
@@ -48,6 +56,8 @@ export default function MoviePlayer({ videoUrl, title }) {
48
  }
49
  overlayTimeout.current = setTimeout(() => setShowControls(false), 3000);
50
  }
 
 
51
  }, [showControls]);
52
 
53
  const handleFastForward = () => {
@@ -71,8 +81,10 @@ export default function MoviePlayer({ videoUrl, title }) {
71
  const togglePlayPause = () => {
72
  if (isPlaying) {
73
  videoRef.current.pause();
 
74
  } else {
75
  videoRef.current.play();
 
76
  }
77
  };
78
 
@@ -105,7 +117,7 @@ export default function MoviePlayer({ videoUrl, title }) {
105
  const exitFullscreen =
106
  doc.exitFullscreen ||
107
  doc.mozCancelFullScreen ||
108
- doc.webkitExitFullscreen ||
109
  doc.msExitFullscreen;
110
 
111
  if (!isFullscreen) {
@@ -128,64 +140,84 @@ export default function MoviePlayer({ videoUrl, title }) {
128
  setShowControls(true);
129
  };
130
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
131
  return (
132
  <div className="video-player-container" onMouseMove={handleMouseMove}>
133
  <div className={`player-overlay ${showControls ? "show" : "hide"}`}>
134
  <h2 className="video-title">{title}</h2>
135
  <div className="controls">
136
  <div className="player-controls-top">
137
- {" "}
138
  <input
139
  type="range"
140
  className="progress-bar"
141
  value={progress}
142
  onChange={handleProgressChange}
143
  />
 
144
  </div>
145
  <div className="player-controls-down">
146
- <button onClick={togglePlayPause} className="play-pause-btn">
147
- {isPlaying ? (
148
- <FontAwesomeIcon icon={faPause} size="xl" />
149
- ) : (
150
- <FontAwesomeIcon icon={faPlay} size="xl" />
151
- )}
152
- </button>
153
- <button
154
- onClick={handleRewind}
155
- className="control-btn"
156
- disabled={!isPlaying}
157
- >
158
- <FontAwesomeIcon icon={faFastBackward} size="xl" />
159
- </button>
160
- <button
161
- onClick={handleFastForward}
162
- className="control-btn"
163
- disabled={!isPlaying}
164
- >
165
- <FontAwesomeIcon icon={faFastForward} size="xl" />
166
- </button>
167
- <button onClick={toggleMute} className="control-btn">
168
- <FontAwesomeIcon
169
- icon={isMuted ? faVolumeMute : faVolumeUp}
170
- size="xl"
 
 
 
 
 
 
 
 
 
 
 
 
 
171
  />
172
- </button>
173
- <input
174
- type="range"
175
- className="volume-control"
176
- min="0"
177
- max="1"
178
- step="0.01"
179
- value={volume}
180
- onChange={handleVolumeChange}
181
- />
182
- <button onClick={toggleFullscreen} className="control-btn">
183
- <FontAwesomeIcon icon={faExpand} size="xl" />
184
- </button>
185
  </div>
186
  </div>
 
187
  </div>
188
-
189
  <video
190
  ref={videoRef}
191
  className="video-element"
 
11
  faVolumeMute,
12
  faExpand,
13
  } from "@fortawesome/free-solid-svg-icons";
14
+ import { Spinner } from "@/components/Spinner";
15
 
16
  export default function MoviePlayer({ videoUrl, title }) {
17
  const videoRef = useRef(null);
 
21
  const [progress, setProgress] = useState(0);
22
  const [isFullscreen, setIsFullscreen] = useState(false);
23
  const [showControls, setShowControls] = useState(true);
24
+ const [isBuffering, setIsBuffering] = useState(false);
25
  const overlayTimeout = useRef(null);
26
 
27
  useEffect(() => {
 
31
  const handlePause = () => setIsPlaying(false);
32
  const handleTimeUpdate = () =>
33
  setProgress((videoElement.currentTime / videoElement.duration) * 100);
34
+ const handleWaiting = () => setIsBuffering(true);
35
+ const handlePlaying = () => setIsBuffering(false);
36
 
37
  videoElement.addEventListener("play", handlePlay);
38
  videoElement.addEventListener("pause", handlePause);
39
  videoElement.addEventListener("timeupdate", handleTimeUpdate);
40
+ videoElement.addEventListener("waiting", handleWaiting);
41
+ videoElement.addEventListener("playing", handlePlaying);
42
 
43
  return () => {
44
  videoElement.removeEventListener("play", handlePlay);
45
  videoElement.removeEventListener("pause", handlePause);
46
  videoElement.removeEventListener("timeupdate", handleTimeUpdate);
47
+ videoElement.removeEventListener("waiting", handleWaiting);
48
+ videoElement.removeEventListener("playing", handlePlaying);
49
  };
50
  }, []);
51
 
 
56
  }
57
  overlayTimeout.current = setTimeout(() => setShowControls(false), 3000);
58
  }
59
+
60
+ return () => clearTimeout(overlayTimeout.current);
61
  }, [showControls]);
62
 
63
  const handleFastForward = () => {
 
81
  const togglePlayPause = () => {
82
  if (isPlaying) {
83
  videoRef.current.pause();
84
+ setShowControls(true);
85
  } else {
86
  videoRef.current.play();
87
+ setShowControls(false);
88
  }
89
  };
90
 
 
117
  const exitFullscreen =
118
  doc.exitFullscreen ||
119
  doc.mozCancelFullScreen ||
120
+ docEl.webkitExitFullscreen ||
121
  doc.msExitFullscreen;
122
 
123
  if (!isFullscreen) {
 
140
  setShowControls(true);
141
  };
142
 
143
+ const formatTime = (seconds) => {
144
+ if (isNaN(seconds)) {
145
+ return '00:00:00';
146
+ }
147
+ const wholeSeconds = Math.floor(seconds);
148
+ const hours = Math.floor(wholeSeconds / 3600);
149
+ const minutes = Math.floor((wholeSeconds % 3600) / 60);
150
+ const secs = wholeSeconds % 60;
151
+
152
+ const formattedHours = String(hours).padStart(2, '0');
153
+ const formattedMinutes = String(minutes).padStart(2, '0');
154
+ const formattedSeconds = String(secs).padStart(2, '0');
155
+ return `${formattedHours}:${formattedMinutes}:${formattedSeconds}`;
156
+ };
157
+
158
  return (
159
  <div className="video-player-container" onMouseMove={handleMouseMove}>
160
  <div className={`player-overlay ${showControls ? "show" : "hide"}`}>
161
  <h2 className="video-title">{title}</h2>
162
  <div className="controls">
163
  <div className="player-controls-top">
164
+ <label className="current-time">{formatTime(videoRef?.current?.currentTime)}</label>
165
  <input
166
  type="range"
167
  className="progress-bar"
168
  value={progress}
169
  onChange={handleProgressChange}
170
  />
171
+ <label className="duration">{formatTime(videoRef?.current?.duration)}</label>
172
  </div>
173
  <div className="player-controls-down">
174
+ <div className="player-controls-left">
175
+ <button
176
+ onClick={handleRewind}
177
+ className="control-btn"
178
+ disabled={!isPlaying}
179
+ >
180
+ <FontAwesomeIcon icon={faFastBackward} size="xl" />
181
+ </button>
182
+ <button onClick={togglePlayPause} className="play-pause-btn">
183
+ {isPlaying ? (
184
+ <FontAwesomeIcon icon={faPause} size="xl" />
185
+ ) : (
186
+ <FontAwesomeIcon icon={faPlay} size="xl" />
187
+ )}
188
+ </button>
189
+ <button
190
+ onClick={handleFastForward}
191
+ className="control-btn"
192
+ disabled={!isPlaying}
193
+ >
194
+ <FontAwesomeIcon icon={faFastForward} size="xl" />
195
+ </button>
196
+ </div>
197
+ <div className="player-controls-right">
198
+ <button onClick={toggleMute} className="control-btn">
199
+ <FontAwesomeIcon
200
+ icon={isMuted ? faVolumeMute : faVolumeUp}
201
+ size="xl"
202
+ />
203
+ </button>
204
+ <input
205
+ type="range"
206
+ className="volume-control"
207
+ min="0"
208
+ max="1"
209
+ step="0.01"
210
+ value={volume}
211
+ onChange={handleVolumeChange}
212
  />
213
+ <button onClick={toggleFullscreen} className="control-btn">
214
+ <FontAwesomeIcon icon={faExpand} size="xl" />
215
+ </button>
216
+ </div>
 
 
 
 
 
 
 
 
 
217
  </div>
218
  </div>
219
+ {isBuffering && <div className="buffering-indicator"><Spinner/></div>}
220
  </div>
 
221
  <video
222
  ref={videoRef}
223
  className="video-element"
frontend/src/components/Spinner.css ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .spinner-wrapper {
2
+ height: 5rem;
3
+ width: 5rem;
4
+ position: relative;
5
+ animation: spinner 2.5s infinite linear both;
6
+ }
7
+
8
+ @keyframes spinner {
9
+ 100% {
10
+ transform: rotate(-360deg);
11
+ }
12
+ }
13
+
14
+ .spinner-dot {
15
+ width: 100%;
16
+ height: 100%;
17
+ position: absolute;
18
+ left: 0;
19
+ top: 0;
20
+ animation: spinner-dot 1.5s infinite ease-in-out both;
21
+ border-radius: 100%;
22
+ }
23
+
24
+ .spinner-dot:before {
25
+ content: "";
26
+ display: block;
27
+ width: 25%;
28
+ height: 25%;
29
+ background-color: rgb(93, 101, 171); /* Use a CSS variable for the color */
30
+ border-radius: 100%;
31
+ animation: spinner-dot-before 1.5s infinite ease-in-out both;
32
+ }
33
+ .spinner-dot:nth-child(1) {
34
+ animation-delay: -1s; /* Adjusted for smooth start */
35
+ }
36
+ .spinner-dot:nth-child(2) {
37
+ animation-delay: -.9s; /* Slightly less delay */
38
+ }
39
+ .spinner-dot:nth-child(3) {
40
+ animation-delay: -.7s; /* Medium delay */
41
+ }
42
+ .spinner-dot:nth-child(4) {
43
+ animation-delay: -0.6s; /* Shorter delay */
44
+ }
45
+ .spinner-dot:nth-child(5) {
46
+ animation-delay: -0.5s; /* Even shorter delay */
47
+ }
48
+ .spinner-dot:nth-child(6) {
49
+ animation-delay: -0.3s; /* Quickest delay */
50
+ }
51
+
52
+ @keyframes spinner-dot {
53
+ 80%,
54
+ 100% {
55
+ transform: rotate(360deg);
56
+ }
57
+ }
58
+
59
+ @keyframes spinner-dot-before {
60
+ 50% {
61
+ transform: scale(0.2);
62
+ }
63
+ 100%,
64
+ 0% {
65
+ transform: scale(.6);
66
+ }
67
+ }
frontend/src/components/Spinner.js ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Spinner.js
2
+ import React from "react";
3
+ import "./Spinner.css"; // Import the CSS file
4
+
5
+ export const Spinner = () => {
6
+ return (
7
+ <div className="spinner-wrapper">
8
+ <div className="spinner-dot" />
9
+ <div className="spinner-dot" />
10
+ <div className="spinner-dot" />
11
+ <div className="spinner-dot" />
12
+ <div className="spinner-dot" />
13
+ <div className="spinner-dot" />
14
+ </div>
15
+ );
16
+ };