import os import re from PyQt6.QtWidgets import ( QWidget, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QPushButton, QFileDialog, QFrame, QApplication ) from PyQt6.QtCore import Qt, pyqtSignal class RoomCodeInput(QLineEdit): """QLineEdit that auto-fills from clipboard if it looks like a room code.""" def focusInEvent(self, event): super().focusInEvent(event) if not self.text().strip(): self._try_paste_room_code() def _try_paste_room_code(self): clipboard = QApplication.clipboard() text = (clipboard.text() or "").strip().upper() if re.match(r'^[A-Z0-9]{4,8}$', text): self.setText(text) self.selectAll() class LobbyWidget(QWidget): # Signals to communicate to VlcSyncApp create_requested = pyqtSignal(str, str, str, object) # username, path, filename, size join_requested = pyqtSignal(str, str) # username, room_code def __init__(self): super().__init__() self.setAcceptDrops(True) self.local_file_path = None self.local_file_name = None self.local_file_size = 0 self._setup_ui() def _setup_ui(self): layout = QVBoxLayout(self) layout.setAlignment(Qt.AlignmentFlag.AlignCenter) # Container for centering container = QFrame() container.setObjectName("lobbyCard") container.setFixedWidth(500) container_layout = QVBoxLayout(container) container_layout.setContentsMargins(30, 30, 30, 30) container_layout.setSpacing(15) # Brand title = QLabel("VideoSync") title.setObjectName("brandTitle") title.setAlignment(Qt.AlignmentFlag.AlignCenter) tagline = QLabel("Watch together, anywhere") tagline.setObjectName("brandTagline") tagline.setAlignment(Qt.AlignmentFlag.AlignCenter) container_layout.addWidget(title) container_layout.addWidget(tagline) container_layout.addSpacing(20) # Username self.username_input = QLineEdit() self.username_input.setPlaceholderText("Enter a display name") container_layout.addWidget(QLabel("YOUR NAME")) container_layout.addWidget(self.username_input) container_layout.addSpacing(20) # Actions Layout (Create vs Join) actions_layout = QHBoxLayout() actions_layout.setSpacing(20) # Create Room Panel create_panel = QVBoxLayout() create_panel.addWidget(QLabel("Create a Room")) self.create_file_btn = QPushButton("Choose Video File") self.create_file_btn.setObjectName("secondaryBtn") self.create_file_btn.clicked.connect(self.select_file) self.create_file_info = QLabel("") self.create_file_info.setObjectName("fileInfo") self.create_file_info.setWordWrap(True) self.create_file_info.hide() self.create_room_btn = QPushButton("Create Room") self.create_room_btn.setObjectName("primaryBtn") self.create_room_btn.setEnabled(False) self.create_room_btn.clicked.connect(self.create_room) create_panel.addWidget(self.create_file_btn) create_panel.addWidget(self.create_file_info) create_panel.addWidget(self.create_room_btn) # Join Room Panel join_panel = QVBoxLayout() join_panel.addWidget(QLabel("Join a Room")) self.room_code_input = RoomCodeInput() self.room_code_input.setPlaceholderText("e.g. ABC123") self.join_room_btn = QPushButton("Join Room") self.join_room_btn.setObjectName("secondaryBtn") self.join_room_btn.setEnabled(False) self.join_room_btn.clicked.connect(self.join_room) join_panel.addWidget(self.room_code_input) join_panel.addWidget(self.join_room_btn) actions_layout.addLayout(create_panel) # Divider divider = QLabel("OR") divider.setAlignment(Qt.AlignmentFlag.AlignCenter) divider.setObjectName("divider") actions_layout.addWidget(divider) actions_layout.addLayout(join_panel) container_layout.addLayout(actions_layout) layout.addWidget(container) # Signals to enable/disable buttons self.username_input.textChanged.connect(self.check_inputs) self.room_code_input.textChanged.connect(self.check_inputs) def _set_local_file(self, file_path: str): self.local_file_path = file_path self.local_file_name = os.path.basename(file_path) self.local_file_size = os.path.getsize(file_path) size_mb = self.local_file_size / (1024 * 1024) self.create_file_info.setText(f"{self.local_file_name}\n{size_mb:.1f} MB") self.create_file_info.show() self.check_inputs() def select_file(self): file_path, _ = QFileDialog.getOpenFileName( self, "Select Video File", "", "Video Files (*.mp4 *.mkv *.avi *.mov *.webm);;All Files (*)" ) if file_path: self._set_local_file(file_path) def dragEnterEvent(self, event): if event.mimeData().hasUrls(): event.accept() else: event.ignore() def dropEvent(self, event): for url in event.mimeData().urls(): file_path = url.toLocalFile() if os.path.isfile(file_path): ext = os.path.splitext(file_path)[1].lower() if ext in ['.mp4', '.mkv', '.avi', '.mov', '.webm']: self._set_local_file(file_path) break def check_inputs(self): has_name = len(self.username_input.text().strip()) > 0 has_file = self.local_file_path is not None has_code = len(self.room_code_input.text().strip()) >= 4 self.create_room_btn.setEnabled(has_name and has_file) self.join_room_btn.setEnabled(has_name and has_code) def create_room(self): username = self.username_input.text().strip() self.create_room_btn.setText("Connecting...") self.create_room_btn.setEnabled(False) self.join_room_btn.setEnabled(False) self.create_requested.emit(username, self.local_file_path, self.local_file_name, self.local_file_size) def join_room(self): username = self.username_input.text().strip() room_code = self.room_code_input.text().strip().upper() self.join_room_btn.setText("Connecting...") self.join_room_btn.setEnabled(False) self.create_room_btn.setEnabled(False) self.join_requested.emit(username, room_code) def reset_ui(self): self.create_room_btn.setText("Create Room") self.join_room_btn.setText("Join Room") self.check_inputs() def clear_file(self): self.local_file_path = None self.local_file_name = None self.local_file_size = 0 self.create_file_info.hide() self.check_inputs()