Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: speechify.voices.list() #13

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
_site/

.DS_Store
**/__pycache__/**
Empty file added speechify-python-sdk/.env
Empty file.
9 changes: 9 additions & 0 deletions speechify-python-sdk/LICENSE.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
MIT License

Copyright (c) 2024-present Irwansyah <[email protected]>

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 changes: 21 additions & 0 deletions speechify-python-sdk/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Speechify Python SDK

[![PyPI - Version](https://img.shields.io/pypi/v/speechify-python-sdk.svg)](https://pypi.org/project/speechify-python-sdk)
[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/speechify-python-sdk.svg)](https://pypi.org/project/speechify-python-sdk)

-----

## Table of Contents

- [Installation](#installation)
- [License](#license)

## Installation

```console
pip install speechify-python-sdk
```

## License

`speechify-python-sdk` is distributed under the terms of the [MIT](https://spdx.org/licenses/MIT.html) license.
63 changes: 63 additions & 0 deletions speechify-python-sdk/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
[build-system]
requires = ["hatchling", "hatch-requirements-txt"]
build-backend = "hatchling.build"

[project]
name = "speechify-python-sdk"
dynamic = ["dependencies", "version"]
description = 'Python SDK for Speechify API'
readme = "README.md"
requires-python = ">=3.8"
license = "MIT"
keywords = []
authors = [
{ name = "Irwansyah", email = "[email protected]" },
]
classifiers = [
"Development Status :: 4 - Beta",
"Programming Language :: Python",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: Python :: Implementation :: PyPy",
]

[project.urls]
Documentation = "https://github.com/Irwansyah/speechify-python-sdk#readme"
Issues = "https://github.com/Irwansyah/speechify-python-sdk/issues"
Source = "https://github.com/Irwansyah/speechify-python-sdk"

[tool.hatch.metadata.hooks.requirements_txt]
files = ["requirements.txt"]

[tool.hatch.version]
path = "src/speechify_python_sdk/__about__.py"

[tool.hatch.envs.types]
extra-dependencies = [
"mypy>=1.0.0",
]
[tool.hatch.envs.types.scripts]
check = "mypy --install-types --non-interactive {args:src/speechify_python_sdk tests}"

[tool.coverage.run]
source_pkgs = ["speechify_python_sdk", "tests"]
branch = true
parallel = true
omit = [
"src/speechify_python_sdk/__about__.py",
]

[tool.coverage.paths]
speechify_python_sdk = ["src/speechify_python_sdk", "*/speechify-python-sdk/src/speechify_python_sdk"]
tests = ["tests", "*/speechify-python-sdk/tests"]

[tool.coverage.report]
exclude_lines = [
"no cov",
"if __name__ == .__main__.:",
"if TYPE_CHECKING:",
]
4 changes: 4 additions & 0 deletions speechify-python-sdk/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pydantic==2.10.4
pytest==8.3.4
python-dotenv==1.0.1
Requests==2.32.3
4 changes: 4 additions & 0 deletions speechify-python-sdk/src/speechify_python_sdk/__about__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# SPDX-FileCopyrightText: 2024-present Irwansyah <[email protected]>
#
# SPDX-License-Identifier: MIT
__version__ = "0.0.1"
3 changes: 3 additions & 0 deletions speechify-python-sdk/src/speechify_python_sdk/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# SPDX-FileCopyrightText: 2024-present Irwansyah <[email protected]>
#
# SPDX-License-Identifier: MIT
27 changes: 27 additions & 0 deletions speechify-python-sdk/src/speechify_python_sdk/lib/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import os

from speechify_python_sdk import __about__
from speechify_python_sdk.lib.speechify_requests import SpeechifyRequests


class Config:
SPEECHIFY_API_KEY = os.environ.get("SPEECHIFY_API_KEY", "")

@classmethod
def sdk_version(cls):
return __about__.__version__

@classmethod
def __setup_requests_base_config__(cls):
SpeechifyRequests.api_key = Config.SPEECHIFY_API_KEY
SpeechifyRequests.sdk_version = Config.sdk_version()

@classmethod
def use_prod(cls):
cls.__setup_requests_base_config__()
SpeechifyRequests.use_prod()

@classmethod
def use_dev(cls):
cls.__setup_requests_base_config__()
SpeechifyRequests.use_dev
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from typing import Generator

from speechify_python_sdk.models.generate_audio_request import GenerateAudioRequest
from speechify_python_sdk.models.generate_audio_response import GenerateAudioResponse


class SpeechifyTTS:

def text_to_speech(self, request: GenerateAudioRequest) -> GenerateAudioResponse:
"""
Gets the speech data for the given input

Arguments:
request: The request parameters

Returns:
A GenerateAudioResponse

Raises:
HTTPError:

- 400 Invalid request parameters
- 402 Insufficient credits
- 403 Access Denied
- 500 Internal Server Error
"""
pass

def stream(self, request: GenerateAudioRequest):
"""
Gets the stream speech for the given input

Returns:
Base64 encoded of the the audio data
"""
pass
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from speechify_python_sdk.models.generate_oauth_token_request import (
GenerateOAuthTokenRequest,
)


class SpeechifyAuth:
def generate_oauth_token(self, request: GenerateOAuthTokenRequest):
"""
Create a new API token for the logged in user

Arguments:
request: The request parameters

Returns:
A GenerateOAuthTokenResponse
"""
pass
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import requests


class SpeechifyRequests:
"""
Responsible to do all HTTP requests to Speechify REST API
"""

api_url = "https://api.sws.speechify.com"
api_key = ""
sdk_version = ""

@classmethod
def use_prod(cls):
cls.api_url = "https://api.sws.speechify.com"

@classmethod
def use_dev(cls):
cls.api_url = "https://api.sws.speechify.dev"

@classmethod
def set_api_key(cls, new_api_key: str):
cls.api_key = new_api_key

@classmethod
def set_config(cls, api_key: str, sdk_version: str, is_use_prod: bool):
cls.api_key = api_key
cls.sdk_version = sdk_version
cls.use_prod() if is_use_prod else cls.use_dev()

def __get_headers__(self):
headers = {
"Authorization": f"Bearer {SpeechifyRequests.api_key}",
"X-Speechify-SDK": "python",
"X-Speechify-SDK-Version": SpeechifyRequests.sdk_version,
}

return headers

def fetch_json(self, path: str):
headers = self.__get_headers__()

response = requests.get(f"{SpeechifyRequests.api_url}{path}", headers=headers)
response.raise_for_status()

return response.json()
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
from typing import List

from speechify_python_sdk.lib.speechify_requests import SpeechifyRequests
from speechify_python_sdk.models.voices_response import VoicesResponse


class SpeechifyVoices:

def __init__(self, requests: SpeechifyRequests):
self.requests = requests

def list(self) -> List[VoicesResponse]:
"""
Gets the list of voices available for the user

Returns:
An empty array if no voices found or
list of VoicesResponse

Raises:
- 500 Internal Server Error

"""
data = self.requests.fetch_json("/v1/voices")

return list(
map(
lambda item: VoicesResponse.model_validate(item),
data,
)
)

def create(self):
"""
Create a personal (cloned) voice for the user

Raises:
- 400 Invalid request params
- 402 Current billing plan does not have access to voice cloning
- 500 Internal Server Error

Returns:
A CreateVoiceResponse

"""
pass

def delete(self):
pass
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from pydantic import BaseModel


class CreateVoiceRequest(BaseModel):
pass
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from typing import List
from pydantic import BaseModel

from speechify_python_sdk.models.voice_model import VoiceModel


class CreateVoiceResponse(BaseModel):
id: str
display_name: str
type: str
models: List[VoiceModel]
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
from typing import Literal, Optional
from pydantic import BaseModel


class GenerateAudioRequest(BaseModel):
"""
Wrap the request parameters to generate an audio for an input text.
"""

def __init__(
self,
input: str,
voice_id: str,
audio_format: Optional[Literal["wav", "mp3", "ogg", "aac"]] = None,
language: Optional[str] = None,
model: Optional[
Literal["simba-english", "simba-multilingual", "simba-turbo"]
] = None,
loudness_normalization=Optional[bool],
):
"""
Create the parameters wrapper.

Args:
input: Plain text or SSML to be synthesized to speech. Refer to https://docs.sws.speechify.com/docs/api-limits for the input size limits.
voice_id: Id of the voice to be used for synthesizing speech. Refer to :func:`~speechify_sdk.lib.speechify_voices.SpeechifyVoices.list` for available voices.
audio_format: The format for the output audio. Note, that the current default is "wav", but there's no guarantee it will not change in the future. We recommend always passing the specific param you expect.
language: Language of the input. Follow the format of an ISO 639-1 language code and an ISO 3166-1 region code, separated by a hyphen, e.g. en-US. Please refer to the list of the supported languages and recommendations regarding this parameter: https://docs.sws.speechify.com/docs/language-support.
model: Model used for audio synthesis. Defaults to simba-base. It could be:

- simba-base ModelBase ModelBase is deprecated. Use simba-english or simba-multilingual instead. @deprecated
- simba-english ModelEnglish
- simba-multilingual ModelMultilingual
- simba-turbo ModelTurbo

loudness_normalization: Determines whether to normalize the audio loudness to a standard level. When enabled, loudness normalization aligns the audio output to the following standards:

- Integrated loudness: -14 LUFS
- True peak: -2 dBTP
- Loudness range: 7 LU
If disabled, the audio loudness will match the original loudness of the selected voice, which may vary significantly and be either too quiet or too loud. Enabling loudness normalization can increase latency due to additional processing required for audio level adjustments.

Returns:
A new instance of the request model containing all the parameters.
"""
self.input = input
self.voice_id = voice_id
self.audio_format = audio_format
self.language = language
self.model = model
self.loudness_normalization = loudness_normalization
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from typing import List, Literal
from pydantic import BaseModel


class SpeechMarkChunk(BaseModel):
end: int
end_time: float
start: int
start_time: float
type: str
value: str


class SpeechMarks(SpeechMarkChunk):
end: int
end_time: float
start: int
start_time: float
type: str
value: str
chunks: List[SpeechMarkChunk]


class GenerateAudioResponse(BaseModel):
audio_data: str
audio_format: Literal["wav", "mp3", "ogg", "aac"]
billable_characters_count: int
speech_marks: SpeechMarks
Loading