ff
Browse files- fixed_app.py +106 -0
fixed_app.py
CHANGED
|
@@ -80,6 +80,56 @@ def load_model(model_name: str):
|
|
| 80 |
logger.error(f"找不到模型 {model_name},请确保模型文件存在")
|
| 81 |
raise HTTPException(status_code=500, detail=f"Model {model_name} not found")
|
| 82 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 83 |
def decode_audio(audio_base64: str) -> str:
|
| 84 |
"""解码base64音频数据并保存为临时文件,返回文件路径"""
|
| 85 |
try:
|
|
@@ -192,6 +242,12 @@ async def transcribe_audio(request: AudioRequest):
|
|
| 192 |
|
| 193 |
logger.info(f"使用whisper二进制: {whisper_binary}")
|
| 194 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 195 |
# 构建命令 - 根据二进制文件类型调整参数
|
| 196 |
if "whisper-cli" in whisper_binary:
|
| 197 |
# 新的whisper-cli命令格式 - 不输出到文件,直接输出到stdout
|
|
@@ -280,6 +336,11 @@ async def transcribe_audio(request: AudioRequest):
|
|
| 280 |
# 清理临时文件
|
| 281 |
if os.path.exists(audio_file):
|
| 282 |
os.unlink(audio_file)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 283 |
|
| 284 |
return StreamingResponse(event_stream(), media_type="text/event-stream")
|
| 285 |
except Exception as e:
|
|
@@ -355,6 +416,50 @@ async def test_audio_decode(request: AudioRequest):
|
|
| 355 |
"error": str(e)
|
| 356 |
}
|
| 357 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 358 |
@app.get("/")
|
| 359 |
async def root():
|
| 360 |
"""根路径"""
|
|
@@ -367,6 +472,7 @@ async def root():
|
|
| 367 |
"transcribe": "/transcribe",
|
| 368 |
"test": "/test",
|
| 369 |
"test-audio": "/test-audio",
|
|
|
|
| 370 |
"test-transcribe": "/test-transcribe"
|
| 371 |
}
|
| 372 |
}
|
|
|
|
| 80 |
logger.error(f"找不到模型 {model_name},请确保模型文件存在")
|
| 81 |
raise HTTPException(status_code=500, detail=f"Model {model_name} not found")
|
| 82 |
|
| 83 |
+
async def convert_audio_to_wav(input_file: str) -> str:
|
| 84 |
+
"""使用ffmpeg将音频文件转换为WAV格式"""
|
| 85 |
+
try:
|
| 86 |
+
# 创建输出文件路径
|
| 87 |
+
output_file = input_file.rsplit('.', 1)[0] + '_converted.wav'
|
| 88 |
+
|
| 89 |
+
# 构建ffmpeg命令
|
| 90 |
+
cmd = [
|
| 91 |
+
'ffmpeg',
|
| 92 |
+
'-i', input_file, # 输入文件
|
| 93 |
+
'-acodec', 'pcm_s16le', # 音频编码器:16位PCM
|
| 94 |
+
'-ar', '16000', # 采样率:16kHz(whisper推荐)
|
| 95 |
+
'-ac', '1', # 声道数:单声道
|
| 96 |
+
'-y', # 覆盖输出文件
|
| 97 |
+
output_file
|
| 98 |
+
]
|
| 99 |
+
|
| 100 |
+
logger.info(f"开始音频转换: {' '.join(cmd)}")
|
| 101 |
+
|
| 102 |
+
# 执行ffmpeg命令
|
| 103 |
+
proc = await asyncio.create_subprocess_exec(
|
| 104 |
+
*cmd,
|
| 105 |
+
stdout=asyncio.subprocess.PIPE,
|
| 106 |
+
stderr=asyncio.subprocess.PIPE
|
| 107 |
+
)
|
| 108 |
+
|
| 109 |
+
stdout, stderr = await proc.communicate()
|
| 110 |
+
|
| 111 |
+
if proc.returncode != 0:
|
| 112 |
+
error_msg = stderr.decode() if stderr else "Unknown ffmpeg error"
|
| 113 |
+
logger.error(f"音频转换失败: {error_msg}")
|
| 114 |
+
raise HTTPException(status_code=500, detail=f"Audio conversion failed: {error_msg}")
|
| 115 |
+
|
| 116 |
+
# 验证输出文件是否存在
|
| 117 |
+
if not os.path.exists(output_file):
|
| 118 |
+
raise HTTPException(status_code=500, detail="Converted audio file not found")
|
| 119 |
+
|
| 120 |
+
# 删除原始文件
|
| 121 |
+
if os.path.exists(input_file):
|
| 122 |
+
os.unlink(input_file)
|
| 123 |
+
|
| 124 |
+
logger.info(f"音频转换成功: {output_file}, 大小: {os.path.getsize(output_file)} 字节")
|
| 125 |
+
return output_file
|
| 126 |
+
|
| 127 |
+
except HTTPException:
|
| 128 |
+
raise
|
| 129 |
+
except Exception as e:
|
| 130 |
+
logger.error(f"音频转换过程中出错: {e}")
|
| 131 |
+
raise HTTPException(status_code=500, detail=f"Audio conversion error: {str(e)}")
|
| 132 |
+
|
| 133 |
def decode_audio(audio_base64: str) -> str:
|
| 134 |
"""解码base64音频数据并保存为临时文件,返回文件路径"""
|
| 135 |
try:
|
|
|
|
| 242 |
|
| 243 |
logger.info(f"使用whisper二进制: {whisper_binary}")
|
| 244 |
|
| 245 |
+
# 检查音频格式,如果不支持则转换为WAV
|
| 246 |
+
supported_formats = ('.wav', '.flac', '.mp3', '.ogg')
|
| 247 |
+
if not audio_file.endswith(supported_formats):
|
| 248 |
+
logger.info(f"音频格式不直接支持,将转换为WAV: {audio_file}")
|
| 249 |
+
audio_file = await convert_audio_to_wav(audio_file)
|
| 250 |
+
|
| 251 |
# 构建命令 - 根据二进制文件类型调整参数
|
| 252 |
if "whisper-cli" in whisper_binary:
|
| 253 |
# 新的whisper-cli命令格式 - 不输出到文件,直接输出到stdout
|
|
|
|
| 336 |
# 清理临时文件
|
| 337 |
if os.path.exists(audio_file):
|
| 338 |
os.unlink(audio_file)
|
| 339 |
+
# 如果有转换后的文件,也要清理
|
| 340 |
+
if audio_file.endswith('_converted.wav'):
|
| 341 |
+
original_file = audio_file.replace('_converted.wav', '.m4a')
|
| 342 |
+
if os.path.exists(original_file):
|
| 343 |
+
os.unlink(original_file)
|
| 344 |
|
| 345 |
return StreamingResponse(event_stream(), media_type="text/event-stream")
|
| 346 |
except Exception as e:
|
|
|
|
| 416 |
"error": str(e)
|
| 417 |
}
|
| 418 |
|
| 419 |
+
@app.post("/test-convert")
|
| 420 |
+
async def test_audio_conversion(request: AudioRequest):
|
| 421 |
+
"""测试音频格式转换功能"""
|
| 422 |
+
try:
|
| 423 |
+
# 解码音频
|
| 424 |
+
audio_file = decode_audio(request.audio)
|
| 425 |
+
logger.info(f"原始音频文件: {audio_file}")
|
| 426 |
+
|
| 427 |
+
# 检查是否需要转换
|
| 428 |
+
if not audio_file.endswith(('.wav', '.flac', '.mp3', '.ogg')):
|
| 429 |
+
logger.info("需要转换格式")
|
| 430 |
+
converted_file = await convert_audio_to_wav(audio_file)
|
| 431 |
+
|
| 432 |
+
# 获取转换后的文件信息
|
| 433 |
+
converted_size = os.path.getsize(converted_file)
|
| 434 |
+
|
| 435 |
+
# 清理文件
|
| 436 |
+
os.unlink(converted_file)
|
| 437 |
+
|
| 438 |
+
return {
|
| 439 |
+
"status": "success",
|
| 440 |
+
"message": "音频转换成功",
|
| 441 |
+
"original_file": audio_file,
|
| 442 |
+
"converted_file": converted_file,
|
| 443 |
+
"converted_size": converted_size,
|
| 444 |
+
"conversion_needed": True
|
| 445 |
+
}
|
| 446 |
+
else:
|
| 447 |
+
# 清理文件
|
| 448 |
+
os.unlink(audio_file)
|
| 449 |
+
|
| 450 |
+
return {
|
| 451 |
+
"status": "success",
|
| 452 |
+
"message": "音频格式已支持,无需转换",
|
| 453 |
+
"original_file": audio_file,
|
| 454 |
+
"conversion_needed": False
|
| 455 |
+
}
|
| 456 |
+
|
| 457 |
+
except Exception as e:
|
| 458 |
+
return {
|
| 459 |
+
"status": "error",
|
| 460 |
+
"error": str(e)
|
| 461 |
+
}
|
| 462 |
+
|
| 463 |
@app.get("/")
|
| 464 |
async def root():
|
| 465 |
"""根路径"""
|
|
|
|
| 472 |
"transcribe": "/transcribe",
|
| 473 |
"test": "/test",
|
| 474 |
"test-audio": "/test-audio",
|
| 475 |
+
"test-convert": "/test-convert",
|
| 476 |
"test-transcribe": "/test-transcribe"
|
| 477 |
}
|
| 478 |
}
|