From 33245996c5377c5bf63ca97813e6174da9e88129 Mon Sep 17 00:00:00 2001 From: harry Date: Sat, 10 May 2025 17:21:13 +0800 Subject: [PATCH] feat: add test for voice service --- test/README.md | 1 + test/services/test_voice.py | 107 ++++++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+) create mode 100644 test/services/test_voice.py diff --git a/test/README.md b/test/README.md index adfb4ad..1deb3a9 100644 --- a/test/README.md +++ b/test/README.md @@ -7,6 +7,7 @@ This directory contains unit tests for the **MoneyPrinterTurbo** project. - `services/`: Tests for components in the `app/services` directory - `test_video.py`: Tests for the video service - `test_task.py`: Tests for the task service + - `test_voice.py`: Tests for the voice service ## Running Tests diff --git a/test/services/test_voice.py b/test/services/test_voice.py new file mode 100644 index 0000000..31f1799 --- /dev/null +++ b/test/services/test_voice.py @@ -0,0 +1,107 @@ +import asyncio +import unittest +import os +import sys +from pathlib import Path + +# add project root to python path +sys.path.insert(0, str(Path(__file__).parent.parent.parent)) + +from app.utils import utils +from app.services import voice as vs + +temp_dir = utils.storage_dir("temp") + +text_en = """ +What is the meaning of life? +This question has puzzled philosophers, scientists, and thinkers of all kinds for centuries. +Throughout history, various cultures and individuals have come up with their interpretations and beliefs around the purpose of life. +Some say it's to seek happiness and self-fulfillment, while others believe it's about contributing to the welfare of others and making a positive impact in the world. +Despite the myriad of perspectives, one thing remains clear: the meaning of life is a deeply personal concept that varies from one person to another. +It's an existential inquiry that encourages us to reflect on our values, desires, and the essence of our existence. +""" + +text_zh = """ +预计未来3天深圳冷空气活动频繁,未来两天持续阴天有小雨,出门带好雨具; +10-11日持续阴天有小雨,日温差小,气温在13-17℃之间,体感阴凉; +12日天气短暂好转,早晚清凉; +""" + +voice_rate=1.0 +voice_volume=1.0 + +class TestVoiceService(unittest.TestCase): + def setUp(self): + self.loop = asyncio.new_event_loop() + asyncio.set_event_loop(self.loop) + + def tearDown(self): + self.loop.close() + + def test_siliconflow(self): + voice_name = "siliconflow:FunAudioLLM/CosyVoice2-0.5B:alex-Male" + voice_name = vs.parse_voice_name(voice_name) + + async def _do(): + parts = voice_name.split(":") + if len(parts) >= 3: + model = parts[1] + # 移除性别后缀,例如 "alex-Male" -> "alex" + voice_with_gender = parts[2] + voice = voice_with_gender.split("-")[0] + # 构建完整的voice参数,格式为 "model:voice" + full_voice = f"{model}:{voice}" + voice_file = f"{temp_dir}/tts-siliconflow-{voice}.mp3" + subtitle_file = f"{temp_dir}/tts-siliconflow-{voice}.srt" + sub_maker = vs.siliconflow_tts( + text=text_zh, model=model, voice=full_voice, voice_file=voice_file, voice_rate=voice_rate, voice_volume=voice_volume + ) + if not sub_maker: + self.fail("siliconflow tts failed") + vs.create_subtitle(sub_maker=sub_maker, text=text_zh, subtitle_file=subtitle_file) + audio_duration = vs.get_audio_duration(sub_maker) + print(f"voice: {voice_name}, audio duration: {audio_duration}s") + else: + self.fail("siliconflow invalid voice name") + + self.loop.run_until_complete(_do()) + + def test_azure_tts_v1(self): + voice_name = "zh-CN-XiaoyiNeural-Female" + voice_name = vs.parse_voice_name(voice_name) + print(voice_name) + + voice_file = f"{temp_dir}/tts-azure-v1-{voice_name}.mp3" + subtitle_file = f"{temp_dir}/tts-azure-v1-{voice_name}.srt" + sub_maker = vs.azure_tts_v1( + text=text_zh, voice_name=voice_name, voice_file=voice_file, voice_rate=voice_rate + ) + if not sub_maker: + self.fail("azure tts v1 failed") + vs.create_subtitle(sub_maker=sub_maker, text=text_zh, subtitle_file=subtitle_file) + audio_duration = vs.get_audio_duration(sub_maker) + print(f"voice: {voice_name}, audio duration: {audio_duration}s") + + def test_azure_tts_v2(self): + voice_name = "zh-CN-XiaoxiaoMultilingualNeural-V2-Female" + voice_name = vs.parse_voice_name(voice_name) + print(voice_name) + + async def _do(): + voice_file = f"{temp_dir}/tts-azure-v2-{voice_name}.mp3" + subtitle_file = f"{temp_dir}/tts-azure-v2-{voice_name}.srt" + sub_maker = vs.azure_tts_v2( + text=text_zh, voice_name=voice_name, voice_file=voice_file + ) + if not sub_maker: + self.fail("azure tts v2 failed") + vs.create_subtitle(sub_maker=sub_maker, text=text_zh, subtitle_file=subtitle_file) + audio_duration = vs.get_audio_duration(sub_maker) + print(f"voice: {voice_name}, audio duration: {audio_duration}s") + + self.loop.run_until_complete(_do()) + +if __name__ == "__main__": + # python -m unittest test.services.test_voice.TestVoiceService.test_azure_tts_v1 + # python -m unittest test.services.test_voice.TestVoiceService.test_azure_tts_v2 + unittest.main() \ No newline at end of file