Added tts.mimic3 integration.

Closes: #226
This commit is contained in:
Fabio Manganiello 2022-09-30 10:50:28 +02:00
parent fef7aff245
commit ae226a5b01
Signed by untrusted user: blacklight
GPG key ID: D90FBA7F76362774
8 changed files with 229 additions and 0 deletions

View file

@ -9,6 +9,7 @@ reported only starting from v0.20.2.
### Added ### Added
- Added [Wallabag integration](https://git.platypush.tech/platypush/platypush/issues/224). - Added [Wallabag integration](https://git.platypush.tech/platypush/platypush/issues/224).
- Added [Mimic3 TTS integration](https://git.platypush.tech/platypush/platypush/issues/226).
## [0.23.6] - 2022-09-19 ## [0.23.6] - 2022-09-19

View file

@ -0,0 +1,5 @@
``tts.mimic3``
==============
.. automodule:: platypush.plugins.tts.mimic3
:members:

View file

@ -132,6 +132,7 @@ Plugins
platypush/plugins/trello.rst platypush/plugins/trello.rst
platypush/plugins/tts.rst platypush/plugins/tts.rst
platypush/plugins/tts.google.rst platypush/plugins/tts.google.rst
platypush/plugins/tts.mimic3.rst
platypush/plugins/tv.samsung.ws.rst platypush/plugins/tv.samsung.ws.rst
platypush/plugins/twilio.rst platypush/plugins/twilio.rst
platypush/plugins/udp.rst platypush/plugins/udp.rst

View file

@ -0,0 +1,46 @@
import requests
from urllib.parse import urljoin
from flask import abort, request, Blueprint
from platypush.backend.http.app import template_folder
mimic3 = Blueprint('mimic3', __name__, template_folder=template_folder)
# Declare routes list
__routes__ = [
mimic3,
]
@mimic3.route('/tts/mimic3/say', methods=['GET'])
def proxy_tts_request():
"""
This route is used to proxy the POST request to the Mimic3 TTS server
through a GET, so it can be easily processed as a URL through a media
plugin.
"""
required_args = {
'text',
'server_url',
'voice',
}
missing_args = required_args.difference(set(request.args.keys()))
if missing_args:
abort(400, f'Missing parameters: {missing_args}')
args = {arg: request.args[arg] for arg in required_args}
rs = requests.post(
urljoin(args['server_url'], '/api/tts'),
data=args['text'],
params={
'voice': args['voice'],
},
)
return rs.content
# vim:sw=4:ts=4:et:

View file

@ -0,0 +1,119 @@
import requests
from typing import Optional
from urllib.parse import urljoin, urlencode
from platypush.backend.http.app.utils import get_local_base_url
from platypush.context import get_backend
from platypush.plugins import action
from platypush.plugins.tts import TtsPlugin
from platypush.schemas.tts.mimic3 import Mimic3VoiceSchema
class TtsMimic3Plugin(TtsPlugin):
"""
TTS plugin that uses the `Mimic3 webserver
<https://github.com/MycroftAI/mimic3>`_ provided by `Mycroft
<https://mycroft.ai/>`_ as a text-to-speech engine.
The easiest way to deploy a Mimic3 instance is probably via Docker:
.. code-block:: bash
$ mkdir -p "$HOME/.local/share/mycroft/mimic3"
$ chmod a+rwx "$HOME/.local/share/mycroft/mimic3"
$ docker run --rm \
-p 59125:59125 \
-v "%h/.local/share/mycroft/mimic3:/home/mimic3/.local/share/mycroft/mimic3" \
'mycroftai/mimic3'
Requires:
* At least a *media plugin* (see
:class:`platypush.plugins.media.MediaPlugin`) enabled/configured -
used for speech playback.
* The ``http`` backend (:class:`platypush.backend.http.HttpBackend`)
enabled - used for proxying the API calls.
"""
def __init__(
self,
server_url: str,
voice: str = 'en_UK/apope_low',
media_plugin: Optional[str] = None,
player_args: Optional[dict] = None,
**kwargs
):
"""
:param server_url: Base URL of the web server that runs the Mimic3 engine.
:param voice: Default voice to be used (default: ``en_UK/apope_low``).
You can get a full list of the voices available on the server
through :method:`.voices`.
:param media_plugin: Media plugin to be used for audio playback. Supported:
- ``media.gstreamer``
- ``media.omxplayer``
- ``media.mplayer``
- ``media.mpv``
- ``media.vlc``
:param player_args: Optional arguments that should be passed to the player plugin's
:meth:`platypush.plugins.media.MediaPlugin.play` method.
"""
super().__init__(media_plugin=media_plugin, player_args=player_args, **kwargs)
self.server_url = server_url
self.voice = voice
@action
def say(
self,
text: str,
server_url: Optional[str] = None,
voice: Optional[str] = None,
player_args: Optional[dict] = None,
):
"""
Say some text.
:param text: Text to say.
:param server_url: Default ``server_url`` override.
:param voice: Default ``voice`` override.
:param player_args: Default ``player_args`` override.
"""
server_url = server_url or self.server_url
voice = voice or self.voice
player_args = player_args or self.player_args
http = get_backend('http')
assert http, 'http backend not configured'
assert self.media_plugin, 'No media plugin configured'
url = (
urljoin(get_local_base_url(), '/tts/mimic3/say')
+ '?'
+ urlencode(
{
'text': text,
'server_url': server_url,
'voice': voice,
}
)
)
self.media_plugin.play(url, **player_args)
@action
def voices(self, server_url: Optional[str] = None):
"""
List the voices available on the server.
:param server_url: Default ``server_url`` override.
:return: .. schema:: tts.mimi3.Mimic3VoiceSchema(many=True)
"""
server_url = server_url or self.server_url
rs = requests.get(urljoin(server_url, '/api/voices'))
rs.raise_for_status()
return Mimic3VoiceSchema().dump(rs.json(), many=True)
# vim:sw=4:ts=4:et:

View file

@ -0,0 +1,6 @@
manifest:
events: {}
install:
pip: []
package: platypush.plugins.tts.mimic3
type: plugin

View file

@ -0,0 +1,51 @@
from marshmallow import Schema, fields
class Mimic3Schema(Schema):
pass
class Mimic3VoiceSchema(Mimic3Schema):
key = fields.String(
required=True,
dump_only=True,
metadata={
'description': 'Unique voice ID',
'example': 'en_UK/apope_low',
},
)
language = fields.String(
required=True,
dump_only=True,
metadata={
'example': 'en_UK',
},
)
language_english = fields.String(
metadata={
'description': 'Name of the language (in English)',
}
)
language_native = fields.String(
metadata={
'description': 'Name of the language (in the native language)',
}
)
name = fields.String(
metadata={
'example': 'apope_low',
}
)
sample_text = fields.String(
metadata={
'example': 'Some text',
}
)
description = fields.String()
aliases = fields.List(fields.String)