Repository: MycroftAI/mycroft-core

Issue:
tts discarding path
the latest cache refactor introduced an issue in TTS handling, 

a file path is requested, but TTS engines return the path where the file was actually created, mycroft is discarding this path and using the originally requested path instead, meaning the file might not exist when playback starts

issue introduced in https://github.com/MycroftAI/mycroft-core/pull/2853

proposed fix: https://github.com/HelloChatterbox/HolmesV/pull/60
tts discarding path
the latest cache refactor introduced an issue in TTS handling, 

a file path is requested, but TTS engines return the path where the file was actually created, mycroft is discarding this path and using the originally requested path instead, meaning the file might not exist when playback starts

issue introduced in https://github.com/MycroftAI/mycroft-core/pull/2853

proposed fix: https://github.com/HelloChatterbox/HolmesV/pull/60


Discussion:
Yeah I can see the argument that the `get_tts()` method returns a file path, so why aren't we using it. However I'm also wondering why the TTS engine is writing the files to a different path than was requested?

This feels like it was an unintentional breaking change as we previously used the return path. Perhaps we revert to use the returned path, but deprecate it and switch to the requested path in the next major release. Unless there's a reason why each specific TTS engine needs to define where the files reside?
Yeah I can see the argument that the `get_tts()` method returns a file path, so why aren't we using it. However I'm also wondering why the TTS engine is writing the files to a different path than was requested?

This feels like it was an unintentional breaking change as we previously used the return path. Perhaps we revert to use the returned path, but deprecate it and switch to the requested path in the next major release. Unless there's a reason why each specific TTS engine needs to define where the files reside?

Resolving patch:
diff --git a/mycroft/tts/tts.py b/mycroft/tts/tts.py
--- a/mycroft/tts/tts.py
+++ b/mycroft/tts/tts.py
@@ -19,6 +19,7 @@
 from abc import ABCMeta, abstractmethod
 from threading import Thread
 from time import time
+from warnings import warn
 
 import os.path
 from os.path import dirname, exists, isdir, join
@@ -370,7 +371,18 @@ def _execute(self, sentence, ident, listen):
                 #  of the TTS cache.  But this requires changing the public
                 #  API of the get_tts method in each engine.
                 audio_file = self.cache.define_audio_file(sentence_hash)
-                _, phonemes = self.get_tts(sentence, str(audio_file.path))
+                # TODO 21.08: remove mutation of audio_file.path.
+                returned_file, phonemes = self.get_tts(
+                    sentence, str(audio_file.path))
+                if returned_file.path != audio_file.path:
+                    warn(
+                        DeprecationWarning(
+                            f"{self.tts_name} is saving files "
+                            "to a different path than requested. If you are "
+                            "the maintainer of this plugin, please adhere to "
+                            "the file path argument provided. Modified paths "
+                            "will be ignored in a future release."))
+                    audio_file = returned_file
                 if phonemes:
                     phoneme_file = self.cache.define_phoneme_file(
                         sentence_hash


Test patch:
diff --git a/test/unittests/tts/test_tts.py b/test/unittests/tts/test_tts.py
--- a/test/unittests/tts/test_tts.py
+++ b/test/unittests/tts/test_tts.py
@@ -96,11 +96,14 @@ def test_execute(self, mock_playback_thread):
         with mock.patch('mycroft.tts.tts.open') as mock_open:
             tts.cache.temporary_cache_dir = Path('/tmp/dummy')
             tts.execute('Oh no, not again', 42)
-        self.assertTrue(tts.get_tts.called)
+        tts.get_tts.assert_called_with(
+            'Oh no, not again',
+            '/tmp/dummy/8da7f22aeb16bc3846ad07b644d59359.wav'
+        )
         tts.queue.put.assert_called_with(
             (
                 'wav',
-                '/tmp/dummy/8da7f22aeb16bc3846ad07b644d59359.wav',
+                str(mock_audio.path),
                 mock_viseme,
                 42,
                 False