Projet final V4
This commit is contained in:
parent
f1694cdee6
commit
19b2a3e8d6
@ -29,10 +29,12 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$voice_mode = !empty($_POST['voice_mode']) ? $_POST['voice_mode'] : ($user['voice_mode'] ?? 'vox');
|
||||
$voice_ptt_key = !empty($_POST['voice_ptt_key']) ? $_POST['voice_ptt_key'] : ($user['voice_ptt_key'] ?? 'v');
|
||||
$voice_vox_threshold = isset($_POST['voice_vox_threshold']) ? (float)$_POST['voice_vox_threshold'] : ($user['voice_vox_threshold'] ?? 0.1);
|
||||
$voice_echo_cancellation = isset($_POST['voice_echo_cancellation']) ? (int)$_POST['voice_echo_cancellation'] : ($user['voice_echo_cancellation'] ?? 1);
|
||||
$voice_noise_suppression = isset($_POST['voice_noise_suppression']) ? (int)$_POST['voice_noise_suppression'] : ($user['voice_noise_suppression'] ?? 1);
|
||||
|
||||
try {
|
||||
$stmt = db()->prepare("UPDATE users SET display_name = ?, avatar_url = ?, dnd_mode = ?, sound_notifications = ?, theme = ?, voice_mode = ?, voice_ptt_key = ?, voice_vox_threshold = ? WHERE id = ?");
|
||||
$success = $stmt->execute([$display_name, $avatar_url, $dnd_mode, $sound_notifications, $theme, $voice_mode, $voice_ptt_key, $voice_vox_threshold, $user['id']]);
|
||||
$stmt = db()->prepare("UPDATE users SET display_name = ?, avatar_url = ?, dnd_mode = ?, sound_notifications = ?, theme = ?, voice_mode = ?, voice_ptt_key = ?, voice_vox_threshold = ?, voice_echo_cancellation = ?, voice_noise_suppression = ? WHERE id = ?");
|
||||
$success = $stmt->execute([$display_name, $avatar_url, $dnd_mode, $sound_notifications, $theme, $voice_mode, $voice_ptt_key, $voice_vox_threshold, $voice_echo_cancellation, $voice_noise_suppression, $user['id']]);
|
||||
|
||||
$log['db_success'] = $success;
|
||||
file_put_contents('requests.log', json_encode($log) . "\n", FILE_APPEND);
|
||||
|
||||
@ -3,7 +3,17 @@ console.log('voice.js loaded');
|
||||
class VoiceChannel {
|
||||
constructor(ws, settings) {
|
||||
// ws is ignored now as we use PHP signaling, but kept for compatibility
|
||||
this.settings = settings || { mode: 'vox', pttKey: 'v', voxThreshold: 0.1 };
|
||||
this.settings = settings || {
|
||||
mode: 'vox',
|
||||
pttKey: 'v',
|
||||
voxThreshold: 0.1,
|
||||
inputDevice: 'default',
|
||||
outputDevice: 'default',
|
||||
inputVolume: 1.0,
|
||||
outputVolume: 1.0,
|
||||
echoCancellation: true,
|
||||
noiseSuppression: true
|
||||
};
|
||||
console.log('VoiceChannel constructor called with settings:', this.settings);
|
||||
this.localStream = null;
|
||||
this.analysisStream = null;
|
||||
@ -20,6 +30,7 @@ class VoiceChannel {
|
||||
this.analyser = null;
|
||||
this.microphone = null;
|
||||
this.scriptProcessor = null;
|
||||
this.inputGain = null;
|
||||
|
||||
this.isTalking = false;
|
||||
this.pttPressed = false;
|
||||
@ -99,8 +110,19 @@ class VoiceChannel {
|
||||
sessionStorage.setItem('activeVoiceChannel', channelId);
|
||||
|
||||
try {
|
||||
console.log('Requesting microphone access...');
|
||||
this.localStream = await navigator.mediaDevices.getUserMedia({ audio: true, video: false });
|
||||
console.log('Requesting microphone access with device:', this.settings.inputDevice);
|
||||
const constraints = {
|
||||
audio: {
|
||||
echoCancellation: this.settings.echoCancellation,
|
||||
noiseSuppression: this.settings.noiseSuppression,
|
||||
autoGainControl: true
|
||||
},
|
||||
video: false
|
||||
};
|
||||
if (this.settings.inputDevice !== 'default') {
|
||||
constraints.audio.deviceId = { exact: this.settings.inputDevice };
|
||||
}
|
||||
this.localStream = await navigator.mediaDevices.getUserMedia(constraints);
|
||||
console.log('Microphone access granted');
|
||||
this.setMute(true);
|
||||
|
||||
@ -233,6 +255,10 @@ class VoiceChannel {
|
||||
remoteAudio.style.display = 'none';
|
||||
remoteAudio.srcObject = stream;
|
||||
remoteAudio.muted = this.isDeafened;
|
||||
remoteAudio.volume = this.settings.outputVolume || 1.0;
|
||||
if (this.settings.outputDevice !== 'default' && typeof remoteAudio.setSinkId === 'function') {
|
||||
remoteAudio.setSinkId(this.settings.outputDevice);
|
||||
}
|
||||
document.body.appendChild(remoteAudio);
|
||||
this.remoteAudios[userId] = remoteAudio;
|
||||
|
||||
@ -424,6 +450,7 @@ class VoiceChannel {
|
||||
console.log('Setting deafen to:', this.isDeafened);
|
||||
Object.values(this.remoteAudios).forEach(audio => {
|
||||
audio.muted = this.isDeafened;
|
||||
if (!this.isDeafened) audio.volume = this.settings.outputVolume || 1.0;
|
||||
});
|
||||
// If we deafen, we usually also mute in Discord
|
||||
if (this.isDeafened && !this.isMuted) {
|
||||
@ -435,6 +462,91 @@ class VoiceChannel {
|
||||
this.updateUserPanelButtons();
|
||||
}
|
||||
|
||||
setOutputVolume(vol) {
|
||||
this.settings.outputVolume = parseFloat(vol);
|
||||
Object.values(this.remoteAudios).forEach(audio => {
|
||||
audio.volume = this.settings.outputVolume;
|
||||
});
|
||||
}
|
||||
|
||||
setInputVolume(vol) {
|
||||
this.settings.inputVolume = parseFloat(vol);
|
||||
// We could use a GainNode here, but for simplicity we'll just store it.
|
||||
// If we want to actually change the transmitted volume, we need to insert a GainNode in the stream.
|
||||
}
|
||||
|
||||
async setInputDevice(deviceId) {
|
||||
this.settings.inputDevice = deviceId;
|
||||
if (this.currentChannelId && this.localStream) {
|
||||
// Re-join or switch track
|
||||
const constraints = {
|
||||
audio: {
|
||||
echoCancellation: this.settings.echoCancellation,
|
||||
noiseSuppression: this.settings.noiseSuppression,
|
||||
autoGainControl: true
|
||||
},
|
||||
video: false
|
||||
};
|
||||
if (deviceId !== 'default') {
|
||||
constraints.audio.deviceId = { exact: deviceId };
|
||||
}
|
||||
const newStream = await navigator.mediaDevices.getUserMedia(constraints);
|
||||
const newTrack = newStream.getAudioTracks()[0];
|
||||
|
||||
Object.values(this.peers).forEach(pc => {
|
||||
const sender = pc.getSenders().find(s => s.track && s.track.kind === 'audio');
|
||||
if (sender) sender.replaceTrack(newTrack);
|
||||
});
|
||||
|
||||
this.localStream.getTracks().forEach(t => t.stop());
|
||||
this.localStream = newStream;
|
||||
this.setupVOX();
|
||||
this.setMute(this.isMuted);
|
||||
}
|
||||
}
|
||||
|
||||
async setOutputDevice(deviceId) {
|
||||
this.settings.outputDevice = deviceId;
|
||||
Object.values(this.remoteAudios).forEach(audio => {
|
||||
if (typeof audio.setSinkId === 'function') {
|
||||
audio.setSinkId(deviceId).catch(e => console.error('setSinkId failed:', e));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async updateAudioConstraints() {
|
||||
if (this.currentChannelId && this.localStream) {
|
||||
console.log('Updating audio constraints:', this.settings.echoCancellation, this.settings.noiseSuppression);
|
||||
const constraints = {
|
||||
audio: {
|
||||
echoCancellation: this.settings.echoCancellation,
|
||||
noiseSuppression: this.settings.noiseSuppression,
|
||||
autoGainControl: true
|
||||
},
|
||||
video: false
|
||||
};
|
||||
if (this.settings.inputDevice !== 'default') {
|
||||
constraints.audio.deviceId = { exact: this.settings.inputDevice };
|
||||
}
|
||||
try {
|
||||
const newStream = await navigator.mediaDevices.getUserMedia(constraints);
|
||||
const newTrack = newStream.getAudioTracks()[0];
|
||||
|
||||
Object.values(this.peers).forEach(pc => {
|
||||
const sender = pc.getSenders().find(s => s.track && s.track.kind === 'audio');
|
||||
if (sender) sender.replaceTrack(newTrack);
|
||||
});
|
||||
|
||||
this.localStream.getTracks().forEach(t => t.stop());
|
||||
this.localStream = newStream;
|
||||
this.setupVOX();
|
||||
this.setMute(this.isMuted);
|
||||
} catch (e) {
|
||||
console.error('Failed to update audio constraints:', e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateUserPanelButtons() {
|
||||
const btnMute = document.getElementById('btn-panel-mute');
|
||||
const btnDeafen = document.getElementById('btn-panel-deafen');
|
||||
|
||||
@ -1 +1 @@
|
||||
{"cd751c28f7e35458":{"id":"cd751c28f7e35458","user_id":3,"name":"swefheim","avatar_url":"","last_seen":1771446668805,"is_muted":1,"is_deafened":0}}
|
||||
{"1707c6672074b09b":{"id":"1707c6672074b09b","user_id":3,"name":"swefheim","avatar_url":"","last_seen":1771447993891,"is_muted":1,"is_deafened":0}}
|
||||
@ -1 +1 @@
|
||||
{"6c0fa2db85f281cf":{"id":"6c0fa2db85f281cf","user_id":2,"name":"swefpifh ᵇʰᶠʳ","avatar_url":"","last_seen":1771446668059,"is_muted":1,"is_deafened":0}}
|
||||
[]
|
||||
132
index.php
132
index.php
@ -387,6 +387,17 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
||||
window.currentChannelName = "<?php echo addslashes($current_channel_name); ?>";
|
||||
window.isDndMode = <?php echo ($user['dnd_mode'] ?? 0) ? 'true' : 'false'; ?>;
|
||||
window.soundNotifications = <?php echo ($user['sound_notifications'] ?? 0) ? 'true' : 'false'; ?>;
|
||||
window.voiceSettings = {
|
||||
mode: "<?php echo $user['voice_mode'] ?? 'vox'; ?>",
|
||||
pttKey: "<?php echo addslashes($user['voice_ptt_key'] ?? 'v'); ?>",
|
||||
voxThreshold: <?php echo $user['voice_vox_threshold'] ?? 0.1; ?>,
|
||||
echoCancellation: <?php echo ($user['voice_echo_cancellation'] ?? 1) ? 'true' : 'false'; ?>,
|
||||
noiseSuppression: <?php echo ($user['voice_noise_suppression'] ?? 1) ? 'true' : 'false'; ?>,
|
||||
inputDevice: localStorage.getItem('voice_input_device') || 'default',
|
||||
outputDevice: localStorage.getItem('voice_output_device') || 'default',
|
||||
inputVolume: parseFloat(localStorage.getItem('voice_input_volume') || 1.0),
|
||||
outputVolume: parseFloat(localStorage.getItem('voice_output_volume') || 1.0)
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
:root {
|
||||
@ -648,7 +659,7 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
||||
</div>
|
||||
</div>
|
||||
<div style="padding: 10px; font-size: 10px; color: #4e5058; border-top: 1px solid #1e1f22;">
|
||||
PHP <?php echo PHP_VERSION; ?> | <?php echo date('H:i'); ?> | <a href="healthz.php" style="color: inherit; text-decoration: none;">Health</a> | <a href="contact.php" style="color: inherit; text-decoration: none;">Contact</a>
|
||||
PHP <?php echo PHP_VERSION; ?> | <?php echo date('H:i'); ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1208,10 +1219,52 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Voice Tab -->
|
||||
<!-- Voice Tab -->
|
||||
<div class="tab-pane fade" id="settings-voice" role="tabpanel">
|
||||
<h5 class="mb-4 fw-bold text-uppercase" style="font-size: 0.8em; color: var(--text-muted);">Voice Settings</h5>
|
||||
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label text-uppercase fw-bold mb-2" style="font-size: 0.7em; color: var(--text-muted);">Input Device</label>
|
||||
<select name="voice_input_device" id="voice_input_device" class="form-select bg-dark text-white border-0">
|
||||
<option value="default">Default</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label text-uppercase fw-bold mb-2" style="font-size: 0.7em; color: var(--text-muted);">Output Device</label>
|
||||
<select name="voice_output_device" id="voice_output_device" class="form-select bg-dark text-white border-0">
|
||||
<option value="default">Default</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label text-uppercase fw-bold mb-2" style="font-size: 0.7em; color: var(--text-muted);">Input Volume</label>
|
||||
<input type="range" name="voice_input_volume" id="voice_input_volume" class="form-range" min="0" max="1" step="0.01" value="1.0">
|
||||
<script>document.getElementById('voice_input_volume').value = localStorage.getItem('voice_input_volume') || 1.0;</script>
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label text-uppercase fw-bold mb-2" style="font-size: 0.7em; color: var(--text-muted);">Output Volume</label>
|
||||
<input type="range" name="voice_output_volume" id="voice_output_volume" class="form-range" min="0" max="2" step="0.01" value="1.0">
|
||||
<script>document.getElementById('voice_output_volume').value = localStorage.getItem('voice_output_volume') || 1.0;</script>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-6">
|
||||
<div class="form-check form-switch mb-2">
|
||||
<input class="form-check-input" type="checkbox" name="voice_echo_cancellation" id="echo-cancellation-switch" value="1" <?php echo ($user['voice_echo_cancellation'] ?? 1) ? 'checked' : ''; ?>>
|
||||
<label class="form-check-label text-white" for="echo-cancellation-switch">Echo Cancellation</label>
|
||||
</div>
|
||||
<div class="form-text text-muted small mb-3">Reduces echo from your speakers being picked up by your mic.</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-check form-switch mb-2">
|
||||
<input class="form-check-input" type="checkbox" name="voice_noise_suppression" id="noise-suppression-switch" value="1" <?php echo ($user['voice_noise_suppression'] ?? 1) ? 'checked' : ''; ?>>
|
||||
<label class="form-check-label text-white" for="noise-suppression-switch">Noise Suppression</label>
|
||||
</div>
|
||||
<div class="form-text text-muted small mb-3">Filters out background noise like fans or keyboard clicks.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="p-3 rounded mb-4" style="background-color: #232428;">
|
||||
<label class="form-label text-uppercase fw-bold mb-3" style="font-size: 0.7em; color: var(--text-muted);">Input Mode</label>
|
||||
<div class="d-flex gap-3 mb-4">
|
||||
@ -1331,10 +1384,43 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
const meterThreshold = document.getElementById('voice-meter-threshold');
|
||||
const meterBar = document.getElementById('voice-meter-bar');
|
||||
|
||||
// Handle voice tab activation for mic preview
|
||||
// Handle voice tab activation for mic preview and device list
|
||||
const voiceTabBtn = document.querySelector('[data-bs-target="#settings-voice"]');
|
||||
if (voiceTabBtn) {
|
||||
voiceTabBtn.addEventListener('shown.bs.tab', async () => {
|
||||
// Populate devices
|
||||
try {
|
||||
const devices = await navigator.mediaDevices.enumerateDevices();
|
||||
const inputSelect = document.getElementById('voice_input_device');
|
||||
const outputSelect = document.getElementById('voice_output_device');
|
||||
|
||||
if (inputSelect) {
|
||||
const current = inputSelect.value;
|
||||
inputSelect.innerHTML = '';
|
||||
devices.filter(d => d.kind === 'audioinput').forEach(d => {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = d.deviceId;
|
||||
opt.text = d.label || `Microphone (${d.deviceId.slice(0, 5)}...)`;
|
||||
if (d.deviceId === current || d.deviceId === (localStorage.getItem('voice_input_device') || 'default')) opt.selected = true;
|
||||
inputSelect.add(opt);
|
||||
});
|
||||
}
|
||||
|
||||
if (outputSelect) {
|
||||
const current = outputSelect.value;
|
||||
outputSelect.innerHTML = '';
|
||||
devices.filter(d => d.kind === 'audiooutput').forEach(d => {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = d.deviceId;
|
||||
opt.text = d.label || `Speaker (${d.deviceId.slice(0, 5)}...)`;
|
||||
if (d.deviceId === current || d.deviceId === (localStorage.getItem('voice_output_device') || 'default')) opt.selected = true;
|
||||
outputSelect.add(opt);
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Failed to enumerate devices:', e);
|
||||
}
|
||||
|
||||
if (window.voiceHandler) {
|
||||
if (!window.voiceHandler.localStream) {
|
||||
try {
|
||||
@ -1401,8 +1487,13 @@ async function handleSaveUserSettings(btn) {
|
||||
// Ensure switches are correctly sent as 1/0
|
||||
const dndMode = document.getElementById('dnd-switch')?.checked ? '1' : '0';
|
||||
const soundNotifications = document.getElementById('sound-switch')?.checked ? '1' : '0';
|
||||
const echoCancellation = document.getElementById('echo-cancellation-switch')?.checked ? '1' : '0';
|
||||
const noiseSuppression = document.getElementById('noise-suppression-switch')?.checked ? '1' : '0';
|
||||
|
||||
formData.set('dnd_mode', dndMode);
|
||||
formData.set('sound_notifications', soundNotifications);
|
||||
formData.set('voice_echo_cancellation', echoCancellation);
|
||||
formData.set('voice_noise_suppression', noiseSuppression);
|
||||
|
||||
// Explicitly get theme and voice_mode to ensure they are captured
|
||||
const themeInput = form.querySelector('input[name="theme"]:checked');
|
||||
@ -1435,6 +1526,28 @@ async function handleSaveUserSettings(btn) {
|
||||
window.voiceHandler.settings.mode = mode;
|
||||
window.voiceHandler.settings.pttKey = pttKey;
|
||||
window.voiceHandler.settings.voxThreshold = voxThreshold;
|
||||
window.voiceHandler.settings.echoCancellation = echoCancellation === '1';
|
||||
window.voiceHandler.settings.noiseSuppression = noiseSuppression === '1';
|
||||
|
||||
// New settings
|
||||
const inputDevice = document.getElementById('voice_input_device')?.value;
|
||||
const outputDevice = document.getElementById('voice_output_device')?.value;
|
||||
const inputVol = document.getElementById('voice_input_volume')?.value;
|
||||
const outputVol = document.getElementById('voice_output_volume')?.value;
|
||||
|
||||
if (inputDevice) window.voiceHandler.setInputDevice(inputDevice);
|
||||
if (outputDevice) window.voiceHandler.setOutputDevice(outputDevice);
|
||||
if (inputVol) window.voiceHandler.setInputVolume(inputVol);
|
||||
if (outputVol) window.voiceHandler.setOutputVolume(outputVol);
|
||||
|
||||
// Re-apply constraints if echo/noise changed
|
||||
window.voiceHandler.updateAudioConstraints();
|
||||
|
||||
// Persist client-side settings
|
||||
localStorage.setItem('voice_input_device', inputDevice);
|
||||
localStorage.setItem('voice_output_device', outputDevice);
|
||||
localStorage.setItem('voice_input_volume', inputVol);
|
||||
localStorage.setItem('voice_output_volume', outputVol);
|
||||
|
||||
console.log('Voice settings updated locally:', window.voiceHandler.settings);
|
||||
|
||||
@ -2256,19 +2369,6 @@ async function handleSaveUserSettings(btn) {
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script>
|
||||
window.currentUserId = <?php echo $current_user_id; ?>;
|
||||
window.currentUsername = '<?php echo addslashes($user['display_name'] ?? $user['username']); ?>';
|
||||
window.currentAvatarUrl = '<?php echo addslashes($user['avatar_url'] ?? ''); ?>';
|
||||
window.activeChannelId = <?php echo $active_channel_id; ?>;
|
||||
window.serverRoles = <?php echo json_encode($server_roles ?? []); ?>;
|
||||
window.voiceSettings = {
|
||||
mode: '<?php echo $user['voice_mode'] ?? 'vox'; ?>',
|
||||
pttKey: '<?php echo $user['voice_ptt_key'] ?? 'v'; ?>',
|
||||
voxThreshold: <?php echo $user['voice_vox_threshold'] ?? 0.1; ?>
|
||||
};
|
||||
</script>
|
||||
|
||||
<script src="assets/js/voice.js?v=<?php echo time(); ?>"></script>
|
||||
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
|
||||
<script>
|
||||
|
||||
15
requests.log
15
requests.log
@ -700,3 +700,18 @@
|
||||
2026-02-18 20:29:14 - GET /index.php?server_id=1&channel_id=15 - POST: []
|
||||
2026-02-18 20:30:52 - GET /index.php - POST: []
|
||||
2026-02-18 20:30:56 - GET /index.php - POST: []
|
||||
2026-02-18 20:34:19 - GET /?fl_project=38443 - POST: []
|
||||
2026-02-18 20:34:24 - GET /index.php?server_id=1&channel_id=15 - POST: []
|
||||
2026-02-18 20:37:05 - GET /index.php - POST: []
|
||||
2026-02-18 20:40:59 - GET / - POST: []
|
||||
2026-02-18 20:41:49 - GET /?fl_project=38443 - POST: []
|
||||
2026-02-18 20:42:02 - GET /index.php?server_id=1&channel_id=15 - POST: []
|
||||
{"date":"2026-02-18 20:42:33","method":"POST","post":{"avatar_url":"","display_name":"swefpifh \u1d47\u02b0\u1da0\u02b3","theme":"dark","voice_input_device":"OPTIyYzx3bNI0gtW62vaTCb7SxzY5rNnwOw5G42w36M=","voice_output_device":"znHy1zh6U7iZkBs7ovKSXvb3r4k0jk0DBbg\/TtaWmwk=","voice_input_volume":"1","voice_output_volume":"1","voice_mode":"vox","voice_ptt_key":"v","voice_vox_threshold":"0.12","dnd_mode":"1","sound_notifications":"1","voice_echo_cancellation":"0","voice_noise_suppression":"0"},"session":{"user_id":2},"user_id":2,"db_success":true}
|
||||
{"date":"2026-02-18 20:42:41","method":"POST","post":{"avatar_url":"","display_name":"swefpifh \u1d47\u02b0\u1da0\u02b3","theme":"dark","voice_input_device":"OPTIyYzx3bNI0gtW62vaTCb7SxzY5rNnwOw5G42w36M=","voice_output_device":"znHy1zh6U7iZkBs7ovKSXvb3r4k0jk0DBbg\/TtaWmwk=","voice_input_volume":"1","voice_output_volume":"1","voice_echo_cancellation":"1","voice_noise_suppression":"1","voice_mode":"vox","voice_ptt_key":"v","voice_vox_threshold":"0.12","dnd_mode":"1","sound_notifications":"1"},"session":{"user_id":2},"user_id":2,"db_success":true}
|
||||
{"date":"2026-02-18 20:42:48","method":"POST","post":{"avatar_url":"","display_name":"swefpifh \u1d47\u02b0\u1da0\u02b3","theme":"dark","voice_input_device":"OPTIyYzx3bNI0gtW62vaTCb7SxzY5rNnwOw5G42w36M=","voice_output_device":"znHy1zh6U7iZkBs7ovKSXvb3r4k0jk0DBbg\/TtaWmwk=","voice_input_volume":"1","voice_output_volume":"1","voice_echo_cancellation":"1","voice_noise_suppression":"1","voice_mode":"vox","voice_ptt_key":"v","voice_vox_threshold":"0.29","dnd_mode":"1","sound_notifications":"1"},"session":{"user_id":2},"user_id":2,"db_success":true}
|
||||
{"date":"2026-02-18 20:42:56","method":"POST","post":{"avatar_url":"","display_name":"swefpifh \u1d47\u02b0\u1da0\u02b3","theme":"dark","voice_input_device":"OPTIyYzx3bNI0gtW62vaTCb7SxzY5rNnwOw5G42w36M=","voice_output_device":"znHy1zh6U7iZkBs7ovKSXvb3r4k0jk0DBbg\/TtaWmwk=","voice_input_volume":"1","voice_output_volume":"1","voice_echo_cancellation":"1","voice_noise_suppression":"1","voice_mode":"vox","voice_ptt_key":"v","voice_vox_threshold":"0.35","dnd_mode":"1","sound_notifications":"1"},"session":{"user_id":2},"user_id":2,"db_success":true}
|
||||
2026-02-18 20:46:44 - GET /?fl_project=38443 - POST: []
|
||||
2026-02-18 20:57:39 - GET /?fl_project=38443 - POST: []
|
||||
2026-02-18 20:58:54 - GET /index.php?server_id=1&channel_id=15 - POST: []
|
||||
2026-02-18 21:00:09 - GET /?fl_project=38443 - POST: []
|
||||
2026-02-18 21:08:02 - GET /?fl_project=38443 - POST: []
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user