Cleanup examples and fix VoicesManager types (#332)

Signed-off-by: rany <rany2@riseup.net>
This commit is contained in:
Rany
2024-11-23 16:29:13 +02:00
committed by GitHub
parent 17e5146606
commit a3d468c7c9
11 changed files with 33 additions and 138 deletions

View File

@@ -1,8 +1,8 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
""" """Simple example to generate an audio file with randomized
Example of dynamic voice selection using VoicesManager. dynamic voice selection based on attributes such as Gender,
""" Language, or Locale."""
import asyncio import asyncio
import random import random

View File

@@ -1,8 +1,6 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
""" """Simple example to generate audio with preset voice using async/await"""
Basic example of edge_tts usage.
"""
import asyncio import asyncio

View File

@@ -1,11 +1,7 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
""" """Example showing how to use use .stream() method to get audio chunks
Streaming TTS example with subtitles. and feed them to SubMaker to generate subtitles"""
This example is similar to the example basic_audio_streaming.py, but it shows
WordBoundary events to create subtitles using SubMaker.
"""
import asyncio import asyncio

View File

@@ -1,35 +0,0 @@
#!/usr/bin/env python3
"""
Basic audio streaming example.
This example shows how to stream the audio data from the TTS engine,
and how to get the WordBoundary events from the engine (which could
be ignored if not needed).
The example streaming_with_subtitles.py shows how to use the
WordBoundary events to create subtitles using SubMaker.
"""
import asyncio
import edge_tts
TEXT = "Hello World!"
VOICE = "en-GB-SoniaNeural"
OUTPUT_FILE = "test.mp3"
async def amain() -> None:
"""Main function"""
communicate = edge_tts.Communicate(TEXT, VOICE)
with open(OUTPUT_FILE, "wb") as file:
async for chunk in communicate.stream():
if chunk["type"] == "audio":
file.write(chunk["data"])
elif chunk["type"] == "WordBoundary":
print(f"WordBoundary: {chunk}")
if __name__ == "__main__":
asyncio.run(amain())

View File

@@ -1,8 +1,7 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
""" """Sync variant of the example for generating audio with a predefined voice"""
Basic example of edge_tts usage in synchronous function
"""
import edge_tts import edge_tts

View File

@@ -1,36 +0,0 @@
#!/usr/bin/env python3
"""
This example shows that sync version of save function also works when run from
a sync function called itself from an async function.
The simple implementation of save_sync() with only asyncio.run would fail in this scenario,
that's why ThreadPoolExecutor is used in implementation.
"""
import asyncio
import edge_tts
TEXT = "Hello World!"
VOICE = "en-GB-SoniaNeural"
OUTPUT_FILE = "test.mp3"
def sync_main() -> None:
"""Main function"""
communicate = edge_tts.Communicate(TEXT, VOICE)
communicate.save_sync(OUTPUT_FILE)
async def amain() -> None:
"""Main function"""
sync_main()
if __name__ == "__main__":
loop = asyncio.get_event_loop_policy().get_event_loop()
try:
loop.run_until_complete(amain())
finally:
loop.close()

View File

@@ -1,42 +0,0 @@
#!/usr/bin/env python3
"""
This example shows the sync version of stream function which also
works when run from a sync function called itself from an async function.
"""
import asyncio
import edge_tts
TEXT = "Hello World!"
VOICE = "en-GB-SoniaNeural"
OUTPUT_FILE = "test.mp3"
def main() -> None:
"""Main function to process audio and metadata synchronously."""
communicate = edge_tts.Communicate(TEXT, VOICE)
with open(OUTPUT_FILE, "wb") as file:
for chunk in communicate.stream_sync():
if chunk["type"] == "audio":
file.write(chunk["data"])
elif chunk["type"] == "WordBoundary":
print(f"WordBoundary: {chunk}")
async def amain() -> None:
"""
Async main function to call sync main function
This demonstrates that this works even when called from an async function.
"""
main()
if __name__ == "__main__":
loop = asyncio.get_event_loop_policy().get_event_loop()
try:
loop.run_until_complete(amain())
finally:
loop.close()

View File

@@ -1,26 +1,30 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
""" """Sync variant of the async .stream() method to
Basic audio streaming example for sync interface get audio chunks and feed them to SubMaker to
generate subtitles"""
"""
import edge_tts import edge_tts
TEXT = "Hello World!" TEXT = "Hello World!"
VOICE = "en-GB-SoniaNeural" VOICE = "en-GB-SoniaNeural"
OUTPUT_FILE = "test.mp3" OUTPUT_FILE = "test.mp3"
SRT_FILE = "test.srt"
def main() -> None: def main() -> None:
"""Main function to process audio and metadata synchronously.""" """Main function"""
communicate = edge_tts.Communicate(TEXT, VOICE) communicate = edge_tts.Communicate(TEXT, VOICE)
submaker = edge_tts.SubMaker()
with open(OUTPUT_FILE, "wb") as file: with open(OUTPUT_FILE, "wb") as file:
for chunk in communicate.stream_sync(): for chunk in communicate.stream_sync():
if chunk["type"] == "audio": if chunk["type"] == "audio":
file.write(chunk["data"]) file.write(chunk["data"])
elif chunk["type"] == "WordBoundary": elif chunk["type"] == "WordBoundary":
print(f"WordBoundary: {chunk}") submaker.feed(chunk)
with open(SRT_FILE, "w", encoding="utf-8") as file:
file.write(submaker.get_srt())
if __name__ == "__main__": if __name__ == "__main__":

View File

@@ -518,7 +518,7 @@ class Communicate:
json.dump(message, metadata) json.dump(message, metadata)
metadata.write("\n") metadata.write("\n")
def stream_sync(self) -> Generator[Dict[str, Any], None, None]: def stream_sync(self) -> Generator[TTSChunk, None, None]:
"""Synchronous interface for async stream method""" """Synchronous interface for async stream method"""
def fetch_async_items(queue: Queue) -> None: # type: ignore def fetch_async_items(queue: Queue) -> None: # type: ignore

View File

@@ -82,3 +82,11 @@ class VoiceManagerVoice(Voice):
"""Voice data for VoiceManager.""" """Voice data for VoiceManager."""
Language: str Language: str
class VoiceManagerFind(TypedDict):
"""Voice data for VoiceManager.find()."""
Gender: NotRequired[Literal["Female", "Male"]]
Locale: NotRequired[str]
Language: NotRequired[str]

View File

@@ -7,10 +7,11 @@ from typing import Any, List, Optional
import aiohttp import aiohttp
import certifi import certifi
from typing_extensions import Unpack
from .constants import SEC_MS_GEC_VERSION, VOICE_HEADERS, VOICE_LIST from .constants import SEC_MS_GEC_VERSION, VOICE_HEADERS, VOICE_LIST
from .drm import DRM from .drm import DRM
from .typing import Voice, VoiceManagerVoice from .typing import Voice, VoiceManagerFind, VoiceManagerVoice
async def __list_voices( async def __list_voices(
@@ -94,7 +95,9 @@ class VoicesManager:
self.called_create: bool = False self.called_create: bool = False
@classmethod @classmethod
async def create(cls: Any, custom_voices: Optional[List[Voice]] = None) -> Any: async def create(
cls: Any, custom_voices: Optional[List[Voice]] = None
) -> "VoicesManager":
""" """
Creates a VoicesManager object and populates it with all available voices. Creates a VoicesManager object and populates it with all available voices.
""" """
@@ -106,7 +109,7 @@ class VoicesManager:
self.called_create = True self.called_create = True
return self return self
def find(self, **kwargs: Any) -> List[VoiceManagerVoice]: def find(self, **kwargs: Unpack[VoiceManagerFind]) -> List[VoiceManagerVoice]:
""" """
Finds all matching voices based on the provided attributes. Finds all matching voices based on the provided attributes.
""" """