burhan-syed/troddit

(Bug) Autoplay don't working in iphone

st4l1nR opened this issue · 2 comments

Troddit autoplay functionality it's not working in iphone safari

Who it's facing the same problem i solved it by setting onProgress={onLoadedData} instead of onLoadedData={onLoadedData} in the video handler here my code:

import Image from "next/image";
import { useInView } from "react-intersection-observer";
import { useState, useEffect, useRef, BaseSyntheticEvent } from "react";
import { useMainContext } from "../MainContext";
import { BsPlay, BsPause, BsVolumeMute, BsVolumeUp } from "react-icons/bs";
import { BiVolumeMute, BiVolumeFull, BiPlay, BiPause } from "react-icons/bi";
import { secondsToHMS } from "../../lib/utils";
import { useKeyPress } from "../hooks/KeyPress";
import { ImSpinner2 } from "react-icons/im";
import React from "react";
import useGlobalState from "../hooks/useGlobalState";
const VideoHandler = ({
  name,
  curPostName = undefined,
  thumbnail,
  placeholder,
  videoInfo,
  maxHeight = {},
  maxHeightNum = -1,
  fullMediaMode = false,
  imgFull = false,
  postMode = false,
  hide = false,
  audio,
  containerDims = undefined,
  uniformMediaMode = false,
}) => {
  const context: any = useMainContext();
  const [vodInfo, setVodInfo] = useState(() => videoInfo);
  const { getGlobalData, setGlobalData, clearGlobalState } = useGlobalState([
    "videoLoadData",
  ]);
  const video: any = useRef();
  const volControls = useRef<HTMLDivElement>(null);
  const audioRef: any = useRef();
  const fullWidthRef: any = useRef();
  const seekRef: any = useRef();

  const [hasAudio, sethasAudio] = useState(videoInfo?.hasAudio ?? false);
  const [videoLoaded, setVideoLoaded] = useState(false);
  const [useFallback, setUseFallback] = useState(false);
  const [audioPlaying, setAudioPlaying] = useState(false);
  const [videoPlaying, setVideoPlaying] = useState(false);
  const [preventPlay, setPreventPlay] = useState(hide);
  const [manualPlay, setmanualPlay] = useState(false);
  const [manualPause, setManualPause] = useState(false);
  const [muted, setMuted] = useState(
    !(context?.audioOnHover && postMode) || preventPlay
  );

  const { volume, setVolume } = context;
  const [prevMuted, setPrevMuted] = useState(true);
  const [manualAudio, setManualAudio] = useState(false);
  const [currentTime, setCurrentTime] = useState(0.0);
  const [videoDuration, setVideoDuration] = useState(0.0);
  const [buffering, setBuffering] = useState(false);
  const [mouseIn, setMouseIn] = useState(false);
  const [focused, setFocused] = useState(postMode);
  const [show, setShow] = useState(postMode ? true : false);
  const [left, setLeft] = useState(false);
  const [heightStyle, setHeightStyle] = useState({});
 

  const { ref } = useInView({
    threshold: [0, 0.7, 0.8, 0.9, 1],
    onChange: (inView, entry) => {
      if (!postMode && !preventPlay && !context?.mediaMode) {
        entry.intersectionRatio == 0
          ? setShow(false)
          : entry.intersectionRatio >= 0.8 && setShow(true);
        show && !postMode && entry.intersectionRatio < 1
          ? setLeft(true)
          : entry.intersectionRatio == 1 && setLeft(false);
      }
    },
  });

  useEffect(() => {
    if (name === curPostName && fullMediaMode) {
      setPreventPlay(false);
    } else if (fullMediaMode && name !== curPostName) {
      setPreventPlay(true);
    }
  }, [curPostName]);

  useEffect(() => {
    if (videoLoaded && show && !manualPause && !preventPlay) {
      playVideo();
    } else if (!show && videoPlaying) {
      pauseVideo();
    } else if (fullMediaMode && preventPlay) {
      pauseVideo();
    }
  }, [videoLoaded, show, preventPlay]);

  const onLoadedData = () => {
    setVideoDuration(video?.current?.duration);

    setVideoLoaded(true);
  };
  const onAudioLoaded = () => {
    sethasAudio(true);
  };

  const [vidHeight, setVidHeight] = useState(videoInfo?.height);
  const [vidWidth, setVidWidth] = useState(videoInfo?.width);
  useEffect(() => {
    const maximizeWidth = () => {
      let r = fullWidthRef?.current?.clientWidth / videoInfo.width;
      setVidWidth(Math.floor(r * videoInfo.width));
      setVidHeight(Math.floor(r * videoInfo.height));
    };
    //if imgFull maximize the video to take full width
    if (imgFull && !containerDims) {
      maximizeWidth();
    }
    //if postMode with portrait container (containerDims) fill it
    else if (postMode && containerDims) {
      let ry = containerDims?.[1] / videoInfo?.height;
      let rx = containerDims?.[0] / videoInfo?.width;
      if (Math.abs(ry - rx) < 0.05) {
        //if minimal cropping just fill the area
        setVidHeight(Math.floor(containerDims?.[1]));
        setVidWidth(Math.floor(containerDims?.[0]));
      } else if (ry <= rx) {
        setVidHeight(containerDims?.[1]);
        setVidWidth(
          Math.floor(videoInfo.width * (containerDims?.[1] / videoInfo?.height))
        );
      } else {
        setVidWidth(containerDims?.[0]);
        setVidHeight(
          Math.floor(videoInfo.height * (containerDims?.[0] / videoInfo?.width))
        );
      }
    }
    //
    //in single column or in a post we will scale video so height is within window. By default maxHeightNum is x * windowHeight (set in Media component)
    else if ((context.columns == 1 || postMode) && maxHeightNum > 0) {
      if (videoInfo.height > maxHeightNum) {
        let r2 = maxHeightNum / videoInfo.height;
        setVidHeight(Math.floor(maxHeightNum));
        setVidWidth(Math.floor(videoInfo.width * r2));
      }
    }
    //otherwise fill the available width to minimize letterboxing
    else {
      maximizeWidth();
    }

    return () => {
      //by default native video height/width
      setVidHeight(videoInfo.height);
      setVidWidth(videoInfo.width);
    };
  }, [
    imgFull,
    maxHeightNum,
    fullWidthRef?.current?.clientWidth,
    videoInfo,
    context.columnOverride,
    postMode,
    containerDims,
  ]);

  useEffect(() => {
    if (videoInfo.hasAudio) {
      audioRef.current = video.current;
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  //unify overall heights to avoid weird sizing issues
  useEffect(() => {
    let h = (video?.current?.clientWidth / vidWidth) * vidHeight;
    if (h > 0) {
      setHeightStyle({
        height: `${h}px`,
      });
    }
  }, [video?.current?.clientWidth, vidHeight, vidWidth]);

  useEffect(() => {
    if (
      ((context?.autoplay && !fullMediaMode) ||
        (context?.autoPlayMode && fullMediaMode && !preventPlay)) &&
      !videoPlaying &&
      (!context.pauseAll || fullMediaMode) &&
      show
    ) {
      video?.current?.play().catch((e) => console.log(e));
    } else if (!context?.autoplay && videoPlaying && !fullMediaMode) {
      video?.current?.pause()?.catch((e) => console.log(e));
    }
  }, [context.autoplay, context.pauseAll, context?.autoPlayMode]);
  useEffect(() => {
    if (context.pauseAll) {
      pauseAll();
    }
  }, [context.pauseAll]);

  //main control for play/pause button
  const playControl = (e?, manual = false) => {
    setShow(true);
    e?.preventDefault();
    e?.stopPropagation();
    if (true) {
      //when video is paused/stopped
      if ((video?.current?.paused || video?.current?.ended) && !videoPlaying) {
        //play the video
        manual && setManualPause(false);
        setBuffering(true);
        video?.current
          ?.play()
          .then(() => {
            //setVideoPlaying(true); this will be handled directly from the video element
            manual && setmanualPlay(true);

            if (hasAudio && !videoInfo.hasAudio) {
              audioRef.current.currentTime = video.current.currentTime;
              audioRef?.current
                ?.play()
                ?.then(() => setAudioPlaying(true))
                .catch((e) => console.log(e));
            }
          })
          .catch((e) => {
            //setVideoPlaying(false); this will be handled directly from the video element
            manual && setmanualPlay(false);
          });
      }
      //pause the video
      else if (!video?.current?.paused && videoPlaying) {
        manual && setManualPause(true);
        setVideoPlaying(false);
        setmanualPlay(false);
        video?.current?.pause();
        if (!audioRef?.current?.paused && !videoInfo.hasAudio) {
          audioRef.current?.pause();
          setAudioPlaying(false);
        }
      }
    }
  };
  //main controls for audio
  const audioControl = (e?, manual = false) => {
    e?.preventDefault();
    e?.stopPropagation();
    //if audio exists
    if (audioRef?.current?.muted !== undefined && hasAudio && !preventPlay) {
      if (!videoInfo.hasAudio) {
        // sync audio
        audioRef.current.currentTime = video.current.currentTime;
        //if audio is not playing and video is playing, play audio
        if (!audioPlaying && videoPlaying) {
          audioRef?.current?.play().catch((e) => console.log(e));
        }
      }

      manual && setManualAudio(true);
      setMuted((m) => !m);
    }
  };

  //for pausing media when another post is opened
  const pauseAll = () => {
    videoLoaded && video?.current?.pause();
    hasAudio && audioRef.current.pause();
  };

  const pauseVideo = () => {
    pauseAudio();
    videoLoaded && video?.current?.pause();
  };

  //pause Audio while video is loading
  const pauseAudio = () => {
    if (hasAudio) {
      audioRef.current?.pause();
    }
  };
  //play Audio..
  const playAudio = () => {
    if (hasAudio) {
      audioRef.current.currentTime = video.current.currentTime;
      audioRef.current.play();
    }
  };

  //play Video..
  const playVideo = () => {
    videoLoaded &&
      video?.current
        ?.play()
        .then(() => playAudio())
        .catch((e) => console.log(e));
    console.log("playing video", video.current.src);
  };

  const handleMouseIn = (e) => {
    setShow(true);
    setMouseIn(true);
    if (context.hoverplay && context.cardStyle !== "row1") {
      if (
        (!manualPlay || video?.current?.paused) &&
        !context.autoplay &&
        !postMode
      ) {
        playControl(e);
      }
    }
    if (!postMode && !manualAudio && context.audioOnHover && !preventPlay) {
      setMuted(false);
    }
  };

  const handleMouseOut = () => {
    setMouseIn(false);
    //not in manual play, then pause audio and video
    if (context.hoverplay && context.cardStyle !== "row1") {
      if (!manualPlay && !context.autoplay && !postMode) {
        // pauseAudio();
        pauseVideo();
      }
    }
    //also mute if not manually unmuted manipulation
    if (
      !manualAudio &&
      !postMode &&
      context.audioOnHover &&
      context.columns !== 1
    ) {
      setMuted(true);
    }
  };

  const [showVol, setShowVol] = useState(false);

  const [seekTime, setSeekTime] = useState("");
  const [seekLeftOfset, setSeekLeftOffset] = useState(0);
  const [seekTargetLength, setSeekTargetLenght] = useState(0);
  const showSeek = (e) => {
    let r = e.nativeEvent.offsetX / e.nativeEvent.target.clientWidth;
    setSeekLeftOffset(e.nativeEvent.offsetX);
    setSeekTargetLenght(e.nativeEvent.target.clientWidth);
    //console.log(secondsToHMS(r * videoDuration));
    setSeekTime(secondsToHMS(r * videoDuration));
  };

  const updateSeek = (e) => {
    e.stopPropagation();
    e.preventDefault();
    //console.log(e, e.nativeEvent.offsetX, e.nativeEvent.target.clientWidth);
    //console.log(video.current);
    let r = e.nativeEvent.offsetX / e.nativeEvent.target.clientWidth;
    //console.log(r, videoDuration, r * videoDuration);
    video.current.currentTime = r * videoDuration;
    if (!videoPlaying) {
      setProgressPerc(video?.current?.currentTime / video?.current?.duration);
    }
  };

  const [volMouseDown, setVolMouseDown] = useState(false);
  const [showVolSlider, setShowVolSlider] = useState(false);
  const updateVolume = (e) => {
    e.stopPropagation();
    e.preventDefault();
    let r = Math.abs(
      1 - e.nativeEvent.offsetY / e.nativeEvent.target.clientHeight
    );
    if (r >= 1) r = 1;
    if (r <= 0.1) r = 0;
    r > 0 ? setMuted(false) : setMuted(true);
    setManualAudio(true);
    setVolume(r);
  };

  const updateVolumeDrag = (e) => {
    if (volMouseDown) {
      updateVolume(e);
    }
  };

  useEffect(() => {
    if (
      (!show || !context?.audioOnHover || left) &&
      (!postMode || fullMediaMode)
    ) {
      setMuted(true);
    } else if (
      context?.audioOnHover &&
      (postMode || context?.columns == 1) &&
      !left
    ) {
      setMuted(false);
    }
  }, [show, context?.audioOnHover, context?.columns, left]);

  useEffect(() => {
    if (audioRef?.current) {
      audioRef.current.muted = muted;
    }
  }, [muted]);

  useEffect(() => {
    if (audioRef?.current) {
      audioRef.current.volume = volume ?? 0.5;
    }
    return () => {
      //
    };
  }, [volume]);

  //smoother progress bar
  const [progressPerc, setProgressPerc] = useState(0);
  const [intervalID, setIntervalID] = useState<any>();
  useEffect(() => {
    if (
      videoPlaying &&
      video?.current &&
      (mouseIn || fullMediaMode) &&
      !preventPlay
    ) {
      let initial = Date.now();
      let timepassed = 0;
      let duration = video?.current?.duration * 1000;
      let initialTime = video?.current?.currentTime * 1000;
      let delta = initialTime / duration;
      let updateSeekRange = () => {
        if (videoPlaying) {
          timepassed = Date.now() - initial;
          delta = (initialTime + timepassed) / duration;
          if (delta > 1) delta = 1;
          setCurrentTime((initialTime + timepassed) / 1000);
          setProgressPerc(delta);
          //handle out of range..
          if (
            (initialTime + timepassed) / 1000 > duration &&
            video?.current?.currentTime >= 0
          ) {
            video.current.currentTime = 0;
          }
        }
      };

      setIntervalID((id) => {
        clearInterval(id);
        return setInterval(updateSeekRange, 10);
      });
    } else {
      clearInterval(intervalID);
    }
    return () => {};
  }, [videoPlaying, mouseIn, fullMediaMode, preventPlay]);

  const setData = useRef<any>();
  useEffect(() => {
    //console.log(progressPerc, context?.autoPlayMode);
    if (
      progressPerc >= 0.9 &&
      fullMediaMode &&
      !setData.current &&
      !preventPlay
    ) {
      //console.log("ended!", progressPerc);
      setData.current = true;
      setTimeout(
        () => {
          //console.log('set', name)
          setGlobalData(name, true);
        },
        videoDuration > 0 ? videoDuration * 0.1 * 1000 : 100
      );
    } else if (progressPerc < 0.9) {
      setData.current = false;
    }
  }, [progressPerc, fullMediaMode, name, videoDuration, preventPlay]);

  const kPress = useKeyPress("k");
  const mPress = useKeyPress("m");

  useEffect(() => {
    if (focused && !context?.replyFocus && !preventPlay) {
      kPress && playControl();
      mPress && audioControl();
    }

    return () => {};
  }, [kPress, mPress, context.replyFocus, focused]);

  const timeoutRef = useRef<any>(null);

  return (
    <div
      className={
        " min-w-full group hover:cursor-pointer overflow-hidden relative" +
        (uniformMediaMode
          ? "object-cover object-center aspect-[9/16] "
          : " flex items-center justify-center")
      }
      onClick={(e) => {
        if (fullMediaMode) {
          if (timeoutRef.current === null) {
            timeoutRef.current = setTimeout(() => {
              playControl(e, true);
              timeoutRef.current = null;
            }, 300);
          }
        } else if (!uniformMediaMode) {
          playControl(e, true);
        }
      }}
      onDoubleClick={(e) => {
        if (fullMediaMode) {
          clearTimeout(timeoutRef.current);
          timeoutRef.current = null;
        }
      }}
      onMouseEnter={(e) => {
        setFocused(true);
        handleMouseIn(e);
      }}
      onMouseLeave={(e) => {
        !postMode && setFocused(false);
        handleMouseOut();
      }}
      ref={ref}
      style={
        containerDims?.[1]
          ? { height: `${containerDims[1]}px` }
          : uniformMediaMode
          ? { minHeight: "100%" }
          : heightStyle
      }
    >
      {((!videoLoaded && (context?.autoPlay || postMode)) || buffering) && (
        <div className="absolute z-10 text-white -translate-x-1/2 -translate-y-1/2 top-1/2 left-1/2">
          <ImSpinner2 className="w-8 h-8 animate-spin" />
        </div>
      )}
      {/* Background Span Image */}
      <div
        className="absolute z-0 min-w-full min-h-full overflow-hidden brightness-[0.2]"
        ref={fullWidthRef}
      >
        <Image
          className={"scale-110 blur-md "}
          src={thumbnail.url}
          alt=""
          layout="fill"
          unoptimized={true}
          priority={imgFull}
          onError={() => {
            setUseFallback(true);
          }}
        />
      </div>
      {/* Controls */}
      <div className="absolute bottom-0 z-10 flex flex-row min-w-full p-1 pb-2 text-white">
        <div
          className={
            (fullMediaMode ? "hidden md:flex" : "flex") +
            " items-center space-x-2"
          }
        >
          <button
            aria-label="play"
            onClick={(e) => {
              playControl(e, true);
            }}
            className={
              (uniformMediaMode ? (mouseIn ? " block  " : " hidden ") : "") +
              (context?.autoplay
                ? `${mouseIn ? " flex " : " hidden "}`
                : " flex ") +
              "items-center justify-center w-8 h-8  bg-black rounded-md bg-opacity-20 hover:bg-opacity-40  "
            }
          >
            <div className="">
              {videoPlaying ? (
                <BiPause className="flex-none w-6 h-6 " />
              ) : (
                <BiPlay className="flex-none w-6 h-6 ml-0.5" />
              )}
            </div>
          </button>
          <div className={" text-sm " + (mouseIn ? " block  " : " hidden ")}>
            {secondsToHMS(currentTime) + "/" + secondsToHMS(videoDuration)}
          </div>
        </div>
        {hasAudio && (
          // vol positioner
          <div
            className="relative ml-auto"
            onMouseEnter={() => setShowVolSlider(true)}
            onMouseLeave={() => setShowVolSlider(false)}
            onClick={(e) => {
              e.preventDefault();
              e.stopPropagation();
            }}
          >
            <div
              //vol container
              className="absolute bottom-0 left-0 w-full h-[170px] bg-transparent"
              onClick={(e) => {
                e.preventDefault();
                e.stopPropagation();
              }}
            >
              <div
                //slider container
                onClick={(e) => {
                  e.preventDefault();
                  e.stopPropagation();
                }}
                onMouseDown={(e) => setVolMouseDown(true)}
                onMouseUp={(e) => setVolMouseDown(false)}
                onMouseLeave={() => setVolMouseDown(false)}
                className={
                  "relative bottom-0 left-0  justify-center w-full h-32 bg-black bg-opacity-40 rounded-md " +
                  (showVolSlider ? " hidden md:flex " : " hidden ")
                }
              >
                <div
                  //slide range
                  className="absolute bottom-0 flex justify-center w-full mb-2 rounded-full h-28"
                  onClick={(e) => {
                    e.preventDefault();
                    e.stopPropagation();
                  }}
                >
                  <div
                    //range controls
                    className="absolute z-30 w-full h-full"
                    ref={volControls}
                    onClick={(e) => updateVolume(e)}
                    onMouseMove={(e) => {
                      updateVolumeDrag(e);
                    }}
                  ></div>
                  <div className="absolute bottom-0 w-2 h-full">
                    <div
                      //control circle
                      className="absolute z-20 w-4 h-4 bg-white border rounded-full -left-1 "
                      style={
                        muted || volume === 0
                          ? { bottom: 0 }
                          : {
                              bottom: `${(volume ?? 0.5) * 100 - 10}%`,
                            }
                      }
                      onMouseDown={() => setVolMouseDown(true)}
                      onMouseUp={() => setVolMouseDown(false)}
                    ></div>
                    <div
                      //vol container
                      className="absolute bottom-0 z-10 w-full origin-bottom rounded-full bg-th-scrollbar"
                      style={{
                        height: `${muted ? 0 : (volume ?? 0.5) * 100}%`,
                      }}
                    ></div>
                    <div className="absolute bottom-0 z-0 w-full h-full rounded-full bg-white/10"></div>
                  </div>
                </div>
              </div>
            </div>
            <button
              aria-label="toggle mute"
              //mute unmute button
              onClick={(e) => audioControl(e, true)}
              onMouseEnter={() => setShowVol(true)}
              onMouseLeave={() => setShowVol(false)}
              className={
                (fullMediaMode || uniformMediaMode
                  ? " hidden md:flex"
                  : "  flex ") +
                " w-8 h-8 relative  items-center justify-center ml-auto bg-black rounded-md bg-opacity-20 hover:bg-opacity-40"
              }
            >
              {muted ? (
                <BiVolumeMute className="flex-none w-4 h-4" />
              ) : (
                <BiVolumeFull className="flex-none w-4 h-4 " />
              )}
            </button>
          </div>
        )}
        <div
          // Progress Bar Container

          id={"progressBarConainer"}
          ref={seekRef}
          className={
            "absolute bottom-0 left-0 z-10  w-full h-5  " +
            (mouseIn || fullMediaMode ? " block " : " hidden ")
          }
        >
          {seekTime !== "" && (
            <div
              className="absolute flex items-center justify-center w-12 py-1 text-sm transition-transform bg-black rounded-lg bg-opacity-40 bottom-4 border-th-border"
              style={{
                left: `${
                  seekLeftOfset <= 24
                    ? 0
                    : seekLeftOfset >= seekTargetLength - 24
                    ? seekTargetLength - 48
                    : seekLeftOfset - 24
                }px`,
              }}
            >
              {seekTime}
            </div>
          )}
          <div
            //video duration
            className="absolute bottom-0 left-0 h-1 origin-left bg-th-scrollbar "
            style={{ width: `${progressPerc * 100}%` }}
          ></div>
          <div
            className="absolute left-0 w-full h-full "
            onMouseMove={(e) => showSeek(e)}
            onMouseLeave={() => setSeekTime("")}
            onClick={(e: any) => {
              updateSeek(e);
            }}
          ></div>
        </div>
      </div>

      {/* Video */}
      <div
        className={
          uniformMediaMode
            ? "min-h-full min-w-full object-cover object-center "
            : "relative overflow-hidden flex  object-fill"
        }
        style={
          uniformMediaMode
            ? { height: `100%` }
            : containerDims?.[1]
            ? { height: `${containerDims[1]}px` }
            : heightStyle
        }
      >
        <div
          //high res placeholder image
          className={
            ` ` +
            `${
              !videoLoaded
                ? " opacity-100 "
                : containerDims?.[1]
                ? " hidden "
                : " opacity-0 "
            }` +
            (!videoLoaded && containerDims?.[1]
              ? " absolute top-1/2 -translate-y-1/2 "
              : "")
          }
        >
          <Image
            className={
              (!containerDims?.[1] ? "absolute bottom-0 left-0" : " ") +
              (placeholder?.url?.includes("http") ? " " : " blur-2xl ")
            } //!postMode ?
            src={
              placeholder?.url?.includes("http")
                ? placeholder.url
                : thumbnail.url
            }
            height={vidHeight}
            width={vidWidth}
            layout={uniformMediaMode ? "fill" : undefined}
            objectFit={uniformMediaMode ? "cover" : undefined}
            alt="placeholder"
            unoptimized={true}
            priority={imgFull}
            onError={() => {
              setUseFallback(true);
            }}
            draggable={false}
          />
        </div>

        <video
          ref={video}
          className={
            (videoLoaded ? "opacity-100 " : " opacity-0") +
            (!containerDims?.[1] ? " absolute top-0 left-0 " : "  ") +
            (uniformMediaMode
              ? " min-w-full min-h-full max-w-full max-h-full m-auto block absolute inset-0 w-0 h-0 "
              : "absolute left-1/2 transform -translate-x-1/2")
          }
          width={`${vidWidth}`}
          height={`${vidHeight}`}
          muted
          loop={true}
          preload={context?.autoplay || postMode ? "auto" : "none"}
          onWaiting={() => {
            if (!muted) setPrevMuted(false);
            //pauseAll();
            setBuffering(true);
            setVideoPlaying(false);
            !vodInfo.hasAudio && pauseAudio();
          }}
          onPlaying={() => {
            setBuffering(false);
            setVideoPlaying(true);
            !vodInfo.hasAudio && playAudio();
          }}
          onPause={() => {
            setVideoPlaying(false);
          }}
          controls={false}
          // onLoadedData={onLoadedData}
          onProgress={onLoadedData}
          playsInline
          onCanPlay={() => {
            vodInfo.hasAudio && onAudioLoaded();
          }}
          draggable={false}
          
        >
          <source data-src={vodInfo.url} src={vodInfo.url} type="video/mp4" />
        </video>
        {/* if video doesn't have its own audio (v.reddits) */}
        {!videoInfo?.hasAudio && audio && (
          <video
            controls={false}
            ref={!videoInfo?.hasAudio && audioRef}
            muted={!muted}
            loop={false}
            className="hidden"
            playsInline
            onCanPlay={onAudioLoaded}
            onPlaying={() => setAudioPlaying(true)}
            onPause={() => setAudioPlaying(false)}
            onWaiting={() => setAudioPlaying(false)}
            onError={(err) => null}
          
          >
            <source data-src={audio} src={audio} type="video/mp4" />
          </video>
        )}
      </div>
    </div>
  );
};

export default VideoHandler;

Thanks for the suggestion. This works but introduces other issues (videos autoplay when autoplay is not toggled on).
I'm going to be rewriting this component to support a HLS player for better cross browser support and do away with the current 'hack' for reddit audio support since this does not work well on iOS either.