Artem Akymenko
2b70b9ca45
feat: Supertonic language + total_steps propagation
...
Language enum expanded from 9 to 33 languages:
- Added 24 new ISO 639-1 languages: AR, BG, CS, DA, DE, EL, ET, FI,
HR, HU, ID, KO, LT, LV, NL, PL, RO, RU, SK, SL, SV, TR, UK, VI
- Updated display_name, is_cjk (added KO)
Supertonic language mapping (32 languages, no ZH):
- engine.py: _SUPERTONIC_LANG_MAP, engine_language(), supported_languages()
- __init__.py: create_engine() passes config.language to pipeline
- pipeline.py: __init__() accepts language, resolves to ISO code;
__call__() passes lang= to TTS.synthesize()
total_steps propagation:
- tts_segments(): +total_steps param, conditionally passed to backend
- synthesize_text(): +total_steps param
- run_tts_segment_loop(): +total_steps param
- executor: all 5 synthesize_text() calls pass total_steps
Integration:
- pipeline_factory: create_pipeline_for_job() passes language to supertonic
- preview path: create_pipeline('supertonic', language=language)
Tests updated to accept total_steps in FakeBackend.__call__
2026-07-28 14:09:42 +03:00
Artem Akymenko
953bef1e71
refactor: group ConversionRequest fields into config objects
...
Domain config types (domain/config_types.py):
- PronunciationConfig: pronunciation/heteronym/normalization overrides
- SubtitleConfig: mode, format, max_words
- CoverConfig: path, mime
Domain functions now accept config objects:
- build_tts_context(subtitle=, pronunciation=) instead of 9 individual params
- make_subtitle_writer(subtitle=) instead of 3 params
- process_and_write_subtitles(subtitle=) instead of 2 params
- embed_m4b_metadata(cover=) instead of 2 params
- build_epub3_package(cover=) instead of 2 params
ConversionRequest: 18 flat fields + 8 config objects
Application/config.py re-exports domain types
All tests updated to new API
2026-07-28 13:41:45 +03:00
Artem Akymenko
146cc81271
refactor: centralize cleanup in app layer
...
- New: application/cleanup.py — flush_cuda(), dispose_engines(), cleanup(), register_ui_cleanup()
- conversion_service.py finally: pool.dispose_all() + voice_cache.clear() + flush_cuda()
- webui/conversion_runner.py: removed gc/cuda finally block (cleanup in run_conversion)
- shutdown.py: 160→120 lines, 5 inline cleanups → 4 process-level + app_cleanup() delegation
- Fixed bugs: _PIPELINES (didn't exist), PluginManager.dispose_all() (never called),
VoiceCache.clear() (never called in finally), duplicate cleanup removed
2026-07-28 13:41:28 +03:00
Artem Akymenko
61204cc389
refactor: move pool/cache/resolver into run_conversion()
...
- New: application/voice_resolver.py — AppVoiceResolver (app-layer, takes ConversionRequest)
- conversion_service.py: run_conversion(request, events) creates PipelinePool, VoiceCache, AppVoiceResolver internally
- conversion_runner.py: 268→165 lines, removed WebUIVoiceResolver, pool/cache creation
- Tests: mock fixture for pool/resolver, removed unused variables
- Added pool.dispose_all() in finally block for cleanup
2026-07-27 13:37:19 +03:00
Artem Akymenko
f7a224cc46
chore: remove dead test files and deleted conversion_adapter
...
All 8 deleted test files were duplicates of existing domain tests:
- test_chapter_overrides → covered by test_chapter_merge_normalize
- test_conversion_chapter_titles → covered by test_chapter_titles
- test_conversion_series → covered by test_title_builder
- test_conversion_voice_resolution → covered by test_voice_resolution
- test_voice_cache → covered by test_voice_resolution
- test_manual_overrides_applied_first → covered by test_pronunciation
- test_conversion_adapters → tested deleted conversion_adapter module
- test_import_layering → tested deleted conversion_adapter module
Also removed deleted conversion_adapter.py (logic moved to conversion_runner).
2026-07-27 09:56:51 +00:00
Artem Akymenko
98ab2d925e
refactor: simplify _build_request, restore epub3_export as config object
...
Simplify _build_request() in WebUI
- Remove profiles loading (load_profiles, normalize_profile_entry)
- Remove PronunciationConfig/Epub3ExportConfig building
- Pass raw fields directly to ConversionRequest
- Clean up unused imports
Bugfix: epub3_export as Optional[Epub3ExportConfig] (None = disabled)
- Reverted generate_epub3: bool + epub3_book_id: str back to object pattern
- Consistent with word_substitution, subtitle_input, chapter_chunk
- Updated conversion_service.py _finalize() to use request.epub3_export
2026-07-27 12:41:29 +03:00
Artem Akymenko
625b6610e2
refactor: remove _prepare_tts_context, use build_tts_context directly
...
- Deleted _prepare_tts_context() (104 lines of duplicated logic)
- Replaced with direct build_tts_context() call in run_conversion()
- Fixed _finalize() to use raw fields (generate_epub3, epub3_book_id)
- Removed _MockJob antipattern
- Updated tests to use raw normalization_overrides field
- ConversionRequest uses raw fields instead of PronunciationConfig/Epub3ExportConfig
2026-07-27 12:19:59 +03:00
Artem Akymenko
0dc491e420
refactor: unify Language enum across all layers
...
- EngineConfig.language: Language (was lang_code: str = 'a')
- Engine owns _KOKORO_LANG_MAP, engine_language(), supported_languages()
- Engine provides language_for_voice_id() for voice catalog
- Plugins/kokoro/__init__.py calls engine_language() internally
- create_pipeline(plugin_id, language=Language) — no kokoro codes
- pipeline_factory.py clean of kokoro-specific code
- Domain functions raise TypeError if non-enum passed
- WebUI api.py: _parse_language() helper at API boundary
- Voice catalog returns ISO codes (lang.value)
- Constants: LANGUAGE_DESCRIPTIONS keyed by Language enum
- All tests updated for Language enum
- 1414 tests pass
2026-07-27 07:36:36 +00:00
Artem Akymenko
713abdfd73
refactor: voice resolution on ConversionRequest, PipelinePool without job param
...
- Added speakers field to ConversionRequest
- Rewrote collect_required_voice_ids(), initialize_voice_cache(),
job_voice_fallback(), chapter_voice_spec(), chunk_voice_spec()
to accept ConversionRequest instead of job
- PipelinePool.get() now takes request= instead of job=
- Updated all tests to use ConversionRequest interface
- 1493 tests passing
2026-07-26 14:11:55 +03:00
Artem Akymenko
73f42e9563
refactor: voice fallback logging + base voice validation
2026-07-26 11:42:56 +03:00
Artem Akymenko
2c61f55f81
refactor: spaCy pre-TTS segmentation moved to shared layer
...
- conversion_pipeline.py: new spacy_pre_tts_segmentation() function
- Handles: condition checks, language exclusion, split_pattern override
- Executor: integrated spaCy pre-TTS before each synthesize_text() call
- 6 new tests for condition checks and fallback behavior
- English excluded from pre-TTS (spaCy only for post-TTS subtitles)
- Fallback to regex when spaCy unavailable
2026-07-26 11:42:43 +03:00
Artem Akymenko
6497e8c47a
refactor: metadata + markers unified in shared layer
...
- MarkerCollector: SRP extraction from executor (observation, not execution)
- Outro marker: executor now records outro as chapter marker
- Voice format: chapter voices [{provider, voice}], chunk voice {provider, voice}
- _finalize(): build_metadata_payload() + metadata.json + record_override_usage()
- ffmetadata: voices list → comma-separated string
- EPUB3: ChunkOverlay.voice = dict, _render_chunk_inline handles dict format
- 8 new tests: multi-speaker, ffmetadata format, EPUB3 format
- Updated existing tests for new voice format
2026-07-26 11:42:27 +03:00
Artem Akymenko
0b953d48e8
refactor: heading transforms + dedup moved to shared layer
...
- conversion_planner.py: caps normalization in _build_chapters()
- conversion_executor.py: heading dedup + state machine via headings_equivalent()
- Fixed seg_start_time → chapter_body_start bug in executor
- Removed getattr fallback defaults in both adapters
- Added 4 tests for caps normalization and heading dedup
- Updated ARCHITECTURE_REFACTOR_PLAN.md with deferred WebUI cleanup
2026-07-26 11:41:59 +03:00
Artem Akymenko
f516cf1985
merge: resolve conflicts with origin/main
...
- Import run_tts_segment_loop (used in pyqt/conversion.py)
- Use build_tts_context() instead of manual _MergeJob + TTSContext
- Drop unused AudioSink import
2026-07-24 18:34:49 +00:00
Artem Akymenko
3857c27aae
refactor: clean unused imports (ruff F401/F811), fix build_tts_context defaults
...
- Remove 77 unused imports across domain/application/runner files via ruff
- Add # noqa: F401 to re-exports used by tests and debug_tts_runner
- Fix build_tts_context: usage_counter uses 'is not None' instead of truthiness
- Fix test assertions: compiled rules use 'replacement' key not 'pronunciation'
- Add re-exports: _compile_pronunciation_rules, _merge_pronunciation_overrides
2026-07-24 21:26:36 +03:00
Artem Akymenko
7ed2addb11
refactor: add build_metadata_payload() to domain, unify metadata assembly in both UIs
2026-07-24 21:26:21 +03:00
Artem Akymenko
cfc7de7abf
refactor: unify PUNCTUATION constants in domain/split_pattern.py
2026-07-24 19:17:39 +03:00
Artem Akymenko
654c395943
refactor: move sanitize_name_for_os to domain/output_paths.py
2026-07-24 19:17:39 +03:00
Artem Akymenko
d1a84cfb8b
refactor: move voice marker functions to domain/voice_markers.py
2026-07-24 19:17:38 +03:00
Artem Akymenko
332934c0cf
test: tests for apply_overrides, LLM mode, usage_counter, chunk_groups
2026-07-24 19:17:38 +03:00
Artem Akymenko
c79838a5a1
fix: chunk_groups_by_chapter in planner, chunks assigned to correct chapter
2026-07-24 19:17:38 +03:00
Artem Akymenko
d51a9118e4
fix: apply_overrides, LLM mode check, usage_counter in service
2026-07-24 19:17:38 +03:00
Artem Akymenko
3311bef2f7
feat: EPUB3 finalizer in service, extraction in ConversionPlan
2026-07-24 19:17:37 +03:00
Artem Akymenko
4123cadd87
feat: m4b finalizer in service (embed_m4b_metadata)
2026-07-24 19:17:37 +03:00
Artem Akymenko
2f83d10a1e
feat: per-chapter subtitle writer in executor
2026-07-24 19:17:37 +03:00
Artem Akymenko
a1241ee9ca
refactor: word substitution in planner, add planner tests
2026-07-24 19:17:37 +03:00
Artem Akymenko
0ee5bb0496
refactor: config objects for feature toggles in ConversionRequest
2026-07-24 19:17:36 +03:00
Artem Akymenko
7d28b7eb52
test: mock subprocess.Popen in test_executor_m4b_forces_merge to eliminate ffmpeg dependency
2026-07-24 19:17:36 +03:00
Deniz Şafak
9201f58770
Add k0sm0naft to GitHub funding list
2026-07-23 16:27:54 +03:00
Deniz Şafak
6274a02d5e
Fix empty voice list when launched via desktop shortcut
...
PluginManager.discover() used a relative path 'plugins', which resolved
against the CWD. When launched from a desktop shortcut the CWD is ~, so
the plugins directory was never found and no voices appeared in the list.
Fall back to the project-relative plugins path when the default relative
path doesn't resolve.
2026-07-23 03:55:07 +03:00
Deniz Şafak
342ea0dfac
Fix spaCy unknown language error: map Kokoro single-letter codes to Language enum in get_spacy_model
2026-07-23 03:33:06 +03:00
Deniz Şafak
27f88b759d
Fix spurious HF HEAD requests: return early on cache hit in tracked_hf_hub_download
2026-07-23 03:26:58 +03:00
Deniz Şafak
dbcbb1c8a9
Fix NameError: add missing 'from pathlib import Path' in pyqt/conversion.py
2026-07-23 03:18:51 +03:00
Deniz Şafak
bd99ee1ba1
fix: subtitle FakeToken split, missing run_tts_segment_loop import
...
- subtitle_generation: split multi-sentence FakeToken into separate entries
- conversion.py: add missing run_tts_segment_loop import
No changes to spacy_utils or Language enum.
2026-07-23 03:15:21 +03:00
Deniz Şafak
d5cddb9749
fix: pass mock job object to merge_pronunciation_overrides instead of positional args
2026-07-23 02:33:18 +03:00
Deniz Şafak
ec55918b04
fix: add load_single_voice to Pipeline wrapper to prevent formula string being used as download filename
2026-07-23 02:28:41 +03:00
Deniz Şafak
14913b45e9
fix: import importlib.util explicitly (not auto-loaded in Python 3.12) — broke plugin loading, causing empty voice lists
2026-07-23 02:17:46 +03:00
Deniz Şafak
a0fdabd81f
fix: suppress harmless Qt portal registration warning on Linux
2026-07-23 01:42:41 +03:00
Deniz Şafak
473631b84e
fix: use theme-aware GREY_BACKGROUND for word substitutions instructions label
2026-07-23 01:41:13 +03:00
Deniz Şafak
0f5003dfdd
fix: add missing imports for get_resource_path and load_integration_settings
2026-07-23 01:37:50 +03:00
Artem Akymenko
fcec4e9fe5
fix: test_stretch_reduces_duration — remove stale self param, fix mock data size, fix atempo assertion
2026-07-22 15:32:18 +03:00
Artem Akymenko
72d5e3d1db
fix: add keys() method to VoiceCache for resolve_intro compatibility
2026-07-22 15:31:43 +03:00
Artem Akymenko
d3682e7672
refactor: dynamic ConversionRequest validation, remove 'or default' from adapters
...
- __post_init__: _apply_none_defaults() iterates dataclasses.fields() dynamically
- _NUMERIC_CONSTRAINTS and _ENUM_CONSTRAINTS dicts replace per-field if chains
- Both adapters pass values as-is (no 'or default' fallbacks)
- 18 validation tests + updated adapter tests for Enum assertions
2026-07-22 11:33:44 +00:00
Artem Akymenko
0805e9fdae
refactor: Language Enum with ISO codes
...
- Language enum: en-US, en-GB, es, fr, hi, it, ja, pt-BR, zh
- Engine-specific mappings (kokoro → single-letter) live in pipeline_factory and synthesize
- spacy_utils uses Language enum keys for model mapping
- split_pattern uses Language enum properties (is_cjk)
- Updated all tests to use ISO codes
2026-07-22 10:54:39 +00:00
Artem Akymenko
4aef73ff85
refactor: remove infrastructure enum duplicates
...
- SubtitleFormat/SubtitleMode now only in domain/enums.py
- Added VTT to SubtitleFormat
- Renamed SENTENCE_HIGHLIGHTING → SENTENCE_HIGHLIGHT for consistency
- Infrastructure subtitle_writer imports from domain
2026-07-22 09:16:40 +00:00
Artem Akymenko
f6a8008f51
refactor: typed Enums for format/mode fields
...
- SubtitleMode, OutputFormat, SaveMode, SubtitleFormat, InputFormat
- Properties: dot_ext, is_lossless, is_book, is_subtitle
- from_str/from_path class methods with normalization
- Updated domain and application layers to use Enums
- 17 new tests for enum validation and properties
2026-07-22 09:01:50 +00:00
Artem Akymenko
dc5257252f
refactor: run_tts_segment_loop also accepts SynthParams
...
- Reduces from 14 params to 5 unique params + SynthParams
- synthesize_text now passes params through cleanly
- PyQt intro/outro direct calls updated
2026-07-22 08:28:19 +00:00
Artem Akymenko
c4cebb8822
refactor: SynthParams dataclass for synthesize_text
...
- Frozen dataclass in domain/conversion_engine.py with common params
- synthesize_text now takes params=SynthParams + unique kwargs
- Executor, PyQt legacy, WebUI legacy, and tests updated
- Adding new common params now only requires changing the dataclass
2026-07-22 08:21:45 +00:00
Artem Akymenko
93f5a46485
refactor: deduplicate synth params in executor
...
- Compute use_spacy and effective_subtitle_mode once instead of 6x each
- Reduces repeated ternary expressions across synthesize_text calls
2026-07-22 11:06:14 +03:00
Artem Akymenko
5f169a4921
refactor: replace executor _slugify with domain sanitize_filename_for_chapter
...
- Use existing domain function instead of duplicated local implementation
- Domain version includes OS-specific sanitization
2026-07-22 11:06:14 +03:00