From e6a755b1d62a889787679de8d97394b25032d23c Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Wed, 18 Feb 2026 19:09:18 +0000 Subject: [PATCH] Vocal canaux avec membres en apercu --- api_v1_voice.php | 13 ++++++--- assets/css/discord.css | 2 +- assets/js/voice.js | 59 ++++++++++++++++++++++++++++++++-------- auth/logout.php | 49 ++++++++++++++++++++++++++++++++- data/3.participants.json | 2 +- index.php | 9 ++++-- requests.log | 19 +++++++++++++ 7 files changed, 132 insertions(+), 21 deletions(-) diff --git a/api_v1_voice.php b/api_v1_voice.php index b36d663..67ca1c7 100644 --- a/api_v1_voice.php +++ b/api_v1_voice.php @@ -107,7 +107,7 @@ if ($action === "join") { if (($p["last_seen"] ?? 0) < $stale_time) unset($ps[$id]); } - $new_id = peer_id(); + $new_id = substr($_REQUEST["peer_id"] ?: peer_id(), 0, 16); $ps[$new_id] = [ "id" => $new_id, "user_id" => $current_user_id, @@ -120,9 +120,14 @@ if ($action === "join") { // DB Integration for sidebar if ($current_user_id > 0) { try { - $stmt = db()->prepare("INSERT INTO voice_sessions (user_id, channel_id, last_seen) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE channel_id = ?, last_seen = ?"); - $stmt->execute([$current_user_id, $room, now_ms(), $room, now_ms()]); - } catch (Exception $e) {} + $stmt = db()->prepare("INSERT INTO voice_sessions (user_id, channel_id, last_seen, peer_id, name) VALUES (?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE channel_id = ?, last_seen = ?, peer_id = ?, name = ?"); + $res = $stmt->execute([$current_user_id, $room, now_ms(), $new_id, $name, $room, now_ms(), $new_id, $name]); + if (!$res) { + error_log("Failed to insert voice session for user $current_user_id in room $room"); + } + } catch (Exception $e) { + error_log("Voice session DB error: " . $e->getMessage()); + } } json_out(["success" => true, "peer_id" => $new_id, "participants" => $ps]); diff --git a/assets/css/discord.css b/assets/css/discord.css index 409eba9..ee53328 100644 --- a/assets/css/discord.css +++ b/assets/css/discord.css @@ -925,7 +925,7 @@ body { } /* Voice active state */ -.voice-item.active { +.voice-item.active, .voice-item.connected { background-color: rgba(35, 165, 89, 0.1); color: #23a559 !important; } diff --git a/assets/js/voice.js b/assets/js/voice.js index 7232825..329eded 100644 --- a/assets/js/voice.js +++ b/assets/js/voice.js @@ -29,7 +29,22 @@ class VoiceChannel { this.speakingUsers = new Set(); this.setupPTTListeners(); - window.addEventListener('beforeunload', () => this.leave()); + window.addEventListener('beforeunload', () => { + // We don't want to leave on page refresh if we want persistence + // but we might want to tell the server we are "still here" soon. + // Actually, for a simple refresh, we just let the session timeout or re-join. + }); + + // Auto-rejoin if we were in a channel + setTimeout(() => { + const savedChannelId = sessionStorage.getItem('activeVoiceChannel'); + const savedPeerId = sessionStorage.getItem('activeVoicePeerId'); + if (savedChannelId) { + console.log('Auto-rejoining voice channel:', savedChannelId); + if (savedPeerId) this.myPeerId = savedPeerId; + this.join(savedChannelId, true); // Pass true to indicate auto-rejoin + } + }, 200); } setupPTTListeners() { @@ -67,18 +82,19 @@ class VoiceChannel { }); } - async join(channelId) { - console.log('VoiceChannel.join process started for channel:', channelId); - if (this.currentChannelId === channelId) { + async join(channelId, isAutoRejoin = false) { + console.log('VoiceChannel.join process started for channel:', channelId, 'isAutoRejoin:', isAutoRejoin); + if (this.currentChannelId === channelId && !isAutoRejoin) { console.log('Already in this channel'); return; } - if (this.currentChannelId) { + if (this.currentChannelId && this.currentChannelId != channelId) { console.log('Leaving previous channel:', this.currentChannelId); this.leave(); } this.currentChannelId = channelId; + sessionStorage.setItem('activeVoiceChannel', channelId); try { console.log('Requesting microphone access...'); @@ -91,13 +107,14 @@ class VoiceChannel { // Join via PHP console.log('Calling API join...'); - const url = `api_v1_voice.php?action=join&room=${channelId}&name=${encodeURIComponent(window.currentUsername || 'Unknown')}`; + const url = `api_v1_voice.php?action=join&room=${channelId}&name=${encodeURIComponent(window.currentUsername || 'Unknown')}${this.myPeerId ? '&peer_id='+this.myPeerId : ''}`; const resp = await fetch(url); const data = await resp.json(); console.log('API join response:', data); if (data.success) { this.myPeerId = data.peer_id; + sessionStorage.setItem('activeVoicePeerId', this.myPeerId); console.log('Joined room with peer_id:', this.myPeerId); // Start polling @@ -381,14 +398,27 @@ class VoiceChannel { } leave() { - if (!this.currentChannelId) return; - console.log('Leaving voice channel:', this.currentChannelId); + if (!this.currentChannelId) { + console.log('VoiceChannel.leave called but no active channel'); + return; + } + console.log('Leaving voice channel:', this.currentChannelId, 'myPeerId:', this.myPeerId); + const cid = this.currentChannelId; + const pid = this.myPeerId; + + sessionStorage.removeItem('activeVoiceChannel'); + sessionStorage.removeItem('activeVoicePeerId'); if (this.pollInterval) clearInterval(this.pollInterval); - fetch(`api_v1_voice.php?action=leave&room=${this.currentChannelId}&peer_id=${this.myPeerId}`); + // Use keepalive for the leave fetch to ensure it reaches the server during page unload + fetch(`api_v1_voice.php?action=leave&room=${cid}&peer_id=${pid}`, { keepalive: true }); if (this.localStream) { - this.localStream.getTracks().forEach(track => track.stop()); + console.log('Stopping local stream tracks'); + this.localStream.getTracks().forEach(track => { + track.stop(); + console.log('Track stopped:', track.kind); + }); this.localStream = null; } if (this.analysisStream) { @@ -482,11 +512,18 @@ class VoiceChannel { // Clear all lists first document.querySelectorAll('.voice-users-list').forEach(el => el.innerHTML = ''); + // Remove connected highlight from all voice items + document.querySelectorAll('.voice-item').forEach(el => { + el.classList.remove('connected'); + }); + // Populate based on data Object.keys(data.channels).forEach(channelId => { - // Fix: The voice-users-list is a sibling of the container of the voice-item const voiceItem = document.querySelector(`.voice-item[data-channel-id="${channelId}"]`); if (voiceItem) { + // Highlight channel as connected if anyone is in it + voiceItem.classList.add('connected'); + const container = voiceItem.closest('.channel-item-container'); if (container) { const listEl = container.querySelector('.voice-users-list'); diff --git a/auth/logout.php b/auth/logout.php index 37bc5ab..9dedbd2 100644 --- a/auth/logout.php +++ b/auth/logout.php @@ -1,5 +1,52 @@ prepare("SELECT channel_id, peer_id FROM voice_sessions WHERE user_id = ?"); + $stmt->execute([$user['id']]); + $sess = $stmt->fetch(); + + if ($sess) { + $room = $sess['channel_id']; + $peerId = $sess['peer_id']; + + // Clean up file-based participants + $p_file = __DIR__ . "/../data/" . $room . ".participants.json"; + if (file_exists($p_file)) { + $raw = @file_get_contents($p_file); + if ($raw) { + $ps = json_decode($raw, true); + if (is_array($ps)) { + $found = false; + if (isset($ps[$peerId])) { + unset($ps[$peerId]); + $found = true; + } + // Also cleanup by user_id just in case + foreach ($ps as $id => $p) { + if (($p['user_id'] ?? 0) == $user['id']) { + unset($ps[$id]); + $found = true; + } + } + if ($found) { + file_put_contents($p_file, json_encode($ps), LOCK_EX); + } + } + } + } + } + + // Clean up DB session + db()->prepare("DELETE FROM voice_sessions WHERE user_id = ?")->execute([$user['id']]); + } catch (Exception $e) { + // Ignore errors during logout cleanup + } +} + session_destroy(); header('Location: login.php'); exit; diff --git a/data/3.participants.json b/data/3.participants.json index a37aac5..a5beed1 100644 --- a/data/3.participants.json +++ b/data/3.participants.json @@ -1 +1 @@ -{"c936fedf9810ea51":{"id":"c936fedf9810ea51","user_id":2,"name":"swefpifh ᵇʰᶠʳ","avatar_url":"","last_seen":1771433918233},"abad0e2fe8760303":{"id":"abad0e2fe8760303","user_id":0,"name":"swefheim","avatar_url":"","last_seen":1771433918373}} \ No newline at end of file +{"3356a3073b77f72d":{"id":"3356a3073b77f72d","user_id":3,"name":"swefheim","avatar_url":"","last_seen":1771441757997},"d58aa0268cc9e8d0":{"id":"d58aa0268cc9e8d0","user_id":2,"name":"swefpifh ᵇʰᶠʳ","avatar_url":"","last_seen":1771441758062}} \ No newline at end of file diff --git a/index.php b/index.php index 32b0534..eff5509 100644 --- a/index.php +++ b/index.php @@ -316,8 +316,10 @@ if ($is_dm_view) { SELECT vs.channel_id, vs.user_id, u.username, u.display_name, u.avatar_url FROM voice_sessions vs JOIN users u ON vs.user_id = u.id + WHERE vs.last_seen > ? "); - $stmt_vs->execute(); + $stale_db_time = (int) floor(microtime(true) * 1000) - 15000; + $stmt_vs->execute([$stale_db_time]); $voice_sessions = $stmt_vs->fetchAll(); $voice_users_by_channel = []; foreach($voice_sessions as $vs) { @@ -553,7 +555,7 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
">
- +
@@ -634,7 +636,8 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? ''; - + +
diff --git a/requests.log b/requests.log index b6413e9..83a0242 100644 --- a/requests.log +++ b/requests.log @@ -657,3 +657,22 @@ 2026-02-18 16:56:38 - GET /index.php - POST: [] 2026-02-18 16:57:15 - GET /?fl_project=38443 - POST: [] 2026-02-18 16:57:23 - GET /index.php - POST: [] +2026-02-18 17:00:18 - GET /?fl_project=38443 - POST: [] +2026-02-18 18:08:55 - GET /index.php - POST: [] +2026-02-18 18:09:11 - GET /index.php - POST: [] +2026-02-18 18:09:30 - GET /index.php - POST: [] +2026-02-18 18:09:31 - GET /index.php - POST: [] +2026-02-18 18:10:17 - GET /index.php - POST: [] +2026-02-18 18:16:02 - GET /?fl_project=38443 - POST: [] +2026-02-18 18:24:35 - GET /index.php - POST: [] +2026-02-18 18:24:40 - GET /index.php - POST: [] +2026-02-18 18:24:50 - GET /index.php - POST: [] +2026-02-18 18:25:18 - GET /index.php - POST: [] +2026-02-18 18:25:21 - GET /index.php - POST: [] +2026-02-18 18:28:00 - GET /?fl_project=38443 - POST: [] +2026-02-18 18:39:54 - GET /?fl_project=38443 - POST: [] +2026-02-18 18:43:31 - GET / - POST: [] +2026-02-18 18:44:03 - GET /?fl_project=38443 - POST: [] +2026-02-18 19:06:59 - GET /index.php - POST: [] +2026-02-18 19:07:22 - GET /index.php - POST: [] +2026-02-18 19:08:00 - GET /index.php?server_id=1&channel_id=6 - POST: []