zoom / index.html
Adamchab's picture
Add 3 files
9230241 verified
<!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%; /* 16:9 Aspect Ratio */
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); /* Miroir pour la caméra locale */
}
.chat-messages {
height: 300px;
overflow-y: auto;
}
.message {
word-wrap: break-word;
}
</style>
</head>
<body class="bg-gray-900 text-white">
<!-- Page d'accueil -->
<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>
<!-- Page de la réunion -->
<div id="meetingPage" class="hidden min-h-screen flex flex-col">
<!-- En-tête -->
<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>
<!-- Contenu principal -->
<div class="flex flex-1">
<!-- Zone vidéo -->
<div class="flex-1 p-4">
<div id="videoContainer" class="participant-grid">
<!-- Les vidéos des participants seront ajoutées ici dynamiquement -->
</div>
</div>
<!-- Chat panel (caché par défaut) -->
<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">
<!-- Messages de chat -->
</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>
<!-- Contrôles -->
<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>
<!-- Modal de partage -->
<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>
// Variables globales
let localStream;
let meetingId = "12345";
let userName = "";
let isMicOn = true;
let isCameraOn = true;
let isChatOpen = false;
let participants = [];
// Éléments DOM
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');
// Événements
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();
});
// Fonctions
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;
}
// Générer un ID de réunion aléatoire si non spécifié
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 {
// Obtenir le flux média (caméra + micro)
localStream = await navigator.mediaDevices.getUserMedia({
audio: true,
video: true
});
// Afficher la page de réunion
homePage.classList.add('hidden');
meetingPage.classList.remove('hidden');
// Afficher l'ID de réunion
meetingIdDisplay.textContent = `ID: ${meetingId}`;
// Ajouter la vidéo locale
addVideoStream(localStream, userName, true);
// Simuler des participants (pour la démo)
setTimeout(() => {
simulateParticipants();
}, 2000);
// Mettre à jour le lien de partage
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() {
// Simuler le partage d'écran (pour la démo)
alert("Fonctionnalité de partage d'écran simulée. En production, vous utiliseriez l'API getDisplayMedia.");
}
function leaveMeeting() {
// Arrêter tous les flux média
if (localStream) {
localStream.getTracks().forEach(track => track.stop());
}
// Retour à la page d'accueil
meetingPage.classList.add('hidden');
homePage.classList.remove('hidden');
// Réinitialiser
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 = '';
// Faire défiler vers le bas
chatMessages.scrollTop = chatMessages.scrollHeight;
// Simuler une réponse (pour la démo)
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);
}
}
}
// Simulation de participants pour la démo
function simulateParticipants() {
if (participants.length < 4) { // Limite à 4 participants pour la démo
const names = ["Alex", "Sophie", "Thomas", "Emma", "Lucas", "Camille"];
const randomName = names[Math.floor(Math.random() * names.length)];
// Simuler un nouveau participant
navigator.mediaDevices.getUserMedia({ video: true, audio: true })
.then(stream => {
addVideoStream(stream, randomName);
})
.catch(() => {
// Si l'accès est refusé, simuler quand même avec une vidéo noire
const blackVideo = document.createElement('video');
blackVideo.autoplay = true;
blackVideo.playsInline = true;
addVideoStream(blackVideo.captureStream(), randomName);
});
// Simuler un autre participant après un délai
if (participants.length < 3) {
setTimeout(simulateParticipants, 3000);
}
}
}
// Vérifier s'il y a un ID de réunion dans l'URL
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>