96dadd7e5b
Moved all files from src/gui/ to ytsage/gui/ and updated import statements to use relative imports within the new package structure. This improves modularity and prepares the codebase for distribution as a proper Python package.
731 lines
27 KiB
Python
731 lines
27 KiB
Python
"""
|
|
Selection dialogs for YTSage application.
|
|
Contains dialogs for selecting subtitles, playlist videos, and SponsorBlock categories.
|
|
"""
|
|
|
|
from PySide6.QtCore import Qt
|
|
from PySide6.QtWidgets import (
|
|
QCheckBox,
|
|
QDialog,
|
|
QDialogButtonBox,
|
|
QHBoxLayout,
|
|
QLabel,
|
|
QLineEdit,
|
|
QPushButton,
|
|
QScrollArea,
|
|
QVBoxLayout,
|
|
QWidget,
|
|
)
|
|
|
|
from ...utils.ytsage_localization import _
|
|
|
|
|
|
class SubtitleSelectionDialog(QDialog):
|
|
def __init__(self, available_manual, available_auto, previously_selected, parent=None) -> None:
|
|
super().__init__(parent)
|
|
self.setWindowTitle(_("dialogs.select_subtitles"))
|
|
self.setMinimumWidth(400)
|
|
self.setMinimumHeight(300)
|
|
|
|
self.available_manual = available_manual
|
|
self.available_auto = available_auto
|
|
self.previously_selected = set(previously_selected) # Use a set for quick lookups
|
|
self.selected_subtitles = list(previously_selected) # Initialize with previous selection
|
|
|
|
layout = QVBoxLayout(self)
|
|
layout.setSpacing(10)
|
|
|
|
# Filter input
|
|
self.filter_input = QLineEdit()
|
|
self.filter_input.setPlaceholderText(_("dialogs.filter_languages_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;
|
|
}
|
|
"""
|
|
)
|
|
layout.addWidget(self.filter_input)
|
|
|
|
# Scroll Area for the list
|
|
scroll_area = QScrollArea()
|
|
scroll_area.setWidgetResizable(True)
|
|
scroll_area.setStyleSheet("QScrollArea { border: none; }") # Remove border around scroll area
|
|
layout.addWidget(scroll_area)
|
|
|
|
# Container widget for list items (needed for scroll area)
|
|
self.list_container = QWidget()
|
|
self.list_layout = QVBoxLayout(self.list_container)
|
|
self.list_layout.setContentsMargins(0, 0, 0, 0)
|
|
self.list_layout.setSpacing(2) # Compact spacing
|
|
self.list_layout.setAlignment(Qt.AlignmentFlag.AlignTop) # Align items to top
|
|
scroll_area.setWidget(self.list_container)
|
|
|
|
# Populate the list initially
|
|
self.populate_list()
|
|
|
|
# OK and Cancel buttons
|
|
button_box = QDialogButtonBox()
|
|
ok_button = button_box.addButton(_("buttons.ok"), QDialogButtonBox.ButtonRole.AcceptRole)
|
|
cancel_button = button_box.addButton(_("buttons.cancel"), QDialogButtonBox.ButtonRole.RejectRole)
|
|
button_box.accepted.connect(self.accept)
|
|
button_box.rejected.connect(self.reject)
|
|
|
|
# Style the buttons
|
|
for button in button_box.buttons():
|
|
button.setStyleSheet(
|
|
"""
|
|
QPushButton {
|
|
background-color: #363636;
|
|
border: 2px solid #3d3d3d;
|
|
border-radius: 4px;
|
|
padding: 5px 15px; /* Adjust padding */
|
|
min-height: 30px; /* Ensure consistent height */
|
|
color: white;
|
|
}
|
|
QPushButton:hover {
|
|
background-color: #444444;
|
|
}
|
|
QPushButton:pressed {
|
|
background-color: #555555;
|
|
}
|
|
"""
|
|
)
|
|
# Style the OK button specifically if needed
|
|
if button_box.buttonRole(button) == QDialogButtonBox.ButtonRole.AcceptRole:
|
|
button.setStyleSheet(
|
|
button.styleSheet()
|
|
+ "QPushButton { background-color: #ff0000; border-color: #cc0000; } QPushButton:hover { background-color: #cc0000; }"
|
|
)
|
|
|
|
layout.addWidget(button_box)
|
|
|
|
def populate_list(self, filter_text="") -> None:
|
|
# Clear existing checkboxes from layout
|
|
while self.list_layout.count():
|
|
item = self.list_layout.takeAt(0)
|
|
widget = item.widget()
|
|
if widget is not None:
|
|
widget.deleteLater()
|
|
|
|
filter_text = filter_text.lower()
|
|
combined_subs = {}
|
|
|
|
# Add manual subs
|
|
for lang_code, sub_info in self.available_manual.items():
|
|
if not filter_text or filter_text in lang_code.lower():
|
|
combined_subs[lang_code] = f"{lang_code} - Manual"
|
|
|
|
# Add auto subs (only if no manual exists and matches filter)
|
|
for lang_code, sub_info in self.available_auto.items():
|
|
if lang_code not in combined_subs: # Don't overwrite manual
|
|
if not filter_text or filter_text in lang_code.lower():
|
|
combined_subs[lang_code] = f"{lang_code} - Auto-generated"
|
|
|
|
if not combined_subs:
|
|
matching_text = _("dialogs.matching") if filter_text else ""
|
|
no_subs_label = QLabel(_("dialogs.no_subtitles_available") + (f" {matching_text} '{filter_text}'" if filter_text else ""))
|
|
no_subs_label.setStyleSheet("color: #aaaaaa; padding: 10px;")
|
|
self.list_layout.addWidget(no_subs_label)
|
|
return
|
|
|
|
# Sort by language code
|
|
sorted_lang_codes = sorted(combined_subs.keys())
|
|
|
|
for lang_code in sorted_lang_codes:
|
|
item_text = combined_subs[lang_code]
|
|
checkbox = QCheckBox(item_text)
|
|
checkbox.setProperty("subtitle_id", item_text) # Store the identifier
|
|
checkbox.setChecked(item_text in self.previously_selected) # Check if previously selected
|
|
checkbox.stateChanged.connect(self.update_selection)
|
|
checkbox.setStyleSheet(
|
|
"""
|
|
QCheckBox {
|
|
color: #ffffff;
|
|
padding: 5px;
|
|
}
|
|
QCheckBox::indicator {
|
|
width: 18px;
|
|
height: 18px;
|
|
border-radius: 4px; /* Square checkboxes */
|
|
}
|
|
QCheckBox::indicator:unchecked {
|
|
border: 2px solid #666666;
|
|
background: #2b2b2b;
|
|
}
|
|
QCheckBox::indicator:checked {
|
|
border: 2px solid #ff0000;
|
|
background: #ff0000;
|
|
}
|
|
"""
|
|
)
|
|
self.list_layout.addWidget(checkbox)
|
|
|
|
self.list_layout.addStretch() # Pushes items up if list is short
|
|
|
|
def filter_list(self) -> None:
|
|
self.populate_list(self.filter_input.text())
|
|
|
|
def update_selection(self, state) -> None:
|
|
sender = self.sender()
|
|
subtitle_id = sender.property("subtitle_id")
|
|
if state == Qt.CheckState.Checked.value:
|
|
if subtitle_id not in self.previously_selected:
|
|
self.previously_selected.add(subtitle_id)
|
|
else:
|
|
if subtitle_id in self.previously_selected:
|
|
self.previously_selected.remove(subtitle_id)
|
|
|
|
def get_selected_subtitles(self) -> list:
|
|
# Return the final set as a list
|
|
return list(self.previously_selected)
|
|
|
|
def accept(self) -> None:
|
|
# Update the final list before closing
|
|
self.selected_subtitles = self.get_selected_subtitles()
|
|
super().accept()
|
|
|
|
|
|
class PlaylistSelectionDialog(QDialog):
|
|
def __init__(self, playlist_entries, previously_selected_string, parent=None) -> None:
|
|
super().__init__(parent)
|
|
self.setWindowTitle(_("playlist.select_videos_title"))
|
|
self.setMinimumWidth(500)
|
|
self.setMinimumHeight(400) # Allow more vertical space
|
|
|
|
self.playlist_entries = playlist_entries
|
|
self.checkboxes = []
|
|
|
|
# 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"))
|
|
deselect_all_btn = QPushButton(_("buttons.deselect_all"))
|
|
select_all_btn.clicked.connect(self._select_all)
|
|
deselect_all_btn.clicked.connect(self._deselect_all)
|
|
# Style the buttons to match the subtitle dialog
|
|
select_all_btn.setStyleSheet(
|
|
"""
|
|
QPushButton {
|
|
background-color: #363636;
|
|
border: 2px solid #3d3d3d;
|
|
border-radius: 4px;
|
|
padding: 5px 15px;
|
|
min-height: 30px;
|
|
color: white;
|
|
}
|
|
QPushButton:hover {
|
|
background-color: #444444;
|
|
}
|
|
QPushButton:pressed {
|
|
background-color: #555555;
|
|
}
|
|
"""
|
|
)
|
|
deselect_all_btn.setStyleSheet(select_all_btn.styleSheet())
|
|
button_layout.addWidget(select_all_btn)
|
|
button_layout.addWidget(deselect_all_btn)
|
|
button_layout.addStretch()
|
|
main_layout.addLayout(button_layout)
|
|
|
|
# Scrollable area for checkboxes
|
|
scroll_area = QScrollArea()
|
|
scroll_area.setWidgetResizable(True)
|
|
scroll_area.setStyleSheet("QScrollArea { border: none; }") # Remove border around scroll area
|
|
scroll_widget = QWidget()
|
|
self.list_layout = QVBoxLayout(scroll_widget) # Layout for checkboxes
|
|
self.list_layout.setContentsMargins(0, 0, 0, 0)
|
|
self.list_layout.setSpacing(2) # Compact spacing
|
|
self.list_layout.setAlignment(Qt.AlignmentFlag.AlignTop) # Align items to top
|
|
scroll_area.setWidget(scroll_widget)
|
|
main_layout.addWidget(scroll_area)
|
|
|
|
# Populate checkboxes
|
|
self._populate_list(previously_selected_string)
|
|
|
|
# Dialog buttons (OK/Cancel)
|
|
button_box = QDialogButtonBox()
|
|
ok_button = button_box.addButton(_("buttons.ok"), QDialogButtonBox.ButtonRole.AcceptRole)
|
|
cancel_button = button_box.addButton(_("buttons.cancel"), QDialogButtonBox.ButtonRole.RejectRole)
|
|
button_box.accepted.connect(self.accept)
|
|
button_box.rejected.connect(self.reject)
|
|
|
|
# Style the buttons to match subtitle dialog
|
|
for button in button_box.buttons():
|
|
button.setStyleSheet(
|
|
"""
|
|
QPushButton {
|
|
background-color: #363636;
|
|
border: 2px solid #3d3d3d;
|
|
border-radius: 4px;
|
|
padding: 5px 15px;
|
|
min-height: 30px;
|
|
color: white;
|
|
}
|
|
QPushButton:hover {
|
|
background-color: #444444;
|
|
}
|
|
QPushButton:pressed {
|
|
background-color: #555555;
|
|
}
|
|
"""
|
|
)
|
|
# Style the OK button specifically if needed
|
|
if button_box.buttonRole(button) == QDialogButtonBox.ButtonRole.AcceptRole:
|
|
button.setStyleSheet(
|
|
button.styleSheet()
|
|
+ "QPushButton { background-color: #ff0000; border-color: #cc0000; } QPushButton:hover { background-color: #cc0000; }"
|
|
)
|
|
|
|
main_layout.addWidget(button_box)
|
|
|
|
# Apply styling to match subtitle dialog
|
|
self.setStyleSheet(
|
|
"""
|
|
QDialog { background-color: #15181b; }
|
|
QCheckBox {
|
|
color: #ffffff;
|
|
padding: 5px;
|
|
}
|
|
QCheckBox::indicator {
|
|
width: 18px;
|
|
height: 18px;
|
|
border-radius: 4px;
|
|
}
|
|
QCheckBox::indicator:unchecked {
|
|
border: 2px solid #666666;
|
|
background: #2b2b2b;
|
|
}
|
|
QCheckBox::indicator:checked {
|
|
border: 2px solid #ff0000;
|
|
background: #ff0000;
|
|
}
|
|
QWidget { background-color: #15181b; }
|
|
"""
|
|
)
|
|
|
|
def _parse_selection_string(self, selection_string) -> set:
|
|
"""Parses a yt-dlp playlist selection string (e.g., '1-3,5,7-9') into a set of 1-based indices."""
|
|
selected_indices = set()
|
|
if not selection_string:
|
|
# If no previous selection, assume all are selected initially
|
|
return set(range(1, len(self.playlist_entries) + 1))
|
|
|
|
parts = selection_string.split(",")
|
|
for part in parts:
|
|
part = part.strip()
|
|
if "-" in part:
|
|
try:
|
|
start, end = map(int, part.split("-"))
|
|
if start <= end:
|
|
selected_indices.update(range(start, end + 1))
|
|
except ValueError:
|
|
pass # Ignore invalid ranges
|
|
else:
|
|
try:
|
|
selected_indices.add(int(part))
|
|
except ValueError:
|
|
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)
|
|
|
|
# Clear existing checkboxes if any (e.g., if repopulating)
|
|
while self.list_layout.count():
|
|
child = self.list_layout.takeAt(0)
|
|
if child.widget():
|
|
child.widget().deleteLater()
|
|
self.checkboxes.clear()
|
|
|
|
for index, entry in enumerate(self.playlist_entries):
|
|
if not entry:
|
|
continue # Skip None entries if yt-dlp returns them
|
|
|
|
video_index = index + 1 # yt-dlp uses 1-based indexing
|
|
title = entry.get("title", f"Video {video_index}")
|
|
|
|
# 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}{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 {
|
|
color: #ffffff;
|
|
padding: 5px;
|
|
}
|
|
QCheckBox::indicator {
|
|
width: 18px;
|
|
height: 18px;
|
|
border-radius: 4px;
|
|
}
|
|
QCheckBox::indicator:unchecked {
|
|
border: 2px solid #666666;
|
|
background: #2b2b2b;
|
|
}
|
|
QCheckBox::indicator:checked {
|
|
border: 2px solid #ff0000;
|
|
background: #ff0000;
|
|
}
|
|
"""
|
|
)
|
|
self.list_layout.addWidget(checkbox)
|
|
self.checkboxes.append(checkbox)
|
|
self.list_layout.addStretch() # Push checkboxes to the top
|
|
|
|
def _select_all(self) -> None:
|
|
for checkbox in self.checkboxes:
|
|
checkbox.setChecked(True)
|
|
|
|
def _deselect_all(self) -> None:
|
|
for checkbox in self.checkboxes:
|
|
checkbox.setChecked(False)
|
|
|
|
def _condense_indices(self, indices: list[int]) -> str:
|
|
"""Condenses a list of 1-based indices into a yt-dlp selection string."""
|
|
if not indices:
|
|
return ""
|
|
|
|
# Remove duplicates and sort in one step
|
|
indices = sorted(set(indices))
|
|
|
|
ranges = []
|
|
start = end = indices[0]
|
|
|
|
for num in indices[1:]:
|
|
if num == end + 1:
|
|
end = num
|
|
else:
|
|
ranges.append(f"{start}-{end}" if start != end else str(start))
|
|
start = end = num
|
|
|
|
# Append the last range
|
|
ranges.append(f"{start}-{end}" if start != end else str(start))
|
|
|
|
return ",".join(ranges)
|
|
|
|
def get_selected_items_string(self) -> str | None:
|
|
"""Returns the selection string based on checked boxes."""
|
|
selected_indices = [cb.property("video_index") for cb in self.checkboxes if cb.isChecked()]
|
|
|
|
# Check if all items are selected
|
|
if len(selected_indices) == len(self.playlist_entries):
|
|
return None # yt-dlp default is all items, so return None or empty string
|
|
|
|
return self._condense_indices(selected_indices)
|
|
|
|
|
|
class SponsorBlockCategoryDialog(QDialog):
|
|
"""Dialog for selecting SponsorBlock categories to remove from videos."""
|
|
|
|
# Default SponsorBlock categories with descriptions
|
|
SPONSORBLOCK_CATEGORIES = {
|
|
"sponsor": {
|
|
"name_key": "sponsorblock.sponsor",
|
|
"description_key": "sponsorblock.sponsor_desc",
|
|
"default": True,
|
|
},
|
|
"selfpromo": {
|
|
"name_key": "sponsorblock.selfpromo",
|
|
"description_key": "sponsorblock.selfpromo_desc",
|
|
"default": True,
|
|
},
|
|
"interaction": {
|
|
"name_key": "sponsorblock.interaction",
|
|
"description_key": "sponsorblock.interaction_desc",
|
|
"default": True,
|
|
},
|
|
"intro": {
|
|
"name_key": "sponsorblock.intro",
|
|
"description_key": "sponsorblock.intro_desc",
|
|
"default": False,
|
|
},
|
|
"outro": {
|
|
"name_key": "sponsorblock.outro",
|
|
"description_key": "sponsorblock.outro_desc",
|
|
"default": False,
|
|
},
|
|
"preview": {
|
|
"name_key": "sponsorblock.preview",
|
|
"description_key": "sponsorblock.preview_desc",
|
|
"default": False,
|
|
},
|
|
"music_offtopic": {
|
|
"name_key": "sponsorblock.music_offtopic",
|
|
"description_key": "sponsorblock.music_offtopic_desc",
|
|
"default": False,
|
|
},
|
|
"filler": {
|
|
"name_key": "sponsorblock.filler",
|
|
"description_key": "sponsorblock.filler_desc",
|
|
"default": False,
|
|
},
|
|
}
|
|
|
|
def __init__(self, previously_selected=None, parent=None) -> None:
|
|
super().__init__(parent)
|
|
self.setWindowTitle(_("dialogs.sponsorblock_categories"))
|
|
self.setMinimumWidth(500)
|
|
self.setMinimumHeight(400)
|
|
|
|
# Set the window icon to match the main app
|
|
if parent:
|
|
self.setWindowIcon(parent.windowIcon())
|
|
|
|
self.previously_selected = set(previously_selected) if previously_selected else set()
|
|
self.checkboxes = {}
|
|
|
|
self.init_ui()
|
|
self.apply_styling()
|
|
|
|
def init_ui(self) -> None:
|
|
layout = QVBoxLayout(self)
|
|
|
|
# Title and description
|
|
title_label = QLabel(_("dialogs.sponsorblock_categories"))
|
|
title_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
|
title_label.setStyleSheet("font-size: 16px; font-weight: bold; color: #ffffff; margin: 10px;")
|
|
layout.addWidget(title_label)
|
|
|
|
desc_label = QLabel(_("dialogs.sponsorblock_description"))
|
|
desc_label.setWordWrap(True)
|
|
desc_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
|
desc_label.setStyleSheet("color: #cccccc; margin: 10px; font-size: 11px;")
|
|
layout.addWidget(desc_label)
|
|
|
|
# Scroll area for categories
|
|
scroll_area = QScrollArea()
|
|
scroll_area.setWidgetResizable(True)
|
|
scroll_area.setStyleSheet("QScrollArea { border: none; }")
|
|
|
|
scroll_widget = QWidget()
|
|
scroll_layout = QVBoxLayout(scroll_widget)
|
|
scroll_layout.setContentsMargins(10, 0, 10, 0)
|
|
scroll_layout.setSpacing(8)
|
|
|
|
# Add category checkboxes
|
|
for category_id, category_info in self.SPONSORBLOCK_CATEGORIES.items():
|
|
# Create a container widget for each category
|
|
category_widget = QWidget()
|
|
category_layout = QVBoxLayout(category_widget)
|
|
category_layout.setContentsMargins(0, 0, 0, 0)
|
|
category_layout.setSpacing(2)
|
|
|
|
# Create checkbox with localized name
|
|
checkbox = QCheckBox(_(category_info["name_key"]))
|
|
checkbox.setProperty("category_id", category_id)
|
|
|
|
# Determine if this category should be checked
|
|
if self.previously_selected:
|
|
# Use previously selected categories
|
|
is_checked = category_id in self.previously_selected
|
|
else:
|
|
# Use default values for first time
|
|
is_checked = category_info["default"]
|
|
|
|
checkbox.setChecked(is_checked)
|
|
|
|
checkbox.setStyleSheet(
|
|
"""
|
|
QCheckBox {
|
|
color: #ffffff;
|
|
padding: 4px;
|
|
spacing: 10px;
|
|
font-weight: bold;
|
|
}
|
|
QCheckBox::indicator {
|
|
width: 18px;
|
|
height: 18px;
|
|
border-radius: 4px;
|
|
}
|
|
QCheckBox::indicator:unchecked {
|
|
border: 2px solid #666666;
|
|
background: #2b2b2b;
|
|
}
|
|
QCheckBox::indicator:checked {
|
|
border: 2px solid #ff0000;
|
|
background: #ff0000;
|
|
}
|
|
"""
|
|
)
|
|
|
|
# Create description label with localized text
|
|
desc_label = QLabel(_(category_info["description_key"]))
|
|
desc_label.setStyleSheet("color: #aaaaaa; font-size: 11px; margin-left: 28px; margin-bottom: 8px;")
|
|
desc_label.setWordWrap(True)
|
|
|
|
category_layout.addWidget(checkbox)
|
|
category_layout.addWidget(desc_label)
|
|
|
|
self.checkboxes[category_id] = checkbox
|
|
scroll_layout.addWidget(category_widget)
|
|
|
|
scroll_layout.addStretch()
|
|
scroll_area.setWidget(scroll_widget)
|
|
layout.addWidget(scroll_area)
|
|
|
|
# Quick selection buttons
|
|
button_layout = QHBoxLayout()
|
|
|
|
select_defaults_btn = QPushButton(_("buttons.select_defaults"))
|
|
select_defaults_btn.clicked.connect(self.select_defaults)
|
|
select_defaults_btn.setStyleSheet(self._get_button_style())
|
|
|
|
select_all_btn = QPushButton(_("buttons.select_all"))
|
|
select_all_btn.clicked.connect(self.select_all)
|
|
select_all_btn.setStyleSheet(self._get_button_style())
|
|
|
|
deselect_all_btn = QPushButton(_("buttons.deselect_all"))
|
|
deselect_all_btn.clicked.connect(self.deselect_all)
|
|
deselect_all_btn.setStyleSheet(self._get_button_style())
|
|
|
|
button_layout.addWidget(select_defaults_btn)
|
|
button_layout.addWidget(select_all_btn)
|
|
button_layout.addWidget(deselect_all_btn)
|
|
button_layout.addStretch()
|
|
|
|
layout.addLayout(button_layout)
|
|
|
|
# Dialog buttons
|
|
button_box = QDialogButtonBox()
|
|
ok_button = button_box.addButton(_("buttons.ok"), QDialogButtonBox.ButtonRole.AcceptRole)
|
|
cancel_button = button_box.addButton(_("buttons.cancel"), QDialogButtonBox.ButtonRole.RejectRole)
|
|
button_box.accepted.connect(self.accept)
|
|
button_box.rejected.connect(self.reject)
|
|
|
|
# Style the dialog buttons
|
|
for button in button_box.buttons():
|
|
button.setStyleSheet(self._get_button_style())
|
|
if button_box.buttonRole(button) == QDialogButtonBox.ButtonRole.AcceptRole:
|
|
button.setStyleSheet(
|
|
button.styleSheet()
|
|
+ "QPushButton { background-color: #ff0000; border-color: #cc0000; } "
|
|
+ "QPushButton:hover { background-color: #cc0000; }"
|
|
)
|
|
|
|
layout.addWidget(button_box)
|
|
|
|
def _get_button_style(self) -> str:
|
|
"""Returns the standard button style for this dialog."""
|
|
return """
|
|
QPushButton {
|
|
background-color: #363636;
|
|
border: 2px solid #3d3d3d;
|
|
border-radius: 4px;
|
|
padding: 5px 15px;
|
|
min-height: 30px;
|
|
color: white;
|
|
}
|
|
QPushButton:hover {
|
|
background-color: #444444;
|
|
}
|
|
QPushButton:pressed {
|
|
background-color: #555555;
|
|
}
|
|
"""
|
|
|
|
def apply_styling(self) -> None:
|
|
"""Apply the dialog styling to match the rest of the application."""
|
|
self.setStyleSheet(
|
|
"""
|
|
QDialog {
|
|
background-color: #15181b;
|
|
color: #ffffff;
|
|
}
|
|
QLabel {
|
|
color: #ffffff;
|
|
}
|
|
QWidget {
|
|
background-color: #15181b;
|
|
}
|
|
"""
|
|
)
|
|
|
|
def select_defaults(self) -> None:
|
|
"""Select only the default categories."""
|
|
for category_id, checkbox in self.checkboxes.items():
|
|
default_value = self.SPONSORBLOCK_CATEGORIES[category_id]["default"]
|
|
checkbox.setChecked(default_value)
|
|
|
|
def select_all(self) -> None:
|
|
"""Select all categories."""
|
|
for checkbox in self.checkboxes.values():
|
|
checkbox.setChecked(True)
|
|
|
|
def deselect_all(self) -> None:
|
|
"""Deselect all categories."""
|
|
for checkbox in self.checkboxes.values():
|
|
checkbox.setChecked(False)
|
|
|
|
def get_selected_categories(self) -> list:
|
|
"""Returns a list of selected category IDs."""
|
|
selected = []
|
|
for category_id, checkbox in self.checkboxes.items():
|
|
if checkbox.isChecked():
|
|
selected.append(category_id)
|
|
return selected
|
|
|
|
def get_selected_categories_string(self) -> str:
|
|
"""Returns a comma-separated string of selected categories for yt-dlp."""
|
|
selected = self.get_selected_categories()
|
|
return ",".join(selected) if selected else ""
|