From 27bbed2b906e456a2b4700cfbaab71e98e8c671f Mon Sep 17 00:00:00 2001 From: JarbasAi Date: Wed, 10 Jan 2024 00:53:02 +0000 Subject: [PATCH] refactor/drop audio service migration to ovos-media --- ovos_audio/__main__.py | 5 +- ovos_audio/playback.py | 12 +- ovos_audio/service.py | 12 +- ovos_audio/transformers.py | 8 +- ovos_audio/utils.py | 5 +- test/unittests/services/failing/__init__.py | 27 --- test/unittests/services/working/__init__.py | 57 ----- test/unittests/test_service.py | 227 -------------------- test/unittests/test_speech.py | 45 +--- test/unittests/test_utils.py | 11 +- 10 files changed, 39 insertions(+), 370 deletions(-) delete mode 100644 test/unittests/services/failing/__init__.py delete mode 100644 test/unittests/services/working/__init__.py delete mode 100644 test/unittests/test_service.py diff --git a/ovos_audio/__main__.py b/ovos_audio/__main__.py index 467e2c5..de8b476 100644 --- a/ovos_audio/__main__.py +++ b/ovos_audio/__main__.py @@ -10,12 +10,13 @@ # See the License for the specific language governing permissions and # limitations under the License. # -from ovos_audio.service import PlaybackService, on_ready, on_error, on_stopping -from ovos_config.locale import setup_locale from ovos_utils import wait_for_exit_signal from ovos_utils.log import init_service_logger from ovos_utils.process_utils import reset_sigint_handler +from ovos_audio.service import PlaybackService, on_ready, on_error, on_stopping +from ovos_config.locale import setup_locale + def main(ready_hook=on_ready, error_hook=on_error, stopping_hook=on_stopping, watchdog=lambda: None): diff --git a/ovos_audio/playback.py b/ovos_audio/playback.py index 1fff082..a203731 100644 --- a/ovos_audio/playback.py +++ b/ovos_audio/playback.py @@ -1,13 +1,15 @@ import random -from ovos_audio.transformers import TTSTransformersService -from ovos_bus_client.message import Message -from ovos_plugin_manager.templates.tts import TTS -from ovos_utils.log import LOG, log_deprecation -from ovos_utils.sound import play_audio from queue import Empty from threading import Thread, Event from time import time +from ovos_utils.log import LOG, log_deprecation +from ovos_utils.sound import play_audio + +from ovos_audio.transformers import TTSTransformersService +from ovos_bus_client.message import Message +from ovos_plugin_manager.templates.tts import TTS + class PlaybackThread(Thread): """Thread class for playing back tts audio and sending diff --git a/ovos_audio/service.py b/ovos_audio/service.py index 35672a6..a858cb8 100644 --- a/ovos_audio/service.py +++ b/ovos_audio/service.py @@ -8,12 +8,6 @@ from tempfile import gettempdir from threading import Thread, Lock -from ovos_bus_client import Message, MessageBusClient -from ovos_bus_client.session import SessionManager -from ovos_config.config import Configuration -from ovos_plugin_manager.g2p import get_g2p_lang_configs, get_g2p_supported_langs, get_g2p_module_configs -from ovos_plugin_manager.tts import TTS -from ovos_plugin_manager.tts import get_tts_supported_langs, get_tts_lang_configs, get_tts_module_configs from ovos_utils.file_utils import resolve_resource_file from ovos_utils.log import LOG from ovos_utils.metrics import Stopwatch @@ -24,6 +18,12 @@ from ovos_audio.transformers import DialogTransformersService from ovos_audio.tts import TTSFactory from ovos_audio.utils import report_timing, validate_message_context +from ovos_bus_client import Message, MessageBusClient +from ovos_bus_client.session import SessionManager +from ovos_config.config import Configuration +from ovos_plugin_manager.g2p import get_g2p_lang_configs, get_g2p_supported_langs, get_g2p_module_configs +from ovos_plugin_manager.tts import TTS +from ovos_plugin_manager.tts import get_tts_supported_langs, get_tts_lang_configs, get_tts_module_configs def on_ready(): diff --git a/ovos_audio/transformers.py b/ovos_audio/transformers.py index 3c745ef..3d9cd03 100644 --- a/ovos_audio/transformers.py +++ b/ovos_audio/transformers.py @@ -1,8 +1,10 @@ +from typing import Tuple + +from ovos_utils.log import LOG + from ovos_bus_client.session import Session, SessionManager from ovos_config import Configuration from ovos_plugin_manager.dialog_transformers import find_dialog_transformer_plugins, find_tts_transformer_plugins -from ovos_utils.log import LOG -from typing import Tuple class DialogTransformersService: @@ -20,7 +22,7 @@ def __init__(self, bus, config=None): def blacklisted_skills(self): # dialog should NEVER be rewritten if it comes from these skills return self.config.get("blacklisted_skills", - ["skill-ovos-icanhazdadjokes.openvoiceos"] # blacklist jokes by default + ["skill-ovos-icanhazdadjokes.openvoiceos"] # blacklist jokes by default ) def load_plugins(self): diff --git a/ovos_audio/utils.py b/ovos_audio/utils.py index 9d48d33..4eaad21 100644 --- a/ovos_audio/utils.py +++ b/ovos_audio/utils.py @@ -14,10 +14,11 @@ # import time +from ovos_utils.log import deprecated +from ovos_utils.signal import check_for_signal + from ovos_bus_client.send_func import send from ovos_config import Configuration -from ovos_utils.log import LOG, deprecated -from ovos_utils.signal import check_for_signal def validate_message_context(message, native_sources=None): diff --git a/test/unittests/services/failing/__init__.py b/test/unittests/services/failing/__init__.py deleted file mode 100644 index 68f366c..0000000 --- a/test/unittests/services/failing/__init__.py +++ /dev/null @@ -1,27 +0,0 @@ -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -from ovos_plugin_manager.templates.audio import AudioBackend - - -class FailingBackend(AudioBackend): - def __init__(self, config, emitter, name='Failing'): - raise Exception - - def supported_uris(self): - return ['file', 'http'] - - -def load_service(base_config, emitter): - instances = [FailingBackend(base_config, emitter)] - return instances diff --git a/test/unittests/services/working/__init__.py b/test/unittests/services/working/__init__.py deleted file mode 100644 index 1998bf9..0000000 --- a/test/unittests/services/working/__init__.py +++ /dev/null @@ -1,57 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -from unittest.mock import Mock -from ovos_plugin_manager.templates.audio import AudioBackend - - -class WorkingBackend(AudioBackend): - def __init__(self, config, bus, name='Working'): - super(WorkingBackend, self).__init__(config, bus) - - # Override instance methods with mocks - self.name = name - self.add_list = Mock() - self.clear_list = Mock() - self.play = Mock() - self.pause = Mock() - self.resume = Mock() - self.stop = Mock() - self.next = Mock() - self.previous = Mock() - self.lower_volume = Mock() - self.restore_volume = Mock() - self.seek_forward = Mock() - self.seek_backward = Mock() - self.track_info = Mock() - self.shutdown = Mock() - - def supported_uris(self): - return ['file', 'http'] - - def play(self): - pass - - def stop(self): - pass - - def add_list(self, playlist): - pass - - def clear_list(self): - pass - - -def load_service(base_config, bus): - instances = [WorkingBackend(base_config, bus)] - return instances diff --git a/test/unittests/test_service.py b/test/unittests/test_service.py deleted file mode 100644 index 6bd86e0..0000000 --- a/test/unittests/test_service.py +++ /dev/null @@ -1,227 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -from os.path import dirname, join, abspath -import unittest -import unittest.mock as mock - -from ovos_bus_client import Message -from ovos_audio.audio import AudioService - -from .services.working import WorkingBackend -"""Tests for Audioservice class""" - -seek_message = Message('seek', data={'seconds': 5}) - - -class MockEmitter: - def __init__(self): - self.reset() - - def once(self, event_type, function): - pass - - def on(self, event_type, function): - pass - - def emit(self, message): - self.types.append(message.msg_type) - self.results.append(message.data) - - def get_types(self): - return self.types - - def get_results(self): - return self.results - - def remove(self, *args, **kwargs): - pass - - def reset(self): - self.types = [] - self.results = [] - - -def setup_mock_backends(mock_load_services, emitter): - backend = WorkingBackend({}, emitter) - second_backend = WorkingBackend({}, emitter, 'second') - mock_load_services.return_value = [backend, second_backend] - return backend, second_backend - - -@unittest.skip("TODO - the mocks no longer apply, rewrite tests") -class TestService(unittest.TestCase): - emitter = MockEmitter() - service_path = abspath(join(dirname(__file__), 'services')) - - @mock.patch('ovos_audio.audio.find_audio_service_plugins') - def test_audio_backend_shutdown(self, mock_load_services): - """Test shutdown of audio backend.""" - backend, second_backend = setup_mock_backends(mock_load_services, - self.emitter) - service = AudioService(self.emitter) - - service.default = backend - - # Check that all backend shutdown methods are called on audioservice - # shutdown - service.shutdown() - self.assertTrue(backend.shutdown.called) - self.assertTrue(second_backend.shutdown.called) - - @mock.patch('ovos_audio.audio.load_plugins') - def test_audio_service_track_start(self, mock_load_services): - """Test start of new track messages.""" - backend, second_backend = setup_mock_backends(mock_load_services, - self.emitter) - service = AudioService(self.emitter) - service.load_services() - service.default = backend - - self.emitter.reset() - service.track_start('The universe song') - service.track_start(None) - self.assertEqual(self.emitter.types, ['mycroft.audio.playing_track', - 'mycroft.audio.queue_end']) - self.assertEqual(self.emitter.results, - [{'track': 'The universe song'}, {}]) - - service.shutdown() - - @mock.patch('ovos_audio.audio.load_plugins') - def test_audio_service_methods_not_playing(self, mock_load_services): - """Check that backend methods aren't called when stopped.""" - backend, second_backend = setup_mock_backends(mock_load_services, - self.emitter) - mock_load_services.return_value = [backend, second_backend] - - service = AudioService(self.emitter) - service.load_services() - - service.default = backend - - # Check that next and prev aren't called if there is nothing playing - service._next() - self.assertFalse(backend.next.called) - service._prev() - self.assertFalse(backend.previous.called) - service._pause() - self.assertFalse(backend.pause.called) - service._resume() - self.assertFalse(backend.resume.called) - service._seek_forward(seek_message) - self.assertFalse(backend.seek_forward.called) - service._seek_backward(seek_message) - self.assertFalse(backend.seek_backward.called) - service._lower_volume() - self.assertFalse(service.volume_is_low) - self.assertFalse(backend.lower_volume.called) - service._restore_volume() - self.assertFalse(backend.lower_volume.called) - - service.shutdown() - - @mock.patch('ovos_audio.audio.load_plugins') - def test_audio_service_methods_playing(self, mock_load_services): - """Check that backend methods are called during playback.""" - backend, second_backend = setup_mock_backends(mock_load_services, - self.emitter) - mock_load_services.return_value = [backend, second_backend] - - service = AudioService(self.emitter) - service.load_services() - - service.default = backend - - # Check that play doesn't play unsupported media uri type - m = Message('audio.service.play', data={'tracks': ['asdf://hello']}) - service._play(m) - self.assertFalse(backend.play.called) - - # Check that play plays supported media uri type - m = Message('audio.service.play', data={'tracks': ['http://hello']}) - service._play(m) - self.assertTrue(backend.play.called) - - # Check that next and prev are called if a backend is playing. - service._next() - self.assertTrue(backend.next.called) - service._prev() - self.assertTrue(backend.previous.called) - service._pause() - self.assertTrue(backend.pause.called) - service._resume() - self.assertTrue(backend.resume.called) - service._lower_volume() - self.assertTrue(service.volume_is_low) - self.assertTrue(backend.lower_volume.called) - service._restore_volume() - self.assertFalse(service.volume_is_low) - self.assertTrue(backend.lower_volume.called) - - # Check that play respects requested backends - m = Message('audio.service.play', - data={'tracks': [['http://hello', 'audio/mp3']], - 'utterance': 'using second'}) - service._play(m) - self.assertTrue(second_backend.play.called) - - service._seek_forward(seek_message) - second_backend.seek_forward.assert_called_with(5) - service._seek_backward(seek_message) - second_backend.seek_backward.assert_called_with(5) - - # Check that stop stops the active backend only if stop is received - # more than 1 second from last play. - second_backend.stop.reset_mock() - self.assertFalse(second_backend.stop.called) - service._stop() - self.assertFalse(second_backend.stop.called) - service.play_start_time -= 1 - service._stop() - self.assertTrue(second_backend.stop.called) - - service.shutdown() - - @mock.patch('ovos_audio.audio.load_plugins') - def test_audio_service_queue_methods(self, mock_load_services): - """Check that backend methods are called during playback.""" - backend, second_backend = setup_mock_backends(mock_load_services, - self.emitter) - mock_load_services.return_value = [backend, second_backend] - - service = AudioService(self.emitter) - service.load_services() - - service.default = backend - - # Check that play doesn't play unsupported media uri type - # Test queueing starts playback if stopped - backend.play.reset_mock() - backend.add_list.reset_mock() - m = Message('audio.service.queue', data={'tracks': ['http://hello']}) - service._queue(m) - backend.add_list.called_with(['http://hello']) - self.assertTrue(backend.play.called) - - # Test queuing doesn't call play if play is in progress - backend.play.reset_mock() - backend.add_list.reset_mock() - service._queue(m) - backend.add_list.called_with(['http://hello']) - self.assertFalse(backend.play.called) - - service.shutdown() - - -if __name__ == "__main__": - unittest.main() diff --git a/test/unittests/test_speech.py b/test/unittests/test_speech.py index 6c91279..5722aeb 100644 --- a/test/unittests/test_speech.py +++ b/test/unittests/test_speech.py @@ -15,11 +15,12 @@ import unittest.mock as mock from queue import Queue -from ovos_audio.service import PlaybackService -from ovos_config import Configuration from ovos_utils.messagebus import Message, FakeBus from ovos_utils.process_utils import ProcessState +from ovos_audio.service import PlaybackService +from ovos_config import Configuration + """Tests for speech dispatch service.""" tts_mock = mock.Mock() @@ -169,13 +170,13 @@ def test_queue(self, mock_TTS, tts_factory_mock, config_mock): speech.handle_queue_audio(msg) # TODO - fix res path and reenable - #f = f"{MYCROFT_ROOT_PATH}/mycroft/res/snd/start_listening.wav" - #msg = Message("", {"filename": f}) - #speech.handle_queue_audio(msg) - #data = mock_TTS.queue.get() - #self.assertEqual(data[0], "wav") - #self.assertEqual(data[1], f) - #self.assertEqual(data[-1], False) + # f = f"{MYCROFT_ROOT_PATH}/mycroft/res/snd/start_listening.wav" + # msg = Message("", {"filename": f}) + # speech.handle_queue_audio(msg) + # data = mock_TTS.queue.get() + # self.assertEqual(data[0], "wav") + # self.assertEqual(data[1], f) + # self.assertEqual(data[-1], False) @mock.patch('ovos_audio.service.report_timing') def test_speak(self, mock_timing, tts_factory_mock, config_mock): @@ -286,32 +287,6 @@ def rcvm(msg): speech.handle_opm_g2p_query(Message("opm.g2p.query")) - @mock.patch('ovos_audio.service.get_audio_service_configs') - def test_opm_audio(self, mock_get_configs, tts_factory_mock, config_mock): - setup_mocks(config_mock, tts_factory_mock) - - ocp = {"type": "ovos_common_play", "active": True} - p = {"type": "ovos_badass_player", "active": True} - - # per module configs, mocking same return val for all plugin inputs (!) - mock_get_configs.return_value = {"ocp": ocp, "badass": p} - - bus = FakeBus() - speech = PlaybackService(bus=bus) - - def rcvm(msg): - msg = json.loads(msg) - self.assertEqual(msg["type"], "opm.audio.query.response") - self.assertEqual(msg["data"]["plugins"], ["ocp", "badass"]) - self.assertEqual(msg["data"]["configs"], {"ocp": ocp, "badass": p}) - ocp["plugin_name"] = 'Ovos Common Play' - p["plugin_name"] = 'Ovos Badass Player' - self.assertEqual(msg["data"]["options"], [ocp, p]) - - bus.on("message", rcvm) - - speech.handle_opm_audio_query(Message("opm.audio.query")) - if __name__ == "__main__": unittest.main() diff --git a/test/unittests/test_utils.py b/test/unittests/test_utils.py index d7192b7..ed1344c 100644 --- a/test/unittests/test_utils.py +++ b/test/unittests/test_utils.py @@ -12,18 +12,17 @@ # import unittest import unittest.mock as mock - +from os.path import exists from shutil import rmtree from threading import Thread from time import sleep -from os.path import exists - -from ovos_utils.signal import create_signal, check_for_signal from ovos_utils.file_utils import get_temp_path +from ovos_utils.signal import create_signal, check_for_signal + from ovos_audio.utils import wait_while_speaking, is_speaking, stop_speaking -"""Tests for public audio service utils.""" +"""Tests for public audio service utils.""" done_waiting = False @@ -63,7 +62,7 @@ def test_stop_speaking(self, mock_send, mock_is_speaking): mock_is_speaking.return_value = True stop_speaking() mock_send.assert_called() - #mock_send.assert_called_with('mycroft.audio.speech.stop') + # mock_send.assert_called_with('mycroft.audio.speech.stop') @mock.patch('ovos_audio.utils.is_speaking') @mock.patch('ovos_audio.utils.send')