T.ME/BIBIL_0DAY
CasperSecurity


Server : Apache/2
System : Linux server-15-235-50-60 5.15.0-164-generic #174-Ubuntu SMP Fri Nov 14 20:25:16 UTC 2025 x86_64
User : gositeme ( 1004)
PHP Version : 8.2.29
Disable Function : exec,system,passthru,shell_exec,proc_close,proc_open,dl,popen,show_source,posix_kill,posix_mkfifo,posix_getpwuid,posix_setpgid,posix_setsid,posix_setuid,posix_setgid,posix_seteuid,posix_setegid,posix_uname
Directory :  /home/gositeme/domains/lavocat.ca/private_html/src/components/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Current File : /home/gositeme/domains/lavocat.ca/private_html/src/components/SimpleVideoCall.tsx
import React, { useRef, useEffect, useState } from 'react';
import { useWebSocket } from '../context/StableWebSocketContext';
import { motion, AnimatePresence } from 'framer-motion';
import Peer from 'simple-peer';

const SimpleVideoCall: React.FC = () => {
  const { videoCallActive, currentVideoCall, endVideoCall, ws } = useWebSocket();
  const localVideoRef = useRef<HTMLVideoElement>(null);
  const remoteVideoRef = useRef<HTMLVideoElement>(null);
  const peerRef = useRef<Peer.Instance | null>(null);
  
  const [localStream, setLocalStream] = useState<MediaStream | null>(null);
  const [remoteStream, setRemoteStream] = useState<MediaStream | null>(null);
  const [connectionStatus, setConnectionStatus] = useState<'connecting' | 'connected' | 'failed'>('connecting');
  const [isLocalMuted, setIsLocalMuted] = useState(false);
  const [isLocalVideoOff, setIsLocalVideoOff] = useState(false);
  const [callDuration, setCallDuration] = useState(0);
  const [callStartTime] = useState<number>(Date.now());

  // ✅ Update call timer every second
  useEffect(() => {
    if (!videoCallActive) return;
    
    const timer = setInterval(() => {
      setCallDuration(Math.floor((Date.now() - callStartTime) / 1000));
    }, 1000);

    return () => clearInterval(timer);
  }, [videoCallActive, callStartTime]);

  // ✅ Format call duration (mm:ss)
  const formatCallDuration = (seconds: number): string => {
    const mins = Math.floor(seconds / 60);
    const secs = seconds % 60;
    return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
  };

  // ✅ Get media stream when call starts
  useEffect(() => {
    if (videoCallActive) {
      console.log('📞 [SIMPLE] Starting media capture');
      startMediaCapture();
    } else {
      console.log('📞 [SIMPLE] Stopping media capture');
      stopMediaCapture();
    }
  }, [videoCallActive]);

  // ✅ Setup WebRTC peer connection when we have a stream
  useEffect(() => {
    if (localStream && currentVideoCall && !peerRef.current) {
      console.log('📞 [SIMPLE] Setting up WebRTC peer connection');
      setupPeerConnection();
    }
  }, [localStream, currentVideoCall]);

  // ✅ WebSocket message handling for WebRTC signaling
  useEffect(() => {
    if (!ws) return;

    const handleMessage = (event: MessageEvent) => {
      try {
        const message = JSON.parse(event.data);
        
        if (message.senderId === currentVideoCall?.recipientId) {
          switch (message.type) {
            case 'webrtc-offer':
            case 'webrtc-answer':
              if (peerRef.current && message.data.signal) {
                console.log('📞 [SIMPLE] Received signaling data:', message.type);
                peerRef.current.signal(message.data.signal);
              }
              break;
            case 'webrtc-ice-candidate':
              if (peerRef.current && message.data.candidate) {
                console.log('📞 [SIMPLE] Received ICE candidate');
                peerRef.current.signal(message.data.candidate);
              }
              break;
            case 'webrtc-end-call':
              console.log('📞 [SIMPLE] Remote user ended call');
              handleEndCall();
              break;
          }
        }
      } catch (error) {
        console.error('📞 [SIMPLE] Error handling WebSocket message:', error);
      }
    };

    ws.addEventListener('message', handleMessage);
    return () => ws.removeEventListener('message', handleMessage);
  }, [ws, currentVideoCall?.recipientId]);

  const startMediaCapture = async () => {
    try {
      console.log('📞 [SIMPLE] Requesting camera and microphone...');
      const stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
      console.log('📞 [SIMPLE] Got media stream:', stream);
      setLocalStream(stream);
      
      if (localVideoRef.current) {
        localVideoRef.current.srcObject = stream;
      }
    } catch (error) {
      console.error('📞 [SIMPLE] Failed to get media:', error);
      // Create mock stream for development
      const canvas = document.createElement('canvas');
      canvas.width = 640;
      canvas.height = 480;
      const ctx = canvas.getContext('2d');
      
      let frame = 0;
      const animate = () => {
        if (ctx) {
          ctx.fillStyle = '#1f2937';
          ctx.fillRect(0, 0, canvas.width, canvas.height);
          
          const x = canvas.width / 2 + Math.sin(frame * 0.05) * 100;
          const y = canvas.height / 2 + Math.cos(frame * 0.03) * 50;
          ctx.fillStyle = '#3b82f6';
          ctx.beginPath();
          ctx.arc(x, y, 30, 0, 2 * Math.PI);
          ctx.fill();
          
          ctx.fillStyle = '#ffffff';
          ctx.font = '20px Arial';
          ctx.textAlign = 'center';
          ctx.fillText('🎭 DEV MODE - Mock Video', canvas.width / 2, canvas.height / 2 + 100);
          
          frame++;
        }
        requestAnimationFrame(animate);
      };
      animate();
      
      const mockStream = canvas.captureStream(30);
      setLocalStream(mockStream);
      if (localVideoRef.current) {
        localVideoRef.current.srcObject = mockStream;
      }
    }
  };

  const stopMediaCapture = () => {
    if (localStream) {
      localStream.getTracks().forEach(track => track.stop());
      setLocalStream(null);
    }
    if (remoteStream) {
      setRemoteStream(null);
    }
    if (peerRef.current) {
      peerRef.current.destroy();
      peerRef.current = null;
    }
  };

  const toggleMute = () => {
    if (localStream) {
      const audioTracks = localStream.getAudioTracks();
      audioTracks.forEach(track => {
        track.enabled = isLocalMuted;
      });
      setIsLocalMuted(!isLocalMuted);
    }
  };

  const toggleVideo = () => {
    if (localStream) {
      const videoTracks = localStream.getVideoTracks();
      videoTracks.forEach(track => {
        track.enabled = isLocalVideoOff;
      });
      setIsLocalVideoOff(!isLocalVideoOff);
    }
  };

  const handleEndCall = () => {
    console.log('📞 [SIMPLE] Ending call');
    
    // Send end call signal to remote user
    if (ws?.readyState === WebSocket.OPEN && currentVideoCall) {
      ws.send(JSON.stringify({
        type: 'webrtc-end-call',
        data: {},
        recipientId: currentVideoCall.recipientId
      }));
    }
    
    stopMediaCapture();
    endVideoCall();
  };

  const setupPeerConnection = () => {
    if (!localStream || !currentVideoCall) return;

    const peer = new Peer({
      initiator: currentVideoCall.isInitiator,
      trickle: true,
      stream: localStream,
    });

    peer.on('signal', (signal) => {
      console.log('📞 [SIMPLE] Sending WebRTC signal');
      const messageType = currentVideoCall.isInitiator ? 'webrtc-offer' : 'webrtc-answer';
      
      if (ws?.readyState === WebSocket.OPEN) {
        ws.send(JSON.stringify({
          type: messageType,
          data: { signal },
          senderId: 'current-user', // Will be set by server
          recipientId: currentVideoCall.recipientId
        }));
      }
    });

    peer.on('stream', (stream) => {
      console.log('📞 [SIMPLE] Received remote stream');
      setRemoteStream(stream);
      setConnectionStatus('connected');
      
      if (remoteVideoRef.current) {
        remoteVideoRef.current.srcObject = stream;
      }
    });

    peer.on('connect', () => {
      console.log('📞 [SIMPLE] Peer connected');
      setConnectionStatus('connected');
    });

    peer.on('error', (error) => {
      console.error('📞 [SIMPLE] Peer error:', error);
      setConnectionStatus('failed');
    });

    peerRef.current = peer;
  };

  if (!videoCallActive || !currentVideoCall) return null;

  return (
    <AnimatePresence>
      <motion.div
        initial={{ opacity: 0 }}
        animate={{ opacity: 1 }}
        exit={{ opacity: 0 }}
        className="fixed inset-0 bg-gray-900 flex flex-col z-[9999]"
      >
        {/* Header */}
        <div className="bg-gray-800 text-white p-4 flex items-center justify-between">
          <div>
            <h2 className="text-lg font-semibold flex items-center gap-2">
              🎥 Video Call with {currentVideoCall.recipientName}
            </h2>
            <div className="flex items-center gap-4">
              <div className="flex items-center gap-2">
                <div className={`w-2 h-2 rounded-full ${
                  connectionStatus === 'connected' ? 'bg-green-500 animate-pulse' :
                  connectionStatus === 'connecting' ? 'bg-yellow-500 animate-pulse' :
                  'bg-red-500'
                }`}></div>
                <p className="text-sm text-gray-300">
                  {connectionStatus === 'connected' ? 'Live' :
                   connectionStatus === 'connecting' ? 'Connecting' :
                   'Failed'}
                </p>
              </div>
              
              <div className="flex items-center gap-2 bg-gray-700 px-3 py-1 rounded-lg">
                <span className="text-sm font-mono text-white">
                  {formatCallDuration(callDuration)}
                </span>
              </div>
            </div>
          </div>
          
          <button
            onClick={handleEndCall}
            className="bg-red-600 hover:bg-red-700 text-white px-6 py-2 rounded-lg transition-all duration-200 flex items-center gap-2 hover:scale-105 transform"
          >
            End Call
          </button>
        </div>

        {/* Video Area */}
        <div className="flex-1 relative bg-black">
          {/* Remote Video */}
          {remoteStream ? (
            <video
              ref={remoteVideoRef}
              autoPlay
              playsInline
              className="w-full h-full object-cover"
            />
          ) : (
            <div className="w-full h-full flex items-center justify-center text-white">
              <div className="text-center">
                {connectionStatus === 'connecting' && (
                  <>
                    <div className="w-16 h-16 border-4 border-t-transparent border-blue-500 rounded-full animate-spin mx-auto mb-4"></div>
                    <p className="text-xl mb-2">🔄 Connecting to {currentVideoCall.recipientName}...</p>
                    <p className="text-sm text-gray-400">Setting up video connection</p>
                  </>
                )}
                {connectionStatus === 'failed' && (
                  <>
                    <div className="text-red-500 text-6xl mb-4">❌</div>
                    <p className="text-xl mb-2">Connection Failed</p>
                    <p className="text-sm text-gray-400">Unable to connect to {currentVideoCall.recipientName}</p>
                  </>
                )}
                {connectionStatus === 'connected' && !remoteStream && (
                  <>
                    <div className="text-green-500 text-6xl mb-4">📞</div>
                    <p className="text-xl mb-2">Connected!</p>
                    <p className="text-sm text-gray-400">Waiting for {currentVideoCall.recipientName}'s video...</p>
                  </>
                )}
                <div className="mt-4 text-xs text-gray-500">
                  Call duration: {formatCallDuration(callDuration)}
                </div>
              </div>
            </div>
          )}

          {/* Local Video */}
          {localStream && (
            <div className="absolute top-4 right-4 w-48 h-36 bg-gray-800 rounded-lg overflow-hidden border-2 border-gray-600 shadow-2xl">
              {isLocalVideoOff ? (
                <div className="w-full h-full flex items-center justify-center bg-gray-700">
                  <span className="text-gray-400">📹</span>
                </div>
              ) : (
                <video
                  ref={localVideoRef}
                  autoPlay
                  playsInline
                  muted
                  className="w-full h-full object-cover"
                />
              )}
              <div className="absolute bottom-2 left-2 text-xs text-white bg-black bg-opacity-75 px-2 py-1 rounded">
                📹 You
              </div>
              {isLocalMuted && (
                <div className="absolute top-2 right-2 bg-red-600 rounded-full p-1 text-xs">
                  🔇
                </div>
              )}
            </div>
          )}
        </div>

        {/* Controls */}
        <div className="bg-gray-800 p-6 flex items-center justify-center space-x-6">
          <button
            onClick={toggleMute}
            className={`w-14 h-14 rounded-full flex items-center justify-center transition-all duration-200 transform hover:scale-110 ${
              isLocalMuted 
                ? 'bg-red-600 hover:bg-red-700 ring-2 ring-red-400' 
                : 'bg-gray-600 hover:bg-gray-700'
            }`}
            title={isLocalMuted ? 'Unmute' : 'Mute'}
          >
            {isLocalMuted ? '🔇' : '🎤'}
          </button>

          <button
            onClick={toggleVideo}
            className={`w-14 h-14 rounded-full flex items-center justify-center transition-all duration-200 transform hover:scale-110 ${
              isLocalVideoOff 
                ? 'bg-red-600 hover:bg-red-700 ring-2 ring-red-400' 
                : 'bg-gray-600 hover:bg-gray-700'
            }`}
            title={isLocalVideoOff ? 'Turn on camera' : 'Turn off camera'}
          >
            {isLocalVideoOff ? '📷' : '📹'}
          </button>

          <button
            onClick={handleEndCall}
            className="w-16 h-16 rounded-full bg-red-600 hover:bg-red-700 flex items-center justify-center transition-all duration-200 transform hover:scale-110 ring-2 ring-red-400 text-2xl"
            title="End call"
          >
            📞
          </button>
        </div>
      </motion.div>
    </AnimatePresence>
  );
};

export default SimpleVideoCall; 

CasperSecurity Mini