""" Search tab - in-app YouTube search via yt-dlp (ytsearchN:) """ from typing import Any, Dict, List, Optional from PySide6.QtWidgets import QHBoxLayout, QLabel, QLineEdit, QPushButton, QVBoxLayout, QWidget from .ytsage_gui_cards import VideoCardGrid from ..core.ytsage_client import YtdlpWorker from ..utils.ytsage_localization import _ from ..utils.ytsage_logger import logger PAGE_SIZE = 24 class SearchPage(QWidget): def __init__(self, router, parent: Optional[QWidget] = None) -> None: super().__init__(parent) self._router = router self._worker: Optional[YtdlpWorker] = None self._query = "" self._offset = 0 layout = QVBoxLayout(self) layout.setContentsMargins(8, 8, 8, 8) bar = QHBoxLayout() self.query_input = QLineEdit() self.query_input.setPlaceholderText(_("search.placeholder")) self.query_input.returnPressed.connect(self.start_search) self.query_input.setMinimumHeight(38) bar.addWidget(self.query_input, stretch=1) self.search_btn = QPushButton(_("search.button")) self.search_btn.setToolTip(_("search.button_tooltip")) self.search_btn.clicked.connect(self.start_search) self.search_btn.setMinimumHeight(38) bar.addWidget(self.search_btn) layout.addLayout(bar) self.status_label = QLabel("") self.status_label.setStyleSheet("color: #9aa0a6; padding: 2px;") layout.addWidget(self.status_label) self.grid = VideoCardGrid(router, self) self.grid.loadMoreRequested.connect(self.load_more) layout.addWidget(self.grid, stretch=1) # ---------------------------------------------------------------- search def start_search(self) -> None: query = self.query_input.text().strip() if not query or self._worker is not None: return self._query = query self._offset = 0 self.grid.clear() self._fetch(append=False) def load_more(self) -> None: if self._worker is None and self._query: self._offset += PAGE_SIZE self._fetch(append=True) def _fetch(self, append: bool) -> None: self.status_label.setText(_("search.searching")) self.search_btn.setEnabled(False) query, offset = self._query, self._offset self._worker = YtdlpWorker(lambda c: c.search(query, n=PAGE_SIZE, offset=offset), parent=self) self._worker.result.connect(lambda entries: self._on_results(entries, append)) self._worker.error.connect(self._on_error) self._worker.finished.connect(self._on_finished) self._worker.start() def _on_results(self, entries: List[Dict[str, Any]], append: bool) -> None: show_more = len(entries) >= PAGE_SIZE if append: self.grid.append_entries(entries, show_load_more=show_more) else: self.grid.set_entries(entries, show_load_more=show_more) self.status_label.setText(_("search.results_count", count=self.grid.card_count())) def _on_error(self, message: str) -> None: logger.error(f"Search failed: {message}") self.status_label.setText(_("search.failed", error=message[:200])) def _on_finished(self) -> None: self.search_btn.setEnabled(True) if self._worker is not None: self._worker.deleteLater() self._worker = None