rebuild
This commit is contained in:
60
rebuild.sh
Executable file
60
rebuild.sh
Executable file
@@ -0,0 +1,60 @@
|
||||
#!/bin/bash
|
||||
# Скрипт для обновления и пересборки Docker контейнера
|
||||
|
||||
set -e
|
||||
|
||||
echo "=========================================="
|
||||
echo "🔄 Updating Camera Server Container"
|
||||
echo "=========================================="
|
||||
|
||||
# Проверяем, находимся ли в корректной директории
|
||||
if [ ! -f "docker-compose.yml" ]; then
|
||||
echo "❌ Error: docker-compose.yml not found"
|
||||
echo "Please run this script from the project root directory"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "1️⃣ Stopping container..."
|
||||
docker-compose down
|
||||
|
||||
echo ""
|
||||
echo "2️⃣ Rebuilding image..."
|
||||
docker-compose build --no-cache
|
||||
|
||||
echo ""
|
||||
echo "3️⃣ Starting container..."
|
||||
docker-compose up -d
|
||||
|
||||
echo ""
|
||||
echo "4️⃣ Waiting for container to be ready..."
|
||||
sleep 3
|
||||
|
||||
echo ""
|
||||
echo "5️⃣ Checking container status..."
|
||||
docker-compose ps
|
||||
|
||||
echo ""
|
||||
echo "6️⃣ Recent logs:"
|
||||
docker-compose logs --tail 20
|
||||
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo "✅ Update complete!"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
echo "Server should be running on:"
|
||||
docker-compose exec camera_server python3 -c "
|
||||
import os
|
||||
from dotenv import load_dotenv
|
||||
load_dotenv()
|
||||
bind_host = os.getenv('BIND_HOST', '0.0.0.0')
|
||||
public_host = os.getenv('PUBLIC_HOST', 'localhost')
|
||||
port = os.getenv('PORT', '8000')
|
||||
print(f' 📡 Public: http://{public_host}:{port}')
|
||||
print(f' 🔗 Local: http://127.0.0.1:{port}')
|
||||
" 2>/dev/null || echo " 📡 Check .env for PUBLIC_HOST and PORT"
|
||||
|
||||
echo ""
|
||||
echo "To view logs in real-time:"
|
||||
echo " docker-compose logs -f"
|
||||
75
server.py
75
server.py
@@ -77,6 +77,7 @@ server_stats = None
|
||||
stats_lock = None
|
||||
cleanup_task = None
|
||||
templates = None
|
||||
admin_frame_queues = None
|
||||
|
||||
# Администраторы
|
||||
ADMINS = [
|
||||
@@ -571,6 +572,7 @@ async def lifespan(app: FastAPI):
|
||||
admin_websockets = {}
|
||||
video_queues = {}
|
||||
command_queues = {}
|
||||
admin_frame_queues = {}
|
||||
room_stats = {}
|
||||
server_stats = {
|
||||
"total_rooms": 0,
|
||||
@@ -1072,6 +1074,17 @@ async def client_websocket_endpoint(websocket: WebSocket, room_id: str, password
|
||||
print(f"[WebSocket Client] ⚠️ {client_id}: video queue is FULL, dropping frame")
|
||||
else:
|
||||
print(f"[WebSocket Client] ❌ {client_id}: video_queue not found!")
|
||||
# Также пробуем переслать тот же байтовый кадр администраторам, если кто-то смотрит
|
||||
try:
|
||||
if admin_frame_queues is not None and client_id in admin_frame_queues:
|
||||
aq = admin_frame_queues[client_id]
|
||||
try:
|
||||
aq.put_nowait(message)
|
||||
except asyncio.QueueFull:
|
||||
# Если очередь админа переполнена, пропускаем кадр
|
||||
pass
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
elif isinstance(message, str):
|
||||
try:
|
||||
@@ -1113,6 +1126,12 @@ async def client_websocket_endpoint(websocket: WebSocket, room_id: str, password
|
||||
|
||||
if video_processor:
|
||||
video_processor.stop()
|
||||
# Очищаем очередь админа, чтобы фреймы перестали отправляться
|
||||
try:
|
||||
if admin_frame_queues is not None and client_id in admin_frame_queues:
|
||||
del admin_frame_queues[client_id]
|
||||
except Exception:
|
||||
pass
|
||||
print(f"[WebSocket Client] ✓ VideoProcessor stopped for {client_id}")
|
||||
print(f"[WebSocket Client] ===== END OF CONNECTION =====\n")
|
||||
|
||||
@@ -1191,26 +1210,60 @@ async def admin_websocket_endpoint(websocket: WebSocket, session_id: str):
|
||||
|
||||
async def _stream_client_to_admin(client_id: str, admin_session_id: str):
|
||||
"""Потоковая передача видео от клиента к администратору"""
|
||||
global admin_frame_queues, admin_websockets, clients, stats_lock
|
||||
|
||||
if client_id not in clients or admin_session_id not in admin_websockets:
|
||||
return
|
||||
|
||||
|
||||
try:
|
||||
await admin_websockets[admin_session_id].send_text(json.dumps({
|
||||
"type": "stream_started",
|
||||
"client_id": client_id
|
||||
}))
|
||||
|
||||
ws = admin_websockets[admin_session_id]
|
||||
# Уведомляем админа о старте стрима
|
||||
await ws.send_text(json.dumps({"type": "stream_started", "client_id": client_id}))
|
||||
|
||||
with stats_lock:
|
||||
if client_id in clients:
|
||||
client = clients[client_id]
|
||||
await admin_websockets[admin_session_id].send_text(json.dumps({
|
||||
await ws.send_text(json.dumps({
|
||||
"type": "stream_info",
|
||||
"message": f"Streaming from client {client_id}",
|
||||
"quality": client["video_settings"]["quality"],
|
||||
"ip": client["ip_address"],
|
||||
"connected_at": client["connected_at"]
|
||||
"quality": client.get("video_settings", {}).get("quality"),
|
||||
"ip": client.get("ip_address"),
|
||||
"connected_at": client.get("connected_at")
|
||||
}))
|
||||
|
||||
|
||||
# Подготовим очередь для пересылки байтов администраторам
|
||||
if admin_frame_queues is None:
|
||||
return
|
||||
|
||||
if client_id not in admin_frame_queues:
|
||||
# Создаём очередь для этого клиента, если её нет
|
||||
admin_frame_queues[client_id] = asyncio.Queue(maxsize=128)
|
||||
|
||||
aq = admin_frame_queues[client_id]
|
||||
print(f"[WebSocket] Started streaming frames to admin {admin_session_id} for client {client_id}")
|
||||
|
||||
# Цикл чтения кадров из очереди и отправки их админу
|
||||
# Выходим только если очередь удалена (клиент отключился) или произойдёт исключение
|
||||
try:
|
||||
while client_id in admin_frame_queues:
|
||||
try:
|
||||
# Пытаемся получить кадр с таймаутом, чтобы периодически проверять наличие очереди
|
||||
frame_bytes = await asyncio.wait_for(aq.get(), timeout=2.0)
|
||||
try:
|
||||
await ws.send_bytes(frame_bytes)
|
||||
except Exception as e:
|
||||
print(f"[WebSocket] Error sending bytes to admin {admin_session_id}: {e}")
|
||||
break
|
||||
except asyncio.TimeoutError:
|
||||
# Таймаут OK — просто продолжаем ждать
|
||||
continue
|
||||
except asyncio.CancelledError:
|
||||
break
|
||||
except Exception as e:
|
||||
print(f"[WebSocket] Error in admin frame loop: {e}")
|
||||
finally:
|
||||
print(f"[WebSocket] Stopped streaming frames to admin {admin_session_id} for client {client_id}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"[WebSocket] Error streaming to admin: {e}")
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ from pathlib import Path
|
||||
# Параметры подключения
|
||||
HOST = "192.168.0.112" # Измените на ваш PUBLIC_HOST из .env
|
||||
PORT = 8000
|
||||
ROOM_ID = "DazqDVlwdGX" # Измените на ID комнаты
|
||||
ROOM_ID = "AtR2yGhr8QM" # Измените на ID комнаты
|
||||
PASSWORD = "1" # Измените на пароль комнаты
|
||||
FRAME_FILE = "test_frame.jpg" # Путь к тестовому JPEG-файлу
|
||||
|
||||
|
||||
Reference in New Issue
Block a user