167 lines
4.7 KiB
Python
167 lines
4.7 KiB
Python
"""
|
|
Main package.
|
|
"""
|
|
|
|
|
|
import argparse
|
|
import asyncio
|
|
import sys
|
|
|
|
from edge_tts import Communicate, SubMaker, list_voices
|
|
|
|
|
|
async def _list_voices():
|
|
"""
|
|
List available voices.
|
|
"""
|
|
for idx, voice in enumerate(await list_voices()):
|
|
if idx != 0:
|
|
print()
|
|
|
|
for key in voice.keys():
|
|
if key in ["SuggestedCodec", "FriendlyName", "Status"]:
|
|
continue
|
|
# print ("%s: %s" % ("Name" if key == "ShortName" else key, voice[key]))
|
|
print(f"{key}: {voice[key]}")
|
|
|
|
|
|
async def _tts(args):
|
|
tts = Communicate()
|
|
subs = SubMaker(args.overlapping)
|
|
if args.write_media:
|
|
media_file = open(args.write_media, "wb") # pylint: disable=consider-using-with
|
|
async for i in tts.run(
|
|
args.text,
|
|
args.enable_sentence_boundary,
|
|
args.enable_word_boundary,
|
|
args.codec,
|
|
args.voice,
|
|
args.pitch,
|
|
args.rate,
|
|
args.volume,
|
|
customspeak=args.custom_ssml,
|
|
):
|
|
if i[2] is not None:
|
|
if not args.write_media:
|
|
sys.stdout.buffer.write(i[2])
|
|
else:
|
|
media_file.write(i[2])
|
|
elif i[0] is not None and i[1] is not None:
|
|
subs.create_sub(i[0], i[1])
|
|
if args.write_media:
|
|
media_file.close()
|
|
if not args.write_subtitles:
|
|
sys.stderr.write(subs.generate_subs())
|
|
else:
|
|
with open(args.write_subtitles, "w", encoding="utf-8") as file:
|
|
file.write(subs.generate_subs())
|
|
|
|
|
|
async def _main():
|
|
parser = argparse.ArgumentParser(description="Microsoft Edge TTS")
|
|
group = parser.add_mutually_exclusive_group(required=True)
|
|
group.add_argument("-t", "--text", help="what TTS will say")
|
|
group.add_argument("-f", "--file", help="same as --text but read from file")
|
|
parser.add_argument(
|
|
"-z",
|
|
"--custom-ssml",
|
|
help="treat text as ssml to send. For more info check https://bit.ly/3fIq13S",
|
|
action="store_true",
|
|
)
|
|
parser.add_argument(
|
|
"-v",
|
|
"--voice",
|
|
help="voice for TTS. "
|
|
"Default: Microsoft Server Speech Text to Speech Voice (en-US, AriaNeural)",
|
|
default="Microsoft Server Speech Text to Speech Voice (en-US, AriaNeural)",
|
|
)
|
|
parser.add_argument(
|
|
"-c",
|
|
"--codec",
|
|
help="codec format. Default: audio-24khz-48kbitrate-mono-mp3. "
|
|
"Another choice is webm-24khz-16bit-mono-opus. "
|
|
"For more info check https://bit.ly/2T33h6S",
|
|
default="audio-24khz-48kbitrate-mono-mp3",
|
|
)
|
|
group.add_argument(
|
|
"-l",
|
|
"--list-voices",
|
|
help="lists available voices. "
|
|
"Edge's list is incomplete so check https://bit.ly/2SFq1d3",
|
|
action="store_true",
|
|
)
|
|
parser.add_argument(
|
|
"-p",
|
|
"--pitch",
|
|
help="set TTS pitch. Default +0Hz, For more info check https://bit.ly/3eAE5Nx",
|
|
default="+0Hz",
|
|
)
|
|
parser.add_argument(
|
|
"-r",
|
|
"--rate",
|
|
help="set TTS rate. Default +0%%. For more info check https://bit.ly/3eAE5Nx",
|
|
default="+0%",
|
|
)
|
|
parser.add_argument(
|
|
"-V",
|
|
"--volume",
|
|
help="set TTS volume. Default +0%%. For more info check https://bit.ly/3eAE5Nx",
|
|
default="+0%",
|
|
)
|
|
parser.add_argument(
|
|
"-s",
|
|
"--enable-sentence-boundary",
|
|
help="enable sentence boundary",
|
|
action="store_true",
|
|
)
|
|
parser.add_argument(
|
|
"-w",
|
|
"--enable-word-boundary",
|
|
help="enable word boundary",
|
|
action="store_true",
|
|
)
|
|
parser.add_argument(
|
|
"-O",
|
|
"--overlapping",
|
|
help="overlapping subtitles in seconds",
|
|
default=5,
|
|
type=float,
|
|
)
|
|
parser.add_argument(
|
|
"--write-media", help="instead of stdout, send media output to provided file"
|
|
)
|
|
parser.add_argument(
|
|
"--write-subtitles",
|
|
help="instead of stderr, send subtitle output to provided file",
|
|
)
|
|
args = parser.parse_args()
|
|
|
|
if args.list_voices:
|
|
await _list_voices()
|
|
sys.exit(0)
|
|
|
|
if args.text is not None or args.file is not None:
|
|
if args.file is not None:
|
|
# we need to use sys.stdin.read() because some devices
|
|
# like Windows and Termux don't have a /dev/stdin.
|
|
if args.file == "/dev/stdin":
|
|
# logger.debug("stdin detected, reading natively from stdin")
|
|
args.text = sys.stdin.read()
|
|
else:
|
|
# logger.debug("reading from %s" % args.file)
|
|
with open(args.file, "r", encoding="utf-8") as file:
|
|
args.text = file.read()
|
|
|
|
await _tts(args)
|
|
|
|
|
|
def main():
|
|
"""
|
|
Main function.
|
|
"""
|
|
asyncio.run(_main())
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|