Added new option: Override item settings with current selection

This commit is contained in:
Deniz Şafak
2025-12-10 01:27:00 +03:00
parent e3cdd1a9ca
commit c43659005e
5 changed files with 201 additions and 50 deletions
+127 -31
View File
@@ -1952,6 +1952,11 @@ class abogen(QWidget):
dialog = QueueManager(self, self.queued_items)
if dialog.exec() == QDialog.DialogCode.Accepted:
self.queued_items = dialog.get_queue()
# Reload config to capture the new "Override" setting
# The QueueManager writes to disk, so we must refresh our local copy
self.config = load_config()
# re-enable/disable buttons based on queue state
self.enable_disable_queue_buttons()
@@ -1967,30 +1972,62 @@ class abogen(QWidget):
def start_next_queued_item(self):
if self.current_queue_index < len(self.queued_items):
queued_item = self.queued_items[self.current_queue_index]
self.selected_file = queued_item.file_name
self.selected_lang = queued_item.lang_code
self.speed_slider.setValue(int(queued_item.speed * 100))
self.selected_voice = queued_item.voice
self.save_option = queued_item.save_option
self.selected_output_folder = queued_item.output_folder
self.subtitle_mode = queued_item.subtitle_mode
self.selected_format = queued_item.output_format
self.char_count = queued_item.total_char_count
self.replace_single_newlines = getattr(
queued_item, "replace_single_newlines", True
# Restore the original file path for save location (Important for EPUB/PDF)
self.displayed_file_path = (
queued_item.save_base_path or queued_item.file_name
)
self.use_silent_gaps = getattr(queued_item, "use_silent_gaps", False)
# Restore save_chapters_separately and merge_chapters_at_end from queued item
# Restore chapter options (Structure specific, must be preserved)
self.save_chapters_separately = getattr(
queued_item, "save_chapters_separately", None
)
self.merge_chapters_at_end = getattr(
queued_item, "merge_chapters_at_end", None
)
# Restore the original file path for save location
self.displayed_file_path = (
queued_item.save_base_path or queued_item.file_name
)
# CHECK GLOBAL OVERRIDE SETTING
if not self.config.get("queue_override_settings", False):
self.selected_lang = queued_item.lang_code
self.speed_slider.setValue(int(queued_item.speed * 100))
# Load the specific voice string
self.selected_voice = queued_item.voice
# Clear complex GUI states so the specific voice string is used
self.mixed_voice_state = None
self.selected_profile_name = None
self.save_option = queued_item.save_option
self.selected_output_folder = queued_item.output_folder
self.subtitle_mode = queued_item.subtitle_mode
self.selected_format = queued_item.output_format
self.replace_single_newlines = getattr(
queued_item, "replace_single_newlines", True
)
self.use_silent_gaps = getattr(queued_item, "use_silent_gaps", False)
self.subtitle_speed_method = getattr(
queued_item, "subtitle_speed_method", "tts"
)
# This ensures that if conversion.py (or utils) reads from config/disk
# instead of using passed arguments, it sees the correct queue values.
self.config["replace_single_newlines"] = self.replace_single_newlines
self.config["subtitle_mode"] = self.subtitle_mode
self.config["selected_format"] = self.selected_format
self.config["use_silent_gaps"] = self.use_silent_gaps
self.config["subtitle_speed_method"] = self.subtitle_speed_method
# Sync Voice/Profile in config
self.config["selected_voice"] = self.selected_voice
if "selected_profile_name" in self.config:
del self.config["selected_profile_name"]
# Note: Speed is already synced via self.speed_slider.setValue() -> update_speed_label()
save_config(self.config)
self.start_conversion(from_queue=True)
else:
# Queue finished, reset index
@@ -2188,36 +2225,95 @@ class abogen(QWidget):
threading.Thread(target=gpu_and_load, daemon=True).start()
def show_queue_summary(self):
"""Show a styled, resizable summary dialog after queue finishes."""
"""Show a summary dialog after queue finishes."""
if not self.queued_items:
return
# Build HTML summary for better styling
# Check if override was active (this determines which settings were ACTUALLY used)
override_active = self.config.get("queue_override_settings", False)
# If override is ON, capture the global settings that were used for processing
if override_active:
g_voice = self.get_voice_formula()
g_lang = self.get_selected_lang(g_voice)
g_speed = self.speed_slider.value() / 100.0
g_sub_mode = self.get_actual_subtitle_mode()
g_format = self.selected_format
g_newlines = self.replace_single_newlines
g_silent_gaps = self.use_silent_gaps
g_speed_method = self.subtitle_speed_method
# Build HTML summary (Default Styling)
summary_html = "<html><body>"
header_text = "Queue finished"
if override_active:
header_text += " (Global Settings Applied)"
summary_html += (
f"<h2 style='color:{COLORS['LIGHT_BG']};'>Queue finished</h2>"
f"<h2>{header_text}</h2>"
f"Processed {len(self.queued_items)} items:<br><br>"
)
for idx, item in enumerate(self.queued_items, 1):
output = getattr(item, "output_path", None)
if not output:
output = "Unknown"
# Resolve Effective Settings
if override_active:
eff_lang = g_lang
eff_voice = g_voice
eff_speed = g_speed
eff_sub_mode = g_sub_mode
eff_format = g_format
eff_newlines = g_newlines
eff_silent = g_silent_gaps
eff_method = g_speed_method
else:
eff_lang = item.lang_code
eff_voice = item.voice
eff_speed = item.speed
eff_sub_mode = item.subtitle_mode
eff_format = item.output_format
eff_newlines = getattr(item, "replace_single_newlines", True)
eff_silent = getattr(item, "use_silent_gaps", False)
eff_method = getattr(item, "subtitle_speed_method", "tts")
# Retrieve File-Specific Data (Never Overridden)
eff_chars = item.total_char_count
eff_input = item.file_name
eff_output = getattr(item, "output_path", "Unknown")
eff_save_sep = getattr(item, "save_chapters_separately", None)
eff_merge = getattr(item, "merge_chapters_at_end", None)
# --- Construct Display Block ---
summary_html += (
f"<span style='color:{COLORS['GREEN']}; font-weight:bold;'>{idx}) {os.path.basename(item.file_name)}</span><br>"
f"<span style='color:{COLORS['LIGHT_DISABLED']};'>Language:</span> {item.lang_code}<br>"
f"<span style='color:{COLORS['LIGHT_DISABLED']};'>Voice:</span> {item.voice}<br>"
f"<span style='color:{COLORS['LIGHT_DISABLED']};'>Speed:</span> {item.speed}<br>"
f"<span style='color:{COLORS['LIGHT_DISABLED']};'>Characters:</span> {item.total_char_count}<br>"
f"<span style='color:{COLORS['LIGHT_DISABLED']};'>Input:</span> {item.file_name}<br>"
f"<span style='color:{COLORS['LIGHT_DISABLED']};'>Output:</span> {output}</span>"
f"<br><br>"
f"<span style='color:{COLORS['GREEN']}; font-weight:bold;'>{idx}) {os.path.basename(eff_input)}</span><br>"
f"<span style='color:{COLORS['LIGHT_DISABLED']};'>Language:</span> {eff_lang}<br>"
f"<span style='color:{COLORS['LIGHT_DISABLED']};'>Voice:</span> {eff_voice}<br>"
f"<span style='color:{COLORS['LIGHT_DISABLED']};'>Speed:</span> {eff_speed}<br>"
f"<span style='color:{COLORS['LIGHT_DISABLED']};'>Characters:</span> {eff_chars}<br>"
f"<span style='color:{COLORS['LIGHT_DISABLED']};'>Format:</span> {eff_format}<br>"
f"<span style='color:{COLORS['LIGHT_DISABLED']};'>Subtitle Mode:</span> {eff_sub_mode}<br>"
f"<span style='color:{COLORS['LIGHT_DISABLED']};'>Method:</span> {eff_method}<br>"
f"<span style='color:{COLORS['LIGHT_DISABLED']};'>Silent Gaps:</span> {eff_silent}<br>"
f"<span style='color:{COLORS['LIGHT_DISABLED']};'>Repl. Newlines:</span> {eff_newlines}<br>"
)
# Book/Chapter specific options
if eff_save_sep is not None:
summary_html += f"<span style='color:{COLORS['LIGHT_DISABLED']};'>Split Chapters:</span> {eff_save_sep}<br>"
if eff_save_sep and eff_merge is not None:
summary_html += f"<span style='color:{COLORS['LIGHT_DISABLED']};'>Merge End:</span> {eff_merge}<br>"
summary_html += (
f"<span style='color:{COLORS['LIGHT_DISABLED']};'>Input:</span> {eff_input}<br>"
f"<span style='color:{COLORS['LIGHT_DISABLED']};'>Output:</span> {eff_output}<br><br>"
)
summary_html += "</body></html>"
dialog = QDialog(self)
dialog.setWindowTitle("Queue Summary")
dialog.resize(550, 650) # Make window resizable and larger
# Allow resizing
dialog.resize(550, 650)
layout = QVBoxLayout(dialog)
text_edit = QTextEdit(dialog)
@@ -2232,7 +2328,7 @@ class abogen(QWidget):
dialog.setLayout(layout)
dialog.setMinimumSize(400, 300)
dialog.setSizeGripEnabled(True) # Allow resizing
dialog.setSizeGripEnabled(True)
dialog.exec()
def on_conversion_finished(self, message, output_path):
+71 -18
View File
@@ -15,11 +15,27 @@ from PyQt6.QtWidgets import (
QWidget,
QSizePolicy,
QAbstractItemView,
QCheckBox,
)
from PyQt6.QtCore import QFileInfo, Qt
from abogen.constants import COLORS
from copy import deepcopy
from PyQt6.QtGui import QFontMetrics
from abogen.utils import load_config, save_config
# Define attributes that are safe to override with global settings
OVERRIDE_FIELDS = [
"lang_code",
"speed",
"voice",
"save_option",
"output_folder",
"subtitle_mode",
"output_format",
"replace_single_newlines",
"use_silent_gaps",
"subtitle_speed_method",
]
class ElidedLabel(QLabel):
@@ -149,6 +165,8 @@ class QueueManager(QDialog):
queue
) # Store a deep copy of the original queue
self.parent = parent
self.config = load_config() # Load config for persistence
layout = QVBoxLayout()
layout.setContentsMargins(15, 15, 15, 15) # set main layout margins
layout.setSpacing(12) # set spacing between widgets in main layout
@@ -165,14 +183,27 @@ class QueueManager(QDialog):
"<h2>How Queue Works?</h2>"
"You can add text and subtitle files (.txt, .srt, .ass, .vtt) directly using the '<b>Add files</b>' button below. "
"To add PDF, EPUB or markdown files, use the input box in the main window and click the <b>'Add to Queue'</b> button. "
"Each file in the queue keeps the configuration settings active when it was added. "
"Changing the main window configuration afterward <b>does not</b> affect files already in the queue. "
"By default, each file in the queue keeps the configuration settings active when they were added. "
"Enabling the <b>'Override item settings with current selection'</b> option below will force all items to use the configuration currently selected in the main window. "
"You can view each file's configuration by hovering over them."
)
instructions.setAlignment(Qt.AlignmentFlag.AlignLeft)
instructions.setWordWrap(True)
instructions.setStyleSheet("margin-bottom: 8px;")
layout.addWidget(instructions)
# Override Checkbox
self.override_chk = QCheckBox("Override item settings with current selection")
self.override_chk.setToolTip(
"If checked, all items in the queue will be processed using the \n"
"settings currently selected in the main window, ignoring their saved state."
)
# Load saved state (default to False)
self.override_chk.setChecked(self.config.get("queue_override_settings", False))
# Trigger process_queue to update tooltips immediately when toggled
self.override_chk.stateChanged.connect(self.process_queue)
self.override_chk.setStyleSheet("margin-bottom: 8px;")
layout.addWidget(self.override_chk)
# Overlay label for empty queue
self.empty_overlay = QLabel(
"Drag and drop your text or subtitle files here or use the 'Add files' button.",
@@ -245,8 +276,21 @@ class QueueManager(QDialog):
return
else:
self.empty_overlay.hide()
# Get current global settings and checkbox state for overrides
current_global_settings = self.get_current_attributes()
is_override_active = self.override_chk.isChecked()
icon_provider = QFileIconProvider()
for item in self.queue:
# Dynamic Attribute Retrieval Helper
def get_val(attr, default=""):
# If override is ON and attr is overrideable, use global setting
if is_override_active and attr in OVERRIDE_FIELDS:
return current_global_settings.get(attr, default)
# Otherwise return the item's saved attribute
return getattr(item, attr, default)
# Determine display file path (prefer save_base_path for original file)
display_file_path = getattr(item, "save_base_path", None) or item.file_name
processing_file_path = item.file_name
@@ -271,8 +315,14 @@ class QueueManager(QDialog):
# Get icon for the display file
icon = icon_provider.icon(QFileInfo(display_file_path))
list_item = QListWidgetItem()
# Set tooltip with detailed info
output_folder = getattr(item, "output_folder", "")
# Tooltip Generation
tooltip = ""
# If override is active, add the warning header on its own line
if is_override_active:
tooltip += "<b style='color: #ff9900;'>(Global Override Active)</b><br>"
output_folder = get_val("output_folder")
# For plain .txt inputs we don't need to show a separate processing file
show_processing = True
try:
@@ -283,30 +333,31 @@ class QueueManager(QDialog):
except Exception:
show_processing = True
tooltip = f"<b>Input File:</b> {display_file_path}<br>"
tooltip += f"<b>Input File:</b> {display_file_path}<br>"
if (
show_processing
and processing_file_path
and processing_file_path != display_file_path
):
tooltip += f"<b>Processing File:</b> {processing_file_path}<br>"
tooltip += (
f"<b>Language:</b> {getattr(item, 'lang_code', '')}<br>"
f"<b>Speed:</b> {getattr(item, 'speed', '')}<br>"
f"<b>Voice:</b> {getattr(item, 'voice', '')}<br>"
f"<b>Save Option:</b> {getattr(item, 'save_option', '')}<br>"
f"<b>Language:</b> {get_val('lang_code')}<br>"
f"<b>Speed:</b> {get_val('speed')}<br>"
f"<b>Voice:</b> {get_val('voice')}<br>"
f"<b>Save Option:</b> {get_val('save_option')}<br>"
)
if output_folder not in (None, "", "None"):
tooltip += f"<b>Output Folder:</b> {output_folder}<br>"
tooltip += (
f"<b>Subtitle Mode:</b> {getattr(item, 'subtitle_mode', '')}<br>"
f"<b>Output Format:</b> {getattr(item, 'output_format', '')}<br>"
f"<b>Subtitle Mode:</b> {get_val('subtitle_mode')}<br>"
f"<b>Output Format:</b> {get_val('output_format')}<br>"
f"<b>Characters:</b> {getattr(item, 'total_char_count', '')}<br>"
f"<b>Replace Single Newlines:</b> {getattr(item, 'replace_single_newlines', True)}<br>"
f"<b>Use Silent Gaps:</b> {getattr(item, 'use_silent_gaps', False)}<br>"
f"<b>Speed Method:</b> {getattr(item, 'subtitle_speed_method', 'tts')}"
f"<b>Replace Single Newlines:</b> {get_val('replace_single_newlines', True)}<br>"
f"<b>Use Silent Gaps:</b> {get_val('use_silent_gaps', False)}<br>"
f"<b>Speed Method:</b> {get_val('subtitle_speed_method', 'tts')}"
)
# Add book handler options if present
# Add book handler options if present (Preserve logic: specific to file structure)
save_chapters_separately = getattr(item, "save_chapters_separately", None)
merge_chapters_at_end = getattr(item, "merge_chapters_at_end", None)
if save_chapters_separately is not None:
@@ -531,7 +582,6 @@ class QueueManager(QDialog):
def add_more_files(self):
from PyQt6.QtWidgets import QFileDialog
from abogen.utils import calculate_text_length # import the function
# Allow .txt, .srt, .ass, and .vtt files
files, _ = QFileDialog.getOpenFileNames(
@@ -774,7 +824,10 @@ class QueueManager(QDialog):
menu.exec(global_pos)
def accept(self):
# Accept: keep changes
# Save the override state to config so it persists globally
self.config["queue_override_settings"] = self.override_chk.isChecked()
save_config(self.config)
super().accept()
def reject(self):