File size: 3,794 Bytes
ff324d9
 
 
 
09093e0
ff324d9
 
 
09093e0
 
 
ff324d9
 
 
09093e0
 
 
 
ff324d9
 
 
09093e0
ff324d9
 
 
 
 
 
09093e0
ff324d9
 
 
 
 
 
 
 
 
 
09093e0
 
 
 
 
 
 
 
 
 
 
ff324d9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
09093e0
ff324d9
 
 
 
 
09093e0
ff324d9
 
09093e0
ff324d9
 
 
 
 
09093e0
 
ff324d9
 
 
 
 
 
 
 
 
 
 
 
 
09093e0
ff324d9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
09093e0
 
ff324d9
 
09093e0
ff324d9
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
import { useState, useEffect, useRef, useCallback, useMemo } from "react";
import LoadingScreen from "./components/LoadingScreen";
import CaptioningView from "./components/CaptioningView";
import WelcomeScreen from "./components/WelcomeScreen";
import InputSourceDialog from "./components/InputSourceDialog";
import type { AppState } from "./types";

export default function App() {
  const [appState, setAppState] = useState<AppState>("welcome");
  const [mediaStream, setMediaStream] = useState<MediaStream | null>(null);
  const [sourceType, setSourceType] = useState<'webcam' | 'screen' | 'file' | null>(null);
  const [isVideoReady, setIsVideoReady] = useState(false);
  const videoRef = useRef<HTMLVideoElement | null>(null);

  const handleSourceSelected = useCallback((stream: MediaStream, type: 'webcam' | 'screen' | 'file') => {
    setMediaStream(stream);
    setSourceType(type);
    setAppState("loading");
  }, []);

  const handleStart = useCallback(() => {
    setAppState("source-selection");
  }, []);

  const handleLoadingComplete = useCallback(() => {
    setAppState("captioning");
  }, []);


  const playVideo = useCallback(async (video: HTMLVideoElement) => {
    try {
      await video.play();
    } catch (error) {
      console.error("Failed to play video:", error);
    }
  }, []);

  const setupVideo = useCallback(
    (video: HTMLVideoElement, stream: MediaStream) => {
      // Check if this is a video file (mock stream with videoFileUrl)
      const videoFileUrl = (stream as any).videoFileUrl;
      
      if (videoFileUrl) {
        // For video files, use the file URL directly
        video.src = videoFileUrl;
        video.srcObject = null;
      } else {
        // For webcam/screen, use the stream
        video.srcObject = stream;
      }

      const handleCanPlay = () => {
        setIsVideoReady(true);
        playVideo(video);
      };

      video.addEventListener("canplay", handleCanPlay, { once: true });

      return () => {
        video.removeEventListener("canplay", handleCanPlay);
      };
    },
    [playVideo],
  );

  useEffect(() => {
    if (mediaStream && videoRef.current) {
      const video = videoRef.current;

      video.srcObject = null;
      video.load();

      const cleanup = setupVideo(video, mediaStream);
      return cleanup;
    }
  }, [mediaStream, setupVideo]);

  const videoBlurState = useMemo(() => {
    switch (appState) {
      case "welcome":
        return "blur(12px) brightness(0.3) saturate(0.7)";
      case "source-selection":
        return "blur(20px) brightness(0.2) saturate(0.5)";
      case "loading":
        return "blur(8px) brightness(0.4) saturate(0.8)";
      case "captioning":
        return "none";
      default:
        return "blur(20px) brightness(0.2) saturate(0.5)";
    }
  }, [appState]);

  return (
    <div className="App relative h-screen overflow-hidden">
      <div className="absolute inset-0 bg-gray-900" />

      {mediaStream && (
        <video
          ref={videoRef}
          autoPlay
          muted
          playsInline
          className="absolute inset-0 w-full h-full object-cover transition-all duration-1000 ease-out"
          style={{
            filter: videoBlurState,
            opacity: isVideoReady ? 1 : 0,
          }}
        />
      )}

      {appState !== "captioning" && <div className="absolute inset-0 bg-gray-900/80 backdrop-blur-sm" />}

      {appState === "welcome" && <WelcomeScreen onStart={handleStart} />}

      {appState === "source-selection" && <InputSourceDialog onSourceSelected={handleSourceSelected} />}

      {appState === "loading" && <LoadingScreen onComplete={handleLoadingComplete} />}

      {appState === "captioning" && <CaptioningView videoRef={videoRef} sourceType={sourceType} />}
    </div>
  );
}