""" Watch tab - player plus play queue ================================== Hosts the embedded mpv PlayerPanel (or the libmpv-missing hint) and a simple play queue. Entries arrive via AppRouter.playVideo / queueVideo. """ from typing import Any, Dict, List, Optional from PySide6.QtCore import Qt from PySide6.QtWidgets import ( QHBoxLayout, QLabel, QListWidget, QListWidgetItem, QPushButton, QSplitter, QVBoxLayout, QWidget, ) from .ytsage_gui_player import PlayerPanel, create_player_panel from ..utils.ytsage_localization import _ from ..utils.ytsage_logger import logger class WatchPage(QWidget): def __init__(self, router, parent: Optional[QWidget] = None) -> None: super().__init__(parent) self._router = router self._queue: List[Dict[str, Any]] = [] layout = QVBoxLayout(self) layout.setContentsMargins(8, 8, 8, 8) splitter = QSplitter(Qt.Orientation.Horizontal, self) layout.addWidget(splitter) self.player = create_player_panel(self) splitter.addWidget(self.player) queue_panel = QWidget(self) queue_layout = QVBoxLayout(queue_panel) queue_layout.setContentsMargins(4, 0, 0, 0) queue_header = QHBoxLayout() queue_label = QLabel(_("player.queue")) queue_label.setStyleSheet("font-weight: bold;") queue_header.addWidget(queue_label) queue_header.addStretch() self.clear_queue_btn = QPushButton("✕") self.clear_queue_btn.setFixedWidth(28) self.clear_queue_btn.setToolTip(_("watch.clear_queue")) self.clear_queue_btn.clicked.connect(self.clear_queue) queue_header.addWidget(self.clear_queue_btn) queue_layout.addLayout(queue_header) self.queue_list = QListWidget() self.queue_list.setDragDropMode(QListWidget.DragDropMode.InternalMove) self.queue_list.itemDoubleClicked.connect(self._on_queue_item_activated) self.queue_list.model().rowsMoved.connect(self._on_rows_moved) queue_layout.addWidget(self.queue_list) splitter.addWidget(queue_panel) splitter.setStretchFactor(0, 4) splitter.setStretchFactor(1, 1) splitter.setSizes([900, 240]) if isinstance(self.player, PlayerPanel): self.player.playbackEnded.connect(self._on_playback_ended) # ------------------------------------------------------------ public API def play_entry(self, entry: Dict[str, Any], resume_pos: float = 0.0) -> None: if isinstance(self.player, PlayerPanel): self.player.play(entry, resume_pos=resume_pos) else: logger.warning("Play requested but libmpv is unavailable") def enqueue(self, entry: Dict[str, Any]) -> None: self._queue.append(dict(entry)) item = QListWidgetItem(entry.get("title") or entry.get("url") or "?") item.setData(Qt.ItemDataRole.UserRole, dict(entry)) self.queue_list.addItem(item) # Start playing right away when nothing is on and this is the first item if isinstance(self.player, PlayerPanel) and not self.player.current_entry() and len(self._queue) == 1: self._play_next_from_queue() def clear_queue(self) -> None: self._queue.clear() self.queue_list.clear() def queue_entries(self) -> List[Dict[str, Any]]: return [self.queue_list.item(i).data(Qt.ItemDataRole.UserRole) for i in range(self.queue_list.count())] # --------------------------------------------------------------- internal def _play_next_from_queue(self) -> None: if self.queue_list.count() == 0: return item = self.queue_list.takeItem(0) entry = item.data(Qt.ItemDataRole.UserRole) if entry in self._queue: self._queue.remove(entry) self.play_entry(entry) def _on_playback_ended(self, reason: str) -> None: if reason in ("eof", "") and self.queue_list.count() > 0: self._play_next_from_queue() def _on_queue_item_activated(self, item: QListWidgetItem) -> None: entry = item.data(Qt.ItemDataRole.UserRole) row = self.queue_list.row(item) self.queue_list.takeItem(row) if entry in self._queue: self._queue.remove(entry) self.play_entry(entry) def _on_rows_moved(self, *args) -> None: self._queue = self.queue_entries() def shutdown(self) -> None: if isinstance(self.player, PlayerPanel): self.player.shutdown()