diff --git a/abogen/constants.py b/abogen/constants.py index fca17a0..955bc4b 100644 --- a/abogen/constants.py +++ b/abogen/constants.py @@ -106,9 +106,18 @@ SAMPLE_VOICE_TEXTS = { COLORS = { "BLUE": "#007dff", "RED": "#c0392b", + "GREEN": "#42ad4a", + "GREEN_BG": "rgba(66, 173, 73, 0.1)", + "GREEN_BG_HOVER": "rgba(66, 173, 73, 0.15)", + "GREEN_BORDER": "#42ad4a", + "BLUE_BG": "rgba(0, 102, 255, 0.05)", + "BLUE_BG_HOVER": "rgba(0, 102, 255, 0.1)", + "BLUE_BORDER_HOVER": "#6ab0de", "YELLOW_BACKGROUND": "rgba(255, 221, 51, 0.40)", "GREY_BACKGROUND": "rgba(128, 128, 128, 0.15)", "RED_BACKGROUND": "rgba(232, 78, 60, 0.15)", + "RED_BG": "rgba(232, 78, 60, 0.10)", + "RED_BG_HOVER": "rgba(232, 78, 60, 0.15)", # Theme palette colors "DARK_BG": "#202326", "DARK_BASE": "#141618", diff --git a/abogen/conversion.py b/abogen/conversion.py index 1e6b262..0db8a83 100644 --- a/abogen/conversion.py +++ b/abogen/conversion.py @@ -324,7 +324,7 @@ class ConversionThread(QThread): if hasattr(self, "file_size_str"): self.log_updated.emit(f"- File size: {self.file_size_str}") - self.log_updated.emit(f"- Total characters: {self.total_char_count:,}") + self.log_updated.emit(f"- Total characters: {int(self.total_char_count):,}") self.log_updated.emit( f"- Language: {self.lang_code} ({LANGUAGE_DESCRIPTIONS.get(self.lang_code, 'Unknown')})" diff --git a/abogen/gui.py b/abogen/gui.py index 2a57274..5b620b4 100644 --- a/abogen/gui.py +++ b/abogen/gui.py @@ -124,14 +124,14 @@ class IconProvider(QFileIconProvider): class InputBox(QLabel): # Define CSS styles as class constants - STYLE_DEFAULT = "border:2px dashed #aaa; border-radius:5px; padding:20px; background:rgba(0, 102, 255, 0.05); min-height:100px;" - STYLE_DEFAULT_HOVER = "background:rgba(0, 102, 255, 0.1); border-color:#6ab0de;" + STYLE_DEFAULT = f"border:2px dashed #aaa; border-radius:5px; padding:20px; background:{COLORS['BLUE_BG']}; min-height:100px;" + STYLE_DEFAULT_HOVER = f"background:{COLORS['BLUE_BG_HOVER']}; border-color:{COLORS['BLUE_BORDER_HOVER']};" - STYLE_ACTIVE = "border:2px dashed #42ad4a; border-radius:5px; padding:20px; background:rgba(66, 173, 73, 0.1); min-height:100px;" - STYLE_ACTIVE_HOVER = "background:rgba(66, 173, 73, 0.15); border-color:#42ad4a;" + STYLE_ACTIVE = f"border:2px dashed {COLORS['GREEN']}; border-radius:5px; padding:20px; background:{COLORS['GREEN_BG']}; min-height:100px;" + STYLE_ACTIVE_HOVER = f"background:{COLORS['GREEN_BG_HOVER']}; border-color:{COLORS['GREEN_BORDER']};" - STYLE_ERROR = "border:2px dashed #e74c3c; border-radius:5px; padding:20px; background:rgba(232, 78, 60, 0.10); min-height:100px; color:#c0392b;" - STYLE_ERROR_HOVER = "background:rgba(232, 78, 60, 0.15); border-color:#e74c3c;" + STYLE_ERROR = f"border:2px dashed {COLORS['RED']}; border-radius:5px; padding:20px; background:{COLORS['RED_BG']}; min-height:100px; color:{COLORS['RED']};" + STYLE_ERROR_HOVER = f"background:{COLORS['RED_BG_HOVER']}; border-color:{COLORS['RED']};" def __init__(self, parent=None): super().__init__(parent) @@ -206,7 +206,7 @@ class InputBox(QLabel): size_str = self._human_readable_size(os.path.getsize(file_path)) name = os.path.basename(file_path) - char_count = "N/A" + char_count = 0 def parse_size(size_str): # Use regex to extract the numeric part @@ -653,6 +653,9 @@ class abogen(QWidget): self.selected_lang = entry[0][0] if entry and entry[0] else None if self.save_option == "Choose output folder" and self.selected_output_folder: self.save_path_label.setText(self.selected_output_folder) + self.save_path_label.setStyleSheet( + f"QLabel {{ color: {COLORS['GREEN']}; }}" + ) self.save_path_label.show() self.subtitle_combo.setCurrentText(self.subtitle_mode) # Enable/disable subtitle options based on selected language (profile or voice) @@ -689,6 +692,26 @@ class abogen(QWidget): container_layout.setSpacing(15) self.input_box = InputBox(self) container_layout.addWidget(self.input_box, 1) + # Manage queue button, start queue button + self.queue_row_widget = QWidget(self) # Make queue_row a QWidget + queue_row = QHBoxLayout(self.queue_row_widget) + queue_row.setContentsMargins(0, 0, 0, 0) + self.btn_add_to_queue = QPushButton("Add to Queue", self) + self.btn_add_to_queue.setFixedHeight(40) + self.btn_add_to_queue.setEnabled(False) + self.btn_add_to_queue.clicked.connect(self.add_to_queue) + queue_row.addWidget(self.btn_add_to_queue) + self.btn_manage_queue = QPushButton("Manage Queue", self) + self.btn_manage_queue.setFixedHeight(40) + self.btn_manage_queue.setEnabled(True) + self.btn_manage_queue.clicked.connect(self.manage_queue) + queue_row.addWidget(self.btn_manage_queue) + self.btn_start_queue = QPushButton("Clear Queue", self) + self.btn_start_queue.setFixedHeight(40) + self.btn_start_queue.setEnabled(False) + self.btn_start_queue.clicked.connect(self.clear_queue) + queue_row.addWidget(self.btn_start_queue) + container_layout.addWidget(self.queue_row_widget) self.log_text = QTextEdit(self) self.log_text.setReadOnly(True) self.log_text.setFrameStyle(QTextEdit.NoFrame) @@ -877,29 +900,6 @@ class abogen(QWidget): controls_layout.addLayout(gpu_layout) - - # Manage queue button, start queue button - queue_row = QHBoxLayout() - # Add to queue button - self.btn_add_to_queue = QPushButton("Add to Queue", self) - self.btn_add_to_queue.setFixedHeight(40) - self.btn_add_to_queue.setEnabled(False) - self.btn_add_to_queue.clicked.connect(self.add_to_queue) - queue_row.addWidget(self.btn_add_to_queue) - # Manage queue button - self.btn_manage_queue = QPushButton("Manage Queue", self) - self.btn_manage_queue.setFixedHeight(40) - self.btn_manage_queue.setEnabled(False) - self.btn_manage_queue.clicked.connect(self.manage_queue) - queue_row.addWidget(self.btn_manage_queue) - # Start queue button - self.btn_start_queue = QPushButton("Start Queue", self) - self.btn_start_queue.setFixedHeight(40) - self.btn_start_queue.setEnabled(False) - self.btn_start_queue.clicked.connect(self.start_queue) - queue_row.addWidget(self.btn_start_queue) - controls_layout.addLayout(queue_row) - # Start button self.btn_start = QPushButton("Start", self) self.btn_start.setFixedHeight(60) @@ -1334,11 +1334,34 @@ class abogen(QWidget): def enable_disable_queue_buttons(self): enabled = bool(self.queued_items) self.btn_start_queue.setEnabled(enabled) - self.btn_manage_queue.setEnabled(enabled) + # Update Manage Queue button text with count + if enabled: + self.btn_manage_queue.setText(f"Manage Queue ({len(self.queued_items)})") + self.btn_manage_queue.setStyleSheet( + f"QPushButton {{ color: {COLORS['GREEN']}; }}" + ) + else: + self.btn_manage_queue.setText("Manage Queue") + self.btn_manage_queue.setStyleSheet("") + # Change main Start button to 'Start queue' if queue has items + if enabled: + self.btn_start.setText("Start queue") + try: + self.btn_start.clicked.disconnect() + except Exception: + pass + self.btn_start.clicked.connect(self.start_queue) + else: + self.btn_start.setText("Start") + try: + self.btn_start.clicked.disconnect() + except Exception: + pass + self.btn_start.clicked.connect(self.start_conversion) def enqueue(self, item : QueuedItem): self.queued_items.append(item) - self.update_log((f"Enqueued: {item.file_name}", True)) + #self.update_log((f"Enqueued: {item.file_name}", True)) # enable start queue button, manage queue button self.enable_disable_queue_buttons() @@ -1362,17 +1385,46 @@ class abogen(QWidget): output_folder=self.selected_output_folder, subtitle_mode=actual_subtitle_mode, output_format=self.selected_format, - total_char_count=self.char_count, - use_gpu=self.gpu_ok + total_char_count=self.char_count ) + + # Prevent adding duplicate items to the queue + for queued_item in self.queued_items: + if ( + queued_item.file_name == item.file_name and + queued_item.lang_code == item.lang_code and + queued_item.speed == item.speed and + queued_item.voice == item.voice and + queued_item.save_option == item.save_option and + queued_item.output_folder == item.output_folder and + queued_item.subtitle_mode == item.subtitle_mode and + queued_item.output_format == item.output_format and + queued_item.total_char_count == item.total_char_count + ): + QMessageBox.warning(self, "Duplicate Item", "This item is already in the queue.") + return + self.enqueue(item) # Clear input after adding to queue self.input_box.clear_input() + self.enable_disable_queue_buttons() + + def clear_queue(self): + # Warn user if more than 1 item in the queue before clearing + if len(self.queued_items) > 1: + reply = QMessageBox.question( + self, + "Confirm Clear Queue", + f"Are you sure you want to clear {len(self.queued_items)} items from the queue?", + QMessageBox.Yes | QMessageBox.No, + QMessageBox.No + ) + if reply != QMessageBox.Yes: + return + self.queued_items = [] + self.enable_disable_queue_buttons() def manage_queue(self): - if not self.queued_items: - self.input_box.set_error("Queue is empty.") - return # show a dialog to manage the queue dialog = QueueManager(self, self.queued_items) if dialog.exec_() == QDialog.Accepted: @@ -1381,9 +1433,6 @@ class abogen(QWidget): self.enable_disable_queue_buttons() def start_queue(self): - if not self.queued_items: - self.input_box.set_error("Queue is empty.") - return self.current_queue_index = 0 # Start from the first item self.start_next_queued_item() @@ -1399,7 +1448,6 @@ class abogen(QWidget): self.subtitle_mode = item.subtitle_mode self.selected_format = item.output_format self.char_count = item.total_char_count - self.gpu_ok = item.use_gpu self.start_conversion() else: # Queue finished, reset index @@ -1455,6 +1503,7 @@ class abogen(QWidget): self.progress_bar.setFormat("%p%") # Reset format initially self.etr_label.hide() # Hide ETR label initially self.controls_widget.hide() + self.queue_row_widget.hide() # Hide queue row when process starts self.progress_bar.show() self.btn_cancel.show() QApplication.processEvents() @@ -1635,6 +1684,7 @@ class abogen(QWidget): if self.open_file_btn: self.open_file_btn.show() self.controls_widget.show() + self.queue_row_widget.show() # Show queue row on reset self.finish_widget.hide() self.btn_start.setText("Start") # Disconnect only if connected, then reconnect @@ -1643,12 +1693,14 @@ class abogen(QWidget): except TypeError: pass # Ignore error if not connected self.btn_start.clicked.connect(self.start_conversion) + self.enable_disable_queue_buttons() except Exception as e: self._show_error_message_box("Reset Error", f"Could not reset UI:\n{e}") def go_back_ui(self): self.finish_widget.hide() self.controls_widget.show() + self.queue_row_widget.show() # Show queue row on go back self.progress_bar.hide() self.restore_input_box() @@ -2023,6 +2075,7 @@ class abogen(QWidget): self.progress_bar.hide() self.btn_cancel.hide() self.controls_widget.show() + self.queue_row_widget.show() # Show queue row on cancel self.finish_widget.hide() self.restore_input_box() display_path = ( diff --git a/abogen/queue_manager_gui.py b/abogen/queue_manager_gui.py index b8da6f3..42ff70b 100644 --- a/abogen/queue_manager_gui.py +++ b/abogen/queue_manager_gui.py @@ -5,35 +5,83 @@ from PyQt5.QtWidgets import ( QDialog, QVBoxLayout, + QHBoxLayout, # added QDialogButtonBox, QPushButton, QListWidget, + QListWidgetItem, + QFileIconProvider, + QLabel, ) - +from PyQt5.QtCore import QFileInfo, Qt +from constants import COLORS class QueueManager(QDialog): - def __init__(self, parent, queue: list, title="Queue Manager", size=(400, 300)): + def __init__(self, parent, queue: list, title="Queue Manager", size=(600, 700)): super().__init__() self.queue = queue self.parent = parent layout = QVBoxLayout() + layout.setContentsMargins(15, 15, 15, 15) # set main layout margins + layout.setSpacing(12) # set spacing between widgets in main layout # list of queued items self.listwidget = QListWidget() - self.listwidget.setSelectionMode(QListWidget.SingleSelection) + self.listwidget.setSelectionMode(QListWidget.ExtendedSelection) self.listwidget.setAlternatingRowColors(True) + self.listwidget.setContextMenuPolicy(Qt.CustomContextMenu) + self.listwidget.customContextMenuRequested.connect(self.show_context_menu) + # Add informative instructions at the top + instructions = QLabel( + "

How Queue Works?

" + "You can add text files (.txt) directly using the 'Add files' button below. " + "To add PDF or EPUB files, use the input box in the main window and click the 'Add to Queue' button. " + "Each file in the queue keeps the configuration settings active when it was added. " + "Changing the main window configuration afterward does not affect files already in the queue. " + "You can view each file's configuration by hovering over them." + ) + instructions.setAlignment(Qt.AlignLeft) + instructions.setWordWrap(True) + instructions.setStyleSheet("margin-bottom: 8px;") + layout.addWidget(instructions) + # Overlay label for empty queue + self.empty_overlay = QLabel( + "No items in the queue.", + self.listwidget + ) + self.empty_overlay.setAlignment(Qt.AlignCenter) + self.empty_overlay.setStyleSheet(f"color: {COLORS['LIGHT_DISABLED']}; background: transparent;") + self.empty_overlay.setWordWrap(True) + self.empty_overlay.hide() # add queue items to the list self.process_queue() - layout.addWidget(self.listwidget) - # add remove item button - remove_button = QPushButton("Remove") - remove_button.clicked.connect(self.remove_item) - layout.addWidget(remove_button) - # add clear queue button - clear_button = QPushButton("Clear Queue") - clear_button.clicked.connect(self.clear_queue) - layout.addWidget(clear_button) + button_row = QHBoxLayout() + button_row.setContentsMargins(0, 0, 0, 0) # optional: no margins for button row + button_row.setSpacing(7) # set spacing between buttons + # Add files button + add_files_button = QPushButton("Add files") + add_files_button.setFixedHeight(40) + add_files_button.clicked.connect(self.add_more_files) + button_row.addWidget(add_files_button) + # Remove button + self.remove_button = QPushButton("Remove selected") + self.remove_button.setFixedHeight(40) + self.remove_button.clicked.connect(self.remove_item) + button_row.addWidget(self.remove_button) + + # Clear button + self.clear_button = QPushButton("Clear Queue") + self.clear_button.setFixedHeight(40) + self.clear_button.clicked.connect(self.clear_queue) + button_row.addWidget(self.clear_button) + + layout.addLayout(button_row) + layout.addWidget(self.listwidget) + + # Connect selection change to update button state + self.listwidget.currentItemChanged.connect(self.update_button_states) + self.listwidget.itemSelectionChanged.connect(self.update_button_states) buttons = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel, self) buttons.accepted.connect(self.accept) @@ -44,30 +92,244 @@ class QueueManager(QDialog): self.setLayout(layout) self.setWindowTitle(title) - self.setMinimumSize(*size) + self.resize(*size) + + self.update_button_states() def process_queue(self): """Process the queue items.""" + self.listwidget.clear() if not self.queue: + self.empty_overlay.resize(self.listwidget.size()) + self.empty_overlay.show() + self.update_button_states() return + else: + self.empty_overlay.hide() + icon_provider = QFileIconProvider() for item in self.queue: - # TODO take just the file name from the item, no directories - self.listwidget.addItem(item.file_name) + # Only show the file name, not the full path + file_name = item.file_name + display_name = file_name + import os + if os.path.sep in file_name: + display_name = os.path.basename(file_name) + # Get icon for the file + icon = icon_provider.icon(QFileInfo(file_name)) + list_item = QListWidgetItem(icon, display_name) + # Set tooltip with detailed info + output_folder = getattr(item, 'output_folder', '') + tooltip = ( + f"Path: {file_name}
" + f"Language: {getattr(item, 'lang_code', '')}
" + f"Speed: {getattr(item, 'speed', '')}
" + f"Voice: {getattr(item, 'voice', '')}
" + f"Save Option: {getattr(item, 'save_option', '')}
" + ) + if output_folder not in (None, '', 'None'): + tooltip += f"Output Folder: {output_folder}
" + tooltip += ( + f"Subtitle Mode: {getattr(item, 'subtitle_mode', '')}
" + f"Output Format: {getattr(item, 'output_format', '')}
" + f"Characters: {getattr(item, 'total_char_count', '')}" + ) + list_item.setToolTip(tooltip) + self.listwidget.addItem(list_item) + + self.update_button_states() def remove_item(self): - item = self.listwidget.currentItem() - if not item: + items = self.listwidget.selectedItems() + if not items: return - found_item = [q for q in self.queue if q.file_name==item.text()][0] - self.queue.remove(found_item) - # rebind the queue to the listwidget - self.listwidget.clear() + import os + display_names = [item.text() for item in items] + to_remove = [] + for q in self.queue: + if os.path.basename(q.file_name) in display_names: + to_remove.append(q) + for item in to_remove: + self.queue.remove(item) self.process_queue() - pass + self.update_button_states() def clear_queue(self): + from PyQt5.QtWidgets import QMessageBox + if len(self.queue) > 1: + reply = QMessageBox.question( + self, + "Confirm Clear Queue", + f"Are you sure you want to clear {len(self.queue)} items from the queue?", + QMessageBox.Yes | QMessageBox.No, + QMessageBox.No + ) + if reply != QMessageBox.Yes: + return self.queue.clear() self.listwidget.clear() + self.empty_overlay.resize(self.listwidget.size()) # Ensure overlay is sized correctly + self.empty_overlay.show() # Show the overlay when queue is empty + self.update_button_states() def get_queue(self): - return self.queue \ No newline at end of file + return self.queue + + def get_current_attributes(self): + # Fetch current attribute values from the parent abogen GUI + attrs = {} + parent = self.parent + if parent is not None: + # lang_code: use parent's get_voice_formula and get_selected_lang + if hasattr(parent, 'get_voice_formula') and hasattr(parent, 'get_selected_lang'): + voice_formula = parent.get_voice_formula() + attrs['lang_code'] = parent.get_selected_lang(voice_formula) + attrs['voice'] = voice_formula + else: + attrs['lang_code'] = getattr(parent, 'selected_lang', '') + attrs['voice'] = getattr(parent, 'selected_voice', '') + # speed + if hasattr(parent, 'speed_slider'): + attrs['speed'] = parent.speed_slider.value() / 100.0 + else: + attrs['speed'] = getattr(parent, 'speed', 1.0) + # save_option + attrs['save_option'] = getattr(parent, 'save_option', '') + # output_folder + attrs['output_folder'] = getattr(parent, 'selected_output_folder', '') + # subtitle_mode + if hasattr(parent, 'get_actual_subtitle_mode'): + attrs['subtitle_mode'] = parent.get_actual_subtitle_mode() + else: + attrs['subtitle_mode'] = getattr(parent, 'subtitle_mode', '') + # output_format + attrs['output_format'] = getattr(parent, 'selected_format', '') + # total_char_count + attrs['total_char_count'] = getattr(parent, 'char_count', '') + else: + # fallback: empty values + attrs = {k: '' for k in [ + 'lang_code', 'speed', 'voice', 'save_option', + 'output_folder', 'subtitle_mode', 'output_format', 'total_char_count']} + return attrs + + def add_more_files(self): + from PyQt5.QtWidgets import QFileDialog, QMessageBox + import os + from utils import calculate_text_length # import the function + # Only allow .txt files + files, _ = QFileDialog.getOpenFileNames(self, "Select .txt files", "", "Text Files (*.txt)") + if not files: + return + # Get current attribute values from GUI + current_attrs = self.get_current_attributes() + duplicates = [] + for file_path in files: + # Create a dummy item with the current GUI attributes + class QueueItem: + pass + item = QueueItem() + item.file_name = file_path + for attr, value in current_attrs.items(): + setattr(item, attr, value) + # Read file content and calculate total_char_count using calculate_text_length + try: + with open(file_path, 'r', encoding='utf-8', errors='ignore') as f: + file_content = f.read() + item.total_char_count = calculate_text_length(file_content) + except Exception: + item.total_char_count = 0 + # Prevent adding duplicate items to the queue (check all attributes) + is_duplicate = False + for queued_item in self.queue: + if ( + getattr(queued_item, 'file_name', None) == getattr(item, 'file_name', None) and + getattr(queued_item, 'lang_code', None) == getattr(item, 'lang_code', None) and + getattr(queued_item, 'speed', None) == getattr(item, 'speed', None) and + getattr(queued_item, 'voice', None) == getattr(item, 'voice', None) and + getattr(queued_item, 'save_option', None) == getattr(item, 'save_option', None) and + getattr(queued_item, 'output_folder', None) == getattr(item, 'output_folder', None) and + getattr(queued_item, 'subtitle_mode', None) == getattr(item, 'subtitle_mode', None) and + getattr(queued_item, 'output_format', None) == getattr(item, 'output_format', None) and + getattr(queued_item, 'total_char_count', None) == getattr(item, 'total_char_count', None) + ): + is_duplicate = True + break + if is_duplicate: + duplicates.append(os.path.basename(file_path)) + continue + self.queue.append(item) + if duplicates: + QMessageBox.warning(self, "Duplicate Item(s)", "The following file(s) with the same attributes are already in the queue and were not added:\n" + '\n'.join(duplicates)) + self.process_queue() + self.update_button_states() + + def resizeEvent(self, event): + super().resizeEvent(event) + if hasattr(self, 'empty_overlay'): + self.empty_overlay.resize(self.listwidget.size()) + + def update_button_states(self): + # Enable Remove if at least one item is selected, else disable + if hasattr(self, 'remove_button'): + selected_count = len(self.listwidget.selectedItems()) + self.remove_button.setEnabled(selected_count > 0) + if selected_count > 1: + self.remove_button.setText(f"Remove selected ({selected_count})") + else: + self.remove_button.setText("Remove selected") + # Disable Clear if queue is empty + if hasattr(self, 'clear_button'): + self.clear_button.setEnabled(bool(self.queue)) + + def show_context_menu(self, pos): + from PyQt5.QtWidgets import QMenu, QAction + from PyQt5.QtGui import QDesktopServices + from PyQt5.QtCore import QUrl + import os + global_pos = self.listwidget.viewport().mapToGlobal(pos) + selected_items = self.listwidget.selectedItems() + menu = QMenu(self) + if len(selected_items) == 1: + remove_action = QAction("Remove this item", self) + remove_action.triggered.connect(self.remove_item) + menu.addAction(remove_action) + go_to_folder_action = QAction("Go to folder", self) + def go_to_folder(): + from PyQt5.QtWidgets import QMessageBox + item = selected_items[0] + display_name = item.text() + for q in self.queue: + if os.path.basename(q.file_name) == display_name: + if not os.path.exists(q.file_name): + QMessageBox.warning(self, "File Not Found", f"The file does not exist.") + return + folder = os.path.dirname(q.file_name) + if os.path.exists(folder): + QDesktopServices.openUrl(QUrl.fromLocalFile(folder)) + break + go_to_folder_action.triggered.connect(go_to_folder) + menu.addAction(go_to_folder_action) + # Add Open file action + open_file_action = QAction("Open file", self) + def open_file(): + from PyQt5.QtWidgets import QMessageBox + item = selected_items[0] + display_name = item.text() + for q in self.queue: + if os.path.basename(q.file_name) == display_name: + if not os.path.exists(q.file_name): + QMessageBox.warning(self, "File Not Found", f"The file does not exist.") + return + QDesktopServices.openUrl(QUrl.fromLocalFile(q.file_name)) + break + open_file_action.triggered.connect(open_file) + menu.addAction(open_file_action) + elif len(selected_items) > 1: + remove_action = QAction(f"Remove selected ({len(selected_items)})", self) + remove_action.triggered.connect(self.remove_item) + menu.addAction(remove_action) + # Always add Clear Queue + clear_action = QAction("Clear Queue", self) + clear_action.triggered.connect(self.clear_queue) + menu.addAction(clear_action) + menu.exec_(global_pos) \ No newline at end of file diff --git a/abogen/queued_item.py b/abogen/queued_item.py index 635dd8f..9b22d03 100644 --- a/abogen/queued_item.py +++ b/abogen/queued_item.py @@ -11,5 +11,4 @@ class QueuedItem: output_folder: str subtitle_mode: str output_format: str - total_char_count: int - use_gpu: bool \ No newline at end of file + total_char_count: int \ No newline at end of file