Skip to content
This repository was archived by the owner on Oct 12, 2022. It is now read-only.

Commit 535c391

Browse files
Add Speech support
1 parent 86a7595 commit 535c391

File tree

7 files changed

+313
-19
lines changed

7 files changed

+313
-19
lines changed

openrobot/api_wrapper/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
from .error import *
44
from .results import *
55
from .translate import *
6+
from .speech import *
67

7-
from . import _async, _sync, translate, results, error
8+
from . import _async, _sync, translate, results, error, speech
89

910
__version__ = '0.2.1.1'

openrobot/api_wrapper/_async.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from .error import *
88
from .results import *
99
from .translate import Translate
10+
from .speech import Speech
1011

1112
try:
1213
from urllib.parse import quote_plus as quote
@@ -249,11 +250,16 @@ async def ocr(self, source: typing.Union[str, io.BytesIO]) -> OCRResult:
249250

250251
js = await self._request('POST', '/api/ocr', data=data)
251252
else:
252-
raise OpenRobotAPIError('source is not a string nor a io.BytesIO.')
253+
raise OpenRobotAPIError('source must be a URL or BytesIO.')
253254

254255
return OCRResult(js)
255256

256257
@property
257258
def translate(self) -> Translate:
258259
""":class:`Translate`: The Translate client."""
259-
return Translate(self, True)
260+
return Translate(self, True)
261+
262+
@property
263+
def speech(self) -> Speech:
264+
""":class:`Speech`: The Speech client."""
265+
return Speech(self, True)

openrobot/api_wrapper/_sync.py

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from .error import *
77
from .results import *
88
from .translate import Translate
9+
from .speech import Speech
910

1011
try:
1112
from urllib.parse import quote_plus as quote
@@ -178,7 +179,7 @@ def celebrity(self, url: str) -> typing.List[CelebrityResult]:
178179
js = self._request('GET', '/api/celebrity', params={'url': url})
179180
return [CelebrityResult(data) for data in js]
180181

181-
def ocr(self, *, url: str = None, fp: io.BytesIO = None) -> OCRResult:
182+
def ocr(self, source: typing.Union[str, io.BytesIO]) -> OCRResult:
182183
"""
183184
Reads text from a image.
184185
@@ -202,19 +203,21 @@ def ocr(self, *, url: str = None, fp: io.BytesIO = None) -> OCRResult:
202203
The OCR/Text found.
203204
"""
204205

205-
if not url and not fp:
206-
raise OpenRobotAPIError('url and fp kwargs cannot be empty.')
207-
elif url and fp:
208-
raise OpenRobotAPIError('url and fp cannot be both not enpty.')
209-
210-
if url:
211-
js = self._request('POST', '/api/ocr', params={'url': url})
206+
if isinstance(source, str):
207+
js = self._request('POST', '/api/ocr', params={'url': source})
208+
elif isinstance(source, io.BytesIO):
209+
js = self._request('POST', '/api/ocr', files={'upload_file': getattr(source, 'getvalue', lambda: source)()})
212210
else:
213-
js = self._request('POST', '/api/ocr', files={'upload_file': getattr(fp, 'getvalue', lambda: fp)()})
211+
raise OpenRobotAPIError('source must be a URL or BytesIO.')
214212

215213
return OCRResult(js)
216214

217215
@property
218216
def translate(self):
219217
""":class:`Translate`: The Translate client."""
220-
return Translate(self, False)
218+
return Translate(self, False)
219+
220+
@property
221+
def speech(self) -> Speech:
222+
""":class:`Speech`: The Speech client."""
223+
return Speech(self, False)

openrobot/api_wrapper/results.py

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,96 @@ def __init__(self, js):
275275
self.gender: typing.Optional[str] = js['Gender'] # TODO: Maybe make this an enum e.g Gender.female or Gender.male
276276
self.face: CelebrityFaceProperty = CelebrityFaceProperty(js['Face'])
277277

278+
class SpeechToTextResult(OpenRobotAPIBaseResult):
279+
"""
280+
The result of /api/speech/speech-to-text endpoint.
281+
282+
Attributes
283+
----------
284+
text: :class:`str`
285+
The text of the recognized speech.
286+
duration: Union[:class:`int`, :class:`float`]
287+
The time taken to recognize the text in the speech in
288+
seconds.
289+
"""
290+
291+
def __init__(self, js):
292+
super().__init__(js)
293+
294+
self.text: str = js['text']
295+
self.duration: typing.Union[int, float] = js['duration']
296+
297+
class TextToSpeechResult(OpenRobotAPIBaseResult):
298+
"""
299+
The result of /api/speech/text-to-speech endpoint.
300+
301+
Attributes
302+
----------
303+
url: :class:`str`
304+
The URL of the speech
305+
"""
306+
307+
def __init__(self, js):
308+
super().__init__(js)
309+
310+
self.url: str = js['url']
311+
312+
class TextToSpeechSupportLanguage:
313+
"""
314+
The languages supported by Text To Speech.
315+
316+
Attributes
317+
----------
318+
code: :class:`str`
319+
The language code.
320+
name: :class:`str`
321+
The human-readable language name.
322+
"""
323+
324+
def __init__(self, js):
325+
self.code: str = js.get('code')
326+
self.name: str = js.get('name')
327+
328+
class TextToSpeechSupportVoice:
329+
"""
330+
The supported voices for Text To Speech.
331+
332+
Attributes
333+
----------
334+
gender: :class:`str`
335+
The voice's gender.
336+
id: :class:`str`
337+
The Voice ID.
338+
language: :class:`TextToSpeechSupportLanguage`
339+
The language of the voice.
340+
name: :class:`str`
341+
The Voice's name.
342+
"""
343+
344+
def __init__(self, js):
345+
self.gender: str = js.get('Gender')
346+
self.id: str = js.get('Id')
347+
self.language: TextToSpeechSupportLanguage = TextToSpeechSupportLanguage({'code': js.get('LanguageCode'),'name': js.get('LanguageName')})
348+
self.name: str = js.get('Name')
349+
350+
class TextToSpeechSupportResult(OpenRobotAPIBaseResult):
351+
"""
352+
The result of /api/speech/text-to-speech/support endpoint.
353+
354+
Attributes
355+
----------
356+
languages: List[:class:`TextToSpeechSupportLanguage`]
357+
The languages supported by Text To Speech.
358+
voices: List[:class:`TextToSpeechSupportVoice`]
359+
The supported voices for Text To Speech.
360+
"""
361+
362+
def __init__(self, js):
363+
super().__init__(js)
364+
365+
self.languages: typing.List[TextToSpeechSupportLanguage] = [TextToSpeechSupportLanguage(language) for language in js['languages']]
366+
self.voices: typing.List[TextToSpeechSupportVoice] = [TextToSpeechSupportVoice(voice) for voice in js['voices']]
367+
278368
class OCRResult(OpenRobotAPIBaseResult):
279369
"""
280370
The result of /api/ocr endpoint.

openrobot/api_wrapper/speech.py

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
import typing
2+
import io
3+
import aiohttp
4+
from .results import SpeechToTextResult, TextToSpeechResult, TextToSpeechSupportResult
5+
from .error import OpenRobotAPIError
6+
7+
class Speech:
8+
"""
9+
The speech client.
10+
"""
11+
12+
def __init__(self, client, is_async: bool):
13+
self._client = client
14+
15+
self._is_async = is_async
16+
17+
def speech_to_text(self, source: typing.Union[str, io.BytesIO], language_code: str) -> typing.Union[typing.Coroutine[None, None, SpeechToTextResult], SpeechToTextResult]:
18+
"""|maybecoro|
19+
20+
Speech to text.
21+
22+
This function is a coroutine if the client is an
23+
:class:`AsyncClient` object, else it would be a synchronous method.
24+
25+
Parameters
26+
----------
27+
source: Union[:class:`str`, :class:`io.BytesIO`]
28+
The source of the speech. This can be either a URL or a
29+
:class:`io.BytesIO` object.
30+
language_code: :class:`str`
31+
The language code of the speech.
32+
voice_id: :class:`str`
33+
The voice id of the speech.
34+
engine: :class:`str`
35+
The engine of the speech.
36+
37+
Raises
38+
------
39+
:exc:`Forbidden`
40+
API Returned a 403 HTTP Status Code.
41+
:exc:`BadRequest`
42+
API Returned a 400 HTTP Status Code.
43+
:exc:`InternalServerError`
44+
API Returned a 500 HTTP Status Code.
45+
46+
Returns
47+
-------
48+
Union[Coroutine[None, None, :class:`SpeechToTextResult`], :class:`SpeechToTextResult`]
49+
The result of the text to speech.
50+
"""
51+
52+
if self._is_async:
53+
async def _text_to_speech() -> TextToSpeechResult:
54+
if isinstance(source, str):
55+
js = await self._client.request('POST', '/api/speech/speech-to-text', params={'url': source})
56+
elif isinstance(source, io.BytesIO):
57+
data = aiohttp.FormData()
58+
data.add_field('file', source)
59+
60+
js = await self._client.request('POST', '/api/speech/speech-to-text', data=data)
61+
else:
62+
raise OpenRobotAPIError('source must be a URL or BytesIO.')
63+
64+
return SpeechToTextResult(js)
65+
66+
return _text_to_speech()
67+
else:
68+
if isinstance(source, str):
69+
js = self._client.request('POST', '/api/speech/speech-to-text', params={'url': source})
70+
elif isinstance(source, io.BytesIO):
71+
data = aiohttp.FormData()
72+
data.add_field('file', source)
73+
74+
js = self._client.request('POST', '/api/speech/speech-to-text', files={'upload_file': getattr(source, 'getvalue', lambda: source)()})
75+
else:
76+
raise OpenRobotAPIError('source must be a URL or BytesIO.')
77+
78+
return SpeechToTextResult(js)
79+
80+
def speech_to_text_support(self) -> typing.Union[typing.Coroutine[None, None, typing.Dict[str, typing.Any]], typing.Dict[str, typing.Any]]:
81+
"""|maybecoro|
82+
83+
Returns the supported details for Speech To Text.
84+
85+
This function is a coroutine if the client is an
86+
:class:`AsyncClient` object, else it would be a synchronous method.
87+
88+
Raises
89+
------
90+
:exc:`Forbidden`
91+
API Returned a 403 HTTP Status Code.
92+
:exc:`BadRequest`
93+
API Returned a 400 HTTP Status Code.
94+
:exc:`InternalServerError`
95+
API Returned a 500 HTTP Status Code.
96+
97+
Returns
98+
-------
99+
Union[Coroutine[None, None, typing.Dict[:class:`str`, :class:`typing.Any`]], typing.Dict[:class:`str`, :class:`typing.Any`]]
100+
The supported details for Speech To Text.
101+
"""
102+
103+
if self._is_async:
104+
async def _speech_to_text_support() -> typing.Dict[str, typing.Any]:
105+
js = await self._client.request('GET', '/api/speech/speech-to-text/supports')
106+
107+
return js
108+
109+
return _speech_to_text_support()
110+
else:
111+
js = self._client.request('GET', '/api/speech/speech-to-text/supports')
112+
113+
return js
114+
115+
def text_to_speech(self, text: str, language_code: str, voice_id: str, *, engine: str = 'standard') -> typing.Union[typing.Coroutine[None, None, TextToSpeechResult], TextToSpeechResult]:
116+
"""|maybecoro|
117+
118+
Text to speech.
119+
120+
This function is a coroutine if the client is an
121+
:class:`AsyncClient` object, else it would be a synchronous method.
122+
123+
Parameters
124+
----------
125+
text: :class:`str`
126+
The text to be speeched.
127+
language_code: :class:`str`
128+
The language code of the speech.
129+
voice_id: :class:`str`
130+
The voice id of the speech.
131+
engine: :class:`str`
132+
The engine of the speech.
133+
134+
Raises
135+
------
136+
:exc:`Forbidden`
137+
API Returned a 403 HTTP Status Code.
138+
:exc:`BadRequest`
139+
API Returned a 400 HTTP Status Code.
140+
:exc:`InternalServerError`
141+
API Returned a 500 HTTP Status Code.
142+
143+
Returns
144+
-------
145+
Union[Coroutine[None, None, :class:`TextToSpeechResult`], :class:`TextToSpeechResult`]
146+
The result of the text to speech.
147+
"""
148+
149+
if self._is_async:
150+
async def _text_to_speech() -> TextToSpeechResult:
151+
js = await self._client.request('GET', '/api/speech/text-to-speech', params={'text': text, 'language_code': language_code, 'voice_id': voice_id, 'engine': engine})
152+
153+
return TextToSpeechResult(js)
154+
155+
return _text_to_speech()
156+
else:
157+
js = self._client.request('GET', '/api/speech/text-to-speech', params={'text': text, 'language_code': language_code, 'voice_id': voice_id, 'engine': engine})
158+
159+
return TextToSpeechResult(js)
160+
161+
def text_to_speech_support(self) -> typing.Union[typing.Coroutine[None, None, TextToSpeechSupportResult], TextToSpeechSupportResult]:
162+
"""|maybecoro|
163+
164+
Returns the supported details for Text To Speech.
165+
166+
This function is a coroutine if the client is an
167+
:class:`AsyncClient` object, else it would be a synchronous method.
168+
169+
Raises
170+
------
171+
:exc:`Forbidden`
172+
API Returned a 403 HTTP Status Code.
173+
:exc:`BadRequest`
174+
API Returned a 400 HTTP Status Code.
175+
:exc:`InternalServerError`
176+
API Returned a 500 HTTP Status Code.
177+
178+
Returns
179+
-------
180+
Union[Coroutine[None, None, :class:`TextToSpeechSupportResult`], :class:`TextToSpeechSupportResult`]
181+
The supported details for Text To Speech.
182+
"""
183+
184+
if self._is_async:
185+
async def _text_to_speech_support() -> TextToSpeechSupportResult:
186+
js = await self._client.request('GET', '/api/speech/text-to-speech/supports')
187+
188+
return TextToSpeechSupportResult(js)
189+
190+
return _text_to_speech_support()
191+
else:
192+
js = self._client.request('GET', '/api/speech/text-to-speech/supports')
193+
194+
return TextToSpeechSupportResult(js)

0 commit comments

Comments
 (0)