|
<!DOCTYPE html> |
|
<html lang="fr"> |
|
<head> |
|
<meta charset="UTF-8"> |
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
<title>Live Vidéo - Système de Partage</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-box { |
|
aspect-ratio: 16/9; |
|
} |
|
|
|
video { |
|
background-color: #222; |
|
} |
|
|
|
.username { |
|
background-color: rgba(0, 0, 0, 0.5); |
|
} |
|
|
|
.status-indicator { |
|
width: 10px; |
|
height: 10px; |
|
} |
|
|
|
.modal { |
|
background-color: rgba(0, 0, 0, 0.5); |
|
} |
|
|
|
@keyframes fadeIn { |
|
from { opacity: 0; transform: translateY(-20px); } |
|
to { opacity: 1; transform: translateY(0); } |
|
} |
|
|
|
.animate-fade-in { |
|
animation: fadeIn 0.3s ease-out forwards; |
|
} |
|
</style> |
|
</head> |
|
<body class="bg-gray-100 font-sans antialiased"> |
|
<div class="container mx-auto px-4 py-8 max-w-6xl"> |
|
<div class="text-center mb-10"> |
|
<h1 class="text-4xl font-bold bg-gradient-to-r from-blue-600 to-purple-600 bg-clip-text text-transparent"> |
|
<i class="fas fa-video mr-2"></i>Plateforme de Live Vidéo |
|
</h1> |
|
<p class="text-gray-600 mt-2">Connectez-vous et partagez des moments en direct</p> |
|
</div> |
|
|
|
<div class="space-y-6"> |
|
|
|
<div id="login-section" class="bg-white rounded-xl shadow-lg p-6"> |
|
<h2 class="text-2xl font-semibold text-gray-800 mb-6 flex items-center"> |
|
<i class="fas fa-door-open text-blue-500 mr-2"></i>Rejoindre ou créer un live |
|
</h2> |
|
|
|
<div class="space-y-4"> |
|
<div> |
|
<label for="username" class="block text-sm font-medium text-gray-700 mb-1"> |
|
<i class="fas fa-user text-blue-500 mr-1"></i>Votre nom |
|
</label> |
|
<input type="text" id="username" placeholder="Entrez votre nom" required |
|
class="w-full px-4 py-3 rounded-lg border border-gray-300 focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition"> |
|
</div> |
|
|
|
<div> |
|
<label for="room-code" class="block text-sm font-medium text-gray-700 mb-1"> |
|
<i class="fas fa-key text-blue-500 mr-1"></i>Code de la salle |
|
</label> |
|
<div class="flex space-x-2"> |
|
<input type="text" id="room-code" placeholder="Entrez le code de la salle" value="a234" |
|
class="flex-1 px-4 py-3 rounded-lg border border-gray-300 focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition"> |
|
<button id="join-btn" class="px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition flex items-center"> |
|
<i class="fas fa-sign-in-alt mr-2"></i>Rejoindre |
|
</button> |
|
</div> |
|
</div> |
|
|
|
<div class="pt-2"> |
|
<button id="create-btn" class="w-full px-6 py-3 bg-purple-600 text-white rounded-lg hover:bg-purple-700 transition flex items-center justify-center"> |
|
<i class="fas fa-plus-circle mr-2"></i>Créer un nouveau live |
|
</button> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
|
|
<div id="video-section" class="hidden bg-white rounded-xl shadow-lg p-6"> |
|
<div class="flex justify-between items-center mb-6"> |
|
<h2 class="text-2xl font-semibold text-gray-800 flex items-center"> |
|
<i class="fas fa-video text-blue-500 mr-2"></i> |
|
Live en cours: <span id="room-display" class="ml-2 bg-blue-100 text-blue-800 px-3 py-1 rounded-full">a234</span> |
|
</h2> |
|
<div id="participant-count" class="flex items-center text-gray-600"> |
|
<i class="fas fa-users mr-2"></i> |
|
<span>1 participant</span> |
|
</div> |
|
</div> |
|
|
|
|
|
<div id="videos" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4"> |
|
<div class="video-box relative rounded-xl overflow-hidden"> |
|
<video id="local-video" autoplay muted playsinline class="w-full h-full object-cover"></video> |
|
<div class="username absolute bottom-3 left-3 text-white px-3 py-1 rounded-lg text-sm"> |
|
<i class="fas fa-user-circle mr-1"></i>Vous |
|
</div> |
|
</div> |
|
</div> |
|
|
|
|
|
<div class="mt-6 flex flex-wrap gap-3 justify-between items-center"> |
|
<div class="flex flex-wrap gap-3"> |
|
<button id="toggle-video" class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition flex items-center"> |
|
<i class="fas fa-video mr-2"></i>Désactiver vidéo |
|
</button> |
|
<button id="toggle-audio" class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition flex items-center"> |
|
<i class="fas fa-microphone mr-2"></i>Désactiver audio |
|
</button> |
|
<button id="share-screen" class="px-4 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 transition flex items-center"> |
|
<i class="fas fa-desktop mr-2"></i>Partager écran |
|
</button> |
|
</div> |
|
<button id="leave-btn" class="px-4 py-2 bg-red-600 text-white rounded-lg hover:bg-red-700 transition flex items-center"> |
|
<i class="fas fa-sign-out-alt mr-2"></i>Quitter |
|
</button> |
|
</div> |
|
</div> |
|
|
|
|
|
<div id="host-controls" class="hidden bg-white rounded-xl shadow-lg p-6"> |
|
<h2 class="text-2xl font-semibold text-gray-800 mb-6 flex items-center"> |
|
<i class="fas fa-cogs text-purple-500 mr-2"></i>Contrôles du live |
|
</h2> |
|
|
|
<div class="space-y-6"> |
|
<div> |
|
<h3 class="text-lg font-medium text-gray-700 mb-2 flex items-center"> |
|
<i class="fas fa-link text-blue-500 mr-2"></i>Lien d'invitation |
|
</h3> |
|
<div class="flex"> |
|
<input type="text" id="invite-link" readonly |
|
class="flex-1 px-4 py-3 rounded-l-lg border border-gray-300 bg-gray-50"> |
|
<button id="copy-btn" class="px-4 py-3 bg-blue-600 text-white rounded-r-lg hover:bg-blue-700 transition flex items-center"> |
|
<i class="fas fa-copy mr-2"></i>Copier |
|
</button> |
|
</div> |
|
</div> |
|
|
|
<div> |
|
<h3 class="text-lg font-medium text-gray-700 mb-2 flex items-center"> |
|
<i class="fas fa-users text-purple-500 mr-2"></i>Participants |
|
</h3> |
|
<div id="participant-list" class="bg-gray-50 rounded-lg p-4 max-h-60 overflow-y-auto space-y-2"> |
|
<div class="flex items-center p-2 bg-white rounded-lg shadow-sm"> |
|
<span class="status-indicator rounded-full bg-blue-500 mr-3"></span> |
|
<span>Vous (hôte)</span> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
|
|
<div id="create-room-modal" class="modal hidden fixed inset-0 flex items-center justify-center z-50"> |
|
<div class="bg-white rounded-xl shadow-2xl p-6 w-full max-w-md animate-fade-in"> |
|
<div class="flex justify-between items-center mb-4"> |
|
<h3 class="text-xl font-semibold text-gray-800 flex items-center"> |
|
<i class="fas fa-plus-circle text-purple-500 mr-2"></i>Créer un nouveau live |
|
</h3> |
|
<button class="close text-gray-500 hover:text-gray-700 text-2xl"> |
|
<i class="fas fa-times"></i> |
|
</button> |
|
</div> |
|
|
|
<p class="text-gray-600 mb-4"> |
|
Choisissez un code pour votre salle (ou laissez vide pour un code généré automatiquement) |
|
</p> |
|
|
|
<div class="mb-6"> |
|
<input type="text" id="new-room-code" placeholder="Code de la salle (optionnel)" |
|
class="w-full px-4 py-3 rounded-lg border border-gray-300 focus:ring-2 focus:ring-purple-500 focus:border-purple-500 transition"> |
|
</div> |
|
|
|
<button id="confirm-create" class="w-full px-6 py-3 bg-purple-600 text-white rounded-lg hover:bg-purple-700 transition flex items-center justify-center"> |
|
<i class="fas fa-check-circle mr-2"></i>Créer et rejoindre |
|
</button> |
|
</div> |
|
</div> |
|
|
|
|
|
<div id="notification-modal" class="modal hidden fixed inset-0 flex items-center justify-center z-50"> |
|
<div class="bg-white rounded-xl shadow-2xl p-6 w-full max-w-md animate-fade-in"> |
|
<div class="flex justify-between items-center mb-4"> |
|
<h3 id="notification-title" class="text-xl font-semibold text-gray-800 flex items-center"> |
|
<i class="fas fa-bell text-blue-500 mr-2"></i>Notification |
|
</h3> |
|
<button class="close text-gray-500 hover:text-gray-700 text-2xl"> |
|
<i class="fas fa-times"></i> |
|
</button> |
|
</div> |
|
|
|
<p id="notification-message" class="text-gray-600 mb-6"> |
|
Message de notification ici. |
|
</p> |
|
|
|
<div class="text-center"> |
|
<button id="notification-confirm" class="px-6 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition"> |
|
OK |
|
</button> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
|
|
<div id="toast" class="hidden fixed bottom-4 right-4 bg-green-500 text-white px-6 py-3 rounded-lg shadow-lg flex items-center"> |
|
<i class="fas fa-check-circle mr-2"></i> |
|
<span id="toast-message">Action réussie!</span> |
|
</div> |
|
|
|
<script> |
|
|
|
let localStream = null; |
|
let peers = {}; |
|
let isHost = false; |
|
let roomCode = "a234"; |
|
let username = ""; |
|
let videoEnabled = true; |
|
let audioEnabled = true; |
|
let screenShareStream = null; |
|
|
|
|
|
const loginSection = document.getElementById('login-section'); |
|
const videoSection = document.getElementById('video-section'); |
|
const hostControls = document.getElementById('host-controls'); |
|
const usernameInput = document.getElementById('username'); |
|
const roomCodeInput = document.getElementById('room-code'); |
|
const joinBtn = document.getElementById('join-btn'); |
|
const createBtn = document.getElementById('create-btn'); |
|
const roomDisplay = document.getElementById('room-display'); |
|
const toggleVideoBtn = document.getElementById('toggle-video'); |
|
const toggleAudioBtn = document.getElementById('toggle-audio'); |
|
const shareScreenBtn = document.getElementById('share-screen'); |
|
const leaveBtn = document.getElementById('leave-btn'); |
|
const inviteLink = document.getElementById('invite-link'); |
|
const copyBtn = document.getElementById('copy-btn'); |
|
const participantCount = document.getElementById('participant-count'); |
|
const participantList = document.getElementById('participant-list'); |
|
const createRoomModal = document.getElementById('create-room-modal'); |
|
const newRoomCodeInput = document.getElementById('new-room-code'); |
|
const confirmCreateBtn = document.getElementById('confirm-create'); |
|
const notificationModal = document.getElementById('notification-modal'); |
|
const notificationTitle = document.getElementById('notification-title'); |
|
const notificationMessage = document.getElementById('notification-message'); |
|
const notificationConfirm = document.getElementById('notification-confirm'); |
|
const localVideo = document.getElementById('local-video'); |
|
const videosContainer = document.getElementById('videos'); |
|
const toast = document.getElementById('toast'); |
|
const toastMessage = document.getElementById('toast-message'); |
|
|
|
|
|
function generateRoomCode() { |
|
return Math.random().toString(36).substring(2, 6).toUpperCase(); |
|
} |
|
|
|
|
|
function showNotification(title, message) { |
|
notificationTitle.textContent = title; |
|
notificationMessage.textContent = message; |
|
notificationModal.classList.remove('hidden'); |
|
} |
|
|
|
|
|
function showToast(message, isSuccess = true) { |
|
toastMessage.textContent = message; |
|
toast.className = `fixed bottom-4 right-4 px-6 py-3 rounded-lg shadow-lg flex items-center animate-fade-in ${isSuccess ? 'bg-green-500' : 'bg-red-500'} text-white`; |
|
toast.classList.remove('hidden'); |
|
|
|
setTimeout(() => { |
|
toast.classList.add('hidden'); |
|
}, 3000); |
|
} |
|
|
|
|
|
async function joinRoom(code) { |
|
if (!username) { |
|
showNotification("Erreur", "Veuillez entrer votre nom pour rejoindre."); |
|
return; |
|
} |
|
|
|
if (!code) { |
|
showNotification("Erreur", "Veuillez entrer un code de salle."); |
|
return; |
|
} |
|
|
|
roomCode = code.toUpperCase(); |
|
|
|
try { |
|
|
|
localStream = await navigator.mediaDevices.getUserMedia({ |
|
video: true, |
|
audio: true |
|
}); |
|
|
|
localVideo.srcObject = localStream; |
|
loginSection.classList.add('hidden'); |
|
videoSection.classList.remove('hidden'); |
|
|
|
if (isHost) { |
|
hostControls.classList.remove('hidden'); |
|
const currentUrl = window.location.href.split('?')[0]; |
|
inviteLink.value = `${currentUrl}?room=${roomCode}`; |
|
} |
|
|
|
roomDisplay.textContent = roomCode; |
|
|
|
showToast(`Vous avez rejoint la salle ${roomCode}`, true); |
|
|
|
|
|
setTimeout(() => { |
|
const videoBox = document.createElement('div'); |
|
videoBox.className = 'video-box relative rounded-xl overflow-hidden'; |
|
videoBox.id = 'video-container-demo-peer'; |
|
|
|
const video = document.createElement('video'); |
|
video.id = 'video-demo-peer'; |
|
video.autoplay = true; |
|
video.playsinline = true; |
|
video.className = 'w-full h-full object-cover'; |
|
|
|
|
|
video.srcObject = new MediaStream([localStream.getVideoTracks()[0].clone()]); |
|
|
|
const usernameDiv = document.createElement('div'); |
|
usernameDiv.className = 'username absolute bottom-3 left-3 text-white px-3 py-1 rounded-lg text-sm'; |
|
usernameDiv.innerHTML = '<i class="fas fa-user-circle mr-1"></i>Participant Test'; |
|
videoBox.appendChild(usernameDiv); |
|
videoBox.appendChild(video); |
|
videosContainer.appendChild(videoBox); |
|
|
|
|
|
participantCount.innerHTML = '<i class="fas fa-users mr-2"></i>2 participants'; |
|
|
|
|
|
const participantItem = document.createElement('div'); |
|
participantItem.className = 'flex items-center p-2 bg-white rounded-lg shadow-sm'; |
|
participantItem.innerHTML = ` |
|
<span class="status-indicator rounded-full bg-green-500 mr-3"></span> |
|
Participant Test |
|
`; |
|
participantList.appendChild(participantItem); |
|
}, 2000); |
|
|
|
} catch (error) { |
|
console.error("Erreur simulation:", error); |
|
showNotification("Erreur", "Impossible d'accéder à la caméra ou au microphone. Veuillez vérifier vos autorisations."); |
|
} |
|
} |
|
|
|
|
|
function leaveRoom() { |
|
if (localStream) { |
|
localStream.getTracks().forEach(track => track.stop()); |
|
} |
|
|
|
if (screenShareStream) { |
|
screenShareStream.getTracks().forEach(track => track.stop()); |
|
} |
|
|
|
|
|
const videos = document.querySelectorAll('.video-box:not(:first-child)'); |
|
videos.forEach(video => video.remove()); |
|
|
|
|
|
localVideo.srcObject = null; |
|
videoSection.classList.add('hidden'); |
|
hostControls.classList.add('hidden'); |
|
loginSection.classList.remove('hidden'); |
|
|
|
showToast("Vous avez quitté la salle", true); |
|
} |
|
|
|
|
|
async function shareScreen() { |
|
try { |
|
|
|
if (screenShareStream) { |
|
stopScreenSharing(); |
|
return; |
|
} |
|
|
|
|
|
screenShareStream = await navigator.mediaDevices.getDisplayMedia({ |
|
video: { |
|
cursor: "always", |
|
displaySurface: "monitor" |
|
}, |
|
audio: false |
|
}); |
|
|
|
|
|
localVideo.srcObject = screenShareStream; |
|
shareScreenBtn.innerHTML = '<i class="fas fa-stop-circle mr-2"></i>Arrêter le partage'; |
|
showToast("Partage d'écran activé", true); |
|
|
|
|
|
screenShareStream.getVideoTracks()[0].onended = () => { |
|
stopScreenSharing(); |
|
}; |
|
|
|
} catch (error) { |
|
console.error("Erreur partage écran:", error); |
|
if (error.name !== 'NotAllowedError') { |
|
showNotification("Erreur", "Impossible de partager l'écran."); |
|
} |
|
} |
|
} |
|
|
|
function stopScreenSharing() { |
|
if (screenShareStream) { |
|
screenShareStream.getTracks().forEach(track => track.stop()); |
|
localVideo.srcObject = localStream; |
|
screenShareStream = null; |
|
shareScreenBtn.innerHTML = '<i class="fas fa-desktop mr-2"></i>Partager écran'; |
|
showToast("Partage d'écran arrêté", true); |
|
} |
|
} |
|
|
|
|
|
joinBtn.addEventListener('click', () => { |
|
username = usernameInput.value.trim(); |
|
const code = roomCodeInput.value.trim().toUpperCase(); |
|
|
|
if (username && code) { |
|
isHost = false; |
|
joinRoom(code); |
|
} else { |
|
showNotification("Informations manquantes", "Veuillez entrer votre nom et le code de la salle."); |
|
} |
|
}); |
|
|
|
createBtn.addEventListener('click', () => { |
|
username = usernameInput.value.trim(); |
|
|
|
if (username) { |
|
isHost = true; |
|
createRoomModal.classList.remove('hidden'); |
|
} else { |
|
showNotification("Nom manquant", "Veuillez entrer votre nom pour créer une salle."); |
|
} |
|
}); |
|
|
|
confirmCreateBtn.addEventListener('click', () => { |
|
const customCode = newRoomCodeInput.value.trim().toUpperCase(); |
|
const code = customCode || generateRoomCode(); |
|
|
|
createRoomModal.classList.add('hidden'); |
|
joinRoom(code); |
|
}); |
|
|
|
toggleVideoBtn.addEventListener('click', () => { |
|
if (localStream) { |
|
const videoTracks = localStream.getVideoTracks(); |
|
videoTracks.forEach(track => { |
|
track.enabled = !track.enabled; |
|
}); |
|
|
|
videoEnabled = videoTracks[0]?.enabled || false; |
|
toggleVideoBtn.innerHTML = videoEnabled |
|
? '<i class="fas fa-video mr-2"></i>Désactiver vidéo' |
|
: '<i class="fas fa-video-slash mr-2"></i>Activer vidéo'; |
|
|
|
showToast(`Vidéo ${videoEnabled ? 'activée' : 'désactivée'}`, true); |
|
} |
|
}); |
|
|
|
toggleAudioBtn.addEventListener('click', () => { |
|
if (localStream) { |
|
const audioTracks = localStream.getAudioTracks(); |
|
audioTracks.forEach(track => { |
|
track.enabled = !track.enabled; |
|
}); |
|
|
|
audioEnabled = audioTracks[0]?.enabled || false; |
|
toggleAudioBtn.innerHTML = audioEnabled |
|
? '<i class="fas fa-microphone mr-2"></i>Désactiver audio' |
|
: '<i class="fas fa-microphone-slash mr-2"></i>Activer audio'; |
|
|
|
showToast(`Audio ${audioEnabled ? 'activé' : 'désactivé'}`, true); |
|
} |
|
}); |
|
|
|
shareScreenBtn.addEventListener('click', shareScreen); |
|
|
|
leaveBtn.addEventListener('click', () => { |
|
leaveRoom(); |
|
}); |
|
|
|
copyBtn.addEventListener('click', () => { |
|
inviteLink.select(); |
|
document.execCommand('copy'); |
|
copyBtn.innerHTML = '<i class="fas fa-check mr-2"></i>Copié !'; |
|
showToast("Lien copié dans le presse-papier", true); |
|
|
|
setTimeout(() => { |
|
copyBtn.innerHTML = '<i class="fas fa-copy mr-2"></i>Copier'; |
|
}, 2000); |
|
}); |
|
|
|
|
|
document.querySelectorAll('.close').forEach(closeBtn => { |
|
closeBtn.addEventListener('click', () => { |
|
createRoomModal.classList.add('hidden'); |
|
notificationModal.classList.add('hidden'); |
|
}); |
|
}); |
|
|
|
notificationConfirm.addEventListener('click', () => { |
|
notificationModal.classList.add('hidden'); |
|
}); |
|
|
|
|
|
window.addEventListener('load', () => { |
|
const urlParams = new URLSearchParams(window.location.search); |
|
const roomParam = urlParams.get('room'); |
|
|
|
if (roomParam) { |
|
roomCodeInput.value = roomParam; |
|
usernameInput.focus(); |
|
} |
|
}); |
|
</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/live" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> |
|
</html> |