Starchik1 commited on
Commit
f6ae838
·
verified ·
1 Parent(s): be15fc2

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +101 -163
main.py CHANGED
@@ -558,18 +558,15 @@ HTML_TEMPLATE = '''
558
  color: white;
559
  }
560
 
561
- .debug-info {
562
- position: fixed;
563
- bottom: 10px;
564
- left: 10px;
565
- background: rgba(0,0,0,0.7);
 
566
  color: white;
567
- padding: 10px;
568
- border-radius: 5px;
569
- font-size: 12px;
570
- max-width: 300px;
571
- display: none;
572
- z-index: 3000;
573
  }
574
  </style>
575
  </head>
@@ -579,9 +576,6 @@ HTML_TEMPLATE = '''
579
  Подключение...
580
  </div>
581
 
582
- <!-- Debug Info -->
583
- <div id="debugInfo" class="debug-info"></div>
584
-
585
  <!-- Login Screen -->
586
  <div id="loginScreen" class="login-screen">
587
  <div style="text-align: center; margin-bottom: 30px;">
@@ -606,7 +600,6 @@ HTML_TEMPLATE = '''
606
  </div>
607
  <div class="call-buttons">
608
  <button class="header-btn" onclick="toggleUsersSidebar()" title="Пользователи">👥</button>
609
- <button class="header-btn" onclick="toggleDebug()" title="Отладка">🐛</button>
610
  </div>
611
  </div>
612
 
@@ -644,7 +637,9 @@ HTML_TEMPLATE = '''
644
  <div class="call-info">
645
  <div class="call-status" id="callStatus">Звонок...</div>
646
  <div class="call-timer" id="callTimer">00:00</div>
647
- <div id="callDebug" style="font-size: 12px; margin-top: 5px;"></div>
 
 
648
  </div>
649
  <div class="call-controls">
650
  <button class="call-control-btn" id="muteBtn" onclick="toggleMute()">🎤</button>
@@ -690,9 +685,8 @@ HTML_TEMPLATE = '''
690
  let isVideoEnabled = true;
691
  let reconnectAttempts = 0;
692
  const MAX_RECONNECT_ATTEMPTS = 5;
693
- let debugEnabled = false;
694
 
695
- // Упрощенная конфигурация WebRTC - только STUN серверы
696
  const rtcConfig = {
697
  iceServers: [
698
  { urls: 'stun:stun.l.google.com:19302' },
@@ -701,9 +695,7 @@ HTML_TEMPLATE = '''
701
  { urls: 'stun:stun3.l.google.com:19302' },
702
  { urls: 'stun:stun4.l.google.com:19302' }
703
  ],
704
- iceCandidatePoolSize: 10,
705
- bundlePolicy: 'max-bundle',
706
- rtcpMuxPolicy: 'require'
707
  };
708
 
709
  function initializeSocket() {
@@ -722,7 +714,7 @@ HTML_TEMPLATE = '''
722
 
723
  // Socket events
724
  socket.on('connect', () => {
725
- logDebug('Connected to server');
726
  updateConnectionStatus(true);
727
  reconnectAttempts = 0;
728
 
@@ -732,7 +724,7 @@ HTML_TEMPLATE = '''
732
  });
733
 
734
  socket.on('disconnect', (reason) => {
735
- logDebug('Disconnected from server: ' + reason);
736
  updateConnectionStatus(false);
737
 
738
  if (reason === 'io server disconnect') {
@@ -746,7 +738,7 @@ HTML_TEMPLATE = '''
746
  });
747
 
748
  socket.on('connect_error', (error) => {
749
- logDebug('Connection error: ' + error);
750
  updateConnectionStatus(false);
751
  });
752
 
@@ -755,13 +747,11 @@ HTML_TEMPLATE = '''
755
  document.getElementById('loginScreen').style.display = 'none';
756
  document.getElementById('chatScreen').style.display = 'flex';
757
  updateConnectionStatus(true);
758
- logDebug('Registered as: ' + data.username);
759
  });
760
 
761
  socket.on('user_list_update', (data) => {
762
  document.getElementById('onlineCount').textContent = data.count;
763
  updateUsersList(data.users || []);
764
- logDebug('Users updated: ' + data.count + ' online');
765
  });
766
 
767
  socket.on('new_message', (message) => {
@@ -773,12 +763,10 @@ HTML_TEMPLATE = '''
773
  });
774
 
775
  socket.on('incoming_call', (data) => {
776
- logDebug('Incoming call from: ' + data.from_user);
777
  showIncomingCall(data.from_user, data.call_id, data.isVideo);
778
  });
779
 
780
  socket.on('call_answered', (data) => {
781
- logDebug('Call answered: ' + data.answer);
782
  if (data.answer) {
783
  startCall(data.isVideo, false);
784
  } else {
@@ -788,20 +776,21 @@ HTML_TEMPLATE = '''
788
  });
789
 
790
  socket.on('call_ended', () => {
791
- logDebug('Call ended by remote');
792
  alert('Звонок завершен');
793
  endCall();
794
  });
795
 
796
  socket.on('webrtc_offer', async (data) => {
797
- logDebug('Received WebRTC offer');
798
- updateCallDebug('Получен WebRTC offer');
799
 
800
  if (!peerConnection) {
801
  await createPeerConnection(false);
802
  }
803
  try {
804
  await peerConnection.setRemoteDescription(new RTCSessionDescription(data.offer));
 
 
805
  const answer = await peerConnection.createAnswer();
806
  await peerConnection.setLocalDescription(answer);
807
 
@@ -810,74 +799,41 @@ HTML_TEMPLATE = '''
810
  to_user: data.from_user,
811
  call_id: data.call_id
812
  });
813
- logDebug('Sent WebRTC answer');
814
- updateCallDebug('Отправлен WebRTC answer');
815
  } catch (error) {
816
  console.error('Error handling WebRTC offer:', error);
817
- logDebug('WebRTC offer error: ' + error);
818
- updateCallDebug('Ошибка WebRTC offer: ' + error);
819
  }
820
  });
821
 
822
  socket.on('webrtc_answer', async (data) => {
823
- logDebug('Received WebRTC answer');
824
- updateCallDebug('Получен WebRTC answer');
825
 
826
  if (peerConnection) {
827
  try {
828
  await peerConnection.setRemoteDescription(new RTCSessionDescription(data.answer));
829
- logDebug('Remote description set');
830
- updateCallDebug('Remote description установлен');
831
  } catch (error) {
832
  console.error('Error setting remote description:', error);
833
- logDebug('Remote description error: ' + error);
834
- updateCallDebug('Ошибка remote description: ' + error);
835
  }
836
  }
837
  });
838
 
839
  socket.on('webrtc_ice_candidate', async (data) => {
840
- logDebug('Received ICE candidate');
841
- updateCallDebug('Получен ICE candidate');
842
-
843
  if (peerConnection && data.candidate) {
844
  try {
845
  await peerConnection.addIceCandidate(new RTCIceCandidate(data.candidate));
846
- logDebug('ICE candidate added');
847
- updateCallDebug('ICE candidate добавлен');
848
  } catch (error) {
849
  console.error('Error adding ICE candidate:', error);
850
- logDebug('ICE candidate error: ' + error);
851
- updateCallDebug('Ошибка ICE candidate: ' + error);
852
  }
853
  }
854
  });
855
  }
856
 
857
- function logDebug(message) {
858
- if (debugEnabled) {
859
- console.log('DEBUG:', message);
860
- const debugElement = document.getElementById('debugInfo');
861
- const timestamp = new Date().toLocaleTimeString();
862
- debugElement.innerHTML = timestamp + ': ' + message + '<br>' + debugElement.innerHTML;
863
- }
864
- }
865
-
866
- function updateCallDebug(message) {
867
- if (debugEnabled) {
868
- const callDebug = document.getElementById('callDebug');
869
- const timestamp = new Date().toLocaleTimeString();
870
- callDebug.innerHTML = timestamp + ': ' + message + '<br>' + callDebug.innerHTML;
871
- }
872
- }
873
-
874
- function toggleDebug() {
875
- debugEnabled = !debugEnabled;
876
- document.getElementById('debugInfo').style.display = debugEnabled ? 'block' : 'none';
877
- document.getElementById('callDebug').style.display = debugEnabled ? 'block' : 'none';
878
- logDebug('Debug mode: ' + (debugEnabled ? 'ON' : 'OFF'));
879
- }
880
-
881
  function updateConnectionStatus(connected) {
882
  const statusElement = document.getElementById('connectionStatus');
883
  statusElement.style.display = 'block';
@@ -890,6 +846,14 @@ HTML_TEMPLATE = '''
890
  }
891
  }
892
 
 
 
 
 
 
 
 
 
893
  // UI Functions
894
  function registerUser() {
895
  const username = document.getElementById('usernameInput').value.trim();
@@ -932,7 +896,6 @@ HTML_TEMPLATE = '''
932
  container.appendChild(messageDiv);
933
  container.scrollTop = container.scrollHeight;
934
 
935
- // Remove placeholder if exists
936
  const placeholder = container.querySelector('div[style*="text-align: center"]');
937
  if (placeholder) {
938
  placeholder.remove();
@@ -1004,14 +967,14 @@ HTML_TEMPLATE = '''
1004
  audio: {
1005
  echoCancellation: true,
1006
  noiseSuppression: true,
1007
- sampleRate: 44100
1008
  }
1009
  });
1010
 
1011
- // Используем более совместимый формат
1012
  const options = {
1013
- mimeType: 'audio/webm; codecs=opus',
1014
- audioBitsPerSecond: 128000
1015
  };
1016
 
1017
  mediaRecorder = new MediaRecorder(stream, options);
@@ -1029,7 +992,7 @@ HTML_TEMPLATE = '''
1029
  const duration = Math.round((Date.now() - startTime) / 1000);
1030
  const audioBlob = new Blob(audioChunks, { type: 'audio/webm' });
1031
 
1032
- // Конвертируем в base64
1033
  const reader = new FileReader();
1034
  reader.onload = function() {
1035
  const base64Audio = reader.result.split(',')[1];
@@ -1040,21 +1003,19 @@ HTML_TEMPLATE = '''
1040
  sender: currentUser.username,
1041
  duration: duration
1042
  });
1043
- logDebug('Voice message sent, duration: ' + duration + 's');
1044
  } else {
1045
  alert('Нет подключения для отправки голосового сообщения');
1046
  }
1047
  };
1048
  reader.readAsDataURL(audioBlob);
1049
 
1050
- // Освобождаем ресурсы
1051
  stream.getTracks().forEach(track => track.stop());
1052
  };
1053
 
1054
- mediaRecorder.start(100); // Собираем данные каждые 100мс
1055
  isRecording = true;
1056
 
1057
- // Show recording overlay
1058
  document.getElementById('recordingOverlay').style.display = 'flex';
1059
 
1060
  } catch (error) {
@@ -1093,8 +1054,6 @@ HTML_TEMPLATE = '''
1093
  isInitiator: true
1094
  };
1095
 
1096
- logDebug('Starting call with ' + targetUser + ', video: ' + isVideo);
1097
-
1098
  socket.emit('call_request', {
1099
  call_id: currentCall.id,
1100
  from_user: currentUser.username,
@@ -1115,7 +1074,6 @@ HTML_TEMPLATE = '''
1115
 
1116
  document.getElementById('callerName').textContent = fromUser;
1117
  document.getElementById('incomingCallOverlay').style.display = 'flex';
1118
- logDebug('Showing incoming call from: ' + fromUser);
1119
  }
1120
 
1121
  function acceptCall() {
@@ -1129,7 +1087,6 @@ HTML_TEMPLATE = '''
1129
  });
1130
 
1131
  startCall(currentCall.isVideo, true);
1132
- logDebug('Call accepted');
1133
  } else {
1134
  alert('Нет подключения к серверу');
1135
  }
@@ -1142,7 +1099,6 @@ HTML_TEMPLATE = '''
1142
  answer: false,
1143
  to_user: currentCall.target
1144
  });
1145
- logDebug('Call rejected');
1146
  }
1147
 
1148
  document.getElementById('incomingCallOverlay').style.display = 'none';
@@ -1152,43 +1108,33 @@ HTML_TEMPLATE = '''
1152
  async function startCall(isVideo, isInitiator) {
1153
  try {
1154
  showCallScreen(isVideo, isInitiator ? 'Звонок...' : 'Разговор...');
 
1155
 
1156
- // Получаем медиапоток с оптимальными настройками
1157
  const constraints = {
1158
  audio: {
1159
  echoCancellation: true,
1160
  noiseSuppression: true,
1161
- autoGainControl: true,
1162
- channelCount: 1,
1163
- sampleRate: 48000
1164
  },
1165
  video: isVideo ? {
1166
- width: { ideal: 640 },
1167
- height: { ideal: 480 },
1168
- frameRate: { ideal: 24 }
1169
  } : false
1170
  };
1171
 
1172
- logDebug('Requesting media with constraints: ' + JSON.stringify(constraints));
1173
-
1174
  localStream = await navigator.mediaDevices.getUserMedia(constraints);
1175
  document.getElementById('localVideo').srcObject = localStream;
1176
- logDebug('Local media stream obtained');
1177
 
1178
  // Создаем peer connection
1179
  await createPeerConnection(isInitiator);
1180
- logDebug('Peer connection created');
1181
 
1182
  if (isInitiator) {
1183
- // Создаем и отправляем offer
1184
- const offerOptions = {
1185
- offerToReceiveAudio: true,
1186
- offerToReceiveVideo: isVideo
1187
- };
1188
-
1189
- const offer = await peerConnection.createOffer(offerOptions);
1190
  await peerConnection.setLocalDescription(offer);
1191
- logDebug('Local description set');
1192
 
1193
  if (socket && socket.connected) {
1194
  socket.emit('webrtc_offer', {
@@ -1196,42 +1142,38 @@ HTML_TEMPLATE = '''
1196
  to_user: currentCall.target,
1197
  call_id: currentCall.id
1198
  });
1199
- logDebug('WebRTC offer sent');
1200
  }
1201
  }
1202
 
1203
- // Запускаем таймер звонка
1204
  startCallTimer();
1205
- logDebug('Call timer started');
1206
 
1207
  } catch (error) {
1208
  console.error('Error starting call:', error);
1209
- logDebug('Call start error: ' + error.message);
1210
  alert(`Ошибка начала звонка: ${error.message}`);
1211
  endCall();
1212
  }
1213
  }
1214
 
1215
  async function createPeerConnection(isInitiator) {
 
 
1216
  peerConnection = new RTCPeerConnection(rtcConfig);
1217
- logDebug('PeerConnection created with config: ' + JSON.stringify(rtcConfig));
1218
 
1219
  // Добавляем локальные треки
1220
  localStream.getTracks().forEach(track => {
1221
  peerConnection.addTrack(track, localStream);
1222
- logDebug('Added local track: ' + track.kind);
1223
  });
1224
 
1225
- // Обрабатываем входящие потоки
1226
  peerConnection.ontrack = (event) => {
1227
- logDebug('Received remote track: ' + event.track.kind);
1228
- updateCallDebug('Получен remote track: ' + event.track.kind);
1229
-
1230
  remoteStream = event.streams[0];
1231
  const remoteVideo = document.getElementById('remoteVideoContainer');
1232
 
1233
  if (event.track.kind === 'video') {
1234
- // Создаем video элемент для удаленного потока
1235
  const videoElement = document.createElement('video');
1236
  videoElement.srcObject = remoteStream;
1237
  videoElement.autoplay = true;
@@ -1240,90 +1182,98 @@ HTML_TEMPLATE = '''
1240
 
1241
  remoteVideo.innerHTML = '';
1242
  remoteVideo.appendChild(videoElement);
1243
- logDebug('Remote video element created');
1244
  } else if (event.track.kind === 'audio') {
1245
- // Для аудио показываем сообщение
1246
  if (!document.querySelector('.remote-video video')) {
1247
  remoteVideo.innerHTML = '<div>Аудиозвонок</div>';
1248
  remoteVideo.className = 'no-video remote-video';
1249
- logDebug('Audio call UI updated');
1250
  }
 
1251
  }
1252
  };
1253
 
1254
- // Обрабатываем ICE кандидаты
1255
  peerConnection.onicecandidate = (event) => {
1256
  if (event.candidate && currentCall && socket && socket.connected) {
1257
  socket.emit('webrtc_ice_candidate', {
1258
  candidate: event.candidate,
1259
  to_user: currentCall.target
1260
  });
1261
- logDebug('ICE candidate sent: ' + event.candidate.type);
1262
- updateCallDebug('Отправлен ICE candidate: ' + event.candidate.type);
1263
  } else if (!event.candidate) {
1264
- logDebug('All ICE candidates sent');
1265
- updateCallDebug('Все ICE candidates отправлены');
1266
  }
1267
  };
1268
 
1269
  peerConnection.onconnectionstatechange = () => {
1270
  const state = peerConnection.connectionState;
1271
- logDebug('Connection state: ' + state);
1272
- updateCallDebug('Состояние соединения: ' + state);
1273
 
1274
  if (state === 'connected') {
1275
  document.getElementById('callStatus').textContent = 'Разговор';
1276
- logDebug('WebRTC connection established');
1277
- updateCallDebug('WebRTC соединение установлено');
1278
  } else if (state === 'failed' || state === 'disconnected') {
1279
- logDebug('WebRTC connection failed');
1280
- updateCallDebug('WebRTC соединение потеряно');
1281
- setTimeout(() => {
1282
- if (peerConnection.connectionState !== 'connected') {
1283
- alert('Соединение потеряно');
1284
- endCall();
1285
- }
1286
- }, 2000);
1287
  }
1288
  };
1289
 
1290
  peerConnection.oniceconnectionstatechange = () => {
1291
  const state = peerConnection.iceConnectionState;
1292
- logDebug('ICE connection state: ' + state);
1293
- updateCallDebug('Состояние ICE: ' + state);
 
 
 
 
 
 
 
 
 
 
 
1294
  };
1295
 
1296
- peerConnection.onsignalingstatechange = () => {
1297
- logDebug('Signaling state: ' + peerConnection.signalingState);
1298
- updateCallDebug('Signaling state: ' + peerConnection.signalingState);
1299
  };
1300
  }
1301
 
 
 
 
 
 
 
 
 
 
 
 
1302
  function showCallScreen(isVideo, status) {
1303
  document.getElementById('callScreen').style.display = 'flex';
1304
  document.getElementById('callStatus').textContent = status;
 
1305
 
1306
  const remoteVideo = document.getElementById('remoteVideoContainer');
1307
  if (!isVideo) {
1308
  remoteVideo.innerHTML = '<div>Аудиозвонок</div>';
1309
  remoteVideo.className = 'no-video remote-video';
1310
  }
1311
-
1312
- logDebug('Call screen shown: ' + status);
1313
  }
1314
 
1315
  function hideCallScreen() {
1316
  document.getElementById('callScreen').style.display = 'none';
1317
  document.getElementById('remoteVideoContainer').innerHTML = '<div>Ожидание подключения...</div>';
1318
  document.getElementById('remoteVideoContainer').className = 'no-video remote-video';
1319
- document.getElementById('callDebug').innerHTML = '';
1320
 
1321
  if (callTimerInterval) {
1322
  clearInterval(callTimerInterval);
1323
  callTimerInterval = null;
1324
  }
1325
-
1326
- logDebug('Call screen hidden');
1327
  }
1328
 
1329
  function startCallTimer() {
@@ -1339,18 +1289,14 @@ HTML_TEMPLATE = '''
1339
  }
1340
 
1341
  function endCall() {
1342
- logDebug('Ending call');
1343
-
1344
  if (peerConnection) {
1345
  peerConnection.close();
1346
  peerConnection = null;
1347
- logDebug('Peer connection closed');
1348
  }
1349
 
1350
  if (localStream) {
1351
  localStream.getTracks().forEach(track => track.stop());
1352
  localStream = null;
1353
- logDebug('Local stream stopped');
1354
  }
1355
 
1356
  if (currentCall && socket && socket.connected) {
@@ -1358,15 +1304,12 @@ HTML_TEMPLATE = '''
1358
  to_user: currentCall.target,
1359
  call_id: currentCall.id
1360
  });
1361
- logDebug('Call end signal sent');
1362
  }
1363
 
1364
  hideCallScreen();
1365
  currentCall = null;
1366
  isMuted = false;
1367
  isVideoEnabled = true;
1368
-
1369
- logDebug('Call completely ended');
1370
  }
1371
 
1372
  function toggleMute() {
@@ -1377,7 +1320,6 @@ HTML_TEMPLATE = '''
1377
  audioTracks[0].enabled = !isMuted;
1378
  document.getElementById('muteBtn').classList.toggle('active', isMuted);
1379
  document.getElementById('muteBtn').textContent = isMuted ? '🎤🚫' : '🎤';
1380
- logDebug('Microphone ' + (isMuted ? 'muted' : 'unmuted'));
1381
  }
1382
  }
1383
  }
@@ -1391,9 +1333,7 @@ HTML_TEMPLATE = '''
1391
  document.getElementById('videoBtn').classList.toggle('active', !isVideoEnabled);
1392
  document.getElementById('videoBtn').textContent = isVideoEnabled ? '📹' : '📹🚫';
1393
 
1394
- // Скрываем/показываем локальное видео
1395
  document.getElementById('localVideo').style.display = isVideoEnabled ? 'block' : 'none';
1396
- logDebug('Video ' + (isVideoEnabled ? 'enabled' : 'disabled'));
1397
  }
1398
  }
1399
  }
@@ -1413,7 +1353,7 @@ HTML_TEMPLATE = '''
1413
  }
1414
  });
1415
 
1416
- // Закрываем sidebar при клике вне его
1417
  document.addEventListener('click', (e) => {
1418
  const sidebar = document.getElementById('usersSidebar');
1419
  if (sidebar.classList.contains('open') &&
@@ -1423,12 +1363,12 @@ HTML_TEMPLATE = '''
1423
  }
1424
  });
1425
 
1426
- // Инициализируем socket при загрузке
1427
  window.addEventListener('load', function() {
1428
  initializeSocket();
1429
  });
1430
 
1431
- // Обрабатываем изменения видимости страницы
1432
  document.addEventListener('visibilitychange', function() {
1433
  if (!document.hidden && (!socket || !socket.connected)) {
1434
  initializeSocket();
@@ -1455,12 +1395,10 @@ def handle_connect():
1455
  def handle_disconnect():
1456
  print(f'Client disconnected: {request.sid}')
1457
 
1458
- # Находим и удаляем отключившегося пользователя
1459
  disconnected_user = None
1460
  for user_id, user_data in list(users.items()):
1461
  if user_data.get('sid') == request.sid:
1462
  disconnected_user = user_data['username']
1463
- # Завершаем активные звонки
1464
  for call_id, call_data in list(active_calls.items()):
1465
  if call_data['from_user'] == disconnected_user or call_data['to_user'] == disconnected_user:
1466
  try:
@@ -1658,7 +1596,7 @@ if __name__ == '__main__':
1658
  try:
1659
  print("Запуск мобильного мессенджера...")
1660
  print("Порт: 7860")
1661
- print("WebRTC: Только STUN серверы (без TURN)")
1662
  socketio.run(
1663
  app,
1664
  host='0.0.0.0',
 
558
  color: white;
559
  }
560
 
561
+ .webrtc-status {
562
+ position: absolute;
563
+ bottom: 120px;
564
+ left: 0;
565
+ right: 0;
566
+ text-align: center;
567
  color: white;
568
+ font-size: 14px;
569
+ z-index: 10;
 
 
 
 
570
  }
571
  </style>
572
  </head>
 
576
  Подключение...
577
  </div>
578
 
 
 
 
579
  <!-- Login Screen -->
580
  <div id="loginScreen" class="login-screen">
581
  <div style="text-align: center; margin-bottom: 30px;">
 
600
  </div>
601
  <div class="call-buttons">
602
  <button class="header-btn" onclick="toggleUsersSidebar()" title="Пользователи">👥</button>
 
603
  </div>
604
  </div>
605
 
 
637
  <div class="call-info">
638
  <div class="call-status" id="callStatus">Звонок...</div>
639
  <div class="call-timer" id="callTimer">00:00</div>
640
+ </div>
641
+ <div class="webrtc-status" id="webrtcStatus">
642
+ Установка соединения...
643
  </div>
644
  <div class="call-controls">
645
  <button class="call-control-btn" id="muteBtn" onclick="toggleMute()">🎤</button>
 
685
  let isVideoEnabled = true;
686
  let reconnectAttempts = 0;
687
  const MAX_RECONNECT_ATTEMPTS = 5;
 
688
 
689
+ // WebRTC configuration - только STUN серверы
690
  const rtcConfig = {
691
  iceServers: [
692
  { urls: 'stun:stun.l.google.com:19302' },
 
695
  { urls: 'stun:stun3.l.google.com:19302' },
696
  { urls: 'stun:stun4.l.google.com:19302' }
697
  ],
698
+ iceCandidatePoolSize: 10
 
 
699
  };
700
 
701
  function initializeSocket() {
 
714
 
715
  // Socket events
716
  socket.on('connect', () => {
717
+ console.log('Connected to server');
718
  updateConnectionStatus(true);
719
  reconnectAttempts = 0;
720
 
 
724
  });
725
 
726
  socket.on('disconnect', (reason) => {
727
+ console.log('Disconnected from server:', reason);
728
  updateConnectionStatus(false);
729
 
730
  if (reason === 'io server disconnect') {
 
738
  });
739
 
740
  socket.on('connect_error', (error) => {
741
+ console.error('Connection error:', error);
742
  updateConnectionStatus(false);
743
  });
744
 
 
747
  document.getElementById('loginScreen').style.display = 'none';
748
  document.getElementById('chatScreen').style.display = 'flex';
749
  updateConnectionStatus(true);
 
750
  });
751
 
752
  socket.on('user_list_update', (data) => {
753
  document.getElementById('onlineCount').textContent = data.count;
754
  updateUsersList(data.users || []);
 
755
  });
756
 
757
  socket.on('new_message', (message) => {
 
763
  });
764
 
765
  socket.on('incoming_call', (data) => {
 
766
  showIncomingCall(data.from_user, data.call_id, data.isVideo);
767
  });
768
 
769
  socket.on('call_answered', (data) => {
 
770
  if (data.answer) {
771
  startCall(data.isVideo, false);
772
  } else {
 
776
  });
777
 
778
  socket.on('call_ended', () => {
 
779
  alert('Звонок завершен');
780
  endCall();
781
  });
782
 
783
  socket.on('webrtc_offer', async (data) => {
784
+ console.log('Received WebRTC offer from', data.from_user);
785
+ updateWebRTCStatus('Получен запрос на соединение');
786
 
787
  if (!peerConnection) {
788
  await createPeerConnection(false);
789
  }
790
  try {
791
  await peerConnection.setRemoteDescription(new RTCSessionDescription(data.offer));
792
+ updateWebRTCStatus('Создание ответа...');
793
+
794
  const answer = await peerConnection.createAnswer();
795
  await peerConnection.setLocalDescription(answer);
796
 
 
799
  to_user: data.from_user,
800
  call_id: data.call_id
801
  });
802
+
803
+ updateWebRTCStatus('Ответ отправлен');
804
  } catch (error) {
805
  console.error('Error handling WebRTC offer:', error);
806
+ updateWebRTCStatus('Ошибка: ' + error.message);
 
807
  }
808
  });
809
 
810
  socket.on('webrtc_answer', async (data) => {
811
+ console.log('Received WebRTC answer from', data.from_user);
812
+ updateWebRTCStatus('Получен ответ на соединение');
813
 
814
  if (peerConnection) {
815
  try {
816
  await peerConnection.setRemoteDescription(new RTCSessionDescription(data.answer));
817
+ updateWebRTCStatus('Соединение установлено');
 
818
  } catch (error) {
819
  console.error('Error setting remote description:', error);
820
+ updateWebRTCStatus('Ошибка установки соединения');
 
821
  }
822
  }
823
  });
824
 
825
  socket.on('webrtc_ice_candidate', async (data) => {
 
 
 
826
  if (peerConnection && data.candidate) {
827
  try {
828
  await peerConnection.addIceCandidate(new RTCIceCandidate(data.candidate));
829
+ console.log('Added remote ICE candidate');
 
830
  } catch (error) {
831
  console.error('Error adding ICE candidate:', error);
 
 
832
  }
833
  }
834
  });
835
  }
836
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
837
  function updateConnectionStatus(connected) {
838
  const statusElement = document.getElementById('connectionStatus');
839
  statusElement.style.display = 'block';
 
846
  }
847
  }
848
 
849
+ function updateWebRTCStatus(message) {
850
+ const statusElement = document.getElementById('webrtcStatus');
851
+ if (statusElement) {
852
+ statusElement.textContent = message;
853
+ console.log('WebRTC Status:', message);
854
+ }
855
+ }
856
+
857
  // UI Functions
858
  function registerUser() {
859
  const username = document.getElementById('usernameInput').value.trim();
 
896
  container.appendChild(messageDiv);
897
  container.scrollTop = container.scrollHeight;
898
 
 
899
  const placeholder = container.querySelector('div[style*="text-align: center"]');
900
  if (placeholder) {
901
  placeholder.remove();
 
967
  audio: {
968
  echoCancellation: true,
969
  noiseSuppression: true,
970
+ channelCount: 1
971
  }
972
  });
973
 
974
+ // Используем формат, который поддерживается большинством браузеров
975
  const options = {
976
+ audioBitsPerSecond: 128000,
977
+ mimeType: 'audio/webm;codecs=opus'
978
  };
979
 
980
  mediaRecorder = new MediaRecorder(stream, options);
 
992
  const duration = Math.round((Date.now() - startTime) / 1000);
993
  const audioBlob = new Blob(audioChunks, { type: 'audio/webm' });
994
 
995
+ // Конвертируем в base64 для передачи
996
  const reader = new FileReader();
997
  reader.onload = function() {
998
  const base64Audio = reader.result.split(',')[1];
 
1003
  sender: currentUser.username,
1004
  duration: duration
1005
  });
 
1006
  } else {
1007
  alert('Нет подключения для отправки голосового сообщения');
1008
  }
1009
  };
1010
  reader.readAsDataURL(audioBlob);
1011
 
1012
+ // Останавливаем все треки
1013
  stream.getTracks().forEach(track => track.stop());
1014
  };
1015
 
1016
+ mediaRecorder.start(100); // Записываем chunks каждые 100ms
1017
  isRecording = true;
1018
 
 
1019
  document.getElementById('recordingOverlay').style.display = 'flex';
1020
 
1021
  } catch (error) {
 
1054
  isInitiator: true
1055
  };
1056
 
 
 
1057
  socket.emit('call_request', {
1058
  call_id: currentCall.id,
1059
  from_user: currentUser.username,
 
1074
 
1075
  document.getElementById('callerName').textContent = fromUser;
1076
  document.getElementById('incomingCallOverlay').style.display = 'flex';
 
1077
  }
1078
 
1079
  function acceptCall() {
 
1087
  });
1088
 
1089
  startCall(currentCall.isVideo, true);
 
1090
  } else {
1091
  alert('Нет подключения к серверу');
1092
  }
 
1099
  answer: false,
1100
  to_user: currentCall.target
1101
  });
 
1102
  }
1103
 
1104
  document.getElementById('incomingCallOverlay').style.display = 'none';
 
1108
  async function startCall(isVideo, isInitiator) {
1109
  try {
1110
  showCallScreen(isVideo, isInitiator ? 'Звонок...' : 'Разговор...');
1111
+ updateWebRTCStatus('Получение медиа-потока...');
1112
 
1113
+ // Получаем медиа-поток
1114
  const constraints = {
1115
  audio: {
1116
  echoCancellation: true,
1117
  noiseSuppression: true,
1118
+ autoGainControl: true
 
 
1119
  },
1120
  video: isVideo ? {
1121
+ width: { min: 640, ideal: 1280, max: 1920 },
1122
+ height: { min: 480, ideal: 720, max: 1080 },
1123
+ frameRate: { ideal: 30, max: 60 }
1124
  } : false
1125
  };
1126
 
 
 
1127
  localStream = await navigator.mediaDevices.getUserMedia(constraints);
1128
  document.getElementById('localVideo').srcObject = localStream;
1129
+ updateWebRTCStatus('Медиа-поток получен');
1130
 
1131
  // Создаем peer connection
1132
  await createPeerConnection(isInitiator);
 
1133
 
1134
  if (isInitiator) {
1135
+ updateWebRTCStatus('Создание предложения...');
1136
+ const offer = await peerConnection.createOffer();
 
 
 
 
 
1137
  await peerConnection.setLocalDescription(offer);
 
1138
 
1139
  if (socket && socket.connected) {
1140
  socket.emit('webrtc_offer', {
 
1142
  to_user: currentCall.target,
1143
  call_id: currentCall.id
1144
  });
1145
+ updateWebRTCStatus('Предложение отправлено');
1146
  }
1147
  }
1148
 
 
1149
  startCallTimer();
 
1150
 
1151
  } catch (error) {
1152
  console.error('Error starting call:', error);
1153
+ updateWebRTCStatus('Ошибка: ' + error.message);
1154
  alert(`Ошибка начала звонка: ${error.message}`);
1155
  endCall();
1156
  }
1157
  }
1158
 
1159
  async function createPeerConnection(isInitiator) {
1160
+ updateWebRTCStatus('Создание PeerConnection...');
1161
+
1162
  peerConnection = new RTCPeerConnection(rtcConfig);
 
1163
 
1164
  // Добавляем локальные треки
1165
  localStream.getTracks().forEach(track => {
1166
  peerConnection.addTrack(track, localStream);
1167
+ console.log('Added local track:', track.kind);
1168
  });
1169
 
1170
+ // Обработка входящих потоков
1171
  peerConnection.ontrack = (event) => {
1172
+ console.log('Received remote track:', event.track.kind);
 
 
1173
  remoteStream = event.streams[0];
1174
  const remoteVideo = document.getElementById('remoteVideoContainer');
1175
 
1176
  if (event.track.kind === 'video') {
 
1177
  const videoElement = document.createElement('video');
1178
  videoElement.srcObject = remoteStream;
1179
  videoElement.autoplay = true;
 
1182
 
1183
  remoteVideo.innerHTML = '';
1184
  remoteVideo.appendChild(videoElement);
1185
+ updateWebRTCStatus('Видео соединение установлено');
1186
  } else if (event.track.kind === 'audio') {
 
1187
  if (!document.querySelector('.remote-video video')) {
1188
  remoteVideo.innerHTML = '<div>Аудиозвонок</div>';
1189
  remoteVideo.className = 'no-video remote-video';
 
1190
  }
1191
+ updateWebRTCStatus('Аудио соединение установлено');
1192
  }
1193
  };
1194
 
1195
+ // Обработка ICE кандидатов
1196
  peerConnection.onicecandidate = (event) => {
1197
  if (event.candidate && currentCall && socket && socket.connected) {
1198
  socket.emit('webrtc_ice_candidate', {
1199
  candidate: event.candidate,
1200
  to_user: currentCall.target
1201
  });
1202
+ console.log('Sent ICE candidate');
 
1203
  } else if (!event.candidate) {
1204
+ console.log('All ICE candidates have been sent');
 
1205
  }
1206
  };
1207
 
1208
  peerConnection.onconnectionstatechange = () => {
1209
  const state = peerConnection.connectionState;
1210
+ console.log('Connection state:', state);
1211
+ updateWebRTCStatus('Состояние: ' + state);
1212
 
1213
  if (state === 'connected') {
1214
  document.getElementById('callStatus').textContent = 'Разговор';
1215
+ updateWebRTCStatus('Соединение установлено!');
 
1216
  } else if (state === 'failed' || state === 'disconnected') {
1217
+ updateWebRTCStatus('Соединение прервано: ' + state);
 
 
 
 
 
 
 
1218
  }
1219
  };
1220
 
1221
  peerConnection.oniceconnectionstatechange = () => {
1222
  const state = peerConnection.iceConnectionState;
1223
+ console.log('ICE connection state:', state);
1224
+
1225
+ if (state === 'connected') {
1226
+ updateWebRTCStatus('ICE соединение установлено');
1227
+ } else if (state === 'failed') {
1228
+ updateWebRTCStatus('ICE соединение не удалось');
1229
+ // Попробуем пересоздать соединение
1230
+ setTimeout(() => {
1231
+ if (peerConnection.iceConnectionState === 'failed') {
1232
+ restartIce();
1233
+ }
1234
+ }, 1000);
1235
+ }
1236
  };
1237
 
1238
+ peerConnection.onnegotiationneeded = async () => {
1239
+ console.log('Negotiation needed');
1240
+ updateWebRTCStatus('Требуется переговоры...');
1241
  };
1242
  }
1243
 
1244
+ async function restartIce() {
1245
+ if (!peerConnection) return;
1246
+
1247
+ try {
1248
+ updateWebRTCStatus('Перезапуск ICE...');
1249
+ await peerConnection.restartIce();
1250
+ } catch (error) {
1251
+ console.error('Error restarting ICE:', error);
1252
+ }
1253
+ }
1254
+
1255
  function showCallScreen(isVideo, status) {
1256
  document.getElementById('callScreen').style.display = 'flex';
1257
  document.getElementById('callStatus').textContent = status;
1258
+ updateWebRTCStatus('Настройка звонка...');
1259
 
1260
  const remoteVideo = document.getElementById('remoteVideoContainer');
1261
  if (!isVideo) {
1262
  remoteVideo.innerHTML = '<div>Аудиозвонок</div>';
1263
  remoteVideo.className = 'no-video remote-video';
1264
  }
 
 
1265
  }
1266
 
1267
  function hideCallScreen() {
1268
  document.getElementById('callScreen').style.display = 'none';
1269
  document.getElementById('remoteVideoContainer').innerHTML = '<div>Ожидание подключения...</div>';
1270
  document.getElementById('remoteVideoContainer').className = 'no-video remote-video';
1271
+ document.getElementById('webrtcStatus').textContent = 'Установка соединения...';
1272
 
1273
  if (callTimerInterval) {
1274
  clearInterval(callTimerInterval);
1275
  callTimerInterval = null;
1276
  }
 
 
1277
  }
1278
 
1279
  function startCallTimer() {
 
1289
  }
1290
 
1291
  function endCall() {
 
 
1292
  if (peerConnection) {
1293
  peerConnection.close();
1294
  peerConnection = null;
 
1295
  }
1296
 
1297
  if (localStream) {
1298
  localStream.getTracks().forEach(track => track.stop());
1299
  localStream = null;
 
1300
  }
1301
 
1302
  if (currentCall && socket && socket.connected) {
 
1304
  to_user: currentCall.target,
1305
  call_id: currentCall.id
1306
  });
 
1307
  }
1308
 
1309
  hideCallScreen();
1310
  currentCall = null;
1311
  isMuted = false;
1312
  isVideoEnabled = true;
 
 
1313
  }
1314
 
1315
  function toggleMute() {
 
1320
  audioTracks[0].enabled = !isMuted;
1321
  document.getElementById('muteBtn').classList.toggle('active', isMuted);
1322
  document.getElementById('muteBtn').textContent = isMuted ? '🎤🚫' : '🎤';
 
1323
  }
1324
  }
1325
  }
 
1333
  document.getElementById('videoBtn').classList.toggle('active', !isVideoEnabled);
1334
  document.getElementById('videoBtn').textContent = isVideoEnabled ? '📹' : '📹🚫';
1335
 
 
1336
  document.getElementById('localVideo').style.display = isVideoEnabled ? 'block' : 'none';
 
1337
  }
1338
  }
1339
  }
 
1353
  }
1354
  });
1355
 
1356
+ // Close sidebar when clicking outside
1357
  document.addEventListener('click', (e) => {
1358
  const sidebar = document.getElementById('usersSidebar');
1359
  if (sidebar.classList.contains('open') &&
 
1363
  }
1364
  });
1365
 
1366
+ // Initialize socket on load
1367
  window.addEventListener('load', function() {
1368
  initializeSocket();
1369
  });
1370
 
1371
+ // Handle page visibility changes
1372
  document.addEventListener('visibilitychange', function() {
1373
  if (!document.hidden && (!socket || !socket.connected)) {
1374
  initializeSocket();
 
1395
  def handle_disconnect():
1396
  print(f'Client disconnected: {request.sid}')
1397
 
 
1398
  disconnected_user = None
1399
  for user_id, user_data in list(users.items()):
1400
  if user_data.get('sid') == request.sid:
1401
  disconnected_user = user_data['username']
 
1402
  for call_id, call_data in list(active_calls.items()):
1403
  if call_data['from_user'] == disconnected_user or call_data['to_user'] == disconnected_user:
1404
  try:
 
1596
  try:
1597
  print("Запуск мобильного мессенджера...")
1598
  print("Порт: 7860")
1599
+ print("Используются только STUN серверы")
1600
  socketio.run(
1601
  app,
1602
  host='0.0.0.0',