UNPKG

fnglish-notebook

Version:

A cross-platform CLI tool for English learning with translation, voice pronunciation, and Notion integration. Supports Windows, macOS, and Linux.

115 lines 4.04 kB
import fs from 'fs'; import path from 'path'; import os from 'os'; // @ts-ignore - play-sound doesn't have types import Player from 'play-sound'; export class SpeechService { apiKey; player; tempDir; constructor(apiKey) { this.apiKey = apiKey; this.player = Player({}); this.tempDir = path.join(os.tmpdir(), 'fnglish-notebook'); // 确保临时目录存在 if (!fs.existsSync(this.tempDir)) { fs.mkdirSync(this.tempDir, { recursive: true }); } } async speakText(text) { try { const audioData = await this.synthesizeSpeech(text); await this.playAudio(audioData); } catch (error) { console.error('语音播放失败:', error); throw new Error(`语音播放失败: ${error instanceof Error ? error.message : '未知错误'}`); } } async synthesizeSpeech(text) { const request = { input: { text: text }, voice: { languageCode: 'en-US', name: 'en-US-Standard-H', // 女声 ssmlGender: 'FEMALE' }, audioConfig: { audioEncoding: 'MP3', speakingRate: 1.0, pitch: 0.0 } }; const url = `https://texttospeech.googleapis.com/v1/text:synthesize?key=${this.apiKey}`; const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(request) }); if (!response.ok) { const errorText = await response.text(); throw new Error(`Google Text-to-Speech API错误: ${response.status} - ${errorText}`); } const data = await response.json(); if (!data.audioContent) { throw new Error('API返回的音频内容为空'); } // 将base64音频数据转换为Buffer return Buffer.from(data.audioContent, 'base64'); } async playAudio(audioBuffer) { return new Promise((resolve, reject) => { // 创建临时音频文件 const tempFilePath = path.join(this.tempDir, `speech_${Date.now()}.mp3`); try { // 写入音频文件 fs.writeFileSync(tempFilePath, audioBuffer); // 播放音频 this.player.play(tempFilePath, (err) => { // 播放完成后删除临时文件 try { fs.unlinkSync(tempFilePath); } catch (cleanupError) { console.warn('清理临时文件失败:', cleanupError); } if (err) { reject(new Error(`音频播放失败: ${err.message}`)); } else { resolve(); } }); } catch (fileError) { reject(new Error(`文件操作失败: ${fileError instanceof Error ? fileError.message : '未知错误'}`)); } }); } // 清理临时目录 cleanupTempFiles() { try { if (fs.existsSync(this.tempDir)) { const files = fs.readdirSync(this.tempDir); files.forEach(file => { if (file.startsWith('speech_') && file.endsWith('.mp3')) { try { fs.unlinkSync(path.join(this.tempDir, file)); } catch (error) { console.warn(`清理临时文件失败: ${file}`, error); } } }); } } catch (error) { console.warn('清理临时目录失败:', error); } } } //# sourceMappingURL=speech.js.map