diff options
Diffstat (limited to 'deskutils/calibre/files/patch-tts-missing-fix')
-rw-r--r-- | deskutils/calibre/files/patch-tts-missing-fix | 81 |
1 files changed, 81 insertions, 0 deletions
diff --git a/deskutils/calibre/files/patch-tts-missing-fix b/deskutils/calibre/files/patch-tts-missing-fix new file mode 100644 index 000000000000..a8ea80597c6a --- /dev/null +++ b/deskutils/calibre/files/patch-tts-missing-fix @@ -0,0 +1,81 @@ +From ee2e5374cec0b5a313e943bdba6cf31f6f48b27f Mon Sep 17 00:00:00 2001 +From: Eli Schwartz <eschwartz93@gmail.com> +Date: Fri, 8 Aug 2025 01:15:04 -0400 +Subject: [PATCH] TTS: gracefully handle missing piper support + +Third party redistributors might choose to skip distributing this for a +couple reasons: +- missing dependencies +- lack of interest in TTS as a feature + +Lay some groundwork for handling this with fewer error message popups. +In particular note that speechd / flite depend on PyQt6 being built with +it, so support *may* appear dynamically after calibre is installed, and +available_engines queries Qt to see what is available. Piper is built +as part of calibre though, and if it has been patched out or skipped +via `setup.py build --only=xxx` we can at least avoid claiming it's +there. + +Entrypoints into TTS eventually tend to consolidate into creating the +backend. This gives us one consistent place to raise errors for missing +backends... which however doesn't handle forcing a backend name. A +forced backend that is unavailable ended up hitting the "no prefs" +fallback code to use the default engine, which returned a different +backend than the one which is *forced*, and later a KeyError when +tweak_book attempted to access the backend name it forced but which +didn't exist. + +Instead, raise an immediate "TTS engine piper is not available" error +dialog box, preventing any further confusing tracebacks. +--- + src/calibre/gui2/tts/types.py | 30 +++++++++++++++++++++--------- + 1 file changed, 21 insertions(+), 9 deletions(-) + +diff --git a/src/calibre/gui2/tts/types.py b/src/calibre/gui2/tts/types.py +index 40a2aad27e71..f509e8bddd16 100644 +--- src/calibre/gui2/tts/types.py ++++ src/calibre/gui2/tts/types.py +@@ -234,11 +234,18 @@ def qt_engine_metadata(name: str, human_name: str, desc: str, allows_choosing_au + ), True) + elif x == 'speechd': + continue +- ans['piper'] = EngineMetadata('piper', _('The Piper Neural Engine'), _( +- 'The "piper" engine can track the currently spoken sentence on screen. It uses a neural network ' +- 'for natural sounding voices. The neural network is run locally on your computer, it is fairly resource intensive to run.' +- ), TrackingCapability.Sentence, can_change_pitch=False, voices_have_quality_metadata=True, has_managed_voices=True, +- has_sentence_delay=True) ++ ++ try: ++ import calibre_extensions.piper ++ except ImportError: ++ pass ++ else: ++ ans['piper'] = EngineMetadata('piper', _('The Piper Neural Engine'), _( ++ 'The "piper" engine can track the currently spoken sentence on screen. It uses a neural network ' ++ 'for natural sounding voices. The neural network is run locally on your computer, it is fairly resource intensive to run.' ++ ), TrackingCapability.Sentence, can_change_pitch=False, voices_have_quality_metadata=True, has_managed_voices=True, ++ has_sentence_delay=True) ++ + if islinux: + try: + from speechd.paths import SPD_SPAWN_CMD +@@ -322,10 +329,15 @@ def create_tts_backend(force_engine: str | None = None, config_name: str = CONFI + if not available_engines(): + raise OSError('There are no available TTS engines. Install a TTS engine before trying to use Read Aloud, such as flite or speech-dispatcher') + prefs = load_config(config_name) +- engine_name = prefs.get('engine', '') if force_engine is None else force_engine +- engine_name = engine_name or default_engine_name() +- if engine_name not in available_engines(): +- engine_name = default_engine_name() ++ if force_engine is not None: ++ engine_name = force_engine ++ if engine_name not in available_engines(): ++ raise OSError(f'TTS engine {force_engine} is not available.') ++ else: ++ engine_name = prefs.get('engine', '') ++ engine_name = engine_name or default_engine_name() ++ if engine_name not in available_engines(): ++ engine_name = default_engine_name() + if engine_name == 'piper': + if engine_name not in engine_instances: + from calibre.gui2.tts.piper import Piper |