fix mobile options

This commit is contained in:
PhilReact 2025-06-23 15:02:47 +03:00
parent 378ec78c25
commit 8afeb4af7b
5 changed files with 323 additions and 280 deletions

View File

@ -21,6 +21,7 @@ interface MobileControlsProps {
openPlaybackMenu: () => void; openPlaybackMenu: () => void;
toggleFullscreen: () => void; toggleFullscreen: () => void;
setProgressRelative: (val: number) => void; setProgressRelative: (val: number) => void;
setLocalProgress: (val: number)=> void;
} }
export const MobileControls = ({ export const MobileControls = ({
showControlsMobile, showControlsMobile,
@ -34,6 +35,7 @@ export const MobileControls = ({
openPlaybackMenu, openPlaybackMenu,
toggleFullscreen, toggleFullscreen,
setProgressRelative, setProgressRelative,
setLocalProgress
}: MobileControlsProps) => { }: MobileControlsProps) => {
return ( return (
<Box <Box
@ -195,6 +197,7 @@ export const MobileControls = ({
playerRef={playerRef} playerRef={playerRef}
progress={progress} progress={progress}
duration={duration} duration={duration}
setLocalProgress={setLocalProgress}
/> />
</Box> </Box>
</Box> </Box>

View File

@ -65,17 +65,26 @@ export const ReloadButton = ({ reloadVideo, isScreenSmall }: any) => {
); );
}; };
export const ProgressSlider = ({ progress, duration, playerRef }: any) => { export const ProgressSlider = ({ progress, setLocalProgress, duration, playerRef }: any) => {
const sliderRef = useRef(null); const sliderRef = useRef(null);
const [isDragging, setIsDragging] = useState(false);
const [sliderValue, setSliderValue] = useState(0); // local slider value
const [hoverX, setHoverX] = useState<number | null>(null); const [hoverX, setHoverX] = useState<number | null>(null);
const [thumbnailUrl, setThumbnailUrl] = useState<string | null>(null); const [thumbnailUrl, setThumbnailUrl] = useState<string | null>(null);
const [showDuration, setShowDuration] = useState(0); const [showDuration, setShowDuration] = useState(0);
const onProgressChange = (e: any, value: number | number[]) => { const onProgressChange = (e: any, value: number | number[]) => {
if (!playerRef.current) return; setIsDragging(true);
setSliderValue(value as number);
playerRef.current?.currentTime(value as number);
}; };
const onChangeCommitted = (e: any, value: number | number[]) => {
if (!playerRef.current) return;
setSliderValue(value as number);
playerRef.current?.currentTime(value as number);
setIsDragging(false);
setLocalProgress(value)
};
const THUMBNAIL_DEBOUNCE = 500; const THUMBNAIL_DEBOUNCE = 500;
const THUMBNAIL_MIN_DIFF = 10; const THUMBNAIL_MIN_DIFF = 10;
@ -158,8 +167,10 @@ export const ProgressSlider = ({ progress, duration, playerRef }: any) => {
onMouseMove={handleMouseMove} onMouseMove={handleMouseMove}
onMouseLeave={handleMouseLeave} onMouseLeave={handleMouseLeave}
onClickCapture={handleClickCapture} onClickCapture={handleClickCapture}
value={progress} value={isDragging ? sliderValue : progress} // use local state if dragging
onChange={onProgressChange} onChange={onProgressChange}
onChangeCommitted={onChangeCommitted}
min={0} min={0}
max={duration || 100} max={duration || 100}
step={0.1} step={0.1}

View File

@ -43,9 +43,10 @@ interface VideoControlsBarProps {
openPlaybackMenu: ()=> void openPlaybackMenu: ()=> void
togglePictureInPicture: ()=> void togglePictureInPicture: ()=> void
isVideoPlayerSmall: boolean isVideoPlayerSmall: boolean
setLocalProgress: (val: number)=> void
} }
export const VideoControlsBar = ({subtitleBtnRef, showControls, playbackRate, increaseSpeed,decreaseSpeed, isFullScreen, showControlsFullScreen, reloadVideo, onVolumeChange, volume, isPlaying, canPlay, isScreenSmall, controlsHeight, playerRef, duration, progress, togglePlay, toggleFullscreen, extractFrames, openSubtitleManager, onSelectPlaybackRate, isMuted, toggleMute, openPlaybackMenu, togglePictureInPicture, isVideoPlayerSmall}: VideoControlsBarProps) => { export const VideoControlsBar = ({subtitleBtnRef, setLocalProgress, showControls, playbackRate, increaseSpeed,decreaseSpeed, isFullScreen, showControlsFullScreen, reloadVideo, onVolumeChange, volume, isPlaying, canPlay, isScreenSmall, controlsHeight, playerRef, duration, progress, togglePlay, toggleFullscreen, extractFrames, openSubtitleManager, onSelectPlaybackRate, isMuted, toggleMute, openPlaybackMenu, togglePictureInPicture, isVideoPlayerSmall}: VideoControlsBarProps) => {
const showMobileControls = isScreenSmall && canPlay; const showMobileControls = isScreenSmall && canPlay;
@ -87,7 +88,7 @@ export const VideoControlsBar = ({subtitleBtnRef, showControls, playbackRate, in
width: '100%' width: '100%'
}}> }}>
<ProgressSlider playerRef={playerRef} progress={progress} duration={duration} /> <ProgressSlider setLocalProgress={setLocalProgress} playerRef={playerRef} progress={progress} duration={duration} />
{!isVideoPlayerSmall && ( {!isVideoPlayerSmall && (
<Box sx={{ <Box sx={{
width: '100%', width: '100%',

View File

@ -33,7 +33,14 @@ import { TimelineActionsComponent } from "./TimelineActionsComponent";
import { PlayBackMenu } from "./VideoControls"; import { PlayBackMenu } from "./VideoControls";
import { useGlobalPlayerStore } from "../../state/pip"; import { useGlobalPlayerStore } from "../../state/pip";
import { LocationContext } from "../../context/GlobalProvider"; import { LocationContext } from "../../context/GlobalProvider";
import { alpha, Box, Drawer, List, ListItem } from "@mui/material"; import {
alpha,
Box,
ClickAwayListener,
Drawer,
List,
ListItem,
} from "@mui/material";
import { MobileControls } from "./MobileControls"; import { MobileControls } from "./MobileControls";
export async function srtBase64ToVttBlobUrl( export async function srtBase64ToVttBlobUrl(
@ -61,21 +68,21 @@ type StretchVideoType = "contain" | "fill" | "cover" | "none" | "scale-down";
export type TimelineAction = export type TimelineAction =
| { | {
type: 'SEEK'; type: "SEEK";
time: number; time: number;
duration: number; duration: number;
label: string; label: string;
onClick?: () => void; onClick?: () => void;
seekToTime: number; // ✅ Required for SEEK seekToTime: number; // ✅ Required for SEEK
placement?: 'TOP-RIGHT' | 'TOP-LEFT' | 'BOTTOM-LEFT' | 'BOTTOM-RIGHT'; placement?: "TOP-RIGHT" | "TOP-LEFT" | "BOTTOM-LEFT" | "BOTTOM-RIGHT";
} }
| { | {
type: 'CUSTOM'; type: "CUSTOM";
time: number; time: number;
duration: number; duration: number;
label: string; label: string;
onClick: () => void; // ✅ Required for CUSTOM onClick: () => void; // ✅ Required for CUSTOM
placement?: 'TOP-RIGHT' | 'TOP-LEFT' | 'BOTTOM-LEFT' | 'BOTTOM-RIGHT'; placement?: "TOP-RIGHT" | "TOP-LEFT" | "BOTTOM-LEFT" | "BOTTOM-RIGHT";
}; };
interface VideoPlayerProps { interface VideoPlayerProps {
qortalVideoResource: QortalGetMetadata; qortalVideoResource: QortalGetMetadata;
@ -84,18 +91,14 @@ interface VideoPlayerProps {
poster?: string; poster?: string;
autoPlay?: boolean; autoPlay?: boolean;
onEnded?: (e: React.SyntheticEvent<HTMLVideoElement, Event>) => void; onEnded?: (e: React.SyntheticEvent<HTMLVideoElement, Event>) => void;
timelineActions?: TimelineAction[] timelineActions?: TimelineAction[];
} }
const videoStyles = { const videoStyles = {
videoContainer: {}, videoContainer: {},
video: {}, video: {},
}; };
async function getVideoMimeTypeFromUrl( async function getVideoMimeTypeFromUrl(
qortalVideoResource: any qortalVideoResource: any
): Promise<string | null> { ): Promise<string | null> {
@ -109,8 +112,8 @@ async function getVideoMimeTypeFromUrl(
return null; return null;
} }
} }
export const isTouchDevice = 'ontouchstart' in window || navigator.maxTouchPoints > 0; export const isTouchDevice =
"ontouchstart" in window || navigator.maxTouchPoints > 0;
export const VideoPlayer = ({ export const VideoPlayer = ({
videoRef, videoRef,
@ -119,14 +122,14 @@ export const VideoPlayer = ({
poster, poster,
autoPlay, autoPlay,
onEnded, onEnded,
timelineActions timelineActions,
}: VideoPlayerProps) => { }: VideoPlayerProps) => {
const containerRef = useRef<HTMLDivElement | null>(null); const containerRef = useRef<HTMLDivElement | null>(null);
const [videoObjectFit] = useState<StretchVideoType>("contain"); const [videoObjectFit] = useState<StretchVideoType>("contain");
const [isPlaying, setIsPlaying] = useState(false); const [isPlaying, setIsPlaying] = useState(false);
const [width, setWidth] = useState(0); const [width, setWidth] = useState(0);
console.log('width',width) console.log("width", width);
useEffect(() => { useEffect(() => {
const observer = new ResizeObserver(([entry]) => { const observer = new ResizeObserver(([entry]) => {
setWidth(entry.contentRect.width); setWidth(entry.contentRect.width);
@ -143,9 +146,9 @@ useEffect(() => {
}) })
); );
const playerRef = useRef<Player | null>(null); const playerRef = useRef<Player | null>(null);
const [drawerOpenSubtitles, setDrawerOpenSubtitles] = useState(false) const [drawerOpenSubtitles, setDrawerOpenSubtitles] = useState(false);
const [drawerOpenPlayback, setDrawerOpenPlayback] = useState(false) const [drawerOpenPlayback, setDrawerOpenPlayback] = useState(false);
const [showControlsMobile, setShowControlsMobile] = useState(false) const [showControlsMobile, setShowControlsMobile] = useState(false);
const [isPlayerInitialized, setIsPlayerInitialized] = useState(false); const [isPlayerInitialized, setIsPlayerInitialized] = useState(false);
const [videoCodec, setVideoCodec] = useState<null | false | string>(null); const [videoCodec, setVideoCodec] = useState<null | false | string>(null);
const [isMuted, setIsMuted] = useState(false); const [isMuted, setIsMuted] = useState(false);
@ -156,12 +159,12 @@ useEffect(() => {
const [showControls, setShowControls] = useState(false); const [showControls, setShowControls] = useState(false);
const [isOpenSubtitleManage, setIsOpenSubtitleManage] = useState(false); const [isOpenSubtitleManage, setIsOpenSubtitleManage] = useState(false);
const subtitleBtnRef = useRef(null); const subtitleBtnRef = useRef(null);
const [currentSubTrack, setCurrentSubTrack] = useState<null | string>(null) const [currentSubTrack, setCurrentSubTrack] = useState<null | string>(null);
const location = useContext(LocationContext) const location = useContext(LocationContext);
const locationRef = useRef<string | null>(null) const locationRef = useRef<string | null>(null);
const [isOpenPlaybackMenu, setIsOpenPlaybackmenu] = useState(false) const [isOpenPlaybackMenu, setIsOpenPlaybackmenu] = useState(false);
const isVideoPlayerSmall = width < 600 const isVideoPlayerSmall = width < 600 || isTouchDevice;
const { const {
reloadVideo, reloadVideo,
togglePlay, togglePlay,
@ -186,7 +189,7 @@ useEffect(() => {
showControlsFullScreen, showControlsFullScreen,
onSelectPlaybackRate, onSelectPlaybackRate,
seekTo, seekTo,
togglePictureInPicture togglePictureInPicture,
} = useVideoPlayerController({ } = useVideoPlayerController({
autoPlay, autoPlay,
playerRef, playerRef,
@ -194,15 +197,14 @@ useEffect(() => {
retryAttempts, retryAttempts,
isPlayerInitialized, isPlayerInitialized,
isMuted, isMuted,
videoRef videoRef,
}); });
useEffect(() => { useEffect(() => {
if (location) { if (location) {
locationRef.current = location.pathname locationRef.current = location.pathname;
} }
},[location]) }, [location]);
const { getProgress } = useProgressStore(); const { getProgress } = useProgressStore();
@ -218,28 +220,26 @@ const enterFullscreen = useCallback(async () => {
await (ref as any).webkitRequestFullscreen(); // Safari fallback await (ref as any).webkitRequestFullscreen(); // Safari fallback
} }
if ( if (
typeof screen.orientation !== 'undefined' && typeof screen.orientation !== "undefined" &&
'lock' in screen.orientation && "lock" in screen.orientation &&
typeof screen.orientation.lock === 'function' typeof screen.orientation.lock === "function"
) { ) {
try { try {
await (screen.orientation as any).lock('landscape'); await (screen.orientation as any).lock("landscape");
} catch (err) { } catch (err) {
console.warn('Orientation lock failed:', err); console.warn("Orientation lock failed:", err);
} }
} }
await qortalRequest({ await qortalRequest({
action: 'SCREEN_ORIENTATION', action: "SCREEN_ORIENTATION",
mode: 'landscape' mode: "landscape",
}) });
} catch (err) { } catch (err) {
console.error('Failed to enter fullscreen or lock orientation:', err); console.error("Failed to enter fullscreen or lock orientation:", err);
} }
}, []); }, []);
// const exitFullscreen = useCallback(() => { // const exitFullscreen = useCallback(() => {
// document?.exitFullscreen(); // document?.exitFullscreen();
// }, [isFullscreen]); // }, [isFullscreen]);
@ -250,25 +250,24 @@ const enterFullscreen = useCallback(async () => {
await document.exitFullscreen(); await document.exitFullscreen();
} }
if ( if (
typeof screen.orientation !== 'undefined' && typeof screen.orientation !== "undefined" &&
'lock' in screen.orientation && "lock" in screen.orientation &&
typeof screen.orientation.lock === 'function' typeof screen.orientation.lock === "function"
) { ) {
try { try {
// Attempt to reset by locking to 'portrait' or 'any' (if supported) // Attempt to reset by locking to 'portrait' or 'any' (if supported)
await screen.orientation.lock('portrait'); // or 'any' if supported await screen.orientation.lock("portrait"); // or 'any' if supported
} catch (err) { } catch (err) {
console.warn('Orientation lock failed:', err); console.warn("Orientation lock failed:", err);
} }
} }
await qortalRequest({ await qortalRequest({
action: 'SCREEN_ORIENTATION', action: "SCREEN_ORIENTATION",
mode: 'portrait' mode: "portrait",
}) });
} catch (err) { } catch (err) {
console.warn('Error exiting fullscreen or unlocking orientation:', err); console.warn("Error exiting fullscreen or unlocking orientation:", err);
} }
}, [isFullscreen]); }, [isFullscreen]);
@ -276,7 +275,6 @@ const enterFullscreen = useCallback(async () => {
isFullscreen ? exitFullscreen() : enterFullscreen(); isFullscreen ? exitFullscreen() : enterFullscreen();
}, [isFullscreen]); }, [isFullscreen]);
const hotkeyHandlers = useMemo( const hotkeyHandlers = useMemo(
() => ({ () => ({
reloadVideo, reloadVideo,
@ -290,7 +288,7 @@ const enterFullscreen = useCallback(async () => {
toggleMute, toggleMute,
setProgressAbsolute, setProgressAbsolute,
setAlwaysShowControls, setAlwaysShowControls,
toggleFullscreen toggleFullscreen,
}), }),
[ [
reloadVideo, reloadVideo,
@ -304,17 +302,17 @@ const enterFullscreen = useCallback(async () => {
toggleMute, toggleMute,
setProgressAbsolute, setProgressAbsolute,
setAlwaysShowControls, setAlwaysShowControls,
toggleFullscreen toggleFullscreen,
] ]
); );
const closeSubtitleManager = useCallback(() => { const closeSubtitleManager = useCallback(() => {
setIsOpenSubtitleManage(false); setIsOpenSubtitleManage(false);
setDrawerOpenSubtitles(false) setDrawerOpenSubtitles(false);
}, []); }, []);
const openSubtitleManager = useCallback(() => { const openSubtitleManager = useCallback(() => {
if (isVideoPlayerSmall) { if (isVideoPlayerSmall) {
setDrawerOpenSubtitles(true) setDrawerOpenSubtitles(true);
} }
setIsOpenSubtitleManage(true); setIsOpenSubtitleManage(true);
}, [isVideoPlayerSmall]); }, [isVideoPlayerSmall]);
@ -323,10 +321,10 @@ const enterFullscreen = useCallback(async () => {
if (!qortalVideoResource) return null; if (!qortalVideoResource) return null;
return `${qortalVideoResource.service}-${qortalVideoResource.name}-${qortalVideoResource.identifier}`; return `${qortalVideoResource.service}-${qortalVideoResource.name}-${qortalVideoResource.identifier}`;
}, [qortalVideoResource]); }, [qortalVideoResource]);
const videoLocationRef = useRef< null | string>(null) const videoLocationRef = useRef<null | string>(null);
useEffect(() => { useEffect(() => {
videoLocationRef.current = videoLocation videoLocationRef.current = videoLocation;
}, [videoLocation]) }, [videoLocation]);
useVideoPlayerHotKeys(hotkeyHandlers); useVideoPlayerHotKeys(hotkeyHandlers);
const updateProgress = useCallback(() => { const updateProgress = useCallback(() => {
@ -342,12 +340,12 @@ const videoLocationRef = useRef< null | string>(null)
useEffect(() => { useEffect(() => {
if (videoLocation) { if (videoLocation) {
const vidId = useGlobalPlayerStore.getState().videoId const vidId = useGlobalPlayerStore.getState().videoId;
if (vidId === videoLocation) { if (vidId === videoLocation) {
togglePlay() togglePlay();
} }
} }
}, [videoLocation]) }, [videoLocation]);
// useEffect(() => { // useEffect(() => {
// const ref = videoRef as React.RefObject<HTMLVideoElement>; // const ref = videoRef as React.RefObject<HTMLVideoElement>;
// if (!ref.current) return; // if (!ref.current) return;
@ -428,7 +426,6 @@ const videoLocationRef = useRef< null | string>(null)
}; };
}, [isPlayerInitialized]); }, [isPlayerInitialized]);
const canvasRef = useRef(null); const canvasRef = useRef(null);
const videoRefForCanvas = useRef<any>(null); const videoRefForCanvas = useRef<any>(null);
const extractFrames = useCallback((time: number): void => { const extractFrames = useCallback((time: number): void => {
@ -463,7 +460,7 @@ const videoLocationRef = useRef< null | string>(null)
const hideTimeout = useRef<any>(null); const hideTimeout = useRef<any>(null);
const resetHideTimer = () => { const resetHideTimer = () => {
if(isTouchDevice) return if (isTouchDevice) return;
setShowControls(true); setShowControls(true);
if (hideTimeout.current) clearTimeout(hideTimeout.current); if (hideTimeout.current) clearTimeout(hideTimeout.current);
hideTimeout.current = setTimeout(() => { hideTimeout.current = setTimeout(() => {
@ -472,24 +469,24 @@ const videoLocationRef = useRef< null | string>(null)
}; };
const handleMouseMove = () => { const handleMouseMove = () => {
if(isTouchDevice) return if (isTouchDevice) return;
resetHideTimer(); resetHideTimer();
}; };
const closePlaybackMenu = useCallback(() => { const closePlaybackMenu = useCallback(() => {
setIsOpenPlaybackmenu(false) setIsOpenPlaybackmenu(false);
setDrawerOpenPlayback(false) setDrawerOpenPlayback(false);
}, []) }, []);
const openPlaybackMenu = useCallback(() => { const openPlaybackMenu = useCallback(() => {
if (isVideoPlayerSmall) { if (isVideoPlayerSmall) {
setDrawerOpenPlayback(true) setDrawerOpenPlayback(true);
return return;
} }
setIsOpenPlaybackmenu(true) setIsOpenPlaybackmenu(true);
}, [isVideoPlayerSmall]) }, [isVideoPlayerSmall]);
useEffect(() => { useEffect(() => {
if(isTouchDevice) return if (isTouchDevice) return;
resetHideTimer(); // initial show resetHideTimer(); // initial show
return () => { return () => {
if (hideTimeout.current) clearTimeout(hideTimeout.current); if (hideTimeout.current) clearTimeout(hideTimeout.current);
@ -511,7 +508,7 @@ setDrawerOpenPlayback(false)
const onSelectSubtitle = useCallback( const onSelectSubtitle = useCallback(
async (subtitle: SubtitlePublishedData) => { async (subtitle: SubtitlePublishedData) => {
if (subtitle === null) { if (subtitle === null) {
setCurrentSubTrack(null) setCurrentSubTrack(null);
if (previousSubtitleUrlRef.current) { if (previousSubtitleUrlRef.current) {
URL.revokeObjectURL(previousSubtitleUrlRef.current); URL.revokeObjectURL(previousSubtitleUrlRef.current);
previousSubtitleUrlRef.current = null; previousSubtitleUrlRef.current = null;
@ -537,7 +534,7 @@ setDrawerOpenPlayback(false)
}); });
} }
return return;
} }
const player = playerRef.current; const player = playerRef.current;
if (!player || !subtitle.subtitleData || !subtitle.type) return; if (!player || !subtitle.subtitleData || !subtitle.type) return;
@ -618,7 +615,6 @@ setDrawerOpenPlayback(false)
(_, i) => (tracksInfo as any)[i] (_, i) => (tracksInfo as any)[i]
); );
for (const track of tracks) { for (const track of tracks) {
if (track.kind === "subtitles") { if (track.kind === "subtitles") {
track.mode = "showing"; // force display track.mode = "showing"; // force display
} }
@ -636,19 +632,17 @@ setDrawerOpenPlayback(false)
return JSON.stringify(qortalVideoResource); return JSON.stringify(qortalVideoResource);
}, [qortalVideoResource]); }, [qortalVideoResource]);
const savedVideoRef = useRef<HTMLVideoElement | null>(null); const savedVideoRef = useRef<HTMLVideoElement | null>(null);
useEffect(() => { useEffect(() => {
if (startPlay) { if (startPlay) {
useGlobalPlayerStore.getState().reset() useGlobalPlayerStore.getState().reset();
} }
}, [startPlay]) }, [startPlay]);
useLayoutEffect(() => { useLayoutEffect(() => {
// Save the video element while it's still mounted // Save the video element while it's still mounted
const video = videoRef as any const video = videoRef as any;
if (video.current) { if (video.current) {
savedVideoRef.current = video.current; savedVideoRef.current = video.current;
} }
@ -687,7 +681,7 @@ savedVideoRef.current = video.current;
playerRef.current = videojs(ref.current, options, () => { playerRef.current = videojs(ref.current, options, () => {
setIsPlayerInitialized(true); setIsPlayerInitialized(true);
ref.current.tabIndex = -1; // Prevents focus entirely ref.current.tabIndex = -1; // Prevents focus entirely
ref.current.style.outline = 'none'; // Backup ref.current.style.outline = "none"; // Backup
playerRef.current?.poster(""); playerRef.current?.poster("");
playerRef.current?.playbackRate(playbackRate); playerRef.current?.playbackRate(playbackRate);
playerRef.current?.volume(volume); playerRef.current?.volume(volume);
@ -695,7 +689,6 @@ savedVideoRef.current = video.current;
const savedProgress = getProgress(videoLocationRef.current); const savedProgress = getProgress(videoLocationRef.current);
if (typeof savedProgress === "number") { if (typeof savedProgress === "number") {
playerRef.current?.currentTime(savedProgress); playerRef.current?.currentTime(savedProgress);
} }
} }
@ -711,9 +704,8 @@ savedVideoRef.current = video.current;
(_, i) => (tracksInfo as any)[i] (_, i) => (tracksInfo as any)[i]
); );
for (const track of tracks) { for (const track of tracks) {
if (track.kind === "subtitles" || track.kind === "captions") {
if (track.kind === 'subtitles' || track.kind === 'captions') { if (track.mode === "showing") {
if (track.mode === 'showing') {
activeTrack = track; activeTrack = track;
break; break;
} }
@ -721,10 +713,9 @@ savedVideoRef.current = video.current;
} }
if (activeTrack) { if (activeTrack) {
setCurrentSubTrack(activeTrack.language || activeTrack.srclang);
setCurrentSubTrack(activeTrack.language || activeTrack.srclang)
} else { } else {
setCurrentSubTrack(null) setCurrentSubTrack(null);
console.log("No subtitle is currently showing"); console.log("No subtitle is currently showing");
} }
}; };
@ -748,7 +739,7 @@ savedVideoRef.current = video.current;
console.error("useEffect start player", error); console.error("useEffect start player", error);
} }
return () => { return () => {
const video = savedVideoRef as any const video = savedVideoRef as any;
const videoEl = video?.current!; const videoEl = video?.current!;
const player = playerRef.current; const player = playerRef.current;
@ -762,10 +753,10 @@ savedVideoRef.current = video.current;
videoSrc: videoEl.src, videoSrc: videoEl.src,
currentTime: current ?? 0, currentTime: current ?? 0,
isPlaying: true, isPlaying: true,
mode: 'floating', mode: "floating",
videoId: videoLocationRef.current, videoId: videoLocationRef.current,
location: locationRef.current || "", location: locationRef.current || "",
type: currentSource || 'video/mp4' type: currentSource || "video/mp4",
}); });
} }
@ -802,7 +793,6 @@ savedVideoRef.current = video.current;
}, [isPlayerInitialized]); }, [isPlayerInitialized]);
const hideTimeoutRef = useRef<number | null>(null); const hideTimeoutRef = useRef<number | null>(null);
const resetHideTimeout = () => { const resetHideTimeout = () => {
setShowControlsMobile(true); setShowControlsMobile(true);
if (hideTimeoutRef.current) clearTimeout(hideTimeoutRef.current); if (hideTimeoutRef.current) clearTimeout(hideTimeoutRef.current);
@ -817,16 +807,16 @@ savedVideoRef.current = video.current;
const container = containerRef.current; const container = containerRef.current;
if (!container) return; if (!container) return;
container.addEventListener('touchstart', handleInteraction); container.addEventListener("touchstart", handleInteraction);
// container.addEventListener('mousemove', handleInteraction); // container.addEventListener('mousemove', handleInteraction);
return () => { return () => {
container.removeEventListener('touchstart', handleInteraction); container.removeEventListener("touchstart", handleInteraction);
// container.removeEventListener('mousemove', handleInteraction); // container.removeEventListener('mousemove', handleInteraction);
}; };
}, []); }, []);
console.log('showControlsMobile', showControlsMobile) console.log("showControlsMobile", showControlsMobile);
return ( return (
<> <>
@ -865,7 +855,13 @@ savedVideoRef.current = video.current;
onVolumeChange={onVolumeChangeHandler} onVolumeChange={onVolumeChangeHandler}
controls={false} controls={false}
/> />
<PlayBackMenu isFromDrawer={false} close={closePlaybackMenu} isOpen={isOpenPlaybackMenu} onSelect={onSelectPlaybackRate} playbackRate={playbackRate} /> <PlayBackMenu
isFromDrawer={false}
close={closePlaybackMenu}
isOpen={isOpenPlaybackMenu}
onSelect={onSelectPlaybackRate}
playbackRate={playbackRate}
/>
{isReady && !showControlsMobile && ( {isReady && !showControlsMobile && (
<VideoControlsBar <VideoControlsBar
@ -896,17 +892,34 @@ savedVideoRef.current = video.current;
toggleMute={toggleMute} toggleMute={toggleMute}
openPlaybackMenu={openPlaybackMenu} openPlaybackMenu={openPlaybackMenu}
togglePictureInPicture={togglePictureInPicture} togglePictureInPicture={togglePictureInPicture}
setLocalProgress={setLocalProgress}
/> />
)} )}
{timelineActions && Array.isArray(timelineActions) && ( {timelineActions && Array.isArray(timelineActions) && (
<TimelineActionsComponent seekTo={seekTo} containerRef={containerRef} progress={localProgress} timelineActions={timelineActions}/> <TimelineActionsComponent
seekTo={seekTo}
containerRef={containerRef}
progress={localProgress}
timelineActions={timelineActions}
/>
)} )}
{showControlsMobile && ( {showControlsMobile && (
<MobileControls setProgressRelative={setProgressRelative} toggleFullscreen={toggleFullscreen} openPlaybackMenu={openPlaybackMenu} openSubtitleManager={openSubtitleManager} togglePlay={togglePlay} isPlaying={isPlaying} setShowControlsMobile={setShowControlsMobile} duration={duration} <MobileControls
progress={localProgress} playerRef={playerRef} showControlsMobile={showControlsMobile} /> setLocalProgress={setLocalProgress}
setProgressRelative={setProgressRelative}
toggleFullscreen={toggleFullscreen}
openPlaybackMenu={openPlaybackMenu}
openSubtitleManager={openSubtitleManager}
togglePlay={togglePlay}
isPlaying={isPlaying}
setShowControlsMobile={setShowControlsMobile}
duration={duration}
progress={localProgress}
playerRef={playerRef}
showControlsMobile={showControlsMobile}
/>
)} )}
{!isVideoPlayerSmall && ( {!isVideoPlayerSmall && (
<SubtitleManager <SubtitleManager
subtitleBtnRef={subtitleBtnRef} subtitleBtnRef={subtitleBtnRef}
@ -919,24 +932,27 @@ savedVideoRef.current = video.current;
isFromDrawer={false} isFromDrawer={false}
/> />
)} )}
<ClickAwayListener onClickAway={() => setDrawerOpenSubtitles(false)}>
</VideoContainer> <Drawer
<Drawer anchor="bottom" open={drawerOpenSubtitles} onClose={() => setDrawerOpenSubtitles(false)} sx={{ variant="persistent"
anchor="bottom"
}} slotProps={{ open={drawerOpenSubtitles}
sx={{}}
slotProps={{
paper: { paper: {
sx: { sx: {
backgroundColor: alpha("#181818", 0.98), backgroundColor: alpha("#181818", 0.98),
borderRadius: 2, borderRadius: 2,
width: '90%', width: "90%",
margin: '0 auto', margin: "0 auto",
p: 1, p: 1,
backgroundImage: 'none', backgroundImage: "none",
mb: 1 mb: 1,
position: "absolute",
}, },
} },
}}> }}
>
<SubtitleManager <SubtitleManager
subtitleBtnRef={subtitleBtnRef} subtitleBtnRef={subtitleBtnRef}
close={closeSubtitleManager} close={closeSubtitleManager}
@ -947,27 +963,39 @@ savedVideoRef.current = video.current;
setDrawerOpenSubtitles={setDrawerOpenSubtitles} setDrawerOpenSubtitles={setDrawerOpenSubtitles}
isFromDrawer={true} isFromDrawer={true}
/> />
</Drawer> </Drawer>
<Drawer anchor="bottom" open={drawerOpenPlayback} onClose={() => setDrawerOpenPlayback(false)} sx={{ </ClickAwayListener>
<ClickAwayListener onClickAway={() => setDrawerOpenPlayback(false)}>
}} slotProps={{ <Drawer
variant="persistent"
anchor="bottom"
open={drawerOpenPlayback}
sx={{}}
slotProps={{
paper: { paper: {
sx: { sx: {
backgroundColor: alpha("#181818", 0.98), backgroundColor: alpha("#181818", 0.98),
borderRadius: 2, borderRadius: 2,
width: '90%', width: "90%",
margin: '0 auto', margin: "0 auto",
p: 1, p: 1,
backgroundImage: 'none', backgroundImage: "none",
mb: 1 mb: 1,
position: "absolute",
}, },
} },
}}> }}
>
<PlayBackMenu isFromDrawer close={closePlaybackMenu} isOpen={true} onSelect={onSelectPlaybackRate} playbackRate={playbackRate} /> <PlayBackMenu
isFromDrawer
close={closePlaybackMenu}
isOpen={true}
onSelect={onSelectPlaybackRate}
playbackRate={playbackRate}
/>
</Drawer> </Drawer>
</ClickAwayListener>
</VideoContainer>
</> </>
); );
}; };

View File

@ -155,7 +155,7 @@ useEffect(() => {
const aspectRatio = 0.75; // 300 / 400 = 3:4 const aspectRatio = 0.75; // 300 / 400 = 3:4
const maxWidthByScreen = screenWidth * 0.75; const maxWidthByScreen = screenWidth * 0.75;
const maxWidthByHeight = (screenHeight * 0.2) / aspectRatio; const maxWidthByHeight = (screenHeight * 0.3) / aspectRatio;
const maxWidth = savedWidthRef.current || Math.min(maxWidthByScreen, maxWidthByHeight); const maxWidth = savedWidthRef.current || Math.min(maxWidthByScreen, maxWidthByHeight);
const maxHeight = savedHeightRef.current || maxWidth * aspectRatio; const maxHeight = savedHeightRef.current || maxWidth * aspectRatio;