ChandimaPrabath
commited on
Commit
•
25dca33
1
Parent(s):
d0c3311
player test 0.1
Browse files
frontend/src/app/layout.js
CHANGED
@@ -1,3 +1,4 @@
|
|
|
|
1 |
import { Inter } from "next/font/google";
|
2 |
import "./globals.css";
|
3 |
import Sidebar from "@/components/Sidebar";
|
@@ -5,20 +6,27 @@ import { FilmProvider } from "@/context/FilmContext";
|
|
5 |
import { config } from "@fortawesome/fontawesome-svg-core";
|
6 |
import "@fortawesome/fontawesome-svg-core/styles.css";
|
7 |
import Header from "@/components/Header";
|
|
|
|
|
8 |
config.autoAddCss = false;
|
9 |
const inter = Inter({ subsets: ["latin"] });
|
10 |
|
11 |
export default function RootLayout({ children }) {
|
|
|
|
|
|
|
12 |
return (
|
13 |
<html lang="en">
|
14 |
<body className={inter.className}>
|
15 |
<div className="app-container">
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
|
|
|
|
22 |
</div>
|
23 |
</body>
|
24 |
</html>
|
|
|
1 |
+
'use client';
|
2 |
import { Inter } from "next/font/google";
|
3 |
import "./globals.css";
|
4 |
import Sidebar from "@/components/Sidebar";
|
|
|
6 |
import { config } from "@fortawesome/fontawesome-svg-core";
|
7 |
import "@fortawesome/fontawesome-svg-core/styles.css";
|
8 |
import Header from "@/components/Header";
|
9 |
+
import { usePathname } from "next/navigation";
|
10 |
+
|
11 |
config.autoAddCss = false;
|
12 |
const inter = Inter({ subsets: ["latin"] });
|
13 |
|
14 |
export default function RootLayout({ children }) {
|
15 |
+
const pathname = usePathname();
|
16 |
+
const isPlayerPage = pathname.startsWith('/player/movie') || pathname.startsWith('/player/tvshow');
|
17 |
+
|
18 |
return (
|
19 |
<html lang="en">
|
20 |
<body className={inter.className}>
|
21 |
<div className="app-container">
|
22 |
+
{!isPlayerPage && (
|
23 |
+
<header>
|
24 |
+
<link rel="icon" href="/favicon.ico" sizes="any" />
|
25 |
+
<Sidebar />
|
26 |
+
<Header />
|
27 |
+
</header>
|
28 |
+
)}
|
29 |
+
<FilmProvider>{children}</FilmProvider>
|
30 |
</div>
|
31 |
</body>
|
32 |
</html>
|
frontend/src/app/player/movie/[title]/MoviePlayerPage.css
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.film-player-container{
|
2 |
+
width: 100dvw;
|
3 |
+
height: 100dvh;
|
4 |
+
}
|
5 |
+
|
6 |
+
.app-container{
|
7 |
+
padding: 0;
|
8 |
+
margin: 0;
|
9 |
+
}
|
frontend/src/app/player/movie/[title]/page.js
CHANGED
@@ -1,7 +1,8 @@
|
|
1 |
"use client";
|
2 |
import apiClient from "@/api/apiClient";
|
3 |
import { useEffect, useState } from "react";
|
4 |
-
import MoviePlayer from "@/components/
|
|
|
5 |
|
6 |
export default function FilmPlayer({ params }) {
|
7 |
const title = decodeURIComponent(params.title);
|
@@ -89,7 +90,7 @@ export default function FilmPlayer({ params }) {
|
|
89 |
<div className="film-player-container">
|
90 |
{loading && <p className="text-white">Loading...</p>}
|
91 |
{error && <p className="text-red-500">{error}</p>}
|
92 |
-
{metadata && !error && (
|
93 |
<div className="metadata">
|
94 |
<p className="text-white">
|
95 |
{metadata.title} ({metadata.year})
|
@@ -105,7 +106,7 @@ export default function FilmPlayer({ params }) {
|
|
105 |
/>
|
106 |
)}
|
107 |
</div>
|
108 |
-
)}
|
109 |
{downloadProgressUrl && downloadProgress ? (
|
110 |
<div className="download-status">
|
111 |
<p className="text-yellow-500">
|
|
|
1 |
"use client";
|
2 |
import apiClient from "@/api/apiClient";
|
3 |
import { useEffect, useState } from "react";
|
4 |
+
import MoviePlayer from "@/components/MoviePlayer";
|
5 |
+
import './MoviePlayerPage.css';
|
6 |
|
7 |
export default function FilmPlayer({ params }) {
|
8 |
const title = decodeURIComponent(params.title);
|
|
|
90 |
<div className="film-player-container">
|
91 |
{loading && <p className="text-white">Loading...</p>}
|
92 |
{error && <p className="text-red-500">{error}</p>}
|
93 |
+
{/* {metadata && !error && (
|
94 |
<div className="metadata">
|
95 |
<p className="text-white">
|
96 |
{metadata.title} ({metadata.year})
|
|
|
106 |
/>
|
107 |
)}
|
108 |
</div>
|
109 |
+
)} */}
|
110 |
{downloadProgressUrl && downloadProgress ? (
|
111 |
<div className="download-status">
|
112 |
<p className="text-yellow-500">
|
frontend/src/components/MoviePlayer.css
CHANGED
@@ -1,37 +1,115 @@
|
|
1 |
.video-player-container {
|
2 |
-
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
.video-player-container {
|
2 |
+
position: relative;
|
3 |
+
max-width: 100dvw;
|
4 |
+
height: 100%;
|
5 |
+
margin: auto;
|
6 |
+
background-color: #000;
|
7 |
+
border-radius: 8px;
|
8 |
+
overflow: hidden;
|
9 |
+
box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
|
10 |
+
display: flex;
|
11 |
+
}
|
12 |
+
|
13 |
+
.video-title {
|
14 |
+
color: #fff;
|
15 |
+
text-align: left;
|
16 |
+
padding: 20px;
|
17 |
+
padding-bottom: 50px;
|
18 |
+
font-size: 1.5em;
|
19 |
+
font-weight: bold;
|
20 |
+
background-image: linear-gradient(#0e0f19 60%, transparent 100%);
|
21 |
+
}
|
22 |
+
|
23 |
+
.video-element {
|
24 |
+
width: 100%;
|
25 |
+
height: auto;
|
26 |
+
display: block;
|
27 |
+
}
|
28 |
+
|
29 |
+
.player-overlay {
|
30 |
+
position: absolute;
|
31 |
+
top: 0;
|
32 |
+
left: 0;
|
33 |
+
width: 100%;
|
34 |
+
height: 100%;
|
35 |
+
display: flex;
|
36 |
+
flex-direction: column;
|
37 |
+
justify-content: space-between;
|
38 |
+
background-color: rgba(0, 0, 0, 0.5);
|
39 |
+
transition: opacity 0.5s;
|
40 |
+
z-index: 100;
|
41 |
+
}
|
42 |
+
|
43 |
+
.player-overlay.hide {
|
44 |
+
opacity: 0;
|
45 |
+
pointer-events: none;
|
46 |
+
}
|
47 |
+
|
48 |
+
.player-overlay.show {
|
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;
|
71 |
+
align-items: center;
|
72 |
+
padding: 10px;
|
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;
|
87 |
+
cursor: pointer;
|
88 |
+
font-size: 1em;
|
89 |
+
transition: background-color 0.3s;
|
90 |
+
}
|
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 |
+
|
102 |
+
.progress-bar,
|
103 |
+
.volume-control {
|
104 |
+
margin: 0 5px;
|
105 |
+
}
|
106 |
+
|
107 |
+
.progress-bar {
|
108 |
+
width: 85%;
|
109 |
+
cursor: pointer;
|
110 |
+
}
|
111 |
+
|
112 |
+
.volume-control {
|
113 |
+
width: 100px;
|
114 |
+
cursor: pointer;
|
115 |
+
}
|
frontend/src/components/MoviePlayer.js
CHANGED
@@ -1,35 +1,70 @@
|
|
1 |
"use client";
|
2 |
import { useEffect, useRef, useState } from "react";
|
3 |
-
import
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4 |
|
5 |
export default function MoviePlayer({ videoUrl, title }) {
|
6 |
const videoRef = useRef(null);
|
7 |
const [isPlaying, setIsPlaying] = useState(false);
|
|
|
|
|
|
|
|
|
|
|
|
|
8 |
|
9 |
useEffect(() => {
|
10 |
const videoElement = videoRef.current;
|
11 |
|
12 |
const handlePlay = () => setIsPlaying(true);
|
13 |
const handlePause = () => setIsPlaying(false);
|
|
|
|
|
14 |
|
15 |
videoElement.addEventListener("play", handlePlay);
|
16 |
videoElement.addEventListener("pause", handlePause);
|
|
|
17 |
|
18 |
return () => {
|
19 |
videoElement.removeEventListener("play", handlePlay);
|
20 |
videoElement.removeEventListener("pause", handlePause);
|
|
|
21 |
};
|
22 |
}, []);
|
23 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
24 |
const handleFastForward = () => {
|
25 |
if (videoRef.current) {
|
26 |
-
videoRef.current.currentTime = Math.min(
|
|
|
|
|
|
|
27 |
}
|
28 |
};
|
29 |
|
30 |
const handleRewind = () => {
|
31 |
if (videoRef.current) {
|
32 |
-
videoRef.current.currentTime = Math.max(
|
|
|
|
|
|
|
33 |
}
|
34 |
};
|
35 |
|
@@ -41,9 +76,116 @@ export default function MoviePlayer({ videoUrl, title }) {
|
|
41 |
}
|
42 |
};
|
43 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
44 |
return (
|
45 |
-
<div className="video-player-container">
|
46 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
47 |
<video
|
48 |
ref={videoRef}
|
49 |
className="video-element"
|
@@ -54,18 +196,11 @@ export default function MoviePlayer({ videoUrl, title }) {
|
|
54 |
<track
|
55 |
kind="subtitles"
|
56 |
label="English"
|
57 |
-
|
58 |
src="path/to/your/subtitles.vtt" // Replace with actual subtitles URL
|
59 |
/>
|
60 |
Your browser does not support the video tag.
|
61 |
</video>
|
62 |
-
<div className="controls">
|
63 |
-
<button onClick={togglePlayPause} className="play-pause-btn">
|
64 |
-
{isPlaying ? '❚❚ Pause' : '► Play'}
|
65 |
-
</button>
|
66 |
-
<button onClick={handleRewind} className="control-btn" disabled={!isPlaying}>⏪ 10s</button>
|
67 |
-
<button onClick={handleFastForward} className="control-btn" disabled={!isPlaying}>⏩ 10s</button>
|
68 |
-
</div>
|
69 |
</div>
|
70 |
);
|
71 |
}
|
|
|
1 |
"use client";
|
2 |
import { useEffect, useRef, useState } from "react";
|
3 |
+
import "./MoviePlayer.css";
|
4 |
+
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
5 |
+
import {
|
6 |
+
faFastBackward,
|
7 |
+
faFastForward,
|
8 |
+
faPause,
|
9 |
+
faPlay,
|
10 |
+
faVolumeUp,
|
11 |
+
faVolumeMute,
|
12 |
+
faExpand,
|
13 |
+
} from "@fortawesome/free-solid-svg-icons";
|
14 |
|
15 |
export default function MoviePlayer({ videoUrl, title }) {
|
16 |
const videoRef = useRef(null);
|
17 |
const [isPlaying, setIsPlaying] = useState(false);
|
18 |
+
const [volume, setVolume] = useState(1);
|
19 |
+
const [isMuted, setIsMuted] = useState(false);
|
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(() => {
|
26 |
const videoElement = videoRef.current;
|
27 |
|
28 |
const handlePlay = () => setIsPlaying(true);
|
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 |
|
44 |
+
useEffect(() => {
|
45 |
+
if (showControls) {
|
46 |
+
if (overlayTimeout.current) {
|
47 |
+
clearTimeout(overlayTimeout.current);
|
48 |
+
}
|
49 |
+
overlayTimeout.current = setTimeout(() => setShowControls(false), 3000);
|
50 |
+
}
|
51 |
+
}, [showControls]);
|
52 |
+
|
53 |
const handleFastForward = () => {
|
54 |
if (videoRef.current) {
|
55 |
+
videoRef.current.currentTime = Math.min(
|
56 |
+
videoRef.current.duration,
|
57 |
+
videoRef.current.currentTime + 10
|
58 |
+
);
|
59 |
}
|
60 |
};
|
61 |
|
62 |
const handleRewind = () => {
|
63 |
if (videoRef.current) {
|
64 |
+
videoRef.current.currentTime = Math.max(
|
65 |
+
0,
|
66 |
+
videoRef.current.currentTime - 10
|
67 |
+
);
|
68 |
}
|
69 |
};
|
70 |
|
|
|
76 |
}
|
77 |
};
|
78 |
|
79 |
+
const handleVolumeChange = (event) => {
|
80 |
+
const volumeValue = event.target.value;
|
81 |
+
setVolume(volumeValue);
|
82 |
+
videoRef.current.volume = volumeValue;
|
83 |
+
setIsMuted(volumeValue === 0);
|
84 |
+
};
|
85 |
+
|
86 |
+
const toggleMute = () => {
|
87 |
+
if (isMuted) {
|
88 |
+
videoRef.current.volume = volume;
|
89 |
+
setIsMuted(false);
|
90 |
+
} else {
|
91 |
+
videoRef.current.volume = 0;
|
92 |
+
setIsMuted(true);
|
93 |
+
}
|
94 |
+
};
|
95 |
+
|
96 |
+
const toggleFullscreen = () => {
|
97 |
+
const doc = window.document;
|
98 |
+
const docEl = doc.documentElement;
|
99 |
+
|
100 |
+
const requestFullscreen =
|
101 |
+
docEl.requestFullscreen ||
|
102 |
+
docEl.mozRequestFullScreen ||
|
103 |
+
docEl.webkitRequestFullscreen ||
|
104 |
+
docEl.msRequestFullscreen;
|
105 |
+
const exitFullscreen =
|
106 |
+
doc.exitFullscreen ||
|
107 |
+
doc.mozCancelFullScreen ||
|
108 |
+
doc.webkitExitFullscreen ||
|
109 |
+
doc.msExitFullscreen;
|
110 |
+
|
111 |
+
if (!isFullscreen) {
|
112 |
+
requestFullscreen.call(docEl);
|
113 |
+
} else {
|
114 |
+
exitFullscreen.call(doc);
|
115 |
+
}
|
116 |
+
|
117 |
+
setIsFullscreen(!isFullscreen);
|
118 |
+
};
|
119 |
+
|
120 |
+
const handleProgressChange = (event) => {
|
121 |
+
const progressValue = event.target.value;
|
122 |
+
videoRef.current.currentTime =
|
123 |
+
(progressValue / 100) * videoRef.current.duration;
|
124 |
+
setProgress(progressValue);
|
125 |
+
};
|
126 |
+
|
127 |
+
const handleMouseMove = () => {
|
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"
|
|
|
196 |
<track
|
197 |
kind="subtitles"
|
198 |
label="English"
|
199 |
+
srcLang="en"
|
200 |
src="path/to/your/subtitles.vtt" // Replace with actual subtitles URL
|
201 |
/>
|
202 |
Your browser does not support the video tag.
|
203 |
</video>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
204 |
</div>
|
205 |
);
|
206 |
}
|