Source code
src/scripts/presentation/edge_cli.py
This file is included in this documentation build. It is displayed as code and is not executed.
"""Audition the configured Edge voice and pronunciation spellings with clean captions.
@author Laurent Declercq l.declercq@agon-innovation.ch
@version 20260921
"""
import argparse
from pathlib import Path
import shutil
import subprocess
import sys
from cli import PYTHON, TOOLS
from pipeline import (
ROOT,
SOURCE,
caption_cues,
edge_options,
ensure_audio,
pronunciation_text,
read_json,
write_json,
)
def main():
"""Generate an isolated MP3 audition using the video pipeline's speech settings."""
parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
parser.add_argument("--locale", choices=["fr", "en", "de"], default="fr")
parser.add_argument(
"--voice", help="Override the Edge voice for this audition only."
)
parser.add_argument("--rate", help="Override speaking speed, for example +0%%.")
parser.add_argument("--text-file", type=Path)
parser.add_argument("--output", type=Path)
parser.add_argument("--offline", action="store_true")
parser.add_argument("--force", action="store_true")
args = parser.parse_args()
if args.offline and args.force:
parser.error("--offline and --force cannot be combined.")
if Path(sys.prefix).resolve() != TOOLS.resolve():
if not PYTHON.is_file():
parser.error("Run yarn presentation:setup first.")
raise SystemExit(
subprocess.run(
[str(PYTHON), str(Path(__file__).resolve()), *sys.argv[1:]]
).returncode
)
settings = read_json(SOURCE / "settings.json")
if args.voice:
settings["voices"][args.locale] = args.voice
if args.rate:
settings.setdefault("rates", {})[args.locale] = args.rate
rate, aliases = edge_options(settings, args.locale)
source = args.text_file or SOURCE / "samples" / f"{args.locale}.txt"
text = source.read_text(encoding="utf-8").strip()
if not text:
parser.error("The audition text must not be empty.")
output = (
args.output
or ROOT / ".build/presentation-voice-samples-edge" / f"edge-{args.locale}.mp3"
)
if output.suffix.lower() != ".mp3":
parser.error("The audition output must have a .mp3 extension.")
audio, words = ensure_audio(
{"id": "audition", "narration": text},
args.locale,
settings,
ROOT / ".build/presentation/edge-auditions",
args.offline,
args.force,
)
output.parent.mkdir(parents=True, exist_ok=True)
shutil.copyfile(audio, output)
output.with_suffix(".txt").write_text(text + "\n", encoding="utf-8")
spoken = pronunciation_text(text, aliases)
output.with_suffix(".spoken.txt").write_text(spoken + "\n", encoding="utf-8")
output.with_suffix(".vtt").write_text(
"WEBVTT\n\n" + "\n".join(caption_cues(words, text, 0)), encoding="utf-8"
)
write_json(
output.with_suffix(".json"),
{
"backend": "edge",
"voice": settings["voices"][args.locale],
"rate": rate,
"pitch": "+0Hz",
"locale": args.locale,
"text": text,
"spokenText": spoken,
"pronunciations": aliases,
},
)
print(f"Generated {output}.")
if __name__ == "__main__":
try:
main()
except (OSError, ValueError, RuntimeError, ImportError) as error:
print(f"Edge audition failed: {error}", file=sys.stderr)
raise SystemExit(1) from error