import { useState, useEffect, useCallback, useMemo } from "react"; import GlassContainer from "./GlassContainer"; import GlassButton from "./GlassButton"; import { GLASS_EFFECTS } from "../constants"; const ERROR_TYPES = { HTTPS: "https", NOT_SUPPORTED: "not-supported", PERMISSION: "permission", GENERAL: "general", } as const; const VIDEO_CONSTRAINTS = { video: { width: { ideal: 1920, max: 1920 }, height: { ideal: 1080, max: 1080 }, facingMode: "user", }, }; const SCREEN_CONSTRAINTS = { video: { width: { ideal: 1920, max: 1920 }, height: { ideal: 1080, max: 1080 }, }, audio: false, }; interface ErrorInfo { type: (typeof ERROR_TYPES)[keyof typeof ERROR_TYPES]; message: string; } interface InputSourceDialogProps { onSourceSelected: (stream: MediaStream, sourceType: 'webcam' | 'screen' | 'file') => void; } type InputSource = 'webcam' | 'screen' | 'file'; export default function InputSourceDialog({ onSourceSelected }: InputSourceDialogProps) { const [selectedSource, setSelectedSource] = useState(null); const [isRequesting, setIsRequesting] = useState(false); const [error, setError] = useState(null); const getErrorInfo = (err: unknown): ErrorInfo => { if (!navigator.mediaDevices) { return { type: ERROR_TYPES.HTTPS, message: "Media access requires a secure connection (HTTPS)", }; } if (err instanceof DOMException) { switch (err.name) { case "NotAllowedError": return { type: ERROR_TYPES.PERMISSION, message: "Media access denied", }; case "NotFoundError": return { type: ERROR_TYPES.GENERAL, message: "No camera found", }; case "NotReadableError": return { type: ERROR_TYPES.GENERAL, message: "Camera is in use by another application", }; case "OverconstrainedError": return { type: ERROR_TYPES.GENERAL, message: "Camera doesn't meet requirements", }; case "SecurityError": return { type: ERROR_TYPES.HTTPS, message: "Security error accessing media", }; default: return { type: ERROR_TYPES.GENERAL, message: `Media error: ${err.name}`, }; } } return { type: ERROR_TYPES.GENERAL, message: "Failed to access media", }; }; const requestWebcamAccess = useCallback(async () => { setIsRequesting(true); setError(null); try { if (!navigator.mediaDevices?.getUserMedia) { throw new Error("NOT_SUPPORTED"); } const stream = await navigator.mediaDevices.getUserMedia(VIDEO_CONSTRAINTS); onSourceSelected(stream, 'webcam'); } catch (err) { const errorInfo = getErrorInfo(err); setError(errorInfo); console.error("Error accessing webcam:", err, errorInfo); } finally { setIsRequesting(false); } }, [onSourceSelected]); const requestScreenAccess = useCallback(async () => { setIsRequesting(true); setError(null); try { if (!navigator.mediaDevices?.getDisplayMedia) { throw new Error("Screen sharing not supported"); } const stream = await navigator.mediaDevices.getDisplayMedia(SCREEN_CONSTRAINTS); onSourceSelected(stream, 'screen'); } catch (err) { const errorInfo = getErrorInfo(err); setError(errorInfo); console.error("Error accessing screen:", err, errorInfo); } finally { setIsRequesting(false); } }, [onSourceSelected]); const handleFileSelect = useCallback((event: React.ChangeEvent) => { const file = event.target.files?.[0]; if (!file) return; // Create a video element that will be used directly instead of canvas stream const videoUrl = URL.createObjectURL(file); // Create a mock stream that signals this is a file source const canvas = document.createElement('canvas'); canvas.width = 1; canvas.height = 1; const mockStream = canvas.captureStream(1); // Store the video file URL on the stream for later use (mockStream as any).videoFileUrl = videoUrl; onSourceSelected(mockStream, 'file'); }, [onSourceSelected]); const renderIcon = (source: InputSource) => { const iconClass = "w-8 h-8"; switch (source) { case 'webcam': return ( ); case 'screen': return ( ); case 'file': return ( ); } }; if (selectedSource && isRequesting) { return (

{selectedSource === 'webcam' ? 'Requesting Camera Access' : selectedSource === 'screen' ? 'Requesting Screen Access' : 'Loading Video File'}

Please allow access in your browser to continue...

); } return (

Choose Input Source

Select how you want to provide video for captioning

{/* Webcam Option */} { setSelectedSource('webcam'); requestWebcamAccess(); }} >
{renderIcon('webcam')}

Webcam

Use your camera for live captioning

{/* Screen Recording Option */} { setSelectedSource('screen'); requestScreenAccess(); }} >
{renderIcon('screen')}

Screen

Record and caption your screen

{/* Video File Option */}
{/* Error Display */} {error && (

Access Failed

{error.message}

{ setError(null); setSelectedSource(null); }} className="px-6 py-3" > Try Different Source
)}
); }