Dify guides: first release, English set, 20 September 2026
Eight step-by-step guides, appendices (design patterns, design card with eight filled-in examples, AI tools in education), facilitator guide and presentation. LeX Consultancy edition; screenshots from the Dutch build of the same apps. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Turns department-meeting-script.txt into a test recording with computer voices, so no real
|
||||
voice ever goes into Dify.
|
||||
|
||||
Usage: OPENAI_API_KEY=sk-... python3 make_recording.py (one voice, alloy)
|
||||
OPENAI_API_KEY=sk-... python3 make_recording.py --three (nova, echo, alloy: one per speaker)
|
||||
Requires ffmpeg and an OpenAI key in the environment variable; the key is never stored.
|
||||
Cost: about 4,000 characters with tts-1, roughly 6 cents.
|
||||
|
||||
Output: department-meeting.mp3 (128 kbps) and department-meeting-small.mp3 (32 kbps mono,
|
||||
under the 5 MB limit of the Speech To Text tool).
|
||||
"""
|
||||
import json, os, re, subprocess, sys, tempfile, urllib.request
|
||||
from pathlib import Path
|
||||
|
||||
HERE = Path(__file__).resolve().parent
|
||||
SCRIPT = HERE / "department-meeting-script.txt"
|
||||
THREE = "--three" in sys.argv
|
||||
VOICES = {"Karen": "nova", "Bram": "echo", "Fatima": "alloy"} if THREE else {}
|
||||
DEFAULT = "alloy"
|
||||
key = os.environ.get("OPENAI_API_KEY") or sys.exit("Set OPENAI_API_KEY in the environment and run again.")
|
||||
|
||||
|
||||
def speak(text: str, voice: str, dest: Path) -> None:
|
||||
req = urllib.request.Request(
|
||||
"https://api.openai.com/v1/audio/speech",
|
||||
data=json.dumps({"model": "tts-1", "voice": voice, "input": text, "response_format": "mp3"}).encode(),
|
||||
headers={"Authorization": f"Bearer {key}", "Content-Type": "application/json"})
|
||||
with urllib.request.urlopen(req, timeout=120) as r:
|
||||
dest.write_bytes(r.read())
|
||||
|
||||
|
||||
lines = [l.strip() for l in SCRIPT.read_text(encoding="utf-8").splitlines() if l.strip()]
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
tmp = Path(tmp); parts = []
|
||||
for i, line in enumerate(lines):
|
||||
m = re.match(r"^(Karen|Bram|Fatima):\s*(.+)$", line)
|
||||
voice, text = (VOICES.get(m.group(1), DEFAULT), m.group(2)) if m else (DEFAULT, line)
|
||||
part = tmp / f"{i:03d}.mp3"; speak(text, voice, part); parts.append(part)
|
||||
print(f"{i + 1}/{len(lines)} {voice}: {text[:50]}...")
|
||||
lst = tmp / "list.txt"; lst.write_text("".join(f"file '{p}'\n" for p in parts))
|
||||
subprocess.run(["ffmpeg", "-y", "-loglevel", "error", "-f", "concat", "-safe", "0", "-i", str(lst),
|
||||
"-c:a", "libmp3lame", "-b:a", "128k", str(HERE / "department-meeting.mp3")], check=True)
|
||||
subprocess.run(["ffmpeg", "-y", "-loglevel", "error", "-i", str(HERE / "department-meeting.mp3"),
|
||||
"-ac", "1", "-ar", "16000", "-b:a", "32k", str(HERE / "department-meeting-small.mp3")], check=True)
|
||||
for name in ("department-meeting.mp3", "department-meeting-small.mp3"):
|
||||
print(f"{name}: {(HERE / name).stat().st_size // 1024} kB")
|
||||
Reference in New Issue
Block a user