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

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +194 -48
main.py CHANGED
@@ -17,10 +17,10 @@ socketio = SocketIO(
17
  cors_allowed_origins="*",
18
  ping_timeout=60,
19
  ping_interval=25,
20
- logger=True,
21
  engineio_logger=False,
22
  async_mode='eventlet',
23
- max_http_buffer_size=100 * 1024 * 1024 # 100MB для больших файлов
24
  )
25
 
26
  # Хранилища данных
@@ -557,6 +557,20 @@ HTML_TEMPLATE = '''
557
  background: #FF4444;
558
  color: white;
559
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
560
  </style>
561
  </head>
562
  <body>
@@ -565,6 +579,9 @@ HTML_TEMPLATE = '''
565
  Подключение...
566
  </div>
567
 
 
 
 
568
  <!-- Login Screen -->
569
  <div id="loginScreen" class="login-screen">
570
  <div style="text-align: center; margin-bottom: 30px;">
@@ -589,6 +606,7 @@ HTML_TEMPLATE = '''
589
  </div>
590
  <div class="call-buttons">
591
  <button class="header-btn" onclick="toggleUsersSidebar()" title="Пользователи">👥</button>
 
592
  </div>
593
  </div>
594
 
@@ -626,6 +644,7 @@ HTML_TEMPLATE = '''
626
  <div class="call-info">
627
  <div class="call-status" id="callStatus">Звонок...</div>
628
  <div class="call-timer" id="callTimer">00:00</div>
 
629
  </div>
630
  <div class="call-controls">
631
  <button class="call-control-btn" id="muteBtn" onclick="toggleMute()">🎤</button>
@@ -671,24 +690,23 @@ HTML_TEMPLATE = '''
671
  let isVideoEnabled = true;
672
  let reconnectAttempts = 0;
673
  const MAX_RECONNECT_ATTEMPTS = 5;
 
674
 
675
- // WebRTC configuration
676
  const rtcConfig = {
677
  iceServers: [
678
  { urls: 'stun:stun.l.google.com:19302' },
679
- {
680
- urls: 'turn:relay1.expressturn.com:3480',
681
- username: '000000002077477631',
682
- credential: 'tCIN7etd5wkqwMtvEXEcL5O6pwY='
683
- }
684
- ]
 
 
685
  };
686
 
687
  function initializeSocket() {
688
- // Получаем базовый URL для корректного подключения к WebSocket
689
- const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
690
- const host = window.location.host;
691
-
692
  socket = io({
693
  transports: ['websocket', 'polling'],
694
  upgrade: true,
@@ -704,22 +722,20 @@ HTML_TEMPLATE = '''
704
 
705
  // Socket events
706
  socket.on('connect', () => {
707
- console.log('Connected to server');
708
  updateConnectionStatus(true);
709
  reconnectAttempts = 0;
710
 
711
- // Re-register if we have a current user
712
  if (currentUser) {
713
  socket.emit('register', currentUser.username);
714
  }
715
  });
716
 
717
  socket.on('disconnect', (reason) => {
718
- console.log('Disconnected from server:', reason);
719
  updateConnectionStatus(false);
720
 
721
  if (reason === 'io server disconnect') {
722
- // Server intentionally disconnected, try to reconnect
723
  setTimeout(() => {
724
  if (reconnectAttempts < MAX_RECONNECT_ATTEMPTS) {
725
  socket.connect();
@@ -730,7 +746,7 @@ HTML_TEMPLATE = '''
730
  });
731
 
732
  socket.on('connect_error', (error) => {
733
- console.error('Connection error:', error);
734
  updateConnectionStatus(false);
735
  });
736
 
@@ -739,11 +755,13 @@ HTML_TEMPLATE = '''
739
  document.getElementById('loginScreen').style.display = 'none';
740
  document.getElementById('chatScreen').style.display = 'flex';
741
  updateConnectionStatus(true);
 
742
  });
743
 
744
  socket.on('user_list_update', (data) => {
745
  document.getElementById('onlineCount').textContent = data.count;
746
  updateUsersList(data.users || []);
 
747
  });
748
 
749
  socket.on('new_message', (message) => {
@@ -755,10 +773,12 @@ HTML_TEMPLATE = '''
755
  });
756
 
757
  socket.on('incoming_call', (data) => {
 
758
  showIncomingCall(data.from_user, data.call_id, data.isVideo);
759
  });
760
 
761
  socket.on('call_answered', (data) => {
 
762
  if (data.answer) {
763
  startCall(data.isVideo, false);
764
  } else {
@@ -768,12 +788,15 @@ HTML_TEMPLATE = '''
768
  });
769
 
770
  socket.on('call_ended', () => {
 
771
  alert('Звонок завершен');
772
  endCall();
773
  });
774
 
775
  socket.on('webrtc_offer', async (data) => {
776
- console.log('Received WebRTC offer');
 
 
777
  if (!peerConnection) {
778
  await createPeerConnection(false);
779
  }
@@ -787,33 +810,74 @@ HTML_TEMPLATE = '''
787
  to_user: data.from_user,
788
  call_id: data.call_id
789
  });
 
 
790
  } catch (error) {
791
  console.error('Error handling WebRTC offer:', error);
 
 
792
  }
793
  });
794
 
795
  socket.on('webrtc_answer', async (data) => {
796
- console.log('Received WebRTC answer');
 
 
797
  if (peerConnection) {
798
  try {
799
  await peerConnection.setRemoteDescription(new RTCSessionDescription(data.answer));
 
 
800
  } catch (error) {
801
  console.error('Error setting remote description:', error);
 
 
802
  }
803
  }
804
  });
805
 
806
  socket.on('webrtc_ice_candidate', async (data) => {
 
 
 
807
  if (peerConnection && data.candidate) {
808
  try {
809
  await peerConnection.addIceCandidate(new RTCIceCandidate(data.candidate));
 
 
810
  } catch (error) {
811
  console.error('Error adding ICE candidate:', error);
 
 
812
  }
813
  }
814
  });
815
  }
816
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
817
  function updateConnectionStatus(connected) {
818
  const statusElement = document.getElementById('connectionStatus');
819
  statusElement.style.display = 'block';
@@ -939,13 +1003,18 @@ HTML_TEMPLATE = '''
939
  const stream = await navigator.mediaDevices.getUserMedia({
940
  audio: {
941
  echoCancellation: true,
942
- noiseSuppression: true
 
943
  }
944
  });
945
 
946
- mediaRecorder = new MediaRecorder(stream, {
947
- mimeType: 'audio/webm;codecs=opus'
948
- });
 
 
 
 
949
 
950
  audioChunks = [];
951
  let startTime = Date.now();
@@ -960,6 +1029,7 @@ HTML_TEMPLATE = '''
960
  const duration = Math.round((Date.now() - startTime) / 1000);
961
  const audioBlob = new Blob(audioChunks, { type: 'audio/webm' });
962
 
 
963
  const reader = new FileReader();
964
  reader.onload = function() {
965
  const base64Audio = reader.result.split(',')[1];
@@ -970,16 +1040,18 @@ HTML_TEMPLATE = '''
970
  sender: currentUser.username,
971
  duration: duration
972
  });
 
973
  } else {
974
  alert('Нет подключения для отправки голосового сообщения');
975
  }
976
  };
977
  reader.readAsDataURL(audioBlob);
978
 
 
979
  stream.getTracks().forEach(track => track.stop());
980
  };
981
 
982
- mediaRecorder.start();
983
  isRecording = true;
984
 
985
  // Show recording overlay
@@ -987,7 +1059,7 @@ HTML_TEMPLATE = '''
987
 
988
  } catch (error) {
989
  console.error('Error recording:', error);
990
- alert('Ошибка доступа к микрофону');
991
  }
992
  }
993
 
@@ -1001,7 +1073,10 @@ HTML_TEMPLATE = '''
1001
 
1002
  function playVoiceMessage(filename) {
1003
  const audio = new Audio(`/voice/${filename}`);
1004
- audio.play().catch(e => console.error('Error playing audio:', e));
 
 
 
1005
  }
1006
 
1007
  // Call Functions
@@ -1018,6 +1093,8 @@ HTML_TEMPLATE = '''
1018
  isInitiator: true
1019
  };
1020
 
 
 
1021
  socket.emit('call_request', {
1022
  call_id: currentCall.id,
1023
  from_user: currentUser.username,
@@ -1038,6 +1115,7 @@ HTML_TEMPLATE = '''
1038
 
1039
  document.getElementById('callerName').textContent = fromUser;
1040
  document.getElementById('incomingCallOverlay').style.display = 'flex';
 
1041
  }
1042
 
1043
  function acceptCall() {
@@ -1051,6 +1129,7 @@ HTML_TEMPLATE = '''
1051
  });
1052
 
1053
  startCall(currentCall.isVideo, true);
 
1054
  } else {
1055
  alert('Нет подключения к серверу');
1056
  }
@@ -1063,6 +1142,7 @@ HTML_TEMPLATE = '''
1063
  answer: false,
1064
  to_user: currentCall.target
1065
  });
 
1066
  }
1067
 
1068
  document.getElementById('incomingCallOverlay').style.display = 'none';
@@ -1073,9 +1153,15 @@ HTML_TEMPLATE = '''
1073
  try {
1074
  showCallScreen(isVideo, isInitiator ? 'Звонок...' : 'Разговор...');
1075
 
1076
- // Get local media stream
1077
  const constraints = {
1078
- audio: true,
 
 
 
 
 
 
1079
  video: isVideo ? {
1080
  width: { ideal: 640 },
1081
  height: { ideal: 480 },
@@ -1083,16 +1169,26 @@ HTML_TEMPLATE = '''
1083
  } : false
1084
  };
1085
 
 
 
1086
  localStream = await navigator.mediaDevices.getUserMedia(constraints);
1087
  document.getElementById('localVideo').srcObject = localStream;
 
1088
 
1089
- // Create peer connection
1090
  await createPeerConnection(isInitiator);
 
1091
 
1092
  if (isInitiator) {
1093
- // Create and send offer
1094
- const offer = await peerConnection.createOffer();
 
 
 
 
 
1095
  await peerConnection.setLocalDescription(offer);
 
1096
 
1097
  if (socket && socket.connected) {
1098
  socket.emit('webrtc_offer', {
@@ -1100,14 +1196,17 @@ HTML_TEMPLATE = '''
1100
  to_user: currentCall.target,
1101
  call_id: currentCall.id
1102
  });
 
1103
  }
1104
  }
1105
 
1106
- // Start call timer
1107
  startCallTimer();
 
1108
 
1109
  } catch (error) {
1110
  console.error('Error starting call:', error);
 
1111
  alert(`Ошибка начала звонка: ${error.message}`);
1112
  endCall();
1113
  }
@@ -1115,20 +1214,24 @@ HTML_TEMPLATE = '''
1115
 
1116
  async function createPeerConnection(isInitiator) {
1117
  peerConnection = new RTCPeerConnection(rtcConfig);
 
1118
 
1119
- // Add local stream tracks
1120
  localStream.getTracks().forEach(track => {
1121
  peerConnection.addTrack(track, localStream);
 
1122
  });
1123
 
1124
- // Handle incoming stream
1125
  peerConnection.ontrack = (event) => {
1126
- console.log('Received remote track:', event.track.kind);
 
 
1127
  remoteStream = event.streams[0];
1128
  const remoteVideo = document.getElementById('remoteVideoContainer');
1129
 
1130
  if (event.track.kind === 'video') {
1131
- // Create video element for remote stream
1132
  const videoElement = document.createElement('video');
1133
  videoElement.srcObject = remoteStream;
1134
  videoElement.autoplay = true;
@@ -1137,34 +1240,62 @@ HTML_TEMPLATE = '''
1137
 
1138
  remoteVideo.innerHTML = '';
1139
  remoteVideo.appendChild(videoElement);
 
1140
  } else if (event.track.kind === 'audio') {
1141
- // For audio only, show message
1142
  if (!document.querySelector('.remote-video video')) {
1143
  remoteVideo.innerHTML = '<div>Аудиозвонок</div>';
1144
  remoteVideo.className = 'no-video remote-video';
 
1145
  }
1146
  }
1147
  };
1148
 
1149
- // Handle ICE candidates
1150
  peerConnection.onicecandidate = (event) => {
1151
  if (event.candidate && currentCall && socket && socket.connected) {
1152
  socket.emit('webrtc_ice_candidate', {
1153
  candidate: event.candidate,
1154
  to_user: currentCall.target
1155
  });
 
 
 
 
 
1156
  }
1157
  };
1158
 
1159
  peerConnection.onconnectionstatechange = () => {
1160
- console.log('Connection state:', peerConnection.connectionState);
1161
- if (peerConnection.connectionState === 'connected') {
 
 
 
1162
  document.getElementById('callStatus').textContent = 'Разговор';
 
 
 
 
 
 
 
 
 
 
 
1163
  }
1164
  };
1165
 
1166
  peerConnection.oniceconnectionstatechange = () => {
1167
- console.log('ICE connection state:', peerConnection.iceConnectionState);
 
 
 
 
 
 
 
1168
  };
1169
  }
1170
 
@@ -1177,17 +1308,22 @@ HTML_TEMPLATE = '''
1177
  remoteVideo.innerHTML = '<div>Аудиозвонок</div>';
1178
  remoteVideo.className = 'no-video remote-video';
1179
  }
 
 
1180
  }
1181
 
1182
  function hideCallScreen() {
1183
  document.getElementById('callScreen').style.display = 'none';
1184
  document.getElementById('remoteVideoContainer').innerHTML = '<div>Ожидание подключения...</div>';
1185
  document.getElementById('remoteVideoContainer').className = 'no-video remote-video';
 
1186
 
1187
  if (callTimerInterval) {
1188
  clearInterval(callTimerInterval);
1189
  callTimerInterval = null;
1190
  }
 
 
1191
  }
1192
 
1193
  function startCallTimer() {
@@ -1203,14 +1339,18 @@ HTML_TEMPLATE = '''
1203
  }
1204
 
1205
  function endCall() {
 
 
1206
  if (peerConnection) {
1207
  peerConnection.close();
1208
  peerConnection = null;
 
1209
  }
1210
 
1211
  if (localStream) {
1212
  localStream.getTracks().forEach(track => track.stop());
1213
  localStream = null;
 
1214
  }
1215
 
1216
  if (currentCall && socket && socket.connected) {
@@ -1218,12 +1358,15 @@ HTML_TEMPLATE = '''
1218
  to_user: currentCall.target,
1219
  call_id: currentCall.id
1220
  });
 
1221
  }
1222
 
1223
  hideCallScreen();
1224
  currentCall = null;
1225
  isMuted = false;
1226
  isVideoEnabled = true;
 
 
1227
  }
1228
 
1229
  function toggleMute() {
@@ -1234,6 +1377,7 @@ HTML_TEMPLATE = '''
1234
  audioTracks[0].enabled = !isMuted;
1235
  document.getElementById('muteBtn').classList.toggle('active', isMuted);
1236
  document.getElementById('muteBtn').textContent = isMuted ? '🎤🚫' : '🎤';
 
1237
  }
1238
  }
1239
  }
@@ -1247,8 +1391,9 @@ HTML_TEMPLATE = '''
1247
  document.getElementById('videoBtn').classList.toggle('active', !isVideoEnabled);
1248
  document.getElementById('videoBtn').textContent = isVideoEnabled ? '📹' : '📹🚫';
1249
 
1250
- // Hide/show local video
1251
  document.getElementById('localVideo').style.display = isVideoEnabled ? 'block' : 'none';
 
1252
  }
1253
  }
1254
  }
@@ -1268,7 +1413,7 @@ HTML_TEMPLATE = '''
1268
  }
1269
  });
1270
 
1271
- // Close sidebar when clicking outside
1272
  document.addEventListener('click', (e) => {
1273
  const sidebar = document.getElementById('usersSidebar');
1274
  if (sidebar.classList.contains('open') &&
@@ -1278,12 +1423,12 @@ HTML_TEMPLATE = '''
1278
  }
1279
  });
1280
 
1281
- // Initialize socket on load
1282
  window.addEventListener('load', function() {
1283
  initializeSocket();
1284
  });
1285
 
1286
- // Handle page visibility changes
1287
  document.addEventListener('visibilitychange', function() {
1288
  if (!document.hidden && (!socket || !socket.connected)) {
1289
  initializeSocket();
@@ -1310,12 +1455,12 @@ def handle_connect():
1310
  def handle_disconnect():
1311
  print(f'Client disconnected: {request.sid}')
1312
 
1313
- # Find and remove the disconnected user
1314
  disconnected_user = None
1315
  for user_id, user_data in list(users.items()):
1316
  if user_data.get('sid') == request.sid:
1317
  disconnected_user = user_data['username']
1318
- # End any active calls
1319
  for call_id, call_data in list(active_calls.items()):
1320
  if call_data['from_user'] == disconnected_user or call_data['to_user'] == disconnected_user:
1321
  try:
@@ -1513,6 +1658,7 @@ if __name__ == '__main__':
1513
  try:
1514
  print("Запуск мобильного мессенджера...")
1515
  print("Порт: 7860")
 
1516
  socketio.run(
1517
  app,
1518
  host='0.0.0.0',
 
17
  cors_allowed_origins="*",
18
  ping_timeout=60,
19
  ping_interval=25,
20
+ logger=False,
21
  engineio_logger=False,
22
  async_mode='eventlet',
23
+ max_http_buffer_size=100 * 1024 * 1024
24
  )
25
 
26
  # Хранилища данных
 
557
  background: #FF4444;
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>
576
  <body>
 
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
  </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
  <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
  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' },
699
+ { urls: 'stun:stun1.l.google.com:19302' },
700
+ { urls: 'stun:stun2.l.google.com:19302' },
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() {
 
 
 
 
710
  socket = io({
711
  transports: ['websocket', 'polling'],
712
  upgrade: true,
 
722
 
723
  // Socket events
724
  socket.on('connect', () => {
725
+ logDebug('Connected to server');
726
  updateConnectionStatus(true);
727
  reconnectAttempts = 0;
728
 
 
729
  if (currentUser) {
730
  socket.emit('register', currentUser.username);
731
  }
732
  });
733
 
734
  socket.on('disconnect', (reason) => {
735
+ logDebug('Disconnected from server: ' + reason);
736
  updateConnectionStatus(false);
737
 
738
  if (reason === 'io server disconnect') {
 
739
  setTimeout(() => {
740
  if (reconnectAttempts < MAX_RECONNECT_ATTEMPTS) {
741
  socket.connect();
 
746
  });
747
 
748
  socket.on('connect_error', (error) => {
749
+ logDebug('Connection error: ' + error);
750
  updateConnectionStatus(false);
751
  });
752
 
 
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
  });
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
  });
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
  }
 
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';
 
1003
  const stream = await navigator.mediaDevices.getUserMedia({
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);
1018
 
1019
  audioChunks = [];
1020
  let startTime = Date.now();
 
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
  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
 
1059
 
1060
  } catch (error) {
1061
  console.error('Error recording:', error);
1062
+ alert('Ошибка доступа к микрофону: ' + error.message);
1063
  }
1064
  }
1065
 
 
1073
 
1074
  function playVoiceMessage(filename) {
1075
  const audio = new Audio(`/voice/${filename}`);
1076
+ audio.play().catch(e => {
1077
+ console.error('Error playing audio:', e);
1078
+ alert('Ошибка воспроизведения аудио');
1079
+ });
1080
  }
1081
 
1082
  // Call Functions
 
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
 
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
  });
1130
 
1131
  startCall(currentCall.isVideo, true);
1132
+ logDebug('Call accepted');
1133
  } else {
1134
  alert('Нет подключения к серверу');
1135
  }
 
1142
  answer: false,
1143
  to_user: currentCall.target
1144
  });
1145
+ logDebug('Call rejected');
1146
  }
1147
 
1148
  document.getElementById('incomingCallOverlay').style.display = 'none';
 
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 },
 
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
  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
  }
 
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
 
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
 
 
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
  }
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
  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
  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
  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
  }
1414
  });
1415
 
1416
+ // Закрываем sidebar при клике вне его
1417
  document.addEventListener('click', (e) => {
1418
  const sidebar = document.getElementById('usersSidebar');
1419
  if (sidebar.classList.contains('open') &&
 
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
  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
  try:
1659
  print("Запуск мобильного мессенджера...")
1660
  print("Порт: 7860")
1661
+ print("WebRTC: Только STUN серверы (без TURN)")
1662
  socketio.run(
1663
  app,
1664
  host='0.0.0.0',