Bundle mission speech with an admin mute option and fix live audio downloads

This commit is contained in:
2026-10-15 16:53:39 +02:00
parent 07c4d70de5
commit 009b2fb591
181 changed files with 19248 additions and 103 deletions
+61
View File
@@ -0,0 +1,61 @@
#!/usr/bin/env python3
"""One-off speech asset generation on the credential-owning speech host.
Resumes completed files and reuses the live helper's existing cache. It never
changes that cache, credentials, mission config or the running helper.
"""
import argparse
import hashlib
import json
from pathlib import Path
from welcome_voice import ENDPOINT, VETERAN_PROMPT, save_clip, synthesize, validate_wav
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--manifest', type=Path, required=True)
parser.add_argument('--output', type=Path, required=True)
parser.add_argument('--cache', type=Path)
parser.add_argument('--project')
args = parser.parse_args()
manifest = json.loads(args.manifest.read_text())
voice, language = manifest['voice'], manifest['language']
identity = [ENDPOINT, voice, language, '', VETERAN_PROMPT]
args.output.mkdir(parents=True, exist_ok=True)
report = []
for entry in manifest['clips']:
name, text = entry['id'], entry['text']
assert name.replace('_', '').isalnum() and 1 <= len(text) <= 800
path = args.output / (name + '.wav')
marker = args.output / (name + '.json')
key = hashlib.sha256(json.dumps([identity, text], ensure_ascii=False).encode()).hexdigest()
origin = 'generated'
if path.exists() and marker.exists() and json.loads(marker.read_text())['cache_key'] == key:
audio = validate_wav(path.read_bytes())
origin = 'resumed'
elif args.cache and (args.cache / (key + '.wav')).is_file():
audio = validate_wav((args.cache / (key + '.wav')).read_bytes())
origin = 'cached'
else:
import google.auth
from google.auth.transport.requests import AuthorizedSession
credentials, _ = google.auth.default(
scopes=['https://www.googleapis.com/auth/cloud-platform'], quota_project_id=args.project)
with AuthorizedSession(credentials, max_refresh_attempts=0) as session:
audio = synthesize(session, ENDPOINT, voice, language, text)
save_clip(path, audio)
record = {'id': name, 'cache_key': key, 'sha256': hashlib.sha256(audio).hexdigest(),
'bytes': len(audio), 'origin': origin, 'voice': voice}
marker.write_text(json.dumps(record, indent=2) + '\n')
report.append(record)
print(f"{name}: {origin}, {len(audio)} bytes", flush=True)
(args.output / 'generation.json').write_text(json.dumps(report, indent=2) + '\n')
if __name__ == '__main__':
try:
main()
except Exception as exc:
# Provider exceptions can contain credentials/URLs; retain a fixed error.
raise SystemExit(f'Bundled speech generation failed ({type(exc).__name__}); completed assets retained.') from None