d80dff371c
Replaced hardcoded status and fallback messages with localized strings across downloader, update, and main UI modules. Updated language files for all supported languages to include new keys for fallback download status, pip timeout, invalid URL, zero selected, and playlist selection dialog title. This improves internationalization and consistency of user-facing messages.
686 lines
25 KiB
Python
686 lines
25 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 src.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)
|
|
|
|
# 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 _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}")
|
|
# Shorten title if too long
|
|
display_title = (title[:70] + "...") if len(title) > 73 else title
|
|
|
|
checkbox = QCheckBox(f"{video_index}. {display_title}")
|
|
checkbox.setChecked(video_index in selected_indices)
|
|
checkbox.setProperty("video_index", video_index) # Store index
|
|
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 ""
|