Skip to content

Commit ffc0d48

Browse files
Merge pull request AOSSIE-Org#1339 from MayankSharma-ops/fix/videoplayer-bugs
Fix/videoplayer bugs
1 parent 1a422f1 commit ffc0d48

3 files changed

Lines changed: 453 additions & 14 deletions

File tree

frontend/src-tauri/tauri.conf.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@
6666
],
6767
"enable": true
6868
},
69-
"csp": "default-src 'self'; img-src 'self' data: asset: http://asset.localhost; media-src 'self' blob: data:; connect-src 'self' ipc: http://ipc.localhost http://localhost:52123 ws://localhost:52123 http://localhost:52124 ws://localhost:52124"
69+
"csp": "default-src 'self'; img-src 'self' data: asset: http://asset.localhost; media-src 'self' blob: data: asset: http://asset.localhost; connect-src 'self' ipc: http://ipc.localhost http://localhost:52123 ws://localhost:52123 http://localhost:52124 ws://localhost:52124"
7070
}
7171
}
7272
}

frontend/src/components/VideoPlayer/NetflixStylePlayer.tsx

Lines changed: 112 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type React from 'react';
2-
import { useState, useRef, useEffect } from 'react';
2+
import { useState, useRef, useEffect, useMemo } from 'react';
33
import {
44
Play,
55
Pause,
@@ -11,6 +11,7 @@ import {
1111
VolumeX,
1212
} from 'lucide-react';
1313
import { Slider } from '../../components/ui/Slider';
14+
import { convertFileSrc } from '@tauri-apps/api/core';
1415

1516
interface NetflixStylePlayerProps {
1617
videoSrc: string;
@@ -27,9 +28,13 @@ export default function NetflixStylePlayer({
2728
const [isMuted, setIsMuted] = useState(false);
2829
const [showControls, setShowControls] = useState(false);
2930
const [isFullscreen, setIsFullscreen] = useState(false);
31+
const [currentTime, setCurrentTime] = useState(0);
32+
const [duration, setDuration] = useState(0);
3033
const videoRef = useRef<HTMLVideoElement>(null);
3134
const containerRef = useRef<HTMLDivElement>(null);
3235

36+
const resolvedSrc = useMemo(() => convertFileSrc(videoSrc), [videoSrc]);
37+
3338
useEffect(() => {
3439
let timeout: NodeJS.Timeout;
3540
const showControlsTemporarily = () => {
@@ -44,16 +49,77 @@ export default function NetflixStylePlayer({
4449
container.addEventListener('mouseenter', showControlsTemporarily);
4550
}
4651

52+
const handleKeyDown = (e: KeyboardEvent) => {
53+
const activeEl = document.activeElement;
54+
if (
55+
activeEl &&
56+
(activeEl.tagName === 'INPUT' ||
57+
activeEl.tagName === 'TEXTAREA' ||
58+
activeEl.getAttribute('contenteditable') === 'true')
59+
) {
60+
return;
61+
}
62+
63+
switch (e.key) {
64+
case ' ':
65+
e.preventDefault();
66+
togglePlay();
67+
showControlsTemporarily();
68+
break;
69+
case 'ArrowLeft':
70+
e.preventDefault();
71+
skipTime(-10);
72+
showControlsTemporarily();
73+
break;
74+
case 'ArrowRight':
75+
e.preventDefault();
76+
skipTime(10);
77+
showControlsTemporarily();
78+
break;
79+
case 'm':
80+
case 'M':
81+
e.preventDefault();
82+
toggleMute();
83+
showControlsTemporarily();
84+
break;
85+
case 'f':
86+
case 'F':
87+
e.preventDefault();
88+
toggleFullScreen();
89+
showControlsTemporarily();
90+
break;
91+
default:
92+
break;
93+
}
94+
};
95+
96+
document.addEventListener('keydown', handleKeyDown);
97+
4798
return () => {
4899
if (container) {
49100
container.removeEventListener('mousemove', showControlsTemporarily);
50101
container.removeEventListener('mouseenter', showControlsTemporarily);
51102
}
103+
document.removeEventListener('keydown', handleKeyDown);
52104
clearTimeout(timeout);
53105
};
54106
}, []);
55107

108+
useEffect(() => {
109+
const handleFullscreenChange = () => {
110+
setIsFullscreen(document.fullscreenElement === containerRef.current);
111+
};
112+
113+
document.addEventListener('fullscreenchange', handleFullscreenChange);
114+
return () => {
115+
document.removeEventListener('fullscreenchange', handleFullscreenChange);
116+
};
117+
}, []);
118+
56119
const formatTime = (timeInSeconds: number) => {
120+
if (!Number.isFinite(timeInSeconds)) {
121+
return '0:00';
122+
}
57123
const hours = Math.floor(timeInSeconds / 3600);
58124
const minutes = Math.floor((timeInSeconds % 3600) / 60);
59125
const seconds = Math.floor(timeInSeconds % 60);
@@ -65,27 +131,51 @@ export default function NetflixStylePlayer({
65131
};
66132

67133
const togglePlay = () => {
68-
if (videoRef.current) {
69-
isPlaying ? videoRef.current.pause() : videoRef.current.play();
70-
setIsPlaying(!isPlaying);
134+
const video = videoRef.current;
135+
if (!video) return;
136+
if (video.paused) {
137+
video.play().catch(() => {});
138+
} else {
139+
video.pause();
71140
}
72141
};
73142

74143
const handleProgress = () => {
75144
if (videoRef.current) {
145+
setCurrentTime(videoRef.current.currentTime);
76146
setProgress(
77-
(videoRef.current.currentTime / videoRef.current.duration) * 100,
147+
videoRef.current.duration
148+
? (videoRef.current.currentTime / videoRef.current.duration) * 100
149+
: 0,
78150
);
79151
}
80152
};
81153

154+
const handleLoadedMetadata = () => {
155+
if (videoRef.current) {
156+
setDuration(videoRef.current.duration);
157+
}
158+
};
159+
160+
const handleDurationChange = () => {
161+
if (videoRef.current) {
162+
setDuration(videoRef.current.duration);
163+
}
164+
};
165+
82166
const handleProgressBarClick = (e: React.MouseEvent<HTMLDivElement>) => {
83167
if (videoRef.current) {
84168
const progressBar = e.currentTarget;
85169
const clickPosition =
86170
(e.clientX - progressBar.getBoundingClientRect().left) /
87171
progressBar.offsetWidth;
88-
videoRef.current.currentTime = clickPosition * videoRef.current.duration;
172+
const rawTime = clickPosition * videoRef.current.duration;
173+
const maxTime = Number.isFinite(videoRef.current.duration)
174+
? videoRef.current.duration
175+
: 0;
176+
const newTime = Math.min(Math.max(rawTime, 0), maxTime);
177+
videoRef.current.currentTime = newTime;
178+
setCurrentTime(newTime);
89179
}
90180
};
91181

@@ -95,27 +185,33 @@ export default function NetflixStylePlayer({
95185
} else {
96186
document.exitFullscreen();
97187
}
98-
setIsFullscreen(!isFullscreen);
99188
};
100189

101190
const skipTime = (seconds: number) => {
102191
if (videoRef.current) {
103-
videoRef.current.currentTime += seconds;
192+
const rawTime = videoRef.current.currentTime + seconds;
193+
const maxTime = Number.isFinite(videoRef.current.duration)
194+
? videoRef.current.duration
195+
: 0;
196+
const newTime = Math.min(Math.max(rawTime, 0), maxTime);
197+
videoRef.current.currentTime = newTime;
198+
setCurrentTime(newTime);
104199
}
105200
};
106201

107202
const handleVolumeChange = (newVolume: number[]) => {
108203
if (videoRef.current) {
109204
const volumeValue = newVolume[0];
110205
videoRef.current.volume = volumeValue;
206+
videoRef.current.muted = volumeValue === 0;
111207
setVolume(volumeValue);
112208
setIsMuted(volumeValue === 0);
113209
}
114210
};
115211

116212
const toggleMute = () => {
117213
if (videoRef.current) {
118-
const newMuteState = !isMuted;
214+
const newMuteState = !videoRef.current.muted;
119215
videoRef.current.muted = newMuteState;
120216
setIsMuted(newMuteState);
121217
}
@@ -130,9 +226,14 @@ export default function NetflixStylePlayer({
130226
>
131227
<video
132228
ref={videoRef}
133-
src={videoSrc}
229+
src={resolvedSrc}
134230
className="h-full w-full"
135231
onTimeUpdate={handleProgress}
232+
onLoadedMetadata={handleLoadedMetadata}
233+
onDurationChange={handleDurationChange}
234+
onPlay={() => setIsPlaying(true)}
235+
onPause={() => setIsPlaying(false)}
236+
onEnded={() => setIsPlaying(false)}
136237
preload="auto"
137238
/>
138239
</div>
@@ -167,9 +268,7 @@ export default function NetflixStylePlayer({
167268
<FastForward size={24} />
168269
</button>
169270
<div className="text-white">
170-
{formatTime(videoRef.current?.currentTime ?? 0) +
171-
' / ' +
172-
formatTime(videoRef.current?.duration ?? 0)}
271+
{formatTime(currentTime) + ' / ' + formatTime(duration)}
173272
</div>
174273
</div>
175274

0 commit comments

Comments
 (0)