diff --git a/languages/en.json b/languages/en.json index 0fff390..8f7684b 100644 --- a/languages/en.json +++ b/languages/en.json @@ -92,6 +92,7 @@ "sponsorblock_description": "Select which types of video segments to automatically remove during download.\nSponsorBlock uses community-submitted data to identify these segments.", "select_subtitles": "Select Subtitles", "filter_languages_placeholder": "Filter languages (e.g., en, es)...", + "filter_playlist_placeholder": "Filter videos...", "no_subtitles_available": "No subtitles available", "matching": "matching" }, @@ -336,6 +337,7 @@ "analyzing_updating_table": "Analyzing (95%)... Updating format table", "analysis_complete": "Analysis complete!", "analyzing_extracting_ytdlp": "Analyzing (30%)... Extracting info", + "analyzing_fetching_first_video": "Analyzing... Fetching formats for first video", "analyzing_processing_data": "Analyzing (60%)... Processing data", "analyzing_processing_formats_ytdlp": "Analyzing (75%)... Processing formats", "analyzing_loading_thumbnail_ytdlp": "Analyzing (85%)... Loading thumbnail", diff --git a/src/gui/ytsage_gui_dialogs/ytsage_dialogs_selection.py b/src/gui/ytsage_gui_dialogs/ytsage_dialogs_selection.py index 9d36b6d..bead261 100644 --- a/src/gui/ytsage_gui_dialogs/ytsage_dialogs_selection.py +++ b/src/gui/ytsage_gui_dialogs/ytsage_dialogs_selection.py @@ -208,6 +208,27 @@ class PlaylistSelectionDialog(QDialog): # Main layout main_layout = QVBoxLayout(self) + # Filter Input + self.filter_input = QLineEdit() + self.filter_input.setPlaceholderText(_("dialogs.filter_playlist_placeholder")) + self.filter_input.textChanged.connect(self.filter_list) + self.filter_input.setStyleSheet( + """ + QLineEdit { + background-color: #363636; + border: 2px solid #3d3d3d; + border-radius: 4px; + padding: 5px; + min-height: 30px; + color: white; + } + QLineEdit:focus { + border-color: #ff0000; + } + """ + ) + main_layout.addWidget(self.filter_input) + # Top buttons (Select/Deselect All) button_layout = QHBoxLayout() select_all_btn = QPushButton(_("buttons.select_all")) @@ -339,6 +360,13 @@ class PlaylistSelectionDialog(QDialog): pass # Ignore invalid numbers return selected_indices + def filter_list(self, text: str) -> None: + """Filter the list of checkboxes based on title.""" + text = text.lower() + for checkbox in self.checkboxes: + title = (checkbox.property("full_title") or "").lower() + checkbox.setVisible(text in title) + def _populate_list(self, previously_selected_string) -> None: """Populates the scroll area with checkboxes for each video.""" selected_indices = self._parse_selection_string(previously_selected_string) @@ -356,12 +384,29 @@ class PlaylistSelectionDialog(QDialog): video_index = index + 1 # yt-dlp uses 1-based indexing title = entry.get("title", f"Video {video_index}") - # Shorten title if too long - display_title = (title[:70] + "...") if len(title) > 73 else title + + # Format duration + duration = entry.get("duration") + duration_str = "" + if duration: + try: + m, s = divmod(int(duration), 60) + h, m = divmod(m, 60) + if h > 0: + duration_str = f" [{h}:{m:02d}:{s:02d}]" + else: + duration_str = f" [{m:02d}:{s:02d}]" + except (ValueError, TypeError): + pass + + # Shorten title if too long but keep enough space for duration + max_len = 65 + display_title = (title[:max_len] + "...") if len(title) > max_len + 3 else title - checkbox = QCheckBox(f"{video_index}. {display_title}") + checkbox = QCheckBox(f"{video_index}. {display_title}{duration_str}") checkbox.setChecked(video_index in selected_indices) checkbox.setProperty("video_index", video_index) # Store index + checkbox.setProperty("full_title", title) # Store full title for filtering checkbox.setStyleSheet( """ QCheckBox {