获取音频时长

main
mula.liu 2025-12-27 15:30:24 +08:00
parent a2a8a48fad
commit 5b1ab08a77
4 changed files with 38 additions and 18 deletions

View File

@ -4,7 +4,8 @@
```bash ```bash
cd /Users/jiliu/工作/projects/imeeting/backend cd /Users/jiliu/工作/projects/imeeting/backend
pip install mutagen source venv/bin/activate
pip install tinytag
``` ```
## 2. 执行数据库迁移 ## 2. 执行数据库迁移
@ -28,8 +29,10 @@ mysql -u root -p imeeting < sql/migrations/add_duration_to_audio_files.sql
### 3.2 音频处理优化 ### 3.2 音频处理优化
**音频时长计算:** **音频时长计算:**
- 使用mutagen库自动识别音频格式并提取时长 - 使用TinyTag库读取音频文件时长
- 支持mp3, m4a, mp4, wav等常见格式 - 纯Python实现无需外部依赖
- 支持mp3, m4a, mp4, wav, ogg, flac等常见格式
- 能处理特殊/损坏元数据的文件
- 时长保存到audio_files.duration字段单位 - 时长保存到audio_files.duration字段单位
- 时长读取功能位于`app/utils/audio_parser.py` - 时长读取功能位于`app/utils/audio_parser.py`
@ -47,8 +50,9 @@ mysql -u root -p imeeting < sql/migrations/add_duration_to_audio_files.sql
## 4. 代码变更清单 ## 4. 代码变更清单
### 后端变更: ### 后端变更:
- `/backend/app/utils/audio_parser.py` - 新增音频时长解析工具 - `/backend/app/utils/audio_parser.py` - 新增音频时长解析工具使用TinyTag
- `/backend/app/api/endpoints/meetings.py` - 音频上传时调用audio_parser获取时长修复Safari播放问题 - `/backend/app/api/endpoints/meetings.py` - 完整文件上传时调用audio_parser获取时长修复Safari播放问题
- `/backend/app/api/endpoints/audio.py` - Stream上传完成时调用audio_parser获取时长
- `/backend/app/services/audio_service.py` - 增加duration参数 - `/backend/app/services/audio_service.py` - 增加duration参数
- `/backend/app/api/endpoints/auth.py` - 登录时记录日志 - `/backend/app/api/endpoints/auth.py` - 登录时记录日志
- `/backend/app/api/endpoints/admin_dashboard.py` - 更新用户统计查询 - `/backend/app/api/endpoints/admin_dashboard.py` - 更新用户统计查询
@ -81,9 +85,10 @@ mysql -u root -p imeeting < sql/migrations/add_duration_to_audio_files.sql
## 6. 注意事项 ## 6. 注意事项
1. **音频时长计算** 1. **音频时长计算**
- 使用mutagen自动识别格式 - 使用TinyTag纯Python库
- 无需系统依赖,跨平台兼容
- 计算失败时duration为0不影响其他功能 - 计算失败时duration为0不影响其他功能
- 支持所有mutagen支持的格式 - 支持所有TinyTag支持的格式
2. **Safari音频播放兼容性** 2. **Safari音频播放兼容性**
- 使用直接src属性而非source子元素 - 使用直接src属性而非source子元素

View File

@ -6,6 +6,7 @@ from app.core.response import create_api_response
from app.services.async_transcription_service import AsyncTranscriptionService from app.services.async_transcription_service import AsyncTranscriptionService
from app.services.async_meeting_service import async_meeting_service from app.services.async_meeting_service import async_meeting_service
from app.services.audio_service import handle_audio_upload from app.services.audio_service import handle_audio_upload
from app.utils.audio_parser import get_audio_duration
from pydantic import BaseModel from pydantic import BaseModel
from typing import Optional, List from typing import Optional, List
from datetime import datetime, timedelta from datetime import datetime, timedelta
@ -460,6 +461,14 @@ async def complete_upload(
file_size = full_path.stat().st_size file_size = full_path.stat().st_size
file_name = full_path.name file_name = full_path.name
# 6.5 获取音频时长
audio_duration = 0
try:
audio_duration = get_audio_duration(str(full_path))
print(f"音频时长: {audio_duration}")
except Exception as e:
print(f"警告: 获取音频时长失败,但不影响后续流程: {e}")
# 7. 调用 audio_service 处理文件(数据库更新、启动转录和总结) # 7. 调用 audio_service 处理文件(数据库更新、启动转录和总结)
result = handle_audio_upload( result = handle_audio_upload(
file_path=file_path, file_path=file_path,
@ -469,7 +478,8 @@ async def complete_upload(
current_user=current_user, current_user=current_user,
auto_summarize=request.auto_summarize, auto_summarize=request.auto_summarize,
background_tasks=background_tasks, background_tasks=background_tasks,
prompt_id=request.prompt_id # 传递提示词模版ID prompt_id=request.prompt_id, # 传递提示词模版ID
duration=audio_duration # 传递时长参数
) )
# 如果处理失败,返回错误 # 如果处理失败,返回错误
@ -492,7 +502,7 @@ async def complete_upload(
"meeting_id": request.meeting_id, "meeting_id": request.meeting_id,
"file_path": file_path, "file_path": file_path,
"file_size": file_size, "file_size": file_size,
"duration": None, # 可以通过ffprobe获取但不是必需的 "duration": audio_duration,
"task_id": transcription_task_id, "task_id": transcription_task_id,
"task_status": "pending" if transcription_task_id else None, "task_status": "pending" if transcription_task_id else None,
"auto_summarize": request.auto_summarize "auto_summarize": request.auto_summarize

View File

@ -4,13 +4,15 @@
用于解析音频文件的元数据信息如时长采样率编码格式等 用于解析音频文件的元数据信息如时长采样率编码格式等
""" """
from mutagen import File as MutagenFile from tinytag import TinyTag
def get_audio_duration(file_path: str) -> int: def get_audio_duration(file_path: str) -> int:
""" """
获取音频文件时长 获取音频文件时长
使用TinyTag读取音频文件时长
Args: Args:
file_path: 音频文件的完整路径 file_path: 音频文件的完整路径
@ -22,13 +24,15 @@ def get_audio_duration(file_path: str) -> int:
- M4A (.m4a) - M4A (.m4a)
- MP4 (.mp4) - MP4 (.mp4)
- WAV (.wav) - WAV (.wav)
- 以及mutagen支持的其他格式 - OGG (.ogg)
- FLAC (.flac)
- 以及TinyTag支持的其他音频格式
""" """
try: try:
audio = MutagenFile(file_path) tag = TinyTag.get(file_path)
if audio is not None and hasattr(audio.info, 'length'): if tag.duration and tag.duration > 0:
return int(audio.info.length) return int(tag.duration)
return 0
except Exception as e: except Exception as e:
print(f"获取音频时长失败 ({file_path}): {e}") print(f"获取音频时长失败 ({file_path}): {e}")
return 0
return 0

View File

@ -22,5 +22,6 @@ psutil
# APK Parsing # APK Parsing
pyaxmlparser pyaxmlparser
# Audio Processing # Audio Metadata
mutagen==1.47.0 tinytag