#!/usr/bin/env python3 """ Audio Processor - TTS voiceover, sound effects, and background music. This module processes audio for roadshow videos, including TTS voiceover, sound effects, and background music using COZE's TTS capability. """ import json import os import time from pathlib import Path from typing import Any, Dict, List, Optional from coze_workload_identity import requests # ============================================================================= # Constants # ============================================================================= SKILL_ID = "7598365301381791753" TTS_API_KEY = os.getenv(f"COZE_TTS_API_{SKILL_ID}", "") DEFAULT_OUTPUT_DIR = "audio" DEFAULT_MODEL = "tts-1" DEFAULT_VOICE = "alloy" # ============================================================================= # TTS Voice Generator # ============================================================================= class TTSVoiceGenerator: """Generator for TTS voiceover using COZE TTS API.""" def __init__( self, api_key: Optional[str] = None, model: str = DEFAULT_MODEL, voice: str = DEFAULT_VOICE ): """ Initialize TTS voice generator. Args: api_key: TTS API key. If not provided, reads from environment. model: TTS model name. voice: Voice style. Raises: ValueError: If API key is not configured. """ self.api_key = api_key or TTS_API_KEY self.model = model self.voice = voice if not self.api_key: raise ValueError( "TTS API key is not configured.\n" "Please set COZE_TTS_API_ environment variable " "or use skill_credentials tool." ) print(f"TTS 语音生成器已初始化") print(f" 模型: {model}") print(f" 音色: {voice}") def generate_audio( self, text: str, output_path: str ) -> bool: """ Generate audio from text using TTS. Args: text: Text to convert to speech. output_path: Output audio file path. Returns: True if successful, False otherwise. """ if not text: print("错误:文本为空") return False output_path = Path(output_path) output_path.parent.mkdir(parents=True, exist_ok=True) print(f"正在生成语音...") print(f" 文本长度: {len(text)} 字符") print(f" 输出路径: {output_path}") # 这里应该调用实际的 TTS API # 由于当前环境限制,使用占位符 # 实际实现时,请使用 COZE 的 TTS API # 示例 API 调用(伪代码) # url = "https://api.openai.com/v1/audio/speech" # headers = { # "Authorization": f"Bearer {self.api_key}", # "Content-Type": "application/json" # } # data = { # "model": self.model, # "input": text, # "voice": self.voice # } try: # 模拟 API 调用 # response = requests.post(url, headers=headers, json=data, timeout=60) # response.raise_for_status() # with open(output_path, 'wb') as f: # f.write(response.content) # 临时实现:创建空文件作为占位符 print("⚠ TTS API 调用未实现,创建占位符文件") with open(output_path, 'w') as f: f.write("TTS audio placeholder") print(f"✓ 音频文件已生成: {output_path}") return True except Exception as e: print(f"错误:生成音频失败 - {e}") return False # ============================================================================= # Audio Processor # ============================================================================= class AudioProcessor: """Processor for audio elements (voiceover, sound effects, music).""" def __init__( self, output_dir: str = DEFAULT_OUTPUT_DIR, tts_generator: Optional[TTSVoiceGenerator] = None ): """ Initialize audio processor. Args: output_dir: Output directory for audio files. tts_generator: TTS generator instance (created if not provided). """ self.output_dir = Path(output_dir) self.output_dir.mkdir(parents=True, exist_ok=True) self.tts_generator = tts_generator or TTSVoiceGenerator() print(f"音频处理器已初始化") print(f" 输出目录: {self.output_dir}") def process_script( self, script_path: str, brand_style_path: Optional[str] = None ) -> Dict[str, Any]: """ Process roadshow script to generate audio elements. Args: script_path: Path to roadshow script file. brand_style_path: Path to brand style JSON (optional). Returns: Processing result with audio file paths. """ print(f"处理演讲稿: {script_path}") # Load script with open(script_path, 'r', encoding='utf-8') as f: script_content = f.read() # Generate voiceover voiceover_path = self.output_dir / "voiceover.mp3" success = self.tts_generator.generate_audio( text=script_content, output_path=str(voiceover_path) ) if not success: return { 'success': False, 'error': 'Failed to generate voiceover' } # Load brand style if provided brand_style = {} if brand_style_path and Path(brand_style_path).exists(): with open(brand_style_path, 'r', encoding='utf-8') as f: brand_style = json.load(f) # Copy background music if available bg_music_path = None music_sources = [ Path("assets/music/background.mp3"), Path("assets/music/background.mp3") ] for music_source in music_sources: if music_source.exists(): bg_music_path = self.output_dir / "background_music.mp3" import shutil shutil.copy2(music_source, bg_music_path) print(f"✓ 背景音乐已复制: {bg_music_path}") break result = { 'success': True, 'voiceover': str(voiceover_path), 'background_music': str(bg_music_path) if bg_music_path else None, 'brand_style': brand_style } return result # ============================================================================= # Main Function # ============================================================================= def main(): """Main entry point.""" import argparse parser = argparse.ArgumentParser( description='Process audio for roadshow video' ) parser.add_argument( '--script', '-s', required=True, help='Roadshow script file path' ) parser.add_argument( '--style-brand', '-b', help='Brand style JSON file path' ) parser.add_argument( '--output', '-o', default=DEFAULT_OUTPUT_DIR, help='Output directory for audio files' ) args = parser.parse_args() # Create processor processor = AudioProcessor(output_dir=args.output) # Process script result = processor.process_script( script_path=args.script, brand_style_path=args.style_brand ) if result['success']: print("\n✓ 音频处理完成") print(f" 配音: {result['voiceover']}") if result['background_music']: print(f" 背景音乐: {result['background_music']}") else: print(f"\n✗ 音频处理失败: {result.get('error')}") if __name__ == '__main__': main()