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