| <!DOCTYPE html> |
| <html lang="fr"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>LiveConnect - Plateforme de vidéo conférence</title> |
| <script src="https://cdn.tailwindcss.com"></script> |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> |
| <style> |
| .video-container { |
| position: relative; |
| padding-bottom: 56.25%; |
| height: 0; |
| overflow: hidden; |
| } |
| .video-container video { |
| position: absolute; |
| top: 0; |
| left: 0; |
| width: 100%; |
| height: 100%; |
| object-fit: cover; |
| } |
| .participant-grid { |
| display: grid; |
| grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); |
| gap: 15px; |
| } |
| .participant { |
| position: relative; |
| border-radius: 10px; |
| overflow: hidden; |
| background: #2d3748; |
| } |
| .participant-name { |
| position: absolute; |
| bottom: 10px; |
| left: 10px; |
| background: rgba(0,0,0,0.5); |
| color: white; |
| padding: 2px 8px; |
| border-radius: 4px; |
| font-size: 12px; |
| } |
| .modal { |
| transition: opacity 0.3s ease; |
| } |
| .modal-overlay { |
| background: rgba(0,0,0,0.7); |
| } |
| #localVideo { |
| transform: scaleX(-1); |
| } |
| .chat-messages { |
| height: 300px; |
| overflow-y: auto; |
| } |
| .message { |
| word-wrap: break-word; |
| } |
| </style> |
| </head> |
| <body class="bg-gray-900 text-white"> |
| |
| <div id="homePage" class="min-h-screen flex flex-col items-center justify-center p-4"> |
| <div class="text-center mb-12"> |
| <h1 class="text-5xl font-bold mb-4 text-blue-400">LiveConnect</h1> |
| <p class="text-xl text-gray-300">Plateforme de vidéo conférence en temps réel</p> |
| </div> |
| |
| <div class="w-full max-w-md bg-gray-800 rounded-xl p-8 shadow-2xl"> |
| <div class="mb-6"> |
| <label for="userName" class="block text-gray-300 mb-2">Votre nom</label> |
| <input type="text" id="userName" placeholder="Entrez votre nom" |
| class="w-full px-4 py-2 rounded-lg bg-gray-700 border border-gray-600 focus:outline-none focus:ring-2 focus:ring-blue-500"> |
| </div> |
| |
| <div class="mb-6"> |
| <label for="roomId" class="block text-gray-300 mb-2">ID de la réunion</label> |
| <input type="text" id="roomId" placeholder="12345" |
| class="w-full px-4 py-2 rounded-lg bg-gray-700 border border-gray-600 focus:outline-none focus:ring-2 focus:ring-blue-500"> |
| </div> |
| |
| <div class="flex flex-col space-y-4"> |
| <button id="joinBtn" class="bg-blue-600 hover:bg-blue-700 text-white py-3 px-6 rounded-lg font-medium transition duration-200 flex items-center justify-center"> |
| <i class="fas fa-video mr-2"></i> Rejoindre |
| </button> |
| <button id="createBtn" class="bg-green-600 hover:bg-green-700 text-white py-3 px-6 rounded-lg font-medium transition duration-200 flex items-center justify-center"> |
| <i class="fas fa-plus mr-2"></i> Créer une réunion |
| </button> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div id="meetingPage" class="hidden min-h-screen flex flex-col"> |
| |
| <header class="bg-gray-800 py-4 px-6 flex justify-between items-center border-b border-gray-700"> |
| <div class="flex items-center"> |
| <h2 class="text-xl font-semibold text-blue-400">LiveConnect</h2> |
| <span id="meetingIdDisplay" class="ml-4 bg-gray-700 px-3 py-1 rounded text-sm"></span> |
| </div> |
| <div class="flex items-center space-x-4"> |
| <button id="chatToggleBtn" class="text-gray-300 hover:text-white"> |
| <i class="fas fa-comment mr-1"></i> Chat |
| </button> |
| <button id="participantsBtn" class="text-gray-300 hover:text-white"> |
| <i class="fas fa-users mr-1"></i> <span id="participantCount">1</span> |
| </button> |
| <button id="shareBtn" class="bg-blue-600 hover:bg-blue-700 text-white px-4 py-1 rounded"> |
| <i class="fas fa-share-alt mr-1"></i> Partager |
| </button> |
| </div> |
| </header> |
|
|
| |
| <div class="flex flex-1"> |
| |
| <div class="flex-1 p-4"> |
| <div id="videoContainer" class="participant-grid"> |
| |
| </div> |
| </div> |
| |
| |
| <div id="chatPanel" class="hidden w-80 bg-gray-800 border-l border-gray-700 flex flex-col"> |
| <div class="p-4 border-b border-gray-700"> |
| <h3 class="font-semibold">Chat de la réunion</h3> |
| </div> |
| <div id="chatMessages" class="chat-messages flex-1 p-4 overflow-y-auto"> |
| |
| </div> |
| <div class="p-4 border-t border-gray-700"> |
| <div class="flex"> |
| <input id="chatInput" type="text" placeholder="Tapez votre message..." |
| class="flex-1 px-4 py-2 rounded-l-lg bg-gray-700 border border-gray-600 focus:outline-none"> |
| <button id="sendChatBtn" class="bg-blue-600 hover:bg-blue-700 px-4 py-2 rounded-r-lg"> |
| <i class="fas fa-paper-plane"></i> |
| </button> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <footer class="bg-gray-800 py-3 px-6 border-t border-gray-700"> |
| <div class="flex justify-center space-x-8"> |
| <button id="micBtn" class="control-btn bg-gray-700 hover:bg-gray-600 text-white p-3 rounded-full"> |
| <i class="fas fa-microphone"></i> |
| </button> |
| <button id="cameraBtn" class="control-btn bg-gray-700 hover:bg-gray-600 text-white p-3 rounded-full"> |
| <i class="fas fa-video"></i> |
| </button> |
| <button id="screenShareBtn" class="control-btn bg-gray-700 hover:bg-gray-600 text-white p-3 rounded-full"> |
| <i class="fas fa-desktop"></i> |
| </button> |
| <button id="leaveBtn" class="control-btn bg-red-600 hover:bg-red-700 text-white px-6 py-3 rounded-full"> |
| <i class="fas fa-phone-slash mr-2"></i> Quitter |
| </button> |
| </div> |
| </footer> |
| </div> |
|
|
| |
| <div id="shareModal" class="modal fixed inset-0 flex items-center justify-center z-50 hidden"> |
| <div class="modal-overlay absolute inset-0"></div> |
| <div class="bg-gray-800 rounded-xl z-50 w-full max-w-md mx-4 p-6"> |
| <div class="flex justify-between items-center mb-4"> |
| <h3 class="text-xl font-semibold">Partager le lien</h3> |
| <button id="closeShareModal" class="text-gray-400 hover:text-white"> |
| <i class="fas fa-times"></i> |
| </button> |
| </div> |
| <div class="mb-4"> |
| <p class="text-gray-300 mb-2">Copiez ce lien et envoyez-le aux participants :</p> |
| <div class="flex"> |
| <input id="shareLink" type="text" readonly |
| class="flex-1 px-4 py-2 rounded-l-lg bg-gray-700 border border-gray-600"> |
| <button id="copyLinkBtn" class="bg-blue-600 hover:bg-blue-700 px-4 py-2 rounded-r-lg"> |
| <i class="fas fa-copy"></i> |
| </button> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| <script> |
| |
| let localStream; |
| let meetingId = "12345"; |
| let userName = ""; |
| let isMicOn = true; |
| let isCameraOn = true; |
| let isChatOpen = false; |
| let participants = []; |
| |
| |
| const homePage = document.getElementById('homePage'); |
| const meetingPage = document.getElementById('meetingPage'); |
| const userNameInput = document.getElementById('userName'); |
| const roomIdInput = document.getElementById('roomId'); |
| const joinBtn = document.getElementById('joinBtn'); |
| const createBtn = document.getElementById('createBtn'); |
| const meetingIdDisplay = document.getElementById('meetingIdDisplay'); |
| const videoContainer = document.getElementById('videoContainer'); |
| const micBtn = document.getElementById('micBtn'); |
| const cameraBtn = document.getElementById('cameraBtn'); |
| const screenShareBtn = document.getElementById('screenShareBtn'); |
| const leaveBtn = document.getElementById('leaveBtn'); |
| const shareBtn = document.getElementById('shareBtn'); |
| const shareModal = document.getElementById('shareModal'); |
| const closeShareModal = document.getElementById('closeShareModal'); |
| const shareLink = document.getElementById('shareLink'); |
| const copyLinkBtn = document.getElementById('copyLinkBtn'); |
| const participantCount = document.getElementById('participantCount'); |
| const chatPanel = document.getElementById('chatPanel'); |
| const chatToggleBtn = document.getElementById('chatToggleBtn'); |
| const chatMessages = document.getElementById('chatMessages'); |
| const chatInput = document.getElementById('chatInput'); |
| const sendChatBtn = document.getElementById('sendChatBtn'); |
| |
| |
| joinBtn.addEventListener('click', joinMeeting); |
| createBtn.addEventListener('click', createMeeting); |
| micBtn.addEventListener('click', toggleMic); |
| cameraBtn.addEventListener('click', toggleCamera); |
| screenShareBtn.addEventListener('click', toggleScreenShare); |
| leaveBtn.addEventListener('click', leaveMeeting); |
| shareBtn.addEventListener('click', showShareModal); |
| closeShareModal.addEventListener('click', hideShareModal); |
| copyLinkBtn.addEventListener('click', copyShareLink); |
| chatToggleBtn.addEventListener('click', toggleChat); |
| sendChatBtn.addEventListener('click', sendMessage); |
| chatInput.addEventListener('keypress', function(e) { |
| if (e.key === 'Enter') sendMessage(); |
| }); |
| |
| |
| function joinMeeting() { |
| userName = userNameInput.value.trim(); |
| const roomId = roomIdInput.value.trim(); |
| |
| if (!userName) { |
| alert("Veuillez entrer votre nom"); |
| return; |
| } |
| |
| if (roomId) { |
| meetingId = roomId; |
| } |
| |
| startMeeting(); |
| } |
| |
| function createMeeting() { |
| userName = userNameInput.value.trim(); |
| |
| if (!userName) { |
| alert("Veuillez entrer votre nom"); |
| return; |
| } |
| |
| |
| if (!roomIdInput.value.trim()) { |
| meetingId = Math.random().toString(36).substring(2, 8).toUpperCase(); |
| roomIdInput.value = meetingId; |
| } else { |
| meetingId = roomIdInput.value.trim(); |
| } |
| |
| startMeeting(); |
| } |
| |
| async function startMeeting() { |
| try { |
| |
| localStream = await navigator.mediaDevices.getUserMedia({ |
| audio: true, |
| video: true |
| }); |
| |
| |
| homePage.classList.add('hidden'); |
| meetingPage.classList.remove('hidden'); |
| |
| |
| meetingIdDisplay.textContent = `ID: ${meetingId}`; |
| |
| |
| addVideoStream(localStream, userName, true); |
| |
| |
| setTimeout(() => { |
| simulateParticipants(); |
| }, 2000); |
| |
| |
| updateShareLink(); |
| |
| } catch (error) { |
| console.error("Erreur d'accès aux médias:", error); |
| alert("Impossible d'accéder à la caméra ou au microphone. Veuillez vérifier vos permissions."); |
| } |
| } |
| |
| function addVideoStream(stream, name, isLocal = false) { |
| const participantId = `participant-${Date.now()}`; |
| const participant = { |
| id: participantId, |
| name: name, |
| stream: stream, |
| isLocal: isLocal |
| }; |
| |
| participants.push(participant); |
| updateParticipantCount(); |
| |
| const videoElement = document.createElement('video'); |
| videoElement.id = participantId; |
| videoElement.srcObject = stream; |
| videoElement.autoplay = true; |
| videoElement.playsInline = true; |
| if (isLocal) { |
| videoElement.muted = true; |
| videoElement.classList.add('local-video'); |
| } |
| |
| const participantDiv = document.createElement('div'); |
| participantDiv.className = 'participant'; |
| participantDiv.appendChild(videoElement); |
| |
| const nameDiv = document.createElement('div'); |
| nameDiv.className = 'participant-name'; |
| nameDiv.textContent = name + (isLocal ? ' (Vous)' : ''); |
| participantDiv.appendChild(nameDiv); |
| |
| videoContainer.appendChild(participantDiv); |
| } |
| |
| function toggleMic() { |
| if (localStream) { |
| const audioTracks = localStream.getAudioTracks(); |
| audioTracks.forEach(track => { |
| track.enabled = !track.enabled; |
| }); |
| |
| isMicOn = !isMicOn; |
| micBtn.classList.toggle('bg-red-600', !isMicOn); |
| micBtn.classList.toggle('bg-gray-700', isMicOn); |
| } |
| } |
| |
| function toggleCamera() { |
| if (localStream) { |
| const videoTracks = localStream.getVideoTracks(); |
| videoTracks.forEach(track => { |
| track.enabled = !track.enabled; |
| }); |
| |
| isCameraOn = !isCameraOn; |
| cameraBtn.classList.toggle('bg-red-600', !isCameraOn); |
| cameraBtn.classList.toggle('bg-gray-700', isCameraOn); |
| } |
| } |
| |
| function toggleScreenShare() { |
| |
| alert("Fonctionnalité de partage d'écran simulée. En production, vous utiliseriez l'API getDisplayMedia."); |
| } |
| |
| function leaveMeeting() { |
| |
| if (localStream) { |
| localStream.getTracks().forEach(track => track.stop()); |
| } |
| |
| |
| meetingPage.classList.add('hidden'); |
| homePage.classList.remove('hidden'); |
| |
| |
| videoContainer.innerHTML = ''; |
| participants = []; |
| } |
| |
| function showShareModal() { |
| shareModal.classList.remove('hidden'); |
| } |
| |
| function hideShareModal() { |
| shareModal.classList.add('hidden'); |
| } |
| |
| function updateShareLink() { |
| const currentUrl = window.location.href.split('?')[0]; |
| shareLink.value = `${currentUrl}?room=${meetingId}`; |
| } |
| |
| function copyShareLink() { |
| shareLink.select(); |
| document.execCommand('copy'); |
| alert('Lien copié dans le presse-papiers !'); |
| } |
| |
| function updateParticipantCount() { |
| participantCount.textContent = participants.length; |
| } |
| |
| function toggleChat() { |
| isChatOpen = !isChatOpen; |
| chatPanel.classList.toggle('hidden', !isChatOpen); |
| } |
| |
| function sendMessage() { |
| const message = chatInput.value.trim(); |
| if (message && userName) { |
| const messageDiv = document.createElement('div'); |
| messageDiv.className = 'message mb-2 p-2 bg-gray-700 rounded'; |
| messageDiv.innerHTML = `<strong>${userName}:</strong> ${message}`; |
| chatMessages.appendChild(messageDiv); |
| chatInput.value = ''; |
| |
| |
| chatMessages.scrollTop = chatMessages.scrollHeight; |
| |
| |
| if (participants.length > 1) { |
| setTimeout(() => { |
| const replyDiv = document.createElement('div'); |
| replyDiv.className = 'message mb-2 p-2 bg-blue-900 rounded'; |
| replyDiv.innerHTML = `<strong>Participant:</strong> Merci pour votre message!`; |
| chatMessages.appendChild(replyDiv); |
| chatMessages.scrollTop = chatMessages.scrollHeight; |
| }, 1000); |
| } |
| } |
| } |
| |
| |
| function simulateParticipants() { |
| if (participants.length < 4) { |
| const names = ["Alex", "Sophie", "Thomas", "Emma", "Lucas", "Camille"]; |
| const randomName = names[Math.floor(Math.random() * names.length)]; |
| |
| |
| navigator.mediaDevices.getUserMedia({ video: true, audio: true }) |
| .then(stream => { |
| addVideoStream(stream, randomName); |
| }) |
| .catch(() => { |
| |
| const blackVideo = document.createElement('video'); |
| blackVideo.autoplay = true; |
| blackVideo.playsInline = true; |
| addVideoStream(blackVideo.captureStream(), randomName); |
| }); |
| |
| |
| if (participants.length < 3) { |
| setTimeout(simulateParticipants, 3000); |
| } |
| } |
| } |
| |
| |
| window.addEventListener('DOMContentLoaded', () => { |
| const urlParams = new URLSearchParams(window.location.search); |
| const roomParam = urlParams.get('room'); |
| |
| if (roomParam) { |
| roomIdInput.value = roomParam; |
| } |
| }); |
| </script> |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=Adamchab/zoom" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> |
| </html> |