pyinstaller / pyinstaller-hooks-contrib

Community maintained hooks for PyInstaller.
Other
94 stars 125 forks source link

The script throws a "Class not registered" error when using PyInstaller (pyttsx3 + IVONA voices), although it works stably in PyCharm #403

Open PokoLokoW opened 2 years ago

PokoLokoW commented 2 years ago
import pyttsx3
tts_engine = pyttsx3.init(debug=True)
tts_engine.setProperty("rate", 140)
tts_voices = tts_engine.getProperty('voices')
for voice in tts_voices:
    if voice.name == "IVONA 2 Tatyana OEM":
        tts_engine.setProperty('voice', voice.id)
        print("Голос озвучки установлен на: " + voice.name)

tts_engine.say("Test message 1")
tts_engine.runAndWait()

tts_engine.say("Test message 2")
tts_engine.runAndWait()

tts_engine.say("Test message 3")
tts_engine.runAndWait()

A simple script works well in pyCharm, but if compiled with the command (removing -F doesn't help)

pyinstaller -F main.py --icon=icon.ico --name FileName

then during the processing of "runAndWait ()" it first shows 3 identical system windows with the error "load lib failed", and after closing these windows it shows an error in the log:

Traceback (most recent call last):

    File "pyttsx3\driver.py", line 92, in _pump

    File "pyttsx3\drivers\sapi5.py", line 56, in say

_ctypes.COMError: (-2147221164, 'Class not registered', (None, None, None, 0, None))

At the same time, if you use the standard system voice, there is no problem.

If the problem is not directly related to PyInstaller, then, if possible, please tell me at least what exactly could be the reason. I've been trying to figure out the problem for the third day and have not yet been able to find a solution.

bwoodsend commented 2 years ago

pyttsx3 has apparently been fixed before and I see they haven't released anything new since then. Either you just need to pip install -U pyinstaller-hooks-contrib or our test for pyttsx3 is woefully inadequate.

PokoLokoW commented 2 years ago

I have the latest version of pyinstaller-hooks-contrib installed. And even uninstalling + reinstalling also did not help fix the situation.

This problem is reproduced on another computer that has Windows 10 installed. And, as I wrote earlier, it is associated with specific voice options. Can you at least suggest how you can determine what data the script lacks for normal operation? I would try to fix the problem myself, but I was never able to get a sufficiently informative log that would help me specify the problem.

bwoodsend commented 2 years ago

Hmm, does --collect-submodules=pyttsx3 make any difference?

rokm commented 2 years ago

I doubt it does - the error message looks like a COM class is not registered, and that sapi5 driver/backend seems to do some runtime generation/compiling when imported. So it sounds like these are not set up correctly for whatever reason...

How does one install that "IVONA 2 Tatyana OEM" voice? Does it need to be installed externally on the system, outside the python/pyttsx3?

PokoLokoW commented 2 years ago

No, --collect-submodules=pyttsx3 did not help

Yes, the voice is installed from outside. I took from the Internet and use this file https://drive.google.com/file/d/1XTIkKdSO42bFOWcoL8K-PUiIzB3fA4N8/view?usp=sharing After installing the voice, it offers to install MiniSpeech. It doesn't need to be installed.

rokm commented 2 years ago

There are DLLs in C:\Program Files (x86)\Speech2Go Voice Package\x64 that need to be discoverable. And since PyInstaller clears the search path, they are not found, resulting in those "load lib failed" messages.

One way to work around this is to collect those DLLs into frozen application, for example via --add-binary "C:\Program Files (x86)\Speech2Go Voice Package\x64\*.dll;.".

I'm not sure if that's enough to make a portable application, though; i.e., does the engine require other stuff from Speech2Go Voice Package? If the user needs to install it on their system anyway, there's not much point in bundling those DLLs. In that case, you can add the system-installed DLL directory to the DLL search path in your python code, before you import pyttsx3. For example:

import os
speech2go_libs_dir = r'C:\Program Files (x86)\Speech2Go Voice Package\x64'
if os.path.isdir(speech2go_libs_dir):
    print(f"Adding {speech2go_libs_dir} to DLL paths...")
    os.add_dll_directory(speech2go_libs_dir)
    if "PATH" in os.environ:
        os.environ["PATH"] = speech2go_libs_dir + os.pathsep + os.environ["PATH"]
    else:
        os.environ["PATH"] = speech2go_libs_dir

import pyttsx3
tts_engine = pyttsx3.init(debug=True)
# [...]
PokoLokoW commented 2 years ago

Yes, it works. Thanks!

Is it possible to take this into account in PyInstaller so that in the future no one will have such problems? I mean clearing the search path.