diff --git a/CHANGELOG.md b/CHANGELOG.md index d3d73b8..93f5ede 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1 +1,2 @@ -- Better approach for detemining the correct configuration folder for Linux and MacOS, using platformdirs. \ No newline at end of file +- Added support for chapter metadata in m4b audio files, enabling proper chapter navigation in audiobooks. Special thanks to @jborza for implementing this feature in PR #10. +- Better approach for determining the correct configuration folder for Linux and MacOS, using platformdirs. \ No newline at end of file diff --git a/abogen/conversion.py b/abogen/conversion.py index 2e2610b..0612e0c 100644 --- a/abogen/conversion.py +++ b/abogen/conversion.py @@ -12,6 +12,7 @@ from utils import clean_text from constants import PROGRAM_NAME, LANGUAGE_DESCRIPTIONS, SAMPLE_VOICE_TEXTS from voice_formulas import get_new_voice import static_ffmpeg +import threading # for efficient waiting def get_sample_voice_text(lang_code): return SAMPLE_VOICE_TEXTS.get(lang_code, SAMPLE_VOICE_TEXTS["a"]) @@ -128,6 +129,7 @@ class ConversionThread(QThread): use_gpu=True, ): # Add use_gpu parameter super().__init__() + self._chapter_options_event = threading.Event() self.np = np_module self.KPipeline = kpipeline_class self.file_name = file_name @@ -252,18 +254,12 @@ class ConversionThread(QThread): and not self.chapter_options_set ): - self.waiting_for_user_input = True - # Emit signal to main thread to show dialog + # Emit signal to main thread and wait self.chapters_detected.emit(total_chapters) - - # Wait for chapter options to be set - while self.waiting_for_user_input and not self.cancel_requested: - time.sleep(0.1) - + self._chapter_options_event.wait() if self.cancel_requested: self.conversion_finished.emit("Cancelled", None) return - self.chapter_options_set = True # Log all detected chapters at the beginning @@ -607,6 +603,7 @@ class ConversionThread(QThread): self.save_chapters_separately = options["save_chapters_separately"] self.merge_chapters_at_end = options["merge_chapters_at_end"] self.waiting_for_user_input = False + self._chapter_options_event.set() def _generate_m4b_with_chapters(self, out_path, chapters_time): # generate chapters.txt in the same folder as the output file diff --git a/abogen/gui.py b/abogen/gui.py index 95ba0e2..b5780db 100644 --- a/abogen/gui.py +++ b/abogen/gui.py @@ -670,13 +670,18 @@ class abogen(QWidget): format_layout = QVBoxLayout() format_layout.addWidget(QLabel("Output Format:", self)) self.format_combo = QComboBox(self) - self.format_combo.setStyleSheet( - "QComboBox { min-height: 20px; padding: 6px 12px; }" + self.format_combo.setStyleSheet("QComboBox { min-height: 20px; padding: 6px 12px; }") + # Add items with display labels and underlying keys + for key, label in [("wav","wav"),("flac","flac"),("mp3","mp3"),("m4b","m4b (with chapters)")]: + self.format_combo.addItem(label, key) + # Initialize selection by matching saved key + idx = self.format_combo.findData(self.selected_format) + if idx >= 0: + self.format_combo.setCurrentIndex(idx) + # Map selection back to key on change + self.format_combo.currentIndexChanged.connect( + lambda i: self.on_format_changed(self.format_combo.itemData(i)) ) - format_options = ["wav", "flac", "mp3", "m4b"] - self.format_combo.addItems(format_options) - self.format_combo.setCurrentText(self.selected_format) - self.format_combo.currentTextChanged.connect(self.on_format_changed) format_layout.addWidget(self.format_combo) controls_layout.addLayout(format_layout) # Save location