mirror of
https://github.com/denizsafak/abogen.git
synced 2026-07-18 13:40:27 +02:00
Added new option: Override item settings with current selection
This commit is contained in:
+127
-31
@@ -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
@@ -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):
|
||||
|
||||
Reference in New Issue
Block a user