-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Replace fast api with litestar. Merge pull request #9
* feat: add InvalidSampleRate exception * feat: replace fastAPI with litestar * chore: rename server.py to app.py * chore: update README * fix: run command in dockerfile * chore: update README.md
- Loading branch information
Showing
11 changed files
with
174 additions
and
152 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
from os import environ | ||
from typing import Annotated | ||
|
||
from dotenv import load_dotenv | ||
from litestar import Litestar, get, Response | ||
from litestar.openapi import OpenAPIConfig | ||
from litestar.config.response_cache import CACHE_FOREVER | ||
from litestar.params import Parameter | ||
|
||
from tts import tts | ||
from openapi_examples import * | ||
from http_exceptions import * | ||
from exceptions import * | ||
|
||
|
||
load_dotenv() | ||
|
||
SILERO_MAX_TEXT_LENGTH = 930 | ||
text_length_limit = min( | ||
int(environ.get("TEXT_LENGTH_LIMIT", SILERO_MAX_TEXT_LENGTH)), | ||
SILERO_MAX_TEXT_LENGTH, | ||
) | ||
|
||
|
||
@get( | ||
"/generate", | ||
summary="Generate WAV audio from text", | ||
media_type="audio/wav", | ||
sync_to_thread=True, | ||
raises=genetate_exceptions, | ||
) | ||
def generate( | ||
text: Annotated[str, Parameter(examples=text_examples)], | ||
speaker: Annotated[str, Parameter(examples=speaker_examples)], | ||
sample_rate: Annotated[ | ||
int, Parameter(examples=sample_rate_examples, default=48_000) | ||
], | ||
) -> Response: | ||
if len(text) > text_length_limit: | ||
raise TextTooLongHTTPException( | ||
{"text": text, "length": len(text), "max_length": text_length_limit} | ||
) | ||
|
||
try: | ||
audio = tts.generate(text, speaker, sample_rate) | ||
except NotFoundModelException: | ||
raise NotFoundSpeakerHTTPException({"speaker": speaker}) | ||
except NotCorrectTextException: | ||
raise NotCorrectTextHTTPException({"text": text}) | ||
except TextTooLongException: | ||
raise TextTooLongHTTPException( | ||
{"text": text, "length": len(text), "max_length": text_length_limit} | ||
) | ||
except InvalidSampleRateException: | ||
raise InvalidSampleRateHTTPException( | ||
{"sample_rate": sample_rate, "valid_sample_rates": tts.VALID_SAMPLE_RATES} | ||
) | ||
else: | ||
return Response(audio, media_type="audio/wav") | ||
|
||
|
||
@get("/speakers", summary="List available speakers", cache=CACHE_FOREVER) | ||
async def speakers() -> dict[str, list[str]]: | ||
return tts.speakers | ||
|
||
|
||
app = Litestar( | ||
[generate, speakers], | ||
openapi_config=OpenAPIConfig(title="Silero TTS API", version="1.0.0"), | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,19 @@ | ||
class NotFoundModelException(Exception): | ||
def __init__(self, speaker_name: str): | ||
self.speaker_name = speaker_name | ||
super().__init__(f"Model not found for speaker: {speaker_name}") | ||
|
||
class NotCorrectTextException(Exception): | ||
def __init__(self, text: str): | ||
self.text = text | ||
super().__init__(f"Text not correct: {text}") | ||
|
||
class TextTooLongException(Exception): | ||
def __init__(self, text: str): | ||
super().__init__(f"Text too long. Length is {len(text)}. Max length is 930 symbols.") | ||
self.text = text | ||
super().__init__(f"Text too long. Length is {len(text)}. Max length is 930 symbols.") | ||
|
||
class InvalidSampleRateException(Exception): | ||
def __init__(self, sample_rate: int) -> None: | ||
self.sample_rate = sample_rate | ||
super().__init__(f"Invalid sample rate {sample_rate}. Supported sample rates are 8 000, 24 000, and 48 000.") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
from typing import Any | ||
|
||
from litestar.exceptions import HTTPException | ||
from litestar import status_codes as status | ||
|
||
|
||
class BaseHTTPException(HTTPException): | ||
headers = {"Content-Type": "application/json"} | ||
|
||
def __init__(self, extra: dict[str, Any] = None) -> None: | ||
super().__init__( | ||
detail=self.detail, | ||
status_code=self.status_code, | ||
headers=self.headers, | ||
extra=extra, | ||
) | ||
|
||
|
||
class NotFoundSpeakerHTTPException(BaseHTTPException): | ||
status_code = status.HTTP_404_NOT_FOUND | ||
detail = "Speaker not found" | ||
|
||
|
||
class NotCorrectTextHTTPException(BaseHTTPException): | ||
status_code = status.HTTP_422_UNPROCESSABLE_ENTITY | ||
detail = "Text is not correct" | ||
|
||
|
||
class TextTooLongHTTPException(BaseHTTPException): | ||
status_code = status.HTTP_413_REQUEST_ENTITY_TOO_LARGE | ||
detail = "Text too long" | ||
|
||
|
||
class InvalidSampleRateHTTPException(BaseHTTPException): | ||
status_code = status.HTTP_400_BAD_REQUEST | ||
detail = "Invalid sample rate" | ||
|
||
|
||
genetate_exceptions = [ | ||
NotFoundSpeakerHTTPException, | ||
NotCorrectTextHTTPException, | ||
TextTooLongHTTPException, | ||
InvalidSampleRateHTTPException, | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,32 +1,19 @@ | ||
from fastapi import Query | ||
from litestar.openapi.spec import Example | ||
|
||
TextExamples = Query( | ||
openapi_examples={ | ||
"ru_1": { | ||
"value": "Съешьте ещё этих мягких французских булочек, да выпейте чаю." | ||
}, | ||
"ru_2": { | ||
"value": "В недрах тундры выдры в гетрах тырят в вёдра ядра кедров." | ||
}, | ||
"en_1": { | ||
"value": "Can you can a canned can into an un-canned can like a canner can can a canned can into an un-canned can?" | ||
}, | ||
} | ||
) | ||
text_examples = [ | ||
Example("ru_1", value="Съешьте ещё этих мягких французских булочек, да выпейте чаю."), | ||
Example("ru_2", value="В недрах тундры выдры в гетрах тырят в вёдра ядра кедров."), | ||
Example("en_1", value="Can you can a canned can into an un-canned can like a canner can can a canned can into an un-canned can?"), | ||
] | ||
|
||
SpeakerExamples = Query( | ||
openapi_examples={ | ||
"ru_aidar": {"value": "aidar"}, | ||
"ru_baya": {"value": "baya"}, | ||
"en_0": {"value": "en_0"}, | ||
} | ||
) | ||
speaker_examples = [ | ||
Example("ru_aidar", value="aidar"), | ||
Example("ru_baya", value="baya"), | ||
Example("en_0", value="en_0"), | ||
] | ||
|
||
SampleRateExamples = Query( | ||
openapi_examples={ | ||
"8 000": {"value": 8_000}, | ||
"24 000": {"value": 24_000}, | ||
"48 000": {"value": 48_000}, | ||
}, | ||
description="Sample rate in Hz", | ||
) | ||
sample_rate_examples = [ | ||
Example("8 000", value=8_000), | ||
Example("24 000", value=24_000), | ||
Example("48 000", value=48_000), | ||
] |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.