59 lines · 2.3 KB
import { useQuery } from "@tanstack/react-query";
import { useEffect, useState } from "react";
import { SCRUB_MANIFEST, scrubManifestSchema, videoUrl, type ScrubManifest } from "~/lib/video";

/** Attaches the HLS master to `video`: hls.js where MSE exists, native HLS (Safari/iOS) otherwise. */
export function useHlsSource(video: HTMLVideoElement | null, src: string): string | null {
  const [error, setError] = useState<string | null>(null);
  useEffect(() => {
    if (!video) return;
    let disposed = false;
    let destroy: (() => void) | undefined;
    setError(null);
    void import("hls.js").then(({ default: Hls }) => {
      if (disposed) return;
      if (!Hls.isSupported()) {
        if (video.canPlayType("application/vnd.apple.mpegurl")) video.src = src;
        else setError("This browser cannot play HLS video");
        return;
      }
      const hls = new Hls({ capLevelToPlayerSize: true, startLevel: -1 });
      hls.on(Hls.Events.ERROR, (_event, data) => {
        if (!data.fatal) return;
        if (data.type === Hls.ErrorTypes.NETWORK_ERROR) hls.startLoad();
        else if (data.type === Hls.ErrorTypes.MEDIA_ERROR) hls.recoverMediaError();
        else setError("Video playback failed");
      });
      hls.loadSource(src);
      hls.attachMedia(video);
      destroy = () => hls.destroy();
    });
    return () => {
      disposed = true;
      destroy?.();
    };
  }, [video, src]);
  return error;
}

/** Loads the scrub sprite manifest; a video without one still plays, it just scrubs without thumbnails. */
export function useScrubManifest(id: string, version: string): ScrubManifest | null {