Files
EscapistsReintroduced/tools/generate_bundled_speech.py
T

62 lines
2.8 KiB
Python

#!/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