From 4665cf700eb67c075e05c3f65ae57fa98d3d5b39 Mon Sep 17 00:00:00 2001 From: Peter Stockings Date: Tue, 3 Mar 2026 19:30:46 +1100 Subject: [PATCH] Add support for /seek, /play, /pause commands in chat --- desktop-client/main.py | 87 ++++++++++++++++++++++++++++-- desktop-client/test_integration.py | 2 +- 2 files changed, 85 insertions(+), 4 deletions(-) diff --git a/desktop-client/main.py b/desktop-client/main.py index 9aa53f8..c9098bb 100644 --- a/desktop-client/main.py +++ b/desktop-client/main.py @@ -593,11 +593,92 @@ class VlcSyncApp(QMainWindow): def fmt(s): return f"{int(s)//3600:02d}:{(int(s)%3600)//60:02d}:{int(s)%60:02d}" self.on_system_message(f"{username} seeked to {fmt(position_s)}") + def _handle_seek_command(self, arg: str) -> bool: + current_ms = self.vlc_player.current_time_ms + length_ms = self.vlc_player.get_length() + if length_ms <= 0: + return False + + try: + target_ms = 0 + if arg.startswith('+') or arg.startswith('-'): + # relative + modifier = 1 if arg.startswith('+') else -1 + num_str = arg[1:] + + if num_str.endswith('s'): + val = float(num_str[:-1]) * 1000 + elif num_str.endswith('m'): + val = float(num_str[:-1]) * 60 * 1000 + elif num_str.endswith('h'): + val = float(num_str[:-1]) * 3600 * 1000 + else: + val = float(num_str) * 1000 # Default to seconds + + target_ms = current_ms + (val * modifier) + elif ":" in arg: + # absolute time like HH:MM:SS or MM:SS + parts = arg.split(":") + parts.reverse() # seconds, minutes, hours + if len(parts) > 0: target_ms += float(parts[0]) * 1000 + if len(parts) > 1: target_ms += float(parts[1]) * 60 * 1000 + if len(parts) > 2: target_ms += float(parts[2]) * 3600 * 1000 + else: + # absolute seconds or something with a suffix but no + or - + if arg.endswith('s'): + target_ms = float(arg[:-1]) * 1000 + elif arg.endswith('m'): + target_ms = float(arg[:-1]) * 60 * 1000 + elif arg.endswith('h'): + target_ms = float(arg[:-1]) * 3600 * 1000 + else: + target_ms = float(arg) * 1000 + + target_ms = max(0, min(target_ms, length_ms)) + self.vlc_player.seek(int(target_ms)) + self.sync_client.send_message({"type": "sync", "action": "seek", "position": target_ms / 1000.0}) + return True + except ValueError: + return False + def send_chat(self): text = self.chat_input.text().strip() - if text: - self.sync_client.send_message({"type": "chat", "message": text}) - self.chat_input.setText("") + if not text: + return + + if text.startswith("/"): + parts = text.split() + cmd = parts[0].lower() + + if cmd == "/play": + self.chat_input.setText("") + if not self.vlc_player.is_playing: + self.toggle_playback() + self.on_system_message(text) + return + elif cmd == "/pause": + self.chat_input.setText("") + if self.vlc_player.is_playing: + self.toggle_playback() + self.on_system_message(text) + return + elif cmd == "/seek": + self.chat_input.setText("") + if len(parts) > 1: + if self._handle_seek_command(parts[1]): + self.on_system_message(text) + else: + self.on_system_message("Invalid time format. Use: 1:23, +30s, -1m") + else: + self.on_system_message("Usage: /seek [time]") + return + elif cmd == "/help": + self.chat_input.setText("") + self.on_system_message("Available commands:
/play - Resume playback
/pause - Pause playback
/seek [time] - Seek to specific time (e.g., 1:23) or offset (e.g., +30s, -1m)
/help - Show this message") + return + + self.sync_client.send_message({"type": "chat", "message": text}) + self.chat_input.setText("") def apply_stylesheet(self): self.setStyleSheet(""" diff --git a/desktop-client/test_integration.py b/desktop-client/test_integration.py index 933cabe..b3c6339 100644 --- a/desktop-client/test_integration.py +++ b/desktop-client/test_integration.py @@ -4,7 +4,7 @@ import websockets import sys async def run_integration_test(): - url = "ws://localhost:3000/ws" + url = "wss://video-sync.peterstockings.com/ws" print("🤖 Test: Starting Integration Test Suite...")