Skip to content

feat(video): Add event listeners for mouse long-press to increase the speed, 0 for start over same as in youtube #1777

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions src/components/CodeBlock.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,12 @@ const CodeBlock: React.FC<CodeBlockProps> = ({ block }) => {
return (
<div className="w-full overflow-auto py-2">
<pre
onClick={handleCopy}
className="group relative flex cursor-pointer justify-between rounded-lg border border-primary/5 bg-neutral-50 p-6 dark:bg-neutral-900"
>
<code className="overflow-auto font-mono text-primary">{code}</code>
<div className="absolute right-4 top-4 flex flex-col text-primary/50 transition-all duration-300 group-hover:opacity-100 lg:opacity-25">
<button onClick={handleCopy} className="absolute right-4 top-4 flex flex-col text-primary/50 transition-all duration-300 group-hover:opacity-100 lg:opacity-25">
<Copy className="size-4 text-primary/50" />
</div>
</button>
</pre>
</div>
);
Expand Down
120 changes: 105 additions & 15 deletions src/components/VideoPlayer2.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ export const VideoPlayer: FunctionComponent<VideoPlayerProps> = ({
const [player, setPlayer] = useState<any>(null);
const searchParams = useSearchParams();
const vidUrl = options.sources[0].src;
const [previousPlaybackRate, setPreviousPlaybackRate] = useState<number>(1);
const [isLongPress, setIsLongPress] = useState<boolean>(false);

const togglePictureInPicture = async () => {
try {
Expand Down Expand Up @@ -96,6 +98,71 @@ export const VideoPlayer: FunctionComponent<VideoPlayerProps> = ({
return pipButtonContainer;
};

// Add event listeners for mouse down/up to handle long-press speed change
useEffect(() => {
if (!player) return;

let longPressTimer: ReturnType<typeof setTimeout> | null = null;

const handleMouseDown = (event: MouseEvent) => {
event.preventDefault();
if (event.button !== 0) return;

setPreviousPlaybackRate(player.playbackRate());

longPressTimer = setTimeout(() => {
setIsLongPress(true);
player.playbackRate(2);
toast.info('2x speed activated');
}, 500);
};

const handleMouseUp = (event: MouseEvent) => {
event.preventDefault();

if (longPressTimer) {
clearTimeout(longPressTimer);
longPressTimer = null;
}

if (isLongPress) {
player.playbackRate(previousPlaybackRate);
setIsLongPress(false);
toast.info(`Speed restored to ${previousPlaybackRate}x`);
}
};

const handleClick = (event: MouseEvent) => {
if (isLongPress) {
event.preventDefault();
event.stopPropagation();
return;
}
// If not a long press, allow normal click behavior (optional)
if (!longPressTimer) {
if (player.paused()) {
player.play();
} else {
player.pause();
}
}
};

const videoElement = player.el();
videoElement.addEventListener('mousedown', handleMouseDown);
videoElement.addEventListener('mouseup', handleMouseUp);
videoElement.addEventListener('mouseleave', handleMouseUp);
videoElement.addEventListener('click', handleClick);

return () => {
if (longPressTimer) clearTimeout(longPressTimer);
videoElement.removeEventListener('mousedown', handleMouseDown);
videoElement.removeEventListener('mouseup', handleMouseUp);
videoElement.removeEventListener('mouseleave', handleMouseUp);
videoElement.removeEventListener('click', handleClick);
};
}, [player, previousPlaybackRate, isLongPress]);

useEffect(() => {
if (!player) return;

Expand Down Expand Up @@ -144,7 +211,6 @@ export const VideoPlayer: FunctionComponent<VideoPlayerProps> = ({
);
}
}, [contentId, player]);

useEffect(() => {
if (!player) {
return;
Expand Down Expand Up @@ -195,7 +261,7 @@ export const VideoPlayer: FunctionComponent<VideoPlayerProps> = ({
player.playbackRate(PLAYBACK_RATES[newIndexComma]);
event.stopPropagation();
break;
case 'ArrowUp': // Increase volume
case 'ArrowUp': // Increase volume (Shift + Up)
videoRef.current?.children[0].children[6].children[3].classList.add(
'vjs-hover',
);
Expand All @@ -208,7 +274,7 @@ export const VideoPlayer: FunctionComponent<VideoPlayerProps> = ({
player.volume(VOLUME_LEVELS[newIndexUp]);
event.stopPropagation();
break;
case 'ArrowDown': // Decrease volume
case 'ArrowDown': // Decrease volume (Shift + Down)
videoRef.current?.children[0].children[6].children[3].classList.add(
'vjs-hover',
);
Expand Down Expand Up @@ -244,14 +310,14 @@ export const VideoPlayer: FunctionComponent<VideoPlayerProps> = ({
player.currentTime(player.currentTime() - 5);
event.stopPropagation();
break;
case 'ArrowUp': // Arrow up for increasing volume
case 'ArrowUp': // Arrow up for increasing volume (without Shift)
event.preventDefault();
player.volume(player.volume() + 0.1);
player.volume(Math.min(player.volume() + 0.1, 1.0)); // Cap at 1.0
event.stopPropagation();
break;
case 'ArrowDown': // Arow dowwn for decreasing volume
case 'ArrowDown': // Arrow down for decreasing volume (without Shift)
event.preventDefault();
player.volume(player.volume() - 0.1);
player.volume(Math.max(player.volume() - 0.1, 0)); // Floor at 0
event.stopPropagation();
break;
case 'KeyF': // F key for fullscreen
Expand Down Expand Up @@ -279,7 +345,7 @@ export const VideoPlayer: FunctionComponent<VideoPlayerProps> = ({
}
event.stopPropagation();
break;
case 'KeyJ': // 'J' key for seeking backward 10 seconds multiplied by the playback rate
case 'KeyJ': // 'J' key for seeking backward 10 seconds multiplied by the playback rate
player.currentTime(player.currentTime() - 10 * player.playbackRate());
event.stopPropagation();
break;
Expand All @@ -294,14 +360,18 @@ export const VideoPlayer: FunctionComponent<VideoPlayerProps> = ({
case 'KeyC':
for (let i = 0; i < tracks.length; i++) {
const track = tracks[i];

if (track.kind === 'subtitles' && track.language === 'en') {
if (track.mode === 'disabled') track.mode = 'showing';
else track.mode = 'disabled';
}
}
event.stopPropagation();
break;
case 'Digit0': // '0' key to restart video (YouTube-like)
player.currentTime(0);
if (player.paused()) player.play(); // Ensure it plays after restart
event.stopPropagation();
break;
case 'Digit1':
player.currentTime(player.duration() * 0.1);
event.stopPropagation();
Expand Down Expand Up @@ -338,10 +408,6 @@ export const VideoPlayer: FunctionComponent<VideoPlayerProps> = ({
player.currentTime(player.duration() * 0.9);
event.stopPropagation();
break;
case 'Digit0':
player.currentTime(0);
event.stopPropagation();
break;
}
};
const handleKeyUp = (event: any) => {
Expand All @@ -354,9 +420,9 @@ export const VideoPlayer: FunctionComponent<VideoPlayerProps> = ({
// Cleanup function
return () => {
document.removeEventListener('keydown', handleKeyPress);
document.removeEventListener('keyup', handleKeyUp);
};
}, [player]);

useEffect(() => {
if (!player) {
return;
Expand Down Expand Up @@ -406,6 +472,30 @@ export const VideoPlayer: FunctionComponent<VideoPlayerProps> = ({
};
}, [player, contentId]);

// Override the default click behavior of the player
useEffect(() => {
if (!player) return;

const originalClickHandler = player.handleClick;

player.handleClick = function (event: MouseEvent) {
// If we're in long press mode, prevent the default click behavior
if (isLongPress) {
event.preventDefault();
event.stopPropagation();
return;
}
// Otherwise, call the original handler
originalClickHandler.call(player, event);
};

return () => {
if (player) {
player.handleClick = originalClickHandler;
}
};
}, [player, isLongPress]);

useEffect(() => {
if (!playerRef.current && videoRef.current) {
const videoElement = document.createElement('video-js');
Expand Down Expand Up @@ -515,4 +605,4 @@ export const VideoPlayer: FunctionComponent<VideoPlayerProps> = ({
);
};

export default VideoPlayer;
export default VideoPlayer;