components/IntroLoopVideo.jsxjavascript
import React, { useState } from "react";
import PropTypes from "prop-types";
import { CacheableVideo } from "./CacheableVideo";

/**
 * Plays `intro` once, then switches to `loop` playing forever. Both videos
 * are mounted from the start - the loop is fetched, decoded and paused at
 * its first frame in the background while the intro plays, so the switch is
 * just an opacity swap instead of a `src` change that would force a fresh
 * fetch/decode at the exact moment of the handoff (visible as a stutter).
 *
 * @param {string} intro - URL of the video played once at the start.
 * @param {string} loop - URL of the video played on loop afterwards.
 * @param {...*} props - Passed through to both underlying `<video>` elements.
 */
export const IntroLoopVideo = ({ intro, loop, ...props }) => {
  const [looping, setLooping] = useState(false);

  return (
    <>
      <CacheableVideo
        src={intro}
        autoPlay
        fallback={null}
        onEnded={() => setLooping(true)}
        {...props}
        style={{ opacity: looping ? 0 : 1 }}
      />
      <CacheableVideo
        src={loop}
        loop
        playing={looping}
        fallback={null}
        {...props}
        style={{ opacity: looping ? 1 : 0 }}
      />
    </>
  );
};

IntroLoopVideo.propTypes = {
  intro: PropTypes.string.isRequired,
  loop: PropTypes.string.isRequired,
};