From d0e42ee691ef93eeb8266f2829e0b235358e5ac8 Mon Sep 17 00:00:00 2001 From: Artem Akymenko Date: Tue, 21 Jul 2026 11:47:36 +0000 Subject: [PATCH] fix: executor subtitle_writer leak + adapter Path/None-default fixes - executor: manage subtitle_writer via ExitStack (stack.callback) - executor: remove manual subtitle_writer.close() - WebUI adapter: wrap source_path, output_folder, cover_image_path in Path() - WebUI adapter: remove unused threading/time imports - PyQt adapter: fix getattr(attr, None) or default for 4 fields --- abogen/application/conversion_executor.py | 5 +---- abogen/pyqt/conversion_adapter.py | 8 ++++---- abogen/webui/conversion_adapter.py | 9 ++++----- 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/abogen/application/conversion_executor.py b/abogen/application/conversion_executor.py index 2fef076..7e291a7 100644 --- a/abogen/application/conversion_executor.py +++ b/abogen/application/conversion_executor.py @@ -134,6 +134,7 @@ def execute_conversion( ) if subtitle_writer: subtitle_writer.open() + stack.callback(subtitle_writer.close) result.subtitle_paths.append(subtitle_writer.path) # Chapter directory @@ -381,10 +382,6 @@ def execute_conversion( ) events.log("Outro synthesized.") - # Close subtitle writer - if subtitle_writer: - subtitle_writer.close() - # Set result metadata result.total_chapters = len(plan.chapters) result.total_segments = sum(len(ch.segments) for ch in plan.chapters) diff --git a/abogen/pyqt/conversion_adapter.py b/abogen/pyqt/conversion_adapter.py index f5ab205..b9fad5e 100644 --- a/abogen/pyqt/conversion_adapter.py +++ b/abogen/pyqt/conversion_adapter.py @@ -68,12 +68,12 @@ def build_conversion_request_from_thread(thread: Any) -> ConversionRequest: voice_profile=getattr(thread, "voice_profile", None), speed=thread.speed or 1.0, use_gpu=thread.use_gpu, - supertonic_total_steps=getattr(thread, "supertonic_total_steps", 5), + supertonic_total_steps=getattr(thread, "supertonic_total_steps", None) or 5, # Output Format output_format=thread.output_format or "wav", subtitle_mode=thread.subtitle_mode or "Disabled", subtitle_format=getattr(thread, "subtitle_format", "srt"), - max_subtitle_words=getattr(thread, "max_subtitle_words", 50), + max_subtitle_words=getattr(thread, "max_subtitle_words", None) or 50, # Save Options save_mode=thread.save_option or "save_next_to_input", output_folder=output_folder, @@ -82,8 +82,8 @@ def build_conversion_request_from_thread(thread: Any) -> ConversionRequest: separate_chapters_format=getattr(thread, "separate_chapters_format", "wav"), save_as_project=getattr(thread, "save_as_project", False), # Timing - silence_between_chapters=getattr(thread, "silence_duration", 2.0), - chapter_intro_delay=getattr(thread, "chapter_intro_delay", 0.0), + silence_between_chapters=getattr(thread, "silence_duration", None) or 2.0, + chapter_intro_delay=getattr(thread, "chapter_intro_delay", None) or 0.0, # Content Processing replace_single_newlines=getattr(thread, "replace_single_newlines", False), read_title_intro=getattr(thread, "read_title_intro", False), diff --git a/abogen/webui/conversion_adapter.py b/abogen/webui/conversion_adapter.py index 7da8da9..836f8af 100644 --- a/abogen/webui/conversion_adapter.py +++ b/abogen/webui/conversion_adapter.py @@ -14,8 +14,7 @@ never accesses Job directly. from __future__ import annotations -import threading -import time +from pathlib import Path from typing import Any, Callable, Dict, List, Optional from abogen.application.conversion_request import ConversionRequest @@ -36,7 +35,7 @@ def build_conversion_request_from_job(job: Any) -> ConversionRequest: """ return ConversionRequest( # Source - source_path=job.stored_path, + source_path=Path(job.stored_path) if job.stored_path else None, original_filename=job.original_filename, # TTS Settings language=job.language or "a", @@ -53,7 +52,7 @@ def build_conversion_request_from_job(job: Any) -> ConversionRequest: max_subtitle_words=job.max_subtitle_words or 50, # Save Options save_mode=job.save_mode or "save_next_to_input", - output_folder=job.output_folder, + output_folder=Path(job.output_folder) if job.output_folder else None, save_chapters_separately=job.save_chapters_separately, merge_chapters_at_end=job.merge_chapters_at_end, separate_chapters_format=job.separate_chapters_format or "wav", @@ -81,7 +80,7 @@ def build_conversion_request_from_job(job: Any) -> ConversionRequest: # Metadata metadata_tags=job.metadata_tags or {}, # Artifacts - cover_image_path=job.cover_image_path, + cover_image_path=Path(job.cover_image_path) if job.cover_image_path else None, cover_image_mime=job.cover_image_mime, generate_epub3=job.generate_epub3, )