#!/usr/bin/env python3 """ Простой веб-сервер для панели оператора GodEye Обслуживает WebRTC интерфейс и Socket.IO соединения """ import socketio import eventlet import eventlet.wsgi from flask import Flask, send_file import json import uuid from datetime import datetime import os # Создаем Flask приложение app = Flask(__name__) # Создаем Socket.IO сервер sio = socketio.Server(cors_allowed_origins="*") app.wsgi_app = socketio.WSGIApp(sio, app.wsgi_app) # Хранилище подключенных устройств и операторов connected_devices = {} connected_operators = {} active_sessions = {} @app.route('/') def serve_interface(): """Обслуживает панель оператора""" return send_file('operator-interface.html') @sio.event def connect(sid, environ): """Обработка подключения клиента""" print(f"[{datetime.now().strftime('%H:%M:%S')}] 🔗 Клиент подключился: {sid}") @sio.event def disconnect(sid): """Обработка отключения клиента""" print(f"[{datetime.now().strftime('%H:%M:%S')}] ❌ Клиент отключился: {sid}") # Удаляем из устройств if sid in connected_devices: device_data = connected_devices[sid] del connected_devices[sid] sio.emit('device_disconnected', { 'deviceId': device_data['deviceId'], 'deviceName': device_data['deviceName'] }) print(f"[{datetime.now().strftime('%H:%M:%S')}] 📱 Устройство отключилось: {device_data['deviceName']}") # Удаляем из операторов if sid in connected_operators: del connected_operators[sid] print(f"[{datetime.now().strftime('%H:%M:%S')}] 👨‍💼 Оператор отключился") @sio.event def device_register(sid, data): """Регистрация Android устройства""" device_id = data.get('deviceId') device_name = data.get('deviceName', 'Unknown Device') connected_devices[sid] = { 'deviceId': device_id, 'deviceName': device_name, 'connectedAt': datetime.now().isoformat() } print(f"[{datetime.now().strftime('%H:%M:%S')}] 📱 Устройство зарегистрировано: {device_name} ({device_id})") # Уведомляем всех операторов о новом устройстве sio.emit('device_connected', { 'deviceId': device_id, 'deviceName': device_name }) # Отправляем подтверждение устройству sio.emit('registration_confirmed', { 'status': 'success', 'message': 'Устройство успешно зарегистрировано' }, room=sid) @sio.event def operator_register(sid, data): """Регистрация оператора""" operator_id = data.get('operatorId', f'operator_{uuid.uuid4().hex[:8]}') connected_operators[sid] = { 'operatorId': operator_id, 'connectedAt': datetime.now().isoformat() } print(f"[{datetime.now().strftime('%H:%M:%S')}] 👨‍💼 Оператор зарегистрирован: {operator_id}") # Отправляем список подключенных устройств devices_list = [] for device_sid, device_data in connected_devices.items(): devices_list.append({ 'deviceId': device_data['deviceId'], 'deviceName': device_data['deviceName'] }) sio.emit('devices_list', {'devices': devices_list}, room=sid) @sio.event def request_camera(sid, data): """Запрос камеры от оператора""" device_id = data.get('deviceId') operator_id = data.get('operatorId') camera_type = data.get('cameraType', 'back') # Убеждаемся что есть значение по умолчанию print(f"[{datetime.now().strftime('%H:%M:%S')}] 📹 Запрос камеры от {operator_id} для устройства {device_id}, тип камеры: {camera_type}") # Находим устройство по ID target_device_sid = None for device_sid, device_data in connected_devices.items(): if device_data['deviceId'] == device_id: target_device_sid = device_sid break if target_device_sid: session_id = str(uuid.uuid4()) # Сохраняем сессию с правильными данными active_sessions[session_id] = { 'deviceSid': target_device_sid, 'operatorSid': sid, 'deviceId': device_id, 'operatorId': operator_id, 'cameraType': camera_type, # Явно сохраняем тип камеры 'status': 'pending', # Добавляем статус 'startedAt': datetime.now().isoformat() } # Отправляем запрос устройству с полными данными sio.emit('camera_request', { 'sessionId': session_id, 'operatorId': operator_id, 'cameraType': camera_type, 'deviceId': device_id, # Добавляем deviceId 'message': f'Оператор {operator_id} запрашивает доступ к камере' }, room=target_device_sid) # Уведомляем оператора о создании сессии sio.emit('session_created', { 'sessionId': session_id, 'deviceId': device_id, 'cameraType': camera_type, 'status': 'pending' }, room=sid) print(f"[{datetime.now().strftime('%H:%M:%S')}] 📤 Запрос отправлен устройству, сессия: {session_id}, камера: {camera_type}") else: sio.emit('error', { 'message': f'Устройство {device_id} не найдено' }, room=sid) @sio.event def camera_approved(sid, data): """Подтверждение доступа к камере от устройства""" session_id = data.get('sessionId') if session_id in active_sessions: session = active_sessions[session_id] operator_sid = session['operatorSid'] # Обновляем статус сессии active_sessions[session_id]['status'] = 'approved' print(f"[{datetime.now().strftime('%H:%M:%S')}] ✅ Доступ к камере подтвержден для сессии: {session_id}") # Уведомляем оператора с полными данными sio.emit('camera_approved', { 'sessionId': session_id, 'deviceId': session['deviceId'], 'cameraType': session['cameraType'], 'status': 'approved' }, room=operator_sid) @sio.event def camera_rejected(sid, data): """Отклонение доступа к камере от устройства""" session_id = data.get('sessionId') reason = data.get('reason', 'Отклонено пользователем') if session_id in active_sessions: session = active_sessions[session_id] operator_sid = session['operatorSid'] print(f"[{datetime.now().strftime('%H:%M:%S')}] ❌ Доступ к камере отклонен для сессии: {session_id}, причина: {reason}") # Уведомляем оператора об отклонении sio.emit('camera_rejected', { 'sessionId': session_id, 'deviceId': session['deviceId'], 'reason': reason }, room=operator_sid) # Удаляем сессию, так как она отклонена del active_sessions[session_id] @sio.event def camera_started(sid, data): """Уведомление о запуске камеры""" session_id = data.get('sessionId') if session_id in active_sessions: session = active_sessions[session_id] operator_sid = session['operatorSid'] # Обновляем статус сессии на активный active_sessions[session_id]['status'] = 'active' active_sessions[session_id]['actualStartedAt'] = datetime.now().isoformat() print(f"[{datetime.now().strftime('%H:%M:%S')}] 📹 Камера запущена для сессии: {session_id}") # Уведомляем оператора с обновленным статусом sio.emit('camera_started', { 'sessionId': session_id, 'deviceId': session['deviceId'], 'cameraType': session['cameraType'], 'status': 'active' }, room=operator_sid) # Также отправляем обновление статуса сессии sio.emit('session_status_update', { 'sessionId': session_id, 'status': 'active', 'cameraType': session['cameraType'] }, room=operator_sid) @sio.event def webrtc_offer(sid, data): """Пересылка WebRTC offer от устройства к оператору""" session_id = data.get('sessionId') offer = data.get('offer') if session_id in active_sessions: session = active_sessions[session_id] operator_sid = session['operatorSid'] print(f"[{datetime.now().strftime('%H:%M:%S')}] 📡 Пересылка WebRTC offer для сессии: {session_id}") # Пересылаем offer оператору sio.emit('webrtc_offer', { 'sessionId': session_id, 'offer': offer }, room=operator_sid) @sio.event def webrtc_answer(sid, data): """Пересылка WebRTC answer от оператора к устройству""" session_id = data.get('sessionId') answer = data.get('answer') if session_id in active_sessions: session = active_sessions[session_id] device_sid = session['deviceSid'] print(f"[{datetime.now().strftime('%H:%M:%S')}] 📡 Пересылка WebRTC answer для сессии: {session_id}") # Пересылаем answer устройству sio.emit('webrtc_answer', { 'sessionId': session_id, 'answer': answer }, room=device_sid) @sio.event def webrtc_ice_candidate(sid, data): """Пересылка ICE candidates между устройством и оператором""" session_id = data.get('sessionId') candidate = data.get('candidate') if session_id in active_sessions: session = active_sessions[session_id] # Определяем куда пересылать (от устройства к оператору или наоборот) if sid == session['deviceSid']: # От устройства к оператору target_sid = session['operatorSid'] else: # От оператора к устройству target_sid = session['deviceSid'] print(f"[{datetime.now().strftime('%H:%M:%S')}] 🧊 Пересылка ICE candidate для сессии: {session_id}") sio.emit('webrtc_ice_candidate', { 'sessionId': session_id, 'candidate': candidate }, room=target_sid) @sio.event def switch_camera(sid, data): """Переключение камеры""" session_id = data.get('sessionId') if session_id in active_sessions: session = active_sessions[session_id] device_sid = session['deviceSid'] print(f"[{datetime.now().strftime('%H:%M:%S')}] 🔄 Переключение камеры для сессии: {session_id}") # Отправляем команду устройству sio.emit('switch_camera', { 'sessionId': session_id }, room=device_sid) @sio.event def stop_camera(sid, data): """Остановка камеры""" session_id = data.get('sessionId') if session_id in active_sessions: session = active_sessions[session_id] device_sid = session['deviceSid'] operator_sid = session['operatorSid'] print(f"[{datetime.now().strftime('%H:%M:%S')}] 🛑 Остановка камеры для сессии: {session_id}") # Отправляем команду устройству sio.emit('stop_camera', { 'sessionId': session_id }, room=device_sid) # Уведомляем оператора sio.emit('camera_stopped', { 'sessionId': session_id }, room=operator_sid) # Удаляем сессию del active_sessions[session_id] if __name__ == '__main__': # ИСПРАВЛЕНО: Используем localhost для локального тестирования host = '0.0.0.0' # Слушаем на всех интерфейсах port = 3001 print("🚀 Запуск GodEye сервера...") print(f"🌐 Веб-интерфейс: http://localhost:{port}") print(f"🔌 Socket.IO endpoint: http://localhost:{port}/socket.io/") print("📱 Настройте Android приложение на адрес вашего компьютера") print("-" * 50) try: eventlet.wsgi.server(eventlet.listen((host, port)), app) except KeyboardInterrupt: print("\n🛑 Сервер остановлен") except Exception as e: print(f"❌ Ошибка сервера: {e}")