PHP Code Editor
<?php // --- CONFIG & DATABASE (JSON) --- $dbDir = __DIR__ . '/db'; $dbFile = $dbDir . '/peerjs-users.json'; if (!is_dir($dbDir)) { @mkdir($dbDir, 0777, true); } if (isset($_GET['reg'])) { $id = preg_replace('/[^a-zA-Z0-9-]/', '', $_GET['reg']); $nick = strip_tags($_GET['nick'] ?? 'Guest'); $data = file_exists($dbFile) ? json_decode(file_get_contents($dbFile), true) : []; $data[$id] = ['nick' => $nick, 'time' => time()]; $active = array_filter($data, function($u) { return (time() - $u['time']) < 15; }); file_put_contents($dbFile, json_encode($active)); exit; } if (isset($_GET['get'])) { header('Content-Type: application/json'); $data = file_exists($dbFile) ? json_decode(file_get_contents($dbFile), true) : []; echo json_encode($data); exit; } ?> <!DOCTYPE html> <html lang="id"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> <title>Voice Pro Mobile</title> <!-- SELALU MENGGUNAKAN PEERJS 1.5.1 SESUAI PERMINTAAN --> <script src="https://cdnjs.cloudflare.com/ajax/libs/peerjs/1.5.1/peerjs.min.js"></script> <style> :root { --primary: #6366f1; --bg: #0f172a; --card: #1e293b; --text: #f8fafc; --success: #22c55e; --danger: #ef4444; } body { font-family: 'Inter', sans-serif; background: var(--bg); color: var(--text); margin: 0; overflow: hidden; } /* BLACK SCREEN OVERLAY (SIMULASI PROXIMITY) */ #proximity-overlay { position: fixed; inset: 0; background: black; z-index: 9999; display: none; pointer-events: all; } #proximity-overlay.active { display: block; } /* LOGIN SCREEN */ #login-screen { position: fixed; inset: 0; background: var(--bg); z-index: 100; display: flex; align-items: center; justify-content: center; padding: 20px; } .login-card { background: var(--card); padding: 40px 30px; border-radius: 30px; width: 100%; max-width: 350px; text-align: center; border: 1px solid #334155; } .login-card input { width: 100%; padding: 15px; border-radius: 12px; border: 1px solid #334155; background: #0f172a; color: white; margin: 20px 0; box-sizing: border-box; font-size: 16px; outline: none; } .btn-start { width: 100%; padding: 15px; border-radius: 12px; border: none; background: var(--primary); color: white; font-weight: bold; cursor: pointer; } /* MAIN UI */ .app-container { width: 100%; max-width: 450px; height: 100vh; margin: auto; display: flex; flex-direction: column; opacity: 0; transition: 0.5s; pointer-events: none; } .app-container.active { opacity: 1; pointer-events: auto; } header { padding: 30px 20px; background: linear-gradient(135deg, #6366f1 0%, #4338ca 100%); border-bottom-left-radius: 30px; border-bottom-right-radius: 30px; } #my-nick-display { font-weight: 800; font-size: 22px; display: block; } .content { padding: 20px; flex-grow: 1; overflow-y: auto; padding-bottom: 100px; } .user-card { background: var(--card); border-radius: 20px; padding: 16px; margin-bottom: 12px; display: flex; align-items: center; justify-content: space-between; border: 1px solid #334155; } .btn-call { width: 48px; height: 48px; border-radius: 16px; border: none; background: var(--success); color: white; cursor: pointer; display: flex; align-items: center; justify-content: center; } .btn-hangup { background: var(--danger) !important; display: none; margin-left: auto; width: 38px; height: 38px; border-radius: 12px; } .status-bar { position: fixed; bottom: 20px; left: 50%; transform: translateX(-50%); width: calc(100% - 40px); max-width: 410px; background: #1e293b; padding: 14px 20px; border-radius: 20px; border: 1px solid #334155; display: flex; align-items: center; gap: 12px; font-size: 13px; z-index: 50; } .dot { width: 10px; height: 10px; background: #ef4444; border-radius: 50%; } .dot.online { background: var(--success); box-shadow: 0 0 12px var(--success); } </style> </head> <body> <div id="proximity-overlay"></div> <div id="login-screen"> <div class="login-card"> <h2>Voice Connect</h2> <input type="text" id="nick-input" placeholder="Nickname..." maxlength="12"> <button class="btn-start" onclick="startApp()">Masuk</button> </div> </div> <div class="app-container" id="main-app"> <header> <span id="my-nick-display">Username</span> <div class="my-status-box" style="font-size:11px; opacity:0.7; margin-top:5px;">ID: <span id="my-peer-id">Connecting...</span></div> </header> <div class="content"><div id="user-display"></div></div> <div class="status-bar"> <div id="status-dot" class="dot"></div> <span id="status-text">Online</span> <button id="hangup-btn" class="btn-call btn-hangup" onclick="hangUp()">✖</button> </div> </div> <audio id="remote-audio" autoplay playsinline></audio> <script> let myPeer, myPeerId, userNick, currentCall, localStream; function startApp() { const input = document.getElementById('nick-input'); if (input.value.trim().length < 3) return alert("Nickname min 3 karakter!"); userNick = input.value.trim(); document.getElementById('my-nick-display').innerText = userNick; document.getElementById('login-screen').style.display = 'none'; document.getElementById('main-app').classList.add('active'); initPeer(); initSensors(); } function initPeer() { myPeer = new Peer('VC-' + Math.floor(1000 + Math.random() * 9000), { config: {'iceServers': [{ 'urls': 'stun:stun.l.google.com:19302' }]} }); myPeer.on('open', (id) => { myPeerId = id; document.getElementById('my-peer-id').innerText = id; document.getElementById('status-dot').classList.add('online'); setInterval(() => { fetch(`?reg=${id}&nick=${encodeURIComponent(userNick)}`); }, 5000); }); myPeer.on('call', (call) => { if (confirm("Terima Panggilan?")) { navigator.mediaDevices.getUserMedia({ audio: { echoCancellation: true, noiseSuppression: true } }).then(stream => { localStream = stream; currentCall = call; call.answer(stream); handleCall(call); }); } }); } function handleCall(call) { document.getElementById('status-text').innerText = "🟢 Berbicara..."; document.getElementById('hangup-btn').style.display = 'flex'; call.on('stream', (rs) => { const audio = document.getElementById('remote-audio'); audio.srcObject = rs; audio.volume = 0.8; // Earpiece trick }); call.on('close', resetUI); } function dial(tid) { document.getElementById('status-text').innerText = "📞 Memanggil..."; navigator.mediaDevices.getUserMedia({ audio: { echoCancellation: true } }).then(stream => { localStream = stream; currentCall = myPeer.call(tid, stream); handleCall(currentCall); }); } function hangUp() { if (currentCall) currentCall.close(); resetUI(); } function resetUI() { if (localStream) localStream.getTracks().forEach(t => t.stop()); document.getElementById('remote-audio').srcObject = null; document.getElementById('status-text').innerText = "Online"; document.getElementById('hangup-btn').style.display = 'none'; document.getElementById('proximity-overlay').classList.remove('active'); } // KHUSUS ANDROID CHROME SENSOR function initSensors() { // Trik menggunakan Ambient Light Sensor jika Proximity diblokir if ('AmbientLightSensor' in window) { try { const sensor = new AmbientLightSensor({ frequency: 10 }); sensor.onreading = () => { if (currentCall) { // Jika gelap (< 5 lux) = HP nempel di kuping if (sensor.illuminance < 5) document.getElementById('proximity-overlay').classList.add('active'); else document.getElementById('proximity-overlay').classList.remove('active'); } }; sensor.start(); } catch (e) { console.log("Sensor API restricted"); } } } setInterval(() => { if (!myPeerId) return; fetch('?get=1').then(r => r.json()).then(data => { const container = document.getElementById('user-display'); container.innerHTML = ""; Object.keys(data).forEach(pid => { if (pid !== myPeerId) { container.innerHTML += `<div class="user-card"> <div class="user-info"><div class="avatar">👤</div> <div><div class="username">${data[pid].nick}</div><div class="id-sub">${pid}</div></div></div> <button class="btn-call" onclick="dial('${pid}')">📞</button></div>`; } }); }); }, 3000); </script> </body> </html>
Run Code
<?php // --- CONFIG & DATABASE (JSON) --- $dbDir = __DIR__ . '/db'; $dbFile = $dbDir . '/peerjs-users.json'; if (!is_dir($dbDir)) { @mkdir($dbDir, 0777, true); } if (isset($_GET['reg'])) { $id = preg_replace('/[^a-zA-Z0-9-]/', '', $_GET['reg']); $nick = strip_tags($_GET['nick'] ?? 'Guest'); $data = file_exists($dbFile) ? json_decode(file_get_contents($dbFile), true) : []; $data[$id] = ['nick' => $nick, 'time' => time()]; $active = array_filter($data, function($u) { return (time() - $u['time']) < 15; }); file_put_contents($dbFile, json_encode($active)); exit; } if (isset($_GET['get'])) { header('Content-Type: application/json'); $data = file_exists($dbFile) ? json_decode(file_get_contents($dbFile), true) : []; echo json_encode($data); exit; } ?> <!DOCTYPE html> <html lang="id"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> <title>Voice Pro Mobile</title> <!-- SELALU MENGGUNAKAN PEERJS 1.5.1 SESUAI PERMINTAAN --> <script src="https://cdnjs.cloudflare.com/ajax/libs/peerjs/1.5.1/peerjs.min.js"></script> <style> :root { --primary: #6366f1; --bg: #0f172a; --card: #1e293b; --text: #f8fafc; --success: #22c55e; --danger: #ef4444; } body { font-family: 'Inter', sans-serif; background: var(--bg); color: var(--text); margin: 0; overflow: hidden; } /* BLACK SCREEN OVERLAY (SIMULASI PROXIMITY) */ #proximity-overlay { position: fixed; inset: 0; background: black; z-index: 9999; display: none; pointer-events: all; } #proximity-overlay.active { display: block; } /* LOGIN SCREEN */ #login-screen { position: fixed; inset: 0; background: var(--bg); z-index: 100; display: flex; align-items: center; justify-content: center; padding: 20px; } .login-card { background: var(--card); padding: 40px 30px; border-radius: 30px; width: 100%; max-width: 350px; text-align: center; border: 1px solid #334155; } .login-card input { width: 100%; padding: 15px; border-radius: 12px; border: 1px solid #334155; background: #0f172a; color: white; margin: 20px 0; box-sizing: border-box; font-size: 16px; outline: none; } .btn-start { width: 100%; padding: 15px; border-radius: 12px; border: none; background: var(--primary); color: white; font-weight: bold; cursor: pointer; } /* MAIN UI */ .app-container { width: 100%; max-width: 450px; height: 100vh; margin: auto; display: flex; flex-direction: column; opacity: 0; transition: 0.5s; pointer-events: none; } .app-container.active { opacity: 1; pointer-events: auto; } header { padding: 30px 20px; background: linear-gradient(135deg, #6366f1 0%, #4338ca 100%); border-bottom-left-radius: 30px; border-bottom-right-radius: 30px; } #my-nick-display { font-weight: 800; font-size: 22px; display: block; } .content { padding: 20px; flex-grow: 1; overflow-y: auto; padding-bottom: 100px; } .user-card { background: var(--card); border-radius: 20px; padding: 16px; margin-bottom: 12px; display: flex; align-items: center; justify-content: space-between; border: 1px solid #334155; } .btn-call { width: 48px; height: 48px; border-radius: 16px; border: none; background: var(--success); color: white; cursor: pointer; display: flex; align-items: center; justify-content: center; } .btn-hangup { background: var(--danger) !important; display: none; margin-left: auto; width: 38px; height: 38px; border-radius: 12px; } .status-bar { position: fixed; bottom: 20px; left: 50%; transform: translateX(-50%); width: calc(100% - 40px); max-width: 410px; background: #1e293b; padding: 14px 20px; border-radius: 20px; border: 1px solid #334155; display: flex; align-items: center; gap: 12px; font-size: 13px; z-index: 50; } .dot { width: 10px; height: 10px; background: #ef4444; border-radius: 50%; } .dot.online { background: var(--success); box-shadow: 0 0 12px var(--success); } </style> </head> <body> <div id="proximity-overlay"></div> <div id="login-screen"> <div class="login-card"> <h2>Voice Connect</h2> <input type="text" id="nick-input" placeholder="Nickname..." maxlength="12"> <button class="btn-start" onclick="startApp()">Masuk</button> </div> </div> <div class="app-container" id="main-app"> <header> <span id="my-nick-display">Username</span> <div class="my-status-box" style="font-size:11px; opacity:0.7; margin-top:5px;">ID: <span id="my-peer-id">Connecting...</span></div> </header> <div class="content"><div id="user-display"></div></div> <div class="status-bar"> <div id="status-dot" class="dot"></div> <span id="status-text">Online</span> <button id="hangup-btn" class="btn-call btn-hangup" onclick="hangUp()">✖</button> </div> </div> <audio id="remote-audio" autoplay playsinline></audio> <script> let myPeer, myPeerId, userNick, currentCall, localStream; function startApp() { const input = document.getElementById('nick-input'); if (input.value.trim().length < 3) return alert("Nickname min 3 karakter!"); userNick = input.value.trim(); document.getElementById('my-nick-display').innerText = userNick; document.getElementById('login-screen').style.display = 'none'; document.getElementById('main-app').classList.add('active'); initPeer(); initSensors(); } function initPeer() { myPeer = new Peer('VC-' + Math.floor(1000 + Math.random() * 9000), { config: {'iceServers': [{ 'urls': 'stun:stun.l.google.com:19302' }]} }); myPeer.on('open', (id) => { myPeerId = id; document.getElementById('my-peer-id').innerText = id; document.getElementById('status-dot').classList.add('online'); setInterval(() => { fetch(`?reg=${id}&nick=${encodeURIComponent(userNick)}`); }, 5000); }); myPeer.on('call', (call) => { if (confirm("Terima Panggilan?")) { navigator.mediaDevices.getUserMedia({ audio: { echoCancellation: true, noiseSuppression: true } }).then(stream => { localStream = stream; currentCall = call; call.answer(stream); handleCall(call); }); } }); } function handleCall(call) { document.getElementById('status-text').innerText = "🟢 Berbicara..."; document.getElementById('hangup-btn').style.display = 'flex'; call.on('stream', (rs) => { const audio = document.getElementById('remote-audio'); audio.srcObject = rs; audio.volume = 0.8; // Earpiece trick }); call.on('close', resetUI); } function dial(tid) { document.getElementById('status-text').innerText = "📞 Memanggil..."; navigator.mediaDevices.getUserMedia({ audio: { echoCancellation: true } }).then(stream => { localStream = stream; currentCall = myPeer.call(tid, stream); handleCall(currentCall); }); } function hangUp() { if (currentCall) currentCall.close(); resetUI(); } function resetUI() { if (localStream) localStream.getTracks().forEach(t => t.stop()); document.getElementById('remote-audio').srcObject = null; document.getElementById('status-text').innerText = "Online"; document.getElementById('hangup-btn').style.display = 'none'; document.getElementById('proximity-overlay').classList.remove('active'); } // KHUSUS ANDROID CHROME SENSOR function initSensors() { // Trik menggunakan Ambient Light Sensor jika Proximity diblokir if ('AmbientLightSensor' in window) { try { const sensor = new AmbientLightSensor({ frequency: 10 }); sensor.onreading = () => { if (currentCall) { // Jika gelap (< 5 lux) = HP nempel di kuping if (sensor.illuminance < 5) document.getElementById('proximity-overlay').classList.add('active'); else document.getElementById('proximity-overlay').classList.remove('active'); } }; sensor.start(); } catch (e) { console.log("Sensor API restricted"); } } } setInterval(() => { if (!myPeerId) return; fetch('?get=1').then(r => r.json()).then(data => { const container = document.getElementById('user-display'); container.innerHTML = ""; Object.keys(data).forEach(pid => { if (pid !== myPeerId) { container.innerHTML += `<div class="user-card"> <div class="user-info"><div class="avatar">👤</div> <div><div class="username">${data[pid].nick}</div><div class="id-sub">${pid}</div></div></div> <button class="btn-call" onclick="dial('${pid}')">📞</button></div>`; } }); }); }, 3000); </script> </body> </html>
Run Code New Tab
Result