Skip to content

Commit 34e0474

Browse files
committed
First commit
0 parents  commit 34e0474

File tree

15 files changed

+575
-0
lines changed

15 files changed

+575
-0
lines changed

.gitignore

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
### pawelad ###
2+
.idea/
3+
4+
### Python ###
5+
# Byte-compiled / optimized / DLL files
6+
__pycache__/
7+
*.py[cod]
8+
*$py.class
9+
10+
# C extensions
11+
*.so
12+
13+
# Distribution / packaging
14+
.Python
15+
env/
16+
build/
17+
develop-eggs/
18+
dist/
19+
downloads/
20+
eggs/
21+
.eggs/
22+
lib/
23+
lib64/
24+
parts/
25+
sdist/
26+
var/
27+
*.egg-info/
28+
.installed.cfg
29+
*.egg
30+
31+
# PyInstaller
32+
# Usually these files are written by a python script from a template
33+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
34+
*.manifest
35+
*.spec
36+
37+
# Installer logs
38+
pip-log.txt
39+
pip-delete-this-directory.txt
40+
41+
# Unit test / coverage reports
42+
htmlcov/
43+
.tox/
44+
.coverage
45+
.coverage.*
46+
.cache
47+
nosetests.xml
48+
coverage.xml
49+
*,cover
50+
.hypothesis/
51+
52+
# Translations
53+
*.mo
54+
*.pot
55+
56+
# Django stuff:
57+
*.log
58+
local_settings.py
59+
60+
# Flask stuff:
61+
instance/
62+
.webassets-cache
63+
64+
# Scrapy stuff:
65+
.scrapy
66+
67+
# Sphinx documentation
68+
docs/_build/
69+
70+
# PyBuilder
71+
target/
72+
73+
# IPython Notebook
74+
.ipynb_checkpoints
75+
76+
# pyenv
77+
.python-version
78+
79+
# celery beat schedule file
80+
celerybeat-schedule
81+
82+
# dotenv
83+
.env
84+
85+
# virtualenv
86+
venv/
87+
ENV/
88+
89+
# Spyder project settings
90+
.spyderproject
91+
92+
# Rope project settings
93+
.ropeproject
94+
95+
96+
### PyCharm ###
97+
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
98+
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
99+
100+
# User-specific stuff:
101+
.idea/workspace.xml
102+
.idea/tasks.xml
103+
.idea/dictionaries
104+
.idea/vcs.xml
105+
.idea/jsLibraryMappings.xml
106+
107+
# Sensitive or high-churn files:
108+
.idea/dataSources.ids
109+
.idea/dataSources.xml
110+
.idea/dataSources.local.xml
111+
.idea/sqlDataSources.xml
112+
.idea/dynamic.xml
113+
.idea/uiDesigner.xml
114+
115+
# Gradle:
116+
.idea/gradle.xml
117+
.idea/libraries
118+
119+
# Mongo Explorer plugin:
120+
.idea/mongoSettings.xml
121+
122+
## File-based project format:
123+
*.iws
124+
125+
## Plugin-specific files:
126+
127+
# IntelliJ
128+
/out/
129+
130+
# mpeltonen/sbt-idea plugin
131+
.idea_modules/
132+
133+
# JIRA plugin
134+
atlassian-ide-plugin.xml
135+
136+
# Crashlytics plugin (for Android Studio and IntelliJ)
137+
com_crashlytics_export_strings.xml
138+
crashlytics.properties
139+
crashlytics-build.properties
140+
fabric.properties
141+
142+
### PyCharm Patch ###
143+
# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
144+
145+
# *.iml
146+
# modules.xml

.travis.yml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
language: python
2+
3+
matrix:
4+
include:
5+
- os: linux
6+
python: 3.4
7+
env: TOXENV=py34
8+
9+
install:
10+
- pip install tox --use-mirrors
11+
12+
script:
13+
- tox

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) 2016 Pythonity
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

MANIFEST.in

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
include *.md
2+
include *.txt
3+
include LICENSE
4+
include tox.ini
5+
recursive-include ivona_api *.mp3
6+
recursive-include ivona_api *.py
7+
recursive-include requirements *.txt

README.md

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# python-ivona-api
2+
Python (3) library that helps you connect to Amazon's [IVONA][ivona]
3+
Speech Cloud from within your Python project. You need its
4+
[access keys][ivona keys] to use it.
5+
6+
It currently only implements `CreateSpeech` and `ListVoices` endpoints,
7+
as they should cover the vast majority of use cases. Lexicons endpoints
8+
may be added in the future.
9+
10+
If you're looking for out-of-the-box solution, have a look at
11+
[ivona-speak][ivona speak github] - it's a script that uses this very
12+
library and lets you use its functionality directly from your shell.
13+
14+
Packages required for running and testing are listed in `requirements`
15+
directory.
16+
17+
## Installation
18+
With `pip`:
19+
```
20+
$ pip3 install ivona_api
21+
```
22+
23+
## API
24+
There's no documentation as of now, but the code is commented and
25+
*should* be pretty straightforward to use.
26+
27+
But feel free to ask [me](mailto:[email protected]) if anything
28+
is unclear.
29+
30+
## Tests
31+
Package was tested with `pytest` and `tox` on Python 3.4
32+
(see `tox.ini`).
33+
34+
To run tests yourself you need to set environment variables with secret
35+
and access keys before running `tox` inside the repository:
36+
```
37+
export IVONA_ACCESS_KEY="YOUR_ACTUAL_ACCESS_KEY"
38+
export IVONA_SECRET_KEY="YOUR_ACTUAL_SECRET_KEY"
39+
```
40+
41+
## Contributions
42+
Package source code is available at [GitHub][ivona api github].
43+
44+
Feel free to use, ask, fork, star, report bugs, fix them, suggest
45+
enhancements and point out any mistakes.
46+
47+
## Authors
48+
Developed and maintained by [Pythonity][pythonity].
49+
50+
Written by [Paweł Adamczak][pawelad].
51+
52+
[ivona]: https://www.ivona.com/
53+
[ivona keys]: http://developer.ivona.com/en/speechcloud/introduction.html#Credentials
54+
[ivona speak github]: https://github.com/Pythonity/ivona-speak
55+
[ivona api github]: https://github.com/Pythonity/python-ivona-api
56+
[pythonity]: http://pythonity.com/
57+
[pawelad]: https://github.com/pawelad

ivona_api/__init__.py

Whitespace-only changes.

ivona_api/ivona_api.py

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
from urllib.parse import urljoin
2+
3+
import requests
4+
from requests_aws4auth import AWS4Auth
5+
6+
7+
IVONA_REGION_ENDPOINTS = {
8+
'eu-west-1': 'https://tts.eu-west-1.ivonacloud.com', # EU, Dublin
9+
'us-east-1': 'https://tts.us-east-1.ivonacloud.com', # US East, N. Virginia
10+
'us-west-2': 'https://tts.us-west-2.ivonacloud.com', # US West, Oregon
11+
}
12+
13+
14+
class IvonaAPI:
15+
"""
16+
Base class that for accessing Ivona web API.
17+
Currently implements 'CreateSpeech' and 'ListVoices' endpoints, without
18+
support for lexicon actions.
19+
"""
20+
ALLOWED_CODECS = ['ogg', 'mp3', 'mp4']
21+
22+
@property
23+
def region(self):
24+
return self._region
25+
26+
@region.setter
27+
def region(self, value):
28+
if value not in IVONA_REGION_ENDPOINTS.keys():
29+
raise ValueError("Incorrect region: {}".format(value))
30+
self._region = value
31+
32+
@property
33+
def codec(self):
34+
return self._codec
35+
36+
@codec.setter
37+
def codec(self, value):
38+
if value.lower() not in self.ALLOWED_CODECS:
39+
raise ValueError("Incorrect codec - only ogg, mp3 and mp4 allowed.")
40+
self._codec = value
41+
42+
def __init__(self, access_key, secret_key, voice_name='Salli',
43+
language='en-US', codec='mp3', region='eu-west-1'):
44+
self.region = region
45+
self._aws4auth = AWS4Auth(access_key, secret_key, region, 'tts')
46+
47+
self.available_voices = self.get_available_voices()
48+
self.set_voice(voice_name, language)
49+
50+
self.codec = codec.lower()
51+
52+
# Below are listed additional parameters that have default values,
53+
# but are initialized here so that they can be changed on instance
54+
# basis if need be
55+
56+
# ['x-slow', 'slow', 'medium', 'fast', 'x-fast', 'default']
57+
self.rate = 'default'
58+
# ['silent', 'x-soft', 'soft', 'medium', 'loud', 'x-loud', 'default']
59+
self.volume = 'default'
60+
# Integer in the range of 0-3000 (in milliseconds)
61+
self.sentence_break = 400
62+
# Integer in the range of 0-5000 (in milliseconds)
63+
self.paragraph_break = 650
64+
65+
def _check_if_voice_exists(self, voice_name, language):
66+
if not any([v['Name'] == voice_name and v['Language'] == language
67+
for v in self.available_voices]):
68+
return False
69+
else:
70+
return True
71+
72+
def set_voice(self, voice_name, language):
73+
"""Make sure that passed voice name and language pair exists"""
74+
if not self._check_if_voice_exists(voice_name, language):
75+
raise ValueError("Incorrect voice name-language pair.")
76+
self._voice_name = voice_name
77+
self._language = language
78+
79+
def get_available_voices(self):
80+
"""
81+
Returns a list of available voices, via 'ListVoices' endpoint
82+
http://developer.ivona.com/en/speechcloud/actions.html#ListVoices
83+
"""
84+
endpoint = urljoin(
85+
IVONA_REGION_ENDPOINTS[self.region], 'ListVoices',
86+
)
87+
r = requests.get(endpoint, auth=self._aws4auth)
88+
89+
if 'x-amzn-ErrorType' in r.headers:
90+
raise IvonaAPIException(r.headers['x-amzn-ErrorType'])
91+
92+
return r.json()['Voices']
93+
94+
def text_to_speech(self, text, file, voice_name=None, language=None):
95+
"""
96+
Saves given text synthesized audio file, via 'CreateSpeech' endpoint
97+
http://developer.ivona.com/en/speechcloud/actions.html#CreateSpeech
98+
"""
99+
if voice_name or language:
100+
if not self._check_if_voice_exists(voice_name, language):
101+
raise ValueError("Incorrect voice name-language pair.")
102+
else:
103+
voice_name = self._voice_name
104+
language = self._language
105+
106+
endpoint = urljoin(
107+
IVONA_REGION_ENDPOINTS[self.region], 'CreateSpeech',
108+
)
109+
110+
data = {
111+
'Input': {
112+
'Data': str(text),
113+
},
114+
'OutputFormat': {
115+
'Codec': self.codec.upper(),
116+
},
117+
'Parameters': {
118+
'Rate': self.rate,
119+
'Volume': self.volume,
120+
'SentenceBreak': self.sentence_break,
121+
'ParagraphBreak': self.paragraph_break,
122+
},
123+
'Voice': {
124+
'Name': voice_name,
125+
'Language': language,
126+
},
127+
}
128+
129+
r = requests.post(endpoint, auth=self._aws4auth, json=data)
130+
131+
if 'x-amzn-ErrorType' in r.headers:
132+
raise IvonaAPIException(r.headers['x-amzn-ErrorType'])
133+
134+
file.write(r.content)
135+
136+
return True
137+
138+
139+
class IvonaAPIException(Exception):
140+
"""Base IvonaAPI exception"""
141+
pass
5.4 KB
Binary file not shown.
6.32 KB
Binary file not shown.

0 commit comments

Comments
 (0)