Add network status/latency info
This commit is contained in:
@@ -52,7 +52,9 @@ class VlcSyncApp(QMainWindow):
|
||||
# Network Service
|
||||
self.sync_client = SyncClientThread("wss://video-sync.peterstockings.com/ws")
|
||||
self.sync_client.connected.connect(self.on_ws_connected)
|
||||
self.sync_client.connected.connect(lambda: self.room_widget.update_connection_status(True))
|
||||
self.sync_client.disconnected.connect(self.on_ws_disconnected)
|
||||
self.sync_client.disconnected.connect(lambda: self.room_widget.update_connection_status(False))
|
||||
self.sync_client.room_joined.connect(self.on_room_joined)
|
||||
self.sync_client.room_rejoined.connect(self.on_room_rejoined)
|
||||
self.sync_client.room_error.connect(self.on_room_error)
|
||||
@@ -61,6 +63,7 @@ class VlcSyncApp(QMainWindow):
|
||||
self.sync_client.chat_message.connect(self.room_widget.add_chat_message)
|
||||
self.sync_client.system_message.connect(self.room_widget.add_system_message)
|
||||
self.sync_client.sync_event.connect(self.room_widget.handle_sync_event)
|
||||
self.sync_client.latency_updated.connect(self.room_widget.update_latency)
|
||||
|
||||
self.apply_stylesheet()
|
||||
|
||||
|
||||
@@ -120,6 +120,12 @@ class RoomWidget(QWidget):
|
||||
self.copy_code_btn.setToolTip("Copy Room Code")
|
||||
self.copy_code_btn.clicked.connect(self.copy_room_code)
|
||||
|
||||
self.status_dot = QLabel("●")
|
||||
self.status_dot.setFixedWidth(20)
|
||||
self.status_dot.setStyleSheet("color: #888; font-size: 14px; background: transparent;")
|
||||
self.status_dot.setToolTip("Connecting...")
|
||||
self._latency_ms = None
|
||||
|
||||
self.room_file_badge = QLabel("📄 No file")
|
||||
self.room_file_badge.setObjectName("fileBadge")
|
||||
|
||||
@@ -129,6 +135,7 @@ class RoomWidget(QWidget):
|
||||
|
||||
topbar_layout.addWidget(self.room_code_display)
|
||||
topbar_layout.addWidget(self.copy_code_btn)
|
||||
topbar_layout.addWidget(self.status_dot)
|
||||
topbar_layout.addStretch()
|
||||
topbar_layout.addWidget(self.room_file_badge)
|
||||
topbar_layout.addWidget(self.leave_btn)
|
||||
@@ -345,6 +352,26 @@ class RoomWidget(QWidget):
|
||||
def set_room_code_display(self, text: str):
|
||||
self.room_code_display.setText(f"Room: {text}")
|
||||
|
||||
def update_connection_status(self, connected: bool):
|
||||
if connected:
|
||||
self.status_dot.setStyleSheet("color: #4BB543; font-size: 14px; background: transparent;")
|
||||
self.status_dot.setToolTip("Connected")
|
||||
else:
|
||||
self.status_dot.setStyleSheet("color: #ff4e45; font-size: 14px; background: transparent;")
|
||||
self.status_dot.setToolTip("Disconnected")
|
||||
self._latency_ms = None
|
||||
|
||||
def update_latency(self, latency_ms: int):
|
||||
self._latency_ms = latency_ms
|
||||
if latency_ms < 100:
|
||||
color = "#4BB543" # green
|
||||
elif latency_ms < 250:
|
||||
color = "#f0ad4e" # yellow
|
||||
else:
|
||||
color = "#ff4e45" # red
|
||||
self.status_dot.setStyleSheet(f"color: {color}; font-size: 14px; background: transparent;")
|
||||
self.status_dot.setToolTip(f"Latency: {latency_ms}ms")
|
||||
|
||||
def toggle_tags_panel(self):
|
||||
if self.tags_list.isHidden():
|
||||
self.tags_list.show()
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import asyncio
|
||||
import json
|
||||
import time
|
||||
import websockets
|
||||
from PyQt6.QtCore import QThread, pyqtSignal
|
||||
|
||||
@@ -15,6 +16,7 @@ class SyncClientThread(QThread):
|
||||
chat_message = pyqtSignal(str, str, int) # author, text, timestamp
|
||||
system_message = pyqtSignal(str)
|
||||
sync_event = pyqtSignal(dict)
|
||||
latency_updated = pyqtSignal(int) # latency in ms
|
||||
|
||||
def __init__(self, url="ws://localhost:3000/ws"):
|
||||
super().__init__()
|
||||
@@ -22,6 +24,7 @@ class SyncClientThread(QThread):
|
||||
self.ws = None
|
||||
self.loop = None
|
||||
self.running = False
|
||||
self._ping_sent_at = 0
|
||||
|
||||
def run(self):
|
||||
"""Runs strictly within the newly created QThread"""
|
||||
@@ -44,6 +47,19 @@ class SyncClientThread(QThread):
|
||||
json_str = json.dumps(message)
|
||||
asyncio.run_coroutine_threadsafe(self.ws.send(json_str), self.loop)
|
||||
|
||||
async def _ping_loop(self, ws):
|
||||
"""Sends WebSocket protocol-level pings every 5s to measure latency."""
|
||||
while self.running:
|
||||
try:
|
||||
pong = await ws.ping()
|
||||
sent_at = time.time()
|
||||
await asyncio.wait_for(pong, timeout=5)
|
||||
latency_ms = int((time.time() - sent_at) * 1000)
|
||||
self.latency_updated.emit(latency_ms)
|
||||
except Exception:
|
||||
break
|
||||
await asyncio.sleep(5)
|
||||
|
||||
async def _connect_and_listen(self):
|
||||
while self.running:
|
||||
try:
|
||||
@@ -51,6 +67,7 @@ class SyncClientThread(QThread):
|
||||
self.ws = ws
|
||||
self.connected.emit()
|
||||
|
||||
ping_task = asyncio.create_task(self._ping_loop(ws))
|
||||
try:
|
||||
async for message in ws:
|
||||
if not self.running:
|
||||
@@ -58,6 +75,8 @@ class SyncClientThread(QThread):
|
||||
self._handle_message(json.loads(message))
|
||||
except websockets.ConnectionClosed:
|
||||
pass
|
||||
finally:
|
||||
ping_task.cancel()
|
||||
|
||||
except Exception as e:
|
||||
print(f"WebSocket Error: {e}")
|
||||
|
||||
Reference in New Issue
Block a user